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 requests
   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 requests, notifiers
  18 *
  19 * User mode requests 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 * request is removed and a new qos target is computed.  This way when the
  24 * request 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/*#define DEBUG*/
  31
  32#include <linux/pm_qos_params.h>
  33#include <linux/sched.h>
  34#include <linux/spinlock.h>
  35#include <linux/slab.h>
  36#include <linux/time.h>
  37#include <linux/fs.h>
  38#include <linux/device.h>
  39#include <linux/miscdevice.h>
  40#include <linux/string.h>
  41#include <linux/platform_device.h>
  42#include <linux/init.h>
  43
  44#include <linux/uaccess.h>
  45
  46/*
  47 * locking rule: all changes to requests or notifiers lists
  48 * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock
  49 * held, taken with _irqsave.  One lock to rule them all
  50 */
  51enum pm_qos_type {
  52        PM_QOS_MAX,             /* return the largest value */
  53        PM_QOS_MIN              /* return the smallest value */
  54};
  55
  56struct pm_qos_object {
  57        struct plist_head requests;
  58        struct blocking_notifier_head *notifiers;
  59        struct miscdevice pm_qos_power_miscdev;
  60        char *name;
  61        s32 default_value;
  62        enum pm_qos_type type;
  63};
  64
  65static DEFINE_SPINLOCK(pm_qos_lock);
  66
  67static struct pm_qos_object null_pm_qos;
  68static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
  69static struct pm_qos_object cpu_dma_pm_qos = {
  70        .requests = PLIST_HEAD_INIT(cpu_dma_pm_qos.requests, pm_qos_lock),
  71        .notifiers = &cpu_dma_lat_notifier,
  72        .name = "cpu_dma_latency",
  73        .default_value = 2000 * USEC_PER_SEC,
  74        .type = PM_QOS_MIN,
  75};
  76
  77static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
  78static struct pm_qos_object network_lat_pm_qos = {
  79        .requests = PLIST_HEAD_INIT(network_lat_pm_qos.requests, pm_qos_lock),
  80        .notifiers = &network_lat_notifier,
  81        .name = "network_latency",
  82        .default_value = 2000 * USEC_PER_SEC,
  83        .type = PM_QOS_MIN
  84};
  85
  86
  87static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
  88static struct pm_qos_object network_throughput_pm_qos = {
  89        .requests = PLIST_HEAD_INIT(network_throughput_pm_qos.requests, pm_qos_lock),
  90        .notifiers = &network_throughput_notifier,
  91        .name = "network_throughput",
  92        .default_value = 0,
  93        .type = PM_QOS_MAX,
  94};
  95
  96
  97static struct pm_qos_object *pm_qos_array[] = {
  98        &null_pm_qos,
  99        &cpu_dma_pm_qos,
 100        &network_lat_pm_qos,
 101        &network_throughput_pm_qos
 102};
 103
 104static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
 105                size_t count, loff_t *f_pos);
 106static int pm_qos_power_open(struct inode *inode, struct file *filp);
 107static int pm_qos_power_release(struct inode *inode, struct file *filp);
 108
 109static const struct file_operations pm_qos_power_fops = {
 110        .write = pm_qos_power_write,
 111        .open = pm_qos_power_open,
 112        .release = pm_qos_power_release,
 113        .llseek = noop_llseek,
 114};
 115
 116/* unlocked internal variant */
 117static inline int pm_qos_get_value(struct pm_qos_object *o)
 118{
 119        if (plist_head_empty(&o->requests))
 120                return o->default_value;
 121
 122        switch (o->type) {
 123        case PM_QOS_MIN:
 124                return plist_first(&o->requests)->prio;
 125
 126        case PM_QOS_MAX:
 127                return plist_last(&o->requests)->prio;
 128
 129        default:
 130                /* runtime check for not using enum */
 131                BUG();
 132        }
 133}
 134
 135static void update_target(struct pm_qos_object *o, struct plist_node *node,
 136                          int del, int value)
 137{
 138        unsigned long flags;
 139        int prev_value, curr_value;
 140
 141        spin_lock_irqsave(&pm_qos_lock, flags);
 142        prev_value = pm_qos_get_value(o);
 143        /* PM_QOS_DEFAULT_VALUE is a signal that the value is unchanged */
 144        if (value != PM_QOS_DEFAULT_VALUE) {
 145                /*
 146                 * to change the list, we atomically remove, reinit
 147                 * with new value and add, then see if the extremal
 148                 * changed
 149                 */
 150                plist_del(node, &o->requests);
 151                plist_node_init(node, value);
 152                plist_add(node, &o->requests);
 153        } else if (del) {
 154                plist_del(node, &o->requests);
 155        } else {
 156                plist_add(node, &o->requests);
 157        }
 158        curr_value = pm_qos_get_value(o);
 159        spin_unlock_irqrestore(&pm_qos_lock, flags);
 160
 161        if (prev_value != curr_value)
 162                blocking_notifier_call_chain(o->notifiers,
 163                                             (unsigned long)curr_value,
 164                                             NULL);
 165}
 166
 167static int register_pm_qos_misc(struct pm_qos_object *qos)
 168{
 169        qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
 170        qos->pm_qos_power_miscdev.name = qos->name;
 171        qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops;
 172
 173        return misc_register(&qos->pm_qos_power_miscdev);
 174}
 175
 176static int find_pm_qos_object_by_minor(int minor)
 177{
 178        int pm_qos_class;
 179
 180        for (pm_qos_class = 0;
 181                pm_qos_class < PM_QOS_NUM_CLASSES; pm_qos_class++) {
 182                if (minor ==
 183                        pm_qos_array[pm_qos_class]->pm_qos_power_miscdev.minor)
 184                        return pm_qos_class;
 185        }
 186        return -1;
 187}
 188
 189/**
 190 * pm_qos_request - returns current system wide qos expectation
 191 * @pm_qos_class: identification of which qos value is requested
 192 *
 193 * This function returns the current target value in an atomic manner.
 194 */
 195int pm_qos_request(int pm_qos_class)
 196{
 197        unsigned long flags;
 198        int value;
 199
 200        spin_lock_irqsave(&pm_qos_lock, flags);
 201        value = pm_qos_get_value(pm_qos_array[pm_qos_class]);
 202        spin_unlock_irqrestore(&pm_qos_lock, flags);
 203
 204        return value;
 205}
 206EXPORT_SYMBOL_GPL(pm_qos_request);
 207
 208int pm_qos_request_active(struct pm_qos_request_list *req)
 209{
 210        return req->pm_qos_class != 0;
 211}
 212EXPORT_SYMBOL_GPL(pm_qos_request_active);
 213
 214/**
 215 * pm_qos_add_request - inserts new qos request into the list
 216 * @dep: pointer to a preallocated handle
 217 * @pm_qos_class: identifies which list of qos request to use
 218 * @value: defines the qos request
 219 *
 220 * This function inserts a new entry in the pm_qos_class list of requested qos
 221 * performance characteristics.  It recomputes the aggregate QoS expectations
 222 * for the pm_qos_class of parameters and initializes the pm_qos_request_list
 223 * handle.  Caller needs to save this handle for later use in updates and
 224 * removal.
 225 */
 226
 227void pm_qos_add_request(struct pm_qos_request_list *dep,
 228                        int pm_qos_class, s32 value)
 229{
 230        struct pm_qos_object *o =  pm_qos_array[pm_qos_class];
 231        int new_value;
 232
 233        if (pm_qos_request_active(dep)) {
 234                WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n");
 235                return;
 236        }
 237        if (value == PM_QOS_DEFAULT_VALUE)
 238                new_value = o->default_value;
 239        else
 240                new_value = value;
 241        plist_node_init(&dep->list, new_value);
 242        dep->pm_qos_class = pm_qos_class;
 243        update_target(o, &dep->list, 0, PM_QOS_DEFAULT_VALUE);
 244}
 245EXPORT_SYMBOL_GPL(pm_qos_add_request);
 246
 247/**
 248 * pm_qos_update_request - modifies an existing qos request
 249 * @pm_qos_req : handle to list element holding a pm_qos request to use
 250 * @value: defines the qos request
 251 *
 252 * Updates an existing qos request for the pm_qos_class of parameters along
 253 * with updating the target pm_qos_class value.
 254 *
 255 * Attempts are made to make this code callable on hot code paths.
 256 */
 257void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
 258                           s32 new_value)
 259{
 260        s32 temp;
 261        struct pm_qos_object *o;
 262
 263        if (!pm_qos_req) /*guard against callers passing in null */
 264                return;
 265
 266        if (!pm_qos_request_active(pm_qos_req)) {
 267                WARN(1, KERN_ERR "pm_qos_update_request() called for unknown object\n");
 268                return;
 269        }
 270
 271        o = pm_qos_array[pm_qos_req->pm_qos_class];
 272
 273        if (new_value == PM_QOS_DEFAULT_VALUE)
 274                temp = o->default_value;
 275        else
 276                temp = new_value;
 277
 278        if (temp != pm_qos_req->list.prio)
 279                update_target(o, &pm_qos_req->list, 0, temp);
 280}
 281EXPORT_SYMBOL_GPL(pm_qos_update_request);
 282
 283/**
 284 * pm_qos_remove_request - modifies an existing qos request
 285 * @pm_qos_req: handle to request list element
 286 *
 287 * Will remove pm qos request from the list of requests and
 288 * recompute the current target value for the pm_qos_class.  Call this
 289 * on slow code paths.
 290 */
 291void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req)
 292{
 293        struct pm_qos_object *o;
 294
 295        if (pm_qos_req == NULL)
 296                return;
 297                /* silent return to keep pcm code cleaner */
 298
 299        if (!pm_qos_request_active(pm_qos_req)) {
 300                WARN(1, KERN_ERR "pm_qos_remove_request() called for unknown object\n");
 301                return;
 302        }
 303
 304        o = pm_qos_array[pm_qos_req->pm_qos_class];
 305        update_target(o, &pm_qos_req->list, 1, PM_QOS_DEFAULT_VALUE);
 306        memset(pm_qos_req, 0, sizeof(*pm_qos_req));
 307}
 308EXPORT_SYMBOL_GPL(pm_qos_remove_request);
 309
 310/**
 311 * pm_qos_add_notifier - sets notification entry for changes to target value
 312 * @pm_qos_class: identifies which qos target changes should be notified.
 313 * @notifier: notifier block managed by caller.
 314 *
 315 * will register the notifier into a notification chain that gets called
 316 * upon changes to the pm_qos_class target value.
 317 */
 318int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier)
 319{
 320        int retval;
 321
 322        retval = blocking_notifier_chain_register(
 323                        pm_qos_array[pm_qos_class]->notifiers, notifier);
 324
 325        return retval;
 326}
 327EXPORT_SYMBOL_GPL(pm_qos_add_notifier);
 328
 329/**
 330 * pm_qos_remove_notifier - deletes notification entry from chain.
 331 * @pm_qos_class: identifies which qos target changes are notified.
 332 * @notifier: notifier block to be removed.
 333 *
 334 * will remove the notifier from the notification chain that gets called
 335 * upon changes to the pm_qos_class target value.
 336 */
 337int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier)
 338{
 339        int retval;
 340
 341        retval = blocking_notifier_chain_unregister(
 342                        pm_qos_array[pm_qos_class]->notifiers, notifier);
 343
 344        return retval;
 345}
 346EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
 347
 348static int pm_qos_power_open(struct inode *inode, struct file *filp)
 349{
 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               struct pm_qos_request_list *req = kzalloc(sizeof(*req), GFP_KERNEL);
 355                if (!req)
 356                        return -ENOMEM;
 357
 358                pm_qos_add_request(req, pm_qos_class, PM_QOS_DEFAULT_VALUE);
 359                filp->private_data = req;
 360
 361                if (filp->private_data)
 362                        return 0;
 363        }
 364        return -EPERM;
 365}
 366
 367static int pm_qos_power_release(struct inode *inode, struct file *filp)
 368{
 369        struct pm_qos_request_list *req;
 370
 371        req = filp->private_data;
 372        pm_qos_remove_request(req);
 373        kfree(req);
 374
 375        return 0;
 376}
 377
 378
 379static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
 380                size_t count, loff_t *f_pos)
 381{
 382        s32 value;
 383        int x;
 384        char ascii_value[11];
 385        struct pm_qos_request_list *pm_qos_req;
 386
 387        if (count == sizeof(s32)) {
 388                if (copy_from_user(&value, buf, sizeof(s32)))
 389                        return -EFAULT;
 390        } else if (count == 11) { /* len('0x12345678/0') */
 391                if (copy_from_user(ascii_value, buf, 11))
 392                        return -EFAULT;
 393                if (strlen(ascii_value) != 10)
 394                        return -EINVAL;
 395                x = sscanf(ascii_value, "%x", &value);
 396                if (x != 1)
 397                        return -EINVAL;
 398                pr_debug("%s, %d, 0x%x\n", ascii_value, x, value);
 399        } else
 400                return -EINVAL;
 401
 402        pm_qos_req = filp->private_data;
 403        pm_qos_update_request(pm_qos_req, value);
 404
 405        return count;
 406}
 407
 408
 409static int __init pm_qos_power_init(void)
 410{
 411        int ret = 0;
 412
 413        ret = register_pm_qos_misc(&cpu_dma_pm_qos);
 414        if (ret < 0) {
 415                printk(KERN_ERR "pm_qos_param: cpu_dma_latency setup failed\n");
 416                return ret;
 417        }
 418        ret = register_pm_qos_misc(&network_lat_pm_qos);
 419        if (ret < 0) {
 420                printk(KERN_ERR "pm_qos_param: network_latency setup failed\n");
 421                return ret;
 422        }
 423        ret = register_pm_qos_misc(&network_throughput_pm_qos);
 424        if (ret < 0)
 425                printk(KERN_ERR
 426                        "pm_qos_param: network_throughput setup failed\n");
 427
 428        return ret;
 429}
 430
 431late_initcall(pm_qos_power_init);
 432