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