]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
usb: fix number of mapped SG DMA entries
authorClemens Ladisch <clemens@ladisch.de>
Sat, 3 Dec 2011 22:41:31 +0000 (23:41 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 12 Jan 2012 19:35:25 +0000 (11:35 -0800)
commit bc677d5b64644c399cd3db6a905453e611f402ab upstream.

Add a new field num_mapped_sgs to struct urb so that we have a place to
store the number of mapped entries and can also retain the original
value of entries in num_sgs.  Previously, usb_hcd_map_urb_for_dma()
would overwrite this with the number of mapped entries, which would
break dma_unmap_sg() because it requires the original number of entries.

This fixes warnings like the following when using USB storage devices:
 ------------[ cut here ]------------
 WARNING: at lib/dma-debug.c:902 check_unmap+0x4e4/0x695()
 ehci_hcd 0000:00:12.2: DMA-API: device driver frees DMA sg list with different entry count [map count=4] [unmap count=1]
 Modules linked in: ohci_hcd ehci_hcd
 Pid: 0, comm: kworker/0:1 Not tainted 3.2.0-rc2+ #319
 Call Trace:
  <IRQ>  [<ffffffff81036d3b>] warn_slowpath_common+0x80/0x98
  [<ffffffff81036de7>] warn_slowpath_fmt+0x41/0x43
  [<ffffffff811fa5ae>] check_unmap+0x4e4/0x695
  [<ffffffff8105e92c>] ? trace_hardirqs_off+0xd/0xf
  [<ffffffff8147208b>] ? _raw_spin_unlock_irqrestore+0x33/0x50
  [<ffffffff811fa84a>] debug_dma_unmap_sg+0xeb/0x117
  [<ffffffff8137b02f>] usb_hcd_unmap_urb_for_dma+0x71/0x188
  [<ffffffff8137b166>] unmap_urb_for_dma+0x20/0x22
  [<ffffffff8137b1c5>] usb_hcd_giveback_urb+0x5d/0xc0
  [<ffffffffa0000d02>] ehci_urb_done+0xf7/0x10c [ehci_hcd]
  [<ffffffffa0001140>] qh_completions+0x429/0x4bd [ehci_hcd]
  [<ffffffffa000340a>] ehci_work+0x95/0x9c0 [ehci_hcd]
  ...
 ---[ end trace f29ac88a5a48c580 ]---
 Mapped at:
  [<ffffffff811faac4>] debug_dma_map_sg+0x45/0x139
  [<ffffffff8137bc0b>] usb_hcd_map_urb_for_dma+0x22e/0x478
  [<ffffffff8137c494>] usb_hcd_submit_urb+0x63f/0x6fa
  [<ffffffff8137d01c>] usb_submit_urb+0x2c7/0x2de
  [<ffffffff8137dcd4>] usb_sg_wait+0x55/0x161

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/hcd.c
drivers/usb/host/ehci-q.c
drivers/usb/host/uhci-q.c
drivers/usb/host/whci/qset.c
drivers/usb/host/xhci-ring.c
include/linux/usb.h

index 39ea00bfb9c4dc0582e224b3a72270d27e11f23b..691d212cac4979ecb2e881ebb5fbe83144723e64 100644 (file)
@@ -1387,11 +1387,10 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
                                        ret = -EAGAIN;
                                else
                                        urb->transfer_flags |= URB_DMA_MAP_SG;
-                               if (n != urb->num_sgs) {
-                                       urb->num_sgs = n;
+                               urb->num_mapped_sgs = n;
+                               if (n != urb->num_sgs)
                                        urb->transfer_flags |=
                                                        URB_DMA_SG_COMBINED;
-                               }
                        } else if (urb->sg) {
                                struct scatterlist *sg = urb->sg;
                                urb->transfer_dma = dma_map_page(
index 0917e3a32465064903dd884cc2c258baadadc0f2..2499b3bce360101b9cc91745ee8bde707961681f 100644 (file)
@@ -649,7 +649,7 @@ qh_urb_transaction (
        /*
         * data transfer stage:  buffer setup
         */
-       i = urb->num_sgs;
+       i = urb->num_mapped_sgs;
        if (len > 0 && i > 0) {
                sg = urb->sg;
                buf = sg_dma_address(sg);
index 84ed28b34f934d86ecef647d73580309baf3f2b8..82539913ad847d1e007662a5b665daf0927b524c 100644 (file)
@@ -943,7 +943,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
        if (usb_pipein(urb->pipe))
                status |= TD_CTRL_SPD;
 
-       i = urb->num_sgs;
+       i = urb->num_mapped_sgs;
        if (len > 0 && i > 0) {
                sg = urb->sg;
                data = sg_dma_address(sg);
index a403b53e86b9fd3dcc742b9da5759330fbc4d212..76083ae9213800cf48ecc1fa2208def7c1cf4542 100644 (file)
@@ -443,7 +443,7 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u
 
        remaining = urb->transfer_buffer_length;
 
-       for_each_sg(urb->sg, sg, urb->num_sgs, i) {
+       for_each_sg(urb->sg, sg, urb->num_mapped_sgs, i) {
                dma_addr_t dma_addr;
                size_t dma_remaining;
                dma_addr_t sp, ep;
@@ -561,7 +561,7 @@ static int qset_add_urb_sg_linearize(struct whc *whc, struct whc_qset *qset,
 
        remaining = urb->transfer_buffer_length;
 
-       for_each_sg(urb->sg, sg, urb->num_sgs, i) {
+       for_each_sg(urb->sg, sg, urb->num_mapped_sgs, i) {
                size_t len;
                size_t sg_remaining;
                void *orig;
index b4b06910f68450e773f6a9ac53368634138902f1..c0c5d6c7cb692627beb640b4201dc8771b7c7793 100644 (file)
@@ -2570,7 +2570,7 @@ static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb)
        struct scatterlist *sg;
 
        sg = NULL;
-       num_sgs = urb->num_sgs;
+       num_sgs = urb->num_mapped_sgs;
        temp = urb->transfer_buffer_length;
 
        xhci_dbg(xhci, "count sg list trbs: \n");
@@ -2754,7 +2754,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
                return -EINVAL;
 
        num_trbs = count_sg_trbs_needed(xhci, urb);
-       num_sgs = urb->num_sgs;
+       num_sgs = urb->num_mapped_sgs;
        total_packet_count = roundup(urb->transfer_buffer_length,
                        le16_to_cpu(urb->ep->desc.wMaxPacketSize));
 
index 73c7df4896074219ad76daee5e9eb0ba6e1a1976..b08e04cf202b44b23be1b21791b11a65169cb18c 100644 (file)
@@ -1202,6 +1202,7 @@ struct urb {
        void *transfer_buffer;          /* (in) associated data buffer */
        dma_addr_t transfer_dma;        /* (in) dma addr for transfer_buffer */
        struct scatterlist *sg;         /* (in) scatter gather buffer list */
+       int num_mapped_sgs;             /* (internal) mapped sg entries */
        int num_sgs;                    /* (in) number of entries in the sg list */
        u32 transfer_buffer_length;     /* (in) data buffer length */
        u32 actual_length;              /* (return) actual transfer length */