1783 lines · 42.9 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/signal.h>#include <linux/sched/hotplug.h>#include <linux/sched/task.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 <linux/percpu-rwsem.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); #if defined(CONFIG_LOCKDEP) && defined(CONFIG_SMP)static struct lock_class_key cpuhp_state_key;static struct lockdep_map cpuhp_state_lock_map =	STATIC_LOCKDEP_MAP_INIT("cpuhp_state", &cpuhp_state_key);#endif /** * 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 DEFINE_STATIC_PERCPU_RWSEM(cpu_hotplug_lock); void cpus_read_lock(void){	percpu_down_read(&cpu_hotplug_lock);}EXPORT_SYMBOL_GPL(cpus_read_lock); void cpus_read_unlock(void){	percpu_up_read(&cpu_hotplug_lock);}EXPORT_SYMBOL_GPL(cpus_read_unlock); void cpus_write_lock(void){	percpu_down_write(&cpu_hotplug_lock);} void cpus_write_unlock(void){	percpu_up_write(&cpu_hotplug_lock);} void lockdep_assert_cpus_held(void){	percpu_rwsem_assert_held(&cpu_hotplug_lock);} /* * 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 */ static void __cpuhp_kick_ap_work(struct cpuhp_cpu_state *st); static int bringup_wait_for_ap(unsigned int cpu){	struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); 	/* Wait for the CPU to reach CPUHP_AP_ONLINE_IDLE */	wait_for_completion(&st->done);	BUG_ON(!cpu_online(cpu)); 	/* Unpark the stopper thread and the hotplug thread of the target cpu */	stop_machine_unpark(cpu);	kthread_unpark(st->thread); 	/* Should we go further up ? */	if (st->target > CPUHP_AP_ONLINE_IDLE) {		__cpuhp_kick_ap_work(st);		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;	return bringup_wait_for_ap(cpu);} /* * 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; 	lock_map_acquire(&cpuhp_state_lock_map);	/* 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;	} else {		/* Cannot happen .... */		BUG_ON(st->state < CPUHP_AP_ONLINE_IDLE); 		/* Regular hotplug work */		if (st->state < st->target)			ret = cpuhp_ap_online(cpu, st);		else if (st->state > st->target)			ret = cpuhp_ap_offline(cpu, st);	}	lock_map_release(&cpuhp_state_lock_map);	st->result = ret;	complete(&st->done);} /* Invoke a single callback on a remote cpu */static intcpuhp_invoke_ap_callback(int cpu, enum cpuhp_state state, bool bringup,			 struct hlist_node *node){	struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu); 	if (!cpu_online(cpu))		return 0; 	lock_map_acquire(&cpuhp_state_lock_map);	lock_map_release(&cpuhp_state_lock_map); 	/*	 * 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, bringup, node); 	st->cb_state = state;	st->single = true;	st->bringup = bringup;	st->node = node; 	/*	 * Make sure the above stores are visible before should_run becomes	 * true. Paired with the mb() above in cpuhp_thread_fun()	 */	smp_mb();	st->should_run = true;	wake_up_process(st->thread);	wait_for_completion(&st->done);	return st->result;} /* Regular hotplug invocation of the AP hotplug thread */static void __cpuhp_kick_ap_work(struct cpuhp_cpu_state *st){	st->result = 0;	st->single = false;	/*	 * Make sure the above stores are visible before should_run becomes	 * true. Paired with the mb() above in cpuhp_thread_fun()	 */

Lines 1–500 of 1783.