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