aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorTrond Myklebust <trond.myklebust@primarydata.com>2015-02-26 16:09:04 -0500
committerTrond Myklebust <trond.myklebust@primarydata.com>2015-03-01 23:23:05 -0500
commitf044636d972246d451e06226cc1675d5da389762 (patch)
tree7fbfe0ffc4f5a5b3f9ccf56de8fe887c7394027c /fs/nfs
parent140e049c64ce848392adbf4678983ecc76888dde (diff)
NFS: Add attribute update barriers to nfs_setattr_update_inode()
Ensure that other operations which raced with our setattr RPC call cannot revert the file attribute changes that were made on the server. To do so, we artificially bump the attribute generation counter on the inode so that all calls to nfs_fattr_init() that precede ours will be dropped. The motivation for the patch came from Chuck Lever's reports of readaheads racing with truncate operations and causing the file size to be reverted. Reported-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com> Tested-by: Chuck Lever <chuck.lever@oracle.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/inode.c17
-rw-r--r--fs/nfs/nfs3proc.c2
-rw-r--r--fs/nfs/nfs4proc.c6
-rw-r--r--fs/nfs/proc.c2
4 files changed, 17 insertions, 10 deletions
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index b0cbc1ba82da..3a2d127de499 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -556,6 +556,7 @@ EXPORT_SYMBOL_GPL(nfs_setattr);
556 * This is a copy of the common vmtruncate, but with the locking 556 * This is a copy of the common vmtruncate, but with the locking
557 * corrected to take into account the fact that NFS requires 557 * corrected to take into account the fact that NFS requires
558 * inode->i_size to be updated under the inode->i_lock. 558 * inode->i_size to be updated under the inode->i_lock.
559 * Note: must be called with inode->i_lock held!
559 */ 560 */
560static int nfs_vmtruncate(struct inode * inode, loff_t offset) 561static int nfs_vmtruncate(struct inode * inode, loff_t offset)
561{ 562{
@@ -565,14 +566,14 @@ static int nfs_vmtruncate(struct inode * inode, loff_t offset)
565 if (err) 566 if (err)
566 goto out; 567 goto out;
567 568
568 spin_lock(&inode->i_lock);
569 i_size_write(inode, offset); 569 i_size_write(inode, offset);
570 /* Optimisation */ 570 /* Optimisation */
571 if (offset == 0) 571 if (offset == 0)
572 NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_DATA; 572 NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_DATA;
573 spin_unlock(&inode->i_lock);
574 573
574 spin_unlock(&inode->i_lock);
575 truncate_pagecache(inode, offset); 575 truncate_pagecache(inode, offset);
576 spin_lock(&inode->i_lock);
576out: 577out:
577 return err; 578 return err;
578} 579}
@@ -585,10 +586,15 @@ out:
585 * Note: we do this in the *proc.c in order to ensure that 586 * Note: we do this in the *proc.c in order to ensure that
586 * it works for things like exclusive creates too. 587 * it works for things like exclusive creates too.
587 */ 588 */
588void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr) 589void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr,
590 struct nfs_fattr *fattr)
589{ 591{
592 /* Barrier: bump the attribute generation count. */
593 nfs_fattr_set_barrier(fattr);
594
595 spin_lock(&inode->i_lock);
596 NFS_I(inode)->attr_gencount = fattr->gencount;
590 if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) { 597 if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) {
591 spin_lock(&inode->i_lock);
592 if ((attr->ia_valid & ATTR_MODE) != 0) { 598 if ((attr->ia_valid & ATTR_MODE) != 0) {
593 int mode = attr->ia_mode & S_IALLUGO; 599 int mode = attr->ia_mode & S_IALLUGO;
594 mode |= inode->i_mode & ~S_IALLUGO; 600 mode |= inode->i_mode & ~S_IALLUGO;
@@ -600,12 +606,13 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr)
600 inode->i_gid = attr->ia_gid; 606 inode->i_gid = attr->ia_gid;
601 nfs_set_cache_invalid(inode, NFS_INO_INVALID_ACCESS 607 nfs_set_cache_invalid(inode, NFS_INO_INVALID_ACCESS
602 | NFS_INO_INVALID_ACL); 608 | NFS_INO_INVALID_ACL);
603 spin_unlock(&inode->i_lock);
604 } 609 }
605 if ((attr->ia_valid & ATTR_SIZE) != 0) { 610 if ((attr->ia_valid & ATTR_SIZE) != 0) {
606 nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC); 611 nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC);
607 nfs_vmtruncate(inode, attr->ia_size); 612 nfs_vmtruncate(inode, attr->ia_size);
608 } 613 }
614 nfs_update_inode(inode, fattr);
615 spin_unlock(&inode->i_lock);
609} 616}
610EXPORT_SYMBOL_GPL(nfs_setattr_update_inode); 617EXPORT_SYMBOL_GPL(nfs_setattr_update_inode);
611 618
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index 78e557c3ab87..11109a137c0c 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -138,7 +138,7 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
138 nfs_fattr_init(fattr); 138 nfs_fattr_init(fattr);
139 status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); 139 status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
140 if (status == 0) 140 if (status == 0)
141 nfs_setattr_update_inode(inode, sattr); 141 nfs_setattr_update_inode(inode, sattr, fattr);
142 dprintk("NFS reply setattr: %d\n", status); 142 dprintk("NFS reply setattr: %d\n", status);
143 return status; 143 return status;
144} 144}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 4e41340e957d..c499e02a58ca 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2416,8 +2416,8 @@ static int _nfs4_do_open(struct inode *dir,
2416 opendata->o_res.f_attr, sattr, 2416 opendata->o_res.f_attr, sattr,
2417 state, label, olabel); 2417 state, label, olabel);
2418 if (status == 0) { 2418 if (status == 0) {
2419 nfs_setattr_update_inode(state->inode, sattr); 2419 nfs_setattr_update_inode(state->inode, sattr,
2420 nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr); 2420 opendata->o_res.f_attr);
2421 nfs_setsecurity(state->inode, opendata->o_res.f_attr, olabel); 2421 nfs_setsecurity(state->inode, opendata->o_res.f_attr, olabel);
2422 } 2422 }
2423 } 2423 }
@@ -3291,7 +3291,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
3291 3291
3292 status = nfs4_do_setattr(inode, cred, fattr, sattr, state, NULL, label); 3292 status = nfs4_do_setattr(inode, cred, fattr, sattr, state, NULL, label);
3293 if (status == 0) { 3293 if (status == 0) {
3294 nfs_setattr_update_inode(inode, sattr); 3294 nfs_setattr_update_inode(inode, sattr, fattr);
3295 nfs_setsecurity(inode, fattr, label); 3295 nfs_setsecurity(inode, fattr, label);
3296 } 3296 }
3297 nfs4_label_free(label); 3297 nfs4_label_free(label);
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index b09cc23d6f43..6202bc0f11bb 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -139,7 +139,7 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
139 nfs_fattr_init(fattr); 139 nfs_fattr_init(fattr);
140 status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0); 140 status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
141 if (status == 0) 141 if (status == 0)
142 nfs_setattr_update_inode(inode, sattr); 142 nfs_setattr_update_inode(inode, sattr, fattr);
143 dprintk("NFS reply setattr: %d\n", status); 143 dprintk("NFS reply setattr: %d\n", status);
144 return status; 144 return status;
145} 145}