NFSv4.1: Fix a race in the pNFS return-on-close code

If we sleep after dropping the inode->i_lock, then we are no longer
atomic with respect to the rpc_wake_up() call in pnfs_layout_remove_lseg().

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
Trond Myklebust 2012-09-20 20:15:57 -04:00
parent 115ce575cb
commit 7fdab069b7
3 changed files with 17 additions and 17 deletions

View file

@ -2137,6 +2137,7 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
{ {
struct nfs4_closedata *calldata = data; struct nfs4_closedata *calldata = data;
struct nfs4_state *state = calldata->state; struct nfs4_state *state = calldata->state;
struct inode *inode = calldata->inode;
int call_close = 0; int call_close = 0;
dprintk("%s: begin!\n", __func__); dprintk("%s: begin!\n", __func__);
@ -2170,16 +2171,13 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
if (calldata->arg.fmode == 0) { if (calldata->arg.fmode == 0) {
task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE]; task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE];
if (calldata->roc && if (calldata->roc &&
pnfs_roc_drain(calldata->inode, &calldata->roc_barrier)) { pnfs_roc_drain(inode, &calldata->roc_barrier, task))
rpc_sleep_on(&NFS_SERVER(calldata->inode)->roc_rpcwaitq,
task, NULL);
goto out; goto out;
}
} }
nfs_fattr_init(calldata->res.fattr); nfs_fattr_init(calldata->res.fattr);
calldata->timestamp = jiffies; calldata->timestamp = jiffies;
if (nfs4_setup_sequence(NFS_SERVER(calldata->inode), if (nfs4_setup_sequence(NFS_SERVER(inode),
&calldata->arg.seq_args, &calldata->arg.seq_args,
&calldata->res.seq_res, &calldata->res.seq_res,
task)) task))

View file

@ -807,27 +807,29 @@ void pnfs_roc_set_barrier(struct inode *ino, u32 barrier)
spin_unlock(&ino->i_lock); spin_unlock(&ino->i_lock);
} }
bool pnfs_roc_drain(struct inode *ino, u32 *barrier) bool pnfs_roc_drain(struct inode *ino, u32 *barrier, struct rpc_task *task)
{ {
struct nfs_inode *nfsi = NFS_I(ino); struct nfs_inode *nfsi = NFS_I(ino);
struct pnfs_layout_hdr *lo;
struct pnfs_layout_segment *lseg; struct pnfs_layout_segment *lseg;
u32 current_seqid;
bool found = false; bool found = false;
spin_lock(&ino->i_lock); spin_lock(&ino->i_lock);
list_for_each_entry(lseg, &nfsi->layout->plh_segs, pls_list) list_for_each_entry(lseg, &nfsi->layout->plh_segs, pls_list)
if (test_bit(NFS_LSEG_ROC, &lseg->pls_flags)) { if (test_bit(NFS_LSEG_ROC, &lseg->pls_flags)) {
rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL);
found = true; found = true;
break; goto out;
} }
if (!found) { lo = nfsi->layout;
struct pnfs_layout_hdr *lo = nfsi->layout; current_seqid = be32_to_cpu(lo->plh_stateid.seqid);
u32 current_seqid = be32_to_cpu(lo->plh_stateid.seqid);
/* Since close does not return a layout stateid for use as /* Since close does not return a layout stateid for use as
* a barrier, we choose the worst-case barrier. * a barrier, we choose the worst-case barrier.
*/ */
*barrier = current_seqid + atomic_read(&lo->plh_outstanding); *barrier = current_seqid + atomic_read(&lo->plh_outstanding);
} out:
spin_unlock(&ino->i_lock); spin_unlock(&ino->i_lock);
return found; return found;
} }

View file

@ -210,7 +210,7 @@ int pnfs_mark_matching_lsegs_invalid(struct pnfs_layout_hdr *lo,
bool pnfs_roc(struct inode *ino); bool pnfs_roc(struct inode *ino);
void pnfs_roc_release(struct inode *ino); void pnfs_roc_release(struct inode *ino);
void pnfs_roc_set_barrier(struct inode *ino, u32 barrier); void pnfs_roc_set_barrier(struct inode *ino, u32 barrier);
bool pnfs_roc_drain(struct inode *ino, u32 *barrier); bool pnfs_roc_drain(struct inode *ino, u32 *barrier, struct rpc_task *task);
void pnfs_set_layoutcommit(struct nfs_write_data *wdata); void pnfs_set_layoutcommit(struct nfs_write_data *wdata);
void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data); void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data);
int pnfs_layoutcommit_inode(struct inode *inode, bool sync); int pnfs_layoutcommit_inode(struct inode *inode, bool sync);
@ -442,7 +442,7 @@ pnfs_roc_set_barrier(struct inode *ino, u32 barrier)
} }
static inline bool static inline bool
pnfs_roc_drain(struct inode *ino, u32 *barrier) pnfs_roc_drain(struct inode *ino, u32 *barrier, struct rpc_task *task)
{ {
return false; return false;
} }