Add the new system bus frequency pm-qos object for older kernels
[~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 static BLOCKING_NOTIFIER_HEAD(system_bus_freq_notifier);
98 static struct pm_qos_object system_bus_freq_pm_qos = {
99         .requirements =
100                 {LIST_HEAD_INIT(system_bus_freq_pm_qos.requirements.list)},
101         .notifiers = &system_bus_freq_notifier,
102         .name = "system_bus_freq",
103         .default_value = 0,
104         .target_value = 0,
105         .comparitor = max_compare
106 };
107
108
109 static struct pm_qos_object *pm_qos_array[] = {
110         &null_pm_qos,
111         &cpu_dma_pm_qos,
112         &network_lat_pm_qos,
113         &network_throughput_pm_qos,
114         &system_bus_freq_pm_qos,
115 };
116
117 static DEFINE_SPINLOCK(pm_qos_lock);
118
119 static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
120                 size_t count, loff_t *f_pos);
121 static int pm_qos_power_open(struct inode *inode, struct file *filp);
122 static int pm_qos_power_release(struct inode *inode, struct file *filp);
123
124 static const struct file_operations pm_qos_power_fops = {
125         .write = pm_qos_power_write,
126         .open = pm_qos_power_open,
127         .release = pm_qos_power_release,
128 };
129
130 /* static helper functions */
131 static s32 max_compare(s32 v1, s32 v2)
132 {
133         return max(v1, v2);
134 }
135
136 static s32 min_compare(s32 v1, s32 v2)
137 {
138         return min(v1, v2);
139 }
140
141 static void update_target(int target)
142 {
143         s32 extreme_value;
144         struct requirement_list *node;
145         unsigned long flags;
146         int call_notifier = 0;
147
148         spin_lock_irqsave(&pm_qos_lock, flags);
149         extreme_value = pm_qos_array[target]->default_value;
150         list_for_each_entry(node,
151                         &pm_qos_array[target]->requirements.list, list) {
152                 extreme_value = pm_qos_array[target]->comparitor(
153                                 extreme_value, node->value);
154         }
155         if (pm_qos_array[target]->target_value != extreme_value) {
156                 call_notifier = 1;
157                 pm_qos_array[target]->target_value = extreme_value;
158                 pr_debug(KERN_ERR "new target for qos %d is %d\n", target,
159                         pm_qos_array[target]->target_value);
160         }
161         spin_unlock_irqrestore(&pm_qos_lock, flags);
162
163         if (call_notifier)
164                 blocking_notifier_call_chain(pm_qos_array[target]->notifiers,
165                         (unsigned long) extreme_value, NULL);
166 }
167
168 static int register_pm_qos_misc(struct pm_qos_object *qos)
169 {
170         qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
171         qos->pm_qos_power_miscdev.name = qos->name;
172         qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
173
174         return misc_register(&qos->pm_qos_power_miscdev);
175 }
176
177 static int find_pm_qos_object_by_minor(int minor)
178 {
179         int pm_qos_class;
180
181         for (pm_qos_class = 0;
182                 pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) {
183                 if (minor ==
184                         pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor)
185                         return pm_qos_class;
186         }
187         return -1;
188 }
189
190 /**
191  * pm_qos_requirement - returns current system wide qos expectation
192  * @pm_qos_class: identification of which qos value is requested
193  *
194  * This function returns the current target value in an atomic manner.
195  */
196 int pm_qos_requirement(int pm_qos_class)
197 {
198         int ret_val;
199         unsigned long flags;
200
201         spin_lock_irqsave(&pm_qos_lock, flags);
202         ret_val = pm_qos_array[pm_qos_class]->target_value;
203         spin_unlock_irqrestore(&pm_qos_lock, flags);
204
205         return ret_val;
206 }
207 EXPORT_SYMBOL_GPL(pm_qos_requirement);
208
209 /**
210  * pm_qos_add_requirement - inserts new qos request into the list
211  * @pm_qos_class: identifies which list of qos request to us
212  * @name: identifies the request
213  * @value: defines the qos request
214  *
215  * This function inserts a new entry in the pm_qos_class list of requested qos
216  * performance charactoistics.  It recomputes the agregate QoS expectations for
217  * the pm_qos_class of parrameters.
218  */
219 int pm_qos_add_requirement(int pm_qos_class, char *name, s32 value)
220 {
221         struct requirement_list *dep;
222         unsigned long flags;
223
224         dep = kzalloc(sizeof(struct requirement_list), GFP_KERNEL);
225         if (dep) {
226                 if (value == PM_QOS_DEFAULT_VALUE)
227                         dep->value = pm_qos_array[pm_qos_class]->default_value;
228                 else
229                         dep->value = value;
230                 dep->name = kstrdup(name, GFP_KERNEL);
231                 if (!dep->name)
232                         goto cleanup;
233
234                 spin_lock_irqsave(&pm_qos_lock, flags);
235                 list_add(&dep->list,
236                         &pm_qos_array[pm_qos_class]->requirements.list);
237                 spin_unlock_irqrestore(&pm_qos_lock, flags);
238                 update_target(pm_qos_class);
239
240                 return 0;
241         }
242
243 cleanup:
244         kfree(dep);
245         return -ENOMEM;
246 }
247 EXPORT_SYMBOL_GPL(pm_qos_add_requirement);
248
249 /**
250  * pm_qos_update_requirement - modifies an existing qos request
251  * @pm_qos_class: identifies which list of qos request to us
252  * @name: identifies the request
253  * @value: defines the qos request
254  *
255  * Updates an existing qos requierement for the pm_qos_class of parameters along
256  * with updating the target pm_qos_class value.
257  *
258  * If the named request isn't in the lest then no change is made.
259  */
260 int pm_qos_update_requirement(int pm_qos_class, char *name, s32 new_value)
261 {
262         unsigned long flags;
263         struct requirement_list *node;
264         int pending_update = 0;
265
266         spin_lock_irqsave(&pm_qos_lock, flags);
267         list_for_each_entry(node,
268                 &pm_qos_array[pm_qos_class]->requirements.list, list) {
269                 if (strcmp(node->name, name) == 0) {
270                         if (new_value == PM_QOS_DEFAULT_VALUE)
271                                 node->value =
272                                 pm_qos_array[pm_qos_class]->default_value;
273                         else
274                                 node->value = new_value;
275                         pending_update = 1;
276                         break;
277                 }
278         }
279         spin_unlock_irqrestore(&pm_qos_lock, flags);
280         if (pending_update)
281                 update_target(pm_qos_class);
282
283         return 0;
284 }
285 EXPORT_SYMBOL_GPL(pm_qos_update_requirement);
286
287 /**
288  * pm_qos_remove_requirement - modifies an existing qos request
289  * @pm_qos_class: identifies which list of qos request to us
290  * @name: identifies the request
291  *
292  * Will remove named qos request from pm_qos_class list of parrameters and
293  * recompute the current target value for the pm_qos_class.
294  */
295 void pm_qos_remove_requirement(int pm_qos_class, char *name)
296 {
297         unsigned long flags;
298         struct requirement_list *node;
299         int pending_update = 0;
300
301         spin_lock_irqsave(&pm_qos_lock, flags);
302         list_for_each_entry(node,
303                 &pm_qos_array[pm_qos_class]->requirements.list, list) {
304                 if (strcmp(node->name, name) == 0) {
305                         kfree(node->name);
306                         list_del(&node->list);
307                         kfree(node);
308                         pending_update = 1;
309                         break;
310                 }
311         }
312         spin_unlock_irqrestore(&pm_qos_lock, flags);
313         if (pending_update)
314                 update_target(pm_qos_class);
315 }
316 EXPORT_SYMBOL_GPL(pm_qos_remove_requirement);
317
318 /**
319  * pm_qos_add_notifier - sets notification entry for changes to target value
320  * @pm_qos_class: identifies which qos target changes should be notified.
321  * @notifier: notifier block managed by caller.
322  *
323  * will register the notifier into a notification chain that gets called
324  * uppon changes to the pm_qos_class target value.
325  */
326  int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier)
327 {
328         int retval;
329
330         retval = blocking_notifier_chain_register(
331                         pm_qos_array[pm_qos_class]->notifiers, notifier);
332
333         return retval;
334 }
335 EXPORT_SYMBOL_GPL(pm_qos_add_notifier);
336
337 /**
338  * pm_qos_remove_notifier - deletes notification entry from chain.
339  * @pm_qos_class: identifies which qos target changes are notified.
340  * @notifier: notifier block to be removed.
341  *
342  * will remove the notifier from the notification chain that gets called
343  * uppon changes to the pm_qos_class target value.
344  */
345 int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
346 {
347         int retval;
348
349         retval = blocking_notifier_chain_unregister(
350                         pm_qos_array[pm_qos_class]->notifiers, notifier);
351
352         return retval;
353 }
354 EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
355
356 #define PID_NAME_LEN sizeof("process_1234567890")
357 static char name[PID_NAME_LEN];
358
359 static int pm_qos_power_open(struct inode *inode, struct file *filp)
360 {
361         int ret;
362         long pm_qos_class;
363
364         pm_qos_class = find_pm_qos_object_by_minor(iminor(inode));
365         if (pm_qos_class >= 0) {
366                 filp->private_data = (void *)pm_qos_class;
367                 sprintf(name, "process_%d", current->pid);
368                 ret = pm_qos_add_requirement(pm_qos_class, name,
369                                         PM_QOS_DEFAULT_VALUE);
370                 if (ret >= 0)
371                         return 0;
372         }
373
374         return -EPERM;
375 }
376
377 static int pm_qos_power_release(struct inode *inode, struct file *filp)
378 {
379         int pm_qos_class;
380
381         pm_qos_class = (long)filp->private_data;
382         sprintf(name, "process_%d", current->pid);
383         pm_qos_remove_requirement(pm_qos_class, name);
384
385         return 0;
386 }
387
388 static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
389                 size_t count, loff_t *f_pos)
390 {
391         s32 value;
392         int pm_qos_class;
393
394         pm_qos_class = (long)filp->private_data;
395         if (count != sizeof(s32))
396                 return -EINVAL;
397         if (copy_from_user(&value, buf, sizeof(s32)))
398                 return -EFAULT;
399         sprintf(name, "process_%d", current->pid);
400         pm_qos_update_requirement(pm_qos_class, name, value);
401
402         return  sizeof(s32);
403 }
404
405 /*
406  * This initializes pm-qos for older kernels.
407  */
408 int compat_pm_qos_power_init(void)
409 {
410         int ret = 0;
411
412         ret = register_pm_qos_misc(&cpu_dma_pm_qos);
413         if (ret < 0) {
414                 printk(KERN_ERR "pm_qos_param: cpu_dma_latency setup failed\n");
415                 return ret;
416         }
417         ret = register_pm_qos_misc(&network_lat_pm_qos);
418         if (ret < 0) {
419                 printk(KERN_ERR "pm_qos_param: network_latency setup failed\n");
420                 return ret;
421         }
422         ret = register_pm_qos_misc(&network_throughput_pm_qos);
423         if (ret < 0) {
424                 printk(KERN_ERR
425                         "pm_qos_param: network_throughput setup failed\n");
426                 return ret;
427         }
428         ret = register_pm_qos_misc(&system_bus_freq_pm_qos);
429         if (ret < 0) {
430                 printk(KERN_ERR
431                         "pm_qos_param: network_throughput setup failed\n");
432                 return ret;
433         }
434
435         return ret;
436 }
437
438 /**
439  * The following things are out of ./lib/vsprintf.c
440  * The new iwlwifi driver is using them.
441  */
442
443 /**
444  * strict_strtoul - convert a string to an unsigned long strictly
445  * @cp: The string to be converted
446  * @base: The number base to use
447  * @res: The converted result value
448  *
449  * strict_strtoul converts a string to an unsigned long only if the
450  * string is really an unsigned long string, any string containing
451  * any invalid char at the tail will be rejected and -EINVAL is returned,
452  * only a newline char at the tail is acceptible because people generally
453  * change a module parameter in the following way:
454  *
455  *      echo 1024 > /sys/module/e1000/parameters/copybreak
456  *
457  * echo will append a newline to the tail.
458  *
459  * It returns 0 if conversion is successful and *res is set to the converted
460  * value, otherwise it returns -EINVAL and *res is set to 0.
461  *
462  * simple_strtoul just ignores the successive invalid characters and
463  * return the converted value of prefix part of the string.
464  */
465 int strict_strtoul(const char *cp, unsigned int base, unsigned long *res);
466
467 /**
468  * strict_strtol - convert a string to a long strictly
469  * @cp: The string to be converted
470  * @base: The number base to use
471  * @res: The converted result value
472  *
473  * strict_strtol is similiar to strict_strtoul, but it allows the first
474  * character of a string is '-'.
475  *
476  * It returns 0 if conversion is successful and *res is set to the converted
477  * value, otherwise it returns -EINVAL and *res is set to 0.
478  */
479 int strict_strtol(const char *cp, unsigned int base, long *res);
480
481 #define define_strict_strtoux(type, valtype)                            \
482 int strict_strtou##type(const char *cp, unsigned int base, valtype *res)\
483 {                                                                       \
484         char *tail;                                                     \
485         valtype val;                                                    \
486         size_t len;                                                     \
487                                                                         \
488         *res = 0;                                                       \
489         len = strlen(cp);                                               \
490         if (len == 0)                                                   \
491                 return -EINVAL;                                         \
492                                                                         \
493         val = simple_strtou##type(cp, &tail, base);                     \
494         if ((*tail == '\0') ||                                          \
495                 ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {\
496                 *res = val;                                             \
497                 return 0;                                               \
498         }                                                               \
499                                                                         \
500         return -EINVAL;                                                 \
501 }                                                                       \
502
503 #define define_strict_strtox(type, valtype)                             \
504 int strict_strto##type(const char *cp, unsigned int base, valtype *res) \
505 {                                                                       \
506         int ret;                                                        \
507         if (*cp == '-') {                                               \
508                 ret = strict_strtou##type(cp+1, base, res);             \
509                 if (!ret)                                               \
510                         *res = -(*res);                                 \
511         } else                                                          \
512                 ret = strict_strtou##type(cp, base, res);               \
513                                                                         \
514         return ret;                                                     \
515 }                                                                       \
516
517 define_strict_strtoux(l, unsigned long)
518 define_strict_strtox(l, long)
519
520 EXPORT_SYMBOL(strict_strtoul);
521 EXPORT_SYMBOL(strict_strtol);
522
523 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) */
524