s390/dasd: add shutdown action
authorStefan Haberland <stefan.haberland@de.ibm.com>
Tue, 19 Jun 2012 15:30:12 +0000 (17:30 +0200)
committerHeiko Carstens <heiko.carstens@de.ibm.com>
Mon, 16 Jul 2012 08:52:50 +0000 (10:52 +0200)
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 <stefan.haberland@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
drivers/s390/block/dasd.c
drivers/s390/block/dasd_eckd.c
drivers/s390/block/dasd_int.h

index f3509120a507f56e3b152e1dee4cb5990ce23d79..2678a8347729b193414db7c5bde0bb24bce7d413 100644 (file)
@@ -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));
index bc2e8a7c265b518e8049bdb1877e0cf57a1b19a2..fc0fe30b2ab5b0d4d6232d9ab06e112579876f66 100644 (file)
@@ -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,
index c05da00583f06c94f5fbb12bb110c7f5a60875c8..297ac3b8c8ec5c5682a3ba705f21174aa9b507ca 100644 (file)
@@ -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 *);