diff options
-rw-r--r-- | include/linux/smp.h | 3 | ||||
-rw-r--r-- | kernel/smp.c | 50 | ||||
-rw-r--r-- | kernel/up.c | 17 |
3 files changed, 70 insertions, 0 deletions
diff --git a/include/linux/smp.h b/include/linux/smp.h index eccae4690f41..8e0cb7a0f836 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h | |||
@@ -196,6 +196,9 @@ extern void arch_enable_nonboot_cpus_end(void); | |||
196 | 196 | ||
197 | void smp_setup_processor_id(void); | 197 | void smp_setup_processor_id(void); |
198 | 198 | ||
199 | int smp_call_on_cpu(unsigned int cpu, int (*func)(void *), void *par, | ||
200 | bool phys); | ||
201 | |||
199 | /* SMP core functions */ | 202 | /* SMP core functions */ |
200 | int smpcfd_prepare_cpu(unsigned int cpu); | 203 | int smpcfd_prepare_cpu(unsigned int cpu); |
201 | int smpcfd_dead_cpu(unsigned int cpu); | 204 | int smpcfd_dead_cpu(unsigned int cpu); |
diff --git a/kernel/smp.c b/kernel/smp.c index 4274ca5f3bbc..f4f6137941cb 100644 --- a/kernel/smp.c +++ b/kernel/smp.c | |||
@@ -725,3 +725,53 @@ void wake_up_all_idle_cpus(void) | |||
725 | preempt_enable(); | 725 | preempt_enable(); |
726 | } | 726 | } |
727 | EXPORT_SYMBOL_GPL(wake_up_all_idle_cpus); | 727 | EXPORT_SYMBOL_GPL(wake_up_all_idle_cpus); |
728 | |||
729 | /** | ||
730 | * smp_call_on_cpu - Call a function on a specific cpu | ||
731 | * | ||
732 | * Used to call a function on a specific cpu and wait for it to return. | ||
733 | * Optionally make sure the call is done on a specified physical cpu via vcpu | ||
734 | * pinning in order to support virtualized environments. | ||
735 | */ | ||
736 | struct smp_call_on_cpu_struct { | ||
737 | struct work_struct work; | ||
738 | struct completion done; | ||
739 | int (*func)(void *); | ||
740 | void *data; | ||
741 | int ret; | ||
742 | int cpu; | ||
743 | }; | ||
744 | |||
745 | static void smp_call_on_cpu_callback(struct work_struct *work) | ||
746 | { | ||
747 | struct smp_call_on_cpu_struct *sscs; | ||
748 | |||
749 | sscs = container_of(work, struct smp_call_on_cpu_struct, work); | ||
750 | if (sscs->cpu >= 0) | ||
751 | hypervisor_pin_vcpu(sscs->cpu); | ||
752 | sscs->ret = sscs->func(sscs->data); | ||
753 | if (sscs->cpu >= 0) | ||
754 | hypervisor_pin_vcpu(-1); | ||
755 | |||
756 | complete(&sscs->done); | ||
757 | } | ||
758 | |||
759 | int smp_call_on_cpu(unsigned int cpu, int (*func)(void *), void *par, bool phys) | ||
760 | { | ||
761 | struct smp_call_on_cpu_struct sscs = { | ||
762 | .work = __WORK_INITIALIZER(sscs.work, smp_call_on_cpu_callback), | ||
763 | .done = COMPLETION_INITIALIZER_ONSTACK(sscs.done), | ||
764 | .func = func, | ||
765 | .data = par, | ||
766 | .cpu = phys ? cpu : -1, | ||
767 | }; | ||
768 | |||
769 | if (cpu >= nr_cpu_ids || !cpu_online(cpu)) | ||
770 | return -ENXIO; | ||
771 | |||
772 | queue_work_on(cpu, system_wq, &sscs.work); | ||
773 | wait_for_completion(&sscs.done); | ||
774 | |||
775 | return sscs.ret; | ||
776 | } | ||
777 | EXPORT_SYMBOL_GPL(smp_call_on_cpu); | ||
diff --git a/kernel/up.c b/kernel/up.c index 3ccee2bd13ba..ee81ac9af4ca 100644 --- a/kernel/up.c +++ b/kernel/up.c | |||
@@ -83,3 +83,20 @@ void on_each_cpu_cond(bool (*cond_func)(int cpu, void *info), | |||
83 | preempt_enable(); | 83 | preempt_enable(); |
84 | } | 84 | } |
85 | EXPORT_SYMBOL(on_each_cpu_cond); | 85 | EXPORT_SYMBOL(on_each_cpu_cond); |
86 | |||
87 | int smp_call_on_cpu(unsigned int cpu, int (*func)(void *), void *par, bool phys) | ||
88 | { | ||
89 | int ret; | ||
90 | |||
91 | if (cpu != 0) | ||
92 | return -ENXIO; | ||
93 | |||
94 | if (phys) | ||
95 | hypervisor_pin_vcpu(0); | ||
96 | ret = func(par); | ||
97 | if (phys) | ||
98 | hypervisor_pin_vcpu(-1); | ||
99 | |||
100 | return ret; | ||
101 | } | ||
102 | EXPORT_SYMBOL_GPL(smp_call_on_cpu); | ||