Skip to contentSkip to conversation

USB on PPC440GP (cache incoherent)

PATCH Roland Dreier 3 participants 6 messages Jun 7, 2002 – Jun 8, 2002

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

I just got USB working on my PPC440GP (IBM Ebony eval board). I'm using an Opti 82C861 OHCI controller, and so far I've tried an Alcor SD card reader (mass storage) and a Belkin usbnet device. My kernel is 2.4.19-pre10 from the bk://ppc.bkbits.net/linuxppc_2_4_devel BitKeeper tree.

I had to make some changes to the USB driver to get this working as there are still some places where structures on the stack are being used for DMA. This causes problems on processors like the 440GP, which are not cache coherent (you can only invalidate a whole cache line, which can corrupt the stack). I just changed all of these places to use kmalloc to get memory instead.

Note that this might not work perfectly on all cache-incoherent processors, since kmalloc could potentially allocate a chunk of memory that is smaller than the processor's cache line size. However it is safe on the 440GP since the 440GP's cache line size is 32 bytes.

I've included a patch with my changes in this email. I'm sending it to the linuxppc-embedded list in case someone else is trying to do USB on a 440 or 405 or similar processor. I'm also sending it to linux-usb-devel in the hope that these changes make it into the mainline kernel.

Best,
Roland

# This is a BitKeeper generated patch for the following project:
# Project Name: Linux 2.4 for PowerPC development tree
# This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas:

#	           ChangeSet	1.1077  -> 1.1078
#	   drivers/usb/usb.c	1.19    -> 1.20
#	   drivers/usb/hub.c	1.14    -> 1.15
#	   drivers/usb/hub.h	1.7     -> 1.8

# drivers/usb/storage/transport.c 1.10 -> 1.11
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/06/07 [email protected] 1.1078
# Fix problems in USB when running on cache-incoherent cpus like PPC440GP # (Don't do DMAs into memory allocated on stack)
# --------------------------------------------
#

drivers/usb/hub.c +62 -31
diff -Nru a/drivers/usb/hub.c b/drivers/usb/hub.c
@@ -155,7 +155,7 @@
 static int usb_hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint)
 {
 	struct usb_device *dev = hub->dev;
-	struct usb_hub_status hubstatus;
+	struct usb_hub_status *hubstatus;
 	char portstr[USB_MAXCHILDREN + 1];
 	unsigned int pipe;
 	int i, maxp, ret;
@@ -258,27 +258,36 @@
 
 	dbg("port removable status: %s", portstr);
 
-	ret = usb_get_hub_status(dev, &hubstatus);
+        hubstatus = kmalloc(sizeof *hubstatus, GFP_KERNEL);
+        if (!hubstatus) {
+                err("Unable to allocate hubstatus");
+                kfree(hub->descriptor);
+                return -1;
+        }
+	ret = usb_get_hub_status(dev, hubstatus);
 	if (ret < 0) {
 		err("Unable to get hub status (err = %d)", ret);
+                kfree(hubstatus);
 		kfree(hub->descriptor);
 		return -1;
 	}
 
-	le16_to_cpus(&hubstatus.wHubStatus);
+	le16_to_cpus(&hubstatus->wHubStatus);
 
 	dbg("local power source is %s",
-		(hubstatus.wHubStatus & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good");
+		(hubstatus->wHubStatus & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good");
 
 	dbg("%sover-current condition exists",
-		(hubstatus.wHubStatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");
+		(hubstatus->wHubStatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");
+
+        kfree(hubstatus);
 
 	/* Start the interrupt endpoint */
 	pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
 	maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
 
-	if (maxp > sizeof(hub->buffer))
-		maxp = sizeof(hub->buffer);
+	if (maxp > USB_HUB_BUFFER_SIZE)
+		maxp = USB_HUB_BUFFER_SIZE;
 
 	hub->urb = usb_alloc_urb(0);
 	if (!hub->urb) {
@@ -357,6 +366,13 @@
 
 	memset(hub, 0, sizeof(*hub));
 
+        hub->buffer = kmalloc(USB_HUB_BUFFER_SIZE, GFP_KERNEL);
+        if (!hub->buffer) {
+                err("couldn't kmalloc hub->buffer");
+                kfree(hub);
+                return NULL;
+        }
+
 	INIT_LIST_HEAD(&hub->event_list);
 	hub->dev = dev;
 	init_MUTEX(&hub->khubd_sem);
@@ -383,6 +399,7 @@
 
 	spin_unlock_irqrestore(&hub_event_lock, flags);
 
+        kfree(hub->buffer);
 	kfree(hub);
 
 	return NULL;
@@ -417,6 +434,11 @@
 		hub->descriptor = NULL;
 	}
 
+        if (hub->buffer) {
+                kfree(hub->buffer);
+                hub->buffer = NULL;
+        }
+
 	/* Free the memory */
 	kfree(hub);
 }
@@ -782,7 +804,7 @@
 	struct list_head *tmp;
 	struct usb_device *dev;
 	struct usb_hub *hub;
-	struct usb_hub_status hubsts;
+	struct usb_hub_status *hubsts;
 	u16 hubstatus;
 	u16 hubchange;
 	u16 portstatus;
@@ -872,22 +894,28 @@
 		} /* end for i */
 
 		/* deal with hub status changes */
-		if (usb_get_hub_status(dev, &hubsts) < 0)
-			err("get_hub_status failed");
-		else {
-			hubstatus = le16_to_cpup(&hubsts.wHubStatus);
-			hubchange = le16_to_cpup(&hubsts.wHubChange);
-			if (hubchange & HUB_CHANGE_LOCAL_POWER) {
-				dbg("hub power change");
-				usb_clear_hub_feature(dev, C_HUB_LOCAL_POWER);
-			}
-			if (hubchange & HUB_CHANGE_OVERCURRENT) {
-				dbg("hub overcurrent change");
-				wait_ms(500);	/* Cool down */
-				usb_clear_hub_feature(dev, C_HUB_OVER_CURRENT);
-                        	usb_hub_power_on(hub);
-			}
+                hubsts = kmalloc(sizeof *hubsts, GFP_KERNEL);
+                if (!hubsts) {
+                        err("couldn't allocate hubsts");
+                } else {
+                        if (usb_get_hub_status(dev, hubsts) < 0)
+                                err("get_hub_status failed");
+                        else {
+                                hubstatus = le16_to_cpup(&hubsts->wHubStatus);
+                                hubchange = le16_to_cpup(&hubsts->wHubChange);
+                                if (hubchange & HUB_CHANGE_LOCAL_POWER) {
+                                        dbg("hub power change");
+                                        usb_clear_hub_feature(dev, C_HUB_LOCAL_POWER);
+                                }
+                                if (hubchange & HUB_CHANGE_OVERCURRENT) {
+                                        dbg("hub overcurrent change");
+                                        wait_ms(500);	/* Cool down */
+                                        usb_clear_hub_feature(dev, C_HUB_OVER_CURRENT);
+                                        usb_hub_power_on(hub);
+                                }
+                        }
 		}
+                kfree(hubsts);
 		up(&hub->khubd_sem);
         } /* end while (1) */
 
@@ -995,7 +1023,7 @@
 int usb_reset_device(struct usb_device *dev)
 {
 	struct usb_device *parent = dev->parent;
-	struct usb_device_descriptor descriptor;
+	struct usb_device_descriptor *descriptor;
 	int i, ret, port = -1;
 
 	if (!parent) {
@@ -1044,17 +1072,19 @@
 	 * If nothing changed, we reprogram the configuration and then
 	 * the alternate settings.
 	 */
-	ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &descriptor,
-			sizeof(descriptor));
+        descriptor = kmalloc(sizeof *descriptor, GFP_KERNEL);
+	ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, descriptor,
+                                 sizeof *descriptor);
 	if (ret < 0)
 		return ret;
 
-	le16_to_cpus(&descriptor.bcdUSB);
-	le16_to_cpus(&descriptor.idVendor);
-	le16_to_cpus(&descriptor.idProduct);
-	le16_to_cpus(&descriptor.bcdDevice);
+	le16_to_cpus(&descriptor->bcdUSB);
+	le16_to_cpus(&descriptor->idVendor);
+	le16_to_cpus(&descriptor->idProduct);
+	le16_to_cpus(&descriptor->bcdDevice);
 
-	if (memcmp(&dev->descriptor, &descriptor, sizeof(descriptor))) {
+	if (memcmp(&dev->descriptor, descriptor, sizeof *descriptor)) {
+                kfree(descriptor);
 		usb_destroy_configuration(dev);
 
 		ret = usb_get_device_descriptor(dev);
@@ -1083,6 +1113,7 @@
 
 		return 1;
 	}
+        kfree(descriptor);
 
 	ret = usb_set_configuration(dev, dev->actconfig->bConfigurationValue);
 	if (ret < 0) {
drivers/usb/hub.h +4 -2
diff -Nru a/drivers/usb/hub.h b/drivers/usb/hub.h
@@ -120,13 +120,15 @@
 
 struct usb_device;
 
+/* add 1 bit for hub status change and add 7 bits to round up to byte boundary */
+#define USB_HUB_BUFFER_SIZE ((USB_MAXCHILDREN + 1 + 7) / 8)
+
 struct usb_hub {
 	struct usb_device *dev;
 
 	struct urb *urb;		/* Interrupt polling pipe */
 
-	char buffer[(USB_MAXCHILDREN + 1 + 7) / 8]; /* add 1 bit for hub status change */
-					/* and add 7 bits to round up to byte boundary */
+        char *buffer;
 	int error;
 	int nerrors;
 
drivers/usb/storage/transport.c +97 -50

271 lines not on this page · raw message

drivers/usb/usb.c +9 -2
diff -Nru a/drivers/usb/usb.c b/drivers/usb/usb.c
@@ -1787,16 +1787,23 @@
 {
 	int i = 5;
 	int result;
+        void *tmp_buf;
 
-	memset(buf,0,size);	// Make sure we parse really received data
+        tmp_buf = kmalloc(size, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+        if (!tmp_buf) {
+          return -ENOMEM;
+        }
+	memset(tmp_buf,0,size);	// Make sure we parse really received data
 
 	while (i--) {
 		if ((result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
 			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
-			(type << 8) + index, 0, buf, size, HZ * GET_TIMEOUT)) > 0 ||
+			(type << 8) + index, 0, tmp_buf, size, HZ * GET_TIMEOUT)) > 0 ||
 		     result == -EPIPE)
 			break;	/* retry if the returned length was 0; flaky device */
 	}
+        memcpy(buf, tmp_buf, size);
+        kfree(tmp_buf);
 	return result;
 }
 

** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/

#1 lore raw
Quoted 3 lines · Roland Dreier

I had to make some changes to the USB driver to get this working as there are still some places where structures on the stack are being used for DMA.

Good to have that -- there have periodically been passes made through drivers to fix such problems, evidently at least 2.4 didn't catch all of them. Corresponding changes should get into 2.5 kernels too.

Quoted 4 lines

Note that this might not work perfectly on all cache-incoherent processors, since kmalloc could potentially allocate a chunk of memory that is smaller than the processor's cache line size. However it is safe on the 440GP since the 440GP's cache line size is 32 bytes.

Could you elaborate? Documentation/DMA-mapping.txt says that kmalloc returns data suitable for DMA, you are saying otherwise. The DMA mapping calls are supposed to handle cache flushing as needed. If they don't, a lot of code will be breaking ...

drivers/usb/hub.h line 133

Quoted 10 lines

struct usb_hub {

drivers/usb/hub.h +1 -2
 	struct usb_device *dev;
 
 	struct urb *urb;		/* Interrupt polling pipe */
 
-	char buffer[(USB_MAXCHILDREN + 1 + 7) / 8]; /* add 1 bit for hub status change */
-					/* and add 7 bits to round up to byte boundary */
+        char *buffer;
 	int error;
 	int nerrors;

This hub.h change (and its follow-ons) should not be necessary since the struct usb_hub is already allocated using kmalloc() in hub_probe(), and so it's DMA-ready. (Modulo the spec issue noted above for DMA-mapping.txt of course!)

- Dave

** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/

#2 lore raw
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

"David" == David Brownell <[email protected]> writes:

Roland> Note that this might not work perfectly on all Roland> cache-incoherent processors, since kmalloc could Roland> potentially allocate a chunk of memory that is smaller Roland> than the processor's cache line size. However it is safe Roland> on the 440GP since the 440GP's cache line size is 32 Roland> bytes.

David> Could you elaborate? Documentation/DMA-mapping.txt says David> that kmalloc returns data suitable for DMA, you are saying David> otherwise. The DMA mapping calls are supposed to handle David> cache flushing as needed. If they don't, a lot of code David> will be breaking ...

I don't know for a fact that there is an architecture where it breaks, but the idea is that for the 440GP, pci_map_single() with PCI_DMA_FROMDEVICE invalidates the cache for the memory it is mapping. At least on the 440GP, you can only invalidate entire cache lines, which means that if the buffer you are using is smaller than a cache line then you can get memory corruption. The 440GP does not flush the cache for PCI_DMA_FROMDEVICE, and even if it did, you could still have problems if you access the same cache line as the buffer is in before the DMA completes.

The smallest buffer that kmalloc() will give you is 32 bytes. If there is an architecture with cache lines bigger than 32 bytes that is not cache coherent for DMA and only allows entire cache lines to be invalidated, then using a kmalloc'ed buffer for DMA could cause problems if it smaller than a cache line.

David> This hub.h change (and its follow-ons) should not be David> necessary since the struct usb_hub is already allocated David> using kmalloc() in hub_probe(), and so it's DMA-ready. David> (Modulo the spec issue noted above for DMA-mapping.txt of David> course!)

The hub.h change moved the buffer member out of struct usb_hub into its own kmalloc'ed buffer. I don't know that having buffer be part of usb_hub was actually causing problems, but I don't think it was safe for the reasons I described above: buffer was not cache line aligned and was smaller than a cache line, so mixing DMA access into buffer and access to other members of struct usb_hub could cause corruption.

Thanks,
Roland

** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/

#3 lore raw
Quoted 7 lines

David> Could you elaborate? Documentation/DMA-mapping.txt says David> that kmalloc returns data suitable for DMA, you are saying David> otherwise. The DMA mapping calls are supposed to handle David> cache flushing as needed. If they don't, a lot of code David> will be breaking ...

Quoted 6 lines

I don't know for a fact that there is an architecture where it breaks, but the idea is that for the 440GP, pci_map_single() with PCI_DMA_FROMDEVICE invalidates the cache for the memory it is mapping. At least on the 440GP, you can only invalidate entire cache lines, which means that if the buffer you are using is smaller than a cache line then you can get memory corruption.

Or more specifically, cache lines can't be shared between DMA and non-DMA purposes while a DMA mapping is active ... regardless of the size of the buffer, its start and end could potentially have such cacheline problems. That makes good sense to me, and I suspect it'd be worth listing the issue in DMA-mapping.txt. (Assuming the relevant gurus corrroborate... :)

That text might vaguely allude to the issue, it says you can use "the addresses returned from those routines" (kmalloc and friends) ... but of course, kmalloc effectively returns a family of addresses (one for each byte returned). Docs on "What memory is DMA-able" have slowly been improving; I can understand why such caching issues might not yet be well addressed.

Quoted 6 lines

The hub.h change moved the buffer member out of struct usb_hub into its own kmalloc'ed buffer. I don't know that having buffer be part of usb_hub was actually causing problems, but I don't think it was safe for the reasons I described above: buffer was not cache line aligned and was smaller than a cache line, so mixing DMA access into buffer and access to other members of struct usb_hub could cause corruption.

A simpler alternate fix might have been to declare that field as aligned, which I think goes more or less like

	char buffer [...] __attribute__ (aligned (L1_CACHE_BYTES));

and size it accordingly, maybe after rearranging fields. In any case, given the subtlety of this issue I'd want to see a comment mentioning the issue, so it stays safe across many generations of maintainers!

- Dave

** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/

#4 lore raw
In reply to Roland Dreier

Hi,

the basic idea is sound, but ...

Quoted 29 lines

# This is a BitKeeper generated patch for the following project:
# Project Name: Linux 2.4 for PowerPC development tree
# This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas:

#	           ChangeSet	1.1077  -> 1.1078
#	   drivers/usb/usb.c	1.19    -> 1.20
#	   drivers/usb/hub.c	1.14    -> 1.15
#	   drivers/usb/hub.h	1.7     -> 1.8

# drivers/usb/storage/transport.c 1.10 -> 1.11
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/06/07 [email protected] 1.1078
# Fix problems in USB when running on cache-incoherent cpus like PPC440GP # (Don't do DMAs into memory allocated on stack)
# --------------------------------------------
#

drivers/usb/hub.c +1 -1
@@ -995,7 +1023,7 @@
 int usb_reset_device(struct usb_device *dev)
 {
 	struct usb_device *parent = dev->parent;
-	struct usb_device_descriptor descriptor;
+	struct usb_device_descriptor *descriptor;
 	int i, ret, port = -1;
 
 	if (!parent) {

drivers/usb/hub.c line 1075

Quoted 6 lines
drivers/usb/hub.c +1 -2
@@ -1044,17 +1072,19 @@
 	 * If nothing changed, we reprogram the configuration and then
 	 * the alternate settings.
 	 */
-	ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &descriptor,
-			sizeof(descriptor));
+        descriptor = kmalloc(sizeof *descriptor, GFP_KERNEL);

This can be used in error handling by storage devices. You must use GFP_NOIO. And you should check for a failure due to OOM.

Quoted 27 lines
drivers/usb/hub.c +8 -5
+	ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, descriptor,
+                                 sizeof *descriptor);
 	if (ret < 0)
 		return ret;
 
-	le16_to_cpus(&descriptor.bcdUSB);
-	le16_to_cpus(&descriptor.idVendor);
-	le16_to_cpus(&descriptor.idProduct);
-	le16_to_cpus(&descriptor.bcdDevice);
+	le16_to_cpus(&descriptor->bcdUSB);
+	le16_to_cpus(&descriptor->idVendor);
+	le16_to_cpus(&descriptor->idProduct);
+	le16_to_cpus(&descriptor->bcdDevice);
 
-	if (memcmp(&dev->descriptor, &descriptor, sizeof(descriptor))) {
+	if (memcmp(&dev->descriptor, descriptor, sizeof *descriptor)) {
+                kfree(descriptor);
 		usb_destroy_configuration(dev);
 
 		ret = usb_get_device_descriptor(dev);

diff -Nru a/drivers/usb/storage/transport.c
b/drivers/usb/storage/transport.c --- a/drivers/usb/storage/transport.c Fri

Jun  7 16:35:31 2002

+++ b/drivers/usb/storage/transport.c Fri Jun 7 16:35:31 2002

drivers/usb/storage/transport.c line 727

Quoted 6 lines
drivers/usb/storage/transport.c +1 -1
@@ -723,7 +723,7 @@
 
 		/* use the new buffer we have */
 		old_request_buffer = srb->request_buffer;
-		srb->request_buffer = srb->sense_buffer;
+		srb->request_buffer = kmalloc(18, in_interrupt() ? GFP_ATOMIC :
 GFP_KERNEL);

Must be "? GFP_ATOMIC : GFP_NOIO" or you could deadlock. However, why do you do this ? The srb is kmalloced.

Quoted 13 lines
drivers/usb/storage/transport.c +2 -2
 		/* set the buffer length for transfer */
 		old_request_bufflen = srb->request_bufflen;
@@ -1077,41 +1088,54 @@
 
 int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
 {
-	struct bulk_cb_wrap bcb;
-	struct bulk_cs_wrap bcs;
+	struct bulk_cb_wrap *bcb;
+	struct bulk_cs_wrap *bcs;
 	int result;

drivers/usb/storage/transport.c line 1103

Quoted 10 lines
drivers/usb/storage/transport.c +6 -0
 	int pipe;
 	int partial;
+        int ret = USB_STOR_TRANSPORT_ERROR;
+
+        bcb = kmalloc(sizeof *bcb, in_interrupt() ? GFP_ATOMIC :
 GFP_KERNEL); +        if (!bcb) {
+                return USB_STOR_TRANSPORT_ERROR;
+        }
+        bcs = kmalloc(sizeof *bcs, in_interrupt() ? GFP_ATOMIC :
 GFP_KERNEL); +        if (!bcs) {

Here again you cannot use GFP_KERNEL. It must be GFP_NOIO instead.

Quoted 59 lines
drivers/usb/storage/transport.c +20 -15
+                kfree(bcb);
+                return USB_STOR_TRANSPORT_ERROR;
+        }
 
 	/* set up the command wrapper */
-	bcb.Signature = cpu_to_le32(US_BULK_CB_SIGN);
-	bcb.DataTransferLength = cpu_to_le32(usb_stor_transfer_length(srb));
-	bcb.Flags = srb->sc_data_direction == SCSI_DATA_READ ? 1 << 7 : 0;
-	bcb.Tag = srb->serial_number;
-	bcb.Lun = srb->cmnd[1] >> 5;
+	bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
+	bcb->DataTransferLength = cpu_to_le32(usb_stor_transfer_length(srb));
+	bcb->Flags = srb->sc_data_direction == SCSI_DATA_READ ? 1 << 7 : 0;
+	bcb->Tag = srb->serial_number;
+	bcb->Lun = srb->cmnd[1] >> 5;
 	if (us->flags & US_FL_SCM_MULT_TARG)
-		bcb.Lun |= srb->target << 4;
-	bcb.Length = srb->cmd_len;
+		bcb->Lun |= srb->target << 4;
+	bcb->Length = srb->cmd_len;
 
 	/* construct the pipe handle */
 	pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
 
 	/* copy the command payload */
-	memset(bcb.CDB, 0, sizeof(bcb.CDB));
-	memcpy(bcb.CDB, srb->cmnd, bcb.Length);
+	memset(bcb->CDB, 0, sizeof(bcb->CDB));
+	memcpy(bcb->CDB, srb->cmnd, bcb->Length);
 
 	/* send it to out endpoint */
 	US_DEBUGP("Bulk command S 0x%x T 0x%x Trg %d LUN %d L %d F %d CL %d\n",
-		  le32_to_cpu(bcb.Signature), bcb.Tag,
-		  (bcb.Lun >> 4), (bcb.Lun & 0x0F),
-		  bcb.DataTransferLength, bcb.Flags, bcb.Length);
-	result = usb_stor_bulk_msg(us, &bcb, pipe, US_BULK_CB_WRAP_LEN,
+		  le32_to_cpu(bcb->Signature), bcb->Tag,
+		  (bcb->Lun >> 4), (bcb->Lun & 0x0F),
+		  bcb->DataTransferLength, bcb->Flags, bcb->Length);
+	result = usb_stor_bulk_msg(us, bcb, pipe, US_BULK_CB_WRAP_LEN,
 				   &partial);
 	US_DEBUGP("Bulk command transfer result=%d\n", result);
 
 	/* if the command was aborted, indicate that */
-	if (result == -ENOENT)
-		return USB_STOR_TRANSPORT_ABORTED;
+	if (result == -ENOENT) {
+                ret = USB_STOR_TRANSPORT_ABORTED;
+                goto out;
+        }
 
 	/* if we stall, we need to clear it before we go on */
 	if (result == -EPIPE) {

diff -Nru a/drivers/usb/usb.c b/drivers/usb/usb.c

drivers/usb/usb.c line 1792

Quoted 7 lines
drivers/usb/usb.c +2 -1
@@ -1787,16 +1787,23 @@
 {
 	int i = 5;
 	int result;
+        void *tmp_buf;
 
-	memset(buf,0,size);	// Make sure we parse really received data
+        tmp_buf = kmalloc(size, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);

usb_control_msg is used. This cannot be called in interrupt.

Quoted 7 lines
drivers/usb/usb.c +4 -0
+        if (!tmp_buf) {
+          return -ENOMEM;
+        }
+	memset(tmp_buf,0,size);	// Make sure we parse really received data
 
 	while (i--) {
 		if ((result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),

drivers/usb/usb.c line 1808

Quoted 10 lines
drivers/usb/usb.c +3 -1
 			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
-			(type << 8) + index, 0, buf, size, HZ * GET_TIMEOUT)) > 0 ||
+			(type << 8) + index, 0, tmp_buf, size, HZ * GET_TIMEOUT)) > 0 ||
 		     result == -EPIPE)
 			break;	/* retry if the returned length was 0; flaky device */
 	}
+        memcpy(buf, tmp_buf, size);
+        kfree(tmp_buf);
 	return result;
 }

HTH
Oliver

** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/

#5 lore raw
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

"Oliver" == Oliver Neukum <[email protected]> writes:

Oliver> This can be used in error handling by storage devices. You Oliver> must use GFP_NOIO. And you should check for a failure due Oliver> to OOM.

Yep, I caught those missed checks for allocation failure. And I will change to using GFP_NOIO where appropriate.

    Roland> -		srb->request_buffer = srb->sense_buffer;
    Roland> +		srb->request_buffer = kmalloc(18, in_interrupt() ? GFP_ATOMIC :

    Oliver> However, why do you do this ? The srb is kmalloced.

Yes, but srb->sense_buffer is not aligned on a cache line boundary. My feeling is that this could corrupt the rest of the cache line, though others have questioned whether this is strictly necessary. I just posted a question on lkml to try to and get an authoritative answer on DMA into unaligned buffers is OK.

Thanks for the comments,
Roland

** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/