summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2021-04-16 14:00:16 -0400
committerChuck Lever <chuck.lever@oracle.com>2021-04-19 16:41:36 -0400
commita0ce48375a367222989c2618fe68bf34db8c7bb7 (patch)
tree3113a0c813c596d7a4c23b23b04c91cabb92e10c
parentf9b60e2209213fdfcc504ba25a404977c5d08b77 (diff)
downloadlinux-a0ce48375a367222989c2618fe68bf34db8c7bb7.tar.gz
linux-a0ce48375a367222989c2618fe68bf34db8c7bb7.tar.bz2
linux-a0ce48375a367222989c2618fe68bf34db8c7bb7.zip
nfsd: track filehandle aliasing in nfs4_files
It's unusual but possible for multiple filehandles to point to the same file. In that case, we may end up with multiple nfs4_files referencing the same inode. For delegation purposes it will turn out to be useful to flag those cases. Signed-off-by: J. Bruce Fields <bfields@redhat.com> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
-rw-r--r--fs/nfsd/nfs4state.c37
-rw-r--r--fs/nfsd/state.h2
2 files changed, 30 insertions, 9 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 442034633ee8..b12f530f6449 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4086,6 +4086,8 @@ static void nfsd4_init_file(struct svc_fh *fh, unsigned int hashval,
fp->fi_share_deny = 0;
memset(fp->fi_fds, 0, sizeof(fp->fi_fds));
memset(fp->fi_access, 0, sizeof(fp->fi_access));
+ fp->fi_aliased = false;
+ fp->fi_inode = d_inode(fh->fh_dentry);
#ifdef CONFIG_NFSD_PNFS
INIT_LIST_HEAD(&fp->fi_lo_states);
atomic_set(&fp->fi_lo_recalls, 0);
@@ -4438,6 +4440,31 @@ find_file_locked(struct svc_fh *fh, unsigned int hashval)
return NULL;
}
+static struct nfs4_file *insert_file(struct nfs4_file *new, struct svc_fh *fh,
+ unsigned int hashval)
+{
+ struct nfs4_file *fp;
+ struct nfs4_file *ret = NULL;
+ bool alias_found = false;
+
+ spin_lock(&state_lock);
+ hlist_for_each_entry_rcu(fp, &file_hashtbl[hashval], fi_hash,
+ lockdep_is_held(&state_lock)) {
+ if (fh_match(&fp->fi_fhandle, &fh->fh_handle)) {
+ if (refcount_inc_not_zero(&fp->fi_ref))
+ ret = fp;
+ } else if (d_inode(fh->fh_dentry) == fp->fi_inode)
+ fp->fi_aliased = alias_found = true;
+ }
+ if (likely(ret == NULL)) {
+ nfsd4_init_file(fh, hashval, new);
+ new->fi_aliased = alias_found;
+ ret = new;
+ }
+ spin_unlock(&state_lock);
+ return ret;
+}
+
static struct nfs4_file * find_file(struct svc_fh *fh)
{
struct nfs4_file *fp;
@@ -4461,15 +4488,7 @@ find_or_add_file(struct nfs4_file *new, struct svc_fh *fh)
if (fp)
return fp;
- spin_lock(&state_lock);
- fp = find_file_locked(fh, hashval);
- if (likely(fp == NULL)) {
- nfsd4_init_file(fh, hashval, new);
- fp = new;
- }
- spin_unlock(&state_lock);
-
- return fp;
+ return insert_file(new, fh, hashval);
}
/*
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 61a2d95d7923..e73bdbb1634a 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -516,6 +516,8 @@ struct nfs4_clnt_odstate {
*/
struct nfs4_file {
refcount_t fi_ref;
+ struct inode * fi_inode;
+ bool fi_aliased;
spinlock_t fi_lock;
struct hlist_node fi_hash; /* hash on fi_fhandle */
struct list_head fi_stateids;