* Re: [PATCH net-next] octeontx2-af: npc: Warn on NPC_IPSEC_SPI key overlap
From: Ratheesh Kannoth @ 2026-07-21 7:13 UTC (permalink / raw)
To: Simon Horman
Cc: linux-kernel, netdev, andrew+netdev, davem, edumazet, kuba,
pabeni, sgoutham, hkelam
In-Reply-To: <20260720171857.278396-2-horms@kernel.org>
On 2026-07-20 at 22:48:58, Simon Horman (horms@kernel.org) wrote:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> https://sashiko.dev/#/patchset/20260713144247.545592-1-rkannoth@marvell.com?part=1
> ---
> --- Patch [1]: [PATCH net-next] octeontx2-af: npc: Warn on NPC_IPSEC_SPI key overlap ---
> commit f018da26dc974202fbe56b064f0bc0375c74613a
> Author: Hariprasad Kelam <hkelam@marvell.com>
>
> octeontx2-af: npc: Warn on NPC_IPSEC_SPI key overlap
>
> When scanning the MKEX profile to determine supported NPC features, warn
> if the SPI extraction field overlaps with other key fields. AH and ESP
> may legitimately use the same key offset for SPI, so continue to
> advertise NPC_IPSEC_SPI via npc_is_field_present() instead of treating
> the overlap as a hard failure.
>
> > diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
> > index 91b5947dae060..b217c1c19810a 100644
> > --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
> > +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
> > @@ -730,6 +730,9 @@ static void npc_set_features(struct rvu *rvu, int blkaddr, u8 intf)
> > *features &= ~BIT_ULL(NPC_OUTER_VID);
> >
> > /* Allow extracting SPI field from AH and ESP headers at same offset */
> > + if (npc_check_overlap(rvu, blkaddr, NPC_IPSEC_SPI, 0, intf))
> > + dev_warn(rvu->dev, "Overlap detected the field NPC_IPSEC_SPI\n");
>
> [Severity: Medium]
> The commit message notes that AH and ESP may legitimately use the same key
> offset for SPI. However, will this new dev_warn() trigger anyway for those
> valid configurations?
>
> If an MKEX profile configures AH and ESP SPI extraction at the same
> offset, npc_check_overlap() will still see them as mutually exclusive fields
> with different lids (LID_LD and LID_LE). Does this result in spurious
> warnings polluting the kernel log on valid hardware configurations?
>
> [Severity: High]
> This is a pre-existing issue, but does npc_check_overlap() have a data
> race when called concurrently?
>
> Looking at npc_check_overlap(), it assigns a local pointer to the shared
> scratchpad array and then mutates it:
>
> rvu_npc_fs.c:npc_check_overlap() {
> ...
> dummy = &mcam->rx_key_fields[NPC_UNKNOWN];
> ...
> memset(dummy, 0, sizeof(struct npc_key_field));
> npc_set_layer_mdata(rvu, mcam, NPC_UNKNOWN, cfg, lid, lt, intf);
> ...
> }
>
> If multiple VFs simultaneously send mailbox commands that trigger overlap
> checks across different CPUs, won't they overwrite this global dummy struct
> and corrupt each other's flow validation?
>
> > + /* Set SPI flag only if AH/ESP and IPSEC_SPI are in the key */
> > if (npc_is_field_present(rvu, NPC_IPSEC_SPI, intf) &&
> > (*features & (BIT_ULL(NPC_IPPROTO_ESP) | BIT_ULL(NPC_IPPROTO_AH))))
> > *features |= BIT_ULL(NPC_IPSEC_SPI);
These comments are not valid. Let me modify the code omment so that AI reviewer
wont raise this again.
pw-bot: changes-requested
^ permalink raw reply
* Re: [PATCH net-next v10 0/4] net: stmmac: eic7700: add eth1 variant support and update delay bindings
From: Paolo Abeni @ 2026-07-21 7:22 UTC (permalink / raw)
To: lizhi2, devicetree, andrew+netdev, davem, edumazet, kuba, robh,
krzk+dt, conor+dt, netdev, mcoquelin.stm32, alexandre.torgue,
rmk+kernel, linux-stm32, linux-arm-kernel, linux-kernel,
maxime.chevallier
Cc: ningyu, linmin, pinkesh.vaghela, pritesh.patel, weishangjuan,
horms
In-Reply-To: <20260707064033.1265-1-lizhi2@eswincomputing.com>
On 7/7/26 8:40 AM, lizhi2@eswincomputing.com wrote:
> From: Zhi Li <lizhi2@eswincomputing.com>
>
> This series updates Ethernet support for the ESWIN EIC7700 SoC,
> including support for the eth1 MAC variant.
>
> The series includes DT binding updates and stmmac glue driver updates
> for the EIC7700 Ethernet controller.
>
> The changes include:
> - Update the tx-internal-delay-ps binding property from a fixed enum
> list to a range-based definition, and make it optional with the
> corresponding driver update.
>
> - Add support for the EIC7700 eth1 MAC variant, which has
> silicon-specific RX sampling behavior and TX timing characteristics.
> A dedicated compatible string is introduced for this hardware variant,
> and the driver applies the required RX clock inversion handling and
> timing adjustments.
>
> Due to silicon characteristics, the eth1 interface has a fixed TX
> internal delay of approximately 2 ns and an RX sampling skew of 4-5 ns
> that cannot be compensated solely by standard RGMII delay settings.
> The binding models the effective TX delay range of this variant, while
> the driver handles the required hardware-specific timing configuration.
>
> Only the DT bindings and driver patches are included in this series. The
> DTS changes are intentionally omitted for the following reasons:
> - The HSP bus infrastructure is being introduced by Pinkesh Vaghela's
> DT series [0], currently under review.
> - The HSPCRG clock/reset series [1] provides additional infrastructure
> for the HSP subsystem.
>
> Once these dependencies are merged, a follow-up DT series will enable
> Ethernet, USB, eMMC, and SD across the HSP bus.
>
> Reference:
> [0] https://lore.kernel.org/lkml/20260706081055.1126275-1-pinkesh.vaghela@einfochips.com/
> [1] https://lore.kernel.org/all/20260605060730.1605-1-dongxuyang@eswincomputing.com/
Applied, thanks!
/P
^ permalink raw reply
* Re: [PATCH net] net/packet: defer vmalloc TX_RING free until skbs finish
From: Eric Dumazet @ 2026-07-21 7:26 UTC (permalink / raw)
To: Kyle Zeng
Cc: netdev, Jakub Kicinski, David S . Miller, Willem de Bruijn,
stable
In-Reply-To: <20260721015824.45829-1-kylebot@openai.com>
On Tue, Jul 21, 2026 at 3:58 AM Kyle Zeng <kylebot@openai.com> wrote:
>
> AF_PACKET TX_RING skbs keep a raw pointer to their ring frame. The skb
> page references preserve page-backed ring blocks after pg_vec is freed,
> but they do not preserve a vmalloc mapping.
>
> tpacket_destruct_skb() currently drops the pending reference before
> writing the timestamp and TP_STATUS_AVAILABLE to the frame. Move the
> decrement after those stores. The smp_wmb() in __packet_set_status()
> orders the frame stores before the decrement.
>
> On socket close, scan every pg_vec entry because allocation can produce
> a mixture of page-backed and vmalloc-backed blocks. If any block is
> vmalloc-backed and TX skbs remain pending, defer the whole vector to
> system_long_wq.
>
> After pg_vec is detached, a late destructor can skip the pending
> decrement. Use socket write-memory accounting as the deferred lifetime
> gate instead: an skb remains charged through its final sock_wfree(),
> after all ring-frame accesses. The delayed work retains a socket
> reference and reschedules itself until no TX skbs remain. Fall back to
> a synchronous wait if the work allocation fails.
>
> Move pending_refcnt release to packet_sock_destruct() so late skb
> destructors and deferred cleanup can safely use it after
> packet_release(). Page-backed teardown remains synchronous, and no lock
> is added to the TX completion hot path.
>
> Fixes: b013840810c2 ("packet: use percpu mmap tx frame pending refcount")
> Cc: stable@vger.kernel.org
> Suggested-by: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
> Assisted-by: Codex:gpt-5.6
> Signed-off-by: Kyle Zeng <kylebot@openai.com>
> ---
Hi Kyle,
Rather than doing kmalloc() inside free_pg_vec() during socket teardown/close,
would it be better to pre-allocate the deferred work storage at ring setup
time (in alloc_pg_vec / packet_set_ring)?
Pre-allocating at ring setup has a few advantages:
1) If allocation fails, setsockopt(PACKET_TX_RING) fails early with -ENOMEM,
avoiding allocation failures during socket close/teardown.
2) Teardown becomes deterministic: free_pg_vec() is guaranteed to have
the storage available to queue work.
3) We can drop the extra fallback logic (packet_wait_for_tx_skbs(),
po->skb_completion, and the 1-jiffy fallback polling loop).
You could embed a struct delayed_work (or a header struct) into the allocation
returned by alloc_pg_vec().
Thanks,
^ permalink raw reply
* Re: [PATCH net] net/iucv: fix use-after-free of a severed iucv_path
From: patchwork-bot+netdevbpf @ 2026-07-21 7:30 UTC (permalink / raw)
To: Bryam Vargas
Cc: pabeni, twinkler, kuba, wintera, edumazet, davem, linux-s390,
hidayath, linux-kernel, netdev, horms, nagamani
In-Reply-To: <20260707-b4-disp-783fedbb-v1-1-463b9dbda2ea@proton.me>
Hello:
This patch was applied to netdev/net.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Tue, 07 Jul 2026 02:00:54 -0500 you wrote:
> From: Bryam Vargas <hexlabsecurity@proton.me>
>
> af_iucv queues not-yet-received message notifications on iucv->message_q,
> each holding a raw pointer to the connection's iucv_path. When the peer
> severs the connection, iucv_sever_path() frees that path with
> iucv_path_free() but leaves the notifications queued. A later recvmsg()
> drains message_q via iucv_process_message_q() and hands the stale path to
> message_receive() -- a use-after-free of the freed iucv_path.
>
> [...]
Here is the summary with links:
- [net] net/iucv: fix use-after-free of a severed iucv_path
https://git.kernel.org/netdev/net/c/be7cc4656eb1
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* [PATCH] vhost-scsi: flush backend after device ioctls
From: Jia Jia @ 2026-07-21 7:36 UTC (permalink / raw)
To: Michael S . Tsirkin, Jason Wang, Mike Christie
Cc: Paolo Bonzini, Stefan Hajnoci, Eugenio Pérez, virtualization,
kvm, netdev, Jia Jia
vhost-scsi translates guest response descriptors into userspace iovecs
at command submission time and later completes those commands
asynchronously through target-core. Device-wide control operations such
as VHOST_SET_MEM_TABLE replace the memory table under the device and
virtqueue mutexes, but historically returned without waiting for
outstanding SCSI commands that still hold the pre-update response
iovecs.
After such a replacement, completion may write virtio_scsi_cmd_resp
through the old host virtual addresses. If the owner has already
remapped those addresses, the write lands on the wrong userspace object.
The kernel tree has carried a TODO for this since the 2012 split of
vhost_dev_ioctl() and vhost_vring_ioctl():
/* TODO: flush backend after dev ioctl. */
A userspace test kept a READ(10) pending, replaced the memory table so
the response GPA mapped to a new HVA, remapped the old response address
as a victim page, and then let the command complete. The completion
wrote the victim page (victim_changed=yes) and left the replacement
page unchanged; a later TUR updated the new mapping instead. So the
pending command retained the pre-update response address across
VHOST_SET_MEM_TABLE.
That same 2012 change deliberately avoided a second backend flush on the
vring-ioctl path: vring updates already flush where appropriate, and an
extra heavy flush would hurt when kick or call fds are reconfigured on
the data path. This fix does not reintroduce that. The default branch
still routes unknown commands through vhost_dev_ioctl() first; only a
non-ENOIOCTLCMD result flushes. Vring ops such as SET_VRING_KICK/CALL,
num, addr, and base return -ENOIOCTLCMD there and fall through to
vhost_vring_ioctl() without this backend flush.
What vhost_dev_ioctl() actually handles on this path is small:
VHOST_SET_OWNER, VHOST_SET_MEM_TABLE, VHOST_SET_LOG_BASE,
VHOST_SET_LOG_FD, and the optional fork-owner ioctls when enabled.
Flushing after those is fine: they are rare device-wide control ops,
and SET_OWNER normally runs before any inflight SCSI work. Call
vhost_scsi_flush() so pre-update worker work and target-core inflight
commands finish before the ioctl returns. As with the existing net
pattern, any non-ENOIOCTLCMD result flushes, including failures that
may have applied a partial update such as VHOST_SET_LOG_BASE.
I later noticed vhost-net and vhost-vsock already use the same device
versus vring split.
This is a control-plane barrier only. Ordinary submission, completion,
kick, and call paths are unchanged. The owner is expected to keep
pre-update mappings valid until the device ioctl returns. Completion
copies the response and signals from the vhost worker without needing
further userspace progress, so waiting in this ioctl does not leave the
owner process stuck on itself.
Signed-off-by: Jia Jia <physicalmtea@gmail.com>
---
drivers/vhost/scsi.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index 9a1253b9d8c5..c3e8f1a0b2d4 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -2424,10 +2424,11 @@ vhost_scsi_ioctl(struct file *f, unsigned int ioctl, unsigned long arg)
default:
mutex_lock(&vs->dev.mutex);
r = vhost_dev_ioctl(&vs->dev, ioctl, argp);
- /* TODO: flush backend after dev ioctl. */
if (r == -ENOIOCTLCMD)
r = vhost_vring_ioctl(&vs->dev, ioctl, argp);
+ else
+ vhost_scsi_flush(vs);
mutex_unlock(&vs->dev.mutex);
return r;
}
}
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v2 net-next 0/3] geneve: make geneve_fill_info() RTNL-less
From: patchwork-bot+netdevbpf @ 2026-07-21 7:40 UTC (permalink / raw)
To: Eric Dumazet
Cc: davem, kuba, pabeni, horms, kuniyu, andrew+netdev, netdev,
eric.dumazet
In-Reply-To: <20260707145331.3717941-1-edumazet@google.com>
Hello:
This series was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Tue, 7 Jul 2026 14:53:27 +0000 you wrote:
> This series makes geneve_fill_info() independent of the RTNL lock by
> converting the device configuration to an RCU-protected pointer.
>
> Historically, geneve_changelink() updated the device configuration by
> copying the new configuration over the old one using memcpy() under RTNL.
>
> To prevent the transmit/receive data paths from reading torn values during
> the copy, geneve_quiesce() was used to pause the data path and wait for
> a synchronize_net(), causing packet loss and latency.
>
> [...]
Here is the summary with links:
- [v2,net-next,1/3] geneve: pass geneve_config pointer to helper functions
https://git.kernel.org/netdev/net-next/c/777434f53e77
- [v2,net-next,2/3] geneve: convert config to RCU-protected pointer
https://git.kernel.org/netdev/net-next/c/0ba269933f73
- [v2,net-next,3/3] geneve: make geneve_fill_info() RTNL independent
https://git.kernel.org/netdev/net-next/c/5415c41a8378
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v5 2/7] dt-bindings: nvmem: layouts: add Microchip/SST SFDP EUI layout
From: Linus Walleij @ 2026-07-21 7:45 UTC (permalink / raw)
To: Manikandan Muralidharan
Cc: pratyush, mwalle, takahiro.kuwano, miquel.raynal, richard,
vigneshr, robh, krzk+dt, conor+dt, srini, nicolas.ferre,
alexandre.belloni, claudiu.beznea, linux, richardcochran, arnd,
michael, linux-mtd, devicetree, linux-kernel, linux-arm-kernel,
netdev
In-Reply-To: <20260721052859.171341-3-manikandan.m@microchip.com>
On Tue, Jul 21, 2026 at 7:29 AM Manikandan Muralidharan
<manikandan.m@microchip.com> wrote:
> Add a binding for the NVMEM layout that exposes the factory-programmed
> EUI-48 identifier from the Microchip/SST vendor-specific SFDP parameter
> table (e.g. SST26VF064BEUI) as a "mac-address" NVMEM cell, and reference
> it from nvmem-layout.yaml.
>
> Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>
I like it!
Reviewed-by: Linus Walleij <linusw@kernel.org>
Yours,
Linus Walleij
^ permalink raw reply
* Re: [PATCH v5 6/7] ARM: dts: microchip: sama5d27_wlsom1: read MAC address from QSPI SFDP
From: Linus Walleij @ 2026-07-21 7:49 UTC (permalink / raw)
To: Manikandan Muralidharan
Cc: pratyush, mwalle, takahiro.kuwano, miquel.raynal, richard,
vigneshr, robh, krzk+dt, conor+dt, srini, nicolas.ferre,
alexandre.belloni, claudiu.beznea, linux, richardcochran, arnd,
michael, linux-mtd, devicetree, linux-kernel, linux-arm-kernel,
netdev
In-Reply-To: <20260721052859.171341-7-manikandan.m@microchip.com>
On Tue, Jul 21, 2026 at 7:30 AM Manikandan Muralidharan
<manikandan.m@microchip.com> wrote:
> Describe the QSPI flash SFDP as an NVMEM provider with the
> microchip,sst26vf-sfdp-eui layout, which exposes the factory-programmed
> EUI-48 as a "mac-address" cell, and point macb0 at it through
> nvmem-cells. This yields a stable MAC address on boards where U-Boot does
> not program one, instead of falling back to a random address.
>
> Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>
Reviewed-by: Linus Walleij <linusw@kernel.org>
Yours,
Linus Walleij
^ permalink raw reply
* Re: [PATCH v5 7/7] ARM: configs: sama5: enable Microchip/SST SFDP EUI NVMEM layout
From: Linus Walleij @ 2026-07-21 7:49 UTC (permalink / raw)
To: Manikandan Muralidharan
Cc: pratyush, mwalle, takahiro.kuwano, miquel.raynal, richard,
vigneshr, robh, krzk+dt, conor+dt, srini, nicolas.ferre,
alexandre.belloni, claudiu.beznea, linux, richardcochran, arnd,
michael, linux-mtd, devicetree, linux-kernel, linux-arm-kernel,
netdev
In-Reply-To: <20260721052859.171341-8-manikandan.m@microchip.com>
On Tue, Jul 21, 2026 at 7:31 AM Manikandan Muralidharan
<manikandan.m@microchip.com> wrote:
> Enable CONFIG_NVMEM_LAYOUT_SST26VF_SFDP_EUI so the factory EUI-48 stored
> in the SST26VF QSPI flash SFDP can be used as a MAC address on boards
> such as the sama5d27_wlsom1.
>
> Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>
Reviewed-by: Linus Walleij <linusw@kernel.org>
Yours,
Linus Walleij
^ permalink raw reply
* Re: [PATCH net v5] net: airoha: fix MIB stats collection to be lossless
From: patchwork-bot+netdevbpf @ 2026-07-21 7:50 UTC (permalink / raw)
To: Aniket Negi
Cc: netdev, lorenzo, matthias.bgg, angelogioacchino.delregno,
aniket.negi, andrew+netdev, davem, edumazet, kuba, pabeni, horms,
ansuelsmth, linux-arm-kernel, linux-mediatek, linux-kernel
In-Reply-To: <20260707152639.105628-1-aniket.negi03@gmail.com>
Hello:
This patch was applied to netdev/net.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Tue, 7 Jul 2026 20:56:39 +0530 you wrote:
> REG_FE_GDM_MIB_CLEAR after every read creates a race window where
> packets arriving between read and clear are lost from statistics.
>
> Switch to a delta-based approach instead:
>
> - 64-bit H+L registers (ok pkts/bytes, E64..L1023): read absolute
> hardware total directly into a local variable; clamp with max(new, old)
> to prevent torn-read regression when the counter carries between the
> two reads.
>
> [...]
Here is the summary with links:
- [net,v5] net: airoha: fix MIB stats collection to be lossless
https://git.kernel.org/netdev/net/c/d163725af84a
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net] net/packet: defer vmalloc TX_RING free until skbs finish
From: Willem de Bruijn @ 2026-07-21 7:57 UTC (permalink / raw)
To: Eric Dumazet, Kyle Zeng
Cc: netdev, Jakub Kicinski, David S . Miller, Willem de Bruijn,
stable
In-Reply-To: <CANn89i+YuuBJ1J8CGD0Eo3JKeNjEd=5tT-3h=6stE5QoYczYXw@mail.gmail.com>
Eric Dumazet wrote:
> On Tue, Jul 21, 2026 at 3:58 AM Kyle Zeng <kylebot@openai.com> wrote:
> >
> > AF_PACKET TX_RING skbs keep a raw pointer to their ring frame. The skb
> > page references preserve page-backed ring blocks after pg_vec is freed,
> > but they do not preserve a vmalloc mapping.
> >
> > tpacket_destruct_skb() currently drops the pending reference before
> > writing the timestamp and TP_STATUS_AVAILABLE to the frame. Move the
> > decrement after those stores. The smp_wmb() in __packet_set_status()
> > orders the frame stores before the decrement.
> >
> > On socket close, scan every pg_vec entry because allocation can produce
> > a mixture of page-backed and vmalloc-backed blocks. If any block is
> > vmalloc-backed and TX skbs remain pending, defer the whole vector to
> > system_long_wq.
> >
> > After pg_vec is detached, a late destructor can skip the pending
> > decrement. Use socket write-memory accounting as the deferred lifetime
> > gate instead: an skb remains charged through its final sock_wfree(),
> > after all ring-frame accesses. The delayed work retains a socket
> > reference and reschedules itself until no TX skbs remain. Fall back to
> > a synchronous wait if the work allocation fails.
> >
> > Move pending_refcnt release to packet_sock_destruct() so late skb
> > destructors and deferred cleanup can safely use it after
> > packet_release(). Page-backed teardown remains synchronous, and no lock
> > is added to the TX completion hot path.
> >
> > Fixes: b013840810c2 ("packet: use percpu mmap tx frame pending refcount")
> > Cc: stable@vger.kernel.org
> > Suggested-by: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
> > Assisted-by: Codex:gpt-5.6
> > Signed-off-by: Kyle Zeng <kylebot@openai.com>
> > ---
>
> Hi Kyle,
>
> Rather than doing kmalloc() inside free_pg_vec() during socket teardown/close,
> would it be better to pre-allocate the deferred work storage at ring setup
> time (in alloc_pg_vec / packet_set_ring)?
Probably superfluous, but: only if vmalloc was used.
Neat secondary feature will be that the non-NULL status of that
pointer can be used to detect use of vmalloc at teardown, without
having to iterate over all the pgvec entries.
> Pre-allocating at ring setup has a few advantages:
>
> 1) If allocation fails, setsockopt(PACKET_TX_RING) fails early with -ENOMEM,
> avoiding allocation failures during socket close/teardown.
>
> 2) Teardown becomes deterministic: free_pg_vec() is guaranteed to have
> the storage available to queue work.
>
> 3) We can drop the extra fallback logic (packet_wait_for_tx_skbs(),
> po->skb_completion, and the 1-jiffy fallback polling loop).
>
> You could embed a struct delayed_work (or a header struct) into the allocation
> returned by alloc_pg_vec().
>
> Thanks,
^ permalink raw reply
* Re: [PATCH 7/8] net: mv643xx: use platform_device_set_fwnode()
From: Bartosz Golaszewski @ 2026-07-21 7:57 UTC (permalink / raw)
To: Andrew Lunn
Cc: Bartosz Golaszewski, Greg Kroah-Hartman, Rafael J. Wysocki,
Danilo Krummrich, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Andi Shyti,
Joerg Roedel (AMD), Will Deacon, Robin Murphy, Andy Shevchenko,
Doug Berger, Florian Fainelli,
Broadcom internal kernel review list, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Ulf Hansson, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Lee Jones, Sebastian Hesselbarth,
Srinivas Kandagatla, driver-core, linuxppc-dev, linux-kernel,
linux-i2c, iommu, netdev, linux-pm, imx, linux-arm-kernel, mfd,
linux-arm-msm, linux-sound, Bartosz Golaszewski
In-Reply-To: <3e14e41f-ecd5-432b-9f52-690a05b38a8a@lunn.ch>
On Mon, 20 Jul 2026 20:28:39 +0200, Andrew Lunn <andrew@lunn.ch> said:
> On Mon, Jul 20, 2026 at 06:01:37PM +0200, Bartosz Golaszewski wrote:
>> On Mon, 20 Jul 2026 16:43:40 +0200, Andrew Lunn <andrew@lunn.ch> said:
>> > On Mon, Jul 20, 2026 at 11:24:54AM +0200, Bartosz Golaszewski wrote:
>> >> Prefer the higher-level platform_device_set_fwnode() over the
>> >> OF-specific platform_device_set_of_node() for dynamically allocated
>> >> platform devices.
>> >>
>> >> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
>> >> ---
>> >> drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +-
>> >> 1 file changed, 1 insertion(+), 1 deletion(-)
>> >>
>> >> diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c
>> >> index 9caa1e47c174c9d7a161b7f2e2ee12a829b813d4..2f2d6cce8d852b9ec3ab42678a04a7915d1f00cc 100644
>> >> --- a/drivers/net/ethernet/marvell/mv643xx_eth.c
>> >> +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c
>> >> @@ -2780,7 +2780,7 @@ static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev,
>> >> goto put_err;
>> >> }
>> >> ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
>> >> - platform_device_set_of_node(ppdev, pnp);
>> >> + platform_device_set_fwnode(ppdev, of_fwnode_handle(pnp));
>> >
>> > This is definitely an OF only driver. There are no other calls to
>> > fwnode functions in this driver, so this is the wrong thing to do.
>> >
>> > Sorry, NACK.
>> >
>>
>> I'm not going to die on this hill but drivers are OF-only until they're not.
>> For example, Qualcomm is now working on a hybrid ACPI-OF approach for
>> laptops[1] and we may end up needing to start converting drivers to fwnode
>> after all.
>>
>> There's no real benefit to sticking to OF-specific APIs unless you need to
>> iterate over all properties of a node or use some other functionality not
>> available in fwnode. The overhead is minimal and it's never a hot path.
>
> There is a lot of benefit to sticking to OF specific APIs, because
> within the kernel OF is well maintained, has active maintainers, there
> are tools to validate bindings, etc. ACPI is a Wild West, each driver
> is a snow flake, there is no review, no binding documentation, no
> validation tools etc.
>
> I hope you allow plenty of time to convert any networking drivers,
> where Linux is driving the hardware, to ACPI. Your first stop will be
> the UEFI forum making a proposal for MDIO busses, because that
> currently is not part of ACPI. You will then need to spend time
> understanding the DT bindings, and figuring out which properties are
> deprecated so need to stay OF only, and which can be converted to dual
> OF/ACPI. I've seen too many naive attempts which blindly convert
> everything, copying all the past errors in the DT binding into the
> brand new ACPI binding. That will get NACKed.
>
> OF != ACPI
>
I've never said that and ACPI is actually irrelevant to this discussion. Fwnode
is the abstraction layer and OF happens to implement it. Unless you need
fine-grained control (only exposed by the lower-level abstraction) or
performance - neither of which is the case here - you should use the top-level
API. As I said: there's no good reason to stick to OF-specific interfaces for
drivers that don't really require it - and even then, we should strive to fill
the gaps in the fwnode API instead.
Bart
^ permalink raw reply
* Re: [PATCH net] tun: prevent underflow in rx headroom calculation
From: Willem de Bruijn @ 2026-07-21 7:59 UTC (permalink / raw)
To: Asim Viladi Oglu Manizada, netdev
Cc: Willem de Bruijn, Jason Wang, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, daniel
In-Reply-To: <20260721014117.2234892-1-manizada@pm.me>
Asim Viladi Oglu Manizada wrote:
> tun_get_user() calculates good_linear with SKB_MAX_HEAD(align). The align
> value comes from ndo_set_rx_headroom() and can be larger than the linear
> space available in a one-page skb head.
>
> OVS can reach this case by carrying headroom from a netkit/VXLAN port to a
> TUN port. SKB_MAX_HEAD() then underflows, leaving good_linear negative.
Separate from protecting tun from such huge headroom, which is good:
Is cooking paths with such huge headroom itself incorrect? Should
netkit have some reasonable upper bound on configurable headroom?
Netkit is not the only path that can reach this, right?
> Assigning that value to the size_t linear variable in tun_get_user()
> converts it to a large positive value. The wrapped value is passed to
> tun_alloc_skb(), where prepad + linear and len - linear wrap. skb->data can
> then end up past the allocated head, and later packet processing can access
> memory outside the skb.
>
> Clamp good_linear to zero when SKB_MAX_HEAD() returns a negative value.
> This lets tun_alloc_skb() allocate the requested headroom and place the
> packet data linearly or in fragments without wrapping.
>
> Fixes: eaea34b23c46 ("net/tun: implement ndo_set_rx_headroom")
> Cc: stable@vger.kernel.org
> Assisted-by: avom-custom-harness:gpt-5.5-qwen3.6-mod-mix
> Signed-off-by: Asim Viladi Oglu Manizada <manizada@pm.me>
Reviewed-by: Willem de Bruijn <willemb@google.com>
^ permalink raw reply
* Re: [PATCH v3 net-next 0/9] Switch support
From: Ratheesh Kannoth @ 2026-07-21 8:00 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, sgoutham
In-Reply-To: <20260714015331.1801922-1-rkannoth@marvell.com>
On 2026-07-14 at 07:23:22, Ratheesh Kannoth (rkannoth@marvell.com) wrote:
> Marvell OcteonTX2 switch hardware is capable of accelerating L2, L3, and
> flow. When representors are enabled through devlink, a logical port is
> created in switch hardware for each representor device.
>
Critical comments reported by sashiko (https://sashiko.dev/#/patchset/20260714015331.1801922-1-rkannoth%40marvell.com)
Will address in v4.
pw-bot: changes-requested
^ permalink raw reply
* Re: [PATCH 1/2] net/socket: Record preference for synchronous wakeups
From: Willem de Bruijn @ 2026-07-21 8:00 UTC (permalink / raw)
To: Srikar Dronamraju, LKML, netdev, David S Miller
Cc: Ingo Molnar, Peter Zijlstra, Dietmar Eggemann, Dust Li, D Wythe,
Eric Dumazet, Jakub Kicinski, Jon Maloy, Kuniyuki Iwashima,
linux-sctp, Mahanta Jambigi, Marcelo Ricardo Leitner, Paolo Abeni,
Sidraya Jayagond, Simon Horman, Tony Lu, Wen Gu, Wenjia Zhang,
Willem de Bruijn, Xin Long, Shrikanth Hegde, Vincent Guittot,
Steven Rostedt, Ben Segall, Mel Gorman, Valentin Schneider,
K Prateek Nayak, Srikar Dronamraju
In-Reply-To: <20260714013940.4068189-5-srikar@linux.ibm.com>
Srikar Dronamraju wrote:
> Scheduler differentiates between affine and non-affine wakeups by the
> way of sync flags. Scheduler prefers to pull the tasks towards the waker
> if the sync flag is set.
>
> In some cases, socket APIs are blindly requesting sync wakeups. This may
> cause load-balance issues and non-optimal performance.
>
> Record whether the most recent blocking socket operation could benefit
What is the heuristic that determines this?
If respinning, please state that explicitly in the commit message.
> from synchronous wakeups. Subsequent readiness notifications use this
> hint to determine whether WF_SYNC should be propagated.
>
> The flag is advisory and affects only wakeup placement decisions.
>
> Signed-off-by: Srikar Dronamraju <srikar@linux.ibm.com>
^ permalink raw reply
* Re: [PATCH net-next] net: sparx5: configure TAS port link speed
From: patchwork-bot+netdevbpf @ 2026-07-21 8:10 UTC (permalink / raw)
To: Robert Marko
Cc: daniel.machon, UNGLinuxDriver, andrew+netdev, davem, edumazet,
kuba, pabeni, Steen.Hegelund, horms, netdev, linux-arm-kernel,
linux-kernel, luka.perkov
In-Reply-To: <20260707170531.1129866-1-robert.marko@sartura.hr>
Hello:
This patch was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Tue, 7 Jul 2026 19:04:48 +0200 you wrote:
> On the TSN and RED variants of LAN969x and SparX-5i TAS (Time-Aware Shaper)
> is present in the silicon.
>
> Currently, the driver does not use configure it at all, which means that
> the TAS_PROFILE_CONFIG.LINK_SPEED[1] value is left at the default of 3
> which means that its configured for 1 Gbps.
>
> [...]
Here is the summary with links:
- [net-next] net: sparx5: configure TAS port link speed
https://git.kernel.org/netdev/net-next/c/80d8e1d428e8
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net v2] net: stmmac: resume PHY before hardware setup when opening the interface
From: Paolo Abeni @ 2026-07-21 8:12 UTC (permalink / raw)
To: Maxime Chevallier, Stefan Agner, David S. Miller, Eric Dumazet,
Jakub Kicinski, Andrew Lunn
Cc: Russell King (Oracle), Ovidiu Panait, Maxime Coquelin,
Alexandre Torgue, netdev, linux-stm32, linux-arm-kernel,
regressions
In-Reply-To: <c8cbf359-e224-44c6-8692-9e1d1527ae0c@bootlin.com>
Hi,
On 7/8/26 11:16 AM, Maxime Chevallier wrote:
> On 7/7/26 21:54, Stefan Agner wrote:
>> Since the referenced commit, changing the MTU on a running interface no
>> longer disconnects and reconnects the PHY; __stmmac_release() merely
>> stops phylink, which also suspends the PHY (BMCR power-down) when WoL
>> is not enabled. __stmmac_open() then performs the DMA software reset in
>> stmmac_hw_setup() before phylink_start() resumes the PHY again.
>>
>> IEEE 802.3 22.2.4.1.5 allows a PHY to stop its receive clock while
>> powered down, and stmmac requires a running receive clock for the DMA
>> software reset to complete (the phylink config sets mac_requires_rxc).
>> On such setups, e.g. the RK3566-based Home Assistant Green with an
>> RTL8211F-VD PHY in RGMII mode, any runtime MTU change now times out and
>> leaves the interface dead:
>>
>> rk_gmac-dwmac fe010000.ethernet end0: Failed to reset the dma
>> rk_gmac-dwmac fe010000.ethernet end0: stmmac_hw_setup: DMA engine initialization failed
>> rk_gmac-dwmac fe010000.ethernet end0: __stmmac_open: Hw setup failed
>> rk_gmac-dwmac fe010000.ethernet end0: failed reopening the interface after MTU change
>>
>> In the field this is triggered by NetworkManager applying an MTU while
>> activating the connection, breaking networking entirely.
>>
>> Resume the PHY in __stmmac_open() before the hardware setup, making it
>> the counterpart of the phylink_stop() in __stmmac_release(), like
>> stmmac_resume() already does for the same reason. phylink_start() also
>> resumes the PHY, but only after stmmac_hw_setup(), and it cannot be
>> moved before the hardware setup since it may bring the link up
>> immediately from a workqueue, racing with the initialization (see the
>> comment in stmmac_resume()). For the regular ndo_open path the PHY has
>> just been attached and is not suspended, in which case
>> phylink_prepare_resume() does nothing.
>>
>> Fixes: db299a0c09e9 ("net: stmmac: move PHY handling out of __stmmac_open()/release()")
>> Link: https://github.com/home-assistant/operating-system/issues/4858
>> Signed-off-by: Stefan Agner <stefan@agner.ch>
>
> I was able to reproduce the issue on imx8mp and socfpga. Adding this case to
> my periodic test list...
>
> Indeed the assymetry isn't very nice, OTOH there's not phylink counterpart
> for phylink_prepare_resume(). This helper was added for the suspend/resume
> case, and only for it as the doc states, it just happens to do exactly
> what we need to fix the issue :
>
> /**
> * phylink_prepare_resume() - prepare to resume a network device
> * @pl: a pointer to a &struct phylink returned from phylink_create()
> *
> * Optional, but if called must be called prior to phylink_resume().
> *
> * Prepare to resume a network device, preparing the PHY as necessary.
> */
> void phylink_prepare_resume(struct phylink *pl)
>
> I think this helper should be renamed and the doc updated, stmmac is the
> sole user, and it's really about controlling that rxc and not about
> suspend / resume. Resuming from suspend is just one of the cases where we
> need that RXC early on.
>
> So either something like 'phylink_prepare_start_or_resume' but it's long,
> or maybe we can be more explicit about it and simply call it:
>
> phylink_start_rxc(pl)
>
> (without a corresponding stop)
I read the above as a possible follow-up more than actual changes
requested to this patch, am I correct?
Thanks,
Paolo
^ permalink raw reply
* [PATCH net-next v2] net: dsa: realtek: rtl8366rb: Fix up port isolation
From: Linus Walleij @ 2026-07-21 8:18 UTC (permalink / raw)
To: Alvin Šipraga, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: netdev, Linus Walleij
Sashiko reports that we incorrectly disable isolation in the setup
loop while what we want to do is to enable it.
Enable it by setting all isolation bits in the isolation mask.
Fix up the comments so it is clear what is going on, including a
missing word in the helper function.
Reported-by: Paolo Abeni <pabeni@redhat.com>
Closes: https://sashiko.dev/#/patchset/20260630-rtl8366rb-improvements-v2-0-05eb9d6a37f5%40kernel.org
Signed-off-by: Linus Walleij <linusw@kernel.org>
---
Changes in v2:
- Properly fix the issue by setting all isolation bits to 1.
- Link to v1: https://patch.msgid.link/20260711-rtl8366rb-fixes-v1-1-12b9210b0cce@kernel.org
---
drivers/net/dsa/realtek/rtl8366rb.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/net/dsa/realtek/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c
index d2fa8ff6a5d0..6d5a6e7bc5f1 100644
--- a/drivers/net/dsa/realtek/rtl8366rb.c
+++ b/drivers/net/dsa/realtek/rtl8366rb.c
@@ -794,8 +794,8 @@ static int rtl8366rb_setup_all_leds_off(struct realtek_priv *priv)
static int rtl8366rb_port_set_isolation(struct realtek_priv *priv, int port,
u32 mask)
{
- /* Bit 0 enables isolation so set this if we enable isolation
- * any of the ports an clear it if we disable on all of them.
+ /* Bit 0 enables isolation so set this if we enable isolation on
+ * any of the ports and clear it if we disable on all of them.
*/
if (mask)
mask = RTL8366RB_PORT_ISO_PORTS(mask) | RTL8366RB_PORT_ISO_EN;
@@ -951,7 +951,8 @@ static int rtl8366rb_setup(struct dsa_switch *ds)
rtl8366rb_port_stp_state_set(ds, dp->index, BR_STATE_DISABLED);
/* Start with all ports completely isolated */
- ret = rtl8366rb_port_set_isolation(priv, dp->index, 0);
+ ret = rtl8366rb_port_set_isolation(priv, dp->index,
+ RTL8366RB_PORT_ISO_PORTS_MASK);
if (ret)
return ret;
@@ -974,7 +975,7 @@ static int rtl8366rb_setup(struct dsa_switch *ds)
if (!dsa_port_is_user(dp))
continue;
- /* Forward only to the CPU */
+ /* Forward only to the CPU, isolate from all other ports */
ret = rtl8366rb_port_set_isolation(priv, dp->index, upports_mask);
if (ret)
return ret;
---
base-commit: 23dad2d088dfc82cae1f5a936f8ff7ffebb38dd9
change-id: 20260702-rtl8366rb-fixes-a93f831ec2a4
Best regards,
--
Linus Walleij <linusw@kernel.org>
^ permalink raw reply related
* [PATCH v4 net-next 0/9] Switch support
From: Ratheesh Kannoth @ 2026-07-21 8:18 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, sgoutham,
Ratheesh Kannoth
Marvell OcteonTX2 switch hardware is capable of accelerating L2, L3, and
flow. When representors are enabled through devlink, a logical port is
created in switch hardware for each representor device.
This patch series implements communication from the host OS to the switch
hardware and vice versa.
control plane (FDB / FIB / flow over mailbox)
+---------------------------------------------------------------------+
| HOST OS |
| |
| +------------+ +---------------------+ +---------------+ |
| | bridge / | | PF + notifiers | | TC / flower | |
| | routing | | (FDB, FIB, flow) | | offload | |
| +------+-----+ +-----------+---------+ +-------+-------+ |
| ^ | | |
| | slow path | | |
| | (unmatched pkts) v v |
| +------+------+ +------+------+ +------+------+ |
| | rep-eth0 | | PF / VF | <----> | RVU AF | |
| +------+------+ +-------------+ +------+------+ |
| | | |
| +------+------+ | |
| | rep-eth1 | | |
| +-------------+ | |
| | slow path (miss / control to host stack) | |
+---------------------------------------------------------------------+
| | | |
| | mailbox / PCIe | |
| v v |
+---------------------------------------------------------------------+
| SWITCH HARDWARE |
| |
| +---------------------------------------------------+ |
| | L2 tables | L3 tables | flow TCAM (HW) | |
| +-------+-------------+---------------+-------------+ |
| | fast path (HW) | |
| v v |
| lport0 <========================> lport1 |
| HW forwarding between ports |
| |
| slow path (miss): packets raised to host via representors |
+---------------------------------------------------------------------+
When representors are created, corresponding logical ports are created in
switchdev. Matching traffic is accelerated in switch hardware using
installed L2, L3, and flow rules. Packets that do not match offloaded
state, or that require software handling, take the slow path through
representors and the host networking stack. Control updates from the host
(FDB, FIB, TC flower) are sent to the AF over mailbox and programmed into
switch hardware tables.
Notifier callbacks are registered to receive system events such as FDB
add/delete and FIB add/delete. Flow add/delete operations are handled
through the ingress flow-table offload path. These events are captured and
processed by the NIC driver and forwarded to the switch device through the
AF driver. All message exchanges use the mailbox interface.
Bridge acceleration:
FDB add/delete notifications are processed, and learned SMAC information is
sent to the switch hardware. The switch inserts a hardware rule to
accelerate packets destined to the MAC address. Switch-initiated FDB
refresh is propagated back to the host bridge through an AF to PF/VF
mailbox and switchdev notifier.
L3 acceleration:
IPv4 and IPv6 FIB updates observed through netdev and FIB notifiers are
queued on the PF and sent to the AF. The AF batches fib_entry structures
and forwards them to switchdev when firmware is ready.
Flow acceleration:
TC flower rules accepted by the ingress flow-table offload callback are
translated into fl_tuple state, ingress and egress pcifunc are resolved
through FIB lookup, and flow updates are sent to the switch hardware
through the mailbox interface. Per-cookie packet counters are kept in sync
using NPC MCAM multi-stats when the switch requests a flow refresh.
Ratheesh Kannoth (9):
octeontx2-af: switch: Add AF to switch mbox and skeleton files
Host to switch mailbox definitions for FDB, FIB, flow, and flow stats;
AF-side L2/L3/flow skeleton objects.
octeontx2-af: switch: Add switch dev to AF mboxes
Switch to AF and AF to switchdev mailbox messages; interface query
handler and NPC helpers for flow delete/stats/features.
octeontx2-pf: switch: Add pf files hierarchy
CONFIG_OCTEONTX_SWITCH and stub PF switch objects for FDB, FIB, flow, and
notifier plumbing.
octeontx2-af: switch: Representor for switch port
Copy devlink switch_id to the AF and map representor pcifunc to a switch
port id when eswitch mode is enabled.
octeontx2-af: PAN switch TL1 scheduling and NPC channel control
Allocate multiple TL1 scheduler queues in PAN mode and honor caller
supplied NPC channel masks for multi-link steering.
octeontx2-pf: register switch notifiers for eswitch offload
Register PF notifier blocks for FIB, neighbour, address, netdev, and
switchdev FDB events; split IPv4/IPv6 handling.
octeontx2: plumb bridge FDB updates through AF and switchdev
End-to-end L2 offload from switchdev FDB notifications to AF to
switchdev, including firmware gating and FDB refresh to host.
octeontx2: offload host FIB updates to switch via AF mailbox
Queue IPv4/IPv6 FIB updates from notifiers and batch fib_entry delivery
from AF to switchdev.
octeontx2: add TC flow offload path for switch flows
Ingress flow-table offload for TC flower rules, AF forwarding to
switchdev, and flow counter refresh from switch to host.
.../net/ethernet/marvell/octeontx2/Kconfig | 13 +
.../ethernet/marvell/octeontx2/af/Makefile | 3 +-
.../net/ethernet/marvell/octeontx2/af/mbox.h | 227 ++++++++
.../net/ethernet/marvell/octeontx2/af/rvu.c | 110 ++++
.../net/ethernet/marvell/octeontx2/af/rvu.h | 6 +
.../ethernet/marvell/octeontx2/af/rvu_nix.c | 53 +-
.../ethernet/marvell/octeontx2/af/rvu_npc.c | 76 +++
.../marvell/octeontx2/af/rvu_npc_fs.c | 11 +
.../ethernet/marvell/octeontx2/af/rvu_rep.c | 3 +-
.../marvell/octeontx2/af/switch/rvu_sw.c | 48 ++
.../marvell/octeontx2/af/switch/rvu_sw.h | 14 +
.../marvell/octeontx2/af/switch/rvu_sw_fl.c | 294 ++++++++++
.../marvell/octeontx2/af/switch/rvu_sw_fl.h | 12 +
.../marvell/octeontx2/af/switch/rvu_sw_l2.c | 283 +++++++++
.../marvell/octeontx2/af/switch/rvu_sw_l2.h | 13 +
.../marvell/octeontx2/af/switch/rvu_sw_l3.c | 215 +++++++
.../marvell/octeontx2/af/switch/rvu_sw_l3.h | 11 +
.../ethernet/marvell/octeontx2/nic/Makefile | 13 +-
.../marvell/octeontx2/nic/otx2_txrx.h | 2 +
.../ethernet/marvell/octeontx2/nic/otx2_vf.c | 17 +
.../net/ethernet/marvell/octeontx2/nic/rep.c | 11 +
.../marvell/octeontx2/nic/switch/sw_fdb.c | 144 +++++
.../marvell/octeontx2/nic/switch/sw_fdb.h | 14 +
.../marvell/octeontx2/nic/switch/sw_fib.c | 132 +++++
.../marvell/octeontx2/nic/switch/sw_fib.h | 16 +
.../marvell/octeontx2/nic/switch/sw_fl.c | 546 ++++++++++++++++++
.../marvell/octeontx2/nic/switch/sw_fl.h | 15 +
.../marvell/octeontx2/nic/switch/sw_nb.c | 422 ++++++++++++++
.../marvell/octeontx2/nic/switch/sw_nb.h | 35 ++
.../marvell/octeontx2/nic/switch/sw_nb_v4.c | 335 +++++++++++
.../marvell/octeontx2/nic/switch/sw_nb_v4.h | 21 +
.../marvell/octeontx2/nic/switch/sw_nb_v6.c | 236 ++++++++
.../marvell/octeontx2/nic/switch/sw_nb_v6.h | 21 +
33 files changed, 3361 insertions(+), 11 deletions(-)
---
v3 -> v4: Addressed Sashiko comments
https://lore.kernel.org/netdev/20260714015331.1801922-1-rkannoth@marvell.com/
v2 -> v3: Addressed Pabolo comments.
https://lore.kernel.org/netdev/20260702045026.2914748-1-rkannoth@marvell.com/
v1 -> v2: Addressed Jakub comments
https://lore.kernel.org/netdev/20260630024715.4124281-1-rkannoth@marvell.com/
--
2.43.0
^ permalink raw reply
* [PATCH v4 net-next 1/9] octeontx2-af: switch: Add AF to switch mbox and skeleton files
From: Ratheesh Kannoth @ 2026-07-21 8:18 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, sgoutham,
Ratheesh Kannoth
In-Reply-To: <20260721081824.1430607-1-rkannoth@marvell.com>
The Marvell switch hardware runs on a Linux OS. This OS receives
various messages, which are parsed to create flow rules that can be
installed on HW. The switch is capable of accelerating both L2 and
L3 flows.
This commit adds mailbox messages used by the Linux OS (on arm64) to
send events to the switch hardware, along with skeleton handler
functions:
fdb messages: Linux bridge FDB messages
fib messages: Linux routing table messages
fl messages: Flow acceleration tuple and actions
status messages: Packet status updates sent to Host Linux to keep
connection-tracked flows active
fl_tuple defines the flow acceleration match tuple exchanged over the
mailbox. It currently carries IPv4 five-tuple and L2 match fields only.
IPv6 flow acceleration is not supported in this patch and will be added
in a follow-up change extending the mailbox ABI.
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
.../ethernet/marvell/octeontx2/af/Makefile | 3 +-
.../net/ethernet/marvell/octeontx2/af/mbox.h | 107 ++++++++++++++++++
.../marvell/octeontx2/af/switch/rvu_sw_fl.c | 21 ++++
.../marvell/octeontx2/af/switch/rvu_sw_fl.h | 11 ++
.../marvell/octeontx2/af/switch/rvu_sw_l2.c | 14 +++
.../marvell/octeontx2/af/switch/rvu_sw_l2.h | 11 ++
.../marvell/octeontx2/af/switch/rvu_sw_l3.c | 14 +++
.../marvell/octeontx2/af/switch/rvu_sw_l3.h | 11 ++
8 files changed, 191 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c
create mode 100644 drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h
create mode 100644 drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c
create mode 100644 drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h
create mode 100644 drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c
create mode 100644 drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/Makefile b/drivers/net/ethernet/marvell/octeontx2/af/Makefile
index 91b7d6e96a61..82dd387308c9 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/Makefile
+++ b/drivers/net/ethernet/marvell/octeontx2/af/Makefile
@@ -3,7 +3,7 @@
# Makefile for Marvell's RVU Admin Function driver
#
-ccflags-y += -I$(src)
+ccflags-y += -I$(src) -I$(src)/switch/
obj-$(CONFIG_OCTEONTX2_MBOX) += rvu_mbox.o
obj-$(CONFIG_OCTEONTX2_AF) += rvu_af.o
@@ -12,5 +12,6 @@ rvu_af-y := cgx.o rvu.o rvu_cgx.o rvu_npa.o rvu_nix.o \
rvu_reg.o rvu_npc.o rvu_debugfs.o ptp.o rvu_npc_fs.o \
rvu_cpt.o rvu_devlink.o rpm.o rvu_cn10k.o rvu_switch.o \
rvu_sdp.o rvu_npc_hash.o mcs.o mcs_rvu_if.o mcs_cnf10kb.o \
+ switch/rvu_sw_l2.o switch/rvu_sw_l3.o switch/rvu_sw_fl.o\
rvu_rep.o cn20k/mbox_init.o cn20k/nix.o cn20k/debugfs.o \
cn20k/npa.o cn20k/npc.o
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
index f87cdf1b971d..2867da47d9f5 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
@@ -164,6 +164,14 @@ M(PTP_GET_CAP, 0x00c, ptp_get_cap, msg_req, ptp_get_cap_rsp) \
M(GET_REP_CNT, 0x00d, get_rep_cnt, msg_req, get_rep_cnt_rsp) \
M(ESW_CFG, 0x00e, esw_cfg, esw_cfg_req, msg_rsp) \
M(REP_EVENT_NOTIFY, 0x00f, rep_event_notify, rep_event, msg_rsp) \
+M(FDB_NOTIFY, 0x010, fdb_notify, \
+ fdb_notify_req, msg_rsp) \
+M(FIB_NOTIFY, 0x011, fib_notify, \
+ fib_notify_req, msg_rsp) \
+M(FL_NOTIFY, 0x012, fl_notify, \
+ fl_notify_req, msg_rsp) \
+M(FL_GET_STATS, 0x013, fl_get_stats, \
+ fl_get_stats_req, fl_get_stats_rsp) \
/* CGX mbox IDs (range 0x200 - 0x3FF) */ \
M(CGX_START_RXTX, 0x200, cgx_start_rxtx, msg_req, msg_rsp) \
M(CGX_STOP_RXTX, 0x201, cgx_stop_rxtx, msg_req, msg_rsp) \
@@ -1807,6 +1815,105 @@ struct rep_event {
struct rep_evt_data evt_data;
};
+#define FDB_ADD BIT_ULL(0)
+#define FDB_DEL BIT_ULL(1)
+#define FIB_CMD BIT_ULL(2)
+#define FL_ADD BIT_ULL(3)
+#define FL_DEL BIT_ULL(4)
+#define DP_ADD BIT_ULL(5)
+
+struct fdb_notify_req {
+ struct mbox_msghdr hdr;
+ u64 flags;
+ u8 mac[ETH_ALEN];
+};
+
+struct fib_entry {
+ u64 cmd;
+ u64 gw_valid : 1;
+ u64 mac_valid : 1;
+ u64 vlan_valid: 1;
+ u64 host : 1;
+ u64 bridge : 1;
+ u64 ipv6 : 1;
+ __be16 vlan_tag;
+ u32 dst_len;
+ u8 dst6_plen;
+ u8 gw6_plen;
+ union {
+ __be32 dst;
+ __be32 dst6[4];
+ };
+ union {
+ __be32 gw;
+ __be32 gw6[4];
+ };
+ u16 port_id;
+ u8 nud_state;
+ u8 mac[ETH_ALEN];
+};
+
+struct fib_notify_req {
+ struct mbox_msghdr hdr;
+ u16 cnt;
+ u16 rsvd[3]; /* explicit padding for entry[] 8-byte alignment */
+ struct fib_entry entry[16];
+};
+
+struct fl_tuple {
+ __be32 ip4src;
+ __be32 m_ip4src;
+ __be32 ip4dst;
+ __be32 m_ip4dst;
+ __be16 sport;
+ __be16 m_sport;
+ __be16 dport;
+ __be16 m_dport;
+ __be16 eth_type;
+ __be16 m_eth_type;
+ u8 proto;
+ u8 smac[6];
+ u8 m_smac[6];
+ u8 dmac[6];
+ u8 m_dmac[6];
+ u64 is_xdev_br : 1;
+ u64 is_indev_br : 1;
+ u64 uni_di : 1;
+ u16 in_pf;
+ u16 xmit_pf;
+ u16 rsvd;
+ u64 features;
+ struct { /* FLOW_ACTION_MANGLE */
+ u8 offset;
+ u8 type;
+ u16 rsvd;
+ u32 mask;
+ u32 val;
+#define MANGLE_ARR_SZ 9
+ } mangle[MANGLE_ARR_SZ]; /* 2 for ETH, 1 for VLAN, 4 for IPv6, 2 for L4. */
+#define MANGLE_LAYER_CNT 4
+ u8 mangle_map[MANGLE_LAYER_CNT]; /* 1 for ETH, 1 for VLAN, 1 for L3, 1 for L4 */
+ u8 mangle_cnt;
+};
+
+struct fl_notify_req {
+ struct mbox_msghdr hdr;
+ u64 cookie;
+ u64 flags;
+ u64 features;
+ struct fl_tuple tuple;
+};
+
+struct fl_get_stats_req {
+ struct mbox_msghdr hdr;
+ u64 cookie;
+};
+
+struct fl_get_stats_rsp {
+ struct mbox_msghdr hdr;
+ u64 pkts_diff;
+};
+
struct flow_msg {
unsigned char dmac[6];
unsigned char smac[6];
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c
new file mode 100644
index 000000000000..1f8b82a84a5d
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#include "rvu.h"
+
+int rvu_mbox_handler_fl_get_stats(struct rvu *rvu,
+ struct fl_get_stats_req *req,
+ struct fl_get_stats_rsp *rsp)
+{
+ return 0;
+}
+
+int rvu_mbox_handler_fl_notify(struct rvu *rvu,
+ struct fl_notify_req *req,
+ struct msg_rsp *rsp)
+{
+ return 0;
+}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h
new file mode 100644
index 000000000000..cf3e5b884f77
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_fl.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+
+#ifndef RVU_SW_FL_H
+#define RVU_SW_FL_H
+
+#endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c
new file mode 100644
index 000000000000..5f805bfa81ed
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#include "rvu.h"
+
+int rvu_mbox_handler_fdb_notify(struct rvu *rvu,
+ struct fdb_notify_req *req,
+ struct msg_rsp *rsp)
+{
+ return 0;
+}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h
new file mode 100644
index 000000000000..ff28612150c9
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l2.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+
+#ifndef RVU_SW_L2_H
+#define RVU_SW_L2_H
+
+#endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c
new file mode 100644
index 000000000000..2b798d5f0644
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#include "rvu.h"
+
+int rvu_mbox_handler_fib_notify(struct rvu *rvu,
+ struct fib_notify_req *req,
+ struct msg_rsp *rsp)
+{
+ return 0;
+}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h
new file mode 100644
index 000000000000..ac8c4f9ba5ac
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw_l3.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+
+#ifndef RVU_SW_L3_H
+#define RVU_SW_L3_H
+
+#endif
--
2.43.0
^ permalink raw reply related
* [PATCH v4 net-next 3/9] octeontx2-pf: switch: Add pf files hierarchy
From: Ratheesh Kannoth @ 2026-07-21 8:18 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, sgoutham,
Ratheesh Kannoth
In-Reply-To: <20260721081824.1430607-1-rkannoth@marvell.com>
Adds CONFIG_OCTEONTX_SWITCH, links stub switch objects into the PF
module, and introduces empty sw_* init/deinit and notifier hooks for
later patches.
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
.../net/ethernet/marvell/octeontx2/Kconfig | 10 +++++++++
.../ethernet/marvell/octeontx2/nic/Makefile | 5 ++++-
.../marvell/octeontx2/nic/switch/sw_fdb.c | 16 ++++++++++++++
.../marvell/octeontx2/nic/switch/sw_fdb.h | 13 ++++++++++++
.../marvell/octeontx2/nic/switch/sw_fib.c | 20 ++++++++++++++++++
.../marvell/octeontx2/nic/switch/sw_fib.h | 20 ++++++++++++++++++
.../marvell/octeontx2/nic/switch/sw_fl.c | 16 ++++++++++++++
.../marvell/octeontx2/nic/switch/sw_fl.h | 13 ++++++++++++
.../marvell/octeontx2/nic/switch/sw_nb.c | 21 +++++++++++++++++++
.../marvell/octeontx2/nic/switch/sw_nb.h | 20 ++++++++++++++++++
10 files changed, 153 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c
create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h
create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c
create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h
create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c
create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h
create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c
create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h
diff --git a/drivers/net/ethernet/marvell/octeontx2/Kconfig b/drivers/net/ethernet/marvell/octeontx2/Kconfig
index 47e549c581f0..e2fb6dd71078 100644
--- a/drivers/net/ethernet/marvell/octeontx2/Kconfig
+++ b/drivers/net/ethernet/marvell/octeontx2/Kconfig
@@ -28,6 +28,16 @@ config NDC_DIS_DYNAMIC_CACHING
, NPA stack pages etc in NDC. Also locks down NIX SQ/CQ/RQ/RSS and
NPA Aura/Pool contexts.
+config OCTEONTX_SWITCH
+ bool "Marvell OcteonTX2 switch driver"
+ depends on (64BIT && COMPILE_TEST) || ARM64
+ depends on OCTEONTX2_PF
+ default n
+ help
+ This driver supports Marvell's OcteonTX2 switch.
+ Marvell SWITCH HW can offload L2, L3 flow. ARM core interacts
+ with Marvell SW HW thru mbox.
+
config OCTEONTX2_PF
tristate "Marvell OcteonTX2 NIC Physical Function driver"
select OCTEONTX2_MBOX
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile
index 883e9f4d601c..123b0af23abd 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile
@@ -9,7 +9,10 @@ obj-$(CONFIG_RVU_ESWITCH) += rvu_rep.o
rvu_nicpf-y := otx2_pf.o otx2_common.o otx2_txrx.o otx2_ethtool.o \
otx2_flows.o otx2_tc.o cn10k.o cn20k.o otx2_dmac_flt.o \
- otx2_devlink.o qos_sq.o qos.o otx2_xsk.o
+ otx2_devlink.o qos_sq.o qos.o otx2_xsk.o \
+ switch/sw_fdb.o switch/sw_fl.o
+rvu_nicpf-$(CONFIG_OCTEONTX_SWITCH) += switch/sw_nb.o switch/sw_fib.o
+
rvu_nicvf-y := otx2_vf.o
rvu_rep-y := rep.o
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c
new file mode 100644
index 000000000000..6842c8d91ffc
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.c
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#include "sw_fdb.h"
+
+int sw_fdb_init(void)
+{
+ return 0;
+}
+
+void sw_fdb_deinit(void)
+{
+}
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h
new file mode 100644
index 000000000000..d4314d6d3ee4
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fdb.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#ifndef SW_FDB_H_
+#define SW_FDB_H_
+
+void sw_fdb_deinit(void);
+int sw_fdb_init(void);
+
+#endif // SW_FDB_H
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c
new file mode 100644
index 000000000000..41a9c5fb58fa
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#include "sw_fib.h"
+
+#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
+
+int sw_fib_init(void)
+{
+ return 0;
+}
+
+void sw_fib_deinit(void)
+{
+}
+
+#endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h
new file mode 100644
index 000000000000..9b72e95f2dd3
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fib.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#ifndef SW_FIB_H_
+#define SW_FIB_H_
+
+#include <linux/kconfig.h>
+
+#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
+void sw_fib_deinit(void);
+int sw_fib_init(void);
+#else
+static inline void sw_fib_deinit(void) {}
+static inline int sw_fib_init(void) { return 0; }
+#endif
+
+#endif /* SW_FIB_H_ */
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c
new file mode 100644
index 000000000000..36a2359a0a48
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.c
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#include "sw_fl.h"
+
+int sw_fl_init(void)
+{
+ return 0;
+}
+
+void sw_fl_deinit(void)
+{
+}
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h
new file mode 100644
index 000000000000..cd018d770a8a
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_fl.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#ifndef SW_FL_H_
+#define SW_FL_H_
+
+void sw_fl_deinit(void);
+int sw_fl_init(void);
+
+#endif // SW_FL_H
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c
new file mode 100644
index 000000000000..243611835e3a
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#include "sw_nb.h"
+
+#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
+
+int sw_nb_unregister(void)
+{
+ return 0;
+}
+
+int sw_nb_register(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h
new file mode 100644
index 000000000000..73cc1e99b8ec
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#ifndef SW_NB_H_
+#define SW_NB_H_
+
+#include <linux/kconfig.h>
+
+#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
+int sw_nb_register(void);
+int sw_nb_unregister(void);
+#else
+static inline int sw_nb_register(void) { return 0; }
+static inline int sw_nb_unregister(void) { return 0; }
+#endif
+
+#endif /* SW_NB_H_ */
--
2.43.0
^ permalink raw reply related
* [PATCH v4 net-next 2/9] octeontx2-af: switch: Add switch dev to AF mboxes
From: Ratheesh Kannoth @ 2026-07-21 8:18 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, sgoutham,
Ratheesh Kannoth
In-Reply-To: <20260721081824.1430607-1-rkannoth@marvell.com>
The Marvell switch hardware runs on a Linux OS. Switch
needs various information from AF driver. These mboxes are defined
to query those from AF driver.
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
.../ethernet/marvell/octeontx2/af/Makefile | 2 +-
.../net/ethernet/marvell/octeontx2/af/mbox.h | 126 ++++++++++++++++++
.../net/ethernet/marvell/octeontx2/af/rvu.c | 126 ++++++++++++++++++
.../net/ethernet/marvell/octeontx2/af/rvu.h | 1 +
.../ethernet/marvell/octeontx2/af/rvu_nix.c | 9 +-
.../ethernet/marvell/octeontx2/af/rvu_npc.c | 111 +++++++++++++++
.../marvell/octeontx2/af/rvu_npc_fs.c | 11 ++
.../marvell/octeontx2/af/switch/rvu_sw.c | 15 +++
.../marvell/octeontx2/af/switch/rvu_sw.h | 11 ++
9 files changed, 408 insertions(+), 4 deletions(-)
create mode 100644 drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c
create mode 100644 drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/Makefile b/drivers/net/ethernet/marvell/octeontx2/af/Makefile
index 82dd387308c9..73f20a44f1a0 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/Makefile
+++ b/drivers/net/ethernet/marvell/octeontx2/af/Makefile
@@ -12,6 +12,6 @@ rvu_af-y := cgx.o rvu.o rvu_cgx.o rvu_npa.o rvu_nix.o \
rvu_reg.o rvu_npc.o rvu_debugfs.o ptp.o rvu_npc_fs.o \
rvu_cpt.o rvu_devlink.o rpm.o rvu_cn10k.o rvu_switch.o \
rvu_sdp.o rvu_npc_hash.o mcs.o mcs_rvu_if.o mcs_cnf10kb.o \
- switch/rvu_sw_l2.o switch/rvu_sw_l3.o switch/rvu_sw_fl.o\
+ switch/rvu_sw.o switch/rvu_sw_l2.o switch/rvu_sw_l3.o switch/rvu_sw_fl.o \
rvu_rep.o cn20k/mbox_init.o cn20k/nix.o cn20k/debugfs.o \
cn20k/npa.o cn20k/npc.o
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
index 2867da47d9f5..23bc66ed854e 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
@@ -172,6 +172,10 @@ M(FL_NOTIFY, 0x012, fl_notify, \
fl_notify_req, msg_rsp) \
M(FL_GET_STATS, 0x013, fl_get_stats, \
fl_get_stats_req, fl_get_stats_rsp) \
+M(IFACE_GET_INFO, 0x014, iface_get_info, msg_req, \
+ iface_get_info_rsp) \
+M(SWDEV2AF_NOTIFY, 0x015, swdev2af_notify, \
+ swdev2af_notify_req, msg_rsp) \
/* CGX mbox IDs (range 0x200 - 0x3FF) */ \
M(CGX_START_RXTX, 0x200, cgx_start_rxtx, msg_req, msg_rsp) \
M(CGX_STOP_RXTX, 0x201, cgx_stop_rxtx, msg_req, msg_rsp) \
@@ -317,6 +321,14 @@ M(NPC_MCAM_GET_DFT_RL_IDXS, 0x601e, npc_get_dft_rl_idxs, \
M(NPC_MCAM_GET_NPC_PFL_INFO, 0x601f, npc_get_pfl_info, \
msg_req, \
npc_get_pfl_info_rsp) \
+M(NPC_MCAM_FLOW_DEL_N_FREE, 0x6020, npc_flow_del_n_free, \
+ npc_flow_del_n_free_req, msg_rsp) \
+M(NPC_MCAM_GET_MUL_STATS, 0x6021, npc_mcam_mul_stats, \
+ npc_mcam_get_mul_stats_req, \
+ npc_mcam_get_mul_stats_rsp) \
+M(NPC_MCAM_GET_FEATURES, 0x6022, npc_mcam_get_features, \
+ msg_req, \
+ npc_mcam_get_features_rsp) \
/* NIX mbox IDs (range 0x8000 - 0xFFFF) */ \
M(NIX_LF_ALLOC, 0x8000, nix_lf_alloc, \
nix_lf_alloc_req, nix_lf_alloc_rsp) \
@@ -446,6 +458,12 @@ M(MCS_INTR_NOTIFY, 0xE00, mcs_intr_notify, mcs_intr_info, msg_rsp)
#define MBOX_UP_REP_MESSAGES \
M(REP_EVENT_UP_NOTIFY, 0xEF0, rep_event_up_notify, rep_event, msg_rsp) \
+#define MBOX_UP_AF2SWDEV_MESSAGES \
+M(AF2SWDEV, 0xEF1, af2swdev_notify, af2swdev_notify_req, msg_rsp)
+
+#define MBOX_UP_AF2PF_FDB_REFRESH_MESSAGES \
+M(AF2PF_FDB_REFRESH, 0xEF2, af2pf_fdb_refresh, af2pf_fdb_refresh_req, msg_rsp)
+
enum {
#define M(_name, _id, _1, _2, _3) MBOX_MSG_ ## _name = _id,
MBOX_MESSAGES
@@ -453,6 +471,8 @@ MBOX_UP_CGX_MESSAGES
MBOX_UP_CPT_MESSAGES
MBOX_UP_MCS_MESSAGES
MBOX_UP_REP_MESSAGES
+MBOX_UP_AF2SWDEV_MESSAGES
+MBOX_UP_AF2PF_FDB_REFRESH_MESSAGES
#undef M
};
@@ -1589,6 +1609,30 @@ struct npc_mcam_alloc_entry_rsp {
u16 entry_list[NPC_MAX_NONCONTIG_ENTRIES];
};
+struct npc_flow_del_n_free_req {
+ struct mbox_msghdr hdr;
+ u16 cnt;
+ u16 entry[256]; /* Entry index to be freed */
+};
+
+struct npc_mcam_get_features_rsp {
+ struct mbox_msghdr hdr;
+ u64 rx_features;
+ u64 tx_features;
+};
+
+struct npc_mcam_get_mul_stats_req {
+ struct mbox_msghdr hdr;
+ u16 cnt;
+ u16 entry[256]; /* mcam entry */
+};
+
+struct npc_mcam_get_mul_stats_rsp {
+ struct mbox_msghdr hdr;
+ u16 cnt;
+ u64 stat[256]; /* counter stats */
+};
+
struct npc_mcam_free_entry_req {
struct mbox_msghdr hdr;
u16 entry; /* Entry index to be freed */
@@ -1914,6 +1958,88 @@ struct fl_get_stats_rsp {
u64 pkts_diff;
};
+struct af2swdev_notify_req {
+ struct mbox_msghdr hdr;
+ u64 flags;
+ u32 port_id;
+ u32 switch_id;
+ union {
+ struct {
+ u8 mac[6];
+ };
+ struct {
+ u8 cnt;
+ struct fib_entry entry[16];
+ };
+
+ struct {
+ u64 cookie;
+ u64 features;
+ struct fl_tuple tuple;
+ };
+ };
+};
+
+struct af2pf_fdb_refresh_req {
+ struct mbox_msghdr hdr;
+ u16 pcifunc;
+ u8 mac[6];
+};
+
+struct iface_info {
+ u8 is_vf : 1;
+ u8 is_sdp : 1;
+ u8 rsvd : 6;
+ u16 pcifunc;
+ u16 rx_chan_base;
+ u16 tx_chan_base;
+ u16 sq_cnt;
+ u16 cq_cnt;
+ u16 rq_cnt;
+ u8 rx_chan_cnt;
+ u8 tx_chan_cnt;
+ u8 tx_link;
+ u8 nix;
+};
+
+/* Max supported */
+#define IFACE_MAX (256 + 32) /* 32 PFs + 256 VFs */
+
+struct iface_get_info_rsp {
+ struct mbox_msghdr hdr;
+ u16 cnt;
+ u8 truncated;
+ u8 rsvd[5];
+ struct iface_info info[IFACE_MAX];
+};
+
+struct fl_info {
+ u64 cookie;
+ u16 mcam_idx[2];
+ u8 dis : 1;
+ u8 uni_di : 1;
+};
+
+struct swdev2af_notify_req {
+ struct mbox_msghdr hdr;
+ u64 msg_type;
+#define SWDEV2AF_MSG_TYPE_FW_STATUS BIT_ULL(0)
+#define SWDEV2AF_MSG_TYPE_REFRESH_FDB BIT_ULL(1)
+#define SWDEV2AF_MSG_TYPE_REFRESH_FL BIT_ULL(2)
+ u16 pcifunc;
+ union {
+ bool fw_up; // FW_STATUS message
+
+ u8 mac[ETH_ALEN]; // fdb refresh message
+
+ struct { // fl refresh message
+ u8 cnt;
+ u8 rsvd[7];
+ struct fl_info fl[64];
+ };
+ };
+};
+
struct flow_msg {
unsigned char dmac[6];
unsigned char smac[6];
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
index ffba56ee8a60..a0ae0ccc1b2b 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
@@ -1990,6 +1990,132 @@ int rvu_mbox_handler_msix_offset(struct rvu *rvu, struct msg_req *req,
return 0;
}
+static void rvu_iface_get_qcnts(struct rvu *rvu, struct rvu_pfvf *pfvf,
+ struct iface_info *info)
+{
+ mutex_lock(&rvu->rsrc_lock);
+
+ info->sq_cnt = 0;
+ info->cq_cnt = 0;
+ info->rq_cnt = 0;
+
+ /* Use each LF queue context size; bitmaps are sized to qsize longs. */
+ if (pfvf->sq_ctx && pfvf->sq_bmap)
+ info->sq_cnt = bitmap_weight(pfvf->sq_bmap, pfvf->sq_ctx->qsize);
+ if (pfvf->cq_ctx && pfvf->cq_bmap)
+ info->cq_cnt = bitmap_weight(pfvf->cq_bmap, pfvf->cq_ctx->qsize);
+ if (pfvf->rq_ctx && pfvf->rq_bmap)
+ info->rq_cnt = bitmap_weight(pfvf->rq_bmap, pfvf->rq_ctx->qsize);
+
+ mutex_unlock(&rvu->rsrc_lock);
+}
+
+int rvu_mbox_handler_iface_get_info(struct rvu *rvu, struct msg_req *req,
+ struct iface_get_info_rsp *rsp)
+{
+ struct iface_info *info;
+ bool truncated = false;
+ struct rvu_pfvf *pfvf;
+ int pf, vf, numvfs;
+ int tot = 0;
+ u16 pcifunc;
+ u64 cfg;
+
+ /* Read-only topology snapshot for switch software; any PF/VF may
+ * request it. Only channel and queue counts already visible to the
+ * requester through AF are reported.
+ */
+ rsp->cnt = 0;
+ rsp->truncated = 0;
+ /* Preserve mbox_msghdr fields pre-filled by the mbox framework. */
+ memset(rsp->info, 0, sizeof(rsp->info));
+ info = rsp->info;
+ for (pf = 0; pf < rvu->hw->total_pfs; pf++) {
+ if (tot >= IFACE_MAX) {
+ truncated = true;
+ goto done;
+ }
+
+ cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_PFX_CFG(pf));
+ numvfs = (cfg >> 12) & 0xFF;
+
+ /* Skip not enabled PFs */
+ if (!(cfg & BIT_ULL(20)))
+ goto chk_vfs;
+
+ /* If Admin function, check on VFs */
+ if (cfg & BIT_ULL(21))
+ goto chk_vfs;
+
+ pcifunc = rvu_make_pcifunc(rvu->pdev, pf, 0);
+ pfvf = rvu_get_pfvf(rvu, pcifunc);
+
+ /* Populate iff at least one Tx channel */
+ if (!pfvf->tx_chan_cnt)
+ goto chk_vfs;
+
+ info->is_vf = 0;
+ info->pcifunc = pcifunc;
+ info->rx_chan_base = pfvf->rx_chan_base;
+ info->rx_chan_cnt = pfvf->rx_chan_cnt;
+ info->tx_chan_base = pfvf->tx_chan_base;
+ info->tx_chan_cnt = pfvf->tx_chan_cnt;
+ info->tx_link = nix_get_tx_link(rvu, pcifunc);
+ if (is_sdp_pfvf(rvu, pcifunc))
+ info->is_sdp = 1;
+
+ rvu_iface_get_qcnts(rvu, pfvf, info);
+
+ if (pfvf->nix_blkaddr == BLKADDR_NIX0)
+ info->nix = 0;
+ else
+ info->nix = 1;
+
+ info++;
+ tot++;
+
+chk_vfs:
+ for (vf = 0; vf < numvfs; vf++) {
+ if (tot >= IFACE_MAX) {
+ truncated = true;
+ goto done;
+ }
+
+ pcifunc = rvu_make_pcifunc(rvu->pdev, pf, vf + 1);
+ pfvf = rvu_get_pfvf(rvu, pcifunc);
+
+ if (!pfvf->tx_chan_cnt)
+ continue;
+
+ info->is_vf = 1;
+ info->pcifunc = pcifunc;
+ info->rx_chan_base = pfvf->rx_chan_base;
+ info->rx_chan_cnt = pfvf->rx_chan_cnt;
+ info->tx_chan_base = pfvf->tx_chan_base;
+ info->tx_chan_cnt = pfvf->tx_chan_cnt;
+ info->tx_link = nix_get_tx_link(rvu, pcifunc);
+ if (is_sdp_pfvf(rvu, pcifunc))
+ info->is_sdp = 1;
+
+ rvu_iface_get_qcnts(rvu, pfvf, info);
+
+ if (pfvf->nix_blkaddr == BLKADDR_NIX0)
+ info->nix = 0;
+ else
+ info->nix = 1;
+
+ info++;
+
+ tot++;
+ }
+ }
+done:
+ rsp->cnt = tot;
+ rsp->truncated = truncated;
+
+ return 0;
+}
+
int rvu_mbox_handler_free_rsrc_cnt(struct rvu *rvu, struct msg_req *req,
struct free_rsrcs_rsp *rsp)
{
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
index c5610f242687..73d2329b5c26 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
@@ -1158,6 +1158,7 @@ void rvu_program_channels(struct rvu *rvu);
/* CN10K NIX */
void rvu_nix_block_cn10k_init(struct rvu *rvu, struct nix_hw *nix_hw);
+int nix_get_tx_link(struct rvu *rvu, u16 pcifunc);
/* CN10K RVU - LMT*/
void rvu_reset_lmt_map_tbl(struct rvu *rvu, u16 pcifunc);
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
index 78667a0977c0..0c6b2b425534 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
@@ -32,7 +32,6 @@ static int nix_free_all_bandprof(struct rvu *rvu, u16 pcifunc);
static void nix_clear_ratelimit_aggr(struct rvu *rvu, struct nix_hw *nix_hw,
u32 leaf_prof);
static const char *nix_get_ctx_name(int ctype);
-static int nix_get_tx_link(struct rvu *rvu, u16 pcifunc);
enum mc_tbl_sz {
MC_TBL_SZ_256,
@@ -906,6 +905,8 @@ static void nix_setup_lso(struct rvu *rvu, struct nix_hw *nix_hw, int blkaddr)
static void nix_ctx_free(struct rvu *rvu, struct rvu_pfvf *pfvf)
{
+ mutex_lock(&rvu->rsrc_lock);
+
kfree(pfvf->rq_bmap);
kfree(pfvf->sq_bmap);
kfree(pfvf->cq_bmap);
@@ -931,6 +932,8 @@ static void nix_ctx_free(struct rvu *rvu, struct rvu_pfvf *pfvf)
pfvf->rss_ctx = NULL;
pfvf->nix_qints_ctx = NULL;
pfvf->cq_ints_ctx = NULL;
+
+ mutex_unlock(&rvu->rsrc_lock);
}
static int nixlf_rss_ctx_init(struct rvu *rvu, int blkaddr,
@@ -2087,10 +2090,10 @@ static void nix_clear_tx_xoff(struct rvu *rvu, int blkaddr,
rvu_write64(rvu, blkaddr, reg, 0x0);
}
-static int nix_get_tx_link(struct rvu *rvu, u16 pcifunc)
+int nix_get_tx_link(struct rvu *rvu, u16 pcifunc)
{
- struct rvu_hwinfo *hw = rvu->hw;
int pf = rvu_get_pf(rvu->pdev, pcifunc);
+ struct rvu_hwinfo *hw = rvu->hw;
u8 cgx_id = 0, lmac_id = 0;
if (is_lbk_vf(rvu, pcifunc)) {/* LBK links */
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
index 08b83de9beb4..9ba34c03db9c 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
@@ -3544,6 +3544,46 @@ int rvu_mbox_handler_npc_mcam_free_entry(struct rvu *rvu,
return rc;
}
+int rvu_mbox_handler_npc_flow_del_n_free(struct rvu *rvu,
+ struct npc_flow_del_n_free_req *mreq,
+ struct msg_rsp *rsp)
+{
+ struct npc_mcam_free_entry_req sreq = { 0 };
+ struct npc_delete_flow_req dreq = { 0 };
+ struct npc_delete_flow_rsp drsp = { 0 };
+ u16 entry[256];
+ int ret = 0, i;
+ bool err = false;
+ u16 cnt;
+
+ sreq.hdr.pcifunc = mreq->hdr.pcifunc;
+ dreq.hdr.pcifunc = mreq->hdr.pcifunc;
+
+ cnt = mreq->cnt;
+ if (!cnt || cnt > 256) {
+ dev_err(rvu->dev, "Invalid cnt=%u\n", cnt);
+ return -EINVAL;
+ }
+
+ /* Snapshot shared mailbox memory before processing the request. */
+ memcpy(entry, mreq->entry, cnt * sizeof(entry[0]));
+
+ for (i = 0; i < cnt; i++) {
+ dreq.entry = entry[i];
+ rvu_mbox_handler_npc_delete_flow(rvu, &dreq, &drsp);
+
+ sreq.entry = entry[i];
+ ret = rvu_mbox_handler_npc_mcam_free_entry(rvu, &sreq, rsp);
+ if (ret) {
+ dev_err(rvu->dev, "free entry error for i=%d entry=%d\n",
+ i, entry[i]);
+ err = true;
+ }
+ }
+
+ return err ? -EINVAL : 0;
+}
+
int rvu_mbox_handler_npc_mcam_read_entry(struct rvu *rvu,
struct npc_mcam_read_entry_req *req,
struct npc_mcam_read_entry_rsp *rsp)
@@ -4398,6 +4438,77 @@ int rvu_mbox_handler_npc_mcam_entry_stats(struct rvu *rvu,
return 0;
}
+int rvu_mbox_handler_npc_mcam_mul_stats(struct rvu *rvu,
+ struct npc_mcam_get_mul_stats_req *req,
+ struct npc_mcam_get_mul_stats_rsp *rsp)
+{
+ struct npc_mcam *mcam = &rvu->hw->mcam;
+ u16 req_cnt, index, cntr, mcam_entry;
+ u16 pcifunc = req->hdr.pcifunc;
+ int blkaddr, cnt = 0, i;
+ u16 entry[256];
+ u64 regval;
+ u32 bank;
+
+ req_cnt = req->cnt;
+ if (!req_cnt || req_cnt > 256) {
+ dev_err(rvu->dev, "%s invalid request cnt=%u\n",
+ __func__, req_cnt);
+ return -EINVAL;
+ }
+
+ /* Snapshot shared mailbox memory before processing the request. */
+ memcpy(entry, req->entry, req_cnt * sizeof(entry[0]));
+
+ blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0);
+ if (blkaddr < 0)
+ return NPC_MCAM_INVALID_REQ;
+
+ mutex_lock(&mcam->lock);
+
+ for (i = 0; i < req_cnt; i++) {
+ mcam_entry = npc_cn20k_vidx2idx(entry[i]);
+
+ if (npc_mcam_verify_entry(mcam, pcifunc, mcam_entry)) {
+ mutex_unlock(&mcam->lock);
+ dev_err(rvu->dev, "%s invalid mcam index=%d\n",
+ __func__, entry[i]);
+ return -EINVAL;
+ }
+
+ index = mcam_entry & (mcam->banksize - 1);
+ bank = npc_get_bank(mcam, mcam_entry);
+
+ if (is_cn20k(rvu->pdev)) {
+ regval = rvu_read64(rvu, blkaddr,
+ NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(index,
+ bank));
+ rsp->stat[cnt] = regval;
+ cnt++;
+ continue;
+ }
+
+ /* read MCAM entry STAT_ACT register */
+ regval = rvu_read64(rvu, blkaddr, NPC_AF_MCAMEX_BANKX_STAT_ACT(index, bank));
+
+ if (!(regval & rvu->hw->npc_stat_ena)) {
+ rsp->stat[cnt] = 0;
+ cnt++;
+ continue;
+ }
+
+ cntr = regval & 0x1FF;
+
+ rsp->stat[cnt] = rvu_read64(rvu, blkaddr, NPC_AF_MATCH_STATX(cntr));
+ rsp->stat[cnt] &= BIT_ULL(48) - 1;
+ cnt++;
+ }
+
+ rsp->cnt = cnt;
+ mutex_unlock(&mcam->lock);
+ return 0;
+}
+
void rvu_npc_clear_ucast_entry(struct rvu *rvu, int pcifunc, int nixlf)
{
struct npc_mcam *mcam = &rvu->hw->mcam;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
index 91b5947dae06..09c7ee8571df 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
@@ -1926,6 +1926,17 @@ static int npc_delete_flow(struct rvu *rvu, struct rvu_npc_mcam_rule *rule,
return rvu_mbox_handler_npc_mcam_dis_entry(rvu, &dis_req, &dis_rsp);
}
+int rvu_mbox_handler_npc_mcam_get_features(struct rvu *rvu,
+ struct msg_req *req,
+ struct npc_mcam_get_features_rsp *rsp)
+{
+ struct npc_mcam *mcam = &rvu->hw->mcam;
+
+ rsp->rx_features = mcam->rx_features;
+ rsp->tx_features = mcam->tx_features;
+ return 0;
+}
+
int rvu_mbox_handler_npc_delete_flow(struct rvu *rvu,
struct npc_delete_flow_req *req,
struct npc_delete_flow_rsp *rsp)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c
new file mode 100644
index 000000000000..fe143ad3f944
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+
+#include "rvu.h"
+
+int rvu_mbox_handler_swdev2af_notify(struct rvu *rvu,
+ struct swdev2af_notify_req *req,
+ struct msg_rsp *rsp)
+{
+ return 0;
+}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h
new file mode 100644
index 000000000000..f28dba556d80
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+
+#ifndef RVU_SWITCH_H
+#define RVU_SWITCH_H
+
+#endif
--
2.43.0
^ permalink raw reply related
* [PATCH v4 net-next 4/9] octeontx2-af: switch: Representor for switch port
From: Ratheesh Kannoth @ 2026-07-21 8:18 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, sgoutham,
Ratheesh Kannoth
In-Reply-To: <20260721081824.1430607-1-rkannoth@marvell.com>
Extends esw_cfg with a devlink-derived switch id, copies it into
rvu->rswitch on the AF, adds rvu_sw_port_id(), exports
rvu_rep_get_vlan_id().
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
.../net/ethernet/marvell/octeontx2/af/mbox.h | 1 +
.../net/ethernet/marvell/octeontx2/af/rvu.h | 5 +++++
.../ethernet/marvell/octeontx2/af/rvu_rep.c | 15 ++++++++++++++-
.../marvell/octeontx2/af/switch/rvu_sw.c | 19 +++++++++++++++++++
.../marvell/octeontx2/af/switch/rvu_sw.h | 5 +++++
.../net/ethernet/marvell/octeontx2/nic/rep.c | 4 ++++
6 files changed, 48 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
index 23bc66ed854e..cdfb5a8bafb9 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
@@ -1834,6 +1834,7 @@ struct get_rep_cnt_rsp {
struct esw_cfg_req {
struct mbox_msghdr hdr;
u8 ena;
+ unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN];
u64 rsvd;
};
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
index 73d2329b5c26..8cf1ad9ec749 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
@@ -576,6 +576,10 @@ struct rvu_switch {
u16 *entry2pcifunc;
u16 mode;
u16 start_entry;
+ unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN];
+#define RVU_SWITCH_FLAG_FW_READY BIT_ULL(0)
+ u64 flags;
+ u16 pcifunc;
};
struct rep_evtq_ent {
@@ -1197,4 +1201,5 @@ int rvu_rep_install_mcam_rules(struct rvu *rvu);
void rvu_rep_update_rules(struct rvu *rvu, u16 pcifunc, bool ena);
int rvu_rep_notify_pfvf_state(struct rvu *rvu, u16 pcifunc, bool enable);
int npc_mcam_verify_entry(struct npc_mcam *mcam, u16 pcifunc, int entry);
+u16 rvu_rep_get_vlan_id(struct rvu *rvu, u16 pcifunc);
#endif /* RVU_H */
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c
index a2781e0f504e..0ee2fd935abf 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_rep.c
@@ -6,6 +6,7 @@
*/
#include <linux/bitfield.h>
+#include <linux/stddef.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/module.h>
@@ -189,7 +190,7 @@ int rvu_mbox_handler_nix_lf_stats(struct rvu *rvu,
return 0;
}
-static u16 rvu_rep_get_vlan_id(struct rvu *rvu, u16 pcifunc)
+u16 rvu_rep_get_vlan_id(struct rvu *rvu, u16 pcifunc)
{
int id;
@@ -429,6 +430,15 @@ int rvu_rep_pf_init(struct rvu *rvu)
return 0;
}
+static bool esw_cfg_req_has_switch_id(const struct esw_cfg_req *req)
+{
+ u16 msg_len = req->hdr.next_msgoff -
+ ALIGN(sizeof(struct mbox_hdr), MBOX_MSG_ALIGN);
+
+ return msg_len >= offsetof(struct esw_cfg_req, switch_id) +
+ MAX_PHYS_ITEM_ID_LEN;
+}
+
int rvu_mbox_handler_esw_cfg(struct rvu *rvu, struct esw_cfg_req *req,
struct msg_rsp *rsp)
{
@@ -436,6 +446,9 @@ int rvu_mbox_handler_esw_cfg(struct rvu *rvu, struct esw_cfg_req *req,
return 0;
rvu->rep_mode = req->ena;
+ if (esw_cfg_req_has_switch_id(req))
+ memcpy(rvu->rswitch.switch_id, req->switch_id,
+ MAX_PHYS_ITEM_ID_LEN);
if (!rvu->rep_mode)
rvu_npc_free_mcam_entries(rvu, req->hdr.pcifunc, -1);
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c
index fe143ad3f944..403d57870efe 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.c
@@ -5,7 +5,26 @@
*
*/
+#include <linux/bitfield.h>
+
#include "rvu.h"
+#include "rvu_sw.h"
+
+u32 rvu_sw_port_id(struct rvu *rvu, u16 pcifunc)
+{
+ u16 rep_id;
+
+ if (!rvu->rep2pfvf_map || !rvu->rep_cnt)
+ return RVU_SW_INVALID_PORT_ID;
+
+ rep_id = rvu_rep_get_vlan_id(rvu, pcifunc);
+ if (rep_id >= rvu->rep_cnt ||
+ rvu->rep2pfvf_map[rep_id] != pcifunc)
+ return RVU_SW_INVALID_PORT_ID;
+
+ return FIELD_PREP(GENMASK_ULL(31, 16), rep_id) |
+ FIELD_PREP(GENMASK_ULL(15, 0), pcifunc);
+}
int rvu_mbox_handler_swdev2af_notify(struct rvu *rvu,
struct swdev2af_notify_req *req,
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h
index f28dba556d80..e9ad32c84576 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/switch/rvu_sw.h
@@ -8,4 +8,9 @@
#ifndef RVU_SWITCH_H
#define RVU_SWITCH_H
+/* RVU Switch */
+#define RVU_SW_INVALID_PORT_ID ((u32)~0U)
+
+u32 rvu_sw_port_id(struct rvu *rvu, u16 pcifunc);
+
#endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c
index 0f5d5642d3f7..257a2ae6a53e 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c
@@ -399,8 +399,11 @@ static void rvu_rep_get_stats64(struct net_device *dev,
static int rvu_eswitch_config(struct otx2_nic *priv, u8 ena)
{
+ struct devlink_port_attrs attrs = {};
struct esw_cfg_req *req;
+ rvu_rep_devlink_set_switch_id(priv, &attrs.switch_id);
+
mutex_lock(&priv->mbox.lock);
req = otx2_mbox_alloc_msg_esw_cfg(&priv->mbox);
if (!req) {
@@ -408,6 +411,7 @@ static int rvu_eswitch_config(struct otx2_nic *priv, u8 ena)
return -ENOMEM;
}
req->ena = ena;
+ memcpy(req->switch_id, attrs.switch_id.id, attrs.switch_id.id_len);
otx2_sync_mbox_msg(&priv->mbox);
mutex_unlock(&priv->mbox.lock);
return 0;
--
2.43.0
^ permalink raw reply related
* [PATCH v4 net-next 5/9] octeontx2-af: switch: TL1 scheduling and NPC channel control
From: Ratheesh Kannoth @ 2026-07-21 8:18 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, sgoutham,
Ratheesh Kannoth
In-Reply-To: <20260721081824.1430607-1-rkannoth@marvell.com>
Switch (PAN) mode needs more than one TL1 scheduler queue index so the
hardware can steer traffic to different links according to NPC flow
rules, not only the PF/VF default Tx link.
Add NIX_TXSCH_ALLOC_FLAG_PAN to nix_txsch_alloc requests: use the PAN
link index for scheduler range calculation, allow multiple TL1 queues
when the aggregate level spans start..end, and allocate indices in
that range. Add TXSCHQ_FREE_PAN_TL1 so TL1 entries in that path can be
freed via nix_txsch_free where they were previously skipped.
For NPC install flow, add set_chanmask so callers can keep a non-default
chan_mask when the requester is not the AF; without it, chan_mask was
always forced to 0xFFF for non-AF functions.
Allocate the NIX LF SQ bitmap with the same span used by
bitmap_weight(..., BITS_PER_LONG * 16) in rvu_get_hwinfo().
Extend struct sg_list with cq_idx and len for transmit-side metadata.
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
.../net/ethernet/marvell/octeontx2/af/mbox.h | 15 ++
.../net/ethernet/marvell/octeontx2/af/rvu.c | 25 ++-
.../net/ethernet/marvell/octeontx2/af/rvu.h | 6 +
.../ethernet/marvell/octeontx2/af/rvu_nix.c | 179 ++++++++++++++++--
.../marvell/octeontx2/af/rvu_npc_fs.c | 20 +-
.../marvell/octeontx2/nic/otx2_txrx.h | 2 +
6 files changed, 216 insertions(+), 31 deletions(-)
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
index cdfb5a8bafb9..a63771d7b102 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
@@ -1158,6 +1158,13 @@ struct nix_txsch_alloc_req {
/* Scheduler queue count request at each level */
u16 schq_contig[NIX_TXSCH_LVL_CNT]; /* No of contiguous queues */
u16 schq[NIX_TXSCH_LVL_CNT]; /* No of non-contiguous queues */
+ /* Set only by the single switchdev PF (rvu->rswitch.pcifunc). This is
+ * not the eswitch representor (rvu->rep_pcifunc). That PF requests two
+ * aggregate-level TL2 queues on the PAN link, one for CGX and one for
+ * SDP steering. No other PF or VF sets this flag.
+ */
+#define NIX_TXSCH_ALLOC_FLAG_PAN BIT(0)
+ u32 flags;
};
struct nix_txsch_alloc_rsp {
@@ -1176,6 +1183,10 @@ struct nix_txsch_alloc_rsp {
struct nix_txsch_free_req {
struct mbox_msghdr hdr;
#define TXSCHQ_FREE_ALL BIT_ULL(0)
+ /* Frees PAN TL2 queues allocated with NIX_TXSCH_ALLOC_FLAG_PAN. Used
+ * only by the switchdev PF (rvu->rswitch.pcifunc), not by other PFs/VFs.
+ */
+#define TXSCHQ_FREE_PAN_TL1 BIT_ULL(1)
u16 flags;
/* Scheduler queue level to be freed */
u16 schq_lvl;
@@ -2115,6 +2126,10 @@ struct npc_install_flow_req {
u8 hw_prio;
u8 req_kw_type; /* Key type to be written */
u8 alloc_entry; /* only for cn20k */
+ /* When set, keep caller chan_mask instead of the CPT default. Only
+ * honored for the switchdev PF; see rvu_mbox_handler_npc_install_flow().
+ */
+ u8 set_chanmask;
/* For now use any priority, once AF driver is changed to
* allocate least priority entry instead of mid zone then make
* NPC_MCAM_LEAST_PRIO as 3
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
index a0ae0ccc1b2b..168a50655351 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
@@ -1990,18 +1990,29 @@ int rvu_mbox_handler_msix_offset(struct rvu *rvu, struct msg_req *req,
return 0;
}
-static void rvu_iface_get_qcnts(struct rvu *rvu, struct rvu_pfvf *pfvf,
- struct iface_info *info)
+static void rvu_iface_get_qcnts(struct rvu *rvu, u16 pcifunc,
+ struct rvu_pfvf *pfvf, struct iface_info *info)
{
+ int sq_bmap_bits;
+
mutex_lock(&rvu->rsrc_lock);
info->sq_cnt = 0;
info->cq_cnt = 0;
info->rq_cnt = 0;
- /* Use each LF queue context size; bitmaps are sized to qsize longs. */
- if (pfvf->sq_ctx && pfvf->sq_bmap)
- info->sq_cnt = bitmap_weight(pfvf->sq_bmap, pfvf->sq_ctx->qsize);
+ if (pfvf->sq_bmap) {
+ /* Match switchdev sq_bmap allocation size in nix_lf_alloc(). */
+ if (rvu_is_switch_pcifunc(rvu, pcifunc))
+ sq_bmap_bits = NIX_SQ_BMAP_BITS;
+ else if (pfvf->sq_ctx)
+ sq_bmap_bits = pfvf->sq_ctx->qsize;
+ else
+ sq_bmap_bits = 0;
+
+ if (sq_bmap_bits)
+ info->sq_cnt = bitmap_weight(pfvf->sq_bmap, sq_bmap_bits);
+ }
if (pfvf->cq_ctx && pfvf->cq_bmap)
info->cq_cnt = bitmap_weight(pfvf->cq_bmap, pfvf->cq_ctx->qsize);
if (pfvf->rq_ctx && pfvf->rq_bmap)
@@ -2064,7 +2075,7 @@ int rvu_mbox_handler_iface_get_info(struct rvu *rvu, struct msg_req *req,
if (is_sdp_pfvf(rvu, pcifunc))
info->is_sdp = 1;
- rvu_iface_get_qcnts(rvu, pfvf, info);
+ rvu_iface_get_qcnts(rvu, pcifunc, pfvf, info);
if (pfvf->nix_blkaddr == BLKADDR_NIX0)
info->nix = 0;
@@ -2097,7 +2108,7 @@ int rvu_mbox_handler_iface_get_info(struct rvu *rvu, struct msg_req *req,
if (is_sdp_pfvf(rvu, pcifunc))
info->is_sdp = 1;
- rvu_iface_get_qcnts(rvu, pfvf, info);
+ rvu_iface_get_qcnts(rvu, pcifunc, pfvf, info);
if (pfvf->nix_blkaddr == BLKADDR_NIX0)
info->nix = 0;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
index 8cf1ad9ec749..0662cc6134b0 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
@@ -335,6 +335,7 @@ struct nix_txsch {
u8 lvl;
#define NIX_TXSCHQ_FREE BIT_ULL(1)
#define NIX_TXSCHQ_CFG_DONE BIT_ULL(0)
+#define NIX_SQ_BMAP_BITS (BITS_PER_LONG * 16)
#define TXSCH_MAP_FUNC(__pfvf_map) ((__pfvf_map) & 0xFFFF)
#define TXSCH_MAP_FLAGS(__pfvf_map) ((__pfvf_map) >> 16)
#define TXSCH_MAP(__func, __flags) (((__func) & 0xFFFF) | ((__flags) << 16))
@@ -904,6 +905,11 @@ static inline bool is_pffunc_af(u16 pcifunc)
return !pcifunc;
}
+static inline bool rvu_is_switch_pcifunc(struct rvu *rvu, u16 pcifunc)
+{
+ return rvu->rswitch.pcifunc && pcifunc == rvu->rswitch.pcifunc;
+}
+
static inline bool is_rvu_fwdata_valid(struct rvu *rvu)
{
return (rvu->fwdata->header_magic == RVU_FWDATA_HEADER_MAGIC) &&
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
index 0c6b2b425534..e3e2f0113328 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
@@ -1052,6 +1052,7 @@ static int rvu_nix_blk_aq_enq_inst(struct rvu *rvu, struct nix_hw *nix_hw,
u16 pcifunc = req->hdr.pcifunc;
int nixlf, blkaddr, rc = 0;
struct nix_aq_inst_s inst;
+ u64 sq_bmap_bits, max_q;
struct rvu_block *block;
struct admin_queue *aq;
struct rvu_pfvf *pfvf;
@@ -1086,10 +1087,25 @@ static int rvu_nix_blk_aq_enq_inst(struct rvu *rvu, struct nix_hw *nix_hw,
if (!pfvf->rq_ctx || req->qidx >= pfvf->rq_ctx->qsize)
rc = NIX_AF_ERR_AQ_ENQUEUE;
break;
- case NIX_AQ_CTYPE_SQ:
- if (!pfvf->sq_ctx || req->qidx >= pfvf->sq_ctx->qsize)
+ case NIX_AQ_CTYPE_SQ: {
+ if (!pfvf->sq_ctx) {
+ rc = NIX_AF_ERR_AQ_ENQUEUE;
+ break;
+ }
+
+ /* Switchdev PF uses a fixed sq_bmap (NIX_SQ_BMAP_BITS); cap qidx
+ * to that span so __set_bit() cannot run past the allocation.
+ * nix_lf_alloc() also rejects sq_cnt above NIX_SQ_BMAP_BITS.
+ */
+ sq_bmap_bits = rvu_is_switch_pcifunc(rvu, pcifunc) ?
+ NIX_SQ_BMAP_BITS :
+ (u64)pfvf->sq_ctx->qsize * BITS_PER_LONG;
+ max_q = min_t(u64, pfvf->sq_ctx->qsize, sq_bmap_bits);
+
+ if ((u64)req->qidx >= max_q)
rc = NIX_AF_ERR_AQ_ENQUEUE;
break;
+ }
case NIX_AQ_CTYPE_CQ:
if (!pfvf->cq_ctx || req->qidx >= pfvf->cq_ctx->qsize)
rc = NIX_AF_ERR_AQ_ENQUEUE;
@@ -1511,16 +1527,26 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
int nixlf, qints, hwctx_size, intf, rc = 0;
u16 bcast, mcast, promisc, ucast;
struct rvu_hwinfo *hw = rvu->hw;
+ u64 cfg, ctx_cfg, sq_bmap_bits;
u16 pcifunc = req->hdr.pcifunc;
bool rules_created = false;
struct rvu_block *block;
struct rvu_pfvf *pfvf;
- u64 cfg, ctx_cfg;
int blkaddr;
if (!req->rq_cnt || !req->sq_cnt || !req->cq_cnt)
return NIX_AF_ERR_PARAM;
+ /* Switchdev PF sq_bmap is fixed at NIX_SQ_BMAP_BITS; reject larger
+ * sq_cnt before allocating context memory or the bitmap.
+ */
+ sq_bmap_bits = rvu_is_switch_pcifunc(rvu, pcifunc) ?
+ NIX_SQ_BMAP_BITS :
+ (u64)req->sq_cnt * BITS_PER_LONG;
+
+ if ((u64)req->sq_cnt > sq_bmap_bits)
+ return NIX_AF_ERR_PARAM;
+
if (req->way_mask)
req->way_mask &= 0xFFFF;
@@ -1600,7 +1626,12 @@ int rvu_mbox_handler_nix_lf_alloc(struct rvu *rvu,
if (rc)
goto free_mem;
- pfvf->sq_bmap = kcalloc(req->sq_cnt, sizeof(long), GFP_KERNEL);
+ if (rvu_is_switch_pcifunc(rvu, pcifunc))
+ /* Fixed-size bitmap; sq_cnt capped to NIX_SQ_BMAP_BITS above. */
+ pfvf->sq_bmap = kcalloc(BITS_TO_LONGS(NIX_SQ_BMAP_BITS),
+ sizeof(long), GFP_KERNEL);
+ else
+ pfvf->sq_bmap = kcalloc(req->sq_cnt, sizeof(long), GFP_KERNEL);
if (!pfvf->sq_bmap) {
rc = -ENOMEM;
goto free_mem;
@@ -2127,6 +2158,25 @@ static void nix_get_txschq_range(struct rvu *rvu, u16 pcifunc,
}
}
+static int nix_get_pan_tx_link(struct rvu *rvu)
+{
+ struct rvu_hwinfo *hw = rvu->hw;
+
+ return hw->cgx_links + hw->lbk_links + 1;
+}
+
+static bool nix_txsch_is_pan_schq(struct rvu *rvu, int schq)
+{
+ int pan_link = nix_get_pan_tx_link(rvu);
+
+ return schq >= pan_link && schq <= pan_link + 1;
+}
+
+static bool nix_txsch_pan_allowed(struct rvu *rvu, u16 pcifunc)
+{
+ return rvu_is_switch_pcifunc(rvu, pcifunc);
+}
+
static int nix_check_txschq_alloc_req(struct rvu *rvu, int lvl, u16 pcifunc,
struct nix_hw *nix_hw,
struct nix_txsch_alloc_req *req)
@@ -2142,12 +2192,27 @@ static int nix_check_txschq_alloc_req(struct rvu *rvu, int lvl, u16 pcifunc,
if (!req_schq)
return 0;
- link = nix_get_tx_link(rvu, pcifunc);
+ if (req->flags & NIX_TXSCH_ALLOC_FLAG_PAN) {
+ if (!nix_txsch_pan_allowed(rvu, pcifunc))
+ return NIX_AF_ERR_TLX_ALLOC_FAIL;
+ link = nix_get_pan_tx_link(rvu);
+ } else {
+ link = nix_get_tx_link(rvu, pcifunc);
+ }
/* For traffic aggregating scheduler level, one queue is enough */
if (lvl >= hw->cap.nix_tx_aggr_lvl) {
- if (req_schq != 1)
+ if (req_schq != 1 && !(req->flags & NIX_TXSCH_ALLOC_FLAG_PAN))
return NIX_AF_ERR_TLX_ALLOC_FAIL;
+ if (req->schq[lvl] > MAX_TXSCHQ_PER_FUNC ||
+ req->schq_contig[lvl] > MAX_TXSCHQ_PER_FUNC)
+ return NIX_AF_ERR_TLX_ALLOC_FAIL;
+ if (req->flags & NIX_TXSCH_ALLOC_FLAG_PAN) {
+ if (link >= txsch->schq.max || link + 1 >= txsch->schq.max)
+ return NIX_AF_ERR_TLX_ALLOC_FAIL;
+ if (req_schq > 2)
+ return NIX_AF_ERR_TLX_ALLOC_FAIL;
+ }
return 0;
}
@@ -2176,9 +2241,9 @@ static int nix_check_txschq_alloc_req(struct rvu *rvu, int lvl, u16 pcifunc,
return 0;
}
-static void nix_txsch_alloc(struct rvu *rvu, struct nix_txsch *txsch,
- struct nix_txsch_alloc_rsp *rsp,
- int lvl, int start, int end)
+static int nix_txsch_alloc(struct rvu *rvu, struct nix_txsch *txsch,
+ struct nix_txsch_alloc_rsp *rsp,
+ int lvl, int start, int end)
{
struct rvu_hwinfo *hw = rvu->hw;
u16 pcifunc = rsp->hdr.pcifunc;
@@ -2188,6 +2253,46 @@ static void nix_txsch_alloc(struct rvu *rvu, struct nix_txsch *txsch,
* on transmit link to which PF_FUNC is mapped to.
*/
if (lvl >= hw->cap.nix_tx_aggr_lvl) {
+ if (start != end) {
+ int want_contig = rsp->schq_contig[lvl];
+ int got_contig = 0, got = 0;
+ int want = rsp->schq[lvl];
+
+ for (schq = start; schq <= end; schq++) {
+ if (test_bit(schq, txsch->schq.bmap))
+ continue;
+
+ if (got_contig < want_contig) {
+ set_bit(schq, txsch->schq.bmap);
+ rsp->schq_contig_list[lvl][got_contig++] = schq;
+ continue;
+ }
+
+ if (got < want) {
+ set_bit(schq, txsch->schq.bmap);
+ rsp->schq_list[lvl][got++] = schq;
+ }
+ }
+
+ rsp->schq_contig[lvl] = got_contig;
+ rsp->schq[lvl] = got;
+
+ if (got_contig < want_contig || got < want) {
+ for (idx = 0; idx < got_contig; idx++)
+ clear_bit(rsp->schq_contig_list[lvl][idx],
+ txsch->schq.bmap);
+ for (idx = 0; idx < got; idx++)
+ clear_bit(rsp->schq_list[lvl][idx],
+ txsch->schq.bmap);
+ rsp->schq_contig[lvl] = 0;
+ rsp->schq[lvl] = 0;
+ dev_err(rvu->dev,
+ "Could not allocate schq at lvl=%u start=%u end=%u\n",
+ lvl, start, end);
+ return -ENOMEM;
+ }
+ return 0;
+ }
/* A single TL queue is allocated */
if (rsp->schq_contig[lvl]) {
rsp->schq_contig[lvl] = 1;
@@ -2202,7 +2307,7 @@ static void nix_txsch_alloc(struct rvu *rvu, struct nix_txsch *txsch,
rsp->schq[lvl] = 1;
rsp->schq_list[lvl][0] = start;
}
- return;
+ return 0;
}
/* Adjust the queue request count if HW supports
@@ -2214,7 +2319,7 @@ static void nix_txsch_alloc(struct rvu *rvu, struct nix_txsch *txsch,
if (idx >= (end - start) || test_bit(schq, txsch->schq.bmap)) {
rsp->schq_contig[lvl] = 0;
rsp->schq[lvl] = 0;
- return;
+ return 0;
}
if (rsp->schq_contig[lvl]) {
@@ -2227,7 +2332,7 @@ static void nix_txsch_alloc(struct rvu *rvu, struct nix_txsch *txsch,
set_bit(schq, txsch->schq.bmap);
rsp->schq_list[lvl][0] = schq;
}
- return;
+ return 0;
}
/* Allocate contiguous queue indices requesty first */
@@ -2258,6 +2363,8 @@ static void nix_txsch_alloc(struct rvu *rvu, struct nix_txsch *txsch,
/* Update how many were allocated */
rsp->schq[lvl] = idx;
}
+
+ return 0;
}
int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu,
@@ -2282,6 +2389,10 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu,
if (!nix_hw)
return NIX_AF_ERR_INVALID_NIXBLK;
+ if ((req->flags & NIX_TXSCH_ALLOC_FLAG_PAN) &&
+ !nix_txsch_pan_allowed(rvu, pcifunc))
+ return NIX_AF_ERR_TLX_ALLOC_FAIL;
+
mutex_lock(&rvu->rsrc_lock);
/* Check if request is valid as per HW capabilities
@@ -2304,11 +2415,14 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu,
rsp->schq[lvl] = req->schq[lvl];
rsp->schq_contig[lvl] = req->schq_contig[lvl];
- link = nix_get_tx_link(rvu, pcifunc);
+ if (req->flags & NIX_TXSCH_ALLOC_FLAG_PAN)
+ link = nix_get_pan_tx_link(rvu);
+ else
+ link = nix_get_tx_link(rvu, pcifunc);
if (lvl >= hw->cap.nix_tx_aggr_lvl) {
start = link;
- end = link;
+ end = link + !!(req->flags & NIX_TXSCH_ALLOC_FLAG_PAN);
} else if (hw->cap.nix_fixed_txschq_mapping) {
nix_get_txschq_range(rvu, pcifunc, link, &start, &end);
} else {
@@ -2316,10 +2430,11 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu,
end = txsch->schq.max;
}
- nix_txsch_alloc(rvu, txsch, rsp, lvl, start, end);
+ if (nix_txsch_alloc(rvu, txsch, rsp, lvl, start, end))
+ goto err;
/* Reset queue config */
- for (idx = 0; idx < req->schq_contig[lvl]; idx++) {
+ for (idx = 0; idx < rsp->schq_contig[lvl]; idx++) {
schq = rsp->schq_contig_list[lvl][idx];
if (!(TXSCH_MAP_FLAGS(pfvf_map[schq]) &
NIX_TXSCHQ_CFG_DONE))
@@ -2329,7 +2444,7 @@ int rvu_mbox_handler_nix_txsch_alloc(struct rvu *rvu,
nix_reset_tx_schedule(rvu, blkaddr, lvl, schq);
}
- for (idx = 0; idx < req->schq[lvl]; idx++) {
+ for (idx = 0; idx < rsp->schq[lvl]; idx++) {
schq = rsp->schq_list[lvl][idx];
if (!(TXSCH_MAP_FLAGS(pfvf_map[schq]) &
NIX_TXSCHQ_CFG_DONE))
@@ -2598,6 +2713,19 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc)
}
nix_clear_tx_xoff(rvu, blkaddr, NIX_TXSCH_LVL_TL1,
nix_get_tx_link(rvu, pcifunc));
+ /* TL1 is at nix_tx_aggr_lvl so the loop above skips it; also clear
+ * PAN TL1 XOFF on switch-owned links before flushing SMQs.
+ */
+ if (nix_txsch_pan_allowed(rvu, pcifunc)) {
+ txsch = &nix_hw->txsch[NIX_TXSCH_LVL_TL1];
+
+ for (schq = nix_get_pan_tx_link(rvu);
+ nix_txsch_is_pan_schq(rvu, schq); schq++) {
+ if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc)
+ continue;
+ nix_clear_tx_xoff(rvu, blkaddr, NIX_TXSCH_LVL_TL1, schq);
+ }
+ }
/* On PF cleanup, clear cfg done flag as
* PF would have changed default config.
@@ -2625,11 +2753,11 @@ static int nix_txschq_free(struct rvu *rvu, u16 pcifunc)
/* TLs above aggregation level are shared across all PF
* and it's VFs, hence skip freeing them.
*/
- if (lvl >= hw->cap.nix_tx_aggr_lvl)
- continue;
-
txsch = &nix_hw->txsch[lvl];
for (schq = 0; schq < txsch->schq.max; schq++) {
+ if (lvl >= hw->cap.nix_tx_aggr_lvl &&
+ !nix_txsch_is_pan_schq(rvu, schq))
+ continue;
if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc)
continue;
nix_reset_tx_schedule(rvu, blkaddr, lvl, schq);
@@ -2673,7 +2801,16 @@ static int nix_txschq_free_one(struct rvu *rvu,
schq = req->schq;
txsch = &nix_hw->txsch[lvl];
- if (lvl >= hw->cap.nix_tx_aggr_lvl || schq >= txsch->schq.max)
+ if (req->flags & TXSCHQ_FREE_PAN_TL1) {
+ if (!nix_txsch_pan_allowed(rvu, pcifunc))
+ return NIX_AF_ERR_TLX_INVALID;
+ if (!nix_txsch_is_pan_schq(rvu, schq))
+ return NIX_AF_ERR_TLX_INVALID;
+ } else if (lvl >= hw->cap.nix_tx_aggr_lvl) {
+ return 0;
+ }
+
+ if (schq >= txsch->schq.max)
return 0;
pfvf_map = txsch->pfvf_map;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
index 09c7ee8571df..a1a82ddb7c50 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
@@ -1828,9 +1828,23 @@ int rvu_mbox_handler_npc_install_flow(struct rvu *rvu,
target = req->hdr.pcifunc;
}
- /* ignore chan_mask in case pf func is not AF, revisit later */
- if (!is_pffunc_af(req->hdr.pcifunc))
- req->chan_mask = rvu_get_cpt_chan_mask(rvu);
+ /* Non-AF callers get the CPT default chan_mask unless the authorized
+ * switchdev PF sets set_chanmask to preserve a caller-supplied mask.
+ * VFs and other PFs must not use set_chanmask; that would bypass
+ * channel isolation.
+ */
+ if (!is_pffunc_af(req->hdr.pcifunc)) {
+ if (req->set_chanmask &&
+ !rvu_is_switch_pcifunc(rvu, req->hdr.pcifunc)) {
+ rvu_npc_free_entry_for_flow_install(rvu,
+ req->hdr.pcifunc,
+ allocated,
+ req->entry);
+ return NPC_FLOW_VF_PERM_DENIED;
+ }
+ if (!req->set_chanmask)
+ req->chan_mask = rvu_get_cpt_chan_mask(rvu);
+ }
err = npc_check_unsupported_flows(rvu, req->features, req->intf);
if (err) {
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h
index acf259d72008..73a98b94426b 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h
@@ -78,6 +78,8 @@ struct otx2_rcv_queue {
struct sg_list {
u16 num_segs;
u16 flags;
+ u16 cq_idx;
+ u16 len;
u64 skb;
u64 size[OTX2_MAX_FRAGS_IN_SQE];
u64 dma_addr[OTX2_MAX_FRAGS_IN_SQE];
--
2.43.0
^ permalink raw reply related
* [PATCH v4 net-next 6/9] octeontx2-pf: switch: Register notifiers for switch offload
From: Ratheesh Kannoth @ 2026-07-21 8:18 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, sgoutham,
Ratheesh Kannoth
In-Reply-To: <20260721081824.1430607-1-rkannoth@marvell.com>
The representor enables switch mode via devlink; register and unregister
the switch notifier blocks when that mode is turned on or off so the PF
can observe FIB routes, neighbour updates, IPv4/IPv6 address changes,
netdev state, and switchdev FDB notifications.
Add sw_nb_v4.c and sw_nb_v6.c for IPv4 and IPv6-specific handling, build
sw_nb_v6.o only when CONFIG_IPV6 is set, and extend sw_nb.c with device
filtering for Cavium ports behind bridges and VLANs.
Initialize and tear down the existing sw_fdb, sw_fib, and sw_fl helpers
together with notifier registration.
Signed-off-by: Ratheesh Kannoth <rkannoth@marvell.com>
---
.../ethernet/marvell/octeontx2/nic/Makefile | 6 +-
.../net/ethernet/marvell/octeontx2/nic/rep.c | 38 +-
.../marvell/octeontx2/nic/switch/sw_nb.c | 502 +++++++++++++++++-
.../marvell/octeontx2/nic/switch/sw_nb.h | 37 +-
.../marvell/octeontx2/nic/switch/sw_nb_v4.c | 358 +++++++++++++
.../marvell/octeontx2/nic/switch/sw_nb_v4.h | 21 +
.../marvell/octeontx2/nic/switch/sw_nb_v6.c | 292 ++++++++++
.../marvell/octeontx2/nic/switch/sw_nb_v6.h | 21 +
8 files changed, 1266 insertions(+), 9 deletions(-)
create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c
create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.h
create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c
create mode 100644 drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile
index 123b0af23abd..02ab0634f58f 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/Makefile
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/Makefile
@@ -11,7 +11,11 @@ rvu_nicpf-y := otx2_pf.o otx2_common.o otx2_txrx.o otx2_ethtool.o \
otx2_flows.o otx2_tc.o cn10k.o cn20k.o otx2_dmac_flt.o \
otx2_devlink.o qos_sq.o qos.o otx2_xsk.o \
switch/sw_fdb.o switch/sw_fl.o
-rvu_nicpf-$(CONFIG_OCTEONTX_SWITCH) += switch/sw_nb.o switch/sw_fib.o
+rvu_nicpf-$(CONFIG_OCTEONTX_SWITCH) += switch/sw_nb.o switch/sw_fib.o \
+ switch/sw_nb_v4.o
+ifneq ($(CONFIG_IPV6),)
+rvu_nicpf-$(CONFIG_OCTEONTX_SWITCH) += switch/sw_nb_v6.o
+endif
rvu_nicvf-y := otx2_vf.o
rvu_rep-y := rep.o
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c
index 257a2ae6a53e..1900235fabc5 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c
@@ -15,6 +15,7 @@
#include "cn10k.h"
#include "otx2_reg.h"
#include "rep.h"
+#include "switch/sw_nb.h"
#define DRV_NAME "rvu_rep"
#define DRV_STRING "Marvell RVU Representor Driver"
@@ -399,22 +400,55 @@ static void rvu_rep_get_stats64(struct net_device *dev,
static int rvu_eswitch_config(struct otx2_nic *priv, u8 ena)
{
+#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
+ struct net_device *netdev = priv->netdev;
+#endif
struct devlink_port_attrs attrs = {};
struct esw_cfg_req *req;
+ int mbox_err;
+#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
+ int err;
+#endif
rvu_rep_devlink_set_switch_id(priv, &attrs.switch_id);
+#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
+ if (ena) {
+ err = sw_nb_register(netdev);
+ if (err)
+ return err;
+ }
+#endif
+
mutex_lock(&priv->mbox.lock);
req = otx2_mbox_alloc_msg_esw_cfg(&priv->mbox);
if (!req) {
mutex_unlock(&priv->mbox.lock);
+#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
+ if (ena)
+ sw_nb_unregister(netdev);
+#endif
return -ENOMEM;
}
req->ena = ena;
memcpy(req->switch_id, attrs.switch_id.id, attrs.switch_id.id_len);
- otx2_sync_mbox_msg(&priv->mbox);
+ mbox_err = otx2_sync_mbox_msg(&priv->mbox);
mutex_unlock(&priv->mbox.lock);
- return 0;
+
+#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
+ if (ena && mbox_err) {
+ sw_nb_unregister(netdev);
+ return mbox_err;
+ }
+
+ if (!ena) {
+ err = sw_nb_unregister(netdev);
+ if (err && !mbox_err)
+ return err;
+ }
+#endif
+
+ return mbox_err;
}
static netdev_tx_t rvu_rep_xmit(struct sk_buff *skb, struct net_device *dev)
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c
index 243611835e3a..8a09876e8297 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.c
@@ -4,18 +4,516 @@
* Copyright (C) 2026 Marvell.
*
*/
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <net/switchdev.h>
+#include <net/netevent.h>
+#include <net/arp.h>
+#include <net/route.h>
+#include <linux/inetdevice.h>
+#include <net/addrconf.h>
+
+#include "../otx2_reg.h"
+#include "../otx2_common.h"
+#include "../otx2_struct.h"
+#include "../cn10k.h"
#include "sw_nb.h"
+#include "sw_fdb.h"
+#include "sw_fib.h"
+#include "sw_fl.h"
+#include "sw_nb_v4.h"
+#include "sw_nb_v6.h"
#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
-int sw_nb_unregister(void)
+/* PF netdev for netdev_* logging when notifier info has no device */
+static struct net_device *sw_nb_pf_netdev;
+/* Notifier registration is only toggled from rvu_eswitch_config(), which is
+ * reached exclusively via otx2_devlink_eswitch_mode_set() on the RVU
+ * representor devlink (otx2_rep_dev()). Devlink holds the per-instance
+ * devlink->lock for the full DEVLINK_CMD_ESWITCH_SET handler (pre_doit
+ * through post_doit), serializing register/unregister on that devlink.
+ * Regular netdev PFs return -EOPNOTSUPP from eswitch_mode_set and never
+ * invoke these helpers, so concurrent devlink changes on other PFs cannot
+ * race on this state.
+ */
+static bool sw_nb_registered;
+
+static const char *sw_nb_cmd2str[OTX2_CMD_MAX] = {
+ [OTX2_DEV_UP] = "OTX2_DEV_UP",
+ [OTX2_DEV_DOWN] = "OTX2_DEV_DOWN",
+ [OTX2_DEV_CHANGE] = "OTX2_DEV_CHANGE",
+ [OTX2_NEIGH_UPDATE] = "OTX2_NEIGH_UPDATE",
+ [OTX2_FIB_ENTRY_REPLACE] = "OTX2_FIB_ENTRY_REPLACE",
+ [OTX2_FIB_ENTRY_ADD] = "OTX2_FIB_ENTRY_ADD",
+ [OTX2_FIB_ENTRY_DEL] = "OTX2_FIB_ENTRY_DEL",
+ [OTX2_FIB_ENTRY_APPEND] = "OTX2_FIB_ENTRY_APPEND",
+};
+
+const char *sw_nb_get_cmd2str(int cmd)
+{
+ return sw_nb_cmd2str[cmd];
+}
+EXPORT_SYMBOL(sw_nb_get_cmd2str);
+
+bool sw_nb_is_cavium_dev(struct net_device *netdev)
+{
+ struct pci_dev *pdev;
+ struct device *dev;
+
+ dev = netdev->dev.parent;
+ if (!dev || dev->bus != &pci_bus_type)
+ return false;
+
+ pdev = to_pci_dev(dev);
+ if (pdev->vendor != PCI_VENDOR_ID_CAVIUM)
+ return false;
+
+ return true;
+}
+
+/* Resolve the Cavium PF netdev used to reach the switch AF for offload.
+ *
+ * For a bridge master netdev, any Cavium netdev enslaved to the bridge is
+ * sufficient: callers only need a PF netdev to obtain the switch AF mailbox
+ * context (pcifunc). Bridge-specific information is tagged separately in
+ * the offload entry (entry->bridge), so walking every lower netdev is not
+ * required here.
+ */
+struct net_device *sw_nb_resolve_pf_dev(struct net_device *dev)
{
+ struct net_device *pf_dev = dev;
+ struct list_head *iter;
+
+ rcu_read_lock();
+
+ if (netif_is_bridge_master(dev)) {
+ iter = &dev->adj_list.lower;
+ pf_dev = netdev_next_lower_dev_rcu(dev, &iter);
+ if (!pf_dev)
+ pf_dev = dev;
+ } else if (is_vlan_dev(dev)) {
+ pf_dev = vlan_dev_real_dev(dev);
+ }
+
+ rcu_read_unlock();
+
+ if (!sw_nb_is_cavium_dev(pf_dev))
+ return NULL;
+
+ return pf_dev;
+}
+
+static int sw_nb_check_slaves(struct net_device *dev,
+ struct netdev_nested_priv *priv)
+{
+ int *cnt;
+
+ if (!priv->flags)
+ return 0;
+
+ priv->flags &= sw_nb_is_cavium_dev(dev);
+ if (priv->flags) {
+ cnt = priv->data;
+ (*cnt)++;
+ }
+
return 0;
}
-int sw_nb_register(void)
+bool sw_nb_is_valid_dev(struct net_device *netdev)
+{
+ struct netdev_nested_priv priv;
+ struct net_device *br;
+ int cnt = 0;
+ bool valid;
+
+ priv.flags = true;
+ priv.data = &cnt;
+
+ rcu_read_lock();
+
+ if (netif_is_bridge_master(netdev) || is_vlan_dev(netdev)) {
+ netdev_walk_all_lower_dev_rcu(netdev, sw_nb_check_slaves, &priv);
+ valid = priv.flags && cnt;
+ rcu_read_unlock();
+ return valid;
+ }
+
+ if (netif_is_bridge_port(netdev)) {
+ br = netdev_master_upper_dev_get_rcu(netdev);
+ if (!br) {
+ rcu_read_unlock();
+ return false;
+ }
+ netdev_walk_all_lower_dev_rcu(br, sw_nb_check_slaves, &priv);
+ valid = priv.flags && cnt;
+ rcu_read_unlock();
+ return valid;
+ }
+
+ rcu_read_unlock();
+
+ return sw_nb_is_cavium_dev(netdev);
+}
+
+static int sw_nb_fdb_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+ struct switchdev_notifier_fdb_info *fdb_info = ptr;
+
+ if (!sw_nb_is_valid_dev(dev))
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ if (fdb_info->is_local)
+ break;
+ break;
+
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ if (fdb_info->is_local)
+ break;
+ break;
+
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block sw_nb_fdb = {
+ .notifier_call = sw_nb_fdb_event,
+};
+
+static void __maybe_unused
+sw_nb_fib_event_dump(unsigned long event, void *ptr)
+{
+ struct fib_entry_notifier_info *fen_info = ptr;
+ struct net_device *log_dev;
+ struct fib_nh *fib_nh;
+ struct fib_info *fi;
+ int i;
+
+ fi = fen_info->fi;
+ log_dev = (fi && fi->fib_nhs) ? fi->fib_nh->fib_nh_dev : sw_nb_pf_netdev;
+ if (log_dev)
+ netdev_info(log_dev, "%s: FIB event=%lu dst=%pI4 dstlen=%u type=%u\n",
+ __func__, event, (const __be32 *)&fen_info->dst,
+ fen_info->dst_len, fen_info->type);
+
+ if (!fi)
+ return;
+
+ fib_nh = fi->fib_nh;
+ for (i = 0; i < fi->fib_nhs; i++, fib_nh++) {
+ if (!fib_nh->fib_nh_dev)
+ continue;
+ netdev_info(fib_nh->fib_nh_dev,
+ "%s: dev=%s saddr=%pI4 gw=%pI4\n",
+ __func__, fib_nh->fib_nh_dev->name,
+ &fib_nh->nh_saddr, &fib_nh->fib_nh_gw4);
+ }
+}
+
+#define SWITCH_NB_FIB_EVENT_DUMP(...) \
+ sw_nb_fib_event_dump(__VA_ARGS__)
+
+int sw_nb_fib_event_to_otx2_event(int event, struct net_device *netdev)
+{
+ switch (event) {
+ case FIB_EVENT_ENTRY_REPLACE:
+ return OTX2_FIB_ENTRY_REPLACE;
+ case FIB_EVENT_ENTRY_ADD:
+ return OTX2_FIB_ENTRY_ADD;
+ case FIB_EVENT_ENTRY_DEL:
+ return OTX2_FIB_ENTRY_DEL;
+ default:
+ break;
+ }
+
+ netdev_err(netdev, "Wrong FIB event %d\n", event);
+ return -1;
+}
+
+static int sw_nb_fib_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct fib_notifier_info *info = ptr;
+
+ switch (event) {
+ case FIB_EVENT_ENTRY_REPLACE:
+ case FIB_EVENT_ENTRY_ADD:
+ case FIB_EVENT_ENTRY_DEL:
+ break;
+ default:
+ if (sw_nb_pf_netdev)
+ netdev_dbg(sw_nb_pf_netdev,
+ "%s: Won't process FIB event %lu\n",
+ __func__, event);
+ return NOTIFY_DONE;
+ }
+
+ switch (info->family) {
+ case AF_INET:
+ return sw_nb_v4_fib_event(nb, event, ptr);
+#if IS_ENABLED(CONFIG_IPV6)
+ case AF_INET6:
+ return sw_nb_v6_fib_event(nb, event, ptr);
+#endif
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block sw_nb_fib = {
+ .notifier_call = sw_nb_fib_event,
+};
+
+static int sw_nb_net_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct neighbour *n = ptr;
+
+ if (!sw_nb_is_valid_dev(n->dev))
+ return NOTIFY_DONE;
+
+ if (event != NETEVENT_NEIGH_UPDATE)
+ return NOTIFY_DONE;
+
+ switch (n->tbl->family) {
+ case AF_INET:
+ return sw_nb_net_v4_neigh_update(nb, event, ptr);
+#if IS_ENABLED(CONFIG_IPV6)
+ case AF_INET6:
+ return sw_nb_net_v6_neigh_update(nb, event, ptr);
+#endif
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block sw_nb_netevent = {
+ .notifier_call = sw_nb_net_event,
+
+};
+
+int sw_nb_inetaddr_event_to_otx2_event(int event, struct net_device *netdev)
+{
+ switch (event) {
+ case NETDEV_CHANGE:
+ return OTX2_DEV_CHANGE;
+ case NETDEV_UP:
+ return OTX2_DEV_UP;
+ case NETDEV_DOWN:
+ return OTX2_DEV_DOWN;
+ default:
+ break;
+ }
+ netdev_dbg(netdev, "%s: Wrong interaddr event %d\n",
+ __func__, event);
+ return -1;
+}
+
+static struct notifier_block sw_nb_v4_inetaddr = {
+ .notifier_call = sw_nb_v4_inetaddr_event,
+};
+
+#if IS_ENABLED(CONFIG_IPV6)
+static struct notifier_block sw_nb_v6_inetaddr = {
+ .notifier_call = sw_nb_v6_inetaddr_event,
+};
+#endif
+
+static int sw_nb_netdev_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct in_device *idev;
+ struct inet6_dev *i6dev;
+
+ if (event != NETDEV_CHANGE &&
+ event != NETDEV_UP &&
+ event != NETDEV_DOWN) {
+ return NOTIFY_DONE;
+ }
+
+ if (!sw_nb_is_valid_dev(dev))
+ return NOTIFY_DONE;
+
+ idev = __in_dev_get_rtnl(dev);
+ if (idev)
+ sw_nb_v4_netdev_event(unused, event, ptr);
+
+#if IS_ENABLED(CONFIG_IPV6)
+ i6dev = __in6_dev_get(dev);
+ if (i6dev)
+ sw_nb_v6_netdev_event(unused, event, ptr);
+#endif
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block sw_nb_netdev = {
+ .notifier_call = sw_nb_netdev_event,
+};
+
+int sw_nb_unregister(struct net_device *netdev)
+{
+ int err, ret = 0;
+
+ if (!sw_nb_registered)
+ return 0;
+
+ err = unregister_switchdev_notifier(&sw_nb_fdb);
+ if (err) {
+ netdev_err(netdev, "Failed to unregister switchdev nb\n");
+ ret = err;
+ }
+
+ err = unregister_fib_notifier(&init_net, &sw_nb_fib);
+ if (err) {
+ netdev_err(netdev, "Failed to unregister fib nb\n");
+ if (!ret)
+ ret = err;
+ }
+
+ err = unregister_netevent_notifier(&sw_nb_netevent);
+ if (err) {
+ netdev_err(netdev, "Failed to unregister netevent\n");
+ if (!ret)
+ ret = err;
+ }
+
+ err = unregister_inetaddr_notifier(&sw_nb_v4_inetaddr);
+ if (err) {
+ netdev_err(netdev, "Failed to unregister addr event\n");
+ if (!ret)
+ ret = err;
+ }
+
+#if IS_ENABLED(CONFIG_IPV6)
+ err = unregister_inet6addr_notifier(&sw_nb_v6_inetaddr);
+ if (err) {
+ netdev_err(netdev, "Failed to unregister addr event\n");
+ if (!ret)
+ ret = err;
+ }
+#endif
+
+ err = unregister_netdevice_notifier(&sw_nb_netdev);
+ if (err) {
+ netdev_err(netdev, "Failed to unregister netdev notifier\n");
+ if (!ret)
+ ret = err;
+ }
+
+ sw_fl_deinit();
+ sw_fib_deinit();
+ sw_fdb_deinit();
+
+ sw_nb_pf_netdev = NULL;
+ sw_nb_registered = false;
+
+ return ret;
+}
+EXPORT_SYMBOL(sw_nb_unregister);
+
+int sw_nb_register(struct net_device *netdev)
+{
+ int err;
+
+ if (sw_nb_registered)
+ return -EBUSY;
+
+ sw_nb_pf_netdev = netdev;
+
+ err = sw_fdb_init();
+ if (err)
+ goto err_clear;
+
+ err = sw_fib_init();
+ if (err)
+ goto err_fdb;
+
+ err = sw_fl_init();
+ if (err)
+ goto err_fib;
+
+ err = register_switchdev_notifier(&sw_nb_fdb);
+ if (err) {
+ netdev_err(netdev, "Failed to register switchdev nb\n");
+ goto err_helpers;
+ }
+
+ err = register_fib_notifier(&init_net, &sw_nb_fib, NULL, NULL);
+ if (err) {
+ netdev_err(netdev, "Failed to register fb notifier block\n");
+ goto err1;
+ }
+
+ err = register_netevent_notifier(&sw_nb_netevent);
+ if (err) {
+ netdev_err(netdev, "Failed to register netevent\n");
+ goto err2;
+ }
+
+#if IS_ENABLED(CONFIG_IPV6)
+ err = register_inet6addr_notifier(&sw_nb_v6_inetaddr);
+ if (err) {
+ netdev_err(netdev, "Failed to register addr event\n");
+ goto err3;
+ }
+#endif
+
+ err = register_inetaddr_notifier(&sw_nb_v4_inetaddr);
+ if (err) {
+ netdev_err(netdev, "Failed to register addr event\n");
+ goto err4;
+ }
+
+ err = register_netdevice_notifier(&sw_nb_netdev);
+ if (err) {
+ netdev_err(netdev, "Failed to register netdevice nb\n");
+ goto err5;
+ }
+
+ sw_nb_registered = true;
+
return 0;
+
+err5:
+ unregister_inetaddr_notifier(&sw_nb_v4_inetaddr);
+
+err4:
+#if IS_ENABLED(CONFIG_IPV6)
+ unregister_inet6addr_notifier(&sw_nb_v6_inetaddr);
+
+err3:
+#endif
+ unregister_netevent_notifier(&sw_nb_netevent);
+
+err2:
+ unregister_fib_notifier(&init_net, &sw_nb_fib);
+
+err1:
+ unregister_switchdev_notifier(&sw_nb_fdb);
+
+err_helpers:
+ sw_fl_deinit();
+err_fib:
+ sw_fib_deinit();
+err_fdb:
+ sw_fdb_deinit();
+err_clear:
+ sw_nb_pf_netdev = NULL;
+ return err;
}
+EXPORT_SYMBOL(sw_nb_register);
#endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h
index 73cc1e99b8ec..e995c0e6046b 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb.h
@@ -9,12 +9,41 @@
#include <linux/kconfig.h>
+struct net_device;
+struct otx2_nic;
+struct af2pf_fdb_refresh_req;
+struct msg_rsp;
+
#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
-int sw_nb_register(void);
-int sw_nb_unregister(void);
+enum {
+ OTX2_DEV_UP = 1,
+ OTX2_DEV_DOWN,
+ OTX2_DEV_CHANGE,
+ OTX2_NEIGH_UPDATE,
+ OTX2_FIB_ENTRY_REPLACE,
+ OTX2_FIB_ENTRY_ADD,
+ OTX2_FIB_ENTRY_DEL,
+ OTX2_FIB_ENTRY_APPEND,
+ OTX2_CMD_MAX,
+};
+
+int sw_nb_register(struct net_device *netdev);
+int sw_nb_unregister(struct net_device *netdev);
+bool sw_nb_is_valid_dev(struct net_device *netdev);
+struct net_device *sw_nb_resolve_pf_dev(struct net_device *dev);
+
+int otx2_mbox_up_handler_af2pf_fdb_refresh(struct otx2_nic *pf,
+ struct af2pf_fdb_refresh_req *req,
+ struct msg_rsp *rsp);
+
+bool sw_nb_is_cavium_dev(struct net_device *netdev);
+int sw_nb_fib_event_to_otx2_event(int event, struct net_device *netdev);
+int sw_nb_inetaddr_event_to_otx2_event(int event, struct net_device *netdev);
+
+const char *sw_nb_get_cmd2str(int cmd);
#else
-static inline int sw_nb_register(void) { return 0; }
-static inline int sw_nb_unregister(void) { return 0; }
+static inline int sw_nb_register(struct net_device *netdev) { return 0; }
+static inline int sw_nb_unregister(struct net_device *netdev) { return 0; }
#endif
#endif /* SW_NB_H_ */
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c
new file mode 100644
index 000000000000..c773fce1bc50
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <net/switchdev.h>
+#include <net/netevent.h>
+#include <net/arp.h>
+#include <net/route.h>
+#include <linux/inetdevice.h>
+
+#include "../otx2_reg.h"
+#include "../otx2_common.h"
+#include "../otx2_struct.h"
+#include "../cn10k.h"
+#include "sw_nb.h"
+#include "sw_fdb.h"
+#include "sw_fib.h"
+#include "sw_fl.h"
+#include "sw_nb_v4.h"
+
+#if IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
+
+int sw_nb_v4_netdev_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct netdev_hw_addr *dev_addr;
+ struct net_device *pf_dev;
+ struct in_device *idev;
+ struct in_ifaddr *ifa;
+ struct fib_entry *entry;
+ struct otx2_nic *pf;
+
+ idev = __in_dev_get_rtnl(dev);
+ if (!idev || !idev->ifa_list)
+ return NOTIFY_DONE;
+
+ /* Switch offload supports a single IPv4 address per interface for now. */
+ ifa = rtnl_dereference(idev->ifa_list);
+
+ entry = kcalloc(1, sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return NOTIFY_DONE;
+
+ entry->cmd = sw_nb_inetaddr_event_to_otx2_event(event, dev);
+ entry->dst = ifa->ifa_address;
+ entry->dst_len = 32;
+ entry->mac_valid = 1;
+ entry->host = 1;
+
+ pf_dev = sw_nb_resolve_pf_dev(dev);
+ if (!pf_dev) {
+ kfree(entry);
+ return NOTIFY_DONE;
+ }
+
+ if (netif_is_bridge_master(dev)) {
+ entry->bridge = 1;
+ } else if (is_vlan_dev(dev)) {
+ entry->vlan_valid = 1;
+ entry->vlan_tag = cpu_to_be16(vlan_dev_vlan_id(dev));
+ }
+
+ pf = netdev_priv(pf_dev);
+ entry->port_id = pf->pcifunc;
+
+ for_each_dev_addr(dev, dev_addr) {
+ ether_addr_copy(entry->mac, dev_addr->addr);
+ break;
+ }
+
+ netdev_dbg(dev, "%s: pushing netdev event from HOST interface address %pI4, %pM, dev=%s\n",
+ __func__, &entry->dst, entry->mac, dev->name);
+ kfree(entry);
+
+ return NOTIFY_DONE;
+}
+
+int sw_nb_v4_inetaddr_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
+ struct net_device *dev = ifa->ifa_dev->dev;
+ struct netdev_hw_addr *dev_addr;
+ struct net_device *pf_dev;
+ struct in_device *idev;
+ struct fib_entry *entry;
+ struct otx2_nic *pf;
+
+ if (event != NETDEV_CHANGE &&
+ event != NETDEV_UP &&
+ event != NETDEV_DOWN) {
+ return NOTIFY_DONE;
+ }
+
+ if (!sw_nb_is_valid_dev(dev))
+ return NOTIFY_DONE;
+
+ idev = __in_dev_get_rtnl(dev);
+ if (!idev || !idev->ifa_list)
+ return NOTIFY_DONE;
+
+ entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);
+ if (!entry)
+ return NOTIFY_DONE;
+
+ entry->cmd = sw_nb_inetaddr_event_to_otx2_event(event, dev);
+ entry->dst = ifa->ifa_address;
+ entry->dst_len = 32;
+ entry->mac_valid = 1;
+ entry->host = 1;
+
+ pf_dev = sw_nb_resolve_pf_dev(dev);
+ if (!pf_dev) {
+ kfree(entry);
+ return NOTIFY_DONE;
+ }
+
+ if (netif_is_bridge_master(dev)) {
+ entry->bridge = 1;
+ } else if (is_vlan_dev(dev)) {
+ entry->vlan_valid = 1;
+ entry->vlan_tag = cpu_to_be16(vlan_dev_vlan_id(dev));
+ }
+
+ pf = netdev_priv(pf_dev);
+ entry->port_id = pf->pcifunc;
+
+ for_each_dev_addr(dev, dev_addr) {
+ ether_addr_copy(entry->mac, dev_addr->addr);
+ break;
+ }
+
+ netdev_dbg(dev, "%s: pushing inetaddr event from HOST interface address %pI4, %pM, %s\n",
+ __func__, &entry->dst, entry->mac, dev->name);
+
+ kfree(entry);
+ return NOTIFY_DONE;
+}
+
+int sw_nb_v4_fib_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev, *pf_dev = NULL, *nh_pf_dev;
+ struct fib_entry_notifier_info *fen_info = ptr;
+ struct fib_entry *entries, *iter;
+ struct netdev_hw_addr *dev_addr;
+ struct neighbour *neigh;
+ struct fib_nh *fib_nh;
+ struct fib_info *fi;
+ struct otx2_nic *pf;
+ __be32 *haddr;
+ int hcnt = 0;
+ int cnt, i;
+
+ /* Process only UNICAST routes add or del */
+ if (fen_info->type != RTN_UNICAST)
+ return NOTIFY_DONE;
+
+ fi = fen_info->fi;
+ if (!fi)
+ return NOTIFY_DONE;
+
+ if (fi->fib_nh_is_v6) {
+ struct net_device *log_dev = (fi->fib_nhs > 0) ?
+ fi->fib_nh->fib_nh_dev : NULL;
+
+ if (log_dev)
+ netdev_dbg(log_dev, "%s: Received v6 notification\n",
+ __func__);
+ return NOTIFY_DONE;
+ }
+
+ entries = kcalloc(fi->fib_nhs, sizeof(*entries), GFP_ATOMIC);
+ if (!entries)
+ return NOTIFY_DONE;
+
+ haddr = kcalloc(fi->fib_nhs, sizeof(*haddr), GFP_ATOMIC);
+ if (!haddr) {
+ kfree(entries);
+ return NOTIFY_DONE;
+ }
+
+ iter = entries;
+ fib_nh = fi->fib_nh;
+ for (i = 0; i < fi->fib_nhs; i++, fib_nh++) {
+ dev = fib_nh->fib_nh_dev;
+
+ if (!dev)
+ continue;
+
+ if (dev->type != ARPHRD_ETHER)
+ continue;
+
+ if (!sw_nb_is_valid_dev(dev))
+ continue;
+
+ iter->cmd = sw_nb_fib_event_to_otx2_event(event, dev);
+ iter->dst = (__force __be32)fen_info->dst;
+ iter->dst_len = fen_info->dst_len;
+ iter->gw = fib_nh->fib_nh_gw4;
+
+ netdev_dbg(dev, "%s: FIB route Rule cmd=%llu dst=%pI4 dst_len=%u gw=%pI4\n",
+ __func__, iter->cmd, &iter->dst, iter->dst_len, &iter->gw);
+
+ nh_pf_dev = sw_nb_resolve_pf_dev(dev);
+ if (!nh_pf_dev) {
+ iter++;
+ continue;
+ }
+ pf_dev = nh_pf_dev;
+
+ if (netif_is_bridge_master(dev)) {
+ iter->bridge = 1;
+ } else if (is_vlan_dev(dev)) {
+ iter->vlan_valid = 1;
+ iter->vlan_tag = cpu_to_be16(vlan_dev_vlan_id(dev));
+ }
+
+ pf = netdev_priv(pf_dev);
+ iter->port_id = pf->pcifunc;
+
+ /* Point-to-point routes, including default routes with no
+ * gateway, are not supported for switch offload.
+ */
+ if (!fib_nh->fib_nh_gw4) {
+ if (iter->dst || iter->dst_len)
+ iter++;
+
+ continue;
+ }
+ iter->gw_valid = 1;
+
+ if (fib_nh->nh_saddr)
+ haddr[hcnt++] = fib_nh->nh_saddr;
+
+ rcu_read_lock();
+ neigh = ip_neigh_gw4(fib_nh->fib_nh_dev, fib_nh->fib_nh_gw4);
+ if (!neigh) {
+ rcu_read_unlock();
+ iter++;
+ continue;
+ }
+
+ if (is_valid_ether_addr(neigh->ha)) {
+ iter->mac_valid = 1;
+ neigh_ha_snapshot(iter->mac, neigh, fib_nh->fib_nh_dev);
+ }
+
+ iter++;
+ rcu_read_unlock();
+ }
+
+ cnt = iter - entries;
+ if (!cnt) {
+ kfree(entries);
+ kfree(haddr);
+ return NOTIFY_DONE;
+ }
+
+ if (pf_dev)
+ netdev_dbg(pf_dev, "pf_dev is %s cnt=%d\n", pf_dev->name, cnt);
+ kfree(entries);
+
+ if (!hcnt) {
+ kfree(haddr);
+ return NOTIFY_DONE;
+ }
+
+ if (!pf_dev) {
+ kfree(haddr);
+ return NOTIFY_DONE;
+ }
+
+ entries = kcalloc(hcnt, sizeof(*entries), GFP_ATOMIC);
+ if (!entries) {
+ kfree(haddr);
+ return NOTIFY_DONE;
+ }
+
+ iter = entries;
+
+ /* Host routes reuse pf_dev/pf from the last resolved Cavium netdev:
+ * pf_dev only identifies the switch AF mailbox context for switchdev
+ * programming; any previously resolved Cavium netdev is sufficient.
+ */
+ for (i = 0; i < hcnt; i++, iter++) {
+ iter->cmd = sw_nb_fib_event_to_otx2_event(event, pf_dev);
+ iter->dst = haddr[i];
+ iter->dst_len = 32;
+ iter->mac_valid = 1;
+ iter->host = 1;
+ iter->port_id = pf->pcifunc;
+
+ rcu_read_lock();
+ for_each_dev_addr(pf_dev, dev_addr) {
+ ether_addr_copy(iter->mac, dev_addr->addr);
+ break;
+ }
+ rcu_read_unlock();
+
+ netdev_dbg(pf_dev, "%s: FIB host Rule cmd=%llu dst=%pI4 dst_len=%u gw=%pI4 %s\n",
+ __func__, iter->cmd, &iter->dst, iter->dst_len, &iter->gw,
+ pf_dev->name);
+ }
+ kfree(entries);
+ kfree(haddr);
+ return NOTIFY_DONE;
+}
+
+int sw_nb_net_v4_neigh_update(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct net_device *pf_dev;
+ struct neighbour *n = ptr;
+ struct fib_entry *entry;
+ struct otx2_nic *pf;
+
+ if (n->tbl != &arp_tbl)
+ return NOTIFY_DONE;
+
+ entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);
+ if (!entry)
+ return NOTIFY_DONE;
+
+ entry->cmd = OTX2_NEIGH_UPDATE;
+ entry->dst = *(__be32 *)n->primary_key;
+ entry->dst_len = n->tbl->key_len * 8;
+ entry->mac_valid = 1;
+ entry->nud_state = n->nud_state;
+ neigh_ha_snapshot(entry->mac, n, n->dev);
+
+ pf_dev = sw_nb_resolve_pf_dev(n->dev);
+ if (!pf_dev) {
+ kfree(entry);
+ return NOTIFY_DONE;
+ }
+
+ if (netif_is_bridge_master(n->dev)) {
+ entry->bridge = 1;
+ } else if (is_vlan_dev(n->dev)) {
+ entry->vlan_valid = 1;
+ entry->vlan_tag = cpu_to_be16(vlan_dev_vlan_id(n->dev));
+ }
+
+ pf = netdev_priv(pf_dev);
+ entry->port_id = pf->pcifunc;
+
+ kfree(entry);
+ return NOTIFY_DONE;
+}
+
+#endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.h
new file mode 100644
index 000000000000..c6dbf4b93a9a
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v4.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#ifndef SW_NB_V4_H_
+#define SW_NB_V4_H_
+
+int sw_nb_v4_fib_event(struct notifier_block *nb,
+ unsigned long event, void *ptr);
+
+int sw_nb_net_v4_neigh_update(struct notifier_block *nb,
+ unsigned long event, void *ptr);
+
+int sw_nb_v4_inetaddr_event(struct notifier_block *nb,
+ unsigned long event, void *ptr);
+
+int sw_nb_v4_netdev_event(struct notifier_block *unused,
+ unsigned long event, void *ptr);
+#endif // SW_NB_V4_H__
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c
new file mode 100644
index 000000000000..62ab00658879
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <net/switchdev.h>
+#include <net/netevent.h>
+#include <net/arp.h>
+#include <net/route.h>
+#include <linux/inetdevice.h>
+#include <net/addrconf.h>
+#include <net/ip6_fib.h>
+#include <net/nexthop.h>
+
+#include "../otx2_reg.h"
+#include "../otx2_common.h"
+#include "../otx2_struct.h"
+#include "../cn10k.h"
+#include "sw_nb.h"
+#include "sw_fdb.h"
+#include "sw_fib.h"
+#include "sw_fl.h"
+#include "sw_nb_v6.h"
+
+#if IS_ENABLED(CONFIG_IPV6) && IS_ENABLED(CONFIG_OCTEONTX_SWITCH)
+
+int sw_nb_v6_netdev_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct netdev_hw_addr *dev_addr;
+ struct net_device *pf_dev;
+ struct inet6_ifaddr *ifp;
+ struct inet6_dev *i6dev;
+ struct fib_entry *entry;
+ struct in6_addr addr;
+ struct otx2_nic *pf;
+ u32 prefix_len;
+
+ i6dev = __in6_dev_get(dev);
+ if (!i6dev)
+ return NOTIFY_DONE;
+
+ /* Invoked from sw_nb_netdev_event() on NETDEV_UP/DOWN/CHANGE, which
+ * run with RTNL held. IPv6 address list updates are also serialized
+ * by RTNL, so addr_list cannot race with concurrent assignments.
+ */
+ rcu_read_lock();
+ /* Switch offload supports a single IPv6 address per interface for now. */
+ ifp = list_first_entry_or_null(&i6dev->addr_list,
+ struct inet6_ifaddr, if_list);
+ if (!ifp) {
+ rcu_read_unlock();
+ return NOTIFY_DONE;
+ }
+
+ if (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL) {
+ rcu_read_unlock();
+ return NOTIFY_DONE;
+ }
+
+ addr = ifp->addr;
+ prefix_len = ifp->prefix_len;
+ rcu_read_unlock();
+
+ entry = kcalloc(1, sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return NOTIFY_DONE;
+
+ pf_dev = sw_nb_resolve_pf_dev(dev);
+ if (!pf_dev) {
+ kfree(entry);
+ return NOTIFY_DONE;
+ }
+
+ entry->cmd = sw_nb_inetaddr_event_to_otx2_event(event, dev);
+ memcpy(entry->dst6, &addr, sizeof(entry->dst6));
+ entry->dst6_plen = prefix_len;
+ entry->host = 1;
+ entry->ipv6 = 1;
+
+ pf = netdev_priv(pf_dev);
+ entry->port_id = pf->pcifunc;
+
+ for_each_dev_addr(dev, dev_addr) {
+ entry->mac_valid = 1;
+ ether_addr_copy(entry->mac, dev_addr->addr);
+ break;
+ }
+
+ netdev_dbg(dev, "netdev event addr=%pI6c plen=%u mac=%pM\n",
+ &addr, prefix_len, entry->mac);
+ kfree(entry);
+ return NOTIFY_DONE;
+}
+
+int sw_nb_v6_fib_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct fib6_entry_notifier_info *f6_eni;
+ struct fib_notifier_info *info = ptr;
+ struct net_device *fib_dev, *pf_dev;
+ struct fib_entry *entry;
+ struct fib6_info *f6i;
+ struct neighbour *neigh;
+ struct fib6_nh *nh6;
+ struct rt6key *key;
+ struct otx2_nic *pf;
+
+ f6_eni = container_of(info, struct fib6_entry_notifier_info, info);
+ f6i = f6_eni->rt;
+
+ fib_dev = fib6_info_nh_dev(f6i);
+
+ if (!fib_dev)
+ return NOTIFY_DONE;
+
+ if (fib_dev->type != ARPHRD_ETHER)
+ return NOTIFY_DONE;
+
+ if (!sw_nb_is_valid_dev(fib_dev))
+ return NOTIFY_DONE;
+
+ if (f6i->fib6_type != RTN_UNICAST)
+ return NOTIFY_DONE;
+
+ key = &f6i->fib6_dst;
+ /* TODO: vlan and bridge support */
+ if (ipv6_addr_type(&key->addr) & IPV6_ADDR_LINKLOCAL)
+ return NOTIFY_DONE;
+
+ netdev_dbg(fib_dev, "fib6dst rt6key.addr=%pI6c len=%u\n", &key->addr,
+ key->plen);
+
+ netdev_dbg(fib_dev, "fib6flags=%#x proto=%u type=%u\n",
+ f6i->fib6_flags, f6i->fib6_protocol, f6i->fib6_type);
+
+ nh6 = f6i->nh ? nexthop_fib6_nh(f6i->nh) : f6i->fib6_nh;
+ netdev_dbg(nh6->fib_nh_dev ? nh6->fib_nh_dev : fib_dev,
+ "nh family=%u dev=%s gw=%pI6c gwfamily=%u\n",
+ nh6->fib_nh_family,
+ nh6->fib_nh_dev ? nh6->fib_nh_dev->name : "No dev",
+ &nh6->fib_nh_gw6, nh6->fib_nh_gw_family);
+
+ pf_dev = sw_nb_resolve_pf_dev(fib_dev);
+ if (!pf_dev)
+ return NOTIFY_DONE;
+
+ pf = netdev_priv(pf_dev);
+
+ entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);
+ if (!entry)
+ return NOTIFY_DONE;
+
+ entry->cmd = sw_nb_fib_event_to_otx2_event(event, fib_dev);
+ entry->ipv6 = 1;
+ entry->port_id = pf->pcifunc;
+ memcpy(entry->dst6, &key->addr, sizeof(entry->dst6));
+ entry->dst6_plen = key->plen;
+
+ memcpy(entry->gw6, &nh6->fib_nh_gw6, sizeof(nh6->fib_nh_gw6));
+ entry->gw_valid = !!(ipv6_addr_type(&nh6->fib_nh_gw6) & IPV6_ADDR_UNICAST);
+
+ /* TODO: No replay mechanism yet when the gateway neighbor is unresolved.
+ * If ip_neigh_gw6() returns NULL the route is skipped here; add replay
+ * from the neighbor update handler once nexthop resolution completes.
+ */
+ rcu_read_lock();
+ neigh = ip_neigh_gw6(fib_dev, &nh6->fib_nh_gw6);
+ if (!neigh) {
+ rcu_read_unlock();
+ kfree(entry);
+ return NOTIFY_DONE;
+ }
+
+ if (is_valid_ether_addr(neigh->ha)) {
+ entry->mac_valid = 1;
+ neigh_ha_snapshot(entry->mac, neigh, fib_dev);
+ netdev_dbg(fib_dev, "fib found MAC=%pM\n", entry->mac);
+ }
+
+ rcu_read_unlock();
+ kfree(entry);
+
+ return NOTIFY_DONE;
+}
+
+int sw_nb_net_v6_neigh_update(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct net_device *pf_dev;
+ struct neighbour *n = ptr;
+ struct fib_entry *entry;
+ struct otx2_nic *pf;
+
+ if (n->tbl != &nd_tbl)
+ return NOTIFY_DONE;
+
+ if (ipv6_addr_type((struct in6_addr *)n->primary_key) & IPV6_ADDR_LINKLOCAL)
+ return NOTIFY_DONE;
+
+ entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);
+ if (!entry)
+ return NOTIFY_DONE;
+
+ pf_dev = sw_nb_resolve_pf_dev(n->dev);
+ if (!pf_dev) {
+ kfree(entry);
+ return NOTIFY_DONE;
+ }
+
+ pf = netdev_priv(pf_dev);
+
+ entry->cmd = OTX2_NEIGH_UPDATE;
+ entry->dst6_plen = n->tbl->key_len * 8;
+ memcpy(entry->dst6, (struct in6_addr *)n->primary_key,
+ sizeof(entry->dst6));
+ entry->ipv6 = 1;
+ entry->nud_state = n->nud_state;
+ neigh_ha_snapshot(entry->mac, n, n->dev);
+ entry->mac_valid = 1;
+ entry->port_id = pf->pcifunc;
+
+ netdev_dbg(n->dev, "v6 neigh update %pI6c mac=%pM plen=%u\n",
+ n->primary_key, entry->mac, n->tbl->key_len * 8);
+ kfree(entry);
+
+ return NOTIFY_DONE;
+}
+
+int sw_nb_v6_inetaddr_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct inet6_ifaddr *ifa6 = (struct inet6_ifaddr *)ptr;
+ struct net_device *dev = ifa6->idev->dev;
+ struct netdev_hw_addr *dev_addr;
+ struct net_device *pf_dev;
+ struct fib_entry *entry;
+ struct otx2_nic *pf;
+
+ if (event != NETDEV_CHANGE &&
+ event != NETDEV_UP &&
+ event != NETDEV_DOWN) {
+ return NOTIFY_DONE;
+ }
+
+ if (dev->type != ARPHRD_ETHER)
+ return NOTIFY_DONE;
+
+ if (!sw_nb_is_valid_dev(dev))
+ return NOTIFY_DONE;
+
+ if (ipv6_addr_type(&ifa6->addr) & IPV6_ADDR_LINKLOCAL)
+ return NOTIFY_DONE;
+
+ entry = kcalloc(1, sizeof(*entry), GFP_ATOMIC);
+ if (!entry)
+ return NOTIFY_DONE;
+
+ pf_dev = sw_nb_resolve_pf_dev(dev);
+ if (!pf_dev) {
+ kfree(entry);
+ return NOTIFY_DONE;
+ }
+
+ pf = netdev_priv(pf_dev);
+
+ entry->cmd = sw_nb_inetaddr_event_to_otx2_event(event, dev);
+ memcpy(entry->dst6, &ifa6->addr, sizeof(entry->dst6));
+ entry->dst6_plen = ifa6->prefix_len;
+ entry->mac_valid = 1;
+ entry->host = 1;
+ entry->ipv6 = 1;
+ entry->port_id = pf->pcifunc;
+
+ for_each_dev_addr(dev, dev_addr) {
+ ether_addr_copy(entry->mac, dev_addr->addr);
+ entry->mac_valid = 1;
+ break;
+ }
+
+ netdev_dbg(dev, "inetaddr addr=%pI6c len=%u %pM\n",
+ &ifa6->addr, ifa6->prefix_len, entry->mac);
+ kfree(entry);
+
+ return NOTIFY_DONE;
+}
+#endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h
new file mode 100644
index 000000000000..f73efc98c311
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/switch/sw_nb_v6.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell switch driver
+ *
+ * Copyright (C) 2026 Marvell.
+ *
+ */
+#ifndef SW_NB_V6_H_
+#define SW_NB_V6_H_
+
+int sw_nb_v6_fib_event(struct notifier_block *nb,
+ unsigned long event, void *ptr);
+
+int sw_nb_net_v6_neigh_update(struct notifier_block *nb,
+ unsigned long event, void *ptr);
+
+int sw_nb_v6_inetaddr_event(struct notifier_block *nb,
+ unsigned long event, void *ptr);
+
+int sw_nb_v6_netdev_event(struct notifier_block *unused,
+ unsigned long event, void *ptr);
+#endif // SW_NB_V6_H__
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox