linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / s390 / cio / device_pgid.c
index 1693a10..85b1020 100644 (file)
@@ -9,6 +9,7 @@
  * Path Group ID functions.
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/init.h>
 
 #include "device.h"
 #include "ioasm.h"
 
-/*
- * Helper function called from interrupt context to decide whether an
- * operation should be tried again.
- */
-static int __ccw_device_should_retry(struct scsw *scsw)
-{
-       /* CC is only valid if start function bit is set. */
-       if ((scsw->fctl & SCSW_FCTL_START_FUNC) && scsw->cc == 1)
-               return 1;
-       /* No more activity. For sense and set PGID we stubbornly try again. */
-       if (!scsw->actl)
-               return 1;
-       return 0;
-}
-
 /*
  * Start Sense Path Group ID helper function. Used in ccw_device_recog
  * and ccw_device_sense_pgid.
@@ -48,17 +34,12 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
        struct subchannel *sch;
        struct ccw1 *ccw;
        int ret;
-       int i;
 
        sch = to_subchannel(cdev->dev.parent);
-       /* Return if we already checked on all paths. */
-       if (cdev->private->imask == 0)
-               return (sch->lpm == 0) ? -ENODEV : -EACCES;
-       i = 8 - ffs(cdev->private->imask);
-
        /* Setup sense path group id channel program. */
        ccw = cdev->private->iccws;
        ccw->cmd_code = CCW_CMD_SENSE_PGID;
+       ccw->cda = (__u32) __pa (&cdev->private->pgid);
        ccw->count = sizeof (struct pgid);
        ccw->flags = CCW_FLAG_SLI;
 
@@ -68,7 +49,6 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
        ret = -ENODEV;
        while (cdev->private->imask != 0) {
                /* Try every path multiple times. */
-               ccw->cda = (__u32) __pa (&cdev->private->pgid[i]);
                if (cdev->private->iretry > 0) {
                        cdev->private->iretry--;
                        ret = cio_start (sch, cdev->private->iccws, 
@@ -85,9 +65,7 @@ __ccw_device_sense_pgid_start(struct ccw_device *cdev)
                }
                cdev->private->imask >>= 1;
                cdev->private->iretry = 5;
-               i++;
        }
-
        return ret;
 }
 
@@ -99,7 +77,7 @@ ccw_device_sense_pgid_start(struct ccw_device *cdev)
        cdev->private->state = DEV_STATE_SENSE_PGID;
        cdev->private->imask = 0x80;
        cdev->private->iretry = 5;
-       memset (&cdev->private->pgid, 0, sizeof (cdev->private->pgid));
+       memset (&cdev->private->pgid, 0, sizeof (struct pgid));
        ret = __ccw_device_sense_pgid_start(cdev);
        if (ret && ret != -EBUSY)
                ccw_device_sense_pgid_done(cdev, ret);
@@ -114,7 +92,6 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
 {
        struct subchannel *sch;
        struct irb *irb;
-       int i;
 
        sch = to_subchannel(cdev->dev.parent);
        irb = &cdev->private->irb;
@@ -148,8 +125,7 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
                              sch->schid.sch_no, sch->orb.lpm);
                return -EACCES;
        }
-       i = 8 - ffs(cdev->private->imask);
-       if (cdev->private->pgid[i].inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
+       if (cdev->private->pgid.inf.ps.state2 == SNID_STATE2_RESVD_ELSE) {
                CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x "
                              "is reserved by someone else\n",
                              cdev->private->devno, sch->schid.ssid,
@@ -170,10 +146,10 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
        int ret;
 
        irb = (struct irb *) __LC_IRB;
-
+       /* Retry sense pgid for cc=1. */
        if (irb->scsw.stctl ==
            (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
-               if (__ccw_device_should_retry(&irb->scsw)) {
+               if (irb->scsw.cc == 1) {
                        ret = __ccw_device_sense_pgid_start(cdev);
                        if (ret && ret != -EBUSY)
                                ccw_device_sense_pgid_done(cdev, ret);
@@ -187,6 +163,12 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
        memset(&cdev->private->irb, 0, sizeof(struct irb));
        switch (ret) {
        /* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */
+       case 0:                 /* Sense Path Group ID successful. */
+               if (cdev->private->pgid.inf.ps.state1 == SNID_STATE1_RESET)
+                       memcpy(&cdev->private->pgid, &css[0]->global_pgid,
+                              sizeof(struct pgid));
+               ccw_device_sense_pgid_done(cdev, 0);
+               break;
        case -EOPNOTSUPP:       /* Sense Path Group ID not supported */
                ccw_device_sense_pgid_done(cdev, -EOPNOTSUPP);
                break;
@@ -195,15 +177,13 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
                break;
        case -EACCES:           /* channel is not operational. */
                sch->lpm &= ~cdev->private->imask;
-               /* Fall through. */
-       case 0:                 /* Sense Path Group ID successful. */
                cdev->private->imask >>= 1;
                cdev->private->iretry = 5;
                /* Fall through. */
        case -EAGAIN:           /* Try again. */
                ret = __ccw_device_sense_pgid_start(cdev);
                if (ret != 0 && ret != -EBUSY)
-                       ccw_device_sense_pgid_done(cdev, ret);
+                       ccw_device_sense_pgid_done(cdev, -ENODEV);
                break;
        case -EUSERS:           /* device is reserved for someone else. */
                ccw_device_sense_pgid_done(cdev, -EUSERS);
@@ -224,20 +204,20 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
        sch = to_subchannel(cdev->dev.parent);
 
        /* Setup sense path group id channel program. */
-       cdev->private->pgid[0].inf.fc = func;
+       cdev->private->pgid.inf.fc = func;
        ccw = cdev->private->iccws;
        if (!cdev->private->flags.pgid_single) {
-               cdev->private->pgid[0].inf.fc |= SPID_FUNC_MULTI_PATH;
+               cdev->private->pgid.inf.fc |= SPID_FUNC_MULTI_PATH;
                ccw->cmd_code = CCW_CMD_SUSPEND_RECONN;
                ccw->cda = 0;
                ccw->count = 0;
                ccw->flags = CCW_FLAG_SLI | CCW_FLAG_CC;
                ccw++;
        } else
-               cdev->private->pgid[0].inf.fc |= SPID_FUNC_SINGLE_PATH;
+               cdev->private->pgid.inf.fc |= SPID_FUNC_SINGLE_PATH;
 
        ccw->cmd_code = CCW_CMD_SET_PGID;
-       ccw->cda = (__u32) __pa (&cdev->private->pgid[0]);
+       ccw->cda = (__u32) __pa (&cdev->private->pgid);
        ccw->count = sizeof (struct pgid);
        ccw->flags = CCW_FLAG_SLI;
 
@@ -264,48 +244,6 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
        return ret;
 }
 
-/*
- * Helper function to send a nop ccw down a path.
- */
-static int __ccw_device_do_nop(struct ccw_device *cdev)
-{
-       struct subchannel *sch;
-       struct ccw1 *ccw;
-       int ret;
-
-       sch = to_subchannel(cdev->dev.parent);
-
-       /* Setup nop channel program. */
-       ccw = cdev->private->iccws;
-       ccw->cmd_code = CCW_CMD_NOOP;
-       ccw->cda = 0;
-       ccw->count = 0;
-       ccw->flags = CCW_FLAG_SLI;
-
-       /* Reset device status. */
-       memset(&cdev->private->irb, 0, sizeof(struct irb));
-
-       /* Try multiple times. */
-       ret = -ENODEV;
-       if (cdev->private->iretry > 0) {
-               cdev->private->iretry--;
-               ret = cio_start (sch, cdev->private->iccws,
-                                cdev->private->imask);
-               /* ret is 0, -EBUSY, -EACCES or -ENODEV */
-               if ((ret != -EACCES) && (ret != -ENODEV))
-                       return ret;
-       }
-       /* nop command failed on this path. Switch it off. */
-       sch->lpm &= ~cdev->private->imask;
-       sch->vpm &= ~cdev->private->imask;
-       CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel "
-                     "0.%x.%04x, lpm %02X, became 'not operational'\n",
-                     cdev->private->devno, sch->schid.ssid,
-                     sch->schid.sch_no, cdev->private->imask);
-       return ret;
-}
-
-
 /*
  * Called from interrupt context to check if a valid answer
  * to Set Path Group ID was received.
@@ -345,29 +283,6 @@ __ccw_device_check_pgid(struct ccw_device *cdev)
        return 0;
 }
 
-/*
- * Called from interrupt context to check the path status after a nop has
- * been send.
- */
-static int __ccw_device_check_nop(struct ccw_device *cdev)
-{
-       struct subchannel *sch;
-       struct irb *irb;
-
-       sch = to_subchannel(cdev->dev.parent);
-       irb = &cdev->private->irb;
-       if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
-               return -ETIME;
-       if (irb->scsw.cc == 3) {
-               CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel 0.%x.%04x,"
-                             " lpm %02X, became 'not operational'\n",
-                             cdev->private->devno, sch->schid.ssid,
-                             sch->schid.sch_no, cdev->private->imask);
-               return -EACCES;
-       }
-       return 0;
-}
-
 static void
 __ccw_device_verify_start(struct ccw_device *cdev)
 {
@@ -382,12 +297,9 @@ __ccw_device_verify_start(struct ccw_device *cdev)
                        if ((sch->vpm & imask) != (sch->lpm & imask))
                                break;
                cdev->private->imask = imask;
-               if (cdev->private->options.pgroup) {
-                       func = (sch->vpm & imask) ?
-                               SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH;
-                       ret = __ccw_device_do_pgid(cdev, func);
-               } else
-                       ret = __ccw_device_do_nop(cdev);
+               func = (sch->vpm & imask) ?
+                       SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH;
+               ret = __ccw_device_do_pgid(cdev, func);
                if (ret == 0 || ret == -EBUSY)
                        return;
                cdev->private->iretry = 5;
@@ -406,20 +318,17 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
        int ret;
 
        irb = (struct irb *) __LC_IRB;
-
+       /* Retry set pgid for cc=1. */
        if (irb->scsw.stctl ==
            (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
-               if (__ccw_device_should_retry(&irb->scsw))
+               if (irb->scsw.cc == 1)
                        __ccw_device_verify_start(cdev);
                return;
        }
        if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
                return;
        sch = to_subchannel(cdev->dev.parent);
-       if (cdev->private->options.pgroup)
-               ret = __ccw_device_check_pgid(cdev);
-       else
-               ret = __ccw_device_check_nop(cdev);
+       ret = __ccw_device_check_pgid(cdev);
        memset(&cdev->private->irb, 0, sizeof(struct irb));
        switch (ret) {
        /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
@@ -437,10 +346,11 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
                 * One of those strange devices which claim to be able
                 * to do multipathing but not for Set Path Group ID.
                 */
-               if (cdev->private->flags.pgid_single)
-                       cdev->private->options.pgroup = 0;
-               else
-                       cdev->private->flags.pgid_single = 1;
+               if (cdev->private->flags.pgid_single) {
+                       ccw_device_verify_done(cdev, -EOPNOTSUPP);
+                       break;
+               }
+               cdev->private->flags.pgid_single = 1;
                /* fall through. */
        case -EAGAIN:           /* Try again. */
                __ccw_device_verify_start(cdev);
@@ -509,10 +419,10 @@ ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
        int ret;
 
        irb = (struct irb *) __LC_IRB;
-
+       /* Retry set pgid for cc=1. */
        if (irb->scsw.stctl ==
            (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
-               if (__ccw_device_should_retry(&irb->scsw))
+               if (irb->scsw.cc == 1)
                        __ccw_device_disband_start(cdev);
                return;
        }