From c1b6981830c42360849750f62cf706fb1bd724e6 Mon Sep 17 00:00:00 2001 From: Elson Roy Serrao Date: Mon, 4 May 2020 11:09:58 -0700 Subject: [PATCH 1/4] usb: f_qdss: Dequeue pending requests upon qdss close When qdss_close is called the pending requests sent to the controller are not stopped and as a result the completion call back can occur at any time. If the qdss bridge driver frees the requests before the completion callback may result in use after free. Call dequeue on the pending requests in qdss close so that the requests are terminated instantly. Change-Id: I2752a598e8b104a6803064343743f591adc924e4 Signed-off-by: Elson Roy Serrao Signed-off-by: Mayank Rana --- drivers/usb/gadget/function/f_qdss.c | 29 ++++++++++++++++++++++++---- drivers/usb/gadget/function/f_qdss.h | 2 ++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c index 0a9d15216f01..788be2b1f474 100644 --- a/drivers/usb/gadget/function/f_qdss.c +++ b/drivers/usb/gadget/function/f_qdss.c @@ -239,6 +239,8 @@ static void qdss_write_complete(struct usb_ep *ep, } spin_lock_irqsave(&qdss->lock, flags); + if (!qdss->debug_inface_enabled) + list_del(&req->list); list_add_tail(&req->list, list_pool); if (req->length != 0) { d_req->actual = req->actual; @@ -810,6 +812,7 @@ static struct f_qdss *alloc_usb_qdss(char *channel_name) INIT_LIST_HEAD(&qdss->ctrl_read_pool); INIT_LIST_HEAD(&qdss->ctrl_write_pool); INIT_LIST_HEAD(&qdss->data_write_pool); + INIT_LIST_HEAD(&qdss->queued_data_pool); INIT_WORK(&qdss->connect_w, usb_qdss_connect_work); INIT_WORK(&qdss->disconnect_w, usb_qdss_disconnect_work); @@ -911,14 +914,14 @@ int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req) unsigned long flags; struct usb_request *req = NULL; - pr_debug("usb_qdss_ctrl_write\n"); + pr_debug("usb_qdss_data_write\n"); if (!qdss) return -ENODEV; spin_lock_irqsave(&qdss->lock, flags); - if (qdss->usb_connected == 0) { + if (qdss->qdss_close || qdss->usb_connected == 0) { spin_unlock_irqrestore(&qdss->lock, flags); return -EIO; } @@ -931,7 +934,7 @@ int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req) req = list_first_entry(&qdss->data_write_pool, struct usb_request, list); - list_del(&req->list); + list_move_tail(&req->list, &qdss->queued_data_pool); spin_unlock_irqrestore(&qdss->lock, flags); req->buf = d_req->buf; @@ -942,7 +945,8 @@ int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req) req->num_mapped_sgs = d_req->num_mapped_sgs; if (usb_ep_queue(qdss->port.data, req, GFP_ATOMIC)) { spin_lock_irqsave(&qdss->lock, flags); - list_add_tail(&req->list, &qdss->data_write_pool); + /* Remove from queued pool and add back to data pool */ + list_move_tail(&req->list, &qdss->data_write_pool); spin_unlock_irqrestore(&qdss->lock, flags); pr_err("qdss usb_ep_queue failed\n"); return -EIO; @@ -989,6 +993,7 @@ struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv, ch->priv = priv; ch->notify = notify; ch->app_conn = 1; + qdss->qdss_close = false; spin_unlock_irqrestore(&qdss_lock, flags); /* the case USB cabel was connected before qdss called qdss_open */ @@ -1005,6 +1010,8 @@ void usb_qdss_close(struct usb_qdss_ch *ch) struct usb_gadget *gadget; unsigned long flags; int status; + struct usb_request *req; + pr_debug("%s\n", __func__); @@ -1017,6 +1024,20 @@ void usb_qdss_close(struct usb_qdss_ch *ch) } qdss = ch->priv_usb; + qdss->qdss_close = true; + while (!list_empty(&qdss->queued_data_pool)) { + req = list_first_entry(&qdss->queued_data_pool, + struct usb_request, list); + spin_unlock_irqrestore(&qdss_lock, flags); + if (usb_ep_dequeue(qdss->port.data, req)) { + spin_lock_irqsave(&qdss_lock, flags); + list_move_tail(&req->list, &qdss->data_write_pool); + spin_unlock_irqrestore(&qdss_lock, flags); + } + spin_lock_irqsave(&qdss_lock, flags); + } + + usb_qdss_free_req(ch); ch->priv_usb = NULL; if (!qdss || !qdss->usb_connected || !strcmp(qdss->ch.name, USB_QDSS_CH_MDM)) { diff --git a/drivers/usb/gadget/function/f_qdss.h b/drivers/usb/gadget/function/f_qdss.h index 13186d6e8988..cd7c554e98fb 100644 --- a/drivers/usb/gadget/function/f_qdss.h +++ b/drivers/usb/gadget/function/f_qdss.h @@ -54,6 +54,7 @@ struct f_qdss { /* for mdm channel SW path */ struct list_head data_write_pool; + struct list_head queued_data_pool; struct work_struct connect_w; struct work_struct disconnect_w; @@ -62,6 +63,7 @@ struct f_qdss { unsigned int ctrl_in_enabled:1; unsigned int ctrl_out_enabled:1; struct workqueue_struct *wq; + bool qdss_close; }; struct usb_qdss_opts { From a394115fd44af9f2e6a663870c5f64665a452f6a Mon Sep 17 00:00:00 2001 From: Elson Roy Serrao Date: Mon, 4 May 2020 11:14:55 -0700 Subject: [PATCH 2/4] usb: f_qdss: Handle async completion of requests during qdss_close When qdss_close is called we try to dequeue the pending and started requests and then free those requests. But when async completion is enabled the completion callback could be called after freeing up that request which may lead to list corruption. Handle this by ensuring that the requests are freed only after the completion callback is done for all the requests. Change-Id: I3e5b1bad06f281422a71c84a617feb65027c2c84 Signed-off-by: Elson Roy Serrao Signed-off-by: Mayank Rana --- drivers/usb/gadget/function/f_qdss.c | 13 +++++++------ include/linux/usb/usb_qdss.h | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c index 788be2b1f474..6d0e77c85b80 100644 --- a/drivers/usb/gadget/function/f_qdss.c +++ b/drivers/usb/gadget/function/f_qdss.c @@ -242,6 +242,7 @@ static void qdss_write_complete(struct usb_ep *ep, if (!qdss->debug_inface_enabled) list_del(&req->list); list_add_tail(&req->list, list_pool); + complete(&d_req->write_done); if (req->length != 0) { d_req->actual = req->actual; d_req->status = req->status; @@ -943,10 +944,12 @@ int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req) req->sg = d_req->sg; req->num_sgs = d_req->num_sgs; req->num_mapped_sgs = d_req->num_mapped_sgs; + reinit_completion(&d_req->write_done); if (usb_ep_queue(qdss->port.data, req, GFP_ATOMIC)) { spin_lock_irqsave(&qdss->lock, flags); /* Remove from queued pool and add back to data pool */ list_move_tail(&req->list, &qdss->data_write_pool); + complete(&d_req->write_done); spin_unlock_irqrestore(&qdss->lock, flags); pr_err("qdss usb_ep_queue failed\n"); return -EIO; @@ -1011,7 +1014,7 @@ void usb_qdss_close(struct usb_qdss_ch *ch) unsigned long flags; int status; struct usb_request *req; - + struct qdss_request *d_req; pr_debug("%s\n", __func__); @@ -1028,12 +1031,10 @@ void usb_qdss_close(struct usb_qdss_ch *ch) while (!list_empty(&qdss->queued_data_pool)) { req = list_first_entry(&qdss->queued_data_pool, struct usb_request, list); + d_req = req->context; spin_unlock_irqrestore(&qdss_lock, flags); - if (usb_ep_dequeue(qdss->port.data, req)) { - spin_lock_irqsave(&qdss_lock, flags); - list_move_tail(&req->list, &qdss->data_write_pool); - spin_unlock_irqrestore(&qdss_lock, flags); - } + usb_ep_dequeue(qdss->port.data, req); + wait_for_completion(&d_req->write_done); spin_lock_irqsave(&qdss_lock, flags); } diff --git a/include/linux/usb/usb_qdss.h b/include/linux/usb/usb_qdss.h index ce1667a661ef..186c4b1a50cd 100644 --- a/include/linux/usb/usb_qdss.h +++ b/include/linux/usb/usb_qdss.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2012-2013, 2017-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2013, 2017-2020 The Linux Foundation. All rights reserved. */ #ifndef __LINUX_USB_QDSS_H @@ -22,6 +22,7 @@ struct qdss_request { struct scatterlist *sg; unsigned int num_sgs; unsigned int num_mapped_sgs; + struct completion write_done; }; struct usb_qdss_ch { From 3b6534f1478eb0f1a394fb8872eb6a32f13c0932 Mon Sep 17 00:00:00 2001 From: Udipto Goswami Date: Fri, 24 Apr 2020 15:46:03 +0530 Subject: [PATCH 3/4] usb: f_qdss: Remove QDSS read functionality as not in use Removing the APIs related to ctrl_read since those are not used by any client of f_qdss. Change-Id: Idf7f1ffd8afe8a72f83e036a7986d35f2f71bea8 Signed-off-by: Udipto Goswami Signed-off-by: Mayank Rana --- .../hwtracing/coresight/coresight-byte-cntr.c | 2 +- drivers/usb/gadget/function/f_qdss.c | 92 +------------------ drivers/usb/gadget/function/f_qdss.h | 1 - include/linux/usb/usb_qdss.h | 14 +-- 4 files changed, 5 insertions(+), 104 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-byte-cntr.c b/drivers/hwtracing/coresight/coresight-byte-cntr.c index 6fcff596935d..1aa933e2b959 100644 --- a/drivers/hwtracing/coresight/coresight-byte-cntr.c +++ b/drivers/hwtracing/coresight/coresight-byte-cntr.c @@ -519,7 +519,7 @@ void usb_bypass_notifier(void *priv, unsigned int event, switch (event) { case USB_QDSS_CONNECT: - usb_qdss_alloc_req(ch, USB_BUF_NUM, 0); + usb_qdss_alloc_req(ch, USB_BUF_NUM); usb_bypass_start(drvdata); queue_work(drvdata->usb_wq, &(drvdata->read_work)); break; diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c index 6d0e77c85b80..7659ae430bfb 100644 --- a/drivers/usb/gadget/function/f_qdss.c +++ b/drivers/usb/gadget/function/f_qdss.c @@ -253,27 +253,6 @@ static void qdss_write_complete(struct usb_ep *ep, qdss->ch.notify(qdss->ch.priv, state, d_req, NULL); } -static void qdss_ctrl_read_complete(struct usb_ep *ep, - struct usb_request *req) -{ - struct f_qdss *qdss = ep->driver_data; - struct qdss_request *d_req = req->context; - unsigned long flags; - - pr_debug("%s\n", __func__); - - d_req->actual = req->actual; - d_req->status = req->status; - - spin_lock_irqsave(&qdss->lock, flags); - list_add_tail(&req->list, &qdss->ctrl_read_pool); - spin_unlock_irqrestore(&qdss->lock, flags); - - if (qdss->ch.notify) - qdss->ch.notify(qdss->ch.priv, USB_QDSS_CTRL_READ_DONE, d_req, - NULL); -} - void usb_qdss_free_req(struct usb_qdss_ch *ch) { struct f_qdss *qdss; @@ -299,17 +278,10 @@ void usb_qdss_free_req(struct usb_qdss_ch *ch) list_del(&req->list); usb_ep_free_request(qdss->port.ctrl_in, req); } - - list_for_each_safe(act, tmp, &qdss->ctrl_read_pool) { - req = list_entry(act, struct usb_request, list); - list_del(&req->list); - usb_ep_free_request(qdss->port.ctrl_out, req); - } } EXPORT_SYMBOL(usb_qdss_free_req); -int usb_qdss_alloc_req(struct usb_qdss_ch *ch, int no_write_buf, - int no_read_buf) +int usb_qdss_alloc_req(struct usb_qdss_ch *ch, int no_write_buf) { struct f_qdss *qdss = ch->priv_usb; struct usb_request *req; @@ -324,10 +296,8 @@ int usb_qdss_alloc_req(struct usb_qdss_ch *ch, int no_write_buf, return -ENODEV; } - if ((qdss->debug_inface_enabled && - (no_write_buf <= 0 || no_read_buf <= 0)) || - (!qdss->debug_inface_enabled && - (no_write_buf <= 0 || no_read_buf))) { + if ((qdss->debug_inface_enabled && no_write_buf <= 0) || + (!qdss->debug_inface_enabled && no_write_buf <= 0)) { pr_err("%s: missing params\n", __func__); return -ENODEV; } @@ -350,16 +320,6 @@ int usb_qdss_alloc_req(struct usb_qdss_ch *ch, int no_write_buf, list_add_tail(&req->list, list_pool); } - for (i = 0; i < no_read_buf; i++) { - req = usb_ep_alloc_request(qdss->port.ctrl_out, GFP_ATOMIC); - if (!req) { - pr_err("%s: ctrl_out allocation err\n", __func__); - goto fail; - } - req->complete = qdss_ctrl_read_complete; - list_add_tail(&req->list, &qdss->ctrl_read_pool); - } - return 0; fail: @@ -810,7 +770,6 @@ static struct f_qdss *alloc_usb_qdss(char *channel_name) spin_unlock_irqrestore(&qdss_lock, flags); spin_lock_init(&qdss->lock); - INIT_LIST_HEAD(&qdss->ctrl_read_pool); INIT_LIST_HEAD(&qdss->ctrl_write_pool); INIT_LIST_HEAD(&qdss->data_write_pool); INIT_LIST_HEAD(&qdss->queued_data_pool); @@ -820,51 +779,6 @@ static struct f_qdss *alloc_usb_qdss(char *channel_name) return qdss; } -int usb_qdss_ctrl_read(struct usb_qdss_ch *ch, struct qdss_request *d_req) -{ - struct f_qdss *qdss = ch->priv_usb; - unsigned long flags; - struct usb_request *req = NULL; - - pr_debug("%s\n", __func__); - - if (!qdss) - return -ENODEV; - - spin_lock_irqsave(&qdss->lock, flags); - - if (qdss->usb_connected == 0) { - spin_unlock_irqrestore(&qdss->lock, flags); - return -EIO; - } - - if (list_empty(&qdss->ctrl_read_pool)) { - spin_unlock_irqrestore(&qdss->lock, flags); - pr_err("error: %s list is empty\n", __func__); - return -EAGAIN; - } - - req = list_first_entry(&qdss->ctrl_read_pool, struct usb_request, list); - list_del(&req->list); - spin_unlock_irqrestore(&qdss->lock, flags); - - req->buf = d_req->buf; - req->length = d_req->length; - req->context = d_req; - - if (usb_ep_queue(qdss->port.ctrl_out, req, GFP_ATOMIC)) { - /* If error add the link to linked list again*/ - spin_lock_irqsave(&qdss->lock, flags); - list_add_tail(&req->list, &qdss->ctrl_read_pool); - spin_unlock_irqrestore(&qdss->lock, flags); - pr_err("qdss usb_ep_queue failed\n"); - return -EIO; - } - - return 0; -} -EXPORT_SYMBOL(usb_qdss_ctrl_read); - int usb_qdss_ctrl_write(struct usb_qdss_ch *ch, struct qdss_request *d_req) { struct f_qdss *qdss = ch->priv_usb; diff --git a/drivers/usb/gadget/function/f_qdss.h b/drivers/usb/gadget/function/f_qdss.h index cd7c554e98fb..cf48773f13f0 100644 --- a/drivers/usb/gadget/function/f_qdss.h +++ b/drivers/usb/gadget/function/f_qdss.h @@ -49,7 +49,6 @@ struct f_qdss { bool debug_inface_enabled; struct usb_request *endless_req; struct usb_qdss_ch ch; - struct list_head ctrl_read_pool; struct list_head ctrl_write_pool; /* for mdm channel SW path */ diff --git a/include/linux/usb/usb_qdss.h b/include/linux/usb/usb_qdss.h index 186c4b1a50cd..9cdb005cfca7 100644 --- a/include/linux/usb/usb_qdss.h +++ b/include/linux/usb/usb_qdss.h @@ -48,12 +48,10 @@ struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv, void (*notify)(void *priv, unsigned int event, struct qdss_request *d_req, struct usb_qdss_ch *ch)); void usb_qdss_close(struct usb_qdss_ch *ch); -int usb_qdss_alloc_req(struct usb_qdss_ch *ch, int n_write, int n_read); +int usb_qdss_alloc_req(struct usb_qdss_ch *ch, int n_write); void usb_qdss_free_req(struct usb_qdss_ch *ch); -int usb_qdss_read(struct usb_qdss_ch *ch, struct qdss_request *d_req); int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req); int usb_qdss_ctrl_write(struct usb_qdss_ch *ch, struct qdss_request *d_req); -int usb_qdss_ctrl_read(struct usb_qdss_ch *ch, struct qdss_request *d_req); #else static inline struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv, void (*n)(void *, unsigned int event, @@ -62,11 +60,6 @@ static inline struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv, return ERR_PTR(-ENODEV); } -static inline int usb_qdss_read(struct usb_qdss_ch *c, struct qdss_request *d) -{ - return -ENODEV; -} - static inline int usb_qdss_write(struct usb_qdss_ch *c, struct qdss_request *d) { return -ENODEV; @@ -78,11 +71,6 @@ static inline int usb_qdss_ctrl_write(struct usb_qdss_ch *c, return -ENODEV; } -static inline int usb_qdss_ctrl_read(struct usb_qdss_ch *c, - struct qdss_request *d) -{ - return -ENODEV; -} static inline int usb_qdss_alloc_req(struct usb_qdss_ch *c, int n_wr, int n_rd) { return -ENODEV; From 5a2932496a1186c671e44746e779f470bec25a34 Mon Sep 17 00:00:00 2001 From: Udipto Goswami Date: Wed, 6 May 2020 18:24:32 -0700 Subject: [PATCH 4/4] usb: f_qdss: Fix watchdog bark issue on wait_for_completion The coresight driver is freeing up d_req as part of notify from write_complete. Accessing write_done form qdss_close, wait_for_completion will be stuck there causing watchdog bark. Fix this by making f_qdss independent of coresight driver from initializing complete. Initialization of completion now occurs from alloc_req. Change-Id: Ie6b1cd445ed6bb38c1a555f6c4c41068090e4fac Signed-off-by: Udipto Goswami Signed-off-by: Mayank Rana --- drivers/usb/gadget/function/f_qdss.c | 70 ++++++++++++++++------------ include/linux/usb/usb_qdss.h | 8 +++- 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c index 7659ae430bfb..7b660241c57c 100644 --- a/drivers/usb/gadget/function/f_qdss.c +++ b/drivers/usb/gadget/function/f_qdss.c @@ -220,7 +220,8 @@ static void qdss_write_complete(struct usb_ep *ep, struct usb_request *req) { struct f_qdss *qdss = ep->driver_data; - struct qdss_request *d_req = req->context; + struct qdss_req *qreq = req->context; + struct qdss_request *d_req = qreq->qdss_req; struct usb_ep *in; struct list_head *list_pool; enum qdss_state state; @@ -240,9 +241,9 @@ static void qdss_write_complete(struct usb_ep *ep, spin_lock_irqsave(&qdss->lock, flags); if (!qdss->debug_inface_enabled) - list_del(&req->list); - list_add_tail(&req->list, list_pool); - complete(&d_req->write_done); + list_del(&qreq->list); + list_add_tail(&qreq->list, list_pool); + complete(&qreq->write_done); if (req->length != 0) { d_req->actual = req->actual; d_req->status = req->status; @@ -256,8 +257,8 @@ static void qdss_write_complete(struct usb_ep *ep, void usb_qdss_free_req(struct usb_qdss_ch *ch) { struct f_qdss *qdss; - struct usb_request *req; struct list_head *act, *tmp; + struct qdss_req *qreq; pr_debug("%s\n", __func__); @@ -268,15 +269,19 @@ void usb_qdss_free_req(struct usb_qdss_ch *ch) } list_for_each_safe(act, tmp, &qdss->data_write_pool) { - req = list_entry(act, struct usb_request, list); - list_del(&req->list); - usb_ep_free_request(qdss->port.data, req); + qreq = list_entry(act, struct qdss_req, list); + list_del(&qreq->list); + usb_ep_free_request(qdss->port.data, qreq->usb_req); + kfree(qreq); + } list_for_each_safe(act, tmp, &qdss->ctrl_write_pool) { - req = list_entry(act, struct usb_request, list); - list_del(&req->list); - usb_ep_free_request(qdss->port.ctrl_in, req); + qreq = list_entry(act, struct qdss_req, list); + list_del(&qreq->list); + usb_ep_free_request(qdss->port.ctrl_in, qreq->usb_req); + kfree(qreq); + } } EXPORT_SYMBOL(usb_qdss_free_req); @@ -288,6 +293,7 @@ int usb_qdss_alloc_req(struct usb_qdss_ch *ch, int no_write_buf) struct usb_ep *in; struct list_head *list_pool; int i; + struct qdss_req *qreq; pr_debug("%s\n", __func__); @@ -311,13 +317,17 @@ 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); req = usb_ep_alloc_request(in, GFP_ATOMIC); if (!req) { pr_err("%s: ctrl_in allocation err\n", __func__); goto fail; } + qreq->usb_req = req; + req->context = qreq; req->complete = qdss_write_complete; - list_add_tail(&req->list, list_pool); + list_add_tail(&qreq->list, list_pool); + init_completion(&qreq->write_done); } return 0; @@ -784,6 +794,7 @@ int usb_qdss_ctrl_write(struct usb_qdss_ch *ch, struct qdss_request *d_req) struct f_qdss *qdss = ch->priv_usb; unsigned long flags; struct usb_request *req = NULL; + struct qdss_req *qreq; pr_debug("%s\n", __func__); @@ -803,17 +814,18 @@ int usb_qdss_ctrl_write(struct usb_qdss_ch *ch, struct qdss_request *d_req) return -EAGAIN; } - req = list_first_entry(&qdss->ctrl_write_pool, struct usb_request, + qreq = list_first_entry(&qdss->ctrl_write_pool, struct qdss_req, list); - list_del(&req->list); + list_del(&qreq->list); spin_unlock_irqrestore(&qdss->lock, flags); + qreq->qdss_req = d_req; + req = qreq->usb_req; req->buf = d_req->buf; req->length = d_req->length; - req->context = d_req; if (usb_ep_queue(qdss->port.ctrl_in, req, GFP_ATOMIC)) { spin_lock_irqsave(&qdss->lock, flags); - list_add_tail(&req->list, &qdss->ctrl_write_pool); + list_add_tail(&qreq->list, &qdss->ctrl_write_pool); spin_unlock_irqrestore(&qdss->lock, flags); pr_err("%s usb_ep_queue failed\n", __func__); return -EIO; @@ -828,6 +840,7 @@ int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req) struct f_qdss *qdss = ch->priv_usb; unsigned long flags; struct usb_request *req = NULL; + struct qdss_req *qreq; pr_debug("usb_qdss_data_write\n"); @@ -847,23 +860,24 @@ int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req) return -EAGAIN; } - req = list_first_entry(&qdss->data_write_pool, struct usb_request, + qreq = list_first_entry(&qdss->data_write_pool, struct qdss_req, list); - list_move_tail(&req->list, &qdss->queued_data_pool); + list_move_tail(&qreq->list, &qdss->queued_data_pool); spin_unlock_irqrestore(&qdss->lock, flags); + qreq->qdss_req = d_req; + req = qreq->usb_req; req->buf = d_req->buf; req->length = d_req->length; - req->context = d_req; req->sg = d_req->sg; req->num_sgs = d_req->num_sgs; req->num_mapped_sgs = d_req->num_mapped_sgs; - reinit_completion(&d_req->write_done); + reinit_completion(&qreq->write_done); if (usb_ep_queue(qdss->port.data, req, GFP_ATOMIC)) { spin_lock_irqsave(&qdss->lock, flags); /* Remove from queued pool and add back to data pool */ - list_move_tail(&req->list, &qdss->data_write_pool); - complete(&d_req->write_done); + list_move_tail(&qreq->list, &qdss->data_write_pool); + complete(&qreq->write_done); spin_unlock_irqrestore(&qdss->lock, flags); pr_err("qdss usb_ep_queue failed\n"); return -EIO; @@ -927,8 +941,7 @@ void usb_qdss_close(struct usb_qdss_ch *ch) struct usb_gadget *gadget; unsigned long flags; int status; - struct usb_request *req; - struct qdss_request *d_req; + struct qdss_req *qreq; pr_debug("%s\n", __func__); @@ -943,12 +956,11 @@ void usb_qdss_close(struct usb_qdss_ch *ch) qdss = ch->priv_usb; qdss->qdss_close = true; while (!list_empty(&qdss->queued_data_pool)) { - req = list_first_entry(&qdss->queued_data_pool, - struct usb_request, list); - d_req = req->context; + qreq = list_first_entry(&qdss->queued_data_pool, + struct qdss_req, list); spin_unlock_irqrestore(&qdss_lock, flags); - usb_ep_dequeue(qdss->port.data, req); - wait_for_completion(&d_req->write_done); + usb_ep_dequeue(qdss->port.data, qreq->usb_req); + wait_for_completion(&qreq->write_done); spin_lock_irqsave(&qdss_lock, flags); } diff --git a/include/linux/usb/usb_qdss.h b/include/linux/usb/usb_qdss.h index 9cdb005cfca7..c5f22c5f4abc 100644 --- a/include/linux/usb/usb_qdss.h +++ b/include/linux/usb/usb_qdss.h @@ -22,7 +22,6 @@ struct qdss_request { struct scatterlist *sg; unsigned int num_sgs; unsigned int num_mapped_sgs; - struct completion write_done; }; struct usb_qdss_ch { @@ -43,6 +42,13 @@ enum qdss_state { USB_QDSS_CTRL_WRITE_DONE, }; +struct qdss_req { + struct usb_request *usb_req; + struct completion write_done; + struct qdss_request *qdss_req; + struct list_head list; +}; + #if IS_ENABLED(CONFIG_USB_F_QDSS) struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv, void (*notify)(void *priv, unsigned int event,