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 }
Cache object: d95d6becf2060827ad490bf1e05b3937
|