Commit fc22945e authored by Chuck Lever's avatar Chuck Lever

NFSD: Set up an rhashtable for the filecache

Add code to initialize and tear down an rhashtable. The rhashtable
is not used yet.
Reviewed-by: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent c7b824c3
......@@ -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 @@ nfsd_file_cache_init(void)
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)
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment