Skip to content
This repository has been archived by the owner on Feb 26, 2020. It is now read-only.

Commit

Permalink
Fix race between taskq_destroy and dynamic spawning thread
Browse files Browse the repository at this point in the history
While taskq_destroy would wait for dynamic_taskq to finish its tasks, but it
does not implies the thread being spawned is up and running. This will cause
taskq to be freed before the thread can exit.

We fix this by using tq_nspawn to indicate how many threads are being spawned
before they are inserted to the thread list. And have taskq_destroy to wait
for it to drop to zero.

Signed-off-by: Chunwei Chen <[email protected]>
Fix #550
  • Loading branch information
Chunwei Chen committed May 23, 2016
1 parent fc6269d commit a815c53
Showing 1 changed file with 25 additions and 5 deletions.
30 changes: 25 additions & 5 deletions module/spl/spl-taskq.c
Original file line number Diff line number Diff line change
Expand Up @@ -763,11 +763,12 @@ taskq_thread_spawn_task(void *arg)
taskq_t *tq = (taskq_t *)arg;
unsigned long flags;

(void) taskq_thread_create(tq);

spin_lock_irqsave_nested(&tq->tq_lock, flags, tq->tq_lock_class);
tq->tq_nspawn--;
spin_unlock_irqrestore(&tq->tq_lock, flags);
if (taskq_thread_create(tq) == NULL) {
/* restore spawning count if failed */
spin_lock_irqsave_nested(&tq->tq_lock, flags, tq->tq_lock_class);
tq->tq_nspawn--;
spin_unlock_irqrestore(&tq->tq_lock, flags);
}
}

/*
Expand Down Expand Up @@ -848,6 +849,14 @@ taskq_thread(void *args)

tsd_set(taskq_tsd, tq);
spin_lock_irqsave_nested(&tq->tq_lock, flags, tq->tq_lock_class);
/*
* If we are dynamically spawned, decrease spawning count. Note that
* we could be created during taskq_create, in which case we shouldn't
* do the decrement. But it's fine because taskq_create will reset
* tq_nspawn later.
*/
if (tq->tq_flags & TASKQ_DYNAMIC)
tq->tq_nspawn--;

/* Immediately exit if more threads than allowed were created. */
if (tq->tq_nthreads >= tq->tq_maxthreads)
Expand Down Expand Up @@ -1063,6 +1072,11 @@ taskq_create(const char *name, int nthreads, pri_t pri,

/* Wait for all threads to be started before potential destroy */
wait_event(tq->tq_wait_waitq, tq->tq_nthreads == count);
/*
* taskq_thread might have touched nspawn, but we don't want them to
* because they're not dynamically spawned. So we reset it to 0
*/
tq->tq_nspawn = 0;

if (rc) {
taskq_destroy(tq);
Expand Down Expand Up @@ -1106,6 +1120,12 @@ taskq_destroy(taskq_t *tq)
up_write(&tq_list_sem);

spin_lock_irqsave_nested(&tq->tq_lock, flags, tq->tq_lock_class);
/* wait for spawning threads to insert themselves to the list */
while (tq->tq_nspawn) {
spin_unlock_irqrestore(&tq->tq_lock, flags);
schedule_timeout_interruptible(1);
spin_lock_irqsave_nested(&tq->tq_lock, flags, tq->tq_lock_class);
}

/*
* Signal each thread to exit and block until it does. Each thread
Expand Down

0 comments on commit a815c53

Please sign in to comment.