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   */