diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/rhashtable.c | 62 | ||||
| -rw-r--r-- | lib/seq_buf.c | 4 | ||||
| -rw-r--r-- | lib/test_rhashtable.c | 11 |
3 files changed, 38 insertions, 39 deletions
diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 9cc4c4a90d00..b5344ef4c684 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
| 18 | #include <linux/init.h> | 18 | #include <linux/init.h> |
| 19 | #include <linux/log2.h> | 19 | #include <linux/log2.h> |
| 20 | #include <linux/sched.h> | ||
| 20 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
| 21 | #include <linux/vmalloc.h> | 22 | #include <linux/vmalloc.h> |
| 22 | #include <linux/mm.h> | 23 | #include <linux/mm.h> |
| @@ -217,15 +218,15 @@ static void bucket_table_free(const struct bucket_table *tbl) | |||
| 217 | static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, | 218 | static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, |
| 218 | size_t nbuckets) | 219 | size_t nbuckets) |
| 219 | { | 220 | { |
| 220 | struct bucket_table *tbl; | 221 | struct bucket_table *tbl = NULL; |
| 221 | size_t size; | 222 | size_t size; |
| 222 | int i; | 223 | int i; |
| 223 | 224 | ||
| 224 | size = sizeof(*tbl) + nbuckets * sizeof(tbl->buckets[0]); | 225 | size = sizeof(*tbl) + nbuckets * sizeof(tbl->buckets[0]); |
| 225 | tbl = kzalloc(size, GFP_KERNEL | __GFP_NOWARN); | 226 | if (size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER)) |
| 227 | tbl = kzalloc(size, GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY); | ||
| 226 | if (tbl == NULL) | 228 | if (tbl == NULL) |
| 227 | tbl = vzalloc(size); | 229 | tbl = vzalloc(size); |
| 228 | |||
| 229 | if (tbl == NULL) | 230 | if (tbl == NULL) |
| 230 | return NULL; | 231 | return NULL; |
| 231 | 232 | ||
| @@ -247,26 +248,24 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, | |||
| 247 | * @ht: hash table | 248 | * @ht: hash table |
| 248 | * @new_size: new table size | 249 | * @new_size: new table size |
| 249 | */ | 250 | */ |
| 250 | bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size) | 251 | static bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size) |
| 251 | { | 252 | { |
| 252 | /* Expand table when exceeding 75% load */ | 253 | /* Expand table when exceeding 75% load */ |
| 253 | return atomic_read(&ht->nelems) > (new_size / 4 * 3) && | 254 | return atomic_read(&ht->nelems) > (new_size / 4 * 3) && |
| 254 | (ht->p.max_shift && atomic_read(&ht->shift) < ht->p.max_shift); | 255 | (!ht->p.max_shift || atomic_read(&ht->shift) < ht->p.max_shift); |
| 255 | } | 256 | } |
| 256 | EXPORT_SYMBOL_GPL(rht_grow_above_75); | ||
| 257 | 257 | ||
| 258 | /** | 258 | /** |
| 259 | * rht_shrink_below_30 - returns true if nelems < 0.3 * table-size | 259 | * rht_shrink_below_30 - returns true if nelems < 0.3 * table-size |
| 260 | * @ht: hash table | 260 | * @ht: hash table |
| 261 | * @new_size: new table size | 261 | * @new_size: new table size |
| 262 | */ | 262 | */ |
| 263 | bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size) | 263 | static bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size) |
| 264 | { | 264 | { |
| 265 | /* Shrink table beneath 30% load */ | 265 | /* Shrink table beneath 30% load */ |
| 266 | return atomic_read(&ht->nelems) < (new_size * 3 / 10) && | 266 | return atomic_read(&ht->nelems) < (new_size * 3 / 10) && |
| 267 | (atomic_read(&ht->shift) > ht->p.min_shift); | 267 | (atomic_read(&ht->shift) > ht->p.min_shift); |
| 268 | } | 268 | } |
| 269 | EXPORT_SYMBOL_GPL(rht_shrink_below_30); | ||
| 270 | 269 | ||
| 271 | static void lock_buckets(struct bucket_table *new_tbl, | 270 | static void lock_buckets(struct bucket_table *new_tbl, |
| 272 | struct bucket_table *old_tbl, unsigned int hash) | 271 | struct bucket_table *old_tbl, unsigned int hash) |
| @@ -414,6 +413,7 @@ int rhashtable_expand(struct rhashtable *ht) | |||
| 414 | } | 413 | } |
| 415 | } | 414 | } |
| 416 | unlock_buckets(new_tbl, old_tbl, new_hash); | 415 | unlock_buckets(new_tbl, old_tbl, new_hash); |
| 416 | cond_resched(); | ||
| 417 | } | 417 | } |
| 418 | 418 | ||
| 419 | /* Unzip interleaved hash chains */ | 419 | /* Unzip interleaved hash chains */ |
| @@ -437,6 +437,7 @@ int rhashtable_expand(struct rhashtable *ht) | |||
| 437 | complete = false; | 437 | complete = false; |
| 438 | 438 | ||
| 439 | unlock_buckets(new_tbl, old_tbl, old_hash); | 439 | unlock_buckets(new_tbl, old_tbl, old_hash); |
| 440 | cond_resched(); | ||
| 440 | } | 441 | } |
| 441 | } | 442 | } |
| 442 | 443 | ||
| @@ -495,6 +496,7 @@ int rhashtable_shrink(struct rhashtable *ht) | |||
| 495 | tbl->buckets[new_hash + new_tbl->size]); | 496 | tbl->buckets[new_hash + new_tbl->size]); |
| 496 | 497 | ||
| 497 | unlock_buckets(new_tbl, tbl, new_hash); | 498 | unlock_buckets(new_tbl, tbl, new_hash); |
| 499 | cond_resched(); | ||
| 498 | } | 500 | } |
| 499 | 501 | ||
| 500 | /* Publish the new, valid hash table */ | 502 | /* Publish the new, valid hash table */ |
| @@ -528,31 +530,19 @@ static void rht_deferred_worker(struct work_struct *work) | |||
| 528 | list_for_each_entry(walker, &ht->walkers, list) | 530 | list_for_each_entry(walker, &ht->walkers, list) |
| 529 | walker->resize = true; | 531 | walker->resize = true; |
| 530 | 532 | ||
| 531 | if (ht->p.grow_decision && ht->p.grow_decision(ht, tbl->size)) | 533 | if (rht_grow_above_75(ht, tbl->size)) |
| 532 | rhashtable_expand(ht); | 534 | rhashtable_expand(ht); |
| 533 | else if (ht->p.shrink_decision && ht->p.shrink_decision(ht, tbl->size)) | 535 | else if (rht_shrink_below_30(ht, tbl->size)) |
| 534 | rhashtable_shrink(ht); | 536 | rhashtable_shrink(ht); |
| 535 | |||
| 536 | unlock: | 537 | unlock: |
| 537 | mutex_unlock(&ht->mutex); | 538 | mutex_unlock(&ht->mutex); |
| 538 | } | 539 | } |
| 539 | 540 | ||
| 540 | static void rhashtable_wakeup_worker(struct rhashtable *ht) | ||
| 541 | { | ||
| 542 | struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); | ||
| 543 | struct bucket_table *new_tbl = rht_dereference_rcu(ht->future_tbl, ht); | ||
| 544 | size_t size = tbl->size; | ||
| 545 | |||
| 546 | /* Only adjust the table if no resizing is currently in progress. */ | ||
| 547 | if (tbl == new_tbl && | ||
| 548 | ((ht->p.grow_decision && ht->p.grow_decision(ht, size)) || | ||
| 549 | (ht->p.shrink_decision && ht->p.shrink_decision(ht, size)))) | ||
| 550 | schedule_work(&ht->run_work); | ||
| 551 | } | ||
| 552 | |||
| 553 | static void __rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj, | 541 | static void __rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj, |
| 554 | struct bucket_table *tbl, u32 hash) | 542 | struct bucket_table *tbl, |
| 543 | const struct bucket_table *old_tbl, u32 hash) | ||
| 555 | { | 544 | { |
| 545 | bool no_resize_running = tbl == old_tbl; | ||
| 556 | struct rhash_head *head; | 546 | struct rhash_head *head; |
| 557 | 547 | ||
| 558 | hash = rht_bucket_index(tbl, hash); | 548 | hash = rht_bucket_index(tbl, hash); |
| @@ -568,8 +558,8 @@ static void __rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj, | |||
| 568 | rcu_assign_pointer(tbl->buckets[hash], obj); | 558 | rcu_assign_pointer(tbl->buckets[hash], obj); |
| 569 | 559 | ||
| 570 | atomic_inc(&ht->nelems); | 560 | atomic_inc(&ht->nelems); |
| 571 | 561 | if (no_resize_running && rht_grow_above_75(ht, tbl->size)) | |
| 572 | rhashtable_wakeup_worker(ht); | 562 | schedule_work(&ht->run_work); |
| 573 | } | 563 | } |
| 574 | 564 | ||
| 575 | /** | 565 | /** |
| @@ -599,7 +589,7 @@ void rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj) | |||
| 599 | hash = obj_raw_hashfn(ht, rht_obj(ht, obj)); | 589 | hash = obj_raw_hashfn(ht, rht_obj(ht, obj)); |
| 600 | 590 | ||
| 601 | lock_buckets(tbl, old_tbl, hash); | 591 | lock_buckets(tbl, old_tbl, hash); |
| 602 | __rhashtable_insert(ht, obj, tbl, hash); | 592 | __rhashtable_insert(ht, obj, tbl, old_tbl, hash); |
| 603 | unlock_buckets(tbl, old_tbl, hash); | 593 | unlock_buckets(tbl, old_tbl, hash); |
| 604 | 594 | ||
| 605 | rcu_read_unlock(); | 595 | rcu_read_unlock(); |
| @@ -681,8 +671,11 @@ found: | |||
| 681 | unlock_buckets(new_tbl, old_tbl, new_hash); | 671 | unlock_buckets(new_tbl, old_tbl, new_hash); |
| 682 | 672 | ||
| 683 | if (ret) { | 673 | if (ret) { |
| 674 | bool no_resize_running = new_tbl == old_tbl; | ||
| 675 | |||
| 684 | atomic_dec(&ht->nelems); | 676 | atomic_dec(&ht->nelems); |
| 685 | rhashtable_wakeup_worker(ht); | 677 | if (no_resize_running && rht_shrink_below_30(ht, new_tbl->size)) |
| 678 | schedule_work(&ht->run_work); | ||
| 686 | } | 679 | } |
| 687 | 680 | ||
| 688 | rcu_read_unlock(); | 681 | rcu_read_unlock(); |
| @@ -852,7 +845,7 @@ bool rhashtable_lookup_compare_insert(struct rhashtable *ht, | |||
| 852 | goto exit; | 845 | goto exit; |
| 853 | } | 846 | } |
| 854 | 847 | ||
| 855 | __rhashtable_insert(ht, obj, new_tbl, new_hash); | 848 | __rhashtable_insert(ht, obj, new_tbl, old_tbl, new_hash); |
| 856 | 849 | ||
| 857 | exit: | 850 | exit: |
| 858 | unlock_buckets(new_tbl, old_tbl, new_hash); | 851 | unlock_buckets(new_tbl, old_tbl, new_hash); |
| @@ -894,6 +887,9 @@ int rhashtable_walk_init(struct rhashtable *ht, struct rhashtable_iter *iter) | |||
| 894 | if (!iter->walker) | 887 | if (!iter->walker) |
| 895 | return -ENOMEM; | 888 | return -ENOMEM; |
| 896 | 889 | ||
| 890 | INIT_LIST_HEAD(&iter->walker->list); | ||
| 891 | iter->walker->resize = false; | ||
| 892 | |||
| 897 | mutex_lock(&ht->mutex); | 893 | mutex_lock(&ht->mutex); |
| 898 | list_add(&iter->walker->list, &ht->walkers); | 894 | list_add(&iter->walker->list, &ht->walkers); |
| 899 | mutex_unlock(&ht->mutex); | 895 | mutex_unlock(&ht->mutex); |
| @@ -1111,8 +1107,7 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) | |||
| 1111 | if (!ht->p.hash_rnd) | 1107 | if (!ht->p.hash_rnd) |
| 1112 | get_random_bytes(&ht->p.hash_rnd, sizeof(ht->p.hash_rnd)); | 1108 | get_random_bytes(&ht->p.hash_rnd, sizeof(ht->p.hash_rnd)); |
| 1113 | 1109 | ||
| 1114 | if (ht->p.grow_decision || ht->p.shrink_decision) | 1110 | INIT_WORK(&ht->run_work, rht_deferred_worker); |
| 1115 | INIT_WORK(&ht->run_work, rht_deferred_worker); | ||
| 1116 | 1111 | ||
| 1117 | return 0; | 1112 | return 0; |
| 1118 | } | 1113 | } |
| @@ -1130,8 +1125,7 @@ void rhashtable_destroy(struct rhashtable *ht) | |||
| 1130 | { | 1125 | { |
| 1131 | ht->being_destroyed = true; | 1126 | ht->being_destroyed = true; |
| 1132 | 1127 | ||
| 1133 | if (ht->p.grow_decision || ht->p.shrink_decision) | 1128 | cancel_work_sync(&ht->run_work); |
| 1134 | cancel_work_sync(&ht->run_work); | ||
| 1135 | 1129 | ||
| 1136 | mutex_lock(&ht->mutex); | 1130 | mutex_lock(&ht->mutex); |
| 1137 | bucket_table_free(rht_dereference(ht->tbl, ht)); | 1131 | bucket_table_free(rht_dereference(ht->tbl, ht)); |
diff --git a/lib/seq_buf.c b/lib/seq_buf.c index 88c0854bd752..5c94e1012a91 100644 --- a/lib/seq_buf.c +++ b/lib/seq_buf.c | |||
| @@ -61,7 +61,7 @@ int seq_buf_vprintf(struct seq_buf *s, const char *fmt, va_list args) | |||
| 61 | 61 | ||
| 62 | if (s->len < s->size) { | 62 | if (s->len < s->size) { |
| 63 | len = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args); | 63 | len = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args); |
| 64 | if (seq_buf_can_fit(s, len)) { | 64 | if (s->len + len < s->size) { |
| 65 | s->len += len; | 65 | s->len += len; |
| 66 | return 0; | 66 | return 0; |
| 67 | } | 67 | } |
| @@ -118,7 +118,7 @@ int seq_buf_bprintf(struct seq_buf *s, const char *fmt, const u32 *binary) | |||
| 118 | 118 | ||
| 119 | if (s->len < s->size) { | 119 | if (s->len < s->size) { |
| 120 | ret = bstr_printf(s->buffer + s->len, len, fmt, binary); | 120 | ret = bstr_printf(s->buffer + s->len, len, fmt, binary); |
| 121 | if (seq_buf_can_fit(s, ret)) { | 121 | if (s->len + ret < s->size) { |
| 122 | s->len += ret; | 122 | s->len += ret; |
| 123 | return 0; | 123 | return 0; |
| 124 | } | 124 | } |
diff --git a/lib/test_rhashtable.c b/lib/test_rhashtable.c index 1dfeba73fc74..67c7593d1dd6 100644 --- a/lib/test_rhashtable.c +++ b/lib/test_rhashtable.c | |||
| @@ -191,18 +191,18 @@ error: | |||
| 191 | return err; | 191 | return err; |
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | static struct rhashtable ht; | ||
| 195 | |||
| 194 | static int __init test_rht_init(void) | 196 | static int __init test_rht_init(void) |
| 195 | { | 197 | { |
| 196 | struct rhashtable ht; | ||
| 197 | struct rhashtable_params params = { | 198 | struct rhashtable_params params = { |
| 198 | .nelem_hint = TEST_HT_SIZE, | 199 | .nelem_hint = TEST_HT_SIZE, |
| 199 | .head_offset = offsetof(struct test_obj, node), | 200 | .head_offset = offsetof(struct test_obj, node), |
| 200 | .key_offset = offsetof(struct test_obj, value), | 201 | .key_offset = offsetof(struct test_obj, value), |
| 201 | .key_len = sizeof(int), | 202 | .key_len = sizeof(int), |
| 202 | .hashfn = jhash, | 203 | .hashfn = jhash, |
| 204 | .max_shift = 1, /* we expand/shrink manually here */ | ||
| 203 | .nulls_base = (3U << RHT_BASE_SHIFT), | 205 | .nulls_base = (3U << RHT_BASE_SHIFT), |
| 204 | .grow_decision = rht_grow_above_75, | ||
| 205 | .shrink_decision = rht_shrink_below_30, | ||
| 206 | }; | 206 | }; |
| 207 | int err; | 207 | int err; |
| 208 | 208 | ||
| @@ -222,6 +222,11 @@ static int __init test_rht_init(void) | |||
| 222 | return err; | 222 | return err; |
| 223 | } | 223 | } |
| 224 | 224 | ||
| 225 | static void __exit test_rht_exit(void) | ||
| 226 | { | ||
| 227 | } | ||
| 228 | |||
| 225 | module_init(test_rht_init); | 229 | module_init(test_rht_init); |
| 230 | module_exit(test_rht_exit); | ||
| 226 | 231 | ||
| 227 | MODULE_LICENSE("GPL v2"); | 232 | MODULE_LICENSE("GPL v2"); |
