mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
nfs: set gencount on protocol-specific remove
commitbd4928ec79("NFS: Avoid changing nlink when file removes and attribute updates race") avoids races on attribute cache with any inflight RPC that may modify inode attributes (and gencount) during i.e. REMOVE. However it does not take into account that REMOVE may trigger a delegation return which also advances gencount (via the attribute refresh during delegation return), and in that case the gencount mismatch is not due to an inflight RPC that already reflected the removal. Push the setting of gencount under each version-specific layer, reordering correctly for nfsv4 case after the delegation returns. This fixes LTP/inotify04 failure afterbd4928ec79. Fixes:bd4928ec79("NFS: Avoid changing nlink when file removes and attribute updates race") Signed-off-by: Roberto Bergantinos Corpas <rbergant@redhat.com> Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
This commit is contained in:
parent
0b5bb4e586
commit
8c955dacf4
13
fs/nfs/dir.c
13
fs/nfs/dir.c
|
|
@ -1935,7 +1935,7 @@ static int nfs_dentry_delete(const struct dentry *dentry)
|
|||
}
|
||||
|
||||
/* Ensure that we revalidate inode->i_nlink */
|
||||
static void nfs_drop_nlink(struct inode *inode, unsigned long gencount)
|
||||
void nfs_drop_nlink(struct inode *inode, unsigned long gencount)
|
||||
{
|
||||
struct nfs_inode *nfsi = NFS_I(inode);
|
||||
|
||||
|
|
@ -1949,6 +1949,7 @@ static void nfs_drop_nlink(struct inode *inode, unsigned long gencount)
|
|||
NFS_INO_INVALID_NLINK);
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_drop_nlink);
|
||||
|
||||
/*
|
||||
* Called when the dentry loses inode.
|
||||
|
|
@ -2554,7 +2555,6 @@ EXPORT_SYMBOL_GPL(nfs_rmdir);
|
|||
static int nfs_safe_remove(struct dentry *dentry)
|
||||
{
|
||||
struct inode *dir = d_inode(dentry->d_parent);
|
||||
struct inode *inode = d_inode(dentry);
|
||||
int error = -EBUSY;
|
||||
|
||||
dfprintk(VFS, "NFS: safe_remove(%pd2)\n", dentry);
|
||||
|
|
@ -2566,14 +2566,7 @@ static int nfs_safe_remove(struct dentry *dentry)
|
|||
}
|
||||
|
||||
trace_nfs_remove_enter(dir, dentry);
|
||||
if (inode != NULL) {
|
||||
unsigned long gencount = READ_ONCE(NFS_I(inode)->attr_gencount);
|
||||
|
||||
error = NFS_PROTO(dir)->remove(dir, dentry);
|
||||
if (error == 0)
|
||||
nfs_drop_nlink(inode, gencount);
|
||||
} else
|
||||
error = NFS_PROTO(dir)->remove(dir, dentry);
|
||||
error = NFS_PROTO(dir)->remove(dir, dentry);
|
||||
if (error == -ENOENT)
|
||||
nfs_dentry_handle_enoent(dentry);
|
||||
trace_nfs_remove_exit(dir, dentry, error);
|
||||
|
|
|
|||
|
|
@ -395,6 +395,7 @@ extern unsigned long nfs_access_cache_count(struct shrinker *shrink,
|
|||
struct shrink_control *sc);
|
||||
extern unsigned long nfs_access_cache_scan(struct shrinker *shrink,
|
||||
struct shrink_control *sc);
|
||||
extern void nfs_drop_nlink(struct inode *inode, unsigned long gencount);
|
||||
struct dentry *nfs_lookup(struct inode *, struct dentry *, unsigned int);
|
||||
void nfs_d_prune_case_insensitive_aliases(struct inode *inode);
|
||||
int nfs_create(struct mnt_idmap *, struct inode *, struct dentry *,
|
||||
|
|
|
|||
|
|
@ -448,13 +448,21 @@ nfs3_proc_remove(struct inode *dir, struct dentry *dentry)
|
|||
.rpc_resp = &res,
|
||||
};
|
||||
int status = -ENOMEM;
|
||||
struct inode *inode = d_inode(dentry);
|
||||
unsigned long gencount;
|
||||
|
||||
dprintk("NFS call remove %pd2\n", dentry);
|
||||
res.dir_attr = nfs_alloc_fattr();
|
||||
if (res.dir_attr == NULL)
|
||||
goto out;
|
||||
|
||||
if (inode)
|
||||
gencount = READ_ONCE(NFS_I(inode)->attr_gencount);
|
||||
|
||||
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
|
||||
if (status == 0 && inode)
|
||||
nfs_drop_nlink(inode, gencount);
|
||||
|
||||
nfs_post_op_update_inode(dir, res.dir_attr);
|
||||
nfs_free_fattr(res.dir_attr);
|
||||
out:
|
||||
|
|
|
|||
|
|
@ -4969,12 +4969,14 @@ static int nfs4_proc_remove(struct inode *dir, struct dentry *dentry)
|
|||
};
|
||||
struct inode *inode = d_inode(dentry);
|
||||
int err;
|
||||
unsigned long gencount;
|
||||
|
||||
if (inode) {
|
||||
if (inode->i_nlink == 1)
|
||||
nfs4_inode_return_delegation(inode);
|
||||
else
|
||||
nfs4_inode_make_writeable(inode);
|
||||
gencount = READ_ONCE(NFS_I(inode)->attr_gencount);
|
||||
}
|
||||
do {
|
||||
err = _nfs4_proc_remove(dir, &dentry->d_name, NF4REG);
|
||||
|
|
@ -4982,6 +4984,9 @@ static int nfs4_proc_remove(struct inode *dir, struct dentry *dentry)
|
|||
err = nfs4_handle_exception(NFS_SERVER(dir), err,
|
||||
&exception);
|
||||
} while (exception.retry);
|
||||
|
||||
if (err == 0 && inode)
|
||||
nfs_drop_nlink(inode, gencount);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -323,9 +323,17 @@ nfs_proc_remove(struct inode *dir, struct dentry *dentry)
|
|||
.rpc_argp = &arg,
|
||||
};
|
||||
int status;
|
||||
struct inode *inode = d_inode(dentry);
|
||||
unsigned long gencount;
|
||||
|
||||
dprintk("NFS call remove %pd2\n",dentry);
|
||||
if (inode)
|
||||
gencount = READ_ONCE(NFS_I(inode)->attr_gencount);
|
||||
|
||||
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
|
||||
if (status == 0 && inode)
|
||||
nfs_drop_nlink(inode, gencount);
|
||||
|
||||
nfs_mark_for_revalidate(dir);
|
||||
|
||||
dprintk("NFS reply remove: %d\n", status);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user