xref: /freebsd/sys/dev/qat/qat_common/adf_fw_counters.c (revision 8aa51e6d7de0a828020de64560d1385e15955a1c)
1 /* SPDX-License-Identifier: BSD-3-Clause */
2 /* Copyright(c) 2007-2025 Intel Corporation */
3 #include <sys/types.h>
4 #include <sys/sysctl.h>
5 #include <sys/systm.h>
6 #include "adf_accel_devices.h"
7 #include "adf_fw_counters.h"
8 #include "adf_common_drv.h"
9 #include "icp_qat_fw_init_admin.h"
10 #include <sys/mutex.h>
11 #include <sys/sbuf.h>
12 #include <sys/priv.h>
13 #define ADF_FW_COUNTERS_BUF_SZ 4096
14 
15 #define ADF_RAS_EVENT_STR "RAS events"
16 #define ADF_FW_REQ_STR "Firmware Requests"
17 #define ADF_FW_RESP_STR "Firmware Responses"
18 
19 static void adf_fw_counters_section_del_all(struct list_head *head);
20 static void adf_fw_counters_del_all(struct adf_accel_dev *accel_dev);
21 static int
22 adf_fw_counters_add_key_value_param(struct adf_accel_dev *accel_dev,
23 				    const char *section_name,
24 				    const unsigned long sec_name_max_size,
25 				    const char *key,
26 				    const void *val);
27 static int adf_fw_counters_section_add(struct adf_accel_dev *accel_dev,
28 				       const char *name,
29 				       const unsigned long name_max_size);
30 int adf_get_fw_counters(struct adf_accel_dev *accel_dev);
31 int adf_read_fw_counters(SYSCTL_HANDLER_ARGS);
32 
33 int
adf_get_fw_counters(struct adf_accel_dev * accel_dev)34 adf_get_fw_counters(struct adf_accel_dev *accel_dev)
35 {
36 	struct icp_qat_fw_init_admin_req req;
37 	struct icp_qat_fw_init_admin_resp resp;
38 	unsigned long ae_mask;
39 	int i;
40 	int ret = 0;
41 	char aeidstr[16] = { 0 };
42 	struct adf_hw_device_data *hw_device;
43 
44 	if (!accel_dev) {
45 		ret = EFAULT;
46 		goto fail_clean;
47 	}
48 	if (!adf_dev_started(accel_dev)) {
49 		device_printf(GET_DEV(accel_dev), "Qat Device not started\n");
50 		ret = EFAULT;
51 		goto fail_clean;
52 	}
53 
54 	hw_device = accel_dev->hw_device;
55 	if (!hw_device) {
56 		ret = EFAULT;
57 		goto fail_clean;
58 	}
59 
60 	adf_fw_counters_del_all(accel_dev);
61 	explicit_bzero(&req, sizeof(struct icp_qat_fw_init_admin_req));
62 	req.cmd_id = ICP_QAT_FW_COUNTERS_GET;
63 	ae_mask = hw_device->ae_mask;
64 	for_each_set_bit(i, &ae_mask, GET_MAX_ACCELENGINES(accel_dev))
65 	{
66 		explicit_bzero(&resp,
67 			       sizeof(struct icp_qat_fw_init_admin_resp));
68 		if (adf_put_admin_msg_sync(accel_dev, i, &req, &resp) ||
69 		    resp.status) {
70 			resp.req_rec_count = ADF_FW_COUNTERS_NO_RESPONSE;
71 			resp.resp_sent_count = ADF_FW_COUNTERS_NO_RESPONSE;
72 			resp.ras_event_count = ADF_FW_COUNTERS_NO_RESPONSE;
73 		}
74 		explicit_bzero(aeidstr, sizeof(aeidstr));
75 		snprintf(aeidstr, sizeof(aeidstr), "AE %2d", i);
76 
77 		if (adf_fw_counters_section_add(accel_dev,
78 						aeidstr,
79 						sizeof(aeidstr))) {
80 			ret = ENOMEM;
81 			goto fail_clean;
82 		}
83 
84 		if (adf_fw_counters_add_key_value_param(
85 			accel_dev,
86 			aeidstr,
87 			sizeof(aeidstr),
88 			ADF_FW_REQ_STR,
89 			(void *)&resp.req_rec_count)) {
90 			adf_fw_counters_del_all(accel_dev);
91 			ret = ENOMEM;
92 			goto fail_clean;
93 		}
94 
95 		if (adf_fw_counters_add_key_value_param(
96 			accel_dev,
97 			aeidstr,
98 			sizeof(aeidstr),
99 			ADF_FW_RESP_STR,
100 			(void *)&resp.resp_sent_count)) {
101 			adf_fw_counters_del_all(accel_dev);
102 			ret = ENOMEM;
103 			goto fail_clean;
104 		}
105 
106 		if (hw_device->count_ras_event &&
107 		    hw_device->count_ras_event(accel_dev,
108 					       (void *)&resp.ras_event_count,
109 					       aeidstr)) {
110 			adf_fw_counters_del_all(accel_dev);
111 			ret = ENOMEM;
112 			goto fail_clean;
113 		}
114 	}
115 
116 fail_clean:
117 	return ret;
118 }
119 
adf_read_fw_counters(SYSCTL_HANDLER_ARGS)120 int adf_read_fw_counters(SYSCTL_HANDLER_ARGS)
121 {
122 	struct adf_accel_dev *accel_dev = arg1;
123 	struct adf_fw_counters_section *ptr = NULL;
124 	struct list_head *list = NULL, *list_ptr = NULL;
125 	struct list_head *tmp = NULL, *tmp_val = NULL;
126 	int ret = 0;
127 	struct sbuf *sbuf = NULL;
128 	char *cbuf = NULL;
129 
130 	if (priv_check(curthread, PRIV_DRIVER) != 0)
131 		return EPERM;
132 
133 	if (accel_dev == NULL) {
134 		return EINVAL;
135 	}
136 	cbuf = malloc(ADF_FW_COUNTERS_BUF_SZ, M_QAT, M_WAITOK | M_ZERO);
137 
138 	sbuf = sbuf_new(NULL, cbuf, ADF_FW_COUNTERS_BUF_SZ, SBUF_FIXEDLEN);
139 	if (sbuf == NULL) {
140 		free(cbuf, M_QAT);
141 		return ENOMEM;
142 	}
143 	ret = adf_get_fw_counters(accel_dev);
144 
145 	if (ret) {
146 		sbuf_delete(sbuf);
147 		free(cbuf, M_QAT);
148 		return ret;
149 	}
150 
151 	sbuf_printf(sbuf,
152 		    "\n+------------------------------------------------+\n");
153 	sbuf_printf(
154 	    sbuf,
155 	    "| FW Statistics for Qat Device					   |\n");
156 	sbuf_printf(sbuf,
157 		    "+------------------------------------------------+\n");
158 
159 	list_for_each_prev_safe(list,
160 				tmp,
161 				&accel_dev->fw_counters_data->ae_sec_list)
162 	{
163 		ptr = list_entry(list, struct adf_fw_counters_section, list);
164 		sbuf_printf(sbuf, "%s\n", ptr->name);
165 		list_for_each_prev_safe(list_ptr, tmp_val, &ptr->param_head)
166 		{
167 			struct adf_fw_counters_val *count =
168 			    list_entry(list_ptr,
169 				       struct adf_fw_counters_val,
170 				       list);
171 			sbuf_printf(sbuf, "%s:%s\n", count->key, count->val);
172 		}
173 	}
174 
175 	sbuf_finish(sbuf);
176 	ret = SYSCTL_OUT(req, sbuf_data(sbuf), sbuf_len(sbuf));
177 	sbuf_delete(sbuf);
178 	free(cbuf, M_QAT);
179 	return ret;
180 }
181 
182 int
adf_fw_count_ras_event(struct adf_accel_dev * accel_dev,u32 * ras_event,char * aeidstr)183 adf_fw_count_ras_event(struct adf_accel_dev *accel_dev,
184 		       u32 *ras_event,
185 		       char *aeidstr)
186 {
187 	unsigned long count = 0;
188 
189 	if (!accel_dev || !ras_event || !aeidstr)
190 		return EINVAL;
191 
192 	count = (*ras_event == ADF_FW_COUNTERS_NO_RESPONSE ?
193 		     ADF_FW_COUNTERS_NO_RESPONSE :
194 		     (unsigned long)*ras_event);
195 
196 	return adf_fw_counters_add_key_value_param(
197 	    accel_dev, aeidstr, 16, ADF_RAS_EVENT_STR, (void *)&count);
198 }
199 
200 /**
201  * adf_fw_counters_add() - Create an acceleration device FW counters table.
202  * @accel_dev:	Pointer to acceleration device.
203  *
204  * Function creates a FW counters statistics table for the given
205  * acceleration device.
206  * The table stores device specific values of FW Requests sent to the FW and
207  * FW Responses received from the FW.
208  * To be used by QAT device specific drivers.
209  *
210  * Return: 0 on success, error code otherwise.
211  */
212 int
adf_fw_counters_add(struct adf_accel_dev * accel_dev)213 adf_fw_counters_add(struct adf_accel_dev *accel_dev)
214 {
215 	struct adf_fw_counters_data *fw_counters_data;
216 	struct sysctl_ctx_list *qat_sysctl_ctx;
217 	struct sysctl_oid *qat_sysctl_tree;
218 
219 	fw_counters_data =
220 	    malloc(sizeof(*fw_counters_data), M_QAT, M_WAITOK | M_ZERO);
221 
222 	INIT_LIST_HEAD(&fw_counters_data->ae_sec_list);
223 
224 	init_rwsem(&fw_counters_data->lock);
225 	accel_dev->fw_counters_data = fw_counters_data;
226 
227 	qat_sysctl_ctx =
228 	    device_get_sysctl_ctx(accel_dev->accel_pci_dev.pci_dev);
229 	qat_sysctl_tree =
230 	    device_get_sysctl_tree(accel_dev->accel_pci_dev.pci_dev);
231 	fw_counters_data->debug =
232 	    SYSCTL_ADD_OID(qat_sysctl_ctx,
233 			   SYSCTL_CHILDREN(qat_sysctl_tree),
234 			   OID_AUTO,
235 			   "fw_counters",
236 			   CTLTYPE_STRING | CTLFLAG_RD,
237 			   accel_dev,
238 			   0,
239 			   adf_read_fw_counters,
240 			   "A",
241 			   "QAT FW counters");
242 	if (!fw_counters_data->debug) {
243 		free(fw_counters_data, M_QAT);
244 		accel_dev->fw_counters_data = NULL;
245 		return ENOMEM;
246 	}
247 
248 	return 0;
249 }
250 
251 static void
adf_fw_counters_del_all(struct adf_accel_dev * accel_dev)252 adf_fw_counters_del_all(struct adf_accel_dev *accel_dev)
253 {
254 	struct adf_fw_counters_data *fw_counters_data =
255 	    accel_dev->fw_counters_data;
256 
257 	down_write(&fw_counters_data->lock);
258 	adf_fw_counters_section_del_all(&fw_counters_data->ae_sec_list);
259 	up_write(&fw_counters_data->lock);
260 }
261 
262 static void
adf_fw_counters_keyval_add(struct adf_fw_counters_val * new,struct adf_fw_counters_section * sec)263 adf_fw_counters_keyval_add(struct adf_fw_counters_val *new,
264 			   struct adf_fw_counters_section *sec)
265 {
266 	list_add_tail(&new->list, &sec->param_head);
267 }
268 
269 static void
adf_fw_counters_keyval_del_all(struct list_head * head)270 adf_fw_counters_keyval_del_all(struct list_head *head)
271 {
272 	struct list_head *list_ptr = NULL, *tmp = NULL;
273 
274 	list_for_each_prev_safe(list_ptr, tmp, head)
275 	{
276 		struct adf_fw_counters_val *ptr =
277 		    list_entry(list_ptr, struct adf_fw_counters_val, list);
278 		list_del(list_ptr);
279 		free(ptr, M_QAT);
280 	}
281 }
282 
283 static void
adf_fw_counters_section_del_all(struct list_head * head)284 adf_fw_counters_section_del_all(struct list_head *head)
285 {
286 	struct adf_fw_counters_section *ptr = NULL;
287 	struct list_head *list = NULL, *tmp = NULL;
288 
289 	list_for_each_prev_safe(list, tmp, head)
290 	{
291 		ptr = list_entry(list, struct adf_fw_counters_section, list);
292 		adf_fw_counters_keyval_del_all(&ptr->param_head);
293 		list_del(list);
294 		free(ptr, M_QAT);
295 	}
296 }
297 
298 static struct adf_fw_counters_section *
adf_fw_counters_sec_find(struct adf_accel_dev * accel_dev,const char * sec_name,const unsigned long sec_name_max_size)299 adf_fw_counters_sec_find(struct adf_accel_dev *accel_dev,
300 			 const char *sec_name,
301 			 const unsigned long sec_name_max_size)
302 {
303 	struct adf_fw_counters_data *fw_counters_data =
304 	    accel_dev->fw_counters_data;
305 	struct list_head *list = NULL;
306 
307 	list_for_each(list, &fw_counters_data->ae_sec_list)
308 	{
309 		struct adf_fw_counters_section *ptr =
310 		    list_entry(list, struct adf_fw_counters_section, list);
311 		if (!strncmp(ptr->name, sec_name, sec_name_max_size))
312 			return ptr;
313 	}
314 	return NULL;
315 }
316 
317 static int
adf_fw_counters_add_key_value_param(struct adf_accel_dev * accel_dev,const char * section_name,const unsigned long sec_name_max_size,const char * key,const void * val)318 adf_fw_counters_add_key_value_param(struct adf_accel_dev *accel_dev,
319 				    const char *section_name,
320 				    const unsigned long sec_name_max_size,
321 				    const char *key,
322 				    const void *val)
323 {
324 	struct adf_fw_counters_data *fw_counters_data =
325 	    accel_dev->fw_counters_data;
326 	struct adf_fw_counters_val *key_val;
327 	struct adf_fw_counters_section *section =
328 	    adf_fw_counters_sec_find(accel_dev,
329 				     section_name,
330 				     sec_name_max_size);
331 	long tmp = *((const long *)val);
332 
333 	if (!section)
334 		return EFAULT;
335 	key_val = malloc(sizeof(*key_val), M_QAT, M_WAITOK | M_ZERO);
336 
337 	INIT_LIST_HEAD(&key_val->list);
338 
339 	if (tmp == ADF_FW_COUNTERS_NO_RESPONSE) {
340 		snprintf(key_val->val,
341 			 FW_COUNTERS_MAX_VAL_LEN_IN_BYTES,
342 			 "No Response");
343 	} else {
344 		snprintf(key_val->val,
345 			 FW_COUNTERS_MAX_VAL_LEN_IN_BYTES,
346 			 "%ld",
347 			 tmp);
348 	}
349 
350 	strlcpy(key_val->key, key, sizeof(key_val->key));
351 	down_write(&fw_counters_data->lock);
352 	adf_fw_counters_keyval_add(key_val, section);
353 	up_write(&fw_counters_data->lock);
354 	return 0;
355 }
356 
357 /**
358  * adf_fw_counters_section_add() - Add AE section entry to FW counters table.
359  * @accel_dev:	Pointer to acceleration device.
360  * @name: Name of the section
361  *
362  * Function adds a section for each AE where FW Requests/Responses and their
363  * values will be stored.
364  * To be used by QAT device specific drivers.
365  *
366  * Return: 0 on success, error code otherwise.
367  */
368 static int
adf_fw_counters_section_add(struct adf_accel_dev * accel_dev,const char * name,const unsigned long name_max_size)369 adf_fw_counters_section_add(struct adf_accel_dev *accel_dev,
370 			    const char *name,
371 			    const unsigned long name_max_size)
372 {
373 	struct adf_fw_counters_data *fw_counters_data =
374 	    accel_dev->fw_counters_data;
375 	struct adf_fw_counters_section *sec =
376 	    adf_fw_counters_sec_find(accel_dev, name, name_max_size);
377 
378 	if (sec)
379 		return 0;
380 
381 	sec = malloc(sizeof(*sec), M_QAT, M_WAITOK | M_ZERO);
382 
383 	strlcpy(sec->name, name, sizeof(sec->name));
384 	INIT_LIST_HEAD(&sec->param_head);
385 
386 	down_write(&fw_counters_data->lock);
387 
388 	list_add_tail(&sec->list, &fw_counters_data->ae_sec_list);
389 	up_write(&fw_counters_data->lock);
390 	return 0;
391 }
392 
393 /**
394  * adf_fw_counters_remove() - Clears acceleration device FW counters table.
395  * @accel_dev:	Pointer to acceleration device.
396  *
397  * Function removes FW counters table from the given acceleration device
398  * and frees all allocated memory.
399  * To be used by QAT device specific drivers.
400  *
401  * Return: void
402  */
403 void
adf_fw_counters_remove(struct adf_accel_dev * accel_dev)404 adf_fw_counters_remove(struct adf_accel_dev *accel_dev)
405 {
406 	struct sysctl_ctx_list *qat_sysctl_ctx;
407 	struct adf_fw_counters_data *fw_counters_data =
408 	    accel_dev->fw_counters_data;
409 
410 	if (!fw_counters_data)
411 		return;
412 
413 	if (fw_counters_data->debug) {
414 		qat_sysctl_ctx =
415 		    device_get_sysctl_ctx(accel_dev->accel_pci_dev.pci_dev);
416 		sysctl_ctx_entry_del(qat_sysctl_ctx, fw_counters_data->debug);
417 		sysctl_remove_oid(fw_counters_data->debug, 1, 1);
418 		fw_counters_data->debug = NULL;
419 	}
420 
421 	down_write(&fw_counters_data->lock);
422 	adf_fw_counters_section_del_all(&fw_counters_data->ae_sec_list);
423 	up_write(&fw_counters_data->lock);
424 	free(fw_counters_data, M_QAT);
425 	accel_dev->fw_counters_data = NULL;
426 }
427