Skip to content

Commit 7e1e776

Browse files
tgrafdavem330
authored andcommitted
lib: Resizable, Scalable, Concurrent Hash Table
Generic implementation of a resizable, scalable, concurrent hash table based on [0]. The implementation supports both, fixed size keys specified via an offset and length, or arbitrary keys via own hash and compare functions. Lookups are lockless and protected as RCU read side critical sections. Automatic growing/shrinking based on user configurable watermarks is available while allowing concurrent lookups to take place. Objects to be hashed must include a struct rhash_head. The reason for not using the existing struct hlist_head is that the expansion and shrinking will have two buckets point to a single entry which would lead in obscure reverse chaining behaviour. Code includes a boot selftest if CONFIG_TEST_RHASHTABLE is defined. [0] https://www.usenix.org/legacy/event/atc11/tech/final_files/Triplett.pdf Signed-off-by: Thomas Graf <tgraf@suug.ch> Reviewed-by: Nikolay Aleksandrov <nikolay@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent d39a9ff commit 7e1e776

File tree

4 files changed

+1019
-1
lines changed

4 files changed

+1019
-1
lines changed

include/linux/rhashtable.h

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
/*
2+
* Resizable, Scalable, Concurrent Hash Table
3+
*
4+
* Copyright (c) 2014 Thomas Graf <tgraf@suug.ch>
5+
* Copyright (c) 2008-2014 Patrick McHardy <kaber@trash.net>
6+
*
7+
* Based on the following paper by Josh Triplett, Paul E. McKenney
8+
* and Jonathan Walpole:
9+
* https://www.usenix.org/legacy/event/atc11/tech/final_files/Triplett.pdf
10+
*
11+
* Code partially derived from nft_hash
12+
*
13+
* This program is free software; you can redistribute it and/or modify
14+
* it under the terms of the GNU General Public License version 2 as
15+
* published by the Free Software Foundation.
16+
*/
17+
18+
#ifndef _LINUX_RHASHTABLE_H
19+
#define _LINUX_RHASHTABLE_H
20+
21+
#include <linux/rculist.h>
22+
23+
struct rhash_head {
24+
struct rhash_head *next;
25+
};
26+
27+
#define INIT_HASH_HEAD(ptr) ((ptr)->next = NULL)
28+
29+
struct bucket_table {
30+
size_t size;
31+
struct rhash_head __rcu *buckets[];
32+
};
33+
34+
typedef u32 (*rht_hashfn_t)(const void *data, u32 len, u32 seed);
35+
typedef u32 (*rht_obj_hashfn_t)(const void *data, u32 seed);
36+
37+
struct rhashtable;
38+
39+
/**
40+
* struct rhashtable_params - Hash table construction parameters
41+
* @nelem_hint: Hint on number of elements, should be 75% of desired size
42+
* @key_len: Length of key
43+
* @key_offset: Offset of key in struct to be hashed
44+
* @head_offset: Offset of rhash_head in struct to be hashed
45+
* @hash_rnd: Seed to use while hashing
46+
* @max_shift: Maximum number of shifts while expanding
47+
* @hashfn: Function to hash key
48+
* @obj_hashfn: Function to hash object
49+
* @grow_decision: If defined, may return true if table should expand
50+
* @shrink_decision: If defined, may return true if table should shrink
51+
* @mutex_is_held: Must return true if protecting mutex is held
52+
*/
53+
struct rhashtable_params {
54+
size_t nelem_hint;
55+
size_t key_len;
56+
size_t key_offset;
57+
size_t head_offset;
58+
u32 hash_rnd;
59+
size_t max_shift;
60+
rht_hashfn_t hashfn;
61+
rht_obj_hashfn_t obj_hashfn;
62+
bool (*grow_decision)(const struct rhashtable *ht,
63+
size_t new_size);
64+
bool (*shrink_decision)(const struct rhashtable *ht,
65+
size_t new_size);
66+
int (*mutex_is_held)(void);
67+
};
68+
69+
/**
70+
* struct rhashtable - Hash table handle
71+
* @tbl: Bucket table
72+
* @nelems: Number of elements in table
73+
* @shift: Current size (1 << shift)
74+
* @p: Configuration parameters
75+
*/
76+
struct rhashtable {
77+
struct bucket_table __rcu *tbl;
78+
size_t nelems;
79+
size_t shift;
80+
struct rhashtable_params p;
81+
};
82+
83+
#ifdef CONFIG_PROVE_LOCKING
84+
int lockdep_rht_mutex_is_held(const struct rhashtable *ht);
85+
#else
86+
static inline int lockdep_rht_mutex_is_held(const struct rhashtable *ht)
87+
{
88+
return 1;
89+
}
90+
#endif /* CONFIG_PROVE_LOCKING */
91+
92+
int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params);
93+
94+
u32 rhashtable_hashfn(const struct rhashtable *ht, const void *key, u32 len);
95+
u32 rhashtable_obj_hashfn(const struct rhashtable *ht, void *ptr);
96+
97+
void rhashtable_insert(struct rhashtable *ht, struct rhash_head *node, gfp_t);
98+
bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *node, gfp_t);
99+
void rhashtable_remove_pprev(struct rhashtable *ht, struct rhash_head *obj,
100+
struct rhash_head **pprev, gfp_t flags);
101+
102+
bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size);
103+
bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size);
104+
105+
int rhashtable_expand(struct rhashtable *ht, gfp_t flags);
106+
int rhashtable_shrink(struct rhashtable *ht, gfp_t flags);
107+
108+
void *rhashtable_lookup(const struct rhashtable *ht, const void *key);
109+
void *rhashtable_lookup_compare(const struct rhashtable *ht, u32 hash,
110+
bool (*compare)(void *, void *), void *arg);
111+
112+
void rhashtable_destroy(const struct rhashtable *ht);
113+
114+
#define rht_dereference(p, ht) \
115+
rcu_dereference_protected(p, lockdep_rht_mutex_is_held(ht))
116+
117+
#define rht_dereference_rcu(p, ht) \
118+
rcu_dereference_check(p, lockdep_rht_mutex_is_held(ht))
119+
120+
/* Internal, use rht_obj() instead */
121+
#define rht_entry(ptr, type, member) container_of(ptr, type, member)
122+
#define rht_entry_safe(ptr, type, member) \
123+
({ \
124+
typeof(ptr) __ptr = (ptr); \
125+
__ptr ? rht_entry(__ptr, type, member) : NULL; \
126+
})
127+
#define rht_entry_safe_rcu(ptr, type, member) \
128+
({ \
129+
typeof(*ptr) __rcu *__ptr = (typeof(*ptr) __rcu __force *)ptr; \
130+
__ptr ? container_of((typeof(ptr))rcu_dereference_raw(__ptr), type, member) : NULL; \
131+
})
132+
133+
#define rht_next_entry_safe(pos, ht, member) \
134+
({ \
135+
pos ? rht_entry_safe(rht_dereference((pos)->member.next, ht), \
136+
typeof(*(pos)), member) : NULL; \
137+
})
138+
139+
/**
140+
* rht_for_each - iterate over hash chain
141+
* @pos: &struct rhash_head to use as a loop cursor.
142+
* @head: head of the hash chain (struct rhash_head *)
143+
* @ht: pointer to your struct rhashtable
144+
*/
145+
#define rht_for_each(pos, head, ht) \
146+
for (pos = rht_dereference(head, ht); \
147+
pos; \
148+
pos = rht_dereference((pos)->next, ht))
149+
150+
/**
151+
* rht_for_each_entry - iterate over hash chain of given type
152+
* @pos: type * to use as a loop cursor.
153+
* @head: head of the hash chain (struct rhash_head *)
154+
* @ht: pointer to your struct rhashtable
155+
* @member: name of the rhash_head within the hashable struct.
156+
*/
157+
#define rht_for_each_entry(pos, head, ht, member) \
158+
for (pos = rht_entry_safe(rht_dereference(head, ht), \
159+
typeof(*(pos)), member); \
160+
pos; \
161+
pos = rht_next_entry_safe(pos, ht, member))
162+
163+
/**
164+
* rht_for_each_entry_safe - safely iterate over hash chain of given type
165+
* @pos: type * to use as a loop cursor.
166+
* @n: type * to use for temporary next object storage
167+
* @head: head of the hash chain (struct rhash_head *)
168+
* @ht: pointer to your struct rhashtable
169+
* @member: name of the rhash_head within the hashable struct.
170+
*
171+
* This hash chain list-traversal primitive allows for the looped code to
172+
* remove the loop cursor from the list.
173+
*/
174+
#define rht_for_each_entry_safe(pos, n, head, ht, member) \
175+
for (pos = rht_entry_safe(rht_dereference(head, ht), \
176+
typeof(*(pos)), member), \
177+
n = rht_next_entry_safe(pos, ht, member); \
178+
pos; \
179+
pos = n, \
180+
n = rht_next_entry_safe(pos, ht, member))
181+
182+
/**
183+
* rht_for_each_rcu - iterate over rcu hash chain
184+
* @pos: &struct rhash_head to use as a loop cursor.
185+
* @head: head of the hash chain (struct rhash_head *)
186+
* @ht: pointer to your struct rhashtable
187+
*
188+
* This hash chain list-traversal primitive may safely run concurrently with
189+
* the _rcu fkht mutation primitives such as rht_insert() as long as the
190+
* traversal is guarded by rcu_read_lock().
191+
*/
192+
#define rht_for_each_rcu(pos, head, ht) \
193+
for (pos = rht_dereference_rcu(head, ht); \
194+
pos; \
195+
pos = rht_dereference_rcu((pos)->next, ht))
196+
197+
/**
198+
* rht_for_each_entry_rcu - iterate over rcu hash chain of given type
199+
* @pos: type * to use as a loop cursor.
200+
* @head: head of the hash chain (struct rhash_head *)
201+
* @member: name of the rhash_head within the hashable struct.
202+
*
203+
* This hash chain list-traversal primitive may safely run concurrently with
204+
* the _rcu fkht mutation primitives such as rht_insert() as long as the
205+
* traversal is guarded by rcu_read_lock().
206+
*/
207+
#define rht_for_each_entry_rcu(pos, head, member) \
208+
for (pos = rht_entry_safe_rcu(head, typeof(*(pos)), member); \
209+
pos; \
210+
pos = rht_entry_safe_rcu((pos)->member.next, \
211+
typeof(*(pos)), member))
212+
213+
#endif /* _LINUX_RHASHTABLE_H */

lib/Kconfig.debug

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,6 +1550,14 @@ config TEST_STRING_HELPERS
15501550
config TEST_KSTRTOX
15511551
tristate "Test kstrto*() family of functions at runtime"
15521552

1553+
config TEST_RHASHTABLE
1554+
bool "Perform selftest on resizable hash table"
1555+
default n
1556+
help
1557+
Enable this option to test the rhashtable functions at boot.
1558+
1559+
If unsure, say N.
1560+
15531561
endmenu # runtime tests
15541562

15551563
config PROVIDE_OHCI1394_DMA_INIT

lib/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
2626
bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
2727
gcd.o lcm.o list_sort.o uuid.o flex_array.o iovec.o clz_ctz.o \
2828
bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o kfifo.o \
29-
percpu-refcount.o percpu_ida.o hash.o
29+
percpu-refcount.o percpu_ida.o hash.o rhashtable.o
3030
obj-y += string_helpers.o
3131
obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o
3232
obj-y += kstrtox.o

0 commit comments

Comments
 (0)