]> Pileus Git - ~andy/linux/blobdiff - drivers/s390/block/dasd_3990_erp.c
[S390] dasd: improve error recovery for internal I/O
[~andy/linux] / drivers / s390 / block / dasd_3990_erp.c
index 316eb1256a999a5839065352a09a286099502ad9..44796ba4eb9b06a42358c57735f47410119da427 100644 (file)
@@ -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 */