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