From eb6e199bef288611157b8198c25d12b32bf058d0 Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Mon, 7 Dec 2009 12:51:51 +0100 Subject: [S390] dasd: improve error recovery for internal I/O Most of the error conditions reported by a FICON storage server indicate situations which can be recovered. Sometimes the host just needs to retry an I/O request, but sometimes the recovery is more complex and requires the device driver to wait, choose a different path, etc. The DASD device driver has a fully featured error recovery for normal block layer I/O, but not for internal I/O request which are for example used during the device bring up. This can lead to situations where the IPL of a system fails because DASD devices are not properly recognized. This patch will extend the internal I/O handling to use the existing error recovery procedures. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_3990_erp.c | 46 +++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 13 deletions(-) (limited to 'drivers/s390/block/dasd_3990_erp.c') diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index 316eb1256a99..44796ba4eb9b 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -69,8 +69,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status) * processing until the started timer has expired or an related * interrupt was received. */ -static void -dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) +static void dasd_3990_erp_block_queue(struct dasd_ccw_req *erp, int expires) { struct dasd_device *device = erp->startdev; @@ -80,10 +79,13 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) "blocking request queue for %is", expires/HZ); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - device->stopped |= DASD_STOPPED_PENDING; + dasd_device_set_stop_bits(device, DASD_STOPPED_PENDING); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); erp->status = DASD_CQR_FILLED; - dasd_block_set_timer(device->block, expires); + if (erp->block) + dasd_block_set_timer(erp->block, expires); + else + dasd_device_set_timer(device, expires); } /* @@ -242,9 +244,13 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) * DESCRIPTION * Setup ERP to do the ERP action 1 (see Reference manual). * Repeat the operation on a different channel path. - * If all alternate paths have been tried, the request is posted with a - * permanent error. - * Note: duplex handling is not implemented (yet). + * As deviation from the recommended recovery action, we reset the path mask + * after we have tried each path and go through all paths a second time. + * This will cover situations where only one path at a time is actually down, + * but all paths fail and recover just with the same sequence and timing as + * we try to use them (flapping links). + * If all alternate paths have been tried twice, the request is posted with + * a permanent error. * * PARAMETER * erp pointer to the current ERP @@ -253,17 +259,25 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) * erp pointer to the ERP * */ -static struct dasd_ccw_req * -dasd_3990_erp_action_1(struct dasd_ccw_req * erp) +static struct dasd_ccw_req *dasd_3990_erp_action_1_sec(struct dasd_ccw_req *erp) { + erp->function = dasd_3990_erp_action_1_sec; + dasd_3990_erp_alternate_path(erp); + return erp; +} +static struct dasd_ccw_req *dasd_3990_erp_action_1(struct dasd_ccw_req *erp) +{ erp->function = dasd_3990_erp_action_1; - dasd_3990_erp_alternate_path(erp); - + if (erp->status == DASD_CQR_FAILED) { + erp->status = DASD_CQR_FILLED; + erp->retries = 10; + erp->lpm = LPM_ANYPATH; + erp->function = dasd_3990_erp_action_1_sec; + } return erp; - -} /* end dasd_3990_erp_action_1 */ +} /* end dasd_3990_erp_action_1(b) */ /* * DASD_3990_ERP_ACTION_4 @@ -2294,6 +2308,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) return cqr; } + ccw = cqr->cpaddr; if (cqr->cpmode == 1) { /* make a shallow copy of the original tcw but set new tsb */ erp->cpmode = 1; @@ -2302,6 +2317,9 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) tsb = (struct tsb *) &tcw[1]; *tcw = *((struct tcw *)cqr->cpaddr); tcw->tsb = (long)tsb; + } else if (ccw->cmd_code == DASD_ECKD_CCW_PSF) { + /* PSF cannot be chained from NOOP/TIC */ + erp->cpaddr = cqr->cpaddr; } else { /* initialize request with default TIC to current ERP/CQR */ ccw = erp->cpaddr; @@ -2486,6 +2504,8 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) erp = dasd_3990_erp_action_1(erp); + } else if (erp->function == dasd_3990_erp_action_1_sec) { + erp = dasd_3990_erp_action_1_sec(erp); } else if (erp->function == dasd_3990_erp_action_5) { /* retries have not been successful */ -- cgit v1.2.3