| .. | .. |
|---|
| 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)); |
|---|