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>
This commit is contained in:
Karthikeyan Ramasubramanian 2016-04-15 13:58:28 -06:00 committed by Jeevan Shriram
parent 1071415ec4
commit 2eabbd25b6

View file

@ -557,6 +557,24 @@ static void ssr_work_func(struct work_struct *work)
mutex_unlock(&einfo->in_ssr_lock); 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 * process_tx_done() - process a tx done task
* @work: The tx done task to process. * @work: The tx done task to process.
@ -899,6 +917,7 @@ static void smd_data_ch_close(struct channel *ch)
{ {
struct intent_info *intent; struct intent_info *intent;
unsigned long flags; unsigned long flags;
struct channel_work *ch_work;
SMDXPRT_INFO(ch->edge, "%s Closing SMD channel lcid %u\n", SMDXPRT_INFO(ch->edge, "%s Closing SMD channel lcid %u\n",
__func__, ch->lcid); __func__, ch->lcid);
@ -919,11 +938,12 @@ static void smd_data_ch_close(struct channel *ch)
smd_close(ch->smd_ch); smd_close(ch->smd_ch);
ch->smd_ch = NULL; ch->smd_ch = NULL;
} else if (ch->local_legacy) { } else if (ch->local_legacy) {
mutex_lock(&ch->edge->rx_cmd_lock); ch_work = kzalloc(sizeof(*ch_work), GFP_KERNEL);
ch->edge->xprt_if.glink_core_if_ptr->rx_cmd_ch_close_ack( if (ch_work) {
&ch->edge->xprt_if, ch_work->ch = ch;
ch->lcid); INIT_WORK(&ch_work->work, deferred_close_ack);
mutex_unlock(&ch->edge->rx_cmd_lock); queue_work(ch->wq, &ch_work->work);
}
} }
mutex_unlock(&ch->ch_probe_lock); mutex_unlock(&ch->ch_probe_lock);