diff options
-rw-r--r-- | include/linux/pm_qos.h | 14 | ||||
-rw-r--r-- | kernel/power/qos.c | 123 |
2 files changed, 81 insertions, 56 deletions
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 97723113ae92..84aa15089896 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h | |||
@@ -44,7 +44,16 @@ struct pm_qos_constraints { | |||
44 | struct blocking_notifier_head *notifiers; | 44 | struct blocking_notifier_head *notifiers; |
45 | }; | 45 | }; |
46 | 46 | ||
47 | /* Action requested to pm_qos_update_target */ | ||
48 | enum pm_qos_req_action { | ||
49 | PM_QOS_ADD_REQ, /* Add a new request */ | ||
50 | PM_QOS_UPDATE_REQ, /* Update an existing request */ | ||
51 | PM_QOS_REMOVE_REQ /* Remove an existing request */ | ||
52 | }; | ||
53 | |||
47 | #ifdef CONFIG_PM | 54 | #ifdef CONFIG_PM |
55 | int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node, | ||
56 | enum pm_qos_req_action action, int value); | ||
48 | void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, | 57 | void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, |
49 | s32 value); | 58 | s32 value); |
50 | void pm_qos_update_request(struct pm_qos_request *req, | 59 | void pm_qos_update_request(struct pm_qos_request *req, |
@@ -56,6 +65,11 @@ int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier); | |||
56 | int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier); | 65 | int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier); |
57 | int pm_qos_request_active(struct pm_qos_request *req); | 66 | int pm_qos_request_active(struct pm_qos_request *req); |
58 | #else | 67 | #else |
68 | static inline int pm_qos_update_target(struct pm_qos_constraints *c, | ||
69 | struct plist_node *node, | ||
70 | enum pm_qos_req_action action, | ||
71 | int value) | ||
72 | { return 0; } | ||
59 | static inline void pm_qos_add_request(struct pm_qos_request *req, | 73 | static inline void pm_qos_add_request(struct pm_qos_request *req, |
60 | int pm_qos_class, s32 value) | 74 | int pm_qos_class, s32 value) |
61 | { return; } | 75 | { return; } |
diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 4a35fe50b777..7c7cd181cabe 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c | |||
@@ -122,17 +122,17 @@ static const struct file_operations pm_qos_power_fops = { | |||
122 | }; | 122 | }; |
123 | 123 | ||
124 | /* unlocked internal variant */ | 124 | /* unlocked internal variant */ |
125 | static inline int pm_qos_get_value(struct pm_qos_object *o) | 125 | static inline int pm_qos_get_value(struct pm_qos_constraints *c) |
126 | { | 126 | { |
127 | if (plist_head_empty(&o->constraints->list)) | 127 | if (plist_head_empty(&c->list)) |
128 | return o->constraints->default_value; | 128 | return c->default_value; |
129 | 129 | ||
130 | switch (o->constraints->type) { | 130 | switch (c->type) { |
131 | case PM_QOS_MIN: | 131 | case PM_QOS_MIN: |
132 | return plist_first(&o->constraints->list)->prio; | 132 | return plist_first(&c->list)->prio; |
133 | 133 | ||
134 | case PM_QOS_MAX: | 134 | case PM_QOS_MAX: |
135 | return plist_last(&o->constraints->list)->prio; | 135 | return plist_last(&c->list)->prio; |
136 | 136 | ||
137 | default: | 137 | default: |
138 | /* runtime check for not using enum */ | 138 | /* runtime check for not using enum */ |
@@ -140,47 +140,73 @@ static inline int pm_qos_get_value(struct pm_qos_object *o) | |||
140 | } | 140 | } |
141 | } | 141 | } |
142 | 142 | ||
143 | static inline s32 pm_qos_read_value(struct pm_qos_object *o) | 143 | static inline s32 pm_qos_read_value(struct pm_qos_constraints *c) |
144 | { | 144 | { |
145 | return o->constraints->target_value; | 145 | return c->target_value; |
146 | } | 146 | } |
147 | 147 | ||
148 | static inline void pm_qos_set_value(struct pm_qos_object *o, s32 value) | 148 | static inline void pm_qos_set_value(struct pm_qos_constraints *c, s32 value) |
149 | { | 149 | { |
150 | o->constraints->target_value = value; | 150 | c->target_value = value; |
151 | } | 151 | } |
152 | 152 | ||
153 | static void update_target(struct pm_qos_object *o, struct plist_node *node, | 153 | /** |
154 | int del, int value) | 154 | * pm_qos_update_target - manages the constraints list and calls the notifiers |
155 | * if needed | ||
156 | * @c: constraints data struct | ||
157 | * @node: request to add to the list, to update or to remove | ||
158 | * @action: action to take on the constraints list | ||
159 | * @value: value of the request to add or update | ||
160 | * | ||
161 | * This function returns 1 if the aggregated constraint value has changed, 0 | ||
162 | * otherwise. | ||
163 | */ | ||
164 | int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node, | ||
165 | enum pm_qos_req_action action, int value) | ||
155 | { | 166 | { |
156 | unsigned long flags; | 167 | unsigned long flags; |
157 | int prev_value, curr_value; | 168 | int prev_value, curr_value, new_value; |
158 | 169 | ||
159 | spin_lock_irqsave(&pm_qos_lock, flags); | 170 | spin_lock_irqsave(&pm_qos_lock, flags); |
160 | prev_value = pm_qos_get_value(o); | 171 | prev_value = pm_qos_get_value(c); |
161 | /* PM_QOS_DEFAULT_VALUE is a signal that the value is unchanged */ | 172 | if (value == PM_QOS_DEFAULT_VALUE) |
162 | if (value != PM_QOS_DEFAULT_VALUE) { | 173 | new_value = c->default_value; |
174 | else | ||
175 | new_value = value; | ||
176 | |||
177 | switch (action) { | ||
178 | case PM_QOS_REMOVE_REQ: | ||
179 | plist_del(node, &c->list); | ||
180 | break; | ||
181 | case PM_QOS_UPDATE_REQ: | ||
163 | /* | 182 | /* |
164 | * to change the list, we atomically remove, reinit | 183 | * to change the list, we atomically remove, reinit |
165 | * with new value and add, then see if the extremal | 184 | * with new value and add, then see if the extremal |
166 | * changed | 185 | * changed |
167 | */ | 186 | */ |
168 | plist_del(node, &o->constraints->list); | 187 | plist_del(node, &c->list); |
169 | plist_node_init(node, value); | 188 | case PM_QOS_ADD_REQ: |
170 | plist_add(node, &o->constraints->list); | 189 | plist_node_init(node, new_value); |
171 | } else if (del) { | 190 | plist_add(node, &c->list); |
172 | plist_del(node, &o->constraints->list); | 191 | break; |
173 | } else { | 192 | default: |
174 | plist_add(node, &o->constraints->list); | 193 | /* no action */ |
194 | ; | ||
175 | } | 195 | } |
176 | curr_value = pm_qos_get_value(o); | 196 | |
177 | pm_qos_set_value(o, curr_value); | 197 | curr_value = pm_qos_get_value(c); |
198 | pm_qos_set_value(c, curr_value); | ||
199 | |||
178 | spin_unlock_irqrestore(&pm_qos_lock, flags); | 200 | spin_unlock_irqrestore(&pm_qos_lock, flags); |
179 | 201 | ||
180 | if (prev_value != curr_value) | 202 | if (prev_value != curr_value) { |
181 | blocking_notifier_call_chain(o->constraints->notifiers, | 203 | blocking_notifier_call_chain(c->notifiers, |
182 | (unsigned long)curr_value, | 204 | (unsigned long)curr_value, |
183 | NULL); | 205 | NULL); |
206 | return 1; | ||
207 | } else { | ||
208 | return 0; | ||
209 | } | ||
184 | } | 210 | } |
185 | 211 | ||
186 | /** | 212 | /** |
@@ -191,7 +217,7 @@ static void update_target(struct pm_qos_object *o, struct plist_node *node, | |||
191 | */ | 217 | */ |
192 | int pm_qos_request(int pm_qos_class) | 218 | int pm_qos_request(int pm_qos_class) |
193 | { | 219 | { |
194 | return pm_qos_read_value(pm_qos_array[pm_qos_class]); | 220 | return pm_qos_read_value(pm_qos_array[pm_qos_class]->constraints); |
195 | } | 221 | } |
196 | EXPORT_SYMBOL_GPL(pm_qos_request); | 222 | EXPORT_SYMBOL_GPL(pm_qos_request); |
197 | 223 | ||
@@ -217,20 +243,16 @@ EXPORT_SYMBOL_GPL(pm_qos_request_active); | |||
217 | void pm_qos_add_request(struct pm_qos_request *req, | 243 | void pm_qos_add_request(struct pm_qos_request *req, |
218 | int pm_qos_class, s32 value) | 244 | int pm_qos_class, s32 value) |
219 | { | 245 | { |
220 | struct pm_qos_object *o = pm_qos_array[pm_qos_class]; | 246 | if (!req) /*guard against callers passing in null */ |
221 | int new_value; | 247 | return; |
222 | 248 | ||
223 | if (pm_qos_request_active(req)) { | 249 | if (pm_qos_request_active(req)) { |
224 | WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n"); | 250 | WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n"); |
225 | return; | 251 | return; |
226 | } | 252 | } |
227 | if (value == PM_QOS_DEFAULT_VALUE) | ||
228 | new_value = o->constraints->default_value; | ||
229 | else | ||
230 | new_value = value; | ||
231 | plist_node_init(&req->node, new_value); | ||
232 | req->pm_qos_class = pm_qos_class; | 253 | req->pm_qos_class = pm_qos_class; |
233 | update_target(o, &req->node, 0, PM_QOS_DEFAULT_VALUE); | 254 | pm_qos_update_target(pm_qos_array[pm_qos_class]->constraints, |
255 | &req->node, PM_QOS_ADD_REQ, value); | ||
234 | } | 256 | } |
235 | EXPORT_SYMBOL_GPL(pm_qos_add_request); | 257 | EXPORT_SYMBOL_GPL(pm_qos_add_request); |
236 | 258 | ||
@@ -247,9 +269,6 @@ EXPORT_SYMBOL_GPL(pm_qos_add_request); | |||
247 | void pm_qos_update_request(struct pm_qos_request *req, | 269 | void pm_qos_update_request(struct pm_qos_request *req, |
248 | s32 new_value) | 270 | s32 new_value) |
249 | { | 271 | { |
250 | s32 temp; | ||
251 | struct pm_qos_object *o; | ||
252 | |||
253 | if (!req) /*guard against callers passing in null */ | 272 | if (!req) /*guard against callers passing in null */ |
254 | return; | 273 | return; |
255 | 274 | ||
@@ -258,15 +277,10 @@ void pm_qos_update_request(struct pm_qos_request *req, | |||
258 | return; | 277 | return; |
259 | } | 278 | } |
260 | 279 | ||
261 | o = pm_qos_array[req->pm_qos_class]; | 280 | if (new_value != req->node.prio) |
262 | 281 | pm_qos_update_target( | |
263 | if (new_value == PM_QOS_DEFAULT_VALUE) | 282 | pm_qos_array[req->pm_qos_class]->constraints, |
264 | temp = o->constraints->default_value; | 283 | &req->node, PM_QOS_UPDATE_REQ, new_value); |
265 | else | ||
266 | temp = new_value; | ||
267 | |||
268 | if (temp != req->node.prio) | ||
269 | update_target(o, &req->node, 0, temp); | ||
270 | } | 284 | } |
271 | EXPORT_SYMBOL_GPL(pm_qos_update_request); | 285 | EXPORT_SYMBOL_GPL(pm_qos_update_request); |
272 | 286 | ||
@@ -280,9 +294,7 @@ EXPORT_SYMBOL_GPL(pm_qos_update_request); | |||
280 | */ | 294 | */ |
281 | void pm_qos_remove_request(struct pm_qos_request *req) | 295 | void pm_qos_remove_request(struct pm_qos_request *req) |
282 | { | 296 | { |
283 | struct pm_qos_object *o; | 297 | if (!req) /*guard against callers passing in null */ |
284 | |||
285 | if (req == NULL) | ||
286 | return; | 298 | return; |
287 | /* silent return to keep pcm code cleaner */ | 299 | /* silent return to keep pcm code cleaner */ |
288 | 300 | ||
@@ -291,8 +303,9 @@ void pm_qos_remove_request(struct pm_qos_request *req) | |||
291 | return; | 303 | return; |
292 | } | 304 | } |
293 | 305 | ||
294 | o = pm_qos_array[req->pm_qos_class]; | 306 | pm_qos_update_target(pm_qos_array[req->pm_qos_class]->constraints, |
295 | update_target(o, &req->node, 1, PM_QOS_DEFAULT_VALUE); | 307 | &req->node, PM_QOS_REMOVE_REQ, |
308 | PM_QOS_DEFAULT_VALUE); | ||
296 | memset(req, 0, sizeof(*req)); | 309 | memset(req, 0, sizeof(*req)); |
297 | } | 310 | } |
298 | EXPORT_SYMBOL_GPL(pm_qos_remove_request); | 311 | EXPORT_SYMBOL_GPL(pm_qos_remove_request); |
@@ -396,7 +409,6 @@ static ssize_t pm_qos_power_read(struct file *filp, char __user *buf, | |||
396 | { | 409 | { |
397 | s32 value; | 410 | s32 value; |
398 | unsigned long flags; | 411 | unsigned long flags; |
399 | struct pm_qos_object *o; | ||
400 | struct pm_qos_request *req = filp->private_data; | 412 | struct pm_qos_request *req = filp->private_data; |
401 | 413 | ||
402 | if (!req) | 414 | if (!req) |
@@ -404,9 +416,8 @@ static ssize_t pm_qos_power_read(struct file *filp, char __user *buf, | |||
404 | if (!pm_qos_request_active(req)) | 416 | if (!pm_qos_request_active(req)) |
405 | return -EINVAL; | 417 | return -EINVAL; |
406 | 418 | ||
407 | o = pm_qos_array[req->pm_qos_class]; | ||
408 | spin_lock_irqsave(&pm_qos_lock, flags); | 419 | spin_lock_irqsave(&pm_qos_lock, flags); |
409 | value = pm_qos_get_value(o); | 420 | value = pm_qos_get_value(pm_qos_array[req->pm_qos_class]->constraints); |
410 | spin_unlock_irqrestore(&pm_qos_lock, flags); | 421 | spin_unlock_irqrestore(&pm_qos_lock, flags); |
411 | 422 | ||
412 | return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32)); | 423 | return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32)); |