* Re: [PATCH v2 5/7] power: reset: add driver for mt6323 poweroff
From: Matthias Brugger @ 2019-07-04 9:15 UTC (permalink / raw)
To: Frank Wunderlich, Lee Jones, Rob Herring, Mark Rutland, Sean Wang,
Sebastian Reichel, Alessandro Zummo, Alexandre Belloni,
devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
linux-pm, linux-rtc, Eddie Huang, Thomas Gleixner,
Richard Fontana, Allison Randal, David S . Miller,
Mauro Carvalho Chehab, Greg Kroah-Hartman, Rob Herring,
Linus Walleij, Nicolas Ferre, Paul E . McKenney
Cc: Josef Friedl
In-Reply-To: <20190703164822.17924-6-frank-w@public-files.de>
On 03/07/2019 18:48, Frank Wunderlich wrote:
> From: Josef Friedl <josef.friedl@speed.at>
>
> Suggested-by: Frank Wunderlich <frank-w@public-files.de>
> Signed-off-by: Josef Friedl <josef.friedl@speed.at>
Why is there a Signed-off-by from Josef for this patch but not for the others?
^ permalink raw reply
* Re: [PATCH v2 3/7] rtc: mt6397: improvements of rtc driver
From: Matthias Brugger @ 2019-07-04 9:13 UTC (permalink / raw)
To: Frank Wunderlich, Lee Jones, Rob Herring, Mark Rutland, Sean Wang,
Sebastian Reichel, Alessandro Zummo, Alexandre Belloni,
devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
linux-pm, linux-rtc, Eddie Huang, Thomas Gleixner,
Richard Fontana, Allison Randal, David S . Miller,
Mauro Carvalho Chehab, Greg Kroah-Hartman, Rob Herring,
Linus Walleij, Nicolas Ferre, Paul E . McKenney
Cc: Josef Friedl
In-Reply-To: <20190703164822.17924-4-frank-w@public-files.de>
On 03/07/2019 18:48, Frank Wunderlich wrote:
> From: Josef Friedl <josef.friedl@speed.at>
>
> - use regmap_read_poll_timeout to drop while-loop
> - use devm-api to drop remove-callback
> - add new compatible for mt6323
>
It's up to the maintainer but I don't like patches doing clean-ups together with
adding support for new HW, although it's a trivial one here.
> Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
> =2D--
> drivers/rtc/rtc-mt6397.c | 55 ++++++++++++++++------------------------
> 1 file changed, 22 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/rtc/rtc-mt6397.c b/drivers/rtc/rtc-mt6397.c
> index c08ee5edf865..e5ddf0d0b6f1 100644
> =2D-- a/drivers/rtc/rtc-mt6397.c
> +++ b/drivers/rtc/rtc-mt6397.c
> @@ -4,16 +4,19 @@
> * Author: Tianping.Fang <tianping.fang@mediatek.com>
Missing in the CC list.
^ permalink raw reply
* Re: [PATCH v2 2/7] rtc: mt6397: move some common definitions into rtc.h
From: Matthias Brugger @ 2019-07-04 9:11 UTC (permalink / raw)
To: Frank Wunderlich, Lee Jones, Rob Herring, Mark Rutland, Sean Wang,
Sebastian Reichel, Alessandro Zummo, Alexandre Belloni,
devicetree, linux-arm-kernel, linux-mediatek, linux-kernel,
linux-pm, linux-rtc, Eddie Huang, Thomas Gleixner,
Richard Fontana, Allison Randal, David S . Miller,
Mauro Carvalho Chehab, Greg Kroah-Hartman, Rob Herring,
Linus Walleij, Nicolas Ferre, Paul E . McKenney
Cc: Josef Friedl
In-Reply-To: <20190703164822.17924-3-frank-w@public-files.de>
On 03/07/2019 18:48, Frank Wunderlich wrote:
> From: Josef Friedl <josef.friedl@speed.at>
>
Still missing commit message. Describe here why you need to do that.
> Suggested-by: Frank Wunderlich <frank-w@public-files.de>
> Signed-off-by: Josef Friedl <josef.friedl@speed.at>
> Signed-off-by: Frank Wunderlich <frank-w@public-files.de>
> =2D--
Please check your email setting as discussed offline. Otherwise your patches
won't get accepted.
Regards,
Matthias
> drivers/rtc/rtc-mt6397.c | 55 +-------------------------
> include/linux/mfd/mt6397/rtc.h | 71 ++++++++++++++++++++++++++++++++++
> 2 files changed, 72 insertions(+), 54 deletions(-)
> create mode 100644 include/linux/mfd/mt6397/rtc.h
>
> diff --git a/drivers/rtc/rtc-mt6397.c b/drivers/rtc/rtc-mt6397.c
> index b46ed4dc7015..c08ee5edf865 100644
> =2D-- a/drivers/rtc/rtc-mt6397.c
> +++ b/drivers/rtc/rtc-mt6397.c
> @@ -9,60 +9,7 @@
> #include <linux/module.h>
> #include <linux/regmap.h>
> #include <linux/rtc.h>
> -#include <linux/irqdomain.h>
> -#include <linux/platform_device.h>
> -#include <linux/of_address.h>
> -#include <linux/of_irq.h>
> -#include <linux/io.h>
> -#include <linux/mfd/mt6397/core.h>
> -
> -#define RTC_BBPU 0x0000
> -#define RTC_BBPU_CBUSY BIT(6)
> -
> -#define RTC_WRTGR 0x003c
> -
> -#define RTC_IRQ_STA 0x0002
> -#define RTC_IRQ_STA_AL BIT(0)
> -#define RTC_IRQ_STA_LP BIT(3)
> -
> -#define RTC_IRQ_EN 0x0004
> -#define RTC_IRQ_EN_AL BIT(0)
> -#define RTC_IRQ_EN_ONESHOT BIT(2)
> -#define RTC_IRQ_EN_LP BIT(3)
> -#define RTC_IRQ_EN_ONESHOT_AL (RTC_IRQ_EN_ONESHOT | RTC_IRQ_EN_AL)
> -
> -#define RTC_AL_MASK 0x0008
> -#define RTC_AL_MASK_DOW BIT(4)
> -
> -#define RTC_TC_SEC 0x000a
> -/* Min, Hour, Dom... register offset to RTC_TC_SEC */
> -#define RTC_OFFSET_SEC 0
> -#define RTC_OFFSET_MIN 1
> -#define RTC_OFFSET_HOUR 2
> -#define RTC_OFFSET_DOM 3
> -#define RTC_OFFSET_DOW 4
> -#define RTC_OFFSET_MTH 5
> -#define RTC_OFFSET_YEAR 6
> -#define RTC_OFFSET_COUNT 7
> -
> -#define RTC_AL_SEC 0x0018
> -
> -#define RTC_PDN2 0x002e
> -#define RTC_PDN2_PWRON_ALARM BIT(4)
> -
> -#define RTC_MIN_YEAR 1968
> -#define RTC_BASE_YEAR 1900
> -#define RTC_NUM_YEARS 128
> -#define RTC_MIN_YEAR_OFFSET (RTC_MIN_YEAR - RTC_BASE_YEAR)
> -
> -struct mt6397_rtc {
> - struct device *dev;
> - struct rtc_device *rtc_dev;
> - struct mutex lock;
> - struct regmap *regmap;
> - int irq;
> - u32 addr_base;
> -};
> +#include <linux/mfd/mt6397/rtc.h>
>
> static int mtk_rtc_write_trigger(struct mt6397_rtc *rtc)
> {
> diff --git a/include/linux/mfd/mt6397/rtc.h b/include/linux/mfd/mt6397/rtc=
> .h
> new file mode 100644
> index 000000000000..b702c29e8c74
> =2D-- /dev/null
> +++ b/include/linux/mfd/mt6397/rtc.h
> @@ -0,0 +1,71 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2014-2018 MediaTek Inc.
> + *
> + * Author: Tianping.Fang <tianping.fang@mediatek.com>
> + * Sean Wang <sean.wang@mediatek.com>
> + */
> +
> +#ifndef _LINUX_MFD_MT6397_RTC_H_
> +#define _LINUX_MFD_MT6397_RTC_H_
> +
> +#include <linux/jiffies.h>
> +#include <linux/mutex.h>
> +#include <linux/regmap.h>
> +#include <linux/rtc.h>
> +
> +#define RTC_BBPU 0x0000
> +#define RTC_BBPU_CBUSY BIT(6)
> +#define RTC_BBPU_KEY (0x43 << 8)
> +
> +#define RTC_WRTGR 0x003c
> +
> +#define RTC_IRQ_STA 0x0002
> +#define RTC_IRQ_STA_AL BIT(0)
> +#define RTC_IRQ_STA_LP BIT(3)
> +
> +#define RTC_IRQ_EN 0x0004
> +#define RTC_IRQ_EN_AL BIT(0)
> +#define RTC_IRQ_EN_ONESHOT BIT(2)
> +#define RTC_IRQ_EN_LP BIT(3)
> +#define RTC_IRQ_EN_ONESHOT_AL (RTC_IRQ_EN_ONESHOT | RTC_IRQ_EN_AL)
> +
> +#define RTC_AL_MASK 0x0008
> +#define RTC_AL_MASK_DOW BIT(4)
> +
> +#define RTC_TC_SEC 0x000a
> +/* Min, Hour, Dom... register offset to RTC_TC_SEC */
> +#define RTC_OFFSET_SEC 0
> +#define RTC_OFFSET_MIN 1
> +#define RTC_OFFSET_HOUR 2
> +#define RTC_OFFSET_DOM 3
> +#define RTC_OFFSET_DOW 4
> +#define RTC_OFFSET_MTH 5
> +#define RTC_OFFSET_YEAR 6
> +#define RTC_OFFSET_COUNT 7
> +
> +#define RTC_AL_SEC 0x0018
> +
> +#define RTC_PDN2 0x002e
> +#define RTC_PDN2_PWRON_ALARM BIT(4)
> +
> +#define RTC_MIN_YEAR 1968
> +#define RTC_BASE_YEAR 1900
> +#define RTC_NUM_YEARS 128
> +#define RTC_MIN_YEAR_OFFSET (RTC_MIN_YEAR - RTC_BASE_YEAR)
> +
> +#define MTK_RTC_POLL_DELAY_US 10
> +#define MTK_RTC_POLL_TIMEOUT (jiffies_to_usecs(HZ))
> +
> +struct mt6397_rtc {
> + struct device *dev;
> + struct rtc_device *rtc_dev;
> +
> + /* Protect register access from multiple tasks */
> + struct mutex lock;
> + struct regmap *regmap;
> + int irq;
> + u32 addr_base;
> +};
> +
> +#endif /* _LINUX_MFD_MT6397_RTC_H_ */
> =2D-
> 2.17.1
>
^ permalink raw reply
* Re: [patch 0/5] cpuidle haltpoll driver and governor (v6)
From: Rafael J. Wysocki @ 2019-07-04 8:32 UTC (permalink / raw)
To: Marcelo Tosatti
Cc: kvm-devel, Paolo Bonzini, Radim Krcmar, Andrea Arcangeli,
Rafael J. Wysocki, Peter Zijlstra, Wanpeng Li,
Konrad Rzeszutek Wilk, Raslan KarimAllah, Boris Ostrovsky,
Ankur Arora, Christian Borntraeger, Linux PM
In-Reply-To: <20190703235124.783034907@amt.cnet>
On Thu, Jul 4, 2019 at 1:59 AM Marcelo Tosatti <mtosatti@redhat.com> wrote:
>
> (rebased against queue branch of kvm.git tree)
>
> The cpuidle-haltpoll driver with haltpoll governor allows the guest
> vcpus to poll for a specified amount of time before halting.
> This provides the following benefits to host side polling:
>
> 1) The POLL flag is set while polling is performed, which allows
> a remote vCPU to avoid sending an IPI (and the associated
> cost of handling the IPI) when performing a wakeup.
>
> 2) The VM-exit cost can be avoided.
>
> The downside of guest side polling is that polling is performed
> even with other runnable tasks in the host.
>
> Results comparing halt_poll_ns and server/client application
> where a small packet is ping-ponged:
>
> host --> 31.33
> halt_poll_ns=300000 / no guest busy spin --> 33.40 (93.8%)
> halt_poll_ns=0 / guest_halt_poll_ns=300000 --> 32.73 (95.7%)
>
> For the SAP HANA benchmarks (where idle_spin is a parameter
> of the previous version of the patch, results should be the
> same):
>
> hpns == halt_poll_ns
>
> idle_spin=0/ idle_spin=800/ idle_spin=0/
> hpns=200000 hpns=0 hpns=800000
> DeleteC06T03 (100 thread) 1.76 1.71 (-3%) 1.78 (+1%)
> InsertC16T02 (100 thread) 2.14 2.07 (-3%) 2.18 (+1.8%)
> DeleteC00T01 (1 thread) 1.34 1.28 (-4.5%) 1.29 (-3.7%)
> UpdateC00T03 (1 thread) 4.72 4.18 (-12%) 4.53 (-5%)
>
> V2:
>
> - Move from x86 to generic code (Paolo/Christian)
> - Add auto-tuning logic (Paolo)
> - Add MSR to disable host side polling (Paolo)
>
> V3:
>
> - Do not be specific about HLT VM-exit in the documentation (Ankur Arora)
> - Mark tuning parameters static and __read_mostly (Andrea Arcangeli)
> - Add WARN_ON if host does not support poll control (Joao Martins)
> - Use sched_clock and cleanup haltpoll_enter_idle (Peter Zijlstra)
> - Mark certain functions in kvm.c as static (kernel test robot)
> - Remove tracepoints as they use RCU from extended quiescent state (kernel
> test robot)
>
> V4:
> - Use a haltpoll governor, use poll_state.c poll code (Rafael J. Wysocki)
>
> V5:
> - Take latency requirement into consideration (Rafael J. Wysocki)
> - Set target_residency/exit_latency to 1 (Rafael J. Wysocki)
> - Do not load cpuidle driver if not virtualized (Rafael J. Wysocki)
>
> V6:
> - Switch from callback to poll_limit_ns variable in cpuidle device structure
> (Rafael J. Wysocki)
> - Move last_used_idx to cpuidle device structure (Rafael J. Wysocki)
> - Drop per-cpu device structure in haltpoll governor (Rafael J. Wysocki)
It looks good to me now, but I have some cpuidle changes in the work
that will clash in some changes in this series if not rebased on top
of it, so IMO it would make sense for me to get patches [1-4/5] at
least into my queue. I can expose an immutable branch with them for
the KVM tree to consume. I can take the last patch in the series as
well if I get an ACK for it.
Would that work for everybody?
^ permalink raw reply
* Re: [RFCv2 0/8] Add imx8mm bus frequency switching
From: Leonard Crestez @ 2019-07-04 8:32 UTC (permalink / raw)
To: Saravana Kannan, Georgi Djakov
Cc: Viresh Kumar, Alexandre Bailon, Stephen Boyd, Michael Turquette,
MyungJoo Ham, Kyungmin Park, Shawn Guo, Aisheng Dong,
Fabio Estevam, Rafael J. Wysocki, Jacky Bai, Anson Huang,
Abel Vesa, Krzysztof Kozlowski, Ulf Hansson,
kernel@pengutronix.de, dl-linux-imx, Linux PM,
linux-clk@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
Vincent Guittot
In-Reply-To: <CAGETcx-p4L3LBVpDBmBrPKXxMUtUXtsw-7AntpWs+AL3kaaP5Q@mail.gmail.com>
On 7/4/2019 6:03 AM, Saravana Kannan wrote:
> On Wed, Jul 3, 2019 at 4:30 PM Leonard Crestez <leonard.crestez@nxp.com> wrote:
>> On 7/4/2019 1:20 AM, Saravana Kannan wrote:
>>
>>>> The interconnect and devfreq parts do not communicate explicitly: they both
>>>> just call clk_set_min_rate and the clk core picks the minimum value that can
>>>> satisfy both. They are thus completely independent.
>>>
>>> Two different parts not talking to each other and just setting min
>>> rate can cause problems for some concurrency use cases. ICC framework
>>> is explicitly designed to handle cases like this and aggregate the
>>> needs correctly. You might want to look into that more closely.
>>
>> The clk framework aggregates the min_rate requests from multiple
>> consumers under a big "prepare_lock" so I expect it will deal with
>> concurrent requests correctly. As for performance: frequency switching
>> shouldn't be a fast path.
>
> Sorry I wasn't clear. I was not talking about locking issues or race
> conditions when I said concurrency use cases. What I meant was if GPU
> wants 5 GB/s and at the same time (concurrent) camera wants 5 GB/s
> you'll need to configure the interconnect for 10 GB/s. If both of them
> just try to set the min freq equivalent for 5 GB/s the performance
> would be bad or functionality might break.
I'm not calling clk_set_min_rate independently for each icc path, that
would be obviously broken. The interconnect framework is still used to
aggregate bandwith requests and in your scenario clk_set_min_rate for
the main NOC would be called in a way that meets the combined 10 GB/s
requests.
It is devfreq which calls clk_set_min_rate independently of
interconnect, this results in CLK performing the final aggregation
between the "proactive" and "reactive" scaling.
>>>> The clk_set_min_rate approach does not mesh very well with the OPP framework.
>>>> Some of interconnect nodes on imx8m can run at different voltages: OPP can
>>>> handle this well but not in response to a clk_set_min_rate from an unrelated
>>>> subsystem. Maybe set voltage on a clk notifier?
>>>
>>> I think if you design it something like below, it might make your life
>>> a whole lot easier.
>>> Hopefully the formatting looks okay on your end. The arrow going up is
>>> just connecting devfreq to ICC.
>>>
>>> Proactive -> ICC -> clk/OPP API to set freq/volt
>>> ^
>>> |
>>> HW measure -> governor -> devfreq ----+
>>>
>>> That way, all voltage/frequency requirements are managed cleanly by
>>> clk/OPP frameworks. The ICC deals with aggregating all the
>>> requirements and devfreq lets you handle reactive scaling and policy.
>>
>> If icc and devfreq are to directly communicate it makes more sense to do
>> it backwards: ICC should set a min_freq on nodes which have a devfreq
>> instance attached and devfreq should implement actual freq switching.
>>
>> HW measurement is done on individual nodes while ICC deals with requests
>> along a path. In particular on imx we have a performance monitor
>> attached to the ddr controller and I doubt it can distinguish between
>> masters so how could this be mapped usefully to an interconnect request?
>
> Ah, that was the missing piece for me -- you are trying to use a
> central performance monitor. I see what you are trying to do.
>
> So you are looking at system wide traffic at DDR and then using that
> to scale the interconnect/DDRC. I don't know how complicated or not
> the IMX interconnect topology is, so please pardon my questions. If
> you are using a performance monitor at the DDR controller, why do you
> need the "proactive" requests from other clients? Wouldn't the
> performance monitor account for all the traffic to DDR?
Reactive scaling is too slow to ramp-up in media playback scenarios and
first few frames would fail.
>> As far as I understand with devfreq the ddrc node could use "ondemand"
>> while the other nodes would default to the "passive" governor and run at
>> predefined default ratios relative to DDRC.
>
> Yes, that approach would also work but I'm not sure why you need the
> ICC framework in that case.
For proactive scaling: to ensure bandwidth *before* traffic starts. In
imx vendor tree that's all that's implemented; for reactive scaling we
just set busfreq to high as soon as cpu leaves min opp.
IMX interconnect topology is not very complex so mechanisms other than
interconnect could be used. But ICC is the most powerful and expressive
subsystem for proactive requests.
>>> If all of this makes sense, please take a look at [2] and provide your
>>> thoughts. I've dropped a few patches from [1] to avoid confusion (too
>>> much going on at once). I think BW OPP tables and having OPP tables
>>> for interconnect paths will be something you'll need (if not now,
>>> eventually) and something you can build on top of nicely.
>>
>> I found it very confusing that you're assigning BW OPP tables to
>> devices. My initial understanding was that BW OPP would map "bandwidth"
>> to "frequency" so BW OPPs should be assigned to links along the
>> interconnect graph. But maybe what you want is to have OPPs for the BW
>> values requested by devices?
>
> I want to have OPPs for bandwidths requested for paths.
Right, this was not very obvious.
> Each interconnect node can also use BW OPPs if that makes sense for them,
> but I think they'd be better served by using frequency OPPs.
Each interconnect node is asked by the framework to ensure a certain BW
is available in "bytes". The nodes could use OPPs with BW values to map
the icc request to a frequency in "hz".
>> Looking at the sdm845 icc provider source and it seems that those
>> "peak/avg" values are actually parameters which go into a firmware
>> command!? It makes sense that you want interconnect to be "below"
>> devfreq since icc_provider.set maps very closely to what firmware exposes.
>
> Even without the firmware (it's mainly there to aggregate requests for
> some system wide resources) or if interconnects are scaled directly
> using clock APIs (older chips), sdm845 would still want ICC to be
> below devfreq. It's because 845 doesn't try to do ICC scaling by
> measuring at the DDR. Each master makes separate requests and then the
> ICC aggregates and sets the frequency. They have their reasons (good
> ones) for doing that.
Maybe I'm confused about how devfreq is used in your scenario: you have
devices which have their own OPPs (like a GPU) and expose this via
devfreq. Then for each GPU OPP you want to pick a the BW value to
request from interconnect, right?
My idea was to use devfreq *after* icc so that you can do stuff like
force a certain NOC to "max" via echo "performance" > $sysfs/governor.
It also allows encapsulating complex freq switching (clk maintainers
don't seem to like my dram clk).
On a second examination there is no actual incompatibility here, devfreq
could be used both below and above ICC.
--
Regards,
Leonard
^ permalink raw reply
* Re: [PATCH] PM: sleep: Drop dev_pm_skip_next_resume_phases()
From: Mika Westerberg @ 2019-07-04 8:01 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: Linux PM, LKML
In-Reply-To: <2100414.Plmip0uhM5@kreacher>
On Thu, Jul 04, 2019 at 01:05:38AM +0200, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
>
> After recent hibernation-related changes, there are no more callers
> of dev_pm_skip_next_resume_phases() except for the PM core itself
> in which it is more straightforward to run the statements from
> that function directly, so do that and drop it.
>
> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
^ permalink raw reply
* Re: [PATCH] ACPI: PM: Unexport acpi_device_get_power()
From: Mika Westerberg @ 2019-07-04 7:46 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: Linux ACPI, Linux PM, LKML
In-Reply-To: <1970901.ZntFDt4DbR@kreacher>
On Thu, Jul 04, 2019 at 01:02:49AM +0200, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
>
> Using acpi_device_get_power() outside of ACPI device initialization
> and ACPI sysfs is problematic due to the way in which power resources
> are handled by it, so unexport it and add a paragraph explaining the
> pitfalls to its kerneldoc comment.
>
> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> ---
Makes perfect sense!
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
^ permalink raw reply
* [PATCH V6 2/7] PM / QOS: Rename __dev_pm_qos_read_value() and dev_pm_qos_raw_read_value()
From: Viresh Kumar @ 2019-07-04 7:36 UTC (permalink / raw)
To: Rafael Wysocki, Kevin Hilman, Ulf Hansson, Pavel Machek,
Len Brown, Daniel Lezcano
Cc: Viresh Kumar, linux-pm, Vincent Guittot, Rafael J . Wysocki,
linux-kernel
In-Reply-To: <cover.1562210705.git.viresh.kumar@linaro.org>
dev_pm_qos_read_value() will soon need to support more constraint types
(min/max frequency) and will have another argument to it, i.e. type of
the constraint. While that is fine for the existing users of
dev_pm_qos_read_value(), but not that optimal for the callers of
__dev_pm_qos_read_value() and dev_pm_qos_raw_read_value() as all the
callers of these two routines are only looking for resume latency
constraint.
Lets make these two routines care only about the resume latency
constraint and rename them to __dev_pm_qos_resume_latency() and
dev_pm_qos_raw_resume_latency().
Suggested-by: Rafael J. Wysocki <rjw@rjwysocki.net>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
drivers/base/power/domain_governor.c | 2 +-
drivers/base/power/qos.c | 13 +++++++++----
drivers/base/power/runtime.c | 2 +-
drivers/cpuidle/governor.c | 2 +-
include/linux/pm_qos.h | 8 ++++----
5 files changed, 16 insertions(+), 11 deletions(-)
diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c
index 3838045c9277..20e56a5be01f 100644
--- a/drivers/base/power/domain_governor.c
+++ b/drivers/base/power/domain_governor.c
@@ -66,7 +66,7 @@ static bool default_suspend_ok(struct device *dev)
td->constraint_changed = false;
td->cached_suspend_ok = false;
td->effective_constraint_ns = 0;
- constraint_ns = __dev_pm_qos_read_value(dev);
+ constraint_ns = __dev_pm_qos_resume_latency(dev);
spin_unlock_irqrestore(&dev->power.lock, flags);
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index cfd463212513..7a0d197f0809 100644
--- a/drivers/base/power/qos.c
+++ b/drivers/base/power/qos.c
@@ -90,16 +90,16 @@ enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask)
EXPORT_SYMBOL_GPL(dev_pm_qos_flags);
/**
- * __dev_pm_qos_read_value - Get PM QoS constraint for a given device.
+ * __dev_pm_qos_resume_latency - Get resume latency constraint for a given device.
* @dev: Device to get the PM QoS constraint value for.
*
* This routine must be called with dev->power.lock held.
*/
-s32 __dev_pm_qos_read_value(struct device *dev)
+s32 __dev_pm_qos_resume_latency(struct device *dev)
{
lockdep_assert_held(&dev->power.lock);
- return dev_pm_qos_raw_read_value(dev);
+ return dev_pm_qos_raw_resume_latency(dev);
}
/**
@@ -112,7 +112,12 @@ s32 dev_pm_qos_read_value(struct device *dev)
s32 ret;
spin_lock_irqsave(&dev->power.lock, flags);
- ret = __dev_pm_qos_read_value(dev);
+
+ if (IS_ERR_OR_NULL(dev->power.qos))
+ ret = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
+ else
+ ret = pm_qos_read_value(&dev->power.qos->resume_latency);
+
spin_unlock_irqrestore(&dev->power.lock, flags);
return ret;
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 952a1e7057c7..b75335508d2c 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -275,7 +275,7 @@ static int rpm_check_suspend_allowed(struct device *dev)
|| (dev->power.request_pending
&& dev->power.request == RPM_REQ_RESUME))
retval = -EAGAIN;
- else if (__dev_pm_qos_read_value(dev) == 0)
+ else if (__dev_pm_qos_resume_latency(dev) == 0)
retval = -EPERM;
else if (dev->power.runtime_status == RPM_SUSPENDED)
retval = 1;
diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c
index 9fddf828a76f..2e3e14192bee 100644
--- a/drivers/cpuidle/governor.c
+++ b/drivers/cpuidle/governor.c
@@ -110,7 +110,7 @@ int cpuidle_governor_latency_req(unsigned int cpu)
{
int global_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
struct device *device = get_cpu_device(cpu);
- int device_req = dev_pm_qos_raw_read_value(device);
+ int device_req = dev_pm_qos_raw_resume_latency(device);
return device_req < global_req ? device_req : global_req;
}
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 58e8749ceac5..5e09d4980786 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -139,7 +139,7 @@ s32 pm_qos_read_value(struct pm_qos_constraints *c);
#ifdef CONFIG_PM
enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask);
enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask);
-s32 __dev_pm_qos_read_value(struct device *dev);
+s32 __dev_pm_qos_resume_latency(struct device *dev);
s32 dev_pm_qos_read_value(struct device *dev);
int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
enum dev_pm_qos_req_type type, s32 value);
@@ -176,7 +176,7 @@ static inline s32 dev_pm_qos_requested_flags(struct device *dev)
return dev->power.qos->flags_req->data.flr.flags;
}
-static inline s32 dev_pm_qos_raw_read_value(struct device *dev)
+static inline s32 dev_pm_qos_raw_resume_latency(struct device *dev)
{
return IS_ERR_OR_NULL(dev->power.qos) ?
PM_QOS_RESUME_LATENCY_NO_CONSTRAINT :
@@ -189,7 +189,7 @@ static inline enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev,
static inline enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev,
s32 mask)
{ return PM_QOS_FLAGS_UNDEFINED; }
-static inline s32 __dev_pm_qos_read_value(struct device *dev)
+static inline s32 __dev_pm_qos_resume_latency(struct device *dev)
{ return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; }
static inline s32 dev_pm_qos_read_value(struct device *dev)
{ return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; }
@@ -245,7 +245,7 @@ static inline s32 dev_pm_qos_requested_resume_latency(struct device *dev)
return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
}
static inline s32 dev_pm_qos_requested_flags(struct device *dev) { return 0; }
-static inline s32 dev_pm_qos_raw_read_value(struct device *dev)
+static inline s32 dev_pm_qos_raw_resume_latency(struct device *dev)
{
return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
}
--
2.21.0.rc0.269.g1a574e7a288b
^ permalink raw reply related
* [PATCH V6 3/7] PM / QOS: Pass request type to dev_pm_qos_read_value()
From: Viresh Kumar @ 2019-07-04 7:36 UTC (permalink / raw)
To: Rafael Wysocki, Len Brown, Pavel Machek, Kevin Hilman,
Ulf Hansson
Cc: Viresh Kumar, linux-pm, Vincent Guittot, Matthias Kaehlcke,
Rafael J . Wysocki, linux-kernel
In-Reply-To: <cover.1562210705.git.viresh.kumar@linaro.org>
In order to allow dev_pm_qos_read_value() to read values for different
QoS requests, pass request type as a parameter to these routines.
For now, it only supports resume-latency request type but will be
extended to frequency limit (min/max) constraints later on.
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
Documentation/power/pm_qos_interface.txt | 2 +-
drivers/base/power/domain_governor.c | 2 +-
drivers/base/power/qos.c | 17 ++++++++++++-----
include/linux/pm_qos.h | 16 +++++++++++++---
4 files changed, 27 insertions(+), 10 deletions(-)
diff --git a/Documentation/power/pm_qos_interface.txt b/Documentation/power/pm_qos_interface.txt
index ec7d662d1707..cfcb1df39799 100644
--- a/Documentation/power/pm_qos_interface.txt
+++ b/Documentation/power/pm_qos_interface.txt
@@ -123,7 +123,7 @@ Will remove the element. After removal it will update the aggregate target and
call the notification trees if the target was changed as a result of removing
the request.
-s32 dev_pm_qos_read_value(device):
+s32 dev_pm_qos_read_value(device, type):
Returns the aggregated value for a given device's constraints list.
enum pm_qos_flags_status dev_pm_qos_flags(device, mask)
diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c
index 20e56a5be01f..daa8c7689f7e 100644
--- a/drivers/base/power/domain_governor.c
+++ b/drivers/base/power/domain_governor.c
@@ -33,7 +33,7 @@ static int dev_update_qos_constraint(struct device *dev, void *data)
* take its current PM QoS constraint (that's the only thing
* known at this point anyway).
*/
- constraint_ns = dev_pm_qos_read_value(dev);
+ constraint_ns = dev_pm_qos_read_value(dev, DEV_PM_QOS_RESUME_LATENCY);
constraint_ns *= NSEC_PER_USEC;
}
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index 7a0d197f0809..2461fed0efa0 100644
--- a/drivers/base/power/qos.c
+++ b/drivers/base/power/qos.c
@@ -105,18 +105,25 @@ s32 __dev_pm_qos_resume_latency(struct device *dev)
/**
* dev_pm_qos_read_value - Get PM QoS constraint for a given device (locked).
* @dev: Device to get the PM QoS constraint value for.
+ * @type: QoS request type.
*/
-s32 dev_pm_qos_read_value(struct device *dev)
+s32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type)
{
+ struct dev_pm_qos *qos = dev->power.qos;
unsigned long flags;
s32 ret;
spin_lock_irqsave(&dev->power.lock, flags);
- if (IS_ERR_OR_NULL(dev->power.qos))
- ret = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
- else
- ret = pm_qos_read_value(&dev->power.qos->resume_latency);
+ switch (type) {
+ case DEV_PM_QOS_RESUME_LATENCY:
+ ret = IS_ERR_OR_NULL(qos) ? PM_QOS_RESUME_LATENCY_NO_CONSTRAINT
+ : pm_qos_read_value(&qos->resume_latency);
+ break;
+ default:
+ WARN_ON(1);
+ ret = 0;
+ }
spin_unlock_irqrestore(&dev->power.lock, flags);
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 5e09d4980786..9a21b7ba72ae 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -140,7 +140,7 @@ s32 pm_qos_read_value(struct pm_qos_constraints *c);
enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask);
enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask);
s32 __dev_pm_qos_resume_latency(struct device *dev);
-s32 dev_pm_qos_read_value(struct device *dev);
+s32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type);
int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
enum dev_pm_qos_req_type type, s32 value);
int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
@@ -191,8 +191,18 @@ static inline enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev,
{ return PM_QOS_FLAGS_UNDEFINED; }
static inline s32 __dev_pm_qos_resume_latency(struct device *dev)
{ return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; }
-static inline s32 dev_pm_qos_read_value(struct device *dev)
- { return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; }
+static inline s32 dev_pm_qos_read_value(struct device *dev,
+ enum dev_pm_qos_req_type type)
+{
+ switch (type) {
+ case DEV_PM_QOS_RESUME_LATENCY:
+ return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
+ default:
+ WARN_ON(1);
+ return 0;
+ }
+}
+
static inline int dev_pm_qos_add_request(struct device *dev,
struct dev_pm_qos_request *req,
enum dev_pm_qos_req_type type,
--
2.21.0.rc0.269.g1a574e7a288b
^ permalink raw reply related
* [PATCH V6 5/7] cpufreq: Register notifiers with the PM QoS framework
From: Viresh Kumar @ 2019-07-04 7:36 UTC (permalink / raw)
To: Rafael Wysocki
Cc: Viresh Kumar, linux-pm, Vincent Guittot, Matthias Kaehlcke,
Ulf Hansson, Rafael J . Wysocki, linux-kernel
In-Reply-To: <cover.1562210705.git.viresh.kumar@linaro.org>
This registers the notifiers for min/max frequency constraints with the
PM QoS framework. The constraints are also taken into consideration in
cpufreq_set_policy().
This also relocates cpufreq_policy_put_kobj() as it is required to be
called from cpufreq_policy_alloc() now.
refresh_frequency_limits() is updated to have proper locking in place and
avoid calling cpufreq_set_policy() for inactive policies.
No constraints are added until now though.
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
drivers/cpufreq/cpufreq.c | 135 ++++++++++++++++++++++++++++++--------
include/linux/cpufreq.h | 3 +
2 files changed, 109 insertions(+), 29 deletions(-)
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index ceb57af15ca0..81117e4d43cc 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -26,6 +26,7 @@
#include <linux/kernel_stat.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/pm_qos.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/syscore_ops.h>
@@ -999,7 +1000,7 @@ static void add_cpu_dev_symlink(struct cpufreq_policy *policy, unsigned int cpu)
{
struct device *dev = get_cpu_device(cpu);
- if (!dev)
+ if (unlikely(!dev))
return;
if (cpumask_test_and_set_cpu(cpu, policy->real_cpus))
@@ -1117,14 +1118,20 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, unsigned int cp
static void refresh_frequency_limits(struct cpufreq_policy *policy)
{
- struct cpufreq_policy new_policy = *policy;
+ struct cpufreq_policy new_policy;
- pr_debug("updating policy for CPU %u\n", policy->cpu);
+ down_write(&policy->rwsem);
+
+ if (!policy_is_inactive(policy)) {
+ new_policy = *policy;
+ pr_debug("updating policy for CPU %u\n", policy->cpu);
- new_policy.min = policy->user_policy.min;
- new_policy.max = policy->user_policy.max;
+ new_policy.min = policy->user_policy.min;
+ new_policy.max = policy->user_policy.max;
+ cpufreq_set_policy(policy, &new_policy);
+ }
- cpufreq_set_policy(policy, &new_policy);
+ up_write(&policy->rwsem);
}
static void handle_update(struct work_struct *work)
@@ -1136,11 +1143,55 @@ static void handle_update(struct work_struct *work)
refresh_frequency_limits(policy);
}
+static int cpufreq_notifier_min(struct notifier_block *nb, unsigned long freq,
+ void *data)
+{
+ struct cpufreq_policy *policy = container_of(nb, struct cpufreq_policy, nb_min);
+
+ schedule_work(&policy->update);
+ return 0;
+}
+
+static int cpufreq_notifier_max(struct notifier_block *nb, unsigned long freq,
+ void *data)
+{
+ struct cpufreq_policy *policy = container_of(nb, struct cpufreq_policy, nb_max);
+
+ schedule_work(&policy->update);
+ return 0;
+}
+
+static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy)
+{
+ struct kobject *kobj;
+ struct completion *cmp;
+
+ down_write(&policy->rwsem);
+ cpufreq_stats_free_table(policy);
+ kobj = &policy->kobj;
+ cmp = &policy->kobj_unregister;
+ up_write(&policy->rwsem);
+ kobject_put(kobj);
+
+ /*
+ * We need to make sure that the underlying kobj is
+ * actually not referenced anymore by anybody before we
+ * proceed with unloading.
+ */
+ pr_debug("waiting for dropping of refcount\n");
+ wait_for_completion(cmp);
+ pr_debug("wait complete\n");
+}
+
static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
{
struct cpufreq_policy *policy;
+ struct device *dev = get_cpu_device(cpu);
int ret;
+ if (!dev)
+ return NULL;
+
policy = kzalloc(sizeof(*policy), GFP_KERNEL);
if (!policy)
return NULL;
@@ -1157,7 +1208,7 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq,
cpufreq_global_kobject, "policy%u", cpu);
if (ret) {
- pr_err("%s: failed to init policy->kobj: %d\n", __func__, ret);
+ dev_err(dev, "%s: failed to init policy->kobj: %d\n", __func__, ret);
/*
* The entire policy object will be freed below, but the extra
* memory allocated for the kobject name needs to be freed by
@@ -1167,6 +1218,25 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
goto err_free_real_cpus;
}
+ policy->nb_min.notifier_call = cpufreq_notifier_min;
+ policy->nb_max.notifier_call = cpufreq_notifier_max;
+
+ ret = dev_pm_qos_add_notifier(dev, &policy->nb_min,
+ DEV_PM_QOS_MIN_FREQUENCY);
+ if (ret) {
+ dev_err(dev, "Failed to register MIN QoS notifier: %d (%*pbl)\n",
+ ret, cpumask_pr_args(policy->cpus));
+ goto err_kobj_remove;
+ }
+
+ ret = dev_pm_qos_add_notifier(dev, &policy->nb_max,
+ DEV_PM_QOS_MAX_FREQUENCY);
+ if (ret) {
+ dev_err(dev, "Failed to register MAX QoS notifier: %d (%*pbl)\n",
+ ret, cpumask_pr_args(policy->cpus));
+ goto err_min_qos_notifier;
+ }
+
INIT_LIST_HEAD(&policy->policy_list);
init_rwsem(&policy->rwsem);
spin_lock_init(&policy->transition_lock);
@@ -1177,6 +1247,11 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
policy->cpu = cpu;
return policy;
+err_min_qos_notifier:
+ dev_pm_qos_remove_notifier(dev, &policy->nb_min,
+ DEV_PM_QOS_MIN_FREQUENCY);
+err_kobj_remove:
+ cpufreq_policy_put_kobj(policy);
err_free_real_cpus:
free_cpumask_var(policy->real_cpus);
err_free_rcpumask:
@@ -1189,30 +1264,9 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
return NULL;
}
-static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy)
-{
- struct kobject *kobj;
- struct completion *cmp;
-
- down_write(&policy->rwsem);
- cpufreq_stats_free_table(policy);
- kobj = &policy->kobj;
- cmp = &policy->kobj_unregister;
- up_write(&policy->rwsem);
- kobject_put(kobj);
-
- /*
- * We need to make sure that the underlying kobj is
- * actually not referenced anymore by anybody before we
- * proceed with unloading.
- */
- pr_debug("waiting for dropping of refcount\n");
- wait_for_completion(cmp);
- pr_debug("wait complete\n");
-}
-
static void cpufreq_policy_free(struct cpufreq_policy *policy)
{
+ struct device *dev = get_cpu_device(policy->cpu);
unsigned long flags;
int cpu;
@@ -1224,6 +1278,11 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy)
per_cpu(cpufreq_cpu_data, cpu) = NULL;
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+ dev_pm_qos_remove_notifier(dev, &policy->nb_max,
+ DEV_PM_QOS_MAX_FREQUENCY);
+ dev_pm_qos_remove_notifier(dev, &policy->nb_min,
+ DEV_PM_QOS_MIN_FREQUENCY);
+
cpufreq_policy_put_kobj(policy);
free_cpumask_var(policy->real_cpus);
free_cpumask_var(policy->related_cpus);
@@ -2283,6 +2342,8 @@ int cpufreq_set_policy(struct cpufreq_policy *policy,
struct cpufreq_policy *new_policy)
{
struct cpufreq_governor *old_gov;
+ struct device *cpu_dev = get_cpu_device(policy->cpu);
+ unsigned long min, max;
int ret;
pr_debug("setting new policy for CPU %u: %u - %u kHz\n",
@@ -2297,11 +2358,27 @@ int cpufreq_set_policy(struct cpufreq_policy *policy,
if (new_policy->min > new_policy->max)
return -EINVAL;
+ /*
+ * PM QoS framework collects all the requests from users and provide us
+ * the final aggregated value here.
+ */
+ min = dev_pm_qos_read_value(cpu_dev, DEV_PM_QOS_MIN_FREQUENCY);
+ max = dev_pm_qos_read_value(cpu_dev, DEV_PM_QOS_MAX_FREQUENCY);
+
+ if (min > new_policy->min)
+ new_policy->min = min;
+ if (max < new_policy->max)
+ new_policy->max = max;
+
/* verify the cpu speed can be set within this limit */
ret = cpufreq_driver->verify(new_policy);
if (ret)
return ret;
+ /*
+ * The notifier-chain shall be removed once all the users of
+ * CPUFREQ_ADJUST are moved to use the QoS framework.
+ */
/* adjust if necessary - all reasons */
blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
CPUFREQ_ADJUST, new_policy);
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index a1467aa7f58b..95425941f46d 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -147,6 +147,9 @@ struct cpufreq_policy {
/* Pointer to the cooling device if used for thermal mitigation */
struct thermal_cooling_device *cdev;
+
+ struct notifier_block nb_min;
+ struct notifier_block nb_max;
};
struct cpufreq_freqs {
--
2.21.0.rc0.269.g1a574e7a288b
^ permalink raw reply related
* [PATCH V6 7/7] cpufreq: Add QoS requests for userspace constraints
From: Viresh Kumar @ 2019-07-04 7:36 UTC (permalink / raw)
To: Rafael Wysocki
Cc: Viresh Kumar, linux-pm, Vincent Guittot, Matthias Kaehlcke,
Ulf Hansson, Rafael J . Wysocki, linux-kernel
In-Reply-To: <cover.1562210705.git.viresh.kumar@linaro.org>
This implements QoS requests to manage userspace configuration of min
and max frequency.
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
drivers/cpufreq/cpufreq.c | 87 ++++++++++++++++++++-------------------
include/linux/cpufreq.h | 8 +---
2 files changed, 46 insertions(+), 49 deletions(-)
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 091789e868ee..13c2f119cc0c 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -718,23 +718,15 @@ static ssize_t show_scaling_cur_freq(struct cpufreq_policy *policy, char *buf)
static ssize_t store_##file_name \
(struct cpufreq_policy *policy, const char *buf, size_t count) \
{ \
- int ret, temp; \
- struct cpufreq_policy new_policy; \
+ unsigned long val; \
+ int ret; \
\
- 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); \
+ ret = sscanf(buf, "%lu", &val); \
if (ret != 1) \
return -EINVAL; \
\
- temp = new_policy.object; \
- ret = cpufreq_set_policy(policy, &new_policy); \
- if (!ret) \
- policy->user_policy.object = temp; \
- \
- return ret ? ret : count; \
+ ret = dev_pm_qos_update_request(policy->object##_freq_req, val);\
+ return ret >= 0 ? count : ret; \
}
store_one(scaling_min_freq, min);
@@ -1126,8 +1118,6 @@ void refresh_frequency_limits(struct cpufreq_policy *policy)
new_policy = *policy;
pr_debug("updating policy for CPU %u\n", policy->cpu);
- new_policy.min = policy->user_policy.min;
- new_policy.max = policy->user_policy.max;
cpufreq_set_policy(policy, &new_policy);
}
@@ -1238,6 +1228,12 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
goto err_min_qos_notifier;
}
+ policy->min_freq_req = kzalloc(2 * sizeof(*policy->min_freq_req),
+ GFP_KERNEL);
+ if (!policy->min_freq_req)
+ goto err_max_qos_notifier;
+
+ policy->max_freq_req = policy->min_freq_req + 1;
INIT_LIST_HEAD(&policy->policy_list);
init_rwsem(&policy->rwsem);
spin_lock_init(&policy->transition_lock);
@@ -1248,6 +1244,9 @@ static struct cpufreq_policy *cpufreq_policy_alloc(unsigned int cpu)
policy->cpu = cpu;
return policy;
+err_max_qos_notifier:
+ dev_pm_qos_remove_notifier(dev, &policy->nb_max,
+ DEV_PM_QOS_MAX_FREQUENCY);
err_min_qos_notifier:
dev_pm_qos_remove_notifier(dev, &policy->nb_min,
DEV_PM_QOS_MIN_FREQUENCY);
@@ -1283,6 +1282,9 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy)
DEV_PM_QOS_MAX_FREQUENCY);
dev_pm_qos_remove_notifier(dev, &policy->nb_min,
DEV_PM_QOS_MIN_FREQUENCY);
+ dev_pm_qos_remove_request(policy->max_freq_req);
+ dev_pm_qos_remove_request(policy->min_freq_req);
+ kfree(policy->min_freq_req);
cpufreq_policy_put_kobj(policy);
free_cpumask_var(policy->real_cpus);
@@ -1361,16 +1363,30 @@ static int cpufreq_online(unsigned int cpu)
cpumask_and(policy->cpus, policy->cpus, cpu_online_mask);
if (new_policy) {
- policy->user_policy.min = policy->min;
- policy->user_policy.max = policy->max;
+ struct device *dev = get_cpu_device(cpu);
for_each_cpu(j, policy->related_cpus) {
per_cpu(cpufreq_cpu_data, j) = policy;
add_cpu_dev_symlink(policy, j);
}
- } else {
- policy->min = policy->user_policy.min;
- policy->max = policy->user_policy.max;
+
+ ret = dev_pm_qos_add_request(dev, policy->min_freq_req,
+ DEV_PM_QOS_MIN_FREQUENCY,
+ policy->min);
+ if (ret < 0) {
+ dev_err(dev, "Failed to add min-freq constraint (%d)\n",
+ ret);
+ goto out_destroy_policy;
+ }
+
+ ret = dev_pm_qos_add_request(dev, policy->max_freq_req,
+ DEV_PM_QOS_MAX_FREQUENCY,
+ policy->max);
+ if (ret < 0) {
+ dev_err(dev, "Failed to add max-freq constraint (%d)\n",
+ ret);
+ goto out_destroy_policy;
+ }
}
if (cpufreq_driver->get && has_target()) {
@@ -2344,7 +2360,6 @@ int cpufreq_set_policy(struct cpufreq_policy *policy,
{
struct cpufreq_governor *old_gov;
struct device *cpu_dev = get_cpu_device(policy->cpu);
- unsigned long min, max;
int ret;
pr_debug("setting new policy for CPU %u: %u - %u kHz\n",
@@ -2352,24 +2367,12 @@ int cpufreq_set_policy(struct cpufreq_policy *policy,
memcpy(&new_policy->cpuinfo, &policy->cpuinfo, sizeof(policy->cpuinfo));
- /*
- * This check works well when we store new min/max freq attributes,
- * because new_policy is a copy of policy with one field updated.
- */
- if (new_policy->min > new_policy->max)
- return -EINVAL;
-
/*
* PM QoS framework collects all the requests from users and provide us
* the final aggregated value here.
*/
- min = dev_pm_qos_read_value(cpu_dev, DEV_PM_QOS_MIN_FREQUENCY);
- max = dev_pm_qos_read_value(cpu_dev, DEV_PM_QOS_MAX_FREQUENCY);
-
- if (min > new_policy->min)
- new_policy->min = min;
- if (max < new_policy->max)
- new_policy->max = max;
+ new_policy->min = dev_pm_qos_read_value(cpu_dev, DEV_PM_QOS_MIN_FREQUENCY);
+ new_policy->max = dev_pm_qos_read_value(cpu_dev, DEV_PM_QOS_MAX_FREQUENCY);
/* verify the cpu speed can be set within this limit */
ret = cpufreq_driver->verify(new_policy);
@@ -2458,10 +2461,9 @@ int cpufreq_set_policy(struct cpufreq_policy *policy,
* @cpu: CPU to re-evaluate the policy for.
*
* Update the current frequency for the cpufreq policy of @cpu and use
- * cpufreq_set_policy() to re-apply the min and max limits saved in the
- * user_policy sub-structure of that policy, which triggers the evaluation
- * of policy notifiers and the cpufreq driver's ->verify() callback for the
- * policy in question, among other things.
+ * cpufreq_set_policy() to re-apply the min and max limits, which triggers the
+ * evaluation of policy notifiers and the cpufreq driver's ->verify() callback
+ * for the policy in question, among other things.
*/
void cpufreq_update_policy(unsigned int cpu)
{
@@ -2521,10 +2523,9 @@ static int cpufreq_boost_set_sw(int state)
break;
}
- down_write(&policy->rwsem);
- policy->user_policy.max = policy->max;
- cpufreq_governor_limits(policy);
- up_write(&policy->rwsem);
+ ret = dev_pm_qos_update_request(policy->max_freq_req, policy->max);
+ if (ret)
+ break;
}
return ret;
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 1fa37b675a80..afc683021ac5 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -50,11 +50,6 @@ struct cpufreq_cpuinfo {
unsigned int transition_latency;
};
-struct cpufreq_user_policy {
- unsigned int min; /* in kHz */
- unsigned int max; /* in kHz */
-};
-
struct cpufreq_policy {
/* CPUs sharing clock, require sw coordination */
cpumask_var_t cpus; /* Online CPUs only */
@@ -84,7 +79,8 @@ struct cpufreq_policy {
struct work_struct update; /* if update_policy() needs to be
* called, but you're in IRQ context */
- struct cpufreq_user_policy user_policy;
+ struct dev_pm_qos_request *min_freq_req;
+ struct dev_pm_qos_request *max_freq_req;
struct cpufreq_frequency_table *freq_table;
enum cpufreq_table_sorting freq_table_sorted;
--
2.21.0.rc0.269.g1a574e7a288b
^ permalink raw reply related
* [PATCH V6 6/7] cpufreq: intel_pstate: Reuse refresh_frequency_limits()
From: Viresh Kumar @ 2019-07-04 7:36 UTC (permalink / raw)
To: Rafael Wysocki, Srinivas Pandruvada, Len Brown
Cc: Viresh Kumar, linux-pm, Vincent Guittot, Rafael J . Wysocki,
linux-kernel
In-Reply-To: <cover.1562210705.git.viresh.kumar@linaro.org>
The implementation of intel_pstate_update_max_freq() is quite similar to
refresh_frequency_limits(), lets reuse it.
Finding minimum of policy->user_policy.max and policy->cpuinfo.max_freq
in intel_pstate_update_max_freq() is redundant as cpufreq_set_policy()
will call the ->verify() callback of intel-pstate driver, which will do
this comparison anyway and so dropping it from
intel_pstate_update_max_freq() doesn't harm.
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
drivers/cpufreq/cpufreq.c | 3 ++-
drivers/cpufreq/intel_pstate.c | 7 +------
include/linux/cpufreq.h | 1 +
3 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 81117e4d43cc..091789e868ee 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1116,7 +1116,7 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, unsigned int cp
return ret;
}
-static void refresh_frequency_limits(struct cpufreq_policy *policy)
+void refresh_frequency_limits(struct cpufreq_policy *policy)
{
struct cpufreq_policy new_policy;
@@ -1133,6 +1133,7 @@ static void refresh_frequency_limits(struct cpufreq_policy *policy)
up_write(&policy->rwsem);
}
+EXPORT_SYMBOL(refresh_frequency_limits);
static void handle_update(struct work_struct *work)
{
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index f2ff5de988c1..cc27d4c59dca 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -898,7 +898,6 @@ static void intel_pstate_update_policies(void)
static void intel_pstate_update_max_freq(unsigned int cpu)
{
struct cpufreq_policy *policy = cpufreq_cpu_acquire(cpu);
- struct cpufreq_policy new_policy;
struct cpudata *cpudata;
if (!policy)
@@ -908,11 +907,7 @@ static void intel_pstate_update_max_freq(unsigned int cpu)
policy->cpuinfo.max_freq = global.turbo_disabled_mf ?
cpudata->pstate.max_freq : cpudata->pstate.turbo_freq;
- memcpy(&new_policy, policy, sizeof(*policy));
- new_policy.max = min(policy->user_policy.max, policy->cpuinfo.max_freq);
- new_policy.min = min(policy->user_policy.min, new_policy.max);
-
- cpufreq_set_policy(policy, &new_policy);
+ refresh_frequency_limits(policy);
cpufreq_cpu_release(policy);
}
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 95425941f46d..1fa37b675a80 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -207,6 +207,7 @@ void cpufreq_cpu_release(struct cpufreq_policy *policy);
int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu);
int cpufreq_set_policy(struct cpufreq_policy *policy,
struct cpufreq_policy *new_policy);
+void refresh_frequency_limits(struct cpufreq_policy *policy);
void cpufreq_update_policy(unsigned int cpu);
void cpufreq_update_limits(unsigned int cpu);
bool have_governor_per_policy(void);
--
2.21.0.rc0.269.g1a574e7a288b
^ permalink raw reply related
* [PATCH V6 4/7] PM / QoS: Add support for MIN/MAX frequency constraints
From: Viresh Kumar @ 2019-07-04 7:36 UTC (permalink / raw)
To: Rafael Wysocki, Len Brown, Pavel Machek
Cc: Viresh Kumar, linux-pm, Vincent Guittot, Matthias Kaehlcke,
Ulf Hansson, Rafael J . Wysocki, linux-kernel
In-Reply-To: <cover.1562210705.git.viresh.kumar@linaro.org>
This patch introduces the min-frequency and max-frequency device
constraints, which will be used by the cpufreq core to begin with.
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
drivers/base/power/qos.c | 111 ++++++++++++++++++++++++++++++++++-----
include/linux/pm_qos.h | 12 +++++
2 files changed, 109 insertions(+), 14 deletions(-)
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index 2461fed0efa0..6c90fd7e2ff8 100644
--- a/drivers/base/power/qos.c
+++ b/drivers/base/power/qos.c
@@ -120,6 +120,14 @@ s32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type)
ret = IS_ERR_OR_NULL(qos) ? PM_QOS_RESUME_LATENCY_NO_CONSTRAINT
: pm_qos_read_value(&qos->resume_latency);
break;
+ case DEV_PM_QOS_MIN_FREQUENCY:
+ ret = IS_ERR_OR_NULL(qos) ? PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE
+ : pm_qos_read_value(&qos->min_frequency);
+ break;
+ case DEV_PM_QOS_MAX_FREQUENCY:
+ ret = IS_ERR_OR_NULL(qos) ? PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE
+ : pm_qos_read_value(&qos->max_frequency);
+ break;
default:
WARN_ON(1);
ret = 0;
@@ -161,6 +169,14 @@ static int apply_constraint(struct dev_pm_qos_request *req,
req->dev->power.set_latency_tolerance(req->dev, value);
}
break;
+ case DEV_PM_QOS_MIN_FREQUENCY:
+ ret = pm_qos_update_target(&qos->min_frequency,
+ &req->data.pnode, action, value);
+ break;
+ case DEV_PM_QOS_MAX_FREQUENCY:
+ ret = pm_qos_update_target(&qos->max_frequency,
+ &req->data.pnode, action, value);
+ break;
case DEV_PM_QOS_FLAGS:
ret = pm_qos_update_flags(&qos->flags, &req->data.flr,
action, value);
@@ -189,12 +205,11 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
if (!qos)
return -ENOMEM;
- n = kzalloc(sizeof(*n), GFP_KERNEL);
+ n = kzalloc(3 * sizeof(*n), GFP_KERNEL);
if (!n) {
kfree(qos);
return -ENOMEM;
}
- BLOCKING_INIT_NOTIFIER_HEAD(n);
c = &qos->resume_latency;
plist_head_init(&c->list);
@@ -203,6 +218,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
c->no_constraint_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
c->type = PM_QOS_MIN;
c->notifiers = n;
+ BLOCKING_INIT_NOTIFIER_HEAD(n);
c = &qos->latency_tolerance;
plist_head_init(&c->list);
@@ -211,6 +227,24 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
c->no_constraint_value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT;
c->type = PM_QOS_MIN;
+ c = &qos->min_frequency;
+ plist_head_init(&c->list);
+ c->target_value = PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE;
+ c->default_value = PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE;
+ c->no_constraint_value = PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE;
+ c->type = PM_QOS_MAX;
+ c->notifiers = ++n;
+ BLOCKING_INIT_NOTIFIER_HEAD(n);
+
+ c = &qos->max_frequency;
+ plist_head_init(&c->list);
+ c->target_value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE;
+ c->default_value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE;
+ c->no_constraint_value = PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE;
+ c->type = PM_QOS_MIN;
+ c->notifiers = ++n;
+ BLOCKING_INIT_NOTIFIER_HEAD(n);
+
INIT_LIST_HEAD(&qos->flags.list);
spin_lock_irq(&dev->power.lock);
@@ -264,11 +298,25 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
memset(req, 0, sizeof(*req));
}
+
c = &qos->latency_tolerance;
plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) {
apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
memset(req, 0, sizeof(*req));
}
+
+ c = &qos->min_frequency;
+ plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) {
+ apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE);
+ memset(req, 0, sizeof(*req));
+ }
+
+ c = &qos->max_frequency;
+ plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) {
+ apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE);
+ memset(req, 0, sizeof(*req));
+ }
+
f = &qos->flags;
list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) {
apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
@@ -380,6 +428,8 @@ static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req,
switch(req->type) {
case DEV_PM_QOS_RESUME_LATENCY:
case DEV_PM_QOS_LATENCY_TOLERANCE:
+ case DEV_PM_QOS_MIN_FREQUENCY:
+ case DEV_PM_QOS_MAX_FREQUENCY:
curr_value = req->data.pnode.prio;
break;
case DEV_PM_QOS_FLAGS:
@@ -492,9 +542,6 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier,
{
int ret = 0;
- if (WARN_ON(type != DEV_PM_QOS_RESUME_LATENCY))
- return -EINVAL;
-
mutex_lock(&dev_pm_qos_mtx);
if (IS_ERR(dev->power.qos))
@@ -502,10 +549,28 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier,
else if (!dev->power.qos)
ret = dev_pm_qos_constraints_allocate(dev);
- if (!ret)
+ if (ret)
+ goto unlock;
+
+ switch (type) {
+ case DEV_PM_QOS_RESUME_LATENCY:
ret = blocking_notifier_chain_register(dev->power.qos->resume_latency.notifiers,
notifier);
+ break;
+ case DEV_PM_QOS_MIN_FREQUENCY:
+ ret = blocking_notifier_chain_register(dev->power.qos->min_frequency.notifiers,
+ notifier);
+ break;
+ case DEV_PM_QOS_MAX_FREQUENCY:
+ ret = blocking_notifier_chain_register(dev->power.qos->max_frequency.notifiers,
+ notifier);
+ break;
+ default:
+ WARN_ON(1);
+ ret = -EINVAL;
+ }
+unlock:
mutex_unlock(&dev_pm_qos_mtx);
return ret;
}
@@ -526,20 +591,35 @@ int dev_pm_qos_remove_notifier(struct device *dev,
struct notifier_block *notifier,
enum dev_pm_qos_req_type type)
{
- int retval = 0;
-
- if (WARN_ON(type != DEV_PM_QOS_RESUME_LATENCY))
- return -EINVAL;
+ int ret = 0;
mutex_lock(&dev_pm_qos_mtx);
/* Silently return if the constraints object is not present. */
- if (!IS_ERR_OR_NULL(dev->power.qos))
- retval = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers,
- notifier);
+ if (IS_ERR_OR_NULL(dev->power.qos))
+ goto unlock;
+
+ switch (type) {
+ case DEV_PM_QOS_RESUME_LATENCY:
+ ret = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers,
+ notifier);
+ break;
+ case DEV_PM_QOS_MIN_FREQUENCY:
+ ret = blocking_notifier_chain_unregister(dev->power.qos->min_frequency.notifiers,
+ notifier);
+ break;
+ case DEV_PM_QOS_MAX_FREQUENCY:
+ ret = blocking_notifier_chain_unregister(dev->power.qos->max_frequency.notifiers,
+ notifier);
+ break;
+ default:
+ WARN_ON(1);
+ ret = -EINVAL;
+ }
+unlock:
mutex_unlock(&dev_pm_qos_mtx);
- return retval;
+ return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_qos_remove_notifier);
@@ -599,6 +679,9 @@ static void __dev_pm_qos_drop_user_request(struct device *dev,
req = dev->power.qos->flags_req;
dev->power.qos->flags_req = NULL;
break;
+ default:
+ WARN_ON(1);
+ return;
}
__dev_pm_qos_remove_request(req);
kfree(req);
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 9a21b7ba72ae..2aebbc5b9950 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -40,6 +40,8 @@ enum pm_qos_flags_status {
#define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT PM_QOS_LATENCY_ANY
#define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS PM_QOS_LATENCY_ANY_NS
#define PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE 0
+#define PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE 0
+#define PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE (-1)
#define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT (-1)
#define PM_QOS_FLAG_NO_POWER_OFF (1 << 0)
@@ -58,6 +60,8 @@ struct pm_qos_flags_request {
enum dev_pm_qos_req_type {
DEV_PM_QOS_RESUME_LATENCY = 1,
DEV_PM_QOS_LATENCY_TOLERANCE,
+ DEV_PM_QOS_MIN_FREQUENCY,
+ DEV_PM_QOS_MAX_FREQUENCY,
DEV_PM_QOS_FLAGS,
};
@@ -99,10 +103,14 @@ struct pm_qos_flags {
struct dev_pm_qos {
struct pm_qos_constraints resume_latency;
struct pm_qos_constraints latency_tolerance;
+ struct pm_qos_constraints min_frequency;
+ struct pm_qos_constraints max_frequency;
struct pm_qos_flags flags;
struct dev_pm_qos_request *resume_latency_req;
struct dev_pm_qos_request *latency_tolerance_req;
struct dev_pm_qos_request *flags_req;
+ struct dev_pm_qos_request *min_frequency_req;
+ struct dev_pm_qos_request *max_frequency_req;
};
/* Action requested to pm_qos_update_target */
@@ -197,6 +205,10 @@ static inline s32 dev_pm_qos_read_value(struct device *dev,
switch (type) {
case DEV_PM_QOS_RESUME_LATENCY:
return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
+ case DEV_PM_QOS_MIN_FREQUENCY:
+ return PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE;
+ case DEV_PM_QOS_MAX_FREQUENCY:
+ return PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE;
default:
WARN_ON(1);
return 0;
--
2.21.0.rc0.269.g1a574e7a288b
^ permalink raw reply related
* [PATCH V6 1/7] PM / QOS: Pass request type to dev_pm_qos_{add|remove}_notifier()
From: Viresh Kumar @ 2019-07-04 7:36 UTC (permalink / raw)
To: Rafael Wysocki, Len Brown, Pavel Machek, Kevin Hilman,
Ulf Hansson
Cc: Viresh Kumar, linux-pm, Vincent Guittot, Matthias Kaehlcke,
Rafael J . Wysocki, linux-kernel
In-Reply-To: <cover.1562210705.git.viresh.kumar@linaro.org>
In order to use the same set of routines to register notifiers for
different request types, update the existing
dev_pm_qos_{add|remove}_notifier() routines with an additional
parameter: request-type.
For now, it only supports resume-latency request type but will be
extended to frequency limit (min/max) constraints later on.
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
Documentation/power/pm_qos_interface.txt | 10 ++++++----
drivers/base/power/domain.c | 8 +++++---
drivers/base/power/qos.c | 14 ++++++++++++--
include/linux/pm_qos.h | 12 ++++++++----
4 files changed, 31 insertions(+), 13 deletions(-)
diff --git a/Documentation/power/pm_qos_interface.txt b/Documentation/power/pm_qos_interface.txt
index 19c5f7b1a7ba..ec7d662d1707 100644
--- a/Documentation/power/pm_qos_interface.txt
+++ b/Documentation/power/pm_qos_interface.txt
@@ -164,12 +164,14 @@ directory.
Notification mechanisms:
The per-device PM QoS framework has a per-device notification tree.
-int dev_pm_qos_add_notifier(device, notifier):
-Adds a notification callback function for the device.
+int dev_pm_qos_add_notifier(device, notifier, type):
+Adds a notification callback function for the device for a particular request
+type.
+
The callback is called when the aggregated value of the device constraints list
-is changed (for resume latency device PM QoS only).
+is changed.
-int dev_pm_qos_remove_notifier(device, notifier):
+int dev_pm_qos_remove_notifier(device, notifier, type):
Removes the notification callback function for the device.
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 33c30c1e6a30..b063bc41b0a9 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1536,7 +1536,8 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
if (ret)
genpd_free_dev_data(dev, gpd_data);
else
- dev_pm_qos_add_notifier(dev, &gpd_data->nb);
+ dev_pm_qos_add_notifier(dev, &gpd_data->nb,
+ DEV_PM_QOS_RESUME_LATENCY);
return ret;
}
@@ -1569,7 +1570,8 @@ static int genpd_remove_device(struct generic_pm_domain *genpd,
pdd = dev->power.subsys_data->domain_data;
gpd_data = to_gpd_data(pdd);
- dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
+ dev_pm_qos_remove_notifier(dev, &gpd_data->nb,
+ DEV_PM_QOS_RESUME_LATENCY);
genpd_lock(genpd);
@@ -1597,7 +1599,7 @@ static int genpd_remove_device(struct generic_pm_domain *genpd,
out:
genpd_unlock(genpd);
- dev_pm_qos_add_notifier(dev, &gpd_data->nb);
+ dev_pm_qos_add_notifier(dev, &gpd_data->nb, DEV_PM_QOS_RESUME_LATENCY);
return ret;
}
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c
index 6c91f8df1d59..cfd463212513 100644
--- a/drivers/base/power/qos.c
+++ b/drivers/base/power/qos.c
@@ -467,6 +467,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
*
* @dev: target device for the constraint
* @notifier: notifier block managed by caller.
+ * @type: request type.
*
* Will register the notifier into a notification chain that gets called
* upon changes to the target value for the device.
@@ -474,10 +475,14 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
* If the device's constraints object doesn't exist when this routine is called,
* it will be created (or error code will be returned if that fails).
*/
-int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
+int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier,
+ enum dev_pm_qos_req_type type)
{
int ret = 0;
+ if (WARN_ON(type != DEV_PM_QOS_RESUME_LATENCY))
+ return -EINVAL;
+
mutex_lock(&dev_pm_qos_mtx);
if (IS_ERR(dev->power.qos))
@@ -500,15 +505,20 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
*
* @dev: target device for the constraint
* @notifier: notifier block to be removed.
+ * @type: request type.
*
* Will remove the notifier from the notification chain that gets called
* upon changes to the target value.
*/
int dev_pm_qos_remove_notifier(struct device *dev,
- struct notifier_block *notifier)
+ struct notifier_block *notifier,
+ enum dev_pm_qos_req_type type)
{
int retval = 0;
+ if (WARN_ON(type != DEV_PM_QOS_RESUME_LATENCY))
+ return -EINVAL;
+
mutex_lock(&dev_pm_qos_mtx);
/* Silently return if the constraints object is not present. */
diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h
index 6ea1ae373d77..58e8749ceac5 100644
--- a/include/linux/pm_qos.h
+++ b/include/linux/pm_qos.h
@@ -146,9 +146,11 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value);
int dev_pm_qos_remove_request(struct dev_pm_qos_request *req);
int dev_pm_qos_add_notifier(struct device *dev,
- struct notifier_block *notifier);
+ struct notifier_block *notifier,
+ enum dev_pm_qos_req_type type);
int dev_pm_qos_remove_notifier(struct device *dev,
- struct notifier_block *notifier);
+ struct notifier_block *notifier,
+ enum dev_pm_qos_req_type type);
void dev_pm_qos_constraints_init(struct device *dev);
void dev_pm_qos_constraints_destroy(struct device *dev);
int dev_pm_qos_add_ancestor_request(struct device *dev,
@@ -202,10 +204,12 @@ static inline int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
static inline int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
{ return 0; }
static inline int dev_pm_qos_add_notifier(struct device *dev,
- struct notifier_block *notifier)
+ struct notifier_block *notifier,
+ enum dev_pm_qos_req_type type)
{ return 0; }
static inline int dev_pm_qos_remove_notifier(struct device *dev,
- struct notifier_block *notifier)
+ struct notifier_block *notifier,
+ enum dev_pm_qos_req_type type)
{ return 0; }
static inline void dev_pm_qos_constraints_init(struct device *dev)
{
--
2.21.0.rc0.269.g1a574e7a288b
^ permalink raw reply related
* [PATCH V6 0/7] cpufreq: Use QoS layer to manage freq-constraints
From: Viresh Kumar @ 2019-07-04 7:36 UTC (permalink / raw)
To: Rafael Wysocki, Daniel Lezcano, Kevin Hilman, Len Brown,
Len Brown, Pavel Machek, Srinivas Pandruvada, Ulf Hansson
Cc: Viresh Kumar, linux-pm, Vincent Guittot, linux-kernel,
Matthias Kaehlcke, Rafael J. Wysocki
Hello,
Sorry about another version after another round of embarrassment over
compilation issues. The same patches have gone through pm/linux-next and
linux-next/master earlier, not sure why the current issues never came up
before. Anyway, they were stupid coding mistakes :(
I have pushed these patches again for the build bot to test, which
should finish before these get merged in PM tree now.
This patchset attempts to manage CPU frequency constraints using the PM
QoS framework. It only does the basic stuff right now and moves the
userspace constraints to use the QoS infrastructure.
Todo:
- Migrate all users to the QoS framework and get rid of cpufreq specific
notifiers.
- Make PM QoS learn about the relation of CPUs in a policy, so a single
list of constraints is managed for all of them instead of per-cpu
constraints.
V5->V6:
- Fixed compilation issues with !CONFIG_PM case on riscv architecture.
V4->V5:
- A new patch added, 6/7.
V3->V4:
- Few commit logs updated as suggested during reviews.
- Separate commit (2/6) to create resume-latency specific routines
- Reused earlier work ("update") for notifiers as well.
- Kept Reviewed-by tags as is as the patches normally got better only.
Please take them back if you find any issues.
V2->V3:
- Add a comment in cpufreq.c as suggested by Qais.
- Rebased on latest pm/linux-next.
V1->V2:
- The previous version introduced a completely new framework, this one
moves to PM QoS instead.
- Lots of changes because of this.
--
viresh
Viresh Kumar (7):
PM / QOS: Pass request type to dev_pm_qos_{add|remove}_notifier()
PM / QOS: Rename __dev_pm_qos_read_value() and
dev_pm_qos_raw_read_value()
PM / QOS: Pass request type to dev_pm_qos_read_value()
PM / QoS: Add support for MIN/MAX frequency constraints
cpufreq: Register notifiers with the PM QoS framework
cpufreq: intel_pstate: Reuse refresh_frequency_limits()
cpufreq: Add QoS requests for userspace constraints
Documentation/power/pm_qos_interface.txt | 12 +-
drivers/base/power/domain.c | 8 +-
drivers/base/power/domain_governor.c | 4 +-
drivers/base/power/qos.c | 135 +++++++++++++--
drivers/base/power/runtime.c | 2 +-
drivers/cpufreq/cpufreq.c | 201 ++++++++++++++++-------
drivers/cpufreq/intel_pstate.c | 7 +-
drivers/cpuidle/governor.c | 2 +-
include/linux/cpufreq.h | 12 +-
include/linux/pm_qos.h | 48 ++++--
10 files changed, 320 insertions(+), 111 deletions(-)
--
2.21.0.rc0.269.g1a574e7a288b
^ permalink raw reply
* Re: [PATCH v3 6/6] interconnect: Add OPP table support for interconnects
From: Vincent Guittot @ 2019-07-04 7:12 UTC (permalink / raw)
To: Saravana Kannan
Cc: Georgi Djakov, Rob Herring, Mark Rutland, Viresh Kumar,
Nishanth Menon, Stephen Boyd, Rafael J. Wysocki, Sweeney, Sean,
daidavid1, Rajendra Nayak, sibis, Bjorn Andersson, Evan Green,
Android Kernel Team, open list:THERMAL,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
linux-kernel
In-Reply-To: <CAGETcx_5gu84FOVmELPnK5uJTE0NEhxYKtdFigoXGyFtjehQvw@mail.gmail.com>
On Wed, 3 Jul 2019 at 23:33, Saravana Kannan <saravanak@google.com> wrote:
>
> On Tue, Jul 2, 2019 at 11:45 PM Vincent Guittot
> <vincent.guittot@linaro.org> wrote:
> >
> > On Wed, 3 Jul 2019 at 03:10, Saravana Kannan <saravanak@google.com> wrote:
> > >
> > > Interconnect paths can have different performance points. Now that OPP
> > > framework supports bandwidth OPP tables, add OPP table support for
> > > interconnects.
> > >
> > > Devices can use the interconnect-opp-table DT property to specify OPP
> > > tables for interconnect paths. And the driver can obtain the OPP table for
> > > an interconnect path by calling icc_get_opp_table().
> >
> > The opp table of a path must come from the aggregation of OPP tables
> > of the interconnect providers.
>
> The aggregation of OPP tables of the providers is certainly the
> superset of what a path can achieve, but to say that OPPs for
> interconnect path should match that superset is an oversimplification
> of the reality in hardware.
>
> There are lots of reasons an interconnect path might not want to use
> all the available bandwidth options across all the interconnects in
> the route.
>
> 1. That particular path might not have been validated or verified
> during the HW design process for some of the frequencies/bandwidth
> combinations of the providers.
All these constraint are provider's constraints and not consumer's one
The consumer asks for a bandwidth according to its needs and then the
providers select the optimal bandwidth of each interconnect after
aggregating all the request and according to what OPP have been
validated
>
> 2. Similarly during parts screening in the factory, some of the
> combinations might not have been screened and can't be guaranteed
> to work.
As above, it's the provider's job to select the final bandwidth
according to its constraint
>
> 3. Only a certain set of bandwidth levels might make sense to use from
> a power/performance balance given the device using it. For example:
> - The big CPU might not want to use some of the lower bandwidths
> but the little CPU might want to.
> - The big CPU might not want to use some intermediate bandwidth
> points if they don't save a lot of power compared to a higher
> bandwidth levels, but the little CPU might want to.
> - The little CPU might never want to use the higher set of
> bandwidth levels since they won't be power efficient for the use
> cases that might run on it.
These example are quite vague about the reasons why little might never
want to use higher bandwidth.
But then, if little doesn't ask high bandwidth it will not use them.
>
> 4. It might not make sense from a system level power perspective.
> Let's take an example of a path S (source) -> A -> B -> C -> D
> (destination).
> - A supports only 2, 5, 7 and 10 GB/s. B supports 1, 2 ... 10 GB/s.
> C supports 5 and 10 GB/s
> - If you combine and list the superset of bandwidth levels
> supported in that path, that'd be 1, 2, 3, ... 10 GB/s.
> - Which set of bandwidth levels make sense will depend on the
> hardware characteristics of the interconnects.
> - If B is the biggest power sink, then you might want to use all 10
> levels.
> - If A is the biggest power sink, then you might want to use all 2,
> 5 and 10 GB/s of the levels.
> - If C is the biggest power sink then you might only want to use 5
> and 10 GB/s
> - The more hops and paths you get the more convoluted this gets.
>
> 5. The design of the interconnects themselves might have an impact on
> which bandwidth levels are used.
> - For example, the FIFO depth between two specific interconnects
> might affect the valid bandwidth levels for a specific path.
> - Say S1 -> A -> B -> D1, S2 -> C -> B -> D1 and S2 -> C -> D2 are
> three paths.
> - If C <-> B FIFO depth is small, then there might be a requirement
> that C and B be closely performance matched to avoid system level
> congestion due to back pressure.
> - So S2 -> D1 path can't use all the bandwidth levels supported by
> C-B combination.
> - But S2 -> D2 can use all the bandwidth levels supported by C.
> - And S1 -> D1 can use all the levels supported by A-B combination.
>
All the examples above makes sense but have to be handle by the
provider not the consumer. The consumer asks for a bandwidth according
to its constraints. Then the provider which is the driver that manages
the interconnect IP, should manage all this hardware and platform
specific stuff related to the interconnect IP in order to set the
optimal bandwidth that fit both consumer constraint and platform
specific configuration.
> These are just some of the reasons I could recollect in a few minutes.
> These are all real world cases I had to deal with in the past several
> years of dealing with scaling interconnects. I'm sure vendors and SoCs
> I'm not familiar with have other good reasons I'm not aware of.
>
> Trying to figure this all out by aggregating OPP tables of
> interconnect providers just isn't feasible nor is it efficient. The
> OPP tables for an interconnect path is describing the valid BW levels
> supported by that path and verified in hardware and makes a lot of
> sense to capture it clearly in DT.
>
> > So such kind of OPP table should be at
> > provider level but not at path level.
>
> They can also use it if they want to, but they'll probably want to use
> a frequency OPP table.
>
>
> -Saravana
>
> >
> > >
> > > Signed-off-by: Saravana Kannan <saravanak@google.com>
> > > ---
> > > drivers/interconnect/core.c | 27 ++++++++++++++++++++++++++-
> > > include/linux/interconnect.h | 7 +++++++
> > > 2 files changed, 33 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c
> > > index 871eb4bc4efc..881bac80bc1e 100644
> > > --- a/drivers/interconnect/core.c
> > > +++ b/drivers/interconnect/core.c
> > > @@ -47,6 +47,7 @@ struct icc_req {
> > > */
> > > struct icc_path {
> > > size_t num_nodes;
> > > + struct opp_table *opp_table;
> > > struct icc_req reqs[];
> > > };
> > >
> > > @@ -313,7 +314,7 @@ struct icc_path *of_icc_get(struct device *dev, const char *name)
> > > {
> > > struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
> > > struct icc_node *src_node, *dst_node;
> > > - struct device_node *np = NULL;
> > > + struct device_node *np = NULL, *opp_node;
> > > struct of_phandle_args src_args, dst_args;
> > > int idx = 0;
> > > int ret;
> > > @@ -381,10 +382,34 @@ struct icc_path *of_icc_get(struct device *dev, const char *name)
> > > dev_err(dev, "%s: invalid path=%ld\n", __func__, PTR_ERR(path));
> > > mutex_unlock(&icc_lock);
> > >
> > > + opp_node = of_parse_phandle(np, "interconnect-opp-table", idx);
> > > + if (opp_node) {
> > > + path->opp_table = dev_pm_opp_of_find_table_from_node(opp_node);
> > > + of_node_put(opp_node);
> > > + }
> > > +
> > > +
> > > return path;
> > > }
> > > EXPORT_SYMBOL_GPL(of_icc_get);
> > >
> > > +/**
> > > + * icc_get_opp_table() - Get the OPP table that corresponds to a path
> > > + * @path: reference to the path returned by icc_get()
> > > + *
> > > + * This function will return the OPP table that corresponds to a path handle.
> > > + * If the interconnect API is disabled, NULL is returned and the consumer
> > > + * drivers will still build. Drivers are free to handle this specifically, but
> > > + * they don't have to.
> > > + *
> > > + * Return: opp_table pointer on success. NULL is returned when the API is
> > > + * disabled or the OPP table is missing.
> > > + */
> > > +struct opp_table *icc_get_opp_table(struct icc_path *path)
> > > +{
> > > + return path->opp_table;
> > > +}
> > > +
> > > /**
> > > * icc_set_bw() - set bandwidth constraints on an interconnect path
> > > * @path: reference to the path returned by icc_get()
> > > diff --git a/include/linux/interconnect.h b/include/linux/interconnect.h
> > > index dc25864755ba..0c0bc55f0e89 100644
> > > --- a/include/linux/interconnect.h
> > > +++ b/include/linux/interconnect.h
> > > @@ -9,6 +9,7 @@
> > >
> > > #include <linux/mutex.h>
> > > #include <linux/types.h>
> > > +#include <linux/pm_opp.h>
> > >
> > > /* macros for converting to icc units */
> > > #define Bps_to_icc(x) ((x) / 1000)
> > > @@ -28,6 +29,7 @@ struct device;
> > > struct icc_path *icc_get(struct device *dev, const int src_id,
> > > const int dst_id);
> > > struct icc_path *of_icc_get(struct device *dev, const char *name);
> > > +struct opp_table *icc_get_opp_table(struct icc_path *path);
> > > void icc_put(struct icc_path *path);
> > > int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw);
> > >
> > > @@ -49,6 +51,11 @@ static inline void icc_put(struct icc_path *path)
> > > {
> > > }
> > >
> > > +static inline struct opp_table *icc_get_opp_table(struct icc_path *path)
> > > +{
> > > + return NULL;
> > > +}
> > > +
> > > static inline int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)
> > > {
> > > return 0;
> > > --
> > > 2.22.0.410.gd8fdbe21b5-goog
> > >
^ permalink raw reply
* Re: [PATCH 1/2] opp: Attach genpds to devices from within OPP core
From: Rajendra Nayak @ 2019-07-04 6:25 UTC (permalink / raw)
To: Viresh Kumar, Rafael Wysocki, niklas.cassel, Viresh Kumar,
Nishanth Menon, Stephen Boyd
Cc: linux-pm, Vincent Guittot, Amit Kucheria, linux-kernel
In-Reply-To: <1bc9053f5c41a10832b58a2a81decbad7f1aded9.1557742920.git.viresh.kumar@linaro.org>
On 5/13/2019 3:54 PM, Viresh Kumar wrote:
> The OPP core requires the virtual device pointers to set performance
> state on behalf of the device, for the multiple power domain case. The
> genpd API (dev_pm_domain_attach_by_name()) has evolved now to support
> even the single power domain case and that lets us add common code for
> handling both the cases more efficiently.
>
> The virtual device structure returned by dev_pm_domain_attach_by_name()
> isn't normally used by the cpufreq drivers as they don't manage power
> on/off of the domains and so is only useful for the OPP core.
This might be true for CPUs but not necessarily so for IO devices which might
want to set up device links so the devices runtime pm calls can trigger the
on/off of the power domain. Is there a way to handle that case now?
>
> This patch moves all the complexity into the OPP core to make the end
> drivers simple. The earlier APIs dev_pm_opp_{set|put}_genpd_virt_dev()
> are reworked into dev_pm_opp_{attach|detach}_genpd(). The new helper
> dev_pm_opp_attach_genpd() accepts a NULL terminated array of strings
> which contains names of all the genpd's to attach. It then attaches all
> the domains and saves the pointers to the virtual devices. The other
> helper undo the work done by this helper.
>
> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
> ---
> @Niklas: Can you please try these patches and confirm they solve the
> issues you were facing ?
>
> drivers/opp/core.c | 128 ++++++++++++++++++++++++++---------------
> include/linux/pm_opp.h | 8 +--
> 2 files changed, 86 insertions(+), 50 deletions(-)
>
> diff --git a/drivers/opp/core.c b/drivers/opp/core.c
> index 0e7703fe733f..67d6b0caeab1 100644
> --- a/drivers/opp/core.c
> +++ b/drivers/opp/core.c
> @@ -1744,91 +1744,127 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table)
> }
> EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper);
>
> +static void _opp_detach_genpd(struct opp_table *opp_table)
> +{
> + int index;
> +
> + for (index = 0; index < opp_table->required_opp_count; index++) {
> + if (!opp_table->genpd_virt_devs[index])
> + continue;
> +
> + dev_pm_domain_detach(opp_table->genpd_virt_devs[index], false);
> + opp_table->genpd_virt_devs[index] = NULL;
> + }
> +}
> +
> /**
> - * dev_pm_opp_set_genpd_virt_dev - Set virtual genpd device for an index
> - * @dev: Consumer device for which the genpd device is getting set.
> - * @virt_dev: virtual genpd device.
> - * @index: index.
> + * dev_pm_opp_attach_genpd - Attach genpd(s) for the device and save virtual device pointer
> + * @dev: Consumer device for which the genpd is getting attached.
> + * @names: Null terminated array of pointers containing names of genpd to attach.
> *
> * Multiple generic power domains for a device are supported with the help of
> * virtual genpd devices, which are created for each consumer device - genpd
> * pair. These are the device structures which are attached to the power domain
> * and are required by the OPP core to set the performance state of the genpd.
> + * The same API also works for the case where single genpd is available and so
> + * we don't need to support that separately.
> *
> * This helper will normally be called by the consumer driver of the device
> - * "dev", as only that has details of the genpd devices.
> + * "dev", as only that has details of the genpd names.
> *
> - * This helper needs to be called once for each of those virtual devices, but
> - * only if multiple domains are available for a device. Otherwise the original
> - * device structure will be used instead by the OPP core.
> + * This helper needs to be called once with a list of all genpd to attach.
> + * Otherwise the original device structure will be used instead by the OPP core.
> */
> -struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev,
> - struct device *virt_dev,
> - int index)
> +struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names)
> {
> struct opp_table *opp_table;
> + struct device *virt_dev;
> + int index, ret = -EINVAL;
> + const char **name = names;
>
> opp_table = dev_pm_opp_get_opp_table(dev);
> if (!opp_table)
> return ERR_PTR(-ENOMEM);
>
> + /*
> + * If the genpd's OPP table isn't already initialized, parsing of the
> + * required-opps fail for dev. We should retry this after genpd's OPP
> + * table is added.
> + */
> + if (!opp_table->required_opp_count) {
> + ret = -EPROBE_DEFER;
> + goto put_table;
> + }
> +
> mutex_lock(&opp_table->genpd_virt_dev_lock);
>
> - if (unlikely(!opp_table->genpd_virt_devs ||
> - index >= opp_table->required_opp_count ||
> - opp_table->genpd_virt_devs[index])) {
> + while (*name) {
> + index = of_property_match_string(dev->of_node,
> + "power-domain-names", *name);
> + if (index < 0) {
> + dev_err(dev, "Failed to find power domain: %s (%d)\n",
> + *name, index);
> + goto err;
> + }
>
> - dev_err(dev, "Invalid request to set required device\n");
> - dev_pm_opp_put_opp_table(opp_table);
> - mutex_unlock(&opp_table->genpd_virt_dev_lock);
> + if (index >= opp_table->required_opp_count) {
> + dev_err(dev, "Index can't be greater than required-opp-count - 1, %s (%d : %d)\n",
> + *name, opp_table->required_opp_count, index);
> + goto err;
> + }
>
> - return ERR_PTR(-EINVAL);
> + if (opp_table->genpd_virt_devs[index]) {
> + dev_err(dev, "Genpd virtual device already set %s\n",
> + *name);
> + goto err;
> + }
> +
> + virt_dev = dev_pm_domain_attach_by_name(dev, *name);
> + if (IS_ERR(virt_dev)) {
> + ret = PTR_ERR(virt_dev);
> + dev_err(dev, "Couldn't attach to pm_domain: %d\n", ret);
> + goto err;
> + }
> +
> + opp_table->genpd_virt_devs[index] = virt_dev;
> + name++;
> }
>
> - opp_table->genpd_virt_devs[index] = virt_dev;
> mutex_unlock(&opp_table->genpd_virt_dev_lock);
>
> return opp_table;
> +
> +err:
> + _opp_detach_genpd(opp_table);
> + mutex_unlock(&opp_table->genpd_virt_dev_lock);
> +
> +put_table:
> + dev_pm_opp_put_opp_table(opp_table);
> +
> + return ERR_PTR(ret);
> }
> +EXPORT_SYMBOL_GPL(dev_pm_opp_attach_genpd);
>
> /**
> - * dev_pm_opp_put_genpd_virt_dev() - Releases resources blocked for genpd device.
> - * @opp_table: OPP table returned by dev_pm_opp_set_genpd_virt_dev().
> - * @virt_dev: virtual genpd device.
> - *
> - * This releases the resource previously acquired with a call to
> - * dev_pm_opp_set_genpd_virt_dev(). The consumer driver shall call this helper
> - * if it doesn't want OPP core to update performance state of a power domain
> - * anymore.
> + * dev_pm_opp_detach_genpd() - Detach genpd(s) from the device.
> + * @opp_table: OPP table returned by dev_pm_opp_attach_genpd().
> + *
> + * This detaches the genpd(s), resets the virtual device pointers, and puts the
> + * OPP table.
> */
> -void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table,
> - struct device *virt_dev)
> +void dev_pm_opp_detach_genpd(struct opp_table *opp_table)
> {
> - int i;
> -
> /*
> * Acquire genpd_virt_dev_lock to make sure virt_dev isn't getting
> * used in parallel.
> */
> mutex_lock(&opp_table->genpd_virt_dev_lock);
> -
> - for (i = 0; i < opp_table->required_opp_count; i++) {
> - if (opp_table->genpd_virt_devs[i] != virt_dev)
> - continue;
> -
> - opp_table->genpd_virt_devs[i] = NULL;
> - dev_pm_opp_put_opp_table(opp_table);
> -
> - /* Drop the vote */
> - dev_pm_genpd_set_performance_state(virt_dev, 0);
> - break;
> - }
> -
> + _opp_detach_genpd(opp_table);
> mutex_unlock(&opp_table->genpd_virt_dev_lock);
>
> - if (unlikely(i == opp_table->required_opp_count))
> - dev_err(virt_dev, "Failed to find required device entry\n");
> + dev_pm_opp_put_opp_table(opp_table);
> }
> +EXPORT_SYMBOL_GPL(dev_pm_opp_detach_genpd);
>
> /**
> * dev_pm_opp_xlate_performance_state() - Find required OPP's pstate for src_table.
> diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
> index b150fe97ce5a..be570761b77a 100644
> --- a/include/linux/pm_opp.h
> +++ b/include/linux/pm_opp.h
> @@ -131,8 +131,8 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name);
> void dev_pm_opp_put_clkname(struct opp_table *opp_table);
> struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data));
> void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table);
> -struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev, struct device *virt_dev, int index);
> -void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table, struct device *virt_dev);
> +struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names);
> +void dev_pm_opp_detach_genpd(struct opp_table *opp_table);
> int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate);
> int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
> int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
> @@ -295,12 +295,12 @@ static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const
>
> static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {}
>
> -static inline struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev, struct device *virt_dev, int index)
> +static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names)
> {
> return ERR_PTR(-ENOTSUPP);
> }
>
> -static inline void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table, struct device *virt_dev) {}
> +static inline void dev_pm_opp_detach_genpd(struct opp_table *opp_table) {}
>
> static inline int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate)
> {
>
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply
* Re: [pm:linux-next 91/102] include/linux/pm_qos.h:197:9: error: expected '(' before 'type'
From: Viresh Kumar @ 2019-07-04 3:18 UTC (permalink / raw)
To: kbuild test robot
Cc: kbuild-all, linux-acpi, devel, linux-pm, Rafael J. Wysocki,
Matthias Kaehlcke, Ulf Hansson
In-Reply-To: <201907040956.ywZASIcA%lkp@intel.com>
On 04-07-19, 09:04, kbuild test robot wrote:
> tree: https://kernel.googlesource.com/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
> head: 267eacd79cadd5e2d6307320497d66119907c411
> commit: 57fa6137402b98327d1247c5aaf3d4f0595a73fe [91/102] PM / QOS: Pass request type to dev_pm_qos_read_value()
> config: riscv-allnoconfig (attached as .config)
> compiler: riscv64-linux-gcc (GCC) 7.4.0
> reproduce:
> wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
> chmod +x ~/bin/make.cross
> git checkout 57fa6137402b98327d1247c5aaf3d4f0595a73fe
> # save the attached .config to linux build tree
> GCC_VERSION=7.4.0 make.cross ARCH=riscv
>
> If you fix the issue, kindly add following tag
> Reported-by: kbuild test robot <lkp@intel.com>
>
> All error/warnings (new ones prefixed by >>):
>
> In file included from kernel//power/qos.c:33:0:
> include/linux/pm_qos.h: In function 'dev_pm_qos_read_value':
> >> include/linux/pm_qos.h:197:9: error: expected '(' before 'type'
> switch type {
> ^~~~
> include/linux/pm_qos.h:197:9: warning: statement with no effect [-Wunused-value]
> >> include/linux/pm_qos.h:204:1: warning: no return statement in function returning non-void [-Wreturn-type]
> }
> ^
> include/linux/pm_qos.h: At top level:
> include/linux/pm_qos.h:219:4: error: expected identifier or '(' before '{' token
> { return 0; }
> ^
> include/linux/pm_qos.h:216:19: warning: 'dev_pm_qos_add_notifier' declared 'static' but never defined [-Wunused-function]
> static inline int dev_pm_qos_add_notifier(struct device *dev,
Very embarrassing. Fixed these two now. Thanks.
--
viresh
^ permalink raw reply
* Re: [RFCv2 0/8] Add imx8mm bus frequency switching
From: Saravana Kannan @ 2019-07-04 3:02 UTC (permalink / raw)
To: Leonard Crestez
Cc: Viresh Kumar, Alexandre Bailon, Georgi Djakov, Stephen Boyd,
Michael Turquette, MyungJoo Ham, Kyungmin Park, Shawn Guo,
Aisheng Dong, Fabio Estevam, Rafael J. Wysocki, Jacky Bai,
Anson Huang, Abel Vesa, Krzysztof Kozlowski, Ulf Hansson,
kernel@pengutronix.de, dl-linux-imx, Linux PM,
linux-clk@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
Vincent Guittot
In-Reply-To: <DB7PR04MB505163FDCAD7BE9A0C71A65EEEFB0@DB7PR04MB5051.eurprd04.prod.outlook.com>
On Wed, Jul 3, 2019 at 4:30 PM Leonard Crestez <leonard.crestez@nxp.com> wrote:
>
> On 7/4/2019 1:20 AM, Saravana Kannan wrote:
>
> >> The interconnect and devfreq parts do not communicate explicitly: they both
> >> just call clk_set_min_rate and the clk core picks the minimum value that can
> >> satisfy both. They are thus completely independent.
> >
> > Two different parts not talking to each other and just setting min
> > rate can cause problems for some concurrency use cases. ICC framework
> > is explicitly designed to handle cases like this and aggregate the
> > needs correctly. You might want to look into that more closely.
>
> The clk framework aggregates the min_rate requests from multiple
> consumers under a big "prepare_lock" so I expect it will deal with
> concurrent requests correctly. As for performance: frequency switching
> shouldn't be a fast path.
Sorry I wasn't clear. I was not talking about locking issues or race
conditions when I said concurrency use cases. What I meant was if GPU
wants 5 GB/s and at the same time (concurrent) camera wants 5 GB/s
you'll need to configure the interconnect for 10 GB/s. If both of them
just try to set the min freq equivalent for 5 GB/s the performance
would be bad or functionality might break.
> >> The clk_set_min_rate approach does not mesh very well with the OPP framework.
> >> Some of interconnect nodes on imx8m can run at different voltages: OPP can
> >> handle this well but not in response to a clk_set_min_rate from an unrelated
> >> subsystem. Maybe set voltage on a clk notifier?
> >
> > I think if you design it something like below, it might make your life
> > a whole lot easier.
> > Hopefully the formatting looks okay on your end. The arrow going up is
> > just connecting devfreq to ICC.
> >
> > Proactive -> ICC -> clk/OPP API to set freq/volt
> > ^
> > |
> > HW measure -> governor -> devfreq ----+
> >
> > That way, all voltage/frequency requirements are managed cleanly by
> > clk/OPP frameworks. The ICC deals with aggregating all the
> > requirements and devfreq lets you handle reactive scaling and policy.
>
> If icc and devfreq are to directly communicate it makes more sense to do
> it backwards: ICC should set a min_freq on nodes which have a devfreq
> instance attached and devfreq should implement actual freq switching.
>
> HW measurement is done on individual nodes while ICC deals with requests
> along a path. In particular on imx we have a performance monitor
> attached to the ddr controller and I doubt it can distinguish between
> masters so how could this be mapped usefully to an interconnect request?
Ah, that was the missing piece for me -- you are trying to use a
central performance monitor. I see what you are trying to do.
So you are looking at system wide traffic at DDR and then using that
to scale the interconnect/DDRC. I don't know how complicated or not
the IMX interconnect topology is, so please pardon my questions. If
you are using a performance monitor at the DDR controller, why do you
need the "proactive" requests from other clients? Wouldn't the
performance monitor account for all the traffic to DDR?
> As far as I understand with devfreq the ddrc node could use "ondemand"
> while the other nodes would default to the "passive" governor and run at
> predefined default ratios relative to DDRC.
Yes, that approach would also work but I'm not sure why you need the
ICC framework in that case.
> > If all of this makes sense, please take a look at [2] and provide your
> > thoughts. I've dropped a few patches from [1] to avoid confusion (too
> > much going on at once). I think BW OPP tables and having OPP tables
> > for interconnect paths will be something you'll need (if not now,
> > eventually) and something you can build on top of nicely.
>
> I found it very confusing that you're assigning BW OPP tables to
> devices. My initial understanding was that BW OPP would map "bandwidth"
> to "frequency" so BW OPPs should be assigned to links along the
> interconnect graph. But maybe what you want is to have OPPs for the BW
> values requested by devices?
I want to have OPPs for bandwidths requested for paths. Each
interconnect node can also use BW OPPs if that makes sense for them,
but I think they'd be better served by using frequency OPPs.
> Looking at the sdm845 icc provider source and it seems that those
> "peak/avg" values are actually parameters which go into a firmware
> command!? It makes sense that you want interconnect to be "below"
> devfreq since icc_provider.set maps very closely to what firmware exposes.
Even without the firmware (it's mainly there to aggregate requests for
some system wide resources) or if interconnects are scaled directly
using clock APIs (older chips), sdm845 would still want ICC to be
below devfreq. It's because 845 doesn't try to do ICC scaling by
measuring at the DDR. Each master makes separate requests and then the
ICC aggregates and sets the frequency. They have their reasons (good
ones) for doing that.
> > Interconnects and interconnect paths quantify their performance
> levels > in terms of bandwidth and not in terms of frequency.
>
> On i.MX we just have a bunch of interconnect IPs for which frequencies
> can be adjusted (in hz)
This is true for every chip. In the end, they all set the Hz of hardware IPs.
> so the above statement doesn't really hold. It
> is up to an icc provider to convert aggregate bandwidth values to
> frequencies along the path.
That's the job of every ICC provider. In the case of i.MX you use
clock APIs and in the case of sdm845 they use some complicated
firmware interface. But in both cases, when the system is tuned for
performance/power everyone in the end cares about frequency and
voltage. If the frequency goes too high, they might reduce the
bandwidth to make sure the frequency remains reasonable for whatever
use case they are profiling.
I think the main difference is where the performance monitoring or
performance decision is done. If you don't have a central performance
monitor or don't want to use one, then the policy decision (which is
where devfreq fits in) will have to happen at the bandwidth decision
level.
-Saravana
^ permalink raw reply
* Re: Thermal microconference proposal @ LPC
From: Amit Kucheria @ 2019-07-04 2:50 UTC (permalink / raw)
To: Pandruvada, Srinivas
Cc: linux-pm@vger.kernel.org, Zhang, Rui, edubezval@gmail.com,
daniel.lezcano@linaro.org
In-Reply-To: <8d4b31d46c5a3ee1f79eb65cbe03bc2065ce8696.camel@intel.com>
Hi Srinivas,
On Thu, Jul 4, 2019 at 2:15 AM Pandruvada, Srinivas
<srinivas.pandruvada@intel.com> wrote:
>
> Hi Amit,
> On Mon, 2019-06-24 at 19:22 +0530, Amit Kucheria wrote:
> > Hi all,
> >
> > We've proposed a thermal microconf at LPC again this year. I've
> > bcc'ed
> > a few people who were interested last year. We already have the
> > following topics listed for the proposal but could certainly use more
> > topics especially from the non-mobile Linux community.
> >
>
>
> Thermal/Power mitigation on high powered laptops
>
> Some of the high powered laptops released with KabyLake and later
> generations of processors can reach more than 50W with some busy
> workloads like kernel compilation. This results in CPU temperature to
> quickly reach close to the critical temperature. To avoid this OEM’s
> have used a very conservative power limits by default. But this
> results in a bad performance on Linux laptops compared to other
> operating systems. We have implemented a solution for Linux to mitigate
> this. This quick presentation will show the approach Linux users
> particularly kernel developers can use to get a very high performance
> from these laptops.
>
> This should be a quick 5-10 min talk.
Thanks for the submission. Is this supposed to be a discussion or only
a presentation?
In any case, Rafael and Eduardo are now triaging the list of talks as MC leads.
Regards,
Amit
^ permalink raw reply
* [pm:linux-next 91/102] include/linux/pm_qos.h:197:9: error: expected '(' before 'type'
From: kbuild test robot @ 2019-07-04 1:04 UTC (permalink / raw)
To: Viresh Kumar
Cc: kbuild-all, linux-acpi, devel, linux-pm, Rafael J. Wysocki,
Matthias Kaehlcke, Ulf Hansson
[-- Attachment #1: Type: text/plain, Size: 2844 bytes --]
tree: https://kernel.googlesource.com/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
head: 267eacd79cadd5e2d6307320497d66119907c411
commit: 57fa6137402b98327d1247c5aaf3d4f0595a73fe [91/102] PM / QOS: Pass request type to dev_pm_qos_read_value()
config: riscv-allnoconfig (attached as .config)
compiler: riscv64-linux-gcc (GCC) 7.4.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
git checkout 57fa6137402b98327d1247c5aaf3d4f0595a73fe
# save the attached .config to linux build tree
GCC_VERSION=7.4.0 make.cross ARCH=riscv
If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>
All error/warnings (new ones prefixed by >>):
In file included from kernel//power/qos.c:33:0:
include/linux/pm_qos.h: In function 'dev_pm_qos_read_value':
>> include/linux/pm_qos.h:197:9: error: expected '(' before 'type'
switch type {
^~~~
include/linux/pm_qos.h:197:9: warning: statement with no effect [-Wunused-value]
>> include/linux/pm_qos.h:204:1: warning: no return statement in function returning non-void [-Wreturn-type]
}
^
include/linux/pm_qos.h: At top level:
include/linux/pm_qos.h:219:4: error: expected identifier or '(' before '{' token
{ return 0; }
^
include/linux/pm_qos.h:216:19: warning: 'dev_pm_qos_add_notifier' declared 'static' but never defined [-Wunused-function]
static inline int dev_pm_qos_add_notifier(struct device *dev,
^~~~~~~~~~~~~~~~~~~~~~~
vim +197 include/linux/pm_qos.h
178
179 static inline s32 dev_pm_qos_raw_resume_latency(struct device *dev)
180 {
181 return IS_ERR_OR_NULL(dev->power.qos) ?
182 PM_QOS_RESUME_LATENCY_NO_CONSTRAINT :
183 pm_qos_read_value(&dev->power.qos->resume_latency);
184 }
185 #else
186 static inline enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev,
187 s32 mask)
188 { return PM_QOS_FLAGS_UNDEFINED; }
189 static inline enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev,
190 s32 mask)
191 { return PM_QOS_FLAGS_UNDEFINED; }
192 static inline s32 __dev_pm_qos_resume_latency(struct device *dev)
193 { return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; }
194 static inline s32 dev_pm_qos_read_value(struct device *dev,
195 enum dev_pm_qos_req_type type)
196 {
> 197 switch type {
198 case DEV_PM_QOS_RESUME_LATENCY:
199 return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
200 default:
201 WARN_ON(1);
202 return 0;
203 }
> 204 }
205
---
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: 5119 bytes --]
^ permalink raw reply
* [pm:linux-next 89/102] include/linux/pm_qos.h:209:4: error: expected identifier or '(' before '{' token
From: kbuild test robot @ 2019-07-04 0:52 UTC (permalink / raw)
To: Viresh Kumar
Cc: kbuild-all, linux-acpi, devel, linux-pm, Rafael J. Wysocki,
Matthias Kaehlcke, Ulf Hansson
[-- Attachment #1: Type: text/plain, Size: 7138 bytes --]
tree: https://kernel.googlesource.com/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
head: 267eacd79cadd5e2d6307320497d66119907c411
commit: 024a47a2732d5f9cd9c039c52074912c6982d786 [89/102] PM / QOS: Pass request type to dev_pm_qos_{add|remove}_notifier()
config: riscv-allnoconfig (attached as .config)
compiler: riscv64-linux-gcc (GCC) 7.4.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
git checkout 024a47a2732d5f9cd9c039c52074912c6982d786
# save the attached .config to linux build tree
GCC_VERSION=7.4.0 make.cross ARCH=riscv
If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>
All errors (new ones prefixed by >>):
In file included from kernel//power/qos.c:33:0:
>> include/linux/pm_qos.h:209:4: error: expected identifier or '(' before '{' token
{ return 0; }
^
include/linux/pm_qos.h:206:19: warning: 'dev_pm_qos_add_notifier' declared 'static' but never defined [-Wunused-function]
static inline int dev_pm_qos_add_notifier(struct device *dev,
^~~~~~~~~~~~~~~~~~~~~~~
vim +209 include/linux/pm_qos.h
6dbf5cea Rafael J. Wysocki 2017-02-24 178
6dbf5cea Rafael J. Wysocki 2017-02-24 179 static inline s32 dev_pm_qos_raw_read_value(struct device *dev)
6dbf5cea Rafael J. Wysocki 2017-02-24 180 {
6dbf5cea Rafael J. Wysocki 2017-02-24 181 return IS_ERR_OR_NULL(dev->power.qos) ?
0759e80b Rafael J. Wysocki 2017-11-07 182 PM_QOS_RESUME_LATENCY_NO_CONSTRAINT :
0759e80b Rafael J. Wysocki 2017-11-07 183 pm_qos_read_value(&dev->power.qos->resume_latency);
6dbf5cea Rafael J. Wysocki 2017-02-24 184 }
e8db0be1 Jean Pihet 2011-08-25 185 #else
ae0fb4b7 Rafael J. Wysocki 2012-10-23 186 static inline enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev,
ae0fb4b7 Rafael J. Wysocki 2012-10-23 187 s32 mask)
ae0fb4b7 Rafael J. Wysocki 2012-10-23 188 { return PM_QOS_FLAGS_UNDEFINED; }
ae0fb4b7 Rafael J. Wysocki 2012-10-23 189 static inline enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev,
ae0fb4b7 Rafael J. Wysocki 2012-10-23 190 s32 mask)
ae0fb4b7 Rafael J. Wysocki 2012-10-23 191 { return PM_QOS_FLAGS_UNDEFINED; }
00dc9ad1 Rafael J. Wysocki 2011-12-01 192 static inline s32 __dev_pm_qos_read_value(struct device *dev)
0759e80b Rafael J. Wysocki 2017-11-07 193 { return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; }
1a9a9152 Rafael J. Wysocki 2011-09-29 194 static inline s32 dev_pm_qos_read_value(struct device *dev)
0759e80b Rafael J. Wysocki 2017-11-07 195 { return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; }
91ff4cb8 Jean Pihet 2011-08-25 196 static inline int dev_pm_qos_add_request(struct device *dev,
91ff4cb8 Jean Pihet 2011-08-25 197 struct dev_pm_qos_request *req,
ae0fb4b7 Rafael J. Wysocki 2012-10-23 198 enum dev_pm_qos_req_type type,
91ff4cb8 Jean Pihet 2011-08-25 199 s32 value)
91ff4cb8 Jean Pihet 2011-08-25 200 { return 0; }
91ff4cb8 Jean Pihet 2011-08-25 201 static inline int dev_pm_qos_update_request(struct dev_pm_qos_request *req,
91ff4cb8 Jean Pihet 2011-08-25 202 s32 new_value)
91ff4cb8 Jean Pihet 2011-08-25 203 { return 0; }
91ff4cb8 Jean Pihet 2011-08-25 204 static inline int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
91ff4cb8 Jean Pihet 2011-08-25 205 { return 0; }
91ff4cb8 Jean Pihet 2011-08-25 206 static inline int dev_pm_qos_add_notifier(struct device *dev,
024a47a2 Viresh Kumar 2019-07-01 207 struct notifier_block *notifier,
024a47a2 Viresh Kumar 2019-07-01 208 enum dev_pm_qos_req_type type);
91ff4cb8 Jean Pihet 2011-08-25 @209 { return 0; }
91ff4cb8 Jean Pihet 2011-08-25 210 static inline int dev_pm_qos_remove_notifier(struct device *dev,
024a47a2 Viresh Kumar 2019-07-01 211 struct notifier_block *notifier,
024a47a2 Viresh Kumar 2019-07-01 212 enum dev_pm_qos_req_type type)
91ff4cb8 Jean Pihet 2011-08-25 213 { return 0; }
91ff4cb8 Jean Pihet 2011-08-25 214 static inline void dev_pm_qos_constraints_init(struct device *dev)
1a9a9152 Rafael J. Wysocki 2011-09-29 215 {
1a9a9152 Rafael J. Wysocki 2011-09-29 216 dev->power.power_state = PMSG_ON;
1a9a9152 Rafael J. Wysocki 2011-09-29 217 }
91ff4cb8 Jean Pihet 2011-08-25 218 static inline void dev_pm_qos_constraints_destroy(struct device *dev)
1a9a9152 Rafael J. Wysocki 2011-09-29 219 {
1a9a9152 Rafael J. Wysocki 2011-09-29 220 dev->power.power_state = PMSG_INVALID;
1a9a9152 Rafael J. Wysocki 2011-09-29 221 }
40a5f8be Rafael J. Wysocki 2011-12-23 222 static inline int dev_pm_qos_add_ancestor_request(struct device *dev,
71d821fd Rafael J. Wysocki 2014-02-11 223 struct dev_pm_qos_request *req,
71d821fd Rafael J. Wysocki 2014-02-11 224 enum dev_pm_qos_req_type type,
71d821fd Rafael J. Wysocki 2014-02-11 225 s32 value)
40a5f8be Rafael J. Wysocki 2011-12-23 226 { return 0; }
85dc0b8a Rafael J. Wysocki 2012-03-13 227 static inline int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value)
85dc0b8a Rafael J. Wysocki 2012-03-13 228 { return 0; }
85dc0b8a Rafael J. Wysocki 2012-03-13 229 static inline void dev_pm_qos_hide_latency_limit(struct device *dev) {}
e39473d0 Rafael J. Wysocki 2012-10-24 230 static inline int dev_pm_qos_expose_flags(struct device *dev, s32 value)
e39473d0 Rafael J. Wysocki 2012-10-24 231 { return 0; }
e39473d0 Rafael J. Wysocki 2012-10-24 232 static inline void dev_pm_qos_hide_flags(struct device *dev) {}
e39473d0 Rafael J. Wysocki 2012-10-24 233 static inline int dev_pm_qos_update_flags(struct device *dev, s32 m, bool set)
e39473d0 Rafael J. Wysocki 2012-10-24 234 { return 0; }
2d984ad1 Rafael J. Wysocki 2014-02-11 235 static inline s32 dev_pm_qos_get_user_latency_tolerance(struct device *dev)
2d984ad1 Rafael J. Wysocki 2014-02-11 236 { return PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT; }
2d984ad1 Rafael J. Wysocki 2014-02-11 237 static inline int dev_pm_qos_update_user_latency_tolerance(struct device *dev, s32 val)
2d984ad1 Rafael J. Wysocki 2014-02-11 238 { return 0; }
13b2c4a0 Mika Westerberg 2015-07-27 239 static inline int dev_pm_qos_expose_latency_tolerance(struct device *dev)
13b2c4a0 Mika Westerberg 2015-07-27 240 { return 0; }
13b2c4a0 Mika Westerberg 2015-07-27 241 static inline void dev_pm_qos_hide_latency_tolerance(struct device *dev) {}
e39473d0 Rafael J. Wysocki 2012-10-24 242
:::::: The code at line 209 was first introduced by commit
:::::: 91ff4cb803df6de9114351b9f2f0f39f397ee03e PM QoS: Implement per-device PM QoS constraints
:::::: TO: Jean Pihet <j-pihet@ti.com>
:::::: CC: Rafael J. Wysocki <rjw@sisk.pl>
---
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: 5119 bytes --]
^ permalink raw reply
* [patch 3/5] governors: unify last_state_idx
From: Marcelo Tosatti @ 2019-07-03 23:51 UTC (permalink / raw)
To: kvm-devel
Cc: Paolo Bonzini, Radim Krcmar, Andrea Arcangeli, Rafael J. Wysocki,
Peter Zijlstra, Wanpeng Li, Konrad Rzeszutek Wilk,
Raslan KarimAllah, Boris Ostrovsky, Ankur Arora,
Christian Borntraeger, linux-pm, Marcelo Tosatti
In-Reply-To: <20190703235124.783034907@amt.cnet>
[-- Attachment #1: 02-unify-last-used-idx --]
[-- Type: text/plain, Size: 6798 bytes --]
Since this field is shared by all governors, move it to
cpuidle device structure.
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Index: linux-2.6-newcpuidle.git/drivers/cpuidle/governors/ladder.c
===================================================================
--- linux-2.6-newcpuidle.git.orig/drivers/cpuidle/governors/ladder.c
+++ linux-2.6-newcpuidle.git/drivers/cpuidle/governors/ladder.c
@@ -38,7 +38,6 @@ struct ladder_device_state {
struct ladder_device {
struct ladder_device_state states[CPUIDLE_STATE_MAX];
- int last_state_idx;
};
static DEFINE_PER_CPU(struct ladder_device, ladder_devices);
@@ -49,12 +48,13 @@ static DEFINE_PER_CPU(struct ladder_devi
* @old_idx: the current state index
* @new_idx: the new target state index
*/
-static inline void ladder_do_selection(struct ladder_device *ldev,
+static inline void ladder_do_selection(struct cpuidle_device *dev,
+ struct ladder_device *ldev,
int old_idx, int new_idx)
{
ldev->states[old_idx].stats.promotion_count = 0;
ldev->states[old_idx].stats.demotion_count = 0;
- ldev->last_state_idx = new_idx;
+ dev->last_state_idx = new_idx;
}
/**
@@ -68,13 +68,13 @@ static int ladder_select_state(struct cp
{
struct ladder_device *ldev = this_cpu_ptr(&ladder_devices);
struct ladder_device_state *last_state;
- int last_residency, last_idx = ldev->last_state_idx;
+ int last_residency, last_idx = dev->last_state_idx;
int first_idx = drv->states[0].flags & CPUIDLE_FLAG_POLLING ? 1 : 0;
int latency_req = cpuidle_governor_latency_req(dev->cpu);
/* Special case when user has set very strict latency requirement */
if (unlikely(latency_req == 0)) {
- ladder_do_selection(ldev, last_idx, 0);
+ ladder_do_selection(dev, ldev, last_idx, 0);
return 0;
}
@@ -91,7 +91,7 @@ static int ladder_select_state(struct cp
last_state->stats.promotion_count++;
last_state->stats.demotion_count = 0;
if (last_state->stats.promotion_count >= last_state->threshold.promotion_count) {
- ladder_do_selection(ldev, last_idx, last_idx + 1);
+ ladder_do_selection(dev, ldev, last_idx, last_idx + 1);
return last_idx + 1;
}
}
@@ -107,7 +107,7 @@ static int ladder_select_state(struct cp
if (drv->states[i].exit_latency <= latency_req)
break;
}
- ladder_do_selection(ldev, last_idx, i);
+ ladder_do_selection(dev, ldev, last_idx, i);
return i;
}
@@ -116,7 +116,7 @@ static int ladder_select_state(struct cp
last_state->stats.demotion_count++;
last_state->stats.promotion_count = 0;
if (last_state->stats.demotion_count >= last_state->threshold.demotion_count) {
- ladder_do_selection(ldev, last_idx, last_idx - 1);
+ ladder_do_selection(dev, ldev, last_idx, last_idx - 1);
return last_idx - 1;
}
}
@@ -139,7 +139,7 @@ static int ladder_enable_device(struct c
struct ladder_device_state *lstate;
struct cpuidle_state *state;
- ldev->last_state_idx = first_idx;
+ dev->last_state_idx = first_idx;
for (i = first_idx; i < drv->state_count; i++) {
state = &drv->states[i];
@@ -167,9 +167,8 @@ static int ladder_enable_device(struct c
*/
static void ladder_reflect(struct cpuidle_device *dev, int index)
{
- struct ladder_device *ldev = this_cpu_ptr(&ladder_devices);
if (index > 0)
- ldev->last_state_idx = index;
+ dev->last_state_idx = index;
}
static struct cpuidle_governor ladder_governor = {
Index: linux-2.6-newcpuidle.git/drivers/cpuidle/governors/menu.c
===================================================================
--- linux-2.6-newcpuidle.git.orig/drivers/cpuidle/governors/menu.c
+++ linux-2.6-newcpuidle.git/drivers/cpuidle/governors/menu.c
@@ -117,7 +117,6 @@
*/
struct menu_device {
- int last_state_idx;
int needs_update;
int tick_wakeup;
@@ -455,7 +454,7 @@ static void menu_reflect(struct cpuidle_
{
struct menu_device *data = this_cpu_ptr(&menu_devices);
- data->last_state_idx = index;
+ dev->last_state_idx = index;
data->needs_update = 1;
data->tick_wakeup = tick_nohz_idle_got_tick();
}
@@ -468,7 +467,7 @@ static void menu_reflect(struct cpuidle_
static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
{
struct menu_device *data = this_cpu_ptr(&menu_devices);
- int last_idx = data->last_state_idx;
+ int last_idx = dev->last_state_idx;
struct cpuidle_state *target = &drv->states[last_idx];
unsigned int measured_us;
unsigned int new_factor;
Index: linux-2.6-newcpuidle.git/drivers/cpuidle/governors/teo.c
===================================================================
--- linux-2.6-newcpuidle.git.orig/drivers/cpuidle/governors/teo.c
+++ linux-2.6-newcpuidle.git/drivers/cpuidle/governors/teo.c
@@ -96,7 +96,6 @@ struct teo_idle_state {
* @time_span_ns: Time between idle state selection and post-wakeup update.
* @sleep_length_ns: Time till the closest timer event (at the selection time).
* @states: Idle states data corresponding to this CPU.
- * @last_state: Idle state entered by the CPU last time.
* @interval_idx: Index of the most recent saved idle interval.
* @intervals: Saved idle duration values.
*/
@@ -104,7 +103,6 @@ struct teo_cpu {
u64 time_span_ns;
u64 sleep_length_ns;
struct teo_idle_state states[CPUIDLE_STATE_MAX];
- int last_state;
int interval_idx;
unsigned int intervals[INTERVALS];
};
@@ -130,7 +128,9 @@ static void teo_update(struct cpuidle_dr
*/
measured_us = sleep_length_us;
} else {
- unsigned int lat = drv->states[cpu_data->last_state].exit_latency;
+ unsigned int lat;
+
+ lat = drv->states[dev->last_state_idx].exit_latency;
measured_us = ktime_to_us(cpu_data->time_span_ns);
/*
@@ -245,9 +245,9 @@ static int teo_select(struct cpuidle_dri
int max_early_idx, idx, i;
ktime_t delta_tick;
- if (cpu_data->last_state >= 0) {
+ if (dev->last_state_idx >= 0) {
teo_update(drv, dev);
- cpu_data->last_state = -1;
+ dev->last_state_idx = -1;
}
cpu_data->time_span_ns = local_clock();
@@ -394,7 +394,7 @@ static void teo_reflect(struct cpuidle_d
{
struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu);
- cpu_data->last_state = state;
+ dev->last_state_idx = state;
/*
* If the wakeup was not "natural", but triggered by one of the safety
* nets, assume that the CPU might have been idle for the entire sleep
Index: linux-2.6-newcpuidle.git/include/linux/cpuidle.h
===================================================================
--- linux-2.6-newcpuidle.git.orig/include/linux/cpuidle.h
+++ linux-2.6-newcpuidle.git/include/linux/cpuidle.h
@@ -85,6 +85,7 @@ struct cpuidle_device {
unsigned int cpu;
ktime_t next_hrtimer;
+ int last_state_idx;
int last_residency;
u64 poll_limit_ns;
struct cpuidle_state_usage states_usage[CPUIDLE_STATE_MAX];
^ permalink raw reply
* [patch 5/5] cpuidle-haltpoll: disable host side polling when kvm virtualized
From: Marcelo Tosatti @ 2019-07-03 23:51 UTC (permalink / raw)
To: kvm-devel
Cc: Paolo Bonzini, Radim Krcmar, Andrea Arcangeli, Rafael J. Wysocki,
Peter Zijlstra, Wanpeng Li, Konrad Rzeszutek Wilk,
Raslan KarimAllah, Boris Ostrovsky, Ankur Arora,
Christian Borntraeger, linux-pm, Marcelo Tosatti
In-Reply-To: <20190703235124.783034907@amt.cnet>
[-- Attachment #1: 04-pollcontrol-guest.patch --]
[-- Type: text/plain, Size: 4631 bytes --]
When performing guest side polling, it is not necessary to
also perform host side polling.
So disable host side polling, via the new MSR interface,
when loading cpuidle-haltpoll driver.
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
---
arch/x86/Kconfig | 7 +++++
arch/x86/include/asm/cpuidle_haltpoll.h | 8 ++++++
arch/x86/kernel/kvm.c | 42 ++++++++++++++++++++++++++++++++
drivers/cpuidle/cpuidle-haltpoll.c | 10 ++++++-
include/linux/cpuidle_haltpoll.h | 16 ++++++++++++
5 files changed, 82 insertions(+), 1 deletion(-)
Index: kvm/arch/x86/include/asm/cpuidle_haltpoll.h
===================================================================
--- /dev/null
+++ kvm/arch/x86/include/asm/cpuidle_haltpoll.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ARCH_HALTPOLL_H
+#define _ARCH_HALTPOLL_H
+
+void arch_haltpoll_enable(void);
+void arch_haltpoll_disable(void);
+
+#endif
Index: kvm/drivers/cpuidle/cpuidle-haltpoll.c
===================================================================
--- kvm.orig/drivers/cpuidle/cpuidle-haltpoll.c
+++ kvm/drivers/cpuidle/cpuidle-haltpoll.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/sched/idle.h>
#include <linux/kvm_para.h>
+#include <linux/cpuidle_haltpoll.h>
static int default_enter_idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
@@ -47,6 +48,7 @@ static struct cpuidle_driver haltpoll_dr
static int __init haltpoll_init(void)
{
+ int ret;
struct cpuidle_driver *drv = &haltpoll_driver;
cpuidle_poll_state_init(drv);
@@ -54,11 +56,16 @@ static int __init haltpoll_init(void)
if (!kvm_para_available())
return 0;
- return cpuidle_register(&haltpoll_driver, NULL);
+ ret = cpuidle_register(&haltpoll_driver, NULL);
+ if (ret == 0)
+ arch_haltpoll_enable();
+
+ return ret;
}
static void __exit haltpoll_exit(void)
{
+ arch_haltpoll_disable();
cpuidle_unregister(&haltpoll_driver);
}
Index: kvm/include/linux/cpuidle_haltpoll.h
===================================================================
--- /dev/null
+++ kvm/include/linux/cpuidle_haltpoll.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _CPUIDLE_HALTPOLL_H
+#define _CPUIDLE_HALTPOLL_H
+
+#ifdef CONFIG_ARCH_CPUIDLE_HALTPOLL
+#include <asm/cpuidle_haltpoll.h>
+#else
+static inline void arch_haltpoll_enable(void)
+{
+}
+
+static inline void arch_haltpoll_disable(void)
+{
+}
+#endif
+#endif
Index: kvm/arch/x86/Kconfig
===================================================================
--- kvm.orig/arch/x86/Kconfig
+++ kvm/arch/x86/Kconfig
@@ -787,6 +787,7 @@ config KVM_GUEST
bool "KVM Guest support (including kvmclock)"
depends on PARAVIRT
select PARAVIRT_CLOCK
+ select ARCH_CPUIDLE_HALTPOLL
default y
---help---
This option enables various optimizations for running under the KVM
@@ -795,6 +796,12 @@ config KVM_GUEST
underlying device model, the host provides the guest with
timing infrastructure such as time of day, and system time
+config ARCH_CPUIDLE_HALTPOLL
+ def_bool n
+ prompt "Disable host haltpoll when loading haltpoll driver"
+ help
+ If virtualized under KVM, disable host haltpoll.
+
config PVH
bool "Support for running PVH guests"
---help---
Index: kvm/arch/x86/kernel/kvm.c
===================================================================
--- kvm.orig/arch/x86/kernel/kvm.c
+++ kvm/arch/x86/kernel/kvm.c
@@ -874,3 +874,45 @@ void __init kvm_spinlock_init(void)
}
#endif /* CONFIG_PARAVIRT_SPINLOCKS */
+
+#ifdef CONFIG_ARCH_CPUIDLE_HALTPOLL
+
+static void kvm_disable_host_haltpoll(void *i)
+{
+ wrmsrl(MSR_KVM_POLL_CONTROL, 0);
+}
+
+static void kvm_enable_host_haltpoll(void *i)
+{
+ wrmsrl(MSR_KVM_POLL_CONTROL, 1);
+}
+
+void arch_haltpoll_enable(void)
+{
+ if (!kvm_para_has_feature(KVM_FEATURE_POLL_CONTROL)) {
+ printk(KERN_ERR "kvm: host does not support poll control\n");
+ printk(KERN_ERR "kvm: host upgrade recommended\n");
+ return;
+ }
+
+ preempt_disable();
+ /* Enable guest halt poll disables host halt poll */
+ kvm_disable_host_haltpoll(NULL);
+ smp_call_function(kvm_disable_host_haltpoll, NULL, 1);
+ preempt_enable();
+}
+EXPORT_SYMBOL_GPL(arch_haltpoll_enable);
+
+void arch_haltpoll_disable(void)
+{
+ if (!kvm_para_has_feature(KVM_FEATURE_POLL_CONTROL))
+ return;
+
+ preempt_disable();
+ /* Enable guest halt poll disables host halt poll */
+ kvm_enable_host_haltpoll(NULL);
+ smp_call_function(kvm_enable_host_haltpoll, NULL, 1);
+ preempt_enable();
+}
+EXPORT_SYMBOL_GPL(arch_haltpoll_disable);
+#endif
^ permalink raw reply
* [patch 4/5] cpuidle: add haltpoll governor
From: Marcelo Tosatti @ 2019-07-03 23:51 UTC (permalink / raw)
To: kvm-devel
Cc: Paolo Bonzini, Radim Krcmar, Andrea Arcangeli, Rafael J. Wysocki,
Peter Zijlstra, Wanpeng Li, Konrad Rzeszutek Wilk,
Raslan KarimAllah, Boris Ostrovsky, Ankur Arora,
Christian Borntraeger, linux-pm, Marcelo Tosatti
In-Reply-To: <20190703235124.783034907@amt.cnet>
[-- Attachment #1: 03-cpuidle-haltpoll-governor --]
[-- Type: text/plain, Size: 9929 bytes --]
The cpuidle_haltpoll governor, in conjunction with the haltpoll cpuidle
driver, allows guest vcpus to poll for a specified amount of time before
halting.
This provides the following benefits to host side polling:
1) The POLL flag is set while polling is performed, which allows
a remote vCPU to avoid sending an IPI (and the associated
cost of handling the IPI) when performing a wakeup.
2) The VM-exit cost can be avoided.
The downside of guest side polling is that polling is performed
even with other runnable tasks in the host.
Results comparing halt_poll_ns and server/client application
where a small packet is ping-ponged:
host --> 31.33
halt_poll_ns=300000 / no guest busy spin --> 33.40 (93.8%)
halt_poll_ns=0 / guest_halt_poll_ns=300000 --> 32.73 (95.7%)
For the SAP HANA benchmarks (where idle_spin is a parameter
of the previous version of the patch, results should be the
same):
hpns == halt_poll_ns
idle_spin=0/ idle_spin=800/ idle_spin=0/
hpns=200000 hpns=0 hpns=800000
DeleteC06T03 (100 thread) 1.76 1.71 (-3%) 1.78 (+1%)
InsertC16T02 (100 thread) 2.14 2.07 (-3%) 2.18 (+1.8%)
DeleteC00T01 (1 thread) 1.34 1.28 (-4.5%) 1.29 (-3.7%)
UpdateC00T03 (1 thread) 4.72 4.18 (-12%) 4.53 (-5%)
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
---
Documentation/virtual/guest-halt-polling.txt | 79 ++++++++++++
drivers/cpuidle/Kconfig | 11 +
drivers/cpuidle/governors/Makefile | 1
drivers/cpuidle/governors/haltpoll.c | 175 +++++++++++++++++++++++++++
4 files changed, 266 insertions(+)
Index: linux-2.6-newcpuidle.git/drivers/cpuidle/Kconfig
===================================================================
--- linux-2.6-newcpuidle.git.orig/drivers/cpuidle/Kconfig
+++ linux-2.6-newcpuidle.git/drivers/cpuidle/Kconfig
@@ -33,6 +33,17 @@ config CPU_IDLE_GOV_TEO
Some workloads benefit from using it and it generally should be safe
to use. Say Y here if you are not happy with the alternatives.
+config CPU_IDLE_GOV_HALTPOLL
+ bool "Haltpoll governor (for virtualized systems)"
+ depends on KVM_GUEST
+ help
+ This governor implements haltpoll idle state selection, to be
+ used in conjunction with the haltpoll cpuidle driver, allowing
+ for polling for a certain amount of time before entering idle
+ state.
+
+ Some virtualized workloads benefit from using it.
+
config DT_IDLE_STATES
bool
Index: linux-2.6-newcpuidle.git/drivers/cpuidle/governors/Makefile
===================================================================
--- linux-2.6-newcpuidle.git.orig/drivers/cpuidle/governors/Makefile
+++ linux-2.6-newcpuidle.git/drivers/cpuidle/governors/Makefile
@@ -6,3 +6,4 @@
obj-$(CONFIG_CPU_IDLE_GOV_LADDER) += ladder.o
obj-$(CONFIG_CPU_IDLE_GOV_MENU) += menu.o
obj-$(CONFIG_CPU_IDLE_GOV_TEO) += teo.o
+obj-$(CONFIG_CPU_IDLE_GOV_HALTPOLL) += haltpoll.o
Index: linux-2.6-newcpuidle.git/drivers/cpuidle/governors/haltpoll.c
===================================================================
--- /dev/null
+++ linux-2.6-newcpuidle.git/drivers/cpuidle/governors/haltpoll.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * haltpoll.c - haltpoll idle governor
+ *
+ * Copyright 2019 Red Hat, Inc. and/or its affiliates.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Authors: Marcelo Tosatti <mtosatti@redhat.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/cpuidle.h>
+#include <linux/time.h>
+#include <linux/ktime.h>
+#include <linux/hrtimer.h>
+#include <linux/tick.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/kvm_para.h>
+
+static unsigned int guest_halt_poll_ns __read_mostly = 200000;
+module_param(guest_halt_poll_ns, uint, 0644);
+
+/* division factor to shrink halt_poll_ns */
+static unsigned int guest_halt_poll_shrink __read_mostly = 2;
+module_param(guest_halt_poll_shrink, uint, 0644);
+
+/* multiplication factor to grow per-cpu poll_limit_ns */
+static unsigned int guest_halt_poll_grow __read_mostly = 2;
+module_param(guest_halt_poll_grow, uint, 0644);
+
+/* value in us to start growing per-cpu halt_poll_ns */
+static unsigned int guest_halt_poll_grow_start __read_mostly = 50000;
+module_param(guest_halt_poll_grow_start, uint, 0644);
+
+/* allow shrinking guest halt poll */
+static bool guest_halt_poll_allow_shrink __read_mostly = true;
+module_param(guest_halt_poll_allow_shrink, bool, 0644);
+
+/**
+ * haltpoll_select - selects the next idle state to enter
+ * @drv: cpuidle driver containing state data
+ * @dev: the CPU
+ * @stop_tick: indication on whether or not to stop the tick
+ */
+static int haltpoll_select(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev,
+ bool *stop_tick)
+{
+ int latency_req = cpuidle_governor_latency_req(dev->cpu);
+
+ if (!drv->state_count || latency_req == 0) {
+ *stop_tick = false;
+ return 0;
+ }
+
+ if (dev->poll_limit_ns == 0)
+ return 1;
+
+ /* Last state was poll? */
+ if (dev->last_state_idx == 0) {
+ /* Halt if no event occurred on poll window */
+ if (dev->poll_time_limit == true)
+ return 1;
+
+ *stop_tick = false;
+ /* Otherwise, poll again */
+ return 0;
+ }
+
+ *stop_tick = false;
+ /* Last state was halt: poll */
+ return 0;
+}
+
+static void adjust_poll_limit(struct cpuidle_device *dev, unsigned int block_us)
+{
+ unsigned int val;
+ u64 block_ns = block_us*NSEC_PER_USEC;
+
+ /* Grow cpu_halt_poll_us if
+ * cpu_halt_poll_us < block_ns < guest_halt_poll_us
+ */
+ if (block_ns > dev->poll_limit_ns && block_ns <= guest_halt_poll_ns) {
+ val = dev->poll_limit_ns * guest_halt_poll_grow;
+
+ if (val < guest_halt_poll_grow_start)
+ val = guest_halt_poll_grow_start;
+ if (val > guest_halt_poll_ns)
+ val = guest_halt_poll_ns;
+
+ dev->poll_limit_ns = val;
+ } else if (block_ns > guest_halt_poll_ns &&
+ guest_halt_poll_allow_shrink) {
+ unsigned int shrink = guest_halt_poll_shrink;
+
+ val = dev->poll_limit_ns;
+ if (shrink == 0)
+ val = 0;
+ else
+ val /= shrink;
+ dev->poll_limit_ns = val;
+ }
+}
+
+/**
+ * haltpoll_reflect - update variables and update poll time
+ * @dev: the CPU
+ * @index: the index of actual entered state
+ */
+static void haltpoll_reflect(struct cpuidle_device *dev, int index)
+{
+ dev->last_state_idx = index;
+
+ if (index != 0)
+ adjust_poll_limit(dev, dev->last_residency);
+}
+
+/**
+ * haltpoll_enable_device - scans a CPU's states and does setup
+ * @drv: cpuidle driver
+ * @dev: the CPU
+ */
+static int haltpoll_enable_device(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev)
+{
+ dev->poll_limit_ns = 0;
+
+ return 0;
+}
+
+static struct cpuidle_governor haltpoll_governor = {
+ .name = "haltpoll",
+ .rating = 21,
+ .enable = haltpoll_enable_device,
+ .select = haltpoll_select,
+ .reflect = haltpoll_reflect,
+};
+
+static int __init init_haltpoll(void)
+{
+ if (kvm_para_available())
+ return cpuidle_register_governor(&haltpoll_governor);
+
+ return 0;
+}
+
+postcore_initcall(init_haltpoll);
Index: linux-2.6-newcpuidle.git/Documentation/virtual/guest-halt-polling.txt
===================================================================
--- /dev/null
+++ linux-2.6-newcpuidle.git/Documentation/virtual/guest-halt-polling.txt
@@ -0,0 +1,79 @@
+Guest halt polling
+==================
+
+The cpuidle_haltpoll driver, with the haltpoll governor, allows
+the guest vcpus to poll for a specified amount of time before
+halting.
+This provides the following benefits to host side polling:
+
+ 1) The POLL flag is set while polling is performed, which allows
+ a remote vCPU to avoid sending an IPI (and the associated
+ cost of handling the IPI) when performing a wakeup.
+
+ 2) The VM-exit cost can be avoided.
+
+The downside of guest side polling is that polling is performed
+even with other runnable tasks in the host.
+
+The basic logic as follows: A global value, guest_halt_poll_ns,
+is configured by the user, indicating the maximum amount of
+time polling is allowed. This value is fixed.
+
+Each vcpu has an adjustable guest_halt_poll_ns
+("per-cpu guest_halt_poll_ns"), which is adjusted by the algorithm
+in response to events (explained below).
+
+Module Parameters
+=================
+
+The haltpoll governor has 5 tunable module parameters:
+
+1) guest_halt_poll_ns:
+Maximum amount of time, in nanoseconds, that polling is
+performed before halting.
+
+Default: 200000
+
+2) guest_halt_poll_shrink:
+Division factor used to shrink per-cpu guest_halt_poll_ns when
+wakeup event occurs after the global guest_halt_poll_ns.
+
+Default: 2
+
+3) guest_halt_poll_grow:
+Multiplication factor used to grow per-cpu guest_halt_poll_ns
+when event occurs after per-cpu guest_halt_poll_ns
+but before global guest_halt_poll_ns.
+
+Default: 2
+
+4) guest_halt_poll_grow_start:
+The per-cpu guest_halt_poll_ns eventually reaches zero
+in case of an idle system. This value sets the initial
+per-cpu guest_halt_poll_ns when growing. This can
+be increased from 10000, to avoid misses during the initial
+growth stage:
+
+10k, 20k, 40k, ... (example assumes guest_halt_poll_grow=2).
+
+Default: 50000
+
+5) guest_halt_poll_allow_shrink:
+
+Bool parameter which allows shrinking. Set to N
+to avoid it (per-cpu guest_halt_poll_ns will remain
+high once achieves global guest_halt_poll_ns value).
+
+Default: Y
+
+The module parameters can be set from the debugfs files in:
+
+ /sys/module/haltpoll/parameters/
+
+Further Notes
+=============
+
+- Care should be taken when setting the guest_halt_poll_ns parameter as a
+large value has the potential to drive the cpu usage to 100% on a machine which
+would be almost entirely idle otherwise.
+
^ permalink raw reply
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