From 2eabbd25b651db053c68cbab6a7fc091a49fe877 Mon Sep 17 00:00:00 2001 From: Karthikeyan Ramasubramanian Date: Fri, 15 Apr 2016 13:58:28 -0600 Subject: 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 --- drivers/soc/qcom/glink_smd_xprt.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) (limited to 'drivers') 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 @@ -557,6 +557,24 @@ static void ssr_work_func(struct work_struct *work) mutex_unlock(&einfo->in_ssr_lock); } +/** + * 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); -- cgit v1.2.3