aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core/hcd.c
diff options
context:
space:
mode:
authorMing Lei <ming.lei@canonical.com>2013-07-03 10:53:07 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-08-12 14:43:48 -0400
commit94dfd7edfd5c9b605caf7b562de7a813d216e011 (patch)
tree380cbf8b4a19b91084cd2c01a3cd8d2fa4e2dca4 /drivers/usb/core/hcd.c
parent5b146f7e016a8727a98b3d48e4f4e128d3624cd5 (diff)
USB: HCD: support giveback of URB in tasklet context
This patch implements the mechanism of giveback of URB in tasklet context, so that hardware interrupt handling time for usb host controller can be saved much, and HCD interrupt handling can be simplified. Motivations: 1), on some arch(such as ARM), DMA mapping/unmapping is a bit time-consuming, for example: when accessing usb mass storage via EHCI on pandaboard, the common length of transfer buffer is 120KB, the time consumed on DMA unmapping may reach hundreds of microseconds; even on A15 based box, the time is still about scores of microseconds 2), on some arch, reading DMA coherent memoery is very time-consuming, the most common example is usb video class driver[1] 3), driver's complete() callback may do much things which is driver specific, so the time is consumed unnecessarily in hardware irq context. 4), running driver's complete() callback in hardware irq context causes that host controller driver has to release its lock in interrupt handler, so reacquiring the lock after return may busy wait a while and increase interrupt handling time. More seriously, releasing the HCD lock makes HCD becoming quite complicated to deal with introduced races. So the patch proposes to run giveback of URB in tasklet context, then time consumed in HCD irq handling doesn't depend on drivers' complete and DMA mapping/unmapping any more, also we can simplify HCD since the HCD lock isn't needed to be released during irq handling. The patch should be reasonable and doable: 1), for drivers, they don't care if the complete() is called in hard irq context or softirq context 2), the biggest change is the situation in which usb_submit_urb() is called in complete() callback, so the introduced tasklet schedule delay might be a con, but it shouldn't be a big deal: - control/bulk asynchronous transfer isn't sensitive to schedule delay - the patch schedules giveback of periodic URBs using tasklet_hi_schedule, so the introduced delay should be very small - for ISOC transfer, generally, drivers submit several URBs concurrently to avoid interrupt delay, so it is OK with the little schedule delay. - for interrupt transfer, generally, drivers only submit one URB at the same time, but interrupt transfer is often used in event report, polling, ... situations, and a little delay should be OK. Considered that HCDs may optimize on submitting URB in complete(), the patch may cause the optimization not working, so introduces one flag to mark if the HCD supports to run giveback URB in tasklet context. When all HCDs are ready, the flag can be removed. [1], http://marc.info/?t=136438111600010&r=1&w=2 Cc: Oliver Neukum <oliver@neukum.org> Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Ming Lei <ming.lei@canonical.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/core/hcd.c')
-rw-r--r--drivers/usb/core/hcd.c147
1 files changed, 117 insertions, 30 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index dc1346fee76c..0ffe016f4036 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -696,15 +696,7 @@ error:
696 /* any errors get returned through the urb completion */ 696 /* any errors get returned through the urb completion */
697 spin_lock_irq(&hcd_root_hub_lock); 697 spin_lock_irq(&hcd_root_hub_lock);
698 usb_hcd_unlink_urb_from_ep(hcd, urb); 698 usb_hcd_unlink_urb_from_ep(hcd, urb);
699
700 /* This peculiar use of spinlocks echoes what real HC drivers do.
701 * Avoiding calls to local_irq_disable/enable makes the code
702 * RT-friendly.
703 */
704 spin_unlock(&hcd_root_hub_lock);
705 usb_hcd_giveback_urb(hcd, urb, status); 699 usb_hcd_giveback_urb(hcd, urb, status);
706 spin_lock(&hcd_root_hub_lock);
707
708 spin_unlock_irq(&hcd_root_hub_lock); 700 spin_unlock_irq(&hcd_root_hub_lock);
709 return 0; 701 return 0;
710} 702}
@@ -744,9 +736,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
744 memcpy(urb->transfer_buffer, buffer, length); 736 memcpy(urb->transfer_buffer, buffer, length);
745 737
746 usb_hcd_unlink_urb_from_ep(hcd, urb); 738 usb_hcd_unlink_urb_from_ep(hcd, urb);
747 spin_unlock(&hcd_root_hub_lock);
748 usb_hcd_giveback_urb(hcd, urb, 0); 739 usb_hcd_giveback_urb(hcd, urb, 0);
749 spin_lock(&hcd_root_hub_lock);
750 } else { 740 } else {
751 length = 0; 741 length = 0;
752 set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags); 742 set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
@@ -836,10 +826,7 @@ static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
836 if (urb == hcd->status_urb) { 826 if (urb == hcd->status_urb) {
837 hcd->status_urb = NULL; 827 hcd->status_urb = NULL;
838 usb_hcd_unlink_urb_from_ep(hcd, urb); 828 usb_hcd_unlink_urb_from_ep(hcd, urb);
839
840 spin_unlock(&hcd_root_hub_lock);
841 usb_hcd_giveback_urb(hcd, urb, status); 829 usb_hcd_giveback_urb(hcd, urb, status);
842 spin_lock(&hcd_root_hub_lock);
843 } 830 }
844 } 831 }
845 done: 832 done:
@@ -1656,6 +1643,72 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
1656 1643
1657/*-------------------------------------------------------------------------*/ 1644/*-------------------------------------------------------------------------*/
1658 1645
1646static void __usb_hcd_giveback_urb(struct urb *urb)
1647{
1648 struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
1649 int status = urb->unlinked;
1650 unsigned long flags;
1651
1652 urb->hcpriv = NULL;
1653 if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
1654 urb->actual_length < urb->transfer_buffer_length &&
1655 !status))
1656 status = -EREMOTEIO;
1657
1658 unmap_urb_for_dma(hcd, urb);
1659 usbmon_urb_complete(&hcd->self, urb, status);
1660 usb_unanchor_urb(urb);
1661
1662 /* pass ownership to the completion handler */
1663 urb->status = status;
1664
1665 /*
1666 * We disable local IRQs here avoid possible deadlock because
1667 * drivers may call spin_lock() to hold lock which might be
1668 * acquired in one hard interrupt handler.
1669 *
1670 * The local_irq_save()/local_irq_restore() around complete()
1671 * will be removed if current USB drivers have been cleaned up
1672 * and no one may trigger the above deadlock situation when
1673 * running complete() in tasklet.
1674 */
1675 local_irq_save(flags);
1676 urb->complete(urb);
1677 local_irq_restore(flags);
1678
1679 atomic_dec(&urb->use_count);
1680 if (unlikely(atomic_read(&urb->reject)))
1681 wake_up(&usb_kill_urb_queue);
1682 usb_put_urb(urb);
1683}
1684
1685static void usb_giveback_urb_bh(unsigned long param)
1686{
1687 struct giveback_urb_bh *bh = (struct giveback_urb_bh *)param;
1688 struct list_head local_list;
1689
1690 spin_lock_irq(&bh->lock);
1691 bh->running = true;
1692 restart:
1693 list_replace_init(&bh->head, &local_list);
1694 spin_unlock_irq(&bh->lock);
1695
1696 while (!list_empty(&local_list)) {
1697 struct urb *urb;
1698
1699 urb = list_entry(local_list.next, struct urb, urb_list);
1700 list_del_init(&urb->urb_list);
1701 __usb_hcd_giveback_urb(urb);
1702 }
1703
1704 /* check if there are new URBs to giveback */
1705 spin_lock_irq(&bh->lock);
1706 if (!list_empty(&bh->head))
1707 goto restart;
1708 bh->running = false;
1709 spin_unlock_irq(&bh->lock);
1710}
1711
1659/** 1712/**
1660 * usb_hcd_giveback_urb - return URB from HCD to device driver 1713 * usb_hcd_giveback_urb - return URB from HCD to device driver
1661 * @hcd: host controller returning the URB 1714 * @hcd: host controller returning the URB
@@ -1675,25 +1728,37 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
1675 */ 1728 */
1676void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status) 1729void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
1677{ 1730{
1678 urb->hcpriv = NULL; 1731 struct giveback_urb_bh *bh;
1679 if (unlikely(urb->unlinked)) 1732 bool running, high_prio_bh;
1680 status = urb->unlinked;
1681 else if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
1682 urb->actual_length < urb->transfer_buffer_length &&
1683 !status))
1684 status = -EREMOTEIO;
1685 1733
1686 unmap_urb_for_dma(hcd, urb); 1734 /* pass status to tasklet via unlinked */
1687 usbmon_urb_complete(&hcd->self, urb, status); 1735 if (likely(!urb->unlinked))
1688 usb_unanchor_urb(urb); 1736 urb->unlinked = status;
1689 1737
1690 /* pass ownership to the completion handler */ 1738 if (!hcd_giveback_urb_in_bh(hcd) && !is_root_hub(urb->dev)) {
1691 urb->status = status; 1739 __usb_hcd_giveback_urb(urb);
1692 urb->complete (urb); 1740 return;
1693 atomic_dec (&urb->use_count); 1741 }
1694 if (unlikely(atomic_read(&urb->reject))) 1742
1695 wake_up (&usb_kill_urb_queue); 1743 if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)) {
1696 usb_put_urb (urb); 1744 bh = &hcd->high_prio_bh;
1745 high_prio_bh = true;
1746 } else {
1747 bh = &hcd->low_prio_bh;
1748 high_prio_bh = false;
1749 }
1750
1751 spin_lock(&bh->lock);
1752 list_add_tail(&urb->urb_list, &bh->head);
1753 running = bh->running;
1754 spin_unlock(&bh->lock);
1755
1756 if (running)
1757 ;
1758 else if (high_prio_bh)
1759 tasklet_hi_schedule(&bh->bh);
1760 else
1761 tasklet_schedule(&bh->bh);
1697} 1762}
1698EXPORT_SYMBOL_GPL(usb_hcd_giveback_urb); 1763EXPORT_SYMBOL_GPL(usb_hcd_giveback_urb);
1699 1764
@@ -2322,6 +2387,14 @@ EXPORT_SYMBOL_GPL (usb_hc_died);
2322 2387
2323/*-------------------------------------------------------------------------*/ 2388/*-------------------------------------------------------------------------*/
2324 2389
2390static void init_giveback_urb_bh(struct giveback_urb_bh *bh)
2391{
2392
2393 spin_lock_init(&bh->lock);
2394 INIT_LIST_HEAD(&bh->head);
2395 tasklet_init(&bh->bh, usb_giveback_urb_bh, (unsigned long)bh);
2396}
2397
2325/** 2398/**
2326 * usb_create_shared_hcd - create and initialize an HCD structure 2399 * usb_create_shared_hcd - create and initialize an HCD structure
2327 * @driver: HC driver that will use this hcd 2400 * @driver: HC driver that will use this hcd
@@ -2590,6 +2663,10 @@ int usb_add_hcd(struct usb_hcd *hcd,
2590 && device_can_wakeup(&hcd->self.root_hub->dev)) 2663 && device_can_wakeup(&hcd->self.root_hub->dev))
2591 dev_dbg(hcd->self.controller, "supports USB remote wakeup\n"); 2664 dev_dbg(hcd->self.controller, "supports USB remote wakeup\n");
2592 2665
2666 /* initialize tasklets */
2667 init_giveback_urb_bh(&hcd->high_prio_bh);
2668 init_giveback_urb_bh(&hcd->low_prio_bh);
2669
2593 /* enable irqs just before we start the controller, 2670 /* enable irqs just before we start the controller,
2594 * if the BIOS provides legacy PCI irqs. 2671 * if the BIOS provides legacy PCI irqs.
2595 */ 2672 */
@@ -2698,6 +2775,16 @@ void usb_remove_hcd(struct usb_hcd *hcd)
2698 usb_disconnect(&rhdev); /* Sets rhdev to NULL */ 2775 usb_disconnect(&rhdev); /* Sets rhdev to NULL */
2699 mutex_unlock(&usb_bus_list_lock); 2776 mutex_unlock(&usb_bus_list_lock);
2700 2777
2778 /*
2779 * tasklet_kill() isn't needed here because:
2780 * - driver's disconnect() called from usb_disconnect() should
2781 * make sure its URBs are completed during the disconnect()
2782 * callback
2783 *
2784 * - it is too late to run complete() here since driver may have
2785 * been removed already now
2786 */
2787
2701 /* Prevent any more root-hub status calls from the timer. 2788 /* Prevent any more root-hub status calls from the timer.
2702 * The HCD might still restart the timer (if a port status change 2789 * The HCD might still restart the timer (if a port status change
2703 * interrupt occurs), but usb_hcd_poll_rh_status() won't invoke 2790 * interrupt occurs), but usb_hcd_poll_rh_status() won't invoke