.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * linux/kernel/power/user.c |
---|
3 | 4 | * |
---|
4 | 5 | * This file provides the user space interface for software suspend/resume. |
---|
5 | 6 | * |
---|
6 | 7 | * Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl> |
---|
7 | | - * |
---|
8 | | - * This file is released under the GPLv2. |
---|
9 | | - * |
---|
10 | 8 | */ |
---|
11 | 9 | |
---|
12 | 10 | #include <linux/suspend.h> |
---|
13 | | -#include <linux/syscalls.h> |
---|
14 | 11 | #include <linux/reboot.h> |
---|
15 | 12 | #include <linux/string.h> |
---|
16 | 13 | #include <linux/device.h> |
---|
.. | .. |
---|
29 | 26 | |
---|
30 | 27 | #include "power.h" |
---|
31 | 28 | |
---|
32 | | - |
---|
33 | | -#define SNAPSHOT_MINOR 231 |
---|
| 29 | +static bool need_wait; |
---|
34 | 30 | |
---|
35 | 31 | static struct snapshot_data { |
---|
36 | 32 | struct snapshot_handle handle; |
---|
.. | .. |
---|
40 | 36 | bool ready; |
---|
41 | 37 | bool platform_support; |
---|
42 | 38 | bool free_bitmaps; |
---|
| 39 | + dev_t dev; |
---|
43 | 40 | } snapshot_state; |
---|
44 | 41 | |
---|
45 | | -atomic_t snapshot_device_available = ATOMIC_INIT(1); |
---|
| 42 | +int is_hibernate_resume_dev(dev_t dev) |
---|
| 43 | +{ |
---|
| 44 | + return hibernation_available() && snapshot_state.dev == dev; |
---|
| 45 | +} |
---|
46 | 46 | |
---|
47 | 47 | static int snapshot_open(struct inode *inode, struct file *filp) |
---|
48 | 48 | { |
---|
49 | 49 | struct snapshot_data *data; |
---|
50 | | - int error, nr_calls = 0; |
---|
| 50 | + int error; |
---|
51 | 51 | |
---|
52 | 52 | if (!hibernation_available()) |
---|
53 | 53 | return -EPERM; |
---|
54 | 54 | |
---|
55 | 55 | lock_system_sleep(); |
---|
56 | 56 | |
---|
57 | | - if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { |
---|
| 57 | + if (!hibernate_acquire()) { |
---|
58 | 58 | error = -EBUSY; |
---|
59 | 59 | goto Unlock; |
---|
60 | 60 | } |
---|
61 | 61 | |
---|
62 | 62 | if ((filp->f_flags & O_ACCMODE) == O_RDWR) { |
---|
63 | | - atomic_inc(&snapshot_device_available); |
---|
| 63 | + hibernate_release(); |
---|
64 | 64 | error = -ENOSYS; |
---|
65 | 65 | goto Unlock; |
---|
66 | 66 | } |
---|
.. | .. |
---|
70 | 70 | memset(&data->handle, 0, sizeof(struct snapshot_handle)); |
---|
71 | 71 | if ((filp->f_flags & O_ACCMODE) == O_RDONLY) { |
---|
72 | 72 | /* Hibernating. The image device should be accessible. */ |
---|
73 | | - data->swap = swsusp_resume_device ? |
---|
74 | | - swap_type_of(swsusp_resume_device, 0, NULL) : -1; |
---|
| 73 | + data->swap = swap_type_of(swsusp_resume_device, 0); |
---|
75 | 74 | data->mode = O_RDONLY; |
---|
76 | 75 | data->free_bitmaps = false; |
---|
77 | | - error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls); |
---|
78 | | - if (error) |
---|
79 | | - __pm_notifier_call_chain(PM_POST_HIBERNATION, --nr_calls, NULL); |
---|
| 76 | + error = pm_notifier_call_chain_robust(PM_HIBERNATION_PREPARE, PM_POST_HIBERNATION); |
---|
80 | 77 | } else { |
---|
81 | 78 | /* |
---|
82 | 79 | * Resuming. We may need to wait for the image device to |
---|
83 | 80 | * appear. |
---|
84 | 81 | */ |
---|
85 | | - wait_for_device_probe(); |
---|
| 82 | + need_wait = true; |
---|
86 | 83 | |
---|
87 | 84 | data->swap = -1; |
---|
88 | 85 | data->mode = O_WRONLY; |
---|
89 | | - error = __pm_notifier_call_chain(PM_RESTORE_PREPARE, -1, &nr_calls); |
---|
| 86 | + error = pm_notifier_call_chain_robust(PM_RESTORE_PREPARE, PM_POST_RESTORE); |
---|
90 | 87 | if (!error) { |
---|
91 | 88 | error = create_basic_memory_bitmaps(); |
---|
92 | 89 | data->free_bitmaps = !error; |
---|
93 | | - } else |
---|
94 | | - nr_calls--; |
---|
95 | | - |
---|
96 | | - if (error) |
---|
97 | | - __pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL); |
---|
| 90 | + } |
---|
98 | 91 | } |
---|
99 | 92 | if (error) |
---|
100 | | - atomic_inc(&snapshot_device_available); |
---|
| 93 | + hibernate_release(); |
---|
101 | 94 | |
---|
102 | 95 | data->frozen = false; |
---|
103 | 96 | data->ready = false; |
---|
104 | 97 | data->platform_support = false; |
---|
| 98 | + data->dev = 0; |
---|
105 | 99 | |
---|
106 | 100 | Unlock: |
---|
107 | 101 | unlock_system_sleep(); |
---|
.. | .. |
---|
117 | 111 | |
---|
118 | 112 | swsusp_free(); |
---|
119 | 113 | data = filp->private_data; |
---|
| 114 | + data->dev = 0; |
---|
120 | 115 | free_all_swap_pages(data->swap); |
---|
121 | 116 | if (data->frozen) { |
---|
122 | 117 | pm_restore_gfp_mask(); |
---|
.. | .. |
---|
127 | 122 | } |
---|
128 | 123 | pm_notifier_call_chain(data->mode == O_RDONLY ? |
---|
129 | 124 | PM_POST_HIBERNATION : PM_POST_RESTORE); |
---|
130 | | - atomic_inc(&snapshot_device_available); |
---|
| 125 | + hibernate_release(); |
---|
131 | 126 | |
---|
132 | 127 | unlock_system_sleep(); |
---|
133 | 128 | |
---|
.. | .. |
---|
174 | 169 | ssize_t res; |
---|
175 | 170 | loff_t pg_offp = *offp & ~PAGE_MASK; |
---|
176 | 171 | |
---|
| 172 | + if (need_wait) { |
---|
| 173 | + wait_for_device_probe(); |
---|
| 174 | + need_wait = false; |
---|
| 175 | + } |
---|
| 176 | + |
---|
177 | 177 | lock_system_sleep(); |
---|
178 | 178 | |
---|
179 | 179 | data = filp->private_data; |
---|
.. | .. |
---|
201 | 201 | return res; |
---|
202 | 202 | } |
---|
203 | 203 | |
---|
| 204 | +struct compat_resume_swap_area { |
---|
| 205 | + compat_loff_t offset; |
---|
| 206 | + u32 dev; |
---|
| 207 | +} __packed; |
---|
| 208 | + |
---|
| 209 | +static int snapshot_set_swap_area(struct snapshot_data *data, |
---|
| 210 | + void __user *argp) |
---|
| 211 | +{ |
---|
| 212 | + sector_t offset; |
---|
| 213 | + dev_t swdev; |
---|
| 214 | + |
---|
| 215 | + if (swsusp_swap_in_use()) |
---|
| 216 | + return -EPERM; |
---|
| 217 | + |
---|
| 218 | + if (in_compat_syscall()) { |
---|
| 219 | + struct compat_resume_swap_area swap_area; |
---|
| 220 | + |
---|
| 221 | + if (copy_from_user(&swap_area, argp, sizeof(swap_area))) |
---|
| 222 | + return -EFAULT; |
---|
| 223 | + swdev = new_decode_dev(swap_area.dev); |
---|
| 224 | + offset = swap_area.offset; |
---|
| 225 | + } else { |
---|
| 226 | + struct resume_swap_area swap_area; |
---|
| 227 | + |
---|
| 228 | + if (copy_from_user(&swap_area, argp, sizeof(swap_area))) |
---|
| 229 | + return -EFAULT; |
---|
| 230 | + swdev = new_decode_dev(swap_area.dev); |
---|
| 231 | + offset = swap_area.offset; |
---|
| 232 | + } |
---|
| 233 | + |
---|
| 234 | + /* |
---|
| 235 | + * User space encodes device types as two-byte values, |
---|
| 236 | + * so we need to recode them |
---|
| 237 | + */ |
---|
| 238 | + data->swap = swap_type_of(swdev, offset); |
---|
| 239 | + if (data->swap < 0) |
---|
| 240 | + return swdev ? -ENODEV : -EINVAL; |
---|
| 241 | + data->dev = swdev; |
---|
| 242 | + return 0; |
---|
| 243 | +} |
---|
| 244 | + |
---|
204 | 245 | static long snapshot_ioctl(struct file *filp, unsigned int cmd, |
---|
205 | 246 | unsigned long arg) |
---|
206 | 247 | { |
---|
.. | .. |
---|
208 | 249 | struct snapshot_data *data; |
---|
209 | 250 | loff_t size; |
---|
210 | 251 | sector_t offset; |
---|
| 252 | + |
---|
| 253 | + if (need_wait) { |
---|
| 254 | + wait_for_device_probe(); |
---|
| 255 | + need_wait = false; |
---|
| 256 | + } |
---|
211 | 257 | |
---|
212 | 258 | if (_IOC_TYPE(cmd) != SNAPSHOT_IOC_MAGIC) |
---|
213 | 259 | return -ENOTTY; |
---|
.. | .. |
---|
228 | 274 | if (data->frozen) |
---|
229 | 275 | break; |
---|
230 | 276 | |
---|
231 | | - printk("Syncing filesystems ... "); |
---|
232 | | - ksys_sync(); |
---|
233 | | - printk("done.\n"); |
---|
| 277 | + ksys_sync_helper(); |
---|
234 | 278 | |
---|
235 | 279 | error = freeze_processes(); |
---|
236 | 280 | if (error) |
---|
.. | .. |
---|
358 | 402 | break; |
---|
359 | 403 | |
---|
360 | 404 | case SNAPSHOT_SET_SWAP_AREA: |
---|
361 | | - if (swsusp_swap_in_use()) { |
---|
362 | | - error = -EPERM; |
---|
363 | | - } else { |
---|
364 | | - struct resume_swap_area swap_area; |
---|
365 | | - dev_t swdev; |
---|
366 | | - |
---|
367 | | - error = copy_from_user(&swap_area, (void __user *)arg, |
---|
368 | | - sizeof(struct resume_swap_area)); |
---|
369 | | - if (error) { |
---|
370 | | - error = -EFAULT; |
---|
371 | | - break; |
---|
372 | | - } |
---|
373 | | - |
---|
374 | | - /* |
---|
375 | | - * User space encodes device types as two-byte values, |
---|
376 | | - * so we need to recode them |
---|
377 | | - */ |
---|
378 | | - swdev = new_decode_dev(swap_area.dev); |
---|
379 | | - if (swdev) { |
---|
380 | | - offset = swap_area.offset; |
---|
381 | | - data->swap = swap_type_of(swdev, offset, NULL); |
---|
382 | | - if (data->swap < 0) |
---|
383 | | - error = -ENODEV; |
---|
384 | | - } else { |
---|
385 | | - data->swap = -1; |
---|
386 | | - error = -EINVAL; |
---|
387 | | - } |
---|
388 | | - } |
---|
| 405 | + error = snapshot_set_swap_area(data, (void __user *)arg); |
---|
389 | 406 | break; |
---|
390 | 407 | |
---|
391 | 408 | default: |
---|
.. | .. |
---|
400 | 417 | } |
---|
401 | 418 | |
---|
402 | 419 | #ifdef CONFIG_COMPAT |
---|
403 | | - |
---|
404 | | -struct compat_resume_swap_area { |
---|
405 | | - compat_loff_t offset; |
---|
406 | | - u32 dev; |
---|
407 | | -} __packed; |
---|
408 | | - |
---|
409 | 420 | static long |
---|
410 | 421 | snapshot_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
---|
411 | 422 | { |
---|
.. | .. |
---|
414 | 425 | switch (cmd) { |
---|
415 | 426 | case SNAPSHOT_GET_IMAGE_SIZE: |
---|
416 | 427 | case SNAPSHOT_AVAIL_SWAP_SIZE: |
---|
417 | | - case SNAPSHOT_ALLOC_SWAP_PAGE: { |
---|
418 | | - compat_loff_t __user *uoffset = compat_ptr(arg); |
---|
419 | | - loff_t offset; |
---|
420 | | - mm_segment_t old_fs; |
---|
421 | | - int err; |
---|
422 | | - |
---|
423 | | - old_fs = get_fs(); |
---|
424 | | - set_fs(KERNEL_DS); |
---|
425 | | - err = snapshot_ioctl(file, cmd, (unsigned long) &offset); |
---|
426 | | - set_fs(old_fs); |
---|
427 | | - if (!err && put_user(offset, uoffset)) |
---|
428 | | - err = -EFAULT; |
---|
429 | | - return err; |
---|
430 | | - } |
---|
431 | | - |
---|
| 428 | + case SNAPSHOT_ALLOC_SWAP_PAGE: |
---|
432 | 429 | case SNAPSHOT_CREATE_IMAGE: |
---|
| 430 | + case SNAPSHOT_SET_SWAP_AREA: |
---|
433 | 431 | return snapshot_ioctl(file, cmd, |
---|
434 | 432 | (unsigned long) compat_ptr(arg)); |
---|
435 | | - |
---|
436 | | - case SNAPSHOT_SET_SWAP_AREA: { |
---|
437 | | - struct compat_resume_swap_area __user *u_swap_area = |
---|
438 | | - compat_ptr(arg); |
---|
439 | | - struct resume_swap_area swap_area; |
---|
440 | | - mm_segment_t old_fs; |
---|
441 | | - int err; |
---|
442 | | - |
---|
443 | | - err = get_user(swap_area.offset, &u_swap_area->offset); |
---|
444 | | - err |= get_user(swap_area.dev, &u_swap_area->dev); |
---|
445 | | - if (err) |
---|
446 | | - return -EFAULT; |
---|
447 | | - old_fs = get_fs(); |
---|
448 | | - set_fs(KERNEL_DS); |
---|
449 | | - err = snapshot_ioctl(file, SNAPSHOT_SET_SWAP_AREA, |
---|
450 | | - (unsigned long) &swap_area); |
---|
451 | | - set_fs(old_fs); |
---|
452 | | - return err; |
---|
453 | | - } |
---|
454 | | - |
---|
455 | 433 | default: |
---|
456 | 434 | return snapshot_ioctl(file, cmd, arg); |
---|
457 | 435 | } |
---|
458 | 436 | } |
---|
459 | | - |
---|
460 | 437 | #endif /* CONFIG_COMPAT */ |
---|
461 | 438 | |
---|
462 | 439 | static const struct file_operations snapshot_fops = { |
---|