rhashtable: Fix sleeping inside RCU critical section in walk_stop
The commit 963ecbd41a
("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>
This commit is contained in:
parent
ce046c568c
commit
ba7c95ea38
|
@ -133,6 +133,7 @@ struct rhashtable_params {
|
||||||
* @p: Configuration parameters
|
* @p: Configuration parameters
|
||||||
* @run_work: Deferred worker to expand/shrink asynchronously
|
* @run_work: Deferred worker to expand/shrink asynchronously
|
||||||
* @mutex: Mutex to protect current/future table swapping
|
* @mutex: Mutex to protect current/future table swapping
|
||||||
|
* @lock: Spin lock to protect walker list
|
||||||
* @being_destroyed: True if table is set up for destruction
|
* @being_destroyed: True if table is set up for destruction
|
||||||
*/
|
*/
|
||||||
struct rhashtable {
|
struct rhashtable {
|
||||||
|
@ -144,6 +145,7 @@ struct rhashtable {
|
||||||
struct rhashtable_params p;
|
struct rhashtable_params p;
|
||||||
struct work_struct run_work;
|
struct work_struct run_work;
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
|
spinlock_t lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -256,8 +256,10 @@ static int rhashtable_rehash_table(struct rhashtable *ht)
|
||||||
/* Publish the new table pointer. */
|
/* Publish the new table pointer. */
|
||||||
rcu_assign_pointer(ht->tbl, new_tbl);
|
rcu_assign_pointer(ht->tbl, new_tbl);
|
||||||
|
|
||||||
|
spin_lock(&ht->lock);
|
||||||
list_for_each_entry(walker, &old_tbl->walkers, list)
|
list_for_each_entry(walker, &old_tbl->walkers, list)
|
||||||
walker->tbl = NULL;
|
walker->tbl = NULL;
|
||||||
|
spin_unlock(&ht->lock);
|
||||||
|
|
||||||
/* Wait for readers. All new readers will see the new
|
/* Wait for readers. All new readers will see the new
|
||||||
* table, and thus no references to the old table will
|
* table, and thus no references to the old table will
|
||||||
|
@ -635,12 +637,12 @@ void rhashtable_walk_stop(struct rhashtable_iter *iter)
|
||||||
|
|
||||||
ht = iter->ht;
|
ht = iter->ht;
|
||||||
|
|
||||||
mutex_lock(&ht->mutex);
|
spin_lock(&ht->lock);
|
||||||
if (tbl->rehash < tbl->size)
|
if (tbl->rehash < tbl->size)
|
||||||
list_add(&iter->walker->list, &tbl->walkers);
|
list_add(&iter->walker->list, &tbl->walkers);
|
||||||
else
|
else
|
||||||
iter->walker->tbl = NULL;
|
iter->walker->tbl = NULL;
|
||||||
mutex_unlock(&ht->mutex);
|
spin_unlock(&ht->lock);
|
||||||
|
|
||||||
iter->p = NULL;
|
iter->p = NULL;
|
||||||
|
|
||||||
|
@ -723,6 +725,7 @@ int rhashtable_init(struct rhashtable *ht,
|
||||||
|
|
||||||
memset(ht, 0, sizeof(*ht));
|
memset(ht, 0, sizeof(*ht));
|
||||||
mutex_init(&ht->mutex);
|
mutex_init(&ht->mutex);
|
||||||
|
spin_lock_init(&ht->lock);
|
||||||
memcpy(&ht->p, params, sizeof(*params));
|
memcpy(&ht->p, params, sizeof(*params));
|
||||||
|
|
||||||
if (params->min_size)
|
if (params->min_size)
|
||||||
|
|
Loading…
Reference in New Issue