diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
commit | ada47b5fe13d89735805b566185f4885f5a3f750 (patch) | |
tree | 644b88f8a71896307d71438e9b3af49126ffb22b /drivers/s390/cio/css.c | |
parent | 43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff) | |
parent | 3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff) |
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'drivers/s390/cio/css.c')
-rw-r--r-- | drivers/s390/cio/css.c | 152 |
1 files changed, 129 insertions, 23 deletions
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 91c25706fa83..511649115bd7 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/list.h> | 18 | #include <linux/list.h> |
19 | #include <linux/reboot.h> | 19 | #include <linux/reboot.h> |
20 | #include <linux/suspend.h> | 20 | #include <linux/suspend.h> |
21 | #include <linux/proc_fs.h> | ||
21 | #include <asm/isc.h> | 22 | #include <asm/isc.h> |
22 | #include <asm/crw.h> | 23 | #include <asm/crw.h> |
23 | 24 | ||
@@ -133,6 +134,8 @@ out: | |||
133 | return rc; | 134 | return rc; |
134 | } | 135 | } |
135 | 136 | ||
137 | static void css_sch_todo(struct work_struct *work); | ||
138 | |||
136 | static struct subchannel * | 139 | static struct subchannel * |
137 | css_alloc_subchannel(struct subchannel_id schid) | 140 | css_alloc_subchannel(struct subchannel_id schid) |
138 | { | 141 | { |
@@ -147,6 +150,7 @@ css_alloc_subchannel(struct subchannel_id schid) | |||
147 | kfree(sch); | 150 | kfree(sch); |
148 | return ERR_PTR(ret); | 151 | return ERR_PTR(ret); |
149 | } | 152 | } |
153 | INIT_WORK(&sch->todo_work, css_sch_todo); | ||
150 | return sch; | 154 | return sch; |
151 | } | 155 | } |
152 | 156 | ||
@@ -190,6 +194,51 @@ void css_sch_device_unregister(struct subchannel *sch) | |||
190 | } | 194 | } |
191 | EXPORT_SYMBOL_GPL(css_sch_device_unregister); | 195 | EXPORT_SYMBOL_GPL(css_sch_device_unregister); |
192 | 196 | ||
197 | static void css_sch_todo(struct work_struct *work) | ||
198 | { | ||
199 | struct subchannel *sch; | ||
200 | enum sch_todo todo; | ||
201 | |||
202 | sch = container_of(work, struct subchannel, todo_work); | ||
203 | /* Find out todo. */ | ||
204 | spin_lock_irq(sch->lock); | ||
205 | todo = sch->todo; | ||
206 | CIO_MSG_EVENT(4, "sch_todo: sch=0.%x.%04x, todo=%d\n", sch->schid.ssid, | ||
207 | sch->schid.sch_no, todo); | ||
208 | sch->todo = SCH_TODO_NOTHING; | ||
209 | spin_unlock_irq(sch->lock); | ||
210 | /* Perform todo. */ | ||
211 | if (todo == SCH_TODO_UNREG) | ||
212 | css_sch_device_unregister(sch); | ||
213 | /* Release workqueue ref. */ | ||
214 | put_device(&sch->dev); | ||
215 | } | ||
216 | |||
217 | /** | ||
218 | * css_sched_sch_todo - schedule a subchannel operation | ||
219 | * @sch: subchannel | ||
220 | * @todo: todo | ||
221 | * | ||
222 | * Schedule the operation identified by @todo to be performed on the slow path | ||
223 | * workqueue. Do nothing if another operation with higher priority is already | ||
224 | * scheduled. Needs to be called with subchannel lock held. | ||
225 | */ | ||
226 | void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo) | ||
227 | { | ||
228 | CIO_MSG_EVENT(4, "sch_todo: sched sch=0.%x.%04x todo=%d\n", | ||
229 | sch->schid.ssid, sch->schid.sch_no, todo); | ||
230 | if (sch->todo >= todo) | ||
231 | return; | ||
232 | /* Get workqueue ref. */ | ||
233 | if (!get_device(&sch->dev)) | ||
234 | return; | ||
235 | sch->todo = todo; | ||
236 | if (!queue_work(cio_work_q, &sch->todo_work)) { | ||
237 | /* Already queued, release workqueue ref. */ | ||
238 | put_device(&sch->dev); | ||
239 | } | ||
240 | } | ||
241 | |||
193 | static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw) | 242 | static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw) |
194 | { | 243 | { |
195 | int i; | 244 | int i; |
@@ -376,8 +425,8 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) | |||
376 | /* Unusable - ignore. */ | 425 | /* Unusable - ignore. */ |
377 | return 0; | 426 | return 0; |
378 | } | 427 | } |
379 | CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, unknown, " | 428 | CIO_MSG_EVENT(4, "event: sch 0.%x.%04x, new\n", schid.ssid, |
380 | "slow path.\n", schid.ssid, schid.sch_no, CIO_OPER); | 429 | schid.sch_no); |
381 | 430 | ||
382 | return css_probe_device(schid); | 431 | return css_probe_device(schid); |
383 | } | 432 | } |
@@ -394,6 +443,10 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) | |||
394 | "Got subchannel machine check but " | 443 | "Got subchannel machine check but " |
395 | "no sch_event handler provided.\n"); | 444 | "no sch_event handler provided.\n"); |
396 | } | 445 | } |
446 | if (ret != 0 && ret != -EAGAIN) { | ||
447 | CIO_MSG_EVENT(2, "eval: sch 0.%x.%04x, rc=%d\n", | ||
448 | sch->schid.ssid, sch->schid.sch_no, ret); | ||
449 | } | ||
397 | return ret; | 450 | return ret; |
398 | } | 451 | } |
399 | 452 | ||
@@ -491,7 +544,7 @@ static void css_slow_path_func(struct work_struct *unused) | |||
491 | } | 544 | } |
492 | 545 | ||
493 | static DECLARE_WORK(slow_path_work, css_slow_path_func); | 546 | static DECLARE_WORK(slow_path_work, css_slow_path_func); |
494 | struct workqueue_struct *slow_path_wq; | 547 | struct workqueue_struct *cio_work_q; |
495 | 548 | ||
496 | void css_schedule_eval(struct subchannel_id schid) | 549 | void css_schedule_eval(struct subchannel_id schid) |
497 | { | 550 | { |
@@ -500,7 +553,7 @@ void css_schedule_eval(struct subchannel_id schid) | |||
500 | spin_lock_irqsave(&slow_subchannel_lock, flags); | 553 | spin_lock_irqsave(&slow_subchannel_lock, flags); |
501 | idset_sch_add(slow_subchannel_set, schid); | 554 | idset_sch_add(slow_subchannel_set, schid); |
502 | atomic_set(&css_eval_scheduled, 1); | 555 | atomic_set(&css_eval_scheduled, 1); |
503 | queue_work(slow_path_wq, &slow_path_work); | 556 | queue_work(cio_work_q, &slow_path_work); |
504 | spin_unlock_irqrestore(&slow_subchannel_lock, flags); | 557 | spin_unlock_irqrestore(&slow_subchannel_lock, flags); |
505 | } | 558 | } |
506 | 559 | ||
@@ -511,7 +564,7 @@ void css_schedule_eval_all(void) | |||
511 | spin_lock_irqsave(&slow_subchannel_lock, flags); | 564 | spin_lock_irqsave(&slow_subchannel_lock, flags); |
512 | idset_fill(slow_subchannel_set); | 565 | idset_fill(slow_subchannel_set); |
513 | atomic_set(&css_eval_scheduled, 1); | 566 | atomic_set(&css_eval_scheduled, 1); |
514 | queue_work(slow_path_wq, &slow_path_work); | 567 | queue_work(cio_work_q, &slow_path_work); |
515 | spin_unlock_irqrestore(&slow_subchannel_lock, flags); | 568 | spin_unlock_irqrestore(&slow_subchannel_lock, flags); |
516 | } | 569 | } |
517 | 570 | ||
@@ -542,14 +595,14 @@ void css_schedule_eval_all_unreg(void) | |||
542 | spin_lock_irqsave(&slow_subchannel_lock, flags); | 595 | spin_lock_irqsave(&slow_subchannel_lock, flags); |
543 | idset_add_set(slow_subchannel_set, unreg_set); | 596 | idset_add_set(slow_subchannel_set, unreg_set); |
544 | atomic_set(&css_eval_scheduled, 1); | 597 | atomic_set(&css_eval_scheduled, 1); |
545 | queue_work(slow_path_wq, &slow_path_work); | 598 | queue_work(cio_work_q, &slow_path_work); |
546 | spin_unlock_irqrestore(&slow_subchannel_lock, flags); | 599 | spin_unlock_irqrestore(&slow_subchannel_lock, flags); |
547 | idset_free(unreg_set); | 600 | idset_free(unreg_set); |
548 | } | 601 | } |
549 | 602 | ||
550 | void css_wait_for_slow_path(void) | 603 | void css_wait_for_slow_path(void) |
551 | { | 604 | { |
552 | flush_workqueue(slow_path_wq); | 605 | flush_workqueue(cio_work_q); |
553 | } | 606 | } |
554 | 607 | ||
555 | /* Schedule reprobing of all unregistered subchannels. */ | 608 | /* Schedule reprobing of all unregistered subchannels. */ |
@@ -684,6 +737,7 @@ static int __init setup_css(int nr) | |||
684 | css->pseudo_subchannel->dev.parent = &css->device; | 737 | css->pseudo_subchannel->dev.parent = &css->device; |
685 | css->pseudo_subchannel->dev.release = css_subchannel_release; | 738 | css->pseudo_subchannel->dev.release = css_subchannel_release; |
686 | dev_set_name(&css->pseudo_subchannel->dev, "defunct"); | 739 | dev_set_name(&css->pseudo_subchannel->dev, "defunct"); |
740 | mutex_init(&css->pseudo_subchannel->reg_mutex); | ||
687 | ret = cio_create_sch_lock(css->pseudo_subchannel); | 741 | ret = cio_create_sch_lock(css->pseudo_subchannel); |
688 | if (ret) { | 742 | if (ret) { |
689 | kfree(css->pseudo_subchannel); | 743 | kfree(css->pseudo_subchannel); |
@@ -816,15 +870,10 @@ static int __init css_bus_init(void) | |||
816 | 870 | ||
817 | /* Try to enable MSS. */ | 871 | /* Try to enable MSS. */ |
818 | ret = chsc_enable_facility(CHSC_SDA_OC_MSS); | 872 | ret = chsc_enable_facility(CHSC_SDA_OC_MSS); |
819 | switch (ret) { | 873 | if (ret) |
820 | case 0: /* Success. */ | ||
821 | max_ssid = __MAX_SSID; | ||
822 | break; | ||
823 | case -ENOMEM: | ||
824 | goto out; | ||
825 | default: | ||
826 | max_ssid = 0; | 874 | max_ssid = 0; |
827 | } | 875 | else /* Success. */ |
876 | max_ssid = __MAX_SSID; | ||
828 | 877 | ||
829 | ret = slow_subchannel_init(); | 878 | ret = slow_subchannel_init(); |
830 | if (ret) | 879 | if (ret) |
@@ -939,12 +988,21 @@ static int __init channel_subsystem_init(void) | |||
939 | ret = css_bus_init(); | 988 | ret = css_bus_init(); |
940 | if (ret) | 989 | if (ret) |
941 | return ret; | 990 | return ret; |
942 | 991 | cio_work_q = create_singlethread_workqueue("cio"); | |
992 | if (!cio_work_q) { | ||
993 | ret = -ENOMEM; | ||
994 | goto out_bus; | ||
995 | } | ||
943 | ret = io_subchannel_init(); | 996 | ret = io_subchannel_init(); |
944 | if (ret) | 997 | if (ret) |
945 | css_bus_cleanup(); | 998 | goto out_wq; |
946 | 999 | ||
947 | return ret; | 1000 | return ret; |
1001 | out_wq: | ||
1002 | destroy_workqueue(cio_work_q); | ||
1003 | out_bus: | ||
1004 | css_bus_cleanup(); | ||
1005 | return ret; | ||
948 | } | 1006 | } |
949 | subsys_initcall(channel_subsystem_init); | 1007 | subsys_initcall(channel_subsystem_init); |
950 | 1008 | ||
@@ -953,10 +1011,25 @@ static int css_settle(struct device_driver *drv, void *unused) | |||
953 | struct css_driver *cssdrv = to_cssdriver(drv); | 1011 | struct css_driver *cssdrv = to_cssdriver(drv); |
954 | 1012 | ||
955 | if (cssdrv->settle) | 1013 | if (cssdrv->settle) |
956 | cssdrv->settle(); | 1014 | return cssdrv->settle(); |
957 | return 0; | 1015 | return 0; |
958 | } | 1016 | } |
959 | 1017 | ||
1018 | int css_complete_work(void) | ||
1019 | { | ||
1020 | int ret; | ||
1021 | |||
1022 | /* Wait for the evaluation of subchannels to finish. */ | ||
1023 | ret = wait_event_interruptible(css_eval_wq, | ||
1024 | atomic_read(&css_eval_scheduled) == 0); | ||
1025 | if (ret) | ||
1026 | return -EINTR; | ||
1027 | flush_workqueue(cio_work_q); | ||
1028 | /* Wait for the subchannel type specific initialization to finish */ | ||
1029 | return bus_for_each_drv(&css_bus_type, NULL, NULL, css_settle); | ||
1030 | } | ||
1031 | |||
1032 | |||
960 | /* | 1033 | /* |
961 | * Wait for the initialization of devices to finish, to make sure we are | 1034 | * 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. | 1035 | * done with our setup if the search for the root device starts. |
@@ -965,13 +1038,46 @@ static int __init channel_subsystem_init_sync(void) | |||
965 | { | 1038 | { |
966 | /* Start initial subchannel evaluation. */ | 1039 | /* Start initial subchannel evaluation. */ |
967 | css_schedule_eval_all(); | 1040 | css_schedule_eval_all(); |
968 | /* Wait for the evaluation of subchannels to finish. */ | 1041 | css_complete_work(); |
969 | wait_event(css_eval_wq, atomic_read(&css_eval_scheduled) == 0); | 1042 | return 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 | } | 1043 | } |
973 | subsys_initcall_sync(channel_subsystem_init_sync); | 1044 | subsys_initcall_sync(channel_subsystem_init_sync); |
974 | 1045 | ||
1046 | void channel_subsystem_reinit(void) | ||
1047 | { | ||
1048 | chsc_enable_facility(CHSC_SDA_OC_MSS); | ||
1049 | } | ||
1050 | |||
1051 | #ifdef CONFIG_PROC_FS | ||
1052 | static ssize_t cio_settle_write(struct file *file, const char __user *buf, | ||
1053 | size_t count, loff_t *ppos) | ||
1054 | { | ||
1055 | int ret; | ||
1056 | |||
1057 | /* Handle pending CRW's. */ | ||
1058 | crw_wait_for_channel_report(); | ||
1059 | ret = css_complete_work(); | ||
1060 | |||
1061 | return ret ? ret : count; | ||
1062 | } | ||
1063 | |||
1064 | static const struct file_operations cio_settle_proc_fops = { | ||
1065 | .write = cio_settle_write, | ||
1066 | }; | ||
1067 | |||
1068 | static int __init cio_settle_init(void) | ||
1069 | { | ||
1070 | struct proc_dir_entry *entry; | ||
1071 | |||
1072 | entry = proc_create("cio_settle", S_IWUSR, NULL, | ||
1073 | &cio_settle_proc_fops); | ||
1074 | if (!entry) | ||
1075 | return -ENOMEM; | ||
1076 | return 0; | ||
1077 | } | ||
1078 | device_initcall(cio_settle_init); | ||
1079 | #endif /*CONFIG_PROC_FS*/ | ||
1080 | |||
975 | int sch_is_pseudo_sch(struct subchannel *sch) | 1081 | int sch_is_pseudo_sch(struct subchannel *sch) |
976 | { | 1082 | { |
977 | return sch == to_css(sch->dev.parent)->pseudo_subchannel; | 1083 | return sch == to_css(sch->dev.parent)->pseudo_subchannel; |
@@ -1095,7 +1201,7 @@ static int css_pm_restore(struct device *dev) | |||
1095 | return drv->restore ? drv->restore(sch) : 0; | 1201 | return drv->restore ? drv->restore(sch) : 0; |
1096 | } | 1202 | } |
1097 | 1203 | ||
1098 | static struct dev_pm_ops css_pm_ops = { | 1204 | static const struct dev_pm_ops css_pm_ops = { |
1099 | .prepare = css_pm_prepare, | 1205 | .prepare = css_pm_prepare, |
1100 | .complete = css_pm_complete, | 1206 | .complete = css_pm_complete, |
1101 | .freeze = css_pm_freeze, | 1207 | .freeze = css_pm_freeze, |