NFSD: Set up an rhashtable for the filecache
authorChuck Lever <chuck.lever@oracle.com>
Fri, 8 Jul 2022 18:26:23 +0000 (14:26 -0400)
committerChuck Lever <chuck.lever@oracle.com>
Sat, 30 Jul 2022 00:12:02 +0000 (20:12 -0400)
Add code to initialize and tear down an rhashtable. The rhashtable
is not used yet.

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
fs/nfsd/filecache.c
fs/nfsd/filecache.h

index 433448b..a5ae5b4 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/fsnotify_backend.h>
 #include <linux/fsnotify.h>
 #include <linux/seq_file.h>
+#include <linux/rhashtable.h>
 
 #include "vfs.h"
 #include "nfsd.h"
@@ -63,6 +64,136 @@ static unsigned long                        nfsd_file_flags;
 static struct fsnotify_group           *nfsd_file_fsnotify_group;
 static atomic_long_t                   nfsd_filecache_count;
 static struct delayed_work             nfsd_filecache_laundrette;
+static struct rhashtable               nfsd_file_rhash_tbl
+                                               ____cacheline_aligned_in_smp;
+
+enum nfsd_file_lookup_type {
+       NFSD_FILE_KEY_INODE,
+       NFSD_FILE_KEY_FULL,
+};
+
+struct nfsd_file_lookup_key {
+       struct inode                    *inode;
+       struct net                      *net;
+       const struct cred               *cred;
+       unsigned char                   need;
+       enum nfsd_file_lookup_type      type;
+};
+
+/*
+ * The returned hash value is based solely on the address of an in-code
+ * inode, a pointer to a slab-allocated object. The entropy in such a
+ * pointer is concentrated in its middle bits.
+ */
+static u32 nfsd_file_inode_hash(const struct inode *inode, u32 seed)
+{
+       unsigned long ptr = (unsigned long)inode;
+       u32 k;
+
+       k = ptr >> L1_CACHE_SHIFT;
+       k &= 0x00ffffff;
+       return jhash2(&k, 1, seed);
+}
+
+/**
+ * nfsd_file_key_hashfn - Compute the hash value of a lookup key
+ * @data: key on which to compute the hash value
+ * @len: rhash table's key_len parameter (unused)
+ * @seed: rhash table's random seed of the day
+ *
+ * Return value:
+ *   Computed 32-bit hash value
+ */
+static u32 nfsd_file_key_hashfn(const void *data, u32 len, u32 seed)
+{
+       const struct nfsd_file_lookup_key *key = data;
+
+       return nfsd_file_inode_hash(key->inode, seed);
+}
+
+/**
+ * nfsd_file_obj_hashfn - Compute the hash value of an nfsd_file
+ * @data: object on which to compute the hash value
+ * @len: rhash table's key_len parameter (unused)
+ * @seed: rhash table's random seed of the day
+ *
+ * Return value:
+ *   Computed 32-bit hash value
+ */
+static u32 nfsd_file_obj_hashfn(const void *data, u32 len, u32 seed)
+{
+       const struct nfsd_file *nf = data;
+
+       return nfsd_file_inode_hash(nf->nf_inode, seed);
+}
+
+static bool
+nfsd_match_cred(const struct cred *c1, const struct cred *c2)
+{
+       int i;
+
+       if (!uid_eq(c1->fsuid, c2->fsuid))
+               return false;
+       if (!gid_eq(c1->fsgid, c2->fsgid))
+               return false;
+       if (c1->group_info == NULL || c2->group_info == NULL)
+               return c1->group_info == c2->group_info;
+       if (c1->group_info->ngroups != c2->group_info->ngroups)
+               return false;
+       for (i = 0; i < c1->group_info->ngroups; i++) {
+               if (!gid_eq(c1->group_info->gid[i], c2->group_info->gid[i]))
+                       return false;
+       }
+       return true;
+}
+
+/**
+ * nfsd_file_obj_cmpfn - Match a cache item against search criteria
+ * @arg: search criteria
+ * @ptr: cache item to check
+ *
+ * Return values:
+ *   %0 - Item matches search criteria
+ *   %1 - Item does not match search criteria
+ */
+static int nfsd_file_obj_cmpfn(struct rhashtable_compare_arg *arg,
+                              const void *ptr)
+{
+       const struct nfsd_file_lookup_key *key = arg->key;
+       const struct nfsd_file *nf = ptr;
+
+       switch (key->type) {
+       case NFSD_FILE_KEY_INODE:
+               if (nf->nf_inode != key->inode)
+                       return 1;
+               break;
+       case NFSD_FILE_KEY_FULL:
+               if (nf->nf_inode != key->inode)
+                       return 1;
+               if (nf->nf_may != key->need)
+                       return 1;
+               if (nf->nf_net != key->net)
+                       return 1;
+               if (!nfsd_match_cred(nf->nf_cred, key->cred))
+                       return 1;
+               if (test_bit(NFSD_FILE_HASHED, &nf->nf_flags) == 0)
+                       return 1;
+               break;
+       }
+       return 0;
+}
+
+static const struct rhashtable_params nfsd_file_rhash_params = {
+       .key_len                = sizeof_field(struct nfsd_file, nf_inode),
+       .key_offset             = offsetof(struct nfsd_file, nf_inode),
+       .head_offset            = offsetof(struct nfsd_file, nf_rhash),
+       .hashfn                 = nfsd_file_key_hashfn,
+       .obj_hashfn             = nfsd_file_obj_hashfn,
+       .obj_cmpfn              = nfsd_file_obj_cmpfn,
+       /* Reduce resizing churn on light workloads */
+       .min_size               = 512,          /* buckets */
+       .automatic_shrinking    = true,
+};
 
 static void
 nfsd_file_schedule_laundrette(void)
@@ -694,13 +825,18 @@ static const struct fsnotify_ops nfsd_file_fsnotify_ops = {
 int
 nfsd_file_cache_init(void)
 {
-       int             ret = -ENOMEM;
+       int             ret;
        unsigned int    i;
 
        lockdep_assert_held(&nfsd_mutex);
        if (test_and_set_bit(NFSD_FILE_CACHE_UP, &nfsd_file_flags) == 1)
                return 0;
 
+       ret = rhashtable_init(&nfsd_file_rhash_tbl, &nfsd_file_rhash_params);
+       if (ret)
+               return ret;
+
+       ret = -ENOMEM;
        nfsd_filecache_wq = alloc_workqueue("nfsd_filecache", 0, 0);
        if (!nfsd_filecache_wq)
                goto out;
@@ -778,6 +914,7 @@ out_err:
        nfsd_file_hashtbl = NULL;
        destroy_workqueue(nfsd_filecache_wq);
        nfsd_filecache_wq = NULL;
+       rhashtable_destroy(&nfsd_file_rhash_tbl);
        goto out;
 }
 
@@ -903,6 +1040,7 @@ nfsd_file_cache_shutdown(void)
        nfsd_file_hashtbl = NULL;
        destroy_workqueue(nfsd_filecache_wq);
        nfsd_filecache_wq = NULL;
+       rhashtable_destroy(&nfsd_file_rhash_tbl);
 
        for_each_possible_cpu(i) {
                per_cpu(nfsd_file_cache_hits, i) = 0;
@@ -914,26 +1052,6 @@ nfsd_file_cache_shutdown(void)
        }
 }
 
-static bool
-nfsd_match_cred(const struct cred *c1, const struct cred *c2)
-{
-       int i;
-
-       if (!uid_eq(c1->fsuid, c2->fsuid))
-               return false;
-       if (!gid_eq(c1->fsgid, c2->fsgid))
-               return false;
-       if (c1->group_info == NULL || c2->group_info == NULL)
-               return c1->group_info == c2->group_info;
-       if (c1->group_info->ngroups != c2->group_info->ngroups)
-               return false;
-       for (i = 0; i < c1->group_info->ngroups; i++) {
-               if (!gid_eq(c1->group_info->gid[i], c2->group_info->gid[i]))
-                       return false;
-       }
-       return true;
-}
-
 static struct nfsd_file *
 nfsd_file_find_locked(struct inode *inode, unsigned int may_flags,
                        unsigned int hashval, struct net *net)
index 82051e1..5cbfc61 100644 (file)
@@ -29,6 +29,7 @@ struct nfsd_file_mark {
  * never be dereferenced, only used for comparison.
  */
 struct nfsd_file {
+       struct rhash_head       nf_rhash;
        struct hlist_node       nf_node;
        struct list_head        nf_lru;
        struct rcu_head         nf_rcu;