PM: QoS: Introduce frequency QoS
Introduce frequency QoS, based on the "raw" low-level PM QoS, to represent min and max frequency requests and aggregate constraints. The min and max frequency requests are to be represented by struct freq_qos_request objects and the aggregate constraints are to be represented by struct freq_constraints objects. The latter are expected to be initialized with the help of freq_constraints_init(). The freq_qos_read_value() helper is defined to retrieve the aggregate constraints values from a given struct freq_constraints object and there are the freq_qos_add_request(), freq_qos_update_request() and freq_qos_remove_request() helpers to manipulate the min and max frequency requests. It is assumed that the the helpers will not run concurrently with each other for the same struct freq_qos_request object, so if that may be the case, their uses must ensure proper synchronization between them (e.g. through locking). In addition, freq_qos_add_notifier() and freq_qos_remove_notifier() are provided to add and remove notifiers that will trigger on aggregate constraint changes to and from a given struct freq_constraints object, respectively. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
This commit is contained in:
Родитель
7d194c2100
Коммит
77751a466e
|
@ -267,4 +267,48 @@ static inline s32 dev_pm_qos_raw_resume_latency(struct device *dev)
|
|||
}
|
||||
#endif
|
||||
|
||||
#define FREQ_QOS_MIN_DEFAULT_VALUE 0
|
||||
#define FREQ_QOS_MAX_DEFAULT_VALUE (-1)
|
||||
|
||||
enum freq_qos_req_type {
|
||||
FREQ_QOS_MIN = 1,
|
||||
FREQ_QOS_MAX,
|
||||
};
|
||||
|
||||
struct freq_constraints {
|
||||
struct pm_qos_constraints min_freq;
|
||||
struct blocking_notifier_head min_freq_notifiers;
|
||||
struct pm_qos_constraints max_freq;
|
||||
struct blocking_notifier_head max_freq_notifiers;
|
||||
};
|
||||
|
||||
struct freq_qos_request {
|
||||
enum freq_qos_req_type type;
|
||||
struct plist_node pnode;
|
||||
struct freq_constraints *qos;
|
||||
};
|
||||
|
||||
static inline int freq_qos_request_active(struct freq_qos_request *req)
|
||||
{
|
||||
return !IS_ERR_OR_NULL(req->qos);
|
||||
}
|
||||
|
||||
void freq_constraints_init(struct freq_constraints *qos);
|
||||
|
||||
s32 freq_qos_read_value(struct freq_constraints *qos,
|
||||
enum freq_qos_req_type type);
|
||||
|
||||
int freq_qos_add_request(struct freq_constraints *qos,
|
||||
struct freq_qos_request *req,
|
||||
enum freq_qos_req_type type, s32 value);
|
||||
int freq_qos_update_request(struct freq_qos_request *req, s32 new_value);
|
||||
int freq_qos_remove_request(struct freq_qos_request *req);
|
||||
|
||||
int freq_qos_add_notifier(struct freq_constraints *qos,
|
||||
enum freq_qos_req_type type,
|
||||
struct notifier_block *notifier);
|
||||
int freq_qos_remove_notifier(struct freq_constraints *qos,
|
||||
enum freq_qos_req_type type,
|
||||
struct notifier_block *notifier);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -650,3 +650,243 @@ static int __init pm_qos_power_init(void)
|
|||
}
|
||||
|
||||
late_initcall(pm_qos_power_init);
|
||||
|
||||
/* Definitions related to the frequency QoS below. */
|
||||
|
||||
/**
|
||||
* freq_constraints_init - Initialize frequency QoS constraints.
|
||||
* @qos: Frequency QoS constraints to initialize.
|
||||
*/
|
||||
void freq_constraints_init(struct freq_constraints *qos)
|
||||
{
|
||||
struct pm_qos_constraints *c;
|
||||
|
||||
c = &qos->min_freq;
|
||||
plist_head_init(&c->list);
|
||||
c->target_value = FREQ_QOS_MIN_DEFAULT_VALUE;
|
||||
c->default_value = FREQ_QOS_MIN_DEFAULT_VALUE;
|
||||
c->no_constraint_value = FREQ_QOS_MIN_DEFAULT_VALUE;
|
||||
c->type = PM_QOS_MAX;
|
||||
c->notifiers = &qos->min_freq_notifiers;
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(c->notifiers);
|
||||
|
||||
c = &qos->max_freq;
|
||||
plist_head_init(&c->list);
|
||||
c->target_value = FREQ_QOS_MAX_DEFAULT_VALUE;
|
||||
c->default_value = FREQ_QOS_MAX_DEFAULT_VALUE;
|
||||
c->no_constraint_value = FREQ_QOS_MAX_DEFAULT_VALUE;
|
||||
c->type = PM_QOS_MIN;
|
||||
c->notifiers = &qos->max_freq_notifiers;
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(c->notifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
* freq_qos_read_value - Get frequency QoS constraint for a given list.
|
||||
* @qos: Constraints to evaluate.
|
||||
* @type: QoS request type.
|
||||
*/
|
||||
s32 freq_qos_read_value(struct freq_constraints *qos,
|
||||
enum freq_qos_req_type type)
|
||||
{
|
||||
s32 ret;
|
||||
|
||||
switch (type) {
|
||||
case FREQ_QOS_MIN:
|
||||
ret = IS_ERR_OR_NULL(qos) ?
|
||||
FREQ_QOS_MIN_DEFAULT_VALUE :
|
||||
pm_qos_read_value(&qos->min_freq);
|
||||
break;
|
||||
case FREQ_QOS_MAX:
|
||||
ret = IS_ERR_OR_NULL(qos) ?
|
||||
FREQ_QOS_MAX_DEFAULT_VALUE :
|
||||
pm_qos_read_value(&qos->max_freq);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* freq_qos_apply - Add/modify/remove frequency QoS request.
|
||||
* @req: Constraint request to apply.
|
||||
* @action: Action to perform (add/update/remove).
|
||||
* @value: Value to assign to the QoS request.
|
||||
*/
|
||||
static int freq_qos_apply(struct freq_qos_request *req,
|
||||
enum pm_qos_req_action action, s32 value)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch(req->type) {
|
||||
case FREQ_QOS_MIN:
|
||||
ret = pm_qos_update_target(&req->qos->min_freq, &req->pnode,
|
||||
action, value);
|
||||
break;
|
||||
case FREQ_QOS_MAX:
|
||||
ret = pm_qos_update_target(&req->qos->max_freq, &req->pnode,
|
||||
action, value);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* freq_qos_add_request - Insert new frequency QoS request into a given list.
|
||||
* @qos: Constraints to update.
|
||||
* @req: Preallocated request object.
|
||||
* @type: Request type.
|
||||
* @value: Request value.
|
||||
*
|
||||
* Insert a new entry into the @qos list of requests, recompute the effective
|
||||
* QoS constraint value for that list and initialize the @req object. The
|
||||
* caller needs to save that object for later use in updates and removal.
|
||||
*
|
||||
* Return 1 if the effective constraint value has changed, 0 if the effective
|
||||
* constraint value has not changed, or a negative error code on failures.
|
||||
*/
|
||||
int freq_qos_add_request(struct freq_constraints *qos,
|
||||
struct freq_qos_request *req,
|
||||
enum freq_qos_req_type type, s32 value)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (IS_ERR_OR_NULL(qos) || !req)
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN(freq_qos_request_active(req),
|
||||
"%s() called for active request\n", __func__))
|
||||
return -EINVAL;
|
||||
|
||||
req->qos = qos;
|
||||
req->type = type;
|
||||
ret = freq_qos_apply(req, PM_QOS_ADD_REQ, value);
|
||||
if (ret < 0) {
|
||||
req->qos = NULL;
|
||||
req->type = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(freq_qos_add_request);
|
||||
|
||||
/**
|
||||
* freq_qos_update_request - Modify existing frequency QoS request.
|
||||
* @req: Request to modify.
|
||||
* @new_value: New request value.
|
||||
*
|
||||
* Update an existing frequency QoS request along with the effective constraint
|
||||
* value for the list of requests it belongs to.
|
||||
*
|
||||
* Return 1 if the effective constraint value has changed, 0 if the effective
|
||||
* constraint value has not changed, or a negative error code on failures.
|
||||
*/
|
||||
int freq_qos_update_request(struct freq_qos_request *req, s32 new_value)
|
||||
{
|
||||
if (!req)
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN(!freq_qos_request_active(req),
|
||||
"%s() called for unknown object\n", __func__))
|
||||
return -EINVAL;
|
||||
|
||||
if (req->pnode.prio == new_value)
|
||||
return 0;
|
||||
|
||||
return freq_qos_apply(req, PM_QOS_UPDATE_REQ, new_value);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(freq_qos_update_request);
|
||||
|
||||
/**
|
||||
* freq_qos_remove_request - Remove frequency QoS request from its list.
|
||||
* @req: Request to remove.
|
||||
*
|
||||
* Remove the given frequency QoS request from the list of constraints it
|
||||
* belongs to and recompute the effective constraint value for that list.
|
||||
*
|
||||
* Return 1 if the effective constraint value has changed, 0 if the effective
|
||||
* constraint value has not changed, or a negative error code on failures.
|
||||
*/
|
||||
int freq_qos_remove_request(struct freq_qos_request *req)
|
||||
{
|
||||
if (!req)
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN(!freq_qos_request_active(req),
|
||||
"%s() called for unknown object\n", __func__))
|
||||
return -EINVAL;
|
||||
|
||||
return freq_qos_apply(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(freq_qos_remove_request);
|
||||
|
||||
/**
|
||||
* freq_qos_add_notifier - Add frequency QoS change notifier.
|
||||
* @qos: List of requests to add the notifier to.
|
||||
* @type: Request type.
|
||||
* @notifier: Notifier block to add.
|
||||
*/
|
||||
int freq_qos_add_notifier(struct freq_constraints *qos,
|
||||
enum freq_qos_req_type type,
|
||||
struct notifier_block *notifier)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (IS_ERR_OR_NULL(qos) || !notifier)
|
||||
return -EINVAL;
|
||||
|
||||
switch (type) {
|
||||
case FREQ_QOS_MIN:
|
||||
ret = blocking_notifier_chain_register(qos->min_freq.notifiers,
|
||||
notifier);
|
||||
break;
|
||||
case FREQ_QOS_MAX:
|
||||
ret = blocking_notifier_chain_register(qos->max_freq.notifiers,
|
||||
notifier);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(freq_qos_add_notifier);
|
||||
|
||||
/**
|
||||
* freq_qos_remove_notifier - Remove frequency QoS change notifier.
|
||||
* @qos: List of requests to remove the notifier from.
|
||||
* @type: Request type.
|
||||
* @notifier: Notifier block to remove.
|
||||
*/
|
||||
int freq_qos_remove_notifier(struct freq_constraints *qos,
|
||||
enum freq_qos_req_type type,
|
||||
struct notifier_block *notifier)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (IS_ERR_OR_NULL(qos) || !notifier)
|
||||
return -EINVAL;
|
||||
|
||||
switch (type) {
|
||||
case FREQ_QOS_MIN:
|
||||
ret = blocking_notifier_chain_unregister(qos->min_freq.notifiers,
|
||||
notifier);
|
||||
break;
|
||||
case FREQ_QOS_MAX:
|
||||
ret = blocking_notifier_chain_unregister(qos->max_freq.notifiers,
|
||||
notifier);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(freq_qos_remove_notifier);
|
||||
|
|
Загрузка…
Ссылка в новой задаче