From nobody Tue Apr 7 14:20:57 2026 Received: from mail.tipi-net.de (mail.tipi-net.de [194.13.80.246]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 133EB38C406; Fri, 13 Mar 2026 09:21:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=194.13.80.246 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773393692; cv=none; b=Z3v5RWmFxso54DtGpVLJ3WmdbV411Q60U6SML2XI+LF4o98L6bhhWZpi4z1CbDSB5JKi1LwgaNntTtswce8uiFUwcGNVZRttkc7dWF5OwGmUkdl9od2B5c/1wjgI9+aC6j3CFICA65WCniCyG2dJ6MvnqVBNeykMxKcmnoKMiPI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773393692; c=relaxed/simple; bh=3kA9lM7oCDmwBK8M854mec3iiuzF6TGWVkELDzJqUno=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=AX6ceSG/3OXiNbZgtd1TzGZb0mHar7OYrvmiZ91pATOmTU20RDj/nyhXVEw1G4kuzr2HqzcXtLN3wkL7tD1cp5a5J2Lbyh3laK0rrggW+0OeiyWmELImUR3sRO0mvypVVCaxVgB/xmy+s4bE37t3g8AE8jOQI5wEbfxL1TdzH+k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=tipi-net.de; spf=pass smtp.mailfrom=tipi-net.de; dkim=pass (2048-bit key) header.d=tipi-net.de header.i=@tipi-net.de header.b=g/AiXdG2; arc=none smtp.client-ip=194.13.80.246 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=tipi-net.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=tipi-net.de Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=tipi-net.de header.i=@tipi-net.de header.b="g/AiXdG2" Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id 4C946A5707; Fri, 13 Mar 2026 10:21:22 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tipi-net.de; s=dkim; t=1773393683; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=sUBslANvz1iK7buw/1PFrNaGDaQNEW2r48m8snuZBHw=; b=g/AiXdG2441GysUrJsgQlQZUg9YWTsI8GD16OlJSlofhqQbCEuWMjqkeAtfXdkCPOVo0Y+ 8sENIDnCOC6mwyhrBwEJsEgC6sR+UXffqknD+Kl5oyrvEztlN6ou+UAR09ERKwZlIBU0P1 n/XAUu4LGj0Ddou+atCcTGqJMhIr1BRIvuAjdSTp4fxgdwsE/xtsR9FVD8TDVrY8Dvnwc0 VHWHcMet+x0Ay+NduqAhsafQHj1jeXhzZNawSxK3N+VySHLHpcTM7SOJibojGgQhAxQh+y WSzmKSgrzRjfyFgNMrGbIAhmaH5qwqRuSYH1QLi4hvdJajRV+CoElXGKpxdF/Q== From: Nicolai Buchwitz To: Andrew Lunn , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Doug Berger , Florian Fainelli Cc: Broadcom internal kernel review list , Vikas Gupta , Bhargava Marreddy , Rajashekar Hudumula , Eric Biggers , Heiner Kallweit , =?UTF-8?q?Markus=20Bl=C3=B6chl?= , Arnd Bergmann , netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Nicolai Buchwitz Subject: [PATCH net-next 5/6] net: bcmgenet: add XDP_REDIRECT and ndo_xdp_xmit support Date: Fri, 13 Mar 2026 10:21:00 +0100 Message-ID: <20260313092101.1344954-6-nb@tipi-net.de> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260313092101.1344954-1-nb@tipi-net.de> References: <20260313092101.1344954-1-nb@tipi-net.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" Add XDP_REDIRECT support and implement ndo_xdp_xmit for receiving redirected frames from other devices. XDP_REDIRECT uses xdp_do_redirect() in the RX path with xdp_do_flush() called once per NAPI poll cycle. ndo_xdp_xmit batches multiple frames into the default TX ring under a single spinlock acquisition, ringing the doorbell once after all frames are queued. Call xdp_features_set/clear_redirect_target in the setup path. Advertise NETDEV_XDP_ACT_REDIRECT and NETDEV_XDP_ACT_NDO_XMIT in xdp_features. Signed-off-by: Nicolai Buchwitz --- .../net/ethernet/broadcom/genet/bcmgenet.c | 93 +++++++++++++++---- 1 file changed, 76 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/e= thernet/broadcom/genet/bcmgenet.c index 373ba5878ca1..30181f9cff98 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -2305,21 +2305,21 @@ static struct sk_buff *bcmgenet_xdp_build_skb(struc= t bcmgenet_rx_ring *ring, return skb; } =20 +/* Submit a single XDP frame to the TX ring. Caller must hold ring->lock. + * Returns true on success. Does not ring the doorbell - caller must + * write TDMA_PROD_INDEX after batching. + */ static bool bcmgenet_xdp_xmit_frame(struct bcmgenet_priv *priv, + struct bcmgenet_tx_ring *ring, struct xdp_frame *xdpf) { - struct bcmgenet_tx_ring *ring =3D &priv->tx_rings[DESC_INDEX]; struct device *kdev =3D &priv->pdev->dev; struct enet_cb *tx_cb_ptr; dma_addr_t mapping; u32 len_stat; =20 - spin_lock(&ring->lock); - - if (ring->free_bds < 1) { - spin_unlock(&ring->lock); + if (ring->free_bds < 1) return false; - } =20 tx_cb_ptr =3D bcmgenet_get_txcb(priv, ring); =20 @@ -2328,7 +2328,6 @@ static bool bcmgenet_xdp_xmit_frame(struct bcmgenet_p= riv *priv, tx_cb_ptr->skb =3D NULL; tx_cb_ptr->xdpf =3D NULL; bcmgenet_put_txcb(priv, ring); - spin_unlock(&ring->lock); return false; } =20 @@ -2347,12 +2346,14 @@ static bool bcmgenet_xdp_xmit_frame(struct bcmgenet= _priv *priv, ring->prod_index++; ring->prod_index &=3D DMA_P_INDEX_MASK; =20 + return true; +} + +static void bcmgenet_xdp_ring_doorbell(struct bcmgenet_priv *priv, + struct bcmgenet_tx_ring *ring) +{ bcmgenet_tdma_ring_writel(priv, ring->index, ring->prod_index, TDMA_PROD_INDEX); - - spin_unlock(&ring->lock); - - return true; } =20 static unsigned int @@ -2368,16 +2369,30 @@ bcmgenet_run_xdp(struct bcmgenet_rx_ring *ring, str= uct bpf_prog *prog, switch (act) { case XDP_PASS: return XDP_PASS; - case XDP_TX: + case XDP_TX: { + struct bcmgenet_tx_ring *tx_ring; + + tx_ring =3D &priv->tx_rings[DESC_INDEX]; xdpf =3D xdp_convert_buff_to_frame(xdp); - if (unlikely(!xdpf) || - unlikely(!bcmgenet_xdp_xmit_frame(priv, xdpf))) { - page_pool_put_full_page(ring->page_pool, rx_page, - true); + if (unlikely(!xdpf)) + goto drop_page; + + spin_lock(&tx_ring->lock); + if (unlikely(!bcmgenet_xdp_xmit_frame(priv, tx_ring, xdpf))) { + spin_unlock(&tx_ring->lock); + xdp_return_frame_rx_napi(xdpf); return XDP_DROP; } + bcmgenet_xdp_ring_doorbell(priv, tx_ring); + spin_unlock(&tx_ring->lock); return XDP_TX; + } + case XDP_REDIRECT: + if (unlikely(xdp_do_redirect(priv->dev, xdp, prog))) + goto drop_page; + return XDP_REDIRECT; case XDP_DROP: +drop_page: page_pool_put_full_page(ring->page_pool, rx_page, true); return XDP_DROP; default: @@ -2400,6 +2415,7 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_= rx_ring *ring, struct bcmgenet_priv *priv =3D ring->priv; struct net_device *dev =3D priv->dev; struct bpf_prog *xdp_prog; + bool xdp_flush =3D false; struct enet_cb *cb; struct sk_buff *skb; u32 dma_length_status; @@ -2538,6 +2554,8 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_= rx_ring *ring, =20 xdp_act =3D bcmgenet_run_xdp(ring, xdp_prog, &xdp, rx_page); + if (xdp_act =3D=3D XDP_REDIRECT) + xdp_flush =3D true; if (xdp_act !=3D XDP_PASS) goto next; =20 @@ -2611,6 +2629,9 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_= rx_ring *ring, bcmgenet_rdma_ring_writel(priv, ring->index, ring->c_index, RDMA_CONS_IN= DEX); } =20 + if (xdp_flush) + xdp_do_flush(); + ring->dim.bytes =3D bytes_processed; ring->dim.packets =3D rxpktprocessed; =20 @@ -3903,10 +3924,16 @@ static int bcmgenet_xdp_setup(struct net_device *de= v, return -EOPNOTSUPP; } =20 + if (!prog) + xdp_features_clear_redirect_target(dev); + old_prog =3D xchg(&priv->xdp_prog, prog); if (old_prog) bpf_prog_put(old_prog); =20 + if (prog) + xdp_features_set_redirect_target(dev, false); + return 0; } =20 @@ -3920,6 +3947,36 @@ static int bcmgenet_xdp(struct net_device *dev, stru= ct netdev_bpf *xdp) } } =20 +static int bcmgenet_xdp_xmit(struct net_device *dev, int num_frames, + struct xdp_frame **frames, u32 flags) +{ + struct bcmgenet_priv *priv =3D netdev_priv(dev); + struct bcmgenet_tx_ring *ring =3D &priv->tx_rings[DESC_INDEX]; + int sent =3D 0; + int i; + + if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) + return -EINVAL; + + if (unlikely(!netif_running(dev))) + return -ENETDOWN; + + spin_lock(&ring->lock); + + for (i =3D 0; i < num_frames; i++) { + if (!bcmgenet_xdp_xmit_frame(priv, ring, frames[i])) + break; + sent++; + } + + if (sent) + bcmgenet_xdp_ring_doorbell(priv, ring); + + spin_unlock(&ring->lock); + + return sent; +} + static const struct net_device_ops bcmgenet_netdev_ops =3D { .ndo_open =3D bcmgenet_open, .ndo_stop =3D bcmgenet_close, @@ -3932,6 +3989,7 @@ static const struct net_device_ops bcmgenet_netdev_op= s =3D { .ndo_get_stats64 =3D bcmgenet_get_stats64, .ndo_change_carrier =3D bcmgenet_change_carrier, .ndo_bpf =3D bcmgenet_xdp, + .ndo_xdp_xmit =3D bcmgenet_xdp_xmit, }; =20 /* GENET hardware parameters/characteristics */ @@ -4234,7 +4292,8 @@ static int bcmgenet_probe(struct platform_device *pde= v) NETIF_F_RXCSUM; dev->hw_features |=3D dev->features; dev->vlan_features |=3D dev->features; - dev->xdp_features =3D NETDEV_XDP_ACT_BASIC; + dev->xdp_features =3D NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT | + NETDEV_XDP_ACT_NDO_XMIT; =20 netdev_sw_irq_coalesce_default_on(dev); =20 --=20 2.51.0