diff options
Diffstat (limited to 'kernel/cpu.c')
| -rw-r--r-- | kernel/cpu.c | 129 | 
1 files changed, 82 insertions, 47 deletions
diff --git a/kernel/cpu.c b/kernel/cpu.c index d7f07a2da5a..a343bde710b 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -19,6 +19,8 @@  #include <linux/mutex.h>  #include <linux/gfp.h>  #include <linux/suspend.h> +#include <linux/lockdep.h> +#include <trace/events/power.h>  #include "smpboot.h" @@ -27,18 +29,23 @@  static DEFINE_MUTEX(cpu_add_remove_lock);  /* - * The following two API's must be used when attempting - * to serialize the updates to cpu_online_mask, cpu_present_mask. + * 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); @@ -57,17 +64,30 @@ static struct {  	 * an ongoing cpu hotplug operation.  	 */  	int refcount; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +	struct lockdep_map dep_map; +#endif  } cpu_hotplug = {  	.active_writer = NULL,  	.lock = __MUTEX_INITIALIZER(cpu_hotplug.lock),  	.refcount = 0, +#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()      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);  	cpu_hotplug.refcount++;  	mutex_unlock(&cpu_hotplug.lock); @@ -87,6 +107,7 @@ void put_online_cpus(void)  	if (!--cpu_hotplug.refcount && unlikely(cpu_hotplug.active_writer))  		wake_up_process(cpu_hotplug.active_writer);  	mutex_unlock(&cpu_hotplug.lock); +	cpuhp_lock_release();  }  EXPORT_SYMBOL_GPL(put_online_cpus); @@ -117,6 +138,7 @@ void cpu_hotplug_begin(void)  {  	cpu_hotplug.active_writer = current; +	cpuhp_lock_acquire();  	for (;;) {  		mutex_lock(&cpu_hotplug.lock);  		if (likely(!cpu_hotplug.refcount)) @@ -131,6 +153,7 @@ void cpu_hotplug_done(void)  {  	cpu_hotplug.active_writer = NULL;  	mutex_unlock(&cpu_hotplug.lock); +	cpuhp_lock_release();  }  /* @@ -166,6 +189,11 @@ int __ref register_cpu_notifier(struct notifier_block *nb)  	return ret;  } +int __ref __register_cpu_notifier(struct notifier_block *nb) +{ +	return raw_notifier_chain_register(&cpu_chain, nb); +} +  static int __cpu_notify(unsigned long val, void *v, int nr_to_call,  			int *nr_calls)  { @@ -189,6 +217,7 @@ static void cpu_notify_nofail(unsigned long val, void *v)  	BUG_ON(cpu_notify(val, v));  }  EXPORT_SYMBOL(register_cpu_notifier); +EXPORT_SYMBOL(__register_cpu_notifier);  void __ref unregister_cpu_notifier(struct notifier_block *nb)  { @@ -198,6 +227,12 @@ void __ref unregister_cpu_notifier(struct notifier_block *nb)  }  EXPORT_SYMBOL(unregister_cpu_notifier); +void __ref __unregister_cpu_notifier(struct notifier_block *nb) +{ +	raw_notifier_chain_unregister(&cpu_chain, nb); +} +EXPORT_SYMBOL(__unregister_cpu_notifier); +  /**   * clear_tasks_mm_cpumask - Safely clear tasks' mm_cpumask for a CPU   * @cpu: a CPU id @@ -249,8 +284,7 @@ static inline void check_for_tasks(int cpu)  		task_cputime(p, &utime, &stime);  		if (task_cpu(p) == cpu && p->state == TASK_RUNNING &&  		    (utime || stime)) -			printk(KERN_WARNING "Task %s (pid = %d) is on cpu %d " -				"(state = %ld, flags = %x)\n", +			pr_warn("Task %s (pid = %d) is on cpu %d (state = %ld, flags = %x)\n",  				p->comm, task_pid_nr(p), cpu,  				p->state, p->flags);  	} @@ -302,12 +336,32 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)  	if (err) {  		nr_calls--;  		__cpu_notify(CPU_DOWN_FAILED | mod, hcpu, nr_calls, NULL); -		printk("%s: attempt to take down CPU %u failed\n", -				__func__, cpu); +		pr_warn("%s: attempt to take down CPU %u failed\n", +			__func__, cpu);  		goto out_release;  	} + +	/* +	 * By now we've cleared cpu_active_mask, wait for all preempt-disabled +	 * and RCU users of this state to go away such that all new such users +	 * will observe it. +	 * +	 * For CONFIG_PREEMPT we have preemptible RCU and its sync_rcu() might +	 * not imply sync_sched(), so explicitly call both. +	 * +	 * Do sync before park smpboot threads to take care the rcu boost case. +	 */ +#ifdef CONFIG_PREEMPT +	synchronize_sched(); +#endif +	synchronize_rcu(); +  	smpboot_park_threads(cpu); +	/* +	 * So now all preempt/rcu users must observe !cpu_active(). +	 */ +  	err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu));  	if (err) {  		/* CPU didn't die: tell everyone.  Can't complain. */ @@ -390,8 +444,8 @@ static int _cpu_up(unsigned int cpu, int tasks_frozen)  	ret = __cpu_notify(CPU_UP_PREPARE | mod, hcpu, -1, &nr_calls);  	if (ret) {  		nr_calls--; -		printk(KERN_WARNING "%s: attempt to bring up CPU %u failed\n", -				__func__, cpu); +		pr_warn("%s: attempt to bring up CPU %u failed\n", +			__func__, cpu);  		goto out_notify;  	} @@ -420,42 +474,18 @@ int cpu_up(unsigned int cpu)  {  	int err = 0; -#ifdef	CONFIG_MEMORY_HOTPLUG -	int nid; -	pg_data_t	*pgdat; -#endif -  	if (!cpu_possible(cpu)) { -		printk(KERN_ERR "can't online cpu %d because it is not " -			"configured as may-hotadd at boot time\n", cpu); +		pr_err("can't online cpu %d because it is not configured as may-hotadd at boot time\n", +		       cpu);  #if defined(CONFIG_IA64) -		printk(KERN_ERR "please check additional_cpus= boot " -				"parameter\n"); +		pr_err("please check additional_cpus= boot parameter\n");  #endif  		return -EINVAL;  	} -#ifdef	CONFIG_MEMORY_HOTPLUG -	nid = cpu_to_node(cpu); -	if (!node_online(nid)) { -		err = mem_online_node(nid); -		if (err) -			return err; -	} - -	pgdat = NODE_DATA(nid); -	if (!pgdat) { -		printk(KERN_ERR -			"Can't online cpu %d due to NULL pgdat\n", cpu); -		return -ENOMEM; -	} - -	if (pgdat->node_zonelists->_zonerefs->zone == NULL) { -		mutex_lock(&zonelists_mutex); -		build_all_zonelists(NULL, NULL); -		mutex_unlock(&zonelists_mutex); -	} -#endif +	err = try_online_node(cpu_to_node(cpu)); +	if (err) +		return err;  	cpu_maps_update_begin(); @@ -487,16 +517,17 @@ int disable_nonboot_cpus(void)  	 */  	cpumask_clear(frozen_cpus); -	printk("Disabling non-boot CPUs ...\n"); +	pr_info("Disabling non-boot CPUs ...\n");  	for_each_online_cpu(cpu) {  		if (cpu == first_cpu)  			continue; +		trace_suspend_resume(TPS("CPU_OFF"), cpu, true);  		error = _cpu_down(cpu, 1); +		trace_suspend_resume(TPS("CPU_OFF"), cpu, false);  		if (!error)  			cpumask_set_cpu(cpu, frozen_cpus);  		else { -			printk(KERN_ERR "Error taking CPU%d down: %d\n", -				cpu, error); +			pr_err("Error taking CPU%d down: %d\n", cpu, error);  			break;  		}  	} @@ -506,7 +537,7 @@ int disable_nonboot_cpus(void)  		/* Make sure the CPUs won't be enabled by someone else */  		cpu_hotplug_disabled = 1;  	} else { -		printk(KERN_ERR "Non-boot CPUs are not disabled\n"); +		pr_err("Non-boot CPUs are not disabled\n");  	}  	cpu_maps_update_done();  	return error; @@ -530,17 +561,19 @@ void __ref enable_nonboot_cpus(void)  	if (cpumask_empty(frozen_cpus))  		goto out; -	printk(KERN_INFO "Enabling non-boot CPUs ...\n"); +	pr_info("Enabling non-boot CPUs ...\n");  	arch_enable_nonboot_cpus_begin();  	for_each_cpu(cpu, frozen_cpus) { +		trace_suspend_resume(TPS("CPU_ON"), cpu, true);  		error = _cpu_up(cpu, 1); +		trace_suspend_resume(TPS("CPU_ON"), cpu, false);  		if (!error) { -			printk(KERN_INFO "CPU%d is up\n", cpu); +			pr_info("CPU%d is up\n", cpu);  			continue;  		} -		printk(KERN_WARNING "Error taking CPU%d up: %d\n", cpu, error); +		pr_warn("Error taking CPU%d up: %d\n", cpu, error);  	}  	arch_enable_nonboot_cpus_end(); @@ -695,10 +728,12 @@ void set_cpu_present(unsigned int cpu, bool present)  void set_cpu_online(unsigned int cpu, bool online)  { -	if (online) +	if (online) {  		cpumask_set_cpu(cpu, to_cpumask(cpu_online_bits)); -	else +		cpumask_set_cpu(cpu, to_cpumask(cpu_active_bits)); +	} else {  		cpumask_clear_cpu(cpu, to_cpumask(cpu_online_bits)); +	}  }  void set_cpu_active(unsigned int cpu, bool active)  | 
