diff options
Diffstat (limited to 'drivers/s390/cio/css.c')
-rw-r--r-- | drivers/s390/cio/css.c | 198 |
1 files changed, 164 insertions, 34 deletions
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index c3df2cd009a4..3b45bbe6cce0 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c | |||
@@ -51,6 +51,62 @@ for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data) | |||
51 | return ret; | 51 | return ret; |
52 | } | 52 | } |
53 | 53 | ||
54 | struct cb_data { | ||
55 | void *data; | ||
56 | struct idset *set; | ||
57 | int (*fn_known_sch)(struct subchannel *, void *); | ||
58 | int (*fn_unknown_sch)(struct subchannel_id, void *); | ||
59 | }; | ||
60 | |||
61 | static int call_fn_known_sch(struct device *dev, void *data) | ||
62 | { | ||
63 | struct subchannel *sch = to_subchannel(dev); | ||
64 | struct cb_data *cb = data; | ||
65 | int rc = 0; | ||
66 | |||
67 | idset_sch_del(cb->set, sch->schid); | ||
68 | if (cb->fn_known_sch) | ||
69 | rc = cb->fn_known_sch(sch, cb->data); | ||
70 | return rc; | ||
71 | } | ||
72 | |||
73 | static int call_fn_unknown_sch(struct subchannel_id schid, void *data) | ||
74 | { | ||
75 | struct cb_data *cb = data; | ||
76 | int rc = 0; | ||
77 | |||
78 | if (idset_sch_contains(cb->set, schid)) | ||
79 | rc = cb->fn_unknown_sch(schid, cb->data); | ||
80 | return rc; | ||
81 | } | ||
82 | |||
83 | int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *), | ||
84 | int (*fn_unknown)(struct subchannel_id, | ||
85 | void *), void *data) | ||
86 | { | ||
87 | struct cb_data cb; | ||
88 | int rc; | ||
89 | |||
90 | cb.set = idset_sch_new(); | ||
91 | if (!cb.set) | ||
92 | return -ENOMEM; | ||
93 | idset_fill(cb.set); | ||
94 | cb.data = data; | ||
95 | cb.fn_known_sch = fn_known; | ||
96 | cb.fn_unknown_sch = fn_unknown; | ||
97 | /* Process registered subchannels. */ | ||
98 | rc = bus_for_each_dev(&css_bus_type, NULL, &cb, call_fn_known_sch); | ||
99 | if (rc) | ||
100 | goto out; | ||
101 | /* Process unregistered subchannels. */ | ||
102 | if (fn_unknown) | ||
103 | rc = for_each_subchannel(call_fn_unknown_sch, &cb); | ||
104 | out: | ||
105 | idset_free(cb.set); | ||
106 | |||
107 | return rc; | ||
108 | } | ||
109 | |||
54 | static struct subchannel * | 110 | static struct subchannel * |
55 | css_alloc_subchannel(struct subchannel_id schid) | 111 | css_alloc_subchannel(struct subchannel_id schid) |
56 | { | 112 | { |
@@ -77,7 +133,7 @@ css_alloc_subchannel(struct subchannel_id schid) | |||
77 | * This is fine even on 64bit since the subchannel is always located | 133 | * This is fine even on 64bit since the subchannel is always located |
78 | * under 2G. | 134 | * under 2G. |
79 | */ | 135 | */ |
80 | sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; | 136 | sch->schib.pmcw.intparm = (u32)(addr_t)sch; |
81 | ret = cio_modify(sch); | 137 | ret = cio_modify(sch); |
82 | if (ret) { | 138 | if (ret) { |
83 | kfree(sch->lock); | 139 | kfree(sch->lock); |
@@ -237,11 +293,25 @@ get_subchannel_by_schid(struct subchannel_id schid) | |||
237 | return dev ? to_subchannel(dev) : NULL; | 293 | return dev ? to_subchannel(dev) : NULL; |
238 | } | 294 | } |
239 | 295 | ||
296 | /** | ||
297 | * css_sch_is_valid() - check if a subchannel is valid | ||
298 | * @schib: subchannel information block for the subchannel | ||
299 | */ | ||
300 | int css_sch_is_valid(struct schib *schib) | ||
301 | { | ||
302 | if ((schib->pmcw.st == SUBCHANNEL_TYPE_IO) && !schib->pmcw.dnv) | ||
303 | return 0; | ||
304 | return 1; | ||
305 | } | ||
306 | EXPORT_SYMBOL_GPL(css_sch_is_valid); | ||
307 | |||
240 | static int css_get_subchannel_status(struct subchannel *sch) | 308 | static int css_get_subchannel_status(struct subchannel *sch) |
241 | { | 309 | { |
242 | struct schib schib; | 310 | struct schib schib; |
243 | 311 | ||
244 | if (stsch(sch->schid, &schib) || !schib.pmcw.dnv) | 312 | if (stsch(sch->schid, &schib)) |
313 | return CIO_GONE; | ||
314 | if (!css_sch_is_valid(&schib)) | ||
245 | return CIO_GONE; | 315 | return CIO_GONE; |
246 | if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev)) | 316 | if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev)) |
247 | return CIO_REVALIDATE; | 317 | return CIO_REVALIDATE; |
@@ -293,7 +363,7 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) | |||
293 | action = UNREGISTER; | 363 | action = UNREGISTER; |
294 | if (sch->driver && sch->driver->notify) { | 364 | if (sch->driver && sch->driver->notify) { |
295 | spin_unlock_irqrestore(sch->lock, flags); | 365 | spin_unlock_irqrestore(sch->lock, flags); |
296 | ret = sch->driver->notify(&sch->dev, event); | 366 | ret = sch->driver->notify(sch, event); |
297 | spin_lock_irqsave(sch->lock, flags); | 367 | spin_lock_irqsave(sch->lock, flags); |
298 | if (ret) | 368 | if (ret) |
299 | action = NONE; | 369 | action = NONE; |
@@ -349,7 +419,7 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) | |||
349 | /* Will be done on the slow path. */ | 419 | /* Will be done on the slow path. */ |
350 | return -EAGAIN; | 420 | return -EAGAIN; |
351 | } | 421 | } |
352 | if (stsch_err(schid, &schib) || !schib.pmcw.dnv) { | 422 | if (stsch_err(schid, &schib) || !css_sch_is_valid(&schib)) { |
353 | /* Unusable - ignore. */ | 423 | /* Unusable - ignore. */ |
354 | return 0; | 424 | return 0; |
355 | } | 425 | } |
@@ -388,20 +458,56 @@ static int __init slow_subchannel_init(void) | |||
388 | return 0; | 458 | return 0; |
389 | } | 459 | } |
390 | 460 | ||
391 | static void css_slow_path_func(struct work_struct *unused) | 461 | static int slow_eval_known_fn(struct subchannel *sch, void *data) |
392 | { | 462 | { |
393 | struct subchannel_id schid; | 463 | int eval; |
464 | int rc; | ||
394 | 465 | ||
395 | CIO_TRACE_EVENT(4, "slowpath"); | ||
396 | spin_lock_irq(&slow_subchannel_lock); | 466 | spin_lock_irq(&slow_subchannel_lock); |
397 | init_subchannel_id(&schid); | 467 | eval = idset_sch_contains(slow_subchannel_set, sch->schid); |
398 | while (idset_sch_get_first(slow_subchannel_set, &schid)) { | 468 | idset_sch_del(slow_subchannel_set, sch->schid); |
399 | idset_sch_del(slow_subchannel_set, schid); | 469 | spin_unlock_irq(&slow_subchannel_lock); |
400 | spin_unlock_irq(&slow_subchannel_lock); | 470 | if (eval) { |
401 | css_evaluate_subchannel(schid, 1); | 471 | rc = css_evaluate_known_subchannel(sch, 1); |
402 | spin_lock_irq(&slow_subchannel_lock); | 472 | if (rc == -EAGAIN) |
473 | css_schedule_eval(sch->schid); | ||
403 | } | 474 | } |
475 | return 0; | ||
476 | } | ||
477 | |||
478 | static int slow_eval_unknown_fn(struct subchannel_id schid, void *data) | ||
479 | { | ||
480 | int eval; | ||
481 | int rc = 0; | ||
482 | |||
483 | spin_lock_irq(&slow_subchannel_lock); | ||
484 | eval = idset_sch_contains(slow_subchannel_set, schid); | ||
485 | idset_sch_del(slow_subchannel_set, schid); | ||
404 | spin_unlock_irq(&slow_subchannel_lock); | 486 | spin_unlock_irq(&slow_subchannel_lock); |
487 | if (eval) { | ||
488 | rc = css_evaluate_new_subchannel(schid, 1); | ||
489 | switch (rc) { | ||
490 | case -EAGAIN: | ||
491 | css_schedule_eval(schid); | ||
492 | rc = 0; | ||
493 | break; | ||
494 | case -ENXIO: | ||
495 | case -ENOMEM: | ||
496 | case -EIO: | ||
497 | /* These should abort looping */ | ||
498 | break; | ||
499 | default: | ||
500 | rc = 0; | ||
501 | } | ||
502 | } | ||
503 | return rc; | ||
504 | } | ||
505 | |||
506 | static void css_slow_path_func(struct work_struct *unused) | ||
507 | { | ||
508 | CIO_TRACE_EVENT(4, "slowpath"); | ||
509 | for_each_subchannel_staged(slow_eval_known_fn, slow_eval_unknown_fn, | ||
510 | NULL); | ||
405 | } | 511 | } |
406 | 512 | ||
407 | static DECLARE_WORK(slow_path_work, css_slow_path_func); | 513 | static DECLARE_WORK(slow_path_work, css_slow_path_func); |
@@ -430,7 +536,6 @@ void css_schedule_eval_all(void) | |||
430 | /* Reprobe subchannel if unregistered. */ | 536 | /* Reprobe subchannel if unregistered. */ |
431 | static int reprobe_subchannel(struct subchannel_id schid, void *data) | 537 | static int reprobe_subchannel(struct subchannel_id schid, void *data) |
432 | { | 538 | { |
433 | struct subchannel *sch; | ||
434 | int ret; | 539 | int ret; |
435 | 540 | ||
436 | CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n", | 541 | CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n", |
@@ -438,13 +543,6 @@ static int reprobe_subchannel(struct subchannel_id schid, void *data) | |||
438 | if (need_reprobe) | 543 | if (need_reprobe) |
439 | return -EAGAIN; | 544 | return -EAGAIN; |
440 | 545 | ||
441 | sch = get_subchannel_by_schid(schid); | ||
442 | if (sch) { | ||
443 | /* Already known. */ | ||
444 | put_device(&sch->dev); | ||
445 | return 0; | ||
446 | } | ||
447 | |||
448 | ret = css_probe_device(schid); | 546 | ret = css_probe_device(schid); |
449 | switch (ret) { | 547 | switch (ret) { |
450 | case 0: | 548 | case 0: |
@@ -472,7 +570,7 @@ static void reprobe_all(struct work_struct *unused) | |||
472 | /* Make sure initial subchannel scan is done. */ | 570 | /* Make sure initial subchannel scan is done. */ |
473 | wait_event(ccw_device_init_wq, | 571 | wait_event(ccw_device_init_wq, |
474 | atomic_read(&ccw_device_init_count) == 0); | 572 | atomic_read(&ccw_device_init_count) == 0); |
475 | ret = for_each_subchannel(reprobe_subchannel, NULL); | 573 | ret = for_each_subchannel_staged(NULL, reprobe_subchannel, NULL); |
476 | 574 | ||
477 | CIO_MSG_EVENT(2, "reprobe done (rc=%d, need_reprobe=%d)\n", ret, | 575 | CIO_MSG_EVENT(2, "reprobe done (rc=%d, need_reprobe=%d)\n", ret, |
478 | need_reprobe); | 576 | need_reprobe); |
@@ -787,8 +885,8 @@ int sch_is_pseudo_sch(struct subchannel *sch) | |||
787 | static int | 885 | static int |
788 | css_bus_match (struct device *dev, struct device_driver *drv) | 886 | css_bus_match (struct device *dev, struct device_driver *drv) |
789 | { | 887 | { |
790 | struct subchannel *sch = container_of (dev, struct subchannel, dev); | 888 | struct subchannel *sch = to_subchannel(dev); |
791 | struct css_driver *driver = container_of (drv, struct css_driver, drv); | 889 | struct css_driver *driver = to_cssdriver(drv); |
792 | 890 | ||
793 | if (sch->st == driver->subchannel_type) | 891 | if (sch->st == driver->subchannel_type) |
794 | return 1; | 892 | return 1; |
@@ -796,32 +894,36 @@ css_bus_match (struct device *dev, struct device_driver *drv) | |||
796 | return 0; | 894 | return 0; |
797 | } | 895 | } |
798 | 896 | ||
799 | static int | 897 | static int css_probe(struct device *dev) |
800 | css_probe (struct device *dev) | ||
801 | { | 898 | { |
802 | struct subchannel *sch; | 899 | struct subchannel *sch; |
900 | int ret; | ||
803 | 901 | ||
804 | sch = to_subchannel(dev); | 902 | sch = to_subchannel(dev); |
805 | sch->driver = container_of (dev->driver, struct css_driver, drv); | 903 | sch->driver = to_cssdriver(dev->driver); |
806 | return (sch->driver->probe ? sch->driver->probe(sch) : 0); | 904 | ret = sch->driver->probe ? sch->driver->probe(sch) : 0; |
905 | if (ret) | ||
906 | sch->driver = NULL; | ||
907 | return ret; | ||
807 | } | 908 | } |
808 | 909 | ||
809 | static int | 910 | static int css_remove(struct device *dev) |
810 | css_remove (struct device *dev) | ||
811 | { | 911 | { |
812 | struct subchannel *sch; | 912 | struct subchannel *sch; |
913 | int ret; | ||
813 | 914 | ||
814 | sch = to_subchannel(dev); | 915 | sch = to_subchannel(dev); |
815 | return (sch->driver->remove ? sch->driver->remove(sch) : 0); | 916 | ret = sch->driver->remove ? sch->driver->remove(sch) : 0; |
917 | sch->driver = NULL; | ||
918 | return ret; | ||
816 | } | 919 | } |
817 | 920 | ||
818 | static void | 921 | static void css_shutdown(struct device *dev) |
819 | css_shutdown (struct device *dev) | ||
820 | { | 922 | { |
821 | struct subchannel *sch; | 923 | struct subchannel *sch; |
822 | 924 | ||
823 | sch = to_subchannel(dev); | 925 | sch = to_subchannel(dev); |
824 | if (sch->driver->shutdown) | 926 | if (sch->driver && sch->driver->shutdown) |
825 | sch->driver->shutdown(sch); | 927 | sch->driver->shutdown(sch); |
826 | } | 928 | } |
827 | 929 | ||
@@ -833,6 +935,34 @@ struct bus_type css_bus_type = { | |||
833 | .shutdown = css_shutdown, | 935 | .shutdown = css_shutdown, |
834 | }; | 936 | }; |
835 | 937 | ||
938 | /** | ||
939 | * css_driver_register - register a css driver | ||
940 | * @cdrv: css driver to register | ||
941 | * | ||
942 | * This is mainly a wrapper around driver_register that sets name | ||
943 | * and bus_type in the embedded struct device_driver correctly. | ||
944 | */ | ||
945 | int css_driver_register(struct css_driver *cdrv) | ||
946 | { | ||
947 | cdrv->drv.name = cdrv->name; | ||
948 | cdrv->drv.bus = &css_bus_type; | ||
949 | cdrv->drv.owner = cdrv->owner; | ||
950 | return driver_register(&cdrv->drv); | ||
951 | } | ||
952 | EXPORT_SYMBOL_GPL(css_driver_register); | ||
953 | |||
954 | /** | ||
955 | * css_driver_unregister - unregister a css driver | ||
956 | * @cdrv: css driver to unregister | ||
957 | * | ||
958 | * This is a wrapper around driver_unregister. | ||
959 | */ | ||
960 | void css_driver_unregister(struct css_driver *cdrv) | ||
961 | { | ||
962 | driver_unregister(&cdrv->drv); | ||
963 | } | ||
964 | EXPORT_SYMBOL_GPL(css_driver_unregister); | ||
965 | |||
836 | subsys_initcall(init_channel_subsystem); | 966 | subsys_initcall(init_channel_subsystem); |
837 | 967 | ||
838 | MODULE_LICENSE("GPL"); | 968 | MODULE_LICENSE("GPL"); |