NFS: Don't report ENOSPC write errors twice
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Sat, 14 May 2022 14:27:02 +0000 (10:27 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 9 Jun 2022 08:23:15 +0000 (10:23 +0200)
[ Upstream commit e6005436f6cc9ed13288f936903f0151e5543485 ]

Any errors reported by the write() system call need to be cleared from
the file descriptor's error tracking. The current call to nfs_wb_all()
causes the error to be reported, but since it doesn't call
file_check_and_advance_wb_err(), we can end up reporting the same error
a second time when the application calls fsync().

Note that since Linux 4.13, the rule is that EIO may be reported for
write(), but it must be reported by a subsequent fsync(), so let's just
drop reporting it in write.

The check for nfs_ctx_key_to_expire() is just a duplicate to the one
already in nfs_write_end(), so let's drop that too.

Reported-by: ChenXiaoSong <chenxiaosong2@huawei.com>
Fixes: ce368536dd61 ("nfs: nfs_file_write() should check for writeback errors")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/nfs/file.c

index 018d29a79e736601272a740f4e4034c4bbf0dc47..1b66362696e07bf7cdb339c79212591663c0c026 100644 (file)
@@ -591,18 +591,6 @@ static const struct vm_operations_struct nfs_file_vm_ops = {
        .page_mkwrite = nfs_vm_page_mkwrite,
 };
 
-static int nfs_need_check_write(struct file *filp, struct inode *inode,
-                               int error)
-{
-       struct nfs_open_context *ctx;
-
-       ctx = nfs_file_open_context(filp);
-       if (nfs_error_is_fatal_on_server(error) ||
-           nfs_ctx_key_to_expire(ctx, inode))
-               return 1;
-       return 0;
-}
-
 ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
 {
        struct file *file = iocb->ki_filp;
@@ -630,7 +618,7 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
        if (iocb->ki_flags & IOCB_APPEND || iocb->ki_pos > i_size_read(inode)) {
                result = nfs_revalidate_file_size(inode, file);
                if (result)
-                       goto out;
+                       return result;
        }
 
        nfs_clear_invalid_mapping(file->f_mapping);
@@ -649,6 +637,7 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
 
        written = result;
        iocb->ki_pos += written;
+       nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written);
 
        if (mntflags & NFS_MOUNT_WRITE_EAGER) {
                result = filemap_fdatawrite_range(file->f_mapping,
@@ -666,17 +655,22 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
        }
        result = generic_write_sync(iocb, written);
        if (result < 0)
-               goto out;
+               return result;
 
+out:
        /* Return error values */
        error = filemap_check_wb_err(file->f_mapping, since);
-       if (nfs_need_check_write(file, inode, error)) {
-               int err = nfs_wb_all(inode);
-               if (err < 0)
-                       result = err;
+       switch (error) {
+       default:
+               break;
+       case -EDQUOT:
+       case -EFBIG:
+       case -ENOSPC:
+               nfs_wb_all(inode);
+               error = file_check_and_advance_wb_err(file);
+               if (error < 0)
+                       result = error;
        }
-       nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written);
-out:
        return result;
 
 out_swapfile: