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 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 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 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 */ 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 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 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 162 static void qat_fw_counters_seq_stop(struct seq_file *sfile, void *v) {} 163 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 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 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 */ 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 */ 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