Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
[pandora-kernel.git] / drivers / s390 / cio / device_fsm.c
index 8867443..4b92c84 100644 (file)
 #include "ioasm.h"
 #include "chp.h"
 
+static int timeout_log_enabled;
+
 int
 device_is_online(struct subchannel *sch)
 {
        struct ccw_device *cdev;
 
-       if (!sch->dev.driver_data)
+       cdev = sch_get_cdev(sch);
+       if (!cdev)
                return 0;
-       cdev = sch->dev.driver_data;
        return (cdev->private->state == DEV_STATE_ONLINE);
 }
 
@@ -41,9 +43,9 @@ device_is_disconnected(struct subchannel *sch)
 {
        struct ccw_device *cdev;
 
-       if (!sch->dev.driver_data)
+       cdev = sch_get_cdev(sch);
+       if (!cdev)
                return 0;
-       cdev = sch->dev.driver_data;
        return (cdev->private->state == DEV_STATE_DISCONNECTED ||
                cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID);
 }
@@ -53,19 +55,21 @@ device_set_disconnected(struct subchannel *sch)
 {
        struct ccw_device *cdev;
 
-       if (!sch->dev.driver_data)
+       cdev = sch_get_cdev(sch);
+       if (!cdev)
                return;
-       cdev = sch->dev.driver_data;
        ccw_device_set_timeout(cdev, 0);
        cdev->private->flags.fake_irb = 0;
        cdev->private->state = DEV_STATE_DISCONNECTED;
+       if (cdev->online)
+               ccw_device_schedule_recovery();
 }
 
 void device_set_intretry(struct subchannel *sch)
 {
        struct ccw_device *cdev;
 
-       cdev = sch->dev.driver_data;
+       cdev = sch_get_cdev(sch);
        if (!cdev)
                return;
        cdev->private->flags.intretry = 1;
@@ -75,13 +79,62 @@ int device_trigger_verify(struct subchannel *sch)
 {
        struct ccw_device *cdev;
 
-       cdev = sch->dev.driver_data;
+       cdev = sch_get_cdev(sch);
        if (!cdev || !cdev->online)
                return -EINVAL;
        dev_fsm_event(cdev, DEV_EVENT_VERIFY);
        return 0;
 }
 
+static int __init ccw_timeout_log_setup(char *unused)
+{
+       timeout_log_enabled = 1;
+       return 1;
+}
+
+__setup("ccw_timeout_log", ccw_timeout_log_setup);
+
+static void ccw_timeout_log(struct ccw_device *cdev)
+{
+       struct schib schib;
+       struct subchannel *sch;
+       struct io_subchannel_private *private;
+       int cc;
+
+       sch = to_subchannel(cdev->dev.parent);
+       private = to_io_private(sch);
+       cc = stsch(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);
+       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);
+       printk(KERN_WARNING "cio: ccw device state: %d\n",
+              cdev->private->state);
+       printk(KERN_WARNING "cio: store subchannel returned: cc=%d\n", cc);
+       printk(KERN_WARNING "cio: schib:\n");
+       print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
+                      &schib, sizeof(schib), 0);
+       printk(KERN_WARNING "cio: ccw device flags:\n");
+       print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
+                      &cdev->private->flags, sizeof(cdev->private->flags), 0);
+}
+
 /*
  * Timeout function. It just triggers a DEV_EVENT_TIMEOUT.
  */
@@ -92,6 +145,8 @@ ccw_device_timeout(unsigned long data)
 
        cdev = (struct ccw_device *) data;
        spin_lock_irq(cdev->ccwlock);
+       if (timeout_log_enabled)
+               ccw_timeout_log(cdev);
        dev_fsm_event(cdev, DEV_EVENT_TIMEOUT);
        spin_unlock_irq(cdev->ccwlock);
 }
@@ -122,9 +177,9 @@ device_kill_pending_timer(struct subchannel *sch)
 {
        struct ccw_device *cdev;
 
-       if (!sch->dev.driver_data)
+       cdev = sch_get_cdev(sch);
+       if (!cdev)
                return;
-       cdev = sch->dev.driver_data;
        ccw_device_set_timeout(cdev, 0);
 }
 
@@ -268,7 +323,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
        switch (state) {
        case DEV_STATE_NOT_OPER:
                CIO_DEBUG(KERN_WARNING, 2,
-                         "cio: SenseID : unknown device %04x on subchannel "
+                         "SenseID : unknown device %04x on subchannel "
                          "0.%x.%04x\n", cdev->private->dev_id.devno,
                          sch->schid.ssid, sch->schid.sch_no);
                break;
@@ -294,7 +349,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
                }
                /* Issue device info message. */
                CIO_DEBUG(KERN_INFO, 2,
-                         "cio: SenseID : device 0.%x.%04x reports: "
+                         "SenseID : device 0.%x.%04x reports: "
                          "CU  Type/Mod = %04X/%02X, Dev Type/Mod = "
                          "%04X/%02X\n",
                          cdev->private->dev_id.ssid,
@@ -304,7 +359,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
                break;
        case DEV_STATE_BOXED:
                CIO_DEBUG(KERN_WARNING, 2,
-                         "cio: SenseID : boxed device %04x on subchannel "
+                         "SenseID : boxed device %04x on subchannel "
                          "0.%x.%04x\n", cdev->private->dev_id.devno,
                          sch->schid.ssid, sch->schid.sch_no);
                break;
@@ -349,7 +404,7 @@ ccw_device_oper_notify(struct work_struct *work)
        sch = to_subchannel(cdev->dev.parent);
        if (sch->driver && sch->driver->notify) {
                spin_unlock_irqrestore(cdev->ccwlock, flags);
-               ret = sch->driver->notify(&sch->dev, CIO_OPER);
+               ret = sch->driver->notify(sch, CIO_OPER);
                spin_lock_irqsave(cdev->ccwlock, flags);
        } else
                ret = 0;
@@ -389,7 +444,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
 
        if (state == DEV_STATE_BOXED)
                CIO_DEBUG(KERN_WARNING, 2,
-                         "cio: Boxed device %04x on subchannel %04x\n",
+                         "Boxed device %04x on subchannel %04x\n",
                          cdev->private->dev_id.devno, sch->schid.sch_no);
 
        if (cdev->private->flags.donotify) {
@@ -500,7 +555,8 @@ ccw_device_recognition(struct ccw_device *cdev)
            (cdev->private->state != DEV_STATE_BOXED))
                return -EINVAL;
        sch = to_subchannel(cdev->dev.parent);
-       ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc);
+       ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc,
+                                   (u32)(addr_t)sch);
        if (ret != 0)
                /* Couldn't enable the subchannel for i/o. Sick device. */
                return ret;
@@ -587,9 +643,10 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
        default:
                /* Reset oper notify indication after verify error. */
                cdev->private->flags.donotify = 0;
-               if (cdev->online)
+               if (cdev->online) {
+                       ccw_device_set_timeout(cdev, 0);
                        dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
-               else
+               else
                        ccw_device_done(cdev, DEV_STATE_NOT_OPER);
                break;
        }
@@ -610,7 +667,8 @@ ccw_device_online(struct ccw_device *cdev)
        sch = to_subchannel(cdev->dev.parent);
        if (css_init_done && !get_device(&cdev->dev))
                return -ENODEV;
-       ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc);
+       ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc,
+                                   (u32)(addr_t)sch);
        if (ret != 0) {
                /* Couldn't enable the subchannel for i/o. Sick device. */
                if (ret == -ENODEV)
@@ -937,7 +995,7 @@ void device_kill_io(struct subchannel *sch)
        int ret;
        struct ccw_device *cdev;
 
-       cdev = sch->dev.driver_data;
+       cdev = sch_get_cdev(sch);
        ret = ccw_device_cancel_halt_clear(cdev);
        if (ret == -EBUSY) {
                ccw_device_set_timeout(cdev, 3*HZ);
@@ -990,7 +1048,8 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
        struct subchannel *sch;
 
        sch = to_subchannel(cdev->dev.parent);
-       if (cio_enable_subchannel(sch, sch->schib.pmcw.isc) != 0)
+       if (cio_enable_subchannel(sch, sch->schib.pmcw.isc,
+                                 (u32)(addr_t)sch) != 0)
                /* Couldn't enable the subchannel for i/o. Sick device. */
                return;
 
@@ -1006,9 +1065,9 @@ device_trigger_reprobe(struct subchannel *sch)
 {
        struct ccw_device *cdev;
 
-       if (!sch->dev.driver_data)
+       cdev = sch_get_cdev(sch);
+       if (!cdev)
                return;
-       cdev = sch->dev.driver_data;
        if (cdev->private->state != DEV_STATE_DISCONNECTED)
                return;
 
@@ -1028,13 +1087,13 @@ device_trigger_reprobe(struct subchannel *sch)
        sch->schib.pmcw.ena = 0;
        if ((sch->lpm & (sch->lpm - 1)) != 0)
                sch->schib.pmcw.mp = 1;
-       sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
+       sch->schib.pmcw.intparm = (u32)(addr_t)sch;
        /* 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(ccw_device_work, &cdev->private->kick_work);
+               queue_work(slow_path_wq, &cdev->private->kick_work);
        } else
                ccw_device_start_id(cdev, 0);
 }
@@ -1223,21 +1282,4 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
        },
 };
 
-/*
- * io_subchannel_irq is called for "real" interrupts or for status
- * pending conditions on msch.
- */
-void
-io_subchannel_irq (struct device *pdev)
-{
-       struct ccw_device *cdev;
-
-       cdev = to_subchannel(pdev)->dev.driver_data;
-
-       CIO_TRACE_EVENT (3, "IRQ");
-       CIO_TRACE_EVENT (3, pdev->bus_id);
-       if (cdev)
-               dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
-}
-
 EXPORT_SYMBOL_GPL(ccw_device_set_timeout);