[RFT,1/3] PM: QoS: Introduce frequency QoS
diff mbox series

Message ID 4551555.oysnf1Sd0E@kreacher
State Awaiting Upstream
Delegated to: Rafael Wysocki
Headers show
Series
  • cpufreq / PM: QoS: Introduce frequency QoS and use it in cpufreq
Related show

Commit Message

Rafael J. Wysocki Oct. 16, 2019, 10:41 a.m. UTC
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(+)

Comments

Viresh Kumar Oct. 17, 2019, 9:41 a.m. UTC | #1
On 16-10-19, 12:41, Rafael J. Wysocki wrote:
> 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;

should this be MIN ?

> +	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;

and this MAX ?

> +	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);
> 
>
Rafael J. Wysocki Oct. 17, 2019, 2:16 p.m. UTC | #2
On Thu, Oct 17, 2019 at 11:41 AM Viresh Kumar <viresh.kumar@linaro.org> wrote:
>
> On 16-10-19, 12:41, Rafael J. Wysocki wrote:
> > 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;
>
> should this be MIN ?

No, it shouldn't.

For the min frequency, the effective constraint needs to be the
maximum of all requests, because that satisfies all of them (each
request means "the frequency cannot be less than this").

> > +     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;
>
> and this MAX ?

Likewise, for the max frequency, the effective constraint needs to be
the minimum of all requests, as each of them means "the frequency
cannot be more than this").

[Also note that the current code in device PM QoS uses MIN and MAX
here in the same way. :-)]
Viresh Kumar Oct. 18, 2019, 5:41 a.m. UTC | #3
On 17-10-19, 16:16, Rafael J. Wysocki wrote:
> [Also note that the current code in device PM QoS uses MIN and MAX
> here in the same way. :-)]

Stupid me, enough embarrassment for the day :(
Leonard Crestez Oct. 24, 2019, 7:01 p.m. UTC | #4
On 16.10.2019 13:48, Rafael J. Wysocki wrote:
> 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;

These min/max_freq_notifiers fields seem unused? They're initialized but 
the freq_qos_add/remove_notifier calls use min/max_freq.notifiers directly.

Should probably just be dropped.

> +/**
> + * 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);
Leonard Crestez Oct. 24, 2019, 7:34 p.m. UTC | #5
On 24.10.2019 22:01, Leonard Crestez wrote:
> On 16.10.2019 13:48, Rafael J. Wysocki wrote:
>> 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;
> 
> These min/max_freq_notifiers fields seem unused? They're initialized but
> the freq_qos_add/remove_notifier calls use min/max_freq.notifiers directly.
> 
> Should probably just be dropped.

Never mind, I see now that pm_qos_constraints.notifiers is actually a 
pointer to notifier block provided by somebody else.

--
Regards,
Leonard
Doug Smythies Nov. 17, 2019, 4:13 p.m. UTC | #6
Apologies if people are getting this e-mail twice.
Because it had an attachment, I think it got deleted
from list distribution.

On 2019.11.16 23:35 Doug Smythies wrote:
> On 2019.10.16 03:41 Rafael J. Wysocki wrote:
>
> ... deleted ...
>
> Hi Rafael,
>
> Not sure, but I think it is this one that
> causes complaining when I try to set the
> intel_pstate driver to passive mode.
> I started from active mode, powersave governor,
> no HWP.
>
> Kernel: 5.4-rc7
>
> I did not go back and try previous 5.4 RCs.
> I did try kernel 5.3-rc8, because I already had
> it installed, and it worked fine.
>
> I use a script (for years), run as sudo:
>
> doug@s15:~/temp$ cat set_cpu_passive
> #! /bin/bash
> cat /sys/devices/system/cpu/intel_pstate/status
> echo passive > /sys/devices/system/cpu/intel_pstate/status
> cat /sys/devices/system/cpu/intel_pstate/status
>
> And I get this (very small excerpt):
>
> freq_qos_add_request() called for active request
> WARNING: CPU: 1 PID: 2758 at kernel/power/qos.c:763 freq_qos_add_request+0x4c/0xa0
> CPU: 1 PID: 2758 Comm: set_cpu_passive Not tainted 5.4.0-rc7-stock #727
> Failed to add freq constraint for CPU0 (-22)
>
> freq_qos_add_request() called for active request
> WARNING: CPU: 1 PID: 2758 at kernel/power/qos.c:763 freq_qos_add_request+0x4c/0xa0
> CPU: 1 PID: 2758 Comm: set_cpu_passive Tainted: G        W         5.4.0-rc7-stock #727
> Failed to add freq constraint for CPU1 (-22)
>
> ...
>
> I'll attach the whole thing, but it will likely get removed
> from the general list e-mails.
>
> ... Doug

I forgot to mention, other than the error messages,
things seems to work fine.

If anyone wants to see what was the attachment, it
is here (for a few days):

http://www.smythies.com/~doug/temp_kernel/rjw.txt
Doug Smythies Nov. 19, 2019, 2:35 p.m. UTC | #7
On 2019.11.17 08:13 Doug Smythies wrote:
> On 2019.11.16 23:35 Doug Smythies wrote:

>> Hi Rafael,
>>
>> Not sure, but I think it is this one that
>> causes complaining when I try to set the
>> intel_pstate driver to passive mode.
>> I started from active mode, powersave governor,
>> no HWP.
>>
>> Kernel: 5.4-rc7
>>
>> I did not go back and try previous 5.4 RCs.

After looking at the git tags for this patch,
I tried kernel 5.4-rc2, which was the closest
Kernel I had to before the patch set was added.
It worked fine, as expected.

>> I did try kernel 5.3-rc8, because I already had
>> it installed, and it worked fine.
>>
>> I use a script (for years), run as sudo:
>>
>> doug@s15:~/temp$ cat set_cpu_passive
>> #! /bin/bash
>> cat /sys/devices/system/cpu/intel_pstate/status
>> echo passive > /sys/devices/system/cpu/intel_pstate/status
>> cat /sys/devices/system/cpu/intel_pstate/status
>>
>> And I get this (very small excerpt):
>>
>> freq_qos_add_request() called for active request
>> WARNING: CPU: 1 PID: 2758 at kernel/power/qos.c:763 freq_qos_add_request+0x4c/0xa0
>> CPU: 1 PID: 2758 Comm: set_cpu_passive Not tainted 5.4.0-rc7-stock #727
>> Failed to add freq constraint for CPU0 (-22)
>>
>> freq_qos_add_request() called for active request
>> WARNING: CPU: 1 PID: 2758 at kernel/power/qos.c:763 freq_qos_add_request+0x4c/0xa0
>> CPU: 1 PID: 2758 Comm: set_cpu_passive Tainted: G        W         5.4.0-rc7-stock #727
>> Failed to add freq constraint for CPU1 (-22)

Updated summary of previous emails:
This patch or patch set breaks the after boot
ability to change CPU frequency scaling drivers.

Using a workaround of booting with
"intel_pstate=passive" seems to prevent the errors.

Changing between the intel_pstate and intel_cpufreq drivers
(i.e. between active and passive modes)
after boot, either way, causes the errors. i.e.

Failed to add freq constraint for CPU7 (-22)
(2 per CPU per attempt)

This is 100% repeatable.

> I forgot to mention, other than the error messages,
> things seems to work fine.

Correction: It is actually quite bad. Eventually,
there will be a "Segmentation fault (core dumped)",
and then even re-boot gets stuck and the only
recourse seems to be the reset button.

This is not 100% repeatable.

I did this (kernel 5.4-rc8):

diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 8ab3170..24c7a6b 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -2491,6 +2491,8 @@ static int intel_pstate_register_driver(struct cpufreq_driver *driver)
 {
        int ret;

+       pr_info("Intel P-state register driver .... \n");
+
        memset(&global, 0, sizeof(global));
        global.max_perf_pct = 100;

@@ -2508,6 +2510,8 @@ static int intel_pstate_register_driver(struct cpufreq_driver *driver)

 static int intel_pstate_unregister_driver(void)
 {
+       pr_info("Intel P-state unregister driver .... \n");
+
        if (hwp_active)
                return -EBUSY;

And got this (dmesg | grep -i pstate):

[    2.024876] intel_pstate: Intel P-state driver initializing
[    2.024883] intel_pstate: Intel P-state register driver ....

Attempt to change from the booted passive mode to active mode:

[  175.903031] intel_pstate: Intel P-state unregister driver ....
[  175.975543] intel_pstate: Intel P-state register driver ....
[  175.975754]  intel_pstate_register_driver+0x4b/0x90
[  175.975756]  ? intel_pstate_unregister_driver+0x31/0x40
[  175.977728]  intel_pstate_register_driver+0x4b/0x90
[  175.977730]  ? intel_pstate_unregister_driver+0x31/0x40
[  175.979644]  intel_pstate_register_driver+0x4b/0x90
[  175.979647]  ? intel_pstate_unregister_driver+0x31/0x40
[  175.981424]  intel_pstate_register_driver+0x4b/0x90
[  175.981427]  ? intel_pstate_unregister_driver+0x31/0x40
[  175.982428]  intel_pstate_register_driver+0x4b/0x90
[  175.982430]  ? intel_pstate_unregister_driver+0x31/0x40
[  175.983127]  intel_pstate_register_driver+0x4b/0x90
[  175.983128]  ? intel_pstate_unregister_driver+0x31/0x40
[  175.983829]  intel_pstate_register_driver+0x4b/0x90
[  175.983832]  ? intel_pstate_unregister_driver+0x31/0x40
[  175.984434]  intel_pstate_register_driver+0x4b/0x90
[  175.984435]  ? intel_pstate_unregister_driver+0x31/0x40
[  175.985040]  intel_pstate_register_driver+0x4b/0x90
[  175.985041]  ? intel_pstate_unregister_driver+0x31/0x40
[  175.985598]  intel_pstate_register_driver+0x4b/0x90
[  175.985600]  ? intel_pstate_unregister_driver+0x31/0x40
[  175.986178]  intel_pstate_register_driver+0x4b/0x90
[  175.986179]  ? intel_pstate_unregister_driver+0x31/0x40
[  175.986721]  intel_pstate_register_driver+0x4b/0x90
[  175.986723]  ? intel_pstate_unregister_driver+0x31/0x40
[  175.987301]  intel_pstate_register_driver+0x4b/0x90
[  175.987302]  ? intel_pstate_unregister_driver+0x31/0x40
[  175.987828]  intel_pstate_register_driver+0x4b/0x90
[  175.987830]  ? intel_pstate_unregister_driver+0x31/0x40
[  175.988420]  intel_pstate_register_driver+0x4b/0x90
[  175.988421]  ? intel_pstate_unregister_driver+0x31/0x40
[  175.988920]  intel_pstate_register_driver+0x4b/0x90
[  175.988921]  ? intel_pstate_unregister_driver+0x31/0x40

Sometimes I get this:

grep . /sys/devices/system/cpu/intel_pstate/*
/sys/devices/system/cpu/intel_pstate/max_perf_pct:100
/sys/devices/system/cpu/intel_pstate/min_perf_pct:42

Instead of this:

grep . /sys/devices/system/cpu/intel_pstate/*
/sys/devices/system/cpu/intel_pstate/max_perf_pct:100
/sys/devices/system/cpu/intel_pstate/min_perf_pct:42
/sys/devices/system/cpu/intel_pstate/no_turbo:0
/sys/devices/system/cpu/intel_pstate/num_pstates:23
/sys/devices/system/cpu/intel_pstate/status:active
/sys/devices/system/cpu/intel_pstate/turbo_pct:18

But do not yet know the exact way to reliably
create it.

This is as far as I got so far.

... Doug
Rafael J. Wysocki Nov. 19, 2019, 7:17 p.m. UTC | #8
On Tue, Nov 19, 2019 at 3:35 PM Doug Smythies <dsmythies@telus.net> wrote:
>
> On 2019.11.17 08:13 Doug Smythies wrote:
> > On 2019.11.16 23:35 Doug Smythies wrote:
>
> >> Hi Rafael,
> >>
> >> Not sure, but I think it is this one that
> >> causes complaining when I try to set the
> >> intel_pstate driver to passive mode.
> >> I started from active mode, powersave governor,
> >> no HWP.
> >>
> >> Kernel: 5.4-rc7
> >>
> >> I did not go back and try previous 5.4 RCs.
>
> After looking at the git tags for this patch,
> I tried kernel 5.4-rc2, which was the closest
> Kernel I had to before the patch set was added.
> It worked fine, as expected.
>
> >> I did try kernel 5.3-rc8, because I already had
> >> it installed, and it worked fine.
> >>
> >> I use a script (for years), run as sudo:
> >>
> >> doug@s15:~/temp$ cat set_cpu_passive
> >> #! /bin/bash
> >> cat /sys/devices/system/cpu/intel_pstate/status
> >> echo passive > /sys/devices/system/cpu/intel_pstate/status
> >> cat /sys/devices/system/cpu/intel_pstate/status
> >>
> >> And I get this (very small excerpt):
> >>
> >> freq_qos_add_request() called for active request
> >> WARNING: CPU: 1 PID: 2758 at kernel/power/qos.c:763 freq_qos_add_request+0x4c/0xa0
> >> CPU: 1 PID: 2758 Comm: set_cpu_passive Not tainted 5.4.0-rc7-stock #727
> >> Failed to add freq constraint for CPU0 (-22)
> >>
> >> freq_qos_add_request() called for active request
> >> WARNING: CPU: 1 PID: 2758 at kernel/power/qos.c:763 freq_qos_add_request+0x4c/0xa0
> >> CPU: 1 PID: 2758 Comm: set_cpu_passive Tainted: G        W         5.4.0-rc7-stock #727
> >> Failed to add freq constraint for CPU1 (-22)
>
> Updated summary of previous emails:
> This patch or patch set breaks the after boot
> ability to change CPU frequency scaling drivers.
>
> Using a workaround of booting with
> "intel_pstate=passive" seems to prevent the errors.
>
> Changing between the intel_pstate and intel_cpufreq drivers
> (i.e. between active and passive modes)
> after boot, either way, causes the errors. i.e.
>
> Failed to add freq constraint for CPU7 (-22)
> (2 per CPU per attempt)

These messages come from acpi_processor_ppc_init() and
acpi_thermal_cpufreq_init(), AFAICS, which are invoked by
acpi_processor_notifier() and that is invoked by the
blocking_notifier_call_chain() in cpufreq_online() which tirggers for
new policies after adding the max freq QoS request to
policy->constraints.

The requests added by them should be removed by
acpi_processor_ppc_exit() and acpi_thermal_cpufreq_exit(),
respectively, invoked by the blocking_notifier_call_chain() in
cpufreq_policy_free(), but it looks like that doesn't happen.

However, I now also see that freq_qos_remove_request() doesn't clear
the qos field in req which is should do, so freq_qos_add_request()
will complain and fail if the object pointed to by req is passed to it
again.

I'll send a patch to test for this later today.
Rafael J. Wysocki Nov. 19, 2019, 10:13 p.m. UTC | #9
On Tuesday, November 19, 2019 8:17:05 PM CET Rafael J. Wysocki wrote:
> On Tue, Nov 19, 2019 at 3:35 PM Doug Smythies <dsmythies@telus.net> wrote:
> >
> > On 2019.11.17 08:13 Doug Smythies wrote:
> > > On 2019.11.16 23:35 Doug Smythies wrote:
> >
> > >> Hi Rafael,
> > >>
> > >> Not sure, but I think it is this one that
> > >> causes complaining when I try to set the
> > >> intel_pstate driver to passive mode.
> > >> I started from active mode, powersave governor,
> > >> no HWP.
> > >>
> > >> Kernel: 5.4-rc7
> > >>
> > >> I did not go back and try previous 5.4 RCs.
> >
> > After looking at the git tags for this patch,
> > I tried kernel 5.4-rc2, which was the closest
> > Kernel I had to before the patch set was added.
> > It worked fine, as expected.
> >
> > >> I did try kernel 5.3-rc8, because I already had
> > >> it installed, and it worked fine.
> > >>
> > >> I use a script (for years), run as sudo:
> > >>
> > >> doug@s15:~/temp$ cat set_cpu_passive
> > >> #! /bin/bash
> > >> cat /sys/devices/system/cpu/intel_pstate/status
> > >> echo passive > /sys/devices/system/cpu/intel_pstate/status
> > >> cat /sys/devices/system/cpu/intel_pstate/status
> > >>
> > >> And I get this (very small excerpt):
> > >>
> > >> freq_qos_add_request() called for active request
> > >> WARNING: CPU: 1 PID: 2758 at kernel/power/qos.c:763 freq_qos_add_request+0x4c/0xa0
> > >> CPU: 1 PID: 2758 Comm: set_cpu_passive Not tainted 5.4.0-rc7-stock #727
> > >> Failed to add freq constraint for CPU0 (-22)
> > >>
> > >> freq_qos_add_request() called for active request
> > >> WARNING: CPU: 1 PID: 2758 at kernel/power/qos.c:763 freq_qos_add_request+0x4c/0xa0
> > >> CPU: 1 PID: 2758 Comm: set_cpu_passive Tainted: G        W         5.4.0-rc7-stock #727
> > >> Failed to add freq constraint for CPU1 (-22)
> >
> > Updated summary of previous emails:
> > This patch or patch set breaks the after boot
> > ability to change CPU frequency scaling drivers.
> >
> > Using a workaround of booting with
> > "intel_pstate=passive" seems to prevent the errors.
> >
> > Changing between the intel_pstate and intel_cpufreq drivers
> > (i.e. between active and passive modes)
> > after boot, either way, causes the errors. i.e.
> >
> > Failed to add freq constraint for CPU7 (-22)
> > (2 per CPU per attempt)
> 
> These messages come from acpi_processor_ppc_init() and
> acpi_thermal_cpufreq_init(), AFAICS, which are invoked by
> acpi_processor_notifier() and that is invoked by the
> blocking_notifier_call_chain() in cpufreq_online() which tirggers for
> new policies after adding the max freq QoS request to
> policy->constraints.
> 
> The requests added by them should be removed by
> acpi_processor_ppc_exit() and acpi_thermal_cpufreq_exit(),
> respectively, invoked by the blocking_notifier_call_chain() in
> cpufreq_policy_free(), but it looks like that doesn't happen.
> 
> However, I now also see that freq_qos_remove_request() doesn't clear
> the qos field in req which is should do, so freq_qos_add_request()
> will complain and fail if the object pointed to by req is passed to it
> again.
> 
> I'll send a patch to test for this later today.
> 

The patch is appended.  Please test it (on top of 5.4-rc8) and report back.


---
 kernel/power/qos.c |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

Index: linux-pm/kernel/power/qos.c
===================================================================
--- linux-pm.orig/kernel/power/qos.c
+++ linux-pm/kernel/power/qos.c
@@ -814,6 +814,8 @@ EXPORT_SYMBOL_GPL(freq_qos_update_reques
  */
 int freq_qos_remove_request(struct freq_qos_request *req)
 {
+	int ret;
+
 	if (!req)
 		return -EINVAL;
 
@@ -821,7 +823,11 @@ int freq_qos_remove_request(struct freq_
 		 "%s() called for unknown object\n", __func__))
 		return -EINVAL;
 
-	return freq_qos_apply(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
+	ret = freq_qos_apply(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
+	req->qos = NULL;
+	req->type = 0;
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(freq_qos_remove_request);
Doug Smythies Nov. 20, 2019, 6:55 a.m. UTC | #10
On 2019.11.19 14:14 Rafael J. Wysocki wrote:
> On Tuesday, November 19, 2019 8:17:05 PM CET Rafael J. Wysocki wrote:

...
 
>> However, I now also see that freq_qos_remove_request() doesn't clear
>> the qos field in req which is should do, so freq_qos_add_request()
>> will complain and fail if the object pointed to by req is passed to it
>> again.
>> 
>> I'll send a patch to test for this later today.
>> 
>
> The patch is appended.  Please test it (on top of 5.4-rc8) and report back.
>
> ---
> kernel/power/qos.c |    8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
> Index: linux-pm/kernel/power/qos.c
> ===================================================================
> --- linux-pm.orig/kernel/power/qos.c
> +++ linux-pm/kernel/power/qos.c
> @@ -814,6 +814,8 @@ EXPORT_SYMBOL_GPL(freq_qos_update_reques
>  */
> int freq_qos_remove_request(struct freq_qos_request *req)
> {
> +	int ret;
> +
> 	if (!req)
> 		return -EINVAL;
> 
> @@ -821,7 +823,11 @@ int freq_qos_remove_request(struct freq_
> 		 "%s() called for unknown object\n", __func__))
> 		return -EINVAL;
> 
> -	return freq_qos_apply(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
> +	ret = freq_qos_apply(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
> +	req->qos = NULL;
> +	req->type = 0;
> +
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(freq_qos_remove_request);
 
Yes the patch fixes the problem. Thanks.

I tested several hundred times switching between
passive and active modes with the intel_pstate driver,
including with various CPUs disabled and re-enabled.

... Doug
Rafael J. Wysocki Nov. 20, 2019, 9:08 a.m. UTC | #11
On Wed, Nov 20, 2019 at 7:55 AM Doug Smythies <dsmythies@telus.net> wrote:
>
> On 2019.11.19 14:14 Rafael J. Wysocki wrote:
> > On Tuesday, November 19, 2019 8:17:05 PM CET Rafael J. Wysocki wrote:
>
> ...
>
> >> However, I now also see that freq_qos_remove_request() doesn't clear
> >> the qos field in req which is should do, so freq_qos_add_request()
> >> will complain and fail if the object pointed to by req is passed to it
> >> again.
> >>
> >> I'll send a patch to test for this later today.
> >>
> >
> > The patch is appended.  Please test it (on top of 5.4-rc8) and report back.
> >
> > ---
> > kernel/power/qos.c |    8 +++++++-
> > 1 file changed, 7 insertions(+), 1 deletion(-)
> >
> > Index: linux-pm/kernel/power/qos.c
> > ===================================================================
> > --- linux-pm.orig/kernel/power/qos.c
> > +++ linux-pm/kernel/power/qos.c
> > @@ -814,6 +814,8 @@ EXPORT_SYMBOL_GPL(freq_qos_update_reques
> >  */
> > int freq_qos_remove_request(struct freq_qos_request *req)
> > {
> > +     int ret;
> > +
> >       if (!req)
> >               return -EINVAL;
> >
> > @@ -821,7 +823,11 @@ int freq_qos_remove_request(struct freq_
> >                "%s() called for unknown object\n", __func__))
> >               return -EINVAL;
> >
> > -     return freq_qos_apply(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
> > +     ret = freq_qos_apply(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
> > +     req->qos = NULL;
> > +     req->type = 0;
> > +
> > +     return ret;
> >  }
> >  EXPORT_SYMBOL_GPL(freq_qos_remove_request);
>
> Yes the patch fixes the problem. Thanks.
>
> I tested several hundred times switching between
> passive and active modes with the intel_pstate driver,
> including with various CPUs disabled and re-enabled.

Thanks a lot!

Let me resend the patch with a changelog and tags.

Patch
diff mbox series

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);