]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
NFS: Fix another fsync() issue after a server reboot
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Sat, 13 Aug 2022 12:22:25 +0000 (08:22 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 31 Aug 2022 15:17:59 +0000 (17:17 +0200)
commit 67f4b5dc49913abcdb5cc736e73674e2f352f81d upstream.

Currently, when the writeback code detects a server reboot, it redirties
any pages that were not committed to disk, and it sets the flag
NFS_CONTEXT_RESEND_WRITES in the nfs_open_context of the file descriptor
that dirtied the file. While this allows the file descriptor in question
to redrive its own writes, it violates the fsync() requirement that we
should be synchronising all writes to disk.
While the problem is infrequent, we do see corner cases where an
untimely server reboot causes the fsync() call to abandon its attempt to
sync data to disk and causing data corruption issues due to missed error
conditions or similar.

In order to tighted up the client's ability to deal with this situation
without introducing livelocks, add a counter that records the number of
times pages are redirtied due to a server reboot-like condition, and use
that in fsync() to redrive the sync to disk.

Fixes: 2197e9b06c22 ("NFS: Fix up fsync() when the server rebooted")
Cc: stable@vger.kernel.org
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/nfs/file.c
fs/nfs/inode.c
fs/nfs/write.c
include/linux/nfs_fs.h

index 2d72b1b7ed74c912e62f0a6a23a20eda38fdbdd7..9a0e4a89cdf1497969db1773ca4f8b0cb706ab4d 100644 (file)
@@ -221,8 +221,10 @@ nfs_file_fsync_commit(struct file *file, int datasync)
 int
 nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
 {
-       struct nfs_open_context *ctx = nfs_file_open_context(file);
        struct inode *inode = file_inode(file);
+       struct nfs_inode *nfsi = NFS_I(inode);
+       long save_nredirtied = atomic_long_read(&nfsi->redirtied_pages);
+       long nredirtied;
        int ret;
 
        trace_nfs_fsync_enter(inode);
@@ -237,15 +239,10 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
                ret = pnfs_sync_inode(inode, !!datasync);
                if (ret != 0)
                        break;
-               if (!test_and_clear_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags))
+               nredirtied = atomic_long_read(&nfsi->redirtied_pages);
+               if (nredirtied == save_nredirtied)
                        break;
-               /*
-                * If nfs_file_fsync_commit detected a server reboot, then
-                * resend all dirty pages that might have been covered by
-                * the NFS_CONTEXT_RESEND_WRITES flag
-                */
-               start = 0;
-               end = LLONG_MAX;
+               save_nredirtied = nredirtied;
        }
 
        trace_nfs_fsync_exit(inode, ret);
index b4e46b0ffa2dc04d268827312286af190fef1ab3..bea7c005119c3e64921fbdd85b63e3865822b483 100644 (file)
@@ -426,6 +426,7 @@ nfs_ilookup(struct super_block *sb, struct nfs_fattr *fattr, struct nfs_fh *fh)
 static void nfs_inode_init_regular(struct nfs_inode *nfsi)
 {
        atomic_long_set(&nfsi->nrequests, 0);
+       atomic_long_set(&nfsi->redirtied_pages, 0);
        INIT_LIST_HEAD(&nfsi->commit_info.list);
        atomic_long_set(&nfsi->commit_info.ncommit, 0);
        atomic_set(&nfsi->commit_info.rpcs_out, 0);
index 1c706465d090b095b98c900d8b5fe7cb407af57e..5d7e1c2061842b113c188ecd90333699830b3eaa 100644 (file)
@@ -1419,10 +1419,12 @@ static void nfs_initiate_write(struct nfs_pgio_header *hdr,
  */
 static void nfs_redirty_request(struct nfs_page *req)
 {
+       struct nfs_inode *nfsi = NFS_I(page_file_mapping(req->wb_page)->host);
+
        /* Bump the transmission count */
        req->wb_nio++;
        nfs_mark_request_dirty(req);
-       set_bit(NFS_CONTEXT_RESEND_WRITES, &nfs_req_openctx(req)->flags);
+       atomic_long_inc(&nfsi->redirtied_pages);
        nfs_end_page_writeback(req);
        nfs_release_request(req);
 }
@@ -1892,7 +1894,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)
                /* We have a mismatch. Write the page again */
                dprintk_cont(" mismatch\n");
                nfs_mark_request_dirty(req);
-               set_bit(NFS_CONTEXT_RESEND_WRITES, &nfs_req_openctx(req)->flags);
+               atomic_long_inc(&NFS_I(data->inode)->redirtied_pages);
        next:
                nfs_unlock_and_release_request(req);
                /* Latency breaker */
index a17c337dbdf1d485d95e726e8e19f41a8c72068b..b1f83697699ee813e51984e35b77fb410b9ca9de 100644 (file)
@@ -182,6 +182,7 @@ struct nfs_inode {
                /* Regular file */
                struct {
                        atomic_long_t   nrequests;
+                       atomic_long_t   redirtied_pages;
                        struct nfs_mds_commit_info commit_info;
                        struct mutex    commit_mutex;
                };