Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
436ce71
Documentation
arch
block
crypto
drivers
fs
include
init
ipc
kernel
irq
power
Kconfig
Makefile
console.c
disk.c
main.c
pm.c
power.h
poweroff.c
process.c
snapshot.c
swap.c
swsusp.c
user.c
time
.gitignore
Kconfig.hz
Kconfig.preempt
Makefile
acct.c
audit.c
audit.h
auditfilter.c
auditsc.c
capability.c
compat.c
configs.c
cpu.c
cpuset.c
delayacct.c
dma.c
exec_domain.c
exit.c
extable.c
fork.c
futex.c
futex_compat.c
hrtimer.c
itimer.c
kallsyms.c
kexec.c
kfifo.c
kmod.c
kprobes.c
ksysfs.c
kthread.c
latency.c
lockdep.c
lockdep_internals.h
lockdep_proc.c
module.c
mutex-debug.c
mutex-debug.h
mutex.c
mutex.h
nsproxy.c
panic.c
params.c
pid.c
posix-cpu-timers.c
posix-timers.c
printk.c
profile.c
ptrace.c
rcupdate.c
rcutorture.c
relay.c
resource.c
rtmutex-debug.c
rtmutex-debug.h
rtmutex-tester.c
rtmutex.c
rtmutex.h
rtmutex_common.h
rwsem.c
sched.c
seccomp.c
signal.c
softirq.c
softlockup.c
spinlock.c
srcu.c
stacktrace.c
stop_machine.c
sys.c
sys_ni.c
sysctl.c
taskstats.c
time.c
timer.c
tsacct.c
uid16.c
user.c
utsname.c
utsname_sysctl.c
wait.c
workqueue.c
lib
mm
net
scripts
security
sound
usr
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
MAINTAINERS
Makefile
README
REPORTING-BUGS
Breadcrumbs
linux
/
kernel
/
power
/
user.c
Blame
Blame
Latest commit
History
History
481 lines (412 loc) · 9.39 KB
Breadcrumbs
linux
/
kernel
/
power
/
user.c
Top
File metadata and controls
Code
Blame
481 lines (412 loc) · 9.39 KB
Raw
/* * 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> #include <linux/reboot.h> #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> #include <linux/console.h> #include <linux/cpu.h> #include <linux/freezer.h> #include <asm/uaccess.h> #include "power.h" #define SNAPSHOT_MINOR 231 static struct snapshot_data { struct snapshot_handle handle; int swap; struct bitmap_page *bitmap; int mode; char frozen; char ready; char platform_suspend; } snapshot_state; static atomic_t device_available = ATOMIC_INIT(1); static int snapshot_open(struct inode *inode, struct file *filp) { struct snapshot_data *data; if (!atomic_add_unless(&device_available, -1, 0)) return -EBUSY; if ((filp->f_flags & O_ACCMODE) == O_RDWR) return -ENOSYS; 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) { data->swap = swsusp_resume_device ? swap_type_of(swsusp_resume_device, 0, NULL) : -1; data->mode = O_RDONLY; } else { data->swap = -1; data->mode = O_WRONLY; } data->bitmap = NULL; data->frozen = 0; data->ready = 0; data->platform_suspend = 0; return 0; } static int snapshot_release(struct inode *inode, struct file *filp) { struct snapshot_data *data; swsusp_free(); data = filp->private_data; free_all_swap_pages(data->swap, data->bitmap); free_bitmap(data->bitmap); if (data->frozen) { mutex_lock(&pm_mutex); thaw_processes(); enable_nonboot_cpus(); mutex_unlock(&pm_mutex); } atomic_inc(&device_available); 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; data = filp->private_data; res = snapshot_read_next(&data->handle, count); if (res > 0) { if (copy_to_user(buf, data_of(data->handle), res)) res = -EFAULT; else *offp = data->handle.offset; } 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; data = filp->private_data; res = snapshot_write_next(&data->handle, count); if (res > 0) { if (copy_from_user(data_of(data->handle), buf, res)) res = -EFAULT; else *offp = data->handle.offset; } return res; } static inline int platform_prepare(void) { int error = 0; if (pm_ops && pm_ops->prepare) error = pm_ops->prepare(PM_SUSPEND_DISK); return error; } static inline void platform_finish(void) { if (pm_ops && pm_ops->finish) pm_ops->finish(PM_SUSPEND_DISK); } static inline int snapshot_suspend(int platform_suspend) { int error; mutex_lock(&pm_mutex); /* Free memory before shutting down devices. */ error = swsusp_shrink_memory(); if (error) goto Finish; if (platform_suspend) { error = platform_prepare(); if (error) goto Finish; } suspend_console(); error = device_suspend(PMSG_FREEZE); if (error) goto Resume_devices; error = disable_nonboot_cpus(); if (!error) { in_suspend = 1; error = swsusp_suspend(); } enable_nonboot_cpus(); Resume_devices: if (platform_suspend) platform_finish(); device_resume(); resume_console(); Finish: mutex_unlock(&pm_mutex); return error; } static inline int snapshot_restore(int platform_suspend) { int error; mutex_lock(&pm_mutex); pm_prepare_console(); if (platform_suspend) { error = platform_prepare(); if (error) goto Finish; } suspend_console(); error = device_suspend(PMSG_PRETHAW); if (error) goto Resume_devices; error = disable_nonboot_cpus(); if (!error) error = swsusp_resume(); enable_nonboot_cpus(); Resume_devices: if (platform_suspend) platform_finish(); device_resume(); resume_console(); Finish: pm_restore_console(); mutex_unlock(&pm_mutex); return error; } static int snapshot_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int error = 0; struct snapshot_data *data; loff_t avail; sector_t offset; 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; data = filp->private_data; switch (cmd) { case SNAPSHOT_FREEZE: if (data->frozen) break; mutex_lock(&pm_mutex); if (freeze_processes()) { thaw_processes(); error = -EBUSY; } mutex_unlock(&pm_mutex); if (!error) data->frozen = 1; break; case SNAPSHOT_UNFREEZE: if (!data->frozen) break; mutex_lock(&pm_mutex); thaw_processes(); mutex_unlock(&pm_mutex); data->frozen = 0; break; case SNAPSHOT_ATOMIC_SNAPSHOT: if (data->mode != O_RDONLY || !data->frozen || data->ready) { error = -EPERM; break; } error = snapshot_suspend(data->platform_suspend); if (!error) error = put_user(in_suspend, (unsigned int __user *)arg); if (!error) data->ready = 1; break; case SNAPSHOT_ATOMIC_RESTORE: snapshot_write_finalize(&data->handle); if (data->mode != O_WRONLY || !data->frozen || !snapshot_image_loaded(&data->handle)) { error = -EPERM; break; } error = snapshot_restore(data->platform_suspend); break; case SNAPSHOT_FREE: swsusp_free(); memset(&data->handle, 0, sizeof(struct snapshot_handle)); data->ready = 0; break; case SNAPSHOT_SET_IMAGE_SIZE: image_size = arg; break; case SNAPSHOT_AVAIL_SWAP: avail = count_swap_pages(data->swap, 1); avail <<= PAGE_SHIFT; error = put_user(avail, (loff_t __user *)arg); break; case SNAPSHOT_GET_SWAP_PAGE: if (data->swap < 0 || data->swap >= MAX_SWAPFILES) { error = -ENODEV; break; } if (!data->bitmap) { data->bitmap = alloc_bitmap(count_swap_pages(data->swap, 0)); if (!data->bitmap) { error = -ENOMEM; break; } } offset = alloc_swapdev_block(data->swap, data->bitmap); if (offset) { offset <<= PAGE_SHIFT; error = put_user(offset, (sector_t __user *)arg); } else { error = -ENOSPC; } break; case SNAPSHOT_FREE_SWAP_PAGES: if (data->swap < 0 || data->swap >= MAX_SWAPFILES) { error = -ENODEV; break; } free_all_swap_pages(data->swap, data->bitmap); free_bitmap(data->bitmap); data->bitmap = NULL; break; case SNAPSHOT_SET_SWAP_FILE: if (!data->bitmap) { /* * User space encodes device types as two-byte values, * so we need to recode them */ if (old_decode_dev(arg)) { data->swap = swap_type_of(old_decode_dev(arg), 0, NULL); if (data->swap < 0) error = -ENODEV; } else { data->swap = -1; error = -EINVAL; } } else { error = -EPERM; } break; case SNAPSHOT_S2RAM: if (!pm_ops) { error = -ENOSYS; break; } if (!data->frozen) { error = -EPERM; break; } if (!mutex_trylock(&pm_mutex)) { error = -EBUSY; break; } if (pm_ops->prepare) { error = pm_ops->prepare(PM_SUSPEND_MEM); if (error) goto OutS3; } /* Put devices to sleep */ suspend_console(); error = device_suspend(PMSG_SUSPEND); if (error) { printk(KERN_ERR "Failed to suspend some devices.\n"); } else { error = disable_nonboot_cpus(); if (!error) { /* Enter S3, system is already frozen */ suspend_enter(PM_SUSPEND_MEM); enable_nonboot_cpus(); } /* Wake up devices */ device_resume(); } resume_console(); if (pm_ops->finish) pm_ops->finish(PM_SUSPEND_MEM); OutS3: mutex_unlock(&pm_mutex); break; case SNAPSHOT_PMOPS: error = -EINVAL; switch (arg) { case PMOPS_PREPARE: if (pm_ops && pm_ops->enter) { data->platform_suspend = 1; error = 0; } else { error = -ENOSYS; } break; case PMOPS_ENTER: if (data->platform_suspend) { kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); error = pm_ops->enter(PM_SUSPEND_DISK); error = 0; } break; case PMOPS_FINISH: if (data->platform_suspend) error = 0; break; default: printk(KERN_ERR "SNAPSHOT_PMOPS: invalid argument %ld\n", arg); } break; case SNAPSHOT_SET_SWAP_AREA: if (data->bitmap) { 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 */ swdev = old_decode_dev(swap_area.dev); if (swdev) { offset = swap_area.offset; data->swap = swap_type_of(swdev, offset, NULL); if (data->swap < 0) error = -ENODEV; } else { data->swap = -1; error = -EINVAL; } } break; default: error = -ENOTTY; } return error; } static const struct file_operations snapshot_fops = { .open = snapshot_open, .release = snapshot_release, .read = snapshot_read, .write = snapshot_write, .llseek = no_llseek, .ioctl = snapshot_ioctl, }; 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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
You can’t perform that action at this time.