diff --git a/debian/changelog b/debian/changelog index 00176aa72..2cd36cab4 100644 --- a/debian/changelog +++ b/debian/changelog @@ -77,6 +77,8 @@ linux (3.10.5-1) UNRELEASED; urgency=low * nl80211: fix another nl80211_fam.attrbuf race * Bump ABI to 2 * ext4: fix retry handling in ext4_ext_truncate() + * atl1c: Fix misuse of netdev_alloc_skb in refilling rx ring + (Closes: #718505) -- Ben Hutchings Tue, 30 Jul 2013 18:09:20 +0200 diff --git a/debian/patches/bugfix/all/atl1c-Fix-misuse-of-netdev_alloc_skb-in-refilling-rx.patch b/debian/patches/bugfix/all/atl1c-Fix-misuse-of-netdev_alloc_skb-in-refilling-rx.patch new file mode 100644 index 000000000..c55c21dc4 --- /dev/null +++ b/debian/patches/bugfix/all/atl1c-Fix-misuse-of-netdev_alloc_skb-in-refilling-rx.patch @@ -0,0 +1,131 @@ +From: Eric Dumazet +Date: Mon, 29 Jul 2013 10:24:04 -0700 +Subject: atl1c: Fix misuse of netdev_alloc_skb in refilling rx ring +Origin: https://git.kernel.org/linus/7b70176421993866e616f1cbc4d0dd4054f1bf78 + +On Mon, 2013-07-29 at 08:30 -0700, Eric Dumazet wrote: +> On Mon, 2013-07-29 at 13:09 +0100, Luis Henriques wrote: +> +> > +> > I confirm that I can't reproduce the issue using this patch. +> > +> +> Thanks, I'll send a polished patch, as this one had an error if +> build_skb() returns NULL (in case sk_buff allocation fails) + +Please try the following patch : It should use 2K frags instead of 4K +for normal 1500 mtu + +Thanks ! + +[PATCH] atl1c: use custom skb allocator + +We had reports ( https://bugzilla.kernel.org/show_bug.cgi?id=54021 ) +that using high order pages for skb allocations is problematic for atl1c + +We do not know exactly what the problem is, but we suspect that crossing +4K pages is not well supported by this hardware. + +Use a custom allocator, using page allocator and 2K fragments for +optimal stack behavior. We might make this allocator generic +in future kernels. + +Signed-off-by: Eric Dumazet +Cc: Luis Henriques +Cc: Neil Horman +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/atheros/atl1c/atl1c.h | 3 ++ + drivers/net/ethernet/atheros/atl1c/atl1c_main.c | 40 ++++++++++++++++++++++++- + 2 files changed, 42 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c.h b/drivers/net/ethernet/atheros/atl1c/atl1c.h +index b2bf324..0f05565 100644 +--- a/drivers/net/ethernet/atheros/atl1c/atl1c.h ++++ b/drivers/net/ethernet/atheros/atl1c/atl1c.h +@@ -520,6 +520,9 @@ struct atl1c_adapter { + struct net_device *netdev; + struct pci_dev *pdev; + struct napi_struct napi; ++ struct page *rx_page; ++ unsigned int rx_page_offset; ++ unsigned int rx_frag_size; + struct atl1c_hw hw; + struct atl1c_hw_stats hw_stats; + struct mii_if_info mii; /* MII interface info */ +diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +index 786a874..a36a760 100644 +--- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c ++++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +@@ -481,10 +481,15 @@ static int atl1c_set_mac_addr(struct net_device *netdev, void *p) + static void atl1c_set_rxbufsize(struct atl1c_adapter *adapter, + struct net_device *dev) + { ++ unsigned int head_size; + int mtu = dev->mtu; + + adapter->rx_buffer_len = mtu > AT_RX_BUF_SIZE ? + roundup(mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN, 8) : AT_RX_BUF_SIZE; ++ ++ head_size = SKB_DATA_ALIGN(adapter->rx_buffer_len + NET_SKB_PAD) + ++ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); ++ adapter->rx_frag_size = roundup_pow_of_two(head_size); + } + + static netdev_features_t atl1c_fix_features(struct net_device *netdev, +@@ -952,6 +957,10 @@ static void atl1c_free_ring_resources(struct atl1c_adapter *adapter) + kfree(adapter->tpd_ring[0].buffer_info); + adapter->tpd_ring[0].buffer_info = NULL; + } ++ if (adapter->rx_page) { ++ put_page(adapter->rx_page); ++ adapter->rx_page = NULL; ++ } + } + + /** +@@ -1639,6 +1648,35 @@ static inline void atl1c_rx_checksum(struct atl1c_adapter *adapter, + skb_checksum_none_assert(skb); + } + ++static struct sk_buff *atl1c_alloc_skb(struct atl1c_adapter *adapter) ++{ ++ struct sk_buff *skb; ++ struct page *page; ++ ++ if (adapter->rx_frag_size > PAGE_SIZE) ++ return netdev_alloc_skb(adapter->netdev, ++ adapter->rx_buffer_len); ++ ++ page = adapter->rx_page; ++ if (!page) { ++ adapter->rx_page = page = alloc_page(GFP_ATOMIC); ++ if (unlikely(!page)) ++ return NULL; ++ adapter->rx_page_offset = 0; ++ } ++ ++ skb = build_skb(page_address(page) + adapter->rx_page_offset, ++ adapter->rx_frag_size); ++ if (likely(skb)) { ++ adapter->rx_page_offset += adapter->rx_frag_size; ++ if (adapter->rx_page_offset >= PAGE_SIZE) ++ adapter->rx_page = NULL; ++ else ++ get_page(page); ++ } ++ return skb; ++} ++ + static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter) + { + struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring; +@@ -1660,7 +1698,7 @@ static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter) + while (next_info->flags & ATL1C_BUFFER_FREE) { + rfd_desc = ATL1C_RFD_DESC(rfd_ring, rfd_next_to_use); + +- skb = netdev_alloc_skb(adapter->netdev, adapter->rx_buffer_len); ++ skb = atl1c_alloc_skb(adapter); + if (unlikely(!skb)) { + if (netif_msg_rx_err(adapter)) + dev_warn(&pdev->dev, "alloc rx buffer failed\n"); diff --git a/debian/patches/series b/debian/patches/series index 12e26c5e3..bd3be69cb 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -113,3 +113,4 @@ bugfix/m68k/ethernat-kconfig.patch debian/sparc-cpufreq-convince-genksyms-that-the-abi-didnt-change.patch bugfix/all/nl80211-fix-another-nl80211_fam-attrbuf-race.patch bugfix/all/ext4-fix-retry-handling-in-ext4_ext_truncate.patch +bugfix/all/atl1c-Fix-misuse-of-netdev_alloc_skb-in-refilling-rx.patch