* RE: [PATCH 07/11] PM / devfreg: Add support policy notifiers
From: MyungJoo Ham @ 2018-05-28 5:19 UTC (permalink / raw)
Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
Brian Norris, Douglas Anderson, Matthias Kaehlcke
In-Reply-To: <20180525203043.249193-8-mka@chromium.org>
>Policy notifiers are called before a frequency change and may narrow
>the min/max frequency range in devfreq_policy, which is used to adjust
>the target frequency if it is beyond this range.
>
>Also add a few helpers:
> - devfreq_verify_within_[dev_]limits()
> - should be used by the notifiers for policy adjustments.
> - dev_to_devfreq()
> - lookup a devfreq strict from a device pointer
>
>Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
>---
> drivers/devfreq/devfreq.c | 47 +++++++++++++++++++++-------
> include/linux/devfreq.h | 66 +++++++++++++++++++++++++++++++++++++++
> 2 files changed, 102 insertions(+), 11 deletions(-)
Hello Matthias,
Why should we have yet another notifier from an instance of devfreq?
Wouldn't it better to let the current notifier (transition notifier)
handle new events as well by adding possible event states to it?
Anyway, is this the reason why you've separated some data of devfreq
into "policy" struct? (I was wondering why while reading commit 6/11).
Cheers
MyungJoo
^ permalink raw reply
* RE: [PATCH 05/11] PM / devfreq: governors: Return device frequency limits instead of user limits
From: MyungJoo Ham @ 2018-05-28 5:04 UTC (permalink / raw)
Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
Brian Norris, Douglas Anderson, Matthias Kaehlcke
In-Reply-To: <20180525203043.249193-6-mka@chromium.org>
>The performance, powersave and simpleondemand governors can return
>df->min/max_freq, which are the user defined frequency limits.
>update_devfreq() already takes care of adjusting the target frequency
>with the user limits if necessary, therefore we can return
>df->scaling_min/max_freq instead, which is the min/max frequency
>supported by the device at a given time (depending on the
>enabled/disabled OPPs)
>
>Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
>---
> drivers/devfreq/governor_performance.c | 2 +-
> drivers/devfreq/governor_powersave.c | 2 +-
> drivers/devfreq/governor_simpleondemand.c | 6 +++---
> 3 files changed, 5 insertions(+), 5 deletions(-)
>
Actually, even scaling_max_freq and scaling_min_freq are
covered centerally at devfreq.c:update_devfreq();
Wouldn't it be sufficient to return UINT_MAX for performance
and return UINT_MIN (0) for powersave, if the purpose is to
remove redundancy?
In the same sense, we may return UINT_MAX for freq-increasing
case for simpleondemand as well, because they are filtered
centrally anyway.
(This commit might be better merged to 4/11 in that case as well.)
Cheers,
MyungJoo
^ permalink raw reply
* Re: [PATCH v14 1/2] cpufreq: Add Kryo CPU scaling driver
From: Viresh Kumar @ 2018-05-28 5:03 UTC (permalink / raw)
To: Ilia Lin
Cc: ilia.lin, vireshk, nm, sboyd, robh, mark.rutland, rjw, linux-pm,
devicetree, linux-kernel
In-Reply-To: <1527252112-7015-2-git-send-email-ilialin@codeaurora.org>
On 25-05-18, 15:41, Ilia Lin wrote:
> In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors,
> the CPU frequency subset and voltage value of each OPP varies
> based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables
> defines the voltage and frequency value based on the msm-id in SMEM
> and speedbin blown in the efuse combination.
> The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
> to provide the OPP framework with required information.
> This is used to determine the voltage and frequency value for each OPP of
> operating-points-v2 table when it is parsed by the OPP framework.
>
> Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
> ---
> MAINTAINERS | 7 ++
> drivers/cpufreq/Kconfig.arm | 10 ++
> drivers/cpufreq/Makefile | 1 +
> drivers/cpufreq/cpufreq-dt-platdev.c | 3 +
> drivers/cpufreq/qcom-cpufreq-kryo.c | 212 +++++++++++++++++++++++++++++++++++
> 5 files changed, 233 insertions(+)
> create mode 100644 drivers/cpufreq/qcom-cpufreq-kryo.c
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
--
viresh
^ permalink raw reply
* RE: [PATCH 04/11] PM / devfreq: Remove redundant frequency adjustment from governors
From: MyungJoo Ham @ 2018-05-28 4:57 UTC (permalink / raw)
Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
Brian Norris, Douglas Anderson, Matthias Kaehlcke
In-Reply-To: <20180525203043.249193-5-mka@chromium.org>
> The userspace and simpleondemand governor determine a target frequency and
> then adjust it according to the df->min/max_freq limits that might have
> been set by user space. This adjustment is redundant, it is done in
> update_devfreq() for any governor, right after returning from
> governor->get_target_freq().
>
> Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
> ---
> drivers/devfreq/governor_simpleondemand.c | 5 -----
> drivers/devfreq/governor_userspace.c | 16 ++++------------
> 2 files changed, 4 insertions(+), 17 deletions(-)
>
Yes, indeed. Governors are no longer required to be aware of min/max freq.
Acked-by: MyungJoo Ham <myungjoo.ham@samsung.com>
^ permalink raw reply
* RE: [PATCH 03/11] PM / devfreq: Remove check for df->max_freq == 0 from governors
From: MyungJoo Ham @ 2018-05-28 4:51 UTC (permalink / raw)
Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
Brian Norris, Douglas Anderson, Matthias Kaehlcke
In-Reply-To: <20180525203043.249193-4-mka@chromium.org>
>Commit "PM / devfreq: Fix handling of min/max_freq == 0" ensures that
>df->max_freq is not 0, remove unnecessary checks.
>
>Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
>---
> drivers/devfreq/governor_performance.c | 5 +----
> drivers/devfreq/governor_simpleondemand.c | 7 +++----
> 2 files changed, 4 insertions(+), 8 deletions(-)
>
>diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c
>index 4d23ecfbd948..1c990cb45098 100644
>
Thanks!
Acked-by: MyungJoo Ham <myungjoo.ham@samsung.com>
^ permalink raw reply
* RE: [PATCH 02/11] PM / devfreq: Fix handling of min/max_freq == 0
From: MyungJoo Ham @ 2018-05-28 3:59 UTC (permalink / raw)
Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
Brian Norris, Douglas Anderson, Matthias Kaehlcke
In-Reply-To: <20180525203043.249193-3-mka@chromium.org>
> Commit ab8f58ad72c4 ("PM / devfreq: Set min/max_freq when adding the
> devfreq device") initializes df->min/max_freq with the min/max OPP when
> the device is added. Later commit f1d981eaecf8 ("PM / devfreq: Use the
> available min/max frequency") adds df->scaling_min/max_freq and the
> following to the frequency adjustment code:
>
> max_freq = MIN(devfreq->scaling_max_freq, devfreq->max_freq);
>
> With the current handling of min/max_freq this is incorrect:
>
> Even though df->max_freq is now initialized to a value != 0 user space
> can still set it to 0, in this case max_freq would be 0 instead of
> df->scaling_max_freq as intended. In consequence the frequency adjustment
> is not performed:
>
> if (max_freq && freq > max_freq) {
> freq = max_freq;
>
> To fix this set df->min/max freq to the min/max OPP in max/max_freq_store,
> when the user passes a value of 0. This also prevents df->max_freq from
> being set below the min OPP when df->min_freq is 0, and similar for
> min_freq. Since it is now guaranteed that df->min/max_freq can't be 0 the
> checks for this case can be removed.
>
> Fixes: f1d981eaecf8 ("PM / devfreq: Use the available min/max frequency")
> Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
> ---
> drivers/devfreq/devfreq.c | 30 ++++++++++++++++++------------
> 1 file changed, 18 insertions(+), 12 deletions(-)
Thanks a lot! Nice Catch.
Acked-by: MyungJoo Ham <myunngjoo.ham@samsung.com>
Cheers,
MyungJoo.
^ permalink raw reply
* Re: [PATCH] PM / runtime: Drop usage count for suppliers at device link removal
From: Rafael J. Wysocki @ 2018-05-27 10:18 UTC (permalink / raw)
To: Ulf Hansson
Cc: Rafael J . Wysocki, Greg Kroah-Hartman, Linux PM, Viresh Kumar,
Vincent Guittot, Todor Tomov, Rajendra Nayak, Jon Hunter,
Linux Kernel Mailing List, Linux ARM
In-Reply-To: <1527150816-8459-1-git-send-email-ulf.hansson@linaro.org>
On Thu, May 24, 2018 at 10:33 AM, Ulf Hansson <ulf.hansson@linaro.org> wrote:
> In the case consumer device is runtime resumed, while the link to the
> supplier is removed, the earlier call to pm_runtime_get_sync() made from
> rpm_get_suppliers() does not get properly balanced with a corresponding
> call to pm_runtime_put(). This leads to that suppliers remains to be
> runtime resumed forever, while they don't need to.
>
> Let's fix the behaviour by calling rpm_put_suppliers() when dropping a
> device link. Not that, since rpm_put_suppliers() checks the
> link->rpm_active flag, we can correctly avoid to call pm_runtime_put() in
> cases when we shouldn't.
>
> Reported-by: Todor Tomov <todor.tomov@linaro.org>
> Fixes: 21d5c57b3726 ("PM / runtime: Use device links")
> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
> ---
>
> Rafael, I am not sure if this is safe from locking point of view. The device
> link write lock has been taken when pm_runtime_drop_link() is called, hence I
> assume calling rpm_put_suppliers() should be fine!? If not, can you please
> advise how to change?
Holding the lock should be sufficient for the list to be stable, so
AFAICS it is OK.
Thanks,
Rafael
^ permalink raw reply
* Re: [PATCH v3 1/2] sched/cpufreq: always consider blocked FAIR utilization
From: Rafael J. Wysocki @ 2018-05-27 9:50 UTC (permalink / raw)
To: Patrick Bellasi
Cc: Linux Kernel Mailing List, Linux PM, Ingo Molnar, Peter Zijlstra,
Rafael J . Wysocki, Viresh Kumar, Vincent Guittot,
Dietmar Eggemann, Morten Rasmussen, Juri Lelli, Joel Fernandes,
Steve Muckle
In-Reply-To: <20180524141023.13765-2-patrick.bellasi@arm.com>
On Thu, May 24, 2018 at 4:10 PM, Patrick Bellasi
<patrick.bellasi@arm.com> wrote:
> Since the refactoring introduced by:
>
> commit 8f111bc357aa ("cpufreq/schedutil: Rewrite CPUFREQ_RT support")
>
> we aggregate FAIR utilization only if this class has runnable tasks.
> This was mainly due to avoid the risk to stay on an high frequency just
> because of the blocked utilization of a CPU not being properly decayed
> while the CPU was idle.
>
> However, since:
>
> commit 31e77c93e432 ("sched/fair: Update blocked load when newly idle")
>
> the FAIR blocked utilization is properly decayed also for IDLE CPUs.
>
> This allows us to use the FAIR blocked utilization as a safe mechanism
> to gracefully reduce the frequency only if no FAIR tasks show up on a
> CPU for a reasonable period of time.
>
> Moreover, we also reduce the frequency drops of CPUs running periodic
> tasks which, depending on the task periodicity and the time required
> for a frequency switch, was increasing the chances to introduce some
> undesirable performance variations.
>
> Reported-by: Vincent Guittot <vincent.guittot@linaro.org>
> Signed-off-by: Patrick Bellasi <patrick.bellasi@arm.com>
> Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
> Acked-by: Vincent Guittot <vincent.guittot@linaro.org>
> Tested-by: Vincent Guittot <vincent.guittot@linaro.org>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> Cc: Vincent Guittot <vincent.guittot@linaro.org>
> Cc: Viresh Kumar <viresh.kumar@linaro.org>
> Cc: Joel Fernandes <joelaf@google.com>
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-pm@vger.kernel.org
Reviewed-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Or please let me know if you want me to apply this one.
> ---
> Changes in v3:
> - add "Tested-by" and "Acked-by" Vincent tags
>
> Changes in v2:
> - add "Acked-by" Viresh tag
> ---
> kernel/sched/cpufreq_schedutil.c | 17 ++++++++---------
> 1 file changed, 8 insertions(+), 9 deletions(-)
>
> diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
> index e13df951aca7..28592b62b1d5 100644
> --- a/kernel/sched/cpufreq_schedutil.c
> +++ b/kernel/sched/cpufreq_schedutil.c
> @@ -183,22 +183,21 @@ static void sugov_get_util(struct sugov_cpu *sg_cpu)
> static unsigned long sugov_aggregate_util(struct sugov_cpu *sg_cpu)
> {
> struct rq *rq = cpu_rq(sg_cpu->cpu);
> - unsigned long util;
>
> - if (rq->rt.rt_nr_running) {
> - util = sg_cpu->max;
> - } else {
> - util = sg_cpu->util_dl;
> - if (rq->cfs.h_nr_running)
> - util += sg_cpu->util_cfs;
> - }
> + if (rq->rt.rt_nr_running)
> + return sg_cpu->max;
>
> /*
> + * Utilization required by DEADLINE must always be granted while, for
> + * FAIR, we use blocked utilization of IDLE CPUs as a mechanism to
> + * gracefully reduce the frequency when no tasks show up for longer
> + * periods of time.
> + *
> * Ideally we would like to set util_dl as min/guaranteed freq and
> * util_cfs + util_dl as requested freq. However, cpufreq is not yet
> * ready for such an interface. So, we only do the latter for now.
> */
> - return min(util, sg_cpu->max);
> + return min(sg_cpu->max, (sg_cpu->util_dl + sg_cpu->util_cfs));
> }
>
> static void sugov_set_iowait_boost(struct sugov_cpu *sg_cpu, u64 time, unsigned int flags)
> --
> 2.15.1
>
^ permalink raw reply
* Re: [PATCH] cpufreq: reinitialize new policy min/max when writing scaling_(max|min)_freq
From: kbuild test robot @ 2018-05-27 6:15 UTC (permalink / raw)
Cc: kbuild-all, rjw, viresh.kumar, linux-pm, linux-kernel,
gengyanping, sunzhaosheng, Kevin Wangtao
In-Reply-To: <1527144234-96396-1-git-send-email-kevin.wangtao@hisilicon.com>
[-- Attachment #1: Type: text/plain, Size: 3341 bytes --]
Hi Kevin,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on pm/linux-next]
[also build test ERROR on v4.17-rc6]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Kevin-Wangtao/cpufreq-reinitialize-new-policy-min-max-when-writing-scaling_-max-min-_freq/20180527-132510
base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
config: i386-randconfig-x079-201821 (attached as .config)
compiler: gcc-7 (Debian 7.3.0-16) 7.3.0
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
All errors (new ones prefixed by >>):
drivers/cpufreq/cpufreq.c: In function 'store_scaling_min_freq':
>> drivers/cpufreq/cpufreq.c:700:12: error: invalid type argument of '->' (have 'struct cpufreq_policy')
new_policy->min = policy->user_policy.min; \
^
drivers/cpufreq/cpufreq.c:715:1: note: in expansion of macro 'store_one'
store_one(scaling_min_freq, min);
^~~~~~~~~
drivers/cpufreq/cpufreq.c:701:12: error: invalid type argument of '->' (have 'struct cpufreq_policy')
new_policy->max = policy->user_policy.max; \
^
drivers/cpufreq/cpufreq.c:715:1: note: in expansion of macro 'store_one'
store_one(scaling_min_freq, min);
^~~~~~~~~
drivers/cpufreq/cpufreq.c: In function 'store_scaling_max_freq':
>> drivers/cpufreq/cpufreq.c:700:12: error: invalid type argument of '->' (have 'struct cpufreq_policy')
new_policy->min = policy->user_policy.min; \
^
drivers/cpufreq/cpufreq.c:716:1: note: in expansion of macro 'store_one'
store_one(scaling_max_freq, max);
^~~~~~~~~
drivers/cpufreq/cpufreq.c:701:12: error: invalid type argument of '->' (have 'struct cpufreq_policy')
new_policy->max = policy->user_policy.max; \
^
drivers/cpufreq/cpufreq.c:716:1: note: in expansion of macro 'store_one'
store_one(scaling_max_freq, max);
^~~~~~~~~
vim +700 drivers/cpufreq/cpufreq.c
685
686 static int cpufreq_set_policy(struct cpufreq_policy *policy,
687 struct cpufreq_policy *new_policy);
688
689 /**
690 * cpufreq_per_cpu_attr_write() / store_##file_name() - sysfs write access
691 */
692 #define store_one(file_name, object) \
693 static ssize_t store_##file_name \
694 (struct cpufreq_policy *policy, const char *buf, size_t count) \
695 { \
696 int ret, temp; \
697 struct cpufreq_policy new_policy; \
698 \
699 memcpy(&new_policy, policy, sizeof(*policy)); \
> 700 new_policy->min = policy->user_policy.min; \
701 new_policy->max = policy->user_policy.max; \
702 \
703 ret = sscanf(buf, "%u", &new_policy.object); \
704 if (ret != 1) \
705 return -EINVAL; \
706 \
707 temp = new_policy.object; \
708 ret = cpufreq_set_policy(policy, &new_policy); \
709 if (!ret) \
710 policy->user_policy.object = temp; \
711 \
712 return ret ? ret : count; \
713 }
714
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 34477 bytes --]
^ permalink raw reply
* [PATCH] power: supply: max1721x: fix spelling mistake "RSenese" -> "RSense"
From: Colin King @ 2018-05-26 13:56 UTC (permalink / raw)
To: Sebastian Reichel, linux-pm; +Cc: kernel-janitors, linux-kernel
From: Colin Ian King <colin.king@canonical.com>
Trivial fix to spelling mistake in dev_warn message text
Signed-off-by: Colin Ian King <colin.king@canonical.com>
---
drivers/power/supply/max1721x_battery.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/power/supply/max1721x_battery.c b/drivers/power/supply/max1721x_battery.c
index 9ee601a03d9b..9ca895b0dabb 100644
--- a/drivers/power/supply/max1721x_battery.c
+++ b/drivers/power/supply/max1721x_battery.c
@@ -372,7 +372,7 @@ static int devm_w1_max1721x_add_device(struct w1_slave *sl)
}
if (!info->rsense) {
- dev_warn(info->w1_dev, "RSenese not calibrated, set 10 mOhms!\n");
+ dev_warn(info->w1_dev, "RSense not calibrated, set 10 mOhms!\n");
info->rsense = 1000; /* in regs in 10^-5 */
}
dev_info(info->w1_dev, "RSense: %d mOhms.\n", info->rsense / 100);
--
2.17.0
^ permalink raw reply related
* [PATCH V2] cpufreq: reinitialize new policy min/max when writing scaling_(max|min)_freq
From: Kevin Wangtao @ 2018-05-26 7:16 UTC (permalink / raw)
To: rjw, viresh.kumar
Cc: linux-pm, linux-kernel, gengyanping, sunzhaosheng, Kevin Wangtao
In-Reply-To: <1527144234-96396-1-git-send-email-kevin.wangtao@hisilicon.com>
consider such situation, current user_policy.min is 1000000,
current user_policy.max is 1200000, in cpufreq_set_policy,
other driver may update policy.min to 1200000, policy.max to
1300000. After that, If we input "echo 1300000 > scaling_min_freq",
then user_policy.min will be 1300000, and user_policy.max is
still 1200000, because the input value is checked with policy.max
not user_policy.max. if we get all related cpus offline and
online again, it will cause cpufreq_init_policy fail because
user_policy.min is higher than user_policy.max.
The solution is when user space tries to write scaling_(max|min)_freq,
the min/max of new_policy should be reinitialized with min/max
of user_policy, like what cpufreq_update_policy does.
Signed-off-by: Kevin Wangtao <kevin.wangtao@hisilicon.com>
---
drivers/cpufreq/cpufreq.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index b79c532..82123a1 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -697,6 +697,8 @@ static ssize_t store_##file_name \
struct cpufreq_policy new_policy; \
\
memcpy(&new_policy, policy, sizeof(*policy)); \
+ new_policy.min = policy->user_policy.min; \
+ new_policy.max = policy->user_policy.max; \
\
ret = sscanf(buf, "%u", &new_policy.object); \
if (ret != 1) \
--
2.8.1
^ permalink raw reply related
* Re: [PATCH] cpufreq: reinitialize new policy min/max when writing scaling_(max|min)_freq
From: Wangtao (Kevin, Kirin) @ 2018-05-26 6:50 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Rafael J. Wysocki, Viresh Kumar, Linux PM,
Linux Kernel Mailing List, gengyanping, sunzhaosheng
In-Reply-To: <CAJZ5v0ih6s9WviSpg9D+kSt2h8T5Vnx-MziOmYuk1H4yZOYLNg@mail.gmail.com>
在 2018/5/24 15:45, Rafael J. Wysocki 写道:
> On Thu, May 24, 2018 at 8:43 AM, Kevin Wangtao
> <kevin.wangtao@hisilicon.com> wrote:
>> consider such situation, current user_policy.min is 1000000,
>> current user_policy.max is 1200000, in cpufreq_set_policy,
>> other driver may update policy.min to 1200000, policy.max to
>> 1300000. After that, If we input "echo 1300000 > scaling_min_freq",
>> then user_policy.min will be 1300000, and user_policy.max is
>> still 1200000, because the input value is checked with policy.max
>> not user_policy.max. if we get all related cpus offline and
>> online again, it will cause cpufreq_init_policy fail because
>> user_policy.min is higher than user_policy.max.
>
> How do you reproduce this, exactly?
I can also reproduce this issue with upstream code, write max frequency to scaling_max_freq
and scaling_min_freq, run benchmark to let cpu cooling take effect to clip freq, then write
the cliped freq to scaling_max_freq, thus user_policy.min is still max frequency but user_policy.max
is cliped freq which is lower than max frequency.
>
>> The solution is when user space tries to write scaling_(max|min)_freq,
>> the min/max of new_policy should be reinitialized with min/max
>> of user_policy, like what cpufreq_update_policy does.
>>
>> Signed-off-by: Kevin Wangtao <kevin.wangtao@hisilicon.com>
>> ---
>> drivers/cpufreq/cpufreq.c | 2 ++
>> 1 file changed, 2 insertions(+)
>>
>> diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
>> index b79c532..8b33e08 100644
>> --- a/drivers/cpufreq/cpufreq.c
>> +++ b/drivers/cpufreq/cpufreq.c
>> @@ -697,6 +697,8 @@ static ssize_t store_##file_name \
>> struct cpufreq_policy new_policy; \
>> \
>> memcpy(&new_policy, policy, sizeof(*policy)); \
>> + new_policy->min = policy->user_policy.min; \
>> + new_policy->max = policy->user_policy.max; \
>
> It looks like you haven't even tried to build this, have you?
>
>> \
>> ret = sscanf(buf, "%u", &new_policy.object); \
>> if (ret != 1) \
>> --
>> 2.8.1
>>
>
> .
>
^ permalink raw reply
* [PATCH] thermal: int340x: Prevent error in reading trip hysteresis attribute
From: Srinivas Pandruvada @ 2018-05-26 1:10 UTC (permalink / raw)
To: rui.zhang, edubezval; +Cc: linux-pm, linux-kernel, Srinivas Pandruvada
Some of the INT340X devices may not have hysteresis defined in the ACPI
definition. In that case reading trip hysteresis results in error. This
spams logs of user space utilities.
In this case instead of returning error, just return hysteresis as 0,
which is correct as there is no hysteresis defined for the device.
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
drivers/thermal/int340x_thermal/int340x_thermal_zone.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.c b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c
index 145a5c53ff5c..dfdf6dbc2ddc 100644
--- a/drivers/thermal/int340x_thermal/int340x_thermal_zone.c
+++ b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c
@@ -147,9 +147,9 @@ static int int340x_thermal_get_trip_hyst(struct thermal_zone_device *zone,
status = acpi_evaluate_integer(d->adev->handle, "GTSH", NULL, &hyst);
if (ACPI_FAILURE(status))
- return -EIO;
-
- *temp = hyst * 100;
+ *temp = 0;
+ else
+ *temp = hyst * 100;
return 0;
}
--
2.17.0
^ permalink raw reply related
* [PATCH] PM / hibernate: Fix oops at snapshot_write().
From: Tetsuo Handa @ 2018-05-26 0:59 UTC (permalink / raw)
To: Rafael J. Wysocki, Pavel Machek, Len Brown
Cc: linux-pm, linux-kernel, Tetsuo Handa, syzbot
syzbot is reporting NULL pointer dereference at snapshot_write() [1].
This is because data->handle is zero-cleared by ioctl(SNAPSHOT_FREE).
Fix this by checking data_of(data->handle) != NULL before using it.
[1] https://syzkaller.appspot.com/bug?id=828a3c71bd344a6de8b6a31233d51a72099f27fd
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Reported-by: syzbot <syzbot+ae590932da6e45d6564d@syzkaller.appspotmail.com>
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <len.brown@intel.com>
Cc: Pavel Machek <pavel@ucw.cz>
---
kernel/power/user.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/kernel/power/user.c b/kernel/power/user.c
index 75c959d..abd2255 100644
--- a/kernel/power/user.c
+++ b/kernel/power/user.c
@@ -186,6 +186,11 @@ static ssize_t snapshot_write(struct file *filp, const char __user *buf,
res = PAGE_SIZE - pg_offp;
}
+ if (!data_of(data->handle)) {
+ res = -EINVAL;
+ goto unlock;
+ }
+
res = simple_write_to_buffer(data_of(data->handle), res, &pg_offp,
buf, count);
if (res > 0)
--
1.8.3.1
^ permalink raw reply related
* Re: [PATCH] cpufreq / CPPC: Add cpuinfo_cur_freq support for CPPC
From: Prakash, Prashanth @ 2018-05-25 21:00 UTC (permalink / raw)
To: George Cherian, George Cherian, linux-kernel, linux-pm; +Cc: rjw, viresh.kumar
In-Reply-To: <5ff2bebc-fc39-dc82-d7e6-0483e38f7d9c@caviumnetworks.com>
On 5/25/2018 12:27 AM, George Cherian wrote:
> Hi Prashanth,
>
> On 05/25/2018 12:55 AM, Prakash, Prashanth wrote:
>> Hi George,
>>
>> On 5/22/2018 5:42 AM, George Cherian wrote:
>>> Per Section 8.4.7.1.3 of ACPI 6.2, The platform provides performance
>>> feedback via set of performance counters. To determine the actual
>>> performance level delivered over time, OSPM may read a set of
>>> performance counters from the Reference Performance Counter Register
>>> and the Delivered Performance Counter Register.
>>>
>>> OSPM calculates the delivered performance over a given time period by
>>> taking a beginning and ending snapshot of both the reference and
>>> delivered performance counters, and calculating:
>>>
>>> delivered_perf = reference_perf X (delta of delivered_perf counter / delta of reference_perf counter).
>>>
>>> Implement the above and hook this to the cpufreq->get method.
>>>
>>> Signed-off-by: George Cherian <george.cherian@cavium.com>
>>> ---
>>> drivers/cpufreq/cppc_cpufreq.c | 44 ++++++++++++++++++++++++++++++++++++++++++
>>> 1 file changed, 44 insertions(+)
>>>
>>> diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
>>> index b15115a..a046915 100644
>>> --- a/drivers/cpufreq/cppc_cpufreq.c
>>> +++ b/drivers/cpufreq/cppc_cpufreq.c
>>> @@ -240,10 +240,54 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
>>> return ret;
>>> }
>>> +static int cppc_get_rate_from_fbctrs(struct cppc_perf_fb_ctrs fb_ctrs_t0,
>>> + struct cppc_perf_fb_ctrs fb_ctrs_t1)
>>> +{
>>> + u64 delta_reference, delta_delivered;
>>> + u64 reference_perf, ratio;
>>> +
>>> + reference_perf = fb_ctrs_t0.reference_perf;
>>> + if (fb_ctrs_t1.reference > fb_ctrs_t0.reference)
>>> + delta_reference = fb_ctrs_t1.reference - fb_ctrs_t0.reference;
>>> + else /* Counters would have wrapped-around */
>>> + delta_reference = ((u64)(~((u64)0)) - fb_ctrs_t0.reference) +
>>> + fb_ctrs_t1.reference;
>>> +
>>> + if (fb_ctrs_t1.delivered > fb_ctrs_t0.delivered)
>>> + delta_delivered = fb_ctrs_t1.delivered - fb_ctrs_t0.delivered;
>>> + else /* Counters would have wrapped-around */
>>> + delta_delivered = ((u64)(~((u64)0)) - fb_ctrs_t0.delivered) +
>>> + fb_ctrs_t1.delivered;
>> We need to check that the wraparound time is long enough to make sure that
>> the counters cannot wrap around more than once. We can register a get() api
>> only after checking that wraparound time value is reasonably high.
>>
>> I am not aware of any platforms where wraparound time is soo short, but
>> wouldn't hurt to check once during init.
> By design the wraparound time is a 64 bit counter, for that matter even
> all the feedback counters too are 64 bit counters. I don't see any
> chance in which the counters can wraparound twice in back to back reads.
> The only situation is in which system itself is running at a really high
> frequency. Even in that case today's spec is not sufficient to support the same.
The spec doesn't say these have to be 64bit registers. The wraparound
counter register is in spec to communicate the worst case(shortest)
counter rollover time.
As as mentioned before this is just a defensive check to make sure that
the platform has not set it to some very low number (which is allowed
by the spec).
>
>>> +
>>> + if (delta_reference) /* Check to avoid divide-by zero */
>>> + ratio = (delta_delivered * 1000) / delta_reference;
>> Why not just return the computed value here instead of *1000 and later /1000?
>> return (ref_per * delta_del) / delta_ref;
> Yes.
>>> + else
>>> + return -EINVAL;
>> Instead of EINVAL, i think we should return current frequency.
>>
> Sorry, I didn't get you, How do you calculate the current frequency?
> Did you mean reference performance?
I mean the performance that OSPM/Linux had requested earlier.
i.e the desired_perf
>
>> The counters can pause if CPUs are in idle state during our sampling interval, so
>> If the counters did not progress, it is reasonable to assume the delivered perf was
>> equal to desired perf.
> No, that is wrong. Here the check is for reference performance delta.
> This counter can never pause. In case of cpuidle only the delivered counters could pause. Delivered counters will pause only if the particular core enters power down mode, Otherwise we would be still clocking the core and we should be getting a delta across 2 sampling periods. In case if the reference counter is paused which by design is not correct then there is no point in returning reference performance numbers. That too is wrong. In case the low level FW is not updating the
> counters properly then it should be evident till Linux, instead of returning a bogus frequency.
Again you are describing how it works on a specific platform and not
how it is described in spec. Section 8.4.7.1.3.1.1 of ACPI 6.2 states
"The Reference Performance Counter Register counts at a fixed rate
any time the processor is active."
Implies the counters *may* pause in idle states - I can imagine an
implementation where you can keep this counter running and
account for it via delivered counter, but we cannot make any
assumptions outside of what the spec describes.
>>
>> Even if platform wanted to limit, since the CPUs were asleep(idle) we could not have
>> observed lower performance, so we will not throw off any logic that could be driven
>> using the returned value.
>>> +
>>> + return (reference_perf * ratio) / 1000;
>> This should be converted to KHz as cpufreq is not aware of CPPC abstract scale
> In our platform all performance registers are implemented in KHz. Because of which we never had an issue with conversion. I am not
> aware whether ACPI mandates to use any particular unit. How is that
> implemented in your platform? Just to avoid any extra conversion don't
> you feel it is better to always report in KHz from firmware.
Again think of spec not a specific platform :)
- The CPPC spec works on abstract scale and cpufreq works in KHz.
- The above computed value is in abstract scale
- The abstarct scale may be in KHz on your platform, but we cannot assume the
same about all the platforms
>
>>> +}
>>> +
>>> +static unsigned int cppc_cpufreq_get_rate(unsigned int cpunum)
>>> +{
>>> + struct cppc_perf_fb_ctrs fb_ctrs_t0 = {0}, fb_ctrs_t1 = {0};
>>> + int ret;
>>> +
>>> + ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t0);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + ret = cppc_get_perf_ctrs(cpunum, &fb_ctrs_t1);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + return cppc_get_rate_from_fbctrs(fb_ctrs_t0, fb_ctrs_t1);
>>> +}
>> We need to make sure that we get a reasonably sample so make sure the reported
>> performance is accurate.
>> The counters can capture short transient throttling/limiting, so by sampling a really
>> short duration of time we could return quite inaccurate measure of performance.
>>
> I would say it as a momentary thing only when the frequency is being ramped up/down.
This exact behavior would depend on how different limiting functions are implemented.
So this would vary from one platform to another.
>
>> We need to include some reasonable delay between the two calls. What is reasonable
>> is debatable - so this can be few(2-10) microseconds defined as default. If the same value
>> doesn't work for all the platforms, we might need to add a platform specific value.
>>
> cppc_get_perf_ctrs itself is a slow call, we have to format the CPC packet and ring a doorbell and then the response to be read from the shared registers. My initial implementation had a delay but in testing,
> I found that it was unnecessary to have such a delay. Can you please
> let me know whether it works without delay in your platform?
>
> Or else let me know whether udelay(10) is sufficient in between the
> calls.
Feedback counters need not be in PCC .
2us should be sufficient.
>>> +
>>> static struct cpufreq_driver cppc_cpufreq_driver = {
>>> .flags = CPUFREQ_CONST_LOOPS,
>>> .verify = cppc_verify_policy,
>>> .target = cppc_cpufreq_set_target,
>>> + .get = cppc_cpufreq_get_rate,
>>> .init = cppc_cpufreq_cpu_init,
>>> .stop_cpu = cppc_cpufreq_stop_cpu,
>>> .name = "cppc_cpufreq",
>>
^ permalink raw reply
* [PATCH 11/11] misc/throttler: Add Chrome OS EC throttler
From: Matthias Kaehlcke @ 2018-05-25 20:30 UTC (permalink / raw)
To: MyungJoo Ham
Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm, devicetree, linux-kernel,
Brian Norris, Douglas Anderson, Matthias Kaehlcke
In-Reply-To: <20180525203043.249193-1-mka@chromium.org>
The driver subscribes to throttling events from the Chrome OS
embedded controller and enables/disables system throttling based
on these events.
Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
---
drivers/misc/throttler/Kconfig | 15 +++
drivers/misc/throttler/Makefile | 1 +
drivers/misc/throttler/cros_ec_throttler.c | 122 +++++++++++++++++++++
3 files changed, 138 insertions(+)
create mode 100644 drivers/misc/throttler/cros_ec_throttler.c
diff --git a/drivers/misc/throttler/Kconfig b/drivers/misc/throttler/Kconfig
index ef8388f6bc0a..652b6817b75c 100644
--- a/drivers/misc/throttler/Kconfig
+++ b/drivers/misc/throttler/Kconfig
@@ -11,3 +11,18 @@ menuconfig THROTTLER
Note that you also need a event monitor module usually called
*_throttler.
+if THROTTLER
+
+config CROS_EC_THROTTLER
+ tristate "Throttler event monitor for the Chrome OS Embedded Controller"
+ default n
+ depends on MFD_CROS_EC
+ ---help---
+ This driver adds support to throttle the system in reaction to
+ Chrome OS EC events.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cros_ec_throttler.
+
+endif # THROTTLER
+
diff --git a/drivers/misc/throttler/Makefile b/drivers/misc/throttler/Makefile
index c8d920cee315..d9b2a77dabc9 100644
--- a/drivers/misc/throttler/Makefile
+++ b/drivers/misc/throttler/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_THROTTLER) += core.o
+obj-$(CONFIG_CROS_EC_THROTTLER) += cros_ec_throttler.o
diff --git a/drivers/misc/throttler/cros_ec_throttler.c b/drivers/misc/throttler/cros_ec_throttler.c
new file mode 100644
index 000000000000..ea6bc002d49c
--- /dev/null
+++ b/drivers/misc/throttler/cros_ec_throttler.c
@@ -0,0 +1,122 @@
+/*
+ * Driver for throttling triggered by EC events.
+ *
+ * Copyright (C) 2018 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/throttler.h>
+
+struct cros_ec_throttler {
+ struct cros_ec_device *ec;
+ struct throttler *throttler;
+ struct notifier_block nb;
+};
+
+static int cros_ec_throttler_event(struct notifier_block *nb,
+ unsigned long queued_during_suspend, void *_notify)
+{
+ struct cros_ec_throttler *cte =
+ container_of(nb, struct cros_ec_throttler, nb);
+ u32 host_event;
+
+ host_event = cros_ec_get_host_event(cte->ec);
+ if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_THROTTLE_START)) {
+ throttler_set_level(cte->throttler, 1);
+
+ return NOTIFY_OK;
+ } else if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_THROTTLE_STOP)) {
+ throttler_set_level(cte->throttler, 0);
+
+ return NOTIFY_OK;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int cros_ec_throttler_probe(struct platform_device *pdev)
+{
+ struct cros_ec_throttler *cte;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ if (!np) {
+ /* should never happen */
+ return -EINVAL;
+ }
+
+ cte = devm_kzalloc(dev, sizeof(*cte), GFP_KERNEL);
+ if (!cte)
+ return -ENOMEM;
+
+ cte->ec = dev_get_drvdata(pdev->dev.parent);
+
+ cte->throttler = throttler_setup(dev);
+ if (IS_ERR(cte->throttler))
+ return PTR_ERR(cte->throttler);
+
+ dev_set_drvdata(dev, cte);
+
+ cte->nb.notifier_call = cros_ec_throttler_event;
+ ret = blocking_notifier_chain_register(&cte->ec->event_notifier,
+ &cte->nb);
+ if (ret < 0) {
+ dev_err(dev, "failed to register notifier\n");
+ throttler_teardown(cte->throttler);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cros_ec_throttler_remove(struct platform_device *pdev)
+{
+ struct cros_ec_throttler *cte = platform_get_drvdata(pdev);
+
+ blocking_notifier_chain_unregister(&cte->ec->event_notifier,
+ &cte->nb);
+
+ throttler_teardown(cte->throttler);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id cros_ec_throttler_of_match[] = {
+ { .compatible = "google,cros-ec-throttler" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, cros_ec_throttler_of_match);
+#endif /* CONFIG_OF */
+
+static struct platform_driver cros_ec_throttler_driver = {
+ .driver = {
+ .name = "cros-ec-throttler",
+ .of_match_table = of_match_ptr(cros_ec_throttler_of_match),
+ },
+ .probe = cros_ec_throttler_probe,
+ .remove = cros_ec_throttler_remove,
+};
+
+module_platform_driver(cros_ec_throttler_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Matthias Kaehlcke <mka@chromium.org>");
+MODULE_DESCRIPTION("Chrome OS EC Throttler");
--
2.17.0.921.gf22659ad46-goog
^ permalink raw reply related
* [PATCH 10/11] dt-bindings: misc: add bindings for throttler
From: Matthias Kaehlcke @ 2018-05-25 20:30 UTC (permalink / raw)
To: MyungJoo Ham
Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm, devicetree, linux-kernel,
Brian Norris, Douglas Anderson, Matthias Kaehlcke
In-Reply-To: <20180525203043.249193-1-mka@chromium.org>
Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
---
.../devicetree/bindings/misc/throttler.txt | 41 +++++++++++++++++++
1 file changed, 41 insertions(+)
create mode 100644 Documentation/devicetree/bindings/misc/throttler.txt
diff --git a/Documentation/devicetree/bindings/misc/throttler.txt b/Documentation/devicetree/bindings/misc/throttler.txt
new file mode 100644
index 000000000000..92f13e94451a
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/throttler.txt
@@ -0,0 +1,41 @@
+Throttler driver
+
+The Throttler is used for non-thermal throttling of system components like
+CPUs or devfreq devices.
+
+
+Optional properties:
+--------------------
+- cpufreq: A sub-node which is container of only cpufreq
+ Type: sub-mode nodes, used to describe the throttling settings
+ for a CPU (or CPU group sharing the same policy).
+
+- devfreq: A sub-node which is container of only devfreq
+ Type: sub-mode nodes, used to describe the throttling settings
+ for a devfreq device.
+
+cpufreq node:
+=============
+
+Required properties:
+--------------------
+- cpu: The id of the CPU to be throttled.
+ Type: unsigned
+ Size: one cell
+
+- throttling-frequencies: Frequencies used for throttling, corresponding
+ Type: unsigned to throttling level 1, 2, ...
+ Size: array
+
+
+devfreq node:
+=============
+
+Required properties:
+--------------------
+- devfreq: A phandle of the devfreq device to be throttled.
+ Type: phandle
+
+- throttling-frequencies: Frequencies used for throttling, corresponding
+ Type: unsigned to throttling level 1, 2, ...
+ Size: array
--
2.17.0.921.gf22659ad46-goog
^ permalink raw reply related
* [PATCH 09/11] misc: throttler: Add core support for non-thermal throttling
From: Matthias Kaehlcke @ 2018-05-25 20:30 UTC (permalink / raw)
To: MyungJoo Ham
Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm, devicetree, linux-kernel,
Brian Norris, Douglas Anderson, Matthias Kaehlcke
In-Reply-To: <20180525203043.249193-1-mka@chromium.org>
The purpose of the throttler is to provide support for non-thermal
throttling. Throttling is triggered by external event, e.g. the
detection of a high battery discharge current, close to the OCP limit
of the battery. The throttler is only in charge of the throttling, not
the monitoring, which is done by another (possibly platform specific)
driver.
Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
---
drivers/misc/Kconfig | 1 +
drivers/misc/Makefile | 1 +
drivers/misc/throttler/Kconfig | 13 ++
drivers/misc/throttler/Makefile | 1 +
drivers/misc/throttler/core.c | 373 ++++++++++++++++++++++++++++++++
include/linux/throttler.h | 10 +
6 files changed, 399 insertions(+)
create mode 100644 drivers/misc/throttler/Kconfig
create mode 100644 drivers/misc/throttler/Makefile
create mode 100644 drivers/misc/throttler/core.c
create mode 100644 include/linux/throttler.h
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 5d713008749b..691d9625d83c 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -513,4 +513,5 @@ source "drivers/misc/echo/Kconfig"
source "drivers/misc/cxl/Kconfig"
source "drivers/misc/ocxl/Kconfig"
source "drivers/misc/cardreader/Kconfig"
+source "drivers/misc/throttler/Kconfig"
endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 20be70c3f118..01a1714dd2ad 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -57,3 +57,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
obj-$(CONFIG_OCXL) += ocxl/
obj-$(CONFIG_MISC_RTSX) += cardreader/
+obj-y += throttler/
diff --git a/drivers/misc/throttler/Kconfig b/drivers/misc/throttler/Kconfig
new file mode 100644
index 000000000000..ef8388f6bc0a
--- /dev/null
+++ b/drivers/misc/throttler/Kconfig
@@ -0,0 +1,13 @@
+menuconfig THROTTLER
+ bool "Throttler support"
+ default n
+ depends on OF
+ select CPU_FREQ
+ select PM_DEVFREQ
+ help
+ This option enables core support for non-thermal throttling of CPUs
+ and devfreq devices.
+
+ Note that you also need a event monitor module usually called
+ *_throttler.
+
diff --git a/drivers/misc/throttler/Makefile b/drivers/misc/throttler/Makefile
new file mode 100644
index 000000000000..c8d920cee315
--- /dev/null
+++ b/drivers/misc/throttler/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_THROTTLER) += core.o
diff --git a/drivers/misc/throttler/core.c b/drivers/misc/throttler/core.c
new file mode 100644
index 000000000000..c058d03212b8
--- /dev/null
+++ b/drivers/misc/throttler/core.c
@@ -0,0 +1,373 @@
+/*
+ * Core code for non-thermal throttling
+ *
+ * Copyright (C) 2018 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/cpufreq.h>
+#include <linux/devfreq.h>
+#include <linux/kernel.h>
+#include <linux/notifier.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+/*
+ * Non-thermal throttling: throttling of system components in response to
+ * external events (e.g. high battery discharge current).
+ *
+ * The throttler supports throttling through cpufreq and devfreq. Multiple
+ * levels of throttling can be configured. At level 0 no throttling is
+ * active on behalf of the throttler, for values > 0 throttling is typically
+ * configured to be increasingly aggressive with each level.
+ * The number of throttling levels is not limited by the throttler (though
+ * it is likely limited by the throttling devices). It is not necessary to
+ * configure the same number of levels for all throttling devices. If the
+ * requested throttling level for a device is higher than the maximum level
+ * of the device the throttler will sleect the maximum throttling level of
+ * the device.
+ *
+ * Non-thermal throttling is split in two parts:
+ *
+ * - throttler core
+ * - parses the thermal policy
+ * - applies throttling settings for a requested level of throttling
+ *
+ * - event monitor driver
+ * - monitors the events that trigger throttling
+ * - determines the throttling level (often limited to on/off)
+ * - requests throttler core to apply throttling settings
+ *
+ * It is possible for a system to have more than one throttler and the
+ * throttlers may make use of the same throttling devices, in case of
+ * conflicting settings for a device the more aggressive values will be
+ * applied.
+ *
+ */
+
+struct thrcfg {
+ uint32_t *freqs;
+ int num_levels;
+};
+
+struct cpufreq_thrdev {
+ uint32_t cpu;
+ struct thrcfg cfg;
+};
+
+struct devfreq_thrdev {
+ struct devfreq *devfreq;
+ struct thrcfg cfg;
+ struct throttler *thr;
+ struct notifier_block nb;
+};
+
+struct __thr_cpufreq {
+ struct cpufreq_thrdev *devs;
+ int ndevs;
+ struct notifier_block nb;
+};
+
+struct __thr_devfreq {
+ struct devfreq_thrdev *devs;
+ int ndevs;
+};
+
+struct throttler {
+ struct device *dev;
+ int level;
+ struct __thr_cpufreq cpufreq;
+ struct __thr_devfreq devfreq;
+};
+
+static unsigned long thr_get_throttling_freq(struct thrcfg *cfg, int level)
+{
+ if (level == 0 ) {
+ WARN(true, "level == 0");
+ return 0;
+ }
+
+ if (level <= cfg->num_levels)
+ return cfg->freqs[level - 1];
+ else
+ return cfg->freqs[cfg->num_levels - 1];
+}
+
+static int thr_cpufreq_event(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct throttler *thr =
+ container_of(nb, struct throttler, cpufreq.nb);
+ struct cpufreq_policy *policy = data;
+ struct cpufreq_thrdev *ctd;
+ int i;
+
+ if ((event != CPUFREQ_ADJUST) || (thr->level == 0))
+ return NOTIFY_DONE;
+
+ for (i = 0; i < thr->cpufreq.ndevs; i++) {
+ ctd = &thr->cpufreq.devs[i];
+
+ if (ctd->cpu == policy->cpu) {
+ unsigned long clamp_freq =
+ thr_get_throttling_freq(&ctd->cfg, thr->level);
+ if (clamp_freq < policy->max) {
+ cpufreq_verify_within_limits(policy, 0, clamp_freq);
+ }
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int thr_devfreq_event(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct devfreq_thrdev *dtd =
+ container_of(nb, struct devfreq_thrdev, nb);
+ struct throttler *thr = dtd->thr;
+ struct devfreq_policy *policy = data;
+ unsigned long clamp_freq;
+
+ if ((event != DEVFREQ_ADJUST) || (thr->level == 0))
+ return NOTIFY_DONE;
+
+ clamp_freq = thr_get_throttling_freq(&dtd->cfg, thr->level);
+ if (clamp_freq < policy->max)
+ devfreq_verify_within_limits(policy, 0, clamp_freq);
+
+ return NOTIFY_DONE;
+}
+
+static void thr_cpufreq_update_policy(struct throttler *thr)
+{
+ int i;
+
+ for (i = 0; i < thr->cpufreq.ndevs; i++) {
+ struct cpufreq_thrdev *ctd = &thr->cpufreq.devs[i];
+ struct cpufreq_policy *policy = cpufreq_cpu_get(ctd->cpu);
+
+ if (!policy) {
+ dev_warn(thr->dev, "CPU%d does have no cpufreq policy!\n", ctd->cpu);
+ continue;
+ }
+
+ cpufreq_update_policy(ctd->cpu);
+ cpufreq_cpu_put(policy);
+ }
+}
+
+static int thr_parse_thrcfg(struct throttler *thr,
+ struct device_node *np, struct thrcfg *cfg) {
+ int err;
+
+ cfg->num_levels =
+ of_property_count_u32_elems(np, "throttling-frequencies");
+ if (cfg->num_levels < 0) {
+ pr_err("%s: failed to determine number of throttling frequencies\n",
+ np->full_name);
+ return cfg->num_levels;
+ }
+
+ cfg->freqs = devm_kzalloc(thr->dev,
+ cfg->num_levels * sizeof(u32), GFP_KERNEL);
+ if (!cfg->freqs)
+ return -ENOMEM;
+
+ err = of_property_read_u32_array(np, "throttling-frequencies",
+ cfg->freqs, cfg->num_levels);
+ if (err) {
+ pr_err("%s: failed to read throttling frequencies\n", np->full_name);
+ return err;
+ }
+
+ return 0;
+}
+
+static struct devfreq *thr_find_devfreq_dev(struct throttler *thr,
+ struct device_node *np_df) {
+ struct device_node *node;
+ struct platform_device *pdev;
+
+ node = of_parse_phandle(np_df, "device", 0);
+ if (!node) {
+ pr_err("%s: failed to get devfreq parent device\n",
+ np_df->full_name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdev = of_find_device_by_node(node);
+ if (!pdev) {
+ pr_err("%s: could not find devfreq parent device\n",
+ node->full_name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return dev_to_devfreq(&pdev->dev);
+}
+
+static int thr_parse_dt(struct throttler *thr, struct device_node *np)
+{
+ struct device_node *node, *child;
+ int err, i;
+
+ node = of_get_child_by_name(np, "cpufreq");
+ if (node) {
+ thr->cpufreq.ndevs = of_get_child_count(node);
+ thr->cpufreq.devs = devm_kzalloc(thr->dev,
+ sizeof(*thr->cpufreq.devs) * thr->cpufreq.ndevs,
+ GFP_KERNEL);
+
+ i = 0;
+ for_each_child_of_node(node, child) {
+ struct cpufreq_thrdev *ctd = &thr->cpufreq.devs[i];
+
+ err = of_property_read_u32(child, "cpu", &ctd->cpu);
+ if (err) {
+ pr_err("%s: failed to read CPU id\n", child->full_name);
+ return err;
+ }
+
+ err = thr_parse_thrcfg(thr, child, &ctd->cfg);
+ if (err)
+ return err;
+
+ i++;
+ }
+ }
+
+ node = of_get_child_by_name(np, "devfreq");
+ if (node) {
+ thr->devfreq.ndevs = of_get_child_count(node);
+ thr->devfreq.devs = devm_kzalloc(thr->dev,
+ sizeof(*thr->devfreq.devs) * thr->devfreq.ndevs,
+ GFP_KERNEL);
+
+ i = 0;
+ for_each_child_of_node(node, child) {
+ struct devfreq_thrdev *dtd = &thr->devfreq.devs[i];
+
+ dtd->thr = thr;
+
+ dtd->devfreq = thr_find_devfreq_dev(thr, child);
+ if (IS_ERR(dtd->devfreq))
+ return PTR_ERR(dtd->devfreq);
+
+ err = thr_parse_thrcfg(thr, child, &dtd->cfg);
+ if (err)
+ return err;
+
+ i++;
+ }
+ }
+
+ return 0;
+}
+
+static void thr_update_devfreq(struct devfreq *devfreq)
+{
+ mutex_lock(&devfreq->lock);
+ update_devfreq(devfreq);
+ mutex_unlock(&devfreq->lock);
+}
+
+void throttler_set_level(struct throttler *thr, int level)
+{
+ int i;
+
+ if (level == thr->level)
+ return;
+
+ dev_dbg(thr->dev, "throttling level: %d\n", level);
+ thr->level = level;
+
+ if (thr->cpufreq.ndevs > 0)
+ thr_cpufreq_update_policy(thr);
+
+ if (thr->devfreq.ndevs > 0)
+ for (i = 0; i < thr->devfreq.ndevs; i++)
+ thr_update_devfreq(thr->devfreq.devs[i].devfreq);
+}
+EXPORT_SYMBOL_GPL(throttler_set_level);
+
+struct throttler *throttler_setup(struct device *dev)
+{
+ struct throttler *thr;
+ struct device_node *np = dev->of_node;
+ int err, i;
+
+ if (!np)
+ /* should never happen */
+ return ERR_PTR(-EINVAL);
+
+ thr = devm_kzalloc(dev, sizeof(*thr), GFP_KERNEL);
+ if (!thr)
+ return ERR_PTR(-ENOMEM);
+
+ thr->dev = dev;
+
+ err = thr_parse_dt(thr, np);
+ if (err)
+ return ERR_PTR(err);
+
+ if (thr->cpufreq.ndevs > 0) {
+ thr->cpufreq.nb.notifier_call = thr_cpufreq_event;
+ err = cpufreq_register_notifier(&thr->cpufreq.nb,
+ CPUFREQ_POLICY_NOTIFIER);
+ if (err < 0) {
+ dev_err(dev, "failed to register cpufreq notifier\n");
+ return ERR_PTR(err);
+ }
+ }
+
+ for (i = 0; i < thr->devfreq.ndevs; i++) {
+ struct devfreq_thrdev *dtd = &thr->devfreq.devs[i];
+
+ dtd->nb.notifier_call = thr_devfreq_event;
+ err = devm_devfreq_register_notifier(dev, dtd->devfreq,
+ &dtd->nb, DEVFREQ_POLICY_NOTIFIER);
+ if (err < 0) {
+ dev_err(dev, "failed to register devfreq notifier\n");
+ goto err_cpufreq_unregister;
+ }
+ }
+
+ return thr;
+
+err_cpufreq_unregister:
+ if (thr->cpufreq.ndevs > 0)
+ cpufreq_unregister_notifier(&thr->cpufreq.nb,
+ CPUFREQ_POLICY_NOTIFIER);
+
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(throttler_setup);
+
+void throttler_teardown(struct throttler *thr)
+{
+ int i;
+
+ thr->level = 0;
+
+ if (thr->cpufreq.ndevs > 0) {
+ thr_cpufreq_update_policy(thr);
+
+ cpufreq_unregister_notifier(&thr->cpufreq.nb,
+ CPUFREQ_POLICY_NOTIFIER);
+ }
+
+ if (thr->devfreq.ndevs > 0)
+ for (i = 0; i < thr->devfreq.ndevs; i++)
+ thr_update_devfreq(thr->devfreq.devs[i].devfreq);
+}
+EXPORT_SYMBOL_GPL(throttler_teardown);
diff --git a/include/linux/throttler.h b/include/linux/throttler.h
new file mode 100644
index 000000000000..cab8c466da4b
--- /dev/null
+++ b/include/linux/throttler.h
@@ -0,0 +1,10 @@
+#ifndef __LINUX_THROTTLER_H__
+#define __LINUX_THROTTLER_H__
+
+struct throttler;
+
+extern struct throttler *throttler_setup(struct device *dev);
+extern void throttler_teardown(struct throttler *thr);
+extern void throttler_set_level(struct throttler *thr, int level);
+
+#endif /* __LINUX_THROTTLER_H__ */
--
2.17.0.921.gf22659ad46-goog
^ permalink raw reply related
* [PATCH 08/11] PM / devfreq: Make update_devfreq() public
From: Matthias Kaehlcke @ 2018-05-25 20:30 UTC (permalink / raw)
To: MyungJoo Ham
Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm, devicetree, linux-kernel,
Brian Norris, Douglas Anderson, Matthias Kaehlcke
In-Reply-To: <20180525203043.249193-1-mka@chromium.org>
Currently update_devfreq() is only visible to devfreq governors outside
of devfreq.c. Make it public to allow drivers that adjust devfreq policies
to cause a re-evaluation of the frequency after a policy change.
Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
---
drivers/devfreq/governor.h | 3 ---
include/linux/devfreq.h | 8 ++++++++
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h
index cfc50a61a90d..608e7549465b 100644
--- a/drivers/devfreq/governor.h
+++ b/drivers/devfreq/governor.h
@@ -54,9 +54,6 @@ struct devfreq_governor {
unsigned int event, void *data);
};
-/* Caution: devfreq->lock must be locked before calling update_devfreq */
-extern int update_devfreq(struct devfreq *devfreq);
-
extern void devfreq_monitor_start(struct devfreq *devfreq);
extern void devfreq_monitor_stop(struct devfreq *devfreq);
extern void devfreq_monitor_suspend(struct devfreq *devfreq);
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index 4a6ed61bbe49..561ab1d5e8d6 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -222,6 +222,14 @@ extern void devm_devfreq_remove_device(struct device *dev,
extern int devfreq_suspend_device(struct devfreq *devfreq);
extern int devfreq_resume_device(struct devfreq *devfreq);
+/**
+ * update_devfreq() - Reevaluate the device and configure frequency
+ * @devfreq: the devfreq device
+ *
+ * Note: devfreq->lock must be held
+ */
+extern int update_devfreq(struct devfreq *devfreq);
+
/* Helper functions for devfreq user device driver with OPP. */
extern struct dev_pm_opp *devfreq_recommended_opp(struct device *dev,
unsigned long *freq, u32 flags);
--
2.17.0.921.gf22659ad46-goog
^ permalink raw reply related
* [PATCH 07/11] PM / devfreg: Add support policy notifiers
From: Matthias Kaehlcke @ 2018-05-25 20:30 UTC (permalink / raw)
To: MyungJoo Ham
Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm, devicetree, linux-kernel,
Brian Norris, Douglas Anderson, Matthias Kaehlcke
In-Reply-To: <20180525203043.249193-1-mka@chromium.org>
Policy notifiers are called before a frequency change and may narrow
the min/max frequency range in devfreq_policy, which is used to adjust
the target frequency if it is beyond this range.
Also add a few helpers:
- devfreq_verify_within_[dev_]limits()
- should be used by the notifiers for policy adjustments.
- dev_to_devfreq()
- lookup a devfreq strict from a device pointer
Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
---
drivers/devfreq/devfreq.c | 47 +++++++++++++++++++++-------
include/linux/devfreq.h | 66 +++++++++++++++++++++++++++++++++++++++
2 files changed, 102 insertions(+), 11 deletions(-)
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 7fd55b49c8ae..939b91f3a7e3 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -72,6 +72,20 @@ static struct devfreq *find_device_devfreq(struct device *dev)
return ERR_PTR(-ENODEV);
}
+/**
+ * dev_to_devfreq() - find devfreq struct using device pointer
+ * @dev: device pointer used to lookup device devfreq.
+ */
+struct devfreq *dev_to_devfreq(struct device *dev) {
+ struct devfreq *devfreq;
+
+ mutex_lock(&devfreq_list_lock);
+ devfreq = find_device_devfreq(dev);
+ mutex_unlock(&devfreq_list_lock);
+
+ return devfreq;
+}
+
static unsigned long find_available_min_freq(struct devfreq *devfreq)
{
struct dev_pm_opp *opp;
@@ -269,20 +283,21 @@ int update_devfreq(struct devfreq *devfreq)
if (!policy->governor)
return -EINVAL;
+ policy->min = policy->devinfo.min_freq;
+ policy->max = policy->devinfo.max_freq;
+
+ srcu_notifier_call_chain(&devfreq->policy_notifier_list,
+ DEVFREQ_ADJUST, policy);
+
/* Reevaluate the proper frequency */
err = policy->governor->get_target_freq(devfreq, &freq);
if (err)
return err;
- /*
- * Adjust the frequency with user freq, QoS and available freq.
- *
- * List from the highest priority
- * max_freq
- * min_freq
- */
- max_freq = MIN(policy->devinfo.max_freq, policy->user.max_freq);
- min_freq = MAX(policy->devinfo.min_freq, policy->user.min_freq);
+ /* Adjust the frequency */
+
+ max_freq = MIN(policy->max, policy->user.max_freq);
+ min_freq = MAX(policy->min, policy->user.min_freq);
if (freq < min_freq) {
freq = min_freq;
@@ -645,6 +660,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
devfreq->last_stat_updated = jiffies;
srcu_init_notifier_head(&devfreq->transition_notifier_list);
+ srcu_init_notifier_head(&devfreq->policy_notifier_list);
mutex_unlock(&devfreq->lock);
@@ -1432,7 +1448,7 @@ EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier);
* devfreq_register_notifier() - Register a driver with devfreq
* @devfreq: The devfreq object.
* @nb: The notifier block to register.
- * @list: DEVFREQ_TRANSITION_NOTIFIER.
+ * @list: DEVFREQ_TRANSITION_NOTIFIER or DEVFREQ_POLICY_NOTIFIER.
*/
int devfreq_register_notifier(struct devfreq *devfreq,
struct notifier_block *nb,
@@ -1448,6 +1464,10 @@ int devfreq_register_notifier(struct devfreq *devfreq,
ret = srcu_notifier_chain_register(
&devfreq->transition_notifier_list, nb);
break;
+ case DEVFREQ_POLICY_NOTIFIER:
+ ret = srcu_notifier_chain_register(
+ &devfreq->policy_notifier_list, nb);
+ break;
default:
ret = -EINVAL;
}
@@ -1460,7 +1480,7 @@ EXPORT_SYMBOL(devfreq_register_notifier);
* devfreq_unregister_notifier() - Unregister a driver with devfreq
* @devfreq: The devfreq object.
* @nb: The notifier block to be unregistered.
- * @list: DEVFREQ_TRANSITION_NOTIFIER.
+ * @list: DEVFREQ_TRANSITION_NOTIFIER or DEVFREQ_POLICY_NOTIFIER.
*/
int devfreq_unregister_notifier(struct devfreq *devfreq,
struct notifier_block *nb,
@@ -1476,6 +1496,11 @@ int devfreq_unregister_notifier(struct devfreq *devfreq,
ret = srcu_notifier_chain_unregister(
&devfreq->transition_notifier_list, nb);
break;
+ case DEVFREQ_POLICY_NOTIFIER:
+ ret = srcu_notifier_chain_unregister(
+ &devfreq->policy_notifier_list, nb);
+ break;
+
default:
ret = -EINVAL;
}
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index 9bf23b976f4d..4a6ed61bbe49 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -33,6 +33,10 @@
#define DEVFREQ_PRECHANGE (0)
#define DEVFREQ_POSTCHANGE (1)
+#define DEVFREQ_POLICY_NOTIFIER 1
+
+#define DEVFREQ_ADJUST 0
+
struct devfreq;
struct devfreq_governor;
@@ -121,12 +125,16 @@ struct devfreq_freq_limits {
/**
* struct devfreq_policy - Devfreq policy
+ * @min: minimum frequency (adjustable by policy notifiers)
+ * @min: maximum frequency (adjustable by policy notifiers)
* @user: frequency limits requested by the user
* @devinfo: frequency limits of the device (available OPPs)
* @governor: method how to choose frequency based on the usage.
* @governor_name: devfreq governor name for use with this devfreq
*/
struct devfreq_policy {
+ unsigned long min;
+ unsigned long max;
struct devfreq_freq_limits user;
struct devfreq_freq_limits devinfo;
const struct devfreq_governor *governor;
@@ -155,6 +163,7 @@ struct devfreq_policy {
* @time_in_state: Statistics of devfreq states
* @last_stat_updated: The last time stat updated
* @transition_notifier_list: list head of DEVFREQ_TRANSITION_NOTIFIER notifier
+ * @policy_notifier_list: list head of DEVFREQ_POLICY_NOTIFIER notifier
*
* This structure stores the devfreq information for a give device.
*
@@ -188,6 +197,7 @@ struct devfreq {
unsigned long last_stat_updated;
struct srcu_notifier_head transition_notifier_list;
+ struct srcu_notifier_head policy_notifier_list;
};
struct devfreq_freqs {
@@ -240,6 +250,46 @@ extern void devm_devfreq_unregister_notifier(struct device *dev,
extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev,
int index);
+/**
+ * devfreq_verify_within_limits() - Adjust a devfreq policy if needed to make
+ * sure its min/max values are within a
+ * specified range.
+ * @policy: the policy
+ * @min: the minimum frequency
+ * @max: the maximum frequency
+ */
+static inline void devfreq_verify_within_limits(struct devfreq_policy *policy,
+ unsigned int min, unsigned int max)
+{
+ if (policy->min < min)
+ policy->min = min;
+ if (policy->max < min)
+ policy->max = min;
+ if (policy->min > max)
+ policy->min = max;
+ if (policy->max > max)
+ policy->max = max;
+ if (policy->min > policy->max)
+ policy->min = policy->max;
+ return;
+}
+
+/**
+ * devfreq_verify_within_dev_limits() - Adjust a devfreq policy if needed to
+ * make sure its min/max values are within
+ * the frequency range supported by the
+ * device.
+ * @policy: the policy
+ */
+static inline void
+devfreq_verify_within_dev_limits(struct devfreq_policy *policy)
+{
+ devfreq_verify_within_limits(policy, policy->devinfo.min_freq,
+ policy->devinfo.max_freq);
+}
+
+struct devfreq *dev_to_devfreq(struct device *dev);
+
#if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND)
/**
* struct devfreq_simple_ondemand_data - void *data fed to struct devfreq
@@ -394,10 +444,26 @@ static inline struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev,
return ERR_PTR(-ENODEV);
}
+static inline void devfreq_verify_within_limits(struct defreq_policy *policy,
+ unsigned int min, unsigned int max)
+{
+}
+
+static inline void
+devfreq_verify_within_dev_limits(struct cpufreq_policy *policy)
+{
+}
+
static inline int devfreq_update_stats(struct devfreq *df)
{
return -EINVAL;
}
+
+static inline struct devfreq *dev_to_devfreq(struct device *dev)
+{
+ return NULL;
+}
+
#endif /* CONFIG_PM_DEVFREQ */
#endif /* __LINUX_DEVFREQ_H__ */
--
2.17.0.921.gf22659ad46-goog
^ permalink raw reply related
* [PATCH 06/11] PM / devfreq: Add struct devfreq_policy
From: Matthias Kaehlcke @ 2018-05-25 20:30 UTC (permalink / raw)
To: MyungJoo Ham
Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm, devicetree, linux-kernel,
Brian Norris, Douglas Anderson, Matthias Kaehlcke
In-Reply-To: <20180525203043.249193-1-mka@chromium.org>
Move variables related with devfreq policy changes from struct devfreq
to the new struct devfreq_policy and add a policy field to struct devfreq.
The following variables are moved:
df->min/max_freq => p->user.min/max_freq
df->scaling_min/max_freq => p->devinfo.min/max_freq
df->governor => p->governor
df->governor_name => p->governor_name
Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
---
drivers/devfreq/devfreq.c | 136 ++++++++++++----------
drivers/devfreq/governor_passive.c | 4 +-
drivers/devfreq/governor_performance.c | 2 +-
drivers/devfreq/governor_powersave.c | 2 +-
drivers/devfreq/governor_simpleondemand.c | 7 +-
include/linux/devfreq.h | 38 ++++--
6 files changed, 108 insertions(+), 81 deletions(-)
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 67da4e7b486b..7fd55b49c8ae 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -255,6 +255,7 @@ static int devfreq_notify_transition(struct devfreq *devfreq,
*/
int update_devfreq(struct devfreq *devfreq)
{
+ struct devfreq_policy *policy = &devfreq->policy;
struct devfreq_freqs freqs;
unsigned long freq, cur_freq, min_freq, max_freq;
int err = 0;
@@ -265,11 +266,11 @@ int update_devfreq(struct devfreq *devfreq)
return -EINVAL;
}
- if (!devfreq->governor)
+ if (!policy->governor)
return -EINVAL;
/* Reevaluate the proper frequency */
- err = devfreq->governor->get_target_freq(devfreq, &freq);
+ err = policy->governor->get_target_freq(devfreq, &freq);
if (err)
return err;
@@ -280,8 +281,8 @@ int update_devfreq(struct devfreq *devfreq)
* max_freq
* min_freq
*/
- max_freq = MIN(devfreq->scaling_max_freq, devfreq->max_freq);
- min_freq = MAX(devfreq->scaling_min_freq, devfreq->min_freq);
+ max_freq = MIN(policy->devinfo.max_freq, policy->user.max_freq);
+ min_freq = MAX(policy->devinfo.min_freq, policy->user.min_freq);
if (freq < min_freq) {
freq = min_freq;
@@ -493,18 +494,19 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
void *devp)
{
struct devfreq *devfreq = container_of(nb, struct devfreq, nb);
+ struct devfreq_policy *policy = &devfreq->policy;
int ret;
mutex_lock(&devfreq->lock);
- devfreq->scaling_min_freq = find_available_min_freq(devfreq);
- if (!devfreq->scaling_min_freq) {
+ policy->devinfo.min_freq = find_available_min_freq(devfreq);
+ if (!policy->devinfo.min_freq) {
mutex_unlock(&devfreq->lock);
return -EINVAL;
}
- devfreq->scaling_max_freq = find_available_max_freq(devfreq);
- if (!devfreq->scaling_max_freq) {
+ policy->devinfo.max_freq = find_available_max_freq(devfreq);
+ if (!policy->devinfo.max_freq) {
mutex_unlock(&devfreq->lock);
return -EINVAL;
}
@@ -524,6 +526,7 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
static void devfreq_dev_release(struct device *dev)
{
struct devfreq *devfreq = to_devfreq(dev);
+ struct devfreq_policy *policy = &devfreq->policy;
mutex_lock(&devfreq_list_lock);
if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) {
@@ -534,8 +537,8 @@ static void devfreq_dev_release(struct device *dev)
list_del(&devfreq->node);
mutex_unlock(&devfreq_list_lock);
- if (devfreq->governor)
- devfreq->governor->event_handler(devfreq,
+ if (policy->governor)
+ policy->governor->event_handler(devfreq,
DEVFREQ_GOV_STOP, NULL);
if (devfreq->profile->exit)
@@ -559,6 +562,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
void *data)
{
struct devfreq *devfreq;
+ struct devfreq_policy *policy;
struct devfreq_governor *governor;
static atomic_t devfreq_no = ATOMIC_INIT(-1);
int err = 0;
@@ -584,13 +588,14 @@ struct devfreq *devfreq_add_device(struct device *dev,
goto err_out;
}
+ policy = &devfreq->policy;
mutex_init(&devfreq->lock);
mutex_lock(&devfreq->lock);
devfreq->dev.parent = dev;
devfreq->dev.class = devfreq_class;
devfreq->dev.release = devfreq_dev_release;
devfreq->profile = profile;
- strncpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN);
+ strncpy(policy->governor_name, governor_name, DEVFREQ_NAME_LEN);
devfreq->previous_freq = profile->initial_freq;
devfreq->last_status.current_frequency = profile->initial_freq;
devfreq->data = data;
@@ -604,21 +609,21 @@ struct devfreq *devfreq_add_device(struct device *dev,
mutex_lock(&devfreq->lock);
}
- devfreq->scaling_min_freq = find_available_min_freq(devfreq);
- if (!devfreq->scaling_min_freq) {
+ policy->devinfo.min_freq = find_available_min_freq(devfreq);
+ if (!policy->devinfo.min_freq) {
mutex_unlock(&devfreq->lock);
err = -EINVAL;
goto err_dev;
}
- devfreq->min_freq = devfreq->scaling_min_freq;
+ policy->user.min_freq = policy->devinfo.min_freq;
- devfreq->scaling_max_freq = find_available_max_freq(devfreq);
- if (!devfreq->scaling_max_freq) {
+ policy->devinfo.max_freq = find_available_max_freq(devfreq);
+ if (!policy->devinfo.max_freq) {
mutex_unlock(&devfreq->lock);
err = -EINVAL;
goto err_dev;
}
- devfreq->max_freq = devfreq->scaling_max_freq;
+ policy->user.max_freq = policy->devinfo.max_freq;
dev_set_name(&devfreq->dev, "devfreq%d",
atomic_inc_return(&devfreq_no));
@@ -646,7 +651,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
mutex_lock(&devfreq_list_lock);
list_add(&devfreq->node, &devfreq_list);
- governor = find_devfreq_governor(devfreq->governor_name);
+ governor = find_devfreq_governor(policy->governor_name);
if (IS_ERR(governor)) {
dev_err(dev, "%s: Unable to find governor for the device\n",
__func__);
@@ -654,9 +659,9 @@ struct devfreq *devfreq_add_device(struct device *dev,
goto err_init;
}
- devfreq->governor = governor;
- err = devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_START,
- NULL);
+ policy->governor = governor;
+ err = policy->governor->event_handler(devfreq, DEVFREQ_GOV_START,
+ NULL);
if (err) {
dev_err(dev, "%s: Unable to start governor for the device\n",
__func__);
@@ -817,10 +822,10 @@ int devfreq_suspend_device(struct devfreq *devfreq)
if (!devfreq)
return -EINVAL;
- if (!devfreq->governor)
+ if (!devfreq->policy.governor)
return 0;
- return devfreq->governor->event_handler(devfreq,
+ return devfreq->policy.governor->event_handler(devfreq,
DEVFREQ_GOV_SUSPEND, NULL);
}
EXPORT_SYMBOL(devfreq_suspend_device);
@@ -838,10 +843,10 @@ int devfreq_resume_device(struct devfreq *devfreq)
if (!devfreq)
return -EINVAL;
- if (!devfreq->governor)
+ if (!devfreq->policy.governor)
return 0;
- return devfreq->governor->event_handler(devfreq,
+ return devfreq->policy.governor->event_handler(devfreq,
DEVFREQ_GOV_RESUME, NULL);
}
EXPORT_SYMBOL(devfreq_resume_device);
@@ -875,30 +880,31 @@ int devfreq_add_governor(struct devfreq_governor *governor)
list_for_each_entry(devfreq, &devfreq_list, node) {
int ret = 0;
struct device *dev = devfreq->dev.parent;
+ struct devfreq_policy *policy = &devfreq->policy;
- if (!strncmp(devfreq->governor_name, governor->name,
+ if (!strncmp(policy->governor_name, governor->name,
DEVFREQ_NAME_LEN)) {
/* The following should never occur */
- if (devfreq->governor) {
+ if (policy->governor) {
dev_warn(dev,
"%s: Governor %s already present\n",
- __func__, devfreq->governor->name);
- ret = devfreq->governor->event_handler(devfreq,
+ __func__, policy->governor->name);
+ ret = policy->governor->event_handler(devfreq,
DEVFREQ_GOV_STOP, NULL);
if (ret) {
dev_warn(dev,
"%s: Governor %s stop = %d\n",
__func__,
- devfreq->governor->name, ret);
+ policy->governor->name, ret);
}
/* Fall through */
}
- devfreq->governor = governor;
- ret = devfreq->governor->event_handler(devfreq,
+ policy->governor = governor;
+ ret = policy->governor->event_handler(devfreq,
DEVFREQ_GOV_START, NULL);
if (ret) {
dev_warn(dev, "%s: Governor %s start=%d\n",
- __func__, devfreq->governor->name,
+ __func__, policy->governor->name,
ret);
}
}
@@ -937,24 +943,25 @@ int devfreq_remove_governor(struct devfreq_governor *governor)
list_for_each_entry(devfreq, &devfreq_list, node) {
int ret;
struct device *dev = devfreq->dev.parent;
+ struct devfreq_policy *policy = &devfreq->policy;
- if (!strncmp(devfreq->governor_name, governor->name,
+ if (!strncmp(policy->governor_name, governor->name,
DEVFREQ_NAME_LEN)) {
/* we should have a devfreq governor! */
- if (!devfreq->governor) {
+ if (!policy->governor) {
dev_warn(dev, "%s: Governor %s NOT present\n",
__func__, governor->name);
continue;
/* Fall through */
}
- ret = devfreq->governor->event_handler(devfreq,
+ ret = policy->governor->event_handler(devfreq,
DEVFREQ_GOV_STOP, NULL);
if (ret) {
dev_warn(dev, "%s: Governor %s stop=%d\n",
- __func__, devfreq->governor->name,
+ __func__, policy->governor->name,
ret);
}
- devfreq->governor = NULL;
+ policy->governor = NULL;
}
}
@@ -969,16 +976,17 @@ EXPORT_SYMBOL(devfreq_remove_governor);
static ssize_t governor_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- if (!to_devfreq(dev)->governor)
+ if (!to_devfreq(dev)->policy.governor)
return -EINVAL;
- return sprintf(buf, "%s\n", to_devfreq(dev)->governor->name);
+ return sprintf(buf, "%s\n", to_devfreq(dev)->policy.governor->name);
}
static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct devfreq *df = to_devfreq(dev);
+ struct devfreq_policy *policy = &df->policy;
int ret;
char str_governor[DEVFREQ_NAME_LEN + 1];
struct devfreq_governor *governor;
@@ -993,29 +1001,29 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
ret = PTR_ERR(governor);
goto out;
}
- if (df->governor == governor) {
+ if (policy->governor == governor) {
ret = 0;
goto out;
- } else if ((df->governor && df->governor->immutable) ||
+ } else if ((policy->governor && policy->governor->immutable) ||
governor->immutable) {
ret = -EINVAL;
goto out;
}
- if (df->governor) {
- ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
+ if (policy->governor) {
+ ret = policy->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
if (ret) {
dev_warn(dev, "%s: Governor %s not stopped(%d)\n",
- __func__, df->governor->name, ret);
+ __func__, policy->governor->name, ret);
goto out;
}
}
- df->governor = governor;
- strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN);
- ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
+ policy->governor = governor;
+ strncpy(policy->governor_name, governor->name, DEVFREQ_NAME_LEN);
+ ret = policy->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
if (ret)
dev_warn(dev, "%s: Governor %s not started(%d)\n",
- __func__, df->governor->name, ret);
+ __func__, policy->governor->name, ret);
out:
mutex_unlock(&devfreq_list_lock);
@@ -1030,6 +1038,7 @@ static ssize_t available_governors_show(struct device *d,
char *buf)
{
struct devfreq *df = to_devfreq(d);
+ struct devfreq_policy *policy = &df->policy;
ssize_t count = 0;
mutex_lock(&devfreq_list_lock);
@@ -1038,9 +1047,9 @@ static ssize_t available_governors_show(struct device *d,
* The devfreq with immutable governor (e.g., passive) shows
* only own governor.
*/
- if (df->governor->immutable) {
+ if (policy->governor->immutable) {
count = scnprintf(&buf[count], DEVFREQ_NAME_LEN,
- "%s ", df->governor_name);
+ "%s ", policy->governor_name);
/*
* The devfreq device shows the registered governor except for
* immutable governors such as passive governor .
@@ -1100,17 +1109,18 @@ static ssize_t polling_interval_store(struct device *dev,
const char *buf, size_t count)
{
struct devfreq *df = to_devfreq(dev);
+ struct devfreq_policy *policy = &df->policy;
unsigned int value;
int ret;
- if (!df->governor)
+ if (!policy->governor)
return -EINVAL;
ret = sscanf(buf, "%u", &value);
if (ret != 1)
return -EINVAL;
- df->governor->event_handler(df, DEVFREQ_GOV_INTERVAL, &value);
+ policy->governor->event_handler(df, DEVFREQ_GOV_INTERVAL, &value);
ret = count;
return ret;
@@ -1131,7 +1141,7 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
mutex_lock(&df->lock);
if (value) {
- if (value > df->max_freq) {
+ if (value > df->policy.user.max_freq) {
ret = -EINVAL;
goto unlock;
}
@@ -1139,7 +1149,7 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
value = df->profile->freq_table[df->profile->max_state - 1];
}
- df->min_freq = value;
+ df->policy.user.min_freq = value;
update_devfreq(df);
ret = count;
unlock:
@@ -1150,9 +1160,10 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
static ssize_t min_freq_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct devfreq *df = to_devfreq(dev);
+ struct devfreq_policy *policy = &to_devfreq(dev)->policy;
- return sprintf(buf, "%lu\n", MAX(df->scaling_min_freq, df->min_freq));
+ return sprintf(buf, "%lu\n",
+ MAX(policy->devinfo.min_freq, policy->user.min_freq));
}
static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
@@ -1169,15 +1180,15 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
mutex_lock(&df->lock);
if (!value) {
- value = df->profile->freq_table[0];
+ value = df->policy.user.min_freq;
} else {
- if (value < df->min_freq) {
+ if (value < df->policy.user.min_freq) {
ret = -EINVAL;
goto unlock;
}
}
- df->max_freq = value;
+ df->policy.user.max_freq = value;
update_devfreq(df);
ret = count;
unlock:
@@ -1189,9 +1200,10 @@ static DEVICE_ATTR_RW(min_freq);
static ssize_t max_freq_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct devfreq *df = to_devfreq(dev);
+ struct devfreq_policy *policy = &to_devfreq(dev)->policy;
- return sprintf(buf, "%lu\n", MIN(df->scaling_max_freq, df->max_freq));
+ return sprintf(buf, "%lu\n",
+ MIN(policy->devinfo.max_freq, policy->user.max_freq));
}
static DEVICE_ATTR_RW(max_freq);
diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c
index 3bc29acbd54e..e0987c749ec2 100644
--- a/drivers/devfreq/governor_passive.c
+++ b/drivers/devfreq/governor_passive.c
@@ -99,12 +99,12 @@ static int update_devfreq_passive(struct devfreq *devfreq, unsigned long freq)
{
int ret;
- if (!devfreq->governor)
+ if (!devfreq->policy.governor)
return -EINVAL;
mutex_lock_nested(&devfreq->lock, SINGLE_DEPTH_NESTING);
- ret = devfreq->governor->get_target_freq(devfreq, &freq);
+ ret = devfreq->policy.governor->get_target_freq(devfreq, &freq);
if (ret < 0)
goto out;
diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c
index a8e3478b3c43..e5767bdbf77d 100644
--- a/drivers/devfreq/governor_performance.c
+++ b/drivers/devfreq/governor_performance.c
@@ -20,7 +20,7 @@ static int devfreq_performance_func(struct devfreq *df,
* target callback should be able to get floor value as
* said in devfreq.h
*/
- *freq = df->scaling_max_freq;
+ *freq = df->policy.devinfo.max_freq;
return 0;
}
diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c
index 8696efd32e5a..86184b3a572a 100644
--- a/drivers/devfreq/governor_powersave.c
+++ b/drivers/devfreq/governor_powersave.c
@@ -20,7 +20,7 @@ static int devfreq_powersave_func(struct devfreq *df,
* target callback should be able to get ceiling value as
* said in devfreq.h
*/
- *freq = df->scaling_min_freq;
+ *freq = df->policy.devinfo.min_freq;
return 0;
}
diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c
index 805fee09c754..076f9f8e33b2 100644
--- a/drivers/devfreq/governor_simpleondemand.c
+++ b/drivers/devfreq/governor_simpleondemand.c
@@ -23,6 +23,7 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
{
int err;
struct devfreq_dev_status *stat;
+ struct devfreq_policy *policy = &df->policy;
unsigned long long a, b;
unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD;
unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL;
@@ -46,7 +47,7 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
/* Assume MAX if it is going to be divided by zero */
if (stat->total_time == 0) {
- *freq = df->scaling_max_freq;
+ *freq = policy->devinfo.max_freq;
return 0;
}
@@ -59,13 +60,13 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
/* Set MAX if it's busy enough */
if (stat->busy_time * 100 >
stat->total_time * dfso_upthreshold) {
- *freq = df->scaling_max_freq;
+ *freq = policy->devinfo.max_freq;
return 0;
}
/* Set MAX if we do not know the initial frequency */
if (stat->current_frequency == 0) {
- *freq = df->scaling_max_freq;
+ *freq = policy->devinfo.max_freq;
return 0;
}
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h
index 3aae5b3af87c..9bf23b976f4d 100644
--- a/include/linux/devfreq.h
+++ b/include/linux/devfreq.h
@@ -109,6 +109,30 @@ struct devfreq_dev_profile {
unsigned int max_state;
};
+/**
+ * struct devfreq_freq_limits - Devfreq frequency limits
+ * @min_freq: minimum frequency
+ * @max_freq: maximum frequency
+ */
+struct devfreq_freq_limits {
+ unsigned long min_freq;
+ unsigned long max_freq;
+};
+
+/**
+ * struct devfreq_policy - Devfreq policy
+ * @user: frequency limits requested by the user
+ * @devinfo: frequency limits of the device (available OPPs)
+ * @governor: method how to choose frequency based on the usage.
+ * @governor_name: devfreq governor name for use with this devfreq
+ */
+struct devfreq_policy {
+ struct devfreq_freq_limits user;
+ struct devfreq_freq_limits devinfo;
+ const struct devfreq_governor *governor;
+ char governor_name[DEVFREQ_NAME_LEN];
+};
+
/**
* struct devfreq - Device devfreq structure
* @node: list node - contains the devices with devfreq that have been
@@ -117,8 +141,6 @@ struct devfreq_dev_profile {
* @dev: device registered by devfreq class. dev.parent is the device
* using devfreq.
* @profile: device-specific devfreq profile
- * @governor: method how to choose frequency based on the usage.
- * @governor_name: devfreq governor name for use with this devfreq
* @nb: notifier block used to notify devfreq object that it should
* reevaluate operable frequencies. Devfreq users may use
* devfreq.nb to the corresponding register notifier call chain.
@@ -126,10 +148,7 @@ struct devfreq_dev_profile {
* @previous_freq: previously configured frequency value.
* @data: Private data of the governor. The devfreq framework does not
* touch this.
- * @min_freq: Limit minimum frequency requested by user (0: none)
- * @max_freq: Limit maximum frequency requested by user (0: none)
- * @scaling_min_freq: Limit minimum frequency requested by OPP interface
- * @scaling_max_freq: Limit maximum frequency requested by OPP interface
+ * @policy: Policy for frequency adjustments
* @stop_polling: devfreq polling status of a device.
* @total_trans: Number of devfreq transitions
* @trans_table: Statistics of devfreq transitions
@@ -151,8 +170,6 @@ struct devfreq {
struct mutex lock;
struct device dev;
struct devfreq_dev_profile *profile;
- const struct devfreq_governor *governor;
- char governor_name[DEVFREQ_NAME_LEN];
struct notifier_block nb;
struct delayed_work work;
@@ -161,10 +178,7 @@ struct devfreq {
void *data; /* private data for governors */
- unsigned long min_freq;
- unsigned long max_freq;
- unsigned long scaling_min_freq;
- unsigned long scaling_max_freq;
+ struct devfreq_policy policy;
bool stop_polling;
/* information for device frequency transition */
--
2.17.0.921.gf22659ad46-goog
^ permalink raw reply related
* [PATCH 05/11] PM / devfreq: governors: Return device frequency limits instead of user limits
From: Matthias Kaehlcke @ 2018-05-25 20:30 UTC (permalink / raw)
To: MyungJoo Ham
Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm, devicetree, linux-kernel,
Brian Norris, Douglas Anderson, Matthias Kaehlcke
In-Reply-To: <20180525203043.249193-1-mka@chromium.org>
The performance, powersave and simpleondemand governors can return
df->min/max_freq, which are the user defined frequency limits.
update_devfreq() already takes care of adjusting the target frequency
with the user limits if necessary, therefore we can return
df->scaling_min/max_freq instead, which is the min/max frequency
supported by the device at a given time (depending on the
enabled/disabled OPPs)
Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
---
drivers/devfreq/governor_performance.c | 2 +-
drivers/devfreq/governor_powersave.c | 2 +-
drivers/devfreq/governor_simpleondemand.c | 6 +++---
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c
index 1c990cb45098..a8e3478b3c43 100644
--- a/drivers/devfreq/governor_performance.c
+++ b/drivers/devfreq/governor_performance.c
@@ -20,7 +20,7 @@ static int devfreq_performance_func(struct devfreq *df,
* target callback should be able to get floor value as
* said in devfreq.h
*/
- *freq = df->max_freq;
+ *freq = df->scaling_max_freq;
return 0;
}
diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c
index 0c42f23249ef..8696efd32e5a 100644
--- a/drivers/devfreq/governor_powersave.c
+++ b/drivers/devfreq/governor_powersave.c
@@ -20,7 +20,7 @@ static int devfreq_powersave_func(struct devfreq *df,
* target callback should be able to get ceiling value as
* said in devfreq.h
*/
- *freq = df->min_freq;
+ *freq = df->scaling_min_freq;
return 0;
}
diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c
index 3da7554b4837..805fee09c754 100644
--- a/drivers/devfreq/governor_simpleondemand.c
+++ b/drivers/devfreq/governor_simpleondemand.c
@@ -46,7 +46,7 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
/* Assume MAX if it is going to be divided by zero */
if (stat->total_time == 0) {
- *freq = df->max_freq;
+ *freq = df->scaling_max_freq;
return 0;
}
@@ -59,13 +59,13 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
/* Set MAX if it's busy enough */
if (stat->busy_time * 100 >
stat->total_time * dfso_upthreshold) {
- *freq = df->max_freq;
+ *freq = df->scaling_max_freq;
return 0;
}
/* Set MAX if we do not know the initial frequency */
if (stat->current_frequency == 0) {
- *freq = df->max_freq;
+ *freq = df->scaling_max_freq;
return 0;
}
--
2.17.0.921.gf22659ad46-goog
^ permalink raw reply related
* [PATCH 04/11] PM / devfreq: Remove redundant frequency adjustment from governors
From: Matthias Kaehlcke @ 2018-05-25 20:30 UTC (permalink / raw)
To: MyungJoo Ham
Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm, devicetree, linux-kernel,
Brian Norris, Douglas Anderson, Matthias Kaehlcke
In-Reply-To: <20180525203043.249193-1-mka@chromium.org>
The userspace and simpleondemand governor determine a target frequency and
then adjust it according to the df->min/max_freq limits that might have
been set by user space. This adjustment is redundant, it is done in
update_devfreq() for any governor, right after returning from
governor->get_target_freq().
Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
---
drivers/devfreq/governor_simpleondemand.c | 5 -----
drivers/devfreq/governor_userspace.c | 16 ++++------------
2 files changed, 4 insertions(+), 17 deletions(-)
diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c
index 278964783fa6..3da7554b4837 100644
--- a/drivers/devfreq/governor_simpleondemand.c
+++ b/drivers/devfreq/governor_simpleondemand.c
@@ -84,11 +84,6 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
b = div_u64(b, (dfso_upthreshold - dfso_downdifferential / 2));
*freq = (unsigned long) b;
- if (df->min_freq && *freq < df->min_freq)
- *freq = df->min_freq;
- if (df->max_freq && *freq > df->max_freq)
- *freq = df->max_freq;
-
return 0;
}
diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c
index 080607c3f34d..378d84c011df 100644
--- a/drivers/devfreq/governor_userspace.c
+++ b/drivers/devfreq/governor_userspace.c
@@ -26,19 +26,11 @@ static int devfreq_userspace_func(struct devfreq *df, unsigned long *freq)
{
struct userspace_data *data = df->data;
- if (data->valid) {
- unsigned long adjusted_freq = data->user_frequency;
-
- if (df->max_freq && adjusted_freq > df->max_freq)
- adjusted_freq = df->max_freq;
-
- if (df->min_freq && adjusted_freq < df->min_freq)
- adjusted_freq = df->min_freq;
-
- *freq = adjusted_freq;
- } else {
+ if (data->valid)
+ *freq = data->user_frequency;
+ else
*freq = df->previous_freq; /* No user freq specified yet */
- }
+
return 0;
}
--
2.17.0.921.gf22659ad46-goog
^ permalink raw reply related
* [PATCH 03/11] PM / devfreq: Remove check for df->max_freq == 0 from governors
From: Matthias Kaehlcke @ 2018-05-25 20:30 UTC (permalink / raw)
To: MyungJoo Ham
Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm, devicetree, linux-kernel,
Brian Norris, Douglas Anderson, Matthias Kaehlcke
In-Reply-To: <20180525203043.249193-1-mka@chromium.org>
Commit "PM / devfreq: Fix handling of min/max_freq == 0" ensures that
df->max_freq is not 0, remove unnecessary checks.
Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
---
drivers/devfreq/governor_performance.c | 5 +----
drivers/devfreq/governor_simpleondemand.c | 7 +++----
2 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c
index 4d23ecfbd948..1c990cb45098 100644
--- a/drivers/devfreq/governor_performance.c
+++ b/drivers/devfreq/governor_performance.c
@@ -20,10 +20,7 @@ static int devfreq_performance_func(struct devfreq *df,
* target callback should be able to get floor value as
* said in devfreq.h
*/
- if (!df->max_freq)
- *freq = UINT_MAX;
- else
- *freq = df->max_freq;
+ *freq = df->max_freq;
return 0;
}
diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c
index 28e0f2de7100..278964783fa6 100644
--- a/drivers/devfreq/governor_simpleondemand.c
+++ b/drivers/devfreq/governor_simpleondemand.c
@@ -27,7 +27,6 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD;
unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL;
struct devfreq_simple_ondemand_data *data = df->data;
- unsigned long max = (df->max_freq) ? df->max_freq : UINT_MAX;
err = devfreq_update_stats(df);
if (err)
@@ -47,7 +46,7 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
/* Assume MAX if it is going to be divided by zero */
if (stat->total_time == 0) {
- *freq = max;
+ *freq = df->max_freq;
return 0;
}
@@ -60,13 +59,13 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
/* Set MAX if it's busy enough */
if (stat->busy_time * 100 >
stat->total_time * dfso_upthreshold) {
- *freq = max;
+ *freq = df->max_freq;
return 0;
}
/* Set MAX if we do not know the initial frequency */
if (stat->current_frequency == 0) {
- *freq = max;
+ *freq = df->max_freq;
return 0;
}
--
2.17.0.921.gf22659ad46-goog
^ permalink raw reply related
* [PATCH 02/11] PM / devfreq: Fix handling of min/max_freq == 0
From: Matthias Kaehlcke @ 2018-05-25 20:30 UTC (permalink / raw)
To: MyungJoo Ham
Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm, devicetree, linux-kernel,
Brian Norris, Douglas Anderson, Matthias Kaehlcke
In-Reply-To: <20180525203043.249193-1-mka@chromium.org>
Commit ab8f58ad72c4 ("PM / devfreq: Set min/max_freq when adding the
devfreq device") initializes df->min/max_freq with the min/max OPP when
the device is added. Later commit f1d981eaecf8 ("PM / devfreq: Use the
available min/max frequency") adds df->scaling_min/max_freq and the
following to the frequency adjustment code:
max_freq = MIN(devfreq->scaling_max_freq, devfreq->max_freq);
With the current handling of min/max_freq this is incorrect:
Even though df->max_freq is now initialized to a value != 0 user space
can still set it to 0, in this case max_freq would be 0 instead of
df->scaling_max_freq as intended. In consequence the frequency adjustment
is not performed:
if (max_freq && freq > max_freq) {
freq = max_freq;
To fix this set df->min/max freq to the min/max OPP in max/max_freq_store,
when the user passes a value of 0. This also prevents df->max_freq from
being set below the min OPP when df->min_freq is 0, and similar for
min_freq. Since it is now guaranteed that df->min/max_freq can't be 0 the
checks for this case can be removed.
Fixes: f1d981eaecf8 ("PM / devfreq: Use the available min/max frequency")
Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
---
drivers/devfreq/devfreq.c | 30 ++++++++++++++++++------------
1 file changed, 18 insertions(+), 12 deletions(-)
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 0057ef5b0a98..67da4e7b486b 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -283,11 +283,11 @@ int update_devfreq(struct devfreq *devfreq)
max_freq = MIN(devfreq->scaling_max_freq, devfreq->max_freq);
min_freq = MAX(devfreq->scaling_min_freq, devfreq->min_freq);
- if (min_freq && freq < min_freq) {
+ if (freq < min_freq) {
freq = min_freq;
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */
}
- if (max_freq && freq > max_freq) {
+ if (freq > max_freq) {
freq = max_freq;
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */
}
@@ -1123,17 +1123,20 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
struct devfreq *df = to_devfreq(dev);
unsigned long value;
int ret;
- unsigned long max;
ret = sscanf(buf, "%lu", &value);
if (ret != 1)
return -EINVAL;
mutex_lock(&df->lock);
- max = df->max_freq;
- if (value && max && value > max) {
- ret = -EINVAL;
- goto unlock;
+
+ if (value) {
+ if (value > df->max_freq) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+ } else {
+ value = df->profile->freq_table[df->profile->max_state - 1];
}
df->min_freq = value;
@@ -1158,17 +1161,20 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
struct devfreq *df = to_devfreq(dev);
unsigned long value;
int ret;
- unsigned long min;
ret = sscanf(buf, "%lu", &value);
if (ret != 1)
return -EINVAL;
mutex_lock(&df->lock);
- min = df->min_freq;
- if (value && min && value < min) {
- ret = -EINVAL;
- goto unlock;
+
+ if (!value) {
+ value = df->profile->freq_table[0];
+ } else {
+ if (value < df->min_freq) {
+ ret = -EINVAL;
+ goto unlock;
+ }
}
df->max_freq = value;
--
2.17.0.921.gf22659ad46-goog
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox