The WHCI HCD will also support urbs with scatter-gather lists. Add a usb_bus field to indicated how many sg list elements are supported by the HCD. Save space in struct urb by adding a URB_HAS_SG flag and reusing transfer_buffer to point to a struct sg_table. Split usb_sg_init() into usb_sg_init_with_sg() and usb_sg_init_without_sg(). Signed-off-by: David Vrabel <david.vrabel@xxxxxxx> --- drivers/usb/core/message.c | 289 +++++++++++++++++++++++------------------- drivers/usb/host/xhci-ring.c | 15 ++- drivers/usb/storage/usb.c | 10 ++ include/linux/usb.h | 9 +- 4 files changed, 184 insertions(+), 139 deletions(-) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 9720e69..a39765c 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -332,6 +332,158 @@ static void sg_complete(struct urb *urb) spin_unlock(&io->lock); } +static int +usb_sg_init_without_sg(struct usb_sg_request *io, struct usb_device *dev, + unsigned pipe, unsigned period, struct scatterlist *sg, + size_t length, gfp_t mem_flags) +{ + int i; + int urb_flags; + int dma; + + /* not all host controllers use DMA (like the mainstream pci ones); + * they can use PIO (sl811) or be software over another transport. + */ + dma = (dev->dev.dma_mask != NULL); + if (dma) + io->entries = usb_buffer_map_sg(dev, usb_pipein(pipe), + sg, io->nents); + else + io->entries = io->nents; + + /* initialize all the urbs we'll use */ + if (io->entries <= 0) + return io->entries; + + io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags); + if (!io->urbs) + goto nomem; + + urb_flags = URB_NO_INTERRUPT; + if (dma) + urb_flags |= URB_NO_TRANSFER_DMA_MAP; + if (usb_pipein(pipe)) + urb_flags |= URB_SHORT_NOT_OK; + + for_each_sg(sg, sg, io->entries, i) { + unsigned len; + + io->urbs[i] = usb_alloc_urb(0, mem_flags); + if (!io->urbs[i]) { + io->entries = i; + goto nomem; + } + + io->urbs[i]->dev = NULL; + io->urbs[i]->pipe = pipe; + io->urbs[i]->interval = period; + io->urbs[i]->transfer_flags = urb_flags; + + io->urbs[i]->complete = sg_complete; + io->urbs[i]->context = io; + + /* + * Some systems need to revert to PIO when DMA is + * temporarily unavailable. For their sakes, both + * transfer_buffer and transfer_dma are set when + * possible. However this can only work on systems + * without: + * + * - HIGHMEM, since DMA buffers located in high memory + * are not directly addressable by the CPU for PIO; + * + * - IOMMU, since dma_map_sg() is allowed to use an + * IOMMU to make virtually discontiguous buffers be + * "dma-contiguous" so that PIO and DMA need diferent + * numbers of URBs. + * + * So when HIGHMEM or IOMMU are in use, transfer_buffer + * is NULL to prevent stale pointers and to help spot + * bugs. + */ + if (dma) { + io->urbs[i]->transfer_dma = sg_dma_address(sg); + len = sg_dma_len(sg); +#if defined(CONFIG_HIGHMEM) || defined(CONFIG_GART_IOMMU) + io->urbs[i]->transfer_buffer = NULL; +#else + io->urbs[i]->transfer_buffer = sg_virt(sg); +#endif + } else { + /* hc may use _only_ transfer_buffer */ + io->urbs[i]->transfer_buffer = sg_virt(sg); + len = sg->length; + } + + if (length) { + len = min_t(unsigned, len, length); + length -= len; + if (length == 0) + io->entries = i + 1; + } + io->urbs[i]->transfer_buffer_length = len; + } + io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT; + + return 0; + +nomem: + sg_clean(io); + return -ENOMEM; +} + +static int +usb_sg_init_with_sg(struct usb_sg_request *io, struct usb_device *dev, + unsigned pipe, unsigned period, struct scatterlist *sg, + size_t length, gfp_t mem_flags) +{ + struct sg_table *sgt; + + if (length == 0) { + struct scatterlist *s; + int i; + for_each_sg(sg, s, io->nents, i) { + length += s->length; + } + } + + /* initialize one urb with transfer_buffer pointing to a new sg_table */ + io->urbs = kmalloc(sizeof(struct urb *) + sizeof(struct sg_table), mem_flags); + if (!io->urbs) + return -ENOMEM; + + sgt = (struct sg_table *)&io->urbs[1]; + sgt->sgl = sg; + sgt->orig_nents = io->nents; + + /* not all host controllers use DMA (like the mainstream pci ones); + * they can use PIO (sl811) or be software over another transport. + */ + if (dev->dev.dma_mask != NULL) + sgt->nents = usb_buffer_map_sg(dev, usb_pipein(pipe), sg, io->nents); + else + sgt->nents = sgt->orig_nents; + if (sgt->nents <= 0) + return sgt->nents; + + io->urbs[0] = usb_alloc_urb(0, mem_flags); + if (!io->urbs[0]) + return -ENOMEM; + io->entries = 1; + + io->urbs[0]->dev = NULL; + io->urbs[0]->pipe = pipe; + io->urbs[0]->interval = period; + io->urbs[0]->transfer_flags = URB_HAS_SG; + + io->urbs[0]->complete = sg_complete; + io->urbs[0]->context = io; + + io->urbs[0]->transfer_buffer = sgt; + io->urbs[0]->transfer_buffer_length = length; + + return 0; +} /** * usb_sg_init - initializes scatterlist-based bulk/interrupt I/O request @@ -362,10 +514,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, unsigned pipe, unsigned period, struct scatterlist *sg, int nents, size_t length, gfp_t mem_flags) { - int i; - int urb_flags; - int dma; - int use_sg; + int ret; if (!io || !dev || !sg || usb_pipecontrol(pipe) @@ -378,128 +527,15 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, io->pipe = pipe; io->sg = sg; io->nents = nents; + io->entries = 0; - /* not all host controllers use DMA (like the mainstream pci ones); - * they can use PIO (sl811) or be software over another transport. - */ - dma = (dev->dev.dma_mask != NULL); - if (dma) - io->entries = usb_buffer_map_sg(dev, usb_pipein(pipe), - sg, nents); + if (dev->bus->sg_tablesize) + ret = usb_sg_init_with_sg(io, dev, pipe, period, sg, length, mem_flags); else - io->entries = nents; - - /* initialize all the urbs we'll use */ - if (io->entries <= 0) - return io->entries; - - /* If we're running on an xHCI host controller, queue the whole scatter - * gather list with one call to urb_enqueue(). This is only for bulk, - * as that endpoint type does not care how the data gets broken up - * across frames. - */ - if (usb_pipebulk(pipe) && - bus_to_hcd(dev->bus)->driver->flags & HCD_USB3) { - io->urbs = kmalloc(sizeof *io->urbs, mem_flags); - use_sg = true; - } else { - io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags); - use_sg = false; - } - if (!io->urbs) - goto nomem; - - urb_flags = URB_NO_INTERRUPT; - if (dma) - urb_flags |= URB_NO_TRANSFER_DMA_MAP; - if (usb_pipein(pipe)) - urb_flags |= URB_SHORT_NOT_OK; - - if (use_sg) { - io->urbs[0] = usb_alloc_urb(0, mem_flags); - if (!io->urbs[0]) { - io->entries = 0; - goto nomem; - } - - io->urbs[0]->dev = NULL; - io->urbs[0]->pipe = pipe; - io->urbs[0]->interval = period; - io->urbs[0]->transfer_flags = urb_flags; - - io->urbs[0]->complete = sg_complete; - io->urbs[0]->context = io; - /* A length of zero means transfer the whole sg list */ - io->urbs[0]->transfer_buffer_length = length; - if (length == 0) { - for_each_sg(sg, sg, io->entries, i) { - io->urbs[0]->transfer_buffer_length += - sg_dma_len(sg); - } - } - io->urbs[0]->sg = io; - io->urbs[0]->num_sgs = io->entries; - io->entries = 1; - } else { - for_each_sg(sg, sg, io->entries, i) { - unsigned len; - - io->urbs[i] = usb_alloc_urb(0, mem_flags); - if (!io->urbs[i]) { - io->entries = i; - goto nomem; - } - - io->urbs[i]->dev = NULL; - io->urbs[i]->pipe = pipe; - io->urbs[i]->interval = period; - io->urbs[i]->transfer_flags = urb_flags; - - io->urbs[i]->complete = sg_complete; - io->urbs[i]->context = io; - - /* - * Some systems need to revert to PIO when DMA is - * temporarily unavailable. For their sakes, both - * transfer_buffer and transfer_dma are set when - * possible. However this can only work on systems - * without: - * - * - HIGHMEM, since DMA buffers located in high memory - * are not directly addressable by the CPU for PIO; - * - * - IOMMU, since dma_map_sg() is allowed to use an - * IOMMU to make virtually discontiguous buffers be - * "dma-contiguous" so that PIO and DMA need diferent - * numbers of URBs. - * - * So when HIGHMEM or IOMMU are in use, transfer_buffer - * is NULL to prevent stale pointers and to help spot - * bugs. - */ - if (dma) { - io->urbs[i]->transfer_dma = sg_dma_address(sg); - len = sg_dma_len(sg); -#if defined(CONFIG_HIGHMEM) || defined(CONFIG_GART_IOMMU) - io->urbs[i]->transfer_buffer = NULL; -#else - io->urbs[i]->transfer_buffer = sg_virt(sg); -#endif - } else { - /* hc may use _only_ transfer_buffer */ - io->urbs[i]->transfer_buffer = sg_virt(sg); - len = sg->length; - } - - if (length) { - len = min_t(unsigned, len, length); - length -= len; - if (length == 0) - io->entries = i + 1; - } - io->urbs[i]->transfer_buffer_length = len; - } - io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT; + ret = usb_sg_init_without_sg(io, dev, pipe, period, sg, length, mem_flags); + if (ret < 0) { + sg_clean(io); + return ret; } /* transaction state */ @@ -507,11 +543,8 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, io->status = 0; io->bytes = 0; init_completion(&io->complete); - return 0; -nomem: - sg_clean(io); - return -ENOMEM; + return 0; } EXPORT_SYMBOL_GPL(usb_sg_init); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index aa88a06..bd5a96e 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1265,15 +1265,16 @@ static int prepare_transfer(struct xhci_hcd *xhci, static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb) { int num_sgs, num_trbs, running_total, temp, i; + struct sg_table *sg_table; struct scatterlist *sg; - sg = NULL; - num_sgs = urb->num_sgs; + sg_table = urb->transfer_buffer; + num_sgs = sg_table->nents; temp = urb->transfer_buffer_length; xhci_dbg(xhci, "count sg list trbs: \n"); num_trbs = 0; - for_each_sg(urb->sg->sg, sg, num_sgs, i) { + for_each_sg(sg_table->sgl, sg, num_sgs, i) { unsigned int previous_total_trbs = num_trbs; unsigned int len = sg_dma_len(sg); @@ -1341,6 +1342,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct xhci_ring *ep_ring; unsigned int num_trbs; struct xhci_td *td; + struct sg_table *sg_table; struct scatterlist *sg; int num_sgs; int trb_buff_len, this_sg_len, running_total; @@ -1352,7 +1354,8 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, ep_ring = xhci->devs[slot_id]->ep_rings[ep_index]; num_trbs = count_sg_trbs_needed(xhci, urb); - num_sgs = urb->num_sgs; + sg_table = urb->transfer_buffer; + num_sgs = sg_table->nents; trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, num_trbs, urb, &td, mem_flags); @@ -1376,7 +1379,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, * the amount of memory allocated for this scatter-gather list. * 3. TRBs buffers can't cross 64KB boundaries. */ - sg = urb->sg->sg; + sg = sg_table->sgl; addr = (u64) sg_dma_address(sg); this_sg_len = sg_dma_len(sg); trb_buff_len = TRB_MAX_BUFF_SIZE - @@ -1480,7 +1483,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, int running_total, trb_buff_len, ret; u64 addr; - if (urb->sg) + if (urb->transfer_flags & URB_HAS_SG) return queue_bulk_sg_tx(xhci, mem_flags, urb, slot_id, ep_index); ep_ring = xhci->devs[slot_id]->ep_rings[ep_index]; diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 8060b85..964d8ee 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -840,6 +840,15 @@ static int usb_stor_scan_thread(void * __us) complete_and_exit(&us->scanning_done, 0); } +static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf) +{ + struct usb_device *usb_dev = interface_to_usbdev(intf); + + if (usb_dev->bus->sg_tablesize) { + return usb_dev->bus->sg_tablesize; + } + return SG_ALL; +} /* First part of general USB mass-storage probing */ int usb_stor_probe1(struct us_data **pus, @@ -868,6 +877,7 @@ int usb_stor_probe1(struct us_data **pus, * Allow 16-byte CDBs and thus > 2TB */ host->max_cmd_len = 16; + host->sg_tablesize = usb_stor_sg_tablesize(intf); *pus = us = host_to_us(host); memset(us, 0, sizeof(struct us_data)); mutex_init(&(us->dev_mutex)); diff --git a/include/linux/usb.h b/include/linux/usb.h index b1e3c2f..b96e7d8 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -331,6 +331,7 @@ struct usb_bus { u8 otg_port; /* 0, or number of OTG/HNP port */ unsigned is_b_host:1; /* true during some HNP roleswitches */ unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */ + int sg_tablesize; /* 0 or largest number of sg list entries */ int devnum_next; /* Next open device number in * round-robin allocation */ @@ -977,6 +978,7 @@ extern int usb_disabled(void); * ignored */ #define URB_NO_TRANSFER_DMA_MAP 0x0004 /* urb->transfer_dma valid on submit */ #define URB_NO_SETUP_DMA_MAP 0x0008 /* urb->setup_dma valid on submit */ +#define URB_HAS_SG 0x0010 /* urb->transfer_buffer points to a struct sg_table */ #define URB_NO_FSBR 0x0020 /* UHCI-specific */ #define URB_ZERO_PACKET 0x0040 /* Finish bulk OUT with short packet */ #define URB_NO_INTERRUPT 0x0080 /* HINT: no non-error interrupt @@ -1041,13 +1043,12 @@ typedef void (*usb_complete_t)(struct urb *); * is set). This buffer must be suitable for DMA; allocate it with * kmalloc() or equivalent. For transfers to "in" endpoints, contents * of this buffer will be modified. This buffer is used for the data - * stage of control transfers. + * stage of control transfers. If @transfer_flags & URB_HAS_SG then + * this points to a struct sg_table instead. * @transfer_dma: When transfer_flags includes URB_NO_TRANSFER_DMA_MAP, * the device driver is saying that it provided this DMA address, * which the host controller driver should use in preference to the * transfer_buffer. - * @sg: scatter gather buffer list - * @num_sgs: number of entries in the sg list * @transfer_buffer_length: How big is transfer_buffer. The transfer may * be broken up into chunks according to the current maximum packet * size for the endpoint, which is a function of the configuration @@ -1203,8 +1204,6 @@ struct urb { unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/ void *transfer_buffer; /* (in) associated data buffer */ dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */ - struct usb_sg_request *sg; /* (in) scatter gather buffer list */ - 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 */ unsigned char *setup_packet; /* (in) setup packet (control only) */ -- 1.6.3.3 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html