From: Stefan Weinhuber Add code to the dasd driver to select the I/O path based on the path preferences of the device. Signed-off-by: Martin Schwidefsky Signed-off-by: Andrew Morton --- 25-akpm/drivers/s390/block/dasd.c | 21 +++++-- 25-akpm/drivers/s390/block/dasd_eckd.c | 97 +++++++++++++++++++++++---------- 25-akpm/drivers/s390/block/dasd_eckd.h | 8 ++ 25-akpm/drivers/s390/block/dasd_erp.c | 3 - 25-akpm/drivers/s390/cio/device_ops.c | 22 +++++-- 25-akpm/include/asm-s390/ccwdev.h | 2 6 files changed, 113 insertions(+), 40 deletions(-) diff -puN drivers/s390/block/dasd.c~s390-dasd-preferred-path-support drivers/s390/block/dasd.c --- 25/drivers/s390/block/dasd.c~s390-dasd-preferred-path-support Thu Mar 24 15:28:58 2005 +++ 25-akpm/drivers/s390/block/dasd.c Thu Mar 24 15:28:58 2005 @@ -7,7 +7,7 @@ * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 * - * $Revision: 1.156 $ + * $Revision: 1.158 $ */ #include @@ -757,6 +757,17 @@ dasd_start_IO(struct dasd_ccw_req * cqr) DBF_DEV_EVENT(DBF_ERR, device, "%s", "start_IO: request timeout, retry later"); break; + case -EACCES: + /* -EACCES indicates that the request used only a + * subset of the available pathes and all these + * pathes are gone. + * Do a retry with all available pathes. + */ + cqr->lpm = LPM_ANYPATH; + DBF_DEV_EVENT(DBF_ERR, device, "%s", + "start_IO: selected pathes gone," + " retry on all pathes"); + break; case -ENODEV: case -EIO: DBF_DEV_EVENT(DBF_ERR, device, "%s", @@ -1222,7 +1233,9 @@ __dasd_start_head(struct dasd_device * d rc = device->discipline->start_IO(cqr); if (rc == 0) dasd_set_timer(device, cqr->expires); - else + else if (rc == -EACCES) { + dasd_schedule_bh(device); + } else /* Hmpf, try again in 1/2 sec */ dasd_set_timer(device, 50); } @@ -1813,8 +1826,8 @@ dasd_generic_set_online (struct ccw_devi if (rc) { printk (KERN_WARNING "dasd_generic couldn't online device %s " - "with discipline %s\n", - cdev->dev.bus_id, discipline->name); + "with discipline %s rc=%i\n", + cdev->dev.bus_id, discipline->name, rc); dasd_delete_device(device); return rc; } diff -puN drivers/s390/block/dasd_eckd.c~s390-dasd-preferred-path-support drivers/s390/block/dasd_eckd.c --- 25/drivers/s390/block/dasd_eckd.c~s390-dasd-preferred-path-support Thu Mar 24 15:28:58 2005 +++ 25-akpm/drivers/s390/block/dasd_eckd.c Thu Mar 24 15:28:58 2005 @@ -7,7 +7,7 @@ * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * - * $Revision: 1.68 $ + * $Revision: 1.69 $ */ #include @@ -56,6 +56,7 @@ static struct dasd_discipline dasd_eckd_ struct dasd_eckd_private { struct dasd_eckd_characteristics rdc_data; struct dasd_eckd_confdata conf_data; + struct dasd_eckd_path path_data; struct eckd_count count_area[5]; int init_cqr_status; int uses_cdl; @@ -447,12 +448,72 @@ dasd_eckd_cdl_reclen(int recid) } static int +dasd_eckd_read_conf(struct dasd_device *device) +{ + void *conf_data; + int conf_len, conf_data_saved; + int rc; + __u8 lpm; + struct dasd_eckd_private *private; + struct dasd_eckd_path *path_data; + + private = (struct dasd_eckd_private *) device->private; + path_data = (struct dasd_eckd_path *) &private->path_data; + path_data->opm = ccw_device_get_path_mask(device->cdev); + lpm = 0x80; + conf_data_saved = 0; + + /* get configuration data per operational path */ + for (lpm = 0x80; lpm; lpm>>= 1) { + if (lpm & path_data->opm){ + rc = read_conf_data_lpm(device->cdev, &conf_data, + &conf_len, lpm); + if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ + MESSAGE(KERN_WARNING, + "Read configuration data returned " + "error %d", rc); + return rc; + } + if (conf_data == NULL) { + MESSAGE(KERN_WARNING, "%s", "No configuration " + "data retrieved"); + continue; /* no errror */ + } + if (conf_len != sizeof (struct dasd_eckd_confdata)) { + MESSAGE(KERN_WARNING, + "sizes of configuration data mismatch" + "%d (read) vs %ld (expected)", + conf_len, + sizeof (struct dasd_eckd_confdata)); + kfree(conf_data); + continue; /* no errror */ + } + /* save first valid configuration data */ + if (!conf_data_saved){ + memcpy(&private->conf_data, conf_data, + sizeof (struct dasd_eckd_confdata)); + conf_data_saved++; + } + switch (((char *)conf_data)[242] & 0x07){ + case 0x02: + path_data->npm |= lpm; + break; + case 0x03: + path_data->ppm |= lpm; + break; + } + kfree(conf_data); + } + } + return 0; +} + + +static int dasd_eckd_check_characteristics(struct dasd_device *device) { struct dasd_eckd_private *private; void *rdc_data; - void *conf_data; - int conf_len; int rc; private = (struct dasd_eckd_private *) device->private; @@ -465,6 +526,7 @@ dasd_eckd_check_characteristics(struct d "data"); return -ENOMEM; } + memset(private, 0, sizeof(struct dasd_eckd_private)); device->private = (void *) private; } /* Invalidate status of initial analysis. */ @@ -494,30 +556,9 @@ dasd_eckd_check_characteristics(struct d private->rdc_data.sec_per_trk); /* Read Configuration Data */ - rc = read_conf_data(device->cdev, &conf_data, &conf_len); - if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ - DEV_MESSAGE(KERN_WARNING, device, - "Read configuration data returned error %d", rc); - return rc; - } - if (conf_data == NULL) { - DEV_MESSAGE(KERN_WARNING, device, "%s", - "No configuration data retrieved"); - return 0; /* no errror */ - } - if (conf_len != sizeof (struct dasd_eckd_confdata)) { - DEV_MESSAGE(KERN_WARNING, device, - "sizes of configuration data mismatch" - "%d (read) vs %ld (expected)", - conf_len, sizeof (struct dasd_eckd_confdata)); - - kfree(conf_data); /* allocated by read_conf_data() */ - return 0; /* no errror */ - } - memcpy(&private->conf_data, conf_data, - sizeof (struct dasd_eckd_confdata)); - kfree(conf_data); /* allocated by read_conf_data() */ - return 0; + rc = dasd_eckd_read_conf (device); + return rc; + } static struct dasd_ccw_req * @@ -1096,7 +1137,7 @@ dasd_eckd_build_cp(struct dasd_device * } cqr->device = device; cqr->expires = 5 * 60 * HZ; /* 5 minutes */ - cqr->lpm = LPM_ANYPATH; + cqr->lpm = private->path_data.ppm; cqr->retries = 256; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; diff -puN drivers/s390/block/dasd_eckd.h~s390-dasd-preferred-path-support drivers/s390/block/dasd_eckd.h --- 25/drivers/s390/block/dasd_eckd.h~s390-dasd-preferred-path-support Thu Mar 24 15:28:58 2005 +++ 25-akpm/drivers/s390/block/dasd_eckd.h Thu Mar 24 15:28:58 2005 @@ -5,7 +5,7 @@ * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 * - * $Revision: 1.9 $ + * $Revision: 1.10 $ */ #ifndef DASD_ECKD_H @@ -326,6 +326,12 @@ struct dasd_eckd_confdata { } __attribute__ ((packed)) neq; } __attribute__ ((packed)); +struct dasd_eckd_path { + __u8 opm; + __u8 ppm; + __u8 npm; +}; + /* * Perform Subsystem Function - Prepare for Read Subsystem Data */ diff -puN drivers/s390/block/dasd_erp.c~s390-dasd-preferred-path-support drivers/s390/block/dasd_erp.c --- 25/drivers/s390/block/dasd_erp.c~s390-dasd-preferred-path-support Thu Mar 24 15:28:58 2005 +++ 25-akpm/drivers/s390/block/dasd_erp.c Thu Mar 24 15:28:58 2005 @@ -7,7 +7,7 @@ * Bugreports.to..: * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 * - * $Revision: 1.13 $ + * $Revision: 1.14 $ */ #include @@ -95,6 +95,7 @@ dasd_default_erp_action(struct dasd_ccw_ DEV_MESSAGE (KERN_DEBUG, device, "default ERP called (%i retries left)", cqr->retries); + cqr->lpm = LPM_ANYPATH; cqr->status = DASD_CQR_QUEUED; } else { DEV_MESSAGE (KERN_WARNING, device, "%s", diff -puN drivers/s390/cio/device_ops.c~s390-dasd-preferred-path-support drivers/s390/cio/device_ops.c --- 25/drivers/s390/cio/device_ops.c~s390-dasd-preferred-path-support Thu Mar 24 15:28:58 2005 +++ 25-akpm/drivers/s390/cio/device_ops.c Thu Mar 24 15:28:58 2005 @@ -291,14 +291,14 @@ ccw_device_wake_up(struct ccw_device *cd } static inline int -__ccw_device_retry_loop(struct ccw_device *cdev, struct ccw1 *ccw, long magic) +__ccw_device_retry_loop(struct ccw_device *cdev, struct ccw1 *ccw, long magic, __u8 lpm) { int ret; struct subchannel *sch; sch = to_subchannel(cdev->dev.parent); do { - ret = cio_start (sch, ccw, 0); + ret = cio_start (sch, ccw, lpm); if ((ret == -EBUSY) || (ret == -EACCES)) { /* Try again later. */ spin_unlock_irq(&sch->lock); @@ -388,7 +388,7 @@ read_dev_chars (struct ccw_device *cdev, ret = -EBUSY; else /* 0x00D9C4C3 == ebcdic "RDC" */ - ret = __ccw_device_retry_loop(cdev, rdc_ccw, 0x00D9C4C3); + ret = __ccw_device_retry_loop(cdev, rdc_ccw, 0x00D9C4C3, 0); /* Restore interrupt handler. */ cdev->handler = handler; @@ -401,10 +401,10 @@ read_dev_chars (struct ccw_device *cdev, } /* - * Read Configuration data + * Read Configuration data using path mask */ int -read_conf_data (struct ccw_device *cdev, void **buffer, int *length) +read_conf_data_lpm (struct ccw_device *cdev, void **buffer, int *length, __u8 lpm) { void (*handler)(struct ccw_device *, unsigned long, struct irb *); struct subchannel *sch; @@ -457,7 +457,7 @@ read_conf_data (struct ccw_device *cdev, ret = -EBUSY; else /* 0x00D9C3C4 == ebcdic "RCD" */ - ret = __ccw_device_retry_loop(cdev, rcd_ccw, 0x00D9C3C4); + ret = __ccw_device_retry_loop(cdev, rcd_ccw, 0x00D9C3C4, lpm); /* Restore interrupt handler. */ cdev->handler = handler; @@ -480,6 +480,15 @@ read_conf_data (struct ccw_device *cdev, } /* + * Read Configuration data + */ +int +read_conf_data (struct ccw_device *cdev, void **buffer, int *length) +{ + return read_conf_data_lpm (cdev, buffer, length, 0); +} + +/* * Try to break the lock on a boxed device. */ int @@ -580,3 +589,4 @@ EXPORT_SYMBOL(read_conf_data); EXPORT_SYMBOL(read_dev_chars); EXPORT_SYMBOL(_ccw_device_get_subchannel_number); EXPORT_SYMBOL(_ccw_device_get_device_number); +EXPORT_SYMBOL_GPL(read_conf_data_lpm); diff -puN include/asm-s390/ccwdev.h~s390-dasd-preferred-path-support include/asm-s390/ccwdev.h --- 25/include/asm-s390/ccwdev.h~s390-dasd-preferred-path-support Thu Mar 24 15:28:58 2005 +++ 25-akpm/include/asm-s390/ccwdev.h Thu Mar 24 15:28:58 2005 @@ -164,6 +164,8 @@ extern int ccw_device_clear(struct ccw_d extern int read_dev_chars(struct ccw_device *cdev, void **buffer, int length); extern int read_conf_data(struct ccw_device *cdev, void **buffer, int *length); +extern int read_conf_data_lpm(struct ccw_device *cdev, void **buffer, + int *length, __u8 lpm); extern int ccw_device_set_online(struct ccw_device *cdev); extern int ccw_device_set_offline(struct ccw_device *cdev); _