Blame view
kernel/power/user.c
9.56 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> |
c336078bf PM / Hibernate: I... |
24 |
#include <linux/compat.h> |
97c7801cd [PATCH] swsusp: U... |
25 |
#include <linux/console.h> |
e3920fb42 [PATCH] Disable C... |
26 |
#include <linux/cpu.h> |
7dfb71030 [PATCH] Add inclu... |
27 |
#include <linux/freezer.h> |
c75108594 PM/Hibernate: Wai... |
28 |
#include <scsi/scsi_scan.h> |
6e1819d61 [PATCH] swsusp: u... |
29 30 31 32 |
#include <asm/uaccess.h> #include "power.h" |
eb57c1cf0 Hibernation: Rewo... |
33 |
|
6e1819d61 [PATCH] swsusp: u... |
34 35 36 37 38 |
#define SNAPSHOT_MINOR 231 static struct snapshot_data { struct snapshot_handle handle; int swap; |
6e1819d61 [PATCH] swsusp: u... |
39 40 41 |
int mode; char frozen; char ready; |
eb57c1cf0 Hibernation: Rewo... |
42 |
char platform_support; |
6e1819d61 [PATCH] swsusp: u... |
43 |
} snapshot_state; |
0709db607 swsusp: use GFP_K... |
44 |
atomic_t snapshot_device_available = ATOMIC_INIT(1); |
6e1819d61 [PATCH] swsusp: u... |
45 46 47 48 |
static int snapshot_open(struct inode *inode, struct file *filp) { struct snapshot_data *data; |
c3e94d899 Hibernation: Add ... |
49 |
int error; |
6e1819d61 [PATCH] swsusp: u... |
50 |
|
bcda53faf PM / Sleep: Repla... |
51 |
lock_system_sleep(); |
25f2f3daa snapshot: Use pm_... |
52 53 54 55 56 |
if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { error = -EBUSY; goto Unlock; } |
6e1819d61 [PATCH] swsusp: u... |
57 |
|
1525a2ad7 swsusp: fix error... |
58 |
if ((filp->f_flags & O_ACCMODE) == O_RDWR) { |
0709db607 swsusp: use GFP_K... |
59 |
atomic_inc(&snapshot_device_available); |
25f2f3daa snapshot: Use pm_... |
60 61 |
error = -ENOSYS; goto Unlock; |
1525a2ad7 swsusp: fix error... |
62 63 |
} if(create_basic_memory_bitmaps()) { |
0709db607 swsusp: use GFP_K... |
64 |
atomic_inc(&snapshot_device_available); |
25f2f3daa snapshot: Use pm_... |
65 66 |
error = -ENOMEM; goto Unlock; |
1525a2ad7 swsusp: fix error... |
67 |
} |
6e1819d61 [PATCH] swsusp: u... |
68 69 70 71 72 |
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... |
73 |
/* Hibernating. The image device should be accessible. */ |
915bae9eb [PATCH] swsusp: u... |
74 |
data->swap = swsusp_resume_device ? |
7bf236874 [PATCH] swsusp: D... |
75 |
swap_type_of(swsusp_resume_device, 0, NULL) : -1; |
6e1819d61 [PATCH] swsusp: u... |
76 |
data->mode = O_RDONLY; |
ebae2604f PM: Fix pm_notifi... |
77 |
error = pm_notifier_call_chain(PM_HIBERNATION_PREPARE); |
c3e94d899 Hibernation: Add ... |
78 |
if (error) |
ebae2604f PM: Fix pm_notifi... |
79 |
pm_notifier_call_chain(PM_POST_HIBERNATION); |
6e1819d61 [PATCH] swsusp: u... |
80 |
} else { |
c75108594 PM/Hibernate: Wai... |
81 82 83 84 85 86 |
/* * Resuming. We may need to wait for the image device to * appear. */ wait_for_device_probe(); scsi_complete_async_scans(); |
6e1819d61 [PATCH] swsusp: u... |
87 88 |
data->swap = -1; data->mode = O_WRONLY; |
ebae2604f PM: Fix pm_notifi... |
89 |
error = pm_notifier_call_chain(PM_RESTORE_PREPARE); |
c3e94d899 Hibernation: Add ... |
90 |
if (error) |
ebae2604f PM: Fix pm_notifi... |
91 |
pm_notifier_call_chain(PM_POST_RESTORE); |
c3e94d899 Hibernation: Add ... |
92 |
} |
8440f4b19 PM: Free memory b... |
93 94 |
if (error) { free_basic_memory_bitmaps(); |
c3e94d899 Hibernation: Add ... |
95 |
atomic_inc(&snapshot_device_available); |
8440f4b19 PM: Free memory b... |
96 |
} |
6e1819d61 [PATCH] swsusp: u... |
97 98 |
data->frozen = 0; data->ready = 0; |
eb57c1cf0 Hibernation: Rewo... |
99 |
data->platform_support = 0; |
6e1819d61 [PATCH] swsusp: u... |
100 |
|
25f2f3daa snapshot: Use pm_... |
101 |
Unlock: |
bcda53faf PM / Sleep: Repla... |
102 |
unlock_system_sleep(); |
25f2f3daa snapshot: Use pm_... |
103 104 |
return error; |
6e1819d61 [PATCH] swsusp: u... |
105 106 107 108 109 |
} static int snapshot_release(struct inode *inode, struct file *filp) { struct snapshot_data *data; |
bcda53faf PM / Sleep: Repla... |
110 |
lock_system_sleep(); |
25f2f3daa snapshot: Use pm_... |
111 |
|
6e1819d61 [PATCH] swsusp: u... |
112 |
swsusp_free(); |
74dfd666d swsusp: do not us... |
113 |
free_basic_memory_bitmaps(); |
6e1819d61 [PATCH] swsusp: u... |
114 |
data = filp->private_data; |
d1d241cc2 swsusp: use rbtre... |
115 |
free_all_swap_pages(data->swap); |
9744997a8 PM / Hibernate: M... |
116 117 |
if (data->frozen) { pm_restore_gfp_mask(); |
6e1819d61 [PATCH] swsusp: u... |
118 |
thaw_processes(); |
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 |
|
d3c1b24c5 PM / Hibernate: S... |
180 181 182 183 184 |
res = simple_write_to_buffer(data_of(data->handle), res, &pg_offp, buf, count); if (res > 0) *offp += res; unlock: |
bcda53faf PM / Sleep: Repla... |
185 |
unlock_system_sleep(); |
25f2f3daa snapshot: Use pm_... |
186 |
|
6e1819d61 [PATCH] swsusp: u... |
187 188 |
return res; } |
52d11025d snapshot: Push BK... |
189 190 |
static long snapshot_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
6e1819d61 [PATCH] swsusp: u... |
191 192 193 |
{ int error = 0; struct snapshot_data *data; |
af508b34d Hibernation: Intr... |
194 |
loff_t size; |
3aef83e0e [PATCH] swsusp: u... |
195 |
sector_t offset; |
6e1819d61 [PATCH] swsusp: u... |
196 197 198 199 200 201 202 |
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_... |
203 204 |
if (!mutex_trylock(&pm_mutex)) return -EBUSY; |
6e1819d61 [PATCH] swsusp: u... |
205 |
|
25f2f3daa snapshot: Use pm_... |
206 |
data = filp->private_data; |
52d11025d snapshot: Push BK... |
207 |
|
6e1819d61 [PATCH] swsusp: u... |
208 209 210 211 212 |
switch (cmd) { case SNAPSHOT_FREEZE: if (data->frozen) break; |
1bfcf1304 pm: rework disabl... |
213 |
|
c3e94d899 Hibernation: Add ... |
214 215 216 217 |
printk("Syncing filesystems ... "); sys_sync(); printk("done. "); |
1bfcf1304 pm: rework disabl... |
218 |
error = usermodehelper_disable(); |
b10d91174 PM: introduce hib... |
219 |
if (error) |
1bfcf1304 pm: rework disabl... |
220 221 222 |
break; error = freeze_processes(); |
03afed8bc freezer: clean up... |
223 |
if (error) |
1bfcf1304 pm: rework disabl... |
224 |
usermodehelper_enable(); |
e5b16746f PM / Hibernate: R... |
225 |
else |
6e1819d61 [PATCH] swsusp: u... |
226 227 228 229 |
data->frozen = 1; break; case SNAPSHOT_UNFREEZE: |
2f41dddbb swsusp: Fix userl... |
230 |
if (!data->frozen || data->ready) |
6e1819d61 [PATCH] swsusp: u... |
231 |
break; |
c9e664f1f PM / Hibernate: F... |
232 |
pm_restore_gfp_mask(); |
6e1819d61 [PATCH] swsusp: u... |
233 |
thaw_processes(); |
1bfcf1304 pm: rework disabl... |
234 |
usermodehelper_enable(); |
6e1819d61 [PATCH] swsusp: u... |
235 236 |
data->frozen = 0; break; |
b694e52eb PM / Hibernate: R... |
237 |
case SNAPSHOT_CREATE_IMAGE: |
6e1819d61 [PATCH] swsusp: u... |
238 239 240 241 |
if (data->mode != O_RDONLY || !data->frozen || data->ready) { error = -EPERM; break; } |
c9e664f1f PM / Hibernate: F... |
242 |
pm_restore_gfp_mask(); |
eb57c1cf0 Hibernation: Rewo... |
243 |
error = hibernation_snapshot(data->platform_support); |
97819a262 PM / Hibernate: T... |
244 |
if (!error) { |
cc5d207c8 Hibernation: Corr... |
245 |
error = put_user(in_suspend, (int __user *)arg); |
97819a262 PM / Hibernate: T... |
246 247 248 249 250 251 252 |
if (!error && !freezer_test_done) data->ready = 1; if (freezer_test_done) { freezer_test_done = false; thaw_processes(); } } |
6e1819d61 [PATCH] swsusp: u... |
253 254 255 |
break; case SNAPSHOT_ATOMIC_RESTORE: |
8357376d3 [PATCH] swsusp: I... |
256 |
snapshot_write_finalize(&data->handle); |
6e1819d61 [PATCH] swsusp: u... |
257 258 259 260 261 |
if (data->mode != O_WRONLY || !data->frozen || !snapshot_image_loaded(&data->handle)) { error = -EPERM; break; } |
eb57c1cf0 Hibernation: Rewo... |
262 |
error = hibernation_restore(data->platform_support); |
6e1819d61 [PATCH] swsusp: u... |
263 264 265 266 267 268 269 |
break; case SNAPSHOT_FREE: swsusp_free(); memset(&data->handle, 0, sizeof(struct snapshot_handle)); data->ready = 0; break; |
b694e52eb PM / Hibernate: R... |
270 |
case SNAPSHOT_PREF_IMAGE_SIZE: |
6e1819d61 [PATCH] swsusp: u... |
271 272 |
image_size = arg; break; |
af508b34d Hibernation: Intr... |
273 274 275 276 277 278 279 280 281 |
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... |
282 |
case SNAPSHOT_AVAIL_SWAP_SIZE: |
af508b34d Hibernation: Intr... |
283 284 285 |
size = count_swap_pages(data->swap, 1); size <<= PAGE_SHIFT; error = put_user(size, (loff_t __user *)arg); |
6e1819d61 [PATCH] swsusp: u... |
286 |
break; |
b694e52eb PM / Hibernate: R... |
287 |
case SNAPSHOT_ALLOC_SWAP_PAGE: |
6e1819d61 [PATCH] swsusp: u... |
288 289 290 291 |
if (data->swap < 0 || data->swap >= MAX_SWAPFILES) { error = -ENODEV; break; } |
d1d241cc2 swsusp: use rbtre... |
292 |
offset = alloc_swapdev_block(data->swap); |
6e1819d61 [PATCH] swsusp: u... |
293 294 |
if (offset) { offset <<= PAGE_SHIFT; |
cc5d207c8 Hibernation: Corr... |
295 |
error = put_user(offset, (loff_t __user *)arg); |
6e1819d61 [PATCH] swsusp: u... |
296 297 298 299 300 301 302 303 304 305 |
} else { error = -ENOSPC; } break; case SNAPSHOT_FREE_SWAP_PAGES: if (data->swap < 0 || data->swap >= MAX_SWAPFILES) { error = -ENODEV; break; } |
d1d241cc2 swsusp: use rbtre... |
306 |
free_all_swap_pages(data->swap); |
6e1819d61 [PATCH] swsusp: u... |
307 |
break; |
9b238205b [PATCH] swsusp: a... |
308 309 310 311 312 |
case SNAPSHOT_S2RAM: if (!data->frozen) { error = -EPERM; break; } |
6c961dfb7 PM: Reduce code d... |
313 314 315 316 317 |
/* * 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... |
318 |
data->ready = 0; |
9b238205b [PATCH] swsusp: a... |
319 |
break; |
eb57c1cf0 Hibernation: Rewo... |
320 321 322 323 324 325 326 327 |
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... |
328 |
case SNAPSHOT_SET_SWAP_AREA: |
d1d241cc2 swsusp: use rbtre... |
329 |
if (swsusp_swap_in_use()) { |
37b2ba12d [PATCH] swsusp: a... |
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
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... |
346 |
swdev = new_decode_dev(swap_area.dev); |
37b2ba12d [PATCH] swsusp: a... |
347 348 |
if (swdev) { offset = swap_area.offset; |
7bf236874 [PATCH] swsusp: D... |
349 |
data->swap = swap_type_of(swdev, offset, NULL); |
37b2ba12d [PATCH] swsusp: a... |
350 351 352 353 354 355 356 357 |
if (data->swap < 0) error = -ENODEV; } else { data->swap = -1; error = -EINVAL; } } break; |
6e1819d61 [PATCH] swsusp: u... |
358 359 360 361 |
default: error = -ENOTTY; } |
25f2f3daa snapshot: Use pm_... |
362 363 |
mutex_unlock(&pm_mutex); |
6e1819d61 [PATCH] swsusp: u... |
364 365 |
return error; } |
c336078bf PM / Hibernate: I... |
366 367 368 369 370 371 372 373 374 375 376 377 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 |
#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... |
425 |
static const struct file_operations snapshot_fops = { |
6e1819d61 [PATCH] swsusp: u... |
426 427 428 429 430 |
.open = snapshot_open, .release = snapshot_release, .read = snapshot_read, .write = snapshot_write, .llseek = no_llseek, |
52d11025d snapshot: Push BK... |
431 |
.unlocked_ioctl = snapshot_ioctl, |
c336078bf PM / Hibernate: I... |
432 433 434 |
#ifdef CONFIG_COMPAT .compat_ioctl = snapshot_compat_ioctl, #endif |
6e1819d61 [PATCH] swsusp: u... |
435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
}; 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); |