3339 lines · 81.3 KB · C
RawDownload
 * Hibernate notifications. */static intcpu_hotplug_pm_callback(struct notifier_block *nb,			unsigned long action, void *ptr){	switch (action) { 	case PM_SUSPEND_PREPARE:	case PM_HIBERNATION_PREPARE:		cpu_hotplug_disable();		break; 	case PM_POST_SUSPEND:	case PM_POST_HIBERNATION:		cpu_hotplug_enable();		break; 	default:		return NOTIFY_DONE;	} 	return NOTIFY_OK;}  static int __init cpu_hotplug_pm_sync_init(void){	/*	 * cpu_hotplug_pm_callback has higher priority than x86	 * bsp_pm_callback which depends on cpu_hotplug_pm_callback	 * to disable cpu hotplug to avoid cpu hotplug race.	 */	pm_notifier(cpu_hotplug_pm_callback, 0);	return 0;}core_initcall(cpu_hotplug_pm_sync_init); #endif /* CONFIG_PM_SLEEP_SMP */ int __boot_cpu_id; #endif /* CONFIG_SMP */ /* Boot processor state steps */static struct cpuhp_step cpuhp_hp_states[] = {	[CPUHP_OFFLINE] = {		.name			= "offline",		.startup.single		= NULL,		.teardown.single	= NULL,	},#ifdef CONFIG_SMP	[CPUHP_CREATE_THREADS]= {		.name			= "threads:prepare",		.startup.single		= smpboot_create_threads,		.teardown.single	= NULL,		.cant_stop		= true,	},	[CPUHP_RANDOM_PREPARE] = {		.name			= "random:prepare",		.startup.single		= random_prepare_cpu,		.teardown.single	= NULL,	},	[CPUHP_WORKQUEUE_PREP] = {		.name			= "workqueue:prepare",		.startup.single		= workqueue_prepare_cpu,		.teardown.single	= NULL,	},	[CPUHP_HRTIMERS_PREPARE] = {		.name			= "hrtimers:prepare",		.startup.single		= hrtimers_prepare_cpu,		.teardown.single	= NULL,	},	[CPUHP_SMPCFD_PREPARE] = {		.name			= "smpcfd:prepare",		.startup.single		= smpcfd_prepare_cpu,		.teardown.single	= smpcfd_dead_cpu,	},	[CPUHP_RELAY_PREPARE] = {		.name			= "relay:prepare",		.startup.single		= relay_prepare_cpu,		.teardown.single	= NULL,	},	[CPUHP_RCUTREE_PREP] = {		.name			= "RCU/tree:prepare",		.startup.single		= rcutree_prepare_cpu,		.teardown.single	= rcutree_dead_cpu,	},	/*	 * 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_PREPARE] = {		.name			= "timers:prepare",		.startup.single		= timers_prepare_cpu,		.teardown.single	= timers_dead_cpu,	}, #ifdef CONFIG_HOTPLUG_SPLIT_STARTUP	/*	 * Kicks the AP alive. AP will wait in cpuhp_ap_sync_alive() until	 * the next step will release it.	 */	[CPUHP_BP_KICK_AP] = {		.name			= "cpu:kick_ap",		.startup.single		= cpuhp_kick_ap_alive,	}, 	/*	 * Waits for the AP to reach cpuhp_ap_sync_alive() and then	 * releases it for the complete bringup.	 */	[CPUHP_BRINGUP_CPU] = {		.name			= "cpu:bringup",		.startup.single		= cpuhp_bringup_ap,		.teardown.single	= finish_cpu,		.cant_stop		= true,	},#else	/*	 * All-in-one CPU bringup state which includes the kick alive.	 */	[CPUHP_BRINGUP_CPU] = {		.name			= "cpu:bringup",		.startup.single		= bringup_cpu,		.teardown.single	= finish_cpu,		.cant_stop		= true,	},#endif	/* Final state before CPU kills itself */	[CPUHP_AP_IDLE_DEAD] = {		.name			= "idle:dead",	},	/*	 * Last state before CPU enters the idle loop to die. Transient state	 * for synchronization.	 */	[CPUHP_AP_OFFLINE] = {		.name			= "ap:offline",		.cant_stop		= true,	},	/* First state is scheduler control. Interrupts are disabled */	[CPUHP_AP_SCHED_STARTING] = {		.name			= "sched:starting",		.startup.single		= sched_cpu_starting,		.teardown.single	= sched_cpu_dying,	},	[CPUHP_AP_RCUTREE_DYING] = {		.name			= "RCU/tree:dying",		.startup.single		= NULL,		.teardown.single	= rcutree_dying_cpu,	},	[CPUHP_AP_SMPCFD_DYING] = {		.name			= "smpcfd:dying",		.startup.single		= NULL,		.teardown.single	= smpcfd_dying_cpu,	},	[CPUHP_AP_HRTIMERS_DYING] = {		.name			= "hrtimers:dying",		.startup.single		= hrtimers_cpu_starting,		.teardown.single	= hrtimers_cpu_dying,	},	[CPUHP_AP_TICK_DYING] = {		.name			= "tick:dying",		.startup.single		= NULL,		.teardown.single	= tick_cpu_dying,	},	/* Entry state on starting. Interrupts enabled from here on. Transient	 * state for synchronsization */	[CPUHP_AP_ONLINE] = {		.name			= "ap:online",	},	/*	 * Handled on control processor until the plugged processor manages	 * this itself.	 */	[CPUHP_TEARDOWN_CPU] = {		.name			= "cpu:teardown",		.startup.single		= NULL,		.teardown.single	= takedown_cpu,		.cant_stop		= true,	}, 	[CPUHP_AP_SCHED_WAIT_EMPTY] = {		.name			= "sched:waitempty",		.startup.single		= NULL,		.teardown.single	= sched_cpu_wait_empty,	}, 	/* Handle smpboot threads park/unpark */	[CPUHP_AP_SMPBOOT_THREADS] = {		.name			= "smpboot/threads:online",		.startup.single		= smpboot_unpark_threads,		.teardown.single	= smpboot_park_threads,	},	[CPUHP_AP_IRQ_AFFINITY_ONLINE] = {		.name			= "irq/affinity:online",		.startup.single		= irq_affinity_online_cpu,		.teardown.single	= NULL,	},	[CPUHP_AP_PERF_ONLINE] = {		.name			= "perf:online",		.startup.single		= perf_event_init_cpu,		.teardown.single	= perf_event_exit_cpu,	},	[CPUHP_AP_WATCHDOG_ONLINE] = {		.name			= "lockup_detector:online",		.startup.single		= lockup_detector_online_cpu,		.teardown.single	= lockup_detector_offline_cpu,	},	[CPUHP_AP_WORKQUEUE_ONLINE] = {		.name			= "workqueue:online",		.startup.single		= workqueue_online_cpu,		.teardown.single	= workqueue_offline_cpu,	},	[CPUHP_AP_RANDOM_ONLINE] = {		.name			= "random:online",		.startup.single		= random_online_cpu,		.teardown.single	= NULL,	},	[CPUHP_AP_RCUTREE_ONLINE] = {		.name			= "RCU/tree:online",		.startup.single		= rcutree_online_cpu,		.teardown.single	= rcutree_offline_cpu,	},#endif	/*	 * The dynamically registered state space is here	 */ #ifdef CONFIG_SMP	/* Last state is scheduler control setting the cpu active */	[CPUHP_AP_ACTIVE] = {		.name			= "sched:active",		.startup.single		= sched_cpu_activate,		.teardown.single	= sched_cpu_deactivate,	},#endif 	/* CPU is fully up and running. */	[CPUHP_ONLINE] = {		.name			= "online",		.startup.single		= NULL,		.teardown.single	= NULL,	},}; /* Sanity check for callbacks */static int cpuhp_cb_check(enum cpuhp_state state){	if (state <= CPUHP_OFFLINE || state >= CPUHP_ONLINE)		return -EINVAL;	return 0;} /* * Returns a free for dynamic slot assignment of the Online state. The states * are protected by the cpuhp_slot_states mutex and an empty slot is identified * by having no name assigned. */static int cpuhp_reserve_state(enum cpuhp_state state){	enum cpuhp_state i, end;	struct cpuhp_step *step; 	switch (state) {	case CPUHP_AP_ONLINE_DYN:		step = cpuhp_hp_states + CPUHP_AP_ONLINE_DYN;		end = CPUHP_AP_ONLINE_DYN_END;		break;	case CPUHP_BP_PREPARE_DYN:		step = cpuhp_hp_states + CPUHP_BP_PREPARE_DYN;		end = CPUHP_BP_PREPARE_DYN_END;		break;	default:		return -EINVAL;	} 	for (i = state; i <= end; i++, step++) {		if (!step->name)			return i;	}	WARN(1, "No more dynamic states available for CPU hotplug\n");	return -ENOSPC;} static int cpuhp_store_callbacks(enum cpuhp_state state, const char *name,				 int (*startup)(unsigned int cpu),				 int (*teardown)(unsigned int cpu),				 bool multi_instance){	/* (Un)Install the callbacks for further cpu hotplug operations */	struct cpuhp_step *sp;	int ret = 0; 	/*	 * If name is NULL, then the state gets removed.	 *	 * CPUHP_AP_ONLINE_DYN and CPUHP_BP_PREPARE_DYN are handed out on	 * the first allocation from these dynamic ranges, so the removal	 * would trigger a new allocation and clear the wrong (already	 * empty) state, leaving the callbacks of the to be cleared state	 * dangling, which causes wreckage on the next hotplug operation.	 */	if (name && (state == CPUHP_AP_ONLINE_DYN ||		     state == CPUHP_BP_PREPARE_DYN)) {		ret = cpuhp_reserve_state(state);		if (ret < 0)			return ret;		state = ret;	}	sp = cpuhp_get_step(state);	if (name && sp->name)		return -EBUSY; 	sp->startup.single = startup;	sp->teardown.single = teardown;	sp->name = name;	sp->multi_instance = multi_instance;	INIT_HLIST_HEAD(&sp->list);	return ret;} static void *cpuhp_get_teardown_cb(enum cpuhp_state state){	return cpuhp_get_step(state)->teardown.single;} /* * Call the startup/teardown function for a step either on the AP or * on the current CPU. */static int cpuhp_issue_call(int cpu, enum cpuhp_state state, bool bringup,			    struct hlist_node *node){	struct cpuhp_step *sp = cpuhp_get_step(state);	int ret; 	/*	 * If there's nothing to do, we done.	 * Relies on the union for multi_instance.	 */	if (cpuhp_step_empty(bringup, sp))		return 0;	/*	 * The non AP bound callbacks can fail on bringup. On teardown	 * e.g. module removal we crash for now.	 */#ifdef CONFIG_SMP	if (cpuhp_is_ap_state(state))		ret = cpuhp_invoke_ap_callback(cpu, state, bringup, node);	else		ret = cpuhp_invoke_callback(cpu, state, bringup, node, NULL);#else	if (cpuhp_is_atomic_state(state)) {		guard(irqsave)();		ret = cpuhp_invoke_callback(cpu, state, bringup, node, NULL);		/* STARTING/DYING must not fail! */		WARN_ON_ONCE(ret);	} else {		ret = cpuhp_invoke_callback(cpu, state, bringup, node, NULL);	}#endif	BUG_ON(ret && !bringup);	return ret;} /* * Called from __cpuhp_setup_state on a recoverable failure. * * Note: The teardown callbacks for rollback are not allowed to fail! */static void cpuhp_rollback_install(int failedcpu, enum cpuhp_state state,				   struct hlist_node *node){	int cpu; 	/* Roll back the already executed steps on the other cpus */	for_each_present_cpu(cpu) {		struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);		int cpustate = st->state; 		if (cpu >= failedcpu)			break; 		/* Did we invoke the startup call on that cpu ? */		if (cpustate >= state)			cpuhp_issue_call(cpu, state, false, node);	}} int __cpuhp_state_add_instance_cpuslocked(enum cpuhp_state state,					  struct hlist_node *node,					  bool invoke){	struct cpuhp_step *sp;	int cpu;	int ret; 	lockdep_assert_cpus_held(); 	sp = cpuhp_get_step(state);	if (sp->multi_instance == false)		return -EINVAL; 	mutex_lock(&cpuhp_state_mutex); 	if (!invoke || !sp->startup.multi)		goto add_node; 	/*	 * Try to call the startup callback for each present cpu	 * depending on the hotplug state of the cpu.	 */	for_each_present_cpu(cpu) {		struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);		int cpustate = st->state; 		if (cpustate < state)			continue; 		ret = cpuhp_issue_call(cpu, state, true, node);		if (ret) {			if (sp->teardown.multi)				cpuhp_rollback_install(cpu, state, node);			goto unlock;		}	}add_node:	ret = 0;	hlist_add_head(node, &sp->list);unlock:	mutex_unlock(&cpuhp_state_mutex);	return ret;} int __cpuhp_state_add_instance(enum cpuhp_state state, struct hlist_node *node,			       bool invoke){	int ret; 	cpus_read_lock();	ret = __cpuhp_state_add_instance_cpuslocked(state, node, invoke);	cpus_read_unlock();	return ret;}EXPORT_SYMBOL_GPL(__cpuhp_state_add_instance); /** * __cpuhp_setup_state_cpuslocked - Setup the callbacks for an hotplug machine state * @state:		The state to setup * @name:		Name of the step * @invoke:		If true, the startup function is invoked for cpus where *			cpu state >= @state * @startup:		startup callback function * @teardown:		teardown callback function * @multi_instance:	State is set up for multiple instances which get *			added afterwards. * * The caller needs to hold cpus read locked while calling this function. * Return: *   On success: *      Positive state number if @state is CPUHP_AP_ONLINE_DYN or CPUHP_BP_PREPARE_DYN; *      0 for all other states *   On failure: proper (negative) error code */int __cpuhp_setup_state_cpuslocked(enum cpuhp_state state,				   const char *name, bool invoke,				   int (*startup)(unsigned int cpu),				   int (*teardown)(unsigned int cpu),				   bool multi_instance){	int cpu, ret = 0;	bool dynstate; 	lockdep_assert_cpus_held(); 	if (cpuhp_cb_check(state) || !name)		return -EINVAL; 	mutex_lock(&cpuhp_state_mutex); 	ret = cpuhp_store_callbacks(state, name, startup, teardown,				    multi_instance); 	dynstate = state == CPUHP_AP_ONLINE_DYN || state == CPUHP_BP_PREPARE_DYN;	if (ret > 0 && dynstate) {		state = ret;		ret = 0;	} 	if (ret || !invoke || !startup)		goto out; 	/*	 * Try to call the startup callback for each present cpu	 * depending on the hotplug state of the cpu.	 */	for_each_present_cpu(cpu) {

Lines 2001–2500 of 3339. Previous lines

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t