root/drivers/crypto/intel/qat/qat_common/adf_sysfs_rl.c
// 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[] = {
        [SVC_ASYM] = "asym",
        [SVC_SYM] = "sym",
        [SVC_DC] = "dc",
        [SVC_DECOMP] = "decomp",
};

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 == SVC_BASE_COUNT)
                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)
{
        struct adf_accel_dev *accel_dev;
        unsigned int val;
        int ret;

        accel_dev = adf_devmgr_pci_to_accel_dev(to_pci_dev(dev));
        if (!accel_dev)
                return -EINVAL;

        ret = sysfs_match_string(rl_services, buf);
        if (ret < 0)
                return ret;

        val = ret;
        if (!adf_is_service_enabled(accel_dev, val))
                return -EINVAL;

        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 = SVC_BASE_COUNT;
        data->input.srv = SVC_BASE_COUNT;
        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;
}