diff mbox

Problem with non aligned DMA in usbnet on ARM

Message ID 4C642AA9.1060404@parrot.com
State Not Applicable, archived
Delegated to: David Miller
Headers show

Commit Message

Matthieu CASTET Aug. 12, 2010, 5:08 p.m. UTC
Martin Fuzzey a écrit :
>> We don't want to add support for this to DMA bounce.  DMA bounce is already
>> a pain in the backside and causes its own set of problems - please let it
>> die a long slow but quite death.
>>
>> If you want to see the kind of pain dmabounce causes, look at this long
>> standing and as yet unsolved bug:
>>
>>   http://bugzilla.kernel.org/show_bug.cgi?id=7760
>>
>>   
> Well I don't know the dmabounce code but why is using it likely to cause
> OOM problems (at least why more so than copying the buffer in the HCD or
> the usb core). In both cases there will be two copies of the buffer in
> memory which could I agree be a problem in memory constrained systems.
> But if we _do_ want to accept unaligned buffers from usb drivers I can't
> see  a way round that.
> 
memmove is our friend :
the buffer allocated in usbnet got an offset.
All you have to do it remove this offset and memmove the data. That what 
I did [1], and why it is better to do it in usb driver.


Matthieu




[1] http://article.gmane.org/gmane.linux.usb.general/28700

Comments

Martin Fuzzey Aug. 13, 2010, 10:06 a.m. UTC | #1
Matthieu CASTET wrote:
> memmove is our friend :
> the buffer allocated in usbnet got an offset.
> All you have to do it remove this offset and memmove the data. That
> what I did [1], and why it is better to do it in usb driver.
>
>
> [1] http://article.gmane.org/gmane.linux.usb.general/28700
The problem I see with this approach is that it requires the usb driver
to be aware of the device controller's quirks  (hence the
gadget_dma32()) your patch adds. That appears to be the norm (and maybe
unavoidable) on the gadget side but on the host side things aren't
supposed to work like that.

The expectation for USB hosts is that any properly written usb driver
will work with any properly written host controller driver. I don't
think it's reasonable to expect driver developers to test their code
with all the HCDs, nor expect HCD developers to test their code with all
the drivers.

So I think a policy needs to be defined to ensure this and enforced in
the code. I can see two possible methods:

1) Require that usb drivers submit buffers obtained from kmalloc() and
friends with no extra offsets. If they want some other alignment later
they can use memmove in the completion handler. Enforce this in the core
by checking the buffer pointers are aligned to ARCH_KMALLOC_MINALIGN

or

2) Require that usb_submit_urb() accept byte aligned buffers. Enforce
this by a new test in usbtest (which all HCDs are expected to pass).
Implement it either in individual HCDs that require it or by letting
HCDs inform the core of their requirements and have the core do the
alignment (as it already does the dma mapping). Of course HCDs that can
implement byte aligned transfers (either natively or using tricks such
as the one Russell suggested) should do so.

I think 2) is the better solution because:
a) Solution 1 will impose a runtime overhead even on platforms / HCDs
that don't need it (including the most common cases)
b) There are more drivers than HCDs

Martin


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Oliver Neukum Aug. 13, 2010, 10:58 a.m. UTC | #2
Am Freitag, 13. August 2010, 12:06:54 schrieb Martin Fuzzey:

> So I think a policy needs to be defined to ensure this and enforced in
> the code. I can see two possible methods:
> 
> 1) Require that usb drivers submit buffers obtained from kmalloc() and
> friends with no extra offsets. If they want some other alignment later
> they can use memmove in the completion handler. Enforce this in the core
> by checking the buffer pointers are aligned to ARCH_KMALLOC_MINALIGN
> 
> or
> 
> 2) Require that usb_submit_urb() accept byte aligned buffers. Enforce
> this by a new test in usbtest (which all HCDs are expected to pass).
> Implement it either in individual HCDs that require it or by letting
> HCDs inform the core of their requirements and have the core do the
> alignment (as it already does the dma mapping). Of course HCDs that can
> implement byte aligned transfers (either natively or using tricks such
> as the one Russell suggested) should do so.
> 
> I think 2) is the better solution because:
> a) Solution 1 will impose a runtime overhead even on platforms / HCDs
> that don't need it (including the most common cases)
> b) There are more drivers than HCDs

Yes. But it doesn't prevent you from publishing this information so
drivers can help the lower levels.
	Regards
		Oliver
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Brownell Aug. 13, 2010, 1:42 p.m. UTC | #3
> Subject: Re: Problem with non aligned DMA in usbnet on ARM

I remain unconvinced the bug is anywhere except
in whatever host controller is rejecting the
buffers it's given.

Does anyone have proof the bug is elsewhere?


Think for a moment what chaos the kernel would be
if arbitrary subsystems were allowed to introduce
random DMA alignment "requirements".  It would not
be possible to pass buffers between subsystems with
any success at all...

> > 1) Require that usb drivers submit buffers obtained
> from kmalloc() and > friends

Requirements are already documented with the
Description of what memory is DMA-able.  It's
not kmalloc() specifically that's required.

 with no extra offsets.

Offsets have nothing to do with being DMA-able.
They might have to do with working around hardware
flaws though.  (ISTR PXA255 also had a 32-bit goof,
making its DMA engine unusable for most purposes.)

 
> > 2) Require that usb_submit_urb() accept byte aligned buffers.

I believe we already require that.  Of course,
the requirement is on HCDs, not on that call.

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Oliver Neukum Aug. 13, 2010, 1:53 p.m. UTC | #4
Am Freitag, 13. August 2010, 15:42:32 schrieb David Brownell:
> > > 2) Require that usb_submit_urb() accept byte aligned buffers.
> 
> I believe we already require that.  Of course,
> the requirement is on HCDs, not on th

Divers fail if HCDs can't do it. But we've never explicitly said that HCDs
need to be able to do that. We should probably document that requirement.

	Regards
		Oliver
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index 1edbc12..ed3ee67 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -214,4 +214,14 @@  static inline bool gadget_supports_altsettings(struct usb_gadget *gadget)
 	return true;
 }
 
+/**
+ * gadget_dma32 - return true if we want buffer aligned on 32 bits (for dma)
+ * @gadget: the gadget in question
+ */
+static inline bool gadget_dma32(struct usb_gadget *gadget)
+{
+	if (gadget_is_musbhdrc(gadget))
+		return true;
+	return false;
+}
 #endif /* __GADGET_CHIPS_H */
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 84ca195..697af90 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -249,7 +249,12 @@  rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
 	 * but on at least one, checksumming fails otherwise.  Note:
 	 * RNDIS headers involve variable numbers of LE32 values.
 	 */
-	skb_reserve(skb, NET_IP_ALIGN);
+	/*
+	 * RX: Do not move data by IP_ALIGN:
+	 * if your DMA controller cannot handle it
+	 */
+	if (!gadget_dma32(dev->gadget))
+		skb_reserve(skb, NET_IP_ALIGN);
 
 	req->buf = skb->data;
 	req->length = size;
@@ -282,6 +287,12 @@  static void rx_complete(struct usb_ep *ep, struct usb_request *req)
 	/* normal completion */
 	case 0:
 		skb_put(skb, req->actual);
+		if (gadget_dma32(dev->gadget) && NET_IP_ALIGN) {
+			u8 *data = skb->data;
+			size_t len = skb_headlen(skb);
+			skb_reserve(skb, NET_IP_ALIGN);
+			memmove(skb->data, data, len);
+		}
 
 		if (dev->unwrap) {
 			unsigned long	flags;
@@ -573,6 +584,24 @@  static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
 
 		length = skb->len;
 	}
+
+	/*
+	 * Align data to 32bit if the dma controller requires it
+	 */
+	if (gadget_dma32(dev->gadget)) {
+		unsigned long align = (unsigned long)skb->data & 3;
+		if (WARN_ON(skb_headroom(skb) < align)) {
+			dev_kfree_skb_any(skb);
+			goto drop;
+		} else if (align) {
+			u8 *data = skb->data;
+			size_t len = skb_headlen(skb);
+			skb->data -= align;
+			memmove(skb->data, data, len);
+			skb_set_tail_pointer(skb, len);
+		}
+	}
+
 	req->buf = skb->data;
 	req->context = skb;
 	req->complete = tx_complete;