Skip to content

Commit

Permalink
NFS: Fix fdatasync/fsync() when confronted with a server reboot
Browse files Browse the repository at this point in the history
If the server reboots before it can commit the unstable writes to disk,
then nfs_commit_release_pages() will detect this when it compares the
verifier returned by COMMIT to the one returned by WRITE. When this
happens, the client needs to resend those writes in order to guarantee
that they make it to stable storage.

This patch adds a signalling mechanism to notify fsync() that it
needs to retry all writes before it can exit.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
  • Loading branch information
Trond Myklebust authored and Trond Myklebust committed Sep 28, 2012
1 parent 795a88c commit 05990d1
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 22 deletions.
34 changes: 22 additions & 12 deletions fs/nfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,23 +259,31 @@ nfs_file_fsync_commit(struct file *file, loff_t start, loff_t end, int datasync)
struct dentry *dentry = file->f_path.dentry;
struct nfs_open_context *ctx = nfs_file_open_context(file);
struct inode *inode = dentry->d_inode;
int have_error, status;
int have_error, do_resend, status;
int ret = 0;

dprintk("NFS: fsync file(%s/%s) datasync %d\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
datasync);

nfs_inc_stats(inode, NFSIOS_VFSFSYNC);
do_resend = test_and_clear_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags);
have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
status = nfs_commit_inode(inode, FLUSH_SYNC);
if (status >= 0 && ret < 0)
status = ret;
have_error |= test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
if (have_error)
if (have_error) {
ret = xchg(&ctx->error, 0);
if (!ret && status < 0)
if (ret)
goto out;
}
if (status < 0) {
ret = status;
goto out;
}
do_resend |= test_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags);
if (do_resend)
ret = -EAGAIN;
out:
return ret;
}
EXPORT_SYMBOL_GPL(nfs_file_fsync_commit);
Expand All @@ -286,13 +294,15 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
int ret;
struct inode *inode = file->f_path.dentry->d_inode;

ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
if (ret != 0)
goto out;
mutex_lock(&inode->i_mutex);
ret = nfs_file_fsync_commit(file, start, end, datasync);
mutex_unlock(&inode->i_mutex);
out:
do {
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
if (ret != 0)
break;
mutex_lock(&inode->i_mutex);
ret = nfs_file_fsync_commit(file, start, end, datasync);
mutex_unlock(&inode->i_mutex);
} while (ret == -EAGAIN);

return ret;
}

Expand Down
22 changes: 12 additions & 10 deletions fs/nfs/nfs4file.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,18 @@ nfs4_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
int ret;
struct inode *inode = file->f_path.dentry->d_inode;

ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
if (ret != 0)
goto out;
mutex_lock(&inode->i_mutex);
ret = nfs_file_fsync_commit(file, start, end, datasync);
if (!ret && !datasync)
/* application has asked for meta-data sync */
ret = pnfs_layoutcommit_inode(inode, true);
mutex_unlock(&inode->i_mutex);
out:
do {
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
if (ret != 0)
break;
mutex_lock(&inode->i_mutex);
ret = nfs_file_fsync_commit(file, start, end, datasync);
if (!ret && !datasync)
/* application has asked for meta-data sync */
ret = pnfs_layoutcommit_inode(inode, true);
mutex_unlock(&inode->i_mutex);
} while (ret == -EAGAIN);

return ret;
}

Expand Down
1 change: 1 addition & 0 deletions fs/nfs/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -1580,6 +1580,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)
/* We have a mismatch. Write the page again */
dprintk(" mismatch\n");
nfs_mark_request_dirty(req);
set_bit(NFS_CONTEXT_RESEND_WRITES, &req->wb_context->flags);
next:
nfs_unlock_and_release_request(req);
}
Expand Down
1 change: 1 addition & 0 deletions include/linux/nfs_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ struct nfs_open_context {

unsigned long flags;
#define NFS_CONTEXT_ERROR_WRITE (0)
#define NFS_CONTEXT_RESEND_WRITES (1)
int error;

struct list_head list;
Expand Down

0 comments on commit 05990d1

Please sign in to comment.