mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-04 01:24:12 +08:00
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: John Youn <johnyoun@synopsys.com> Tested-by: Heiko Stuebner <heiko@sntech.de> Tested-by: Doug Anderson <dianders@chromium.org> Signed-off-by: Mian Yousaf Kaukab <yousaf.kaukab@intel.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
parent
be9d39881f
commit
b58e6ceef9
@ -359,7 +359,7 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg)
|
||||
|
||||
/* Caller must hold driver lock */
|
||||
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)
|
||||
{
|
||||
struct dwc2_qtd *qtd;
|
||||
@ -391,8 +391,7 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
|
||||
return -ENOMEM;
|
||||
|
||||
dwc2_hcd_qtd_init(qtd, urb);
|
||||
retval = dwc2_hcd_qtd_add(hsotg, qtd, (struct dwc2_qh **)ep_handle,
|
||||
mem_flags);
|
||||
retval = dwc2_hcd_qtd_add(hsotg, qtd, qh);
|
||||
if (retval) {
|
||||
dev_err(hsotg->dev,
|
||||
"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,
|
||||
u32 tflags = 0;
|
||||
void *buf;
|
||||
unsigned long flags;
|
||||
struct dwc2_qh *qh;
|
||||
bool qh_allocated = false;
|
||||
|
||||
if (dbg_urb(urb)) {
|
||||
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,
|
||||
urb->iso_frame_desc[i].length);
|
||||
|
||||
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);
|
||||
retval = usb_hcd_link_urb_to_ep(hcd, urb);
|
||||
if (retval)
|
||||
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)
|
||||
goto fail2;
|
||||
|
||||
@ -2549,6 +2561,17 @@ fail2:
|
||||
fail1:
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
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:
|
||||
kfree(dwc2_urb);
|
||||
|
||||
|
@ -463,6 +463,9 @@ extern void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
|
||||
/* Schedule Queue Functions */
|
||||
/* Implemented in hcd_queue.c */
|
||||
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 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);
|
||||
@ -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 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 */
|
||||
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,
|
||||
*
|
||||
* 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,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
@ -767,57 +767,32 @@ void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb)
|
||||
*
|
||||
* @hsotg: The DWC HCD structure
|
||||
* @qtd: The QTD to add
|
||||
* @qh: Out parameter to return queue head
|
||||
* @atomic_alloc: Flag to do atomic alloc if needed
|
||||
* @qh: Queue head to add qtd to
|
||||
*
|
||||
* 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
|
||||
* will create a new QH. If the QH to which the QTD is added is not currently
|
||||
* scheduled, it is placed into the proper schedule based on its EP type.
|
||||
* If the QH to which the QTD is added is not currently 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,
|
||||
struct dwc2_qh **qh, gfp_t mem_flags)
|
||||
struct dwc2_qh *qh)
|
||||
{
|
||||
struct dwc2_hcd_urb *urb = qtd->urb;
|
||||
int allocated = 0;
|
||||
int retval;
|
||||
|
||||
/*
|
||||
* Get the QH which holds the QTD-list to insert to. Create QH if it
|
||||
* doesn't exist.
|
||||
*/
|
||||
if (*qh == NULL) {
|
||||
*qh = dwc2_hcd_qh_create(hsotg, urb, mem_flags);
|
||||
if (*qh == NULL)
|
||||
return -ENOMEM;
|
||||
allocated = 1;
|
||||
if (unlikely(!qh)) {
|
||||
dev_err(hsotg->dev, "%s: Invalid QH\n", __func__);
|
||||
retval = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
retval = dwc2_hcd_qh_add(hsotg, *qh);
|
||||
retval = dwc2_hcd_qh_add(hsotg, qh);
|
||||
if (retval)
|
||||
goto fail;
|
||||
|
||||
qtd->qh = *qh;
|
||||
list_add_tail(&qtd->qtd_list_entry, &(*qh)->qtd_list);
|
||||
qtd->qh = qh;
|
||||
list_add_tail(&qtd->qtd_list_entry, &qh->qtd_list);
|
||||
|
||||
return 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user