diff options
Diffstat (limited to 'kernel/power')
| -rw-r--r-- | kernel/power/hibernate.c | 47 | ||||
| -rw-r--r-- | kernel/power/main.c | 20 | ||||
| -rw-r--r-- | kernel/power/power.h | 7 | ||||
| -rw-r--r-- | kernel/power/process.c | 24 | ||||
| -rw-r--r-- | kernel/power/snapshot.c | 7 | ||||
| -rw-r--r-- | kernel/power/suspend.c | 84 | ||||
| -rw-r--r-- | kernel/power/user.c | 12 | 
7 files changed, 96 insertions, 105 deletions
diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 6d6d28870335..0a186cfde788 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -245,8 +245,8 @@ void swsusp_show_speed(struct timeval *start, struct timeval *stop,   * create_image - Create a hibernation image.   * @platform_mode: Whether or not to use the platform driver.   * - * Execute device drivers' .freeze_noirq() callbacks, create a hibernation image - * and execute the drivers' .thaw_noirq() callbacks. + * Execute device drivers' "late" and "noirq" freeze callbacks, create a + * hibernation image and run the drivers' "noirq" and "early" thaw callbacks.   *   * Control reappears in this routine after the subsequent restore.   */ @@ -254,7 +254,7 @@ static int create_image(int platform_mode)  {  	int error; -	error = dpm_suspend_noirq(PMSG_FREEZE); +	error = dpm_suspend_end(PMSG_FREEZE);  	if (error) {  		printk(KERN_ERR "PM: Some devices failed to power down, "  			"aborting hibernation\n"); @@ -306,7 +306,7 @@ static int create_image(int platform_mode)   Platform_finish:  	platform_finish(platform_mode); -	dpm_resume_noirq(in_suspend ? +	dpm_resume_start(in_suspend ?  		(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);  	return error; @@ -343,13 +343,13 @@ int hibernation_snapshot(int platform_mode)  		 * successful freezer test.  		 */  		freezer_test_done = true; -		goto Cleanup; +		goto Thaw;  	}  	error = dpm_prepare(PMSG_FREEZE);  	if (error) {  		dpm_complete(PMSG_RECOVER); -		goto Cleanup; +		goto Thaw;  	}  	suspend_console(); @@ -385,6 +385,8 @@ int hibernation_snapshot(int platform_mode)  	platform_end(platform_mode);  	return error; + Thaw: +	thaw_kernel_threads();   Cleanup:  	swsusp_free();  	goto Close; @@ -394,16 +396,16 @@ int hibernation_snapshot(int platform_mode)   * resume_target_kernel - Restore system state from a hibernation image.   * @platform_mode: Whether or not to use the platform driver.   * - * Execute device drivers' .freeze_noirq() callbacks, restore the contents of - * highmem that have not been restored yet from the image and run the low-level - * code that will restore the remaining contents of memory and switch to the - * just restored target kernel. + * Execute device drivers' "noirq" and "late" freeze callbacks, restore the + * contents of highmem that have not been restored yet from the image and run + * the low-level code that will restore the remaining contents of memory and + * switch to the just restored target kernel.   */  static int resume_target_kernel(bool platform_mode)  {  	int error; -	error = dpm_suspend_noirq(PMSG_QUIESCE); +	error = dpm_suspend_end(PMSG_QUIESCE);  	if (error) {  		printk(KERN_ERR "PM: Some devices failed to power down, "  			"aborting resume\n"); @@ -460,7 +462,7 @@ static int resume_target_kernel(bool platform_mode)   Cleanup:  	platform_restore_cleanup(platform_mode); -	dpm_resume_noirq(PMSG_RECOVER); +	dpm_resume_start(PMSG_RECOVER);  	return error;  } @@ -518,7 +520,7 @@ int hibernation_platform_enter(void)  		goto Resume_devices;  	} -	error = dpm_suspend_noirq(PMSG_HIBERNATE); +	error = dpm_suspend_end(PMSG_HIBERNATE);  	if (error)  		goto Resume_devices; @@ -549,7 +551,7 @@ int hibernation_platform_enter(void)   Platform_finish:  	hibernation_ops->finish(); -	dpm_resume_noirq(PMSG_RESTORE); +	dpm_resume_start(PMSG_RESTORE);   Resume_devices:  	entering_platform_hibernation = false; @@ -616,7 +618,7 @@ int hibernate(void)  	/* Allocate memory management structures */  	error = create_basic_memory_bitmaps();  	if (error) -		goto Exit; +		goto Enable_umh;  	printk(KERN_INFO "PM: Syncing filesystems ... ");  	sys_sync(); @@ -624,15 +626,11 @@ int hibernate(void)  	error = freeze_processes();  	if (error) -		goto Finish; +		goto Free_bitmaps;  	error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM); -	if (error) -		goto Thaw; -	if (freezer_test_done) { -		freezer_test_done = false; +	if (error || freezer_test_done)  		goto Thaw; -	}  	if (in_suspend) {  		unsigned int flags = 0; @@ -657,8 +655,13 @@ int hibernate(void)   Thaw:  	thaw_processes(); - Finish: + +	/* Don't bother checking whether freezer_test_done is true */ +	freezer_test_done = false; + + Free_bitmaps:  	free_basic_memory_bitmaps(); + Enable_umh:  	usermodehelper_enable();   Exit:  	pm_notifier_call_chain(PM_POST_HIBERNATION); diff --git a/kernel/power/main.c b/kernel/power/main.c index 9824b41e5a18..1c12581f1c62 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -165,16 +165,20 @@ static int suspend_stats_show(struct seq_file *s, void *unused)  	last_errno %= REC_FAILED_NUM;  	last_step = suspend_stats.last_failed_step + REC_FAILED_NUM - 1;  	last_step %= REC_FAILED_NUM; -	seq_printf(s, "%s: %d\n%s: %d\n%s: %d\n%s: %d\n" -			"%s: %d\n%s: %d\n%s: %d\n%s: %d\n", +	seq_printf(s, "%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n" +			"%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n",  			"success", suspend_stats.success,  			"fail", suspend_stats.fail,  			"failed_freeze", suspend_stats.failed_freeze,  			"failed_prepare", suspend_stats.failed_prepare,  			"failed_suspend", suspend_stats.failed_suspend, +			"failed_suspend_late", +				suspend_stats.failed_suspend_late,  			"failed_suspend_noirq",  				suspend_stats.failed_suspend_noirq,  			"failed_resume", suspend_stats.failed_resume, +			"failed_resume_early", +				suspend_stats.failed_resume_early,  			"failed_resume_noirq",  				suspend_stats.failed_resume_noirq);  	seq_printf(s,	"failures:\n  last_failed_dev:\t%-s\n", @@ -287,16 +291,10 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,  #ifdef CONFIG_SUSPEND  	for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) { -		if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) +		if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) { +			error = pm_suspend(state);  			break; -	} -	if (state < PM_SUSPEND_MAX && *s) { -		error = enter_state(state); -		if (error) { -			suspend_stats.fail++; -			dpm_save_failed_errno(error); -		} else -			suspend_stats.success++; +		}  	}  #endif diff --git a/kernel/power/power.h b/kernel/power/power.h index 21724eee5206..98f3622d7407 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -177,13 +177,11 @@ extern const char *const pm_states[];  extern bool valid_state(suspend_state_t state);  extern int suspend_devices_and_enter(suspend_state_t state); -extern int enter_state(suspend_state_t state);  #else /* !CONFIG_SUSPEND */  static inline int suspend_devices_and_enter(suspend_state_t state)  {  	return -ENOSYS;  } -static inline int enter_state(suspend_state_t state) { return -ENOSYS; }  static inline bool valid_state(suspend_state_t state) { return false; }  #endif /* !CONFIG_SUSPEND */ @@ -234,16 +232,14 @@ static inline int suspend_freeze_processes(void)  	int error;  	error = freeze_processes(); -  	/*  	 * freeze_processes() automatically thaws every task if freezing  	 * fails. So we need not do anything extra upon error.  	 */  	if (error) -		goto Finish; +		return error;  	error = freeze_kernel_threads(); -  	/*  	 * freeze_kernel_threads() thaws only kernel threads upon freezing  	 * failure. So we have to thaw the userspace tasks ourselves. @@ -251,7 +247,6 @@ static inline int suspend_freeze_processes(void)  	if (error)  		thaw_processes(); - Finish:  	return error;  } diff --git a/kernel/power/process.c b/kernel/power/process.c index 7e426459e60a..0d2aeb226108 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -53,11 +53,9 @@ static int try_to_freeze_tasks(bool user_only)  			 * It is "frozen enough".  If the task does wake  			 * up, it will immediately call try_to_freeze.  			 * -			 * Because freeze_task() goes through p's -			 * scheduler lock after setting TIF_FREEZE, it's -			 * guaranteed that either we see TASK_RUNNING or -			 * try_to_stop() after schedule() in ptrace/signal -			 * stop sees TIF_FREEZE. +			 * Because freeze_task() goes through p's scheduler lock, it's +			 * guaranteed that TASK_STOPPED/TRACED -> TASK_RUNNING +			 * transition can't race with task state testing here.  			 */  			if (!task_is_stopped_or_traced(p) &&  			    !freezer_should_skip(p)) @@ -98,13 +96,15 @@ static int try_to_freeze_tasks(bool user_only)  		       elapsed_csecs / 100, elapsed_csecs % 100,  		       todo - wq_busy, wq_busy); -		read_lock(&tasklist_lock); -		do_each_thread(g, p) { -			if (!wakeup && !freezer_should_skip(p) && -			    p != current && freezing(p) && !frozen(p)) -				sched_show_task(p); -		} while_each_thread(g, p); -		read_unlock(&tasklist_lock); +		if (!wakeup) { +			read_lock(&tasklist_lock); +			do_each_thread(g, p) { +				if (p != current && !freezer_should_skip(p) +				    && freezing(p) && !frozen(p)) +					sched_show_task(p); +			} while_each_thread(g, p); +			read_unlock(&tasklist_lock); +		}  	} else {  		printk("(elapsed %d.%02d seconds) ", elapsed_csecs / 100,  			elapsed_csecs % 100); diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 6a768e537001..8e2e7461375f 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -711,9 +711,10 @@ static void mark_nosave_pages(struct memory_bitmap *bm)  	list_for_each_entry(region, &nosave_regions, list) {  		unsigned long pfn; -		pr_debug("PM: Marking nosave pages: %016lx - %016lx\n", -				region->start_pfn << PAGE_SHIFT, -				region->end_pfn << PAGE_SHIFT); +		pr_debug("PM: Marking nosave pages: [mem %#010llx-%#010llx]\n", +			 (unsigned long long) region->start_pfn << PAGE_SHIFT, +			 ((unsigned long long) region->end_pfn << PAGE_SHIFT) +				- 1);  		for (pfn = region->start_pfn; pfn < region->end_pfn; pfn++)  			if (pfn_valid(pfn)) { diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 4fd51beed879..88e5c967370d 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -37,8 +37,8 @@ const char *const pm_states[PM_SUSPEND_MAX] = {  static const struct platform_suspend_ops *suspend_ops;  /** - *	suspend_set_ops - Set the global suspend method table. - *	@ops:	Pointer to ops structure. + * suspend_set_ops - Set the global suspend method table. + * @ops: Suspend operations to use.   */  void suspend_set_ops(const struct platform_suspend_ops *ops)  { @@ -58,11 +58,11 @@ bool valid_state(suspend_state_t state)  }  /** - * suspend_valid_only_mem - generic memory-only valid callback + * suspend_valid_only_mem - Generic memory-only valid callback.   * - * Platform drivers that implement mem suspend only and only need - * to check for that in their .valid callback can use this instead - * of rolling their own .valid callback. + * Platform drivers that implement mem suspend only and only need to check for + * that in their .valid() callback can use this instead of rolling their own + * .valid() callback.   */  int suspend_valid_only_mem(suspend_state_t state)  { @@ -83,10 +83,11 @@ static int suspend_test(int level)  }  /** - *	suspend_prepare - Do prep work before entering low-power state. + * suspend_prepare - Prepare for entering system sleep state.   * - *	This is common code that is called for each state that we're entering. - *	Run suspend notifiers, allocate a console and stop all processes. + * Common code run for every system sleep state that can be entered (except for + * hibernation).  Run suspend notifiers, allocate the "suspend" console and + * freeze processes.   */  static int suspend_prepare(void)  { @@ -131,9 +132,9 @@ void __attribute__ ((weak)) arch_suspend_enable_irqs(void)  }  /** - * suspend_enter - enter the desired system sleep state. - * @state: State to enter - * @wakeup: Returns information that suspend should not be entered again. + * suspend_enter - Make the system enter the given sleep state. + * @state: System sleep state to enter. + * @wakeup: Returns information that the sleep state should not be re-entered.   *   * This function should be called after devices have been suspended.   */ @@ -147,7 +148,7 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)  			goto Platform_finish;  	} -	error = dpm_suspend_noirq(PMSG_SUSPEND); +	error = dpm_suspend_end(PMSG_SUSPEND);  	if (error) {  		printk(KERN_ERR "PM: Some devices failed to power down\n");  		goto Platform_finish; @@ -189,7 +190,7 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)  	if (suspend_ops->wake)  		suspend_ops->wake(); -	dpm_resume_noirq(PMSG_RESUME); +	dpm_resume_start(PMSG_RESUME);   Platform_finish:  	if (suspend_ops->finish) @@ -199,9 +200,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)  }  /** - *	suspend_devices_and_enter - suspend devices and enter the desired system - *				    sleep state. - *	@state:		  state to enter + * suspend_devices_and_enter - Suspend devices and enter system sleep state. + * @state: System sleep state to enter.   */  int suspend_devices_and_enter(suspend_state_t state)  { @@ -251,10 +251,10 @@ int suspend_devices_and_enter(suspend_state_t state)  }  /** - *	suspend_finish - Do final work before exiting suspend sequence. + * suspend_finish - Clean up before finishing the suspend sequence.   * - *	Call platform code to clean up, restart processes, and free the - *	console that we've allocated. This is not called for suspend-to-disk. + * Call platform code to clean up, restart processes, and free the console that + * we've allocated. This routine is not called for hibernation.   */  static void suspend_finish(void)  { @@ -265,16 +265,14 @@ static void suspend_finish(void)  }  /** - *	enter_state - Do common work of entering low-power state. - *	@state:		pm_state structure for state we're entering. + * enter_state - Do common work needed to enter system sleep state. + * @state: System sleep state to enter.   * - *	Make sure we're the only ones trying to enter a sleep state. Fail - *	if someone has beat us to it, since we don't want anything weird to - *	happen when we wake up. - *	Then, do the setup for suspend, enter the state, and cleaup (after - *	we've woken up). + * Make sure that no one else is trying to put the system into a sleep state. + * Fail if that's not the case.  Otherwise, prepare for system suspend, make the + * system enter the given sleep state and clean up after wakeup.   */ -int enter_state(suspend_state_t state) +static int enter_state(suspend_state_t state)  {  	int error; @@ -310,24 +308,26 @@ int enter_state(suspend_state_t state)  }  /** - *	pm_suspend - Externally visible function for suspending system. - *	@state:		Enumerated value of state to enter. + * pm_suspend - Externally visible function for suspending the system. + * @state: System sleep state to enter.   * - *	Determine whether or not value is within range, get state - *	structure, and enter (above). + * Check if the value of @state represents one of the supported states, + * execute enter_state() and update system suspend statistics.   */  int pm_suspend(suspend_state_t state)  { -	int ret; -	if (state > PM_SUSPEND_ON && state < PM_SUSPEND_MAX) { -		ret = enter_state(state); -		if (ret) { -			suspend_stats.fail++; -			dpm_save_failed_errno(ret); -		} else -			suspend_stats.success++; -		return ret; +	int error; + +	if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX) +		return -EINVAL; + +	error = enter_state(state); +	if (error) { +		suspend_stats.fail++; +		dpm_save_failed_errno(error); +	} else { +		suspend_stats.success++;  	} -	return -EINVAL; +	return error;  }  EXPORT_SYMBOL(pm_suspend); diff --git a/kernel/power/user.c b/kernel/power/user.c index 3e100075b13c..33c4329205af 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c @@ -249,16 +249,10 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,  		}  		pm_restore_gfp_mask();  		error = hibernation_snapshot(data->platform_support); -		if (error) { -			thaw_kernel_threads(); -		} else { +		if (!error) {  			error = put_user(in_suspend, (int __user *)arg); -			if (!error && !freezer_test_done) -				data->ready = 1; -			if (freezer_test_done) { -				freezer_test_done = false; -				thaw_kernel_threads(); -			} +			data->ready = !freezer_test_done && !error; +			freezer_test_done = false;  		}  		break;  | 
