From: Luis R. Rodriguez Date: Tue, 12 Jan 2010 01:07:28 +0000 (-0800) Subject: Extract pm-qos code into compat/pm_qos_params.c X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=5bb94c3921ab7935a5846c15548bd22caa3c6452;p=openwrt%2Fstaging%2Fblogic.git Extract pm-qos code into compat/pm_qos_params.c This should make it easier to synch with later version of kernel/pm_qos_params.c Signed-off-by: Luis R. Rodriguez --- diff --git a/compat/Makefile b/compat/Makefile index fd2f99fe5ded..d950f47fe600 100644 --- a/compat/Makefile +++ b/compat/Makefile @@ -10,7 +10,11 @@ compat-$(CONFIG_COMPAT_KERNEL_19) += compat-2.6.19.o compat-$(CONFIG_COMPAT_KERNEL_21) += compat-2.6.21.o compat-$(CONFIG_COMPAT_KERNEL_22) += compat-2.6.22.o compat-$(CONFIG_COMPAT_KERNEL_23) += compat-2.6.23.o -compat-$(CONFIG_COMPAT_KERNEL_24) += compat-2.6.24.o + +compat-$(CONFIG_COMPAT_KERNEL_24) += \ + compat-2.6.24.o \ + pm_qos_params.o + compat-$(CONFIG_COMPAT_KERNEL_25) += compat-2.6.25.o compat-$(CONFIG_COMPAT_KERNEL_26) += compat-2.6.26.o compat-$(CONFIG_COMPAT_KERNEL_27) += compat-2.6.27.o diff --git a/compat/compat-2.6.25.c b/compat/compat-2.6.25.c index 7ce42326b770..7546821031d6 100644 --- a/compat/compat-2.6.25.c +++ b/compat/compat-2.6.25.c @@ -8,433 +8,11 @@ * Compatibility file for Linux wireless for kernels 2.6.25. */ -#include - /* All things not in 2.6.22, 2.6.23 and 2.6.24 */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)) #include -/* - * Backport work for QoS dependencies (kernel/pm_qos_params.c) - * pm-qos stuff written by mark gross mgross@linux.intel.com. - * - * ipw2100 now makes use of - * pm_qos_add_requirement(), - * pm_qos_update_requirement() and - * pm_qos_remove_requirement() from it - * - * mac80211 uses the network latency to determine if to enable or not - * dynamic PS. mac80211 also and registers a notifier for when - * the latency changes. Since older kernels do no thave pm-qos stuff - * we just implement it completley here and register it upon cfg80211 - * init. I haven't tested ipw2100 on 2.6.24 though. - * - * This is copied from the kernel written by mark gross mgross@linux.intel.com - */ - -/* - * locking rule: all changes to target_value or requirements or notifiers lists - * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock - * held, taken with _irqsave. One lock to rule them all - */ -struct requirement_list { - struct list_head list; - union { - s32 value; - s32 usec; - s32 kbps; - }; - char *name; -}; - -static s32 max_compare(s32 v1, s32 v2); -static s32 min_compare(s32 v1, s32 v2); - -struct pm_qos_object { - struct requirement_list requirements; - struct blocking_notifier_head *notifiers; - struct miscdevice pm_qos_power_miscdev; - char *name; - s32 default_value; - s32 target_value; - s32 (*comparitor)(s32, s32); -}; - -static struct pm_qos_object null_pm_qos; -static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier); -static struct pm_qos_object cpu_dma_pm_qos = { - .requirements = {LIST_HEAD_INIT(cpu_dma_pm_qos.requirements.list)}, - .notifiers = &cpu_dma_lat_notifier, - .name = "cpu_dma_latency", - .default_value = 2000 * USEC_PER_SEC, - .target_value = 2000 * USEC_PER_SEC, - .comparitor = min_compare -}; - -static BLOCKING_NOTIFIER_HEAD(network_lat_notifier); -static struct pm_qos_object network_lat_pm_qos = { - .requirements = {LIST_HEAD_INIT(network_lat_pm_qos.requirements.list)}, - .notifiers = &network_lat_notifier, - .name = "network_latency", - .default_value = 2000 * USEC_PER_SEC, - .target_value = 2000 * USEC_PER_SEC, - .comparitor = min_compare -}; - - -static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier); -static struct pm_qos_object network_throughput_pm_qos = { - .requirements = - {LIST_HEAD_INIT(network_throughput_pm_qos.requirements.list)}, - .notifiers = &network_throughput_notifier, - .name = "network_throughput", - .default_value = 0, - .target_value = 0, - .comparitor = max_compare -}; - -static BLOCKING_NOTIFIER_HEAD(system_bus_freq_notifier); -static struct pm_qos_object system_bus_freq_pm_qos = { - .requirements = - {LIST_HEAD_INIT(system_bus_freq_pm_qos.requirements.list)}, - .notifiers = &system_bus_freq_notifier, - .name = "system_bus_freq", - .default_value = 0, - .target_value = 0, - .comparitor = max_compare -}; - - -static struct pm_qos_object *pm_qos_array[] = { - [PM_QOS_RESERVED] = &null_pm_qos, - [PM_QOS_CPU_DMA_LATENCY] = &cpu_dma_pm_qos, - [PM_QOS_NETWORK_LATENCY] = &network_lat_pm_qos, - [PM_QOS_NETWORK_THROUGHPUT] = &network_throughput_pm_qos, - [PM_QOS_SYSTEM_BUS_FREQ] = &system_bus_freq_pm_qos, -}; - -static DEFINE_SPINLOCK(pm_qos_lock); - -static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, - size_t count, loff_t *f_pos); -static int pm_qos_power_open(struct inode *inode, struct file *filp); -static int pm_qos_power_release(struct inode *inode, struct file *filp); - -static const struct file_operations pm_qos_power_fops = { - .write = pm_qos_power_write, - .open = pm_qos_power_open, - .release = pm_qos_power_release, -}; - -/* static helper functions */ -static s32 max_compare(s32 v1, s32 v2) -{ - return max(v1, v2); -} - -static s32 min_compare(s32 v1, s32 v2) -{ - return min(v1, v2); -} - -static void update_target(int target) -{ - s32 extreme_value; - struct requirement_list *node; - unsigned long flags; - int call_notifier = 0; - - spin_lock_irqsave(&pm_qos_lock, flags); - extreme_value = pm_qos_array[target]->default_value; - list_for_each_entry(node, - &pm_qos_array[target]->requirements.list, list) { - extreme_value = pm_qos_array[target]->comparitor( - extreme_value, node->value); - } - if (pm_qos_array[target]->target_value != extreme_value) { - call_notifier = 1; - pm_qos_array[target]->target_value = extreme_value; - pr_debug(KERN_ERR "new target for qos %d is %d\n", target, - pm_qos_array[target]->target_value); - } - spin_unlock_irqrestore(&pm_qos_lock, flags); - - if (call_notifier) - blocking_notifier_call_chain(pm_qos_array[target]->notifiers, - (unsigned long) extreme_value, NULL); -} - -static int register_pm_qos_misc(struct pm_qos_object *qos) -{ - qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR; - qos->pm_qos_power_miscdev.name = qos->name; - qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops; - - return misc_register(&qos->pm_qos_power_miscdev); -} - -static int find_pm_qos_object_by_minor(int minor) -{ - int pm_qos_class; - - for (pm_qos_class = 0; - pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) { - if (minor == - pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor) - return pm_qos_class; - } - return -1; -} - -/** - * pm_qos_requirement - returns current system wide qos expectation - * @pm_qos_class: identification of which qos value is requested - * - * This function returns the current target value in an atomic manner. - */ -int pm_qos_requirement(int pm_qos_class) -{ - int ret_val; - unsigned long flags; - - spin_lock_irqsave(&pm_qos_lock, flags); - ret_val = pm_qos_array[pm_qos_class]->target_value; - spin_unlock_irqrestore(&pm_qos_lock, flags); - - return ret_val; -} -EXPORT_SYMBOL_GPL(pm_qos_requirement); - -/** - * pm_qos_add_requirement - inserts new qos request into the list - * @pm_qos_class: identifies which list of qos request to us - * @name: identifies the request - * @value: defines the qos request - * - * This function inserts a new entry in the pm_qos_class list of requested qos - * performance charactoistics. It recomputes the agregate QoS expectations for - * the pm_qos_class of parrameters. - */ -int pm_qos_add_requirement(int pm_qos_class, char *name, s32 value) -{ - struct requirement_list *dep; - unsigned long flags; - - dep = kzalloc(sizeof(struct requirement_list), GFP_KERNEL); - if (dep) { - if (value == PM_QOS_DEFAULT_VALUE) - dep->value = pm_qos_array[pm_qos_class]->default_value; - else - dep->value = value; - dep->name = kstrdup(name, GFP_KERNEL); - if (!dep->name) - goto cleanup; - - spin_lock_irqsave(&pm_qos_lock, flags); - list_add(&dep->list, - &pm_qos_array[pm_qos_class]->requirements.list); - spin_unlock_irqrestore(&pm_qos_lock, flags); - update_target(pm_qos_class); - - return 0; - } - -cleanup: - kfree(dep); - return -ENOMEM; -} -EXPORT_SYMBOL_GPL(pm_qos_add_requirement); - -/** - * pm_qos_update_requirement - modifies an existing qos request - * @pm_qos_class: identifies which list of qos request to us - * @name: identifies the request - * @value: defines the qos request - * - * Updates an existing qos requierement for the pm_qos_class of parameters along - * with updating the target pm_qos_class value. - * - * If the named request isn't in the lest then no change is made. - */ -int pm_qos_update_requirement(int pm_qos_class, char *name, s32 new_value) -{ - unsigned long flags; - struct requirement_list *node; - int pending_update = 0; - - spin_lock_irqsave(&pm_qos_lock, flags); - list_for_each_entry(node, - &pm_qos_array[pm_qos_class]->requirements.list, list) { - if (strcmp(node->name, name) == 0) { - if (new_value == PM_QOS_DEFAULT_VALUE) - node->value = - pm_qos_array[pm_qos_class]->default_value; - else - node->value = new_value; - pending_update = 1; - break; - } - } - spin_unlock_irqrestore(&pm_qos_lock, flags); - if (pending_update) - update_target(pm_qos_class); - - return 0; -} -EXPORT_SYMBOL_GPL(pm_qos_update_requirement); - -/** - * pm_qos_remove_requirement - modifies an existing qos request - * @pm_qos_class: identifies which list of qos request to us - * @name: identifies the request - * - * Will remove named qos request from pm_qos_class list of parrameters and - * recompute the current target value for the pm_qos_class. - */ -void pm_qos_remove_requirement(int pm_qos_class, char *name) -{ - unsigned long flags; - struct requirement_list *node; - int pending_update = 0; - - spin_lock_irqsave(&pm_qos_lock, flags); - list_for_each_entry(node, - &pm_qos_array[pm_qos_class]->requirements.list, list) { - if (strcmp(node->name, name) == 0) { - kfree(node->name); - list_del(&node->list); - kfree(node); - pending_update = 1; - break; - } - } - spin_unlock_irqrestore(&pm_qos_lock, flags); - if (pending_update) - update_target(pm_qos_class); -} -EXPORT_SYMBOL_GPL(pm_qos_remove_requirement); - -/** - * pm_qos_add_notifier - sets notification entry for changes to target value - * @pm_qos_class: identifies which qos target changes should be notified. - * @notifier: notifier block managed by caller. - * - * will register the notifier into a notification chain that gets called - * uppon changes to the pm_qos_class target value. - */ - int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier) -{ - int retval; - - retval = blocking_notifier_chain_register( - pm_qos_array[pm_qos_class]->notifiers, notifier); - - return retval; -} -EXPORT_SYMBOL_GPL(pm_qos_add_notifier); - -/** - * pm_qos_remove_notifier - deletes notification entry from chain. - * @pm_qos_class: identifies which qos target changes are notified. - * @notifier: notifier block to be removed. - * - * will remove the notifier from the notification chain that gets called - * uppon changes to the pm_qos_class target value. - */ -int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier) -{ - int retval; - - retval = blocking_notifier_chain_unregister( - pm_qos_array[pm_qos_class]->notifiers, notifier); - - return retval; -} -EXPORT_SYMBOL_GPL(pm_qos_remove_notifier); - -#define PID_NAME_LEN sizeof("process_1234567890") -static char name[PID_NAME_LEN]; - -static int pm_qos_power_open(struct inode *inode, struct file *filp) -{ - int ret; - long pm_qos_class; - - pm_qos_class = find_pm_qos_object_by_minor(iminor(inode)); - if (pm_qos_class >= 0) { - filp->private_data = (void *)pm_qos_class; - sprintf(name, "process_%d", current->pid); - ret = pm_qos_add_requirement(pm_qos_class, name, - PM_QOS_DEFAULT_VALUE); - if (ret >= 0) - return 0; - } - - return -EPERM; -} - -static int pm_qos_power_release(struct inode *inode, struct file *filp) -{ - int pm_qos_class; - - pm_qos_class = (long)filp->private_data; - sprintf(name, "process_%d", current->pid); - pm_qos_remove_requirement(pm_qos_class, name); - - return 0; -} - -static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, - size_t count, loff_t *f_pos) -{ - s32 value; - int pm_qos_class; - - pm_qos_class = (long)filp->private_data; - if (count != sizeof(s32)) - return -EINVAL; - if (copy_from_user(&value, buf, sizeof(s32))) - return -EFAULT; - sprintf(name, "process_%d", current->pid); - pm_qos_update_requirement(pm_qos_class, name, value); - - return sizeof(s32); -} - -/* - * This initializes pm-qos for older kernels. - */ -int compat_pm_qos_power_init(void) -{ - int ret = 0; - - ret = register_pm_qos_misc(&cpu_dma_pm_qos); - if (ret < 0) { - printk(KERN_ERR "pm_qos_param: cpu_dma_latency setup failed\n"); - return ret; - } - ret = register_pm_qos_misc(&network_lat_pm_qos); - if (ret < 0) { - printk(KERN_ERR "pm_qos_param: network_latency setup failed\n"); - return ret; - } - ret = register_pm_qos_misc(&network_throughput_pm_qos); - if (ret < 0) { - printk(KERN_ERR - "pm_qos_param: network_throughput setup failed\n"); - return ret; - } - ret = register_pm_qos_misc(&system_bus_freq_pm_qos); - if (ret < 0) { - printk(KERN_ERR - "pm_qos_param: network_throughput setup failed\n"); - return ret; - } - - return ret; -} - /** * The following things are out of ./lib/vsprintf.c * The new iwlwifi driver is using them. diff --git a/compat/pm_qos_params.c b/compat/pm_qos_params.c new file mode 100644 index 000000000000..da93e957b8fe --- /dev/null +++ b/compat/pm_qos_params.c @@ -0,0 +1,428 @@ +#include + +/* All things not in 2.6.22, 2.6.23 and 2.6.24 */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)) + +#include + +/* + * Backport work for QoS dependencies (kernel/pm_qos_params.c) + * pm-qos stuff written by mark gross mgross@linux.intel.com. + * + * ipw2100 now makes use of + * pm_qos_add_requirement(), + * pm_qos_update_requirement() and + * pm_qos_remove_requirement() from it + * + * mac80211 uses the network latency to determine if to enable or not + * dynamic PS. mac80211 also and registers a notifier for when + * the latency changes. Since older kernels do no thave pm-qos stuff + * we just implement it completley here and register it upon cfg80211 + * init. I haven't tested ipw2100 on 2.6.24 though. + * + * This is copied from the kernel written by mark gross mgross@linux.intel.com + */ + +/* + * locking rule: all changes to target_value or requirements or notifiers lists + * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock + * held, taken with _irqsave. One lock to rule them all + */ +struct requirement_list { + struct list_head list; + union { + s32 value; + s32 usec; + s32 kbps; + }; + char *name; +}; + +static s32 max_compare(s32 v1, s32 v2); +static s32 min_compare(s32 v1, s32 v2); + +struct pm_qos_object { + struct requirement_list requirements; + struct blocking_notifier_head *notifiers; + struct miscdevice pm_qos_power_miscdev; + char *name; + s32 default_value; + s32 target_value; + s32 (*comparitor)(s32, s32); +}; + +static struct pm_qos_object null_pm_qos; +static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier); +static struct pm_qos_object cpu_dma_pm_qos = { + .requirements = {LIST_HEAD_INIT(cpu_dma_pm_qos.requirements.list)}, + .notifiers = &cpu_dma_lat_notifier, + .name = "cpu_dma_latency", + .default_value = 2000 * USEC_PER_SEC, + .target_value = 2000 * USEC_PER_SEC, + .comparitor = min_compare +}; + +static BLOCKING_NOTIFIER_HEAD(network_lat_notifier); +static struct pm_qos_object network_lat_pm_qos = { + .requirements = {LIST_HEAD_INIT(network_lat_pm_qos.requirements.list)}, + .notifiers = &network_lat_notifier, + .name = "network_latency", + .default_value = 2000 * USEC_PER_SEC, + .target_value = 2000 * USEC_PER_SEC, + .comparitor = min_compare +}; + + +static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier); +static struct pm_qos_object network_throughput_pm_qos = { + .requirements = + {LIST_HEAD_INIT(network_throughput_pm_qos.requirements.list)}, + .notifiers = &network_throughput_notifier, + .name = "network_throughput", + .default_value = 0, + .target_value = 0, + .comparitor = max_compare +}; + +static BLOCKING_NOTIFIER_HEAD(system_bus_freq_notifier); +static struct pm_qos_object system_bus_freq_pm_qos = { + .requirements = + {LIST_HEAD_INIT(system_bus_freq_pm_qos.requirements.list)}, + .notifiers = &system_bus_freq_notifier, + .name = "system_bus_freq", + .default_value = 0, + .target_value = 0, + .comparitor = max_compare +}; + + +static struct pm_qos_object *pm_qos_array[] = { + [PM_QOS_RESERVED] = &null_pm_qos, + [PM_QOS_CPU_DMA_LATENCY] = &cpu_dma_pm_qos, + [PM_QOS_NETWORK_LATENCY] = &network_lat_pm_qos, + [PM_QOS_NETWORK_THROUGHPUT] = &network_throughput_pm_qos, + [PM_QOS_SYSTEM_BUS_FREQ] = &system_bus_freq_pm_qos, +}; + +static DEFINE_SPINLOCK(pm_qos_lock); + +static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos); +static int pm_qos_power_open(struct inode *inode, struct file *filp); +static int pm_qos_power_release(struct inode *inode, struct file *filp); + +static const struct file_operations pm_qos_power_fops = { + .write = pm_qos_power_write, + .open = pm_qos_power_open, + .release = pm_qos_power_release, +}; + +/* static helper functions */ +static s32 max_compare(s32 v1, s32 v2) +{ + return max(v1, v2); +} + +static s32 min_compare(s32 v1, s32 v2) +{ + return min(v1, v2); +} + +static void update_target(int target) +{ + s32 extreme_value; + struct requirement_list *node; + unsigned long flags; + int call_notifier = 0; + + spin_lock_irqsave(&pm_qos_lock, flags); + extreme_value = pm_qos_array[target]->default_value; + list_for_each_entry(node, + &pm_qos_array[target]->requirements.list, list) { + extreme_value = pm_qos_array[target]->comparitor( + extreme_value, node->value); + } + if (pm_qos_array[target]->target_value != extreme_value) { + call_notifier = 1; + pm_qos_array[target]->target_value = extreme_value; + pr_debug(KERN_ERR "new target for qos %d is %d\n", target, + pm_qos_array[target]->target_value); + } + spin_unlock_irqrestore(&pm_qos_lock, flags); + + if (call_notifier) + blocking_notifier_call_chain(pm_qos_array[target]->notifiers, + (unsigned long) extreme_value, NULL); +} + +static int register_pm_qos_misc(struct pm_qos_object *qos) +{ + qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR; + qos->pm_qos_power_miscdev.name = qos->name; + qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops; + + return misc_register(&qos->pm_qos_power_miscdev); +} + +static int find_pm_qos_object_by_minor(int minor) +{ + int pm_qos_class; + + for (pm_qos_class = 0; + pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) { + if (minor == + pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor) + return pm_qos_class; + } + return -1; +} + +/** + * pm_qos_requirement - returns current system wide qos expectation + * @pm_qos_class: identification of which qos value is requested + * + * This function returns the current target value in an atomic manner. + */ +int pm_qos_requirement(int pm_qos_class) +{ + int ret_val; + unsigned long flags; + + spin_lock_irqsave(&pm_qos_lock, flags); + ret_val = pm_qos_array[pm_qos_class]->target_value; + spin_unlock_irqrestore(&pm_qos_lock, flags); + + return ret_val; +} +EXPORT_SYMBOL_GPL(pm_qos_requirement); + +/** + * pm_qos_add_requirement - inserts new qos request into the list + * @pm_qos_class: identifies which list of qos request to us + * @name: identifies the request + * @value: defines the qos request + * + * This function inserts a new entry in the pm_qos_class list of requested qos + * performance charactoistics. It recomputes the agregate QoS expectations for + * the pm_qos_class of parrameters. + */ +int pm_qos_add_requirement(int pm_qos_class, char *name, s32 value) +{ + struct requirement_list *dep; + unsigned long flags; + + dep = kzalloc(sizeof(struct requirement_list), GFP_KERNEL); + if (dep) { + if (value == PM_QOS_DEFAULT_VALUE) + dep->value = pm_qos_array[pm_qos_class]->default_value; + else + dep->value = value; + dep->name = kstrdup(name, GFP_KERNEL); + if (!dep->name) + goto cleanup; + + spin_lock_irqsave(&pm_qos_lock, flags); + list_add(&dep->list, + &pm_qos_array[pm_qos_class]->requirements.list); + spin_unlock_irqrestore(&pm_qos_lock, flags); + update_target(pm_qos_class); + + return 0; + } + +cleanup: + kfree(dep); + return -ENOMEM; +} +EXPORT_SYMBOL_GPL(pm_qos_add_requirement); + +/** + * pm_qos_update_requirement - modifies an existing qos request + * @pm_qos_class: identifies which list of qos request to us + * @name: identifies the request + * @value: defines the qos request + * + * Updates an existing qos requierement for the pm_qos_class of parameters along + * with updating the target pm_qos_class value. + * + * If the named request isn't in the lest then no change is made. + */ +int pm_qos_update_requirement(int pm_qos_class, char *name, s32 new_value) +{ + unsigned long flags; + struct requirement_list *node; + int pending_update = 0; + + spin_lock_irqsave(&pm_qos_lock, flags); + list_for_each_entry(node, + &pm_qos_array[pm_qos_class]->requirements.list, list) { + if (strcmp(node->name, name) == 0) { + if (new_value == PM_QOS_DEFAULT_VALUE) + node->value = + pm_qos_array[pm_qos_class]->default_value; + else + node->value = new_value; + pending_update = 1; + break; + } + } + spin_unlock_irqrestore(&pm_qos_lock, flags); + if (pending_update) + update_target(pm_qos_class); + + return 0; +} +EXPORT_SYMBOL_GPL(pm_qos_update_requirement); + +/** + * pm_qos_remove_requirement - modifies an existing qos request + * @pm_qos_class: identifies which list of qos request to us + * @name: identifies the request + * + * Will remove named qos request from pm_qos_class list of parrameters and + * recompute the current target value for the pm_qos_class. + */ +void pm_qos_remove_requirement(int pm_qos_class, char *name) +{ + unsigned long flags; + struct requirement_list *node; + int pending_update = 0; + + spin_lock_irqsave(&pm_qos_lock, flags); + list_for_each_entry(node, + &pm_qos_array[pm_qos_class]->requirements.list, list) { + if (strcmp(node->name, name) == 0) { + kfree(node->name); + list_del(&node->list); + kfree(node); + pending_update = 1; + break; + } + } + spin_unlock_irqrestore(&pm_qos_lock, flags); + if (pending_update) + update_target(pm_qos_class); +} +EXPORT_SYMBOL_GPL(pm_qos_remove_requirement); + +/** + * pm_qos_add_notifier - sets notification entry for changes to target value + * @pm_qos_class: identifies which qos target changes should be notified. + * @notifier: notifier block managed by caller. + * + * will register the notifier into a notification chain that gets called + * uppon changes to the pm_qos_class target value. + */ + int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier) +{ + int retval; + + retval = blocking_notifier_chain_register( + pm_qos_array[pm_qos_class]->notifiers, notifier); + + return retval; +} +EXPORT_SYMBOL_GPL(pm_qos_add_notifier); + +/** + * pm_qos_remove_notifier - deletes notification entry from chain. + * @pm_qos_class: identifies which qos target changes are notified. + * @notifier: notifier block to be removed. + * + * will remove the notifier from the notification chain that gets called + * uppon changes to the pm_qos_class target value. + */ +int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier) +{ + int retval; + + retval = blocking_notifier_chain_unregister( + pm_qos_array[pm_qos_class]->notifiers, notifier); + + return retval; +} +EXPORT_SYMBOL_GPL(pm_qos_remove_notifier); + +#define PID_NAME_LEN sizeof("process_1234567890") +static char name[PID_NAME_LEN]; + +static int pm_qos_power_open(struct inode *inode, struct file *filp) +{ + int ret; + long pm_qos_class; + + pm_qos_class = find_pm_qos_object_by_minor(iminor(inode)); + if (pm_qos_class >= 0) { + filp->private_data = (void *)pm_qos_class; + sprintf(name, "process_%d", current->pid); + ret = pm_qos_add_requirement(pm_qos_class, name, + PM_QOS_DEFAULT_VALUE); + if (ret >= 0) + return 0; + } + + return -EPERM; +} + +static int pm_qos_power_release(struct inode *inode, struct file *filp) +{ + int pm_qos_class; + + pm_qos_class = (long)filp->private_data; + sprintf(name, "process_%d", current->pid); + pm_qos_remove_requirement(pm_qos_class, name); + + return 0; +} + +static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + s32 value; + int pm_qos_class; + + pm_qos_class = (long)filp->private_data; + if (count != sizeof(s32)) + return -EINVAL; + if (copy_from_user(&value, buf, sizeof(s32))) + return -EFAULT; + sprintf(name, "process_%d", current->pid); + pm_qos_update_requirement(pm_qos_class, name, value); + + return sizeof(s32); +} + +/* + * This initializes pm-qos for older kernels. + */ +int compat_pm_qos_power_init(void) +{ + int ret = 0; + + ret = register_pm_qos_misc(&cpu_dma_pm_qos); + if (ret < 0) { + printk(KERN_ERR "pm_qos_param: cpu_dma_latency setup failed\n"); + return ret; + } + ret = register_pm_qos_misc(&network_lat_pm_qos); + if (ret < 0) { + printk(KERN_ERR "pm_qos_param: network_latency setup failed\n"); + return ret; + } + ret = register_pm_qos_misc(&network_throughput_pm_qos); + if (ret < 0) { + printk(KERN_ERR + "pm_qos_param: network_throughput setup failed\n"); + return ret; + } + ret = register_pm_qos_misc(&system_bus_freq_pm_qos); + if (ret < 0) { + printk(KERN_ERR + "pm_qos_param: network_throughput setup failed\n"); + return ret; + } + + return ret; +} + +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) */