From 4679e8933ae6b555e570a7e1b8963c7a0f59c6e5 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Tue, 19 Jun 2012 17:30:12 +0200 Subject: s390/dasd: add shutdown action Add a mechanism to wait for outstanding IO during shutdown. Schedule the block_bh and device_bh and wait until our request queues are empty. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky Signed-off-by: Heiko Carstens --- drivers/s390/block/dasd.c | 32 ++++++++++++++++++++++++++++++++ drivers/s390/block/dasd_eckd.c | 1 + drivers/s390/block/dasd_int.h | 1 + 3 files changed, 34 insertions(+) (limited to 'drivers') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index f3509120a507..2678a8347729 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -82,6 +82,7 @@ static void dasd_profile_exit(struct dasd_profile *); static wait_queue_head_t dasd_init_waitq; static wait_queue_head_t dasd_flush_wq; static wait_queue_head_t generic_waitq; +static wait_queue_head_t shutdown_waitq; /* * Allocate memory for a new device structure. @@ -1994,6 +1995,8 @@ static void dasd_device_tasklet(struct dasd_device *device) /* Now check if the head of the ccw queue needs to be started. */ __dasd_device_start_head(device); spin_unlock_irq(get_ccwdev_lock(device->cdev)); + if (waitqueue_active(&shutdown_waitq)) + wake_up(&shutdown_waitq); dasd_put_device(device); } @@ -2632,6 +2635,8 @@ static void dasd_block_tasklet(struct dasd_block *block) __dasd_block_start_head(block); spin_unlock(&block->queue_lock); spin_unlock_irq(&block->request_queue_lock); + if (waitqueue_active(&shutdown_waitq)) + wake_up(&shutdown_waitq); dasd_put_device(block->base); } @@ -3474,6 +3479,32 @@ char *dasd_get_sense(struct irb *irb) } EXPORT_SYMBOL_GPL(dasd_get_sense); +static inline int _wait_for_empty_queues(struct dasd_device *device) +{ + if (device->block) + return list_empty(&device->ccw_queue) && + list_empty(&device->block->ccw_queue); + else + return list_empty(&device->ccw_queue); +} + +void dasd_generic_shutdown(struct ccw_device *cdev) +{ + struct dasd_device *device; + + device = dasd_device_from_cdev(cdev); + if (IS_ERR(device)) + return; + + if (device->block) + dasd_schedule_block_bh(device->block); + + dasd_schedule_device_bh(device); + + wait_event(shutdown_waitq, _wait_for_empty_queues(device)); +} +EXPORT_SYMBOL_GPL(dasd_generic_shutdown); + static int __init dasd_init(void) { int rc; @@ -3481,6 +3512,7 @@ static int __init dasd_init(void) init_waitqueue_head(&dasd_init_waitq); init_waitqueue_head(&dasd_flush_wq); init_waitqueue_head(&generic_waitq); + init_waitqueue_head(&shutdown_waitq); /* register 'common' DASD debug area, used for all DBF_XXX calls */ dasd_debug_area = debug_register("dasd", 1, 1, 8 * sizeof(long)); diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index bc2e8a7c265b..fc0fe30b2ab5 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -4247,6 +4247,7 @@ static struct ccw_driver dasd_eckd_driver = { .set_online = dasd_eckd_set_online, .notify = dasd_generic_notify, .path_event = dasd_generic_path_event, + .shutdown = dasd_generic_shutdown, .freeze = dasd_generic_pm_freeze, .thaw = dasd_generic_restore_device, .restore = dasd_generic_restore_device, diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index c05da00583f0..297ac3b8c8ec 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -686,6 +686,7 @@ int dasd_generic_set_offline (struct ccw_device *cdev); int dasd_generic_notify(struct ccw_device *, int); int dasd_generic_last_path_gone(struct dasd_device *); int dasd_generic_path_operational(struct dasd_device *); +void dasd_generic_shutdown(struct ccw_device *); void dasd_generic_handle_state_change(struct dasd_device *); int dasd_generic_pm_freeze(struct ccw_device *); -- cgit v1.2.3-55-g7522