From 585b954e1f2fa325d425b0786e4525ac7c9ae575 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 25 Oct 2010 16:10:34 +0200 Subject: [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 Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/device.c | 9 +++++++++ drivers/s390/cio/device_fsm.c | 32 ++++++++++++++++++++++++++++++++ drivers/s390/cio/device_pgid.c | 23 +++++++++++++++++------ drivers/s390/cio/io_sch.h | 7 +++++-- 4 files changed, 63 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 07b1a074beaf..881bdfd99140 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -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; } diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index c9b852647f01..4395c01a9dac 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -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); } /* diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c index 82a5ad0d63f6..07a4fd29f096 100644 --- a/drivers/s390/cio/device_pgid.c +++ b/drivers/s390/cio/device_pgid.c @@ -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) { diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h index 469ef93f2302..d024d2c21897 100644 --- a/drivers/s390/cio/io_sch.h +++ b/drivers/s390/cio/io_sch.h @@ -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 */ -- cgit v1.2.3