📄 svclock.c
字号:
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)) { 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. */ 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); 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;}/* * Test for presence of a conflicting lock. */u32nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock, struct nlm_lock *conflock){ struct file_lock *fl; dprintk("lockd: nlmsvc_testlock(%04x/%ld, ty=%d, %Ld-%Ld)\n", file->f_file.f_dentry->d_inode->i_dev, 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); if ((fl = posix_test_lock(&file->f_file, &lock->fl)) != NULL) { dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n", fl->fl_type, (long long)fl->fl_start, (long long)fl->fl_end); conflock->caller = "somehost"; /* FIXME */ conflock->oh.len = 0; /* don't return OH info */ conflock->fl = *fl; 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. */u32nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock){ int error; dprintk("lockd: nlmsvc_unlock(%04x/%ld, pi=%d, %Ld-%Ld)\n", file->f_file.f_dentry->d_inode->i_dev, 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, 0); 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. */u32nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock){ struct nlm_block *block; dprintk("lockd: nlmsvc_cancel(%04x/%ld, pi=%d, %Ld-%Ld)\n", file->f_file.f_dentry->d_inode->i_dev, 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); if ((block = nlmsvc_lookup_block(file, lock, 1)) != NULL) nlmsvc_delete_block(block, 1); up(&file->f_sema); return nlm_granted;}/* * 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 voidnlmsvc_notify_blocked(struct file_lock *fl){ struct nlm_block **bp, *block; dprintk("lockd: VFS unblock notification for block %p\n", fl); posix_unblock_lock(fl); for (bp = &nlm_blocked; (block = *bp); bp = &block->b_next) { if (nlm_compare_locks(&block->b_call.a_args.lock.fl, fl)) { nlmsvc_insert_block(block, 0); svc_wake_up(block->b_daemon); return; } } printk(KERN_WARNING "lockd: notification for unknown block!\n");}/* * 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 voidnlmsvc_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, 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, 30 * HZ); /* Call the client */ nlm_get_host(block->b_call.a_host); if (nlmsvc_async_call(&block->b_call, NLMPROC_GRANTED_MSG, nlmsvc_grant_callback) < 0) nlm_release_host(block->b_call.a_host); up(&file->f_sema);}/* * 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). */static voidnlmsvc_grant_callback(struct rpc_task *task){ struct nlm_rqst *call = (struct nlm_rqst *) task->tk_calldata; struct nlm_block *block; unsigned long timeout; dprintk("lockd: GRANT_MSG RPC callback\n"); dprintk("callback: looking for cookie %x \n", *(unsigned int *)(call->a_args.cookie.data)); if (!(block = nlmsvc_find_block(&call->a_args.cookie))) { dprintk("lockd: no block for cookie %x\n", *(u32 *)(call->a_args.cookie.data)); return; } /* 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); block->b_incall = 0; nlm_release_host(call->a_host);}/* * We received a GRANT_RES callback. Try to find the corresponding * block. */voidnlmsvc_grant_reply(struct nlm_cookie *cookie, u32 status){ struct nlm_block *block; struct nlm_file *file; if (!(block = nlmsvc_find_block(cookie))) return; file = block->b_file; file->f_count++; down(&file->f_sema); if ((block = nlmsvc_find_block(cookie)) != NULL) { if (status == NLM_LCK_DENIED_GRACE_PERIOD) { /* Try again in a couple of seconds */ nlmsvc_insert_block(block, 10 * HZ); block = NULL; } else { /* Lock is now held by client, or has been rejected. * In both cases, the block should be removed. */ file->f_count++; up(&file->f_sema); if (status == NLM_LCK_GRANTED) nlmsvc_delete_block(block, 0); else nlmsvc_delete_block(block, 1); } } if (!block) up(&file->f_sema); nlm_release_file(file);}/* * 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 longnlmsvc_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)) { 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); if (block->b_done) nlmsvc_delete_block(block, 0); else nlmsvc_grant_blocked(block); } if ((block = nlm_blocked) && block->b_when != NLM_NEVER) return (block->b_when - jiffies); return MAX_SCHEDULE_TIMEOUT;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -