Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/kernel/power/swsusp.c | |
3 | * | |
96bc7aec | 4 | * This file provides code to write suspend image to swap and read it back. |
1da177e4 LT |
5 | * |
6 | * Copyright (C) 1998-2001 Gabor Kuti <seasons@fornax.hu> | |
25761b6e | 7 | * Copyright (C) 1998,2001-2005 Pavel Machek <pavel@suse.cz> |
1da177e4 LT |
8 | * |
9 | * This file is released under the GPLv2. | |
10 | * | |
11 | * I'd like to thank the following people for their work: | |
2e4d5822 | 12 | * |
1da177e4 LT |
13 | * Pavel Machek <pavel@ucw.cz>: |
14 | * Modifications, defectiveness pointing, being with me at the very beginning, | |
15 | * suspend to swap space, stop all tasks. Port to 2.4.18-ac and 2.5.17. | |
16 | * | |
2e4d5822 | 17 | * Steve Doddi <dirk@loth.demon.co.uk>: |
1da177e4 LT |
18 | * Support the possibility of hardware state restoring. |
19 | * | |
20 | * Raph <grey.havens@earthling.net>: | |
21 | * Support for preserving states of network devices and virtual console | |
22 | * (including X and svgatextmode) | |
23 | * | |
24 | * Kurt Garloff <garloff@suse.de>: | |
25 | * Straightened the critical function in order to prevent compilers from | |
26 | * playing tricks with local variables. | |
27 | * | |
28 | * Andreas Mohr <a.mohr@mailto.de> | |
29 | * | |
30 | * Alex Badea <vampire@go.ro>: | |
31 | * Fixed runaway init | |
32 | * | |
7088a5c0 | 33 | * Rafael J. Wysocki <rjw@sisk.pl> |
61159a31 | 34 | * Reworked the freeing of memory and the handling of swap |
7088a5c0 | 35 | * |
1da177e4 LT |
36 | * More state savers are welcome. Especially for the scsi layer... |
37 | * | |
38 | * For TODOs,FIXMEs also look in Documentation/power/swsusp.txt | |
39 | */ | |
40 | ||
1da177e4 LT |
41 | #include <linux/mm.h> |
42 | #include <linux/suspend.h> | |
1da177e4 | 43 | #include <linux/spinlock.h> |
1da177e4 LT |
44 | #include <linux/kernel.h> |
45 | #include <linux/major.h> | |
46 | #include <linux/swap.h> | |
47 | #include <linux/pm.h> | |
1da177e4 LT |
48 | #include <linux/swapops.h> |
49 | #include <linux/bootmem.h> | |
50 | #include <linux/syscalls.h> | |
1da177e4 | 51 | #include <linux/highmem.h> |
0d3a9abe | 52 | #include <linux/time.h> |
d1d241cc | 53 | #include <linux/rbtree.h> |
a8af7898 | 54 | #include <linux/io.h> |
1da177e4 LT |
55 | |
56 | #include "power.h" | |
57 | ||
f577eb30 RW |
58 | int in_suspend __nosavedata = 0; |
59 | ||
1da177e4 | 60 | /** |
f577eb30 RW |
61 | * The following functions are used for tracing the allocated |
62 | * swap pages, so that they can be freed in case of an error. | |
1da177e4 | 63 | */ |
7088a5c0 | 64 | |
d1d241cc RW |
65 | struct swsusp_extent { |
66 | struct rb_node node; | |
67 | unsigned long start; | |
68 | unsigned long end; | |
69 | }; | |
1da177e4 | 70 | |
d1d241cc | 71 | static struct rb_root swsusp_extents = RB_ROOT; |
7088a5c0 | 72 | |
d1d241cc | 73 | static int swsusp_extents_insert(unsigned long swap_offset) |
7088a5c0 | 74 | { |
d1d241cc RW |
75 | struct rb_node **new = &(swsusp_extents.rb_node); |
76 | struct rb_node *parent = NULL; | |
77 | struct swsusp_extent *ext; | |
78 | ||
79 | /* Figure out where to put the new node */ | |
80 | while (*new) { | |
81 | ext = container_of(*new, struct swsusp_extent, node); | |
82 | parent = *new; | |
83 | if (swap_offset < ext->start) { | |
84 | /* Try to merge */ | |
85 | if (swap_offset == ext->start - 1) { | |
86 | ext->start--; | |
87 | return 0; | |
88 | } | |
89 | new = &((*new)->rb_left); | |
90 | } else if (swap_offset > ext->end) { | |
91 | /* Try to merge */ | |
92 | if (swap_offset == ext->end + 1) { | |
93 | ext->end++; | |
94 | return 0; | |
95 | } | |
96 | new = &((*new)->rb_right); | |
97 | } else { | |
98 | /* It already is in the tree */ | |
99 | return -EINVAL; | |
7088a5c0 | 100 | } |
1da177e4 | 101 | } |
d1d241cc RW |
102 | /* Add the new node and rebalance the tree. */ |
103 | ext = kzalloc(sizeof(struct swsusp_extent), GFP_KERNEL); | |
104 | if (!ext) | |
105 | return -ENOMEM; | |
106 | ||
107 | ext->start = swap_offset; | |
108 | ext->end = swap_offset; | |
109 | rb_link_node(&ext->node, parent, new); | |
110 | rb_insert_color(&ext->node, &swsusp_extents); | |
f577eb30 | 111 | return 0; |
7088a5c0 | 112 | } |
1da177e4 | 113 | |
d1d241cc RW |
114 | /** |
115 | * alloc_swapdev_block - allocate a swap page and register that it has | |
116 | * been allocated, so that it can be freed in case of an error. | |
117 | */ | |
118 | ||
119 | sector_t alloc_swapdev_block(int swap) | |
7088a5c0 | 120 | { |
f577eb30 RW |
121 | unsigned long offset; |
122 | ||
123 | offset = swp_offset(get_swap_page_of_type(swap)); | |
124 | if (offset) { | |
d1d241cc | 125 | if (swsusp_extents_insert(offset)) |
f577eb30 | 126 | swap_free(swp_entry(swap, offset)); |
3aef83e0 RW |
127 | else |
128 | return swapdev_block(swap, offset); | |
7088a5c0 | 129 | } |
3aef83e0 | 130 | return 0; |
7088a5c0 | 131 | } |
1da177e4 | 132 | |
d1d241cc RW |
133 | /** |
134 | * free_all_swap_pages - free swap pages allocated for saving image data. | |
135 | * It also frees the extents used to register which swap entres had been | |
136 | * allocated. | |
137 | */ | |
138 | ||
139 | void free_all_swap_pages(int swap) | |
7088a5c0 | 140 | { |
d1d241cc RW |
141 | struct rb_node *node; |
142 | ||
143 | while ((node = swsusp_extents.rb_node)) { | |
144 | struct swsusp_extent *ext; | |
145 | unsigned long offset; | |
146 | ||
147 | ext = container_of(node, struct swsusp_extent, node); | |
148 | rb_erase(node, &swsusp_extents); | |
149 | for (offset = ext->start; offset <= ext->end; offset++) | |
150 | swap_free(swp_entry(swap, offset)); | |
151 | ||
152 | kfree(ext); | |
1da177e4 | 153 | } |
7088a5c0 RW |
154 | } |
155 | ||
d1d241cc RW |
156 | int swsusp_swap_in_use(void) |
157 | { | |
158 | return (swsusp_extents.rb_node != NULL); | |
159 | } | |
160 | ||
0d3a9abe RW |
161 | /** |
162 | * swsusp_show_speed - print the time elapsed between two events represented by | |
163 | * @start and @stop | |
164 | * | |
165 | * @nr_pages - number of pages processed between @start and @stop | |
166 | * @msg - introductory message to print | |
167 | */ | |
168 | ||
169 | void swsusp_show_speed(struct timeval *start, struct timeval *stop, | |
170 | unsigned nr_pages, char *msg) | |
171 | { | |
172 | s64 elapsed_centisecs64; | |
173 | int centisecs; | |
174 | int k; | |
175 | int kps; | |
176 | ||
177 | elapsed_centisecs64 = timeval_to_ns(stop) - timeval_to_ns(start); | |
178 | do_div(elapsed_centisecs64, NSEC_PER_SEC / 100); | |
179 | centisecs = elapsed_centisecs64; | |
180 | if (centisecs == 0) | |
181 | centisecs = 1; /* avoid div-by-zero */ | |
182 | k = nr_pages * (PAGE_SIZE / 1024); | |
183 | kps = (k * 100) / centisecs; | |
23976728 RW |
184 | printk(KERN_INFO "PM: %s %d kbytes in %d.%02d seconds (%d.%02d MB/s)\n", |
185 | msg, k, | |
0d3a9abe RW |
186 | centisecs / 100, centisecs % 100, |
187 | kps / 1000, (kps % 1000) / 10); | |
188 | } |