1 /* SPDX-License-Identifier: BSD-3-Clause */
2 /* Copyright(c) 2007-2022 Intel Corporation */
3 /* $FreeBSD$ */
4 #include <sys/types.h>
5 #include <sys/sysctl.h>
6 #include <sys/systm.h>
7 #include "adf_accel_devices.h"
8 #include "adf_fw_counters.h"
9 #include "adf_common_drv.h"
10 #include "icp_qat_fw_init_admin.h"
11 #include <sys/mutex.h>
12 #include <sys/sbuf.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
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 
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 (accel_dev == NULL) {
131 		return EINVAL;
132 	}
133 	cbuf = malloc(ADF_FW_COUNTERS_BUF_SZ, M_QAT, M_WAITOK | M_ZERO);
134 
135 	sbuf = sbuf_new(NULL, cbuf, ADF_FW_COUNTERS_BUF_SZ, SBUF_FIXEDLEN);
136 	if (sbuf == NULL) {
137 		free(cbuf, M_QAT);
138 		return ENOMEM;
139 	}
140 	ret = adf_get_fw_counters(accel_dev);
141 
142 	if (ret) {
143 		sbuf_delete(sbuf);
144 		free(cbuf, M_QAT);
145 		return ret;
146 	}
147 
148 	sbuf_printf(sbuf,
149 		    "\n+------------------------------------------------+\n");
150 	sbuf_printf(
151 	    sbuf,
152 	    "| FW Statistics for Qat Device					   |\n");
153 	sbuf_printf(sbuf,
154 		    "+------------------------------------------------+\n");
155 
156 	list_for_each_prev_safe(list,
157 				tmp,
158 				&accel_dev->fw_counters_data->ae_sec_list)
159 	{
160 		ptr = list_entry(list, struct adf_fw_counters_section, list);
161 		sbuf_printf(sbuf, "%s\n", ptr->name);
162 		list_for_each_prev_safe(list_ptr, tmp_val, &ptr->param_head)
163 		{
164 			struct adf_fw_counters_val *count =
165 			    list_entry(list_ptr,
166 				       struct adf_fw_counters_val,
167 				       list);
168 			sbuf_printf(sbuf, "%s:%s\n", count->key, count->val);
169 		}
170 	}
171 
172 	sbuf_finish(sbuf);
173 	ret = SYSCTL_OUT(req, sbuf_data(sbuf), sbuf_len(sbuf));
174 	sbuf_delete(sbuf);
175 	free(cbuf, M_QAT);
176 	return ret;
177 }
178 
179 int
180 adf_fw_count_ras_event(struct adf_accel_dev *accel_dev,
181 		       u32 *ras_event,
182 		       char *aeidstr)
183 {
184 	unsigned long count = 0;
185 
186 	if (!accel_dev || !ras_event || !aeidstr)
187 		return EINVAL;
188 
189 	count = (*ras_event == ADF_FW_COUNTERS_NO_RESPONSE ?
190 		     ADF_FW_COUNTERS_NO_RESPONSE :
191 		     (unsigned long)*ras_event);
192 
193 	return adf_fw_counters_add_key_value_param(
194 	    accel_dev, aeidstr, 16, ADF_RAS_EVENT_STR, (void *)&count);
195 }
196 
197 /**
198  * adf_fw_counters_add() - Create an acceleration device FW counters table.
199  * @accel_dev:	Pointer to acceleration device.
200  *
201  * Function creates a FW counters statistics table for the given
202  * acceleration device.
203  * The table stores device specific values of FW Requests sent to the FW and
204  * FW Responses received from the FW.
205  * To be used by QAT device specific drivers.
206  *
207  * Return: 0 on success, error code otherwise.
208  */
209 int
210 adf_fw_counters_add(struct adf_accel_dev *accel_dev)
211 {
212 	struct adf_fw_counters_data *fw_counters_data;
213 	struct sysctl_ctx_list *qat_sysctl_ctx;
214 	struct sysctl_oid *qat_sysctl_tree;
215 	struct sysctl_oid *rc = 0;
216 
217 	fw_counters_data =
218 	    malloc(sizeof(*fw_counters_data), M_QAT, M_WAITOK | M_ZERO);
219 
220 	INIT_LIST_HEAD(&fw_counters_data->ae_sec_list);
221 
222 	init_rwsem(&fw_counters_data->lock);
223 	accel_dev->fw_counters_data = fw_counters_data;
224 
225 	qat_sysctl_ctx =
226 	    device_get_sysctl_ctx(accel_dev->accel_pci_dev.pci_dev);
227 	qat_sysctl_tree =
228 	    device_get_sysctl_tree(accel_dev->accel_pci_dev.pci_dev);
229 	rc = SYSCTL_ADD_OID(qat_sysctl_ctx,
230 			    SYSCTL_CHILDREN(qat_sysctl_tree),
231 			    OID_AUTO,
232 			    "fw_counters",
233 			    CTLTYPE_STRING | CTLFLAG_RD,
234 			    accel_dev,
235 			    0,
236 			    adf_read_fw_counters,
237 			    "A",
238 			    "QAT FW counters");
239 	if (!rc)
240 		return ENOMEM;
241 	else
242 		return 0;
243 }
244 
245 static void
246 adf_fw_counters_del_all(struct adf_accel_dev *accel_dev)
247 {
248 	struct adf_fw_counters_data *fw_counters_data =
249 	    accel_dev->fw_counters_data;
250 
251 	down_write(&fw_counters_data->lock);
252 	adf_fw_counters_section_del_all(&fw_counters_data->ae_sec_list);
253 	up_write(&fw_counters_data->lock);
254 }
255 
256 static void
257 adf_fw_counters_keyval_add(struct adf_fw_counters_val *new,
258 			   struct adf_fw_counters_section *sec)
259 {
260 	list_add_tail(&new->list, &sec->param_head);
261 }
262 
263 static void
264 adf_fw_counters_keyval_del_all(struct list_head *head)
265 {
266 	struct list_head *list_ptr = NULL, *tmp = NULL;
267 
268 	list_for_each_prev_safe(list_ptr, tmp, head)
269 	{
270 		struct adf_fw_counters_val *ptr =
271 		    list_entry(list_ptr, struct adf_fw_counters_val, list);
272 		list_del(list_ptr);
273 		free(ptr, M_QAT);
274 	}
275 }
276 
277 static void
278 adf_fw_counters_section_del_all(struct list_head *head)
279 {
280 	struct adf_fw_counters_section *ptr = NULL;
281 	struct list_head *list = NULL, *tmp = NULL;
282 
283 	list_for_each_prev_safe(list, tmp, head)
284 	{
285 		ptr = list_entry(list, struct adf_fw_counters_section, list);
286 		adf_fw_counters_keyval_del_all(&ptr->param_head);
287 		list_del(list);
288 		free(ptr, M_QAT);
289 	}
290 }
291 
292 static struct adf_fw_counters_section *
293 adf_fw_counters_sec_find(struct adf_accel_dev *accel_dev,
294 			 const char *sec_name,
295 			 const unsigned long sec_name_max_size)
296 {
297 	struct adf_fw_counters_data *fw_counters_data =
298 	    accel_dev->fw_counters_data;
299 	struct list_head *list = NULL;
300 
301 	list_for_each(list, &fw_counters_data->ae_sec_list)
302 	{
303 		struct adf_fw_counters_section *ptr =
304 		    list_entry(list, struct adf_fw_counters_section, list);
305 		if (!strncmp(ptr->name, sec_name, sec_name_max_size))
306 			return ptr;
307 	}
308 	return NULL;
309 }
310 
311 static int
312 adf_fw_counters_add_key_value_param(struct adf_accel_dev *accel_dev,
313 				    const char *section_name,
314 				    const unsigned long sec_name_max_size,
315 				    const char *key,
316 				    const void *val)
317 {
318 	struct adf_fw_counters_data *fw_counters_data =
319 	    accel_dev->fw_counters_data;
320 	struct adf_fw_counters_val *key_val;
321 	struct adf_fw_counters_section *section =
322 	    adf_fw_counters_sec_find(accel_dev,
323 				     section_name,
324 				     sec_name_max_size);
325 	long tmp = *((const long *)val);
326 
327 	if (!section)
328 		return EFAULT;
329 	key_val = malloc(sizeof(*key_val), M_QAT, M_WAITOK | M_ZERO);
330 
331 	INIT_LIST_HEAD(&key_val->list);
332 
333 	if (tmp == ADF_FW_COUNTERS_NO_RESPONSE) {
334 		snprintf(key_val->val,
335 			 FW_COUNTERS_MAX_VAL_LEN_IN_BYTES,
336 			 "No Response");
337 	} else {
338 		snprintf(key_val->val,
339 			 FW_COUNTERS_MAX_VAL_LEN_IN_BYTES,
340 			 "%ld",
341 			 tmp);
342 	}
343 
344 	strlcpy(key_val->key, key, sizeof(key_val->key));
345 	down_write(&fw_counters_data->lock);
346 	adf_fw_counters_keyval_add(key_val, section);
347 	up_write(&fw_counters_data->lock);
348 	return 0;
349 }
350 
351 /**
352  * adf_fw_counters_section_add() - Add AE section entry to FW counters table.
353  * @accel_dev:	Pointer to acceleration device.
354  * @name: Name of the section
355  *
356  * Function adds a section for each AE where FW Requests/Responses and their
357  * values will be stored.
358  * To be used by QAT device specific drivers.
359  *
360  * Return: 0 on success, error code otherwise.
361  */
362 static int
363 adf_fw_counters_section_add(struct adf_accel_dev *accel_dev,
364 			    const char *name,
365 			    const unsigned long name_max_size)
366 {
367 	struct adf_fw_counters_data *fw_counters_data =
368 	    accel_dev->fw_counters_data;
369 	struct adf_fw_counters_section *sec =
370 	    adf_fw_counters_sec_find(accel_dev, name, name_max_size);
371 
372 	if (sec)
373 		return 0;
374 
375 	sec = malloc(sizeof(*sec), M_QAT, M_WAITOK | M_ZERO);
376 
377 	strlcpy(sec->name, name, sizeof(sec->name));
378 	INIT_LIST_HEAD(&sec->param_head);
379 
380 	down_write(&fw_counters_data->lock);
381 
382 	list_add_tail(&sec->list, &fw_counters_data->ae_sec_list);
383 	up_write(&fw_counters_data->lock);
384 	return 0;
385 }
386 
387 /**
388  * adf_fw_counters_remove() - Clears acceleration device FW counters table.
389  * @accel_dev:	Pointer to acceleration device.
390  *
391  * Function removes FW counters table from the given acceleration device
392  * and frees all allocated memory.
393  * To be used by QAT device specific drivers.
394  *
395  * Return: void
396  */
397 void
398 adf_fw_counters_remove(struct adf_accel_dev *accel_dev)
399 {
400 	struct adf_fw_counters_data *fw_counters_data =
401 	    accel_dev->fw_counters_data;
402 
403 	if (!fw_counters_data)
404 		return;
405 
406 	down_write(&fw_counters_data->lock);
407 	adf_fw_counters_section_del_all(&fw_counters_data->ae_sec_list);
408 	up_write(&fw_counters_data->lock);
409 	free(fw_counters_data, M_QAT);
410 	accel_dev->fw_counters_data = NULL;
411 }
412