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