driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I6J5O7
---------------------------------------------------------------
hns roce support 4 congestion control algorithms. Each algorihm involves multiple parameters. This patch add port sysfs directory for each algorithm, which allows users to modify the parameters of these algorithms.
Signed-off-by: Chengchang Tang tangchengchang@huawei.com Reviewed-by: Yangyang Li liyangyang20@huawei.com --- drivers/infiniband/hw/hns/Makefile | 2 +- drivers/infiniband/hw/hns/hns_roce_device.h | 42 +- drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 77 ++++ drivers/infiniband/hw/hns/hns_roce_hw_v2.h | 132 ++++++ drivers/infiniband/hw/hns/hns_roce_main.c | 2 + drivers/infiniband/hw/hns/hns_roce_sysfs.c | 445 ++++++++++++++++++++ 6 files changed, 695 insertions(+), 5 deletions(-) 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 77d8e41ffe7b..09f95fedd15d 100644 --- a/drivers/infiniband/hw/hns/Makefile +++ b/drivers/infiniband/hw/hns/Makefile @@ -10,7 +10,7 @@ ccflags-y += -I $(srctree)/drivers/net/ethernet/hisilicon/hns3/hns3_common 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_bond.o hns_roce_dca.o hns_roce_debugfs.o + hns_roce_bond.o hns_roce_dca.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 869d952b32b2..9d5580040e6b 100644 --- a/drivers/infiniband/hw/hns/hns_roce_device.h +++ b/drivers/infiniband/hw/hns/hns_roce_device.h @@ -767,11 +767,19 @@ struct hns_roce_eq_table { struct hns_roce_eq *eq; };
+enum hns_roce_scc_algo { + HNS_ROCE_SCC_ALGO_DCQCN = 0, + HNS_ROCE_SCC_ALGO_LDCP, + HNS_ROCE_SCC_ALGO_HC3, + HNS_ROCE_SCC_ALGO_DIP, + HNS_ROCE_SCC_ALGO_TOTAL, +}; + enum cong_type { - CONG_TYPE_DCQCN, - CONG_TYPE_LDCP, - CONG_TYPE_HC3, - CONG_TYPE_DIP, + CONG_TYPE_DCQCN = 1 << HNS_ROCE_SCC_ALGO_DCQCN, + CONG_TYPE_LDCP = 1 << HNS_ROCE_SCC_ALGO_LDCP, + CONG_TYPE_HC3 = 1 << HNS_ROCE_SCC_ALGO_HC3, + CONG_TYPE_DIP = 1 << HNS_ROCE_SCC_ALGO_DIP, };
struct hns_roce_caps { @@ -1016,6 +1024,28 @@ struct hns_roce_hw { int (*bond_init)(struct hns_roce_dev *hr_dev); bool (*bond_is_active)(struct hns_roce_dev *hr_dev); struct net_device *(*get_bond_netdev)(struct hns_roce_dev *hr_dev); + int (*config_scc_param)(struct hns_roce_dev *hr_dev, u8 port_num, + enum hns_roce_scc_algo algo); + int (*query_scc_param)(struct hns_roce_dev *hr_dev, u8 port_num, + enum hns_roce_scc_algo 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_scc_algo algo_type; + struct delayed_work scc_cfg_dwork; + struct hns_roce_dev *hr_dev; + u8 port_num; +}; + +struct hns_roce_port { + struct hns_roce_dev *hr_dev; + u8 port_num; + struct kobject kobj; + struct hns_roce_scc_param *scc_param; };
struct hns_roce_dev { @@ -1094,6 +1124,7 @@ struct hns_roce_dev { struct delayed_work bond_work; struct hns_roce_bond_group *bond_grp; struct netdev_lag_lower_state_info slave_state; + struct hns_roce_port port_data[HNS_ROCE_MAX_PORTS]; atomic64_t *dfx_cnt; };
@@ -1379,4 +1410,7 @@ 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); +int hns_roce_create_port_files(struct ib_device *ibdev, u8 port_num, + struct kobject *kobj); +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 a76a57c7e36a..c9826a010f38 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -7146,6 +7146,81 @@ static void hns_roce_v2_cleanup_eq_table(struct hns_roce_dev *hr_dev) kfree(eq_table->eq); }
+static 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, + u8 port_num, + enum hns_roce_scc_algo algo) +{ + struct hns_roce_scc_param *scc_param; + struct hns_roce_cmq_desc desc; + struct hns_roce_port *pdata; + int ret; + + if (port_num > hr_dev->caps.num_ports) { + ibdev_err_ratelimited(&hr_dev->ib_dev, + "invalid port num %u.\n", port_num); + return -ENODEV; + } + + if (algo >= HNS_ROCE_SCC_ALGO_TOTAL) { + ibdev_err_ratelimited(&hr_dev->ib_dev, "invalid SCC algo.\n"); + return -EINVAL; + } + + hns_roce_cmq_setup_basic_desc(&desc, scc_opcode[algo], false); + pdata = &hr_dev->port_data[port_num - 1]; + scc_param = &pdata->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, + u8 port_num, enum hns_roce_scc_algo algo) +{ + struct hns_roce_scc_param *scc_param; + struct hns_roce_cmq_desc desc; + struct hns_roce_port *pdata; + int ret; + + if (port_num > hr_dev->caps.num_ports) { + ibdev_err_ratelimited(&hr_dev->ib_dev, + "invalid port num %u.\n", port_num); + return -ENODEV; + } + + if (algo >= HNS_ROCE_SCC_ALGO_TOTAL) { + ibdev_err_ratelimited(&hr_dev->ib_dev, "invalid SCC algo.\n"); + 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; + } + + pdata = &hr_dev->port_data[port_num - 1]; + scc_param = &pdata->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, @@ -7198,6 +7273,8 @@ static const struct hns_roce_hw hns_roce_hw_v2 = { .bond_is_active = hns_roce_bond_is_active, .get_bond_netdev = hns_roce_get_bond_netdev, .query_hw_counter = hns_roce_hw_v2_query_counter, + .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 7d5c304a8342..90401577865e 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h @@ -227,6 +227,10 @@ enum { enum hns_roce_opcode_type { HNS_QUERY_FW_VER = 0x0001, HNS_QUERY_MAC_TYPE = 0x0389, + 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, @@ -1468,6 +1472,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 d98825154a82..4d1d2629d8f0 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -870,6 +870,7 @@ static const struct ib_device_ops hns_roce_dev_ops = { .reg_user_mr = hns_roce_reg_user_mr, .alloc_hw_stats = hns_roce_alloc_hw_port_stats, .get_hw_stats = hns_roce_get_hw_stats, + .init_port = hns_roce_create_port_files,
INIT_RDMA_OBJ_SIZE(ib_ah, hns_roce_ah, ibah), INIT_RDMA_OBJ_SIZE(ib_cq, hns_roce_cq, ib_cq), @@ -1441,6 +1442,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_device(hr_dev); hns_roce_unregister_debugfs(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..507cc7147a2e --- /dev/null +++ b/drivers/infiniband/hw/hns/hns_roce_sysfs.c @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * Copyright (c) 2023 Hisilicon Limited. + */ + +#include "hnae3.h" +#include "hns_roce_device.h" +#include "hns_roce_hw_v2.h" + +struct hns_port_attribute { + struct attribute attr; + ssize_t (*show)(struct hns_roce_port *pdata, + struct hns_port_attribute *attr, char *buf); + ssize_t (*store)(struct hns_roce_port *pdata, + struct hns_port_attribute *attr, const char *buf, + size_t count); +}; + +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->port_num, + scc_param->algo_type); +} + +static int alloc_scc_param(struct hns_roce_dev *hr_dev, + struct hns_roce_port *pdata) +{ + struct hns_roce_scc_param *scc_param; + int i; + + scc_param = kcalloc(HNS_ROCE_SCC_ALGO_TOTAL, sizeof(*scc_param), + GFP_KERNEL); + if (!scc_param) + return -ENOMEM; + + for (i = 0; i < HNS_ROCE_SCC_ALGO_TOTAL; i++) { + scc_param[i].algo_type = i; + scc_param[i].timestamp = jiffies; + scc_param[i].hr_dev = hr_dev; + scc_param[i].port_num = pdata->port_num; + INIT_DELAYED_WORK(&scc_param[i].scc_cfg_dwork, + scc_param_config_work); + } + + pdata->scc_param = scc_param; + return 0; +} + +struct hns_port_cc_attr { + struct hns_port_attribute port_attr; + enum hns_roce_scc_algo algo_type; + u32 offset; + u32 size; + u32 max; + u32 min; +}; + +static int scc_attr_check(struct hns_port_cc_attr *scc_attr) +{ + if (WARN_ON(scc_attr->size > sizeof(u32))) + return -EINVAL; + + if (WARN_ON(scc_attr->algo_type >= HNS_ROCE_SCC_ALGO_TOTAL)) + return -EINVAL; + return 0; +} + +static ssize_t scc_attr_show(struct hns_roce_port *pdata, + struct hns_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_scc_param *scc_param; + unsigned long exp_time; + __le32 val = 0; + int ret; + + ret = scc_attr_check(scc_attr); + if (ret) + return ret; + + scc_param = &pdata->scc_param[scc_attr->algo_type]; + + /* Only HW param need be queried */ + if (scc_attr->offset < offsetof(typeof(*scc_param), lifespan)) { + exp_time = scc_param->timestamp + + msecs_to_jiffies(scc_param->lifespan); + + if (time_is_before_eq_jiffies(exp_time)) { + scc_param->timestamp = jiffies; + pdata->hr_dev->hw->query_scc_param(pdata->hr_dev, + pdata->port_num, 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 hns_roce_port *pdata, + struct hns_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_scc_param *scc_param; + unsigned long lifespan_jiffies; + unsigned long exp_time; + __le32 attr_val; + u32 val; + int ret; + + ret = scc_attr_check(scc_attr); + 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 = &pdata->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(pdata->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 hns_port_attribute *port_attr = + container_of(attr, struct hns_port_attribute, attr); + struct hns_port_cc_attr *scc_attr = + container_of(port_attr, struct hns_port_cc_attr, port_attr); + struct hns_roce_port *pdata = + container_of(kobj, struct hns_roce_port, kobj); + struct hns_roce_dev *hr_dev = pdata->hr_dev; + + if (hr_dev->is_vf || + !(hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_QP_FLOW_CTRL)) + return 0; + + if (!(hr_dev->caps.cong_type & (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, HNS_ROCE_SCC_ALGO_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, HNS_ROCE_SCC_ALGO_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, HNS_ROCE_SCC_ALGO_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, HNS_ROCE_SCC_ALGO_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, +}; + +static ssize_t hns_roce_port_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct hns_port_attribute *port_attr = + container_of(attr, struct hns_port_attribute, attr); + struct hns_roce_port *p = + container_of(kobj, struct hns_roce_port, kobj); + + if (!port_attr->show) + return -EIO; + + return port_attr->show(p, port_attr, buf); +} + +static ssize_t hns_roce_port_attr_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t count) +{ + struct hns_port_attribute *port_attr = + container_of(attr, struct hns_port_attribute, attr); + struct hns_roce_port *p = + container_of(kobj, struct hns_roce_port, kobj); + + if (!port_attr->store) + return -EIO; + + return port_attr->store(p, port_attr, buf, count); +} + +static void hns_roce_port_release(struct kobject *kobj) +{ + struct hns_roce_port *pdata = + container_of(kobj, struct hns_roce_port, kobj); + + kfree(pdata->scc_param); +} + +static const struct sysfs_ops hns_roce_port_ops = { + .show = hns_roce_port_attr_show, + .store = hns_roce_port_attr_store, +}; + +static struct kobj_type hns_roce_port_ktype = { + .release = hns_roce_port_release, + .sysfs_ops = &hns_roce_port_ops, +}; + +int hns_roce_create_port_files(struct ib_device *ibdev, u8 port_num, + struct kobject *kobj) +{ + struct hns_roce_dev *hr_dev = to_hr_dev(ibdev); + struct hns_roce_port *pdata; + int ret; + + if (!port_num || port_num > hr_dev->caps.num_ports) { + ibdev_err(ibdev, "fail to create port sysfs for invalid port %u.\n", + port_num); + return -ENODEV; + } + + pdata = &hr_dev->port_data[port_num - 1]; + pdata->hr_dev = hr_dev; + pdata->port_num = port_num; + ret = kobject_init_and_add(&pdata->kobj, &hns_roce_port_ktype, + kobj, "cc_param"); + if (ret) { + ibdev_err(ibdev, "fail to create port(%u) sysfs, ret = %d.\n", + port_num, ret); + goto fail_kobj; + } + kobject_uevent(&pdata->kobj, KOBJ_ADD); + + ret = sysfs_create_groups(&pdata->kobj, hns_attr_port_groups); + if (ret) { + ibdev_err(ibdev, + "fail to create port(%u) cc param sysfs, ret = %d.\n", + port_num, ret); + goto fail_kobj; + } + + ret = alloc_scc_param(hr_dev, pdata); + if (ret) { + dev_err(hr_dev->dev, "alloc scc param failed, ret = %d!\n", + ret); + goto fail_group; + } + + return ret; + +fail_group: + sysfs_remove_groups(&pdata->kobj, hns_attr_port_groups); + +fail_kobj: + kobject_put(&pdata->kobj); + + return ret; +} + +static void hns_roce_unregister_port_sysfs(struct hns_roce_dev *hr_dev, + u8 port_num) +{ + struct hns_roce_port *pdata; + + pdata = &hr_dev->port_data[port_num]; + sysfs_remove_groups(&pdata->kobj, hns_attr_port_groups); + kobject_put(&pdata->kobj); +} + +void hns_roce_unregister_sysfs(struct hns_roce_dev *hr_dev) +{ + int i; + + for (i = 0; i < hr_dev->caps.num_ports; i++) + hns_roce_unregister_port_sysfs(hr_dev, i); +}