diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2015-03-23 18:53:17 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-03-23 22:16:07 -0400 |
commit | ba7c95ea3870fe7b847466d39a049ab6f156aa2c (patch) | |
tree | 8fed9deb6a4a0c1f52634ff0bd07fb6a0edb8dde /lib | |
parent | ce046c568cbfb4734583131086f88cfe993c01d0 (diff) |
rhashtable: Fix sleeping inside RCU critical section in walk_stop
The commit 963ecbd41a1026d99ec7537c050867428c397b89 ("rhashtable:
Fix use-after-free in rhashtable_walk_stop") fixed a real bug
but created another one because we may end up sleeping inside an
RCU critical section.
This patch fixes it properly by replacing the mutex with a spin
lock that specifically protects the walker lists.
Reported-by: Sasha Levin <sasha.levin@oracle.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/rhashtable.c | 7 |
1 files changed, 5 insertions, 2 deletions
diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 7686c1e9934a..e96ad1a52c90 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c | |||
@@ -256,8 +256,10 @@ static int rhashtable_rehash_table(struct rhashtable *ht) | |||
256 | /* Publish the new table pointer. */ | 256 | /* Publish the new table pointer. */ |
257 | rcu_assign_pointer(ht->tbl, new_tbl); | 257 | rcu_assign_pointer(ht->tbl, new_tbl); |
258 | 258 | ||
259 | spin_lock(&ht->lock); | ||
259 | list_for_each_entry(walker, &old_tbl->walkers, list) | 260 | list_for_each_entry(walker, &old_tbl->walkers, list) |
260 | walker->tbl = NULL; | 261 | walker->tbl = NULL; |
262 | spin_unlock(&ht->lock); | ||
261 | 263 | ||
262 | /* Wait for readers. All new readers will see the new | 264 | /* Wait for readers. All new readers will see the new |
263 | * table, and thus no references to the old table will | 265 | * table, and thus no references to the old table will |
@@ -635,12 +637,12 @@ void rhashtable_walk_stop(struct rhashtable_iter *iter) | |||
635 | 637 | ||
636 | ht = iter->ht; | 638 | ht = iter->ht; |
637 | 639 | ||
638 | mutex_lock(&ht->mutex); | 640 | spin_lock(&ht->lock); |
639 | if (tbl->rehash < tbl->size) | 641 | if (tbl->rehash < tbl->size) |
640 | list_add(&iter->walker->list, &tbl->walkers); | 642 | list_add(&iter->walker->list, &tbl->walkers); |
641 | else | 643 | else |
642 | iter->walker->tbl = NULL; | 644 | iter->walker->tbl = NULL; |
643 | mutex_unlock(&ht->mutex); | 645 | spin_unlock(&ht->lock); |
644 | 646 | ||
645 | iter->p = NULL; | 647 | iter->p = NULL; |
646 | 648 | ||
@@ -723,6 +725,7 @@ int rhashtable_init(struct rhashtable *ht, | |||
723 | 725 | ||
724 | memset(ht, 0, sizeof(*ht)); | 726 | memset(ht, 0, sizeof(*ht)); |
725 | mutex_init(&ht->mutex); | 727 | mutex_init(&ht->mutex); |
728 | spin_lock_init(&ht->lock); | ||
726 | memcpy(&ht->p, params, sizeof(*params)); | 729 | memcpy(&ht->p, params, sizeof(*params)); |
727 | 730 | ||
728 | if (params->min_size) | 731 | if (params->min_size) |