2007-04-27 02:55:03 +04:00
/* AFS volume location management
2005-04-17 02:20:36 +04:00
*
2007-04-27 02:55:03 +04:00
* Copyright ( C ) 2002 , 2007 Red Hat , Inc . All Rights Reserved .
2005-04-17 02:20:36 +04:00
* Written by David Howells ( dhowells @ redhat . com )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
Detach sched.h from mm.h
First thing mm.h does is including sched.h solely for can_do_mlock() inline
function which has "current" dereference inside. By dealing with can_do_mlock()
mm.h can be detached from sched.h which is good. See below, why.
This patch
a) removes unconditional inclusion of sched.h from mm.h
b) makes can_do_mlock() normal function in mm/mlock.c
c) exports can_do_mlock() to not break compilation
d) adds sched.h inclusions back to files that were getting it indirectly.
e) adds less bloated headers to some files (asm/signal.h, jiffies.h) that were
getting them indirectly
Net result is:
a) mm.h users would get less code to open, read, preprocess, parse, ... if
they don't need sched.h
b) sched.h stops being dependency for significant number of files:
on x86_64 allmodconfig touching sched.h results in recompile of 4083 files,
after patch it's only 3744 (-8.3%).
Cross-compile tested on
all arm defconfigs, all mips defconfigs, all powerpc defconfigs,
alpha alpha-up
arm
i386 i386-up i386-defconfig i386-allnoconfig
ia64 ia64-up
m68k
mips
parisc parisc-up
powerpc powerpc-up
s390 s390-up
sparc sparc-up
sparc64 sparc64-up
um-x86_64
x86_64 x86_64-up x86_64-defconfig x86_64-allnoconfig
as well as my two usual configs.
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-05-21 01:22:52 +04:00
# include <linux/sched.h>
2005-04-17 02:20:36 +04:00
# include "internal.h"
2007-10-17 10:26:41 +04:00
static unsigned afs_vlocation_timeout = 10 ; /* volume location timeout in seconds */
static unsigned afs_vlocation_update_timeout = 10 * 60 ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
static void afs_vlocation_reaper ( struct work_struct * ) ;
static void afs_vlocation_updater ( struct work_struct * ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
static LIST_HEAD ( afs_vlocation_updates ) ;
static LIST_HEAD ( afs_vlocation_graveyard ) ;
static DEFINE_SPINLOCK ( afs_vlocation_updates_lock ) ;
static DEFINE_SPINLOCK ( afs_vlocation_graveyard_lock ) ;
static DECLARE_DELAYED_WORK ( afs_vlocation_reap , afs_vlocation_reaper ) ;
static DECLARE_DELAYED_WORK ( afs_vlocation_update , afs_vlocation_updater ) ;
static struct workqueue_struct * afs_vlocation_update_worker ;
2005-04-17 02:20:36 +04:00
/*
* iterate through the VL servers in a cell until one of them admits knowing
* about the volume in question
*/
2007-04-27 02:55:03 +04:00
static int afs_vlocation_access_vl_by_name ( struct afs_vlocation * vl ,
2007-04-27 02:57:07 +04:00
struct key * key ,
2005-04-17 02:20:36 +04:00
struct afs_cache_vlocation * vldb )
{
2007-04-27 02:55:03 +04:00
struct afs_cell * cell = vl - > cell ;
struct in_addr addr ;
2005-04-17 02:20:36 +04:00
int count , ret ;
2007-04-27 02:55:03 +04:00
_enter ( " %s,%s " , cell - > name , vl - > vldb . name ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
down_write ( & vl - > cell - > vl_sem ) ;
2005-04-17 02:20:36 +04:00
ret = - ENOMEDIUM ;
for ( count = cell - > vl_naddrs ; count > 0 ; count - - ) {
2007-04-27 02:55:03 +04:00
addr = cell - > vl_addrs [ cell - > vl_curr_svix ] ;
_debug ( " CellServ[%hu]: %08x " , cell - > vl_curr_svix , addr . s_addr ) ;
2005-04-17 02:20:36 +04:00
/* attempt to access the VL server */
2007-04-27 02:57:07 +04:00
ret = afs_vl_get_entry_by_name ( & addr , key , vl - > vldb . name , vldb ,
2007-04-27 02:55:03 +04:00
& afs_sync_call ) ;
2005-04-17 02:20:36 +04:00
switch ( ret ) {
case 0 :
goto out ;
case - ENOMEM :
case - ENONET :
case - ENETUNREACH :
case - EHOSTUNREACH :
case - ECONNREFUSED :
if ( ret = = - ENOMEM | | ret = = - ENONET )
goto out ;
goto rotate ;
case - ENOMEDIUM :
goto out ;
default :
2007-04-27 02:55:03 +04:00
ret = - EIO ;
2005-04-17 02:20:36 +04:00
goto rotate ;
}
/* rotate the server records upon lookup failure */
rotate :
cell - > vl_curr_svix + + ;
cell - > vl_curr_svix % = cell - > vl_naddrs ;
}
2007-04-27 02:49:28 +04:00
out :
2007-04-27 02:55:03 +04:00
up_write ( & vl - > cell - > vl_sem ) ;
2005-04-17 02:20:36 +04:00
_leave ( " = %d " , ret ) ;
return ret ;
2007-04-27 02:49:28 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* iterate through the VL servers in a cell until one of them admits knowing
* about the volume in question
*/
2007-04-27 02:55:03 +04:00
static int afs_vlocation_access_vl_by_id ( struct afs_vlocation * vl ,
2007-04-27 02:57:07 +04:00
struct key * key ,
2005-04-17 02:20:36 +04:00
afs_volid_t volid ,
afs_voltype_t voltype ,
struct afs_cache_vlocation * vldb )
{
2007-04-27 02:55:03 +04:00
struct afs_cell * cell = vl - > cell ;
struct in_addr addr ;
2005-04-17 02:20:36 +04:00
int count , ret ;
_enter ( " %s,%x,%d, " , cell - > name , volid , voltype ) ;
2007-04-27 02:55:03 +04:00
down_write ( & vl - > cell - > vl_sem ) ;
2005-04-17 02:20:36 +04:00
ret = - ENOMEDIUM ;
for ( count = cell - > vl_naddrs ; count > 0 ; count - - ) {
2007-04-27 02:55:03 +04:00
addr = cell - > vl_addrs [ cell - > vl_curr_svix ] ;
_debug ( " CellServ[%hu]: %08x " , cell - > vl_curr_svix , addr . s_addr ) ;
2005-04-17 02:20:36 +04:00
/* attempt to access the VL server */
2007-04-27 02:57:07 +04:00
ret = afs_vl_get_entry_by_id ( & addr , key , volid , voltype , vldb ,
2007-04-27 02:55:03 +04:00
& afs_sync_call ) ;
2005-04-17 02:20:36 +04:00
switch ( ret ) {
case 0 :
goto out ;
case - ENOMEM :
case - ENONET :
case - ENETUNREACH :
case - EHOSTUNREACH :
case - ECONNREFUSED :
if ( ret = = - ENOMEM | | ret = = - ENONET )
goto out ;
goto rotate ;
2007-04-27 02:55:03 +04:00
case - EBUSY :
vl - > upd_busy_cnt + + ;
if ( vl - > upd_busy_cnt < = 3 ) {
if ( vl - > upd_busy_cnt > 1 ) {
/* second+ BUSY - sleep a little bit */
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
schedule_timeout ( 1 ) ;
__set_current_state ( TASK_RUNNING ) ;
}
continue ;
}
break ;
2005-04-17 02:20:36 +04:00
case - ENOMEDIUM :
2007-04-27 02:55:03 +04:00
vl - > upd_rej_cnt + + ;
goto rotate ;
2005-04-17 02:20:36 +04:00
default :
2007-04-27 02:55:03 +04:00
ret = - EIO ;
2005-04-17 02:20:36 +04:00
goto rotate ;
}
/* rotate the server records upon lookup failure */
rotate :
cell - > vl_curr_svix + + ;
cell - > vl_curr_svix % = cell - > vl_naddrs ;
2007-04-27 02:55:03 +04:00
vl - > upd_busy_cnt = 0 ;
2005-04-17 02:20:36 +04:00
}
2007-04-27 02:49:28 +04:00
out :
2007-04-27 02:55:03 +04:00
if ( ret < 0 & & vl - > upd_rej_cnt > 0 ) {
printk ( KERN_NOTICE " kAFS: "
" Active volume no longer valid '%s' \n " ,
vl - > vldb . name ) ;
vl - > valid = 0 ;
ret = - ENOMEDIUM ;
}
up_write ( & vl - > cell - > vl_sem ) ;
2005-04-17 02:20:36 +04:00
_leave ( " = %d " , ret ) ;
return ret ;
2007-04-27 02:49:28 +04:00
}
2005-04-17 02:20:36 +04:00
/*
2007-04-27 02:55:03 +04:00
* allocate a volume location record
2005-04-17 02:20:36 +04:00
*/
2007-04-27 02:55:03 +04:00
static struct afs_vlocation * afs_vlocation_alloc ( struct afs_cell * cell ,
const char * name ,
size_t namesz )
2005-04-17 02:20:36 +04:00
{
2007-04-27 02:55:03 +04:00
struct afs_vlocation * vl ;
vl = kzalloc ( sizeof ( struct afs_vlocation ) , GFP_KERNEL ) ;
if ( vl ) {
vl - > cell = cell ;
vl - > state = AFS_VL_NEW ;
atomic_set ( & vl - > usage , 1 ) ;
INIT_LIST_HEAD ( & vl - > link ) ;
INIT_LIST_HEAD ( & vl - > grave ) ;
INIT_LIST_HEAD ( & vl - > update ) ;
init_waitqueue_head ( & vl - > waitq ) ;
2007-04-27 07:39:14 +04:00
spin_lock_init ( & vl - > lock ) ;
2007-04-27 02:55:03 +04:00
memcpy ( vl - > vldb . name , name , namesz ) ;
2005-04-17 02:20:36 +04:00
}
2007-04-27 02:55:03 +04:00
_leave ( " = %p " , vl ) ;
return vl ;
}
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
/*
* update record if we found it in the cache
*/
static int afs_vlocation_update_record ( struct afs_vlocation * vl ,
2007-04-27 02:57:07 +04:00
struct key * key ,
2007-04-27 02:55:03 +04:00
struct afs_cache_vlocation * vldb )
{
afs_voltype_t voltype ;
afs_volid_t vid ;
int ret ;
2005-04-17 02:20:36 +04:00
/* try to look up a cached volume in the cell VL databases by ID */
_debug ( " Locally Cached: %s %02x { %08x(%x) %08x(%x) %08x(%x) } " ,
2007-04-27 02:55:03 +04:00
vl - > vldb . name ,
vl - > vldb . vidmask ,
ntohl ( vl - > vldb . servers [ 0 ] . s_addr ) ,
vl - > vldb . srvtmask [ 0 ] ,
ntohl ( vl - > vldb . servers [ 1 ] . s_addr ) ,
vl - > vldb . srvtmask [ 1 ] ,
ntohl ( vl - > vldb . servers [ 2 ] . s_addr ) ,
vl - > vldb . srvtmask [ 2 ] ) ;
2005-04-17 02:20:36 +04:00
_debug ( " Vids: %08x %08x %08x " ,
2007-04-27 02:55:03 +04:00
vl - > vldb . vid [ 0 ] ,
vl - > vldb . vid [ 1 ] ,
vl - > vldb . vid [ 2 ] ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
if ( vl - > vldb . vidmask & AFS_VOL_VTM_RW ) {
vid = vl - > vldb . vid [ 0 ] ;
2005-04-17 02:20:36 +04:00
voltype = AFSVL_RWVOL ;
2007-04-27 02:55:03 +04:00
} else if ( vl - > vldb . vidmask & AFS_VOL_VTM_RO ) {
vid = vl - > vldb . vid [ 1 ] ;
2005-04-17 02:20:36 +04:00
voltype = AFSVL_ROVOL ;
2007-04-27 02:55:03 +04:00
} else if ( vl - > vldb . vidmask & AFS_VOL_VTM_BAK ) {
vid = vl - > vldb . vid [ 2 ] ;
2005-04-17 02:20:36 +04:00
voltype = AFSVL_BACKVOL ;
2007-04-27 02:49:28 +04:00
} else {
2005-04-17 02:20:36 +04:00
BUG ( ) ;
vid = 0 ;
voltype = 0 ;
}
2007-04-27 02:55:03 +04:00
/* contact the server to make sure the volume is still available
* - TODO : need to handle disconnected operation here
*/
2007-04-27 02:57:07 +04:00
ret = afs_vlocation_access_vl_by_id ( vl , key , vid , voltype , vldb ) ;
2005-04-17 02:20:36 +04:00
switch ( ret ) {
/* net error */
default :
2007-04-27 02:55:03 +04:00
printk ( KERN_WARNING " kAFS: "
" failed to update volume '%s' (%x) up in '%s': %d \n " ,
vl - > vldb . name , vid , vl - > cell - > name , ret ) ;
_leave ( " = %d " , ret ) ;
return ret ;
2005-04-17 02:20:36 +04:00
/* pulled from local cache into memory */
case 0 :
2007-04-27 02:55:03 +04:00
_leave ( " = 0 " ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
/* uh oh... looks like the volume got deleted */
case - ENOMEDIUM :
2007-04-27 02:55:03 +04:00
printk ( KERN_ERR " kAFS: "
" volume '%s' (%x) does not exist '%s' \n " ,
vl - > vldb . name , vid , vl - > cell - > name ) ;
2005-04-17 02:20:36 +04:00
/* TODO: make existing record unavailable */
2007-04-27 02:55:03 +04:00
_leave ( " = %d " , ret ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2007-04-27 02:55:03 +04:00
}
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
/*
* apply the update to a VL record
*/
static void afs_vlocation_apply_update ( struct afs_vlocation * vl ,
struct afs_cache_vlocation * vldb )
{
_debug ( " Done VL Lookup: %s %02x { %08x(%x) %08x(%x) %08x(%x) } " ,
vldb - > name , vldb - > vidmask ,
ntohl ( vldb - > servers [ 0 ] . s_addr ) , vldb - > srvtmask [ 0 ] ,
ntohl ( vldb - > servers [ 1 ] . s_addr ) , vldb - > srvtmask [ 1 ] ,
ntohl ( vldb - > servers [ 2 ] . s_addr ) , vldb - > srvtmask [ 2 ] ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
_debug ( " Vids: %08x %08x %08x " ,
vldb - > vid [ 0 ] , vldb - > vid [ 1 ] , vldb - > vid [ 2 ] ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
if ( strcmp ( vldb - > name , vl - > vldb . name ) ! = 0 )
printk ( KERN_NOTICE " kAFS: "
" name of volume '%s' changed to '%s' on server \n " ,
vl - > vldb . name , vldb - > name ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
vl - > vldb = * vldb ;
2005-04-17 02:20:36 +04:00
2009-04-03 19:42:41 +04:00
# ifdef CONFIG_AFS_FSCACHE
fscache_update_cookie ( vl - > cache ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-04-27 02:49:28 +04:00
}
2005-04-17 02:20:36 +04:00
/*
2007-04-27 02:55:03 +04:00
* fill in a volume location record , consulting the cache and the VL server
* both
2005-04-17 02:20:36 +04:00
*/
2007-04-27 02:57:07 +04:00
static int afs_vlocation_fill_in_record ( struct afs_vlocation * vl ,
struct key * key )
2005-04-17 02:20:36 +04:00
{
2007-04-27 02:55:03 +04:00
struct afs_cache_vlocation vldb ;
int ret ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
_enter ( " " ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
ASSERTCMP ( vl - > valid , = = , 0 ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
memset ( & vldb , 0 , sizeof ( vldb ) ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
/* see if we have an in-cache copy (will set vl->valid if there is) */
2009-04-03 19:42:41 +04:00
# ifdef CONFIG_AFS_FSCACHE
vl - > cache = fscache_acquire_cookie ( vl - > cell - > cache ,
& afs_vlocation_cache_index_def , vl ) ;
2007-04-27 02:55:03 +04:00
# endif
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
if ( vl - > valid ) {
/* try to update a known volume in the cell VL databases by
* ID as the name may have changed */
_debug ( " found in cache " ) ;
2007-04-27 02:57:07 +04:00
ret = afs_vlocation_update_record ( vl , key , & vldb ) ;
2007-04-27 02:55:03 +04:00
} else {
/* try to look up an unknown volume in the cell VL databases by
* name */
2007-04-27 02:57:07 +04:00
ret = afs_vlocation_access_vl_by_name ( vl , key , & vldb ) ;
2007-04-27 02:55:03 +04:00
if ( ret < 0 ) {
printk ( " kAFS: failed to locate '%s' in cell '%s' \n " ,
vl - > vldb . name , vl - > cell - > name ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
}
2007-04-27 02:55:03 +04:00
afs_vlocation_apply_update ( vl , & vldb ) ;
_leave ( " = 0 " ) ;
return 0 ;
2007-04-27 02:49:28 +04:00
}
2005-04-17 02:20:36 +04:00
/*
2007-04-27 02:55:03 +04:00
* queue a vlocation record for updates
2005-04-17 02:20:36 +04:00
*/
2007-10-17 10:26:41 +04:00
static void afs_vlocation_queue_for_updates ( struct afs_vlocation * vl )
2005-04-17 02:20:36 +04:00
{
2007-04-27 02:55:03 +04:00
struct afs_vlocation * xvl ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
/* wait at least 10 minutes before updating... */
vl - > update_at = get_seconds ( ) + afs_vlocation_update_timeout ;
spin_lock ( & afs_vlocation_updates_lock ) ;
if ( ! list_empty ( & afs_vlocation_updates ) ) {
/* ... but wait at least 1 second more than the newest record
* already queued so that we don ' t spam the VL server suddenly
* with lots of requests
*/
xvl = list_entry ( afs_vlocation_updates . prev ,
struct afs_vlocation , update ) ;
if ( vl - > update_at < = xvl - > update_at )
vl - > update_at = xvl - > update_at + 1 ;
} else {
queue_delayed_work ( afs_vlocation_update_worker ,
& afs_vlocation_update ,
afs_vlocation_update_timeout * HZ ) ;
2005-04-17 02:20:36 +04:00
}
2007-04-27 02:55:03 +04:00
list_add_tail ( & vl - > update , & afs_vlocation_updates ) ;
spin_unlock ( & afs_vlocation_updates_lock ) ;
2007-04-27 02:49:28 +04:00
}
2005-04-17 02:20:36 +04:00
/*
2007-04-27 02:55:03 +04:00
* lookup volume location
* - iterate through the VL servers in a cell until one of them admits knowing
* about the volume in question
* - lookup in the local cache if not able to find on the VL server
* - insert / update in the local cache if did get a VL response
2005-04-17 02:20:36 +04:00
*/
2007-04-27 02:55:03 +04:00
struct afs_vlocation * afs_vlocation_lookup ( struct afs_cell * cell ,
2007-04-27 02:57:07 +04:00
struct key * key ,
2007-04-27 02:55:03 +04:00
const char * name ,
size_t namesz )
2005-04-17 02:20:36 +04:00
{
2007-04-27 02:55:03 +04:00
struct afs_vlocation * vl ;
int ret ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:57:07 +04:00
_enter ( " {%s},{%x},%*.*s,%zu " ,
cell - > name , key_serial ( key ) ,
( int ) namesz , ( int ) namesz , name , namesz ) ;
2005-04-17 02:20:36 +04:00
2007-11-06 01:50:57 +03:00
if ( namesz > = sizeof ( vl - > vldb . name ) ) {
2007-04-27 02:55:03 +04:00
_leave ( " = -ENAMETOOLONG " ) ;
return ERR_PTR ( - ENAMETOOLONG ) ;
}
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
/* see if we have an in-memory copy first */
down_write ( & cell - > vl_sem ) ;
spin_lock ( & cell - > vl_lock ) ;
list_for_each_entry ( vl , & cell - > vl_list , link ) {
if ( vl - > vldb . name [ namesz ] ! = ' \0 ' )
continue ;
if ( memcmp ( vl - > vldb . name , name , namesz ) = = 0 )
goto found_in_memory ;
2005-04-17 02:20:36 +04:00
}
2007-04-27 02:55:03 +04:00
spin_unlock ( & cell - > vl_lock ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
/* not in the cell's in-memory lists - create a new record */
vl = afs_vlocation_alloc ( cell , name , namesz ) ;
if ( ! vl ) {
up_write ( & cell - > vl_sem ) ;
return ERR_PTR ( - ENOMEM ) ;
}
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
afs_get_cell ( cell ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
list_add_tail ( & vl - > link , & cell - > vl_list ) ;
vl - > state = AFS_VL_CREATING ;
up_write ( & cell - > vl_sem ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
fill_in_record :
2007-04-27 02:57:07 +04:00
ret = afs_vlocation_fill_in_record ( vl , key ) ;
2007-04-27 02:55:03 +04:00
if ( ret < 0 )
goto error_abandon ;
2007-04-27 07:39:14 +04:00
spin_lock ( & vl - > lock ) ;
2007-04-27 02:55:03 +04:00
vl - > state = AFS_VL_VALID ;
2007-04-27 07:39:14 +04:00
spin_unlock ( & vl - > lock ) ;
2007-04-28 02:26:30 +04:00
wake_up ( & vl - > waitq ) ;
2005-04-17 02:20:36 +04:00
2009-04-03 19:42:41 +04:00
/* update volume entry in local cache */
# ifdef CONFIG_AFS_FSCACHE
fscache_update_cookie ( vl - > cache ) ;
# endif
2007-04-27 02:55:03 +04:00
/* schedule for regular updates */
afs_vlocation_queue_for_updates ( vl ) ;
goto success ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
found_in_memory :
/* found in memory */
_debug ( " found in memory " ) ;
atomic_inc ( & vl - > usage ) ;
spin_unlock ( & cell - > vl_lock ) ;
if ( ! list_empty ( & vl - > grave ) ) {
spin_lock ( & afs_vlocation_graveyard_lock ) ;
list_del_init ( & vl - > grave ) ;
spin_unlock ( & afs_vlocation_graveyard_lock ) ;
2005-04-17 02:20:36 +04:00
}
2007-04-27 02:55:03 +04:00
up_write ( & cell - > vl_sem ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
/* see if it was an abandoned record that we might try filling in */
2007-04-27 07:39:14 +04:00
spin_lock ( & vl - > lock ) ;
2007-04-27 02:55:03 +04:00
while ( vl - > state ! = AFS_VL_VALID ) {
afs_vlocation_state_t state = vl - > state ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
_debug ( " invalid [state %d] " , state ) ;
2005-04-17 02:20:36 +04:00
2007-04-28 02:26:30 +04:00
if ( state = = AFS_VL_NEW | | state = = AFS_VL_NO_VOLUME ) {
2007-04-27 07:39:14 +04:00
vl - > state = AFS_VL_CREATING ;
spin_unlock ( & vl - > lock ) ;
goto fill_in_record ;
2007-04-27 02:55:03 +04:00
}
/* must now wait for creation or update by someone else to
* complete */
_debug ( " wait " ) ;
2007-04-27 07:39:14 +04:00
spin_unlock ( & vl - > lock ) ;
2007-04-28 02:26:30 +04:00
ret = wait_event_interruptible ( vl - > waitq ,
vl - > state = = AFS_VL_NEW | |
vl - > state = = AFS_VL_VALID | |
vl - > state = = AFS_VL_NO_VOLUME ) ;
2007-04-27 02:55:03 +04:00
if ( ret < 0 )
goto error ;
2007-04-27 07:39:14 +04:00
spin_lock ( & vl - > lock ) ;
2005-04-17 02:20:36 +04:00
}
2007-04-27 07:39:14 +04:00
spin_unlock ( & vl - > lock ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
success :
2009-04-03 19:42:41 +04:00
_leave ( " = %p " , vl ) ;
2007-04-27 02:55:03 +04:00
return vl ;
error_abandon :
2007-04-27 07:39:14 +04:00
spin_lock ( & vl - > lock ) ;
2007-04-27 02:55:03 +04:00
vl - > state = AFS_VL_NEW ;
2007-04-27 07:39:14 +04:00
spin_unlock ( & vl - > lock ) ;
2007-04-28 02:26:30 +04:00
wake_up ( & vl - > waitq ) ;
2007-04-27 02:55:03 +04:00
error :
ASSERT ( vl ! = NULL ) ;
afs_put_vlocation ( vl ) ;
2005-04-17 02:20:36 +04:00
_leave ( " = %d " , ret ) ;
2007-04-27 02:55:03 +04:00
return ERR_PTR ( ret ) ;
2007-04-27 02:49:28 +04:00
}
2005-04-17 02:20:36 +04:00
/*
2007-04-27 02:55:03 +04:00
* finish using a volume location record
2005-04-17 02:20:36 +04:00
*/
2007-04-27 02:55:03 +04:00
void afs_put_vlocation ( struct afs_vlocation * vl )
2005-04-17 02:20:36 +04:00
{
2007-04-27 02:55:03 +04:00
if ( ! vl )
return ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
_enter ( " %s " , vl - > vldb . name ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
ASSERTCMP ( atomic_read ( & vl - > usage ) , > , 0 ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
if ( likely ( ! atomic_dec_and_test ( & vl - > usage ) ) ) {
_leave ( " " ) ;
return ;
}
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
spin_lock ( & afs_vlocation_graveyard_lock ) ;
if ( atomic_read ( & vl - > usage ) = = 0 ) {
_debug ( " buried " ) ;
list_move_tail ( & vl - > grave , & afs_vlocation_graveyard ) ;
vl - > time_of_death = get_seconds ( ) ;
schedule_delayed_work ( & afs_vlocation_reap ,
afs_vlocation_timeout * HZ ) ;
/* suspend updates on this record */
if ( ! list_empty ( & vl - > update ) ) {
spin_lock ( & afs_vlocation_updates_lock ) ;
list_del_init ( & vl - > update ) ;
spin_unlock ( & afs_vlocation_updates_lock ) ;
}
}
spin_unlock ( & afs_vlocation_graveyard_lock ) ;
_leave ( " [killed?] " ) ;
2007-04-27 02:49:28 +04:00
}
2005-04-17 02:20:36 +04:00
/*
2007-04-27 02:55:03 +04:00
* destroy a dead volume location record
2005-04-17 02:20:36 +04:00
*/
2007-04-27 02:55:03 +04:00
static void afs_vlocation_destroy ( struct afs_vlocation * vl )
2005-04-17 02:20:36 +04:00
{
2007-04-27 02:55:03 +04:00
_enter ( " %p " , vl ) ;
2005-04-17 02:20:36 +04:00
2009-04-03 19:42:41 +04:00
# ifdef CONFIG_AFS_FSCACHE
fscache_relinquish_cookie ( vl - > cache , 0 ) ;
2007-04-27 02:55:03 +04:00
# endif
afs_put_cell ( vl - > cell ) ;
kfree ( vl ) ;
}
/*
* reap dead volume location records
*/
static void afs_vlocation_reaper ( struct work_struct * work )
{
LIST_HEAD ( corpses ) ;
struct afs_vlocation * vl ;
unsigned long delay , expiry ;
time_t now ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
_enter ( " " ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
now = get_seconds ( ) ;
spin_lock ( & afs_vlocation_graveyard_lock ) ;
while ( ! list_empty ( & afs_vlocation_graveyard ) ) {
vl = list_entry ( afs_vlocation_graveyard . next ,
struct afs_vlocation , grave ) ;
_debug ( " check %p " , vl ) ;
/* the queue is ordered most dead first */
expiry = vl - > time_of_death + afs_vlocation_timeout ;
if ( expiry > now ) {
delay = ( expiry - now ) * HZ ;
_debug ( " delay %lu " , delay ) ;
if ( ! schedule_delayed_work ( & afs_vlocation_reap ,
delay ) ) {
cancel_delayed_work ( & afs_vlocation_reap ) ;
schedule_delayed_work ( & afs_vlocation_reap ,
delay ) ;
}
break ;
}
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
spin_lock ( & vl - > cell - > vl_lock ) ;
if ( atomic_read ( & vl - > usage ) > 0 ) {
_debug ( " no reap " ) ;
list_del_init ( & vl - > grave ) ;
2007-04-27 02:49:28 +04:00
} else {
2007-04-27 02:55:03 +04:00
_debug ( " reap " ) ;
list_move_tail ( & vl - > grave , & corpses ) ;
list_del_init ( & vl - > link ) ;
2005-04-17 02:20:36 +04:00
}
2007-04-27 02:55:03 +04:00
spin_unlock ( & vl - > cell - > vl_lock ) ;
2005-04-17 02:20:36 +04:00
}
2007-04-27 02:55:03 +04:00
spin_unlock ( & afs_vlocation_graveyard_lock ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
/* now reap the corpses we've extracted */
while ( ! list_empty ( & corpses ) ) {
vl = list_entry ( corpses . next , struct afs_vlocation , grave ) ;
list_del ( & vl - > grave ) ;
afs_vlocation_destroy ( vl ) ;
2005-04-17 02:20:36 +04:00
}
_leave ( " " ) ;
2007-04-27 02:55:03 +04:00
}
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
/*
* initialise the VL update process
*/
int __init afs_vlocation_update_init ( void )
{
afs_vlocation_update_worker =
create_singlethread_workqueue ( " kafs_vlupdated " ) ;
return afs_vlocation_update_worker ? 0 : - ENOMEM ;
}
/*
* discard all the volume location records for rmmod
*/
2007-05-03 14:12:46 +04:00
void afs_vlocation_purge ( void )
2007-04-27 02:55:03 +04:00
{
afs_vlocation_timeout = 0 ;
spin_lock ( & afs_vlocation_updates_lock ) ;
list_del_init ( & afs_vlocation_updates ) ;
spin_unlock ( & afs_vlocation_updates_lock ) ;
cancel_delayed_work ( & afs_vlocation_update ) ;
queue_delayed_work ( afs_vlocation_update_worker ,
& afs_vlocation_update , 0 ) ;
destroy_workqueue ( afs_vlocation_update_worker ) ;
cancel_delayed_work ( & afs_vlocation_reap ) ;
schedule_delayed_work ( & afs_vlocation_reap , 0 ) ;
2007-04-27 02:49:28 +04:00
}
2005-04-17 02:20:36 +04:00
/*
2007-04-27 02:55:03 +04:00
* update a volume location
2005-04-17 02:20:36 +04:00
*/
2007-04-27 02:55:03 +04:00
static void afs_vlocation_updater ( struct work_struct * work )
2005-04-17 02:20:36 +04:00
{
struct afs_cache_vlocation vldb ;
2007-04-27 02:55:03 +04:00
struct afs_vlocation * vl , * xvl ;
time_t now ;
long timeout ;
2005-04-17 02:20:36 +04:00
int ret ;
2007-04-27 02:55:03 +04:00
_enter ( " " ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
now = get_seconds ( ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
/* find a record to update */
spin_lock ( & afs_vlocation_updates_lock ) ;
for ( ; ; ) {
if ( list_empty ( & afs_vlocation_updates ) ) {
spin_unlock ( & afs_vlocation_updates_lock ) ;
_leave ( " [nothing] " ) ;
return ;
}
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
vl = list_entry ( afs_vlocation_updates . next ,
struct afs_vlocation , update ) ;
if ( atomic_read ( & vl - > usage ) > 0 )
break ;
list_del_init ( & vl - > update ) ;
2005-04-17 02:20:36 +04:00
}
2007-04-27 02:55:03 +04:00
timeout = vl - > update_at - now ;
if ( timeout > 0 ) {
queue_delayed_work ( afs_vlocation_update_worker ,
& afs_vlocation_update , timeout * HZ ) ;
spin_unlock ( & afs_vlocation_updates_lock ) ;
_leave ( " [nothing] " ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2007-04-27 02:55:03 +04:00
list_del_init ( & vl - > update ) ;
atomic_inc ( & vl - > usage ) ;
spin_unlock ( & afs_vlocation_updates_lock ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
/* we can now perform the update */
_debug ( " update %s " , vl - > vldb . name ) ;
vl - > state = AFS_VL_UPDATING ;
vl - > upd_rej_cnt = 0 ;
vl - > upd_busy_cnt = 0 ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:57:07 +04:00
ret = afs_vlocation_update_record ( vl , NULL , & vldb ) ;
2007-04-27 07:39:14 +04:00
spin_lock ( & vl - > lock ) ;
2007-04-27 02:55:03 +04:00
switch ( ret ) {
case 0 :
afs_vlocation_apply_update ( vl , & vldb ) ;
vl - > state = AFS_VL_VALID ;
break ;
case - ENOMEDIUM :
vl - > state = AFS_VL_VOLUME_DELETED ;
break ;
default :
vl - > state = AFS_VL_UNCERTAIN ;
break ;
}
2007-04-27 07:39:14 +04:00
spin_unlock ( & vl - > lock ) ;
2007-04-28 02:26:30 +04:00
wake_up ( & vl - > waitq ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
/* and then reschedule */
_debug ( " reschedule " ) ;
vl - > update_at = get_seconds ( ) + afs_vlocation_update_timeout ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
spin_lock ( & afs_vlocation_updates_lock ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
if ( ! list_empty ( & afs_vlocation_updates ) ) {
/* next update in 10 minutes, but wait at least 1 second more
* than the newest record already queued so that we don ' t spam
* the VL server suddenly with lots of requests
*/
xvl = list_entry ( afs_vlocation_updates . prev ,
struct afs_vlocation , update ) ;
if ( vl - > update_at < = xvl - > update_at )
vl - > update_at = xvl - > update_at + 1 ;
xvl = list_entry ( afs_vlocation_updates . next ,
struct afs_vlocation , update ) ;
timeout = xvl - > update_at - now ;
if ( timeout < 0 )
timeout = 0 ;
} else {
timeout = afs_vlocation_update_timeout ;
2005-04-17 02:20:36 +04:00
}
2007-04-27 02:55:03 +04:00
ASSERT ( list_empty ( & vl - > update ) ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
list_add_tail ( & vl - > update , & afs_vlocation_updates ) ;
2005-04-17 02:20:36 +04:00
2007-04-27 02:55:03 +04:00
_debug ( " timeout %ld " , timeout ) ;
queue_delayed_work ( afs_vlocation_update_worker ,
& afs_vlocation_update , timeout * HZ ) ;
spin_unlock ( & afs_vlocation_updates_lock ) ;
afs_put_vlocation ( vl ) ;
2007-04-27 02:49:28 +04:00
}