FILE
vpa-pmu.c
File preview with syntax highlighting.
linux-6.19.14/arch/powerpc/perf/vpa-pmu.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.