Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+27 −6
Original line number Original line Diff line number Diff line
@@ -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;
 * @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 {
 * 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 {
	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(
	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(
+16 −1
Original line number Original line Diff line number Diff line
@@ -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,


	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,
	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;