From bbfa34e5d2b1bb33a553086c469637b3777a7276 Mon Sep 17 00:00:00 2001 From: Udipto Goswami Date: Fri, 26 Feb 2021 14:08:57 +0530 Subject: [PATCH] usb: f_qdss: Avoid wait_for_completion if qdss_disable is called Commit 3741fb2cfb88 ("coresight: byte-cntr: Free USB requests when disconnected") introduced a regression which leads qdss_disable & qdss_close paths to race between them. If qdss_disable path is processing, it would queue disconnect_work which will notify the bypass notifier of USB_DISCONNECT and it will call free_req. Within same time frame if qdss_close is called, it will try to go ahead dequeue the request and wait_for_completion. But since free_req got called, it will free the qreq instance which wait_for_completion uses resulting in kernel panic. Fix this by checking for ep_dequeue status if dequeue was success then only wait_for_completion. Also taking the list operation under qdss->lock in order to avoid any races between qdss_write as well. Change-Id: I7bf9eab4296a509f5b459fa7768987c4a1f1287f Signed-off-by: Udipto Goswami --- drivers/usb/gadget/function/f_qdss.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c index 14f5449b791d..3908f5c750d2 100644 --- a/drivers/usb/gadget/function/f_qdss.c +++ b/drivers/usb/gadget/function/f_qdss.c @@ -901,16 +901,20 @@ void usb_qdss_close(struct usb_qdss_ch *ch) qdss_log("channel:%s\n", ch->name); qdss = ch->priv_usb; qdss->qdss_close = true; + spin_lock(&qdss->lock); while (!list_empty(&qdss->queued_data_pool)) { qreq = list_first_entry(&qdss->queued_data_pool, struct qdss_req, list); + spin_unlock(&qdss->lock); spin_unlock_irqrestore(&channel_lock, flags); qdss_log("dequeue req:%pK\n", qreq->usb_req); - usb_ep_dequeue(qdss->port.data, qreq->usb_req); - wait_for_completion(&qreq->write_done); + if (!usb_ep_dequeue(qdss->port.data, qreq->usb_req)) + wait_for_completion(&qreq->write_done); spin_lock_irqsave(&channel_lock, flags); + spin_lock(&qdss->lock); } + spin_unlock(&qdss->lock); spin_unlock_irqrestore(&channel_lock, flags); usb_qdss_free_req(ch); spin_lock_irqsave(&channel_lock, flags);