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 { ...@@ -333,7 +333,7 @@ struct client_obd {
void *cl_writeback_work; void *cl_writeback_work;
void *cl_lru_work; void *cl_lru_work;
/* hash tables for osc_quota_info */ /* 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) #define obd2cli_tgt(obd) ((char *)(obd)->u.cli.cl_target_uuid.uuid)
......
...@@ -188,8 +188,9 @@ extern struct lu_kmem_descr osc_caches[]; ...@@ -188,8 +188,9 @@ extern struct lu_kmem_descr osc_caches[];
extern struct kmem_cache *osc_quota_kmem; extern struct kmem_cache *osc_quota_kmem;
struct osc_quota_info { struct osc_quota_info {
/** linkage for quota hash table */ /** linkage for quota hash table */
struct hlist_node oqi_hash; struct rhash_head oqi_hash;
u32 oqi_id; u32 oqi_id;
struct rcu_head rcu;
}; };
int osc_quota_setup(struct obd_device *obd); int osc_quota_setup(struct obd_device *obd);
......
...@@ -27,6 +27,13 @@ ...@@ -27,6 +27,13 @@
#include <obd_class.h> #include <obd_class.h>
#include "osc_internal.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) static inline struct osc_quota_info *osc_oqi_alloc(u32 id)
{ {
struct osc_quota_info *oqi; struct osc_quota_info *oqi;
...@@ -45,9 +52,10 @@ int osc_quota_chkdq(struct client_obd *cli, const unsigned int qid[]) ...@@ -45,9 +52,10 @@ int osc_quota_chkdq(struct client_obd *cli, const unsigned int qid[])
for (type = 0; type < MAXQUOTAS; type++) { for (type = 0; type < MAXQUOTAS; type++) {
struct osc_quota_info *oqi; 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) { 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() * freed by osc_quota_setdq()
*/ */
...@@ -63,6 +71,14 @@ int osc_quota_chkdq(struct client_obd *cli, const unsigned int qid[]) ...@@ -63,6 +71,14 @@ int osc_quota_chkdq(struct client_obd *cli, const unsigned int qid[])
return QUOTA_OK; 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 \ #define MD_QUOTA_FLAG(type) ((type == USRQUOTA) ? OBD_MD_FLUSRQUOTA \
: OBD_MD_FLGRPQUOTA) : OBD_MD_FLGRPQUOTA)
#define FL_QUOTA_FLAG(type) ((type == USRQUOTA) ? OBD_FL_NO_USRQUOTA \ #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[], ...@@ -84,11 +100,14 @@ int osc_quota_setdq(struct client_obd *cli, const unsigned int qid[],
continue; continue;
/* lookup the ID in the per-type hash table */ /* 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) { if ((flags & FL_QUOTA_FLAG(type)) != 0) {
/* This ID is getting close to its quota limit, let's /* This ID is getting close to its quota limit, let's
* switch to sync I/O * switch to sync I/O
*/ */
rcu_read_unlock();
if (oqi) if (oqi)
continue; continue;
...@@ -98,12 +117,16 @@ int osc_quota_setdq(struct client_obd *cli, const unsigned int qid[], ...@@ -98,12 +117,16 @@ int osc_quota_setdq(struct client_obd *cli, const unsigned int qid[],
break; break;
} }
rc = cfs_hash_add_unique(cli->cl_quota_hash[type], rc = rhashtable_lookup_insert_fast(&cli->cl_quota_hash[type],
&qid[type], &oqi->oqi_hash); &oqi->oqi_hash, quota_hash_params);
/* race with others? */ /* race with others? */
if (rc == -EALREADY) { if (rc) {
rc = 0;
kmem_cache_free(osc_quota_kmem, oqi); 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", 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[], ...@@ -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 /* This ID is now off the hook, let's remove it from
* the hash table * the hash table
*/ */
if (!oqi) if (!oqi) {
rcu_read_unlock();
continue; continue;
}
oqi = cfs_hash_del_key(cli->cl_quota_hash[type], if (rhashtable_remove_fast(&cli->cl_quota_hash[type],
&qid[type]); &oqi->oqi_hash, quota_hash_params) == 0)
if (oqi) call_rcu(&oqi->rcu, osc_quota_free);
kmem_cache_free(osc_quota_kmem, oqi); rcu_read_unlock();
CDEBUG(D_QUOTA, "%s: setdq to remove for %s %d (%p)\n", CDEBUG(D_QUOTA, "%s: setdq to remove for %s %d (%p)\n",
cli_name(cli), cli_name(cli),
type == USRQUOTA ? "user" : "group", type == USRQUOTA ? "user" : "group",
...@@ -132,93 +155,21 @@ int osc_quota_setdq(struct client_obd *cli, const unsigned int qid[], ...@@ -132,93 +155,21 @@ int osc_quota_setdq(struct client_obd *cli, const unsigned int qid[],
return rc; 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 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 osc_quota_free(&oqi->rcu);
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);
} }
#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) int osc_quota_setup(struct obd_device *obd)
{ {
struct client_obd *cli = &obd->u.cli; struct client_obd *cli = &obd->u.cli;
int i, type; int i, type;
for (type = 0; type < MAXQUOTAS; type++) { for (type = 0; type < MAXQUOTAS; type++) {
cli->cl_quota_hash[type] = cfs_hash_create("QUOTA_HASH", if (rhashtable_init(&cli->cl_quota_hash[type], &quota_hash_params) != 0)
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])
break; break;
} }
...@@ -226,7 +177,7 @@ int osc_quota_setup(struct obd_device *obd) ...@@ -226,7 +177,7 @@ int osc_quota_setup(struct obd_device *obd)
return 0; return 0;
for (i = 0; i < type; i++) for (i = 0; i < type; i++)
cfs_hash_putref(cli->cl_quota_hash[i]); rhashtable_destroy(&cli->cl_quota_hash[i]);
return -ENOMEM; return -ENOMEM;
} }
...@@ -237,7 +188,8 @@ int osc_quota_cleanup(struct obd_device *obd) ...@@ -237,7 +188,8 @@ int osc_quota_cleanup(struct obd_device *obd)
int type; int type;
for (type = 0; type < MAXQUOTAS; 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; 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