mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-16 13:19:56 -04:00
Merge branch 'smp-hotplug-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull smp hotplug updates from Thomas Gleixner:
"This is the next part of the hotplug rework.
- Convert all notifiers with a priority assigned
- Convert all CPU_STARTING/DYING notifiers
The final removal of the STARTING/DYING infrastructure will happen
when the merge window closes.
Another 700 hundred line of unpenetrable maze gone :)"
* 'smp-hotplug-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (70 commits)
timers/core: Correct callback order during CPU hot plug
leds/trigger/cpu: Move from CPU_STARTING to ONLINE level
powerpc/numa: Convert to hotplug state machine
arm/perf: Fix hotplug state machine conversion
irqchip/armada: Avoid unused function warnings
ARC/time: Convert to hotplug state machine
clocksource/atlas7: Convert to hotplug state machine
clocksource/armada-370-xp: Convert to hotplug state machine
clocksource/exynos_mct: Convert to hotplug state machine
clocksource/arm_global_timer: Convert to hotplug state machine
rcu: Convert rcutree to hotplug state machine
KVM/arm/arm64/vgic-new: Convert to hotplug state machine
smp/cfd: Convert core to hotplug state machine
x86/x2apic: Convert to CPU hotplug state machine
profile: Convert to hotplug state machine
timers/core: Convert to hotplug state machine
hrtimer: Convert to hotplug state machine
x86/tboot: Convert to hotplug state machine
arm64/armv8 deprecated: Convert to hotplug state machine
hwtracing/coresight-etm4x: Convert to hotplug state machine
...
This commit is contained in:
66
kernel/cpu.c
66
kernel/cpu.c
@@ -517,6 +517,13 @@ static int cpuhp_invoke_ap_callback(int cpu, enum cpuhp_state state,
|
||||
if (!cpu_online(cpu))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If we are up and running, use the hotplug thread. For early calls
|
||||
* we invoke the thread function directly.
|
||||
*/
|
||||
if (!st->thread)
|
||||
return cpuhp_invoke_callback(cpu, state, cb);
|
||||
|
||||
st->cb_state = state;
|
||||
st->cb = cb;
|
||||
/*
|
||||
@@ -1173,6 +1180,31 @@ static struct cpuhp_step cpuhp_bp_states[] = {
|
||||
.teardown = NULL,
|
||||
.cant_stop = true,
|
||||
},
|
||||
[CPUHP_PERF_PREPARE] = {
|
||||
.name = "perf prepare",
|
||||
.startup = perf_event_init_cpu,
|
||||
.teardown = perf_event_exit_cpu,
|
||||
},
|
||||
[CPUHP_WORKQUEUE_PREP] = {
|
||||
.name = "workqueue prepare",
|
||||
.startup = workqueue_prepare_cpu,
|
||||
.teardown = NULL,
|
||||
},
|
||||
[CPUHP_HRTIMERS_PREPARE] = {
|
||||
.name = "hrtimers prepare",
|
||||
.startup = hrtimers_prepare_cpu,
|
||||
.teardown = hrtimers_dead_cpu,
|
||||
},
|
||||
[CPUHP_SMPCFD_PREPARE] = {
|
||||
.name = "SMPCFD prepare",
|
||||
.startup = smpcfd_prepare_cpu,
|
||||
.teardown = smpcfd_dead_cpu,
|
||||
},
|
||||
[CPUHP_RCUTREE_PREP] = {
|
||||
.name = "RCU-tree prepare",
|
||||
.startup = rcutree_prepare_cpu,
|
||||
.teardown = rcutree_dead_cpu,
|
||||
},
|
||||
/*
|
||||
* Preparatory and dead notifiers. Will be replaced once the notifiers
|
||||
* are converted to states.
|
||||
@@ -1184,6 +1216,16 @@ static struct cpuhp_step cpuhp_bp_states[] = {
|
||||
.skip_onerr = true,
|
||||
.cant_stop = true,
|
||||
},
|
||||
/*
|
||||
* On the tear-down path, timers_dead_cpu() must be invoked
|
||||
* before blk_mq_queue_reinit_notify() from notify_dead(),
|
||||
* otherwise a RCU stall occurs.
|
||||
*/
|
||||
[CPUHP_TIMERS_DEAD] = {
|
||||
.name = "timers dead",
|
||||
.startup = NULL,
|
||||
.teardown = timers_dead_cpu,
|
||||
},
|
||||
/* Kicks the plugged cpu into life */
|
||||
[CPUHP_BRINGUP_CPU] = {
|
||||
.name = "cpu:bringup",
|
||||
@@ -1191,6 +1233,10 @@ static struct cpuhp_step cpuhp_bp_states[] = {
|
||||
.teardown = NULL,
|
||||
.cant_stop = true,
|
||||
},
|
||||
[CPUHP_AP_SMPCFD_DYING] = {
|
||||
.startup = NULL,
|
||||
.teardown = smpcfd_dying_cpu,
|
||||
},
|
||||
/*
|
||||
* Handled on controll processor until the plugged processor manages
|
||||
* this itself.
|
||||
@@ -1227,6 +1273,10 @@ static struct cpuhp_step cpuhp_ap_states[] = {
|
||||
.startup = sched_cpu_starting,
|
||||
.teardown = sched_cpu_dying,
|
||||
},
|
||||
[CPUHP_AP_RCUTREE_DYING] = {
|
||||
.startup = NULL,
|
||||
.teardown = rcutree_dying_cpu,
|
||||
},
|
||||
/*
|
||||
* Low level startup/teardown notifiers. Run with interrupts
|
||||
* disabled. Will be removed once the notifiers are converted to
|
||||
@@ -1250,6 +1300,22 @@ static struct cpuhp_step cpuhp_ap_states[] = {
|
||||
.startup = smpboot_unpark_threads,
|
||||
.teardown = NULL,
|
||||
},
|
||||
[CPUHP_AP_PERF_ONLINE] = {
|
||||
.name = "perf online",
|
||||
.startup = perf_event_init_cpu,
|
||||
.teardown = perf_event_exit_cpu,
|
||||
},
|
||||
[CPUHP_AP_WORKQUEUE_ONLINE] = {
|
||||
.name = "workqueue online",
|
||||
.startup = workqueue_online_cpu,
|
||||
.teardown = workqueue_offline_cpu,
|
||||
},
|
||||
[CPUHP_AP_RCUTREE_ONLINE] = {
|
||||
.name = "RCU-tree online",
|
||||
.startup = rcutree_online_cpu,
|
||||
.teardown = rcutree_offline_cpu,
|
||||
},
|
||||
|
||||
/*
|
||||
* Online/down_prepare notifiers. Will be removed once the notifiers
|
||||
* are converted to states.
|
||||
|
||||
@@ -10357,7 +10357,7 @@ static void __init perf_event_init_all_cpus(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void perf_event_init_cpu(int cpu)
|
||||
int perf_event_init_cpu(unsigned int cpu)
|
||||
{
|
||||
struct swevent_htable *swhash = &per_cpu(swevent_htable, cpu);
|
||||
|
||||
@@ -10370,6 +10370,7 @@ static void perf_event_init_cpu(int cpu)
|
||||
rcu_assign_pointer(swhash->swevent_hlist, hlist);
|
||||
}
|
||||
mutex_unlock(&swhash->hlist_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined CONFIG_HOTPLUG_CPU || defined CONFIG_KEXEC_CORE
|
||||
@@ -10401,14 +10402,17 @@ static void perf_event_exit_cpu_context(int cpu)
|
||||
}
|
||||
srcu_read_unlock(&pmus_srcu, idx);
|
||||
}
|
||||
#else
|
||||
|
||||
static void perf_event_exit_cpu(int cpu)
|
||||
static void perf_event_exit_cpu_context(int cpu) { }
|
||||
|
||||
#endif
|
||||
|
||||
int perf_event_exit_cpu(unsigned int cpu)
|
||||
{
|
||||
perf_event_exit_cpu_context(cpu);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline void perf_event_exit_cpu(int cpu) { }
|
||||
#endif
|
||||
|
||||
static int
|
||||
perf_reboot(struct notifier_block *notifier, unsigned long val, void *v)
|
||||
@@ -10430,46 +10434,6 @@ static struct notifier_block perf_reboot_notifier = {
|
||||
.priority = INT_MIN,
|
||||
};
|
||||
|
||||
static int
|
||||
perf_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu)
|
||||
{
|
||||
unsigned int cpu = (long)hcpu;
|
||||
|
||||
switch (action & ~CPU_TASKS_FROZEN) {
|
||||
|
||||
case CPU_UP_PREPARE:
|
||||
/*
|
||||
* This must be done before the CPU comes alive, because the
|
||||
* moment we can run tasks we can encounter (software) events.
|
||||
*
|
||||
* Specifically, someone can have inherited events on kthreadd
|
||||
* or a pre-existing worker thread that gets re-bound.
|
||||
*/
|
||||
perf_event_init_cpu(cpu);
|
||||
break;
|
||||
|
||||
case CPU_DOWN_PREPARE:
|
||||
/*
|
||||
* This must be done before the CPU dies because after that an
|
||||
* active event might want to IPI the CPU and that'll not work
|
||||
* so great for dead CPUs.
|
||||
*
|
||||
* XXX smp_call_function_single() return -ENXIO without a warn
|
||||
* so we could possibly deal with this.
|
||||
*
|
||||
* This is safe against new events arriving because
|
||||
* sys_perf_event_open() serializes against hotplug using
|
||||
* get_online_cpus().
|
||||
*/
|
||||
perf_event_exit_cpu(cpu);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
void __init perf_event_init(void)
|
||||
{
|
||||
int ret;
|
||||
@@ -10482,7 +10446,7 @@ void __init perf_event_init(void)
|
||||
perf_pmu_register(&perf_cpu_clock, NULL, -1);
|
||||
perf_pmu_register(&perf_task_clock, NULL, -1);
|
||||
perf_tp_register();
|
||||
perf_cpu_notifier(perf_cpu_notify);
|
||||
perf_event_init_cpu(smp_processor_id());
|
||||
register_reboot_notifier(&perf_reboot_notifier);
|
||||
|
||||
ret = init_hw_breakpoint();
|
||||
|
||||
185
kernel/profile.c
185
kernel/profile.c
@@ -328,68 +328,57 @@ static void do_profile_hits(int type, void *__pc, unsigned int nr_hits)
|
||||
put_cpu();
|
||||
}
|
||||
|
||||
static int profile_cpu_callback(struct notifier_block *info,
|
||||
unsigned long action, void *__cpu)
|
||||
static int profile_dead_cpu(unsigned int cpu)
|
||||
{
|
||||
int node, cpu = (unsigned long)__cpu;
|
||||
struct page *page;
|
||||
int i;
|
||||
|
||||
if (prof_cpu_mask != NULL)
|
||||
cpumask_clear_cpu(cpu, prof_cpu_mask);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (per_cpu(cpu_profile_hits, cpu)[i]) {
|
||||
page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[i]);
|
||||
per_cpu(cpu_profile_hits, cpu)[i] = NULL;
|
||||
__free_page(page);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int profile_prepare_cpu(unsigned int cpu)
|
||||
{
|
||||
int i, node = cpu_to_mem(cpu);
|
||||
struct page *page;
|
||||
|
||||
switch (action) {
|
||||
case CPU_UP_PREPARE:
|
||||
case CPU_UP_PREPARE_FROZEN:
|
||||
node = cpu_to_mem(cpu);
|
||||
per_cpu(cpu_profile_flip, cpu) = 0;
|
||||
if (!per_cpu(cpu_profile_hits, cpu)[1]) {
|
||||
page = __alloc_pages_node(node,
|
||||
GFP_KERNEL | __GFP_ZERO,
|
||||
0);
|
||||
if (!page)
|
||||
return notifier_from_errno(-ENOMEM);
|
||||
per_cpu(cpu_profile_hits, cpu)[1] = page_address(page);
|
||||
per_cpu(cpu_profile_flip, cpu) = 0;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (per_cpu(cpu_profile_hits, cpu)[i])
|
||||
continue;
|
||||
|
||||
page = __alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO, 0);
|
||||
if (!page) {
|
||||
profile_dead_cpu(cpu);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (!per_cpu(cpu_profile_hits, cpu)[0]) {
|
||||
page = __alloc_pages_node(node,
|
||||
GFP_KERNEL | __GFP_ZERO,
|
||||
0);
|
||||
if (!page)
|
||||
goto out_free;
|
||||
per_cpu(cpu_profile_hits, cpu)[0] = page_address(page);
|
||||
}
|
||||
break;
|
||||
out_free:
|
||||
page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[1]);
|
||||
per_cpu(cpu_profile_hits, cpu)[1] = NULL;
|
||||
__free_page(page);
|
||||
return notifier_from_errno(-ENOMEM);
|
||||
case CPU_ONLINE:
|
||||
case CPU_ONLINE_FROZEN:
|
||||
if (prof_cpu_mask != NULL)
|
||||
cpumask_set_cpu(cpu, prof_cpu_mask);
|
||||
break;
|
||||
case CPU_UP_CANCELED:
|
||||
case CPU_UP_CANCELED_FROZEN:
|
||||
case CPU_DEAD:
|
||||
case CPU_DEAD_FROZEN:
|
||||
if (prof_cpu_mask != NULL)
|
||||
cpumask_clear_cpu(cpu, prof_cpu_mask);
|
||||
if (per_cpu(cpu_profile_hits, cpu)[0]) {
|
||||
page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[0]);
|
||||
per_cpu(cpu_profile_hits, cpu)[0] = NULL;
|
||||
__free_page(page);
|
||||
}
|
||||
if (per_cpu(cpu_profile_hits, cpu)[1]) {
|
||||
page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[1]);
|
||||
per_cpu(cpu_profile_hits, cpu)[1] = NULL;
|
||||
__free_page(page);
|
||||
}
|
||||
break;
|
||||
per_cpu(cpu_profile_hits, cpu)[i] = page_address(page);
|
||||
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int profile_online_cpu(unsigned int cpu)
|
||||
{
|
||||
if (prof_cpu_mask != NULL)
|
||||
cpumask_set_cpu(cpu, prof_cpu_mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* !CONFIG_SMP */
|
||||
#define profile_flip_buffers() do { } while (0)
|
||||
#define profile_discard_flip_buffers() do { } while (0)
|
||||
#define profile_cpu_callback NULL
|
||||
|
||||
static void do_profile_hits(int type, void *__pc, unsigned int nr_hits)
|
||||
{
|
||||
@@ -531,83 +520,43 @@ static const struct file_operations proc_profile_operations = {
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static void profile_nop(void *unused)
|
||||
{
|
||||
}
|
||||
|
||||
static int create_hash_tables(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
int node = cpu_to_mem(cpu);
|
||||
struct page *page;
|
||||
|
||||
page = __alloc_pages_node(node,
|
||||
GFP_KERNEL | __GFP_ZERO | __GFP_THISNODE,
|
||||
0);
|
||||
if (!page)
|
||||
goto out_cleanup;
|
||||
per_cpu(cpu_profile_hits, cpu)[1]
|
||||
= (struct profile_hit *)page_address(page);
|
||||
page = __alloc_pages_node(node,
|
||||
GFP_KERNEL | __GFP_ZERO | __GFP_THISNODE,
|
||||
0);
|
||||
if (!page)
|
||||
goto out_cleanup;
|
||||
per_cpu(cpu_profile_hits, cpu)[0]
|
||||
= (struct profile_hit *)page_address(page);
|
||||
}
|
||||
return 0;
|
||||
out_cleanup:
|
||||
prof_on = 0;
|
||||
smp_mb();
|
||||
on_each_cpu(profile_nop, NULL, 1);
|
||||
for_each_online_cpu(cpu) {
|
||||
struct page *page;
|
||||
|
||||
if (per_cpu(cpu_profile_hits, cpu)[0]) {
|
||||
page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[0]);
|
||||
per_cpu(cpu_profile_hits, cpu)[0] = NULL;
|
||||
__free_page(page);
|
||||
}
|
||||
if (per_cpu(cpu_profile_hits, cpu)[1]) {
|
||||
page = virt_to_page(per_cpu(cpu_profile_hits, cpu)[1]);
|
||||
per_cpu(cpu_profile_hits, cpu)[1] = NULL;
|
||||
__free_page(page);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
#define create_hash_tables() ({ 0; })
|
||||
#endif
|
||||
|
||||
int __ref create_proc_profile(void) /* false positive from hotcpu_notifier */
|
||||
int __ref create_proc_profile(void)
|
||||
{
|
||||
struct proc_dir_entry *entry;
|
||||
#ifdef CONFIG_SMP
|
||||
enum cpuhp_state online_state;
|
||||
#endif
|
||||
|
||||
int err = 0;
|
||||
|
||||
if (!prof_on)
|
||||
return 0;
|
||||
#ifdef CONFIG_SMP
|
||||
err = cpuhp_setup_state(CPUHP_PROFILE_PREPARE, "PROFILE_PREPARE",
|
||||
profile_prepare_cpu, profile_dead_cpu);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
cpu_notifier_register_begin();
|
||||
|
||||
if (create_hash_tables()) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "AP_PROFILE_ONLINE",
|
||||
profile_online_cpu, NULL);
|
||||
if (err < 0)
|
||||
goto err_state_prep;
|
||||
online_state = err;
|
||||
err = 0;
|
||||
#endif
|
||||
entry = proc_create("profile", S_IWUSR | S_IRUGO,
|
||||
NULL, &proc_profile_operations);
|
||||
if (!entry)
|
||||
goto out;
|
||||
goto err_state_onl;
|
||||
proc_set_size(entry, (1 + prof_len) * sizeof(atomic_t));
|
||||
__hotcpu_notifier(profile_cpu_callback, 0);
|
||||
|
||||
out:
|
||||
cpu_notifier_register_done();
|
||||
return err;
|
||||
err_state_onl:
|
||||
#ifdef CONFIG_SMP
|
||||
cpuhp_remove_state(online_state);
|
||||
err_state_prep:
|
||||
cpuhp_remove_state(CPUHP_PROFILE_PREPARE);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
subsys_initcall(create_proc_profile);
|
||||
|
||||
@@ -1073,11 +1073,11 @@ EXPORT_SYMBOL_GPL(rcu_is_watching);
|
||||
* offline to continue to use RCU for one jiffy after marking itself
|
||||
* offline in the cpu_online_mask. This leniency is necessary given the
|
||||
* non-atomic nature of the online and offline processing, for example,
|
||||
* the fact that a CPU enters the scheduler after completing the CPU_DYING
|
||||
* notifiers.
|
||||
* the fact that a CPU enters the scheduler after completing the teardown
|
||||
* of the CPU.
|
||||
*
|
||||
* This is also why RCU internally marks CPUs online during the
|
||||
* CPU_UP_PREPARE phase and offline during the CPU_DEAD phase.
|
||||
* This is also why RCU internally marks CPUs online during in the
|
||||
* preparation phase and offline after the CPU has been taken down.
|
||||
*
|
||||
* Disable checking if in an NMI handler because we cannot safely report
|
||||
* errors from NMI handlers anyway.
|
||||
@@ -3806,12 +3806,58 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp)
|
||||
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
||||
}
|
||||
|
||||
static void rcu_prepare_cpu(int cpu)
|
||||
int rcutree_prepare_cpu(unsigned int cpu)
|
||||
{
|
||||
struct rcu_state *rsp;
|
||||
|
||||
for_each_rcu_flavor(rsp)
|
||||
rcu_init_percpu_data(cpu, rsp);
|
||||
|
||||
rcu_prepare_kthreads(cpu);
|
||||
rcu_spawn_all_nocb_kthreads(cpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rcutree_affinity_setting(unsigned int cpu, int outgoing)
|
||||
{
|
||||
struct rcu_data *rdp = per_cpu_ptr(rcu_state_p->rda, cpu);
|
||||
|
||||
rcu_boost_kthread_setaffinity(rdp->mynode, outgoing);
|
||||
}
|
||||
|
||||
int rcutree_online_cpu(unsigned int cpu)
|
||||
{
|
||||
sync_sched_exp_online_cleanup(cpu);
|
||||
rcutree_affinity_setting(cpu, -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rcutree_offline_cpu(unsigned int cpu)
|
||||
{
|
||||
rcutree_affinity_setting(cpu, cpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int rcutree_dying_cpu(unsigned int cpu)
|
||||
{
|
||||
struct rcu_state *rsp;
|
||||
|
||||
for_each_rcu_flavor(rsp)
|
||||
rcu_cleanup_dying_cpu(rsp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rcutree_dead_cpu(unsigned int cpu)
|
||||
{
|
||||
struct rcu_state *rsp;
|
||||
|
||||
for_each_rcu_flavor(rsp) {
|
||||
rcu_cleanup_dead_cpu(cpu, rsp);
|
||||
do_nocb_deferred_wakeup(per_cpu_ptr(rsp->rda, cpu));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
@@ -3851,52 +3897,6 @@ void rcu_report_dead(unsigned int cpu)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Handle CPU online/offline notification events.
|
||||
*/
|
||||
int rcu_cpu_notify(struct notifier_block *self,
|
||||
unsigned long action, void *hcpu)
|
||||
{
|
||||
long cpu = (long)hcpu;
|
||||
struct rcu_data *rdp = per_cpu_ptr(rcu_state_p->rda, cpu);
|
||||
struct rcu_node *rnp = rdp->mynode;
|
||||
struct rcu_state *rsp;
|
||||
|
||||
switch (action) {
|
||||
case CPU_UP_PREPARE:
|
||||
case CPU_UP_PREPARE_FROZEN:
|
||||
rcu_prepare_cpu(cpu);
|
||||
rcu_prepare_kthreads(cpu);
|
||||
rcu_spawn_all_nocb_kthreads(cpu);
|
||||
break;
|
||||
case CPU_ONLINE:
|
||||
case CPU_DOWN_FAILED:
|
||||
sync_sched_exp_online_cleanup(cpu);
|
||||
rcu_boost_kthread_setaffinity(rnp, -1);
|
||||
break;
|
||||
case CPU_DOWN_PREPARE:
|
||||
rcu_boost_kthread_setaffinity(rnp, cpu);
|
||||
break;
|
||||
case CPU_DYING:
|
||||
case CPU_DYING_FROZEN:
|
||||
for_each_rcu_flavor(rsp)
|
||||
rcu_cleanup_dying_cpu(rsp);
|
||||
break;
|
||||
case CPU_DEAD:
|
||||
case CPU_DEAD_FROZEN:
|
||||
case CPU_UP_CANCELED:
|
||||
case CPU_UP_CANCELED_FROZEN:
|
||||
for_each_rcu_flavor(rsp) {
|
||||
rcu_cleanup_dead_cpu(cpu, rsp);
|
||||
do_nocb_deferred_wakeup(per_cpu_ptr(rsp->rda, cpu));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int rcu_pm_notify(struct notifier_block *self,
|
||||
unsigned long action, void *hcpu)
|
||||
{
|
||||
@@ -4208,10 +4208,9 @@ void __init rcu_init(void)
|
||||
* this is called early in boot, before either interrupts
|
||||
* or the scheduler are operational.
|
||||
*/
|
||||
cpu_notifier(rcu_cpu_notify, 0);
|
||||
pm_notifier(rcu_pm_notify, 0);
|
||||
for_each_online_cpu(cpu)
|
||||
rcu_cpu_notify(NULL, CPU_UP_PREPARE, (void *)(long)cpu);
|
||||
rcutree_prepare_cpu(cpu);
|
||||
}
|
||||
|
||||
#include "tree_exp.h"
|
||||
|
||||
81
kernel/smp.c
81
kernel/smp.c
@@ -33,69 +33,54 @@ static DEFINE_PER_CPU_SHARED_ALIGNED(struct llist_head, call_single_queue);
|
||||
|
||||
static void flush_smp_call_function_queue(bool warn_cpu_offline);
|
||||
|
||||
static int
|
||||
hotplug_cfd(struct notifier_block *nfb, unsigned long action, void *hcpu)
|
||||
int smpcfd_prepare_cpu(unsigned int cpu)
|
||||
{
|
||||
long cpu = (long)hcpu;
|
||||
struct call_function_data *cfd = &per_cpu(cfd_data, cpu);
|
||||
|
||||
switch (action) {
|
||||
case CPU_UP_PREPARE:
|
||||
case CPU_UP_PREPARE_FROZEN:
|
||||
if (!zalloc_cpumask_var_node(&cfd->cpumask, GFP_KERNEL,
|
||||
cpu_to_node(cpu)))
|
||||
return notifier_from_errno(-ENOMEM);
|
||||
cfd->csd = alloc_percpu(struct call_single_data);
|
||||
if (!cfd->csd) {
|
||||
free_cpumask_var(cfd->cpumask);
|
||||
return notifier_from_errno(-ENOMEM);
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
case CPU_UP_CANCELED:
|
||||
case CPU_UP_CANCELED_FROZEN:
|
||||
/* Fall-through to the CPU_DEAD[_FROZEN] case. */
|
||||
|
||||
case CPU_DEAD:
|
||||
case CPU_DEAD_FROZEN:
|
||||
if (!zalloc_cpumask_var_node(&cfd->cpumask, GFP_KERNEL,
|
||||
cpu_to_node(cpu)))
|
||||
return -ENOMEM;
|
||||
cfd->csd = alloc_percpu(struct call_single_data);
|
||||
if (!cfd->csd) {
|
||||
free_cpumask_var(cfd->cpumask);
|
||||
free_percpu(cfd->csd);
|
||||
break;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
case CPU_DYING:
|
||||
case CPU_DYING_FROZEN:
|
||||
/*
|
||||
* The IPIs for the smp-call-function callbacks queued by other
|
||||
* CPUs might arrive late, either due to hardware latencies or
|
||||
* because this CPU disabled interrupts (inside stop-machine)
|
||||
* before the IPIs were sent. So flush out any pending callbacks
|
||||
* explicitly (without waiting for the IPIs to arrive), to
|
||||
* ensure that the outgoing CPU doesn't go offline with work
|
||||
* still pending.
|
||||
*/
|
||||
flush_smp_call_function_queue(false);
|
||||
break;
|
||||
#endif
|
||||
};
|
||||
|
||||
return NOTIFY_OK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct notifier_block hotplug_cfd_notifier = {
|
||||
.notifier_call = hotplug_cfd,
|
||||
};
|
||||
int smpcfd_dead_cpu(unsigned int cpu)
|
||||
{
|
||||
struct call_function_data *cfd = &per_cpu(cfd_data, cpu);
|
||||
|
||||
free_cpumask_var(cfd->cpumask);
|
||||
free_percpu(cfd->csd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smpcfd_dying_cpu(unsigned int cpu)
|
||||
{
|
||||
/*
|
||||
* The IPIs for the smp-call-function callbacks queued by other
|
||||
* CPUs might arrive late, either due to hardware latencies or
|
||||
* because this CPU disabled interrupts (inside stop-machine)
|
||||
* before the IPIs were sent. So flush out any pending callbacks
|
||||
* explicitly (without waiting for the IPIs to arrive), to
|
||||
* ensure that the outgoing CPU doesn't go offline with work
|
||||
* still pending.
|
||||
*/
|
||||
flush_smp_call_function_queue(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init call_function_init(void)
|
||||
{
|
||||
void *cpu = (void *)(long)smp_processor_id();
|
||||
int i;
|
||||
|
||||
for_each_possible_cpu(i)
|
||||
init_llist_head(&per_cpu(call_single_queue, i));
|
||||
|
||||
hotplug_cfd(&hotplug_cfd_notifier, CPU_UP_PREPARE, cpu);
|
||||
register_cpu_notifier(&hotplug_cfd_notifier);
|
||||
smpcfd_prepare_cpu(smp_processor_id());
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1590,7 +1590,7 @@ SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp,
|
||||
/*
|
||||
* Functions related to boot-time initialization:
|
||||
*/
|
||||
static void init_hrtimers_cpu(int cpu)
|
||||
int hrtimers_prepare_cpu(unsigned int cpu)
|
||||
{
|
||||
struct hrtimer_cpu_base *cpu_base = &per_cpu(hrtimer_bases, cpu);
|
||||
int i;
|
||||
@@ -1602,6 +1602,7 @@ static void init_hrtimers_cpu(int cpu)
|
||||
|
||||
cpu_base->cpu = cpu;
|
||||
hrtimer_init_hres(cpu_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
@@ -1636,7 +1637,7 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
|
||||
}
|
||||
}
|
||||
|
||||
static void migrate_hrtimers(int scpu)
|
||||
int hrtimers_dead_cpu(unsigned int scpu)
|
||||
{
|
||||
struct hrtimer_cpu_base *old_base, *new_base;
|
||||
int i;
|
||||
@@ -1665,45 +1666,14 @@ static void migrate_hrtimers(int scpu)
|
||||
/* Check, if we got expired work to do */
|
||||
__hrtimer_peek_ahead_timers();
|
||||
local_irq_enable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_HOTPLUG_CPU */
|
||||
|
||||
static int hrtimer_cpu_notify(struct notifier_block *self,
|
||||
unsigned long action, void *hcpu)
|
||||
{
|
||||
int scpu = (long)hcpu;
|
||||
|
||||
switch (action) {
|
||||
|
||||
case CPU_UP_PREPARE:
|
||||
case CPU_UP_PREPARE_FROZEN:
|
||||
init_hrtimers_cpu(scpu);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
case CPU_DEAD:
|
||||
case CPU_DEAD_FROZEN:
|
||||
migrate_hrtimers(scpu);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block hrtimers_nb = {
|
||||
.notifier_call = hrtimer_cpu_notify,
|
||||
};
|
||||
|
||||
void __init hrtimers_init(void)
|
||||
{
|
||||
hrtimer_cpu_notify(&hrtimers_nb, (unsigned long)CPU_UP_PREPARE,
|
||||
(void *)(long)smp_processor_id());
|
||||
register_cpu_notifier(&hrtimers_nb);
|
||||
hrtimers_prepare_cpu(smp_processor_id());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1804,7 +1804,7 @@ static void migrate_timer_list(struct timer_base *new_base, struct hlist_head *h
|
||||
}
|
||||
}
|
||||
|
||||
static void migrate_timers(int cpu)
|
||||
int timers_dead_cpu(unsigned int cpu)
|
||||
{
|
||||
struct timer_base *old_base;
|
||||
struct timer_base *new_base;
|
||||
@@ -1831,29 +1831,9 @@ static void migrate_timers(int cpu)
|
||||
spin_unlock_irq(&new_base->lock);
|
||||
put_cpu_ptr(&timer_bases);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int timer_cpu_notify(struct notifier_block *self,
|
||||
unsigned long action, void *hcpu)
|
||||
{
|
||||
switch (action) {
|
||||
case CPU_DEAD:
|
||||
case CPU_DEAD_FROZEN:
|
||||
migrate_timers((long)hcpu);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static inline void timer_register_cpu_notifier(void)
|
||||
{
|
||||
cpu_notifier(timer_cpu_notify, 0);
|
||||
}
|
||||
#else
|
||||
static inline void timer_register_cpu_notifier(void) { }
|
||||
#endif /* CONFIG_HOTPLUG_CPU */
|
||||
|
||||
static void __init init_timer_cpu(int cpu)
|
||||
@@ -1881,7 +1861,6 @@ void __init init_timers(void)
|
||||
{
|
||||
init_timer_cpus();
|
||||
init_timer_stats();
|
||||
timer_register_cpu_notifier();
|
||||
open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
|
||||
}
|
||||
|
||||
|
||||
@@ -4607,84 +4607,65 @@ static void restore_unbound_workers_cpumask(struct worker_pool *pool, int cpu)
|
||||
WARN_ON_ONCE(set_cpus_allowed_ptr(worker->task, &cpumask) < 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Workqueues should be brought up before normal priority CPU notifiers.
|
||||
* This will be registered high priority CPU notifier.
|
||||
*/
|
||||
static int workqueue_cpu_up_callback(struct notifier_block *nfb,
|
||||
unsigned long action,
|
||||
void *hcpu)
|
||||
int workqueue_prepare_cpu(unsigned int cpu)
|
||||
{
|
||||
struct worker_pool *pool;
|
||||
|
||||
for_each_cpu_worker_pool(pool, cpu) {
|
||||
if (pool->nr_workers)
|
||||
continue;
|
||||
if (!create_worker(pool))
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int workqueue_online_cpu(unsigned int cpu)
|
||||
{
|
||||
int cpu = (unsigned long)hcpu;
|
||||
struct worker_pool *pool;
|
||||
struct workqueue_struct *wq;
|
||||
int pi;
|
||||
|
||||
switch (action & ~CPU_TASKS_FROZEN) {
|
||||
case CPU_UP_PREPARE:
|
||||
for_each_cpu_worker_pool(pool, cpu) {
|
||||
if (pool->nr_workers)
|
||||
continue;
|
||||
if (!create_worker(pool))
|
||||
return NOTIFY_BAD;
|
||||
}
|
||||
break;
|
||||
mutex_lock(&wq_pool_mutex);
|
||||
|
||||
case CPU_DOWN_FAILED:
|
||||
case CPU_ONLINE:
|
||||
mutex_lock(&wq_pool_mutex);
|
||||
for_each_pool(pool, pi) {
|
||||
mutex_lock(&pool->attach_mutex);
|
||||
|
||||
for_each_pool(pool, pi) {
|
||||
mutex_lock(&pool->attach_mutex);
|
||||
if (pool->cpu == cpu)
|
||||
rebind_workers(pool);
|
||||
else if (pool->cpu < 0)
|
||||
restore_unbound_workers_cpumask(pool, cpu);
|
||||
|
||||
if (pool->cpu == cpu)
|
||||
rebind_workers(pool);
|
||||
else if (pool->cpu < 0)
|
||||
restore_unbound_workers_cpumask(pool, cpu);
|
||||
|
||||
mutex_unlock(&pool->attach_mutex);
|
||||
}
|
||||
|
||||
/* update NUMA affinity of unbound workqueues */
|
||||
list_for_each_entry(wq, &workqueues, list)
|
||||
wq_update_unbound_numa(wq, cpu, true);
|
||||
|
||||
mutex_unlock(&wq_pool_mutex);
|
||||
break;
|
||||
mutex_unlock(&pool->attach_mutex);
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
|
||||
/* update NUMA affinity of unbound workqueues */
|
||||
list_for_each_entry(wq, &workqueues, list)
|
||||
wq_update_unbound_numa(wq, cpu, true);
|
||||
|
||||
mutex_unlock(&wq_pool_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Workqueues should be brought down after normal priority CPU notifiers.
|
||||
* This will be registered as low priority CPU notifier.
|
||||
*/
|
||||
static int workqueue_cpu_down_callback(struct notifier_block *nfb,
|
||||
unsigned long action,
|
||||
void *hcpu)
|
||||
int workqueue_offline_cpu(unsigned int cpu)
|
||||
{
|
||||
int cpu = (unsigned long)hcpu;
|
||||
struct work_struct unbind_work;
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
switch (action & ~CPU_TASKS_FROZEN) {
|
||||
case CPU_DOWN_PREPARE:
|
||||
/* unbinding per-cpu workers should happen on the local CPU */
|
||||
INIT_WORK_ONSTACK(&unbind_work, wq_unbind_fn);
|
||||
queue_work_on(cpu, system_highpri_wq, &unbind_work);
|
||||
/* unbinding per-cpu workers should happen on the local CPU */
|
||||
INIT_WORK_ONSTACK(&unbind_work, wq_unbind_fn);
|
||||
queue_work_on(cpu, system_highpri_wq, &unbind_work);
|
||||
|
||||
/* update NUMA affinity of unbound workqueues */
|
||||
mutex_lock(&wq_pool_mutex);
|
||||
list_for_each_entry(wq, &workqueues, list)
|
||||
wq_update_unbound_numa(wq, cpu, false);
|
||||
mutex_unlock(&wq_pool_mutex);
|
||||
/* update NUMA affinity of unbound workqueues */
|
||||
mutex_lock(&wq_pool_mutex);
|
||||
list_for_each_entry(wq, &workqueues, list)
|
||||
wq_update_unbound_numa(wq, cpu, false);
|
||||
mutex_unlock(&wq_pool_mutex);
|
||||
|
||||
/* wait for per-cpu unbinding to finish */
|
||||
flush_work(&unbind_work);
|
||||
destroy_work_on_stack(&unbind_work);
|
||||
break;
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
/* wait for per-cpu unbinding to finish */
|
||||
flush_work(&unbind_work);
|
||||
destroy_work_on_stack(&unbind_work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
@@ -5486,9 +5467,6 @@ static int __init init_workqueues(void)
|
||||
|
||||
pwq_cache = KMEM_CACHE(pool_workqueue, SLAB_PANIC);
|
||||
|
||||
cpu_notifier(workqueue_cpu_up_callback, CPU_PRI_WORKQUEUE_UP);
|
||||
hotcpu_notifier(workqueue_cpu_down_callback, CPU_PRI_WORKQUEUE_DOWN);
|
||||
|
||||
wq_numa_init();
|
||||
|
||||
/* initialize CPU pools */
|
||||
|
||||
Reference in New Issue
Block a user