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

Commit b362348e authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm-perf: Added support for Perf data collection"

parents e3a8eb77 ee81f186
Loading
Loading
Loading
Loading
+281 −5
Original line number Diff line number Diff line
@@ -20,9 +20,11 @@
#include <linux/sched/core_ctl.h>
#include <soc/qcom/msm_performance.h>
#include <linux/spinlock.h>
#include <linux/jiffies.h>
#include <linux/circ_buf.h>
#include <linux/ktime.h>
#include <linux/perf_event.h>
#include <linux/errno.h>
#include <linux/topology.h>

/*
 * Sched will provide the data for every 20ms window,
@@ -32,6 +34,16 @@
#define POLL_INT 25
#define NODE_NAME_MAX_CHARS 16
#define QUEUE_POOL_SIZE 512 /*2^8 always keep in 2^x */
#define INST_EV 0x08 /* 0th event*/
#define CYC_EV 0x11 /* 1st event*/
static DEFINE_PER_CPU(bool, cpu_is_idle);
static DEFINE_PER_CPU(bool, cpu_is_hp);

enum event_idx {
	INST_EVENT,
	CYC_EVENT,
	NO_OF_EVENT
};

enum cpu_clusters {
	MIN = 0,
@@ -68,6 +80,13 @@ static struct queue_indicies curr_pos;

static DEFINE_SPINLOCK(gfx_circ_buff_lock);

static struct event_data {
	struct perf_event *pevent;
	u64 prev_count;
	u64 cur_delta;
	u64 cached_total_count;
} pmu_events[NO_OF_EVENT][NR_CPUS];

struct events {
	spinlock_t cpu_hotplug_lock;
	bool cpu_hotplug;
@@ -80,6 +99,7 @@ static unsigned int aggr_big_nr;
static unsigned int aggr_top_load;
static unsigned int top_load[CLUSTER_MAX];
static unsigned int curr_cap[CLUSTER_MAX];
static bool max_cap_cpus[NR_CPUS];

static cpumask_var_t limit_mask_min;
static cpumask_var_t limit_mask_max;
@@ -441,13 +461,221 @@ static struct attribute_group notify_attr_group = {
static struct kobject *notify_kobj;

/*******************************sysfs ends************************************/
static int hotplug_notify(unsigned int cpu)

/*****************PMU Data Collection*****************/
static struct perf_event_attr *msm_perf_alloc_attr(void)
{
	struct perf_event_attr *attr = NULL;

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

	attr->type = PERF_TYPE_RAW;
	attr->size = sizeof(struct perf_event_attr);
	attr->pinned = 1;

	return attr;
}

static int set_event(struct event_data *ev, int cpu,
					 struct perf_event_attr *attr)
{
	struct perf_event *pevent;

	pevent = perf_event_create_kernel_counter(attr,
				cpu, NULL, NULL, NULL);
	if (IS_ERR(pevent)) {
		pr_err("msm_perf: %s failed, eventId:0x%x, cpu:%d, error code:%ld\n",
				__func__, attr->config, cpu, PTR_ERR(pevent));
		return PTR_ERR(pevent);
	}
	ev->pevent = pevent;
	perf_event_enable(pevent);

	return 0;
}

static void free_pmu_counters(unsigned int cpu)
{
	int i = 0;

	for (i = 0; i < NO_OF_EVENT; i++) {
		pmu_events[i][cpu].prev_count = 0;
		pmu_events[i][cpu].cur_delta = 0;
		pmu_events[i][cpu].cached_total_count = 0;
		if (pmu_events[i][cpu].pevent) {
			perf_event_disable(pmu_events[i][cpu].pevent);
			perf_event_release_kernel(pmu_events[i][cpu].pevent);
			pmu_events[i][cpu].pevent = NULL;
		}
	}
}

static int init_pmu_counter(void)
{
	struct perf_event_attr *attr = msm_perf_alloc_attr();
	int cpu;
	unsigned long cpu_capacity[NR_CPUS];
	unsigned long min_cpu_capacity = ULONG_MAX;
	int ret = 0;

	if (!attr)
		return -ENOMEM;

	/* Create events per CPU */
	for_each_possible_cpu(cpu) {
		/* create Instruction event */
		attr->config = INST_EV;
		ret = set_event(&pmu_events[INST_EVENT][cpu], cpu, attr);
		if (ret < 0) {
			kfree(attr);
			return ret;
		}
		/* create cycle event */
		attr->config = CYC_EV;
		ret = set_event(&pmu_events[CYC_EVENT][cpu], cpu, attr);
		if (ret < 0) {
			free_pmu_counters(cpu);
			kfree(attr);
			return ret;
		}
		/* find capacity per cpu */
		cpu_capacity[cpu] = arch_scale_cpu_capacity(cpu);
		if (cpu_capacity[cpu] < min_cpu_capacity)
			min_cpu_capacity = cpu_capacity[cpu];
	}

	/* determine cpu index for maximum capacity cpus */
	for_each_possible_cpu(cpu) {
		if (cpu_capacity[cpu] > min_cpu_capacity)
			max_cap_cpus[cpu] = true;
	}

	kfree(attr);
	return 0;
}

static inline void msm_perf_read_event(struct event_data *event)
{
	u64 ev_count = 0;
	u64 total, enabled, running;

	if (!event->pevent)
		return;

	if (!per_cpu(cpu_is_idle, event->pevent->cpu) &&
				!per_cpu(cpu_is_hp, event->pevent->cpu))
		total = perf_event_read_value(event->pevent, &enabled, &running);
	else
		total = event->cached_total_count;

	ev_count = total - event->prev_count;
	event->prev_count = total;
	event->cur_delta = ev_count;

}

static int get_cpu_total_instruction(char *buf, const struct kernel_param *kp)
{
	u64 instruction = 0;
	u64 cycles = 0;
	u64 total_inst_big = 0;
	u64 total_inst_little = 0;
	u64 ipc_big = 0;
	u64 ipc_little = 0;
	int cnt = 0, cpu;

	for_each_possible_cpu(cpu) {
		/* Read Instruction event */
		msm_perf_read_event(&pmu_events[INST_EVENT][cpu]);
		/* Read Cycle event */
		msm_perf_read_event(&pmu_events[CYC_EVENT][cpu]);
		instruction = pmu_events[INST_EVENT][cpu].cur_delta;
		cycles = pmu_events[CYC_EVENT][cpu].cur_delta;
		/* collecting max inst and ipc for max cap and min cap cpus */
		if (max_cap_cpus[cpu]) {
			if (cycles)
				ipc_big = max(ipc_big,
						((instruction*100)/cycles));
			total_inst_big += instruction;
		} else {
			if (cycles)
				ipc_little = max(ipc_little,
						((instruction*100)/cycles));
			total_inst_little += instruction;
		}
	}

	cnt += scnprintf(buf, PAGE_SIZE, "%llu:%llu:%llu:%llu\n",
			total_inst_big, ipc_big,
			total_inst_little, ipc_little);

	return cnt;
}

static const struct kernel_param_ops param_ops_cpu_total_instruction = {
	.set = NULL,
	.get = get_cpu_total_instruction,
};
module_param_cb(inst, &param_ops_cpu_total_instruction, NULL, 0444);


static int restart_events(unsigned int cpu, bool cpu_up)
{
	struct perf_event_attr *attr = msm_perf_alloc_attr();
	int ret = 0;

	if (!attr)
		return -ENOMEM;

	if (cpu_up) {
		/* create Instruction event */
		attr->config = INST_EV;
		ret = set_event(&pmu_events[INST_EVENT][cpu], cpu, attr);
		if (ret < 0) {
			kfree(attr);
			return ret;
		}
		/* create cycle event */
		attr->config = CYC_EV;
		ret = set_event(&pmu_events[CYC_EVENT][cpu], cpu, attr);
		if (ret < 0) {
			free_pmu_counters(cpu);
			kfree(attr);
			return ret;
		}
	} else {
		free_pmu_counters(cpu);
	}

	kfree(attr);
	return 0;
}

static int hotplug_notify_down(unsigned int cpu)
{
	unsigned long flags;

	if (events_group.init_success) {
		spin_lock_irqsave(&(events_group.cpu_hotplug_lock), flags);
		per_cpu(cpu_is_hp, cpu) = true;
		restart_events(cpu, false);
		spin_unlock_irqrestore(&(events_group.cpu_hotplug_lock), flags);
	}

	return 0;
}

static int hotplug_notify_up(unsigned int cpu)
{
	unsigned long flags;

	if (events_group.init_success) {
		spin_lock_irqsave(&(events_group.cpu_hotplug_lock), flags);
		events_group.cpu_hotplug = true;
		restart_events(cpu, true);
		per_cpu(cpu_is_hp, cpu) = false;
		spin_unlock_irqrestore(&(events_group.cpu_hotplug_lock), flags);
		wake_up_process(events_notify_thread);
	}
@@ -455,6 +683,42 @@ static int hotplug_notify(unsigned int cpu)
	return 0;
}

static int msm_perf_idle_read_events(unsigned int cpu)
{
	int ret = 0, i;

	for (i = 0; i < NO_OF_EVENT; i++) {
		if (pmu_events[i][cpu].pevent)
			ret = perf_event_read_local(pmu_events[i][cpu].pevent,
					&pmu_events[i][cpu].cached_total_count, NULL, NULL);
	}

	return ret;
}

static int msm_perf_idle_notif(struct notifier_block *nb, unsigned long action,
							void *data)
{
	int ret = NOTIFY_OK;
	int cpu = smp_processor_id();

	switch (action) {
	case IDLE_START:
		__this_cpu_write(cpu_is_idle, true);
		if (!per_cpu(cpu_is_hp, cpu))
			ret = msm_perf_idle_read_events(cpu);
		break;
	case IDLE_END:
		__this_cpu_write(cpu_is_idle, false);
		break;
	}
	return NOTIFY_OK;
}

static struct notifier_block msm_perf_event_idle_nb = {
	.notifier_call = msm_perf_idle_notif,
};

static int events_notify_userspace(void *data)
{
	unsigned long flags;
@@ -714,7 +978,9 @@ module_param_cb(splh_notif, &param_ops_splh_notification, &splh_notif, 0644);

static int __init msm_performance_init(void)
{
	unsigned int cpu;
	int ret;

	if (!alloc_cpumask_var(&limit_mask_min, GFP_KERNEL))
		return -ENOMEM;

@@ -723,14 +989,24 @@ static int __init msm_performance_init(void)
		return -ENOMEM;
	}

	ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE,
	get_online_cpus();
	for_each_possible_cpu(cpu) {
		if (!cpumask_test_cpu(cpu, cpu_online_mask))
			per_cpu(cpu_is_hp, cpu) = true;
	}

	ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
		"msm_performance_cpu_hotplug",
		hotplug_notify,
		NULL);
		hotplug_notify_up,
		hotplug_notify_down);

	put_online_cpus();

	init_events_group();
	init_notify_group();
	init_pmu_counter();

	idle_notifier_register(&msm_perf_event_idle_nb);
	return 0;
}
late_initcall(msm_performance_init);