xref: /linux/arch/powerpc/perf/vpa-pmu.c (revision 60675d4ca1ef0857e44eba5849b74a3a998d0c0f)
1176cda06SKajol Jain // SPDX-License-Identifier: GPL-2.0-or-later
2176cda06SKajol Jain /*
3176cda06SKajol Jain  * Performance monitoring support for Virtual Processor Area(VPA) based counters
4176cda06SKajol Jain  *
5176cda06SKajol Jain  * Copyright (C) 2024 IBM Corporation
6176cda06SKajol Jain  */
7176cda06SKajol Jain #define pr_fmt(fmt) "vpa_pmu: " fmt
8176cda06SKajol Jain 
9176cda06SKajol Jain #include <linux/module.h>
10176cda06SKajol Jain #include <linux/perf_event.h>
11176cda06SKajol Jain #include <asm/kvm_ppc.h>
12176cda06SKajol Jain #include <asm/kvm_book3s_64.h>
13176cda06SKajol Jain 
14176cda06SKajol Jain #define MODULE_VERS "1.0"
15176cda06SKajol Jain #define MODULE_NAME "pseries_vpa_pmu"
16176cda06SKajol Jain 
17176cda06SKajol Jain #define EVENT(_name, _code)     enum{_name = _code}
18176cda06SKajol Jain 
19176cda06SKajol Jain #define VPA_PMU_EVENT_VAR(_id)  event_attr_##_id
20176cda06SKajol Jain #define VPA_PMU_EVENT_PTR(_id)  (&event_attr_##_id.attr.attr)
21176cda06SKajol Jain 
22176cda06SKajol Jain static ssize_t vpa_pmu_events_sysfs_show(struct device *dev,
23176cda06SKajol Jain 					 struct device_attribute *attr, char *page)
24176cda06SKajol Jain {
25176cda06SKajol Jain 	struct perf_pmu_events_attr *pmu_attr;
26176cda06SKajol Jain 
27176cda06SKajol Jain 	pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr);
28176cda06SKajol Jain 
29176cda06SKajol Jain 	return sprintf(page, "event=0x%02llx\n", pmu_attr->id);
30176cda06SKajol Jain }
31176cda06SKajol Jain 
32176cda06SKajol Jain #define VPA_PMU_EVENT_ATTR(_name, _id)				\
33176cda06SKajol Jain 	PMU_EVENT_ATTR(_name, VPA_PMU_EVENT_VAR(_id), _id,	\
34176cda06SKajol Jain 			vpa_pmu_events_sysfs_show)
35176cda06SKajol Jain 
36176cda06SKajol Jain EVENT(L1_TO_L2_CS_LAT,	0x1);
37176cda06SKajol Jain EVENT(L2_TO_L1_CS_LAT,	0x2);
38176cda06SKajol Jain EVENT(L2_RUNTIME_AGG,	0x3);
39176cda06SKajol Jain 
40176cda06SKajol Jain VPA_PMU_EVENT_ATTR(l1_to_l2_lat,  L1_TO_L2_CS_LAT);
41176cda06SKajol Jain VPA_PMU_EVENT_ATTR(l2_to_l1_lat,  L2_TO_L1_CS_LAT);
42176cda06SKajol Jain VPA_PMU_EVENT_ATTR(l2_runtime_agg, L2_RUNTIME_AGG);
43176cda06SKajol Jain 
44176cda06SKajol Jain static struct attribute *vpa_pmu_events_attr[] = {
45176cda06SKajol Jain 	VPA_PMU_EVENT_PTR(L1_TO_L2_CS_LAT),
46176cda06SKajol Jain 	VPA_PMU_EVENT_PTR(L2_TO_L1_CS_LAT),
47176cda06SKajol Jain 	VPA_PMU_EVENT_PTR(L2_RUNTIME_AGG),
48176cda06SKajol Jain 	NULL
49176cda06SKajol Jain };
50176cda06SKajol Jain 
51176cda06SKajol Jain static const struct attribute_group vpa_pmu_events_group = {
52176cda06SKajol Jain 	.name = "events",
53176cda06SKajol Jain 	.attrs = vpa_pmu_events_attr,
54176cda06SKajol Jain };
55176cda06SKajol Jain 
56176cda06SKajol Jain PMU_FORMAT_ATTR(event, "config:0-31");
57176cda06SKajol Jain static struct attribute *vpa_pmu_format_attr[] = {
58176cda06SKajol Jain 	&format_attr_event.attr,
59176cda06SKajol Jain 	NULL,
60176cda06SKajol Jain };
61176cda06SKajol Jain 
62176cda06SKajol Jain static struct attribute_group vpa_pmu_format_group = {
63176cda06SKajol Jain 	.name = "format",
64176cda06SKajol Jain 	.attrs = vpa_pmu_format_attr,
65176cda06SKajol Jain };
66176cda06SKajol Jain 
67176cda06SKajol Jain static const struct attribute_group *vpa_pmu_attr_groups[] = {
68176cda06SKajol Jain 	&vpa_pmu_events_group,
69176cda06SKajol Jain 	&vpa_pmu_format_group,
70176cda06SKajol Jain 	NULL
71176cda06SKajol Jain };
72176cda06SKajol Jain 
73176cda06SKajol Jain static int vpa_pmu_event_init(struct perf_event *event)
74176cda06SKajol Jain {
75176cda06SKajol Jain 	if (event->attr.type != event->pmu->type)
76176cda06SKajol Jain 		return -ENOENT;
77176cda06SKajol Jain 
78176cda06SKajol Jain 	/* it does not support event sampling mode */
79176cda06SKajol Jain 	if (is_sampling_event(event))
80176cda06SKajol Jain 		return -EOPNOTSUPP;
81176cda06SKajol Jain 
82176cda06SKajol Jain 	/* no branch sampling */
83176cda06SKajol Jain 	if (has_branch_stack(event))
84176cda06SKajol Jain 		return -EOPNOTSUPP;
85176cda06SKajol Jain 
86176cda06SKajol Jain 	/* Invalid event code */
87176cda06SKajol Jain 	if ((event->attr.config <= 0) || (event->attr.config > 3))
88176cda06SKajol Jain 		return -EINVAL;
89176cda06SKajol Jain 
90176cda06SKajol Jain 	return 0;
91176cda06SKajol Jain }
92176cda06SKajol Jain 
93176cda06SKajol Jain static unsigned long get_counter_data(struct perf_event *event)
94176cda06SKajol Jain {
95176cda06SKajol Jain 	unsigned int config = event->attr.config;
96176cda06SKajol Jain 	u64 data;
97176cda06SKajol Jain 
98176cda06SKajol Jain 	switch (config) {
99176cda06SKajol Jain 	case L1_TO_L2_CS_LAT:
100*f26f9933SKajol Jain 		if (event->attach_state & PERF_ATTACH_TASK)
101*f26f9933SKajol Jain 			data = kvmhv_get_l1_to_l2_cs_time_vcpu();
102*f26f9933SKajol Jain 		else
103176cda06SKajol Jain 			data = kvmhv_get_l1_to_l2_cs_time();
104176cda06SKajol Jain 		break;
105176cda06SKajol Jain 	case L2_TO_L1_CS_LAT:
106*f26f9933SKajol Jain 		if (event->attach_state & PERF_ATTACH_TASK)
107*f26f9933SKajol Jain 			data = kvmhv_get_l2_to_l1_cs_time_vcpu();
108*f26f9933SKajol Jain 		else
109176cda06SKajol Jain 			data = kvmhv_get_l2_to_l1_cs_time();
110176cda06SKajol Jain 		break;
111176cda06SKajol Jain 	case L2_RUNTIME_AGG:
112*f26f9933SKajol Jain 		if (event->attach_state & PERF_ATTACH_TASK)
113*f26f9933SKajol Jain 			data = kvmhv_get_l2_runtime_agg_vcpu();
114*f26f9933SKajol Jain 		else
115176cda06SKajol Jain 			data = kvmhv_get_l2_runtime_agg();
116176cda06SKajol Jain 		break;
117176cda06SKajol Jain 	default:
118176cda06SKajol Jain 		data = 0;
119176cda06SKajol Jain 		break;
120176cda06SKajol Jain 	}
121176cda06SKajol Jain 
122176cda06SKajol Jain 	return data;
123176cda06SKajol Jain }
124176cda06SKajol Jain 
125176cda06SKajol Jain static int vpa_pmu_add(struct perf_event *event, int flags)
126176cda06SKajol Jain {
127176cda06SKajol Jain 	u64 data;
128176cda06SKajol Jain 
129176cda06SKajol Jain 	kvmhv_set_l2_counters_status(smp_processor_id(), true);
130176cda06SKajol Jain 
131176cda06SKajol Jain 	data = get_counter_data(event);
132176cda06SKajol Jain 	local64_set(&event->hw.prev_count, data);
133176cda06SKajol Jain 
134176cda06SKajol Jain 	return 0;
135176cda06SKajol Jain }
136176cda06SKajol Jain 
137176cda06SKajol Jain static void vpa_pmu_read(struct perf_event *event)
138176cda06SKajol Jain {
139176cda06SKajol Jain 	u64 prev_data, new_data, final_data;
140176cda06SKajol Jain 
141176cda06SKajol Jain 	prev_data = local64_read(&event->hw.prev_count);
142176cda06SKajol Jain 	new_data = get_counter_data(event);
143176cda06SKajol Jain 	final_data = new_data - prev_data;
144176cda06SKajol Jain 
145176cda06SKajol Jain 	local64_add(final_data, &event->count);
146176cda06SKajol Jain }
147176cda06SKajol Jain 
148176cda06SKajol Jain static void vpa_pmu_del(struct perf_event *event, int flags)
149176cda06SKajol Jain {
150176cda06SKajol Jain 	vpa_pmu_read(event);
151176cda06SKajol Jain 
152176cda06SKajol Jain 	/*
153176cda06SKajol Jain 	 * Disable vpa counter accumulation
154176cda06SKajol Jain 	 */
155176cda06SKajol Jain 	kvmhv_set_l2_counters_status(smp_processor_id(), false);
156176cda06SKajol Jain }
157176cda06SKajol Jain 
158176cda06SKajol Jain static struct pmu vpa_pmu = {
159176cda06SKajol Jain 	.task_ctx_nr	= perf_sw_context,
160176cda06SKajol Jain 	.name		= "vpa_pmu",
161176cda06SKajol Jain 	.event_init	= vpa_pmu_event_init,
162176cda06SKajol Jain 	.add		= vpa_pmu_add,
163176cda06SKajol Jain 	.del		= vpa_pmu_del,
164176cda06SKajol Jain 	.read		= vpa_pmu_read,
165176cda06SKajol Jain 	.attr_groups	= vpa_pmu_attr_groups,
166176cda06SKajol Jain 	.capabilities	= PERF_PMU_CAP_NO_EXCLUDE | PERF_PMU_CAP_NO_INTERRUPT,
167176cda06SKajol Jain };
168176cda06SKajol Jain 
169176cda06SKajol Jain static int __init pseries_vpa_pmu_init(void)
170176cda06SKajol Jain {
171176cda06SKajol Jain 	/*
172176cda06SKajol Jain 	 * List of current Linux on Power platforms and
173176cda06SKajol Jain 	 * this driver is supported only in PowerVM LPAR
174176cda06SKajol Jain 	 * (L1) platform.
175176cda06SKajol Jain 	 *
176176cda06SKajol Jain 	 *	Enabled    Linux on Power Platforms
177176cda06SKajol Jain 	 *      ----------------------------------------
178176cda06SKajol Jain 	 *        [X]      PowerVM LPAR (L1)
179176cda06SKajol Jain 	 *        [ ]      KVM Guest On PowerVM KoP(L2)
180176cda06SKajol Jain 	 *        [ ]      Baremetal(PowerNV)
181176cda06SKajol Jain 	 *        [ ]      KVM Guest On PowerNV
182176cda06SKajol Jain 	 */
183176cda06SKajol Jain 	if (!firmware_has_feature(FW_FEATURE_LPAR) || is_kvm_guest())
184176cda06SKajol Jain 		return -ENODEV;
185176cda06SKajol Jain 
186176cda06SKajol Jain 	perf_pmu_register(&vpa_pmu, vpa_pmu.name, -1);
187176cda06SKajol Jain 	pr_info("Virtual Processor Area PMU registered.\n");
188176cda06SKajol Jain 
189176cda06SKajol Jain 	return 0;
190176cda06SKajol Jain }
191176cda06SKajol Jain 
192176cda06SKajol Jain static void __exit pseries_vpa_pmu_cleanup(void)
193176cda06SKajol Jain {
194176cda06SKajol Jain 	perf_pmu_unregister(&vpa_pmu);
195176cda06SKajol Jain 	pr_info("Virtual Processor Area PMU unregistered.\n");
196176cda06SKajol Jain }
197176cda06SKajol Jain 
198176cda06SKajol Jain module_init(pseries_vpa_pmu_init);
199176cda06SKajol Jain module_exit(pseries_vpa_pmu_cleanup);
200176cda06SKajol Jain MODULE_DESCRIPTION("Perf Driver for pSeries VPA pmu counter");
201176cda06SKajol Jain MODULE_AUTHOR("Kajol Jain <kjain@linux.ibm.com>");
202176cda06SKajol Jain MODULE_AUTHOR("Madhavan Srinivasan <maddy@linux.ibm.com>");
203176cda06SKajol Jain MODULE_LICENSE("GPL");
204