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