aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2013-07-29 13:24:04 -0400
committerDavid S. Miller <davem@davemloft.net>2013-07-30 21:12:07 -0400
commit7b70176421993866e616f1cbc4d0dd4054f1bf78 (patch)
treebcbc0648f8fb8a1715eda99ec25c9053a0525d47
parente1ee3673a83cc02b6b5e43c9e647d8dd5e1c4e26 (diff)
atl1c: Fix misuse of netdev_alloc_skb in refilling rx ring
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 <edumazet@google.com> Cc: Luis Henriques <luis.henriques@canonical.com> Cc: Neil Horman <nhorman@tuxdriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c.h3
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c_main.c40
2 files changed, 42 insertions, 1 deletions
diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c.h b/drivers/net/ethernet/atheros/atl1c/atl1c.h
index b2bf324631dc..0f0556526ba9 100644
--- a/drivers/net/ethernet/atheros/atl1c/atl1c.h
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c.h
@@ -520,6 +520,9 @@ struct atl1c_adapter {
520 struct net_device *netdev; 520 struct net_device *netdev;
521 struct pci_dev *pdev; 521 struct pci_dev *pdev;
522 struct napi_struct napi; 522 struct napi_struct napi;
523 struct page *rx_page;
524 unsigned int rx_page_offset;
525 unsigned int rx_frag_size;
523 struct atl1c_hw hw; 526 struct atl1c_hw hw;
524 struct atl1c_hw_stats hw_stats; 527 struct atl1c_hw_stats hw_stats;
525 struct mii_if_info mii; /* MII interface info */ 528 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 786a87483298..a36a760ada28 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)
481static void atl1c_set_rxbufsize(struct atl1c_adapter *adapter, 481static void atl1c_set_rxbufsize(struct atl1c_adapter *adapter,
482 struct net_device *dev) 482 struct net_device *dev)
483{ 483{
484 unsigned int head_size;
484 int mtu = dev->mtu; 485 int mtu = dev->mtu;
485 486
486 adapter->rx_buffer_len = mtu > AT_RX_BUF_SIZE ? 487 adapter->rx_buffer_len = mtu > AT_RX_BUF_SIZE ?
487 roundup(mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN, 8) : AT_RX_BUF_SIZE; 488 roundup(mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN, 8) : AT_RX_BUF_SIZE;
489
490 head_size = SKB_DATA_ALIGN(adapter->rx_buffer_len + NET_SKB_PAD) +
491 SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
492 adapter->rx_frag_size = roundup_pow_of_two(head_size);
488} 493}
489 494
490static netdev_features_t atl1c_fix_features(struct net_device *netdev, 495static netdev_features_t atl1c_fix_features(struct net_device *netdev,
@@ -952,6 +957,10 @@ static void atl1c_free_ring_resources(struct atl1c_adapter *adapter)
952 kfree(adapter->tpd_ring[0].buffer_info); 957 kfree(adapter->tpd_ring[0].buffer_info);
953 adapter->tpd_ring[0].buffer_info = NULL; 958 adapter->tpd_ring[0].buffer_info = NULL;
954 } 959 }
960 if (adapter->rx_page) {
961 put_page(adapter->rx_page);
962 adapter->rx_page = NULL;
963 }
955} 964}
956 965
957/** 966/**
@@ -1639,6 +1648,35 @@ static inline void atl1c_rx_checksum(struct atl1c_adapter *adapter,
1639 skb_checksum_none_assert(skb); 1648 skb_checksum_none_assert(skb);
1640} 1649}
1641 1650
1651static struct sk_buff *atl1c_alloc_skb(struct atl1c_adapter *adapter)
1652{
1653 struct sk_buff *skb;
1654 struct page *page;
1655
1656 if (adapter->rx_frag_size > PAGE_SIZE)
1657 return netdev_alloc_skb(adapter->netdev,
1658 adapter->rx_buffer_len);
1659
1660 page = adapter->rx_page;
1661 if (!page) {
1662 adapter->rx_page = page = alloc_page(GFP_ATOMIC);
1663 if (unlikely(!page))
1664 return NULL;
1665 adapter->rx_page_offset = 0;
1666 }
1667
1668 skb = build_skb(page_address(page) + adapter->rx_page_offset,
1669 adapter->rx_frag_size);
1670 if (likely(skb)) {
1671 adapter->rx_page_offset += adapter->rx_frag_size;
1672 if (adapter->rx_page_offset >= PAGE_SIZE)
1673 adapter->rx_page = NULL;
1674 else
1675 get_page(page);
1676 }
1677 return skb;
1678}
1679
1642static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter) 1680static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter)
1643{ 1681{
1644 struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring; 1682 struct atl1c_rfd_ring *rfd_ring = &adapter->rfd_ring;
@@ -1660,7 +1698,7 @@ static int atl1c_alloc_rx_buffer(struct atl1c_adapter *adapter)
1660 while (next_info->flags & ATL1C_BUFFER_FREE) { 1698 while (next_info->flags & ATL1C_BUFFER_FREE) {
1661 rfd_desc = ATL1C_RFD_DESC(rfd_ring, rfd_next_to_use); 1699 rfd_desc = ATL1C_RFD_DESC(rfd_ring, rfd_next_to_use);
1662 1700
1663 skb = netdev_alloc_skb(adapter->netdev, adapter->rx_buffer_len); 1701 skb = atl1c_alloc_skb(adapter);
1664 if (unlikely(!skb)) { 1702 if (unlikely(!skb)) {
1665 if (netif_msg_rx_err(adapter)) 1703 if (netif_msg_rx_err(adapter))
1666 dev_warn(&pdev->dev, "alloc rx buffer failed\n"); 1704 dev_warn(&pdev->dev, "alloc rx buffer failed\n");