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 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 (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 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 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 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 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 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 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 * 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 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 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 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