diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-03-20 12:34:32 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-03-21 10:31:19 -0400 |
commit | a073dbff359f4741013ae4b8395f5364c5e00b48 (patch) | |
tree | 511029ebb36cce1b4a03e4181da0dfe91cac268e /fs | |
parent | 4376c94618c26225e69e17b7c91169c45a90b292 (diff) |
NFSv4.1: Fix a race in pNFS layoutcommit
We need to clear the NFS_LSEG_LAYOUTCOMMIT bits atomically with the
NFS_INO_LAYOUTCOMMIT bit, otherwise we may end up with situations
where the two are out of sync.
The first half of the problem is to ensure that pnfs_layoutcommit_inode
clears the NFS_LSEG_LAYOUTCOMMIT bit through pnfs_list_write_lseg.
We still need to keep the reference to those segments until the RPC call
is finished, so in order to make it clear _where_ those references come
from, we add a helper pnfs_list_write_lseg_done() that cleans up after
pnfs_list_write_lseg.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Acked-by: Benny Halevy <bhalevy@tonian.com>
Cc: stable@vger.kernel.org
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/nfs4proc.c | 14 | ||||
-rw-r--r-- | fs/nfs/pnfs.c | 19 |
2 files changed, 18 insertions, 15 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index b2671cb0f901..6ccdd4fd9b59 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -6416,22 +6416,8 @@ nfs4_layoutcommit_done(struct rpc_task *task, void *calldata) | |||
6416 | static void nfs4_layoutcommit_release(void *calldata) | 6416 | static void nfs4_layoutcommit_release(void *calldata) |
6417 | { | 6417 | { |
6418 | struct nfs4_layoutcommit_data *data = calldata; | 6418 | struct nfs4_layoutcommit_data *data = calldata; |
6419 | struct pnfs_layout_segment *lseg, *tmp; | ||
6420 | unsigned long *bitlock = &NFS_I(data->args.inode)->flags; | ||
6421 | 6419 | ||
6422 | pnfs_cleanup_layoutcommit(data); | 6420 | pnfs_cleanup_layoutcommit(data); |
6423 | /* Matched by references in pnfs_set_layoutcommit */ | ||
6424 | list_for_each_entry_safe(lseg, tmp, &data->lseg_list, pls_lc_list) { | ||
6425 | list_del_init(&lseg->pls_lc_list); | ||
6426 | if (test_and_clear_bit(NFS_LSEG_LAYOUTCOMMIT, | ||
6427 | &lseg->pls_flags)) | ||
6428 | pnfs_put_lseg(lseg); | ||
6429 | } | ||
6430 | |||
6431 | clear_bit_unlock(NFS_INO_LAYOUTCOMMITTING, bitlock); | ||
6432 | smp_mb__after_clear_bit(); | ||
6433 | wake_up_bit(bitlock, NFS_INO_LAYOUTCOMMITTING); | ||
6434 | |||
6435 | put_rpccred(data->cred); | 6421 | put_rpccred(data->cred); |
6436 | kfree(data); | 6422 | kfree(data); |
6437 | } | 6423 | } |
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 48ac5aad6258..3d900916fd41 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c | |||
@@ -1746,11 +1746,27 @@ static void pnfs_list_write_lseg(struct inode *inode, struct list_head *listp) | |||
1746 | 1746 | ||
1747 | list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) { | 1747 | list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) { |
1748 | if (lseg->pls_range.iomode == IOMODE_RW && | 1748 | if (lseg->pls_range.iomode == IOMODE_RW && |
1749 | test_bit(NFS_LSEG_LAYOUTCOMMIT, &lseg->pls_flags)) | 1749 | test_and_clear_bit(NFS_LSEG_LAYOUTCOMMIT, &lseg->pls_flags)) |
1750 | list_add(&lseg->pls_lc_list, listp); | 1750 | list_add(&lseg->pls_lc_list, listp); |
1751 | } | 1751 | } |
1752 | } | 1752 | } |
1753 | 1753 | ||
1754 | static void pnfs_list_write_lseg_done(struct inode *inode, struct list_head *listp) | ||
1755 | { | ||
1756 | struct pnfs_layout_segment *lseg, *tmp; | ||
1757 | unsigned long *bitlock = &NFS_I(inode)->flags; | ||
1758 | |||
1759 | /* Matched by references in pnfs_set_layoutcommit */ | ||
1760 | list_for_each_entry_safe(lseg, tmp, listp, pls_lc_list) { | ||
1761 | list_del_init(&lseg->pls_lc_list); | ||
1762 | pnfs_put_lseg(lseg); | ||
1763 | } | ||
1764 | |||
1765 | clear_bit_unlock(NFS_INO_LAYOUTCOMMITTING, bitlock); | ||
1766 | smp_mb__after_clear_bit(); | ||
1767 | wake_up_bit(bitlock, NFS_INO_LAYOUTCOMMITTING); | ||
1768 | } | ||
1769 | |||
1754 | void pnfs_set_lo_fail(struct pnfs_layout_segment *lseg) | 1770 | void pnfs_set_lo_fail(struct pnfs_layout_segment *lseg) |
1755 | { | 1771 | { |
1756 | pnfs_layout_io_set_failed(lseg->pls_layout, lseg->pls_range.iomode); | 1772 | pnfs_layout_io_set_failed(lseg->pls_layout, lseg->pls_range.iomode); |
@@ -1795,6 +1811,7 @@ void pnfs_cleanup_layoutcommit(struct nfs4_layoutcommit_data *data) | |||
1795 | 1811 | ||
1796 | if (nfss->pnfs_curr_ld->cleanup_layoutcommit) | 1812 | if (nfss->pnfs_curr_ld->cleanup_layoutcommit) |
1797 | nfss->pnfs_curr_ld->cleanup_layoutcommit(data); | 1813 | nfss->pnfs_curr_ld->cleanup_layoutcommit(data); |
1814 | pnfs_list_write_lseg_done(data->args.inode, &data->lseg_list); | ||
1798 | } | 1815 | } |
1799 | 1816 | ||
1800 | /* | 1817 | /* |