File Index
Files and directories
Sign in
FILE

vpa-pmu.c

File preview with syntax highlighting.

linux-6.19.14/arch/powerpc/perf/vpa-pmu.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Performance monitoring support for Virtual Processor Area(VPA) based counters
  4 *
  5 * Copyright (C) 2024 IBM Corporation
  6 */
  7#define pr_fmt(fmt) "vpa_pmu: " fmt
  8
  9#include <linux/module.h>
 10#include <linux/perf_event.h>
 11#include <asm/kvm_ppc.h>
 12#include <asm/kvm_book3s_64.h>
 13
 14#define MODULE_VERS "1.0"
 15#define MODULE_NAME "pseries_vpa_pmu"
 16
 17#define EVENT(_name, _code)     enum{_name = _code}
 18
 19#define VPA_PMU_EVENT_VAR(_id)  event_attr_##_id
 20#define VPA_PMU_EVENT_PTR(_id)  (&event_attr_##_id.attr.attr)
 21
 22static ssize_t vpa_pmu_events_sysfs_show(struct device *dev,
 23					 struct device_attribute *attr, char *page)
 24{
 25	struct perf_pmu_events_attr *pmu_attr;
 26
 27	pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr);
 28
 29	return sprintf(page, "event=0x%02llx\n", pmu_attr->id);
 30}
 31
 32#define VPA_PMU_EVENT_ATTR(_name, _id)				\
 33	PMU_EVENT_ATTR(_name, VPA_PMU_EVENT_VAR(_id), _id,	\
 34			vpa_pmu_events_sysfs_show)
 35
 36EVENT(L1_TO_L2_CS_LAT,	0x1);
 37EVENT(L2_TO_L1_CS_LAT,	0x2);
 38EVENT(L2_RUNTIME_AGG,	0x3);
 39
 40VPA_PMU_EVENT_ATTR(l1_to_l2_lat,  L1_TO_L2_CS_LAT);
 41VPA_PMU_EVENT_ATTR(l2_to_l1_lat,  L2_TO_L1_CS_LAT);
 42VPA_PMU_EVENT_ATTR(l2_runtime_agg, L2_RUNTIME_AGG);
 43
 44static struct attribute *vpa_pmu_events_attr[] = {
 45	VPA_PMU_EVENT_PTR(L1_TO_L2_CS_LAT),
 46	VPA_PMU_EVENT_PTR(L2_TO_L1_CS_LAT),
 47	VPA_PMU_EVENT_PTR(L2_RUNTIME_AGG),
 48	NULL
 49};
 50
 51static const struct attribute_group vpa_pmu_events_group = {
 52	.name = "events",
 53	.attrs = vpa_pmu_events_attr,
 54};
 55
 56PMU_FORMAT_ATTR(event, "config:0-31");
 57static struct attribute *vpa_pmu_format_attr[] = {
 58	&format_attr_event.attr,
 59	NULL,
 60};
 61
 62static struct attribute_group vpa_pmu_format_group = {
 63	.name = "format",
 64	.attrs = vpa_pmu_format_attr,
 65};
 66
 67static const struct attribute_group *vpa_pmu_attr_groups[] = {
 68	&vpa_pmu_events_group,
 69	&vpa_pmu_format_group,
 70	NULL
 71};
 72
 73static int vpa_pmu_event_init(struct perf_event *event)
 74{
 75	if (event->attr.type != event->pmu->type)
 76		return -ENOENT;
 77
 78	/* it does not support event sampling mode */
 79	if (is_sampling_event(event))
 80		return -EOPNOTSUPP;
 81
 82	/* no branch sampling */
 83	if (has_branch_stack(event))
 84		return -EOPNOTSUPP;
 85
 86	/* Invalid event code */
 87	if ((event->attr.config <= 0) || (event->attr.config > 3))
 88		return -EINVAL;
 89
 90	return 0;
 91}
 92
 93static unsigned long get_counter_data(struct perf_event *event)
 94{
 95	unsigned int config = event->attr.config;
 96	u64 data;
 97
 98	switch (config) {
 99	case L1_TO_L2_CS_LAT:
100		if (event->attach_state & PERF_ATTACH_TASK)
101			data = kvmhv_get_l1_to_l2_cs_time_vcpu();
102		else
103			data = kvmhv_get_l1_to_l2_cs_time();
104		break;
105	case L2_TO_L1_CS_LAT:
106		if (event->attach_state & PERF_ATTACH_TASK)
107			data = kvmhv_get_l2_to_l1_cs_time_vcpu();
108		else
109			data = kvmhv_get_l2_to_l1_cs_time();
110		break;
111	case L2_RUNTIME_AGG:
112		if (event->attach_state & PERF_ATTACH_TASK)
113			data = kvmhv_get_l2_runtime_agg_vcpu();
114		else
115			data = kvmhv_get_l2_runtime_agg();
116		break;
117	default:
118		data = 0;
119		break;
120	}
121
122	return data;
123}
124
125static int vpa_pmu_add(struct perf_event *event, int flags)
126{
127	u64 data;
128
129	kvmhv_set_l2_counters_status(smp_processor_id(), true);
130
131	data = get_counter_data(event);
132	local64_set(&event->hw.prev_count, data);
133
134	return 0;
135}
136
137static void vpa_pmu_read(struct perf_event *event)
138{
139	u64 prev_data, new_data, final_data;
140
141	prev_data = local64_read(&event->hw.prev_count);
142	new_data = get_counter_data(event);
143	final_data = new_data - prev_data;
144
145	local64_add(final_data, &event->count);
146}
147
148static void vpa_pmu_del(struct perf_event *event, int flags)
149{
150	vpa_pmu_read(event);
151
152	/*
153	 * Disable vpa counter accumulation
154	 */
155	kvmhv_set_l2_counters_status(smp_processor_id(), false);
156}
157
158static struct pmu vpa_pmu = {
159	.module		= THIS_MODULE,
160	.task_ctx_nr	= perf_sw_context,
161	.name		= "vpa_pmu",
162	.event_init	= vpa_pmu_event_init,
163	.add		= vpa_pmu_add,
164	.del		= vpa_pmu_del,
165	.read		= vpa_pmu_read,
166	.attr_groups	= vpa_pmu_attr_groups,
167	.capabilities	= PERF_PMU_CAP_NO_EXCLUDE | PERF_PMU_CAP_NO_INTERRUPT,
168};
169
170static int __init pseries_vpa_pmu_init(void)
171{
172	/*
173	 * List of current Linux on Power platforms and
174	 * this driver is supported only in PowerVM LPAR
175	 * (L1) platform.
176	 *
177	 *	Enabled    Linux on Power Platforms
178	 *      ----------------------------------------
179	 *        [X]      PowerVM LPAR (L1)
180	 *        [ ]      KVM Guest On PowerVM KoP(L2)
181	 *        [ ]      Baremetal(PowerNV)
182	 *        [ ]      KVM Guest On PowerNV
183	 */
184	if (!firmware_has_feature(FW_FEATURE_LPAR) || is_kvm_guest())
185		return -ENODEV;
186
187	perf_pmu_register(&vpa_pmu, vpa_pmu.name, -1);
188	pr_info("Virtual Processor Area PMU registered.\n");
189
190	return 0;
191}
192
193static void __exit pseries_vpa_pmu_cleanup(void)
194{
195	perf_pmu_unregister(&vpa_pmu);
196	pr_info("Virtual Processor Area PMU unregistered.\n");
197}
198
199module_init(pseries_vpa_pmu_init);
200module_exit(pseries_vpa_pmu_cleanup);
201MODULE_DESCRIPTION("Perf Driver for pSeries VPA pmu counter");
202MODULE_AUTHOR("Kajol Jain <kjain@linux.ibm.com>");
203MODULE_AUTHOR("Madhavan Srinivasan <maddy@linux.ibm.com>");
204MODULE_LICENSE("GPL");
~/ Read-only file preview.