2005-04-17 02:20:36 +04:00
/*
* linux / fs / lockd / svclock . c
*
* Handling of server - side locks , mostly of the blocked variety .
* This is the ugliest part of lockd because we tread on very thin ice .
* GRANT and CANCEL calls may get stuck , meet in mid - flight , etc .
* IMNSHO introducing the grant callback into the NLM protocol was one
* of the worst ideas Sun ever had . Except maybe for the idea of doing
* NFS file locking at all .
*
* I ' m trying hard to avoid race conditions by protecting most accesses
* to a file ' s list of blocked locks through a semaphore . The global
* list of blocked locks is not protected in this fashion however .
* Therefore , some functions ( such as the RPC callback for the async grant
* call ) move blocked locks towards the head of the list * while some other
* process might be traversing it * . This should not be a problem in
* practice , because this will only cause functions traversing the list
* to visit some blocks twice .
*
* Copyright ( C ) 1996 , Olaf Kirch < okir @ monad . swb . de >
*/
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/smp_lock.h>
# include <linux/sunrpc/clnt.h>
# include <linux/sunrpc/svc.h>
# include <linux/lockd/nlm.h>
# include <linux/lockd/lockd.h>
# define NLMDBG_FACILITY NLMDBG_SVCLOCK
# ifdef CONFIG_LOCKD_V4
# define nlm_deadlock nlm4_deadlock
# else
# define nlm_deadlock nlm_lck_denied
# endif
2006-03-20 21:44:39 +03:00
static void nlmsvc_release_block ( struct nlm_block * block ) ;
2005-04-17 02:20:36 +04:00
static void nlmsvc_insert_block ( struct nlm_block * block , unsigned long ) ;
2006-10-04 13:15:57 +04:00
static void nlmsvc_remove_block ( struct nlm_block * block ) ;
2006-01-03 11:55:04 +03:00
2006-03-20 21:44:39 +03:00
static int nlmsvc_setgrantargs ( struct nlm_rqst * call , struct nlm_lock * lock ) ;
static void nlmsvc_freegrantargs ( struct nlm_rqst * call ) ;
2006-01-03 11:55:04 +03:00
static const struct rpc_call_ops nlmsvc_grant_ops ;
2005-04-17 02:20:36 +04:00
/*
* The list of blocked locks to retry
*/
2006-10-04 13:15:57 +04:00
static LIST_HEAD ( nlm_blocked ) ;
2005-04-17 02:20:36 +04:00
/*
* Insert a blocked lock into the global list
*/
static void
nlmsvc_insert_block ( struct nlm_block * block , unsigned long when )
{
2006-10-04 13:15:57 +04:00
struct nlm_block * b ;
struct list_head * pos ;
2005-04-17 02:20:36 +04:00
dprintk ( " lockd: nlmsvc_insert_block(%p, %ld) \n " , block , when ) ;
2006-10-04 13:15:57 +04:00
if ( list_empty ( & block - > b_list ) ) {
kref_get ( & block - > b_count ) ;
} else {
list_del_init ( & block - > b_list ) ;
}
pos = & nlm_blocked ;
2005-04-17 02:20:36 +04:00
if ( when ! = NLM_NEVER ) {
if ( ( when + = jiffies ) = = NLM_NEVER )
when + + ;
2006-10-04 13:15:57 +04:00
list_for_each ( pos , & nlm_blocked ) {
b = list_entry ( pos , struct nlm_block , b_list ) ;
if ( time_after ( b - > b_when , when ) | | b - > b_when = = NLM_NEVER )
break ;
}
/* On normal exit from the loop, pos == &nlm_blocked,
* so we will be adding to the end of the list - good
*/
}
2005-04-17 02:20:36 +04:00
2006-10-04 13:15:57 +04:00
list_add_tail ( & block - > b_list , pos ) ;
2005-04-17 02:20:36 +04:00
block - > b_when = when ;
}
/*
* Remove a block from the global list
*/
2006-10-04 13:15:57 +04:00
static inline void
2005-04-17 02:20:36 +04:00
nlmsvc_remove_block ( struct nlm_block * block )
{
2006-10-04 13:15:57 +04:00
if ( ! list_empty ( & block - > b_list ) ) {
list_del_init ( & block - > b_list ) ;
nlmsvc_release_block ( block ) ;
2005-04-17 02:20:36 +04:00
}
}
/*
2006-03-20 21:44:47 +03:00
* Find a block for a given lock
2005-04-17 02:20:36 +04:00
*/
static struct nlm_block *
2006-03-20 21:44:47 +03:00
nlmsvc_lookup_block ( struct nlm_file * file , struct nlm_lock * lock )
2005-04-17 02:20:36 +04:00
{
2006-10-04 13:15:57 +04:00
struct nlm_block * block ;
2005-04-17 02:20:36 +04:00
struct file_lock * fl ;
dprintk ( " lockd: nlmsvc_lookup_block f=%p pd=%d %Ld-%Ld ty=%d \n " ,
file , lock - > fl . fl_pid ,
( long long ) lock - > fl . fl_start ,
( long long ) lock - > fl . fl_end , lock - > fl . fl_type ) ;
2006-10-04 13:15:57 +04:00
list_for_each_entry ( block , & nlm_blocked , b_list ) {
2006-03-20 21:44:45 +03:00
fl = & block - > b_call - > a_args . lock . fl ;
2005-04-17 02:20:36 +04:00
dprintk ( " lockd: check f=%p pd=%d %Ld-%Ld ty=%d cookie=%s \n " ,
block - > b_file , fl - > fl_pid ,
( long long ) fl - > fl_start ,
( long long ) fl - > fl_end , fl - > fl_type ,
2006-03-20 21:44:45 +03:00
nlmdbg_cookie2a ( & block - > b_call - > a_args . cookie ) ) ;
2005-04-17 02:20:36 +04:00
if ( block - > b_file = = file & & nlm_compare_locks ( fl , & lock - > fl ) ) {
2006-03-20 21:44:39 +03:00
kref_get ( & block - > b_count ) ;
2005-04-17 02:20:36 +04:00
return block ;
}
}
return NULL ;
}
static inline int nlm_cookie_match ( struct nlm_cookie * a , struct nlm_cookie * b )
{
if ( a - > len ! = b - > len )
return 0 ;
if ( memcmp ( a - > data , b - > data , a - > len ) )
return 0 ;
return 1 ;
}
/*
* Find a block with a given NLM cookie .
*/
static inline struct nlm_block *
2006-10-04 13:16:03 +04:00
nlmsvc_find_block ( struct nlm_cookie * cookie )
2005-04-17 02:20:36 +04:00
{
struct nlm_block * block ;
2006-10-04 13:15:57 +04:00
list_for_each_entry ( block , & nlm_blocked , b_list ) {
2006-10-04 13:16:03 +04:00
if ( nlm_cookie_match ( & block - > b_call - > a_args . cookie , cookie ) )
2006-10-04 13:15:57 +04:00
goto found ;
2005-04-17 02:20:36 +04:00
}
2006-10-04 13:15:57 +04:00
return NULL ;
found :
2006-10-04 13:16:03 +04:00
dprintk ( " nlmsvc_find_block(%s): block=%p \n " , nlmdbg_cookie2a ( cookie ) , block ) ;
2006-10-04 13:15:57 +04:00
kref_get ( & block - > b_count ) ;
2005-04-17 02:20:36 +04:00
return block ;
}
/*
* Create a block and initialize it .
*
* Note : we explicitly set the cookie of the grant reply to that of
* the blocked lock request . The spec explicitly mentions that the client
* should _not_ rely on the callback containing the same cookie as the
* request , but ( as I found out later ) that ' s because some implementations
* do just this . Never mind the standards comittees , they support our
* logging industries .
2006-10-04 13:16:03 +04:00
*
* 10 years later : I hope we can safely ignore these old and broken
* clients by now . Let ' s fix this so we can uniquely identify an incoming
* GRANTED_RES message by cookie , without having to rely on the client ' s IP
* address . - - okir
2005-04-17 02:20:36 +04:00
*/
static inline struct nlm_block *
nlmsvc_create_block ( struct svc_rqst * rqstp , struct nlm_file * file ,
2006-11-29 00:27:06 +03:00
struct nlm_lock * lock , struct nlm_cookie * cookie )
2005-04-17 02:20:36 +04:00
{
struct nlm_block * block ;
struct nlm_host * host ;
2006-03-20 21:44:45 +03:00
struct nlm_rqst * call = NULL ;
2005-04-17 02:20:36 +04:00
/* Create host handle for callback */
2006-10-04 13:15:52 +04:00
host = nlmsvc_lookup_host ( rqstp , lock - > caller , lock - > len ) ;
2005-04-17 02:20:36 +04:00
if ( host = = NULL )
return NULL ;
2006-03-20 21:44:45 +03:00
call = nlm_alloc_call ( host ) ;
if ( call = = NULL )
return NULL ;
2005-04-17 02:20:36 +04:00
/* Allocate memory for block, and initialize arguments */
2006-03-20 21:44:45 +03:00
block = kzalloc ( sizeof ( * block ) , GFP_KERNEL ) ;
if ( block = = NULL )
2005-04-17 02:20:36 +04:00
goto failed ;
2006-03-20 21:44:39 +03:00
kref_init ( & block - > b_count ) ;
2006-10-04 13:15:57 +04:00
INIT_LIST_HEAD ( & block - > b_list ) ;
INIT_LIST_HEAD ( & block - > b_flist ) ;
2005-04-17 02:20:36 +04:00
2006-03-20 21:44:45 +03:00
if ( ! nlmsvc_setgrantargs ( call , lock ) )
2005-04-17 02:20:36 +04:00
goto failed_free ;
/* Set notifier function for VFS, and init args */
2006-03-20 21:44:45 +03:00
call - > a_args . lock . fl . fl_flags | = FL_SLEEP ;
call - > a_args . lock . fl . fl_lmops = & nlmsvc_lock_operations ;
2006-10-04 13:16:03 +04:00
nlmclnt_next_cookie ( & call - > a_args . cookie ) ;
2005-04-17 02:20:36 +04:00
dprintk ( " lockd: created block %p... \n " , block ) ;
/* Create and initialize the block */
block - > b_daemon = rqstp - > rq_server ;
block - > b_host = host ;
block - > b_file = file ;
2006-11-29 00:27:06 +03:00
block - > b_fl = NULL ;
2006-03-20 21:44:47 +03:00
file - > f_count + + ;
2005-04-17 02:20:36 +04:00
/* Add to file's list of blocks */
2006-10-04 13:15:57 +04:00
list_add ( & block - > b_flist , & file - > f_blocks ) ;
2005-04-17 02:20:36 +04:00
/* Set up RPC arguments for callback */
2006-03-20 21:44:45 +03:00
block - > b_call = call ;
2005-04-17 02:20:36 +04:00
call - > a_flags = RPC_TASK_ASYNC ;
2006-03-20 21:44:45 +03:00
call - > a_block = block ;
2005-04-17 02:20:36 +04:00
return block ;
failed_free :
kfree ( block ) ;
failed :
2006-03-20 21:44:45 +03:00
nlm_release_call ( call ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
/*
* Delete a block . If the lock was cancelled or the grant callback
* failed , unlock is set to 1.
* It is the caller ' s responsibility to check whether the file
* can be closed hereafter .
*/
2006-03-20 21:44:39 +03:00
static int nlmsvc_unlink_block ( struct nlm_block * block )
2005-04-17 02:20:36 +04:00
{
2006-03-20 21:44:38 +03:00
int status ;
2006-03-20 21:44:39 +03:00
dprintk ( " lockd: unlinking block %p... \n " , block ) ;
2005-04-17 02:20:36 +04:00
/* Remove block from list */
2006-03-20 21:44:45 +03:00
status = posix_unblock_lock ( block - > b_file - > f_file , & block - > b_call - > a_args . lock . fl ) ;
2005-04-17 02:20:36 +04:00
nlmsvc_remove_block ( block ) ;
2006-03-20 21:44:39 +03:00
return status ;
}
2005-04-17 02:20:36 +04:00
2006-03-20 21:44:39 +03:00
static void nlmsvc_free_block ( struct kref * kref )
{
struct nlm_block * block = container_of ( kref , struct nlm_block , b_count ) ;
struct nlm_file * file = block - > b_file ;
dprintk ( " lockd: freeing block %p... \n " , block ) ;
2005-04-17 02:20:36 +04:00
/* Remove block from file's list of blocks */
2006-10-04 13:16:06 +04:00
mutex_lock ( & file - > f_mutex ) ;
2006-10-04 13:15:57 +04:00
list_del_init ( & block - > b_flist ) ;
2006-10-04 13:16:06 +04:00
mutex_unlock ( & file - > f_mutex ) ;
2005-04-17 02:20:36 +04:00
2006-03-20 21:44:45 +03:00
nlmsvc_freegrantargs ( block - > b_call ) ;
nlm_release_call ( block - > b_call ) ;
2006-03-20 21:44:47 +03:00
nlm_release_file ( block - > b_file ) ;
2006-11-29 00:26:51 +03:00
kfree ( block - > b_fl ) ;
2005-04-17 02:20:36 +04:00
kfree ( block ) ;
2006-03-20 21:44:39 +03:00
}
static void nlmsvc_release_block ( struct nlm_block * block )
{
if ( block ! = NULL )
kref_put ( & block - > b_count , nlmsvc_free_block ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-04 13:15:59 +04:00
/*
* Loop over all blocks and delete blocks held by
* a matching host .
*/
void nlmsvc_traverse_blocks ( struct nlm_host * host ,
struct nlm_file * file ,
nlm_host_match_fn_t match )
2006-03-20 21:44:47 +03:00
{
2006-10-04 13:15:57 +04:00
struct nlm_block * block , * next ;
2006-03-20 21:44:47 +03:00
restart :
2006-10-04 13:16:06 +04:00
mutex_lock ( & file - > f_mutex ) ;
2006-10-04 13:15:57 +04:00
list_for_each_entry_safe ( block , next , & file - > f_blocks , b_flist ) {
2006-10-04 13:15:59 +04:00
if ( ! match ( block - > b_host , host ) )
2006-03-20 21:44:47 +03:00
continue ;
2006-10-04 13:15:57 +04:00
/* Do not destroy blocks that are not on
* the global retry list - why ? */
if ( list_empty ( & block - > b_list ) )
2006-03-20 21:44:47 +03:00
continue ;
kref_get ( & block - > b_count ) ;
2006-10-04 13:16:06 +04:00
mutex_unlock ( & file - > f_mutex ) ;
2006-03-20 21:44:47 +03:00
nlmsvc_unlink_block ( block ) ;
nlmsvc_release_block ( block ) ;
goto restart ;
}
2006-10-04 13:16:06 +04:00
mutex_unlock ( & file - > f_mutex ) ;
2006-03-20 21:44:47 +03:00
}
2006-03-20 21:44:39 +03:00
/*
* Initialize arguments for GRANTED call . The nlm_rqst structure
* has been cleared already .
*/
static int nlmsvc_setgrantargs ( struct nlm_rqst * call , struct nlm_lock * lock )
{
locks_copy_lock ( & call - > a_args . lock . fl , & lock - > fl ) ;
memcpy ( & call - > a_args . lock . fh , & lock - > fh , sizeof ( call - > a_args . lock . fh ) ) ;
2006-10-02 13:18:11 +04:00
call - > a_args . lock . caller = utsname ( ) - > nodename ;
2006-03-20 21:44:39 +03:00
call - > a_args . lock . oh . len = lock - > oh . len ;
/* set default data area */
call - > a_args . lock . oh . data = call - > a_owner ;
call - > a_args . lock . svid = lock - > fl . fl_pid ;
if ( lock - > oh . len > NLMCLNT_OHSIZE ) {
void * data = kmalloc ( lock - > oh . len , GFP_KERNEL ) ;
2006-03-20 21:44:45 +03:00
if ( ! data )
2006-03-20 21:44:39 +03:00
return 0 ;
call - > a_args . lock . oh . data = ( u8 * ) data ;
}
memcpy ( call - > a_args . lock . oh . data , lock - > oh . data , lock - > oh . len ) ;
return 1 ;
}
static void nlmsvc_freegrantargs ( struct nlm_rqst * call )
{
2006-03-20 21:44:45 +03:00
if ( call - > a_args . lock . oh . data ! = call - > a_owner )
2006-03-20 21:44:39 +03:00
kfree ( call - > a_args . lock . oh . data ) ;
}
2006-11-29 00:26:47 +03:00
/*
* Deferred lock request handling for non - blocking lock
*/
static u32
nlmsvc_defer_lock_rqst ( struct svc_rqst * rqstp , struct nlm_block * block )
{
u32 status = nlm_lck_denied_nolocks ;
block - > b_flags | = B_QUEUED ;
nlmsvc_insert_block ( block , NLM_TIMEOUT ) ;
block - > b_cache_req = & rqstp - > rq_chandle ;
if ( rqstp - > rq_chandle . defer ) {
block - > b_deferred_req =
rqstp - > rq_chandle . defer ( block - > b_cache_req ) ;
if ( block - > b_deferred_req ! = NULL )
status = nlm_drop_reply ;
}
dprintk ( " lockd: nlmsvc_defer_lock_rqst block %p flags %d status %d \n " ,
block , block - > b_flags , status ) ;
return status ;
}
2005-04-17 02:20:36 +04:00
/*
* Attempt to establish a lock , and if it can ' t be granted , block it
* if required .
*/
2006-10-20 10:28:46 +04:00
__be32
2005-04-17 02:20:36 +04:00
nlmsvc_lock ( struct svc_rqst * rqstp , struct nlm_file * file ,
struct nlm_lock * lock , int wait , struct nlm_cookie * cookie )
{
2006-03-20 21:44:38 +03:00
struct nlm_block * block , * newblock = NULL ;
2005-04-17 02:20:36 +04:00
int error ;
2006-10-20 10:28:46 +04:00
__be32 ret ;
2005-04-17 02:20:36 +04:00
dprintk ( " lockd: nlmsvc_lock(%s/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d) \n " ,
2006-12-08 13:37:18 +03:00
file - > f_file - > f_path . dentry - > d_inode - > i_sb - > s_id ,
file - > f_file - > f_path . dentry - > d_inode - > i_ino ,
2005-04-17 02:20:36 +04:00
lock - > fl . fl_type , lock - > fl . fl_pid ,
( long long ) lock - > fl . fl_start ,
( long long ) lock - > fl . fl_end ,
wait ) ;
2006-03-20 21:44:38 +03:00
lock - > fl . fl_flags & = ~ FL_SLEEP ;
2005-04-17 02:20:36 +04:00
again :
/* Lock file against concurrent access */
2006-10-04 13:16:06 +04:00
mutex_lock ( & file - > f_mutex ) ;
2006-03-20 21:44:38 +03:00
/* Get existing block (in case client is busy-waiting) */
2006-03-20 21:44:47 +03:00
block = nlmsvc_lookup_block ( file , lock ) ;
2006-03-20 21:44:38 +03:00
if ( block = = NULL ) {
if ( newblock ! = NULL )
2006-03-20 21:44:45 +03:00
lock = & newblock - > b_call - > a_args . lock ;
2006-03-20 21:44:38 +03:00
} else
2006-03-20 21:44:45 +03:00
lock = & block - > b_call - > a_args . lock ;
2005-04-17 02:20:36 +04:00
2007-01-19 00:15:35 +03:00
error = posix_lock_file ( file - > f_file , & lock - > fl , NULL ) ;
2006-03-20 21:44:38 +03:00
lock - > fl . fl_flags & = ~ FL_SLEEP ;
2005-04-17 02:20:36 +04:00
2006-03-20 21:44:25 +03:00
dprintk ( " lockd: posix_lock_file returned %d \n " , error ) ;
2006-03-20 21:44:38 +03:00
switch ( error ) {
2005-04-17 02:20:36 +04:00
case 0 :
2006-03-20 21:44:24 +03:00
ret = nlm_granted ;
goto out ;
2006-03-20 21:44:38 +03:00
case - EAGAIN :
break ;
case - EDEADLK :
2006-03-20 21:44:24 +03:00
ret = nlm_deadlock ;
goto out ;
2005-04-17 02:20:36 +04:00
default : /* includes ENOLCK */
2006-03-20 21:44:24 +03:00
ret = nlm_lck_denied_nolocks ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2006-03-20 21:44:38 +03:00
ret = nlm_lck_denied ;
if ( ! wait )
goto out ;
ret = nlm_lck_blocked ;
if ( block ! = NULL )
goto out ;
2005-04-17 02:20:36 +04:00
/* If we don't have a block, create and initialize it. Then
* retry because we may have slept in kmalloc . */
2006-10-04 13:16:06 +04:00
/* We have to release f_mutex as nlmsvc_create_block may try to
2005-04-17 02:20:36 +04:00
* to claim it while doing host garbage collection */
2006-03-20 21:44:38 +03:00
if ( newblock = = NULL ) {
2006-10-04 13:16:06 +04:00
mutex_unlock ( & file - > f_mutex ) ;
2005-04-17 02:20:36 +04:00
dprintk ( " lockd: blocking on this lock (allocating). \n " ) ;
2006-03-20 21:44:38 +03:00
if ( ! ( newblock = nlmsvc_create_block ( rqstp , file , lock , cookie ) ) )
2005-04-17 02:20:36 +04:00
return nlm_lck_denied_nolocks ;
goto again ;
}
/* Append to list of blocked */
2006-03-20 21:44:38 +03:00
nlmsvc_insert_block ( newblock , NLM_NEVER ) ;
2006-03-20 21:44:24 +03:00
out :
2006-10-04 13:16:06 +04:00
mutex_unlock ( & file - > f_mutex ) ;
2006-03-20 21:44:39 +03:00
nlmsvc_release_block ( newblock ) ;
nlmsvc_release_block ( block ) ;
2006-03-20 21:44:24 +03:00
dprintk ( " lockd: nlmsvc_lock returned %u \n " , ret ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
/*
* Test for presence of a conflicting lock .
*/
2006-10-20 10:28:46 +04:00
__be32
2006-11-29 00:27:06 +03:00
nlmsvc_testlock ( struct svc_rqst * rqstp , struct nlm_file * file ,
struct nlm_lock * lock , struct nlm_lock * conflock ,
struct nlm_cookie * cookie )
2005-04-17 02:20:36 +04:00
{
2006-11-29 00:27:06 +03:00
struct nlm_block * block = NULL ;
int error ;
__be32 ret ;
2005-04-17 02:20:36 +04:00
dprintk ( " lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld) \n " ,
2006-12-08 13:37:18 +03:00
file - > f_file - > f_path . dentry - > d_inode - > i_sb - > s_id ,
file - > f_file - > f_path . dentry - > d_inode - > i_ino ,
2005-04-17 02:20:36 +04:00
lock - > fl . fl_type ,
( long long ) lock - > fl . fl_start ,
( long long ) lock - > fl . fl_end ) ;
2006-11-29 00:27:06 +03:00
/* Get existing block (in case client is busy-waiting) */
block = nlmsvc_lookup_block ( file , lock ) ;
if ( block = = NULL ) {
struct file_lock * conf = kzalloc ( sizeof ( * conf ) , GFP_KERNEL ) ;
if ( conf = = NULL )
return nlm_granted ;
block = nlmsvc_create_block ( rqstp , file , lock , cookie ) ;
if ( block = = NULL ) {
kfree ( conf ) ;
return nlm_granted ;
}
block - > b_fl = conf ;
}
if ( block - > b_flags & B_QUEUED ) {
dprintk ( " lockd: nlmsvc_testlock deferred block %p flags %d fl %p \n " ,
block , block - > b_flags , block - > b_fl ) ;
if ( block - > b_flags & B_TIMED_OUT ) {
nlmsvc_unlink_block ( block ) ;
return nlm_lck_denied ;
}
if ( block - > b_flags & B_GOT_CALLBACK ) {
if ( block - > b_fl ! = NULL
& & block - > b_fl - > fl_type ! = F_UNLCK ) {
lock - > fl = * block - > b_fl ;
goto conf_lock ;
}
else {
nlmsvc_unlink_block ( block ) ;
return nlm_granted ;
}
}
return nlm_drop_reply ;
2005-04-17 02:20:36 +04:00
}
2006-11-29 00:27:06 +03:00
error = vfs_test_lock ( file - > f_file , & lock - > fl ) ;
if ( error = = - EINPROGRESS )
return nlmsvc_defer_lock_rqst ( rqstp , block ) ;
if ( error ) {
ret = nlm_lck_denied_nolocks ;
goto out ;
}
if ( lock - > fl . fl_type = = F_UNLCK ) {
ret = nlm_granted ;
goto out ;
}
conf_lock :
dprintk ( " lockd: conflicting lock(ty=%d, %Ld-%Ld) \n " ,
lock - > fl . fl_type , ( long long ) lock - > fl . fl_start ,
( long long ) lock - > fl . fl_end ) ;
conflock - > caller = " somehost " ; /* FIXME */
conflock - > len = strlen ( conflock - > caller ) ;
conflock - > oh . len = 0 ; /* don't return OH info */
conflock - > svid = lock - > fl . fl_pid ;
conflock - > fl . fl_type = lock - > fl . fl_type ;
conflock - > fl . fl_start = lock - > fl . fl_start ;
conflock - > fl . fl_end = lock - > fl . fl_end ;
ret = nlm_lck_denied ;
out :
if ( block )
nlmsvc_release_block ( block ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
/*
* Remove a lock .
* This implies a CANCEL call : We send a GRANT_MSG , the client replies
* with a GRANT_RES call which gets lost , and calls UNLOCK immediately
* afterwards . In this case the block will still be there , and hence
* must be removed .
*/
2006-10-20 10:28:46 +04:00
__be32
2005-04-17 02:20:36 +04:00
nlmsvc_unlock ( struct nlm_file * file , struct nlm_lock * lock )
{
int error ;
dprintk ( " lockd: nlmsvc_unlock(%s/%ld, pi=%d, %Ld-%Ld) \n " ,
2006-12-08 13:37:18 +03:00
file - > f_file - > f_path . dentry - > d_inode - > i_sb - > s_id ,
file - > f_file - > f_path . dentry - > d_inode - > i_ino ,
2005-04-17 02:20:36 +04:00
lock - > fl . fl_pid ,
( long long ) lock - > fl . fl_start ,
( long long ) lock - > fl . fl_end ) ;
/* First, cancel any lock that might be there */
nlmsvc_cancel_blocked ( file , lock ) ;
lock - > fl . fl_type = F_UNLCK ;
2007-01-19 00:15:35 +03:00
error = posix_lock_file ( file - > f_file , & lock - > fl , NULL ) ;
2005-04-17 02:20:36 +04:00
return ( error < 0 ) ? nlm_lck_denied_nolocks : nlm_granted ;
}
/*
* Cancel a previously blocked request .
*
* A cancel request always overrides any grant that may currently
* be in progress .
* The calling procedure must check whether the file can be closed .
*/
2006-10-20 10:28:46 +04:00
__be32
2005-04-17 02:20:36 +04:00
nlmsvc_cancel_blocked ( struct nlm_file * file , struct nlm_lock * lock )
{
struct nlm_block * block ;
2006-01-03 11:55:46 +03:00
int status = 0 ;
2005-04-17 02:20:36 +04:00
dprintk ( " lockd: nlmsvc_cancel(%s/%ld, pi=%d, %Ld-%Ld) \n " ,
2006-12-08 13:37:18 +03:00
file - > f_file - > f_path . dentry - > d_inode - > i_sb - > s_id ,
file - > f_file - > f_path . dentry - > d_inode - > i_ino ,
2005-04-17 02:20:36 +04:00
lock - > fl . fl_pid ,
( long long ) lock - > fl . fl_start ,
( long long ) lock - > fl . fl_end ) ;
2006-10-04 13:16:06 +04:00
mutex_lock ( & file - > f_mutex ) ;
2006-03-20 21:44:47 +03:00
block = nlmsvc_lookup_block ( file , lock ) ;
2006-10-04 13:16:06 +04:00
mutex_unlock ( & file - > f_mutex ) ;
2006-03-20 21:44:47 +03:00
if ( block ! = NULL ) {
2006-03-20 21:44:39 +03:00
status = nlmsvc_unlink_block ( block ) ;
nlmsvc_release_block ( block ) ;
}
2006-01-03 11:55:46 +03:00
return status ? nlm_lck_denied : nlm_granted ;
2005-04-17 02:20:36 +04:00
}
2006-11-29 00:26:51 +03:00
/*
* This is a callback from the filesystem for VFS file lock requests .
* It will be used if fl_grant is defined and the filesystem can not
* respond to the request immediately .
* For GETLK request it will copy the reply to the nlm_block .
* For SETLK or SETLKW request it will get the local posix lock .
* In all cases it will move the block to the head of nlm_blocked q where
* nlmsvc_retry_blocked ( ) can send back a reply for SETLKW or revisit the
* deferred rpc for GETLK and SETLK .
*/
static void
nlmsvc_update_deferred_block ( struct nlm_block * block , struct file_lock * conf ,
int result )
{
block - > b_flags | = B_GOT_CALLBACK ;
if ( result = = 0 )
block - > b_granted = 1 ;
else
block - > b_flags | = B_TIMED_OUT ;
if ( conf ) {
if ( block - > b_fl )
locks_copy_lock ( block - > b_fl , conf ) ;
}
}
static int nlmsvc_grant_deferred ( struct file_lock * fl , struct file_lock * conf ,
int result )
{
struct nlm_block * block ;
int rc = - ENOENT ;
lock_kernel ( ) ;
list_for_each_entry ( block , & nlm_blocked , b_list ) {
if ( nlm_compare_locks ( & block - > b_call - > a_args . lock . fl , fl ) ) {
dprintk ( " lockd: nlmsvc_notify_blocked block %p flags %d \n " ,
block , block - > b_flags ) ;
if ( block - > b_flags & B_QUEUED ) {
if ( block - > b_flags & B_TIMED_OUT ) {
rc = - ENOLCK ;
break ;
}
nlmsvc_update_deferred_block ( block , conf , result ) ;
} else if ( result = = 0 )
block - > b_granted = 1 ;
nlmsvc_insert_block ( block , 0 ) ;
svc_wake_up ( block - > b_daemon ) ;
rc = 0 ;
break ;
}
}
unlock_kernel ( ) ;
if ( rc = = - ENOENT )
printk ( KERN_WARNING " lockd: grant for unknown block \n " ) ;
return rc ;
}
2005-04-17 02:20:36 +04:00
/*
* Unblock a blocked lock request . This is a callback invoked from the
* VFS layer when a lock on which we blocked is removed .
*
* This function doesn ' t grant the blocked lock instantly , but rather moves
* the block to the head of nlm_blocked where it can be picked up by lockd .
*/
static void
nlmsvc_notify_blocked ( struct file_lock * fl )
{
2006-10-04 13:15:57 +04:00
struct nlm_block * block ;
2005-04-17 02:20:36 +04:00
dprintk ( " lockd: VFS unblock notification for block %p \n " , fl ) ;
2006-10-04 13:15:57 +04:00
list_for_each_entry ( block , & nlm_blocked , b_list ) {
2006-03-20 21:44:45 +03:00
if ( nlm_compare_locks ( & block - > b_call - > a_args . lock . fl , fl ) ) {
2005-04-17 02:20:36 +04:00
nlmsvc_insert_block ( block , 0 ) ;
svc_wake_up ( block - > b_daemon ) ;
return ;
}
}
printk ( KERN_WARNING " lockd: notification for unknown block! \n " ) ;
}
static int nlmsvc_same_owner ( struct file_lock * fl1 , struct file_lock * fl2 )
{
return fl1 - > fl_owner = = fl2 - > fl_owner & & fl1 - > fl_pid = = fl2 - > fl_pid ;
}
struct lock_manager_operations nlmsvc_lock_operations = {
. fl_compare_owner = nlmsvc_same_owner ,
. fl_notify = nlmsvc_notify_blocked ,
2006-11-29 00:26:51 +03:00
. fl_grant = nlmsvc_grant_deferred ,
2005-04-17 02:20:36 +04:00
} ;
/*
* Try to claim a lock that was previously blocked .
*
* Note that we use both the RPC_GRANTED_MSG call _and_ an async
* RPC thread when notifying the client . This seems like overkill . . .
* Here ' s why :
* - we don ' t want to use a synchronous RPC thread , otherwise
* we might find ourselves hanging on a dead portmapper .
* - Some lockd implementations ( e . g . HP ) don ' t react to
* RPC_GRANTED calls ; they seem to insist on RPC_GRANTED_MSG calls .
*/
static void
nlmsvc_grant_blocked ( struct nlm_block * block )
{
struct nlm_file * file = block - > b_file ;
2006-03-20 21:44:45 +03:00
struct nlm_lock * lock = & block - > b_call - > a_args . lock ;
2005-04-17 02:20:36 +04:00
int error ;
dprintk ( " lockd: grant blocked lock %p \n " , block ) ;
2006-11-29 00:26:51 +03:00
kref_get ( & block - > b_count ) ;
2005-04-17 02:20:36 +04:00
/* Unlink block request from list */
2006-03-20 21:44:39 +03:00
nlmsvc_unlink_block ( block ) ;
2005-04-17 02:20:36 +04:00
/* If b_granted is true this means we've been here before.
* Just retry the grant callback , possibly refreshing the RPC
* binding */
if ( block - > b_granted ) {
nlm_rebind_host ( block - > b_host ) ;
goto callback ;
}
/* Try the lock operation again */
2006-03-20 21:44:38 +03:00
lock - > fl . fl_flags | = FL_SLEEP ;
2007-01-19 00:15:35 +03:00
error = posix_lock_file ( file - > f_file , & lock - > fl , NULL ) ;
2006-03-20 21:44:38 +03:00
lock - > fl . fl_flags & = ~ FL_SLEEP ;
2006-03-20 21:44:25 +03:00
switch ( error ) {
case 0 :
break ;
case - EAGAIN :
2005-04-17 02:20:36 +04:00
dprintk ( " lockd: lock still blocked \n " ) ;
nlmsvc_insert_block ( block , NLM_NEVER ) ;
2006-11-29 00:26:51 +03:00
nlmsvc_release_block ( block ) ;
2006-03-20 21:44:47 +03:00
return ;
2006-03-20 21:44:25 +03:00
default :
2005-04-17 02:20:36 +04:00
printk ( KERN_WARNING " lockd: unexpected error %d in %s! \n " ,
- error , __FUNCTION__ ) ;
nlmsvc_insert_block ( block , 10 * HZ ) ;
2006-11-29 00:26:51 +03:00
nlmsvc_release_block ( block ) ;
2006-03-20 21:44:47 +03:00
return ;
2005-04-17 02:20:36 +04:00
}
callback :
/* Lock was granted by VFS. */
dprintk ( " lockd: GRANTing blocked lock. \n " ) ;
block - > b_granted = 1 ;
/* Schedule next grant callback in 30 seconds */
nlmsvc_insert_block ( block , 30 * HZ ) ;
/* Call the client */
2007-02-03 02:37:43 +03:00
nlm_async_call ( block - > b_call , NLMPROC_GRANTED_MSG , & nlmsvc_grant_ops ) ;
2005-04-17 02:20:36 +04:00
}
/*
* This is the callback from the RPC layer when the NLM_GRANTED_MSG
* RPC call has succeeded or timed out .
* Like all RPC callbacks , it is invoked by the rpciod process , so it
* better not sleep . Therefore , we put the blocked lock on the nlm_blocked
* chain once more in order to have it removed by lockd itself ( which can
* then sleep on the file semaphore without disrupting e . g . the nfs client ) .
*/
2006-01-03 11:55:04 +03:00
static void nlmsvc_grant_callback ( struct rpc_task * task , void * data )
2005-04-17 02:20:36 +04:00
{
2006-01-03 11:55:04 +03:00
struct nlm_rqst * call = data ;
2006-03-20 21:44:45 +03:00
struct nlm_block * block = call - > a_block ;
2005-04-17 02:20:36 +04:00
unsigned long timeout ;
dprintk ( " lockd: GRANT_MSG RPC callback \n " ) ;
/* Technically, we should down the file semaphore here. Since we
* move the block towards the head of the queue only , no harm
* can be done , though . */
if ( task - > tk_status < 0 ) {
/* RPC error: Re-insert for retransmission */
timeout = 10 * HZ ;
} else {
/* Call was successful, now wait for client callback */
timeout = 60 * HZ ;
}
nlmsvc_insert_block ( block , timeout ) ;
svc_wake_up ( block - > b_daemon ) ;
2006-03-20 21:44:39 +03:00
}
2006-04-18 21:21:50 +04:00
static void nlmsvc_grant_release ( void * data )
2006-03-20 21:44:39 +03:00
{
2006-03-20 21:44:45 +03:00
struct nlm_rqst * call = data ;
nlmsvc_release_block ( call - > a_block ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-03 11:55:04 +03:00
static const struct rpc_call_ops nlmsvc_grant_ops = {
. rpc_call_done = nlmsvc_grant_callback ,
2006-03-20 21:44:39 +03:00
. rpc_release = nlmsvc_grant_release ,
2006-01-03 11:55:04 +03:00
} ;
2005-04-17 02:20:36 +04:00
/*
* We received a GRANT_RES callback . Try to find the corresponding
* block .
*/
void
2006-12-13 11:35:03 +03:00
nlmsvc_grant_reply ( struct nlm_cookie * cookie , __be32 status )
2005-04-17 02:20:36 +04:00
{
struct nlm_block * block ;
2006-10-04 13:16:03 +04:00
dprintk ( " grant_reply: looking for cookie %x, s=%d \n " ,
* ( unsigned int * ) ( cookie - > data ) , status ) ;
if ( ! ( block = nlmsvc_find_block ( cookie ) ) )
2005-04-17 02:20:36 +04:00
return ;
2006-01-03 11:55:42 +03:00
if ( block ) {
2006-12-13 11:35:03 +03:00
if ( status = = nlm_lck_denied_grace_period ) {
2005-04-17 02:20:36 +04:00
/* Try again in a couple of seconds */
nlmsvc_insert_block ( block , 10 * HZ ) ;
} else {
/* Lock is now held by client, or has been rejected.
* In both cases , the block should be removed . */
2006-03-20 21:44:39 +03:00
nlmsvc_unlink_block ( block ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-03-20 21:44:39 +03:00
nlmsvc_release_block ( block ) ;
2005-04-17 02:20:36 +04:00
}
2006-11-29 00:26:51 +03:00
/* Helper function to handle retry of a deferred block.
* If it is a blocking lock , call grant_blocked .
* For a non - blocking lock or test lock , revisit the request .
*/
static void
retry_deferred_block ( struct nlm_block * block )
{
if ( ! ( block - > b_flags & B_GOT_CALLBACK ) )
block - > b_flags | = B_TIMED_OUT ;
nlmsvc_insert_block ( block , NLM_TIMEOUT ) ;
dprintk ( " revisit block %p flags %d \n " , block , block - > b_flags ) ;
if ( block - > b_deferred_req ) {
block - > b_deferred_req - > revisit ( block - > b_deferred_req , 0 ) ;
block - > b_deferred_req = NULL ;
}
}
2005-04-17 02:20:36 +04:00
/*
* Retry all blocked locks that have been notified . This is where lockd
* picks up locks that can be granted , or grant notifications that must
* be retransmitted .
*/
unsigned long
nlmsvc_retry_blocked ( void )
{
2006-10-04 13:15:57 +04:00
unsigned long timeout = MAX_SCHEDULE_TIMEOUT ;
struct nlm_block * block ;
while ( ! list_empty ( & nlm_blocked ) ) {
block = list_entry ( nlm_blocked . next , struct nlm_block , b_list ) ;
2005-04-17 02:20:36 +04:00
if ( block - > b_when = = NLM_NEVER )
break ;
2006-10-04 13:15:57 +04:00
if ( time_after ( block - > b_when , jiffies ) ) {
timeout = block - > b_when - jiffies ;
2005-04-17 02:20:36 +04:00
break ;
2006-10-04 13:15:57 +04:00
}
2006-08-03 23:07:47 +04:00
dprintk ( " nlmsvc_retry_blocked(%p, when=%ld) \n " ,
block , block - > b_when ) ;
2006-11-29 00:26:51 +03:00
if ( block - > b_flags & B_QUEUED ) {
dprintk ( " nlmsvc_retry_blocked delete block (%p, granted=%d, flags=%d) \n " ,
block , block - > b_granted , block - > b_flags ) ;
retry_deferred_block ( block ) ;
} else
nlmsvc_grant_blocked ( block ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-04 13:15:57 +04:00
return timeout ;
2005-04-17 02:20:36 +04:00
}