diff options
Diffstat (limited to 'target/linux/generic/patches-3.18/760-8139cp-fixes-from-4.3.patch')
-rw-r--r-- | target/linux/generic/patches-3.18/760-8139cp-fixes-from-4.3.patch | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/target/linux/generic/patches-3.18/760-8139cp-fixes-from-4.3.patch b/target/linux/generic/patches-3.18/760-8139cp-fixes-from-4.3.patch new file mode 100644 index 0000000000..de4c12730f --- /dev/null +++ b/target/linux/generic/patches-3.18/760-8139cp-fixes-from-4.3.patch @@ -0,0 +1,367 @@ +commit 41b976414c88016e2c9d9b2f6667ee67a998d388 +Author: David Woodhouse <David.Woodhouse@intel.com> +Date: Wed Sep 23 09:45:31 2015 +0100 + + 8139cp: Dump contents of descriptor ring on TX timeout + + We are seeing unexplained TX timeouts under heavy load. Let's try to get + a better idea of what's going on. + + Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> + Signed-off-by: David S. Miller <davem@davemloft.net> + +commit 7f4c685633e2df9ba10d49a31dda13715745db37 +Author: David Woodhouse <David.Woodhouse@intel.com> +Date: Wed Sep 23 09:45:16 2015 +0100 + + 8139cp: Fix DMA unmapping of transmitted buffers + + The low 16 bits of the 'opts1' field in the TX descriptor are supposed + to still contain the buffer length when the descriptor is handed back to + us. In practice, at least on my hardware, they don't. So stash the + original value of the opts1 field and get the length to unmap from + there. + + There are other ways we could have worked out the length, but I actually + want a stash of the opts1 field anyway so that I can dump it alongside + the contents of the descriptor ring when we suffer a TX timeout. + + Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> + Signed-off-by: David S. Miller <davem@davemloft.net> + +commit 0a5aeee0b79fa99d8e04c98dd4e87d4f52aa497b +Author: David Woodhouse <David.Woodhouse@intel.com> +Date: Wed Sep 23 09:44:57 2015 +0100 + + 8139cp: Reduce duplicate csum/tso code in cp_start_xmit() + + We calculate the value of the opts1 descriptor field in three different + places. With two different behaviours when given an invalid packet to + be checksummed — none of them correct. Sort that out. + + Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> + Signed-off-by: David S. Miller <davem@davemloft.net> + +commit a3b804043f490aeec57d8ca5baccdd35e6250857 +Author: David Woodhouse <David.Woodhouse@intel.com> +Date: Wed Sep 23 09:44:38 2015 +0100 + + 8139cp: Fix TSO/scatter-gather descriptor setup + + When sending a TSO frame in multiple buffers, we were neglecting to set + the first descriptor up in TSO mode. + + Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> + Signed-off-by: David S. Miller <davem@davemloft.net> + +commit 26b0bad6ac3a0167792dc4ffb276c29bc597d239 +Author: David Woodhouse <David.Woodhouse@intel.com> +Date: Wed Sep 23 09:44:06 2015 +0100 + + 8139cp: Fix tx_queued debug message to print correct slot numbers + + After a certain amount of staring at the debug output of this driver, I + realised it was lying to me. + + Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> + Signed-off-by: David S. Miller <davem@davemloft.net> + +commit aaa0062ecf4877a26dea66bee1039c6eaf906c94 +Author: David Woodhouse <David.Woodhouse@intel.com> +Date: Wed Sep 23 09:43:41 2015 +0100 + + 8139cp: Do not re-enable RX interrupts in cp_tx_timeout() + + If an RX interrupt was already received but NAPI has not yet run when + the RX timeout happens, we end up in cp_tx_timeout() with RX interrupts + already disabled. Blindly re-enabling them will cause an IRQ storm. + + (This is made particularly horrid by the fact that cp_interrupt() always + returns that it's handled the interrupt, even when it hasn't actually + done anything. If it didn't do that, the core IRQ code would have + detected the storm and handled it, I'd have had a clear smoking gun + backtrace instead of just a spontaneously resetting router, and I'd have + at *least* two days of my life back. Changing the return value of + cp_interrupt() will be argued about under separate cover.) + + Unconditionally leave RX interrupts disabled after the reset, and + schedule NAPI to check the receive ring and re-enable them. + + Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> + Signed-off-by: David S. Miller <davem@davemloft.net> + +commit 7a8a8e75d505147358b225173e890ada43a267e2 +Author: David Woodhouse <dwmw2@infradead.org> +Date: Fri Sep 18 00:21:54 2015 +0100 + + 8139cp: Call __cp_set_rx_mode() from cp_tx_timeout() + + Unless we reset the RX config, on real hardware I don't seem to receive + any packets after a TX timeout. + + Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> + Signed-off-by: David S. Miller <davem@davemloft.net> + +commit fc27bd115b334e3ebdc682a42a47c3aea2566dcc +Author: David Woodhouse <dwmw2@infradead.org> +Date: Fri Sep 18 00:19:08 2015 +0100 + + 8139cp: Use dev_kfree_skb_any() instead of dev_kfree_skb() in cp_clean_rings() + + This can be called from cp_tx_timeout() with interrupts disabled. + Spotted by Francois Romieu <romieu@fr.zoreil.com> + + Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> + Signed-off-by: David S. Miller <davem@davemloft.net> +diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c +index d79e33b..686334f 100644 +--- a/drivers/net/ethernet/realtek/8139cp.c ++++ b/drivers/net/ethernet/realtek/8139cp.c +@@ -157,6 +157,7 @@ enum { + NWayAdvert = 0x66, /* MII ADVERTISE */ + NWayLPAR = 0x68, /* MII LPA */ + NWayExpansion = 0x6A, /* MII Expansion */ ++ TxDmaOkLowDesc = 0x82, /* Low 16 bit address of a Tx descriptor. */ + Config5 = 0xD8, /* Config5 */ + TxPoll = 0xD9, /* Tell chip to check Tx descriptors for work */ + RxMaxSize = 0xDA, /* Max size of an Rx packet (8169 only) */ +@@ -341,6 +342,7 @@ struct cp_private { + unsigned tx_tail; + struct cp_desc *tx_ring; + struct sk_buff *tx_skb[CP_TX_RING_SIZE]; ++ u32 tx_opts[CP_TX_RING_SIZE]; + + unsigned rx_buf_sz; + unsigned wol_enabled : 1; /* Is Wake-on-LAN enabled? */ +@@ -665,7 +667,7 @@ static void cp_tx (struct cp_private *cp) + BUG_ON(!skb); + + dma_unmap_single(&cp->pdev->dev, le64_to_cpu(txd->addr), +- le32_to_cpu(txd->opts1) & 0xffff, ++ cp->tx_opts[tx_tail] & 0xffff, + PCI_DMA_TODEVICE); + + if (status & LastFrag) { +@@ -733,7 +735,7 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb, + { + struct cp_private *cp = netdev_priv(dev); + unsigned entry; +- u32 eor, flags; ++ u32 eor, opts1; + unsigned long intr_flags; + __le32 opts2; + int mss = 0; +@@ -753,6 +755,21 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb, + mss = skb_shinfo(skb)->gso_size; + + opts2 = cpu_to_le32(cp_tx_vlan_tag(skb)); ++ opts1 = DescOwn; ++ if (mss) ++ opts1 |= LargeSend | ((mss & MSSMask) << MSSShift); ++ else if (skb->ip_summed == CHECKSUM_PARTIAL) { ++ const struct iphdr *ip = ip_hdr(skb); ++ if (ip->protocol == IPPROTO_TCP) ++ opts1 |= IPCS | TCPCS; ++ else if (ip->protocol == IPPROTO_UDP) ++ opts1 |= IPCS | UDPCS; ++ else { ++ WARN_ONCE(1, ++ "Net bug: asked to checksum invalid Legacy IP packet\n"); ++ goto out_dma_error; ++ } ++ } + + if (skb_shinfo(skb)->nr_frags == 0) { + struct cp_desc *txd = &cp->tx_ring[entry]; +@@ -768,31 +785,20 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb, + txd->addr = cpu_to_le64(mapping); + wmb(); + +- flags = eor | len | DescOwn | FirstFrag | LastFrag; +- +- if (mss) +- flags |= LargeSend | ((mss & MSSMask) << MSSShift); +- else if (skb->ip_summed == CHECKSUM_PARTIAL) { +- const struct iphdr *ip = ip_hdr(skb); +- if (ip->protocol == IPPROTO_TCP) +- flags |= IPCS | TCPCS; +- else if (ip->protocol == IPPROTO_UDP) +- flags |= IPCS | UDPCS; +- else +- WARN_ON(1); /* we need a WARN() */ +- } ++ opts1 |= eor | len | FirstFrag | LastFrag; + +- txd->opts1 = cpu_to_le32(flags); ++ txd->opts1 = cpu_to_le32(opts1); + wmb(); + + cp->tx_skb[entry] = skb; +- entry = NEXT_TX(entry); ++ cp->tx_opts[entry] = opts1; ++ netif_dbg(cp, tx_queued, cp->dev, "tx queued, slot %d, skblen %d\n", ++ entry, skb->len); + } else { + struct cp_desc *txd; +- u32 first_len, first_eor; ++ u32 first_len, first_eor, ctrl; + dma_addr_t first_mapping; + int frag, first_entry = entry; +- const struct iphdr *ip = ip_hdr(skb); + + /* We must give this initial chunk to the device last. + * Otherwise we could race with the device. +@@ -805,14 +811,14 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb, + goto out_dma_error; + + cp->tx_skb[entry] = skb; +- entry = NEXT_TX(entry); + + for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) { + const skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag]; + u32 len; +- u32 ctrl; + dma_addr_t mapping; + ++ entry = NEXT_TX(entry); ++ + len = skb_frag_size(this_frag); + mapping = dma_map_single(&cp->pdev->dev, + skb_frag_address(this_frag), +@@ -824,19 +830,7 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb, + + eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; + +- ctrl = eor | len | DescOwn; +- +- if (mss) +- ctrl |= LargeSend | +- ((mss & MSSMask) << MSSShift); +- else if (skb->ip_summed == CHECKSUM_PARTIAL) { +- if (ip->protocol == IPPROTO_TCP) +- ctrl |= IPCS | TCPCS; +- else if (ip->protocol == IPPROTO_UDP) +- ctrl |= IPCS | UDPCS; +- else +- BUG(); +- } ++ ctrl = opts1 | eor | len; + + if (frag == skb_shinfo(skb)->nr_frags - 1) + ctrl |= LastFrag; +@@ -849,8 +843,8 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb, + txd->opts1 = cpu_to_le32(ctrl); + wmb(); + ++ cp->tx_opts[entry] = ctrl; + cp->tx_skb[entry] = skb; +- entry = NEXT_TX(entry); + } + + txd = &cp->tx_ring[first_entry]; +@@ -858,27 +852,17 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb, + txd->addr = cpu_to_le64(first_mapping); + wmb(); + +- if (skb->ip_summed == CHECKSUM_PARTIAL) { +- if (ip->protocol == IPPROTO_TCP) +- txd->opts1 = cpu_to_le32(first_eor | first_len | +- FirstFrag | DescOwn | +- IPCS | TCPCS); +- else if (ip->protocol == IPPROTO_UDP) +- txd->opts1 = cpu_to_le32(first_eor | first_len | +- FirstFrag | DescOwn | +- IPCS | UDPCS); +- else +- BUG(); +- } else +- txd->opts1 = cpu_to_le32(first_eor | first_len | +- FirstFrag | DescOwn); ++ ctrl = opts1 | first_eor | first_len | FirstFrag; ++ txd->opts1 = cpu_to_le32(ctrl); + wmb(); ++ ++ cp->tx_opts[first_entry] = ctrl; ++ netif_dbg(cp, tx_queued, cp->dev, "tx queued, slots %d-%d, skblen %d\n", ++ first_entry, entry, skb->len); + } +- cp->tx_head = entry; ++ cp->tx_head = NEXT_TX(entry); + + netdev_sent_queue(dev, skb->len); +- netif_dbg(cp, tx_queued, cp->dev, "tx queued, slot %d, skblen %d\n", +- entry, skb->len); + if (TX_BUFFS_AVAIL(cp) <= (MAX_SKB_FRAGS + 1)) + netif_stop_queue(dev); + +@@ -1115,6 +1099,7 @@ static int cp_init_rings (struct cp_private *cp) + { + memset(cp->tx_ring, 0, sizeof(struct cp_desc) * CP_TX_RING_SIZE); + cp->tx_ring[CP_TX_RING_SIZE - 1].opts1 = cpu_to_le32(RingEnd); ++ memset(cp->tx_opts, 0, sizeof(cp->tx_opts)); + + cp_init_rings_index(cp); + +@@ -1151,7 +1136,7 @@ static void cp_clean_rings (struct cp_private *cp) + desc = cp->rx_ring + i; + dma_unmap_single(&cp->pdev->dev,le64_to_cpu(desc->addr), + cp->rx_buf_sz, PCI_DMA_FROMDEVICE); +- dev_kfree_skb(cp->rx_skb[i]); ++ dev_kfree_skb_any(cp->rx_skb[i]); + } + } + +@@ -1164,7 +1149,7 @@ static void cp_clean_rings (struct cp_private *cp) + le32_to_cpu(desc->opts1) & 0xffff, + PCI_DMA_TODEVICE); + if (le32_to_cpu(desc->opts1) & LastFrag) +- dev_kfree_skb(skb); ++ dev_kfree_skb_any(skb); + cp->dev->stats.tx_dropped++; + } + } +@@ -1172,6 +1157,7 @@ static void cp_clean_rings (struct cp_private *cp) + + memset(cp->rx_ring, 0, sizeof(struct cp_desc) * CP_RX_RING_SIZE); + memset(cp->tx_ring, 0, sizeof(struct cp_desc) * CP_TX_RING_SIZE); ++ memset(cp->tx_opts, 0, sizeof(cp->tx_opts)); + + memset(cp->rx_skb, 0, sizeof(struct sk_buff *) * CP_RX_RING_SIZE); + memset(cp->tx_skb, 0, sizeof(struct sk_buff *) * CP_TX_RING_SIZE); +@@ -1249,7 +1235,7 @@ static void cp_tx_timeout(struct net_device *dev) + { + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; +- int rc; ++ int rc, i; + + netdev_warn(dev, "Transmit timeout, status %2x %4x %4x %4x\n", + cpr8(Cmd), cpr16(CpCmd), +@@ -1257,13 +1243,26 @@ static void cp_tx_timeout(struct net_device *dev) + + spin_lock_irqsave(&cp->lock, flags); + ++ netif_dbg(cp, tx_err, cp->dev, "TX ring head %d tail %d desc %x\n", ++ cp->tx_head, cp->tx_tail, cpr16(TxDmaOkLowDesc)); ++ for (i = 0; i < CP_TX_RING_SIZE; i++) { ++ netif_dbg(cp, tx_err, cp->dev, ++ "TX slot %d @%p: %08x (%08x) %08x %llx %p\n", ++ i, &cp->tx_ring[i], le32_to_cpu(cp->tx_ring[i].opts1), ++ cp->tx_opts[i], le32_to_cpu(cp->tx_ring[i].opts2), ++ le64_to_cpu(cp->tx_ring[i].addr), ++ cp->tx_skb[i]); ++ } ++ + cp_stop_hw(cp); + cp_clean_rings(cp); + rc = cp_init_rings(cp); + cp_start_hw(cp); +- cp_enable_irq(cp); ++ __cp_set_rx_mode(dev); ++ cpw16_f(IntrMask, cp_norx_intr_mask); + + netif_wake_queue(dev); ++ napi_schedule(&cp->napi); + + spin_unlock_irqrestore(&cp->lock, flags); + } |