/* * 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; struct nlm_lock *lock = &block->b_call.a_args.lock; struct file_lock *conflock; int error; dprintk("lockd: grant blocked lock %p\n", block); /* First thing is lock the file */ down(&file->f_sema); /* Unlink block request from list */ nlmsvc_remove_block(block); /* 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 */ if ((conflock = posix_test_lock(&file->f_file, &lock->fl)) != NULL) { /* Bummer, we blocked again */ dprintk("lockd: lock still blocked\n"); nlmsvc_insert_block(block, NLM_NEVER); posix_block_lock(conflock, &lock->fl); up(&file->f_sema); return; } /* Alright, no conflicting lock. Now lock it for real. If the * following yields an error, this is most probably due to low * memory. Retry the lock in a few seconds. */ if ((error = posix_lock_file(&file->f_file, &lock->fl, 0)) < 0) { printk(KERN_WARNING "lockd: unexpected error %d in %s!\n", -error, __FUNCTION__); nlmsvc_insert_block(block, jiffies + 10 * HZ); up(&file->f_sema); return; } callback: /* Lock was granted by VFS. */ dprintk("lockd: GRANTing blocked lock.\n"); block->b_granted = 1; block->b_incall = 1; /* Schedule next grant callback in 30 seconds */ nlmsvc_insert_block(block, jiffies + 30 * HZ); /* Call the client */ nlmclnt_async_call(&block->b_call, NLMPROC_GRANTED_MSG, nlmsvc_grant_callback); up(&file->f_sema); }
/* * 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. */ static void nlmsvc_delete_block(struct nlm_block *block, int unlock) { struct file_lock *fl = &block->b_call.a_args.lock.fl; struct nlm_file *file = block->b_file; struct nlm_block **bp; dprintk("lockd: deleting block %p...\n", block); /* Remove block from list */ nlmsvc_remove_block(block); posix_unblock_lock(&file->f_file, fl); block->b_granted = 0; /* If the block is in the middle of a GRANT callback, * don't kill it yet. */ if (block->b_incall) { nlmsvc_insert_block(block, NLM_NEVER); block->b_done = 1; return; } /* 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; } } if (block->b_host) nlm_release_host(block->b_host); nlmclnt_freegrantargs(&block->b_call); kfree(block); }
/* * 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); if (block->b_queued) nlmsvc_remove_block(block); for (bp = &nlm_blocked; (b = *bp); bp = &b->b_next) if (when < b->b_when) break; block->b_queued = 1; block->b_when = when; block->b_next = b; *bp = block; }
/* * 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); if (block->b_queued) nlmsvc_remove_block(block); bp = &nlm_blocked; if (when != NLM_NEVER) { if ((when += jiffies) > NLM_NEVER) when = NLM_NEVER; while ((b = *bp) && time_before_eq(b->b_when,when) && b->b_when != NLM_NEVER) bp = &b->b_next; } else while ((b = *bp)) bp = &b->b_next; block->b_queued = 1; block->b_when = when; block->b_next = b; *bp = block; }