Commit a4b9559d authored by Xiangwei Li's avatar Xiangwei Li Committed by Ma Wupeng
Browse files

PM / devfreq: Add until governor

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: default avatarXiangwei Li <liwei728@huawei.com>
parent 65e12c7d
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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
+127 −0
Original line number Diff line number Diff line
// 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");
+25 −7
Original line number Diff line number Diff line
@@ -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;
}
+15 −0
Original line number Diff line number Diff line
@@ -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,