mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 18:13:41 +02:00
NFS: Fix up directory verifier races
In order to avoid having our dentry revalidation race with an update of the directory on the server, we need to store the verifier before the RPC calls to LOOKUP and READDIR. Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com> Reviewed-by: Benjamin Coddington <bcodding@gmail.com> Tested-by: Benjamin Coddington <bcodding@gmail.com> Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
This commit is contained in:
parent
bb6d3fb354
commit
a1147b8281
21
fs/nfs/dir.c
21
fs/nfs/dir.c
|
|
@ -155,6 +155,7 @@ typedef struct {
|
||||||
loff_t current_index;
|
loff_t current_index;
|
||||||
decode_dirent_t decode;
|
decode_dirent_t decode;
|
||||||
|
|
||||||
|
unsigned long dir_verifier;
|
||||||
unsigned long timestamp;
|
unsigned long timestamp;
|
||||||
unsigned long gencount;
|
unsigned long gencount;
|
||||||
unsigned int cache_entry_index;
|
unsigned int cache_entry_index;
|
||||||
|
|
@ -353,6 +354,7 @@ int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc,
|
||||||
again:
|
again:
|
||||||
timestamp = jiffies;
|
timestamp = jiffies;
|
||||||
gencount = nfs_inc_attr_generation_counter();
|
gencount = nfs_inc_attr_generation_counter();
|
||||||
|
desc->dir_verifier = nfs_save_change_attribute(inode);
|
||||||
error = NFS_PROTO(inode)->readdir(file_dentry(file), cred, entry->cookie, pages,
|
error = NFS_PROTO(inode)->readdir(file_dentry(file), cred, entry->cookie, pages,
|
||||||
NFS_SERVER(inode)->dtsize, desc->plus);
|
NFS_SERVER(inode)->dtsize, desc->plus);
|
||||||
if (error < 0) {
|
if (error < 0) {
|
||||||
|
|
@ -455,13 +457,13 @@ void nfs_force_use_readdirplus(struct inode *dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
|
void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry,
|
||||||
|
unsigned long dir_verifier)
|
||||||
{
|
{
|
||||||
struct qstr filename = QSTR_INIT(entry->name, entry->len);
|
struct qstr filename = QSTR_INIT(entry->name, entry->len);
|
||||||
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
|
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
struct dentry *alias;
|
struct dentry *alias;
|
||||||
struct inode *dir = d_inode(parent);
|
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
|
|
@ -500,7 +502,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
|
||||||
if (nfs_same_file(dentry, entry)) {
|
if (nfs_same_file(dentry, entry)) {
|
||||||
if (!entry->fh->size)
|
if (!entry->fh->size)
|
||||||
goto out;
|
goto out;
|
||||||
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
nfs_set_verifier(dentry, dir_verifier);
|
||||||
status = nfs_refresh_inode(d_inode(dentry), entry->fattr);
|
status = nfs_refresh_inode(d_inode(dentry), entry->fattr);
|
||||||
if (!status)
|
if (!status)
|
||||||
nfs_setsecurity(d_inode(dentry), entry->fattr, entry->label);
|
nfs_setsecurity(d_inode(dentry), entry->fattr, entry->label);
|
||||||
|
|
@ -526,7 +528,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
dentry = alias;
|
dentry = alias;
|
||||||
}
|
}
|
||||||
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
nfs_set_verifier(dentry, dir_verifier);
|
||||||
out:
|
out:
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
}
|
}
|
||||||
|
|
@ -564,7 +566,8 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
if (desc->plus)
|
if (desc->plus)
|
||||||
nfs_prime_dcache(file_dentry(desc->file), entry);
|
nfs_prime_dcache(file_dentry(desc->file), entry,
|
||||||
|
desc->dir_verifier);
|
||||||
|
|
||||||
status = nfs_readdir_add_to_array(entry, page);
|
status = nfs_readdir_add_to_array(entry, page);
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
|
|
@ -1159,6 +1162,7 @@ nfs_lookup_revalidate_dentry(struct inode *dir, struct dentry *dentry,
|
||||||
struct nfs_fh *fhandle;
|
struct nfs_fh *fhandle;
|
||||||
struct nfs_fattr *fattr;
|
struct nfs_fattr *fattr;
|
||||||
struct nfs4_label *label;
|
struct nfs4_label *label;
|
||||||
|
unsigned long dir_verifier;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
|
|
@ -1168,6 +1172,7 @@ nfs_lookup_revalidate_dentry(struct inode *dir, struct dentry *dentry,
|
||||||
if (fhandle == NULL || fattr == NULL || IS_ERR(label))
|
if (fhandle == NULL || fattr == NULL || IS_ERR(label))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
dir_verifier = nfs_save_change_attribute(dir);
|
||||||
ret = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr, label);
|
ret = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr, label);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
|
|
@ -1188,7 +1193,7 @@ nfs_lookup_revalidate_dentry(struct inode *dir, struct dentry *dentry,
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
nfs_setsecurity(inode, fattr, label);
|
nfs_setsecurity(inode, fattr, label);
|
||||||
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
nfs_set_verifier(dentry, dir_verifier);
|
||||||
|
|
||||||
/* set a readdirplus hint that we had a cache miss */
|
/* set a readdirplus hint that we had a cache miss */
|
||||||
nfs_force_use_readdirplus(dir);
|
nfs_force_use_readdirplus(dir);
|
||||||
|
|
@ -1415,6 +1420,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
|
||||||
struct nfs_fh *fhandle = NULL;
|
struct nfs_fh *fhandle = NULL;
|
||||||
struct nfs_fattr *fattr = NULL;
|
struct nfs_fattr *fattr = NULL;
|
||||||
struct nfs4_label *label = NULL;
|
struct nfs4_label *label = NULL;
|
||||||
|
unsigned long dir_verifier;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
dfprintk(VFS, "NFS: lookup(%pd2)\n", dentry);
|
dfprintk(VFS, "NFS: lookup(%pd2)\n", dentry);
|
||||||
|
|
@ -1440,6 +1446,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
|
||||||
if (IS_ERR(label))
|
if (IS_ERR(label))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
dir_verifier = nfs_save_change_attribute(dir);
|
||||||
trace_nfs_lookup_enter(dir, dentry, flags);
|
trace_nfs_lookup_enter(dir, dentry, flags);
|
||||||
error = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr, label);
|
error = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr, label);
|
||||||
if (error == -ENOENT)
|
if (error == -ENOENT)
|
||||||
|
|
@ -1463,7 +1470,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
|
||||||
goto out_label;
|
goto out_label;
|
||||||
dentry = res;
|
dentry = res;
|
||||||
}
|
}
|
||||||
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
nfs_set_verifier(dentry, dir_verifier);
|
||||||
out_label:
|
out_label:
|
||||||
trace_nfs_lookup_exit(dir, dentry, flags, error);
|
trace_nfs_lookup_exit(dir, dentry, flags, error);
|
||||||
nfs4_label_free(label);
|
nfs4_label_free(label);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user