Commit 31ccde2d authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

rhashtable: Allow hashfn to be unset

Since every current rhashtable user uses jhash as their hash
function, the fact that jhash is an inline function causes each
user to generate a copy of its code.

This function provides a solution to this problem by allowing
hashfn to be unset.  In which case rhashtable will automatically
set it to jhash.  Furthermore, if the key length is a multiple
of 4, we will switch over to jhash2.
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Acked-by: default avatarThomas Graf <tgraf@suug.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent de91b25c
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/jhash.h>
#include <linux/list_nulls.h> #include <linux/list_nulls.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/mutex.h> #include <linux/mutex.h>
...@@ -103,7 +104,7 @@ struct rhashtable; ...@@ -103,7 +104,7 @@ struct rhashtable;
* @min_size: Minimum size while shrinking * @min_size: Minimum size while shrinking
* @nulls_base: Base value to generate nulls marker * @nulls_base: Base value to generate nulls marker
* @locks_mul: Number of bucket locks to allocate per cpu (default: 128) * @locks_mul: Number of bucket locks to allocate per cpu (default: 128)
* @hashfn: Function to hash key * @hashfn: Hash function (default: jhash2 if !(key_len % 4), or jhash)
* @obj_hashfn: Function to hash object * @obj_hashfn: Function to hash object
* @obj_cmpfn: Function to compare key with object * @obj_cmpfn: Function to compare key with object
*/ */
...@@ -125,6 +126,7 @@ struct rhashtable_params { ...@@ -125,6 +126,7 @@ struct rhashtable_params {
* struct rhashtable - Hash table handle * struct rhashtable - Hash table handle
* @tbl: Bucket table * @tbl: Bucket table
* @nelems: Number of elements in table * @nelems: Number of elements in table
* @key_len: Key length for hashfn
* @p: Configuration parameters * @p: Configuration parameters
* @run_work: Deferred worker to expand/shrink asynchronously * @run_work: Deferred worker to expand/shrink asynchronously
* @mutex: Mutex to protect current/future table swapping * @mutex: Mutex to protect current/future table swapping
...@@ -134,6 +136,7 @@ struct rhashtable { ...@@ -134,6 +136,7 @@ struct rhashtable {
struct bucket_table __rcu *tbl; struct bucket_table __rcu *tbl;
atomic_t nelems; atomic_t nelems;
bool being_destroyed; bool being_destroyed;
unsigned int key_len;
struct rhashtable_params p; struct rhashtable_params p;
struct work_struct run_work; struct work_struct run_work;
struct mutex mutex; struct mutex mutex;
...@@ -199,13 +202,31 @@ static inline unsigned int rht_key_hashfn( ...@@ -199,13 +202,31 @@ static inline unsigned int rht_key_hashfn(
struct rhashtable *ht, const struct bucket_table *tbl, struct rhashtable *ht, const struct bucket_table *tbl,
const void *key, const struct rhashtable_params params) const void *key, const struct rhashtable_params params)
{ {
unsigned hash;
/* params must be equal to ht->p if it isn't constant. */ /* params must be equal to ht->p if it isn't constant. */
unsigned key_len = __builtin_constant_p(params.key_len) ? if (!__builtin_constant_p(params.key_len))
(params.key_len ?: ht->p.key_len) : hash = ht->p.hashfn(key, ht->key_len, tbl->hash_rnd);
params.key_len; else if (params.key_len) {
unsigned key_len = params.key_len;
if (params.hashfn)
hash = params.hashfn(key, key_len, tbl->hash_rnd);
else if (key_len & (sizeof(u32) - 1))
hash = jhash(key, key_len, tbl->hash_rnd);
else
hash = jhash2(key, key_len / sizeof(u32),
tbl->hash_rnd);
} else {
unsigned key_len = ht->p.key_len;
if (params.hashfn)
hash = params.hashfn(key, key_len, tbl->hash_rnd);
else
hash = jhash(key, key_len, tbl->hash_rnd);
}
return rht_bucket_index(tbl, params.hashfn(key, key_len, return rht_bucket_index(tbl, hash);
tbl->hash_rnd));
} }
static inline unsigned int rht_head_hashfn( static inline unsigned int rht_head_hashfn(
......
...@@ -532,6 +532,11 @@ static size_t rounded_hashtable_size(const struct rhashtable_params *params) ...@@ -532,6 +532,11 @@ static size_t rounded_hashtable_size(const struct rhashtable_params *params)
(unsigned long)params->min_size); (unsigned long)params->min_size);
} }
static u32 rhashtable_jhash2(const void *key, u32 length, u32 seed)
{
return jhash2(key, length, seed);
}
/** /**
* rhashtable_init - initialize a new hash table * rhashtable_init - initialize a new hash table
* @ht: hash table to be initialized * @ht: hash table to be initialized
...@@ -583,7 +588,7 @@ int rhashtable_init(struct rhashtable *ht, ...@@ -583,7 +588,7 @@ int rhashtable_init(struct rhashtable *ht,
size = HASH_DEFAULT_SIZE; size = HASH_DEFAULT_SIZE;
if ((!(params->key_len && params->hashfn) && !params->obj_hashfn) || if ((!params->key_len && !params->obj_hashfn) ||
(params->obj_hashfn && !params->obj_cmpfn)) (params->obj_hashfn && !params->obj_cmpfn))
return -EINVAL; return -EINVAL;
...@@ -610,6 +615,16 @@ int rhashtable_init(struct rhashtable *ht, ...@@ -610,6 +615,16 @@ int rhashtable_init(struct rhashtable *ht,
else else
ht->p.locks_mul = BUCKET_LOCKS_PER_CPU; ht->p.locks_mul = BUCKET_LOCKS_PER_CPU;
ht->key_len = ht->p.key_len;
if (!params->hashfn) {
ht->p.hashfn = jhash;
if (!(ht->key_len & (sizeof(u32) - 1))) {
ht->key_len /= sizeof(u32);
ht->p.hashfn = rhashtable_jhash2;
}
}
tbl = bucket_table_alloc(ht, size); tbl = bucket_table_alloc(ht, size);
if (tbl == NULL) if (tbl == NULL)
return -ENOMEM; return -ENOMEM;
......
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