?? svclock.c
字號:
/* * Test for presence of a conflicting lock. */__be32nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, struct nlm_host *host, struct nlm_lock *lock, struct nlm_lock *conflock, struct nlm_cookie *cookie){ struct nlm_block *block = NULL; int error; __be32 ret; dprintk("lockd: nlmsvc_testlock(%s/%ld, ty=%d, %Ld-%Ld)\n", file->f_file->f_path.dentry->d_inode->i_sb->s_id, file->f_file->f_path.dentry->d_inode->i_ino, lock->fl.fl_type, (long long)lock->fl.fl_start, (long long)lock->fl.fl_end); /* 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, host, 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); ret = nlm_lck_denied; goto out; } if (block->b_flags & B_GOT_CALLBACK) { nlmsvc_unlink_block(block); if (block->b_fl != NULL && block->b_fl->fl_type != F_UNLCK) { lock->fl = *block->b_fl; goto conf_lock; } else { ret = nlm_granted; goto out; } } ret = nlm_drop_reply; goto out; } if (locks_in_grace()) { ret = nlm_lck_denied_grace_period; goto out; } error = vfs_test_lock(file->f_file, &lock->fl); if (error == FILE_LOCK_DEFERRED) { ret = nlmsvc_defer_lock_rqst(rqstp, block); goto out; } 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;}/* * 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. */__be32nlmsvc_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_path.dentry->d_inode->i_sb->s_id, file->f_file->f_path.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 = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL); 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. */__be32nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock){ struct nlm_block *block; int status = 0; dprintk("lockd: nlmsvc_cancel(%s/%ld, pi=%d, %Ld-%Ld)\n", file->f_file->f_path.dentry->d_inode->i_sb->s_id, file->f_file->f_path.dentry->d_inode->i_ino, lock->fl.fl_pid, (long long)lock->fl.fl_start, (long long)lock->fl.fl_end); if (locks_in_grace()) return nlm_lck_denied_grace_period; mutex_lock(&file->f_mutex); block = nlmsvc_lookup_block(file, lock); mutex_unlock(&file->f_mutex); if (block != NULL) { vfs_cancel_lock(block->b_file->f_file, &block->b_call->a_args.lock.fl); status = nlmsvc_unlink_block(block); nlmsvc_release_block(block); } return status ? nlm_lck_denied : nlm_granted;}/* * 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 voidnlmsvc_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;}/* * 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 *block; dprintk("lockd: VFS unblock notification for block %p\n", fl); list_for_each_entry(block, &nlm_blocked, b_list) { 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");}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, .fl_grant = nlmsvc_grant_deferred,};/* * 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; int error; dprintk("lockd: grant blocked lock %p\n", block); kref_get(&block->b_count); /* Unlink block request from list */ nlmsvc_unlink_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 */ lock->fl.fl_flags |= FL_SLEEP; error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL); lock->fl.fl_flags &= ~FL_SLEEP; switch (error) { case 0: break; case FILE_LOCK_DEFERRED: dprintk("lockd: lock still blocked error %d\n", error); nlmsvc_insert_block(block, NLM_NEVER); nlmsvc_release_block(block); return; default: printk(KERN_WARNING "lockd: unexpected error %d in %s!\n", -error, __func__); nlmsvc_insert_block(block, 10 * HZ); nlmsvc_release_block(block); return; }callback: /* Lock was granted by VFS. */ dprintk("lockd: GRANTing blocked lock.\n"); block->b_granted = 1; /* keep block on the list, but don't reattempt until the RPC * completes or the submission fails */ nlmsvc_insert_block(block, NLM_NEVER); /* Call the client -- use a soft RPC task since nlmsvc_retry_blocked * will queue up a new one if this one times out */ error = nlm_async_call(block->b_call, NLMPROC_GRANTED_MSG, &nlmsvc_grant_ops); /* RPC submission failed, wait a bit and retry */ if (error < 0) nlmsvc_insert_block(block, 10 * HZ);}/* * 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 void nlmsvc_grant_callback(struct rpc_task *task, void *data){ struct nlm_rqst *call = data; struct nlm_block *block = call->a_block; unsigned long timeout; dprintk("lockd: GRANT_MSG RPC callback\n"); lock_kernel(); /* if the block is not on a list at this point then it has * been invalidated. Don't try to requeue it. * * FIXME: it's possible that the block is removed from the list * after this check but before the nlmsvc_insert_block. In that * case it will be added back. Perhaps we need better locking * for nlm_blocked? */ if (list_empty(&block->b_list)) goto out; /* 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);out: unlock_kernel();}static void nlmsvc_grant_release(void *data){ struct nlm_rqst *call = data; lock_kernel(); nlmsvc_release_block(call->a_block); unlock_kernel();}static const struct rpc_call_ops nlmsvc_grant_ops = { .rpc_call_done = nlmsvc_grant_callback, .rpc_release = nlmsvc_grant_release,};/* * We received a GRANT_RES callback. Try to find the corresponding * block. */voidnlmsvc_grant_reply(struct nlm_cookie *cookie, __be32 status){ struct nlm_block *block; dprintk("grant_reply: looking for cookie %x, s=%d \n", *(unsigned int *)(cookie->data), status); if (!(block = nlmsvc_find_block(cookie))) return; if (block) { 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. */ nlmsvc_unlink_block(block); } } nlmsvc_release_block(block);}/* 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 voidretry_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; }}/* * 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){ unsigned long timeout = MAX_SCHEDULE_TIMEOUT; struct nlm_block *block; while (!list_empty(&nlm_blocked) && !kthread_should_stop()) { block = list_entry(nlm_blocked.next, struct nlm_block, b_list); if (block->b_when == NLM_NEVER) break; if (time_after(block->b_when, jiffies)) { timeout = block->b_when - jiffies; break; } dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n", block, block->b_when); 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); } return timeout;}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -