diff options
author | NeilBrown <neilb@suse.de> | 2011-04-18 04:25:42 -0400 |
---|---|---|
committer | NeilBrown <neilb@suse.de> | 2011-04-18 04:25:42 -0400 |
commit | 97658cdd3af7d01461874c93b89afa4a2465e7c6 (patch) | |
tree | d4e2f930850eb3981307a3eec8c86399dcebf68a /drivers/md/md.c | |
parent | 482c083492ddaa32ef5864bae3d143dc8bcdf7d1 (diff) |
md: provide generic support for handling unplug callbacks.
When an md device adds a request to a queue, it can call
mddev_check_plugged.
If this succeeds then we know that the md thread will be woken up
shortly, and ->plug_cnt will be non-zero until then, so some
processing can be delayed.
If it fails, then no unplug callback is expected and the make_request
function needs to do whatever is required to make the request happen.
Signed-off-by: NeilBrown <neilb@suse.de>
Diffstat (limited to 'drivers/md/md.c')
-rw-r--r-- | drivers/md/md.c | 56 |
1 files changed, 56 insertions, 0 deletions
diff --git a/drivers/md/md.c b/drivers/md/md.c index fb11170c717e..6e853c61d87e 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c | |||
@@ -445,6 +445,61 @@ void md_flush_request(mddev_t *mddev, struct bio *bio) | |||
445 | } | 445 | } |
446 | EXPORT_SYMBOL(md_flush_request); | 446 | EXPORT_SYMBOL(md_flush_request); |
447 | 447 | ||
448 | /* Support for plugging. | ||
449 | * This mirrors the plugging support in request_queue, but does not | ||
450 | * require having a whole queue or request structures. | ||
451 | * We allocate an md_plug_cb for each md device and each thread it gets | ||
452 | * plugged on. This links tot the private plug_handle structure in the | ||
453 | * personality data where we keep a count of the number of outstanding | ||
454 | * plugs so other code can see if a plug is active. | ||
455 | */ | ||
456 | struct md_plug_cb { | ||
457 | struct blk_plug_cb cb; | ||
458 | mddev_t *mddev; | ||
459 | }; | ||
460 | |||
461 | static void plugger_unplug(struct blk_plug_cb *cb) | ||
462 | { | ||
463 | struct md_plug_cb *mdcb = container_of(cb, struct md_plug_cb, cb); | ||
464 | if (atomic_dec_and_test(&mdcb->mddev->plug_cnt)) | ||
465 | md_wakeup_thread(mdcb->mddev->thread); | ||
466 | kfree(mdcb); | ||
467 | } | ||
468 | |||
469 | /* Check that an unplug wakeup will come shortly. | ||
470 | * If not, wakeup the md thread immediately | ||
471 | */ | ||
472 | int mddev_check_plugged(mddev_t *mddev) | ||
473 | { | ||
474 | struct blk_plug *plug = current->plug; | ||
475 | struct md_plug_cb *mdcb; | ||
476 | |||
477 | if (!plug) | ||
478 | return 0; | ||
479 | |||
480 | list_for_each_entry(mdcb, &plug->cb_list, cb.list) { | ||
481 | if (mdcb->cb.callback == plugger_unplug && | ||
482 | mdcb->mddev == mddev) { | ||
483 | /* Already on the list, move to top */ | ||
484 | if (mdcb != list_first_entry(&plug->cb_list, | ||
485 | struct md_plug_cb, | ||
486 | cb.list)) | ||
487 | list_move(&mdcb->cb.list, &plug->cb_list); | ||
488 | return 1; | ||
489 | } | ||
490 | } | ||
491 | /* Not currently on the callback list */ | ||
492 | mdcb = kmalloc(sizeof(*mdcb), GFP_ATOMIC); | ||
493 | if (!mdcb) | ||
494 | return 0; | ||
495 | |||
496 | mdcb->mddev = mddev; | ||
497 | mdcb->cb.callback = plugger_unplug; | ||
498 | atomic_inc(&mddev->plug_cnt); | ||
499 | list_add(&mdcb->cb.list, &plug->cb_list); | ||
500 | return 1; | ||
501 | } | ||
502 | EXPORT_SYMBOL_GPL(mddev_check_plugged); | ||
448 | 503 | ||
449 | static inline mddev_t *mddev_get(mddev_t *mddev) | 504 | static inline mddev_t *mddev_get(mddev_t *mddev) |
450 | { | 505 | { |
@@ -494,6 +549,7 @@ void mddev_init(mddev_t *mddev) | |||
494 | atomic_set(&mddev->active, 1); | 549 | atomic_set(&mddev->active, 1); |
495 | atomic_set(&mddev->openers, 0); | 550 | atomic_set(&mddev->openers, 0); |
496 | atomic_set(&mddev->active_io, 0); | 551 | atomic_set(&mddev->active_io, 0); |
552 | atomic_set(&mddev->plug_cnt, 0); | ||
497 | spin_lock_init(&mddev->write_lock); | 553 | spin_lock_init(&mddev->write_lock); |
498 | atomic_set(&mddev->flush_pending, 0); | 554 | atomic_set(&mddev->flush_pending, 0); |
499 | init_waitqueue_head(&mddev->sb_wait); | 555 | init_waitqueue_head(&mddev->sb_wait); |