aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@suse.de>2010-07-18 20:00:18 -0400
committerRafael J. Wysocki <rjw@sisk.pl>2010-07-18 20:00:18 -0400
commit5f279845f9d684661563894d44729a0c706375b4 (patch)
treede26a630f8b573ccf725a48652700504d1088d8d /kernel
parent12e4d0cc2e0a776a526c93bb2fcb9267abc6e0b1 (diff)
pm_qos: Reimplement using plists
A lot of the pm_qos extremal value handling is really duplicating what a priority ordered list does, just in a less efficient fashion. Simply redoing the implementation in terms of a plist gets rid of a lot of this junk (although there are several other strange things that could do with tidying up, like pm_qos_request_list has to carry the pm_qos_class with every node, simply because it doesn't get passed in to pm_qos_update_request even though every caller knows full well what parameter it's updating). I think this redo is a win independent of android, so we should do something like this now. Signed-off-by: James Bottomley <James.Bottomley@suse.de> Signed-off-by: mark gross <markgross@thegnar.org> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/pm_qos_params.c172
1 files changed, 86 insertions, 86 deletions
diff --git a/kernel/pm_qos_params.c b/kernel/pm_qos_params.c
index f42d3f737a33..db8e51d7f392 100644
--- a/kernel/pm_qos_params.c
+++ b/kernel/pm_qos_params.c
@@ -30,6 +30,7 @@
30/*#define DEBUG*/ 30/*#define DEBUG*/
31 31
32#include <linux/pm_qos_params.h> 32#include <linux/pm_qos_params.h>
33#include <linux/plist.h>
33#include <linux/sched.h> 34#include <linux/sched.h>
34#include <linux/spinlock.h> 35#include <linux/spinlock.h>
35#include <linux/slab.h> 36#include <linux/slab.h>
@@ -49,58 +50,53 @@
49 * held, taken with _irqsave. One lock to rule them all 50 * held, taken with _irqsave. One lock to rule them all
50 */ 51 */
51struct pm_qos_request_list { 52struct pm_qos_request_list {
52 struct list_head list; 53 struct plist_node list;
53 union {
54 s32 value;
55 s32 usec;
56 s32 kbps;
57 };
58 int pm_qos_class; 54 int pm_qos_class;
59}; 55};
60 56
61static s32 max_compare(s32 v1, s32 v2); 57enum pm_qos_type {
62static s32 min_compare(s32 v1, s32 v2); 58 PM_QOS_MAX, /* return the largest value */
59 PM_QOS_MIN /* return the smallest value */
60};
63 61
64struct pm_qos_object { 62struct pm_qos_object {
65 struct pm_qos_request_list requests; 63 struct plist_head requests;
66 struct blocking_notifier_head *notifiers; 64 struct blocking_notifier_head *notifiers;
67 struct miscdevice pm_qos_power_miscdev; 65 struct miscdevice pm_qos_power_miscdev;
68 char *name; 66 char *name;
69 s32 default_value; 67 s32 default_value;
70 atomic_t target_value; 68 enum pm_qos_type type;
71 s32 (*comparitor)(s32, s32);
72}; 69};
73 70
71static DEFINE_SPINLOCK(pm_qos_lock);
72
74static struct pm_qos_object null_pm_qos; 73static struct pm_qos_object null_pm_qos;
75static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier); 74static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
76static struct pm_qos_object cpu_dma_pm_qos = { 75static struct pm_qos_object cpu_dma_pm_qos = {
77 .requests = {LIST_HEAD_INIT(cpu_dma_pm_qos.requests.list)}, 76 .requests = PLIST_HEAD_INIT(cpu_dma_pm_qos.requests, pm_qos_lock),
78 .notifiers = &cpu_dma_lat_notifier, 77 .notifiers = &cpu_dma_lat_notifier,
79 .name = "cpu_dma_latency", 78 .name = "cpu_dma_latency",
80 .default_value = 2000 * USEC_PER_SEC, 79 .default_value = 2000 * USEC_PER_SEC,
81 .target_value = ATOMIC_INIT(2000 * USEC_PER_SEC), 80 .type = PM_QOS_MIN,
82 .comparitor = min_compare
83}; 81};
84 82
85static BLOCKING_NOTIFIER_HEAD(network_lat_notifier); 83static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
86static struct pm_qos_object network_lat_pm_qos = { 84static struct pm_qos_object network_lat_pm_qos = {
87 .requests = {LIST_HEAD_INIT(network_lat_pm_qos.requests.list)}, 85 .requests = PLIST_HEAD_INIT(network_lat_pm_qos.requests, pm_qos_lock),
88 .notifiers = &network_lat_notifier, 86 .notifiers = &network_lat_notifier,
89 .name = "network_latency", 87 .name = "network_latency",
90 .default_value = 2000 * USEC_PER_SEC, 88 .default_value = 2000 * USEC_PER_SEC,
91 .target_value = ATOMIC_INIT(2000 * USEC_PER_SEC), 89 .type = PM_QOS_MIN
92 .comparitor = min_compare
93}; 90};
94 91
95 92
96static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier); 93static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
97static struct pm_qos_object network_throughput_pm_qos = { 94static struct pm_qos_object network_throughput_pm_qos = {
98 .requests = {LIST_HEAD_INIT(network_throughput_pm_qos.requests.list)}, 95 .requests = PLIST_HEAD_INIT(network_throughput_pm_qos.requests, pm_qos_lock),
99 .notifiers = &network_throughput_notifier, 96 .notifiers = &network_throughput_notifier,
100 .name = "network_throughput", 97 .name = "network_throughput",
101 .default_value = 0, 98 .default_value = 0,
102 .target_value = ATOMIC_INIT(0), 99 .type = PM_QOS_MAX,
103 .comparitor = max_compare
104}; 100};
105 101
106 102
@@ -111,8 +107,6 @@ static struct pm_qos_object *pm_qos_array[] = {
111 &network_throughput_pm_qos 107 &network_throughput_pm_qos
112}; 108};
113 109
114static DEFINE_SPINLOCK(pm_qos_lock);
115
116static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, 110static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
117 size_t count, loff_t *f_pos); 111 size_t count, loff_t *f_pos);
118static int pm_qos_power_open(struct inode *inode, struct file *filp); 112static int pm_qos_power_open(struct inode *inode, struct file *filp);
@@ -124,46 +118,55 @@ static const struct file_operations pm_qos_power_fops = {
124 .release = pm_qos_power_release, 118 .release = pm_qos_power_release,
125}; 119};
126 120
127/* static helper functions */ 121/* unlocked internal variant */
128static s32 max_compare(s32 v1, s32 v2) 122static inline int pm_qos_get_value(struct pm_qos_object *o)
129{ 123{
130 return max(v1, v2); 124 if (plist_head_empty(&o->requests))
131} 125 return o->default_value;
132 126
133static s32 min_compare(s32 v1, s32 v2) 127 switch (o->type) {
134{ 128 case PM_QOS_MIN:
135 return min(v1, v2); 129 return plist_last(&o->requests)->prio;
136}
137 130
131 case PM_QOS_MAX:
132 return plist_first(&o->requests)->prio;
138 133
139static void update_target(int pm_qos_class) 134 default:
135 /* runtime check for not using enum */
136 BUG();
137 }
138}
139
140static void update_target(struct pm_qos_object *o, struct plist_node *node,
141 int del, int value)
140{ 142{
141 s32 extreme_value;
142 struct pm_qos_request_list *node;
143 unsigned long flags; 143 unsigned long flags;
144 int call_notifier = 0; 144 int prev_value, curr_value;
145 145
146 spin_lock_irqsave(&pm_qos_lock, flags); 146 spin_lock_irqsave(&pm_qos_lock, flags);
147 extreme_value = pm_qos_array[pm_qos_class]->default_value; 147 prev_value = pm_qos_get_value(o);
148 list_for_each_entry(node, 148 /* PM_QOS_DEFAULT_VALUE is a signal that the value is unchanged */
149 &pm_qos_array[pm_qos_class]->requests.list, list) { 149 if (value != PM_QOS_DEFAULT_VALUE) {
150 extreme_value = pm_qos_array[pm_qos_class]->comparitor( 150 /*
151 extreme_value, node->value); 151 * to change the list, we atomically remove, reinit
152 } 152 * with new value and add, then see if the extremal
153 if (atomic_read(&pm_qos_array[pm_qos_class]->target_value) != 153 * changed
154 extreme_value) { 154 */
155 call_notifier = 1; 155 plist_del(node, &o->requests);
156 atomic_set(&pm_qos_array[pm_qos_class]->target_value, 156 plist_node_init(node, value);
157 extreme_value); 157 plist_add(node, &o->requests);
158 pr_debug(KERN_ERR "new target for qos %d is %d\n", pm_qos_class, 158 } else if (del) {
159 atomic_read(&pm_qos_array[pm_qos_class]->target_value)); 159 plist_del(node, &o->requests);
160 } else {
161 plist_add(node, &o->requests);
160 } 162 }
163 curr_value = pm_qos_get_value(o);
161 spin_unlock_irqrestore(&pm_qos_lock, flags); 164 spin_unlock_irqrestore(&pm_qos_lock, flags);
162 165
163 if (call_notifier) 166 if (prev_value != curr_value)
164 blocking_notifier_call_chain( 167 blocking_notifier_call_chain(o->notifiers,
165 pm_qos_array[pm_qos_class]->notifiers, 168 (unsigned long)curr_value,
166 (unsigned long) extreme_value, NULL); 169 NULL);
167} 170}
168 171
169static int register_pm_qos_misc(struct pm_qos_object *qos) 172static int register_pm_qos_misc(struct pm_qos_object *qos)
@@ -196,7 +199,14 @@ static int find_pm_qos_object_by_minor(int minor)
196 */ 199 */
197int pm_qos_request(int pm_qos_class) 200int pm_qos_request(int pm_qos_class)
198{ 201{
199 return atomic_read(&pm_qos_array[pm_qos_class]->target_value); 202 unsigned long flags;
203 int value;
204
205 spin_lock_irqsave(&pm_qos_lock, flags);
206 value = pm_qos_get_value(pm_qos_array[pm_qos_class]);
207 spin_unlock_irqrestore(&pm_qos_lock, flags);
208
209 return value;
200} 210}
201EXPORT_SYMBOL_GPL(pm_qos_request); 211EXPORT_SYMBOL_GPL(pm_qos_request);
202 212
@@ -214,21 +224,19 @@ EXPORT_SYMBOL_GPL(pm_qos_request);
214struct pm_qos_request_list *pm_qos_add_request(int pm_qos_class, s32 value) 224struct pm_qos_request_list *pm_qos_add_request(int pm_qos_class, s32 value)
215{ 225{
216 struct pm_qos_request_list *dep; 226 struct pm_qos_request_list *dep;
217 unsigned long flags;
218 227
219 dep = kzalloc(sizeof(struct pm_qos_request_list), GFP_KERNEL); 228 dep = kzalloc(sizeof(struct pm_qos_request_list), GFP_KERNEL);
220 if (dep) { 229 if (dep) {
230 struct pm_qos_object *o = pm_qos_array[pm_qos_class];
231 int new_value;
232
221 if (value == PM_QOS_DEFAULT_VALUE) 233 if (value == PM_QOS_DEFAULT_VALUE)
222 dep->value = pm_qos_array[pm_qos_class]->default_value; 234 new_value = o->default_value;
223 else 235 else
224 dep->value = value; 236 new_value = value;
237 plist_node_init(&dep->list, new_value);
225 dep->pm_qos_class = pm_qos_class; 238 dep->pm_qos_class = pm_qos_class;
226 239 update_target(o, &dep->list, 0, PM_QOS_DEFAULT_VALUE);
227 spin_lock_irqsave(&pm_qos_lock, flags);
228 list_add(&dep->list,
229 &pm_qos_array[pm_qos_class]->requests.list);
230 spin_unlock_irqrestore(&pm_qos_lock, flags);
231 update_target(pm_qos_class);
232 } 240 }
233 241
234 return dep; 242 return dep;
@@ -246,27 +254,23 @@ EXPORT_SYMBOL_GPL(pm_qos_add_request);
246 * Attempts are made to make this code callable on hot code paths. 254 * Attempts are made to make this code callable on hot code paths.
247 */ 255 */
248void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req, 256void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
249 s32 new_value) 257 s32 new_value)
250{ 258{
251 unsigned long flags;
252 int pending_update = 0;
253 s32 temp; 259 s32 temp;
260 struct pm_qos_object *o;
254 261
255 if (pm_qos_req) { /*guard against callers passing in null */ 262 if (!pm_qos_req) /*guard against callers passing in null */
256 spin_lock_irqsave(&pm_qos_lock, flags); 263 return;
257 if (new_value == PM_QOS_DEFAULT_VALUE) 264
258 temp = pm_qos_array[pm_qos_req->pm_qos_class]->default_value; 265 o = pm_qos_array[pm_qos_req->pm_qos_class];
259 else 266
260 temp = new_value; 267 if (new_value == PM_QOS_DEFAULT_VALUE)
261 268 temp = o->default_value;
262 if (temp != pm_qos_req->value) { 269 else
263 pending_update = 1; 270 temp = new_value;
264 pm_qos_req->value = temp; 271
265 } 272 if (temp != pm_qos_req->list.prio)
266 spin_unlock_irqrestore(&pm_qos_lock, flags); 273 update_target(o, &pm_qos_req->list, 0, temp);
267 if (pending_update)
268 update_target(pm_qos_req->pm_qos_class);
269 }
270} 274}
271EXPORT_SYMBOL_GPL(pm_qos_update_request); 275EXPORT_SYMBOL_GPL(pm_qos_update_request);
272 276
@@ -280,19 +284,15 @@ EXPORT_SYMBOL_GPL(pm_qos_update_request);
280 */ 284 */
281void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req) 285void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req)
282{ 286{
283 unsigned long flags; 287 struct pm_qos_object *o;
284 int qos_class;
285 288
286 if (pm_qos_req == NULL) 289 if (pm_qos_req == NULL)
287 return; 290 return;
288 /* silent return to keep pcm code cleaner */ 291 /* silent return to keep pcm code cleaner */
289 292
290 qos_class = pm_qos_req->pm_qos_class; 293 o = pm_qos_array[pm_qos_req->pm_qos_class];
291 spin_lock_irqsave(&pm_qos_lock, flags); 294 update_target(o, &pm_qos_req->list, 1, PM_QOS_DEFAULT_VALUE);
292 list_del(&pm_qos_req->list);
293 kfree(pm_qos_req); 295 kfree(pm_qos_req);
294 spin_unlock_irqrestore(&pm_qos_lock, flags);
295 update_target(qos_class);
296} 296}
297EXPORT_SYMBOL_GPL(pm_qos_remove_request); 297EXPORT_SYMBOL_GPL(pm_qos_remove_request);
298 298