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