mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	rhashtable: Introduce rhashtable_walk_*
Some existing rhashtable users get too intimate with it by walking the buckets directly. This prevents us from easily changing the internals of rhashtable. This patch adds the helpers rhashtable_walk_init/exit/start/next/stop which will replace these custom walkers. They are meant to be usable for both procfs seq_file walks as well as walking by a netlink dump. The iterator structure should fit inside a netlink dump cb structure, with at least one element to spare. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									28134a53d6
								
							
						
					
					
						commit
						f2dba9c6ff
					
				
					 2 changed files with 198 additions and 0 deletions
				
			
		| 
						 | 
					@ -18,6 +18,7 @@
 | 
				
			||||||
#ifndef _LINUX_RHASHTABLE_H
 | 
					#ifndef _LINUX_RHASHTABLE_H
 | 
				
			||||||
#define _LINUX_RHASHTABLE_H
 | 
					#define _LINUX_RHASHTABLE_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/compiler.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>
 | 
				
			||||||
| 
						 | 
					@ -111,6 +112,7 @@ struct rhashtable_params {
 | 
				
			||||||
 * @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
 | 
				
			||||||
 | 
					 * @walkers: List of active walkers
 | 
				
			||||||
 * @being_destroyed: True if table is set up for destruction
 | 
					 * @being_destroyed: True if table is set up for destruction
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
struct rhashtable {
 | 
					struct rhashtable {
 | 
				
			||||||
| 
						 | 
					@ -121,9 +123,36 @@ struct rhashtable {
 | 
				
			||||||
	struct rhashtable_params	p;
 | 
						struct rhashtable_params	p;
 | 
				
			||||||
	struct work_struct		run_work;
 | 
						struct work_struct		run_work;
 | 
				
			||||||
	struct mutex                    mutex;
 | 
						struct mutex                    mutex;
 | 
				
			||||||
 | 
						struct list_head		walkers;
 | 
				
			||||||
	bool                            being_destroyed;
 | 
						bool                            being_destroyed;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct rhashtable_walker - Hash table walker
 | 
				
			||||||
 | 
					 * @list: List entry on list of walkers
 | 
				
			||||||
 | 
					 * @resize: Resize event occured
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct rhashtable_walker {
 | 
				
			||||||
 | 
						struct list_head list;
 | 
				
			||||||
 | 
						bool resize;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * struct rhashtable_iter - Hash table iterator, fits into netlink cb
 | 
				
			||||||
 | 
					 * @ht: Table to iterate through
 | 
				
			||||||
 | 
					 * @p: Current pointer
 | 
				
			||||||
 | 
					 * @walker: Associated rhashtable walker
 | 
				
			||||||
 | 
					 * @slot: Current slot
 | 
				
			||||||
 | 
					 * @skip: Number of entries to skip in slot
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct rhashtable_iter {
 | 
				
			||||||
 | 
						struct rhashtable *ht;
 | 
				
			||||||
 | 
						struct rhash_head *p;
 | 
				
			||||||
 | 
						struct rhashtable_walker *walker;
 | 
				
			||||||
 | 
						unsigned int slot;
 | 
				
			||||||
 | 
						unsigned int skip;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline unsigned long rht_marker(const struct rhashtable *ht, u32 hash)
 | 
					static inline unsigned long rht_marker(const struct rhashtable *ht, u32 hash)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return NULLS_MARKER(ht->p.nulls_base + hash);
 | 
						return NULLS_MARKER(ht->p.nulls_base + hash);
 | 
				
			||||||
| 
						 | 
					@ -179,6 +208,12 @@ bool rhashtable_lookup_compare_insert(struct rhashtable *ht,
 | 
				
			||||||
				      bool (*compare)(void *, void *),
 | 
									      bool (*compare)(void *, void *),
 | 
				
			||||||
				      void *arg);
 | 
									      void *arg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter);
 | 
				
			||||||
 | 
					void rhashtable_walk_exit(struct rhashtable_iter *iter);
 | 
				
			||||||
 | 
					int rhashtable_walk_start(struct rhashtable_iter *iter) __acquires(RCU);
 | 
				
			||||||
 | 
					void *rhashtable_walk_next(struct rhashtable_iter *iter);
 | 
				
			||||||
 | 
					void rhashtable_walk_stop(struct rhashtable_iter *iter) __releases(RCU);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void rhashtable_destroy(struct rhashtable *ht);
 | 
					void rhashtable_destroy(struct rhashtable *ht);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define rht_dereference(p, ht) \
 | 
					#define rht_dereference(p, ht) \
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										163
									
								
								lib/rhashtable.c
									
									
									
									
									
								
							
							
						
						
									
										163
									
								
								lib/rhashtable.c
									
									
									
									
									
								
							| 
						 | 
					@ -484,6 +484,7 @@ static void rht_deferred_worker(struct work_struct *work)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rhashtable *ht;
 | 
						struct rhashtable *ht;
 | 
				
			||||||
	struct bucket_table *tbl;
 | 
						struct bucket_table *tbl;
 | 
				
			||||||
 | 
						struct rhashtable_walker *walker;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ht = container_of(work, struct rhashtable, run_work);
 | 
						ht = container_of(work, struct rhashtable, run_work);
 | 
				
			||||||
	mutex_lock(&ht->mutex);
 | 
						mutex_lock(&ht->mutex);
 | 
				
			||||||
| 
						 | 
					@ -492,6 +493,9 @@ static void rht_deferred_worker(struct work_struct *work)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tbl = rht_dereference(ht->tbl, ht);
 | 
						tbl = rht_dereference(ht->tbl, ht);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						list_for_each_entry(walker, &ht->walkers, list)
 | 
				
			||||||
 | 
							walker->resize = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ht->p.grow_decision && ht->p.grow_decision(ht, tbl->size))
 | 
						if (ht->p.grow_decision && ht->p.grow_decision(ht, tbl->size))
 | 
				
			||||||
		rhashtable_expand(ht);
 | 
							rhashtable_expand(ht);
 | 
				
			||||||
	else if (ht->p.shrink_decision && ht->p.shrink_decision(ht, tbl->size))
 | 
						else if (ht->p.shrink_decision && ht->p.shrink_decision(ht, tbl->size))
 | 
				
			||||||
| 
						 | 
					@ -822,6 +826,164 @@ bool rhashtable_lookup_compare_insert(struct rhashtable *ht,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(rhashtable_lookup_compare_insert);
 | 
					EXPORT_SYMBOL_GPL(rhashtable_lookup_compare_insert);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * rhashtable_walk_init - Initialise an iterator
 | 
				
			||||||
 | 
					 * @ht:		Table to walk over
 | 
				
			||||||
 | 
					 * @iter:	Hash table Iterator
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This function prepares a hash table walk.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Note that if you restart a walk after rhashtable_walk_stop you
 | 
				
			||||||
 | 
					 * may see the same object twice.  Also, you may miss objects if
 | 
				
			||||||
 | 
					 * there are removals in between rhashtable_walk_stop and the next
 | 
				
			||||||
 | 
					 * call to rhashtable_walk_start.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * For a completely stable walk you should construct your own data
 | 
				
			||||||
 | 
					 * structure outside the hash table.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This function may sleep so you must not call it from interrupt
 | 
				
			||||||
 | 
					 * context or with spin locks held.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You must call rhashtable_walk_exit if this function returns
 | 
				
			||||||
 | 
					 * successfully.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						iter->ht = ht;
 | 
				
			||||||
 | 
						iter->p = NULL;
 | 
				
			||||||
 | 
						iter->slot = 0;
 | 
				
			||||||
 | 
						iter->skip = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						iter->walker = kmalloc(sizeof(*iter->walker), GFP_KERNEL);
 | 
				
			||||||
 | 
						if (!iter->walker)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mutex_lock(&ht->mutex);
 | 
				
			||||||
 | 
						list_add(&iter->walker->list, &ht->walkers);
 | 
				
			||||||
 | 
						mutex_unlock(&ht->mutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(rhashtable_walk_init);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * rhashtable_walk_exit - Free an iterator
 | 
				
			||||||
 | 
					 * @iter:	Hash table Iterator
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This function frees resources allocated by rhashtable_walk_init.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void rhashtable_walk_exit(struct rhashtable_iter *iter)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						mutex_lock(&iter->ht->mutex);
 | 
				
			||||||
 | 
						list_del(&iter->walker->list);
 | 
				
			||||||
 | 
						mutex_unlock(&iter->ht->mutex);
 | 
				
			||||||
 | 
						kfree(iter->walker);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(rhashtable_walk_exit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * rhashtable_walk_start - Start a hash table walk
 | 
				
			||||||
 | 
					 * @iter:	Hash table iterator
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Start a hash table walk.  Note that we take the RCU lock in all
 | 
				
			||||||
 | 
					 * cases including when we return an error.  So you must always call
 | 
				
			||||||
 | 
					 * rhashtable_walk_stop to clean up.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns zero if successful.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns -EAGAIN if resize event occured.  Note that the iterator
 | 
				
			||||||
 | 
					 * will rewind back to the beginning and you may use it immediately
 | 
				
			||||||
 | 
					 * by calling rhashtable_walk_next.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int rhashtable_walk_start(struct rhashtable_iter *iter)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						rcu_read_lock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (iter->walker->resize) {
 | 
				
			||||||
 | 
							iter->slot = 0;
 | 
				
			||||||
 | 
							iter->skip = 0;
 | 
				
			||||||
 | 
							iter->walker->resize = false;
 | 
				
			||||||
 | 
							return -EAGAIN;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(rhashtable_walk_start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * rhashtable_walk_next - Return the next object and advance the iterator
 | 
				
			||||||
 | 
					 * @iter:	Hash table iterator
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Note that you must call rhashtable_walk_stop when you are finished
 | 
				
			||||||
 | 
					 * with the walk.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns the next object or NULL when the end of the table is reached.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns -EAGAIN if resize event occured.  Note that the iterator
 | 
				
			||||||
 | 
					 * will rewind back to the beginning and you may continue to use it.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void *rhashtable_walk_next(struct rhashtable_iter *iter)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct bucket_table *tbl;
 | 
				
			||||||
 | 
						struct rhashtable *ht = iter->ht;
 | 
				
			||||||
 | 
						struct rhash_head *p = iter->p;
 | 
				
			||||||
 | 
						void *obj = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tbl = rht_dereference_rcu(ht->tbl, ht);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (p) {
 | 
				
			||||||
 | 
							p = rht_dereference_bucket_rcu(p->next, tbl, iter->slot);
 | 
				
			||||||
 | 
							goto next;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (; iter->slot < tbl->size; iter->slot++) {
 | 
				
			||||||
 | 
							int skip = iter->skip;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							rht_for_each_rcu(p, tbl, iter->slot) {
 | 
				
			||||||
 | 
								if (!skip)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								skip--;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					next:
 | 
				
			||||||
 | 
							if (!rht_is_a_nulls(p)) {
 | 
				
			||||||
 | 
								iter->skip++;
 | 
				
			||||||
 | 
								iter->p = p;
 | 
				
			||||||
 | 
								obj = rht_obj(ht, p);
 | 
				
			||||||
 | 
								goto out;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							iter->skip = 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						iter->p = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out:
 | 
				
			||||||
 | 
						if (iter->walker->resize) {
 | 
				
			||||||
 | 
							iter->p = NULL;
 | 
				
			||||||
 | 
							iter->slot = 0;
 | 
				
			||||||
 | 
							iter->skip = 0;
 | 
				
			||||||
 | 
							iter->walker->resize = false;
 | 
				
			||||||
 | 
							return ERR_PTR(-EAGAIN);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return obj;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(rhashtable_walk_next);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * rhashtable_walk_stop - Finish a hash table walk
 | 
				
			||||||
 | 
					 * @iter:	Hash table iterator
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Finish a hash table walk.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void rhashtable_walk_stop(struct rhashtable_iter *iter)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						rcu_read_unlock();
 | 
				
			||||||
 | 
						iter->p = NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL_GPL(rhashtable_walk_stop);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static size_t rounded_hashtable_size(struct rhashtable_params *params)
 | 
					static size_t rounded_hashtable_size(struct rhashtable_params *params)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return max(roundup_pow_of_two(params->nelem_hint * 4 / 3),
 | 
						return max(roundup_pow_of_two(params->nelem_hint * 4 / 3),
 | 
				
			||||||
| 
						 | 
					@ -894,6 +1056,7 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params)
 | 
				
			||||||
	memset(ht, 0, sizeof(*ht));
 | 
						memset(ht, 0, sizeof(*ht));
 | 
				
			||||||
	mutex_init(&ht->mutex);
 | 
						mutex_init(&ht->mutex);
 | 
				
			||||||
	memcpy(&ht->p, params, sizeof(*params));
 | 
						memcpy(&ht->p, params, sizeof(*params));
 | 
				
			||||||
 | 
						INIT_LIST_HEAD(&ht->walkers);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (params->locks_mul)
 | 
						if (params->locks_mul)
 | 
				
			||||||
		ht->p.locks_mul = roundup_pow_of_two(params->locks_mul);
 | 
							ht->p.locks_mul = roundup_pow_of_two(params->locks_mul);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue