Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bb3ac62b authored by Rohit Gupta's avatar Rohit Gupta
Browse files

PM / devfreq: memlat: Move per-CPU stats inside device nodes



Currently each memlat monitor stores details such as previous sample
timestamp and perf counts in per-CPU structures. This can result in
multiple monitors overwriting each others' data.
Move these stats within per device structures so that each of the
monitors have exclusive access to them.

Change-Id: I0a60e95eebd72f0e602cbf9cba05380324b75b3f
Signed-off-by: default avatarRohit Gupta <rohgup@codeaurora.org>
parent 492b05c7
Loading
Loading
Loading
Loading
+90 −73
Original line number Diff line number Diff line
@@ -47,22 +47,32 @@ struct event_data {
	unsigned long prev_count;
};

struct memlat_hwmon_data {
struct cpu_pmu_stats {
	struct event_data events[NUM_EVENTS];
	ktime_t prev_ts;
	bool init_pending;
	unsigned long cache_miss_event;
	unsigned long inst_event;
};
static DEFINE_PER_CPU(struct memlat_hwmon_data, pm_data);

struct cpu_grp_info {
	cpumask_t cpus;
	cpumask_t inited_cpus;
	unsigned long cache_miss_event;
	unsigned long inst_event;
	struct cpu_pmu_stats *cpustats;
	struct memlat_hwmon hw;
	struct notifier_block arm_memlat_cpu_notif;
	struct list_head mon_list;
};

static unsigned long compute_freq(struct memlat_hwmon_data *hw_data,
#define to_cpustats(cpu_grp, cpu) \
	(&cpu_grp->cpustats[cpu - cpumask_first(&cpu_grp->cpus)])
#define to_devstats(cpu_grp, cpu) \
	(&cpu_grp->hw.core_stats[cpu - cpumask_first(&cpu_grp->cpus)])
#define to_cpu_grp(hwmon) container_of(hwmon, struct cpu_grp_info, hw)

static LIST_HEAD(memlat_mon_list);
static DEFINE_MUTEX(list_lock);

static unsigned long compute_freq(struct cpu_pmu_stats *cpustats,
						unsigned long cyc_cnt)
{
	ktime_t ts;
@@ -70,10 +80,10 @@ static unsigned long compute_freq(struct memlat_hwmon_data *hw_data,
	unsigned long freq = 0;

	ts = ktime_get();
	diff = ktime_to_us(ktime_sub(ts, hw_data->prev_ts));
	diff = ktime_to_us(ktime_sub(ts, cpustats->prev_ts));
	if (!diff)
		diff = 1;
	hw_data->prev_ts = ts;
	cpustats->prev_ts = ts;
	freq = cyc_cnt;
	do_div(freq, diff);

@@ -99,69 +109,59 @@ static inline unsigned long read_event(struct event_data *event)

static void read_perf_counters(int cpu, struct cpu_grp_info *cpu_grp)
{
	int cpu_idx;
	struct memlat_hwmon_data *hw_data = &per_cpu(pm_data, cpu);
	struct memlat_hwmon *hw = &cpu_grp->hw;
	struct cpu_pmu_stats *cpustats = to_cpustats(cpu_grp, cpu);
	struct dev_stats *devstats = to_devstats(cpu_grp, cpu);
	unsigned long cyc_cnt;

	if (hw_data->init_pending)
		return;

	cpu_idx = cpu - cpumask_first(&cpu_grp->cpus);

	hw->core_stats[cpu_idx].inst_count =
			read_event(&hw_data->events[INST_IDX]);

	hw->core_stats[cpu_idx].mem_count =
			read_event(&hw_data->events[CM_IDX]);

	cyc_cnt = read_event(&hw_data->events[CYC_IDX]);
	hw->core_stats[cpu_idx].freq = compute_freq(hw_data, cyc_cnt);
	devstats->inst_count = read_event(&cpustats->events[INST_IDX]);
	devstats->mem_count = read_event(&cpustats->events[CM_IDX]);
	cyc_cnt = read_event(&cpustats->events[CYC_IDX]);
	devstats->freq = compute_freq(cpustats, cyc_cnt);
}

static unsigned long get_cnt(struct memlat_hwmon *hw)
{
	int cpu;
	struct cpu_grp_info *cpu_grp = container_of(hw,
					struct cpu_grp_info, hw);
	struct cpu_grp_info *cpu_grp = to_cpu_grp(hw);

	for_each_cpu(cpu, &cpu_grp->cpus)
	for_each_cpu(cpu, &cpu_grp->inited_cpus)
		read_perf_counters(cpu, cpu_grp);

	return 0;
}

static void delete_events(struct memlat_hwmon_data *hw_data)
static void delete_events(struct cpu_pmu_stats *cpustats)
{
	int i;

	for (i = 0; i < NUM_EVENTS; i++) {
		hw_data->events[i].prev_count = 0;
		perf_event_release_kernel(hw_data->events[i].pevent);
		cpustats->events[i].prev_count = 0;
		perf_event_release_kernel(cpustats->events[i].pevent);
	}
}

static void stop_hwmon(struct memlat_hwmon *hw)
{
	int cpu, idx;
	struct memlat_hwmon_data *hw_data;
	struct cpu_grp_info *cpu_grp = container_of(hw,
					struct cpu_grp_info, hw);
	int cpu;
	struct cpu_grp_info *cpu_grp = to_cpu_grp(hw);
	struct dev_stats *devstats;

	get_online_cpus();
	for_each_cpu(cpu, &cpu_grp->cpus) {
		hw_data = &per_cpu(pm_data, cpu);
		if (hw_data->init_pending)
			hw_data->init_pending = false;
		else
			delete_events(hw_data);
	for_each_cpu(cpu, &cpu_grp->inited_cpus) {
		delete_events(to_cpustats(cpu_grp, cpu));

		/* Clear governor data */
		idx = cpu - cpumask_first(&cpu_grp->cpus);
		hw->core_stats[idx].inst_count = 0;
		hw->core_stats[idx].mem_count = 0;
		hw->core_stats[idx].freq = 0;
		devstats = to_devstats(cpu_grp, cpu);
		devstats->inst_count = 0;
		devstats->mem_count = 0;
		devstats->freq = 0;
	}
	mutex_lock(&list_lock);
	if (!cpumask_equal(&cpu_grp->cpus, &cpu_grp->inited_cpus))
		list_del(&cpu_grp->mon_list);
	mutex_unlock(&list_lock);
	cpumask_clear(&cpu_grp->inited_cpus);

	put_online_cpus();

	unregister_cpu_notifier(&cpu_grp->arm_memlat_cpu_notif);
@@ -173,7 +173,7 @@ static struct perf_event_attr *alloc_attr(void)

	attr = kzalloc(sizeof(struct perf_event_attr), GFP_KERNEL);
	if (!attr)
		return ERR_PTR(-ENOMEM);
		return attr;

	attr->type = PERF_TYPE_RAW;
	attr->size = sizeof(struct perf_event_attr);
@@ -183,37 +183,38 @@ static struct perf_event_attr *alloc_attr(void)
	return attr;
}

static int set_events(struct memlat_hwmon_data *hw_data, int cpu)
static int set_events(struct cpu_grp_info *cpu_grp, int cpu)
{
	struct perf_event *pevent;
	struct perf_event_attr *attr;
	int err;
	struct cpu_pmu_stats *cpustats = to_cpustats(cpu_grp, cpu);

	/* Allocate an attribute for event initialization */
	attr = alloc_attr();
	if (IS_ERR(attr))
		return PTR_ERR(attr);
	if (!attr)
		return -ENOMEM;

	attr->config = hw_data->inst_event;
	attr->config = cpu_grp->inst_event;
	pevent = perf_event_create_kernel_counter(attr, cpu, NULL, NULL, NULL);
	if (IS_ERR(pevent))
		goto err_out;
	hw_data->events[INST_IDX].pevent = pevent;
	perf_event_enable(hw_data->events[INST_IDX].pevent);
	cpustats->events[INST_IDX].pevent = pevent;
	perf_event_enable(cpustats->events[INST_IDX].pevent);

	attr->config = hw_data->cache_miss_event;
	attr->config = cpu_grp->cache_miss_event;
	pevent = perf_event_create_kernel_counter(attr, cpu, NULL, NULL, NULL);
	if (IS_ERR(pevent))
		goto err_out;
	hw_data->events[CM_IDX].pevent = pevent;
	perf_event_enable(hw_data->events[CM_IDX].pevent);
	cpustats->events[CM_IDX].pevent = pevent;
	perf_event_enable(cpustats->events[CM_IDX].pevent);

	attr->config = CYC_EV;
	pevent = perf_event_create_kernel_counter(attr, cpu, NULL, NULL, NULL);
	if (IS_ERR(pevent))
		goto err_out;
	hw_data->events[CYC_IDX].pevent = pevent;
	perf_event_enable(hw_data->events[CYC_IDX].pevent);
	cpustats->events[CYC_IDX].pevent = pevent;
	perf_event_enable(cpustats->events[CYC_IDX].pevent);

	kfree(attr);
	return 0;
@@ -228,15 +229,24 @@ static int arm_memlat_cpu_callback(struct notifier_block *nb,
		unsigned long action, void *hcpu)
{
	unsigned long cpu = (unsigned long)hcpu;
	struct memlat_hwmon_data *hw_data = &per_cpu(pm_data, cpu);
	struct cpu_grp_info *cpu_grp, *tmp;

	if ((action != CPU_ONLINE) || !hw_data->init_pending)
	if (action != CPU_ONLINE)
		return NOTIFY_OK;

	if (set_events(hw_data, cpu))
		pr_warn("Failed to create perf event for CPU%lu\n", cpu);

	hw_data->init_pending = false;
	mutex_lock(&list_lock);
	list_for_each_entry_safe(cpu_grp, tmp, &memlat_mon_list, mon_list) {
		if (!cpumask_test_cpu(cpu, &cpu_grp->cpus) ||
		    cpumask_test_cpu(cpu, &cpu_grp->inited_cpus))
			continue;
		if (set_events(cpu_grp, cpu))
			pr_warn("Failed to create perf ev for CPU%lu\n", cpu);
		else
			cpumask_set_cpu(cpu, &cpu_grp->inited_cpus);
		if (cpumask_equal(&cpu_grp->cpus, &cpu_grp->inited_cpus))
			list_del(&cpu_grp->mon_list);
	}
	mutex_unlock(&list_lock);

	return NOTIFY_OK;
}
@@ -244,29 +254,32 @@ static int arm_memlat_cpu_callback(struct notifier_block *nb,
static int start_hwmon(struct memlat_hwmon *hw)
{
	int cpu, ret = 0;
	struct memlat_hwmon_data *hw_data;
	struct cpu_grp_info *cpu_grp = container_of(hw,
					struct cpu_grp_info, hw);
	struct cpu_grp_info *cpu_grp = to_cpu_grp(hw);

	register_cpu_notifier(&cpu_grp->arm_memlat_cpu_notif);

	get_online_cpus();
	for_each_cpu(cpu, &cpu_grp->cpus) {
		hw_data = &per_cpu(pm_data, cpu);
		ret = set_events(hw_data, cpu);
		ret = set_events(cpu_grp, cpu);
		if (ret) {
			if (!cpu_online(cpu)) {
				hw_data->init_pending = true;
				ret = 0;
			} else {
				pr_warn("Perf event init failed on CPU%d\n",
					cpu);
				break;
			}
		} else {
			cpumask_set_cpu(cpu, &cpu_grp->inited_cpus);
		}
	}
	mutex_lock(&list_lock);
	if (!cpumask_equal(&cpu_grp->cpus, &cpu_grp->inited_cpus))
		list_add_tail(&cpu_grp->mon_list, &memlat_mon_list);
	mutex_unlock(&list_lock);

	put_online_cpus();

	return ret;
}

@@ -328,6 +341,11 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev)
	if (!hw->core_stats)
		return -ENOMEM;

	cpu_grp->cpustats = devm_kzalloc(dev, hw->num_cores *
			sizeof(*(cpu_grp->cpustats)), GFP_KERNEL);
	if (!cpu_grp->cpustats)
		return -ENOMEM;

	ret = of_property_read_u32(dev->of_node, "qcom,cachemiss-ev",
			&cachemiss_ev);
	if (ret) {
@@ -335,6 +353,7 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev)
				L2DM_EV);
		cachemiss_ev = L2DM_EV;
	}
	cpu_grp->cache_miss_event = cachemiss_ev;

	ret = of_property_read_u32(dev->of_node, "qcom,inst-ev", &inst_ev);
	if (ret) {
@@ -342,12 +361,10 @@ static int arm_memlat_mon_driver_probe(struct platform_device *pdev)
				INST_EV);
		inst_ev = INST_EV;
	}
	cpu_grp->inst_event = inst_ev;

	for_each_cpu(cpu, &cpu_grp->cpus) {
		hw->core_stats[cpu - cpumask_first(&cpu_grp->cpus)].id = cpu;
		(&per_cpu(pm_data, cpu))->cache_miss_event = cachemiss_ev;
		(&per_cpu(pm_data, cpu))->inst_event = inst_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;