| // SPDX-License-Identifier: GPL-2.0-only |
| /* Copyright(c) 2023 Intel Corporation */ |
| |
| #define dev_fmt(fmt) "RateLimiting: " fmt |
| |
| #include <linux/dev_printk.h> |
| #include <linux/pci.h> |
| #include <linux/sysfs.h> |
| #include <linux/types.h> |
| |
| #include "adf_common_drv.h" |
| #include "adf_rl.h" |
| #include "adf_sysfs_rl.h" |
| |
| #define GET_RL_STRUCT(accel_dev) ((accel_dev)->rate_limiting->user_input) |
| |
| enum rl_ops { |
| ADD, |
| UPDATE, |
| RM, |
| RM_ALL, |
| GET, |
| }; |
| |
| enum rl_params { |
| RP_MASK, |
| ID, |
| CIR, |
| PIR, |
| SRV, |
| CAP_REM_SRV, |
| }; |
| |
| static const char *const rl_services[] = { |
| [ADF_SVC_ASYM] = "asym", |
| [ADF_SVC_SYM] = "sym", |
| [ADF_SVC_DC] = "dc", |
| }; |
| |
| static const char *const rl_operations[] = { |
| [ADD] = "add", |
| [UPDATE] = "update", |
| [RM] = "rm", |
| [RM_ALL] = "rm_all", |
| [GET] = "get", |
| }; |
| |
| static int set_param_u(struct device *dev, enum rl_params param, u64 set) |
| { |
| struct adf_rl_interface_data *data; |
| struct adf_accel_dev *accel_dev; |
| int ret = 0; |
| |
| accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); |
| if (!accel_dev) |
| return -EINVAL; |
| |
| data = &GET_RL_STRUCT(accel_dev); |
| |
| down_write(&data->lock); |
| switch (param) { |
| case RP_MASK: |
| data->input.rp_mask = set; |
| break; |
| case CIR: |
| data->input.cir = set; |
| break; |
| case PIR: |
| data->input.pir = set; |
| break; |
| case SRV: |
| data->input.srv = set; |
| break; |
| case CAP_REM_SRV: |
| data->cap_rem_srv = set; |
| break; |
| default: |
| ret = -EINVAL; |
| break; |
| } |
| up_write(&data->lock); |
| |
| return ret; |
| } |
| |
| static int set_param_s(struct device *dev, enum rl_params param, int set) |
| { |
| struct adf_rl_interface_data *data; |
| struct adf_accel_dev *accel_dev; |
| |
| accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); |
| if (!accel_dev || param != ID) |
| return -EINVAL; |
| |
| data = &GET_RL_STRUCT(accel_dev); |
| |
| down_write(&data->lock); |
| data->input.sla_id = set; |
| up_write(&data->lock); |
| |
| return 0; |
| } |
| |
| static int get_param_u(struct device *dev, enum rl_params param, u64 *get) |
| { |
| struct adf_rl_interface_data *data; |
| struct adf_accel_dev *accel_dev; |
| int ret = 0; |
| |
| accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); |
| if (!accel_dev) |
| return -EINVAL; |
| |
| data = &GET_RL_STRUCT(accel_dev); |
| |
| down_read(&data->lock); |
| switch (param) { |
| case RP_MASK: |
| *get = data->input.rp_mask; |
| break; |
| case CIR: |
| *get = data->input.cir; |
| break; |
| case PIR: |
| *get = data->input.pir; |
| break; |
| case SRV: |
| *get = data->input.srv; |
| break; |
| default: |
| ret = -EINVAL; |
| } |
| up_read(&data->lock); |
| |
| return ret; |
| } |
| |
| static int get_param_s(struct device *dev, enum rl_params param) |
| { |
| struct adf_rl_interface_data *data; |
| struct adf_accel_dev *accel_dev; |
| int ret = 0; |
| |
| accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); |
| if (!accel_dev) |
| return -EINVAL; |
| |
| data = &GET_RL_STRUCT(accel_dev); |
| |
| down_read(&data->lock); |
| if (param == ID) |
| ret = data->input.sla_id; |
| up_read(&data->lock); |
| |
| return ret; |
| } |
| |
| static ssize_t rp_show(struct device *dev, struct device_attribute *attr, |
| char *buf) |
| { |
| int ret; |
| u64 get; |
| |
| ret = get_param_u(dev, RP_MASK, &get); |
| if (ret) |
| return ret; |
| |
| return sysfs_emit(buf, "%#llx\n", get); |
| } |
| |
| static ssize_t rp_store(struct device *dev, struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| int err; |
| u64 val; |
| |
| err = kstrtou64(buf, 16, &val); |
| if (err) |
| return err; |
| |
| err = set_param_u(dev, RP_MASK, val); |
| if (err) |
| return err; |
| |
| return count; |
| } |
| static DEVICE_ATTR_RW(rp); |
| |
| static ssize_t id_show(struct device *dev, struct device_attribute *attr, |
| char *buf) |
| { |
| return sysfs_emit(buf, "%d\n", get_param_s(dev, ID)); |
| } |
| |
| static ssize_t id_store(struct device *dev, struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| int err; |
| int val; |
| |
| err = kstrtoint(buf, 10, &val); |
| if (err) |
| return err; |
| |
| err = set_param_s(dev, ID, val); |
| if (err) |
| return err; |
| |
| return count; |
| } |
| static DEVICE_ATTR_RW(id); |
| |
| static ssize_t cir_show(struct device *dev, struct device_attribute *attr, |
| char *buf) |
| { |
| int ret; |
| u64 get; |
| |
| ret = get_param_u(dev, CIR, &get); |
| if (ret) |
| return ret; |
| |
| return sysfs_emit(buf, "%llu\n", get); |
| } |
| |
| static ssize_t cir_store(struct device *dev, struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| unsigned int val; |
| int err; |
| |
| err = kstrtouint(buf, 10, &val); |
| if (err) |
| return err; |
| |
| err = set_param_u(dev, CIR, val); |
| if (err) |
| return err; |
| |
| return count; |
| } |
| static DEVICE_ATTR_RW(cir); |
| |
| static ssize_t pir_show(struct device *dev, struct device_attribute *attr, |
| char *buf) |
| { |
| int ret; |
| u64 get; |
| |
| ret = get_param_u(dev, PIR, &get); |
| if (ret) |
| return ret; |
| |
| return sysfs_emit(buf, "%llu\n", get); |
| } |
| |
| static ssize_t pir_store(struct device *dev, struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| unsigned int val; |
| int err; |
| |
| err = kstrtouint(buf, 10, &val); |
| if (err) |
| return err; |
| |
| err = set_param_u(dev, PIR, val); |
| if (err) |
| return err; |
| |
| return count; |
| } |
| static DEVICE_ATTR_RW(pir); |
| |
| static ssize_t srv_show(struct device *dev, struct device_attribute *attr, |
| char *buf) |
| { |
| int ret; |
| u64 get; |
| |
| ret = get_param_u(dev, SRV, &get); |
| if (ret) |
| return ret; |
| |
| if (get == ADF_SVC_NONE) |
| return -EINVAL; |
| |
| return sysfs_emit(buf, "%s\n", rl_services[get]); |
| } |
| |
| static ssize_t srv_store(struct device *dev, struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| unsigned int val; |
| int ret; |
| |
| ret = sysfs_match_string(rl_services, buf); |
| if (ret < 0) |
| return ret; |
| |
| val = ret; |
| ret = set_param_u(dev, SRV, val); |
| if (ret) |
| return ret; |
| |
| return count; |
| } |
| static DEVICE_ATTR_RW(srv); |
| |
| static ssize_t cap_rem_show(struct device *dev, struct device_attribute *attr, |
| char *buf) |
| { |
| struct adf_rl_interface_data *data; |
| struct adf_accel_dev *accel_dev; |
| int ret, rem_cap; |
| |
| accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); |
| if (!accel_dev) |
| return -EINVAL; |
| |
| data = &GET_RL_STRUCT(accel_dev); |
| |
| down_read(&data->lock); |
| rem_cap = adf_rl_get_capability_remaining(accel_dev, data->cap_rem_srv, |
| RL_SLA_EMPTY_ID); |
| up_read(&data->lock); |
| if (rem_cap < 0) |
| return rem_cap; |
| |
| ret = sysfs_emit(buf, "%u\n", rem_cap); |
| |
| return ret; |
| } |
| |
| static ssize_t cap_rem_store(struct device *dev, struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| unsigned int val; |
| int ret; |
| |
| ret = sysfs_match_string(rl_services, buf); |
| if (ret < 0) |
| return ret; |
| |
| val = ret; |
| ret = set_param_u(dev, CAP_REM_SRV, val); |
| if (ret) |
| return ret; |
| |
| return count; |
| } |
| static DEVICE_ATTR_RW(cap_rem); |
| |
| static ssize_t sla_op_store(struct device *dev, struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct adf_rl_interface_data *data; |
| struct adf_accel_dev *accel_dev; |
| int ret; |
| |
| accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev)); |
| if (!accel_dev) |
| return -EINVAL; |
| |
| data = &GET_RL_STRUCT(accel_dev); |
| |
| ret = sysfs_match_string(rl_operations, buf); |
| if (ret < 0) |
| return ret; |
| |
| down_write(&data->lock); |
| switch (ret) { |
| case ADD: |
| data->input.parent_id = RL_PARENT_DEFAULT_ID; |
| data->input.type = RL_LEAF; |
| data->input.sla_id = 0; |
| ret = adf_rl_add_sla(accel_dev, &data->input); |
| if (ret) |
| goto err_free_lock; |
| break; |
| case UPDATE: |
| ret = adf_rl_update_sla(accel_dev, &data->input); |
| if (ret) |
| goto err_free_lock; |
| break; |
| case RM: |
| ret = adf_rl_remove_sla(accel_dev, data->input.sla_id); |
| if (ret) |
| goto err_free_lock; |
| break; |
| case RM_ALL: |
| adf_rl_remove_sla_all(accel_dev, false); |
| break; |
| case GET: |
| ret = adf_rl_get_sla(accel_dev, &data->input); |
| if (ret) |
| goto err_free_lock; |
| break; |
| default: |
| ret = -EINVAL; |
| goto err_free_lock; |
| } |
| up_write(&data->lock); |
| |
| return count; |
| |
| err_free_lock: |
| up_write(&data->lock); |
| |
| return ret; |
| } |
| static DEVICE_ATTR_WO(sla_op); |
| |
| static struct attribute *qat_rl_attrs[] = { |
| &dev_attr_rp.attr, |
| &dev_attr_id.attr, |
| &dev_attr_cir.attr, |
| &dev_attr_pir.attr, |
| &dev_attr_srv.attr, |
| &dev_attr_cap_rem.attr, |
| &dev_attr_sla_op.attr, |
| NULL, |
| }; |
| |
| static struct attribute_group qat_rl_group = { |
| .attrs = qat_rl_attrs, |
| .name = "qat_rl", |
| }; |
| |
| int adf_sysfs_rl_add(struct adf_accel_dev *accel_dev) |
| { |
| struct adf_rl_interface_data *data; |
| int ret; |
| |
| data = &GET_RL_STRUCT(accel_dev); |
| |
| ret = device_add_group(&GET_DEV(accel_dev), &qat_rl_group); |
| if (ret) |
| dev_err(&GET_DEV(accel_dev), |
| "Failed to create qat_rl attribute group\n"); |
| |
| data->cap_rem_srv = ADF_SVC_NONE; |
| data->input.srv = ADF_SVC_NONE; |
| data->sysfs_added = true; |
| |
| return ret; |
| } |
| |
| void adf_sysfs_rl_rm(struct adf_accel_dev *accel_dev) |
| { |
| struct adf_rl_interface_data *data; |
| |
| data = &GET_RL_STRUCT(accel_dev); |
| if (!data->sysfs_added) |
| return; |
| |
| device_remove_group(&GET_DEV(accel_dev), &qat_rl_group); |
| data->sysfs_added = false; |
| } |