Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+27 −4
Original line number Diff line number Diff line
@@ -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 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
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);

+4 −1
Original line number Diff line number Diff line
@@ -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,
+12 −37
Original line number Diff line number Diff line
@@ -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;
}