diff options
Diffstat (limited to 'kernel/power/user.c')
| -rw-r--r-- | kernel/power/user.c | 247 | 
1 files changed, 123 insertions, 124 deletions
diff --git a/kernel/power/user.c b/kernel/power/user.c index e819e17877c..526e8911460 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c @@ -20,37 +20,15 @@  #include <linux/swapops.h>  #include <linux/pm.h>  #include <linux/fs.h> +#include <linux/compat.h>  #include <linux/console.h>  #include <linux/cpu.h>  #include <linux/freezer.h> -#include <scsi/scsi_scan.h>  #include <asm/uaccess.h>  #include "power.h" -/* - * NOTE: The SNAPSHOT_SET_SWAP_FILE and SNAPSHOT_PMOPS ioctls are obsolete and - * will be removed in the future.  They are only preserved here for - * compatibility with existing userland utilities. - */ -#define SNAPSHOT_SET_SWAP_FILE	_IOW(SNAPSHOT_IOC_MAGIC, 10, unsigned int) -#define SNAPSHOT_PMOPS		_IOW(SNAPSHOT_IOC_MAGIC, 12, unsigned int) - -#define PMOPS_PREPARE	1 -#define PMOPS_ENTER	2 -#define PMOPS_FINISH	3 - -/* - * NOTE: The following ioctl definitions are wrong and have been replaced with - * correct ones.  They are only preserved here for compatibility with existing - * userland utilities and will be removed in the future. - */ -#define SNAPSHOT_ATOMIC_SNAPSHOT	_IOW(SNAPSHOT_IOC_MAGIC, 3, void *) -#define SNAPSHOT_SET_IMAGE_SIZE		_IOW(SNAPSHOT_IOC_MAGIC, 6, unsigned long) -#define SNAPSHOT_AVAIL_SWAP		_IOR(SNAPSHOT_IOC_MAGIC, 7, void *) -#define SNAPSHOT_GET_SWAP_PAGE		_IOR(SNAPSHOT_IOC_MAGIC, 8, void *) -  #define SNAPSHOT_MINOR	231 @@ -58,9 +36,10 @@ static struct snapshot_data {  	struct snapshot_handle handle;  	int swap;  	int mode; -	char frozen; -	char ready; -	char platform_support; +	bool frozen; +	bool ready; +	bool platform_support; +	bool free_bitmaps;  } snapshot_state;  atomic_t snapshot_device_available = ATOMIC_INIT(1); @@ -70,7 +49,10 @@ static int snapshot_open(struct inode *inode, struct file *filp)  	struct snapshot_data *data;  	int error; -	mutex_lock(&pm_mutex); +	if (!hibernation_available()) +		return -EPERM; + +	lock_system_sleep();  	if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {  		error = -EBUSY; @@ -82,11 +64,6 @@ static int snapshot_open(struct inode *inode, struct file *filp)  		error = -ENOSYS;  		goto Unlock;  	} -	if(create_basic_memory_bitmaps()) { -		atomic_inc(&snapshot_device_available); -		error = -ENOMEM; -		goto Unlock; -	}  	nonseekable_open(inode, filp);  	data = &snapshot_state;  	filp->private_data = data; @@ -96,6 +73,7 @@ static int snapshot_open(struct inode *inode, struct file *filp)  		data->swap = swsusp_resume_device ?  			swap_type_of(swsusp_resume_device, 0, NULL) : -1;  		data->mode = O_RDONLY; +		data->free_bitmaps = false;  		error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE);  		if (error)  			pm_notifier_call_chain(PM_POST_HIBERNATION); @@ -105,22 +83,26 @@ static int snapshot_open(struct inode *inode, struct file *filp)  		 * appear.  		 */  		wait_for_device_probe(); -		scsi_complete_async_scans();  		data->swap = -1;  		data->mode = O_WRONLY;  		error = pm_notifier_call_chain(PM_RESTORE_PREPARE); +		if (!error) { +			error = create_basic_memory_bitmaps(); +			data->free_bitmaps = !error; +		}  		if (error)  			pm_notifier_call_chain(PM_POST_RESTORE);  	}  	if (error)  		atomic_inc(&snapshot_device_available); -	data->frozen = 0; -	data->ready = 0; -	data->platform_support = 0; + +	data->frozen = false; +	data->ready = false; +	data->platform_support = false;   Unlock: -	mutex_unlock(&pm_mutex); +	unlock_system_sleep();  	return error;  } @@ -129,19 +111,23 @@ static int snapshot_release(struct inode *inode, struct file *filp)  {  	struct snapshot_data *data; -	mutex_lock(&pm_mutex); +	lock_system_sleep();  	swsusp_free(); -	free_basic_memory_bitmaps();  	data = filp->private_data;  	free_all_swap_pages(data->swap); -	if (data->frozen) +	if (data->frozen) { +		pm_restore_gfp_mask(); +		free_basic_memory_bitmaps();  		thaw_processes(); -	pm_notifier_call_chain(data->mode == O_WRONLY ? +	} else if (data->free_bitmaps) { +		free_basic_memory_bitmaps(); +	} +	pm_notifier_call_chain(data->mode == O_RDONLY ?  			PM_POST_HIBERNATION : PM_POST_RESTORE);  	atomic_inc(&snapshot_device_available); -	mutex_unlock(&pm_mutex); +	unlock_system_sleep();  	return 0;  } @@ -153,7 +139,7 @@ static ssize_t snapshot_read(struct file *filp, char __user *buf,  	ssize_t res;  	loff_t pg_offp = *offp & ~PAGE_MASK; -	mutex_lock(&pm_mutex); +	lock_system_sleep();  	data = filp->private_data;  	if (!data->ready) { @@ -174,7 +160,7 @@ static ssize_t snapshot_read(struct file *filp, char __user *buf,  		*offp += res;   Unlock: -	mutex_unlock(&pm_mutex); +	unlock_system_sleep();  	return res;  } @@ -186,7 +172,7 @@ static ssize_t snapshot_write(struct file *filp, const char __user *buf,  	ssize_t res;  	loff_t pg_offp = *offp & ~PAGE_MASK; -	mutex_lock(&pm_mutex); +	lock_system_sleep();  	data = filp->private_data; @@ -203,20 +189,11 @@ static ssize_t snapshot_write(struct file *filp, const char __user *buf,  	if (res > 0)  		*offp += res;  unlock: -	mutex_unlock(&pm_mutex); +	unlock_system_sleep();  	return res;  } -static void snapshot_deprecated_ioctl(unsigned int cmd) -{ -	if (printk_ratelimit()) -		printk(KERN_NOTICE "%pf: ioctl '%.8x' is deprecated and will " -				"be removed soon, update your suspend-to-disk " -				"utilities\n", -				__builtin_return_address(0), cmd); -} -  static long snapshot_ioctl(struct file *filp, unsigned int cmd,  							unsigned long arg)  { @@ -235,6 +212,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,  	if (!mutex_trylock(&pm_mutex))  		return -EBUSY; +	lock_device_hotplug();  	data = filp->private_data;  	switch (cmd) { @@ -247,39 +225,40 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,  		sys_sync();  		printk("done.\n"); -		error = usermodehelper_disable(); +		error = freeze_processes();  		if (error)  			break; -		error = freeze_processes(); -		if (error) { +		error = create_basic_memory_bitmaps(); +		if (error)  			thaw_processes(); -			usermodehelper_enable(); -		} -		if (!error) -			data->frozen = 1; +		else +			data->frozen = true; +  		break;  	case SNAPSHOT_UNFREEZE:  		if (!data->frozen || data->ready)  			break; +		pm_restore_gfp_mask(); +		free_basic_memory_bitmaps(); +		data->free_bitmaps = false;  		thaw_processes(); -		usermodehelper_enable(); -		data->frozen = 0; +		data->frozen = false;  		break; -	case SNAPSHOT_ATOMIC_SNAPSHOT: -		snapshot_deprecated_ioctl(cmd);  	case SNAPSHOT_CREATE_IMAGE:  		if (data->mode != O_RDONLY || !data->frozen  || data->ready) {  			error = -EPERM;  			break;  		} +		pm_restore_gfp_mask();  		error = hibernation_snapshot(data->platform_support); -		if (!error) +		if (!error) {  			error = put_user(in_suspend, (int __user *)arg); -		if (!error) -			data->ready = 1; +			data->ready = !freezer_test_done && !error; +			freezer_test_done = false; +		}  		break;  	case SNAPSHOT_ATOMIC_RESTORE: @@ -295,11 +274,18 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,  	case SNAPSHOT_FREE:  		swsusp_free();  		memset(&data->handle, 0, sizeof(struct snapshot_handle)); -		data->ready = 0; +		data->ready = false; +		/* +		 * It is necessary to thaw kernel threads here, because +		 * SNAPSHOT_CREATE_IMAGE may be invoked directly after +		 * SNAPSHOT_FREE.  In that case, if kernel threads were not +		 * thawed, the preallocation of memory carried out by +		 * hibernation_snapshot() might run into problems (i.e. it +		 * might fail or even deadlock). +		 */ +		thaw_kernel_threads();  		break; -	case SNAPSHOT_SET_IMAGE_SIZE: -		snapshot_deprecated_ioctl(cmd);  	case SNAPSHOT_PREF_IMAGE_SIZE:  		image_size = arg;  		break; @@ -314,16 +300,12 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,  		error = put_user(size, (loff_t __user *)arg);  		break; -	case SNAPSHOT_AVAIL_SWAP: -		snapshot_deprecated_ioctl(cmd);  	case SNAPSHOT_AVAIL_SWAP_SIZE:  		size = count_swap_pages(data->swap, 1);  		size <<= PAGE_SHIFT;  		error = put_user(size, (loff_t __user *)arg);  		break; -	case SNAPSHOT_GET_SWAP_PAGE: -		snapshot_deprecated_ioctl(cmd);  	case SNAPSHOT_ALLOC_SWAP_PAGE:  		if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {  			error = -ENODEV; @@ -346,27 +328,6 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,  		free_all_swap_pages(data->swap);  		break; -	case SNAPSHOT_SET_SWAP_FILE: /* This ioctl is deprecated */ -		snapshot_deprecated_ioctl(cmd); -		if (!swsusp_swap_in_use()) { -			/* -			 * User space encodes device types as two-byte values, -			 * so we need to recode them -			 */ -			if (old_decode_dev(arg)) { -				data->swap = swap_type_of(old_decode_dev(arg), -							0, NULL); -				if (data->swap < 0) -					error = -ENODEV; -			} else { -				data->swap = -1; -				error = -EINVAL; -			} -		} else { -			error = -EPERM; -		} -		break; -  	case SNAPSHOT_S2RAM:  		if (!data->frozen) {  			error = -EPERM; @@ -377,6 +338,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,  		 * PM_HIBERNATION_PREPARE  		 */  		error = suspend_devices_and_enter(PM_SUSPEND_MEM); +		data->ready = false;  		break;  	case SNAPSHOT_PLATFORM_SUPPORT: @@ -388,33 +350,6 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,  			error = hibernation_platform_enter();  		break; -	case SNAPSHOT_PMOPS: /* This ioctl is deprecated */ -		snapshot_deprecated_ioctl(cmd); -		error = -EINVAL; - -		switch (arg) { - -		case PMOPS_PREPARE: -			data->platform_support = 1; -			error = 0; -			break; - -		case PMOPS_ENTER: -			if (data->platform_support) -				error = hibernation_platform_enter(); -			break; - -		case PMOPS_FINISH: -			if (data->platform_support) -				error = 0; -			break; - -		default: -			printk(KERN_ERR "SNAPSHOT_PMOPS: invalid argument %ld\n", arg); - -		} -		break; -  	case SNAPSHOT_SET_SWAP_AREA:  		if (swsusp_swap_in_use()) {  			error = -EPERM; @@ -451,11 +386,72 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,  	} +	unlock_device_hotplug();  	mutex_unlock(&pm_mutex);  	return error;  } +#ifdef CONFIG_COMPAT + +struct compat_resume_swap_area { +	compat_loff_t offset; +	u32 dev; +} __packed; + +static long +snapshot_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ +	BUILD_BUG_ON(sizeof(loff_t) != sizeof(compat_loff_t)); + +	switch (cmd) { +	case SNAPSHOT_GET_IMAGE_SIZE: +	case SNAPSHOT_AVAIL_SWAP_SIZE: +	case SNAPSHOT_ALLOC_SWAP_PAGE: { +		compat_loff_t __user *uoffset = compat_ptr(arg); +		loff_t offset; +		mm_segment_t old_fs; +		int err; + +		old_fs = get_fs(); +		set_fs(KERNEL_DS); +		err = snapshot_ioctl(file, cmd, (unsigned long) &offset); +		set_fs(old_fs); +		if (!err && put_user(offset, uoffset)) +			err = -EFAULT; +		return err; +	} + +	case SNAPSHOT_CREATE_IMAGE: +		return snapshot_ioctl(file, cmd, +				      (unsigned long) compat_ptr(arg)); + +	case SNAPSHOT_SET_SWAP_AREA: { +		struct compat_resume_swap_area __user *u_swap_area = +			compat_ptr(arg); +		struct resume_swap_area swap_area; +		mm_segment_t old_fs; +		int err; + +		err = get_user(swap_area.offset, &u_swap_area->offset); +		err |= get_user(swap_area.dev, &u_swap_area->dev); +		if (err) +			return -EFAULT; +		old_fs = get_fs(); +		set_fs(KERNEL_DS); +		err = snapshot_ioctl(file, SNAPSHOT_SET_SWAP_AREA, +				     (unsigned long) &swap_area); +		set_fs(old_fs); +		return err; +	} + +	default: +		return snapshot_ioctl(file, cmd, arg); +	} +} + +#endif /* CONFIG_COMPAT */ +  static const struct file_operations snapshot_fops = {  	.open = snapshot_open,  	.release = snapshot_release, @@ -463,6 +459,9 @@ static const struct file_operations snapshot_fops = {  	.write = snapshot_write,  	.llseek = no_llseek,  	.unlocked_ioctl = snapshot_ioctl, +#ifdef CONFIG_COMPAT +	.compat_ioctl = snapshot_compat_ioctl, +#endif  };  static struct miscdevice snapshot_device = {  | 
