Commit 36f678e7 authored by Chengchang Tang's avatar Chengchang Tang
Browse files

RDMA/hns: Fix possible RAS when DCA is not attached

driver inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IAMO4D



----------------------------------------------------------------------

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: d8cca476 ("RDMA/hns: Add method for attaching WQE buffer")
Signed-off-by: default avatarChengchang Tang <tangchengchang@huawei.com>
Signed-off-by: default avatarXinghai Cen <cenxinghai@h-partners.com>
parent d9b104fa
Loading
Loading
Loading
Loading
+54 −2
Original line number Diff line number Diff line
@@ -306,6 +306,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 IS_ERR(pages) ? PTR_ERR(pages) : -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)
@@ -332,6 +359,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,
@@ -977,8 +1027,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))
			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);
		}
+2 −0
Original line number Diff line number Diff line
@@ -74,4 +74,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
+3 −0
Original line number Diff line number Diff line
@@ -1234,6 +1234,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)
+12 −0
Original line number Diff line number Diff line
@@ -1489,6 +1489,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;
@@ -1599,6 +1610,7 @@ void hns_roce_exit(struct hns_roce_dev *hr_dev, bool bond_cleanup)
	hns_roce_unregister_device(hr_dev, bond_cleanup);
	hns_roce_unregister_debugfs(hr_dev);
	hns_roce_unregister_poe_ch(hr_dev);
	hns_roce_free_dca_safe_buf(hr_dev);

	if (hr_dev->hw->hw_exit)
		hr_dev->hw->hw_exit(hr_dev);
+23 −0
Original line number Diff line number Diff line
@@ -825,6 +825,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;
@@ -845,6 +847,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,
@@ -862,6 +879,12 @@ static int alloc_qp_wqe(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp,
		page_shift = ucmd->pageshift;

	dca_en = check_dca_is_enable(hr_dev, 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);