Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: isp1760: urb_dequeue doesn't always find the urbs

The option driver (and presumably others) allocates several URBs when it
opens and tries to free them when it closes. The isp1760_urb_dequeue
function gets called, but the packet being dequeued is not necessarily at
the
front of one of the 32 queues. If not, the isp1760_urb_done function doesn't
get called for the URB and the process trying to free it hangs forever on a
wait_queue. This patch does two things. If the URB being dequeued has others
queued behind it, it re-queues them. And it searches the queues looking for
the URB being dequeued rather than just looking at the one at the front of
the queue.

[bigeasy@linutronix] whitespace fixes, reformating

Cc: stable <stable@kernel.org>
Signed-off-by: Warren Free <wfree@ipmn.com>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Warren Free and committed by
Greg Kroah-Hartman
0afb20e0 cab98a0a

+22 -2
+22 -2
drivers/usb/host/isp1760-hcd.c
··· 1658 1658 u32 reg_base, or_reg, skip_reg; 1659 1659 unsigned long flags; 1660 1660 struct ptd ptd; 1661 + packet_enqueue *pe; 1661 1662 1662 1663 switch (usb_pipetype(urb->pipe)) { 1663 1664 case PIPE_ISOCHRONOUS: ··· 1670 1669 reg_base = INT_REGS_OFFSET; 1671 1670 or_reg = HC_INT_IRQ_MASK_OR_REG; 1672 1671 skip_reg = HC_INT_PTD_SKIPMAP_REG; 1672 + pe = enqueue_an_INT_packet; 1673 1673 break; 1674 1674 1675 1675 default: ··· 1678 1676 reg_base = ATL_REGS_OFFSET; 1679 1677 or_reg = HC_ATL_IRQ_MASK_OR_REG; 1680 1678 skip_reg = HC_ATL_PTD_SKIPMAP_REG; 1679 + pe = enqueue_an_ATL_packet; 1681 1680 break; 1682 1681 } 1683 1682 ··· 1690 1687 u32 skip_map; 1691 1688 u32 or_map; 1692 1689 struct isp1760_qtd *qtd; 1690 + struct isp1760_qh *qh = ints->qh; 1693 1691 1694 1692 skip_map = isp1760_readl(hcd->regs + skip_reg); 1695 1693 skip_map |= 1 << i; ··· 1703 1699 priv_write_copy(priv, (u32 *)&ptd, hcd->regs + reg_base 1704 1700 + i * sizeof(ptd), sizeof(ptd)); 1705 1701 qtd = ints->qtd; 1706 - 1707 - clean_up_qtdlist(qtd); 1702 + qtd = clean_up_qtdlist(qtd); 1708 1703 1709 1704 free_mem(priv, ints->payload); 1710 1705 ··· 1714 1711 ints->payload = 0; 1715 1712 1716 1713 isp1760_urb_done(priv, urb, status); 1714 + if (qtd) 1715 + pe(hcd, qh, qtd); 1717 1716 break; 1717 + 1718 + } else if (ints->qtd) { 1719 + struct isp1760_qtd *qtd, *prev_qtd = ints->qtd; 1720 + 1721 + for (qtd = ints->qtd->hw_next; qtd; qtd = qtd->hw_next) { 1722 + if (qtd->urb == urb) { 1723 + prev_qtd->hw_next = clean_up_qtdlist(qtd); 1724 + isp1760_urb_done(priv, urb, status); 1725 + break; 1726 + } 1727 + prev_qtd = qtd; 1728 + } 1729 + /* we found the urb before the end of the list */ 1730 + if (qtd) 1731 + break; 1718 1732 } 1719 1733 ints++; 1720 1734 }