Backport pm-qos for kernels <= 2.6.24
[~emulex/for-vlad/old/compat.git] / compat / compat-2.6.25.c
1 /*
2  * Copyright 2007-2010  Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * Compatibility file for Linux wireless for kernels 2.6.25.
9  */
10
11 #include <net/compat.h>
12
13 /* All things not in 2.6.22, 2.6.23 and 2.6.24 */
14 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25))
15
16 #include <linux/miscdevice.h>
17
18 /*
19  * Backport work for QoS dependencies (kernel/pm_qos_params.c)
20  * pm-qos stuff written by mark gross mgross@linux.intel.com.
21  *
22  * ipw2100 now makes use of
23  * pm_qos_add_requirement(),
24  * pm_qos_update_requirement() and
25  * pm_qos_remove_requirement() from it
26  *
27  * mac80211 uses the network latency to determine if to enable or not
28  * dynamic PS. mac80211 also and registers a notifier for when
29  * the latency changes. Since older kernels do no thave pm-qos stuff
30  * we just implement it completley here and register it upon cfg80211
31  * init. I haven't tested ipw2100 on 2.6.24 though.
32  *
33  * This is copied from the kernel written by mark gross mgross@linux.intel.com
34  */
35
36 /*
37  * locking rule: all changes to target_value or requirements or notifiers lists
38  * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock
39  * held, taken with _irqsave.  One lock to rule them all
40  */
41 struct requirement_list {
42         struct list_head list;
43         union {
44                 s32 value;
45                 s32 usec;
46                 s32 kbps;
47         };
48         char *name;
49 };
50
51 static s32 max_compare(s32 v1, s32 v2);
52 static s32 min_compare(s32 v1, s32 v2);
53
54 struct pm_qos_object {
55         struct requirement_list requirements;
56         struct blocking_notifier_head *notifiers;
57         struct miscdevice pm_qos_power_miscdev;
58         char *name;
59         s32 default_value;
60         s32 target_value;
61         s32 (*comparitor)(s32, s32);
62 };
63
64 static struct pm_qos_object null_pm_qos;
65 static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
66 static struct pm_qos_object cpu_dma_pm_qos = {
67         .requirements = {LIST_HEAD_INIT(cpu_dma_pm_qos.requirements.list)},
68         .notifiers = &cpu_dma_lat_notifier,
69         .name = "cpu_dma_latency",
70         .default_value = 2000 * USEC_PER_SEC,
71         .target_value = 2000 * USEC_PER_SEC,
72         .comparitor = min_compare
73 };
74
75 static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
76 static struct pm_qos_object network_lat_pm_qos = {
77         .requirements = {LIST_HEAD_INIT(network_lat_pm_qos.requirements.list)},
78         .notifiers = &network_lat_notifier,
79         .name = "network_latency",
80         .default_value = 2000 * USEC_PER_SEC,
81         .target_value = 2000 * USEC_PER_SEC,
82         .comparitor = min_compare
83 };
84
85
86 static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
87 static struct pm_qos_object network_throughput_pm_qos = {
88         .requirements =
89                 {LIST_HEAD_INIT(network_throughput_pm_qos.requirements.list)},
90         .notifiers = &network_throughput_notifier,
91         .name = "network_throughput",
92         .default_value = 0,
93         .target_value = 0,
94         .comparitor = max_compare
95 };
96
97
98 static struct pm_qos_object *pm_qos_array[] = {
99         &null_pm_qos,
100         &cpu_dma_pm_qos,
101         &network_lat_pm_qos,
102         &network_throughput_pm_qos
103 };
104
105 static DEFINE_SPINLOCK(pm_qos_lock);
106
107 static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
108                 size_t count, loff_t *f_pos);
109 static int pm_qos_power_open(struct inode *inode, struct file *filp);
110 static int pm_qos_power_release(struct inode *inode, struct file *filp);
111
112 static const struct file_operations pm_qos_power_fops = {
113         .write = pm_qos_power_write,
114         .open = pm_qos_power_open,
115         .release = pm_qos_power_release,
116 };
117
118 /* static helper functions */
119 static s32 max_compare(s32 v1, s32 v2)
120 {
121         return max(v1, v2);
122 }
123
124 static s32 min_compare(s32 v1, s32 v2)
125 {
126         return min(v1, v2);
127 }
128
129 static void update_target(int target)
130 {
131         s32 extreme_value;
132         struct requirement_list *node;
133         unsigned long flags;
134         int call_notifier = 0;
135
136         spin_lock_irqsave(&pm_qos_lock, flags);
137         extreme_value = pm_qos_array[target]->default_value;
138         list_for_each_entry(node,
139                         &pm_qos_array[target]->requirements.list, list) {
140                 extreme_value = pm_qos_array[target]->comparitor(
141                                 extreme_value, node->value);
142         }
143         if (pm_qos_array[target]->target_value != extreme_value) {
144                 call_notifier = 1;
145                 pm_qos_array[target]->target_value = extreme_value;
146                 pr_debug(KERN_ERR "new target for qos %d is %d\n", target,
147                         pm_qos_array[target]->target_value);
148         }
149         spin_unlock_irqrestore(&pm_qos_lock, flags);
150
151         if (call_notifier)
152                 blocking_notifier_call_chain(pm_qos_array[target]->notifiers,
153                         (unsigned long) extreme_value, NULL);
154 }
155
156 static int register_pm_qos_misc(struct pm_qos_object *qos)
157 {
158         qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
159         qos->pm_qos_power_miscdev.name = qos->name;
160         qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
161
162         return misc_register(&qos->pm_qos_power_miscdev);
163 }
164
165 static int find_pm_qos_object_by_minor(int minor)
166 {
167         int pm_qos_class;
168
169         for (pm_qos_class = 0;
170                 pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) {
171                 if (minor ==
172                         pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor)
173                         return pm_qos_class;
174         }
175         return -1;
176 }
177
178 /**
179  * pm_qos_requirement - returns current system wide qos expectation
180  * @pm_qos_class: identification of which qos value is requested
181  *
182  * This function returns the current target value in an atomic manner.
183  */
184 int pm_qos_requirement(int pm_qos_class)
185 {
186         int ret_val;
187         unsigned long flags;
188
189         spin_lock_irqsave(&pm_qos_lock, flags);
190         ret_val = pm_qos_array[pm_qos_class]->target_value;
191         spin_unlock_irqrestore(&pm_qos_lock, flags);
192
193         return ret_val;
194 }
195 EXPORT_SYMBOL_GPL(pm_qos_requirement);
196
197 /**
198  * pm_qos_add_requirement - inserts new qos request into the list
199  * @pm_qos_class: identifies which list of qos request to us
200  * @name: identifies the request
201  * @value: defines the qos request
202  *
203  * This function inserts a new entry in the pm_qos_class list of requested qos
204  * performance charactoistics.  It recomputes the agregate QoS expectations for
205  * the pm_qos_class of parrameters.
206  */
207 int pm_qos_add_requirement(int pm_qos_class, char *name, s32 value)
208 {
209         struct requirement_list *dep;
210         unsigned long flags;
211
212         dep = kzalloc(sizeof(struct requirement_list), GFP_KERNEL);
213         if (dep) {
214                 if (value == PM_QOS_DEFAULT_VALUE)
215                         dep->value = pm_qos_array[pm_qos_class]->default_value;
216                 else
217                         dep->value = value;
218                 dep->name = kstrdup(name, GFP_KERNEL);
219                 if (!dep->name)
220                         goto cleanup;
221
222                 spin_lock_irqsave(&pm_qos_lock, flags);
223                 list_add(&dep->list,
224                         &pm_qos_array[pm_qos_class]->requirements.list);
225                 spin_unlock_irqrestore(&pm_qos_lock, flags);
226                 update_target(pm_qos_class);
227
228                 return 0;
229         }
230
231 cleanup:
232         kfree(dep);
233         return -ENOMEM;
234 }
235 EXPORT_SYMBOL_GPL(pm_qos_add_requirement);
236
237 /**
238  * pm_qos_update_requirement - modifies an existing qos request
239  * @pm_qos_class: identifies which list of qos request to us
240  * @name: identifies the request
241  * @value: defines the qos request
242  *
243  * Updates an existing qos requierement for the pm_qos_class of parameters along
244  * with updating the target pm_qos_class value.
245  *
246  * If the named request isn't in the lest then no change is made.
247  */
248 int pm_qos_update_requirement(int pm_qos_class, char *name, s32 new_value)
249 {
250         unsigned long flags;
251         struct requirement_list *node;
252         int pending_update = 0;
253
254         spin_lock_irqsave(&pm_qos_lock, flags);
255         list_for_each_entry(node,
256                 &pm_qos_array[pm_qos_class]->requirements.list, list) {
257                 if (strcmp(node->name, name) == 0) {
258                         if (new_value == PM_QOS_DEFAULT_VALUE)
259                                 node->value =
260                                 pm_qos_array[pm_qos_class]->default_value;
261                         else
262                                 node->value = new_value;
263                         pending_update = 1;
264                         break;
265                 }
266         }
267         spin_unlock_irqrestore(&pm_qos_lock, flags);
268         if (pending_update)
269                 update_target(pm_qos_class);
270
271         return 0;
272 }
273 EXPORT_SYMBOL_GPL(pm_qos_update_requirement);
274
275 /**
276  * pm_qos_remove_requirement - modifies an existing qos request
277  * @pm_qos_class: identifies which list of qos request to us
278  * @name: identifies the request
279  *
280  * Will remove named qos request from pm_qos_class list of parrameters and
281  * recompute the current target value for the pm_qos_class.
282  */
283 void pm_qos_remove_requirement(int pm_qos_class, char *name)
284 {
285         unsigned long flags;
286         struct requirement_list *node;
287         int pending_update = 0;
288
289         spin_lock_irqsave(&pm_qos_lock, flags);
290         list_for_each_entry(node,
291                 &pm_qos_array[pm_qos_class]->requirements.list, list) {
292                 if (strcmp(node->name, name) == 0) {
293                         kfree(node->name);
294                         list_del(&node->list);
295                         kfree(node);
296                         pending_update = 1;
297                         break;
298                 }
299         }
300         spin_unlock_irqrestore(&pm_qos_lock, flags);
301         if (pending_update)
302                 update_target(pm_qos_class);
303 }
304 EXPORT_SYMBOL_GPL(pm_qos_remove_requirement);
305
306 /**
307  * pm_qos_add_notifier - sets notification entry for changes to target value
308  * @pm_qos_class: identifies which qos target changes should be notified.
309  * @notifier: notifier block managed by caller.
310  *
311  * will register the notifier into a notification chain that gets called
312  * uppon changes to the pm_qos_class target value.
313  */
314  int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier)
315 {
316         int retval;
317
318         retval = blocking_notifier_chain_register(
319                         pm_qos_array[pm_qos_class]->notifiers, notifier);
320
321         return retval;
322 }
323 EXPORT_SYMBOL_GPL(pm_qos_add_notifier);
324
325 /**
326  * pm_qos_remove_notifier - deletes notification entry from chain.
327  * @pm_qos_class: identifies which qos target changes are notified.
328  * @notifier: notifier block to be removed.
329  *
330  * will remove the notifier from the notification chain that gets called
331  * uppon changes to the pm_qos_class target value.
332  */
333 int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
334 {
335         int retval;
336
337         retval = blocking_notifier_chain_unregister(
338                         pm_qos_array[pm_qos_class]->notifiers, notifier);
339
340         return retval;
341 }
342 EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
343
344 #define PID_NAME_LEN sizeof("process_1234567890")
345 static char name[PID_NAME_LEN];
346
347 static int pm_qos_power_open(struct inode *inode, struct file *filp)
348 {
349         int ret;
350         long pm_qos_class;
351
352         pm_qos_class = find_pm_qos_object_by_minor(iminor(inode));
353         if (pm_qos_class >= 0) {
354                 filp->private_data = (void *)pm_qos_class;
355                 sprintf(name, "process_%d", current->pid);
356                 ret = pm_qos_add_requirement(pm_qos_class, name,
357                                         PM_QOS_DEFAULT_VALUE);
358                 if (ret >= 0)
359                         return 0;
360         }
361
362         return -EPERM;
363 }
364
365 static int pm_qos_power_release(struct inode *inode, struct file *filp)
366 {
367         int pm_qos_class;
368
369         pm_qos_class = (long)filp->private_data;
370         sprintf(name, "process_%d", current->pid);
371         pm_qos_remove_requirement(pm_qos_class, name);
372
373         return 0;
374 }
375
376 static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
377                 size_t count, loff_t *f_pos)
378 {
379         s32 value;
380         int pm_qos_class;
381
382         pm_qos_class = (long)filp->private_data;
383         if (count != sizeof(s32))
384                 return -EINVAL;
385         if (copy_from_user(&value, buf, sizeof(s32)))
386                 return -EFAULT;
387         sprintf(name, "process_%d", current->pid);
388         pm_qos_update_requirement(pm_qos_class, name, value);
389
390         return  sizeof(s32);
391 }
392
393 /*
394  * This initializes pm-qos for older kernels.
395  */
396 int compat_pm_qos_power_init(void)
397 {
398         int ret = 0;
399
400         ret = register_pm_qos_misc(&cpu_dma_pm_qos);
401         if (ret < 0) {
402                 printk(KERN_ERR "pm_qos_param: cpu_dma_latency setup failed\n");
403                 return ret;
404         }
405         ret = register_pm_qos_misc(&network_lat_pm_qos);
406         if (ret < 0) {
407                 printk(KERN_ERR "pm_qos_param: network_latency setup failed\n");
408                 return ret;
409         }
410         ret = register_pm_qos_misc(&network_throughput_pm_qos);
411         if (ret < 0)
412                 printk(KERN_ERR
413                         "pm_qos_param: network_throughput setup failed\n");
414
415         return ret;
416 }
417
418 /**
419  * The following things are out of ./lib/vsprintf.c
420  * The new iwlwifi driver is using them.
421  */
422
423 /**
424  * strict_strtoul - convert a string to an unsigned long strictly
425  * @cp: The string to be converted
426  * @base: The number base to use
427  * @res: The converted result value
428  *
429  * strict_strtoul converts a string to an unsigned long only if the
430  * string is really an unsigned long string, any string containing
431  * any invalid char at the tail will be rejected and -EINVAL is returned,
432  * only a newline char at the tail is acceptible because people generally
433  * change a module parameter in the following way:
434  *
435  *      echo 1024 > /sys/module/e1000/parameters/copybreak
436  *
437  * echo will append a newline to the tail.
438  *
439  * It returns 0 if conversion is successful and *res is set to the converted
440  * value, otherwise it returns -EINVAL and *res is set to 0.
441  *
442  * simple_strtoul just ignores the successive invalid characters and
443  * return the converted value of prefix part of the string.
444  */
445 int strict_strtoul(const char *cp, unsigned int base, unsigned long *res);
446
447 /**
448  * strict_strtol - convert a string to a long strictly
449  * @cp: The string to be converted
450  * @base: The number base to use
451  * @res: The converted result value
452  *
453  * strict_strtol is similiar to strict_strtoul, but it allows the first
454  * character of a string is '-'.
455  *
456  * It returns 0 if conversion is successful and *res is set to the converted
457  * value, otherwise it returns -EINVAL and *res is set to 0.
458  */
459 int strict_strtol(const char *cp, unsigned int base, long *res);
460
461 #define define_strict_strtoux(type, valtype)                            \
462 int strict_strtou##type(const char *cp, unsigned int base, valtype *res)\
463 {                                                                       \
464         char *tail;                                                     \
465         valtype val;                                                    \
466         size_t len;                                                     \
467                                                                         \
468         *res = 0;                                                       \
469         len = strlen(cp);                                               \
470         if (len == 0)                                                   \
471                 return -EINVAL;                                         \
472                                                                         \
473         val = simple_strtou##type(cp, &tail, base);                     \
474         if ((*tail == '\0') ||                                          \
475                 ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {\
476                 *res = val;                                             \
477                 return 0;                                               \
478         }                                                               \
479                                                                         \
480         return -EINVAL;                                                 \
481 }                                                                       \
482
483 #define define_strict_strtox(type, valtype)                             \
484 int strict_strto##type(const char *cp, unsigned int base, valtype *res) \
485 {                                                                       \
486         int ret;                                                        \
487         if (*cp == '-') {                                               \
488                 ret = strict_strtou##type(cp+1, base, res);             \
489                 if (!ret)                                               \
490                         *res = -(*res);                                 \
491         } else                                                          \
492                 ret = strict_strtou##type(cp, base, res);               \
493                                                                         \
494         return ret;                                                     \
495 }                                                                       \
496
497 define_strict_strtoux(l, unsigned long)
498 define_strict_strtox(l, long)
499
500 EXPORT_SYMBOL(strict_strtoul);
501 EXPORT_SYMBOL(strict_strtol);
502
503 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) */
504