diff options
author | Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | 2007-04-27 10:01:34 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2007-04-27 10:01:40 -0400 |
commit | 83b3370c79b91b9be3f6540c3c914e689134b45f (patch) | |
tree | ad7c062b260c0259c74e45ff869208c1ad139629 /drivers/s390/cio/chsc.c | |
parent | 387b734fc2b55f776b192c7afdfd892ba42347d4 (diff) |
[S390] cio: replace subchannel evaluation queue with bitmap
Use a bitmap for indicating which subchannels require evaluation
instead of allocating memory for each evaluation request. This
approach reduces memory consumption during recovery in case of
massive evaluation request occurrence and removes the need for
memory allocation failure handling.
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/chsc.c')
-rw-r--r-- | drivers/s390/cio/chsc.c | 130 |
1 files changed, 33 insertions, 97 deletions
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 02615eb43984..89a130a62654 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c | |||
@@ -195,12 +195,8 @@ static void terminate_internal_io(struct subchannel *sch) | |||
195 | if (cio_clear(sch)) { | 195 | if (cio_clear(sch)) { |
196 | /* Recheck device in case clear failed. */ | 196 | /* Recheck device in case clear failed. */ |
197 | sch->lpm = 0; | 197 | sch->lpm = 0; |
198 | if (device_trigger_verify(sch) != 0) { | 198 | if (device_trigger_verify(sch) != 0) |
199 | if(css_enqueue_subchannel_slow(sch->schid)) { | 199 | css_schedule_eval(sch->schid); |
200 | css_clear_subchannel_slow_list(); | ||
201 | need_rescan = 1; | ||
202 | } | ||
203 | } | ||
204 | return; | 200 | return; |
205 | } | 201 | } |
206 | /* Request retry of internal operation. */ | 202 | /* Request retry of internal operation. */ |
@@ -262,11 +258,8 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) | |||
262 | 258 | ||
263 | out_unreg: | 259 | out_unreg: |
264 | sch->lpm = 0; | 260 | sch->lpm = 0; |
265 | if (css_enqueue_subchannel_slow(sch->schid)) { | ||
266 | css_clear_subchannel_slow_list(); | ||
267 | need_rescan = 1; | ||
268 | } | ||
269 | spin_unlock_irq(sch->lock); | 261 | spin_unlock_irq(sch->lock); |
262 | css_schedule_eval(sch->schid); | ||
270 | return 0; | 263 | return 0; |
271 | } | 264 | } |
272 | 265 | ||
@@ -281,9 +274,6 @@ void chsc_chp_offline(struct chp_id chpid) | |||
281 | return; | 274 | return; |
282 | bus_for_each_dev(&css_bus_type, NULL, &chpid, | 275 | bus_for_each_dev(&css_bus_type, NULL, &chpid, |
283 | s390_subchannel_remove_chpid); | 276 | s390_subchannel_remove_chpid); |
284 | |||
285 | if (need_rescan || css_slow_subchannels_exist()) | ||
286 | queue_work(slow_path_wq, &slow_path_work); | ||
287 | } | 277 | } |
288 | 278 | ||
289 | struct res_acc_data { | 279 | struct res_acc_data { |
@@ -331,7 +321,6 @@ static int | |||
331 | s390_process_res_acc_new_sch(struct subchannel_id schid) | 321 | s390_process_res_acc_new_sch(struct subchannel_id schid) |
332 | { | 322 | { |
333 | struct schib schib; | 323 | struct schib schib; |
334 | int ret; | ||
335 | /* | 324 | /* |
336 | * We don't know the device yet, but since a path | 325 | * We don't know the device yet, but since a path |
337 | * may be available now to the device we'll have | 326 | * may be available now to the device we'll have |
@@ -342,15 +331,10 @@ s390_process_res_acc_new_sch(struct subchannel_id schid) | |||
342 | */ | 331 | */ |
343 | if (stsch_err(schid, &schib)) | 332 | if (stsch_err(schid, &schib)) |
344 | /* We're through */ | 333 | /* We're through */ |
345 | return need_rescan ? -EAGAIN : -ENXIO; | 334 | return -ENXIO; |
346 | 335 | ||
347 | /* Put it on the slow path. */ | 336 | /* Put it on the slow path. */ |
348 | ret = css_enqueue_subchannel_slow(schid); | 337 | css_schedule_eval(schid); |
349 | if (ret) { | ||
350 | css_clear_subchannel_slow_list(); | ||
351 | need_rescan = 1; | ||
352 | return -EAGAIN; | ||
353 | } | ||
354 | return 0; | 338 | return 0; |
355 | } | 339 | } |
356 | 340 | ||
@@ -392,10 +376,8 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) | |||
392 | } | 376 | } |
393 | 377 | ||
394 | 378 | ||
395 | static int | 379 | static void s390_process_res_acc (struct res_acc_data *res_data) |
396 | s390_process_res_acc (struct res_acc_data *res_data) | ||
397 | { | 380 | { |
398 | int rc; | ||
399 | char dbf_txt[15]; | 381 | char dbf_txt[15]; |
400 | 382 | ||
401 | sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid, | 383 | sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid, |
@@ -413,12 +395,7 @@ s390_process_res_acc (struct res_acc_data *res_data) | |||
413 | * The more information we have (info), the less scanning | 395 | * The more information we have (info), the less scanning |
414 | * will we have to do. | 396 | * will we have to do. |
415 | */ | 397 | */ |
416 | rc = for_each_subchannel(__s390_process_res_acc, res_data); | 398 | for_each_subchannel(__s390_process_res_acc, res_data); |
417 | if (css_slow_subchannels_exist()) | ||
418 | rc = -EAGAIN; | ||
419 | else if (rc != -EAGAIN) | ||
420 | rc = 0; | ||
421 | return rc; | ||
422 | } | 399 | } |
423 | 400 | ||
424 | static int | 401 | static int |
@@ -470,7 +447,7 @@ struct chsc_sei_area { | |||
470 | /* ccdf has to be big enough for a link-incident record */ | 447 | /* ccdf has to be big enough for a link-incident record */ |
471 | } __attribute__ ((packed)); | 448 | } __attribute__ ((packed)); |
472 | 449 | ||
473 | static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) | 450 | static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) |
474 | { | 451 | { |
475 | struct chp_id chpid; | 452 | struct chp_id chpid; |
476 | int id; | 453 | int id; |
@@ -478,7 +455,7 @@ static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) | |||
478 | CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n", | 455 | CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n", |
479 | sei_area->rs, sei_area->rsid); | 456 | sei_area->rs, sei_area->rsid); |
480 | if (sei_area->rs != 4) | 457 | if (sei_area->rs != 4) |
481 | return 0; | 458 | return; |
482 | id = __get_chpid_from_lir(sei_area->ccdf); | 459 | id = __get_chpid_from_lir(sei_area->ccdf); |
483 | if (id < 0) | 460 | if (id < 0) |
484 | CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n"); | 461 | CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n"); |
@@ -487,21 +464,18 @@ static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) | |||
487 | chpid.id = id; | 464 | chpid.id = id; |
488 | chsc_chp_offline(chpid); | 465 | chsc_chp_offline(chpid); |
489 | } | 466 | } |
490 | |||
491 | return 0; | ||
492 | } | 467 | } |
493 | 468 | ||
494 | static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) | 469 | static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) |
495 | { | 470 | { |
496 | struct res_acc_data res_data; | 471 | struct res_acc_data res_data; |
497 | struct chp_id chpid; | 472 | struct chp_id chpid; |
498 | int status; | 473 | int status; |
499 | int rc; | ||
500 | 474 | ||
501 | CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, " | 475 | CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, " |
502 | "rs_id=%04x)\n", sei_area->rs, sei_area->rsid); | 476 | "rs_id=%04x)\n", sei_area->rs, sei_area->rsid); |
503 | if (sei_area->rs != 4) | 477 | if (sei_area->rs != 4) |
504 | return 0; | 478 | return; |
505 | chp_id_init(&chpid); | 479 | chp_id_init(&chpid); |
506 | chpid.id = sei_area->rsid; | 480 | chpid.id = sei_area->rsid; |
507 | /* allocate a new channel path structure, if needed */ | 481 | /* allocate a new channel path structure, if needed */ |
@@ -509,7 +483,7 @@ static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) | |||
509 | if (status < 0) | 483 | if (status < 0) |
510 | chp_new(chpid); | 484 | chp_new(chpid); |
511 | else if (!status) | 485 | else if (!status) |
512 | return 0; | 486 | return; |
513 | memset(&res_data, 0, sizeof(struct res_acc_data)); | 487 | memset(&res_data, 0, sizeof(struct res_acc_data)); |
514 | res_data.chpid = chpid; | 488 | res_data.chpid = chpid; |
515 | if ((sei_area->vf & 0xc0) != 0) { | 489 | if ((sei_area->vf & 0xc0) != 0) { |
@@ -521,9 +495,7 @@ static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) | |||
521 | /* link address */ | 495 | /* link address */ |
522 | res_data.fla_mask = 0xff00; | 496 | res_data.fla_mask = 0xff00; |
523 | } | 497 | } |
524 | rc = s390_process_res_acc(&res_data); | 498 | s390_process_res_acc(&res_data); |
525 | |||
526 | return rc; | ||
527 | } | 499 | } |
528 | 500 | ||
529 | struct chp_config_data { | 501 | struct chp_config_data { |
@@ -532,7 +504,7 @@ struct chp_config_data { | |||
532 | u8 pc; | 504 | u8 pc; |
533 | }; | 505 | }; |
534 | 506 | ||
535 | static int chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) | 507 | static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) |
536 | { | 508 | { |
537 | struct chp_config_data *data; | 509 | struct chp_config_data *data; |
538 | struct chp_id chpid; | 510 | struct chp_id chpid; |
@@ -540,7 +512,7 @@ static int chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) | |||
540 | 512 | ||
541 | CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n"); | 513 | CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n"); |
542 | if (sei_area->rs != 0) | 514 | if (sei_area->rs != 0) |
543 | return 0; | 515 | return; |
544 | data = (struct chp_config_data *) &(sei_area->ccdf); | 516 | data = (struct chp_config_data *) &(sei_area->ccdf); |
545 | chp_id_init(&chpid); | 517 | chp_id_init(&chpid); |
546 | for (num = 0; num <= __MAX_CHPID; num++) { | 518 | for (num = 0; num <= __MAX_CHPID; num++) { |
@@ -561,52 +533,44 @@ static int chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) | |||
561 | break; | 533 | break; |
562 | } | 534 | } |
563 | } | 535 | } |
564 | |||
565 | return 0; | ||
566 | } | 536 | } |
567 | 537 | ||
568 | static int chsc_process_sei(struct chsc_sei_area *sei_area) | 538 | static void chsc_process_sei(struct chsc_sei_area *sei_area) |
569 | { | 539 | { |
570 | int rc; | ||
571 | |||
572 | /* Check if we might have lost some information. */ | 540 | /* Check if we might have lost some information. */ |
573 | if (sei_area->flags & 0x40) | 541 | if (sei_area->flags & 0x40) { |
574 | CIO_CRW_EVENT(2, "chsc: event overflow\n"); | 542 | CIO_CRW_EVENT(2, "chsc: event overflow\n"); |
543 | css_schedule_eval_all(); | ||
544 | } | ||
575 | /* which kind of information was stored? */ | 545 | /* which kind of information was stored? */ |
576 | rc = 0; | ||
577 | switch (sei_area->cc) { | 546 | switch (sei_area->cc) { |
578 | case 1: /* link incident*/ | 547 | case 1: /* link incident*/ |
579 | rc = chsc_process_sei_link_incident(sei_area); | 548 | chsc_process_sei_link_incident(sei_area); |
580 | break; | 549 | break; |
581 | case 2: /* i/o resource accessibiliy */ | 550 | case 2: /* i/o resource accessibiliy */ |
582 | rc = chsc_process_sei_res_acc(sei_area); | 551 | chsc_process_sei_res_acc(sei_area); |
583 | break; | 552 | break; |
584 | case 8: /* channel-path-configuration notification */ | 553 | case 8: /* channel-path-configuration notification */ |
585 | rc = chsc_process_sei_chp_config(sei_area); | 554 | chsc_process_sei_chp_config(sei_area); |
586 | break; | 555 | break; |
587 | default: /* other stuff */ | 556 | default: /* other stuff */ |
588 | CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n", | 557 | CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n", |
589 | sei_area->cc); | 558 | sei_area->cc); |
590 | break; | 559 | break; |
591 | } | 560 | } |
592 | |||
593 | return rc; | ||
594 | } | 561 | } |
595 | 562 | ||
596 | int chsc_process_crw(void) | 563 | void chsc_process_crw(void) |
597 | { | 564 | { |
598 | struct chsc_sei_area *sei_area; | 565 | struct chsc_sei_area *sei_area; |
599 | int ret; | ||
600 | int rc; | ||
601 | 566 | ||
602 | if (!sei_page) | 567 | if (!sei_page) |
603 | return 0; | 568 | return; |
604 | /* Access to sei_page is serialized through machine check handler | 569 | /* Access to sei_page is serialized through machine check handler |
605 | * thread, so no need for locking. */ | 570 | * thread, so no need for locking. */ |
606 | sei_area = sei_page; | 571 | sei_area = sei_page; |
607 | 572 | ||
608 | CIO_TRACE_EVENT( 2, "prcss"); | 573 | CIO_TRACE_EVENT( 2, "prcss"); |
609 | ret = 0; | ||
610 | do { | 574 | do { |
611 | memset(sei_area, 0, sizeof(*sei_area)); | 575 | memset(sei_area, 0, sizeof(*sei_area)); |
612 | sei_area->request.length = 0x0010; | 576 | sei_area->request.length = 0x0010; |
@@ -616,37 +580,26 @@ int chsc_process_crw(void) | |||
616 | 580 | ||
617 | if (sei_area->response.code == 0x0001) { | 581 | if (sei_area->response.code == 0x0001) { |
618 | CIO_CRW_EVENT(4, "chsc: sei successful\n"); | 582 | CIO_CRW_EVENT(4, "chsc: sei successful\n"); |
619 | rc = chsc_process_sei(sei_area); | 583 | chsc_process_sei(sei_area); |
620 | if (rc) | ||
621 | ret = rc; | ||
622 | } else { | 584 | } else { |
623 | CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", | 585 | CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", |
624 | sei_area->response.code); | 586 | sei_area->response.code); |
625 | ret = 0; | ||
626 | break; | 587 | break; |
627 | } | 588 | } |
628 | } while (sei_area->flags & 0x80); | 589 | } while (sei_area->flags & 0x80); |
629 | |||
630 | return ret; | ||
631 | } | 590 | } |
632 | 591 | ||
633 | static int | 592 | static int |
634 | __chp_add_new_sch(struct subchannel_id schid) | 593 | __chp_add_new_sch(struct subchannel_id schid) |
635 | { | 594 | { |
636 | struct schib schib; | 595 | struct schib schib; |
637 | int ret; | ||
638 | 596 | ||
639 | if (stsch_err(schid, &schib)) | 597 | if (stsch_err(schid, &schib)) |
640 | /* We're through */ | 598 | /* We're through */ |
641 | return need_rescan ? -EAGAIN : -ENXIO; | 599 | return -ENXIO; |
642 | 600 | ||
643 | /* Put it on the slow path. */ | 601 | /* Put it on the slow path. */ |
644 | ret = css_enqueue_subchannel_slow(schid); | 602 | css_schedule_eval(schid); |
645 | if (ret) { | ||
646 | css_clear_subchannel_slow_list(); | ||
647 | need_rescan = 1; | ||
648 | return -EAGAIN; | ||
649 | } | ||
650 | return 0; | 603 | return 0; |
651 | } | 604 | } |
652 | 605 | ||
@@ -693,22 +646,15 @@ __chp_add(struct subchannel_id schid, void *data) | |||
693 | return 0; | 646 | return 0; |
694 | } | 647 | } |
695 | 648 | ||
696 | int chsc_chp_online(struct chp_id chpid) | 649 | void chsc_chp_online(struct chp_id chpid) |
697 | { | 650 | { |
698 | int rc; | ||
699 | char dbf_txt[15]; | 651 | char dbf_txt[15]; |
700 | 652 | ||
701 | sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id); | 653 | sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id); |
702 | CIO_TRACE_EVENT(2, dbf_txt); | 654 | CIO_TRACE_EVENT(2, dbf_txt); |
703 | 655 | ||
704 | if (chp_get_status(chpid) == 0) | 656 | if (chp_get_status(chpid) != 0) |
705 | return 0; | 657 | for_each_subchannel(__chp_add, &chpid); |
706 | rc = for_each_subchannel(__chp_add, &chpid); | ||
707 | if (css_slow_subchannels_exist()) | ||
708 | rc = -EAGAIN; | ||
709 | if (rc != -EAGAIN) | ||
710 | rc = 0; | ||
711 | return rc; | ||
712 | } | 658 | } |
713 | 659 | ||
714 | static void __s390_subchannel_vary_chpid(struct subchannel *sch, | 660 | static void __s390_subchannel_vary_chpid(struct subchannel *sch, |
@@ -749,12 +695,8 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch, | |||
749 | sch->driver->verify(&sch->dev); | 695 | sch->driver->verify(&sch->dev); |
750 | } | 696 | } |
751 | } else if (!sch->lpm) { | 697 | } else if (!sch->lpm) { |
752 | if (device_trigger_verify(sch) != 0) { | 698 | if (device_trigger_verify(sch) != 0) |
753 | if (css_enqueue_subchannel_slow(sch->schid)) { | 699 | css_schedule_eval(sch->schid); |
754 | css_clear_subchannel_slow_list(); | ||
755 | need_rescan = 1; | ||
756 | } | ||
757 | } | ||
758 | } else if (sch->driver && sch->driver->verify) | 700 | } else if (sch->driver && sch->driver->verify) |
759 | sch->driver->verify(&sch->dev); | 701 | sch->driver->verify(&sch->dev); |
760 | break; | 702 | break; |
@@ -801,11 +743,7 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data) | |||
801 | /* We're through */ | 743 | /* We're through */ |
802 | return -ENXIO; | 744 | return -ENXIO; |
803 | /* Put it on the slow path. */ | 745 | /* Put it on the slow path. */ |
804 | if (css_enqueue_subchannel_slow(schid)) { | 746 | css_schedule_eval(schid); |
805 | css_clear_subchannel_slow_list(); | ||
806 | need_rescan = 1; | ||
807 | return -EAGAIN; | ||
808 | } | ||
809 | return 0; | 747 | return 0; |
810 | } | 748 | } |
811 | 749 | ||
@@ -826,8 +764,6 @@ int chsc_chp_vary(struct chp_id chpid, int on) | |||
826 | if (on) | 764 | if (on) |
827 | /* Scan for new devices on varied on path. */ | 765 | /* Scan for new devices on varied on path. */ |
828 | for_each_subchannel(__s390_vary_chpid_on, NULL); | 766 | for_each_subchannel(__s390_vary_chpid_on, NULL); |
829 | if (need_rescan || css_slow_subchannels_exist()) | ||
830 | queue_work(slow_path_wq, &slow_path_work); | ||
831 | return 0; | 767 | return 0; |
832 | } | 768 | } |
833 | 769 | ||