diff options
Diffstat (limited to 'drivers/s390/cio/css.c')
-rw-r--r-- | drivers/s390/cio/css.c | 252 |
1 files changed, 128 insertions, 124 deletions
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 393c73c47f87..91c25706fa83 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c | |||
@@ -31,8 +31,7 @@ | |||
31 | #include "chp.h" | 31 | #include "chp.h" |
32 | 32 | ||
33 | int css_init_done = 0; | 33 | int css_init_done = 0; |
34 | static int need_reprobe = 0; | 34 | int max_ssid; |
35 | static int max_ssid = 0; | ||
36 | 35 | ||
37 | struct channel_subsystem *channel_subsystems[__MAX_CSSID + 1]; | 36 | struct channel_subsystem *channel_subsystems[__MAX_CSSID + 1]; |
38 | 37 | ||
@@ -315,12 +314,18 @@ int css_probe_device(struct subchannel_id schid) | |||
315 | int ret; | 314 | int ret; |
316 | struct subchannel *sch; | 315 | struct subchannel *sch; |
317 | 316 | ||
318 | sch = css_alloc_subchannel(schid); | 317 | if (cio_is_console(schid)) |
319 | if (IS_ERR(sch)) | 318 | sch = cio_get_console_subchannel(); |
320 | return PTR_ERR(sch); | 319 | else { |
320 | sch = css_alloc_subchannel(schid); | ||
321 | if (IS_ERR(sch)) | ||
322 | return PTR_ERR(sch); | ||
323 | } | ||
321 | ret = css_register_subchannel(sch); | 324 | ret = css_register_subchannel(sch); |
322 | if (ret) | 325 | if (ret) { |
323 | put_device(&sch->dev); | 326 | if (!cio_is_console(schid)) |
327 | put_device(&sch->dev); | ||
328 | } | ||
324 | return ret; | 329 | return ret; |
325 | } | 330 | } |
326 | 331 | ||
@@ -409,10 +414,14 @@ static void css_evaluate_subchannel(struct subchannel_id schid, int slow) | |||
409 | 414 | ||
410 | static struct idset *slow_subchannel_set; | 415 | static struct idset *slow_subchannel_set; |
411 | static spinlock_t slow_subchannel_lock; | 416 | static spinlock_t slow_subchannel_lock; |
417 | static wait_queue_head_t css_eval_wq; | ||
418 | static atomic_t css_eval_scheduled; | ||
412 | 419 | ||
413 | static int __init slow_subchannel_init(void) | 420 | static int __init slow_subchannel_init(void) |
414 | { | 421 | { |
415 | spin_lock_init(&slow_subchannel_lock); | 422 | spin_lock_init(&slow_subchannel_lock); |
423 | atomic_set(&css_eval_scheduled, 0); | ||
424 | init_waitqueue_head(&css_eval_wq); | ||
416 | slow_subchannel_set = idset_sch_new(); | 425 | slow_subchannel_set = idset_sch_new(); |
417 | if (!slow_subchannel_set) { | 426 | if (!slow_subchannel_set) { |
418 | CIO_MSG_EVENT(0, "could not allocate slow subchannel set\n"); | 427 | CIO_MSG_EVENT(0, "could not allocate slow subchannel set\n"); |
@@ -468,9 +477,17 @@ static int slow_eval_unknown_fn(struct subchannel_id schid, void *data) | |||
468 | 477 | ||
469 | static void css_slow_path_func(struct work_struct *unused) | 478 | static void css_slow_path_func(struct work_struct *unused) |
470 | { | 479 | { |
480 | unsigned long flags; | ||
481 | |||
471 | CIO_TRACE_EVENT(4, "slowpath"); | 482 | CIO_TRACE_EVENT(4, "slowpath"); |
472 | for_each_subchannel_staged(slow_eval_known_fn, slow_eval_unknown_fn, | 483 | for_each_subchannel_staged(slow_eval_known_fn, slow_eval_unknown_fn, |
473 | NULL); | 484 | NULL); |
485 | spin_lock_irqsave(&slow_subchannel_lock, flags); | ||
486 | if (idset_is_empty(slow_subchannel_set)) { | ||
487 | atomic_set(&css_eval_scheduled, 0); | ||
488 | wake_up(&css_eval_wq); | ||
489 | } | ||
490 | spin_unlock_irqrestore(&slow_subchannel_lock, flags); | ||
474 | } | 491 | } |
475 | 492 | ||
476 | static DECLARE_WORK(slow_path_work, css_slow_path_func); | 493 | static DECLARE_WORK(slow_path_work, css_slow_path_func); |
@@ -482,6 +499,7 @@ void css_schedule_eval(struct subchannel_id schid) | |||
482 | 499 | ||
483 | spin_lock_irqsave(&slow_subchannel_lock, flags); | 500 | spin_lock_irqsave(&slow_subchannel_lock, flags); |
484 | idset_sch_add(slow_subchannel_set, schid); | 501 | idset_sch_add(slow_subchannel_set, schid); |
502 | atomic_set(&css_eval_scheduled, 1); | ||
485 | queue_work(slow_path_wq, &slow_path_work); | 503 | queue_work(slow_path_wq, &slow_path_work); |
486 | spin_unlock_irqrestore(&slow_subchannel_lock, flags); | 504 | spin_unlock_irqrestore(&slow_subchannel_lock, flags); |
487 | } | 505 | } |
@@ -492,80 +510,53 @@ void css_schedule_eval_all(void) | |||
492 | 510 | ||
493 | spin_lock_irqsave(&slow_subchannel_lock, flags); | 511 | spin_lock_irqsave(&slow_subchannel_lock, flags); |
494 | idset_fill(slow_subchannel_set); | 512 | idset_fill(slow_subchannel_set); |
513 | atomic_set(&css_eval_scheduled, 1); | ||
495 | queue_work(slow_path_wq, &slow_path_work); | 514 | queue_work(slow_path_wq, &slow_path_work); |
496 | spin_unlock_irqrestore(&slow_subchannel_lock, flags); | 515 | spin_unlock_irqrestore(&slow_subchannel_lock, flags); |
497 | } | 516 | } |
498 | 517 | ||
499 | void css_wait_for_slow_path(void) | 518 | static int __unset_registered(struct device *dev, void *data) |
500 | { | 519 | { |
501 | flush_workqueue(slow_path_wq); | 520 | struct idset *set = data; |
502 | } | 521 | struct subchannel *sch = to_subchannel(dev); |
503 | |||
504 | /* Reprobe subchannel if unregistered. */ | ||
505 | static int reprobe_subchannel(struct subchannel_id schid, void *data) | ||
506 | { | ||
507 | int ret; | ||
508 | |||
509 | CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n", | ||
510 | schid.ssid, schid.sch_no); | ||
511 | if (need_reprobe) | ||
512 | return -EAGAIN; | ||
513 | |||
514 | ret = css_probe_device(schid); | ||
515 | switch (ret) { | ||
516 | case 0: | ||
517 | break; | ||
518 | case -ENXIO: | ||
519 | case -ENOMEM: | ||
520 | case -EIO: | ||
521 | /* These should abort looping */ | ||
522 | break; | ||
523 | default: | ||
524 | ret = 0; | ||
525 | } | ||
526 | |||
527 | return ret; | ||
528 | } | ||
529 | 522 | ||
530 | static void reprobe_after_idle(struct work_struct *unused) | 523 | idset_sch_del(set, sch->schid); |
531 | { | 524 | return 0; |
532 | /* Make sure initial subchannel scan is done. */ | ||
533 | wait_event(ccw_device_init_wq, | ||
534 | atomic_read(&ccw_device_init_count) == 0); | ||
535 | if (need_reprobe) | ||
536 | css_schedule_reprobe(); | ||
537 | } | 525 | } |
538 | 526 | ||
539 | static DECLARE_WORK(reprobe_idle_work, reprobe_after_idle); | 527 | void css_schedule_eval_all_unreg(void) |
540 | |||
541 | /* Work function used to reprobe all unregistered subchannels. */ | ||
542 | static void reprobe_all(struct work_struct *unused) | ||
543 | { | 528 | { |
544 | int ret; | 529 | unsigned long flags; |
545 | 530 | struct idset *unreg_set; | |
546 | CIO_MSG_EVENT(4, "reprobe start\n"); | ||
547 | 531 | ||
548 | /* Make sure initial subchannel scan is done. */ | 532 | /* Find unregistered subchannels. */ |
549 | if (atomic_read(&ccw_device_init_count) != 0) { | 533 | unreg_set = idset_sch_new(); |
550 | queue_work(ccw_device_work, &reprobe_idle_work); | 534 | if (!unreg_set) { |
535 | /* Fallback. */ | ||
536 | css_schedule_eval_all(); | ||
551 | return; | 537 | return; |
552 | } | 538 | } |
553 | need_reprobe = 0; | 539 | idset_fill(unreg_set); |
554 | ret = for_each_subchannel_staged(NULL, reprobe_subchannel, NULL); | 540 | bus_for_each_dev(&css_bus_type, NULL, unreg_set, __unset_registered); |
555 | 541 | /* Apply to slow_subchannel_set. */ | |
556 | CIO_MSG_EVENT(4, "reprobe done (rc=%d, need_reprobe=%d)\n", ret, | 542 | spin_lock_irqsave(&slow_subchannel_lock, flags); |
557 | need_reprobe); | 543 | idset_add_set(slow_subchannel_set, unreg_set); |
544 | atomic_set(&css_eval_scheduled, 1); | ||
545 | queue_work(slow_path_wq, &slow_path_work); | ||
546 | spin_unlock_irqrestore(&slow_subchannel_lock, flags); | ||
547 | idset_free(unreg_set); | ||
558 | } | 548 | } |
559 | 549 | ||
560 | static DECLARE_WORK(css_reprobe_work, reprobe_all); | 550 | void css_wait_for_slow_path(void) |
551 | { | ||
552 | flush_workqueue(slow_path_wq); | ||
553 | } | ||
561 | 554 | ||
562 | /* Schedule reprobing of all unregistered subchannels. */ | 555 | /* Schedule reprobing of all unregistered subchannels. */ |
563 | void css_schedule_reprobe(void) | 556 | void css_schedule_reprobe(void) |
564 | { | 557 | { |
565 | need_reprobe = 1; | 558 | css_schedule_eval_all_unreg(); |
566 | queue_work(slow_path_wq, &css_reprobe_work); | ||
567 | } | 559 | } |
568 | |||
569 | EXPORT_SYMBOL_GPL(css_schedule_reprobe); | 560 | EXPORT_SYMBOL_GPL(css_schedule_reprobe); |
570 | 561 | ||
571 | /* | 562 | /* |
@@ -601,49 +592,6 @@ static void css_process_crw(struct crw *crw0, struct crw *crw1, int overflow) | |||
601 | css_evaluate_subchannel(mchk_schid, 0); | 592 | css_evaluate_subchannel(mchk_schid, 0); |
602 | } | 593 | } |
603 | 594 | ||
604 | static int __init | ||
605 | __init_channel_subsystem(struct subchannel_id schid, void *data) | ||
606 | { | ||
607 | struct subchannel *sch; | ||
608 | int ret; | ||
609 | |||
610 | if (cio_is_console(schid)) | ||
611 | sch = cio_get_console_subchannel(); | ||
612 | else { | ||
613 | sch = css_alloc_subchannel(schid); | ||
614 | if (IS_ERR(sch)) | ||
615 | ret = PTR_ERR(sch); | ||
616 | else | ||
617 | ret = 0; | ||
618 | switch (ret) { | ||
619 | case 0: | ||
620 | break; | ||
621 | case -ENOMEM: | ||
622 | panic("Out of memory in init_channel_subsystem\n"); | ||
623 | /* -ENXIO: no more subchannels. */ | ||
624 | case -ENXIO: | ||
625 | return ret; | ||
626 | /* -EIO: this subchannel set not supported. */ | ||
627 | case -EIO: | ||
628 | return ret; | ||
629 | default: | ||
630 | return 0; | ||
631 | } | ||
632 | } | ||
633 | /* | ||
634 | * We register ALL valid subchannels in ioinfo, even those | ||
635 | * that have been present before init_channel_subsystem. | ||
636 | * These subchannels can't have been registered yet (kmalloc | ||
637 | * not working) so we do it now. This is true e.g. for the | ||
638 | * console subchannel. | ||
639 | */ | ||
640 | if (css_register_subchannel(sch)) { | ||
641 | if (!cio_is_console(schid)) | ||
642 | put_device(&sch->dev); | ||
643 | } | ||
644 | return 0; | ||
645 | } | ||
646 | |||
647 | static void __init | 595 | static void __init |
648 | css_generate_pgid(struct channel_subsystem *css, u32 tod_high) | 596 | css_generate_pgid(struct channel_subsystem *css, u32 tod_high) |
649 | { | 597 | { |
@@ -854,19 +802,30 @@ static struct notifier_block css_power_notifier = { | |||
854 | * The struct subchannel's are created during probing (except for the | 802 | * The struct subchannel's are created during probing (except for the |
855 | * static console subchannel). | 803 | * static console subchannel). |
856 | */ | 804 | */ |
857 | static int __init | 805 | static int __init css_bus_init(void) |
858 | init_channel_subsystem (void) | ||
859 | { | 806 | { |
860 | int ret, i; | 807 | int ret, i; |
861 | 808 | ||
862 | ret = chsc_determine_css_characteristics(); | 809 | ret = chsc_determine_css_characteristics(); |
863 | if (ret == -ENOMEM) | 810 | if (ret == -ENOMEM) |
864 | goto out; /* No need to continue. */ | 811 | goto out; |
865 | 812 | ||
866 | ret = chsc_alloc_sei_area(); | 813 | ret = chsc_alloc_sei_area(); |
867 | if (ret) | 814 | if (ret) |
868 | goto out; | 815 | goto out; |
869 | 816 | ||
817 | /* Try to enable MSS. */ | ||
818 | ret = chsc_enable_facility(CHSC_SDA_OC_MSS); | ||
819 | switch (ret) { | ||
820 | case 0: /* Success. */ | ||
821 | max_ssid = __MAX_SSID; | ||
822 | break; | ||
823 | case -ENOMEM: | ||
824 | goto out; | ||
825 | default: | ||
826 | max_ssid = 0; | ||
827 | } | ||
828 | |||
870 | ret = slow_subchannel_init(); | 829 | ret = slow_subchannel_init(); |
871 | if (ret) | 830 | if (ret) |
872 | goto out; | 831 | goto out; |
@@ -878,17 +837,6 @@ init_channel_subsystem (void) | |||
878 | if ((ret = bus_register(&css_bus_type))) | 837 | if ((ret = bus_register(&css_bus_type))) |
879 | goto out; | 838 | goto out; |
880 | 839 | ||
881 | /* Try to enable MSS. */ | ||
882 | ret = chsc_enable_facility(CHSC_SDA_OC_MSS); | ||
883 | switch (ret) { | ||
884 | case 0: /* Success. */ | ||
885 | max_ssid = __MAX_SSID; | ||
886 | break; | ||
887 | case -ENOMEM: | ||
888 | goto out_bus; | ||
889 | default: | ||
890 | max_ssid = 0; | ||
891 | } | ||
892 | /* Setup css structure. */ | 840 | /* Setup css structure. */ |
893 | for (i = 0; i <= __MAX_CSSID; i++) { | 841 | for (i = 0; i <= __MAX_CSSID; i++) { |
894 | struct channel_subsystem *css; | 842 | struct channel_subsystem *css; |
@@ -934,7 +882,6 @@ init_channel_subsystem (void) | |||
934 | /* Enable default isc for I/O subchannels. */ | 882 | /* Enable default isc for I/O subchannels. */ |
935 | isc_register(IO_SCH_ISC); | 883 | isc_register(IO_SCH_ISC); |
936 | 884 | ||
937 | for_each_subchannel(__init_channel_subsystem, NULL); | ||
938 | return 0; | 885 | return 0; |
939 | out_file: | 886 | out_file: |
940 | if (css_chsc_characteristics.secm) | 887 | if (css_chsc_characteristics.secm) |
@@ -955,17 +902,76 @@ out_unregister: | |||
955 | &dev_attr_cm_enable); | 902 | &dev_attr_cm_enable); |
956 | device_unregister(&css->device); | 903 | device_unregister(&css->device); |
957 | } | 904 | } |
958 | out_bus: | ||
959 | bus_unregister(&css_bus_type); | 905 | bus_unregister(&css_bus_type); |
960 | out: | 906 | out: |
961 | crw_unregister_handler(CRW_RSC_CSS); | 907 | crw_unregister_handler(CRW_RSC_CSS); |
962 | chsc_free_sei_area(); | 908 | chsc_free_sei_area(); |
963 | kfree(slow_subchannel_set); | 909 | idset_free(slow_subchannel_set); |
964 | pr_alert("The CSS device driver initialization failed with " | 910 | pr_alert("The CSS device driver initialization failed with " |
965 | "errno=%d\n", ret); | 911 | "errno=%d\n", ret); |
966 | return ret; | 912 | return ret; |
967 | } | 913 | } |
968 | 914 | ||
915 | static void __init css_bus_cleanup(void) | ||
916 | { | ||
917 | struct channel_subsystem *css; | ||
918 | int i; | ||
919 | |||
920 | for (i = 0; i <= __MAX_CSSID; i++) { | ||
921 | css = channel_subsystems[i]; | ||
922 | device_unregister(&css->pseudo_subchannel->dev); | ||
923 | css->pseudo_subchannel = NULL; | ||
924 | if (css_chsc_characteristics.secm) | ||
925 | device_remove_file(&css->device, &dev_attr_cm_enable); | ||
926 | device_unregister(&css->device); | ||
927 | } | ||
928 | bus_unregister(&css_bus_type); | ||
929 | crw_unregister_handler(CRW_RSC_CSS); | ||
930 | chsc_free_sei_area(); | ||
931 | idset_free(slow_subchannel_set); | ||
932 | isc_unregister(IO_SCH_ISC); | ||
933 | } | ||
934 | |||
935 | static int __init channel_subsystem_init(void) | ||
936 | { | ||
937 | int ret; | ||
938 | |||
939 | ret = css_bus_init(); | ||
940 | if (ret) | ||
941 | return ret; | ||
942 | |||
943 | ret = io_subchannel_init(); | ||
944 | if (ret) | ||
945 | css_bus_cleanup(); | ||
946 | |||
947 | return ret; | ||
948 | } | ||
949 | subsys_initcall(channel_subsystem_init); | ||
950 | |||
951 | static int css_settle(struct device_driver *drv, void *unused) | ||
952 | { | ||
953 | struct css_driver *cssdrv = to_cssdriver(drv); | ||
954 | |||
955 | if (cssdrv->settle) | ||
956 | cssdrv->settle(); | ||
957 | return 0; | ||
958 | } | ||
959 | |||
960 | /* | ||
961 | * Wait for the initialization of devices to finish, to make sure we are | ||
962 | * done with our setup if the search for the root device starts. | ||
963 | */ | ||
964 | static int __init channel_subsystem_init_sync(void) | ||
965 | { | ||
966 | /* Start initial subchannel evaluation. */ | ||
967 | css_schedule_eval_all(); | ||
968 | /* Wait for the evaluation of subchannels to finish. */ | ||
969 | wait_event(css_eval_wq, atomic_read(&css_eval_scheduled) == 0); | ||
970 | /* Wait for the subchannel type specific initialization to finish */ | ||
971 | return bus_for_each_drv(&css_bus_type, NULL, NULL, css_settle); | ||
972 | } | ||
973 | subsys_initcall_sync(channel_subsystem_init_sync); | ||
974 | |||
969 | int sch_is_pseudo_sch(struct subchannel *sch) | 975 | int sch_is_pseudo_sch(struct subchannel *sch) |
970 | { | 976 | { |
971 | return sch == to_css(sch->dev.parent)->pseudo_subchannel; | 977 | return sch == to_css(sch->dev.parent)->pseudo_subchannel; |
@@ -1135,7 +1141,5 @@ void css_driver_unregister(struct css_driver *cdrv) | |||
1135 | } | 1141 | } |
1136 | EXPORT_SYMBOL_GPL(css_driver_unregister); | 1142 | EXPORT_SYMBOL_GPL(css_driver_unregister); |
1137 | 1143 | ||
1138 | subsys_initcall(init_channel_subsystem); | ||
1139 | |||
1140 | MODULE_LICENSE("GPL"); | 1144 | MODULE_LICENSE("GPL"); |
1141 | EXPORT_SYMBOL(css_bus_type); | 1145 | EXPORT_SYMBOL(css_bus_type); |