The qat_rl sysfs attribute group is registered within the adf_dev_start()
function, alongside other driver components.
If any of the functions preceding the group registration fails,
the adf_dev_start() function returns, and the caller, to undo the
operation, invokes adf_dev_stop() followed by adf_dev_shutdown().
However, the current flow lacks information about whether the
registration of the qat_rl attribute group was successful or not.
In cases where this condition is encountered, an error similar to
the following might be reported:
4xxx 0000:6b:00.0: Starting device qat_dev0
4xxx 0000:6b:00.0: qat_dev0 started 9 acceleration engines
4xxx 0000:6b:00.0: Failed to send init message
4xxx 0000:6b:00.0: Failed to start device qat_dev0
sysfs group 'qat_rl' not found for kobject '0000:6b:00.0'
...
sysfs_remove_groups+0x2d/0x50
adf_sysfs_rl_rm+0x44/0x70 [intel_qat]
adf_rl_stop+0x2d/0xb0 [intel_qat]
adf_dev_stop+0x33/0x1d0 [intel_qat]
adf_dev_down+0xf1/0x150 [intel_qat]
...
4xxx 0000:6b:00.0: qat_dev0 stopped 9 acceleration engines
4xxx 0000:6b:00.0: Resetting device qat_dev0
To prevent attempting to remove attributes from a group that has not
been added yet, a flag named 'sysfs_added' is introduced. This flag
is set to true upon the successful registration of the attribute group.
Fixes: d9fb840837
("crypto: qat - add rate limiting feature to qat_4xxx")
Signed-off-by: Damian Muszynski <damian.muszynski@intel.com>
Reviewed-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
Reviewed-by: Ahsan Atta <ahsan.atta@intel.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
459 lines
8.3 KiB
C
459 lines
8.3 KiB
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[] = {
|
|
[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;
|
|
}
|