Commit d7a0dab8 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'core-smp-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull core SMP updates from Ingo Molnar:
 "Two main change is generic vCPU pinning and physical CPU SMP-call
  support, for Xen to be able to perform certain calls on specific
  physical CPUs - by Juergen Gross"

* 'core-smp-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  smp: Allocate smp_call_on_cpu() workqueue on stack too
  hwmon: Use smp_call_on_cpu() for dell-smm i8k
  dcdbas: Make use of smp_call_on_cpu()
  xen: Add xen_pin_vcpu() to support calling functions on a dedicated pCPU
  smp: Add function to execute a function synchronously on a CPU
  virt, sched: Add generic vCPU pinning support
  xen: Sync xen header
parents 4b978934 8db54949
...@@ -8860,6 +8860,7 @@ S: Supported ...@@ -8860,6 +8860,7 @@ S: Supported
F: Documentation/virtual/paravirt_ops.txt F: Documentation/virtual/paravirt_ops.txt
F: arch/*/kernel/paravirt* F: arch/*/kernel/paravirt*
F: arch/*/include/asm/paravirt.h F: arch/*/include/asm/paravirt.h
F: include/linux/hypervisor.h
PARIDE DRIVERS FOR PARALLEL PORT IDE DEVICES PARIDE DRIVERS FOR PARALLEL PORT IDE DEVICES
M: Tim Waugh <tim@cyberelk.net> M: Tim Waugh <tim@cyberelk.net>
......
...@@ -43,6 +43,9 @@ struct hypervisor_x86 { ...@@ -43,6 +43,9 @@ struct hypervisor_x86 {
/* X2APIC detection (run once per boot) */ /* X2APIC detection (run once per boot) */
bool (*x2apic_available)(void); bool (*x2apic_available)(void);
/* pin current vcpu to specified physical cpu (run rarely) */
void (*pin_vcpu)(int);
}; };
extern const struct hypervisor_x86 *x86_hyper; extern const struct hypervisor_x86 *x86_hyper;
...@@ -56,6 +59,7 @@ extern const struct hypervisor_x86 x86_hyper_kvm; ...@@ -56,6 +59,7 @@ extern const struct hypervisor_x86 x86_hyper_kvm;
extern void init_hypervisor(struct cpuinfo_x86 *c); extern void init_hypervisor(struct cpuinfo_x86 *c);
extern void init_hypervisor_platform(void); extern void init_hypervisor_platform(void);
extern bool hypervisor_x2apic_available(void); extern bool hypervisor_x2apic_available(void);
extern void hypervisor_pin_vcpu(int cpu);
#else #else
static inline void init_hypervisor(struct cpuinfo_x86 *c) { } static inline void init_hypervisor(struct cpuinfo_x86 *c) { }
static inline void init_hypervisor_platform(void) { } static inline void init_hypervisor_platform(void) { }
......
...@@ -86,3 +86,14 @@ bool __init hypervisor_x2apic_available(void) ...@@ -86,3 +86,14 @@ bool __init hypervisor_x2apic_available(void)
x86_hyper->x2apic_available && x86_hyper->x2apic_available &&
x86_hyper->x2apic_available(); x86_hyper->x2apic_available();
} }
void hypervisor_pin_vcpu(int cpu)
{
if (!x86_hyper)
return;
if (x86_hyper->pin_vcpu)
x86_hyper->pin_vcpu(cpu);
else
WARN_ONCE(1, "vcpu pinning requested but not supported!\n");
}
...@@ -1925,6 +1925,45 @@ static void xen_set_cpu_features(struct cpuinfo_x86 *c) ...@@ -1925,6 +1925,45 @@ static void xen_set_cpu_features(struct cpuinfo_x86 *c)
} }
} }
static void xen_pin_vcpu(int cpu)
{
static bool disable_pinning;
struct sched_pin_override pin_override;
int ret;
if (disable_pinning)
return;
pin_override.pcpu = cpu;
ret = HYPERVISOR_sched_op(SCHEDOP_pin_override, &pin_override);
/* Ignore errors when removing override. */
if (cpu < 0)
return;
switch (ret) {
case -ENOSYS:
pr_warn("Unable to pin on physical cpu %d. In case of problems consider vcpu pinning.\n",
cpu);
disable_pinning = true;
break;
case -EPERM:
WARN(1, "Trying to pin vcpu without having privilege to do so\n");
disable_pinning = true;
break;
case -EINVAL:
case -EBUSY:
pr_warn("Physical cpu %d not available for pinning. Check Xen cpu configuration.\n",
cpu);
break;
case 0:
break;
default:
WARN(1, "rc %d while trying to pin vcpu\n", ret);
disable_pinning = true;
}
}
const struct hypervisor_x86 x86_hyper_xen = { const struct hypervisor_x86 x86_hyper_xen = {
.name = "Xen", .name = "Xen",
.detect = xen_platform, .detect = xen_platform,
...@@ -1933,6 +1972,7 @@ const struct hypervisor_x86 x86_hyper_xen = { ...@@ -1933,6 +1972,7 @@ const struct hypervisor_x86 x86_hyper_xen = {
#endif #endif
.x2apic_available = xen_x2apic_para_available, .x2apic_available = xen_x2apic_para_available,
.set_cpu_features = xen_set_cpu_features, .set_cpu_features = xen_set_cpu_features,
.pin_vcpu = xen_pin_vcpu,
}; };
EXPORT_SYMBOL(x86_hyper_xen); EXPORT_SYMBOL(x86_hyper_xen);
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/cpu.h>
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -238,33 +239,14 @@ static ssize_t host_control_on_shutdown_store(struct device *dev, ...@@ -238,33 +239,14 @@ static ssize_t host_control_on_shutdown_store(struct device *dev,
return count; return count;
} }
/** static int raise_smi(void *par)
* dcdbas_smi_request: generate SMI request
*
* Called with smi_data_lock.
*/
int dcdbas_smi_request(struct smi_cmd *smi_cmd)
{ {
cpumask_var_t old_mask; struct smi_cmd *smi_cmd = par;
int ret = 0;
if (smi_cmd->magic != SMI_CMD_MAGIC) {
dev_info(&dcdbas_pdev->dev, "%s: invalid magic value\n",
__func__);
return -EBADR;
}
/* SMI requires CPU 0 */
if (!alloc_cpumask_var(&old_mask, GFP_KERNEL))
return -ENOMEM;
cpumask_copy(old_mask, &current->cpus_allowed);
set_cpus_allowed_ptr(current, cpumask_of(0));
if (smp_processor_id() != 0) { if (smp_processor_id() != 0) {
dev_dbg(&dcdbas_pdev->dev, "%s: failed to get CPU 0\n", dev_dbg(&dcdbas_pdev->dev, "%s: failed to get CPU 0\n",
__func__); __func__);
ret = -EBUSY; return -EBUSY;
goto out;
} }
/* generate SMI */ /* generate SMI */
...@@ -280,9 +262,28 @@ int dcdbas_smi_request(struct smi_cmd *smi_cmd) ...@@ -280,9 +262,28 @@ int dcdbas_smi_request(struct smi_cmd *smi_cmd)
: "memory" : "memory"
); );
out: return 0;
set_cpus_allowed_ptr(current, old_mask); }
free_cpumask_var(old_mask); /**
* dcdbas_smi_request: generate SMI request
*
* Called with smi_data_lock.
*/
int dcdbas_smi_request(struct smi_cmd *smi_cmd)
{
int ret;
if (smi_cmd->magic != SMI_CMD_MAGIC) {
dev_info(&dcdbas_pdev->dev, "%s: invalid magic value\n",
__func__);
return -EBADR;
}
/* SMI requires CPU 0 */
get_online_cpus();
ret = smp_call_on_cpu(0, raise_smi, smi_cmd, true);
put_online_cpus();
return ret; return ret;
} }
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/cpu.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -36,6 +37,7 @@ ...@@ -36,6 +37,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/smp.h>
#include <linux/i8k.h> #include <linux/i8k.h>
...@@ -134,11 +136,11 @@ static inline const char *i8k_get_dmi_data(int field) ...@@ -134,11 +136,11 @@ static inline const char *i8k_get_dmi_data(int field)
/* /*
* Call the System Management Mode BIOS. Code provided by Jonathan Buzzard. * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard.
*/ */
static int i8k_smm(struct smm_regs *regs) static int i8k_smm_func(void *par)
{ {
int rc; int rc;
struct smm_regs *regs = par;
int eax = regs->eax; int eax = regs->eax;
cpumask_var_t old_mask;
#ifdef DEBUG #ifdef DEBUG
int ebx = regs->ebx; int ebx = regs->ebx;
...@@ -149,16 +151,8 @@ static int i8k_smm(struct smm_regs *regs) ...@@ -149,16 +151,8 @@ static int i8k_smm(struct smm_regs *regs)
#endif #endif
/* SMM requires CPU 0 */ /* SMM requires CPU 0 */
if (!alloc_cpumask_var(&old_mask, GFP_KERNEL)) if (smp_processor_id() != 0)
return -ENOMEM; return -EBUSY;
cpumask_copy(old_mask, &current->cpus_allowed);
rc = set_cpus_allowed_ptr(current, cpumask_of(0));
if (rc)
goto out;
if (smp_processor_id() != 0) {
rc = -EBUSY;
goto out;
}
#if defined(CONFIG_X86_64) #if defined(CONFIG_X86_64)
asm volatile("pushq %%rax\n\t" asm volatile("pushq %%rax\n\t"
...@@ -216,10 +210,6 @@ static int i8k_smm(struct smm_regs *regs) ...@@ -216,10 +210,6 @@ static int i8k_smm(struct smm_regs *regs)
if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax) if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
rc = -EINVAL; rc = -EINVAL;
out:
set_cpus_allowed_ptr(current, old_mask);
free_cpumask_var(old_mask);
#ifdef DEBUG #ifdef DEBUG
rettime = ktime_get(); rettime = ktime_get();
delta = ktime_sub(rettime, calltime); delta = ktime_sub(rettime, calltime);
...@@ -231,6 +221,20 @@ static int i8k_smm(struct smm_regs *regs) ...@@ -231,6 +221,20 @@ static int i8k_smm(struct smm_regs *regs)
return rc; return rc;
} }
/*
* Call the System Management Mode BIOS.
*/
static int i8k_smm(struct smm_regs *regs)
{
int ret;
get_online_cpus();
ret = smp_call_on_cpu(0, i8k_smm_func, regs, true);
put_online_cpus();
return ret;
}
/* /*
* Read the fan status. * Read the fan status.
*/ */
......
#ifndef __LINUX_HYPEVISOR_H
#define __LINUX_HYPEVISOR_H
/*
* Generic Hypervisor support
* Juergen Gross <jgross@suse.com>
*/
#ifdef CONFIG_HYPERVISOR_GUEST
#include <asm/hypervisor.h>
#else
static inline void hypervisor_pin_vcpu(int cpu)
{
}
#endif
#endif /* __LINUX_HYPEVISOR_H */
...@@ -196,6 +196,9 @@ extern void arch_enable_nonboot_cpus_end(void); ...@@ -196,6 +196,9 @@ extern void arch_enable_nonboot_cpus_end(void);
void smp_setup_processor_id(void); void smp_setup_processor_id(void);
int smp_call_on_cpu(unsigned int cpu, int (*func)(void *), void *par,
bool phys);
/* SMP core functions */ /* SMP core functions */
int smpcfd_prepare_cpu(unsigned int cpu); int smpcfd_prepare_cpu(unsigned int cpu);
int smpcfd_dead_cpu(unsigned int cpu); int smpcfd_dead_cpu(unsigned int cpu);
......
...@@ -3,6 +3,24 @@ ...@@ -3,6 +3,24 @@
* *
* Scheduler state interactions * Scheduler state interactions
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Copyright (c) 2005, Keir Fraser <keir@xensource.com> * Copyright (c) 2005, Keir Fraser <keir@xensource.com>
*/ */
...@@ -11,19 +29,31 @@ ...@@ -11,19 +29,31 @@
#include <xen/interface/event_channel.h> #include <xen/interface/event_channel.h>
/*
* Guest Scheduler Operations
*
* The SCHEDOP interface provides mechanisms for a guest to interact
* with the scheduler, including yield, blocking and shutting itself
* down.
*/
/* /*
* The prototype for this hypercall is: * The prototype for this hypercall is:
* long sched_op_new(int cmd, void *arg) * long HYPERVISOR_sched_op(enum sched_op cmd, void *arg, ...)
*
* @cmd == SCHEDOP_??? (scheduler operation). * @cmd == SCHEDOP_??? (scheduler operation).
* @arg == Operation-specific extra argument(s), as described below. * @arg == Operation-specific extra argument(s), as described below.
* ... == Additional Operation-specific extra arguments, described below.
* *
* **NOTE**: * Versions of Xen prior to 3.0.2 provided only the following legacy version
* Versions of Xen prior to 3.0.2 provide only the following legacy version
* of this hypercall, supporting only the commands yield, block and shutdown: * of this hypercall, supporting only the commands yield, block and shutdown:
* long sched_op(int cmd, unsigned long arg) * long sched_op(int cmd, unsigned long arg)
* @cmd == SCHEDOP_??? (scheduler operation). * @cmd == SCHEDOP_??? (scheduler operation).
* @arg == 0 (SCHEDOP_yield and SCHEDOP_block) * @arg == 0 (SCHEDOP_yield and SCHEDOP_block)
* == SHUTDOWN_* code (SCHEDOP_shutdown) * == SHUTDOWN_* code (SCHEDOP_shutdown)
*
* This legacy version is available to new guests as:
* long HYPERVISOR_sched_op_compat(enum sched_op cmd, unsigned long arg)
*/ */
/* /*
...@@ -44,12 +74,17 @@ ...@@ -44,12 +74,17 @@
/* /*
* Halt execution of this domain (all VCPUs) and notify the system controller. * Halt execution of this domain (all VCPUs) and notify the system controller.
* @arg == pointer to sched_shutdown structure. * @arg == pointer to sched_shutdown structure.
*
* If the sched_shutdown_t reason is SHUTDOWN_suspend then
* x86 PV guests must also set RDX (EDX for 32-bit guests) to the MFN
* of the guest's start info page. RDX/EDX is the third hypercall
* argument.
*
* In addition, which reason is SHUTDOWN_suspend this hypercall
* returns 1 if suspend was cancelled or the domain was merely
* checkpointed, and 0 if it is resuming in a new domain.
*/ */
#define SCHEDOP_shutdown 2 #define SCHEDOP_shutdown 2
struct sched_shutdown {
unsigned int reason; /* SHUTDOWN_* */
};
DEFINE_GUEST_HANDLE_STRUCT(sched_shutdown);
/* /*
* Poll a set of event-channel ports. Return when one or more are pending. An * Poll a set of event-channel ports. Return when one or more are pending. An
...@@ -57,12 +92,6 @@ DEFINE_GUEST_HANDLE_STRUCT(sched_shutdown); ...@@ -57,12 +92,6 @@ DEFINE_GUEST_HANDLE_STRUCT(sched_shutdown);
* @arg == pointer to sched_poll structure. * @arg == pointer to sched_poll structure.
*/ */
#define SCHEDOP_poll 3 #define SCHEDOP_poll 3
struct sched_poll {
GUEST_HANDLE(evtchn_port_t) ports;
unsigned int nr_ports;
uint64_t timeout;
};
DEFINE_GUEST_HANDLE_STRUCT(sched_poll);
/* /*
* Declare a shutdown for another domain. The main use of this function is * Declare a shutdown for another domain. The main use of this function is
...@@ -71,15 +100,11 @@ DEFINE_GUEST_HANDLE_STRUCT(sched_poll); ...@@ -71,15 +100,11 @@ DEFINE_GUEST_HANDLE_STRUCT(sched_poll);
* @arg == pointer to sched_remote_shutdown structure. * @arg == pointer to sched_remote_shutdown structure.
*/ */
#define SCHEDOP_remote_shutdown 4 #define SCHEDOP_remote_shutdown 4
struct sched_remote_shutdown {
domid_t domain_id; /* Remote domain ID */
unsigned int reason; /* SHUTDOWN_xxx reason */
};
/* /*
* Latch a shutdown code, so that when the domain later shuts down it * Latch a shutdown code, so that when the domain later shuts down it
* reports this code to the control tools. * reports this code to the control tools.
* @arg == as for SCHEDOP_shutdown. * @arg == sched_shutdown, as for SCHEDOP_shutdown.
*/ */
#define SCHEDOP_shutdown_code 5 #define SCHEDOP_shutdown_code 5
...@@ -92,10 +117,47 @@ struct sched_remote_shutdown { ...@@ -92,10 +117,47 @@ struct sched_remote_shutdown {
* With id != 0 and timeout != 0, poke watchdog timer and set new timeout. * With id != 0 and timeout != 0, poke watchdog timer and set new timeout.
*/ */
#define SCHEDOP_watchdog 6 #define SCHEDOP_watchdog 6
/*
* Override the current vcpu affinity by pinning it to one physical cpu or
* undo this override restoring the previous affinity.
* @arg == pointer to sched_pin_override structure.
*
* A negative pcpu value will undo a previous pin override and restore the
* previous cpu affinity.
* This call is allowed for the hardware domain only and requires the cpu
* to be part of the domain's cpupool.
*/
#define SCHEDOP_pin_override 7
struct sched_shutdown {
unsigned int reason; /* SHUTDOWN_* => shutdown reason */
};
DEFINE_GUEST_HANDLE_STRUCT(sched_shutdown);
struct sched_poll {
GUEST_HANDLE(evtchn_port_t) ports;
unsigned int nr_ports;
uint64_t timeout;
};
DEFINE_GUEST_HANDLE_STRUCT(sched_poll);
struct sched_remote_shutdown {
domid_t domain_id; /* Remote domain ID */
unsigned int reason; /* SHUTDOWN_* => shutdown reason */
};
DEFINE_GUEST_HANDLE_STRUCT(sched_remote_shutdown);
struct sched_watchdog { struct sched_watchdog {
uint32_t id; /* watchdog ID */ uint32_t id; /* watchdog ID */
uint32_t timeout; /* timeout */ uint32_t timeout; /* timeout */
}; };
DEFINE_GUEST_HANDLE_STRUCT(sched_watchdog);
struct sched_pin_override {
int32_t pcpu;
};
DEFINE_GUEST_HANDLE_STRUCT(sched_pin_override);
/* /*
* Reason codes for SCHEDOP_shutdown. These may be interpreted by control * Reason codes for SCHEDOP_shutdown. These may be interpreted by control
...@@ -107,6 +169,7 @@ struct sched_watchdog { ...@@ -107,6 +169,7 @@ struct sched_watchdog {
#define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */ #define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */
#define SHUTDOWN_crash 3 /* Tell controller we've crashed. */ #define SHUTDOWN_crash 3 /* Tell controller we've crashed. */
#define SHUTDOWN_watchdog 4 /* Restart because watchdog time expired. */ #define SHUTDOWN_watchdog 4 /* Restart because watchdog time expired. */
/* /*
* Domain asked to perform 'soft reset' for it. The expected behavior is to * Domain asked to perform 'soft reset' for it. The expected behavior is to
* reset internal Xen state for the domain returning it to the point where it * reset internal Xen state for the domain returning it to the point where it
...@@ -115,5 +178,6 @@ struct sched_watchdog { ...@@ -115,5 +178,6 @@ struct sched_watchdog {
* interfaces again. * interfaces again.
*/ */
#define SHUTDOWN_soft_reset 5 #define SHUTDOWN_soft_reset 5
#define SHUTDOWN_MAX 5 /* Maximum valid shutdown reason. */
#endif /* __XEN_PUBLIC_SCHED_H__ */ #endif /* __XEN_PUBLIC_SCHED_H__ */
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/hypervisor.h>
#include "smpboot.h" #include "smpboot.h"
...@@ -724,3 +725,54 @@ void wake_up_all_idle_cpus(void) ...@@ -724,3 +725,54 @@ void wake_up_all_idle_cpus(void)
preempt_enable(); preempt_enable();
} }
EXPORT_SYMBOL_GPL(wake_up_all_idle_cpus); 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 = {
.done = COMPLETION_INITIALIZER_ONSTACK(sscs.done),
.func = func,
.data = par,
.cpu = phys ? cpu : -1,
};
INIT_WORK_ONSTACK(&sscs.work, smp_call_on_cpu_callback);
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);
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/hypervisor.h>
int smp_call_function_single(int cpu, void (*func) (void *info), void *info, int smp_call_function_single(int cpu, void (*func) (void *info), void *info,
int wait) int wait)
...@@ -82,3 +83,20 @@ void on_each_cpu_cond(bool (*cond_func)(int cpu, void *info), ...@@ -82,3 +83,20 @@ void on_each_cpu_cond(bool (*cond_func)(int cpu, void *info),
preempt_enable(); preempt_enable();
} }
EXPORT_SYMBOL(on_each_cpu_cond); EXPORT_SYMBOL(on_each_cpu_cond);
int smp_call_on_cpu(unsigned int cpu, int (*func)(void *), void *par, bool phys)
{
int ret;
if (cpu != 0)
return -ENXIO;
if (phys)
hypervisor_pin_vcpu(0);
ret = func(par);
if (phys)
hypervisor_pin_vcpu(-1);
return ret;
}
EXPORT_SYMBOL_GPL(smp_call_on_cpu);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment