usb: fix number of mapped SG DMA entries
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> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
8ad028bd97
commit
bc677d5b64
|
@ -1398,11 +1398,10 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
|
||||||
ret = -EAGAIN;
|
ret = -EAGAIN;
|
||||||
else
|
else
|
||||||
urb->transfer_flags |= URB_DMA_MAP_SG;
|
urb->transfer_flags |= URB_DMA_MAP_SG;
|
||||||
if (n != urb->num_sgs) {
|
urb->num_mapped_sgs = n;
|
||||||
urb->num_sgs = n;
|
if (n != urb->num_sgs)
|
||||||
urb->transfer_flags |=
|
urb->transfer_flags |=
|
||||||
URB_DMA_SG_COMBINED;
|
URB_DMA_SG_COMBINED;
|
||||||
}
|
|
||||||
} else if (urb->sg) {
|
} else if (urb->sg) {
|
||||||
struct scatterlist *sg = urb->sg;
|
struct scatterlist *sg = urb->sg;
|
||||||
urb->transfer_dma = dma_map_page(
|
urb->transfer_dma = dma_map_page(
|
||||||
|
|
|
@ -658,7 +658,7 @@ qh_urb_transaction (
|
||||||
/*
|
/*
|
||||||
* data transfer stage: buffer setup
|
* data transfer stage: buffer setup
|
||||||
*/
|
*/
|
||||||
i = urb->num_sgs;
|
i = urb->num_mapped_sgs;
|
||||||
if (len > 0 && i > 0) {
|
if (len > 0 && i > 0) {
|
||||||
sg = urb->sg;
|
sg = urb->sg;
|
||||||
buf = sg_dma_address(sg);
|
buf = sg_dma_address(sg);
|
||||||
|
|
|
@ -943,7 +943,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb,
|
||||||
if (usb_pipein(urb->pipe))
|
if (usb_pipein(urb->pipe))
|
||||||
status |= TD_CTRL_SPD;
|
status |= TD_CTRL_SPD;
|
||||||
|
|
||||||
i = urb->num_sgs;
|
i = urb->num_mapped_sgs;
|
||||||
if (len > 0 && i > 0) {
|
if (len > 0 && i > 0) {
|
||||||
sg = urb->sg;
|
sg = urb->sg;
|
||||||
data = sg_dma_address(sg);
|
data = sg_dma_address(sg);
|
||||||
|
|
|
@ -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;
|
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;
|
dma_addr_t dma_addr;
|
||||||
size_t dma_remaining;
|
size_t dma_remaining;
|
||||||
dma_addr_t sp, ep;
|
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;
|
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 len;
|
||||||
size_t sg_remaining;
|
size_t sg_remaining;
|
||||||
void *orig;
|
void *orig;
|
||||||
|
|
|
@ -2551,7 +2551,7 @@ static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb)
|
||||||
struct scatterlist *sg;
|
struct scatterlist *sg;
|
||||||
|
|
||||||
sg = NULL;
|
sg = NULL;
|
||||||
num_sgs = urb->num_sgs;
|
num_sgs = urb->num_mapped_sgs;
|
||||||
temp = urb->transfer_buffer_length;
|
temp = urb->transfer_buffer_length;
|
||||||
|
|
||||||
xhci_dbg(xhci, "count sg list trbs: \n");
|
xhci_dbg(xhci, "count sg list trbs: \n");
|
||||||
|
@ -2735,7 +2735,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
num_trbs = count_sg_trbs_needed(xhci, urb);
|
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,
|
total_packet_count = roundup(urb->transfer_buffer_length,
|
||||||
usb_endpoint_maxp(&urb->ep->desc));
|
usb_endpoint_maxp(&urb->ep->desc));
|
||||||
|
|
||||||
|
|
|
@ -1221,6 +1221,7 @@ struct urb {
|
||||||
void *transfer_buffer; /* (in) associated data buffer */
|
void *transfer_buffer; /* (in) associated data buffer */
|
||||||
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
|
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
|
||||||
struct scatterlist *sg; /* (in) scatter gather buffer list */
|
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 */
|
int num_sgs; /* (in) number of entries in the sg list */
|
||||||
u32 transfer_buffer_length; /* (in) data buffer length */
|
u32 transfer_buffer_length; /* (in) data buffer length */
|
||||||
u32 actual_length; /* (return) actual transfer length */
|
u32 actual_length; /* (return) actual transfer length */
|
||||||
|
|
Loading…
Reference in New Issue