Skip to content

Commit

Permalink
rhashtable: Fix race in rhashtable_destroy() and use regular work_struct
Browse files Browse the repository at this point in the history
When we put our declared work task in the global workqueue with
schedule_delayed_work(), its delay parameter is always zero.
Therefore, we should define a regular work in rhashtable structure
instead of a delayed work.

By the way, we add a condition to check whether resizing functions
are NULL before cancelling the work, avoiding to cancel an
uninitialized work.

Lastly, while we wait for all work items we submitted before to run
to completion with cancel_delayed_work(), ht->mutex has been taken in
rhashtable_destroy(). Moreover, cancel_delayed_work() doesn't return
until all work items are accomplished, and when work items are
scheduled, the work's function - rht_deferred_worker() will be called.
However, as rht_deferred_worker() also needs to acquire the lock,
deadlock might happen at the moment as the lock is already held before.
So if the cancel work function is moved out of the lock covered scope,
this will avoid the deadlock.

Fixes: 97defe1 ("rhashtable: Per bucket locks & deferred expansion/shrinking")
Signed-off-by: Ying Xue <ying.xue@windriver.com>
Cc: Thomas Graf <tgraf@suug.ch>
Acked-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Ying Xue authored and David S. Miller committed Jan 16, 2015
1 parent cbbd366 commit 57699a4
Show file tree
Hide file tree
Showing 2 changed files with 7 additions and 7 deletions.
2 changes: 1 addition & 1 deletion include/linux/rhashtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ struct rhashtable {
atomic_t nelems;
atomic_t shift;
struct rhashtable_params p;
struct delayed_work run_work;
struct work_struct run_work;
struct mutex mutex;
bool being_destroyed;
};
Expand Down
12 changes: 6 additions & 6 deletions lib/rhashtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ static void rht_deferred_worker(struct work_struct *work)
struct rhashtable *ht;
struct bucket_table *tbl;

ht = container_of(work, struct rhashtable, run_work.work);
ht = container_of(work, struct rhashtable, run_work);
mutex_lock(&ht->mutex);
tbl = rht_dereference(ht->tbl, ht);

Expand All @@ -507,7 +507,7 @@ static void rhashtable_wakeup_worker(struct rhashtable *ht)
if (tbl == new_tbl &&
((ht->p.grow_decision && ht->p.grow_decision(ht, size)) ||
(ht->p.shrink_decision && ht->p.shrink_decision(ht, size))))
schedule_delayed_work(&ht->run_work, 0);
schedule_work(&ht->run_work);
}

static void __rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj,
Expand Down Expand Up @@ -903,7 +903,7 @@ int rhashtable_init(struct rhashtable *ht, struct rhashtable_params *params)
get_random_bytes(&ht->p.hash_rnd, sizeof(ht->p.hash_rnd));

if (ht->p.grow_decision || ht->p.shrink_decision)
INIT_DEFERRABLE_WORK(&ht->run_work, rht_deferred_worker);
INIT_WORK(&ht->run_work, rht_deferred_worker);

return 0;
}
Expand All @@ -921,11 +921,11 @@ void rhashtable_destroy(struct rhashtable *ht)
{
ht->being_destroyed = true;

mutex_lock(&ht->mutex);
if (ht->p.grow_decision || ht->p.shrink_decision)
cancel_work_sync(&ht->run_work);

cancel_delayed_work(&ht->run_work);
mutex_lock(&ht->mutex);
bucket_table_free(rht_dereference(ht->tbl, ht));

mutex_unlock(&ht->mutex);
}
EXPORT_SYMBOL_GPL(rhashtable_destroy);
Expand Down

0 comments on commit 57699a4

Please sign in to comment.