aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <trond.myklebust@primarydata.com>2015-02-26 17:36:09 -0500
committerTrond Myklebust <trond.myklebust@primarydata.com>2015-03-01 23:23:06 -0500
commita08a8cd375db9769588257e7782f6b6b68561b88 (patch)
treed8546485f16b6ffc2c1d15a2009d384fc3f488ef
parentf5062003465c20cfe584d9129a463322ad5cf4ea (diff)
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>
-rw-r--r--fs/nfs/inode.c25
-rw-r--r--fs/nfs/internal.h1
-rw-r--r--fs/nfs/nfs3proc.c2
-rw-r--r--fs/nfs/nfs4proc.c2
-rw-r--r--fs/nfs/proc.c4
-rw-r--r--fs/nfs/write.c30
-rw-r--r--include/linux/nfs_fs.h1
7 files changed, 57 insertions, 8 deletions
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 299bf7171a4d..ff9a6795da46 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1491,7 +1491,7 @@ int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr)
1491EXPORT_SYMBOL_GPL(nfs_post_op_update_inode); 1491EXPORT_SYMBOL_GPL(nfs_post_op_update_inode);
1492 1492
1493/** 1493/**
1494 * nfs_post_op_update_inode_force_wcc - try to update the inode attribute cache 1494 * nfs_post_op_update_inode_force_wcc_locked - update the inode attribute cache
1495 * @inode - pointer to inode 1495 * @inode - pointer to inode
1496 * @fattr - updated attributes 1496 * @fattr - updated attributes
1497 * 1497 *
@@ -1501,11 +1501,10 @@ EXPORT_SYMBOL_GPL(nfs_post_op_update_inode);
1501 * 1501 *
1502 * This function is mainly designed to be used by the ->write_done() functions. 1502 * This function is mainly designed to be used by the ->write_done() functions.
1503 */ 1503 */
1504int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr) 1504int nfs_post_op_update_inode_force_wcc_locked(struct inode *inode, struct nfs_fattr *fattr)
1505{ 1505{
1506 int status; 1506 int status;
1507 1507
1508 spin_lock(&inode->i_lock);
1509 /* Don't do a WCC update if these attributes are already stale */ 1508 /* Don't do a WCC update if these attributes are already stale */
1510 if ((fattr->valid & NFS_ATTR_FATTR) == 0 || 1509 if ((fattr->valid & NFS_ATTR_FATTR) == 0 ||
1511 !nfs_inode_attrs_need_update(inode, fattr)) { 1510 !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
1537 } 1536 }
1538out_noforce: 1537out_noforce:
1539 status = nfs_post_op_update_inode_locked(inode, fattr); 1538 status = nfs_post_op_update_inode_locked(inode, fattr);
1539 return status;
1540}
1541
1542/**
1543 * nfs_post_op_update_inode_force_wcc - try to update the inode attribute cache
1544 * @inode - pointer to inode
1545 * @fattr - updated attributes
1546 *
1547 * After an operation that has changed the inode metadata, mark the
1548 * attribute cache as being invalid, then try to update it. Fake up
1549 * weak cache consistency data, if none exist.
1550 *
1551 * This function is mainly designed to be used by the ->write_done() functions.
1552 */
1553int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr)
1554{
1555 int status;
1556
1557 spin_lock(&inode->i_lock);
1558 status = nfs_post_op_update_inode_force_wcc_locked(inode, fattr);
1540 spin_unlock(&inode->i_lock); 1559 spin_unlock(&inode->i_lock);
1541 return status; 1560 return status;
1542} 1561}
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index b802fb3a2d99..9e6475bc5ba2 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -459,6 +459,7 @@ void nfs_mark_request_commit(struct nfs_page *req,
459 struct nfs_commit_info *cinfo, 459 struct nfs_commit_info *cinfo,
460 u32 ds_commit_idx); 460 u32 ds_commit_idx);
461int nfs_write_need_commit(struct nfs_pgio_header *); 461int nfs_write_need_commit(struct nfs_pgio_header *);
462void nfs_writeback_update_inode(struct nfs_pgio_header *hdr);
462int nfs_generic_commit_list(struct inode *inode, struct list_head *head, 463int nfs_generic_commit_list(struct inode *inode, struct list_head *head,
463 int how, struct nfs_commit_info *cinfo); 464 int how, struct nfs_commit_info *cinfo);
464void nfs_retry_commit(struct list_head *page_list, 465void nfs_retry_commit(struct list_head *page_list,
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index 11109a137c0c..1f11d2533ee4 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -834,7 +834,7 @@ static int nfs3_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
834 if (nfs3_async_handle_jukebox(task, inode)) 834 if (nfs3_async_handle_jukebox(task, inode))
835 return -EAGAIN; 835 return -EAGAIN;
836 if (task->tk_status >= 0) 836 if (task->tk_status >= 0)
837 nfs_post_op_update_inode_force_wcc(inode, hdr->res.fattr); 837 nfs_writeback_update_inode(hdr);
838 return 0; 838 return 0;
839} 839}
840 840
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index c499e02a58ca..b022e64b76a5 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -4237,7 +4237,7 @@ static int nfs4_write_done_cb(struct rpc_task *task,
4237 } 4237 }
4238 if (task->tk_status >= 0) { 4238 if (task->tk_status >= 0) {
4239 renew_lease(NFS_SERVER(inode), hdr->timestamp); 4239 renew_lease(NFS_SERVER(inode), hdr->timestamp);
4240 nfs_post_op_update_inode_force_wcc(inode, &hdr->fattr); 4240 nfs_writeback_update_inode(hdr);
4241 } 4241 }
4242 return 0; 4242 return 0;
4243} 4243}
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index 6202bc0f11bb..c63189acd052 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -609,10 +609,8 @@ static int nfs_proc_pgio_rpc_prepare(struct rpc_task *task,
609 609
610static int nfs_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr) 610static int nfs_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
611{ 611{
612 struct inode *inode = hdr->inode;
613
614 if (task->tk_status >= 0) 612 if (task->tk_status >= 0)
615 nfs_post_op_update_inode_force_wcc(inode, hdr->res.fattr); 613 nfs_writeback_update_inode(hdr);
616 return 0; 614 return 0;
617} 615}
618 616
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 595d81e354d1..849ed784d6ac 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -1377,6 +1377,36 @@ static int nfs_should_remove_suid(const struct inode *inode)
1377 return 0; 1377 return 0;
1378} 1378}
1379 1379
1380static void nfs_writeback_check_extend(struct nfs_pgio_header *hdr,
1381 struct nfs_fattr *fattr)
1382{
1383 struct nfs_pgio_args *argp = &hdr->args;
1384 struct nfs_pgio_res *resp = &hdr->res;
1385
1386 if (!(fattr->valid & NFS_ATTR_FATTR_SIZE))
1387 return;
1388 if (argp->offset + resp->count != fattr->size)
1389 return;
1390 if (nfs_size_to_loff_t(fattr->size) < i_size_read(hdr->inode))
1391 return;
1392 /* Set attribute barrier */
1393 nfs_fattr_set_barrier(fattr);
1394}
1395
1396void nfs_writeback_update_inode(struct nfs_pgio_header *hdr)
1397{
1398 struct nfs_fattr *fattr = hdr->res.fattr;
1399 struct inode *inode = hdr->inode;
1400
1401 if (fattr == NULL)
1402 return;
1403 spin_lock(&inode->i_lock);
1404 nfs_writeback_check_extend(hdr, fattr);
1405 nfs_post_op_update_inode_force_wcc_locked(inode, fattr);
1406 spin_unlock(&inode->i_lock);
1407}
1408EXPORT_SYMBOL_GPL(nfs_writeback_update_inode);
1409
1380/* 1410/*
1381 * This function is called when the WRITE call is complete. 1411 * This function is called when the WRITE call is complete.
1382 */ 1412 */
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index f26e64e0aff8..59b1516b9fd4 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -343,6 +343,7 @@ extern struct inode *nfs_fhget(struct super_block *, struct nfs_fh *,
343extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *); 343extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
344extern int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr); 344extern int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr);
345extern int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr); 345extern int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr);
346extern int nfs_post_op_update_inode_force_wcc_locked(struct inode *inode, struct nfs_fattr *fattr);
346extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *); 347extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
347extern void nfs_access_add_cache(struct inode *, struct nfs_access_entry *); 348extern void nfs_access_add_cache(struct inode *, struct nfs_access_entry *);
348extern void nfs_access_set_mask(struct nfs_access_entry *, u32); 349extern void nfs_access_set_mask(struct nfs_access_entry *, u32);