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