/* CPU control. * (C) 2001, 2002, 2003, 2004 Rusty Russell * * This code is licenced under the GPL. */#include <linux/proc_fs.h>#include <linux/smp.h>#include <linux/init.h>#include <linux/notifier.h>#include <linux/sched.h>#include <linux/unistd.h>#include <linux/cpu.h>#include <linux/oom.h>#include <linux/rcupdate.h>#include <linux/export.h>#include <linux/bug.h>#include <linux/kthread.h>#include <linux/stop_machine.h>#include <linux/mutex.h>#include <linux/gfp.h>#include <linux/suspend.h>#include <linux/lockdep.h>#include <linux/tick.h>#include <linux/irq.h>#include <linux/smpboot.h>#include <linux/relay.h>#include <linux/slab.h> #include <trace/events/power.h>#define CREATE_TRACE_POINTS#include <trace/events/cpuhp.h> #include "smpboot.h" /** * cpuhp_cpu_state - Per cpu hotplug state storage * @state: The current cpu state * @target: The target state * @thread: Pointer to the hotplug thread * @should_run: Thread should execute * @rollback: Perform a rollback * @single: Single callback invocation * @bringup: Single callback bringup or teardown selector * @cb_state: The state for a single callback (install/uninstall) * @result: Result of the operation * @done: Signal completion to the issuer of the task */struct cpuhp_cpu_state { enum cpuhp_state state; enum cpuhp_state target;#ifdef CONFIG_SMP struct task_struct *thread; bool should_run; bool rollback; bool single; bool bringup; struct hlist_node *node; enum cpuhp_state cb_state; int result; struct completion done;#endif}; static DEFINE_PER_CPU(struct cpuhp_cpu_state, cpuhp_state); /** * cpuhp_step - Hotplug state machine step * @name: Name of the step * @startup: Startup function of the step * @teardown: Teardown function of the step * @skip_onerr: Do not invoke the functions on error rollback * Will go away once the notifiers are gone * @cant_stop: Bringup/teardown can't be stopped at this step */struct cpuhp_step { const char *name; union { int (*single)(unsigned int cpu); int (*multi)(unsigned int cpu, struct hlist_node *node); } startup; union { int (*single)(unsigned int cpu); int (*multi)(unsigned int cpu, struct hlist_node *node); } teardown; struct hlist_head list; bool skip_onerr; bool cant_stop; bool multi_instance;}; static DEFINE_MUTEX(cpuhp_state_mutex);static struct cpuhp_step cpuhp_bp_states[];static struct cpuhp_step cpuhp_ap_states[]; static bool cpuhp_is_ap_state(enum cpuhp_state state){ /* * The extra check for CPUHP_TEARDOWN_CPU is only for documentation * purposes as that state is handled explicitly in cpu_down. */ return state > CPUHP_BRINGUP_CPU && state != CPUHP_TEARDOWN_CPU;} static struct cpuhp_step *cpuhp_get_step(enum cpuhp_state state){ struct cpuhp_step *sp; sp = cpuhp_is_ap_state(state) ? cpuhp_ap_states : cpuhp_bp_states; return sp + state;} /** * cpuhp_invoke_callback _ Invoke the callbacks for a given state * @cpu: The cpu for which the callback should be invoked * @step: The step in the state machine * @bringup: True if the bringup callback should be invoked * * Called from cpu hotplug and from the state register machinery. */static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state, bool bringup, struct hlist_node *node){ struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); struct cpuhp_step *step = cpuhp_get_step(state); int (*cbm)(unsigned int cpu, struct hlist_node *node); int (*cb)(unsigned int cpu); int ret, cnt; if (!step->multi_instance) { cb = bringup ? step->startup.single : step->teardown.single; if (!cb) return 0; trace_cpuhp_enter(cpu, st->target, state, cb); ret = cb(cpu); trace_cpuhp_exit(cpu, st->state, state, ret); return ret; } cbm = bringup ? step->startup.multi : step->teardown.multi; if (!cbm) return 0; /* Single invocation for instance add/remove */ if (node) { trace_cpuhp_multi_enter(cpu, st->target, state, cbm, node); ret = cbm(cpu, node); trace_cpuhp_exit(cpu, st->state, state, ret); return ret; } /* State transition. Invoke on all instances */ cnt = 0; hlist_for_each(node, &step->list) { trace_cpuhp_multi_enter(cpu, st->target, state, cbm, node); ret = cbm(cpu, node); trace_cpuhp_exit(cpu, st->state, state, ret); if (ret) goto err; cnt++; } return 0;err: /* Rollback the instances if one failed */ cbm = !bringup ? step->startup.multi : step->teardown.multi; if (!cbm) return ret; hlist_for_each(node, &step->list) { if (!cnt--) break; cbm(cpu, node); } return ret;} #ifdef CONFIG_SMP/* Serializes the updates to cpu_online_mask, cpu_present_mask */static DEFINE_MUTEX(cpu_add_remove_lock);bool cpuhp_tasks_frozen;EXPORT_SYMBOL_GPL(cpuhp_tasks_frozen); /* * The following two APIs (cpu_maps_update_begin/done) must be used when * attempting to serialize the updates to cpu_online_mask & cpu_present_mask. */void cpu_maps_update_begin(void){ mutex_lock(&cpu_add_remove_lock);} void cpu_maps_update_done(void){ mutex_unlock(&cpu_add_remove_lock);} /* If set, cpu_up and cpu_down will return -EBUSY and do nothing. * Should always be manipulated under cpu_add_remove_lock */static int cpu_hotplug_disabled; #ifdef CONFIG_HOTPLUG_CPU static struct { struct task_struct *active_writer; /* wait queue to wake up the active_writer */ wait_queue_head_t wq; /* verifies that no writer will get active while readers are active */ struct mutex lock; /* * Also blocks the new readers during * an ongoing cpu hotplug operation. */ atomic_t refcount; #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map;#endif} cpu_hotplug = { .active_writer = NULL, .wq = __WAIT_QUEUE_HEAD_INITIALIZER(cpu_hotplug.wq), .lock = __MUTEX_INITIALIZER(cpu_hotplug.lock),#ifdef CONFIG_DEBUG_LOCK_ALLOC .dep_map = STATIC_LOCKDEP_MAP_INIT("cpu_hotplug.dep_map", &cpu_hotplug.dep_map),#endif}; /* Lockdep annotations for get/put_online_cpus() and cpu_hotplug_begin/end() */#define cpuhp_lock_acquire_read() lock_map_acquire_read(&cpu_hotplug.dep_map)#define cpuhp_lock_acquire_tryread() \ lock_map_acquire_tryread(&cpu_hotplug.dep_map)#define cpuhp_lock_acquire() lock_map_acquire(&cpu_hotplug.dep_map)#define cpuhp_lock_release() lock_map_release(&cpu_hotplug.dep_map) void get_online_cpus(void){ might_sleep(); if (cpu_hotplug.active_writer == current) return; cpuhp_lock_acquire_read(); mutex_lock(&cpu_hotplug.lock); atomic_inc(&cpu_hotplug.refcount); mutex_unlock(&cpu_hotplug.lock);}EXPORT_SYMBOL_GPL(get_online_cpus); void put_online_cpus(void){ int refcount; if (cpu_hotplug.active_writer == current) return; refcount = atomic_dec_return(&cpu_hotplug.refcount); if (WARN_ON(refcount < 0)) /* try to fix things up */ atomic_inc(&cpu_hotplug.refcount); if (refcount <= 0 && waitqueue_active(&cpu_hotplug.wq)) wake_up(&cpu_hotplug.wq); cpuhp_lock_release(); }EXPORT_SYMBOL_GPL(put_online_cpus); /* * This ensures that the hotplug operation can begin only when the * refcount goes to zero. * * Note that during a cpu-hotplug operation, the new readers, if any, * will be blocked by the cpu_hotplug.lock * * Since cpu_hotplug_begin() is always called after invoking * cpu_maps_update_begin(), we can be sure that only one writer is active. * * Note that theoretically, there is a possibility of a livelock: * - Refcount goes to zero, last reader wakes up the sleeping * writer. * - Last reader unlocks the cpu_hotplug.lock. * - A new reader arrives at this moment, bumps up the refcount. * - The writer acquires the cpu_hotplug.lock finds the refcount * non zero and goes to sleep again. * * However, this is very difficult to achieve in practice since * get_online_cpus() not an api which is called all that often. * */void cpu_hotplug_begin(void){ DEFINE_WAIT(wait); cpu_hotplug.active_writer = current; cpuhp_lock_acquire(); for (;;) { mutex_lock(&cpu_hotplug.lock); prepare_to_wait(&cpu_hotplug.wq, &wait, TASK_UNINTERRUPTIBLE); if (likely(!atomic_read(&cpu_hotplug.refcount))) break; mutex_unlock(&cpu_hotplug.lock); schedule(); } finish_wait(&cpu_hotplug.wq, &wait);} void cpu_hotplug_done(void){ cpu_hotplug.active_writer = NULL; mutex_unlock(&cpu_hotplug.lock); cpuhp_lock_release();} /* * Wait for currently running CPU hotplug operations to complete (if any) and * disable future CPU hotplug (from sysfs). The 'cpu_add_remove_lock' protects * the 'cpu_hotplug_disabled' flag. The same lock is also acquired by the * hotplug path before performing hotplug operations. So acquiring that lock * guarantees mutual exclusion from any currently running hotplug operations. */void cpu_hotplug_disable(void){ cpu_maps_update_begin(); cpu_hotplug_disabled++; cpu_maps_update_done();}EXPORT_SYMBOL_GPL(cpu_hotplug_disable); static void __cpu_hotplug_enable(void){ if (WARN_ONCE(!cpu_hotplug_disabled, "Unbalanced cpu hotplug enable\n")) return; cpu_hotplug_disabled--;} void cpu_hotplug_enable(void){ cpu_maps_update_begin(); __cpu_hotplug_enable(); cpu_maps_update_done();}EXPORT_SYMBOL_GPL(cpu_hotplug_enable);#endif /* CONFIG_HOTPLUG_CPU */ /* Notifier wrappers for transitioning to state machine */ static int bringup_wait_for_ap(unsigned int cpu){ struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); wait_for_completion(&st->done); return st->result;} static int bringup_cpu(unsigned int cpu){ struct task_struct *idle = idle_thread_get(cpu); int ret; /* * Some architectures have to walk the irq descriptors to * setup the vector space for the cpu which comes online. * Prevent irq alloc/free across the bringup. */ irq_lock_sparse(); /* Arch-specific enabling code. */ ret = __cpu_up(cpu, idle); irq_unlock_sparse(); if (ret) return ret; ret = bringup_wait_for_ap(cpu); BUG_ON(!cpu_online(cpu)); return ret;} /* * Hotplug state machine related functions */static void undo_cpu_down(unsigned int cpu, struct cpuhp_cpu_state *st){ for (st->state++; st->state < st->target; st->state++) { struct cpuhp_step *step = cpuhp_get_step(st->state); if (!step->skip_onerr) cpuhp_invoke_callback(cpu, st->state, true, NULL); }} static int cpuhp_down_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st, enum cpuhp_state target){ enum cpuhp_state prev_state = st->state; int ret = 0; for (; st->state > target; st->state--) { ret = cpuhp_invoke_callback(cpu, st->state, false, NULL); if (ret) { st->target = prev_state; undo_cpu_down(cpu, st); break; } } return ret;} static void undo_cpu_up(unsigned int cpu, struct cpuhp_cpu_state *st){ for (st->state--; st->state > st->target; st->state--) { struct cpuhp_step *step = cpuhp_get_step(st->state); if (!step->skip_onerr) cpuhp_invoke_callback(cpu, st->state, false, NULL); }} static int cpuhp_up_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st, enum cpuhp_state target){ enum cpuhp_state prev_state = st->state; int ret = 0; while (st->state < target) { st->state++; ret = cpuhp_invoke_callback(cpu, st->state, true, NULL); if (ret) { st->target = prev_state; undo_cpu_up(cpu, st); break; } } return ret;} /* * The cpu hotplug threads manage the bringup and teardown of the cpus */static void cpuhp_create(unsigned int cpu){ struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); init_completion(&st->done);} static int cpuhp_should_run(unsigned int cpu){ struct cpuhp_cpu_state *st = this_cpu_ptr(&cpuhp_state); return st->should_run;} /* Execute the teardown callbacks. Used to be CPU_DOWN_PREPARE */static int cpuhp_ap_offline(unsigned int cpu, struct cpuhp_cpu_state *st){ enum cpuhp_state target = max((int)st->target, CPUHP_TEARDOWN_CPU); return cpuhp_down_callbacks(cpu, st, target);} /* Execute the online startup callbacks. Used to be CPU_ONLINE */static int cpuhp_ap_online(unsigned int cpu, struct cpuhp_cpu_state *st){ return cpuhp_up_callbacks(cpu, st, st->target);} /* * Execute teardown/startup callbacks on the plugged cpu. Also used to invoke * callbacks when a state gets [un]installed at runtime. */static void cpuhp_thread_fun(unsigned int cpu){ struct cpuhp_cpu_state *st = this_cpu_ptr(&cpuhp_state); int ret = 0; /* * Paired with the mb() in cpuhp_kick_ap_work and * cpuhp_invoke_ap_callback, so the work set is consistent visible. */ smp_mb(); if (!st->should_run) return; st->should_run = false; /* Single callback invocation for [un]install ? */ if (st->single) { if (st->cb_state < CPUHP_AP_ONLINE) { local_irq_disable(); ret = cpuhp_invoke_callback(cpu, st->cb_state, st->bringup, st->node); local_irq_enable(); } else { ret = cpuhp_invoke_callback(cpu, st->cb_state, st->bringup, st->node); } } else if (st->rollback) { BUG_ON(st->state < CPUHP_AP_ONLINE_IDLE); undo_cpu_down(cpu, st); st->rollback = false;Lines 1–500 of 1805. Load more
/* CPU control. * (C) 2001, 2002, 2003, 2004 Rusty Russell * * This code is licenced under the GPL. */ #include <linux/proc_fs.h> #include <linux/smp.h> #include <linux/init.h> #include <linux/notifier.h> #include <linux/sched.h> #include <linux/unistd.h> #include <linux/cpu.h> #include <linux/oom.h> #include <linux/rcupdate.h> #include <linux/export.h> #include <linux/bug.h> #include <linux/kthread.h> #include <linux/stop_machine.h> #include <linux/mutex.h> #include <linux/gfp.h> #include <linux/suspend.h> #include <linux/lockdep.h> #include <linux/tick.h> #include <linux/irq.h> #include <linux/smpboot.h> #include <linux/relay.h> #include <linux/slab.h> #include <trace/events/power.h> #define CREATE_TRACE_POINTS #include <trace/events/cpuhp.h> #include "smpboot.h" /** * cpuhp_cpu_state - Per cpu hotplug state storage * @state: The current cpu state * @target: The target state * @thread: Pointer to the hotplug thread * @should_run: Thread should execute * @rollback: Perform a rollback * @single: Single callback invocation * @bringup: Single callback bringup or teardown selector * @cb_state: The state for a single callback (install/uninstall) * @result: Result of the operation * @done: Signal completion to the issuer of the task */ struct cpuhp_cpu_state { enum cpuhp_state state; enum cpuhp_state target; #ifdef CONFIG_SMP struct task_struct *thread; bool should_run; bool rollback; bool single; bool bringup; struct hlist_node *node; enum cpuhp_state cb_state; int result; struct completion done; #endif }; static DEFINE_PER_CPU(struct cpuhp_cpu_state, cpuhp_state); /** * cpuhp_step - Hotplug state machine step * @name: Name of the step * @startup: Startup function of the step * @teardown: Teardown function of the step * @skip_onerr: Do not invoke the functions on error rollback * Will go away once the notifiers are gone * @cant_stop: Bringup/teardown can't be stopped at this step */ struct cpuhp_step { const char *name; union { int (*single)(unsigned int cpu); int (*multi)(unsigned int cpu, struct hlist_node *node); } startup; union { int (*single)(unsigned int cpu); int (*multi)(unsigned int cpu, struct hlist_node *node); } teardown; struct hlist_head list; bool skip_onerr; bool cant_stop; bool multi_instance; }; static DEFINE_MUTEX(cpuhp_state_mutex); static struct cpuhp_step cpuhp_bp_states[]; static struct cpuhp_step cpuhp_ap_states[]; static bool cpuhp_is_ap_state(enum cpuhp_state state) { /* * The extra check for CPUHP_TEARDOWN_CPU is only for documentation * purposes as that state is handled explicitly in cpu_down. */ return state > CPUHP_BRINGUP_CPU && state != CPUHP_TEARDOWN_CPU; } static struct cpuhp_step *cpuhp_get_step(enum cpuhp_state state) { struct cpuhp_step *sp; sp = cpuhp_is_ap_state(state) ? cpuhp_ap_states : cpuhp_bp_states; return sp + state; } /** * cpuhp_invoke_callback _ Invoke the callbacks for a given state * @cpu: The cpu for which the callback should be invoked * @step: The step in the state machine * @bringup: True if the bringup callback should be invoked * * Called from cpu hotplug and from the state register machinery. */ static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state, bool bringup, struct hlist_node *node) { struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); struct cpuhp_step *step = cpuhp_get_step(state); int (*cbm)(unsigned int cpu, struct hlist_node *node); int (*cb)(unsigned int cpu); int ret, cnt; if (!step->multi_instance) { cb = bringup ? step->startup.single : step->teardown.single; if (!cb) return 0; trace_cpuhp_enter(cpu, st->target, state, cb); ret = cb(cpu); trace_cpuhp_exit(cpu, st->state, state, ret); return ret; } cbm = bringup ? step->startup.multi : step->teardown.multi; if (!cbm) return 0; /* Single invocation for instance add/remove */ if (node) { trace_cpuhp_multi_enter(cpu, st->target, state, cbm, node); ret = cbm(cpu, node); trace_cpuhp_exit(cpu, st->state, state, ret); return ret; } /* State transition. Invoke on all instances */ cnt = 0; hlist_for_each(node, &step->list) { trace_cpuhp_multi_enter(cpu, st->target, state, cbm, node); ret = cbm(cpu, node); trace_cpuhp_exit(cpu, st->state, state, ret); if (ret) goto err; cnt++; } return 0; err: /* Rollback the instances if one failed */ cbm = !bringup ? step->startup.multi : step->teardown.multi; if (!cbm) return ret; hlist_for_each(node, &step->list) { if (!cnt--) break; cbm(cpu, node); } return ret; } #ifdef CONFIG_SMP /* Serializes the updates to cpu_online_mask, cpu_present_mask */ static DEFINE_MUTEX(cpu_add_remove_lock); bool cpuhp_tasks_frozen; EXPORT_SYMBOL_GPL(cpuhp_tasks_frozen); /* * The following two APIs (cpu_maps_update_begin/done) must be used when * attempting to serialize the updates to cpu_online_mask & cpu_present_mask. */ void cpu_maps_update_begin(void) { mutex_lock(&cpu_add_remove_lock); } void cpu_maps_update_done(void) { mutex_unlock(&cpu_add_remove_lock); } /* If set, cpu_up and cpu_down will return -EBUSY and do nothing. * Should always be manipulated under cpu_add_remove_lock */ static int cpu_hotplug_disabled; #ifdef CONFIG_HOTPLUG_CPU static struct { struct task_struct *active_writer; /* wait queue to wake up the active_writer */ wait_queue_head_t wq; /* verifies that no writer will get active while readers are active */ struct mutex lock; /* * Also blocks the new readers during * an ongoing cpu hotplug operation. */ atomic_t refcount; #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif } cpu_hotplug = { .active_writer = NULL, .wq = __WAIT_QUEUE_HEAD_INITIALIZER(cpu_hotplug.wq), .lock = __MUTEX_INITIALIZER(cpu_hotplug.lock), #ifdef CONFIG_DEBUG_LOCK_ALLOC .dep_map = STATIC_LOCKDEP_MAP_INIT("cpu_hotplug.dep_map", &cpu_hotplug.dep_map), #endif }; /* Lockdep annotations for get/put_online_cpus() and cpu_hotplug_begin/end() */ #define cpuhp_lock_acquire_read() lock_map_acquire_read(&cpu_hotplug.dep_map) #define cpuhp_lock_acquire_tryread() \ lock_map_acquire_tryread(&cpu_hotplug.dep_map) #define cpuhp_lock_acquire() lock_map_acquire(&cpu_hotplug.dep_map) #define cpuhp_lock_release() lock_map_release(&cpu_hotplug.dep_map) void get_online_cpus(void) { might_sleep(); if (cpu_hotplug.active_writer == current) return; cpuhp_lock_acquire_read(); mutex_lock(&cpu_hotplug.lock); atomic_inc(&cpu_hotplug.refcount); mutex_unlock(&cpu_hotplug.lock); } EXPORT_SYMBOL_GPL(get_online_cpus); void put_online_cpus(void) { int refcount; if (cpu_hotplug.active_writer == current) return; refcount = atomic_dec_return(&cpu_hotplug.refcount); if (WARN_ON(refcount < 0)) /* try to fix things up */ atomic_inc(&cpu_hotplug.refcount); if (refcount <= 0 && waitqueue_active(&cpu_hotplug.wq)) wake_up(&cpu_hotplug.wq); cpuhp_lock_release(); } EXPORT_SYMBOL_GPL(put_online_cpus); /* * This ensures that the hotplug operation can begin only when the * refcount goes to zero. * * Note that during a cpu-hotplug operation, the new readers, if any, * will be blocked by the cpu_hotplug.lock * * Since cpu_hotplug_begin() is always called after invoking * cpu_maps_update_begin(), we can be sure that only one writer is active. * * Note that theoretically, there is a possibility of a livelock: * - Refcount goes to zero, last reader wakes up the sleeping * writer. * - Last reader unlocks the cpu_hotplug.lock. * - A new reader arrives at this moment, bumps up the refcount. * - The writer acquires the cpu_hotplug.lock finds the refcount * non zero and goes to sleep again. * * However, this is very difficult to achieve in practice since * get_online_cpus() not an api which is called all that often. * */ void cpu_hotplug_begin(void) { DEFINE_WAIT(wait); cpu_hotplug.active_writer = current; cpuhp_lock_acquire(); for (;;) { mutex_lock(&cpu_hotplug.lock); prepare_to_wait(&cpu_hotplug.wq, &wait, TASK_UNINTERRUPTIBLE); if (likely(!atomic_read(&cpu_hotplug.refcount))) break; mutex_unlock(&cpu_hotplug.lock); schedule(); } finish_wait(&cpu_hotplug.wq, &wait); } void cpu_hotplug_done(void) { cpu_hotplug.active_writer = NULL; mutex_unlock(&cpu_hotplug.lock); cpuhp_lock_release(); } /* * Wait for currently running CPU hotplug operations to complete (if any) and * disable future CPU hotplug (from sysfs). The 'cpu_add_remove_lock' protects * the 'cpu_hotplug_disabled' flag. The same lock is also acquired by the * hotplug path before performing hotplug operations. So acquiring that lock * guarantees mutual exclusion from any currently running hotplug operations. */ void cpu_hotplug_disable(void) { cpu_maps_update_begin(); cpu_hotplug_disabled++; cpu_maps_update_done(); } EXPORT_SYMBOL_GPL(cpu_hotplug_disable); static void __cpu_hotplug_enable(void) { if (WARN_ONCE(!cpu_hotplug_disabled, "Unbalanced cpu hotplug enable\n")) return; cpu_hotplug_disabled--; } void cpu_hotplug_enable(void) { cpu_maps_update_begin(); __cpu_hotplug_enable(); cpu_maps_update_done(); } EXPORT_SYMBOL_GPL(cpu_hotplug_enable); #endif /* CONFIG_HOTPLUG_CPU */ /* Notifier wrappers for transitioning to state machine */ static int bringup_wait_for_ap(unsigned int cpu) { struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); wait_for_completion(&st->done); return st->result; } static int bringup_cpu(unsigned int cpu) { struct task_struct *idle = idle_thread_get(cpu); int ret; /* * Some architectures have to walk the irq descriptors to * setup the vector space for the cpu which comes online. * Prevent irq alloc/free across the bringup. */ irq_lock_sparse(); /* Arch-specific enabling code. */ ret = __cpu_up(cpu, idle); irq_unlock_sparse(); if (ret) return ret; ret = bringup_wait_for_ap(cpu); BUG_ON(!cpu_online(cpu)); return ret; } /* * Hotplug state machine related functions */ static void undo_cpu_down(unsigned int cpu, struct cpuhp_cpu_state *st) { for (st->state++; st->state < st->target; st->state++) { struct cpuhp_step *step = cpuhp_get_step(st->state); if (!step->skip_onerr) cpuhp_invoke_callback(cpu, st->state, true, NULL); } } static int cpuhp_down_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st, enum cpuhp_state target) { enum cpuhp_state prev_state = st->state; int ret = 0; for (; st->state > target; st->state--) { ret = cpuhp_invoke_callback(cpu, st->state, false, NULL); if (ret) { st->target = prev_state; undo_cpu_down(cpu, st); break; } } return ret; } static void undo_cpu_up(unsigned int cpu, struct cpuhp_cpu_state *st) { for (st->state--; st->state > st->target; st->state--) { struct cpuhp_step *step = cpuhp_get_step(st->state); if (!step->skip_onerr) cpuhp_invoke_callback(cpu, st->state, false, NULL); } } static int cpuhp_up_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st, enum cpuhp_state target) { enum cpuhp_state prev_state = st->state; int ret = 0; while (st->state < target) { st->state++; ret = cpuhp_invoke_callback(cpu, st->state, true, NULL); if (ret) { st->target = prev_state; undo_cpu_up(cpu, st); break; } } return ret; } /* * The cpu hotplug threads manage the bringup and teardown of the cpus */ static void cpuhp_create(unsigned int cpu) { struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); init_completion(&st->done); } static int cpuhp_should_run(unsigned int cpu) { struct cpuhp_cpu_state *st = this_cpu_ptr(&cpuhp_state); return st->should_run; } /* Execute the teardown callbacks. Used to be CPU_DOWN_PREPARE */ static int cpuhp_ap_offline(unsigned int cpu, struct cpuhp_cpu_state *st) { enum cpuhp_state target = max((int)st->target, CPUHP_TEARDOWN_CPU); return cpuhp_down_callbacks(cpu, st, target); } /* Execute the online startup callbacks. Used to be CPU_ONLINE */ static int cpuhp_ap_online(unsigned int cpu, struct cpuhp_cpu_state *st) { return cpuhp_up_callbacks(cpu, st, st->target); } /* * Execute teardown/startup callbacks on the plugged cpu. Also used to invoke * callbacks when a state gets [un]installed at runtime. */ static void cpuhp_thread_fun(unsigned int cpu) { struct cpuhp_cpu_state *st = this_cpu_ptr(&cpuhp_state); int ret = 0; /* * Paired with the mb() in cpuhp_kick_ap_work and * cpuhp_invoke_ap_callback, so the work set is consistent visible. */ smp_mb(); if (!st->should_run) return; st->should_run = false; /* Single callback invocation for [un]install ? */ if (st->single) { if (st->cb_state < CPUHP_AP_ONLINE) { local_irq_disable(); ret = cpuhp_invoke_callback(cpu, st->cb_state, st->bringup, st->node); local_irq_enable(); } else { ret = cpuhp_invoke_callback(cpu, st->cb_state, st->bringup, st->node); } } else if (st->rollback) { BUG_ON(st->state < CPUHP_AP_ONLINE_IDLE); undo_cpu_down(cpu, st); st->rollback = false;