Commit b58e6cee authored by Mian Yousaf Kaukab's avatar Mian Yousaf Kaukab Committed by Felipe Balbi

usb: dwc2: host: allocate qh before atomic enqueue

To avoid sleep while atomic bugs, allocate qh before calling
dwc2_hcd_urb_enqueue. qh pointer can be used directly now instead of
passing ep->hcpriv as double pointer.
Acked-by: default avatarJohn Youn <johnyoun@synopsys.com>
Tested-by: default avatarHeiko Stuebner <heiko@sntech.de>
Tested-by: default avatarDoug Anderson <dianders@chromium.org>
Signed-off-by: default avatarMian Yousaf Kaukab <yousaf.kaukab@intel.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent be9d3988
...@@ -359,7 +359,7 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg) ...@@ -359,7 +359,7 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg)
/* Caller must hold driver lock */ /* Caller must hold driver lock */
static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
struct dwc2_hcd_urb *urb, void **ep_handle, struct dwc2_hcd_urb *urb, struct dwc2_qh *qh,
gfp_t mem_flags) gfp_t mem_flags)
{ {
struct dwc2_qtd *qtd; struct dwc2_qtd *qtd;
...@@ -391,8 +391,7 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, ...@@ -391,8 +391,7 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
return -ENOMEM; return -ENOMEM;
dwc2_hcd_qtd_init(qtd, urb); dwc2_hcd_qtd_init(qtd, urb);
retval = dwc2_hcd_qtd_add(hsotg, qtd, (struct dwc2_qh **)ep_handle, retval = dwc2_hcd_qtd_add(hsotg, qtd, qh);
mem_flags);
if (retval) { if (retval) {
dev_err(hsotg->dev, dev_err(hsotg->dev,
"DWC OTG HCD URB Enqueue failed adding QTD. Error status %d\n", "DWC OTG HCD URB Enqueue failed adding QTD. Error status %d\n",
...@@ -2445,6 +2444,8 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, ...@@ -2445,6 +2444,8 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
u32 tflags = 0; u32 tflags = 0;
void *buf; void *buf;
unsigned long flags; unsigned long flags;
struct dwc2_qh *qh;
bool qh_allocated = false;
if (dbg_urb(urb)) { if (dbg_urb(urb)) {
dev_vdbg(hsotg->dev, "DWC OTG HCD URB Enqueue\n"); dev_vdbg(hsotg->dev, "DWC OTG HCD URB Enqueue\n");
...@@ -2523,13 +2524,24 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, ...@@ -2523,13 +2524,24 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
urb->iso_frame_desc[i].length); urb->iso_frame_desc[i].length);
urb->hcpriv = dwc2_urb; urb->hcpriv = dwc2_urb;
qh = (struct dwc2_qh *) ep->hcpriv;
/* Create QH for the endpoint if it doesn't exist */
if (!qh) {
qh = dwc2_hcd_qh_create(hsotg, dwc2_urb, mem_flags);
if (!qh) {
retval = -ENOMEM;
goto fail0;
}
ep->hcpriv = qh;
qh_allocated = true;
}
spin_lock_irqsave(&hsotg->lock, flags); spin_lock_irqsave(&hsotg->lock, flags);
retval = usb_hcd_link_urb_to_ep(hcd, urb); retval = usb_hcd_link_urb_to_ep(hcd, urb);
if (retval) if (retval)
goto fail1; goto fail1;
retval = dwc2_hcd_urb_enqueue(hsotg, dwc2_urb, &ep->hcpriv, mem_flags); retval = dwc2_hcd_urb_enqueue(hsotg, dwc2_urb, qh, mem_flags);
if (retval) if (retval)
goto fail2; goto fail2;
...@@ -2549,6 +2561,17 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, ...@@ -2549,6 +2561,17 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
fail1: fail1:
spin_unlock_irqrestore(&hsotg->lock, flags); spin_unlock_irqrestore(&hsotg->lock, flags);
urb->hcpriv = NULL; urb->hcpriv = NULL;
if (qh_allocated) {
struct dwc2_qtd *qtd2, *qtd2_tmp;
ep->hcpriv = NULL;
dwc2_hcd_qh_unlink(hsotg, qh);
/* Free each QTD in the QH's QTD list */
list_for_each_entry_safe(qtd2, qtd2_tmp, &qh->qtd_list,
qtd_list_entry)
dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh);
dwc2_hcd_qh_free(hsotg, qh);
}
fail0: fail0:
kfree(dwc2_urb); kfree(dwc2_urb);
......
...@@ -463,6 +463,9 @@ extern void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg, ...@@ -463,6 +463,9 @@ extern void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
/* Schedule Queue Functions */ /* Schedule Queue Functions */
/* Implemented in hcd_queue.c */ /* Implemented in hcd_queue.c */
extern void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg); extern void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg);
extern struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
struct dwc2_hcd_urb *urb,
gfp_t mem_flags);
extern void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh); extern void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
extern int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh); extern int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
extern void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh); extern void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
...@@ -471,7 +474,7 @@ extern void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, ...@@ -471,7 +474,7 @@ extern void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
extern void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb); extern void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb);
extern int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, extern int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
struct dwc2_qh **qh, gfp_t mem_flags); struct dwc2_qh *qh);
/* Unlinks and frees a QTD */ /* Unlinks and frees a QTD */
static inline void dwc2_hcd_qtd_unlink_and_free(struct dwc2_hsotg *hsotg, static inline void dwc2_hcd_qtd_unlink_and_free(struct dwc2_hsotg *hsotg,
......
...@@ -191,7 +191,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, ...@@ -191,7 +191,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
* *
* Return: Pointer to the newly allocated QH, or NULL on error * Return: Pointer to the newly allocated QH, or NULL on error
*/ */
static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg, struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
struct dwc2_hcd_urb *urb, struct dwc2_hcd_urb *urb,
gfp_t mem_flags) gfp_t mem_flags)
{ {
...@@ -767,57 +767,32 @@ void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb) ...@@ -767,57 +767,32 @@ void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb)
* *
* @hsotg: The DWC HCD structure * @hsotg: The DWC HCD structure
* @qtd: The QTD to add * @qtd: The QTD to add
* @qh: Out parameter to return queue head * @qh: Queue head to add qtd to
* @atomic_alloc: Flag to do atomic alloc if needed
* *
* Return: 0 if successful, negative error code otherwise * Return: 0 if successful, negative error code otherwise
* *
* Finds the correct QH to place the QTD into. If it does not find a QH, it * If the QH to which the QTD is added is not currently scheduled, it is placed
* will create a new QH. If the QH to which the QTD is added is not currently * into the proper schedule based on its EP type.
* scheduled, it is placed into the proper schedule based on its EP type.
*/ */
int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
struct dwc2_qh **qh, gfp_t mem_flags) struct dwc2_qh *qh)
{ {
struct dwc2_hcd_urb *urb = qtd->urb;
int allocated = 0;
int retval; int retval;
/* if (unlikely(!qh)) {
* Get the QH which holds the QTD-list to insert to. Create QH if it dev_err(hsotg->dev, "%s: Invalid QH\n", __func__);
* doesn't exist. retval = -EINVAL;
*/ goto fail;
if (*qh == NULL) {
*qh = dwc2_hcd_qh_create(hsotg, urb, mem_flags);
if (*qh == NULL)
return -ENOMEM;
allocated = 1;
} }
retval = dwc2_hcd_qh_add(hsotg, *qh); retval = dwc2_hcd_qh_add(hsotg, qh);
if (retval) if (retval)
goto fail; goto fail;
qtd->qh = *qh; qtd->qh = qh;
list_add_tail(&qtd->qtd_list_entry, &(*qh)->qtd_list); list_add_tail(&qtd->qtd_list_entry, &qh->qtd_list);
return 0; return 0;
fail: fail:
if (allocated) {
struct dwc2_qtd *qtd2, *qtd2_tmp;
struct dwc2_qh *qh_tmp = *qh;
*qh = NULL;
dwc2_hcd_qh_unlink(hsotg, qh_tmp);
/* Free each QTD in the QH's QTD list */
list_for_each_entry_safe(qtd2, qtd2_tmp, &qh_tmp->qtd_list,
qtd_list_entry)
dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh_tmp);
dwc2_hcd_qh_free(hsotg, qh_tmp);
}
return retval; return retval;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment