Skip to content

Commit 26c5f03

Browse files
ftang1davem330
authored andcommitted
net: alx: use custom skb allocator
This patch follows Eric Dumazet's commit 7b70176 for Atheros atl1c driver to fix one exactly same bug in alx driver, that the network link will be lost in 1-5 minutes after the device is up. My laptop Lenovo Y580 with Atheros AR8161 ethernet device hit the same problem with kernel 4.4, and it will be cured by Jarod Wilson's commit c406700 for alx driver which get merged in 4.5. But there are still some alx devices can't function well even with Jarod's patch, while this patch could make them work fine. More details on https://bugzilla.kernel.org/show_bug.cgi?id=70761 The debug shows the issue is very likely to be related with the RX DMA address, specifically 0x...f80, if RX buffer get 0x...f80 several times, their will be RX overflow error and device will stop working. For kernel 4.5.0 with Jarod's patch which works fine with my AR8161/Lennov Y580, if I made some change to the __netdev_alloc_skb --> __alloc_page_frag() to make the allocated buffer can get an address with 0x...f80, then the same error happens. If I make it to 0x...f40 or 0x....fc0, everything will be still fine. So I tend to believe that the 0x..f80 address cause the silicon to behave abnormally. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=70761 Cc: Eric Dumazet <[email protected]> Cc: Johannes Berg <[email protected]> Cc: Jarod Wilson <[email protected]> Signed-off-by: Feng Tang <[email protected]> Tested-by: Ole Lukoie <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent f6988cb commit 26c5f03

File tree

2 files changed

+51
-1
lines changed

2 files changed

+51
-1
lines changed

drivers/net/ethernet/atheros/alx/alx.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ struct alx_priv {
9696
unsigned int rx_ringsz;
9797
unsigned int rxbuf_size;
9898

99+
struct page *rx_page;
100+
unsigned int rx_page_offset;
101+
unsigned int rx_frag_size;
102+
99103
struct napi_struct napi;
100104
struct alx_tx_queue txq;
101105
struct alx_rx_queue rxq;

drivers/net/ethernet/atheros/alx/main.c

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,35 @@ static void alx_free_txbuf(struct alx_priv *alx, int entry)
7070
}
7171
}
7272

73+
static struct sk_buff *alx_alloc_skb(struct alx_priv *alx, gfp_t gfp)
74+
{
75+
struct sk_buff *skb;
76+
struct page *page;
77+
78+
if (alx->rx_frag_size > PAGE_SIZE)
79+
return __netdev_alloc_skb(alx->dev, alx->rxbuf_size, gfp);
80+
81+
page = alx->rx_page;
82+
if (!page) {
83+
alx->rx_page = page = alloc_page(gfp);
84+
if (unlikely(!page))
85+
return NULL;
86+
alx->rx_page_offset = 0;
87+
}
88+
89+
skb = build_skb(page_address(page) + alx->rx_page_offset,
90+
alx->rx_frag_size);
91+
if (likely(skb)) {
92+
alx->rx_page_offset += alx->rx_frag_size;
93+
if (alx->rx_page_offset >= PAGE_SIZE)
94+
alx->rx_page = NULL;
95+
else
96+
get_page(page);
97+
}
98+
return skb;
99+
}
100+
101+
73102
static int alx_refill_rx_ring(struct alx_priv *alx, gfp_t gfp)
74103
{
75104
struct alx_rx_queue *rxq = &alx->rxq;
@@ -86,7 +115,7 @@ static int alx_refill_rx_ring(struct alx_priv *alx, gfp_t gfp)
86115
while (!cur_buf->skb && next != rxq->read_idx) {
87116
struct alx_rfd *rfd = &rxq->rfd[cur];
88117

89-
skb = __netdev_alloc_skb(alx->dev, alx->rxbuf_size, gfp);
118+
skb = alx_alloc_skb(alx, gfp);
90119
if (!skb)
91120
break;
92121
dma = dma_map_single(&alx->hw.pdev->dev,
@@ -124,6 +153,7 @@ static int alx_refill_rx_ring(struct alx_priv *alx, gfp_t gfp)
124153
alx_write_mem16(&alx->hw, ALX_RFD_PIDX, cur);
125154
}
126155

156+
127157
return count;
128158
}
129159

@@ -592,6 +622,11 @@ static void alx_free_rings(struct alx_priv *alx)
592622
kfree(alx->txq.bufs);
593623
kfree(alx->rxq.bufs);
594624

625+
if (alx->rx_page) {
626+
put_page(alx->rx_page);
627+
alx->rx_page = NULL;
628+
}
629+
595630
dma_free_coherent(&alx->hw.pdev->dev,
596631
alx->descmem.size,
597632
alx->descmem.virt,
@@ -646,6 +681,7 @@ static int alx_request_irq(struct alx_priv *alx)
646681
alx->dev->name, alx);
647682
if (!err)
648683
goto out;
684+
649685
/* fall back to legacy interrupt */
650686
pci_disable_msi(alx->hw.pdev);
651687
}
@@ -689,6 +725,7 @@ static int alx_init_sw(struct alx_priv *alx)
689725
struct pci_dev *pdev = alx->hw.pdev;
690726
struct alx_hw *hw = &alx->hw;
691727
int err;
728+
unsigned int head_size;
692729

693730
err = alx_identify_hw(alx);
694731
if (err) {
@@ -704,7 +741,12 @@ static int alx_init_sw(struct alx_priv *alx)
704741

705742
hw->smb_timer = 400;
706743
hw->mtu = alx->dev->mtu;
744+
707745
alx->rxbuf_size = ALX_MAX_FRAME_LEN(hw->mtu);
746+
head_size = SKB_DATA_ALIGN(alx->rxbuf_size + NET_SKB_PAD) +
747+
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
748+
alx->rx_frag_size = roundup_pow_of_two(head_size);
749+
708750
alx->tx_ringsz = 256;
709751
alx->rx_ringsz = 512;
710752
hw->imt = 200;
@@ -806,6 +848,7 @@ static int alx_change_mtu(struct net_device *netdev, int mtu)
806848
{
807849
struct alx_priv *alx = netdev_priv(netdev);
808850
int max_frame = ALX_MAX_FRAME_LEN(mtu);
851+
unsigned int head_size;
809852

810853
if ((max_frame < ALX_MIN_FRAME_SIZE) ||
811854
(max_frame > ALX_MAX_FRAME_SIZE))
@@ -817,6 +860,9 @@ static int alx_change_mtu(struct net_device *netdev, int mtu)
817860
netdev->mtu = mtu;
818861
alx->hw.mtu = mtu;
819862
alx->rxbuf_size = max(max_frame, ALX_DEF_RXBUF_SIZE);
863+
head_size = SKB_DATA_ALIGN(alx->rxbuf_size + NET_SKB_PAD) +
864+
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
865+
alx->rx_frag_size = roundup_pow_of_two(head_size);
820866
netdev_update_features(netdev);
821867
if (netif_running(netdev))
822868
alx_reinit(alx);

0 commit comments

Comments
 (0)