Merge branch 'topic/core-cleanup' into for-linus
[safe/jmp/linux-2.6] / drivers / s390 / cio / device_fsm.c
index dc93730..c9b8526 100644 (file)
@@ -39,31 +39,45 @@ static void ccw_timeout_log(struct ccw_device *cdev)
        struct schib schib;
        struct subchannel *sch;
        struct io_subchannel_private *private;
+       union orb *orb;
        int cc;
 
        sch = to_subchannel(cdev->dev.parent);
        private = to_io_private(sch);
-       cc = stsch(sch->schid, &schib);
+       orb = &private->orb;
+       cc = stsch_err(sch->schid, &schib);
 
        printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, "
               "device information:\n", get_clock());
        printk(KERN_WARNING "cio: orb:\n");
        print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
-                      &private->orb, sizeof(private->orb), 0);
-       printk(KERN_WARNING "cio: ccw device bus id: %s\n", cdev->dev.bus_id);
-       printk(KERN_WARNING "cio: subchannel bus id: %s\n", sch->dev.bus_id);
+                      orb, sizeof(*orb), 0);
+       printk(KERN_WARNING "cio: ccw device bus id: %s\n",
+              dev_name(&cdev->dev));
+       printk(KERN_WARNING "cio: subchannel bus id: %s\n",
+              dev_name(&sch->dev));
        printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, "
               "vpm: %02x\n", sch->lpm, sch->opm, sch->vpm);
 
-       if ((void *)(addr_t)private->orb.cpa == &private->sense_ccw ||
-           (void *)(addr_t)private->orb.cpa == cdev->private->iccws)
-               printk(KERN_WARNING "cio: last channel program (intern):\n");
-       else
-               printk(KERN_WARNING "cio: last channel program:\n");
-
-       print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
-                      (void *)(addr_t)private->orb.cpa,
-                      sizeof(struct ccw1), 0);
+       if (orb->tm.b) {
+               printk(KERN_WARNING "cio: orb indicates transport mode\n");
+               printk(KERN_WARNING "cio: last tcw:\n");
+               print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
+                              (void *)(addr_t)orb->tm.tcw,
+                              sizeof(struct tcw), 0);
+       } else {
+               printk(KERN_WARNING "cio: orb indicates command mode\n");
+               if ((void *)(addr_t)orb->cmd.cpa == &private->sense_ccw ||
+                   (void *)(addr_t)orb->cmd.cpa == cdev->private->iccws)
+                       printk(KERN_WARNING "cio: last channel program "
+                              "(intern):\n");
+               else
+                       printk(KERN_WARNING "cio: last channel program:\n");
+
+               print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
+                              (void *)(addr_t)orb->cmd.cpa,
+                              sizeof(struct ccw1), 0);
+       }
        printk(KERN_WARNING "cio: ccw device state: %d\n",
               cdev->private->state);
        printk(KERN_WARNING "cio: store subchannel returned: cc=%d\n", cc);
@@ -126,8 +140,7 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
        int ret;
 
        sch = to_subchannel(cdev->dev.parent);
-       ret = stsch(sch->schid, &sch->schib);
-       if (ret || !sch->schib.pmcw.dnv)
+       if (cio_update_schib(sch))
                return -ENODEV; 
        if (!sch->schib.pmcw.ena)
                /* Not operational -> done. */
@@ -135,10 +148,13 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
        /* Stage 1: cancel io. */
        if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_HALT_PEND) &&
            !(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) {
-               ret = cio_cancel(sch);
-               if (ret != -EINVAL)
-                       return ret;
-               /* cancel io unsuccessful. From now on it is asynchronous. */
+               if (!scsw_is_tm(&sch->schib.scsw)) {
+                       ret = cio_cancel(sch);
+                       if (ret != -EINVAL)
+                               return ret;
+               }
+               /* cancel io unsuccessful or not applicable (transport mode).
+                * Continue with asynchronous instructions. */
                cdev->private->iretry = 3;      /* 3 halt retries. */
        }
        if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) {
@@ -161,29 +177,21 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
        panic("Can't stop i/o on subchannel.\n");
 }
 
-static int
-ccw_device_handle_oper(struct ccw_device *cdev)
+void ccw_device_update_sense_data(struct ccw_device *cdev)
 {
-       struct subchannel *sch;
+       memset(&cdev->id, 0, sizeof(cdev->id));
+       cdev->id.cu_type   = cdev->private->senseid.cu_type;
+       cdev->id.cu_model  = cdev->private->senseid.cu_model;
+       cdev->id.dev_type  = cdev->private->senseid.dev_type;
+       cdev->id.dev_model = cdev->private->senseid.dev_model;
+}
 
-       sch = to_subchannel(cdev->dev.parent);
-       cdev->private->flags.recog_done = 1;
-       /*
-        * Check if cu type and device type still match. If
-        * not, it is certainly another device and we have to
-        * de- and re-register.
-        */
-       if (cdev->id.cu_type != cdev->private->senseid.cu_type ||
-           cdev->id.cu_model != cdev->private->senseid.cu_model ||
-           cdev->id.dev_type != cdev->private->senseid.dev_type ||
-           cdev->id.dev_model != cdev->private->senseid.dev_model) {
-               PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_do_unreg_rereg);
-               queue_work(ccw_device_work, &cdev->private->kick_work);
-               return 0;
-       }
-       cdev->private->flags.donotify = 1;
-       return 1;
+int ccw_device_test_sense_data(struct ccw_device *cdev)
+{
+       return cdev->id.cu_type == cdev->private->senseid.cu_type &&
+               cdev->id.cu_model == cdev->private->senseid.cu_model &&
+               cdev->id.dev_type == cdev->private->senseid.dev_type &&
+               cdev->id.dev_model == cdev->private->senseid.dev_model;
 }
 
 /*
@@ -217,84 +225,73 @@ static void
 ccw_device_recog_done(struct ccw_device *cdev, int state)
 {
        struct subchannel *sch;
-       int notify, old_lpm, same_dev;
+       int old_lpm;
 
        sch = to_subchannel(cdev->dev.parent);
 
-       ccw_device_set_timeout(cdev, 0);
-       cio_disable_subchannel(sch);
+       if (cio_disable_subchannel(sch))
+               state = DEV_STATE_NOT_OPER;
        /*
         * Now that we tried recognition, we have performed device selection
         * through ssch() and the path information is up to date.
         */
        old_lpm = sch->lpm;
-       stsch(sch->schid, &sch->schib);
-       sch->lpm = sch->schib.pmcw.pam & sch->opm;
+
        /* Check since device may again have become not operational. */
-       if (!sch->schib.pmcw.dnv)
+       if (cio_update_schib(sch))
                state = DEV_STATE_NOT_OPER;
+       else
+               sch->lpm = sch->schib.pmcw.pam & sch->opm;
+
        if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID)
                /* Force reprobe on all chpids. */
                old_lpm = 0;
        if (sch->lpm != old_lpm)
                __recover_lost_chpids(sch, old_lpm);
-       if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) {
-               if (state == DEV_STATE_NOT_OPER) {
-                       cdev->private->flags.recog_done = 1;
-                       cdev->private->state = DEV_STATE_DISCONNECTED;
-                       return;
-               }
-               /* Boxed devices don't need extra treatment. */
+       if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID &&
+           (state == DEV_STATE_NOT_OPER || state == DEV_STATE_BOXED)) {
+               cdev->private->flags.recog_done = 1;
+               cdev->private->state = DEV_STATE_DISCONNECTED;
+               wake_up(&cdev->private->wait_q);
+               return;
+       }
+       if (cdev->private->flags.resuming) {
+               cdev->private->state = state;
+               cdev->private->flags.recog_done = 1;
+               wake_up(&cdev->private->wait_q);
+               return;
        }
-       notify = 0;
-       same_dev = 0; /* Keep the compiler quiet... */
        switch (state) {
        case DEV_STATE_NOT_OPER:
-               CIO_MSG_EVENT(2, "SenseID : unknown device %04x on "
-                             "subchannel 0.%x.%04x\n",
-                             cdev->private->dev_id.devno,
-                             sch->schid.ssid, sch->schid.sch_no);
                break;
        case DEV_STATE_OFFLINE:
-               if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) {
-                       same_dev = ccw_device_handle_oper(cdev);
-                       notify = 1;
+               if (!cdev->online) {
+                       ccw_device_update_sense_data(cdev);
+                       break;
                }
-               /* fill out sense information */
-               memset(&cdev->id, 0, sizeof(cdev->id));
-               cdev->id.cu_type   = cdev->private->senseid.cu_type;
-               cdev->id.cu_model  = cdev->private->senseid.cu_model;
-               cdev->id.dev_type  = cdev->private->senseid.dev_type;
-               cdev->id.dev_model = cdev->private->senseid.dev_model;
-               if (notify) {
-                       cdev->private->state = DEV_STATE_OFFLINE;
-                       if (same_dev) {
-                               /* Get device online again. */
-                               ccw_device_online(cdev);
-                               wake_up(&cdev->private->wait_q);
-                       }
-                       return;
+               cdev->private->state = DEV_STATE_OFFLINE;
+               cdev->private->flags.recog_done = 1;
+               if (ccw_device_test_sense_data(cdev)) {
+                       cdev->private->flags.donotify = 1;
+                       ccw_device_online(cdev);
+                       wake_up(&cdev->private->wait_q);
+               } else {
+                       ccw_device_update_sense_data(cdev);
+                       ccw_device_sched_todo(cdev, CDEV_TODO_REBIND);
                }
-               /* Issue device info message. */
-               CIO_MSG_EVENT(4, "SenseID : device 0.%x.%04x reports: "
-                             "CU  Type/Mod = %04X/%02X, Dev Type/Mod = "
-                             "%04X/%02X\n",
-                             cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno,
-                             cdev->id.cu_type, cdev->id.cu_model,
-                             cdev->id.dev_type, cdev->id.dev_model);
-               break;
+               return;
        case DEV_STATE_BOXED:
-               CIO_MSG_EVENT(0, "SenseID : boxed device %04x on "
-                             " subchannel 0.%x.%04x\n",
-                             cdev->private->dev_id.devno,
-                             sch->schid.ssid, sch->schid.sch_no);
+               if (cdev->id.cu_type != 0) { /* device was recognized before */
+                       cdev->private->flags.recog_done = 1;
+                       cdev->private->state = DEV_STATE_BOXED;
+                       wake_up(&cdev->private->wait_q);
+                       return;
+               }
                break;
        }
        cdev->private->state = state;
        io_subchannel_recog_done(cdev);
-       if (state != DEV_STATE_NOT_OPER)
-               wake_up(&cdev->private->wait_q);
+       wake_up(&cdev->private->wait_q);
 }
 
 /*
@@ -316,38 +313,50 @@ ccw_device_sense_id_done(struct ccw_device *cdev, int err)
        }
 }
 
+/**
+  * ccw_device_notify() - inform the device's driver about an event
+  * @cdev: device for which an event occured
+  * @event: event that occurred
+  *
+  * Returns:
+  *   -%EINVAL if the device is offline or has no driver.
+  *   -%EOPNOTSUPP if the device's driver has no notifier registered.
+  *   %NOTIFY_OK if the driver wants to keep the device.
+  *   %NOTIFY_BAD if the driver doesn't want to keep the device.
+  */
 int ccw_device_notify(struct ccw_device *cdev, int event)
 {
+       int ret = -EINVAL;
+
        if (!cdev->drv)
-               return 0;
+               goto out;
        if (!cdev->online)
-               return 0;
-       return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
+               goto out;
+       CIO_MSG_EVENT(2, "notify called for 0.%x.%04x, event=%d\n",
+                     cdev->private->dev_id.ssid, cdev->private->dev_id.devno,
+                     event);
+       if (!cdev->drv->notify) {
+               ret = -EOPNOTSUPP;
+               goto out;
+       }
+       if (cdev->drv->notify(cdev, event))
+               ret = NOTIFY_OK;
+       else
+               ret = NOTIFY_BAD;
+out:
+       return ret;
 }
 
-static void
-ccw_device_oper_notify(struct work_struct *work)
+static void ccw_device_oper_notify(struct ccw_device *cdev)
 {
-       struct ccw_device_private *priv;
-       struct ccw_device *cdev;
-       int ret;
-       unsigned long flags;
-
-       priv = container_of(work, struct ccw_device_private, kick_work);
-       cdev = priv->cdev;
-       ret = ccw_device_notify(cdev, CIO_OPER);
-       spin_lock_irqsave(cdev->ccwlock, flags);
-       if (ret) {
+       if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_OK) {
                /* Reenable channel measurements, if needed. */
-               spin_unlock_irqrestore(cdev->ccwlock, flags);
-               cmf_reenable(cdev);
-               spin_lock_irqsave(cdev->ccwlock, flags);
-               wake_up(&cdev->private->wait_q);
+               ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF);
+               return;
        }
-       spin_unlock_irqrestore(cdev->ccwlock, flags);
-       if (!ret)
-               /* Driver doesn't want device back. */
-               ccw_device_do_unreg_rereg(work);
+       /* Driver doesn't want device back. */
+       ccw_device_set_notoper(cdev);
+       ccw_device_sched_todo(cdev, CDEV_TODO_REBIND);
 }
 
 /*
@@ -370,126 +379,51 @@ ccw_device_done(struct ccw_device *cdev, int state)
 
        cdev->private->state = state;
 
-
-       if (state == DEV_STATE_BOXED)
+       switch (state) {
+       case DEV_STATE_BOXED:
                CIO_MSG_EVENT(0, "Boxed device %04x on subchannel %04x\n",
                              cdev->private->dev_id.devno, sch->schid.sch_no);
-
-       if (cdev->private->flags.donotify) {
+               if (cdev->online &&
+                   ccw_device_notify(cdev, CIO_BOXED) != NOTIFY_OK)
+                       ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
                cdev->private->flags.donotify = 0;
-               PREPARE_WORK(&cdev->private->kick_work, ccw_device_oper_notify);
-               queue_work(ccw_device_notify_work, &cdev->private->kick_work);
-       }
-       wake_up(&cdev->private->wait_q);
-
-       if (css_init_done && state != DEV_STATE_ONLINE)
-               put_device (&cdev->dev);
-}
-
-static int cmp_pgid(struct pgid *p1, struct pgid *p2)
-{
-       char *c1;
-       char *c2;
-
-       c1 = (char *)p1;
-       c2 = (char *)p2;
-
-       return memcmp(c1 + 1, c2 + 1, sizeof(struct pgid) - 1);
-}
-
-static void __ccw_device_get_common_pgid(struct ccw_device *cdev)
-{
-       int i;
-       int last;
-
-       last = 0;
-       for (i = 0; i < 8; i++) {
-               if (cdev->private->pgid[i].inf.ps.state1 == SNID_STATE1_RESET)
-                       /* No PGID yet */
-                       continue;
-               if (cdev->private->pgid[last].inf.ps.state1 ==
-                   SNID_STATE1_RESET) {
-                       /* First non-zero PGID */
-                       last = i;
-                       continue;
-               }
-               if (cmp_pgid(&cdev->private->pgid[i],
-                            &cdev->private->pgid[last]) == 0)
-                       /* Non-conflicting PGIDs */
-                       continue;
-
-               /* PGID mismatch, can't pathgroup. */
-               CIO_MSG_EVENT(0, "SNID - pgid mismatch for device "
-                             "0.%x.%04x, can't pathgroup\n",
-                             cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno);
-               cdev->private->options.pgroup = 0;
-               return;
-       }
-       if (cdev->private->pgid[last].inf.ps.state1 ==
-           SNID_STATE1_RESET)
-               /* No previous pgid found */
-               memcpy(&cdev->private->pgid[0],
-                      &channel_subsystems[0]->global_pgid,
-                      sizeof(struct pgid));
-       else
-               /* Use existing pgid */
-               memcpy(&cdev->private->pgid[0], &cdev->private->pgid[last],
-                      sizeof(struct pgid));
-}
-
-/*
- * Function called from device_pgid.c after sense path ground has completed.
- */
-void
-ccw_device_sense_pgid_done(struct ccw_device *cdev, int err)
-{
-       struct subchannel *sch;
-
-       sch = to_subchannel(cdev->dev.parent);
-       switch (err) {
-       case -EOPNOTSUPP: /* path grouping not supported, use nop instead. */
-               cdev->private->options.pgroup = 0;
                break;
-       case 0: /* success */
-       case -EACCES: /* partial success, some paths not operational */
-               /* Check if all pgids are equal or 0. */
-               __ccw_device_get_common_pgid(cdev);
+       case DEV_STATE_NOT_OPER:
+               CIO_MSG_EVENT(0, "Device %04x gone on subchannel %04x\n",
+                             cdev->private->dev_id.devno, sch->schid.sch_no);
+               if (ccw_device_notify(cdev, CIO_GONE) != NOTIFY_OK)
+                       ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
+               else
+                       ccw_device_set_disconnected(cdev);
+               cdev->private->flags.donotify = 0;
+               break;
+       case DEV_STATE_DISCONNECTED:
+               CIO_MSG_EVENT(0, "Disconnected device %04x on subchannel "
+                             "%04x\n", cdev->private->dev_id.devno,
+                             sch->schid.sch_no);
+               if (ccw_device_notify(cdev, CIO_NO_PATH) != NOTIFY_OK)
+                       ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
+               else
+                       ccw_device_set_disconnected(cdev);
+               cdev->private->flags.donotify = 0;
                break;
-       case -ETIME:            /* Sense path group id stopped by timeout. */
-       case -EUSERS:           /* device is reserved for someone else. */
-               ccw_device_done(cdev, DEV_STATE_BOXED);
-               return;
        default:
-               ccw_device_done(cdev, DEV_STATE_NOT_OPER);
-               return;
+               break;
        }
-       /* Start Path Group verification. */
-       cdev->private->state = DEV_STATE_VERIFY;
-       cdev->private->flags.doverify = 0;
-       ccw_device_verify_start(cdev);
+
+       if (cdev->private->flags.donotify) {
+               cdev->private->flags.donotify = 0;
+               ccw_device_oper_notify(cdev);
+       }
+       wake_up(&cdev->private->wait_q);
 }
 
 /*
  * Start device recognition.
  */
-int
-ccw_device_recognition(struct ccw_device *cdev)
+void ccw_device_recognition(struct ccw_device *cdev)
 {
-       struct subchannel *sch;
-       int ret;
-
-       if ((cdev->private->state != DEV_STATE_NOT_OPER) &&
-           (cdev->private->state != DEV_STATE_BOXED))
-               return -EINVAL;
-       sch = to_subchannel(cdev->dev.parent);
-       ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
-       if (ret != 0)
-               /* Couldn't enable the subchannel for i/o. Sick device. */
-               return ret;
-
-       /* After 60s the device recognition is considered to have failed. */
-       ccw_device_set_timeout(cdev, 60*HZ);
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
 
        /*
         * We used to start here with a sense pgid to find out whether a device
@@ -501,32 +435,33 @@ ccw_device_recognition(struct ccw_device *cdev)
         */
        cdev->private->flags.recog_done = 0;
        cdev->private->state = DEV_STATE_SENSE_ID;
+       if (cio_enable_subchannel(sch, (u32) (addr_t) sch)) {
+               ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER);
+               return;
+       }
        ccw_device_sense_id_start(cdev);
-       return 0;
 }
 
 /*
- * Handle timeout in device recognition.
+ * Handle events for states that use the ccw request infrastructure.
  */
-static void
-ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event)
+static void ccw_device_request_event(struct ccw_device *cdev, enum dev_event e)
 {
-       int ret;
-
-       ret = ccw_device_cancel_halt_clear(cdev);
-       switch (ret) {
-       case 0:
-               ccw_device_recog_done(cdev, DEV_STATE_BOXED);
+       switch (e) {
+       case DEV_EVENT_NOTOPER:
+               ccw_request_notoper(cdev);
                break;
-       case -ENODEV:
-               ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER);
+       case DEV_EVENT_INTERRUPT:
+               ccw_request_handler(cdev);
+               break;
+       case DEV_EVENT_TIMEOUT:
+               ccw_request_timeout(cdev);
                break;
        default:
-               ccw_device_set_timeout(cdev, 3*HZ);
+               break;
        }
 }
 
-
 void
 ccw_device_verify_done(struct ccw_device *cdev, int err)
 {
@@ -534,18 +469,19 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
 
        sch = to_subchannel(cdev->dev.parent);
        /* Update schib - pom may have changed. */
-       stsch(sch->schid, &sch->schib);
+       if (cio_update_schib(sch)) {
+               err = -ENODEV;
+               goto callback;
+       }
        /* Update lpm with verified path mask. */
        sch->lpm = sch->vpm;
        /* Repeat path verification? */
        if (cdev->private->flags.doverify) {
-               cdev->private->flags.doverify = 0;
                ccw_device_verify_start(cdev);
                return;
        }
+callback:
        switch (err) {
-       case -EOPNOTSUPP: /* path grouping not supported, just set online. */
-               cdev->private->options.pgroup = 0;
        case 0:
                ccw_device_done(cdev, DEV_STATE_ONLINE);
                /* Deliver fake irb to device driver, if needed. */
@@ -564,18 +500,20 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
                }
                break;
        case -ETIME:
+       case -EUSERS:
                /* Reset oper notify indication after verify error. */
                cdev->private->flags.donotify = 0;
                ccw_device_done(cdev, DEV_STATE_BOXED);
                break;
+       case -EACCES:
+               /* Reset oper notify indication after verify error. */
+               cdev->private->flags.donotify = 0;
+               ccw_device_done(cdev, DEV_STATE_DISCONNECTED);
+               break;
        default:
                /* Reset oper notify indication after verify error. */
                cdev->private->flags.donotify = 0;
-               if (cdev->online) {
-                       ccw_device_set_timeout(cdev, 0);
-                       dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
-               } else
-                       ccw_device_done(cdev, DEV_STATE_NOT_OPER);
+               ccw_device_done(cdev, DEV_STATE_NOT_OPER);
                break;
        }
 }
@@ -593,8 +531,6 @@ ccw_device_online(struct ccw_device *cdev)
            (cdev->private->state != DEV_STATE_BOXED))
                return -EINVAL;
        sch = to_subchannel(cdev->dev.parent);
-       if (css_init_done && !get_device(&cdev->dev))
-               return -ENODEV;
        ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
        if (ret != 0) {
                /* Couldn't enable the subchannel for i/o. Sick device. */
@@ -602,17 +538,9 @@ ccw_device_online(struct ccw_device *cdev)
                        dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
                return ret;
        }
-       /* Do we want to do path grouping? */
-       if (!cdev->private->options.pgroup) {
-               /* Start initial path verification. */
-               cdev->private->state = DEV_STATE_VERIFY;
-               cdev->private->flags.doverify = 0;
-               ccw_device_verify_start(cdev);
-               return 0;
-       }
-       /* Do a SensePGID first. */
-       cdev->private->state = DEV_STATE_SENSE_PGID;
-       ccw_device_sense_pgid_start(cdev);
+       /* Start initial path verification. */
+       cdev->private->state = DEV_STATE_VERIFY;
+       ccw_device_verify_start(cdev);
        return 0;
 }
 
@@ -628,7 +556,6 @@ ccw_device_disband_done(struct ccw_device *cdev, int err)
                break;
        default:
                cdev->private->flags.donotify = 0;
-               dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
                ccw_device_done(cdev, DEV_STATE_NOT_OPER);
                break;
        }
@@ -642,19 +569,30 @@ ccw_device_offline(struct ccw_device *cdev)
 {
        struct subchannel *sch;
 
+       /* Allow ccw_device_offline while disconnected. */
+       if (cdev->private->state == DEV_STATE_DISCONNECTED ||
+           cdev->private->state == DEV_STATE_NOT_OPER) {
+               cdev->private->flags.donotify = 0;
+               ccw_device_done(cdev, DEV_STATE_NOT_OPER);
+               return 0;
+       }
+       if (cdev->private->state == DEV_STATE_BOXED) {
+               ccw_device_done(cdev, DEV_STATE_BOXED);
+               return 0;
+       }
        if (ccw_device_is_orphan(cdev)) {
                ccw_device_done(cdev, DEV_STATE_OFFLINE);
                return 0;
        }
        sch = to_subchannel(cdev->dev.parent);
-       if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv)
+       if (cio_update_schib(sch))
                return -ENODEV;
        if (scsw_actl(&sch->schib.scsw) != 0)
                return -EBUSY;
        if (cdev->private->state != DEV_STATE_ONLINE)
                return -EINVAL;
        /* Are we doing path grouping? */
-       if (!cdev->private->options.pgroup) {
+       if (!cdev->private->flags.pgroup) {
                /* No, set state offline immediately. */
                ccw_device_done(cdev, DEV_STATE_OFFLINE);
                return 0;
@@ -666,45 +604,25 @@ ccw_device_offline(struct ccw_device *cdev)
 }
 
 /*
- * Handle timeout in device online/offline process.
- */
-static void
-ccw_device_onoff_timeout(struct ccw_device *cdev, enum dev_event dev_event)
-{
-       int ret;
-
-       ret = ccw_device_cancel_halt_clear(cdev);
-       switch (ret) {
-       case 0:
-               ccw_device_done(cdev, DEV_STATE_BOXED);
-               break;
-       case -ENODEV:
-               ccw_device_done(cdev, DEV_STATE_NOT_OPER);
-               break;
-       default:
-               ccw_device_set_timeout(cdev, 3*HZ);
-       }
-}
-
-/*
- * Handle not oper event in device recognition.
+ * Handle not operational event in non-special state.
  */
-static void
-ccw_device_recog_notoper(struct ccw_device *cdev, enum dev_event dev_event)
+static void ccw_device_generic_notoper(struct ccw_device *cdev,
+                                      enum dev_event dev_event)
 {
-       ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER);
+       if (ccw_device_notify(cdev, CIO_GONE) != NOTIFY_OK)
+               ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
+       else
+               ccw_device_set_disconnected(cdev);
 }
 
 /*
- * Handle not operational event in non-special state.
+ * Handle path verification event in offline state.
  */
-static void ccw_device_generic_notoper(struct ccw_device *cdev,
-                                      enum dev_event dev_event)
+static void ccw_device_offline_verify(struct ccw_device *cdev,
+                                     enum dev_event dev_event)
 {
-       struct subchannel *sch;
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
 
-       cdev->private->state = DEV_STATE_NOT_OPER;
-       sch = to_subchannel(cdev->dev.parent);
        css_schedule_eval(sch->schid);
 }
 
@@ -725,7 +643,10 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
         * Since we might not just be coming from an interrupt from the
         * subchannel we have to update the schib.
         */
-       stsch(sch->schid, &sch->schib);
+       if (cio_update_schib(sch)) {
+               ccw_device_verify_done(cdev, -ENODEV);
+               return;
+       }
 
        if (scsw_actl(&sch->schib.scsw) != 0 ||
            (scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_STATUS_PEND) ||
@@ -740,22 +661,40 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
        }
        /* Device is idle, we can do the path verification. */
        cdev->private->state = DEV_STATE_VERIFY;
-       cdev->private->flags.doverify = 0;
        ccw_device_verify_start(cdev);
 }
 
 /*
+ * Handle path verification event in boxed state.
+ */
+static void ccw_device_boxed_verify(struct ccw_device *cdev,
+                                   enum dev_event dev_event)
+{
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+
+       if (cdev->online) {
+               if (cio_enable_subchannel(sch, (u32) (addr_t) sch))
+                       ccw_device_done(cdev, DEV_STATE_NOT_OPER);
+               else
+                       ccw_device_online_verify(cdev, dev_event);
+       } else
+               css_schedule_eval(sch->schid);
+}
+
+/*
  * Got an interrupt for a normal io (state online).
  */
 static void
 ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event)
 {
        struct irb *irb;
+       int is_cmd;
 
-       irb = (struct irb *) __LC_IRB;
+       irb = (struct irb *)&S390_lowcore.irb;
+       is_cmd = !scsw_is_tm(&irb->scsw);
        /* Check for unsolicited interrupt. */
        if (!scsw_is_solicited(&irb->scsw)) {
-               if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
+               if (is_cmd && (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
                    !irb->esw.esw0.erw.cons) {
                        /* Unit check but no sense data. Need basic sense. */
                        if (ccw_device_do_sense(cdev, irb) != 0)
@@ -774,7 +713,7 @@ call_handler_unsol:
        }
        /* Accumulate status and find out if a basic sense is needed. */
        ccw_device_accumulate_irb(cdev, irb);
-       if (cdev->private->flags.dosense) {
+       if (is_cmd && cdev->private->flags.dosense) {
                if (ccw_device_do_sense(cdev, irb) == 0) {
                        cdev->private->state = DEV_STATE_W4SENSE;
                }
@@ -816,7 +755,7 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
 {
        struct irb *irb;
 
-       irb = (struct irb *) __LC_IRB;
+       irb = (struct irb *)&S390_lowcore.irb;
        /* Check for unsolicited interrupt. */
        if (scsw_stctl(&irb->scsw) ==
            (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
@@ -840,12 +779,6 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
         */
        if (scsw_fctl(&irb->scsw) &
            (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
-               /* Retry Basic Sense if requested. */
-               if (cdev->private->flags.intretry) {
-                       cdev->private->flags.intretry = 0;
-                       ccw_device_do_sense(cdev, irb);
-                       return;
-               }
                cdev->private->flags.dosense = 0;
                memset(&cdev->private->irb, 0, sizeof(struct irb));
                ccw_device_accumulate_irb(cdev, irb);
@@ -860,6 +793,8 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
        }
 call_handler:
        cdev->private->state = DEV_STATE_ONLINE;
+       /* In case sensing interfered with setting the device online */
+       wake_up(&cdev->private->wait_q);
        /* Call the handler. */
        if (ccw_device_call_handler(cdev) && cdev->private->flags.doverify)
                /* Start delayed path verification. */
@@ -867,21 +802,6 @@ call_handler:
 }
 
 static void
-ccw_device_clear_verify(struct ccw_device *cdev, enum dev_event dev_event)
-{
-       struct irb *irb;
-
-       irb = (struct irb *) __LC_IRB;
-       /* Accumulate status. We don't do basic sense. */
-       ccw_device_accumulate_irb(cdev, irb);
-       /* Remember to clear irb to avoid residuals. */
-       memset(&cdev->private->irb, 0, sizeof(struct irb));
-       /* Try to start delayed device verification. */
-       ccw_device_online_verify(cdev, 0);
-       /* Note: Don't call handler for cio initiated clear! */
-}
-
-static void
 ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event)
 {
        struct subchannel *sch;
@@ -938,32 +858,6 @@ ccw_device_delay_verify(struct ccw_device *cdev, enum dev_event dev_event)
 }
 
 static void
-ccw_device_stlck_done(struct ccw_device *cdev, enum dev_event dev_event)
-{
-       struct irb *irb;
-
-       switch (dev_event) {
-       case DEV_EVENT_INTERRUPT:
-               irb = (struct irb *) __LC_IRB;
-               /* Check for unsolicited interrupt. */
-               if ((scsw_stctl(&irb->scsw) ==
-                    (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) &&
-                   (!scsw_cc(&irb->scsw)))
-                       /* FIXME: we should restart stlck here, but this
-                        * is extremely unlikely ... */
-                       goto out_wakeup;
-
-               ccw_device_accumulate_irb(cdev, irb);
-               /* We don't care about basic sense etc. */
-               break;
-       default: /* timeout */
-               break;
-       }
-out_wakeup:
-       wake_up(&cdev->private->wait_q);
-}
-
-static void
 ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
 {
        struct subchannel *sch;
@@ -972,10 +866,6 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
        if (cio_enable_subchannel(sch, (u32)(addr_t)sch) != 0)
                /* Couldn't enable the subchannel for i/o. Sick device. */
                return;
-
-       /* After 60s the device recognition is considered to have failed. */
-       ccw_device_set_timeout(cdev, 60*HZ);
-
        cdev->private->state = DEV_STATE_DISCONNECTED_SENSE_ID;
        ccw_device_sense_id_start(cdev);
 }
@@ -989,39 +879,38 @@ void ccw_device_trigger_reprobe(struct ccw_device *cdev)
 
        sch = to_subchannel(cdev->dev.parent);
        /* Update some values. */
-       if (stsch(sch->schid, &sch->schib))
-               return;
-       if (!sch->schib.pmcw.dnv)
+       if (cio_update_schib(sch))
                return;
        /*
         * The pim, pam, pom values may not be accurate, but they are the best
         * we have before performing device selection :/
         */
        sch->lpm = sch->schib.pmcw.pam & sch->opm;
-       /* Re-set some bits in the pmcw that were lost. */
-       sch->schib.pmcw.csense = 1;
-       sch->schib.pmcw.ena = 0;
-       if ((sch->lpm & (sch->lpm - 1)) != 0)
-               sch->schib.pmcw.mp = 1;
+       /*
+        * Use the initial configuration since we can't be shure that the old
+        * paths are valid.
+        */
+       io_subchannel_init_config(sch);
+       if (cio_commit_config(sch))
+               return;
+
        /* We should also udate ssd info, but this has to wait. */
        /* Check if this is another device which appeared on the same sch. */
-       if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
-               PREPARE_WORK(&cdev->private->kick_work,
-                            ccw_device_move_to_orphanage);
-               queue_work(slow_path_wq, &cdev->private->kick_work);
-       } else
+       if (sch->schib.pmcw.dev != cdev->private->dev_id.devno)
+               css_schedule_eval(sch->schid);
+       else
                ccw_device_start_id(cdev, 0);
 }
 
-static void
-ccw_device_offline_irq(struct ccw_device *cdev, enum dev_event dev_event)
+static void ccw_device_disabled_irq(struct ccw_device *cdev,
+                                   enum dev_event dev_event)
 {
        struct subchannel *sch;
 
        sch = to_subchannel(cdev->dev.parent);
        /*
-        * An interrupt in state offline means a previous disable was not
-        * successful. Try again.
+        * An interrupt in a disabled state means a previous disable was not
+        * successful - should not happen, but we try to disable again.
         */
        cio_disable_subchannel(sch);
 }
@@ -1046,10 +935,7 @@ static void
 ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event)
 {
        ccw_device_set_timeout(cdev, 0);
-       if (dev_event == DEV_EVENT_NOTOPER)
-               cdev->private->state = DEV_STATE_NOT_OPER;
-       else
-               cdev->private->state = DEV_STATE_OFFLINE;
+       cdev->private->state = DEV_STATE_NOT_OPER;
        wake_up(&cdev->private->wait_q);
 }
 
@@ -1059,17 +945,11 @@ ccw_device_quiesce_timeout(struct ccw_device *cdev, enum dev_event dev_event)
        int ret;
 
        ret = ccw_device_cancel_halt_clear(cdev);
-       switch (ret) {
-       case 0:
-               cdev->private->state = DEV_STATE_OFFLINE;
-               wake_up(&cdev->private->wait_q);
-               break;
-       case -ENODEV:
+       if (ret == -EBUSY) {
+               ccw_device_set_timeout(cdev, HZ/10);
+       } else {
                cdev->private->state = DEV_STATE_NOT_OPER;
                wake_up(&cdev->private->wait_q);
-               break;
-       default:
-               ccw_device_set_timeout(cdev, HZ/10);
        }
 }
 
@@ -1083,50 +963,37 @@ ccw_device_nop(struct ccw_device *cdev, enum dev_event dev_event)
 }
 
 /*
- * Bug operation action. 
- */
-static void
-ccw_device_bug(struct ccw_device *cdev, enum dev_event dev_event)
-{
-       CIO_MSG_EVENT(0, "Internal state [%i][%i] not handled for device "
-                     "0.%x.%04x\n", cdev->private->state, dev_event,
-                     cdev->private->dev_id.ssid,
-                     cdev->private->dev_id.devno);
-       BUG();
-}
-
-/*
  * device statemachine
  */
 fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
        [DEV_STATE_NOT_OPER] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_nop,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_bug,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_disabled_irq,
                [DEV_EVENT_TIMEOUT]     = ccw_device_nop,
                [DEV_EVENT_VERIFY]      = ccw_device_nop,
        },
        [DEV_STATE_SENSE_PGID] = {
-               [DEV_EVENT_NOTOPER]     = ccw_device_generic_notoper,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_sense_pgid_irq,
-               [DEV_EVENT_TIMEOUT]     = ccw_device_onoff_timeout,
+               [DEV_EVENT_NOTOPER]     = ccw_device_request_event,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_request_event,
+               [DEV_EVENT_TIMEOUT]     = ccw_device_request_event,
                [DEV_EVENT_VERIFY]      = ccw_device_nop,
        },
        [DEV_STATE_SENSE_ID] = {
-               [DEV_EVENT_NOTOPER]     = ccw_device_recog_notoper,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_sense_id_irq,
-               [DEV_EVENT_TIMEOUT]     = ccw_device_recog_timeout,
+               [DEV_EVENT_NOTOPER]     = ccw_device_request_event,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_request_event,
+               [DEV_EVENT_TIMEOUT]     = ccw_device_request_event,
                [DEV_EVENT_VERIFY]      = ccw_device_nop,
        },
        [DEV_STATE_OFFLINE] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_generic_notoper,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_offline_irq,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_disabled_irq,
                [DEV_EVENT_TIMEOUT]     = ccw_device_nop,
-               [DEV_EVENT_VERIFY]      = ccw_device_nop,
+               [DEV_EVENT_VERIFY]      = ccw_device_offline_verify,
        },
        [DEV_STATE_VERIFY] = {
-               [DEV_EVENT_NOTOPER]     = ccw_device_generic_notoper,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_verify_irq,
-               [DEV_EVENT_TIMEOUT]     = ccw_device_onoff_timeout,
+               [DEV_EVENT_NOTOPER]     = ccw_device_request_event,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_request_event,
+               [DEV_EVENT_TIMEOUT]     = ccw_device_request_event,
                [DEV_EVENT_VERIFY]      = ccw_device_delay_verify,
        },
        [DEV_STATE_ONLINE] = {
@@ -1142,24 +1009,18 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
                [DEV_EVENT_VERIFY]      = ccw_device_online_verify,
        },
        [DEV_STATE_DISBAND_PGID] = {
-               [DEV_EVENT_NOTOPER]     = ccw_device_generic_notoper,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_disband_irq,
-               [DEV_EVENT_TIMEOUT]     = ccw_device_onoff_timeout,
+               [DEV_EVENT_NOTOPER]     = ccw_device_request_event,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_request_event,
+               [DEV_EVENT_TIMEOUT]     = ccw_device_request_event,
                [DEV_EVENT_VERIFY]      = ccw_device_nop,
        },
        [DEV_STATE_BOXED] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_generic_notoper,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_stlck_done,
-               [DEV_EVENT_TIMEOUT]     = ccw_device_stlck_done,
-               [DEV_EVENT_VERIFY]      = ccw_device_nop,
-       },
-       /* states to wait for i/o completion before doing something */
-       [DEV_STATE_CLEAR_VERIFY] = {
-               [DEV_EVENT_NOTOPER]     = ccw_device_generic_notoper,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_clear_verify,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_nop,
                [DEV_EVENT_TIMEOUT]     = ccw_device_nop,
-               [DEV_EVENT_VERIFY]      = ccw_device_nop,
+               [DEV_EVENT_VERIFY]      = ccw_device_boxed_verify,
        },
+       /* states to wait for i/o completion before doing something */
        [DEV_STATE_TIMEOUT_KILL] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_generic_notoper,
                [DEV_EVENT_INTERRUPT]   = ccw_device_killing_irq,
@@ -1176,13 +1037,13 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
        [DEV_STATE_DISCONNECTED] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_nop,
                [DEV_EVENT_INTERRUPT]   = ccw_device_start_id,
-               [DEV_EVENT_TIMEOUT]     = ccw_device_bug,
+               [DEV_EVENT_TIMEOUT]     = ccw_device_nop,
                [DEV_EVENT_VERIFY]      = ccw_device_start_id,
        },
        [DEV_STATE_DISCONNECTED_SENSE_ID] = {
-               [DEV_EVENT_NOTOPER]     = ccw_device_recog_notoper,
-               [DEV_EVENT_INTERRUPT]   = ccw_device_sense_id_irq,
-               [DEV_EVENT_TIMEOUT]     = ccw_device_recog_timeout,
+               [DEV_EVENT_NOTOPER]     = ccw_device_request_event,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_request_event,
+               [DEV_EVENT_TIMEOUT]     = ccw_device_request_event,
                [DEV_EVENT_VERIFY]      = ccw_device_nop,
        },
        [DEV_STATE_CMFCHANGE] = {
@@ -1197,6 +1058,12 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
                [DEV_EVENT_TIMEOUT]     = ccw_device_update_cmfblock,
                [DEV_EVENT_VERIFY]      = ccw_device_update_cmfblock,
        },
+       [DEV_STATE_STEAL_LOCK] = {
+               [DEV_EVENT_NOTOPER]     = ccw_device_request_event,
+               [DEV_EVENT_INTERRUPT]   = ccw_device_request_event,
+               [DEV_EVENT_TIMEOUT]     = ccw_device_request_event,
+               [DEV_EVENT_VERIFY]      = ccw_device_nop,
+       },
 };
 
 EXPORT_SYMBOL_GPL(ccw_device_set_timeout);