Commit 585b954e authored by Sebastian Ott's avatar Sebastian Ott Committed by Martin Schwidefsky

[S390] cio: notify drivers of channel path events

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: default avatarSebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent eb4f5d93
...@@ -91,6 +91,16 @@ struct ccw_device { ...@@ -91,6 +91,16 @@ struct ccw_device {
void (*handler) (struct ccw_device *, unsigned long, struct irb *); 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. * Possible CIO actions triggered by the unit check handler.
*/ */
...@@ -109,6 +119,7 @@ enum uc_todo { ...@@ -109,6 +119,7 @@ enum uc_todo {
* @set_online: called when setting device online * @set_online: called when setting device online
* @set_offline: called when setting device offline * @set_offline: called when setting device offline
* @notify: notify driver of device state changes * @notify: notify driver of device state changes
* @path_event: notify driver of channel path events
* @shutdown: called at device shutdown * @shutdown: called at device shutdown
* @prepare: prepare for pm state transition * @prepare: prepare for pm state transition
* @complete: undo work done in @prepare * @complete: undo work done in @prepare
...@@ -127,6 +138,7 @@ struct ccw_driver { ...@@ -127,6 +138,7 @@ struct ccw_driver {
int (*set_online) (struct ccw_device *); int (*set_online) (struct ccw_device *);
int (*set_offline) (struct ccw_device *); int (*set_offline) (struct ccw_device *);
int (*notify) (struct ccw_device *, int); int (*notify) (struct ccw_device *, int);
void (*path_event) (struct ccw_device *, int *);
void (*shutdown) (struct ccw_device *); void (*shutdown) (struct ccw_device *);
int (*prepare) (struct ccw_device *); int (*prepare) (struct ccw_device *);
void (*complete) (struct ccw_device *); void (*complete) (struct ccw_device *);
......
...@@ -1147,6 +1147,7 @@ static void io_subchannel_terminate_path(struct subchannel *sch, u8 mask) ...@@ -1147,6 +1147,7 @@ static void io_subchannel_terminate_path(struct subchannel *sch, u8 mask)
static int io_subchannel_chp_event(struct subchannel *sch, static int io_subchannel_chp_event(struct subchannel *sch,
struct chp_link *link, int event) struct chp_link *link, int event)
{ {
struct ccw_device *cdev = sch_get_cdev(sch);
int mask; int mask;
mask = chp_ssd_get_mask(&sch->ssd_info, link); mask = chp_ssd_get_mask(&sch->ssd_info, link);
...@@ -1156,22 +1157,30 @@ static int io_subchannel_chp_event(struct subchannel *sch, ...@@ -1156,22 +1157,30 @@ static int io_subchannel_chp_event(struct subchannel *sch,
case CHP_VARY_OFF: case CHP_VARY_OFF:
sch->opm &= ~mask; sch->opm &= ~mask;
sch->lpm &= ~mask; sch->lpm &= ~mask;
if (cdev)
cdev->private->path_gone_mask |= mask;
io_subchannel_terminate_path(sch, mask); io_subchannel_terminate_path(sch, mask);
break; break;
case CHP_VARY_ON: case CHP_VARY_ON:
sch->opm |= mask; sch->opm |= mask;
sch->lpm |= mask; sch->lpm |= mask;
if (cdev)
cdev->private->path_new_mask |= mask;
io_subchannel_verify(sch); io_subchannel_verify(sch);
break; break;
case CHP_OFFLINE: case CHP_OFFLINE:
if (cio_update_schib(sch)) if (cio_update_schib(sch))
return -ENODEV; return -ENODEV;
if (cdev)
cdev->private->path_gone_mask |= mask;
io_subchannel_terminate_path(sch, mask); io_subchannel_terminate_path(sch, mask);
break; break;
case CHP_ONLINE: case CHP_ONLINE:
if (cio_update_schib(sch)) if (cio_update_schib(sch))
return -ENODEV; return -ENODEV;
sch->lpm |= mask & sch->opm; sch->lpm |= mask & sch->opm;
if (cdev)
cdev->private->path_new_mask |= mask;
io_subchannel_verify(sch); io_subchannel_verify(sch);
break; break;
} }
......
...@@ -349,9 +349,13 @@ int ccw_device_notify(struct ccw_device *cdev, int event) ...@@ -349,9 +349,13 @@ int ccw_device_notify(struct ccw_device *cdev, int event)
static void ccw_device_oper_notify(struct ccw_device *cdev) 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) { if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_OK) {
/* Reenable channel measurements, if needed. */ /* Reenable channel measurements, if needed. */
ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF); ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF);
/* Save indication for new paths. */
cdev->private->path_new_mask = sch->vpm;
return; return;
} }
/* Driver doesn't want device back. */ /* Driver doesn't want device back. */
...@@ -462,6 +466,32 @@ static void ccw_device_request_event(struct ccw_device *cdev, enum dev_event e) ...@@ -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 void
ccw_device_verify_done(struct ccw_device *cdev, int err) ccw_device_verify_done(struct ccw_device *cdev, int err)
{ {
...@@ -498,6 +528,7 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) ...@@ -498,6 +528,7 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
&cdev->private->irb); &cdev->private->irb);
memset(&cdev->private->irb, 0, sizeof(struct irb)); memset(&cdev->private->irb, 0, sizeof(struct irb));
} }
ccw_device_report_path_events(cdev);
break; break;
case -ETIME: case -ETIME:
case -EUSERS: case -EUSERS:
...@@ -516,6 +547,7 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) ...@@ -516,6 +547,7 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
ccw_device_done(cdev, DEV_STATE_NOT_OPER); ccw_device_done(cdev, DEV_STATE_NOT_OPER);
break; break;
} }
ccw_device_reset_path_events(cdev);
} }
/* /*
......
...@@ -213,6 +213,17 @@ static void spid_start(struct ccw_device *cdev) ...@@ -213,6 +213,17 @@ static void spid_start(struct ccw_device *cdev)
spid_do(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) static int pgid_cmp(struct pgid *p1, struct pgid *p2)
{ {
return memcmp((char *) p1 + 1, (char *) p2 + 1, return memcmp((char *) p1 + 1, (char *) p2 + 1,
...@@ -223,7 +234,7 @@ static int pgid_cmp(struct pgid *p1, struct pgid *p2) ...@@ -223,7 +234,7 @@ static int pgid_cmp(struct pgid *p1, struct pgid *p2)
* Determine pathgroup state from PGID data. * Determine pathgroup state from PGID data.
*/ */
static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, 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 *pgid = &cdev->private->pgid[0];
struct pgid *first = NULL; struct pgid *first = NULL;
...@@ -238,9 +249,8 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, ...@@ -238,9 +249,8 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
continue; continue;
if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE) if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE)
*reserved = 1; *reserved = 1;
if (pgid->inf.ps.state1 == SNID_STATE1_RESET) { if (pgid_is_reset(pgid)) {
/* A PGID was reset. */ *reset |= lpm;
*reset = 1;
continue; continue;
} }
if (!first) { if (!first) {
...@@ -307,7 +317,7 @@ static void snid_done(struct ccw_device *cdev, int rc) ...@@ -307,7 +317,7 @@ static void snid_done(struct ccw_device *cdev, int rc)
struct pgid *pgid; struct pgid *pgid;
int mismatch = 0; int mismatch = 0;
int reserved = 0; int reserved = 0;
int reset = 0; u8 reset = 0;
u8 donepm; u8 donepm;
if (rc) if (rc)
...@@ -321,11 +331,12 @@ static void snid_done(struct ccw_device *cdev, int rc) ...@@ -321,11 +331,12 @@ static void snid_done(struct ccw_device *cdev, int rc)
donepm = pgid_to_donepm(cdev); donepm = pgid_to_donepm(cdev);
sch->vpm = donepm & sch->opm; sch->vpm = donepm & sch->opm;
cdev->private->pgid_todo_mask &= ~donepm; cdev->private->pgid_todo_mask &= ~donepm;
cdev->private->pgid_reset_mask |= reset;
pgid_fill(cdev, pgid); pgid_fill(cdev, pgid);
} }
out: out:
CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x " 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, id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm,
cdev->private->pgid_todo_mask, mismatch, reserved, reset); cdev->private->pgid_todo_mask, mismatch, reserved, reset);
switch (rc) { switch (rc) {
......
...@@ -153,6 +153,9 @@ struct ccw_device_private { ...@@ -153,6 +153,9 @@ struct ccw_device_private {
int iretry; int iretry;
u8 pgid_valid_mask; /* mask of valid PGIDs */ u8 pgid_valid_mask; /* mask of valid PGIDs */
u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */ 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 { struct {
unsigned int fast:1; /* post with "channel end" */ unsigned int fast:1; /* post with "channel end" */
unsigned int repall:1; /* report every interrupt status */ unsigned int repall:1; /* report every interrupt status */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment