diff options
| author | Chris Mason <chris.mason@oracle.com> | 2009-09-15 20:02:33 -0400 |
|---|---|---|
| committer | Chris Mason <chris.mason@oracle.com> | 2009-09-15 20:20:17 -0400 |
| commit | 6e74057c4686dc12ea767b4bdc50a63876056e1c (patch) | |
| tree | e7c70b8e08ab9e5363be28bcbcc72348122ae6e4 /fs/btrfs/async-thread.c | |
| parent | 627e421a3f35ad6b52dc58982fb6f8a97c30dcd7 (diff) | |
Btrfs: Fix async thread shutdown race
It was possible for an async worker thread to be selected to
receive a new work item, but exit before the work item was
actually placed into that thread's work list.
This commit fixes the race by incrementing the num_pending
counter earlier, and making sure to check the number of pending
work items before a thread exits.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/async-thread.c')
| -rw-r--r-- | fs/btrfs/async-thread.c | 16 |
1 files changed, 10 insertions, 6 deletions
diff --git a/fs/btrfs/async-thread.c b/fs/btrfs/async-thread.c index 80e33bc96c84..282ca085c2fb 100644 --- a/fs/btrfs/async-thread.c +++ b/fs/btrfs/async-thread.c | |||
| @@ -191,7 +191,8 @@ static int try_worker_shutdown(struct btrfs_worker_thread *worker) | |||
| 191 | !worker->working && | 191 | !worker->working && |
| 192 | !list_empty(&worker->worker_list) && | 192 | !list_empty(&worker->worker_list) && |
| 193 | list_empty(&worker->prio_pending) && | 193 | list_empty(&worker->prio_pending) && |
| 194 | list_empty(&worker->pending)) { | 194 | list_empty(&worker->pending) && |
| 195 | atomic_read(&worker->num_pending) == 0) { | ||
| 195 | freeit = 1; | 196 | freeit = 1; |
| 196 | list_del_init(&worker->worker_list); | 197 | list_del_init(&worker->worker_list); |
| 197 | worker->workers->num_workers--; | 198 | worker->workers->num_workers--; |
| @@ -485,7 +486,6 @@ static struct btrfs_worker_thread *next_worker(struct btrfs_workers *workers) | |||
| 485 | */ | 486 | */ |
| 486 | next = workers->worker_list.next; | 487 | next = workers->worker_list.next; |
| 487 | worker = list_entry(next, struct btrfs_worker_thread, worker_list); | 488 | worker = list_entry(next, struct btrfs_worker_thread, worker_list); |
| 488 | atomic_inc(&worker->num_pending); | ||
| 489 | worker->sequence++; | 489 | worker->sequence++; |
| 490 | 490 | ||
| 491 | if (worker->sequence % workers->idle_thresh == 0) | 491 | if (worker->sequence % workers->idle_thresh == 0) |
| @@ -521,8 +521,7 @@ again: | |||
| 521 | goto again; | 521 | goto again; |
| 522 | } | 522 | } |
| 523 | } | 523 | } |
| 524 | spin_unlock_irqrestore(&workers->lock, flags); | 524 | goto found; |
| 525 | return worker; | ||
| 526 | 525 | ||
| 527 | fallback: | 526 | fallback: |
| 528 | fallback = NULL; | 527 | fallback = NULL; |
| @@ -537,6 +536,12 @@ fallback: | |||
| 537 | BUG_ON(!fallback); | 536 | BUG_ON(!fallback); |
| 538 | worker = list_entry(fallback, | 537 | worker = list_entry(fallback, |
| 539 | struct btrfs_worker_thread, worker_list); | 538 | struct btrfs_worker_thread, worker_list); |
| 539 | found: | ||
| 540 | /* | ||
| 541 | * this makes sure the worker doesn't exit before it is placed | ||
| 542 | * onto a busy/idle list | ||
| 543 | */ | ||
| 544 | atomic_inc(&worker->num_pending); | ||
| 540 | spin_unlock_irqrestore(&workers->lock, flags); | 545 | spin_unlock_irqrestore(&workers->lock, flags); |
| 541 | return worker; | 546 | return worker; |
| 542 | } | 547 | } |
| @@ -569,7 +574,7 @@ int btrfs_requeue_work(struct btrfs_work *work) | |||
| 569 | spin_lock(&worker->workers->lock); | 574 | spin_lock(&worker->workers->lock); |
| 570 | worker->idle = 0; | 575 | worker->idle = 0; |
| 571 | list_move_tail(&worker->worker_list, | 576 | list_move_tail(&worker->worker_list, |
| 572 | &worker->workers->worker_list); | 577 | &worker->workers->worker_list); |
| 573 | spin_unlock(&worker->workers->lock); | 578 | spin_unlock(&worker->workers->lock); |
| 574 | } | 579 | } |
| 575 | if (!worker->working) { | 580 | if (!worker->working) { |
| @@ -627,7 +632,6 @@ int btrfs_queue_worker(struct btrfs_workers *workers, struct btrfs_work *work) | |||
| 627 | list_add_tail(&work->list, &worker->prio_pending); | 632 | list_add_tail(&work->list, &worker->prio_pending); |
| 628 | else | 633 | else |
| 629 | list_add_tail(&work->list, &worker->pending); | 634 | list_add_tail(&work->list, &worker->pending); |
| 630 | atomic_inc(&worker->num_pending); | ||
| 631 | check_busy_worker(worker); | 635 | check_busy_worker(worker); |
| 632 | 636 | ||
| 633 | /* | 637 | /* |
