kernel: fix more bgmac dma init/cleanup issues
[openwrt/openwrt.git] / target / linux / generic / patches-3.18 / 077-07-bgmac-simplify-rx-DMA-error-handling.patch
1 From: Felix Fietkau <nbd@openwrt.org>
2 Date: Sun, 12 Apr 2015 22:23:07 +0200
3 Subject: [PATCH] bgmac: simplify rx DMA error handling
4
5 Unmap the DMA buffer before checking it. If an error occurs, free the
6 buffer and allocate a new one. If allocation or mapping fails, retry as
7 long as there is NAPI poll budget left (count every attempt instead of
8 every frame).
9
10 Signed-off-by: Felix Fietkau <nbd@openwrt.org>
11 ---
12
13 --- a/drivers/net/ethernet/broadcom/bgmac.c
14 +++ b/drivers/net/ethernet/broadcom/bgmac.c
15 @@ -404,51 +404,33 @@ static int bgmac_dma_rx_read(struct bgma
16 void *buf = slot->buf;
17 u16 len, flags;
18
19 - /* Unmap buffer to make it accessible to the CPU */
20 - dma_sync_single_for_cpu(dma_dev, slot->dma_addr,
21 - BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
22 -
23 - /* Get info from the header */
24 - len = le16_to_cpu(rx->len);
25 - flags = le16_to_cpu(rx->flags);
26 + if (++handled >= weight - 1) /* Should never be greater */
27 + break;
28
29 do {
30 - dma_addr_t old_dma_addr = slot->dma_addr;
31 - int err;
32 + if (!slot->dma_addr)
33 + break;
34 +
35 + /* Unmap buffer to make it accessible to the CPU */
36 + dma_unmap_single(dma_dev, slot->dma_addr,
37 + BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
38 + slot->dma_addr = 0;
39 +
40 + /* Get info from the header */
41 + len = le16_to_cpu(rx->len);
42 + flags = le16_to_cpu(rx->flags);
43
44 /* Check for poison and drop or pass the packet */
45 if (len == 0xdead && flags == 0xbeef) {
46 bgmac_err(bgmac, "Found poisoned packet at slot %d, DMA issue!\n",
47 ring->start);
48 - dma_sync_single_for_device(dma_dev,
49 - slot->dma_addr,
50 - BGMAC_RX_BUF_SIZE,
51 - DMA_FROM_DEVICE);
52 + put_page(virt_to_head_page(buf));
53 break;
54 }
55
56 /* Omit CRC. */
57 len -= ETH_FCS_LEN;
58
59 - /* Prepare new skb as replacement */
60 - err = bgmac_dma_rx_skb_for_slot(bgmac, slot);
61 - if (err) {
62 - /* Poison the old skb */
63 - rx->len = cpu_to_le16(0xdead);
64 - rx->flags = cpu_to_le16(0xbeef);
65 -
66 - dma_sync_single_for_device(dma_dev,
67 - slot->dma_addr,
68 - BGMAC_RX_BUF_SIZE,
69 - DMA_FROM_DEVICE);
70 - break;
71 - }
72 - bgmac_dma_rx_setup_desc(bgmac, ring, ring->start);
73 -
74 - /* Unmap old skb, we'll pass it to the netfif */
75 - dma_unmap_single(dma_dev, old_dma_addr,
76 - BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE);
77 -
78 skb = build_skb(buf, BGMAC_RX_ALLOC_SIZE);
79 skb_put(skb, BGMAC_RX_FRAME_OFFSET +
80 BGMAC_RX_BUF_OFFSET + len);
81 @@ -458,14 +440,16 @@ static int bgmac_dma_rx_read(struct bgma
82 skb_checksum_none_assert(skb);
83 skb->protocol = eth_type_trans(skb, bgmac->net_dev);
84 napi_gro_receive(&bgmac->napi, skb);
85 - handled++;
86 } while (0);
87
88 + /* Prepare new skb as replacement */
89 + if (bgmac_dma_rx_skb_for_slot(bgmac, slot))
90 + continue;
91 +
92 + bgmac_dma_rx_setup_desc(bgmac, ring, ring->start);
93 +
94 if (++ring->start >= BGMAC_RX_RING_SLOTS)
95 ring->start = 0;
96 -
97 - if (handled >= weight) /* Should never be greater */
98 - break;
99 }
100
101 return handled;
102 @@ -528,14 +512,14 @@ static void bgmac_dma_rx_ring_free(struc
103
104 for (i = 0; i < ring->num_slots; i++) {
105 slot = &ring->slots[i];
106 - if (!slot->buf)
107 + if (!slot->dma_addr)
108 continue;
109
110 - if (slot->dma_addr)
111 - dma_unmap_single(dma_dev, slot->dma_addr,
112 - BGMAC_RX_BUF_SIZE,
113 - DMA_FROM_DEVICE);
114 + dma_unmap_single(dma_dev, slot->dma_addr,
115 + BGMAC_RX_BUF_SIZE,
116 + DMA_FROM_DEVICE);
117 put_page(virt_to_head_page(slot->buf));
118 + slot->dma_addr = 0;
119 }
120 }
121