PM / devfreq: memlat: Add support for compute-bound logic

Currently, an entirely separate governor is used to handle compute-bound
cases.  Roll this functionality into the existing memlat governor and
allow devices to choose which logic to target by specifying different
drivers in the 'compatible' field.

Change-Id: I8dc41bf0474309c3564dec8e6c813511b8a7fc02
Signed-off-by: Jonathan Avila <avilaj@codeaurora.org>
This commit is contained in:
Jonathan Avila 2017-10-12 15:15:47 -07:00
parent a85abb445c
commit aaa9357227
4 changed files with 120 additions and 31 deletions

View File

@ -4,7 +4,7 @@ arm-memlat-mon is a device that represents the use of the PMU in ARM cores
to measure the parameters for latency driven memory access patterns.
Required properties:
- compatible: Must be "qcom,arm-memlat-mon"
- compatible: Must be "qcom,arm-memlat-mon" or "qcom,arm-cpu-mon"
- qcom,cpulist: List of CPU phandles to be monitored in a cluster
- qcom,target-dev: The DT device that corresponds to this master port
- qcom,core-dev-table: A mapping table of core frequency to a required bandwidth vote at the

View File

@ -31,6 +31,7 @@
#include "governor.h"
#include "governor_memlat.h"
#include <linux/perf_event.h>
#include <linux/of_device.h>
enum ev_index {
INST_IDX,
@ -60,6 +61,10 @@ struct cpu_grp_info {
struct memlat_hwmon hw;
};
struct memlat_mon_spec {
bool is_compute;
};
#define to_cpustats(cpu_grp, cpu) \
(&cpu_grp->cpustats[cpu - cpumask_first(&cpu_grp->cpus)])
#define to_devstats(cpu_grp, cpu) \
@ -91,6 +96,9 @@ static inline unsigned long read_event(struct event_data *event)
unsigned long ev_count;
u64 total, enabled, running;
if (!event->pevent)
return 0;
total = perf_event_read_value(event->pevent, &enabled, &running);
ev_count = total - event->prev_count;
event->prev_count = total;
@ -257,6 +265,7 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct memlat_hwmon *hw;
struct cpu_grp_info *cpu_grp;
const struct memlat_mon_spec *spec;
int cpu, ret;
u32 event_id;
@ -290,6 +299,22 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev)
cpu_grp->event_ids[CYC_IDX] = CYC_EV;
for_each_cpu(cpu, &cpu_grp->cpus)
to_devstats(cpu_grp, cpu)->id = cpu;
hw->start_hwmon = &start_hwmon;
hw->stop_hwmon = &stop_hwmon;
hw->get_cnt = &get_cnt;
spec = of_device_get_match_data(dev);
if (spec && spec->is_compute) {
ret = register_compute(dev, hw);
if (ret)
pr_err("Compute Gov registration failed\n");
return ret;
}
ret = of_property_read_u32(dev->of_node, "qcom,cachemiss-ev",
&event_id);
if (ret) {
@ -314,24 +339,21 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev)
else
cpu_grp->event_ids[STALL_CYC_IDX] = event_id;
for_each_cpu(cpu, &cpu_grp->cpus)
to_devstats(cpu_grp, cpu)->id = cpu;
hw->start_hwmon = &start_hwmon;
hw->stop_hwmon = &stop_hwmon;
hw->get_cnt = &get_cnt;
ret = register_memlat(dev, hw);
if (ret) {
if (ret)
pr_err("Mem Latency Gov registration failed\n");
return ret;
}
return 0;
return ret;
}
static const struct memlat_mon_spec spec[] = {
[0] = { false },
[1] = { true },
};
static const struct of_device_id memlat_match_table[] = {
{ .compatible = "qcom,arm-memlat-mon" },
{ .compatible = "qcom,arm-memlat-mon", .data = &spec[0] },
{ .compatible = "qcom,arm-cpu-mon", .data = &spec[1] },
{}
};

View File

@ -48,7 +48,8 @@ struct memlat_node {
static LIST_HEAD(memlat_list);
static DEFINE_MUTEX(list_lock);
static int use_cnt;
static int memlat_use_cnt;
static int compute_use_cnt;
static DEFINE_MUTEX(state_lock);
#define show_attr(name) \
@ -240,8 +241,7 @@ static int devfreq_memlat_get_freq(struct devfreq *df,
if (hw->core_stats[i].mem_count)
ratio /= hw->core_stats[i].mem_count;
if (!hw->core_stats[i].inst_count
|| !hw->core_stats[i].freq)
if (!hw->core_stats[i].freq)
continue;
trace_memlat_dev_meas(dev_name(df->dev.parent),
@ -280,16 +280,26 @@ static int devfreq_memlat_get_freq(struct devfreq *df,
gov_attr(ratio_ceil, 1U, 10000U);
gov_attr(stall_floor, 0U, 100U);
static struct attribute *dev_attr[] = {
static struct attribute *memlat_dev_attr[] = {
&dev_attr_ratio_ceil.attr,
&dev_attr_stall_floor.attr,
&dev_attr_freq_map.attr,
NULL,
};
static struct attribute_group dev_attr_group = {
static struct attribute *compute_dev_attr[] = {
&dev_attr_freq_map.attr,
NULL,
};
static struct attribute_group memlat_dev_attr_group = {
.name = "mem_latency",
.attrs = dev_attr,
.attrs = memlat_dev_attr,
};
static struct attribute_group compute_dev_attr_group = {
.name = "compute",
.attrs = compute_dev_attr,
};
#define MIN_MS 10U
@ -338,6 +348,12 @@ static struct devfreq_governor devfreq_gov_memlat = {
.event_handler = devfreq_memlat_ev_handler,
};
static struct devfreq_governor devfreq_gov_compute = {
.name = "compute",
.get_target_freq = devfreq_memlat_get_freq,
.event_handler = devfreq_memlat_ev_handler,
};
#define NUM_COLS 2
static struct core_dev_map *init_core_dev_map(struct device *dev,
char *prop_name)
@ -380,20 +396,17 @@ static struct core_dev_map *init_core_dev_map(struct device *dev,
return tbl;
}
int register_memlat(struct device *dev, struct memlat_hwmon *hw)
static struct memlat_node *register_common(struct device *dev,
struct memlat_hwmon *hw)
{
int ret = 0;
struct memlat_node *node;
if (!hw->dev && !hw->of_node)
return -EINVAL;
return ERR_PTR(-EINVAL);
node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL);
if (!node)
return -ENOMEM;
node->gov = &devfreq_gov_memlat;
node->attr_grp = &dev_attr_group;
return ERR_PTR(-ENOMEM);
node->ratio_ceil = 10;
node->hw = hw;
@ -401,20 +414,68 @@ int register_memlat(struct device *dev, struct memlat_hwmon *hw)
hw->freq_map = init_core_dev_map(dev, "qcom,core-dev-table");
if (!hw->freq_map) {
dev_err(dev, "Couldn't find the core-dev freq table!\n");
return -EINVAL;
return ERR_PTR(-EINVAL);
}
mutex_lock(&list_lock);
list_add_tail(&node->list, &memlat_list);
mutex_unlock(&list_lock);
return node;
}
int register_compute(struct device *dev, struct memlat_hwmon *hw)
{
struct memlat_node *node;
int ret = 0;
node = register_common(dev, hw);
if (IS_ERR(node)) {
ret = PTR_ERR(node);
goto out;
}
mutex_lock(&state_lock);
if (!use_cnt)
ret = devfreq_add_governor(&devfreq_gov_memlat);
node->gov = &devfreq_gov_compute;
node->attr_grp = &compute_dev_attr_group;
if (!compute_use_cnt)
ret = devfreq_add_governor(&devfreq_gov_compute);
if (!ret)
use_cnt++;
compute_use_cnt++;
mutex_unlock(&state_lock);
out:
if (!ret)
dev_info(dev, "Compute governor registered.\n");
else
dev_err(dev, "Compute governor registration failed!\n");
return ret;
}
int register_memlat(struct device *dev, struct memlat_hwmon *hw)
{
struct memlat_node *node;
int ret = 0;
node = register_common(dev, hw);
if (IS_ERR(node)) {
ret = PTR_ERR(node);
goto out;
}
mutex_lock(&state_lock);
node->gov = &devfreq_gov_memlat;
node->attr_grp = &memlat_dev_attr_group;
if (!memlat_use_cnt)
ret = devfreq_add_governor(&devfreq_gov_memlat);
if (!ret)
memlat_use_cnt++;
mutex_unlock(&state_lock);
out:
if (!ret)
dev_info(dev, "Memory Latency governor registered.\n");
else

View File

@ -74,10 +74,16 @@ struct memlat_hwmon {
#ifdef CONFIG_DEVFREQ_GOV_MEMLAT
int register_memlat(struct device *dev, struct memlat_hwmon *hw);
int register_compute(struct device *dev, struct memlat_hwmon *hw);
int update_memlat(struct memlat_hwmon *hw);
#else
static inline int register_memlat(struct device *dev,
struct memlat_hwmon *hw)
struct memlat_hwmon *hw)
{
return 0;
}
static inline int register_compute(struct device *dev,
struct memlat_hwmon *hw)
{
return 0;
}