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

Commit 9439eb94 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman
Browse files

USB: update spinlock usage for root-hub URBs



This patch (as952) adjusts the spinlock usage in the root-hub
emulation part of usbcore, to make it match more closely the pattern
used by regular host controller drivers.  To wit: The private lock
(usb_hcd_root_hub_lock) is held throughout the important parts, and it
is dropped temporarily without re-enabling interrupts around the call
to usb_hcd_giveback_urb().

A nice side effect is that the code now avoids calling
local_irq_save(), thereby becoming more RT-friendly.

Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent d617bc83
Loading
Loading
Loading
Loading
+29 −23
Original line number Diff line number Diff line
@@ -356,10 +356,11 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
	const u8	*bufp = tbuf;
	int		len = 0;
	int		patch_wakeup = 0;
	unsigned long	flags;
	int		status = 0;
	int		n;

	might_sleep();

	cmd = (struct usb_ctrlrequest *) urb->setup_packet;
	typeReq  = (cmd->bRequestType << 8) | cmd->bRequest;
	wValue   = le16_to_cpu (cmd->wValue);
@@ -523,13 +524,21 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
	}

	/* any errors get returned through the urb completion */
	local_irq_save (flags);
	spin_lock_irq(&hcd_root_hub_lock);
	spin_lock(&urb->lock);
	if (urb->status == -EINPROGRESS)
		urb->status = status;
	spin_unlock(&urb->lock);

	/* This peculiar use of spinlocks echoes what real HC drivers do.
	 * Avoiding calls to local_irq_disable/enable makes the code
	 * RT-friendly.
	 */
	spin_unlock(&hcd_root_hub_lock);
	usb_hcd_giveback_urb(hcd, urb);
	local_irq_restore (flags);
	spin_lock(&hcd_root_hub_lock);

	spin_unlock_irq(&hcd_root_hub_lock);
	return 0;
}

@@ -559,8 +568,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
	if (length > 0) {

		/* try to complete the status urb */
		local_irq_save (flags);
		spin_lock(&hcd_root_hub_lock);
		spin_lock_irqsave(&hcd_root_hub_lock, flags);
		urb = hcd->status_urb;
		if (urb) {
			spin_lock(&urb->lock);
@@ -574,16 +582,16 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
			} else		/* urb has been unlinked */
				length = 0;
			spin_unlock(&urb->lock);

			spin_unlock(&hcd_root_hub_lock);
			usb_hcd_giveback_urb(hcd, urb);
			spin_lock(&hcd_root_hub_lock);
		} else
			length = 0;
		spin_unlock(&hcd_root_hub_lock);

		/* local irqs are always blocked in completions */
		if (length > 0)
			usb_hcd_giveback_urb (hcd, urb);
		else
		if (length <= 0)
			hcd->poll_pending = 1;
		local_irq_restore (flags);
		spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
	}

	/* The USB 2.0 spec says 256 ms.  This is close enough and won't
@@ -651,25 +659,23 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
{
	unsigned long	flags;

	spin_lock_irqsave(&hcd_root_hub_lock, flags);
	if (usb_endpoint_num(&urb->ep->desc) == 0) {	/* Control URB */
		;	/* Do nothing */

	} else {				/* Status URB */
		if (!hcd->uses_new_polling)
			del_timer (&hcd->rh_timer);
		local_irq_save (flags);
		spin_lock (&hcd_root_hub_lock);
		if (urb == hcd->status_urb) {
			hcd->status_urb = NULL;
			urb->hcpriv = NULL;
		} else
			urb = NULL;		/* wasn't fully queued */

			spin_unlock(&hcd_root_hub_lock);
		if (urb)
			usb_hcd_giveback_urb(hcd, urb);
		local_irq_restore (flags);
			spin_lock(&hcd_root_hub_lock);
		}

	}
	spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
	return 0;
}