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 
18 enum adf_fw_counters_types {
19 	ADF_FW_REQUESTS,
20 	ADF_FW_RESPONSES,
21 	ADF_FW_COUNTERS_COUNT
22 };
23 
24 static const char * const adf_fw_counter_names[] = {
25 	[ADF_FW_REQUESTS] = "Requests",
26 	[ADF_FW_RESPONSES] = "Responses",
27 };
28 
29 static_assert(ARRAY_SIZE(adf_fw_counter_names) == ADF_FW_COUNTERS_COUNT);
30 
31 struct adf_ae_counters {
32 	u16 ae;
33 	u64 values[ADF_FW_COUNTERS_COUNT];
34 };
35 
36 struct adf_fw_counters {
37 	u16 ae_count;
38 	struct adf_ae_counters ae_counters[] __counted_by(ae_count);
39 };
40 
adf_fw_counters_parse_ae_values(struct adf_ae_counters * ae_counters,u32 ae,u64 req_count,u64 resp_count)41 static 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 
adf_fw_counters_load_from_device(struct adf_accel_dev * accel_dev,struct adf_fw_counters * fw_counters)49 static 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 
adf_fw_counters_allocate(unsigned long ae_count)79 static 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  */
adf_fw_counters_get(struct adf_accel_dev * accel_dev)107 static 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 
qat_fw_counters_seq_start(struct seq_file * sfile,loff_t * pos)137 static 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 
qat_fw_counters_seq_next(struct seq_file * sfile,void * v,loff_t * pos)150 static 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 
qat_fw_counters_seq_stop(struct seq_file * sfile,void * v)162 static void qat_fw_counters_seq_stop(struct seq_file *sfile, void *v) {}
163 
qat_fw_counters_seq_show(struct seq_file * sfile,void * v)164 static 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 
186 static 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 
qat_fw_counters_file_open(struct inode * inode,struct file * file)193 static 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 
qat_fw_counters_file_release(struct inode * inode,struct file * file)215 static 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 
224 static 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  */
adf_fw_counters_dbgfs_add(struct adf_accel_dev * accel_dev)244 void 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  */
adf_fw_counters_dbgfs_rm(struct adf_accel_dev * accel_dev)261 void 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