1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Performance event support for s390x 4 * 5 * Copyright IBM Corp. 2012, 2013 6 * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com> 7 */ 8 #define KMSG_COMPONENT "perf" 9 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 10 11 #include <linux/kernel.h> 12 #include <linux/perf_event.h> 13 #include <linux/kvm_host.h> 14 #include <linux/percpu.h> 15 #include <linux/export.h> 16 #include <linux/seq_file.h> 17 #include <linux/spinlock.h> 18 #include <linux/sysfs.h> 19 #include <asm/irq.h> 20 #include <asm/cpu_mf.h> 21 #include <asm/lowcore.h> 22 #include <asm/processor.h> 23 #include <asm/sysinfo.h> 24 25 const char *perf_pmu_name(void) 26 { 27 if (cpum_cf_avail() || cpum_sf_avail()) 28 return "CPU-Measurement Facilities (CPU-MF)"; 29 return "pmu"; 30 } 31 EXPORT_SYMBOL(perf_pmu_name); 32 33 int perf_num_counters(void) 34 { 35 int num = 0; 36 37 if (cpum_cf_avail()) 38 num += PERF_CPUM_CF_MAX_CTR; 39 if (cpum_sf_avail()) 40 num += PERF_CPUM_SF_MAX_CTR; 41 42 return num; 43 } 44 EXPORT_SYMBOL(perf_num_counters); 45 46 static struct kvm_s390_sie_block *sie_block(struct pt_regs *regs) 47 { 48 struct stack_frame *stack = (struct stack_frame *) regs->gprs[15]; 49 50 if (!stack) 51 return NULL; 52 53 return (struct kvm_s390_sie_block *) stack->empty1[0]; 54 } 55 56 static bool is_in_guest(struct pt_regs *regs) 57 { 58 if (user_mode(regs)) 59 return false; 60 #if IS_ENABLED(CONFIG_KVM) 61 return instruction_pointer(regs) == (unsigned long) &sie_exit; 62 #else 63 return false; 64 #endif 65 } 66 67 static unsigned long guest_is_user_mode(struct pt_regs *regs) 68 { 69 return sie_block(regs)->gpsw.mask & PSW_MASK_PSTATE; 70 } 71 72 static unsigned long instruction_pointer_guest(struct pt_regs *regs) 73 { 74 return sie_block(regs)->gpsw.addr; 75 } 76 77 unsigned long perf_instruction_pointer(struct pt_regs *regs) 78 { 79 return is_in_guest(regs) ? instruction_pointer_guest(regs) 80 : instruction_pointer(regs); 81 } 82 83 static unsigned long perf_misc_guest_flags(struct pt_regs *regs) 84 { 85 return guest_is_user_mode(regs) ? PERF_RECORD_MISC_GUEST_USER 86 : PERF_RECORD_MISC_GUEST_KERNEL; 87 } 88 89 static unsigned long perf_misc_flags_sf(struct pt_regs *regs) 90 { 91 struct perf_sf_sde_regs *sde_regs; 92 unsigned long flags; 93 94 sde_regs = (struct perf_sf_sde_regs *) ®s->int_parm_long; 95 if (sde_regs->in_guest) 96 flags = user_mode(regs) ? PERF_RECORD_MISC_GUEST_USER 97 : PERF_RECORD_MISC_GUEST_KERNEL; 98 else 99 flags = user_mode(regs) ? PERF_RECORD_MISC_USER 100 : PERF_RECORD_MISC_KERNEL; 101 return flags; 102 } 103 104 unsigned long perf_misc_flags(struct pt_regs *regs) 105 { 106 /* Check if the cpum_sf PMU has created the pt_regs structure. 107 * In this case, perf misc flags can be easily extracted. Otherwise, 108 * do regular checks on the pt_regs content. 109 */ 110 if (regs->int_code == 0x1407 && regs->int_parm == CPU_MF_INT_SF_PRA) 111 if (!regs->gprs[15]) 112 return perf_misc_flags_sf(regs); 113 114 if (is_in_guest(regs)) 115 return perf_misc_guest_flags(regs); 116 117 return user_mode(regs) ? PERF_RECORD_MISC_USER 118 : PERF_RECORD_MISC_KERNEL; 119 } 120 121 static void print_debug_cf(void) 122 { 123 struct cpumf_ctr_info cf_info; 124 int cpu = smp_processor_id(); 125 126 memset(&cf_info, 0, sizeof(cf_info)); 127 if (!qctri(&cf_info)) 128 pr_info("CPU[%i] CPUM_CF: ver=%u.%u A=%04x E=%04x C=%04x\n", 129 cpu, cf_info.cfvn, cf_info.csvn, 130 cf_info.auth_ctl, cf_info.enable_ctl, cf_info.act_ctl); 131 } 132 133 static void print_debug_sf(void) 134 { 135 struct hws_qsi_info_block si; 136 int cpu = smp_processor_id(); 137 138 memset(&si, 0, sizeof(si)); 139 if (qsi(&si)) 140 return; 141 142 pr_info("CPU[%i] CPUM_SF: basic=%i diag=%i min=%lu max=%lu cpu_speed=%u\n", 143 cpu, si.as, si.ad, si.min_sampl_rate, si.max_sampl_rate, 144 si.cpu_speed); 145 146 if (si.as) 147 pr_info("CPU[%i] CPUM_SF: Basic-sampling: a=%i e=%i c=%i" 148 " bsdes=%i tear=%016lx dear=%016lx\n", cpu, 149 si.as, si.es, si.cs, si.bsdes, si.tear, si.dear); 150 if (si.ad) 151 pr_info("CPU[%i] CPUM_SF: Diagnostic-sampling: a=%i e=%i c=%i" 152 " dsdes=%i tear=%016lx dear=%016lx\n", cpu, 153 si.ad, si.ed, si.cd, si.dsdes, si.tear, si.dear); 154 } 155 156 void perf_event_print_debug(void) 157 { 158 unsigned long flags; 159 160 local_irq_save(flags); 161 if (cpum_cf_avail()) 162 print_debug_cf(); 163 if (cpum_sf_avail()) 164 print_debug_sf(); 165 local_irq_restore(flags); 166 } 167 168 /* Service level infrastructure */ 169 static void sl_print_counter(struct seq_file *m) 170 { 171 struct cpumf_ctr_info ci; 172 173 memset(&ci, 0, sizeof(ci)); 174 if (qctri(&ci)) 175 return; 176 177 seq_printf(m, "CPU-MF: Counter facility: version=%u.%u " 178 "authorization=%04x\n", ci.cfvn, ci.csvn, ci.auth_ctl); 179 } 180 181 static void sl_print_sampling(struct seq_file *m) 182 { 183 struct hws_qsi_info_block si; 184 185 memset(&si, 0, sizeof(si)); 186 if (qsi(&si)) 187 return; 188 189 if (!si.as && !si.ad) 190 return; 191 192 seq_printf(m, "CPU-MF: Sampling facility: min_rate=%lu max_rate=%lu" 193 " cpu_speed=%u\n", si.min_sampl_rate, si.max_sampl_rate, 194 si.cpu_speed); 195 if (si.as) 196 seq_printf(m, "CPU-MF: Sampling facility: mode=basic" 197 " sample_size=%u\n", si.bsdes); 198 if (si.ad) 199 seq_printf(m, "CPU-MF: Sampling facility: mode=diagnostic" 200 " sample_size=%u\n", si.dsdes); 201 } 202 203 static void service_level_perf_print(struct seq_file *m, 204 struct service_level *sl) 205 { 206 if (cpum_cf_avail()) 207 sl_print_counter(m); 208 if (cpum_sf_avail()) 209 sl_print_sampling(m); 210 } 211 212 static struct service_level service_level_perf = { 213 .seq_print = service_level_perf_print, 214 }; 215 216 static int __init service_level_perf_register(void) 217 { 218 return register_service_level(&service_level_perf); 219 } 220 arch_initcall(service_level_perf_register); 221 222 static int __perf_callchain_kernel(void *data, unsigned long address, int reliable) 223 { 224 struct perf_callchain_entry_ctx *entry = data; 225 226 perf_callchain_store(entry, address); 227 return 0; 228 } 229 230 void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, 231 struct pt_regs *regs) 232 { 233 if (user_mode(regs)) 234 return; 235 dump_trace(__perf_callchain_kernel, entry, NULL, regs->gprs[15]); 236 } 237 238 /* Perf definitions for PMU event attributes in sysfs */ 239 ssize_t cpumf_events_sysfs_show(struct device *dev, 240 struct device_attribute *attr, char *page) 241 { 242 struct perf_pmu_events_attr *pmu_attr; 243 244 pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); 245 return sprintf(page, "event=0x%04llx\n", pmu_attr->id); 246 } 247