diff options
author | David S. Miller <davem@davemloft.net> | 2015-02-27 16:06:21 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-02-27 16:06:21 -0500 |
commit | c0eebfa3233ef59109aa314ef19451d2156a2635 (patch) | |
tree | 3e4d9c6941729846c3c55c7f256197321311b3aa | |
parent | 0d79a493e507437a2135e5ac1a447d4d503488d8 (diff) | |
parent | 4c4b52d9b2df45e8216d3e30b5452e4a364d2cac (diff) |
Merge branch 'rhashtable'
Daniel Borkmann says:
====================
rhashtable updates
As discussed, I'm sending out rhashtable fixups for -net.
I have a couple of more patches I was working on last week pending,
i.e. to get rid of ht->nelems and ht->shift atomic operations which
speed-up pure insertions/deletions, e.g. on my laptop I have 2 threads,
inserting 7M entries each, that will reduce insertion time from ~1,450 ms
to 865 ms (performance should even be better after removing the
grow/shrink indirections). I guess that however is rather something
for net-next.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/rhashtable.h | 13 | ||||
-rw-r--r-- | lib/rhashtable.c | 58 | ||||
-rw-r--r-- | lib/test_rhashtable.c | 3 | ||||
-rw-r--r-- | net/netfilter/nft_hash.c | 2 | ||||
-rw-r--r-- | net/netlink/af_netlink.c | 2 | ||||
-rw-r--r-- | net/tipc/socket.c | 2 |
6 files changed, 19 insertions, 61 deletions
diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index cb2104be2135..d438eeb08bff 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h | |||
@@ -79,12 +79,6 @@ struct rhashtable; | |||
79 | * @locks_mul: Number of bucket locks to allocate per cpu (default: 128) | 79 | * @locks_mul: Number of bucket locks to allocate per cpu (default: 128) |
80 | * @hashfn: Function to hash key | 80 | * @hashfn: Function to hash key |
81 | * @obj_hashfn: Function to hash object | 81 | * @obj_hashfn: Function to hash object |
82 | * @grow_decision: If defined, may return true if table should expand | ||
83 | * @shrink_decision: If defined, may return true if table should shrink | ||
84 | * | ||
85 | * Note: when implementing the grow and shrink decision function, min/max | ||
86 | * shift must be enforced, otherwise, resizing watermarks they set may be | ||
87 | * useless. | ||
88 | */ | 82 | */ |
89 | struct rhashtable_params { | 83 | struct rhashtable_params { |
90 | size_t nelem_hint; | 84 | size_t nelem_hint; |
@@ -98,10 +92,6 @@ struct rhashtable_params { | |||
98 | size_t locks_mul; | 92 | size_t locks_mul; |
99 | rht_hashfn_t hashfn; | 93 | rht_hashfn_t hashfn; |
100 | rht_obj_hashfn_t obj_hashfn; | 94 | rht_obj_hashfn_t obj_hashfn; |
101 | bool (*grow_decision)(const struct rhashtable *ht, | ||
102 | size_t new_size); | ||
103 | bool (*shrink_decision)(const struct rhashtable *ht, | ||
104 | size_t new_size); | ||
105 | }; | 95 | }; |
106 | 96 | ||
107 | /** | 97 | /** |
@@ -193,9 +183,6 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params); | |||
193 | void rhashtable_insert(struct rhashtable *ht, struct rhash_head *node); | 183 | void rhashtable_insert(struct rhashtable *ht, struct rhash_head *node); |
194 | bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *node); | 184 | bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *node); |
195 | 185 | ||
196 | bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size); | ||
197 | bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size); | ||
198 | |||
199 | int rhashtable_expand(struct rhashtable *ht); | 186 | int rhashtable_expand(struct rhashtable *ht); |
200 | int rhashtable_shrink(struct rhashtable *ht); | 187 | int rhashtable_shrink(struct rhashtable *ht); |
201 | 188 | ||
diff --git a/lib/rhashtable.c b/lib/rhashtable.c index e3a04e4b3ec5..090641db4c0d 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c | |||
@@ -247,26 +247,24 @@ static struct bucket_table *bucket_table_alloc(struct rhashtable *ht, | |||
247 | * @ht: hash table | 247 | * @ht: hash table |
248 | * @new_size: new table size | 248 | * @new_size: new table size |
249 | */ | 249 | */ |
250 | bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size) | 250 | static bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size) |
251 | { | 251 | { |
252 | /* Expand table when exceeding 75% load */ | 252 | /* Expand table when exceeding 75% load */ |
253 | return atomic_read(&ht->nelems) > (new_size / 4 * 3) && | 253 | return atomic_read(&ht->nelems) > (new_size / 4 * 3) && |
254 | (ht->p.max_shift && atomic_read(&ht->shift) < ht->p.max_shift); | 254 | (!ht->p.max_shift || atomic_read(&ht->shift) < ht->p.max_shift); |
255 | } | 255 | } |
256 | EXPORT_SYMBOL_GPL(rht_grow_above_75); | ||
257 | 256 | ||
258 | /** | 257 | /** |
259 | * rht_shrink_below_30 - returns true if nelems < 0.3 * table-size | 258 | * rht_shrink_below_30 - returns true if nelems < 0.3 * table-size |
260 | * @ht: hash table | 259 | * @ht: hash table |
261 | * @new_size: new table size | 260 | * @new_size: new table size |
262 | */ | 261 | */ |
263 | bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size) | 262 | static bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size) |
264 | { | 263 | { |
265 | /* Shrink table beneath 30% load */ | 264 | /* Shrink table beneath 30% load */ |
266 | return atomic_read(&ht->nelems) < (new_size * 3 / 10) && | 265 | return atomic_read(&ht->nelems) < (new_size * 3 / 10) && |
267 | (atomic_read(&ht->shift) > ht->p.min_shift); | 266 | (atomic_read(&ht->shift) > ht->p.min_shift); |
268 | } | 267 | } |
269 | EXPORT_SYMBOL_GPL(rht_shrink_below_30); | ||
270 | 268 | ||
271 | static void lock_buckets(struct bucket_table *new_tbl, | 269 | static void lock_buckets(struct bucket_table *new_tbl, |
272 | struct bucket_table *old_tbl, unsigned int hash) | 270 | struct bucket_table *old_tbl, unsigned int hash) |
@@ -528,40 +526,19 @@ static void rht_deferred_worker(struct work_struct *work) | |||
528 | list_for_each_entry(walker, &ht->walkers, list) | 526 | list_for_each_entry(walker, &ht->walkers, list) |
529 | walker->resize = true; | 527 | walker->resize = true; |
530 | 528 | ||
531 | if (ht->p.grow_decision && ht->p.grow_decision(ht, tbl->size)) | 529 | if (rht_grow_above_75(ht, tbl->size)) |
532 | rhashtable_expand(ht); | 530 | rhashtable_expand(ht); |
533 | else if (ht->p.shrink_decision && ht->p.shrink_decision(ht, tbl->size)) | 531 | else if (rht_shrink_below_30(ht, tbl->size)) |
534 | rhashtable_shrink(ht); | 532 | rhashtable_shrink(ht); |
535 | |||
536 | unlock: | 533 | unlock: |
537 | mutex_unlock(&ht->mutex); | 534 | mutex_unlock(&ht->mutex); |
538 | } | 535 | } |
539 | 536 | ||
540 | static void rhashtable_probe_expand(struct rhashtable *ht) | ||
541 | { | ||
542 | const struct bucket_table *new_tbl = rht_dereference_rcu(ht->future_tbl, ht); | ||
543 | const struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); | ||
544 | |||
545 | /* Only adjust the table if no resizing is currently in progress. */ | ||
546 | if (tbl == new_tbl && ht->p.grow_decision && | ||
547 | ht->p.grow_decision(ht, tbl->size)) | ||
548 | schedule_work(&ht->run_work); | ||
549 | } | ||
550 | |||
551 | static void rhashtable_probe_shrink(struct rhashtable *ht) | ||
552 | { | ||
553 | const struct bucket_table *new_tbl = rht_dereference_rcu(ht->future_tbl, ht); | ||
554 | const struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht); | ||
555 | |||
556 | /* Only adjust the table if no resizing is currently in progress. */ | ||
557 | if (tbl == new_tbl && ht->p.shrink_decision && | ||
558 | ht->p.shrink_decision(ht, tbl->size)) | ||
559 | schedule_work(&ht->run_work); | ||
560 | } | ||
561 | |||
562 | static void __rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj, | 537 | static void __rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj, |
563 | struct bucket_table *tbl, u32 hash) | 538 | struct bucket_table *tbl, |
539 | const struct bucket_table *old_tbl, u32 hash) | ||
564 | { | 540 | { |
541 | bool no_resize_running = tbl == old_tbl; | ||
565 | struct rhash_head *head; | 542 | struct rhash_head *head; |
566 | 543 | ||
567 | hash = rht_bucket_index(tbl, hash); | 544 | hash = rht_bucket_index(tbl, hash); |
@@ -577,8 +554,8 @@ static void __rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj, | |||
577 | rcu_assign_pointer(tbl->buckets[hash], obj); | 554 | rcu_assign_pointer(tbl->buckets[hash], obj); |
578 | 555 | ||
579 | atomic_inc(&ht->nelems); | 556 | atomic_inc(&ht->nelems); |
580 | 557 | if (no_resize_running && rht_grow_above_75(ht, tbl->size)) | |
581 | rhashtable_probe_expand(ht); | 558 | schedule_work(&ht->run_work); |
582 | } | 559 | } |
583 | 560 | ||
584 | /** | 561 | /** |
@@ -608,7 +585,7 @@ void rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj) | |||
608 | hash = obj_raw_hashfn(ht, rht_obj(ht, obj)); | 585 | hash = obj_raw_hashfn(ht, rht_obj(ht, obj)); |
609 | 586 | ||
610 | lock_buckets(tbl, old_tbl, hash); | 587 | lock_buckets(tbl, old_tbl, hash); |
611 | __rhashtable_insert(ht, obj, tbl, hash); | 588 | __rhashtable_insert(ht, obj, tbl, old_tbl, hash); |
612 | unlock_buckets(tbl, old_tbl, hash); | 589 | unlock_buckets(tbl, old_tbl, hash); |
613 | 590 | ||
614 | rcu_read_unlock(); | 591 | rcu_read_unlock(); |
@@ -690,8 +667,11 @@ found: | |||
690 | unlock_buckets(new_tbl, old_tbl, new_hash); | 667 | unlock_buckets(new_tbl, old_tbl, new_hash); |
691 | 668 | ||
692 | if (ret) { | 669 | if (ret) { |
670 | bool no_resize_running = new_tbl == old_tbl; | ||
671 | |||
693 | atomic_dec(&ht->nelems); | 672 | atomic_dec(&ht->nelems); |
694 | rhashtable_probe_shrink(ht); | 673 | if (no_resize_running && rht_shrink_below_30(ht, new_tbl->size)) |
674 | schedule_work(&ht->run_work); | ||
695 | } | 675 | } |
696 | 676 | ||
697 | rcu_read_unlock(); | 677 | rcu_read_unlock(); |
@@ -861,7 +841,7 @@ bool rhashtable_lookup_compare_insert(struct rhashtable *ht, | |||
861 | goto exit; | 841 | goto exit; |
862 | } | 842 | } |
863 | 843 | ||
864 | __rhashtable_insert(ht, obj, new_tbl, new_hash); | 844 | __rhashtable_insert(ht, obj, new_tbl, old_tbl, new_hash); |
865 | 845 | ||
866 | exit: | 846 | exit: |
867 | unlock_buckets(new_tbl, old_tbl, new_hash); | 847 | unlock_buckets(new_tbl, old_tbl, new_hash); |
@@ -1123,8 +1103,7 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params) | |||
1123 | if (!ht->p.hash_rnd) | 1103 | if (!ht->p.hash_rnd) |
1124 | get_random_bytes(&ht->p.hash_rnd, sizeof(ht->p.hash_rnd)); | 1104 | get_random_bytes(&ht->p.hash_rnd, sizeof(ht->p.hash_rnd)); |
1125 | 1105 | ||
1126 | if (ht->p.grow_decision || ht->p.shrink_decision) | 1106 | INIT_WORK(&ht->run_work, rht_deferred_worker); |
1127 | INIT_WORK(&ht->run_work, rht_deferred_worker); | ||
1128 | 1107 | ||
1129 | return 0; | 1108 | return 0; |
1130 | } | 1109 | } |
@@ -1142,8 +1121,7 @@ void rhashtable_destroy(struct rhashtable *ht) | |||
1142 | { | 1121 | { |
1143 | ht->being_destroyed = true; | 1122 | ht->being_destroyed = true; |
1144 | 1123 | ||
1145 | if (ht->p.grow_decision || ht->p.shrink_decision) | 1124 | cancel_work_sync(&ht->run_work); |
1146 | cancel_work_sync(&ht->run_work); | ||
1147 | 1125 | ||
1148 | mutex_lock(&ht->mutex); | 1126 | mutex_lock(&ht->mutex); |
1149 | bucket_table_free(rht_dereference(ht->tbl, ht)); | 1127 | bucket_table_free(rht_dereference(ht->tbl, ht)); |
diff --git a/lib/test_rhashtable.c b/lib/test_rhashtable.c index 58b995323c44..67c7593d1dd6 100644 --- a/lib/test_rhashtable.c +++ b/lib/test_rhashtable.c | |||
@@ -201,9 +201,8 @@ static int __init test_rht_init(void) | |||
201 | .key_offset = offsetof(struct test_obj, value), | 201 | .key_offset = offsetof(struct test_obj, value), |
202 | .key_len = sizeof(int), | 202 | .key_len = sizeof(int), |
203 | .hashfn = jhash, | 203 | .hashfn = jhash, |
204 | .max_shift = 1, /* we expand/shrink manually here */ | ||
204 | .nulls_base = (3U << RHT_BASE_SHIFT), | 205 | .nulls_base = (3U << RHT_BASE_SHIFT), |
205 | .grow_decision = rht_grow_above_75, | ||
206 | .shrink_decision = rht_shrink_below_30, | ||
207 | }; | 206 | }; |
208 | int err; | 207 | int err; |
209 | 208 | ||
diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index 61e6c407476a..c82df0a48fcd 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c | |||
@@ -192,8 +192,6 @@ static int nft_hash_init(const struct nft_set *set, | |||
192 | .key_offset = offsetof(struct nft_hash_elem, key), | 192 | .key_offset = offsetof(struct nft_hash_elem, key), |
193 | .key_len = set->klen, | 193 | .key_len = set->klen, |
194 | .hashfn = jhash, | 194 | .hashfn = jhash, |
195 | .grow_decision = rht_grow_above_75, | ||
196 | .shrink_decision = rht_shrink_below_30, | ||
197 | }; | 195 | }; |
198 | 196 | ||
199 | return rhashtable_init(priv, ¶ms); | 197 | return rhashtable_init(priv, ¶ms); |
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 2702673f0f23..05919bf3f670 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -3126,8 +3126,6 @@ static int __init netlink_proto_init(void) | |||
3126 | .key_len = sizeof(u32), /* portid */ | 3126 | .key_len = sizeof(u32), /* portid */ |
3127 | .hashfn = jhash, | 3127 | .hashfn = jhash, |
3128 | .max_shift = 16, /* 64K */ | 3128 | .max_shift = 16, /* 64K */ |
3129 | .grow_decision = rht_grow_above_75, | ||
3130 | .shrink_decision = rht_shrink_below_30, | ||
3131 | }; | 3129 | }; |
3132 | 3130 | ||
3133 | if (err != 0) | 3131 | if (err != 0) |
diff --git a/net/tipc/socket.c b/net/tipc/socket.c index f73e975af80b..b4d4467d0bb0 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c | |||
@@ -2364,8 +2364,6 @@ int tipc_sk_rht_init(struct net *net) | |||
2364 | .hashfn = jhash, | 2364 | .hashfn = jhash, |
2365 | .max_shift = 20, /* 1M */ | 2365 | .max_shift = 20, /* 1M */ |
2366 | .min_shift = 8, /* 256 */ | 2366 | .min_shift = 8, /* 256 */ |
2367 | .grow_decision = rht_grow_above_75, | ||
2368 | .shrink_decision = rht_shrink_below_30, | ||
2369 | }; | 2367 | }; |
2370 | 2368 | ||
2371 | return rhashtable_init(&tn->sk_rht, &rht_params); | 2369 | return rhashtable_init(&tn->sk_rht, &rht_params); |