diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2010-10-25 10:10:34 -0400 |
---|---|---|
committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2010-10-25 10:10:19 -0400 |
commit | 585b954e1f2fa325d425b0786e4525ac7c9ae575 (patch) | |
tree | e79174793782e81820c7f6896284f7e341bf9e9d | |
parent | eb4f5d93d70458ea644e0f46737f0bcf87280e83 (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.h | 12 | ||||
-rw-r--r-- | drivers/s390/cio/device.c | 9 | ||||
-rw-r--r-- | drivers/s390/cio/device_fsm.c | 32 | ||||
-rw-r--r-- | drivers/s390/cio/device_pgid.c | 23 | ||||
-rw-r--r-- | drivers/s390/cio/io_sch.h | 7 |
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 | */ |
97 | enum uc_todo { | 107 | enum 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: | |||
1147 | static int io_subchannel_chp_event(struct subchannel *sch, | 1147 | static 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 | ||
350 | static void ccw_device_oper_notify(struct ccw_device *cdev) | 350 | static 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 | ||
469 | static 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 | |||
488 | static 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 | |||
465 | void | 495 | void |
466 | ccw_device_verify_done(struct ccw_device *cdev, int err) | 496 | ccw_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 | ||
216 | static 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 | |||
216 | static int pgid_cmp(struct pgid *p1, struct pgid *p2) | 227 | static 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 | */ |
225 | static void pgid_analyze(struct ccw_device *cdev, struct pgid **p, | 236 | static 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 | } |
326 | out: | 337 | out: |
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 */ |