Commit 55161065 authored by NeilBrown's avatar NeilBrown Committed by Greg Kroah-Hartman

staging: lustre: convert osc_quota hash to rhashtable

As this is indexed by an integer, an extensible array
or extensible bitmap would be better.
If/when xarray lands, we should change to use that.

For now, just a simple conversion to rhashtable.

When removing an entry, we need to hold rcu_read_lock()
across the lookup and remove in case we race with another thread
performing a removal.  This means we need to use call_rcu()
to free the quota info so we need an rcu_head in there, which
unfortunately doubles the size of the structure.
Signed-off-by: default avatarNeilBrown <neilb@suse.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 4206c444
......@@ -333,7 +333,7 @@ struct client_obd {
void *cl_writeback_work;
void *cl_lru_work;
/* hash tables for osc_quota_info */
struct cfs_hash *cl_quota_hash[MAXQUOTAS];
struct rhashtable cl_quota_hash[MAXQUOTAS];
};
#define obd2cli_tgt(obd) ((char *)(obd)->u.cli.cl_target_uuid.uuid)
......
......@@ -188,8 +188,9 @@ extern struct lu_kmem_descr osc_caches[];
extern struct kmem_cache *osc_quota_kmem;
struct osc_quota_info {
/** linkage for quota hash table */
struct hlist_node oqi_hash;
struct rhash_head oqi_hash;
u32 oqi_id;
struct rcu_head rcu;
};
int osc_quota_setup(struct obd_device *obd);
......
......@@ -27,6 +27,13 @@
#include <obd_class.h>
#include "osc_internal.h"
static const struct rhashtable_params quota_hash_params = {
.key_len = sizeof(u32),
.key_offset = offsetof(struct osc_quota_info, oqi_id),
.head_offset = offsetof(struct osc_quota_info, oqi_hash),
.automatic_shrinking = true,
};
static inline struct osc_quota_info *osc_oqi_alloc(u32 id)
{
struct osc_quota_info *oqi;
......@@ -45,9 +52,10 @@ int osc_quota_chkdq(struct client_obd *cli, const unsigned int qid[])
for (type = 0; type < MAXQUOTAS; type++) {
struct osc_quota_info *oqi;
oqi = cfs_hash_lookup(cli->cl_quota_hash[type], &qid[type]);
oqi = rhashtable_lookup_fast(&cli->cl_quota_hash[type], &qid[type],
quota_hash_params);
if (oqi) {
/* do not try to access oqi here, it could have been
/* Must not access oqi here, it could have been
* freed by osc_quota_setdq()
*/
......@@ -63,6 +71,14 @@ int osc_quota_chkdq(struct client_obd *cli, const unsigned int qid[])
return QUOTA_OK;
}
static void osc_quota_free(struct rcu_head *head)
{
struct osc_quota_info *oqi = container_of(head, struct osc_quota_info, rcu);
kmem_cache_free(osc_quota_kmem, oqi);
}
#define MD_QUOTA_FLAG(type) ((type == USRQUOTA) ? OBD_MD_FLUSRQUOTA \
: OBD_MD_FLGRPQUOTA)
#define FL_QUOTA_FLAG(type) ((type == USRQUOTA) ? OBD_FL_NO_USRQUOTA \
......@@ -84,11 +100,14 @@ int osc_quota_setdq(struct client_obd *cli, const unsigned int qid[],
continue;
/* lookup the ID in the per-type hash table */
oqi = cfs_hash_lookup(cli->cl_quota_hash[type], &qid[type]);
rcu_read_lock();
oqi = rhashtable_lookup_fast(&cli->cl_quota_hash[type], &qid[type],
quota_hash_params);
if ((flags & FL_QUOTA_FLAG(type)) != 0) {
/* This ID is getting close to its quota limit, let's
* switch to sync I/O
*/
rcu_read_unlock();
if (oqi)
continue;
......@@ -98,12 +117,16 @@ int osc_quota_setdq(struct client_obd *cli, const unsigned int qid[],
break;
}
rc = cfs_hash_add_unique(cli->cl_quota_hash[type],
&qid[type], &oqi->oqi_hash);
rc = rhashtable_lookup_insert_fast(&cli->cl_quota_hash[type],
&oqi->oqi_hash, quota_hash_params);
/* race with others? */
if (rc == -EALREADY) {
rc = 0;
if (rc) {
kmem_cache_free(osc_quota_kmem, oqi);
if (rc != -EEXIST) {
rc = -ENOMEM;
break;
}
rc = 0;
}
CDEBUG(D_QUOTA, "%s: setdq to insert for %s %d (%d)\n",
......@@ -114,14 +137,14 @@ int osc_quota_setdq(struct client_obd *cli, const unsigned int qid[],
/* This ID is now off the hook, let's remove it from
* the hash table
*/
if (!oqi)
if (!oqi) {
rcu_read_unlock();
continue;
oqi = cfs_hash_del_key(cli->cl_quota_hash[type],
&qid[type]);
if (oqi)
kmem_cache_free(osc_quota_kmem, oqi);
}
if (rhashtable_remove_fast(&cli->cl_quota_hash[type],
&oqi->oqi_hash, quota_hash_params) == 0)
call_rcu(&oqi->rcu, osc_quota_free);
rcu_read_unlock();
CDEBUG(D_QUOTA, "%s: setdq to remove for %s %d (%p)\n",
cli_name(cli),
type == USRQUOTA ? "user" : "group",
......@@ -132,93 +155,21 @@ int osc_quota_setdq(struct client_obd *cli, const unsigned int qid[],
return rc;
}
/*
* Hash operations for uid/gid <-> osc_quota_info
*/
static unsigned int
oqi_hashfn(struct cfs_hash *hs, const void *key, unsigned int mask)
{
return cfs_hash_u32_hash(*((__u32 *)key), mask);
}
static int
oqi_keycmp(const void *key, struct hlist_node *hnode)
{
struct osc_quota_info *oqi;
u32 uid;
LASSERT(key);
uid = *((u32 *)key);
oqi = hlist_entry(hnode, struct osc_quota_info, oqi_hash);
return uid == oqi->oqi_id;
}
static void *
oqi_key(struct hlist_node *hnode)
{
struct osc_quota_info *oqi;
oqi = hlist_entry(hnode, struct osc_quota_info, oqi_hash);
return &oqi->oqi_id;
}
static void *
oqi_object(struct hlist_node *hnode)
{
return hlist_entry(hnode, struct osc_quota_info, oqi_hash);
}
static void
oqi_get(struct cfs_hash *hs, struct hlist_node *hnode)
oqi_exit(void *vquota, void *data)
{
}
struct osc_quota_info *oqi = vquota;
static void
oqi_put_locked(struct cfs_hash *hs, struct hlist_node *hnode)
{
}
static void
oqi_exit(struct cfs_hash *hs, struct hlist_node *hnode)
{
struct osc_quota_info *oqi;
oqi = hlist_entry(hnode, struct osc_quota_info, oqi_hash);
kmem_cache_free(osc_quota_kmem, oqi);
osc_quota_free(&oqi->rcu);
}
#define HASH_QUOTA_BKT_BITS 5
#define HASH_QUOTA_CUR_BITS 5
#define HASH_QUOTA_MAX_BITS 15
static struct cfs_hash_ops quota_hash_ops = {
.hs_hash = oqi_hashfn,
.hs_keycmp = oqi_keycmp,
.hs_key = oqi_key,
.hs_object = oqi_object,
.hs_get = oqi_get,
.hs_put_locked = oqi_put_locked,
.hs_exit = oqi_exit,
};
int osc_quota_setup(struct obd_device *obd)
{
struct client_obd *cli = &obd->u.cli;
int i, type;
for (type = 0; type < MAXQUOTAS; type++) {
cli->cl_quota_hash[type] = cfs_hash_create("QUOTA_HASH",
HASH_QUOTA_CUR_BITS,
HASH_QUOTA_MAX_BITS,
HASH_QUOTA_BKT_BITS,
0,
CFS_HASH_MIN_THETA,
CFS_HASH_MAX_THETA,
&quota_hash_ops,
CFS_HASH_DEFAULT);
if (!cli->cl_quota_hash[type])
if (rhashtable_init(&cli->cl_quota_hash[type], &quota_hash_params) != 0)
break;
}
......@@ -226,7 +177,7 @@ int osc_quota_setup(struct obd_device *obd)
return 0;
for (i = 0; i < type; i++)
cfs_hash_putref(cli->cl_quota_hash[i]);
rhashtable_destroy(&cli->cl_quota_hash[i]);
return -ENOMEM;
}
......@@ -237,7 +188,8 @@ int osc_quota_cleanup(struct obd_device *obd)
int type;
for (type = 0; type < MAXQUOTAS; type++)
cfs_hash_putref(cli->cl_quota_hash[type]);
rhashtable_free_and_destroy(&cli->cl_quota_hash[type],
oqi_exit, NULL);
return 0;
}
......
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