Commit f3cce1c3 authored by Weili Qian's avatar Weili Qian Committed by Zheng Zengkai
Browse files

crypto: hisilicon/qm - update reset flow

mainline inclusion
from mainline-master
commit 760fe22c
category: feature
bugzilla: 173981
CVE: NA

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=760fe22cf5e9f5d0212aa4c9aef555625c167627



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

This patch updates the reset flow based on PF/VF communications. VFs
will be stopped after receiving reset message from PF, and wait for
reset finish to restart VFs.

Signed-off-by: default avatarWeili Qian <qianweili@huawei.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarMingqiang Ling <lingmingqiang@huawei.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
parent 4717f5b6
Loading
Loading
Loading
Loading
+262 −17
Original line number Diff line number Diff line
@@ -201,7 +201,10 @@
#define QM_WAIT_DST_ACK			10
#define QM_MAX_PF_WAIT_COUNT		10
#define QM_MAX_VF_WAIT_COUNT		40

#define QM_VF_RESET_WAIT_US            20000
#define QM_VF_RESET_WAIT_CNT           3000
#define QM_VF_RESET_WAIT_TIMEOUT_US    \
	(QM_VF_RESET_WAIT_US * QM_VF_RESET_WAIT_CNT)

#define QM_DFX_MB_CNT_VF		0x104010
#define QM_DFX_DB_CNT_VF		0x104020
@@ -285,6 +288,16 @@ enum acc_err_result {
	ACC_ERR_RECOVERED,
};

enum qm_mb_cmd {
	QM_PF_FLR_PREPARE = 0x01,
	QM_PF_SRST_PREPARE,
	QM_PF_RESET_DONE,
	QM_VF_PREPARE_DONE,
	QM_VF_PREPARE_FAIL,
	QM_VF_START_DONE,
	QM_VF_START_FAIL,
};

struct qm_cqe {
	__le32 rsvd0;
	__le16 cmd_id;
@@ -1890,9 +1903,74 @@ static void qm_clear_cmd_interrupt(struct hisi_qm *qm, u64 vf_mask)
	writel(val, qm->io_base + QM_IFC_INT_SOURCE_V);
}

static void qm_handle_vf_msg(struct hisi_qm *qm, u32 vf_id)
{
	struct device *dev = &qm->pdev->dev;
	u32 cmd;
	u64 msg;
	int ret;

	ret = qm_get_mb_cmd(qm, &msg, vf_id);
	if (ret) {
		dev_err(dev, "failed to get msg from VF(%u)!\n", vf_id);
		return;
	}

	cmd = msg & QM_MB_CMD_DATA_MASK;
	switch (cmd) {
	case QM_VF_PREPARE_FAIL:
		dev_err(dev, "failed to stop VF(%u)!\n", vf_id);
		break;
	case QM_VF_START_FAIL:
		dev_err(dev, "failed to start VF(%u)!\n", vf_id);
		break;
	case QM_VF_PREPARE_DONE:
	case QM_VF_START_DONE:
		break;
	default:
		dev_err(dev, "unsupported cmd %u sent by VF(%u)!\n", cmd, vf_id);
		break;
	}
}

static int qm_wait_vf_prepare_finish(struct hisi_qm *qm)
{
	struct device *dev = &qm->pdev->dev;
	u32 vfs_num = qm->vfs_num;
	int cnt = 0;
	int ret = 0;
	u64 val;
	u32 i;

	if (!qm->vfs_num || qm->ver < QM_HW_V3)
		return 0;

	while (true) {
		val = readq(qm->io_base + QM_IFC_INT_SOURCE_P);
		/* All VFs send command to PF, break */
		if ((val & GENMASK(vfs_num, 1)) == GENMASK(vfs_num, 1))
			break;

		if (++cnt > QM_MAX_PF_WAIT_COUNT) {
			ret = -EBUSY;
			break;
		}

		msleep(QM_WAIT_DST_ACK);
	}

	/* PF check VFs msg */
	for (i = 1; i <= vfs_num; i++) {
		if (val & BIT(i))
			qm_handle_vf_msg(qm, i);
		else
			dev_err(dev, "VF(%u) not ping PF!\n", i);
	}

	/* PF clear interrupt to ack VFs */
	qm_clear_cmd_interrupt(qm, val);

	return ret;
}

static void qm_trigger_vf_interrupt(struct hisi_qm *qm, u32 fun_num)
@@ -4038,7 +4116,8 @@ static int qm_vf_reset_prepare(struct hisi_qm *qm,
	return ret;
}

static int qm_try_stop_vfs(struct hisi_qm *qm, enum qm_stop_reason stop_reason)
static int qm_try_stop_vfs(struct hisi_qm *qm, u64 cmd,
			   enum qm_stop_reason stop_reason)
{
	struct pci_dev *pdev = qm->pdev;
	int ret;
@@ -4046,9 +4125,16 @@ static int qm_try_stop_vfs(struct hisi_qm *qm, enum qm_stop_reason stop_reason)
	if (!qm->vfs_num)
		return 0;

	/* Kunpeng930 supports to notify VFs to stop before PF reset */
	if (qm->ops->ping_all_vfs) {
		ret = qm->ops->ping_all_vfs(qm, cmd);
		if (ret)
			pci_err(pdev, "failed to send cmd to all VFs before PF reset!\n");
	} else {
		ret = qm_vf_reset_prepare(qm, stop_reason);
		if (ret)
			pci_err(pdev, "failed to prepare reset, ret = %d.\n", ret);
	}

	return ret;
}
@@ -4072,7 +4158,14 @@ static int qm_reset_prepare_ready(struct hisi_qm *qm)
	struct pci_dev *pdev = qm->pdev;
	struct hisi_qm *pf_qm = pci_get_drvdata(pci_physfn(pdev));

	/*
	 * PF and VF on host doesnot support resetting at the
	 * same time on Kunpeng920.
	 */
	if (qm->ver < QM_HW_V3)
		return qm_wait_reset_finish(pf_qm);

	return qm_wait_reset_finish(qm);
}

static void qm_reset_bit_clear(struct hisi_qm *qm)
@@ -4080,7 +4173,10 @@ static void qm_reset_bit_clear(struct hisi_qm *qm)
	struct pci_dev *pdev = qm->pdev;
	struct hisi_qm *pf_qm = pci_get_drvdata(pci_physfn(pdev));

	if (qm->ver < QM_HW_V3)
		clear_bit(QM_RESETTING, &pf_qm->misc_ctl);

	clear_bit(QM_RESETTING, &qm->misc_ctl);
}

static int qm_controller_reset_prepare(struct hisi_qm *qm)
@@ -4094,7 +4190,11 @@ static int qm_controller_reset_prepare(struct hisi_qm *qm)
		return ret;
	}

	ret = qm_try_stop_vfs(qm, QM_SOFT_RESET);
	/* PF obtains the information of VF by querying the register. */
	qm_cmd_uninit(qm);

	/* Whether VFs stop successfully, soft reset will continue. */
	ret = qm_try_stop_vfs(qm, QM_PF_SRST_PREPARE, QM_SOFT_RESET);
	if (ret)
		pci_err(pdev, "failed to stop vfs by pf in soft reset.\n");

@@ -4243,7 +4343,7 @@ static int qm_vf_reset_done(struct hisi_qm *qm)
	return ret;
}

static int qm_try_start_vfs(struct hisi_qm *qm)
static int qm_try_start_vfs(struct hisi_qm *qm, enum qm_mb_cmd cmd)
{
	struct pci_dev *pdev = qm->pdev;
	int ret;
@@ -4257,9 +4357,16 @@ static int qm_try_start_vfs(struct hisi_qm *qm)
		return ret;
	}

	/* Kunpeng930 supports to notify VFs to start after PF reset. */
	if (qm->ops->ping_all_vfs) {
		ret = qm->ops->ping_all_vfs(qm, cmd);
		if (ret)
			pci_warn(pdev, "failed to send cmd to all VFs after PF reset!\n");
	} else {
		ret = qm_vf_reset_done(qm);
		if (ret)
			pci_warn(pdev, "failed to start vfs, ret = %d.\n", ret);
	}

	return ret;
}
@@ -4363,7 +4470,7 @@ static int qm_controller_reset_done(struct hisi_qm *qm)
		return ret;
	}

	ret = qm_try_start_vfs(qm);
	ret = qm_try_start_vfs(qm, QM_PF_RESET_DONE);
	if (ret)
		pci_err(pdev, "failed to start vfs by pf in soft reset.\n");

@@ -4371,6 +4478,7 @@ static int qm_controller_reset_done(struct hisi_qm *qm)
	if (ret)
		pci_err(pdev, "failed to start by vfs in soft reset!\n");

	qm_cmd_init(qm);
	qm_restart_done(qm);

	qm_reset_bit_clear(qm);
@@ -4462,7 +4570,11 @@ void hisi_qm_reset_prepare(struct pci_dev *pdev)
		return;
	}

	ret = qm_try_stop_vfs(qm, QM_SOFT_RESET);
	/* PF obtains the information of VF by querying the register. */
	if (qm->fun_type == QM_HW_PF)
		qm_cmd_uninit(qm);

	ret = qm_try_stop_vfs(qm, QM_PF_FLR_PREPARE, QM_FLR);
	if (ret)
		pci_err(pdev, "failed to stop vfs by pf in FLR.\n");

@@ -4517,7 +4629,7 @@ void hisi_qm_reset_done(struct pci_dev *pdev)
		goto flr_done;
	}

	ret = qm_try_start_vfs(qm);
	ret = qm_try_start_vfs(qm, QM_PF_RESET_DONE);
	if (ret)
		pci_err(pdev, "failed to start vfs by pf in FLR.\n");

@@ -4526,6 +4638,9 @@ void hisi_qm_reset_done(struct pci_dev *pdev)
		pci_err(pdev, "failed to start by vfs in FLR!\n");

flr_done:
	if (qm->fun_type == QM_HW_PF)
		qm_cmd_init(qm);

	if (qm_flr_reset_complete(pdev))
		pci_info(pdev, "FLR reset complete\n");

@@ -4621,12 +4736,128 @@ static void hisi_qm_controller_reset(struct work_struct *rst_work)

}

static void qm_pf_reset_vf_prepare(struct hisi_qm *qm,
				   enum qm_stop_reason stop_reason)
{
	enum qm_mb_cmd cmd = QM_VF_PREPARE_DONE;
	struct pci_dev *pdev = qm->pdev;
	int ret;

	ret = qm_reset_prepare_ready(qm);
	if (ret) {
		dev_err(&pdev->dev, "reset prepare not ready!\n");
		atomic_set(&qm->status.flags, QM_STOP);
		cmd = QM_VF_PREPARE_FAIL;
		goto err_prepare;
	}

	ret = hisi_qm_stop(qm, stop_reason);
	if (ret) {
		dev_err(&pdev->dev, "failed to stop QM, ret = %d.\n", ret);
		atomic_set(&qm->status.flags, QM_STOP);
		cmd = QM_VF_PREPARE_FAIL;
		goto err_prepare;
	}

err_prepare:
	pci_save_state(pdev);
	ret = qm->ops->ping_pf(qm, cmd);
	if (ret)
		dev_warn(&pdev->dev, "PF responds timeout in reset prepare!\n");
}

static void qm_pf_reset_vf_done(struct hisi_qm *qm)
{
	enum qm_mb_cmd cmd = QM_VF_START_DONE;
	struct pci_dev *pdev = qm->pdev;
	int ret;

	pci_restore_state(pdev);
	ret = hisi_qm_start(qm);
	if (ret) {
		dev_err(&pdev->dev, "failed to start QM, ret = %d.\n", ret);
		cmd = QM_VF_START_FAIL;
	}

	ret = qm->ops->ping_pf(qm, cmd);
	if (ret)
		dev_warn(&pdev->dev, "PF responds timeout in reset done!\n");

	qm_reset_bit_clear(qm);
}

static int qm_wait_pf_reset_finish(struct hisi_qm *qm)
{
	struct device *dev = &qm->pdev->dev;
	u32 val, cmd;
	u64 msg;
	int ret;

	/* Wait for reset to finish */
	ret = readl_relaxed_poll_timeout(qm->io_base + QM_IFC_INT_SOURCE_V, val,
					 val == BIT(0), QM_VF_RESET_WAIT_US,
					 QM_VF_RESET_WAIT_TIMEOUT_US);
	/* hardware completion status should be available by this time */
	if (ret) {
		dev_err(dev, "couldn't get reset done status from PF, timeout!\n");
		return -ETIMEDOUT;
	}

	/*
	 * Whether message is got successfully,
	 * VF needs to ack PF by clearing the interrupt.
	 */
	ret = qm_get_mb_cmd(qm, &msg, 0);
	qm_clear_cmd_interrupt(qm, 0);
	if (ret) {
		dev_err(dev, "failed to get msg from PF in reset done!\n");
		return ret;
	}

	cmd = msg & QM_MB_CMD_DATA_MASK;
	if (cmd != QM_PF_RESET_DONE) {
		dev_err(dev, "the cmd(%u) is not reset done!\n", cmd);
		ret = -EINVAL;
	}

	return ret;
}

static void qm_pf_reset_vf_process(struct hisi_qm *qm,
				   enum qm_stop_reason stop_reason)
{
	struct device *dev = &qm->pdev->dev;
	int ret;

	dev_info(dev, "device reset start...\n");

	/* The message is obtained by querying the register during resetting */
	qm_cmd_uninit(qm);
	qm_pf_reset_vf_prepare(qm, stop_reason);

	ret = qm_wait_pf_reset_finish(qm);
	if (ret)
		goto err_get_status;

	qm_pf_reset_vf_done(qm);
	qm_cmd_init(qm);

	dev_info(dev, "device reset done.\n");

	return;

err_get_status:
	qm_cmd_init(qm);
	qm_reset_bit_clear(qm);
}

static void qm_cmd_process(struct work_struct *cmd_process)
{
	struct hisi_qm *qm = container_of(cmd_process,
					struct hisi_qm, cmd_process);
	struct device *dev = &qm->pdev->dev;
	u64 msg;
	u32 cmd;
	int ret;

	/*
@@ -4635,9 +4866,23 @@ static void qm_cmd_process(struct work_struct *cmd_process)
	 */
	ret = qm_get_mb_cmd(qm, &msg, 0);
	qm_clear_cmd_interrupt(qm, 0);
	if (ret)
	if (ret) {
		dev_err(dev, "failed to get msg from source!\n");
		return;
	}

	cmd = msg & QM_MB_CMD_DATA_MASK;
	switch (cmd) {
	case QM_PF_FLR_PREPARE:
		qm_pf_reset_vf_process(qm, QM_FLR);
		break;
	case QM_PF_SRST_PREPARE:
		qm_pf_reset_vf_process(qm, QM_SOFT_RESET);
		break;
	default:
		dev_err(dev, "unsupported cmd %u sent by PF!\n", cmd);
		break;
	}
}

/**