summaryrefslogtreecommitdiffstats
path: root/lib/rhashtable.c
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2015-03-23 09:50:26 -0400
committerDavid S. Miller <davem@davemloft.net>2015-03-23 22:07:52 -0400
commitb824478b2145be78ac19e1cf44e2b9036c7a9608 (patch)
tree19c7d4ad479fd987eef5fd9b6063df27fd26012a /lib/rhashtable.c
parent18093d1c0d1e032142ee24825678b0a8977d74ba (diff)
rhashtable: Add multiple rehash support
This patch adds the missing bits to allow multiple rehashes. The read-side as well as remove already handle this correctly. So it's only the rehasher and insertion that need modification to handle this. Note that this patch doesn't actually enable it so for now rehashing is still only performed by the worker thread. This patch also disables the explicit expand/shrink interface because the table is meant to expand and shrink automatically, and continuing to export these interfaces unnecessarily complicates the life of the rehasher since the rehash process is now composed of two parts. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Acked-by: Thomas Graf <tgraf@suug.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'lib/rhashtable.c')
-rw-r--r--lib/rhashtable.c87
1 files changed, 72 insertions, 15 deletions
diff --git a/lib/rhashtable.c b/lib/rhashtable.c
index 9623be345d9c..5e04403e25f5 100644
--- a/lib/rhashtable.c
+++ b/lib/rhashtable.c
@@ -136,11 +136,24 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,
136 return tbl; 136 return tbl;
137} 137}
138 138
139static struct bucket_table *rhashtable_last_table(struct rhashtable *ht,
140 struct bucket_table *tbl)
141{
142 struct bucket_table *new_tbl;
143
144 do {
145 new_tbl = tbl;
146 tbl = rht_dereference_rcu(tbl->future_tbl, ht);
147 } while (tbl);
148
149 return new_tbl;
150}
151
139static int rhashtable_rehash_one(struct rhashtable *ht, unsigned old_hash) 152static int rhashtable_rehash_one(struct rhashtable *ht, unsigned old_hash)
140{ 153{
141 struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht); 154 struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht);
142 struct bucket_table *new_tbl = 155 struct bucket_table *new_tbl = rhashtable_last_table(ht,
143 rht_dereference(old_tbl->future_tbl, ht) ?: old_tbl; 156 rht_dereference_rcu(old_tbl->future_tbl, ht));
144 struct rhash_head __rcu **pprev = &old_tbl->buckets[old_hash]; 157 struct rhash_head __rcu **pprev = &old_tbl->buckets[old_hash];
145 int err = -ENOENT; 158 int err = -ENOENT;
146 struct rhash_head *head, *next, *entry; 159 struct rhash_head *head, *next, *entry;
@@ -196,12 +209,18 @@ static void rhashtable_rehash_chain(struct rhashtable *ht, unsigned old_hash)
196 spin_unlock_bh(old_bucket_lock); 209 spin_unlock_bh(old_bucket_lock);
197} 210}
198 211
199static void rhashtable_rehash(struct rhashtable *ht, 212static int rhashtable_rehash_attach(struct rhashtable *ht,
200 struct bucket_table *new_tbl) 213 struct bucket_table *old_tbl,
214 struct bucket_table *new_tbl)
201{ 215{
202 struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht); 216 /* Protect future_tbl using the first bucket lock. */
203 struct rhashtable_walker *walker; 217 spin_lock_bh(old_tbl->locks);
204 unsigned old_hash; 218
219 /* Did somebody beat us to it? */
220 if (rcu_access_pointer(old_tbl->future_tbl)) {
221 spin_unlock_bh(old_tbl->locks);
222 return -EEXIST;
223 }
205 224
206 /* Make insertions go into the new, empty table right away. Deletions 225 /* Make insertions go into the new, empty table right away. Deletions
207 * and lookups will be attempted in both tables until we synchronize. 226 * and lookups will be attempted in both tables until we synchronize.
@@ -211,6 +230,22 @@ static void rhashtable_rehash(struct rhashtable *ht,
211 /* Ensure the new table is visible to readers. */ 230 /* Ensure the new table is visible to readers. */
212 smp_wmb(); 231 smp_wmb();
213 232
233 spin_unlock_bh(old_tbl->locks);
234
235 return 0;
236}
237
238static int rhashtable_rehash_table(struct rhashtable *ht)
239{
240 struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht);
241 struct bucket_table *new_tbl;
242 struct rhashtable_walker *walker;
243 unsigned old_hash;
244
245 new_tbl = rht_dereference(old_tbl->future_tbl, ht);
246 if (!new_tbl)
247 return 0;
248
214 for (old_hash = 0; old_hash < old_tbl->size; old_hash++) 249 for (old_hash = 0; old_hash < old_tbl->size; old_hash++)
215 rhashtable_rehash_chain(ht, old_hash); 250 rhashtable_rehash_chain(ht, old_hash);
216 251
@@ -225,6 +260,8 @@ static void rhashtable_rehash(struct rhashtable *ht,
225 * remain. 260 * remain.
226 */ 261 */
227 call_rcu(&old_tbl->rcu, bucket_table_free_rcu); 262 call_rcu(&old_tbl->rcu, bucket_table_free_rcu);
263
264 return rht_dereference(new_tbl->future_tbl, ht) ? -EAGAIN : 0;
228} 265}
229 266
230/** 267/**
@@ -242,20 +279,25 @@ static void rhashtable_rehash(struct rhashtable *ht,
242 * It is valid to have concurrent insertions and deletions protected by per 279 * It is valid to have concurrent insertions and deletions protected by per
243 * bucket locks or concurrent RCU protected lookups and traversals. 280 * bucket locks or concurrent RCU protected lookups and traversals.
244 */ 281 */
245int rhashtable_expand(struct rhashtable *ht) 282static int rhashtable_expand(struct rhashtable *ht)
246{ 283{
247 struct bucket_table *new_tbl, *old_tbl = rht_dereference(ht->tbl, ht); 284 struct bucket_table *new_tbl, *old_tbl = rht_dereference(ht->tbl, ht);
285 int err;
248 286
249 ASSERT_RHT_MUTEX(ht); 287 ASSERT_RHT_MUTEX(ht);
250 288
289 old_tbl = rhashtable_last_table(ht, old_tbl);
290
251 new_tbl = bucket_table_alloc(ht, old_tbl->size * 2); 291 new_tbl = bucket_table_alloc(ht, old_tbl->size * 2);
252 if (new_tbl == NULL) 292 if (new_tbl == NULL)
253 return -ENOMEM; 293 return -ENOMEM;
254 294
255 rhashtable_rehash(ht, new_tbl); 295 err = rhashtable_rehash_attach(ht, old_tbl, new_tbl);
256 return 0; 296 if (err)
297 bucket_table_free(new_tbl);
298
299 return err;
257} 300}
258EXPORT_SYMBOL_GPL(rhashtable_expand);
259 301
260/** 302/**
261 * rhashtable_shrink - Shrink hash table while allowing concurrent lookups 303 * rhashtable_shrink - Shrink hash table while allowing concurrent lookups
@@ -273,10 +315,11 @@ EXPORT_SYMBOL_GPL(rhashtable_expand);
273 * It is valid to have concurrent insertions and deletions protected by per 315 * It is valid to have concurrent insertions and deletions protected by per
274 * bucket locks or concurrent RCU protected lookups and traversals. 316 * bucket locks or concurrent RCU protected lookups and traversals.
275 */ 317 */
276int rhashtable_shrink(struct rhashtable *ht) 318static int rhashtable_shrink(struct rhashtable *ht)
277{ 319{
278 struct bucket_table *new_tbl, *old_tbl = rht_dereference(ht->tbl, ht); 320 struct bucket_table *new_tbl, *old_tbl = rht_dereference(ht->tbl, ht);
279 unsigned size = roundup_pow_of_two(atomic_read(&ht->nelems) * 3 / 2); 321 unsigned size = roundup_pow_of_two(atomic_read(&ht->nelems) * 3 / 2);
322 int err;
280 323
281 ASSERT_RHT_MUTEX(ht); 324 ASSERT_RHT_MUTEX(ht);
282 325
@@ -286,19 +329,25 @@ int rhashtable_shrink(struct rhashtable *ht)
286 if (old_tbl->size <= size) 329 if (old_tbl->size <= size)
287 return 0; 330 return 0;
288 331
332 if (rht_dereference(old_tbl->future_tbl, ht))
333 return -EEXIST;
334
289 new_tbl = bucket_table_alloc(ht, size); 335 new_tbl = bucket_table_alloc(ht, size);
290 if (new_tbl == NULL) 336 if (new_tbl == NULL)
291 return -ENOMEM; 337 return -ENOMEM;
292 338
293 rhashtable_rehash(ht, new_tbl); 339 err = rhashtable_rehash_attach(ht, old_tbl, new_tbl);
294 return 0; 340 if (err)
341 bucket_table_free(new_tbl);
342
343 return err;
295} 344}
296EXPORT_SYMBOL_GPL(rhashtable_shrink);
297 345
298static void rht_deferred_worker(struct work_struct *work) 346static void rht_deferred_worker(struct work_struct *work)
299{ 347{
300 struct rhashtable *ht; 348 struct rhashtable *ht;
301 struct bucket_table *tbl; 349 struct bucket_table *tbl;
350 int err = 0;
302 351
303 ht = container_of(work, struct rhashtable, run_work); 352 ht = container_of(work, struct rhashtable, run_work);
304 mutex_lock(&ht->mutex); 353 mutex_lock(&ht->mutex);
@@ -306,13 +355,20 @@ static void rht_deferred_worker(struct work_struct *work)
306 goto unlock; 355 goto unlock;
307 356
308 tbl = rht_dereference(ht->tbl, ht); 357 tbl = rht_dereference(ht->tbl, ht);
358 tbl = rhashtable_last_table(ht, tbl);
309 359
310 if (rht_grow_above_75(ht, tbl)) 360 if (rht_grow_above_75(ht, tbl))
311 rhashtable_expand(ht); 361 rhashtable_expand(ht);
312 else if (rht_shrink_below_30(ht, tbl)) 362 else if (rht_shrink_below_30(ht, tbl))
313 rhashtable_shrink(ht); 363 rhashtable_shrink(ht);
364
365 err = rhashtable_rehash_table(ht);
366
314unlock: 367unlock:
315 mutex_unlock(&ht->mutex); 368 mutex_unlock(&ht->mutex);
369
370 if (err)
371 schedule_work(&ht->run_work);
316} 372}
317 373
318int rhashtable_insert_slow(struct rhashtable *ht, const void *key, 374int rhashtable_insert_slow(struct rhashtable *ht, const void *key,
@@ -323,6 +379,7 @@ int rhashtable_insert_slow(struct rhashtable *ht, const void *key,
323 unsigned hash; 379 unsigned hash;
324 int err = -EEXIST; 380 int err = -EEXIST;
325 381
382 tbl = rhashtable_last_table(ht, tbl);
326 hash = head_hashfn(ht, tbl, obj); 383 hash = head_hashfn(ht, tbl, obj);
327 spin_lock_nested(rht_bucket_lock(tbl, hash), SINGLE_DEPTH_NESTING); 384 spin_lock_nested(rht_bucket_lock(tbl, hash), SINGLE_DEPTH_NESTING);
328 385