diff options
author | Artem Bityutskiy <Artem.Bityutskiy@nokia.com> | 2010-07-25 07:29:12 -0400 |
---|---|---|
committer | Jens Axboe <jaxboe@fusionio.com> | 2010-08-07 12:53:19 -0400 |
commit | 94eac5e62364df4e605e451218ee6024a7ba664f (patch) | |
tree | 5e4b7d85361ad20821ba3371acc1536262846f22 /mm | |
parent | 6f904ff0e39ea88f81eb77e8dfb4e1238492f0a8 (diff) |
writeback: fix possible race when creating bdi threads
This patch fixes a very unlikely race condition on the bdi forker thread error
path: when bdi thread creation fails, 'bdi->wb.task' may contain the error code
for a short period of time. If at the same time someone submits a work to this
bdi, we can end up with an oops 'bdi_queue_work()' while executing
'wake_up_process(wb->task)'.
This patch fixes the issue by introducing a temporary variable 'task' and
storing the possible error code there, so that 'wb->task' would never take
erroneous values.
Note, this race is very unlikely and I never hit it, so it is theoretical, but
nevertheless worth fixing.
This patch also merges 2 comments which were previously separate.
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/backing-dev.c | 28 |
1 files changed, 11 insertions, 17 deletions
diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 4e9ed2a8521f..327e36d1d623 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c | |||
@@ -331,8 +331,8 @@ static int bdi_forker_thread(void *ptr) | |||
331 | set_user_nice(current, 0); | 331 | set_user_nice(current, 0); |
332 | 332 | ||
333 | for (;;) { | 333 | for (;;) { |
334 | struct task_struct *task; | ||
334 | struct backing_dev_info *bdi, *tmp; | 335 | struct backing_dev_info *bdi, *tmp; |
335 | struct bdi_writeback *wb; | ||
336 | 336 | ||
337 | /* | 337 | /* |
338 | * Temporary measure, we want to make sure we don't see | 338 | * Temporary measure, we want to make sure we don't see |
@@ -383,29 +383,23 @@ static int bdi_forker_thread(void *ptr) | |||
383 | list_del_init(&bdi->bdi_list); | 383 | list_del_init(&bdi->bdi_list); |
384 | spin_unlock_bh(&bdi_lock); | 384 | spin_unlock_bh(&bdi_lock); |
385 | 385 | ||
386 | wb = &bdi->wb; | 386 | task = kthread_run(bdi_writeback_thread, &bdi->wb, "flush-%s", |
387 | wb->task = kthread_run(bdi_writeback_thread, wb, "flush-%s", | 387 | dev_name(bdi->dev)); |
388 | dev_name(bdi->dev)); | 388 | if (IS_ERR(task)) { |
389 | /* | ||
390 | * If thread creation fails, then readd the bdi to | ||
391 | * the pending list and force writeout of the bdi | ||
392 | * from this forker thread. That will free some memory | ||
393 | * and we can try again. | ||
394 | */ | ||
395 | if (IS_ERR(wb->task)) { | ||
396 | wb->task = NULL; | ||
397 | |||
398 | /* | 389 | /* |
399 | * Add this 'bdi' to the back, so we get | 390 | * If thread creation fails, then readd the bdi back to |
400 | * a chance to flush other bdi's to free | 391 | * the list and force writeout of the bdi from this |
401 | * memory. | 392 | * forker thread. That will free some memory and we can |
393 | * try again. Add it to the tail so we get a chance to | ||
394 | * flush other bdi's to free memory. | ||
402 | */ | 395 | */ |
403 | spin_lock_bh(&bdi_lock); | 396 | spin_lock_bh(&bdi_lock); |
404 | list_add_tail(&bdi->bdi_list, &bdi_pending_list); | 397 | list_add_tail(&bdi->bdi_list, &bdi_pending_list); |
405 | spin_unlock_bh(&bdi_lock); | 398 | spin_unlock_bh(&bdi_lock); |
406 | 399 | ||
407 | bdi_flush_io(bdi); | 400 | bdi_flush_io(bdi); |
408 | } | 401 | } else |
402 | bdi->wb.task = task; | ||
409 | } | 403 | } |
410 | 404 | ||
411 | return 0; | 405 | return 0; |