linux/kernel/pm_qos_params.c
<<
>>
Prefs
   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 */
  49struct requirement_list {
  50        struct list_head list;
  51        union {
  52                s32 value;
  53                s32 usec;
  54                s32 kbps;
  55        };
  56        char *name;
  57};
  58
  59static s32 max_compare(s32 v1, s32 v2);
  60static s32 min_compare(s32 v1, s32 v2);
  61
  62struct 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
  72static struct pm_qos_object null_pm_qos;
  73static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
  74static 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
  83static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
  84static 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
  94static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
  95static 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
 106static 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
 113static DEFINE_SPINLOCK(pm_qos_lock);
 114
 115static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
 116                size_t count, loff_t *f_pos);
 117static int pm_qos_power_open(struct inode *inode, struct file *filp);
 118static int pm_qos_power_release(struct inode *inode, struct file *filp);
 119
 120static 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 */
 127static s32 max_compare(s32 v1, s32 v2)
 128{
 129        return max(v1, v2);
 130}
 131
 132static s32 min_compare(s32 v1, s32 v2)
 133{
 134        return min(v1, v2);
 135}
 136
 137
 138static 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
 165static 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
 174static 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 */
 193int 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}
 204EXPORT_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 */
 216int 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
 240cleanup:
 241        kfree(dep);
 242        return -ENOMEM;
 243}
 244EXPORT_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 */
 257int 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}
 282EXPORT_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 */
 292void 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}
 313EXPORT_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}
 332EXPORT_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 */
 342int 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}
 351EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
 352
 353#define PID_NAME_LEN sizeof("process_1234567890")
 354static char name[PID_NAME_LEN];
 355
 356static 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
 374static 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
 385static 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
 403static 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
 425late_initcall(pm_qos_power_init);
 426
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.