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