forked from Minki/linux
NFS: Fix fdatasync/fsync() when confronted with a server reboot
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>
This commit is contained in:
parent
795a88c968
commit
05990d1bf2
@ -259,7 +259,7 @@ nfs_file_fsync_commit(struct file *file, loff_t start, loff_t end, int datasync)
|
|||||||
struct dentry *dentry = file->f_path.dentry;
|
struct dentry *dentry = file->f_path.dentry;
|
||||||
struct nfs_open_context *ctx = nfs_file_open_context(file);
|
struct nfs_open_context *ctx = nfs_file_open_context(file);
|
||||||
struct inode *inode = dentry->d_inode;
|
struct inode *inode = dentry->d_inode;
|
||||||
int have_error, status;
|
int have_error, do_resend, status;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
dprintk("NFS: fsync file(%s/%s) datasync %d\n",
|
dprintk("NFS: fsync file(%s/%s) datasync %d\n",
|
||||||
@ -267,15 +267,23 @@ nfs_file_fsync_commit(struct file *file, loff_t start, loff_t end, int datasync)
|
|||||||
datasync);
|
datasync);
|
||||||
|
|
||||||
nfs_inc_stats(inode, NFSIOS_VFSFSYNC);
|
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);
|
have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
|
||||||
status = nfs_commit_inode(inode, FLUSH_SYNC);
|
status = nfs_commit_inode(inode, FLUSH_SYNC);
|
||||||
if (status >= 0 && ret < 0)
|
|
||||||
status = ret;
|
|
||||||
have_error |= test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
|
have_error |= test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
|
||||||
if (have_error)
|
if (have_error) {
|
||||||
ret = xchg(&ctx->error, 0);
|
ret = xchg(&ctx->error, 0);
|
||||||
if (!ret && status < 0)
|
if (ret)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (status < 0) {
|
||||||
ret = status;
|
ret = status;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
do_resend |= test_bit(NFS_CONTEXT_RESEND_WRITES, &ctx->flags);
|
||||||
|
if (do_resend)
|
||||||
|
ret = -EAGAIN;
|
||||||
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(nfs_file_fsync_commit);
|
EXPORT_SYMBOL_GPL(nfs_file_fsync_commit);
|
||||||
@ -286,13 +294,15 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
|||||||
int ret;
|
int ret;
|
||||||
struct inode *inode = file->f_path.dentry->d_inode;
|
struct inode *inode = file->f_path.dentry->d_inode;
|
||||||
|
|
||||||
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
do {
|
||||||
if (ret != 0)
|
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
||||||
goto out;
|
if (ret != 0)
|
||||||
mutex_lock(&inode->i_mutex);
|
break;
|
||||||
ret = nfs_file_fsync_commit(file, start, end, datasync);
|
mutex_lock(&inode->i_mutex);
|
||||||
mutex_unlock(&inode->i_mutex);
|
ret = nfs_file_fsync_commit(file, start, end, datasync);
|
||||||
out:
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
} while (ret == -EAGAIN);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,16 +95,18 @@ nfs4_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
|||||||
int ret;
|
int ret;
|
||||||
struct inode *inode = file->f_path.dentry->d_inode;
|
struct inode *inode = file->f_path.dentry->d_inode;
|
||||||
|
|
||||||
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
do {
|
||||||
if (ret != 0)
|
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
||||||
goto out;
|
if (ret != 0)
|
||||||
mutex_lock(&inode->i_mutex);
|
break;
|
||||||
ret = nfs_file_fsync_commit(file, start, end, datasync);
|
mutex_lock(&inode->i_mutex);
|
||||||
if (!ret && !datasync)
|
ret = nfs_file_fsync_commit(file, start, end, datasync);
|
||||||
/* application has asked for meta-data sync */
|
if (!ret && !datasync)
|
||||||
ret = pnfs_layoutcommit_inode(inode, true);
|
/* application has asked for meta-data sync */
|
||||||
mutex_unlock(&inode->i_mutex);
|
ret = pnfs_layoutcommit_inode(inode, true);
|
||||||
out:
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
} while (ret == -EAGAIN);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1580,6 +1580,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)
|
|||||||
/* We have a mismatch. Write the page again */
|
/* We have a mismatch. Write the page again */
|
||||||
dprintk(" mismatch\n");
|
dprintk(" mismatch\n");
|
||||||
nfs_mark_request_dirty(req);
|
nfs_mark_request_dirty(req);
|
||||||
|
set_bit(NFS_CONTEXT_RESEND_WRITES, &req->wb_context->flags);
|
||||||
next:
|
next:
|
||||||
nfs_unlock_and_release_request(req);
|
nfs_unlock_and_release_request(req);
|
||||||
}
|
}
|
||||||
|
@ -103,6 +103,7 @@ struct nfs_open_context {
|
|||||||
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
#define NFS_CONTEXT_ERROR_WRITE (0)
|
#define NFS_CONTEXT_ERROR_WRITE (0)
|
||||||
|
#define NFS_CONTEXT_RESEND_WRITES (1)
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
Loading…
Reference in New Issue
Block a user