Commit e6005436 authored by Trond Myklebust's avatar Trond Myklebust Committed by Anna Schumaker
Browse files

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: default avatarChenXiaoSong <chenxiaosong2@huawei.com>
Fixes: ce368536 ("nfs: nfs_file_write() should check for writeback errors")
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: default avatarAnna Schumaker <Anna.Schumaker@Netapp.com>
parent 9641d9bc
Loading
Loading
Loading
Loading
+14 −20
Original line number Diff line number Diff line
@@ -598,18 +598,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;
@@ -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)) {
		result = nfs_revalidate_file_size(inode, file);
		if (result)
			goto out;
			return result;
	}

	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;
	iocb->ki_pos += written;
	nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, written);

	if (mntflags & NFS_MOUNT_WRITE_EAGER) {
		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);
	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: