]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - kernel/power/qos.c
PM QoS: Reorganize data structs
[mirror_ubuntu-zesty-kernel.git] / kernel / power / qos.c
CommitLineData
d82b3518
MG
1/*
2 * This module exposes the interface to kernel space for specifying
3 * QoS dependencies. It provides infrastructure for registration of:
4 *
ed77134b 5 * Dependents on a QoS value : register requests
d82b3518
MG
6 * Watchers of QoS value : get notified when target QoS value changes
7 *
8 * This QoS design is best effort based. Dependents register their QoS needs.
9 * Watchers register to keep track of the current QoS needs of the system.
10 *
11 * There are 3 basic classes of QoS parameter: latency, timeout, throughput
12 * each have defined units:
13 * latency: usec
14 * timeout: usec <-- currently not used.
15 * throughput: kbs (kilo byte / sec)
16 *
ed77134b 17 * There are lists of pm_qos_objects each one wrapping requests, notifiers
d82b3518 18 *
ed77134b 19 * User mode requests on a QOS parameter register themselves to the
d82b3518
MG
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
22 * client continues to be accounted for. Upon file release the usermode
ed77134b
MG
23 * request is removed and a new qos target is computed. This way when the
24 * request that the application has is cleaned up when closes the file
d82b3518
MG
25 * pointer or exits the pm_qos_object will get an opportunity to clean up.
26 *
bf1db69f 27 * Mark Gross <mgross@linux.intel.com>
d82b3518
MG
28 */
29
ed77134b
MG
30/*#define DEBUG*/
31
e8db0be1 32#include <linux/pm_qos.h>
d82b3518
MG
33#include <linux/sched.h>
34#include <linux/spinlock.h>
35#include <linux/slab.h>
36#include <linux/time.h>
37#include <linux/fs.h>
38#include <linux/device.h>
39#include <linux/miscdevice.h>
40#include <linux/string.h>
41#include <linux/platform_device.h>
42#include <linux/init.h>
0775a60a 43#include <linux/kernel.h>
d82b3518
MG
44
45#include <linux/uaccess.h>
46
47/*
cc749986 48 * locking rule: all changes to constraints or notifiers lists
d82b3518
MG
49 * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock
50 * held, taken with _irqsave. One lock to rule them all
51 */
d82b3518 52struct pm_qos_object {
4e1779ba 53 struct pm_qos_constraints *constraints;
d82b3518
MG
54 struct miscdevice pm_qos_power_miscdev;
55 char *name;
d82b3518
MG
56};
57
5f279845
JB
58static DEFINE_SPINLOCK(pm_qos_lock);
59
d82b3518 60static struct pm_qos_object null_pm_qos;
4e1779ba 61
d82b3518 62static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
4e1779ba
JP
63static struct pm_qos_constraints cpu_dma_constraints = {
64 .list = PLIST_HEAD_INIT(cpu_dma_constraints.list),
333c5ae9
TC
65 .target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
66 .default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
5f279845 67 .type = PM_QOS_MIN,
4e1779ba
JP
68 .notifiers = &cpu_dma_lat_notifier,
69};
70static struct pm_qos_object cpu_dma_pm_qos = {
71 .constraints = &cpu_dma_constraints,
d82b3518
MG
72};
73
74static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
4e1779ba
JP
75static struct pm_qos_constraints network_lat_constraints = {
76 .list = PLIST_HEAD_INIT(network_lat_constraints.list),
333c5ae9
TC
77 .target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
78 .default_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
4e1779ba
JP
79 .type = PM_QOS_MIN,
80 .notifiers = &network_lat_notifier,
81};
82static struct pm_qos_object network_lat_pm_qos = {
83 .constraints = &network_lat_constraints,
84 .name = "network_latency",
d82b3518
MG
85};
86
87
88static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
4e1779ba
JP
89static struct pm_qos_constraints network_tput_constraints = {
90 .list = PLIST_HEAD_INIT(network_tput_constraints.list),
333c5ae9
TC
91 .target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
92 .default_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
5f279845 93 .type = PM_QOS_MAX,
4e1779ba
JP
94 .notifiers = &network_throughput_notifier,
95};
96static struct pm_qos_object network_throughput_pm_qos = {
97 .constraints = &network_tput_constraints,
98 .name = "network_throughput",
d82b3518
MG
99};
100
101
102static struct pm_qos_object *pm_qos_array[] = {
103 &null_pm_qos,
104 &cpu_dma_pm_qos,
105 &network_lat_pm_qos,
106 &network_throughput_pm_qos
107};
108
d82b3518
MG
109static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
110 size_t count, loff_t *f_pos);
f9b9e806
TR
111static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
112 size_t count, loff_t *f_pos);
d82b3518
MG
113static int pm_qos_power_open(struct inode *inode, struct file *filp);
114static int pm_qos_power_release(struct inode *inode, struct file *filp);
115
116static const struct file_operations pm_qos_power_fops = {
117 .write = pm_qos_power_write,
f9b9e806 118 .read = pm_qos_power_read,
d82b3518
MG
119 .open = pm_qos_power_open,
120 .release = pm_qos_power_release,
6038f373 121 .llseek = noop_llseek,
d82b3518
MG
122};
123
5f279845
JB
124/* unlocked internal variant */
125static inline int pm_qos_get_value(struct pm_qos_object *o)
d82b3518 126{
4e1779ba
JP
127 if (plist_head_empty(&o->constraints->list))
128 return o->constraints->default_value;
d82b3518 129
4e1779ba 130 switch (o->constraints->type) {
5f279845 131 case PM_QOS_MIN:
4e1779ba 132 return plist_first(&o->constraints->list)->prio;
d82b3518 133
5f279845 134 case PM_QOS_MAX:
4e1779ba 135 return plist_last(&o->constraints->list)->prio;
d82b3518 136
5f279845
JB
137 default:
138 /* runtime check for not using enum */
139 BUG();
140 }
141}
142
333c5ae9
TC
143static inline s32 pm_qos_read_value(struct pm_qos_object *o)
144{
4e1779ba 145 return o->constraints->target_value;
333c5ae9
TC
146}
147
148static inline void pm_qos_set_value(struct pm_qos_object *o, s32 value)
149{
4e1779ba 150 o->constraints->target_value = value;
333c5ae9
TC
151}
152
5f279845
JB
153static void update_target(struct pm_qos_object *o, struct plist_node *node,
154 int del, int value)
d82b3518 155{
d82b3518 156 unsigned long flags;
5f279845 157 int prev_value, curr_value;
d82b3518
MG
158
159 spin_lock_irqsave(&pm_qos_lock, flags);
5f279845
JB
160 prev_value = pm_qos_get_value(o);
161 /* PM_QOS_DEFAULT_VALUE is a signal that the value is unchanged */
162 if (value != PM_QOS_DEFAULT_VALUE) {
163 /*
164 * to change the list, we atomically remove, reinit
165 * with new value and add, then see if the extremal
166 * changed
167 */
4e1779ba 168 plist_del(node, &o->constraints->list);
5f279845 169 plist_node_init(node, value);
4e1779ba 170 plist_add(node, &o->constraints->list);
5f279845 171 } else if (del) {
4e1779ba 172 plist_del(node, &o->constraints->list);
5f279845 173 } else {
4e1779ba 174 plist_add(node, &o->constraints->list);
d82b3518 175 }
5f279845 176 curr_value = pm_qos_get_value(o);
333c5ae9 177 pm_qos_set_value(o, curr_value);
d82b3518
MG
178 spin_unlock_irqrestore(&pm_qos_lock, flags);
179
5f279845 180 if (prev_value != curr_value)
4e1779ba 181 blocking_notifier_call_chain(o->constraints->notifiers,
5f279845
JB
182 (unsigned long)curr_value,
183 NULL);
d82b3518
MG
184}
185
d82b3518 186/**
ed77134b 187 * pm_qos_request - returns current system wide qos expectation
d82b3518
MG
188 * @pm_qos_class: identification of which qos value is requested
189 *
333c5ae9 190 * This function returns the current target value.
d82b3518 191 */
ed77134b 192int pm_qos_request(int pm_qos_class)
d82b3518 193{
333c5ae9 194 return pm_qos_read_value(pm_qos_array[pm_qos_class]);
d82b3518 195}
ed77134b 196EXPORT_SYMBOL_GPL(pm_qos_request);
d82b3518 197
cc749986 198int pm_qos_request_active(struct pm_qos_request *req)
82f68251
JB
199{
200 return req->pm_qos_class != 0;
201}
202EXPORT_SYMBOL_GPL(pm_qos_request_active);
203
d82b3518 204/**
ed77134b 205 * pm_qos_add_request - inserts new qos request into the list
cc749986 206 * @req: pointer to a preallocated handle
25cc69ec 207 * @pm_qos_class: identifies which list of qos request to use
d82b3518
MG
208 * @value: defines the qos request
209 *
210 * This function inserts a new entry in the pm_qos_class list of requested qos
bf1db69f 211 * performance characteristics. It recomputes the aggregate QoS expectations
cc749986 212 * for the pm_qos_class of parameters and initializes the pm_qos_request
25cc69ec
SK
213 * handle. Caller needs to save this handle for later use in updates and
214 * removal.
d82b3518 215 */
25cc69ec 216
cc749986 217void pm_qos_add_request(struct pm_qos_request *req,
82f68251 218 int pm_qos_class, s32 value)
d82b3518 219{
82f68251
JB
220 struct pm_qos_object *o = pm_qos_array[pm_qos_class];
221 int new_value;
d82b3518 222
cc749986 223 if (pm_qos_request_active(req)) {
82f68251
JB
224 WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n");
225 return;
226 }
227 if (value == PM_QOS_DEFAULT_VALUE)
4e1779ba 228 new_value = o->constraints->default_value;
82f68251
JB
229 else
230 new_value = value;
cc749986
JP
231 plist_node_init(&req->node, new_value);
232 req->pm_qos_class = pm_qos_class;
233 update_target(o, &req->node, 0, PM_QOS_DEFAULT_VALUE);
d82b3518 234}
ed77134b 235EXPORT_SYMBOL_GPL(pm_qos_add_request);
d82b3518
MG
236
237/**
ed77134b 238 * pm_qos_update_request - modifies an existing qos request
cc749986 239 * @req : handle to list element holding a pm_qos request to use
d82b3518
MG
240 * @value: defines the qos request
241 *
ed77134b 242 * Updates an existing qos request for the pm_qos_class of parameters along
d82b3518
MG
243 * with updating the target pm_qos_class value.
244 *
ed77134b 245 * Attempts are made to make this code callable on hot code paths.
d82b3518 246 */
cc749986 247void pm_qos_update_request(struct pm_qos_request *req,
5f279845 248 s32 new_value)
d82b3518 249{
ed77134b 250 s32 temp;
5f279845 251 struct pm_qos_object *o;
d82b3518 252
cc749986 253 if (!req) /*guard against callers passing in null */
5f279845
JB
254 return;
255
cc749986 256 if (!pm_qos_request_active(req)) {
82f68251
JB
257 WARN(1, KERN_ERR "pm_qos_update_request() called for unknown object\n");
258 return;
259 }
260
cc749986 261 o = pm_qos_array[req->pm_qos_class];
5f279845
JB
262
263 if (new_value == PM_QOS_DEFAULT_VALUE)
4e1779ba 264 temp = o->constraints->default_value;
5f279845
JB
265 else
266 temp = new_value;
267
cc749986
JP
268 if (temp != req->node.prio)
269 update_target(o, &req->node, 0, temp);
d82b3518 270}
ed77134b 271EXPORT_SYMBOL_GPL(pm_qos_update_request);
d82b3518
MG
272
273/**
ed77134b 274 * pm_qos_remove_request - modifies an existing qos request
cc749986 275 * @req: handle to request list element
d82b3518 276 *
cc749986 277 * Will remove pm qos request from the list of constraints and
ed77134b
MG
278 * recompute the current target value for the pm_qos_class. Call this
279 * on slow code paths.
d82b3518 280 */
cc749986 281void pm_qos_remove_request(struct pm_qos_request *req)
d82b3518 282{
5f279845 283 struct pm_qos_object *o;
ed77134b 284
cc749986 285 if (req == NULL)
ed77134b
MG
286 return;
287 /* silent return to keep pcm code cleaner */
d82b3518 288
cc749986 289 if (!pm_qos_request_active(req)) {
82f68251
JB
290 WARN(1, KERN_ERR "pm_qos_remove_request() called for unknown object\n");
291 return;
292 }
293
cc749986
JP
294 o = pm_qos_array[req->pm_qos_class];
295 update_target(o, &req->node, 1, PM_QOS_DEFAULT_VALUE);
296 memset(req, 0, sizeof(*req));
d82b3518 297}
ed77134b 298EXPORT_SYMBOL_GPL(pm_qos_remove_request);
d82b3518
MG
299
300/**
301 * pm_qos_add_notifier - sets notification entry for changes to target value
302 * @pm_qos_class: identifies which qos target changes should be notified.
303 * @notifier: notifier block managed by caller.
304 *
305 * will register the notifier into a notification chain that gets called
bf1db69f 306 * upon changes to the pm_qos_class target value.
d82b3518 307 */
ed77134b 308int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier)
d82b3518
MG
309{
310 int retval;
311
312 retval = blocking_notifier_chain_register(
4e1779ba
JP
313 pm_qos_array[pm_qos_class]->constraints->notifiers,
314 notifier);
d82b3518
MG
315
316 return retval;
317}
318EXPORT_SYMBOL_GPL(pm_qos_add_notifier);
319
320/**
321 * pm_qos_remove_notifier - deletes notification entry from chain.
322 * @pm_qos_class: identifies which qos target changes are notified.
323 * @notifier: notifier block to be removed.
324 *
325 * will remove the notifier from the notification chain that gets called
bf1db69f 326 * upon changes to the pm_qos_class target value.
d82b3518
MG
327 */
328int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
329{
330 int retval;
331
332 retval = blocking_notifier_chain_unregister(
4e1779ba
JP
333 pm_qos_array[pm_qos_class]->constraints->notifiers,
334 notifier);
d82b3518
MG
335
336 return retval;
337}
338EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
339
4a31a334
JP
340/* User space interface to PM QoS classes via misc devices */
341static int register_pm_qos_misc(struct pm_qos_object *qos)
342{
343 qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
344 qos->pm_qos_power_miscdev.name = qos->name;
345 qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
346
347 return misc_register(&qos->pm_qos_power_miscdev);
348}
349
350static int find_pm_qos_object_by_minor(int minor)
351{
352 int pm_qos_class;
353
354 for (pm_qos_class = 0;
355 pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) {
356 if (minor ==
357 pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor)
358 return pm_qos_class;
359 }
360 return -1;
361}
362
d82b3518
MG
363static int pm_qos_power_open(struct inode *inode, struct file *filp)
364{
d82b3518
MG
365 long pm_qos_class;
366
367 pm_qos_class = find_pm_qos_object_by_minor(iminor(inode));
368 if (pm_qos_class >= 0) {
cc749986 369 struct pm_qos_request *req = kzalloc(sizeof(*req), GFP_KERNEL);
82f68251
JB
370 if (!req)
371 return -ENOMEM;
372
373 pm_qos_add_request(req, pm_qos_class, PM_QOS_DEFAULT_VALUE);
374 filp->private_data = req;
ed77134b
MG
375
376 if (filp->private_data)
d82b3518
MG
377 return 0;
378 }
d82b3518
MG
379 return -EPERM;
380}
381
382static int pm_qos_power_release(struct inode *inode, struct file *filp)
383{
cc749986 384 struct pm_qos_request *req;
d82b3518 385
82f68251 386 req = filp->private_data;
ed77134b 387 pm_qos_remove_request(req);
82f68251 388 kfree(req);
d82b3518
MG
389
390 return 0;
391}
392
ed77134b 393
f9b9e806
TR
394static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
395 size_t count, loff_t *f_pos)
396{
397 s32 value;
398 unsigned long flags;
399 struct pm_qos_object *o;
cc749986 400 struct pm_qos_request *req = filp->private_data;
f9b9e806 401
cc749986 402 if (!req)
f9b9e806 403 return -EINVAL;
cc749986 404 if (!pm_qos_request_active(req))
f9b9e806
TR
405 return -EINVAL;
406
cc749986 407 o = pm_qos_array[req->pm_qos_class];
f9b9e806
TR
408 spin_lock_irqsave(&pm_qos_lock, flags);
409 value = pm_qos_get_value(o);
410 spin_unlock_irqrestore(&pm_qos_lock, flags);
411
412 return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32));
413}
414
d82b3518
MG
415static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
416 size_t count, loff_t *f_pos)
417{
418 s32 value;
cc749986 419 struct pm_qos_request *req;
ed77134b
MG
420
421 if (count == sizeof(s32)) {
422 if (copy_from_user(&value, buf, sizeof(s32)))
423 return -EFAULT;
0775a60a
RW
424 } else if (count <= 11) { /* ASCII perhaps? */
425 char ascii_value[11];
426 unsigned long int ulval;
427 int ret;
428
429 if (copy_from_user(ascii_value, buf, count))
ed77134b 430 return -EFAULT;
0775a60a
RW
431
432 if (count > 10) {
433 if (ascii_value[10] == '\n')
434 ascii_value[10] = '\0';
435 else
436 return -EINVAL;
437 } else {
438 ascii_value[count] = '\0';
439 }
440 ret = strict_strtoul(ascii_value, 16, &ulval);
441 if (ret) {
442 pr_debug("%s, 0x%lx, 0x%x\n", ascii_value, ulval, ret);
ed77134b 443 return -EINVAL;
0775a60a
RW
444 }
445 value = (s32)lower_32_bits(ulval);
446 } else {
d82b3518 447 return -EINVAL;
0775a60a 448 }
d82b3518 449
cc749986
JP
450 req = filp->private_data;
451 pm_qos_update_request(req, value);
ed77134b
MG
452
453 return count;
d82b3518
MG
454}
455
456
457static int __init pm_qos_power_init(void)
458{
459 int ret = 0;
460
461 ret = register_pm_qos_misc(&cpu_dma_pm_qos);
462 if (ret < 0) {
463 printk(KERN_ERR "pm_qos_param: cpu_dma_latency setup failed\n");
464 return ret;
465 }
466 ret = register_pm_qos_misc(&network_lat_pm_qos);
467 if (ret < 0) {
468 printk(KERN_ERR "pm_qos_param: network_latency setup failed\n");
469 return ret;
470 }
471 ret = register_pm_qos_misc(&network_throughput_pm_qos);
472 if (ret < 0)
473 printk(KERN_ERR
474 "pm_qos_param: network_throughput setup failed\n");
475
476 return ret;
477}
478
479late_initcall(pm_qos_power_init);