|
@@ -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);
|