[S390] cio: improve unit check handling for internal operations
[linux-block.git] / drivers / s390 / cio / device_ops.c
index a60124264beec97583a150973f07550eb4845193..84b9b18eabc25159d752ea1f78a0792263e97bd3 100644 (file)
@@ -96,6 +96,12 @@ ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa,
        ret = cio_set_options (sch, flags);
        if (ret)
                return ret;
+       /* Adjust requested path mask to excluded varied off paths. */
+       if (lpm) {
+               lpm &= sch->opm;
+               if (lpm == 0)
+                       return -EACCES;
+       }
        ret = cio_start_key (sch, cpa, lpm, key);
        if (ret == 0)
                cdev->private->intparm = intparm;
@@ -210,6 +216,9 @@ ccw_device_call_handler(struct ccw_device *cdev)
              (stctl & SCSW_STCTL_PRIM_STATUS)))
                return 0;
 
+       /* Clear pending timers for device driver initiated I/O. */
+       if (ending_status)
+               ccw_device_set_timeout(cdev, 0);
        /*
         * Now we are ready to call the device driver interrupt handler.
         */
@@ -250,7 +259,7 @@ ccw_device_get_path_mask(struct ccw_device *cdev)
        if (!sch)
                return 0;
        else
-               return sch->vpm;
+               return sch->lpm;
 }
 
 static void
@@ -263,6 +272,9 @@ ccw_device_wake_up(struct ccw_device *cdev, unsigned long ip, struct irb *irb)
        /* Abuse intparm for error reporting. */
        if (IS_ERR(irb))
                cdev->private->intparm = -EIO;
+       else if (irb->scsw.cc == 1)
+               /* Retry for deferred condition code. */
+               cdev->private->intparm = -EAGAIN;
        else if ((irb->scsw.dstat !=
                  (DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
                 (irb->scsw.cstat != 0)) {
@@ -276,10 +288,10 @@ ccw_device_wake_up(struct ccw_device *cdev, unsigned long ip, struct irb *irb)
                 if (cdev->private->flags.doverify ||
                         cdev->private->state == DEV_STATE_VERIFY)
                         cdev->private->intparm = -EAGAIN;
-                if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) &&
-                    !(irb->ecw[0] &
-                      (SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ)))
-                        cdev->private->intparm = -EAGAIN;
+               else if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) &&
+                        !(irb->ecw[0] &
+                          (SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ)))
+                       cdev->private->intparm = -EAGAIN;
                else if ((irb->scsw.dstat & DEV_STAT_ATTENTION) &&
                         (irb->scsw.dstat & DEV_STAT_DEV_END) &&
                         (irb->scsw.dstat & DEV_STAT_UNIT_EXCEP))
@@ -300,8 +312,11 @@ __ccw_device_retry_loop(struct ccw_device *cdev, struct ccw1 *ccw, long magic, _
 
        sch = to_subchannel(cdev->dev.parent);
        do {
+               ccw_device_set_timeout(cdev, 60 * HZ);
                ret = cio_start (sch, ccw, lpm);
-               if ((ret == -EBUSY) || (ret == -EACCES)) {
+               if (ret != 0)
+                       ccw_device_set_timeout(cdev, 0);
+               if (ret == -EBUSY) {
                        /* Try again later. */
                        spin_unlock_irq(&sch->lock);
                        msleep(10);
@@ -430,6 +445,13 @@ read_conf_data_lpm (struct ccw_device *cdev, void **buffer, int *length, __u8 lp
        if (!ciw || ciw->cmd == 0)
                return -EOPNOTSUPP;
 
+       /* Adjust requested path mask to excluded varied off paths. */
+       if (lpm) {
+               lpm &= sch->opm;
+               if (lpm == 0)
+                       return -EACCES;
+       }
+
        rcd_ccw = kzalloc(sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
        if (!rcd_ccw)
                return -ENOMEM;