Commit ba75bc59 authored by Weibo Zhao's avatar Weibo Zhao Committed by JiangShui
Browse files

hns3 udma: add feature of hardware reset

driver inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I85R2F


CVE: NA

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

This patch add feature of hardware reset. In some extreme
scenarios, the hardware needs to be reset to try to fix.
Driver should exit and reinit after hardware reset.

Signed-off-by: default avatarWeibo Zhao <zhaoweibo3@huawei.com>
parent 4888db6e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
enum {
	UDMA_MMAP_UAR_PAGE,
	UDMA_MMAP_DWQE_PAGE,
	UDMA_MMAP_RESET_PAGE,
};

enum udma_jfc_init_attr_mask {
+6 −0
Original line number Diff line number Diff line
@@ -591,6 +591,12 @@ static int udma_cmd_mbox_wait(struct udma_dev *dev, struct udma_cmq_desc *desc,
int udma_cmd_mbox(struct udma_dev *dev, struct udma_cmq_desc *desc,
		  uint32_t timeout, int vfid)
{
	bool is_busy;

	if (dev->hw->chk_mbox_avail)
		if (!dev->hw->chk_mbox_avail(dev, &is_busy))
			return is_busy ? -EBUSY : 0;

	if (dev->cmd.use_events)
		return udma_cmd_mbox_wait(dev, desc, timeout, vfid);
	else
+13 −0
Original line number Diff line number Diff line
@@ -168,6 +168,12 @@ enum udma_instance_state {
	UDMA_STATE_UNINIT,
};

#define UDMA_IS_RESETTING	1

enum {
	UDMA_RST_DIRECT_RETURN = 0,
};

enum udma_event {
	UDMA_EVENT_TYPE_COMM_EST		= 0x03,
	UDMA_EVENT_TYPE_WQ_CATAS_ERROR		= 0x05,
@@ -454,6 +460,10 @@ enum udma_device_state {
	UDMA_DEVICE_STATE_UNINIT,
};

struct udma_reset_state {
	uint32_t reset_state; /* stored to use in user space */
};

struct udma_cmq {
	struct udma_cmq_ring	csq;
	uint16_t tx_timeout;
@@ -491,6 +501,7 @@ struct udma_hw {
			 uint16_t token, int event);
	int (*poll_mbox_done)(struct udma_dev *udma_dev,
			      uint32_t timeout);
	bool (*chk_mbox_avail)(struct udma_dev *udma_dev, bool *is_busy);
	int (*set_hem)(struct udma_dev *udma_dev,
		       struct udma_hem_table *table, int obj, int step_idx);
	int (*clear_hem)(struct udma_dev *udma_dev,
@@ -754,6 +765,8 @@ struct udma_dev {
	uint64_t			sys_image_guid;
	struct udma_cmdq		cmd;
	int				cmd_mod;
	struct page			*reset_page; /* store reset state */
	void				*reset_kaddr; /* addr of reset page */
	const struct udma_hw		*hw;
	void				*priv;
	struct workqueue_struct		*irq_workq;
+258 −1
Original line number Diff line number Diff line
@@ -1110,27 +1110,136 @@ static int udma_hw_set_eid(struct udma_dev *udma_dev, union ubcore_eid eid)
	return ret;
}

static void func_clr_hw_resetting_state(struct udma_dev *udma_dev,
					struct hnae3_handle *handle)
{
	const struct hnae3_ae_ops *ops = handle->ae_algo->ops;
	uint64_t end;

	udma_dev->dis_db = true;

	dev_warn(udma_dev->dev,
		 "Func clear is pending, device in resetting state.\n");
	end = UDMA_HW_RST_TIMEOUT;
	while (end) {
		if (!ops->get_hw_reset_stat(handle)) {
			udma_dev->is_reset = true;
			dev_info(udma_dev->dev,
				 "Func clear success after reset.\n");
			return;
		}
		msleep(UDMA_HW_RST_COMPLETION_WAIT);
		end -= UDMA_HW_RST_COMPLETION_WAIT;
	}

	dev_warn(udma_dev->dev, "Func clear failed.\n");
}

static void func_clr_sw_resetting_state(struct udma_dev *udma_dev,
					struct hnae3_handle *handle)
{
	const struct hnae3_ae_ops *ops = handle->ae_algo->ops;
	uint64_t end;

	udma_dev->dis_db = true;

	dev_warn(udma_dev->dev,
		 "Func clear is pending, device in resetting state.\n");
	end = UDMA_HW_RST_TIMEOUT;
	while (end) {
		if (ops->ae_dev_reset_cnt(handle) !=
		    udma_dev->reset_cnt) {
			udma_dev->is_reset = true;
			dev_info(udma_dev->dev,
				 "Func clear success after sw reset\n");
			return;
		}
		msleep(UDMA_HW_RST_COMPLETION_WAIT);
		end -= UDMA_HW_RST_COMPLETION_WAIT;
	}

	dev_warn(udma_dev->dev,
		 "Func clear failed because of unfinished sw reset\n");
}

static void udma_func_clr_rst_proc(struct udma_dev *udma_dev, int retval,
				   int flag)
{
	struct udma_priv *priv = (struct udma_priv *)udma_dev->priv;
	struct hnae3_handle *handle = priv->handle;
	const struct hnae3_ae_ops *ops = handle->ae_algo->ops;

	if (ops->ae_dev_reset_cnt(handle) != udma_dev->reset_cnt) {
		udma_dev->dis_db = true;
		udma_dev->is_reset = true;
		dev_info(udma_dev->dev, "Func clear success after reset.\n");
		return;
	}

	if (ops->get_hw_reset_stat(handle)) {
		func_clr_hw_resetting_state(udma_dev, handle);
		return;
	}

	if (ops->ae_dev_resetting(handle) &&
	    handle->udmainfo.instance_state == UDMA_STATE_INIT) {
		func_clr_sw_resetting_state(udma_dev, handle);
		return;
	}

	if (retval && !flag)
		dev_warn(udma_dev->dev,
			 "Func clear read failed, ret = %d.\n", retval);

	dev_warn(udma_dev->dev, "Func clear failed.\n");
}

static bool check_device_is_in_reset(struct udma_dev *udma_dev)
{
	struct udma_priv *priv = (struct udma_priv *)udma_dev->priv;
	struct hnae3_handle *handle = priv->handle;
	const struct hnae3_ae_ops *ops = handle->ae_algo->ops;

	if (udma_dev->reset_cnt != ops->ae_dev_reset_cnt(handle))
		return true;

	if (ops->get_hw_reset_stat(handle))
		return true;

	if (ops->ae_dev_resetting(handle))
		return true;

	return false;
}

static void __udma_function_clear(struct udma_dev *udma_dev, int vf_id)
{
	bool fclr_write_fail_flag = false;
	struct udma_func_clear *resp;
	struct udma_cmq_desc desc;
	uint64_t end;
	int ret = 0;

	if (check_device_is_in_reset(udma_dev))
		goto out;

	udma_cmq_setup_basic_desc(&desc, UDMA_OPC_FUNC_CLEAR, false);
	resp = (struct udma_func_clear *)desc.data;
	resp->rst_funcid_en = cpu_to_le32(vf_id);

	ret = udma_cmq_send(udma_dev, &desc, 1);
	if (ret) {
		fclr_write_fail_flag = true;
		dev_err(udma_dev->dev, "Func clear write failed, ret = %d.\n",
			ret);
		return;
		goto out;
	}

	msleep(UDMA_READ_FUNC_CLEAR_FLAG_INTERVAL);
	end = UDMA_FUNC_CLEAR_TIMEOUT_MSECS;
	while (end) {
		if (check_device_is_in_reset(udma_dev))
			goto out;
		msleep(UDMA_READ_FUNC_CLEAR_FLAG_FAIL_WAIT);
		end -= UDMA_READ_FUNC_CLEAR_FLAG_FAIL_WAIT;

@@ -1148,6 +1257,9 @@ static void __udma_function_clear(struct udma_dev *udma_dev, int vf_id)
			return;
		}
	}

out:
	udma_func_clr_rst_proc(udma_dev, ret, fclr_write_fail_flag);
}

static void udma_free_vf_resource(struct udma_dev *udma_dev, int vf_id)
@@ -1311,6 +1423,31 @@ static void udma_free_link_table(struct udma_dev *udma_dev)
	free_link_table_buf(udma_dev, &priv->ext_llm);
}

static int udma_get_reset_page(struct udma_dev *dev)
{
	dev->reset_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
	if (!dev->reset_page)
		return -ENOMEM;

	dev->reset_kaddr = vmap(&dev->reset_page, 1, VM_MAP, PAGE_KERNEL);
	if (!dev->reset_kaddr)
		goto err_vmap;

	return 0;

err_vmap:
	put_page(dev->reset_page);
	return -ENOMEM;
}

static void udma_put_reset_page(struct udma_dev *dev)
{
	vunmap(dev->reset_kaddr);
	dev->reset_kaddr = NULL;
	put_page(dev->reset_page);
	dev->reset_page = NULL;
}

static int udma_clear_extdb_list_info(struct udma_dev *udma_dev)
{
	struct udma_cmq_desc desc;
@@ -1401,6 +1538,13 @@ static int udma_hw_init(struct udma_dev *udma_dev)
{
	int ret;

	ret = udma_get_reset_page(udma_dev);
	if (ret) {
		dev_err(udma_dev->dev,
			"get reset page failed, ret = %d.\n", ret);
		return ret;
	}

	/* UDMA requires the extdb info to be cleared before using */
	ret = udma_clear_extdb_list_info(udma_dev);
	if (ret)
@@ -1421,6 +1565,7 @@ static int udma_hw_init(struct udma_dev *udma_dev)
err_llm_init_failed:
	put_hem_table(udma_dev);
err_clear_extdb_failed:
	udma_put_reset_page(udma_dev);

	return ret;
}
@@ -1627,6 +1772,7 @@ static const struct udma_hw udma_hw = {
	.hw_exit = udma_hw_exit,
	.post_mbox = udma_post_mbox,
	.poll_mbox_done = udma_poll_mbox_done,
	.chk_mbox_avail = udma_chk_mbox_is_avail,
	.set_hem = udma_set_hem,
	.clear_hem = udma_clear_hem,
	.set_eid = udma_hw_set_eid,
@@ -1729,6 +1875,7 @@ static void __udma_uninit_instance(struct hnae3_handle *handle,

static int udma_init_instance(struct hnae3_handle *handle)
{
	const struct hnae3_ae_ops *ops = handle->ae_algo->ops;
	struct device *dev = &handle->pdev->dev;
	const struct pci_device_id *id;
	int ret;
@@ -1746,11 +1893,21 @@ static int udma_init_instance(struct hnae3_handle *handle)
			return ret;

		dev_err(dev, "UDMA instance init failed! ret = %d\n", ret);
		if (ops->ae_dev_resetting(handle) ||
		    ops->get_hw_reset_stat(handle))
			goto reset_chk_err;
		else
			return ret;
	}

	handle->udmainfo.instance_state = UDMA_STATE_INITED;

	return 0;

reset_chk_err:
	dev_err(dev, "Device is being reset, please retry later.\n");

	return -EBUSY;
}

static void udma_uninit_instance(struct hnae3_handle *handle, bool reset)
@@ -1765,6 +1922,105 @@ static void udma_uninit_instance(struct hnae3_handle *handle, bool reset)
	handle->udmainfo.instance_state = UDMA_STATE_NON_INIT;
}

static void udma_reset_notify_user(struct udma_dev *dev)
{
	struct udma_reset_state *state;

	state = (struct udma_reset_state *)dev->reset_kaddr;

	state->reset_state = UDMA_IS_RESETTING;
	/* Ensure reset state was flushed in memory */
	wmb();
}

static int udma_reset_notify_down(struct hnae3_handle *handle)
{
	struct udma_dev *dev;

	if (handle->udmainfo.instance_state != UDMA_STATE_INITED) {
		set_bit(UDMA_RST_DIRECT_RETURN, &handle->udmainfo.state);
		return 0;
	}

	handle->udmainfo.reset_state = UDMA_STATE_RST_DOWN;
	clear_bit(UDMA_RST_DIRECT_RETURN, &handle->udmainfo.state);

	dev = handle->priv;
	if (!dev)
		return 0;

	dev->dis_db = true;

	udma_reset_notify_user(dev);

	return 0;
}

static int udma_reset_notify_init(struct hnae3_handle *handle)
{
	struct device *dev = &handle->pdev->dev;
	int ret;

	if (test_and_clear_bit(UDMA_RST_DIRECT_RETURN,
			       &handle->udmainfo.state)) {
		handle->udmainfo.reset_state = UDMA_STATE_RST_INITED;
		return 0;
	}

	handle->udmainfo.reset_state = UDMA_STATE_RST_INIT;

	dev_info(&handle->pdev->dev, "In reset process UDMA client reinit.\n");
	ret = __udma_init_instance(handle);
	if (ret) {
		/* when reset notify type is HNAE3_INIT_CLIENT In reset notify
		 * callback function, UB Engine reinitialize. If UDMA reinit
		 * failed, we should inform NIC driver.
		 */
		handle->priv = NULL;
		dev_err(dev, "In reset process UDMA reinit failed %d.\n", ret);
	} else {
		handle->udmainfo.reset_state = UDMA_STATE_RST_INITED;
		dev_info(dev, "Reset done, UDMA client reinit finished.\n");
	}

	return ret;
}

static int udma_reset_notify_uninit(struct hnae3_handle *handle)
{
	if (test_bit(UDMA_RST_DIRECT_RETURN, &handle->udmainfo.state))
		return 0;

	handle->udmainfo.reset_state = UDMA_STATE_RST_UNINIT;
	dev_info(&handle->pdev->dev, "In reset process UDMA client uninit.\n");
	msleep(UDMA_HW_RST_UNINT_DELAY);
	__udma_uninit_instance(handle, false);

	return 0;
}

static int udma_reset_notify(struct hnae3_handle *handle,
			     enum hnae3_reset_notify_type type)
{
	int ret = 0;

	switch (type) {
	case HNAE3_DOWN_CLIENT:
		ret = udma_reset_notify_down(handle);
		break;
	case HNAE3_INIT_CLIENT:
		ret = udma_reset_notify_init(handle);
		break;
	case HNAE3_UNINIT_CLIENT:
		ret = udma_reset_notify_uninit(handle);
		break;
	default:
		break;
	}

	return ret;
}

static void udma_link_status_change(struct hnae3_handle *handle, bool linkup)
{
	struct net_device *net_dev;
@@ -1809,6 +2065,7 @@ static const struct hnae3_client_ops udma_ops = {
	.init_instance = udma_init_instance,
	.uninit_instance = udma_uninit_instance,
	.link_status_change = udma_link_status_change,
	.reset_notify = udma_reset_notify,
};

static struct hnae3_client udma_client = {
+5 −0
Original line number Diff line number Diff line
@@ -115,6 +115,11 @@ struct udma_query_oor_cmq {
#define UDMA_READ_FUNC_CLEAR_FLAG_INTERVAL	40
#define UDMA_READ_FUNC_CLEAR_FLAG_FAIL_WAIT	20

#define UDMA_HW_RST_TIMEOUT		1000
#define UDMA_HW_RST_UNINT_DELAY		100

#define UDMA_HW_RST_COMPLETION_WAIT	20

#define UDMA_FUNC_IRQ_RSV 2
#define UDMA_1US_CFG 999
#define UDMA_EXT_LLM_ENTRY_SZ		8
Loading