lkml.org 
[lkml]   [2019]   [Oct]   [16]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFT][PATCH 1/3] PM: QoS: Introduce frequency QoS
    Date
    From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

    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>
    ---
    include/linux/pm_qos.h | 44 ++++++++
    kernel/power/qos.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++++
    2 files changed, 284 insertions(+)

    Index: linux-pm/include/linux/pm_qos.h
    ===================================================================
    --- linux-pm.orig/include/linux/pm_qos.h
    +++ linux-pm/include/linux/pm_qos.h
    @@ -267,4 +267,48 @@ static inline s32 dev_pm_qos_raw_resume_
    }
    #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
    Index: linux-pm/kernel/power/qos.c
    ===================================================================
    --- linux-pm.orig/kernel/power/qos.c
    +++ linux-pm/kernel/power/qos.c
    @@ -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);


    \
     
     \ /
      Last update: 2019-10-16 12:48    [W:2.420 / U:0.140 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site