From 3d5ac7833796b3a34e90e364641f1d910cd4c59b Mon Sep 17 00:00:00 2001 From: Michael Adisumarta Date: Thu, 5 Sep 2019 14:33:58 -0700 Subject: [PATCH] msm: ipa: fix race condition between USB connect and modem SSR This fix solves the race conditions between USB connect and modem SSR by using mutex protection. Change-Id: If97264a7c875f3e0c66b34b1b02e48b21c99e319 Acked-by: Jyothi J Signed-off-by: Michael Adisumarta --- drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c | 74 +++++++++++++++-------- 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c index ad06bca34255..22af3c478a13 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mpm.c @@ -375,12 +375,6 @@ struct ipa_mpm_mhi_driver { bool init_complete; /* General MPM mutex to protect concurrent update of MPM GSI states */ struct mutex mutex; - /* - * Mutex to protect IPA clock vote/unvote to make sure IPA isn't double - * devoted for concurrency scenarios such as SSR and LPM mode CB - * concurrency. - */ - struct mutex lpm_mutex; /* * Mutex to protect mhi_dev update/ access, for concurrency such as * 5G SSR and USB disconnect/connect. @@ -1357,7 +1351,6 @@ static void ipa_mpm_mhip_shutdown(int mhip_idx) ipa_mpm_clean_mhip_chan(mhip_idx, dl_cons_chan); - mutex_lock(&ipa_mpm_ctx->md[mhip_idx].lpm_mutex); if (!ipa_mpm_ctx->md[mhip_idx].in_lpm) { ipa_mpm_vote_unvote_ipa_clk(CLK_OFF, mhip_idx); /* while in modem shutdown scenarios such as SSR, no explicit @@ -1365,10 +1358,8 @@ static void ipa_mpm_mhip_shutdown(int mhip_idx) */ ipa_mpm_ctx->md[mhip_idx].in_lpm = true; } - mutex_unlock(&ipa_mpm_ctx->md[mhip_idx].lpm_mutex); mutex_lock(&ipa_mpm_ctx->md[mhip_idx].mhi_mutex); ipa_mpm_ctx->md[mhip_idx].mhi_dev = NULL; - ipa_mpm_ctx->md[mhip_idx].init_complete = false; mutex_unlock(&ipa_mpm_ctx->md[mhip_idx].mhi_mutex); IPA_MPM_FUNC_EXIT(); } @@ -1399,6 +1390,18 @@ static int ipa_mpm_vote_unvote_pcie_clk(enum ipa_mpm_clk_vote_type vote, return 0; } + if (!ipa_mpm_ctx->md[probe_id].init_complete) { + /* + * SSR might be in progress, dont have to vote/unvote for + * IPA clocks as it will be taken care in remove_cb/subsequent + * probe. + */ + IPA_MPM_DBG("SSR in progress, return\n"); + mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); + return 0; + } + mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); + IPA_MPM_ERR("PCIe clock vote/unvote = %d probe_id = %d clk_cnt = %d\n", vote, probe_id, atomic_read(&ipa_mpm_ctx->md[probe_id].clk_cnt.pcie_clk_cnt)); @@ -1409,7 +1412,6 @@ static int ipa_mpm_vote_unvote_pcie_clk(enum ipa_mpm_clk_vote_type vote, if (result) { IPA_MPM_ERR("mhi_sync_get failed for probe_id %d\n", result, probe_id); - mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); return result; } @@ -1423,7 +1425,6 @@ static int ipa_mpm_vote_unvote_pcie_clk(enum ipa_mpm_clk_vote_type vote, IPA_MPM_DBG("probe_id %d PCIE clock already devoted\n", probe_id); WARN_ON(1); - mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); return 0; } mhi_device_put(ipa_mpm_ctx->md[probe_id].mhi_dev, MHI_VOTE_BUS); @@ -1431,8 +1432,6 @@ static int ipa_mpm_vote_unvote_pcie_clk(enum ipa_mpm_clk_vote_type vote, atomic_dec(&ipa_mpm_ctx->md[probe_id].clk_cnt.pcie_clk_cnt); atomic_dec(&ipa_mpm_ctx->pcie_clk_total_cnt); } - - mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); return result; } @@ -1482,11 +1481,9 @@ static int ipa_mpm_start_stop_remote_mhip_chan( return ret; } - mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); /* For error state, expect modem SSR to recover from error */ if (ipa_mpm_ctx->md[probe_id].remote_state == MPM_MHIP_REMOTE_ERR) { IPA_MPM_ERR("Remote channels in err state for %d\n", probe_id); - mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); return -EFAULT; } @@ -1497,12 +1494,14 @@ static int ipa_mpm_start_stop_remote_mhip_chan( probe_id); } else { ret = mhi_resume_transfer(mhi_dev); + mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); if (ret) ipa_mpm_ctx->md[probe_id].remote_state = MPM_MHIP_REMOTE_ERR; else ipa_mpm_ctx->md[probe_id].remote_state = MPM_MHIP_REMOTE_START; + mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); } } else { if (ipa_mpm_ctx->md[probe_id].remote_state == @@ -1511,15 +1510,16 @@ static int ipa_mpm_start_stop_remote_mhip_chan( probe_id); } else { ret = mhi_pause_transfer(mhi_dev); + mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); if (ret) ipa_mpm_ctx->md[probe_id].remote_state = MPM_MHIP_REMOTE_ERR; else ipa_mpm_ctx->md[probe_id].remote_state = MPM_MHIP_REMOTE_STOP; + mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); } } - mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); return ret; } @@ -1563,6 +1563,15 @@ static enum mhip_status_type ipa_mpm_start_stop_mhip_chan( IPA_MPM_ERR("fail to get EP# for idx %d\n", ipa_ep_idx); return MHIP_STATUS_EP_NOT_FOUND; } + + mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); + if (!ipa_mpm_ctx->md[probe_id].init_complete) { + IPA_MPM_ERR("MHIP probe %d not initialized\n", probe_id); + mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); + return MHIP_STATUS_EP_NOT_READY; + } + mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); + ep = &ipa3_ctx->ep[ipa_ep_idx]; if (mhip_chan == IPA_MPM_MHIP_CHAN_UL) { @@ -1951,10 +1960,8 @@ static int ipa_mpm_mhi_probe_cb(struct mhi_device *mhi_dev, ipa_mpm_ctx->md[probe_id].remote_state = MPM_MHIP_REMOTE_STOP; mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); ipa_mpm_vote_unvote_pcie_clk(CLK_ON, probe_id); - mutex_lock(&ipa_mpm_ctx->md[probe_id].lpm_mutex); ipa_mpm_vote_unvote_ipa_clk(CLK_ON, probe_id); ipa_mpm_ctx->md[probe_id].in_lpm = false; - mutex_unlock(&ipa_mpm_ctx->md[probe_id].lpm_mutex); IPA_MPM_DBG("ul chan = %d, dl_chan = %d\n", ul_prod, dl_cons); /* @@ -2089,8 +2096,10 @@ static int ipa_mpm_mhi_probe_cb(struct mhi_device *mhi_dev, * to MHI driver. remove_cb will be called eventually when * Device side comes from where pending cleanup happens. */ + mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); atomic_inc(&ipa_mpm_ctx->probe_cnt); - ipa_mpm_ctx->md[probe_id].init_complete = true; + ipa_mpm_ctx->md[probe_id].init_complete = false; + mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); IPA_MPM_FUNC_EXIT(); return 0; } @@ -2271,8 +2280,6 @@ static int ipa_mpm_mhi_probe_cb(struct mhi_device *mhi_dev, } atomic_inc(&ipa_mpm_ctx->probe_cnt); - ipa_mpm_ctx->md[probe_id].init_complete = true; - /* Check if ODL pipe is connected to MHIP DPL pipe before probe */ if (probe_id == IPA_MPM_MHIP_CH_ID_2 && ipa3_is_odl_connected()) { @@ -2280,7 +2287,9 @@ static int ipa_mpm_mhi_probe_cb(struct mhi_device *mhi_dev, ret = ipa_mpm_set_dma_mode(IPA_CLIENT_MHI_PRIME_DPL_PROD, IPA_CLIENT_USB_DPL_CONS, false); } - + mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); + ipa_mpm_ctx->md[probe_id].init_complete = true; + mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex); IPA_MPM_FUNC_EXIT(); return 0; @@ -2349,6 +2358,11 @@ static void ipa_mpm_mhi_remove_cb(struct mhi_device *mhi_dev) } IPA_MPM_DBG("remove_cb for mhip_idx = %d", mhip_idx); + + mutex_lock(&ipa_mpm_ctx->md[mhip_idx].mhi_mutex); + ipa_mpm_ctx->md[mhip_idx].init_complete = false; + mutex_unlock(&ipa_mpm_ctx->md[mhip_idx].mhi_mutex); + ipa_mpm_mhip_shutdown(mhip_idx); atomic_dec(&ipa_mpm_ctx->probe_cnt); @@ -2387,7 +2401,19 @@ static void ipa_mpm_mhi_status_cb(struct mhi_device *mhi_dev, return; } - mutex_lock(&ipa_mpm_ctx->md[mhip_idx].lpm_mutex); + mutex_lock(&ipa_mpm_ctx->md[mhip_idx].mhi_mutex); + if (!ipa_mpm_ctx->md[mhip_idx].init_complete) { + /* + * SSR might be in progress, dont have to vote/unvote for + * IPA clocks as it will be taken care in remove_cb/subsequent + * probe. + */ + IPA_MPM_DBG("SSR in progress, return\n"); + mutex_unlock(&ipa_mpm_ctx->md[mhip_idx].mhi_mutex); + return; + } + mutex_unlock(&ipa_mpm_ctx->md[mhip_idx].mhi_mutex); + switch (mhi_cb) { case MHI_CB_IDLE: break; @@ -2424,7 +2450,6 @@ static void ipa_mpm_mhi_status_cb(struct mhi_device *mhi_dev, IPA_MPM_ERR("unexpected event %d\n", mhi_cb); break; } - mutex_unlock(&ipa_mpm_ctx->md[mhip_idx].lpm_mutex); } static void ipa_mpm_mhip_map_prot(enum ipa_usb_teth_prot prot, @@ -2777,7 +2802,6 @@ static int ipa_mpm_probe(struct platform_device *pdev) for (i = 0; i < IPA_MPM_MHIP_CH_ID_MAX; i++) { mutex_init(&ipa_mpm_ctx->md[i].mutex); mutex_init(&ipa_mpm_ctx->md[i].mhi_mutex); - mutex_init(&ipa_mpm_ctx->md[i].lpm_mutex); } ipa_mpm_ctx->dev_info.pdev = pdev;