diff options
Diffstat (limited to 'kernel/pm_qos_params.c')
-rw-r--r-- | kernel/pm_qos_params.c | 101 |
1 files changed, 76 insertions, 25 deletions
diff --git a/kernel/pm_qos_params.c b/kernel/pm_qos_params.c index 645e541a45f6..6824ca7d4d0c 100644 --- a/kernel/pm_qos_params.c +++ b/kernel/pm_qos_params.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <linux/string.h> | 40 | #include <linux/string.h> |
41 | #include <linux/platform_device.h> | 41 | #include <linux/platform_device.h> |
42 | #include <linux/init.h> | 42 | #include <linux/init.h> |
43 | #include <linux/kernel.h> | ||
43 | 44 | ||
44 | #include <linux/uaccess.h> | 45 | #include <linux/uaccess.h> |
45 | 46 | ||
@@ -53,11 +54,17 @@ enum pm_qos_type { | |||
53 | PM_QOS_MIN /* return the smallest value */ | 54 | PM_QOS_MIN /* return the smallest value */ |
54 | }; | 55 | }; |
55 | 56 | ||
57 | /* | ||
58 | * Note: The lockless read path depends on the CPU accessing | ||
59 | * target_value atomically. Atomic access is only guaranteed on all CPU | ||
60 | * types linux supports for 32 bit quantites | ||
61 | */ | ||
56 | struct pm_qos_object { | 62 | struct pm_qos_object { |
57 | struct plist_head requests; | 63 | struct plist_head requests; |
58 | struct blocking_notifier_head *notifiers; | 64 | struct blocking_notifier_head *notifiers; |
59 | struct miscdevice pm_qos_power_miscdev; | 65 | struct miscdevice pm_qos_power_miscdev; |
60 | char *name; | 66 | char *name; |
67 | s32 target_value; /* Do not change to 64 bit */ | ||
61 | s32 default_value; | 68 | s32 default_value; |
62 | enum pm_qos_type type; | 69 | enum pm_qos_type type; |
63 | }; | 70 | }; |
@@ -70,7 +77,8 @@ static struct pm_qos_object cpu_dma_pm_qos = { | |||
70 | .requests = PLIST_HEAD_INIT(cpu_dma_pm_qos.requests, pm_qos_lock), | 77 | .requests = PLIST_HEAD_INIT(cpu_dma_pm_qos.requests, pm_qos_lock), |
71 | .notifiers = &cpu_dma_lat_notifier, | 78 | .notifiers = &cpu_dma_lat_notifier, |
72 | .name = "cpu_dma_latency", | 79 | .name = "cpu_dma_latency", |
73 | .default_value = 2000 * USEC_PER_SEC, | 80 | .target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, |
81 | .default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, | ||
74 | .type = PM_QOS_MIN, | 82 | .type = PM_QOS_MIN, |
75 | }; | 83 | }; |
76 | 84 | ||
@@ -79,7 +87,8 @@ static struct pm_qos_object network_lat_pm_qos = { | |||
79 | .requests = PLIST_HEAD_INIT(network_lat_pm_qos.requests, pm_qos_lock), | 87 | .requests = PLIST_HEAD_INIT(network_lat_pm_qos.requests, pm_qos_lock), |
80 | .notifiers = &network_lat_notifier, | 88 | .notifiers = &network_lat_notifier, |
81 | .name = "network_latency", | 89 | .name = "network_latency", |
82 | .default_value = 2000 * USEC_PER_SEC, | 90 | .target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE, |
91 | .default_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE, | ||
83 | .type = PM_QOS_MIN | 92 | .type = PM_QOS_MIN |
84 | }; | 93 | }; |
85 | 94 | ||
@@ -89,7 +98,8 @@ static struct pm_qos_object network_throughput_pm_qos = { | |||
89 | .requests = PLIST_HEAD_INIT(network_throughput_pm_qos.requests, pm_qos_lock), | 98 | .requests = PLIST_HEAD_INIT(network_throughput_pm_qos.requests, pm_qos_lock), |
90 | .notifiers = &network_throughput_notifier, | 99 | .notifiers = &network_throughput_notifier, |
91 | .name = "network_throughput", | 100 | .name = "network_throughput", |
92 | .default_value = 0, | 101 | .target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE, |
102 | .default_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE, | ||
93 | .type = PM_QOS_MAX, | 103 | .type = PM_QOS_MAX, |
94 | }; | 104 | }; |
95 | 105 | ||
@@ -103,13 +113,17 @@ static struct pm_qos_object *pm_qos_array[] = { | |||
103 | 113 | ||
104 | static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, | 114 | static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, |
105 | size_t count, loff_t *f_pos); | 115 | size_t count, loff_t *f_pos); |
116 | static ssize_t pm_qos_power_read(struct file *filp, char __user *buf, | ||
117 | size_t count, loff_t *f_pos); | ||
106 | static int pm_qos_power_open(struct inode *inode, struct file *filp); | 118 | static int pm_qos_power_open(struct inode *inode, struct file *filp); |
107 | static int pm_qos_power_release(struct inode *inode, struct file *filp); | 119 | static int pm_qos_power_release(struct inode *inode, struct file *filp); |
108 | 120 | ||
109 | static const struct file_operations pm_qos_power_fops = { | 121 | static const struct file_operations pm_qos_power_fops = { |
110 | .write = pm_qos_power_write, | 122 | .write = pm_qos_power_write, |
123 | .read = pm_qos_power_read, | ||
111 | .open = pm_qos_power_open, | 124 | .open = pm_qos_power_open, |
112 | .release = pm_qos_power_release, | 125 | .release = pm_qos_power_release, |
126 | .llseek = noop_llseek, | ||
113 | }; | 127 | }; |
114 | 128 | ||
115 | /* unlocked internal variant */ | 129 | /* unlocked internal variant */ |
@@ -120,10 +134,10 @@ static inline int pm_qos_get_value(struct pm_qos_object *o) | |||
120 | 134 | ||
121 | switch (o->type) { | 135 | switch (o->type) { |
122 | case PM_QOS_MIN: | 136 | case PM_QOS_MIN: |
123 | return plist_last(&o->requests)->prio; | 137 | return plist_first(&o->requests)->prio; |
124 | 138 | ||
125 | case PM_QOS_MAX: | 139 | case PM_QOS_MAX: |
126 | return plist_first(&o->requests)->prio; | 140 | return plist_last(&o->requests)->prio; |
127 | 141 | ||
128 | default: | 142 | default: |
129 | /* runtime check for not using enum */ | 143 | /* runtime check for not using enum */ |
@@ -131,6 +145,16 @@ static inline int pm_qos_get_value(struct pm_qos_object *o) | |||
131 | } | 145 | } |
132 | } | 146 | } |
133 | 147 | ||
148 | static inline s32 pm_qos_read_value(struct pm_qos_object *o) | ||
149 | { | ||
150 | return o->target_value; | ||
151 | } | ||
152 | |||
153 | static inline void pm_qos_set_value(struct pm_qos_object *o, s32 value) | ||
154 | { | ||
155 | o->target_value = value; | ||
156 | } | ||
157 | |||
134 | static void update_target(struct pm_qos_object *o, struct plist_node *node, | 158 | static void update_target(struct pm_qos_object *o, struct plist_node *node, |
135 | int del, int value) | 159 | int del, int value) |
136 | { | 160 | { |
@@ -155,6 +179,7 @@ static void update_target(struct pm_qos_object *o, struct plist_node *node, | |||
155 | plist_add(node, &o->requests); | 179 | plist_add(node, &o->requests); |
156 | } | 180 | } |
157 | curr_value = pm_qos_get_value(o); | 181 | curr_value = pm_qos_get_value(o); |
182 | pm_qos_set_value(o, curr_value); | ||
158 | spin_unlock_irqrestore(&pm_qos_lock, flags); | 183 | spin_unlock_irqrestore(&pm_qos_lock, flags); |
159 | 184 | ||
160 | if (prev_value != curr_value) | 185 | if (prev_value != curr_value) |
@@ -189,18 +214,11 @@ static int find_pm_qos_object_by_minor(int minor) | |||
189 | * pm_qos_request - returns current system wide qos expectation | 214 | * pm_qos_request - returns current system wide qos expectation |
190 | * @pm_qos_class: identification of which qos value is requested | 215 | * @pm_qos_class: identification of which qos value is requested |
191 | * | 216 | * |
192 | * This function returns the current target value in an atomic manner. | 217 | * This function returns the current target value. |
193 | */ | 218 | */ |
194 | int pm_qos_request(int pm_qos_class) | 219 | int pm_qos_request(int pm_qos_class) |
195 | { | 220 | { |
196 | unsigned long flags; | 221 | return pm_qos_read_value(pm_qos_array[pm_qos_class]); |
197 | int value; | ||
198 | |||
199 | spin_lock_irqsave(&pm_qos_lock, flags); | ||
200 | value = pm_qos_get_value(pm_qos_array[pm_qos_class]); | ||
201 | spin_unlock_irqrestore(&pm_qos_lock, flags); | ||
202 | |||
203 | return value; | ||
204 | } | 222 | } |
205 | EXPORT_SYMBOL_GPL(pm_qos_request); | 223 | EXPORT_SYMBOL_GPL(pm_qos_request); |
206 | 224 | ||
@@ -375,30 +393,63 @@ static int pm_qos_power_release(struct inode *inode, struct file *filp) | |||
375 | } | 393 | } |
376 | 394 | ||
377 | 395 | ||
396 | static ssize_t pm_qos_power_read(struct file *filp, char __user *buf, | ||
397 | size_t count, loff_t *f_pos) | ||
398 | { | ||
399 | s32 value; | ||
400 | unsigned long flags; | ||
401 | struct pm_qos_object *o; | ||
402 | struct pm_qos_request_list *pm_qos_req = filp->private_data; | ||
403 | |||
404 | if (!pm_qos_req) | ||
405 | return -EINVAL; | ||
406 | if (!pm_qos_request_active(pm_qos_req)) | ||
407 | return -EINVAL; | ||
408 | |||
409 | o = pm_qos_array[pm_qos_req->pm_qos_class]; | ||
410 | spin_lock_irqsave(&pm_qos_lock, flags); | ||
411 | value = pm_qos_get_value(o); | ||
412 | spin_unlock_irqrestore(&pm_qos_lock, flags); | ||
413 | |||
414 | return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32)); | ||
415 | } | ||
416 | |||
378 | static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, | 417 | static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, |
379 | size_t count, loff_t *f_pos) | 418 | size_t count, loff_t *f_pos) |
380 | { | 419 | { |
381 | s32 value; | 420 | s32 value; |
382 | int x; | ||
383 | char ascii_value[11]; | ||
384 | struct pm_qos_request_list *pm_qos_req; | 421 | struct pm_qos_request_list *pm_qos_req; |
385 | 422 | ||
386 | if (count == sizeof(s32)) { | 423 | if (count == sizeof(s32)) { |
387 | if (copy_from_user(&value, buf, sizeof(s32))) | 424 | if (copy_from_user(&value, buf, sizeof(s32))) |
388 | return -EFAULT; | 425 | return -EFAULT; |
389 | } else if (count == 11) { /* len('0x12345678/0') */ | 426 | } else if (count <= 11) { /* ASCII perhaps? */ |
390 | if (copy_from_user(ascii_value, buf, 11)) | 427 | char ascii_value[11]; |
428 | unsigned long int ulval; | ||
429 | int ret; | ||
430 | |||
431 | if (copy_from_user(ascii_value, buf, count)) | ||
391 | return -EFAULT; | 432 | return -EFAULT; |
392 | if (strlen(ascii_value) != 10) | 433 | |
393 | return -EINVAL; | 434 | if (count > 10) { |
394 | x = sscanf(ascii_value, "%x", &value); | 435 | if (ascii_value[10] == '\n') |
395 | if (x != 1) | 436 | ascii_value[10] = '\0'; |
437 | else | ||
438 | return -EINVAL; | ||
439 | } else { | ||
440 | ascii_value[count] = '\0'; | ||
441 | } | ||
442 | ret = strict_strtoul(ascii_value, 16, &ulval); | ||
443 | if (ret) { | ||
444 | pr_debug("%s, 0x%lx, 0x%x\n", ascii_value, ulval, ret); | ||
396 | return -EINVAL; | 445 | return -EINVAL; |
397 | pr_debug("%s, %d, 0x%x\n", ascii_value, x, value); | 446 | } |
398 | } else | 447 | value = (s32)lower_32_bits(ulval); |
448 | } else { | ||
399 | return -EINVAL; | 449 | return -EINVAL; |
450 | } | ||
400 | 451 | ||
401 | pm_qos_req = (struct pm_qos_request_list *)filp->private_data; | 452 | pm_qos_req = filp->private_data; |
402 | pm_qos_update_request(pm_qos_req, value); | 453 | pm_qos_update_request(pm_qos_req, value); |
403 | 454 | ||
404 | return count; | 455 | return count; |