From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-by2nam01on0122.outbound.protection.outlook.com ([104.47.34.122]:34106 "EHLO NAM01-BY2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1733225AbeITI2f (ORCPT ); Thu, 20 Sep 2018 04:28:35 -0400 From: Sasha Levin To: "stable@vger.kernel.org" , "linux-kernel@vger.kernel.org" CC: Tomer Tayar , Ariel Elior , "David S . Miller" , Sasha Levin Subject: [PATCH AUTOSEL 4.18 06/56] qed: Prevent a possible deadlock during driver load and unload Date: Thu, 20 Sep 2018 02:47:30 +0000 Message-ID: <20180920024716.58490-6-alexander.levin@microsoft.com> References: <20180920024716.58490-1-alexander.levin@microsoft.com> In-Reply-To: <20180920024716.58490-1-alexander.levin@microsoft.com> Content-Language: en-US Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Sender: stable-owner@vger.kernel.org List-ID: From: Tomer Tayar [ Upstream commit eaa50fc59e5841910987e90b0438b2643041f508 ] The MFW manages an internal lock to prevent concurrent hardware (de)initialization of different PFs. This, together with the busy-waiting for the MFW's responses for commands, might lead to a deadlock during concurrent load or unload of PFs. This patch adds the option to sleep within the busy-waiting, and uses it for the (un)load requests (which are not sent from an interrupt context) to prevent the possible deadlock. Signed-off-by: Tomer Tayar Signed-off-by: Ariel Elior Signed-off-by: David S. Miller Signed-off-by: Sasha Levin --- drivers/net/ethernet/qlogic/qed/qed_mcp.c | 43 ++++++++++++++++------- drivers/net/ethernet/qlogic/qed/qed_mcp.h | 21 ++++++----- 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethern= et/qlogic/qed/qed_mcp.c index 34e0db313850..c9dada0d2a44 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -48,7 +48,7 @@ #include "qed_reg_addr.h" #include "qed_sriov.h" =20 -#define CHIP_MCP_RESP_ITER_US 10 +#define QED_MCP_RESP_ITER_US 10 =20 #define QED_DRV_MB_MAX_RETRIES (500 * 1000) /* Account for 5 sec */ #define QED_MCP_RESET_RETRIES (50 * 1000) /* Account for 500 msec */ @@ -317,7 +317,7 @@ static void qed_mcp_reread_offsets(struct qed_hwfn *p_h= wfn, =20 int qed_mcp_reset(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { - u32 org_mcp_reset_seq, seq, delay =3D CHIP_MCP_RESP_ITER_US, cnt =3D 0; + u32 org_mcp_reset_seq, seq, delay =3D QED_MCP_RESP_ITER_US, cnt =3D 0; int rc =3D 0; =20 /* Ensure that only a single thread is accessing the mailbox */ @@ -449,10 +449,10 @@ static int _qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, struct qed_mcp_mb_params *p_mb_params, - u32 max_retries, u32 delay) + u32 max_retries, u32 usecs) { + u32 cnt =3D 0, msecs =3D DIV_ROUND_UP(usecs, 1000); struct qed_mcp_cmd_elem *p_cmd_elem; - u32 cnt =3D 0; u16 seq_num; int rc =3D 0; =20 @@ -475,7 +475,11 @@ _qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn, goto err; =20 spin_unlock_bh(&p_hwfn->mcp_info->cmd_lock); - udelay(delay); + + if (QED_MB_FLAGS_IS_SET(p_mb_params, CAN_SLEEP)) + msleep(msecs); + else + udelay(usecs); } while (++cnt < max_retries); =20 if (cnt >=3D max_retries) { @@ -504,7 +508,11 @@ _qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn, * The spinlock stays locked until the list element is removed. */ =20 - udelay(delay); + if (QED_MB_FLAGS_IS_SET(p_mb_params, CAN_SLEEP)) + msleep(msecs); + else + udelay(usecs); + spin_lock_bh(&p_hwfn->mcp_info->cmd_lock); =20 if (p_cmd_elem->b_is_completed) @@ -539,7 +547,7 @@ _qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn, "MFW mailbox: response 0x%08x param 0x%08x [after %d.%03d ms]\n", p_mb_params->mcp_resp, p_mb_params->mcp_param, - (cnt * delay) / 1000, (cnt * delay) % 1000); + (cnt * usecs) / 1000, (cnt * usecs) % 1000); =20 /* Clear the sequence number from the MFW response */ p_mb_params->mcp_resp &=3D FW_MSG_CODE_MASK; @@ -557,7 +565,7 @@ static int qed_mcp_cmd_and_union(struct qed_hwfn *p_hwf= n, { size_t union_data_size =3D sizeof(union drv_union_data); u32 max_retries =3D QED_DRV_MB_MAX_RETRIES; - u32 delay =3D CHIP_MCP_RESP_ITER_US; + u32 usecs =3D QED_MCP_RESP_ITER_US; =20 /* MCP not initialized */ if (!qed_mcp_is_init(p_hwfn)) { @@ -574,8 +582,13 @@ static int qed_mcp_cmd_and_union(struct qed_hwfn *p_hw= fn, return -EINVAL; } =20 + if (QED_MB_FLAGS_IS_SET(p_mb_params, CAN_SLEEP)) { + max_retries =3D DIV_ROUND_UP(max_retries, 1000); + usecs *=3D 1000; + } + return _qed_mcp_cmd_and_union(p_hwfn, p_ptt, p_mb_params, max_retries, - delay); + usecs); } =20 int qed_mcp_cmd(struct qed_hwfn *p_hwfn, @@ -792,6 +805,7 @@ __qed_mcp_load_req(struct qed_hwfn *p_hwfn, mb_params.data_src_size =3D sizeof(load_req); mb_params.p_data_dst =3D &load_rsp; mb_params.data_dst_size =3D sizeof(load_rsp); + mb_params.flags =3D QED_MB_FLAG_CAN_SLEEP; =20 DP_VERBOSE(p_hwfn, QED_MSG_SP, "Load Request: param 0x%08x [init_hw %d, drv_type %d, hsi_ver %d, pda= 0x%04x]\n", @@ -1013,7 +1027,8 @@ int qed_mcp_load_req(struct qed_hwfn *p_hwfn, =20 int qed_mcp_unload_req(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { - u32 wol_param, mcp_resp, mcp_param; + struct qed_mcp_mb_params mb_params; + u32 wol_param; =20 switch (p_hwfn->cdev->wol_config) { case QED_OV_WOL_DISABLED: @@ -1031,8 +1046,12 @@ int qed_mcp_unload_req(struct qed_hwfn *p_hwfn, stru= ct qed_ptt *p_ptt) wol_param =3D DRV_MB_PARAM_UNLOAD_WOL_MCP; } =20 - return qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_UNLOAD_REQ, wol_param, - &mcp_resp, &mcp_param); + memset(&mb_params, 0, sizeof(mb_params)); + mb_params.cmd =3D DRV_MSG_CODE_UNLOAD_REQ; + mb_params.param =3D wol_param; + mb_params.flags =3D QED_MB_FLAG_CAN_SLEEP; + + return qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); } =20 int qed_mcp_unload_done(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethern= et/qlogic/qed/qed_mcp.h index 632a838f1fe3..8223daefcc37 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -660,14 +660,19 @@ struct qed_mcp_info { }; =20 struct qed_mcp_mb_params { - u32 cmd; - u32 param; - void *p_data_src; - u8 data_src_size; - void *p_data_dst; - u8 data_dst_size; - u32 mcp_resp; - u32 mcp_param; + u32 cmd; + u32 param; + void *p_data_src; + void *p_data_dst; + u8 data_src_size; + u8 data_dst_size; + u32 mcp_resp; + u32 mcp_param; + u32 flags; +#define QED_MB_FLAG_CAN_SLEEP (0x1 << 0) +#define QED_MB_FLAGS_IS_SET(params, flag) \ + ({ typeof(params) __params =3D (params); \ + (__params && (__params->flags & QED_MB_FLAG_ ## flag)); }) }; =20 struct qed_drv_tlv_hdr { --=20 2.17.1