1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * nd_perf.c: NVDIMM Device Performance Monitoring Unit support 4 * 5 * Perf interface to expose nvdimm performance stats. 6 * 7 * Copyright (C) 2021 IBM Corporation 8 */ 9 10 #define pr_fmt(fmt) "nvdimm_pmu: " fmt 11 12 #include <linux/nd.h> 13 14 #define EVENT(_name, _code) enum{_name = _code} 15 16 /* 17 * NVDIMM Events codes. 18 */ 19 20 /* Controller Reset Count */ 21 EVENT(CTL_RES_CNT, 0x1); 22 /* Controller Reset Elapsed Time */ 23 EVENT(CTL_RES_TM, 0x2); 24 /* Power-on Seconds */ 25 EVENT(POWERON_SECS, 0x3); 26 /* Life Remaining */ 27 EVENT(MEM_LIFE, 0x4); 28 /* Critical Resource Utilization */ 29 EVENT(CRI_RES_UTIL, 0x5); 30 /* Host Load Count */ 31 EVENT(HOST_L_CNT, 0x6); 32 /* Host Store Count */ 33 EVENT(HOST_S_CNT, 0x7); 34 /* Host Store Duration */ 35 EVENT(HOST_S_DUR, 0x8); 36 /* Host Load Duration */ 37 EVENT(HOST_L_DUR, 0x9); 38 /* Media Read Count */ 39 EVENT(MED_R_CNT, 0xa); 40 /* Media Write Count */ 41 EVENT(MED_W_CNT, 0xb); 42 /* Media Read Duration */ 43 EVENT(MED_R_DUR, 0xc); 44 /* Media Write Duration */ 45 EVENT(MED_W_DUR, 0xd); 46 /* Cache Read Hit Count */ 47 EVENT(CACHE_RH_CNT, 0xe); 48 /* Cache Write Hit Count */ 49 EVENT(CACHE_WH_CNT, 0xf); 50 /* Fast Write Count */ 51 EVENT(FAST_W_CNT, 0x10); 52 53 NVDIMM_EVENT_ATTR(ctl_res_cnt, CTL_RES_CNT); 54 NVDIMM_EVENT_ATTR(ctl_res_tm, CTL_RES_TM); 55 NVDIMM_EVENT_ATTR(poweron_secs, POWERON_SECS); 56 NVDIMM_EVENT_ATTR(mem_life, MEM_LIFE); 57 NVDIMM_EVENT_ATTR(cri_res_util, CRI_RES_UTIL); 58 NVDIMM_EVENT_ATTR(host_l_cnt, HOST_L_CNT); 59 NVDIMM_EVENT_ATTR(host_s_cnt, HOST_S_CNT); 60 NVDIMM_EVENT_ATTR(host_s_dur, HOST_S_DUR); 61 NVDIMM_EVENT_ATTR(host_l_dur, HOST_L_DUR); 62 NVDIMM_EVENT_ATTR(med_r_cnt, MED_R_CNT); 63 NVDIMM_EVENT_ATTR(med_w_cnt, MED_W_CNT); 64 NVDIMM_EVENT_ATTR(med_r_dur, MED_R_DUR); 65 NVDIMM_EVENT_ATTR(med_w_dur, MED_W_DUR); 66 NVDIMM_EVENT_ATTR(cache_rh_cnt, CACHE_RH_CNT); 67 NVDIMM_EVENT_ATTR(cache_wh_cnt, CACHE_WH_CNT); 68 NVDIMM_EVENT_ATTR(fast_w_cnt, FAST_W_CNT); 69 70 static struct attribute *nvdimm_events_attr[] = { 71 NVDIMM_EVENT_PTR(CTL_RES_CNT), 72 NVDIMM_EVENT_PTR(CTL_RES_TM), 73 NVDIMM_EVENT_PTR(POWERON_SECS), 74 NVDIMM_EVENT_PTR(MEM_LIFE), 75 NVDIMM_EVENT_PTR(CRI_RES_UTIL), 76 NVDIMM_EVENT_PTR(HOST_L_CNT), 77 NVDIMM_EVENT_PTR(HOST_S_CNT), 78 NVDIMM_EVENT_PTR(HOST_S_DUR), 79 NVDIMM_EVENT_PTR(HOST_L_DUR), 80 NVDIMM_EVENT_PTR(MED_R_CNT), 81 NVDIMM_EVENT_PTR(MED_W_CNT), 82 NVDIMM_EVENT_PTR(MED_R_DUR), 83 NVDIMM_EVENT_PTR(MED_W_DUR), 84 NVDIMM_EVENT_PTR(CACHE_RH_CNT), 85 NVDIMM_EVENT_PTR(CACHE_WH_CNT), 86 NVDIMM_EVENT_PTR(FAST_W_CNT), 87 NULL 88 }; 89 90 static struct attribute_group nvdimm_pmu_events_group = { 91 .name = "events", 92 .attrs = nvdimm_events_attr, 93 }; 94 95 PMU_FORMAT_ATTR(event, "config:0-4"); 96 97 static struct attribute *nvdimm_pmu_format_attr[] = { 98 &format_attr_event.attr, 99 NULL, 100 }; 101 102 static struct attribute_group nvdimm_pmu_format_group = { 103 .name = "format", 104 .attrs = nvdimm_pmu_format_attr, 105 }; 106 107 ssize_t nvdimm_events_sysfs_show(struct device *dev, 108 struct device_attribute *attr, char *page) 109 { 110 struct perf_pmu_events_attr *pmu_attr; 111 112 pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr); 113 114 return sprintf(page, "event=0x%02llx\n", pmu_attr->id); 115 } 116 117 static ssize_t nvdimm_pmu_cpumask_show(struct device *dev, 118 struct device_attribute *attr, char *buf) 119 { 120 struct pmu *pmu = dev_get_drvdata(dev); 121 struct nvdimm_pmu *nd_pmu; 122 123 nd_pmu = container_of(pmu, struct nvdimm_pmu, pmu); 124 125 return cpumap_print_to_pagebuf(true, buf, cpumask_of(nd_pmu->cpu)); 126 } 127 128 static int nvdimm_pmu_cpu_offline(unsigned int cpu, struct hlist_node *node) 129 { 130 struct nvdimm_pmu *nd_pmu; 131 u32 target; 132 int nodeid; 133 const struct cpumask *cpumask; 134 135 nd_pmu = hlist_entry_safe(node, struct nvdimm_pmu, node); 136 137 /* Clear it, incase given cpu is set in nd_pmu->arch_cpumask */ 138 cpumask_test_and_clear_cpu(cpu, &nd_pmu->arch_cpumask); 139 140 /* 141 * If given cpu is not same as current designated cpu for 142 * counter access, just return. 143 */ 144 if (cpu != nd_pmu->cpu) 145 return 0; 146 147 /* Check for any active cpu in nd_pmu->arch_cpumask */ 148 target = cpumask_any(&nd_pmu->arch_cpumask); 149 150 /* 151 * Incase we don't have any active cpu in nd_pmu->arch_cpumask, 152 * check in given cpu's numa node list. 153 */ 154 if (target >= nr_cpu_ids) { 155 nodeid = cpu_to_node(cpu); 156 cpumask = cpumask_of_node(nodeid); 157 target = cpumask_any_but(cpumask, cpu); 158 } 159 nd_pmu->cpu = target; 160 161 /* Migrate nvdimm pmu events to the new target cpu if valid */ 162 if (target >= 0 && target < nr_cpu_ids) 163 perf_pmu_migrate_context(&nd_pmu->pmu, cpu, target); 164 165 return 0; 166 } 167 168 static int nvdimm_pmu_cpu_online(unsigned int cpu, struct hlist_node *node) 169 { 170 struct nvdimm_pmu *nd_pmu; 171 172 nd_pmu = hlist_entry_safe(node, struct nvdimm_pmu, node); 173 174 if (nd_pmu->cpu >= nr_cpu_ids) 175 nd_pmu->cpu = cpu; 176 177 return 0; 178 } 179 180 static int create_cpumask_attr_group(struct nvdimm_pmu *nd_pmu) 181 { 182 struct perf_pmu_events_attr *pmu_events_attr; 183 struct attribute **attrs_group; 184 struct attribute_group *nvdimm_pmu_cpumask_group; 185 186 pmu_events_attr = kzalloc(sizeof(*pmu_events_attr), GFP_KERNEL); 187 if (!pmu_events_attr) 188 return -ENOMEM; 189 190 attrs_group = kzalloc(2 * sizeof(struct attribute *), GFP_KERNEL); 191 if (!attrs_group) { 192 kfree(pmu_events_attr); 193 return -ENOMEM; 194 } 195 196 /* Allocate memory for cpumask attribute group */ 197 nvdimm_pmu_cpumask_group = kzalloc(sizeof(*nvdimm_pmu_cpumask_group), GFP_KERNEL); 198 if (!nvdimm_pmu_cpumask_group) { 199 kfree(pmu_events_attr); 200 kfree(attrs_group); 201 return -ENOMEM; 202 } 203 204 sysfs_attr_init(&pmu_events_attr->attr.attr); 205 pmu_events_attr->attr.attr.name = "cpumask"; 206 pmu_events_attr->attr.attr.mode = 0444; 207 pmu_events_attr->attr.show = nvdimm_pmu_cpumask_show; 208 attrs_group[0] = &pmu_events_attr->attr.attr; 209 attrs_group[1] = NULL; 210 211 nvdimm_pmu_cpumask_group->attrs = attrs_group; 212 nd_pmu->pmu.attr_groups[NVDIMM_PMU_CPUMASK_ATTR] = nvdimm_pmu_cpumask_group; 213 return 0; 214 } 215 216 static int nvdimm_pmu_cpu_hotplug_init(struct nvdimm_pmu *nd_pmu) 217 { 218 int nodeid, rc; 219 const struct cpumask *cpumask; 220 221 /* 222 * Incase of cpu hotplug feature, arch specific code 223 * can provide required cpumask which can be used 224 * to get designatd cpu for counter access. 225 * Check for any active cpu in nd_pmu->arch_cpumask. 226 */ 227 if (!cpumask_empty(&nd_pmu->arch_cpumask)) { 228 nd_pmu->cpu = cpumask_any(&nd_pmu->arch_cpumask); 229 } else { 230 /* pick active cpu from the cpumask of device numa node. */ 231 nodeid = dev_to_node(nd_pmu->dev); 232 cpumask = cpumask_of_node(nodeid); 233 nd_pmu->cpu = cpumask_any(cpumask); 234 } 235 236 rc = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "perf/nvdimm:online", 237 nvdimm_pmu_cpu_online, nvdimm_pmu_cpu_offline); 238 239 if (rc < 0) 240 return rc; 241 242 nd_pmu->cpuhp_state = rc; 243 244 /* Register the pmu instance for cpu hotplug */ 245 rc = cpuhp_state_add_instance_nocalls(nd_pmu->cpuhp_state, &nd_pmu->node); 246 if (rc) { 247 cpuhp_remove_multi_state(nd_pmu->cpuhp_state); 248 return rc; 249 } 250 251 /* Create cpumask attribute group */ 252 rc = create_cpumask_attr_group(nd_pmu); 253 if (rc) { 254 cpuhp_state_remove_instance_nocalls(nd_pmu->cpuhp_state, &nd_pmu->node); 255 cpuhp_remove_multi_state(nd_pmu->cpuhp_state); 256 return rc; 257 } 258 259 return 0; 260 } 261 262 static void nvdimm_pmu_free_hotplug_memory(struct nvdimm_pmu *nd_pmu) 263 { 264 cpuhp_state_remove_instance_nocalls(nd_pmu->cpuhp_state, &nd_pmu->node); 265 cpuhp_remove_multi_state(nd_pmu->cpuhp_state); 266 267 if (nd_pmu->pmu.attr_groups[NVDIMM_PMU_CPUMASK_ATTR]) 268 kfree(nd_pmu->pmu.attr_groups[NVDIMM_PMU_CPUMASK_ATTR]->attrs); 269 kfree(nd_pmu->pmu.attr_groups[NVDIMM_PMU_CPUMASK_ATTR]); 270 } 271 272 int register_nvdimm_pmu(struct nvdimm_pmu *nd_pmu, struct platform_device *pdev) 273 { 274 int rc; 275 276 if (!nd_pmu || !pdev) 277 return -EINVAL; 278 279 /* event functions like add/del/read/event_init and pmu name should not be NULL */ 280 if (WARN_ON_ONCE(!(nd_pmu->pmu.event_init && nd_pmu->pmu.add && 281 nd_pmu->pmu.del && nd_pmu->pmu.read && nd_pmu->pmu.name))) 282 return -EINVAL; 283 284 nd_pmu->pmu.attr_groups = kzalloc((NVDIMM_PMU_NULL_ATTR + 1) * 285 sizeof(struct attribute_group *), GFP_KERNEL); 286 if (!nd_pmu->pmu.attr_groups) 287 return -ENOMEM; 288 289 /* 290 * Add platform_device->dev pointer to nvdimm_pmu to access 291 * device data in events functions. 292 */ 293 nd_pmu->dev = &pdev->dev; 294 295 /* Fill attribute groups for the nvdimm pmu device */ 296 nd_pmu->pmu.attr_groups[NVDIMM_PMU_FORMAT_ATTR] = &nvdimm_pmu_format_group; 297 nd_pmu->pmu.attr_groups[NVDIMM_PMU_EVENT_ATTR] = &nvdimm_pmu_events_group; 298 nd_pmu->pmu.attr_groups[NVDIMM_PMU_NULL_ATTR] = NULL; 299 300 /* Fill attribute group for cpumask */ 301 rc = nvdimm_pmu_cpu_hotplug_init(nd_pmu); 302 if (rc) { 303 pr_info("cpu hotplug feature failed for device: %s\n", nd_pmu->pmu.name); 304 kfree(nd_pmu->pmu.attr_groups); 305 return rc; 306 } 307 308 rc = perf_pmu_register(&nd_pmu->pmu, nd_pmu->pmu.name, -1); 309 if (rc) { 310 kfree(nd_pmu->pmu.attr_groups); 311 nvdimm_pmu_free_hotplug_memory(nd_pmu); 312 return rc; 313 } 314 315 pr_info("%s NVDIMM performance monitor support registered\n", 316 nd_pmu->pmu.name); 317 318 return 0; 319 } 320 EXPORT_SYMBOL_GPL(register_nvdimm_pmu); 321 322 void unregister_nvdimm_pmu(struct nvdimm_pmu *nd_pmu) 323 { 324 perf_pmu_unregister(&nd_pmu->pmu); 325 nvdimm_pmu_free_hotplug_memory(nd_pmu); 326 kfree(nd_pmu); 327 } 328 EXPORT_SYMBOL_GPL(unregister_nvdimm_pmu); 329