diff options
| author | Wim Van Sebroeck <wim@iguana.be> | 2007-05-11 19:03:13 +0000 | 
|---|---|---|
| committer | Wim Van Sebroeck <wim@iguana.be> | 2007-05-11 19:03:13 +0000 | 
| commit | 5c34202b8bf942da411b6599668a76b07449bbfd (patch) | |
| tree | 5719c361321eaddc8e4f1b0c8a7994f0e9a6fdd3 /kernel/power/disk.c | |
| parent | 0d4804b31f91cfbcff6d62af0bc09a893a1c8ae0 (diff) | |
| parent | 1f8a6b658a943b4f04a1fc7b3a420360202c86cd (diff) | |
Merge /pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'kernel/power/disk.c')
| -rw-r--r-- | kernel/power/disk.c | 244 | 
1 files changed, 159 insertions, 85 deletions
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 02e4fb69111..b5f0543ed84 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -30,30 +30,69 @@ char resume_file[256] = CONFIG_PM_STD_PARTITION;  dev_t swsusp_resume_device;  sector_t swsusp_resume_block; +enum { +	HIBERNATION_INVALID, +	HIBERNATION_PLATFORM, +	HIBERNATION_TEST, +	HIBERNATION_TESTPROC, +	HIBERNATION_SHUTDOWN, +	HIBERNATION_REBOOT, +	/* keep last */ +	__HIBERNATION_AFTER_LAST +}; +#define HIBERNATION_MAX (__HIBERNATION_AFTER_LAST-1) +#define HIBERNATION_FIRST (HIBERNATION_INVALID + 1) + +static int hibernation_mode = HIBERNATION_SHUTDOWN; + +struct hibernation_ops *hibernation_ops; + +/** + * hibernation_set_ops - set the global hibernate operations + * @ops: the hibernation operations to use in subsequent hibernation transitions + */ + +void hibernation_set_ops(struct hibernation_ops *ops) +{ +	if (ops && !(ops->prepare && ops->enter && ops->finish)) { +		WARN_ON(1); +		return; +	} +	mutex_lock(&pm_mutex); +	hibernation_ops = ops; +	if (ops) +		hibernation_mode = HIBERNATION_PLATFORM; +	else if (hibernation_mode == HIBERNATION_PLATFORM) +		hibernation_mode = HIBERNATION_SHUTDOWN; + +	mutex_unlock(&pm_mutex); +} + +  /**   *	platform_prepare - prepare the machine for hibernation using the   *	platform driver if so configured and return an error code if it fails   */ -static inline int platform_prepare(void) +static int platform_prepare(void)  { -	int error = 0; +	return (hibernation_mode == HIBERNATION_PLATFORM && hibernation_ops) ? +		hibernation_ops->prepare() : 0; +} -	switch (pm_disk_mode) { -	case PM_DISK_TEST: -	case PM_DISK_TESTPROC: -	case PM_DISK_SHUTDOWN: -	case PM_DISK_REBOOT: -		break; -	default: -		if (pm_ops && pm_ops->prepare) -			error = pm_ops->prepare(PM_SUSPEND_DISK); -	} -	return error; +/** + *	platform_finish - switch the machine to the normal mode of operation + *	using the platform driver (must be called after platform_prepare()) + */ + +static void platform_finish(void) +{ +	if (hibernation_mode == HIBERNATION_PLATFORM && hibernation_ops) +		hibernation_ops->finish();  }  /** - *	power_down - Shut machine down for hibernate. + *	power_down - Shut the machine down for hibernation.   *   *	Use the platform driver, if configured so; otherwise try   *	to power off or reboot. @@ -61,20 +100,20 @@ static inline int platform_prepare(void)  static void power_down(void)  { -	switch (pm_disk_mode) { -	case PM_DISK_TEST: -	case PM_DISK_TESTPROC: +	switch (hibernation_mode) { +	case HIBERNATION_TEST: +	case HIBERNATION_TESTPROC:  		break; -	case PM_DISK_SHUTDOWN: +	case HIBERNATION_SHUTDOWN:  		kernel_power_off();  		break; -	case PM_DISK_REBOOT: +	case HIBERNATION_REBOOT:  		kernel_restart(NULL);  		break; -	default: -		if (pm_ops && pm_ops->enter) { +	case HIBERNATION_PLATFORM: +		if (hibernation_ops) {  			kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); -			pm_ops->enter(PM_SUSPEND_DISK); +			hibernation_ops->enter();  			break;  		}  	} @@ -87,20 +126,6 @@ static void power_down(void)  	while(1);  } -static inline void platform_finish(void) -{ -	switch (pm_disk_mode) { -	case PM_DISK_TEST: -	case PM_DISK_TESTPROC: -	case PM_DISK_SHUTDOWN: -	case PM_DISK_REBOOT: -		break; -	default: -		if (pm_ops && pm_ops->finish) -			pm_ops->finish(PM_SUSPEND_DISK); -	} -} -  static void unprepare_processes(void)  {  	thaw_processes(); @@ -120,25 +145,33 @@ static int prepare_processes(void)  }  /** - *	pm_suspend_disk - The granpappy of hibernation power management. - * - *	If not, then call swsusp to do its thing, then figure out how - *	to power down the system. + *	hibernate - The granpappy of the built-in hibernation management   */ -int pm_suspend_disk(void) +int hibernate(void)  {  	int error; +	/* The snapshot device should not be opened while we're running */ +	if (!atomic_add_unless(&snapshot_device_available, -1, 0)) +		return -EBUSY; + +	/* Allocate memory management structures */ +	error = create_basic_memory_bitmaps(); +	if (error) +		goto Exit; +  	error = prepare_processes();  	if (error) -		return error; +		goto Finish; -	if (pm_disk_mode == PM_DISK_TESTPROC) { +	mutex_lock(&pm_mutex); +	if (hibernation_mode == HIBERNATION_TESTPROC) {  		printk("swsusp debug: Waiting for 5 seconds.\n");  		mdelay(5000);  		goto Thaw;  	} +  	/* Free memory before shutting down devices. */  	error = swsusp_shrink_memory();  	if (error) @@ -158,7 +191,7 @@ int pm_suspend_disk(void)  	if (error)  		goto Enable_cpus; -	if (pm_disk_mode == PM_DISK_TEST) { +	if (hibernation_mode == HIBERNATION_TEST) {  		printk("swsusp debug: Waiting for 5 seconds.\n");  		mdelay(5000);  		goto Enable_cpus; @@ -195,7 +228,12 @@ int pm_suspend_disk(void)  	device_resume();  	resume_console();   Thaw: +	mutex_unlock(&pm_mutex);  	unprepare_processes(); + Finish: +	free_basic_memory_bitmaps(); + Exit: +	atomic_inc(&snapshot_device_available);  	return error;  } @@ -206,7 +244,7 @@ int pm_suspend_disk(void)   *	Called as a late_initcall (so all devices are discovered and   *	initialized), we call swsusp to see if we have a saved image or not.   *	If so, we quiesce devices, the restore the saved image. We will - *	return above (in pm_suspend_disk() ) if everything goes well. + *	return above (in hibernate() ) if everything goes well.   *	Otherwise, we fail gracefully and return to the normally   *	scheduled program.   * @@ -239,13 +277,21 @@ static int software_resume(void)  	}  	pr_debug("PM: Checking swsusp image.\n"); -  	error = swsusp_check();  	if (error) -		goto Done; +		goto Unlock; -	pr_debug("PM: Preparing processes for restore.\n"); +	/* The snapshot device should not be opened while we're running */ +	if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { +		error = -EBUSY; +		goto Unlock; +	} + +	error = create_basic_memory_bitmaps(); +	if (error) +		goto Finish; +	pr_debug("PM: Preparing processes for restore.\n");  	error = prepare_processes();  	if (error) {  		swsusp_close(); @@ -280,7 +326,11 @@ static int software_resume(void)  	printk(KERN_ERR "PM: Restore failed, recovering.\n");  	unprepare_processes();   Done: +	free_basic_memory_bitmaps(); + Finish: +	atomic_inc(&snapshot_device_available);  	/* For success case, the suspend path will release the lock */ + Unlock:  	mutex_unlock(&pm_mutex);  	pr_debug("PM: Resume from disk failed.\n");  	return 0; @@ -289,25 +339,26 @@ static int software_resume(void)  late_initcall(software_resume); -static const char * const pm_disk_modes[] = { -	[PM_DISK_PLATFORM]	= "platform", -	[PM_DISK_SHUTDOWN]	= "shutdown", -	[PM_DISK_REBOOT]	= "reboot", -	[PM_DISK_TEST]		= "test", -	[PM_DISK_TESTPROC]	= "testproc", +static const char * const hibernation_modes[] = { +	[HIBERNATION_PLATFORM]	= "platform", +	[HIBERNATION_SHUTDOWN]	= "shutdown", +	[HIBERNATION_REBOOT]	= "reboot", +	[HIBERNATION_TEST]	= "test", +	[HIBERNATION_TESTPROC]	= "testproc",  };  /** - *	disk - Control suspend-to-disk mode + *	disk - Control hibernation mode   *   *	Suspend-to-disk can be handled in several ways. We have a few options   *	for putting the system to sleep - using the platform driver (e.g. ACPI - *	or other pm_ops), powering off the system or rebooting the system - *	(for testing) as well as the two test modes. + *	or other hibernation_ops), powering off the system or rebooting the + *	system (for testing) as well as the two test modes.   *   *	The system can support 'platform', and that is known a priori (and - *	encoded in pm_ops). However, the user may choose 'shutdown' or 'reboot' - *	as alternatives, as well as the test modes 'test' and 'testproc'. + *	encoded by the presence of hibernation_ops). However, the user may + *	choose 'shutdown' or 'reboot' as alternatives, as well as one fo the + *	test modes, 'test' or 'testproc'.   *   *	show() will display what the mode is currently set to.   *	store() will accept one of @@ -319,67 +370,90 @@ static const char * const pm_disk_modes[] = {   *	'testproc'   *   *	It will only change to 'platform' if the system - *	supports it (as determined from pm_ops->pm_disk_mode). + *	supports it (as determined by having hibernation_ops).   */ -static ssize_t disk_show(struct subsystem * subsys, char * buf) +static ssize_t disk_show(struct kset *kset, char *buf)  { -	return sprintf(buf, "%s\n", pm_disk_modes[pm_disk_mode]); +	int i; +	char *start = buf; + +	for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { +		if (!hibernation_modes[i]) +			continue; +		switch (i) { +		case HIBERNATION_SHUTDOWN: +		case HIBERNATION_REBOOT: +		case HIBERNATION_TEST: +		case HIBERNATION_TESTPROC: +			break; +		case HIBERNATION_PLATFORM: +			if (hibernation_ops) +				break; +			/* not a valid mode, continue with loop */ +			continue; +		} +		if (i == hibernation_mode) +			buf += sprintf(buf, "[%s] ", hibernation_modes[i]); +		else +			buf += sprintf(buf, "%s ", hibernation_modes[i]); +	} +	buf += sprintf(buf, "\n"); +	return buf-start;  } -static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n) +static ssize_t disk_store(struct kset *kset, const char *buf, size_t n)  {  	int error = 0;  	int i;  	int len;  	char *p; -	suspend_disk_method_t mode = 0; +	int mode = HIBERNATION_INVALID;  	p = memchr(buf, '\n', n);  	len = p ? p - buf : n;  	mutex_lock(&pm_mutex); -	for (i = PM_DISK_PLATFORM; i < PM_DISK_MAX; i++) { -		if (!strncmp(buf, pm_disk_modes[i], len)) { +	for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { +		if (!strncmp(buf, hibernation_modes[i], len)) {  			mode = i;  			break;  		}  	} -	if (mode) { +	if (mode != HIBERNATION_INVALID) {  		switch (mode) { -		case PM_DISK_SHUTDOWN: -		case PM_DISK_REBOOT: -		case PM_DISK_TEST: -		case PM_DISK_TESTPROC: -			pm_disk_mode = mode; +		case HIBERNATION_SHUTDOWN: +		case HIBERNATION_REBOOT: +		case HIBERNATION_TEST: +		case HIBERNATION_TESTPROC: +			hibernation_mode = mode;  			break; -		default: -			if (pm_ops && pm_ops->enter && -			    (mode == pm_ops->pm_disk_mode)) -				pm_disk_mode = mode; +		case HIBERNATION_PLATFORM: +			if (hibernation_ops) +				hibernation_mode = mode;  			else  				error = -EINVAL;  		} -	} else { +	} else  		error = -EINVAL; -	} -	pr_debug("PM: suspend-to-disk mode set to '%s'\n", -		 pm_disk_modes[mode]); +	if (!error) +		pr_debug("PM: suspend-to-disk mode set to '%s'\n", +			 hibernation_modes[mode]);  	mutex_unlock(&pm_mutex);  	return error ? error : n;  }  power_attr(disk); -static ssize_t resume_show(struct subsystem * subsys, char *buf) +static ssize_t resume_show(struct kset *kset, char *buf)  {  	return sprintf(buf,"%d:%d\n", MAJOR(swsusp_resume_device),  		       MINOR(swsusp_resume_device));  } -static ssize_t resume_store(struct subsystem *subsys, const char *buf, size_t n) +static ssize_t resume_store(struct kset *kset, const char *buf, size_t n)  {  	unsigned int maj, min;  	dev_t res; @@ -405,12 +479,12 @@ static ssize_t resume_store(struct subsystem *subsys, const char *buf, size_t n)  power_attr(resume); -static ssize_t image_size_show(struct subsystem * subsys, char *buf) +static ssize_t image_size_show(struct kset *kset, char *buf)  {  	return sprintf(buf, "%lu\n", image_size);  } -static ssize_t image_size_store(struct subsystem * subsys, const char * buf, size_t n) +static ssize_t image_size_store(struct kset *kset, const char *buf, size_t n)  {  	unsigned long size; @@ -439,7 +513,7 @@ static struct attribute_group attr_group = {  static int __init pm_disk_init(void)  { -	return sysfs_create_group(&power_subsys.kset.kobj,&attr_group); +	return sysfs_create_group(&power_subsys.kobj, &attr_group);  }  core_initcall(pm_disk_init);  | 
