| From 2197e020fcec5a90479e11a466efe5f011da0b17 Mon Sep 17 00:00:00 2001 |
| From: Finn Thain <fthain@telegraphics.com.au> |
| Date: Thu, 23 Jan 2020 09:07:26 +1100 |
| Subject: [PATCH] net/sonic: Fix receive buffer handling |
| |
| commit 9e311820f67e740f4fb8dcb82b4c4b5b05bdd1a5 upstream. |
| |
| The SONIC can sometimes advance its rx buffer pointer (RRP register) |
| without advancing its rx descriptor pointer (CRDA register). As a result |
| the index of the current rx descriptor may not equal that of the current |
| rx buffer. The driver mistakenly assumes that they are always equal. |
| This assumption leads to incorrect packet lengths and possible packet |
| duplication. Avoid this by calling a new function to locate the buffer |
| corresponding to a given descriptor. |
| |
| Fixes: efcce839360f ("[PATCH] macsonic/jazzsonic network drivers update") |
| Tested-by: Stan Johnson <userm57@yahoo.com> |
| Signed-off-by: Finn Thain <fthain@telegraphics.com.au> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/net/ethernet/natsemi/sonic.c b/drivers/net/ethernet/natsemi/sonic.c |
| index b31c7af32dfa..c666bbf15116 100644 |
| --- a/drivers/net/ethernet/natsemi/sonic.c |
| +++ b/drivers/net/ethernet/natsemi/sonic.c |
| @@ -413,6 +413,21 @@ static irqreturn_t sonic_interrupt(int irq, void *dev_id) |
| return IRQ_HANDLED; |
| } |
| |
| +/* Return the array index corresponding to a given Receive Buffer pointer. */ |
| +static int index_from_addr(struct sonic_local *lp, dma_addr_t addr, |
| + unsigned int last) |
| +{ |
| + unsigned int i = last; |
| + |
| + do { |
| + i = (i + 1) & SONIC_RRS_MASK; |
| + if (addr == lp->rx_laddr[i]) |
| + return i; |
| + } while (i != last); |
| + |
| + return -ENOENT; |
| +} |
| + |
| /* |
| * We have a good packet(s), pass it/them up the network stack. |
| */ |
| @@ -432,6 +447,16 @@ static void sonic_rx(struct net_device *dev) |
| |
| status = sonic_rda_get(dev, entry, SONIC_RD_STATUS); |
| if (status & SONIC_RCR_PRX) { |
| + u32 addr = (sonic_rda_get(dev, entry, |
| + SONIC_RD_PKTPTR_H) << 16) | |
| + sonic_rda_get(dev, entry, SONIC_RD_PKTPTR_L); |
| + int i = index_from_addr(lp, addr, entry); |
| + |
| + if (i < 0) { |
| + WARN_ONCE(1, "failed to find buffer!\n"); |
| + break; |
| + } |
| + |
| /* Malloc up new buffer. */ |
| new_skb = netdev_alloc_skb(dev, SONIC_RBSIZE + 2); |
| if (new_skb == NULL) { |
| @@ -453,7 +478,7 @@ static void sonic_rx(struct net_device *dev) |
| |
| /* now we have a new skb to replace it, pass the used one up the stack */ |
| dma_unmap_single(lp->device, lp->rx_laddr[entry], SONIC_RBSIZE, DMA_FROM_DEVICE); |
| - used_skb = lp->rx_skb[entry]; |
| + used_skb = lp->rx_skb[i]; |
| pkt_len = sonic_rda_get(dev, entry, SONIC_RD_PKTLEN); |
| skb_trim(used_skb, pkt_len); |
| used_skb->protocol = eth_type_trans(used_skb, dev); |
| @@ -462,13 +487,13 @@ static void sonic_rx(struct net_device *dev) |
| lp->stats.rx_bytes += pkt_len; |
| |
| /* and insert the new skb */ |
| - lp->rx_laddr[entry] = new_laddr; |
| - lp->rx_skb[entry] = new_skb; |
| + lp->rx_laddr[i] = new_laddr; |
| + lp->rx_skb[i] = new_skb; |
| |
| bufadr_l = (unsigned long)new_laddr & 0xffff; |
| bufadr_h = (unsigned long)new_laddr >> 16; |
| - sonic_rra_put(dev, entry, SONIC_RR_BUFADR_L, bufadr_l); |
| - sonic_rra_put(dev, entry, SONIC_RR_BUFADR_H, bufadr_h); |
| + sonic_rra_put(dev, i, SONIC_RR_BUFADR_L, bufadr_l); |
| + sonic_rra_put(dev, i, SONIC_RR_BUFADR_H, bufadr_h); |
| } else { |
| /* This should only happen, if we enable accepting broken packets. */ |
| } |
| diff --git a/drivers/net/ethernet/natsemi/sonic.h b/drivers/net/ethernet/natsemi/sonic.h |
| index 9e4ff8dd032d..e6d47e45c5c2 100644 |
| --- a/drivers/net/ethernet/natsemi/sonic.h |
| +++ b/drivers/net/ethernet/natsemi/sonic.h |
| @@ -275,8 +275,9 @@ |
| #define SONIC_NUM_RDS SONIC_NUM_RRS /* number of receive descriptors */ |
| #define SONIC_NUM_TDS 16 /* number of transmit descriptors */ |
| |
| -#define SONIC_RDS_MASK (SONIC_NUM_RDS-1) |
| -#define SONIC_TDS_MASK (SONIC_NUM_TDS-1) |
| +#define SONIC_RRS_MASK (SONIC_NUM_RRS - 1) |
| +#define SONIC_RDS_MASK (SONIC_NUM_RDS - 1) |
| +#define SONIC_TDS_MASK (SONIC_NUM_TDS - 1) |
| |
| #define SONIC_RBSIZE 1520 /* size of one resource buffer */ |
| |
| -- |
| 2.7.4 |
| |