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