crypto: hisilicon/qm - supports writing QoS int the host
authorKai Ye <yekai13@huawei.com>
Fri, 11 Jun 2021 09:06:43 +0000 (17:06 +0800)
committerHerbert Xu <herbert@gondor.apana.org.au>
Thu, 17 Jun 2021 07:07:31 +0000 (15:07 +0800)
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>
drivers/crypto/hisilicon/qm.c
drivers/crypto/hisilicon/qm.h

index efa14c9ee976348f199e81702db4100ab2f9011e..2dd450fdc01a48b4a0b1b7d48a4c625db3c83897 100644 (file)
 #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)     | \
 enum vft_type {
        SQC_VFT = 0,
        CQC_VFT,
+       SHAPER_VFT,
 };
 
 enum acc_err_result {
@@ -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,
@@ -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);
@@ -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;
 
@@ -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;
                }
        }
 
@@ -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;
 
@@ -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);
@@ -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)
@@ -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)
@@ -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)) {
@@ -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
@@ -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");
@@ -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);
index 8a36bade103d8ecbd3c90ba536b3fd6e2131e489..035eaf8c442ddfb1307de6756a9fd09a2584a6ab 100644 (file)
@@ -76,6 +76,9 @@
 #define QM_Q_DEPTH                     1024
 #define QM_MIN_QNUM                     2
 #define HISI_ACC_SGL_SGE_NR_MAX                255
+#define QM_SHAPER_CFG                  0x100164
+#define QM_SHAPER_ENABLE               BIT(30)
+#define QM_SHAPER_TYPE1_OFFSET         10
 
 /* page number for queue file region */
 #define QM_DOORBELL_PAGE_NR            1
@@ -148,6 +151,14 @@ struct qm_debug {
        struct debugfs_file files[DEBUG_FILE_NUM];
 };
 
+struct qm_shaper_factor {
+       u32 func_qos;
+       u64 cir_b;
+       u64 cir_u;
+       u64 cir_s;
+       u64 cbs_s;
+};
+
 struct qm_dma {
        void *va;
        dma_addr_t dma;
@@ -262,6 +273,9 @@ struct hisi_qm {
        resource_size_t db_phys_base;
        struct uacce_device *uacce;
        int mode;
+       struct qm_shaper_factor *factor;
+       u32 mb_qos;
+       u32 type_rate;
 };
 
 struct hisi_qp_status {