aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArtem Bityutskiy <Artem.Bityutskiy@nokia.com>2010-07-25 07:29:12 -0400
committerJens Axboe <jaxboe@fusionio.com>2010-08-07 12:53:19 -0400
commit94eac5e62364df4e605e451218ee6024a7ba664f (patch)
tree5e4b7d85361ad20821ba3371acc1536262846f22
parent6f904ff0e39ea88f81eb77e8dfb4e1238492f0a8 (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>
-rw-r--r--mm/backing-dev.c28
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;