mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
Merge branch 'pm-qos' into pm-for-linus
* pm-qos: PM / QoS: Update Documentation for the pm_qos and dev_pm_qos frameworks PM / QoS: Add function dev_pm_qos_read_value() (v3) PM QoS: Add global notification mechanism for device constraints PM QoS: Implement per-device PM QoS constraints PM QoS: Generalize and export constraints management code PM QoS: Reorganize data structs PM QoS: Code reorganization PM QoS: Minor clean-ups PM QoS: Move and rename the implementation files
This commit is contained in:
commit
9696cc9007
@ -4,14 +4,19 @@ This interface provides a kernel and user mode interface for registering
|
||||
performance expectations by drivers, subsystems and user space applications on
|
||||
one of the parameters.
|
||||
|
||||
Currently we have {cpu_dma_latency, network_latency, network_throughput} as the
|
||||
initial set of pm_qos parameters.
|
||||
Two different PM QoS frameworks are available:
|
||||
1. PM QoS classes for cpu_dma_latency, network_latency, network_throughput.
|
||||
2. the per-device PM QoS framework provides the API to manage the per-device latency
|
||||
constraints.
|
||||
|
||||
Each parameters have defined units:
|
||||
* latency: usec
|
||||
* timeout: usec
|
||||
* throughput: kbs (kilo bit / sec)
|
||||
|
||||
|
||||
1. PM QoS framework
|
||||
|
||||
The infrastructure exposes multiple misc device nodes one per implemented
|
||||
parameter. The set of parameters implement is defined by pm_qos_power_init()
|
||||
and pm_qos_params.h. This is done because having the available parameters
|
||||
@ -23,14 +28,18 @@ an aggregated target value. The aggregated target value is updated with
|
||||
changes to the request list or elements of the list. Typically the
|
||||
aggregated target value is simply the max or min of the request values held
|
||||
in the parameter list elements.
|
||||
Note: the aggregated target value is implemented as an atomic variable so that
|
||||
reading the aggregated value does not require any locking mechanism.
|
||||
|
||||
|
||||
From kernel mode the use of this interface is simple:
|
||||
|
||||
handle = pm_qos_add_request(param_class, target_value):
|
||||
Will insert an element into the list for that identified PM_QOS class with the
|
||||
void pm_qos_add_request(handle, param_class, target_value):
|
||||
Will insert an element into the list for that identified PM QoS class with the
|
||||
target value. Upon change to this list the new target is recomputed and any
|
||||
registered notifiers are called only if the target value is now different.
|
||||
Clients of pm_qos need to save the returned handle.
|
||||
Clients of pm_qos need to save the returned handle for future use in other
|
||||
pm_qos API functions.
|
||||
|
||||
void pm_qos_update_request(handle, new_target_value):
|
||||
Will update the list element pointed to by the handle with the new target value
|
||||
@ -42,6 +51,20 @@ Will remove the element. After removal it will update the aggregate target and
|
||||
call the notification tree if the target was changed as a result of removing
|
||||
the request.
|
||||
|
||||
int pm_qos_request(param_class):
|
||||
Returns the aggregated value for a given PM QoS class.
|
||||
|
||||
int pm_qos_request_active(handle):
|
||||
Returns if the request is still active, i.e. it has not been removed from a
|
||||
PM QoS class constraints list.
|
||||
|
||||
int pm_qos_add_notifier(param_class, notifier):
|
||||
Adds a notification callback function to the PM QoS class. The callback is
|
||||
called when the aggregated value for the PM QoS class is changed.
|
||||
|
||||
int pm_qos_remove_notifier(int param_class, notifier):
|
||||
Removes the notification callback function for the PM QoS class.
|
||||
|
||||
|
||||
From user mode:
|
||||
Only processes can register a pm_qos request. To provide for automatic
|
||||
@ -63,4 +86,63 @@ To remove the user mode request for a target value simply close the device
|
||||
node.
|
||||
|
||||
|
||||
2. PM QoS per-device latency framework
|
||||
|
||||
For each device a list of performance requests is maintained along with
|
||||
an aggregated target value. The aggregated target value is updated with
|
||||
changes to the request list or elements of the list. Typically the
|
||||
aggregated target value is simply the max or min of the request values held
|
||||
in the parameter list elements.
|
||||
Note: the aggregated target value is implemented as an atomic variable so that
|
||||
reading the aggregated value does not require any locking mechanism.
|
||||
|
||||
|
||||
From kernel mode the use of this interface is the following:
|
||||
|
||||
int dev_pm_qos_add_request(device, handle, value):
|
||||
Will insert an element into the list for that identified device with the
|
||||
target value. Upon change to this list the new target is recomputed and any
|
||||
registered notifiers are called only if the target value is now different.
|
||||
Clients of dev_pm_qos need to save the handle for future use in other
|
||||
dev_pm_qos API functions.
|
||||
|
||||
int dev_pm_qos_update_request(handle, new_value):
|
||||
Will update the list element pointed to by the handle with the new target value
|
||||
and recompute the new aggregated target, calling the notification trees if the
|
||||
target is changed.
|
||||
|
||||
int dev_pm_qos_remove_request(handle):
|
||||
Will remove the element. After removal it will update the aggregate target and
|
||||
call the notification trees if the target was changed as a result of removing
|
||||
the request.
|
||||
|
||||
s32 dev_pm_qos_read_value(device):
|
||||
Returns the aggregated value for a given device's constraints list.
|
||||
|
||||
|
||||
Notification mechanisms:
|
||||
The per-device PM QoS framework has 2 different and distinct notification trees:
|
||||
a per-device notification tree and a global notification tree.
|
||||
|
||||
int dev_pm_qos_add_notifier(device, notifier):
|
||||
Adds a notification callback function for the device.
|
||||
The callback is called when the aggregated value of the device constraints list
|
||||
is changed.
|
||||
|
||||
int dev_pm_qos_remove_notifier(device, notifier):
|
||||
Removes the notification callback function for the device.
|
||||
|
||||
int dev_pm_qos_add_global_notifier(notifier):
|
||||
Adds a notification callback function in the global notification tree of the
|
||||
framework.
|
||||
The callback is called when the aggregated value for any device is changed.
|
||||
|
||||
int dev_pm_qos_remove_global_notifier(notifier):
|
||||
Removes the notification callback function from the global notification tree
|
||||
of the framework.
|
||||
|
||||
|
||||
From user mode:
|
||||
No API for user space access to the per-device latency constraints is provided
|
||||
yet - still under discussion.
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/string.h>
|
||||
|
@ -37,7 +37,7 @@
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/sched.h> /* need_resched() */
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/irqflags.h>
|
||||
|
@ -1,4 +1,4 @@
|
||||
obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o
|
||||
obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o
|
||||
obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
|
||||
obj-$(CONFIG_PM_RUNTIME) += runtime.o
|
||||
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
|
||||
@ -6,4 +6,4 @@ obj-$(CONFIG_PM_OPP) += opp.o
|
||||
obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o
|
||||
obj-$(CONFIG_HAVE_CLK) += clock_ops.o
|
||||
|
||||
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
|
||||
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
|
||||
|
@ -65,6 +65,7 @@ void device_pm_init(struct device *dev)
|
||||
spin_lock_init(&dev->power.lock);
|
||||
pm_runtime_init(dev);
|
||||
INIT_LIST_HEAD(&dev->power.entry);
|
||||
dev->power.power_state = PMSG_INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -96,6 +97,7 @@ void device_pm_add(struct device *dev)
|
||||
dev_warn(dev, "parent %s should not be sleeping\n",
|
||||
dev_name(dev->parent));
|
||||
list_add_tail(&dev->power.entry, &dpm_list);
|
||||
dev_pm_qos_constraints_init(dev);
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
}
|
||||
|
||||
@ -109,6 +111,7 @@ void device_pm_remove(struct device *dev)
|
||||
dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
|
||||
complete_all(&dev->power.completion);
|
||||
mutex_lock(&dpm_list_mtx);
|
||||
dev_pm_qos_constraints_destroy(dev);
|
||||
list_del_init(&dev->power.entry);
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
device_wakeup_disable(dev);
|
||||
|
@ -1,3 +1,5 @@
|
||||
#include <linux/pm_qos.h>
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
extern void pm_runtime_init(struct device *dev);
|
||||
@ -35,15 +37,21 @@ extern void device_pm_move_last(struct device *);
|
||||
static inline void device_pm_init(struct device *dev)
|
||||
{
|
||||
spin_lock_init(&dev->power.lock);
|
||||
dev->power.power_state = PMSG_INVALID;
|
||||
pm_runtime_init(dev);
|
||||
}
|
||||
|
||||
static inline void device_pm_add(struct device *dev)
|
||||
{
|
||||
dev_pm_qos_constraints_init(dev);
|
||||
}
|
||||
|
||||
static inline void device_pm_remove(struct device *dev)
|
||||
{
|
||||
dev_pm_qos_constraints_destroy(dev);
|
||||
pm_runtime_remove(dev);
|
||||
}
|
||||
|
||||
static inline void device_pm_add(struct device *dev) {}
|
||||
static inline void device_pm_move_before(struct device *deva,
|
||||
struct device *devb) {}
|
||||
static inline void device_pm_move_after(struct device *deva,
|
||||
|
419
drivers/base/power/qos.c
Normal file
419
drivers/base/power/qos.c
Normal file
@ -0,0 +1,419 @@
|
||||
/*
|
||||
* Devices PM QoS constraints management
|
||||
*
|
||||
* Copyright (C) 2011 Texas Instruments, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*
|
||||
* This module exposes the interface to kernel space for specifying
|
||||
* per-device PM QoS dependencies. It provides infrastructure for registration
|
||||
* of:
|
||||
*
|
||||
* Dependents on a QoS value : register requests
|
||||
* Watchers of QoS value : get notified when target QoS value changes
|
||||
*
|
||||
* This QoS design is best effort based. Dependents register their QoS needs.
|
||||
* Watchers register to keep track of the current QoS needs of the system.
|
||||
* Watchers can register different types of notification callbacks:
|
||||
* . a per-device notification callback using the dev_pm_qos_*_notifier API.
|
||||
* The notification chain data is stored in the per-device constraint
|
||||
* data struct.
|
||||
* . a system-wide notification callback using the dev_pm_qos_*_global_notifier
|
||||
* API. The notification chain data is stored in a static variable.
|
||||
*
|
||||
* Note about the per-device constraint data struct allocation:
|
||||
* . The per-device constraints data struct ptr is tored into the device
|
||||
* dev_pm_info.
|
||||
* . To minimize the data usage by the per-device constraints, the data struct
|
||||
* is only allocated at the first call to dev_pm_qos_add_request.
|
||||
* . The data is later free'd when the device is removed from the system.
|
||||
* . A global mutex protects the constraints users from the data being
|
||||
* allocated and free'd.
|
||||
*/
|
||||
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
|
||||
static DEFINE_MUTEX(dev_pm_qos_mtx);
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
|
||||
|
||||
/**
|
||||
* dev_pm_qos_read_value - Get PM QoS constraint for a given device.
|
||||
* @dev: Device to get the PM QoS constraint value for.
|
||||
*/
|
||||
s32 dev_pm_qos_read_value(struct device *dev)
|
||||
{
|
||||
struct pm_qos_constraints *c;
|
||||
unsigned long flags;
|
||||
s32 ret = 0;
|
||||
|
||||
spin_lock_irqsave(&dev->power.lock, flags);
|
||||
|
||||
c = dev->power.constraints;
|
||||
if (c)
|
||||
ret = pm_qos_read_value(c);
|
||||
|
||||
spin_unlock_irqrestore(&dev->power.lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* apply_constraint
|
||||
* @req: constraint request to apply
|
||||
* @action: action to perform add/update/remove, of type enum pm_qos_req_action
|
||||
* @value: defines the qos request
|
||||
*
|
||||
* Internal function to update the constraints list using the PM QoS core
|
||||
* code and if needed call the per-device and the global notification
|
||||
* callbacks
|
||||
*/
|
||||
static int apply_constraint(struct dev_pm_qos_request *req,
|
||||
enum pm_qos_req_action action, int value)
|
||||
{
|
||||
int ret, curr_value;
|
||||
|
||||
ret = pm_qos_update_target(req->dev->power.constraints,
|
||||
&req->node, action, value);
|
||||
|
||||
if (ret) {
|
||||
/* Call the global callbacks if needed */
|
||||
curr_value = pm_qos_read_value(req->dev->power.constraints);
|
||||
blocking_notifier_call_chain(&dev_pm_notifiers,
|
||||
(unsigned long)curr_value,
|
||||
req);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* dev_pm_qos_constraints_allocate
|
||||
* @dev: device to allocate data for
|
||||
*
|
||||
* Called at the first call to add_request, for constraint data allocation
|
||||
* Must be called with the dev_pm_qos_mtx mutex held
|
||||
*/
|
||||
static int dev_pm_qos_constraints_allocate(struct device *dev)
|
||||
{
|
||||
struct pm_qos_constraints *c;
|
||||
struct blocking_notifier_head *n;
|
||||
|
||||
c = kzalloc(sizeof(*c), GFP_KERNEL);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
n = kzalloc(sizeof(*n), GFP_KERNEL);
|
||||
if (!n) {
|
||||
kfree(c);
|
||||
return -ENOMEM;
|
||||
}
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(n);
|
||||
|
||||
plist_head_init(&c->list);
|
||||
c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
|
||||
c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
|
||||
c->type = PM_QOS_MIN;
|
||||
c->notifiers = n;
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
dev->power.constraints = c;
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dev_pm_qos_constraints_init - Initalize device's PM QoS constraints pointer.
|
||||
* @dev: target device
|
||||
*
|
||||
* Called from the device PM subsystem during device insertion under
|
||||
* device_pm_lock().
|
||||
*/
|
||||
void dev_pm_qos_constraints_init(struct device *dev)
|
||||
{
|
||||
mutex_lock(&dev_pm_qos_mtx);
|
||||
dev->power.constraints = NULL;
|
||||
dev->power.power_state = PMSG_ON;
|
||||
mutex_unlock(&dev_pm_qos_mtx);
|
||||
}
|
||||
|
||||
/**
|
||||
* dev_pm_qos_constraints_destroy
|
||||
* @dev: target device
|
||||
*
|
||||
* Called from the device PM subsystem on device removal under device_pm_lock().
|
||||
*/
|
||||
void dev_pm_qos_constraints_destroy(struct device *dev)
|
||||
{
|
||||
struct dev_pm_qos_request *req, *tmp;
|
||||
struct pm_qos_constraints *c;
|
||||
|
||||
mutex_lock(&dev_pm_qos_mtx);
|
||||
|
||||
dev->power.power_state = PMSG_INVALID;
|
||||
c = dev->power.constraints;
|
||||
if (!c)
|
||||
goto out;
|
||||
|
||||
/* Flush the constraints list for the device */
|
||||
plist_for_each_entry_safe(req, tmp, &c->list, node) {
|
||||
/*
|
||||
* Update constraints list and call the notification
|
||||
* callbacks if needed
|
||||
*/
|
||||
apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
|
||||
memset(req, 0, sizeof(*req));
|
||||
}
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
dev->power.constraints = NULL;
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
|
||||
kfree(c->notifiers);
|
||||
kfree(c);
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev_pm_qos_mtx);
|
||||
}
|
||||
|
||||
/**
|
||||
* dev_pm_qos_add_request - inserts new qos request into the list
|
||||
* @dev: target device for the constraint
|
||||
* @req: pointer to a preallocated handle
|
||||
* @value: defines the qos request
|
||||
*
|
||||
* This function inserts a new entry in the device constraints list of
|
||||
* requested qos performance characteristics. It recomputes the aggregate
|
||||
* QoS expectations of parameters and initializes the dev_pm_qos_request
|
||||
* handle. Caller needs to save this handle for later use in updates and
|
||||
* removal.
|
||||
*
|
||||
* Returns 1 if the aggregated constraint value has changed,
|
||||
* 0 if the aggregated constraint value has not changed,
|
||||
* -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
|
||||
* to allocate for data structures, -ENODEV if the device has just been removed
|
||||
* from the system.
|
||||
*/
|
||||
int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
|
||||
s32 value)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!dev || !req) /*guard against callers passing in null */
|
||||
return -EINVAL;
|
||||
|
||||
if (dev_pm_qos_request_active(req)) {
|
||||
WARN(1, KERN_ERR "dev_pm_qos_add_request() called for already "
|
||||
"added request\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
req->dev = dev;
|
||||
|
||||
mutex_lock(&dev_pm_qos_mtx);
|
||||
|
||||
if (!dev->power.constraints) {
|
||||
if (dev->power.power_state.event == PM_EVENT_INVALID) {
|
||||
/* The device has been removed from the system. */
|
||||
req->dev = NULL;
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
} else {
|
||||
/*
|
||||
* Allocate the constraints data on the first call to
|
||||
* add_request, i.e. only if the data is not already
|
||||
* allocated and if the device has not been removed.
|
||||
*/
|
||||
ret = dev_pm_qos_constraints_allocate(dev);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
ret = apply_constraint(req, PM_QOS_ADD_REQ, value);
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev_pm_qos_mtx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
|
||||
|
||||
/**
|
||||
* dev_pm_qos_update_request - modifies an existing qos request
|
||||
* @req : handle to list element holding a dev_pm_qos request to use
|
||||
* @new_value: defines the qos request
|
||||
*
|
||||
* Updates an existing dev PM qos request along with updating the
|
||||
* target value.
|
||||
*
|
||||
* Attempts are made to make this code callable on hot code paths.
|
||||
*
|
||||
* Returns 1 if the aggregated constraint value has changed,
|
||||
* 0 if the aggregated constraint value has not changed,
|
||||
* -EINVAL in case of wrong parameters, -ENODEV if the device has been
|
||||
* removed from the system
|
||||
*/
|
||||
int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
|
||||
s32 new_value)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!req) /*guard against callers passing in null */
|
||||
return -EINVAL;
|
||||
|
||||
if (!dev_pm_qos_request_active(req)) {
|
||||
WARN(1, KERN_ERR "dev_pm_qos_update_request() called for "
|
||||
"unknown object\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&dev_pm_qos_mtx);
|
||||
|
||||
if (req->dev->power.constraints) {
|
||||
if (new_value != req->node.prio)
|
||||
ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
|
||||
new_value);
|
||||
} else {
|
||||
/* Return if the device has been removed */
|
||||
ret = -ENODEV;
|
||||
}
|
||||
|
||||
mutex_unlock(&dev_pm_qos_mtx);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
|
||||
|
||||
/**
|
||||
* dev_pm_qos_remove_request - modifies an existing qos request
|
||||
* @req: handle to request list element
|
||||
*
|
||||
* Will remove pm qos request from the list of constraints and
|
||||
* recompute the current target value. Call this on slow code paths.
|
||||
*
|
||||
* Returns 1 if the aggregated constraint value has changed,
|
||||
* 0 if the aggregated constraint value has not changed,
|
||||
* -EINVAL in case of wrong parameters, -ENODEV if the device has been
|
||||
* removed from the system
|
||||
*/
|
||||
int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!req) /*guard against callers passing in null */
|
||||
return -EINVAL;
|
||||
|
||||
if (!dev_pm_qos_request_active(req)) {
|
||||
WARN(1, KERN_ERR "dev_pm_qos_remove_request() called for "
|
||||
"unknown object\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&dev_pm_qos_mtx);
|
||||
|
||||
if (req->dev->power.constraints) {
|
||||
ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
|
||||
PM_QOS_DEFAULT_VALUE);
|
||||
memset(req, 0, sizeof(*req));
|
||||
} else {
|
||||
/* Return if the device has been removed */
|
||||
ret = -ENODEV;
|
||||
}
|
||||
|
||||
mutex_unlock(&dev_pm_qos_mtx);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
|
||||
|
||||
/**
|
||||
* dev_pm_qos_add_notifier - sets notification entry for changes to target value
|
||||
* of per-device PM QoS constraints
|
||||
*
|
||||
* @dev: target device for the constraint
|
||||
* @notifier: notifier block managed by caller.
|
||||
*
|
||||
* Will register the notifier into a notification chain that gets called
|
||||
* upon changes to the target value for the device.
|
||||
*/
|
||||
int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
mutex_lock(&dev_pm_qos_mtx);
|
||||
|
||||
/* Silently return if the constraints object is not present. */
|
||||
if (dev->power.constraints)
|
||||
retval = blocking_notifier_chain_register(
|
||||
dev->power.constraints->notifiers,
|
||||
notifier);
|
||||
|
||||
mutex_unlock(&dev_pm_qos_mtx);
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
|
||||
|
||||
/**
|
||||
* dev_pm_qos_remove_notifier - deletes notification for changes to target value
|
||||
* of per-device PM QoS constraints
|
||||
*
|
||||
* @dev: target device for the constraint
|
||||
* @notifier: notifier block to be removed.
|
||||
*
|
||||
* Will remove the notifier from the notification chain that gets called
|
||||
* upon changes to the target value.
|
||||
*/
|
||||
int dev_pm_qos_remove_notifier(struct device *dev,
|
||||
struct notifier_block *notifier)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
mutex_lock(&dev_pm_qos_mtx);
|
||||
|
||||
/* Silently return if the constraints object is not present. */
|
||||
if (dev->power.constraints)
|
||||
retval = blocking_notifier_chain_unregister(
|
||||
dev->power.constraints->notifiers,
|
||||
notifier);
|
||||
|
||||
mutex_unlock(&dev_pm_qos_mtx);
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
|
||||
|
||||
/**
|
||||
* dev_pm_qos_add_global_notifier - sets notification entry for changes to
|
||||
* target value of the PM QoS constraints for any device
|
||||
*
|
||||
* @notifier: notifier block managed by caller.
|
||||
*
|
||||
* Will register the notifier into a notification chain that gets called
|
||||
* upon changes to the target value for any device.
|
||||
*/
|
||||
int dev_pm_qos_add_global_notifier(struct notifier_block *notifier)
|
||||
{
|
||||
return blocking_notifier_chain_register(&dev_pm_notifiers, notifier);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_qos_add_global_notifier);
|
||||
|
||||
/**
|
||||
* dev_pm_qos_remove_global_notifier - deletes notification for changes to
|
||||
* target value of PM QoS constraints for any device
|
||||
*
|
||||
* @notifier: notifier block to be removed.
|
||||
*
|
||||
* Will remove the notifier from the notification chain that gets called
|
||||
* upon changes to the target value for any device.
|
||||
*/
|
||||
int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier)
|
||||
{
|
||||
return blocking_notifier_chain_unregister(&dev_pm_notifiers, notifier);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_qos_remove_global_notifier);
|
@ -12,7 +12,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/ktime.h>
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/hrtimer.h>
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include <media/videobuf-dma-sg.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/via-core.h>
|
||||
#include <linux/via-gpio.h>
|
||||
#include <linux/via_i2c.h>
|
||||
@ -69,7 +69,7 @@ struct via_camera {
|
||||
struct mutex lock;
|
||||
enum viacam_opstate opstate;
|
||||
unsigned long flags;
|
||||
struct pm_qos_request_list qos_request;
|
||||
struct pm_qos_request qos_request;
|
||||
/*
|
||||
* GPIO info for power/reset management
|
||||
*/
|
||||
|
@ -47,7 +47,7 @@
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/aer.h>
|
||||
#include <linux/prefetch.h>
|
||||
|
@ -161,7 +161,7 @@ that only one external action is invoked at a time.
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <linux/pm_qos.h>
|
||||
|
||||
#include <net/lib80211.h>
|
||||
|
||||
@ -174,7 +174,7 @@ that only one external action is invoked at a time.
|
||||
#define DRV_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver"
|
||||
#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation"
|
||||
|
||||
static struct pm_qos_request_list ipw2100_pm_qos_req;
|
||||
static struct pm_qos_request ipw2100_pm_qos_req;
|
||||
|
||||
/* Debugging stuff */
|
||||
#ifdef CONFIG_IPW2100_DEBUG
|
||||
|
@ -31,7 +31,7 @@
|
||||
#include <linux/if_link.h>
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/atomic.h>
|
||||
@ -964,7 +964,7 @@ struct net_device {
|
||||
*/
|
||||
char name[IFNAMSIZ];
|
||||
|
||||
struct pm_qos_request_list pm_qos_req;
|
||||
struct pm_qos_request pm_qos_req;
|
||||
|
||||
/* device name hash chain */
|
||||
struct hlist_node name_hlist;
|
||||
|
@ -326,6 +326,7 @@ extern struct dev_pm_ops generic_subsys_pm_ops;
|
||||
* requested by a driver.
|
||||
*/
|
||||
|
||||
#define PM_EVENT_INVALID (-1)
|
||||
#define PM_EVENT_ON 0x0000
|
||||
#define PM_EVENT_FREEZE 0x0001
|
||||
#define PM_EVENT_SUSPEND 0x0002
|
||||
@ -346,6 +347,7 @@ extern struct dev_pm_ops generic_subsys_pm_ops;
|
||||
#define PM_EVENT_AUTO_SUSPEND (PM_EVENT_AUTO | PM_EVENT_SUSPEND)
|
||||
#define PM_EVENT_AUTO_RESUME (PM_EVENT_AUTO | PM_EVENT_RESUME)
|
||||
|
||||
#define PMSG_INVALID ((struct pm_message){ .event = PM_EVENT_INVALID, })
|
||||
#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, })
|
||||
#define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, })
|
||||
#define PMSG_QUIESCE ((struct pm_message){ .event = PM_EVENT_QUIESCE, })
|
||||
@ -481,6 +483,7 @@ struct dev_pm_info {
|
||||
unsigned long accounting_timestamp;
|
||||
#endif
|
||||
struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */
|
||||
struct pm_qos_constraints *constraints;
|
||||
};
|
||||
|
||||
extern void update_pm_runtime_accounting(struct device *dev);
|
||||
|
155
include/linux/pm_qos.h
Normal file
155
include/linux/pm_qos.h
Normal file
@ -0,0 +1,155 @@
|
||||
#ifndef _LINUX_PM_QOS_H
|
||||
#define _LINUX_PM_QOS_H
|
||||
/* interface for the pm_qos_power infrastructure of the linux kernel.
|
||||
*
|
||||
* Mark Gross <mgross@linux.intel.com>
|
||||
*/
|
||||
#include <linux/plist.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#define PM_QOS_RESERVED 0
|
||||
#define PM_QOS_CPU_DMA_LATENCY 1
|
||||
#define PM_QOS_NETWORK_LATENCY 2
|
||||
#define PM_QOS_NETWORK_THROUGHPUT 3
|
||||
|
||||
#define PM_QOS_NUM_CLASSES 4
|
||||
#define PM_QOS_DEFAULT_VALUE -1
|
||||
|
||||
#define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
|
||||
#define PM_QOS_NETWORK_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
|
||||
#define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE 0
|
||||
#define PM_QOS_DEV_LAT_DEFAULT_VALUE 0
|
||||
|
||||
struct pm_qos_request {
|
||||
struct plist_node node;
|
||||
int pm_qos_class;
|
||||
};
|
||||
|
||||
struct dev_pm_qos_request {
|
||||
struct plist_node node;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
enum pm_qos_type {
|
||||
PM_QOS_UNITIALIZED,
|
||||
PM_QOS_MAX, /* return the largest value */
|
||||
PM_QOS_MIN /* return the smallest value */
|
||||
};
|
||||
|
||||
/*
|
||||
* Note: The lockless read path depends on the CPU accessing
|
||||
* target_value atomically. Atomic access is only guaranteed on all CPU
|
||||
* types linux supports for 32 bit quantites
|
||||
*/
|
||||
struct pm_qos_constraints {
|
||||
struct plist_head list;
|
||||
s32 target_value; /* Do not change to 64 bit */
|
||||
s32 default_value;
|
||||
enum pm_qos_type type;
|
||||
struct blocking_notifier_head *notifiers;
|
||||
};
|
||||
|
||||
/* Action requested to pm_qos_update_target */
|
||||
enum pm_qos_req_action {
|
||||
PM_QOS_ADD_REQ, /* Add a new request */
|
||||
PM_QOS_UPDATE_REQ, /* Update an existing request */
|
||||
PM_QOS_REMOVE_REQ /* Remove an existing request */
|
||||
};
|
||||
|
||||
static inline int dev_pm_qos_request_active(struct dev_pm_qos_request *req)
|
||||
{
|
||||
return req->dev != 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
|
||||
enum pm_qos_req_action action, int value);
|
||||
void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class,
|
||||
s32 value);
|
||||
void pm_qos_update_request(struct pm_qos_request *req,
|
||||
s32 new_value);
|
||||
void pm_qos_remove_request(struct pm_qos_request *req);
|
||||
|
||||
int pm_qos_request(int pm_qos_class);
|
||||
int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
|
||||
int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
|
||||
int pm_qos_request_active(struct pm_qos_request *req);
|
||||
s32 pm_qos_read_value(struct pm_qos_constraints *c);
|
||||
|
||||
s32 dev_pm_qos_read_value(struct device *dev);
|
||||
int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
|
||||
s32 value);
|
||||
int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
|
||||
int dev_pm_qos_remove_request(struct dev_pm_qos_request *req);
|
||||
int dev_pm_qos_add_notifier(struct device *dev,
|
||||
struct notifier_block *notifier);
|
||||
int dev_pm_qos_remove_notifier(struct device *dev,
|
||||
struct notifier_block *notifier);
|
||||
int dev_pm_qos_add_global_notifier(struct notifier_block *notifier);
|
||||
int dev_pm_qos_remove_global_notifier(struct notifier_block *notifier);
|
||||
void dev_pm_qos_constraints_init(struct device *dev);
|
||||
void dev_pm_qos_constraints_destroy(struct device *dev);
|
||||
#else
|
||||
static inline int pm_qos_update_target(struct pm_qos_constraints *c,
|
||||
struct plist_node *node,
|
||||
enum pm_qos_req_action action,
|
||||
int value)
|
||||
{ return 0; }
|
||||
static inline void pm_qos_add_request(struct pm_qos_request *req,
|
||||
int pm_qos_class, s32 value)
|
||||
{ return; }
|
||||
static inline void pm_qos_update_request(struct pm_qos_request *req,
|
||||
s32 new_value)
|
||||
{ return; }
|
||||
static inline void pm_qos_remove_request(struct pm_qos_request *req)
|
||||
{ return; }
|
||||
|
||||
static inline int pm_qos_request(int pm_qos_class)
|
||||
{ return 0; }
|
||||
static inline int pm_qos_add_notifier(int pm_qos_class,
|
||||
struct notifier_block *notifier)
|
||||
{ return 0; }
|
||||
static inline int pm_qos_remove_notifier(int pm_qos_class,
|
||||
struct notifier_block *notifier)
|
||||
{ return 0; }
|
||||
static inline int pm_qos_request_active(struct pm_qos_request *req)
|
||||
{ return 0; }
|
||||
static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
|
||||
{ return 0; }
|
||||
|
||||
static inline s32 dev_pm_qos_read_value(struct device *dev)
|
||||
{ return 0; }
|
||||
static inline int dev_pm_qos_add_request(struct device *dev,
|
||||
struct dev_pm_qos_request *req,
|
||||
s32 value)
|
||||
{ return 0; }
|
||||
static inline int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
|
||||
s32 new_value)
|
||||
{ return 0; }
|
||||
static inline int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
|
||||
{ return 0; }
|
||||
static inline int dev_pm_qos_add_notifier(struct device *dev,
|
||||
struct notifier_block *notifier)
|
||||
{ return 0; }
|
||||
static inline int dev_pm_qos_remove_notifier(struct device *dev,
|
||||
struct notifier_block *notifier)
|
||||
{ return 0; }
|
||||
static inline int dev_pm_qos_add_global_notifier(
|
||||
struct notifier_block *notifier)
|
||||
{ return 0; }
|
||||
static inline int dev_pm_qos_remove_global_notifier(
|
||||
struct notifier_block *notifier)
|
||||
{ return 0; }
|
||||
static inline void dev_pm_qos_constraints_init(struct device *dev)
|
||||
{
|
||||
dev->power.power_state = PMSG_ON;
|
||||
}
|
||||
static inline void dev_pm_qos_constraints_destroy(struct device *dev)
|
||||
{
|
||||
dev->power.power_state = PMSG_INVALID;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,38 +0,0 @@
|
||||
#ifndef _LINUX_PM_QOS_PARAMS_H
|
||||
#define _LINUX_PM_QOS_PARAMS_H
|
||||
/* interface for the pm_qos_power infrastructure of the linux kernel.
|
||||
*
|
||||
* Mark Gross <mgross@linux.intel.com>
|
||||
*/
|
||||
#include <linux/plist.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/miscdevice.h>
|
||||
|
||||
#define PM_QOS_RESERVED 0
|
||||
#define PM_QOS_CPU_DMA_LATENCY 1
|
||||
#define PM_QOS_NETWORK_LATENCY 2
|
||||
#define PM_QOS_NETWORK_THROUGHPUT 3
|
||||
|
||||
#define PM_QOS_NUM_CLASSES 4
|
||||
#define PM_QOS_DEFAULT_VALUE -1
|
||||
|
||||
#define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
|
||||
#define PM_QOS_NETWORK_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC)
|
||||
#define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE 0
|
||||
|
||||
struct pm_qos_request_list {
|
||||
struct plist_node list;
|
||||
int pm_qos_class;
|
||||
};
|
||||
|
||||
void pm_qos_add_request(struct pm_qos_request_list *l, int pm_qos_class, s32 value);
|
||||
void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
|
||||
s32 new_value);
|
||||
void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req);
|
||||
|
||||
int pm_qos_request(int pm_qos_class);
|
||||
int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier);
|
||||
int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
|
||||
int pm_qos_request_active(struct pm_qos_request_list *req);
|
||||
|
||||
#endif
|
@ -29,7 +29,7 @@
|
||||
#include <linux/poll.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <linux/pm_qos.h>
|
||||
|
||||
#define snd_pcm_substream_chip(substream) ((substream)->private_data)
|
||||
#define snd_pcm_chip(pcm) ((pcm)->private_data)
|
||||
@ -373,7 +373,7 @@ struct snd_pcm_substream {
|
||||
int number;
|
||||
char name[32]; /* substream name */
|
||||
int stream; /* stream (direction) */
|
||||
struct pm_qos_request_list latency_pm_qos_req; /* pm_qos request */
|
||||
struct pm_qos_request latency_pm_qos_req; /* pm_qos request */
|
||||
size_t buffer_bytes_max; /* limit ring buffer size */
|
||||
struct snd_dma_buffer dma_buffer;
|
||||
unsigned int dma_buf_id;
|
||||
|
@ -9,7 +9,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o \
|
||||
rcupdate.o extable.o params.o posix-timers.o \
|
||||
kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
|
||||
hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
|
||||
notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o \
|
||||
notifier.o ksysfs.o sched_clock.o cred.o \
|
||||
async.o range.o
|
||||
obj-y += groups.o
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
ccflags-$(CONFIG_PM_DEBUG) := -DDEBUG
|
||||
|
||||
obj-$(CONFIG_PM) += main.o
|
||||
obj-$(CONFIG_PM) += main.o qos.o
|
||||
obj-$(CONFIG_PM_SLEEP) += console.o
|
||||
obj-$(CONFIG_FREEZER) += process.o
|
||||
obj-$(CONFIG_SUSPEND) += suspend.o
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
/*#define DEBUG*/
|
||||
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
@ -45,62 +45,57 @@
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
/*
|
||||
* locking rule: all changes to requests or notifiers lists
|
||||
* locking rule: all changes to constraints 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
|
||||
*/
|
||||
enum pm_qos_type {
|
||||
PM_QOS_MAX, /* return the largest value */
|
||||
PM_QOS_MIN /* return the smallest value */
|
||||
};
|
||||
|
||||
/*
|
||||
* Note: The lockless read path depends on the CPU accessing
|
||||
* target_value atomically. Atomic access is only guaranteed on all CPU
|
||||
* types linux supports for 32 bit quantites
|
||||
*/
|
||||
struct pm_qos_object {
|
||||
struct plist_head requests;
|
||||
struct blocking_notifier_head *notifiers;
|
||||
struct pm_qos_constraints *constraints;
|
||||
struct miscdevice pm_qos_power_miscdev;
|
||||
char *name;
|
||||
s32 target_value; /* Do not change to 64 bit */
|
||||
s32 default_value;
|
||||
enum pm_qos_type type;
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(pm_qos_lock);
|
||||
|
||||
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 = {
|
||||
.requests = PLIST_HEAD_INIT(cpu_dma_pm_qos.requests),
|
||||
.notifiers = &cpu_dma_lat_notifier,
|
||||
.name = "cpu_dma_latency",
|
||||
static struct pm_qos_constraints cpu_dma_constraints = {
|
||||
.list = PLIST_HEAD_INIT(cpu_dma_constraints.list),
|
||||
.target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
|
||||
.default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
|
||||
.type = PM_QOS_MIN,
|
||||
.notifiers = &cpu_dma_lat_notifier,
|
||||
};
|
||||
static struct pm_qos_object cpu_dma_pm_qos = {
|
||||
.constraints = &cpu_dma_constraints,
|
||||
};
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
|
||||
static struct pm_qos_object network_lat_pm_qos = {
|
||||
.requests = PLIST_HEAD_INIT(network_lat_pm_qos.requests),
|
||||
.notifiers = &network_lat_notifier,
|
||||
.name = "network_latency",
|
||||
static struct pm_qos_constraints network_lat_constraints = {
|
||||
.list = PLIST_HEAD_INIT(network_lat_constraints.list),
|
||||
.target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
|
||||
.default_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
|
||||
.type = PM_QOS_MIN
|
||||
.type = PM_QOS_MIN,
|
||||
.notifiers = &network_lat_notifier,
|
||||
};
|
||||
static struct pm_qos_object network_lat_pm_qos = {
|
||||
.constraints = &network_lat_constraints,
|
||||
.name = "network_latency",
|
||||
};
|
||||
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
|
||||
static struct pm_qos_object network_throughput_pm_qos = {
|
||||
.requests = PLIST_HEAD_INIT(network_throughput_pm_qos.requests),
|
||||
.notifiers = &network_throughput_notifier,
|
||||
.name = "network_throughput",
|
||||
static struct pm_qos_constraints network_tput_constraints = {
|
||||
.list = PLIST_HEAD_INIT(network_tput_constraints.list),
|
||||
.target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
|
||||
.default_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
|
||||
.type = PM_QOS_MAX,
|
||||
.notifiers = &network_throughput_notifier,
|
||||
};
|
||||
static struct pm_qos_object network_throughput_pm_qos = {
|
||||
.constraints = &network_tput_constraints,
|
||||
.name = "network_throughput",
|
||||
};
|
||||
|
||||
|
||||
@ -127,17 +122,17 @@ static const struct file_operations pm_qos_power_fops = {
|
||||
};
|
||||
|
||||
/* unlocked internal variant */
|
||||
static inline int pm_qos_get_value(struct pm_qos_object *o)
|
||||
static inline int pm_qos_get_value(struct pm_qos_constraints *c)
|
||||
{
|
||||
if (plist_head_empty(&o->requests))
|
||||
return o->default_value;
|
||||
if (plist_head_empty(&c->list))
|
||||
return c->default_value;
|
||||
|
||||
switch (o->type) {
|
||||
switch (c->type) {
|
||||
case PM_QOS_MIN:
|
||||
return plist_first(&o->requests)->prio;
|
||||
return plist_first(&c->list)->prio;
|
||||
|
||||
case PM_QOS_MAX:
|
||||
return plist_last(&o->requests)->prio;
|
||||
return plist_last(&c->list)->prio;
|
||||
|
||||
default:
|
||||
/* runtime check for not using enum */
|
||||
@ -145,49 +140,217 @@ static inline int pm_qos_get_value(struct pm_qos_object *o)
|
||||
}
|
||||
}
|
||||
|
||||
static inline s32 pm_qos_read_value(struct pm_qos_object *o)
|
||||
s32 pm_qos_read_value(struct pm_qos_constraints *c)
|
||||
{
|
||||
return o->target_value;
|
||||
return c->target_value;
|
||||
}
|
||||
|
||||
static inline void pm_qos_set_value(struct pm_qos_object *o, s32 value)
|
||||
static inline void pm_qos_set_value(struct pm_qos_constraints *c, s32 value)
|
||||
{
|
||||
o->target_value = value;
|
||||
c->target_value = value;
|
||||
}
|
||||
|
||||
static void update_target(struct pm_qos_object *o, struct plist_node *node,
|
||||
int del, int value)
|
||||
/**
|
||||
* pm_qos_update_target - manages the constraints list and calls the notifiers
|
||||
* if needed
|
||||
* @c: constraints data struct
|
||||
* @node: request to add to the list, to update or to remove
|
||||
* @action: action to take on the constraints list
|
||||
* @value: value of the request to add or update
|
||||
*
|
||||
* This function returns 1 if the aggregated constraint value has changed, 0
|
||||
* otherwise.
|
||||
*/
|
||||
int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
|
||||
enum pm_qos_req_action action, int value)
|
||||
{
|
||||
unsigned long flags;
|
||||
int prev_value, curr_value;
|
||||
int prev_value, curr_value, new_value;
|
||||
|
||||
spin_lock_irqsave(&pm_qos_lock, flags);
|
||||
prev_value = pm_qos_get_value(o);
|
||||
/* PM_QOS_DEFAULT_VALUE is a signal that the value is unchanged */
|
||||
if (value != PM_QOS_DEFAULT_VALUE) {
|
||||
prev_value = pm_qos_get_value(c);
|
||||
if (value == PM_QOS_DEFAULT_VALUE)
|
||||
new_value = c->default_value;
|
||||
else
|
||||
new_value = value;
|
||||
|
||||
switch (action) {
|
||||
case PM_QOS_REMOVE_REQ:
|
||||
plist_del(node, &c->list);
|
||||
break;
|
||||
case PM_QOS_UPDATE_REQ:
|
||||
/*
|
||||
* to change the list, we atomically remove, reinit
|
||||
* with new value and add, then see if the extremal
|
||||
* changed
|
||||
*/
|
||||
plist_del(node, &o->requests);
|
||||
plist_node_init(node, value);
|
||||
plist_add(node, &o->requests);
|
||||
} else if (del) {
|
||||
plist_del(node, &o->requests);
|
||||
} else {
|
||||
plist_add(node, &o->requests);
|
||||
plist_del(node, &c->list);
|
||||
case PM_QOS_ADD_REQ:
|
||||
plist_node_init(node, new_value);
|
||||
plist_add(node, &c->list);
|
||||
break;
|
||||
default:
|
||||
/* no action */
|
||||
;
|
||||
}
|
||||
curr_value = pm_qos_get_value(o);
|
||||
pm_qos_set_value(o, curr_value);
|
||||
|
||||
curr_value = pm_qos_get_value(c);
|
||||
pm_qos_set_value(c, curr_value);
|
||||
|
||||
spin_unlock_irqrestore(&pm_qos_lock, flags);
|
||||
|
||||
if (prev_value != curr_value)
|
||||
blocking_notifier_call_chain(o->notifiers,
|
||||
if (prev_value != curr_value) {
|
||||
blocking_notifier_call_chain(c->notifiers,
|
||||
(unsigned long)curr_value,
|
||||
NULL);
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_qos_request - returns current system wide qos expectation
|
||||
* @pm_qos_class: identification of which qos value is requested
|
||||
*
|
||||
* This function returns the current target value.
|
||||
*/
|
||||
int pm_qos_request(int pm_qos_class)
|
||||
{
|
||||
return pm_qos_read_value(pm_qos_array[pm_qos_class]->constraints);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_qos_request);
|
||||
|
||||
int pm_qos_request_active(struct pm_qos_request *req)
|
||||
{
|
||||
return req->pm_qos_class != 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_qos_request_active);
|
||||
|
||||
/**
|
||||
* pm_qos_add_request - inserts new qos request into the list
|
||||
* @req: pointer to a preallocated handle
|
||||
* @pm_qos_class: identifies which list of qos request to use
|
||||
* @value: defines the qos request
|
||||
*
|
||||
* This function inserts a new entry in the pm_qos_class list of requested qos
|
||||
* performance characteristics. It recomputes the aggregate QoS expectations
|
||||
* for the pm_qos_class of parameters and initializes the pm_qos_request
|
||||
* handle. Caller needs to save this handle for later use in updates and
|
||||
* removal.
|
||||
*/
|
||||
|
||||
void pm_qos_add_request(struct pm_qos_request *req,
|
||||
int pm_qos_class, s32 value)
|
||||
{
|
||||
if (!req) /*guard against callers passing in null */
|
||||
return;
|
||||
|
||||
if (pm_qos_request_active(req)) {
|
||||
WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n");
|
||||
return;
|
||||
}
|
||||
req->pm_qos_class = pm_qos_class;
|
||||
pm_qos_update_target(pm_qos_array[pm_qos_class]->constraints,
|
||||
&req->node, PM_QOS_ADD_REQ, value);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_qos_add_request);
|
||||
|
||||
/**
|
||||
* pm_qos_update_request - modifies an existing qos request
|
||||
* @req : handle to list element holding a pm_qos request to use
|
||||
* @value: defines the qos request
|
||||
*
|
||||
* Updates an existing qos request for the pm_qos_class of parameters along
|
||||
* with updating the target pm_qos_class value.
|
||||
*
|
||||
* Attempts are made to make this code callable on hot code paths.
|
||||
*/
|
||||
void pm_qos_update_request(struct pm_qos_request *req,
|
||||
s32 new_value)
|
||||
{
|
||||
if (!req) /*guard against callers passing in null */
|
||||
return;
|
||||
|
||||
if (!pm_qos_request_active(req)) {
|
||||
WARN(1, KERN_ERR "pm_qos_update_request() called for unknown object\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (new_value != req->node.prio)
|
||||
pm_qos_update_target(
|
||||
pm_qos_array[req->pm_qos_class]->constraints,
|
||||
&req->node, PM_QOS_UPDATE_REQ, new_value);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_qos_update_request);
|
||||
|
||||
/**
|
||||
* pm_qos_remove_request - modifies an existing qos request
|
||||
* @req: handle to request list element
|
||||
*
|
||||
* Will remove pm qos request from the list of constraints and
|
||||
* recompute the current target value for the pm_qos_class. Call this
|
||||
* on slow code paths.
|
||||
*/
|
||||
void pm_qos_remove_request(struct pm_qos_request *req)
|
||||
{
|
||||
if (!req) /*guard against callers passing in null */
|
||||
return;
|
||||
/* silent return to keep pcm code cleaner */
|
||||
|
||||
if (!pm_qos_request_active(req)) {
|
||||
WARN(1, KERN_ERR "pm_qos_remove_request() called for unknown object\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pm_qos_update_target(pm_qos_array[req->pm_qos_class]->constraints,
|
||||
&req->node, PM_QOS_REMOVE_REQ,
|
||||
PM_QOS_DEFAULT_VALUE);
|
||||
memset(req, 0, sizeof(*req));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_qos_remove_request);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* upon 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]->constraints->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
|
||||
* upon 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]->constraints->notifiers,
|
||||
notifier);
|
||||
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_qos_remove_notifier);
|
||||
|
||||
/* User space interface to PM QoS classes via misc devices */
|
||||
static int register_pm_qos_misc(struct pm_qos_object *qos)
|
||||
{
|
||||
qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
|
||||
@ -210,165 +373,13 @@ static int find_pm_qos_object_by_minor(int minor)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_qos_request - returns current system wide qos expectation
|
||||
* @pm_qos_class: identification of which qos value is requested
|
||||
*
|
||||
* This function returns the current target value.
|
||||
*/
|
||||
int pm_qos_request(int pm_qos_class)
|
||||
{
|
||||
return pm_qos_read_value(pm_qos_array[pm_qos_class]);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_qos_request);
|
||||
|
||||
int pm_qos_request_active(struct pm_qos_request_list *req)
|
||||
{
|
||||
return req->pm_qos_class != 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_qos_request_active);
|
||||
|
||||
/**
|
||||
* pm_qos_add_request - inserts new qos request into the list
|
||||
* @dep: pointer to a preallocated handle
|
||||
* @pm_qos_class: identifies which list of qos request to use
|
||||
* @value: defines the qos request
|
||||
*
|
||||
* This function inserts a new entry in the pm_qos_class list of requested qos
|
||||
* performance characteristics. It recomputes the aggregate QoS expectations
|
||||
* for the pm_qos_class of parameters and initializes the pm_qos_request_list
|
||||
* handle. Caller needs to save this handle for later use in updates and
|
||||
* removal.
|
||||
*/
|
||||
|
||||
void pm_qos_add_request(struct pm_qos_request_list *dep,
|
||||
int pm_qos_class, s32 value)
|
||||
{
|
||||
struct pm_qos_object *o = pm_qos_array[pm_qos_class];
|
||||
int new_value;
|
||||
|
||||
if (pm_qos_request_active(dep)) {
|
||||
WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n");
|
||||
return;
|
||||
}
|
||||
if (value == PM_QOS_DEFAULT_VALUE)
|
||||
new_value = o->default_value;
|
||||
else
|
||||
new_value = value;
|
||||
plist_node_init(&dep->list, new_value);
|
||||
dep->pm_qos_class = pm_qos_class;
|
||||
update_target(o, &dep->list, 0, PM_QOS_DEFAULT_VALUE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_qos_add_request);
|
||||
|
||||
/**
|
||||
* pm_qos_update_request - modifies an existing qos request
|
||||
* @pm_qos_req : handle to list element holding a pm_qos request to use
|
||||
* @value: defines the qos request
|
||||
*
|
||||
* Updates an existing qos request for the pm_qos_class of parameters along
|
||||
* with updating the target pm_qos_class value.
|
||||
*
|
||||
* Attempts are made to make this code callable on hot code paths.
|
||||
*/
|
||||
void pm_qos_update_request(struct pm_qos_request_list *pm_qos_req,
|
||||
s32 new_value)
|
||||
{
|
||||
s32 temp;
|
||||
struct pm_qos_object *o;
|
||||
|
||||
if (!pm_qos_req) /*guard against callers passing in null */
|
||||
return;
|
||||
|
||||
if (!pm_qos_request_active(pm_qos_req)) {
|
||||
WARN(1, KERN_ERR "pm_qos_update_request() called for unknown object\n");
|
||||
return;
|
||||
}
|
||||
|
||||
o = pm_qos_array[pm_qos_req->pm_qos_class];
|
||||
|
||||
if (new_value == PM_QOS_DEFAULT_VALUE)
|
||||
temp = o->default_value;
|
||||
else
|
||||
temp = new_value;
|
||||
|
||||
if (temp != pm_qos_req->list.prio)
|
||||
update_target(o, &pm_qos_req->list, 0, temp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_qos_update_request);
|
||||
|
||||
/**
|
||||
* pm_qos_remove_request - modifies an existing qos request
|
||||
* @pm_qos_req: handle to request list element
|
||||
*
|
||||
* Will remove pm qos request from the list of requests and
|
||||
* recompute the current target value for the pm_qos_class. Call this
|
||||
* on slow code paths.
|
||||
*/
|
||||
void pm_qos_remove_request(struct pm_qos_request_list *pm_qos_req)
|
||||
{
|
||||
struct pm_qos_object *o;
|
||||
|
||||
if (pm_qos_req == NULL)
|
||||
return;
|
||||
/* silent return to keep pcm code cleaner */
|
||||
|
||||
if (!pm_qos_request_active(pm_qos_req)) {
|
||||
WARN(1, KERN_ERR "pm_qos_remove_request() called for unknown object\n");
|
||||
return;
|
||||
}
|
||||
|
||||
o = pm_qos_array[pm_qos_req->pm_qos_class];
|
||||
update_target(o, &pm_qos_req->list, 1, PM_QOS_DEFAULT_VALUE);
|
||||
memset(pm_qos_req, 0, sizeof(*pm_qos_req));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_qos_remove_request);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* upon 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
|
||||
* upon 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);
|
||||
|
||||
static int pm_qos_power_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
long pm_qos_class;
|
||||
|
||||
pm_qos_class = find_pm_qos_object_by_minor(iminor(inode));
|
||||
if (pm_qos_class >= 0) {
|
||||
struct pm_qos_request_list *req = kzalloc(sizeof(*req), GFP_KERNEL);
|
||||
struct pm_qos_request *req = kzalloc(sizeof(*req), GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -383,7 +394,7 @@ static int pm_qos_power_open(struct inode *inode, struct file *filp)
|
||||
|
||||
static int pm_qos_power_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct pm_qos_request_list *req;
|
||||
struct pm_qos_request *req;
|
||||
|
||||
req = filp->private_data;
|
||||
pm_qos_remove_request(req);
|
||||
@ -398,17 +409,15 @@ static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
|
||||
{
|
||||
s32 value;
|
||||
unsigned long flags;
|
||||
struct pm_qos_object *o;
|
||||
struct pm_qos_request_list *pm_qos_req = filp->private_data;
|
||||
struct pm_qos_request *req = filp->private_data;
|
||||
|
||||
if (!pm_qos_req)
|
||||
if (!req)
|
||||
return -EINVAL;
|
||||
if (!pm_qos_request_active(pm_qos_req))
|
||||
if (!pm_qos_request_active(req))
|
||||
return -EINVAL;
|
||||
|
||||
o = pm_qos_array[pm_qos_req->pm_qos_class];
|
||||
spin_lock_irqsave(&pm_qos_lock, flags);
|
||||
value = pm_qos_get_value(o);
|
||||
value = pm_qos_get_value(pm_qos_array[req->pm_qos_class]->constraints);
|
||||
spin_unlock_irqrestore(&pm_qos_lock, flags);
|
||||
|
||||
return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32));
|
||||
@ -418,7 +427,7 @@ static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *f_pos)
|
||||
{
|
||||
s32 value;
|
||||
struct pm_qos_request_list *pm_qos_req;
|
||||
struct pm_qos_request *req;
|
||||
|
||||
if (count == sizeof(s32)) {
|
||||
if (copy_from_user(&value, buf, sizeof(s32)))
|
||||
@ -449,8 +458,8 @@ static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pm_qos_req = filp->private_data;
|
||||
pm_qos_update_request(pm_qos_req, value);
|
||||
req = filp->private_data;
|
||||
pm_qos_update_request(req, value);
|
||||
|
||||
return count;
|
||||
}
|
@ -19,7 +19,7 @@
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/cfg80211.h>
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/mac80211.h>
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <net/sch_generic.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/mac80211.h>
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include <linux/file.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <sound/core.h>
|
||||
|
Loading…
Reference in New Issue
Block a user