NFSv4: Convert the NFSv4 close and open_downgrade operations to use asynchronous RPC calls. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 171 ++++++++++++++++++++++--------------------------- fs/nfs/nfs4state.c | 10 +- include/linux/nfs_fs.h | 3 3 files changed, 83 insertions(+), 101 deletions(-) Index: linux-2.6.10-rc1/fs/nfs/nfs4proc.c =================================================================== --- linux-2.6.10-rc1.orig/fs/nfs/nfs4proc.c 2004-10-27 14:20:28.444111925 -0400 +++ linux-2.6.10-rc1/fs/nfs/nfs4proc.c 2004-10-27 14:24:05.954057052 -0400 @@ -660,6 +660,61 @@ int nfs4_do_setattr(struct nfs_server *s return err; } +struct nfs4_closedata { + struct inode *inode; + struct nfs4_state *state; + struct nfs_closeargs arg; + struct nfs_closeres res; +}; + +static void nfs4_close_done(struct rpc_task *task) +{ + struct nfs4_closedata *calldata = (struct nfs4_closedata *)task->tk_calldata; + struct nfs4_state *state = calldata->state; + struct nfs4_state_owner *sp = state->owner; + struct nfs_server *server = NFS_SERVER(calldata->inode); + + /* hmm. we are done with the inode, and in the process of freeing + * the state_owner. we keep this around to process errors + */ + nfs4_increment_seqid(task->tk_status, sp); + switch (task->tk_status) { + case 0: + state->state = calldata->arg.open_flags; + memcpy(&state->stateid, &calldata->res.stateid, + sizeof(state->stateid)); + break; + case -NFS4ERR_STALE_STATEID: + case -NFS4ERR_EXPIRED: + state->state = calldata->arg.open_flags; + nfs4_schedule_state_recovery(server->nfs4_state); + break; + default: + if (nfs4_async_handle_error(task, server) == -EAGAIN) { + rpc_restart_call(task); + return; + } + } + nfs4_put_open_state(state); + up(&sp->so_sema); + nfs4_put_state_owner(sp); + up_read(&server->nfs4_state->cl_sem); + kfree(calldata); +} + +static inline int nfs4_close_call(struct rpc_clnt *clnt, struct nfs4_closedata *calldata) +{ + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE], + .rpc_argp = &calldata->arg, + .rpc_resp = &calldata->res, + .rpc_cred = calldata->state->owner->so_cred, + }; + if (calldata->arg.open_flags != 0) + msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE]; + return rpc_call_async(clnt, &msg, 0, nfs4_close_done, calldata); +} + /* * 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). @@ -671,104 +726,34 @@ int nfs4_do_setattr(struct nfs_server *s * * NOTE: Caller must be holding the sp->so_owner semaphore! */ -static int _nfs4_do_close(struct inode *inode, struct nfs4_state *state) +int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode) { - struct nfs4_state_owner *sp = state->owner; - int status = 0; - struct nfs_closeargs arg = { - .fh = NFS_FH(inode), - }; - struct nfs_closeres res; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE], - .rpc_argp = &arg, - .rpc_resp = &res, - .rpc_cred = sp->so_cred, - }; + struct nfs4_closedata *calldata; + int status; - if (test_bit(NFS_DELEGATED_STATE, &state->flags)) + /* Tell caller we're done */ + if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { + state->state = mode; return 0; - memcpy(&arg.stateid, &state->stateid, sizeof(arg.stateid)); + } + calldata = (struct nfs4_closedata *)kmalloc(sizeof(*calldata), GFP_KERNEL); + if (calldata == NULL) + return -ENOMEM; + calldata->inode = inode; + calldata->state = state; + calldata->arg.fh = NFS_FH(inode); /* Serialization for the sequence id */ - arg.seqid = sp->so_seqid, - status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, RPC_TASK_NOINTR); - - /* hmm. we are done with the inode, and in the process of freeing - * the state_owner. we keep this around to process errors + calldata->arg.seqid = state->owner->so_seqid; + calldata->arg.open_flags = mode; + memcpy(&calldata->arg.stateid, &state->stateid, + sizeof(calldata->arg.stateid)); + status = nfs4_close_call(NFS_SERVER(inode)->client, calldata); + /* + * Return -EINPROGRESS on success in order to indicate to the + * caller that an asynchronous RPC call has been launched, and + * that it will release the semaphores on completion. */ - nfs4_increment_seqid(status, sp); - if (!status) - memcpy(&state->stateid, &res.stateid, sizeof(state->stateid)); - - return status; -} - -int nfs4_do_close(struct inode *inode, struct nfs4_state *state) -{ - struct nfs_server *server = NFS_SERVER(state->inode); - struct nfs4_exception exception = { }; - int err; - do { - err = _nfs4_do_close(inode, state); - switch (err) { - case -NFS4ERR_STALE_STATEID: - case -NFS4ERR_EXPIRED: - nfs4_schedule_state_recovery(server->nfs4_state); - err = 0; - default: - state->state = 0; - } - err = nfs4_handle_exception(server, err, &exception); - } while (exception.retry); - return err; -} - -static int _nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode) -{ - struct nfs4_state_owner *sp = state->owner; - int status = 0; - struct nfs_closeargs arg = { - .fh = NFS_FH(inode), - .seqid = sp->so_seqid, - .open_flags = mode, - }; - struct nfs_closeres res; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE], - .rpc_argp = &arg, - .rpc_resp = &res, - .rpc_cred = sp->so_cred, - }; - - if (test_bit(NFS_DELEGATED_STATE, &state->flags)) - return 0; - memcpy(&arg.stateid, &state->stateid, sizeof(arg.stateid)); - status = rpc_call_sync(NFS_SERVER(inode)->client, &msg, RPC_TASK_NOINTR); - nfs4_increment_seqid(status, sp); - if (!status) - memcpy(&state->stateid, &res.stateid, sizeof(state->stateid)); - - return status; -} - -int nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode) -{ - struct nfs_server *server = NFS_SERVER(state->inode); - struct nfs4_exception exception = { }; - int err; - do { - err = _nfs4_do_downgrade(inode, state, mode); - switch (err) { - case -NFS4ERR_STALE_STATEID: - case -NFS4ERR_EXPIRED: - nfs4_schedule_state_recovery(server->nfs4_state); - err = 0; - default: - state->state = mode; - } - err = nfs4_handle_exception(server, err, &exception); - } while (exception.retry); - return err; + return (status == 0) ? -EINPROGRESS : status; } struct inode * Index: linux-2.6.10-rc1/fs/nfs/nfs4state.c =================================================================== --- linux-2.6.10-rc1.orig/fs/nfs/nfs4state.c 2004-10-27 14:10:16.000000000 -0400 +++ linux-2.6.10-rc1/fs/nfs/nfs4state.c 2004-10-27 14:55:53.169283327 -0400 @@ -445,7 +445,7 @@ nfs4_get_open_state(struct inode *inode, state->owner = owner; atomic_inc(&owner->so_count); list_add(&state->inode_states, &nfsi->open_states); - state->inode = inode; + state->inode = igrab(inode); spin_unlock(&inode->i_lock); } else { spin_unlock(&inode->i_lock); @@ -471,6 +471,7 @@ void nfs4_put_open_state(struct nfs4_sta list_del(&state->inode_states); spin_unlock(&inode->i_lock); list_del(&state->open_states); + iput(inode); BUG_ON (state->state != 0); nfs4_free_open_state(state); nfs4_put_state_owner(owner); @@ -486,7 +487,6 @@ void nfs4_close_state(struct nfs4_state struct nfs4_state_owner *owner = state->owner; struct nfs4_client *clp = owner->so_client; int newstate; - int status = 0; atomic_inc(&owner->so_count); down_read(&clp->cl_sem); @@ -508,10 +508,8 @@ void nfs4_close_state(struct nfs4_state newstate |= FMODE_WRITE; if (state->state == newstate) goto out; - if (newstate != 0) - status = nfs4_do_downgrade(inode, state, newstate); - else - status = nfs4_do_close(inode, state); + if (nfs4_do_close(inode, state, newstate) == -EINPROGRESS) + return; } out: nfs4_put_open_state(state); Index: linux-2.6.10-rc1/include/linux/nfs_fs.h =================================================================== --- linux-2.6.10-rc1.orig/include/linux/nfs_fs.h 2004-10-27 14:10:27.000000000 -0400 +++ linux-2.6.10-rc1/include/linux/nfs_fs.h 2004-10-27 14:20:28.573094500 -0400 @@ -651,8 +651,7 @@ extern int nfs4_proc_setclientid_confirm extern int nfs4_open_reclaim(struct nfs4_state_owner *, struct nfs4_state *); extern int nfs4_proc_async_renew(struct nfs4_client *); extern int nfs4_proc_renew(struct nfs4_client *); -extern int nfs4_do_close(struct inode *, struct nfs4_state *); -extern int nfs4_do_downgrade(struct inode *inode, struct nfs4_state *state, mode_t mode); +extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state, mode_t mode); extern int nfs4_wait_clnt_recover(struct rpc_clnt *, struct nfs4_client *); extern struct inode *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); extern int nfs4_open_revalidate(struct inode *, struct dentry *, int);