Commit e519f15b 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/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: 10bb3b80 ("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 c66cd73a
Loading
Loading
Loading
Loading
+54 −2
Original line number Diff line number Diff line
@@ -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))
			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
@@ -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
+3 −0
Original line number Diff line number Diff line
@@ -1163,6 +1163,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)
+13 −0
Original line number Diff line number Diff line
@@ -1370,6 +1370,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;
@@ -1483,6 +1494,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);
+23 −0
Original line number Diff line number Diff line
@@ -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);