diff options
Diffstat (limited to 'kernel/reboot.c')
| -rw-r--r-- | kernel/reboot.c | 101 | 
1 files changed, 63 insertions, 38 deletions
| diff --git a/kernel/reboot.c b/kernel/reboot.c index a091145ee710..3c35445bf5ad 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c @@ -315,6 +315,43 @@ static int sys_off_notify(struct notifier_block *nb,  	return handler->sys_off_cb(&data);  } +static struct sys_off_handler platform_sys_off_handler; + +static struct sys_off_handler *alloc_sys_off_handler(int priority) +{ +	struct sys_off_handler *handler; +	gfp_t flags; + +	/* +	 * Platforms like m68k can't allocate sys_off handler dynamically +	 * at the early boot time because memory allocator isn't available yet. +	 */ +	if (priority == SYS_OFF_PRIO_PLATFORM) { +		handler = &platform_sys_off_handler; +		if (handler->cb_data) +			return ERR_PTR(-EBUSY); +	} else { +		if (system_state > SYSTEM_RUNNING) +			flags = GFP_ATOMIC; +		else +			flags = GFP_KERNEL; + +		handler = kzalloc(sizeof(*handler), flags); +		if (!handler) +			return ERR_PTR(-ENOMEM); +	} + +	return handler; +} + +static void free_sys_off_handler(struct sys_off_handler *handler) +{ +	if (handler == &platform_sys_off_handler) +		memset(handler, 0, sizeof(*handler)); +	else +		kfree(handler); +} +  /**   *	register_sys_off_handler - Register sys-off handler   *	@mode: Sys-off mode @@ -345,9 +382,9 @@ register_sys_off_handler(enum sys_off_mode mode,  	struct sys_off_handler *handler;  	int err; -	handler = kzalloc(sizeof(*handler), GFP_KERNEL); -	if (!handler) -		return ERR_PTR(-ENOMEM); +	handler = alloc_sys_off_handler(priority); +	if (IS_ERR(handler)) +		return handler;  	switch (mode) {  	case SYS_OFF_MODE_POWER_OFF_PREPARE: @@ -364,7 +401,7 @@ register_sys_off_handler(enum sys_off_mode mode,  		break;  	default: -		kfree(handler); +		free_sys_off_handler(handler);  		return ERR_PTR(-EINVAL);  	} @@ -391,7 +428,7 @@ register_sys_off_handler(enum sys_off_mode mode,  	}  	if (err) { -		kfree(handler); +		free_sys_off_handler(handler);  		return ERR_PTR(err);  	} @@ -409,7 +446,7 @@ void unregister_sys_off_handler(struct sys_off_handler *handler)  {  	int err; -	if (!handler) +	if (IS_ERR_OR_NULL(handler))  		return;  	if (handler->blocking) @@ -422,7 +459,7 @@ void unregister_sys_off_handler(struct sys_off_handler *handler)  	/* sanity check, shall never happen */  	WARN_ON(err); -	kfree(handler); +	free_sys_off_handler(handler);  }  EXPORT_SYMBOL_GPL(unregister_sys_off_handler); @@ -584,7 +621,23 @@ static void do_kernel_power_off_prepare(void)   */  void do_kernel_power_off(void)  { +	struct sys_off_handler *sys_off = NULL; + +	/* +	 * Register sys-off handlers for legacy PM callback. This allows +	 * legacy PM callbacks temporary co-exist with the new sys-off API. +	 * +	 * TODO: Remove legacy handlers once all legacy PM users will be +	 *       switched to the sys-off based APIs. +	 */ +	if (pm_power_off) +		sys_off = register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, +						   SYS_OFF_PRIO_DEFAULT, +						   legacy_pm_power_off, NULL); +  	atomic_notifier_call_chain(&power_off_handler_list, 0, NULL); + +	unregister_sys_off_handler(sys_off);  }  /** @@ -595,7 +648,8 @@ void do_kernel_power_off(void)   */  bool kernel_can_power_off(void)  { -	return !atomic_notifier_call_chain_is_empty(&power_off_handler_list); +	return !atomic_notifier_call_chain_is_empty(&power_off_handler_list) || +		pm_power_off;  }  EXPORT_SYMBOL_GPL(kernel_can_power_off); @@ -630,7 +684,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,  		void __user *, arg)  {  	struct pid_namespace *pid_ns = task_active_pid_ns(current); -	struct sys_off_handler *sys_off = NULL;  	char buffer[256];  	int ret = 0; @@ -655,21 +708,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,  	if (ret)  		return ret; -	/* -	 * Register sys-off handlers for legacy PM callback. This allows -	 * legacy PM callbacks temporary co-exist with the new sys-off API. -	 * -	 * TODO: Remove legacy handlers once all legacy PM users will be -	 *       switched to the sys-off based APIs. -	 */ -	if (pm_power_off) { -		sys_off = register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, -						   SYS_OFF_PRIO_DEFAULT, -						   legacy_pm_power_off, NULL); -		if (IS_ERR(sys_off)) -			return PTR_ERR(sys_off); -	} -  	/* Instead of trying to make the power_off code look like  	 * halt when pm_power_off is not set do it the easy way.  	 */ @@ -727,7 +765,6 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,  		break;  	}  	mutex_unlock(&system_transition_mutex); -	unregister_sys_off_handler(sys_off);  	return ret;  } @@ -782,11 +819,9 @@ static int __orderly_reboot(void)  	ret = run_cmd(reboot_cmd);  	if (ret) { -		printk_prefer_direct_enter();  		pr_warn("Failed to start orderly reboot: forcing the issue\n");  		emergency_sync();  		kernel_restart(NULL); -		printk_prefer_direct_exit();  	}  	return ret; @@ -799,7 +834,6 @@ static int __orderly_poweroff(bool force)  	ret = run_cmd(poweroff_cmd);  	if (ret && force) { -		printk_prefer_direct_enter();  		pr_warn("Failed to start orderly shutdown: forcing the issue\n");  		/* @@ -809,7 +843,6 @@ static int __orderly_poweroff(bool force)  		 */  		emergency_sync();  		kernel_power_off(); -		printk_prefer_direct_exit();  	}  	return ret; @@ -867,8 +900,6 @@ EXPORT_SYMBOL_GPL(orderly_reboot);   */  static void hw_failure_emergency_poweroff_func(struct work_struct *work)  { -	printk_prefer_direct_enter(); -  	/*  	 * We have reached here after the emergency shutdown waiting period has  	 * expired. This means orderly_poweroff has not been able to shut off @@ -885,8 +916,6 @@ static void hw_failure_emergency_poweroff_func(struct work_struct *work)  	 */  	pr_emerg("Hardware protection shutdown failed. Trying emergency restart\n");  	emergency_restart(); - -	printk_prefer_direct_exit();  }  static DECLARE_DELAYED_WORK(hw_failure_emergency_poweroff_work, @@ -925,13 +954,11 @@ void hw_protection_shutdown(const char *reason, int ms_until_forced)  {  	static atomic_t allow_proceed = ATOMIC_INIT(1); -	printk_prefer_direct_enter(); -  	pr_emerg("HARDWARE PROTECTION shutdown (%s)\n", reason);  	/* Shutdown should be initiated only once. */  	if (!atomic_dec_and_test(&allow_proceed)) -		goto out; +		return;  	/*  	 * Queue a backup emergency shutdown in the event of @@ -939,8 +966,6 @@ void hw_protection_shutdown(const char *reason, int ms_until_forced)  	 */  	hw_failure_emergency_poweroff(ms_until_forced);  	orderly_poweroff(true); -out: -	printk_prefer_direct_exit();  }  EXPORT_SYMBOL_GPL(hw_protection_shutdown); | 
