|
@@ -176,6 +176,31 @@ static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)
|
|
return remote_llseek(filp, offset, origin);
|
|
return remote_llseek(filp, offset, origin);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Helper for nfs_file_flush() and nfs_fsync()
|
|
|
|
+ *
|
|
|
|
+ * Notice that it clears the NFS_CONTEXT_ERROR_WRITE before synching to
|
|
|
|
+ * disk, but it retrieves and clears ctx->error after synching, despite
|
|
|
|
+ * the two being set at the same time in nfs_context_set_write_error().
|
|
|
|
+ * This is because the former is used to notify the _next_ call to
|
|
|
|
+ * nfs_file_write() that a write error occured, and hence cause it to
|
|
|
|
+ * fall back to doing a synchronous write.
|
|
|
|
+ */
|
|
|
|
+static int nfs_do_fsync(struct nfs_open_context *ctx, struct inode *inode)
|
|
|
|
+{
|
|
|
|
+ int have_error, status;
|
|
|
|
+ int ret = 0;
|
|
|
|
+
|
|
|
|
+ have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
|
|
|
|
+ status = nfs_wb_all(inode);
|
|
|
|
+ have_error |= test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
|
|
|
|
+ if (have_error)
|
|
|
|
+ ret = xchg(&ctx->error, 0);
|
|
|
|
+ if (!ret)
|
|
|
|
+ ret = status;
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Flush all dirty pages, and check for write errors.
|
|
* Flush all dirty pages, and check for write errors.
|
|
*
|
|
*
|
|
@@ -192,16 +217,11 @@ nfs_file_flush(struct file *file, fl_owner_t id)
|
|
if ((file->f_mode & FMODE_WRITE) == 0)
|
|
if ((file->f_mode & FMODE_WRITE) == 0)
|
|
return 0;
|
|
return 0;
|
|
nfs_inc_stats(inode, NFSIOS_VFSFLUSH);
|
|
nfs_inc_stats(inode, NFSIOS_VFSFLUSH);
|
|
- lock_kernel();
|
|
|
|
|
|
+
|
|
/* Ensure that data+attribute caches are up to date after close() */
|
|
/* Ensure that data+attribute caches are up to date after close() */
|
|
- status = nfs_wb_all(inode);
|
|
|
|
- if (!status) {
|
|
|
|
- status = ctx->error;
|
|
|
|
- ctx->error = 0;
|
|
|
|
- if (!status)
|
|
|
|
- nfs_revalidate_inode(NFS_SERVER(inode), inode);
|
|
|
|
- }
|
|
|
|
- unlock_kernel();
|
|
|
|
|
|
+ status = nfs_do_fsync(ctx, inode);
|
|
|
|
+ if (!status)
|
|
|
|
+ nfs_revalidate_inode(NFS_SERVER(inode), inode);
|
|
return status;
|
|
return status;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -278,19 +298,11 @@ nfs_fsync(struct file *file, struct dentry *dentry, int datasync)
|
|
{
|
|
{
|
|
struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
|
|
struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data;
|
|
struct inode *inode = dentry->d_inode;
|
|
struct inode *inode = dentry->d_inode;
|
|
- int status;
|
|
|
|
|
|
|
|
dfprintk(VFS, "nfs: fsync(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);
|
|
dfprintk(VFS, "nfs: fsync(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);
|
|
|
|
|
|
nfs_inc_stats(inode, NFSIOS_VFSFSYNC);
|
|
nfs_inc_stats(inode, NFSIOS_VFSFSYNC);
|
|
- lock_kernel();
|
|
|
|
- status = nfs_wb_all(inode);
|
|
|
|
- if (!status) {
|
|
|
|
- status = ctx->error;
|
|
|
|
- ctx->error = 0;
|
|
|
|
- }
|
|
|
|
- unlock_kernel();
|
|
|
|
- return status;
|
|
|
|
|
|
+ return nfs_do_fsync(ctx, inode);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -377,6 +389,18 @@ static struct vm_operations_struct nfs_file_vm_ops = {
|
|
.page_mkwrite = nfs_vm_page_mkwrite,
|
|
.page_mkwrite = nfs_vm_page_mkwrite,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static int nfs_need_sync_write(struct file *filp, struct inode *inode)
|
|
|
|
+{
|
|
|
|
+ struct nfs_open_context *ctx;
|
|
|
|
+
|
|
|
|
+ if (IS_SYNC(inode) || (filp->f_flags & O_SYNC))
|
|
|
|
+ return 1;
|
|
|
|
+ ctx = filp->private_data;
|
|
|
|
+ if (test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags))
|
|
|
|
+ return 1;
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
|
|
static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
|
|
unsigned long nr_segs, loff_t pos)
|
|
unsigned long nr_segs, loff_t pos)
|
|
{
|
|
{
|
|
@@ -413,8 +437,8 @@ static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
|
|
nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, count);
|
|
nfs_add_stats(inode, NFSIOS_NORMALWRITTENBYTES, count);
|
|
result = generic_file_aio_write(iocb, iov, nr_segs, pos);
|
|
result = generic_file_aio_write(iocb, iov, nr_segs, pos);
|
|
/* Return error values for O_SYNC and IS_SYNC() */
|
|
/* Return error values for O_SYNC and IS_SYNC() */
|
|
- if (result >= 0 && (IS_SYNC(inode) || (iocb->ki_filp->f_flags & O_SYNC))) {
|
|
|
|
- int err = nfs_fsync(iocb->ki_filp, dentry, 1);
|
|
|
|
|
|
+ if (result >= 0 && nfs_need_sync_write(iocb->ki_filp, inode)) {
|
|
|
|
+ int err = nfs_do_fsync(iocb->ki_filp->private_data, inode);
|
|
if (err < 0)
|
|
if (err < 0)
|
|
result = err;
|
|
result = err;
|
|
}
|
|
}
|