From 9c062eae64dbf08c4f2a205a4888a084dd5b6ecd Mon Sep 17 00:00:00 2001 From: Stephen Dickey Date: Mon, 1 Nov 2021 17:22:09 -0700 Subject: [PATCH] pause: port the users of pause Port the users of pause from the 5.10 kernel to ensure code is up to date. Change-Id: Ibe52cc37e0dca425f2f472b9cf254a2453bd9c47 Signed-off-by: Stephen Dickey --- drivers/soc/qcom/hyp_core_ctl.c | 2 + drivers/thermal/qcom/Kconfig | 11 ++ drivers/thermal/qcom/Makefile | 1 + drivers/thermal/qcom/thermal_pause.c | 168 ++++++++++++++++++--------- include/linux/thermal_pause.h | 33 ++++++ kernel/sched/walt/Makefile | 2 +- kernel/sched/walt/walt_pause.c | 6 +- 7 files changed, 162 insertions(+), 61 deletions(-) create mode 100644 include/linux/thermal_pause.h diff --git a/drivers/soc/qcom/hyp_core_ctl.c b/drivers/soc/qcom/hyp_core_ctl.c index ae9eac7a548c..c5a00da88e9a 100644 --- a/drivers/soc/qcom/hyp_core_ctl.c +++ b/drivers/soc/qcom/hyp_core_ctl.c @@ -29,6 +29,8 @@ #include +#include + #define MAX_RESERVE_CPUS (num_possible_cpus()/2) static DEFINE_PER_CPU(struct freq_qos_request, qos_min_req); diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig index bfd889422dd3..53641c3470a1 100644 --- a/drivers/thermal/qcom/Kconfig +++ b/drivers/thermal/qcom/Kconfig @@ -41,3 +41,14 @@ config QCOM_LMH input from temperature and current sensors. On many newer Qualcomm SoCs LMh is configured in the firmware and this feature need not be enabled. However, on certain SoCs like sdm845 LMh has to be configured from kernel. + +config QTI_CPU_PAUSE_COOLING_DEVICE + tristate "QTI CPU Pause cooling devices" + depends on THERMAL_OF && HOTPLUG_CPU + depends on ARCH_QCOM || COMPILE_TEST + help + This enables the QTI CPU Pause cooling device. These cooling + devices will be used by QTI chipset to pause a CPU from being + scheduled and hence will let the CPU to power collapse. Pausing + a CPU will be used when the CPU frequency mitigation + is not good enough to achieve the necessary cooling. \ No newline at end of file diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile index 0fa2512042e7..99a16fd8a22c 100644 --- a/drivers/thermal/qcom/Makefile +++ b/drivers/thermal/qcom/Makefile @@ -6,3 +6,4 @@ qcom_tsens-y += tsens.o tsens-v2.o tsens-v1.o tsens-v0_1.o \ obj-$(CONFIG_QCOM_SPMI_ADC_TM5) += qcom-spmi-adc-tm5.o obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o obj-$(CONFIG_QCOM_LMH) += lmh.o +obj-$(CONFIG_QTI_CPU_PAUSE_COOLING_DEVICE) += thermal_pause.o diff --git a/drivers/thermal/qcom/thermal_pause.c b/drivers/thermal/qcom/thermal_pause.c index f76db4033441..0c926c220c94 100644 --- a/drivers/thermal/qcom/thermal_pause.c +++ b/drivers/thermal/qcom/thermal_pause.c @@ -23,14 +23,18 @@ enum thermal_pause_levels { MAX_THERMAL_PAUSE_LEVEL }; +#define THERMAL_PAUSE_RETRY_COUNT 5 + struct thermal_pause_cdev { struct list_head node; cpumask_t cpu_mask; - bool thermal_pause_level; + enum thermal_pause_levels thermal_pause_level; + enum thermal_pause_levels thermal_pause_req; struct thermal_cooling_device *cdev; struct device_node *np; char cdev_name[THERMAL_NAME_LENGTH]; struct work_struct reg_work; + struct work_struct pause_update_work; }; static DEFINE_MUTEX(cpus_pause_lock); @@ -39,22 +43,25 @@ static LIST_HEAD(thermal_pause_cdev_list); static struct cpumask cpus_in_max_cooling_level; static enum cpuhp_state cpu_hp_online; -static BLOCKING_NOTIFIER_HEAD(multi_max_cooling_level_notifer); +static BLOCKING_NOTIFIER_HEAD(thermal_pause_notifier); -void cpu_cooling_multi_max_level_notifier_register(struct notifier_block *n) +void thermal_pause_notifier_register(struct notifier_block *n) { - blocking_notifier_chain_register(&multi_max_cooling_level_notifer, n); + blocking_notifier_chain_register(&thermal_pause_notifier, n); } +EXPORT_SYMBOL(thermal_pause_notifier_register); -void cpu_cooling_multi_max_level_notifier_unregister(struct notifier_block *n) +void thermal_pause_notifier_unregister(struct notifier_block *n) { - blocking_notifier_chain_unregister(&multi_max_cooling_level_notifer, n); + blocking_notifier_chain_unregister(&thermal_pause_notifier, n); } +EXPORT_SYMBOL(thermal_pause_notifier_unregister); -const struct cpumask *cpu_cooling_multi_get_max_level_cpumask(void) +const struct cpumask *thermal_paused_cpumask(void) { return &cpus_in_max_cooling_level; } +EXPORT_SYMBOL(thermal_paused_cpumask); static int thermal_pause_hp_online(unsigned int online_cpu) { @@ -76,26 +83,21 @@ static int thermal_pause_hp_online(unsigned int online_cpu) } /** - * thermal_pause_cpus_pause - function to pause a group of cpus at - * the specified level. + * thermal_pause_work - work function to pause a group of cpus at + * the specified level. * - * @thermal_pause_cdev: the pause device + * @thermal_pasue_cdev: the cdev currently being processed * - * function to handle setting the current cpus paused by + * Function to handle setting the current cpus paused by * this driver for the mask specified in the device. - * it assumes the mutex is locked. - * - * Returns 0 if CPUs were paused, error otherwise + * it assumes the mutex is locked upon entrance. */ -static int thermal_pause_cpus_pause(struct thermal_pause_cdev *thermal_pause_cdev) +static int thermal_pause_work(struct thermal_pause_cdev *thermal_pause_cdev) { int cpu = 0; int ret = -EBUSY; cpumask_t cpus_to_pause, cpus_to_notify; - if (thermal_pause_cdev->thermal_pause_level) - return ret; - cpumask_copy(&cpus_to_pause, &thermal_pause_cdev->cpu_mask); pr_debug("Pause:%*pbl\n", cpumask_pr_args(&thermal_pause_cdev->cpu_mask)); @@ -120,33 +122,29 @@ static int thermal_pause_cpus_pause(struct thermal_pause_cdev *thermal_pause_cde /* Failure. These cpus not paused by thermal */ pr_err("Error pausing CPU:%*pbl. err:%d\n", cpumask_pr_args(&thermal_pause_cdev->cpu_mask), ret); - return ret; } return ret; } /** - * thermal_pause_cpus_unpause - function to unpause a + * thermal_resume_work - work function to unpause a * group of cpus in the mask for this cdev * - * @thermal_pause_cdev: the pause device + * @thermal_pasue_cdev: the cdev currently being processed * - * function to handle enabling the group of cpus in the cdev - * - * Returns 0 if CPUs were unpaused, + * Function to handle enabling the group of cpus in the cdev. + * This is performed as a work function to avoid conflicts + * between a hotplug event invoking a pause cooling device, + * and a thermal event invoking a pause cooling device. */ -static int thermal_pause_cpus_unpause(struct thermal_pause_cdev *thermal_pause_cdev) +static int thermal_resume_work(struct thermal_pause_cdev *thermal_pause_cdev) { int cpu = 0; int ret = -ENODEV; cpumask_t cpus_to_unpause, new_paused_cpus, cpus_to_notify; struct thermal_pause_cdev *cdev; - /* do not unpause a cooling device not paused */ - if (!thermal_pause_cdev->thermal_pause_level) - return ret; - cpumask_copy(&cpus_to_unpause, &thermal_pause_cdev->cpu_mask); pr_debug("Unpause:%*pbl\n", cpumask_pr_args(&cpus_to_unpause)); @@ -177,12 +175,73 @@ static int thermal_pause_cpus_unpause(struct thermal_pause_cdev *thermal_pause_c /* Failure. Ref-count for cpus controlled by thermal still set */ pr_err("Error resuming CPU:%*pbl. err:%d\n", cpumask_pr_args(&thermal_pause_cdev->cpu_mask), ret); - return ret; } return ret; } +/** + * thermal_pause_set_update_work: Enforce Requested State + * + * @work: the work structure for this work + * + * Enforce the most recent requested cooling state, if + * it is a mismatch with the current state. Since the + * request is made in a different context from the + * enforcement, it is possible to have an updated request + * after completing the resume/pause request. + * + * Handle a post-operation mismatch between the cooling state + * and the requested state. + * + * This is performed as a work function to avoid conflicts + * between a hotplug event invoking a pause cooling device, + * and a thermal event invoking a pause cooling device. + */ +static void thermal_pause_update_work(struct work_struct *work) +{ + int ret = 0; + int retcnt = THERMAL_PAUSE_RETRY_COUNT; + struct thermal_pause_cdev *thermal_pause_cdev = + container_of(work, struct thermal_pause_cdev, pause_update_work); + + mutex_lock(&cpus_pause_lock); + +retry: + if (thermal_pause_cdev->thermal_pause_req != thermal_pause_cdev->thermal_pause_level) { + if (thermal_pause_cdev->thermal_pause_req == THERMAL_NO_CPU_PAUSE) { + ret = thermal_resume_work(thermal_pause_cdev); + if (ret >= 0) + thermal_pause_cdev->thermal_pause_level = THERMAL_NO_CPU_PAUSE; + } else { + ret = thermal_pause_work(thermal_pause_cdev); + if (ret >= 0) + thermal_pause_cdev->thermal_pause_level = THERMAL_GROUP_CPU_PAUSE; + } + if (ret < 0 && retcnt > 0) { + retcnt--; + goto retry; + } + } + + /* + * if the pause/resume operation itself failed (and failed THERMAL_PAUSE_RETRY_COUNT + * times) then ret will be negative here. Do not repeatedly retry if pause itself + * failed, because this can happen indefinitely. + * + * If instead the pause request has been toggled back and forth many times by + * the thermal framework, this can be handled here. + */ + if (ret >= 0 && + thermal_pause_cdev->thermal_pause_req != thermal_pause_cdev->thermal_pause_level) { + pr_debug("Pause: requested state changed while workfn running\n"); + retcnt = THERMAL_PAUSE_RETRY_COUNT; + goto retry; + } + + mutex_unlock(&cpus_pause_lock); +} + /** * thermal_pause_set_cur_state - callback function to set the current cooling * level. @@ -190,7 +249,8 @@ static int thermal_pause_cpus_unpause(struct thermal_pause_cdev *thermal_pause_c * @level: set this variable to the current cooling level. * * Callback for the thermal cooling device to change the cpu pause - * current cooling level. + * current cooling level, by making a requested and queueing the + * work to be done. * * Return: 0 on success, an error code otherwise. */ @@ -198,29 +258,22 @@ static int thermal_pause_set_cur_state(struct thermal_cooling_device *cdev, unsigned long level) { struct thermal_pause_cdev *thermal_pause_cdev = cdev->devdata; - int ret = 0; if (level >= MAX_THERMAL_PAUSE_LEVEL) return -EINVAL; - if (thermal_pause_cdev->thermal_pause_level == level) - return 0; - mutex_lock(&cpus_pause_lock); - if (level == THERMAL_GROUP_CPU_PAUSE) - ret = thermal_pause_cpus_pause(thermal_pause_cdev); + + if (level) + thermal_pause_cdev->thermal_pause_req = THERMAL_GROUP_CPU_PAUSE; else - ret = thermal_pause_cpus_unpause(thermal_pause_cdev); - /* - * only change the pause level if things were successful. Otherwise - * an unsuccessful pause operation can be followed by a resume - * operation, resuming cpus not paused by this cooling device. - */ - if (ret == 0) - thermal_pause_cdev->thermal_pause_level = level; + thermal_pause_cdev->thermal_pause_req = THERMAL_NO_CPU_PAUSE; + + queue_work(system_highpri_wq, &thermal_pause_cdev->pause_update_work); mutex_unlock(&cpus_pause_lock); - return ret; + + return 0; } /** @@ -307,6 +360,7 @@ static int thermal_pause_probe(struct platform_device *pdev) struct of_phandle_iterator it; cpumask_t cpu_mask; unsigned long mask = 0; + const char *alias; INIT_LIST_HEAD(&thermal_pause_cdev_list); cpumask_clear(&cpus_in_max_cooling_level); @@ -339,17 +393,22 @@ static int thermal_pause_probe(struct platform_device *pdev) of_node_put(subsys_np); return -ENOMEM; } - - snprintf(thermal_pause_cdev->cdev_name, THERMAL_NAME_LENGTH, + ret = of_property_read_string(subsys_np, + "qcom,cdev-alias", &alias); + if (ret) + snprintf(thermal_pause_cdev->cdev_name, THERMAL_NAME_LENGTH, "thermal-pause-%X", mask); + else + strscpy(thermal_pause_cdev->cdev_name, alias, + THERMAL_NAME_LENGTH); thermal_pause_cdev->thermal_pause_level = false; thermal_pause_cdev->cdev = NULL; thermal_pause_cdev->np = subsys_np; cpumask_copy(&thermal_pause_cdev->cpu_mask, &cpu_mask); - INIT_WORK(&thermal_pause_cdev->reg_work, - thermal_pause_register_cdev); + INIT_WORK(&thermal_pause_cdev->reg_work, thermal_pause_register_cdev); + INIT_WORK(&thermal_pause_cdev->pause_update_work, thermal_pause_update_work); list_add(&thermal_pause_cdev->node, &thermal_pause_cdev_list); } @@ -378,13 +437,8 @@ static int thermal_pause_remove(struct platform_device *pdev) /* for each asserted cooling device, resume the CPUs */ if (thermal_pause_cdev->thermal_pause_level) { - mutex_unlock(&cpus_pause_lock); - ret = walt_resume_cpus(&thermal_pause_cdev->cpu_mask); - - if (ret < 0) - pr_err("Error resuming CPU:%*pbl. err:%d\n", - cpumask_pr_args(&thermal_pause_cdev->cpu_mask), ret); - mutex_lock(&cpus_pause_lock); + thermal_pause_cdev->thermal_pause_req = THERMAL_NO_CPU_PAUSE; + queue_work(system_highpri_wq, &thermal_pause_cdev->pause_update_work); } if (thermal_pause_cdev->cdev) diff --git a/include/linux/thermal_pause.h b/include/linux/thermal_pause.h new file mode 100644 index 000000000000..0769cdd8d460 --- /dev/null +++ b/include/linux/thermal_pause.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#ifndef _LINUX_THERMAL_PAUSE_H +#define _LINUX_THERMAL_PAUSE_H + +#include +#include + +#if IS_ENABLED(CONFIG_QTI_CPU_PAUSE_COOLING_DEVICE) +extern void thermal_pause_notifier_register(struct notifier_block *n); +extern void thermal_pause_notifier_unregister(struct notifier_block *n); +extern const struct cpumask *thermal_paused_cpumask(void); +#else +static inline +void thermal_pause_notifier_register(struct notifier_block *n) +{ +} + +static inline +void thermal_pause_notifier_unregister(struct notifier_block *n) +{ +} + +static inline const struct cpumask *thermal_paused_cpumask(void) +{ + return cpu_none_mask; +} +#endif /* CONFIG_QTI_CPU_PAUSE_COOLING_DEVICE */ + +#endif /* _LINUX_THERMAL_PAUSE_H */ diff --git a/kernel/sched/walt/Makefile b/kernel/sched/walt/Makefile index ed35366108ed..870d9b2fcfe3 100644 --- a/kernel/sched/walt/Makefile +++ b/kernel/sched/walt/Makefile @@ -4,7 +4,7 @@ KCOV_INSTRUMENT := n KCSAN_SANITIZE := n obj-$(CONFIG_SCHED_WALT) += sched-walt.o -sched-walt-$(CONFIG_SCHED_WALT) := walt.o boost.o sched_avg.o walt_pause.o core_ctl.o trace.o input-boost.o sysctl.o cpufreq_walt.o fixup.o walt_lb.o walt_rt.o walt_cfs.o walt_tp.o +sched-walt-$(CONFIG_SCHED_WALT) := walt.o boost.o sched_avg.o walt_pause.o core_ctl.o trace.o input-boost.o sysctl.o cpufreq_walt.o fixup.o walt_lb.o walt_rt.o walt_cfs.o obj-$(CONFIG_SCHED_WALT_DEBUG) += sched-walt-debug.o sched-walt-debug-$(CONFIG_SCHED_WALT_DEBUG) := walt_debug.o preemptirq_long.o diff --git a/kernel/sched/walt/walt_pause.c b/kernel/sched/walt/walt_pause.c index cca3d5904923..ef6bdcbc897a 100644 --- a/kernel/sched/walt/walt_pause.c +++ b/kernel/sched/walt/walt_pause.c @@ -66,7 +66,7 @@ int walt_pause_cpus(struct cpumask *cpus) goto unlock; } - ret = pause_cpus(cpus); + // ret = pause_cpus(cpus); if (ret < 0) pr_debug("pause_cpus failure ret=%d cpus=%*pbl\n", ret, cpumask_pr_args(&requested_cpus)); @@ -95,7 +95,7 @@ int walt_resume_cpus(struct cpumask *cpus) if (cpumask_empty(cpus)) goto unlock; - ret = resume_cpus(cpus); + // ret = resume_cpus(cpus); if (ret < 0) { pr_debug("resume_cpus failure ret=%d cpus=%*pbl\n", ret, cpumask_pr_args(&requested_cpus)); @@ -156,7 +156,7 @@ static void walt_pause_online_workfn(struct work_struct *work) if (cpumask_empty(&re_pause_cpus)) goto unlock; - ret = pause_cpus(&re_pause_cpus); + //ret = pause_cpus(&re_pause_cpus); unlock: mutex_unlock(&pause_lock);