diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-20 12:03:55 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-20 12:03:55 -0400 |
commit | 46ee9645094ad1eb5b4888882ecaa1fb87dcd2a3 (patch) | |
tree | d0a48e993568b6a2415cfc21fc06eaa2fd886429 /kernel | |
parent | fa5312d9e87e7222c6c384c4e930dc149bc1178d (diff) | |
parent | 25f3a5a2854dce8b8413fd24cc9d5b9e3632be54 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6:
PM: PM QOS update fix
Freezer / cgroup freezer: Update stale locking comments
PM / platform_bus: Allow runtime PM by default
i2c: Fix bus-level power management callbacks
PM QOS update
PM / Hibernate: Fix block_io.c printk warning
PM / Hibernate: Group swap ops
PM / Hibernate: Move the first_sector out of swsusp_write
PM / Hibernate: Separate block_io
PM / Hibernate: Snapshot cleanup
FS / libfs: Implement simple_write_to_buffer
PM / Hibernate: document open(/dev/snapshot) side effects
PM / Runtime: Add sysfs debug files
PM: Improve device power management document
PM: Update device power management document
PM: Allow runtime_suspend methods to call pm_schedule_suspend()
PM: pm_wakeup - switch to using bool
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/cgroup_freezer.c | 21 | ||||
-rw-r--r-- | kernel/pm_qos_params.c | 218 | ||||
-rw-r--r-- | kernel/power/Makefile | 3 | ||||
-rw-r--r-- | kernel/power/block_io.c | 103 | ||||
-rw-r--r-- | kernel/power/power.h | 27 | ||||
-rw-r--r-- | kernel/power/snapshot.c | 145 | ||||
-rw-r--r-- | kernel/power/swap.c | 333 | ||||
-rw-r--r-- | kernel/power/user.c | 37 |
8 files changed, 456 insertions, 431 deletions
diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index e5c0244962b0..ce71ed53e88f 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c | |||
@@ -89,10 +89,10 @@ struct cgroup_subsys freezer_subsys; | |||
89 | 89 | ||
90 | /* Locks taken and their ordering | 90 | /* Locks taken and their ordering |
91 | * ------------------------------ | 91 | * ------------------------------ |
92 | * css_set_lock | ||
93 | * cgroup_mutex (AKA cgroup_lock) | 92 | * cgroup_mutex (AKA cgroup_lock) |
94 | * task->alloc_lock (AKA task_lock) | ||
95 | * freezer->lock | 93 | * freezer->lock |
94 | * css_set_lock | ||
95 | * task->alloc_lock (AKA task_lock) | ||
96 | * task->sighand->siglock | 96 | * task->sighand->siglock |
97 | * | 97 | * |
98 | * cgroup code forces css_set_lock to be taken before task->alloc_lock | 98 | * cgroup code forces css_set_lock to be taken before task->alloc_lock |
@@ -100,33 +100,38 @@ struct cgroup_subsys freezer_subsys; | |||
100 | * freezer_create(), freezer_destroy(): | 100 | * freezer_create(), freezer_destroy(): |
101 | * cgroup_mutex [ by cgroup core ] | 101 | * cgroup_mutex [ by cgroup core ] |
102 | * | 102 | * |
103 | * can_attach(): | 103 | * freezer_can_attach(): |
104 | * cgroup_mutex | 104 | * cgroup_mutex (held by caller of can_attach) |
105 | * | 105 | * |
106 | * cgroup_frozen(): | 106 | * cgroup_freezing_or_frozen(): |
107 | * task->alloc_lock (to get task's cgroup) | 107 | * task->alloc_lock (to get task's cgroup) |
108 | * | 108 | * |
109 | * freezer_fork() (preserving fork() performance means can't take cgroup_mutex): | 109 | * freezer_fork() (preserving fork() performance means can't take cgroup_mutex): |
110 | * task->alloc_lock (to get task's cgroup) | ||
111 | * freezer->lock | 110 | * freezer->lock |
112 | * sighand->siglock (if the cgroup is freezing) | 111 | * sighand->siglock (if the cgroup is freezing) |
113 | * | 112 | * |
114 | * freezer_read(): | 113 | * freezer_read(): |
115 | * cgroup_mutex | 114 | * cgroup_mutex |
116 | * freezer->lock | 115 | * freezer->lock |
116 | * write_lock css_set_lock (cgroup iterator start) | ||
117 | * task->alloc_lock | ||
117 | * read_lock css_set_lock (cgroup iterator start) | 118 | * read_lock css_set_lock (cgroup iterator start) |
118 | * | 119 | * |
119 | * freezer_write() (freeze): | 120 | * freezer_write() (freeze): |
120 | * cgroup_mutex | 121 | * cgroup_mutex |
121 | * freezer->lock | 122 | * freezer->lock |
123 | * write_lock css_set_lock (cgroup iterator start) | ||
124 | * task->alloc_lock | ||
122 | * read_lock css_set_lock (cgroup iterator start) | 125 | * read_lock css_set_lock (cgroup iterator start) |
123 | * sighand->siglock | 126 | * sighand->siglock (fake signal delivery inside freeze_task()) |
124 | * | 127 | * |
125 | * freezer_write() (unfreeze): | 128 | * freezer_write() (unfreeze): |
126 | * cgroup_mutex | 129 | * cgroup_mutex |
127 | * freezer->lock | 130 | * freezer->lock |
131 | * write_lock css_set_lock (cgroup iterator start) | ||
132 | * task->alloc_lock | ||
128 | * read_lock css_set_lock (cgroup iterator start) | 133 | * read_lock css_set_lock (cgroup iterator start) |
129 | * task->alloc_lock (to prevent races with freeze_task()) | 134 | * task->alloc_lock (inside thaw_process(), prevents race with refrigerator()) |
130 | * sighand->siglock | 135 | * sighand->siglock |
131 | */ | 136 | */ |
132 | static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss, | 137 | static struct cgroup_subsys_state *freezer_create(struct cgroup_subsys *ss, |
diff --git a/kernel/pm_qos_params.c b/kernel/pm_qos_params.c index 3db49b9ca374..f42d3f737a33 100644 --- a/kernel/pm_qos_params.c +++ b/kernel/pm_qos_params.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * This module exposes the interface to kernel space for specifying | 2 | * This module exposes the interface to kernel space for specifying |
3 | * QoS dependencies. It provides infrastructure for registration of: | 3 | * QoS dependencies. It provides infrastructure for registration of: |
4 | * | 4 | * |
5 | * Dependents on a QoS value : register requirements | 5 | * Dependents on a QoS value : register requests |
6 | * Watchers of QoS value : get notified when target QoS value changes | 6 | * Watchers of QoS value : get notified when target QoS value changes |
7 | * | 7 | * |
8 | * This QoS design is best effort based. Dependents register their QoS needs. | 8 | * This QoS design is best effort based. Dependents register their QoS needs. |
@@ -14,19 +14,21 @@ | |||
14 | * timeout: usec <-- currently not used. | 14 | * timeout: usec <-- currently not used. |
15 | * throughput: kbs (kilo byte / sec) | 15 | * throughput: kbs (kilo byte / sec) |
16 | * | 16 | * |
17 | * There are lists of pm_qos_objects each one wrapping requirements, notifiers | 17 | * There are lists of pm_qos_objects each one wrapping requests, notifiers |
18 | * | 18 | * |
19 | * User mode requirements on a QOS parameter register themselves to the | 19 | * User mode requests on a QOS parameter register themselves to the |
20 | * subsystem by opening the device node /dev/... and writing there request to | 20 | * subsystem by opening the device node /dev/... and writing there request to |
21 | * the node. As long as the process holds a file handle open to the node the | 21 | * the node. As long as the process holds a file handle open to the node the |
22 | * client continues to be accounted for. Upon file release the usermode | 22 | * client continues to be accounted for. Upon file release the usermode |
23 | * requirement is removed and a new qos target is computed. This way when the | 23 | * request is removed and a new qos target is computed. This way when the |
24 | * requirement that the application has is cleaned up when closes the file | 24 | * request that the application has is cleaned up when closes the file |
25 | * pointer or exits the pm_qos_object will get an opportunity to clean up. | 25 | * pointer or exits the pm_qos_object will get an opportunity to clean up. |
26 | * | 26 | * |
27 | * Mark Gross <mgross@linux.intel.com> | 27 | * Mark Gross <mgross@linux.intel.com> |
28 | */ | 28 | */ |
29 | 29 | ||
30 | /*#define DEBUG*/ | ||
31 | |||
30 | #include <linux/pm_qos_params.h> | 32 | #include <linux/pm_qos_params.h> |
31 | #include <linux/sched.h> | 33 | #include <linux/sched.h> |
32 | #include <linux/spinlock.h> | 34 | #include <linux/spinlock.h> |
@@ -42,25 +44,25 @@ | |||
42 | #include <linux/uaccess.h> | 44 | #include <linux/uaccess.h> |
43 | 45 | ||
44 | /* | 46 | /* |
45 | * locking rule: all changes to requirements or notifiers lists | 47 | * locking rule: all changes to requests or notifiers lists |
46 | * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock | 48 | * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock |
47 | * held, taken with _irqsave. One lock to rule them all | 49 | * held, taken with _irqsave. One lock to rule them all |
48 | */ | 50 | */ |
49 | struct requirement_list { | 51 | struct pm_qos_request_list { |
50 | struct list_head list; | 52 | struct list_head list; |
51 | union { | 53 | union { |
52 | s32 value; | 54 | s32 value; |
53 | s32 usec; | 55 | s32 usec; |
54 | s32 kbps; | 56 | s32 kbps; |
55 | }; | 57 | }; |
56 | char *name; | 58 | int pm_qos_class; |
57 | }; | 59 | }; |
58 | 60 | ||
59 | static s32 max_compare(s32 v1, s32 v2); | 61 | static s32 max_compare(s32 v1, s32 v2); |
60 | static s32 min_compare(s32 v1, s32 v2); | 62 | static s32 min_compare(s32 v1, s32 v2); |
61 | 63 | ||
62 | struct pm_qos_object { | 64 | struct pm_qos_object { |
63 | struct requirement_list requirements; | 65 | struct pm_qos_request_list requests; |
64 | struct blocking_notifier_head *notifiers; | 66 | struct blocking_notifier_head *notifiers; |
65 | struct miscdevice pm_qos_power_miscdev; | 67 | struct miscdevice pm_qos_power_miscdev; |
66 | char *name; | 68 | char *name; |
@@ -72,7 +74,7 @@ struct pm_qos_object { | |||
72 | static struct pm_qos_object null_pm_qos; | 74 | static struct pm_qos_object null_pm_qos; |
73 | static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier); | 75 | static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier); |
74 | static struct pm_qos_object cpu_dma_pm_qos = { | 76 | static struct pm_qos_object cpu_dma_pm_qos = { |
75 | .requirements = {LIST_HEAD_INIT(cpu_dma_pm_qos.requirements.list)}, | 77 | .requests = {LIST_HEAD_INIT(cpu_dma_pm_qos.requests.list)}, |
76 | .notifiers = &cpu_dma_lat_notifier, | 78 | .notifiers = &cpu_dma_lat_notifier, |
77 | .name = "cpu_dma_latency", | 79 | .name = "cpu_dma_latency", |
78 | .default_value = 2000 * USEC_PER_SEC, | 80 | .default_value = 2000 * USEC_PER_SEC, |
@@ -82,7 +84,7 @@ static struct pm_qos_object cpu_dma_pm_qos = { | |||
82 | 84 | ||
83 | static BLOCKING_NOTIFIER_HEAD(network_lat_notifier); | 85 | static BLOCKING_NOTIFIER_HEAD(network_lat_notifier); |
84 | static struct pm_qos_object network_lat_pm_qos = { | 86 | static struct pm_qos_object network_lat_pm_qos = { |
85 | .requirements = {LIST_HEAD_INIT(network_lat_pm_qos.requirements.list)}, | 87 | .requests = {LIST_HEAD_INIT(network_lat_pm_qos.requests.list)}, |
86 | .notifiers = &network_lat_notifier, | 88 | .notifiers = &network_lat_notifier, |
87 | .name = "network_latency", | 89 | .name = "network_latency", |
88 | .default_value = 2000 * USEC_PER_SEC, | 90 | .default_value = 2000 * USEC_PER_SEC, |
@@ -93,8 +95,7 @@ static struct pm_qos_object network_lat_pm_qos = { | |||
93 | 95 | ||
94 | static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier); | 96 | static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier); |
95 | static struct pm_qos_object network_throughput_pm_qos = { | 97 | static struct pm_qos_object network_throughput_pm_qos = { |
96 | .requirements = | 98 | .requests = {LIST_HEAD_INIT(network_throughput_pm_qos.requests.list)}, |
97 | {LIST_HEAD_INIT(network_throughput_pm_qos.requirements.list)}, | ||
98 | .notifiers = &network_throughput_notifier, | 99 | .notifiers = &network_throughput_notifier, |
99 | .name = "network_throughput", | 100 | .name = "network_throughput", |
100 | .default_value = 0, | 101 | .default_value = 0, |
@@ -135,31 +136,34 @@ static s32 min_compare(s32 v1, s32 v2) | |||
135 | } | 136 | } |
136 | 137 | ||
137 | 138 | ||
138 | static void update_target(int target) | 139 | static void update_target(int pm_qos_class) |
139 | { | 140 | { |
140 | s32 extreme_value; | 141 | s32 extreme_value; |
141 | struct requirement_list *node; | 142 | struct pm_qos_request_list *node; |
142 | unsigned long flags; | 143 | unsigned long flags; |
143 | int call_notifier = 0; | 144 | int call_notifier = 0; |
144 | 145 | ||
145 | spin_lock_irqsave(&pm_qos_lock, flags); | 146 | spin_lock_irqsave(&pm_qos_lock, flags); |
146 | extreme_value = pm_qos_array[target]->default_value; | 147 | extreme_value = pm_qos_array[pm_qos_class]->default_value; |
147 | list_for_each_entry(node, | 148 | list_for_each_entry(node, |
148 | &pm_qos_array[target]->requirements.list, list) { | 149 | &pm_qos_array[pm_qos_class]->requests.list, list) { |
149 | extreme_value = pm_qos_array[target]->comparitor( | 150 | extreme_value = pm_qos_array[pm_qos_class]->comparitor( |
150 | extreme_value, node->value); | 151 | extreme_value, node->value); |
151 | } | 152 | } |
152 | if (atomic_read(&pm_qos_array[target]->target_value) != extreme_value) { | 153 | if (atomic_read(&pm_qos_array[pm_qos_class]->target_value) != |
154 | extreme_value) { | ||
153 | call_notifier = 1; | 155 | call_notifier = 1; |
154 | atomic_set(&pm_qos_array[target]->target_value, extreme_value); | 156 | atomic_set(&pm_qos_array[pm_qos_class]->target_value, |
155 | pr_debug(KERN_ERR "new target for qos %d is %d\n", target, | 157 | extreme_value); |
156 | atomic_read(&pm_qos_array[target]->target_value)); | 158 | pr_debug(KERN_ERR "new target for qos %d is %d\n", pm_qos_class, |
159 | atomic_read(&pm_qos_array[pm_qos_class]->target_value)); | ||
157 | } | 160 | } |
158 | spin_unlock_irqrestore(&pm_qos_lock, flags); | 161 | spin_unlock_irqrestore(&pm_qos_lock, flags); |
159 | 162 | ||
160 | if (call_notifier) | 163 | if (call_notifier) |
161 | blocking_notifier_call_chain(pm_qos_array[target]->notifiers, | 164 | blocking_notifier_call_chain( |
162 | (unsigned long) extreme_value, NULL); | 165 | pm_qos_array[pm_qos_class]->notifiers, |
166 | (unsigned long) extreme_value, NULL); | ||
163 | } | 167 | } |
164 | 168 | ||
165 | static int register_pm_qos_misc(struct pm_qos_object *qos) | 169 | static int register_pm_qos_misc(struct pm_qos_object *qos) |
@@ -185,125 +189,112 @@ static int find_pm_qos_object_by_minor(int minor) | |||
185 | } | 189 | } |
186 | 190 | ||
187 | /** | 191 | /** |
188 | * pm_qos_requirement - returns current system wide qos expectation | 192 | * pm_qos_request - returns current system wide qos expectation |
189 | * @pm_qos_class: identification of which qos value is requested | 193 | * @pm_qos_class: identification of which qos value is requested |
190 | * | 194 | * |
191 | * This function returns the current target value in an atomic manner. | 195 | * This function returns the current target value in an atomic manner. |
192 | */ | 196 | */ |
193 | int pm_qos_requirement(int pm_qos_class) | 197 | int pm_qos_request(int pm_qos_class) |
194 | { | 198 | { |
195 | return atomic_read(&pm_qos_array[pm_qos_class]->target_value); | 199 | return atomic_read(&pm_qos_array[pm_qos_class]->target_value); |
196 | } | 200 | } |
197 | EXPORT_SYMBOL_GPL(pm_qos_requirement); | 201 | EXPORT_SYMBOL_GPL(pm_qos_request); |
198 | 202 | ||
199 | /** | 203 | /** |
200 | * pm_qos_add_requirement - inserts new qos request into the list | 204 | * pm_qos_add_request - inserts new qos request into the list |
201 | * @pm_qos_class: identifies which list of qos request to us | 205 | * @pm_qos_class: identifies which list of qos request to us |
202 | * @name: identifies the request | ||
203 | * @value: defines the qos request | 206 | * @value: defines the qos request |
204 | * | 207 | * |
205 | * This function inserts a new entry in the pm_qos_class list of requested qos | 208 | * This function inserts a new entry in the pm_qos_class list of requested qos |
206 | * performance characteristics. It recomputes the aggregate QoS expectations | 209 | * performance characteristics. It recomputes the aggregate QoS expectations |
207 | * for the pm_qos_class of parameters. | 210 | * for the pm_qos_class of parameters, and returns the pm_qos_request list |
211 | * element as a handle for use in updating and removal. Call needs to save | ||
212 | * this handle for later use. | ||
208 | */ | 213 | */ |
209 | int pm_qos_add_requirement(int pm_qos_class, char *name, s32 value) | 214 | struct pm_qos_request_list *pm_qos_add_request(int pm_qos_class, s32 value) |
210 | { | 215 | { |
211 | struct requirement_list *dep; | 216 | struct pm_qos_request_list *dep; |
212 | unsigned long flags; | 217 | unsigned long flags; |
213 | 218 | ||
214 | dep = kzalloc(sizeof(struct requirement_list), GFP_KERNEL); | 219 | dep = kzalloc(sizeof(struct pm_qos_request_list), GFP_KERNEL); |
215 | if (dep) { | 220 | if (dep) { |
216 | if (value == PM_QOS_DEFAULT_VALUE) | 221 | if (value == PM_QOS_DEFAULT_VALUE) |
217 | dep->value = pm_qos_array[pm_qos_class]->default_value; | 222 | dep->value = pm_qos_array[pm_qos_class]->default_value; |
218 | else | 223 | else |
219 | dep->value = value; | 224 | dep->value = value; |
220 | dep->name = kstrdup(name, GFP_KERNEL); | 225 | dep->pm_qos_class = pm_qos_class; |
221 | if (!dep->name) | ||
222 | goto cleanup; | ||
223 | 226 | ||
224 | spin_lock_irqsave(&pm_qos_lock, flags); | 227 | spin_lock_irqsave(&pm_qos_lock, flags); |
225 | list_add(&dep->list, | 228 | list_add(&dep->list, |
226 | &pm_qos_array[pm_qos_class]->requirements.list); | 229 | &pm_qos_array[pm_qos_class]->requests.list); |
227 | spin_unlock_irqrestore(&pm_qos_lock, flags); | 230 | spin_unlock_irqrestore(&pm_qos_lock, flags); |
228 | update_target(pm_qos_class); | 231 | update_target(pm_qos_class); |
229 | |||
230 | return 0; | ||
231 | } | 232 | } |
232 | 233 | ||
233 | cleanup: | 234 | return dep; |
234 | kfree(dep); | ||
235 | return -ENOMEM; | ||
236 | } | 235 | } |
237 | EXPORT_SYMBOL_GPL(pm_qos_add_requirement); | 236 | EXPORT_SYMBOL_GPL(pm_qos_add_request); |
238 | 237 | ||
239 | /** | 238 | /** |
240 | * pm_qos_update_requirement - modifies an existing qos request | 239 | * pm_qos_update_request - modifies an existing qos request |
241 | * @pm_qos_class: identifies which list of qos request to us | 240 | * @pm_qos_req : handle to list element holding a pm_qos request to use |
242 | * @name: identifies the request | ||
243 | * @value: defines the qos request | 241 | * @value: defines the qos request |
244 | * | 242 | * |
245 | * Updates an existing qos requirement for the pm_qos_class of parameters along | 243 | * Updates an existing qos request for the pm_qos_class of parameters along |
246 | * with updating the target pm_qos_class value. | 244 | * with updating the target pm_qos_class value. |
247 | * | 245 | * |
248 | * If the named request isn't in the list then no change is made. | 246 | * Attempts are made to make this code callable on hot code paths. |
249 | */ | 247 | */ |
250 | int pm_qos_update_requirement(int pm_qos_class, char *name, s32 new_value) | 248 | void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req, |
249 | s32 new_value) | ||
251 | { | 250 | { |
252 | unsigned long flags; | 251 | unsigned long flags; |
253 | struct requirement_list *node; | ||
254 | int pending_update = 0; | 252 | int pending_update = 0; |
253 | s32 temp; | ||
255 | 254 | ||
256 | spin_lock_irqsave(&pm_qos_lock, flags); | 255 | if (pm_qos_req) { /*guard against callers passing in null */ |
257 | list_for_each_entry(node, | 256 | spin_lock_irqsave(&pm_qos_lock, flags); |
258 | &pm_qos_array[pm_qos_class]->requirements.list, list) { | 257 | if (new_value == PM_QOS_DEFAULT_VALUE) |
259 | if (strcmp(node->name, name) == 0) { | 258 | temp = pm_qos_array[pm_qos_req->pm_qos_class]->default_value; |
260 | if (new_value == PM_QOS_DEFAULT_VALUE) | 259 | else |
261 | node->value = | 260 | temp = new_value; |
262 | pm_qos_array[pm_qos_class]->default_value; | 261 | |
263 | else | 262 | if (temp != pm_qos_req->value) { |
264 | node->value = new_value; | ||
265 | pending_update = 1; | 263 | pending_update = 1; |
266 | break; | 264 | pm_qos_req->value = temp; |
267 | } | 265 | } |
266 | spin_unlock_irqrestore(&pm_qos_lock, flags); | ||
267 | if (pending_update) | ||
268 | update_target(pm_qos_req->pm_qos_class); | ||
268 | } | 269 | } |
269 | spin_unlock_irqrestore(&pm_qos_lock, flags); | ||
270 | if (pending_update) | ||
271 | update_target(pm_qos_class); | ||
272 | |||
273 | return 0; | ||
274 | } | 270 | } |
275 | EXPORT_SYMBOL_GPL(pm_qos_update_requirement); | 271 | EXPORT_SYMBOL_GPL(pm_qos_update_request); |
276 | 272 | ||
277 | /** | 273 | /** |
278 | * pm_qos_remove_requirement - modifies an existing qos request | 274 | * pm_qos_remove_request - modifies an existing qos request |
279 | * @pm_qos_class: identifies which list of qos request to us | 275 | * @pm_qos_req: handle to request list element |
280 | * @name: identifies the request | ||
281 | * | 276 | * |
282 | * Will remove named qos request from pm_qos_class list of parameters and | 277 | * Will remove pm qos request from the list of requests and |
283 | * recompute the current target value for the pm_qos_class. | 278 | * recompute the current target value for the pm_qos_class. Call this |
279 | * on slow code paths. | ||
284 | */ | 280 | */ |
285 | void pm_qos_remove_requirement(int pm_qos_class, char *name) | 281 | void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req) |
286 | { | 282 | { |
287 | unsigned long flags; | 283 | unsigned long flags; |
288 | struct requirement_list *node; | 284 | int qos_class; |
289 | int pending_update = 0; | ||
290 | 285 | ||
286 | if (pm_qos_req == NULL) | ||
287 | return; | ||
288 | /* silent return to keep pcm code cleaner */ | ||
289 | |||
290 | qos_class = pm_qos_req->pm_qos_class; | ||
291 | spin_lock_irqsave(&pm_qos_lock, flags); | 291 | spin_lock_irqsave(&pm_qos_lock, flags); |
292 | list_for_each_entry(node, | 292 | list_del(&pm_qos_req->list); |
293 | &pm_qos_array[pm_qos_class]->requirements.list, list) { | 293 | kfree(pm_qos_req); |
294 | if (strcmp(node->name, name) == 0) { | ||
295 | kfree(node->name); | ||
296 | list_del(&node->list); | ||
297 | kfree(node); | ||
298 | pending_update = 1; | ||
299 | break; | ||
300 | } | ||
301 | } | ||
302 | spin_unlock_irqrestore(&pm_qos_lock, flags); | 294 | spin_unlock_irqrestore(&pm_qos_lock, flags); |
303 | if (pending_update) | 295 | update_target(qos_class); |
304 | update_target(pm_qos_class); | ||
305 | } | 296 | } |
306 | EXPORT_SYMBOL_GPL(pm_qos_remove_requirement); | 297 | EXPORT_SYMBOL_GPL(pm_qos_remove_request); |
307 | 298 | ||
308 | /** | 299 | /** |
309 | * pm_qos_add_notifier - sets notification entry for changes to target value | 300 | * pm_qos_add_notifier - sets notification entry for changes to target value |
@@ -313,7 +304,7 @@ EXPORT_SYMBOL_GPL(pm_qos_remove_requirement); | |||
313 | * will register the notifier into a notification chain that gets called | 304 | * will register the notifier into a notification chain that gets called |
314 | * upon changes to the pm_qos_class target value. | 305 | * upon changes to the pm_qos_class target value. |
315 | */ | 306 | */ |
316 | int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier) | 307 | int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier) |
317 | { | 308 | { |
318 | int retval; | 309 | int retval; |
319 | 310 | ||
@@ -343,21 +334,16 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier) | |||
343 | } | 334 | } |
344 | EXPORT_SYMBOL_GPL(pm_qos_remove_notifier); | 335 | EXPORT_SYMBOL_GPL(pm_qos_remove_notifier); |
345 | 336 | ||
346 | #define PID_NAME_LEN 32 | ||
347 | |||
348 | static int pm_qos_power_open(struct inode *inode, struct file *filp) | 337 | static int pm_qos_power_open(struct inode *inode, struct file *filp) |
349 | { | 338 | { |
350 | int ret; | ||
351 | long pm_qos_class; | 339 | long pm_qos_class; |
352 | char name[PID_NAME_LEN]; | ||
353 | 340 | ||
354 | pm_qos_class = find_pm_qos_object_by_minor(iminor(inode)); | 341 | pm_qos_class = find_pm_qos_object_by_minor(iminor(inode)); |
355 | if (pm_qos_class >= 0) { | 342 | if (pm_qos_class >= 0) { |
356 | filp->private_data = (void *)pm_qos_class; | 343 | filp->private_data = (void *) pm_qos_add_request(pm_qos_class, |
357 | snprintf(name, PID_NAME_LEN, "process_%d", current->pid); | 344 | PM_QOS_DEFAULT_VALUE); |
358 | ret = pm_qos_add_requirement(pm_qos_class, name, | 345 | |
359 | PM_QOS_DEFAULT_VALUE); | 346 | if (filp->private_data) |
360 | if (ret >= 0) | ||
361 | return 0; | 347 | return 0; |
362 | } | 348 | } |
363 | return -EPERM; | 349 | return -EPERM; |
@@ -365,32 +351,40 @@ static int pm_qos_power_open(struct inode *inode, struct file *filp) | |||
365 | 351 | ||
366 | static int pm_qos_power_release(struct inode *inode, struct file *filp) | 352 | static int pm_qos_power_release(struct inode *inode, struct file *filp) |
367 | { | 353 | { |
368 | int pm_qos_class; | 354 | struct pm_qos_request_list *req; |
369 | char name[PID_NAME_LEN]; | ||
370 | 355 | ||
371 | pm_qos_class = (long)filp->private_data; | 356 | req = (struct pm_qos_request_list *)filp->private_data; |
372 | snprintf(name, PID_NAME_LEN, "process_%d", current->pid); | 357 | pm_qos_remove_request(req); |
373 | pm_qos_remove_requirement(pm_qos_class, name); | ||
374 | 358 | ||
375 | return 0; | 359 | return 0; |
376 | } | 360 | } |
377 | 361 | ||
362 | |||
378 | static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, | 363 | static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, |
379 | size_t count, loff_t *f_pos) | 364 | size_t count, loff_t *f_pos) |
380 | { | 365 | { |
381 | s32 value; | 366 | s32 value; |
382 | int pm_qos_class; | 367 | int x; |
383 | char name[PID_NAME_LEN]; | 368 | char ascii_value[11]; |
384 | 369 | struct pm_qos_request_list *pm_qos_req; | |
385 | pm_qos_class = (long)filp->private_data; | 370 | |
386 | if (count != sizeof(s32)) | 371 | if (count == sizeof(s32)) { |
372 | if (copy_from_user(&value, buf, sizeof(s32))) | ||
373 | return -EFAULT; | ||
374 | } else if (count == 11) { /* len('0x12345678/0') */ | ||
375 | if (copy_from_user(ascii_value, buf, 11)) | ||
376 | return -EFAULT; | ||
377 | x = sscanf(ascii_value, "%x", &value); | ||
378 | if (x != 1) | ||
379 | return -EINVAL; | ||
380 | pr_debug(KERN_ERR "%s, %d, 0x%x\n", ascii_value, x, value); | ||
381 | } else | ||
387 | return -EINVAL; | 382 | return -EINVAL; |
388 | if (copy_from_user(&value, buf, sizeof(s32))) | ||
389 | return -EFAULT; | ||
390 | snprintf(name, PID_NAME_LEN, "process_%d", current->pid); | ||
391 | pm_qos_update_requirement(pm_qos_class, name, value); | ||
392 | 383 | ||
393 | return sizeof(s32); | 384 | pm_qos_req = (struct pm_qos_request_list *)filp->private_data; |
385 | pm_qos_update_request(pm_qos_req, value); | ||
386 | |||
387 | return count; | ||
394 | } | 388 | } |
395 | 389 | ||
396 | 390 | ||
diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 43191815f874..524e058dcf06 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile | |||
@@ -8,7 +8,8 @@ obj-$(CONFIG_PM_SLEEP) += console.o | |||
8 | obj-$(CONFIG_FREEZER) += process.o | 8 | obj-$(CONFIG_FREEZER) += process.o |
9 | obj-$(CONFIG_SUSPEND) += suspend.o | 9 | obj-$(CONFIG_SUSPEND) += suspend.o |
10 | obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o | 10 | obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o |
11 | obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o | 11 | obj-$(CONFIG_HIBERNATION) += hibernate.o snapshot.o swap.o user.o \ |
12 | block_io.o | ||
12 | obj-$(CONFIG_HIBERNATION_NVS) += hibernate_nvs.o | 13 | obj-$(CONFIG_HIBERNATION_NVS) += hibernate_nvs.o |
13 | 14 | ||
14 | obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o | 15 | obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o |
diff --git a/kernel/power/block_io.c b/kernel/power/block_io.c new file mode 100644 index 000000000000..97024fd40cd5 --- /dev/null +++ b/kernel/power/block_io.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * This file provides functions for block I/O operations on swap/file. | ||
3 | * | ||
4 | * Copyright (C) 1998,2001-2005 Pavel Machek <pavel@ucw.cz> | ||
5 | * Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl> | ||
6 | * | ||
7 | * This file is released under the GPLv2. | ||
8 | */ | ||
9 | |||
10 | #include <linux/bio.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/pagemap.h> | ||
13 | #include <linux/swap.h> | ||
14 | |||
15 | #include "power.h" | ||
16 | |||
17 | /** | ||
18 | * submit - submit BIO request. | ||
19 | * @rw: READ or WRITE. | ||
20 | * @off physical offset of page. | ||
21 | * @page: page we're reading or writing. | ||
22 | * @bio_chain: list of pending biod (for async reading) | ||
23 | * | ||
24 | * Straight from the textbook - allocate and initialize the bio. | ||
25 | * If we're reading, make sure the page is marked as dirty. | ||
26 | * Then submit it and, if @bio_chain == NULL, wait. | ||
27 | */ | ||
28 | static int submit(int rw, struct block_device *bdev, sector_t sector, | ||
29 | struct page *page, struct bio **bio_chain) | ||
30 | { | ||
31 | const int bio_rw = rw | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG); | ||
32 | struct bio *bio; | ||
33 | |||
34 | bio = bio_alloc(__GFP_WAIT | __GFP_HIGH, 1); | ||
35 | bio->bi_sector = sector; | ||
36 | bio->bi_bdev = bdev; | ||
37 | bio->bi_end_io = end_swap_bio_read; | ||
38 | |||
39 | if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { | ||
40 | printk(KERN_ERR "PM: Adding page to bio failed at %llu\n", | ||
41 | (unsigned long long)sector); | ||
42 | bio_put(bio); | ||
43 | return -EFAULT; | ||
44 | } | ||
45 | |||
46 | lock_page(page); | ||
47 | bio_get(bio); | ||
48 | |||
49 | if (bio_chain == NULL) { | ||
50 | submit_bio(bio_rw, bio); | ||
51 | wait_on_page_locked(page); | ||
52 | if (rw == READ) | ||
53 | bio_set_pages_dirty(bio); | ||
54 | bio_put(bio); | ||
55 | } else { | ||
56 | if (rw == READ) | ||
57 | get_page(page); /* These pages are freed later */ | ||
58 | bio->bi_private = *bio_chain; | ||
59 | *bio_chain = bio; | ||
60 | submit_bio(bio_rw, bio); | ||
61 | } | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | int hib_bio_read_page(pgoff_t page_off, void *addr, struct bio **bio_chain) | ||
66 | { | ||
67 | return submit(READ, hib_resume_bdev, page_off * (PAGE_SIZE >> 9), | ||
68 | virt_to_page(addr), bio_chain); | ||
69 | } | ||
70 | |||
71 | int hib_bio_write_page(pgoff_t page_off, void *addr, struct bio **bio_chain) | ||
72 | { | ||
73 | return submit(WRITE, hib_resume_bdev, page_off * (PAGE_SIZE >> 9), | ||
74 | virt_to_page(addr), bio_chain); | ||
75 | } | ||
76 | |||
77 | int hib_wait_on_bio_chain(struct bio **bio_chain) | ||
78 | { | ||
79 | struct bio *bio; | ||
80 | struct bio *next_bio; | ||
81 | int ret = 0; | ||
82 | |||
83 | if (bio_chain == NULL) | ||
84 | return 0; | ||
85 | |||
86 | bio = *bio_chain; | ||
87 | if (bio == NULL) | ||
88 | return 0; | ||
89 | while (bio) { | ||
90 | struct page *page; | ||
91 | |||
92 | next_bio = bio->bi_private; | ||
93 | page = bio->bi_io_vec[0].bv_page; | ||
94 | wait_on_page_locked(page); | ||
95 | if (!PageUptodate(page) || PageError(page)) | ||
96 | ret = -EIO; | ||
97 | put_page(page); | ||
98 | bio_put(bio); | ||
99 | bio = next_bio; | ||
100 | } | ||
101 | *bio_chain = NULL; | ||
102 | return ret; | ||
103 | } | ||
diff --git a/kernel/power/power.h b/kernel/power/power.h index 46c5a26630a3..006270fe382d 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h | |||
@@ -97,24 +97,12 @@ extern int hibernate_preallocate_memory(void); | |||
97 | */ | 97 | */ |
98 | 98 | ||
99 | struct snapshot_handle { | 99 | struct snapshot_handle { |
100 | loff_t offset; /* number of the last byte ready for reading | ||
101 | * or writing in the sequence | ||
102 | */ | ||
103 | unsigned int cur; /* number of the block of PAGE_SIZE bytes the | 100 | unsigned int cur; /* number of the block of PAGE_SIZE bytes the |
104 | * next operation will refer to (ie. current) | 101 | * next operation will refer to (ie. current) |
105 | */ | 102 | */ |
106 | unsigned int cur_offset; /* offset with respect to the current | ||
107 | * block (for the next operation) | ||
108 | */ | ||
109 | unsigned int prev; /* number of the block of PAGE_SIZE bytes that | ||
110 | * was the current one previously | ||
111 | */ | ||
112 | void *buffer; /* address of the block to read from | 103 | void *buffer; /* address of the block to read from |
113 | * or write to | 104 | * or write to |
114 | */ | 105 | */ |
115 | unsigned int buf_offset; /* location to read from or write to, | ||
116 | * given as a displacement from 'buffer' | ||
117 | */ | ||
118 | int sync_read; /* Set to one to notify the caller of | 106 | int sync_read; /* Set to one to notify the caller of |
119 | * snapshot_write_next() that it may | 107 | * snapshot_write_next() that it may |
120 | * need to call wait_on_bio_chain() | 108 | * need to call wait_on_bio_chain() |
@@ -125,12 +113,12 @@ struct snapshot_handle { | |||
125 | * snapshot_read_next()/snapshot_write_next() is allowed to | 113 | * snapshot_read_next()/snapshot_write_next() is allowed to |
126 | * read/write data after the function returns | 114 | * read/write data after the function returns |
127 | */ | 115 | */ |
128 | #define data_of(handle) ((handle).buffer + (handle).buf_offset) | 116 | #define data_of(handle) ((handle).buffer) |
129 | 117 | ||
130 | extern unsigned int snapshot_additional_pages(struct zone *zone); | 118 | extern unsigned int snapshot_additional_pages(struct zone *zone); |
131 | extern unsigned long snapshot_get_image_size(void); | 119 | extern unsigned long snapshot_get_image_size(void); |
132 | extern int snapshot_read_next(struct snapshot_handle *handle, size_t count); | 120 | extern int snapshot_read_next(struct snapshot_handle *handle); |
133 | extern int snapshot_write_next(struct snapshot_handle *handle, size_t count); | 121 | extern int snapshot_write_next(struct snapshot_handle *handle); |
134 | extern void snapshot_write_finalize(struct snapshot_handle *handle); | 122 | extern void snapshot_write_finalize(struct snapshot_handle *handle); |
135 | extern int snapshot_image_loaded(struct snapshot_handle *handle); | 123 | extern int snapshot_image_loaded(struct snapshot_handle *handle); |
136 | 124 | ||
@@ -154,6 +142,15 @@ extern int swsusp_read(unsigned int *flags_p); | |||
154 | extern int swsusp_write(unsigned int flags); | 142 | extern int swsusp_write(unsigned int flags); |
155 | extern void swsusp_close(fmode_t); | 143 | extern void swsusp_close(fmode_t); |
156 | 144 | ||
145 | /* kernel/power/block_io.c */ | ||
146 | extern struct block_device *hib_resume_bdev; | ||
147 | |||
148 | extern int hib_bio_read_page(pgoff_t page_off, void *addr, | ||
149 | struct bio **bio_chain); | ||
150 | extern int hib_bio_write_page(pgoff_t page_off, void *addr, | ||
151 | struct bio **bio_chain); | ||
152 | extern int hib_wait_on_bio_chain(struct bio **bio_chain); | ||
153 | |||
157 | struct timeval; | 154 | struct timeval; |
158 | /* kernel/power/swsusp.c */ | 155 | /* kernel/power/swsusp.c */ |
159 | extern void swsusp_show_speed(struct timeval *, struct timeval *, | 156 | extern void swsusp_show_speed(struct timeval *, struct timeval *, |
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index be861c26dda7..25ce010e9f8b 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c | |||
@@ -1604,14 +1604,9 @@ pack_pfns(unsigned long *buf, struct memory_bitmap *bm) | |||
1604 | * snapshot_handle structure. The structure gets updated and a pointer | 1604 | * snapshot_handle structure. The structure gets updated and a pointer |
1605 | * to it should be passed to this function every next time. | 1605 | * to it should be passed to this function every next time. |
1606 | * | 1606 | * |
1607 | * The @count parameter should contain the number of bytes the caller | ||
1608 | * wants to read from the snapshot. It must not be zero. | ||
1609 | * | ||
1610 | * On success the function returns a positive number. Then, the caller | 1607 | * On success the function returns a positive number. Then, the caller |
1611 | * is allowed to read up to the returned number of bytes from the memory | 1608 | * is allowed to read up to the returned number of bytes from the memory |
1612 | * location computed by the data_of() macro. The number returned | 1609 | * location computed by the data_of() macro. |
1613 | * may be smaller than @count, but this only happens if the read would | ||
1614 | * cross a page boundary otherwise. | ||
1615 | * | 1610 | * |
1616 | * The function returns 0 to indicate the end of data stream condition, | 1611 | * The function returns 0 to indicate the end of data stream condition, |
1617 | * and a negative number is returned on error. In such cases the | 1612 | * and a negative number is returned on error. In such cases the |
@@ -1619,7 +1614,7 @@ pack_pfns(unsigned long *buf, struct memory_bitmap *bm) | |||
1619 | * any more. | 1614 | * any more. |
1620 | */ | 1615 | */ |
1621 | 1616 | ||
1622 | int snapshot_read_next(struct snapshot_handle *handle, size_t count) | 1617 | int snapshot_read_next(struct snapshot_handle *handle) |
1623 | { | 1618 | { |
1624 | if (handle->cur > nr_meta_pages + nr_copy_pages) | 1619 | if (handle->cur > nr_meta_pages + nr_copy_pages) |
1625 | return 0; | 1620 | return 0; |
@@ -1630,7 +1625,7 @@ int snapshot_read_next(struct snapshot_handle *handle, size_t count) | |||
1630 | if (!buffer) | 1625 | if (!buffer) |
1631 | return -ENOMEM; | 1626 | return -ENOMEM; |
1632 | } | 1627 | } |
1633 | if (!handle->offset) { | 1628 | if (!handle->cur) { |
1634 | int error; | 1629 | int error; |
1635 | 1630 | ||
1636 | error = init_header((struct swsusp_info *)buffer); | 1631 | error = init_header((struct swsusp_info *)buffer); |
@@ -1639,42 +1634,30 @@ int snapshot_read_next(struct snapshot_handle *handle, size_t count) | |||
1639 | handle->buffer = buffer; | 1634 | handle->buffer = buffer; |
1640 | memory_bm_position_reset(&orig_bm); | 1635 | memory_bm_position_reset(&orig_bm); |
1641 | memory_bm_position_reset(©_bm); | 1636 | memory_bm_position_reset(©_bm); |
1642 | } | 1637 | } else if (handle->cur <= nr_meta_pages) { |
1643 | if (handle->prev < handle->cur) { | 1638 | memset(buffer, 0, PAGE_SIZE); |
1644 | if (handle->cur <= nr_meta_pages) { | 1639 | pack_pfns(buffer, &orig_bm); |
1645 | memset(buffer, 0, PAGE_SIZE); | 1640 | } else { |
1646 | pack_pfns(buffer, &orig_bm); | 1641 | struct page *page; |
1647 | } else { | ||
1648 | struct page *page; | ||
1649 | 1642 | ||
1650 | page = pfn_to_page(memory_bm_next_pfn(©_bm)); | 1643 | page = pfn_to_page(memory_bm_next_pfn(©_bm)); |
1651 | if (PageHighMem(page)) { | 1644 | if (PageHighMem(page)) { |
1652 | /* Highmem pages are copied to the buffer, | 1645 | /* Highmem pages are copied to the buffer, |
1653 | * because we can't return with a kmapped | 1646 | * because we can't return with a kmapped |
1654 | * highmem page (we may not be called again). | 1647 | * highmem page (we may not be called again). |
1655 | */ | 1648 | */ |
1656 | void *kaddr; | 1649 | void *kaddr; |
1657 | 1650 | ||
1658 | kaddr = kmap_atomic(page, KM_USER0); | 1651 | kaddr = kmap_atomic(page, KM_USER0); |
1659 | memcpy(buffer, kaddr, PAGE_SIZE); | 1652 | memcpy(buffer, kaddr, PAGE_SIZE); |
1660 | kunmap_atomic(kaddr, KM_USER0); | 1653 | kunmap_atomic(kaddr, KM_USER0); |
1661 | handle->buffer = buffer; | 1654 | handle->buffer = buffer; |
1662 | } else { | 1655 | } else { |
1663 | handle->buffer = page_address(page); | 1656 | handle->buffer = page_address(page); |
1664 | } | ||
1665 | } | 1657 | } |
1666 | handle->prev = handle->cur; | ||
1667 | } | ||
1668 | handle->buf_offset = handle->cur_offset; | ||
1669 | if (handle->cur_offset + count >= PAGE_SIZE) { | ||
1670 | count = PAGE_SIZE - handle->cur_offset; | ||
1671 | handle->cur_offset = 0; | ||
1672 | handle->cur++; | ||
1673 | } else { | ||
1674 | handle->cur_offset += count; | ||
1675 | } | 1658 | } |
1676 | handle->offset += count; | 1659 | handle->cur++; |
1677 | return count; | 1660 | return PAGE_SIZE; |
1678 | } | 1661 | } |
1679 | 1662 | ||
1680 | /** | 1663 | /** |
@@ -2133,14 +2116,9 @@ static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca) | |||
2133 | * snapshot_handle structure. The structure gets updated and a pointer | 2116 | * snapshot_handle structure. The structure gets updated and a pointer |
2134 | * to it should be passed to this function every next time. | 2117 | * to it should be passed to this function every next time. |
2135 | * | 2118 | * |
2136 | * The @count parameter should contain the number of bytes the caller | ||
2137 | * wants to write to the image. It must not be zero. | ||
2138 | * | ||
2139 | * On success the function returns a positive number. Then, the caller | 2119 | * On success the function returns a positive number. Then, the caller |
2140 | * is allowed to write up to the returned number of bytes to the memory | 2120 | * is allowed to write up to the returned number of bytes to the memory |
2141 | * location computed by the data_of() macro. The number returned | 2121 | * location computed by the data_of() macro. |
2142 | * may be smaller than @count, but this only happens if the write would | ||
2143 | * cross a page boundary otherwise. | ||
2144 | * | 2122 | * |
2145 | * The function returns 0 to indicate the "end of file" condition, | 2123 | * The function returns 0 to indicate the "end of file" condition, |
2146 | * and a negative number is returned on error. In such cases the | 2124 | * and a negative number is returned on error. In such cases the |
@@ -2148,16 +2126,18 @@ static void *get_buffer(struct memory_bitmap *bm, struct chain_allocator *ca) | |||
2148 | * any more. | 2126 | * any more. |
2149 | */ | 2127 | */ |
2150 | 2128 | ||
2151 | int snapshot_write_next(struct snapshot_handle *handle, size_t count) | 2129 | int snapshot_write_next(struct snapshot_handle *handle) |
2152 | { | 2130 | { |
2153 | static struct chain_allocator ca; | 2131 | static struct chain_allocator ca; |
2154 | int error = 0; | 2132 | int error = 0; |
2155 | 2133 | ||
2156 | /* Check if we have already loaded the entire image */ | 2134 | /* Check if we have already loaded the entire image */ |
2157 | if (handle->prev && handle->cur > nr_meta_pages + nr_copy_pages) | 2135 | if (handle->cur > 1 && handle->cur > nr_meta_pages + nr_copy_pages) |
2158 | return 0; | 2136 | return 0; |
2159 | 2137 | ||
2160 | if (handle->offset == 0) { | 2138 | handle->sync_read = 1; |
2139 | |||
2140 | if (!handle->cur) { | ||
2161 | if (!buffer) | 2141 | if (!buffer) |
2162 | /* This makes the buffer be freed by swsusp_free() */ | 2142 | /* This makes the buffer be freed by swsusp_free() */ |
2163 | buffer = get_image_page(GFP_ATOMIC, PG_ANY); | 2143 | buffer = get_image_page(GFP_ATOMIC, PG_ANY); |
@@ -2166,56 +2146,43 @@ int snapshot_write_next(struct snapshot_handle *handle, size_t count) | |||
2166 | return -ENOMEM; | 2146 | return -ENOMEM; |
2167 | 2147 | ||
2168 | handle->buffer = buffer; | 2148 | handle->buffer = buffer; |
2169 | } | 2149 | } else if (handle->cur == 1) { |
2170 | handle->sync_read = 1; | 2150 | error = load_header(buffer); |
2171 | if (handle->prev < handle->cur) { | 2151 | if (error) |
2172 | if (handle->prev == 0) { | 2152 | return error; |
2173 | error = load_header(buffer); | ||
2174 | if (error) | ||
2175 | return error; | ||
2176 | 2153 | ||
2177 | error = memory_bm_create(©_bm, GFP_ATOMIC, PG_ANY); | 2154 | error = memory_bm_create(©_bm, GFP_ATOMIC, PG_ANY); |
2178 | if (error) | 2155 | if (error) |
2179 | return error; | 2156 | return error; |
2157 | |||
2158 | } else if (handle->cur <= nr_meta_pages + 1) { | ||
2159 | error = unpack_orig_pfns(buffer, ©_bm); | ||
2160 | if (error) | ||
2161 | return error; | ||
2180 | 2162 | ||
2181 | } else if (handle->prev <= nr_meta_pages) { | 2163 | if (handle->cur == nr_meta_pages + 1) { |
2182 | error = unpack_orig_pfns(buffer, ©_bm); | 2164 | error = prepare_image(&orig_bm, ©_bm); |
2183 | if (error) | 2165 | if (error) |
2184 | return error; | 2166 | return error; |
2185 | 2167 | ||
2186 | if (handle->prev == nr_meta_pages) { | 2168 | chain_init(&ca, GFP_ATOMIC, PG_SAFE); |
2187 | error = prepare_image(&orig_bm, ©_bm); | 2169 | memory_bm_position_reset(&orig_bm); |
2188 | if (error) | 2170 | restore_pblist = NULL; |
2189 | return error; | ||
2190 | |||
2191 | chain_init(&ca, GFP_ATOMIC, PG_SAFE); | ||
2192 | memory_bm_position_reset(&orig_bm); | ||
2193 | restore_pblist = NULL; | ||
2194 | handle->buffer = get_buffer(&orig_bm, &ca); | ||
2195 | handle->sync_read = 0; | ||
2196 | if (IS_ERR(handle->buffer)) | ||
2197 | return PTR_ERR(handle->buffer); | ||
2198 | } | ||
2199 | } else { | ||
2200 | copy_last_highmem_page(); | ||
2201 | handle->buffer = get_buffer(&orig_bm, &ca); | 2171 | handle->buffer = get_buffer(&orig_bm, &ca); |
2172 | handle->sync_read = 0; | ||
2202 | if (IS_ERR(handle->buffer)) | 2173 | if (IS_ERR(handle->buffer)) |
2203 | return PTR_ERR(handle->buffer); | 2174 | return PTR_ERR(handle->buffer); |
2204 | if (handle->buffer != buffer) | ||
2205 | handle->sync_read = 0; | ||
2206 | } | 2175 | } |
2207 | handle->prev = handle->cur; | ||
2208 | } | ||
2209 | handle->buf_offset = handle->cur_offset; | ||
2210 | if (handle->cur_offset + count >= PAGE_SIZE) { | ||
2211 | count = PAGE_SIZE - handle->cur_offset; | ||
2212 | handle->cur_offset = 0; | ||
2213 | handle->cur++; | ||
2214 | } else { | 2176 | } else { |
2215 | handle->cur_offset += count; | 2177 | copy_last_highmem_page(); |
2178 | handle->buffer = get_buffer(&orig_bm, &ca); | ||
2179 | if (IS_ERR(handle->buffer)) | ||
2180 | return PTR_ERR(handle->buffer); | ||
2181 | if (handle->buffer != buffer) | ||
2182 | handle->sync_read = 0; | ||
2216 | } | 2183 | } |
2217 | handle->offset += count; | 2184 | handle->cur++; |
2218 | return count; | 2185 | return PAGE_SIZE; |
2219 | } | 2186 | } |
2220 | 2187 | ||
2221 | /** | 2188 | /** |
@@ -2230,7 +2197,7 @@ void snapshot_write_finalize(struct snapshot_handle *handle) | |||
2230 | { | 2197 | { |
2231 | copy_last_highmem_page(); | 2198 | copy_last_highmem_page(); |
2232 | /* Free only if we have loaded the image entirely */ | 2199 | /* Free only if we have loaded the image entirely */ |
2233 | if (handle->prev && handle->cur > nr_meta_pages + nr_copy_pages) { | 2200 | if (handle->cur > 1 && handle->cur > nr_meta_pages + nr_copy_pages) { |
2234 | memory_bm_free(&orig_bm, PG_UNSAFE_CLEAR); | 2201 | memory_bm_free(&orig_bm, PG_UNSAFE_CLEAR); |
2235 | free_highmem_data(); | 2202 | free_highmem_data(); |
2236 | } | 2203 | } |
diff --git a/kernel/power/swap.c b/kernel/power/swap.c index 66824d71983a..b0bb21778391 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c | |||
@@ -29,6 +29,40 @@ | |||
29 | 29 | ||
30 | #define SWSUSP_SIG "S1SUSPEND" | 30 | #define SWSUSP_SIG "S1SUSPEND" |
31 | 31 | ||
32 | /* | ||
33 | * The swap map is a data structure used for keeping track of each page | ||
34 | * written to a swap partition. It consists of many swap_map_page | ||
35 | * structures that contain each an array of MAP_PAGE_SIZE swap entries. | ||
36 | * These structures are stored on the swap and linked together with the | ||
37 | * help of the .next_swap member. | ||
38 | * | ||
39 | * The swap map is created during suspend. The swap map pages are | ||
40 | * allocated and populated one at a time, so we only need one memory | ||
41 | * page to set up the entire structure. | ||
42 | * | ||
43 | * During resume we also only need to use one swap_map_page structure | ||
44 | * at a time. | ||
45 | */ | ||
46 | |||
47 | #define MAP_PAGE_ENTRIES (PAGE_SIZE / sizeof(sector_t) - 1) | ||
48 | |||
49 | struct swap_map_page { | ||
50 | sector_t entries[MAP_PAGE_ENTRIES]; | ||
51 | sector_t next_swap; | ||
52 | }; | ||
53 | |||
54 | /** | ||
55 | * The swap_map_handle structure is used for handling swap in | ||
56 | * a file-alike way | ||
57 | */ | ||
58 | |||
59 | struct swap_map_handle { | ||
60 | struct swap_map_page *cur; | ||
61 | sector_t cur_swap; | ||
62 | sector_t first_sector; | ||
63 | unsigned int k; | ||
64 | }; | ||
65 | |||
32 | struct swsusp_header { | 66 | struct swsusp_header { |
33 | char reserved[PAGE_SIZE - 20 - sizeof(sector_t) - sizeof(int)]; | 67 | char reserved[PAGE_SIZE - 20 - sizeof(sector_t) - sizeof(int)]; |
34 | sector_t image; | 68 | sector_t image; |
@@ -145,110 +179,24 @@ int swsusp_swap_in_use(void) | |||
145 | */ | 179 | */ |
146 | 180 | ||
147 | static unsigned short root_swap = 0xffff; | 181 | static unsigned short root_swap = 0xffff; |
148 | static struct block_device *resume_bdev; | 182 | struct block_device *hib_resume_bdev; |
149 | |||
150 | /** | ||
151 | * submit - submit BIO request. | ||
152 | * @rw: READ or WRITE. | ||
153 | * @off physical offset of page. | ||
154 | * @page: page we're reading or writing. | ||
155 | * @bio_chain: list of pending biod (for async reading) | ||
156 | * | ||
157 | * Straight from the textbook - allocate and initialize the bio. | ||
158 | * If we're reading, make sure the page is marked as dirty. | ||
159 | * Then submit it and, if @bio_chain == NULL, wait. | ||
160 | */ | ||
161 | static int submit(int rw, pgoff_t page_off, struct page *page, | ||
162 | struct bio **bio_chain) | ||
163 | { | ||
164 | const int bio_rw = rw | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG); | ||
165 | struct bio *bio; | ||
166 | |||
167 | bio = bio_alloc(__GFP_WAIT | __GFP_HIGH, 1); | ||
168 | bio->bi_sector = page_off * (PAGE_SIZE >> 9); | ||
169 | bio->bi_bdev = resume_bdev; | ||
170 | bio->bi_end_io = end_swap_bio_read; | ||
171 | |||
172 | if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { | ||
173 | printk(KERN_ERR "PM: Adding page to bio failed at %ld\n", | ||
174 | page_off); | ||
175 | bio_put(bio); | ||
176 | return -EFAULT; | ||
177 | } | ||
178 | |||
179 | lock_page(page); | ||
180 | bio_get(bio); | ||
181 | |||
182 | if (bio_chain == NULL) { | ||
183 | submit_bio(bio_rw, bio); | ||
184 | wait_on_page_locked(page); | ||
185 | if (rw == READ) | ||
186 | bio_set_pages_dirty(bio); | ||
187 | bio_put(bio); | ||
188 | } else { | ||
189 | if (rw == READ) | ||
190 | get_page(page); /* These pages are freed later */ | ||
191 | bio->bi_private = *bio_chain; | ||
192 | *bio_chain = bio; | ||
193 | submit_bio(bio_rw, bio); | ||
194 | } | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static int bio_read_page(pgoff_t page_off, void *addr, struct bio **bio_chain) | ||
199 | { | ||
200 | return submit(READ, page_off, virt_to_page(addr), bio_chain); | ||
201 | } | ||
202 | |||
203 | static int bio_write_page(pgoff_t page_off, void *addr, struct bio **bio_chain) | ||
204 | { | ||
205 | return submit(WRITE, page_off, virt_to_page(addr), bio_chain); | ||
206 | } | ||
207 | |||
208 | static int wait_on_bio_chain(struct bio **bio_chain) | ||
209 | { | ||
210 | struct bio *bio; | ||
211 | struct bio *next_bio; | ||
212 | int ret = 0; | ||
213 | |||
214 | if (bio_chain == NULL) | ||
215 | return 0; | ||
216 | |||
217 | bio = *bio_chain; | ||
218 | if (bio == NULL) | ||
219 | return 0; | ||
220 | while (bio) { | ||
221 | struct page *page; | ||
222 | |||
223 | next_bio = bio->bi_private; | ||
224 | page = bio->bi_io_vec[0].bv_page; | ||
225 | wait_on_page_locked(page); | ||
226 | if (!PageUptodate(page) || PageError(page)) | ||
227 | ret = -EIO; | ||
228 | put_page(page); | ||
229 | bio_put(bio); | ||
230 | bio = next_bio; | ||
231 | } | ||
232 | *bio_chain = NULL; | ||
233 | return ret; | ||
234 | } | ||
235 | 183 | ||
236 | /* | 184 | /* |
237 | * Saving part | 185 | * Saving part |
238 | */ | 186 | */ |
239 | 187 | ||
240 | static int mark_swapfiles(sector_t start, unsigned int flags) | 188 | static int mark_swapfiles(struct swap_map_handle *handle, unsigned int flags) |
241 | { | 189 | { |
242 | int error; | 190 | int error; |
243 | 191 | ||
244 | bio_read_page(swsusp_resume_block, swsusp_header, NULL); | 192 | hib_bio_read_page(swsusp_resume_block, swsusp_header, NULL); |
245 | if (!memcmp("SWAP-SPACE",swsusp_header->sig, 10) || | 193 | if (!memcmp("SWAP-SPACE",swsusp_header->sig, 10) || |
246 | !memcmp("SWAPSPACE2",swsusp_header->sig, 10)) { | 194 | !memcmp("SWAPSPACE2",swsusp_header->sig, 10)) { |
247 | memcpy(swsusp_header->orig_sig,swsusp_header->sig, 10); | 195 | memcpy(swsusp_header->orig_sig,swsusp_header->sig, 10); |
248 | memcpy(swsusp_header->sig,SWSUSP_SIG, 10); | 196 | memcpy(swsusp_header->sig,SWSUSP_SIG, 10); |
249 | swsusp_header->image = start; | 197 | swsusp_header->image = handle->first_sector; |
250 | swsusp_header->flags = flags; | 198 | swsusp_header->flags = flags; |
251 | error = bio_write_page(swsusp_resume_block, | 199 | error = hib_bio_write_page(swsusp_resume_block, |
252 | swsusp_header, NULL); | 200 | swsusp_header, NULL); |
253 | } else { | 201 | } else { |
254 | printk(KERN_ERR "PM: Swap header not found!\n"); | 202 | printk(KERN_ERR "PM: Swap header not found!\n"); |
@@ -260,25 +208,26 @@ static int mark_swapfiles(sector_t start, unsigned int flags) | |||
260 | /** | 208 | /** |
261 | * swsusp_swap_check - check if the resume device is a swap device | 209 | * swsusp_swap_check - check if the resume device is a swap device |
262 | * and get its index (if so) | 210 | * and get its index (if so) |
211 | * | ||
212 | * This is called before saving image | ||
263 | */ | 213 | */ |
264 | 214 | static int swsusp_swap_check(void) | |
265 | static int swsusp_swap_check(void) /* This is called before saving image */ | ||
266 | { | 215 | { |
267 | int res; | 216 | int res; |
268 | 217 | ||
269 | res = swap_type_of(swsusp_resume_device, swsusp_resume_block, | 218 | res = swap_type_of(swsusp_resume_device, swsusp_resume_block, |
270 | &resume_bdev); | 219 | &hib_resume_bdev); |
271 | if (res < 0) | 220 | if (res < 0) |
272 | return res; | 221 | return res; |
273 | 222 | ||
274 | root_swap = res; | 223 | root_swap = res; |
275 | res = blkdev_get(resume_bdev, FMODE_WRITE); | 224 | res = blkdev_get(hib_resume_bdev, FMODE_WRITE); |
276 | if (res) | 225 | if (res) |
277 | return res; | 226 | return res; |
278 | 227 | ||
279 | res = set_blocksize(resume_bdev, PAGE_SIZE); | 228 | res = set_blocksize(hib_resume_bdev, PAGE_SIZE); |
280 | if (res < 0) | 229 | if (res < 0) |
281 | blkdev_put(resume_bdev, FMODE_WRITE); | 230 | blkdev_put(hib_resume_bdev, FMODE_WRITE); |
282 | 231 | ||
283 | return res; | 232 | return res; |
284 | } | 233 | } |
@@ -309,42 +258,9 @@ static int write_page(void *buf, sector_t offset, struct bio **bio_chain) | |||
309 | } else { | 258 | } else { |
310 | src = buf; | 259 | src = buf; |
311 | } | 260 | } |
312 | return bio_write_page(offset, src, bio_chain); | 261 | return hib_bio_write_page(offset, src, bio_chain); |
313 | } | 262 | } |
314 | 263 | ||
315 | /* | ||
316 | * The swap map is a data structure used for keeping track of each page | ||
317 | * written to a swap partition. It consists of many swap_map_page | ||
318 | * structures that contain each an array of MAP_PAGE_SIZE swap entries. | ||
319 | * These structures are stored on the swap and linked together with the | ||
320 | * help of the .next_swap member. | ||
321 | * | ||
322 | * The swap map is created during suspend. The swap map pages are | ||
323 | * allocated and populated one at a time, so we only need one memory | ||
324 | * page to set up the entire structure. | ||
325 | * | ||
326 | * During resume we also only need to use one swap_map_page structure | ||
327 | * at a time. | ||
328 | */ | ||
329 | |||
330 | #define MAP_PAGE_ENTRIES (PAGE_SIZE / sizeof(sector_t) - 1) | ||
331 | |||
332 | struct swap_map_page { | ||
333 | sector_t entries[MAP_PAGE_ENTRIES]; | ||
334 | sector_t next_swap; | ||
335 | }; | ||
336 | |||
337 | /** | ||
338 | * The swap_map_handle structure is used for handling swap in | ||
339 | * a file-alike way | ||
340 | */ | ||
341 | |||
342 | struct swap_map_handle { | ||
343 | struct swap_map_page *cur; | ||
344 | sector_t cur_swap; | ||
345 | unsigned int k; | ||
346 | }; | ||
347 | |||
348 | static void release_swap_writer(struct swap_map_handle *handle) | 264 | static void release_swap_writer(struct swap_map_handle *handle) |
349 | { | 265 | { |
350 | if (handle->cur) | 266 | if (handle->cur) |
@@ -354,16 +270,33 @@ static void release_swap_writer(struct swap_map_handle *handle) | |||
354 | 270 | ||
355 | static int get_swap_writer(struct swap_map_handle *handle) | 271 | static int get_swap_writer(struct swap_map_handle *handle) |
356 | { | 272 | { |
273 | int ret; | ||
274 | |||
275 | ret = swsusp_swap_check(); | ||
276 | if (ret) { | ||
277 | if (ret != -ENOSPC) | ||
278 | printk(KERN_ERR "PM: Cannot find swap device, try " | ||
279 | "swapon -a.\n"); | ||
280 | return ret; | ||
281 | } | ||
357 | handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL); | 282 | handle->cur = (struct swap_map_page *)get_zeroed_page(GFP_KERNEL); |
358 | if (!handle->cur) | 283 | if (!handle->cur) { |
359 | return -ENOMEM; | 284 | ret = -ENOMEM; |
285 | goto err_close; | ||
286 | } | ||
360 | handle->cur_swap = alloc_swapdev_block(root_swap); | 287 | handle->cur_swap = alloc_swapdev_block(root_swap); |
361 | if (!handle->cur_swap) { | 288 | if (!handle->cur_swap) { |
362 | release_swap_writer(handle); | 289 | ret = -ENOSPC; |
363 | return -ENOSPC; | 290 | goto err_rel; |
364 | } | 291 | } |
365 | handle->k = 0; | 292 | handle->k = 0; |
293 | handle->first_sector = handle->cur_swap; | ||
366 | return 0; | 294 | return 0; |
295 | err_rel: | ||
296 | release_swap_writer(handle); | ||
297 | err_close: | ||
298 | swsusp_close(FMODE_WRITE); | ||
299 | return ret; | ||
367 | } | 300 | } |
368 | 301 | ||
369 | static int swap_write_page(struct swap_map_handle *handle, void *buf, | 302 | static int swap_write_page(struct swap_map_handle *handle, void *buf, |
@@ -380,7 +313,7 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf, | |||
380 | return error; | 313 | return error; |
381 | handle->cur->entries[handle->k++] = offset; | 314 | handle->cur->entries[handle->k++] = offset; |
382 | if (handle->k >= MAP_PAGE_ENTRIES) { | 315 | if (handle->k >= MAP_PAGE_ENTRIES) { |
383 | error = wait_on_bio_chain(bio_chain); | 316 | error = hib_wait_on_bio_chain(bio_chain); |
384 | if (error) | 317 | if (error) |
385 | goto out; | 318 | goto out; |
386 | offset = alloc_swapdev_block(root_swap); | 319 | offset = alloc_swapdev_block(root_swap); |
@@ -406,6 +339,24 @@ static int flush_swap_writer(struct swap_map_handle *handle) | |||
406 | return -EINVAL; | 339 | return -EINVAL; |
407 | } | 340 | } |
408 | 341 | ||
342 | static int swap_writer_finish(struct swap_map_handle *handle, | ||
343 | unsigned int flags, int error) | ||
344 | { | ||
345 | if (!error) { | ||
346 | flush_swap_writer(handle); | ||
347 | printk(KERN_INFO "PM: S"); | ||
348 | error = mark_swapfiles(handle, flags); | ||
349 | printk("|\n"); | ||
350 | } | ||
351 | |||
352 | if (error) | ||
353 | free_all_swap_pages(root_swap); | ||
354 | release_swap_writer(handle); | ||
355 | swsusp_close(FMODE_WRITE); | ||
356 | |||
357 | return error; | ||
358 | } | ||
359 | |||
409 | /** | 360 | /** |
410 | * save_image - save the suspend image data | 361 | * save_image - save the suspend image data |
411 | */ | 362 | */ |
@@ -431,7 +382,7 @@ static int save_image(struct swap_map_handle *handle, | |||
431 | bio = NULL; | 382 | bio = NULL; |
432 | do_gettimeofday(&start); | 383 | do_gettimeofday(&start); |
433 | while (1) { | 384 | while (1) { |
434 | ret = snapshot_read_next(snapshot, PAGE_SIZE); | 385 | ret = snapshot_read_next(snapshot); |
435 | if (ret <= 0) | 386 | if (ret <= 0) |
436 | break; | 387 | break; |
437 | ret = swap_write_page(handle, data_of(*snapshot), &bio); | 388 | ret = swap_write_page(handle, data_of(*snapshot), &bio); |
@@ -441,7 +392,7 @@ static int save_image(struct swap_map_handle *handle, | |||
441 | printk(KERN_CONT "\b\b\b\b%3d%%", nr_pages / m); | 392 | printk(KERN_CONT "\b\b\b\b%3d%%", nr_pages / m); |
442 | nr_pages++; | 393 | nr_pages++; |
443 | } | 394 | } |
444 | err2 = wait_on_bio_chain(&bio); | 395 | err2 = hib_wait_on_bio_chain(&bio); |
445 | do_gettimeofday(&stop); | 396 | do_gettimeofday(&stop); |
446 | if (!ret) | 397 | if (!ret) |
447 | ret = err2; | 398 | ret = err2; |
@@ -483,50 +434,34 @@ int swsusp_write(unsigned int flags) | |||
483 | struct swap_map_handle handle; | 434 | struct swap_map_handle handle; |
484 | struct snapshot_handle snapshot; | 435 | struct snapshot_handle snapshot; |
485 | struct swsusp_info *header; | 436 | struct swsusp_info *header; |
437 | unsigned long pages; | ||
486 | int error; | 438 | int error; |
487 | 439 | ||
488 | error = swsusp_swap_check(); | 440 | pages = snapshot_get_image_size(); |
441 | error = get_swap_writer(&handle); | ||
489 | if (error) { | 442 | if (error) { |
490 | printk(KERN_ERR "PM: Cannot find swap device, try " | 443 | printk(KERN_ERR "PM: Cannot get swap writer\n"); |
491 | "swapon -a.\n"); | ||
492 | return error; | 444 | return error; |
493 | } | 445 | } |
446 | if (!enough_swap(pages)) { | ||
447 | printk(KERN_ERR "PM: Not enough free swap\n"); | ||
448 | error = -ENOSPC; | ||
449 | goto out_finish; | ||
450 | } | ||
494 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); | 451 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); |
495 | error = snapshot_read_next(&snapshot, PAGE_SIZE); | 452 | error = snapshot_read_next(&snapshot); |
496 | if (error < PAGE_SIZE) { | 453 | if (error < PAGE_SIZE) { |
497 | if (error >= 0) | 454 | if (error >= 0) |
498 | error = -EFAULT; | 455 | error = -EFAULT; |
499 | 456 | ||
500 | goto out; | 457 | goto out_finish; |
501 | } | 458 | } |
502 | header = (struct swsusp_info *)data_of(snapshot); | 459 | header = (struct swsusp_info *)data_of(snapshot); |
503 | if (!enough_swap(header->pages)) { | 460 | error = swap_write_page(&handle, header, NULL); |
504 | printk(KERN_ERR "PM: Not enough free swap\n"); | 461 | if (!error) |
505 | error = -ENOSPC; | 462 | error = save_image(&handle, &snapshot, pages - 1); |
506 | goto out; | 463 | out_finish: |
507 | } | 464 | error = swap_writer_finish(&handle, flags, error); |
508 | error = get_swap_writer(&handle); | ||
509 | if (!error) { | ||
510 | sector_t start = handle.cur_swap; | ||
511 | |||
512 | error = swap_write_page(&handle, header, NULL); | ||
513 | if (!error) | ||
514 | error = save_image(&handle, &snapshot, | ||
515 | header->pages - 1); | ||
516 | |||
517 | if (!error) { | ||
518 | flush_swap_writer(&handle); | ||
519 | printk(KERN_INFO "PM: S"); | ||
520 | error = mark_swapfiles(start, flags); | ||
521 | printk("|\n"); | ||
522 | } | ||
523 | } | ||
524 | if (error) | ||
525 | free_all_swap_pages(root_swap); | ||
526 | |||
527 | release_swap_writer(&handle); | ||
528 | out: | ||
529 | swsusp_close(FMODE_WRITE); | ||
530 | return error; | 465 | return error; |
531 | } | 466 | } |
532 | 467 | ||
@@ -542,18 +477,21 @@ static void release_swap_reader(struct swap_map_handle *handle) | |||
542 | handle->cur = NULL; | 477 | handle->cur = NULL; |
543 | } | 478 | } |
544 | 479 | ||
545 | static int get_swap_reader(struct swap_map_handle *handle, sector_t start) | 480 | static int get_swap_reader(struct swap_map_handle *handle, |
481 | unsigned int *flags_p) | ||
546 | { | 482 | { |
547 | int error; | 483 | int error; |
548 | 484 | ||
549 | if (!start) | 485 | *flags_p = swsusp_header->flags; |
486 | |||
487 | if (!swsusp_header->image) /* how can this happen? */ | ||
550 | return -EINVAL; | 488 | return -EINVAL; |
551 | 489 | ||
552 | handle->cur = (struct swap_map_page *)get_zeroed_page(__GFP_WAIT | __GFP_HIGH); | 490 | handle->cur = (struct swap_map_page *)get_zeroed_page(__GFP_WAIT | __GFP_HIGH); |
553 | if (!handle->cur) | 491 | if (!handle->cur) |
554 | return -ENOMEM; | 492 | return -ENOMEM; |
555 | 493 | ||
556 | error = bio_read_page(start, handle->cur, NULL); | 494 | error = hib_bio_read_page(swsusp_header->image, handle->cur, NULL); |
557 | if (error) { | 495 | if (error) { |
558 | release_swap_reader(handle); | 496 | release_swap_reader(handle); |
559 | return error; | 497 | return error; |
@@ -573,21 +511,28 @@ static int swap_read_page(struct swap_map_handle *handle, void *buf, | |||
573 | offset = handle->cur->entries[handle->k]; | 511 | offset = handle->cur->entries[handle->k]; |
574 | if (!offset) | 512 | if (!offset) |
575 | return -EFAULT; | 513 | return -EFAULT; |
576 | error = bio_read_page(offset, buf, bio_chain); | 514 | error = hib_bio_read_page(offset, buf, bio_chain); |
577 | if (error) | 515 | if (error) |
578 | return error; | 516 | return error; |
579 | if (++handle->k >= MAP_PAGE_ENTRIES) { | 517 | if (++handle->k >= MAP_PAGE_ENTRIES) { |
580 | error = wait_on_bio_chain(bio_chain); | 518 | error = hib_wait_on_bio_chain(bio_chain); |
581 | handle->k = 0; | 519 | handle->k = 0; |
582 | offset = handle->cur->next_swap; | 520 | offset = handle->cur->next_swap; |
583 | if (!offset) | 521 | if (!offset) |
584 | release_swap_reader(handle); | 522 | release_swap_reader(handle); |
585 | else if (!error) | 523 | else if (!error) |
586 | error = bio_read_page(offset, handle->cur, NULL); | 524 | error = hib_bio_read_page(offset, handle->cur, NULL); |
587 | } | 525 | } |
588 | return error; | 526 | return error; |
589 | } | 527 | } |
590 | 528 | ||
529 | static int swap_reader_finish(struct swap_map_handle *handle) | ||
530 | { | ||
531 | release_swap_reader(handle); | ||
532 | |||
533 | return 0; | ||
534 | } | ||
535 | |||
591 | /** | 536 | /** |
592 | * load_image - load the image using the swap map handle | 537 | * load_image - load the image using the swap map handle |
593 | * @handle and the snapshot handle @snapshot | 538 | * @handle and the snapshot handle @snapshot |
@@ -615,21 +560,21 @@ static int load_image(struct swap_map_handle *handle, | |||
615 | bio = NULL; | 560 | bio = NULL; |
616 | do_gettimeofday(&start); | 561 | do_gettimeofday(&start); |
617 | for ( ; ; ) { | 562 | for ( ; ; ) { |
618 | error = snapshot_write_next(snapshot, PAGE_SIZE); | 563 | error = snapshot_write_next(snapshot); |
619 | if (error <= 0) | 564 | if (error <= 0) |
620 | break; | 565 | break; |
621 | error = swap_read_page(handle, data_of(*snapshot), &bio); | 566 | error = swap_read_page(handle, data_of(*snapshot), &bio); |
622 | if (error) | 567 | if (error) |
623 | break; | 568 | break; |
624 | if (snapshot->sync_read) | 569 | if (snapshot->sync_read) |
625 | error = wait_on_bio_chain(&bio); | 570 | error = hib_wait_on_bio_chain(&bio); |
626 | if (error) | 571 | if (error) |
627 | break; | 572 | break; |
628 | if (!(nr_pages % m)) | 573 | if (!(nr_pages % m)) |
629 | printk("\b\b\b\b%3d%%", nr_pages / m); | 574 | printk("\b\b\b\b%3d%%", nr_pages / m); |
630 | nr_pages++; | 575 | nr_pages++; |
631 | } | 576 | } |
632 | err2 = wait_on_bio_chain(&bio); | 577 | err2 = hib_wait_on_bio_chain(&bio); |
633 | do_gettimeofday(&stop); | 578 | do_gettimeofday(&stop); |
634 | if (!error) | 579 | if (!error) |
635 | error = err2; | 580 | error = err2; |
@@ -657,20 +602,20 @@ int swsusp_read(unsigned int *flags_p) | |||
657 | struct snapshot_handle snapshot; | 602 | struct snapshot_handle snapshot; |
658 | struct swsusp_info *header; | 603 | struct swsusp_info *header; |
659 | 604 | ||
660 | *flags_p = swsusp_header->flags; | ||
661 | |||
662 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); | 605 | memset(&snapshot, 0, sizeof(struct snapshot_handle)); |
663 | error = snapshot_write_next(&snapshot, PAGE_SIZE); | 606 | error = snapshot_write_next(&snapshot); |
664 | if (error < PAGE_SIZE) | 607 | if (error < PAGE_SIZE) |
665 | return error < 0 ? error : -EFAULT; | 608 | return error < 0 ? error : -EFAULT; |
666 | header = (struct swsusp_info *)data_of(snapshot); | 609 | header = (struct swsusp_info *)data_of(snapshot); |
667 | error = get_swap_reader(&handle, swsusp_header->image); | 610 | error = get_swap_reader(&handle, flags_p); |
611 | if (error) | ||
612 | goto end; | ||
668 | if (!error) | 613 | if (!error) |
669 | error = swap_read_page(&handle, header, NULL); | 614 | error = swap_read_page(&handle, header, NULL); |
670 | if (!error) | 615 | if (!error) |
671 | error = load_image(&handle, &snapshot, header->pages - 1); | 616 | error = load_image(&handle, &snapshot, header->pages - 1); |
672 | release_swap_reader(&handle); | 617 | swap_reader_finish(&handle); |
673 | 618 | end: | |
674 | if (!error) | 619 | if (!error) |
675 | pr_debug("PM: Image successfully loaded\n"); | 620 | pr_debug("PM: Image successfully loaded\n"); |
676 | else | 621 | else |
@@ -686,11 +631,11 @@ int swsusp_check(void) | |||
686 | { | 631 | { |
687 | int error; | 632 | int error; |
688 | 633 | ||
689 | resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ); | 634 | hib_resume_bdev = open_by_devnum(swsusp_resume_device, FMODE_READ); |
690 | if (!IS_ERR(resume_bdev)) { | 635 | if (!IS_ERR(hib_resume_bdev)) { |
691 | set_blocksize(resume_bdev, PAGE_SIZE); | 636 | set_blocksize(hib_resume_bdev, PAGE_SIZE); |
692 | memset(swsusp_header, 0, PAGE_SIZE); | 637 | memset(swsusp_header, 0, PAGE_SIZE); |
693 | error = bio_read_page(swsusp_resume_block, | 638 | error = hib_bio_read_page(swsusp_resume_block, |
694 | swsusp_header, NULL); | 639 | swsusp_header, NULL); |
695 | if (error) | 640 | if (error) |
696 | goto put; | 641 | goto put; |
@@ -698,7 +643,7 @@ int swsusp_check(void) | |||
698 | if (!memcmp(SWSUSP_SIG, swsusp_header->sig, 10)) { | 643 | if (!memcmp(SWSUSP_SIG, swsusp_header->sig, 10)) { |
699 | memcpy(swsusp_header->sig, swsusp_header->orig_sig, 10); | 644 | memcpy(swsusp_header->sig, swsusp_header->orig_sig, 10); |
700 | /* Reset swap signature now */ | 645 | /* Reset swap signature now */ |
701 | error = bio_write_page(swsusp_resume_block, | 646 | error = hib_bio_write_page(swsusp_resume_block, |
702 | swsusp_header, NULL); | 647 | swsusp_header, NULL); |
703 | } else { | 648 | } else { |
704 | error = -EINVAL; | 649 | error = -EINVAL; |
@@ -706,11 +651,11 @@ int swsusp_check(void) | |||
706 | 651 | ||
707 | put: | 652 | put: |
708 | if (error) | 653 | if (error) |
709 | blkdev_put(resume_bdev, FMODE_READ); | 654 | blkdev_put(hib_resume_bdev, FMODE_READ); |
710 | else | 655 | else |
711 | pr_debug("PM: Signature found, resuming\n"); | 656 | pr_debug("PM: Signature found, resuming\n"); |
712 | } else { | 657 | } else { |
713 | error = PTR_ERR(resume_bdev); | 658 | error = PTR_ERR(hib_resume_bdev); |
714 | } | 659 | } |
715 | 660 | ||
716 | if (error) | 661 | if (error) |
@@ -725,12 +670,12 @@ put: | |||
725 | 670 | ||
726 | void swsusp_close(fmode_t mode) | 671 | void swsusp_close(fmode_t mode) |
727 | { | 672 | { |
728 | if (IS_ERR(resume_bdev)) { | 673 | if (IS_ERR(hib_resume_bdev)) { |
729 | pr_debug("PM: Image device not initialised\n"); | 674 | pr_debug("PM: Image device not initialised\n"); |
730 | return; | 675 | return; |
731 | } | 676 | } |
732 | 677 | ||
733 | blkdev_put(resume_bdev, mode); | 678 | blkdev_put(hib_resume_bdev, mode); |
734 | } | 679 | } |
735 | 680 | ||
736 | static int swsusp_header_init(void) | 681 | static int swsusp_header_init(void) |
diff --git a/kernel/power/user.c b/kernel/power/user.c index a8c96212bc1b..e819e17877ca 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c | |||
@@ -151,6 +151,7 @@ static ssize_t snapshot_read(struct file *filp, char __user *buf, | |||
151 | { | 151 | { |
152 | struct snapshot_data *data; | 152 | struct snapshot_data *data; |
153 | ssize_t res; | 153 | ssize_t res; |
154 | loff_t pg_offp = *offp & ~PAGE_MASK; | ||
154 | 155 | ||
155 | mutex_lock(&pm_mutex); | 156 | mutex_lock(&pm_mutex); |
156 | 157 | ||
@@ -159,14 +160,19 @@ static ssize_t snapshot_read(struct file *filp, char __user *buf, | |||
159 | res = -ENODATA; | 160 | res = -ENODATA; |
160 | goto Unlock; | 161 | goto Unlock; |
161 | } | 162 | } |
162 | res = snapshot_read_next(&data->handle, count); | 163 | if (!pg_offp) { /* on page boundary? */ |
163 | if (res > 0) { | 164 | res = snapshot_read_next(&data->handle); |
164 | if (copy_to_user(buf, data_of(data->handle), res)) | 165 | if (res <= 0) |
165 | res = -EFAULT; | 166 | goto Unlock; |
166 | else | 167 | } else { |
167 | *offp = data->handle.offset; | 168 | res = PAGE_SIZE - pg_offp; |
168 | } | 169 | } |
169 | 170 | ||
171 | res = simple_read_from_buffer(buf, count, &pg_offp, | ||
172 | data_of(data->handle), res); | ||
173 | if (res > 0) | ||
174 | *offp += res; | ||
175 | |||
170 | Unlock: | 176 | Unlock: |
171 | mutex_unlock(&pm_mutex); | 177 | mutex_unlock(&pm_mutex); |
172 | 178 | ||
@@ -178,18 +184,25 @@ static ssize_t snapshot_write(struct file *filp, const char __user *buf, | |||
178 | { | 184 | { |
179 | struct snapshot_data *data; | 185 | struct snapshot_data *data; |
180 | ssize_t res; | 186 | ssize_t res; |
187 | loff_t pg_offp = *offp & ~PAGE_MASK; | ||
181 | 188 | ||
182 | mutex_lock(&pm_mutex); | 189 | mutex_lock(&pm_mutex); |
183 | 190 | ||
184 | data = filp->private_data; | 191 | data = filp->private_data; |
185 | res = snapshot_write_next(&data->handle, count); | 192 | |
186 | if (res > 0) { | 193 | if (!pg_offp) { |
187 | if (copy_from_user(data_of(data->handle), buf, res)) | 194 | res = snapshot_write_next(&data->handle); |
188 | res = -EFAULT; | 195 | if (res <= 0) |
189 | else | 196 | goto unlock; |
190 | *offp = data->handle.offset; | 197 | } else { |
198 | res = PAGE_SIZE - pg_offp; | ||
191 | } | 199 | } |
192 | 200 | ||
201 | res = simple_write_to_buffer(data_of(data->handle), res, &pg_offp, | ||
202 | buf, count); | ||
203 | if (res > 0) | ||
204 | *offp += res; | ||
205 | unlock: | ||
193 | mutex_unlock(&pm_mutex); | 206 | mutex_unlock(&pm_mutex); |
194 | 207 | ||
195 | return res; | 208 | return res; |