2005-04-17 02:20:36 +04:00
/* vlocation.c: volume location management
*
* Copyright ( C ) 2002 Red Hat , Inc . All Rights Reserved .
* 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>
# include <linux/slab.h>
# include <linux/fs.h>
# include <linux/pagemap.h>
# include "volume.h"
# include "cell.h"
# include "cmservice.h"
# include "fsclient.h"
# include "vlclient.h"
# include "kafstimod.h"
# include <rxrpc/connection.h>
# include "internal.h"
# define AFS_VLDB_TIMEOUT HZ*1000
static void afs_vlocation_update_timer ( struct afs_timer * timer ) ;
static void afs_vlocation_update_attend ( struct afs_async_op * op ) ;
static void afs_vlocation_update_discard ( struct afs_async_op * op ) ;
static void __afs_put_vlocation ( struct afs_vlocation * vlocation ) ;
static void __afs_vlocation_timeout ( struct afs_timer * timer )
{
struct afs_vlocation * vlocation =
list_entry ( timer , struct afs_vlocation , timeout ) ;
_debug ( " VL TIMEOUT [%s{u=%d}] " ,
vlocation - > vldb . name , atomic_read ( & vlocation - > usage ) ) ;
afs_vlocation_do_timeout ( vlocation ) ;
}
static const struct afs_timer_ops afs_vlocation_timer_ops = {
. timed_out = __afs_vlocation_timeout ,
} ;
static const struct afs_timer_ops afs_vlocation_update_timer_ops = {
. timed_out = afs_vlocation_update_timer ,
} ;
static const struct afs_async_op_ops afs_vlocation_update_op_ops = {
. attend = afs_vlocation_update_attend ,
. discard = afs_vlocation_update_discard ,
} ;
static LIST_HEAD ( afs_vlocation_update_pendq ) ; /* queue of VLs awaiting update */
static struct afs_vlocation * afs_vlocation_update ; /* VL currently being updated */
static DEFINE_SPINLOCK ( afs_vlocation_update_lock ) ; /* lock guarding update queue */
# ifdef AFS_CACHING_SUPPORT
static cachefs_match_val_t afs_vlocation_cache_match ( void * target ,
const void * entry ) ;
static void afs_vlocation_cache_update ( void * source , void * entry ) ;
struct cachefs_index_def afs_vlocation_cache_index_def = {
. name = " vldb " ,
. data_size = sizeof ( struct afs_cache_vlocation ) ,
. keys [ 0 ] = { CACHEFS_INDEX_KEYS_ASCIIZ , 64 } ,
. match = afs_vlocation_cache_match ,
. update = afs_vlocation_cache_update ,
} ;
# endif
/*****************************************************************************/
/*
* iterate through the VL servers in a cell until one of them admits knowing
* about the volume in question
* - caller must have cell - > vl_sem write - locked
*/
static int afs_vlocation_access_vl_by_name ( struct afs_vlocation * vlocation ,
const char * name ,
unsigned namesz ,
struct afs_cache_vlocation * vldb )
{
struct afs_server * server = NULL ;
struct afs_cell * cell = vlocation - > cell ;
int count , ret ;
_enter ( " %s,%*.*s,%u " , cell - > name , namesz , namesz , name , namesz ) ;
ret = - ENOMEDIUM ;
for ( count = cell - > vl_naddrs ; count > 0 ; count - - ) {
_debug ( " CellServ[%hu]: %08x " ,
cell - > vl_curr_svix ,
cell - > vl_addrs [ cell - > vl_curr_svix ] . s_addr ) ;
/* try and create a server */
ret = afs_server_lookup ( cell ,
& cell - > vl_addrs [ cell - > vl_curr_svix ] ,
& server ) ;
switch ( ret ) {
case 0 :
break ;
case - ENOMEM :
case - ENONET :
goto out ;
default :
goto rotate ;
}
/* attempt to access the VL server */
ret = afs_rxvl_get_entry_by_name ( server , name , namesz , vldb ) ;
switch ( ret ) {
case 0 :
afs_put_server ( server ) ;
goto out ;
case - ENOMEM :
case - ENONET :
case - ENETUNREACH :
case - EHOSTUNREACH :
case - ECONNREFUSED :
down_write ( & server - > sem ) ;
if ( server - > vlserver ) {
rxrpc_put_connection ( server - > vlserver ) ;
server - > vlserver = NULL ;
}
up_write ( & server - > sem ) ;
afs_put_server ( server ) ;
if ( ret = = - ENOMEM | | ret = = - ENONET )
goto out ;
goto rotate ;
case - ENOMEDIUM :
afs_put_server ( server ) ;
goto out ;
default :
afs_put_server ( server ) ;
ret = - ENOMEDIUM ;
goto rotate ;
}
/* rotate the server records upon lookup failure */
rotate :
cell - > vl_curr_svix + + ;
cell - > vl_curr_svix % = cell - > vl_naddrs ;
}
out :
_leave ( " = %d " , ret ) ;
return ret ;
} /* end afs_vlocation_access_vl_by_name() */
/*****************************************************************************/
/*
* iterate through the VL servers in a cell until one of them admits knowing
* about the volume in question
* - caller must have cell - > vl_sem write - locked
*/
static int afs_vlocation_access_vl_by_id ( struct afs_vlocation * vlocation ,
afs_volid_t volid ,
afs_voltype_t voltype ,
struct afs_cache_vlocation * vldb )
{
struct afs_server * server = NULL ;
struct afs_cell * cell = vlocation - > cell ;
int count , ret ;
_enter ( " %s,%x,%d, " , cell - > name , volid , voltype ) ;
ret = - ENOMEDIUM ;
for ( count = cell - > vl_naddrs ; count > 0 ; count - - ) {
_debug ( " CellServ[%hu]: %08x " ,
cell - > vl_curr_svix ,
cell - > vl_addrs [ cell - > vl_curr_svix ] . s_addr ) ;
/* try and create a server */
ret = afs_server_lookup ( cell ,
& cell - > vl_addrs [ cell - > vl_curr_svix ] ,
& server ) ;
switch ( ret ) {
case 0 :
break ;
case - ENOMEM :
case - ENONET :
goto out ;
default :
goto rotate ;
}
/* attempt to access the VL server */
ret = afs_rxvl_get_entry_by_id ( server , volid , voltype , vldb ) ;
switch ( ret ) {
case 0 :
afs_put_server ( server ) ;
goto out ;
case - ENOMEM :
case - ENONET :
case - ENETUNREACH :
case - EHOSTUNREACH :
case - ECONNREFUSED :
down_write ( & server - > sem ) ;
if ( server - > vlserver ) {
rxrpc_put_connection ( server - > vlserver ) ;
server - > vlserver = NULL ;
}
up_write ( & server - > sem ) ;
afs_put_server ( server ) ;
if ( ret = = - ENOMEM | | ret = = - ENONET )
goto out ;
goto rotate ;
case - ENOMEDIUM :
afs_put_server ( server ) ;
goto out ;
default :
afs_put_server ( server ) ;
ret = - ENOMEDIUM ;
goto rotate ;
}
/* rotate the server records upon lookup failure */
rotate :
cell - > vl_curr_svix + + ;
cell - > vl_curr_svix % = cell - > vl_naddrs ;
}
out :
_leave ( " = %d " , ret ) ;
return ret ;
} /* end afs_vlocation_access_vl_by_id() */
/*****************************************************************************/
/*
* lookup volume location
* - caller must have cell - > vol_sem write - locked
* - 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
*/
int afs_vlocation_lookup ( struct afs_cell * cell ,
const char * name ,
unsigned namesz ,
struct afs_vlocation * * _vlocation )
{
struct afs_cache_vlocation vldb ;
struct afs_vlocation * vlocation ;
afs_voltype_t voltype ;
afs_volid_t vid ;
int active = 0 , ret ;
_enter ( " {%s},%*.*s,%u, " , cell - > name , namesz , namesz , name , namesz ) ;
if ( namesz > sizeof ( vlocation - > vldb . name ) ) {
_leave ( " = -ENAMETOOLONG " ) ;
return - ENAMETOOLONG ;
}
/* search the cell's active list first */
list_for_each_entry ( vlocation , & cell - > vl_list , link ) {
if ( namesz < sizeof ( vlocation - > vldb . name ) & &
vlocation - > vldb . name [ namesz ] ! = ' \0 ' )
continue ;
if ( memcmp ( vlocation - > vldb . name , name , namesz ) = = 0 )
goto found_in_memory ;
}
/* search the cell's graveyard list second */
spin_lock ( & cell - > vl_gylock ) ;
list_for_each_entry ( vlocation , & cell - > vl_graveyard , link ) {
if ( namesz < sizeof ( vlocation - > vldb . name ) & &
vlocation - > vldb . name [ namesz ] ! = ' \0 ' )
continue ;
if ( memcmp ( vlocation - > vldb . name , name , namesz ) = = 0 )
goto found_in_graveyard ;
}
spin_unlock ( & cell - > vl_gylock ) ;
/* not in the cell's in-memory lists - create a new record */
vlocation = kmalloc ( sizeof ( struct afs_vlocation ) , GFP_KERNEL ) ;
if ( ! vlocation )
return - ENOMEM ;
memset ( vlocation , 0 , sizeof ( struct afs_vlocation ) ) ;
atomic_set ( & vlocation - > usage , 1 ) ;
INIT_LIST_HEAD ( & vlocation - > link ) ;
rwlock_init ( & vlocation - > lock ) ;
memcpy ( vlocation - > vldb . name , name , namesz ) ;
afs_timer_init ( & vlocation - > timeout , & afs_vlocation_timer_ops ) ;
afs_timer_init ( & vlocation - > upd_timer , & afs_vlocation_update_timer_ops ) ;
afs_async_op_init ( & vlocation - > upd_op , & afs_vlocation_update_op_ops ) ;
afs_get_cell ( cell ) ;
vlocation - > cell = cell ;
list_add_tail ( & vlocation - > link , & cell - > vl_list ) ;
# ifdef AFS_CACHING_SUPPORT
/* we want to store it in the cache, plus it might already be
* encached */
cachefs_acquire_cookie ( cell - > cache ,
& afs_volume_cache_index_def ,
vlocation ,
& vlocation - > cache ) ;
if ( vlocation - > valid )
goto found_in_cache ;
# endif
/* try to look up an unknown volume in the cell VL databases by name */
ret = afs_vlocation_access_vl_by_name ( vlocation , name , namesz , & vldb ) ;
if ( ret < 0 ) {
printk ( " kAFS: failed to locate '%*.*s' in cell '%s' \n " ,
namesz , namesz , name , cell - > name ) ;
goto error ;
}
goto found_on_vlserver ;
found_in_graveyard :
/* found in the graveyard - resurrect */
_debug ( " found in graveyard " ) ;
atomic_inc ( & vlocation - > usage ) ;
2006-06-26 11:24:46 +04:00
list_move_tail ( & vlocation - > link , & cell - > vl_list ) ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & cell - > vl_gylock ) ;
afs_kafstimod_del_timer ( & vlocation - > timeout ) ;
goto active ;
found_in_memory :
/* found in memory - check to see if it's active */
_debug ( " found in memory " ) ;
atomic_inc ( & vlocation - > usage ) ;
active :
active = 1 ;
# ifdef AFS_CACHING_SUPPORT
found_in_cache :
# endif
/* try to look up a cached volume in the cell VL databases by ID */
_debug ( " found in cache " ) ;
_debug ( " Locally Cached: %s %02x { %08x(%x) %08x(%x) %08x(%x) } " ,
vlocation - > vldb . name ,
vlocation - > vldb . vidmask ,
ntohl ( vlocation - > vldb . servers [ 0 ] . s_addr ) ,
vlocation - > vldb . srvtmask [ 0 ] ,
ntohl ( vlocation - > vldb . servers [ 1 ] . s_addr ) ,
vlocation - > vldb . srvtmask [ 1 ] ,
ntohl ( vlocation - > vldb . servers [ 2 ] . s_addr ) ,
vlocation - > vldb . srvtmask [ 2 ]
) ;
_debug ( " Vids: %08x %08x %08x " ,
vlocation - > vldb . vid [ 0 ] ,
vlocation - > vldb . vid [ 1 ] ,
vlocation - > vldb . vid [ 2 ] ) ;
if ( vlocation - > vldb . vidmask & AFS_VOL_VTM_RW ) {
vid = vlocation - > vldb . vid [ 0 ] ;
voltype = AFSVL_RWVOL ;
}
else if ( vlocation - > vldb . vidmask & AFS_VOL_VTM_RO ) {
vid = vlocation - > vldb . vid [ 1 ] ;
voltype = AFSVL_ROVOL ;
}
else if ( vlocation - > vldb . vidmask & AFS_VOL_VTM_BAK ) {
vid = vlocation - > vldb . vid [ 2 ] ;
voltype = AFSVL_BACKVOL ;
}
else {
BUG ( ) ;
vid = 0 ;
voltype = 0 ;
}
ret = afs_vlocation_access_vl_by_id ( vlocation , vid , voltype , & vldb ) ;
switch ( ret ) {
/* net error */
default :
printk ( " kAFS: failed to volume '%*.*s' (%x) up in '%s': %d \n " ,
namesz , namesz , name , vid , cell - > name , ret ) ;
goto error ;
/* pulled from local cache into memory */
case 0 :
goto found_on_vlserver ;
/* uh oh... looks like the volume got deleted */
case - ENOMEDIUM :
printk ( " kAFS: volume '%*.*s' (%x) does not exist '%s' \n " ,
namesz , namesz , name , vid , cell - > name ) ;
/* TODO: make existing record unavailable */
goto error ;
}
found_on_vlserver :
_debug ( " Done VL Lookup: %*.*s %02x { %08x(%x) %08x(%x) %08x(%x) } " ,
namesz , namesz , 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 ]
) ;
_debug ( " Vids: %08x %08x %08x " , vldb . vid [ 0 ] , vldb . vid [ 1 ] , vldb . vid [ 2 ] ) ;
if ( ( namesz < sizeof ( vlocation - > vldb . name ) & &
vlocation - > vldb . name [ namesz ] ! = ' \0 ' ) | |
memcmp ( vldb . name , name , namesz ) ! = 0 )
printk ( " kAFS: name of volume '%*.*s' changed to '%s' on server \n " ,
namesz , namesz , name , vldb . name ) ;
memcpy ( & vlocation - > vldb , & vldb , sizeof ( vlocation - > vldb ) ) ;
afs_kafstimod_add_timer ( & vlocation - > upd_timer , 10 * HZ ) ;
# ifdef AFS_CACHING_SUPPORT
/* update volume entry in local cache */
cachefs_update_cookie ( vlocation - > cache ) ;
# endif
* _vlocation = vlocation ;
_leave ( " = 0 (%p) " , vlocation ) ;
return 0 ;
error :
if ( vlocation ) {
if ( active ) {
__afs_put_vlocation ( vlocation ) ;
}
else {
list_del ( & vlocation - > link ) ;
# ifdef AFS_CACHING_SUPPORT
cachefs_relinquish_cookie ( vlocation - > cache , 0 ) ;
# endif
afs_put_cell ( vlocation - > cell ) ;
kfree ( vlocation ) ;
}
}
_leave ( " = %d " , ret ) ;
return ret ;
} /* end afs_vlocation_lookup() */
/*****************************************************************************/
/*
* finish using a volume location record
* - caller must have cell - > vol_sem write - locked
*/
static void __afs_put_vlocation ( struct afs_vlocation * vlocation )
{
struct afs_cell * cell ;
if ( ! vlocation )
return ;
_enter ( " %s " , vlocation - > vldb . name ) ;
cell = vlocation - > cell ;
/* sanity check */
BUG_ON ( atomic_read ( & vlocation - > usage ) < = 0 ) ;
spin_lock ( & cell - > vl_gylock ) ;
if ( likely ( ! atomic_dec_and_test ( & vlocation - > usage ) ) ) {
spin_unlock ( & cell - > vl_gylock ) ;
_leave ( " " ) ;
return ;
}
/* move to graveyard queue */
2006-06-26 11:24:46 +04:00
list_move_tail ( & vlocation - > link , & cell - > vl_graveyard ) ;
2005-04-17 02:20:36 +04:00
/* remove from pending timeout queue (refcounted if actually being
* updated ) */
list_del_init ( & vlocation - > upd_op . link ) ;
/* time out in 10 secs */
afs_kafstimod_del_timer ( & vlocation - > upd_timer ) ;
afs_kafstimod_add_timer ( & vlocation - > timeout , 10 * HZ ) ;
spin_unlock ( & cell - > vl_gylock ) ;
_leave ( " [killed] " ) ;
} /* end __afs_put_vlocation() */
/*****************************************************************************/
/*
* finish using a volume location record
*/
void afs_put_vlocation ( struct afs_vlocation * vlocation )
{
if ( vlocation ) {
struct afs_cell * cell = vlocation - > cell ;
down_write ( & cell - > vl_sem ) ;
__afs_put_vlocation ( vlocation ) ;
up_write ( & cell - > vl_sem ) ;
}
} /* end afs_put_vlocation() */
/*****************************************************************************/
/*
* timeout vlocation record
* - removes from the cell ' s graveyard if the usage count is zero
*/
void afs_vlocation_do_timeout ( struct afs_vlocation * vlocation )
{
struct afs_cell * cell ;
_enter ( " %s " , vlocation - > vldb . name ) ;
cell = vlocation - > cell ;
BUG_ON ( atomic_read ( & vlocation - > usage ) < 0 ) ;
/* remove from graveyard if still dead */
spin_lock ( & cell - > vl_gylock ) ;
if ( atomic_read ( & vlocation - > usage ) = = 0 )
list_del_init ( & vlocation - > link ) ;
else
vlocation = NULL ;
spin_unlock ( & cell - > vl_gylock ) ;
if ( ! vlocation ) {
_leave ( " " ) ;
return ; /* resurrected */
}
/* we can now destroy it properly */
# ifdef AFS_CACHING_SUPPORT
cachefs_relinquish_cookie ( vlocation - > cache , 0 ) ;
# endif
afs_put_cell ( cell ) ;
kfree ( vlocation ) ;
_leave ( " [destroyed] " ) ;
} /* end afs_vlocation_do_timeout() */
/*****************************************************************************/
/*
* send an update operation to the currently selected server
*/
static int afs_vlocation_update_begin ( struct afs_vlocation * vlocation )
{
afs_voltype_t voltype ;
afs_volid_t vid ;
int ret ;
_enter ( " %s{ufs=%u ucs=%u} " ,
vlocation - > vldb . name ,
vlocation - > upd_first_svix ,
vlocation - > upd_curr_svix ) ;
/* try to look up a cached volume in the cell VL databases by ID */
if ( vlocation - > vldb . vidmask & AFS_VOL_VTM_RW ) {
vid = vlocation - > vldb . vid [ 0 ] ;
voltype = AFSVL_RWVOL ;
}
else if ( vlocation - > vldb . vidmask & AFS_VOL_VTM_RO ) {
vid = vlocation - > vldb . vid [ 1 ] ;
voltype = AFSVL_ROVOL ;
}
else if ( vlocation - > vldb . vidmask & AFS_VOL_VTM_BAK ) {
vid = vlocation - > vldb . vid [ 2 ] ;
voltype = AFSVL_BACKVOL ;
}
else {
BUG ( ) ;
vid = 0 ;
voltype = 0 ;
}
/* contact the chosen server */
ret = afs_server_lookup (
vlocation - > cell ,
& vlocation - > cell - > vl_addrs [ vlocation - > upd_curr_svix ] ,
& vlocation - > upd_op . server ) ;
switch ( ret ) {
case 0 :
break ;
case - ENOMEM :
case - ENONET :
default :
_leave ( " = %d " , ret ) ;
return ret ;
}
/* initiate the update operation */
ret = afs_rxvl_get_entry_by_id_async ( & vlocation - > upd_op , vid , voltype ) ;
if ( ret < 0 ) {
_leave ( " = %d " , ret ) ;
return ret ;
}
_leave ( " = %d " , ret ) ;
return ret ;
} /* end afs_vlocation_update_begin() */
/*****************************************************************************/
/*
* abandon updating a VL record
* - does not restart the update timer
*/
static void afs_vlocation_update_abandon ( struct afs_vlocation * vlocation ,
afs_vlocation_upd_t state ,
int ret )
{
_enter ( " %s,%u " , vlocation - > vldb . name , state ) ;
if ( ret < 0 )
printk ( " kAFS: Abandoning VL update '%s': %d \n " ,
vlocation - > vldb . name , ret ) ;
/* discard the server record */
afs_put_server ( vlocation - > upd_op . server ) ;
vlocation - > upd_op . server = NULL ;
spin_lock ( & afs_vlocation_update_lock ) ;
afs_vlocation_update = NULL ;
vlocation - > upd_state = state ;
/* TODO: start updating next VL record on pending list */
spin_unlock ( & afs_vlocation_update_lock ) ;
_leave ( " " ) ;
} /* end afs_vlocation_update_abandon() */
/*****************************************************************************/
/*
* handle periodic update timeouts and busy retry timeouts
* - called from kafstimod
*/
static void afs_vlocation_update_timer ( struct afs_timer * timer )
{
struct afs_vlocation * vlocation =
list_entry ( timer , struct afs_vlocation , upd_timer ) ;
int ret ;
_enter ( " %s " , vlocation - > vldb . name ) ;
/* only update if not in the graveyard (defend against putting too) */
spin_lock ( & vlocation - > cell - > vl_gylock ) ;
if ( ! atomic_read ( & vlocation - > usage ) )
goto out_unlock1 ;
spin_lock ( & afs_vlocation_update_lock ) ;
/* if we were woken up due to EBUSY sleep then restart immediately if
* possible or else jump to front of pending queue */
if ( vlocation - > upd_state = = AFS_VLUPD_BUSYSLEEP ) {
if ( afs_vlocation_update ) {
list_add ( & vlocation - > upd_op . link ,
& afs_vlocation_update_pendq ) ;
}
else {
afs_get_vlocation ( vlocation ) ;
afs_vlocation_update = vlocation ;
vlocation - > upd_state = AFS_VLUPD_INPROGRESS ;
}
goto out_unlock2 ;
}
/* put on pending queue if there's already another update in progress */
if ( afs_vlocation_update ) {
vlocation - > upd_state = AFS_VLUPD_PENDING ;
list_add_tail ( & vlocation - > upd_op . link ,
& afs_vlocation_update_pendq ) ;
goto out_unlock2 ;
}
/* hold a ref on it while actually updating */
afs_get_vlocation ( vlocation ) ;
afs_vlocation_update = vlocation ;
vlocation - > upd_state = AFS_VLUPD_INPROGRESS ;
spin_unlock ( & afs_vlocation_update_lock ) ;
spin_unlock ( & vlocation - > cell - > vl_gylock ) ;
/* okay... we can start the update */
_debug ( " BEGIN VL UPDATE [%s] " , vlocation - > vldb . name ) ;
vlocation - > upd_first_svix = vlocation - > cell - > vl_curr_svix ;
vlocation - > upd_curr_svix = vlocation - > upd_first_svix ;
vlocation - > upd_rej_cnt = 0 ;
vlocation - > upd_busy_cnt = 0 ;
ret = afs_vlocation_update_begin ( vlocation ) ;
if ( ret < 0 ) {
afs_vlocation_update_abandon ( vlocation , AFS_VLUPD_SLEEP , ret ) ;
afs_kafstimod_add_timer ( & vlocation - > upd_timer ,
AFS_VLDB_TIMEOUT ) ;
afs_put_vlocation ( vlocation ) ;
}
_leave ( " " ) ;
return ;
out_unlock2 :
spin_unlock ( & afs_vlocation_update_lock ) ;
out_unlock1 :
spin_unlock ( & vlocation - > cell - > vl_gylock ) ;
_leave ( " " ) ;
return ;
} /* end afs_vlocation_update_timer() */
/*****************************************************************************/
/*
* attend to an update operation upon which an event happened
* - called in kafsasyncd context
*/
static void afs_vlocation_update_attend ( struct afs_async_op * op )
{
struct afs_cache_vlocation vldb ;
struct afs_vlocation * vlocation =
list_entry ( op , struct afs_vlocation , upd_op ) ;
unsigned tmp ;
int ret ;
_enter ( " %s " , vlocation - > vldb . name ) ;
ret = afs_rxvl_get_entry_by_id_async2 ( op , & vldb ) ;
switch ( ret ) {
case - EAGAIN :
_leave ( " [unfinished] " ) ;
return ;
case 0 :
_debug ( " END VL UPDATE: %d \n " , ret ) ;
vlocation - > valid = 1 ;
_debug ( " Done VL Lookup: %02x { %08x(%x) %08x(%x) %08x(%x) } " ,
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 ]
) ;
_debug ( " Vids: %08x %08x %08x " ,
vldb . vid [ 0 ] , vldb . vid [ 1 ] , vldb . vid [ 2 ] ) ;
afs_vlocation_update_abandon ( vlocation , AFS_VLUPD_SLEEP , 0 ) ;
down_write ( & vlocation - > cell - > vl_sem ) ;
/* actually update the cache */
if ( strncmp ( vldb . name , vlocation - > vldb . name ,
sizeof ( vlocation - > vldb . name ) ) ! = 0 )
printk ( " kAFS: name of volume '%s' "
" changed to '%s' on server \n " ,
vlocation - > vldb . name , vldb . name ) ;
memcpy ( & vlocation - > vldb , & vldb , sizeof ( vlocation - > vldb ) ) ;
#if 0
/* TODO update volume entry in local cache */
# endif
up_write ( & vlocation - > cell - > vl_sem ) ;
if ( ret < 0 )
printk ( " kAFS: failed to update local cache: %d \n " , ret ) ;
afs_kafstimod_add_timer ( & vlocation - > upd_timer ,
AFS_VLDB_TIMEOUT ) ;
afs_put_vlocation ( vlocation ) ;
_leave ( " [found] " ) ;
return ;
case - ENOMEDIUM :
vlocation - > upd_rej_cnt + + ;
goto try_next ;
/* the server is locked - retry in a very short while */
case - EBUSY :
vlocation - > upd_busy_cnt + + ;
if ( vlocation - > upd_busy_cnt > 3 )
goto try_next ; /* too many retries */
afs_vlocation_update_abandon ( vlocation ,
AFS_VLUPD_BUSYSLEEP , 0 ) ;
afs_kafstimod_add_timer ( & vlocation - > upd_timer , HZ / 2 ) ;
afs_put_vlocation ( vlocation ) ;
_leave ( " [busy] " ) ;
return ;
case - ENETUNREACH :
case - EHOSTUNREACH :
case - ECONNREFUSED :
case - EREMOTEIO :
/* record bad vlserver info in the cell too
* - TODO : use down_write_trylock ( ) if available
*/
if ( vlocation - > upd_curr_svix = = vlocation - > cell - > vl_curr_svix )
vlocation - > cell - > vl_curr_svix =
vlocation - > cell - > vl_curr_svix %
vlocation - > cell - > vl_naddrs ;
case - EBADRQC :
case - EINVAL :
case - EACCES :
case - EBADMSG :
goto try_next ;
default :
goto abandon ;
}
/* try contacting the next server */
try_next :
vlocation - > upd_busy_cnt = 0 ;
/* discard the server record */
afs_put_server ( vlocation - > upd_op . server ) ;
vlocation - > upd_op . server = NULL ;
tmp = vlocation - > cell - > vl_naddrs ;
if ( tmp = = 0 )
goto abandon ;
vlocation - > upd_curr_svix + + ;
if ( vlocation - > upd_curr_svix > = tmp )
vlocation - > upd_curr_svix = 0 ;
if ( vlocation - > upd_first_svix > = tmp )
vlocation - > upd_first_svix = tmp - 1 ;
/* move to the next server */
if ( vlocation - > upd_curr_svix ! = vlocation - > upd_first_svix ) {
afs_vlocation_update_begin ( vlocation ) ;
_leave ( " [next] " ) ;
return ;
}
/* run out of servers to try - was the volume rejected? */
if ( vlocation - > upd_rej_cnt > 0 ) {
printk ( " kAFS: Active volume no longer valid '%s' \n " ,
vlocation - > vldb . name ) ;
vlocation - > valid = 0 ;
afs_vlocation_update_abandon ( vlocation , AFS_VLUPD_SLEEP , 0 ) ;
afs_kafstimod_add_timer ( & vlocation - > upd_timer ,
AFS_VLDB_TIMEOUT ) ;
afs_put_vlocation ( vlocation ) ;
_leave ( " [invalidated] " ) ;
return ;
}
/* abandon the update */
abandon :
afs_vlocation_update_abandon ( vlocation , AFS_VLUPD_SLEEP , ret ) ;
afs_kafstimod_add_timer ( & vlocation - > upd_timer , HZ * 10 ) ;
afs_put_vlocation ( vlocation ) ;
_leave ( " [abandoned] " ) ;
} /* end afs_vlocation_update_attend() */
/*****************************************************************************/
/*
* deal with an update operation being discarded
* - called in kafsasyncd context when it ' s dying due to rmmod
* - the call has already been aborted and put ( ) ' d
*/
static void afs_vlocation_update_discard ( struct afs_async_op * op )
{
struct afs_vlocation * vlocation =
list_entry ( op , struct afs_vlocation , upd_op ) ;
_enter ( " %s " , vlocation - > vldb . name ) ;
afs_put_server ( op - > server ) ;
op - > server = NULL ;
afs_put_vlocation ( vlocation ) ;
_leave ( " " ) ;
} /* end afs_vlocation_update_discard() */
/*****************************************************************************/
/*
* match a VLDB record stored in the cache
* - may also load target from entry
*/
# ifdef AFS_CACHING_SUPPORT
static cachefs_match_val_t afs_vlocation_cache_match ( void * target ,
const void * entry )
{
const struct afs_cache_vlocation * vldb = entry ;
struct afs_vlocation * vlocation = target ;
_enter ( " {%s},{%s} " , vlocation - > vldb . name , vldb - > name ) ;
if ( strncmp ( vlocation - > vldb . name , vldb - > name , sizeof ( vldb - > name ) ) = = 0
) {
if ( ! vlocation - > valid | |
vlocation - > vldb . rtime = = vldb - > rtime
) {
vlocation - > vldb = * vldb ;
vlocation - > valid = 1 ;
_leave ( " = SUCCESS [c->m] " ) ;
return CACHEFS_MATCH_SUCCESS ;
}
/* need to update cache if cached info differs */
else if ( memcmp ( & vlocation - > vldb , vldb , sizeof ( * vldb ) ) ! = 0 ) {
/* delete if VIDs for this name differ */
if ( memcmp ( & vlocation - > vldb . vid ,
& vldb - > vid ,
sizeof ( vldb - > vid ) ) ! = 0 ) {
_leave ( " = DELETE " ) ;
return CACHEFS_MATCH_SUCCESS_DELETE ;
}
_leave ( " = UPDATE " ) ;
return CACHEFS_MATCH_SUCCESS_UPDATE ;
}
else {
_leave ( " = SUCCESS " ) ;
return CACHEFS_MATCH_SUCCESS ;
}
}
_leave ( " = FAILED " ) ;
return CACHEFS_MATCH_FAILED ;
} /* end afs_vlocation_cache_match() */
# endif
/*****************************************************************************/
/*
* update a VLDB record stored in the cache
*/
# ifdef AFS_CACHING_SUPPORT
static void afs_vlocation_cache_update ( void * source , void * entry )
{
struct afs_cache_vlocation * vldb = entry ;
struct afs_vlocation * vlocation = source ;
_enter ( " " ) ;
* vldb = vlocation - > vldb ;
} /* end afs_vlocation_cache_update() */
# endif