/* * 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); }
/* * 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, u32 cookie) { struct file_lock *conflock; struct nlm_block *block; int error; dprintk("lockd: nlmsvc_lock(%04x/%ld, ty=%d, pi=%d, %ld-%ld, bl=%d)\n", file->f_file.f_dentry->d_inode->i_dev, file->f_file.f_dentry->d_inode->i_ino, lock->fl.fl_type, lock->fl.fl_pid, lock->fl.fl_start, lock->fl.fl_end, wait); /* Lock file against concurrent access */ down(&file->f_sema); /* Get existing block (in case client is busy-waiting) */ block = nlmsvc_lookup_block(file, lock, 0); lock->fl.fl_flags |= FL_LOCKD; again: if (!(conflock = posix_test_lock(&file->f_file, &lock->fl))) { error = posix_lock_file(&file->f_file, &lock->fl, 0); if (block) nlmsvc_delete_block(block, 0); up(&file->f_sema); dprintk("lockd: posix_lock_file returned %d\n", -error); switch(-error) { case 0: return nlm_granted; case EDEADLK: /* no applicable NLM status */ case EAGAIN: return nlm_lck_denied; default: /* includes ENOLCK */ return nlm_lck_denied_nolocks; } } if (!wait) { up(&file->f_sema); return nlm_lck_denied; } /* If we don't have a block, create and initialize it. Then * retry because we may have slept in kmalloc. */ if (block == NULL) { dprintk("lockd: blocking on this lock (allocating).\n"); if (!(block = nlmsvc_create_block(rqstp, file, lock, cookie))) return nlm_lck_denied_nolocks; goto again; } /* Append to list of blocked */ nlmsvc_insert_block(block, NLM_NEVER); /* Now add block to block list of the conflicting lock */ dprintk("lockd: blocking on this lock.\n"); posix_block_lock(conflock, &block->b_call.a_args.lock.fl); up(&file->f_sema); return nlm_lck_blocked; }
/* * 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) { struct file_lock *conflock; struct nlm_block *block, *nblock = NULL; int error; dprintk("lockd: nlmsvc_lock(%04x/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)\n", file->f_file.f_dentry->d_inode->i_dev, 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); /* Get existing block (in case client is busy-waiting) */ block = nlmsvc_lookup_block(file, lock, 0); lock->fl.fl_flags |= FL_LOCKD; again: /* Lock file against concurrent access */ down(&file->f_sema); if (!(conflock = posix_test_lock(&file->f_file, &lock->fl))) { error = posix_lock_file(&file->f_file, &lock->fl, 0); if (block) nlmsvc_delete_block(block, 0); up(&file->f_sema); dprintk("lockd: posix_lock_file returned %d\n", -error); switch(-error) { case 0: return nlm_granted; case EDEADLK: return nlm_deadlock; case EAGAIN: return nlm_lck_denied; default: /* includes ENOLCK */ return nlm_lck_denied_nolocks; } } if (!wait) { up(&file->f_sema); return nlm_lck_denied; } if (posix_locks_deadlock(&lock->fl, conflock)) { if (nblock) nlmsvc_delete_block(nblock, 0); up(&file->f_sema); return nlm_deadlock; } /* 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 */ if (block == NULL) { up(&file->f_sema); dprintk("lockd: blocking on this lock (allocating).\n"); if (!(block = nlmsvc_create_block(rqstp, file, lock, cookie))) return nlm_lck_denied_nolocks; nblock = block; goto again; } /* Append to list of blocked */ nlmsvc_insert_block(block, NLM_NEVER); if (list_empty(&block->b_call.a_args.lock.fl.fl_block)) { /* Now add block to block list of the conflicting lock if we haven't done so. */ dprintk("lockd: blocking on this lock.\n"); posix_block_lock(conflock, &block->b_call.a_args.lock.fl); } up(&file->f_sema); return nlm_lck_blocked; }