[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: Sebastian Ott <sebott@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
eb4f5d93d7
commit
585b954e1f
|
@ -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 *);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue