[PATCH 08/20] net: xilinx: tsn: deliver endpoint RX frames to DSA user ports

From: Nagadheeraj Rottela

Date: Fri Aug 07 2026 - 06:47:01 EST


The DSA core routes an RX frame to a user port from the port metadata
attached to the skb. Without that metadata, frames from the two MACs
cannot reach their swpN netdevs.

Allocate a METADATA_HW_PORT_MUX entry per MAC port at probe and attach
the matching entry to each RX frame by its TUSER port_id. Frames from
MAC1 and MAC2 now reach the correct user netdev, which completes the
conduit data path.

Signed-off-by: Nagadheeraj Rottela <nagadheeraj.rottela@xxxxxxx>
---
.../net/ethernet/xilinx/tsn/xilinx_tsn_ep.c | 51 ++++++++++++++++++-
1 file changed, 50 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c b/drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c
index 7615d27f3b36..051285b47ddb 100644
--- a/drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c
+++ b/drivers/net/ethernet/xilinx/tsn/xilinx_tsn_ep.c
@@ -29,6 +29,7 @@
#include <linux/string.h>
#include <linux/types.h>

+#include <net/dst_metadata.h>
#include <net/netdev_queues.h>

#include "xilinx_tsn.h"
@@ -56,6 +57,9 @@
#define TSN_TUSER_PORT_MAC1 0x1
#define TSN_TUSER_PORT_MAC2 0x2

+/* Sized to index port_md[] by TUSER port_id (1 or 2, slot 0 unused) */
+#define XLNX_TSN_EP_PORT_MD_SLOTS (TSN_TUSER_PORT_MAC2 + 1)
+
/**
* struct skbuf_dma_descriptor - skb container for each in-flight DMA descriptor
* @sgl: scatter-gather list backing the DMA mapping
@@ -107,6 +111,8 @@ struct xlnx_tsn_ep_dma_chan {
* @tx_chans: array of TX channels (size @num_tx_queues)
* @rx_chans: array of RX channels (size @num_rx_queues)
* @closing: set in ndo_stop so the RX completion callback stops re-arming
+ * @port_md: per-TUSER-port METADATA_HW_PORT_MUX entries attached on RX,
+ * indexed by port_id (1 for MAC1, 2 for MAC2)
*/
struct xlnx_tsn_ep {
struct net_device *ndev;
@@ -124,6 +130,8 @@ struct xlnx_tsn_ep {
struct xlnx_tsn_ep_dma_chan **rx_chans;

bool closing;
+
+ struct metadata_dst *port_md[XLNX_TSN_EP_PORT_MD_SLOTS];
};

static inline struct skbuf_dma_descriptor *
@@ -254,6 +262,7 @@ static void ep_dma_rx_cb(void *data, const struct dmaengine_result *result)
}

skb_put(skb, rx_len);
+ skb_dst_set_noref(skb, &ep->port_md[port_id]->dst);
skb->dev = ndev;
skb->protocol = eth_type_trans(skb, ndev);
skb->ip_summed = CHECKSUM_NONE;
@@ -771,6 +780,37 @@ static int ep_count_dma_queues(struct device *dev, u32 *out_tx, u32 *out_rx)
return 0;
}

+static void ep_free_port_md(struct xlnx_tsn_ep *ep)
+{
+ int i;
+
+ for (i = 0; i < XLNX_TSN_EP_PORT_MD_SLOTS; i++) {
+ if (ep->port_md[i]) {
+ metadata_dst_free(ep->port_md[i]);
+ ep->port_md[i] = NULL;
+ }
+ }
+}
+
+static int ep_alloc_port_md(struct xlnx_tsn_ep *ep)
+{
+ int i;
+
+ for (i = TSN_TUSER_PORT_MAC1; i <= TSN_TUSER_PORT_MAC2; i++) {
+ struct metadata_dst *md;
+
+ md = metadata_dst_alloc(0, METADATA_HW_PORT_MUX, GFP_KERNEL);
+ if (!md) {
+ ep_free_port_md(ep);
+ return -ENOMEM;
+ }
+ md->u.port_info.port_id = i;
+ ep->port_md[i] = md;
+ }
+
+ return 0;
+}
+
static int xlnx_tsn_ep_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -835,16 +875,24 @@ static int xlnx_tsn_ep_probe(struct platform_device *pdev)
ndev->dev_addr);
}

+ ret = ep_alloc_port_md(ep);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to allocate per-port metadata\n");
+ goto err_free_ndev;
+ }
+
platform_set_drvdata(pdev, ep);

ret = register_netdev(ndev);
if (ret) {
dev_err_probe(dev, ret, "failed to register net device\n");
- goto err_free_ndev;
+ goto err_free_md;
}

return 0;

+err_free_md:
+ ep_free_port_md(ep);
err_free_ndev:
free_netdev(ndev);
return ret;
@@ -858,6 +906,7 @@ static void xlnx_tsn_ep_remove(struct platform_device *pdev)
return;

unregister_netdev(ep->ndev);
+ ep_free_port_md(ep);
free_netdev(ep->ndev);
}

--
2.34.1