Skip to content

Commit

Permalink
crypto: hisilicon/qm - supports writing QoS int the host
Browse files Browse the repository at this point in the history
Based on the Token bucket algorithm. The HAC driver supports to configure
each function's QoS in the host. The driver supports writing QoS by the
debugfs node that named "alg_qos". The qos value is 1~1000.

Signed-off-by: Kai Ye <yekai13@huawei.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
  • Loading branch information
Kai Ye authored and Herbert Xu committed Jun 17, 2021
1 parent 10ff997 commit 72b010d
Show file tree
Hide file tree
Showing 2 changed files with 310 additions and 2 deletions.
298 changes: 296 additions & 2 deletions drivers/crypto/hisilicon/qm.c
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,32 @@
#define QM_DRIVER_REMOVING 0
#define QM_RST_SCHED 1
#define QM_RESETTING 2
#define QM_QOS_PARAM_NUM 2
#define QM_QOS_VAL_NUM 1
#define QM_QOS_BDF_PARAM_NUM 4
#define QM_QOS_MAX_VAL 1000
#define QM_QOS_RATE 100
#define QM_QOS_EXPAND_RATE 1000
#define QM_SHAPER_CIR_B_MASK GENMASK(7, 0)
#define QM_SHAPER_CIR_U_MASK GENMASK(10, 8)
#define QM_SHAPER_CIR_S_MASK GENMASK(14, 11)
#define QM_SHAPER_FACTOR_CIR_U_SHIFT 8
#define QM_SHAPER_FACTOR_CIR_S_SHIFT 11
#define QM_SHAPER_FACTOR_CBS_B_SHIFT 15
#define QM_SHAPER_FACTOR_CBS_S_SHIFT 19
#define QM_SHAPER_CBS_B 1
#define QM_SHAPER_CBS_S 16
#define QM_SHAPER_VFT_OFFSET 6
#define QM_QOS_MIN_ERROR_RATE 5
#define QM_QOS_TYPICAL_NUM 8
#define QM_SHAPER_MIN_CBS_S 8
#define QM_QOS_TICK 0x300U
#define QM_QOS_DIVISOR_CLK 0x1f40U
#define QM_QOS_MAX_CIR_B 200
#define QM_QOS_MIN_CIR_B 100
#define QM_QOS_MAX_CIR_U 6
#define QM_QOS_MAX_CIR_S 11
#define QM_QOS_VAL_MAX_LEN 32

#define QM_MK_CQC_DW3_V1(hop_num, pg_sz, buf_sz, cqe_sz) \
(((hop_num) << QM_CQ_HOP_NUM_SHIFT) | \
Expand Down Expand Up @@ -280,6 +306,7 @@
enum vft_type {
SQC_VFT = 0,
CQC_VFT,
SHAPER_VFT,
};

enum acc_err_result {
Expand All @@ -288,6 +315,11 @@ enum acc_err_result {
ACC_ERR_RECOVERED,
};

enum qm_alg_type {
ALG_TYPE_0,
ALG_TYPE_1,
};

enum qm_mb_cmd {
QM_PF_FLR_PREPARE = 0x01,
QM_PF_SRST_PREPARE,
Expand Down Expand Up @@ -460,6 +492,11 @@ static const char * const qp_s[] = {
"none", "init", "start", "stop", "close",
};

static const u32 typical_qos_val[QM_QOS_TYPICAL_NUM] = {100, 250, 500, 1000,
10000, 25000, 50000, 100000};
static const u32 typical_qos_cbs_s[QM_QOS_TYPICAL_NUM] = {9, 10, 11, 12, 16,
17, 18, 19};

static bool qm_avail_state(struct hisi_qm *qm, enum qm_state new)
{
enum qm_state curr = atomic_read(&qm->status.flags);
Expand Down Expand Up @@ -899,8 +936,69 @@ static void qm_init_prefetch(struct hisi_qm *qm)
writel(page_type, qm->io_base + QM_PAGE_SIZE);
}

/*
* the formula:
* IR = X Mbps if ir = 1 means IR = 100 Mbps, if ir = 10000 means = 10Gbps
*
* IR_b * (2 ^ IR_u) * 8
* IR(Mbps) * 10 ^ -3 = -------------------------
* Tick * (2 ^ IR_s)
*/
static u32 acc_shaper_para_calc(u64 cir_b, u64 cir_u, u64 cir_s)
{
return ((cir_b * QM_QOS_DIVISOR_CLK) * (1 << cir_u)) /
(QM_QOS_TICK * (1 << cir_s));
}

static u32 acc_shaper_calc_cbs_s(u32 ir)
{
int i;

if (ir < typical_qos_val[0])
return QM_SHAPER_MIN_CBS_S;

for (i = 1; i < QM_QOS_TYPICAL_NUM; i++) {
if (ir >= typical_qos_val[i - 1] && ir < typical_qos_val[i])
return typical_qos_cbs_s[i - 1];
}

return typical_qos_cbs_s[QM_QOS_TYPICAL_NUM - 1];
}

static int qm_get_shaper_para(u32 ir, struct qm_shaper_factor *factor)
{
u32 cir_b, cir_u, cir_s, ir_calc;
u32 error_rate;

factor->cbs_s = acc_shaper_calc_cbs_s(ir);

for (cir_b = QM_QOS_MIN_CIR_B; cir_b <= QM_QOS_MAX_CIR_B; cir_b++) {
for (cir_u = 0; cir_u <= QM_QOS_MAX_CIR_U; cir_u++) {
for (cir_s = 0; cir_s <= QM_QOS_MAX_CIR_S; cir_s++) {
/** the formula is changed to:
* IR_b * (2 ^ IR_u) * DIVISOR_CLK
* IR(Mbps) = -------------------------
* 768 * (2 ^ IR_s)
*/
ir_calc = acc_shaper_para_calc(cir_b, cir_u,
cir_s);
error_rate = QM_QOS_EXPAND_RATE * (u32)abs(ir_calc - ir) / ir;
if (error_rate <= QM_QOS_MIN_ERROR_RATE) {
factor->cir_b = cir_b;
factor->cir_u = cir_u;
factor->cir_s = cir_s;

return 0;
}
}
}
}

return -EINVAL;
}

static void qm_vft_data_cfg(struct hisi_qm *qm, enum vft_type type, u32 base,
u32 number)
u32 number, struct qm_shaper_factor *factor)
{
u64 tmp = 0;

Expand Down Expand Up @@ -929,6 +1027,15 @@ static void qm_vft_data_cfg(struct hisi_qm *qm, enum vft_type type, u32 base,
tmp = QM_CQC_VFT_VALID;
}
break;
case SHAPER_VFT:
if (qm->ver >= QM_HW_V3) {
tmp = factor->cir_b |
(factor->cir_u << QM_SHAPER_FACTOR_CIR_U_SHIFT) |
(factor->cir_s << QM_SHAPER_FACTOR_CIR_S_SHIFT) |
(QM_SHAPER_CBS_B << QM_SHAPER_FACTOR_CBS_B_SHIFT) |
(factor->cbs_s << QM_SHAPER_FACTOR_CBS_S_SHIFT);
}
break;
}
}

Expand All @@ -939,6 +1046,7 @@ static void qm_vft_data_cfg(struct hisi_qm *qm, enum vft_type type, u32 base,
static int qm_set_vft_common(struct hisi_qm *qm, enum vft_type type,
u32 fun_num, u32 base, u32 number)
{
struct qm_shaper_factor *factor = &qm->factor[fun_num];
unsigned int val;
int ret;

Expand All @@ -950,9 +1058,12 @@ static int qm_set_vft_common(struct hisi_qm *qm, enum vft_type type,

writel(0x0, qm->io_base + QM_VFT_CFG_OP_WR);
writel(type, qm->io_base + QM_VFT_CFG_TYPE);
if (type == SHAPER_VFT)
fun_num |= base << QM_SHAPER_VFT_OFFSET;

writel(fun_num, qm->io_base + QM_VFT_CFG);

qm_vft_data_cfg(qm, type, base, number);
qm_vft_data_cfg(qm, type, base, number, factor);

writel(0x0, qm->io_base + QM_VFT_CFG_RDY);
writel(0x1, qm->io_base + QM_VFT_CFG_OP_ENABLE);
Expand All @@ -962,6 +1073,27 @@ static int qm_set_vft_common(struct hisi_qm *qm, enum vft_type type,
POLL_TIMEOUT);
}

static int qm_shaper_init_vft(struct hisi_qm *qm, u32 fun_num)
{
int ret, i;

qm->factor[fun_num].func_qos = QM_QOS_MAX_VAL;
ret = qm_get_shaper_para(QM_QOS_MAX_VAL * QM_QOS_RATE, &qm->factor[fun_num]);
if (ret) {
dev_err(&qm->pdev->dev, "failed to calculate shaper parameter!\n");
return ret;
}
writel(qm->type_rate, qm->io_base + QM_SHAPER_CFG);
for (i = ALG_TYPE_0; i <= ALG_TYPE_1; i++) {
/* The base number of queue reuse for different alg type */
ret = qm_set_vft_common(qm, SHAPER_VFT, fun_num, i, 1);
if (ret)
return ret;
}

return 0;
}

/* The config should be conducted after qm_dev_mem_reset() */
static int qm_set_sqc_cqc_vft(struct hisi_qm *qm, u32 fun_num, u32 base,
u32 number)
Expand All @@ -974,7 +1106,21 @@ static int qm_set_sqc_cqc_vft(struct hisi_qm *qm, u32 fun_num, u32 base,
return ret;
}

/* init default shaper qos val */
if (qm->ver >= QM_HW_V3) {
ret = qm_shaper_init_vft(qm, fun_num);
if (ret)
goto back_sqc_cqc;
}

return 0;
back_sqc_cqc:
for (i = SQC_VFT; i <= CQC_VFT; i++) {
ret = qm_set_vft_common(qm, i, fun_num, 0, 0);
if (ret)
return ret;
}
return ret;
}

static int qm_get_vft_v2(struct hisi_qm *qm, u32 *base, u32 *number)
Expand Down Expand Up @@ -3113,6 +3259,7 @@ void hisi_qm_uninit(struct hisi_qm *qm)
struct device *dev = &pdev->dev;

qm_cmd_uninit(qm);
kfree(qm->factor);
down_write(&qm->qps_lock);

if (!qm_avail_state(qm, QM_CLOSE)) {
Expand Down Expand Up @@ -3842,6 +3989,149 @@ static int qm_clear_vft_config(struct hisi_qm *qm)
return 0;
}

static int qm_func_shaper_enable(struct hisi_qm *qm, u32 fun_index, u32 qos)
{
struct device *dev = &qm->pdev->dev;
u32 ir = qos * QM_QOS_RATE;
int ret, total_vfs, i;

total_vfs = pci_sriov_get_totalvfs(qm->pdev);
if (fun_index > total_vfs)
return -EINVAL;

qm->factor[fun_index].func_qos = qos;

ret = qm_get_shaper_para(ir, &qm->factor[fun_index]);
if (ret) {
dev_err(dev, "failed to calculate shaper parameter!\n");
return -EINVAL;
}

for (i = ALG_TYPE_0; i <= ALG_TYPE_1; i++) {
/* The base number of queue reuse for different alg type */
ret = qm_set_vft_common(qm, SHAPER_VFT, fun_index, i, 1);
if (ret) {
dev_err(dev, "type: %d, failed to set shaper vft!\n", i);
return -EINVAL;
}
}

return 0;
}

static ssize_t qm_qos_value_init(const char *buf, unsigned long *val)
{
int buflen = strlen(buf);
int ret, i;

for (i = 0; i < buflen; i++) {
if (!isdigit(buf[i]))
return -EINVAL;
}

ret = sscanf(buf, "%ld", val);
if (ret != QM_QOS_VAL_NUM)
return -EINVAL;

return 0;
}

static ssize_t qm_algqos_write(struct file *filp, const char __user *buf,
size_t count, loff_t *pos)
{
struct hisi_qm *qm = filp->private_data;
char tbuf[QM_DBG_READ_LEN];
int tmp1, bus, device, function;
char tbuf_bdf[QM_DBG_READ_LEN] = {0};
char val_buf[QM_QOS_VAL_MAX_LEN] = {0};
unsigned int fun_index;
unsigned long val = 0;
int len, ret;

if (qm->fun_type == QM_HW_VF)
return -EINVAL;

/* Mailbox and reset cannot be operated at the same time */
if (test_and_set_bit(QM_RESETTING, &qm->misc_ctl)) {
pci_err(qm->pdev, "dev resetting, write alg qos failed!\n");
return -EAGAIN;
}

if (*pos != 0) {
ret = 0;
goto err_get_status;
}

if (count >= QM_DBG_READ_LEN) {
ret = -ENOSPC;
goto err_get_status;
}

len = simple_write_to_buffer(tbuf, QM_DBG_READ_LEN - 1, pos, buf, count);
if (len < 0) {
ret = len;
goto err_get_status;
}

tbuf[len] = '\0';
ret = sscanf(tbuf, "%s %s", tbuf_bdf, val_buf);
if (ret != QM_QOS_PARAM_NUM) {
ret = -EINVAL;
goto err_get_status;
}

ret = qm_qos_value_init(val_buf, &val);
if (val == 0 || val > QM_QOS_MAX_VAL || ret) {
pci_err(qm->pdev, "input qos value is error, please set 1~1000!\n");
ret = -EINVAL;
goto err_get_status;
}

ret = sscanf(tbuf_bdf, "%d:%x:%d.%d", &tmp1, &bus, &device, &function);
if (ret != QM_QOS_BDF_PARAM_NUM) {
pci_err(qm->pdev, "input pci bdf value is error!\n");
ret = -EINVAL;
goto err_get_status;
}

fun_index = device * 8 + function;

ret = qm_func_shaper_enable(qm, fun_index, val);
if (ret) {
pci_err(qm->pdev, "failed to enable function shaper!\n");
ret = -EINVAL;
goto err_get_status;
}

ret = count;

err_get_status:
clear_bit(QM_RESETTING, &qm->misc_ctl);
return ret;
}

static const struct file_operations qm_algqos_fops = {
.owner = THIS_MODULE,
.open = simple_open,
.write = qm_algqos_write,
};

/**
* hisi_qm_set_algqos_init() - Initialize function qos debugfs files.
* @qm: The qm for which we want to add debugfs files.
*
* Create function qos debugfs files.
*/
static void hisi_qm_set_algqos_init(struct hisi_qm *qm)
{
if (qm->fun_type == QM_HW_PF)
debugfs_create_file("alg_qos", 0644, qm->debug.debug_root,
qm, &qm_algqos_fops);
else
debugfs_create_file("alg_qos", 0444, qm->debug.debug_root,
qm, &qm_algqos_fops);
}

/**
* hisi_qm_sriov_enable() - enable virtual functions
* @pdev: the PCIe device
Expand Down Expand Up @@ -3896,6 +4186,7 @@ EXPORT_SYMBOL_GPL(hisi_qm_sriov_enable);
int hisi_qm_sriov_disable(struct pci_dev *pdev, bool is_frozen)
{
struct hisi_qm *qm = pci_get_drvdata(pdev);
int total_vfs = pci_sriov_get_totalvfs(qm->pdev);

if (pci_vfs_assigned(pdev)) {
pci_err(pdev, "Failed to disable VFs as VFs are assigned!\n");
Expand All @@ -3909,6 +4200,9 @@ int hisi_qm_sriov_disable(struct pci_dev *pdev, bool is_frozen)
}

pci_disable_sriov(pdev);
/* clear vf function shaper configure array */
memset(qm->factor + 1, 0, sizeof(struct qm_shaper_factor) * total_vfs);

return qm_clear_vft_config(qm);
}
EXPORT_SYMBOL_GPL(hisi_qm_sriov_disable);
Expand Down
Loading

0 comments on commit 72b010d

Please sign in to comment.