From: Chengchang Tang <tangchengchang@xxxxxxxxxx> hns RoCE supports 4 congestion control algorithms. Each algorihm involves multiple parameters. Add port sysfs directory for each algorithm to allow modifying their parameters. Signed-off-by: Chengchang Tang <tangchengchang@xxxxxxxxxx> Signed-off-by: Junxian Huang <huangjunxian6@xxxxxxxxxxxxx> --- drivers/infiniband/hw/hns/Makefile | 2 +- drivers/infiniband/hw/hns/hns_roce_device.h | 20 ++ drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 59 ++++ drivers/infiniband/hw/hns/hns_roce_hw_v2.h | 132 ++++++++ drivers/infiniband/hw/hns/hns_roce_main.c | 3 + drivers/infiniband/hw/hns/hns_roce_sysfs.c | 346 ++++++++++++++++++++ 6 files changed, 561 insertions(+), 1 deletion(-) create mode 100644 drivers/infiniband/hw/hns/hns_roce_sysfs.c diff --git a/drivers/infiniband/hw/hns/Makefile b/drivers/infiniband/hw/hns/Makefile index be1e1cdbcfa8..161615fda3ae 100644 --- a/drivers/infiniband/hw/hns/Makefile +++ b/drivers/infiniband/hw/hns/Makefile @@ -8,7 +8,7 @@ ccflags-y := -I $(srctree)/drivers/net/ethernet/hisilicon/hns3 hns-roce-objs := hns_roce_main.o hns_roce_cmd.o hns_roce_pd.o \ hns_roce_ah.o hns_roce_hem.o hns_roce_mr.o hns_roce_qp.o \ hns_roce_cq.o hns_roce_alloc.o hns_roce_db.o hns_roce_srq.o hns_roce_restrack.o \ - hns_roce_debugfs.o + hns_roce_debugfs.o hns_roce_sysfs.o ifdef CONFIG_INFINIBAND_HNS_HIP08 hns-roce-hw-v2-objs := hns_roce_hw_v2.o $(hns-roce-objs) diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h index c3cbd0a494bf..d6a9a510f541 100644 --- a/drivers/infiniband/hw/hns/hns_roce_device.h +++ b/drivers/infiniband/hw/hns/hns_roce_device.h @@ -599,6 +599,7 @@ enum hns_roce_cong_type { CONG_TYPE_LDCP, CONG_TYPE_HC3, CONG_TYPE_DIP, + CONG_TYPE_TOTAL, }; struct hns_roce_qp { @@ -952,6 +953,20 @@ struct hns_roce_hw { u64 *stats, u32 port, int *hw_counters); const struct ib_device_ops *hns_roce_dev_ops; const struct ib_device_ops *hns_roce_dev_srq_ops; + int (*config_scc_param)(struct hns_roce_dev *hr_dev, + enum hns_roce_cong_type algo); + int (*query_scc_param)(struct hns_roce_dev *hr_dev, + enum hns_roce_cong_type alog); +}; + +#define HNS_ROCE_SCC_PARAM_SIZE 4 +struct hns_roce_scc_param { + __le32 param[HNS_ROCE_SCC_PARAM_SIZE]; + u32 lifespan; + unsigned long timestamp; + enum hns_roce_cong_type algo_type; + struct delayed_work scc_cfg_dwork; + struct hns_roce_dev *hr_dev; }; struct hns_roce_dev { @@ -1017,6 +1032,7 @@ struct hns_roce_dev { u64 dwqe_page; struct hns_roce_dev_debugfs dbgfs; atomic64_t *dfx_cnt; + struct hns_roce_scc_param *scc_param; }; static inline struct hns_roce_dev *to_hr_dev(struct ib_device *ib_dev) @@ -1152,6 +1168,8 @@ static inline u8 get_tclass(const struct ib_global_route *grh) grh->traffic_class >> DSCP_SHIFT : grh->traffic_class; } +extern const struct attribute_group *hns_attr_port_groups[]; + void hns_roce_init_uar_table(struct hns_roce_dev *dev); int hns_roce_uar_alloc(struct hns_roce_dev *dev, struct hns_roce_uar *uar); @@ -1292,4 +1310,6 @@ struct hns_user_mmap_entry * hns_roce_user_mmap_entry_insert(struct ib_ucontext *ucontext, u64 address, size_t length, enum hns_roce_mmap_type mmap_type); +void hns_roce_register_sysfs(struct hns_roce_dev *hr_dev); +void hns_roce_unregister_sysfs(struct hns_roce_dev *hr_dev); #endif /* _HNS_ROCE_DEVICE_H */ diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index ba7ae792d279..9d16ccc3d65d 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -6689,6 +6689,63 @@ static void hns_roce_v2_cleanup_eq_table(struct hns_roce_dev *hr_dev) kfree(eq_table->eq); } +static const enum hns_roce_opcode_type scc_opcode[] = { + HNS_ROCE_OPC_CFG_DCQCN_PARAM, + HNS_ROCE_OPC_CFG_LDCP_PARAM, + HNS_ROCE_OPC_CFG_HC3_PARAM, + HNS_ROCE_OPC_CFG_DIP_PARAM, +}; + +static int hns_roce_v2_config_scc_param(struct hns_roce_dev *hr_dev, + enum hns_roce_cong_type algo) +{ + struct hns_roce_scc_param *scc_param; + struct hns_roce_cmq_desc desc; + int ret; + + if (algo >= CONG_TYPE_TOTAL) + return -EINVAL; + + hns_roce_cmq_setup_basic_desc(&desc, scc_opcode[algo], false); + scc_param = &hr_dev->scc_param[algo]; + memcpy(&desc.data, scc_param, sizeof(scc_param->param)); + + ret = hns_roce_cmq_send(hr_dev, &desc, 1); + if (ret) + ibdev_err_ratelimited(&hr_dev->ib_dev, + "failed to configure scc param, opcode: 0x%x, ret = %d.\n", + le16_to_cpu(desc.opcode), ret); + return ret; +} + +static int hns_roce_v2_query_scc_param(struct hns_roce_dev *hr_dev, + enum hns_roce_cong_type algo) +{ + struct hns_roce_scc_param *scc_param; + struct hns_roce_cmq_desc desc; + int ret; + + if (hr_dev->pci_dev->revision <= PCI_REVISION_ID_HIP08 || hr_dev->is_vf) + return -EOPNOTSUPP; + + if (algo >= CONG_TYPE_TOTAL) + return -EINVAL; + + hns_roce_cmq_setup_basic_desc(&desc, scc_opcode[algo], true); + ret = hns_roce_cmq_send(hr_dev, &desc, 1); + if (ret) { + ibdev_err_ratelimited(&hr_dev->ib_dev, + "failed to query scc param, opcode: 0x%x, ret = %d.\n", + le16_to_cpu(desc.opcode), ret); + return ret; + } + + scc_param = &hr_dev->scc_param[algo]; + memcpy(scc_param, &desc.data, sizeof(scc_param->param)); + + return 0; +} + static const struct ib_device_ops hns_roce_v2_dev_ops = { .destroy_qp = hns_roce_v2_destroy_qp, .modify_cq = hns_roce_v2_modify_cq, @@ -6737,6 +6794,8 @@ static const struct hns_roce_hw hns_roce_hw_v2 = { .query_hw_counter = hns_roce_hw_v2_query_counter, .hns_roce_dev_ops = &hns_roce_v2_dev_ops, .hns_roce_dev_srq_ops = &hns_roce_v2_dev_srq_ops, + .config_scc_param = hns_roce_v2_config_scc_param, + .query_scc_param = hns_roce_v2_query_scc_param, }; static const struct pci_device_id hns_roce_hw_v2_pci_tbl[] = { diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h index df04bc8ede57..3167003c4f94 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h @@ -195,6 +195,10 @@ enum { /* CMQ command */ enum hns_roce_opcode_type { HNS_QUERY_FW_VER = 0x0001, + HNS_ROCE_OPC_CFG_DCQCN_PARAM = 0x1A80, + HNS_ROCE_OPC_CFG_LDCP_PARAM = 0x1A81, + HNS_ROCE_OPC_CFG_HC3_PARAM = 0x1A82, + HNS_ROCE_OPC_CFG_DIP_PARAM = 0x1A83, HNS_ROCE_OPC_QUERY_HW_VER = 0x8000, HNS_ROCE_OPC_CFG_GLOBAL_PARAM = 0x8001, HNS_ROCE_OPC_ALLOC_PF_RES = 0x8004, @@ -1429,6 +1433,134 @@ struct hns_roce_wqe_atomic_seg { __le64 cmp_data; }; +#define HNS_ROCE_DCQCN_AI_OFS 0 +#define HNS_ROCE_DCQCN_AI_SZ sizeof(u16) +#define HNS_ROCE_DCQCN_AI_MAX ((u16)(~0U)) +#define HNS_ROCE_DCQCN_F_OFS (HNS_ROCE_DCQCN_AI_OFS + HNS_ROCE_DCQCN_AI_SZ) +#define HNS_ROCE_DCQCN_F_SZ sizeof(u8) +#define HNS_ROCE_DCQCN_F_MAX ((u8)(~0U)) +#define HNS_ROCE_DCQCN_TKP_OFS (HNS_ROCE_DCQCN_F_OFS + HNS_ROCE_DCQCN_F_SZ) +#define HNS_ROCE_DCQCN_TKP_SZ sizeof(u8) +#define HNS_ROCE_DCQCN_TKP_MAX 15 +#define HNS_ROCE_DCQCN_TMP_OFS (HNS_ROCE_DCQCN_TKP_OFS + HNS_ROCE_DCQCN_TKP_SZ) +#define HNS_ROCE_DCQCN_TMP_SZ sizeof(u16) +#define HNS_ROCE_DCQCN_TMP_MAX 15 +#define HNS_ROCE_DCQCN_ALP_OFS (HNS_ROCE_DCQCN_TMP_OFS + HNS_ROCE_DCQCN_TMP_SZ) +#define HNS_ROCE_DCQCN_ALP_SZ sizeof(u16) +#define HNS_ROCE_DCQCN_ALP_MAX ((u16)(~0U)) +#define HNS_ROCE_DCQCN_MAX_SPEED_OFS (HNS_ROCE_DCQCN_ALP_OFS + \ + HNS_ROCE_DCQCN_ALP_SZ) +#define HNS_ROCE_DCQCN_MAX_SPEED_SZ sizeof(u32) +#define HNS_ROCE_DCQCN_MAX_SPEED_MAX ((u32)(~0U)) +#define HNS_ROCE_DCQCN_G_OFS (HNS_ROCE_DCQCN_MAX_SPEED_OFS + \ + HNS_ROCE_DCQCN_MAX_SPEED_SZ) +#define HNS_ROCE_DCQCN_G_SZ sizeof(u8) +#define HNS_ROCE_DCQCN_G_MAX 15 +#define HNS_ROCE_DCQCN_AL_OFS (HNS_ROCE_DCQCN_G_OFS + HNS_ROCE_DCQCN_G_SZ) +#define HNS_ROCE_DCQCN_AL_SZ sizeof(u8) +#define HNS_ROCE_DCQCN_AL_MAX ((u8)(~0U)) +#define HNS_ROCE_DCQCN_CNP_TIME_OFS (HNS_ROCE_DCQCN_AL_OFS + \ + HNS_ROCE_DCQCN_AL_SZ) +#define HNS_ROCE_DCQCN_CNP_TIME_SZ sizeof(u8) +#define HNS_ROCE_DCQCN_CNP_TIME_MAX ((u8)(~0U)) +#define HNS_ROCE_DCQCN_ASHIFT_OFS (HNS_ROCE_DCQCN_CNP_TIME_OFS + \ + HNS_ROCE_DCQCN_CNP_TIME_SZ) +#define HNS_ROCE_DCQCN_ASHIFT_SZ sizeof(u8) +#define HNS_ROCE_DCQCN_ASHIFT_MAX 15 +#define HNS_ROCE_DCQCN_LIFESPAN_OFS (HNS_ROCE_DCQCN_ASHIFT_OFS + \ + HNS_ROCE_DCQCN_ASHIFT_SZ) +#define HNS_ROCE_DCQCN_LIFESPAN_SZ sizeof(u32) +#define HNS_ROCE_DCQCN_LIFESPAN_MAX 1000 + +#define HNS_ROCE_LDCP_CWD0_OFS 0 +#define HNS_ROCE_LDCP_CWD0_SZ sizeof(u32) +#define HNS_ROCE_LDCP_CWD0_MAX ((u32)(~0U)) +#define HNS_ROCE_LDCP_ALPHA_OFS (HNS_ROCE_LDCP_CWD0_OFS + HNS_ROCE_LDCP_CWD0_SZ) +#define HNS_ROCE_LDCP_ALPHA_SZ sizeof(u8) +#define HNS_ROCE_LDCP_ALPHA_MAX ((u8)(~0U)) +#define HNS_ROCE_LDCP_GAMMA_OFS (HNS_ROCE_LDCP_ALPHA_OFS + \ + HNS_ROCE_LDCP_ALPHA_SZ) +#define HNS_ROCE_LDCP_GAMMA_SZ sizeof(u8) +#define HNS_ROCE_LDCP_GAMMA_MAX ((u8)(~0U)) +#define HNS_ROCE_LDCP_BETA_OFS (HNS_ROCE_LDCP_GAMMA_OFS + \ + HNS_ROCE_LDCP_GAMMA_SZ) +#define HNS_ROCE_LDCP_BETA_SZ sizeof(u8) +#define HNS_ROCE_LDCP_BETA_MAX ((u8)(~0U)) +#define HNS_ROCE_LDCP_ETA_OFS (HNS_ROCE_LDCP_BETA_OFS + HNS_ROCE_LDCP_BETA_SZ) +#define HNS_ROCE_LDCP_ETA_SZ sizeof(u8) +#define HNS_ROCE_LDCP_ETA_MAX ((u8)(~0U)) +#define HNS_ROCE_LDCP_LIFESPAN_OFS (4 * sizeof(u32)) +#define HNS_ROCE_LDCP_LIFESPAN_SZ sizeof(u32) +#define HNS_ROCE_LDCP_LIFESPAN_MAX 1000 + +#define HNS_ROCE_HC3_INITIAL_WINDOW_OFS 0 +#define HNS_ROCE_HC3_INITIAL_WINDOW_SZ sizeof(u32) +#define HNS_ROCE_HC3_INITIAL_WINDOW_MAX ((u32)(~0U)) +#define HNS_ROCE_HC3_BANDWIDTH_OFS (HNS_ROCE_HC3_INITIAL_WINDOW_OFS + \ + HNS_ROCE_HC3_INITIAL_WINDOW_SZ) +#define HNS_ROCE_HC3_BANDWIDTH_SZ sizeof(u32) +#define HNS_ROCE_HC3_BANDWIDTH_MAX ((u32)(~0U)) +#define HNS_ROCE_HC3_QLEN_SHIFT_OFS (HNS_ROCE_HC3_BANDWIDTH_OFS + \ + HNS_ROCE_HC3_BANDWIDTH_SZ) +#define HNS_ROCE_HC3_QLEN_SHIFT_SZ sizeof(u8) +#define HNS_ROCE_HC3_QLEN_SHIFT_MAX ((u8)(~0U)) +#define HNS_ROCE_HC3_PORT_USAGE_SHIFT_OFS (HNS_ROCE_HC3_QLEN_SHIFT_OFS + \ + HNS_ROCE_HC3_QLEN_SHIFT_SZ) +#define HNS_ROCE_HC3_PORT_USAGE_SHIFT_SZ sizeof(u8) +#define HNS_ROCE_HC3_PORT_USAGE_SHIFT_MAX ((u8)(~0U)) +#define HNS_ROCE_HC3_OVER_PERIOD_OFS (HNS_ROCE_HC3_PORT_USAGE_SHIFT_OFS + \ + HNS_ROCE_HC3_PORT_USAGE_SHIFT_SZ) +#define HNS_ROCE_HC3_OVER_PERIOD_SZ sizeof(u8) +#define HNS_ROCE_HC3_OVER_PERIOD_MAX ((u8)(~0U)) +#define HNS_ROCE_HC3_MAX_STAGE_OFS (HNS_ROCE_HC3_OVER_PERIOD_OFS + \ + HNS_ROCE_HC3_OVER_PERIOD_SZ) +#define HNS_ROCE_HC3_MAX_STAGE_SZ sizeof(u8) +#define HNS_ROCE_HC3_MAX_STAGE_MAX ((u8)(~0U)) +#define HNS_ROCE_HC3_GAMMA_SHIFT_OFS (HNS_ROCE_HC3_MAX_STAGE_OFS + \ + HNS_ROCE_HC3_MAX_STAGE_SZ) +#define HNS_ROCE_HC3_GAMMA_SHIFT_SZ sizeof(u8) +#define HNS_ROCE_HC3_GAMMA_SHIFT_MAX 15 +#define HNS_ROCE_HC3_LIFESPAN_OFS (4 * sizeof(u32)) +#define HNS_ROCE_HC3_LIFESPAN_SZ sizeof(u32) +#define HNS_ROCE_HC3_LIFESPAN_MAX 1000 + +#define HNS_ROCE_DIP_AI_OFS 0 +#define HNS_ROCE_DIP_AI_SZ sizeof(u16) +#define HNS_ROCE_DIP_AI_MAX ((u16)(~0U)) +#define HNS_ROCE_DIP_F_OFS (HNS_ROCE_DIP_AI_OFS + HNS_ROCE_DIP_AI_SZ) +#define HNS_ROCE_DIP_F_SZ sizeof(u8) +#define HNS_ROCE_DIP_F_MAX ((u8)(~0U)) +#define HNS_ROCE_DIP_TKP_OFS (HNS_ROCE_DIP_F_OFS + HNS_ROCE_DIP_F_SZ) +#define HNS_ROCE_DIP_TKP_SZ sizeof(u8) +#define HNS_ROCE_DIP_TKP_MAX 15 +#define HNS_ROCE_DIP_TMP_OFS (HNS_ROCE_DIP_TKP_OFS + HNS_ROCE_DIP_TKP_SZ) +#define HNS_ROCE_DIP_TMP_SZ sizeof(u16) +#define HNS_ROCE_DIP_TMP_MAX 15 +#define HNS_ROCE_DIP_ALP_OFS (HNS_ROCE_DIP_TMP_OFS + HNS_ROCE_DIP_TMP_SZ) +#define HNS_ROCE_DIP_ALP_SZ sizeof(u16) +#define HNS_ROCE_DIP_ALP_MAX ((u16)(~0U)) +#define HNS_ROCE_DIP_MAX_SPEED_OFS (HNS_ROCE_DIP_ALP_OFS + HNS_ROCE_DIP_ALP_SZ) +#define HNS_ROCE_DIP_MAX_SPEED_SZ sizeof(u32) +#define HNS_ROCE_DIP_MAX_SPEED_MAX ((u32)(~0U)) +#define HNS_ROCE_DIP_G_OFS (HNS_ROCE_DIP_MAX_SPEED_OFS + \ + HNS_ROCE_DIP_MAX_SPEED_SZ) +#define HNS_ROCE_DIP_G_SZ sizeof(u8) +#define HNS_ROCE_DIP_G_MAX 15 +#define HNS_ROCE_DIP_AL_OFS (HNS_ROCE_DIP_G_OFS + HNS_ROCE_DIP_G_SZ) +#define HNS_ROCE_DIP_AL_SZ sizeof(u8) +#define HNS_ROCE_DIP_AL_MAX ((u8)(~0U)) +#define HNS_ROCE_DIP_CNP_TIME_OFS (HNS_ROCE_DIP_AL_OFS + HNS_ROCE_DIP_AL_SZ) +#define HNS_ROCE_DIP_CNP_TIME_SZ sizeof(u8) +#define HNS_ROCE_DIP_CNP_TIME_MAX ((u8)(~0U)) +#define HNS_ROCE_DIP_ASHIFT_OFS (HNS_ROCE_DIP_CNP_TIME_OFS + \ + HNS_ROCE_DIP_CNP_TIME_SZ) +#define HNS_ROCE_DIP_ASHIFT_SZ sizeof(u8) +#define HNS_ROCE_DIP_ASHIFT_MAX 15 +#define HNS_ROCE_DIP_LIFESPAN_OFS (HNS_ROCE_DIP_ASHIFT_OFS + \ + HNS_ROCE_DIP_ASHIFT_SZ) +#define HNS_ROCE_DIP_LIFESPAN_SZ sizeof(u32) +#define HNS_ROCE_DIP_LIFESPAN_MAX 1000 + struct hns_roce_sccc_clr { __le32 qpn; __le32 rsv[5]; diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index 1dc60c2b2b7a..f0d7bf3f0d67 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -636,6 +636,7 @@ static const struct ib_device_ops hns_roce_dev_ops = { .query_pkey = hns_roce_query_pkey, .query_port = hns_roce_query_port, .reg_user_mr = hns_roce_reg_user_mr, + .port_groups = hns_attr_port_groups, INIT_RDMA_OBJ_SIZE(ib_ah, hns_roce_ah, ibah), INIT_RDMA_OBJ_SIZE(ib_cq, hns_roce_cq, ib_cq), @@ -1110,6 +1111,7 @@ int hns_roce_init(struct hns_roce_dev *hr_dev) goto error_failed_register_device; hns_roce_register_debugfs(hr_dev); + hns_roce_register_sysfs(hr_dev); return 0; @@ -1143,6 +1145,7 @@ int hns_roce_init(struct hns_roce_dev *hr_dev) void hns_roce_exit(struct hns_roce_dev *hr_dev) { + hns_roce_unregister_sysfs(hr_dev); hns_roce_unregister_debugfs(hr_dev); hns_roce_unregister_device(hr_dev); diff --git a/drivers/infiniband/hw/hns/hns_roce_sysfs.c b/drivers/infiniband/hw/hns/hns_roce_sysfs.c new file mode 100644 index 000000000000..4600144dceff --- /dev/null +++ b/drivers/infiniband/hw/hns/hns_roce_sysfs.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * Copyright (c) 2024 Hisilicon Limited. + */ + +#include <rdma/ib_sysfs.h> + +#include "hnae3.h" +#include "hns_roce_device.h" +#include "hns_roce_hw_v2.h" + +static void scc_param_config_work(struct work_struct *work) +{ + struct hns_roce_scc_param *scc_param = container_of(work, + struct hns_roce_scc_param, scc_cfg_dwork.work); + struct hns_roce_dev *hr_dev = scc_param->hr_dev; + + hr_dev->hw->config_scc_param(hr_dev, scc_param->algo_type); +} + +static void get_default_scc_param(struct hns_roce_dev *hr_dev) +{ + int ret; + int i; + + for (i = 0; i < CONG_TYPE_TOTAL; i++) { + hr_dev->scc_param[i].timestamp = jiffies; + ret = hr_dev->hw->query_scc_param(hr_dev, i); + if (ret && ret != -EOPNOTSUPP) + ibdev_warn_ratelimited(&hr_dev->ib_dev, + "failed to get default parameters of scc algo %d, ret = %d.\n", + i, ret); + } +} + +static int alloc_scc_param(struct hns_roce_dev *hr_dev) +{ + struct hns_roce_scc_param *scc_param; + int i; + + scc_param = kvcalloc(CONG_TYPE_TOTAL, sizeof(*scc_param), + GFP_KERNEL); + if (!scc_param) + return -ENOMEM; + + for (i = 0; i < CONG_TYPE_TOTAL; i++) { + scc_param[i].algo_type = i; + scc_param[i].hr_dev = hr_dev; + INIT_DELAYED_WORK(&scc_param[i].scc_cfg_dwork, + scc_param_config_work); + } + + hr_dev->scc_param = scc_param; + + get_default_scc_param(hr_dev); + + return 0; +} + +struct hns_port_cc_attr { + struct ib_port_attribute port_attr; + enum hns_roce_cong_type algo_type; + u32 offset; + u32 size; + u32 max; + u32 min; +}; + +static int scc_attr_check(struct hns_roce_dev *hr_dev, + struct hns_port_cc_attr *scc_attr, u32 port_num) +{ + if (port_num > hr_dev->caps.num_ports) + return -ENODEV; + + if (WARN_ON(scc_attr->size > sizeof(u32))) + return -EINVAL; + + if (WARN_ON(scc_attr->algo_type >= CONG_TYPE_TOTAL)) + return -EINVAL; + + return 0; +} + +static ssize_t scc_attr_show(struct ib_device *ibdev, u32 port_num, + struct ib_port_attribute *attr, char *buf) +{ + struct hns_port_cc_attr *scc_attr = + container_of(attr, struct hns_port_cc_attr, port_attr); + struct hns_roce_dev *hr_dev = to_hr_dev(ibdev); + struct hns_roce_scc_param *scc_param; + __le32 val = 0; + int ret; + + ret = scc_attr_check(hr_dev, scc_attr, port_num); + if (ret) + return ret; + + scc_param = &hr_dev->scc_param[scc_attr->algo_type]; + + memcpy(&val, (void *)scc_param + scc_attr->offset, scc_attr->size); + + return sysfs_emit(buf, "%u\n", le32_to_cpu(val)); +} + +static ssize_t scc_attr_store(struct ib_device *ibdev, u32 port_num, + struct ib_port_attribute *attr, const char *buf, + size_t count) +{ + struct hns_port_cc_attr *scc_attr = + container_of(attr, struct hns_port_cc_attr, port_attr); + struct hns_roce_dev *hr_dev = to_hr_dev(ibdev); + struct hns_roce_scc_param *scc_param; + unsigned long lifespan_jiffies; + unsigned long exp_time; + __le32 attr_val; + u32 val; + int ret; + + ret = scc_attr_check(hr_dev, scc_attr, port_num); + if (ret) + return ret; + + if (kstrtou32(buf, 0, &val)) + return -EINVAL; + + if (val > scc_attr->max || val < scc_attr->min) + return -EINVAL; + + attr_val = cpu_to_le32(val); + scc_param = &hr_dev->scc_param[scc_attr->algo_type]; + memcpy((void *)scc_param + scc_attr->offset, &attr_val, + scc_attr->size); + + /* lifespan is only used for driver */ + if (scc_attr->offset >= offsetof(typeof(*scc_param), lifespan)) + return count; + + lifespan_jiffies = msecs_to_jiffies(scc_param->lifespan); + exp_time = scc_param->timestamp + lifespan_jiffies; + + if (time_is_before_eq_jiffies(exp_time)) { + scc_param->timestamp = jiffies; + queue_delayed_work(hr_dev->irq_workq, &scc_param->scc_cfg_dwork, + lifespan_jiffies); + } + + return count; +} + +static umode_t scc_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int i) +{ + struct ib_port_attribute *port_attr = + container_of(attr, struct ib_port_attribute, attr); + struct hns_port_cc_attr *scc_attr = + container_of(port_attr, struct hns_port_cc_attr, port_attr); + u32 port_num; + struct ib_device *ibdev = ib_port_sysfs_get_ibdev_kobj(kobj, &port_num); + struct hns_roce_dev *hr_dev = to_hr_dev(ibdev); + + if (hr_dev->is_vf || + !(hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_QP_FLOW_CTRL)) + return 0; + + if (!(hr_dev->caps.cong_cap & (1 << scc_attr->algo_type))) + return 0; + + return 0644; +} + +#define __HNS_SCC_ATTR(_name, _type, _offset, _size, _min, _max) { \ + .port_attr = __ATTR(_name, 0644, scc_attr_show, scc_attr_store), \ + .algo_type = _type, \ + .offset = _offset, \ + .size = _size, \ + .min = _min, \ + .max = _max, \ +} + +#define HNS_PORT_DCQCN_CC_ATTR_RW(_name, NAME) \ + struct hns_port_cc_attr hns_roce_port_attr_dcqcn_##_name = \ + __HNS_SCC_ATTR(_name, CONG_TYPE_DCQCN, \ + HNS_ROCE_DCQCN_##NAME##_OFS, \ + HNS_ROCE_DCQCN_##NAME##_SZ, \ + 0, HNS_ROCE_DCQCN_##NAME##_MAX) + +HNS_PORT_DCQCN_CC_ATTR_RW(ai, AI); +HNS_PORT_DCQCN_CC_ATTR_RW(f, F); +HNS_PORT_DCQCN_CC_ATTR_RW(tkp, TKP); +HNS_PORT_DCQCN_CC_ATTR_RW(tmp, TMP); +HNS_PORT_DCQCN_CC_ATTR_RW(alp, ALP); +HNS_PORT_DCQCN_CC_ATTR_RW(max_speed, MAX_SPEED); +HNS_PORT_DCQCN_CC_ATTR_RW(g, G); +HNS_PORT_DCQCN_CC_ATTR_RW(al, AL); +HNS_PORT_DCQCN_CC_ATTR_RW(cnp_time, CNP_TIME); +HNS_PORT_DCQCN_CC_ATTR_RW(ashift, ASHIFT); +HNS_PORT_DCQCN_CC_ATTR_RW(lifespan, LIFESPAN); + +static struct attribute *dcqcn_param_attrs[] = { + &hns_roce_port_attr_dcqcn_ai.port_attr.attr, + &hns_roce_port_attr_dcqcn_f.port_attr.attr, + &hns_roce_port_attr_dcqcn_tkp.port_attr.attr, + &hns_roce_port_attr_dcqcn_tmp.port_attr.attr, + &hns_roce_port_attr_dcqcn_alp.port_attr.attr, + &hns_roce_port_attr_dcqcn_max_speed.port_attr.attr, + &hns_roce_port_attr_dcqcn_g.port_attr.attr, + &hns_roce_port_attr_dcqcn_al.port_attr.attr, + &hns_roce_port_attr_dcqcn_cnp_time.port_attr.attr, + &hns_roce_port_attr_dcqcn_ashift.port_attr.attr, + &hns_roce_port_attr_dcqcn_lifespan.port_attr.attr, + NULL, +}; + +static const struct attribute_group dcqcn_cc_param_group = { + .name = "dcqcn_cc_param", + .attrs = dcqcn_param_attrs, + .is_visible = scc_attr_is_visible, +}; + +#define HNS_PORT_LDCP_CC_ATTR_RW(_name, NAME) \ + struct hns_port_cc_attr hns_roce_port_attr_ldcp_##_name = \ + __HNS_SCC_ATTR(_name, CONG_TYPE_LDCP, \ + HNS_ROCE_LDCP_##NAME##_OFS, \ + HNS_ROCE_LDCP_##NAME##_SZ, \ + 0, HNS_ROCE_LDCP_##NAME##_MAX) + +HNS_PORT_LDCP_CC_ATTR_RW(cwd0, CWD0); +HNS_PORT_LDCP_CC_ATTR_RW(alpha, ALPHA); +HNS_PORT_LDCP_CC_ATTR_RW(gamma, GAMMA); +HNS_PORT_LDCP_CC_ATTR_RW(beta, BETA); +HNS_PORT_LDCP_CC_ATTR_RW(eta, ETA); +HNS_PORT_LDCP_CC_ATTR_RW(lifespan, LIFESPAN); + +static struct attribute *ldcp_param_attrs[] = { + &hns_roce_port_attr_ldcp_cwd0.port_attr.attr, + &hns_roce_port_attr_ldcp_alpha.port_attr.attr, + &hns_roce_port_attr_ldcp_gamma.port_attr.attr, + &hns_roce_port_attr_ldcp_beta.port_attr.attr, + &hns_roce_port_attr_ldcp_eta.port_attr.attr, + &hns_roce_port_attr_ldcp_lifespan.port_attr.attr, + NULL, +}; + +static const struct attribute_group ldcp_cc_param_group = { + .name = "ldcp_cc_param", + .attrs = ldcp_param_attrs, + .is_visible = scc_attr_is_visible, +}; + +#define HNS_PORT_HC3_CC_ATTR_RW(_name, NAME) \ + struct hns_port_cc_attr hns_roce_port_attr_hc3_##_name = \ + __HNS_SCC_ATTR(_name, CONG_TYPE_HC3, \ + HNS_ROCE_HC3_##NAME##_OFS, \ + HNS_ROCE_HC3_##NAME##_SZ, \ + 0, HNS_ROCE_HC3_##NAME##_MAX) + +HNS_PORT_HC3_CC_ATTR_RW(initial_window, INITIAL_WINDOW); +HNS_PORT_HC3_CC_ATTR_RW(bandwidth, BANDWIDTH); +HNS_PORT_HC3_CC_ATTR_RW(qlen_shift, QLEN_SHIFT); +HNS_PORT_HC3_CC_ATTR_RW(port_usage_shift, PORT_USAGE_SHIFT); +HNS_PORT_HC3_CC_ATTR_RW(over_period, OVER_PERIOD); +HNS_PORT_HC3_CC_ATTR_RW(max_stage, MAX_STAGE); +HNS_PORT_HC3_CC_ATTR_RW(gamma_shift, GAMMA_SHIFT); +HNS_PORT_HC3_CC_ATTR_RW(lifespan, LIFESPAN); + +static struct attribute *hc3_param_attrs[] = { + &hns_roce_port_attr_hc3_initial_window.port_attr.attr, + &hns_roce_port_attr_hc3_bandwidth.port_attr.attr, + &hns_roce_port_attr_hc3_qlen_shift.port_attr.attr, + &hns_roce_port_attr_hc3_port_usage_shift.port_attr.attr, + &hns_roce_port_attr_hc3_over_period.port_attr.attr, + &hns_roce_port_attr_hc3_max_stage.port_attr.attr, + &hns_roce_port_attr_hc3_gamma_shift.port_attr.attr, + &hns_roce_port_attr_hc3_lifespan.port_attr.attr, + NULL, +}; + +static const struct attribute_group hc3_cc_param_group = { + .name = "hc3_cc_param", + .attrs = hc3_param_attrs, + .is_visible = scc_attr_is_visible, +}; + +#define HNS_PORT_DIP_CC_ATTR_RW(_name, NAME) \ + struct hns_port_cc_attr hns_roce_port_attr_dip_##_name = \ + __HNS_SCC_ATTR(_name, CONG_TYPE_DIP, \ + HNS_ROCE_DIP_##NAME##_OFS, \ + HNS_ROCE_DIP_##NAME##_SZ, \ + 0, HNS_ROCE_DIP_##NAME##_MAX) + +HNS_PORT_DIP_CC_ATTR_RW(ai, AI); +HNS_PORT_DIP_CC_ATTR_RW(f, F); +HNS_PORT_DIP_CC_ATTR_RW(tkp, TKP); +HNS_PORT_DIP_CC_ATTR_RW(tmp, TMP); +HNS_PORT_DIP_CC_ATTR_RW(alp, ALP); +HNS_PORT_DIP_CC_ATTR_RW(max_speed, MAX_SPEED); +HNS_PORT_DIP_CC_ATTR_RW(g, G); +HNS_PORT_DIP_CC_ATTR_RW(al, AL); +HNS_PORT_DIP_CC_ATTR_RW(cnp_time, CNP_TIME); +HNS_PORT_DIP_CC_ATTR_RW(ashift, ASHIFT); +HNS_PORT_DIP_CC_ATTR_RW(lifespan, LIFESPAN); + +static struct attribute *dip_param_attrs[] = { + &hns_roce_port_attr_dip_ai.port_attr.attr, + &hns_roce_port_attr_dip_f.port_attr.attr, + &hns_roce_port_attr_dip_tkp.port_attr.attr, + &hns_roce_port_attr_dip_tmp.port_attr.attr, + &hns_roce_port_attr_dip_alp.port_attr.attr, + &hns_roce_port_attr_dip_max_speed.port_attr.attr, + &hns_roce_port_attr_dip_g.port_attr.attr, + &hns_roce_port_attr_dip_al.port_attr.attr, + &hns_roce_port_attr_dip_cnp_time.port_attr.attr, + &hns_roce_port_attr_dip_ashift.port_attr.attr, + &hns_roce_port_attr_dip_lifespan.port_attr.attr, + NULL, +}; + +static const struct attribute_group dip_cc_param_group = { + .name = "dip_cc_param", + .attrs = dip_param_attrs, + .is_visible = scc_attr_is_visible, +}; + +const struct attribute_group *hns_attr_port_groups[] = { + &dcqcn_cc_param_group, + &ldcp_cc_param_group, + &hc3_cc_param_group, + &dip_cc_param_group, + NULL, +}; + +void hns_roce_register_sysfs(struct hns_roce_dev *hr_dev) +{ + int ret; + + ret = alloc_scc_param(hr_dev); + if (ret) + dev_err(hr_dev->dev, "alloc scc param failed, ret = %d!\n", + ret); +} + +void hns_roce_unregister_sysfs(struct hns_roce_dev *hr_dev) +{ + if (hr_dev->scc_param) + kvfree(hr_dev->scc_param); +} -- 2.30.0