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/nfs/pnfs.c | |
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/nfs/pnfs.c')
-rw-r--r-- | fs/nfs/pnfs.c | 19 |
1 files changed, 18 insertions, 1 deletions
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 | /* |