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:
Rafael J. Wysocki 2011-10-07 23:17:07 +02:00
commit 9696cc9007
25 changed files with 927 additions and 286 deletions

View File

@ -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 performance expectations by drivers, subsystems and user space applications on
one of the parameters. one of the parameters.
Currently we have {cpu_dma_latency, network_latency, network_throughput} as the Two different PM QoS frameworks are available:
initial set of pm_qos parameters. 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: Each parameters have defined units:
* latency: usec * latency: usec
* timeout: usec * timeout: usec
* throughput: kbs (kilo bit / sec) * throughput: kbs (kilo bit / sec)
1. PM QoS framework
The infrastructure exposes multiple misc device nodes one per implemented The infrastructure exposes multiple misc device nodes one per implemented
parameter. The set of parameters implement is defined by pm_qos_power_init() 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 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 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 aggregated target value is simply the max or min of the request values held
in the parameter list elements. 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: From kernel mode the use of this interface is simple:
handle = pm_qos_add_request(param_class, target_value): 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 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 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. 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): void pm_qos_update_request(handle, new_target_value):
Will update the list element pointed to by the handle with the 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 call the notification tree if the target was changed as a result of removing
the request. 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: From user mode:
Only processes can register a pm_qos request. To provide for automatic 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. 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.

View File

@ -18,7 +18,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/pm_qos_params.h> #include <linux/pm_qos.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/string.h> #include <linux/string.h>

View File

@ -37,7 +37,7 @@
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/sched.h> /* need_resched() */ #include <linux/sched.h> /* need_resched() */
#include <linux/pm_qos_params.h> #include <linux/pm_qos.h>
#include <linux/clockchips.h> #include <linux/clockchips.h>
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/irqflags.h> #include <linux/irqflags.h>

View File

@ -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_SLEEP) += main.o wakeup.o
obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_RUNTIME) += runtime.o
obj-$(CONFIG_PM_TRACE_RTC) += trace.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_PM_GENERIC_DOMAINS) += domain.o
obj-$(CONFIG_HAVE_CLK) += clock_ops.o obj-$(CONFIG_HAVE_CLK) += clock_ops.o
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG

View File

@ -65,6 +65,7 @@ void device_pm_init(struct device *dev)
spin_lock_init(&dev->power.lock); spin_lock_init(&dev->power.lock);
pm_runtime_init(dev); pm_runtime_init(dev);
INIT_LIST_HEAD(&dev->power.entry); 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_warn(dev, "parent %s should not be sleeping\n",
dev_name(dev->parent)); dev_name(dev->parent));
list_add_tail(&dev->power.entry, &dpm_list); list_add_tail(&dev->power.entry, &dpm_list);
dev_pm_qos_constraints_init(dev);
mutex_unlock(&dpm_list_mtx); 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)); dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
complete_all(&dev->power.completion); complete_all(&dev->power.completion);
mutex_lock(&dpm_list_mtx); mutex_lock(&dpm_list_mtx);
dev_pm_qos_constraints_destroy(dev);
list_del_init(&dev->power.entry); list_del_init(&dev->power.entry);
mutex_unlock(&dpm_list_mtx); mutex_unlock(&dpm_list_mtx);
device_wakeup_disable(dev); device_wakeup_disable(dev);

View File

@ -1,3 +1,5 @@
#include <linux/pm_qos.h>
#ifdef CONFIG_PM_RUNTIME #ifdef CONFIG_PM_RUNTIME
extern void pm_runtime_init(struct device *dev); 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) static inline void device_pm_init(struct device *dev)
{ {
spin_lock_init(&dev->power.lock); spin_lock_init(&dev->power.lock);
dev->power.power_state = PMSG_INVALID;
pm_runtime_init(dev); 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) static inline void device_pm_remove(struct device *dev)
{ {
dev_pm_qos_constraints_destroy(dev);
pm_runtime_remove(dev); pm_runtime_remove(dev);
} }
static inline void device_pm_add(struct device *dev) {}
static inline void device_pm_move_before(struct device *deva, static inline void device_pm_move_before(struct device *deva,
struct device *devb) {} struct device *devb) {}
static inline void device_pm_move_after(struct device *deva, static inline void device_pm_move_after(struct device *deva,

419
drivers/base/power/qos.c Normal file
View 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);

View File

@ -12,7 +12,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/pm_qos_params.h> #include <linux/pm_qos.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/ktime.h> #include <linux/ktime.h>

View File

@ -14,7 +14,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/pm_qos_params.h> #include <linux/pm_qos.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>

View File

@ -12,7 +12,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/pm_qos_params.h> #include <linux/pm_qos.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/ktime.h> #include <linux/ktime.h>
#include <linux/hrtimer.h> #include <linux/hrtimer.h>

View File

@ -21,7 +21,7 @@
#include <media/videobuf-dma-sg.h> #include <media/videobuf-dma-sg.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/dma-mapping.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-core.h>
#include <linux/via-gpio.h> #include <linux/via-gpio.h>
#include <linux/via_i2c.h> #include <linux/via_i2c.h>
@ -69,7 +69,7 @@ struct via_camera {
struct mutex lock; struct mutex lock;
enum viacam_opstate opstate; enum viacam_opstate opstate;
unsigned long flags; unsigned long flags;
struct pm_qos_request_list qos_request; struct pm_qos_request qos_request;
/* /*
* GPIO info for power/reset management * GPIO info for power/reset management
*/ */

View File

@ -47,7 +47,7 @@
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/pm_qos_params.h> #include <linux/pm_qos.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/aer.h> #include <linux/aer.h>
#include <linux/prefetch.h> #include <linux/prefetch.h>

View File

@ -161,7 +161,7 @@ that only one external action is invoked at a time.
#include <linux/firmware.h> #include <linux/firmware.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/pm_qos_params.h> #include <linux/pm_qos.h>
#include <net/lib80211.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_DESCRIPTION "Intel(R) PRO/Wireless 2100 Network Driver"
#define DRV_COPYRIGHT "Copyright(c) 2003-2006 Intel Corporation" #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 */ /* Debugging stuff */
#ifdef CONFIG_IPW2100_DEBUG #ifdef CONFIG_IPW2100_DEBUG

View File

@ -31,7 +31,7 @@
#include <linux/if_link.h> #include <linux/if_link.h>
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/pm_qos_params.h> #include <linux/pm_qos.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/atomic.h> #include <linux/atomic.h>
@ -964,7 +964,7 @@ struct net_device {
*/ */
char name[IFNAMSIZ]; char name[IFNAMSIZ];
struct pm_qos_request_list pm_qos_req; struct pm_qos_request pm_qos_req;
/* device name hash chain */ /* device name hash chain */
struct hlist_node name_hlist; struct hlist_node name_hlist;

View File

@ -326,6 +326,7 @@ extern struct dev_pm_ops generic_subsys_pm_ops;
* requested by a driver. * requested by a driver.
*/ */
#define PM_EVENT_INVALID (-1)
#define PM_EVENT_ON 0x0000 #define PM_EVENT_ON 0x0000
#define PM_EVENT_FREEZE 0x0001 #define PM_EVENT_FREEZE 0x0001
#define PM_EVENT_SUSPEND 0x0002 #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_SUSPEND (PM_EVENT_AUTO | PM_EVENT_SUSPEND)
#define PM_EVENT_AUTO_RESUME (PM_EVENT_AUTO | PM_EVENT_RESUME) #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_ON ((struct pm_message){ .event = PM_EVENT_ON, })
#define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, }) #define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, })
#define PMSG_QUIESCE ((struct pm_message){ .event = PM_EVENT_QUIESCE, }) #define PMSG_QUIESCE ((struct pm_message){ .event = PM_EVENT_QUIESCE, })
@ -481,6 +483,7 @@ struct dev_pm_info {
unsigned long accounting_timestamp; unsigned long accounting_timestamp;
#endif #endif
struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */
struct pm_qos_constraints *constraints;
}; };
extern void update_pm_runtime_accounting(struct device *dev); extern void update_pm_runtime_accounting(struct device *dev);

155
include/linux/pm_qos.h Normal file
View 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

View File

@ -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

View File

@ -29,7 +29,7 @@
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/bitops.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_substream_chip(substream) ((substream)->private_data)
#define snd_pcm_chip(pcm) ((pcm)->private_data) #define snd_pcm_chip(pcm) ((pcm)->private_data)
@ -373,7 +373,7 @@ struct snd_pcm_substream {
int number; int number;
char name[32]; /* substream name */ char name[32]; /* substream name */
int stream; /* stream (direction) */ 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 */ size_t buffer_bytes_max; /* limit ring buffer size */
struct snd_dma_buffer dma_buffer; struct snd_dma_buffer dma_buffer;
unsigned int dma_buf_id; unsigned int dma_buf_id;

View File

@ -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 \ rcupdate.o extable.o params.o posix-timers.o \
kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.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 \ 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 async.o range.o
obj-y += groups.o obj-y += groups.o

View File

@ -1,7 +1,7 @@
ccflags-$(CONFIG_PM_DEBUG) := -DDEBUG 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_PM_SLEEP) += console.o
obj-$(CONFIG_FREEZER) += process.o obj-$(CONFIG_FREEZER) += process.o
obj-$(CONFIG_SUSPEND) += suspend.o obj-$(CONFIG_SUSPEND) += suspend.o

View File

@ -29,7 +29,7 @@
/*#define DEBUG*/ /*#define DEBUG*/
#include <linux/pm_qos_params.h> #include <linux/pm_qos.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -45,62 +45,57 @@
#include <linux/uaccess.h> #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 * 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 * 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 pm_qos_object {
struct plist_head requests; struct pm_qos_constraints *constraints;
struct blocking_notifier_head *notifiers;
struct miscdevice pm_qos_power_miscdev; struct miscdevice pm_qos_power_miscdev;
char *name; 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 DEFINE_SPINLOCK(pm_qos_lock);
static struct pm_qos_object null_pm_qos; static struct pm_qos_object null_pm_qos;
static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier); static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier);
static struct pm_qos_object cpu_dma_pm_qos = { static struct pm_qos_constraints cpu_dma_constraints = {
.requests = PLIST_HEAD_INIT(cpu_dma_pm_qos.requests), .list = PLIST_HEAD_INIT(cpu_dma_constraints.list),
.notifiers = &cpu_dma_lat_notifier,
.name = "cpu_dma_latency",
.target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, .target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
.default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, .default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE,
.type = PM_QOS_MIN, .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 BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
static struct pm_qos_object network_lat_pm_qos = { static struct pm_qos_constraints network_lat_constraints = {
.requests = PLIST_HEAD_INIT(network_lat_pm_qos.requests), .list = PLIST_HEAD_INIT(network_lat_constraints.list),
.notifiers = &network_lat_notifier,
.name = "network_latency",
.target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE, .target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
.default_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 BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
static struct pm_qos_object network_throughput_pm_qos = { static struct pm_qos_constraints network_tput_constraints = {
.requests = PLIST_HEAD_INIT(network_throughput_pm_qos.requests), .list = PLIST_HEAD_INIT(network_tput_constraints.list),
.notifiers = &network_throughput_notifier,
.name = "network_throughput",
.target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE, .target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
.default_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE, .default_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
.type = PM_QOS_MAX, .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 */ /* 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)) if (plist_head_empty(&c->list))
return o->default_value; return c->default_value;
switch (o->type) { switch (c->type) {
case PM_QOS_MIN: case PM_QOS_MIN:
return plist_first(&o->requests)->prio; return plist_first(&c->list)->prio;
case PM_QOS_MAX: case PM_QOS_MAX:
return plist_last(&o->requests)->prio; return plist_last(&c->list)->prio;
default: default:
/* runtime check for not using enum */ /* 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; unsigned long flags;
int prev_value, curr_value; int prev_value, curr_value, new_value;
spin_lock_irqsave(&pm_qos_lock, flags); spin_lock_irqsave(&pm_qos_lock, flags);
prev_value = pm_qos_get_value(o); prev_value = pm_qos_get_value(c);
/* PM_QOS_DEFAULT_VALUE is a signal that the value is unchanged */ if (value == PM_QOS_DEFAULT_VALUE)
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 * to change the list, we atomically remove, reinit
* with new value and add, then see if the extremal * with new value and add, then see if the extremal
* changed * changed
*/ */
plist_del(node, &o->requests); plist_del(node, &c->list);
plist_node_init(node, value); case PM_QOS_ADD_REQ:
plist_add(node, &o->requests); plist_node_init(node, new_value);
} else if (del) { plist_add(node, &c->list);
plist_del(node, &o->requests); break;
} else { default:
plist_add(node, &o->requests); /* 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); spin_unlock_irqrestore(&pm_qos_lock, flags);
if (prev_value != curr_value) if (prev_value != curr_value) {
blocking_notifier_call_chain(o->notifiers, blocking_notifier_call_chain(c->notifiers,
(unsigned long)curr_value, (unsigned long)curr_value,
NULL); 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) static int register_pm_qos_misc(struct pm_qos_object *qos)
{ {
qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR; 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; 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) static int pm_qos_power_open(struct inode *inode, struct file *filp)
{ {
long pm_qos_class; long pm_qos_class;
pm_qos_class = find_pm_qos_object_by_minor(iminor(inode)); pm_qos_class = find_pm_qos_object_by_minor(iminor(inode));
if (pm_qos_class >= 0) { 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) if (!req)
return -ENOMEM; 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) 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; req = filp->private_data;
pm_qos_remove_request(req); pm_qos_remove_request(req);
@ -398,17 +409,15 @@ static ssize_t pm_qos_power_read(struct file *filp, char __user *buf,
{ {
s32 value; s32 value;
unsigned long flags; unsigned long flags;
struct pm_qos_object *o; struct pm_qos_request *req = filp->private_data;
struct pm_qos_request_list *pm_qos_req = filp->private_data;
if (!pm_qos_req) if (!req)
return -EINVAL; return -EINVAL;
if (!pm_qos_request_active(pm_qos_req)) if (!pm_qos_request_active(req))
return -EINVAL; return -EINVAL;
o = pm_qos_array[pm_qos_req->pm_qos_class];
spin_lock_irqsave(&pm_qos_lock, flags); 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); spin_unlock_irqrestore(&pm_qos_lock, flags);
return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32)); 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) size_t count, loff_t *f_pos)
{ {
s32 value; s32 value;
struct pm_qos_request_list *pm_qos_req; struct pm_qos_request *req;
if (count == sizeof(s32)) { if (count == sizeof(s32)) {
if (copy_from_user(&value, buf, 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; return -EINVAL;
} }
pm_qos_req = filp->private_data; req = filp->private_data;
pm_qos_update_request(pm_qos_req, value); pm_qos_update_request(req, value);
return count; return count;
} }

View File

@ -19,7 +19,7 @@
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/pm_qos_params.h> #include <linux/pm_qos.h>
#include <linux/inetdevice.h> #include <linux/inetdevice.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/cfg80211.h> #include <net/cfg80211.h>

View File

@ -17,7 +17,7 @@
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/pm_qos_params.h> #include <linux/pm_qos.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <net/mac80211.h> #include <net/mac80211.h>

View File

@ -14,7 +14,7 @@
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/pm_qos_params.h> #include <linux/pm_qos.h>
#include <net/sch_generic.h> #include <net/sch_generic.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <net/mac80211.h> #include <net/mac80211.h>

View File

@ -23,7 +23,7 @@
#include <linux/file.h> #include <linux/file.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/pm_qos_params.h> #include <linux/pm_qos.h>
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <sound/core.h> #include <sound/core.h>