diff options
author | Cornelia Huck <cornelia.huck@de.ibm.com> | 2008-07-14 03:58:45 -0400 |
---|---|---|
committer | Heiko Carstens <heiko.carstens@de.ibm.com> | 2008-07-14 04:02:05 -0400 |
commit | c820de39bd083222f5be2563181c87493e436f7c (patch) | |
tree | 4861db1aeca00d55d76b5844ad209d81a2795105 /drivers/s390/cio | |
parent | 7e9db9eaefdb8798730790214ff1b7746006ec98 (diff) |
[S390] cio: Rework css driver.
Rework the css driver methods to provide sane callbacks for
subchannels of all types.
As a bonus, this cleans up and simplyfies the machine check
handling for I/O subchannels a lot.
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r-- | drivers/s390/cio/chp.c | 20 | ||||
-rw-r--r-- | drivers/s390/cio/chp.h | 13 | ||||
-rw-r--r-- | drivers/s390/cio/chsc.c | 218 | ||||
-rw-r--r-- | drivers/s390/cio/cio.c | 1 | ||||
-rw-r--r-- | drivers/s390/cio/css.c | 123 | ||||
-rw-r--r-- | drivers/s390/cio/css.h | 38 | ||||
-rw-r--r-- | drivers/s390/cio/device.c | 365 | ||||
-rw-r--r-- | drivers/s390/cio/device.h | 7 | ||||
-rw-r--r-- | drivers/s390/cio/device_fsm.c | 106 |
9 files changed, 381 insertions, 510 deletions
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 297cdceb0ca4..297f1653b52b 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c | |||
@@ -496,6 +496,26 @@ void chp_process_crw(int id, int status) | |||
496 | chsc_chp_offline(chpid); | 496 | chsc_chp_offline(chpid); |
497 | } | 497 | } |
498 | 498 | ||
499 | int chp_ssd_get_mask(struct chsc_ssd_info *ssd, struct res_acc_data *data) | ||
500 | { | ||
501 | int i; | ||
502 | int mask; | ||
503 | |||
504 | for (i = 0; i < 8; i++) { | ||
505 | mask = 0x80 >> i; | ||
506 | if (!(ssd->path_mask & mask)) | ||
507 | continue; | ||
508 | if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid)) | ||
509 | continue; | ||
510 | if ((ssd->fla_valid_mask & mask) && | ||
511 | ((ssd->fla[i] & data->fla_mask) != data->fla)) | ||
512 | continue; | ||
513 | return mask; | ||
514 | } | ||
515 | return 0; | ||
516 | } | ||
517 | EXPORT_SYMBOL_GPL(chp_ssd_get_mask); | ||
518 | |||
499 | static inline int info_bit_num(struct chp_id id) | 519 | static inline int info_bit_num(struct chp_id id) |
500 | { | 520 | { |
501 | return id.id + id.cssid * (__MAX_CHPID + 1); | 521 | return id.id + id.cssid * (__MAX_CHPID + 1); |
diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h index 59c2fc069d9e..f03b0d2cdc09 100644 --- a/drivers/s390/cio/chp.h +++ b/drivers/s390/cio/chp.h | |||
@@ -19,6 +19,17 @@ | |||
19 | #define CHP_STATUS_RESERVED 2 | 19 | #define CHP_STATUS_RESERVED 2 |
20 | #define CHP_STATUS_NOT_RECOGNIZED 3 | 20 | #define CHP_STATUS_NOT_RECOGNIZED 3 |
21 | 21 | ||
22 | #define CHP_ONLINE 0 | ||
23 | #define CHP_OFFLINE 1 | ||
24 | #define CHP_VARY_ON 2 | ||
25 | #define CHP_VARY_OFF 3 | ||
26 | |||
27 | struct res_acc_data { | ||
28 | struct chp_id chpid; | ||
29 | u32 fla_mask; | ||
30 | u16 fla; | ||
31 | }; | ||
32 | |||
22 | static inline int chp_test_bit(u8 *bitmap, int num) | 33 | static inline int chp_test_bit(u8 *bitmap, int num) |
23 | { | 34 | { |
24 | int byte = num >> 3; | 35 | int byte = num >> 3; |
@@ -50,5 +61,5 @@ int chp_new(struct chp_id chpid); | |||
50 | void chp_cfg_schedule(struct chp_id chpid, int configure); | 61 | void chp_cfg_schedule(struct chp_id chpid, int configure); |
51 | void chp_cfg_cancel_deconfigure(struct chp_id chpid); | 62 | void chp_cfg_cancel_deconfigure(struct chp_id chpid); |
52 | int chp_info_get_status(struct chp_id chpid); | 63 | int chp_info_get_status(struct chp_id chpid); |
53 | 64 | int chp_ssd_get_mask(struct chsc_ssd_info *, struct res_acc_data *); | |
54 | #endif /* S390_CHP_H */ | 65 | #endif /* S390_CHP_H */ |
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 5de86908b0d0..1c0f5db94c7b 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c | |||
@@ -2,8 +2,7 @@ | |||
2 | * drivers/s390/cio/chsc.c | 2 | * drivers/s390/cio/chsc.c |
3 | * S/390 common I/O routines -- channel subsystem call | 3 | * S/390 common I/O routines -- channel subsystem call |
4 | * | 4 | * |
5 | * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH, | 5 | * Copyright IBM Corp. 1999,2008 |
6 | * IBM Corporation | ||
7 | * Author(s): Ingo Adlung (adlung@de.ibm.com) | 6 | * Author(s): Ingo Adlung (adlung@de.ibm.com) |
8 | * Cornelia Huck (cornelia.huck@de.ibm.com) | 7 | * Cornelia Huck (cornelia.huck@de.ibm.com) |
9 | * Arnd Bergmann (arndb@de.ibm.com) | 8 | * Arnd Bergmann (arndb@de.ibm.com) |
@@ -127,77 +126,12 @@ out_free: | |||
127 | return ret; | 126 | return ret; |
128 | } | 127 | } |
129 | 128 | ||
130 | static int check_for_io_on_path(struct subchannel *sch, int mask) | ||
131 | { | ||
132 | int cc; | ||
133 | |||
134 | cc = stsch(sch->schid, &sch->schib); | ||
135 | if (cc) | ||
136 | return 0; | ||
137 | if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask) | ||
138 | return 1; | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static void terminate_internal_io(struct subchannel *sch) | ||
143 | { | ||
144 | if (cio_clear(sch)) { | ||
145 | /* Recheck device in case clear failed. */ | ||
146 | sch->lpm = 0; | ||
147 | if (device_trigger_verify(sch) != 0) | ||
148 | css_schedule_eval(sch->schid); | ||
149 | return; | ||
150 | } | ||
151 | /* Request retry of internal operation. */ | ||
152 | device_set_intretry(sch); | ||
153 | /* Call handler. */ | ||
154 | if (sch->driver && sch->driver->termination) | ||
155 | sch->driver->termination(sch); | ||
156 | } | ||
157 | |||
158 | static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data) | 129 | static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data) |
159 | { | 130 | { |
160 | int j; | ||
161 | int mask; | ||
162 | struct chp_id *chpid = data; | ||
163 | struct schib schib; | ||
164 | |||
165 | for (j = 0; j < 8; j++) { | ||
166 | mask = 0x80 >> j; | ||
167 | if ((sch->schib.pmcw.pim & mask) && | ||
168 | (sch->schib.pmcw.chpid[j] == chpid->id)) | ||
169 | break; | ||
170 | } | ||
171 | if (j >= 8) | ||
172 | return 0; | ||
173 | |||
174 | spin_lock_irq(sch->lock); | 131 | spin_lock_irq(sch->lock); |
175 | 132 | if (sch->driver && sch->driver->chp_event) | |
176 | stsch(sch->schid, &schib); | 133 | if (sch->driver->chp_event(sch, data, CHP_OFFLINE) != 0) |
177 | if (!css_sch_is_valid(&schib)) | ||
178 | goto out_unreg; | ||
179 | memcpy(&sch->schib, &schib, sizeof(struct schib)); | ||
180 | /* Check for single path devices. */ | ||
181 | if (sch->schib.pmcw.pim == 0x80) | ||
182 | goto out_unreg; | ||
183 | |||
184 | if (check_for_io_on_path(sch, mask)) { | ||
185 | if (device_is_online(sch)) | ||
186 | device_kill_io(sch); | ||
187 | else { | ||
188 | terminate_internal_io(sch); | ||
189 | /* Re-start path verification. */ | ||
190 | if (sch->driver && sch->driver->verify) | ||
191 | sch->driver->verify(sch); | ||
192 | } | ||
193 | } else { | ||
194 | /* trigger path verification. */ | ||
195 | if (sch->driver && sch->driver->verify) | ||
196 | sch->driver->verify(sch); | ||
197 | else if (sch->lpm == mask) | ||
198 | goto out_unreg; | 134 | goto out_unreg; |
199 | } | ||
200 | |||
201 | spin_unlock_irq(sch->lock); | 135 | spin_unlock_irq(sch->lock); |
202 | return 0; | 136 | return 0; |
203 | 137 | ||
@@ -242,53 +176,11 @@ static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data) | |||
242 | return 0; | 176 | return 0; |
243 | } | 177 | } |
244 | 178 | ||
245 | struct res_acc_data { | ||
246 | struct chp_id chpid; | ||
247 | u32 fla_mask; | ||
248 | u16 fla; | ||
249 | }; | ||
250 | |||
251 | static int get_res_chpid_mask(struct chsc_ssd_info *ssd, | ||
252 | struct res_acc_data *data) | ||
253 | { | ||
254 | int i; | ||
255 | int mask; | ||
256 | |||
257 | for (i = 0; i < 8; i++) { | ||
258 | mask = 0x80 >> i; | ||
259 | if (!(ssd->path_mask & mask)) | ||
260 | continue; | ||
261 | if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid)) | ||
262 | continue; | ||
263 | if ((ssd->fla_valid_mask & mask) && | ||
264 | ((ssd->fla[i] & data->fla_mask) != data->fla)) | ||
265 | continue; | ||
266 | return mask; | ||
267 | } | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static int __s390_process_res_acc(struct subchannel *sch, void *data) | 179 | static int __s390_process_res_acc(struct subchannel *sch, void *data) |
272 | { | 180 | { |
273 | int chp_mask, old_lpm; | ||
274 | struct res_acc_data *res_data = data; | ||
275 | |||
276 | spin_lock_irq(sch->lock); | 181 | spin_lock_irq(sch->lock); |
277 | chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data); | 182 | if (sch->driver && sch->driver->chp_event) |
278 | if (chp_mask == 0) | 183 | sch->driver->chp_event(sch, data, CHP_ONLINE); |
279 | goto out; | ||
280 | if (stsch(sch->schid, &sch->schib)) | ||
281 | goto out; | ||
282 | old_lpm = sch->lpm; | ||
283 | sch->lpm = ((sch->schib.pmcw.pim & | ||
284 | sch->schib.pmcw.pam & | ||
285 | sch->schib.pmcw.pom) | ||
286 | | chp_mask) & sch->opm; | ||
287 | if (!old_lpm && sch->lpm) | ||
288 | device_trigger_reprobe(sch); | ||
289 | else if (sch->driver && sch->driver->verify) | ||
290 | sch->driver->verify(sch); | ||
291 | out: | ||
292 | spin_unlock_irq(sch->lock); | 184 | spin_unlock_irq(sch->lock); |
293 | 185 | ||
294 | return 0; | 186 | return 0; |
@@ -509,114 +401,36 @@ void chsc_process_crw(void) | |||
509 | } while (sei_area->flags & 0x80); | 401 | } while (sei_area->flags & 0x80); |
510 | } | 402 | } |
511 | 403 | ||
512 | static int __chp_add_new_sch(struct subchannel_id schid, void *data) | ||
513 | { | ||
514 | struct schib schib; | ||
515 | |||
516 | if (stsch_err(schid, &schib)) | ||
517 | /* We're through */ | ||
518 | return -ENXIO; | ||
519 | |||
520 | /* Put it on the slow path. */ | ||
521 | css_schedule_eval(schid); | ||
522 | return 0; | ||
523 | } | ||
524 | |||
525 | |||
526 | static int __chp_add(struct subchannel *sch, void *data) | ||
527 | { | ||
528 | int i, mask; | ||
529 | struct chp_id *chpid = data; | ||
530 | |||
531 | spin_lock_irq(sch->lock); | ||
532 | for (i=0; i<8; i++) { | ||
533 | mask = 0x80 >> i; | ||
534 | if ((sch->schib.pmcw.pim & mask) && | ||
535 | (sch->schib.pmcw.chpid[i] == chpid->id)) | ||
536 | break; | ||
537 | } | ||
538 | if (i==8) { | ||
539 | spin_unlock_irq(sch->lock); | ||
540 | return 0; | ||
541 | } | ||
542 | if (stsch(sch->schid, &sch->schib)) { | ||
543 | spin_unlock_irq(sch->lock); | ||
544 | css_schedule_eval(sch->schid); | ||
545 | return 0; | ||
546 | } | ||
547 | sch->lpm = ((sch->schib.pmcw.pim & | ||
548 | sch->schib.pmcw.pam & | ||
549 | sch->schib.pmcw.pom) | ||
550 | | mask) & sch->opm; | ||
551 | |||
552 | if (sch->driver && sch->driver->verify) | ||
553 | sch->driver->verify(sch); | ||
554 | |||
555 | spin_unlock_irq(sch->lock); | ||
556 | |||
557 | return 0; | ||
558 | } | ||
559 | |||
560 | void chsc_chp_online(struct chp_id chpid) | 404 | void chsc_chp_online(struct chp_id chpid) |
561 | { | 405 | { |
562 | char dbf_txt[15]; | 406 | char dbf_txt[15]; |
407 | struct res_acc_data res_data; | ||
563 | 408 | ||
564 | sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id); | 409 | sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id); |
565 | CIO_TRACE_EVENT(2, dbf_txt); | 410 | CIO_TRACE_EVENT(2, dbf_txt); |
566 | 411 | ||
567 | if (chp_get_status(chpid) != 0) { | 412 | if (chp_get_status(chpid) != 0) { |
413 | memset(&res_data, 0, sizeof(struct res_acc_data)); | ||
414 | res_data.chpid = chpid; | ||
568 | /* Wait until previous actions have settled. */ | 415 | /* Wait until previous actions have settled. */ |
569 | css_wait_for_slow_path(); | 416 | css_wait_for_slow_path(); |
570 | for_each_subchannel_staged(__chp_add, __chp_add_new_sch, | 417 | for_each_subchannel_staged(__s390_process_res_acc, NULL, |
571 | &chpid); | 418 | &res_data); |
572 | } | 419 | } |
573 | } | 420 | } |
574 | 421 | ||
575 | static void __s390_subchannel_vary_chpid(struct subchannel *sch, | 422 | static void __s390_subchannel_vary_chpid(struct subchannel *sch, |
576 | struct chp_id chpid, int on) | 423 | struct chp_id chpid, int on) |
577 | { | 424 | { |
578 | int chp, old_lpm; | ||
579 | int mask; | ||
580 | unsigned long flags; | 425 | unsigned long flags; |
426 | struct res_acc_data res_data; | ||
581 | 427 | ||
428 | memset(&res_data, 0, sizeof(struct res_acc_data)); | ||
429 | res_data.chpid = chpid; | ||
582 | spin_lock_irqsave(sch->lock, flags); | 430 | spin_lock_irqsave(sch->lock, flags); |
583 | old_lpm = sch->lpm; | 431 | if (sch->driver && sch->driver->chp_event) |
584 | for (chp = 0; chp < 8; chp++) { | 432 | sch->driver->chp_event(sch, &res_data, |
585 | mask = 0x80 >> chp; | 433 | on ? CHP_VARY_ON : CHP_VARY_OFF); |
586 | if (!(sch->ssd_info.path_mask & mask)) | ||
587 | continue; | ||
588 | if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid)) | ||
589 | continue; | ||
590 | |||
591 | if (on) { | ||
592 | sch->opm |= mask; | ||
593 | sch->lpm |= mask; | ||
594 | if (!old_lpm) | ||
595 | device_trigger_reprobe(sch); | ||
596 | else if (sch->driver && sch->driver->verify) | ||
597 | sch->driver->verify(sch); | ||
598 | break; | ||
599 | } | ||
600 | sch->opm &= ~mask; | ||
601 | sch->lpm &= ~mask; | ||
602 | if (check_for_io_on_path(sch, mask)) { | ||
603 | if (device_is_online(sch)) | ||
604 | /* Path verification is done after killing. */ | ||
605 | device_kill_io(sch); | ||
606 | else { | ||
607 | /* Kill and retry internal I/O. */ | ||
608 | terminate_internal_io(sch); | ||
609 | /* Re-start path verification. */ | ||
610 | if (sch->driver && sch->driver->verify) | ||
611 | sch->driver->verify(sch); | ||
612 | } | ||
613 | } else if (!sch->lpm) { | ||
614 | if (device_trigger_verify(sch) != 0) | ||
615 | css_schedule_eval(sch->schid); | ||
616 | } else if (sch->driver && sch->driver->verify) | ||
617 | sch->driver->verify(sch); | ||
618 | break; | ||
619 | } | ||
620 | spin_unlock_irqrestore(sch->lock, flags); | 434 | spin_unlock_irqrestore(sch->lock, flags); |
621 | } | 435 | } |
622 | 436 | ||
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 903e23ae8ed5..fdb164f36109 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c | |||
@@ -564,6 +564,7 @@ int cio_validate_subchannel(struct subchannel *sch, struct subchannel_id schid) | |||
564 | } | 564 | } |
565 | /* Copy subchannel type from path management control word. */ | 565 | /* Copy subchannel type from path management control word. */ |
566 | sch->st = sch->schib.pmcw.st; | 566 | sch->st = sch->schib.pmcw.st; |
567 | |||
567 | switch (sch->st) { | 568 | switch (sch->st) { |
568 | case SUBCHANNEL_TYPE_IO: | 569 | case SUBCHANNEL_TYPE_IO: |
569 | err = cio_validate_io_subchannel(sch); | 570 | err = cio_validate_io_subchannel(sch); |
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 53e7496dc90c..020566571e07 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c | |||
@@ -283,7 +283,7 @@ static int css_register_subchannel(struct subchannel *sch) | |||
283 | return ret; | 283 | return ret; |
284 | } | 284 | } |
285 | 285 | ||
286 | static int css_probe_device(struct subchannel_id schid) | 286 | int css_probe_device(struct subchannel_id schid) |
287 | { | 287 | { |
288 | int ret; | 288 | int ret; |
289 | struct subchannel *sch; | 289 | struct subchannel *sch; |
@@ -330,112 +330,6 @@ int css_sch_is_valid(struct schib *schib) | |||
330 | } | 330 | } |
331 | EXPORT_SYMBOL_GPL(css_sch_is_valid); | 331 | EXPORT_SYMBOL_GPL(css_sch_is_valid); |
332 | 332 | ||
333 | static int css_get_subchannel_status(struct subchannel *sch) | ||
334 | { | ||
335 | struct schib schib; | ||
336 | |||
337 | if (stsch(sch->schid, &schib)) | ||
338 | return CIO_GONE; | ||
339 | if (!css_sch_is_valid(&schib)) | ||
340 | return CIO_GONE; | ||
341 | if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev)) | ||
342 | return CIO_REVALIDATE; | ||
343 | if (!sch->lpm) | ||
344 | return CIO_NO_PATH; | ||
345 | return CIO_OPER; | ||
346 | } | ||
347 | |||
348 | static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) | ||
349 | { | ||
350 | int event, ret, disc; | ||
351 | unsigned long flags; | ||
352 | enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action; | ||
353 | |||
354 | spin_lock_irqsave(sch->lock, flags); | ||
355 | disc = device_is_disconnected(sch); | ||
356 | if (disc && slow) { | ||
357 | /* Disconnected devices are evaluated directly only.*/ | ||
358 | spin_unlock_irqrestore(sch->lock, flags); | ||
359 | return 0; | ||
360 | } | ||
361 | /* No interrupt after machine check - kill pending timers. */ | ||
362 | device_kill_pending_timer(sch); | ||
363 | if (!disc && !slow) { | ||
364 | /* Non-disconnected devices are evaluated on the slow path. */ | ||
365 | spin_unlock_irqrestore(sch->lock, flags); | ||
366 | return -EAGAIN; | ||
367 | } | ||
368 | event = css_get_subchannel_status(sch); | ||
369 | CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n", | ||
370 | sch->schid.ssid, sch->schid.sch_no, event, | ||
371 | disc ? "disconnected" : "normal", | ||
372 | slow ? "slow" : "fast"); | ||
373 | /* Analyze subchannel status. */ | ||
374 | action = NONE; | ||
375 | switch (event) { | ||
376 | case CIO_NO_PATH: | ||
377 | if (disc) { | ||
378 | /* Check if paths have become available. */ | ||
379 | action = REPROBE; | ||
380 | break; | ||
381 | } | ||
382 | /* fall through */ | ||
383 | case CIO_GONE: | ||
384 | /* Prevent unwanted effects when opening lock. */ | ||
385 | cio_disable_subchannel(sch); | ||
386 | device_set_disconnected(sch); | ||
387 | /* Ask driver what to do with device. */ | ||
388 | action = UNREGISTER; | ||
389 | if (sch->driver && sch->driver->notify) { | ||
390 | spin_unlock_irqrestore(sch->lock, flags); | ||
391 | ret = sch->driver->notify(sch, event); | ||
392 | spin_lock_irqsave(sch->lock, flags); | ||
393 | if (ret) | ||
394 | action = NONE; | ||
395 | } | ||
396 | break; | ||
397 | case CIO_REVALIDATE: | ||
398 | /* Device will be removed, so no notify necessary. */ | ||
399 | if (disc) | ||
400 | /* Reprobe because immediate unregister might block. */ | ||
401 | action = REPROBE; | ||
402 | else | ||
403 | action = UNREGISTER_PROBE; | ||
404 | break; | ||
405 | case CIO_OPER: | ||
406 | if (disc) | ||
407 | /* Get device operational again. */ | ||
408 | action = REPROBE; | ||
409 | break; | ||
410 | } | ||
411 | /* Perform action. */ | ||
412 | ret = 0; | ||
413 | switch (action) { | ||
414 | case UNREGISTER: | ||
415 | case UNREGISTER_PROBE: | ||
416 | /* Unregister device (will use subchannel lock). */ | ||
417 | spin_unlock_irqrestore(sch->lock, flags); | ||
418 | css_sch_device_unregister(sch); | ||
419 | spin_lock_irqsave(sch->lock, flags); | ||
420 | |||
421 | /* Reset intparm to zeroes. */ | ||
422 | sch->schib.pmcw.intparm = 0; | ||
423 | cio_modify(sch); | ||
424 | break; | ||
425 | case REPROBE: | ||
426 | device_trigger_reprobe(sch); | ||
427 | break; | ||
428 | default: | ||
429 | break; | ||
430 | } | ||
431 | spin_unlock_irqrestore(sch->lock, flags); | ||
432 | /* Probe if necessary. */ | ||
433 | if (action == UNREGISTER_PROBE) | ||
434 | ret = css_probe_device(sch->schid); | ||
435 | |||
436 | return ret; | ||
437 | } | ||
438 | |||
439 | static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) | 333 | static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) |
440 | { | 334 | { |
441 | struct schib schib; | 335 | struct schib schib; |
@@ -454,6 +348,21 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) | |||
454 | return css_probe_device(schid); | 348 | return css_probe_device(schid); |
455 | } | 349 | } |
456 | 350 | ||
351 | static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) | ||
352 | { | ||
353 | int ret = 0; | ||
354 | |||
355 | if (sch->driver) { | ||
356 | if (sch->driver->sch_event) | ||
357 | ret = sch->driver->sch_event(sch, slow); | ||
358 | else | ||
359 | dev_dbg(&sch->dev, | ||
360 | "Got subchannel machine check but " | ||
361 | "no sch_event handler provided.\n"); | ||
362 | } | ||
363 | return ret; | ||
364 | } | ||
365 | |||
457 | static void css_evaluate_subchannel(struct subchannel_id schid, int slow) | 366 | static void css_evaluate_subchannel(struct subchannel_id schid, int slow) |
458 | { | 367 | { |
459 | struct subchannel *sch; | 368 | struct subchannel *sch; |
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index e0fc7b499784..4cdc132c86bb 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h | |||
@@ -58,18 +58,27 @@ struct pgid { | |||
58 | __u32 tod_high; /* high word TOD clock */ | 58 | __u32 tod_high; /* high word TOD clock */ |
59 | } __attribute__ ((packed)); | 59 | } __attribute__ ((packed)); |
60 | 60 | ||
61 | /* | ||
62 | * A css driver handles all subchannels of one type. | ||
63 | */ | ||
64 | struct subchannel; | 61 | struct subchannel; |
62 | /** | ||
63 | * struct css_driver - device driver for subchannels | ||
64 | * @owner: owning module | ||
65 | * @subchannel_type: subchannel type supported by this driver | ||
66 | * @drv: embedded device driver structure | ||
67 | * @irq: called on interrupts | ||
68 | * @chp_event: called for events affecting a channel path | ||
69 | * @sch_event: called for events affecting the subchannel | ||
70 | * @probe: function called on probe | ||
71 | * @remove: function called on remove | ||
72 | * @shutdown: called at device shutdown | ||
73 | * @name: name of the device driver | ||
74 | */ | ||
65 | struct css_driver { | 75 | struct css_driver { |
66 | struct module *owner; | 76 | struct module *owner; |
67 | unsigned int subchannel_type; | 77 | unsigned int subchannel_type; |
68 | struct device_driver drv; | 78 | struct device_driver drv; |
69 | void (*irq)(struct subchannel *); | 79 | void (*irq)(struct subchannel *); |
70 | int (*notify)(struct subchannel *, int); | 80 | int (*chp_event)(struct subchannel *, void *, int); |
71 | void (*verify)(struct subchannel *); | 81 | int (*sch_event)(struct subchannel *, int); |
72 | void (*termination)(struct subchannel *); | ||
73 | int (*probe)(struct subchannel *); | 82 | int (*probe)(struct subchannel *); |
74 | int (*remove)(struct subchannel *); | 83 | int (*remove)(struct subchannel *); |
75 | void (*shutdown)(struct subchannel *); | 84 | void (*shutdown)(struct subchannel *); |
@@ -87,7 +96,8 @@ extern int css_driver_register(struct css_driver *); | |||
87 | extern void css_driver_unregister(struct css_driver *); | 96 | extern void css_driver_unregister(struct css_driver *); |
88 | 97 | ||
89 | extern void css_sch_device_unregister(struct subchannel *); | 98 | extern void css_sch_device_unregister(struct subchannel *); |
90 | extern struct subchannel * get_subchannel_by_schid(struct subchannel_id); | 99 | extern int css_probe_device(struct subchannel_id); |
100 | extern struct subchannel *get_subchannel_by_schid(struct subchannel_id); | ||
91 | extern int css_init_done; | 101 | extern int css_init_done; |
92 | int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *), | 102 | int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *), |
93 | int (*fn_unknown)(struct subchannel_id, | 103 | int (*fn_unknown)(struct subchannel_id, |
@@ -119,20 +129,6 @@ struct channel_subsystem { | |||
119 | extern struct bus_type css_bus_type; | 129 | extern struct bus_type css_bus_type; |
120 | extern struct channel_subsystem *channel_subsystems[]; | 130 | extern struct channel_subsystem *channel_subsystems[]; |
121 | 131 | ||
122 | /* Some helper functions for disconnected state. */ | ||
123 | int device_is_disconnected(struct subchannel *); | ||
124 | void device_set_disconnected(struct subchannel *); | ||
125 | void device_trigger_reprobe(struct subchannel *); | ||
126 | |||
127 | /* Helper functions for vary on/off. */ | ||
128 | int device_is_online(struct subchannel *); | ||
129 | void device_kill_io(struct subchannel *); | ||
130 | void device_set_intretry(struct subchannel *sch); | ||
131 | int device_trigger_verify(struct subchannel *sch); | ||
132 | |||
133 | /* Machine check helper function. */ | ||
134 | void device_kill_pending_timer(struct subchannel *); | ||
135 | |||
136 | /* Helper functions to build lists for the slow path. */ | 132 | /* Helper functions to build lists for the slow path. */ |
137 | void css_schedule_eval(struct subchannel_id schid); | 133 | void css_schedule_eval(struct subchannel_id schid); |
138 | void css_schedule_eval_all(void); | 134 | void css_schedule_eval_all(void); |
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 23b129fd4d8d..9281b25087a6 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -2,8 +2,7 @@ | |||
2 | * drivers/s390/cio/device.c | 2 | * drivers/s390/cio/device.c |
3 | * bus driver for ccw devices | 3 | * bus driver for ccw devices |
4 | * | 4 | * |
5 | * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, | 5 | * Copyright IBM Corp. 2002,2008 |
6 | * IBM Corporation | ||
7 | * Author(s): Arnd Bergmann (arndb@de.ibm.com) | 6 | * Author(s): Arnd Bergmann (arndb@de.ibm.com) |
8 | * Cornelia Huck (cornelia.huck@de.ibm.com) | 7 | * Cornelia Huck (cornelia.huck@de.ibm.com) |
9 | * Martin Schwidefsky (schwidefsky@de.ibm.com) | 8 | * Martin Schwidefsky (schwidefsky@de.ibm.com) |
@@ -126,19 +125,17 @@ struct bus_type ccw_bus_type; | |||
126 | static void io_subchannel_irq(struct subchannel *); | 125 | static void io_subchannel_irq(struct subchannel *); |
127 | static int io_subchannel_probe(struct subchannel *); | 126 | static int io_subchannel_probe(struct subchannel *); |
128 | static int io_subchannel_remove(struct subchannel *); | 127 | static int io_subchannel_remove(struct subchannel *); |
129 | static int io_subchannel_notify(struct subchannel *, int); | ||
130 | static void io_subchannel_verify(struct subchannel *); | ||
131 | static void io_subchannel_ioterm(struct subchannel *); | ||
132 | static void io_subchannel_shutdown(struct subchannel *); | 128 | static void io_subchannel_shutdown(struct subchannel *); |
129 | static int io_subchannel_sch_event(struct subchannel *, int); | ||
130 | static int io_subchannel_chp_event(struct subchannel *, void *, int); | ||
133 | 131 | ||
134 | static struct css_driver io_subchannel_driver = { | 132 | static struct css_driver io_subchannel_driver = { |
135 | .owner = THIS_MODULE, | 133 | .owner = THIS_MODULE, |
136 | .subchannel_type = SUBCHANNEL_TYPE_IO, | 134 | .subchannel_type = SUBCHANNEL_TYPE_IO, |
137 | .name = "io_subchannel", | 135 | .name = "io_subchannel", |
138 | .irq = io_subchannel_irq, | 136 | .irq = io_subchannel_irq, |
139 | .notify = io_subchannel_notify, | 137 | .sch_event = io_subchannel_sch_event, |
140 | .verify = io_subchannel_verify, | 138 | .chp_event = io_subchannel_chp_event, |
141 | .termination = io_subchannel_ioterm, | ||
142 | .probe = io_subchannel_probe, | 139 | .probe = io_subchannel_probe, |
143 | .remove = io_subchannel_remove, | 140 | .remove = io_subchannel_remove, |
144 | .shutdown = io_subchannel_shutdown, | 141 | .shutdown = io_subchannel_shutdown, |
@@ -786,7 +783,7 @@ static void sch_attach_device(struct subchannel *sch, | |||
786 | sch_set_cdev(sch, cdev); | 783 | sch_set_cdev(sch, cdev); |
787 | cdev->private->schid = sch->schid; | 784 | cdev->private->schid = sch->schid; |
788 | cdev->ccwlock = sch->lock; | 785 | cdev->ccwlock = sch->lock; |
789 | device_trigger_reprobe(sch); | 786 | ccw_device_trigger_reprobe(cdev); |
790 | spin_unlock_irq(sch->lock); | 787 | spin_unlock_irq(sch->lock); |
791 | } | 788 | } |
792 | 789 | ||
@@ -1265,11 +1262,7 @@ static int io_subchannel_notify(struct subchannel *sch, int event) | |||
1265 | cdev = sch_get_cdev(sch); | 1262 | cdev = sch_get_cdev(sch); |
1266 | if (!cdev) | 1263 | if (!cdev) |
1267 | return 0; | 1264 | return 0; |
1268 | if (!cdev->drv) | 1265 | return ccw_device_notify(cdev, event); |
1269 | return 0; | ||
1270 | if (!cdev->online) | ||
1271 | return 0; | ||
1272 | return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0; | ||
1273 | } | 1266 | } |
1274 | 1267 | ||
1275 | static void io_subchannel_verify(struct subchannel *sch) | 1268 | static void io_subchannel_verify(struct subchannel *sch) |
@@ -1281,22 +1274,98 @@ static void io_subchannel_verify(struct subchannel *sch) | |||
1281 | dev_fsm_event(cdev, DEV_EVENT_VERIFY); | 1274 | dev_fsm_event(cdev, DEV_EVENT_VERIFY); |
1282 | } | 1275 | } |
1283 | 1276 | ||
1284 | static void io_subchannel_ioterm(struct subchannel *sch) | 1277 | static int check_for_io_on_path(struct subchannel *sch, int mask) |
1285 | { | 1278 | { |
1286 | struct ccw_device *cdev; | 1279 | int cc; |
1287 | 1280 | ||
1288 | cdev = sch_get_cdev(sch); | 1281 | cc = stsch(sch->schid, &sch->schib); |
1289 | if (!cdev) | 1282 | if (cc) |
1290 | return; | 1283 | return 0; |
1291 | /* Internal I/O will be retried by the interrupt handler. */ | 1284 | if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask) |
1292 | if (cdev->private->flags.intretry) | 1285 | return 1; |
1286 | return 0; | ||
1287 | } | ||
1288 | |||
1289 | static void terminate_internal_io(struct subchannel *sch, | ||
1290 | struct ccw_device *cdev) | ||
1291 | { | ||
1292 | if (cio_clear(sch)) { | ||
1293 | /* Recheck device in case clear failed. */ | ||
1294 | sch->lpm = 0; | ||
1295 | if (cdev->online) | ||
1296 | dev_fsm_event(cdev, DEV_EVENT_VERIFY); | ||
1297 | else | ||
1298 | css_schedule_eval(sch->schid); | ||
1293 | return; | 1299 | return; |
1300 | } | ||
1294 | cdev->private->state = DEV_STATE_CLEAR_VERIFY; | 1301 | cdev->private->state = DEV_STATE_CLEAR_VERIFY; |
1302 | /* Request retry of internal operation. */ | ||
1303 | cdev->private->flags.intretry = 1; | ||
1304 | /* Call handler. */ | ||
1295 | if (cdev->handler) | 1305 | if (cdev->handler) |
1296 | cdev->handler(cdev, cdev->private->intparm, | 1306 | cdev->handler(cdev, cdev->private->intparm, |
1297 | ERR_PTR(-EIO)); | 1307 | ERR_PTR(-EIO)); |
1298 | } | 1308 | } |
1299 | 1309 | ||
1310 | static void io_subchannel_terminate_path(struct subchannel *sch, u8 mask) | ||
1311 | { | ||
1312 | struct ccw_device *cdev; | ||
1313 | |||
1314 | cdev = sch_get_cdev(sch); | ||
1315 | if (!cdev) | ||
1316 | return; | ||
1317 | if (check_for_io_on_path(sch, mask)) { | ||
1318 | if (cdev->private->state == DEV_STATE_ONLINE) | ||
1319 | ccw_device_kill_io(cdev); | ||
1320 | else { | ||
1321 | terminate_internal_io(sch, cdev); | ||
1322 | /* Re-start path verification. */ | ||
1323 | dev_fsm_event(cdev, DEV_EVENT_VERIFY); | ||
1324 | } | ||
1325 | } else | ||
1326 | /* trigger path verification. */ | ||
1327 | dev_fsm_event(cdev, DEV_EVENT_VERIFY); | ||
1328 | |||
1329 | } | ||
1330 | |||
1331 | static int io_subchannel_chp_event(struct subchannel *sch, void *data, | ||
1332 | int event) | ||
1333 | { | ||
1334 | int mask; | ||
1335 | struct res_acc_data *res_data; | ||
1336 | |||
1337 | res_data = data; | ||
1338 | mask = chp_ssd_get_mask(&sch->ssd_info, res_data); | ||
1339 | if (!mask) | ||
1340 | return 0; | ||
1341 | switch (event) { | ||
1342 | case CHP_VARY_OFF: | ||
1343 | sch->opm &= ~mask; | ||
1344 | sch->lpm &= ~mask; | ||
1345 | io_subchannel_terminate_path(sch, mask); | ||
1346 | break; | ||
1347 | case CHP_VARY_ON: | ||
1348 | sch->opm |= mask; | ||
1349 | sch->lpm |= mask; | ||
1350 | io_subchannel_verify(sch); | ||
1351 | break; | ||
1352 | case CHP_OFFLINE: | ||
1353 | if (stsch(sch->schid, &sch->schib)) | ||
1354 | return -ENXIO; | ||
1355 | if (!css_sch_is_valid(&sch->schib)) | ||
1356 | return -ENODEV; | ||
1357 | io_subchannel_terminate_path(sch, mask); | ||
1358 | break; | ||
1359 | case CHP_ONLINE: | ||
1360 | if (stsch(sch->schid, &sch->schib)) | ||
1361 | return -ENXIO; | ||
1362 | sch->lpm |= mask & sch->opm; | ||
1363 | io_subchannel_verify(sch); | ||
1364 | break; | ||
1365 | } | ||
1366 | return 0; | ||
1367 | } | ||
1368 | |||
1300 | static void | 1369 | static void |
1301 | io_subchannel_shutdown(struct subchannel *sch) | 1370 | io_subchannel_shutdown(struct subchannel *sch) |
1302 | { | 1371 | { |
@@ -1326,6 +1395,195 @@ io_subchannel_shutdown(struct subchannel *sch) | |||
1326 | cio_disable_subchannel(sch); | 1395 | cio_disable_subchannel(sch); |
1327 | } | 1396 | } |
1328 | 1397 | ||
1398 | static int io_subchannel_get_status(struct subchannel *sch) | ||
1399 | { | ||
1400 | struct schib schib; | ||
1401 | |||
1402 | if (stsch(sch->schid, &schib) || !schib.pmcw.dnv) | ||
1403 | return CIO_GONE; | ||
1404 | if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev)) | ||
1405 | return CIO_REVALIDATE; | ||
1406 | if (!sch->lpm) | ||
1407 | return CIO_NO_PATH; | ||
1408 | return CIO_OPER; | ||
1409 | } | ||
1410 | |||
1411 | static int device_is_disconnected(struct ccw_device *cdev) | ||
1412 | { | ||
1413 | if (!cdev) | ||
1414 | return 0; | ||
1415 | return (cdev->private->state == DEV_STATE_DISCONNECTED || | ||
1416 | cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID); | ||
1417 | } | ||
1418 | |||
1419 | static int recovery_check(struct device *dev, void *data) | ||
1420 | { | ||
1421 | struct ccw_device *cdev = to_ccwdev(dev); | ||
1422 | int *redo = data; | ||
1423 | |||
1424 | spin_lock_irq(cdev->ccwlock); | ||
1425 | switch (cdev->private->state) { | ||
1426 | case DEV_STATE_DISCONNECTED: | ||
1427 | CIO_MSG_EVENT(3, "recovery: trigger 0.%x.%04x\n", | ||
1428 | cdev->private->dev_id.ssid, | ||
1429 | cdev->private->dev_id.devno); | ||
1430 | dev_fsm_event(cdev, DEV_EVENT_VERIFY); | ||
1431 | *redo = 1; | ||
1432 | break; | ||
1433 | case DEV_STATE_DISCONNECTED_SENSE_ID: | ||
1434 | *redo = 1; | ||
1435 | break; | ||
1436 | } | ||
1437 | spin_unlock_irq(cdev->ccwlock); | ||
1438 | |||
1439 | return 0; | ||
1440 | } | ||
1441 | |||
1442 | static void recovery_work_func(struct work_struct *unused) | ||
1443 | { | ||
1444 | int redo = 0; | ||
1445 | |||
1446 | bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check); | ||
1447 | if (redo) { | ||
1448 | spin_lock_irq(&recovery_lock); | ||
1449 | if (!timer_pending(&recovery_timer)) { | ||
1450 | if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1) | ||
1451 | recovery_phase++; | ||
1452 | mod_timer(&recovery_timer, jiffies + | ||
1453 | recovery_delay[recovery_phase] * HZ); | ||
1454 | } | ||
1455 | spin_unlock_irq(&recovery_lock); | ||
1456 | } else | ||
1457 | CIO_MSG_EVENT(4, "recovery: end\n"); | ||
1458 | } | ||
1459 | |||
1460 | static DECLARE_WORK(recovery_work, recovery_work_func); | ||
1461 | |||
1462 | static void recovery_func(unsigned long data) | ||
1463 | { | ||
1464 | /* | ||
1465 | * We can't do our recovery in softirq context and it's not | ||
1466 | * performance critical, so we schedule it. | ||
1467 | */ | ||
1468 | schedule_work(&recovery_work); | ||
1469 | } | ||
1470 | |||
1471 | static void ccw_device_schedule_recovery(void) | ||
1472 | { | ||
1473 | unsigned long flags; | ||
1474 | |||
1475 | CIO_MSG_EVENT(4, "recovery: schedule\n"); | ||
1476 | spin_lock_irqsave(&recovery_lock, flags); | ||
1477 | if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) { | ||
1478 | recovery_phase = 0; | ||
1479 | mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ); | ||
1480 | } | ||
1481 | spin_unlock_irqrestore(&recovery_lock, flags); | ||
1482 | } | ||
1483 | |||
1484 | static void device_set_disconnected(struct ccw_device *cdev) | ||
1485 | { | ||
1486 | if (!cdev) | ||
1487 | return; | ||
1488 | ccw_device_set_timeout(cdev, 0); | ||
1489 | cdev->private->flags.fake_irb = 0; | ||
1490 | cdev->private->state = DEV_STATE_DISCONNECTED; | ||
1491 | if (cdev->online) | ||
1492 | ccw_device_schedule_recovery(); | ||
1493 | } | ||
1494 | |||
1495 | static int io_subchannel_sch_event(struct subchannel *sch, int slow) | ||
1496 | { | ||
1497 | int event, ret, disc; | ||
1498 | unsigned long flags; | ||
1499 | enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action; | ||
1500 | struct ccw_device *cdev; | ||
1501 | |||
1502 | spin_lock_irqsave(sch->lock, flags); | ||
1503 | cdev = sch_get_cdev(sch); | ||
1504 | disc = device_is_disconnected(cdev); | ||
1505 | if (disc && slow) { | ||
1506 | /* Disconnected devices are evaluated directly only.*/ | ||
1507 | spin_unlock_irqrestore(sch->lock, flags); | ||
1508 | return 0; | ||
1509 | } | ||
1510 | /* No interrupt after machine check - kill pending timers. */ | ||
1511 | if (cdev) | ||
1512 | ccw_device_set_timeout(cdev, 0); | ||
1513 | if (!disc && !slow) { | ||
1514 | /* Non-disconnected devices are evaluated on the slow path. */ | ||
1515 | spin_unlock_irqrestore(sch->lock, flags); | ||
1516 | return -EAGAIN; | ||
1517 | } | ||
1518 | event = io_subchannel_get_status(sch); | ||
1519 | CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n", | ||
1520 | sch->schid.ssid, sch->schid.sch_no, event, | ||
1521 | disc ? "disconnected" : "normal", | ||
1522 | slow ? "slow" : "fast"); | ||
1523 | /* Analyze subchannel status. */ | ||
1524 | action = NONE; | ||
1525 | switch (event) { | ||
1526 | case CIO_NO_PATH: | ||
1527 | if (disc) { | ||
1528 | /* Check if paths have become available. */ | ||
1529 | action = REPROBE; | ||
1530 | break; | ||
1531 | } | ||
1532 | /* fall through */ | ||
1533 | case CIO_GONE: | ||
1534 | /* Prevent unwanted effects when opening lock. */ | ||
1535 | cio_disable_subchannel(sch); | ||
1536 | device_set_disconnected(cdev); | ||
1537 | /* Ask driver what to do with device. */ | ||
1538 | action = UNREGISTER; | ||
1539 | spin_unlock_irqrestore(sch->lock, flags); | ||
1540 | ret = io_subchannel_notify(sch, event); | ||
1541 | spin_lock_irqsave(sch->lock, flags); | ||
1542 | if (ret) | ||
1543 | action = NONE; | ||
1544 | break; | ||
1545 | case CIO_REVALIDATE: | ||
1546 | /* Device will be removed, so no notify necessary. */ | ||
1547 | if (disc) | ||
1548 | /* Reprobe because immediate unregister might block. */ | ||
1549 | action = REPROBE; | ||
1550 | else | ||
1551 | action = UNREGISTER_PROBE; | ||
1552 | break; | ||
1553 | case CIO_OPER: | ||
1554 | if (disc) | ||
1555 | /* Get device operational again. */ | ||
1556 | action = REPROBE; | ||
1557 | break; | ||
1558 | } | ||
1559 | /* Perform action. */ | ||
1560 | ret = 0; | ||
1561 | switch (action) { | ||
1562 | case UNREGISTER: | ||
1563 | case UNREGISTER_PROBE: | ||
1564 | /* Unregister device (will use subchannel lock). */ | ||
1565 | spin_unlock_irqrestore(sch->lock, flags); | ||
1566 | css_sch_device_unregister(sch); | ||
1567 | spin_lock_irqsave(sch->lock, flags); | ||
1568 | |||
1569 | /* Reset intparm to zeroes. */ | ||
1570 | sch->schib.pmcw.intparm = 0; | ||
1571 | cio_modify(sch); | ||
1572 | break; | ||
1573 | case REPROBE: | ||
1574 | ccw_device_trigger_reprobe(cdev); | ||
1575 | break; | ||
1576 | default: | ||
1577 | break; | ||
1578 | } | ||
1579 | spin_unlock_irqrestore(sch->lock, flags); | ||
1580 | /* Probe if necessary. */ | ||
1581 | if (action == UNREGISTER_PROBE) | ||
1582 | ret = css_probe_device(sch->schid); | ||
1583 | |||
1584 | return ret; | ||
1585 | } | ||
1586 | |||
1329 | #ifdef CONFIG_CCW_CONSOLE | 1587 | #ifdef CONFIG_CCW_CONSOLE |
1330 | static struct ccw_device console_cdev; | 1588 | static struct ccw_device console_cdev; |
1331 | static struct ccw_device_private console_private; | 1589 | static struct ccw_device_private console_private; |
@@ -1558,71 +1816,6 @@ ccw_device_get_subchannel_id(struct ccw_device *cdev) | |||
1558 | return sch->schid; | 1816 | return sch->schid; |
1559 | } | 1817 | } |
1560 | 1818 | ||
1561 | static int recovery_check(struct device *dev, void *data) | ||
1562 | { | ||
1563 | struct ccw_device *cdev = to_ccwdev(dev); | ||
1564 | int *redo = data; | ||
1565 | |||
1566 | spin_lock_irq(cdev->ccwlock); | ||
1567 | switch (cdev->private->state) { | ||
1568 | case DEV_STATE_DISCONNECTED: | ||
1569 | CIO_MSG_EVENT(4, "recovery: trigger 0.%x.%04x\n", | ||
1570 | cdev->private->dev_id.ssid, | ||
1571 | cdev->private->dev_id.devno); | ||
1572 | dev_fsm_event(cdev, DEV_EVENT_VERIFY); | ||
1573 | *redo = 1; | ||
1574 | break; | ||
1575 | case DEV_STATE_DISCONNECTED_SENSE_ID: | ||
1576 | *redo = 1; | ||
1577 | break; | ||
1578 | } | ||
1579 | spin_unlock_irq(cdev->ccwlock); | ||
1580 | |||
1581 | return 0; | ||
1582 | } | ||
1583 | |||
1584 | static void recovery_work_func(struct work_struct *unused) | ||
1585 | { | ||
1586 | int redo = 0; | ||
1587 | |||
1588 | bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check); | ||
1589 | if (redo) { | ||
1590 | spin_lock_irq(&recovery_lock); | ||
1591 | if (!timer_pending(&recovery_timer)) { | ||
1592 | if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1) | ||
1593 | recovery_phase++; | ||
1594 | mod_timer(&recovery_timer, jiffies + | ||
1595 | recovery_delay[recovery_phase] * HZ); | ||
1596 | } | ||
1597 | spin_unlock_irq(&recovery_lock); | ||
1598 | } else | ||
1599 | CIO_MSG_EVENT(4, "recovery: end\n"); | ||
1600 | } | ||
1601 | |||
1602 | static DECLARE_WORK(recovery_work, recovery_work_func); | ||
1603 | |||
1604 | static void recovery_func(unsigned long data) | ||
1605 | { | ||
1606 | /* | ||
1607 | * We can't do our recovery in softirq context and it's not | ||
1608 | * performance critical, so we schedule it. | ||
1609 | */ | ||
1610 | schedule_work(&recovery_work); | ||
1611 | } | ||
1612 | |||
1613 | void ccw_device_schedule_recovery(void) | ||
1614 | { | ||
1615 | unsigned long flags; | ||
1616 | |||
1617 | CIO_MSG_EVENT(4, "recovery: schedule\n"); | ||
1618 | spin_lock_irqsave(&recovery_lock, flags); | ||
1619 | if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) { | ||
1620 | recovery_phase = 0; | ||
1621 | mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ); | ||
1622 | } | ||
1623 | spin_unlock_irqrestore(&recovery_lock, flags); | ||
1624 | } | ||
1625 | |||
1626 | MODULE_LICENSE("GPL"); | 1819 | MODULE_LICENSE("GPL"); |
1627 | EXPORT_SYMBOL(ccw_device_set_online); | 1820 | EXPORT_SYMBOL(ccw_device_set_online); |
1628 | EXPORT_SYMBOL(ccw_device_set_offline); | 1821 | EXPORT_SYMBOL(ccw_device_set_offline); |
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index cb08092be39f..9800a8335a3f 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h | |||
@@ -88,8 +88,6 @@ int ccw_device_recognition(struct ccw_device *); | |||
88 | int ccw_device_online(struct ccw_device *); | 88 | int ccw_device_online(struct ccw_device *); |
89 | int ccw_device_offline(struct ccw_device *); | 89 | int ccw_device_offline(struct ccw_device *); |
90 | 90 | ||
91 | void ccw_device_schedule_recovery(void); | ||
92 | |||
93 | /* Function prototypes for device status and basic sense stuff. */ | 91 | /* Function prototypes for device status and basic sense stuff. */ |
94 | void ccw_device_accumulate_irb(struct ccw_device *, struct irb *); | 92 | void ccw_device_accumulate_irb(struct ccw_device *, struct irb *); |
95 | void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *); | 93 | void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *); |
@@ -118,6 +116,11 @@ int ccw_device_call_handler(struct ccw_device *); | |||
118 | 116 | ||
119 | int ccw_device_stlck(struct ccw_device *); | 117 | int ccw_device_stlck(struct ccw_device *); |
120 | 118 | ||
119 | /* Helper function for machine check handling. */ | ||
120 | void ccw_device_trigger_reprobe(struct ccw_device *); | ||
121 | void ccw_device_kill_io(struct ccw_device *); | ||
122 | int ccw_device_notify(struct ccw_device *, int); | ||
123 | |||
121 | /* qdio needs this. */ | 124 | /* qdio needs this. */ |
122 | void ccw_device_set_timeout(struct ccw_device *, int); | 125 | void ccw_device_set_timeout(struct ccw_device *, int); |
123 | extern struct subchannel_id ccw_device_get_subchannel_id(struct ccw_device *); | 126 | extern struct subchannel_id ccw_device_get_subchannel_id(struct ccw_device *); |
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index e268d5a77c12..c9b97cbc2203 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c | |||
@@ -2,8 +2,7 @@ | |||
2 | * drivers/s390/cio/device_fsm.c | 2 | * drivers/s390/cio/device_fsm.c |
3 | * finite state machine for device handling | 3 | * finite state machine for device handling |
4 | * | 4 | * |
5 | * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH, | 5 | * Copyright IBM Corp. 2002,2008 |
6 | * IBM Corporation | ||
7 | * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) | 6 | * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) |
8 | * Martin Schwidefsky (schwidefsky@de.ibm.com) | 7 | * Martin Schwidefsky (schwidefsky@de.ibm.com) |
9 | */ | 8 | */ |
@@ -27,65 +26,6 @@ | |||
27 | 26 | ||
28 | static int timeout_log_enabled; | 27 | static int timeout_log_enabled; |
29 | 28 | ||
30 | int | ||
31 | device_is_online(struct subchannel *sch) | ||
32 | { | ||
33 | struct ccw_device *cdev; | ||
34 | |||
35 | cdev = sch_get_cdev(sch); | ||
36 | if (!cdev) | ||
37 | return 0; | ||
38 | return (cdev->private->state == DEV_STATE_ONLINE); | ||
39 | } | ||
40 | |||
41 | int | ||
42 | device_is_disconnected(struct subchannel *sch) | ||
43 | { | ||
44 | struct ccw_device *cdev; | ||
45 | |||
46 | cdev = sch_get_cdev(sch); | ||
47 | if (!cdev) | ||
48 | return 0; | ||
49 | return (cdev->private->state == DEV_STATE_DISCONNECTED || | ||
50 | cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID); | ||
51 | } | ||
52 | |||
53 | void | ||
54 | device_set_disconnected(struct subchannel *sch) | ||
55 | { | ||
56 | struct ccw_device *cdev; | ||
57 | |||
58 | cdev = sch_get_cdev(sch); | ||
59 | if (!cdev) | ||
60 | return; | ||
61 | ccw_device_set_timeout(cdev, 0); | ||
62 | cdev->private->flags.fake_irb = 0; | ||
63 | cdev->private->state = DEV_STATE_DISCONNECTED; | ||
64 | if (cdev->online) | ||
65 | ccw_device_schedule_recovery(); | ||
66 | } | ||
67 | |||
68 | void device_set_intretry(struct subchannel *sch) | ||
69 | { | ||
70 | struct ccw_device *cdev; | ||
71 | |||
72 | cdev = sch_get_cdev(sch); | ||
73 | if (!cdev) | ||
74 | return; | ||
75 | cdev->private->flags.intretry = 1; | ||
76 | } | ||
77 | |||
78 | int device_trigger_verify(struct subchannel *sch) | ||
79 | { | ||
80 | struct ccw_device *cdev; | ||
81 | |||
82 | cdev = sch_get_cdev(sch); | ||
83 | if (!cdev || !cdev->online) | ||
84 | return -EINVAL; | ||
85 | dev_fsm_event(cdev, DEV_EVENT_VERIFY); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | static int __init ccw_timeout_log_setup(char *unused) | 29 | static int __init ccw_timeout_log_setup(char *unused) |
90 | { | 30 | { |
91 | timeout_log_enabled = 1; | 31 | timeout_log_enabled = 1; |
@@ -171,18 +111,6 @@ ccw_device_set_timeout(struct ccw_device *cdev, int expires) | |||
171 | add_timer(&cdev->private->timer); | 111 | add_timer(&cdev->private->timer); |
172 | } | 112 | } |
173 | 113 | ||
174 | /* Kill any pending timers after machine check. */ | ||
175 | void | ||
176 | device_kill_pending_timer(struct subchannel *sch) | ||
177 | { | ||
178 | struct ccw_device *cdev; | ||
179 | |||
180 | cdev = sch_get_cdev(sch); | ||
181 | if (!cdev) | ||
182 | return; | ||
183 | ccw_device_set_timeout(cdev, 0); | ||
184 | } | ||
185 | |||
186 | /* | 114 | /* |
187 | * Cancel running i/o. This is called repeatedly since halt/clear are | 115 | * Cancel running i/o. This is called repeatedly since halt/clear are |
188 | * asynchronous operations. We do one try with cio_cancel, two tries | 116 | * asynchronous operations. We do one try with cio_cancel, two tries |
@@ -388,25 +316,27 @@ ccw_device_sense_id_done(struct ccw_device *cdev, int err) | |||
388 | } | 316 | } |
389 | } | 317 | } |
390 | 318 | ||
319 | int ccw_device_notify(struct ccw_device *cdev, int event) | ||
320 | { | ||
321 | if (!cdev->drv) | ||
322 | return 0; | ||
323 | if (!cdev->online) | ||
324 | return 0; | ||
325 | return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0; | ||
326 | } | ||
327 | |||
391 | static void | 328 | static void |
392 | ccw_device_oper_notify(struct work_struct *work) | 329 | ccw_device_oper_notify(struct work_struct *work) |
393 | { | 330 | { |
394 | struct ccw_device_private *priv; | 331 | struct ccw_device_private *priv; |
395 | struct ccw_device *cdev; | 332 | struct ccw_device *cdev; |
396 | struct subchannel *sch; | ||
397 | int ret; | 333 | int ret; |
398 | unsigned long flags; | 334 | unsigned long flags; |
399 | 335 | ||
400 | priv = container_of(work, struct ccw_device_private, kick_work); | 336 | priv = container_of(work, struct ccw_device_private, kick_work); |
401 | cdev = priv->cdev; | 337 | cdev = priv->cdev; |
338 | ret = ccw_device_notify(cdev, CIO_OPER); | ||
402 | spin_lock_irqsave(cdev->ccwlock, flags); | 339 | spin_lock_irqsave(cdev->ccwlock, flags); |
403 | sch = to_subchannel(cdev->dev.parent); | ||
404 | if (sch->driver && sch->driver->notify) { | ||
405 | spin_unlock_irqrestore(cdev->ccwlock, flags); | ||
406 | ret = sch->driver->notify(sch, CIO_OPER); | ||
407 | spin_lock_irqsave(cdev->ccwlock, flags); | ||
408 | } else | ||
409 | ret = 0; | ||
410 | if (ret) { | 340 | if (ret) { |
411 | /* Reenable channel measurements, if needed. */ | 341 | /* Reenable channel measurements, if needed. */ |
412 | spin_unlock_irqrestore(cdev->ccwlock, flags); | 342 | spin_unlock_irqrestore(cdev->ccwlock, flags); |
@@ -986,12 +916,10 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event) | |||
986 | ERR_PTR(-EIO)); | 916 | ERR_PTR(-EIO)); |
987 | } | 917 | } |
988 | 918 | ||
989 | void device_kill_io(struct subchannel *sch) | 919 | void ccw_device_kill_io(struct ccw_device *cdev) |
990 | { | 920 | { |
991 | int ret; | 921 | int ret; |
992 | struct ccw_device *cdev; | ||
993 | 922 | ||
994 | cdev = sch_get_cdev(sch); | ||
995 | ret = ccw_device_cancel_halt_clear(cdev); | 923 | ret = ccw_device_cancel_halt_clear(cdev); |
996 | if (ret == -EBUSY) { | 924 | if (ret == -EBUSY) { |
997 | ccw_device_set_timeout(cdev, 3*HZ); | 925 | ccw_device_set_timeout(cdev, 3*HZ); |
@@ -1055,17 +983,14 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event) | |||
1055 | ccw_device_sense_id_start(cdev); | 983 | ccw_device_sense_id_start(cdev); |
1056 | } | 984 | } |
1057 | 985 | ||
1058 | void | 986 | void ccw_device_trigger_reprobe(struct ccw_device *cdev) |
1059 | device_trigger_reprobe(struct subchannel *sch) | ||
1060 | { | 987 | { |
1061 | struct ccw_device *cdev; | 988 | struct subchannel *sch; |
1062 | 989 | ||
1063 | cdev = sch_get_cdev(sch); | ||
1064 | if (!cdev) | ||
1065 | return; | ||
1066 | if (cdev->private->state != DEV_STATE_DISCONNECTED) | 990 | if (cdev->private->state != DEV_STATE_DISCONNECTED) |
1067 | return; | 991 | return; |
1068 | 992 | ||
993 | sch = to_subchannel(cdev->dev.parent); | ||
1069 | /* Update some values. */ | 994 | /* Update some values. */ |
1070 | if (stsch(sch->schid, &sch->schib)) | 995 | if (stsch(sch->schid, &sch->schib)) |
1071 | return; | 996 | return; |
@@ -1081,7 +1006,6 @@ device_trigger_reprobe(struct subchannel *sch) | |||
1081 | sch->schib.pmcw.ena = 0; | 1006 | sch->schib.pmcw.ena = 0; |
1082 | if ((sch->lpm & (sch->lpm - 1)) != 0) | 1007 | if ((sch->lpm & (sch->lpm - 1)) != 0) |
1083 | sch->schib.pmcw.mp = 1; | 1008 | sch->schib.pmcw.mp = 1; |
1084 | sch->schib.pmcw.intparm = (u32)(addr_t)sch; | ||
1085 | /* We should also udate ssd info, but this has to wait. */ | 1009 | /* We should also udate ssd info, but this has to wait. */ |
1086 | /* Check if this is another device which appeared on the same sch. */ | 1010 | /* Check if this is another device which appeared on the same sch. */ |
1087 | if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { | 1011 | if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) { |