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 } 412