[S390] cio: notify drivers of channel path events
authorSebastian Ott <sebott@linux.vnet.ibm.com>
Mon, 25 Oct 2010 14:10:34 +0000 (16:10 +0200)
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>
Mon, 25 Oct 2010 14:10:19 +0000 (16:10 +0200)
This patch adds a notification mechanism to inform ccw drivers
about changes to channel paths, which occured while the device
is online.

Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/ccwdev.h
drivers/s390/cio/device.c
drivers/s390/cio/device_fsm.c
drivers/s390/cio/device_pgid.c
drivers/s390/cio/io_sch.h

index f3ba0fa..e850111 100644 (file)
@@ -91,6 +91,16 @@ struct ccw_device {
        void (*handler) (struct ccw_device *, unsigned long, struct irb *);
 };
 
+/*
+ * Possible events used by the path_event notifier.
+ */
+#define PE_NONE                                0x0
+#define PE_PATH_GONE                   0x1 /* A path is no longer available. */
+#define PE_PATH_AVAILABLE              0x2 /* A path has become available and
+                                              was successfully verified. */
+#define PE_PATHGROUP_ESTABLISHED       0x4 /* A pathgroup was reset and had
+                                              to be established again. */
+
 /*
  * Possible CIO actions triggered by the unit check handler.
  */
@@ -109,6 +119,7 @@ enum uc_todo {
  * @set_online: called when setting device online
  * @set_offline: called when setting device offline
  * @notify: notify driver of device state changes
+ * @path_event: notify driver of channel path events
  * @shutdown: called at device shutdown
  * @prepare: prepare for pm state transition
  * @complete: undo work done in @prepare
@@ -127,6 +138,7 @@ struct ccw_driver {
        int (*set_online) (struct ccw_device *);
        int (*set_offline) (struct ccw_device *);
        int (*notify) (struct ccw_device *, int);
+       void (*path_event) (struct ccw_device *, int *);
        void (*shutdown) (struct ccw_device *);
        int (*prepare) (struct ccw_device *);
        void (*complete) (struct ccw_device *);
index 07b1a07..881bdfd 100644 (file)
@@ -1147,6 +1147,7 @@ err:
 static int io_subchannel_chp_event(struct subchannel *sch,
                                   struct chp_link *link, int event)
 {
+       struct ccw_device *cdev = sch_get_cdev(sch);
        int mask;
 
        mask = chp_ssd_get_mask(&sch->ssd_info, link);
@@ -1156,22 +1157,30 @@ static int io_subchannel_chp_event(struct subchannel *sch,
        case CHP_VARY_OFF:
                sch->opm &= ~mask;
                sch->lpm &= ~mask;
+               if (cdev)
+                       cdev->private->path_gone_mask |= mask;
                io_subchannel_terminate_path(sch, mask);
                break;
        case CHP_VARY_ON:
                sch->opm |= mask;
                sch->lpm |= mask;
+               if (cdev)
+                       cdev->private->path_new_mask |= mask;
                io_subchannel_verify(sch);
                break;
        case CHP_OFFLINE:
                if (cio_update_schib(sch))
                        return -ENODEV;
+               if (cdev)
+                       cdev->private->path_gone_mask |= mask;
                io_subchannel_terminate_path(sch, mask);
                break;
        case CHP_ONLINE:
                if (cio_update_schib(sch))
                        return -ENODEV;
                sch->lpm |= mask & sch->opm;
+               if (cdev)
+                       cdev->private->path_new_mask |= mask;
                io_subchannel_verify(sch);
                break;
        }
index c9b8526..4395c01 100644 (file)
@@ -349,9 +349,13 @@ out:
 
 static void ccw_device_oper_notify(struct ccw_device *cdev)
 {
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+
        if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_OK) {
                /* Reenable channel measurements, if needed. */
                ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF);
+               /* Save indication for new paths. */
+               cdev->private->path_new_mask = sch->vpm;
                return;
        }
        /* Driver doesn't want device back. */
@@ -462,6 +466,32 @@ static void ccw_device_request_event(struct ccw_device *cdev, enum dev_event e)
        }
 }
 
+static void ccw_device_report_path_events(struct ccw_device *cdev)
+{
+       struct subchannel *sch = to_subchannel(cdev->dev.parent);
+       int path_event[8];
+       int chp, mask;
+
+       for (chp = 0, mask = 0x80; chp < 8; chp++, mask >>= 1) {
+               path_event[chp] = PE_NONE;
+               if (mask & cdev->private->path_gone_mask & ~(sch->vpm))
+                       path_event[chp] |= PE_PATH_GONE;
+               if (mask & cdev->private->path_new_mask & sch->vpm)
+                       path_event[chp] |= PE_PATH_AVAILABLE;
+               if (mask & cdev->private->pgid_reset_mask & sch->vpm)
+                       path_event[chp] |= PE_PATHGROUP_ESTABLISHED;
+       }
+       if (cdev->online && cdev->drv->path_event)
+               cdev->drv->path_event(cdev, path_event);
+}
+
+static void ccw_device_reset_path_events(struct ccw_device *cdev)
+{
+       cdev->private->path_gone_mask = 0;
+       cdev->private->path_new_mask = 0;
+       cdev->private->pgid_reset_mask = 0;
+}
+
 void
 ccw_device_verify_done(struct ccw_device *cdev, int err)
 {
@@ -498,6 +528,7 @@ callback:
                                              &cdev->private->irb);
                        memset(&cdev->private->irb, 0, sizeof(struct irb));
                }
+               ccw_device_report_path_events(cdev);
                break;
        case -ETIME:
        case -EUSERS:
@@ -516,6 +547,7 @@ callback:
                ccw_device_done(cdev, DEV_STATE_NOT_OPER);
                break;
        }
+       ccw_device_reset_path_events(cdev);
 }
 
 /*
index 82a5ad0..07a4fd2 100644 (file)
@@ -213,6 +213,17 @@ static void spid_start(struct ccw_device *cdev)
        spid_do(cdev);
 }
 
+static int pgid_is_reset(struct pgid *p)
+{
+       char *c;
+
+       for (c = (char *)p + 1; c < (char *)(p + 1); c++) {
+               if (*c != 0)
+                       return 0;
+       }
+       return 1;
+}
+
 static int pgid_cmp(struct pgid *p1, struct pgid *p2)
 {
        return memcmp((char *) p1 + 1, (char *) p2 + 1,
@@ -223,7 +234,7 @@ static int pgid_cmp(struct pgid *p1, struct pgid *p2)
  * Determine pathgroup state from PGID data.
  */
 static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
-                        int *mismatch, int *reserved, int *reset)
+                        int *mismatch, int *reserved, u8 *reset)
 {
        struct pgid *pgid = &cdev->private->pgid[0];
        struct pgid *first = NULL;
@@ -238,9 +249,8 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
                        continue;
                if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE)
                        *reserved = 1;
-               if (pgid->inf.ps.state1 == SNID_STATE1_RESET) {
-                       /* A PGID was reset. */
-                       *reset = 1;
+               if (pgid_is_reset(pgid)) {
+                       *reset |= lpm;
                        continue;
                }
                if (!first) {
@@ -307,7 +317,7 @@ static void snid_done(struct ccw_device *cdev, int rc)
        struct pgid *pgid;
        int mismatch = 0;
        int reserved = 0;
-       int reset = 0;
+       u8 reset = 0;
        u8 donepm;
 
        if (rc)
@@ -321,11 +331,12 @@ static void snid_done(struct ccw_device *cdev, int rc)
                donepm = pgid_to_donepm(cdev);
                sch->vpm = donepm & sch->opm;
                cdev->private->pgid_todo_mask &= ~donepm;
+               cdev->private->pgid_reset_mask |= reset;
                pgid_fill(cdev, pgid);
        }
 out:
        CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x "
-                     "todo=%02x mism=%d rsvd=%d reset=%d\n", id->ssid,
+                     "todo=%02x mism=%d rsvd=%d reset=%02x\n", id->ssid,
                      id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm,
                      cdev->private->pgid_todo_mask, mismatch, reserved, reset);
        switch (rc) {
index 469ef93..d024d2c 100644 (file)
@@ -151,8 +151,11 @@ struct ccw_device_private {
        struct subchannel_id schid;     /* subchannel number */
        struct ccw_request req;         /* internal I/O request */
        int iretry;
-       u8 pgid_valid_mask;             /* mask of valid PGIDs */
-       u8 pgid_todo_mask;              /* mask of PGIDs to be adjusted */
+       u8 pgid_valid_mask;     /* mask of valid PGIDs */
+       u8 pgid_todo_mask;      /* mask of PGIDs to be adjusted */
+       u8 pgid_reset_mask;     /* mask of PGIDs which were reset */
+       u8 path_gone_mask;      /* mask of paths, that became unavailable */
+       u8 path_new_mask;       /* mask of paths, that became available */
        struct {
                unsigned int fast:1;    /* post with "channel end" */
                unsigned int repall:1;  /* report every interrupt status */