hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IBC4SJ
--------------------------------
Added governor util that adjusts frequency based on resource utilization.
Signed-off-by: Xiangwei Li liwei728@huawei.com --- drivers/devfreq/Kconfig | 6 ++ drivers/devfreq/Makefile | 1 + drivers/devfreq/governor_util.c | 127 +++++++++++++++++++++++++++++ drivers/devfreq/hisi_uncore_freq.c | 32 ++++++-- include/linux/devfreq.h | 15 ++++ 5 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 drivers/devfreq/governor_util.c
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 158ef3728bf1..1bcdce9a89d3 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -73,6 +73,12 @@ config DEVFREQ_GOV_PASSIVE through sysfs entries. The passive governor recommends that devfreq device uses the OPP table to get the frequency/voltage.
+config DEVFREQ_GOV_UTIL + tristate "Util" + help + This governor Adjust the frequency based on the load utilization + rate. + comment "DEVFREQ Drivers"
config ARM_EXYNOS_BUS_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 04113d583515..963f6b573858 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o +obj-$(CONFIG_DEVFREQ_GOV_UTIL) += governor_util.o
# DEVFREQ Drivers obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o diff --git a/drivers/devfreq/governor_util.c b/drivers/devfreq/governor_util.c new file mode 100644 index 000000000000..023a40c66302 --- /dev/null +++ b/drivers/devfreq/governor_util.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/drivers/devfreq/governor_util.c + * + * Copyright (C) 2024 HISI UNCORE + * Xiangwei Li liwei728@huawei.com + */ + +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/devfreq.h> +#include <linux/math64.h> +#include "governor.h" + +/* Default constants for DevFreq-Util (DFUL) */ +#define BW_UTIL_DEFAULT (50) + +static int devfreq_util_func(struct devfreq *df, + unsigned long *freq) +{ + int err; + struct devfreq_dev_status *stat; + unsigned long cur_bw, max_bw; + unsigned long cur_freq, step_freq; + unsigned long min_freq, max_freq; + unsigned int util, dful_val = BW_UTIL_DEFAULT; + struct devfreq_util_data *data = df->data; + + err = devfreq_update_stats(df); + if (err) + return err; + + stat = &df->last_status; + + if (data) { + dful_val = data->dful_val; + } + + if (dful_val > 100) + return -EINVAL; + + /* Assume MAX if it is going to be divided by zero */ + if (stat->total_time == 0) { + *freq = df->scaling_max_freq;; + return 0; + } + + /* Prevent overflow */ + if (stat->busy_time >= (1 << 24) || stat->total_time >= (1 << 24)) { + stat->busy_time >>= 7; + stat->total_time >>= 7; + } + + min_freq = df->scaling_min_freq; + max_freq = df->scaling_max_freq; + cur_freq = df->previous_freq; + cur_bw = stat->busy_time; + max_bw = stat->total_time; + + /* Set the desired frequency based on the load */ + util = div_u64(cur_bw * 100, + max_bw * div_u64(cur_freq * 100, max_freq) / 100); + *freq = cur_freq * div_u64(util * 100, dful_val) / 100; + + step_freq = div_u64(max_freq - min_freq, + df->profile->max_state - 1); + *freq = div_u64(*freq, step_freq) * step_freq; + *freq = clamp(*freq, min_freq, max_freq); + + return 0; +} + +static int devfreq_util_handler(struct devfreq *devfreq, + unsigned int event, void *data) +{ + switch (event) { + case DEVFREQ_GOV_START: + devfreq_monitor_start(devfreq); + break; + + case DEVFREQ_GOV_STOP: + devfreq_monitor_stop(devfreq); + break; + + case DEVFREQ_GOV_UPDATE_INTERVAL: + devfreq_update_interval(devfreq, (unsigned int *)data); + break; + + case DEVFREQ_GOV_SUSPEND: + devfreq_monitor_suspend(devfreq); + break; + + case DEVFREQ_GOV_RESUME: + devfreq_monitor_resume(devfreq); + break; + + default: + break; + } + + return 0; +} + +static struct devfreq_governor devfreq_util = { + .name = DEVFREQ_GOV_UTIL, + .get_target_freq = devfreq_util_func, + .event_handler = devfreq_util_handler, +}; + +static int __init devfreq_util_init(void) +{ + return devfreq_add_governor(&devfreq_util); +} +subsys_initcall(devfreq_util_init); + +static void __exit devfreq_util_exit(void) +{ + int ret; + + ret = devfreq_remove_governor(&devfreq_util); + if (ret) + pr_err("%s: failed remove governor %d\n", __func__, ret); + + return; +} +module_exit(devfreq_util_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/devfreq/hisi_uncore_freq.c b/drivers/devfreq/hisi_uncore_freq.c index 481923416166..656ae196b599 100644 --- a/drivers/devfreq/hisi_uncore_freq.c +++ b/drivers/devfreq/hisi_uncore_freq.c @@ -240,6 +240,28 @@ static int hisi_uncore_target(struct device *dev, unsigned long *freq, static int hisi_uncore_get_dev_status(struct device *dev, struct devfreq_dev_status *stat) { + int rc, i, ratio; + struct related_event *event; + struct devfreq_event_data edata; + struct hisi_uncore_freq *uncore = dev_get_drvdata(dev); + + ratio = 0; + for (i = 0; i < uncore->related_event_cnt; ++i) { + event = &uncore->related_events[i]; + event->edev = devfreq_event_get_edev_by_dev(&event->pdev->dev); + if (!event->edev) + continue; + rc = devfreq_event_get_event(event->edev, &edata); + if (rc) + return rc; + + if (ratio <= edata.load_count * 1000 / edata.total_count) { + stat->busy_time = edata.load_count; + stat->total_time = edata.total_count; + ratio = edata.load_count * 1000 / edata.total_count; + } + } + return 0; }
@@ -466,13 +488,6 @@ static int creat_related_event(struct hisi_uncore_freq *uncore, char *name) if (IS_ERR(event->pdev)) return PTR_ERR(event->pdev);
- event->edev = devfreq_event_get_edev_by_dev(&event->pdev->dev); - if (event->edev) { - dev_err(&event->pdev->dev, "devfreq event dev do not added\n"); - platform_device_unregister(event->pdev); - return -ENODEV; - } - strncpy(event->name, name, strlen(name));
return 0; @@ -483,6 +498,7 @@ static void remove_related_event(struct hisi_uncore_freq *uncore) int i; struct related_event *event;
+ devfreq_suspend_device(uncore->devfreq); for (i = 0; i < uncore->related_event_cnt; ++i) { event = &uncore->related_events[i]; event->edev = NULL; @@ -538,6 +554,8 @@ static ssize_t related_events_store(struct device *dev, uncore->related_event_cnt++; }
+ devfreq_resume_device(uncore->devfreq); + kfree(item); return count; } diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index d312ffbac4dd..b1a5f62a0cee 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -21,6 +21,7 @@ #define DEVFREQ_GOV_POWERSAVE "powersave" #define DEVFREQ_GOV_USERSPACE "userspace" #define DEVFREQ_GOV_PASSIVE "passive" +#define DEVFREQ_GOV_UTIL "util"
/* DEVFREQ notifier interface */ #define DEVFREQ_TRANSITION_NOTIFIER (0) @@ -337,6 +338,20 @@ struct devfreq_passive_data { struct list_head cpu_data_list; };
+#if IS_ENABLED(CONFIG_DEVFREQ_GOV_UTIL) +/** + * struct devfreq_util_data - ``void *data`` fed to struct devfreq + * and devfreq_add_device + * @dful_val: Resource utilization baseline. + * + * If the fed devfreq_util_data pointer is NULL to the governor, + * the governor uses the default values. + */ +struct devfreq_util_data { + unsigned int dful_val; +}; +#endif + #if !defined(CONFIG_PM_DEVFREQ) static inline struct devfreq *devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile,