1716 lines · 41.5 KB · C
RawDownload
/* 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 <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 * @cb_stat:	The state for a single callback (install/uninstall) * @cb:		Single callback function (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;	enum cpuhp_state	cb_state;	int			(*cb)(unsigned int cpu);	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;	int		(*startup)(unsigned int cpu);	int		(*teardown)(unsigned int cpu);	bool		skip_onerr;	bool		cant_stop;}; static DEFINE_MUTEX(cpuhp_state_mutex);static struct cpuhp_step cpuhp_bp_states[];static struct cpuhp_step cpuhp_ap_states[]; /** * 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 * @cb:		The callback function to invoke * * Called from cpu hotplug and from the state register machinery */static int cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state step,				 int (*cb)(unsigned int)){	struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);	int ret = 0; 	if (cb) {		trace_cpuhp_enter(cpu, st->target, step, cb);		ret = cb(cpu);		trace_cpuhp_exit(cpu, st->state, step, ret);	}	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. * The APIs cpu_notifier_register_begin/done() must be used to protect CPU * hotplug callback (un)registration performed using __register_cpu_notifier() * or __unregister_cpu_notifier(). */void cpu_maps_update_begin(void){	mutex_lock(&cpu_add_remove_lock);}EXPORT_SYMBOL(cpu_notifier_register_begin); void cpu_maps_update_done(void){	mutex_unlock(&cpu_add_remove_lock);}EXPORT_SYMBOL(cpu_notifier_register_done); static RAW_NOTIFIER_HEAD(cpu_chain); /* 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 = {.name = "cpu_hotplug.lock" },#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); void cpu_hotplug_enable(void){	cpu_maps_update_begin();	WARN_ON(--cpu_hotplug_disabled < 0);	cpu_maps_update_done();}EXPORT_SYMBOL_GPL(cpu_hotplug_enable);#endif	/* CONFIG_HOTPLUG_CPU */ /* Need to know about CPUs going up/down? */int register_cpu_notifier(struct notifier_block *nb){	int ret;	cpu_maps_update_begin();	ret = raw_notifier_chain_register(&cpu_chain, nb);	cpu_maps_update_done();	return ret;} int __register_cpu_notifier(struct notifier_block *nb){	return raw_notifier_chain_register(&cpu_chain, nb);} static int __cpu_notify(unsigned long val, unsigned int cpu, int nr_to_call,			int *nr_calls){	unsigned long mod = cpuhp_tasks_frozen ? CPU_TASKS_FROZEN : 0;	void *hcpu = (void *)(long)cpu; 	int ret; 	ret = __raw_notifier_call_chain(&cpu_chain, val | mod, hcpu, nr_to_call,					nr_calls); 	return notifier_to_errno(ret);} static int cpu_notify(unsigned long val, unsigned int cpu){	return __cpu_notify(val, cpu, -1, NULL);} static void cpu_notify_nofail(unsigned long val, unsigned int cpu){	BUG_ON(cpu_notify(val, cpu));} /* Notifier wrappers for transitioning to state machine */static int notify_prepare(unsigned int cpu){	int nr_calls = 0;	int ret; 	ret = __cpu_notify(CPU_UP_PREPARE, cpu, -1, &nr_calls);	if (ret) {		nr_calls--;		printk(KERN_WARNING "%s: attempt to bring up CPU %u failed\n",				__func__, cpu);		__cpu_notify(CPU_UP_CANCELED, cpu, nr_calls, NULL);	}	return ret;} static int notify_online(unsigned int cpu){	cpu_notify(CPU_ONLINE, cpu);	return 0;} static int notify_starting(unsigned int cpu){	cpu_notify(CPU_STARTING, cpu);	return 0;} 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; 	/* Arch-specific enabling code. */	ret = __cpu_up(cpu, idle);	if (ret) {		cpu_notify(CPU_UP_CANCELED, cpu);		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,			  struct cpuhp_step *steps){	for (st->state++; st->state < st->target; st->state++) {		struct cpuhp_step *step = steps + st->state; 		if (!step->skip_onerr)			cpuhp_invoke_callback(cpu, st->state, step->startup);	}} static int cpuhp_down_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st,				struct cpuhp_step *steps, enum cpuhp_state target){	enum cpuhp_state prev_state = st->state;	int ret = 0; 	for (; st->state > target; st->state--) {		struct cpuhp_step *step = steps + st->state; 		ret = cpuhp_invoke_callback(cpu, st->state, step->teardown);		if (ret) {			st->target = prev_state;			undo_cpu_down(cpu, st, steps);			break;		}	}	return ret;} static void undo_cpu_up(unsigned int cpu, struct cpuhp_cpu_state *st,			struct cpuhp_step *steps){	for (st->state--; st->state > st->target; st->state--) {		struct cpuhp_step *step = steps + st->state; 		if (!step->skip_onerr)			cpuhp_invoke_callback(cpu, st->state, step->teardown);	}} static int cpuhp_up_callbacks(unsigned int cpu, struct cpuhp_cpu_state *st,			      struct cpuhp_step *steps, enum cpuhp_state target){	enum cpuhp_state prev_state = st->state;	int ret = 0; 	while (st->state < target) {		struct cpuhp_step *step; 		st->state++;		step = steps + st->state;		ret = cpuhp_invoke_callback(cpu, st->state, step->startup);		if (ret) {			st->target = prev_state;			undo_cpu_up(cpu, st, steps);			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, cpuhp_ap_states, 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, cpuhp_ap_states, 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->cb) {		if (st->cb_state < CPUHP_AP_ONLINE) {			local_irq_disable();			ret = cpuhp_invoke_callback(cpu, st->cb_state, st->cb);			local_irq_enable();		} else {			ret = cpuhp_invoke_callback(cpu, st->cb_state, st->cb);		}	} else if (st->rollback) {		BUG_ON(st->state < CPUHP_AP_ONLINE_IDLE); 		undo_cpu_down(cpu, st, cpuhp_ap_states);		/*		 * This is a momentary workaround to keep the notifier users		 * happy. Will go away once we got rid of the notifiers.		 */		cpu_notify_nofail(CPU_DOWN_FAILED, cpu);		st->rollback = false;	} else {		/* Cannot happen .... */		BUG_ON(st->state < CPUHP_AP_ONLINE_IDLE); 

Lines 1–500 of 1716.