.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Resizable, Scalable, Concurrent Hash Table |
---|
3 | 4 | * |
---|
.. | .. |
---|
8 | 9 | * Code partially derived from nft_hash |
---|
9 | 10 | * Rewritten with rehash code from br_multicast plus single list |
---|
10 | 11 | * pointer as suggested by Josh Triplett |
---|
11 | | - * |
---|
12 | | - * This program is free software; you can redistribute it and/or modify |
---|
13 | | - * it under the terms of the GNU General Public License version 2 as |
---|
14 | | - * published by the Free Software Foundation. |
---|
15 | 12 | */ |
---|
16 | 13 | |
---|
17 | 14 | #include <linux/atomic.h> |
---|
.. | .. |
---|
31 | 28 | |
---|
32 | 29 | #define HASH_DEFAULT_SIZE 64UL |
---|
33 | 30 | #define HASH_MIN_SIZE 4U |
---|
34 | | -#define BUCKET_LOCKS_PER_CPU 32UL |
---|
35 | 31 | |
---|
36 | 32 | union nested_table { |
---|
37 | 33 | union nested_table __rcu *table; |
---|
38 | | - struct rhash_head __rcu *bucket; |
---|
| 34 | + struct rhash_lock_head __rcu *bucket; |
---|
39 | 35 | }; |
---|
40 | 36 | |
---|
41 | 37 | static u32 head_hashfn(struct rhashtable *ht, |
---|
.. | .. |
---|
56 | 52 | |
---|
57 | 53 | int lockdep_rht_bucket_is_held(const struct bucket_table *tbl, u32 hash) |
---|
58 | 54 | { |
---|
59 | | - spinlock_t *lock = rht_bucket_lock(tbl, hash); |
---|
60 | | - |
---|
61 | | - return (debug_locks) ? lockdep_is_held(lock) : 1; |
---|
| 55 | + if (!debug_locks) |
---|
| 56 | + return 1; |
---|
| 57 | + if (unlikely(tbl->nest)) |
---|
| 58 | + return 1; |
---|
| 59 | + return bit_spin_is_locked(0, (unsigned long *)&tbl->buckets[hash]); |
---|
62 | 60 | } |
---|
63 | 61 | EXPORT_SYMBOL_GPL(lockdep_rht_bucket_is_held); |
---|
64 | 62 | #else |
---|
65 | 63 | #define ASSERT_RHT_MUTEX(HT) |
---|
66 | 64 | #endif |
---|
| 65 | + |
---|
| 66 | +static inline union nested_table *nested_table_top( |
---|
| 67 | + const struct bucket_table *tbl) |
---|
| 68 | +{ |
---|
| 69 | + /* The top-level bucket entry does not need RCU protection |
---|
| 70 | + * because it's set at the same time as tbl->nest. |
---|
| 71 | + */ |
---|
| 72 | + return (void *)rcu_dereference_protected(tbl->buckets[0], 1); |
---|
| 73 | +} |
---|
67 | 74 | |
---|
68 | 75 | static void nested_table_free(union nested_table *ntbl, unsigned int size) |
---|
69 | 76 | { |
---|
.. | .. |
---|
71 | 78 | const unsigned int len = 1 << shift; |
---|
72 | 79 | unsigned int i; |
---|
73 | 80 | |
---|
74 | | - ntbl = rcu_dereference_raw(ntbl->table); |
---|
| 81 | + ntbl = rcu_dereference_protected(ntbl->table, 1); |
---|
75 | 82 | if (!ntbl) |
---|
76 | 83 | return; |
---|
77 | 84 | |
---|
.. | .. |
---|
91 | 98 | union nested_table *ntbl; |
---|
92 | 99 | unsigned int i; |
---|
93 | 100 | |
---|
94 | | - ntbl = (union nested_table *)rcu_dereference_raw(tbl->buckets[0]); |
---|
| 101 | + ntbl = nested_table_top(tbl); |
---|
95 | 102 | |
---|
96 | 103 | for (i = 0; i < len; i++) |
---|
97 | 104 | nested_table_free(ntbl + i, size); |
---|
.. | .. |
---|
104 | 111 | if (tbl->nest) |
---|
105 | 112 | nested_bucket_table_free(tbl); |
---|
106 | 113 | |
---|
107 | | - free_bucket_spinlocks(tbl->locks); |
---|
108 | 114 | kvfree(tbl); |
---|
109 | 115 | } |
---|
110 | 116 | |
---|
.. | .. |
---|
131 | 137 | INIT_RHT_NULLS_HEAD(ntbl[i].bucket); |
---|
132 | 138 | } |
---|
133 | 139 | |
---|
134 | | - rcu_assign_pointer(*prev, ntbl); |
---|
135 | | - |
---|
136 | | - return ntbl; |
---|
| 140 | + if (cmpxchg((union nested_table **)prev, NULL, ntbl) == NULL) |
---|
| 141 | + return ntbl; |
---|
| 142 | + /* Raced with another thread. */ |
---|
| 143 | + kfree(ntbl); |
---|
| 144 | + return rcu_dereference(*prev); |
---|
137 | 145 | } |
---|
138 | 146 | |
---|
139 | 147 | static struct bucket_table *nested_bucket_table_alloc(struct rhashtable *ht, |
---|
.. | .. |
---|
169 | 177 | gfp_t gfp) |
---|
170 | 178 | { |
---|
171 | 179 | struct bucket_table *tbl = NULL; |
---|
172 | | - size_t size, max_locks; |
---|
| 180 | + size_t size; |
---|
173 | 181 | int i; |
---|
| 182 | + static struct lock_class_key __key; |
---|
174 | 183 | |
---|
175 | | - size = sizeof(*tbl) + nbuckets * sizeof(tbl->buckets[0]); |
---|
176 | | - tbl = kvzalloc(size, gfp); |
---|
| 184 | + tbl = kvzalloc(struct_size(tbl, buckets, nbuckets), gfp); |
---|
177 | 185 | |
---|
178 | 186 | size = nbuckets; |
---|
179 | 187 | |
---|
.. | .. |
---|
185 | 193 | if (tbl == NULL) |
---|
186 | 194 | return NULL; |
---|
187 | 195 | |
---|
| 196 | + lockdep_init_map(&tbl->dep_map, "rhashtable_bucket", &__key, 0); |
---|
| 197 | + |
---|
188 | 198 | tbl->size = size; |
---|
189 | 199 | |
---|
190 | | - max_locks = size >> 1; |
---|
191 | | - if (tbl->nest) |
---|
192 | | - max_locks = min_t(size_t, max_locks, 1U << tbl->nest); |
---|
193 | | - |
---|
194 | | - if (alloc_bucket_spinlocks(&tbl->locks, &tbl->locks_mask, max_locks, |
---|
195 | | - ht->p.locks_mul, gfp) < 0) { |
---|
196 | | - bucket_table_free(tbl); |
---|
197 | | - return NULL; |
---|
198 | | - } |
---|
199 | | - |
---|
| 200 | + rcu_head_init(&tbl->rcu); |
---|
200 | 201 | INIT_LIST_HEAD(&tbl->walkers); |
---|
201 | 202 | |
---|
202 | 203 | tbl->hash_rnd = get_random_u32(); |
---|
.. | .. |
---|
220 | 221 | return new_tbl; |
---|
221 | 222 | } |
---|
222 | 223 | |
---|
223 | | -static int rhashtable_rehash_one(struct rhashtable *ht, unsigned int old_hash) |
---|
| 224 | +static int rhashtable_rehash_one(struct rhashtable *ht, |
---|
| 225 | + struct rhash_lock_head __rcu **bkt, |
---|
| 226 | + unsigned int old_hash) |
---|
224 | 227 | { |
---|
225 | 228 | struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht); |
---|
226 | 229 | struct bucket_table *new_tbl = rhashtable_last_table(ht, old_tbl); |
---|
227 | | - struct rhash_head __rcu **pprev = rht_bucket_var(old_tbl, old_hash); |
---|
228 | 230 | int err = -EAGAIN; |
---|
229 | 231 | struct rhash_head *head, *next, *entry; |
---|
230 | | - spinlock_t *new_bucket_lock; |
---|
| 232 | + struct rhash_head __rcu **pprev = NULL; |
---|
231 | 233 | unsigned int new_hash; |
---|
232 | 234 | |
---|
233 | 235 | if (new_tbl->nest) |
---|
.. | .. |
---|
235 | 237 | |
---|
236 | 238 | err = -ENOENT; |
---|
237 | 239 | |
---|
238 | | - rht_for_each(entry, old_tbl, old_hash) { |
---|
| 240 | + rht_for_each_from(entry, rht_ptr(bkt, old_tbl, old_hash), |
---|
| 241 | + old_tbl, old_hash) { |
---|
239 | 242 | err = 0; |
---|
240 | 243 | next = rht_dereference_bucket(entry->next, old_tbl, old_hash); |
---|
241 | 244 | |
---|
.. | .. |
---|
250 | 253 | |
---|
251 | 254 | new_hash = head_hashfn(ht, new_tbl, entry); |
---|
252 | 255 | |
---|
253 | | - new_bucket_lock = rht_bucket_lock(new_tbl, new_hash); |
---|
| 256 | + rht_lock_nested(new_tbl, &new_tbl->buckets[new_hash], SINGLE_DEPTH_NESTING); |
---|
254 | 257 | |
---|
255 | | - spin_lock_nested(new_bucket_lock, SINGLE_DEPTH_NESTING); |
---|
256 | | - head = rht_dereference_bucket(new_tbl->buckets[new_hash], |
---|
257 | | - new_tbl, new_hash); |
---|
| 258 | + head = rht_ptr(new_tbl->buckets + new_hash, new_tbl, new_hash); |
---|
258 | 259 | |
---|
259 | 260 | RCU_INIT_POINTER(entry->next, head); |
---|
260 | 261 | |
---|
261 | | - rcu_assign_pointer(new_tbl->buckets[new_hash], entry); |
---|
262 | | - spin_unlock(new_bucket_lock); |
---|
| 262 | + rht_assign_unlock(new_tbl, &new_tbl->buckets[new_hash], entry); |
---|
263 | 263 | |
---|
264 | | - rcu_assign_pointer(*pprev, next); |
---|
| 264 | + if (pprev) |
---|
| 265 | + rcu_assign_pointer(*pprev, next); |
---|
| 266 | + else |
---|
| 267 | + /* Need to preserved the bit lock. */ |
---|
| 268 | + rht_assign_locked(bkt, next); |
---|
265 | 269 | |
---|
266 | 270 | out: |
---|
267 | 271 | return err; |
---|
.. | .. |
---|
271 | 275 | unsigned int old_hash) |
---|
272 | 276 | { |
---|
273 | 277 | struct bucket_table *old_tbl = rht_dereference(ht->tbl, ht); |
---|
274 | | - spinlock_t *old_bucket_lock; |
---|
| 278 | + struct rhash_lock_head __rcu **bkt = rht_bucket_var(old_tbl, old_hash); |
---|
275 | 279 | int err; |
---|
276 | 280 | |
---|
277 | | - old_bucket_lock = rht_bucket_lock(old_tbl, old_hash); |
---|
| 281 | + if (!bkt) |
---|
| 282 | + return 0; |
---|
| 283 | + rht_lock(old_tbl, bkt); |
---|
278 | 284 | |
---|
279 | | - spin_lock_bh(old_bucket_lock); |
---|
280 | | - while (!(err = rhashtable_rehash_one(ht, old_hash))) |
---|
| 285 | + while (!(err = rhashtable_rehash_one(ht, bkt, old_hash))) |
---|
281 | 286 | ; |
---|
282 | 287 | |
---|
283 | | - if (err == -ENOENT) { |
---|
284 | | - old_tbl->rehash++; |
---|
| 288 | + if (err == -ENOENT) |
---|
285 | 289 | err = 0; |
---|
286 | | - } |
---|
287 | | - spin_unlock_bh(old_bucket_lock); |
---|
| 290 | + rht_unlock(old_tbl, bkt); |
---|
288 | 291 | |
---|
289 | 292 | return err; |
---|
290 | 293 | } |
---|
.. | .. |
---|
299 | 302 | * rcu_assign_pointer(). |
---|
300 | 303 | */ |
---|
301 | 304 | |
---|
302 | | - if (cmpxchg(&old_tbl->future_tbl, NULL, new_tbl) != NULL) |
---|
| 305 | + if (cmpxchg((struct bucket_table **)&old_tbl->future_tbl, NULL, |
---|
| 306 | + new_tbl) != NULL) |
---|
303 | 307 | return -EEXIST; |
---|
304 | 308 | |
---|
305 | 309 | return 0; |
---|
.. | .. |
---|
330 | 334 | spin_lock(&ht->lock); |
---|
331 | 335 | list_for_each_entry(walker, &old_tbl->walkers, list) |
---|
332 | 336 | walker->tbl = NULL; |
---|
333 | | - spin_unlock(&ht->lock); |
---|
334 | 337 | |
---|
335 | 338 | /* Wait for readers. All new readers will see the new |
---|
336 | 339 | * table, and thus no references to the old table will |
---|
337 | 340 | * remain. |
---|
| 341 | + * We do this inside the locked region so that |
---|
| 342 | + * rhashtable_walk_stop() can use rcu_head_after_call_rcu() |
---|
| 343 | + * to check if it should not re-link the table. |
---|
338 | 344 | */ |
---|
339 | 345 | call_rcu(&old_tbl->rcu, bucket_table_free_rcu); |
---|
| 346 | + spin_unlock(&ht->lock); |
---|
340 | 347 | |
---|
341 | 348 | return rht_dereference(new_tbl->future_tbl, ht) ? -EAGAIN : 0; |
---|
342 | 349 | } |
---|
.. | .. |
---|
478 | 485 | } |
---|
479 | 486 | |
---|
480 | 487 | static void *rhashtable_lookup_one(struct rhashtable *ht, |
---|
| 488 | + struct rhash_lock_head __rcu **bkt, |
---|
481 | 489 | struct bucket_table *tbl, unsigned int hash, |
---|
482 | 490 | const void *key, struct rhash_head *obj) |
---|
483 | 491 | { |
---|
.. | .. |
---|
485 | 493 | .ht = ht, |
---|
486 | 494 | .key = key, |
---|
487 | 495 | }; |
---|
488 | | - struct rhash_head __rcu **pprev; |
---|
| 496 | + struct rhash_head __rcu **pprev = NULL; |
---|
489 | 497 | struct rhash_head *head; |
---|
490 | 498 | int elasticity; |
---|
491 | 499 | |
---|
492 | 500 | elasticity = RHT_ELASTICITY; |
---|
493 | | - pprev = rht_bucket_var(tbl, hash); |
---|
494 | | - rht_for_each_continue(head, *pprev, tbl, hash) { |
---|
| 501 | + rht_for_each_from(head, rht_ptr(bkt, tbl, hash), tbl, hash) { |
---|
495 | 502 | struct rhlist_head *list; |
---|
496 | 503 | struct rhlist_head *plist; |
---|
497 | 504 | |
---|
.. | .. |
---|
513 | 520 | RCU_INIT_POINTER(list->next, plist); |
---|
514 | 521 | head = rht_dereference_bucket(head->next, tbl, hash); |
---|
515 | 522 | RCU_INIT_POINTER(list->rhead.next, head); |
---|
516 | | - rcu_assign_pointer(*pprev, obj); |
---|
| 523 | + if (pprev) |
---|
| 524 | + rcu_assign_pointer(*pprev, obj); |
---|
| 525 | + else |
---|
| 526 | + /* Need to preserve the bit lock */ |
---|
| 527 | + rht_assign_locked(bkt, obj); |
---|
517 | 528 | |
---|
518 | 529 | return NULL; |
---|
519 | 530 | } |
---|
.. | .. |
---|
524 | 535 | return ERR_PTR(-ENOENT); |
---|
525 | 536 | } |
---|
526 | 537 | |
---|
527 | | -static struct bucket_table *rhashtable_insert_one(struct rhashtable *ht, |
---|
528 | | - struct bucket_table *tbl, |
---|
529 | | - unsigned int hash, |
---|
530 | | - struct rhash_head *obj, |
---|
531 | | - void *data) |
---|
| 538 | +static struct bucket_table *rhashtable_insert_one( |
---|
| 539 | + struct rhashtable *ht, struct rhash_lock_head __rcu **bkt, |
---|
| 540 | + struct bucket_table *tbl, unsigned int hash, struct rhash_head *obj, |
---|
| 541 | + void *data) |
---|
532 | 542 | { |
---|
533 | | - struct rhash_head __rcu **pprev; |
---|
534 | 543 | struct bucket_table *new_tbl; |
---|
535 | 544 | struct rhash_head *head; |
---|
536 | 545 | |
---|
.. | .. |
---|
553 | 562 | if (unlikely(rht_grow_above_100(ht, tbl))) |
---|
554 | 563 | return ERR_PTR(-EAGAIN); |
---|
555 | 564 | |
---|
556 | | - pprev = rht_bucket_insert(ht, tbl, hash); |
---|
557 | | - if (!pprev) |
---|
558 | | - return ERR_PTR(-ENOMEM); |
---|
559 | | - |
---|
560 | | - head = rht_dereference_bucket(*pprev, tbl, hash); |
---|
| 565 | + head = rht_ptr(bkt, tbl, hash); |
---|
561 | 566 | |
---|
562 | 567 | RCU_INIT_POINTER(obj->next, head); |
---|
563 | 568 | if (ht->rhlist) { |
---|
.. | .. |
---|
567 | 572 | RCU_INIT_POINTER(list->next, NULL); |
---|
568 | 573 | } |
---|
569 | 574 | |
---|
570 | | - rcu_assign_pointer(*pprev, obj); |
---|
| 575 | + /* bkt is always the head of the list, so it holds |
---|
| 576 | + * the lock, which we need to preserve |
---|
| 577 | + */ |
---|
| 578 | + rht_assign_locked(bkt, obj); |
---|
571 | 579 | |
---|
572 | 580 | atomic_inc(&ht->nelems); |
---|
573 | 581 | if (rht_grow_above_75(ht, tbl)) |
---|
.. | .. |
---|
581 | 589 | { |
---|
582 | 590 | struct bucket_table *new_tbl; |
---|
583 | 591 | struct bucket_table *tbl; |
---|
| 592 | + struct rhash_lock_head __rcu **bkt; |
---|
584 | 593 | unsigned int hash; |
---|
585 | | - spinlock_t *lock; |
---|
586 | 594 | void *data; |
---|
587 | 595 | |
---|
588 | | - tbl = rcu_dereference(ht->tbl); |
---|
| 596 | + new_tbl = rcu_dereference(ht->tbl); |
---|
589 | 597 | |
---|
590 | | - /* All insertions must grab the oldest table containing |
---|
591 | | - * the hashed bucket that is yet to be rehashed. |
---|
592 | | - */ |
---|
593 | | - for (;;) { |
---|
594 | | - hash = rht_head_hashfn(ht, tbl, obj, ht->p); |
---|
595 | | - lock = rht_bucket_lock(tbl, hash); |
---|
596 | | - spin_lock_bh(lock); |
---|
597 | | - |
---|
598 | | - if (tbl->rehash <= hash) |
---|
599 | | - break; |
---|
600 | | - |
---|
601 | | - spin_unlock_bh(lock); |
---|
602 | | - tbl = rht_dereference_rcu(tbl->future_tbl, ht); |
---|
603 | | - } |
---|
604 | | - |
---|
605 | | - data = rhashtable_lookup_one(ht, tbl, hash, key, obj); |
---|
606 | | - new_tbl = rhashtable_insert_one(ht, tbl, hash, obj, data); |
---|
607 | | - if (PTR_ERR(new_tbl) != -EEXIST) |
---|
608 | | - data = ERR_CAST(new_tbl); |
---|
609 | | - |
---|
610 | | - while (!IS_ERR_OR_NULL(new_tbl)) { |
---|
| 598 | + do { |
---|
611 | 599 | tbl = new_tbl; |
---|
612 | 600 | hash = rht_head_hashfn(ht, tbl, obj, ht->p); |
---|
613 | | - spin_lock_nested(rht_bucket_lock(tbl, hash), |
---|
614 | | - SINGLE_DEPTH_NESTING); |
---|
| 601 | + if (rcu_access_pointer(tbl->future_tbl)) |
---|
| 602 | + /* Failure is OK */ |
---|
| 603 | + bkt = rht_bucket_var(tbl, hash); |
---|
| 604 | + else |
---|
| 605 | + bkt = rht_bucket_insert(ht, tbl, hash); |
---|
| 606 | + if (bkt == NULL) { |
---|
| 607 | + new_tbl = rht_dereference_rcu(tbl->future_tbl, ht); |
---|
| 608 | + data = ERR_PTR(-EAGAIN); |
---|
| 609 | + } else { |
---|
| 610 | + rht_lock(tbl, bkt); |
---|
| 611 | + data = rhashtable_lookup_one(ht, bkt, tbl, |
---|
| 612 | + hash, key, obj); |
---|
| 613 | + new_tbl = rhashtable_insert_one(ht, bkt, tbl, |
---|
| 614 | + hash, obj, data); |
---|
| 615 | + if (PTR_ERR(new_tbl) != -EEXIST) |
---|
| 616 | + data = ERR_CAST(new_tbl); |
---|
615 | 617 | |
---|
616 | | - data = rhashtable_lookup_one(ht, tbl, hash, key, obj); |
---|
617 | | - new_tbl = rhashtable_insert_one(ht, tbl, hash, obj, data); |
---|
618 | | - if (PTR_ERR(new_tbl) != -EEXIST) |
---|
619 | | - data = ERR_CAST(new_tbl); |
---|
620 | | - |
---|
621 | | - spin_unlock(rht_bucket_lock(tbl, hash)); |
---|
622 | | - } |
---|
623 | | - |
---|
624 | | - spin_unlock_bh(lock); |
---|
| 618 | + rht_unlock(tbl, bkt); |
---|
| 619 | + } |
---|
| 620 | + } while (!IS_ERR_OR_NULL(new_tbl)); |
---|
625 | 621 | |
---|
626 | 622 | if (PTR_ERR(data) == -EAGAIN) |
---|
627 | 623 | data = ERR_PTR(rhashtable_insert_rehash(ht, tbl) ?: |
---|
.. | .. |
---|
686 | 682 | * rhashtable_walk_exit - Free an iterator |
---|
687 | 683 | * @iter: Hash table Iterator |
---|
688 | 684 | * |
---|
689 | | - * This function frees resources allocated by rhashtable_walk_init. |
---|
| 685 | + * This function frees resources allocated by rhashtable_walk_enter. |
---|
690 | 686 | */ |
---|
691 | 687 | void rhashtable_walk_exit(struct rhashtable_iter *iter) |
---|
692 | 688 | { |
---|
.. | .. |
---|
943 | 939 | ht = iter->ht; |
---|
944 | 940 | |
---|
945 | 941 | spin_lock(&ht->lock); |
---|
946 | | - if (tbl->rehash < tbl->size) |
---|
947 | | - list_add(&iter->walker.list, &tbl->walkers); |
---|
948 | | - else |
---|
| 942 | + if (rcu_head_after_call_rcu(&tbl->rcu, bucket_table_free_rcu)) |
---|
| 943 | + /* This bucket table is being freed, don't re-link it. */ |
---|
949 | 944 | iter->walker.tbl = NULL; |
---|
| 945 | + else |
---|
| 946 | + list_add(&iter->walker.list, &tbl->walkers); |
---|
950 | 947 | spin_unlock(&ht->lock); |
---|
951 | 948 | |
---|
952 | 949 | out: |
---|
.. | .. |
---|
1045 | 1042 | ht->p.min_size = max_t(u16, ht->p.min_size, HASH_MIN_SIZE); |
---|
1046 | 1043 | |
---|
1047 | 1044 | size = rounded_hashtable_size(&ht->p); |
---|
1048 | | - |
---|
1049 | | - if (params->locks_mul) |
---|
1050 | | - ht->p.locks_mul = roundup_pow_of_two(params->locks_mul); |
---|
1051 | | - else |
---|
1052 | | - ht->p.locks_mul = BUCKET_LOCKS_PER_CPU; |
---|
1053 | 1045 | |
---|
1054 | 1046 | ht->key_len = ht->p.key_len; |
---|
1055 | 1047 | if (!params->hashfn) { |
---|
.. | .. |
---|
1152 | 1144 | struct rhash_head *pos, *next; |
---|
1153 | 1145 | |
---|
1154 | 1146 | cond_resched(); |
---|
1155 | | - for (pos = rht_dereference(*rht_bucket(tbl, i), ht), |
---|
| 1147 | + for (pos = rht_ptr_exclusive(rht_bucket(tbl, i)), |
---|
1156 | 1148 | next = !rht_is_a_nulls(pos) ? |
---|
1157 | 1149 | rht_dereference(pos->next, ht) : NULL; |
---|
1158 | 1150 | !rht_is_a_nulls(pos); |
---|
.. | .. |
---|
1179 | 1171 | } |
---|
1180 | 1172 | EXPORT_SYMBOL_GPL(rhashtable_destroy); |
---|
1181 | 1173 | |
---|
1182 | | -struct rhash_head __rcu **rht_bucket_nested(const struct bucket_table *tbl, |
---|
1183 | | - unsigned int hash) |
---|
| 1174 | +struct rhash_lock_head __rcu **__rht_bucket_nested( |
---|
| 1175 | + const struct bucket_table *tbl, unsigned int hash) |
---|
1184 | 1176 | { |
---|
1185 | 1177 | const unsigned int shift = PAGE_SHIFT - ilog2(sizeof(void *)); |
---|
1186 | | - static struct rhash_head __rcu *rhnull = |
---|
1187 | | - (struct rhash_head __rcu *)NULLS_MARKER(0); |
---|
1188 | 1178 | unsigned int index = hash & ((1 << tbl->nest) - 1); |
---|
1189 | 1179 | unsigned int size = tbl->size >> tbl->nest; |
---|
1190 | 1180 | unsigned int subhash = hash; |
---|
1191 | 1181 | union nested_table *ntbl; |
---|
1192 | 1182 | |
---|
1193 | | - ntbl = (union nested_table *)rcu_dereference_raw(tbl->buckets[0]); |
---|
| 1183 | + ntbl = nested_table_top(tbl); |
---|
1194 | 1184 | ntbl = rht_dereference_bucket_rcu(ntbl[index].table, tbl, hash); |
---|
1195 | 1185 | subhash >>= tbl->nest; |
---|
1196 | 1186 | |
---|
.. | .. |
---|
1203 | 1193 | } |
---|
1204 | 1194 | |
---|
1205 | 1195 | if (!ntbl) |
---|
1206 | | - return &rhnull; |
---|
| 1196 | + return NULL; |
---|
1207 | 1197 | |
---|
1208 | 1198 | return &ntbl[subhash].bucket; |
---|
1209 | 1199 | |
---|
1210 | 1200 | } |
---|
| 1201 | +EXPORT_SYMBOL_GPL(__rht_bucket_nested); |
---|
| 1202 | + |
---|
| 1203 | +struct rhash_lock_head __rcu **rht_bucket_nested( |
---|
| 1204 | + const struct bucket_table *tbl, unsigned int hash) |
---|
| 1205 | +{ |
---|
| 1206 | + static struct rhash_lock_head __rcu *rhnull; |
---|
| 1207 | + |
---|
| 1208 | + if (!rhnull) |
---|
| 1209 | + INIT_RHT_NULLS_HEAD(rhnull); |
---|
| 1210 | + return __rht_bucket_nested(tbl, hash) ?: &rhnull; |
---|
| 1211 | +} |
---|
1211 | 1212 | EXPORT_SYMBOL_GPL(rht_bucket_nested); |
---|
1212 | 1213 | |
---|
1213 | | -struct rhash_head __rcu **rht_bucket_nested_insert(struct rhashtable *ht, |
---|
1214 | | - struct bucket_table *tbl, |
---|
1215 | | - unsigned int hash) |
---|
| 1214 | +struct rhash_lock_head __rcu **rht_bucket_nested_insert( |
---|
| 1215 | + struct rhashtable *ht, struct bucket_table *tbl, unsigned int hash) |
---|
1216 | 1216 | { |
---|
1217 | 1217 | const unsigned int shift = PAGE_SHIFT - ilog2(sizeof(void *)); |
---|
1218 | 1218 | unsigned int index = hash & ((1 << tbl->nest) - 1); |
---|
1219 | 1219 | unsigned int size = tbl->size >> tbl->nest; |
---|
1220 | 1220 | union nested_table *ntbl; |
---|
1221 | 1221 | |
---|
1222 | | - ntbl = (union nested_table *)rcu_dereference_raw(tbl->buckets[0]); |
---|
| 1222 | + ntbl = nested_table_top(tbl); |
---|
1223 | 1223 | hash >>= tbl->nest; |
---|
1224 | 1224 | ntbl = nested_table_alloc(ht, &ntbl[index].table, |
---|
1225 | 1225 | size <= (1 << shift)); |
---|