linux/drivers/base/power/qos.c
<<
>>
Prefs
   1/*
   2 * Devices PM QoS constraints management
   3 *
   4 * Copyright (C) 2011 Texas Instruments, Inc.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 *
  11 * This module exposes the interface to kernel space for specifying
  12 * per-device PM QoS dependencies. It provides infrastructure for registration
  13 * of:
  14 *
  15 * Dependents on a QoS value : register requests
  16 * Watchers of QoS value : get notified when target QoS value changes
  17 *
  18 * This QoS design is best effort based. Dependents register their QoS needs.
  19 * Watchers register to keep track of the current QoS needs of the system.
  20 * Watchers can register different types of notification callbacks:
  21 *  . a per-device notification callback using the dev_pm_qos_*_notifier API.
  22 *    The notification chain data is stored in the per-device constraint
  23 *    data struct.
  24 *  . a system-wide notification callback using the dev_pm_qos_*_global_notifier
  25 *    API. The notification chain data is stored in a static variable.
  26 *
  27 * Note about the per-device constraint data struct allocation:
  28 * . The per-device constraints data struct ptr is tored into the device
  29 *    dev_pm_info.
  30 * . To minimize the data usage by the per-device constraints, the data struct
  31 *   is only allocated at the first call to dev_pm_qos_add_request.
  32 * . The data is later free'd when the device is removed from the system.
  33 *  . A global mutex protects the constraints users from the data being
  34 *     allocated and free'd.
  35 */
  36
  37#include <linux/pm_qos.h>
  38#include <linux/spinlock.h>
  39#include <linux/slab.h>
  40#include <linux/device.h>
  41#include <linux/mutex.h>
  42#include <linux/export.h>
  43
  44
  45static DEFINE_MUTEX(dev_pm_qos_mtx);
  46
  47static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
  48
  49/**
  50 * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
  51 * @dev: Device to get the PM QoS constraint value for.
  52 */
  53s32 dev_pm_qos_read_value(struct device *dev)
  54{
  55        struct pm_qos_constraints *c;
  56        unsigned long flags;
  57        s32 ret = 0;
  58
  59        spin_lock_irqsave(&dev->power.lock, flags);
  60
  61        c = dev->power.constraints;
  62        if (c)
  63                ret = pm_qos_read_value(c);
  64
  65        spin_unlock_irqrestore(&dev->power.lock, flags);
  66
  67        return ret;
  68}
  69
  70/*
  71 * apply_constraint
  72 * @req: constraint request to apply
  73 * @action: action to perform add/update/remove, of type enum pm_qos_req_action
  74 * @value: defines the qos request
  75 *
  76 * Internal function to update the constraints list using the PM QoS core
  77 * code and if needed call the per-device and the global notification
  78 * callbacks
  79 */
  80static int apply_constraint(struct dev_pm_qos_request *req,
  81                            enum pm_qos_req_action action, int value)
  82{
  83        int ret, curr_value;
  84
  85        ret = pm_qos_update_target(req->dev->power.constraints,
  86                                   &req->node, action, value);
  87
  88        if (ret) {
  89                /* Call the global callbacks if needed */
  90                curr_value = pm_qos_read_value(req->dev->power.constraints);
  91                blocking_notifier_call_chain(&dev_pm_notifiers,
  92                                             (unsigned long)curr_value,
  93                                             req);
  94        }
  95
  96        return ret;
  97}
  98
  99/*
 100 * dev_pm_qos_constraints_allocate
 101 * @dev: device to allocate data for
 102 *
 103 * Called at the first call to add_request, for constraint data allocation
 104 * Must be called with the dev_pm_qos_mtx mutex held
 105 */
 106static int dev_pm_qos_constraints_allocate(struct device *dev)
 107{
 108        struct pm_qos_constraints *c;
 109        struct blocking_notifier_head *n;
 110
 111        c = kzalloc(sizeof(*c), GFP_KERNEL);
 112        if (!c)
 113                return -ENOMEM;
 114
 115        n = kzalloc(sizeof(*n), GFP_KERNEL);
 116        if (!n) {
 117                kfree(c);
 118                return -ENOMEM;
 119        }
 120        BLOCKING_INIT_NOTIFIER_HEAD(n);
 121
 122        plist_head_init(&c->list);
 123        c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
 124        c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
 125        c->type = PM_QOS_MIN;
 126        c->notifiers = n;
 127
 128        spin_lock_irq(&dev->power.lock);
 129        dev->power.constraints = c;
 130        spin_unlock_irq(&dev->power.lock);
 131
 132        return 0;
 133}
 134
 135/**
 136 * dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
 137 * @dev: target device
 138 *
 139 * Called from the device PM subsystem during device insertion under
 140 * device_pm_lock().
 141 */
 142void dev_pm_qos_constraints_init(struct device *dev)
 143{
 144        mutex_lock(&dev_pm_qos_mtx);
 145        dev->power.constraints = NULL;
 146        dev->power.power_state = PMSG_ON;
 147        mutex_unlock(&dev_pm_qos_mtx);
 148}
 149
 150/**
 151 * dev_pm_qos_constraints_destroy
 152 * @dev: target device
 153 *
 154 * Called from the device PM subsystem on device removal under device_pm_lock().
 155 */
 156void dev_pm_qos_constraints_destroy(struct device *dev)
 157{
 158        struct dev_pm_qos_request *req, *tmp;
 159        struct pm_qos_constraints *c;
 160
 161        mutex_lock(&dev_pm_qos_mtx);
 162
 163        dev->power.power_state = PMSG_INVALID;
 164        c = dev->power.constraints;
 165        if (!c)
 166                goto out;
 167
 168        /* Flush the constraints list for the device */
 169        plist_for_each_entry_safe(req, tmp, &c->list, node) {
 170                /*
 171                 * Update constraints list and call the notification
 172                 * callbacks if needed
 173                 */
 174                apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
 175                memset(req, 0, sizeof(*req));
 176        }
 177
 178        spin_lock_irq(&dev->power.lock);
 179        dev->power.constraints = NULL;
 180        spin_unlock_irq(&dev->power.lock);
 181
 182        kfree(c->notifiers);
 183        kfree(c);
 184
 185 out:
 186        mutex_unlock(&dev_pm_qos_mtx);
 187}
 188
 189/**
 190 * dev_pm_qos_add_request - inserts new qos request into the list
 191 * @dev: target device for the constraint
 192 * @req: pointer to a preallocated handle
 193 * @value: defines the qos request
 194 *
 195 * This function inserts a new entry in the device constraints list of
 196 * requested qos performance characteristics. It recomputes the aggregate
 197 * QoS expectations of parameters and initializes the dev_pm_qos_request
 198 * handle.  Caller needs to save this handle for later use in updates and
 199 * removal.
 200 *
 201 * Returns 1 if the aggregated constraint value has changed,
 202 * 0 if the aggregated constraint value has not changed,
 203 * -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
 204 * to allocate for data structures, -ENODEV if the device has just been removed
 205 * from the system.
 206 */
 207int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
 208                           s32 value)
 209{
 210        int ret = 0;
 211
 212        if (!dev || !req) /*guard against callers passing in null */
 213                return -EINVAL;
 214
 215        if (WARN(dev_pm_qos_request_active(req),
 216                 "%s() called for already added request\n", __func__))
 217                return -EINVAL;
 218
 219        req->dev = dev;
 220
 221        mutex_lock(&dev_pm_qos_mtx);
 222
 223        if (!dev->power.constraints) {
 224                if (dev->power.power_state.event == PM_EVENT_INVALID) {
 225                        /* The device has been removed from the system. */
 226                        req->dev = NULL;
 227                        ret = -ENODEV;
 228                        goto out;
 229                } else {
 230                        /*
 231                         * Allocate the constraints data on the first call to
 232                         * add_request, i.e. only if the data is not already
 233                         * allocated and if the device has not been removed.
 234                         */
 235                        ret = dev_pm_qos_constraints_allocate(dev);
 236                }
 237        }
 238
 239        if (!ret)
 240                ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
 241
 242 out:
 243        mutex_unlock(&dev_pm_qos_mtx);
 244
 245        return ret;
 246}
 247EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
 248
 249/**
 250 * dev_pm_qos_update_request - modifies an existing qos request
 251 * @req : handle to list element holding a dev_pm_qos request to use
 252 * @new_value: defines the qos request
 253 *
 254 * Updates an existing dev PM qos request along with updating the
 255 * target value.
 256 *
 257 * Attempts are made to make this code callable on hot code paths.
 258 *
 259 * Returns 1 if the aggregated constraint value has changed,
 260 * 0 if the aggregated constraint value has not changed,
 261 * -EINVAL in case of wrong parameters, -ENODEV if the device has been
 262 * removed from the system
 263 */
 264int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
 265                              s32 new_value)
 266{
 267        int ret = 0;
 268
 269        if (!req) /*guard against callers passing in null */
 270                return -EINVAL;
 271
 272        if (WARN(!dev_pm_qos_request_active(req),
 273                 "%s() called for unknown object\n", __func__))
 274                return -EINVAL;
 275
 276        mutex_lock(&dev_pm_qos_mtx);
 277
 278        if (req->dev->power.constraints) {
 279                if (new_value != req->node.prio)
 280                        ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
 281                                               new_value);
 282        } else {
 283                /* Return if the device has been removed */
 284                ret = -ENODEV;
 285        }
 286
 287        mutex_unlock(&dev_pm_qos_mtx);
 288        return ret;
 289}
 290EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
 291
 292/**
 293 * dev_pm_qos_remove_request - modifies an existing qos request
 294 * @req: handle to request list element
 295 *
 296 * Will remove pm qos request from the list of constraints and
 297 * recompute the current target value. Call this on slow code paths.
 298 *
 299 * Returns 1 if the aggregated constraint value has changed,
 300 * 0 if the aggregated constraint value has not changed,
 301 * -EINVAL in case of wrong parameters, -ENODEV if the device has been
 302 * removed from the system
 303 */
 304int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
 305{
 306        int ret = 0;
 307
 308        if (!req) /*guard against callers passing in null */
 309                return -EINVAL;
 310
 311        if (WARN(!dev_pm_qos_request_active(req),
 312                 "%s() called for unknown object\n", __func__))
 313                return -EINVAL;
 314
 315        mutex_lock(&dev_pm_qos_mtx);
 316
 317        if (req->dev->power.constraints) {
 318                ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
 319                                       PM_QOS_DEFAULT_VALUE);
 320                memset(req, 0, sizeof(*req));
 321        } else {
 322                /* Return if the device has been removed */
 323                ret = -ENODEV;
 324        }
 325
 326        mutex_unlock(&dev_pm_qos_mtx);
 327        return ret;
 328}
 329EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
 330
 331/**
 332 * dev_pm_qos_add_notifier - sets notification entry for changes to target value
 333 * of per-device PM QoS constraints
 334 *
 335 * @dev: target device for the constraint
 336 * @notifier: notifier block managed by caller.
 337 *
 338 * Will register the notifier into a notification chain that gets called
 339 * upon changes to the target value for the device.
 340 */
 341int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
 342{
 343        int retval = 0;
 344
 345        mutex_lock(&dev_pm_qos_mtx);
 346
 347        /* Silently return if the constraints object is not present. */
 348        if (dev->power.constraints)
 349                retval = blocking_notifier_chain_register(
 350                                dev->power.constraints->notifiers,
 351                                notifier);
 352
 353        mutex_unlock(&dev_pm_qos_mtx);
 354        return retval;
 355}
 356EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
 357
 358/**
 359 * dev_pm_qos_remove_notifier - deletes notification for changes to target value
 360 * of per-device PM QoS constraints
 361 *
 362 * @dev: target device for the constraint
 363 * @notifier: notifier block to be removed.
 364 *
 365 * Will remove the notifier from the notification chain that gets called
 366 * upon changes to the target value.
 367 */
 368int dev_pm_qos_remove_notifier(struct device *dev,
 369                               struct notifier_block *notifier)
 370{
 371        int retval = 0;
 372
 373        mutex_lock(&dev_pm_qos_mtx);
 374
 375        /* Silently return if the constraints object is not present. */
 376        if (dev->power.constraints)
 377                retval = blocking_notifier_chain_unregister(
 378                                dev->power.constraints->notifiers,
 379                                notifier);
 380
 381        mutex_unlock(&dev_pm_qos_mtx);
 382        return retval;
 383}
 384EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
 385
 386/**
 387 * dev_pm_qos_add_global_notifier - sets notification entry for changes to
 388 * target value of the PM QoS constraints for any device
 389 *
 390 * @notifier: notifier block managed by caller.
 391 *
 392 * Will register the notifier into a notification chain that gets called
 393 * upon changes to the target value for any device.
 394 */
 395int dev_pm_qos_add_global_notifier(struct notifier_block *notifier)
 396{
 397        return blocking_notifier_chain_register(&dev_pm_notifiers, notifier);
 398}
 399EXPORT_SYMBOL_GPL(dev_pm_qos_add_global_notifier);
 400
 401/**
 402 * dev_pm_qos_remove_global_notifier - deletes notification for changes to
 403 * target value of PM QoS constraints for any device
 404 *
 405 * @notifier: notifier block to be removed.
 406 *
 407 * Will remove the notifier from the notification chain that gets called
 408 * upon changes to the target value for any device.
 409 */
 410int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier)
 411{
 412        return blocking_notifier_chain_unregister(&dev_pm_notifiers, notifier);
 413}
 414EXPORT_SYMBOL_GPL(dev_pm_qos_remove_global_notifier);
 415