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