diff options
Diffstat (limited to 'kernel/power/user.c')
-rw-r--r-- | kernel/power/user.c | 195 |
1 files changed, 93 insertions, 102 deletions
diff --git a/kernel/power/user.c b/kernel/power/user.c index 6d8f535c2b88..3e100075b13c 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/swapops.h> | 21 | #include <linux/swapops.h> |
22 | #include <linux/pm.h> | 22 | #include <linux/pm.h> |
23 | #include <linux/fs.h> | 23 | #include <linux/fs.h> |
24 | #include <linux/compat.h> | ||
24 | #include <linux/console.h> | 25 | #include <linux/console.h> |
25 | #include <linux/cpu.h> | 26 | #include <linux/cpu.h> |
26 | #include <linux/freezer.h> | 27 | #include <linux/freezer.h> |
@@ -30,28 +31,6 @@ | |||
30 | 31 | ||
31 | #include "power.h" | 32 | #include "power.h" |
32 | 33 | ||
33 | /* | ||
34 | * NOTE: The SNAPSHOT_SET_SWAP_FILE and SNAPSHOT_PMOPS ioctls are obsolete and | ||
35 | * will be removed in the future. They are only preserved here for | ||
36 | * compatibility with existing userland utilities. | ||
37 | */ | ||
38 | #define SNAPSHOT_SET_SWAP_FILE _IOW(SNAPSHOT_IOC_MAGIC, 10, unsigned int) | ||
39 | #define SNAPSHOT_PMOPS _IOW(SNAPSHOT_IOC_MAGIC, 12, unsigned int) | ||
40 | |||
41 | #define PMOPS_PREPARE 1 | ||
42 | #define PMOPS_ENTER 2 | ||
43 | #define PMOPS_FINISH 3 | ||
44 | |||
45 | /* | ||
46 | * NOTE: The following ioctl definitions are wrong and have been replaced with | ||
47 | * correct ones. They are only preserved here for compatibility with existing | ||
48 | * userland utilities and will be removed in the future. | ||
49 | */ | ||
50 | #define SNAPSHOT_ATOMIC_SNAPSHOT _IOW(SNAPSHOT_IOC_MAGIC, 3, void *) | ||
51 | #define SNAPSHOT_SET_IMAGE_SIZE _IOW(SNAPSHOT_IOC_MAGIC, 6, unsigned long) | ||
52 | #define SNAPSHOT_AVAIL_SWAP _IOR(SNAPSHOT_IOC_MAGIC, 7, void *) | ||
53 | #define SNAPSHOT_GET_SWAP_PAGE _IOR(SNAPSHOT_IOC_MAGIC, 8, void *) | ||
54 | |||
55 | 34 | ||
56 | #define SNAPSHOT_MINOR 231 | 35 | #define SNAPSHOT_MINOR 231 |
57 | 36 | ||
@@ -71,7 +50,7 @@ static int snapshot_open(struct inode *inode, struct file *filp) | |||
71 | struct snapshot_data *data; | 50 | struct snapshot_data *data; |
72 | int error; | 51 | int error; |
73 | 52 | ||
74 | mutex_lock(&pm_mutex); | 53 | lock_system_sleep(); |
75 | 54 | ||
76 | if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { | 55 | if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { |
77 | error = -EBUSY; | 56 | error = -EBUSY; |
@@ -123,7 +102,7 @@ static int snapshot_open(struct inode *inode, struct file *filp) | |||
123 | data->platform_support = 0; | 102 | data->platform_support = 0; |
124 | 103 | ||
125 | Unlock: | 104 | Unlock: |
126 | mutex_unlock(&pm_mutex); | 105 | unlock_system_sleep(); |
127 | 106 | ||
128 | return error; | 107 | return error; |
129 | } | 108 | } |
@@ -132,7 +111,7 @@ static int snapshot_release(struct inode *inode, struct file *filp) | |||
132 | { | 111 | { |
133 | struct snapshot_data *data; | 112 | struct snapshot_data *data; |
134 | 113 | ||
135 | mutex_lock(&pm_mutex); | 114 | lock_system_sleep(); |
136 | 115 | ||
137 | swsusp_free(); | 116 | swsusp_free(); |
138 | free_basic_memory_bitmaps(); | 117 | free_basic_memory_bitmaps(); |
@@ -146,7 +125,7 @@ static int snapshot_release(struct inode *inode, struct file *filp) | |||
146 | PM_POST_HIBERNATION : PM_POST_RESTORE); | 125 | PM_POST_HIBERNATION : PM_POST_RESTORE); |
147 | atomic_inc(&snapshot_device_available); | 126 | atomic_inc(&snapshot_device_available); |
148 | 127 | ||
149 | mutex_unlock(&pm_mutex); | 128 | unlock_system_sleep(); |
150 | 129 | ||
151 | return 0; | 130 | return 0; |
152 | } | 131 | } |
@@ -158,7 +137,7 @@ static ssize_t snapshot_read(struct file *filp, char __user *buf, | |||
158 | ssize_t res; | 137 | ssize_t res; |
159 | loff_t pg_offp = *offp & ~PAGE_MASK; | 138 | loff_t pg_offp = *offp & ~PAGE_MASK; |
160 | 139 | ||
161 | mutex_lock(&pm_mutex); | 140 | lock_system_sleep(); |
162 | 141 | ||
163 | data = filp->private_data; | 142 | data = filp->private_data; |
164 | if (!data->ready) { | 143 | if (!data->ready) { |
@@ -179,7 +158,7 @@ static ssize_t snapshot_read(struct file *filp, char __user *buf, | |||
179 | *offp += res; | 158 | *offp += res; |
180 | 159 | ||
181 | Unlock: | 160 | Unlock: |
182 | mutex_unlock(&pm_mutex); | 161 | unlock_system_sleep(); |
183 | 162 | ||
184 | return res; | 163 | return res; |
185 | } | 164 | } |
@@ -191,7 +170,7 @@ static ssize_t snapshot_write(struct file *filp, const char __user *buf, | |||
191 | ssize_t res; | 170 | ssize_t res; |
192 | loff_t pg_offp = *offp & ~PAGE_MASK; | 171 | loff_t pg_offp = *offp & ~PAGE_MASK; |
193 | 172 | ||
194 | mutex_lock(&pm_mutex); | 173 | lock_system_sleep(); |
195 | 174 | ||
196 | data = filp->private_data; | 175 | data = filp->private_data; |
197 | 176 | ||
@@ -208,20 +187,11 @@ static ssize_t snapshot_write(struct file *filp, const char __user *buf, | |||
208 | if (res > 0) | 187 | if (res > 0) |
209 | *offp += res; | 188 | *offp += res; |
210 | unlock: | 189 | unlock: |
211 | mutex_unlock(&pm_mutex); | 190 | unlock_system_sleep(); |
212 | 191 | ||
213 | return res; | 192 | return res; |
214 | } | 193 | } |
215 | 194 | ||
216 | static void snapshot_deprecated_ioctl(unsigned int cmd) | ||
217 | { | ||
218 | if (printk_ratelimit()) | ||
219 | printk(KERN_NOTICE "%pf: ioctl '%.8x' is deprecated and will " | ||
220 | "be removed soon, update your suspend-to-disk " | ||
221 | "utilities\n", | ||
222 | __builtin_return_address(0), cmd); | ||
223 | } | ||
224 | |||
225 | static long snapshot_ioctl(struct file *filp, unsigned int cmd, | 195 | static long snapshot_ioctl(struct file *filp, unsigned int cmd, |
226 | unsigned long arg) | 196 | unsigned long arg) |
227 | { | 197 | { |
@@ -257,11 +227,9 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, | |||
257 | break; | 227 | break; |
258 | 228 | ||
259 | error = freeze_processes(); | 229 | error = freeze_processes(); |
260 | if (error) { | 230 | if (error) |
261 | thaw_processes(); | ||
262 | usermodehelper_enable(); | 231 | usermodehelper_enable(); |
263 | } | 232 | else |
264 | if (!error) | ||
265 | data->frozen = 1; | 233 | data->frozen = 1; |
266 | break; | 234 | break; |
267 | 235 | ||
@@ -274,8 +242,6 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, | |||
274 | data->frozen = 0; | 242 | data->frozen = 0; |
275 | break; | 243 | break; |
276 | 244 | ||
277 | case SNAPSHOT_ATOMIC_SNAPSHOT: | ||
278 | snapshot_deprecated_ioctl(cmd); | ||
279 | case SNAPSHOT_CREATE_IMAGE: | 245 | case SNAPSHOT_CREATE_IMAGE: |
280 | if (data->mode != O_RDONLY || !data->frozen || data->ready) { | 246 | if (data->mode != O_RDONLY || !data->frozen || data->ready) { |
281 | error = -EPERM; | 247 | error = -EPERM; |
@@ -283,10 +249,17 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, | |||
283 | } | 249 | } |
284 | pm_restore_gfp_mask(); | 250 | pm_restore_gfp_mask(); |
285 | error = hibernation_snapshot(data->platform_support); | 251 | error = hibernation_snapshot(data->platform_support); |
286 | if (!error) | 252 | if (error) { |
253 | thaw_kernel_threads(); | ||
254 | } else { | ||
287 | error = put_user(in_suspend, (int __user *)arg); | 255 | error = put_user(in_suspend, (int __user *)arg); |
288 | if (!error) | 256 | if (!error && !freezer_test_done) |
289 | data->ready = 1; | 257 | data->ready = 1; |
258 | if (freezer_test_done) { | ||
259 | freezer_test_done = false; | ||
260 | thaw_kernel_threads(); | ||
261 | } | ||
262 | } | ||
290 | break; | 263 | break; |
291 | 264 | ||
292 | case SNAPSHOT_ATOMIC_RESTORE: | 265 | case SNAPSHOT_ATOMIC_RESTORE: |
@@ -303,10 +276,17 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, | |||
303 | swsusp_free(); | 276 | swsusp_free(); |
304 | memset(&data->handle, 0, sizeof(struct snapshot_handle)); | 277 | memset(&data->handle, 0, sizeof(struct snapshot_handle)); |
305 | data->ready = 0; | 278 | data->ready = 0; |
279 | /* | ||
280 | * It is necessary to thaw kernel threads here, because | ||
281 | * SNAPSHOT_CREATE_IMAGE may be invoked directly after | ||
282 | * SNAPSHOT_FREE. In that case, if kernel threads were not | ||
283 | * thawed, the preallocation of memory carried out by | ||
284 | * hibernation_snapshot() might run into problems (i.e. it | ||
285 | * might fail or even deadlock). | ||
286 | */ | ||
287 | thaw_kernel_threads(); | ||
306 | break; | 288 | break; |
307 | 289 | ||
308 | case SNAPSHOT_SET_IMAGE_SIZE: | ||
309 | snapshot_deprecated_ioctl(cmd); | ||
310 | case SNAPSHOT_PREF_IMAGE_SIZE: | 290 | case SNAPSHOT_PREF_IMAGE_SIZE: |
311 | image_size = arg; | 291 | image_size = arg; |
312 | break; | 292 | break; |
@@ -321,16 +301,12 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, | |||
321 | error = put_user(size, (loff_t __user *)arg); | 301 | error = put_user(size, (loff_t __user *)arg); |
322 | break; | 302 | break; |
323 | 303 | ||
324 | case SNAPSHOT_AVAIL_SWAP: | ||
325 | snapshot_deprecated_ioctl(cmd); | ||
326 | case SNAPSHOT_AVAIL_SWAP_SIZE: | 304 | case SNAPSHOT_AVAIL_SWAP_SIZE: |
327 | size = count_swap_pages(data->swap, 1); | 305 | size = count_swap_pages(data->swap, 1); |
328 | size <<= PAGE_SHIFT; | 306 | size <<= PAGE_SHIFT; |
329 | error = put_user(size, (loff_t __user *)arg); | 307 | error = put_user(size, (loff_t __user *)arg); |
330 | break; | 308 | break; |
331 | 309 | ||
332 | case SNAPSHOT_GET_SWAP_PAGE: | ||
333 | snapshot_deprecated_ioctl(cmd); | ||
334 | case SNAPSHOT_ALLOC_SWAP_PAGE: | 310 | case SNAPSHOT_ALLOC_SWAP_PAGE: |
335 | if (data->swap < 0 || data->swap >= MAX_SWAPFILES) { | 311 | if (data->swap < 0 || data->swap >= MAX_SWAPFILES) { |
336 | error = -ENODEV; | 312 | error = -ENODEV; |
@@ -353,27 +329,6 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, | |||
353 | free_all_swap_pages(data->swap); | 329 | free_all_swap_pages(data->swap); |
354 | break; | 330 | break; |
355 | 331 | ||
356 | case SNAPSHOT_SET_SWAP_FILE: /* This ioctl is deprecated */ | ||
357 | snapshot_deprecated_ioctl(cmd); | ||
358 | if (!swsusp_swap_in_use()) { | ||
359 | /* | ||
360 | * User space encodes device types as two-byte values, | ||
361 | * so we need to recode them | ||
362 | */ | ||
363 | if (old_decode_dev(arg)) { | ||
364 | data->swap = swap_type_of(old_decode_dev(arg), | ||
365 | 0, NULL); | ||
366 | if (data->swap < 0) | ||
367 | error = -ENODEV; | ||
368 | } else { | ||
369 | data->swap = -1; | ||
370 | error = -EINVAL; | ||
371 | } | ||
372 | } else { | ||
373 | error = -EPERM; | ||
374 | } | ||
375 | break; | ||
376 | |||
377 | case SNAPSHOT_S2RAM: | 332 | case SNAPSHOT_S2RAM: |
378 | if (!data->frozen) { | 333 | if (!data->frozen) { |
379 | error = -EPERM; | 334 | error = -EPERM; |
@@ -396,33 +351,6 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, | |||
396 | error = hibernation_platform_enter(); | 351 | error = hibernation_platform_enter(); |
397 | break; | 352 | break; |
398 | 353 | ||
399 | case SNAPSHOT_PMOPS: /* This ioctl is deprecated */ | ||
400 | snapshot_deprecated_ioctl(cmd); | ||
401 | error = -EINVAL; | ||
402 | |||
403 | switch (arg) { | ||
404 | |||
405 | case PMOPS_PREPARE: | ||
406 | data->platform_support = 1; | ||
407 | error = 0; | ||
408 | break; | ||
409 | |||
410 | case PMOPS_ENTER: | ||
411 | if (data->platform_support) | ||
412 | error = hibernation_platform_enter(); | ||
413 | break; | ||
414 | |||
415 | case PMOPS_FINISH: | ||
416 | if (data->platform_support) | ||
417 | error = 0; | ||
418 | break; | ||
419 | |||
420 | default: | ||
421 | printk(KERN_ERR "SNAPSHOT_PMOPS: invalid argument %ld\n", arg); | ||
422 | |||
423 | } | ||
424 | break; | ||
425 | |||
426 | case SNAPSHOT_SET_SWAP_AREA: | 354 | case SNAPSHOT_SET_SWAP_AREA: |
427 | if (swsusp_swap_in_use()) { | 355 | if (swsusp_swap_in_use()) { |
428 | error = -EPERM; | 356 | error = -EPERM; |
@@ -464,6 +392,66 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd, | |||
464 | return error; | 392 | return error; |
465 | } | 393 | } |
466 | 394 | ||
395 | #ifdef CONFIG_COMPAT | ||
396 | |||
397 | struct compat_resume_swap_area { | ||
398 | compat_loff_t offset; | ||
399 | u32 dev; | ||
400 | } __packed; | ||
401 | |||
402 | static long | ||
403 | snapshot_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
404 | { | ||
405 | BUILD_BUG_ON(sizeof(loff_t) != sizeof(compat_loff_t)); | ||
406 | |||
407 | switch (cmd) { | ||
408 | case SNAPSHOT_GET_IMAGE_SIZE: | ||
409 | case SNAPSHOT_AVAIL_SWAP_SIZE: | ||
410 | case SNAPSHOT_ALLOC_SWAP_PAGE: { | ||
411 | compat_loff_t __user *uoffset = compat_ptr(arg); | ||
412 | loff_t offset; | ||
413 | mm_segment_t old_fs; | ||
414 | int err; | ||
415 | |||
416 | old_fs = get_fs(); | ||
417 | set_fs(KERNEL_DS); | ||
418 | err = snapshot_ioctl(file, cmd, (unsigned long) &offset); | ||
419 | set_fs(old_fs); | ||
420 | if (!err && put_user(offset, uoffset)) | ||
421 | err = -EFAULT; | ||
422 | return err; | ||
423 | } | ||
424 | |||
425 | case SNAPSHOT_CREATE_IMAGE: | ||
426 | return snapshot_ioctl(file, cmd, | ||
427 | (unsigned long) compat_ptr(arg)); | ||
428 | |||
429 | case SNAPSHOT_SET_SWAP_AREA: { | ||
430 | struct compat_resume_swap_area __user *u_swap_area = | ||
431 | compat_ptr(arg); | ||
432 | struct resume_swap_area swap_area; | ||
433 | mm_segment_t old_fs; | ||
434 | int err; | ||
435 | |||
436 | err = get_user(swap_area.offset, &u_swap_area->offset); | ||
437 | err |= get_user(swap_area.dev, &u_swap_area->dev); | ||
438 | if (err) | ||
439 | return -EFAULT; | ||
440 | old_fs = get_fs(); | ||
441 | set_fs(KERNEL_DS); | ||
442 | err = snapshot_ioctl(file, SNAPSHOT_SET_SWAP_AREA, | ||
443 | (unsigned long) &swap_area); | ||
444 | set_fs(old_fs); | ||
445 | return err; | ||
446 | } | ||
447 | |||
448 | default: | ||
449 | return snapshot_ioctl(file, cmd, arg); | ||
450 | } | ||
451 | } | ||
452 | |||
453 | #endif /* CONFIG_COMPAT */ | ||
454 | |||
467 | static const struct file_operations snapshot_fops = { | 455 | static const struct file_operations snapshot_fops = { |
468 | .open = snapshot_open, | 456 | .open = snapshot_open, |
469 | .release = snapshot_release, | 457 | .release = snapshot_release, |
@@ -471,6 +459,9 @@ static const struct file_operations snapshot_fops = { | |||
471 | .write = snapshot_write, | 459 | .write = snapshot_write, |
472 | .llseek = no_llseek, | 460 | .llseek = no_llseek, |
473 | .unlocked_ioctl = snapshot_ioctl, | 461 | .unlocked_ioctl = snapshot_ioctl, |
462 | #ifdef CONFIG_COMPAT | ||
463 | .compat_ioctl = snapshot_compat_ioctl, | ||
464 | #endif | ||
474 | }; | 465 | }; |
475 | 466 | ||
476 | static struct miscdevice snapshot_device = { | 467 | static struct miscdevice snapshot_device = { |