diff options
Diffstat (limited to 'net/iucv/iucv.c')
-rw-r--r-- | net/iucv/iucv.c | 311 |
1 files changed, 299 insertions, 12 deletions
diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index a35240f61ec..c833481d32e 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c | |||
@@ -1,7 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * IUCV base infrastructure. | 2 | * IUCV base infrastructure. |
3 | * | 3 | * |
4 | * Copyright 2001, 2006 IBM Deutschland Entwicklung GmbH, IBM Corporation | 4 | * Copyright IBM Corp. 2001, 2009 |
5 | * | ||
5 | * Author(s): | 6 | * Author(s): |
6 | * Original source: | 7 | * Original source: |
7 | * Alan Altmark (Alan_Altmark@us.ibm.com) Sept. 2000 | 8 | * Alan Altmark (Alan_Altmark@us.ibm.com) Sept. 2000 |
@@ -10,6 +11,8 @@ | |||
10 | * Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com) | 11 | * Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com) |
11 | * Rewritten for af_iucv: | 12 | * Rewritten for af_iucv: |
12 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 13 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
14 | * PM functions: | ||
15 | * Ursula Braun (ursula.braun@de.ibm.com) | ||
13 | * | 16 | * |
14 | * Documentation used: | 17 | * Documentation used: |
15 | * The original source | 18 | * The original source |
@@ -45,6 +48,7 @@ | |||
45 | #include <linux/err.h> | 48 | #include <linux/err.h> |
46 | #include <linux/device.h> | 49 | #include <linux/device.h> |
47 | #include <linux/cpu.h> | 50 | #include <linux/cpu.h> |
51 | #include <linux/reboot.h> | ||
48 | #include <net/iucv/iucv.h> | 52 | #include <net/iucv/iucv.h> |
49 | #include <asm/atomic.h> | 53 | #include <asm/atomic.h> |
50 | #include <asm/ebcdic.h> | 54 | #include <asm/ebcdic.h> |
@@ -75,9 +79,24 @@ static int iucv_bus_match(struct device *dev, struct device_driver *drv) | |||
75 | return 0; | 79 | return 0; |
76 | } | 80 | } |
77 | 81 | ||
82 | static int iucv_pm_prepare(struct device *); | ||
83 | static void iucv_pm_complete(struct device *); | ||
84 | static int iucv_pm_freeze(struct device *); | ||
85 | static int iucv_pm_thaw(struct device *); | ||
86 | static int iucv_pm_restore(struct device *); | ||
87 | |||
88 | static struct dev_pm_ops iucv_pm_ops = { | ||
89 | .prepare = iucv_pm_prepare, | ||
90 | .complete = iucv_pm_complete, | ||
91 | .freeze = iucv_pm_freeze, | ||
92 | .thaw = iucv_pm_thaw, | ||
93 | .restore = iucv_pm_restore, | ||
94 | }; | ||
95 | |||
78 | struct bus_type iucv_bus = { | 96 | struct bus_type iucv_bus = { |
79 | .name = "iucv", | 97 | .name = "iucv", |
80 | .match = iucv_bus_match, | 98 | .match = iucv_bus_match, |
99 | .pm = &iucv_pm_ops, | ||
81 | }; | 100 | }; |
82 | EXPORT_SYMBOL(iucv_bus); | 101 | EXPORT_SYMBOL(iucv_bus); |
83 | 102 | ||
@@ -147,6 +166,7 @@ enum iucv_command_codes { | |||
147 | IUCV_RESUME = 14, | 166 | IUCV_RESUME = 14, |
148 | IUCV_SEVER = 15, | 167 | IUCV_SEVER = 15, |
149 | IUCV_SETMASK = 16, | 168 | IUCV_SETMASK = 16, |
169 | IUCV_SETCONTROLMASK = 17, | ||
150 | }; | 170 | }; |
151 | 171 | ||
152 | /* | 172 | /* |
@@ -280,6 +300,7 @@ union iucv_param { | |||
280 | * Anchor for per-cpu IUCV command parameter block. | 300 | * Anchor for per-cpu IUCV command parameter block. |
281 | */ | 301 | */ |
282 | static union iucv_param *iucv_param[NR_CPUS]; | 302 | static union iucv_param *iucv_param[NR_CPUS]; |
303 | static union iucv_param *iucv_param_irq[NR_CPUS]; | ||
283 | 304 | ||
284 | /** | 305 | /** |
285 | * iucv_call_b2f0 | 306 | * iucv_call_b2f0 |
@@ -358,11 +379,23 @@ static void iucv_allow_cpu(void *data) | |||
358 | * 0x10 - Flag to allow priority message completion interrupts | 379 | * 0x10 - Flag to allow priority message completion interrupts |
359 | * 0x08 - Flag to allow IUCV control interrupts | 380 | * 0x08 - Flag to allow IUCV control interrupts |
360 | */ | 381 | */ |
361 | parm = iucv_param[cpu]; | 382 | parm = iucv_param_irq[cpu]; |
362 | memset(parm, 0, sizeof(union iucv_param)); | 383 | memset(parm, 0, sizeof(union iucv_param)); |
363 | parm->set_mask.ipmask = 0xf8; | 384 | parm->set_mask.ipmask = 0xf8; |
364 | iucv_call_b2f0(IUCV_SETMASK, parm); | 385 | iucv_call_b2f0(IUCV_SETMASK, parm); |
365 | 386 | ||
387 | /* | ||
388 | * Enable all iucv control interrupts. | ||
389 | * ipmask contains bits for the different interrupts | ||
390 | * 0x80 - Flag to allow pending connections interrupts | ||
391 | * 0x40 - Flag to allow connection complete interrupts | ||
392 | * 0x20 - Flag to allow connection severed interrupts | ||
393 | * 0x10 - Flag to allow connection quiesced interrupts | ||
394 | * 0x08 - Flag to allow connection resumed interrupts | ||
395 | */ | ||
396 | memset(parm, 0, sizeof(union iucv_param)); | ||
397 | parm->set_mask.ipmask = 0xf8; | ||
398 | iucv_call_b2f0(IUCV_SETCONTROLMASK, parm); | ||
366 | /* Set indication that iucv interrupts are allowed for this cpu. */ | 399 | /* Set indication that iucv interrupts are allowed for this cpu. */ |
367 | cpu_set(cpu, iucv_irq_cpumask); | 400 | cpu_set(cpu, iucv_irq_cpumask); |
368 | } | 401 | } |
@@ -379,7 +412,7 @@ static void iucv_block_cpu(void *data) | |||
379 | union iucv_param *parm; | 412 | union iucv_param *parm; |
380 | 413 | ||
381 | /* Disable all iucv interrupts. */ | 414 | /* Disable all iucv interrupts. */ |
382 | parm = iucv_param[cpu]; | 415 | parm = iucv_param_irq[cpu]; |
383 | memset(parm, 0, sizeof(union iucv_param)); | 416 | memset(parm, 0, sizeof(union iucv_param)); |
384 | iucv_call_b2f0(IUCV_SETMASK, parm); | 417 | iucv_call_b2f0(IUCV_SETMASK, parm); |
385 | 418 | ||
@@ -388,6 +421,31 @@ static void iucv_block_cpu(void *data) | |||
388 | } | 421 | } |
389 | 422 | ||
390 | /** | 423 | /** |
424 | * iucv_block_cpu_almost | ||
425 | * @data: unused | ||
426 | * | ||
427 | * Allow connection-severed interrupts only on this cpu. | ||
428 | */ | ||
429 | static void iucv_block_cpu_almost(void *data) | ||
430 | { | ||
431 | int cpu = smp_processor_id(); | ||
432 | union iucv_param *parm; | ||
433 | |||
434 | /* Allow iucv control interrupts only */ | ||
435 | parm = iucv_param_irq[cpu]; | ||
436 | memset(parm, 0, sizeof(union iucv_param)); | ||
437 | parm->set_mask.ipmask = 0x08; | ||
438 | iucv_call_b2f0(IUCV_SETMASK, parm); | ||
439 | /* Allow iucv-severed interrupt only */ | ||
440 | memset(parm, 0, sizeof(union iucv_param)); | ||
441 | parm->set_mask.ipmask = 0x20; | ||
442 | iucv_call_b2f0(IUCV_SETCONTROLMASK, parm); | ||
443 | |||
444 | /* Clear indication that iucv interrupts are allowed for this cpu. */ | ||
445 | cpu_clear(cpu, iucv_irq_cpumask); | ||
446 | } | ||
447 | |||
448 | /** | ||
391 | * iucv_declare_cpu | 449 | * iucv_declare_cpu |
392 | * @data: unused | 450 | * @data: unused |
393 | * | 451 | * |
@@ -403,7 +461,7 @@ static void iucv_declare_cpu(void *data) | |||
403 | return; | 461 | return; |
404 | 462 | ||
405 | /* Declare interrupt buffer. */ | 463 | /* Declare interrupt buffer. */ |
406 | parm = iucv_param[cpu]; | 464 | parm = iucv_param_irq[cpu]; |
407 | memset(parm, 0, sizeof(union iucv_param)); | 465 | memset(parm, 0, sizeof(union iucv_param)); |
408 | parm->db.ipbfadr1 = virt_to_phys(iucv_irq_data[cpu]); | 466 | parm->db.ipbfadr1 = virt_to_phys(iucv_irq_data[cpu]); |
409 | rc = iucv_call_b2f0(IUCV_DECLARE_BUFFER, parm); | 467 | rc = iucv_call_b2f0(IUCV_DECLARE_BUFFER, parm); |
@@ -460,7 +518,7 @@ static void iucv_retrieve_cpu(void *data) | |||
460 | iucv_block_cpu(NULL); | 518 | iucv_block_cpu(NULL); |
461 | 519 | ||
462 | /* Retrieve interrupt buffer. */ | 520 | /* Retrieve interrupt buffer. */ |
463 | parm = iucv_param[cpu]; | 521 | parm = iucv_param_irq[cpu]; |
464 | iucv_call_b2f0(IUCV_RETRIEVE_BUFFER, parm); | 522 | iucv_call_b2f0(IUCV_RETRIEVE_BUFFER, parm); |
465 | 523 | ||
466 | /* Clear indication that an iucv buffer exists for this cpu. */ | 524 | /* Clear indication that an iucv buffer exists for this cpu. */ |
@@ -574,11 +632,22 @@ static int __cpuinit iucv_cpu_notify(struct notifier_block *self, | |||
574 | iucv_irq_data[cpu] = NULL; | 632 | iucv_irq_data[cpu] = NULL; |
575 | return NOTIFY_BAD; | 633 | return NOTIFY_BAD; |
576 | } | 634 | } |
635 | iucv_param_irq[cpu] = kmalloc_node(sizeof(union iucv_param), | ||
636 | GFP_KERNEL|GFP_DMA, cpu_to_node(cpu)); | ||
637 | if (!iucv_param_irq[cpu]) { | ||
638 | kfree(iucv_param[cpu]); | ||
639 | iucv_param[cpu] = NULL; | ||
640 | kfree(iucv_irq_data[cpu]); | ||
641 | iucv_irq_data[cpu] = NULL; | ||
642 | return NOTIFY_BAD; | ||
643 | } | ||
577 | break; | 644 | break; |
578 | case CPU_UP_CANCELED: | 645 | case CPU_UP_CANCELED: |
579 | case CPU_UP_CANCELED_FROZEN: | 646 | case CPU_UP_CANCELED_FROZEN: |
580 | case CPU_DEAD: | 647 | case CPU_DEAD: |
581 | case CPU_DEAD_FROZEN: | 648 | case CPU_DEAD_FROZEN: |
649 | kfree(iucv_param_irq[cpu]); | ||
650 | iucv_param_irq[cpu] = NULL; | ||
582 | kfree(iucv_param[cpu]); | 651 | kfree(iucv_param[cpu]); |
583 | iucv_param[cpu] = NULL; | 652 | iucv_param[cpu] = NULL; |
584 | kfree(iucv_irq_data[cpu]); | 653 | kfree(iucv_irq_data[cpu]); |
@@ -625,7 +694,7 @@ static int iucv_sever_pathid(u16 pathid, u8 userdata[16]) | |||
625 | { | 694 | { |
626 | union iucv_param *parm; | 695 | union iucv_param *parm; |
627 | 696 | ||
628 | parm = iucv_param[smp_processor_id()]; | 697 | parm = iucv_param_irq[smp_processor_id()]; |
629 | memset(parm, 0, sizeof(union iucv_param)); | 698 | memset(parm, 0, sizeof(union iucv_param)); |
630 | if (userdata) | 699 | if (userdata) |
631 | memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser)); | 700 | memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser)); |
@@ -746,6 +815,28 @@ void iucv_unregister(struct iucv_handler *handler, int smp) | |||
746 | } | 815 | } |
747 | EXPORT_SYMBOL(iucv_unregister); | 816 | EXPORT_SYMBOL(iucv_unregister); |
748 | 817 | ||
818 | static int iucv_reboot_event(struct notifier_block *this, | ||
819 | unsigned long event, void *ptr) | ||
820 | { | ||
821 | int i, rc; | ||
822 | |||
823 | get_online_cpus(); | ||
824 | on_each_cpu(iucv_block_cpu, NULL, 1); | ||
825 | preempt_disable(); | ||
826 | for (i = 0; i < iucv_max_pathid; i++) { | ||
827 | if (iucv_path_table[i]) | ||
828 | rc = iucv_sever_pathid(i, NULL); | ||
829 | } | ||
830 | preempt_enable(); | ||
831 | put_online_cpus(); | ||
832 | iucv_disable(); | ||
833 | return NOTIFY_DONE; | ||
834 | } | ||
835 | |||
836 | static struct notifier_block iucv_reboot_notifier = { | ||
837 | .notifier_call = iucv_reboot_event, | ||
838 | }; | ||
839 | |||
749 | /** | 840 | /** |
750 | * iucv_path_accept | 841 | * iucv_path_accept |
751 | * @path: address of iucv path structure | 842 | * @path: address of iucv path structure |
@@ -765,6 +856,10 @@ int iucv_path_accept(struct iucv_path *path, struct iucv_handler *handler, | |||
765 | int rc; | 856 | int rc; |
766 | 857 | ||
767 | local_bh_disable(); | 858 | local_bh_disable(); |
859 | if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) { | ||
860 | rc = -EIO; | ||
861 | goto out; | ||
862 | } | ||
768 | /* Prepare parameter block. */ | 863 | /* Prepare parameter block. */ |
769 | parm = iucv_param[smp_processor_id()]; | 864 | parm = iucv_param[smp_processor_id()]; |
770 | memset(parm, 0, sizeof(union iucv_param)); | 865 | memset(parm, 0, sizeof(union iucv_param)); |
@@ -780,6 +875,7 @@ int iucv_path_accept(struct iucv_path *path, struct iucv_handler *handler, | |||
780 | path->msglim = parm->ctrl.ipmsglim; | 875 | path->msglim = parm->ctrl.ipmsglim; |
781 | path->flags = parm->ctrl.ipflags1; | 876 | path->flags = parm->ctrl.ipflags1; |
782 | } | 877 | } |
878 | out: | ||
783 | local_bh_enable(); | 879 | local_bh_enable(); |
784 | return rc; | 880 | return rc; |
785 | } | 881 | } |
@@ -809,6 +905,10 @@ int iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler, | |||
809 | 905 | ||
810 | spin_lock_bh(&iucv_table_lock); | 906 | spin_lock_bh(&iucv_table_lock); |
811 | iucv_cleanup_queue(); | 907 | iucv_cleanup_queue(); |
908 | if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) { | ||
909 | rc = -EIO; | ||
910 | goto out; | ||
911 | } | ||
812 | parm = iucv_param[smp_processor_id()]; | 912 | parm = iucv_param[smp_processor_id()]; |
813 | memset(parm, 0, sizeof(union iucv_param)); | 913 | memset(parm, 0, sizeof(union iucv_param)); |
814 | parm->ctrl.ipmsglim = path->msglim; | 914 | parm->ctrl.ipmsglim = path->msglim; |
@@ -843,6 +943,7 @@ int iucv_path_connect(struct iucv_path *path, struct iucv_handler *handler, | |||
843 | rc = -EIO; | 943 | rc = -EIO; |
844 | } | 944 | } |
845 | } | 945 | } |
946 | out: | ||
846 | spin_unlock_bh(&iucv_table_lock); | 947 | spin_unlock_bh(&iucv_table_lock); |
847 | return rc; | 948 | return rc; |
848 | } | 949 | } |
@@ -864,12 +965,17 @@ int iucv_path_quiesce(struct iucv_path *path, u8 userdata[16]) | |||
864 | int rc; | 965 | int rc; |
865 | 966 | ||
866 | local_bh_disable(); | 967 | local_bh_disable(); |
968 | if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) { | ||
969 | rc = -EIO; | ||
970 | goto out; | ||
971 | } | ||
867 | parm = iucv_param[smp_processor_id()]; | 972 | parm = iucv_param[smp_processor_id()]; |
868 | memset(parm, 0, sizeof(union iucv_param)); | 973 | memset(parm, 0, sizeof(union iucv_param)); |
869 | if (userdata) | 974 | if (userdata) |
870 | memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser)); | 975 | memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser)); |
871 | parm->ctrl.ippathid = path->pathid; | 976 | parm->ctrl.ippathid = path->pathid; |
872 | rc = iucv_call_b2f0(IUCV_QUIESCE, parm); | 977 | rc = iucv_call_b2f0(IUCV_QUIESCE, parm); |
978 | out: | ||
873 | local_bh_enable(); | 979 | local_bh_enable(); |
874 | return rc; | 980 | return rc; |
875 | } | 981 | } |
@@ -891,12 +997,17 @@ int iucv_path_resume(struct iucv_path *path, u8 userdata[16]) | |||
891 | int rc; | 997 | int rc; |
892 | 998 | ||
893 | local_bh_disable(); | 999 | local_bh_disable(); |
1000 | if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) { | ||
1001 | rc = -EIO; | ||
1002 | goto out; | ||
1003 | } | ||
894 | parm = iucv_param[smp_processor_id()]; | 1004 | parm = iucv_param[smp_processor_id()]; |
895 | memset(parm, 0, sizeof(union iucv_param)); | 1005 | memset(parm, 0, sizeof(union iucv_param)); |
896 | if (userdata) | 1006 | if (userdata) |
897 | memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser)); | 1007 | memcpy(parm->ctrl.ipuser, userdata, sizeof(parm->ctrl.ipuser)); |
898 | parm->ctrl.ippathid = path->pathid; | 1008 | parm->ctrl.ippathid = path->pathid; |
899 | rc = iucv_call_b2f0(IUCV_RESUME, parm); | 1009 | rc = iucv_call_b2f0(IUCV_RESUME, parm); |
1010 | out: | ||
900 | local_bh_enable(); | 1011 | local_bh_enable(); |
901 | return rc; | 1012 | return rc; |
902 | } | 1013 | } |
@@ -915,15 +1026,18 @@ int iucv_path_sever(struct iucv_path *path, u8 userdata[16]) | |||
915 | int rc; | 1026 | int rc; |
916 | 1027 | ||
917 | preempt_disable(); | 1028 | preempt_disable(); |
1029 | if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) { | ||
1030 | rc = -EIO; | ||
1031 | goto out; | ||
1032 | } | ||
918 | if (iucv_active_cpu != smp_processor_id()) | 1033 | if (iucv_active_cpu != smp_processor_id()) |
919 | spin_lock_bh(&iucv_table_lock); | 1034 | spin_lock_bh(&iucv_table_lock); |
920 | rc = iucv_sever_pathid(path->pathid, userdata); | 1035 | rc = iucv_sever_pathid(path->pathid, userdata); |
921 | if (!rc) { | 1036 | iucv_path_table[path->pathid] = NULL; |
922 | iucv_path_table[path->pathid] = NULL; | 1037 | list_del_init(&path->list); |
923 | list_del_init(&path->list); | ||
924 | } | ||
925 | if (iucv_active_cpu != smp_processor_id()) | 1038 | if (iucv_active_cpu != smp_processor_id()) |
926 | spin_unlock_bh(&iucv_table_lock); | 1039 | spin_unlock_bh(&iucv_table_lock); |
1040 | out: | ||
927 | preempt_enable(); | 1041 | preempt_enable(); |
928 | return rc; | 1042 | return rc; |
929 | } | 1043 | } |
@@ -946,6 +1060,10 @@ int iucv_message_purge(struct iucv_path *path, struct iucv_message *msg, | |||
946 | int rc; | 1060 | int rc; |
947 | 1061 | ||
948 | local_bh_disable(); | 1062 | local_bh_disable(); |
1063 | if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) { | ||
1064 | rc = -EIO; | ||
1065 | goto out; | ||
1066 | } | ||
949 | parm = iucv_param[smp_processor_id()]; | 1067 | parm = iucv_param[smp_processor_id()]; |
950 | memset(parm, 0, sizeof(union iucv_param)); | 1068 | memset(parm, 0, sizeof(union iucv_param)); |
951 | parm->purge.ippathid = path->pathid; | 1069 | parm->purge.ippathid = path->pathid; |
@@ -957,6 +1075,7 @@ int iucv_message_purge(struct iucv_path *path, struct iucv_message *msg, | |||
957 | msg->audit = (*(u32 *) &parm->purge.ipaudit) >> 8; | 1075 | msg->audit = (*(u32 *) &parm->purge.ipaudit) >> 8; |
958 | msg->tag = parm->purge.ipmsgtag; | 1076 | msg->tag = parm->purge.ipmsgtag; |
959 | } | 1077 | } |
1078 | out: | ||
960 | local_bh_enable(); | 1079 | local_bh_enable(); |
961 | return rc; | 1080 | return rc; |
962 | } | 1081 | } |
@@ -1033,6 +1152,10 @@ int __iucv_message_receive(struct iucv_path *path, struct iucv_message *msg, | |||
1033 | if (msg->flags & IUCV_IPRMDATA) | 1152 | if (msg->flags & IUCV_IPRMDATA) |
1034 | return iucv_message_receive_iprmdata(path, msg, flags, | 1153 | return iucv_message_receive_iprmdata(path, msg, flags, |
1035 | buffer, size, residual); | 1154 | buffer, size, residual); |
1155 | if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) { | ||
1156 | rc = -EIO; | ||
1157 | goto out; | ||
1158 | } | ||
1036 | parm = iucv_param[smp_processor_id()]; | 1159 | parm = iucv_param[smp_processor_id()]; |
1037 | memset(parm, 0, sizeof(union iucv_param)); | 1160 | memset(parm, 0, sizeof(union iucv_param)); |
1038 | parm->db.ipbfadr1 = (u32)(addr_t) buffer; | 1161 | parm->db.ipbfadr1 = (u32)(addr_t) buffer; |
@@ -1048,6 +1171,7 @@ int __iucv_message_receive(struct iucv_path *path, struct iucv_message *msg, | |||
1048 | if (residual) | 1171 | if (residual) |
1049 | *residual = parm->db.ipbfln1f; | 1172 | *residual = parm->db.ipbfln1f; |
1050 | } | 1173 | } |
1174 | out: | ||
1051 | return rc; | 1175 | return rc; |
1052 | } | 1176 | } |
1053 | EXPORT_SYMBOL(__iucv_message_receive); | 1177 | EXPORT_SYMBOL(__iucv_message_receive); |
@@ -1101,6 +1225,10 @@ int iucv_message_reject(struct iucv_path *path, struct iucv_message *msg) | |||
1101 | int rc; | 1225 | int rc; |
1102 | 1226 | ||
1103 | local_bh_disable(); | 1227 | local_bh_disable(); |
1228 | if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) { | ||
1229 | rc = -EIO; | ||
1230 | goto out; | ||
1231 | } | ||
1104 | parm = iucv_param[smp_processor_id()]; | 1232 | parm = iucv_param[smp_processor_id()]; |
1105 | memset(parm, 0, sizeof(union iucv_param)); | 1233 | memset(parm, 0, sizeof(union iucv_param)); |
1106 | parm->db.ippathid = path->pathid; | 1234 | parm->db.ippathid = path->pathid; |
@@ -1108,6 +1236,7 @@ int iucv_message_reject(struct iucv_path *path, struct iucv_message *msg) | |||
1108 | parm->db.iptrgcls = msg->class; | 1236 | parm->db.iptrgcls = msg->class; |
1109 | parm->db.ipflags1 = (IUCV_IPTRGCLS | IUCV_IPFGMID | IUCV_IPFGPID); | 1237 | parm->db.ipflags1 = (IUCV_IPTRGCLS | IUCV_IPFGMID | IUCV_IPFGPID); |
1110 | rc = iucv_call_b2f0(IUCV_REJECT, parm); | 1238 | rc = iucv_call_b2f0(IUCV_REJECT, parm); |
1239 | out: | ||
1111 | local_bh_enable(); | 1240 | local_bh_enable(); |
1112 | return rc; | 1241 | return rc; |
1113 | } | 1242 | } |
@@ -1135,6 +1264,10 @@ int iucv_message_reply(struct iucv_path *path, struct iucv_message *msg, | |||
1135 | int rc; | 1264 | int rc; |
1136 | 1265 | ||
1137 | local_bh_disable(); | 1266 | local_bh_disable(); |
1267 | if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) { | ||
1268 | rc = -EIO; | ||
1269 | goto out; | ||
1270 | } | ||
1138 | parm = iucv_param[smp_processor_id()]; | 1271 | parm = iucv_param[smp_processor_id()]; |
1139 | memset(parm, 0, sizeof(union iucv_param)); | 1272 | memset(parm, 0, sizeof(union iucv_param)); |
1140 | if (flags & IUCV_IPRMDATA) { | 1273 | if (flags & IUCV_IPRMDATA) { |
@@ -1152,6 +1285,7 @@ int iucv_message_reply(struct iucv_path *path, struct iucv_message *msg, | |||
1152 | parm->db.iptrgcls = msg->class; | 1285 | parm->db.iptrgcls = msg->class; |
1153 | } | 1286 | } |
1154 | rc = iucv_call_b2f0(IUCV_REPLY, parm); | 1287 | rc = iucv_call_b2f0(IUCV_REPLY, parm); |
1288 | out: | ||
1155 | local_bh_enable(); | 1289 | local_bh_enable(); |
1156 | return rc; | 1290 | return rc; |
1157 | } | 1291 | } |
@@ -1180,6 +1314,10 @@ int __iucv_message_send(struct iucv_path *path, struct iucv_message *msg, | |||
1180 | union iucv_param *parm; | 1314 | union iucv_param *parm; |
1181 | int rc; | 1315 | int rc; |
1182 | 1316 | ||
1317 | if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) { | ||
1318 | rc = -EIO; | ||
1319 | goto out; | ||
1320 | } | ||
1183 | parm = iucv_param[smp_processor_id()]; | 1321 | parm = iucv_param[smp_processor_id()]; |
1184 | memset(parm, 0, sizeof(union iucv_param)); | 1322 | memset(parm, 0, sizeof(union iucv_param)); |
1185 | if (flags & IUCV_IPRMDATA) { | 1323 | if (flags & IUCV_IPRMDATA) { |
@@ -1202,6 +1340,7 @@ int __iucv_message_send(struct iucv_path *path, struct iucv_message *msg, | |||
1202 | rc = iucv_call_b2f0(IUCV_SEND, parm); | 1340 | rc = iucv_call_b2f0(IUCV_SEND, parm); |
1203 | if (!rc) | 1341 | if (!rc) |
1204 | msg->id = parm->db.ipmsgid; | 1342 | msg->id = parm->db.ipmsgid; |
1343 | out: | ||
1205 | return rc; | 1344 | return rc; |
1206 | } | 1345 | } |
1207 | EXPORT_SYMBOL(__iucv_message_send); | 1346 | EXPORT_SYMBOL(__iucv_message_send); |
@@ -1262,6 +1401,10 @@ int iucv_message_send2way(struct iucv_path *path, struct iucv_message *msg, | |||
1262 | int rc; | 1401 | int rc; |
1263 | 1402 | ||
1264 | local_bh_disable(); | 1403 | local_bh_disable(); |
1404 | if (!cpu_isset(smp_processor_id(), iucv_buffer_cpumask)) { | ||
1405 | rc = -EIO; | ||
1406 | goto out; | ||
1407 | } | ||
1265 | parm = iucv_param[smp_processor_id()]; | 1408 | parm = iucv_param[smp_processor_id()]; |
1266 | memset(parm, 0, sizeof(union iucv_param)); | 1409 | memset(parm, 0, sizeof(union iucv_param)); |
1267 | if (flags & IUCV_IPRMDATA) { | 1410 | if (flags & IUCV_IPRMDATA) { |
@@ -1287,6 +1430,7 @@ int iucv_message_send2way(struct iucv_path *path, struct iucv_message *msg, | |||
1287 | rc = iucv_call_b2f0(IUCV_SEND, parm); | 1430 | rc = iucv_call_b2f0(IUCV_SEND, parm); |
1288 | if (!rc) | 1431 | if (!rc) |
1289 | msg->id = parm->db.ipmsgid; | 1432 | msg->id = parm->db.ipmsgid; |
1433 | out: | ||
1290 | local_bh_enable(); | 1434 | local_bh_enable(); |
1291 | return rc; | 1435 | return rc; |
1292 | } | 1436 | } |
@@ -1378,6 +1522,8 @@ static void iucv_path_complete(struct iucv_irq_data *data) | |||
1378 | struct iucv_path_complete *ipc = (void *) data; | 1522 | struct iucv_path_complete *ipc = (void *) data; |
1379 | struct iucv_path *path = iucv_path_table[ipc->ippathid]; | 1523 | struct iucv_path *path = iucv_path_table[ipc->ippathid]; |
1380 | 1524 | ||
1525 | if (path) | ||
1526 | path->flags = ipc->ipflags1; | ||
1381 | if (path && path->handler && path->handler->path_complete) | 1527 | if (path && path->handler && path->handler->path_complete) |
1382 | path->handler->path_complete(path, ipc->ipuser); | 1528 | path->handler->path_complete(path, ipc->ipuser); |
1383 | } | 1529 | } |
@@ -1413,7 +1559,7 @@ static void iucv_path_severed(struct iucv_irq_data *data) | |||
1413 | else { | 1559 | else { |
1414 | iucv_sever_pathid(path->pathid, NULL); | 1560 | iucv_sever_pathid(path->pathid, NULL); |
1415 | iucv_path_table[path->pathid] = NULL; | 1561 | iucv_path_table[path->pathid] = NULL; |
1416 | list_del_init(&path->list); | 1562 | list_del(&path->list); |
1417 | iucv_path_free(path); | 1563 | iucv_path_free(path); |
1418 | } | 1564 | } |
1419 | } | 1565 | } |
@@ -1675,6 +1821,130 @@ static void iucv_external_interrupt(u16 code) | |||
1675 | spin_unlock(&iucv_queue_lock); | 1821 | spin_unlock(&iucv_queue_lock); |
1676 | } | 1822 | } |
1677 | 1823 | ||
1824 | static int iucv_pm_prepare(struct device *dev) | ||
1825 | { | ||
1826 | int rc = 0; | ||
1827 | |||
1828 | #ifdef CONFIG_PM_DEBUG | ||
1829 | printk(KERN_INFO "iucv_pm_prepare\n"); | ||
1830 | #endif | ||
1831 | if (dev->driver && dev->driver->pm && dev->driver->pm->prepare) | ||
1832 | rc = dev->driver->pm->prepare(dev); | ||
1833 | return rc; | ||
1834 | } | ||
1835 | |||
1836 | static void iucv_pm_complete(struct device *dev) | ||
1837 | { | ||
1838 | #ifdef CONFIG_PM_DEBUG | ||
1839 | printk(KERN_INFO "iucv_pm_complete\n"); | ||
1840 | #endif | ||
1841 | if (dev->driver && dev->driver->pm && dev->driver->pm->complete) | ||
1842 | dev->driver->pm->complete(dev); | ||
1843 | } | ||
1844 | |||
1845 | /** | ||
1846 | * iucv_path_table_empty() - determine if iucv path table is empty | ||
1847 | * | ||
1848 | * Returns 0 if there are still iucv pathes defined | ||
1849 | * 1 if there are no iucv pathes defined | ||
1850 | */ | ||
1851 | int iucv_path_table_empty(void) | ||
1852 | { | ||
1853 | int i; | ||
1854 | |||
1855 | for (i = 0; i < iucv_max_pathid; i++) { | ||
1856 | if (iucv_path_table[i]) | ||
1857 | return 0; | ||
1858 | } | ||
1859 | return 1; | ||
1860 | } | ||
1861 | |||
1862 | /** | ||
1863 | * iucv_pm_freeze() - Freeze PM callback | ||
1864 | * @dev: iucv-based device | ||
1865 | * | ||
1866 | * disable iucv interrupts | ||
1867 | * invoke callback function of the iucv-based driver | ||
1868 | * shut down iucv, if no iucv-pathes are established anymore | ||
1869 | */ | ||
1870 | static int iucv_pm_freeze(struct device *dev) | ||
1871 | { | ||
1872 | int cpu; | ||
1873 | int rc = 0; | ||
1874 | |||
1875 | #ifdef CONFIG_PM_DEBUG | ||
1876 | printk(KERN_WARNING "iucv_pm_freeze\n"); | ||
1877 | #endif | ||
1878 | for_each_cpu_mask_nr(cpu, iucv_irq_cpumask) | ||
1879 | smp_call_function_single(cpu, iucv_block_cpu_almost, NULL, 1); | ||
1880 | if (dev->driver && dev->driver->pm && dev->driver->pm->freeze) | ||
1881 | rc = dev->driver->pm->freeze(dev); | ||
1882 | if (iucv_path_table_empty()) | ||
1883 | iucv_disable(); | ||
1884 | return rc; | ||
1885 | } | ||
1886 | |||
1887 | /** | ||
1888 | * iucv_pm_thaw() - Thaw PM callback | ||
1889 | * @dev: iucv-based device | ||
1890 | * | ||
1891 | * make iucv ready for use again: allocate path table, declare interrupt buffers | ||
1892 | * and enable iucv interrupts | ||
1893 | * invoke callback function of the iucv-based driver | ||
1894 | */ | ||
1895 | static int iucv_pm_thaw(struct device *dev) | ||
1896 | { | ||
1897 | int rc = 0; | ||
1898 | |||
1899 | #ifdef CONFIG_PM_DEBUG | ||
1900 | printk(KERN_WARNING "iucv_pm_thaw\n"); | ||
1901 | #endif | ||
1902 | if (!iucv_path_table) { | ||
1903 | rc = iucv_enable(); | ||
1904 | if (rc) | ||
1905 | goto out; | ||
1906 | } | ||
1907 | if (cpus_empty(iucv_irq_cpumask)) { | ||
1908 | if (iucv_nonsmp_handler) | ||
1909 | /* enable interrupts on one cpu */ | ||
1910 | iucv_allow_cpu(NULL); | ||
1911 | else | ||
1912 | /* enable interrupts on all cpus */ | ||
1913 | iucv_setmask_mp(); | ||
1914 | } | ||
1915 | if (dev->driver && dev->driver->pm && dev->driver->pm->thaw) | ||
1916 | rc = dev->driver->pm->thaw(dev); | ||
1917 | out: | ||
1918 | return rc; | ||
1919 | } | ||
1920 | |||
1921 | /** | ||
1922 | * iucv_pm_restore() - Restore PM callback | ||
1923 | * @dev: iucv-based device | ||
1924 | * | ||
1925 | * make iucv ready for use again: allocate path table, declare interrupt buffers | ||
1926 | * and enable iucv interrupts | ||
1927 | * invoke callback function of the iucv-based driver | ||
1928 | */ | ||
1929 | static int iucv_pm_restore(struct device *dev) | ||
1930 | { | ||
1931 | int rc = 0; | ||
1932 | |||
1933 | #ifdef CONFIG_PM_DEBUG | ||
1934 | printk(KERN_WARNING "iucv_pm_restore %p\n", iucv_path_table); | ||
1935 | #endif | ||
1936 | if (cpus_empty(iucv_irq_cpumask)) { | ||
1937 | rc = iucv_query_maxconn(); | ||
1938 | rc = iucv_enable(); | ||
1939 | if (rc) | ||
1940 | goto out; | ||
1941 | } | ||
1942 | if (dev->driver && dev->driver->pm && dev->driver->pm->restore) | ||
1943 | rc = dev->driver->pm->restore(dev); | ||
1944 | out: | ||
1945 | return rc; | ||
1946 | } | ||
1947 | |||
1678 | /** | 1948 | /** |
1679 | * iucv_init | 1949 | * iucv_init |
1680 | * | 1950 | * |
@@ -1717,23 +1987,37 @@ static int __init iucv_init(void) | |||
1717 | rc = -ENOMEM; | 1987 | rc = -ENOMEM; |
1718 | goto out_free; | 1988 | goto out_free; |
1719 | } | 1989 | } |
1990 | iucv_param_irq[cpu] = kmalloc_node(sizeof(union iucv_param), | ||
1991 | GFP_KERNEL|GFP_DMA, cpu_to_node(cpu)); | ||
1992 | if (!iucv_param_irq[cpu]) { | ||
1993 | rc = -ENOMEM; | ||
1994 | goto out_free; | ||
1995 | } | ||
1996 | |||
1720 | } | 1997 | } |
1721 | rc = register_hotcpu_notifier(&iucv_cpu_notifier); | 1998 | rc = register_hotcpu_notifier(&iucv_cpu_notifier); |
1722 | if (rc) | 1999 | if (rc) |
1723 | goto out_free; | 2000 | goto out_free; |
2001 | rc = register_reboot_notifier(&iucv_reboot_notifier); | ||
2002 | if (rc) | ||
2003 | goto out_cpu; | ||
1724 | ASCEBC(iucv_error_no_listener, 16); | 2004 | ASCEBC(iucv_error_no_listener, 16); |
1725 | ASCEBC(iucv_error_no_memory, 16); | 2005 | ASCEBC(iucv_error_no_memory, 16); |
1726 | ASCEBC(iucv_error_pathid, 16); | 2006 | ASCEBC(iucv_error_pathid, 16); |
1727 | iucv_available = 1; | 2007 | iucv_available = 1; |
1728 | rc = bus_register(&iucv_bus); | 2008 | rc = bus_register(&iucv_bus); |
1729 | if (rc) | 2009 | if (rc) |
1730 | goto out_cpu; | 2010 | goto out_reboot; |
1731 | return 0; | 2011 | return 0; |
1732 | 2012 | ||
2013 | out_reboot: | ||
2014 | unregister_reboot_notifier(&iucv_reboot_notifier); | ||
1733 | out_cpu: | 2015 | out_cpu: |
1734 | unregister_hotcpu_notifier(&iucv_cpu_notifier); | 2016 | unregister_hotcpu_notifier(&iucv_cpu_notifier); |
1735 | out_free: | 2017 | out_free: |
1736 | for_each_possible_cpu(cpu) { | 2018 | for_each_possible_cpu(cpu) { |
2019 | kfree(iucv_param_irq[cpu]); | ||
2020 | iucv_param_irq[cpu] = NULL; | ||
1737 | kfree(iucv_param[cpu]); | 2021 | kfree(iucv_param[cpu]); |
1738 | iucv_param[cpu] = NULL; | 2022 | iucv_param[cpu] = NULL; |
1739 | kfree(iucv_irq_data[cpu]); | 2023 | kfree(iucv_irq_data[cpu]); |
@@ -1762,8 +2046,11 @@ static void __exit iucv_exit(void) | |||
1762 | list_for_each_entry_safe(p, n, &iucv_work_queue, list) | 2046 | list_for_each_entry_safe(p, n, &iucv_work_queue, list) |
1763 | kfree(p); | 2047 | kfree(p); |
1764 | spin_unlock_irq(&iucv_queue_lock); | 2048 | spin_unlock_irq(&iucv_queue_lock); |
2049 | unregister_reboot_notifier(&iucv_reboot_notifier); | ||
1765 | unregister_hotcpu_notifier(&iucv_cpu_notifier); | 2050 | unregister_hotcpu_notifier(&iucv_cpu_notifier); |
1766 | for_each_possible_cpu(cpu) { | 2051 | for_each_possible_cpu(cpu) { |
2052 | kfree(iucv_param_irq[cpu]); | ||
2053 | iucv_param_irq[cpu] = NULL; | ||
1767 | kfree(iucv_param[cpu]); | 2054 | kfree(iucv_param[cpu]); |
1768 | iucv_param[cpu] = NULL; | 2055 | iucv_param[cpu] = NULL; |
1769 | kfree(iucv_irq_data[cpu]); | 2056 | kfree(iucv_irq_data[cpu]); |