All of the above --- fs/lockd/clntproc.c | 38 + fs/lockd/svc.c | 4 fs/lockd/svc4proc.c | 15 fs/lockd/svclock.c | 14 fs/lockd/svcproc.c | 14 fs/nfs/delegation.c | 46 + fs/nfs/delegation.h | 1 fs/nfs/dir.c | 3 fs/nfs/direct.c | 2 fs/nfs/inode.c | 69 +- fs/nfs/nfs3proc.c | 44 + fs/nfs/nfs4_fs.h | 20 - fs/nfs/nfs4proc.c | 1482 ++++++++++++++++++++++++++---------------- fs/nfs/nfs4renewd.c | 14 fs/nfs/nfs4state.c | 183 +++-- fs/nfs/nfs4xdr.c | 152 ++-- fs/nfs/proc.c | 29 + fs/nfs/read.c | 10 fs/nfs/unlink.c | 30 - fs/nfs/write.c | 43 - fs/nfsd/nfs4callback.c | 10 include/linux/lockd/lockd.h | 2 include/linux/nfs_fs.h | 12 include/linux/nfs_xdr.h | 54 +- include/linux/sunrpc/clnt.h | 3 include/linux/sunrpc/sched.h | 45 + include/linux/writeback.h | 9 mm/page-writeback.c | 10 net/sunrpc/clnt.c | 62 +- net/sunrpc/pmap_clnt.c | 8 net/sunrpc/rpc_pipe.c | 2 net/sunrpc/sched.c | 174 +++-- net/sunrpc/sunrpc_syms.c | 4 33 files changed, 1574 insertions(+), 1034 deletions(-) diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index c5a3364..816333c 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -26,11 +26,12 @@ static int nlmclnt_test(struct nlm_rqst *, struct file_lock *); static int nlmclnt_lock(struct nlm_rqst *, struct file_lock *); static int nlmclnt_unlock(struct nlm_rqst *, struct file_lock *); -static void nlmclnt_unlock_callback(struct rpc_task *); -static void nlmclnt_cancel_callback(struct rpc_task *); static int nlm_stat_to_errno(u32 stat); static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *host); +static const struct rpc_call_ops nlmclnt_unlock_ops; +static const struct rpc_call_ops nlmclnt_cancel_ops; + /* * Cookie counter for NLM requests */ @@ -399,8 +400,7 @@ in_grace_period: /* * Generic NLM call, async version. */ -int -nlmsvc_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback) +int nlmsvc_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) { struct nlm_host *host = req->a_host; struct rpc_clnt *clnt; @@ -419,13 +419,12 @@ nlmsvc_async_call(struct nlm_rqst *req, msg.rpc_proc = &clnt->cl_procinfo[proc]; /* bootstrap and kick off the async RPC call */ - status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req); + status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, tk_ops, req); return status; } -static int -nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback) +static int nlmclnt_async_call(struct nlm_rqst *req, u32 proc, const struct rpc_call_ops *tk_ops) { struct nlm_host *host = req->a_host; struct rpc_clnt *clnt; @@ -448,7 +447,7 @@ nlmclnt_async_call(struct nlm_rqst *req, /* Increment host refcount */ nlm_get_host(host); /* bootstrap and kick off the async RPC call */ - status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req); + status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, tk_ops, req); if (status < 0) nlm_release_host(host); return status; @@ -664,7 +663,7 @@ nlmclnt_unlock(struct nlm_rqst *req, str if (req->a_flags & RPC_TASK_ASYNC) { status = nlmclnt_async_call(req, NLMPROC_UNLOCK, - nlmclnt_unlock_callback); + &nlmclnt_unlock_ops); /* Hrmf... Do the unlock early since locks_remove_posix() * really expects us to free the lock synchronously */ do_vfs_lock(fl); @@ -692,10 +691,9 @@ nlmclnt_unlock(struct nlm_rqst *req, str return -ENOLCK; } -static void -nlmclnt_unlock_callback(struct rpc_task *task) +static void nlmclnt_unlock_callback(struct rpc_task *task, void *data) { - struct nlm_rqst *req = (struct nlm_rqst *) task->tk_calldata; + struct nlm_rqst *req = data; int status = req->a_res.status; if (RPC_ASSASSINATED(task)) @@ -722,6 +720,10 @@ die: rpc_restart_call(task); } +static const struct rpc_call_ops nlmclnt_unlock_ops = { + .rpc_call_done = nlmclnt_unlock_callback, +}; + /* * Cancel a blocked lock request. * We always use an async RPC call for this in order not to hang a @@ -750,8 +752,7 @@ nlmclnt_cancel(struct nlm_host *host, st nlmclnt_setlockargs(req, fl); - status = nlmclnt_async_call(req, NLMPROC_CANCEL, - nlmclnt_cancel_callback); + status = nlmclnt_async_call(req, NLMPROC_CANCEL, &nlmclnt_cancel_ops); if (status < 0) { nlmclnt_release_lockargs(req); kfree(req); @@ -765,10 +766,9 @@ nlmclnt_cancel(struct nlm_host *host, st return status; } -static void -nlmclnt_cancel_callback(struct rpc_task *task) +static void nlmclnt_cancel_callback(struct rpc_task *task, void *data) { - struct nlm_rqst *req = (struct nlm_rqst *) task->tk_calldata; + struct nlm_rqst *req = data; if (RPC_ASSASSINATED(task)) goto die; @@ -807,6 +807,10 @@ retry_cancel: rpc_delay(task, 30 * HZ); } +static const struct rpc_call_ops nlmclnt_cancel_ops = { + .rpc_call_done = nlmclnt_cancel_callback, +}; + /* * Convert an NLM status code to a generic kernel errno */ diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 12a857c..71a30b4 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -178,6 +178,8 @@ lockd(struct svc_rqst *rqstp) } + flush_signals(current); + /* * Check whether there's a new lockd process before * shutting down the hosts and clearing the slot. @@ -192,8 +194,6 @@ lockd(struct svc_rqst *rqstp) "lockd: new process, skipping host shutdown\n"); wake_up(&lockd_exit); - flush_signals(current); - /* Exit the RPC thread */ svc_exit_thread(rqstp); diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 489670e..4063095 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -22,7 +22,8 @@ #define NLMDBG_FACILITY NLMDBG_CLIENT static u32 nlm4svc_callback(struct svc_rqst *, u32, struct nlm_res *); -static void nlm4svc_callback_exit(struct rpc_task *); + +static const struct rpc_call_ops nlm4svc_callback_ops; /* * Obtain client and file from arguments @@ -470,7 +471,6 @@ nlm4svc_proc_granted_res(struct svc_rqst } - /* * This is the generic lockd callback for async RPC calls */ @@ -494,7 +494,7 @@ nlm4svc_callback(struct svc_rqst *rqstp, call->a_host = host; memcpy(&call->a_args, resp, sizeof(*resp)); - if (nlmsvc_async_call(call, proc, nlm4svc_callback_exit) < 0) + if (nlmsvc_async_call(call, proc, &nlm4svc_callback_ops) < 0) goto error; return rpc_success; @@ -504,10 +504,9 @@ nlm4svc_callback(struct svc_rqst *rqstp, return rpc_system_err; } -static void -nlm4svc_callback_exit(struct rpc_task *task) +static void nlm4svc_callback_exit(struct rpc_task *task, void *data) { - struct nlm_rqst *call = (struct nlm_rqst *) task->tk_calldata; + struct nlm_rqst *call = data; if (task->tk_status < 0) { dprintk("lockd: %4d callback failed (errno = %d)\n", @@ -517,6 +516,10 @@ nlm4svc_callback_exit(struct rpc_task *t kfree(call); } +static const struct rpc_call_ops nlm4svc_callback_ops = { + .rpc_call_done = nlm4svc_callback_exit, +}; + /* * NLM Server procedures. */ diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 49f9597..87d09a0 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -41,7 +41,8 @@ static void nlmsvc_insert_block(struct nlm_block *block, unsigned long); static int nlmsvc_remove_block(struct nlm_block *block); -static void nlmsvc_grant_callback(struct rpc_task *task); + +static const struct rpc_call_ops nlmsvc_grant_ops; /* * The list of blocked locks to retry @@ -562,7 +563,7 @@ callback: /* 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) + &nlmsvc_grant_ops) < 0) nlm_release_host(block->b_call.a_host); up(&file->f_sema); } @@ -575,10 +576,9 @@ callback: * 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) +static void nlmsvc_grant_callback(struct rpc_task *task, void *data) { - struct nlm_rqst *call = (struct nlm_rqst *) task->tk_calldata; + struct nlm_rqst *call = data; struct nlm_block *block; unsigned long timeout; struct sockaddr_in *peer_addr = RPC_PEERADDR(task->tk_client); @@ -614,6 +614,10 @@ nlmsvc_grant_callback(struct rpc_task *t nlm_release_host(call->a_host); } +static const struct rpc_call_ops nlmsvc_grant_ops = { + .rpc_call_done = nlmsvc_grant_callback, +}; + /* * We received a GRANT_RES callback. Try to find the corresponding * block. diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index 757e344..3bc437e 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -23,7 +23,8 @@ #define NLMDBG_FACILITY NLMDBG_CLIENT static u32 nlmsvc_callback(struct svc_rqst *, u32, struct nlm_res *); -static void nlmsvc_callback_exit(struct rpc_task *); + +static const struct rpc_call_ops nlmsvc_callback_ops; #ifdef CONFIG_LOCKD_V4 static u32 @@ -518,7 +519,7 @@ nlmsvc_callback(struct svc_rqst *rqstp, call->a_host = host; memcpy(&call->a_args, resp, sizeof(*resp)); - if (nlmsvc_async_call(call, proc, nlmsvc_callback_exit) < 0) + if (nlmsvc_async_call(call, proc, &nlmsvc_callback_ops) < 0) goto error; return rpc_success; @@ -528,10 +529,9 @@ nlmsvc_callback(struct svc_rqst *rqstp, return rpc_system_err; } -static void -nlmsvc_callback_exit(struct rpc_task *task) +static void nlmsvc_callback_exit(struct rpc_task *task, void *data) { - struct nlm_rqst *call = (struct nlm_rqst *) task->tk_calldata; + struct nlm_rqst *call = data; if (task->tk_status < 0) { dprintk("lockd: %4d callback failed (errno = %d)\n", @@ -541,6 +541,10 @@ nlmsvc_callback_exit(struct rpc_task *ta kfree(call); } +static const struct rpc_call_ops nlmsvc_callback_ops = { + .rpc_call_done = nlmsvc_callback_exit, +}; + /* * NLM Server procedures. */ diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 618a327..75dfb1c 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -8,6 +8,7 @@ */ #include #include +#include #include #include #include @@ -231,6 +232,51 @@ restart: spin_unlock(&clp->cl_lock); } +int nfs_do_expire_all_delegations(void *ptr) +{ + struct nfs4_client *clp = ptr; + struct nfs_delegation *delegation; + struct inode *inode; + int err = 0; + + allow_signal(SIGKILL); +restart: + spin_lock(&clp->cl_lock); + if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) != 0) + goto out; + if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) + goto out; + list_for_each_entry(delegation, &clp->cl_delegations, super_list) { + inode = igrab(delegation->inode); + if (inode == NULL) + continue; + spin_unlock(&clp->cl_lock); + err = nfs_inode_return_delegation(inode); + iput(inode); + if (!err) + goto restart; + } +out: + spin_unlock(&clp->cl_lock); + nfs4_put_client(clp); + module_put_and_exit(0); +} + +void nfs_expire_all_delegations(struct nfs4_client *clp) +{ + struct task_struct *task; + + __module_get(THIS_MODULE); + atomic_inc(&clp->cl_count); + task = kthread_run(nfs_do_expire_all_delegations, clp, + "%u.%u.%u.%u-delegreturn", + NIPQUAD(clp->cl_addr)); + if (!IS_ERR(task)) + return; + nfs4_put_client(clp); + module_put(THIS_MODULE); +} + /* * Return all delegations following an NFS4ERR_CB_PATH_DOWN error. */ diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index 2fcc30d..fbc50ec 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -30,6 +30,7 @@ int nfs_async_inode_return_delegation(st struct inode *nfs_delegation_find_inode(struct nfs4_client *clp, const struct nfs_fh *fhandle); void nfs_return_all_delegations(struct super_block *sb); +void nfs_expire_all_delegations(struct nfs4_client *clp); void nfs_handle_cb_pathdown(struct nfs4_client *clp); void nfs_delegation_mark_reclaim(struct nfs4_client *clp); diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 7370583..c0d1a21 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1287,6 +1287,7 @@ dentry->d_parent->d_name.name, dentry->d nfs_begin_data_update(dentry->d_inode); error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, dir, &qsilly); + nfs_mark_for_revalidate(dentry->d_inode); nfs_end_data_update(dentry->d_inode); } else error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, @@ -1334,6 +1335,7 @@ static int nfs_safe_remove(struct dentry /* The VFS may want to delete this inode */ if (error == 0) inode->i_nlink--; + nfs_mark_for_revalidate(inode); nfs_end_data_update(inode); } else error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); @@ -1556,6 +1558,7 @@ go_ahead: nfs_begin_data_update(old_inode); error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name, new_dir, &new_dentry->d_name); + nfs_mark_for_revalidate(old_inode); nfs_end_data_update(old_inode); nfs_end_data_update(new_dir); nfs_end_data_update(old_dir); diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index b497c71..a2d2814 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -268,8 +268,6 @@ static void nfs_direct_read_schedule(str NFS_PROTO(inode)->read_setup(data); data->task.tk_cookie = (unsigned long) inode; - data->task.tk_calldata = data; - data->task.tk_release = nfs_readdata_release; data->complete = nfs_direct_read_result; lock_kernel(); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index aaab1a5..027cc75 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -54,7 +54,7 @@ #define NFS_MAX_READAHEAD (RPC_DEF_SLOT_TABLE - 1) static void nfs_invalidate_inode(struct inode *); -static int nfs_update_inode(struct inode *, struct nfs_fattr *, unsigned long); +static int nfs_update_inode(struct inode *, struct nfs_fattr *); static struct inode *nfs_alloc_inode(struct super_block *sb); static void nfs_destroy_inode(struct inode *); @@ -1080,8 +1080,6 @@ __nfs_revalidate_inode(struct nfs_server int status = -ESTALE; struct nfs_fattr fattr; struct nfs_inode *nfsi = NFS_I(inode); - unsigned long verifier; - unsigned long cache_validity; dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n", inode->i_sb->s_id, (long long)NFS_FILEID(inode)); @@ -1106,8 +1104,6 @@ __nfs_revalidate_inode(struct nfs_server } } - /* Protect against RPC races by saving the change attribute */ - verifier = nfs_save_change_attribute(inode); status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), &fattr); if (status != 0) { dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n", @@ -1122,7 +1118,7 @@ __nfs_revalidate_inode(struct nfs_server } spin_lock(&inode->i_lock); - status = nfs_update_inode(inode, &fattr, verifier); + status = nfs_update_inode(inode, &fattr); if (status) { spin_unlock(&inode->i_lock); dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n", @@ -1130,20 +1126,11 @@ __nfs_revalidate_inode(struct nfs_server (long long)NFS_FILEID(inode), status); goto out; } - cache_validity = nfsi->cache_validity; - nfsi->cache_validity &= ~NFS_INO_REVAL_PAGECACHE; - - /* - * We may need to keep the attributes marked as invalid if - * we raced with nfs_end_attr_update(). - */ - if (time_after_eq(verifier, nfsi->cache_change_attribute)) - nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME); spin_unlock(&inode->i_lock); nfs_revalidate_mapping(inode, inode->i_mapping); - if (cache_validity & NFS_INO_INVALID_ACL) + if (nfsi->cache_validity & NFS_INO_INVALID_ACL) nfs_zap_acl_cache(inode); dfprintk(PAGECACHE, "NFS: (%s/%Ld) revalidation complete\n", @@ -1346,10 +1333,8 @@ int nfs_refresh_inode(struct inode *inod return 0; spin_lock(&inode->i_lock); nfsi->cache_validity &= ~NFS_INO_REVAL_PAGECACHE; - if (nfs_verify_change_attribute(inode, fattr->time_start)) - nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME); if (time_after(fattr->time_start, nfsi->last_updated)) - status = nfs_update_inode(inode, fattr, fattr->time_start); + status = nfs_update_inode(inode, fattr); else status = nfs_check_inode_attributes(inode, fattr); @@ -1375,10 +1360,7 @@ int nfs_post_op_update_inode(struct inod nfsi->cache_validity |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS; goto out; } - status = nfs_update_inode(inode, fattr, fattr->time_start); - if (time_after_eq(fattr->time_start, nfsi->cache_change_attribute)) - nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME|NFS_INO_REVAL_PAGECACHE); - nfsi->cache_change_attribute = jiffies; + status = nfs_update_inode(inode, fattr); out: spin_unlock(&inode->i_lock); return status; @@ -1396,12 +1378,12 @@ out: * * A very similar scenario holds for the dir cache. */ -static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsigned long verifier) +static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) { struct nfs_inode *nfsi = NFS_I(inode); loff_t cur_isize, new_isize; unsigned int invalid = 0; - int data_unstable; + int data_stable; dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n", __FUNCTION__, inode->i_sb->s_id, inode->i_ino, @@ -1432,8 +1414,9 @@ static int nfs_update_inode(struct inode nfsi->last_updated = jiffies; /* Are we racing with known updates of the metadata on the server? */ - data_unstable = ! (nfs_verify_change_attribute(inode, verifier) || - (nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE)); + data_stable = nfs_verify_change_attribute(inode, fattr->time_start); + if (data_stable) + nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME); /* Check if our cached file size is stale */ new_isize = nfs_size_to_loff_t(fattr->size); @@ -1442,7 +1425,7 @@ static int nfs_update_inode(struct inode /* Do we perhaps have any outstanding writes? */ if (nfsi->npages == 0) { /* No, but did we race with nfs_end_data_update()? */ - if (time_after_eq(verifier, nfsi->cache_change_attribute)) { + if (data_stable) { inode->i_size = new_isize; invalid |= NFS_INO_INVALID_DATA; } @@ -1451,6 +1434,7 @@ static int nfs_update_inode(struct inode inode->i_size = new_isize; invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; } + nfsi->cache_change_attribute = jiffies; dprintk("NFS: isize change on server for file %s/%ld\n", inode->i_sb->s_id, inode->i_ino); } @@ -1460,8 +1444,8 @@ static int nfs_update_inode(struct inode memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime)); dprintk("NFS: mtime change on server for file %s/%ld\n", inode->i_sb->s_id, inode->i_ino); - if (!data_unstable) - invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; + invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA; + nfsi->cache_change_attribute = jiffies; } if ((fattr->valid & NFS_ATTR_FATTR_V4) @@ -1469,15 +1453,15 @@ static int nfs_update_inode(struct inode dprintk("NFS: change_attr change on server for file %s/%ld\n", inode->i_sb->s_id, inode->i_ino); nfsi->change_attr = fattr->change_attr; - if (!data_unstable) - invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; + invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; + nfsi->cache_change_attribute = jiffies; } /* If ctime has changed we should definitely clear access+acl caches */ if (!timespec_equal(&inode->i_ctime, &fattr->ctime)) { - if (!data_unstable) - invalid |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; + invalid |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime)); + nfsi->cache_change_attribute = jiffies; } memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime)); @@ -1515,6 +1499,8 @@ static int nfs_update_inode(struct inode if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) invalid &= ~NFS_INO_INVALID_DATA; + if (data_stable) + invalid &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME|NFS_INO_REVAL_PAGECACHE); if (!nfs_have_delegation(inode, FMODE_READ)) nfsi->cache_validity |= invalid; @@ -1818,23 +1804,9 @@ static int nfs4_fill_super(struct super_ clnt->cl_softrtry = 1; clnt->cl_chatty = 1; clp->cl_rpcclient = clnt; - clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0); - if (IS_ERR(clp->cl_cred)) { - up_write(&clp->cl_sem); - err = PTR_ERR(clp->cl_cred); - clp->cl_cred = NULL; - goto out_fail; - } memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); nfs_idmap_new(clp); } - if (list_empty(&clp->cl_superblocks)) { - err = nfs4_init_client(clp); - if (err != 0) { - up_write(&clp->cl_sem); - goto out_fail; - } - } list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks); clnt = rpc_clone_client(clp->cl_rpcclient); if (!IS_ERR(clnt)) @@ -2066,6 +2038,7 @@ static struct inode *nfs_alloc_inode(str return NULL; nfsi->flags = 0UL; nfsi->cache_validity = 0UL; + nfsi->cache_change_attribute = jiffies; #ifdef CONFIG_NFS_V3_ACL nfsi->acl_access = ERR_PTR(-EAGAIN); nfsi->acl_default = ERR_PTR(-EAGAIN); diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 92c870d..c172a75 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -732,19 +732,23 @@ nfs3_proc_pathconf(struct nfs_server *se extern u32 *nfs3_decode_dirent(u32 *, struct nfs_entry *, int); -static void -nfs3_read_done(struct rpc_task *task) +static void nfs3_read_done(struct rpc_task *task, void *calldata) { - struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata; + struct nfs_read_data *data = calldata; if (nfs3_async_handle_jukebox(task)) return; /* Call back common NFS readpage processing */ if (task->tk_status >= 0) nfs_refresh_inode(data->inode, &data->fattr); - nfs_readpage_result(task); + nfs_readpage_result(task, calldata); } +static const struct rpc_call_ops nfs3_read_ops = { + .rpc_call_done = nfs3_read_done, + .rpc_release = nfs_readdata_release, +}; + static void nfs3_proc_read_setup(struct nfs_read_data *data) { @@ -762,23 +766,26 @@ nfs3_proc_read_setup(struct nfs_read_dat flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0); /* Finalize the task. */ - rpc_init_task(task, NFS_CLIENT(inode), nfs3_read_done, flags); + rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs3_read_ops, data); rpc_call_setup(task, &msg, 0); } -static void -nfs3_write_done(struct rpc_task *task) +static void nfs3_write_done(struct rpc_task *task, void *calldata) { - struct nfs_write_data *data; + struct nfs_write_data *data = calldata; if (nfs3_async_handle_jukebox(task)) return; - data = (struct nfs_write_data *)task->tk_calldata; if (task->tk_status >= 0) nfs_post_op_update_inode(data->inode, data->res.fattr); - nfs_writeback_done(task); + nfs_writeback_done(task, calldata); } +static const struct rpc_call_ops nfs3_write_ops = { + .rpc_call_done = nfs3_write_done, + .rpc_release = nfs_writedata_release, +}; + static void nfs3_proc_write_setup(struct nfs_write_data *data, int how) { @@ -806,23 +813,26 @@ nfs3_proc_write_setup(struct nfs_write_d flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; /* Finalize the task. */ - rpc_init_task(task, NFS_CLIENT(inode), nfs3_write_done, flags); + rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs3_write_ops, data); rpc_call_setup(task, &msg, 0); } -static void -nfs3_commit_done(struct rpc_task *task) +static void nfs3_commit_done(struct rpc_task *task, void *calldata) { - struct nfs_write_data *data; + struct nfs_write_data *data = calldata; if (nfs3_async_handle_jukebox(task)) return; - data = (struct nfs_write_data *)task->tk_calldata; if (task->tk_status >= 0) nfs_post_op_update_inode(data->inode, data->res.fattr); - nfs_commit_done(task); + nfs_commit_done(task, calldata); } +static const struct rpc_call_ops nfs3_commit_ops = { + .rpc_call_done = nfs3_commit_done, + .rpc_release = nfs_commit_release, +}; + static void nfs3_proc_commit_setup(struct nfs_write_data *data, int how) { @@ -840,7 +850,7 @@ nfs3_proc_commit_setup(struct nfs_write_ flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; /* Finalize the task. */ - rpc_init_task(task, NFS_CLIENT(inode), nfs3_commit_done, flags); + rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs3_commit_ops, data); rpc_call_setup(task, &msg, 0); } diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index b7f262d..0f5e4e7 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -38,7 +38,8 @@ struct idmap; ((err) != NFSERR_NOFILEHANDLE)) enum nfs4_client_state { - NFS4CLNT_OK = 0, + NFS4CLNT_STATE_RECOVER = 0, + NFS4CLNT_LEASE_EXPIRED, }; /* @@ -67,7 +68,6 @@ struct nfs4_client { atomic_t cl_count; struct rpc_clnt * cl_rpcclient; - struct rpc_cred * cl_cred; struct list_head cl_superblocks; /* List of nfs_server structs */ @@ -76,7 +76,6 @@ struct nfs4_client { struct work_struct cl_renewd; struct work_struct cl_recoverd; - wait_queue_head_t cl_waitq; struct rpc_wait_queue cl_rpcwaitq; /* used for the setclientid verifier */ @@ -182,8 +181,9 @@ struct nfs4_state { nfs4_stateid stateid; - unsigned int nreaders; - unsigned int nwriters; + unsigned int n_rdonly; + unsigned int n_wronly; + unsigned int n_rdwr; int state; /* State on the server (R,W, or RW) */ atomic_t count; }; @@ -210,10 +210,10 @@ extern ssize_t nfs4_listxattr(struct den /* nfs4proc.c */ extern int nfs4_map_errors(int err); -extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short); -extern int nfs4_proc_setclientid_confirm(struct nfs4_client *); -extern int nfs4_proc_async_renew(struct nfs4_client *); -extern int nfs4_proc_renew(struct nfs4_client *); +extern int nfs4_proc_setclientid(struct nfs4_client *, u32, unsigned short, struct rpc_cred *); +extern int nfs4_proc_setclientid_confirm(struct nfs4_client *, struct rpc_cred *); +extern int nfs4_proc_async_renew(struct nfs4_client *, struct rpc_cred *); +extern int nfs4_proc_renew(struct nfs4_client *, struct rpc_cred *); extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state); extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *); @@ -237,8 +237,8 @@ extern void init_nfsv4_state(struct nfs_ extern void destroy_nfsv4_state(struct nfs_server *); extern struct nfs4_client *nfs4_get_client(struct in_addr *); extern void nfs4_put_client(struct nfs4_client *clp); -extern int nfs4_init_client(struct nfs4_client *clp); extern struct nfs4_client *nfs4_find_client(struct in_addr *); +struct rpc_cred *nfs4_get_renew_cred(struct nfs4_client *clp); extern u32 nfs4_alloc_lockowner_id(struct nfs4_client *); extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 60e0dd8..b334915 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -57,11 +57,13 @@ #define NFS4_POLL_RETRY_MIN (1*HZ) #define NFS4_POLL_RETRY_MAX (15*HZ) -static int _nfs4_proc_open_confirm(struct rpc_clnt *clnt, const struct nfs_fh *fh, struct nfs4_state_owner *sp, nfs4_stateid *stateid, struct nfs_seqid *seqid); +struct nfs4_opendata; +static int _nfs4_proc_open(struct nfs4_opendata *data); static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *); static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry); static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception); +static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp); extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus); extern struct rpc_procinfo nfs4_procedures[]; @@ -173,8 +175,7 @@ static void nfs4_setup_readdir(u64 cooki kunmap_atomic(start, KM_USER0); } -static void -renew_lease(struct nfs_server *server, unsigned long timestamp) +static void renew_lease(const struct nfs_server *server, unsigned long timestamp) { struct nfs4_client *clp = server->nfs4_state; spin_lock(&clp->cl_lock); @@ -194,21 +195,123 @@ static void update_changeattr(struct ino spin_unlock(&inode->i_lock); } +struct nfs4_opendata { + atomic_t count; + struct nfs_openargs o_arg; + struct nfs_openres o_res; + struct nfs_open_confirmargs c_arg; + struct nfs_open_confirmres c_res; + struct nfs_fattr f_attr; + struct nfs_fattr dir_attr; + struct dentry *dentry; + struct dentry *dir; + struct nfs4_state_owner *owner; + struct iattr attrs; + unsigned long timestamp; + int rpc_status; + int cancelled; +}; + +static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry, + struct nfs4_state_owner *sp, int flags, + const struct iattr *attrs) +{ + struct dentry *parent = dget_parent(dentry); + struct inode *dir = parent->d_inode; + struct nfs_server *server = NFS_SERVER(dir); + struct nfs4_opendata *p; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (p == NULL) + goto err; + p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid); + if (p->o_arg.seqid == NULL) + goto err_free; + atomic_set(&p->count, 1); + p->dentry = dget(dentry); + p->dir = parent; + p->owner = sp; + atomic_inc(&sp->so_count); + p->o_arg.fh = NFS_FH(dir); + p->o_arg.open_flags = flags, + p->o_arg.clientid = server->nfs4_state->cl_clientid; + p->o_arg.id = sp->so_id; + p->o_arg.name = &dentry->d_name; + p->o_arg.server = server; + p->o_arg.bitmask = server->attr_bitmask; + p->o_arg.claim = NFS4_OPEN_CLAIM_NULL; + p->o_res.f_attr = &p->f_attr; + p->o_res.dir_attr = &p->dir_attr; + p->o_res.server = server; + nfs_fattr_init(&p->f_attr); + nfs_fattr_init(&p->dir_attr); + if (flags & O_EXCL) { + u32 *s = (u32 *) p->o_arg.u.verifier.data; + s[0] = jiffies; + s[1] = current->pid; + } else if (flags & O_CREAT) { + p->o_arg.u.attrs = &p->attrs; + memcpy(&p->attrs, attrs, sizeof(p->attrs)); + } + p->c_arg.fh = &p->o_res.fh; + p->c_arg.stateid = &p->o_res.stateid; + p->c_arg.seqid = p->o_arg.seqid; + return p; +err_free: + kfree(p); +err: + dput(parent); + return NULL; +} + +static void nfs4_opendata_free(struct nfs4_opendata *p) +{ + if (p != NULL && atomic_dec_and_test(&p->count)) { + nfs_free_seqid(p->o_arg.seqid); + nfs4_put_state_owner(p->owner); + dput(p->dir); + dput(p->dentry); + kfree(p); + } +} + /* Helper for asynchronous RPC calls */ -static int nfs4_call_async(struct rpc_clnt *clnt, rpc_action tk_begin, - rpc_action tk_exit, void *calldata) +static int nfs4_call_async(struct rpc_clnt *clnt, + const struct rpc_call_ops *tk_ops, void *calldata) { struct rpc_task *task; - if (!(task = rpc_new_task(clnt, tk_exit, RPC_TASK_ASYNC))) + if (!(task = rpc_new_task(clnt, RPC_TASK_ASYNC, tk_ops, calldata))) return -ENOMEM; - - task->tk_calldata = calldata; - task->tk_action = tk_begin; rpc_execute(task); return 0; } +static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task) +{ + sigset_t oldset; + int ret; + + rpc_clnt_sigmask(task->tk_client, &oldset); + ret = rpc_wait_for_completion_task(task); + rpc_clnt_sigunmask(task->tk_client, &oldset); + return ret; +} + +static inline void update_open_stateflags(struct nfs4_state *state, mode_t open_flags) +{ + switch (open_flags) { + case FMODE_WRITE: + state->n_wronly++; + break; + case FMODE_READ: + state->n_rdonly++; + break; + case FMODE_READ|FMODE_WRITE: + state->n_rdwr++; + } +} + static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags) { struct inode *inode = state->inode; @@ -218,41 +321,134 @@ static void update_open_stateid(struct n spin_lock(&state->owner->so_lock); spin_lock(&inode->i_lock); memcpy(&state->stateid, stateid, sizeof(state->stateid)); - if ((open_flags & FMODE_WRITE)) - state->nwriters++; - if (open_flags & FMODE_READ) - state->nreaders++; + update_open_stateflags(state, open_flags); nfs4_state_set_mode_locked(state, state->state | open_flags); spin_unlock(&inode->i_lock); spin_unlock(&state->owner->so_lock); } +static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data) +{ + struct inode *inode; + struct nfs4_state *state = NULL; + + if (!(data->f_attr.valid & NFS_ATTR_FATTR)) + goto out; + inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr); + if (inode == NULL) + goto out; + state = nfs4_get_open_state(inode, data->owner); + if (state == NULL) + goto put_inode; + update_open_stateid(state, &data->o_res.stateid, data->o_arg.open_flags); +put_inode: + iput(inode); +out: + return state; +} + +static struct nfs_open_context *nfs4_state_find_open_context(struct nfs4_state *state) +{ + struct nfs_inode *nfsi = NFS_I(state->inode); + struct nfs_open_context *ctx; + + spin_lock(&state->inode->i_lock); + list_for_each_entry(ctx, &nfsi->open_files, list) { + if (ctx->state != state) + continue; + get_nfs_open_context(ctx); + spin_unlock(&state->inode->i_lock); + return ctx; + } + spin_unlock(&state->inode->i_lock); + return ERR_PTR(-ENOENT); +} + +static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, mode_t openflags, nfs4_stateid *stateid) +{ + int ret; + + opendata->o_arg.open_flags = openflags; + ret = _nfs4_proc_open(opendata); + if (ret != 0) + return ret; + memcpy(stateid->data, opendata->o_res.stateid.data, + sizeof(stateid->data)); + return 0; +} + +static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *state) +{ + nfs4_stateid stateid; + struct nfs4_state *newstate; + int mode = 0; + int delegation = 0; + int ret; + + /* memory barrier prior to reading state->n_* */ + smp_rmb(); + if (state->n_rdwr != 0) { + ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &stateid); + if (ret != 0) + return ret; + mode |= FMODE_READ|FMODE_WRITE; + if (opendata->o_res.delegation_type != 0) + delegation = opendata->o_res.delegation_type; + smp_rmb(); + } + if (state->n_wronly != 0) { + ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &stateid); + if (ret != 0) + return ret; + mode |= FMODE_WRITE; + if (opendata->o_res.delegation_type != 0) + delegation = opendata->o_res.delegation_type; + smp_rmb(); + } + if (state->n_rdonly != 0) { + ret = nfs4_open_recover_helper(opendata, FMODE_READ, &stateid); + if (ret != 0) + return ret; + mode |= FMODE_READ; + } + clear_bit(NFS_DELEGATED_STATE, &state->flags); + if (mode == 0) + return 0; + if (opendata->o_res.delegation_type == 0) + opendata->o_res.delegation_type = delegation; + opendata->o_arg.open_flags |= mode; + newstate = nfs4_opendata_to_nfs4_state(opendata); + if (newstate != NULL) { + if (opendata->o_res.delegation_type != 0) { + struct nfs_inode *nfsi = NFS_I(newstate->inode); + int delegation_flags = 0; + if (nfsi->delegation) + delegation_flags = nfsi->delegation->flags; + if (!(delegation_flags & NFS_DELEGATION_NEED_RECLAIM)) + nfs_inode_set_delegation(newstate->inode, + opendata->owner->so_cred, + &opendata->o_res); + else + nfs_inode_reclaim_delegation(newstate->inode, + opendata->owner->so_cred, + &opendata->o_res); + } + nfs4_close_state(newstate, opendata->o_arg.open_flags); + } + if (newstate != state) + return -ESTALE; + return 0; +} + /* * OPEN_RECLAIM: * reclaim state on the server after a reboot. */ -static int _nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) +static int _nfs4_do_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) { - struct inode *inode = state->inode; - struct nfs_server *server = NFS_SERVER(inode); - struct nfs_delegation *delegation = NFS_I(inode)->delegation; - struct nfs_openargs o_arg = { - .fh = NFS_FH(inode), - .id = sp->so_id, - .open_flags = state->state, - .clientid = server->nfs4_state->cl_clientid, - .claim = NFS4_OPEN_CLAIM_PREVIOUS, - .bitmask = server->attr_bitmask, - }; - struct nfs_openres o_res = { - .server = server, /* Grrr */ - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR], - .rpc_argp = &o_arg, - .rpc_resp = &o_res, - .rpc_cred = sp->so_cred, - }; + struct nfs_delegation *delegation = NFS_I(state->inode)->delegation; + struct nfs4_opendata *opendata; + int delegation_type = 0; int status; if (delegation != NULL) { @@ -262,38 +458,27 @@ static int _nfs4_open_reclaim(struct nfs set_bit(NFS_DELEGATED_STATE, &state->flags); return 0; } - o_arg.u.delegation_type = delegation->type; + delegation_type = delegation->type; } - o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid); - if (o_arg.seqid == NULL) + opendata = nfs4_opendata_alloc(dentry, sp, 0, NULL); + if (opendata == NULL) return -ENOMEM; - status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR); - /* Confirm the sequence as being established */ - nfs_confirm_seqid(&sp->so_seqid, status); - nfs_increment_open_seqid(status, o_arg.seqid); - if (status == 0) { - memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid)); - if (o_res.delegation_type != 0) { - nfs_inode_reclaim_delegation(inode, sp->so_cred, &o_res); - /* Did the server issue an immediate delegation recall? */ - if (o_res.do_recall) - nfs_async_inode_return_delegation(inode, &o_res.stateid); - } - } - nfs_free_seqid(o_arg.seqid); - clear_bit(NFS_DELEGATED_STATE, &state->flags); - /* Ensure we update the inode attributes */ - NFS_CACHEINV(inode); + opendata->o_arg.claim = NFS4_OPEN_CLAIM_PREVIOUS; + opendata->o_arg.fh = NFS_FH(state->inode); + nfs_copy_fh(&opendata->o_res.fh, opendata->o_arg.fh); + opendata->o_arg.u.delegation_type = delegation_type; + status = nfs4_open_recover(opendata, state); + nfs4_opendata_free(opendata); return status; } -static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) +static int nfs4_do_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) { struct nfs_server *server = NFS_SERVER(state->inode); struct nfs4_exception exception = { }; int err; do { - err = _nfs4_open_reclaim(sp, state); + err = _nfs4_do_open_reclaim(sp, state, dentry); if (err != -NFS4ERR_DELAY) break; nfs4_handle_exception(server, err, &exception); @@ -301,63 +486,36 @@ static int nfs4_open_reclaim(struct nfs4 return err; } +static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) +{ + struct nfs_open_context *ctx; + int ret; + + ctx = nfs4_state_find_open_context(state); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + ret = nfs4_do_open_reclaim(sp, state, ctx->dentry); + put_nfs_open_context(ctx); + return ret; +} + static int _nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state) { struct nfs4_state_owner *sp = state->owner; - struct inode *inode = dentry->d_inode; - struct nfs_server *server = NFS_SERVER(inode); - struct dentry *parent = dget_parent(dentry); - struct nfs_openargs arg = { - .fh = NFS_FH(parent->d_inode), - .clientid = server->nfs4_state->cl_clientid, - .name = &dentry->d_name, - .id = sp->so_id, - .server = server, - .bitmask = server->attr_bitmask, - .claim = NFS4_OPEN_CLAIM_DELEGATE_CUR, - }; - struct nfs_openres res = { - .server = server, - }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR], - .rpc_argp = &arg, - .rpc_resp = &res, - .rpc_cred = sp->so_cred, - }; - int status = 0; + struct nfs4_opendata *opendata; + int ret; if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) - goto out; - if (state->state == 0) - goto out; - arg.seqid = nfs_alloc_seqid(&sp->so_seqid); - status = -ENOMEM; - if (arg.seqid == NULL) - goto out; - arg.open_flags = state->state; - memcpy(arg.u.delegation.data, state->stateid.data, sizeof(arg.u.delegation.data)); - status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR); - nfs_increment_open_seqid(status, arg.seqid); - if (status != 0) - goto out_free; - if(res.rflags & NFS4_OPEN_RESULT_CONFIRM) { - status = _nfs4_proc_open_confirm(server->client, NFS_FH(inode), - sp, &res.stateid, arg.seqid); - if (status != 0) - goto out_free; - } - nfs_confirm_seqid(&sp->so_seqid, 0); - if (status >= 0) { - memcpy(state->stateid.data, res.stateid.data, - sizeof(state->stateid.data)); - clear_bit(NFS_DELEGATED_STATE, &state->flags); - } -out_free: - nfs_free_seqid(arg.seqid); -out: - dput(parent); - return status; + return 0; + opendata = nfs4_opendata_alloc(dentry, sp, 0, NULL); + if (opendata == NULL) + return -ENOMEM; + opendata->o_arg.claim = NFS4_OPEN_CLAIM_DELEGATE_CUR; + memcpy(opendata->o_arg.u.delegation.data, state->stateid.data, + sizeof(opendata->o_arg.u.delegation.data)); + ret = nfs4_open_recover(opendata, state); + nfs4_opendata_free(opendata); + return ret; } int nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state) @@ -382,82 +540,202 @@ int nfs4_open_delegation_recall(struct d return err; } -static int _nfs4_proc_open_confirm(struct rpc_clnt *clnt, const struct nfs_fh *fh, struct nfs4_state_owner *sp, nfs4_stateid *stateid, struct nfs_seqid *seqid) +static void nfs4_open_confirm_prepare(struct rpc_task *task, void *calldata) { - struct nfs_open_confirmargs arg = { - .fh = fh, - .seqid = seqid, - .stateid = *stateid, - }; - struct nfs_open_confirmres res; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_CONFIRM], - .rpc_argp = &arg, - .rpc_resp = &res, - .rpc_cred = sp->so_cred, + struct nfs4_opendata *data = calldata; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_CONFIRM], + .rpc_argp = &data->c_arg, + .rpc_resp = &data->c_res, + .rpc_cred = data->owner->so_cred, }; + data->timestamp = jiffies; + rpc_call_setup(task, &msg, 0); +} + +static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata) +{ + struct nfs4_opendata *data = calldata; + + data->rpc_status = task->tk_status; + if (RPC_ASSASSINATED(task)) + return; + if (data->rpc_status == 0) { + memcpy(data->o_res.stateid.data, data->c_res.stateid.data, + sizeof(data->o_res.stateid.data)); + renew_lease(data->o_res.server, data->timestamp); + } + nfs_increment_open_seqid(data->rpc_status, data->c_arg.seqid); + nfs_confirm_seqid(&data->owner->so_seqid, data->rpc_status); +} + +static void nfs4_open_confirm_release(void *calldata) +{ + struct nfs4_opendata *data = calldata; + struct nfs4_state *state = NULL; + + /* If this request hasn't been cancelled, do nothing */ + if (data->cancelled == 0) + goto out_free; + /* In case of error, no cleanup! */ + if (data->rpc_status != 0) + goto out_free; + nfs_confirm_seqid(&data->owner->so_seqid, 0); + state = nfs4_opendata_to_nfs4_state(data); + if (state != NULL) + nfs4_close_state(state, data->o_arg.open_flags); +out_free: + nfs4_opendata_free(data); +} + +static const struct rpc_call_ops nfs4_open_confirm_ops = { + .rpc_call_prepare = nfs4_open_confirm_prepare, + .rpc_call_done = nfs4_open_confirm_done, + .rpc_release = nfs4_open_confirm_release, +}; + +/* + * Note: On error, nfs4_proc_open_confirm will free the struct nfs4_opendata + */ +static int _nfs4_proc_open_confirm(struct nfs4_opendata *data) +{ + struct nfs_server *server = NFS_SERVER(data->dir->d_inode); + struct rpc_task *task; int status; - status = rpc_call_sync(clnt, &msg, RPC_TASK_NOINTR); - /* Confirm the sequence as being established */ - nfs_confirm_seqid(&sp->so_seqid, status); - nfs_increment_open_seqid(status, seqid); - if (status >= 0) - memcpy(stateid, &res.stateid, sizeof(*stateid)); + atomic_inc(&data->count); + task = rpc_run_task(server->client, RPC_TASK_ASYNC, &nfs4_open_confirm_ops, data); + if (IS_ERR(task)) { + nfs4_opendata_free(data); + return PTR_ERR(task); + } + status = nfs4_wait_for_completion_rpc_task(task); + if (status != 0) { + data->cancelled = 1; + smp_wmb(); + } else + status = data->rpc_status; + rpc_release_task(task); return status; } -static int _nfs4_proc_open(struct inode *dir, struct nfs4_state_owner *sp, struct nfs_openargs *o_arg, struct nfs_openres *o_res) +static void nfs4_open_prepare(struct rpc_task *task, void *calldata) { - struct nfs_server *server = NFS_SERVER(dir); + struct nfs4_opendata *data = calldata; + struct nfs4_state_owner *sp = data->owner; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN], - .rpc_argp = o_arg, - .rpc_resp = o_res, + .rpc_argp = &data->o_arg, + .rpc_resp = &data->o_res, .rpc_cred = sp->so_cred, }; - int status; + + if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0) + return; + /* Update sequence id. */ + data->o_arg.id = sp->so_id; + data->o_arg.clientid = sp->so_client->cl_clientid; + if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS) + msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR]; + data->timestamp = jiffies; + rpc_call_setup(task, &msg, 0); +} - /* Update sequence id. The caller must serialize! */ - o_arg->id = sp->so_id; - o_arg->clientid = sp->so_client->cl_clientid; +static void nfs4_open_done(struct rpc_task *task, void *calldata) +{ + struct nfs4_opendata *data = calldata; - status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR); - if (status == 0) { - /* OPEN on anything except a regular file is disallowed in NFSv4 */ - switch (o_res->f_attr->mode & S_IFMT) { + data->rpc_status = task->tk_status; + if (RPC_ASSASSINATED(task)) + return; + if (task->tk_status == 0) { + switch (data->o_res.f_attr->mode & S_IFMT) { case S_IFREG: break; case S_IFLNK: - status = -ELOOP; + data->rpc_status = -ELOOP; break; case S_IFDIR: - status = -EISDIR; + data->rpc_status = -EISDIR; break; default: - status = -ENOTDIR; + data->rpc_status = -ENOTDIR; } + renew_lease(data->o_res.server, data->timestamp); } + nfs_increment_open_seqid(data->rpc_status, data->o_arg.seqid); +} - nfs_increment_open_seqid(status, o_arg->seqid); +static void nfs4_open_release(void *calldata) +{ + struct nfs4_opendata *data = calldata; + struct nfs4_state *state = NULL; + + /* If this request hasn't been cancelled, do nothing */ + if (data->cancelled == 0) + goto out_free; + /* In case of error, no cleanup! */ + if (data->rpc_status != 0) + goto out_free; + /* In case we need an open_confirm, no cleanup! */ + if (data->o_res.rflags & NFS4_OPEN_RESULT_CONFIRM) + goto out_free; + nfs_confirm_seqid(&data->owner->so_seqid, 0); + state = nfs4_opendata_to_nfs4_state(data); + if (state != NULL) + nfs4_close_state(state, data->o_arg.open_flags); +out_free: + nfs4_opendata_free(data); +} + +static const struct rpc_call_ops nfs4_open_ops = { + .rpc_call_prepare = nfs4_open_prepare, + .rpc_call_done = nfs4_open_done, + .rpc_release = nfs4_open_release, +}; + +/* + * Note: On error, nfs4_proc_open will free the struct nfs4_opendata + */ +static int _nfs4_proc_open(struct nfs4_opendata *data) +{ + struct inode *dir = data->dir->d_inode; + struct nfs_server *server = NFS_SERVER(dir); + struct nfs_openargs *o_arg = &data->o_arg; + struct nfs_openres *o_res = &data->o_res; + struct rpc_task *task; + int status; + + atomic_inc(&data->count); + task = rpc_run_task(server->client, RPC_TASK_ASYNC, &nfs4_open_ops, data); + if (IS_ERR(task)) { + nfs4_opendata_free(data); + return PTR_ERR(task); + } + status = nfs4_wait_for_completion_rpc_task(task); + if (status != 0) { + data->cancelled = 1; + smp_wmb(); + } else + status = data->rpc_status; + rpc_release_task(task); if (status != 0) - goto out; + return status; + if (o_arg->open_flags & O_CREAT) { update_changeattr(dir, &o_res->cinfo); nfs_post_op_update_inode(dir, o_res->dir_attr); } else nfs_refresh_inode(dir, o_res->dir_attr); if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) { - status = _nfs4_proc_open_confirm(server->client, &o_res->fh, - sp, &o_res->stateid, o_arg->seqid); + status = _nfs4_proc_open_confirm(data); if (status != 0) - goto out; + return status; } - nfs_confirm_seqid(&sp->so_seqid, 0); + nfs_confirm_seqid(&data->owner->so_seqid, 0); if (!(o_res->f_attr->valid & NFS_ATTR_FATTR)) - status = server->rpc_ops->getattr(server, &o_res->fh, o_res->f_attr); -out: - return status; + return server->rpc_ops->getattr(server, &o_res->fh, o_res->f_attr); + return 0; } static int _nfs4_do_access(struct inode *inode, struct rpc_cred *cred, int openflags) @@ -488,6 +766,15 @@ out: return -EACCES; } +int nfs4_recover_expired_lease(struct nfs_server *server) +{ + struct nfs4_client *clp = server->nfs4_state; + + if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) + nfs4_schedule_state_recovery(clp); + return nfs4_wait_clnt_recover(server->client, clp); +} + /* * OPEN_EXPIRED: * reclaim state on the server after a network partition. @@ -495,77 +782,31 @@ out: */ static int _nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) { - struct dentry *parent = dget_parent(dentry); - struct inode *dir = parent->d_inode; struct inode *inode = state->inode; - struct nfs_server *server = NFS_SERVER(dir); struct nfs_delegation *delegation = NFS_I(inode)->delegation; - struct nfs_fattr f_attr, dir_attr; - struct nfs_openargs o_arg = { - .fh = NFS_FH(dir), - .open_flags = state->state, - .name = &dentry->d_name, - .bitmask = server->attr_bitmask, - .claim = NFS4_OPEN_CLAIM_NULL, - }; - struct nfs_openres o_res = { - .f_attr = &f_attr, - .dir_attr = &dir_attr, - .server = server, - }; - int status = 0; + struct nfs4_opendata *opendata; + int openflags = state->state & (FMODE_READ|FMODE_WRITE); + int ret; if (delegation != NULL && !(delegation->flags & NFS_DELEGATION_NEED_RECLAIM)) { - status = _nfs4_do_access(inode, sp->so_cred, state->state); - if (status < 0) - goto out; + ret = _nfs4_do_access(inode, sp->so_cred, openflags); + if (ret < 0) + return ret; memcpy(&state->stateid, &delegation->stateid, sizeof(state->stateid)); set_bit(NFS_DELEGATED_STATE, &state->flags); - goto out; + return 0; } - o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid); - status = -ENOMEM; - if (o_arg.seqid == NULL) - goto out; - nfs_fattr_init(&f_attr); - nfs_fattr_init(&dir_attr); - status = _nfs4_proc_open(dir, sp, &o_arg, &o_res); - if (status != 0) - goto out_nodeleg; - /* Check if files differ */ - if ((f_attr.mode & S_IFMT) != (inode->i_mode & S_IFMT)) - goto out_stale; - /* Has the file handle changed? */ - if (nfs_compare_fh(&o_res.fh, NFS_FH(inode)) != 0) { - /* Verify if the change attributes are the same */ - if (f_attr.change_attr != NFS_I(inode)->change_attr) - goto out_stale; - if (nfs_size_to_loff_t(f_attr.size) != inode->i_size) - goto out_stale; - /* Lets just pretend that this is the same file */ - nfs_copy_fh(NFS_FH(inode), &o_res.fh); - NFS_I(inode)->fileid = f_attr.fileid; - } - memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid)); - if (o_res.delegation_type != 0) { - if (!(delegation->flags & NFS_DELEGATION_NEED_RECLAIM)) - nfs_inode_set_delegation(inode, sp->so_cred, &o_res); - else - nfs_inode_reclaim_delegation(inode, sp->so_cred, &o_res); + opendata = nfs4_opendata_alloc(dentry, sp, openflags, NULL); + if (opendata == NULL) + return -ENOMEM; + ret = nfs4_open_recover(opendata, state); + if (ret == -ESTALE) { + /* Invalidate the state owner so we don't ever use it again */ + nfs4_drop_state_owner(sp); + d_drop(dentry); } -out_nodeleg: - nfs_free_seqid(o_arg.seqid); - clear_bit(NFS_DELEGATED_STATE, &state->flags); -out: - dput(parent); - return status; -out_stale: - status = -ESTALE; - /* Invalidate the state owner so we don't ever use it again */ - nfs4_drop_state_owner(sp); - d_drop(dentry); - /* Should we be trying to close that stateid? */ - goto out_nodeleg; + nfs4_opendata_free(opendata); + return ret; } static inline int nfs4_do_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) @@ -584,26 +825,19 @@ static inline int nfs4_do_open_expired(s static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state) { - struct nfs_inode *nfsi = NFS_I(state->inode); struct nfs_open_context *ctx; - int status; + int ret; - spin_lock(&state->inode->i_lock); - list_for_each_entry(ctx, &nfsi->open_files, list) { - if (ctx->state != state) - continue; - get_nfs_open_context(ctx); - spin_unlock(&state->inode->i_lock); - status = nfs4_do_open_expired(sp, state, ctx->dentry); - put_nfs_open_context(ctx); - return status; - } - spin_unlock(&state->inode->i_lock); - return -ENOENT; + ctx = nfs4_state_find_open_context(state); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + ret = nfs4_do_open_expired(sp, state, ctx->dentry); + put_nfs_open_context(ctx); + return ret; } /* - * Returns an nfs4_state + an extra reference to the inode + * Returns a referenced nfs4_state if there is an open delegation on the file */ static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred *cred, struct nfs4_state **res) { @@ -616,6 +850,14 @@ static int _nfs4_open_delegated(struct i int open_flags = flags & (FMODE_READ|FMODE_WRITE); int err; + err = -ENOMEM; + if (!(sp = nfs4_get_state_owner(server, cred))) { + dprintk("%s: nfs4_get_state_owner failed!\n", __FUNCTION__); + return err; + } + err = nfs4_recover_expired_lease(server); + if (err != 0) + goto out_put_state_owner; /* Protect against reboot recovery - NOTE ORDER! */ down_read(&clp->cl_sem); /* Protect against delegation recall */ @@ -625,10 +867,6 @@ static int _nfs4_open_delegated(struct i if (delegation == NULL || (delegation->type & open_flags) != open_flags) goto out_err; err = -ENOMEM; - if (!(sp = nfs4_get_state_owner(server, cred))) { - dprintk("%s: nfs4_get_state_owner failed!\n", __FUNCTION__); - goto out_err; - } state = nfs4_get_open_state(inode, sp); if (state == NULL) goto out_err; @@ -636,39 +874,34 @@ static int _nfs4_open_delegated(struct i err = -ENOENT; if ((state->state & open_flags) == open_flags) { spin_lock(&inode->i_lock); - if (open_flags & FMODE_READ) - state->nreaders++; - if (open_flags & FMODE_WRITE) - state->nwriters++; + update_open_stateflags(state, open_flags); spin_unlock(&inode->i_lock); goto out_ok; } else if (state->state != 0) - goto out_err; + goto out_put_open_state; lock_kernel(); err = _nfs4_do_access(inode, cred, open_flags); unlock_kernel(); if (err != 0) - goto out_err; + goto out_put_open_state; set_bit(NFS_DELEGATED_STATE, &state->flags); update_open_stateid(state, &delegation->stateid, open_flags); out_ok: nfs4_put_state_owner(sp); up_read(&nfsi->rwsem); up_read(&clp->cl_sem); - igrab(inode); *res = state; - return 0; + return 0; +out_put_open_state: + nfs4_put_open_state(state); out_err: - if (sp != NULL) { - if (state != NULL) - nfs4_put_open_state(state); - nfs4_put_state_owner(sp); - } up_read(&nfsi->rwsem); up_read(&clp->cl_sem); if (err != -EACCES) nfs_inode_return_delegation(inode); +out_put_state_owner: + nfs4_put_state_owner(sp); return err; } @@ -689,7 +922,7 @@ static struct nfs4_state *nfs4_open_dele } /* - * Returns an nfs4_state + an referenced inode + * Returns a referenced nfs4_state */ static int _nfs4_do_open(struct inode *dir, struct dentry *dentry, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res) { @@ -697,73 +930,46 @@ static int _nfs4_do_open(struct inode *d struct nfs4_state *state = NULL; struct nfs_server *server = NFS_SERVER(dir); struct nfs4_client *clp = server->nfs4_state; - struct inode *inode = NULL; + struct nfs4_opendata *opendata; int status; - struct nfs_fattr f_attr, dir_attr; - struct nfs_openargs o_arg = { - .fh = NFS_FH(dir), - .open_flags = flags, - .name = &dentry->d_name, - .server = server, - .bitmask = server->attr_bitmask, - .claim = NFS4_OPEN_CLAIM_NULL, - }; - struct nfs_openres o_res = { - .f_attr = &f_attr, - .dir_attr = &dir_attr, - .server = server, - }; /* Protect against reboot recovery conflicts */ - down_read(&clp->cl_sem); status = -ENOMEM; if (!(sp = nfs4_get_state_owner(server, cred))) { dprintk("nfs4_do_open: nfs4_get_state_owner failed!\n"); goto out_err; } - if (flags & O_EXCL) { - u32 *p = (u32 *) o_arg.u.verifier.data; - p[0] = jiffies; - p[1] = current->pid; - } else - o_arg.u.attrs = sattr; - /* Serialization for the sequence id */ + status = nfs4_recover_expired_lease(server); + if (status != 0) + goto err_put_state_owner; + down_read(&clp->cl_sem); + status = -ENOMEM; + opendata = nfs4_opendata_alloc(dentry, sp, flags, sattr); + if (opendata == NULL) + goto err_put_state_owner; - o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid); - if (o_arg.seqid == NULL) - return -ENOMEM; - nfs_fattr_init(&f_attr); - nfs_fattr_init(&dir_attr); - status = _nfs4_proc_open(dir, sp, &o_arg, &o_res); + status = _nfs4_proc_open(opendata); if (status != 0) - goto out_err; + goto err_opendata_free; status = -ENOMEM; - inode = nfs_fhget(dir->i_sb, &o_res.fh, &f_attr); - if (!inode) - goto out_err; - state = nfs4_get_open_state(inode, sp); - if (!state) - goto out_err; - update_open_stateid(state, &o_res.stateid, flags); - if (o_res.delegation_type != 0) - nfs_inode_set_delegation(inode, cred, &o_res); - nfs_free_seqid(o_arg.seqid); + state = nfs4_opendata_to_nfs4_state(opendata); + if (state == NULL) + goto err_opendata_free; + if (opendata->o_res.delegation_type != 0) + nfs_inode_set_delegation(state->inode, cred, &opendata->o_res); + nfs4_opendata_free(opendata); nfs4_put_state_owner(sp); up_read(&clp->cl_sem); *res = state; return 0; +err_opendata_free: + nfs4_opendata_free(opendata); +err_put_state_owner: + nfs4_put_state_owner(sp); out_err: - if (sp != NULL) { - if (state != NULL) - nfs4_put_open_state(state); - nfs_free_seqid(o_arg.seqid); - nfs4_put_state_owner(sp); - } /* Note: clp->cl_sem must be released before nfs4_put_open_state()! */ up_read(&clp->cl_sem); - if (inode != NULL) - iput(inode); *res = NULL; return status; } @@ -830,6 +1036,7 @@ static int _nfs4_do_setattr(struct nfs_s .rpc_argp = &arg, .rpc_resp = &res, }; + unsigned long timestamp = jiffies; int status; nfs_fattr_init(fattr); @@ -841,6 +1048,8 @@ static int _nfs4_do_setattr(struct nfs_s memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid)); status = rpc_call_sync(server->client, &msg, 0); + if (status == 0 && state != NULL) + renew_lease(server, timestamp); return status; } @@ -865,12 +1074,13 @@ struct nfs4_closedata { struct nfs_closeargs arg; struct nfs_closeres res; struct nfs_fattr fattr; + unsigned long timestamp; }; -static void nfs4_free_closedata(struct nfs4_closedata *calldata) +static void nfs4_free_closedata(void *data) { - struct nfs4_state *state = calldata->state; - struct nfs4_state_owner *sp = state->owner; + struct nfs4_closedata *calldata = data; + struct nfs4_state_owner *sp = calldata->state->owner; nfs4_put_open_state(calldata->state); nfs_free_seqid(calldata->arg.seqid); @@ -878,12 +1088,14 @@ static void nfs4_free_closedata(struct n kfree(calldata); } -static void nfs4_close_done(struct rpc_task *task) +static void nfs4_close_done(struct rpc_task *task, void *data) { - struct nfs4_closedata *calldata = (struct nfs4_closedata *)task->tk_calldata; + struct nfs4_closedata *calldata = data; struct nfs4_state *state = calldata->state; struct nfs_server *server = NFS_SERVER(calldata->inode); + if (RPC_ASSASSINATED(task)) + return; /* hmm. we are done with the inode, and in the process of freeing * the state_owner. we keep this around to process errors */ @@ -892,6 +1104,7 @@ static void nfs4_close_done(struct rpc_t case 0: memcpy(&state->stateid, &calldata->res.stateid, sizeof(state->stateid)); + renew_lease(server, calldata->timestamp); break; case -NFS4ERR_STALE_STATEID: case -NFS4ERR_EXPIRED: @@ -904,12 +1117,11 @@ static void nfs4_close_done(struct rpc_t } } nfs_refresh_inode(calldata->inode, calldata->res.fattr); - nfs4_free_closedata(calldata); } -static void nfs4_close_begin(struct rpc_task *task) +static void nfs4_close_prepare(struct rpc_task *task, void *data) { - struct nfs4_closedata *calldata = (struct nfs4_closedata *)task->tk_calldata; + struct nfs4_closedata *calldata = data; struct nfs4_state *state = calldata->state; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE], @@ -918,10 +1130,8 @@ static void nfs4_close_begin(struct rpc_ .rpc_cred = state->owner->so_cred, }; int mode = 0, old_mode; - int status; - status = nfs_wait_on_sequence(calldata->arg.seqid, task); - if (status != 0) + if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0) return; /* Recalculate the new open mode in case someone reopened the file * while we were waiting in line to be scheduled. @@ -929,26 +1139,34 @@ static void nfs4_close_begin(struct rpc_ spin_lock(&state->owner->so_lock); spin_lock(&calldata->inode->i_lock); mode = old_mode = state->state; - if (state->nreaders == 0) - mode &= ~FMODE_READ; - if (state->nwriters == 0) - mode &= ~FMODE_WRITE; + if (state->n_rdwr == 0) { + if (state->n_rdonly == 0) + mode &= ~FMODE_READ; + if (state->n_wronly == 0) + mode &= ~FMODE_WRITE; + } nfs4_state_set_mode_locked(state, mode); spin_unlock(&calldata->inode->i_lock); spin_unlock(&state->owner->so_lock); if (mode == old_mode || test_bit(NFS_DELEGATED_STATE, &state->flags)) { - nfs4_free_closedata(calldata); - task->tk_exit = NULL; - rpc_exit(task, 0); + /* Note: exit _without_ calling nfs4_close_done */ + task->tk_action = NULL; return; } nfs_fattr_init(calldata->res.fattr); if (mode != 0) msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE]; calldata->arg.open_flags = mode; + calldata->timestamp = jiffies; rpc_call_setup(task, &msg, 0); } +static const struct rpc_call_ops nfs4_close_ops = { + .rpc_call_prepare = nfs4_close_prepare, + .rpc_call_done = nfs4_close_done, + .rpc_release = nfs4_free_closedata, +}; + /* * It is possible for data to be read/written from a mem-mapped file * after the sys_close call (which hits the vfs layer as a flush). @@ -981,8 +1199,7 @@ int nfs4_do_close(struct inode *inode, s calldata->res.fattr = &calldata->fattr; calldata->res.server = server; - status = nfs4_call_async(server->client, nfs4_close_begin, - nfs4_close_done, calldata); + status = nfs4_call_async(server->client, &nfs4_close_ops, calldata); if (status == 0) goto out; @@ -1034,7 +1251,7 @@ nfs4_atomic_open(struct inode *dir, stru d_add(dentry, NULL); return (struct dentry *)state; } - res = d_add_unique(dentry, state->inode); + res = d_add_unique(dentry, igrab(state->inode)); if (res != NULL) dentry = res; nfs4_intent_set_file(nd, dentry, state); @@ -1046,7 +1263,6 @@ nfs4_open_revalidate(struct inode *dir, { struct rpc_cred *cred; struct nfs4_state *state; - struct inode *inode; cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); if (IS_ERR(cred)) @@ -1070,9 +1286,7 @@ nfs4_open_revalidate(struct inode *dir, } goto out_drop; } - inode = state->inode; - iput(inode); - if (inode == dentry->d_inode) { + if (state->inode == dentry->d_inode) { nfs4_intent_set_file(nd, dentry, state); return 1; } @@ -1506,10 +1720,17 @@ static int _nfs4_proc_write(struct nfs_w dprintk("NFS call write %d @ %Ld\n", wdata->args.count, (long long) wdata->args.offset); + wdata->args.bitmask = server->attr_bitmask; + wdata->res.server = server; + wdata->timestamp = jiffies; nfs_fattr_init(fattr); status = rpc_call_sync(server->client, &msg, rpcflags); dprintk("NFS reply write: %d\n", status); - return status; + if (status < 0) + return status; + renew_lease(server, wdata->timestamp); + nfs_post_op_update_inode(inode, fattr); + return wdata->res.count; } static int nfs4_proc_write(struct nfs_write_data *wdata) @@ -1540,9 +1761,16 @@ static int _nfs4_proc_commit(struct nfs_ dprintk("NFS call commit %d @ %Ld\n", cdata->args.count, (long long) cdata->args.offset); + cdata->args.bitmask = server->attr_bitmask; + cdata->res.server = server; + cdata->timestamp = jiffies; nfs_fattr_init(fattr); status = rpc_call_sync(server->client, &msg, 0); + if (status >= 0) + renew_lease(server, cdata->timestamp); dprintk("NFS reply commit: %d\n", status); + if (status >= 0) + nfs_post_op_update_inode(inode, fattr); return status; } @@ -1592,7 +1820,7 @@ nfs4_proc_create(struct inode *dir, stru status = PTR_ERR(state); goto out; } - d_instantiate(dentry, state->inode); + d_instantiate(dentry, igrab(state->inode)); if (flags & O_EXCL) { struct nfs_fattr fattr; status = nfs4_do_setattr(NFS_SERVER(dir), &fattr, @@ -2116,10 +2344,9 @@ static int nfs4_proc_pathconf(struct nfs return err; } -static void -nfs4_read_done(struct rpc_task *task) +static void nfs4_read_done(struct rpc_task *task, void *calldata) { - struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata; + struct nfs_read_data *data = calldata; struct inode *inode = data->inode; if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { @@ -2129,9 +2356,14 @@ nfs4_read_done(struct rpc_task *task) if (task->tk_status > 0) renew_lease(NFS_SERVER(inode), data->timestamp); /* Call back common NFS readpage processing */ - nfs_readpage_result(task); + nfs_readpage_result(task, calldata); } +static const struct rpc_call_ops nfs4_read_ops = { + .rpc_call_done = nfs4_read_done, + .rpc_release = nfs_readdata_release, +}; + static void nfs4_proc_read_setup(struct nfs_read_data *data) { @@ -2151,14 +2383,13 @@ nfs4_proc_read_setup(struct nfs_read_dat flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0); /* Finalize the task. */ - rpc_init_task(task, NFS_CLIENT(inode), nfs4_read_done, flags); + rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs4_read_ops, data); rpc_call_setup(task, &msg, 0); } -static void -nfs4_write_done(struct rpc_task *task) +static void nfs4_write_done(struct rpc_task *task, void *calldata) { - struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; + struct nfs_write_data *data = calldata; struct inode *inode = data->inode; if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { @@ -2170,9 +2401,14 @@ nfs4_write_done(struct rpc_task *task) nfs_post_op_update_inode(inode, data->res.fattr); } /* Call back common NFS writeback processing */ - nfs_writeback_done(task); + nfs_writeback_done(task, calldata); } +static const struct rpc_call_ops nfs4_write_ops = { + .rpc_call_done = nfs4_write_done, + .rpc_release = nfs_writedata_release, +}; + static void nfs4_proc_write_setup(struct nfs_write_data *data, int how) { @@ -2205,14 +2441,13 @@ nfs4_proc_write_setup(struct nfs_write_d flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; /* Finalize the task. */ - rpc_init_task(task, NFS_CLIENT(inode), nfs4_write_done, flags); + rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs4_write_ops, data); rpc_call_setup(task, &msg, 0); } -static void -nfs4_commit_done(struct rpc_task *task) +static void nfs4_commit_done(struct rpc_task *task, void *calldata) { - struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; + struct nfs_write_data *data = calldata; struct inode *inode = data->inode; if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) { @@ -2222,9 +2457,14 @@ nfs4_commit_done(struct rpc_task *task) if (task->tk_status >= 0) nfs_post_op_update_inode(inode, data->res.fattr); /* Call back common NFS writeback processing */ - nfs_commit_done(task); + nfs_commit_done(task, calldata); } +static const struct rpc_call_ops nfs4_commit_ops = { + .rpc_call_done = nfs4_commit_done, + .rpc_release = nfs_commit_release, +}; + static void nfs4_proc_commit_setup(struct nfs_write_data *data, int how) { @@ -2246,7 +2486,7 @@ nfs4_proc_commit_setup(struct nfs_write_ flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; /* Finalize the task. */ - rpc_init_task(task, NFS_CLIENT(inode), nfs4_commit_done, flags); + rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs4_commit_ops, data); rpc_call_setup(task, &msg, 0); } @@ -2254,11 +2494,10 @@ nfs4_proc_commit_setup(struct nfs_write_ * nfs4_proc_async_renew(): This is not one of the nfs_rpc_ops; it is a special * standalone procedure for queueing an asynchronous RENEW. */ -static void -renew_done(struct rpc_task *task) +static void nfs4_renew_done(struct rpc_task *task, void *data) { struct nfs4_client *clp = (struct nfs4_client *)task->tk_msg.rpc_argp; - unsigned long timestamp = (unsigned long)task->tk_calldata; + unsigned long timestamp = (unsigned long)data; if (task->tk_status < 0) { switch (task->tk_status) { @@ -2275,26 +2514,28 @@ renew_done(struct rpc_task *task) spin_unlock(&clp->cl_lock); } -int -nfs4_proc_async_renew(struct nfs4_client *clp) +static const struct rpc_call_ops nfs4_renew_ops = { + .rpc_call_done = nfs4_renew_done, +}; + +int nfs4_proc_async_renew(struct nfs4_client *clp, struct rpc_cred *cred) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], .rpc_argp = clp, - .rpc_cred = clp->cl_cred, + .rpc_cred = cred, }; return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT, - renew_done, (void *)jiffies); + &nfs4_renew_ops, (void *)jiffies); } -int -nfs4_proc_renew(struct nfs4_client *clp) +int nfs4_proc_renew(struct nfs4_client *clp, struct rpc_cred *cred) { struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RENEW], .rpc_argp = clp, - .rpc_cred = clp->cl_cred, + .rpc_cred = cred, }; unsigned long now = jiffies; int status; @@ -2510,7 +2751,7 @@ nfs4_async_handle_error(struct rpc_task case -NFS4ERR_EXPIRED: rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL, NULL); nfs4_schedule_state_recovery(clp); - if (test_bit(NFS4CLNT_OK, &clp->cl_state)) + if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) == 0) rpc_wake_up_task(task); task->tk_status = 0; return -EAGAIN; @@ -2527,25 +2768,25 @@ nfs4_async_handle_error(struct rpc_task return 0; } +static int nfs4_wait_bit_interruptible(void *word) +{ + if (signal_pending(current)) + return -ERESTARTSYS; + schedule(); + return 0; +} + static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp) { - DEFINE_WAIT(wait); sigset_t oldset; - int interruptible, res = 0; + int res; might_sleep(); rpc_clnt_sigmask(clnt, &oldset); - interruptible = TASK_UNINTERRUPTIBLE; - if (clnt->cl_intr) - interruptible = TASK_INTERRUPTIBLE; - prepare_to_wait(&clp->cl_waitq, &wait, interruptible); - nfs4_schedule_state_recovery(clp); - if (clnt->cl_intr && signalled()) - res = -ERESTARTSYS; - else if (!test_bit(NFS4CLNT_OK, &clp->cl_state)) - schedule(); - finish_wait(&clp->cl_waitq, &wait); + res = wait_on_bit(&clp->cl_state, NFS4CLNT_STATE_RECOVER, + nfs4_wait_bit_interruptible, + TASK_INTERRUPTIBLE); rpc_clnt_sigunmask(clnt, &oldset); return res; } @@ -2588,6 +2829,7 @@ int nfs4_handle_exception(const struct n case -NFS4ERR_STALE_CLIENTID: case -NFS4ERR_STALE_STATEID: case -NFS4ERR_EXPIRED: + nfs4_schedule_state_recovery(clp); ret = nfs4_wait_clnt_recover(server->client, clp); if (ret == 0) exception->retry = 1; @@ -2604,7 +2846,7 @@ int nfs4_handle_exception(const struct n return nfs4_map_errors(ret); } -int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short port) +int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short port, struct rpc_cred *cred) { nfs4_verifier sc_verifier; struct nfs4_setclientid setclientid = { @@ -2615,7 +2857,7 @@ int nfs4_proc_setclientid(struct nfs4_cl .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID], .rpc_argp = &setclientid, .rpc_resp = clp, - .rpc_cred = clp->cl_cred, + .rpc_cred = cred, }; u32 *p; int loop = 0; @@ -2629,7 +2871,7 @@ int nfs4_proc_setclientid(struct nfs4_cl setclientid.sc_name_len = scnprintf(setclientid.sc_name, sizeof(setclientid.sc_name), "%s/%u.%u.%u.%u %s %u", clp->cl_ipaddr, NIPQUAD(clp->cl_addr.s_addr), - clp->cl_cred->cr_ops->cr_name, + cred->cr_ops->cr_name, clp->cl_id_uniquifier); setclientid.sc_netid_len = scnprintf(setclientid.sc_netid, sizeof(setclientid.sc_netid), "tcp"); @@ -2652,14 +2894,14 @@ int nfs4_proc_setclientid(struct nfs4_cl } int -nfs4_proc_setclientid_confirm(struct nfs4_client *clp) +nfs4_proc_setclientid_confirm(struct nfs4_client *clp, struct rpc_cred *cred) { struct nfs_fsinfo fsinfo; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID_CONFIRM], .rpc_argp = clp, .rpc_resp = &fsinfo, - .rpc_cred = clp->cl_cred, + .rpc_cred = cred, }; unsigned long now; int status; @@ -2670,24 +2912,83 @@ nfs4_proc_setclientid_confirm(struct nfs spin_lock(&clp->cl_lock); clp->cl_lease_time = fsinfo.lease_time * HZ; clp->cl_last_renewal = now; + clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); spin_unlock(&clp->cl_lock); } return status; } -static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid) +struct nfs4_delegreturndata { + struct nfs4_delegreturnargs args; + struct nfs_fh fh; + nfs4_stateid stateid; + struct rpc_cred *cred; + unsigned long timestamp; + const struct nfs_server *server; + int rpc_status; +}; + +static void nfs4_delegreturn_prepare(struct rpc_task *task, void *calldata) { - struct nfs4_delegreturnargs args = { - .fhandle = NFS_FH(inode), - .stateid = stateid, - }; + struct nfs4_delegreturndata *data = calldata; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DELEGRETURN], - .rpc_argp = &args, - .rpc_cred = cred, + .rpc_argp = &data->args, + .rpc_cred = data->cred, }; + rpc_call_setup(task, &msg, 0); +} - return rpc_call_sync(NFS_CLIENT(inode), &msg, 0); +static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata) +{ + struct nfs4_delegreturndata *data = calldata; + data->rpc_status = task->tk_status; + if (data->rpc_status == 0) + renew_lease(data->server, data->timestamp); +} + +static void nfs4_delegreturn_release(void *calldata) +{ + struct nfs4_delegreturndata *data = calldata; + + put_rpccred(data->cred); + kfree(calldata); +} + +const static struct rpc_call_ops nfs4_delegreturn_ops = { + .rpc_call_prepare = nfs4_delegreturn_prepare, + .rpc_call_done = nfs4_delegreturn_done, + .rpc_release = nfs4_delegreturn_release, +}; + +static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid) +{ + struct nfs4_delegreturndata *data; + struct rpc_task *task; + int status; + + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + data->args.fhandle = &data->fh; + data->args.stateid = &data->stateid; + nfs_copy_fh(&data->fh, NFS_FH(inode)); + memcpy(&data->stateid, stateid, sizeof(data->stateid)); + data->cred = get_rpccred(cred); + data->timestamp = jiffies; + data->server = NFS_SERVER(inode); + data->rpc_status = 0; + + task = rpc_run_task(NFS_CLIENT(inode), RPC_TASK_ASYNC, &nfs4_delegreturn_ops, data); + if (IS_ERR(task)) { + nfs4_delegreturn_release(data); + return PTR_ERR(task); + } + status = nfs4_wait_for_completion_rpc_task(task); + if (status == 0) + status = data->rpc_status; + rpc_release_task(task); + return status; } int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid) @@ -2725,43 +3026,17 @@ nfs4_set_lock_task_retry(unsigned long t return timeout; } -static inline int -nfs4_lck_type(int cmd, struct file_lock *request) -{ - /* set lock type */ - switch (request->fl_type) { - case F_RDLCK: - return IS_SETLKW(cmd) ? NFS4_READW_LT : NFS4_READ_LT; - case F_WRLCK: - return IS_SETLKW(cmd) ? NFS4_WRITEW_LT : NFS4_WRITE_LT; - case F_UNLCK: - return NFS4_WRITE_LT; - } - BUG(); - return 0; -} - -static inline uint64_t -nfs4_lck_length(struct file_lock *request) -{ - if (request->fl_end == OFFSET_MAX) - return ~(uint64_t)0; - return request->fl_end - request->fl_start + 1; -} - static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request) { struct inode *inode = state->inode; struct nfs_server *server = NFS_SERVER(inode); struct nfs4_client *clp = server->nfs4_state; - struct nfs_lockargs arg = { + struct nfs_lockt_args arg = { .fh = NFS_FH(inode), - .type = nfs4_lck_type(cmd, request), - .offset = request->fl_start, - .length = nfs4_lck_length(request), + .fl = request, }; - struct nfs_lockres res = { - .server = server, + struct nfs_lockt_res res = { + .denied = request, }; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKT], @@ -2769,36 +3044,23 @@ static int _nfs4_proc_getlk(struct nfs4_ .rpc_resp = &res, .rpc_cred = state->owner->so_cred, }; - struct nfs_lowner nlo; struct nfs4_lock_state *lsp; int status; down_read(&clp->cl_sem); - nlo.clientid = clp->cl_clientid; + arg.lock_owner.clientid = clp->cl_clientid; status = nfs4_set_lock_state(state, request); if (status != 0) goto out; lsp = request->fl_u.nfs4_fl.owner; - nlo.id = lsp->ls_id; - arg.u.lockt = &nlo; + arg.lock_owner.id = lsp->ls_id; status = rpc_call_sync(server->client, &msg, 0); - if (!status) { - request->fl_type = F_UNLCK; - } else if (status == -NFS4ERR_DENIED) { - int64_t len, start, end; - start = res.u.denied.offset; - len = res.u.denied.length; - end = start + len - 1; - if (end < 0 || len == 0) - request->fl_end = OFFSET_MAX; - else - request->fl_end = (loff_t)end; - request->fl_start = (loff_t)start; - request->fl_type = F_WRLCK; - if (res.u.denied.type & 1) - request->fl_type = F_RDLCK; - request->fl_pid = 0; - status = 0; + switch (status) { + case 0: + request->fl_type = F_UNLCK; + break; + case -NFS4ERR_DENIED: + status = 0; } out: up_read(&clp->cl_sem); @@ -2838,196 +3100,314 @@ static int do_vfs_lock(struct file *file } struct nfs4_unlockdata { - struct nfs_lockargs arg; - struct nfs_locku_opargs luargs; - struct nfs_lockres res; + struct nfs_locku_args arg; + struct nfs_locku_res res; struct nfs4_lock_state *lsp; struct nfs_open_context *ctx; - atomic_t refcount; - struct completion completion; + struct file_lock fl; + const struct nfs_server *server; + unsigned long timestamp; }; -static void nfs4_locku_release_calldata(struct nfs4_unlockdata *calldata) -{ - if (atomic_dec_and_test(&calldata->refcount)) { - nfs_free_seqid(calldata->luargs.seqid); - nfs4_put_lock_state(calldata->lsp); - put_nfs_open_context(calldata->ctx); - kfree(calldata); - } +static struct nfs4_unlockdata *nfs4_alloc_unlockdata(struct file_lock *fl, + struct nfs_open_context *ctx, + struct nfs4_lock_state *lsp, + struct nfs_seqid *seqid) +{ + struct nfs4_unlockdata *p; + struct inode *inode = lsp->ls_state->inode; + + p = kmalloc(sizeof(*p), GFP_KERNEL); + if (p == NULL) + return NULL; + p->arg.fh = NFS_FH(inode); + p->arg.fl = &p->fl; + p->arg.seqid = seqid; + p->arg.stateid = &lsp->ls_stateid; + p->lsp = lsp; + atomic_inc(&lsp->ls_count); + /* Ensure we don't close file until we're done freeing locks! */ + p->ctx = get_nfs_open_context(ctx); + memcpy(&p->fl, fl, sizeof(p->fl)); + p->server = NFS_SERVER(inode); + return p; } -static void nfs4_locku_complete(struct nfs4_unlockdata *calldata) +static void nfs4_locku_release_calldata(void *data) { - complete(&calldata->completion); - nfs4_locku_release_calldata(calldata); + struct nfs4_unlockdata *calldata = data; + nfs_free_seqid(calldata->arg.seqid); + nfs4_put_lock_state(calldata->lsp); + put_nfs_open_context(calldata->ctx); + kfree(calldata); } -static void nfs4_locku_done(struct rpc_task *task) +static void nfs4_locku_done(struct rpc_task *task, void *data) { - struct nfs4_unlockdata *calldata = (struct nfs4_unlockdata *)task->tk_calldata; + struct nfs4_unlockdata *calldata = data; - nfs_increment_lock_seqid(task->tk_status, calldata->luargs.seqid); + if (RPC_ASSASSINATED(task)) + return; + nfs_increment_lock_seqid(task->tk_status, calldata->arg.seqid); switch (task->tk_status) { case 0: memcpy(calldata->lsp->ls_stateid.data, - calldata->res.u.stateid.data, + calldata->res.stateid.data, sizeof(calldata->lsp->ls_stateid.data)); + renew_lease(calldata->server, calldata->timestamp); break; case -NFS4ERR_STALE_STATEID: case -NFS4ERR_EXPIRED: - nfs4_schedule_state_recovery(calldata->res.server->nfs4_state); + nfs4_schedule_state_recovery(calldata->server->nfs4_state); break; default: - if (nfs4_async_handle_error(task, calldata->res.server) == -EAGAIN) { + if (nfs4_async_handle_error(task, calldata->server) == -EAGAIN) { rpc_restart_call(task); - return; } } - nfs4_locku_complete(calldata); } -static void nfs4_locku_begin(struct rpc_task *task) +static void nfs4_locku_prepare(struct rpc_task *task, void *data) { - struct nfs4_unlockdata *calldata = (struct nfs4_unlockdata *)task->tk_calldata; + struct nfs4_unlockdata *calldata = data; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKU], .rpc_argp = &calldata->arg, .rpc_resp = &calldata->res, .rpc_cred = calldata->lsp->ls_state->owner->so_cred, }; - int status; - status = nfs_wait_on_sequence(calldata->luargs.seqid, task); - if (status != 0) + if (nfs_wait_on_sequence(calldata->arg.seqid, task) != 0) return; if ((calldata->lsp->ls_flags & NFS_LOCK_INITIALIZED) == 0) { - nfs4_locku_complete(calldata); - task->tk_exit = NULL; - rpc_exit(task, 0); + /* Note: exit _without_ running nfs4_locku_done */ + task->tk_action = NULL; return; } + calldata->timestamp = jiffies; rpc_call_setup(task, &msg, 0); } +static const struct rpc_call_ops nfs4_locku_ops = { + .rpc_call_prepare = nfs4_locku_prepare, + .rpc_call_done = nfs4_locku_done, + .rpc_release = nfs4_locku_release_calldata, +}; + +static struct rpc_task *nfs4_do_unlck(struct file_lock *fl, + struct nfs_open_context *ctx, + struct nfs4_lock_state *lsp, + struct nfs_seqid *seqid) +{ + struct nfs4_unlockdata *data; + struct rpc_task *task; + + data = nfs4_alloc_unlockdata(fl, ctx, lsp, seqid); + if (data == NULL) { + nfs_free_seqid(seqid); + return ERR_PTR(-ENOMEM); + } + + /* Unlock _before_ we do the RPC call */ + do_vfs_lock(fl->fl_file, fl); + task = rpc_run_task(NFS_CLIENT(lsp->ls_state->inode), RPC_TASK_ASYNC, &nfs4_locku_ops, data); + if (IS_ERR(task)) + nfs4_locku_release_calldata(data); + return task; +} + static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request) { - struct nfs4_unlockdata *calldata; - struct inode *inode = state->inode; - struct nfs_server *server = NFS_SERVER(inode); + struct nfs_seqid *seqid; struct nfs4_lock_state *lsp; - int status; + struct rpc_task *task; + int status = 0; /* Is this a delegated lock? */ if (test_bit(NFS_DELEGATED_STATE, &state->flags)) - return do_vfs_lock(request->fl_file, request); + goto out_unlock; + /* Is this open_owner holding any locks on the server? */ + if (test_bit(LK_STATE_IN_USE, &state->flags) == 0) + goto out_unlock; status = nfs4_set_lock_state(state, request); if (status != 0) - return status; + goto out_unlock; lsp = request->fl_u.nfs4_fl.owner; - /* We might have lost the locks! */ - if ((lsp->ls_flags & NFS_LOCK_INITIALIZED) == 0) - return 0; - calldata = kmalloc(sizeof(*calldata), GFP_KERNEL); - if (calldata == NULL) - return -ENOMEM; - calldata->luargs.seqid = nfs_alloc_seqid(&lsp->ls_seqid); - if (calldata->luargs.seqid == NULL) { - kfree(calldata); - return -ENOMEM; - } - calldata->luargs.stateid = &lsp->ls_stateid; - calldata->arg.fh = NFS_FH(inode); - calldata->arg.type = nfs4_lck_type(cmd, request); - calldata->arg.offset = request->fl_start; - calldata->arg.length = nfs4_lck_length(request); - calldata->arg.u.locku = &calldata->luargs; - calldata->res.server = server; - calldata->lsp = lsp; - atomic_inc(&lsp->ls_count); - - /* Ensure we don't close file until we're done freeing locks! */ - calldata->ctx = get_nfs_open_context((struct nfs_open_context*)request->fl_file->private_data); - - atomic_set(&calldata->refcount, 2); - init_completion(&calldata->completion); - - status = nfs4_call_async(NFS_SERVER(inode)->client, nfs4_locku_begin, - nfs4_locku_done, calldata); - if (status == 0) - wait_for_completion_interruptible(&calldata->completion); + status = -ENOMEM; + seqid = nfs_alloc_seqid(&lsp->ls_seqid); + if (seqid == NULL) + goto out_unlock; + task = nfs4_do_unlck(request, request->fl_file->private_data, lsp, seqid); + status = PTR_ERR(task); + if (IS_ERR(task)) + goto out_unlock; + status = nfs4_wait_for_completion_rpc_task(task); + rpc_release_task(task); + return status; +out_unlock: do_vfs_lock(request->fl_file, request); - nfs4_locku_release_calldata(calldata); return status; } -static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *request, int reclaim) +struct nfs4_lockdata { + struct nfs_lock_args arg; + struct nfs_lock_res res; + struct nfs4_lock_state *lsp; + struct nfs_open_context *ctx; + struct file_lock fl; + unsigned long timestamp; + int rpc_status; + int cancelled; +}; + +static struct nfs4_lockdata *nfs4_alloc_lockdata(struct file_lock *fl, + struct nfs_open_context *ctx, struct nfs4_lock_state *lsp) { - struct inode *inode = state->inode; + struct nfs4_lockdata *p; + struct inode *inode = lsp->ls_state->inode; struct nfs_server *server = NFS_SERVER(inode); - struct nfs4_lock_state *lsp = request->fl_u.nfs4_fl.owner; - struct nfs_lock_opargs largs = { - .lock_stateid = &lsp->ls_stateid, - .open_stateid = &state->stateid, - .lock_owner = { - .clientid = server->nfs4_state->cl_clientid, - .id = lsp->ls_id, - }, - .reclaim = reclaim, - }; - struct nfs_lockargs arg = { - .fh = NFS_FH(inode), - .type = nfs4_lck_type(cmd, request), - .offset = request->fl_start, - .length = nfs4_lck_length(request), - .u = { - .lock = &largs, - }, - }; - struct nfs_lockres res = { - .server = server, - }; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (p == NULL) + return NULL; + + p->arg.fh = NFS_FH(inode); + p->arg.fl = &p->fl; + p->arg.lock_seqid = nfs_alloc_seqid(&lsp->ls_seqid); + if (p->arg.lock_seqid == NULL) + goto out_free; + p->arg.lock_stateid = &lsp->ls_stateid; + p->arg.lock_owner.clientid = server->nfs4_state->cl_clientid; + p->arg.lock_owner.id = lsp->ls_id; + p->lsp = lsp; + atomic_inc(&lsp->ls_count); + p->ctx = get_nfs_open_context(ctx); + memcpy(&p->fl, fl, sizeof(p->fl)); + return p; +out_free: + kfree(p); + return NULL; +} + +static void nfs4_lock_prepare(struct rpc_task *task, void *calldata) +{ + struct nfs4_lockdata *data = calldata; + struct nfs4_state *state = data->lsp->ls_state; + struct nfs4_state_owner *sp = state->owner; struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCK], - .rpc_argp = &arg, - .rpc_resp = &res, - .rpc_cred = state->owner->so_cred, + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCK], + .rpc_argp = &data->arg, + .rpc_resp = &data->res, + .rpc_cred = sp->so_cred, }; - int status = -ENOMEM; - largs.lock_seqid = nfs_alloc_seqid(&lsp->ls_seqid); - if (largs.lock_seqid == NULL) - return -ENOMEM; - if (!(lsp->ls_seqid.flags & NFS_SEQID_CONFIRMED)) { - struct nfs4_state_owner *owner = state->owner; - - largs.open_seqid = nfs_alloc_seqid(&owner->so_seqid); - if (largs.open_seqid == NULL) + if (nfs_wait_on_sequence(data->arg.lock_seqid, task) != 0) + return; + dprintk("%s: begin!\n", __FUNCTION__); + /* Do we need to do an open_to_lock_owner? */ + if (!(data->arg.lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED)) { + data->arg.open_seqid = nfs_alloc_seqid(&sp->so_seqid); + if (data->arg.open_seqid == NULL) { + data->rpc_status = -ENOMEM; + task->tk_action = NULL; goto out; - largs.new_lock_owner = 1; - status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR); - /* increment open seqid on success, and seqid mutating errors */ - if (largs.new_lock_owner != 0) { - nfs_increment_open_seqid(status, largs.open_seqid); - if (status == 0) - nfs_confirm_seqid(&lsp->ls_seqid, 0); } - nfs_free_seqid(largs.open_seqid); - } else - status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR); - /* increment lock seqid on success, and seqid mutating errors*/ - nfs_increment_lock_seqid(status, largs.lock_seqid); - /* save the returned stateid. */ - if (status == 0) { - memcpy(lsp->ls_stateid.data, res.u.stateid.data, - sizeof(lsp->ls_stateid.data)); - lsp->ls_flags |= NFS_LOCK_INITIALIZED; - } else if (status == -NFS4ERR_DENIED) - status = -EAGAIN; + data->arg.open_stateid = &state->stateid; + data->arg.new_lock_owner = 1; + } + data->timestamp = jiffies; + rpc_call_setup(task, &msg, 0); out: - nfs_free_seqid(largs.lock_seqid); - return status; + dprintk("%s: done!, ret = %d\n", __FUNCTION__, data->rpc_status); +} + +static void nfs4_lock_done(struct rpc_task *task, void *calldata) +{ + struct nfs4_lockdata *data = calldata; + + dprintk("%s: begin!\n", __FUNCTION__); + + data->rpc_status = task->tk_status; + if (RPC_ASSASSINATED(task)) + goto out; + if (data->arg.new_lock_owner != 0) { + nfs_increment_open_seqid(data->rpc_status, data->arg.open_seqid); + if (data->rpc_status == 0) + nfs_confirm_seqid(&data->lsp->ls_seqid, 0); + else + goto out; + } + if (data->rpc_status == 0) { + memcpy(data->lsp->ls_stateid.data, data->res.stateid.data, + sizeof(data->lsp->ls_stateid.data)); + data->lsp->ls_flags |= NFS_LOCK_INITIALIZED; + renew_lease(NFS_SERVER(data->ctx->dentry->d_inode), data->timestamp); + } + nfs_increment_lock_seqid(data->rpc_status, data->arg.lock_seqid); +out: + dprintk("%s: done, ret = %d!\n", __FUNCTION__, data->rpc_status); +} + +static void nfs4_lock_release(void *calldata) +{ + struct nfs4_lockdata *data = calldata; + + dprintk("%s: begin!\n", __FUNCTION__); + if (data->arg.open_seqid != NULL) + nfs_free_seqid(data->arg.open_seqid); + if (data->cancelled != 0) { + struct rpc_task *task; + task = nfs4_do_unlck(&data->fl, data->ctx, data->lsp, + data->arg.lock_seqid); + if (!IS_ERR(task)) + rpc_release_task(task); + dprintk("%s: cancelling lock!\n", __FUNCTION__); + } else + nfs_free_seqid(data->arg.lock_seqid); + nfs4_put_lock_state(data->lsp); + put_nfs_open_context(data->ctx); + kfree(data); + dprintk("%s: done!\n", __FUNCTION__); +} + +static const struct rpc_call_ops nfs4_lock_ops = { + .rpc_call_prepare = nfs4_lock_prepare, + .rpc_call_done = nfs4_lock_done, + .rpc_release = nfs4_lock_release, +}; + +static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int reclaim) +{ + struct nfs4_lockdata *data; + struct rpc_task *task; + int ret; + + dprintk("%s: begin!\n", __FUNCTION__); + data = nfs4_alloc_lockdata(fl, fl->fl_file->private_data, + fl->fl_u.nfs4_fl.owner); + if (data == NULL) + return -ENOMEM; + if (IS_SETLKW(cmd)) + data->arg.block = 1; + if (reclaim != 0) + data->arg.reclaim = 1; + task = rpc_run_task(NFS_CLIENT(state->inode), RPC_TASK_ASYNC, + &nfs4_lock_ops, data); + if (IS_ERR(task)) { + nfs4_lock_release(data); + return PTR_ERR(task); + } + ret = nfs4_wait_for_completion_rpc_task(task); + if (ret == 0) { + ret = data->rpc_status; + if (ret == -NFS4ERR_DENIED) + ret = -EAGAIN; + } else + data->cancelled = 1; + rpc_release_task(task); + dprintk("%s: done, ret = %d!\n", __FUNCTION__, ret); + return ret; } static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request) diff --git a/fs/nfs/nfs4renewd.c b/fs/nfs/nfs4renewd.c index a300162..5d764d8 100644 --- a/fs/nfs/nfs4renewd.c +++ b/fs/nfs/nfs4renewd.c @@ -54,6 +54,7 @@ #include #include #include "nfs4_fs.h" +#include "delegation.h" #define NFSDBG_FACILITY NFSDBG_PROC @@ -61,6 +62,7 @@ void nfs4_renew_state(void *data) { struct nfs4_client *clp = (struct nfs4_client *)data; + struct rpc_cred *cred; long lease, timeout; unsigned long last, now; @@ -68,7 +70,7 @@ nfs4_renew_state(void *data) dprintk("%s: start\n", __FUNCTION__); /* Are there any active superblocks? */ if (list_empty(&clp->cl_superblocks)) - goto out; + goto out; spin_lock(&clp->cl_lock); lease = clp->cl_lease_time; last = clp->cl_last_renewal; @@ -76,9 +78,17 @@ nfs4_renew_state(void *data) timeout = (2 * lease) / 3 + (long)last - (long)now; /* Are we close to a lease timeout? */ if (time_after(now, last + lease/3)) { + cred = nfs4_get_renew_cred(clp); + if (cred == NULL) { + set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); + spin_unlock(&clp->cl_lock); + nfs_expire_all_delegations(clp); + goto out; + } spin_unlock(&clp->cl_lock); /* Queue an asynchronous RENEW. */ - nfs4_proc_async_renew(clp); + nfs4_proc_async_renew(clp, cred); + put_rpccred(cred); timeout = (2 * lease) / 3; spin_lock(&clp->cl_lock); } else diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 5ef4c57..afad025 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -43,6 +43,8 @@ #include #include #include +#include +#include #include #include @@ -57,8 +59,6 @@ const nfs4_stateid zero_stateid; static DEFINE_SPINLOCK(state_spinlock); static LIST_HEAD(nfs4_clientid_list); -static void nfs4_recover_state(void *); - void init_nfsv4_state(struct nfs_server *server) { @@ -91,11 +91,10 @@ nfs4_alloc_client(struct in_addr *addr) if (nfs_callback_up() < 0) return NULL; - if ((clp = kmalloc(sizeof(*clp), GFP_KERNEL)) == NULL) { + if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) { nfs_callback_down(); return NULL; } - memset(clp, 0, sizeof(*clp)); memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr)); init_rwsem(&clp->cl_sem); INIT_LIST_HEAD(&clp->cl_delegations); @@ -103,14 +102,12 @@ nfs4_alloc_client(struct in_addr *addr) INIT_LIST_HEAD(&clp->cl_unused); spin_lock_init(&clp->cl_lock); atomic_set(&clp->cl_count, 1); - INIT_WORK(&clp->cl_recoverd, nfs4_recover_state, clp); INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp); INIT_LIST_HEAD(&clp->cl_superblocks); - init_waitqueue_head(&clp->cl_waitq); rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client"); clp->cl_rpcclient = ERR_PTR(-EINVAL); clp->cl_boot_time = CURRENT_TIME; - clp->cl_state = 1 << NFS4CLNT_OK; + clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; return clp; } @@ -127,8 +124,6 @@ nfs4_free_client(struct nfs4_client *clp kfree(sp); } BUG_ON(!list_empty(&clp->cl_state_owners)); - if (clp->cl_cred) - put_rpccred(clp->cl_cred); nfs_idmap_delete(clp); if (!IS_ERR(clp->cl_rpcclient)) rpc_shutdown_client(clp->cl_rpcclient); @@ -193,27 +188,22 @@ nfs4_put_client(struct nfs4_client *clp) list_del(&clp->cl_servers); spin_unlock(&state_spinlock); BUG_ON(!list_empty(&clp->cl_superblocks)); - wake_up_all(&clp->cl_waitq); rpc_wake_up(&clp->cl_rpcwaitq); nfs4_kill_renewd(clp); nfs4_free_client(clp); } -static int __nfs4_init_client(struct nfs4_client *clp) +static int nfs4_init_client(struct nfs4_client *clp, struct rpc_cred *cred) { - int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, nfs_callback_tcpport); + int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, + nfs_callback_tcpport, cred); if (status == 0) - status = nfs4_proc_setclientid_confirm(clp); + status = nfs4_proc_setclientid_confirm(clp, cred); if (status == 0) nfs4_schedule_state_renewal(clp); return status; } -int nfs4_init_client(struct nfs4_client *clp) -{ - return nfs4_map_errors(__nfs4_init_client(clp)); -} - u32 nfs4_alloc_lockowner_id(struct nfs4_client *clp) { @@ -235,6 +225,32 @@ nfs4_client_grab_unused(struct nfs4_clie return sp; } +struct rpc_cred *nfs4_get_renew_cred(struct nfs4_client *clp) +{ + struct nfs4_state_owner *sp; + struct rpc_cred *cred = NULL; + + list_for_each_entry(sp, &clp->cl_state_owners, so_list) { + if (list_empty(&sp->so_states)) + continue; + cred = get_rpccred(sp->so_cred); + break; + } + return cred; +} + +struct rpc_cred *nfs4_get_setclientid_cred(struct nfs4_client *clp) +{ + struct nfs4_state_owner *sp; + + if (!list_empty(&clp->cl_state_owners)) { + sp = list_entry(clp->cl_state_owners.next, + struct nfs4_state_owner, so_list); + return get_rpccred(sp->so_cred); + } + return NULL; +} + static struct nfs4_state_owner * nfs4_find_state_owner(struct nfs4_client *clp, struct rpc_cred *cred) { @@ -349,14 +365,9 @@ nfs4_alloc_open_state(void) { struct nfs4_state *state; - state = kmalloc(sizeof(*state), GFP_KERNEL); + state = kzalloc(sizeof(*state), GFP_KERNEL); if (!state) return NULL; - state->state = 0; - state->nreaders = 0; - state->nwriters = 0; - state->flags = 0; - memset(state->stateid.data, 0, sizeof(state->stateid.data)); atomic_set(&state->count, 1); INIT_LIST_HEAD(&state->lock_states); spin_lock_init(&state->state_lock); @@ -475,15 +486,23 @@ void nfs4_close_state(struct nfs4_state /* Protect against nfs4_find_state() */ spin_lock(&owner->so_lock); spin_lock(&inode->i_lock); - if (mode & FMODE_READ) - state->nreaders--; - if (mode & FMODE_WRITE) - state->nwriters--; + switch (mode & (FMODE_READ | FMODE_WRITE)) { + case FMODE_READ: + state->n_rdonly--; + break; + case FMODE_WRITE: + state->n_wronly--; + break; + case FMODE_READ|FMODE_WRITE: + state->n_rdwr--; + } oldstate = newstate = state->state; - if (state->nreaders == 0) - newstate &= ~FMODE_READ; - if (state->nwriters == 0) - newstate &= ~FMODE_WRITE; + if (state->n_rdwr == 0) { + if (state->n_rdonly == 0) + newstate &= ~FMODE_READ; + if (state->n_wronly == 0) + newstate &= ~FMODE_WRITE; + } if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { nfs4_state_set_mode_locked(state, newstate); oldstate = newstate; @@ -733,45 +752,43 @@ out: } static int reclaimer(void *); -struct reclaimer_args { - struct nfs4_client *clp; - struct completion complete; -}; + +static inline void nfs4_clear_recover_bit(struct nfs4_client *clp) +{ + smp_mb__before_clear_bit(); + clear_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state); + smp_mb__after_clear_bit(); + wake_up_bit(&clp->cl_state, NFS4CLNT_STATE_RECOVER); + rpc_wake_up(&clp->cl_rpcwaitq); +} /* * State recovery routine */ -void -nfs4_recover_state(void *data) +static void nfs4_recover_state(struct nfs4_client *clp) { - struct nfs4_client *clp = (struct nfs4_client *)data; - struct reclaimer_args args = { - .clp = clp, - }; - might_sleep(); - - init_completion(&args.complete); + struct task_struct *task; - if (kernel_thread(reclaimer, &args, CLONE_KERNEL) < 0) - goto out_failed_clear; - wait_for_completion(&args.complete); - return; -out_failed_clear: - set_bit(NFS4CLNT_OK, &clp->cl_state); - wake_up_all(&clp->cl_waitq); - rpc_wake_up(&clp->cl_rpcwaitq); + __module_get(THIS_MODULE); + atomic_inc(&clp->cl_count); + task = kthread_run(reclaimer, clp, "%u.%u.%u.%u-reclaim", + NIPQUAD(clp->cl_addr)); + if (!IS_ERR(task)) + return; + nfs4_clear_recover_bit(clp); + nfs4_put_client(clp); + module_put(THIS_MODULE); } /* * Schedule a state recovery attempt */ -void -nfs4_schedule_state_recovery(struct nfs4_client *clp) +void nfs4_schedule_state_recovery(struct nfs4_client *clp) { if (!clp) return; - if (test_and_clear_bit(NFS4CLNT_OK, &clp->cl_state)) - schedule_work(&clp->cl_recoverd); + if (test_and_set_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) == 0) + nfs4_recover_state(clp); } static int nfs4_reclaim_locks(struct nfs4_state_recovery_ops *ops, struct nfs4_state *state) @@ -887,18 +904,14 @@ static void nfs4_state_mark_reclaim(stru static int reclaimer(void *ptr) { - struct reclaimer_args *args = (struct reclaimer_args *)ptr; - struct nfs4_client *clp = args->clp; + struct nfs4_client *clp = ptr; struct nfs4_state_owner *sp; struct nfs4_state_recovery_ops *ops; + struct rpc_cred *cred; int status = 0; - daemonize("%u.%u.%u.%u-reclaim", NIPQUAD(clp->cl_addr)); allow_signal(SIGKILL); - atomic_inc(&clp->cl_count); - complete(&args->complete); - /* Ensure exclusive access to NFSv4 state */ lock_kernel(); down_write(&clp->cl_sem); @@ -906,20 +919,33 @@ static int reclaimer(void *ptr) if (list_empty(&clp->cl_superblocks)) goto out; restart_loop: - status = nfs4_proc_renew(clp); - switch (status) { - case 0: - case -NFS4ERR_CB_PATH_DOWN: - goto out; - case -NFS4ERR_STALE_CLIENTID: - case -NFS4ERR_LEASE_MOVED: - ops = &nfs4_reboot_recovery_ops; - break; - default: - ops = &nfs4_network_partition_recovery_ops; - }; + ops = &nfs4_network_partition_recovery_ops; + /* Are there any open files on this volume? */ + cred = nfs4_get_renew_cred(clp); + if (cred != NULL) { + /* Yes there are: try to renew the old lease */ + status = nfs4_proc_renew(clp, cred); + switch (status) { + case 0: + case -NFS4ERR_CB_PATH_DOWN: + put_rpccred(cred); + goto out; + case -NFS4ERR_STALE_CLIENTID: + case -NFS4ERR_LEASE_MOVED: + ops = &nfs4_reboot_recovery_ops; + } + } else { + /* "reboot" to ensure we clear all state on the server */ + clp->cl_boot_time = CURRENT_TIME; + cred = nfs4_get_setclientid_cred(clp); + } + /* We're going to have to re-establish a clientid */ nfs4_state_mark_reclaim(clp); - status = __nfs4_init_client(clp); + status = -ENOENT; + if (cred != NULL) { + status = nfs4_init_client(clp, cred); + put_rpccred(cred); + } if (status) goto out_error; /* Mark all delegations for reclaim */ @@ -940,14 +966,13 @@ restart_loop: } nfs_delegation_reap_unclaimed(clp); out: - set_bit(NFS4CLNT_OK, &clp->cl_state); up_write(&clp->cl_sem); unlock_kernel(); - wake_up_all(&clp->cl_waitq); - rpc_wake_up(&clp->cl_rpcwaitq); if (status == -NFS4ERR_CB_PATH_DOWN) nfs_handle_cb_pathdown(clp); + nfs4_clear_recover_bit(clp); nfs4_put_client(clp); + module_put_and_exit(0); return 0; out_error: printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u with error %d\n", diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index fbbace8..5d6bda4 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -742,69 +742,80 @@ static int encode_link(struct xdr_stream return 0; } +static inline int nfs4_lock_type(struct file_lock *fl, int block) +{ + if ((fl->fl_type & (F_RDLCK|F_WRLCK|F_UNLCK)) == F_RDLCK) + return block ? NFS4_READW_LT : NFS4_READ_LT; + return block ? NFS4_WRITEW_LT : NFS4_WRITE_LT; +} + +static inline uint64_t nfs4_lock_length(struct file_lock *fl) +{ + if (fl->fl_end == OFFSET_MAX) + return ~(uint64_t)0; + return fl->fl_end - fl->fl_start + 1; +} + /* * opcode,type,reclaim,offset,length,new_lock_owner = 32 * open_seqid,open_stateid,lock_seqid,lock_owner.clientid, lock_owner.id = 40 */ -static int encode_lock(struct xdr_stream *xdr, const struct nfs_lockargs *arg) +static int encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args) { uint32_t *p; - struct nfs_lock_opargs *opargs = arg->u.lock; RESERVE_SPACE(32); WRITE32(OP_LOCK); - WRITE32(arg->type); - WRITE32(opargs->reclaim); - WRITE64(arg->offset); - WRITE64(arg->length); - WRITE32(opargs->new_lock_owner); - if (opargs->new_lock_owner){ + WRITE32(nfs4_lock_type(args->fl, args->block)); + WRITE32(args->reclaim); + WRITE64(args->fl->fl_start); + WRITE64(nfs4_lock_length(args->fl)); + WRITE32(args->new_lock_owner); + if (args->new_lock_owner){ RESERVE_SPACE(40); - WRITE32(opargs->open_seqid->sequence->counter); - WRITEMEM(opargs->open_stateid->data, sizeof(opargs->open_stateid->data)); - WRITE32(opargs->lock_seqid->sequence->counter); - WRITE64(opargs->lock_owner.clientid); + WRITE32(args->open_seqid->sequence->counter); + WRITEMEM(args->open_stateid->data, sizeof(args->open_stateid->data)); + WRITE32(args->lock_seqid->sequence->counter); + WRITE64(args->lock_owner.clientid); WRITE32(4); - WRITE32(opargs->lock_owner.id); + WRITE32(args->lock_owner.id); } else { RESERVE_SPACE(20); - WRITEMEM(opargs->lock_stateid->data, sizeof(opargs->lock_stateid->data)); - WRITE32(opargs->lock_seqid->sequence->counter); + WRITEMEM(args->lock_stateid->data, sizeof(args->lock_stateid->data)); + WRITE32(args->lock_seqid->sequence->counter); } return 0; } -static int encode_lockt(struct xdr_stream *xdr, const struct nfs_lockargs *arg) +static int encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *args) { uint32_t *p; - struct nfs_lowner *opargs = arg->u.lockt; RESERVE_SPACE(40); WRITE32(OP_LOCKT); - WRITE32(arg->type); - WRITE64(arg->offset); - WRITE64(arg->length); - WRITE64(opargs->clientid); + WRITE32(nfs4_lock_type(args->fl, 0)); + WRITE64(args->fl->fl_start); + WRITE64(nfs4_lock_length(args->fl)); + WRITE64(args->lock_owner.clientid); WRITE32(4); - WRITE32(opargs->id); + WRITE32(args->lock_owner.id); return 0; } -static int encode_locku(struct xdr_stream *xdr, const struct nfs_lockargs *arg) +static int encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *args) { uint32_t *p; - struct nfs_locku_opargs *opargs = arg->u.locku; RESERVE_SPACE(44); WRITE32(OP_LOCKU); - WRITE32(arg->type); - WRITE32(opargs->seqid->sequence->counter); - WRITEMEM(opargs->stateid->data, sizeof(opargs->stateid->data)); - WRITE64(arg->offset); - WRITE64(arg->length); + WRITE32(nfs4_lock_type(args->fl, 0)); + WRITE32(args->seqid->sequence->counter); + WRITEMEM(args->stateid->data, sizeof(args->stateid->data)); + WRITE64(args->fl->fl_start); + WRITE64(nfs4_lock_length(args->fl)); return 0; } @@ -964,9 +975,9 @@ static int encode_open_confirm(struct xd { uint32_t *p; - RESERVE_SPACE(8+sizeof(arg->stateid.data)); + RESERVE_SPACE(8+sizeof(arg->stateid->data)); WRITE32(OP_OPEN_CONFIRM); - WRITEMEM(arg->stateid.data, sizeof(arg->stateid.data)); + WRITEMEM(arg->stateid->data, sizeof(arg->stateid->data)); WRITE32(arg->seqid->sequence->counter); return 0; @@ -1499,9 +1510,6 @@ static int nfs4_xdr_enc_open(struct rpc_ }; int status; - status = nfs_wait_on_sequence(args->seqid, req->rq_task); - if (status != 0) - goto out; xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, &hdr); status = encode_putfh(&xdr, args->fh); @@ -1538,9 +1546,6 @@ static int nfs4_xdr_enc_open_confirm(str }; int status; - status = nfs_wait_on_sequence(args->seqid, req->rq_task); - if (status != 0) - goto out; xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, &hdr); status = encode_putfh(&xdr, args->fh); @@ -1558,19 +1563,19 @@ static int nfs4_xdr_enc_open_noattr(stru { struct xdr_stream xdr; struct compound_hdr hdr = { - .nops = 2, + .nops = 3, }; int status; - status = nfs_wait_on_sequence(args->seqid, req->rq_task); - if (status != 0) - goto out; xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, &hdr); status = encode_putfh(&xdr, args->fh); if (status) goto out; status = encode_open(&xdr, args); + if (status) + goto out; + status = encode_getfattr(&xdr, args->bitmask); out: return status; } @@ -1602,21 +1607,14 @@ out: /* * Encode a LOCK request */ -static int nfs4_xdr_enc_lock(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *args) +static int nfs4_xdr_enc_lock(struct rpc_rqst *req, uint32_t *p, struct nfs_lock_args *args) { struct xdr_stream xdr; struct compound_hdr hdr = { .nops = 2, }; - struct nfs_lock_opargs *opargs = args->u.lock; int status; - status = nfs_wait_on_sequence(opargs->lock_seqid, req->rq_task); - if (status != 0) - goto out; - /* Do we need to do an open_to_lock_owner? */ - if (opargs->lock_seqid->sequence->flags & NFS_SEQID_CONFIRMED) - opargs->new_lock_owner = 0; xdr_init_encode(&xdr, &req->rq_snd_buf, p); encode_compound_hdr(&xdr, &hdr); status = encode_putfh(&xdr, args->fh); @@ -1630,7 +1628,7 @@ out: /* * Encode a LOCKT request */ -static int nfs4_xdr_enc_lockt(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *args) +static int nfs4_xdr_enc_lockt(struct rpc_rqst *req, uint32_t *p, struct nfs_lockt_args *args) { struct xdr_stream xdr; struct compound_hdr hdr = { @@ -1651,7 +1649,7 @@ out: /* * Encode a LOCKU request */ -static int nfs4_xdr_enc_locku(struct rpc_rqst *req, uint32_t *p, struct nfs_lockargs *args) +static int nfs4_xdr_enc_locku(struct rpc_rqst *req, uint32_t *p, struct nfs_locku_args *args) { struct xdr_stream xdr; struct compound_hdr hdr = { @@ -2955,55 +2953,64 @@ static int decode_link(struct xdr_stream /* * We create the owner, so we know a proper owner.id length is 4. */ -static int decode_lock_denied (struct xdr_stream *xdr, struct nfs_lock_denied *denied) +static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl) { + uint64_t offset, length, clientid; uint32_t *p; - uint32_t namelen; + uint32_t namelen, type; READ_BUF(32); - READ64(denied->offset); - READ64(denied->length); - READ32(denied->type); - READ64(denied->owner.clientid); + READ64(offset); + READ64(length); + READ32(type); + if (fl != NULL) { + fl->fl_start = (loff_t)offset; + fl->fl_end = fl->fl_start + (loff_t)length - 1; + if (length == ~(uint64_t)0) + fl->fl_end = OFFSET_MAX; + fl->fl_type = F_WRLCK; + if (type & 1) + fl->fl_type = F_RDLCK; + fl->fl_pid = 0; + } + READ64(clientid); READ32(namelen); READ_BUF(namelen); - if (namelen == 4) - READ32(denied->owner.id); return -NFS4ERR_DENIED; } -static int decode_lock(struct xdr_stream *xdr, struct nfs_lockres *res) +static int decode_lock(struct xdr_stream *xdr, struct nfs_lock_res *res) { uint32_t *p; int status; status = decode_op_hdr(xdr, OP_LOCK); if (status == 0) { - READ_BUF(sizeof(res->u.stateid.data)); - COPYMEM(res->u.stateid.data, sizeof(res->u.stateid.data)); + READ_BUF(sizeof(res->stateid.data)); + COPYMEM(res->stateid.data, sizeof(res->stateid.data)); } else if (status == -NFS4ERR_DENIED) - return decode_lock_denied(xdr, &res->u.denied); + return decode_lock_denied(xdr, NULL); return status; } -static int decode_lockt(struct xdr_stream *xdr, struct nfs_lockres *res) +static int decode_lockt(struct xdr_stream *xdr, struct nfs_lockt_res *res) { int status; status = decode_op_hdr(xdr, OP_LOCKT); if (status == -NFS4ERR_DENIED) - return decode_lock_denied(xdr, &res->u.denied); + return decode_lock_denied(xdr, res->denied); return status; } -static int decode_locku(struct xdr_stream *xdr, struct nfs_lockres *res) +static int decode_locku(struct xdr_stream *xdr, struct nfs_locku_res *res) { uint32_t *p; int status; status = decode_op_hdr(xdr, OP_LOCKU); if (status == 0) { - READ_BUF(sizeof(res->u.stateid.data)); - COPYMEM(res->u.stateid.data, sizeof(res->u.stateid.data)); + READ_BUF(sizeof(res->stateid.data)); + COPYMEM(res->stateid.data, sizeof(res->stateid.data)); } return status; } @@ -3831,6 +3838,9 @@ static int nfs4_xdr_dec_open_noattr(stru if (status) goto out; status = decode_open(&xdr, res); + if (status) + goto out; + decode_getfattr(&xdr, res->f_attr, res->server); out: return status; } @@ -3864,7 +3874,7 @@ out: /* * Decode LOCK response */ -static int nfs4_xdr_dec_lock(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) +static int nfs4_xdr_dec_lock(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lock_res *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -3885,7 +3895,7 @@ out: /* * Decode LOCKT response */ -static int nfs4_xdr_dec_lockt(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) +static int nfs4_xdr_dec_lockt(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockt_res *res) { struct xdr_stream xdr; struct compound_hdr hdr; @@ -3906,7 +3916,7 @@ out: /* * Decode LOCKU response */ -static int nfs4_xdr_dec_locku(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_lockres *res) +static int nfs4_xdr_dec_locku(struct rpc_rqst *rqstp, uint32_t *p, struct nfs_locku_res *res) { struct xdr_stream xdr; struct compound_hdr hdr; diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index a48a003..6145e82 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -375,6 +375,7 @@ nfs_proc_link(struct inode *inode, struc dprintk("NFS call link %s\n", name->name); status = rpc_call(NFS_CLIENT(inode), NFSPROC_LINK, &arg, NULL, 0); + nfs_mark_for_revalidate(inode); nfs_mark_for_revalidate(dir); dprintk("NFS reply link: %d\n", status); return status; @@ -546,10 +547,9 @@ nfs_proc_pathconf(struct nfs_server *ser extern u32 * nfs_decode_dirent(u32 *, struct nfs_entry *, int); -static void -nfs_read_done(struct rpc_task *task) +static void nfs_read_done(struct rpc_task *task, void *calldata) { - struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata; + struct nfs_read_data *data = calldata; if (task->tk_status >= 0) { nfs_refresh_inode(data->inode, data->res.fattr); @@ -559,9 +559,14 @@ nfs_read_done(struct rpc_task *task) if (data->args.offset + data->args.count >= data->res.fattr->size) data->res.eof = 1; } - nfs_readpage_result(task); + nfs_readpage_result(task, calldata); } +static const struct rpc_call_ops nfs_read_ops = { + .rpc_call_done = nfs_read_done, + .rpc_release = nfs_readdata_release, +}; + static void nfs_proc_read_setup(struct nfs_read_data *data) { @@ -579,20 +584,24 @@ nfs_proc_read_setup(struct nfs_read_data flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0); /* Finalize the task. */ - rpc_init_task(task, NFS_CLIENT(inode), nfs_read_done, flags); + rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs_read_ops, data); rpc_call_setup(task, &msg, 0); } -static void -nfs_write_done(struct rpc_task *task) +static void nfs_write_done(struct rpc_task *task, void *calldata) { - struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; + struct nfs_write_data *data = calldata; if (task->tk_status >= 0) nfs_post_op_update_inode(data->inode, data->res.fattr); - nfs_writeback_done(task); + nfs_writeback_done(task, calldata); } +static const struct rpc_call_ops nfs_write_ops = { + .rpc_call_done = nfs_write_done, + .rpc_release = nfs_writedata_release, +}; + static void nfs_proc_write_setup(struct nfs_write_data *data, int how) { @@ -613,7 +622,7 @@ nfs_proc_write_setup(struct nfs_write_da flags = (how & FLUSH_SYNC) ? 0 : RPC_TASK_ASYNC; /* Finalize the task. */ - rpc_init_task(task, NFS_CLIENT(inode), nfs_write_done, flags); + rpc_init_task(task, NFS_CLIENT(inode), flags, &nfs_write_ops, data); rpc_call_setup(task, &msg, 0); } diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 5f20eaf..2148624 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -42,9 +42,8 @@ mempool_t *nfs_rdata_mempool; #define MIN_POOL_READ (32) -void nfs_readdata_release(struct rpc_task *task) +void nfs_readdata_release(void *data) { - struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata; nfs_readdata_free(data); } @@ -220,9 +219,6 @@ static void nfs_read_rpcsetup(struct nfs NFS_PROTO(inode)->read_setup(data); data->task.tk_cookie = (unsigned long)inode; - data->task.tk_calldata = data; - /* Release requests */ - data->task.tk_release = nfs_readdata_release; dprintk("NFS: %4d initiated read call (req %s/%Ld, %u bytes @ offset %Lu)\n", data->task.tk_pid, @@ -452,9 +448,9 @@ static void nfs_readpage_result_full(str * This is the callback from RPC telling us whether a reply was * received or some error occurred (timeout or socket shutdown). */ -void nfs_readpage_result(struct rpc_task *task) +void nfs_readpage_result(struct rpc_task *task, void *calldata) { - struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata; + struct nfs_read_data *data = calldata; struct nfs_readargs *argp = &data->args; struct nfs_readres *resp = &data->res; int status = task->tk_status; diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index d639d17..a65c7b5 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -87,10 +87,9 @@ nfs_copy_dname(struct dentry *dentry, st * We delay initializing RPC info until after the call to dentry_iput() * in order to minimize races against rename(). */ -static void -nfs_async_unlink_init(struct rpc_task *task) +static void nfs_async_unlink_init(struct rpc_task *task, void *calldata) { - struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata; + struct nfs_unlinkdata *data = calldata; struct dentry *dir = data->dir; struct rpc_message msg = { .rpc_cred = data->cred, @@ -116,10 +115,9 @@ nfs_async_unlink_init(struct rpc_task *t * * Do the directory attribute update. */ -static void -nfs_async_unlink_done(struct rpc_task *task) +static void nfs_async_unlink_done(struct rpc_task *task, void *calldata) { - struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata; + struct nfs_unlinkdata *data = calldata; struct dentry *dir = data->dir; struct inode *dir_i; @@ -141,13 +139,18 @@ nfs_async_unlink_done(struct rpc_task *t * We need to call nfs_put_unlinkdata as a 'tk_release' task since the * rpc_task would be freed too. */ -static void -nfs_async_unlink_release(struct rpc_task *task) +static void nfs_async_unlink_release(void *calldata) { - struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata; + struct nfs_unlinkdata *data = calldata; nfs_put_unlinkdata(data); } +static const struct rpc_call_ops nfs_unlink_ops = { + .rpc_call_prepare = nfs_async_unlink_init, + .rpc_call_done = nfs_async_unlink_done, + .rpc_release = nfs_async_unlink_release, +}; + /** * nfs_async_unlink - asynchronous unlinking of a file * @dentry: dentry to unlink @@ -157,7 +160,6 @@ nfs_async_unlink(struct dentry *dentry) { struct dentry *dir = dentry->d_parent; struct nfs_unlinkdata *data; - struct rpc_task *task; struct rpc_clnt *clnt = NFS_CLIENT(dir->d_inode); int status = -ENOMEM; @@ -178,17 +180,13 @@ nfs_async_unlink(struct dentry *dentry) nfs_deletes = data; data->count = 1; - task = &data->task; - rpc_init_task(task, clnt, nfs_async_unlink_done , RPC_TASK_ASYNC); - task->tk_calldata = data; - task->tk_action = nfs_async_unlink_init; - task->tk_release = nfs_async_unlink_release; + rpc_init_task(&data->task, clnt, RPC_TASK_ASYNC, &nfs_unlink_ops, data); spin_lock(&dentry->d_lock); dentry->d_flags |= DCACHE_NFSFS_RENAMED; spin_unlock(&dentry->d_lock); - rpc_sleep_on(&nfs_delete_queue, task, NULL, NULL); + rpc_sleep_on(&nfs_delete_queue, &data->task, NULL, NULL); status = 0; out: return status; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 8f71e76..80bc4ea 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -104,9 +104,8 @@ static inline void nfs_commit_free(struc mempool_free(p, nfs_commit_mempool); } -static void nfs_writedata_release(struct rpc_task *task) +void nfs_writedata_release(void *wdata) { - struct nfs_write_data *wdata = (struct nfs_write_data *)task->tk_calldata; nfs_writedata_free(wdata); } @@ -189,6 +188,7 @@ static int nfs_writepage_sync(struct nfs (long long)NFS_FILEID(inode), count, (long long)(page_offset(page) + offset)); + set_page_writeback(page); nfs_begin_data_update(inode); do { if (count < wsize) @@ -221,6 +221,7 @@ static int nfs_writepage_sync(struct nfs io_error: nfs_end_data_update(inode); + end_page_writeback(page); nfs_writedata_free(wdata); return written ? written : result; } @@ -230,19 +231,16 @@ static int nfs_writepage_async(struct nf unsigned int offset, unsigned int count) { struct nfs_page *req; - int status; req = nfs_update_request(ctx, inode, page, offset, count); - status = (IS_ERR(req)) ? PTR_ERR(req) : 0; - if (status < 0) - goto out; + if (IS_ERR(req)) + return PTR_ERR(req); /* Update file length */ nfs_grow_file(page, offset, count); /* Set the PG_uptodate flag? */ nfs_mark_uptodate(page, offset, count); nfs_unlock_request(req); - out: - return status; + return 0; } static int wb_priority(struct writeback_control *wbc) @@ -302,11 +300,8 @@ do_it: lock_kernel(); if (!IS_SYNC(inode) && inode_referenced) { err = nfs_writepage_async(ctx, inode, page, 0, offset); - if (err >= 0) { - err = 0; - if (wbc->for_reclaim) - nfs_flush_inode(inode, 0, 0, FLUSH_STABLE); - } + if (!wbc->for_writepages) + nfs_flush_inode(inode, 0, 0, wb_priority(wbc)); } else { err = nfs_writepage_sync(ctx, inode, page, 0, offset, priority); @@ -875,9 +870,6 @@ static void nfs_write_rpcsetup(struct nf data->task.tk_priority = flush_task_priority(how); data->task.tk_cookie = (unsigned long)inode; - data->task.tk_calldata = data; - /* Release requests */ - data->task.tk_release = nfs_writedata_release; dprintk("NFS: %4d initiated write call (req %s/%Ld, %u bytes @ offset %Lu)\n", data->task.tk_pid, @@ -929,7 +921,7 @@ static int nfs_flush_multi(struct list_h atomic_set(&req->wb_complete, requests); ClearPageError(page); - SetPageWriteback(page); + set_page_writeback(page); offset = 0; nbytes = req->wb_bytes; do { @@ -992,7 +984,7 @@ static int nfs_flush_one(struct list_hea nfs_list_remove_request(req); nfs_list_add_request(req, &data->pages); ClearPageError(req->wb_page); - SetPageWriteback(req->wb_page); + set_page_writeback(req->wb_page); *pages++ = req->wb_page; count += req->wb_bytes; } @@ -1135,9 +1127,9 @@ static void nfs_writeback_done_full(stru /* * This function is called when the WRITE call is complete. */ -void nfs_writeback_done(struct rpc_task *task) +void nfs_writeback_done(struct rpc_task *task, void *calldata) { - struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata; + struct nfs_write_data *data = calldata; struct nfs_writeargs *argp = &data->args; struct nfs_writeres *resp = &data->res; @@ -1204,9 +1196,8 @@ void nfs_writeback_done(struct rpc_task #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) -static void nfs_commit_release(struct rpc_task *task) +void nfs_commit_release(void *wdata) { - struct nfs_write_data *wdata = (struct nfs_write_data *)task->tk_calldata; nfs_commit_free(wdata); } @@ -1242,9 +1233,6 @@ static void nfs_commit_rpcsetup(struct l data->task.tk_priority = flush_task_priority(how); data->task.tk_cookie = (unsigned long)inode; - data->task.tk_calldata = data; - /* Release requests */ - data->task.tk_release = nfs_commit_release; dprintk("NFS: %4d initiated commit call\n", data->task.tk_pid); } @@ -1281,10 +1269,9 @@ nfs_commit_list(struct list_head *head, /* * COMMIT call returned */ -void -nfs_commit_done(struct rpc_task *task) +void nfs_commit_done(struct rpc_task *task, void *calldata) { - struct nfs_write_data *data = (struct nfs_write_data *)task->tk_calldata; + struct nfs_write_data *data = calldata; struct nfs_page *req; int res = 0; diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 583c071..cf92008 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -53,7 +53,7 @@ #define NFSPROC4_CB_COMPOUND 1 /* declarations */ -static void nfs4_cb_null(struct rpc_task *task); +static const struct rpc_call_ops nfs4_cb_null_ops; /* Index of predefined Linux callback client operations */ @@ -447,7 +447,7 @@ nfsd4_probe_callback(struct nfs4_client msg.rpc_cred = nfsd4_lookupcred(clp,0); if (IS_ERR(msg.rpc_cred)) goto out_rpciod; - status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, nfs4_cb_null, NULL); + status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, &nfs4_cb_null_ops, NULL); put_rpccred(msg.rpc_cred); if (status != 0) { @@ -469,7 +469,7 @@ out_err: } static void -nfs4_cb_null(struct rpc_task *task) +nfs4_cb_null(struct rpc_task *task, void *dummy) { struct nfs4_client *clp = (struct nfs4_client *)task->tk_msg.rpc_argp; struct nfs4_callback *cb = &clp->cl_callback; @@ -488,6 +488,10 @@ out: put_nfs4_client(clp); } +static const struct rpc_call_ops nfs4_cb_null_ops = { + .rpc_call_done = nfs4_cb_null, +}; + /* * called with dp->dl_count inc'ed. * nfs4_lock_state() may or may not have been called. diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 16d4e5a..95c8fea 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -172,7 +172,7 @@ extern struct nlm_host *nlm_find_client( /* * Server-side lock handling */ -int nlmsvc_async_call(struct nlm_rqst *, u32, rpc_action); +int nlmsvc_async_call(struct nlm_rqst *, u32, const struct rpc_call_ops *); u32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *, struct nlm_lock *, int, struct nlm_cookie *); u32 nlmsvc_unlock(struct nlm_file *, struct nlm_lock *); diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 12787a9..25f8e77 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -405,10 +405,12 @@ extern int nfs_writepage(struct page *p extern int nfs_writepages(struct address_space *, struct writeback_control *); extern int nfs_flush_incompatible(struct file *file, struct page *page); extern int nfs_updatepage(struct file *, struct page *, unsigned int, unsigned int); -extern void nfs_writeback_done(struct rpc_task *task); +extern void nfs_writeback_done(struct rpc_task *task, void *data); +extern void nfs_writedata_release(void *data); #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) -extern void nfs_commit_done(struct rpc_task *); +extern void nfs_commit_done(struct rpc_task *, void *data); +extern void nfs_commit_release(void *data); #endif /* @@ -480,7 +482,9 @@ static inline void nfs_writedata_free(st extern int nfs_readpage(struct file *, struct page *); extern int nfs_readpages(struct file *, struct address_space *, struct list_head *, unsigned); -extern void nfs_readpage_result(struct rpc_task *); +extern void nfs_readpage_result(struct rpc_task *, void *); +extern void nfs_readdata_release(void *data); + /* * Allocate and free nfs_read_data structures @@ -500,8 +504,6 @@ static inline void nfs_readdata_free(str mempool_free(p, nfs_rdata_mempool); } -extern void nfs_readdata_release(struct rpc_task *task); - /* * linux/fs/nfs3proc.c */ diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 4071866..b8b0eed 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -137,7 +137,7 @@ struct nfs_openres { */ struct nfs_open_confirmargs { const struct nfs_fh * fh; - nfs4_stateid stateid; + nfs4_stateid * stateid; struct nfs_seqid * seqid; }; @@ -165,50 +165,46 @@ struct nfs_closeres { * * Arguments to the lock,lockt, and locku call. * */ struct nfs_lowner { - __u64 clientid; - u32 id; + __u64 clientid; + u32 id; }; -struct nfs_lock_opargs { +struct nfs_lock_args { + struct nfs_fh * fh; + struct file_lock * fl; struct nfs_seqid * lock_seqid; nfs4_stateid * lock_stateid; struct nfs_seqid * open_seqid; nfs4_stateid * open_stateid; - struct nfs_lowner lock_owner; - __u32 reclaim; - __u32 new_lock_owner; + struct nfs_lowner lock_owner; + unsigned char block : 1; + unsigned char reclaim : 1; + unsigned char new_lock_owner : 1; }; -struct nfs_locku_opargs { +struct nfs_lock_res { + nfs4_stateid stateid; +}; + +struct nfs_locku_args { + struct nfs_fh * fh; + struct file_lock * fl; struct nfs_seqid * seqid; nfs4_stateid * stateid; }; -struct nfs_lockargs { - struct nfs_fh * fh; - __u32 type; - __u64 offset; - __u64 length; - union { - struct nfs_lock_opargs *lock; /* LOCK */ - struct nfs_lowner *lockt; /* LOCKT */ - struct nfs_locku_opargs *locku; /* LOCKU */ - } u; +struct nfs_locku_res { + nfs4_stateid stateid; }; -struct nfs_lock_denied { - __u64 offset; - __u64 length; - __u32 type; - struct nfs_lowner owner; +struct nfs_lockt_args { + struct nfs_fh * fh; + struct file_lock * fl; + struct nfs_lowner lock_owner; }; -struct nfs_lockres { - union { - nfs4_stateid stateid;/* LOCK success, LOCKU */ - struct nfs_lock_denied denied; /* LOCK failed, LOCKT success */ - } u; - const struct nfs_server * server; +struct nfs_lockt_res { + struct file_lock * denied; /* LOCK, LOCKT failed */ }; struct nfs4_delegreturnargs { diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index ab151bb..b0ab959 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -126,7 +126,8 @@ int rpc_register(u32, u32, int, unsigne void rpc_call_setup(struct rpc_task *, struct rpc_message *, int); int rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, - int flags, rpc_action callback, void *clntdata); + int flags, const struct rpc_call_ops *tk_ops, + void *calldata); int rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags); void rpc_restart_call(struct rpc_task *); diff --git a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h index 4d77e90..94b0afa 100644 --- a/include/linux/sunrpc/sched.h +++ b/include/linux/sunrpc/sched.h @@ -27,6 +27,7 @@ struct rpc_message { struct rpc_cred * rpc_cred; /* Credentials */ }; +struct rpc_call_ops; struct rpc_wait_queue; struct rpc_wait { struct list_head list; /* wait queue links */ @@ -41,6 +42,7 @@ struct rpc_task { #ifdef RPC_DEBUG unsigned long tk_magic; /* 0xf00baa */ #endif + atomic_t tk_count; /* Reference count */ struct list_head tk_task; /* global list of tasks */ struct rpc_clnt * tk_client; /* RPC client */ struct rpc_rqst * tk_rqstp; /* RPC request */ @@ -61,13 +63,12 @@ struct rpc_task { * timeout_fn to be executed by timer bottom half * callback to be executed after waking up * action next procedure for async tasks - * exit exit async task and report to caller + * tk_ops caller callbacks */ void (*tk_timeout_fn)(struct rpc_task *); void (*tk_callback)(struct rpc_task *); void (*tk_action)(struct rpc_task *); - void (*tk_exit)(struct rpc_task *); - void (*tk_release)(struct rpc_task *); + const struct rpc_call_ops *tk_ops; void * tk_calldata; /* @@ -78,7 +79,6 @@ struct rpc_task { struct timer_list tk_timer; /* kernel timer */ unsigned long tk_timeout; /* timeout for rpc_sleep() */ unsigned short tk_flags; /* misc flags */ - unsigned char tk_active : 1;/* Task has been activated */ unsigned char tk_priority : 2;/* Task priority */ unsigned long tk_runstate; /* Task run status */ struct workqueue_struct *tk_workqueue; /* Normally rpciod, but could @@ -111,6 +111,13 @@ struct rpc_task { typedef void (*rpc_action)(struct rpc_task *); +struct rpc_call_ops { + void (*rpc_call_prepare)(struct rpc_task *, void *); + void (*rpc_call_done)(struct rpc_task *, void *); + void (*rpc_release)(void *); +}; + + /* * RPC task flags */ @@ -129,7 +136,6 @@ typedef void (*rpc_action)(struct rpc_ #define RPC_IS_SWAPPER(t) ((t)->tk_flags & RPC_TASK_SWAPPER) #define RPC_DO_ROOTOVERRIDE(t) ((t)->tk_flags & RPC_TASK_ROOTCREDS) #define RPC_ASSASSINATED(t) ((t)->tk_flags & RPC_TASK_KILLED) -#define RPC_IS_ACTIVATED(t) ((t)->tk_active) #define RPC_DO_CALLBACK(t) ((t)->tk_callback != NULL) #define RPC_IS_SOFT(t) ((t)->tk_flags & RPC_TASK_SOFT) #define RPC_TASK_UNINTERRUPTIBLE(t) ((t)->tk_flags & RPC_TASK_NOINTR) @@ -138,6 +144,7 @@ typedef void (*rpc_action)(struct rpc_ #define RPC_TASK_QUEUED 1 #define RPC_TASK_WAKEUP 2 #define RPC_TASK_HAS_TIMER 3 +#define RPC_TASK_ACTIVE 4 #define RPC_IS_RUNNING(t) (test_bit(RPC_TASK_RUNNING, &(t)->tk_runstate)) #define rpc_set_running(t) (set_bit(RPC_TASK_RUNNING, &(t)->tk_runstate)) @@ -168,6 +175,15 @@ typedef void (*rpc_action)(struct rpc_ smp_mb__after_clear_bit(); \ } while (0) +#define RPC_IS_ACTIVATED(t) (test_bit(RPC_TASK_ACTIVE, &(t)->tk_runstate)) +#define rpc_set_active(t) (set_bit(RPC_TASK_ACTIVE, &(t)->tk_runstate)) +#define rpc_clear_active(t) \ + do { \ + smp_mb__before_clear_bit(); \ + clear_bit(RPC_TASK_ACTIVE, &(t)->tk_runstate); \ + smp_mb__after_clear_bit(); \ + } while(0) + /* * Task priorities. * Note: if you change these, you must also change @@ -228,11 +244,16 @@ struct rpc_wait_queue { /* * Function prototypes */ -struct rpc_task *rpc_new_task(struct rpc_clnt *, rpc_action, int flags); +struct rpc_task *rpc_new_task(struct rpc_clnt *, int flags, + const struct rpc_call_ops *ops, void *data); +struct rpc_task *rpc_run_task(struct rpc_clnt *clnt, int flags, + const struct rpc_call_ops *ops, void *data); struct rpc_task *rpc_new_child(struct rpc_clnt *, struct rpc_task *parent); -void rpc_init_task(struct rpc_task *, struct rpc_clnt *, - rpc_action exitfunc, int flags); +void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, + int flags, const struct rpc_call_ops *ops, + void *data); void rpc_release_task(struct rpc_task *); +void rpc_exit_task(struct rpc_task *); void rpc_killall_tasks(struct rpc_clnt *); int rpc_execute(struct rpc_task *); void rpc_run_child(struct rpc_task *parent, struct rpc_task *child, @@ -250,6 +271,7 @@ void * rpc_malloc(struct rpc_task *, si int rpciod_up(void); void rpciod_down(void); void rpciod_wake_up(void); +int __rpc_wait_for_completion_task(struct rpc_task *task, int (*)(void *)); #ifdef RPC_DEBUG void rpc_show_tasks(void); #endif @@ -259,7 +281,12 @@ void rpc_destroy_mempool(void); static inline void rpc_exit(struct rpc_task *task, int status) { task->tk_status = status; - task->tk_action = NULL; + task->tk_action = rpc_exit_task; +} + +static inline int rpc_wait_for_completion_task(struct rpc_task *task) +{ + return __rpc_wait_for_completion_task(task, NULL); } #ifdef RPC_DEBUG diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 343d883..2e89fea 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -53,10 +53,11 @@ struct writeback_control { loff_t start; loff_t end; - unsigned nonblocking:1; /* Don't get stuck on request queues */ - unsigned encountered_congestion:1; /* An output: a queue is full */ - unsigned for_kupdate:1; /* A kupdate writeback */ - unsigned for_reclaim:1; /* Invoked from the page allocator */ + unsigned nonblocking:1; /* Don't get stuck on request queues */ + unsigned encountered_congestion:1; /* An output: a queue is full */ + unsigned for_kupdate:1; /* A kupdate writeback */ + unsigned for_reclaim:1; /* Invoked from the page allocator */ + unsigned for_writepages:1; /* This is a writepages() call */ }; /* diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 0166ea1..5240e42 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -550,11 +550,17 @@ void __init page_writeback_init(void) int do_writepages(struct address_space *mapping, struct writeback_control *wbc) { + int ret; + if (wbc->nr_to_write <= 0) return 0; + wbc->for_writepages = 1; if (mapping->a_ops->writepages) - return mapping->a_ops->writepages(mapping, wbc); - return generic_writepages(mapping, wbc); + ret = mapping->a_ops->writepages(mapping, wbc); + else + ret = generic_writepages(mapping, wbc); + wbc->for_writepages = 0; + return ret; } /** diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 61c3abe..b23c0d3 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -374,19 +374,23 @@ out: * Default callback for async RPC calls */ static void -rpc_default_callback(struct rpc_task *task) +rpc_default_callback(struct rpc_task *task, void *data) { } +static const struct rpc_call_ops rpc_default_ops = { + .rpc_call_done = rpc_default_callback, +}; + /* * Export the signal mask handling for synchronous code that * sleeps on RPC calls */ -#define RPC_INTR_SIGNALS (sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGKILL)) +#define RPC_INTR_SIGNALS (sigmask(SIGHUP) | sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGTERM)) static void rpc_save_sigmask(sigset_t *oldset, int intr) { - unsigned long sigallow = 0; + unsigned long sigallow = sigmask(SIGKILL); sigset_t sigmask; /* Block all signals except those listed in sigallow */ @@ -432,7 +436,7 @@ int rpc_call_sync(struct rpc_clnt *clnt, BUG_ON(flags & RPC_TASK_ASYNC); status = -ENOMEM; - task = rpc_new_task(clnt, NULL, flags); + task = rpc_new_task(clnt, flags, &rpc_default_ops, NULL); if (task == NULL) goto out; @@ -442,14 +446,15 @@ int rpc_call_sync(struct rpc_clnt *clnt, rpc_call_setup(task, msg, 0); /* Set up the call info struct and execute the task */ - if (task->tk_status == 0) { + status = task->tk_status; + if (status == 0) { + atomic_inc(&task->tk_count); status = rpc_execute(task); - } else { - status = task->tk_status; - rpc_release_task(task); + if (status == 0) + status = task->tk_status; } - rpc_restore_sigmask(&oldset); + rpc_release_task(task); out: return status; } @@ -459,7 +464,7 @@ out: */ int rpc_call_async(struct rpc_clnt *clnt, struct rpc_message *msg, int flags, - rpc_action callback, void *data) + const struct rpc_call_ops *tk_ops, void *data) { struct rpc_task *task; sigset_t oldset; @@ -472,12 +477,9 @@ rpc_call_async(struct rpc_clnt *clnt, st flags |= RPC_TASK_ASYNC; /* Create/initialize a new RPC task */ - if (!callback) - callback = rpc_default_callback; status = -ENOMEM; - if (!(task = rpc_new_task(clnt, callback, flags))) + if (!(task = rpc_new_task(clnt, flags, tk_ops, data))) goto out; - task->tk_calldata = data; /* Mask signals on GSS_AUTH upcalls */ rpc_task_sigmask(task, &oldset); @@ -511,7 +513,7 @@ rpc_call_setup(struct rpc_task *task, st if (task->tk_status == 0) task->tk_action = call_start; else - task->tk_action = NULL; + task->tk_action = rpc_exit_task; } void @@ -892,7 +894,7 @@ call_transmit(struct rpc_task *task) if (task->tk_status < 0) return; if (!task->tk_msg.rpc_proc->p_decode) { - task->tk_action = NULL; + task->tk_action = rpc_exit_task; rpc_wake_up_task(task); } return; @@ -1039,13 +1041,14 @@ call_decode(struct rpc_task *task) sizeof(req->rq_rcv_buf)) != 0); /* Verify the RPC header */ - if (!(p = call_verify(task))) { - if (task->tk_action == NULL) - return; - goto out_retry; + p = call_verify(task); + if (IS_ERR(p)) { + if (p == ERR_PTR(-EAGAIN)) + goto out_retry; + return; } - task->tk_action = NULL; + task->tk_action = rpc_exit_task; if (decode) task->tk_status = rpcauth_unwrap_resp(task, decode, req, p, @@ -1138,7 +1141,7 @@ call_verify(struct rpc_task *task) if ((n = ntohl(*p++)) != RPC_REPLY) { printk(KERN_WARNING "call_verify: not an RPC reply: %x\n", n); - goto out_retry; + goto out_garbage; } if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) { if (--len < 0) @@ -1168,7 +1171,7 @@ call_verify(struct rpc_task *task) task->tk_pid); rpcauth_invalcred(task); task->tk_action = call_refresh; - return NULL; + goto out_retry; case RPC_AUTH_BADCRED: case RPC_AUTH_BADVERF: /* possibly garbled cred/verf? */ @@ -1178,7 +1181,7 @@ call_verify(struct rpc_task *task) dprintk("RPC: %4d call_verify: retry garbled creds\n", task->tk_pid); task->tk_action = call_bind; - return NULL; + goto out_retry; case RPC_AUTH_TOOWEAK: printk(KERN_NOTICE "call_verify: server requires stronger " "authentication.\n"); @@ -1193,7 +1196,7 @@ call_verify(struct rpc_task *task) } if (!(p = rpcauth_checkverf(task, p))) { printk(KERN_WARNING "call_verify: auth check failed\n"); - goto out_retry; /* bad verifier, retry */ + goto out_garbage; /* bad verifier, retry */ } len = p - (u32 *)iov->iov_base - 1; if (len < 0) @@ -1230,23 +1233,24 @@ call_verify(struct rpc_task *task) /* Also retry */ } -out_retry: +out_garbage: task->tk_client->cl_stats->rpcgarbage++; if (task->tk_garb_retry) { task->tk_garb_retry--; dprintk("RPC %s: retrying %4d\n", __FUNCTION__, task->tk_pid); task->tk_action = call_bind; - return NULL; +out_retry: + return ERR_PTR(-EAGAIN); } printk(KERN_WARNING "RPC %s: retry failed, exit EIO\n", __FUNCTION__); out_eio: error = -EIO; out_err: rpc_exit(task, error); - return NULL; + return ERR_PTR(error); out_overflow: printk(KERN_WARNING "RPC %s: server reply was truncated.\n", __FUNCTION__); - goto out_retry; + goto out_garbage; } static int rpcproc_encode_null(void *rqstp, u32 *data, void *obj) diff --git a/net/sunrpc/pmap_clnt.c b/net/sunrpc/pmap_clnt.c index a398575..cad4568 100644 --- a/net/sunrpc/pmap_clnt.c +++ b/net/sunrpc/pmap_clnt.c @@ -90,8 +90,7 @@ bailout: map->pm_binding = 0; rpc_wake_up(&map->pm_bindwait); spin_unlock(&pmap_lock); - task->tk_status = -EIO; - task->tk_action = NULL; + rpc_exit(task, -EIO); } #ifdef CONFIG_ROOT_NFS @@ -138,11 +137,10 @@ pmap_getport_done(struct rpc_task *task) task->tk_pid, task->tk_status, clnt->cl_port); if (task->tk_status < 0) { /* Make the calling task exit with an error */ - task->tk_action = NULL; + task->tk_action = rpc_exit_task; } else if (clnt->cl_port == 0) { /* Program not registered */ - task->tk_status = -EACCES; - task->tk_action = NULL; + rpc_exit(task, -EACCES); } else { /* byte-swap port number first */ clnt->cl_port = htons(clnt->cl_port); diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index e3b242d..c76ea22 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -59,7 +59,6 @@ __rpc_purge_upcall(struct inode *inode, struct rpc_inode *rpci = RPC_I(inode); __rpc_purge_list(rpci, &rpci->pipe, err); - __rpc_purge_list(rpci, &rpci->in_upcall, err); rpci->pipelen = 0; wake_up(&rpci->waitq); } @@ -119,6 +118,7 @@ rpc_close_pipes(struct inode *inode) down(&inode->i_sem); if (rpci->ops != NULL) { rpci->nreaders = 0; + __rpc_purge_list(rpci, &rpci->in_upcall, -EPIPE); __rpc_purge_upcall(inode, -EPIPE); rpci->nwriters = 0; if (rpci->ops->release_pipe) diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 54e60a6..48510e3 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -264,6 +264,35 @@ void rpc_init_wait_queue(struct rpc_wait } EXPORT_SYMBOL(rpc_init_wait_queue); +static int rpc_wait_bit_interruptible(void *word) +{ + if (signal_pending(current)) + return -ERESTARTSYS; + schedule(); + return 0; +} + +/* + * Mark an RPC call as having completed by clearing the 'active' bit + */ +static inline void rpc_mark_complete_task(struct rpc_task *task) +{ + rpc_clear_active(task); + wake_up_bit(&task->tk_runstate, RPC_TASK_ACTIVE); +} + +/* + * Allow callers to wait for completion of an RPC call + */ +int __rpc_wait_for_completion_task(struct rpc_task *task, int (*action)(void *)) +{ + if (action == NULL) + action = rpc_wait_bit_interruptible; + return wait_on_bit(&task->tk_runstate, RPC_TASK_ACTIVE, + action, TASK_INTERRUPTIBLE); +} +EXPORT_SYMBOL(__rpc_wait_for_completion_task); + /* * Make an RPC task runnable. * @@ -299,10 +328,7 @@ static void rpc_make_runnable(struct rpc static inline void rpc_schedule_run(struct rpc_task *task) { - /* Don't run a child twice! */ - if (RPC_IS_ACTIVATED(task)) - return; - task->tk_active = 1; + rpc_set_active(task); rpc_make_runnable(task); } @@ -324,8 +350,7 @@ static void __rpc_sleep_on(struct rpc_wa } /* Mark the task as being activated if so needed */ - if (!RPC_IS_ACTIVATED(task)) - task->tk_active = 1; + rpc_set_active(task); __rpc_add_wait_queue(q, task); @@ -555,36 +580,30 @@ __rpc_atrun(struct rpc_task *task) } /* - * Helper that calls task->tk_exit if it exists and then returns - * true if we should exit __rpc_execute. + * Helper to call task->tk_ops->rpc_call_prepare */ -static inline int __rpc_do_exit(struct rpc_task *task) +static void rpc_prepare_task(struct rpc_task *task) { - if (task->tk_exit != NULL) { - lock_kernel(); - task->tk_exit(task); - unlock_kernel(); - /* If tk_action is non-null, we should restart the call */ - if (task->tk_action != NULL) { - if (!RPC_ASSASSINATED(task)) { - /* Release RPC slot and buffer memory */ - xprt_release(task); - rpc_free(task); - return 0; - } - printk(KERN_ERR "RPC: dead task tried to walk away.\n"); - } - } - return 1; + task->tk_ops->rpc_call_prepare(task, task->tk_calldata); } -static int rpc_wait_bit_interruptible(void *word) +/* + * Helper that calls task->tk_ops->rpc_call_done if it exists + */ +void rpc_exit_task(struct rpc_task *task) { - if (signal_pending(current)) - return -ERESTARTSYS; - schedule(); - return 0; + task->tk_action = NULL; + if (task->tk_ops->rpc_call_done != NULL) { + task->tk_ops->rpc_call_done(task, task->tk_calldata); + if (task->tk_action != NULL) { + WARN_ON(RPC_ASSASSINATED(task)); + /* Always release the RPC slot and buffer memory */ + xprt_release(task); + rpc_free(task); + } + } } +EXPORT_SYMBOL(rpc_exit_task); /* * This is the RPC `scheduler' (or rather, the finite state machine). @@ -631,12 +650,11 @@ static int __rpc_execute(struct rpc_task * by someone else. */ if (!RPC_IS_QUEUED(task)) { - if (task->tk_action != NULL) { - lock_kernel(); - task->tk_action(task); - unlock_kernel(); - } else if (__rpc_do_exit(task)) + if (task->tk_action == NULL) break; + lock_kernel(); + task->tk_action(task); + unlock_kernel(); } /* @@ -676,9 +694,9 @@ static int __rpc_execute(struct rpc_task dprintk("RPC: %4d sync task resuming\n", task->tk_pid); } - dprintk("RPC: %4d exit() = %d\n", task->tk_pid, task->tk_status); - status = task->tk_status; - + dprintk("RPC: %4d, return %d, status %d\n", task->tk_pid, status, task->tk_status); + /* Wake up anyone who is waiting for task completion */ + rpc_mark_complete_task(task); /* Release all resources associated with the task */ rpc_release_task(task); return status; @@ -696,9 +714,7 @@ static int __rpc_execute(struct rpc_task int rpc_execute(struct rpc_task *task) { - BUG_ON(task->tk_active); - - task->tk_active = 1; + rpc_set_active(task); rpc_set_running(task); return __rpc_execute(task); } @@ -754,15 +770,19 @@ rpc_free(struct rpc_task *task) /* * Creation and deletion of RPC task structures */ -void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, rpc_action callback, int flags) +void rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt, int flags, const struct rpc_call_ops *tk_ops, void *calldata) { memset(task, 0, sizeof(*task)); init_timer(&task->tk_timer); task->tk_timer.data = (unsigned long) task; task->tk_timer.function = (void (*)(unsigned long)) rpc_run_timer; + atomic_set(&task->tk_count, 1); task->tk_client = clnt; task->tk_flags = flags; - task->tk_exit = callback; + task->tk_ops = tk_ops; + if (tk_ops->rpc_call_prepare != NULL) + task->tk_action = rpc_prepare_task; + task->tk_calldata = calldata; /* Initialize retry counters */ task->tk_garb_retry = 2; @@ -791,6 +811,8 @@ void rpc_init_task(struct rpc_task *task list_add_tail(&task->tk_task, &all_tasks); spin_unlock(&rpc_sched_lock); + BUG_ON(task->tk_ops == NULL); + dprintk("RPC: %4d new task procpid %d\n", task->tk_pid, current->pid); } @@ -801,8 +823,7 @@ rpc_alloc_task(void) return (struct rpc_task *)mempool_alloc(rpc_task_mempool, GFP_NOFS); } -static void -rpc_default_free_task(struct rpc_task *task) +static void rpc_free_task(struct rpc_task *task) { dprintk("RPC: %4d freeing task\n", task->tk_pid); mempool_free(task, rpc_task_mempool); @@ -813,8 +834,7 @@ rpc_default_free_task(struct rpc_task *t * clean up after an allocation failure, as the client may * have specified "oneshot". */ -struct rpc_task * -rpc_new_task(struct rpc_clnt *clnt, rpc_action callback, int flags) +struct rpc_task *rpc_new_task(struct rpc_clnt *clnt, int flags, const struct rpc_call_ops *tk_ops, void *calldata) { struct rpc_task *task; @@ -822,10 +842,7 @@ rpc_new_task(struct rpc_clnt *clnt, rpc_ if (!task) goto cleanup; - rpc_init_task(task, clnt, callback, flags); - - /* Replace tk_release */ - task->tk_release = rpc_default_free_task; + rpc_init_task(task, clnt, flags, tk_ops, calldata); dprintk("RPC: %4d allocated task\n", task->tk_pid); task->tk_flags |= RPC_TASK_DYNAMIC; @@ -845,11 +862,15 @@ cleanup: void rpc_release_task(struct rpc_task *task) { - dprintk("RPC: %4d release task\n", task->tk_pid); + const struct rpc_call_ops *tk_ops = task->tk_ops; + void *calldata = task->tk_calldata; #ifdef RPC_DEBUG BUG_ON(task->tk_magic != RPC_TASK_MAGIC_ID); #endif + if (!atomic_dec_and_test(&task->tk_count)) + return; + dprintk("RPC: %4d release task\n", task->tk_pid); /* Remove from global task list */ spin_lock(&rpc_sched_lock); @@ -857,7 +878,6 @@ void rpc_release_task(struct rpc_task *t spin_unlock(&rpc_sched_lock); BUG_ON (RPC_IS_QUEUED(task)); - task->tk_active = 0; /* Synchronously delete any running timer */ rpc_delete_timer(task); @@ -876,11 +896,34 @@ void rpc_release_task(struct rpc_task *t #ifdef RPC_DEBUG task->tk_magic = 0; #endif - if (task->tk_release) - task->tk_release(task); + if (task->tk_flags & RPC_TASK_DYNAMIC) + rpc_free_task(task); + if (tk_ops->rpc_release) + tk_ops->rpc_release(calldata); } /** + * rpc_run_task - Allocate a new RPC task, then run rpc_execute against it + * @clnt - pointer to RPC client + * @flags - RPC flags + * @ops - RPC call ops + * @data - user call data + */ +struct rpc_task *rpc_run_task(struct rpc_clnt *clnt, int flags, + const struct rpc_call_ops *ops, + void *data) +{ + struct rpc_task *task; + task = rpc_new_task(clnt, flags, ops, data); + if (task == NULL) + return ERR_PTR(-ENOMEM); + atomic_inc(&task->tk_count); + rpc_execute(task); + return task; +} +EXPORT_SYMBOL(rpc_run_task); + +/** * rpc_find_parent - find the parent of a child task. * @child: child task * @@ -890,12 +933,11 @@ void rpc_release_task(struct rpc_task *t * * Caller must hold childq.lock */ -static inline struct rpc_task *rpc_find_parent(struct rpc_task *child) +static inline struct rpc_task *rpc_find_parent(struct rpc_task *child, struct rpc_task *parent) { - struct rpc_task *task, *parent; + struct rpc_task *task; struct list_head *le; - parent = (struct rpc_task *) child->tk_calldata; task_for_each(task, le, &childq.tasks[0]) if (task == parent) return parent; @@ -903,18 +945,22 @@ static inline struct rpc_task *rpc_find_ return NULL; } -static void rpc_child_exit(struct rpc_task *child) +static void rpc_child_exit(struct rpc_task *child, void *calldata) { struct rpc_task *parent; spin_lock_bh(&childq.lock); - if ((parent = rpc_find_parent(child)) != NULL) { + if ((parent = rpc_find_parent(child, calldata)) != NULL) { parent->tk_status = child->tk_status; __rpc_wake_up_task(parent); } spin_unlock_bh(&childq.lock); } +static const struct rpc_call_ops rpc_child_ops = { + .rpc_call_done = rpc_child_exit, +}; + /* * Note: rpc_new_task releases the client after a failure. */ @@ -923,11 +969,9 @@ rpc_new_child(struct rpc_clnt *clnt, str { struct rpc_task *task; - task = rpc_new_task(clnt, NULL, RPC_TASK_ASYNC | RPC_TASK_CHILD); + task = rpc_new_task(clnt, RPC_TASK_ASYNC | RPC_TASK_CHILD, &rpc_child_ops, parent); if (!task) goto fail; - task->tk_exit = rpc_child_exit; - task->tk_calldata = parent; return task; fail: @@ -1063,7 +1107,7 @@ void rpc_show_tasks(void) return; } printk("-pid- proc flgs status -client- -prog- --rqstp- -timeout " - "-rpcwait -action- --exit--\n"); + "-rpcwait -action- ---ops--\n"); alltask_for_each(t, le, &all_tasks) { const char *rpc_waitq = "none"; @@ -1078,7 +1122,7 @@ void rpc_show_tasks(void) (t->tk_client ? t->tk_client->cl_prog : 0), t->tk_rqstp, t->tk_timeout, rpc_waitq, - t->tk_action, t->tk_exit); + t->tk_action, t->tk_ops); } spin_unlock(&rpc_sched_lock); } diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index a03d4b6..9f73732 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -30,8 +30,6 @@ EXPORT_SYMBOL(rpc_init_task); EXPORT_SYMBOL(rpc_sleep_on); EXPORT_SYMBOL(rpc_wake_up_next); EXPORT_SYMBOL(rpc_wake_up_task); -EXPORT_SYMBOL(rpc_new_child); -EXPORT_SYMBOL(rpc_run_child); EXPORT_SYMBOL(rpciod_down); EXPORT_SYMBOL(rpciod_up); EXPORT_SYMBOL(rpc_new_task); @@ -45,7 +43,6 @@ EXPORT_SYMBOL(rpc_clone_client); EXPORT_SYMBOL(rpc_bind_new_program); EXPORT_SYMBOL(rpc_destroy_client); EXPORT_SYMBOL(rpc_shutdown_client); -EXPORT_SYMBOL(rpc_release_client); EXPORT_SYMBOL(rpc_killall_tasks); EXPORT_SYMBOL(rpc_call_sync); EXPORT_SYMBOL(rpc_call_async); @@ -120,7 +117,6 @@ EXPORT_SYMBOL(unix_domain_find); /* Generic XDR */ EXPORT_SYMBOL(xdr_encode_string); -EXPORT_SYMBOL(xdr_decode_string); EXPORT_SYMBOL(xdr_decode_string_inplace); EXPORT_SYMBOL(xdr_decode_netobj); EXPORT_SYMBOL(xdr_encode_netobj);