Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[pandora-kernel.git] / kernel / power / user.c
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 #include <linux/console.h>
23 #include <linux/cpu.h>
24
25 #include <asm/uaccess.h>
26
27 #include "power.h"
28
29 #define SNAPSHOT_MINOR  231
30
31 static struct snapshot_data {
32         struct snapshot_handle handle;
33         int swap;
34         struct bitmap_page *bitmap;
35         int mode;
36         char frozen;
37         char ready;
38 } snapshot_state;
39
40 static atomic_t device_available = ATOMIC_INIT(1);
41
42 static int snapshot_open(struct inode *inode, struct file *filp)
43 {
44         struct snapshot_data *data;
45
46         if (!atomic_add_unless(&device_available, -1, 0))
47                 return -EBUSY;
48
49         if ((filp->f_flags & O_ACCMODE) == O_RDWR)
50                 return -ENOSYS;
51
52         nonseekable_open(inode, filp);
53         data = &snapshot_state;
54         filp->private_data = data;
55         memset(&data->handle, 0, sizeof(struct snapshot_handle));
56         if ((filp->f_flags & O_ACCMODE) == O_RDONLY) {
57                 data->swap = swsusp_resume_device ? swap_type_of(swsusp_resume_device) : -1;
58                 data->mode = O_RDONLY;
59         } else {
60                 data->swap = -1;
61                 data->mode = O_WRONLY;
62         }
63         data->bitmap = NULL;
64         data->frozen = 0;
65         data->ready = 0;
66
67         return 0;
68 }
69
70 static int snapshot_release(struct inode *inode, struct file *filp)
71 {
72         struct snapshot_data *data;
73
74         swsusp_free();
75         data = filp->private_data;
76         free_all_swap_pages(data->swap, data->bitmap);
77         free_bitmap(data->bitmap);
78         if (data->frozen) {
79                 down(&pm_sem);
80                 thaw_processes();
81                 enable_nonboot_cpus();
82                 up(&pm_sem);
83         }
84         atomic_inc(&device_available);
85         return 0;
86 }
87
88 static ssize_t snapshot_read(struct file *filp, char __user *buf,
89                              size_t count, loff_t *offp)
90 {
91         struct snapshot_data *data;
92         ssize_t res;
93
94         data = filp->private_data;
95         res = snapshot_read_next(&data->handle, count);
96         if (res > 0) {
97                 if (copy_to_user(buf, data_of(data->handle), res))
98                         res = -EFAULT;
99                 else
100                         *offp = data->handle.offset;
101         }
102         return res;
103 }
104
105 static ssize_t snapshot_write(struct file *filp, const char __user *buf,
106                               size_t count, loff_t *offp)
107 {
108         struct snapshot_data *data;
109         ssize_t res;
110
111         data = filp->private_data;
112         res = snapshot_write_next(&data->handle, count);
113         if (res > 0) {
114                 if (copy_from_user(data_of(data->handle), buf, res))
115                         res = -EFAULT;
116                 else
117                         *offp = data->handle.offset;
118         }
119         return res;
120 }
121
122 static int snapshot_ioctl(struct inode *inode, struct file *filp,
123                           unsigned int cmd, unsigned long arg)
124 {
125         int error = 0;
126         struct snapshot_data *data;
127         loff_t offset, avail;
128
129         if (_IOC_TYPE(cmd) != SNAPSHOT_IOC_MAGIC)
130                 return -ENOTTY;
131         if (_IOC_NR(cmd) > SNAPSHOT_IOC_MAXNR)
132                 return -ENOTTY;
133         if (!capable(CAP_SYS_ADMIN))
134                 return -EPERM;
135
136         data = filp->private_data;
137
138         switch (cmd) {
139
140         case SNAPSHOT_FREEZE:
141                 if (data->frozen)
142                         break;
143                 down(&pm_sem);
144                 error = disable_nonboot_cpus();
145                 if (!error) {
146                         error = freeze_processes();
147                         if (error) {
148                                 thaw_processes();
149                                 enable_nonboot_cpus();
150                                 error = -EBUSY;
151                         }
152                 }
153                 up(&pm_sem);
154                 if (!error)
155                         data->frozen = 1;
156                 break;
157
158         case SNAPSHOT_UNFREEZE:
159                 if (!data->frozen)
160                         break;
161                 down(&pm_sem);
162                 thaw_processes();
163                 enable_nonboot_cpus();
164                 up(&pm_sem);
165                 data->frozen = 0;
166                 break;
167
168         case SNAPSHOT_ATOMIC_SNAPSHOT:
169                 if (data->mode != O_RDONLY || !data->frozen  || data->ready) {
170                         error = -EPERM;
171                         break;
172                 }
173                 down(&pm_sem);
174                 /* Free memory before shutting down devices. */
175                 error = swsusp_shrink_memory();
176                 if (!error) {
177                         suspend_console();
178                         error = device_suspend(PMSG_FREEZE);
179                         if (!error) {
180                                 in_suspend = 1;
181                                 error = swsusp_suspend();
182                                 device_resume();
183                         }
184                         resume_console();
185                 }
186                 up(&pm_sem);
187                 if (!error)
188                         error = put_user(in_suspend, (unsigned int __user *)arg);
189                 if (!error)
190                         data->ready = 1;
191                 break;
192
193         case SNAPSHOT_ATOMIC_RESTORE:
194                 if (data->mode != O_WRONLY || !data->frozen ||
195                     !snapshot_image_loaded(&data->handle)) {
196                         error = -EPERM;
197                         break;
198                 }
199                 snapshot_free_unused_memory(&data->handle);
200                 down(&pm_sem);
201                 pm_prepare_console();
202                 suspend_console();
203                 error = device_suspend(PMSG_PRETHAW);
204                 if (!error) {
205                         error = swsusp_resume();
206                         device_resume();
207                 }
208                 resume_console();
209                 pm_restore_console();
210                 up(&pm_sem);
211                 break;
212
213         case SNAPSHOT_FREE:
214                 swsusp_free();
215                 memset(&data->handle, 0, sizeof(struct snapshot_handle));
216                 data->ready = 0;
217                 break;
218
219         case SNAPSHOT_SET_IMAGE_SIZE:
220                 image_size = arg;
221                 break;
222
223         case SNAPSHOT_AVAIL_SWAP:
224                 avail = count_swap_pages(data->swap, 1);
225                 avail <<= PAGE_SHIFT;
226                 error = put_user(avail, (loff_t __user *)arg);
227                 break;
228
229         case SNAPSHOT_GET_SWAP_PAGE:
230                 if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
231                         error = -ENODEV;
232                         break;
233                 }
234                 if (!data->bitmap) {
235                         data->bitmap = alloc_bitmap(count_swap_pages(data->swap, 0));
236                         if (!data->bitmap) {
237                                 error = -ENOMEM;
238                                 break;
239                         }
240                 }
241                 offset = alloc_swap_page(data->swap, data->bitmap);
242                 if (offset) {
243                         offset <<= PAGE_SHIFT;
244                         error = put_user(offset, (loff_t __user *)arg);
245                 } else {
246                         error = -ENOSPC;
247                 }
248                 break;
249
250         case SNAPSHOT_FREE_SWAP_PAGES:
251                 if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
252                         error = -ENODEV;
253                         break;
254                 }
255                 free_all_swap_pages(data->swap, data->bitmap);
256                 free_bitmap(data->bitmap);
257                 data->bitmap = NULL;
258                 break;
259
260         case SNAPSHOT_SET_SWAP_FILE:
261                 if (!data->bitmap) {
262                         /*
263                          * User space encodes device types as two-byte values,
264                          * so we need to recode them
265                          */
266                         if (old_decode_dev(arg)) {
267                                 data->swap = swap_type_of(old_decode_dev(arg));
268                                 if (data->swap < 0)
269                                         error = -ENODEV;
270                         } else {
271                                 data->swap = -1;
272                                 error = -EINVAL;
273                         }
274                 } else {
275                         error = -EPERM;
276                 }
277                 break;
278
279         case SNAPSHOT_S2RAM:
280                 if (!data->frozen) {
281                         error = -EPERM;
282                         break;
283                 }
284
285                 if (down_trylock(&pm_sem)) {
286                         error = -EBUSY;
287                         break;
288                 }
289
290                 if (pm_ops->prepare) {
291                         error = pm_ops->prepare(PM_SUSPEND_MEM);
292                         if (error)
293                                 goto OutS3;
294                 }
295
296                 /* Put devices to sleep */
297                 suspend_console();
298                 error = device_suspend(PMSG_SUSPEND);
299                 if (error) {
300                         printk(KERN_ERR "Failed to suspend some devices.\n");
301                 } else {
302                         /* Enter S3, system is already frozen */
303                         suspend_enter(PM_SUSPEND_MEM);
304
305                         /* Wake up devices */
306                         device_resume();
307                 }
308                 resume_console();
309                 if (pm_ops->finish)
310                         pm_ops->finish(PM_SUSPEND_MEM);
311
312 OutS3:
313                 up(&pm_sem);
314                 break;
315
316         default:
317                 error = -ENOTTY;
318
319         }
320
321         return error;
322 }
323
324 static struct file_operations snapshot_fops = {
325         .open = snapshot_open,
326         .release = snapshot_release,
327         .read = snapshot_read,
328         .write = snapshot_write,
329         .llseek = no_llseek,
330         .ioctl = snapshot_ioctl,
331 };
332
333 static struct miscdevice snapshot_device = {
334         .minor = SNAPSHOT_MINOR,
335         .name = "snapshot",
336         .fops = &snapshot_fops,
337 };
338
339 static int __init snapshot_device_init(void)
340 {
341         return misc_register(&snapshot_device);
342 };
343
344 device_initcall(snapshot_device_init);