[PATCH] swsusp: add resume_offset command line parameter
[powerpc.git] / kernel / power / disk.c
1 /*
2  * kernel/power/disk.c - Suspend-to-disk support.
3  *
4  * Copyright (c) 2003 Patrick Mochel
5  * Copyright (c) 2003 Open Source Development Lab
6  * Copyright (c) 2004 Pavel Machek <pavel@suse.cz>
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/delay.h>
18 #include <linux/fs.h>
19 #include <linux/mount.h>
20 #include <linux/pm.h>
21 #include <linux/console.h>
22 #include <linux/cpu.h>
23
24 #include "power.h"
25
26
27 static int noresume = 0;
28 char resume_file[256] = CONFIG_PM_STD_PARTITION;
29 dev_t swsusp_resume_device;
30 sector_t swsusp_resume_block;
31
32 /**
33  *      power_down - Shut machine down for hibernate.
34  *      @mode:          Suspend-to-disk mode
35  *
36  *      Use the platform driver, if configured so, and return gracefully if it
37  *      fails.
38  *      Otherwise, try to power off and reboot. If they fail, halt the machine,
39  *      there ain't no turning back.
40  */
41
42 static void power_down(suspend_disk_method_t mode)
43 {
44         int error = 0;
45
46         switch(mode) {
47         case PM_DISK_PLATFORM:
48                 kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK);
49                 error = pm_ops->enter(PM_SUSPEND_DISK);
50                 break;
51         case PM_DISK_SHUTDOWN:
52                 kernel_power_off();
53                 break;
54         case PM_DISK_REBOOT:
55                 kernel_restart(NULL);
56                 break;
57         }
58         kernel_halt();
59         /* Valid image is on the disk, if we continue we risk serious data corruption
60            after resume. */
61         printk(KERN_CRIT "Please power me down manually\n");
62         while(1);
63 }
64
65 static inline void platform_finish(void)
66 {
67         if (pm_disk_mode == PM_DISK_PLATFORM) {
68                 if (pm_ops && pm_ops->finish)
69                         pm_ops->finish(PM_SUSPEND_DISK);
70         }
71 }
72
73 static int prepare_processes(void)
74 {
75         int error = 0;
76
77         pm_prepare_console();
78
79         error = disable_nonboot_cpus();
80         if (error)
81                 goto enable_cpus;
82
83         if (freeze_processes()) {
84                 error = -EBUSY;
85                 goto thaw;
86         }
87
88         if (pm_disk_mode == PM_DISK_TESTPROC) {
89                 printk("swsusp debug: Waiting for 5 seconds.\n");
90                 mdelay(5000);
91                 goto thaw;
92         }
93
94         /* Free memory before shutting down devices. */
95         if (!(error = swsusp_shrink_memory()))
96                 return 0;
97 thaw:
98         thaw_processes();
99 enable_cpus:
100         enable_nonboot_cpus();
101         pm_restore_console();
102         return error;
103 }
104
105 static void unprepare_processes(void)
106 {
107         platform_finish();
108         thaw_processes();
109         enable_nonboot_cpus();
110         pm_restore_console();
111 }
112
113 /**
114  *      pm_suspend_disk - The granpappy of hibernation power management.
115  *
116  *      If we're going through the firmware, then get it over with quickly.
117  *
118  *      If not, then call swsusp to do its thing, then figure out how
119  *      to power down the system.
120  */
121
122 int pm_suspend_disk(void)
123 {
124         int error;
125
126         error = prepare_processes();
127         if (error)
128                 return error;
129
130         if (pm_disk_mode == PM_DISK_TESTPROC)
131                 goto Thaw;
132
133         suspend_console();
134         error = device_suspend(PMSG_FREEZE);
135         if (error) {
136                 resume_console();
137                 printk("Some devices failed to suspend\n");
138                 goto Thaw;
139         }
140
141         if (pm_disk_mode == PM_DISK_TEST) {
142                 printk("swsusp debug: Waiting for 5 seconds.\n");
143                 mdelay(5000);
144                 goto Done;
145         }
146
147         pr_debug("PM: snapshotting memory.\n");
148         in_suspend = 1;
149         if ((error = swsusp_suspend()))
150                 goto Done;
151
152         if (in_suspend) {
153                 device_resume();
154                 resume_console();
155                 pr_debug("PM: writing image.\n");
156                 error = swsusp_write();
157                 if (!error)
158                         power_down(pm_disk_mode);
159                 else {
160                         swsusp_free();
161                         goto Thaw;
162                 }
163         } else {
164                 pr_debug("PM: Image restored successfully.\n");
165         }
166
167         swsusp_free();
168  Done:
169         device_resume();
170         resume_console();
171  Thaw:
172         unprepare_processes();
173         return error;
174 }
175
176
177 /**
178  *      software_resume - Resume from a saved image.
179  *
180  *      Called as a late_initcall (so all devices are discovered and
181  *      initialized), we call swsusp to see if we have a saved image or not.
182  *      If so, we quiesce devices, the restore the saved image. We will
183  *      return above (in pm_suspend_disk() ) if everything goes well.
184  *      Otherwise, we fail gracefully and return to the normally
185  *      scheduled program.
186  *
187  */
188
189 static int software_resume(void)
190 {
191         int error;
192
193         down(&pm_sem);
194         if (!swsusp_resume_device) {
195                 if (!strlen(resume_file)) {
196                         up(&pm_sem);
197                         return -ENOENT;
198                 }
199                 swsusp_resume_device = name_to_dev_t(resume_file);
200                 pr_debug("swsusp: Resume From Partition %s\n", resume_file);
201         } else {
202                 pr_debug("swsusp: Resume From Partition %d:%d\n",
203                          MAJOR(swsusp_resume_device), MINOR(swsusp_resume_device));
204         }
205
206         if (noresume) {
207                 /**
208                  * FIXME: If noresume is specified, we need to find the partition
209                  * and reset it back to normal swap space.
210                  */
211                 up(&pm_sem);
212                 return 0;
213         }
214
215         pr_debug("PM: Checking swsusp image.\n");
216
217         if ((error = swsusp_check()))
218                 goto Done;
219
220         pr_debug("PM: Preparing processes for restore.\n");
221
222         if ((error = prepare_processes())) {
223                 swsusp_close();
224                 goto Done;
225         }
226
227         pr_debug("PM: Reading swsusp image.\n");
228
229         if ((error = swsusp_read())) {
230                 swsusp_free();
231                 goto Thaw;
232         }
233
234         pr_debug("PM: Preparing devices for restore.\n");
235
236         suspend_console();
237         if ((error = device_suspend(PMSG_PRETHAW))) {
238                 resume_console();
239                 printk("Some devices failed to suspend\n");
240                 swsusp_free();
241                 goto Thaw;
242         }
243
244         mb();
245
246         pr_debug("PM: Restoring saved image.\n");
247         swsusp_resume();
248         pr_debug("PM: Restore failed, recovering.n");
249         device_resume();
250         resume_console();
251  Thaw:
252         unprepare_processes();
253  Done:
254         /* For success case, the suspend path will release the lock */
255         up(&pm_sem);
256         pr_debug("PM: Resume from disk failed.\n");
257         return 0;
258 }
259
260 late_initcall(software_resume);
261
262
263 static const char * const pm_disk_modes[] = {
264         [PM_DISK_FIRMWARE]      = "firmware",
265         [PM_DISK_PLATFORM]      = "platform",
266         [PM_DISK_SHUTDOWN]      = "shutdown",
267         [PM_DISK_REBOOT]        = "reboot",
268         [PM_DISK_TEST]          = "test",
269         [PM_DISK_TESTPROC]      = "testproc",
270 };
271
272 /**
273  *      disk - Control suspend-to-disk mode
274  *
275  *      Suspend-to-disk can be handled in several ways. The greatest
276  *      distinction is who writes memory to disk - the firmware or the OS.
277  *      If the firmware does it, we assume that it also handles suspending
278  *      the system.
279  *      If the OS does it, then we have three options for putting the system
280  *      to sleep - using the platform driver (e.g. ACPI or other PM registers),
281  *      powering off the system or rebooting the system (for testing).
282  *
283  *      The system will support either 'firmware' or 'platform', and that is
284  *      known a priori (and encoded in pm_ops). But, the user may choose
285  *      'shutdown' or 'reboot' as alternatives.
286  *
287  *      show() will display what the mode is currently set to.
288  *      store() will accept one of
289  *
290  *      'firmware'
291  *      'platform'
292  *      'shutdown'
293  *      'reboot'
294  *
295  *      It will only change to 'firmware' or 'platform' if the system
296  *      supports it (as determined from pm_ops->pm_disk_mode).
297  */
298
299 static ssize_t disk_show(struct subsystem * subsys, char * buf)
300 {
301         return sprintf(buf, "%s\n", pm_disk_modes[pm_disk_mode]);
302 }
303
304
305 static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n)
306 {
307         int error = 0;
308         int i;
309         int len;
310         char *p;
311         suspend_disk_method_t mode = 0;
312
313         p = memchr(buf, '\n', n);
314         len = p ? p - buf : n;
315
316         down(&pm_sem);
317         for (i = PM_DISK_FIRMWARE; i < PM_DISK_MAX; i++) {
318                 if (!strncmp(buf, pm_disk_modes[i], len)) {
319                         mode = i;
320                         break;
321                 }
322         }
323         if (mode) {
324                 if (mode == PM_DISK_SHUTDOWN || mode == PM_DISK_REBOOT ||
325                      mode == PM_DISK_TEST || mode == PM_DISK_TESTPROC) {
326                         pm_disk_mode = mode;
327                 } else {
328                         if (pm_ops && pm_ops->enter &&
329                             (mode == pm_ops->pm_disk_mode))
330                                 pm_disk_mode = mode;
331                         else
332                                 error = -EINVAL;
333                 }
334         } else {
335                 error = -EINVAL;
336         }
337
338         pr_debug("PM: suspend-to-disk mode set to '%s'\n",
339                  pm_disk_modes[mode]);
340         up(&pm_sem);
341         return error ? error : n;
342 }
343
344 power_attr(disk);
345
346 static ssize_t resume_show(struct subsystem * subsys, char *buf)
347 {
348         return sprintf(buf,"%d:%d\n", MAJOR(swsusp_resume_device),
349                        MINOR(swsusp_resume_device));
350 }
351
352 static ssize_t resume_store(struct subsystem *subsys, const char *buf, size_t n)
353 {
354         unsigned int maj, min;
355         dev_t res;
356         int ret = -EINVAL;
357
358         if (sscanf(buf, "%u:%u", &maj, &min) != 2)
359                 goto out;
360
361         res = MKDEV(maj,min);
362         if (maj != MAJOR(res) || min != MINOR(res))
363                 goto out;
364
365         down(&pm_sem);
366         swsusp_resume_device = res;
367         up(&pm_sem);
368         printk("Attempting manual resume\n");
369         noresume = 0;
370         software_resume();
371         ret = n;
372 out:
373         return ret;
374 }
375
376 power_attr(resume);
377
378 static ssize_t image_size_show(struct subsystem * subsys, char *buf)
379 {
380         return sprintf(buf, "%lu\n", image_size);
381 }
382
383 static ssize_t image_size_store(struct subsystem * subsys, const char * buf, size_t n)
384 {
385         unsigned long size;
386
387         if (sscanf(buf, "%lu", &size) == 1) {
388                 image_size = size;
389                 return n;
390         }
391
392         return -EINVAL;
393 }
394
395 power_attr(image_size);
396
397 static struct attribute * g[] = {
398         &disk_attr.attr,
399         &resume_attr.attr,
400         &image_size_attr.attr,
401         NULL,
402 };
403
404
405 static struct attribute_group attr_group = {
406         .attrs = g,
407 };
408
409
410 static int __init pm_disk_init(void)
411 {
412         return sysfs_create_group(&power_subsys.kset.kobj,&attr_group);
413 }
414
415 core_initcall(pm_disk_init);
416
417
418 static int __init resume_setup(char *str)
419 {
420         if (noresume)
421                 return 1;
422
423         strncpy( resume_file, str, 255 );
424         return 1;
425 }
426
427 static int __init resume_offset_setup(char *str)
428 {
429         unsigned long long offset;
430
431         if (noresume)
432                 return 1;
433
434         if (sscanf(str, "%llu", &offset) == 1)
435                 swsusp_resume_block = offset;
436
437         return 1;
438 }
439
440 static int __init noresume_setup(char *str)
441 {
442         noresume = 1;
443         return 1;
444 }
445
446 __setup("noresume", noresume_setup);
447 __setup("resume_offset=", resume_offset_setup);
448 __setup("resume=", resume_setup);