This patch provides a pair of APIs rdma_register_poe_channel()/ rdma_unregister_poe_channel() to support register/unregister POE channels which could be used for CQs.
At the same time, this patch also supports user creation of CQ bound to a POE channel.
BTW, This patch also add a debugfs for POE to help with problem diagnosis.
Signed-off-by: Chengchang Tang tangchengchang@huawei.com --- drivers/infiniband/hw/hns/Makefile | 3 +- drivers/infiniband/hw/hns/hns_roce_cq.c | 58 +++++++++++++- drivers/infiniband/hw/hns/hns_roce_debugfs.c | 111 +++++++++++++++++++++++++++ drivers/infiniband/hw/hns/hns_roce_device.h | 27 +++++++ drivers/infiniband/hw/hns/hns_roce_ext.c | 39 ++++++++++ drivers/infiniband/hw/hns/hns_roce_ext.h | 22 ++++++ drivers/infiniband/hw/hns/hns_roce_hw_v2.c | 71 +++++++++++++++++ drivers/infiniband/hw/hns/hns_roce_hw_v2.h | 17 ++++ drivers/infiniband/hw/hns/hns_roce_main.c | 32 ++++++++ drivers/infiniband/hw/hns/hns_roce_poe.c | 97 +++++++++++++++++++++++ include/uapi/rdma/hns-abi.h | 8 ++ 11 files changed, 482 insertions(+), 3 deletions(-) create mode 100644 drivers/infiniband/hw/hns/hns_roce_ext.c create mode 100644 drivers/infiniband/hw/hns/hns_roce_ext.h create mode 100644 drivers/infiniband/hw/hns/hns_roce_poe.c
diff --git a/drivers/infiniband/hw/hns/Makefile b/drivers/infiniband/hw/hns/Makefile index 09f95fe..04dce5e 100644 --- a/drivers/infiniband/hw/hns/Makefile +++ b/drivers/infiniband/hw/hns/Makefile @@ -10,7 +10,8 @@ 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_sysfs.o + hns_roce_bond.o hns_roce_dca.o hns_roce_debugfs.o hns_roce_sysfs.o \ + hns_roce_poe.o hns_roce_ext.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_cq.c b/drivers/infiniband/hw/hns/hns_roce_cq.c index 6997c3d..3bc029c 100644 --- a/drivers/infiniband/hw/hns/hns_roce_cq.c +++ b/drivers/infiniband/hw/hns/hns_roce_cq.c @@ -311,10 +311,32 @@ static int get_cq_ucmd(struct hns_roce_cq *hr_cq, struct ib_udata *udata, return 0; }
-static void set_cq_param(struct hns_roce_cq *hr_cq, u32 cq_entries, int vector, +static int set_poe_param(struct hns_roce_dev *hr_dev, + struct hns_roce_cq *hr_cq, + struct hns_roce_ib_create_cq *ucmd) +{ + if (!(ucmd->create_flags & HNS_ROCE_CREATE_CQ_FLAGS_POE_MODE)) + return 0; + + if (!poe_is_supported(hr_dev)) + return -EOPNOTSUPP; + + if (ucmd->poe_channel >= hr_dev->poe_ctx.poe_num) + return -EINVAL; + + if (!hr_dev->poe_ctx.poe_ch[ucmd->poe_channel].en) + return -EFAULT; + + hr_cq->flags |= HNS_ROCE_CQ_FLAG_POE_EN; + hr_cq->poe_channel = ucmd->poe_channel; + return 0; +} + +static int set_cq_param(struct hns_roce_cq *hr_cq, u32 cq_entries, int vector, struct hns_roce_ib_create_cq *ucmd) { struct hns_roce_dev *hr_dev = to_hr_dev(hr_cq->ib_cq.device); + int ret;
cq_entries = max(cq_entries, hr_dev->caps.min_cqes); cq_entries = roundup_pow_of_two(cq_entries); @@ -325,6 +347,15 @@ static void set_cq_param(struct hns_roce_cq *hr_cq, u32 cq_entries, int vector, spin_lock_init(&hr_cq->lock); INIT_LIST_HEAD(&hr_cq->sq_list); INIT_LIST_HEAD(&hr_cq->rq_list); + + if (!(ucmd->create_flags)) + return 0; + + ret = set_poe_param(hr_dev, hr_cq, ucmd); + if (ret) + return ret; + + return 0; }
static int set_cqe_size(struct hns_roce_cq *hr_cq, struct ib_udata *udata, @@ -353,6 +384,22 @@ static int set_cqe_size(struct hns_roce_cq *hr_cq, struct ib_udata *udata, return 0; }
+static void poe_ch_ref_cnt_inc(struct hns_roce_dev *hr_dev, + struct hns_roce_cq *hr_cq) +{ + struct hns_roce_poe_ch *poe_ch = + &hr_dev->poe_ctx.poe_ch[hr_cq->poe_channel]; + refcount_inc(&poe_ch->ref_cnt); +} + +static void poe_ch_ref_cnt_dec(struct hns_roce_dev *hr_dev, + struct hns_roce_cq *hr_cq) +{ + struct hns_roce_poe_ch *poe_ch = + &hr_dev->poe_ctx.poe_ch[hr_cq->poe_channel]; + refcount_dec(&poe_ch->ref_cnt); +} + int hns_roce_create_cq(struct ib_cq *ib_cq, const struct ib_cq_init_attr *attr, struct ib_udata *udata) { @@ -379,7 +426,9 @@ int hns_roce_create_cq(struct ib_cq *ib_cq, const struct ib_cq_init_attr *attr,
}
- set_cq_param(hr_cq, attr->cqe, attr->comp_vector, &ucmd); + ret = set_cq_param(hr_cq, attr->cqe, attr->comp_vector, &ucmd); + if (ret) + goto err_out;
ret = set_cqe_size(hr_cq, udata, &ucmd); if (ret) @@ -412,6 +461,9 @@ int hns_roce_create_cq(struct ib_cq *ib_cq, const struct ib_cq_init_attr *attr,
if (udata) { resp.cqn = hr_cq->cqn; + resp.cap_flags = hr_cq->flags; + if (hr_cq->flags & HNS_ROCE_CQ_FLAG_POE_EN) + poe_ch_ref_cnt_inc(hr_dev, hr_cq); ret = ib_copy_to_udata(udata, &resp, min(udata->outlen, sizeof(resp))); if (ret) @@ -444,6 +496,8 @@ int hns_roce_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) struct hns_roce_dev *hr_dev = to_hr_dev(ib_cq->device); struct hns_roce_cq *hr_cq = to_hr_cq(ib_cq);
+ if (hr_cq->flags & HNS_ROCE_CQ_FLAG_POE_EN) + poe_ch_ref_cnt_dec(hr_dev, hr_cq); free_cqc(hr_dev, hr_cq); free_cqn(hr_dev, hr_cq->cqn); free_cq_db(hr_dev, hr_cq, udata); diff --git a/drivers/infiniband/hw/hns/hns_roce_debugfs.c b/drivers/infiniband/hw/hns/hns_roce_debugfs.c index 35d81e7..023b111 100644 --- a/drivers/infiniband/hw/hns/hns_roce_debugfs.c +++ b/drivers/infiniband/hw/hns/hns_roce_debugfs.c @@ -74,10 +74,22 @@ struct hns_dca_debugfs { struct hns_dca_ctx_debugfs kctx; /* kDCA context */ };
+struct hns_poe_ch_debugfs { + struct dentry *root; /* dev debugfs entry */ + struct hns_debugfs_seqfile en; /* enable stats fir this ch */ + struct hns_debugfs_seqfile addr; /* addr of this ch */ + struct hns_debugfs_seqfile ref_cnt; /* ref_cnt for this ch */ +}; + +struct hns_poe_debugfs { + struct dentry *root; /* dev debugfs entry */ +}; + /* Debugfs for device */ struct hns_roce_dev_debugfs { struct dentry *root; struct hns_dca_debugfs *dca_root; + struct hns_poe_debugfs *poe_root; };
struct dca_mem_stats { @@ -497,6 +509,97 @@ static void destroy_dca_debugfs(struct hns_dca_debugfs *dca_dbgfs) kfree(dca_dbgfs); }
+static int poe_debugfs_en_show(struct seq_file *file, void *offset) +{ + struct hns_roce_poe_ch *poe_ch = file->private; + + seq_printf(file, "%-10s\n", poe_ch->en ? "enable" : "disable"); + return 0; +} + +static int poe_debugfs_addr_show(struct seq_file *file, void *offset) +{ +#define POE_ADDR_OFFSET_MASK GENMASK(31, 0) + struct hns_roce_poe_ch *poe_ch = file->private; + + seq_printf(file, "0x%llx\n", poe_ch->addr & POE_ADDR_OFFSET_MASK); + return 0; +} + +static int poe_debugfs_ref_cnt_show(struct seq_file *file, void *offset) +{ + struct hns_roce_poe_ch *poe_ch = file->private; + + seq_printf(file, "0x%-10u\n", refcount_read(&poe_ch->ref_cnt)); + return 0; +} + +static void init_poe_ch_debugfs(struct hns_roce_dev *hr_dev, uint8_t index, + struct dentry *parent) +{ +#define POE_CH_NAME_LEN 10 + struct hns_roce_poe_ch *poe_ch = &hr_dev->poe_ctx.poe_ch[index]; + struct hns_poe_ch_debugfs *dbgfs; + char name[POE_CH_NAME_LEN]; + + dbgfs = kvzalloc(sizeof(*dbgfs), GFP_KERNEL); + if (!dbgfs) + return; + + snprintf(name, sizeof(name), "poe_%u", index); + dbgfs->root = debugfs_create_dir(name, parent); + + init_debugfs_seqfile(&dbgfs->en, "en", dbgfs->root, + poe_debugfs_en_show, poe_ch); + init_debugfs_seqfile(&dbgfs->addr, "addr", dbgfs->root, + poe_debugfs_addr_show, poe_ch); + init_debugfs_seqfile(&dbgfs->ref_cnt, "ref_cnt", dbgfs->root, + poe_debugfs_ref_cnt_show, poe_ch); + poe_ch->poe_ch_debugfs = dbgfs; +} + +static void cleanup_poe_ch_debugfs(struct hns_roce_dev *hr_dev, uint8_t index) +{ + struct hns_roce_poe_ch *poe_ch = &hr_dev->poe_ctx.poe_ch[index]; + struct hns_poe_ch_debugfs *dbgfs = poe_ch->poe_ch_debugfs; + + cleanup_debugfs_seqfile(&dbgfs->en); + cleanup_debugfs_seqfile(&dbgfs->addr); + cleanup_debugfs_seqfile(&dbgfs->ref_cnt); + debugfs_remove_recursive(dbgfs->root); + kvfree(dbgfs); +} + +static struct hns_poe_debugfs * +create_poe_debugfs(struct hns_roce_dev *hr_dev, struct dentry *parent) +{ + struct hns_poe_debugfs *dbgfs; + int i; + + dbgfs = kvzalloc(sizeof(*dbgfs), GFP_KERNEL); + if (!dbgfs) + return NULL; + + dbgfs->root = debugfs_create_dir("poe", parent); + + for (i = 0; i < hr_dev->poe_ctx.poe_num; i++) + init_poe_ch_debugfs(hr_dev, i, dbgfs->root); + + return dbgfs; +} + +static void destroy_poe_debugfs(struct hns_roce_dev *hr_dev, + struct hns_poe_debugfs *poe_dbgfs) +{ + int i; + + for (i = 0; i < hr_dev->poe_ctx.poe_num; i++) + cleanup_poe_ch_debugfs(hr_dev, i); + + debugfs_remove_recursive(poe_dbgfs->root); + kvfree(poe_dbgfs); +} + /* debugfs for ucontext */ void hns_roce_register_uctx_debugfs(struct hns_roce_dev *hr_dev, struct hns_roce_ucontext *uctx) @@ -553,6 +656,9 @@ void hns_roce_register_debugfs(struct hns_roce_dev *hr_dev) if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_DCA_MODE) dbgfs->dca_root = create_dca_debugfs(hr_dev, dbgfs->root);
+ if (poe_is_supported(hr_dev)) + dbgfs->poe_root = create_poe_debugfs(hr_dev, dbgfs->root); + hr_dev->dbgfs = dbgfs; }
@@ -572,6 +678,11 @@ void hns_roce_unregister_debugfs(struct hns_roce_dev *hr_dev) dbgfs->dca_root = NULL; }
+ if (dbgfs->poe_root) { + destroy_poe_debugfs(hr_dev, dbgfs->poe_root); + dbgfs->poe_root = NULL; + } + debugfs_remove_recursive(dbgfs->root); hr_dev->dbgfs = NULL; kfree(dbgfs); diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h index 5d48990..ef8bef9 100644 --- a/drivers/infiniband/hw/hns/hns_roce_device.h +++ b/drivers/infiniband/hw/hns/hns_roce_device.h @@ -164,6 +164,7 @@ enum { HNS_ROCE_CAP_FLAG_CQE_INLINE = BIT(19), HNS_ROCE_CAP_FLAG_BOND = BIT(21), HNS_ROCE_CAP_FLAG_SRQ_RECORD_DB = BIT(22), + HNS_ROCE_CAP_FLAG_POE = BIT(27), };
#define HNS_ROCE_DB_TYPE_COUNT 2 @@ -221,6 +222,18 @@ struct hns_user_mmap_entry { u64 address; };
+struct hns_roce_poe_ch { + uint8_t en; + refcount_t ref_cnt; + uint64_t addr; + void *poe_ch_debugfs; +}; + +struct hns_roce_poe_ctx { + uint8_t poe_num; + struct hns_roce_poe_ch *poe_ch; +}; + struct hns_roce_dca_ctx { struct list_head pool; /* all DCA mems link to @pool */ spinlock_t pool_lock; /* protect @pool */ @@ -493,6 +506,7 @@ struct hns_roce_cq { struct list_head rq_list; /* all qps on this recv cq */ int is_armed; /* cq is armed */ struct list_head node; /* all armed cqs are on a list */ + u8 poe_channel; };
struct hns_roce_idx_que { @@ -788,6 +802,8 @@ enum congest_type { HNS_ROCE_CONGEST_TYPE_DIP = 1 << HNS_ROCE_SCC_ALGO_DIP, };
+#define HNS_ROCE_POE_CH_NUM 4 + struct hns_roce_caps { u64 fw_ver; u8 num_ports; @@ -919,6 +935,7 @@ struct hns_roce_caps { u16 default_ceq_arm_st; u8 congest_type; u8 default_congest_type; + u8 poe_ch_num; };
enum hns_roce_device_state { @@ -1033,6 +1050,7 @@ struct hns_roce_hw { 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); + int (*cfg_poe_ch)(struct hns_roce_dev *hr_dev, u32 index, u64 poe_addr); };
#define HNS_ROCE_SCC_PARAM_SIZE 4 @@ -1132,6 +1150,7 @@ struct hns_roce_dev { struct notifier_block bond_nb; struct hns_roce_port port_data[HNS_ROCE_MAX_PORTS]; atomic64_t *dfx_cnt; + struct hns_roce_poe_ctx poe_ctx; /* poe ch array */ };
static inline struct hns_roce_dev *to_hr_dev(struct ib_device *ib_dev) @@ -1289,6 +1308,11 @@ static inline u8 get_hr_bus_num(struct hns_roce_dev *hr_dev) return hr_dev->pci_dev->bus->number; }
+static inline bool poe_is_supported(struct hns_roce_dev *hr_dev) +{ + return !!(hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_POE); +} + 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);
@@ -1430,4 +1454,7 @@ hns_roce_user_mmap_entry_insert(struct ib_ucontext *ucontext, u64 address, 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); +int hns_roce_register_poe_channel(struct hns_roce_dev *hr_dev, u8 channel, + u64 poe_addr); +int hns_roce_unregister_poe_channel(struct hns_roce_dev *hr_dev, u8 channel); #endif /* _HNS_ROCE_DEVICE_H */ diff --git a/drivers/infiniband/hw/hns/hns_roce_ext.c b/drivers/infiniband/hw/hns/hns_roce_ext.c new file mode 100644 index 00000000..9aa17de --- /dev/null +++ b/drivers/infiniband/hw/hns/hns_roce_ext.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * Copyright (c) 2023 Hisilicon Limited. + */ + +#include <rdma/ib_verbs.h> +#include "hns_roce_device.h" + +static bool is_hns_roce(struct ib_device *ib_dev) +{ + if (ib_dev && ib_dev->ops.driver_id == RDMA_DRIVER_HNS) + return true; + + return false; +} + +int rdma_register_poe_channel(struct ib_device *ib_dev, u8 channel, + u64 poe_addr) +{ + struct hns_roce_dev *hr_dev = to_hr_dev(ib_dev); + + if (!is_hns_roce(ib_dev)) + return -EOPNOTSUPP; + + return hns_roce_register_poe_channel(hr_dev, channel, poe_addr); +} +EXPORT_SYMBOL(rdma_register_poe_channel); + +int rdma_unregister_poe_channel(struct ib_device *ib_dev, u8 channel) +{ + struct hns_roce_dev *hr_dev = to_hr_dev(ib_dev); + + if (!is_hns_roce(ib_dev)) + return -EOPNOTSUPP; + + return hns_roce_unregister_poe_channel(hr_dev, channel); +} +EXPORT_SYMBOL(rdma_unregister_poe_channel); + diff --git a/drivers/infiniband/hw/hns/hns_roce_ext.h b/drivers/infiniband/hw/hns/hns_roce_ext.h new file mode 100644 index 00000000..7d71465 --- /dev/null +++ b/drivers/infiniband/hw/hns/hns_roce_ext.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * Copyright (c) 2023 Hisilicon Limited. + */ + +#ifndef __HNS_ROCE_EXT_H +#define __HNS_ROCE_EXT_H +#include <linux/types.h> + +/** + * rdma_register_notify_addr - Register an POE channel for this RDMA device. + * @channel - POE channel index. + * @poe_addr - POE channel address. + * + * If the current POE device is not associated with CQ, then it will be + * allowed to be re-registered. Otherwise, re-registration or + * de-registration will report an EBUSY error. + */ +int rdma_register_poe_channel(struct ib_device *ib_dev, u8 channel, u64 poe_addr); +int rdma_unregister_poe_channel(struct ib_device *ib_dev, u8 channel); + +#endif diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 05edb6c..3d35d7c 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -2448,6 +2448,9 @@ static int hns_roce_query_caps(struct hns_roce_dev *hr_dev) if (!(caps->page_size_cap & PAGE_SIZE)) caps->page_size_cap = HNS_ROCE_V2_PAGE_SIZE_SUPPORTED;
+ if (hr_dev->pci_dev->revision >= PCI_REVISION_ID_HIP09) + caps->poe_ch_num = HNS_ROCE_POE_CH_NUM; + if (!hr_dev->is_vf) { caps->cqe_sz = resp_a->cqe_sz; caps->qpc_sz = le16_to_cpu(resp_b->qpc_sz); @@ -3862,6 +3865,11 @@ static void hns_roce_v2_write_cqc(struct hns_roce_dev *hr_dev, hr_reg_write(cq_context, CQC_CEQN, hr_cq->vector); hr_reg_write(cq_context, CQC_CQN, hr_cq->cqn);
+ if (hr_cq->flags & HNS_ROCE_CQ_FLAG_POE_EN) { + hr_reg_enable(cq_context, CQC_POE_EN); + hr_reg_write(cq_context, CQC_POE_NUM, hr_cq->poe_channel); + } + if (hr_cq->cqe_size == HNS_ROCE_V3_CQE_SIZE) hr_reg_write(cq_context, CQC_CQE_SIZE, CQE_SIZE_64B);
@@ -7154,6 +7162,68 @@ static int hns_roce_v2_query_scc_param(struct hns_roce_dev *hr_dev, return 0; }
+static int config_poe_addr(struct hns_roce_dev *hr_dev, + u32 channel_id, u64 addr) +{ + struct hns_roce_poe_cfg_addr_cmq *cmd; + struct hns_roce_cmq_desc desc; + int ret; + + hns_roce_cmq_setup_basic_desc(&desc, HNS_ROCE_OPC_CFG_POE_ADDR, false); + cmd = (struct hns_roce_poe_cfg_addr_cmq *)desc.data; + cmd->channel_id = cpu_to_le32(channel_id); + cmd->poe_addr_l = cpu_to_le32(lower_32_bits(addr)); + cmd->poe_addr_h = cpu_to_le32(upper_32_bits(addr)); + + ret = hns_roce_cmq_send(hr_dev, &desc, 1); + if (ret) + ibdev_err_ratelimited(&hr_dev->ib_dev, + "configure poe channel %u addr failed, ret = %d.\n", + channel_id, ret); + return ret; +} + +static int config_poe_attr(struct hns_roce_dev *hr_dev, u32 channel_id, bool en) +{ + struct hns_roce_poe_cfg_attr_cmq *cmd; + struct hns_roce_cmq_desc desc; + int ret; + + hns_roce_cmq_setup_basic_desc(&desc, HNS_ROCE_OPC_CFG_POE_ATTR, false); + cmd = (struct hns_roce_poe_cfg_attr_cmq *)desc.data; + cmd->channel_id = cpu_to_le32(channel_id); + cmd->rsv_en_outstd = en ? 1 : 0; + + ret = hns_roce_cmq_send(hr_dev, &desc, 1); + if (ret) + ibdev_err_ratelimited(&hr_dev->ib_dev, + "configure poe channel %u attr failed, ret = %d.\n", + channel_id, ret); + return ret; +} + +static int hns_roce_cfg_poe_ch(struct hns_roce_dev *hr_dev, u32 index, + u64 poe_addr) +{ + int ret; + + if (index >= hr_dev->caps.poe_ch_num) { + ibdev_err_ratelimited(&hr_dev->ib_dev, + "invalid POE channel %u.\n", index); + return -EINVAL; + } + + ret = config_poe_addr(hr_dev, index, poe_addr); + if (ret) + return ret; + + ret = config_poe_attr(hr_dev, index, !!poe_addr); + if (ret) + config_poe_addr(hr_dev, index, 0); + + return ret; +} + 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, @@ -7208,6 +7278,7 @@ static const struct hns_roce_hw hns_roce_hw_v2 = { .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, + .cfg_poe_ch = hns_roce_cfg_poe_ch, };
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 e6a9140..d3b0fa1 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h @@ -204,6 +204,8 @@ enum hns_roce_opcode_type { HNS_ROCE_OPC_QUERY_HW_VER = 0x8000, HNS_ROCE_OPC_CFG_GLOBAL_PARAM = 0x8001, HNS_ROCE_OPC_ALLOC_PF_RES = 0x8004, + HNS_ROCE_OPC_CFG_POE_ADDR = 0x801B, + HNS_ROCE_OPC_CFG_POE_ATTR = 0x801C, HNS_ROCE_OPC_QUERY_COUNTER = 0x8206, HNS_ROCE_OPC_QUERY_PF_RES = 0x8400, HNS_ROCE_OPC_ALLOC_VF_RES = 0x8401, @@ -1282,6 +1284,21 @@ struct hns_roce_cmq_req { __le32 data[6]; };
+struct hns_roce_poe_cfg_addr_cmq { + __le32 channel_id; + __le32 poe_addr_l; + __le32 poe_addr_h; + __le32 rsv[3]; +}; + +#define V2_POE_ATTR_EN V2_POE_ATTR_FIELD_LOC(40, 40) +#define V2_POE_ATTR_FIELD_LOC(h, l) FIELD_LOC(struct hns_roce_poe_cfg_attr_cmq, h, l) +struct hns_roce_poe_cfg_attr_cmq { + __le32 channel_id; + __le32 rsv_en_outstd; + __le32 rsv[4]; +}; + #define CMQ_REQ_FIELD_LOC(h, l) FIELD_LOC(struct hns_roce_cmq_req, h, l)
struct hns_roce_cmq_desc { diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index 142a562..af9d054 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -1427,6 +1427,36 @@ void hns_roce_handle_device_err(struct hns_roce_dev *hr_dev) spin_unlock_irqrestore(&hr_dev->qp_list_lock, flags); }
+static void hns_roce_register_poe_ch(struct hns_roce_dev *hr_dev) +{ + struct hns_roce_poe_ch *poe_ch; + + if (!poe_is_supported(hr_dev) || hr_dev->caps.poe_ch_num <= 0) + goto out; + + poe_ch = kvcalloc(hr_dev->caps.poe_ch_num, + sizeof(struct hns_roce_poe_ch), GFP_KERNEL); + if (!poe_ch) + goto out; + + hr_dev->poe_ctx.poe_num = hr_dev->caps.poe_ch_num; + hr_dev->poe_ctx.poe_ch = poe_ch; + return; + +out: + hr_dev->poe_ctx.poe_num = 0; + hr_dev->poe_ctx.poe_ch = NULL; + +} + +static void hns_roce_unregister_poe_ch(struct hns_roce_dev *hr_dev) +{ + if (!poe_is_supported(hr_dev) || hr_dev->caps.poe_ch_num <= 0) + return; + + kvfree(hr_dev->poe_ctx.poe_ch); +} + static int hns_roce_alloc_dfx_cnt(struct hns_roce_dev *hr_dev) { hr_dev->dfx_cnt = kcalloc(HNS_ROCE_DFX_CNT_TOTAL, sizeof(atomic64_t), @@ -1511,6 +1541,7 @@ int hns_roce_init(struct hns_roce_dev *hr_dev) if (ret) goto error_failed_register_device;
+ hns_roce_register_poe_ch(hr_dev); hns_roce_register_debugfs(hr_dev);
return 0; @@ -1548,6 +1579,7 @@ void hns_roce_exit(struct hns_roce_dev *hr_dev, bool bond_cleanup) hns_roce_unregister_sysfs(hr_dev); hns_roce_unregister_device(hr_dev, bond_cleanup); hns_roce_unregister_debugfs(hr_dev); + hns_roce_unregister_poe_ch(hr_dev);
if (hr_dev->hw->hw_exit) hr_dev->hw->hw_exit(hr_dev); diff --git a/drivers/infiniband/hw/hns/hns_roce_poe.c b/drivers/infiniband/hw/hns/hns_roce_poe.c new file mode 100644 index 00000000..4de51eb --- /dev/null +++ b/drivers/infiniband/hw/hns/hns_roce_poe.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * Copyright (c) 2023 Hisilicon Limited. All rights reserved. + */ + +#include <rdma/ib_user_verbs.h> +#include <rdma/ib_verbs.h> +#include <rdma/uverbs_types.h> +#include <rdma/uverbs_ioctl.h> +#include <rdma/ib_umem.h> +#include "hns_roce_device.h" + +static int hns_roce_config_poe_ch(struct hns_roce_dev *hr_dev, u32 index, + u64 poe_addr) +{ + int ret; + + if (!hr_dev->hw->cfg_poe_ch) { + ibdev_err_ratelimited(&hr_dev->ib_dev, + "configure POE channel has not been supported in this device.\n"); + return -EOPNOTSUPP; + } + + ret = hr_dev->hw->cfg_poe_ch(hr_dev, index, poe_addr); + if (ret) + ibdev_err_ratelimited(&hr_dev->ib_dev, + "configure POE channel %u failed, ret = %d.\n", + index, ret); + + return ret; +} + +static bool check_poe_in_use(struct hns_roce_poe_ch *poe_ch) +{ + return poe_ch->en && refcount_read(&poe_ch->ref_cnt) > 1; +} + +static void update_poe_ch(struct hns_roce_poe_ch *poe_ch, u64 poe_addr) +{ + if (poe_addr) { + if (poe_addr != poe_ch->addr) + refcount_set(&poe_ch->ref_cnt, 1); + } else { + refcount_set(&poe_ch->ref_cnt, 0); + } + poe_ch->en = !!poe_addr; + poe_ch->addr = poe_addr; +} + +int hns_roce_register_poe_channel(struct hns_roce_dev *hr_dev, u8 channel, + u64 poe_addr) +{ + struct hns_roce_poe_ch *poe_ch; + int ret; + + if (!poe_is_supported(hr_dev)) + return -EOPNOTSUPP; + + if (channel >= hr_dev->poe_ctx.poe_num || !poe_addr) + return -EINVAL; + + poe_ch = &hr_dev->poe_ctx.poe_ch[channel]; + if (check_poe_in_use(poe_ch)) + return -EBUSY; + + ret = hns_roce_config_poe_ch(hr_dev, channel, poe_addr); + if (ret) + return ret; + + update_poe_ch(poe_ch, poe_addr); + + return ret; +} + +int hns_roce_unregister_poe_channel(struct hns_roce_dev *hr_dev, u8 channel) +{ + struct hns_roce_poe_ch *poe_ch; + int ret; + + if (!poe_is_supported(hr_dev)) + return -EOPNOTSUPP; + + if (channel >= hr_dev->poe_ctx.poe_num) + return -EINVAL; + + poe_ch = &hr_dev->poe_ctx.poe_ch[channel]; + if (check_poe_in_use(poe_ch)) + return -EBUSY; + + ret = hns_roce_config_poe_ch(hr_dev, channel, 0); + if (ret) + return ret; + + update_poe_ch(poe_ch, 0); + + return ret; +} diff --git a/include/uapi/rdma/hns-abi.h b/include/uapi/rdma/hns-abi.h index 9d18a69..82eabfc 100644 --- a/include/uapi/rdma/hns-abi.h +++ b/include/uapi/rdma/hns-abi.h @@ -36,15 +36,23 @@
#include <linux/types.h>
+enum hns_roce_create_cq_create_flags { + HNS_ROCE_CREATE_CQ_FLAGS_POE_MODE = 1 << 0, +}; + struct hns_roce_ib_create_cq { __aligned_u64 buf_addr; __aligned_u64 db_addr; __u32 cqe_size; __u32 reserved; + __aligned_u64 create_flags; /* Use enum hns_roce_create_cq_create_flags */ + __u8 poe_channel; + __u8 rsv[7]; };
enum hns_roce_cq_cap_flags { HNS_ROCE_CQ_FLAG_RECORD_DB = 1 << 0, + HNS_ROCE_CQ_FLAG_POE_EN = 1 << 2, };
struct hns_roce_ib_create_cq_resp {