diff options
-rw-r--r-- | Documentation/pm_qos_interface.txt | 59 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle.c | 7 | ||||
-rw-r--r-- | drivers/cpuidle/governors/ladder.c | 5 | ||||
-rw-r--r-- | drivers/cpuidle/governors/menu.c | 4 | ||||
-rw-r--r-- | include/linux/pm_qos_params.h | 25 | ||||
-rw-r--r-- | kernel/Makefile | 2 | ||||
-rw-r--r-- | kernel/pm_qos_params.c | 425 |
7 files changed, 520 insertions, 7 deletions
diff --git a/Documentation/pm_qos_interface.txt b/Documentation/pm_qos_interface.txt new file mode 100644 index 000000000000..49adb1a33514 --- /dev/null +++ b/Documentation/pm_qos_interface.txt | |||
@@ -0,0 +1,59 @@ | |||
1 | PM quality of Service interface. | ||
2 | |||
3 | This interface provides a kernel and user mode interface for registering | ||
4 | performance expectations by drivers, subsystems and user space applications on | ||
5 | one of the parameters. | ||
6 | |||
7 | Currently we have {cpu_dma_latency, network_latency, network_throughput} as the | ||
8 | initial set of pm_qos parameters. | ||
9 | |||
10 | The infrastructure exposes multiple misc device nodes one per implemented | ||
11 | parameter. The set of parameters implement is defined by pm_qos_power_init() | ||
12 | and pm_qos_params.h. This is done because having the available parameters | ||
13 | being runtime configurable or changeable from a driver was seen as too easy to | ||
14 | abuse. | ||
15 | |||
16 | For each parameter a list of performance requirements is maintained along with | ||
17 | an aggregated target value. The aggregated target value is updated with | ||
18 | changes to the requirement list or elements of the list. Typically the | ||
19 | aggregated target value is simply the max or min of the requirement values held | ||
20 | in the parameter list elements. | ||
21 | |||
22 | From kernel mode the use of this interface is simple: | ||
23 | pm_qos_add_requirement(param_id, name, target_value): | ||
24 | Will insert a named element in the list for that identified PM_QOS parameter | ||
25 | with the target value. Upon change to this list the new target is recomputed | ||
26 | and any registered notifiers are called only if the target value is now | ||
27 | different. | ||
28 | |||
29 | pm_qos_update_requirement(param_id, name, new_target_value): | ||
30 | Will search the list identified by the param_id for the named list element and | ||
31 | then update its target value, calling the notification tree if the aggregated | ||
32 | target is changed. with that name is already registered. | ||
33 | |||
34 | pm_qos_remove_requirement(param_id, name): | ||
35 | Will search the identified list for the named element and remove it, after | ||
36 | removal it will update the aggregate target and call the notification tree if | ||
37 | the target was changed as a result of removing the named requirement. | ||
38 | |||
39 | |||
40 | From user mode: | ||
41 | Only processes can register a pm_qos requirement. To provide for automatic | ||
42 | cleanup for process the interface requires the process to register its | ||
43 | parameter requirements in the following way: | ||
44 | |||
45 | To register the default pm_qos target for the specific parameter, the process | ||
46 | must open one of /dev/[cpu_dma_latency, network_latency, network_throughput] | ||
47 | |||
48 | As long as the device node is held open that process has a registered | ||
49 | requirement on the parameter. The name of the requirement is "process_<PID>" | ||
50 | derived from the current->pid from within the open system call. | ||
51 | |||
52 | To change the requested target value the process needs to write a s32 value to | ||
53 | the open device node. This translates to a pm_qos_update_requirement call. | ||
54 | |||
55 | To remove the user mode request for a target value simply close the device | ||
56 | node. | ||
57 | |||
58 | |||
59 | |||
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index d2fabe7863a9..2a98d99cbd46 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c | |||
@@ -12,7 +12,7 @@ | |||
12 | #include <linux/mutex.h> | 12 | #include <linux/mutex.h> |
13 | #include <linux/sched.h> | 13 | #include <linux/sched.h> |
14 | #include <linux/notifier.h> | 14 | #include <linux/notifier.h> |
15 | #include <linux/latency.h> | 15 | #include <linux/pm_qos_params.h> |
16 | #include <linux/cpu.h> | 16 | #include <linux/cpu.h> |
17 | #include <linux/cpuidle.h> | 17 | #include <linux/cpuidle.h> |
18 | 18 | ||
@@ -265,7 +265,10 @@ static struct notifier_block cpuidle_latency_notifier = { | |||
265 | .notifier_call = cpuidle_latency_notify, | 265 | .notifier_call = cpuidle_latency_notify, |
266 | }; | 266 | }; |
267 | 267 | ||
268 | #define latency_notifier_init(x) do { register_latency_notifier(x); } while (0) | 268 | static inline void latency_notifier_init(struct notifier_block *n) |
269 | { | ||
270 | pm_qos_add_notifier(PM_QOS_CPU_DMA_LATENCY, n); | ||
271 | } | ||
269 | 272 | ||
270 | #else /* CONFIG_SMP */ | 273 | #else /* CONFIG_SMP */ |
271 | 274 | ||
diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c index eb666ecae7c9..ba7b9a6b17a1 100644 --- a/drivers/cpuidle/governors/ladder.c +++ b/drivers/cpuidle/governors/ladder.c | |||
@@ -14,7 +14,7 @@ | |||
14 | 14 | ||
15 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
16 | #include <linux/cpuidle.h> | 16 | #include <linux/cpuidle.h> |
17 | #include <linux/latency.h> | 17 | #include <linux/pm_qos_params.h> |
18 | #include <linux/moduleparam.h> | 18 | #include <linux/moduleparam.h> |
19 | #include <linux/jiffies.h> | 19 | #include <linux/jiffies.h> |
20 | 20 | ||
@@ -81,7 +81,8 @@ static int ladder_select_state(struct cpuidle_device *dev) | |||
81 | /* consider promotion */ | 81 | /* consider promotion */ |
82 | if (last_idx < dev->state_count - 1 && | 82 | if (last_idx < dev->state_count - 1 && |
83 | last_residency > last_state->threshold.promotion_time && | 83 | last_residency > last_state->threshold.promotion_time && |
84 | dev->states[last_idx + 1].exit_latency <= system_latency_constraint()) { | 84 | dev->states[last_idx + 1].exit_latency <= |
85 | pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY)) { | ||
85 | last_state->stats.promotion_count++; | 86 | last_state->stats.promotion_count++; |
86 | last_state->stats.demotion_count = 0; | 87 | last_state->stats.demotion_count = 0; |
87 | if (last_state->stats.promotion_count >= last_state->threshold.promotion_count) { | 88 | if (last_state->stats.promotion_count >= last_state->threshold.promotion_count) { |
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 299d45c3bdd2..78d77c5dc35c 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c | |||
@@ -8,7 +8,7 @@ | |||
8 | 8 | ||
9 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
10 | #include <linux/cpuidle.h> | 10 | #include <linux/cpuidle.h> |
11 | #include <linux/latency.h> | 11 | #include <linux/pm_qos_params.h> |
12 | #include <linux/time.h> | 12 | #include <linux/time.h> |
13 | #include <linux/ktime.h> | 13 | #include <linux/ktime.h> |
14 | #include <linux/hrtimer.h> | 14 | #include <linux/hrtimer.h> |
@@ -48,7 +48,7 @@ static int menu_select(struct cpuidle_device *dev) | |||
48 | break; | 48 | break; |
49 | if (s->target_residency > data->predicted_us) | 49 | if (s->target_residency > data->predicted_us) |
50 | break; | 50 | break; |
51 | if (s->exit_latency > system_latency_constraint()) | 51 | if (s->exit_latency > pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY)) |
52 | break; | 52 | break; |
53 | } | 53 | } |
54 | 54 | ||
diff --git a/include/linux/pm_qos_params.h b/include/linux/pm_qos_params.h new file mode 100644 index 000000000000..2e4e97bd19f7 --- /dev/null +++ b/include/linux/pm_qos_params.h | |||
@@ -0,0 +1,25 @@ | |||
1 | /* interface for the pm_qos_power infrastructure of the linux kernel. | ||
2 | * | ||
3 | * Mark Gross | ||
4 | */ | ||
5 | #include <linux/list.h> | ||
6 | #include <linux/notifier.h> | ||
7 | #include <linux/miscdevice.h> | ||
8 | |||
9 | #define PM_QOS_RESERVED 0 | ||
10 | #define PM_QOS_CPU_DMA_LATENCY 1 | ||
11 | #define PM_QOS_NETWORK_LATENCY 2 | ||
12 | #define PM_QOS_NETWORK_THROUGHPUT 3 | ||
13 | |||
14 | #define PM_QOS_NUM_CLASSES 4 | ||
15 | #define PM_QOS_DEFAULT_VALUE -1 | ||
16 | |||
17 | int pm_qos_add_requirement(int qos, char *name, s32 value); | ||
18 | int pm_qos_update_requirement(int qos, char *name, s32 new_value); | ||
19 | void pm_qos_remove_requirement(int qos, char *name); | ||
20 | |||
21 | int pm_qos_requirement(int qos); | ||
22 | |||
23 | int pm_qos_add_notifier(int qos, struct notifier_block *notifier); | ||
24 | int pm_qos_remove_notifier(int qos, struct notifier_block *notifier); | ||
25 | |||
diff --git a/kernel/Makefile b/kernel/Makefile index db9af707ff5b..8331243a4e5e 100644 --- a/kernel/Makefile +++ b/kernel/Makefile | |||
@@ -9,7 +9,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \ | |||
9 | rcupdate.o extable.o params.o posix-timers.o \ | 9 | rcupdate.o extable.o params.o posix-timers.o \ |
10 | kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ | 10 | kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ |
11 | hrtimer.o rwsem.o latency.o nsproxy.o srcu.o \ | 11 | hrtimer.o rwsem.o latency.o nsproxy.o srcu.o \ |
12 | utsname.o notifier.o ksysfs.o | 12 | utsname.o notifier.o ksysfs.o pm_qos_params.o |
13 | 13 | ||
14 | obj-$(CONFIG_SYSCTL) += sysctl_check.o | 14 | obj-$(CONFIG_SYSCTL) += sysctl_check.o |
15 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | 15 | obj-$(CONFIG_STACKTRACE) += stacktrace.o |
diff --git a/kernel/pm_qos_params.c b/kernel/pm_qos_params.c new file mode 100644 index 000000000000..0afe32be4c85 --- /dev/null +++ b/kernel/pm_qos_params.c | |||
@@ -0,0 +1,425 @@ | |||
1 | /* | ||
2 | * This module exposes the interface to kernel space for specifying | ||
3 | * QoS dependencies. It provides infrastructure for registration of: | ||
4 | * | ||
5 | * Dependents on a QoS value : register requirements | ||
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 | * | ||
17 | * There are lists of pm_qos_objects each one wrapping requirements, notifiers | ||
18 | * | ||
19 | * User mode requirements on a QOS parameter register themselves to the | ||
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 | ||
23 | * requirement 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 | ||
25 | * pointer or exits the pm_qos_object will get an opportunity to clean up. | ||
26 | * | ||
27 | * mark gross mgross@linux.intel.com | ||
28 | */ | ||
29 | |||
30 | #include <linux/pm_qos_params.h> | ||
31 | #include <linux/sched.h> | ||
32 | #include <linux/spinlock.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/time.h> | ||
35 | #include <linux/fs.h> | ||
36 | #include <linux/device.h> | ||
37 | #include <linux/miscdevice.h> | ||
38 | #include <linux/string.h> | ||
39 | #include <linux/platform_device.h> | ||
40 | #include <linux/init.h> | ||
41 | |||
42 | #include <linux/uaccess.h> | ||
43 | |||
44 | /* | ||
45 | * locking rule: all changes to target_value or requirements or notifiers lists | ||
46 | * 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 | ||
48 | */ | ||
49 | struct requirement_list { | ||
50 | struct list_head list; | ||
51 | union { | ||
52 | s32 value; | ||
53 | s32 usec; | ||
54 | s32 kbps; | ||
55 | }; | ||
56 | char *name; | ||
57 | }; | ||
58 | |||
59 | static s32 max_compare(s32 v1, s32 v2); | ||
60 | static s32 min_compare(s32 v1, s32 v2); | ||
61 | |||
62 | struct pm_qos_object { | ||
63 | struct requirement_list requirements; | ||
64 | struct blocking_notifier_head *notifiers; | ||
65 | struct miscdevice pm_qos_power_miscdev; | ||
66 | char *name; | ||
67 | s32 default_value; | ||
68 | s32 target_value; | ||
69 | s32 (*comparitor)(s32, s32); | ||
70 | }; | ||
71 | |||
72 | static struct pm_qos_object null_pm_qos; | ||
73 | static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier); | ||
74 | static struct pm_qos_object cpu_dma_pm_qos = { | ||
75 | .requirements = {LIST_HEAD_INIT(cpu_dma_pm_qos.requirements.list)}, | ||
76 | .notifiers = &cpu_dma_lat_notifier, | ||
77 | .name = "cpu_dma_latency", | ||
78 | .default_value = 2000 * USEC_PER_SEC, | ||
79 | .target_value = 2000 * USEC_PER_SEC, | ||
80 | .comparitor = min_compare | ||
81 | }; | ||
82 | |||
83 | static BLOCKING_NOTIFIER_HEAD(network_lat_notifier); | ||
84 | static struct pm_qos_object network_lat_pm_qos = { | ||
85 | .requirements = {LIST_HEAD_INIT(network_lat_pm_qos.requirements.list)}, | ||
86 | .notifiers = &network_lat_notifier, | ||
87 | .name = "network_latency", | ||
88 | .default_value = 2000 * USEC_PER_SEC, | ||
89 | .target_value = 2000 * USEC_PER_SEC, | ||
90 | .comparitor = min_compare | ||
91 | }; | ||
92 | |||
93 | |||
94 | static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier); | ||
95 | static struct pm_qos_object network_throughput_pm_qos = { | ||
96 | .requirements = | ||
97 | {LIST_HEAD_INIT(network_throughput_pm_qos.requirements.list)}, | ||
98 | .notifiers = &network_throughput_notifier, | ||
99 | .name = "network_throughput", | ||
100 | .default_value = 0, | ||
101 | .target_value = 0, | ||
102 | .comparitor = max_compare | ||
103 | }; | ||
104 | |||
105 | |||
106 | static struct pm_qos_object *pm_qos_array[] = { | ||
107 | &null_pm_qos, | ||
108 | &cpu_dma_pm_qos, | ||
109 | &network_lat_pm_qos, | ||
110 | &network_throughput_pm_qos | ||
111 | }; | ||
112 | |||
113 | static DEFINE_SPINLOCK(pm_qos_lock); | ||
114 | |||
115 | static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, | ||
116 | size_t count, loff_t *f_pos); | ||
117 | static int pm_qos_power_open(struct inode *inode, struct file *filp); | ||
118 | static int pm_qos_power_release(struct inode *inode, struct file *filp); | ||
119 | |||
120 | static const struct file_operations pm_qos_power_fops = { | ||
121 | .write = pm_qos_power_write, | ||
122 | .open = pm_qos_power_open, | ||
123 | .release = pm_qos_power_release, | ||
124 | }; | ||
125 | |||
126 | /* static helper functions */ | ||
127 | static s32 max_compare(s32 v1, s32 v2) | ||
128 | { | ||
129 | return max(v1, v2); | ||
130 | } | ||
131 | |||
132 | static s32 min_compare(s32 v1, s32 v2) | ||
133 | { | ||
134 | return min(v1, v2); | ||
135 | } | ||
136 | |||
137 | |||
138 | static void update_target(int target) | ||
139 | { | ||
140 | s32 extreme_value; | ||
141 | struct requirement_list *node; | ||
142 | unsigned long flags; | ||
143 | int call_notifier = 0; | ||
144 | |||
145 | spin_lock_irqsave(&pm_qos_lock, flags); | ||
146 | extreme_value = pm_qos_array[target]->default_value; | ||
147 | list_for_each_entry(node, | ||
148 | &pm_qos_array[target]->requirements.list, list) { | ||
149 | extreme_value = pm_qos_array[target]->comparitor( | ||
150 | extreme_value, node->value); | ||
151 | } | ||
152 | if (pm_qos_array[target]->target_value != extreme_value) { | ||
153 | call_notifier = 1; | ||
154 | pm_qos_array[target]->target_value = extreme_value; | ||
155 | pr_debug(KERN_ERR "new target for qos %d is %d\n", target, | ||
156 | pm_qos_array[target]->target_value); | ||
157 | } | ||
158 | spin_unlock_irqrestore(&pm_qos_lock, flags); | ||
159 | |||
160 | if (call_notifier) | ||
161 | blocking_notifier_call_chain(pm_qos_array[target]->notifiers, | ||
162 | (unsigned long) extreme_value, NULL); | ||
163 | } | ||
164 | |||
165 | static int register_pm_qos_misc(struct pm_qos_object *qos) | ||
166 | { | ||
167 | qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR; | ||
168 | qos->pm_qos_power_miscdev.name = qos->name; | ||
169 | qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops; | ||
170 | |||
171 | return misc_register(&qos->pm_qos_power_miscdev); | ||
172 | } | ||
173 | |||
174 | static int find_pm_qos_object_by_minor(int minor) | ||
175 | { | ||
176 | int pm_qos_class; | ||
177 | |||
178 | for (pm_qos_class = 0; | ||
179 | pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) { | ||
180 | if (minor == | ||
181 | pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor) | ||
182 | return pm_qos_class; | ||
183 | } | ||
184 | return -1; | ||
185 | } | ||
186 | |||
187 | /** | ||
188 | * pm_qos_requirement - returns current system wide qos expectation | ||
189 | * @pm_qos_class: identification of which qos value is requested | ||
190 | * | ||
191 | * This function returns the current target value in an atomic manner. | ||
192 | */ | ||
193 | int pm_qos_requirement(int pm_qos_class) | ||
194 | { | ||
195 | int ret_val; | ||
196 | unsigned long flags; | ||
197 | |||
198 | spin_lock_irqsave(&pm_qos_lock, flags); | ||
199 | ret_val = pm_qos_array[pm_qos_class]->target_value; | ||
200 | spin_unlock_irqrestore(&pm_qos_lock, flags); | ||
201 | |||
202 | return ret_val; | ||
203 | } | ||
204 | EXPORT_SYMBOL_GPL(pm_qos_requirement); | ||
205 | |||
206 | /** | ||
207 | * pm_qos_add_requirement - inserts new qos request into the list | ||
208 | * @pm_qos_class: identifies which list of qos request to us | ||
209 | * @name: identifies the request | ||
210 | * @value: defines the qos request | ||
211 | * | ||
212 | * This function inserts a new entry in the pm_qos_class list of requested qos | ||
213 | * performance charactoistics. It recomputes the agregate QoS expectations for | ||
214 | * the pm_qos_class of parrameters. | ||
215 | */ | ||
216 | int pm_qos_add_requirement(int pm_qos_class, char *name, s32 value) | ||
217 | { | ||
218 | struct requirement_list *dep; | ||
219 | unsigned long flags; | ||
220 | |||
221 | dep = kzalloc(sizeof(struct requirement_list), GFP_KERNEL); | ||
222 | if (dep) { | ||
223 | if (value == PM_QOS_DEFAULT_VALUE) | ||
224 | dep->value = pm_qos_array[pm_qos_class]->default_value; | ||
225 | else | ||
226 | dep->value = value; | ||
227 | dep->name = kstrdup(name, GFP_KERNEL); | ||
228 | if (!dep->name) | ||
229 | goto cleanup; | ||
230 | |||
231 | spin_lock_irqsave(&pm_qos_lock, flags); | ||
232 | list_add(&dep->list, | ||
233 | &pm_qos_array[pm_qos_class]->requirements.list); | ||
234 | spin_unlock_irqrestore(&pm_qos_lock, flags); | ||
235 | update_target(pm_qos_class); | ||
236 | |||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | cleanup: | ||
241 | kfree(dep); | ||
242 | return -ENOMEM; | ||
243 | } | ||
244 | EXPORT_SYMBOL_GPL(pm_qos_add_requirement); | ||
245 | |||
246 | /** | ||
247 | * pm_qos_update_requirement - modifies an existing qos request | ||
248 | * @pm_qos_class: identifies which list of qos request to us | ||
249 | * @name: identifies the request | ||
250 | * @value: defines the qos request | ||
251 | * | ||
252 | * Updates an existing qos requierement for the pm_qos_class of parameters along | ||
253 | * with updating the target pm_qos_class value. | ||
254 | * | ||
255 | * If the named request isn't in the lest then no change is made. | ||
256 | */ | ||
257 | int pm_qos_update_requirement(int pm_qos_class, char *name, s32 new_value) | ||
258 | { | ||
259 | unsigned long flags; | ||
260 | struct requirement_list *node; | ||
261 | int pending_update = 0; | ||
262 | |||
263 | spin_lock_irqsave(&pm_qos_lock, flags); | ||
264 | list_for_each_entry(node, | ||
265 | &pm_qos_array[pm_qos_class]->requirements.list, list) { | ||
266 | if (strcmp(node->name, name) == 0) { | ||
267 | if (new_value == PM_QOS_DEFAULT_VALUE) | ||
268 | node->value = | ||
269 | pm_qos_array[pm_qos_class]->default_value; | ||
270 | else | ||
271 | node->value = new_value; | ||
272 | pending_update = 1; | ||
273 | break; | ||
274 | } | ||
275 | } | ||
276 | spin_unlock_irqrestore(&pm_qos_lock, flags); | ||
277 | if (pending_update) | ||
278 | update_target(pm_qos_class); | ||
279 | |||
280 | return 0; | ||
281 | } | ||
282 | EXPORT_SYMBOL_GPL(pm_qos_update_requirement); | ||
283 | |||
284 | /** | ||
285 | * pm_qos_remove_requirement - modifies an existing qos request | ||
286 | * @pm_qos_class: identifies which list of qos request to us | ||
287 | * @name: identifies the request | ||
288 | * | ||
289 | * Will remove named qos request from pm_qos_class list of parrameters and | ||
290 | * recompute the current target value for the pm_qos_class. | ||
291 | */ | ||
292 | void pm_qos_remove_requirement(int pm_qos_class, char *name) | ||
293 | { | ||
294 | unsigned long flags; | ||
295 | struct requirement_list *node; | ||
296 | int pending_update = 0; | ||
297 | |||
298 | spin_lock_irqsave(&pm_qos_lock, flags); | ||
299 | list_for_each_entry(node, | ||
300 | &pm_qos_array[pm_qos_class]->requirements.list, list) { | ||
301 | if (strcmp(node->name, name) == 0) { | ||
302 | kfree(node->name); | ||
303 | list_del(&node->list); | ||
304 | kfree(node); | ||
305 | pending_update = 1; | ||
306 | break; | ||
307 | } | ||
308 | } | ||
309 | spin_unlock_irqrestore(&pm_qos_lock, flags); | ||
310 | if (pending_update) | ||
311 | update_target(pm_qos_class); | ||
312 | } | ||
313 | EXPORT_SYMBOL_GPL(pm_qos_remove_requirement); | ||
314 | |||
315 | /** | ||
316 | * pm_qos_add_notifier - sets notification entry for changes to target value | ||
317 | * @pm_qos_class: identifies which qos target changes should be notified. | ||
318 | * @notifier: notifier block managed by caller. | ||
319 | * | ||
320 | * will register the notifier into a notification chain that gets called | ||
321 | * uppon changes to the pm_qos_class target value. | ||
322 | */ | ||
323 | int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier) | ||
324 | { | ||
325 | int retval; | ||
326 | |||
327 | retval = blocking_notifier_chain_register( | ||
328 | pm_qos_array[pm_qos_class]->notifiers, notifier); | ||
329 | |||
330 | return retval; | ||
331 | } | ||
332 | EXPORT_SYMBOL_GPL(pm_qos_add_notifier); | ||
333 | |||
334 | /** | ||
335 | * pm_qos_remove_notifier - deletes notification entry from chain. | ||
336 | * @pm_qos_class: identifies which qos target changes are notified. | ||
337 | * @notifier: notifier block to be removed. | ||
338 | * | ||
339 | * will remove the notifier from the notification chain that gets called | ||
340 | * uppon changes to the pm_qos_class target value. | ||
341 | */ | ||
342 | int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier) | ||
343 | { | ||
344 | int retval; | ||
345 | |||
346 | retval = blocking_notifier_chain_unregister( | ||
347 | pm_qos_array[pm_qos_class]->notifiers, notifier); | ||
348 | |||
349 | return retval; | ||
350 | } | ||
351 | EXPORT_SYMBOL_GPL(pm_qos_remove_notifier); | ||
352 | |||
353 | #define PID_NAME_LEN sizeof("process_1234567890") | ||
354 | static char name[PID_NAME_LEN]; | ||
355 | |||
356 | static int pm_qos_power_open(struct inode *inode, struct file *filp) | ||
357 | { | ||
358 | int ret; | ||
359 | long pm_qos_class; | ||
360 | |||
361 | pm_qos_class = find_pm_qos_object_by_minor(iminor(inode)); | ||
362 | if (pm_qos_class >= 0) { | ||
363 | filp->private_data = (void *)pm_qos_class; | ||
364 | sprintf(name, "process_%d", current->pid); | ||
365 | ret = pm_qos_add_requirement(pm_qos_class, name, | ||
366 | PM_QOS_DEFAULT_VALUE); | ||
367 | if (ret >= 0) | ||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | return -EPERM; | ||
372 | } | ||
373 | |||
374 | static int pm_qos_power_release(struct inode *inode, struct file *filp) | ||
375 | { | ||
376 | int pm_qos_class; | ||
377 | |||
378 | pm_qos_class = (long)filp->private_data; | ||
379 | sprintf(name, "process_%d", current->pid); | ||
380 | pm_qos_remove_requirement(pm_qos_class, name); | ||
381 | |||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, | ||
386 | size_t count, loff_t *f_pos) | ||
387 | { | ||
388 | s32 value; | ||
389 | int pm_qos_class; | ||
390 | |||
391 | pm_qos_class = (long)filp->private_data; | ||
392 | if (count != sizeof(s32)) | ||
393 | return -EINVAL; | ||
394 | if (copy_from_user(&value, buf, sizeof(s32))) | ||
395 | return -EFAULT; | ||
396 | sprintf(name, "process_%d", current->pid); | ||
397 | pm_qos_update_requirement(pm_qos_class, name, value); | ||
398 | |||
399 | return sizeof(s32); | ||
400 | } | ||
401 | |||
402 | |||
403 | static int __init pm_qos_power_init(void) | ||
404 | { | ||
405 | int ret = 0; | ||
406 | |||
407 | ret = register_pm_qos_misc(&cpu_dma_pm_qos); | ||
408 | if (ret < 0) { | ||
409 | printk(KERN_ERR "pm_qos_param: cpu_dma_latency setup failed\n"); | ||
410 | return ret; | ||
411 | } | ||
412 | ret = register_pm_qos_misc(&network_lat_pm_qos); | ||
413 | if (ret < 0) { | ||
414 | printk(KERN_ERR "pm_qos_param: network_latency setup failed\n"); | ||
415 | return ret; | ||
416 | } | ||
417 | ret = register_pm_qos_misc(&network_throughput_pm_qos); | ||
418 | if (ret < 0) | ||
419 | printk(KERN_ERR | ||
420 | "pm_qos_param: network_throughput setup failed\n"); | ||
421 | |||
422 | return ret; | ||
423 | } | ||
424 | |||
425 | late_initcall(pm_qos_power_init); | ||