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; |
