]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - kernel/power/user.c
swsusp: fix error paths in snapshot_open
[linux-2.6-omap-h63xx.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/reboot.h>
15 #include <linux/string.h>
16 #include <linux/device.h>
17 #include <linux/miscdevice.h>
18 #include <linux/mm.h>
19 #include <linux/swap.h>
20 #include <linux/swapops.h>
21 #include <linux/pm.h>
22 #include <linux/fs.h>
23 #include <linux/console.h>
24 #include <linux/cpu.h>
25 #include <linux/freezer.h>
26
27 #include <asm/uaccess.h>
28
29 #include "power.h"
30
31 #define SNAPSHOT_MINOR  231
32
33 static struct snapshot_data {
34         struct snapshot_handle handle;
35         int swap;
36         struct bitmap_page *bitmap;
37         int mode;
38         char frozen;
39         char ready;
40         char platform_suspend;
41 } snapshot_state;
42
43 static atomic_t device_available = ATOMIC_INIT(1);
44
45 static int snapshot_open(struct inode *inode, struct file *filp)
46 {
47         struct snapshot_data *data;
48
49         if (!atomic_add_unless(&device_available, -1, 0))
50                 return -EBUSY;
51
52         if ((filp->f_flags & O_ACCMODE) == O_RDWR) {
53                 atomic_inc(&device_available);
54                 return -ENOSYS;
55         }
56         if(create_basic_memory_bitmaps()) {
57                 atomic_inc(&device_available);
58                 return -ENOMEM;
59         }
60         nonseekable_open(inode, filp);
61         data = &snapshot_state;
62         filp->private_data = data;
63         memset(&data->handle, 0, sizeof(struct snapshot_handle));
64         if ((filp->f_flags & O_ACCMODE) == O_RDONLY) {
65                 data->swap = swsusp_resume_device ?
66                         swap_type_of(swsusp_resume_device, 0, NULL) : -1;
67                 data->mode = O_RDONLY;
68         } else {
69                 data->swap = -1;
70                 data->mode = O_WRONLY;
71         }
72         data->bitmap = NULL;
73         data->frozen = 0;
74         data->ready = 0;
75         data->platform_suspend = 0;
76
77         return 0;
78 }
79
80 static int snapshot_release(struct inode *inode, struct file *filp)
81 {
82         struct snapshot_data *data;
83
84         swsusp_free();
85         free_basic_memory_bitmaps();
86         data = filp->private_data;
87         free_all_swap_pages(data->swap, data->bitmap);
88         free_bitmap(data->bitmap);
89         if (data->frozen) {
90                 mutex_lock(&pm_mutex);
91                 thaw_processes();
92                 enable_nonboot_cpus();
93                 mutex_unlock(&pm_mutex);
94         }
95         atomic_inc(&device_available);
96         return 0;
97 }
98
99 static ssize_t snapshot_read(struct file *filp, char __user *buf,
100                              size_t count, loff_t *offp)
101 {
102         struct snapshot_data *data;
103         ssize_t res;
104
105         data = filp->private_data;
106         res = snapshot_read_next(&data->handle, count);
107         if (res > 0) {
108                 if (copy_to_user(buf, data_of(data->handle), res))
109                         res = -EFAULT;
110                 else
111                         *offp = data->handle.offset;
112         }
113         return res;
114 }
115
116 static ssize_t snapshot_write(struct file *filp, const char __user *buf,
117                               size_t count, loff_t *offp)
118 {
119         struct snapshot_data *data;
120         ssize_t res;
121
122         data = filp->private_data;
123         res = snapshot_write_next(&data->handle, count);
124         if (res > 0) {
125                 if (copy_from_user(data_of(data->handle), buf, res))
126                         res = -EFAULT;
127                 else
128                         *offp = data->handle.offset;
129         }
130         return res;
131 }
132
133 static inline int platform_prepare(void)
134 {
135         int error = 0;
136
137         if (pm_ops && pm_ops->prepare)
138                 error = pm_ops->prepare(PM_SUSPEND_DISK);
139
140         return error;
141 }
142
143 static inline void platform_finish(void)
144 {
145         if (pm_ops && pm_ops->finish)
146                 pm_ops->finish(PM_SUSPEND_DISK);
147 }
148
149 static inline int snapshot_suspend(int platform_suspend)
150 {
151         int error;
152
153         mutex_lock(&pm_mutex);
154         /* Free memory before shutting down devices. */
155         error = swsusp_shrink_memory();
156         if (error)
157                 goto Finish;
158
159         if (platform_suspend) {
160                 error = platform_prepare();
161                 if (error)
162                         goto Finish;
163         }
164         suspend_console();
165         error = device_suspend(PMSG_FREEZE);
166         if (error)
167                 goto Resume_devices;
168
169         error = disable_nonboot_cpus();
170         if (!error) {
171                 in_suspend = 1;
172                 error = swsusp_suspend();
173         }
174         enable_nonboot_cpus();
175  Resume_devices:
176         if (platform_suspend)
177                 platform_finish();
178
179         device_resume();
180         resume_console();
181  Finish:
182         mutex_unlock(&pm_mutex);
183         return error;
184 }
185
186 static inline int snapshot_restore(int platform_suspend)
187 {
188         int error;
189
190         mutex_lock(&pm_mutex);
191         pm_prepare_console();
192         if (platform_suspend) {
193                 error = platform_prepare();
194                 if (error)
195                         goto Finish;
196         }
197         suspend_console();
198         error = device_suspend(PMSG_PRETHAW);
199         if (error)
200                 goto Resume_devices;
201
202         error = disable_nonboot_cpus();
203         if (!error)
204                 error = swsusp_resume();
205
206         enable_nonboot_cpus();
207  Resume_devices:
208         if (platform_suspend)
209                 platform_finish();
210
211         device_resume();
212         resume_console();
213  Finish:
214         pm_restore_console();
215         mutex_unlock(&pm_mutex);
216         return error;
217 }
218
219 static int snapshot_ioctl(struct inode *inode, struct file *filp,
220                           unsigned int cmd, unsigned long arg)
221 {
222         int error = 0;
223         struct snapshot_data *data;
224         loff_t avail;
225         sector_t offset;
226
227         if (_IOC_TYPE(cmd) != SNAPSHOT_IOC_MAGIC)
228                 return -ENOTTY;
229         if (_IOC_NR(cmd) > SNAPSHOT_IOC_MAXNR)
230                 return -ENOTTY;
231         if (!capable(CAP_SYS_ADMIN))
232                 return -EPERM;
233
234         data = filp->private_data;
235
236         switch (cmd) {
237
238         case SNAPSHOT_FREEZE:
239                 if (data->frozen)
240                         break;
241                 mutex_lock(&pm_mutex);
242                 if (freeze_processes()) {
243                         thaw_processes();
244                         error = -EBUSY;
245                 }
246                 mutex_unlock(&pm_mutex);
247                 if (!error)
248                         data->frozen = 1;
249                 break;
250
251         case SNAPSHOT_UNFREEZE:
252                 if (!data->frozen)
253                         break;
254                 mutex_lock(&pm_mutex);
255                 thaw_processes();
256                 mutex_unlock(&pm_mutex);
257                 data->frozen = 0;
258                 break;
259
260         case SNAPSHOT_ATOMIC_SNAPSHOT:
261                 if (data->mode != O_RDONLY || !data->frozen  || data->ready) {
262                         error = -EPERM;
263                         break;
264                 }
265                 error = snapshot_suspend(data->platform_suspend);
266                 if (!error)
267                         error = put_user(in_suspend, (unsigned int __user *)arg);
268                 if (!error)
269                         data->ready = 1;
270                 break;
271
272         case SNAPSHOT_ATOMIC_RESTORE:
273                 snapshot_write_finalize(&data->handle);
274                 if (data->mode != O_WRONLY || !data->frozen ||
275                     !snapshot_image_loaded(&data->handle)) {
276                         error = -EPERM;
277                         break;
278                 }
279                 error = snapshot_restore(data->platform_suspend);
280                 break;
281
282         case SNAPSHOT_FREE:
283                 swsusp_free();
284                 memset(&data->handle, 0, sizeof(struct snapshot_handle));
285                 data->ready = 0;
286                 break;
287
288         case SNAPSHOT_SET_IMAGE_SIZE:
289                 image_size = arg;
290                 break;
291
292         case SNAPSHOT_AVAIL_SWAP:
293                 avail = count_swap_pages(data->swap, 1);
294                 avail <<= PAGE_SHIFT;
295                 error = put_user(avail, (loff_t __user *)arg);
296                 break;
297
298         case SNAPSHOT_GET_SWAP_PAGE:
299                 if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
300                         error = -ENODEV;
301                         break;
302                 }
303                 if (!data->bitmap) {
304                         data->bitmap = alloc_bitmap(count_swap_pages(data->swap, 0));
305                         if (!data->bitmap) {
306                                 error = -ENOMEM;
307                                 break;
308                         }
309                 }
310                 offset = alloc_swapdev_block(data->swap, data->bitmap);
311                 if (offset) {
312                         offset <<= PAGE_SHIFT;
313                         error = put_user(offset, (sector_t __user *)arg);
314                 } else {
315                         error = -ENOSPC;
316                 }
317                 break;
318
319         case SNAPSHOT_FREE_SWAP_PAGES:
320                 if (data->swap < 0 || data->swap >= MAX_SWAPFILES) {
321                         error = -ENODEV;
322                         break;
323                 }
324                 free_all_swap_pages(data->swap, data->bitmap);
325                 free_bitmap(data->bitmap);
326                 data->bitmap = NULL;
327                 break;
328
329         case SNAPSHOT_SET_SWAP_FILE:
330                 if (!data->bitmap) {
331                         /*
332                          * User space encodes device types as two-byte values,
333                          * so we need to recode them
334                          */
335                         if (old_decode_dev(arg)) {
336                                 data->swap = swap_type_of(old_decode_dev(arg),
337                                                         0, NULL);
338                                 if (data->swap < 0)
339                                         error = -ENODEV;
340                         } else {
341                                 data->swap = -1;
342                                 error = -EINVAL;
343                         }
344                 } else {
345                         error = -EPERM;
346                 }
347                 break;
348
349         case SNAPSHOT_S2RAM:
350                 if (!pm_ops) {
351                         error = -ENOSYS;
352                         break;
353                 }
354
355                 if (!data->frozen) {
356                         error = -EPERM;
357                         break;
358                 }
359
360                 if (!mutex_trylock(&pm_mutex)) {
361                         error = -EBUSY;
362                         break;
363                 }
364
365                 if (pm_ops->prepare) {
366                         error = pm_ops->prepare(PM_SUSPEND_MEM);
367                         if (error)
368                                 goto OutS3;
369                 }
370
371                 /* Put devices to sleep */
372                 suspend_console();
373                 error = device_suspend(PMSG_SUSPEND);
374                 if (error) {
375                         printk(KERN_ERR "Failed to suspend some devices.\n");
376                 } else {
377                         error = disable_nonboot_cpus();
378                         if (!error) {
379                                 /* Enter S3, system is already frozen */
380                                 suspend_enter(PM_SUSPEND_MEM);
381                                 enable_nonboot_cpus();
382                         }
383                         /* Wake up devices */
384                         device_resume();
385                 }
386                 resume_console();
387                 if (pm_ops->finish)
388                         pm_ops->finish(PM_SUSPEND_MEM);
389
390  OutS3:
391                 mutex_unlock(&pm_mutex);
392                 break;
393
394         case SNAPSHOT_PMOPS:
395                 error = -EINVAL;
396
397                 switch (arg) {
398
399                 case PMOPS_PREPARE:
400                         if (pm_ops && pm_ops->enter) {
401                                 data->platform_suspend = 1;
402                                 error = 0;
403                         } else {
404                                 error = -ENOSYS;
405                         }
406                         break;
407
408                 case PMOPS_ENTER:
409                         if (data->platform_suspend) {
410                                 kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK);
411                                 error = pm_ops->enter(PM_SUSPEND_DISK);
412                                 error = 0;
413                         }
414                         break;
415
416                 case PMOPS_FINISH:
417                         if (data->platform_suspend)
418                                 error = 0;
419
420                         break;
421
422                 default:
423                         printk(KERN_ERR "SNAPSHOT_PMOPS: invalid argument %ld\n", arg);
424
425                 }
426                 break;
427
428         case SNAPSHOT_SET_SWAP_AREA:
429                 if (data->bitmap) {
430                         error = -EPERM;
431                 } else {
432                         struct resume_swap_area swap_area;
433                         dev_t swdev;
434
435                         error = copy_from_user(&swap_area, (void __user *)arg,
436                                         sizeof(struct resume_swap_area));
437                         if (error) {
438                                 error = -EFAULT;
439                                 break;
440                         }
441
442                         /*
443                          * User space encodes device types as two-byte values,
444                          * so we need to recode them
445                          */
446                         swdev = old_decode_dev(swap_area.dev);
447                         if (swdev) {
448                                 offset = swap_area.offset;
449                                 data->swap = swap_type_of(swdev, offset, NULL);
450                                 if (data->swap < 0)
451                                         error = -ENODEV;
452                         } else {
453                                 data->swap = -1;
454                                 error = -EINVAL;
455                         }
456                 }
457                 break;
458
459         default:
460                 error = -ENOTTY;
461
462         }
463
464         return error;
465 }
466
467 static const struct file_operations snapshot_fops = {
468         .open = snapshot_open,
469         .release = snapshot_release,
470         .read = snapshot_read,
471         .write = snapshot_write,
472         .llseek = no_llseek,
473         .ioctl = snapshot_ioctl,
474 };
475
476 static struct miscdevice snapshot_device = {
477         .minor = SNAPSHOT_MINOR,
478         .name = "snapshot",
479         .fops = &snapshot_fops,
480 };
481
482 static int __init snapshot_device_init(void)
483 {
484         return misc_register(&snapshot_device);
485 };
486
487 device_initcall(snapshot_device_init);