From 93d9e6f93e1586fcc97498c764be2e8c8401f4bd Mon Sep 17 00:00:00 2001 From: Lukasz Luba <lukasz.luba@arm.com> Date: Tue, 9 Nov 2021 19:57:12 +0000 Subject: cpufreq: qcom-cpufreq-hw: Update offline CPUs per-cpu thermal pressure The thermal pressure signal gives information to the scheduler about reduced CPU capacity due to thermal. It is based on a value stored in a per-cpu 'thermal_pressure' variable. The online CPUs will get the new value there, while the offline won't. Unfortunately, when the CPU is back online, the value read from per-cpu variable might be wrong (stale data). This might affect the scheduler decisions, since it sees the CPU capacity differently than what is actually available. Fix it by making sure that all online+offline CPUs would get the proper value in their per-cpu variable when there is throttling or throttling is removed. Fixes: 275157b367f479 ("cpufreq: qcom-cpufreq-hw: Add dcvs interrupt support") Reviewed-by: Thara Gopinath <thara.gopinath@linaro.org> Signed-off-by: Lukasz Luba <lukasz.luba@arm.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> --- drivers/cpufreq/qcom-cpufreq-hw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/cpufreq/qcom-cpufreq-hw.c') diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index a2be0df7e174..0138b2ec406d 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -304,7 +304,8 @@ static void qcom_lmh_dcvs_notify(struct qcom_cpufreq_data *data) if (capacity > max_capacity) capacity = max_capacity; - arch_set_thermal_pressure(policy->cpus, max_capacity - capacity); + arch_set_thermal_pressure(policy->related_cpus, + max_capacity - capacity); /* * In the unlikely case policy is unregistered do not enable -- cgit v1.2.3-70-g09d2 From 0258cb19c77deb755747b97f690931193ced0c55 Mon Sep 17 00:00:00 2001 From: Lukasz Luba <lukasz.luba@arm.com> Date: Tue, 9 Nov 2021 19:57:13 +0000 Subject: cpufreq: qcom-cpufreq-hw: Use new thermal pressure update function Thermal pressure provides a new API, which allows to use CPU frequency as an argument. That removes the need of local conversion to capacity. Use this new API and remove old local conversion code. The new arch_update_thermal_pressure() also accepts boost frequencies, which solves issue in the driver code with wrong reduced capacity calculation. The reduced capacity was calculated wrongly due to 'policy->cpuinfo.max_freq' used as a divider. The value present there was actually the boost frequency. Thus, even a normal maximum frequency value which corresponds to max CPU capacity (arch_scale_cpu_capacity(cpu_id)) is not able to remove the capping. The second side effect which is solved is that the reduced frequency wasn't properly translated into the right reduced capacity, e.g. boost frequency = 3000MHz (stored in policy->cpuinfo.max_freq) max normal frequency = 2500MHz (which is 1024 capacity) 2nd highest frequency = 2000MHz (which translates to 819 capacity) Then in a scenario when the 'throttled_freq' max allowed frequency was 2000MHz the driver translated it into 682 capacity: capacity = 1024 * 2000 / 3000 = 682 Then set the pressure value bigger than actually applied by the HW: max_capacity - capacity => 1024 - 682 = 342 (<- thermal pressure) Which was causing higher throttling and misleading task scheduler about available CPU capacity. A proper calculation in such case should be: capacity = 1024 * 2000 / 2500 = 819 1024 - 819 = 205 (<- thermal pressure) This patch relies on the new arch_update_thermal_pressure() handling correctly such use case (with boost frequencies). Signed-off-by: Lukasz Luba <lukasz.luba@arm.com> Reviewed-by: Thara Gopinath <thara.gopinath@linaro.org> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> --- drivers/cpufreq/qcom-cpufreq-hw.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'drivers/cpufreq/qcom-cpufreq-hw.c') diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 0138b2ec406d..248135e5087e 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -275,10 +275,10 @@ static unsigned int qcom_lmh_get_throttle_freq(struct qcom_cpufreq_data *data) static void qcom_lmh_dcvs_notify(struct qcom_cpufreq_data *data) { - unsigned long max_capacity, capacity, freq_hz, throttled_freq; struct cpufreq_policy *policy = data->policy; int cpu = cpumask_first(policy->cpus); struct device *dev = get_cpu_device(cpu); + unsigned long freq_hz, throttled_freq; struct dev_pm_opp *opp; unsigned int freq; @@ -295,17 +295,8 @@ static void qcom_lmh_dcvs_notify(struct qcom_cpufreq_data *data) throttled_freq = freq_hz / HZ_PER_KHZ; - /* Update thermal pressure */ - - max_capacity = arch_scale_cpu_capacity(cpu); - capacity = mult_frac(max_capacity, throttled_freq, policy->cpuinfo.max_freq); - - /* Don't pass boost capacity to scheduler */ - if (capacity > max_capacity) - capacity = max_capacity; - - arch_set_thermal_pressure(policy->related_cpus, - max_capacity - capacity); + /* Update thermal pressure (the boost frequencies are accepted) */ + arch_update_thermal_pressure(policy->related_cpus, throttled_freq); /* * In the unlikely case policy is unregistered do not enable -- cgit v1.2.3-70-g09d2 From be6592ed56a7cca8a001ff339cb9325bfa3c6e3f Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel <ardb@kernel.org> Date: Thu, 11 Nov 2021 17:48:06 +0200 Subject: cpufreq: qcom-cpufreq-hw: Avoid stack buffer for IRQ name Registering an IRQ requires the string buffer containing the name to remain allocated, as the name is not copied into another buffer. So let's add a irq_name field to the data struct instead, which is guaranteed to have the appropriate lifetime. Cc: Thara Gopinath <thara.gopinath@linaro.org> Cc: Bjorn Andersson <bjorn.andersson@linaro.org> Cc: Andy Gross <agross@kernel.org> Signed-off-by: Ard Biesheuvel <ardb@kernel.org> Reviewed-by: Thara Gopinath <thara.gopinath@linaro.org> Tested-by: Steev Klimaszewski <steev@kali.org> Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org> Reviewed-by: Matthias Kaehlcke <mka@chromium.org> Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> --- drivers/cpufreq/qcom-cpufreq-hw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/cpufreq/qcom-cpufreq-hw.c') diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 248135e5087e..63c11e7dfba0 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -46,6 +46,7 @@ struct qcom_cpufreq_data { */ struct mutex throttle_lock; int throttle_irq; + char irq_name[15]; bool cancel_throttle; struct delayed_work throttle_work; struct cpufreq_policy *policy; @@ -367,7 +368,6 @@ static int qcom_cpufreq_hw_lmh_init(struct cpufreq_policy *policy, int index) { struct qcom_cpufreq_data *data = policy->driver_data; struct platform_device *pdev = cpufreq_get_driver_data(); - char irq_name[15]; int ret; /* @@ -384,11 +384,11 @@ static int qcom_cpufreq_hw_lmh_init(struct cpufreq_policy *policy, int index) mutex_init(&data->throttle_lock); INIT_DEFERRABLE_WORK(&data->throttle_work, qcom_lmh_dcvs_poll); - snprintf(irq_name, sizeof(irq_name), "dcvsh-irq-%u", policy->cpu); + snprintf(data->irq_name, sizeof(data->irq_name), "dcvsh-irq-%u", policy->cpu); ret = request_threaded_irq(data->throttle_irq, NULL, qcom_lmh_dcvs_handle_irq, - IRQF_ONESHOT, irq_name, data); + IRQF_ONESHOT, data->irq_name, data); if (ret) { - dev_err(&pdev->dev, "Error registering %s: %d\n", irq_name, ret); + dev_err(&pdev->dev, "Error registering %s: %d\n", data->irq_name, ret); return 0; } -- cgit v1.2.3-70-g09d2 From e0e27c3d4e20dab861566f1c348ae44e4b498630 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org> Date: Thu, 11 Nov 2021 17:48:07 +0200 Subject: cpufreq: qcom-hw: Fix probable nested interrupt handling Re-enabling an interrupt from its own interrupt handler may cause an interrupt storm, if there is a pending interrupt and because its handling is disabled due to already done entrance into the handler above in the stack. Also, apparently it is improper to lock a mutex in an interrupt contex. Fixes: 275157b367f4 ("cpufreq: qcom-cpufreq-hw: Add dcvs interrupt support") Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org> Reviewed-by: Matthias Kaehlcke <mka@chromium.org> Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> --- drivers/cpufreq/qcom-cpufreq-hw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/cpufreq/qcom-cpufreq-hw.c') diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 63c11e7dfba0..5b609e205435 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -335,9 +335,9 @@ static irqreturn_t qcom_lmh_dcvs_handle_irq(int irq, void *data) /* Disable interrupt and enable polling */ disable_irq_nosync(c_data->throttle_irq); - qcom_lmh_dcvs_notify(c_data); + schedule_delayed_work(&c_data->throttle_work, 0); - return 0; + return IRQ_HANDLED; } static const struct qcom_cpufreq_soc_data qcom_soc_data = { -- cgit v1.2.3-70-g09d2 From 3ed6dfbd3bb987b3d2de86304ae45972ebff5870 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org> Date: Thu, 11 Nov 2021 17:48:08 +0200 Subject: cpufreq: qcom-hw: Set CPU affinity of dcvsh interrupts In runtime CPU cluster specific dcvsh interrupts may be handled on unrelated CPU cores, it leads to an issue of too excessive number of received and handled interrupts, but this is not observed, if CPU affinity of the interrupt handler is set in accordance to CPU clusters. The change reduces a number of received interrupts in about 10-100 times. Signed-off-by: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org> Reviewed-by: Matthias Kaehlcke <mka@chromium.org> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> --- drivers/cpufreq/qcom-cpufreq-hw.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/cpufreq/qcom-cpufreq-hw.c') diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 5b609e205435..5b0acf5448c3 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -392,6 +392,11 @@ static int qcom_cpufreq_hw_lmh_init(struct cpufreq_policy *policy, int index) return 0; } + ret = irq_set_affinity_hint(data->throttle_irq, policy->cpus); + if (ret) + dev_err(&pdev->dev, "Failed to set CPU affinity of %s[%d]\n", + data->irq_name, data->throttle_irq); + return 0; } -- cgit v1.2.3-70-g09d2 From 8f5783ad9eb83747471f61f94dbe209fb9fb8a7d Mon Sep 17 00:00:00 2001 From: Stephen Boyd <swboyd@chromium.org> Date: Tue, 16 Nov 2021 18:03:46 -0800 Subject: cpufreq: qcom-hw: Use optional irq API Use platform_get_irq_optional() to avoid a noisy error message when the irq isn't specified. The irq is definitely optional given that we only care about errors that are -EPROBE_DEFER here. Cc: Thara Gopinath <thara.gopinath@linaro.org> Signed-off-by: Stephen Boyd <swboyd@chromium.org> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> --- drivers/cpufreq/qcom-cpufreq-hw.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/cpufreq/qcom-cpufreq-hw.c') diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 5b0acf5448c3..05f3d7876e44 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -374,9 +374,11 @@ static int qcom_cpufreq_hw_lmh_init(struct cpufreq_policy *policy, int index) * Look for LMh interrupt. If no interrupt line is specified / * if there is an error, allow cpufreq to be enabled as usual. */ - data->throttle_irq = platform_get_irq(pdev, index); - if (data->throttle_irq <= 0) - return data->throttle_irq == -EPROBE_DEFER ? -EPROBE_DEFER : 0; + data->throttle_irq = platform_get_irq_optional(pdev, index); + if (data->throttle_irq == -ENXIO) + return 0; + if (data->throttle_irq < 0) + return data->throttle_irq; data->cancel_throttle = false; data->policy = policy; -- cgit v1.2.3-70-g09d2