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