[RFC PATCH 3/3] thermal/drivers/cpuidle_cooling: cpuidle cooling driver for non DT/ARM
From: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Date: 2021-08-24 05:17:38
Subsystem:
the rest, thermal · Maintainers:
Linus Torvalds, Rafael J. Wysocki, Daniel Lezcano
This driver has same functionality as existing cpuidle_cooling driver. This uses idle injection to cool a CPU. This driver reuses all the code from the existing driver, except the DT specific function. Each CPU is registered as a cooling device, with type = idle-x, where x is the cpu number. It registers two module parameters: idle_duration_us : To change default idle duration idle_latency_us : To change default latency The meaning of these parameters is same as in the existing code. It is described in the header of file drivers/powercap/idle_inject.c. Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> --- drivers/thermal/Kconfig | 12 ++ drivers/thermal/Makefile | 2 + drivers/thermal/cpuidle_cooling_generic.c | 155 ++++++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 drivers/thermal/cpuidle_cooling_generic.c
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 3d669a1774d3..3efe3df75337 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig@@ -193,6 +193,18 @@ config CPU_IDLE_THERMAL idle cycle. endif +config CPU_IDLE_THERMAL_GENERIC + tristate "CPU idle cooling device" + depends on !CPU_THERMAL + select CPU_IDLE_THERMAL_CORE + help + This implements the CPU cooling mechanism through + idle injection. This will throttle the CPU by injecting + idle cycle. + + This driver can also be built as a module. If so, the module will + be called cpuidle_cooling_generic. + config DEVFREQ_THERMAL bool "Generic device cooling support" depends on PM_DEVFREQ
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 769d025beb11..c6dce523ab2b 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile@@ -26,6 +26,8 @@ thermal_sys-$(CONFIG_CPU_FREQ_THERMAL) += cpufreq_cooling.o thermal_sys-$(CONFIG_CPU_IDLE_THERMAL_CORE) += cpuidle_cooling_core.o thermal_sys-$(CONFIG_CPU_IDLE_THERMAL) += cpuidle_cooling.o +obj-$(CONFIG_CPU_IDLE_THERMAL_GENERIC) += cpuidle_cooling_generic.o + # devfreq cooling thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
diff --git a/drivers/thermal/cpuidle_cooling_generic.c b/drivers/thermal/cpuidle_cooling_generic.c
new file mode 100644
index 000000000000..adb33dfba2e7
--- /dev/null
+++ b/drivers/thermal/cpuidle_cooling_generic.c@@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generic cpu idle cooling driver + * Copyright (c) 2021, Intel Corporation. + * All rights reserved. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/cpufeature.h> +#include <linux/cpuhotplug.h> +#include <linux/idle_inject.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/thermal.h> +#include <linux/topology.h> + +#include "cpuidle_cooling_core.h" + +#define IDLE_DURATION 10000 +#define IDLE_LATENCY 5000 + +static int idle_duration_us = IDLE_DURATION; +static int idle_latency_us = IDLE_LATENCY; + +module_param(idle_duration_us, int, 0644); +MODULE_PARM_DESC(idle_duration_us, + "Idle duration in us."); + +module_param(idle_latency_us, int, 0644); +MODULE_PARM_DESC(idle_latency_us, + "Idle latency in us."); + +struct cpuidle_cooling { + struct thermal_cooling_device *cdev; + struct idle_inject_device *ii_dev; + struct cpuidle_cooling_device *idle_cdev; +}; +static DEFINE_PER_CPU(struct cpuidle_cooling, cooling_devs); +static cpumask_t cpuidle_cpu_mask; + +static int cpuidle_cooling_register(int cpu) +{ + struct cpuidle_cooling *cooling_dev = &per_cpu(cooling_devs, cpu); + struct cpuidle_cooling_device *idle_cdev; + struct thermal_cooling_device *cdev; + struct idle_inject_device *ii_dev; + char *name; + int ret; + + if (cpumask_test_cpu(cpu, &cpuidle_cpu_mask)) + return 0; + + idle_cdev = kzalloc(sizeof(*idle_cdev), GFP_KERNEL); + if (!idle_cdev) { + ret = -ENOMEM; + goto out; + } + + ii_dev = idle_inject_register((struct cpumask *)cpumask_of(cpu)); + if (!ii_dev) { + pr_err("idle_inject_register failed for cpu:%d\n", cpu); + ret = -EINVAL; + goto out_kfree; + } + + idle_inject_set_duration(ii_dev, TICK_USEC, idle_duration_us); + idle_inject_set_latency(ii_dev, idle_latency_us); + + idle_cdev->ii_dev = ii_dev; + + name = kasprintf(GFP_KERNEL, "idle-%d", cpu); + if (!name) { + ret = -ENOMEM; + goto out_unregister; + } + + cdev = thermal_cooling_device_register(name, idle_cdev, + cpuidle_cooling_get_ops()); + if (IS_ERR(cdev)) { + ret = PTR_ERR(cdev); + goto out_kfree_name; + } + + pr_debug("%s: Idle injection set with idle duration=%u, latency=%u\n", + name, idle_duration_us, idle_latency_us); + + kfree(name); + + cooling_dev->cdev = cdev; + cooling_dev->ii_dev = ii_dev; + cooling_dev->idle_cdev = idle_cdev; + cpumask_set_cpu(cpu, &cpuidle_cpu_mask); + + return 0; + +out_kfree_name: + kfree(name); +out_unregister: + idle_inject_unregister(ii_dev); +out_kfree: + kfree(idle_cdev); +out: + return ret; +} + +static void cpuidle_cooling_unregister(int cpu) +{ + struct cpuidle_cooling *cooling_dev = &per_cpu(cooling_devs, cpu); + + thermal_cooling_device_unregister(cooling_dev->cdev); + idle_inject_unregister(cooling_dev->ii_dev); + kfree(cooling_dev->idle_cdev); +} + +static int cpuidle_cooling_cpu_online(unsigned int cpu) +{ + cpuidle_cooling_register(cpu); + + return 0; +} + +static enum cpuhp_state cpuidle_cooling_hp_state __read_mostly; + +static int __init cpuidle_cooling_init(void) +{ + int ret; + + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, + "thermal/cpuidle_cooling:online", + cpuidle_cooling_cpu_online, NULL); + if (ret < 0) + return ret; + + cpuidle_cooling_hp_state = ret; + + return 0; +} +module_init(cpuidle_cooling_init) + +static void __exit cpuidle_cooling_exit(void) +{ + int i; + + cpuhp_remove_state(cpuidle_cooling_hp_state); + + for_each_cpu(i, &cpuidle_cpu_mask) { + cpuidle_cooling_unregister(i); + } +} +module_exit(cpuidle_cooling_exit) + +MODULE_LICENSE("GPL v2");
--
2.30.2