aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/smp.h3
-rw-r--r--kernel/smp.c50
-rw-r--r--kernel/up.c17
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
197void smp_setup_processor_id(void); 197void smp_setup_processor_id(void);
198 198
199int smp_call_on_cpu(unsigned int cpu, int (*func)(void *), void *par,
200 bool phys);
201
199/* SMP core functions */ 202/* SMP core functions */
200int smpcfd_prepare_cpu(unsigned int cpu); 203int smpcfd_prepare_cpu(unsigned int cpu);
201int smpcfd_dead_cpu(unsigned int cpu); 204int 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}
727EXPORT_SYMBOL_GPL(wake_up_all_idle_cpus); 727EXPORT_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 */
736struct 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
745static 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
759int 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}
777EXPORT_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}
85EXPORT_SYMBOL(on_each_cpu_cond); 85EXPORT_SYMBOL(on_each_cpu_cond);
86
87int 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}
102EXPORT_SYMBOL_GPL(smp_call_on_cpu);