Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.4.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/* 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 16
 17
 18enum adf_fw_counters_types {
 19	ADF_FW_REQUESTS,
 20	ADF_FW_RESPONSES,
 21	ADF_FW_COUNTERS_COUNT
 22};
 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 device
 98 *
 99 * Allocates and returns a table of counters containing execution statistics
100 * for each non-admin AE available through the supplied acceleration device.
101 * The caller becomes the owner of such memory and is responsible for
102 * the deallocation through a call to kfree().
103 *
104 * Returns: a pointer to a dynamically allocated struct adf_fw_counters
105 *          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 FW
234 * execution counters.
235 * @accel_dev:  Pointer to a QAT acceleration device
236 *
237 * Function creates a file to display a table with statistics for the given
238 * QAT acceleration device. The table stores device specific execution values
239 * for each AE, such as the number of requests sent to the FW and responses
240 * received from the FW.
241 *
242 * Return: void
243 */
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 given
257 * QAT acceleration device.
258 *
259 * Return: void
260 */
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}