aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/power/user.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2006-03-23 06:00:03 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-23 10:38:07 -0500
commit6e1819d615f24ce0726a7d0bd3dd0152d7b21654 (patch)
treeabc68747446e8241a1a7103882b9f6b6e24fa274 /kernel/power/user.c
parent543cc27d09643640cbc34189c03a40beb8227aef (diff)
[PATCH] swsusp: userland interface
This patch introduces a user space interface for swsusp. The interface is based on a special character device, called the snapshot device, that allows user space processes to perform suspend and resume-related operations with the help of some ioctls and the read()/write() functions.  Additionally it allows these processes to allocate free swap pages from a selected swap partition, called the resume partition, so that they know which sectors of the resume partition are available to them. The interface uses the same low-level system memory snapshot-handling functions that are used by the built-it swap-writing/reading code of swsusp. The interface documentation is included in the patch. The patch assumes that the major and minor numbers of the snapshot device will be 10 (ie. misc device) and 231, the registration of which has already been requested. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Pavel Machek <pavel@ucw.cz> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel/power/user.c')
-rw-r--r--kernel/power/user.c301
1 files changed, 301 insertions, 0 deletions
diff --git a/kernel/power/user.c b/kernel/power/user.c
new file mode 100644
index 000000000000..8cabc405ca10
--- /dev/null
+++ b/kernel/power/user.c
@@ -0,0 +1,301 @@
1/*
2 * linux/kernel/power/user.c
3 *
4 * This file provides the user space interface for software suspend/resume.
5 *
6 * Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl>
7 *
8 * This file is released under the GPLv2.
9 *
10 */
11
12#include <linux/suspend.h>
13#include <linux/syscalls.h>
14#include <linux/string.h>
15#include <linux/device.h>
16#include <linux/miscdevice.h>
17#include <linux/mm.h>
18#include <linux/swap.h>
19#include <linux/swapops.h>
20#include <linux/pm.h>
21#include <linux/fs.h>
22
23#include <asm/uaccess.h>
24
25#include "power.h"
26
27#define SNAPSHOT_MINOR 231
28
29static struct snapshot_data {
30 struct snapshot_handle handle;
31 int swap;
32 struct bitmap_page *bitmap;
33 int mode;
34 char frozen;
35 char ready;
36} snapshot_state;
37
38static atomic_t device_available = ATOMIC_INIT(1);
39
40static int snapshot_open(struct inode *inode, struct file *filp)
41{
42 struct snapshot_data *data;
43
44 if (!atomic_add_unless(&device_available, -1, 0))
45 return -EBUSY;
46
47 if ((filp->f_flags & O_ACCMODE) == O_RDWR)
48 return -ENOSYS;
49
50 nonseekable_open(inode, filp);
51 data = &snapshot_state;
52 filp->private_data = data;
53 memset(&data->handle, 0, sizeof(struct snapshot_handle));
54 if ((filp->f_flags & O_ACCMODE) == O_RDONLY) {
55 data->swap = swsusp_resume_device ? swap_type_of(swsusp_resume_device) : -1;
56 data->mode = O_RDONLY;
57 } else {
58 data->swap = -1;
59 data->mode = O_WRONLY;
60 }
61 data->bitmap = NULL;
62 data->frozen = 0;
63 data->ready = 0;
64
65 return 0;
66}
67
68static int snapshot_release(struct inode *inode, struct file *filp)
69{
70 struct snapshot_data *data;
71
72 swsusp_free();
73 data = filp->private_data;
74 free_all_swap_pages(data->swap, data->bitmap);
75 free_bitmap(data->bitmap);
76 if (data->frozen) {
77 down(&pm_sem);
78 thaw_processes();
79 enable_nonboot_cpus();
80 up(&pm_sem);
81 }
82 atomic_inc(&device_available);
83 return 0;
84}
85
86static ssize_t snapshot_read(struct file *filp, char __user *buf,
87 size_t count, loff_t *offp)
88{
89 struct snapshot_data *data;
90 ssize_t res;
91
92 data = filp->private_data;
93 res = snapshot_read_next(&data->handle, count);
94 if (res > 0) {
95 if (copy_to_user(buf, data_of(data->handle), res))
96 res = -EFAULT;
97 else
98 *offp = data->handle.offset;
99 }
100 return res;
101}
102
103static ssize_t snapshot_write(struct file *filp, const char __user *buf,
104 size_t count, loff_t *offp)
105{
106 struct snapshot_data *data;
107 ssize_t res;
108
109 data = filp->private_data;
110 res = snapshot_write_next(&data->handle, count);
111 if (res > 0) {
112 if (copy_from_user(data_of(data->handle), buf, res))
113 res = -EFAULT;
114 else
115 *offp = data->handle.offset;
116 }
117 return res;
118}
119
120static int snapshot_ioctl(struct inode *inode, struct file *filp,
121 unsigned int cmd, unsigned long arg)
122{
123 int error = 0;
124 struct snapshot_data *data;
125 loff_t offset, avail;
126
127 if (_IOC_TYPE(cmd) != SNAPSHOT_IOC_MAGIC)
128 return -ENOTTY;
129 if (_IOC_NR(cmd) > SNAPSHOT_IOC_MAXNR)
130 return -ENOTTY;
131 if (!capable(CAP_SYS_ADMIN))
132 return -EPERM;
133
134 data = filp->private_data;
135
136 switch (cmd) {
137
138 case SNAPSHOT_FREEZE:
139 if (data->frozen)
140 break;
141 sys_sync();
142 down(&pm_sem);
143 pm_prepare_console();
144 disable_nonboot_cpus();
145 if (freeze_processes()) {
146 thaw_processes();
147 enable_nonboot_cpus();
148 pm_restore_console();
149 error = -EBUSY;
150 }
151 up(&pm_sem);
152 if (!error)
153 data->frozen = 1;
154 break;
155
156 case SNAPSHOT_UNFREEZE:
157 if (!data->frozen)
158 break;
159 down(&pm_sem);
160 thaw_processes();
161 enable_nonboot_cpus();
162 pm_restore_console();
163 up(&pm_sem);
164 data->frozen = 0;
165 break;
166
167 case SNAPSHOT_ATOMIC_SNAPSHOT:
168 if (data->mode != O_RDONLY || !data->frozen || data->ready) {
169 error = -EPERM;
170 break;
171 }
172 down(&pm_sem);
173 /* Free memory before shutting down devices. */
174 error = swsusp_shrink_memory();
175 if (!error) {
176 error = device_suspend(PMSG_FREEZE);
177 if (!error) {
178 in_suspend = 1;
179 error = swsusp_suspend();
180 device_resume();
181 }
182 }
183 up(&pm_sem);
184 if (!error)
185 error = put_user(in_suspend, (unsigned int __user *)arg);
186 if (!error)
187 data->ready = 1;
188 break;
189
190 case SNAPSHOT_ATOMIC_RESTORE:
191 if (data->mode != O_WRONLY || !data->frozen ||
192 !snapshot_image_loaded(&data->handle)) {
193 error = -EPERM;
194 break;
195 }
196 down(&pm_sem);
197 pm_prepare_console();
198 error = device_suspend(PMSG_FREEZE);
199 if (!error) {
200 error = swsusp_resume();
201 device_resume();
202 }
203 pm_restore_console();
204 up(&pm_sem);
205 break;
206
207 case SNAPSHOT_FREE:
208 swsusp_free();
209 memset(&data->handle, 0, sizeof(struct snapshot_handle));
210 data->ready = 0;
211 break;
212
213 case SNAPSHOT_SET_IMAGE_SIZE:
214 image_size = arg;
215 break;
216
217 case SNAPSHOT_AVAIL_SWAP:
218 avail = count_swap_pages(data->swap, 1);
219 avail <<= PAGE_SHIFT;
220 error = put_user(avail, (loff_t __user *)arg);
221 break;
222
223 case SNAPSHOT_GET_SWAP_PAGE:
224 if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
225 error = -ENODEV;
226 break;
227 }
228 if (!data->bitmap) {
229 data->bitmap = alloc_bitmap(count_swap_pages(data->swap, 0));
230 if (!data->bitmap) {
231 error = -ENOMEM;
232 break;
233 }
234 }
235 offset = alloc_swap_page(data->swap, data->bitmap);
236 if (offset) {
237 offset <<= PAGE_SHIFT;
238 error = put_user(offset, (loff_t __user *)arg);
239 } else {
240 error = -ENOSPC;
241 }
242 break;
243
244 case SNAPSHOT_FREE_SWAP_PAGES:
245 if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
246 error = -ENODEV;
247 break;
248 }
249 free_all_swap_pages(data->swap, data->bitmap);
250 free_bitmap(data->bitmap);
251 data->bitmap = NULL;
252 break;
253
254 case SNAPSHOT_SET_SWAP_FILE:
255 if (!data->bitmap) {
256 /*
257 * User space encodes device types as two-byte values,
258 * so we need to recode them
259 */
260 if (old_decode_dev(arg)) {
261 data->swap = swap_type_of(old_decode_dev(arg));
262 if (data->swap < 0)
263 error = -ENODEV;
264 } else {
265 data->swap = -1;
266 error = -EINVAL;
267 }
268 } else {
269 error = -EPERM;
270 }
271 break;
272
273 default:
274 error = -ENOTTY;
275
276 }
277
278 return error;
279}
280
281static struct file_operations snapshot_fops = {
282 .open = snapshot_open,
283 .release = snapshot_release,
284 .read = snapshot_read,
285 .write = snapshot_write,
286 .llseek = no_llseek,
287 .ioctl = snapshot_ioctl,
288};
289
290static struct miscdevice snapshot_device = {
291 .minor = SNAPSHOT_MINOR,
292 .name = "snapshot",
293 .fops = &snapshot_fops,
294};
295
296static int __init snapshot_device_init(void)
297{
298 return misc_register(&snapshot_device);
299};
300
301device_initcall(snapshot_device_init);