266 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* Copyright(c) 2023 Intel Corporation */3#include <linux/bitops.h>4#include <linux/debugfs.h>5#include <linux/err.h>6#include <linux/fs.h>7#include <linux/kernel.h>8#include <linux/seq_file.h>9#include <linux/types.h>10 11#include "adf_accel_devices.h"12#include "adf_admin.h"13#include "adf_common_drv.h"14#include "adf_fw_counters.h"15 16#define ADF_FW_COUNTERS_MAX_PADDING 1617 18enum adf_fw_counters_types {19 ADF_FW_REQUESTS,20 ADF_FW_RESPONSES,21 ADF_FW_COUNTERS_COUNT22};23 24static const char * const adf_fw_counter_names[] = {25 [ADF_FW_REQUESTS] = "Requests",26 [ADF_FW_RESPONSES] = "Responses",27};28 29static_assert(ARRAY_SIZE(adf_fw_counter_names) == ADF_FW_COUNTERS_COUNT);30 31struct adf_ae_counters {32 u16 ae;33 u64 values[ADF_FW_COUNTERS_COUNT];34};35 36struct adf_fw_counters {37 u16 ae_count;38 struct adf_ae_counters ae_counters[] __counted_by(ae_count);39};40 41static void adf_fw_counters_parse_ae_values(struct adf_ae_counters *ae_counters, u32 ae,42 u64 req_count, u64 resp_count)43{44 ae_counters->ae = ae;45 ae_counters->values[ADF_FW_REQUESTS] = req_count;46 ae_counters->values[ADF_FW_RESPONSES] = resp_count;47}48 49static int adf_fw_counters_load_from_device(struct adf_accel_dev *accel_dev,50 struct adf_fw_counters *fw_counters)51{52 struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);53 unsigned long ae_mask;54 unsigned int i;55 unsigned long ae;56 57 /* Ignore the admin AEs */58 ae_mask = hw_data->ae_mask & ~hw_data->admin_ae_mask;59 60 if (hweight_long(ae_mask) > fw_counters->ae_count)61 return -EINVAL;62 63 i = 0;64 for_each_set_bit(ae, &ae_mask, GET_MAX_ACCELENGINES(accel_dev)) {65 u64 req_count, resp_count;66 int ret;67 68 ret = adf_get_ae_fw_counters(accel_dev, ae, &req_count, &resp_count);69 if (ret)70 return ret;71 72 adf_fw_counters_parse_ae_values(&fw_counters->ae_counters[i++], ae,73 req_count, resp_count);74 }75 76 return 0;77}78 79static struct adf_fw_counters *adf_fw_counters_allocate(unsigned long ae_count)80{81 struct adf_fw_counters *fw_counters;82 83 if (unlikely(!ae_count))84 return ERR_PTR(-EINVAL);85 86 fw_counters = kmalloc(struct_size(fw_counters, ae_counters, ae_count), GFP_KERNEL);87 if (!fw_counters)88 return ERR_PTR(-ENOMEM);89 90 fw_counters->ae_count = ae_count;91 92 return fw_counters;93}94 95/**96 * adf_fw_counters_get() - Return FW counters for the provided device.97 * @accel_dev: Pointer to a QAT acceleration device98 *99 * Allocates and returns a table of counters containing execution statistics100 * for each non-admin AE available through the supplied acceleration device.101 * The caller becomes the owner of such memory and is responsible for102 * the deallocation through a call to kfree().103 *104 * Returns: a pointer to a dynamically allocated struct adf_fw_counters105 * on success, or a negative value on error.106 */107static struct adf_fw_counters *adf_fw_counters_get(struct adf_accel_dev *accel_dev)108{109 struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);110 struct adf_fw_counters *fw_counters;111 unsigned long ae_count;112 int ret;113 114 if (!adf_dev_started(accel_dev)) {115 dev_err(&GET_DEV(accel_dev), "QAT Device not started\n");116 return ERR_PTR(-EFAULT);117 }118 119 /* Ignore the admin AEs */120 ae_count = hweight_long(hw_data->ae_mask & ~hw_data->admin_ae_mask);121 122 fw_counters = adf_fw_counters_allocate(ae_count);123 if (IS_ERR(fw_counters))124 return fw_counters;125 126 ret = adf_fw_counters_load_from_device(accel_dev, fw_counters);127 if (ret) {128 kfree(fw_counters);129 dev_err(&GET_DEV(accel_dev),130 "Failed to create QAT fw_counters file table [%d].\n", ret);131 return ERR_PTR(ret);132 }133 134 return fw_counters;135}136 137static void *qat_fw_counters_seq_start(struct seq_file *sfile, loff_t *pos)138{139 struct adf_fw_counters *fw_counters = sfile->private;140 141 if (*pos == 0)142 return SEQ_START_TOKEN;143 144 if (*pos > fw_counters->ae_count)145 return NULL;146 147 return &fw_counters->ae_counters[*pos - 1];148}149 150static void *qat_fw_counters_seq_next(struct seq_file *sfile, void *v, loff_t *pos)151{152 struct adf_fw_counters *fw_counters = sfile->private;153 154 (*pos)++;155 156 if (*pos > fw_counters->ae_count)157 return NULL;158 159 return &fw_counters->ae_counters[*pos - 1];160}161 162static void qat_fw_counters_seq_stop(struct seq_file *sfile, void *v) {}163 164static int qat_fw_counters_seq_show(struct seq_file *sfile, void *v)165{166 int i;167 168 if (v == SEQ_START_TOKEN) {169 seq_puts(sfile, "AE ");170 for (i = 0; i < ADF_FW_COUNTERS_COUNT; ++i)171 seq_printf(sfile, " %*s", ADF_FW_COUNTERS_MAX_PADDING,172 adf_fw_counter_names[i]);173 } else {174 struct adf_ae_counters *ae_counters = (struct adf_ae_counters *)v;175 176 seq_printf(sfile, "%2d:", ae_counters->ae);177 for (i = 0; i < ADF_FW_COUNTERS_COUNT; ++i)178 seq_printf(sfile, " %*llu", ADF_FW_COUNTERS_MAX_PADDING,179 ae_counters->values[i]);180 }181 seq_putc(sfile, '\n');182 183 return 0;184}185 186static const struct seq_operations qat_fw_counters_sops = {187 .start = qat_fw_counters_seq_start,188 .next = qat_fw_counters_seq_next,189 .stop = qat_fw_counters_seq_stop,190 .show = qat_fw_counters_seq_show,191};192 193static int qat_fw_counters_file_open(struct inode *inode, struct file *file)194{195 struct adf_accel_dev *accel_dev = inode->i_private;196 struct seq_file *fw_counters_seq_file;197 struct adf_fw_counters *fw_counters;198 int ret;199 200 fw_counters = adf_fw_counters_get(accel_dev);201 if (IS_ERR(fw_counters))202 return PTR_ERR(fw_counters);203 204 ret = seq_open(file, &qat_fw_counters_sops);205 if (unlikely(ret)) {206 kfree(fw_counters);207 return ret;208 }209 210 fw_counters_seq_file = file->private_data;211 fw_counters_seq_file->private = fw_counters;212 return ret;213}214 215static int qat_fw_counters_file_release(struct inode *inode, struct file *file)216{217 struct seq_file *seq = file->private_data;218 219 kfree(seq->private);220 seq->private = NULL;221 222 return seq_release(inode, file); }223 224static const struct file_operations qat_fw_counters_fops = {225 .owner = THIS_MODULE,226 .open = qat_fw_counters_file_open,227 .read = seq_read,228 .llseek = seq_lseek,229 .release = qat_fw_counters_file_release,230};231 232/**233 * adf_fw_counters_dbgfs_add() - Create a debugfs file containing FW234 * execution counters.235 * @accel_dev: Pointer to a QAT acceleration device236 *237 * Function creates a file to display a table with statistics for the given238 * QAT acceleration device. The table stores device specific execution values239 * for each AE, such as the number of requests sent to the FW and responses240 * received from the FW.241 *242 * Return: void243 */244void adf_fw_counters_dbgfs_add(struct adf_accel_dev *accel_dev)245{246 accel_dev->fw_cntr_dbgfile = debugfs_create_file("fw_counters", 0400,247 accel_dev->debugfs_dir,248 accel_dev,249 &qat_fw_counters_fops);250}251 252/**253 * adf_fw_counters_dbgfs_rm() - Remove the debugfs file containing FW counters.254 * @accel_dev: Pointer to a QAT acceleration device.255 *256 * Function removes the file providing the table of statistics for the given257 * QAT acceleration device.258 *259 * Return: void260 */261void adf_fw_counters_dbgfs_rm(struct adf_accel_dev *accel_dev)262{263 debugfs_remove(accel_dev->fw_cntr_dbgfile);264 accel_dev->fw_cntr_dbgfile = NULL;265}266