xref: /linux/drivers/nvdimm/nd_perf.c (revision 0fab1ba6ad6ba1f76380f92ead95c6e861ef8116)
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