#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include "adf_accel_devices.h"
#include "adf_fw_counters.h"
#include "adf_common_drv.h"
#include "icp_qat_fw_init_admin.h"
#include <sys/mutex.h>
#include <sys/sbuf.h>
#include <sys/priv.h>
#define ADF_FW_COUNTERS_BUF_SZ 4096
#define ADF_RAS_EVENT_STR "RAS events"
#define ADF_FW_REQ_STR "Firmware Requests"
#define ADF_FW_RESP_STR "Firmware Responses"
static void adf_fw_counters_section_del_all(struct list_head *head);
static void adf_fw_counters_del_all(struct adf_accel_dev *accel_dev);
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);
static int adf_fw_counters_section_add(struct adf_accel_dev *accel_dev,
const char *name,
const unsigned long name_max_size);
int adf_get_fw_counters(struct adf_accel_dev *accel_dev);
int adf_read_fw_counters(SYSCTL_HANDLER_ARGS);
int
adf_get_fw_counters(struct adf_accel_dev *accel_dev)
{
struct icp_qat_fw_init_admin_req req;
struct icp_qat_fw_init_admin_resp resp;
unsigned long ae_mask;
int i;
int ret = 0;
char aeidstr[16] = { 0 };
struct adf_hw_device_data *hw_device;
if (!accel_dev) {
ret = EFAULT;
goto fail_clean;
}
if (!adf_dev_started(accel_dev)) {
device_printf(GET_DEV(accel_dev), "Qat Device not started\n");
ret = EFAULT;
goto fail_clean;
}
hw_device = accel_dev->hw_device;
if (!hw_device) {
ret = EFAULT;
goto fail_clean;
}
adf_fw_counters_del_all(accel_dev);
explicit_bzero(&req, sizeof(struct icp_qat_fw_init_admin_req));
req.cmd_id = ICP_QAT_FW_COUNTERS_GET;
ae_mask = hw_device->ae_mask;
for_each_set_bit(i, &ae_mask, GET_MAX_ACCELENGINES(accel_dev))
{
explicit_bzero(&resp,
sizeof(struct icp_qat_fw_init_admin_resp));
if (adf_put_admin_msg_sync(accel_dev, i, &req, &resp) ||
resp.status) {
resp.req_rec_count = ADF_FW_COUNTERS_NO_RESPONSE;
resp.resp_sent_count = ADF_FW_COUNTERS_NO_RESPONSE;
resp.ras_event_count = ADF_FW_COUNTERS_NO_RESPONSE;
}
explicit_bzero(aeidstr, sizeof(aeidstr));
snprintf(aeidstr, sizeof(aeidstr), "AE %2d", i);
if (adf_fw_counters_section_add(accel_dev,
aeidstr,
sizeof(aeidstr))) {
ret = ENOMEM;
goto fail_clean;
}
if (adf_fw_counters_add_key_value_param(
accel_dev,
aeidstr,
sizeof(aeidstr),
ADF_FW_REQ_STR,
(void *)&resp.req_rec_count)) {
adf_fw_counters_del_all(accel_dev);
ret = ENOMEM;
goto fail_clean;
}
if (adf_fw_counters_add_key_value_param(
accel_dev,
aeidstr,
sizeof(aeidstr),
ADF_FW_RESP_STR,
(void *)&resp.resp_sent_count)) {
adf_fw_counters_del_all(accel_dev);
ret = ENOMEM;
goto fail_clean;
}
if (hw_device->count_ras_event &&
hw_device->count_ras_event(accel_dev,
(void *)&resp.ras_event_count,
aeidstr)) {
adf_fw_counters_del_all(accel_dev);
ret = ENOMEM;
goto fail_clean;
}
}
fail_clean:
return ret;
}
int adf_read_fw_counters(SYSCTL_HANDLER_ARGS)
{
struct adf_accel_dev *accel_dev = arg1;
struct adf_fw_counters_section *ptr = NULL;
struct list_head *list = NULL, *list_ptr = NULL;
struct list_head *tmp = NULL, *tmp_val = NULL;
int ret = 0;
struct sbuf *sbuf = NULL;
char *cbuf = NULL;
if (priv_check(curthread, PRIV_DRIVER) != 0)
return EPERM;
if (accel_dev == NULL) {
return EINVAL;
}
cbuf = malloc(ADF_FW_COUNTERS_BUF_SZ, M_QAT, M_WAITOK | M_ZERO);
sbuf = sbuf_new(NULL, cbuf, ADF_FW_COUNTERS_BUF_SZ, SBUF_FIXEDLEN);
if (sbuf == NULL) {
free(cbuf, M_QAT);
return ENOMEM;
}
ret = adf_get_fw_counters(accel_dev);
if (ret) {
sbuf_delete(sbuf);
free(cbuf, M_QAT);
return ret;
}
sbuf_printf(sbuf,
"\n+------------------------------------------------+\n");
sbuf_printf(
sbuf,
"| FW Statistics for Qat Device |\n");
sbuf_printf(sbuf,
"+------------------------------------------------+\n");
list_for_each_prev_safe(list,
tmp,
&accel_dev->fw_counters_data->ae_sec_list)
{
ptr = list_entry(list, struct adf_fw_counters_section, list);
sbuf_printf(sbuf, "%s\n", ptr->name);
list_for_each_prev_safe(list_ptr, tmp_val, &ptr->param_head)
{
struct adf_fw_counters_val *count =
list_entry(list_ptr,
struct adf_fw_counters_val,
list);
sbuf_printf(sbuf, "%s:%s\n", count->key, count->val);
}
}
sbuf_finish(sbuf);
ret = SYSCTL_OUT(req, sbuf_data(sbuf), sbuf_len(sbuf));
sbuf_delete(sbuf);
free(cbuf, M_QAT);
return ret;
}
int
adf_fw_count_ras_event(struct adf_accel_dev *accel_dev,
u32 *ras_event,
char *aeidstr)
{
unsigned long count = 0;
if (!accel_dev || !ras_event || !aeidstr)
return EINVAL;
count = (*ras_event == ADF_FW_COUNTERS_NO_RESPONSE ?
ADF_FW_COUNTERS_NO_RESPONSE :
(unsigned long)*ras_event);
return adf_fw_counters_add_key_value_param(
accel_dev, aeidstr, 16, ADF_RAS_EVENT_STR, (void *)&count);
}
int
adf_fw_counters_add(struct adf_accel_dev *accel_dev)
{
struct adf_fw_counters_data *fw_counters_data;
struct sysctl_ctx_list *qat_sysctl_ctx;
struct sysctl_oid *qat_sysctl_tree;
fw_counters_data =
malloc(sizeof(*fw_counters_data), M_QAT, M_WAITOK | M_ZERO);
INIT_LIST_HEAD(&fw_counters_data->ae_sec_list);
init_rwsem(&fw_counters_data->lock);
accel_dev->fw_counters_data = fw_counters_data;
qat_sysctl_ctx =
device_get_sysctl_ctx(accel_dev->accel_pci_dev.pci_dev);
qat_sysctl_tree =
device_get_sysctl_tree(accel_dev->accel_pci_dev.pci_dev);
fw_counters_data->debug =
SYSCTL_ADD_OID(qat_sysctl_ctx,
SYSCTL_CHILDREN(qat_sysctl_tree),
OID_AUTO,
"fw_counters",
CTLTYPE_STRING | CTLFLAG_RD,
accel_dev,
0,
adf_read_fw_counters,
"A",
"QAT FW counters");
if (!fw_counters_data->debug) {
free(fw_counters_data, M_QAT);
accel_dev->fw_counters_data = NULL;
return ENOMEM;
}
return 0;
}
static void
adf_fw_counters_del_all(struct adf_accel_dev *accel_dev)
{
struct adf_fw_counters_data *fw_counters_data =
accel_dev->fw_counters_data;
down_write(&fw_counters_data->lock);
adf_fw_counters_section_del_all(&fw_counters_data->ae_sec_list);
up_write(&fw_counters_data->lock);
}
static void
adf_fw_counters_keyval_add(struct adf_fw_counters_val *new,
struct adf_fw_counters_section *sec)
{
list_add_tail(&new->list, &sec->param_head);
}
static void
adf_fw_counters_keyval_del_all(struct list_head *head)
{
struct list_head *list_ptr = NULL, *tmp = NULL;
list_for_each_prev_safe(list_ptr, tmp, head)
{
struct adf_fw_counters_val *ptr =
list_entry(list_ptr, struct adf_fw_counters_val, list);
list_del(list_ptr);
free(ptr, M_QAT);
}
}
static void
adf_fw_counters_section_del_all(struct list_head *head)
{
struct adf_fw_counters_section *ptr = NULL;
struct list_head *list = NULL, *tmp = NULL;
list_for_each_prev_safe(list, tmp, head)
{
ptr = list_entry(list, struct adf_fw_counters_section, list);
adf_fw_counters_keyval_del_all(&ptr->param_head);
list_del(list);
free(ptr, M_QAT);
}
}
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)
{
struct adf_fw_counters_data *fw_counters_data =
accel_dev->fw_counters_data;
struct list_head *list = NULL;
list_for_each(list, &fw_counters_data->ae_sec_list)
{
struct adf_fw_counters_section *ptr =
list_entry(list, struct adf_fw_counters_section, list);
if (!strncmp(ptr->name, sec_name, sec_name_max_size))
return ptr;
}
return NULL;
}
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)
{
struct adf_fw_counters_data *fw_counters_data =
accel_dev->fw_counters_data;
struct adf_fw_counters_val *key_val;
struct adf_fw_counters_section *section =
adf_fw_counters_sec_find(accel_dev,
section_name,
sec_name_max_size);
long tmp = *((const long *)val);
if (!section)
return EFAULT;
key_val = malloc(sizeof(*key_val), M_QAT, M_WAITOK | M_ZERO);
INIT_LIST_HEAD(&key_val->list);
if (tmp == ADF_FW_COUNTERS_NO_RESPONSE) {
snprintf(key_val->val,
FW_COUNTERS_MAX_VAL_LEN_IN_BYTES,
"No Response");
} else {
snprintf(key_val->val,
FW_COUNTERS_MAX_VAL_LEN_IN_BYTES,
"%ld",
tmp);
}
strlcpy(key_val->key, key, sizeof(key_val->key));
down_write(&fw_counters_data->lock);
adf_fw_counters_keyval_add(key_val, section);
up_write(&fw_counters_data->lock);
return 0;
}
static int
adf_fw_counters_section_add(struct adf_accel_dev *accel_dev,
const char *name,
const unsigned long name_max_size)
{
struct adf_fw_counters_data *fw_counters_data =
accel_dev->fw_counters_data;
struct adf_fw_counters_section *sec =
adf_fw_counters_sec_find(accel_dev, name, name_max_size);
if (sec)
return 0;
sec = malloc(sizeof(*sec), M_QAT, M_WAITOK | M_ZERO);
strlcpy(sec->name, name, sizeof(sec->name));
INIT_LIST_HEAD(&sec->param_head);
down_write(&fw_counters_data->lock);
list_add_tail(&sec->list, &fw_counters_data->ae_sec_list);
up_write(&fw_counters_data->lock);
return 0;
}
void
adf_fw_counters_remove(struct adf_accel_dev *accel_dev)
{
struct sysctl_ctx_list *qat_sysctl_ctx;
struct adf_fw_counters_data *fw_counters_data =
accel_dev->fw_counters_data;
if (!fw_counters_data)
return;
if (fw_counters_data->debug) {
qat_sysctl_ctx =
device_get_sysctl_ctx(accel_dev->accel_pci_dev.pci_dev);
sysctl_ctx_entry_del(qat_sysctl_ctx, fw_counters_data->debug);
sysctl_remove_oid(fw_counters_data->debug, 1, 1);
fw_counters_data->debug = NULL;
}
down_write(&fw_counters_data->lock);
adf_fw_counters_section_del_all(&fw_counters_data->ae_sec_list);
up_write(&fw_counters_data->lock);
free(fw_counters_data, M_QAT);
accel_dev->fw_counters_data = NULL;
}