Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: kernel: Backport mvneta crash fix to 5.15 #12375

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,62 @@
From 43ed6fff01333868a1d0e19876f67c22d9939952 Mon Sep 17 00:00:00 2001
From: Yuval Shaia <yshaia@marvell.com>
Date: Wed, 13 Oct 2021 09:49:21 +0300
Subject: [PATCH] net: mvneta: Delete unused variable

The variable pp is not in use - delete it.

Signed-off-by: Yuval Shaia <yshaia@marvell.com>
Link: https://lore.kernel.org/r/20211013064921.26346-1-yshaia@marvell.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
drivers/net/ethernet/marvell/mvneta.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)

--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -1914,7 +1914,7 @@ static int mvneta_rx_refill(struct mvnet
}

/* Handle tx checksum */
-static u32 mvneta_skb_tx_csum(struct mvneta_port *pp, struct sk_buff *skb)
+static u32 mvneta_skb_tx_csum(struct sk_buff *skb)
{
if (skb->ip_summed == CHECKSUM_PARTIAL) {
int ip_hdr_len = 0;
@@ -2595,8 +2595,7 @@ err_drop_frame:
}

static inline void
-mvneta_tso_put_hdr(struct sk_buff *skb,
- struct mvneta_port *pp, struct mvneta_tx_queue *txq)
+mvneta_tso_put_hdr(struct sk_buff *skb, struct mvneta_tx_queue *txq)
{
int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
struct mvneta_tx_buf *buf = &txq->buf[txq->txq_put_index];
@@ -2604,7 +2603,7 @@ mvneta_tso_put_hdr(struct sk_buff *skb,

tx_desc = mvneta_txq_next_desc_get(txq);
tx_desc->data_size = hdr_len;
- tx_desc->command = mvneta_skb_tx_csum(pp, skb);
+ tx_desc->command = mvneta_skb_tx_csum(skb);
tx_desc->command |= MVNETA_TXD_F_DESC;
tx_desc->buf_phys_addr = txq->tso_hdrs_phys +
txq->txq_put_index * TSO_HEADER_SIZE;
@@ -2681,7 +2680,7 @@ static int mvneta_tx_tso(struct sk_buff
hdr = txq->tso_hdrs + txq->txq_put_index * TSO_HEADER_SIZE;
tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0);

- mvneta_tso_put_hdr(skb, pp, txq);
+ mvneta_tso_put_hdr(skb, txq);

while (data_left > 0) {
int size;
@@ -2799,7 +2798,7 @@ static netdev_tx_t mvneta_tx(struct sk_b
/* Get a descriptor for the first part of the packet */
tx_desc = mvneta_txq_next_desc_get(txq);

- tx_cmd = mvneta_skb_tx_csum(pp, skb);
+ tx_cmd = mvneta_skb_tx_csum(skb);

tx_desc->data_size = skb_headlen(skb);

@@ -0,0 +1,37 @@
From 0cf39c6543469aae4a30cba354243125514ed568 Mon Sep 17 00:00:00 2001
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
Date: Wed, 29 Mar 2023 13:11:17 +0100
Subject: [PATCH] net: mvneta: fix potential double-frees in
mvneta_txq_sw_deinit()

Reported on the Turris forum, mvneta provokes kernel warnings in the
architecture DMA mapping code when mvneta_setup_txqs() fails to
allocate memory. This happens because when mvneta_cleanup_txqs() is
called in the mvneta_stop() path, we leave pointers in the structure
that have been freed.

Then on mvneta_open(), we call mvneta_setup_txqs(), which starts
allocating memory. On memory allocation failure, mvneta_cleanup_txqs()
will walk all the queues freeing any non-NULL pointers - which includes
pointers that were previously freed in mvneta_stop().

Fix this by setting these pointers to NULL to prevent double-freeing
of the same memory.

Link: https://forum.turris.cz/t/random-kernel-exceptions-on-hbl-tos-7-0/18865/8
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
---
drivers/net/ethernet/marvell/mvneta.c | 2 ++
1 file changed, 2 insertions(+)

--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -3481,6 +3481,8 @@ static void mvneta_txq_sw_deinit(struct

netdev_tx_reset_queue(nq);

+ txq->buf = NULL;
+ txq->tso_hdrs = NULL;
txq->descs = NULL;
txq->last_desc = 0;
txq->next_desc_to_proc = 0;
@@ -0,0 +1,111 @@
From d6d80269cf5c79f9dfe7d69f8b41a72015c89748 Mon Sep 17 00:00:00 2001
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
Date: Mon, 3 Apr 2023 19:30:20 +0100
Subject: [PATCH 1/5] net: mvneta: fix transmit path dma-unmapping on error

The transmit code assumes that the transmit descriptors that are used
begin with the first descriptor in the ring, but this may not be the
case. Fix this by providing a new function that dma-unmaps a range of
numbered descriptor entries, and use that to do the unmapping.

Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
---
drivers/net/ethernet/marvell/mvneta.c | 53 +++++++++++++++++----------
1 file changed, 33 insertions(+), 20 deletions(-)

--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -2647,14 +2647,40 @@ mvneta_tso_put_data(struct net_device *d
return 0;
}

+static void mvneta_release_descs(struct mvneta_port *pp,
+ struct mvneta_tx_queue *txq,
+ int first, int num)
+{
+ int desc_idx, i;
+
+ desc_idx = first + num;
+ if (desc_idx >= txq->size)
+ desc_idx -= txq->size;
+
+ for (i = num; i >= 0; i--) {
+ struct mvneta_tx_desc *tx_desc = txq->descs + desc_idx;
+
+ if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr))
+ dma_unmap_single(pp->dev->dev.parent,
+ tx_desc->buf_phys_addr,
+ tx_desc->data_size,
+ DMA_TO_DEVICE);
+
+ mvneta_txq_desc_put(txq);
+
+ if (desc_idx == 0)
+ desc_idx = txq->size;
+ desc_idx -= 1;
+ }
+}
+
static int mvneta_tx_tso(struct sk_buff *skb, struct net_device *dev,
struct mvneta_tx_queue *txq)
{
int hdr_len, total_len, data_left;
- int desc_count = 0;
+ int first_desc, desc_count = 0;
struct mvneta_port *pp = netdev_priv(dev);
struct tso_t tso;
- int i;

/* Count needed descriptors */
if ((txq->count + tso_count_descs(skb)) >= txq->size)
@@ -2665,6 +2691,8 @@ static int mvneta_tx_tso(struct sk_buff
return 0;
}

+ first_desc = txq->txq_put_index;
+
/* Initialize the TSO handler, and prepare the first payload */
hdr_len = tso_start(skb, &tso);

@@ -2705,15 +2733,7 @@ err_release:
/* Release all used data descriptors; header descriptors must not
* be DMA-unmapped.
*/
- for (i = desc_count - 1; i >= 0; i--) {
- struct mvneta_tx_desc *tx_desc = txq->descs + i;
- if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr))
- dma_unmap_single(pp->dev->dev.parent,
- tx_desc->buf_phys_addr,
- tx_desc->data_size,
- DMA_TO_DEVICE);
- mvneta_txq_desc_put(txq);
- }
+ mvneta_release_descs(pp, txq, first_desc, desc_count - 1);
return 0;
}

@@ -2723,6 +2743,7 @@ static int mvneta_tx_frag_process(struct
{
struct mvneta_tx_desc *tx_desc;
int i, nr_frags = skb_shinfo(skb)->nr_frags;
+ int first_desc = txq->txq_put_index;

for (i = 0; i < nr_frags; i++) {
struct mvneta_tx_buf *buf = &txq->buf[txq->txq_put_index];
@@ -2761,15 +2782,7 @@ error:
/* Release all descriptors that were used to map fragments of
* this packet, as well as the corresponding DMA mappings
*/
- for (i = i - 1; i >= 0; i--) {
- tx_desc = txq->descs + i;
- dma_unmap_single(pp->dev->dev.parent,
- tx_desc->buf_phys_addr,
- tx_desc->data_size,
- DMA_TO_DEVICE);
- mvneta_txq_desc_put(txq);
- }
-
+ mvneta_release_descs(pp, txq, first_desc, i - 1);
return -ENOMEM;
}

@@ -0,0 +1,42 @@
From e3c77d0a1b635d114c147fd2078afb57ed558b81 Mon Sep 17 00:00:00 2001
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
Date: Mon, 3 Apr 2023 19:30:25 +0100
Subject: [PATCH 2/5] net: mvneta: mark mapped and tso buffers separately

Mark dma-mapped skbs and TSO buffers separately, so we can use
buf->type to identify their differences.

Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
---
drivers/net/ethernet/marvell/mvneta.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)

--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -607,6 +607,7 @@ struct mvneta_rx_desc {
#endif

enum mvneta_tx_buf_type {
+ MVNETA_TYPE_TSO,
MVNETA_TYPE_SKB,
MVNETA_TYPE_XDP_TX,
MVNETA_TYPE_XDP_NDO,
@@ -1852,7 +1853,8 @@ static void mvneta_txq_bufs_free(struct
dma_unmap_single(pp->dev->dev.parent,
tx_desc->buf_phys_addr,
tx_desc->data_size, DMA_TO_DEVICE);
- if (buf->type == MVNETA_TYPE_SKB && buf->skb) {
+ if ((buf->type == MVNETA_TYPE_TSO ||
+ buf->type == MVNETA_TYPE_SKB) && buf->skb) {
bytes_compl += buf->skb->len;
pkts_compl++;
dev_kfree_skb_any(buf->skb);
@@ -2607,7 +2609,7 @@ mvneta_tso_put_hdr(struct sk_buff *skb,
tx_desc->command |= MVNETA_TXD_F_DESC;
tx_desc->buf_phys_addr = txq->tso_hdrs_phys +
txq->txq_put_index * TSO_HEADER_SIZE;
- buf->type = MVNETA_TYPE_SKB;
+ buf->type = MVNETA_TYPE_TSO;
buf->skb = NULL;

mvneta_txq_inc_put(txq);
@@ -0,0 +1,59 @@
From fe2abc1abc0dfc6c13fe8f189216f00dbbb33044 Mon Sep 17 00:00:00 2001
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
Date: Mon, 3 Apr 2023 19:30:30 +0100
Subject: [PATCH 3/5] net: mvneta: use buf->type to determine whether to
dma-unmap

Now that we use a different buffer type for TSO headers, we can use
buf->type to determine whether the original buffer was DMA-mapped or
not. The rules are:

MVNETA_TYPE_XDP_TX - from a DMA pool, no unmap is required
MVNETA_TYPE_XDP_NDO - dma_map_single()'d
MVNETA_TYPE_SKB - normal skbuff, dma_map_single()'d
MVNETA_TYPE_TSO - from the TSO buffer area

This means we only need to call dma_unmap_single() on the XDP_NDO and
SKB types of buffer, and we no longer need the private IS_TSO_HEADER()
which relies on the TSO region being contiguously allocated.

Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
---
drivers/net/ethernet/marvell/mvneta.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)

--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -334,10 +334,6 @@
MVNETA_SKB_HEADROOM))
#define MVNETA_MAX_RX_BUF_SIZE (PAGE_SIZE - MVNETA_SKB_PAD)

-#define IS_TSO_HEADER(txq, addr) \
- ((addr >= txq->tso_hdrs_phys) && \
- (addr < txq->tso_hdrs_phys + txq->size * TSO_HEADER_SIZE))
-
#define MVNETA_RX_GET_BM_POOL_ID(rxd) \
(((rxd)->status & MVNETA_RXD_BM_POOL_MASK) >> MVNETA_RXD_BM_POOL_SHIFT)

@@ -1848,8 +1844,8 @@ static void mvneta_txq_bufs_free(struct

mvneta_txq_inc_get(txq);

- if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr) &&
- buf->type != MVNETA_TYPE_XDP_TX)
+ if (buf->type == MVNETA_TYPE_XDP_NDO ||
+ buf->type == MVNETA_TYPE_SKB)
dma_unmap_single(pp->dev->dev.parent,
tx_desc->buf_phys_addr,
tx_desc->data_size, DMA_TO_DEVICE);
@@ -2661,8 +2657,9 @@ static void mvneta_release_descs(struct

for (i = num; i >= 0; i--) {
struct mvneta_tx_desc *tx_desc = txq->descs + desc_idx;
+ struct mvneta_tx_buf *buf = &txq->buf[desc_idx];

- if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr))
+ if (buf->type == MVNETA_TYPE_SKB)
dma_unmap_single(pp->dev->dev.parent,
tx_desc->buf_phys_addr,
tx_desc->data_size,
@@ -0,0 +1,65 @@
From 210ca75d4949f1ace8ea53a75148806cc28224a0 Mon Sep 17 00:00:00 2001
From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
Date: Mon, 3 Apr 2023 19:30:35 +0100
Subject: [PATCH 4/5] net: mvneta: move tso_build_hdr() into
mvneta_tso_put_hdr()

Move tso_build_hdr() into mvneta_tso_put_hdr() so that all the TSO
header building code is in one place.

Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
---
drivers/net/ethernet/marvell/mvneta.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)

--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -2592,19 +2592,24 @@ err_drop_frame:
return rx_done;
}

-static inline void
-mvneta_tso_put_hdr(struct sk_buff *skb, struct mvneta_tx_queue *txq)
+static void mvneta_tso_put_hdr(struct sk_buff *skb, struct mvneta_tx_queue *txq,
+ struct tso_t *tso, int size, bool is_last)
{
- int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
+ int tso_offset, hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
struct mvneta_tx_buf *buf = &txq->buf[txq->txq_put_index];
struct mvneta_tx_desc *tx_desc;
+ char *hdr;
+
+ tso_offset = txq->txq_put_index * TSO_HEADER_SIZE;
+
+ hdr = txq->tso_hdrs + tso_offset;
+ tso_build_hdr(skb, hdr, tso, size, is_last);

tx_desc = mvneta_txq_next_desc_get(txq);
tx_desc->data_size = hdr_len;
tx_desc->command = mvneta_skb_tx_csum(skb);
tx_desc->command |= MVNETA_TXD_F_DESC;
- tx_desc->buf_phys_addr = txq->tso_hdrs_phys +
- txq->txq_put_index * TSO_HEADER_SIZE;
+ tx_desc->buf_phys_addr = txq->tso_hdrs_phys + tso_offset;
buf->type = MVNETA_TYPE_TSO;
buf->skb = NULL;

@@ -2697,17 +2702,12 @@ static int mvneta_tx_tso(struct sk_buff

total_len = skb->len - hdr_len;
while (total_len > 0) {
- char *hdr;
-
data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len);
total_len -= data_left;
desc_count++;

/* prepare packet headers: MAC + IP + TCP */
- hdr = txq->tso_hdrs + txq->txq_put_index * TSO_HEADER_SIZE;
- tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0);
-
- mvneta_tso_put_hdr(skb, txq);
+ mvneta_tso_put_hdr(skb, txq, &tso, data_left, total_len == 0);

while (data_left > 0) {
int size;