385 lines · c
1// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)2/* Copyright(c) 2014 - 2020 Intel Corporation */3#include <linux/mutex.h>4#include <linux/slab.h>5#include <linux/list.h>6#include <linux/seq_file.h>7#include "adf_accel_devices.h"8#include "adf_cfg.h"9#include "adf_common_drv.h"10 11static DEFINE_MUTEX(qat_cfg_read_lock);12 13static void *qat_dev_cfg_start(struct seq_file *sfile, loff_t *pos)14{15 struct adf_cfg_device_data *dev_cfg = sfile->private;16 17 mutex_lock(&qat_cfg_read_lock);18 return seq_list_start(&dev_cfg->sec_list, *pos);19}20 21static int qat_dev_cfg_show(struct seq_file *sfile, void *v)22{23 struct list_head *list;24 struct adf_cfg_section *sec =25 list_entry(v, struct adf_cfg_section, list);26 27 seq_printf(sfile, "[%s]\n", sec->name);28 list_for_each(list, &sec->param_head) {29 struct adf_cfg_key_val *ptr =30 list_entry(list, struct adf_cfg_key_val, list);31 seq_printf(sfile, "%s = %s\n", ptr->key, ptr->val);32 }33 return 0;34}35 36static void *qat_dev_cfg_next(struct seq_file *sfile, void *v, loff_t *pos)37{38 struct adf_cfg_device_data *dev_cfg = sfile->private;39 40 return seq_list_next(v, &dev_cfg->sec_list, pos);41}42 43static void qat_dev_cfg_stop(struct seq_file *sfile, void *v)44{45 mutex_unlock(&qat_cfg_read_lock);46}47 48static const struct seq_operations qat_dev_cfg_sops = {49 .start = qat_dev_cfg_start,50 .next = qat_dev_cfg_next,51 .stop = qat_dev_cfg_stop,52 .show = qat_dev_cfg_show53};54 55DEFINE_SEQ_ATTRIBUTE(qat_dev_cfg);56 57/**58 * adf_cfg_dev_add() - Create an acceleration device configuration table.59 * @accel_dev: Pointer to acceleration device.60 *61 * Function creates a configuration table for the given acceleration device.62 * The table stores device specific config values.63 * To be used by QAT device specific drivers.64 *65 * Return: 0 on success, error code otherwise.66 */67int adf_cfg_dev_add(struct adf_accel_dev *accel_dev)68{69 struct adf_cfg_device_data *dev_cfg_data;70 71 dev_cfg_data = kzalloc(sizeof(*dev_cfg_data), GFP_KERNEL);72 if (!dev_cfg_data)73 return -ENOMEM;74 INIT_LIST_HEAD(&dev_cfg_data->sec_list);75 init_rwsem(&dev_cfg_data->lock);76 accel_dev->cfg = dev_cfg_data;77 return 0;78}79EXPORT_SYMBOL_GPL(adf_cfg_dev_add);80 81void adf_cfg_dev_dbgfs_add(struct adf_accel_dev *accel_dev)82{83 struct adf_cfg_device_data *dev_cfg_data = accel_dev->cfg;84 85 dev_cfg_data->debug = debugfs_create_file("dev_cfg", 0400,86 accel_dev->debugfs_dir,87 dev_cfg_data,88 &qat_dev_cfg_fops);89}90 91void adf_cfg_dev_dbgfs_rm(struct adf_accel_dev *accel_dev)92{93 struct adf_cfg_device_data *dev_cfg_data = accel_dev->cfg;94 95 if (!dev_cfg_data)96 return;97 98 debugfs_remove(dev_cfg_data->debug);99 dev_cfg_data->debug = NULL;100}101 102static void adf_cfg_section_del_all(struct list_head *head);103static void adf_cfg_section_del_all_except(struct list_head *head,104 const char *section_name);105 106void adf_cfg_del_all(struct adf_accel_dev *accel_dev)107{108 struct adf_cfg_device_data *dev_cfg_data = accel_dev->cfg;109 110 down_write(&dev_cfg_data->lock);111 adf_cfg_section_del_all(&dev_cfg_data->sec_list);112 up_write(&dev_cfg_data->lock);113 clear_bit(ADF_STATUS_CONFIGURED, &accel_dev->status);114}115 116void adf_cfg_del_all_except(struct adf_accel_dev *accel_dev,117 const char *section_name)118{119 struct adf_cfg_device_data *dev_cfg_data = accel_dev->cfg;120 121 down_write(&dev_cfg_data->lock);122 adf_cfg_section_del_all_except(&dev_cfg_data->sec_list, section_name);123 up_write(&dev_cfg_data->lock);124 clear_bit(ADF_STATUS_CONFIGURED, &accel_dev->status);125}126 127/**128 * adf_cfg_dev_remove() - Clears acceleration device configuration table.129 * @accel_dev: Pointer to acceleration device.130 *131 * Function removes configuration table from the given acceleration device132 * and frees all allocated memory.133 * To be used by QAT device specific drivers.134 *135 * Return: void136 */137void adf_cfg_dev_remove(struct adf_accel_dev *accel_dev)138{139 struct adf_cfg_device_data *dev_cfg_data = accel_dev->cfg;140 141 if (!dev_cfg_data)142 return;143 144 down_write(&dev_cfg_data->lock);145 adf_cfg_section_del_all(&dev_cfg_data->sec_list);146 up_write(&dev_cfg_data->lock);147 kfree(dev_cfg_data);148 accel_dev->cfg = NULL;149}150EXPORT_SYMBOL_GPL(adf_cfg_dev_remove);151 152static void adf_cfg_keyval_add(struct adf_cfg_key_val *new,153 struct adf_cfg_section *sec)154{155 list_add_tail(&new->list, &sec->param_head);156}157 158static void adf_cfg_keyval_remove(const char *key, struct adf_cfg_section *sec)159{160 struct list_head *head = &sec->param_head;161 struct list_head *list_ptr, *tmp;162 163 list_for_each_prev_safe(list_ptr, tmp, head) {164 struct adf_cfg_key_val *ptr =165 list_entry(list_ptr, struct adf_cfg_key_val, list);166 167 if (strncmp(ptr->key, key, sizeof(ptr->key)))168 continue;169 170 list_del(list_ptr);171 kfree(ptr);172 break;173 }174}175 176static void adf_cfg_keyval_del_all(struct list_head *head)177{178 struct list_head *list_ptr, *tmp;179 180 list_for_each_prev_safe(list_ptr, tmp, head) {181 struct adf_cfg_key_val *ptr =182 list_entry(list_ptr, struct adf_cfg_key_val, list);183 list_del(list_ptr);184 kfree(ptr);185 }186}187 188static void adf_cfg_section_del_all(struct list_head *head)189{190 struct adf_cfg_section *ptr;191 struct list_head *list, *tmp;192 193 list_for_each_prev_safe(list, tmp, head) {194 ptr = list_entry(list, struct adf_cfg_section, list);195 adf_cfg_keyval_del_all(&ptr->param_head);196 list_del(list);197 kfree(ptr);198 }199}200 201static void adf_cfg_section_del_all_except(struct list_head *head,202 const char *section_name)203{204 struct list_head *list, *tmp;205 struct adf_cfg_section *ptr;206 207 list_for_each_prev_safe(list, tmp, head) {208 ptr = list_entry(list, struct adf_cfg_section, list);209 if (!strcmp(ptr->name, section_name))210 continue;211 adf_cfg_keyval_del_all(&ptr->param_head);212 list_del(list);213 kfree(ptr);214 }215}216 217static struct adf_cfg_key_val *adf_cfg_key_value_find(struct adf_cfg_section *s,218 const char *key)219{220 struct list_head *list;221 222 list_for_each(list, &s->param_head) {223 struct adf_cfg_key_val *ptr =224 list_entry(list, struct adf_cfg_key_val, list);225 if (!strcmp(ptr->key, key))226 return ptr;227 }228 return NULL;229}230 231static struct adf_cfg_section *adf_cfg_sec_find(struct adf_accel_dev *accel_dev,232 const char *sec_name)233{234 struct adf_cfg_device_data *cfg = accel_dev->cfg;235 struct list_head *list;236 237 list_for_each(list, &cfg->sec_list) {238 struct adf_cfg_section *ptr =239 list_entry(list, struct adf_cfg_section, list);240 if (!strcmp(ptr->name, sec_name))241 return ptr;242 }243 return NULL;244}245 246static int adf_cfg_key_val_get(struct adf_accel_dev *accel_dev,247 const char *sec_name,248 const char *key_name,249 char *val)250{251 struct adf_cfg_section *sec = adf_cfg_sec_find(accel_dev, sec_name);252 struct adf_cfg_key_val *keyval = NULL;253 254 if (sec)255 keyval = adf_cfg_key_value_find(sec, key_name);256 if (keyval) {257 memcpy(val, keyval->val, ADF_CFG_MAX_VAL_LEN_IN_BYTES);258 return 0;259 }260 return -ENODATA;261}262 263/**264 * adf_cfg_add_key_value_param() - Add key-value config entry to config table.265 * @accel_dev: Pointer to acceleration device.266 * @section_name: Name of the section where the param will be added267 * @key: The key string268 * @val: Value pain for the given @key269 * @type: Type - string, int or address270 *271 * Function adds configuration key - value entry in the appropriate section272 * in the given acceleration device. If the key exists already, the value273 * is updated.274 * To be used by QAT device specific drivers.275 *276 * Return: 0 on success, error code otherwise.277 */278int adf_cfg_add_key_value_param(struct adf_accel_dev *accel_dev,279 const char *section_name,280 const char *key, const void *val,281 enum adf_cfg_val_type type)282{283 struct adf_cfg_device_data *cfg = accel_dev->cfg;284 struct adf_cfg_key_val *key_val;285 struct adf_cfg_section *section = adf_cfg_sec_find(accel_dev,286 section_name);287 char temp_val[ADF_CFG_MAX_VAL_LEN_IN_BYTES];288 289 if (!section)290 return -EFAULT;291 292 key_val = kzalloc(sizeof(*key_val), GFP_KERNEL);293 if (!key_val)294 return -ENOMEM;295 296 INIT_LIST_HEAD(&key_val->list);297 strscpy(key_val->key, key, sizeof(key_val->key));298 299 if (type == ADF_DEC) {300 snprintf(key_val->val, ADF_CFG_MAX_VAL_LEN_IN_BYTES,301 "%ld", (*((long *)val)));302 } else if (type == ADF_STR) {303 strscpy(key_val->val, (char *)val, sizeof(key_val->val));304 } else if (type == ADF_HEX) {305 snprintf(key_val->val, ADF_CFG_MAX_VAL_LEN_IN_BYTES,306 "0x%lx", (unsigned long)val);307 } else {308 dev_err(&GET_DEV(accel_dev), "Unknown type given.\n");309 kfree(key_val);310 return -EINVAL;311 }312 key_val->type = type;313 314 /* Add the key-value pair as below policy:315 * 1. if the key doesn't exist, add it;316 * 2. if the key already exists with a different value then update it317 * to the new value (the key is deleted and the newly created318 * key_val containing the new value is added to the database);319 * 3. if the key exists with the same value, then return without doing320 * anything (the newly created key_val is freed).321 */322 down_write(&cfg->lock);323 if (!adf_cfg_key_val_get(accel_dev, section_name, key, temp_val)) {324 if (strncmp(temp_val, key_val->val, sizeof(temp_val))) {325 adf_cfg_keyval_remove(key, section);326 } else {327 kfree(key_val);328 goto out;329 }330 }331 332 adf_cfg_keyval_add(key_val, section);333 334out:335 up_write(&cfg->lock);336 return 0;337}338EXPORT_SYMBOL_GPL(adf_cfg_add_key_value_param);339 340/**341 * adf_cfg_section_add() - Add config section entry to config table.342 * @accel_dev: Pointer to acceleration device.343 * @name: Name of the section344 *345 * Function adds configuration section where key - value entries346 * will be stored.347 * To be used by QAT device specific drivers.348 *349 * Return: 0 on success, error code otherwise.350 */351int adf_cfg_section_add(struct adf_accel_dev *accel_dev, const char *name)352{353 struct adf_cfg_device_data *cfg = accel_dev->cfg;354 struct adf_cfg_section *sec = adf_cfg_sec_find(accel_dev, name);355 356 if (sec)357 return 0;358 359 sec = kzalloc(sizeof(*sec), GFP_KERNEL);360 if (!sec)361 return -ENOMEM;362 363 strscpy(sec->name, name, sizeof(sec->name));364 INIT_LIST_HEAD(&sec->param_head);365 down_write(&cfg->lock);366 list_add_tail(&sec->list, &cfg->sec_list);367 up_write(&cfg->lock);368 return 0;369}370EXPORT_SYMBOL_GPL(adf_cfg_section_add);371 372int adf_cfg_get_param_value(struct adf_accel_dev *accel_dev,373 const char *section, const char *name,374 char *value)375{376 struct adf_cfg_device_data *cfg = accel_dev->cfg;377 int ret;378 379 down_read(&cfg->lock);380 ret = adf_cfg_key_val_get(accel_dev, section, name, value);381 up_read(&cfg->lock);382 return ret;383}384EXPORT_SYMBOL_GPL(adf_cfg_get_param_value);385