Merge tag 'smp-core-2020-03-30' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull core SMP updates from Thomas Gleixner: "CPU (hotplug) updates: - Support for locked CSD objects in smp_call_function_single_async() which allows to simplify callsites in the scheduler core and MIPS - Treewide consolidation of CPU hotplug functions which ensures the consistency between the sysfs interface and kernel state. The low level functions cpu_up/down() are now confined to the core code and not longer accessible from random code" * tag 'smp-core-2020-03-30' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (22 commits) cpu/hotplug: Ignore pm_wakeup_pending() for disable_nonboot_cpus() cpu/hotplug: Hide cpu_up/down() cpu/hotplug: Move bringup of secondary CPUs out of smp_init() torture: Replace cpu_up/down() with add/remove_cpu() firmware: psci: Replace cpu_up/down() with add/remove_cpu() xen/cpuhotplug: Replace cpu_up/down() with device_online/offline() parisc: Replace cpu_up/down() with add/remove_cpu() sparc: Replace cpu_up/down() with add/remove_cpu() powerpc: Replace cpu_up/down() with add/remove_cpu() x86/smp: Replace cpu_up/down() with add/remove_cpu() arm64: hibernate: Use bringup_hibernate_cpu() cpu/hotplug: Provide bringup_hibernate_cpu() arm64: Use reboot_cpu instead of hardconding it to 0 arm64: Don't use disable_nonboot_cpus() ARM: Use reboot_cpu instead of hardcoding it to 0 ARM: Don't use disable_nonboot_cpus() ia64: Replace cpu_down() with smp_shutdown_nonboot_cpus() cpu/hotplug: Create a new function to shutdown nonboot cpus cpu/hotplug: Add new {add,remove}_cpu() functions sched/core: Remove rq.hrtick_csd_pending ...
This commit is contained in:
143
kernel/cpu.c
143
kernel/cpu.c
@@ -1041,7 +1041,7 @@ static int cpu_down_maps_locked(unsigned int cpu, enum cpuhp_state target)
|
||||
return _cpu_down(cpu, 0, target);
|
||||
}
|
||||
|
||||
static int do_cpu_down(unsigned int cpu, enum cpuhp_state target)
|
||||
static int cpu_down(unsigned int cpu, enum cpuhp_state target)
|
||||
{
|
||||
int err;
|
||||
|
||||
@@ -1051,11 +1051,72 @@ static int do_cpu_down(unsigned int cpu, enum cpuhp_state target)
|
||||
return err;
|
||||
}
|
||||
|
||||
int cpu_down(unsigned int cpu)
|
||||
/**
|
||||
* cpu_device_down - Bring down a cpu device
|
||||
* @dev: Pointer to the cpu device to offline
|
||||
*
|
||||
* This function is meant to be used by device core cpu subsystem only.
|
||||
*
|
||||
* Other subsystems should use remove_cpu() instead.
|
||||
*/
|
||||
int cpu_device_down(struct device *dev)
|
||||
{
|
||||
return do_cpu_down(cpu, CPUHP_OFFLINE);
|
||||
return cpu_down(dev->id, CPUHP_OFFLINE);
|
||||
}
|
||||
|
||||
int remove_cpu(unsigned int cpu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
lock_device_hotplug();
|
||||
ret = device_offline(get_cpu_device(cpu));
|
||||
unlock_device_hotplug();
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(remove_cpu);
|
||||
|
||||
void smp_shutdown_nonboot_cpus(unsigned int primary_cpu)
|
||||
{
|
||||
unsigned int cpu;
|
||||
int error;
|
||||
|
||||
cpu_maps_update_begin();
|
||||
|
||||
/*
|
||||
* Make certain the cpu I'm about to reboot on is online.
|
||||
*
|
||||
* This is inline to what migrate_to_reboot_cpu() already do.
|
||||
*/
|
||||
if (!cpu_online(primary_cpu))
|
||||
primary_cpu = cpumask_first(cpu_online_mask);
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
if (cpu == primary_cpu)
|
||||
continue;
|
||||
|
||||
error = cpu_down_maps_locked(cpu, CPUHP_OFFLINE);
|
||||
if (error) {
|
||||
pr_err("Failed to offline CPU%d - error=%d",
|
||||
cpu, error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure all but the reboot CPU are offline.
|
||||
*/
|
||||
BUG_ON(num_online_cpus() > 1);
|
||||
|
||||
/*
|
||||
* Make sure the CPUs won't be enabled by someone else after this
|
||||
* point. Kexec will reboot to a new kernel shortly resetting
|
||||
* everything along the way.
|
||||
*/
|
||||
cpu_hotplug_disabled++;
|
||||
|
||||
cpu_maps_update_done();
|
||||
}
|
||||
EXPORT_SYMBOL(cpu_down);
|
||||
|
||||
#else
|
||||
#define takedown_cpu NULL
|
||||
@@ -1124,8 +1185,8 @@ static int _cpu_up(unsigned int cpu, int tasks_frozen, enum cpuhp_state target)
|
||||
}
|
||||
|
||||
/*
|
||||
* The caller of do_cpu_up might have raced with another
|
||||
* caller. Ignore it for now.
|
||||
* The caller of cpu_up() might have raced with another
|
||||
* caller. Nothing to do.
|
||||
*/
|
||||
if (st->state >= target)
|
||||
goto out;
|
||||
@@ -1169,7 +1230,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_cpu_up(unsigned int cpu, enum cpuhp_state target)
|
||||
static int cpu_up(unsigned int cpu, enum cpuhp_state target)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
@@ -1203,16 +1264,70 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
int cpu_up(unsigned int cpu)
|
||||
/**
|
||||
* cpu_device_up - Bring up a cpu device
|
||||
* @dev: Pointer to the cpu device to online
|
||||
*
|
||||
* This function is meant to be used by device core cpu subsystem only.
|
||||
*
|
||||
* Other subsystems should use add_cpu() instead.
|
||||
*/
|
||||
int cpu_device_up(struct device *dev)
|
||||
{
|
||||
return do_cpu_up(cpu, CPUHP_ONLINE);
|
||||
return cpu_up(dev->id, CPUHP_ONLINE);
|
||||
}
|
||||
|
||||
int add_cpu(unsigned int cpu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
lock_device_hotplug();
|
||||
ret = device_online(get_cpu_device(cpu));
|
||||
unlock_device_hotplug();
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(add_cpu);
|
||||
|
||||
/**
|
||||
* bringup_hibernate_cpu - Bring up the CPU that we hibernated on
|
||||
* @sleep_cpu: The cpu we hibernated on and should be brought up.
|
||||
*
|
||||
* On some architectures like arm64, we can hibernate on any CPU, but on
|
||||
* wake up the CPU we hibernated on might be offline as a side effect of
|
||||
* using maxcpus= for example.
|
||||
*/
|
||||
int bringup_hibernate_cpu(unsigned int sleep_cpu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!cpu_online(sleep_cpu)) {
|
||||
pr_info("Hibernated on a CPU that is offline! Bringing CPU up.\n");
|
||||
ret = cpu_up(sleep_cpu, CPUHP_ONLINE);
|
||||
if (ret) {
|
||||
pr_err("Failed to bring hibernate-CPU up!\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bringup_nonboot_cpus(unsigned int setup_max_cpus)
|
||||
{
|
||||
unsigned int cpu;
|
||||
|
||||
for_each_present_cpu(cpu) {
|
||||
if (num_online_cpus() >= setup_max_cpus)
|
||||
break;
|
||||
if (!cpu_online(cpu))
|
||||
cpu_up(cpu, CPUHP_ONLINE);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpu_up);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP_SMP
|
||||
static cpumask_var_t frozen_cpus;
|
||||
|
||||
int freeze_secondary_cpus(int primary)
|
||||
int __freeze_secondary_cpus(int primary, bool suspend)
|
||||
{
|
||||
int cpu, error = 0;
|
||||
|
||||
@@ -1237,7 +1352,7 @@ int freeze_secondary_cpus(int primary)
|
||||
if (cpu == primary)
|
||||
continue;
|
||||
|
||||
if (pm_wakeup_pending()) {
|
||||
if (suspend && pm_wakeup_pending()) {
|
||||
pr_info("Wakeup pending. Abort CPU freeze\n");
|
||||
error = -EBUSY;
|
||||
break;
|
||||
@@ -2028,9 +2143,9 @@ static ssize_t write_cpuhp_target(struct device *dev,
|
||||
goto out;
|
||||
|
||||
if (st->state < target)
|
||||
ret = do_cpu_up(dev->id, target);
|
||||
ret = cpu_up(dev->id, target);
|
||||
else
|
||||
ret = do_cpu_down(dev->id, target);
|
||||
ret = cpu_down(dev->id, target);
|
||||
out:
|
||||
unlock_device_hotplug();
|
||||
return ret ? ret : count;
|
||||
|
@@ -269,7 +269,6 @@ static void __hrtick_start(void *arg)
|
||||
|
||||
rq_lock(rq, &rf);
|
||||
__hrtick_restart(rq);
|
||||
rq->hrtick_csd_pending = 0;
|
||||
rq_unlock(rq, &rf);
|
||||
}
|
||||
|
||||
@@ -293,12 +292,10 @@ void hrtick_start(struct rq *rq, u64 delay)
|
||||
|
||||
hrtimer_set_expires(timer, time);
|
||||
|
||||
if (rq == this_rq()) {
|
||||
if (rq == this_rq())
|
||||
__hrtick_restart(rq);
|
||||
} else if (!rq->hrtick_csd_pending) {
|
||||
else
|
||||
smp_call_function_single_async(cpu_of(rq), &rq->hrtick_csd);
|
||||
rq->hrtick_csd_pending = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
@@ -322,8 +319,6 @@ void hrtick_start(struct rq *rq, u64 delay)
|
||||
static void hrtick_rq_init(struct rq *rq)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
rq->hrtick_csd_pending = 0;
|
||||
|
||||
rq->hrtick_csd.flags = 0;
|
||||
rq->hrtick_csd.func = __hrtick_start;
|
||||
rq->hrtick_csd.info = rq;
|
||||
|
@@ -992,7 +992,6 @@ struct rq {
|
||||
|
||||
#ifdef CONFIG_SCHED_HRTICK
|
||||
#ifdef CONFIG_SMP
|
||||
int hrtick_csd_pending;
|
||||
call_single_data_t hrtick_csd;
|
||||
#endif
|
||||
struct hrtimer hrtick_timer;
|
||||
|
23
kernel/smp.c
23
kernel/smp.c
@@ -329,6 +329,11 @@ EXPORT_SYMBOL(smp_call_function_single);
|
||||
* (ie: embedded in an object) and is responsible for synchronizing it
|
||||
* such that the IPIs performed on the @csd are strictly serialized.
|
||||
*
|
||||
* If the function is called with one csd which has not yet been
|
||||
* processed by previous call to smp_call_function_single_async(), the
|
||||
* function will return immediately with -EBUSY showing that the csd
|
||||
* object is still in progress.
|
||||
*
|
||||
* NOTE: Be careful, there is unfortunately no current debugging facility to
|
||||
* validate the correctness of this serialization.
|
||||
*/
|
||||
@@ -338,14 +343,17 @@ int smp_call_function_single_async(int cpu, call_single_data_t *csd)
|
||||
|
||||
preempt_disable();
|
||||
|
||||
/* We could deadlock if we have to wait here with interrupts disabled! */
|
||||
if (WARN_ON_ONCE(csd->flags & CSD_FLAG_LOCK))
|
||||
csd_lock_wait(csd);
|
||||
if (csd->flags & CSD_FLAG_LOCK) {
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
csd->flags = CSD_FLAG_LOCK;
|
||||
smp_wmb();
|
||||
|
||||
err = generic_exec_single(cpu, csd, csd->func, csd->info);
|
||||
|
||||
out:
|
||||
preempt_enable();
|
||||
|
||||
return err;
|
||||
@@ -589,20 +597,13 @@ void __init setup_nr_cpu_ids(void)
|
||||
void __init smp_init(void)
|
||||
{
|
||||
int num_nodes, num_cpus;
|
||||
unsigned int cpu;
|
||||
|
||||
idle_threads_init();
|
||||
cpuhp_threads_init();
|
||||
|
||||
pr_info("Bringing up secondary CPUs ...\n");
|
||||
|
||||
/* FIXME: This should be done in userspace --RR */
|
||||
for_each_present_cpu(cpu) {
|
||||
if (num_online_cpus() >= setup_max_cpus)
|
||||
break;
|
||||
if (!cpu_online(cpu))
|
||||
cpu_up(cpu);
|
||||
}
|
||||
bringup_nonboot_cpus(setup_max_cpus);
|
||||
|
||||
num_nodes = num_online_nodes();
|
||||
num_cpus = num_online_cpus();
|
||||
|
@@ -101,7 +101,7 @@ bool torture_offline(int cpu, long *n_offl_attempts, long *n_offl_successes,
|
||||
torture_type, cpu);
|
||||
starttime = jiffies;
|
||||
(*n_offl_attempts)++;
|
||||
ret = cpu_down(cpu);
|
||||
ret = remove_cpu(cpu);
|
||||
if (ret) {
|
||||
s = "";
|
||||
if (!rcu_inkernel_boot_has_ended() && ret == -EBUSY) {
|
||||
@@ -159,7 +159,7 @@ bool torture_online(int cpu, long *n_onl_attempts, long *n_onl_successes,
|
||||
torture_type, cpu);
|
||||
starttime = jiffies;
|
||||
(*n_onl_attempts)++;
|
||||
ret = cpu_up(cpu);
|
||||
ret = add_cpu(cpu);
|
||||
if (ret) {
|
||||
s = "";
|
||||
if (!rcu_inkernel_boot_has_ended() && ret == -EBUSY) {
|
||||
@@ -209,17 +209,18 @@ torture_onoff(void *arg)
|
||||
for_each_online_cpu(cpu)
|
||||
maxcpu = cpu;
|
||||
WARN_ON(maxcpu < 0);
|
||||
if (!IS_MODULE(CONFIG_TORTURE_TEST))
|
||||
if (!IS_MODULE(CONFIG_TORTURE_TEST)) {
|
||||
for_each_possible_cpu(cpu) {
|
||||
if (cpu_online(cpu))
|
||||
continue;
|
||||
ret = cpu_up(cpu);
|
||||
ret = add_cpu(cpu);
|
||||
if (ret && verbose) {
|
||||
pr_alert("%s" TORTURE_FLAG
|
||||
"%s: Initial online %d: errno %d\n",
|
||||
__func__, torture_type, cpu, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (maxcpu == 0) {
|
||||
VERBOSE_TOROUT_STRING("Only one CPU, so CPU-hotplug testing is disabled");
|
||||
|
Reference in New Issue
Block a user