Now that a single URB can transmit a SG list, we can remove the usb_sg_init / usb_sg_wait / usb_sg_cancel API. It only has two users -- usb-storage and usbtest. I have not tested this patch! I'm currently travelling and would prefer not to test it on my laptop. The diffstat is quite compelling though, and the usb-storage driver gets quite a lot simpler (net -31 lines of code from that driver). Not-signed-off-by: Matthew Wilcox <willy@xxxxxxxxxxxxxxx> drivers/usb/core/message.c | 357 ---------------------------------------- drivers/usb/misc/usbtest.c | 103 +++++++---- drivers/usb/storage/transport.c | 53 +---- drivers/usb/storage/usb.h | 12 - include/linux/usb.h | 51 ----- 5 files changed, 83 insertions(+), 493 deletions(-) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 9f0ce7d..4c310ce 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -248,363 +248,6 @@ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, } EXPORT_SYMBOL_GPL(usb_bulk_msg); -/*-------------------------------------------------------------------*/ - -static void sg_clean(struct usb_sg_request *io) -{ - if (io->urbs) { - while (io->entries--) - usb_free_urb(io->urbs [io->entries]); - kfree(io->urbs); - io->urbs = NULL; - } - io->dev = NULL; -} - -static void sg_complete(struct urb *urb) -{ - struct usb_sg_request *io = urb->context; - int status = urb->status; - - spin_lock(&io->lock); - - /* In 2.5 we require hcds' endpoint queues not to progress after fault - * reports, until the completion callback (this!) returns. That lets - * device driver code (like this routine) unlink queued urbs first, - * if it needs to, since the HC won't work on them at all. So it's - * not possible for page N+1 to overwrite page N, and so on. - * - * That's only for "hard" faults; "soft" faults (unlinks) sometimes - * complete before the HCD can get requests away from hardware, - * though never during cleanup after a hard fault. - */ - if (io->status - && (io->status != -ECONNRESET - || status != -ECONNRESET) - && urb->actual_length) { - dev_err(io->dev->bus->controller, - "dev %s ep%d%s scatterlist error %d/%d\n", - io->dev->devpath, - usb_endpoint_num(&urb->ep->desc), - usb_urb_dir_in(urb) ? "in" : "out", - status, io->status); - /* BUG (); */ - } - - if (io->status == 0 && status && status != -ECONNRESET) { - int i, found, retval; - - io->status = status; - - /* the previous urbs, and this one, completed already. - * unlink pending urbs so they won't rx/tx bad data. - * careful: unlink can sometimes be synchronous... - */ - spin_unlock(&io->lock); - for (i = 0, found = 0; i < io->entries; i++) { - if (!io->urbs [i] || !io->urbs [i]->dev) - continue; - if (found) { - retval = usb_unlink_urb(io->urbs [i]); - if (retval != -EINPROGRESS && - retval != -ENODEV && - retval != -EBUSY) - dev_err(&io->dev->dev, - "%s, unlink --> %d\n", - __func__, retval); - } else if (urb == io->urbs [i]) - found = 1; - } - spin_lock(&io->lock); - } - urb->dev = NULL; - - /* on the last completion, signal usb_sg_wait() */ - io->bytes += urb->actual_length; - io->count--; - if (!io->count) - complete(&io->complete); - - spin_unlock(&io->lock); -} - - -/** - * usb_sg_init - initializes scatterlist-based bulk/interrupt I/O request - * @io: request block being initialized. until usb_sg_wait() returns, - * treat this as a pointer to an opaque block of memory, - * @dev: the usb device that will send or receive the data - * @pipe: endpoint "pipe" used to transfer the data - * @period: polling rate for interrupt endpoints, in frames or - * (for high speed endpoints) microframes; ignored for bulk - * @sg: scatterlist entries - * @nents: how many entries in the scatterlist - * @length: how many bytes to send from the scatterlist, or zero to - * send every byte identified in the list. - * @mem_flags: SLAB_* flags affecting memory allocations in this call - * - * Returns zero for success, else a negative errno value. This initializes a - * scatter/gather request, allocating resources such as I/O mappings and urb - * memory (except maybe memory used by USB controller drivers). - * - * The request must be issued using usb_sg_wait(), which waits for the I/O to - * complete (or to be canceled) and then cleans up all resources allocated by - * usb_sg_init(). - * - * The request may be canceled with usb_sg_cancel(), either before or after - * usb_sg_wait() is called. - */ -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 use_sg; - - if (!io || !dev || !sg - || usb_pipecontrol(pipe) - || usb_pipeisoc(pipe) - || nents <= 0) - return -EINVAL; - - spin_lock_init(&io->lock); - io->dev = dev; - io->pipe = pipe; - - if (dev->bus->sg_tablesize > 0) { - use_sg = true; - io->entries = 1; - } else { - use_sg = false; - io->entries = nents; - } - - /* initialize all the urbs we'll use */ - io->urbs = kmalloc(io->entries * sizeof *io->urbs, mem_flags); - if (!io->urbs) - goto nomem; - - urb_flags = URB_NO_INTERRUPT; - if (usb_pipein(pipe)) - urb_flags |= URB_SHORT_NOT_OK; - - for_each_sg(sg, sg, io->entries, i) { - struct urb *urb; - unsigned len; - - urb = usb_alloc_urb(0, mem_flags); - if (!urb) { - io->entries = i; - goto nomem; - } - io->urbs[i] = urb; - - urb->dev = NULL; - urb->pipe = pipe; - urb->interval = period; - urb->transfer_flags = urb_flags; - urb->complete = sg_complete; - urb->context = io; - urb->sg = sg; - - if (use_sg) { - /* There is no single transfer buffer */ - urb->transfer_buffer = NULL; - urb->num_sgs = nents; - - /* A length of zero means transfer the whole sg list */ - len = length; - if (len == 0) { - struct scatterlist *sg2; - int j; - - for_each_sg(sg, sg2, nents, j) - len += sg2->length; - } - } else { - /* - * Some systems can't use DMA; they use PIO instead. - * For their sakes, transfer_buffer is set whenever - * possible. - */ - if (!PageHighMem(sg_page(sg))) - urb->transfer_buffer = sg_virt(sg); - else - urb->transfer_buffer = NULL; - - len = sg->length; - if (length) { - len = min_t(unsigned, len, length); - length -= len; - if (length == 0) - io->entries = i + 1; - } - } - urb->transfer_buffer_length = len; - } - io->urbs[--i]->transfer_flags &= ~URB_NO_INTERRUPT; - - /* transaction state */ - io->count = io->entries; - io->status = 0; - io->bytes = 0; - init_completion(&io->complete); - return 0; - -nomem: - sg_clean(io); - return -ENOMEM; -} -EXPORT_SYMBOL_GPL(usb_sg_init); - -/** - * usb_sg_wait - synchronously execute scatter/gather request - * @io: request block handle, as initialized with usb_sg_init(). - * some fields become accessible when this call returns. - * Context: !in_interrupt () - * - * This function blocks until the specified I/O operation completes. It - * leverages the grouping of the related I/O requests to get good transfer - * rates, by queueing the requests. At higher speeds, such queuing can - * significantly improve USB throughput. - * - * There are three kinds of completion for this function. - * (1) success, where io->status is zero. The number of io->bytes - * transferred is as requested. - * (2) error, where io->status is a negative errno value. The number - * of io->bytes transferred before the error is usually less - * than requested, and can be nonzero. - * (3) cancellation, a type of error with status -ECONNRESET that - * is initiated by usb_sg_cancel(). - * - * When this function returns, all memory allocated through usb_sg_init() or - * this call will have been freed. The request block parameter may still be - * passed to usb_sg_cancel(), or it may be freed. It could also be - * reinitialized and then reused. - * - * Data Transfer Rates: - * - * Bulk transfers are valid for full or high speed endpoints. - * The best full speed data rate is 19 packets of 64 bytes each - * per frame, or 1216 bytes per millisecond. - * The best high speed data rate is 13 packets of 512 bytes each - * per microframe, or 52 KBytes per millisecond. - * - * The reason to use interrupt transfers through this API would most likely - * be to reserve high speed bandwidth, where up to 24 KBytes per millisecond - * could be transferred. That capability is less useful for low or full - * speed interrupt endpoints, which allow at most one packet per millisecond, - * of at most 8 or 64 bytes (respectively). - * - * It is not necessary to call this function to reserve bandwidth for devices - * under an xHCI host controller, as the bandwidth is reserved when the - * configuration or interface alt setting is selected. - */ -void usb_sg_wait(struct usb_sg_request *io) -{ - int i; - int entries = io->entries; - - /* queue the urbs. */ - spin_lock_irq(&io->lock); - i = 0; - while (i < entries && !io->status) { - int retval; - - io->urbs[i]->dev = io->dev; - retval = usb_submit_urb(io->urbs [i], GFP_ATOMIC); - - /* after we submit, let completions or cancelations fire; - * we handshake using io->status. - */ - spin_unlock_irq(&io->lock); - switch (retval) { - /* maybe we retrying will recover */ - case -ENXIO: /* hc didn't queue this one */ - case -EAGAIN: - case -ENOMEM: - io->urbs[i]->dev = NULL; - retval = 0; - yield(); - break; - - /* no error? continue immediately. - * - * NOTE: to work better with UHCI (4K I/O buffer may - * need 3K of TDs) it may be good to limit how many - * URBs are queued at once; N milliseconds? - */ - case 0: - ++i; - cpu_relax(); - break; - - /* fail any uncompleted urbs */ - default: - io->urbs[i]->dev = NULL; - io->urbs[i]->status = retval; - dev_dbg(&io->dev->dev, "%s, submit --> %d\n", - __func__, retval); - usb_sg_cancel(io); - } - spin_lock_irq(&io->lock); - if (retval && (io->status == 0 || io->status == -ECONNRESET)) - io->status = retval; - } - io->count -= entries - i; - if (io->count == 0) - complete(&io->complete); - spin_unlock_irq(&io->lock); - - /* OK, yes, this could be packaged as non-blocking. - * So could the submit loop above ... but it's easier to - * solve neither problem than to solve both! - */ - wait_for_completion(&io->complete); - - sg_clean(io); -} -EXPORT_SYMBOL_GPL(usb_sg_wait); - -/** - * usb_sg_cancel - stop scatter/gather i/o issued by usb_sg_wait() - * @io: request block, initialized with usb_sg_init() - * - * This stops a request after it has been started by usb_sg_wait(). - * It can also prevents one initialized by usb_sg_init() from starting, - * so that call just frees resources allocated to the request. - */ -void usb_sg_cancel(struct usb_sg_request *io) -{ - unsigned long flags; - - spin_lock_irqsave(&io->lock, flags); - - /* shut everything down, if it didn't already */ - if (!io->status) { - int i; - - io->status = -ECONNRESET; - spin_unlock(&io->lock); - for (i = 0; i < io->entries; i++) { - int retval; - - if (!io->urbs [i]->dev) - continue; - retval = usb_unlink_urb(io->urbs [i]); - if (retval != -EINPROGRESS && retval != -EBUSY) - dev_warn(&io->dev->dev, "%s, unlink --> %d\n", - __func__, retval); - } - spin_lock(&io->lock); - } - spin_unlock_irqrestore(&io->lock, flags); -} -EXPORT_SYMBOL_GPL(usb_sg_cancel); - -/*-------------------------------------------------------------------*/ - /** * usb_get_descriptor - issues a generic GET_DESCRIPTOR request * @dev: the device whose descriptor is being retrieved diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index eef370e..62ab9b3 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -397,29 +397,60 @@ alloc_sglist (int nents, int max, int vary) return sg; } +static struct urb *sglist_alloc_urb ( + struct usb_device *udev, + int pipe, + int nents, + unsigned long bytes, + int vary) +{ + struct urb *urb; + + urb = usb_alloc_urb (0, GFP_KERNEL); + if (!urb) + return urb; + usb_fill_bulk_urb (urb, udev, pipe, NULL, bytes, simple_callback, NULL); + urb->interval = (udev->speed == USB_SPEED_HIGH) + ? (INTERRUPT_RATE << 3) + : INTERRUPT_RATE; + if (usb_pipein (pipe)) + urb->transfer_flags |= URB_SHORT_NOT_OK; + urb->sg = alloc_sglist(nents, bytes, vary); + if (!urb->sg) { + usb_free_urb (urb); + urb = NULL; + } + urb->num_sgs = nents; + + return urb; +} + +static void sglist_free_urb (struct urb *urb) +{ + free_sglist(urb->sg, urb->num_sgs); + usb_free_urb (urb); +} + static int perform_sglist ( struct usbtest_dev *tdev, - unsigned iterations, - int pipe, - struct usb_sg_request *req, - struct scatterlist *sg, - int nents + struct urb *urb, + unsigned iterations ) { - struct usb_device *udev = testdev_to_usbdev(tdev); + struct usb_device *udev = urb->dev; + struct completion completion; int retval = 0; + urb->context = &completion; while (retval == 0 && iterations-- > 0) { - retval = usb_sg_init (req, udev, pipe, - (udev->speed == USB_SPEED_HIGH) - ? (INTERRUPT_RATE << 3) - : INTERRUPT_RATE, - sg, nents, 0, GFP_KERNEL); - - if (retval) + init_completion (&completion); + if ((retval = usb_submit_urb (urb, GFP_KERNEL)) != 0) break; - usb_sg_wait (req); - retval = req->status; + + /* NOTE: no timeouts; can't be broken out of by interrupt */ + wait_for_completion (&completion); + retval = urb->status; + urb->dev = udev; /* FIXME check resulting data pattern */ @@ -1564,8 +1595,6 @@ usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf) struct usbtest_param *param = buf; int retval = -EOPNOTSUPP; struct urb *urb; - struct scatterlist *sg; - struct usb_sg_request req; struct timeval start; unsigned i; @@ -1694,15 +1723,15 @@ usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf) "TEST 5: write %d sglists %d entries of %d bytes\n", param->iterations, param->sglen, param->length); - sg = alloc_sglist (param->sglen, param->length, 0); - if (!sg) { + urb = sglist_alloc_urb (udev, dev->out_pipe, param->sglen, + param->length, 0); + if (!urb) { retval = -ENOMEM; break; } // FIRMWARE: bulk sink (maybe accepts short writes) - retval = perform_sglist(dev, param->iterations, dev->out_pipe, - &req, sg, param->sglen); - free_sglist (sg, param->sglen); + retval = perform_sglist(dev, urb, param->iterations); + sglist_free_urb (urb); break; case 6: @@ -1712,15 +1741,15 @@ usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf) "TEST 6: read %d sglists %d entries of %d bytes\n", param->iterations, param->sglen, param->length); - sg = alloc_sglist (param->sglen, param->length, 0); - if (!sg) { + urb = sglist_alloc_urb (udev, dev->in_pipe, param->sglen, + param->length, 0); + if (!urb) { retval = -ENOMEM; break; } // FIRMWARE: bulk source (maybe generates short writes) - retval = perform_sglist(dev, param->iterations, dev->in_pipe, - &req, sg, param->sglen); - free_sglist (sg, param->sglen); + retval = perform_sglist(dev, urb, param->iterations); + sglist_free_urb (urb); break; case 7: if (dev->out_pipe == 0 || param->sglen == 0 || param->vary == 0) @@ -1729,15 +1758,15 @@ usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf) "TEST 7: write/%d %d sglists %d entries 0..%d bytes\n", param->vary, param->iterations, param->sglen, param->length); - sg = alloc_sglist (param->sglen, param->length, param->vary); - if (!sg) { + urb = sglist_alloc_urb (udev, dev->out_pipe, param->sglen, + param->length, param->vary); + if (!urb) { retval = -ENOMEM; break; } // FIRMWARE: bulk sink (maybe accepts short writes) - retval = perform_sglist(dev, param->iterations, dev->out_pipe, - &req, sg, param->sglen); - free_sglist (sg, param->sglen); + retval = perform_sglist(dev, urb, param->iterations); + sglist_free_urb (urb); break; case 8: if (dev->in_pipe == 0 || param->sglen == 0 || param->vary == 0) @@ -1746,15 +1775,15 @@ usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf) "TEST 8: read/%d %d sglists %d entries 0..%d bytes\n", param->vary, param->iterations, param->sglen, param->length); - sg = alloc_sglist (param->sglen, param->length, param->vary); - if (!sg) { + urb = sglist_alloc_urb (udev, dev->in_pipe, param->sglen, + param->length, param->vary); + if (!urb) { retval = -ENOMEM; break; } // FIRMWARE: bulk source (maybe generates short writes) - retval = perform_sglist(dev, param->iterations, dev->in_pipe, - &req, sg, param->sglen); - free_sglist (sg, param->sglen); + retval = perform_sglist(dev, urb, param->iterations); + sglist_free_urb (urb); break; /* non-queued sanity tests for control (chapter 9 subset) */ diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 64ec073..66962f5 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -412,7 +412,7 @@ EXPORT_SYMBOL_GPL(usb_stor_bulk_transfer_buf); * Transfer a scatter-gather list via bulk transfer * * This function does basically the same thing as usb_stor_bulk_transfer_buf() - * above, but it uses the usbcore scatter-gather library. + * above */ static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe, struct scatterlist *sg, int num_sg, unsigned int length, @@ -420,43 +420,20 @@ static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe, { int result; - /* don't submit s-g requests during abort processing */ - if (test_bit(US_FLIDX_ABORTING, &us->dflags)) - return USB_STOR_XFER_ERROR; - - /* initialize the scatter-gather request block */ - US_DEBUGP("%s: xfer %u bytes, %d entries\n", __func__, - length, num_sg); - result = usb_sg_init(&us->current_sg, us->pusb_dev, pipe, 0, - sg, num_sg, length, GFP_NOIO); - if (result) { - US_DEBUGP("usb_sg_init returned %d\n", result); - return USB_STOR_XFER_ERROR; - } - - /* since the block has been initialized successfully, it's now - * okay to cancel it */ - set_bit(US_FLIDX_SG_ACTIVE, &us->dflags); - - /* did an abort occur during the submission? */ - if (test_bit(US_FLIDX_ABORTING, &us->dflags)) { - - /* cancel the request, if it hasn't been cancelled already */ - if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags)) { - US_DEBUGP("-- cancelling sg request\n"); - usb_sg_cancel(&us->current_sg); - } - } + US_DEBUGP("%s: xfer %u bytes\n", __func__, length); - /* wait for the completion of the transfer */ - usb_sg_wait(&us->current_sg); - clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags); + /* fill and submit the URB */ + usb_fill_bulk_urb(us->current_urb, us->pusb_dev, pipe, NULL, length, + usb_stor_blocking_completion, NULL); + us->current_urb->num_sgs = num_sg; + us->current_urb->sg = sg; + result = usb_stor_msg_common(us, 0); - result = us->current_sg.status; + /* store the actual length of the data transferred */ if (act_len) - *act_len = us->current_sg.bytes; - return interpret_urb_result(us, pipe, length, result, - us->current_sg.bytes); + *act_len = us->current_urb->actual_length; + return interpret_urb_result(us, pipe, length, result, + us->current_urb->actual_length); } /* @@ -868,12 +845,6 @@ void usb_stor_stop_transport(struct us_data *us) US_DEBUGP("-- cancelling URB\n"); usb_unlink_urb(us->current_urb); } - - /* If we are waiting for a scatter-gather operation, cancel it. */ - if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags)) { - US_DEBUGP("-- cancelling sg request\n"); - usb_sg_cancel(&us->current_sg); - } } /* diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 89d3bff..bd18dcf 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -67,12 +67,11 @@ struct us_unusual_dev { /* Dynamic bitflag definitions (us->dflags): used in set_bit() etc. */ #define US_FLIDX_URB_ACTIVE 0 /* current_urb is in use */ -#define US_FLIDX_SG_ACTIVE 1 /* current_sg is in use */ -#define US_FLIDX_ABORTING 2 /* abort is in progress */ -#define US_FLIDX_DISCONNECTING 3 /* disconnect in progress */ -#define US_FLIDX_RESETTING 4 /* device reset in progress */ -#define US_FLIDX_TIMED_OUT 5 /* SCSI midlayer timed out */ -#define US_FLIDX_DONT_SCAN 6 /* don't scan (disconnect) */ +#define US_FLIDX_ABORTING 1 /* abort is in progress */ +#define US_FLIDX_DISCONNECTING 2 /* disconnect in progress */ +#define US_FLIDX_RESETTING 3 /* device reset in progress */ +#define US_FLIDX_TIMED_OUT 4 /* SCSI midlayer timed out */ +#define US_FLIDX_DONT_SCAN 5 /* don't scan (disconnect) */ #define USB_STOR_STRING_LEN 32 @@ -137,7 +136,6 @@ struct us_data { /* control and bulk communications data */ struct urb *current_urb; /* USB requests */ struct usb_ctrlrequest *cr; /* control requests */ - struct usb_sg_request current_sg; /* scatter-gather req. */ unsigned char *iobuf; /* I/O buffer */ dma_addr_t iobuf_dma; /* buffer DMA addresses */ struct task_struct *ctl_thread; /* the control thread */ diff --git a/include/linux/usb.h b/include/linux/usb.h index 35fe6ab..ac3ac4f 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1430,57 +1430,6 @@ extern int usb_driver_set_configuration(struct usb_device *udev, int config); #define USB_CTRL_GET_TIMEOUT 5000 #define USB_CTRL_SET_TIMEOUT 5000 - -/** - * struct usb_sg_request - support for scatter/gather I/O - * @status: zero indicates success, else negative errno - * @bytes: counts bytes transferred. - * - * These requests are initialized using usb_sg_init(), and then are used - * as request handles passed to usb_sg_wait() or usb_sg_cancel(). Most - * members of the request object aren't for driver access. - * - * The status and bytecount values are valid only after usb_sg_wait() - * returns. If the status is zero, then the bytecount matches the total - * from the request. - * - * After an error completion, drivers may need to clear a halt condition - * on the endpoint. - */ -struct usb_sg_request { - int status; - size_t bytes; - - /* private: - * members below are private to usbcore, - * and are not provided for driver access! - */ - spinlock_t lock; - - struct usb_device *dev; - int pipe; - - int entries; - struct urb **urbs; - - int count; - struct completion complete; -}; - -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 -); -void usb_sg_cancel(struct usb_sg_request *io); -void usb_sg_wait(struct usb_sg_request *io); - - /* ----------------------------------------------------------------------- */ /* -- 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