2005-04-17 02:20:36 +04:00
/*
* linux / fs / lockd / clntlock . c
*
* Lock handling for the client side NLM implementation
*
* Copyright ( C ) 1996 , Olaf Kirch < okir @ monad . swb . de >
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/time.h>
# include <linux/nfs_fs.h>
# include <linux/sunrpc/clnt.h>
# include <linux/sunrpc/svc.h>
# include <linux/lockd/lockd.h>
# include <linux/smp_lock.h>
# define NLMDBG_FACILITY NLMDBG_CLIENT
/*
* Local function prototypes
*/
static int reclaimer ( void * ptr ) ;
/*
* The following functions handle blocking and granting from the
* client perspective .
*/
/*
* This is the representation of a blocked client lock .
*/
struct nlm_wait {
2005-06-22 21:16:31 +04:00
struct list_head b_list ; /* linked list */
2005-04-17 02:20:36 +04:00
wait_queue_head_t b_wait ; /* where to wait on */
struct nlm_host * b_host ;
struct file_lock * b_lock ; /* local file lock */
unsigned short b_reclaim ; /* got to reclaim lock */
2006-12-13 11:35:03 +03:00
__be32 b_status ; /* grant callback status */
2005-04-17 02:20:36 +04:00
} ;
2005-06-22 21:16:31 +04:00
static LIST_HEAD ( nlm_blocked ) ;
2005-04-17 02:20:36 +04:00
/*
2005-06-22 21:16:31 +04:00
* Queue up a lock for blocking so that the GRANTED request can see it
2005-04-17 02:20:36 +04:00
*/
2006-03-20 21:44:44 +03:00
struct nlm_wait * nlmclnt_prepare_block ( struct nlm_host * host , struct file_lock * fl )
2005-06-22 21:16:31 +04:00
{
struct nlm_wait * block ;
block = kmalloc ( sizeof ( * block ) , GFP_KERNEL ) ;
2006-03-20 21:44:44 +03:00
if ( block ! = NULL ) {
block - > b_host = host ;
block - > b_lock = fl ;
init_waitqueue_head ( & block - > b_wait ) ;
2006-12-13 11:35:03 +03:00
block - > b_status = nlm_lck_blocked ;
2006-03-20 21:44:44 +03:00
list_add ( & block - > b_list , & nlm_blocked ) ;
}
return block ;
2005-06-22 21:16:31 +04:00
}
2006-03-20 21:44:44 +03:00
void nlmclnt_finish_block ( struct nlm_wait * block )
2005-04-17 02:20:36 +04:00
{
2005-06-22 21:16:31 +04:00
if ( block = = NULL )
return ;
list_del ( & block - > b_list ) ;
kfree ( block ) ;
}
2005-04-17 02:20:36 +04:00
2005-06-22 21:16:31 +04:00
/*
* Block on a lock
*/
2006-03-20 21:44:44 +03:00
int nlmclnt_block ( struct nlm_wait * block , struct nlm_rqst * req , long timeout )
2005-06-22 21:16:31 +04:00
{
long ret ;
2005-04-17 02:20:36 +04:00
2005-06-22 21:16:31 +04:00
/* A borken server might ask us to block even if we didn't
* request it . Just say no !
*/
2006-03-20 21:44:44 +03:00
if ( block = = NULL )
2005-06-22 21:16:31 +04:00
return - EAGAIN ;
2005-04-17 02:20:36 +04:00
/* Go to sleep waiting for GRANT callback. Some servers seem
* to lose callbacks , however , so we ' re going to poll from
* time to time just to make sure .
*
* For now , the retry frequency is pretty high ; normally
* a 1 minute timeout would do . See the comment before
* nlmclnt_lock for an explanation .
*/
2005-06-22 21:16:31 +04:00
ret = wait_event_interruptible_timeout ( block - > b_wait ,
2006-12-13 11:35:03 +03:00
block - > b_status ! = nlm_lck_blocked ,
2005-06-22 21:16:31 +04:00
timeout ) ;
2006-03-20 21:44:44 +03:00
if ( ret < 0 )
return - ERESTARTSYS ;
req - > a_res . status = block - > b_status ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*
* The server lockd has called us back to tell us the lock was granted
*/
2006-10-20 10:28:46 +04:00
__be32 nlmclnt_grant ( const struct sockaddr_in * addr , const struct nlm_lock * lock )
2005-04-17 02:20:36 +04:00
{
2006-02-15 00:53:04 +03:00
const struct file_lock * fl = & lock - > fl ;
const struct nfs_fh * fh = & lock - > fh ;
2005-04-17 02:20:36 +04:00
struct nlm_wait * block ;
2006-10-20 10:28:46 +04:00
__be32 res = nlm_lck_denied ;
2005-04-17 02:20:36 +04:00
/*
* Look up blocked request based on arguments .
* Warning : must not use cookie to match it !
*/
2005-06-22 21:16:31 +04:00
list_for_each_entry ( block , & nlm_blocked , b_list ) {
2006-02-15 00:53:04 +03:00
struct file_lock * fl_blocked = block - > b_lock ;
2006-03-20 21:44:06 +03:00
if ( fl_blocked - > fl_start ! = fl - > fl_start )
continue ;
if ( fl_blocked - > fl_end ! = fl - > fl_end )
continue ;
/*
* Careful ! The NLM server will return the 32 - bit " pid " that
* we put on the wire : in this case the lockowner " pid " .
*/
if ( fl_blocked - > fl_u . nfs_fl . owner - > pid ! = lock - > svid )
2006-02-15 00:53:04 +03:00
continue ;
if ( ! nlm_cmp_addr ( & block - > b_host - > h_addr , addr ) )
continue ;
2006-12-08 13:37:18 +03:00
if ( nfs_compare_fh ( NFS_FH ( fl_blocked - > fl_file - > f_path . dentry - > d_inode ) , fh ) ! = 0 )
2006-02-15 00:53:04 +03:00
continue ;
/* Alright, we found a lock. Set the return status
* and wake up the caller
*/
2006-12-13 11:35:03 +03:00
block - > b_status = nlm_granted ;
2006-02-15 00:53:04 +03:00
wake_up ( & block - > b_wait ) ;
res = nlm_granted ;
2005-04-17 02:20:36 +04:00
}
2005-06-22 21:16:31 +04:00
return res ;
2005-04-17 02:20:36 +04:00
}
/*
* The following procedures deal with the recovery of locks after a
* server crash .
*/
/*
* Reclaim all locks on server host . We do this by spawning a separate
* reclaimer thread .
*/
void
2006-10-04 13:15:55 +04:00
nlmclnt_recovery ( struct nlm_host * host )
2005-04-17 02:20:36 +04:00
{
2006-06-09 17:40:27 +04:00
if ( ! host - > h_reclaiming + + ) {
2005-04-17 02:20:36 +04:00
nlm_get_host ( host ) ;
__module_get ( THIS_MODULE ) ;
2007-05-11 09:55:07 +04:00
if ( kernel_thread ( reclaimer , host , CLONE_FS | CLONE_FILES ) < 0 )
2005-04-17 02:20:36 +04:00
module_put ( THIS_MODULE ) ;
}
}
static int
reclaimer ( void * ptr )
{
struct nlm_host * host = ( struct nlm_host * ) ptr ;
struct nlm_wait * block ;
2006-03-20 21:44:40 +03:00
struct file_lock * fl , * next ;
2006-06-09 17:40:27 +04:00
u32 nsmstate ;
2005-04-17 02:20:36 +04:00
daemonize ( " %s-reclaim " , host - > h_name ) ;
allow_signal ( SIGKILL ) ;
2006-10-04 13:15:55 +04:00
down_write ( & host - > h_rwsem ) ;
2005-04-17 02:20:36 +04:00
/* This one ensures that our parent doesn't terminate while the
* reclaim is in progress */
lock_kernel ( ) ;
2006-10-02 13:17:53 +04:00
lockd_up ( 0 ) ; /* note: this cannot fail as lockd is already running */
2005-04-17 02:20:36 +04:00
2007-01-30 00:19:51 +03:00
dprintk ( " lockd: reclaiming locks for host %s \n " , host - > h_name ) ;
2006-10-04 13:15:55 +04:00
2005-04-17 02:20:36 +04:00
restart :
2006-06-09 17:40:27 +04:00
nsmstate = host - > h_nsmstate ;
2006-10-04 13:15:55 +04:00
/* Force a portmap getport - the peer's lockd will
* most likely end up on a different port .
*/
2006-10-04 13:16:04 +04:00
host - > h_nextrebind = jiffies ;
2006-10-04 13:15:55 +04:00
nlm_rebind_host ( host ) ;
/* First, reclaim all locks that have been granted. */
list_splice_init ( & host - > h_granted , & host - > h_reclaim ) ;
2006-03-20 21:44:40 +03:00
list_for_each_entry_safe ( fl , next , & host - > h_reclaim , fl_u . nfs_fl . list ) {
2006-03-20 21:44:41 +03:00
list_del_init ( & fl - > fl_u . nfs_fl . list ) ;
2005-04-17 02:20:36 +04:00
2006-10-04 13:15:55 +04:00
/* Why are we leaking memory here? --okir */
2005-04-17 02:20:36 +04:00
if ( signalled ( ) )
2006-03-20 21:44:41 +03:00
continue ;
2006-06-09 17:40:27 +04:00
if ( nlmclnt_reclaim ( host , fl ) ! = 0 )
continue ;
list_add_tail ( & fl - > fl_u . nfs_fl . list , & host - > h_granted ) ;
if ( host - > h_nsmstate ! = nsmstate ) {
/* Argh! The server rebooted again! */
goto restart ;
}
2005-04-17 02:20:36 +04:00
}
2006-10-04 13:15:55 +04:00
host - > h_reclaiming = 0 ;
up_write ( & host - > h_rwsem ) ;
2007-01-30 00:19:51 +03:00
dprintk ( " NLM: done reclaiming locks for host %s \n " , host - > h_name ) ;
2005-04-17 02:20:36 +04:00
/* Now, wake up all processes that sleep on a blocked lock */
2005-06-22 21:16:31 +04:00
list_for_each_entry ( block , & nlm_blocked , b_list ) {
2005-04-17 02:20:36 +04:00
if ( block - > b_host = = host ) {
2006-12-13 11:35:03 +03:00
block - > b_status = nlm_lck_denied_grace_period ;
2005-04-17 02:20:36 +04:00
wake_up ( & block - > b_wait ) ;
}
}
/* Release host handle after use */
nlm_release_host ( host ) ;
lockd_down ( ) ;
unlock_kernel ( ) ;
module_put_and_exit ( 0 ) ;
}