Skip to content

Commit c4f24df

Browse files
committed
NFS: Fix unstable write completion
We do want to respect the FLUSH_SYNC argument to nfs_commit_inode() to ensure that all outstanding COMMIT requests to the inode in question are complete. Currently we may exit early from both nfs_commit_inode() and nfs_write_inode() even if there are COMMIT requests in flight, or unstable writes on the commit list. In order to get the right semantics w.r.t. sync_inode(), we don't need to have nfs_commit_inode() reset the inode dirty flags when called from nfs_wb_page() and/or nfs_wb_all(). We just need to ensure that nfs_write_inode() leaves them in the right state if there are outstanding commits, or stable pages. Reported-by: Scott Mayhew <smayhew@redhat.com> Fixes: dc4fd9a ("nfs: don't wait on commit in nfs_commit_inode()...") Cc: stable@vger.kernel.org # v4.14+ Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
1 parent 9c6376e commit c4f24df

File tree

1 file changed

+43
-40
lines changed

1 file changed

+43
-40
lines changed

fs/nfs/write.c

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1876,40 +1876,43 @@ int nfs_generic_commit_list(struct inode *inode, struct list_head *head,
18761876
return status;
18771877
}
18781878

1879-
int nfs_commit_inode(struct inode *inode, int how)
1879+
static int __nfs_commit_inode(struct inode *inode, int how,
1880+
struct writeback_control *wbc)
18801881
{
18811882
LIST_HEAD(head);
18821883
struct nfs_commit_info cinfo;
18831884
int may_wait = how & FLUSH_SYNC;
1884-
int error = 0;
1885-
int res;
1885+
int ret, nscan;
18861886

18871887
nfs_init_cinfo_from_inode(&cinfo, inode);
18881888
nfs_commit_begin(cinfo.mds);
1889-
res = nfs_scan_commit(inode, &head, &cinfo);
1890-
if (res)
1891-
error = nfs_generic_commit_list(inode, &head, how, &cinfo);
1889+
for (;;) {
1890+
ret = nscan = nfs_scan_commit(inode, &head, &cinfo);
1891+
if (ret <= 0)
1892+
break;
1893+
ret = nfs_generic_commit_list(inode, &head, how, &cinfo);
1894+
if (ret < 0)
1895+
break;
1896+
ret = 0;
1897+
if (wbc && wbc->sync_mode == WB_SYNC_NONE) {
1898+
if (nscan < wbc->nr_to_write)
1899+
wbc->nr_to_write -= nscan;
1900+
else
1901+
wbc->nr_to_write = 0;
1902+
}
1903+
if (nscan < INT_MAX)
1904+
break;
1905+
cond_resched();
1906+
}
18921907
nfs_commit_end(cinfo.mds);
1893-
if (res == 0)
1894-
return res;
1895-
if (error < 0)
1896-
goto out_error;
1897-
if (!may_wait)
1898-
goto out_mark_dirty;
1899-
error = wait_on_commit(cinfo.mds);
1900-
if (error < 0)
1901-
return error;
1902-
return res;
1903-
out_error:
1904-
res = error;
1905-
/* Note: If we exit without ensuring that the commit is complete,
1906-
* we must mark the inode as dirty. Otherwise, future calls to
1907-
* sync_inode() with the WB_SYNC_ALL flag set will fail to ensure
1908-
* that the data is on the disk.
1909-
*/
1910-
out_mark_dirty:
1911-
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
1912-
return res;
1908+
if (ret || !may_wait)
1909+
return ret;
1910+
return wait_on_commit(cinfo.mds);
1911+
}
1912+
1913+
int nfs_commit_inode(struct inode *inode, int how)
1914+
{
1915+
return __nfs_commit_inode(inode, how, NULL);
19131916
}
19141917
EXPORT_SYMBOL_GPL(nfs_commit_inode);
19151918

@@ -1919,11 +1922,11 @@ int nfs_write_inode(struct inode *inode, struct writeback_control *wbc)
19191922
int flags = FLUSH_SYNC;
19201923
int ret = 0;
19211924

1922-
/* no commits means nothing needs to be done */
1923-
if (!atomic_long_read(&nfsi->commit_info.ncommit))
1924-
return ret;
1925-
19261925
if (wbc->sync_mode == WB_SYNC_NONE) {
1926+
/* no commits means nothing needs to be done */
1927+
if (!atomic_long_read(&nfsi->commit_info.ncommit))
1928+
goto check_requests_outstanding;
1929+
19271930
/* Don't commit yet if this is a non-blocking flush and there
19281931
* are a lot of outstanding writes for this mapping.
19291932
*/
@@ -1934,16 +1937,16 @@ int nfs_write_inode(struct inode *inode, struct writeback_control *wbc)
19341937
flags = 0;
19351938
}
19361939

1937-
ret = nfs_commit_inode(inode, flags);
1938-
if (ret >= 0) {
1939-
if (wbc->sync_mode == WB_SYNC_NONE) {
1940-
if (ret < wbc->nr_to_write)
1941-
wbc->nr_to_write -= ret;
1942-
else
1943-
wbc->nr_to_write = 0;
1944-
}
1945-
return 0;
1946-
}
1940+
ret = __nfs_commit_inode(inode, flags, wbc);
1941+
if (!ret) {
1942+
if (flags & FLUSH_SYNC)
1943+
return 0;
1944+
} else if (atomic_long_read(&nfsi->commit_info.ncommit))
1945+
goto out_mark_dirty;
1946+
1947+
check_requests_outstanding:
1948+
if (!atomic_read(&nfsi->commit_info.rpcs_out))
1949+
return ret;
19471950
out_mark_dirty:
19481951
__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
19491952
return ret;

0 commit comments

Comments
 (0)