Merge "usb: f_qdss: Fix race between qdss_alloc_req & qdss_close"

This commit is contained in:
qctecmdr 2020-07-13 22:34:38 -07:00 • committed by Gerrit - the friendly Code Review server
commit b71e7e2a4e

View file

@ -15,7 +15,7 @@
#include "f_qdss.h"
static DEFINE_SPINLOCK(qdss_lock);
static DEFINE_SPINLOCK(channel_lock);
static LIST_HEAD(usb_qdss_ch_list);
static struct usb_interface_descriptor qdss_data_intf_desc = {
@ -260,12 +260,18 @@ void usb_qdss_free_req(struct usb_qdss_ch *ch)
struct list_head *act, *tmp;
struct qdss_req *qreq;
int data_write_req = 0, ctrl_write_req = 0;
unsigned long flags;
spin_lock_irqsave(&channel_lock, flags);
qdss = ch->priv_usb;
if (!qdss) {
spin_unlock_irqrestore(&channel_lock, flags);
pr_err("%s: qdss ctx is NULL\n", __func__);
return;
}
spin_unlock_irqrestore(&channel_lock, flags);
spin_lock_irqsave(&qdss->lock, flags);
list_for_each_safe(act, tmp, &qdss->data_write_pool) {
qreq = list_entry(act, struct qdss_req, list);
@ -285,6 +291,7 @@ void usb_qdss_free_req(struct usb_qdss_ch *ch)
qdss_log("channel:%s data_write_req:%d ctrl_write_req:%d freed\n",
qdss->ch.name, data_write_req, ctrl_write_req);
spin_unlock_irqrestore(&qdss->lock, flags);
}
EXPORT_SYMBOL(usb_qdss_free_req);
@ -296,6 +303,7 @@ int usb_qdss_alloc_req(struct usb_qdss_ch *ch, int no_write_buf)
struct list_head *list_pool;
int i;
struct qdss_req *qreq;
unsigned long flags;
qdss_log("channel:%s num_write_buf:%d\n", ch->name, no_write_buf);
@ -320,16 +328,22 @@ int usb_qdss_alloc_req(struct usb_qdss_ch *ch, int no_write_buf)
for (i = 0; i < no_write_buf; i++) {
qreq = kzalloc(sizeof(struct qdss_req), GFP_KERNEL);
if (!qreq)
goto fail;
req = usb_ep_alloc_request(in, GFP_ATOMIC);
if (!req) {
pr_err("%s: ctrl_in allocation err\n", __func__);
kfree(qreq);
goto fail;
}
spin_lock_irqsave(&qdss->lock, flags);
qreq->usb_req = req;
req->context = qreq;
req->complete = qdss_write_complete;
list_add_tail(&qreq->list, list_pool);
init_completion(&qreq->write_done);
spin_unlock_irqrestore(&qdss->lock, flags);
}
return 0;
@ -747,7 +761,7 @@ static struct f_qdss *alloc_usb_qdss(char *channel_name)
struct usb_qdss_ch *ch;
unsigned long flags;
spin_lock_irqsave(&qdss_lock, flags);
spin_lock_irqsave(&channel_lock, flags);
list_for_each_entry(ch, &usb_qdss_ch_list, list) {
if (!strcmp(channel_name, ch->name)) {
found = 1;
@ -756,13 +770,13 @@ static struct f_qdss *alloc_usb_qdss(char *channel_name)
}
if (found) {
spin_unlock_irqrestore(&qdss_lock, flags);
spin_unlock_irqrestore(&channel_lock, flags);
pr_err("%s: (%s) is already available.\n",
__func__, channel_name);
return ERR_PTR(-EEXIST);
}
spin_unlock_irqrestore(&qdss_lock, flags);
spin_unlock_irqrestore(&channel_lock, flags);
qdss = kzalloc(sizeof(struct f_qdss), GFP_KERNEL);
if (!qdss) {
pr_err("%s: Unable to allocate qdss device\n", __func__);
@ -775,11 +789,11 @@ static struct f_qdss *alloc_usb_qdss(char *channel_name)
return ERR_PTR(-ENOMEM);
}
spin_lock_irqsave(&qdss_lock, flags);
spin_lock_irqsave(&channel_lock, flags);
ch = &qdss->ch;
ch->name = channel_name;
list_add_tail(&ch->list, &usb_qdss_ch_list);
spin_unlock_irqrestore(&qdss_lock, flags);
spin_unlock_irqrestore(&channel_lock, flags);
spin_lock_init(&qdss->lock);
INIT_LIST_HEAD(&qdss->ctrl_write_pool);
@ -910,7 +924,7 @@ struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv,
return NULL;
}
spin_lock_irqsave(&qdss_lock, flags);
spin_lock_irqsave(&channel_lock, flags);
/* Check if we already have a channel with this name */
list_for_each_entry(ch, &usb_qdss_ch_list, list) {
if (!strcmp(name, ch->name)) {
@ -920,7 +934,7 @@ struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv,
}
if (!found) {
spin_unlock_irqrestore(&qdss_lock, flags);
spin_unlock_irqrestore(&channel_lock, flags);
qdss_log("failed to find channel:%s\n", name);
return NULL;
}
@ -932,7 +946,7 @@ struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv,
ch->notify = notify;
ch->app_conn = 1;
qdss->qdss_close = false;
spin_unlock_irqrestore(&qdss_lock, flags);
spin_unlock_irqrestore(&channel_lock, flags);
/* the case USB cabel was connected before qdss called qdss_open */
if (qdss->usb_connected == 1)
@ -950,9 +964,9 @@ void usb_qdss_close(struct usb_qdss_ch *ch)
int status;
struct qdss_req *qreq;
spin_lock_irqsave(&qdss_lock, flags);
spin_lock_irqsave(&channel_lock, flags);
if (!ch->priv_usb) {
spin_unlock_irqrestore(&qdss_lock, flags);
spin_unlock_irqrestore(&channel_lock, flags);
pr_err("%s is called for %s without calling usb_qdss_open()\n",
__func__, ch->name);
return;
@ -964,31 +978,33 @@ void usb_qdss_close(struct usb_qdss_ch *ch)
while (!list_empty(&qdss->queued_data_pool)) {
qreq = list_first_entry(&qdss->queued_data_pool,
struct qdss_req, list);
spin_unlock_irqrestore(&qdss_lock, flags);
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);
spin_lock_irqsave(&qdss_lock, flags);
spin_lock_irqsave(&channel_lock, flags);
}
spin_unlock_irqrestore(&channel_lock, flags);
usb_qdss_free_req(ch);
spin_lock_irqsave(&channel_lock, flags);
ch->priv_usb = NULL;
ch->notify = NULL;
if (!qdss || !qdss->usb_connected ||
!strcmp(qdss->ch.name, USB_QDSS_CH_MDM)) {
ch->app_conn = 0;
spin_unlock_irqrestore(&qdss_lock, flags);
spin_unlock_irqrestore(&channel_lock, flags);
return;
}
if (qdss->endless_req) {
spin_unlock_irqrestore(&qdss_lock, flags);
spin_unlock_irqrestore(&channel_lock, flags);
usb_ep_dequeue(qdss->port.data, qdss->endless_req);
spin_lock_irqsave(&qdss_lock, flags);
spin_lock_irqsave(&channel_lock, flags);
}
gadget = qdss->gadget;
ch->app_conn = 0;
spin_unlock_irqrestore(&qdss_lock, flags);
spin_unlock_irqrestore(&channel_lock, flags);
status = uninit_data(qdss->port.data);
if (status)
@ -1012,13 +1028,13 @@ static void qdss_cleanup(void)
list_for_each_safe(act, tmp, &usb_qdss_ch_list) {
_ch = list_entry(act, struct usb_qdss_ch, list);
qdss = container_of(_ch, struct f_qdss, ch);
spin_lock_irqsave(&qdss_lock, flags);
spin_lock_irqsave(&channel_lock, flags);
destroy_workqueue(qdss->wq);
if (!_ch->priv) {
list_del(&_ch->list);
kfree(qdss);
}
spin_unlock_irqrestore(&qdss_lock, flags);
spin_unlock_irqrestore(&channel_lock, flags);
}
}