Skip to content

Commit f2dba9c

Browse files
herbertxdavem330
authored andcommitted
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>
1 parent 28134a5 commit f2dba9c

File tree

2 files changed

+198
-0
lines changed

2 files changed

+198
-0
lines changed

include/linux/rhashtable.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#ifndef _LINUX_RHASHTABLE_H
1919
#define _LINUX_RHASHTABLE_H
2020

21+
#include <linux/compiler.h>
2122
#include <linux/list_nulls.h>
2223
#include <linux/workqueue.h>
2324
#include <linux/mutex.h>
@@ -111,6 +112,7 @@ struct rhashtable_params {
111112
* @p: Configuration parameters
112113
* @run_work: Deferred worker to expand/shrink asynchronously
113114
* @mutex: Mutex to protect current/future table swapping
115+
* @walkers: List of active walkers
114116
* @being_destroyed: True if table is set up for destruction
115117
*/
116118
struct rhashtable {
@@ -121,9 +123,36 @@ struct rhashtable {
121123
struct rhashtable_params p;
122124
struct work_struct run_work;
123125
struct mutex mutex;
126+
struct list_head walkers;
124127
bool being_destroyed;
125128
};
126129

130+
/**
131+
* struct rhashtable_walker - Hash table walker
132+
* @list: List entry on list of walkers
133+
* @resize: Resize event occured
134+
*/
135+
struct rhashtable_walker {
136+
struct list_head list;
137+
bool resize;
138+
};
139+
140+
/**
141+
* struct rhashtable_iter - Hash table iterator, fits into netlink cb
142+
* @ht: Table to iterate through
143+
* @p: Current pointer
144+
* @walker: Associated rhashtable walker
145+
* @slot: Current slot
146+
* @skip: Number of entries to skip in slot
147+
*/
148+
struct rhashtable_iter {
149+
struct rhashtable *ht;
150+
struct rhash_head *p;
151+
struct rhashtable_walker *walker;
152+
unsigned int slot;
153+
unsigned int skip;
154+
};
155+
127156
static inline unsigned long rht_marker(const struct rhashtable *ht, u32 hash)
128157
{
129158
return NULLS_MARKER(ht->p.nulls_base + hash);
@@ -179,6 +208,12 @@ bool rhashtable_lookup_compare_insert(struct rhashtable *ht,
179208
bool (*compare)(void *, void *),
180209
void *arg);
181210

211+
int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter);
212+
void rhashtable_walk_exit(struct rhashtable_iter *iter);
213+
int rhashtable_walk_start(struct rhashtable_iter *iter) __acquires(RCU);
214+
void *rhashtable_walk_next(struct rhashtable_iter *iter);
215+
void rhashtable_walk_stop(struct rhashtable_iter *iter) __releases(RCU);
216+
182217
void rhashtable_destroy(struct rhashtable *ht);
183218

184219
#define rht_dereference(p, ht) \

lib/rhashtable.c

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ static void rht_deferred_worker(struct work_struct *work)
484484
{
485485
struct rhashtable *ht;
486486
struct bucket_table *tbl;
487+
struct rhashtable_walker *walker;
487488

488489
ht = container_of(work, struct rhashtable, run_work);
489490
mutex_lock(&ht->mutex);
@@ -492,6 +493,9 @@ static void rht_deferred_worker(struct work_struct *work)
492493

493494
tbl = rht_dereference(ht->tbl, ht);
494495

496+
list_for_each_entry(walker, &ht->walkers, list)
497+
walker->resize = true;
498+
495499
if (ht->p.grow_decision && ht->p.grow_decision(ht, tbl->size))
496500
rhashtable_expand(ht);
497501
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,
822826
}
823827
EXPORT_SYMBOL_GPL(rhashtable_lookup_compare_insert);
824828

829+
/**
830+
* rhashtable_walk_init - Initialise an iterator
831+
* @ht: Table to walk over
832+
* @iter: Hash table Iterator
833+
*
834+
* This function prepares a hash table walk.
835+
*
836+
* Note that if you restart a walk after rhashtable_walk_stop you
837+
* may see the same object twice. Also, you may miss objects if
838+
* there are removals in between rhashtable_walk_stop and the next
839+
* call to rhashtable_walk_start.
840+
*
841+
* For a completely stable walk you should construct your own data
842+
* structure outside the hash table.
843+
*
844+
* This function may sleep so you must not call it from interrupt
845+
* context or with spin locks held.
846+
*
847+
* You must call rhashtable_walk_exit if this function returns
848+
* successfully.
849+
*/
850+
int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter)
851+
{
852+
iter->ht = ht;
853+
iter->p = NULL;
854+
iter->slot = 0;
855+
iter->skip = 0;
856+
857+
iter->walker = kmalloc(sizeof(*iter->walker), GFP_KERNEL);
858+
if (!iter->walker)
859+
return -ENOMEM;
860+
861+
mutex_lock(&ht->mutex);
862+
list_add(&iter->walker->list, &ht->walkers);
863+
mutex_unlock(&ht->mutex);
864+
865+
return 0;
866+
}
867+
EXPORT_SYMBOL_GPL(rhashtable_walk_init);
868+
869+
/**
870+
* rhashtable_walk_exit - Free an iterator
871+
* @iter: Hash table Iterator
872+
*
873+
* This function frees resources allocated by rhashtable_walk_init.
874+
*/
875+
void rhashtable_walk_exit(struct rhashtable_iter *iter)
876+
{
877+
mutex_lock(&iter->ht->mutex);
878+
list_del(&iter->walker->list);
879+
mutex_unlock(&iter->ht->mutex);
880+
kfree(iter->walker);
881+
}
882+
EXPORT_SYMBOL_GPL(rhashtable_walk_exit);
883+
884+
/**
885+
* rhashtable_walk_start - Start a hash table walk
886+
* @iter: Hash table iterator
887+
*
888+
* Start a hash table walk. Note that we take the RCU lock in all
889+
* cases including when we return an error. So you must always call
890+
* rhashtable_walk_stop to clean up.
891+
*
892+
* Returns zero if successful.
893+
*
894+
* Returns -EAGAIN if resize event occured. Note that the iterator
895+
* will rewind back to the beginning and you may use it immediately
896+
* by calling rhashtable_walk_next.
897+
*/
898+
int rhashtable_walk_start(struct rhashtable_iter *iter)
899+
{
900+
rcu_read_lock();
901+
902+
if (iter->walker->resize) {
903+
iter->slot = 0;
904+
iter->skip = 0;
905+
iter->walker->resize = false;
906+
return -EAGAIN;
907+
}
908+
909+
return 0;
910+
}
911+
EXPORT_SYMBOL_GPL(rhashtable_walk_start);
912+
913+
/**
914+
* rhashtable_walk_next - Return the next object and advance the iterator
915+
* @iter: Hash table iterator
916+
*
917+
* Note that you must call rhashtable_walk_stop when you are finished
918+
* with the walk.
919+
*
920+
* Returns the next object or NULL when the end of the table is reached.
921+
*
922+
* Returns -EAGAIN if resize event occured. Note that the iterator
923+
* will rewind back to the beginning and you may continue to use it.
924+
*/
925+
void *rhashtable_walk_next(struct rhashtable_iter *iter)
926+
{
927+
const struct bucket_table *tbl;
928+
struct rhashtable *ht = iter->ht;
929+
struct rhash_head *p = iter->p;
930+
void *obj = NULL;
931+
932+
tbl = rht_dereference_rcu(ht->tbl, ht);
933+
934+
if (p) {
935+
p = rht_dereference_bucket_rcu(p->next, tbl, iter->slot);
936+
goto next;
937+
}
938+
939+
for (; iter->slot < tbl->size; iter->slot++) {
940+
int skip = iter->skip;
941+
942+
rht_for_each_rcu(p, tbl, iter->slot) {
943+
if (!skip)
944+
break;
945+
skip--;
946+
}
947+
948+
next:
949+
if (!rht_is_a_nulls(p)) {
950+
iter->skip++;
951+
iter->p = p;
952+
obj = rht_obj(ht, p);
953+
goto out;
954+
}
955+
956+
iter->skip = 0;
957+
}
958+
959+
iter->p = NULL;
960+
961+
out:
962+
if (iter->walker->resize) {
963+
iter->p = NULL;
964+
iter->slot = 0;
965+
iter->skip = 0;
966+
iter->walker->resize = false;
967+
return ERR_PTR(-EAGAIN);
968+
}
969+
970+
return obj;
971+
}
972+
EXPORT_SYMBOL_GPL(rhashtable_walk_next);
973+
974+
/**
975+
* rhashtable_walk_stop - Finish a hash table walk
976+
* @iter: Hash table iterator
977+
*
978+
* Finish a hash table walk.
979+
*/
980+
void rhashtable_walk_stop(struct rhashtable_iter *iter)
981+
{
982+
rcu_read_unlock();
983+
iter->p = NULL;
984+
}
985+
EXPORT_SYMBOL_GPL(rhashtable_walk_stop);
986+
825987
static size_t rounded_hashtable_size(struct rhashtable_params *params)
826988
{
827989
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)
8941056
memset(ht, 0, sizeof(*ht));
8951057
mutex_init(&ht->mutex);
8961058
memcpy(&ht->p, params, sizeof(*params));
1059+
INIT_LIST_HEAD(&ht->walkers);
8971060

8981061
if (params->locks_mul)
8991062
ht->p.locks_mul = roundup_pow_of_two(params->locks_mul);

0 commit comments

Comments
 (0)