* Re: [PATCH v10 net-next 2/5] psp: add new netlink cmd for dev-assoc and dev-disassoc
From: Wei Wang @ 2026-04-07 23:36 UTC (permalink / raw)
To: Daniel Zahka
Cc: netdev, Jakub Kicinski, Willem de Bruijn, David Wei, Andrew Lunn,
David S . Miller, Eric Dumazet, Simon Horman, Wei Wang
In-Reply-To: <2758ac3a-50dc-451a-990a-93e4db9d4bd6@gmail.com>
On Mon, Apr 6, 2026 at 6:27 AM Daniel Zahka <daniel.zahka@gmail.com> wrote:
>
>
> On 4/5/26 1:58 AM, Wei Wang wrote:
> > From: Wei Wang <weibunny@fb.com>
> >
> > The main purpose of this cmd is to be able to associate a
> > non-psp-capable device (e.g. veth or netkit) with a psp device.
> > One use case is if we create a pair of veth/netkit, and assign 1 end
> > inside a netns, while leaving the other end within the default netns,
> > with a real PSP device, e.g. netdevsim or a physical PSP-capable NIC.
> > With this command, we could associate the veth/netkit inside the netns
> > with PSP device, so the virtual device could act as PSP-capable device
> > to initiate PSP connections, and performs PSP encryption/decryption on
> > the real PSP device.
> >
> > Signed-off-by: Wei Wang <weibunny@fb.com>
> > ---
> > Documentation/netlink/specs/psp.yaml | 67 +++++-
> > include/net/psp/types.h | 15 ++
> > include/uapi/linux/psp.h | 13 ++
> > net/psp/psp-nl-gen.c | 32 +++
> > net/psp/psp-nl-gen.h | 2 +
> > net/psp/psp_main.c | 20 ++
> > net/psp/psp_nl.c | 319 ++++++++++++++++++++++++++-
> > 7 files changed, 457 insertions(+), 11 deletions(-)
> >
> ...
> >
> > +/**
> > + * Admin version of psp_device_get_locked() where it returns psd only if
> > + * current netns is the same as psd->main_netdev's netns.
> > + */
> > int psp_device_get_locked_admin(const struct genl_split_ops *ops,
> > struct sk_buff *skb, struct genl_info *info)
> > {
> > return __psp_device_get_locked(ops, skb, info, true);
> > }
> >
> > +/**
> > + * Non-admin version of psp_device_get_locked() where it returns psd in netns
> > + * for not only psd->main_netdev but all netdevs in psd->assoc_dev_list.
> > + */
> > int psp_device_get_locked(const struct genl_split_ops *ops,
> > struct sk_buff *skb, struct genl_info *info)
> > {
> > @@ -103,11 +179,74 @@ psp_device_unlock(const struct genl_split_ops *ops, struct sk_buff *skb,
> > sockfd_put(socket);
> > }
>
>
> There's a warning that these comments have the kdoc open sequence, but
> are not proper kdoc comments.
>
> > +
> > static int
> > psp_nl_dev_fill(struct psp_dev *psd, struct sk_buff *rsp,
> > const struct genl_info *info)
> > {
> > + struct net *cur_net;
> > void *hdr;
> > + int err;
> > +
> > + cur_net = genl_info_net(info);
> > +
> > + /* Skip this device if we're in an associated netns but have no
> > + * associated devices in cur_net
> > + */
> > + if (cur_net != dev_net(psd->main_netdev) &&
> > + !psp_has_assoc_dev_in_ns(psd, cur_net))
> > + return 0;
> >
>
>
> Is this branch dead code given we either arrived here via
> psp_dev_check_access(), or psp_nl_build_dev_ntf() which should only use
> associated netns's?
>
Yes. But should we keep this check to prevent future usage of this
function to misbehave?
> >
> > +int psp_nl_dev_assoc_doit(struct sk_buff *skb, struct genl_info *info)
> > +{
> > + struct psp_dev *psd = info->user_ptr[0];
> > + struct psp_assoc_dev *psp_assoc_dev;
> > + struct net_device *assoc_dev;
> > + struct sk_buff *rsp;
> > + u32 assoc_ifindex;
> > + struct net *net;
> > + int nsid;
> > +
> > + if (GENL_REQ_ATTR_CHECK(info, PSP_A_DEV_IFINDEX))
> > + return -EINVAL;
> > +
> > + if (info->attrs[PSP_A_DEV_NSID]) {
> > + nsid = nla_get_s32(info->attrs[PSP_A_DEV_NSID]);
> > +
> > + net = get_net_ns_by_id(genl_info_net(info), nsid);
> > + if (!net) {
> > + NL_SET_BAD_ATTR(info->extack,
> > + info->attrs[PSP_A_DEV_NSID]);
> > + return -EINVAL;
> > + }
> > + } else {
> > + net = get_net(genl_info_net(info));
> > + }
> > +
> > + psp_assoc_dev = kzalloc(sizeof(*psp_assoc_dev), GFP_KERNEL);
> > + if (!psp_assoc_dev) {
> > + put_net(net);
> > + return -ENOMEM;
> > + }
> > +
> > + assoc_ifindex = nla_get_u32(info->attrs[PSP_A_DEV_IFINDEX]);
> > + assoc_dev = netdev_get_by_index(net, assoc_ifindex,
> > + &psp_assoc_dev->dev_tracker,
> > + GFP_KERNEL);
> > + if (!assoc_dev) {
> > + put_net(net);
> > + kfree(psp_assoc_dev);
> > + NL_SET_BAD_ATTR(info->extack, info->attrs[PSP_A_DEV_IFINDEX]);
> > + return -ENODEV;
> > + }
> > +
> > + /* Check if device is already associated with a PSP device */
> > + if (cmpxchg(&assoc_dev->psp_dev, NULL, RCU_INITIALIZER(psd))) {
> > + NL_SET_ERR_MSG(info->extack,
> > + "Device already associated with a PSP device");
> > + netdev_put(assoc_dev, &psp_assoc_dev->dev_tracker);
> > + put_net(net);
> > + kfree(psp_assoc_dev);
> > + return -EBUSY;
> > + }
> > +
> > + psp_assoc_dev->assoc_dev = assoc_dev;
> > + rsp = psp_nl_reply_new(info);
> > + if (!rsp) {
> > + rcu_assign_pointer(assoc_dev->psp_dev, NULL);
> > + netdev_put(assoc_dev, &psp_assoc_dev->dev_tracker);
> > + put_net(net);
> > + kfree(psp_assoc_dev);
> > + return -ENOMEM;
> > + }
> > +
> > + list_add_tail(&psp_assoc_dev->dev_list, &psd->assoc_dev_list);
> > +
> > + put_net(net);
> > +
> > + psp_nl_notify_dev(psd, PSP_CMD_DEV_CHANGE_NTF);
> > +
> > + return psp_nl_reply_send(rsp, info);
> > +}
> > +
>
>
> This function could probably benefit from a goto style cleanup chain,
> given the overlapping set of actions to unwind at each error.
>
Ack. Updating in the new version.
> >
> > int psp_assoc_device_get_locked(const struct genl_split_ops *ops,
> > @@ -320,7 +617,9 @@ int psp_assoc_device_get_locked(const struct genl_split_ops *ops,
> >
> > psd = psp_dev_get_for_sock(socket->sk);
> > if (psd) {
> > + mutex_lock(&psd->lock);
> > err = psp_dev_check_access(psd, genl_info_net(info), false);
> > + mutex_unlock(&psd->lock);
>
>
> This looks like a "TOCTOU" issue on the mutable assoc_dev_list, but I
> think it ends up being a benign race.
Yea. Will address in the next version.
>
>
> > if (err) {
> > psp_dev_put(psd);
> > psd = NULL;
>
>
> Some minor comments, but otherwise:
>
> Reviewed-by: Daniel Zahka <daniel.zahka@gmail.com>
>
>
^ permalink raw reply
* Re: [PATCH v9 02/10] x86/bhi: Make clear_bhb_loop() effective on newer CPUs
From: Jim Mattson @ 2026-04-07 23:27 UTC (permalink / raw)
To: Pawan Gupta
Cc: x86, Jon Kohler, Nikolay Borisov, H. Peter Anvin, Josh Poimboeuf,
David Kaplan, Sean Christopherson, Borislav Petkov, Dave Hansen,
Peter Zijlstra, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, KP Singh, Jiri Olsa, David S. Miller,
David Laight, Andy Lutomirski, Thomas Gleixner, Ingo Molnar,
David Ahern, Martin KaFai Lau, Eduard Zingerman, Song Liu,
Yonghong Song, John Fastabend, Stanislav Fomichev, Hao Luo,
Paolo Bonzini, Jonathan Corbet, linux-kernel, kvm, Asit Mallick,
Tao Zhang, bpf, netdev, linux-doc, chao.gao
In-Reply-To: <20260407222738.lrartp6evfp7yhti@desk>
On Tue, Apr 7, 2026 at 3:27 PM Pawan Gupta
<pawan.kumar.gupta@linux.intel.com> wrote:
>
> On Tue, Apr 07, 2026 at 01:53:24PM -0700, Jim Mattson wrote:
> > On Tue, Apr 7, 2026 at 12:11 PM Pawan Gupta
> > <pawan.kumar.gupta@linux.intel.com> wrote:
> > >
> > > On Tue, Apr 07, 2026 at 11:40:57AM -0700, Jim Mattson wrote:
> > > > My proposal is as follows:
> > > >
> > > > 1. The (advanced) hypervisor can advertise to the guest (via CPUID bit
> > > > or MSR bit) that the short BHB clearing sequence is adequate. This may
> > > > mean either that the VM will only be hosted on pre-Alder Lake hardware
> > > > or that the hypervisor will set BHI_DIS_S behind the back of the
> > > > guest. Presumably, this bit would not be reported if BHI_CTRL is
> > > > advertised to the guest.
> > > > 2. If the guest sees this bit, then it can use the short sequence. If
> > > > it doesn't see this bit, it must use the long sequence.
> > >
> > > Thats a good middle ground. Let me check with folks internally what they
> > > think about defining a new software-only bit.
> > >
> > > Third case, for a guest that doesn't want BHI_DIS_S, userspace should be
> > > allowed to override setting BHI_DIS_S. Then this proposed bit can indicate
> > > that long sequence is required.
> >
> > That case can be handled by the paravirtual mitigation MSRs, right?
>
> Yes. But, that was the part that received the most pushback.
What is your proposed BHI_DIS_S override mechanism, then?
^ permalink raw reply
* Re: [net-next v9 07/10] net: bnxt: Implement software USO
From: Joe Damato @ 2026-04-07 23:23 UTC (permalink / raw)
To: netdev, Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: horms, linux-kernel, leon
In-Reply-To: <20260407220313.3990909-8-joe@dama.to>
On Tue, Apr 07, 2026 at 03:03:03PM -0700, Joe Damato wrote:
[...]
> v9:
> - Added inline slot check to prevent possible overwriting of in-flight
> headers (suggested by AI).
[...]
> netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,
> struct bnxt_tx_ring_info *txr,
> struct netdev_queue *txq,
> struct sk_buff *skb)
> {
[...]
> +
> + /* BD backpressure alone cannot prevent overwriting in-flight
> + * headers in the inline buffer. Check slot availability directly.
> + */
> + slots = txr->tx_inline_prod - txr->tx_inline_cons;
> + slots = BNXT_SW_USO_MAX_SEGS - slots;
> +
> + if (unlikely(slots < num_segs)) {
> + netif_txq_try_stop(txq, slots, num_segs);
> + return NETDEV_TX_BUSY;
This is the check I added. AI says this is wrong and netdev_queues.h says:
* @get_desc must be a formula or a function call, it must always
* return up-to-date information when evaluated!
which I obviously failed to do, so I'm pretty sure I got this wrong.
^ permalink raw reply
* Re: [PATCH net-next v5 00/10] Decouple receive and transmit enablement in team driver
From: Marc Harvey @ 2026-04-07 23:06 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Jiri Pirko, Andrew Lunn, David S. Miller, Eric Dumazet,
Paolo Abeni, Shuah Khan, Simon Horman, netdev, linux-kernel,
linux-kselftest
In-Reply-To: <CANkEMg=DOiwj-ZbTLGC3oXaQuPM=sGOjg09CTXNpguCL5dh7Ug@mail.gmail.com>
On Mon, Apr 6, 2026 at 10:04 PM Marc Harvey <marcharvey@google.com> wrote:
>
> On Mon, Apr 6, 2026 at 7:44 AM Jakub Kicinski <kuba@kernel.org> wrote:
> >
> > On Mon, 06 Apr 2026 03:03:36 +0000 Marc Harvey wrote:
> > > Allow independent control over receive and transmit enablement states
> > > for aggregated ports in the team driver.
> > >
> > > The motivation is that IEE 802.3ad LACP "independent control" can't
> > > be implemented for the team driver currently. This was added to the
> > > bonding driver in commit 240fd405528b ("bonding: Add independent
> > > control state machine").
> > >
> > > This series also has a few patches that add tests to show that the old
> > > coupled enablement still works and that the new decoupled enablement
> > > works as intended (4, 5, and 10).
> > >
> > > There are three patches with small fixes as well, with the goal of
> > > making the final decoupling patch clearer (1, 2, and 3).
> >
> > activebackup:
> >
> > TAP version 13
> > 1..1
> > # overriding timeout to 2400
> > # selftests: drivers/net/team: teamd_activebackup.sh
> > # Setting up two-link aggregation for runner activebackup
> > # Teamd version is: teamd 1.32
> > # Conf files are /tmp/tmp.ydjNK9Um7H and /tmp/tmp.xZuc3cWbN0
> > # This program is not intended to be run as root.
> > # This program is not intended to be run as root.
> > # Created team devices
> > # Teamd PIDs are 21457 and 21461
> > # exec of "ip link set eth0 up" failed: No such file or directory
> > # exec of "ip link set eth0 up" failed: No such file or directory
> > # exec of "ip link set eth1 up" failed: No such file or directory
> > # exec of "ip link set eth1 up" failed: No such file or directory
> > # PING fd00::2 (fd00::2) 56 data bytes
> > # 64 bytes from fd00::2: icmp_seq=1 ttl=64 time=0.753 ms
> > #
> > # --- fd00::2 ping statistics ---
> > # 1 packets transmitted, 1 received, 0% packet loss, time 0ms
> > # rtt min/avg/max/mdev = 0.753/0.753/0.753/0.000 msPacket count for test_team2 was 0
> > # Waiting for eth0 in ns2-lZ0gqd to stop receiving
> > # Packet count for eth0 was 0Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # Waiting for eth1 in ns2-lZ0gqd to stop receiving
> > # Packet count for eth1 was 0Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # TEST: teamd active backup runner test [FAIL]
> > # Traffic did not reach team interface in NS2.
> > # Tearing down two-link aggregation
> > # Failed to kill daemon: Timer expired
> > # Failed to kill daemon: Timer expired
> > # Sending sigkill to teamd for test_team1
> > # rm: cannot remove '/var/run/teamd/test_team1.pid': No such file or directory
> > # rm: cannot remove '/var/run/teamd/test_team1.sock': No such file or directory
> > # Sending sigkill to teamd for test_team2
> > # rm: cannot remove '/var/run/teamd/test_team2.pid': No such file or directory
> > # rm: cannot remove '/var/run/teamd/test_team2.sock': No such file or directory
> > not ok 1 selftests: drivers/net/team: teamd_activebackup.sh # exit=1
> >
> >
> > transmit_failover:
> >
> > TAP version 13
> > 1..1
> > # overriding timeout to 2400
> > # selftests: drivers/net/team: transmit_failover.sh
> > # Error: ipv6: address not found.
> > # Setting team in ns2-yxjiUo to mode roundrobin
> > # Error: ipv6: address not found.
> > # Setting team in ns1-Jht6kA to mode broadcast
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # TEST: Failover of 'broadcast' test [FAIL]
> > # eth0 not transmitting when both links enabled
> > # Setting team in ns1-Jht6kA to mode roundrobin
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # TEST: Failover of 'roundrobin' test [FAIL]
> > # eth0 not transmitting when both links enabled
> > # Setting team in ns1-Jht6kA to mode random
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # Packet count for eth0 was 0
> > # Packet count for eth1 was 0
> > # TEST: Failover of 'random' test [FAIL]
> > # eth0 not transmitting when both links enabled
> > not ok 1 selftests: drivers/net/team: transmit_failover.sh # exit=1
> > --
> > pw-bot: cr
>
> Apologies for all of the test failures. Before sending this revision,
> I ran each test thousands of times and observed no failures, so I
> thought the flakiness would be resolved.
>
> No matter what I try, I can't recreate either issue on my end. I've
> tried building with the exact config from one of the test runs
> (https://netdev-ctrl.bots.linux.dev/logs/vmksft/bonding/results/590921/).
> I've tried stressing the VM according to
> https://github.com/linux-netdev/nipa/wiki/How-to-run-netdev-selftests-CI-style#reproducing-unstable-tests
> (this makes the tests time out, but I can still see traffic). I've
> tried using the netdev-testing/net-next-2026-04-06--09-00 kernel
> source. I've tried in nested and unnested virtual machines. I've also
> tried running multiple test instances in parallel, but nothing
> recreates the issues. The issues seem related to tcpdump, but without
> reproducing them, I can only guess. Any suggestions for running the
> tests exactly as the CI does would be greatly appreciated.
>
> - Marc
Thank you very much to kuniyu@google.com, who figured out how to
recreate the issue on Fedora. Fedora's /etc/services maps TCP port
1234 to the "search-agent" service (normal), which tcpdump then uses
to text-replace port numbers in its output. So the tests were looking
for ${ip_address}.1234, but tcpdump was spitting out
${ip_address}.search_agent. What is strange is that the test already
uses tcpdump's "-n" option: "Don't convert addresses (i.e., host
addresses, port numbers, etc.) to names."
It turns out that Fedora has a patched version of tcpdump that
separates the normal "-n" option into two options! "-n" handles host
addresses, and "-nn" handles port and protocol numbers. The tcpdump
invocation used by the selftests only uses "-n". What's stranger is
that passing "-nn" to tcpdump is actually portable, because under the
hood it is treated as a counter, with or without the Fedora patch:
https://github.com/the-tcpdump-group/tcpdump/blob/master/tcpdump.c#L1915
(thanks again to Kuniyuki for discovering this).
For v6, I will just change the TCP port to one that is not used by a
service, and will make the tcpdump helper function in the
net/forwarding lib use "-nn" instead of "-n".
- Marc
^ permalink raw reply
* Re: [PATCH v5 0/9] driver core: Fix some race conditions
From: Danilo Krummrich @ 2026-04-07 22:58 UTC (permalink / raw)
To: Douglas Anderson, m.szyprowski, Robin Murphy
Cc: Greg Kroah-Hartman, Rafael J . Wysocki, Alan Stern,
Alexey Kardashevskiy, Johan Hovold, Eric Dumazet, Leon Romanovsky,
Christoph Hellwig, maz, Alexander Lobakin, Saravana Kannan,
Andrew Morton, Frank.Li, Jason Gunthorpe, alex, alexander.stein,
andre.przywara, andrew, andrew, andriy.shevchenko, aou, ardb,
astewart, bhelgaas, brgl, broonie, catalin.marinas, chleroy,
davem, david, devicetree, dmaengine, driver-core, gbatra,
gregory.clement, hkallweit1, iommu, jirislaby, joel, joro, kees,
kevin.brodsky, kuba, lenb, lgirdwood, linux-acpi,
linux-arm-kernel, linux-aspeed, linux-cxl, linux-kernel,
linux-mips, linux-mm, linux-pci, linux-riscv, linux-serial,
linux-snps-arc, linux-usb, linux, linuxppc-dev, maddy, mani,
miko.lenczewski, mpe, netdev, npiggin, osalvador, oupton, pabeni,
palmer, peter.ujfalusi, peterz, pjw, robh, sebastian.hesselbarth,
tglx, tsbogend, vgupta, vkoul, will, willy, yangyicong,
yeoreum.yun
In-Reply-To: <20260406232444.3117516-1-dianders@chromium.org>
On Tue Apr 7, 2026 at 1:22 AM CEST, Douglas Anderson wrote:
Applied to driver-core-testing, thanks!
> Douglas Anderson (9):
> driver core: Don't let a device probe until it's ready
> driver core: Replace dev->can_match with dev_can_match()
> driver core: Replace dev->dma_iommu with dev_dma_iommu()
> driver core: Replace dev->dma_skip_sync with dev_dma_skip_sync()
> driver core: Replace dev->dma_ops_bypass with dev_dma_ops_bypass()
> driver core: Replace dev->state_synced with dev_state_synced()
> driver core: Replace dev->dma_coherent with dev_dma_coherent()
[ Since all DEV_FLAG_DMA_COHERENT accessors are exposed unconditionally,
also drop the CONFIG guards around dev_assign_dma_coherent() in
device_initialize() to ensure a correct default value. - Danilo ]
> driver core: Replace dev->of_node_reused with dev_of_node_reused()
> driver core: Replace dev->offline + ->offline_disabled with accessors
^ permalink raw reply
* Re: [PATCH net] ice: fix VF queue configuration with low MTU values
From: Jacob Keller @ 2026-04-07 22:50 UTC (permalink / raw)
To: Jose Ignacio Tornos Martinez, intel-wired-lan
Cc: netdev, Tony Nguyen, Przemek Kitszel, Andrew Lunn,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Aleksandr Loktionov, Michal Swiatkowski, Dave Ertman,
Michal Kubiak, stable
In-Reply-To: <20260406145641.1020623-1-jtornosm@redhat.com>
On 4/6/2026 7:56 AM, Jose Ignacio Tornos Martinez wrote:
> The ice driver's VF queue configuration validation rejects
> databuffer_size values below 1024 bytes, which prevents VFs from
> using MTU values below 871 bytes.
>
> The iavf driver calculates databuffer_size based on the MTU using:
> databuffer_size = ALIGN(MTU + LIBETH_RX_LL_LEN, 128)
>
> where LIBETH_RX_LL_LEN = 26 (ETH_HLEN + 2*VLAN_HLEN + ETH_FCS_LEN).
>
> For MTU values below 871:
> MTU 870: 870 + 26 = 896, aligned to 128 = 896 (< 1024, rejected)
> MTU 871: 871 + 26 = 897, aligned to 128 = 1024 (>= 1024, accepted)
>
> The 1024-byte minimum seems unnecessarily restrictive, because the hardware
> supports databuffer_size as low as 128 bytes (the alignment boundary),
> which should allow MTU values down to the standard minimum of 68 bytes.
>
> I haven't found the reason why the limit was configured in the commit
> 9c7dd7566d18 ("ice: add validation in OP_CONFIG_VSI_QUEUES VF message"), so
> with no more information and since it is working, change the minimum
> databuffer_size validation from 1024 to 128 bytes to allow standard low
> MTU values while still preventing invalid configurations.
>
I dug through some of our internal history and found that there was no
justification on why 1024 was chosen.
I agree with your assessment that the value of 128 makes the most sense
as it is the actual hardware minimum. I wonder if we used to always use
data buffer sizes of 1024 before the conversion to libeth. Either way, I
think it makes sense to allow smaller buffers since the modern iAVF will
request them.
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
> Fixes: 9c7dd7566d18 ("ice: add validation in OP_CONFIG_VSI_QUEUES VF message")
> cc: stable@vger.kernel.org
> Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
> ---
> drivers/net/ethernet/intel/ice/virt/queues.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/intel/ice/virt/queues.c b/drivers/net/ethernet/intel/ice/virt/queues.c
> index f73d5a3e83d4..31be2f76181c 100644
> --- a/drivers/net/ethernet/intel/ice/virt/queues.c
> +++ b/drivers/net/ethernet/intel/ice/virt/queues.c
> @@ -840,7 +840,7 @@ int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg)
>
> if (qpi->rxq.databuffer_size != 0 &&
> (qpi->rxq.databuffer_size > ((16 * 1024) - 128) ||
> - qpi->rxq.databuffer_size < 1024))
> + qpi->rxq.databuffer_size < 128))
> goto error_param;
>
> ring->rx_buf_len = qpi->rxq.databuffer_size;
^ permalink raw reply
* Re: [PATCH net-next v1] r8169: implement get_module functions for rtl8127atf
From: Fabio Baltieri @ 2026-04-07 22:44 UTC (permalink / raw)
To: Andrew Lunn
Cc: Heiner Kallweit, nic_swsd, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King - ARM Linux, netdev, linux-kernel
In-Reply-To: <368d3f30-b4af-4548-a4b9-f8e4e4f9a4cb@lunn.ch>
On Sun, Apr 05, 2026 at 12:46:41AM +0200, Andrew Lunn wrote:
> > Hi Fabio, thanks for the patch. Being able to access the SFP I2C bus is an
> > important step towards future phylink/SFP support.
> >
> > @Russell/@Andrew
> > I'm not really familiar with the phylink and sfp code. And I would like to have
> > the functionality being implemented here reusable once we do further steps
> > towards phylink support in r8169. So how shall the code be modeled, which
> > components are needed?
> > - Shall the SFP I2C bus be modeled as a struct i2c_adapter?
>
> Yes, that would be best. Call i2c_add_adapter() to add it to the I2C
> core. The SFP code in drivers/net/phy can then make use of it.
Hey Andrew, thanks for the pointers, I was able to instantiate the dw
i2c controller, same as txgbe/txgbe_phy.c really.
For some reasons I had to revert 560072246088, was getting a "spurious
STOP detected" but I'll follow up on that separately. Also find an issue
in the txgbe driver (I think?), sent
https://lore.kernel.org/netdev/20260405222013.5347-1-fabio.baltieri@gmail.com/
for it.
> > - Shall we (partially?) implement a struct sfp, so that functions like
> > sfp_module_eeprom() can be used (implicitly)?
> >
> > I assume this patch includes logic which duplicates what is available in
> > phylink/sfp already. I'd a appreciate any hints you can provide.
>
> No. phylink etc will end up populating netdev->sfp_bus, and then
> get_module_eeprom_by_page() should then just make the module work in
> ethtool.
>
> The interesting bit if gluing it all together, without DT. But
> txgbe_phy.c should be something you can copy from.
>
> Does the out of tree driver give access to GPIOs connected to the SFP
> cage pins? Ideally you want those as well, for loss of signal,
> transmitter disable, knowing when a module has been inserted into the
> cage, etc.
It doesn't, but I was able to find them anyway, just dumping registers
randomly. :-) It looks like that is a bit more than "Ideally", the sfp
driver probes happily with no gpios defined but then it doesn't have
any ways to detect module changes (insertion/removal), it doesn't even
go into polling mode but even then it seems like it doesn't do detection
without a detect pin. I think the sfp probe function could be a bit more
defensive to inoperable configurations.
Anyway I was able to make detection and los work, so I have some code
with gpio, i2c and the sfp module detect and showing up in hwmon, and
sitting in waitdev state.
>
> And you will need a PCS driver.
>
> But first step is probably to work with the existing Base-T devices
> and convert the driver to phylink.
Ok I was going to ask how to connect the one above with the ethtool
handlers, so you are saying that first the whole driver needs to be
converted to use phylink/pcs and then that should work automatically-ish
right?
Can I send the current code I have as an RFC in the meantime to get some
early feedback? And then I guess it'll have to be reworked on top of the
phylink stuff (which I did not try to implement).
It's a pretty substantial amount of boilerplate code so I suspect Heiner
may want to refactor things about it, feels like it could live in its
own file.
Thanks for the help so far,
Fabio
^ permalink raw reply
* Re: [PATCH v9 02/10] x86/bhi: Make clear_bhb_loop() effective on newer CPUs
From: Pawan Gupta @ 2026-04-07 22:27 UTC (permalink / raw)
To: Jim Mattson
Cc: x86, Jon Kohler, Nikolay Borisov, H. Peter Anvin, Josh Poimboeuf,
David Kaplan, Sean Christopherson, Borislav Petkov, Dave Hansen,
Peter Zijlstra, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, KP Singh, Jiri Olsa, David S. Miller,
David Laight, Andy Lutomirski, Thomas Gleixner, Ingo Molnar,
David Ahern, Martin KaFai Lau, Eduard Zingerman, Song Liu,
Yonghong Song, John Fastabend, Stanislav Fomichev, Hao Luo,
Paolo Bonzini, Jonathan Corbet, linux-kernel, kvm, Asit Mallick,
Tao Zhang, bpf, netdev, linux-doc, chao.gao
In-Reply-To: <CALMp9eTK0o7Z7-oTB8ohvmoh-vy-Y2qjdUvbqD6HaEhOEPZmhw@mail.gmail.com>
On Tue, Apr 07, 2026 at 01:53:24PM -0700, Jim Mattson wrote:
> On Tue, Apr 7, 2026 at 12:11 PM Pawan Gupta
> <pawan.kumar.gupta@linux.intel.com> wrote:
> >
> > On Tue, Apr 07, 2026 at 11:40:57AM -0700, Jim Mattson wrote:
> > > My proposal is as follows:
> > >
> > > 1. The (advanced) hypervisor can advertise to the guest (via CPUID bit
> > > or MSR bit) that the short BHB clearing sequence is adequate. This may
> > > mean either that the VM will only be hosted on pre-Alder Lake hardware
> > > or that the hypervisor will set BHI_DIS_S behind the back of the
> > > guest. Presumably, this bit would not be reported if BHI_CTRL is
> > > advertised to the guest.
> > > 2. If the guest sees this bit, then it can use the short sequence. If
> > > it doesn't see this bit, it must use the long sequence.
> >
> > Thats a good middle ground. Let me check with folks internally what they
> > think about defining a new software-only bit.
> >
> > Third case, for a guest that doesn't want BHI_DIS_S, userspace should be
> > allowed to override setting BHI_DIS_S. Then this proposed bit can indicate
> > that long sequence is required.
>
> That case can be handled by the paravirtual mitigation MSRs, right?
Yes. But, that was the part that received the most pushback.
^ permalink raw reply
* Re: [PATCH v3 11/15] media: qcom: Switch to generic PAS TZ APIs
From: Trilok Soni @ 2026-04-07 22:14 UTC (permalink / raw)
To: Sumit Garg, Jorge Ramirez, vikash.garodia
Cc: linux-arm-msm, devicetree, dri-devel, freedreno, linux-media,
netdev, linux-wireless, ath12k, linux-remoteproc, andersson,
konradybcio, robh, krzk+dt, conor+dt, robin.clark, sean, akhilpo,
lumag, abhinav.kumar, jesszhan0024, marijn.suijten, airlied,
simona, dikshita.agarwal, bod, mchehab, elder, andrew+netdev,
davem, edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
mukesh.ojha, pavan.kondeti, tonyh, vignesh.viswanathan,
srinivas.kandagatla, amirreza.zarrabi, jens.wiklander, op-tee,
apurupa, skare, harshal.dev, linux-kernel, Sumit Garg
In-Reply-To: <adOcMsk8a_Clb4WZ@sumit-xelite>
On 4/6/2026 4:42 AM, Sumit Garg wrote:
> Hi Jorge,
>
> On Fri, Apr 03, 2026 at 11:37:07AM +0200, Jorge Ramirez wrote:
>> On 27/03/26 18:40:39, Sumit Garg wrote:
>>> From: Sumit Garg <sumit.garg@oss.qualcomm.com>
>>>
>>> Switch qcom media client drivers over to generic PAS TZ APIs. Generic PAS
>>> TZ service allows to support multiple TZ implementation backends like QTEE
>>> based SCM PAS service, OP-TEE based PAS service and any further future TZ
>>> backend service.
>>
>> OP-TEE based PAS service relies on the linux driver to configure the
>> iommu (just as it is done on the no_tz case). This generic patch does
>> not cover that requirement.
>
> That's exactly the reason why the kodiak EL2 dtso disables venus by
> default in patch #1 due to missing IOMMU configuration.
>
>>
>> Because of that, it is probably better if the commit message doesnt
>> mention OP-TEE and instead maybe indicate that PAS wll support TEEs that
>> implement the same restrictions that QTEE (ie, iommu configuration).
>
> The scope for this patch is to just adopt the generic PAS layer without
> affecting the client functionality.
>
>>
>> I can send an RFC for OP-TEE support based on the integration work being
>> carried out here [1]
>
> @Vikash may know better details about support for IOMMU configuration
> for venus since it's a generic functionality missing when Linux runs in
> EL2 whether it's with QTEE or OP-TEE.
>
> However, feel free to propose your work to initiate discussions again.
Vikas and team depends on some of the IOMMU patches to get accepted
before they enable the EL2 venus support. Please reach out to him
and Prakash Gupta at Qualcomm.
>
>>
>> [1] https://github.com/OP-TEE/optee_os/pull/7721#discussion_r3016923507
>
> -Sumit
^ permalink raw reply
* [net-next v9 10/10] selftests: drv-net: Add USO test
From: Joe Damato @ 2026-04-07 22:03 UTC (permalink / raw)
To: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Shuah Khan
Cc: horms, michael.chan, pavan.chebbi, linux-kernel, leon, Joe Damato,
linux-kselftest
In-Reply-To: <20260407220313.3990909-1-joe@dama.to>
Add a simple test for USO. Tests both ipv4 and ipv6 with several full
segments and a partial segment.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Joe Damato <joe@dama.to>
---
v9:
- Use UDP-LISTEN instead of UDP-RECV in socat receiver (suggested by AI).
- Fixed stale docstring.
- Removed unused return value.
v7:
- Dropped Pavan's Reviewed-by as there were changes.
- Update to use ksft_variants with a generator and a parameterized test_uso
function.
- Save original USO state and restore it at the end of the test.
- Replace sleep with cfg.wait_hw_stats_settle
- Use a socat receiver and check tx stats locally instead of rx on the
remote.
v5:
- Added Pavan's Reviewed-by. No functional changes.
v4:
- Fix python linter issues (unused imports, docstring, etc).
rfcv2:
- new in rfcv2
tools/testing/selftests/drivers/net/Makefile | 1 +
tools/testing/selftests/drivers/net/uso.py | 103 +++++++++++++++++++
2 files changed, 104 insertions(+)
create mode 100755 tools/testing/selftests/drivers/net/uso.py
diff --git a/tools/testing/selftests/drivers/net/Makefile b/tools/testing/selftests/drivers/net/Makefile
index 7c7fa75b80c2..335c2ce4b9ab 100644
--- a/tools/testing/selftests/drivers/net/Makefile
+++ b/tools/testing/selftests/drivers/net/Makefile
@@ -21,6 +21,7 @@ TEST_PROGS := \
ring_reconfig.py \
shaper.py \
stats.py \
+ uso.py \
xdp.py \
# end of TEST_PROGS
diff --git a/tools/testing/selftests/drivers/net/uso.py b/tools/testing/selftests/drivers/net/uso.py
new file mode 100755
index 000000000000..6d61e56cab3c
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/uso.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+"""Test USO
+
+Sends large UDP datagrams with UDP_SEGMENT and verifies that the peer
+receives the expected total payload and that the NIC transmitted at least
+the expected number of segments.
+"""
+import random
+import socket
+import string
+
+from lib.py import ksft_run, ksft_exit, KsftSkipEx
+from lib.py import ksft_eq, ksft_ge, ksft_variants, KsftNamedVariant
+from lib.py import NetDrvEpEnv
+from lib.py import bkg, defer, ethtool, ip, rand_port, wait_port_listen
+
+# python doesn't expose this constant, so we need to hardcode it to enable UDP
+# segmentation for large payloads
+UDP_SEGMENT = 103
+
+
+def _send_uso(cfg, ipver, mss, total_payload, port):
+ if ipver == "4":
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ dst = (cfg.remote_addr_v["4"], port)
+ else:
+ sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
+ dst = (cfg.remote_addr_v["6"], port)
+
+ sock.setsockopt(socket.IPPROTO_UDP, UDP_SEGMENT, mss)
+ payload = ''.join(random.choice(string.ascii_lowercase)
+ for _ in range(total_payload))
+ sock.sendto(payload.encode(), dst)
+ sock.close()
+
+
+def _get_tx_packets(cfg):
+ stats = ip(f"-s link show dev {cfg.ifname}", json=True)[0]
+ return stats['stats64']['tx']['packets']
+
+
+def _test_uso(cfg, ipver, mss, total_payload):
+ cfg.require_ipver(ipver)
+ cfg.require_cmd("socat", remote=True)
+
+ features = ethtool(f"-k {cfg.ifname}", json=True)
+ uso_was_on = features[0]["tx-udp-segmentation"]["active"]
+
+ try:
+ ethtool(f"-K {cfg.ifname} tx-udp-segmentation on")
+ except Exception as exc:
+ raise KsftSkipEx(
+ "Device does not support tx-udp-segmentation") from exc
+ if not uso_was_on:
+ defer(ethtool, f"-K {cfg.ifname} tx-udp-segmentation off")
+
+ expected_segs = (total_payload + mss - 1) // mss
+
+ port = rand_port(stype=socket.SOCK_DGRAM)
+ rx_cmd = f"socat -{ipver} -T 2 -u UDP-LISTEN:{port},reuseport STDOUT"
+
+ tx_before = _get_tx_packets(cfg)
+
+ with bkg(rx_cmd, host=cfg.remote, exit_wait=True) as rx:
+ wait_port_listen(port, proto="udp", host=cfg.remote)
+ _send_uso(cfg, ipver, mss, total_payload, port)
+
+ ksft_eq(len(rx.stdout), total_payload,
+ comment=f"Received {len(rx.stdout)}B, expected {total_payload}B")
+
+ cfg.wait_hw_stats_settle()
+
+ tx_after = _get_tx_packets(cfg)
+ tx_delta = tx_after - tx_before
+
+ ksft_ge(tx_delta, expected_segs,
+ comment=f"Expected >= {expected_segs} tx packets, got {tx_delta}")
+
+
+def _uso_variants():
+ for ipver in ["4", "6"]:
+ yield KsftNamedVariant(f"v{ipver}_partial", ipver, 1400, 1400 * 10 + 500)
+ yield KsftNamedVariant(f"v{ipver}_exact", ipver, 1400, 1400 * 5)
+
+
+@ksft_variants(_uso_variants())
+def test_uso(cfg, ipver, mss, total_payload):
+ """Send a USO datagram and verify the peer receives the expected segments."""
+ _test_uso(cfg, ipver, mss, total_payload)
+
+
+def main() -> None:
+ """Run USO tests."""
+ with NetDrvEpEnv(__file__) as cfg:
+ ksft_run([test_uso],
+ args=(cfg, ))
+ ksft_exit()
+
+
+if __name__ == "__main__":
+ main()
--
2.52.0
^ permalink raw reply related
* [net-next v9 09/10] net: bnxt: Dispatch to SW USO
From: Joe Damato @ 2026-04-07 22:03 UTC (permalink / raw)
To: netdev, Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: horms, linux-kernel, leon, Joe Damato
In-Reply-To: <20260407220313.3990909-1-joe@dama.to>
Wire in the SW USO path added in preceding commits when hardware USO is
not possible.
When a GSO skb with SKB_GSO_UDP_L4 arrives and the NIC lacks HW USO
capability, redirect to bnxt_sw_udp_gso_xmit() which handles software
segmentation into individual UDP frames submitted directly to the TX
ring.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
Signed-off-by: Joe Damato <joe@dama.to>
---
v5:
- Added Pavan's Reviewed-by. No functional changes.
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 73e692ae2253..68cad2951fe0 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -508,6 +508,11 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
}
#endif
+ if (skb_is_gso(skb) &&
+ (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) &&
+ !(bp->flags & BNXT_FLAG_UDP_GSO_CAP))
+ return bnxt_sw_udp_gso_xmit(bp, txr, txq, skb);
+
free_size = bnxt_tx_avail(bp, txr);
if (unlikely(free_size < skb_shinfo(skb)->nr_frags + 2)) {
/* We must have raced with NAPI cleanup */
--
2.52.0
^ permalink raw reply related
* [net-next v9 08/10] net: bnxt: Add SW GSO completion and teardown support
From: Joe Damato @ 2026-04-07 22:03 UTC (permalink / raw)
To: netdev, Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: horms, linux-kernel, leon, Joe Damato
In-Reply-To: <20260407220313.3990909-1-joe@dama.to>
Update __bnxt_tx_int and bnxt_free_one_tx_ring_skbs to handle SW GSO
segments:
- MID segments: adjust tx_pkts/tx_bytes accounting and skip skb free
(the skb is shared across all segments and freed only once)
- LAST segments: call tso_dma_map_complete() to tear down the IOVA
mapping if one was used. On the fallback path, payload DMA unmapping
is handled by the existing per-BD dma_unmap_len walk.
Both MID and LAST completions advance tx_inline_cons to release the
segment's inline header slot back to the ring.
is_sw_gso is initialized to zero, so the new code paths are not run.
Add logic for feature advertisement and guardrails for ring sizing.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Joe Damato <joe@dama.to>
---
v9:
- Always allocate header buffer for non-HW-USO NICs. Avoids a possible
NULL deref if USO is toggled off and the device is brought down, up,
and USO is re-enabled (suggested by AI).
- Adjust bnxt_min_tx_desc_cnt to take a feature parameter. This is needed
to prevent stale features from being examined (suggested by AI).
v7:
- Dropped Pavan's Reviewed-by because some changes were made.
- Added helper bnxt_min_tx_desc_cnt to avoid repeated code computing
descriptor counts.
- Updated to use tso_dma_map_complete helper instead of calling the DMA
IOVA API directly.
v5:
- Added Pavan's Reviewed-by. No functional changes.
v3:
- completion paths updated to use DMA IOVA APIs to teardown mappings.
rfcv2:
- Update the shared header buffer consumer on TX completion.
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 70 ++++++++++++++++---
.../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 19 ++++-
drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h | 9 +++
3 files changed, 87 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 74968ca1f4e2..73e692ae2253 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -74,6 +74,8 @@
#include "bnxt_debugfs.h"
#include "bnxt_coredump.h"
#include "bnxt_hwmon.h"
+#include "bnxt_gso.h"
+#include <net/tso.h>
#define BNXT_TX_TIMEOUT (5 * HZ)
#define BNXT_DEF_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_HW | \
@@ -817,12 +819,13 @@ static bool __bnxt_tx_int(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
bool rc = false;
while (RING_TX(bp, cons) != hw_cons) {
- struct bnxt_sw_tx_bd *tx_buf;
+ struct bnxt_sw_tx_bd *tx_buf, *head_buf;
struct sk_buff *skb;
bool is_ts_pkt;
int j, last;
tx_buf = &txr->tx_buf_ring[RING_TX(bp, cons)];
+ head_buf = tx_buf;
skb = tx_buf->skb;
if (unlikely(!skb)) {
@@ -869,6 +872,20 @@ static bool __bnxt_tx_int(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
DMA_TO_DEVICE, 0);
}
}
+
+ if (unlikely(head_buf->is_sw_gso)) {
+ txr->tx_inline_cons++;
+ if (head_buf->is_sw_gso == BNXT_SW_GSO_LAST) {
+ tso_dma_map_complete(&pdev->dev,
+ &head_buf->sw_gso_cstate);
+ } else {
+ tx_pkts--;
+ tx_bytes -= skb->len;
+ skb = NULL;
+ }
+ head_buf->is_sw_gso = 0;
+ }
+
if (unlikely(is_ts_pkt)) {
if (BNXT_CHIP_P5(bp)) {
/* PTP worker takes ownership of the skb */
@@ -3412,6 +3429,7 @@ static void bnxt_free_one_tx_ring_skbs(struct bnxt *bp,
for (i = 0; i < max_idx;) {
struct bnxt_sw_tx_bd *tx_buf = &txr->tx_buf_ring[i];
+ struct bnxt_sw_tx_bd *head_buf = tx_buf;
struct sk_buff *skb;
int j, last;
@@ -3464,7 +3482,17 @@ static void bnxt_free_one_tx_ring_skbs(struct bnxt *bp,
DMA_TO_DEVICE, 0);
}
}
- dev_kfree_skb(skb);
+ if (head_buf->is_sw_gso) {
+ txr->tx_inline_cons++;
+ if (head_buf->is_sw_gso == BNXT_SW_GSO_LAST) {
+ tso_dma_map_complete(&pdev->dev,
+ &head_buf->sw_gso_cstate);
+ } else {
+ skb = NULL;
+ }
+ }
+ if (skb)
+ dev_kfree_skb(skb);
}
netdev_tx_reset_queue(netdev_get_tx_queue(bp->dev, idx));
}
@@ -3990,9 +4018,9 @@ static void bnxt_free_tx_inline_buf(struct bnxt_tx_ring_info *txr,
txr->tx_inline_size = 0;
}
-static int __maybe_unused bnxt_alloc_tx_inline_buf(struct bnxt_tx_ring_info *txr,
- struct pci_dev *pdev,
- unsigned int size)
+static int bnxt_alloc_tx_inline_buf(struct bnxt_tx_ring_info *txr,
+ struct pci_dev *pdev,
+ unsigned int size)
{
txr->tx_inline_buf = kmalloc(size, GFP_KERNEL);
if (!txr->tx_inline_buf)
@@ -4095,6 +4123,13 @@ static int bnxt_alloc_tx_rings(struct bnxt *bp)
sizeof(struct tx_push_bd);
txr->data_mapping = cpu_to_le64(mapping);
}
+ if (!(bp->flags & BNXT_FLAG_UDP_GSO_CAP)) {
+ rc = bnxt_alloc_tx_inline_buf(txr, pdev,
+ BNXT_SW_USO_MAX_SEGS *
+ TSO_HEADER_SIZE);
+ if (rc)
+ return rc;
+ }
qidx = bp->tc_to_qidx[j];
ring->queue_id = bp->q_info[qidx].queue_id;
spin_lock_init(&txr->xdp_tx_lock);
@@ -4633,10 +4668,13 @@ static int bnxt_init_rx_rings(struct bnxt *bp)
static int bnxt_init_tx_rings(struct bnxt *bp)
{
+ netdev_features_t features;
u16 i;
+ features = bp->dev->features;
+
bp->tx_wake_thresh = max_t(int, bp->tx_ring_size / 2,
- BNXT_MIN_TX_DESC_CNT);
+ bnxt_min_tx_desc_cnt(bp, features));
for (i = 0; i < bp->tx_nr_rings; i++) {
struct bnxt_tx_ring_info *txr = &bp->tx_ring[i];
@@ -13835,6 +13873,11 @@ static netdev_features_t bnxt_fix_features(struct net_device *dev,
if ((features & NETIF_F_NTUPLE) && !bnxt_rfs_capable(bp, false))
features &= ~NETIF_F_NTUPLE;
+ if ((features & NETIF_F_GSO_UDP_L4) &&
+ !(bp->flags & BNXT_FLAG_UDP_GSO_CAP) &&
+ bp->tx_ring_size < 2 * BNXT_SW_USO_MAX_DESCS)
+ features &= ~NETIF_F_GSO_UDP_L4;
+
if ((bp->flags & BNXT_FLAG_NO_AGG_RINGS) || bp->xdp_prog)
features &= ~(NETIF_F_LRO | NETIF_F_GRO_HW);
@@ -13880,6 +13923,9 @@ static int bnxt_set_features(struct net_device *dev, netdev_features_t features)
int rc = 0;
bool re_init = false;
+ bp->tx_wake_thresh = max_t(int, bp->tx_ring_size / 2,
+ bnxt_min_tx_desc_cnt(bp, features));
+
flags &= ~BNXT_FLAG_ALL_CONFIG_FEATS;
if (features & NETIF_F_GRO_HW)
flags |= BNXT_FLAG_GRO;
@@ -16905,8 +16951,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
NETIF_F_GSO_UDP_TUNNEL_CSUM | NETIF_F_GSO_GRE_CSUM |
NETIF_F_GSO_PARTIAL | NETIF_F_RXHASH |
NETIF_F_RXCSUM | NETIF_F_GRO;
- if (bp->flags & BNXT_FLAG_UDP_GSO_CAP)
- dev->hw_features |= NETIF_F_GSO_UDP_L4;
+ dev->hw_features |= NETIF_F_GSO_UDP_L4;
if (BNXT_SUPPORTS_TPA(bp))
dev->hw_features |= NETIF_F_LRO;
@@ -16939,8 +16984,15 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
dev->priv_flags |= IFF_UNICAST_FLT;
netif_set_tso_max_size(dev, GSO_MAX_SIZE);
- if (bp->tso_max_segs)
+ if (!(bp->flags & BNXT_FLAG_UDP_GSO_CAP)) {
+ u16 max_segs = BNXT_SW_USO_MAX_SEGS;
+
+ if (bp->tso_max_segs)
+ max_segs = min_t(u16, max_segs, bp->tso_max_segs);
+ netif_set_tso_max_segs(dev, max_segs);
+ } else if (bp->tso_max_segs) {
netif_set_tso_max_segs(dev, bp->tso_max_segs);
+ }
dev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
NETDEV_XDP_ACT_RX_SG;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
index 6826bf762d26..9ded88196bb4 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
@@ -33,6 +33,7 @@
#include "bnxt_xdp.h"
#include "bnxt_ptp.h"
#include "bnxt_ethtool.h"
+#include "bnxt_gso.h"
#include "bnxt_nvm_defs.h" /* NVRAM content constant and structure defs */
#include "bnxt_fw_hdr.h" /* Firmware hdr constant and structure defs */
#include "bnxt_coredump.h"
@@ -852,12 +853,18 @@ static int bnxt_set_ringparam(struct net_device *dev,
u8 tcp_data_split = kernel_ering->tcp_data_split;
struct bnxt *bp = netdev_priv(dev);
u8 hds_config_mod;
+ int rc;
if ((ering->rx_pending > BNXT_MAX_RX_DESC_CNT) ||
(ering->tx_pending > BNXT_MAX_TX_DESC_CNT) ||
(ering->tx_pending < BNXT_MIN_TX_DESC_CNT))
return -EINVAL;
+ if ((dev->features & NETIF_F_GSO_UDP_L4) &&
+ !(bp->flags & BNXT_FLAG_UDP_GSO_CAP) &&
+ ering->tx_pending < 2 * BNXT_SW_USO_MAX_DESCS)
+ return -EINVAL;
+
hds_config_mod = tcp_data_split != dev->cfg->hds_config;
if (tcp_data_split == ETHTOOL_TCP_DATA_SPLIT_DISABLED && hds_config_mod)
return -EINVAL;
@@ -882,9 +889,17 @@ static int bnxt_set_ringparam(struct net_device *dev,
bp->tx_ring_size = ering->tx_pending;
bnxt_set_ring_params(bp);
- if (netif_running(dev))
- return bnxt_open_nic(bp, false, false);
+ if (netif_running(dev)) {
+ rc = bnxt_open_nic(bp, false, false);
+ if (rc)
+ return rc;
+ }
+ /* ring size changes may affect features (SW USO requires a minimum
+ * ring size), so recalculate features to ensure the correct features
+ * are blocked/available.
+ */
+ netdev_update_features(dev);
return 0;
}
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h
index f01e8102dcd7..3bee626a6303 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h
@@ -23,6 +23,15 @@
*/
#define BNXT_SW_USO_MAX_DESCS (3 * BNXT_SW_USO_MAX_SEGS + MAX_SKB_FRAGS + 1)
+static inline int bnxt_min_tx_desc_cnt(struct bnxt *bp,
+ netdev_features_t features)
+{
+ if (!(bp->flags & BNXT_FLAG_UDP_GSO_CAP) &&
+ (features & NETIF_F_GSO_UDP_L4))
+ return BNXT_SW_USO_MAX_DESCS;
+ return BNXT_MIN_TX_DESC_CNT;
+}
+
netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,
struct bnxt_tx_ring_info *txr,
struct netdev_queue *txq,
--
2.52.0
^ permalink raw reply related
* [net-next v9 07/10] net: bnxt: Implement software USO
From: Joe Damato @ 2026-04-07 22:03 UTC (permalink / raw)
To: netdev, Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: horms, linux-kernel, leon, Joe Damato
In-Reply-To: <20260407220313.3990909-1-joe@dama.to>
Implement bnxt_sw_udp_gso_xmit() using the core tso_dma_map API and
the pre-allocated TX inline buffer for per-segment headers.
The xmit path:
1. Calls tso_start() to initialize TSO state
2. Stack-allocates a tso_dma_map and calls tso_dma_map_init() to
DMA-map the linear payload and all frags upfront.
3. For each segment:
- Copies and patches headers via tso_build_hdr() into the
pre-allocated tx_inline_buf (DMA-synced per segment)
- Counts payload BDs via tso_dma_map_count()
- Emits long BD (header) + ext BD + payload BDs
- Payload BDs use tso_dma_map_next() which yields (dma_addr,
chunk_len, mapping_len) tuples.
Header BDs set dma_unmap_len=0 since the inline buffer is pre-allocated
and unmapped only at ring teardown.
Completion state is updated by calling tso_dma_map_completion_save() for
the last segment.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Joe Damato <joe@dama.to>
---
v9:
- Added inline slot check to prevent possible overwriting of in-flight
headers (suggested by AI).
- Set TX_BD_FLAGS_IP_CKSUM conditionally on !tso.ipv6 (suggested by AI).
v8:
- Zero csum fields on per-segment header copy after tso_build_hdr()
instead of on the original skb, avoiding the need for skb_cow_head, as
suggested by Eric Dumazet.
v7:
- Dropped Pavan's Reviewed-by as some changes were made.
- Updated struct bnxt_sw_tx_bd to embed a tso_dma_map_completion_state
struct for tracking completion state.
- Dropped an unnecessary slot check.
- Eliminated an ugly looking ternary to simplify the code.
- Call tso_dma_map_completion_save to update completion state.
v6:
- Addressed Paolo's feedback where the IOVA API could fail transiently,
leaving stale state in iova_state. Fix this by always copying the state,
noting that dma_iova_try_alloc is called unconditionally in the
tso_dma_map_init function (via tso_dma_iova_try), which zeroes the state
even if the API can't be used.
- Since this was a very minor change, I retained Pavan's Reviewed-by.
v5:
- Added __maybe_unused to last_unmap_len and last_unmap_addr to silence a
build warning when CONFIG_NEED_DMA_MAP_STATE is disabled. No functional
changes.
- Added Pavan's Reviewed-by.
v4:
- Fixed the early return issue Pavan pointed out when num_segs <= 1; use the
drop label instead of returning.
v3:
- Added iova_state and iova_total_len to struct bnxt_sw_tx_bd.
- Stores iova_state on the last segment's tx_buf during xmit.
rfcv2:
- set the unmap len on the last descriptor, so that when completions fire
only the last completion unmaps the region.
drivers/net/ethernet/broadcom/bnxt/bnxt.h | 3 +
drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c | 214 ++++++++++++++++++
2 files changed, 217 insertions(+)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index 6b38b84924e0..fe50576ae525 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -11,6 +11,8 @@
#ifndef BNXT_H
#define BNXT_H
+#include <net/tso.h>
+
#define DRV_MODULE_NAME "bnxt_en"
/* DO NOT CHANGE DRV_VER_* defines
@@ -899,6 +901,7 @@ struct bnxt_sw_tx_bd {
u16 rx_prod;
u16 txts_prod;
};
+ struct tso_dma_map_completion_state sw_gso_cstate;
};
#define BNXT_SW_GSO_MID 1
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
index b296769ee4fe..0d4a59aae88e 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
@@ -19,11 +19,225 @@
#include "bnxt.h"
#include "bnxt_gso.h"
+static u32 bnxt_sw_gso_lhint(unsigned int len)
+{
+ if (len <= 512)
+ return TX_BD_FLAGS_LHINT_512_AND_SMALLER;
+ else if (len <= 1023)
+ return TX_BD_FLAGS_LHINT_512_TO_1023;
+ else if (len <= 2047)
+ return TX_BD_FLAGS_LHINT_1024_TO_2047;
+ else
+ return TX_BD_FLAGS_LHINT_2048_AND_LARGER;
+}
+
netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,
struct bnxt_tx_ring_info *txr,
struct netdev_queue *txq,
struct sk_buff *skb)
{
+ unsigned int last_unmap_len __maybe_unused = 0;
+ dma_addr_t last_unmap_addr __maybe_unused = 0;
+ struct bnxt_sw_tx_bd *last_unmap_buf = NULL;
+ unsigned int hdr_len, mss, num_segs;
+ struct pci_dev *pdev = bp->pdev;
+ unsigned int total_payload;
+ struct tso_dma_map map;
+ u32 vlan_tag_flags = 0;
+ int i, bds_needed;
+ struct tso_t tso;
+ u16 prod, slots;
+ u16 cfa_action;
+ __le32 csum;
+
+ hdr_len = tso_start(skb, &tso);
+ mss = skb_shinfo(skb)->gso_size;
+ total_payload = skb->len - hdr_len;
+ num_segs = DIV_ROUND_UP(total_payload, mss);
+
+ if (unlikely(num_segs <= 1))
+ goto drop;
+
+ /* Upper bound on the number of descriptors needed.
+ *
+ * Each segment uses 1 long BD + 1 ext BD + payload BDs, which is
+ * at most num_segs + nr_frags (each frag boundary crossing adds at
+ * most 1 extra BD).
+ */
+ bds_needed = 3 * num_segs + skb_shinfo(skb)->nr_frags + 1;
+
+ if (unlikely(bnxt_tx_avail(bp, txr) < bds_needed)) {
+ netif_txq_try_stop(txq, bnxt_tx_avail(bp, txr),
+ bp->tx_wake_thresh);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* BD backpressure alone cannot prevent overwriting in-flight
+ * headers in the inline buffer. Check slot availability directly.
+ */
+ slots = txr->tx_inline_prod - txr->tx_inline_cons;
+ slots = BNXT_SW_USO_MAX_SEGS - slots;
+
+ if (unlikely(slots < num_segs)) {
+ netif_txq_try_stop(txq, slots, num_segs);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (unlikely(tso_dma_map_init(&map, &pdev->dev, skb, hdr_len)))
+ goto drop;
+
+ cfa_action = bnxt_xmit_get_cfa_action(skb);
+ if (skb_vlan_tag_present(skb)) {
+ vlan_tag_flags = TX_BD_CFA_META_KEY_VLAN |
+ skb_vlan_tag_get(skb);
+ if (skb->vlan_proto == htons(ETH_P_8021Q))
+ vlan_tag_flags |= 1 << TX_BD_CFA_META_TPID_SHIFT;
+ }
+
+ csum = cpu_to_le32(TX_BD_FLAGS_TCP_UDP_CHKSUM);
+ if (!tso.ipv6)
+ csum |= cpu_to_le32(TX_BD_FLAGS_IP_CKSUM);
+
+ prod = txr->tx_prod;
+
+ for (i = 0; i < num_segs; i++) {
+ unsigned int seg_payload = min_t(unsigned int, mss,
+ total_payload - i * mss);
+ u16 slot = (txr->tx_inline_prod + i) &
+ (BNXT_SW_USO_MAX_SEGS - 1);
+ struct bnxt_sw_tx_bd *tx_buf;
+ unsigned int mapping_len;
+ dma_addr_t this_hdr_dma;
+ unsigned int chunk_len;
+ unsigned int offset;
+ dma_addr_t dma_addr;
+ struct tx_bd *txbd;
+ struct udphdr *uh;
+ void *this_hdr;
+ int bd_count;
+ bool last;
+ u32 flags;
+
+ last = (i == num_segs - 1);
+ offset = slot * TSO_HEADER_SIZE;
+ this_hdr = txr->tx_inline_buf + offset;
+ this_hdr_dma = txr->tx_inline_dma + offset;
+
+ tso_build_hdr(skb, this_hdr, &tso, seg_payload, last);
+
+ /* Zero stale csum fields copied from the original skb;
+ * HW offload recomputes from scratch.
+ */
+ uh = this_hdr + skb_transport_offset(skb);
+ uh->check = 0;
+ if (!tso.ipv6) {
+ struct iphdr *iph = this_hdr + skb_network_offset(skb);
+
+ iph->check = 0;
+ }
+
+ dma_sync_single_for_device(&pdev->dev, this_hdr_dma,
+ hdr_len, DMA_TO_DEVICE);
+
+ bd_count = tso_dma_map_count(&map, seg_payload);
+
+ tx_buf = &txr->tx_buf_ring[RING_TX(bp, prod)];
+ txbd = &txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)];
+
+ tx_buf->skb = skb;
+ tx_buf->nr_frags = bd_count;
+ tx_buf->is_push = 0;
+ tx_buf->is_ts_pkt = 0;
+
+ dma_unmap_addr_set(tx_buf, mapping, this_hdr_dma);
+ dma_unmap_len_set(tx_buf, len, 0);
+
+ if (last) {
+ tx_buf->is_sw_gso = BNXT_SW_GSO_LAST;
+ tso_dma_map_completion_save(&map, &tx_buf->sw_gso_cstate);
+ } else {
+ tx_buf->is_sw_gso = BNXT_SW_GSO_MID;
+ }
+
+ flags = (hdr_len << TX_BD_LEN_SHIFT) |
+ TX_BD_TYPE_LONG_TX_BD |
+ TX_BD_CNT(2 + bd_count);
+
+ flags |= bnxt_sw_gso_lhint(hdr_len + seg_payload);
+
+ txbd->tx_bd_len_flags_type = cpu_to_le32(flags);
+ txbd->tx_bd_haddr = cpu_to_le64(this_hdr_dma);
+ txbd->tx_bd_opaque = SET_TX_OPAQUE(bp, txr, prod,
+ 2 + bd_count);
+
+ prod = NEXT_TX(prod);
+ bnxt_init_ext_bd(bp, txr, prod, csum,
+ vlan_tag_flags, cfa_action);
+
+ /* set dma_unmap_len on the LAST BD touching each
+ * region. Since completions are in-order, the last segment
+ * completes after all earlier ones, so the unmap is safe.
+ */
+ while (tso_dma_map_next(&map, &dma_addr, &chunk_len,
+ &mapping_len, seg_payload)) {
+ prod = NEXT_TX(prod);
+ txbd = &txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)];
+ tx_buf = &txr->tx_buf_ring[RING_TX(bp, prod)];
+
+ txbd->tx_bd_haddr = cpu_to_le64(dma_addr);
+ dma_unmap_addr_set(tx_buf, mapping, dma_addr);
+ dma_unmap_len_set(tx_buf, len, 0);
+ tx_buf->skb = NULL;
+ tx_buf->is_sw_gso = 0;
+
+ if (mapping_len) {
+ if (last_unmap_buf) {
+ dma_unmap_addr_set(last_unmap_buf,
+ mapping,
+ last_unmap_addr);
+ dma_unmap_len_set(last_unmap_buf,
+ len,
+ last_unmap_len);
+ }
+ last_unmap_addr = dma_addr;
+ last_unmap_len = mapping_len;
+ }
+ last_unmap_buf = tx_buf;
+
+ flags = chunk_len << TX_BD_LEN_SHIFT;
+ txbd->tx_bd_len_flags_type = cpu_to_le32(flags);
+ txbd->tx_bd_opaque = 0;
+
+ seg_payload -= chunk_len;
+ }
+
+ txbd->tx_bd_len_flags_type |=
+ cpu_to_le32(TX_BD_FLAGS_PACKET_END);
+
+ prod = NEXT_TX(prod);
+ }
+
+ if (last_unmap_buf) {
+ dma_unmap_addr_set(last_unmap_buf, mapping, last_unmap_addr);
+ dma_unmap_len_set(last_unmap_buf, len, last_unmap_len);
+ }
+
+ txr->tx_inline_prod += num_segs;
+
+ netdev_tx_sent_queue(txq, skb->len);
+
+ WRITE_ONCE(txr->tx_prod, prod);
+ /* Sync BDs before doorbell */
+ wmb();
+ bnxt_db_write(bp, &txr->tx_db, prod);
+
+ if (unlikely(bnxt_tx_avail(bp, txr) <= bp->tx_wake_thresh))
+ netif_txq_try_stop(txq, bnxt_tx_avail(bp, txr),
+ bp->tx_wake_thresh);
+
+ return NETDEV_TX_OK;
+
+drop:
dev_kfree_skb_any(skb);
dev_core_stats_tx_dropped_inc(bp->dev);
return NETDEV_TX_OK;
--
2.52.0
^ permalink raw reply related
* [net-next v9 06/10] net: bnxt: Add boilerplate GSO code
From: Joe Damato @ 2026-04-07 22:03 UTC (permalink / raw)
To: netdev, Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Richard Cochran,
Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
John Fastabend, Stanislav Fomichev
Cc: horms, linux-kernel, leon, Joe Damato, bpf
In-Reply-To: <20260407220313.3990909-1-joe@dama.to>
Add bnxt_gso.c and bnxt_gso.h with a stub bnxt_sw_udp_gso_xmit()
function, SW USO constants (BNXT_SW_USO_MAX_SEGS,
BNXT_SW_USO_MAX_DESCS), and the is_sw_gso field in bnxt_sw_tx_bd
with BNXT_SW_GSO_MID/LAST markers.
The full SW USO implementation will be added in a future commit.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
Signed-off-by: Joe Damato <joe@dama.to>
---
v7:
- Changed the placement of is_sw_gso in struct bnxt_sw_tx_bd to be near
other is_* fields.
- No functional changes.
v5:
- Added Pavan's Reviewed-by. No functional changes.
drivers/net/ethernet/broadcom/bnxt/Makefile | 2 +-
drivers/net/ethernet/broadcom/bnxt/bnxt.h | 4 +++
drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c | 30 ++++++++++++++++++
drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h | 31 +++++++++++++++++++
4 files changed, 66 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
create mode 100644 drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h
diff --git a/drivers/net/ethernet/broadcom/bnxt/Makefile b/drivers/net/ethernet/broadcom/bnxt/Makefile
index ba6c239d52fa..debef78c8b6d 100644
--- a/drivers/net/ethernet/broadcom/bnxt/Makefile
+++ b/drivers/net/ethernet/broadcom/bnxt/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_BNXT) += bnxt_en.o
-bnxt_en-y := bnxt.o bnxt_hwrm.o bnxt_sriov.o bnxt_ethtool.o bnxt_dcb.o bnxt_ulp.o bnxt_xdp.o bnxt_ptp.o bnxt_vfr.o bnxt_devlink.o bnxt_dim.o bnxt_coredump.o
+bnxt_en-y := bnxt.o bnxt_hwrm.o bnxt_sriov.o bnxt_ethtool.o bnxt_dcb.o bnxt_ulp.o bnxt_xdp.o bnxt_ptp.o bnxt_vfr.o bnxt_devlink.o bnxt_dim.o bnxt_coredump.o bnxt_gso.o
bnxt_en-$(CONFIG_BNXT_FLOWER_OFFLOAD) += bnxt_tc.o
bnxt_en-$(CONFIG_DEBUG_FS) += bnxt_debugfs.o
bnxt_en-$(CONFIG_BNXT_HWMON) += bnxt_hwmon.o
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index d98a58aa30f6..6b38b84924e0 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -892,6 +892,7 @@ struct bnxt_sw_tx_bd {
struct page *page;
u8 is_ts_pkt;
u8 is_push;
+ u8 is_sw_gso;
u8 action;
unsigned short nr_frags;
union {
@@ -900,6 +901,9 @@ struct bnxt_sw_tx_bd {
};
};
+#define BNXT_SW_GSO_MID 1
+#define BNXT_SW_GSO_LAST 2
+
struct bnxt_sw_rx_bd {
void *data;
u8 *data_ptr;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
new file mode 100644
index 000000000000..b296769ee4fe
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Broadcom NetXtreme-C/E network driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/netdev_queues.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/udp.h>
+#include <net/tso.h>
+#include <linux/bnxt/hsi.h>
+
+#include "bnxt.h"
+#include "bnxt_gso.h"
+
+netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,
+ struct bnxt_tx_ring_info *txr,
+ struct netdev_queue *txq,
+ struct sk_buff *skb)
+{
+ dev_kfree_skb_any(skb);
+ dev_core_stats_tx_dropped_inc(bp->dev);
+ return NETDEV_TX_OK;
+}
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h
new file mode 100644
index 000000000000..f01e8102dcd7
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Broadcom NetXtreme-C/E network driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef BNXT_GSO_H
+#define BNXT_GSO_H
+
+/* Maximum segments the stack may send in a single SW USO skb.
+ * This caps gso_max_segs for NICs without HW USO support.
+ */
+#define BNXT_SW_USO_MAX_SEGS 64
+
+/* Worst-case TX descriptors consumed by one SW USO packet:
+ * Each segment: 1 long BD + 1 ext BD + payload BDs.
+ * Total payload BDs across all segs <= num_segs + nr_frags (each frag
+ * boundary crossing adds at most 1 extra BD).
+ * So: 3 * max_segs + MAX_SKB_FRAGS + 1 = 3 * 64 + 17 + 1 = 210.
+ */
+#define BNXT_SW_USO_MAX_DESCS (3 * BNXT_SW_USO_MAX_SEGS + MAX_SKB_FRAGS + 1)
+
+netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,
+ struct bnxt_tx_ring_info *txr,
+ struct netdev_queue *txq,
+ struct sk_buff *skb);
+
+#endif
--
2.52.0
^ permalink raw reply related
* [net-next v9 05/10] net: bnxt: Add TX inline buffer infrastructure
From: Joe Damato @ 2026-04-07 22:03 UTC (permalink / raw)
To: netdev, Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: horms, linux-kernel, leon, Joe Damato
In-Reply-To: <20260407220313.3990909-1-joe@dama.to>
Add per-ring pre-allocated inline buffer fields (tx_inline_buf,
tx_inline_dma, tx_inline_size) to bnxt_tx_ring_info and helpers to
allocate and free them. A producer and consumer (tx_inline_prod,
tx_inline_cons) are added to track which slot(s) of the inline buffer
are in-use.
The inline buffer will be used by the SW USO path for pre-allocated,
pre-DMA-mapped per-segment header copies. In the future, this
could be extended to support TX copybreak.
Allocation helper is marked __maybe_unused in this commit because it
will be wired in later.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
Signed-off-by: Joe Damato <joe@dama.to>
---
v5:
- Added Pavan's Reviewed-by. No functional changes.
rfcv2:
- Added a producer and consumer to correctly track the in use header slots.
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 35 +++++++++++++++++++++++
drivers/net/ethernet/broadcom/bnxt/bnxt.h | 6 ++++
2 files changed, 41 insertions(+)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 32a0e71e9fb7..74968ca1f4e2 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -3977,6 +3977,39 @@ static int bnxt_alloc_rx_rings(struct bnxt *bp)
return rc;
}
+static void bnxt_free_tx_inline_buf(struct bnxt_tx_ring_info *txr,
+ struct pci_dev *pdev)
+{
+ if (!txr->tx_inline_buf)
+ return;
+
+ dma_unmap_single(&pdev->dev, txr->tx_inline_dma,
+ txr->tx_inline_size, DMA_TO_DEVICE);
+ kfree(txr->tx_inline_buf);
+ txr->tx_inline_buf = NULL;
+ txr->tx_inline_size = 0;
+}
+
+static int __maybe_unused bnxt_alloc_tx_inline_buf(struct bnxt_tx_ring_info *txr,
+ struct pci_dev *pdev,
+ unsigned int size)
+{
+ txr->tx_inline_buf = kmalloc(size, GFP_KERNEL);
+ if (!txr->tx_inline_buf)
+ return -ENOMEM;
+
+ txr->tx_inline_dma = dma_map_single(&pdev->dev, txr->tx_inline_buf,
+ size, DMA_TO_DEVICE);
+ if (dma_mapping_error(&pdev->dev, txr->tx_inline_dma)) {
+ kfree(txr->tx_inline_buf);
+ txr->tx_inline_buf = NULL;
+ return -ENOMEM;
+ }
+ txr->tx_inline_size = size;
+
+ return 0;
+}
+
static void bnxt_free_tx_rings(struct bnxt *bp)
{
int i;
@@ -3995,6 +4028,8 @@ static void bnxt_free_tx_rings(struct bnxt *bp)
txr->tx_push = NULL;
}
+ bnxt_free_tx_inline_buf(txr, pdev);
+
ring = &txr->tx_ring_struct;
bnxt_free_ring(bp, &ring->ring_mem);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index 83b4136ccd31..d98a58aa30f6 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -996,6 +996,12 @@ struct bnxt_tx_ring_info {
dma_addr_t tx_push_mapping;
__le64 data_mapping;
+ void *tx_inline_buf;
+ dma_addr_t tx_inline_dma;
+ unsigned int tx_inline_size;
+ u16 tx_inline_prod;
+ u16 tx_inline_cons;
+
#define BNXT_DEV_STATE_CLOSING 0x1
u32 dev_state;
--
2.52.0
^ permalink raw reply related
* [net-next v9 04/10] net: bnxt: Use dma_unmap_len for TX completion unmapping
From: Joe Damato @ 2026-04-07 22:03 UTC (permalink / raw)
To: netdev, Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: horms, linux-kernel, leon, Joe Damato
In-Reply-To: <20260407220313.3990909-1-joe@dama.to>
Store the DMA mapping length in each TX buffer descriptor via
dma_unmap_len_set at submit time, and use dma_unmap_len at completion
time.
This is a no-op for normal packets but prepares for software USO,
where header BDs set dma_unmap_len to 0 because the header buffer
is unmapped collectively rather than per-segment.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
Signed-off-by: Joe Damato <joe@dama.to>
---
v4:
- Added Pavan's Reviewed-by tag. No functional changes.
rfcv2:
- Use some local variables to shorten long lines. No functional change from
rfcv1.
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 63 ++++++++++++++---------
1 file changed, 40 insertions(+), 23 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index d1f0969b781c..32a0e71e9fb7 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -656,6 +656,7 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev)
goto tx_free;
dma_unmap_addr_set(tx_buf, mapping, mapping);
+ dma_unmap_len_set(tx_buf, len, len);
flags = (len << TX_BD_LEN_SHIFT) | TX_BD_TYPE_LONG_TX_BD |
TX_BD_CNT(last_frag + 2);
@@ -720,6 +721,7 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev)
tx_buf = &txr->tx_buf_ring[RING_TX(bp, prod)];
netmem_dma_unmap_addr_set(skb_frag_netmem(frag), tx_buf,
mapping, mapping);
+ dma_unmap_len_set(tx_buf, len, len);
txbd->tx_bd_haddr = cpu_to_le64(mapping);
@@ -809,7 +811,8 @@ static bool __bnxt_tx_int(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
u16 hw_cons = txr->tx_hw_cons;
unsigned int tx_bytes = 0;
u16 cons = txr->tx_cons;
- skb_frag_t *frag;
+ unsigned int dma_len;
+ dma_addr_t dma_addr;
int tx_pkts = 0;
bool rc = false;
@@ -844,19 +847,27 @@ static bool __bnxt_tx_int(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
goto next_tx_int;
}
- dma_unmap_single(&pdev->dev, dma_unmap_addr(tx_buf, mapping),
- skb_headlen(skb), DMA_TO_DEVICE);
+ if (dma_unmap_len(tx_buf, len)) {
+ dma_addr = dma_unmap_addr(tx_buf, mapping);
+ dma_len = dma_unmap_len(tx_buf, len);
+
+ dma_unmap_single(&pdev->dev, dma_addr, dma_len,
+ DMA_TO_DEVICE);
+ }
+
last = tx_buf->nr_frags;
for (j = 0; j < last; j++) {
- frag = &skb_shinfo(skb)->frags[j];
cons = NEXT_TX(cons);
tx_buf = &txr->tx_buf_ring[RING_TX(bp, cons)];
- netmem_dma_unmap_page_attrs(&pdev->dev,
- dma_unmap_addr(tx_buf,
- mapping),
- skb_frag_size(frag),
- DMA_TO_DEVICE, 0);
+ if (dma_unmap_len(tx_buf, len)) {
+ dma_addr = dma_unmap_addr(tx_buf, mapping);
+ dma_len = dma_unmap_len(tx_buf, len);
+
+ netmem_dma_unmap_page_attrs(&pdev->dev,
+ dma_addr, dma_len,
+ DMA_TO_DEVICE, 0);
+ }
}
if (unlikely(is_ts_pkt)) {
if (BNXT_CHIP_P5(bp)) {
@@ -3394,6 +3405,8 @@ static void bnxt_free_one_tx_ring_skbs(struct bnxt *bp,
{
int i, max_idx;
struct pci_dev *pdev = bp->pdev;
+ unsigned int dma_len;
+ dma_addr_t dma_addr;
max_idx = bp->tx_nr_pages * TX_DESC_CNT;
@@ -3404,10 +3417,10 @@ static void bnxt_free_one_tx_ring_skbs(struct bnxt *bp,
if (idx < bp->tx_nr_rings_xdp &&
tx_buf->action == XDP_REDIRECT) {
- dma_unmap_single(&pdev->dev,
- dma_unmap_addr(tx_buf, mapping),
- dma_unmap_len(tx_buf, len),
- DMA_TO_DEVICE);
+ dma_addr = dma_unmap_addr(tx_buf, mapping);
+ dma_len = dma_unmap_len(tx_buf, len);
+
+ dma_unmap_single(&pdev->dev, dma_addr, dma_len, DMA_TO_DEVICE);
xdp_return_frame(tx_buf->xdpf);
tx_buf->action = 0;
tx_buf->xdpf = NULL;
@@ -3429,23 +3442,27 @@ static void bnxt_free_one_tx_ring_skbs(struct bnxt *bp,
continue;
}
- dma_unmap_single(&pdev->dev,
- dma_unmap_addr(tx_buf, mapping),
- skb_headlen(skb),
- DMA_TO_DEVICE);
+ if (dma_unmap_len(tx_buf, len)) {
+ dma_addr = dma_unmap_addr(tx_buf, mapping);
+ dma_len = dma_unmap_len(tx_buf, len);
+
+ dma_unmap_single(&pdev->dev, dma_addr, dma_len, DMA_TO_DEVICE);
+ }
last = tx_buf->nr_frags;
i += 2;
for (j = 0; j < last; j++, i++) {
int ring_idx = i & bp->tx_ring_mask;
- skb_frag_t *frag = &skb_shinfo(skb)->frags[j];
tx_buf = &txr->tx_buf_ring[ring_idx];
- netmem_dma_unmap_page_attrs(&pdev->dev,
- dma_unmap_addr(tx_buf,
- mapping),
- skb_frag_size(frag),
- DMA_TO_DEVICE, 0);
+ if (dma_unmap_len(tx_buf, len)) {
+ dma_addr = dma_unmap_addr(tx_buf, mapping);
+ dma_len = dma_unmap_len(tx_buf, len);
+
+ netmem_dma_unmap_page_attrs(&pdev->dev,
+ dma_addr, dma_len,
+ DMA_TO_DEVICE, 0);
+ }
}
dev_kfree_skb(skb);
}
--
2.52.0
^ permalink raw reply related
* [net-next v9 03/10] net: bnxt: Add a helper for tx_bd_ext
From: Joe Damato @ 2026-04-07 22:02 UTC (permalink / raw)
To: netdev, Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: horms, linux-kernel, leon, Joe Damato
In-Reply-To: <20260407220313.3990909-1-joe@dama.to>
Factor out some code to setup tx_bd_exts into a helper function. This
helper will be used by SW USO implementation in the following commits.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
Signed-off-by: Joe Damato <joe@dama.to>
---
v4:
- Added Pavan's Reviewed-by tag. No functional changes.
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 9 ++-------
drivers/net/ethernet/broadcom/bnxt/bnxt.h | 18 ++++++++++++++++++
2 files changed, 20 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index d4288c458576..d1f0969b781c 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -663,10 +663,9 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev)
txbd->tx_bd_opaque = SET_TX_OPAQUE(bp, txr, prod, 2 + last_frag);
prod = NEXT_TX(prod);
- txbd1 = (struct tx_bd_ext *)
- &txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)];
+ txbd1 = bnxt_init_ext_bd(bp, txr, prod, lflags, vlan_tag_flags,
+ cfa_action);
- txbd1->tx_bd_hsize_lflags = lflags;
if (skb_is_gso(skb)) {
bool udp_gso = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4);
u32 hdr_len;
@@ -693,7 +692,6 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev)
} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
txbd1->tx_bd_hsize_lflags |=
cpu_to_le32(TX_BD_FLAGS_TCP_UDP_CHKSUM);
- txbd1->tx_bd_mss = 0;
}
length >>= 9;
@@ -706,9 +704,6 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev)
flags |= bnxt_lhint_arr[length];
txbd->tx_bd_len_flags_type = cpu_to_le32(flags);
- txbd1->tx_bd_cfa_meta = cpu_to_le32(vlan_tag_flags);
- txbd1->tx_bd_cfa_action =
- cpu_to_le32(cfa_action << TX_BD_CFA_ACTION_SHIFT);
txbd0 = txbd;
for (i = 0; i < last_frag; i++) {
frag = &skb_shinfo(skb)->frags[i];
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index 2b40a5bd57af..83b4136ccd31 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -2836,6 +2836,24 @@ static inline u32 bnxt_tx_avail(struct bnxt *bp,
return bp->tx_ring_size - (used & bp->tx_ring_mask);
}
+static inline struct tx_bd_ext *
+bnxt_init_ext_bd(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
+ u16 prod, __le32 lflags, u32 vlan_tag_flags,
+ u32 cfa_action)
+{
+ struct tx_bd_ext *txbd1;
+
+ txbd1 = (struct tx_bd_ext *)
+ &txr->tx_desc_ring[TX_RING(bp, prod)][TX_IDX(prod)];
+ txbd1->tx_bd_hsize_lflags = lflags;
+ txbd1->tx_bd_mss = 0;
+ txbd1->tx_bd_cfa_meta = cpu_to_le32(vlan_tag_flags);
+ txbd1->tx_bd_cfa_action =
+ cpu_to_le32(cfa_action << TX_BD_CFA_ACTION_SHIFT);
+
+ return txbd1;
+}
+
static inline void bnxt_writeq(struct bnxt *bp, u64 val,
volatile void __iomem *addr)
{
--
2.52.0
^ permalink raw reply related
* [net-next v9 02/10] net: bnxt: Export bnxt_xmit_get_cfa_action
From: Joe Damato @ 2026-04-07 22:02 UTC (permalink / raw)
To: netdev, Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: horms, linux-kernel, leon, Joe Damato
In-Reply-To: <20260407220313.3990909-1-joe@dama.to>
Export bnxt_xmit_get_cfa_action so that it can be used in future commits
which add software USO support to bnxt.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
Signed-off-by: Joe Damato <joe@dama.to>
---
v4:
- Added Pavan's Reviewed-by tag. No functional changes.
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 +-
drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index fe8b886ff82e..d4288c458576 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -447,7 +447,7 @@ const u16 bnxt_lhint_arr[] = {
TX_BD_FLAGS_LHINT_2048_AND_LARGER,
};
-static u16 bnxt_xmit_get_cfa_action(struct sk_buff *skb)
+u16 bnxt_xmit_get_cfa_action(struct sk_buff *skb)
{
struct metadata_dst *md_dst = skb_metadata_dst(skb);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index 3558a36ece12..2b40a5bd57af 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -2969,6 +2969,7 @@ unsigned int bnxt_get_avail_cp_rings_for_en(struct bnxt *bp);
int bnxt_reserve_rings(struct bnxt *bp, bool irq_re_init);
void bnxt_tx_disable(struct bnxt *bp);
void bnxt_tx_enable(struct bnxt *bp);
+u16 bnxt_xmit_get_cfa_action(struct sk_buff *skb);
void bnxt_sched_reset_txr(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
u16 curr);
void bnxt_report_link(struct bnxt *bp);
--
2.52.0
^ permalink raw reply related
* [net-next v9 01/10] net: tso: Introduce tso_dma_map and helpers
From: Joe Damato @ 2026-04-07 22:02 UTC (permalink / raw)
To: netdev, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman
Cc: andrew+netdev, michael.chan, pavan.chebbi, linux-kernel, leon,
Joe Damato
In-Reply-To: <20260407220313.3990909-1-joe@dama.to>
Add struct tso_dma_map to tso.h for tracking DMA addresses of mapped
GSO payload data and tso_dma_map_completion_state.
The tso_dma_map combines DMA mapping storage with iterator state, allowing
drivers to walk pre-mapped DMA regions linearly. Includes fields for
the DMA IOVA path (iova_state, iova_offset, total_len) and a fallback
per-region path (linear_dma, frags[], frag_idx, offset).
The tso_dma_map_completion_state makes the IOVA completion state opaque
for drivers. Drivers are expected to allocate this and use the added
helpers to update the completion state.
Adds skb_frag_phys() to skbuff.h, returning the physical address
of a paged fragment's data, which is used by the tso_dma_map helpers
introduced in this commit described below.
The added TSO DMA map helpers are:
tso_dma_map_init(): DMA-maps the linear payload region and all frags
upfront. Prefers the DMA IOVA API for a single contiguous mapping with
one IOTLB sync; falls back to per-region dma_map_phys() otherwise.
Returns 0 on success, cleans up partial mappings on failure.
tso_dma_map_cleanup(): Handles both IOVA and fallback teardown paths.
tso_dma_map_count(): counts how many descriptors the next N bytes of
payload will need. Returns 1 if IOVA is used since the mapping is
contiguous.
tso_dma_map_next(): yields the next (dma_addr, chunk_len) pair.
On the IOVA path, each segment is a single contiguous chunk. On the
fallback path, indicates when a chunk starts a new DMA mapping so the
driver can set dma_unmap_len on that descriptor for completion-time
unmapping.
tso_dma_map_completion_save(): updates the completion state. Drivers
will call this at xmit time.
tso_dma_map_complete(): tears down the mapping at completion time and
returns true if the IOVA path was used. If it was not used, this is a
no-op and returns false.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Joe Damato <joe@dama.to>
---
v9:
- Fix typo in commit message.
- Fix kdoc.
- Initialize tso_dma_map before early return in tso_dma_map_init
(suggested by AI).
v7:
- Squashed the struct and helpers (patch 1 and 2 from v6) into this one
patch.
- Added tso_dma_map_completion_state and helpers
tso_dma_map_completion_save and tso_dma_map_complete to operate on the
struct and keep the DMA IOVA completely opaque from drivers.
- Removed unnecessary duplicated code in tso_dma_map_next and
tso_dma_map_cleanup.
v4:
- Fix the kdoc for the TSO helpers. No functional changes.
v3:
- struct tso_dma_map extended to track IOVA state and
a fallback per-region path.
- Added skb_frag_phys helper include/linux/skbuff.h.
- Added tso_dma_map_use_iova() inline helper in tso.h.
- Updated the helpers to use the DMA IOVA API and falls back to per-region
mapping instead.
include/linux/skbuff.h | 11 ++
include/net/tso.h | 100 +++++++++++++++
net/core/tso.c | 269 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 380 insertions(+)
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 26fe18bcfad8..2bcf78a4de7b 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -3763,6 +3763,17 @@ static inline void *skb_frag_address_safe(const skb_frag_t *frag)
return ptr + skb_frag_off(frag);
}
+/**
+ * skb_frag_phys - gets the physical address of the data in a paged fragment
+ * @frag: the paged fragment buffer
+ *
+ * Returns: the physical address of the data within @frag.
+ */
+static inline phys_addr_t skb_frag_phys(const skb_frag_t *frag)
+{
+ return page_to_phys(skb_frag_page(frag)) + skb_frag_off(frag);
+}
+
/**
* skb_frag_page_copy() - sets the page in a fragment from another fragment
* @fragto: skb fragment where page is set
diff --git a/include/net/tso.h b/include/net/tso.h
index e7e157ae0526..da82aabd1d48 100644
--- a/include/net/tso.h
+++ b/include/net/tso.h
@@ -3,6 +3,7 @@
#define _TSO_H
#include <linux/skbuff.h>
+#include <linux/dma-mapping.h>
#include <net/ip.h>
#define TSO_HEADER_SIZE 256
@@ -28,4 +29,103 @@ void tso_build_hdr(const struct sk_buff *skb, char *hdr, struct tso_t *tso,
void tso_build_data(const struct sk_buff *skb, struct tso_t *tso, int size);
int tso_start(struct sk_buff *skb, struct tso_t *tso);
+/**
+ * struct tso_dma_map - DMA mapping state for GSO payload
+ * @dev: device used for DMA mapping
+ * @skb: the GSO skb being mapped
+ * @hdr_len: per-segment header length
+ * @iova_state: DMA IOVA state (when IOMMU available)
+ * @iova_offset: global byte offset into IOVA range (IOVA path only)
+ * @total_len: total payload length
+ * @frag_idx: current region (-1 = linear, 0..nr_frags-1 = frag)
+ * @offset: byte offset within current region
+ * @linear_dma: DMA address of the linear payload
+ * @linear_len: length of the linear payload
+ * @nr_frags: number of frags successfully DMA-mapped
+ * @frags: per-frag DMA address and length
+ *
+ * DMA-maps the payload regions of a GSO skb (linear data + frags).
+ * Prefers the DMA IOVA API for a single contiguous mapping with one
+ * IOTLB sync; falls back to per-region dma_map_phys() otherwise.
+ */
+struct tso_dma_map {
+ struct device *dev;
+ const struct sk_buff *skb;
+ unsigned int hdr_len;
+ /* IOVA path */
+ struct dma_iova_state iova_state;
+ size_t iova_offset;
+ size_t total_len;
+ /* Fallback path if IOVA path fails */
+ int frag_idx;
+ unsigned int offset;
+ dma_addr_t linear_dma;
+ unsigned int linear_len;
+ unsigned int nr_frags;
+ struct {
+ dma_addr_t dma;
+ unsigned int len;
+ } frags[MAX_SKB_FRAGS];
+};
+
+/**
+ * struct tso_dma_map_completion_state - Completion-time cleanup state
+ * @iova_state: DMA IOVA state (when IOMMU available)
+ * @total_len: total payload length of the IOVA mapping
+ *
+ * Drivers store this on their SW ring at xmit time via
+ * tso_dma_map_completion_save(), then call tso_dma_map_complete() at
+ * completion time.
+ */
+struct tso_dma_map_completion_state {
+ struct dma_iova_state iova_state;
+ size_t total_len;
+};
+
+int tso_dma_map_init(struct tso_dma_map *map, struct device *dev,
+ const struct sk_buff *skb, unsigned int hdr_len);
+void tso_dma_map_cleanup(struct tso_dma_map *map);
+unsigned int tso_dma_map_count(struct tso_dma_map *map, unsigned int len);
+bool tso_dma_map_next(struct tso_dma_map *map, dma_addr_t *addr,
+ unsigned int *chunk_len, unsigned int *mapping_len,
+ unsigned int seg_remaining);
+
+/**
+ * tso_dma_map_completion_save - save state needed for completion-time cleanup
+ * @map: the xmit-time DMA map
+ * @cstate: driver-owned storage that persists until completion
+ *
+ * Should be called at xmit time to update the completion state and later passed
+ * to tso_dma_map_complete().
+ */
+static inline void
+tso_dma_map_completion_save(const struct tso_dma_map *map,
+ struct tso_dma_map_completion_state *cstate)
+{
+ cstate->iova_state = map->iova_state;
+ cstate->total_len = map->total_len;
+}
+
+/**
+ * tso_dma_map_complete - tear down mapping at completion time
+ * @dev: the device that owns the mapping
+ * @cstate: state saved by tso_dma_map_completion_save()
+ *
+ * Return: true if the IOVA path was used and the mapping has been
+ * destroyed; false if the fallback per-region path was used and the
+ * driver must unmap via its normal completion path.
+ */
+static inline bool
+tso_dma_map_complete(struct device *dev,
+ struct tso_dma_map_completion_state *cstate)
+{
+ if (dma_use_iova(&cstate->iova_state)) {
+ dma_iova_destroy(dev, &cstate->iova_state, cstate->total_len,
+ DMA_TO_DEVICE, 0);
+ return true;
+ }
+
+ return false;
+}
+
#endif /* _TSO_H */
diff --git a/net/core/tso.c b/net/core/tso.c
index 6df997b9076e..f2e625e7b64f 100644
--- a/net/core/tso.c
+++ b/net/core/tso.c
@@ -3,6 +3,7 @@
#include <linux/if_vlan.h>
#include <net/ip.h>
#include <net/tso.h>
+#include <linux/dma-mapping.h>
#include <linux/unaligned.h>
void tso_build_hdr(const struct sk_buff *skb, char *hdr, struct tso_t *tso,
@@ -87,3 +88,271 @@ int tso_start(struct sk_buff *skb, struct tso_t *tso)
return hdr_len;
}
EXPORT_SYMBOL(tso_start);
+
+static int tso_dma_iova_try(struct device *dev, struct tso_dma_map *map,
+ phys_addr_t phys, size_t linear_len, size_t total_len,
+ size_t *offset)
+{
+ const struct sk_buff *skb;
+ unsigned int nr_frags;
+ int i;
+
+ if (!dma_iova_try_alloc(dev, &map->iova_state, phys, total_len))
+ return 1;
+
+ skb = map->skb;
+ nr_frags = skb_shinfo(skb)->nr_frags;
+
+ if (linear_len) {
+ if (dma_iova_link(dev, &map->iova_state,
+ phys, *offset, linear_len,
+ DMA_TO_DEVICE, 0))
+ goto iova_fail;
+ map->linear_len = linear_len;
+ *offset += linear_len;
+ }
+
+ for (i = 0; i < nr_frags; i++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ unsigned int frag_len = skb_frag_size(frag);
+
+ if (dma_iova_link(dev, &map->iova_state,
+ skb_frag_phys(frag), *offset,
+ frag_len, DMA_TO_DEVICE, 0)) {
+ map->nr_frags = i;
+ goto iova_fail;
+ }
+ map->frags[i].len = frag_len;
+ *offset += frag_len;
+ map->nr_frags = i + 1;
+ }
+
+ if (dma_iova_sync(dev, &map->iova_state, 0, total_len))
+ goto iova_fail;
+
+ return 0;
+
+iova_fail:
+ dma_iova_destroy(dev, &map->iova_state, *offset,
+ DMA_TO_DEVICE, 0);
+ memset(&map->iova_state, 0, sizeof(map->iova_state));
+
+ /* reset map state */
+ map->frag_idx = -1;
+ map->offset = 0;
+ map->linear_len = 0;
+ map->nr_frags = 0;
+
+ return 1;
+}
+
+/**
+ * tso_dma_map_init - DMA-map GSO payload regions
+ * @map: map struct to initialize
+ * @dev: device for DMA mapping
+ * @skb: the GSO skb
+ * @hdr_len: per-segment header length in bytes
+ *
+ * DMA-maps the linear payload (after headers) and all frags.
+ * Prefers the DMA IOVA API (one contiguous mapping, one IOTLB sync);
+ * falls back to per-region dma_map_phys() when IOVA is not available.
+ * Positions the iterator at byte 0 of the payload.
+ *
+ * Return: 0 on success, -ENOMEM on DMA mapping failure (partial mappings
+ * are cleaned up internally).
+ */
+int tso_dma_map_init(struct tso_dma_map *map, struct device *dev,
+ const struct sk_buff *skb, unsigned int hdr_len)
+{
+ unsigned int linear_len = skb_headlen(skb) - hdr_len;
+ unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
+ size_t total_len = skb->len - hdr_len;
+ size_t offset = 0;
+ phys_addr_t phys;
+ int i;
+
+ map->dev = dev;
+ map->skb = skb;
+ map->hdr_len = hdr_len;
+ map->frag_idx = -1;
+ map->offset = 0;
+ map->iova_offset = 0;
+ map->total_len = total_len;
+ map->linear_len = 0;
+ map->nr_frags = 0;
+ memset(&map->iova_state, 0, sizeof(map->iova_state));
+
+ if (!total_len)
+ return 0;
+
+ if (linear_len)
+ phys = virt_to_phys(skb->data + hdr_len);
+ else
+ phys = skb_frag_phys(&skb_shinfo(skb)->frags[0]);
+
+ if (tso_dma_iova_try(dev, map, phys, linear_len, total_len, &offset)) {
+ /* IOVA path failed, map state was reset. Fallback to
+ * per-region dma_map_phys()
+ */
+ if (linear_len) {
+ map->linear_dma = dma_map_phys(dev, phys, linear_len,
+ DMA_TO_DEVICE, 0);
+ if (dma_mapping_error(dev, map->linear_dma))
+ return -ENOMEM;
+ map->linear_len = linear_len;
+ }
+
+ for (i = 0; i < nr_frags; i++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ unsigned int frag_len = skb_frag_size(frag);
+
+ map->frags[i].len = frag_len;
+ map->frags[i].dma = dma_map_phys(dev, skb_frag_phys(frag),
+ frag_len, DMA_TO_DEVICE, 0);
+ if (dma_mapping_error(dev, map->frags[i].dma)) {
+ tso_dma_map_cleanup(map);
+ return -ENOMEM;
+ }
+ map->nr_frags = i + 1;
+ }
+ }
+
+ if (linear_len == 0 && nr_frags > 0)
+ map->frag_idx = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL(tso_dma_map_init);
+
+/**
+ * tso_dma_map_cleanup - unmap all DMA regions in a tso_dma_map
+ * @map: the map to clean up
+ *
+ * Handles both IOVA and fallback paths. For IOVA, calls
+ * dma_iova_destroy(). For fallback, unmaps each region individually.
+ */
+void tso_dma_map_cleanup(struct tso_dma_map *map)
+{
+ int i;
+
+ if (dma_use_iova(&map->iova_state)) {
+ dma_iova_destroy(map->dev, &map->iova_state, map->total_len,
+ DMA_TO_DEVICE, 0);
+ memset(&map->iova_state, 0, sizeof(map->iova_state));
+ } else {
+ if (map->linear_len)
+ dma_unmap_phys(map->dev, map->linear_dma, map->linear_len,
+ DMA_TO_DEVICE, 0);
+
+ for (i = 0; i < map->nr_frags; i++)
+ dma_unmap_phys(map->dev, map->frags[i].dma, map->frags[i].len,
+ DMA_TO_DEVICE, 0);
+ }
+
+ map->linear_len = 0;
+ map->nr_frags = 0;
+}
+EXPORT_SYMBOL(tso_dma_map_cleanup);
+
+/**
+ * tso_dma_map_count - count descriptors for a payload range
+ * @map: the payload map
+ * @len: number of payload bytes in this segment
+ *
+ * Counts how many contiguous DMA region chunks the next @len bytes
+ * will span, without advancing the iterator. On the IOVA path this
+ * is always 1 (contiguous). On the fallback path, uses region sizes
+ * from the current position.
+ *
+ * Return: the number of descriptors needed for @len bytes of payload.
+ */
+unsigned int tso_dma_map_count(struct tso_dma_map *map, unsigned int len)
+{
+ unsigned int offset = map->offset;
+ int idx = map->frag_idx;
+ unsigned int count = 0;
+
+ if (!len)
+ return 0;
+
+ if (dma_use_iova(&map->iova_state))
+ return 1;
+
+ while (len > 0) {
+ unsigned int region_len, chunk;
+
+ if (idx == -1)
+ region_len = map->linear_len;
+ else
+ region_len = map->frags[idx].len;
+
+ chunk = min(len, region_len - offset);
+ len -= chunk;
+ count++;
+ offset = 0;
+ idx++;
+ }
+
+ return count;
+}
+EXPORT_SYMBOL(tso_dma_map_count);
+
+/**
+ * tso_dma_map_next - yield the next DMA address range
+ * @map: the payload map
+ * @addr: output DMA address
+ * @chunk_len: output chunk length
+ * @mapping_len: full DMA mapping length when this chunk starts a new
+ * mapping region, or 0 when continuing a previous one.
+ * On the IOVA path this is always 0 (driver must not
+ * do per-region unmaps; use tso_dma_map_cleanup instead).
+ * @seg_remaining: bytes left in current segment
+ *
+ * Yields the next (dma_addr, chunk_len) pair and advances the iterator.
+ * On the IOVA path, the entire payload is contiguous so each segment
+ * is always a single chunk.
+ *
+ * Return: true if a chunk was yielded, false when @seg_remaining is 0.
+ */
+bool tso_dma_map_next(struct tso_dma_map *map, dma_addr_t *addr,
+ unsigned int *chunk_len, unsigned int *mapping_len,
+ unsigned int seg_remaining)
+{
+ unsigned int region_len, chunk;
+
+ if (!seg_remaining)
+ return false;
+
+ /* IOVA path: contiguous DMA range, no region boundaries */
+ if (dma_use_iova(&map->iova_state)) {
+ *addr = map->iova_state.addr + map->iova_offset;
+ *chunk_len = seg_remaining;
+ *mapping_len = 0;
+ map->iova_offset += seg_remaining;
+ return true;
+ }
+
+ /* Fallback path: per-region iteration */
+
+ if (map->frag_idx == -1) {
+ region_len = map->linear_len;
+ chunk = min(seg_remaining, region_len - map->offset);
+ *addr = map->linear_dma + map->offset;
+ } else {
+ region_len = map->frags[map->frag_idx].len;
+ chunk = min(seg_remaining, region_len - map->offset);
+ *addr = map->frags[map->frag_idx].dma + map->offset;
+ }
+
+ *mapping_len = (map->offset == 0) ? region_len : 0;
+ *chunk_len = chunk;
+ map->offset += chunk;
+
+ if (map->offset >= region_len) {
+ map->frag_idx++;
+ map->offset = 0;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL(tso_dma_map_next);
--
2.52.0
^ permalink raw reply related
* [net-next v9 00/10] Add TSO map-once DMA helpers and bnxt SW USO support
From: Joe Damato @ 2026-04-07 22:02 UTC (permalink / raw)
To: netdev
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms, michael.chan,
pavan.chebbi, linux-kernel, leon, Joe Damato
Greetings:
This series extends net/tso to add a data structure and some helpers allowing
drivers to DMA map headers and packet payloads a single time. The helpers can
then be used to reference slices of shared mapping for each segment. This
helps to avoid the cost of repeated DMA mappings, especially on systems which
use an IOMMU. N per-packet DMA maps are replaced with a single map for the
entire GSO skb. As of v3, the series uses the DMA IOVA API (as suggested by
Leon [1]) and provides a fallback path when an IOMMU is not in use. The DMA
IOVA API provides even better efficiency than the v2; see below.
The added helpers are then used in bnxt to add support for software UDP
Segmentation Offloading (SW USO) for older bnxt devices which do not have
support for USO in hardware. Since the helpers are generic, other drivers
can be extended similarly.
The v2 showed a ~4x reduction in DMA mapping calls at the same wire packet
rate on production traffic with a bnxt device. The v3, however, shows a larger
reduction of about ~6x at the same wire packet rate. This is thanks to Leon's
suggestion of using the DMA IOVA API [1].
Special care is taken to make bnxt ethtool operations work correctly: the ring
size cannot be reduced below a minimum threshold while USO is enabled and
growing the ring automatically re-enables USO if it was previously blocked.
This v9 contains several changes, mostly stuff AI found. Changes are listed
below and in the per-patch changelog.
I re-ran the python test and the test passed on my bnxt system. I also ran
this on a production system.
Thanks,
Joe
[1]: https://lore.kernel.org/netdev/20260316194419.GH61385@unreal/
[2]: https://lore.kernel.org/netdev/ab1f764b-de03-48f5-a781-356495257d25@redhat.com/
v9:
- Patch 1:
- Fix typo in commit message.
- Fix kdoc.
- Initialize tso_dma_map before early return in tso_dma_map_init
(suggested by AI).
- Patch 7 (both suggested by AI):
- Added inline slot check to prevent possible overwriting of in-flight
headers in the buffer.
- Made TX_BD_FLAGS_IP_CKSUM conditional on !tso.ipv6
- Patch 8 (suggested by AI):
- Always allocate header buffer for non-HW-USO NICs. Avoids a possible
NULL deref if USO is toggled off, the device is brought down, brought
up, and USO is re-enabled.
- Adjust bnxt_min_tx_desc_cnt to take a feature parameter, which is needed to
prevent stale features from being examined.
- Patch 10:
- Use UDP-LISTEN instead of UDP-RECV in socat receiver (suggested by AI).
- Fixed docstring.
- Removed unused return value.
v8: https://lore.kernel.org/netdev/20260403003524.2564973-1-joe@dama.to/
- Zero csum fields on per-segment header copy after tso_build_hdr()
instead of on the original skb, avoiding the need for skb_cow_head, as
suggested by Eric Dumazet.
v7: https://lore.kernel.org/netdev/20260401233745.2333858-1-joe@dama.to/
- Squashed patches 1 and 2 of the v6 into patch 1 of this series, as
requested by Jakub.
- Added tso_dma_map_completion_state and helpers so that drivers don't call
any of the DMA IOVA API directly. See the changelog in patch 1 for
details.
- Changed the placement of the is_sw_gso field in struct bnxt_sw_tx_bd in
patch 6, as request by Jakub.
- Updated struct bnxt_sw_tx_bd to embed a tso_dma_map_completion_state for
tracking completion state and dropped an unnecessary slot check from patch
7.
- Added bnxt_min_tx_desc_cnt helper to factor out descriptor counting and
use the newly added tso_dma_map_complete from bnxt instead of calling the
DMA IOVA API directly in patch 8.
- Various fixes to the python test in patch 10: use ksft_variants, socat on
the receiving side, and cfg.wait_hw_stats_settle instead of sleep.
v6: https://lore.kernel.org/netdev/20260326235238.2940471-1-joe@dama.to/
- Addressed Paolo's request [2] to avoid possible stale iova_state if the
IOVA API starts to fail transiently. See patch 8.
v5: https://lore.kernel.org/netdev/20260323183844.3146982-1-joe@dama.to/
- Adjusted patch 8 to address the kernel test robot. See patch changelog, no
functional change.
- Added Pavan's Reviewed-by to patches 6-12.
v4: https://lore.kernel.org/all/20260320144141.260246-1-joe@dama.to/
- Fixed kdoc issues in patch 2. No functional change.
- Added Pavan's Reviewed-by to patches 3, 4, and 5.
- Fixed the issue Pavan (and the AI review) pointed out in patch 8. See
patch changelog.
- Added parentheses around gso_type check in patch 11 for clarity. No
functional change.
- Fixed python linter issues in patch 12. No functional change.
v3: https://lore.kernel.org/netdev/20260318191325.1819881-1-joe@dama.to/
- Converted from RFC to an actual submission.
- Updated based on Leon's feedback to use the DMA IOVA API. See individual
patches for update information.
RFCv2: https://lore.kernel.org/netdev/20260312223457.1999489-1-joe@dama.to/
- Some bugs were discovered shortly after sending: incorrect handling of the
shared header space and a bug in the unmap path in the TX completion.
Sorry about that; I was more careful this time.
- On that note: this rfc includes a test.
RFCv1: https://lore.kernel.org/netdev/20260310212209.2263939-1-joe@dama.to/
Joe Damato (10):
net: tso: Introduce tso_dma_map and helpers
net: bnxt: Export bnxt_xmit_get_cfa_action
net: bnxt: Add a helper for tx_bd_ext
net: bnxt: Use dma_unmap_len for TX completion unmapping
net: bnxt: Add TX inline buffer infrastructure
net: bnxt: Add boilerplate GSO code
net: bnxt: Implement software USO
net: bnxt: Add SW GSO completion and teardown support
net: bnxt: Dispatch to SW USO
selftests: drv-net: Add USO test
drivers/net/ethernet/broadcom/bnxt/Makefile | 2 +-
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 178 +++++++++---
drivers/net/ethernet/broadcom/bnxt/bnxt.h | 32 +++
.../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 19 +-
drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c | 244 ++++++++++++++++
drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h | 40 +++
include/linux/skbuff.h | 11 +
include/net/tso.h | 100 +++++++
net/core/tso.c | 269 ++++++++++++++++++
tools/testing/selftests/drivers/net/Makefile | 1 +
tools/testing/selftests/drivers/net/uso.py | 103 +++++++
11 files changed, 959 insertions(+), 40 deletions(-)
create mode 100644 drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
create mode 100644 drivers/net/ethernet/broadcom/bnxt/bnxt_gso.h
create mode 100755 tools/testing/selftests/drivers/net/uso.py
base-commit: 2ce8a41113eda1adddc1e6dc43cf89383ec6dc22
--
2.52.0
^ permalink raw reply
* Re: [Intel-wired-lan] [PATCH iwl-net] idpf: fix read_dev_clk_lock spinlock init in idpf_ptp_init()
From: Tantilov, Emil S @ 2026-04-07 22:00 UTC (permalink / raw)
To: Simon Horman
Cc: intel-wired-lan, netdev, anthony.l.nguyen, aleksandr.loktionov,
przemyslaw.kitszel, andrew+netdev, davem, edumazet, kuba, pabeni,
richardcochran, milena.olech, jacob.e.keller, konstantin.ilichev
In-Reply-To: <20260407160241.470945-3-horms@kernel.org>
On 4/7/2026 9:02 AM, Simon Horman wrote:
> From: 'Simon Horman' <horms@kernel.org>
>
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://sashiko.dev
>
> Simon says: I don't agree with the regression characterisation made by
> the AI review - I think this patch is good. But I do think the issues
> flagged by the AI review warrant investigation.
The point of the change is to resolve the use of uninitialized spinlock.
The questions below appear to be generated around that code, which would
be out of scope for this patch, but I will address them anyway ...
>
> ---
> idpf: fix read_dev_clk_lock spinlock init in idpf_ptp_init()
>
> This commit moves the initialization of read_dev_clk_lock earlier in
> idpf_ptp_init() to ensure it is initialized before the PTP auxiliary
> worker is scheduled.
>
>> diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
>> index eec91c4f0a75a..4a51d2727547d 100644
>> --- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c
>> +++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c
>> @@ -952,6 +952,8 @@ int idpf_ptp_init(struct idpf_adapter *adapter)
>> goto free_ptp;
>> }
>>
>> + spin_lock_init(&adapter->ptp->read_dev_clk_lock);
>> +
>> err = idpf_ptp_create_clock(adapter);
>
> This patch moves the lock initialization to prevent the worker from accessing
> an uninitialized lock. However, idpf_ptp_create_clock() registers the PTP
> device, and shortly after, ptp_schedule_worker() is called. Both happen
> before the hardware clock baseline and initial time are set later in
> idpf_ptp_init().
>
> Could this introduce a regression where userspace opens the PTP device and
> interacts with an uninitialized clock, or has its settings overwritten by
> the driver's delayed idpf_ptp_settime64() call?
If such an issue existed it would not be because of this change (see my
point above). There is a check in idpf_ptp_settime64() that should
protect against this case:
https://git.kernel.org/pub/scm/linux/kernel/git/tnguy/net-queue.git/tree/drivers/net/ethernet/intel/idpf/idpf_ptp.c?h=dev-queue#n424
access = adapter->ptp->set_dev_clk_time_access;
if (access != IDPF_PTP_MAILBOX)
return -EOPNOTSUPP;
basically, the initial time write will only happen when the capability
is actually set.
>
> Additionally, could the scheduled worker read the uninitialized zero-time
> and overwrite the software cache after the main thread sets the real time?
>
> Moving the device registration and worker scheduling to the very end of
> idpf_ptp_init() might resolve these races and naturally fix the lock
> initialization ordering.
I think this is a valid observation, but I am not sure it would be a
problem in practice, because the vport queues are not receiving at that
point. I can take a look in more details, but if a fix is needed, it
will not be part of this patch.
>
> Also, since this code manages worker synchronization, is there a regression
> when updating cached_phc_time and cached_phc_jiffies?
>
> In idpf_ptp_update_cached_phctime(), these variables are updated
> sequentially using the WRITE_ONCE() macro.
>
> Because this function can be executed concurrently by the PTP worker
> thread and ioctls, thread interleaving could result in an inconsistent
> state where one variable is from one thread and the other from another.
>
> Readers like idpf_ptp_extend_ts() load these variables using plain C
> accesses without mutual exclusion or a seqlock.
>
> Could this cause valid timestamps to be extended using an incorrect or
> outdated baseline, silently corrupting network timestamps?
Not sure why the AI went on the "regression" tangent here. I think it is
obvious none of these cases are regressions caused by the patch. That
being said, I think this looks like a valid concern, but again, will be
addressed via separate patch if needed.
Thanks,
Emil
>
> [ ... ]
^ permalink raw reply
* Re: [PATCH net-next 3/3] selftests: net: psp: add crypt-offset and spi-threshold test cases
From: Willem de Bruijn @ 2026-04-07 21:52 UTC (permalink / raw)
To: Akhilesh Samineni, davem, edumazet, kuba, pabeni, andrew+netdev,
horms, willemb, daniel.zahka
Cc: netdev, linux-kernel, jayakrishnan.udayavarma, ajit.khaparde,
kiran.kella, akhilesh.samineni, sachin.suman
In-Reply-To: <20260406222305.4111170-4-akhilesh.samineni@broadcom.com>
Akhilesh Samineni wrote:
> Add test cases to set and get crypt-offset and spi-threshold attributes,
> verifying both the applied value and the restored prior value.
>
> Signed-off-by: Akhilesh Samineni <akhilesh.samineni@broadcom.com>
> Reviewed-by: Kiran Kella <kiran.kella@broadcom.com>
> Reviewed-by: Ajit Kumar Khaparde <ajit.khaparde@broadcom.com>
> ---
> tools/testing/selftests/drivers/net/psp.py | 32 ++++++++++++++++++++++
> 1 file changed, 32 insertions(+)
>
> diff --git a/tools/testing/selftests/drivers/net/psp.py b/tools/testing/selftests/drivers/net/psp.py
> index 864d9fce1094..9253aab29ded 100755
> --- a/tools/testing/selftests/drivers/net/psp.py
> +++ b/tools/testing/selftests/drivers/net/psp.py
> @@ -171,6 +171,38 @@ def dev_get_device_bad(cfg):
> ksft_true(raised)
>
>
> +def dev_set_crypt_offset(cfg):
> + """ Set and get the crypt-offset """
> + _init_psp_dev(cfg)
> +
> + dev = cfg.pspnl.dev_get({'id': cfg.psp_dev_id})
> + orig = dev['crypt-offset']
> + cfg.pspnl.dev_set({"id": cfg.psp_dev_id,
> + "crypt-offset": 5})
> + dev = cfg.pspnl.dev_get({'id': cfg.psp_dev_id})
> + ksft_eq(dev['crypt-offset'], 5)
> + cfg.pspnl.dev_set({"id": cfg.psp_dev_id,
> + "crypt-offset": orig})
> + dev = cfg.pspnl.dev_get({'id': cfg.psp_dev_id})
> + ksft_eq(dev['crypt-offset'], orig)
> +
> +
> +def dev_set_spi_threshold(cfg):
> + """ Set and get the spi-threshold """
> + _init_psp_dev(cfg)
> +
> + dev = cfg.pspnl.dev_get({'id': cfg.psp_dev_id})
> + orig = dev['spi-threshold']
> + cfg.pspnl.dev_set({"id": cfg.psp_dev_id,
> + "spi-threshold": 10})
> + dev = cfg.pspnl.dev_get({'id': cfg.psp_dev_id})
> + ksft_eq(dev['spi-threshold'], 10)
> + cfg.pspnl.dev_set({"id": cfg.psp_dev_id,
> + "spi-threshold": orig})
> + dev = cfg.pspnl.dev_get({'id': cfg.psp_dev_id})
> + ksft_eq(dev['spi-threshold'], orig)
These tests mainly verify that netlink works as intended. Not sure how
much value that brings.
Once crypt-offset requires bounds checking (say), such control ops
functional tests may become more valuable.
More interesting would be to see the effect on the datapath. E.g.,
a crypt-offset that actually leaves plaintext. Not sure how easy or
hard this is, so don't take this as a requirement. But maybe something
that achievable with PSP packetdrill (eventually)?
^ permalink raw reply
* Re: [PATCH net-next 2/3] netdevsim: psp: handle the new crypt-offset and spi-threshold get/set operations
From: Willem de Bruijn @ 2026-04-07 21:49 UTC (permalink / raw)
To: Akhilesh Samineni, davem, edumazet, kuba, pabeni, andrew+netdev,
horms, willemb, daniel.zahka
Cc: netdev, linux-kernel, jayakrishnan.udayavarma, ajit.khaparde,
kiran.kella, akhilesh.samineni, sachin.suman
In-Reply-To: <20260406222305.4111170-3-akhilesh.samineni@broadcom.com>
Akhilesh Samineni wrote:
> Implement the crypt-offset and spi-threshold get/set in netdevsim PSP.
>
> Signed-off-by: Akhilesh Samineni <akhilesh.samineni@broadcom.com>
> Reviewed-by: Kiran Kella <kiran.kella@broadcom.com>
> Reviewed-by: Ajit Kumar Khaparde <ajit.khaparde@broadcom.com>
> ---
> drivers/net/netdevsim/netdevsim.h | 2 ++
> drivers/net/netdevsim/psp.c | 6 ++++++
> 2 files changed, 8 insertions(+)
>
> diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
> index c904e14f6b3f..3ad7d42391c0 100644
> --- a/drivers/net/netdevsim/netdevsim.h
> +++ b/drivers/net/netdevsim/netdevsim.h
> @@ -117,6 +117,8 @@ struct netdevsim {
> struct psp_dev *dev;
> u32 spi;
> u32 assoc_cnt;
> + u8 crypt_offset;
> + u32 spi_threshold;
> } psp;
>
> struct nsim_bus_dev *nsim_bus_dev;
> diff --git a/drivers/net/netdevsim/psp.c b/drivers/net/netdevsim/psp.c
> index 0b4d717253b0..9098edf00c5c 100644
> --- a/drivers/net/netdevsim/psp.c
> +++ b/drivers/net/netdevsim/psp.c
> @@ -122,6 +122,11 @@ static int
> nsim_psp_set_config(struct psp_dev *psd, struct psp_dev_config *conf,
> struct netlink_ext_ack *extack)
> {
> + struct netdevsim *ns = psd->drv_priv;
> +
> + ns->psp.crypt_offset = conf->crypt_offset;
> + ns->psp.spi_threshold = conf->spi_threshold;
> +
> return 0;
> }
>
> @@ -249,6 +254,7 @@ int nsim_psp_init(struct netdevsim *ns)
> if (err)
> return err;
>
> + ns->psp.spi_threshold = PSP_SPI_THRESHOLD_DEFAULT;
> debugfs_create_file("psp_rereg", 0200, ddir, ns, &nsim_psp_rereg_fops);
> return 0;
Default initialization should probably all complete before the device
is made visible with psp_dev_create.
^ permalink raw reply
* Re: [PATCH,net-next] tcp: Add TCP ROCCET congestion control module.
From: Neal Cardwell @ 2026-04-07 21:48 UTC (permalink / raw)
To: Tim Fuechsel
Cc: David S. Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Kuniyuki Iwashima, linux-kernel,
netdev, Lukas Prause
In-Reply-To: <adIUY2N6HOKZNzSJ@volt-roccet-vm>
On Sun, Apr 5, 2026 at 3:51 AM Tim Fuechsel <t.fuechsel@gmx.de> wrote:
>
> TCP ROCCET is an extension of TCP CUBIC that improves its overall
> performance. By its mode of function, CUBIC causes bufferbloat while
> it tries to detect the available throughput of a network path. This is
> particularly a problem with large buffers in mobile networks. A more
> detailed description and analysis of this problem caused by TCP CUBIC
> can be found in [1].
Thanks for posting this patch. I agree that improving the bufferbloat
caused by CUBIC and similar algorithms is an important area for work.
> TCP ROCCET addresses this problem by adding two
> additional metrics to detect congestion (queueing and bufferbloat)
> on a network path.
Normally I think of bufferbloat as excessive queuing. Thus normally I
would think of queuing and bufferbloat as essentially the same metric.
So it seems confusing for this sentence to claim that these are two
additional metrics rather than one.
Furthermore, these mentions of "queueing" and "bufferbloat" are the
last time those words appear in the entire patch. It's unclear to the
reader how your high-level description in this sentence connects with
the algorithm or the code.
Please clarify in the commit description what you mean by "two
additional metrics to detect congestion (queueing and bufferbloat)",
how you define "queueing", how you define "bufferbloat", and how the
algorithm measures and uses these metrics.
> TCP ROCCET achieves better performance than CUBIC
> and BBRv3, by maintaining similar throughput while reducing the latency.
Please reference figures in the paper and mention specific concrete
numerical examples of latency reductions to quantify these statements.
> In addition, TCP ROCCET does not have fairness issues when sharing a
> link with TCP CUBIC and BBRv3.
Can you please elaborate on this statement here? AFAICT from figures 7
and 8 in https://arxiv.org/pdf/2510.25281 it seems ROCCET is
essentially starved by CUBIC when sharing a bottleneck with CUBIC when
the bottleneck has 2*BDP or more of buffering. AFAICT it sounds like
ROCCET does have "fairness issues when sharing a link with TCP CUBIC"?
> A paper that evaluates the performance
> and function of TCP ROCCET has already been peer-reviewed and will be
> presented at the WONS 2026 conference. A draft of this paper can be
> found here [2].
>
> [1] https://doi.org/10.1109/VTC2023-Fall60731.2023.10333357
> [2] https://arxiv.org/abs/2510.25281
Thanks for the links to the paper on ROCCET. This is very helpful.
> Signed-off-by: Lukas Prause <lukas.prause@ikt.uni-hannover.de>
> Signed-off-by: Tim Fuechsel <t.fuechsel@gmx.de>
> ---
> net/ipv4/Kconfig | 11 +
> net/ipv4/Makefile | 1 +
> net/ipv4/tcp_roccet.c | 686 ++++++++++++++++++++++++++++++++++++++++++
> net/ipv4/tcp_roccet.h | 60 ++++
> 4 files changed, 758 insertions(+)
> create mode 100644 net/ipv4/tcp_roccet.c
> create mode 100644 net/ipv4/tcp_roccet.h
>
> diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
> index 21e5164e30db..33625111c7f0 100644
> --- a/net/ipv4/Kconfig
> +++ b/net/ipv4/Kconfig
> @@ -663,6 +663,17 @@ config TCP_CONG_CDG
> delay gradients." In Networking 2011. Preprint:
> http://caia.swin.edu.au/cv/dahayes/content/networking2011-cdg-preprint.pdf
>
> +config TCP_CONG_ROCCET
> + tristate "ROCCET TCP"
> + default n
> + help
> + TCP ROCCET is a sender-side only modification of the TCP CUBIC
> + protocol stack that optimizes the performance of TCP congestion
s/TCP CUBIC protocol stack/TCP CUBIC congestion control algorithm/
> + control. Especially for networks with large buffers (wireless,
> + cellular networks), TCP ROCCET has improved performance by maintaining
> + similar throughput as CUBIC while reducing the latency.
> + For more information, see: https://arxiv.org/abs/2510.25281
nit: AFAICT there's an extra space in front of the word "For".
> +
> config TCP_CONG_BBR
> tristate "BBR TCP"
> default n
> diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
> index 7f9f98813986..82ed7989dcb3 100644
> --- a/net/ipv4/Makefile
> +++ b/net/ipv4/Makefile
> @@ -45,6 +45,7 @@ obj-$(CONFIG_INET_TCP_DIAG) += tcp_diag.o
> obj-$(CONFIG_INET_UDP_DIAG) += udp_diag.o
> obj-$(CONFIG_INET_RAW_DIAG) += raw_diag.o
> obj-$(CONFIG_TCP_CONG_BBR) += tcp_bbr.o
> +obj-$(CONFIG_TCP_CONG_ROCCET) += tcp_roccet.o
> obj-$(CONFIG_TCP_CONG_BIC) += tcp_bic.o
> obj-$(CONFIG_TCP_CONG_CDG) += tcp_cdg.o
> obj-$(CONFIG_TCP_CONG_CUBIC) += tcp_cubic.o
> diff --git a/net/ipv4/tcp_roccet.c b/net/ipv4/tcp_roccet.c
> new file mode 100644
> index 000000000000..b0ec3053182f
> --- /dev/null
> +++ b/net/ipv4/tcp_roccet.c
> @@ -0,0 +1,686 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * TCP ROCCET: An RTT-Oriented CUBIC Congestion Control
> + * Extension for 5G and Beyond Networks
> + *
> + * TCP ROCCET is a new TCP congestion control
> + * algorithm suited for current cellular 5G NR beyond networks.
> + * It extends the kernel default congestion control CUBIC
> + * and improves its performance, and additionally solves an
> + * unwanted side effects of CUBIC’s implementation.
Please specify what side effect or side effects ROCCET is claiming to
solve (presumably bufferbloat?).
> + * ROCCET uses its own Slow Start, called LAUNCH, where loss
> + * is not considered as a congestion event.
Expressed in isolation like this, that sounds potentially dangerous.
Please mention what signal(s) ROCCET uses to exit slow start if it's
not using loss.
In addition, from reading the code AFAICT the connection does use loss
to exit slow start (see my remarks below in this message). So AFAICT
this summary seems inaccurate, or at least misleading?
> +static __always_inline void update_min_rtt(struct sock *sk)
> +{
> + struct roccettcp *ca = inet_csk_ca(sk);
> + u32 now = jiffies_to_usecs(tcp_jiffies32);
> +
> + if (now - ca->curr_min_rtt_timed.time >
> + ROCCET_RTT_LOOKBACK_S * USEC_PER_SEC &&
> + calculate_min_rtt) {
> + u32 new_min_rtt = max(ca->curr_rtt, 1);
> + u32 old_min_rtt = ca->curr_min_rtt_timed.rtt;
> +
> + u32 interpolated_min_rtt =
> + (new_min_rtt * roccet_min_rtt_interpolation_factor +
> + old_min_rtt *
> + (100 - roccet_min_rtt_interpolation_factor)) /
> + 100;
> +
> + ca->curr_min_rtt_timed.rtt = interpolated_min_rtt;
> + ca->curr_min_rtt_timed.time = now;
> + }
If no lower RTT is found for 10 seconds, the algorithm interpolates
the `min_rtt` upwards towards the current RTT.
+ If the path is persistently congested (e.g., a large buffer is
constantly full), the `min_rtt` baseline will drift up.
+ This makes the algorithm less sensitive to queueing delay over
time, potentially defeating the purpose of reducing bufferbloat in the
long run. Contrast this with BBR, which actively drains the queue
(using the ProbeRTT mechanism) to try to find the true physical
minimum RTT.
Can you please add a comment explaining why the ROCCET algorithm takes
this approach, and how the algorithm expects to avoid queues that
ratchet ever higher?
> +/* Update ack rate sampled by 100ms.
> + */
> +static __always_inline void update_ack_rate(struct sock *sk)
> +{
> + struct roccettcp *ca = inet_csk_ca(sk);
> + u32 now = jiffies_to_usecs(tcp_jiffies32);
> + u32 interval = USEC_PER_MSEC * 100;
> +
> + if ((u32)(now - ca->ack_rate.last_rate_time) >= interval) {
> + ca->ack_rate.last_rate_time = now;
> + ca->ack_rate.last_rate = ca->ack_rate.curr_rate;
> + ca->ack_rate.curr_rate = ca->ack_rate.cnt;
> + ca->ack_rate.cnt = 0;
> + } else {
> + ca->ack_rate.cnt += 1;
> + }
Here, `cnt` is incremented by `1` on every call, regardless of the
`acked` value (number of packets ACKed in this event).
+ This measures ACK frequency rather than data delivery rate.
+ This approach is highly sensitive to receiver behavior like
Delayed ACKs, LRO (Large Receive Offload), or GRO (Generic Receive
Offload), where one ACK event might acknowledge many packets.
+ To measure rate, I would suggest accumulating bytes ACKed (or
packets ACKed) rather than just counting the number of ACK events.
> + if (ca->bw_limit.next_check == 0)
> + ca->bw_limit.next_check = now + 5 * ca->curr_rtt;
> +
> + ca->bw_limit.sum_cwnd += tcp_snd_cwnd(tp);
> + ca->bw_limit.sum_acked += acked;
> +
> + if (ca->bw_limit.next_check < now) {
This comparison (ca->bw_limit.next_check < now) does not properly
handle wrapping of the 32-bit timestamps. You probably want to
subtract the two numbers and look at the result, since subtraction
will handle the wrapping. Please see how tcp_cubic uses tcp_jiffies32
for examples of how to do this.
> + /* We send more data as we got acked in the last 5 RTTs */
This seems to have a typo and seems to intend to say: "We sent
significantly more data than we got acked in the last 5 RTTs".
> + if ((ca->bw_limit.sum_cwnd * 100) / ca->bw_limit.sum_acked >=
> + ack_rate_diff_ca)
> + bw_limit_detect = 1;
AFAICT this logic for updating and using ca->bw_limit.sum_cwnd appears
to be mathematically flawed for its stated purpose:
+ `sum_cwnd` is accumulated on every ACK event. Over a period of 5
RTTs, if we assume continuous sending at window size $W$, the number
of ACK events is roughly proportional to $W$. Thus, `sum_cwnd` will be
roughly $5 * num_acks_per_round * W$.
+ `sum_acked` accumulates the number of packets ACKed (`acked`). Over
5 RTTs of continuous sending, this will simply be roughly the number
of packets ACKed, which is roughly $5 * W$ (if the flow is not
application-limited).
+ The quantity `sum_cwnd * 100 / sum_acked` will therefore be roughly
$(5 * num_acks_per_round * W) * 100/ (5 * W) = num_acks_per_round *
100 $, not a measure of bandwidth limitation (it does not tell you if
you are really sending more data than is being ACKed).
+ With the default `ack_rate_diff_ca` of `200`, this condition will
become true for $sum_cwnd * 100 / sum_acked >= 200$, i.e.
$num_acks_per_round * 100 >= 200$. So AFAICT we expect this condition
to be true if there are 2 or more ACKs in a round trip. This makes
`bw_limit_detect` effectively a no-op or always-on trigger rather than
a true detector of queue growth or bandwidth limits.
If you want to really check whether the connection is sending
significantly more data than is being ACKed, then AFAICT you need to
address the following issues:
+ A cwnd is a per-round-trip number, not a per-ACK number (as it is
treated here).
+ Application-limited flows do not always send a full cwnd worth of
data (as the flow is assumed to do here).
+ Data sent is out of phase by one round trip with data ACKed, so if a
connection is growing its sending rate by a factor of X per round trip
then we expect the data sent in a round trip to be X times greater
than the data ACKed in that round trip even if the bottleneck
bandwidth is not saturated yet. So if you want to compare data sent vs
data ACKed, you need to keep this in mind.
Furthermore, AFAICT the ack_rate_diff_ca parameter used by this
algorithm differs massively from the value described in the paper. The
paper says: "If the amount of incoming ACKs over 5 RTTs deviates more
than 20 % from the cum_cwnd over the same time period". AFAICT
ack_rate_diff_ca is 200, thus this code checks for a 200% deviation,
not a 20% deviation.
Did the experiments in the paper use the approach documented in the
paper, or the approach documented in this code? They are very
different, AFAICT.
> +
> + /* reset struct and set next end of period */
> + ca->bw_limit.sum_cwnd = 1;
> +
> + /* set to 1 to avoid division by zero */
> + ca->bw_limit.sum_acked = 1;
Both of these are incorrect ways to reset these fields. Sums should be
reset to 0. To avoid division by zero, check for a denominator of 0
before the division.
> +__bpf_kfunc static u32 roccettcp_recalc_ssthresh(struct sock *sk)
> +{
> + const struct tcp_sock *tp = tcp_sk(sk);
> + struct roccettcp *ca = inet_csk_ca(sk);
> +
> + if (ignore_loss)
> + return tcp_snd_cwnd(tp);
Having a module parameter to ignore loss in this way makes it too easy
for users to cause excessive congestion. I would urge you to remove
that module parameter. Researchers can add that sort of mechanism in
their own code for research.
> +
> + /* Don't exit slow start if loss occurs. */
> + if (tcp_in_slow_start(tp))
> + return tcp_snd_cwnd(tp);
This comment seems incorrect. If roccettcp_recalc_ssthresh() is called
from tcp_init_cwnd_reduction() then AFAICT ssthresh will be set to
cwnd. This should cause tcp_in_slow_start() (return tcp_snd_cwnd(tp)
< tp->snd_ssthresh) to return false. So the flow should no longer be
in slow start. So AFAICT the flow has actually exited slow start.
AFAICT what the comment means to say is something like: "When loss
occurs in slow start, exit slow start but do not decrease cwnd." Is
that what you mean to say?
If so, that sounds dangerous, by itself in isolation.
+ In general, in a loss-based algorithm like CUBIC, ignoring loss in
Slow Start is extremely dangerous. Slow Start is designed to probe
capacity exponentially; if it causes losses, it usually means it has
significantly overshot the available bandwidth.
+ By returning the current `cwnd` as the new `ssthresh`, the algorithm
will not back off properly on loss during Slow Start, potentially
causing massive congestion or severe unfairness to other flows.
Can you please add a comment explaining why you feel this
roccettcp_recalc_ssthresh() behavior is safe, and what it is trying to
achieve, at a high level?
Thanks,
neal
^ permalink raw reply
* Re: [PATCH net-next 2/3] netdevsim: psp: handle the new crypt-offset and spi-threshold get/set operations
From: Willem de Bruijn @ 2026-04-07 21:43 UTC (permalink / raw)
To: Akhilesh Samineni, davem, edumazet, kuba, pabeni, andrew+netdev,
horms, willemb, daniel.zahka
Cc: netdev, linux-kernel, jayakrishnan.udayavarma, ajit.khaparde,
kiran.kella, akhilesh.samineni, sachin.suman
In-Reply-To: <20260406222305.4111170-3-akhilesh.samineni@broadcom.com>
Akhilesh Samineni wrote:
> Implement the crypt-offset and spi-threshold get/set in netdevsim PSP.
>
> Signed-off-by: Akhilesh Samineni <akhilesh.samineni@broadcom.com>
> Reviewed-by: Kiran Kella <kiran.kella@broadcom.com>
> Reviewed-by: Ajit Kumar Khaparde <ajit.khaparde@broadcom.com>
> ---
> drivers/net/netdevsim/netdevsim.h | 2 ++
> drivers/net/netdevsim/psp.c | 6 ++++++
> 2 files changed, 8 insertions(+)
>
> diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
> index c904e14f6b3f..3ad7d42391c0 100644
> --- a/drivers/net/netdevsim/netdevsim.h
> +++ b/drivers/net/netdevsim/netdevsim.h
> @@ -117,6 +117,8 @@ struct netdevsim {
> struct psp_dev *dev;
> u32 spi;
> u32 assoc_cnt;
> + u8 crypt_offset;
Minor: variable names are already not aligned. No need for two spaces.
> + u32 spi_threshold;
> } psp;
>
> struct nsim_bus_dev *nsim_bus_dev;
> diff --git a/drivers/net/netdevsim/psp.c b/drivers/net/netdevsim/psp.c
> index 0b4d717253b0..9098edf00c5c 100644
> --- a/drivers/net/netdevsim/psp.c
> +++ b/drivers/net/netdevsim/psp.c
> @@ -122,6 +122,11 @@ static int
> nsim_psp_set_config(struct psp_dev *psd, struct psp_dev_config *conf,
> struct netlink_ext_ack *extack)
> {
> + struct netdevsim *ns = psd->drv_priv;
> +
> + ns->psp.crypt_offset = conf->crypt_offset;
> + ns->psp.spi_threshold = conf->spi_threshold;
> +
> return 0;
> }
>
> @@ -249,6 +254,7 @@ int nsim_psp_init(struct netdevsim *ns)
> if (err)
> return err;
>
> + ns->psp.spi_threshold = PSP_SPI_THRESHOLD_DEFAULT;
> debugfs_create_file("psp_rereg", 0200, ddir, ns, &nsim_psp_rereg_fops);
> return 0;
> }
> --
> 2.45.4
>
^ permalink raw reply
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