1134 lines · 30.3 KB · C
RawDownload
/* * Resizable, Scalable, Concurrent Hash Table * * Copyright (c) 2014-2015 Thomas Graf <tgraf@suug.ch> * Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net> * * Based on the following paper: * https://www.usenix.org/legacy/event/atc11/tech/final_files/Triplett.pdf * * Code partially derived from nft_hash * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/kernel.h>#include <linux/init.h>#include <linux/log2.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/mm.h>#include <linux/jhash.h>#include <linux/random.h>#include <linux/rhashtable.h>#include <linux/err.h> #define HASH_DEFAULT_SIZE	64UL#define HASH_MIN_SIZE		4UL#define BUCKET_LOCKS_PER_CPU   128UL /* Base bits plus 1 bit for nulls marker */#define HASH_RESERVED_SPACE	(RHT_BASE_BITS + 1) enum {	RHT_LOCK_NORMAL,	RHT_LOCK_NESTED,}; /* The bucket lock is selected based on the hash and protects mutations * on a group of hash buckets. * * A maximum of tbl->size/2 bucket locks is allocated. This ensures that * a single lock always covers both buckets which may both contains * entries which link to the same bucket of the old table during resizing. * This allows to simplify the locking as locking the bucket in both * tables during resize always guarantee protection. * * IMPORTANT: When holding the bucket lock of both the old and new table * during expansions and shrinking, the old bucket lock must always be * acquired first. */static spinlock_t *bucket_lock(const struct bucket_table *tbl, u32 hash){	return &tbl->locks[hash & tbl->locks_mask];} static void *rht_obj(const struct rhashtable *ht, const struct rhash_head *he){	return (void *) he - ht->p.head_offset;} static u32 rht_bucket_index(const struct bucket_table *tbl, u32 hash){	return hash & (tbl->size - 1);} static u32 obj_raw_hashfn(const struct rhashtable *ht, const void *ptr){	u32 hash; 	if (unlikely(!ht->p.key_len))		hash = ht->p.obj_hashfn(ptr, ht->p.hash_rnd);	else		hash = ht->p.hashfn(ptr + ht->p.key_offset, ht->p.key_len,				    ht->p.hash_rnd); 	return hash >> HASH_RESERVED_SPACE;} static u32 key_hashfn(struct rhashtable *ht, const void *key, u32 len){	return ht->p.hashfn(key, len, ht->p.hash_rnd) >> HASH_RESERVED_SPACE;} static u32 head_hashfn(const struct rhashtable *ht,		       const struct bucket_table *tbl,		       const struct rhash_head *he){	return rht_bucket_index(tbl, obj_raw_hashfn(ht, rht_obj(ht, he)));} #ifdef CONFIG_PROVE_LOCKINGstatic void debug_dump_buckets(const struct rhashtable *ht,			       const struct bucket_table *tbl){	struct rhash_head *he;	unsigned int i, hash; 	for (i = 0; i < tbl->size; i++) {		pr_warn(" [Bucket %d] ", i);		rht_for_each_rcu(he, tbl, i) {			hash = head_hashfn(ht, tbl, he);			pr_cont("[hash = %#x, lock = %p] ",				hash, bucket_lock(tbl, hash));		}		pr_cont("\n");	} } static void debug_dump_table(struct rhashtable *ht,			     const struct bucket_table *tbl,			     unsigned int hash){	struct bucket_table *old_tbl, *future_tbl; 	pr_emerg("BUG: lock for hash %#x in table %p not held\n",		 hash, tbl); 	rcu_read_lock();	future_tbl = rht_dereference_rcu(ht->future_tbl, ht);	old_tbl = rht_dereference_rcu(ht->tbl, ht);	if (future_tbl != old_tbl) {		pr_warn("Future table %p (size: %zd)\n",			future_tbl, future_tbl->size);		debug_dump_buckets(ht, future_tbl);	} 	pr_warn("Table %p (size: %zd)\n", old_tbl, old_tbl->size);	debug_dump_buckets(ht, old_tbl); 	rcu_read_unlock();} #define ASSERT_RHT_MUTEX(HT) BUG_ON(!lockdep_rht_mutex_is_held(HT))#define ASSERT_BUCKET_LOCK(HT, TBL, HASH)				\	do {								\		if (unlikely(!lockdep_rht_bucket_is_held(TBL, HASH))) {	\			debug_dump_table(HT, TBL, HASH);		\			BUG();						\		}							\	} while (0) int lockdep_rht_mutex_is_held(struct rhashtable *ht){	return (debug_locks) ? lockdep_is_held(&ht->mutex) : 1;}EXPORT_SYMBOL_GPL(lockdep_rht_mutex_is_held); int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, u32 hash){	spinlock_t *lock = bucket_lock(tbl, hash); 	return (debug_locks) ? lockdep_is_held(lock) : 1;}EXPORT_SYMBOL_GPL(lockdep_rht_bucket_is_held);#else#define ASSERT_RHT_MUTEX(HT)#define ASSERT_BUCKET_LOCK(HT, TBL, HASH)#endif  static struct rhash_head __rcu **bucket_tail(struct bucket_table *tbl, u32 n){	struct rhash_head __rcu **pprev; 	for (pprev = &tbl->buckets[n];	     !rht_is_a_nulls(rht_dereference_bucket(*pprev, tbl, n));	     pprev = &rht_dereference_bucket(*pprev, tbl, n)->next)		; 	return pprev;} static int alloc_bucket_locks(struct rhashtable *ht, struct bucket_table *tbl){	unsigned int i, size;#if defined(CONFIG_PROVE_LOCKING)	unsigned int nr_pcpus = 2;#else	unsigned int nr_pcpus = num_possible_cpus();#endif 	nr_pcpus = min_t(unsigned int, nr_pcpus, 32UL);	size = roundup_pow_of_two(nr_pcpus * ht->p.locks_mul); 	/* Never allocate more than 0.5 locks per bucket */	size = min_t(unsigned int, size, tbl->size >> 1); 	if (sizeof(spinlock_t) != 0) {#ifdef CONFIG_NUMA		if (size * sizeof(spinlock_t) > PAGE_SIZE)			tbl->locks = vmalloc(size * sizeof(spinlock_t));		else#endif		tbl->locks = kmalloc_array(size, sizeof(spinlock_t),					   GFP_KERNEL);		if (!tbl->locks)			return -ENOMEM;		for (i = 0; i < size; i++)			spin_lock_init(&tbl->locks[i]);	}	tbl->locks_mask = size - 1; 	return 0;} static void bucket_table_free(const struct bucket_table *tbl){	if (tbl)		kvfree(tbl->locks); 	kvfree(tbl);} static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,					       size_t nbuckets){	struct bucket_table *tbl = NULL;	size_t size;	int i; 	size = sizeof(*tbl) + nbuckets * sizeof(tbl->buckets[0]);	if (size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER))		tbl = kzalloc(size, GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);	if (tbl == NULL)		tbl = vzalloc(size);	if (tbl == NULL)		return NULL; 	tbl->size = nbuckets; 	if (alloc_bucket_locks(ht, tbl) < 0) {		bucket_table_free(tbl);		return NULL;	} 	for (i = 0; i < nbuckets; i++)		INIT_RHT_NULLS_HEAD(tbl->buckets[i], ht, i); 	return tbl;} /** * rht_grow_above_75 - returns true if nelems > 0.75 * table-size * @ht:		hash table * @new_size:	new table size */static bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size){	/* Expand table when exceeding 75% load */	return atomic_read(&ht->nelems) > (new_size / 4 * 3) &&	       (!ht->p.max_shift || atomic_read(&ht->shift) < ht->p.max_shift);} /** * rht_shrink_below_30 - returns true if nelems < 0.3 * table-size * @ht:		hash table * @new_size:	new table size */static bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size){	/* Shrink table beneath 30% load */	return atomic_read(&ht->nelems) < (new_size * 3 / 10) &&	       (atomic_read(&ht->shift) > ht->p.min_shift);} static void lock_buckets(struct bucket_table *new_tbl,			 struct bucket_table *old_tbl, unsigned int hash)	__acquires(old_bucket_lock){	spin_lock_bh(bucket_lock(old_tbl, hash));	if (new_tbl != old_tbl)		spin_lock_bh_nested(bucket_lock(new_tbl, hash),				    RHT_LOCK_NESTED);} static void unlock_buckets(struct bucket_table *new_tbl,			   struct bucket_table *old_tbl, unsigned int hash)	__releases(old_bucket_lock){	if (new_tbl != old_tbl)		spin_unlock_bh(bucket_lock(new_tbl, hash));	spin_unlock_bh(bucket_lock(old_tbl, hash));} /** * Unlink entries on bucket which hash to different bucket. * * Returns true if no more work needs to be performed on the bucket. */static bool hashtable_chain_unzip(struct rhashtable *ht,				  const struct bucket_table *new_tbl,				  struct bucket_table *old_tbl,				  size_t old_hash){	struct rhash_head *he, *p, *next;	unsigned int new_hash, new_hash2; 	ASSERT_BUCKET_LOCK(ht, old_tbl, old_hash); 	/* Old bucket empty, no work needed. */	p = rht_dereference_bucket(old_tbl->buckets[old_hash], old_tbl,				   old_hash);	if (rht_is_a_nulls(p))		return false; 	new_hash = head_hashfn(ht, new_tbl, p);	ASSERT_BUCKET_LOCK(ht, new_tbl, new_hash); 	/* Advance the old bucket pointer one or more times until it	 * reaches a node that doesn't hash to the same bucket as the	 * previous node p. Call the previous node p;	 */	rht_for_each_continue(he, p->next, old_tbl, old_hash) {		new_hash2 = head_hashfn(ht, new_tbl, he);		ASSERT_BUCKET_LOCK(ht, new_tbl, new_hash2); 		if (new_hash != new_hash2)			break;		p = he;	}	rcu_assign_pointer(old_tbl->buckets[old_hash], p->next); 	/* Find the subsequent node which does hash to the same	 * bucket as node P, or NULL if no such node exists.	 */	INIT_RHT_NULLS_HEAD(next, ht, old_hash);	if (!rht_is_a_nulls(he)) {		rht_for_each_continue(he, he->next, old_tbl, old_hash) {			if (head_hashfn(ht, new_tbl, he) == new_hash) {				next = he;				break;			}		}	} 	/* Set p's next pointer to that subsequent node pointer,	 * bypassing the nodes which do not hash to p's bucket	 */	rcu_assign_pointer(p->next, next); 	p = rht_dereference_bucket(old_tbl->buckets[old_hash], old_tbl,				   old_hash); 	return !rht_is_a_nulls(p);} static void link_old_to_new(struct rhashtable *ht, struct bucket_table *new_tbl,			    unsigned int new_hash, struct rhash_head *entry){	ASSERT_BUCKET_LOCK(ht, new_tbl, new_hash); 	rcu_assign_pointer(*bucket_tail(new_tbl, new_hash), entry);} /** * rhashtable_expand - Expand hash table while allowing concurrent lookups * @ht:		the hash table to expand * * A secondary bucket array is allocated and the hash entries are migrated * while keeping them on both lists until the end of the RCU grace period. * * This function may only be called in a context where it is safe to call * synchronize_rcu(), e.g. not within a rcu_read_lock() section. * * The caller must ensure that no concurrent resizing occurs by holding * ht->mutex. * * It is valid to have concurrent insertions and deletions protected by per * bucket locks or concurrent RCU protected lookups and traversals. */int rhashtable_expand(struct rhashtable *ht){	struct bucket_table *new_tbl, *old_tbl = rht_dereference(ht->tbl, ht);	struct rhash_head *he;	unsigned int new_hash, old_hash;	bool complete = false; 	ASSERT_RHT_MUTEX(ht); 	new_tbl = bucket_table_alloc(ht, old_tbl->size * 2);	if (new_tbl == NULL)		return -ENOMEM; 	atomic_inc(&ht->shift); 	/* Make insertions go into the new, empty table right away. Deletions	 * and lookups will be attempted in both tables until we synchronize.	 * The synchronize_rcu() guarantees for the new table to be picked up	 * so no new additions go into the old table while we relink.	 */	rcu_assign_pointer(ht->future_tbl, new_tbl);	synchronize_rcu(); 	/* For each new bucket, search the corresponding old bucket for the	 * first entry that hashes to the new bucket, and link the end of	 * newly formed bucket chain (containing entries added to future	 * table) to that entry. Since all the entries which will end up in	 * the new bucket appear in the same old bucket, this constructs an	 * entirely valid new hash table, but with multiple buckets	 * "zipped" together into a single imprecise chain.	 */	for (new_hash = 0; new_hash < new_tbl->size; new_hash++) {		old_hash = rht_bucket_index(old_tbl, new_hash);		lock_buckets(new_tbl, old_tbl, new_hash);		rht_for_each(he, old_tbl, old_hash) {			if (head_hashfn(ht, new_tbl, he) == new_hash) {				link_old_to_new(ht, new_tbl, new_hash, he);				break;			}		}		unlock_buckets(new_tbl, old_tbl, new_hash);		cond_resched();	} 	/* Unzip interleaved hash chains */	while (!complete && !ht->being_destroyed) {		/* Wait for readers. All new readers will see the new		 * table, and thus no references to the old table will		 * remain.		 */		synchronize_rcu(); 		/* For each bucket in the old table (each of which		 * contains items from multiple buckets of the new		 * table): ...		 */		complete = true;		for (old_hash = 0; old_hash < old_tbl->size; old_hash++) {			lock_buckets(new_tbl, old_tbl, old_hash); 			if (hashtable_chain_unzip(ht, new_tbl, old_tbl,						  old_hash))				complete = false; 			unlock_buckets(new_tbl, old_tbl, old_hash);			cond_resched();		}	} 	rcu_assign_pointer(ht->tbl, new_tbl);	synchronize_rcu(); 	bucket_table_free(old_tbl);	return 0;}EXPORT_SYMBOL_GPL(rhashtable_expand); /** * rhashtable_shrink - Shrink hash table while allowing concurrent lookups * @ht:		the hash table to shrink * * This function may only be called in a context where it is safe to call * synchronize_rcu(), e.g. not within a rcu_read_lock() section. * * The caller must ensure that no concurrent resizing occurs by holding * ht->mutex. * * The caller must ensure that no concurrent table mutations take place. * It is however valid to have concurrent lookups if they are RCU protected. * * It is valid to have concurrent insertions and deletions protected by per * bucket locks or concurrent RCU protected lookups and traversals. */int rhashtable_shrink(struct rhashtable *ht){	struct bucket_table *new_tbl, *tbl = rht_dereference(ht->tbl, ht);	unsigned int new_hash; 	ASSERT_RHT_MUTEX(ht); 	new_tbl = bucket_table_alloc(ht, tbl->size / 2);	if (new_tbl == NULL)		return -ENOMEM; 	rcu_assign_pointer(ht->future_tbl, new_tbl);	synchronize_rcu(); 	/* Link the first entry in the old bucket to the end of the	 * bucket in the new table. As entries are concurrently being	 * added to the new table, lock down the new bucket. As we	 * always divide the size in half when shrinking, each bucket	 * in the new table maps to exactly two buckets in the old	 * table.	 */	for (new_hash = 0; new_hash < new_tbl->size; new_hash++) {		lock_buckets(new_tbl, tbl, new_hash); 		rcu_assign_pointer(*bucket_tail(new_tbl, new_hash),				   tbl->buckets[new_hash]);		ASSERT_BUCKET_LOCK(ht, tbl, new_hash + new_tbl->size);		rcu_assign_pointer(*bucket_tail(new_tbl, new_hash),				   tbl->buckets[new_hash + new_tbl->size]); 		unlock_buckets(new_tbl, tbl, new_hash);		cond_resched();	}

Lines 1–500 of 1134.