1865b50feSLucas Segarra Fernandez // SPDX-License-Identifier: GPL-2.0-only
2865b50feSLucas Segarra Fernandez /* Copyright(c) 2023 Intel Corporation */
3865b50feSLucas Segarra Fernandez #include <linux/bitops.h>
4865b50feSLucas Segarra Fernandez #include <linux/debugfs.h>
5865b50feSLucas Segarra Fernandez #include <linux/err.h>
6865b50feSLucas Segarra Fernandez #include <linux/fs.h>
7865b50feSLucas Segarra Fernandez #include <linux/kernel.h>
8865b50feSLucas Segarra Fernandez #include <linux/seq_file.h>
9865b50feSLucas Segarra Fernandez #include <linux/types.h>
10865b50feSLucas Segarra Fernandez 
11865b50feSLucas Segarra Fernandez #include "adf_accel_devices.h"
12*8e6857f7SGiovanni Cabiddu #include "adf_admin.h"
13865b50feSLucas Segarra Fernandez #include "adf_common_drv.h"
14865b50feSLucas Segarra Fernandez #include "adf_fw_counters.h"
15865b50feSLucas Segarra Fernandez 
16865b50feSLucas Segarra Fernandez #define ADF_FW_COUNTERS_MAX_PADDING 16
17865b50feSLucas Segarra Fernandez 
18865b50feSLucas Segarra Fernandez enum adf_fw_counters_types {
19865b50feSLucas Segarra Fernandez 	ADF_FW_REQUESTS,
20865b50feSLucas Segarra Fernandez 	ADF_FW_RESPONSES,
21865b50feSLucas Segarra Fernandez 	ADF_FW_COUNTERS_COUNT
22865b50feSLucas Segarra Fernandez };
23865b50feSLucas Segarra Fernandez 
24865b50feSLucas Segarra Fernandez static const char * const adf_fw_counter_names[] = {
25865b50feSLucas Segarra Fernandez 	[ADF_FW_REQUESTS] = "Requests",
26865b50feSLucas Segarra Fernandez 	[ADF_FW_RESPONSES] = "Responses",
27865b50feSLucas Segarra Fernandez };
28865b50feSLucas Segarra Fernandez 
29865b50feSLucas Segarra Fernandez static_assert(ARRAY_SIZE(adf_fw_counter_names) == ADF_FW_COUNTERS_COUNT);
30865b50feSLucas Segarra Fernandez 
31865b50feSLucas Segarra Fernandez struct adf_ae_counters {
32865b50feSLucas Segarra Fernandez 	u16 ae;
33865b50feSLucas Segarra Fernandez 	u64 values[ADF_FW_COUNTERS_COUNT];
34865b50feSLucas Segarra Fernandez };
35865b50feSLucas Segarra Fernandez 
36865b50feSLucas Segarra Fernandez struct adf_fw_counters {
37865b50feSLucas Segarra Fernandez 	u16 ae_count;
38141f12beSKees Cook 	struct adf_ae_counters ae_counters[] __counted_by(ae_count);
39865b50feSLucas Segarra Fernandez };
40865b50feSLucas Segarra Fernandez 
adf_fw_counters_parse_ae_values(struct adf_ae_counters * ae_counters,u32 ae,u64 req_count,u64 resp_count)41865b50feSLucas Segarra Fernandez static void adf_fw_counters_parse_ae_values(struct adf_ae_counters *ae_counters, u32 ae,
42865b50feSLucas Segarra Fernandez 					    u64 req_count, u64 resp_count)
43865b50feSLucas Segarra Fernandez {
44865b50feSLucas Segarra Fernandez 	ae_counters->ae = ae;
45865b50feSLucas Segarra Fernandez 	ae_counters->values[ADF_FW_REQUESTS] = req_count;
46865b50feSLucas Segarra Fernandez 	ae_counters->values[ADF_FW_RESPONSES] = resp_count;
47865b50feSLucas Segarra Fernandez }
48865b50feSLucas Segarra Fernandez 
adf_fw_counters_load_from_device(struct adf_accel_dev * accel_dev,struct adf_fw_counters * fw_counters)49865b50feSLucas Segarra Fernandez static int adf_fw_counters_load_from_device(struct adf_accel_dev *accel_dev,
50865b50feSLucas Segarra Fernandez 					    struct adf_fw_counters *fw_counters)
51865b50feSLucas Segarra Fernandez {
52865b50feSLucas Segarra Fernandez 	struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
53865b50feSLucas Segarra Fernandez 	unsigned long ae_mask;
54865b50feSLucas Segarra Fernandez 	unsigned int i;
55865b50feSLucas Segarra Fernandez 	unsigned long ae;
56865b50feSLucas Segarra Fernandez 
57865b50feSLucas Segarra Fernandez 	/* Ignore the admin AEs */
58865b50feSLucas Segarra Fernandez 	ae_mask = hw_data->ae_mask & ~hw_data->admin_ae_mask;
59865b50feSLucas Segarra Fernandez 
60865b50feSLucas Segarra Fernandez 	if (hweight_long(ae_mask) > fw_counters->ae_count)
61865b50feSLucas Segarra Fernandez 		return -EINVAL;
62865b50feSLucas Segarra Fernandez 
63865b50feSLucas Segarra Fernandez 	i = 0;
64865b50feSLucas Segarra Fernandez 	for_each_set_bit(ae, &ae_mask, GET_MAX_ACCELENGINES(accel_dev)) {
65865b50feSLucas Segarra Fernandez 		u64 req_count, resp_count;
66865b50feSLucas Segarra Fernandez 		int ret;
67865b50feSLucas Segarra Fernandez 
68865b50feSLucas Segarra Fernandez 		ret = adf_get_ae_fw_counters(accel_dev, ae, &req_count, &resp_count);
69865b50feSLucas Segarra Fernandez 		if (ret)
70865b50feSLucas Segarra Fernandez 			return ret;
71865b50feSLucas Segarra Fernandez 
72865b50feSLucas Segarra Fernandez 		adf_fw_counters_parse_ae_values(&fw_counters->ae_counters[i++], ae,
73865b50feSLucas Segarra Fernandez 						req_count, resp_count);
74865b50feSLucas Segarra Fernandez 	}
75865b50feSLucas Segarra Fernandez 
76865b50feSLucas Segarra Fernandez 	return 0;
77865b50feSLucas Segarra Fernandez }
78865b50feSLucas Segarra Fernandez 
adf_fw_counters_allocate(unsigned long ae_count)79865b50feSLucas Segarra Fernandez static struct adf_fw_counters *adf_fw_counters_allocate(unsigned long ae_count)
80865b50feSLucas Segarra Fernandez {
81865b50feSLucas Segarra Fernandez 	struct adf_fw_counters *fw_counters;
82865b50feSLucas Segarra Fernandez 
83865b50feSLucas Segarra Fernandez 	if (unlikely(!ae_count))
84865b50feSLucas Segarra Fernandez 		return ERR_PTR(-EINVAL);
85865b50feSLucas Segarra Fernandez 
86865b50feSLucas Segarra Fernandez 	fw_counters = kmalloc(struct_size(fw_counters, ae_counters, ae_count), GFP_KERNEL);
87865b50feSLucas Segarra Fernandez 	if (!fw_counters)
88865b50feSLucas Segarra Fernandez 		return ERR_PTR(-ENOMEM);
89865b50feSLucas Segarra Fernandez 
90865b50feSLucas Segarra Fernandez 	fw_counters->ae_count = ae_count;
91865b50feSLucas Segarra Fernandez 
92865b50feSLucas Segarra Fernandez 	return fw_counters;
93865b50feSLucas Segarra Fernandez }
94865b50feSLucas Segarra Fernandez 
95865b50feSLucas Segarra Fernandez /**
96865b50feSLucas Segarra Fernandez  * adf_fw_counters_get() - Return FW counters for the provided device.
97865b50feSLucas Segarra Fernandez  * @accel_dev: Pointer to a QAT acceleration device
98865b50feSLucas Segarra Fernandez  *
99865b50feSLucas Segarra Fernandez  * Allocates and returns a table of counters containing execution statistics
100865b50feSLucas Segarra Fernandez  * for each non-admin AE available through the supplied acceleration device.
101865b50feSLucas Segarra Fernandez  * The caller becomes the owner of such memory and is responsible for
102865b50feSLucas Segarra Fernandez  * the deallocation through a call to kfree().
103865b50feSLucas Segarra Fernandez  *
104865b50feSLucas Segarra Fernandez  * Returns: a pointer to a dynamically allocated struct adf_fw_counters
105865b50feSLucas Segarra Fernandez  *          on success, or a negative value on error.
106865b50feSLucas Segarra Fernandez  */
adf_fw_counters_get(struct adf_accel_dev * accel_dev)107865b50feSLucas Segarra Fernandez static struct adf_fw_counters *adf_fw_counters_get(struct adf_accel_dev *accel_dev)
108865b50feSLucas Segarra Fernandez {
109865b50feSLucas Segarra Fernandez 	struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
110865b50feSLucas Segarra Fernandez 	struct adf_fw_counters *fw_counters;
111865b50feSLucas Segarra Fernandez 	unsigned long ae_count;
112865b50feSLucas Segarra Fernandez 	int ret;
113865b50feSLucas Segarra Fernandez 
114865b50feSLucas Segarra Fernandez 	if (!adf_dev_started(accel_dev)) {
115865b50feSLucas Segarra Fernandez 		dev_err(&GET_DEV(accel_dev), "QAT Device not started\n");
116865b50feSLucas Segarra Fernandez 		return ERR_PTR(-EFAULT);
117865b50feSLucas Segarra Fernandez 	}
118865b50feSLucas Segarra Fernandez 
119865b50feSLucas Segarra Fernandez 	/* Ignore the admin AEs */
120865b50feSLucas Segarra Fernandez 	ae_count = hweight_long(hw_data->ae_mask & ~hw_data->admin_ae_mask);
121865b50feSLucas Segarra Fernandez 
122865b50feSLucas Segarra Fernandez 	fw_counters = adf_fw_counters_allocate(ae_count);
123865b50feSLucas Segarra Fernandez 	if (IS_ERR(fw_counters))
124865b50feSLucas Segarra Fernandez 		return fw_counters;
125865b50feSLucas Segarra Fernandez 
126865b50feSLucas Segarra Fernandez 	ret = adf_fw_counters_load_from_device(accel_dev, fw_counters);
127865b50feSLucas Segarra Fernandez 	if (ret) {
128865b50feSLucas Segarra Fernandez 		kfree(fw_counters);
129865b50feSLucas Segarra Fernandez 		dev_err(&GET_DEV(accel_dev),
130865b50feSLucas Segarra Fernandez 			"Failed to create QAT fw_counters file table [%d].\n", ret);
131865b50feSLucas Segarra Fernandez 		return ERR_PTR(ret);
132865b50feSLucas Segarra Fernandez 	}
133865b50feSLucas Segarra Fernandez 
134865b50feSLucas Segarra Fernandez 	return fw_counters;
135865b50feSLucas Segarra Fernandez }
136865b50feSLucas Segarra Fernandez 
qat_fw_counters_seq_start(struct seq_file * sfile,loff_t * pos)137865b50feSLucas Segarra Fernandez static void *qat_fw_counters_seq_start(struct seq_file *sfile, loff_t *pos)
138865b50feSLucas Segarra Fernandez {
139865b50feSLucas Segarra Fernandez 	struct adf_fw_counters *fw_counters = sfile->private;
140865b50feSLucas Segarra Fernandez 
141865b50feSLucas Segarra Fernandez 	if (*pos == 0)
142865b50feSLucas Segarra Fernandez 		return SEQ_START_TOKEN;
143865b50feSLucas Segarra Fernandez 
144865b50feSLucas Segarra Fernandez 	if (*pos > fw_counters->ae_count)
145865b50feSLucas Segarra Fernandez 		return NULL;
146865b50feSLucas Segarra Fernandez 
147865b50feSLucas Segarra Fernandez 	return &fw_counters->ae_counters[*pos - 1];
148865b50feSLucas Segarra Fernandez }
149865b50feSLucas Segarra Fernandez 
qat_fw_counters_seq_next(struct seq_file * sfile,void * v,loff_t * pos)150865b50feSLucas Segarra Fernandez static void *qat_fw_counters_seq_next(struct seq_file *sfile, void *v, loff_t *pos)
151865b50feSLucas Segarra Fernandez {
152865b50feSLucas Segarra Fernandez 	struct adf_fw_counters *fw_counters = sfile->private;
153865b50feSLucas Segarra Fernandez 
154865b50feSLucas Segarra Fernandez 	(*pos)++;
155865b50feSLucas Segarra Fernandez 
156865b50feSLucas Segarra Fernandez 	if (*pos > fw_counters->ae_count)
157865b50feSLucas Segarra Fernandez 		return NULL;
158865b50feSLucas Segarra Fernandez 
159865b50feSLucas Segarra Fernandez 	return &fw_counters->ae_counters[*pos - 1];
160865b50feSLucas Segarra Fernandez }
161865b50feSLucas Segarra Fernandez 
qat_fw_counters_seq_stop(struct seq_file * sfile,void * v)162865b50feSLucas Segarra Fernandez static void qat_fw_counters_seq_stop(struct seq_file *sfile, void *v) {}
163865b50feSLucas Segarra Fernandez 
qat_fw_counters_seq_show(struct seq_file * sfile,void * v)164865b50feSLucas Segarra Fernandez static int qat_fw_counters_seq_show(struct seq_file *sfile, void *v)
165865b50feSLucas Segarra Fernandez {
166865b50feSLucas Segarra Fernandez 	int i;
167865b50feSLucas Segarra Fernandez 
168865b50feSLucas Segarra Fernandez 	if (v == SEQ_START_TOKEN) {
169865b50feSLucas Segarra Fernandez 		seq_puts(sfile, "AE ");
170865b50feSLucas Segarra Fernandez 		for (i = 0; i < ADF_FW_COUNTERS_COUNT; ++i)
171865b50feSLucas Segarra Fernandez 			seq_printf(sfile, " %*s", ADF_FW_COUNTERS_MAX_PADDING,
172865b50feSLucas Segarra Fernandez 				   adf_fw_counter_names[i]);
173865b50feSLucas Segarra Fernandez 	} else {
174865b50feSLucas Segarra Fernandez 		struct adf_ae_counters *ae_counters = (struct adf_ae_counters *)v;
175865b50feSLucas Segarra Fernandez 
176865b50feSLucas Segarra Fernandez 		seq_printf(sfile, "%2d:", ae_counters->ae);
177865b50feSLucas Segarra Fernandez 		for (i = 0; i < ADF_FW_COUNTERS_COUNT; ++i)
178865b50feSLucas Segarra Fernandez 			seq_printf(sfile, " %*llu", ADF_FW_COUNTERS_MAX_PADDING,
179865b50feSLucas Segarra Fernandez 				   ae_counters->values[i]);
180865b50feSLucas Segarra Fernandez 	}
181865b50feSLucas Segarra Fernandez 	seq_putc(sfile, '\n');
182865b50feSLucas Segarra Fernandez 
183865b50feSLucas Segarra Fernandez 	return 0;
184865b50feSLucas Segarra Fernandez }
185865b50feSLucas Segarra Fernandez 
186865b50feSLucas Segarra Fernandez static const struct seq_operations qat_fw_counters_sops = {
187865b50feSLucas Segarra Fernandez 	.start = qat_fw_counters_seq_start,
188865b50feSLucas Segarra Fernandez 	.next = qat_fw_counters_seq_next,
189865b50feSLucas Segarra Fernandez 	.stop = qat_fw_counters_seq_stop,
190865b50feSLucas Segarra Fernandez 	.show = qat_fw_counters_seq_show,
191865b50feSLucas Segarra Fernandez };
192865b50feSLucas Segarra Fernandez 
qat_fw_counters_file_open(struct inode * inode,struct file * file)193865b50feSLucas Segarra Fernandez static int qat_fw_counters_file_open(struct inode *inode, struct file *file)
194865b50feSLucas Segarra Fernandez {
195865b50feSLucas Segarra Fernandez 	struct adf_accel_dev *accel_dev = inode->i_private;
196865b50feSLucas Segarra Fernandez 	struct seq_file *fw_counters_seq_file;
197865b50feSLucas Segarra Fernandez 	struct adf_fw_counters *fw_counters;
198865b50feSLucas Segarra Fernandez 	int ret;
199865b50feSLucas Segarra Fernandez 
200865b50feSLucas Segarra Fernandez 	fw_counters = adf_fw_counters_get(accel_dev);
201865b50feSLucas Segarra Fernandez 	if (IS_ERR(fw_counters))
202865b50feSLucas Segarra Fernandez 		return PTR_ERR(fw_counters);
203865b50feSLucas Segarra Fernandez 
204865b50feSLucas Segarra Fernandez 	ret = seq_open(file, &qat_fw_counters_sops);
205865b50feSLucas Segarra Fernandez 	if (unlikely(ret)) {
206865b50feSLucas Segarra Fernandez 		kfree(fw_counters);
207865b50feSLucas Segarra Fernandez 		return ret;
208865b50feSLucas Segarra Fernandez 	}
209865b50feSLucas Segarra Fernandez 
210865b50feSLucas Segarra Fernandez 	fw_counters_seq_file = file->private_data;
211865b50feSLucas Segarra Fernandez 	fw_counters_seq_file->private = fw_counters;
212865b50feSLucas Segarra Fernandez 	return ret;
213865b50feSLucas Segarra Fernandez }
214865b50feSLucas Segarra Fernandez 
qat_fw_counters_file_release(struct inode * inode,struct file * file)215865b50feSLucas Segarra Fernandez static int qat_fw_counters_file_release(struct inode *inode, struct file *file)
216865b50feSLucas Segarra Fernandez {
217865b50feSLucas Segarra Fernandez 	struct seq_file *seq = file->private_data;
218865b50feSLucas Segarra Fernandez 
219865b50feSLucas Segarra Fernandez 	kfree(seq->private);
220865b50feSLucas Segarra Fernandez 	seq->private = NULL;
221865b50feSLucas Segarra Fernandez 
222865b50feSLucas Segarra Fernandez 	return seq_release(inode, file); }
223865b50feSLucas Segarra Fernandez 
224865b50feSLucas Segarra Fernandez static const struct file_operations qat_fw_counters_fops = {
225865b50feSLucas Segarra Fernandez 	.owner = THIS_MODULE,
226865b50feSLucas Segarra Fernandez 	.open = qat_fw_counters_file_open,
227865b50feSLucas Segarra Fernandez 	.read = seq_read,
228865b50feSLucas Segarra Fernandez 	.llseek = seq_lseek,
229865b50feSLucas Segarra Fernandez 	.release = qat_fw_counters_file_release,
230865b50feSLucas Segarra Fernandez };
231865b50feSLucas Segarra Fernandez 
232865b50feSLucas Segarra Fernandez /**
233865b50feSLucas Segarra Fernandez  * adf_fw_counters_dbgfs_add() - Create a debugfs file containing FW
234865b50feSLucas Segarra Fernandez  * execution counters.
235865b50feSLucas Segarra Fernandez  * @accel_dev:  Pointer to a QAT acceleration device
236865b50feSLucas Segarra Fernandez  *
237865b50feSLucas Segarra Fernandez  * Function creates a file to display a table with statistics for the given
238865b50feSLucas Segarra Fernandez  * QAT acceleration device. The table stores device specific execution values
239865b50feSLucas Segarra Fernandez  * for each AE, such as the number of requests sent to the FW and responses
240865b50feSLucas Segarra Fernandez  * received from the FW.
241865b50feSLucas Segarra Fernandez  *
242865b50feSLucas Segarra Fernandez  * Return: void
243865b50feSLucas Segarra Fernandez  */
adf_fw_counters_dbgfs_add(struct adf_accel_dev * accel_dev)244865b50feSLucas Segarra Fernandez void adf_fw_counters_dbgfs_add(struct adf_accel_dev *accel_dev)
245865b50feSLucas Segarra Fernandez {
246865b50feSLucas Segarra Fernandez 	accel_dev->fw_cntr_dbgfile = debugfs_create_file("fw_counters", 0400,
247865b50feSLucas Segarra Fernandez 							 accel_dev->debugfs_dir,
248865b50feSLucas Segarra Fernandez 							 accel_dev,
249865b50feSLucas Segarra Fernandez 							 &qat_fw_counters_fops);
250865b50feSLucas Segarra Fernandez }
251865b50feSLucas Segarra Fernandez 
252865b50feSLucas Segarra Fernandez /**
253865b50feSLucas Segarra Fernandez  * adf_fw_counters_dbgfs_rm() - Remove the debugfs file containing FW counters.
254865b50feSLucas Segarra Fernandez  * @accel_dev:  Pointer to a QAT acceleration device.
255865b50feSLucas Segarra Fernandez  *
256865b50feSLucas Segarra Fernandez  * Function removes the file providing the table of statistics for the given
257865b50feSLucas Segarra Fernandez  * QAT acceleration device.
258865b50feSLucas Segarra Fernandez  *
259865b50feSLucas Segarra Fernandez  * Return: void
260865b50feSLucas Segarra Fernandez  */
adf_fw_counters_dbgfs_rm(struct adf_accel_dev * accel_dev)261865b50feSLucas Segarra Fernandez void adf_fw_counters_dbgfs_rm(struct adf_accel_dev *accel_dev)
262865b50feSLucas Segarra Fernandez {
263865b50feSLucas Segarra Fernandez 	debugfs_remove(accel_dev->fw_cntr_dbgfile);
264865b50feSLucas Segarra Fernandez 	accel_dev->fw_cntr_dbgfile = NULL;
265865b50feSLucas Segarra Fernandez }
266