fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / s390 / block / dasd_eckd.c
index 822e2a2..fdaa471 100644 (file)
@@ -1,7 +1,7 @@
-/* 
+/*
  * File...........: linux/drivers/s390/block/dasd_eckd.c
  * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- *                 Horst Hummel <Horst.Hummel@de.ibm.com> 
+ *                 Horst Hummel <Horst.Hummel@de.ibm.com>
  *                 Carsten Otte <Cotte@de.ibm.com>
  *                 Martin Schwidefsky <schwidefsky@de.ibm.com>
  * Bugreports.to..: <Linux390@de.ibm.com>
@@ -9,7 +9,6 @@
  *
  */
 
-#include <linux/config.h>
 #include <linux/stddef.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
@@ -24,6 +23,7 @@
 #include <asm/io.h>
 #include <asm/todclk.h>
 #include <asm/uaccess.h>
+#include <asm/cio.h>
 #include <asm/ccwdev.h>
 
 #include "dasd_int.h"
@@ -65,16 +65,16 @@ struct dasd_eckd_private {
 /* The ccw bus type uses this table to find devices that it sends to
  * dasd_eckd_probe */
 static struct ccw_device_id dasd_eckd_ids[] = {
-       { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3390, 0), driver_info: 0x1},
-       { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3390, 0), driver_info: 0x2},
-       { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3390, 0), driver_info: 0x3},
-       { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3380, 0), driver_info: 0x4},
-       { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3380, 0), driver_info: 0x5},
-       { CCW_DEVICE_DEVTYPE (0x9343, 0, 0x9345, 0), driver_info: 0x6},
-       { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3390, 0), driver_info: 0x7},
-       { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3380, 0), driver_info: 0x8},
-       { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3390, 0), driver_info: 0x9},
-       { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3380, 0), driver_info: 0xa},
+       { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3390, 0), .driver_info = 0x1},
+       { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3390, 0), .driver_info = 0x2},
+       { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3390, 0), .driver_info = 0x3},
+       { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3380, 0), .driver_info = 0x4},
+       { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3380, 0), .driver_info = 0x5},
+       { CCW_DEVICE_DEVTYPE (0x9343, 0, 0x9345, 0), .driver_info = 0x6},
+       { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3390, 0), .driver_info = 0x7},
+       { CCW_DEVICE_DEVTYPE (0x2107, 0, 0x3380, 0), .driver_info = 0x8},
+       { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3390, 0), .driver_info = 0x9},
+       { CCW_DEVICE_DEVTYPE (0x1750, 0, 0x3380, 0), .driver_info = 0xa},
        { /* end of list */ },
 };
 
@@ -89,17 +89,22 @@ dasd_eckd_probe (struct ccw_device *cdev)
 {
        int ret;
 
-       ret = dasd_generic_probe (cdev, &dasd_eckd_discipline);
-       if (ret)
+       /* set ECKD specific ccw-device options */
+       ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE);
+       if (ret) {
+               printk(KERN_WARNING
+                      "dasd_eckd_probe: could not set ccw-device options "
+                      "for %s\n", cdev->dev.bus_id);
                return ret;
-       ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP | CCWDEV_ALLOW_FORCE);
-       return 0;
+       }
+       ret = dasd_generic_probe(cdev, &dasd_eckd_discipline);
+       return ret;
 }
 
 static int
 dasd_eckd_set_online(struct ccw_device *cdev)
 {
-       return dasd_generic_set_online (cdev, &dasd_eckd_discipline);
+       return dasd_generic_set_online(cdev, &dasd_eckd_discipline);
 }
 
 static struct ccw_driver dasd_eckd_driver = {
@@ -210,14 +215,14 @@ check_XRC (struct ccw1         *de_ccw,
 
         /* switch on System Time Stamp - needed for XRC Support */
         if (private->rdc_data.facilities.XRC_supported) {
-                
+
                 data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid'   */
                 data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
-                
+
                 data->ep_sys_time = get_clock ();
-                
+
                 de_ccw->count = sizeof (struct DE_eckd_data);
-                de_ccw->flags |= CCW_FLAG_SLI;  
+               de_ccw->flags |= CCW_FLAG_SLI;
         }
 
         return;
@@ -296,8 +301,8 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
        /* check for sequential prestage - enhance cylinder range */
        if (data->attributes.operation == DASD_SEQ_PRESTAGE ||
            data->attributes.operation == DASD_SEQ_ACCESS) {
-               
-               if (end.cyl + private->attrib.nr_cyl < geo.cyl) 
+
+               if (end.cyl + private->attrib.nr_cyl < geo.cyl)
                        end.cyl += private->attrib.nr_cyl;
                else
                        end.cyl = (geo.cyl - 1);
@@ -317,7 +322,7 @@ locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk,
        struct dasd_eckd_private *private;
        int sector;
        int dn, d;
-                               
+
        private = (struct dasd_eckd_private *) device->private;
 
        DBF_DEV_EVENT(DBF_INFO, device,
@@ -446,6 +451,39 @@ dasd_eckd_cdl_reclen(int recid)
        return LABEL_SIZE;
 }
 
+/*
+ * Generate device unique id that specifies the physical device.
+ */
+static int
+dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid)
+{
+       struct dasd_eckd_private *private;
+       struct dasd_eckd_confdata *confdata;
+
+       private = (struct dasd_eckd_private *) device->private;
+       if (!private)
+               return -ENODEV;
+       confdata = &private->conf_data;
+       if (!confdata)
+               return -ENODEV;
+
+       memset(uid, 0, sizeof(struct dasd_uid));
+       memcpy(uid->vendor, confdata->ned1.HDA_manufacturer,
+              sizeof(uid->vendor) - 1);
+       EBCASC(uid->vendor, sizeof(uid->vendor) - 1);
+       memcpy(uid->serial, confdata->ned1.HDA_location,
+              sizeof(uid->serial) - 1);
+       EBCASC(uid->serial, sizeof(uid->serial) - 1);
+       uid->ssid = confdata->neq.subsystemID;
+       if (confdata->ned2.sneq.flags == 0x40) {
+               uid->alias = 1;
+               uid->unit_addr = confdata->ned2.sneq.base_unit_addr;
+       } else
+               uid->unit_addr = confdata->ned1.unit_addr;
+
+       return 0;
+}
+
 static int
 dasd_eckd_read_conf(struct dasd_device *device)
 {
@@ -507,17 +545,101 @@ dasd_eckd_read_conf(struct dasd_device *device)
        return 0;
 }
 
+/*
+ * Build CP for Perform Subsystem Function - SSC.
+ */
+struct dasd_ccw_req *
+dasd_eckd_build_psf_ssc(struct dasd_device *device)
+{
+       struct dasd_ccw_req *cqr;
+       struct dasd_psf_ssc_data *psf_ssc_data;
+       struct ccw1 *ccw;
+
+       cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ ,
+                                 sizeof(struct dasd_psf_ssc_data),
+                                 device);
+
+       if (IS_ERR(cqr)) {
+              DEV_MESSAGE(KERN_WARNING, device, "%s",
+                          "Could not allocate PSF-SSC request");
+              return cqr;
+       }
+       psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data;
+       psf_ssc_data->order = PSF_ORDER_SSC;
+       psf_ssc_data->suborder = 0x08;
+
+       ccw = cqr->cpaddr;
+       ccw->cmd_code = DASD_ECKD_CCW_PSF;
+       ccw->cda = (__u32)(addr_t)psf_ssc_data;
+       ccw->count = 66;
+
+       cqr->device = device;
+       cqr->expires = 10*HZ;
+       cqr->buildclk = get_clock();
+       cqr->status = DASD_CQR_FILLED;
+       return cqr;
+}
+
+/*
+ * Perform Subsystem Function.
+ * It is necessary to trigger CIO for channel revalidation since this
+ * call might change behaviour of DASD devices.
+ */
+static int
+dasd_eckd_psf_ssc(struct dasd_device *device)
+{
+       struct dasd_ccw_req *cqr;
+       int rc;
+
+       cqr = dasd_eckd_build_psf_ssc(device);
+       if (IS_ERR(cqr))
+              return PTR_ERR(cqr);
+
+       rc = dasd_sleep_on(cqr);
+       if (!rc)
+              /* trigger CIO to reprobe devices */
+              css_schedule_reprobe();
+       dasd_sfree_request(cqr, cqr->device);
+       return rc;
+}
+
+/*
+ * Valide storage server of current device.
+ */
+static int
+dasd_eckd_validate_server(struct dasd_device *device, struct dasd_uid *uid)
+{
+       int rc;
+
+       /* Currently PAV is the only reason to 'validate' server on LPAR */
+       if (dasd_nopav || MACHINE_IS_VM)
+               return 0;
 
+       rc = dasd_eckd_psf_ssc(device);
+       /* may be requested feature is not available on server,
+        * therefore just report error and go ahead */
+       DEV_MESSAGE(KERN_INFO, device,
+                   "PSF-SSC on storage subsystem %s.%s.%04x returned rc=%d",
+                   uid->vendor, uid->serial, uid->ssid, rc);
+       /* RE-Read Configuration Data */
+       return dasd_eckd_read_conf(device);
+}
+
+/*
+ * Check device characteristics.
+ * If the device is accessible using ECKD discipline, the device is enabled.
+ */
 static int
 dasd_eckd_check_characteristics(struct dasd_device *device)
 {
        struct dasd_eckd_private *private;
+       struct dasd_uid uid;
        void *rdc_data;
        int rc;
 
        private = (struct dasd_eckd_private *) device->private;
        if (private == NULL) {
-               private = kmalloc(sizeof(struct dasd_eckd_private),
+               private = kzalloc(sizeof(struct dasd_eckd_private),
                                  GFP_KERNEL | GFP_DMA);
                if (private == NULL) {
                        DEV_MESSAGE(KERN_WARNING, device, "%s",
@@ -525,7 +647,6 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
                                    "data");
                        return -ENOMEM;
                }
-               memset(private, 0, sizeof(struct dasd_eckd_private));
                device->private = (void *) private;
        }
        /* Invalidate status of initial analysis. */
@@ -534,15 +655,29 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
        private->attrib.operation = DASD_NORMAL_CACHE;
        private->attrib.nr_cyl = 0;
 
+       /* Read Configuration Data */
+       rc = dasd_eckd_read_conf(device);
+       if (rc)
+               return rc;
+
+       /* Generate device unique id and register in devmap */
+       rc = dasd_eckd_generate_uid(device, &uid);
+       if (rc)
+               return rc;
+       rc = dasd_set_uid(device->cdev, &uid);
+       if (rc == 1)    /* new server found */
+               rc = dasd_eckd_validate_server(device, &uid);
+       if (rc)
+               return rc;
+
        /* Read Device Characteristics */
        rdc_data = (void *) &(private->rdc_data);
+       memset(rdc_data, 0, sizeof(rdc_data));
        rc = read_dev_chars(device->cdev, &rdc_data, 64);
-       if (rc) {
+       if (rc)
                DEV_MESSAGE(KERN_WARNING, device,
-                           "Read device characteristics returned error %d",
-                           rc);
-               return rc;
-       }
+                           "Read device characteristics returned "
+                           "rc=%d", rc);
 
        DEV_MESSAGE(KERN_INFO, device,
                    "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d",
@@ -553,11 +688,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
                    private->rdc_data.no_cyl,
                    private->rdc_data.trk_per_cyl,
                    private->rdc_data.sec_per_trk);
-
-       /* Read Configuration Data */
-       rc = dasd_eckd_read_conf (device);
        return rc;
-
 }
 
 static struct dasd_ccw_req *
@@ -726,7 +857,7 @@ dasd_eckd_end_analysis(struct dasd_device *device)
                    ((private->rdc_data.no_cyl *
                      private->rdc_data.trk_per_cyl *
                      blk_per_trk * (device->bp_block >> 9)) >> 1),
-                   ((blk_per_trk * device->bp_block) >> 10), 
+                   ((blk_per_trk * device->bp_block) >> 10),
                    private->uses_cdl ?
                    "compatible disk layout" : "linux disk layout");
 
@@ -923,7 +1054,7 @@ dasd_eckd_format_device(struct dasd_device * device,
                                if (i < 3) {
                                        ect->kl = 4;
                                        ect->dl = sizes_trk0[i] - 4;
-                               } 
+                               }
                        }
                        if ((fdata->intensity & 0x08) &&
                            fdata->start_unit == 1) {
@@ -1084,7 +1215,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
                dst = page_address(bv->bv_page) + bv->bv_offset;
                if (dasd_page_cache) {
                        char *copy = kmem_cache_alloc(dasd_page_cache,
-                                                     SLAB_DMA | __GFP_NOWARN);
+                                                     GFP_DMA | __GFP_NOWARN);
                        if (copy && rq_data_dir(req) == WRITE)
                                memcpy(copy + bv->bv_offset, dst, bv->bv_len);
                        if (copy)
@@ -1135,7 +1266,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
                        recid++;
                }
        }
-       if (req->flags & REQ_FAILFAST)
+       if (req->cmd_flags & REQ_FAILFAST)
                set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
        cqr->device = device;
        cqr->expires = 5 * 60 * HZ;     /* 5 minutes */
@@ -1223,23 +1354,18 @@ dasd_eckd_fill_info(struct dasd_device * device,
 
 /*
  * Release device ioctl.
- * Buils a channel programm to releases a prior reserved 
+ * Buils a channel programm to releases a prior reserved
  * (see dasd_eckd_reserve) device.
  */
 static int
-dasd_eckd_release(struct block_device *bdev, int no, long args)
+dasd_eckd_release(struct dasd_device *device)
 {
-       struct dasd_device *device;
        struct dasd_ccw_req *cqr;
        int rc;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
 
-       device = bdev->bd_disk->private_data;
-       if (device == NULL)
-               return -ENODEV;
-
        cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
                                   1, 32, device);
        if (IS_ERR(cqr)) {
@@ -1268,23 +1394,18 @@ dasd_eckd_release(struct block_device *bdev, int no, long args)
 /*
  * Reserve device ioctl.
  * Options are set to 'synchronous wait for interrupt' and
- * 'timeout the request'. This leads to a terminate IO if 
- * the interrupt is outstanding for a certain time. 
+ * 'timeout the request'. This leads to a terminate IO if
+ * the interrupt is outstanding for a certain time.
  */
 static int
-dasd_eckd_reserve(struct block_device *bdev, int no, long args)
+dasd_eckd_reserve(struct dasd_device *device)
 {
-       struct dasd_device *device;
        struct dasd_ccw_req *cqr;
        int rc;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
 
-       device = bdev->bd_disk->private_data;
-       if (device == NULL)
-               return -ENODEV;
-
        cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
                                   1, 32, device);
        if (IS_ERR(cqr)) {
@@ -1312,23 +1433,18 @@ dasd_eckd_reserve(struct block_device *bdev, int no, long args)
 
 /*
  * Steal lock ioctl - unconditional reserve device.
- * Buils a channel programm to break a device's reservation. 
+ * Buils a channel programm to break a device's reservation.
  * (unconditional reserve)
  */
 static int
-dasd_eckd_steal_lock(struct block_device *bdev, int no, long args)
+dasd_eckd_steal_lock(struct dasd_device *device)
 {
-       struct dasd_device *device;
        struct dasd_ccw_req *cqr;
        int rc;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
 
-       device = bdev->bd_disk->private_data;
-       if (device == NULL)
-               return -ENODEV;
-
        cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
                                   1, 32, device);
        if (IS_ERR(cqr)) {
@@ -1358,19 +1474,14 @@ dasd_eckd_steal_lock(struct block_device *bdev, int no, long args)
  * Read performance statistics
  */
 static int
-dasd_eckd_performance(struct block_device *bdev, int no, long args)
+dasd_eckd_performance(struct dasd_device *device, void __user *argp)
 {
-       struct dasd_device *device;
        struct dasd_psf_prssd_data *prssdp;
        struct dasd_rssd_perf_stats_t *stats;
        struct dasd_ccw_req *cqr;
        struct ccw1 *ccw;
        int rc;
 
-       device = bdev->bd_disk->private_data;
-       if (device == NULL)
-               return -ENODEV;
-
        cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
                                   1 /* PSF */  + 1 /* RSSD */ ,
                                   (sizeof (struct dasd_psf_prssd_data) +
@@ -1414,8 +1525,9 @@ dasd_eckd_performance(struct block_device *bdev, int no, long args)
                /* Prepare for Read Subsystem Data */
                prssdp = (struct dasd_psf_prssd_data *) cqr->data;
                stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1);
-               rc = copy_to_user((long __user *) args, (long *) stats,
-                                 sizeof(struct dasd_rssd_perf_stats_t));
+               if (copy_to_user(argp, stats,
+                                sizeof(struct dasd_rssd_perf_stats_t)))
+                       rc = -EFAULT;
        }
        dasd_sfree_request(cqr, cqr->device);
        return rc;
@@ -1426,27 +1538,22 @@ dasd_eckd_performance(struct block_device *bdev, int no, long args)
  * Returnes the cache attributes used in Define Extend (DE).
  */
 static int
-dasd_eckd_get_attrib (struct block_device *bdev, int no, long args)
+dasd_eckd_get_attrib(struct dasd_device *device, void __user *argp)
 {
-       struct dasd_device *device;
-        struct dasd_eckd_private *private;
-        struct attrib_data_t attrib;
+       struct dasd_eckd_private *private =
+               (struct dasd_eckd_private *)device->private;
+       struct attrib_data_t attrib = private->attrib;
        int rc;
 
         if (!capable(CAP_SYS_ADMIN))
                 return -EACCES;
-        if (!args)
+       if (!argp)
                 return -EINVAL;
 
-        device = bdev->bd_disk->private_data;
-        if (device == NULL)
-                return -ENODEV;
-
-        private = (struct dasd_eckd_private *) device->private;
-        attrib = private->attrib;
-
-        rc = copy_to_user((long __user *) args, (long *) &attrib,
-                         sizeof (struct attrib_data_t));
+       rc = 0;
+       if (copy_to_user(argp, (long *) &attrib,
+                        sizeof (struct attrib_data_t)))
+               rc = -EFAULT;
 
        return rc;
 }
@@ -1456,26 +1563,19 @@ dasd_eckd_get_attrib (struct block_device *bdev, int no, long args)
  * Stores the attributes for cache operation to be used in Define Extend (DE).
  */
 static int
-dasd_eckd_set_attrib(struct block_device *bdev, int no, long args)
+dasd_eckd_set_attrib(struct dasd_device *device, void __user *argp)
 {
-       struct dasd_device *device;
-       struct dasd_eckd_private *private;
+       struct dasd_eckd_private *private =
+               (struct dasd_eckd_private *)device->private;
        struct attrib_data_t attrib;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
-       if (!args)
+       if (!argp)
                return -EINVAL;
 
-       device = bdev->bd_disk->private_data;
-       if (device == NULL)
-               return -ENODEV;
-
-       if (copy_from_user(&attrib, (void __user *) args,
-                          sizeof (struct attrib_data_t))) {
+       if (copy_from_user(&attrib, argp, sizeof(struct attrib_data_t)))
                return -EFAULT;
-       }
-       private = (struct dasd_eckd_private *) device->private;
        private->attrib = attrib;
 
        DEV_MESSAGE(KERN_INFO, device,
@@ -1484,6 +1584,61 @@ dasd_eckd_set_attrib(struct block_device *bdev, int no, long args)
        return 0;
 }
 
+static int
+dasd_eckd_ioctl(struct dasd_device *device, unsigned int cmd, void __user *argp)
+{
+       switch (cmd) {
+       case BIODASDGATTR:
+               return dasd_eckd_get_attrib(device, argp);
+       case BIODASDSATTR:
+               return dasd_eckd_set_attrib(device, argp);
+       case BIODASDPSRD:
+               return dasd_eckd_performance(device, argp);
+       case BIODASDRLSE:
+               return dasd_eckd_release(device);
+       case BIODASDRSRV:
+               return dasd_eckd_reserve(device);
+       case BIODASDSLCK:
+               return dasd_eckd_steal_lock(device);
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+/*
+ * Dump the range of CCWs into 'page' buffer
+ * and return number of printed chars.
+ */
+static inline int
+dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page)
+{
+       int len, count;
+       char *datap;
+
+       len = 0;
+       while (from <= to) {
+               len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+                              " CCW %p: %08X %08X DAT:",
+                              from, ((int *) from)[0], ((int *) from)[1]);
+
+               /* get pointer to data (consider IDALs) */
+               if (from->flags & CCW_FLAG_IDA)
+                       datap = (char *) *((addr_t *) (addr_t) from->cda);
+               else
+                       datap = (char *) ((addr_t) from->cda);
+
+               /* dump data (max 32 bytes) */
+               for (count = 0; count < from->count && count < 32; count++) {
+                       if (count % 8 == 0) len += sprintf(page + len, " ");
+                       if (count % 4 == 0) len += sprintf(page + len, " ");
+                       len += sprintf(page + len, "%02x", datap[count]);
+               }
+               len += sprintf(page + len, "\n");
+               from++;
+       }
+       return len;
+}
+
 /*
  * Print sense data and related channel program.
  * Parts are printed because printk buffer is only 1024 bytes.
@@ -1493,8 +1648,8 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
                     struct irb *irb)
 {
        char *page;
-       struct ccw1 *act, *end, *last;
-       int len, sl, sct, count;
+       struct ccw1 *first, *last, *fail, *from, *to;
+       int len, sl, sct;
 
        page = (char *) get_zeroed_page(GFP_ATOMIC);
        if (page == NULL) {
@@ -1502,7 +1657,8 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
                            "No memory to dump sense data");
                return;
        }
-       len = sprintf(page, KERN_ERR PRINTK_HEADER
+       /* dump the sense data */
+       len = sprintf(page,  KERN_ERR PRINTK_HEADER
                      " I/O status report for device %s:\n",
                      device->cdev->dev.bus_id);
        len += sprintf(page + len, KERN_ERR PRINTK_HEADER
@@ -1527,87 +1683,55 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
 
                if (irb->ecw[27] & DASD_SENSE_BIT_0) {
                        /* 24 Byte Sense Data */
-                       len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-                                      " 24 Byte: %x MSG %x, "
-                                      "%s MSGb to SYSOP\n",
-                                      irb->ecw[7] >> 4, irb->ecw[7] & 0x0f,
-                                      irb->ecw[1] & 0x10 ? "" : "no");
+                       sprintf(page + len, KERN_ERR PRINTK_HEADER
+                               " 24 Byte: %x MSG %x, "
+                               "%s MSGb to SYSOP\n",
+                               irb->ecw[7] >> 4, irb->ecw[7] & 0x0f,
+                               irb->ecw[1] & 0x10 ? "" : "no");
                } else {
                        /* 32 Byte Sense Data */
-                       len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-                                      " 32 Byte: Format: %x "
-                                      "Exception class %x\n",
-                                      irb->ecw[6] & 0x0f, irb->ecw[22] >> 4);
+                       sprintf(page + len, KERN_ERR PRINTK_HEADER
+                               " 32 Byte: Format: %x "
+                               "Exception class %x\n",
+                               irb->ecw[6] & 0x0f, irb->ecw[22] >> 4);
                }
        } else {
-               len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-                              " SORRY - NO VALID SENSE AVAILABLE\n");
+               sprintf(page + len, KERN_ERR PRINTK_HEADER
+                       " SORRY - NO VALID SENSE AVAILABLE\n");
        }
-       MESSAGE_LOG(KERN_ERR, "%s",
-                   page + sizeof(KERN_ERR PRINTK_HEADER));
-
-       /* dump the Channel Program */
-       /* print first CCWs (maximum 8) */
-       act = req->cpaddr;
-        for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
-       end = min(act + 8, last);
-       len = sprintf(page, KERN_ERR PRINTK_HEADER
+       printk("%s", page);
+
+       /* dump the Channel Program (max 140 Bytes per line) */
+       /* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */
+       first = req->cpaddr;
+       for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
+       to = min(first + 6, last);
+       len = sprintf(page,  KERN_ERR PRINTK_HEADER
                      " Related CP in req: %p\n", req);
-       while (act <= end) {
-               len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-                              " CCW %p: %08X %08X DAT:",
-                              act, ((int *) act)[0], ((int *) act)[1]);
-               for (count = 0; count < 32 && count < act->count;
-                    count += sizeof(int))
-                       len += sprintf(page + len, " %08X",
-                                      ((int *) (addr_t) act->cda)
-                                      [(count>>2)]);
-               len += sprintf(page + len, "\n");
-               act++;
-       }
-       MESSAGE_LOG(KERN_ERR, "%s",
-                   page + sizeof(KERN_ERR PRINTK_HEADER));
+       dasd_eckd_dump_ccw_range(first, to, page + len);
+       printk("%s", page);
 
-       /* print failing CCW area */
+       /* print failing CCW area (maximum 4) */
+       /* scsw->cda is either valid or zero  */
        len = 0;
-       if (act <  ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2) {
-               act = ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2;
-               len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
-       }
-       end = min((struct ccw1 *)(addr_t) irb->scsw.cpa + 2, last);
-       while (act <= end) {
-               len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-                              " CCW %p: %08X %08X DAT:",
-                              act, ((int *) act)[0], ((int *) act)[1]);
-               for (count = 0; count < 32 && count < act->count;
-                    count += sizeof(int))
-                       len += sprintf(page + len, " %08X",
-                                      ((int *) (addr_t) act->cda)
-                                      [(count>>2)]);
-               len += sprintf(page + len, "\n");
-               act++;
+       from = ++to;
+       fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */
+       if (from <  fail - 2) {
+               from = fail - 2;     /* there is a gap - print header */
+               len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n");
        }
+       to = min(fail + 1, last);
+       len += dasd_eckd_dump_ccw_range(from, to, page + len);
 
-       /* print last CCWs */
-       if (act <  last - 2) {
-               act = last - 2;
+       /* print last CCWs (maximum 2) */
+       from = max(from, ++to);
+       if (from < last - 1) {
+               from = last - 1;     /* there is a gap - print header */
                len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
        }
-       while (act <= last) {
-               len += sprintf(page + len, KERN_ERR PRINTK_HEADER
-                              " CCW %p: %08X %08X DAT:",
-                              act, ((int *) act)[0], ((int *) act)[1]);
-               for (count = 0; count < 32 && count < act->count;
-                    count += sizeof(int))
-                       len += sprintf(page + len, " %08X",
-                                      ((int *) (addr_t) act->cda)
-                                      [(count>>2)]);
-               len += sprintf(page + len, "\n");
-               act++;
-       }
+       len += dasd_eckd_dump_ccw_range(from, last, page + len);
        if (len > 0)
-               MESSAGE_LOG(KERN_ERR, "%s",
-                           page + sizeof(KERN_ERR PRINTK_HEADER));
+               printk("%s", page);
        free_page((unsigned long) page);
 }
 
@@ -1642,86 +1766,21 @@ static struct dasd_discipline dasd_eckd_discipline = {
        .free_cp = dasd_eckd_free_cp,
        .dump_sense = dasd_eckd_dump_sense,
        .fill_info = dasd_eckd_fill_info,
+       .ioctl = dasd_eckd_ioctl,
 };
 
 static int __init
 dasd_eckd_init(void)
 {
-       int ret;
-
-       dasd_ioctl_no_register(THIS_MODULE, BIODASDGATTR,
-                              dasd_eckd_get_attrib);
-       dasd_ioctl_no_register(THIS_MODULE, BIODASDSATTR,
-                              dasd_eckd_set_attrib);
-       dasd_ioctl_no_register(THIS_MODULE, BIODASDPSRD,
-                              dasd_eckd_performance);
-       dasd_ioctl_no_register(THIS_MODULE, BIODASDRLSE,
-                              dasd_eckd_release);
-       dasd_ioctl_no_register(THIS_MODULE, BIODASDRSRV,
-                              dasd_eckd_reserve);
-       dasd_ioctl_no_register(THIS_MODULE, BIODASDSLCK,
-                              dasd_eckd_steal_lock);
-
        ASCEBC(dasd_eckd_discipline.ebcname, 4);
-
-       ret = ccw_driver_register(&dasd_eckd_driver);
-       if (ret) {
-               dasd_ioctl_no_unregister(THIS_MODULE, BIODASDGATTR,
-                                        dasd_eckd_get_attrib);
-               dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSATTR,
-                                        dasd_eckd_set_attrib);
-               dasd_ioctl_no_unregister(THIS_MODULE, BIODASDPSRD,
-                                        dasd_eckd_performance);
-               dasd_ioctl_no_unregister(THIS_MODULE, BIODASDRLSE,
-                                        dasd_eckd_release);
-               dasd_ioctl_no_unregister(THIS_MODULE, BIODASDRSRV,
-                                        dasd_eckd_reserve);
-               dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSLCK,
-                                        dasd_eckd_steal_lock);
-               return ret;
-       }
-
-       dasd_generic_auto_online(&dasd_eckd_driver);
-       return 0;
+       return ccw_driver_register(&dasd_eckd_driver);
 }
 
 static void __exit
 dasd_eckd_cleanup(void)
 {
        ccw_driver_unregister(&dasd_eckd_driver);
-
-       dasd_ioctl_no_unregister(THIS_MODULE, BIODASDGATTR,
-                                dasd_eckd_get_attrib);
-       dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSATTR,
-                                dasd_eckd_set_attrib);
-       dasd_ioctl_no_unregister(THIS_MODULE, BIODASDPSRD,
-                                dasd_eckd_performance);
-       dasd_ioctl_no_unregister(THIS_MODULE, BIODASDRLSE,
-                                dasd_eckd_release);
-       dasd_ioctl_no_unregister(THIS_MODULE, BIODASDRSRV,
-                                dasd_eckd_reserve);
-       dasd_ioctl_no_unregister(THIS_MODULE, BIODASDSLCK,
-                                dasd_eckd_steal_lock);
 }
 
 module_init(dasd_eckd_init);
 module_exit(dasd_eckd_cleanup);
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4 
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: 1
- * tab-width: 8
- * End:
- */