Netdev List
 help / color / mirror / Atom feed
From: Michael Dege <michael.dege@renesas.com>
To: "Yoshihiro Shimoda" <yoshihiro.shimoda.uh@renesas.com>,
	"Andrew Lunn" <andrew+netdev@lunn.ch>,
	"David S. Miller" <davem@davemloft.net>,
	"Eric Dumazet" <edumazet@google.com>,
	"Jakub Kicinski" <kuba@kernel.org>,
	"Paolo Abeni" <pabeni@redhat.com>,
	"Niklas Söderlund" <niklas.soderlund@ragnatech.se>,
	"Paul Barker" <paul@pbarker.dev>
Cc: netdev@vger.kernel.org, linux-renesas-soc@vger.kernel.org,
	 linux-kernel@vger.kernel.org,
	Michael Dege <michael.dege@renesas.com>
Subject: [PATCH net-next v4 05/13] net: renesas: rswitch: add exception path for packets with unknown dst MAC
Date: Mon, 11 May 2026 10:52:08 +0200	[thread overview]
Message-ID: <20260511-rswitch_add_vlans-v4-5-a5a225f8faae@renesas.com> (raw)
In-Reply-To: <20260511-rswitch_add_vlans-v4-0-a5a225f8faae@renesas.com>

Packets with unknown MAC address cannot be handled by the HW forwarding.
These need to be forwarded, via an exception path, to the network driver.

Creates a queue for the exeption path. Packets received with unknown
src/dst address need to be passed to the CPU. The received packet does not
have the correct source port information, this is derived from the
descriptor and added to the new queue. The received packet is added to the
new queue and sent to the CPU for MAC learning. The CPU will broadcast the
received packet, to all ports. This is how the HW learns the new MAC
address.

Signed-off-by: Michael Dege <michael.dege@renesas.com>
---
 drivers/net/ethernet/renesas/rswitch.h      |  2 +
 drivers/net/ethernet/renesas/rswitch_main.c | 94 ++++++++++++++++++++++++++---
 2 files changed, 89 insertions(+), 7 deletions(-)

diff --git a/drivers/net/ethernet/renesas/rswitch.h b/drivers/net/ethernet/renesas/rswitch.h
index e56c15dd4ecd..8415f52a239e 100644
--- a/drivers/net/ethernet/renesas/rswitch.h
+++ b/drivers/net/ethernet/renesas/rswitch.h
@@ -1148,6 +1148,7 @@ struct rswitch_gwca {
 	struct rswitch_gwca_queue *queues;
 	int num_queues;
 	struct rswitch_gwca_queue ts_queue;
+	struct rswitch_gwca_queue *l2_shared_rx_queue;
 	DECLARE_BITMAP(used, RSWITCH_MAX_NUM_QUEUES);
 	u32 tx_irq_bits[RSWITCH_NUM_IRQ_REGS];
 	u32 rx_irq_bits[RSWITCH_NUM_IRQ_REGS];
@@ -1162,6 +1163,7 @@ struct rswitch_device {
 	void __iomem *addr;
 	struct rswitch_gwca_queue *tx_queue;
 	struct rswitch_gwca_queue *rx_queue;
+	struct rswitch_gwca_queue *rx_old_queue;
 	struct sk_buff *ts_skb[TS_TAGS_PER_PORT];
 	DECLARE_BITMAP(ts_skb_used, TS_TAGS_PER_PORT);
 	bool disabled;
diff --git a/drivers/net/ethernet/renesas/rswitch_main.c b/drivers/net/ethernet/renesas/rswitch_main.c
index e05c42db5f4c..bf26c1a3384a 100644
--- a/drivers/net/ethernet/renesas/rswitch_main.c
+++ b/drivers/net/ethernet/renesas/rswitch_main.c
@@ -682,6 +682,34 @@ static int rswitch_rxdmac_init(struct rswitch_private *priv, unsigned int index)
 	return rswitch_gwca_queue_ext_ts_format(ndev->dev.parent, priv, rdev->rx_queue);
 }
 
+static int rswitch_shared_rx_queue_alloc(struct rswitch_private *priv)
+{
+	struct rswitch_gwca *gwca = &priv->gwca;
+	struct device *dev = &priv->pdev->dev;
+
+	int err;
+
+	gwca->l2_shared_rx_queue = rswitch_gwca_get(priv);
+	if (!gwca->l2_shared_rx_queue)
+		return -EBUSY;
+
+	err = rswitch_gwca_queue_alloc(NULL, priv, gwca->l2_shared_rx_queue, false, RX_RING_SIZE);
+	if (err < 0) {
+		rswitch_gwca_put(priv, gwca->l2_shared_rx_queue);
+		return err;
+	}
+
+	return rswitch_gwca_queue_ext_ts_format(dev, priv, gwca->l2_shared_rx_queue);
+}
+
+static void rswitch_shared_rx_queue_free(struct rswitch_private *priv)
+{
+	struct rswitch_gwca *gwca = &priv->gwca;
+
+	rswitch_gwca_queue_free(&priv->pdev->dev, gwca->l2_shared_rx_queue);
+	rswitch_gwca_put(priv, gwca->l2_shared_rx_queue);
+}
+
 static int rswitch_gwca_hw_init(struct rswitch_private *priv)
 {
 	unsigned int i;
@@ -719,6 +747,10 @@ static int rswitch_gwca_hw_init(struct rswitch_private *priv)
 			return err;
 	}
 
+	err = rswitch_shared_rx_queue_alloc(priv);
+	if (err < 0)
+		return err;
+
 	return rswitch_gwca_change_mode(priv, GWMC_OPC_OPERATION);
 }
 
@@ -730,6 +762,8 @@ static int rswitch_gwca_hw_deinit(struct rswitch_private *priv)
 	if (err < 0)
 		return err;
 
+	rswitch_shared_rx_queue_free(priv);
+
 	return rswitch_gwca_change_mode(priv, GWMC_OPC_DISABLE);
 }
 
@@ -938,10 +972,15 @@ static int rswitch_poll(struct napi_struct *napi, int budget)
 
 	if (napi_complete_done(napi, budget - quota)) {
 		spin_lock_irqsave(&priv->lock, flags);
+		if (rdev->rx_old_queue) {
+			rdev->rx_queue = rdev->rx_old_queue;
+			rdev->rx_old_queue = NULL;
+		}
 		if (test_bit(rdev->port, priv->opened_ports)) {
 			rswitch_enadis_data_irq(priv, rdev->tx_queue->index, true);
 			rswitch_enadis_data_irq(priv, rdev->rx_queue->index, true);
 		}
+		rswitch_enadis_data_irq(priv, priv->gwca.l2_shared_rx_queue->index, true);
 		spin_unlock_irqrestore(&priv->lock, flags);
 	}
 
@@ -954,15 +993,40 @@ static int rswitch_poll(struct napi_struct *napi, int budget)
 	return 0;
 }
 
-static void rswitch_queue_interrupt(struct net_device *ndev)
+static void rswitch_queue_interrupt(struct rswitch_private *priv, struct rswitch_gwca_queue *gq)
 {
-	struct rswitch_device *rdev = netdev_priv(ndev);
+	struct rswitch_ext_ts_desc *desc;
+	struct rswitch_device *rdev;
+	struct net_device *ndev;
+	unsigned int rx_q_index;
+	u32 spn;
+
+	rdev = netdev_priv(gq->ndev);
+	rx_q_index = rdev->rx_queue->index;
+
+/* If we receive a shared queue through the exception path, it will be missing the ndev
+ * pointer. This needs to be added to be able to determine from which port the packet was
+ * received. Then we temporarily exchange the rx_queue pointer in rdev. This will be
+ * restored after the packet has been processed.
+ */
+
+	if (gq->index == priv->gwca.l2_shared_rx_queue->index) {
+		desc = &gq->rx_ring[gq->cur];
+		spn = FIELD_GET(SPN, desc->info1);
+		ndev = priv->rdev[spn]->ndev;
+		rdev = netdev_priv(ndev);
+		gq->ndev = ndev;
+		/* store original rx_queue */
+		rdev->rx_old_queue = rdev->rx_queue;
+		rdev->rx_queue = gq;
+	}
 
 	if (napi_schedule_prep(&rdev->napi)) {
-		spin_lock(&rdev->priv->lock);
-		rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, false);
-		rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, false);
-		spin_unlock(&rdev->priv->lock);
+		spin_lock(&priv->lock);
+		rswitch_enadis_data_irq(priv, rdev->tx_queue->index, false);
+		rswitch_enadis_data_irq(priv, rx_q_index, false);
+		rswitch_enadis_data_irq(priv, priv->gwca.l2_shared_rx_queue->index, false);
+		spin_unlock(&priv->lock);
 		__napi_schedule(&rdev->napi);
 	}
 }
@@ -980,7 +1044,7 @@ static irqreturn_t rswitch_data_irq(struct rswitch_private *priv, u32 *dis)
 			continue;
 
 		rswitch_ack_data_irq(priv, gq->index);
-		rswitch_queue_interrupt(gq->ndev);
+		rswitch_queue_interrupt(priv, gq);
 	}
 
 	return IRQ_HANDLED;
@@ -1517,6 +1581,14 @@ static int rswitch_serdes_set_params(struct rswitch_device *rdev)
 	return phy_set_speed(rdev->serdes, rdev->etha->speed);
 }
 
+static void rswitch_etha_set_exception_path(struct rswitch_private *priv)
+{
+	iowrite32(FDMACUFEF, priv->addr + FWCEPRC2);
+	iowrite32(FIELD_PREP(EPCS, GWCA_INDEX) |
+		  FIELD_PREP(EPCSD, priv->gwca.l2_shared_rx_queue->index),
+		  priv->addr + FWCEPTC);
+}
+
 static int rswitch_ether_port_init_one(struct rswitch_device *rdev)
 {
 	int err;
@@ -1570,6 +1642,8 @@ static int rswitch_ether_port_init_all(struct rswitch_private *priv)
 	unsigned int i;
 	int err;
 
+	rswitch_etha_set_exception_path(priv);
+
 	rswitch_for_each_enabled_port(priv, i) {
 		err = rswitch_ether_port_init_one(priv->rdev[i]);
 		if (err)
@@ -1620,6 +1694,7 @@ static int rswitch_open(struct net_device *ndev)
 	bitmap_set(rdev->priv->opened_ports, rdev->port, 1);
 	rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, true);
 	rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, true);
+	rswitch_enadis_data_irq(rdev->priv, rdev->priv->gwca.l2_shared_rx_queue->index, true);
 	spin_unlock_irqrestore(&rdev->priv->lock, flags);
 
 	phy_start(ndev->phydev);
@@ -1646,6 +1721,7 @@ static int rswitch_stop(struct net_device *ndev)
 	spin_lock_irqsave(&rdev->priv->lock, flags);
 	rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, false);
 	rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, false);
+	rswitch_enadis_data_irq(rdev->priv, rdev->priv->gwca.l2_shared_rx_queue->index, false);
 	bitmap_clear(rdev->priv->opened_ports, rdev->port, 1);
 	spin_unlock_irqrestore(&rdev->priv->lock, flags);
 
@@ -1953,6 +2029,7 @@ static int rswitch_device_alloc(struct rswitch_private *priv, unsigned int index
 	rdev->port = index;
 	rdev->etha = &priv->etha[index];
 	rdev->addr = priv->addr;
+	rdev->rx_old_queue = NULL;
 
 	ndev->base_addr = (unsigned long)rdev->addr;
 	snprintf(ndev->name, IFNAMSIZ, "tsn%d", index);
@@ -2170,6 +2247,9 @@ static int renesas_eth_sw_probe(struct platform_device *pdev)
 	priv->gwca.index = AGENT_INDEX_GWCA;
 	priv->gwca.num_queues = min(RSWITCH_NUM_PORTS * NUM_QUEUES_PER_NDEV,
 				    RSWITCH_MAX_NUM_QUEUES);
+	/* One extra queue for L2 switch reception */
+	priv->gwca.num_queues = min(priv->gwca.num_queues + 1,
+				    RSWITCH_MAX_NUM_QUEUES);
 	priv->gwca.queues = devm_kcalloc(&pdev->dev, priv->gwca.num_queues,
 					 sizeof(*priv->gwca.queues), GFP_KERNEL);
 	if (!priv->gwca.queues)

-- 
2.43.0


  parent reply	other threads:[~2026-05-11  8:57 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-11  8:52 [net-next PATCH v4 00/13] net: renesas: rswitch: R-Car S4 add VLAN aware switching Michael Dege
2026-05-11  8:52 ` [PATCH net-next v4 01/13] net: renesas: rswitch: improve port change mode functions Michael Dege
2026-05-11  8:52 ` [PATCH net-next v4 02/13] net: renesas: rswitch: use device instead of net_device Michael Dege
2026-05-11  8:52 ` [PATCH net-next v4 03/13] net: renesas: rswitch: fix FWPC2 register access macros Michael Dege
2026-05-11  8:52 ` [PATCH net-next v4 04/13] net: renesas: rswitch: add register definitions for vlan support Michael Dege
2026-05-11  8:52 ` Michael Dege [this message]
2026-05-11  8:52 ` [PATCH net-next v4 06/13] net: renesas: rswitch: add forwarding rules for gwca Michael Dege
2026-05-11  8:52 ` [PATCH net-next v4 07/13] net: renesas: rswitch: make helper functions available to whole driver Michael Dege
2026-05-11  8:52 ` [PATCH net-next v4 08/13] net: renesas: rswitch: add basic vlan init to rswitch_fwd_init Michael Dege
2026-05-11  8:52 ` [PATCH net-next v4 09/13] net: renesas: rswitch: update port HW init Michael Dege
2026-05-11  8:52 ` [PATCH net-next v4 10/13] net: renesas: rswitch: clean up is_rdev rswitch_device checking Michael Dege
2026-05-11  8:52 ` [PATCH net-next v4 11/13] net: renesas: rswitch: add passing of rswitch_private into notifiers Michael Dege
2026-05-11  8:52 ` [PATCH net-next v4 12/13] net: renesas: rswitch: add handler for FDB notification Michael Dege
2026-05-11  8:52 ` [PATCH net-next v4 13/13] net: renesas: rswitch: add vlan aware switching Michael Dege

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260511-rswitch_add_vlans-v4-5-a5a225f8faae@renesas.com \
    --to=michael.dege@renesas.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-renesas-soc@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=niklas.soderlund@ragnatech.se \
    --cc=pabeni@redhat.com \
    --cc=paul@pbarker.dev \
    --cc=yoshihiro.shimoda.uh@renesas.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox