]> git.itanic.dy.fi Git - linux-stable/commitdiff
net/sonic: Fix receive buffer handling
authorFinn Thain <fthain@telegraphics.com.au>
Wed, 22 Jan 2020 22:07:26 +0000 (09:07 +1100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 5 Feb 2020 13:03:45 +0000 (13:03 +0000)
[ Upstream commit 9e311820f67e740f4fb8dcb82b4c4b5b05bdd1a5 ]

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: Sasha Levin <sashal@kernel.org>
drivers/net/ethernet/natsemi/sonic.c
drivers/net/ethernet/natsemi/sonic.h

index 0374e834f865e5d6fef80800ded42482116b5c13..21766ec12ef206690c6e9e19a4e273d05b8feab4 100644 (file)
@@ -423,6 +423,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.
  */
@@ -442,6 +457,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) {
@@ -463,7 +488,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);
@@ -472,13 +497,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. */
                        lp->stats.rx_errors++;
index a009a99c0e5441c32998e785eedea9eaec3ff2bf..d9f8ceb5353a41ce382babb3d0c8eb6c7e81ae39 100644 (file)
 #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 */