diff options
author | Clemens Ladisch <clemens@ladisch.de> | 2011-12-03 17:41:31 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2012-01-12 14:35:25 -0500 |
commit | 766b8a7f7ee006dfd73dbc676addd80f7dbe86ef (patch) | |
tree | 818c4c1709838ac321940e68624ec6d0228b9db1 | |
parent | 0d5b25f934978301b2a1385c72f3b6abe04db484 (diff) |
usb: fix number of mapped SG DMA entries
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>
-rw-r--r-- | drivers/usb/core/hcd.c | 5 | ||||
-rw-r--r-- | drivers/usb/host/ehci-q.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/uhci-q.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/whci/qset.c | 4 | ||||
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 4 | ||||
-rw-r--r-- | include/linux/usb.h | 1 |
6 files changed, 9 insertions, 9 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 39ea00bfb9c..691d212cac4 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c | |||
@@ -1387,11 +1387,10 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, | |||
1387 | ret = -EAGAIN; | 1387 | ret = -EAGAIN; |
1388 | else | 1388 | else |
1389 | urb->transfer_flags |= URB_DMA_MAP_SG; | 1389 | urb->transfer_flags |= URB_DMA_MAP_SG; |
1390 | if (n != urb->num_sgs) { | 1390 | urb->num_mapped_sgs = n; |
1391 | urb->num_sgs = n; | 1391 | if (n != urb->num_sgs) |
1392 | urb->transfer_flags |= | 1392 | urb->transfer_flags |= |
1393 | URB_DMA_SG_COMBINED; | 1393 | URB_DMA_SG_COMBINED; |
1394 | } | ||
1395 | } else if (urb->sg) { | 1394 | } else if (urb->sg) { |
1396 | struct scatterlist *sg = urb->sg; | 1395 | struct scatterlist *sg = urb->sg; |
1397 | urb->transfer_dma = dma_map_page( | 1396 | urb->transfer_dma = dma_map_page( |
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 0917e3a3246..2499b3bce36 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c | |||
@@ -649,7 +649,7 @@ qh_urb_transaction ( | |||
649 | /* | 649 | /* |
650 | * data transfer stage: buffer setup | 650 | * data transfer stage: buffer setup |
651 | */ | 651 | */ |
652 | i = urb->num_sgs; | 652 | i = urb->num_mapped_sgs; |
653 | if (len > 0 && i > 0) { | 653 | if (len > 0 && i > 0) { |
654 | sg = urb->sg; | 654 | sg = urb->sg; |
655 | buf = sg_dma_address(sg); | 655 | buf = sg_dma_address(sg); |
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 84ed28b34f9..82539913ad8 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c | |||
@@ -943,7 +943,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, | |||
943 | if (usb_pipein(urb->pipe)) | 943 | if (usb_pipein(urb->pipe)) |
944 | status |= TD_CTRL_SPD; | 944 | status |= TD_CTRL_SPD; |
945 | 945 | ||
946 | i = urb->num_sgs; | 946 | i = urb->num_mapped_sgs; |
947 | if (len > 0 && i > 0) { | 947 | if (len > 0 && i > 0) { |
948 | sg = urb->sg; | 948 | sg = urb->sg; |
949 | data = sg_dma_address(sg); | 949 | data = sg_dma_address(sg); |
diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c index a403b53e86b..76083ae9213 100644 --- a/drivers/usb/host/whci/qset.c +++ b/drivers/usb/host/whci/qset.c | |||
@@ -443,7 +443,7 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u | |||
443 | 443 | ||
444 | remaining = urb->transfer_buffer_length; | 444 | remaining = urb->transfer_buffer_length; |
445 | 445 | ||
446 | for_each_sg(urb->sg, sg, urb->num_sgs, i) { | 446 | for_each_sg(urb->sg, sg, urb->num_mapped_sgs, i) { |
447 | dma_addr_t dma_addr; | 447 | dma_addr_t dma_addr; |
448 | size_t dma_remaining; | 448 | size_t dma_remaining; |
449 | dma_addr_t sp, ep; | 449 | dma_addr_t sp, ep; |
@@ -561,7 +561,7 @@ static int qset_add_urb_sg_linearize(struct whc *whc, struct whc_qset *qset, | |||
561 | 561 | ||
562 | remaining = urb->transfer_buffer_length; | 562 | remaining = urb->transfer_buffer_length; |
563 | 563 | ||
564 | for_each_sg(urb->sg, sg, urb->num_sgs, i) { | 564 | for_each_sg(urb->sg, sg, urb->num_mapped_sgs, i) { |
565 | size_t len; | 565 | size_t len; |
566 | size_t sg_remaining; | 566 | size_t sg_remaining; |
567 | void *orig; | 567 | void *orig; |
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index b4b06910f68..c0c5d6c7cb6 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c | |||
@@ -2570,7 +2570,7 @@ static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb) | |||
2570 | struct scatterlist *sg; | 2570 | struct scatterlist *sg; |
2571 | 2571 | ||
2572 | sg = NULL; | 2572 | sg = NULL; |
2573 | num_sgs = urb->num_sgs; | 2573 | num_sgs = urb->num_mapped_sgs; |
2574 | temp = urb->transfer_buffer_length; | 2574 | temp = urb->transfer_buffer_length; |
2575 | 2575 | ||
2576 | xhci_dbg(xhci, "count sg list trbs: \n"); | 2576 | 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, | |||
2754 | return -EINVAL; | 2754 | return -EINVAL; |
2755 | 2755 | ||
2756 | num_trbs = count_sg_trbs_needed(xhci, urb); | 2756 | num_trbs = count_sg_trbs_needed(xhci, urb); |
2757 | num_sgs = urb->num_sgs; | 2757 | num_sgs = urb->num_mapped_sgs; |
2758 | total_packet_count = roundup(urb->transfer_buffer_length, | 2758 | total_packet_count = roundup(urb->transfer_buffer_length, |
2759 | le16_to_cpu(urb->ep->desc.wMaxPacketSize)); | 2759 | le16_to_cpu(urb->ep->desc.wMaxPacketSize)); |
2760 | 2760 | ||
diff --git a/include/linux/usb.h b/include/linux/usb.h index 73c7df48960..b08e04cf202 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -1202,6 +1202,7 @@ struct urb { | |||
1202 | void *transfer_buffer; /* (in) associated data buffer */ | 1202 | void *transfer_buffer; /* (in) associated data buffer */ |
1203 | dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */ | 1203 | dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */ |
1204 | struct scatterlist *sg; /* (in) scatter gather buffer list */ | 1204 | struct scatterlist *sg; /* (in) scatter gather buffer list */ |
1205 | int num_mapped_sgs; /* (internal) mapped sg entries */ | ||
1205 | int num_sgs; /* (in) number of entries in the sg list */ | 1206 | int num_sgs; /* (in) number of entries in the sg list */ |
1206 | u32 transfer_buffer_length; /* (in) data buffer length */ | 1207 | u32 transfer_buffer_length; /* (in) data buffer length */ |
1207 | u32 actual_length; /* (return) actual transfer length */ | 1208 | u32 actual_length; /* (return) actual transfer length */ |