diff options
author | Thomas Graf <tgraf@suug.ch> | 2015-03-24 09:18:20 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-03-24 17:48:40 -0400 |
commit | 6b6f302ceda7a052dab545d6c69abf5f0d4a6cab (patch) | |
tree | 8464850fd94dba0dfa34c75c46dfb3fcf4c3c381 /lib/rhashtable.c | |
parent | b5e2c150ac914f28a28833b57397bec0b0a2bd5f (diff) |
rhashtable: Add rhashtable_free_and_destroy()
rhashtable_destroy() variant which stops rehashes, iterates over
the table and calls a callback to release resources.
Avoids need for nft_hash to embed rhashtable internals and allows to
get rid of the being_destroyed flag. It also saves a 2nd mutex
lock upon destruction.
Also fixes an RCU lockdep splash on nft set destruction due to
calling rht_for_each_entry_safe() without holding bucket locks.
Open code this loop as we need know that no mutations may occur in
parallel.
Signed-off-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.c | 49 |
1 files changed, 39 insertions, 10 deletions
diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 50374d181148..4b7b7e672b93 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c | |||
@@ -359,8 +359,6 @@ static void rht_deferred_worker(struct work_struct *work) | |||
359 | 359 | ||
360 | ht = container_of(work, struct rhashtable, run_work); | 360 | ht = container_of(work, struct rhashtable, run_work); |
361 | mutex_lock(&ht->mutex); | 361 | mutex_lock(&ht->mutex); |
362 | if (ht->being_destroyed) | ||
363 | goto unlock; | ||
364 | 362 | ||
365 | tbl = rht_dereference(ht->tbl, ht); | 363 | tbl = rht_dereference(ht->tbl, ht); |
366 | tbl = rhashtable_last_table(ht, tbl); | 364 | tbl = rhashtable_last_table(ht, tbl); |
@@ -372,7 +370,6 @@ static void rht_deferred_worker(struct work_struct *work) | |||
372 | 370 | ||
373 | err = rhashtable_rehash_table(ht); | 371 | err = rhashtable_rehash_table(ht); |
374 | 372 | ||
375 | unlock: | ||
376 | mutex_unlock(&ht->mutex); | 373 | mutex_unlock(&ht->mutex); |
377 | 374 | ||
378 | if (err) | 375 | if (err) |
@@ -783,21 +780,53 @@ int rhashtable_init(struct rhashtable *ht, | |||
783 | EXPORT_SYMBOL_GPL(rhashtable_init); | 780 | EXPORT_SYMBOL_GPL(rhashtable_init); |
784 | 781 | ||
785 | /** | 782 | /** |
786 | * rhashtable_destroy - destroy hash table | 783 | * rhashtable_free_and_destroy - free elements and destroy hash table |
787 | * @ht: the hash table to destroy | 784 | * @ht: the hash table to destroy |
785 | * @free_fn: callback to release resources of element | ||
786 | * @arg: pointer passed to free_fn | ||
788 | * | 787 | * |
789 | * Frees the bucket array. This function is not rcu safe, therefore the caller | 788 | * Stops an eventual async resize. If defined, invokes free_fn for each |
790 | * has to make sure that no resizing may happen by unpublishing the hashtable | 789 | * element to releasal resources. Please note that RCU protected |
791 | * and waiting for the quiescent cycle before releasing the bucket array. | 790 | * readers may still be accessing the elements. Releasing of resources |
791 | * must occur in a compatible manner. Then frees the bucket array. | ||
792 | * | ||
793 | * This function will eventually sleep to wait for an async resize | ||
794 | * to complete. The caller is responsible that no further write operations | ||
795 | * occurs in parallel. | ||
792 | */ | 796 | */ |
793 | void rhashtable_destroy(struct rhashtable *ht) | 797 | void rhashtable_free_and_destroy(struct rhashtable *ht, |
798 | void (*free_fn)(void *ptr, void *arg), | ||
799 | void *arg) | ||
794 | { | 800 | { |
795 | ht->being_destroyed = true; | 801 | const struct bucket_table *tbl; |
802 | unsigned int i; | ||
796 | 803 | ||
797 | cancel_work_sync(&ht->run_work); | 804 | cancel_work_sync(&ht->run_work); |
798 | 805 | ||
799 | mutex_lock(&ht->mutex); | 806 | mutex_lock(&ht->mutex); |
800 | bucket_table_free(rht_dereference(ht->tbl, ht)); | 807 | tbl = rht_dereference(ht->tbl, ht); |
808 | if (free_fn) { | ||
809 | for (i = 0; i < tbl->size; i++) { | ||
810 | struct rhash_head *pos, *next; | ||
811 | |||
812 | for (pos = rht_dereference(tbl->buckets[i], ht), | ||
813 | next = !rht_is_a_nulls(pos) ? | ||
814 | rht_dereference(pos->next, ht) : NULL; | ||
815 | !rht_is_a_nulls(pos); | ||
816 | pos = next, | ||
817 | next = !rht_is_a_nulls(pos) ? | ||
818 | rht_dereference(pos->next, ht) : NULL) | ||
819 | free_fn(rht_obj(ht, pos), arg); | ||
820 | } | ||
821 | } | ||
822 | |||
823 | bucket_table_free(tbl); | ||
801 | mutex_unlock(&ht->mutex); | 824 | mutex_unlock(&ht->mutex); |
802 | } | 825 | } |
826 | EXPORT_SYMBOL_GPL(rhashtable_free_and_destroy); | ||
827 | |||
828 | void rhashtable_destroy(struct rhashtable *ht) | ||
829 | { | ||
830 | return rhashtable_free_and_destroy(ht, NULL, NULL); | ||
831 | } | ||
803 | EXPORT_SYMBOL_GPL(rhashtable_destroy); | 832 | EXPORT_SYMBOL_GPL(rhashtable_destroy); |