Merge branch 'misc' into release
[safe/jmp/linux-2.6] / drivers / s390 / block / dasd_eckd.c
index 614813f..1c500c4 100644 (file)
@@ -23,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>
@@ -78,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
@@ -86,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);
@@ -706,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)
@@ -749,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;
@@ -885,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 */
@@ -941,16 +945,14 @@ static int dasd_eckd_read_features(struct dasd_device *device)
                                    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 */
@@ -1012,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;
@@ -1025,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;
@@ -1057,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;
@@ -1068,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);
 }
 
 /*
@@ -1090,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);
@@ -1123,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;
                }
@@ -1139,12 +1148,21 @@ 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);
+
+       /* 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);
@@ -1153,9 +1171,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
        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 */
@@ -1256,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
@@ -1269,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;
@@ -1295,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;
@@ -1607,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;
@@ -2338,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);
@@ -2688,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 */
@@ -2793,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);
@@ -3211,8 +3266,10 @@ int dasd_eckd_pm_freeze(struct dasd_device *device)
 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;
 
@@ -3225,7 +3282,8 @@ int dasd_eckd_restore_device(struct dasd_device *device)
        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");
+               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);
@@ -3235,25 +3293,30 @@ int dasd_eckd_restore_device(struct dasd_device *device)
        if (is_known < 0)
                return is_known;
        if (!is_known) {
-               /* new lcu found */
-               rc = dasd_eckd_validate_server(device); /* will switch pav on */
-               if (rc)
-                       goto out_err;
-       }
+               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 */
-       memset(&private->rdc_data, 0, sizeof(private->rdc_data));
        rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC,
-                                        &private->rdc_data, 64);
+                                        &temp_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_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);