aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Ott <sebott@linux.vnet.ibm.com>2010-10-25 10:10:34 -0400
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>2010-10-25 10:10:19 -0400
commit585b954e1f2fa325d425b0786e4525ac7c9ae575 (patch)
treee79174793782e81820c7f6896284f7e341bf9e9d
parenteb4f5d93d70458ea644e0f46737f0bcf87280e83 (diff)
[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>
-rw-r--r--arch/s390/include/asm/ccwdev.h12
-rw-r--r--drivers/s390/cio/device.c9
-rw-r--r--drivers/s390/cio/device_fsm.c32
-rw-r--r--drivers/s390/cio/device_pgid.c23
-rw-r--r--drivers/s390/cio/io_sch.h7
5 files changed, 75 insertions, 8 deletions
diff --git a/arch/s390/include/asm/ccwdev.h b/arch/s390/include/asm/ccwdev.h
index f3ba0fa98de6..e8501115eca8 100644
--- a/arch/s390/include/asm/ccwdev.h
+++ b/arch/s390/include/asm/ccwdev.h
@@ -92,6 +92,16 @@ struct ccw_device {
92}; 92};
93 93
94/* 94/*
95 * Possible events used by the path_event notifier.
96 */
97#define PE_NONE 0x0
98#define PE_PATH_GONE 0x1 /* A path is no longer available. */
99#define PE_PATH_AVAILABLE 0x2 /* A path has become available and
100 was successfully verified. */
101#define PE_PATHGROUP_ESTABLISHED 0x4 /* A pathgroup was reset and had
102 to be established again. */
103
104/*
95 * Possible CIO actions triggered by the unit check handler. 105 * Possible CIO actions triggered by the unit check handler.
96 */ 106 */
97enum uc_todo { 107enum uc_todo {
@@ -109,6 +119,7 @@ enum uc_todo {
109 * @set_online: called when setting device online 119 * @set_online: called when setting device online
110 * @set_offline: called when setting device offline 120 * @set_offline: called when setting device offline
111 * @notify: notify driver of device state changes 121 * @notify: notify driver of device state changes
122 * @path_event: notify driver of channel path events
112 * @shutdown: called at device shutdown 123 * @shutdown: called at device shutdown
113 * @prepare: prepare for pm state transition 124 * @prepare: prepare for pm state transition
114 * @complete: undo work done in @prepare 125 * @complete: undo work done in @prepare
@@ -127,6 +138,7 @@ struct ccw_driver {
127 int (*set_online) (struct ccw_device *); 138 int (*set_online) (struct ccw_device *);
128 int (*set_offline) (struct ccw_device *); 139 int (*set_offline) (struct ccw_device *);
129 int (*notify) (struct ccw_device *, int); 140 int (*notify) (struct ccw_device *, int);
141 void (*path_event) (struct ccw_device *, int *);
130 void (*shutdown) (struct ccw_device *); 142 void (*shutdown) (struct ccw_device *);
131 int (*prepare) (struct ccw_device *); 143 int (*prepare) (struct ccw_device *);
132 void (*complete) (struct ccw_device *); 144 void (*complete) (struct ccw_device *);
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:
1147static int io_subchannel_chp_event(struct subchannel *sch, 1147static int io_subchannel_chp_event(struct subchannel *sch,
1148 struct chp_link *link, int event) 1148 struct chp_link *link, int event)
1149{ 1149{
1150 struct ccw_device *cdev = sch_get_cdev(sch);
1150 int mask; 1151 int mask;
1151 1152
1152 mask = chp_ssd_get_mask(&sch->ssd_info, link); 1153 mask = chp_ssd_get_mask(&sch->ssd_info, link);
@@ -1156,22 +1157,30 @@ static int io_subchannel_chp_event(struct subchannel *sch,
1156 case CHP_VARY_OFF: 1157 case CHP_VARY_OFF:
1157 sch->opm &= ~mask; 1158 sch->opm &= ~mask;
1158 sch->lpm &= ~mask; 1159 sch->lpm &= ~mask;
1160 if (cdev)
1161 cdev->private->path_gone_mask |= mask;
1159 io_subchannel_terminate_path(sch, mask); 1162 io_subchannel_terminate_path(sch, mask);
1160 break; 1163 break;
1161 case CHP_VARY_ON: 1164 case CHP_VARY_ON:
1162 sch->opm |= mask; 1165 sch->opm |= mask;
1163 sch->lpm |= mask; 1166 sch->lpm |= mask;
1167 if (cdev)
1168 cdev->private->path_new_mask |= mask;
1164 io_subchannel_verify(sch); 1169 io_subchannel_verify(sch);
1165 break; 1170 break;
1166 case CHP_OFFLINE: 1171 case CHP_OFFLINE:
1167 if (cio_update_schib(sch)) 1172 if (cio_update_schib(sch))
1168 return -ENODEV; 1173 return -ENODEV;
1174 if (cdev)
1175 cdev->private->path_gone_mask |= mask;
1169 io_subchannel_terminate_path(sch, mask); 1176 io_subchannel_terminate_path(sch, mask);
1170 break; 1177 break;
1171 case CHP_ONLINE: 1178 case CHP_ONLINE:
1172 if (cio_update_schib(sch)) 1179 if (cio_update_schib(sch))
1173 return -ENODEV; 1180 return -ENODEV;
1174 sch->lpm |= mask & sch->opm; 1181 sch->lpm |= mask & sch->opm;
1182 if (cdev)
1183 cdev->private->path_new_mask |= mask;
1175 io_subchannel_verify(sch); 1184 io_subchannel_verify(sch);
1176 break; 1185 break;
1177 } 1186 }
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:
349 349
350static void ccw_device_oper_notify(struct ccw_device *cdev) 350static void ccw_device_oper_notify(struct ccw_device *cdev)
351{ 351{
352 struct subchannel *sch = to_subchannel(cdev->dev.parent);
353
352 if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_OK) { 354 if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_OK) {
353 /* Reenable channel measurements, if needed. */ 355 /* Reenable channel measurements, if needed. */
354 ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF); 356 ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF);
357 /* Save indication for new paths. */
358 cdev->private->path_new_mask = sch->vpm;
355 return; 359 return;
356 } 360 }
357 /* Driver doesn't want device back. */ 361 /* 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 } 466 }
463} 467}
464 468
469static void ccw_device_report_path_events(struct ccw_device *cdev)
470{
471 struct subchannel *sch = to_subchannel(cdev->dev.parent);
472 int path_event[8];
473 int chp, mask;
474
475 for (chp = 0, mask = 0x80; chp < 8; chp++, mask >>= 1) {
476 path_event[chp] = PE_NONE;
477 if (mask & cdev->private->path_gone_mask & ~(sch->vpm))
478 path_event[chp] |= PE_PATH_GONE;
479 if (mask & cdev->private->path_new_mask & sch->vpm)
480 path_event[chp] |= PE_PATH_AVAILABLE;
481 if (mask & cdev->private->pgid_reset_mask & sch->vpm)
482 path_event[chp] |= PE_PATHGROUP_ESTABLISHED;
483 }
484 if (cdev->online && cdev->drv->path_event)
485 cdev->drv->path_event(cdev, path_event);
486}
487
488static void ccw_device_reset_path_events(struct ccw_device *cdev)
489{
490 cdev->private->path_gone_mask = 0;
491 cdev->private->path_new_mask = 0;
492 cdev->private->pgid_reset_mask = 0;
493}
494
465void 495void
466ccw_device_verify_done(struct ccw_device *cdev, int err) 496ccw_device_verify_done(struct ccw_device *cdev, int err)
467{ 497{
@@ -498,6 +528,7 @@ callback:
498 &cdev->private->irb); 528 &cdev->private->irb);
499 memset(&cdev->private->irb, 0, sizeof(struct irb)); 529 memset(&cdev->private->irb, 0, sizeof(struct irb));
500 } 530 }
531 ccw_device_report_path_events(cdev);
501 break; 532 break;
502 case -ETIME: 533 case -ETIME:
503 case -EUSERS: 534 case -EUSERS:
@@ -516,6 +547,7 @@ callback:
516 ccw_device_done(cdev, DEV_STATE_NOT_OPER); 547 ccw_device_done(cdev, DEV_STATE_NOT_OPER);
517 break; 548 break;
518 } 549 }
550 ccw_device_reset_path_events(cdev);
519} 551}
520 552
521/* 553/*
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)
213 spid_do(cdev); 213 spid_do(cdev);
214} 214}
215 215
216static int pgid_is_reset(struct pgid *p)
217{
218 char *c;
219
220 for (c = (char *)p + 1; c < (char *)(p + 1); c++) {
221 if (*c != 0)
222 return 0;
223 }
224 return 1;
225}
226
216static int pgid_cmp(struct pgid *p1, struct pgid *p2) 227static int pgid_cmp(struct pgid *p1, struct pgid *p2)
217{ 228{
218 return memcmp((char *) p1 + 1, (char *) p2 + 1, 229 return memcmp((char *) p1 + 1, (char *) p2 + 1,
@@ -223,7 +234,7 @@ static int pgid_cmp(struct pgid *p1, struct pgid *p2)
223 * Determine pathgroup state from PGID data. 234 * Determine pathgroup state from PGID data.
224 */ 235 */
225static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, 236static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
226 int *mismatch, int *reserved, int *reset) 237 int *mismatch, int *reserved, u8 *reset)
227{ 238{
228 struct pgid *pgid = &cdev->private->pgid[0]; 239 struct pgid *pgid = &cdev->private->pgid[0];
229 struct pgid *first = NULL; 240 struct pgid *first = NULL;
@@ -238,9 +249,8 @@ static void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
238 continue; 249 continue;
239 if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE) 250 if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE)
240 *reserved = 1; 251 *reserved = 1;
241 if (pgid->inf.ps.state1 == SNID_STATE1_RESET) { 252 if (pgid_is_reset(pgid)) {
242 /* A PGID was reset. */ 253 *reset |= lpm;
243 *reset = 1;
244 continue; 254 continue;
245 } 255 }
246 if (!first) { 256 if (!first) {
@@ -307,7 +317,7 @@ static void snid_done(struct ccw_device *cdev, int rc)
307 struct pgid *pgid; 317 struct pgid *pgid;
308 int mismatch = 0; 318 int mismatch = 0;
309 int reserved = 0; 319 int reserved = 0;
310 int reset = 0; 320 u8 reset = 0;
311 u8 donepm; 321 u8 donepm;
312 322
313 if (rc) 323 if (rc)
@@ -321,11 +331,12 @@ static void snid_done(struct ccw_device *cdev, int rc)
321 donepm = pgid_to_donepm(cdev); 331 donepm = pgid_to_donepm(cdev);
322 sch->vpm = donepm & sch->opm; 332 sch->vpm = donepm & sch->opm;
323 cdev->private->pgid_todo_mask &= ~donepm; 333 cdev->private->pgid_todo_mask &= ~donepm;
334 cdev->private->pgid_reset_mask |= reset;
324 pgid_fill(cdev, pgid); 335 pgid_fill(cdev, pgid);
325 } 336 }
326out: 337out:
327 CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x " 338 CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x "
328 "todo=%02x mism=%d rsvd=%d reset=%d\n", id->ssid, 339 "todo=%02x mism=%d rsvd=%d reset=%02x\n", id->ssid,
329 id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm, 340 id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm,
330 cdev->private->pgid_todo_mask, mismatch, reserved, reset); 341 cdev->private->pgid_todo_mask, mismatch, reserved, reset);
331 switch (rc) { 342 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 {
151 struct subchannel_id schid; /* subchannel number */ 151 struct subchannel_id schid; /* subchannel number */
152 struct ccw_request req; /* internal I/O request */ 152 struct ccw_request req; /* internal I/O request */
153 int iretry; 153 int iretry;
154 u8 pgid_valid_mask; /* mask of valid PGIDs */ 154 u8 pgid_valid_mask; /* mask of valid PGIDs */
155 u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */ 155 u8 pgid_todo_mask; /* mask of PGIDs to be adjusted */
156 u8 pgid_reset_mask; /* mask of PGIDs which were reset */
157 u8 path_gone_mask; /* mask of paths, that became unavailable */
158 u8 path_new_mask; /* mask of paths, that became available */
156 struct { 159 struct {
157 unsigned int fast:1; /* post with "channel end" */ 160 unsigned int fast:1; /* post with "channel end" */
158 unsigned int repall:1; /* report every interrupt status */ 161 unsigned int repall:1; /* report every interrupt status */