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