mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 13:55:32 +00:00
NFS: Add attribute update barriers to NFS writebacks
Ensure that other operations that race with our write RPC calls cannot revert the file size updates that were made on the server. Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com> Tested-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
parent
f506200346
commit
a08a8cd375
7 changed files with 57 additions and 8 deletions
|
@ -1491,7 +1491,7 @@ int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
|||
EXPORT_SYMBOL_GPL(nfs_post_op_update_inode);
|
||||
|
||||
/**
|
||||
* nfs_post_op_update_inode_force_wcc - try to update the inode attribute cache
|
||||
* nfs_post_op_update_inode_force_wcc_locked - update the inode attribute cache
|
||||
* @inode - pointer to inode
|
||||
* @fattr - updated attributes
|
||||
*
|
||||
|
@ -1501,11 +1501,10 @@ EXPORT_SYMBOL_GPL(nfs_post_op_update_inode);
|
|||
*
|
||||
* This function is mainly designed to be used by the ->write_done() functions.
|
||||
*/
|
||||
int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr)
|
||||
int nfs_post_op_update_inode_force_wcc_locked(struct inode *inode, struct nfs_fattr *fattr)
|
||||
{
|
||||
int status;
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
/* Don't do a WCC update if these attributes are already stale */
|
||||
if ((fattr->valid & NFS_ATTR_FATTR) == 0 ||
|
||||
!nfs_inode_attrs_need_update(inode, fattr)) {
|
||||
|
@ -1537,6 +1536,26 @@ int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fa
|
|||
}
|
||||
out_noforce:
|
||||
status = nfs_post_op_update_inode_locked(inode, fattr);
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_post_op_update_inode_force_wcc - try to update the inode attribute cache
|
||||
* @inode - pointer to inode
|
||||
* @fattr - updated attributes
|
||||
*
|
||||
* After an operation that has changed the inode metadata, mark the
|
||||
* attribute cache as being invalid, then try to update it. Fake up
|
||||
* weak cache consistency data, if none exist.
|
||||
*
|
||||
* This function is mainly designed to be used by the ->write_done() functions.
|
||||
*/
|
||||
int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr)
|
||||
{
|
||||
int status;
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
status = nfs_post_op_update_inode_force_wcc_locked(inode, fattr);
|
||||
spin_unlock(&inode->i_lock);
|
||||
return status;
|
||||
}
|
||||
|
|
|
@ -459,6 +459,7 @@ void nfs_mark_request_commit(struct nfs_page *req,
|
|||
struct nfs_commit_info *cinfo,
|
||||
u32 ds_commit_idx);
|
||||
int nfs_write_need_commit(struct nfs_pgio_header *);
|
||||
void nfs_writeback_update_inode(struct nfs_pgio_header *hdr);
|
||||
int nfs_generic_commit_list(struct inode *inode, struct list_head *head,
|
||||
int how, struct nfs_commit_info *cinfo);
|
||||
void nfs_retry_commit(struct list_head *page_list,
|
||||
|
|
|
@ -834,7 +834,7 @@ static int nfs3_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
|
|||
if (nfs3_async_handle_jukebox(task, inode))
|
||||
return -EAGAIN;
|
||||
if (task->tk_status >= 0)
|
||||
nfs_post_op_update_inode_force_wcc(inode, hdr->res.fattr);
|
||||
nfs_writeback_update_inode(hdr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -4237,7 +4237,7 @@ static int nfs4_write_done_cb(struct rpc_task *task,
|
|||
}
|
||||
if (task->tk_status >= 0) {
|
||||
renew_lease(NFS_SERVER(inode), hdr->timestamp);
|
||||
nfs_post_op_update_inode_force_wcc(inode, &hdr->fattr);
|
||||
nfs_writeback_update_inode(hdr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -609,10 +609,8 @@ static int nfs_proc_pgio_rpc_prepare(struct rpc_task *task,
|
|||
|
||||
static int nfs_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
|
||||
{
|
||||
struct inode *inode = hdr->inode;
|
||||
|
||||
if (task->tk_status >= 0)
|
||||
nfs_post_op_update_inode_force_wcc(inode, hdr->res.fattr);
|
||||
nfs_writeback_update_inode(hdr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1377,6 +1377,36 @@ static int nfs_should_remove_suid(const struct inode *inode)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void nfs_writeback_check_extend(struct nfs_pgio_header *hdr,
|
||||
struct nfs_fattr *fattr)
|
||||
{
|
||||
struct nfs_pgio_args *argp = &hdr->args;
|
||||
struct nfs_pgio_res *resp = &hdr->res;
|
||||
|
||||
if (!(fattr->valid & NFS_ATTR_FATTR_SIZE))
|
||||
return;
|
||||
if (argp->offset + resp->count != fattr->size)
|
||||
return;
|
||||
if (nfs_size_to_loff_t(fattr->size) < i_size_read(hdr->inode))
|
||||
return;
|
||||
/* Set attribute barrier */
|
||||
nfs_fattr_set_barrier(fattr);
|
||||
}
|
||||
|
||||
void nfs_writeback_update_inode(struct nfs_pgio_header *hdr)
|
||||
{
|
||||
struct nfs_fattr *fattr = hdr->res.fattr;
|
||||
struct inode *inode = hdr->inode;
|
||||
|
||||
if (fattr == NULL)
|
||||
return;
|
||||
spin_lock(&inode->i_lock);
|
||||
nfs_writeback_check_extend(hdr, fattr);
|
||||
nfs_post_op_update_inode_force_wcc_locked(inode, fattr);
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_writeback_update_inode);
|
||||
|
||||
/*
|
||||
* This function is called when the WRITE call is complete.
|
||||
*/
|
||||
|
|
|
@ -343,6 +343,7 @@ extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *,
|
|||
extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
|
||||
extern int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr);
|
||||
extern int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr);
|
||||
extern int nfs_post_op_update_inode_force_wcc_locked(struct inode *inode, struct nfs_fattr *fattr);
|
||||
extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
|
||||
extern void nfs_access_add_cache(struct inode *, struct nfs_access_entry *);
|
||||
extern void nfs_access_set_mask(struct nfs_access_entry *, u32);
|
||||
|
|
Loading…
Reference in a new issue