Merge branch 'misc' into release
[safe/jmp/linux-2.6] / drivers / s390 / block / dasd_eckd.c
index c4e8181..1c500c4 100644 (file)
@@ -5,13 +5,12 @@
  *                 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,2000
+ * Copyright IBM Corp. 1999, 2009
  * EMC Symmetrix ioctl Copyright EMC Corporation, 2008
  * Author.........: Nigel Hislop <hislop_nigel@emc.com>
- *
  */
 
-#define KMSG_COMPONENT "dasd"
+#define KMSG_COMPONENT "dasd-eckd"
 
 #include <linux/stddef.h>
 #include <linux/kernel.h>
@@ -24,8 +23,8 @@
 #include <asm/debug.h>
 #include <asm/idals.h>
 #include <asm/ebcdic.h>
+#include <asm/compat.h>
 #include <asm/io.h>
-#include <asm/todclk.h>
 #include <asm/uaccess.h>
 #include <asm/cio.h>
 #include <asm/ccwdev.h>
@@ -79,6 +78,11 @@ MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids);
 
 static struct ccw_driver dasd_eckd_driver; /* see below */
 
+#define INIT_CQR_OK 0
+#define INIT_CQR_UNFORMATTED 1
+#define INIT_CQR_ERROR 2
+
+
 /* initial attempt at a probe function. this can be simplified once
  * the other detection code is gone */
 static int
@@ -87,11 +91,12 @@ dasd_eckd_probe (struct ccw_device *cdev)
        int ret;
 
        /* set ECKD specific ccw-device options */
-       ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE);
+       ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE |
+                                    CCWDEV_DO_PATHGROUP | CCWDEV_DO_MULTIPATH);
        if (ret) {
-               DBF_EVENT(DBF_WARNING,
-                      "dasd_eckd_probe: could not set ccw-device options "
-                      "for %s\n", dev_name(&cdev->dev));
+               DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s",
+                               "dasd_eckd_probe: could not set "
+                               "ccw-device options");
                return ret;
        }
        ret = dasd_generic_probe(cdev, &dasd_eckd_discipline);
@@ -104,17 +109,6 @@ dasd_eckd_set_online(struct ccw_device *cdev)
        return dasd_generic_set_online(cdev, &dasd_eckd_discipline);
 }
 
-static struct ccw_driver dasd_eckd_driver = {
-       .name        = "dasd-eckd",
-       .owner       = THIS_MODULE,
-       .ids         = dasd_eckd_ids,
-       .probe       = dasd_eckd_probe,
-       .remove      = dasd_generic_remove,
-       .set_offline = dasd_generic_set_offline,
-       .set_online  = dasd_eckd_set_online,
-       .notify      = dasd_generic_notify,
-};
-
 static const int sizes_trk0[] = { 28, 148, 84 };
 #define LABEL_SIZE 140
 
@@ -718,7 +712,7 @@ static int dasd_eckd_generate_uid(struct dasd_device *device,
               sizeof(uid->serial) - 1);
        EBCASC(uid->serial, sizeof(uid->serial) - 1);
        uid->ssid = private->gneq->subsystemID;
-       uid->real_unit_addr = private->ned->unit_addr;;
+       uid->real_unit_addr = private->ned->unit_addr;
        if (private->sneq) {
                uid->type = private->sneq->sua_flags;
                if (uid->type == UA_BASE_PAV_ALIAS)
@@ -742,7 +736,8 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device,
        struct dasd_ccw_req *cqr;
        struct ccw1 *ccw;
 
-       cqr = dasd_smalloc_request("ECKD", 1 /* RCD */, ciw->count, device);
+       cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */, ciw->count,
+                                  device);
 
        if (IS_ERR(cqr)) {
                DBF_DEV_EVENT(DBF_WARNING, device, "%s",
@@ -760,8 +755,7 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device,
        cqr->block = NULL;
        cqr->expires = 10*HZ;
        cqr->lpm = lpm;
-       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;
@@ -896,16 +890,15 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
                        rc = dasd_eckd_read_conf_lpm(device, &conf_data,
                                                     &conf_len, lpm);
                        if (rc && rc != -EOPNOTSUPP) {  /* -EOPNOTSUPP is ok */
-                               DBF_EVENT(DBF_WARNING,
+                               DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
                                          "Read configuration data returned "
-                                         "error %d for device: %s", rc,
-                                         dev_name(&device->cdev->dev));
+                                         "error %d", rc);
                                return rc;
                        }
                        if (conf_data == NULL) {
-                               DBF_EVENT(DBF_WARNING, "No configuration "
-                                         "data retrieved for device: %s",
-                                         dev_name(&device->cdev->dev));
+                               DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
+                                               "No configuration data "
+                                               "retrieved");
                                continue;       /* no error */
                        }
                        /* save first valid configuration data */
@@ -946,22 +939,20 @@ static int dasd_eckd_read_features(struct dasd_device *device)
        struct dasd_eckd_private *private;
 
        private = (struct dasd_eckd_private *) device->private;
-       cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
-                                  1 /* PSF */  + 1 /* RSSD */ ,
+       memset(&private->features, 0, sizeof(struct dasd_rssd_features));
+       cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */,
                                   (sizeof(struct dasd_psf_prssd_data) +
                                    sizeof(struct dasd_rssd_features)),
                                   device);
        if (IS_ERR(cqr)) {
-               DBF_EVENT(DBF_WARNING, "Could not allocate initialization "
-                         "request for device: %s",
-                         dev_name(&device->cdev->dev));
+               DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "Could not "
+                               "allocate initialization request");
                return PTR_ERR(cqr);
        }
        cqr->startdev = device;
        cqr->memdev = device;
        cqr->block = NULL;
-       clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
-       cqr->retries = 5;
+       cqr->retries = 256;
        cqr->expires = 10 * HZ;
 
        /* Prepare for Read Subsystem Data */
@@ -994,7 +985,9 @@ static int dasd_eckd_read_features(struct dasd_device *device)
                features = (struct dasd_rssd_features *) (prssdp + 1);
                memcpy(&private->features, features,
                       sizeof(struct dasd_rssd_features));
-       }
+       } else
+               dev_warn(&device->cdev->dev, "Reading device feature codes"
+                        " failed with rc=%d\n", rc);
        dasd_sfree_request(cqr, cqr->memdev);
        return rc;
 }
@@ -1010,7 +1003,7 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device,
        struct dasd_psf_ssc_data *psf_ssc_data;
        struct ccw1 *ccw;
 
-       cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ ,
+       cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ ,
                                  sizeof(struct dasd_psf_ssc_data),
                                  device);
 
@@ -1021,9 +1014,9 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device,
        }
        psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data;
        psf_ssc_data->order = PSF_ORDER_SSC;
-       psf_ssc_data->suborder = 0x40;
+       psf_ssc_data->suborder = 0xc0;
        if (enable_pav) {
-               psf_ssc_data->suborder |= 0x88;
+               psf_ssc_data->suborder |= 0x08;
                psf_ssc_data->reserved[0] = 0x88;
        }
        ccw = cqr->cpaddr;
@@ -1034,6 +1027,7 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device,
        cqr->startdev = device;
        cqr->memdev = device;
        cqr->block = NULL;
+       cqr->retries = 256;
        cqr->expires = 10*HZ;
        cqr->buildclk = get_clock();
        cqr->status = DASD_CQR_FILLED;
@@ -1066,7 +1060,7 @@ dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav)
 /*
  * Valide storage server of current device.
  */
-static int dasd_eckd_validate_server(struct dasd_device *device)
+static void dasd_eckd_validate_server(struct dasd_device *device)
 {
        int rc;
        struct dasd_eckd_private *private;
@@ -1077,15 +1071,12 @@ static int dasd_eckd_validate_server(struct dasd_device *device)
        else
                enable_pav = 1;
        rc = dasd_eckd_psf_ssc(device, enable_pav);
+
        /* may be requested feature is not available on server,
         * therefore just report error and go ahead */
        private = (struct dasd_eckd_private *) device->private;
-       DBF_EVENT(DBF_WARNING, "PSF-SSC on storage subsystem %s.%s.%04x "
-                 "returned rc=%d for device: %s",
-                 private->uid.vendor, private->uid.serial,
-                 private->uid.ssid, rc, dev_name(&device->cdev->dev));
-       /* RE-Read Configuration Data */
-       return dasd_eckd_read_conf(device);
+       DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x "
+                       "returned rc=%d", private->uid.ssid, rc);
 }
 
 /*
@@ -1099,6 +1090,15 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
        struct dasd_block *block;
        int is_known, rc;
 
+       if (!ccw_device_is_pathgroup(device->cdev)) {
+               dev_warn(&device->cdev->dev,
+                        "A channel path group could not be established\n");
+               return -EIO;
+       }
+       if (!ccw_device_is_multipath(device->cdev)) {
+               dev_info(&device->cdev->dev,
+                        "The DASD is not operating in multipath mode\n");
+       }
        private = (struct dasd_eckd_private *) device->private;
        if (!private) {
                private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);
@@ -1132,9 +1132,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
        if (private->uid.type == UA_BASE_DEVICE) {
                block = dasd_alloc_block();
                if (IS_ERR(block)) {
-                       DBF_EVENT(DBF_WARNING, "could not allocate dasd "
-                                 "block structure for device: %s",
-                                 dev_name(&device->cdev->dev));
+                       DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
+                                       "could not allocate dasd "
+                                       "block structure");
                        rc = PTR_ERR(block);
                        goto out_err1;
                }
@@ -1148,25 +1148,31 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
                rc = is_known;
                goto out_err2;
        }
+       /*
+        * dasd_eckd_vaildate_server is done on the first device that
+        * is found for an LCU. All later other devices have to wait
+        * for it, so they will read the correct feature codes.
+        */
        if (!is_known) {
-               /* new lcu found */
-               rc = dasd_eckd_validate_server(device); /* will switch pav on */
-               if (rc)
-                       goto out_err3;
-       }
+               dasd_eckd_validate_server(device);
+               dasd_alias_lcu_setup_complete(device);
+       } else
+               dasd_alias_wait_for_lcu_setup(device);
 
-       /* Read Feature Codes */
-       rc = dasd_eckd_read_features(device);
+       /* device may report different configuration data after LCU setup */
+       rc = dasd_eckd_read_conf(device);
        if (rc)
                goto out_err3;
 
+       /* Read Feature Codes */
+       dasd_eckd_read_features(device);
+
        /* Read Device Characteristics */
-       rc = dasd_generic_read_dev_chars(device, "ECKD", &private->rdc_data,
-                                        64);
+       rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC,
+                                        &private->rdc_data, 64);
        if (rc) {
-               DBF_EVENT(DBF_WARNING,
-                         "Read device characteristics failed, rc=%d for "
-                         "device: %s", rc, dev_name(&device->cdev->dev));
+               DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
+                               "Read device characteristic failed, rc=%d", rc);
                goto out_err3;
        }
        /* find the vaild cylinder size */
@@ -1229,8 +1235,7 @@ dasd_eckd_analysis_ccw(struct dasd_device *device)
 
        cplength = 8;
        datasize = sizeof(struct DE_eckd_data) + 2*sizeof(struct LO_eckd_data);
-       cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
-                                  cplength, datasize, device);
+       cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device);
        if (IS_ERR(cqr))
                return cqr;
        ccw = cqr->cpaddr;
@@ -1268,12 +1273,29 @@ dasd_eckd_analysis_ccw(struct dasd_device *device)
        cqr->block = NULL;
        cqr->startdev = device;
        cqr->memdev = device;
-       cqr->retries = 0;
+       cqr->retries = 255;
        cqr->buildclk = get_clock();
        cqr->status = DASD_CQR_FILLED;
        return cqr;
 }
 
+/* differentiate between 'no record found' and any other error */
+static int dasd_eckd_analysis_evaluation(struct dasd_ccw_req *init_cqr)
+{
+       char *sense;
+       if (init_cqr->status == DASD_CQR_DONE)
+               return INIT_CQR_OK;
+       else if (init_cqr->status == DASD_CQR_NEED_ERP ||
+                init_cqr->status == DASD_CQR_FAILED) {
+               sense = dasd_get_sense(&init_cqr->irb);
+               if (sense && (sense[1] & SNS1_NO_REC_FOUND))
+                       return INIT_CQR_UNFORMATTED;
+               else
+                       return INIT_CQR_ERROR;
+       } else
+               return INIT_CQR_ERROR;
+}
+
 /*
  * This is the callback function for the init_analysis cqr. It saves
  * the status of the initial analysis ccw before it frees it and kicks
@@ -1281,21 +1303,20 @@ dasd_eckd_analysis_ccw(struct dasd_device *device)
  * dasd_eckd_do_analysis again (if the devices has not been marked
  * for deletion in the meantime).
  */
-static void
-dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data)
+static void dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr,
+                                       void *data)
 {
        struct dasd_eckd_private *private;
        struct dasd_device *device;
 
        device = init_cqr->startdev;
        private = (struct dasd_eckd_private *) device->private;
-       private->init_cqr_status = init_cqr->status;
+       private->init_cqr_status = dasd_eckd_analysis_evaluation(init_cqr);
        dasd_sfree_request(init_cqr, device);
        dasd_kick_device(device);
 }
 
-static int
-dasd_eckd_start_analysis(struct dasd_block *block)
+static int dasd_eckd_start_analysis(struct dasd_block *block)
 {
        struct dasd_eckd_private *private;
        struct dasd_ccw_req *init_cqr;
@@ -1307,27 +1328,44 @@ dasd_eckd_start_analysis(struct dasd_block *block)
        init_cqr->callback = dasd_eckd_analysis_callback;
        init_cqr->callback_data = NULL;
        init_cqr->expires = 5*HZ;
+       /* first try without ERP, so we can later handle unformatted
+        * devices as special case
+        */
+       clear_bit(DASD_CQR_FLAGS_USE_ERP, &init_cqr->flags);
+       init_cqr->retries = 0;
        dasd_add_request_head(init_cqr);
        return -EAGAIN;
 }
 
-static int
-dasd_eckd_end_analysis(struct dasd_block *block)
+static int dasd_eckd_end_analysis(struct dasd_block *block)
 {
        struct dasd_device *device;
        struct dasd_eckd_private *private;
        struct eckd_count *count_area;
        unsigned int sb, blk_per_trk;
        int status, i;
+       struct dasd_ccw_req *init_cqr;
 
        device = block->base;
        private = (struct dasd_eckd_private *) device->private;
        status = private->init_cqr_status;
        private->init_cqr_status = -1;
-       if (status != DASD_CQR_DONE) {
-               dev_warn(&device->cdev->dev,
-                           "The DASD is not formatted\n");
+       if (status == INIT_CQR_ERROR) {
+               /* try again, this time with full ERP */
+               init_cqr = dasd_eckd_analysis_ccw(device);
+               dasd_sleep_on(init_cqr);
+               status = dasd_eckd_analysis_evaluation(init_cqr);
+               dasd_sfree_request(init_cqr, device);
+       }
+
+       if (status == INIT_CQR_UNFORMATTED) {
+               dev_warn(&device->cdev->dev, "The DASD is not formatted\n");
                return -EMEDIUMTYPE;
+       } else if (status == INIT_CQR_ERROR) {
+               dev_err(&device->cdev->dev,
+                       "Detecting the DASD disk layout failed because "
+                       "of an I/O error\n");
+               return -EIO;
        }
 
        private->uses_cdl = 1;
@@ -1511,8 +1549,7 @@ dasd_eckd_format_device(struct dasd_device * device,
                return ERR_PTR(-EINVAL);
        }
        /* Allocate the format ccw request. */
-       fcp = dasd_smalloc_request(dasd_eckd_discipline.name,
-                                  cplength, datasize, device);
+       fcp = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize, device);
        if (IS_ERR(fcp))
                return fcp;
 
@@ -1620,8 +1657,7 @@ dasd_eckd_format_device(struct dasd_device * device,
        }
        fcp->startdev = device;
        fcp->memdev = device;
-       clear_bit(DASD_CQR_FLAGS_USE_ERP, &fcp->flags);
-       fcp->retries = 5;       /* set retry counter to enable default ERP */
+       fcp->retries = 256;
        fcp->buildclk = get_clock();
        fcp->status = DASD_CQR_FILLED;
        return fcp;
@@ -1708,8 +1744,7 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device,
                DBF_DEV_EVENT(DBF_ERR, device, "%s",
                            "unsolicited interrupt received "
                            "(sense available)");
-               device->discipline->dump_sense_dbf(device, NULL, irb,
-                                                  "unsolicited");
+               device->discipline->dump_sense_dbf(device, irb, "unsolicited");
        }
 
        dasd_schedule_device_bh(device);
@@ -1796,8 +1831,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single(
                datasize += count*sizeof(struct LO_eckd_data);
        }
        /* Allocate the ccw request. */
-       cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
-                                  cplength, datasize, startdev);
+       cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize,
+                                  startdev);
        if (IS_ERR(cqr))
                return cqr;
        ccw = cqr->cpaddr;
@@ -1961,8 +1996,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track(
                cidaw * sizeof(unsigned long long);
 
        /* Allocate the ccw request. */
-       cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
-                                  cplength, datasize, startdev);
+       cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, datasize,
+                                  startdev);
        if (IS_ERR(cqr))
                return cqr;
        ccw = cqr->cpaddr;
@@ -2262,8 +2297,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
 
        /* Allocate the ccw request. */
        itcw_size = itcw_calc_size(0, ctidaw, 0);
-       cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
-                                  0, itcw_size, startdev);
+       cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 0, itcw_size, startdev);
        if (IS_ERR(cqr))
                return cqr;
 
@@ -2353,6 +2387,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
        /* Calculate number of blocks/records per track. */
        blksize = block->bp_block;
        blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
+       if (blk_per_trk == 0)
+               return ERR_PTR(-EINVAL);
        /* Calculate record id of first and last block. */
        first_rec = first_trk = blk_rq_pos(req) >> block->s2b_shift;
        first_offs = sector_div(first_trk, blk_per_trk);
@@ -2570,8 +2606,7 @@ dasd_eckd_release(struct dasd_device *device)
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
 
-       cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
-                                  1, 32, device);
+       cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
        if (IS_ERR(cqr)) {
                DBF_DEV_EVENT(DBF_WARNING, device, "%s",
                            "Could not allocate initialization request");
@@ -2613,8 +2648,7 @@ dasd_eckd_reserve(struct dasd_device *device)
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
 
-       cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
-                                  1, 32, device);
+       cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
        if (IS_ERR(cqr)) {
                DBF_DEV_EVENT(DBF_WARNING, device, "%s",
                            "Could not allocate initialization request");
@@ -2655,8 +2689,7 @@ dasd_eckd_steal_lock(struct dasd_device *device)
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
 
-       cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
-                                  1, 32, device);
+       cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1, 32, device);
        if (IS_ERR(cqr)) {
                DBF_DEV_EVENT(DBF_WARNING, device, "%s",
                            "Could not allocate initialization request");
@@ -2694,8 +2727,7 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp)
        struct ccw1 *ccw;
        int rc;
 
-       cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
-                                  1 /* PSF */  + 1 /* RSSD */ ,
+       cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */  + 1 /* RSSD */,
                                   (sizeof(struct dasd_psf_prssd_data) +
                                    sizeof(struct dasd_rssd_perf_stats_t)),
                                   device);
@@ -2707,6 +2739,7 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp)
        cqr->startdev = device;
        cqr->memdev = device;
        cqr->retries = 0;
+       clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
        cqr->expires = 10 * HZ;
 
        /* Prepare for Read Subsystem Data */
@@ -2812,13 +2845,16 @@ static int dasd_symm_io(struct dasd_device *device, void __user *argp)
        rc = -EFAULT;
        if (copy_from_user(&usrparm, argp, sizeof(usrparm)))
                goto out;
-#ifndef CONFIG_64BIT
-       /* Make sure pointers are sane even on 31 bit. */
-       if ((usrparm.psf_data >> 32) != 0 || (usrparm.rssd_result >> 32) != 0) {
+       if (is_compat_task() || sizeof(long) == 4) {
+               /* Make sure pointers are sane even on 31 bit. */
                rc = -EINVAL;
-               goto out;
+               if ((usrparm.psf_data >> 32) != 0)
+                       goto out;
+               if ((usrparm.rssd_result >> 32) != 0)
+                       goto out;
+               usrparm.psf_data &= 0x7fffffffULL;
+               usrparm.rssd_result &= 0x7fffffffULL;
        }
-#endif
        /* alloc I/O data area */
        psf_data = kzalloc(usrparm.psf_data_len, GFP_KERNEL | GFP_DMA);
        rssd_result = kzalloc(usrparm.rssd_result_len, GFP_KERNEL | GFP_DMA);
@@ -2841,7 +2877,7 @@ static int dasd_symm_io(struct dasd_device *device, void __user *argp)
        }
 
        /* setup CCWs for PSF + RSSD */
-       cqr = dasd_smalloc_request("ECKD", 2 , 0, device);
+       cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2 , 0, device);
        if (IS_ERR(cqr)) {
                DBF_DEV_EVENT(DBF_WARNING, device, "%s",
                        "Could not allocate initialization request");
@@ -2953,42 +2989,20 @@ dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page)
 }
 
 static void
-dasd_eckd_dump_sense_dbf(struct dasd_device *device, struct dasd_ccw_req *req,
-                        struct irb *irb, char *reason)
+dasd_eckd_dump_sense_dbf(struct dasd_device *device, struct irb *irb,
+                        char *reason)
 {
        u64 *sense;
-       int sl;
-       struct tsb *tsb;
 
-       sense = NULL;
-       tsb = NULL;
-       if (req && scsw_is_tm(&req->irb.scsw)) {
-               if (irb->scsw.tm.tcw)
-                       tsb = tcw_get_tsb(
-                               (struct tcw *)(unsigned long)irb->scsw.tm.tcw);
-               if (tsb && (irb->scsw.tm.fcxs == 0x01)) {
-                       switch (tsb->flags & 0x07) {
-                       case 1: /* tsa_iostat */
-                               sense = (u64 *)tsb->tsa.iostat.sense;
-                       break;
-                       case 2: /* ts_ddpc */
-                               sense = (u64 *)tsb->tsa.ddpc.sense;
-                       break;
-                       case 3: /* tsa_intrg */
-                       break;
-                       }
-               }
-       } else {
-               if (irb->esw.esw0.erw.cons)
-                       sense = (u64 *)irb->ecw;
-       }
+       sense = (u64 *) dasd_get_sense(irb);
        if (sense) {
-               for (sl = 0; sl < 4; sl++) {
-                       DBF_DEV_EVENT(DBF_EMERG, device,
-                                     "%s: %016llx %016llx %016llx %016llx",
-                                     reason, sense[0], sense[1], sense[2],
-                                     sense[3]);
-               }
+               DBF_DEV_EVENT(DBF_EMERG, device,
+                             "%s: %s %02x%02x%02x %016llx %016llx %016llx "
+                             "%016llx", reason,
+                             scsw_is_tm(&irb->scsw) ? "t" : "c",
+                             scsw_cc(&irb->scsw), scsw_cstat(&irb->scsw),
+                             scsw_dstat(&irb->scsw), sense[0], sense[1],
+                             sense[2], sense[3]);
        } else {
                DBF_DEV_EVENT(DBF_EMERG, device, "%s",
                              "SORRY - NO VALID SENSE AVAILABLE\n");
@@ -3017,8 +3031,9 @@ static void dasd_eckd_dump_sense_ccw(struct dasd_device *device,
                      " I/O status report for device %s:\n",
                      dev_name(&device->cdev->dev));
        len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-                      " in req: %p CS: 0x%02X DS: 0x%02X\n", req,
-                      scsw_cstat(&irb->scsw), scsw_dstat(&irb->scsw));
+                      " in req: %p CS: 0x%02X DS: 0x%02X CC: 0x%02X RC: %d\n",
+                      req, scsw_cstat(&irb->scsw), scsw_dstat(&irb->scsw),
+                      scsw_cc(&irb->scsw), req->intrc);
        len += sprintf(page + len, KERN_ERR PRINTK_HEADER
                       " device %s: Failing CCW: %p\n",
                       dev_name(&device->cdev->dev),
@@ -3119,9 +3134,10 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device,
                      " I/O status report for device %s:\n",
                      dev_name(&device->cdev->dev));
        len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-                      " in req: %p CS: 0x%02X DS: 0x%02X "
+                      " in req: %p CS: 0x%02X DS: 0x%02X CC: 0x%02X RC: %d "
                       "fcxs: 0x%02X schxs: 0x%02X\n", req,
                       scsw_cstat(&irb->scsw), scsw_dstat(&irb->scsw),
+                      scsw_cc(&irb->scsw), req->intrc,
                       irb->scsw.tm.fcxs, irb->scsw.tm.schxs);
        len += sprintf(page + len, KERN_ERR PRINTK_HEADER
                       " device %s: Failing TCW: %p\n",
@@ -3234,6 +3250,96 @@ static void dasd_eckd_dump_sense(struct dasd_device *device,
                dasd_eckd_dump_sense_ccw(device, req, irb);
 }
 
+int dasd_eckd_pm_freeze(struct dasd_device *device)
+{
+       /*
+        * the device should be disconnected from our LCU structure
+        * on restore we will reconnect it and reread LCU specific
+        * information like PAV support that might have changed
+        */
+       dasd_alias_remove_device(device);
+       dasd_alias_disconnect_device_from_lcu(device);
+
+       return 0;
+}
+
+int dasd_eckd_restore_device(struct dasd_device *device)
+{
+       struct dasd_eckd_private *private;
+       struct dasd_eckd_characteristics temp_rdc_data;
+       int is_known, rc;
+       struct dasd_uid temp_uid;
+       unsigned long flags;
+
+       private = (struct dasd_eckd_private *) device->private;
+
+       /* Read Configuration Data */
+       rc = dasd_eckd_read_conf(device);
+       if (rc)
+               goto out_err;
+
+       /* Generate device unique id and register in devmap */
+       rc = dasd_eckd_generate_uid(device, &private->uid);
+       dasd_get_uid(device->cdev, &temp_uid);
+       if (memcmp(&private->uid, &temp_uid, sizeof(struct dasd_uid)) != 0)
+               dev_err(&device->cdev->dev, "The UID of the DASD has "
+                       "changed\n");
+       if (rc)
+               goto out_err;
+       dasd_set_uid(device->cdev, &private->uid);
+
+       /* register lcu with alias handling, enable PAV if this is a new lcu */
+       is_known = dasd_alias_make_device_known_to_lcu(device);
+       if (is_known < 0)
+               return is_known;
+       if (!is_known) {
+               dasd_eckd_validate_server(device);
+               dasd_alias_lcu_setup_complete(device);
+       } else
+               dasd_alias_wait_for_lcu_setup(device);
+
+       /* RE-Read Configuration Data */
+       rc = dasd_eckd_read_conf(device);
+       if (rc)
+               goto out_err;
+
+       /* Read Feature Codes */
+       dasd_eckd_read_features(device);
+
+       /* Read Device Characteristics */
+       rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC,
+                                        &temp_rdc_data, 64);
+       if (rc) {
+               DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
+                               "Read device characteristic failed, rc=%d", rc);
+               goto out_err;
+       }
+       spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+       memcpy(&private->rdc_data, &temp_rdc_data, sizeof(temp_rdc_data));
+       spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+
+       /* add device to alias management */
+       dasd_alias_add_device(device);
+
+       return 0;
+
+out_err:
+       return -1;
+}
+
+static struct ccw_driver dasd_eckd_driver = {
+       .name        = "dasd-eckd",
+       .owner       = THIS_MODULE,
+       .ids         = dasd_eckd_ids,
+       .probe       = dasd_eckd_probe,
+       .remove      = dasd_generic_remove,
+       .set_offline = dasd_generic_set_offline,
+       .set_online  = dasd_eckd_set_online,
+       .notify      = dasd_generic_notify,
+       .freeze      = dasd_generic_pm_freeze,
+       .thaw        = dasd_generic_restore_device,
+       .restore     = dasd_generic_restore_device,
+};
 
 /*
  * max_blocks is dependent on the amount of storage that is available
@@ -3272,13 +3378,21 @@ static struct dasd_discipline dasd_eckd_discipline = {
        .dump_sense_dbf = dasd_eckd_dump_sense_dbf,
        .fill_info = dasd_eckd_fill_info,
        .ioctl = dasd_eckd_ioctl,
+       .freeze = dasd_eckd_pm_freeze,
+       .restore = dasd_eckd_restore_device,
 };
 
 static int __init
 dasd_eckd_init(void)
 {
+       int ret;
+
        ASCEBC(dasd_eckd_discipline.ebcname, 4);
-       return ccw_driver_register(&dasd_eckd_driver);
+       ret = ccw_driver_register(&dasd_eckd_driver);
+       if (!ret)
+               wait_for_device_probe();
+
+       return ret;
 }
 
 static void __exit