Commit fc57062f authored by Horatiu Vultur's avatar Horatiu Vultur Committed by Jakub Kicinski

net: lan966x: Fix unmapping of received frames using FDMA

When lan966x was receiving a frame, then it was building the skb and
after that it was calling dma_unmap_single with frame size as the
length. This actually has 2 issues:
1. It is using a length to map and a different length to unmap.
2. When the unmap was happening, the data was sync for cpu but it could
   be that this will overwrite what build_skb was initializing.

The fix for these two problems is to change the order of operations.
First to sync the frame for cpu, then to build the skb and in the end to
unmap using the correct size but without sync the frame again for cpu.

Fixes: c8349639 ("net: lan966x: Add FDMA functionality")
Signed-off-by: default avatarHoratiu Vultur <horatiu.vultur@microchip.com>
Link: https://lore.kernel.org/r/20221031133421.1283196-1-horatiu.vultur@microchip.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 70644f72
...@@ -414,13 +414,15 @@ static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx) ...@@ -414,13 +414,15 @@ static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx)
/* Get the received frame and unmap it */ /* Get the received frame and unmap it */
db = &rx->dcbs[rx->dcb_index].db[rx->db_index]; db = &rx->dcbs[rx->dcb_index].db[rx->db_index];
page = rx->page[rx->dcb_index][rx->db_index]; page = rx->page[rx->dcb_index][rx->db_index];
dma_sync_single_for_cpu(lan966x->dev, (dma_addr_t)db->dataptr,
FDMA_DCB_STATUS_BLOCKL(db->status),
DMA_FROM_DEVICE);
skb = build_skb(page_address(page), PAGE_SIZE << rx->page_order); skb = build_skb(page_address(page), PAGE_SIZE << rx->page_order);
if (unlikely(!skb)) if (unlikely(!skb))
goto unmap_page; goto unmap_page;
dma_unmap_single(lan966x->dev, (dma_addr_t)db->dataptr,
FDMA_DCB_STATUS_BLOCKL(db->status),
DMA_FROM_DEVICE);
skb_put(skb, FDMA_DCB_STATUS_BLOCKL(db->status)); skb_put(skb, FDMA_DCB_STATUS_BLOCKL(db->status));
lan966x_ifh_get_src_port(skb->data, &src_port); lan966x_ifh_get_src_port(skb->data, &src_port);
...@@ -429,6 +431,10 @@ static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx) ...@@ -429,6 +431,10 @@ static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx)
if (WARN_ON(src_port >= lan966x->num_phys_ports)) if (WARN_ON(src_port >= lan966x->num_phys_ports))
goto free_skb; goto free_skb;
dma_unmap_single_attrs(lan966x->dev, (dma_addr_t)db->dataptr,
PAGE_SIZE << rx->page_order, DMA_FROM_DEVICE,
DMA_ATTR_SKIP_CPU_SYNC);
skb->dev = lan966x->ports[src_port]->dev; skb->dev = lan966x->ports[src_port]->dev;
skb_pull(skb, IFH_LEN * sizeof(u32)); skb_pull(skb, IFH_LEN * sizeof(u32));
...@@ -454,9 +460,9 @@ static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx) ...@@ -454,9 +460,9 @@ static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx)
free_skb: free_skb:
kfree_skb(skb); kfree_skb(skb);
unmap_page: unmap_page:
dma_unmap_page(lan966x->dev, (dma_addr_t)db->dataptr, dma_unmap_single_attrs(lan966x->dev, (dma_addr_t)db->dataptr,
FDMA_DCB_STATUS_BLOCKL(db->status), PAGE_SIZE << rx->page_order, DMA_FROM_DEVICE,
DMA_FROM_DEVICE); DMA_ATTR_SKIP_CPU_SYNC);
__free_pages(page, rx->page_order); __free_pages(page, rx->page_order);
return NULL; return NULL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment