driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IB30V8
----------------------------------------------------------------------
RAS may occur if the DCA buffer is not attached and the DB is knocked out.
This patch adds a safe page for DCA, which will be attached to QP if no DCA buffer is attached to avoid the HW accessing illegal addresses.
Fixes: 10bb3b802412 ("RDMA/hns: Add method for attaching WQE buffer") Signed-off-by: Chengchang Tang tangchengchang@huawei.com Signed-off-by: Xinghai Cen cenxinghai@h-partners.com --- drivers/infiniband/hw/hns/hns_roce_dca.c | 56 ++++++++++++++++++++- drivers/infiniband/hw/hns/hns_roce_dca.h | 2 + drivers/infiniband/hw/hns/hns_roce_device.h | 3 ++ drivers/infiniband/hw/hns/hns_roce_main.c | 13 +++++ drivers/infiniband/hw/hns/hns_roce_qp.c | 23 +++++++++ 5 files changed, 95 insertions(+), 2 deletions(-)
diff --git a/drivers/infiniband/hw/hns/hns_roce_dca.c b/drivers/infiniband/hw/hns/hns_roce_dca.c index 4cef41591795..f435f6f5d8a3 100644 --- a/drivers/infiniband/hw/hns/hns_roce_dca.c +++ b/drivers/infiniband/hw/hns/hns_roce_dca.c @@ -309,6 +309,33 @@ hr_qp_to_dca_ctx(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp) return to_hr_dca_ctx(hr_dev, uctx); }
+int hns_roce_map_dca_safe_page(struct hns_roce_dev *hr_dev, + struct hns_roce_qp *hr_qp) +{ + unsigned int page_count = hr_qp->dca_cfg.npages; + struct ib_device *ibdev = &hr_dev->ib_dev; + dma_addr_t *pages; + unsigned int i; + int ret; + + pages = kvcalloc(page_count, sizeof(dma_addr_t), GFP_KERNEL); + if (IS_ERR_OR_NULL(pages)) { + ibdev_err(ibdev, "failed to alloc DCA safe page array.\n"); + return -ENOMEM; + } + + for (i = 0; i < page_count; i++) + pages[i] = hr_dev->dca_safe_page; + + ret = hns_roce_mtr_map(hr_dev, &hr_qp->mtr, pages, page_count); + if (ret) + ibdev_err(ibdev, "failed to map safe page for DCA, ret = %d.\n", + ret); + + kvfree(pages); + return ret; +} + static int config_dca_qpc(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp, dma_addr_t *pages, int page_count) @@ -335,6 +362,29 @@ static int config_dca_qpc(struct hns_roce_dev *hr_dev, return 0; }
+static int config_dca_qpc_to_safe_page(struct hns_roce_dev *hr_dev, + struct hns_roce_qp *hr_qp) +{ + unsigned int page_count = hr_qp->dca_cfg.npages; + dma_addr_t *pages; + unsigned int i; + int ret; + + might_sleep(); + + pages = kvcalloc(page_count, sizeof(dma_addr_t), GFP_KERNEL); + if (IS_ERR_OR_NULL(pages)) + return -ENOMEM; + + for (i = 0; i < page_count; i++) + pages[i] = hr_dev->dca_safe_page; + + ret = config_dca_qpc(hr_dev, hr_qp, pages, page_count); + + kvfree(pages); + return ret; +} + static int setup_dca_buf_to_hw(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp, struct hns_roce_dca_ctx *ctx, u32 buf_id, @@ -980,8 +1030,10 @@ static void process_aging_dca_mem(struct hns_roce_dev *hr_dev, spin_unlock(&ctx->aging_lock);
if (start_free_dca_buf(ctx, cfg->dcan)) { - if (hr_dev->hw->chk_dca_buf_inactive(hr_dev, hr_qp)) - free_buf_from_dca_mem(ctx, cfg); + if (hr_dev->hw->chk_dca_buf_inactive(hr_dev, hr_qp)) { + if (!config_dca_qpc_to_safe_page(hr_dev, hr_qp)) + free_buf_from_dca_mem(ctx, cfg); + }
stop_free_dca_buf(ctx, cfg->dcan); } diff --git a/drivers/infiniband/hw/hns/hns_roce_dca.h b/drivers/infiniband/hw/hns/hns_roce_dca.h index 7733887ce5e1..36f03f5357d7 100644 --- a/drivers/infiniband/hw/hns/hns_roce_dca.h +++ b/drivers/infiniband/hw/hns/hns_roce_dca.h @@ -75,4 +75,6 @@ void hns_roce_modify_dca(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp,
void hns_roce_enum_dca_pool(struct hns_roce_dca_ctx *dca_ctx, void *param, hns_dca_enum_callback cb); +int hns_roce_map_dca_safe_page(struct hns_roce_dev *hr_dev, + struct hns_roce_qp *hr_qp); #endif diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h index f434bf349887..e2dfab08cc7a 100644 --- a/drivers/infiniband/hw/hns/hns_roce_device.h +++ b/drivers/infiniband/hw/hns/hns_roce_device.h @@ -1156,6 +1156,9 @@ struct hns_roce_dev { struct mutex mtr_unfree_list_mutex; /* protect mtr_unfree_list */ struct list_head umem_unfree_list; /* list of unfree umem on this dev */ struct mutex umem_unfree_list_mutex; /* protect umem_unfree_list */ + + void *dca_safe_buf; + dma_addr_t dca_safe_page; };
static inline struct hns_roce_dev *to_hr_dev(struct ib_device *ib_dev) diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index 520112bd43d5..9c6651fc87c7 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -1368,6 +1368,17 @@ static void hns_roce_dealloc_dfx_cnt(struct hns_roce_dev *hr_dev) kvfree(hr_dev->dfx_cnt); }
+static void hns_roce_free_dca_safe_buf(struct hns_roce_dev *hr_dev) +{ + if (!hr_dev->dca_safe_buf) + return; + + dma_free_coherent(hr_dev->dev, PAGE_SIZE, hr_dev->dca_safe_buf, + hr_dev->dca_safe_page); + hr_dev->dca_safe_page = 0; + hr_dev->dca_safe_buf = NULL; +} + int hns_roce_init(struct hns_roce_dev *hr_dev) { struct device *dev = hr_dev->dev; @@ -1481,6 +1492,8 @@ void hns_roce_exit(struct hns_roce_dev *hr_dev, bool bond_cleanup) hns_roce_dealloc_scc_param(hr_dev); hns_roce_unregister_debugfs(hr_dev);
+ hns_roce_free_dca_safe_buf(hr_dev); + if (hr_dev->hw->hw_exit) hr_dev->hw->hw_exit(hr_dev); hns_roce_free_unfree_umem(hr_dev); diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c index 77ec0c8678d3..94124a2dd00b 100644 --- a/drivers/infiniband/hw/hns/hns_roce_qp.c +++ b/drivers/infiniband/hw/hns/hns_roce_qp.c @@ -844,6 +844,8 @@ static int alloc_wqe_buf(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp, hns_roce_disable_dca(hr_dev, hr_qp, udata); kvfree(hr_qp->mtr_node); hr_qp->mtr_node = NULL; + } else if (dca_en) { + ret = hns_roce_map_dca_safe_page(hr_dev, hr_qp); }
return ret; @@ -864,6 +866,21 @@ static void free_wqe_buf(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp, hns_roce_disable_dca(hr_dev, hr_qp, udata); }
+static int alloc_dca_safe_page(struct hns_roce_dev *hr_dev) +{ + struct ib_device *ibdev = &hr_dev->ib_dev; + + hr_dev->dca_safe_buf = dma_alloc_coherent(hr_dev->dev, PAGE_SIZE, + &hr_dev->dca_safe_page, + GFP_KERNEL); + if (!hr_dev->dca_safe_buf) { + ibdev_err(ibdev, "failed to alloc dca safe page.\n"); + return -ENOMEM; + } + + return 0; +} + static int alloc_qp_wqe(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp, struct ib_qp_init_attr *init_attr, struct ib_udata *udata, @@ -882,6 +899,12 @@ static int alloc_qp_wqe(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp,
dca_en = check_dca_is_enable(hr_dev, hr_qp, init_attr, !!udata, ucmd->buf_addr); + if (dca_en && !hr_dev->dca_safe_buf) { + ret = alloc_dca_safe_page(hr_dev); + if (ret) + return ret; + } + ret = set_wqe_buf_attr(hr_dev, hr_qp, dca_en, page_shift, &buf_attr); if (ret) { ibdev_err(ibdev, "failed to split WQE buf, ret = %d.\n", ret);