ChangeSet 1.808.2.25, 2002/10/28 11:58:59-08:00, mdharm-usb@one-eyed-alien.net [PATCH] USB storage: use scatter-gather core primitives This patch switches the usb-storage driver to using the new USB core scatter-gather primitives. This _should_ create a significant performance gain. diff -Nru a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c --- a/drivers/usb/storage/transport.c Mon Oct 28 13:51:29 2002 +++ b/drivers/usb/storage/transport.c Mon Oct 28 13:51:29 2002 @@ -655,43 +655,107 @@ } /* + * 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 primitives + */ +int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe, + struct scatterlist *sg, int num_sg, unsigned int length, + unsigned int *act_len) +{ + int result; + int partial; + + /* initialize the scatter-gather request block */ + US_DEBUGP("usb_stor_bulk_transfer_sglist(): xfer %d bytes, " + "%d entires\n", length, num_sg); + result = usb_sg_init(us->current_sg, us->pusb_dev, pipe, 0, + sg, num_sg, length, SLAB_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_CANCEL_SG, &us->flags); + + /* has the current command been aborted? */ + if (atomic_read(&us->sm_state) == US_STATE_ABORTING) { + + /* cancel the request, if it hasn't been cancelled already */ + if (test_and_clear_bit(US_FLIDX_CANCEL_SG, &us->flags)) { + US_DEBUGP("-- cancelling sg request\n"); + usb_sg_cancel(us->current_sg); + } + } + + usb_sg_wait(us->current_sg); + clear_bit(US_FLIDX_CANCEL_SG, &us->flags); + + result = us->current_sg->status; + partial = us->current_sg->bytes; + US_DEBUGP("usb_sg_wait() returned %d xferrerd %d/%d\n", + result, partial, length); + if (act_len) + *act_len = partial; + + /* if we stall, we need to clear it before we go on */ + if (result == -EPIPE) { + US_DEBUGP("clearing endpoint halt for pipe 0x%x," + "stalled at %d bytes\n", pipe, partial); + if (usb_stor_clear_halt(us, pipe) < 0) + return USB_STOR_XFER_ERROR; + return USB_STOR_XFER_STALLED; + } + + /* NAK - that means we've tried this a few times already */ + if (result == -ETIMEDOUT) { + US_DEBUGP("-- device NAKed\n"); + return USB_STOR_XFER_ERROR; + } + + /* the catch-all error case */ + if (result) { + US_DEBUGP("-- unknown error\n"); + return USB_STOR_XFER_ERROR; + } + + /* did we send all the data? */ + if (partial == length) { + US_DEBUGP("-- transfer complete\n"); + return USB_STOR_XFER_GOOD; + } + + /* no error code, so we must have transferred some data, + * just not all of it */ + US_DEBUGP("-- transferred only %d bytes\n", partial); + return USB_STOR_XFER_SHORT; +} + +/* * Transfer an entire SCSI command's worth of data payload over the bulk * pipe. * - * Note that this uses usb_stor_bulk_transfer_buf to achieve its goals -- - * this function simply determines if we're going to use scatter-gather or not, - * and acts appropriately. + * Nore that this uses the usb_stor_bulk_transfer_buf() and + * usb_stor_bulk_transfer_sglist() to achieve its goals -- + * this function simply determines whether we're going to use + * scatter-gather or not, and acts apropriately. */ int usb_stor_bulk_transfer_sg(struct us_data* us, unsigned int pipe, char *buf, unsigned int length_left, int use_sg, int *residual) { - int i; - int result = USB_STOR_XFER_ERROR; - struct scatterlist *sg; - unsigned int amount; + int result; unsigned int partial; /* are we scatter-gathering? */ if (use_sg) { - - /* loop over all the scatter gather structures and - * make the appropriate requests for each, until done - */ - sg = (struct scatterlist *) buf; - for (i = 0; (i < use_sg) && (length_left > 0); (i++, ++sg)) { - - /* transfer the lesser of the next buffer or the - * remaining data */ - amount = sg->length < length_left ? - sg->length : length_left; - result = usb_stor_bulk_transfer_buf(us, pipe, - sg_address(*sg), amount, &partial); - length_left -= partial; - - /* if we get an error, end the loop here */ - if (result != USB_STOR_XFER_GOOD) - break; - } + /* use the usb core scatter-gather primitives */ + result = usb_stor_bulk_transfer_sglist(us, pipe, + (struct scatterlist *) buf, use_sg, + length_left, &partial); + length_left -= partial; } else { /* no scatter-gather, just make the request */ result = usb_stor_bulk_transfer_buf(us, pipe, buf, @@ -924,6 +988,12 @@ if (test_and_clear_bit(US_FLIDX_CAN_CANCEL, &us->flags)) { 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_CANCEL_SG, &us->flags)) { + US_DEBUGP("-- cancelling sg request\n"); + usb_sg_cancel(us->current_sg); } /* If we are waiting for an IRQ, simulate it */ diff -Nru a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h --- a/drivers/usb/storage/transport.h Mon Oct 28 13:51:29 2002 +++ b/drivers/usb/storage/transport.h Mon Oct 28 13:51:29 2002 @@ -164,6 +164,9 @@ void *data, u16 size); extern int usb_stor_bulk_transfer_buf(struct us_data *us, unsigned int pipe, char *buf, unsigned int length, unsigned int *act_len); +extern int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe, + struct scatterlist *sg, int num_sg, unsigned int length, + unsigned int *act_len); extern int usb_stor_bulk_transfer_sg(struct us_data *us, unsigned int pipe, char *buf, unsigned int length, int use_sg, int *residual); diff -Nru a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c --- a/drivers/usb/storage/usb.c Mon Oct 28 13:51:29 2002 +++ b/drivers/usb/storage/usb.c Mon Oct 28 13:51:29 2002 @@ -547,6 +547,13 @@ return 2; } + US_DEBUGP("Allocating scatter-gather request block\n"); + ss->current_sg = kmalloc(sizeof(*ss->current_sg), GFP_KERNEL); + if (!ss->current_sg) { + US_DEBUGP("allocation failed\n"); + return 5; + } + /* allocate the IRQ URB, if it is needed */ if (ss->protocol == US_PR_CBI) { US_DEBUGP("Allocating IRQ for CBI transport\n"); @@ -606,6 +613,12 @@ ss->irq_urb = NULL; } up(&(ss->irq_urb_sem)); + + /* free the scatter-gather request block */ + if (ss->current_sg) { + kfree(ss->current_sg); + ss->current_sg = NULL; + } /* free up the main URB for this device */ if (ss->current_urb) { diff -Nru a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h --- a/drivers/usb/storage/usb.h Mon Oct 28 13:51:29 2002 +++ b/drivers/usb/storage/usb.h Mon Oct 28 13:51:29 2002 @@ -107,6 +107,7 @@ #define US_FL_DEV_ATTACHED 0x00010000 /* is the device attached? */ #define US_FLIDX_IP_WANTED 17 /* 0x00020000 is an IRQ expected? */ #define US_FLIDX_CAN_CANCEL 18 /* 0x00040000 okay to cancel current_urb? */ +#define US_FLIDX_CANCEL_SG 19 /* 0x00080000 okay to cancel current_sg? */ /* processing state machine states */ @@ -184,6 +185,7 @@ /* control and bulk communications data */ struct urb *current_urb; /* non-int USB requests */ struct usb_ctrlrequest *dr; /* control requests */ + struct usb_sg_request *current_sg; /* scatter-gather USB */ /* the semaphore for sleeping the control thread */ struct semaphore sema; /* to sleep thread on */