Merge branch 'topic/core-cleanup' into for-linus
[safe/jmp/linux-2.6] / drivers / s390 / block / dasd.c
index 0570794..fa2339c 100644 (file)
@@ -5,8 +5,7 @@
  *                 Carsten Otte <Cotte@de.ibm.com>
  *                 Martin Schwidefsky <schwidefsky@de.ibm.com>
  * Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
- *
+ * Copyright IBM Corp. 1999, 2009
  */
 
 #define KMSG_COMPONENT "dasd"
 #include <linux/slab.h>
 #include <linux/buffer_head.h>
 #include <linux/hdreg.h>
+#include <linux/async.h>
+#include <linux/mutex.h>
 
 #include <asm/ccwdev.h>
 #include <asm/ebcdic.h>
 #include <asm/idals.h>
-#include <asm/todclk.h>
 #include <asm/itcw.h>
+#include <asm/diag.h>
 
 /* This is ugly... */
 #define PRINTK_HEADER "dasd:"
@@ -36,6 +37,9 @@
  */
 #define DASD_CHANQ_MAX_SIZE 4
 
+#define DASD_SLEEPON_START_TAG (void *) 1
+#define DASD_SLEEPON_END_TAG   (void *) 2
+
 /*
  * SECTION: exported variables of dasd.c
  */
@@ -60,9 +64,11 @@ static int dasd_flush_block_queue(struct dasd_block *);
 static void dasd_device_tasklet(struct dasd_device *);
 static void dasd_block_tasklet(struct dasd_block *);
 static void do_kick_device(struct work_struct *);
+static void do_restore_device(struct work_struct *);
 static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *);
 static void dasd_device_timeout(unsigned long);
 static void dasd_block_timeout(unsigned long);
+static void __dasd_process_erp(struct dasd_device *, struct dasd_ccw_req *);
 
 /*
  * SECTION: Operations on the device structure.
@@ -108,8 +114,10 @@ struct dasd_device *dasd_alloc_device(void)
        device->timer.function = dasd_device_timeout;
        device->timer.data = (unsigned long) device;
        INIT_WORK(&device->kick_work, do_kick_device);
+       INIT_WORK(&device->restore_device, do_restore_device);
        device->state = DASD_STATE_NEW;
        device->target = DASD_STATE_NEW;
+       mutex_init(&device->state_mutex);
 
        return device;
 }
@@ -319,8 +327,8 @@ static int dasd_state_ready_to_basic(struct dasd_device *device)
                        device->state = DASD_STATE_READY;
                        return rc;
                }
-               dasd_destroy_partitions(block);
                dasd_flush_request_queue(block);
+               dasd_destroy_partitions(block);
                block->blocks = 0;
                block->bp_block = 0;
                block->s2b_shift = 0;
@@ -468,7 +476,7 @@ static int dasd_decrease_state(struct dasd_device *device)
  */
 static void dasd_change_state(struct dasd_device *device)
 {
-        int rc;
+       int rc;
 
        if (device->state == device->target)
                /* Already where we want to go today... */
@@ -477,8 +485,10 @@ static void dasd_change_state(struct dasd_device *device)
                rc = dasd_increase_state(device);
        else
                rc = dasd_decrease_state(device);
-        if (rc && rc != -EAGAIN)
-                device->target = device->state;
+       if (rc == -EAGAIN)
+               return;
+       if (rc)
+               device->target = device->state;
 
        if (device->state == device->target)
                wake_up(&dasd_init_waitq);
@@ -496,7 +506,9 @@ static void dasd_change_state(struct dasd_device *device)
 static void do_kick_device(struct work_struct *work)
 {
        struct dasd_device *device = container_of(work, struct dasd_device, kick_work);
+       mutex_lock(&device->state_mutex);
        dasd_change_state(device);
+       mutex_unlock(&device->state_mutex);
        dasd_schedule_device_bh(device);
        dasd_put_device(device);
 }
@@ -509,20 +521,43 @@ void dasd_kick_device(struct dasd_device *device)
 }
 
 /*
+ * dasd_restore_device will schedule a call do do_restore_device to the kernel
+ * event daemon.
+ */
+static void do_restore_device(struct work_struct *work)
+{
+       struct dasd_device *device = container_of(work, struct dasd_device,
+                                                 restore_device);
+       device->cdev->drv->restore(device->cdev);
+       dasd_put_device(device);
+}
+
+void dasd_restore_device(struct dasd_device *device)
+{
+       dasd_get_device(device);
+       /* queue call to dasd_restore_device to the kernel event daemon. */
+       schedule_work(&device->restore_device);
+}
+
+/*
  * Set the target state for a device and starts the state change.
  */
 void dasd_set_target_state(struct dasd_device *device, int target)
 {
+       dasd_get_device(device);
+       mutex_lock(&device->state_mutex);
        /* If we are in probeonly mode stop at DASD_STATE_READY. */
        if (dasd_probeonly && target > DASD_STATE_READY)
                target = DASD_STATE_READY;
        if (device->target != target) {
-                if (device->state == target)
+               if (device->state == target)
                        wake_up(&dasd_init_waitq);
                device->target = target;
        }
        if (device->state != device->target)
                dasd_change_state(device);
+       mutex_unlock(&device->state_mutex);
+       dasd_put_device(device);
 }
 
 /*
@@ -597,7 +632,7 @@ static void dasd_profile_end(struct dasd_block *block,
        if (dasd_profile_level != DASD_PROFILE_ON)
                return;
 
-       sectors = req->nr_sectors;
+       sectors = blk_rq_sectors(req);
        if (!cqr->buildclk || !cqr->startclk ||
            !cqr->stopclk || !cqr->endclk ||
            !sectors)
@@ -641,14 +676,14 @@ static void dasd_profile_end(struct dasd_block *block,
  * memory and 2) dasd_smalloc_request uses the static ccw memory
  * that gets allocated for each device.
  */
-struct dasd_ccw_req *dasd_kmalloc_request(char *magic, int cplength,
+struct dasd_ccw_req *dasd_kmalloc_request(int magic, int cplength,
                                          int datasize,
                                          struct dasd_device *device)
 {
        struct dasd_ccw_req *cqr;
 
        /* Sanity checks */
-       BUG_ON( magic == NULL || datasize > PAGE_SIZE ||
+       BUG_ON(datasize > PAGE_SIZE ||
             (cplength*sizeof(struct ccw1)) > PAGE_SIZE);
 
        cqr = kzalloc(sizeof(struct dasd_ccw_req), GFP_ATOMIC);
@@ -672,14 +707,13 @@ struct dasd_ccw_req *dasd_kmalloc_request(char *magic, int cplength,
                        return ERR_PTR(-ENOMEM);
                }
        }
-       strncpy((char *) &cqr->magic, magic, 4);
-       ASCEBC((char *) &cqr->magic, 4);
+       cqr->magic =  magic;
        set_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
        dasd_get_device(device);
        return cqr;
 }
 
-struct dasd_ccw_req *dasd_smalloc_request(char *magic, int cplength,
+struct dasd_ccw_req *dasd_smalloc_request(int magic, int cplength,
                                          int datasize,
                                          struct dasd_device *device)
 {
@@ -689,7 +723,7 @@ struct dasd_ccw_req *dasd_smalloc_request(char *magic, int cplength,
        int size;
 
        /* Sanity checks */
-       BUG_ON( magic == NULL || datasize > PAGE_SIZE ||
+       BUG_ON(datasize > PAGE_SIZE ||
             (cplength*sizeof(struct ccw1)) > PAGE_SIZE);
 
        size = (sizeof(struct dasd_ccw_req) + 7L) & -8L;
@@ -716,8 +750,7 @@ struct dasd_ccw_req *dasd_smalloc_request(char *magic, int cplength,
                cqr->data = data;
                memset(cqr->data, 0, datasize);
        }
-       strncpy((char *) &cqr->magic, magic, 4);
-       ASCEBC((char *) &cqr->magic, 4);
+       cqr->magic = magic;
        set_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
        dasd_get_device(device);
        return cqr;
@@ -845,8 +878,10 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
 
        /* Check the cqr */
        rc = dasd_check_cqr(cqr);
-       if (rc)
+       if (rc) {
+               cqr->intrc = rc;
                return rc;
+       }
        device = (struct dasd_device *) cqr->startdev;
        if (cqr->retries < 0) {
                /* internal error 14 - start_IO run out of retries */
@@ -869,9 +904,6 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
        switch (rc) {
        case 0:
                cqr->status = DASD_CQR_IN_IO;
-               DBF_DEV_EVENT(DBF_DEBUG, device,
-                             "start_IO: request %p started successful",
-                             cqr);
                break;
        case -EBUSY:
                DBF_DEV_EVENT(DBF_DEBUG, device, "%s",
@@ -900,6 +932,12 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
                DBF_DEV_EVENT(DBF_DEBUG, device, "%s",
                              "start_IO: -EIO device gone, retry");
                break;
+       case -EINVAL:
+               /* most likely caused in power management context */
+               DBF_DEV_EVENT(DBF_DEBUG, device, "%s",
+                             "start_IO: -EINVAL device currently "
+                             "not accessible");
+               break;
        default:
                /* internal error 11 - unknown rc */
                snprintf(errorstring, ERRORLENGTH, "11 %d", rc);
@@ -909,6 +947,7 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
                BUG();
                break;
        }
+       cqr->intrc = rc;
        return rc;
 }
 
@@ -928,7 +967,7 @@ static void dasd_device_timeout(unsigned long ptr)
        device = (struct dasd_device *) ptr;
        spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
        /* re-activate request queue */
-        device->stopped &= ~DASD_STOPPED_PENDING;
+       dasd_device_remove_stop_bits(device, DASD_STOPPED_PENDING);
        spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
        dasd_schedule_device_bh(device);
 }
@@ -962,19 +1001,26 @@ static void dasd_handle_killed_request(struct ccw_device *cdev,
                return;
        cqr = (struct dasd_ccw_req *) intparm;
        if (cqr->status != DASD_CQR_IN_IO) {
-               DBF_EVENT(DBF_DEBUG,
-                       "invalid status in handle_killed_request: "
-                       "bus_id %s, status %02x",
-                       dev_name(&cdev->dev), cqr->status);
+               DBF_EVENT_DEVID(DBF_DEBUG, cdev,
+                               "invalid status in handle_killed_request: "
+                               "%02x", cqr->status);
                return;
        }
 
-       device = (struct dasd_device *) cqr->startdev;
-       if (device == NULL ||
-           device != dasd_device_from_cdev_locked(cdev) ||
-           strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
-               DBF_DEV_EVENT(DBF_DEBUG, device, "invalid device in request: "
-                             "bus_id %s", dev_name(&cdev->dev));
+       device = dasd_device_from_cdev_locked(cdev);
+       if (IS_ERR(device)) {
+               DBF_EVENT_DEVID(DBF_DEBUG, cdev, "%s",
+                               "unable to get device from cdev");
+               return;
+       }
+
+       if (!cqr->startdev ||
+           device != cqr->startdev ||
+           strncmp(cqr->startdev->discipline->ebcname,
+                   (char *) &cqr->magic, 4)) {
+               DBF_EVENT_DEVID(DBF_DEBUG, cdev, "%s",
+                               "invalid device in request");
+               dasd_put_device(device);
                return;
        }
 
@@ -991,7 +1037,7 @@ void dasd_generic_handle_state_change(struct dasd_device *device)
        /* First of all start sense subsystem status request. */
        dasd_eer_snss(device);
 
-       device->stopped &= ~DASD_STOPPED_PENDING;
+       dasd_device_remove_stop_bits(device, DASD_STOPPED_PENDING);
        dasd_schedule_device_bh(device);
        if (device->block)
                dasd_schedule_block_bh(device->block);
@@ -1013,12 +1059,13 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
                case -EIO:
                        break;
                case -ETIMEDOUT:
-                       DBF_EVENT(DBF_WARNING, "%s(%s): request timed out\n",
-                              __func__, dev_name(&cdev->dev));
+                       DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s: "
+                                       "request timed out\n", __func__);
                        break;
                default:
-                       DBF_EVENT(DBF_WARNING, "%s(%s): unknown error %ld\n",
-                              __func__, dev_name(&cdev->dev), PTR_ERR(irb));
+                       DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s: "
+                                       "unknown error %ld\n", __func__,
+                                       PTR_ERR(irb));
                }
                dasd_handle_killed_request(cdev, intparm);
                return;
@@ -1046,8 +1093,8 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
        device = (struct dasd_device *) cqr->startdev;
        if (!device ||
            strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
-               DBF_DEV_EVENT(DBF_DEBUG, device, "invalid device in request: "
-                             "bus_id %s", dev_name(&cdev->dev));
+               DBF_EVENT_DEVID(DBF_DEBUG, cdev, "%s",
+                               "invalid device in request");
                return;
        }
 
@@ -1373,6 +1420,20 @@ void dasd_schedule_device_bh(struct dasd_device *device)
        tasklet_hi_schedule(&device->tasklet);
 }
 
+void dasd_device_set_stop_bits(struct dasd_device *device, int bits)
+{
+       device->stopped |= bits;
+}
+EXPORT_SYMBOL_GPL(dasd_device_set_stop_bits);
+
+void dasd_device_remove_stop_bits(struct dasd_device *device, int bits)
+{
+       device->stopped &= ~bits;
+       if (!device->stopped)
+               wake_up(&generic_waitq);
+}
+EXPORT_SYMBOL_GPL(dasd_device_remove_stop_bits);
+
 /*
  * Queue a request to the head of the device ccw_queue.
  * Start the I/O if possible.
@@ -1414,7 +1475,10 @@ void dasd_add_request_tail(struct dasd_ccw_req *cqr)
  */
 static void dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data)
 {
-       wake_up((wait_queue_head_t *) data);
+       spin_lock_irq(get_ccwdev_lock(cqr->startdev->cdev));
+       cqr->callback_data = DASD_SLEEPON_END_TAG;
+       spin_unlock_irq(get_ccwdev_lock(cqr->startdev->cdev));
+       wake_up(&generic_waitq);
 }
 
 static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr)
@@ -1424,56 +1488,141 @@ static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr)
 
        device = cqr->startdev;
        spin_lock_irq(get_ccwdev_lock(device->cdev));
-       rc = ((cqr->status == DASD_CQR_DONE ||
-              cqr->status == DASD_CQR_NEED_ERP ||
-              cqr->status == DASD_CQR_TERMINATED) &&
-             list_empty(&cqr->devlist));
+       rc = (cqr->callback_data == DASD_SLEEPON_END_TAG);
        spin_unlock_irq(get_ccwdev_lock(device->cdev));
        return rc;
 }
 
 /*
- * Queue a request to the tail of the device ccw_queue and wait for
- * it's completion.
+ * checks if error recovery is necessary, returns 1 if yes, 0 otherwise.
  */
-int dasd_sleep_on(struct dasd_ccw_req *cqr)
+static int __dasd_sleep_on_erp(struct dasd_ccw_req *cqr)
 {
        struct dasd_device *device;
-       int rc;
+       dasd_erp_fn_t erp_fn;
 
+       if (cqr->status == DASD_CQR_FILLED)
+               return 0;
        device = cqr->startdev;
+       if (test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) {
+               if (cqr->status == DASD_CQR_TERMINATED) {
+                       device->discipline->handle_terminated_request(cqr);
+                       return 1;
+               }
+               if (cqr->status == DASD_CQR_NEED_ERP) {
+                       erp_fn = device->discipline->erp_action(cqr);
+                       erp_fn(cqr);
+                       return 1;
+               }
+               if (cqr->status == DASD_CQR_FAILED)
+                       dasd_log_sense(cqr, &cqr->irb);
+               if (cqr->refers) {
+                       __dasd_process_erp(device, cqr);
+                       return 1;
+               }
+       }
+       return 0;
+}
 
-       cqr->callback = dasd_wakeup_cb;
-       cqr->callback_data = (void *) &generic_waitq;
-       dasd_add_request_tail(cqr);
-       wait_event(generic_waitq, _wait_for_wakeup(cqr));
+static int __dasd_sleep_on_loop_condition(struct dasd_ccw_req *cqr)
+{
+       if (test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) {
+               if (cqr->refers) /* erp is not done yet */
+                       return 1;
+               return ((cqr->status != DASD_CQR_DONE) &&
+                       (cqr->status != DASD_CQR_FAILED));
+       } else
+               return (cqr->status == DASD_CQR_FILLED);
+}
+
+static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible)
+{
+       struct dasd_device *device;
+       int rc;
+       struct list_head ccw_queue;
+       struct dasd_ccw_req *cqr;
+
+       INIT_LIST_HEAD(&ccw_queue);
+       maincqr->status = DASD_CQR_FILLED;
+       device = maincqr->startdev;
+       list_add(&maincqr->blocklist, &ccw_queue);
+       for (cqr = maincqr;  __dasd_sleep_on_loop_condition(cqr);
+            cqr = list_first_entry(&ccw_queue,
+                                   struct dasd_ccw_req, blocklist)) {
+
+               if (__dasd_sleep_on_erp(cqr))
+                       continue;
+               if (cqr->status != DASD_CQR_FILLED) /* could be failed */
+                       continue;
+
+               /* Non-temporary stop condition will trigger fail fast */
+               if (device->stopped & ~DASD_STOPPED_PENDING &&
+                   test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&
+                   (!dasd_eer_enabled(device))) {
+                       cqr->status = DASD_CQR_FAILED;
+                       continue;
+               }
+
+               /* Don't try to start requests if device is stopped */
+               if (interruptible) {
+                       rc = wait_event_interruptible(
+                               generic_waitq, !(device->stopped));
+                       if (rc == -ERESTARTSYS) {
+                               cqr->status = DASD_CQR_FAILED;
+                               maincqr->intrc = rc;
+                               continue;
+                       }
+               } else
+                       wait_event(generic_waitq, !(device->stopped));
 
-       /* Request status is either done or failed. */
-       rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
+               cqr->callback = dasd_wakeup_cb;
+               cqr->callback_data = DASD_SLEEPON_START_TAG;
+               dasd_add_request_tail(cqr);
+               if (interruptible) {
+                       rc = wait_event_interruptible(
+                               generic_waitq, _wait_for_wakeup(cqr));
+                       if (rc == -ERESTARTSYS) {
+                               dasd_cancel_req(cqr);
+                               /* wait (non-interruptible) for final status */
+                               wait_event(generic_waitq,
+                                          _wait_for_wakeup(cqr));
+                               cqr->status = DASD_CQR_FAILED;
+                               maincqr->intrc = rc;
+                               continue;
+                       }
+               } else
+                       wait_event(generic_waitq, _wait_for_wakeup(cqr));
+       }
+
+       maincqr->endclk = get_clock();
+       if ((maincqr->status != DASD_CQR_DONE) &&
+           (maincqr->intrc != -ERESTARTSYS))
+               dasd_log_sense(maincqr, &maincqr->irb);
+       if (maincqr->status == DASD_CQR_DONE)
+               rc = 0;
+       else if (maincqr->intrc)
+               rc = maincqr->intrc;
+       else
+               rc = -EIO;
        return rc;
 }
 
 /*
+ * Queue a request to the tail of the device ccw_queue and wait for
+ * it's completion.
+ */
+int dasd_sleep_on(struct dasd_ccw_req *cqr)
+{
+       return _dasd_sleep_on(cqr, 0);
+}
+
+/*
  * Queue a request to the tail of the device ccw_queue and wait
  * interruptible for it's completion.
  */
 int dasd_sleep_on_interruptible(struct dasd_ccw_req *cqr)
 {
-       struct dasd_device *device;
-       int rc;
-
-       device = cqr->startdev;
-       cqr->callback = dasd_wakeup_cb;
-       cqr->callback_data = (void *) &generic_waitq;
-       dasd_add_request_tail(cqr);
-       rc = wait_event_interruptible(generic_waitq, _wait_for_wakeup(cqr));
-       if (rc == -ERESTARTSYS) {
-               dasd_cancel_req(cqr);
-               /* wait (non-interruptible) for final status */
-               wait_event(generic_waitq, _wait_for_wakeup(cqr));
-       }
-       rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
-       return rc;
+       return _dasd_sleep_on(cqr, 1);
 }
 
 /*
@@ -1506,7 +1655,7 @@ int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr)
        }
 
        cqr->callback = dasd_wakeup_cb;
-       cqr->callback_data = (void *) &generic_waitq;
+       cqr->callback_data = DASD_SLEEPON_START_TAG;
        cqr->status = DASD_CQR_QUEUED;
        list_add(&cqr->devlist, &device->ccw_queue);
 
@@ -1517,8 +1666,12 @@ int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr)
 
        wait_event(generic_waitq, _wait_for_wakeup(cqr));
 
-       /* Request status is either done or failed. */
-       rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
+       if (cqr->status == DASD_CQR_DONE)
+               rc = 0;
+       else if (cqr->intrc)
+               rc = cqr->intrc;
+       else
+               rc = -EIO;
        return rc;
 }
 
@@ -1554,7 +1707,6 @@ int dasd_cancel_req(struct dasd_ccw_req *cqr)
                                cqr, rc);
                } else {
                        cqr->stopclk = get_clock();
-                       rc = 1;
                }
                break;
        default: /* already finished or clear pending - do nothing */
@@ -1583,7 +1735,7 @@ static void dasd_block_timeout(unsigned long ptr)
        block = (struct dasd_block *) ptr;
        spin_lock_irqsave(get_ccwdev_lock(block->base->cdev), flags);
        /* re-activate request queue */
-       block->base->stopped &= ~DASD_STOPPED_PENDING;
+       dasd_device_remove_stop_bits(block->base, DASD_STOPPED_PENDING);
        spin_unlock_irqrestore(get_ccwdev_lock(block->base->cdev), flags);
        dasd_schedule_block_bh(block);
 }
@@ -1608,22 +1760,12 @@ void dasd_block_clear_timer(struct dasd_block *block)
 }
 
 /*
- * posts the buffer_cache about a finalized request
- */
-static inline void dasd_end_request(struct request *req, int error)
-{
-       if (__blk_end_request(req, error, blk_rq_bytes(req)))
-               BUG();
-}
-
-/*
  * Process finished error recovery ccw.
  */
-static inline void __dasd_block_process_erp(struct dasd_block *block,
-                                           struct dasd_ccw_req *cqr)
+static void __dasd_process_erp(struct dasd_device *device,
+                              struct dasd_ccw_req *cqr)
 {
        dasd_erp_fn_t erp_fn;
-       struct dasd_device *device = block->base;
 
        if (cqr->status == DASD_CQR_DONE)
                DBF_DEV_EVENT(DBF_NOTICE, device, "%s", "ERP successful");
@@ -1656,21 +1798,20 @@ static void __dasd_process_request_queue(struct dasd_block *block)
         * for that. State DASD_STATE_ONLINE is normal block device
         * operation.
         */
-       if (basedev->state < DASD_STATE_READY)
+       if (basedev->state < DASD_STATE_READY) {
+               while ((req = blk_fetch_request(block->request_queue)))
+                       __blk_end_request_all(req, -EIO);
                return;
+       }
        /* Now we try to fetch requests from the request queue */
-       while (!blk_queue_plugged(queue) &&
-              elv_next_request(queue)) {
-
-               req = elv_next_request(queue);
-
+       while (!blk_queue_plugged(queue) && (req = blk_peek_request(queue))) {
                if (basedev->features & DASD_FEATURE_READONLY &&
                    rq_data_dir(req) == WRITE) {
                        DBF_DEV_EVENT(DBF_ERR, basedev,
                                      "Rejecting write request %p",
                                      req);
-                       blkdev_dequeue_request(req);
-                       dasd_end_request(req, -EIO);
+                       blk_start_request(req);
+                       __blk_end_request_all(req, -EIO);
                        continue;
                }
                cqr = basedev->discipline->build_cp(basedev, block, req);
@@ -1688,9 +1829,12 @@ static void __dasd_process_request_queue(struct dasd_block *block)
                                 */
                                if (!list_empty(&block->ccw_queue))
                                        break;
-                               spin_lock_irqsave(get_ccwdev_lock(basedev->cdev), flags);
-                               basedev->stopped |= DASD_STOPPED_PENDING;
-                               spin_unlock_irqrestore(get_ccwdev_lock(basedev->cdev), flags);
+                               spin_lock_irqsave(
+                                       get_ccwdev_lock(basedev->cdev), flags);
+                               dasd_device_set_stop_bits(basedev,
+                                                         DASD_STOPPED_PENDING);
+                               spin_unlock_irqrestore(
+                                       get_ccwdev_lock(basedev->cdev), flags);
                                dasd_block_set_timer(block, HZ/2);
                                break;
                        }
@@ -1698,8 +1842,8 @@ static void __dasd_process_request_queue(struct dasd_block *block)
                                      "CCW creation failed (rc=%ld) "
                                      "on request %p",
                                      PTR_ERR(cqr), req);
-                       blkdev_dequeue_request(req);
-                       dasd_end_request(req, -EIO);
+                       blk_start_request(req);
+                       __blk_end_request_all(req, -EIO);
                        continue;
                }
                /*
@@ -1708,7 +1852,7 @@ static void __dasd_process_request_queue(struct dasd_block *block)
                 */
                cqr->callback_data = (void *) req;
                cqr->status = DASD_CQR_FILLED;
-               blkdev_dequeue_request(req);
+               blk_start_request(req);
                list_add_tail(&cqr->blocklist, &block->ccw_queue);
                dasd_profile_start(block, cqr, req);
        }
@@ -1725,7 +1869,7 @@ static void __dasd_cleanup_cqr(struct dasd_ccw_req *cqr)
        status = cqr->block->base->discipline->free_cp(cqr, req);
        if (status <= 0)
                error = status ? status : -EIO;
-       dasd_end_request(req, error);
+       __blk_end_request_all(req, error);
 }
 
 /*
@@ -1758,7 +1902,8 @@ restart:
                /*  Process requests that may be recovered */
                if (cqr->status == DASD_CQR_NEED_ERP) {
                        erp_fn = base->discipline->erp_action(cqr);
-                       erp_fn(cqr);
+                       if (IS_ERR(erp_fn(cqr)))
+                               continue;
                        goto restart;
                }
 
@@ -1776,7 +1921,7 @@ restart:
                        cqr->status = DASD_CQR_FILLED;
                        cqr->retries = 255;
                        spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
-                       base->stopped |= DASD_STOPPED_QUIESCE;
+                       dasd_device_set_stop_bits(base, DASD_STOPPED_QUIESCE);
                        spin_unlock_irqrestore(get_ccwdev_lock(base->cdev),
                                               flags);
                        goto restart;
@@ -1784,7 +1929,7 @@ restart:
 
                /* Process finished ERP request. */
                if (cqr->refers) {
-                       __dasd_block_process_erp(block, cqr);
+                       __dasd_process_erp(base, cqr);
                        goto restart;
                }
 
@@ -1915,7 +2060,7 @@ restart_cb:
                /* Process finished ERP request. */
                if (cqr->refers) {
                        spin_lock_bh(&block->queue_lock);
-                       __dasd_block_process_erp(block, cqr);
+                       __dasd_process_erp(block->base, cqr);
                        spin_unlock_bh(&block->queue_lock);
                        /* restart list_for_xx loop since dasd_process_erp
                         * might remove multiple elements */
@@ -1997,11 +2142,10 @@ static void dasd_setup_queue(struct dasd_block *block)
 {
        int max;
 
-       blk_queue_hardsect_size(block->request_queue, block->bp_block);
+       blk_queue_logical_block_size(block->request_queue, block->bp_block);
        max = block->base->discipline->max_blocks << block->s2b_shift;
-       blk_queue_max_sectors(block->request_queue, max);
-       blk_queue_max_phys_segments(block->request_queue, -1L);
-       blk_queue_max_hw_segments(block->request_queue, -1L);
+       blk_queue_max_hw_sectors(block->request_queue, max);
+       blk_queue_max_segments(block->request_queue, -1L);
        /* with page sized segments we can translate each segement into
         * one idaw/tidaw
         */
@@ -2032,19 +2176,21 @@ static void dasd_flush_request_queue(struct dasd_block *block)
                return;
 
        spin_lock_irq(&block->request_queue_lock);
-       while ((req = elv_next_request(block->request_queue))) {
-               blkdev_dequeue_request(req);
-               dasd_end_request(req, -EIO);
-       }
+       while ((req = blk_fetch_request(block->request_queue)))
+               __blk_end_request_all(req, -EIO);
        spin_unlock_irq(&block->request_queue_lock);
 }
 
 static int dasd_open(struct block_device *bdev, fmode_t mode)
 {
        struct dasd_block *block = bdev->bd_disk->private_data;
-       struct dasd_device *base = block->base;
+       struct dasd_device *base;
        int rc;
 
+       if (!block)
+               return -ENODEV;
+
+       base = block->base;
        atomic_inc(&block->open_count);
        if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) {
                rc = -ENODEV;
@@ -2071,6 +2217,13 @@ static int dasd_open(struct block_device *bdev, fmode_t mode)
                goto out;
        }
 
+       if ((mode & FMODE_WRITE) &&
+           (test_bit(DASD_FLAG_DEVICE_RO, &base->flags) ||
+            (base->features & DASD_FEATURE_READONLY))) {
+               rc = -EROFS;
+               goto out;
+       }
+
        return 0;
 
 out:
@@ -2098,9 +2251,9 @@ static int dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
        struct dasd_device *base;
 
        block = bdev->bd_disk->private_data;
-       base = block->base;
        if (!block)
                return -ENODEV;
+       base = block->base;
 
        if (!base->discipline ||
            !base->discipline->fill_geometry)
@@ -2111,7 +2264,7 @@ static int dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
        return 0;
 }
 
-struct block_device_operations
+const struct block_device_operations
 dasd_device_operations = {
        .owner          = THIS_MODULE,
        .open           = dasd_open,
@@ -2149,6 +2302,45 @@ dasd_exit(void)
  */
 
 /*
+ * Is the device read-only?
+ * Note that this function does not report the setting of the
+ * readonly device attribute, but how it is configured in z/VM.
+ */
+int dasd_device_is_ro(struct dasd_device *device)
+{
+       struct ccw_dev_id dev_id;
+       struct diag210 diag_data;
+       int rc;
+
+       if (!MACHINE_IS_VM)
+               return 0;
+       ccw_device_get_id(device->cdev, &dev_id);
+       memset(&diag_data, 0, sizeof(diag_data));
+       diag_data.vrdcdvno = dev_id.devno;
+       diag_data.vrdclen = sizeof(diag_data);
+       rc = diag210(&diag_data);
+       if (rc == 0 || rc == 2) {
+               return diag_data.vrdcvfla & 0x80;
+       } else {
+               DBF_EVENT(DBF_WARNING, "diag210 failed for dev=%04x with rc=%d",
+                         dev_id.devno, rc);
+               return 0;
+       }
+}
+EXPORT_SYMBOL_GPL(dasd_device_is_ro);
+
+static void dasd_generic_auto_online(void *data, async_cookie_t cookie)
+{
+       struct ccw_device *cdev = data;
+       int ret;
+
+       ret = ccw_device_set_online(cdev);
+       if (ret)
+               pr_warning("%s: Setting the DASD online failed with rc=%d\n",
+                          dev_name(&cdev->dev), ret);
+}
+
+/*
  * Initial attempt at a probe function. this can be simplified once
  * the other detection code is gone.
  */
@@ -2157,18 +2349,11 @@ int dasd_generic_probe(struct ccw_device *cdev,
 {
        int ret;
 
-       ret = ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP);
-       if (ret) {
-               DBF_EVENT(DBF_WARNING,
-                      "dasd_generic_probe: could not set ccw-device options "
-                      "for %s\n", dev_name(&cdev->dev));
-               return ret;
-       }
        ret = dasd_add_sysfs_files(cdev);
        if (ret) {
-               DBF_EVENT(DBF_WARNING,
-                      "dasd_generic_probe: could not add sysfs entries "
-                      "for %s\n", dev_name(&cdev->dev));
+               DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s",
+                               "dasd_generic_probe: could not add "
+                               "sysfs entries");
                return ret;
        }
        cdev->handler = &dasd_int_handler;
@@ -2180,10 +2365,7 @@ int dasd_generic_probe(struct ccw_device *cdev,
         */
        if ((dasd_get_feature(cdev, DASD_FEATURE_INITIAL_ONLINE) > 0 ) ||
            (dasd_autodetect && dasd_busid_known(dev_name(&cdev->dev)) != 0))
-               ret = ccw_device_set_online(cdev);
-       if (ret)
-               pr_warning("%s: Setting the DASD online failed with rc=%d\n",
-                      dev_name(&cdev->dev), ret);
+               async_schedule(dasd_generic_auto_online, cdev);
        return 0;
 }
 
@@ -2291,12 +2473,9 @@ int dasd_generic_set_online(struct ccw_device *cdev,
                pr_debug("dasd_generic device %s found\n",
                                dev_name(&cdev->dev));
 
-       /* FIXME: we have to wait for the root device but we don't want
-        * to wait for each single device but for all at once. */
        wait_event(dasd_init_waitq, _wait_for_device(device));
 
        dasd_put_device(device);
-
        return rc;
 }
 
@@ -2376,14 +2555,20 @@ int dasd_generic_notify(struct ccw_device *cdev, int event)
                                cqr->status = DASD_CQR_QUEUED;
                                cqr->retries++;
                        }
-               device->stopped |= DASD_STOPPED_DC_WAIT;
+               dasd_device_set_stop_bits(device, DASD_STOPPED_DC_WAIT);
                dasd_device_clear_timer(device);
                dasd_schedule_device_bh(device);
                ret = 1;
                break;
        case CIO_OPER:
                /* FIXME: add a sanity check. */
-               device->stopped &= ~DASD_STOPPED_DC_WAIT;
+               dasd_device_remove_stop_bits(device, DASD_STOPPED_DC_WAIT);
+               if (device->stopped & DASD_UNRESUMED_PM) {
+                       dasd_device_remove_stop_bits(device, DASD_UNRESUMED_PM);
+                       dasd_restore_device(device);
+                       ret = 1;
+                       break;
+               }
                dasd_schedule_device_bh(device);
                if (device->block)
                        dasd_schedule_block_bh(device->block);
@@ -2394,13 +2579,102 @@ int dasd_generic_notify(struct ccw_device *cdev, int event)
        return ret;
 }
 
+int dasd_generic_pm_freeze(struct ccw_device *cdev)
+{
+       struct dasd_ccw_req *cqr, *n;
+       int rc;
+       struct list_head freeze_queue;
+       struct dasd_device *device = dasd_device_from_cdev(cdev);
+
+       if (IS_ERR(device))
+               return PTR_ERR(device);
+       /* disallow new I/O  */
+       dasd_device_set_stop_bits(device, DASD_STOPPED_PM);
+       /* clear active requests */
+       INIT_LIST_HEAD(&freeze_queue);
+       spin_lock_irq(get_ccwdev_lock(cdev));
+       rc = 0;
+       list_for_each_entry_safe(cqr, n, &device->ccw_queue, devlist) {
+               /* Check status and move request to flush_queue */
+               if (cqr->status == DASD_CQR_IN_IO) {
+                       rc = device->discipline->term_IO(cqr);
+                       if (rc) {
+                               /* unable to terminate requeust */
+                               dev_err(&device->cdev->dev,
+                                       "Unable to terminate request %p "
+                                       "on suspend\n", cqr);
+                               spin_unlock_irq(get_ccwdev_lock(cdev));
+                               dasd_put_device(device);
+                               return rc;
+                       }
+               }
+               list_move_tail(&cqr->devlist, &freeze_queue);
+       }
+
+       spin_unlock_irq(get_ccwdev_lock(cdev));
+
+       list_for_each_entry_safe(cqr, n, &freeze_queue, devlist) {
+               wait_event(dasd_flush_wq,
+                          (cqr->status != DASD_CQR_CLEAR_PENDING));
+               if (cqr->status == DASD_CQR_CLEARED)
+                       cqr->status = DASD_CQR_QUEUED;
+       }
+       /* move freeze_queue to start of the ccw_queue */
+       spin_lock_irq(get_ccwdev_lock(cdev));
+       list_splice_tail(&freeze_queue, &device->ccw_queue);
+       spin_unlock_irq(get_ccwdev_lock(cdev));
+
+       if (device->discipline->freeze)
+               rc = device->discipline->freeze(device);
+
+       dasd_put_device(device);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(dasd_generic_pm_freeze);
+
+int dasd_generic_restore_device(struct ccw_device *cdev)
+{
+       struct dasd_device *device = dasd_device_from_cdev(cdev);
+       int rc = 0;
+
+       if (IS_ERR(device))
+               return PTR_ERR(device);
+
+       /* allow new IO again */
+       dasd_device_remove_stop_bits(device,
+                                    (DASD_STOPPED_PM | DASD_UNRESUMED_PM));
+
+       dasd_schedule_device_bh(device);
+
+       /*
+        * call discipline restore function
+        * if device is stopped do nothing e.g. for disconnected devices
+        */
+       if (device->discipline->restore && !(device->stopped))
+               rc = device->discipline->restore(device);
+       if (rc || device->stopped)
+               /*
+                * if the resume failed for the DASD we put it in
+                * an UNRESUMED stop state
+                */
+               device->stopped |= DASD_UNRESUMED_PM;
+
+       if (device->block)
+               dasd_schedule_block_bh(device->block);
+
+       dasd_put_device(device);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(dasd_generic_restore_device);
+
 static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device,
                                                   void *rdc_buffer,
                                                   int rdc_buffer_size,
-                                                  char *magic)
+                                                  int magic)
 {
        struct dasd_ccw_req *cqr;
        struct ccw1 *ccw;
+       unsigned long *idaw;
 
        cqr = dasd_smalloc_request(magic, 1 /* RDC */, rdc_buffer_size, device);
 
@@ -2414,27 +2688,34 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device,
 
        ccw = cqr->cpaddr;
        ccw->cmd_code = CCW_CMD_RDC;
-       ccw->cda = (__u32)(addr_t)rdc_buffer;
-       ccw->count = rdc_buffer_size;
+       if (idal_is_needed(rdc_buffer, rdc_buffer_size)) {
+               idaw = (unsigned long *) (cqr->data);
+               ccw->cda = (__u32)(addr_t) idaw;
+               ccw->flags = CCW_FLAG_IDA;
+               idaw = idal_create_words(idaw, rdc_buffer, rdc_buffer_size);
+       } else {
+               ccw->cda = (__u32)(addr_t) rdc_buffer;
+               ccw->flags = 0;
+       }
 
+       ccw->count = rdc_buffer_size;
        cqr->startdev = device;
        cqr->memdev = device;
        cqr->expires = 10*HZ;
-       clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
-       cqr->retries = 2;
+       cqr->retries = 256;
        cqr->buildclk = get_clock();
        cqr->status = DASD_CQR_FILLED;
        return cqr;
 }
 
 
-int dasd_generic_read_dev_chars(struct dasd_device *device, char *magic,
-                               void **rdc_buffer, int rdc_buffer_size)
+int dasd_generic_read_dev_chars(struct dasd_device *device, int magic,
+                               void *rdc_buffer, int rdc_buffer_size)
 {
        int ret;
        struct dasd_ccw_req *cqr;
 
-       cqr = dasd_generic_build_rdc(device, *rdc_buffer, rdc_buffer_size,
+       cqr = dasd_generic_build_rdc(device, rdc_buffer, rdc_buffer_size,
                                     magic);
        if (IS_ERR(cqr))
                return PTR_ERR(cqr);