NFS: Don't report ENOSPC write errors twice

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: ce368536dd ("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>
This commit is contained in:
Trond Myklebust 2022-05-14 10:27:02 -04:00 committed by Anna Schumaker
parent 9641d9bc9b
commit e6005436f6

View File

@ -598,18 +598,6 @@ static const struct vm_operations_struct nfs_file_vm_ops = {
.page_mkwrite = nfs_vm_page_mkwrite, .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) ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
{ {
struct file *file = iocb->ki_filp; struct file *file = iocb->ki_filp;
@ -637,7 +625,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)) { if (iocb->ki_flags & IOCB_APPEND || iocb->ki_pos > i_size_read(inode)) {
result = nfs_revalidate_file_size(inode, file); result = nfs_revalidate_file_size(inode, file);
if (result) if (result)
goto out; return result;
} }
nfs_clear_invalid_mapping(file->f_mapping); nfs_clear_invalid_mapping(file->f_mapping);
@ -656,6 +644,7 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
written = result; written = result;
iocb->ki_pos += written; iocb->ki_pos += written;
nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written);
if (mntflags & NFS_MOUNT_WRITE_EAGER) { if (mntflags & NFS_MOUNT_WRITE_EAGER) {
result = filemap_fdatawrite_range(file->f_mapping, result = filemap_fdatawrite_range(file->f_mapping,
@ -673,17 +662,22 @@ ssize_t nfs_file_write(struct kiocb *iocb, struct iov_iter *from)
} }
result = generic_write_sync(iocb, written); result = generic_write_sync(iocb, written);
if (result < 0) if (result < 0)
goto out; return result;
out:
/* Return error values */ /* Return error values */
error = filemap_check_wb_err(file->f_mapping, since); error = filemap_check_wb_err(file->f_mapping, since);
if (nfs_need_check_write(file, inode, error)) { switch (error) {
int err = nfs_wb_all(inode); default:
if (err < 0) break;
result = err; 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; return result;
out_swapfile: out_swapfile: