NFS: Cleanups for the network partition reclaim code Signed-off-by: Trond Myklebust --- nfs4proc.c | 97 ++++++++++++++++++++++++++++++++++-------------------------- nfs4state.c | 20 ++++++++++-- 2 files changed, 72 insertions(+), 45 deletions(-) Index: linux-2.6.11/fs/nfs/nfs4proc.c =================================================================== --- linux-2.6.11.orig/fs/nfs/nfs4proc.c +++ linux-2.6.11/fs/nfs/nfs4proc.c @@ -190,6 +190,23 @@ static void update_changeattr(struct ino nfsi->change_attr = cinfo->after; } +static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags) +{ + struct inode *inode = state->inode; + + open_flags &= (FMODE_READ|FMODE_WRITE); + /* Protect against nfs4_find_state() */ + spin_lock(&inode->i_lock); + state->state |= open_flags; + /* NB! List reordering - see the reclaim code for why. */ + if ((open_flags & FMODE_WRITE) && 0 == state->nwriters++) + list_move(&state->open_states, &state->owner->so_states); + if (open_flags & FMODE_READ) + state->nreaders++; + memcpy(&state->stateid, stateid, sizeof(state->stateid)); + spin_unlock(&inode->i_lock); +} + /* * OPEN_RECLAIM: * reclaim state on the server after a reboot. @@ -334,7 +351,7 @@ 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) +static inline int _nfs4_proc_open_confirm(struct rpc_clnt *clnt, const struct nfs_fh *fh, struct nfs4_state_owner *sp, nfs4_stateid *stateid) { struct nfs_open_confirmargs arg = { .fh = fh, @@ -357,6 +374,39 @@ static int _nfs4_proc_open_confirm(struc 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) +{ + struct nfs_server *server = NFS_SERVER(dir); + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN], + .rpc_argp = o_arg, + .rpc_resp = o_res, + .rpc_cred = sp->so_cred, + }; + int status; + + /* Update sequence id. The caller must serialize! */ + o_arg->seqid = sp->so_seqid; + o_arg->id = sp->so_id; + o_arg->clientid = sp->so_client->cl_clientid; + + status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR); + nfs4_increment_seqid(status, sp); + if (status != 0) + goto out; + update_changeattr(dir, &o_res->cinfo); + if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) { + status = _nfs4_proc_open_confirm(server->client, &o_res->fh, + sp, &o_res->stateid); + if (status != 0) + goto out; + } + 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; +} + static int _nfs4_do_access(struct inode *inode, struct rpc_cred *cred, int openflags) { struct nfs_access_entry cache; @@ -434,16 +484,8 @@ static int _nfs4_open_delegated(struct i unlock_kernel(); if (err != 0) goto out_err; - spin_lock(&inode->i_lock); - memcpy(state->stateid.data, delegation->stateid.data, - sizeof(state->stateid.data)); - state->state |= open_flags; - if (open_flags & FMODE_READ) - state->nreaders++; - if (open_flags & FMODE_WRITE) - state->nwriters++; set_bit(NFS_DELEGATED_STATE, &state->flags); - spin_unlock(&inode->i_lock); + update_open_stateid(state, &delegation->stateid, open_flags); out_ok: up(&sp->so_sema); nfs4_put_state_owner(sp); @@ -506,12 +548,6 @@ static int _nfs4_do_open(struct inode *d .f_attr = &f_attr, .server = server, }; - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN], - .rpc_argp = &o_arg, - .rpc_resp = &o_res, - .rpc_cred = cred, - }; /* Protect against reboot recovery conflicts */ down_read(&clp->cl_sem); @@ -528,26 +564,10 @@ static int _nfs4_do_open(struct inode *d o_arg.u.attrs = sattr; /* Serialization for the sequence id */ down(&sp->so_sema); - o_arg.seqid = sp->so_seqid; - o_arg.id = sp->so_id; - o_arg.clientid = clp->cl_clientid, - status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR); - nfs4_increment_seqid(status, sp); - if (status) + status = _nfs4_proc_open(dir, sp, &o_arg, &o_res); + if (status != 0) goto out_err; - update_changeattr(dir, &o_res.cinfo); - if(o_res.rflags & NFS4_OPEN_RESULT_CONFIRM) { - status = _nfs4_proc_open_confirm(server->client, &o_res.fh, - sp, &o_res.stateid); - if (status != 0) - goto out_err; - } - if (!(f_attr.valid & NFS_ATTR_FATTR)) { - status = server->rpc_ops->getattr(server, &o_res.fh, &f_attr); - if (status < 0) - goto out_err; - } status = -ENOMEM; inode = nfs_fhget(dir->i_sb, &o_res.fh, &f_attr); @@ -556,14 +576,7 @@ static int _nfs4_do_open(struct inode *d state = nfs4_get_open_state(inode, sp); if (!state) goto out_err; - memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid)); - spin_lock(&inode->i_lock); - if (flags & FMODE_READ) - state->nreaders++; - if (flags & FMODE_WRITE) - state->nwriters++; - state->state |= flags & (FMODE_READ|FMODE_WRITE); - spin_unlock(&inode->i_lock); + update_open_stateid(state, &o_res.stateid, flags); if (o_res.delegation_type != 0) nfs_inode_set_delegation(inode, cred, &o_res); up(&sp->so_sema); Index: linux-2.6.11/fs/nfs/nfs4state.c =================================================================== --- linux-2.6.11.orig/fs/nfs/nfs4state.c +++ linux-2.6.11/fs/nfs/nfs4state.c @@ -447,7 +447,9 @@ nfs4_get_open_state(struct inode *inode, if (state == NULL && new != NULL) { state = new; /* Caller *must* be holding owner->so_sem */ - list_add(&state->open_states, &owner->so_states); + /* Note: The reclaim code dictates that we add stateless + * and read-only stateids to the end of the list */ + list_add_tail(&state->open_states, &owner->so_states); state->owner = owner; atomic_inc(&owner->so_count); list_add(&state->inode_states, &nfsi->open_states); @@ -503,8 +505,12 @@ void nfs4_close_state(struct nfs4_state state->nreaders--; if (mode & FMODE_WRITE) state->nwriters--; - if (state->nwriters == 0 && state->nreaders == 0) - list_del_init(&state->inode_states); + if (state->nwriters == 0) { + if (state->nreaders == 0) + list_del_init(&state->inode_states); + /* See reclaim code */ + list_move_tail(&state->open_states, &owner->so_states); + } spin_unlock(&inode->i_lock); newstate = 0; if (state->state != 0) { @@ -798,6 +804,14 @@ static int nfs4_reclaim_open_state(struc struct nfs4_lock_state *lock; int status = 0; + /* Note: we rely on the sp->so_states list being ordered + * so that we always reclaim open(O_RDWR) and/or open(O_WRITE) + * states first. + * This is needed to ensure that the server won't give us any + * read delegations that we have to return if, say, we are + * recovering after a network partition or a reboot from a + * server that doesn't support a grace period. + */ list_for_each_entry(state, &sp->so_states, open_states) { if (state->state == 0) continue;