diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-09-20 20:15:57 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-09-28 16:03:11 -0400 |
commit | 7fdab069b7172f2348cf3d87e19c6c24340292bf (patch) | |
tree | 487de5c39bb8be13be52091acc83139435943000 /fs/nfs | |
parent | 115ce575cb10918514d053ef15f597a4e6ff60e9 (diff) |
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>
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/nfs4proc.c | 8 | ||||
-rw-r--r-- | fs/nfs/pnfs.c | 22 | ||||
-rw-r--r-- | fs/nfs/pnfs.h | 4 |
3 files changed, 17 insertions, 17 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index e605d417a006..6d5750cabd8d 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -2137,6 +2137,7 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data) | |||
2137 | { | 2137 | { |
2138 | struct nfs4_closedata *calldata = data; | 2138 | struct nfs4_closedata *calldata = data; |
2139 | struct nfs4_state *state = calldata->state; | 2139 | struct nfs4_state *state = calldata->state; |
2140 | struct inode *inode = calldata->inode; | ||
2140 | int call_close = 0; | 2141 | int call_close = 0; |
2141 | 2142 | ||
2142 | dprintk("%s: begin!\n", __func__); | 2143 | dprintk("%s: begin!\n", __func__); |
@@ -2170,16 +2171,13 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data) | |||
2170 | if (calldata->arg.fmode == 0) { | 2171 | if (calldata->arg.fmode == 0) { |
2171 | task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE]; | 2172 | task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE]; |
2172 | if (calldata->roc && | 2173 | if (calldata->roc && |
2173 | pnfs_roc_drain(calldata->inode, &calldata->roc_barrier)) { | 2174 | pnfs_roc_drain(inode, &calldata->roc_barrier, task)) |
2174 | rpc_sleep_on(&NFS_SERVER(calldata->inode)->roc_rpcwaitq, | ||
2175 | task, NULL); | ||
2176 | goto out; | 2175 | goto out; |
2177 | } | ||
2178 | } | 2176 | } |
2179 | 2177 | ||
2180 | nfs_fattr_init(calldata->res.fattr); | 2178 | nfs_fattr_init(calldata->res.fattr); |
2181 | calldata->timestamp = jiffies; | 2179 | calldata->timestamp = jiffies; |
2182 | if (nfs4_setup_sequence(NFS_SERVER(calldata->inode), | 2180 | if (nfs4_setup_sequence(NFS_SERVER(inode), |
2183 | &calldata->arg.seq_args, | 2181 | &calldata->arg.seq_args, |
2184 | &calldata->res.seq_res, | 2182 | &calldata->res.seq_res, |
2185 | task)) | 2183 | task)) |
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 9ee3bd705b94..8b32f8745337 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c | |||
@@ -807,27 +807,29 @@ void pnfs_roc_set_barrier(struct inode *ino, u32 barrier) | |||
807 | spin_unlock(&ino->i_lock); | 807 | spin_unlock(&ino->i_lock); |
808 | } | 808 | } |
809 | 809 | ||
810 | bool pnfs_roc_drain(struct inode *ino, u32 *barrier) | 810 | bool pnfs_roc_drain(struct inode *ino, u32 *barrier, struct rpc_task *task) |
811 | { | 811 | { |
812 | struct nfs_inode *nfsi = NFS_I(ino); | 812 | struct nfs_inode *nfsi = NFS_I(ino); |
813 | struct pnfs_layout_hdr *lo; | ||
813 | struct pnfs_layout_segment *lseg; | 814 | struct pnfs_layout_segment *lseg; |
815 | u32 current_seqid; | ||
814 | bool found = false; | 816 | bool found = false; |
815 | 817 | ||
816 | spin_lock(&ino->i_lock); | 818 | spin_lock(&ino->i_lock); |
817 | list_for_each_entry(lseg, &nfsi->layout->plh_segs, pls_list) | 819 | list_for_each_entry(lseg, &nfsi->layout->plh_segs, pls_list) |
818 | if (test_bit(NFS_LSEG_ROC, &lseg->pls_flags)) { | 820 | if (test_bit(NFS_LSEG_ROC, &lseg->pls_flags)) { |
821 | rpc_sleep_on(&NFS_SERVER(ino)->roc_rpcwaitq, task, NULL); | ||
819 | found = true; | 822 | found = true; |
820 | break; | 823 | goto out; |
821 | } | 824 | } |
822 | if (!found) { | 825 | lo = nfsi->layout; |
823 | struct pnfs_layout_hdr *lo = nfsi->layout; | 826 | current_seqid = be32_to_cpu(lo->plh_stateid.seqid); |
824 | u32 current_seqid = be32_to_cpu(lo->plh_stateid.seqid); | ||
825 | 827 | ||
826 | /* Since close does not return a layout stateid for use as | 828 | /* Since close does not return a layout stateid for use as |
827 | * a barrier, we choose the worst-case barrier. | 829 | * a barrier, we choose the worst-case barrier. |
828 | */ | 830 | */ |
829 | *barrier = current_seqid + atomic_read(&lo->plh_outstanding); | 831 | *barrier = current_seqid + atomic_read(&lo->plh_outstanding); |
830 | } | 832 | out: |
831 | spin_unlock(&ino->i_lock); | 833 | spin_unlock(&ino->i_lock); |
832 | return found; | 834 | return found; |
833 | } | 835 | } |
diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 9735031e1e1a..aa9fa1b1ff4a 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h | |||
@@ -210,7 +210,7 @@ int pnfs_mark_matching_lsegs_invalid(struct pnfs_layout_hdr *lo, | |||
210 | bool pnfs_roc(struct inode *ino); | 210 | bool pnfs_roc(struct inode *ino); |
211 | void pnfs_roc_release(struct inode *ino); | 211 | void pnfs_roc_release(struct inode *ino); |
212 | void pnfs_roc_set_barrier(struct inode *ino, u32 barrier); | 212 | void pnfs_roc_set_barrier(struct inode *ino, u32 barrier); |
213 | bool pnfs_roc_drain(struct inode *ino, u32 *barrier); | 213 | bool pnfs_roc_drain(struct inode *ino, u32 *barrier, struct rpc_task *task); |
214 | void pnfs_set_layoutcommit(struct nfs_write_data *wdata); | 214 | void pnfs_set_layoutcommit(struct nfs_write_data *wdata); |
215 | void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data); | 215 | void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data); |
216 | int pnfs_layoutcommit_inode(struct inode *inode, bool sync); | 216 | int pnfs_layoutcommit_inode(struct inode *inode, bool sync); |
@@ -442,7 +442,7 @@ pnfs_roc_set_barrier(struct inode *ino, u32 barrier) | |||
442 | } | 442 | } |
443 | 443 | ||
444 | static inline bool | 444 | static inline bool |
445 | pnfs_roc_drain(struct inode *ino, u32 *barrier) | 445 | pnfs_roc_drain(struct inode *ino, u32 *barrier, struct rpc_task *task) |
446 | { | 446 | { |
447 | return false; | 447 | return false; |
448 | } | 448 | } |