diff options
author | Karthikeyan Ramasubramanian <kramasub@codeaurora.org> | 2016-04-15 13:58:28 -0600 |
---|---|---|
committer | Jeevan Shriram <jshriram@codeaurora.org> | 2016-04-22 11:57:50 -0700 |
commit | 2eabbd25b651db053c68cbab6a7fc091a49fe877 (patch) | |
tree | 9ed56f678fd0793ae1d8f8304034c8b4a2fe633d /drivers | |
parent | 1071415ec45bbb04016b8da428d4d39befa6e813 (diff) |
soc: qcom: glink_smd_xprt: Defer channel close ACK operation
Depending on the timing of two competing transport registration with
G-Link core, a channel gets opened on one transport and then immediately
migrated to another transport. This channel migration happens in the
context of glink_open() operation itself and causes a re-lock attempt
on an already locked mutex.
Defer the channel close ACK operation so that channel migration does not
hit the deadlock.
CRs-Fixed: 1004150
Change-Id: I188846b95369b674830bc01ddeca764ad6d4d391
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/soc/qcom/glink_smd_xprt.c | 30 |
1 files changed, 25 insertions, 5 deletions
diff --git a/drivers/soc/qcom/glink_smd_xprt.c b/drivers/soc/qcom/glink_smd_xprt.c index 9936dc22400c..c0ed33ed2845 100644 --- a/drivers/soc/qcom/glink_smd_xprt.c +++ b/drivers/soc/qcom/glink_smd_xprt.c @@ -558,6 +558,24 @@ static void ssr_work_func(struct work_struct *work) } /** + * deferred_close_ack() - Generate a deferred channel close ack + * @work: The channel close ack work to generate. + */ +static void deferred_close_ack(struct work_struct *work) +{ + struct channel_work *ch_work; + struct channel *ch; + + ch_work = container_of(work, struct channel_work, work); + ch = ch_work->ch; + mutex_lock(&ch->edge->rx_cmd_lock); + ch->edge->xprt_if.glink_core_if_ptr->rx_cmd_ch_close_ack( + &ch->edge->xprt_if, ch->lcid); + mutex_unlock(&ch->edge->rx_cmd_lock); + kfree(ch_work); +} + +/** * process_tx_done() - process a tx done task * @work: The tx done task to process. */ @@ -899,6 +917,7 @@ static void smd_data_ch_close(struct channel *ch) { struct intent_info *intent; unsigned long flags; + struct channel_work *ch_work; SMDXPRT_INFO(ch->edge, "%s Closing SMD channel lcid %u\n", __func__, ch->lcid); @@ -919,11 +938,12 @@ static void smd_data_ch_close(struct channel *ch) smd_close(ch->smd_ch); ch->smd_ch = NULL; } else if (ch->local_legacy) { - mutex_lock(&ch->edge->rx_cmd_lock); - ch->edge->xprt_if.glink_core_if_ptr->rx_cmd_ch_close_ack( - &ch->edge->xprt_if, - ch->lcid); - mutex_unlock(&ch->edge->rx_cmd_lock); + ch_work = kzalloc(sizeof(*ch_work), GFP_KERNEL); + if (ch_work) { + ch_work->ch = ch; + INIT_WORK(&ch_work->work, deferred_close_ack); + queue_work(ch->wq, &ch_work->work); + } } mutex_unlock(&ch->ch_probe_lock); |