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 ) ;
static int 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
*/
static struct nlm_block * nlm_blocked ;
/*
* Insert a blocked lock into the global list
*/
static void
nlmsvc_insert_block ( struct nlm_block * block , unsigned long when )
{
struct nlm_block * * bp , * b ;
dprintk ( " lockd: nlmsvc_insert_block(%p, %ld) \n " , block , when ) ;
2006-03-20 21:44:39 +03:00
kref_get ( & block - > b_count ) ;
2005-04-17 02:20:36 +04:00
if ( block - > b_queued )
nlmsvc_remove_block ( block ) ;
bp = & nlm_blocked ;
if ( when ! = NLM_NEVER ) {
if ( ( when + = jiffies ) = = NLM_NEVER )
when + + ;
while ( ( b = * bp ) & & time_before_eq ( b - > b_when , when ) & & b - > b_when ! = NLM_NEVER )
bp = & b - > b_next ;
} else
while ( ( b = * bp ) ! = 0 )
bp = & b - > b_next ;
block - > b_queued = 1 ;
block - > b_when = when ;
block - > b_next = b ;
* bp = block ;
}
/*
* Remove a block from the global list
*/
static int
nlmsvc_remove_block ( struct nlm_block * block )
{
struct nlm_block * * bp , * b ;
if ( ! block - > b_queued )
return 1 ;
for ( bp = & nlm_blocked ; ( b = * bp ) ! = 0 ; bp = & b - > b_next ) {
if ( b = = block ) {
* bp = block - > b_next ;
block - > b_queued = 0 ;
2006-03-20 21:44:39 +03:00
nlmsvc_release_block ( block ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
}
return 0 ;
}
/*
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
{
struct nlm_block * * head , * block ;
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 ) ;
for ( head = & nlm_blocked ; ( block = * head ) ! = 0 ; head = & block - > b_next ) {
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 *
nlmsvc_find_block ( struct nlm_cookie * cookie , struct sockaddr_in * sin )
{
struct nlm_block * block ;
for ( block = nlm_blocked ; block ; block = block - > b_next ) {
dprintk ( " cookie: head of blocked queue %p, block %p \n " ,
nlm_blocked , block ) ;
2006-03-20 21:44:45 +03:00
if ( nlm_cookie_match ( & block - > b_call - > a_args . cookie , cookie )
2005-04-17 02:20:36 +04:00
& & nlm_cmp_addr ( sin , & block - > b_host - > h_addr ) )
break ;
}
2006-03-20 21:44:39 +03:00
if ( block ! = NULL )
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 .
*/
static inline struct nlm_block *
nlmsvc_create_block ( struct svc_rqst * rqstp , struct nlm_file * file ,
struct nlm_lock * lock , struct nlm_cookie * cookie )
{
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-03-20 21:44:39 +03:00
host = nlmsvc_lookup_host ( rqstp ) ;
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 ) ;
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 ;
call - > a_args . cookie = * cookie ; /* see above */
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-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 */
block - > b_fnext = file - > f_blocks ;
file - > f_blocks = block ;
/* 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 ;
struct nlm_block * * bp ;
dprintk ( " lockd: freeing block %p... \n " , block ) ;
2005-04-17 02:20:36 +04:00
2006-03-20 21:44:47 +03:00
down ( & file - > f_sema ) ;
2005-04-17 02:20:36 +04:00
/* Remove block from file's list of blocks */
for ( bp = & file - > f_blocks ; * bp ; bp = & ( * bp ) - > b_fnext ) {
if ( * bp = = block ) {
* bp = block - > b_fnext ;
break ;
}
}
2006-03-20 21:44:47 +03:00
up ( & file - > f_sema ) ;
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 ) ;
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-03-20 21:44:47 +03:00
static void nlmsvc_act_mark ( struct nlm_host * host , struct nlm_file * file )
{
struct nlm_block * block ;
down ( & file - > f_sema ) ;
for ( block = file - > f_blocks ; block ! = NULL ; block = block - > b_fnext )
block - > b_host - > h_inuse = 1 ;
up ( & file - > f_sema ) ;
}
static void nlmsvc_act_unlock ( struct nlm_host * host , struct nlm_file * file )
{
struct nlm_block * block ;
restart :
down ( & file - > f_sema ) ;
for ( block = file - > f_blocks ; block ! = NULL ; block = block - > b_fnext ) {
if ( host ! = NULL & & host ! = block - > b_host )
continue ;
if ( ! block - > b_queued )
continue ;
kref_get ( & block - > b_count ) ;
up ( & file - > f_sema ) ;
nlmsvc_unlink_block ( block ) ;
nlmsvc_release_block ( block ) ;
goto restart ;
}
up ( & file - > f_sema ) ;
}
2005-04-17 02:20:36 +04:00
/*
* Loop over all blocks and perform the action specified .
* ( NLM_ACT_CHECK handled by nlmsvc_inspect_file ) .
*/
2006-03-21 07:24:13 +03:00
void
2005-04-17 02:20:36 +04:00
nlmsvc_traverse_blocks ( struct nlm_host * host , struct nlm_file * file , int action )
{
2006-03-20 21:44:47 +03:00
if ( action = = NLM_ACT_MARK )
nlmsvc_act_mark ( host , file ) ;
else
nlmsvc_act_unlock ( host , file ) ;
2005-04-17 02:20:36 +04: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 ) ) ;
call - > a_args . lock . caller = system_utsname . nodename ;
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 ) ;
}
2005-04-17 02:20:36 +04:00
/*
* Attempt to establish a lock , and if it can ' t be granted , block it
* if required .
*/
u32
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-03-20 21:44:24 +03:00
u32 ret ;
2005-04-17 02:20:36 +04:00
dprintk ( " lockd: nlmsvc_lock(%s/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d) \n " ,
file - > f_file - > f_dentry - > d_inode - > i_sb - > s_id ,
file - > f_file - > f_dentry - > d_inode - > i_ino ,
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 */
down ( & file - > f_sema ) ;
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
2006-03-20 21:44:25 +03:00
error = posix_lock_file ( file - > f_file , & lock - > fl ) ;
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 . */
/* We have to release f_sema as nlmsvc_create_block may try to
* to claim it while doing host garbage collection */
2006-03-20 21:44:38 +03:00
if ( newblock = = NULL ) {
2005-04-17 02:20:36 +04:00
up ( & file - > f_sema ) ;
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-03-20 21:44:38 +03:00
up ( & file - > f_sema ) ;
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 .
*/
u32
nlmsvc_testlock ( struct nlm_file * file , struct nlm_lock * lock ,
struct nlm_lock * conflock )
{
dprintk ( " lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld) \n " ,
file - > f_file - > f_dentry - > d_inode - > i_sb - > s_id ,
file - > f_file - > f_dentry - > d_inode - > i_ino ,
lock - > fl . fl_type ,
( long long ) lock - > fl . fl_start ,
( long long ) lock - > fl . fl_end ) ;
2006-03-20 21:44:26 +03:00
if ( posix_test_lock ( file - > f_file , & lock - > fl , & conflock - > fl ) ) {
2005-04-17 02:20:36 +04:00
dprintk ( " lockd: conflicting lock(ty=%d, %Ld-%Ld) \n " ,
2006-03-20 21:44:26 +03:00
conflock - > fl . fl_type ,
( long long ) conflock - > fl . fl_start ,
( long long ) conflock - > fl . fl_end ) ;
2005-04-17 02:20:36 +04:00
conflock - > caller = " somehost " ; /* FIXME */
conflock - > oh . len = 0 ; /* don't return OH info */
2006-03-20 21:44:26 +03:00
conflock - > svid = conflock - > fl . fl_pid ;
2005-04-17 02:20:36 +04:00
return nlm_lck_denied ;
}
return nlm_granted ;
}
/*
* 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 .
*/
u32
nlmsvc_unlock ( struct nlm_file * file , struct nlm_lock * lock )
{
int error ;
dprintk ( " lockd: nlmsvc_unlock(%s/%ld, pi=%d, %Ld-%Ld) \n " ,
file - > f_file - > f_dentry - > d_inode - > i_sb - > s_id ,
file - > f_file - > f_dentry - > d_inode - > i_ino ,
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 ;
error = posix_lock_file ( file - > f_file , & lock - > fl ) ;
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 .
*/
u32
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 " ,
file - > f_file - > f_dentry - > d_inode - > i_sb - > s_id ,
file - > f_file - > f_dentry - > d_inode - > i_ino ,
lock - > fl . fl_pid ,
( long long ) lock - > fl . fl_start ,
( long long ) lock - > fl . fl_end ) ;
down ( & file - > f_sema ) ;
2006-03-20 21:44:47 +03:00
block = nlmsvc_lookup_block ( file , lock ) ;
up ( & file - > f_sema ) ;
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
}
/*
* 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 )
{
struct nlm_block * * bp , * block ;
dprintk ( " lockd: VFS unblock notification for block %p \n " , fl ) ;
for ( bp = & nlm_blocked ; ( block = * bp ) ! = 0 ; bp = & block - > b_next ) {
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 ,
} ;
/*
* 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 ) ;
/* 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 ;
2006-03-20 21:44:25 +03:00
error = posix_lock_file ( file - > f_file , & lock - > fl ) ;
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-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-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 */
2006-03-20 21:44:39 +03:00
kref_get ( & block - > b_count ) ;
2006-03-20 21:44:45 +03:00
if ( nlm_async_call ( block - > b_call , NLMPROC_GRANTED_MSG ,
2006-01-03 11:55:04 +03:00
& nlmsvc_grant_ops ) < 0 )
2006-03-20 21:44:39 +03:00
nlmsvc_release_block ( block ) ;
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 if ( block - > b_done ) {
/* Block already removed, kill it for real */
timeout = 0 ;
} 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
nlmsvc_grant_reply ( struct svc_rqst * rqstp , struct nlm_cookie * cookie , u32 status )
{
struct nlm_block * block ;
struct nlm_file * file ;
dprintk ( " grant_reply: looking for cookie %x, host (%08x), s=%d \n " ,
* ( unsigned int * ) ( cookie - > data ) ,
ntohl ( rqstp - > rq_addr . sin_addr . s_addr ) , status ) ;
if ( ! ( block = nlmsvc_find_block ( cookie , & rqstp - > rq_addr ) ) )
return ;
file = block - > b_file ;
2006-01-03 11:55:42 +03:00
if ( block ) {
2005-04-17 02:20:36 +04:00
if ( status = = NLM_LCK_DENIED_GRACE_PERIOD ) {
/* 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
}
/*
* 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 )
{
struct nlm_block * block ;
dprintk ( " nlmsvc_retry_blocked(%p, when=%ld) \n " ,
nlm_blocked ,
nlm_blocked ? nlm_blocked - > b_when : 0 ) ;
while ( ( block = nlm_blocked ) ! = 0 ) {
if ( block - > b_when = = NLM_NEVER )
break ;
if ( time_after ( block - > b_when , jiffies ) )
break ;
dprintk ( " nlmsvc_retry_blocked(%p, when=%ld, done=%d) \n " ,
block , block - > b_when , block - > b_done ) ;
2006-03-20 21:44:39 +03:00
kref_get ( & block - > b_count ) ;
2005-04-17 02:20:36 +04:00
if ( block - > b_done )
2006-03-20 21:44:39 +03:00
nlmsvc_unlink_block ( block ) ;
2005-04-17 02:20:36 +04:00
else
nlmsvc_grant_blocked ( block ) ;
2006-03-20 21:44:39 +03:00
nlmsvc_release_block ( block ) ;
2005-04-17 02:20:36 +04:00
}
if ( ( block = nlm_blocked ) & & block - > b_when ! = NLM_NEVER )
return ( block - > b_when - jiffies ) ;
return MAX_SCHEDULE_TIMEOUT ;
}