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 <jyothij@qti.qualcomm.com>
Signed-off-by: Michael Adisumarta <madisuma@codeaurora.org>
This commit is contained in:
Michael Adisumarta 2019-09-05 14:33:58 -07:00 • committed by Gerrit - the friendly Code Review server
commit 3d5ac78337

View file

@ -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;