Commit 6c389fc9 authored by Zefir Kurtisi's avatar Zefir Kurtisi Committed by David S. Miller

gianfar: fix size of scatter-gathered frames

The current scatter-gather logic in gianfar is flawed, since
it does not consider the eTSEC's RxBD 'Data Length' field is
context depening: for the last fragment it contains the full
frame size, while fragments contain the fragment size, which
equals the value written to register MRBLR.

This causes data corruption as soon as the hardware starts
to fragment receiving frames. As a result, the size of
fragmented frames is increased by
(nr_frags - 1) * MRBLR

We first noticed this issue working with DSA, where an ICMP
request sized 1472 bytes causes the scatter-gather logic to
kick in. The full Ethernet frame (1518) gets increased by
DSA (4), GMAC_FCB_LEN (8), and FSL_GIANFAR_DEV_HAS_TIMER
(priv->padding=8) to a total of 1538 octets, which is
fragmented by the hardware and reconstructed by the driver
to a 3074 octet frame.

This patch fixes the problem by adjusting the size of
the last fragment.

It was tested by setting MRBLR to different multiples of
64, proving correct scatter-gather operation on frames
with up to 9000 octets in size.
Signed-off-by: default avatarZefir Kurtisi <zefir.kurtisi@neratec.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b323431b
...@@ -2922,17 +2922,25 @@ static bool gfar_add_rx_frag(struct gfar_rx_buff *rxb, u32 lstatus, ...@@ -2922,17 +2922,25 @@ static bool gfar_add_rx_frag(struct gfar_rx_buff *rxb, u32 lstatus,
{ {
unsigned int size = lstatus & BD_LENGTH_MASK; unsigned int size = lstatus & BD_LENGTH_MASK;
struct page *page = rxb->page; struct page *page = rxb->page;
bool last = !!(lstatus & BD_LFLAG(RXBD_LAST));
/* Remove the FCS from the packet length */ /* Remove the FCS from the packet length */
if (likely(lstatus & BD_LFLAG(RXBD_LAST))) if (last)
size -= ETH_FCS_LEN; size -= ETH_FCS_LEN;
if (likely(first)) if (likely(first)) {
skb_put(skb, size); skb_put(skb, size);
else } else {
/* the last fragments' length contains the full frame length */
if (last)
size -= skb->len;
/* in case the last fragment consisted only of the FCS */
if (size > 0)
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
rxb->page_offset + RXBUF_ALIGNMENT, rxb->page_offset + RXBUF_ALIGNMENT,
size, GFAR_RXB_TRUESIZE); size, GFAR_RXB_TRUESIZE);
}
/* try reuse page */ /* try reuse page */
if (unlikely(page_count(page) != 1)) if (unlikely(page_count(page) != 1))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment