smp: Add function to execute a function synchronously on a CPU
[linux-2.6-block.git] / kernel / smp.c
index 4274ca5f3bbcdad3de081d8e56d42a14f38d18b1..f4f6137941cbefd27bf068bf1c9d93a9b5976499 100644 (file)
@@ -725,3 +725,53 @@ void wake_up_all_idle_cpus(void)
        preempt_enable();
 }
 EXPORT_SYMBOL_GPL(wake_up_all_idle_cpus);
+
+/**
+ * smp_call_on_cpu - Call a function on a specific cpu
+ *
+ * Used to call a function on a specific cpu and wait for it to return.
+ * Optionally make sure the call is done on a specified physical cpu via vcpu
+ * pinning in order to support virtualized environments.
+ */
+struct smp_call_on_cpu_struct {
+       struct work_struct      work;
+       struct completion       done;
+       int                     (*func)(void *);
+       void                    *data;
+       int                     ret;
+       int                     cpu;
+};
+
+static void smp_call_on_cpu_callback(struct work_struct *work)
+{
+       struct smp_call_on_cpu_struct *sscs;
+
+       sscs = container_of(work, struct smp_call_on_cpu_struct, work);
+       if (sscs->cpu >= 0)
+               hypervisor_pin_vcpu(sscs->cpu);
+       sscs->ret = sscs->func(sscs->data);
+       if (sscs->cpu >= 0)
+               hypervisor_pin_vcpu(-1);
+
+       complete(&sscs->done);
+}
+
+int smp_call_on_cpu(unsigned int cpu, int (*func)(void *), void *par, bool phys)
+{
+       struct smp_call_on_cpu_struct sscs = {
+               .work = __WORK_INITIALIZER(sscs.work, smp_call_on_cpu_callback),
+               .done = COMPLETION_INITIALIZER_ONSTACK(sscs.done),
+               .func = func,
+               .data = par,
+               .cpu  = phys ? cpu : -1,
+       };
+
+       if (cpu >= nr_cpu_ids || !cpu_online(cpu))
+               return -ENXIO;
+
+       queue_work_on(cpu, system_wq, &sscs.work);
+       wait_for_completion(&sscs.done);
+
+       return sscs.ret;
+}
+EXPORT_SYMBOL_GPL(smp_call_on_cpu);