Blame view
kernel/power/user.c
10.1 KB
55716d264 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
6e1819d61 [PATCH] swsusp: u... |
2 3 4 5 6 7 |
/* * linux/kernel/power/user.c * * This file provides the user space interface for software suspend/resume. * * Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl> |
6e1819d61 [PATCH] swsusp: u... |
8 9 10 |
*/ #include <linux/suspend.h> |
3592695c3 [PATCH] uswsusp: ... |
11 |
#include <linux/reboot.h> |
6e1819d61 [PATCH] swsusp: u... |
12 13 14 15 16 17 18 19 |
#include <linux/string.h> #include <linux/device.h> #include <linux/miscdevice.h> #include <linux/mm.h> #include <linux/swap.h> #include <linux/swapops.h> #include <linux/pm.h> #include <linux/fs.h> |
c336078bf PM / Hibernate: I... |
20 |
#include <linux/compat.h> |
97c7801cd [PATCH] swsusp: U... |
21 |
#include <linux/console.h> |
e3920fb42 [PATCH] Disable C... |
22 |
#include <linux/cpu.h> |
7dfb71030 [PATCH] Add inclu... |
23 |
#include <linux/freezer.h> |
6e1819d61 [PATCH] swsusp: u... |
24 |
|
7c0f6ba68 Replace <asm/uacc... |
25 |
#include <linux/uaccess.h> |
6e1819d61 [PATCH] swsusp: u... |
26 27 |
#include "power.h" |
eb57c1cf0 Hibernation: Rewo... |
28 |
|
6e1819d61 [PATCH] swsusp: u... |
29 30 31 32 33 |
#define SNAPSHOT_MINOR 231 static struct snapshot_data { struct snapshot_handle handle; int swap; |
6e1819d61 [PATCH] swsusp: u... |
34 |
int mode; |
7bc9b1cff PM / Hibernate: U... |
35 36 37 |
bool frozen; bool ready; bool platform_support; |
aab172891 PM / hibernate: F... |
38 |
bool free_bitmaps; |
6e1819d61 [PATCH] swsusp: u... |
39 |
} snapshot_state; |
0709db607 swsusp: use GFP_K... |
40 |
atomic_t snapshot_device_available = ATOMIC_INIT(1); |
6e1819d61 [PATCH] swsusp: u... |
41 42 43 44 |
static int snapshot_open(struct inode *inode, struct file *filp) { struct snapshot_data *data; |
ea00f4f4f PM / sleep: make ... |
45 |
int error, nr_calls = 0; |
6e1819d61 [PATCH] swsusp: u... |
46 |
|
a6e15a390 PM / hibernate: i... |
47 48 |
if (!hibernation_available()) return -EPERM; |
bcda53faf PM / Sleep: Repla... |
49 |
lock_system_sleep(); |
25f2f3daa snapshot: Use pm_... |
50 51 52 53 54 |
if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { error = -EBUSY; goto Unlock; } |
6e1819d61 [PATCH] swsusp: u... |
55 |
|
1525a2ad7 swsusp: fix error... |
56 |
if ((filp->f_flags & O_ACCMODE) == O_RDWR) { |
0709db607 swsusp: use GFP_K... |
57 |
atomic_inc(&snapshot_device_available); |
25f2f3daa snapshot: Use pm_... |
58 59 |
error = -ENOSYS; goto Unlock; |
1525a2ad7 swsusp: fix error... |
60 |
} |
6e1819d61 [PATCH] swsusp: u... |
61 62 63 64 65 |
nonseekable_open(inode, filp); data = &snapshot_state; filp->private_data = data; memset(&data->handle, 0, sizeof(struct snapshot_handle)); if ((filp->f_flags & O_ACCMODE) == O_RDONLY) { |
c75108594 PM/Hibernate: Wai... |
66 |
/* Hibernating. The image device should be accessible. */ |
915bae9eb [PATCH] swsusp: u... |
67 |
data->swap = swsusp_resume_device ? |
7bf236874 [PATCH] swsusp: D... |
68 |
swap_type_of(swsusp_resume_device, 0, NULL) : -1; |
6e1819d61 [PATCH] swsusp: u... |
69 |
data->mode = O_RDONLY; |
6a0c7cd33 PM / Hibernate: D... |
70 |
data->free_bitmaps = false; |
ea00f4f4f PM / sleep: make ... |
71 |
error = __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, -1, &nr_calls); |
c3e94d899 Hibernation: Add ... |
72 |
if (error) |
ea00f4f4f PM / sleep: make ... |
73 |
__pm_notifier_call_chain(PM_POST_HIBERNATION, --nr_calls, NULL); |
6e1819d61 [PATCH] swsusp: u... |
74 |
} else { |
c75108594 PM/Hibernate: Wai... |
75 76 77 78 79 |
/* * Resuming. We may need to wait for the image device to * appear. */ wait_for_device_probe(); |
c75108594 PM/Hibernate: Wai... |
80 |
|
6e1819d61 [PATCH] swsusp: u... |
81 82 |
data->swap = -1; data->mode = O_WRONLY; |
ea00f4f4f PM / sleep: make ... |
83 |
error = __pm_notifier_call_chain(PM_RESTORE_PREPARE, -1, &nr_calls); |
aab172891 PM / hibernate: F... |
84 85 86 |
if (!error) { error = create_basic_memory_bitmaps(); data->free_bitmaps = !error; |
ea00f4f4f PM / sleep: make ... |
87 88 |
} else nr_calls--; |
c3e94d899 Hibernation: Add ... |
89 |
if (error) |
ea00f4f4f PM / sleep: make ... |
90 |
__pm_notifier_call_chain(PM_POST_RESTORE, nr_calls, NULL); |
c3e94d899 Hibernation: Add ... |
91 |
} |
8fd37a4c9 PM / hibernate: C... |
92 |
if (error) |
c3e94d899 Hibernation: Add ... |
93 |
atomic_inc(&snapshot_device_available); |
8fd37a4c9 PM / hibernate: C... |
94 |
|
7bc9b1cff PM / Hibernate: U... |
95 96 97 |
data->frozen = false; data->ready = false; data->platform_support = false; |
6e1819d61 [PATCH] swsusp: u... |
98 |
|
25f2f3daa snapshot: Use pm_... |
99 |
Unlock: |
bcda53faf PM / Sleep: Repla... |
100 |
unlock_system_sleep(); |
25f2f3daa snapshot: Use pm_... |
101 102 |
return error; |
6e1819d61 [PATCH] swsusp: u... |
103 104 105 106 107 |
} static int snapshot_release(struct inode *inode, struct file *filp) { struct snapshot_data *data; |
bcda53faf PM / Sleep: Repla... |
108 |
lock_system_sleep(); |
25f2f3daa snapshot: Use pm_... |
109 |
|
6e1819d61 [PATCH] swsusp: u... |
110 111 |
swsusp_free(); data = filp->private_data; |
d1d241cc2 swsusp: use rbtre... |
112 |
free_all_swap_pages(data->swap); |
9744997a8 PM / Hibernate: M... |
113 114 |
if (data->frozen) { pm_restore_gfp_mask(); |
8fd37a4c9 PM / hibernate: C... |
115 |
free_basic_memory_bitmaps(); |
6e1819d61 [PATCH] swsusp: u... |
116 |
thaw_processes(); |
aab172891 PM / hibernate: F... |
117 118 |
} else if (data->free_bitmaps) { free_basic_memory_bitmaps(); |
9744997a8 PM / Hibernate: M... |
119 |
} |
1497dd1d2 PM / Hibernate: F... |
120 |
pm_notifier_call_chain(data->mode == O_RDONLY ? |
c3e94d899 Hibernation: Add ... |
121 |
PM_POST_HIBERNATION : PM_POST_RESTORE); |
0709db607 swsusp: use GFP_K... |
122 |
atomic_inc(&snapshot_device_available); |
25f2f3daa snapshot: Use pm_... |
123 |
|
bcda53faf PM / Sleep: Repla... |
124 |
unlock_system_sleep(); |
25f2f3daa snapshot: Use pm_... |
125 |
|
6e1819d61 [PATCH] swsusp: u... |
126 127 128 129 130 131 132 133 |
return 0; } static ssize_t snapshot_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) { struct snapshot_data *data; ssize_t res; |
d3c1b24c5 PM / Hibernate: S... |
134 |
loff_t pg_offp = *offp & ~PAGE_MASK; |
6e1819d61 [PATCH] swsusp: u... |
135 |
|
bcda53faf PM / Sleep: Repla... |
136 |
lock_system_sleep(); |
25f2f3daa snapshot: Use pm_... |
137 |
|
6e1819d61 [PATCH] swsusp: u... |
138 |
data = filp->private_data; |
25f2f3daa snapshot: Use pm_... |
139 140 141 142 |
if (!data->ready) { res = -ENODATA; goto Unlock; } |
d3c1b24c5 PM / Hibernate: S... |
143 144 145 146 147 148 |
if (!pg_offp) { /* on page boundary? */ res = snapshot_read_next(&data->handle); if (res <= 0) goto Unlock; } else { res = PAGE_SIZE - pg_offp; |
6e1819d61 [PATCH] swsusp: u... |
149 |
} |
25f2f3daa snapshot: Use pm_... |
150 |
|
d3c1b24c5 PM / Hibernate: S... |
151 152 153 154 |
res = simple_read_from_buffer(buf, count, &pg_offp, data_of(data->handle), res); if (res > 0) *offp += res; |
25f2f3daa snapshot: Use pm_... |
155 |
Unlock: |
bcda53faf PM / Sleep: Repla... |
156 |
unlock_system_sleep(); |
25f2f3daa snapshot: Use pm_... |
157 |
|
6e1819d61 [PATCH] swsusp: u... |
158 159 160 161 162 163 164 165 |
return res; } static ssize_t snapshot_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp) { struct snapshot_data *data; ssize_t res; |
d3c1b24c5 PM / Hibernate: S... |
166 |
loff_t pg_offp = *offp & ~PAGE_MASK; |
6e1819d61 [PATCH] swsusp: u... |
167 |
|
bcda53faf PM / Sleep: Repla... |
168 |
lock_system_sleep(); |
25f2f3daa snapshot: Use pm_... |
169 |
|
6e1819d61 [PATCH] swsusp: u... |
170 |
data = filp->private_data; |
d3c1b24c5 PM / Hibernate: S... |
171 172 173 174 175 176 177 |
if (!pg_offp) { res = snapshot_write_next(&data->handle); if (res <= 0) goto unlock; } else { res = PAGE_SIZE - pg_offp; |
6e1819d61 [PATCH] swsusp: u... |
178 |
} |
25f2f3daa snapshot: Use pm_... |
179 |
|
fc14eebfc PM / hibernate: F... |
180 181 182 183 |
if (!data_of(data->handle)) { res = -EINVAL; goto unlock; } |
d3c1b24c5 PM / Hibernate: S... |
184 185 186 187 188 |
res = simple_write_to_buffer(data_of(data->handle), res, &pg_offp, buf, count); if (res > 0) *offp += res; unlock: |
bcda53faf PM / Sleep: Repla... |
189 |
unlock_system_sleep(); |
25f2f3daa snapshot: Use pm_... |
190 |
|
6e1819d61 [PATCH] swsusp: u... |
191 192 |
return res; } |
52d11025d snapshot: Push BK... |
193 194 |
static long snapshot_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
6e1819d61 [PATCH] swsusp: u... |
195 196 197 |
{ int error = 0; struct snapshot_data *data; |
af508b34d Hibernation: Intr... |
198 |
loff_t size; |
3aef83e0e [PATCH] swsusp: u... |
199 |
sector_t offset; |
6e1819d61 [PATCH] swsusp: u... |
200 201 202 203 204 205 206 |
if (_IOC_TYPE(cmd) != SNAPSHOT_IOC_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > SNAPSHOT_IOC_MAXNR) return -ENOTTY; if (!capable(CAP_SYS_ADMIN)) return -EPERM; |
55f2503c3 PM / reboot: Elim... |
207 |
if (!mutex_trylock(&system_transition_mutex)) |
25f2f3daa snapshot: Use pm_... |
208 |
return -EBUSY; |
6e1819d61 [PATCH] swsusp: u... |
209 |
|
942f40155 PM / hibernate / ... |
210 |
lock_device_hotplug(); |
25f2f3daa snapshot: Use pm_... |
211 |
data = filp->private_data; |
52d11025d snapshot: Push BK... |
212 |
|
6e1819d61 [PATCH] swsusp: u... |
213 214 215 216 217 |
switch (cmd) { case SNAPSHOT_FREEZE: if (data->frozen) break; |
1bfcf1304 pm: rework disabl... |
218 |
|
b5dee3130 PM / sleep: Refac... |
219 |
ksys_sync_helper(); |
c3e94d899 Hibernation: Add ... |
220 |
|
1bfcf1304 pm: rework disabl... |
221 |
error = freeze_processes(); |
8fd37a4c9 PM / hibernate: C... |
222 223 224 225 226 227 228 |
if (error) break; error = create_basic_memory_bitmaps(); if (error) thaw_processes(); else |
7bc9b1cff PM / Hibernate: U... |
229 |
data->frozen = true; |
8fd37a4c9 PM / hibernate: C... |
230 |
|
6e1819d61 [PATCH] swsusp: u... |
231 232 233 |
break; case SNAPSHOT_UNFREEZE: |
2f41dddbb swsusp: Fix userl... |
234 |
if (!data->frozen || data->ready) |
6e1819d61 [PATCH] swsusp: u... |
235 |
break; |
c9e664f1f PM / Hibernate: F... |
236 |
pm_restore_gfp_mask(); |
8fd37a4c9 PM / hibernate: C... |
237 |
free_basic_memory_bitmaps(); |
aab172891 PM / hibernate: F... |
238 |
data->free_bitmaps = false; |
6e1819d61 [PATCH] swsusp: u... |
239 |
thaw_processes(); |
7bc9b1cff PM / Hibernate: U... |
240 |
data->frozen = false; |
6e1819d61 [PATCH] swsusp: u... |
241 |
break; |
b694e52eb PM / Hibernate: R... |
242 |
case SNAPSHOT_CREATE_IMAGE: |
6e1819d61 [PATCH] swsusp: u... |
243 244 245 246 |
if (data->mode != O_RDONLY || !data->frozen || data->ready) { error = -EPERM; break; } |
c9e664f1f PM / Hibernate: F... |
247 |
pm_restore_gfp_mask(); |
eb57c1cf0 Hibernation: Rewo... |
248 |
error = hibernation_snapshot(data->platform_support); |
51d6ff7ac PM / Hibernate: T... |
249 |
if (!error) { |
cc5d207c8 Hibernation: Corr... |
250 |
error = put_user(in_suspend, (int __user *)arg); |
a556d5b58 PM / Hibernate: R... |
251 252 |
data->ready = !freezer_test_done && !error; freezer_test_done = false; |
97819a262 PM / Hibernate: T... |
253 |
} |
6e1819d61 [PATCH] swsusp: u... |
254 255 256 |
break; case SNAPSHOT_ATOMIC_RESTORE: |
8357376d3 [PATCH] swsusp: I... |
257 |
snapshot_write_finalize(&data->handle); |
6e1819d61 [PATCH] swsusp: u... |
258 259 260 261 262 |
if (data->mode != O_WRONLY || !data->frozen || !snapshot_image_loaded(&data->handle)) { error = -EPERM; break; } |
eb57c1cf0 Hibernation: Rewo... |
263 |
error = hibernation_restore(data->platform_support); |
6e1819d61 [PATCH] swsusp: u... |
264 265 266 267 268 |
break; case SNAPSHOT_FREE: swsusp_free(); memset(&data->handle, 0, sizeof(struct snapshot_handle)); |
7bc9b1cff PM / Hibernate: U... |
269 |
data->ready = false; |
181e9bdef PM / Hibernate: F... |
270 271 272 273 274 275 276 277 278 |
/* * 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(); |
6e1819d61 [PATCH] swsusp: u... |
279 |
break; |
b694e52eb PM / Hibernate: R... |
280 |
case SNAPSHOT_PREF_IMAGE_SIZE: |
6e1819d61 [PATCH] swsusp: u... |
281 282 |
image_size = arg; break; |
af508b34d Hibernation: Intr... |
283 284 285 286 287 288 289 290 291 |
case SNAPSHOT_GET_IMAGE_SIZE: if (!data->ready) { error = -ENODATA; break; } size = snapshot_get_image_size(); size <<= PAGE_SHIFT; error = put_user(size, (loff_t __user *)arg); break; |
b694e52eb PM / Hibernate: R... |
292 |
case SNAPSHOT_AVAIL_SWAP_SIZE: |
af508b34d Hibernation: Intr... |
293 294 295 |
size = count_swap_pages(data->swap, 1); size <<= PAGE_SHIFT; error = put_user(size, (loff_t __user *)arg); |
6e1819d61 [PATCH] swsusp: u... |
296 |
break; |
b694e52eb PM / Hibernate: R... |
297 |
case SNAPSHOT_ALLOC_SWAP_PAGE: |
6e1819d61 [PATCH] swsusp: u... |
298 299 300 301 |
if (data->swap < 0 || data->swap >= MAX_SWAPFILES) { error = -ENODEV; break; } |
d1d241cc2 swsusp: use rbtre... |
302 |
offset = alloc_swapdev_block(data->swap); |
6e1819d61 [PATCH] swsusp: u... |
303 304 |
if (offset) { offset <<= PAGE_SHIFT; |
cc5d207c8 Hibernation: Corr... |
305 |
error = put_user(offset, (loff_t __user *)arg); |
6e1819d61 [PATCH] swsusp: u... |
306 307 308 309 310 311 312 313 314 315 |
} else { error = -ENOSPC; } break; case SNAPSHOT_FREE_SWAP_PAGES: if (data->swap < 0 || data->swap >= MAX_SWAPFILES) { error = -ENODEV; break; } |
d1d241cc2 swsusp: use rbtre... |
316 |
free_all_swap_pages(data->swap); |
6e1819d61 [PATCH] swsusp: u... |
317 |
break; |
9b238205b [PATCH] swsusp: a... |
318 319 320 321 322 |
case SNAPSHOT_S2RAM: if (!data->frozen) { error = -EPERM; break; } |
6c961dfb7 PM: Reduce code d... |
323 324 325 326 327 |
/* * Tasks are frozen and the notifiers have been called with * PM_HIBERNATION_PREPARE */ error = suspend_devices_and_enter(PM_SUSPEND_MEM); |
7bc9b1cff PM / Hibernate: U... |
328 |
data->ready = false; |
9b238205b [PATCH] swsusp: a... |
329 |
break; |
eb57c1cf0 Hibernation: Rewo... |
330 331 332 333 334 335 336 337 |
case SNAPSHOT_PLATFORM_SUPPORT: data->platform_support = !!arg; break; case SNAPSHOT_POWER_OFF: if (data->platform_support) error = hibernation_platform_enter(); break; |
37b2ba12d [PATCH] swsusp: a... |
338 |
case SNAPSHOT_SET_SWAP_AREA: |
d1d241cc2 swsusp: use rbtre... |
339 |
if (swsusp_swap_in_use()) { |
37b2ba12d [PATCH] swsusp: a... |
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
error = -EPERM; } else { struct resume_swap_area swap_area; dev_t swdev; error = copy_from_user(&swap_area, (void __user *)arg, sizeof(struct resume_swap_area)); if (error) { error = -EFAULT; break; } /* * User space encodes device types as two-byte values, * so we need to recode them */ |
d88d4050d PM / Hibernate: u... |
356 |
swdev = new_decode_dev(swap_area.dev); |
37b2ba12d [PATCH] swsusp: a... |
357 358 |
if (swdev) { offset = swap_area.offset; |
7bf236874 [PATCH] swsusp: D... |
359 |
data->swap = swap_type_of(swdev, offset, NULL); |
37b2ba12d [PATCH] swsusp: a... |
360 361 362 363 364 365 366 367 |
if (data->swap < 0) error = -ENODEV; } else { data->swap = -1; error = -EINVAL; } } break; |
6e1819d61 [PATCH] swsusp: u... |
368 369 370 371 |
default: error = -ENOTTY; } |
25f2f3daa snapshot: Use pm_... |
372 |
|
942f40155 PM / hibernate / ... |
373 |
unlock_device_hotplug(); |
55f2503c3 PM / reboot: Elim... |
374 |
mutex_unlock(&system_transition_mutex); |
25f2f3daa snapshot: Use pm_... |
375 |
|
6e1819d61 [PATCH] swsusp: u... |
376 377 |
return error; } |
c336078bf PM / Hibernate: I... |
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 |
#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 */ |
15ad7cdcf [PATCH] struct se... |
437 |
static const struct file_operations snapshot_fops = { |
6e1819d61 [PATCH] swsusp: u... |
438 439 440 441 442 |
.open = snapshot_open, .release = snapshot_release, .read = snapshot_read, .write = snapshot_write, .llseek = no_llseek, |
52d11025d snapshot: Push BK... |
443 |
.unlocked_ioctl = snapshot_ioctl, |
c336078bf PM / Hibernate: I... |
444 445 446 |
#ifdef CONFIG_COMPAT .compat_ioctl = snapshot_compat_ioctl, #endif |
6e1819d61 [PATCH] swsusp: u... |
447 448 449 450 451 452 453 454 455 456 457 458 459 460 |
}; static struct miscdevice snapshot_device = { .minor = SNAPSHOT_MINOR, .name = "snapshot", .fops = &snapshot_fops, }; static int __init snapshot_device_init(void) { return misc_register(&snapshot_device); }; device_initcall(snapshot_device_init); |