* Re: [PATCH v12 nf-next 3/7] netfilter: nf_flow_table_offload: Add nf_flow_rule_bridge()
From: Eric Woudstra @ 2026-07-08 18:36 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Florian Westphal, Phil Sutter,
Nikolay Aleksandrov, Ido Schimmel, Kuniyuki Iwashima,
Stanislav Fomichev, Samiullah Khawaja, Hangbin Liu, Krishna Kumar,
Martin Karsten, netdev, netfilter-devel, bridge
In-Reply-To: <ak4dAXHDmTDRr7-b@chamomile>
On 7/8/26 11:48 AM, Pablo Neira Ayuso wrote:
> Hi,
>
> On Tue, Jul 07, 2026 at 11:10:41AM +0200, Eric Woudstra wrote:
>> Add nf_flow_rule_bridge().
>>
>> It only calls the common rule and adds the redirect.
>
> I decided to use the new _unsupp() function, so we don't pretend
> bridge hw offload is already supported. We will need a driver before
> we can add this, this stub does not provide much. I guess your goal
> was just to avoid a crash here.
>
No, I am already using hw_offload between bridged interfaces
on the mt7986 succesfully for almost 2 years.
It works dsa-port to direct interface (lan1 to eth1 on Bananapi R3) and
between direct interfaces (eth0 to eth1 on Bananapi-R3-mini)
It can also be tested with my bridge_fastpath.sh selftest script.
This script uses veth-device pairs to test the software fastpath.
It can also use 2 real interfaces interconnected in a loop of copper,
when chosen with commandline arguments. Then it tests software- and
hardware-fastpath. It also tests many different scenarios.
So this is why I've added it, as it is already functional. If a software
fastpath is setup correctly, the hardware fastpath is also functional.
>> Reviewed-by: Nikolay Aleksandrov <razor@blackwall.org>
>> Signed-off-by: Eric Woudstra <ericwouds@gmail.com>
>> ---
>> include/net/netfilter/nf_flow_table.h | 3 +++
>> net/netfilter/nf_flow_table_offload.c | 13 +++++++++++++
>> 2 files changed, 16 insertions(+)
>>
>> diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
>> index 7b23b245a5a86..5c6e3b65ae85b 100644
>> --- a/include/net/netfilter/nf_flow_table.h
>> +++ b/include/net/netfilter/nf_flow_table.h
>> @@ -368,6 +368,9 @@ void nf_flow_table_offload_flush_cleanup(struct nf_flowtable *flowtable);
>> int nf_flow_table_offload_setup(struct nf_flowtable *flowtable,
>> struct net_device *dev,
>> enum flow_block_command cmd);
>> +int nf_flow_rule_bridge(struct net *net, struct flow_offload *flow,
>> + enum flow_offload_tuple_dir dir,
>> + struct nf_flow_rule *flow_rule);
>> int nf_flow_rule_route_ipv4(struct net *net, struct flow_offload *flow,
>> enum flow_offload_tuple_dir dir,
>> struct nf_flow_rule *flow_rule);
>> diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
>> index 002ec15d988bd..5566ebda7b7d3 100644
>> --- a/net/netfilter/nf_flow_table_offload.c
>> +++ b/net/netfilter/nf_flow_table_offload.c
>> @@ -740,6 +740,19 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
>> return 0;
>> }
>>
>> +int nf_flow_rule_bridge(struct net *net, struct flow_offload *flow,
>> + enum flow_offload_tuple_dir dir,
>> + struct nf_flow_rule *flow_rule)
>> +{
>> + if (nf_flow_rule_route_common(net, flow, dir, flow_rule) < 0)
>> + return -1;
>> +
>> + flow_offload_redirect(net, flow, dir, flow_rule);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(nf_flow_rule_bridge);
>> +
>> int nf_flow_rule_route_ipv4(struct net *net, struct flow_offload *flow,
>> enum flow_offload_tuple_dir dir,
>> struct nf_flow_rule *flow_rule)
>> --
>> 2.53.0
>>
^ permalink raw reply
* Re: [PATCH 5/9] ax88179_178a: Add support for ethtool pause parameter configuration
From: Birger Koblitz @ 2026-07-08 18:35 UTC (permalink / raw)
To: Maxime Chevallier, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: linux-usb, netdev, linux-kernel
In-Reply-To: <587499ee-d87e-4056-8d2a-8fda2ef3f0f1@bootlin.com>
On 7/1/26 12:04, Maxime Chevallier wrote:
> pause autoneg is not the same as link-wide autoneg. If link autoneg is disabled,
> you have to keep track on how pause autoneg was configured by user, so that this
> can be re-applied when link aneg gets re-enabled.
>
Will be fixed in v2 as suggested.
> The best way to have this correct is to use phylink, but for that you'd need to
> have a proper PHY driver instead of using the mii_ API here.
phylink was discussed elsewhere.
Birger
^ permalink raw reply
* Re: [PATCH 3/9] ax88179_178a: Add support for AX88179A MMD access
From: Birger Koblitz @ 2026-07-08 18:33 UTC (permalink / raw)
To: Maxime Chevallier, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni
Cc: linux-usb, netdev, linux-kernel
In-Reply-To: <b01b4c18-bb2d-413b-a993-4a9cf4d50549@bootlin.com>
Thanks Maxime for reviewing this patch!
On 7/1/26 11:53, Maxime Chevallier wrote:
> ADVERTISE_RESV isn't an unknown bit, it's been added in 802.3 as ADVERTISE_XNP
> for Extended Next Page, see :
>
> commit e7a62edd34b1 ("net: phy: qcom: at803x: Use the correct bit to disable extended next page")
>
> we shoudln't use ADVERTISE_RESV anymore in-kernel (there are a few callsites left).
>
> As this seems to be a vendor-specific behaviour for that bit, please add a
> local #define for it in this driver, to make it clear that this is a device-specific
> value.
Will fix as suggested in v2 call it AX_ADVERTISE_2500.
And btw, I have to mention that I used the cross-referencing of bootlin
a lot for writing this patch-series, and it was enormously helpful. Thanks for that tool!
Birger
^ permalink raw reply
* Re: [PATCH net-next] gtp: annotate PDP lookups under RTNL
From: Simon Horman @ 2026-07-08 18:32 UTC (permalink / raw)
To: Paolo Abeni
Cc: Pablo Neira Ayuso, Runyu Xiao, laforge, andrew+netdev, davem,
edumazet, kuba, osmocom-net-gprs, netdev, linux-kernel,
jianhao.xu
In-Reply-To: <8d5fe948-6979-46be-866a-69a98ee391d8@redhat.com>
On Wed, Jul 08, 2026 at 01:10:58PM +0200, Paolo Abeni wrote:
> On 7/8/26 12:35 PM, Simon Horman wrote:
> > On Tue, Jul 07, 2026 at 04:51:12PM +0200, Pablo Neira Ayuso wrote:
> >> On Tue, Jul 07, 2026 at 03:28:20PM +0100, Simon Horman wrote:
> >>> On Wed, Jul 01, 2026 at 08:39:25PM +0800, Runyu Xiao wrote:
> >>>> The GTP PDP lookup helpers are shared by RCU-protected data and report
> >>>> paths and RTNL-protected control paths such as gtp_genl_new_pdp(). The
> >>>> helpers walk RCU hlists, but they do not currently pass the RTNL
> >>>> condition for the control-path lookups.
> >>>>
> >>>> Pass lockdep_rtnl_is_held() to the PDP hlist iterators. Existing
> >>>> RCU-reader callers remain valid because the RCU-list macros also accept
> >>>> an active RCU read-side section; the added condition only documents the
> >>>> non-RCU protection already used by RTNL control paths.
> >>>>
> >>>> This was found by our static analysis tool and then manually reviewed
> >>>> against the current tree. The dynamic triage evidence is a
> >>>> target-matched CONFIG_PROVE_RCU_LIST warning; the change is limited
> >>>> to documenting the existing protection contract.
> >>>>
> >>>> This is a lockdep annotation cleanup. It does not change PDP lifetime or
> >>>> hash updates.
> >>>>
> >>>> Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
> >>>
> >>> Reviewed-by: Simon Horman <horms@kernel.org>
> >>>
> >>> There is an AI-generated review of this patch available on sashko.dev.
> >>> While I don't believe that the issues raised there should impede progress
> >>> of this patch you may want to look into them as possible follow-up.
> >>
> >> This patch refers to the rtnl_lock, but it is the genetlink mutex that
> >> protects updates on the PDP context list.
> >>
> >> Then, from packet path, rcu lookups are performed.
> >>
> >> I think this patch is not correct.
> >
> > Hi Pablo,
> >
> > Of course you are correct.
> > Sorry for not realising this earlier.
>
> Human slop here made me wrongly apply this patch. Could either of you
> please share a formal revert?
Sure, will do.
^ permalink raw reply
* Re: [PATCH v5 net 2/7] i40e: avoid null ptr dereference in i40e_ptp_stop()
From: Simon Horman @ 2026-07-08 18:31 UTC (permalink / raw)
To: Maciej Fijalkowski
Cc: intel-wired-lan, netdev, magnus.karlsson, kuba, pabeni,
przemyslaw.kitszel, jacob.e.keller
In-Reply-To: <ak6HOhJh/pxdloEs@boxer>
On Wed, Jul 08, 2026 at 07:22:02PM +0200, Maciej Fijalkowski wrote:
> On Wed, Jul 08, 2026 at 04:04:56PM +0100, Simon Horman wrote:
> > This is an AI-generated review of your patch. The human sending this
> > email has considered the AI review valid, or at least plausible.
> > Full review at: https://sashiko.dev
> > ---
> > i40e: avoid null ptr dereference in i40e_ptp_stop()
> >
> > Check if main_vsi is not null before calling dev_info() to avoid a null
> > pointer dereference when i40e_vsi_clear() frees the main VSI during rebuild.
> >
> > > diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
> > > index ff62b5f2c8150..ca93df4d67859 100644
> > > --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c
> > > +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
> > > @@ -1556,8 +1556,9 @@ void i40e_ptp_stop(struct i40e_pf *pf)
> > > if (pf->ptp_clock) {
> > > ptp_clock_unregister(pf->ptp_clock);
> > > pf->ptp_clock = NULL;
> > > - dev_info(&pf->pdev->dev, "%s: removed PHC on %s\n", __func__,
> > > - main_vsi->netdev->name);
> > > + if (main_vsi)
> > > + dev_info(&pf->pdev->dev, "%s: removed PHC on %s\n", __func__,
> > > + main_vsi->netdev->name);
> >
> > [Severity: High]
> > This isn't a bug introduced by this patch, but this lockless check appears
> > to introduce a time-of-check to time-of-use race condition.
>
> Thanks Simon I saw these, however since all of the issues are classified
> as pre-existing issues IMHO these changes are okay to be taken and then
> we could go with another round of fixes.
Yes, agreed. Sorry about the noise.
^ permalink raw reply
* Re: [PATCH 2/9] ax88179_178a: Add HW support for AX179A-based chips
From: Birger Koblitz @ 2026-07-08 18:27 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, linux-usb, netdev, linux-kernel
In-Reply-To: <6b368510-05d4-4d73-b588-8002d0d59915@lunn.ch>
On 7/1/26 17:05, Andrew Lunn wrote:
>> #include <linux/usb/usbnet.h>
>> #include <uapi/linux/mdio.h>
>> #include <linux/mdio.h>
>> +#include <linux/if_vlan.h>
>
> Does this patch require this header?
Yes, because of the use of __vlan_hwaccel_put_tag later in the patch.
>
>> @@ -414,7 +570,6 @@ static int ax88179_suspend(struct usb_interface *intf, pm_message_t message)
>>
>> usbnet_suspend(intf, message);
>>
>> - /* Enable WoL */
>> if (priv->wolopts) {
>
> Please try to avoid changes like this.
Fixed in v2.
>
>> /* Force bulk-in zero length */
>> ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
>> - 2, 2, &tmp16);
>> + 2, 2, &tmp16);
>>
>> tmp16 |= AX_PHYPWR_RSTCTL_BZ | AX_PHYPWR_RSTCTL_IPRL;
>> ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
>> - 2, 2, &tmp16);
>> + 2, 2, &tmp16);
>
> Please put white space changes in another patch.
Will be removed from v2.
>
>> + /* Initialize MII structure */
>> + dev->mii.dev = dev->net;
>> + dev->mii.mdio_read = ax88179_mdio_read;
>> + dev->mii.mdio_write = ax88179_mdio_write;
>> + dev->mii.phy_id_mask = 0xff;
>> + dev->mii.reg_num_mask = 0xff;
>> + dev->mii.phy_id = 0x03;
>
> If this device is going to have a long term future, it really should
> move to phylink.
As discussed previously, I started converting the code to phylink,
but for the PHYs implemented as IP-cores in the the AX88179A-based chips,
the behaviour of the PHYs is so different from their discrete counterparts
because of other firmware used, that even if they have the same PHY-ID as
their discrete counterparts, they behave entirely differently and in a
way that is adapted to being used via USB as part of a USB-Ethernet
adapter.
Only few of the USB-Ethernet drivers presently use phylink (asix,
lan78xx), btw.
Birger
^ permalink raw reply
* Re: [PATCH RFC net-next 3/3] net: dsa: mxl862xx: add devlink flash_update and info_get
From: Manuel Ebner @ 2026-07-08 18:25 UTC (permalink / raw)
To: Daniel Golle, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
linux-kernel, netdev
In-Reply-To: <ak0KOrM5E2_sPIHf@makrotopia.org>
Hi Daniel,
On Tue, 2026-07-07 at 16:16 +0200, Daniel Golle wrote:
> Implement runtime firmware upgrade via "devlink dev flash" and version
> reporting via "devlink dev info":
>
> devlink dev info mdio_bus/<bus>/<addr>
> devlink dev flash mdio_bus/<bus>/<addr> file <firmware.bin>
>
> The driver sends SYS_MISC_FW_UPDATE to enter MCUboot rescue mode,
> transfers the signed image over the SB PDI bulk-transfer protocol
> (clause-22 SMDIO), waits for the switch to reboot, then schedules
> device_reprobe() for a clean remove()+probe() cycle.
This could be split up into two sentences.
> Before the transfer begins the driver closes all conduit interfaces
> and marks every netdev (user and conduit) not-present via
> netif_device_detach() so that userspace cannot bring ports back up
> during the ~15 minute flash process. Progress is reported through
> devlink status notifications. Once the FW_UPDATE command has been
> sent the switch is in MCUboot mode and normal operation can only be
> restored by a reprobe, so the driver always schedules one regardless
> of transfer outcome.
>
> The reprobe work item is dynamically allocated (following the iwlwifi
> pattern)
I'm not familiar with the net subsystem, if the iwlwifi pattern is not
trivial consider adding a link or a explanation.
> because device_reprobe() triggers remove() which frees the
> devm-managed priv while the work is still executing.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> ---
> drivers/net/dsa/mxl862xx/Makefile | 2 +-
> drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 1 +
> drivers/net/dsa/mxl862xx/mxl862xx-fw.c | 434 +++++++++++++++++++++++
> drivers/net/dsa/mxl862xx/mxl862xx-fw.h | 15 +
> drivers/net/dsa/mxl862xx/mxl862xx-host.c | 7 +
> drivers/net/dsa/mxl862xx/mxl862xx.c | 3 +
> drivers/net/dsa/mxl862xx/mxl862xx.h | 2 +
> 7 files changed, 463 insertions(+), 1 deletion(-)
> create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-fw.c
> create mode 100644 drivers/net/dsa/mxl862xx/mxl862xx-fw.h
>
> diff --git a/drivers/net/dsa/mxl862xx/Makefile b/drivers/net/dsa/mxl862xx/Makefile
> index a7be0e6669df..bccac0d0f703 100644
> --- a/drivers/net/dsa/mxl862xx/Makefile
> +++ b/drivers/net/dsa/mxl862xx/Makefile
> @@ -1,3 +1,3 @@
> # SPDX-License-Identifier: GPL-2.0
> obj-$(CONFIG_NET_DSA_MXL862) += mxl862xx_dsa.o
> -mxl862xx_dsa-y := mxl862xx.o mxl862xx-host.o mxl862xx-phylink.o
> +mxl862xx_dsa-y := mxl862xx.o mxl862xx-host.o mxl862xx-phylink.o mxl862xx-fw.o
> diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
> b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
> index c87a955c13c4..e2aa2934e9e1 100644
> --- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
> +++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
> @@ -70,6 +70,7 @@
> #define INT_GPHY_READ (GPY_GPY2XX_MAGIC + 0x1)
> #define INT_GPHY_WRITE (GPY_GPY2XX_MAGIC + 0x2)
>
> +#define SYS_MISC_FW_UPDATE (SYS_MISC_MAGIC + 0x1)
> #define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x2)
>
> #define MXL862XX_XPCS_PCS_CONFIG (MXL862XX_XPCS_MAGIC + 0x1)
> diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-fw.c b/drivers/net/dsa/mxl862xx/mxl862xx-
> fw.c
> new file mode 100644
> index 000000000000..7cd4a462667b
> --- /dev/null
> +++ b/drivers/net/dsa/mxl862xx/mxl862xx-fw.c
> @@ -0,0 +1,434 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Firmware flash and devlink support for MaxLinear MxL862xx
If you have a data sheet for the device please add a link.
> + *
> + * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
> + *
> + * Usage:
> + * # Query running firmware version:
> + * devlink dev info mdio_bus/<bus>/<addr>
> + *
> + * # Flash new firmware (all ports are taken down automatically):
> + * devlink dev flash mdio_bus/<bus>/<addr> file <firmware.bin>
> + *
> + * The flash process takes approximately 15 minutes.
Does the user get informed about the wait?
> Progress is
> + * reported via devlink status notifications. After a successful (or
> + * failed) flash the driver reprobes the device automatically.
> + */
>
> [...]
> +
> +/* Timeouts */
> +#define MXL862XX_FW_READY_TIMEOUT_MS 30000
> +#define MXL862XX_FW_ACK_TIMEOUT_MS 5000
> +#define MXL862XX_FW_ERASE_TIMEOUT_MS 300000 /* flash erase is very slow */
Slow is relative.
Can you give an erase speed in MB/s or time for the whole memory?
> [...]
> +
> + dev_info(ds->dev, "flash: running firmware %u.%u.%u\n",
> + priv->fw_version.major, priv->fw_version.minor,
> + priv->fw_version.revision);
> +
> + /* Close all user and CPU ports while the firmware is still
> + * alive. dev_close() on user ports triggers multicast group
> + * leave and host MDB/FDB removal on the CPU port through the
> + * normal DSA callbacks so the core's tracking lists are
> + * drained before we enter MCUboot. Then mark user ports
> + * not-present so userspace cannot bring them back up during
> + * the (slow) flash process. The conduit is only closed, not
(slow) -> up to 20 minutes long flash process.
or -> circa 15 minutes long flash process.
> + * detached -- it is owned by the Ethernet MAC driver and
> + * dev_open() during reprobe must be able to bring it back.
> + */
> +[...]
> + /* Silently discard all API commands during the teardown that
> + * reprobe triggers -- the switch firmware has been reset and
> + * has no knowledge of the old configuration.
> + */
> + priv->skip_teardown = true;
> +
> + reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL);
-> kzalloc_obj(*reprobe); - GFP_KERNEL is the default, therefore omitted.
as suggested in 2932ba8d9c99 ("slab: Introduce kmalloc_obj() and family")
Did you skip checkpatch or is it out of date?
Anyway thanks
Manuel
> [...]
^ permalink raw reply
* Re: [PATCH net-next v6 3/6] pds_core: add PLDM firmware update support via devlink flash
From: Rao, Nikhil @ 2026-07-08 18:25 UTC (permalink / raw)
To: Paolo Abeni
Cc: netdev, kuba, brett.creeley, eric.joyner, andrew+netdev, davem,
edumazet, jacob.e.keller, nikhil.rao
In-Reply-To: <20260702091100.93838-1-pabeni@redhat.com>
On 7/2/2026 2:11 AM, Paolo Abeni wrote:
> From: AI Reviewer <ai@example.com>
>
> 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://netdev-ai.bots.linux.dev/sashiko/
> ---
> pds_core: add PLDM firmware update support via devlink flash
>
> Implements PLDM firmware update via devlink flash using the upstream
> pldmfw API. Adds whole-package and per-component update paths, and
> introduces a deferred-DMA list that releases buffers after timed-out
> devcmds. A few questions inline.
>
>> diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h
>> index b7fe9ad73349..c686f0bbbaeb 100644
>> --- a/drivers/net/ethernet/amd/pds_core/core.h
>> +++ b/drivers/net/ethernet/amd/pds_core/core.h
>> @@ -23,6 +23,14 @@
>> #define PDSC_SETUP_RECOVERY false
>> #define PDSC_SETUP_INIT true
>>
>> +struct pdsc_deferred_dma {
>> + struct list_head list;
>> + dma_addr_t dma_addr;
>> + void *va;
>> + size_t size;
>> + enum dma_data_direction dir;
>> +};
>> +
>
> [ ... ]
>
>> diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c
>> index 5c0ca3d0b000..6082b28915db 100644
>> --- a/drivers/net/ethernet/amd/pds_core/dev.c
>> +++ b/drivers/net/ethernet/amd/pds_core/dev.c
>> @@ -206,15 +206,53 @@ static int __pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,
>> else
>> memcpy_fromio(comp, &pdsc->cmd_regs->comp, sizeof(*comp));
>>
>> + if (err != -ETIMEDOUT && err != -EAGAIN)
>> + pdsc_deferred_dma_free(pdsc);
>> +
>> return err;
>> }
>
> [High]
> Can the deferred-DMA scheme actually guarantee that the device has
> finished accessing the buffer before it is unmapped and freed?
>
> The trigger for releasing a deferred entry is "the next devcmd returned
> something other than -ETIMEDOUT/-EAGAIN", plus pdsc_remove() and
> pdsc_reset_prepare(). None of these provide a hardware-level fence that
> the previous in-flight DMA has drained. In the remove/reset paths the
> free runs after pci_disable_device(), which clears bus-master but does
> not synchronously drain pending transactions.
>
> The DMA_FROM_DEVICE case in pdsc_get_component_info() is particularly
> exposed because the buffer is plain kernel slab memory: a late device
> write after kfree would corrupt unrelated allocations.
Will fix in v7 by adding a config space read after pci_disable_device()
in pdsc_teardown() before freeing deferred DMA buffers. PCI transaction
ordering rules guarantee that a read completion cannot return until all
prior device-initiated memory writes have completed. This ensures
in-flight DMA has drained before buffers are freed.
>
> Also note that pdsc_send_package_data(), pdsc_get_component_info(), and
> pdsc_flash_component_chunk() only defer on -EAGAIN/-ETIMEDOUT and
> immediately unmap+kfree on any other error (-EIO, -ENXIO, ...). Is the
> device guaranteed not to have started any DMA in those cases?
Yes. When devcmd returns -EIO or -ENXIO (or any non-timeout error), the
command has completed, the MMIO read that retrieved the completion
status acts as a DMA fence. The immediate free is safe because command
completion implies DMA finished.
>
>> +void pdsc_deferred_dma_add(struct pdsc *pdsc, struct pdsc_deferred_dma *entry,
>> + dma_addr_t dma_addr, void *va, size_t size,
>> + enum dma_data_direction dir)
>> +{
>> + entry->dma_addr = dma_addr;
>> + entry->va = va;
>> + entry->size = size;
>> + entry->dir = dir;
>> +
>> + spin_lock(&pdsc->deferred_dma_lock);
>> + list_add_tail(&entry->list, &pdsc->deferred_dma_list);
>> + spin_unlock(&pdsc->deferred_dma_lock);
>> +}
>> +
>> +void pdsc_deferred_dma_free(struct pdsc *pdsc)
>> +{
>> + struct pdsc_deferred_dma *entry, *tmp;
>> +
>> + spin_lock(&pdsc->deferred_dma_lock);
>> + list_for_each_entry_safe(entry, tmp, &pdsc->deferred_dma_list, list) {
>> + dma_unmap_single(pdsc->dev, entry->dma_addr,
>> + entry->size, entry->dir);
>> + kfree(entry->va);
>> + list_del(&entry->list);
>> + kfree(entry);
>> + }
>> + spin_unlock(&pdsc->deferred_dma_lock);
>> +}
>
> [Medium]
> Is it intentional to hold deferred_dma_lock across dma_unmap_single()
> and two kfree()s per entry? This runs from __pdsc_devcmd_locked() on
> every successful devcmd while devcmd_lock is also held, and
> dma_unmap_single() can trigger IOMMU IOTLB invalidations. Would
> list_splice_init() under the lock followed by iteration on a detached
> list scale better when many entries have accumulated?
Will fix in v7, using list_splice_init() to detach the list under the
lock, then iterate and free without holding the lock.
> [ ... ]
>
>> diff --git a/drivers/net/ethernet/amd/pds_core/fw.c b/drivers/net/ethernet/amd/pds_core/fw.c
>> index fa626719e68d..433b0a8df4c1 100644
>> --- a/drivers/net/ethernet/amd/pds_core/fw.c
>> +++ b/drivers/net/ethernet/amd/pds_core/fw.c
>
> [ ... ]
>
>> +#define PDSC_FW_COMPONENT_PREFIX "fw."
>> +#define PDSC_FW_COMPONENT_FULL_NAME_BUFLEN \
>> + (sizeof(PDSC_FW_COMPONENT_PREFIX) + PDS_CORE_FW_COMPONENT_NAME_BUFLEN)
>> +
>> +/* Driver-defined component type to name mapping.
>> + * PDS_CORE_FW_TYPE_MAIN is NULL - handled specially as "fw" without prefix.
>> + */
>> +static const char * const pdsc_fw_type_names[] = {
>> + [PDS_CORE_FW_TYPE_MAIN] = NULL,
>> + [PDS_CORE_FW_TYPE_BOOT] = "bootloader",
>> + [PDS_CORE_FW_TYPE_CPLD] = "cpld",
>> + [PDS_CORE_FW_TYPE_SECURE] = "secure",
>> + [PDS_CORE_FW_TYPE_FPGA] = "fpga",
>> + [PDS_CORE_FW_TYPE_SUC_MAIN] = "suc",
>> + [PDS_CORE_FW_TYPE_SUC_BOOT] = "suc.bootloader",
>> + [PDS_CORE_FW_TYPE_UBOOT] = "uboot",
>> +};
>
> [Low, Low]
> The documentation table added in this same patch lists fw.gold as a
> driver-defined component name, and the kerneldoc on
> enum pds_core_fw_component_type says gold variants are "reported with
> a .gold suffix (e.g., fw.gold)". The table above has no gold entry, so
> pdsc_name_to_fw_type("fw.gold") returns 0 and pdsc_pldm_firmware_update()
> rejects the request with -ENOENT. Should fw.gold either be wired up or
> the docs clarify that gold is read-only?
Will update documentation in v7. The component for fw.gold is "fw". Gold
firmware can be updated by specifying component "fw" with a goldfw
package file.
> PDSC_FW_COMPONENT_FULL_NAME_BUFLEN is defined here but never used;
> pdsc_flash_component() uses an ad-hoc sizeof(PDSC_FW_COMPONENT_PREFIX) +
> 16 instead. Should this macro be wired up or dropped?
>
Will fix in v7, using the PDSC_FW_COMPONENT_FULL_NAME_BUFLEN macro in
pdsc_flash_component() for the component_name_buf array size.
> [ ... ]
>
>> +int pdsc_get_component_info(struct pdsc *pdsc)
>> +{
>> + union pds_core_dev_cmd cmd = {
>> + .get_component_info.opcode = PDS_CORE_CMD_GET_COMPONENT_INFO,
>> + .get_component_info.ver = 1,
>> + };
>
> [ ... ]
>
>> + if (comp.get_component_info.ver == 0) {
>> + /* Don't support backward compatibility as version 0 has
>> + * alignment issues, so give a hint to users to update
>> + * their firmware
>> + */
>> + dev_warn_once(pdsc->dev,
>> + "Incompatible get_component_info version %u reported by firmware\n",
>> + comp.get_component_info.ver);
>> + err = 0;
>> + goto out;
>> + }
>
> [Low]
> This branch returns 0 without populating pdsc->fw_components. The caller
> pdsc_pldm_firmware_update() uses:
>
> if (!pdsc->fw_components.num_components) {
> err = pdsc_get_component_info(pdsc);
> if (err) { ... return err; }
> }
>
> if (params->component) {
> u8 type = pdsc_name_to_fw_type(params->component);
>
> if (!type || !pdsc_component_type_exists(pdsc, type))
> return -ENOENT;
> }
>
> so on a device that reports ver == 0, the cache never populates and
> every per-component flash returns -ENOENT. Should this branch return an
> explicit error instead, or otherwise prevent the per-component lookup
> from running?
>
The dev_warn_once message tells the user to update their firmware.
Per-component flash will fail with -ENOENT, but the user needs to
upgrade firmware anyway to use this feature. Returning an explicit error
here would prevent the basic flash operation from working on old firmware.
> [ ... ]
>
>> +static int pdsc_devcmd_send_component(struct pdsc *pdsc,
>> + struct pds_core_flash_component *info,
>> + u16 info_sz, dma_addr_t addr, u32 length,
>> + u32 offset, u16 slot_id,
>> + union pds_core_dev_comp *comp)
>> +{
>> + union pds_core_dev_cmd cmd = {
>> + .send_component.opcode = PDS_CORE_CMD_SEND_COMPONENT,
>> + .send_component.ver = 1,
>> + .send_component.operation = PDS_CORE_SEND_COMPONENT_START,
>> + .send_component.data_pa = cpu_to_le64(addr),
>> + .send_component.data_len = cpu_to_le32(length),
>> + .send_component.offset = cpu_to_le32(offset),
>> + .send_component.slot_id = slot_id,
>> + };
>> + unsigned long timeout = 300 * HZ;
>> + unsigned long start_time;
>> + unsigned long end_time;
>> + int err;
>> +
>> + start_time = jiffies;
>> + end_time = start_time + timeout;
>> + do {
>> + /* prevent noisy/benign devcmd failures */
>> + err = pdsc_devcmd_with_data_nomsg(pdsc, &cmd, info, info_sz,
>> + comp, 60);
>> + if (err != -EAGAIN)
>> + break;
>> +
>> + /* if required, subsequent commands check status of
>> + * PDS_CORE_CMD_SEND_COMPONENT command, which returns
>> + * EAGAIN while the command is still running,
>> + * else we get the final command status.
>> + */
>> + cmd.send_component.operation = PDS_CORE_SEND_COMPONENT_STATUS;
>> + msleep(20);
>> + } while (time_before(jiffies, end_time));
>
> [Low]
> In the STATUS retry phase, only cmd.send_component.operation changes
> between iterations; data_pa, data_len, offset, and slot_id remain set
> to the original START values, and pdsc_devcmd_with_data_nomsg() also
> re-issues memcpy_toio(&pdsc->cmd_regs->data, info, info_sz) on every
> poll (potentially up to ~15000 iterations over the 300s timeout at 20ms
> cadence). Does the firmware ignore data_pa/data_len on the STATUS
> opcode, or could it re-initiate DMA from the same buffer? Could the
> status poll send just the operation field?
>
Firmware ignores data_pa/data_len/offset on STATUS operation. Only the
operation field is meaningful for polling completion status.
> [ ... ]
>
>> +static int pdsc_finalize_update(struct pldmfw *context)
>> +{
> [ ... ]
>> + start_time = jiffies;
>> + end_time = start_time + (PDSC_FW_INSTALL_TIMEOUT * HZ);
>> + do {
>> + err = pdsc_devcmd_finalize_update(pdsc);
>> + if (!err || err != -EAGAIN)
>> + break;
>> +
>> + dev_dbg(dev, "retrying finalize_update: %pe\n", ERR_PTR(err));
>> + msleep(20);
>> + } while (time_before(jiffies, end_time) && err == -EAGAIN);
>
> [Low]
> Is the !err in if (!err || err != -EAGAIN) intentional? err == 0 already
> satisfies err != -EAGAIN, so the condition reduces to err != -EAGAIN.
Will fix in v7, simplified to just if (err != -EAGAIN).
> [ ... ]
>
>> +static int pdsc_pldm_firmware_update(struct pdsc *pdsc,
>> + struct devlink_flash_update_params *params,
>> + struct netlink_ext_ack *extack,
>> + const struct firmware *fw)
>> +{
>> + struct pds_core_fwu_priv priv = {};
>> + int err;
>> +
>> + if (!pdsc->fw_components.num_components) {
>> + err = pdsc_get_component_info(pdsc);
>> + if (err) {
>> + dev_err(pdsc->dev,
>> + "Failed to get component info: %pe\n",
>> + ERR_PTR(err));
>> + return err;
>> + }
>> + }
>> +
>> + if (params->component) {
>> + u8 type = pdsc_name_to_fw_type(params->component);
>> +
>> + if (!type || !pdsc_component_type_exists(pdsc, type))
>> + return -ENOENT;
>> + }
>> +
>> + INIT_LIST_HEAD(&priv.components);
>> + priv.context.ops = &pdsc_pldmfw_ops;
>> + priv.context.dev = pdsc->dev;
>> + priv.params = params;
>> + priv.pdsc = pdsc;
>> +
>> + err = pldmfw_flash_image(&priv.context, fw);
>> + pdsc_free_fwu_priv(&priv);
>> +
>> + return err;
>> +}
>
> [Medium]
> The extack parameter is accepted here and struct pds_core_fwu_priv
> declares a netlink_ext_ack *extack field, but priv.extack is never
> assigned, and none of the PLDM callbacks (pdsc_match_record_descs,
> pdsc_send_package_data, pdsc_send_component_table, pdsc_flash_component,
> pdsc_finalize_update) call NL_SET_ERR_MSG_MOD(). The legacy path uses
> extack at multiple call sites ("Firmware wait timed out", "Segment
> download failed", ...). Was the intent to propagate extack diagnostics
> on the PLDM path too, including for the -ENOENT returned above?
Will fix in v7 by adding NL_SET_ERR_MSG_MOD calls to PLDM error paths.
>
>> +int pdsc_firmware_update(struct pdsc *pdsc,
>> + struct devlink_flash_update_params *params,
>> + struct netlink_ext_ack *extack)
>> +{
>> + int err;
>> +
>> + if (pdsc->dev_ident.version >= PDS_CORE_IDENTITY_VERSION_2 &&
>> + pdsc->dev_ident.capabilities &
>> + cpu_to_le64(PDS_CORE_DEV_CAP_PLDM_FW_UPDATE))
>> + err = pdsc_pldm_firmware_update(pdsc, params, extack,
>> + params->fw);
>> + else
>> + err = pdsc_legacy_firmware_update(pdsc, params->fw, extack);
>> +
>> + /* Invalidate cached component info so next info_get refreshes */
>> + pdsc->fw_components.num_components = 0;
>> +
>> + return err;
>> +}
>
> [Medium]
> What happens here when a user runs:
>
> devlink dev flash pci/0000:b5:00.0 file foo component fw.cpld
>
> against a device that does not advertise PDS_CORE_DEV_CAP_PLDM_FW_UPDATE?
> The dispatcher falls through to pdsc_legacy_firmware_update(pdsc,
> params->fw, extack), which has no notion of components and proceeds to
> flash the entire image.
>
> The commit message says:
>
> Not all components support per-component update - devlink will
> reject the request if the specified component cannot be updated.
>
> Should the dispatcher return -EOPNOTSUPP (or similar) when
> params->component != NULL on the legacy path, to match that promise?
The legacy path should reject component parameters, -net fix [1]
>
>> diff --git a/include/linux/pds/pds_core_if.h b/include/linux/pds/pds_core_if.h
>> index 619186f26b5b..5a1fafaccf20 100644
>> --- a/include/linux/pds/pds_core_if.h
>> +++ b/include/linux/pds/pds_core_if.h
>> @@ -279,11 +299,20 @@ enum pds_core_fw_control_oper {
>> PDS_CORE_FW_GET_LIST = 7,
>> };
>>
>> +/**
>> + * enum pds_core_fw_slot - Firmware slot identifiers
>> + * @PDS_CORE_FW_SLOT_INVALID: Let firmware select slot based on package metadata
>> + * @PDS_CORE_FW_SLOT_A: Primary firmware slot A
>> + * @PDS_CORE_FW_SLOT_B: Primary firmware slot B
>> + * @PDS_CORE_FW_SLOT_GOLD: Gold/recovery firmware slot
>> + * @PDS_CORE_FW_SLOT_MAX: Sentinel value indicating no slot resolved
>> + */
>> enum pds_core_fw_slot {
>> PDS_CORE_FW_SLOT_INVALID = 0,
>> PDS_CORE_FW_SLOT_A = 1,
>> PDS_CORE_FW_SLOT_B = 2,
>> PDS_CORE_FW_SLOT_GOLD = 3,
>> + PDS_CORE_FW_SLOT_MAX = 0xff,
>> };
>
> [Low]
> PDS_CORE_FW_SLOT_MAX is added with value 0xff to this wire-protocol
> enum but has no references anywhere. Should it be wired up to something
> or dropped before the enum value is locked in by firmware?
Firmware uses this value internally as a sentinel. The driver doesn't
branch on it (relies on response_code), but defining it prevents future
enum additions from colliding with 0xff.
Thanks,
Nikhil
[1]
https://lore.kernel.org/netdev/20260708163649.128620-1-nikhil.rao@amd.com
^ permalink raw reply
* Re: [PATCH 1/9] ax88179_178a: Fix endianness of pause watermark register
From: Birger Koblitz @ 2026-07-08 18:23 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, linux-usb, netdev, linux-kernel
In-Reply-To: <b43415e2-9248-4578-a19b-d1766dcc34fa@lunn.ch>
Thanks for reviewing this Andrew!
On 7/1/26 16:57, Andrew Lunn wrote:
> On Wed, Jul 01, 2026 at 07:42:47AM +0200, Birger Koblitz wrote:
>> The 16-bit pause watermark register is little endian as
>> described in the ASIX 4.1.0 out-of-tree driver. Correct the
>> register byte sequence but also swap the configuration values
>> used in the code in order to keep the current behaviour.
>>
>> The endianness is relevant for 16-bit writes to the register.
>
> Is this a Fix which should be back ported in stable?
No. The behaviour of the code does not change, this merely
assigns correct names to registers, which is only interesting
for new code, which will be based on the latest Linux code.
Birger
^ permalink raw reply
* Re: [PATCH iproute2-next] ip: add OVPN device mode support
From: David Ahern @ 2026-07-08 18:14 UTC (permalink / raw)
To: Antonio Quartulli, Marco Baffo, netdev; +Cc: Stephen Hemminger
In-Reply-To: <39bbffbc-3a33-4458-a0ba-424942ed5e25@openvpn.net>
On 7/8/26 11:11 AM, Antonio Quartulli wrote:
> Actually that already works out of the box, without teaching iproute2
> about 'ovpn'.
>
> You can go with "ip link add ovpn0 type ovpn" and the interface will
> nicely show up.
>
> However, the ovpn device mode is set to the default value, hence this
> patch enhances that part.
ok, thanks for the clarification.
^ permalink raw reply
* Re: [PATCH net] netfilter: bridge: fix stale prevhdr pointer in br_ip6_fragment()
From: Xiang Mei @ 2026-07-08 18:13 UTC (permalink / raw)
To: Florian Westphal
Cc: Pablo Neira Ayuso, Phil Sutter, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, netfilter-devel,
coreteam, netdev, linux-kernel, AutonomousCodeSecurity, tgopinath,
kys
In-Reply-To: <ak44aLdDrMZXb6fC@strlen.de>
On Wed, Jul 8, 2026 at 4:46 AM Florian Westphal <fw@strlen.de> wrote:
>
> Xiang Mei (Microsoft) <xmei5@asu.edu> wrote:
> > br_ip6_fragment() gets prevhdr, a pointer into the skb head, from
> > ip6_find_1stfragopt(), then calls skb_checksum_help(). For a cloned skb
> > skb_checksum_help() reallocates the head via pskb_expand_head(), leaving
> > prevhdr dangling. It is later dereferenced in ip6_frag_next(), causing a
> > use-after-free write.
> >
> > Re-find prevhdr after skb_checksum_help() so it points into the current
> > head.
> >
> > BUG: KASAN: slab-use-after-free in ip6_frag_next (net/ipv6/ip6_output.c:857)
> > Write of size 1 at addr ffff888013ff5016 by task exploit/141
> > Call Trace:
> > ...
> > kasan_report (mm/kasan/report.c:595)
> > ip6_frag_next (net/ipv6/ip6_output.c:857)
> > br_ip6_fragment (net/ipv6/netfilter.c:212)
> > nf_ct_bridge_post (net/bridge/netfilter/nf_conntrack_bridge.c:407)
> > nf_hook_slow (net/netfilter/core.c:619)
> > br_forward_finish (net/bridge/br_forward.c:66)
> > __br_forward (net/bridge/br_forward.c:115)
> > maybe_deliver (net/bridge/br_forward.c:191)
> > br_flood (net/bridge/br_forward.c:245)
> > br_handle_frame_finish (net/bridge/br_input.c:229)
> > br_handle_frame (net/bridge/br_input.c:442)
> > ...
> > packet_sendmsg (net/packet/af_packet.c:3114)
> > ...
> > do_syscall_64 (arch/x86/entry/syscall_64.c:94)
> > entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
> > Kernel panic - not syncing: Fatal exception in interrupt
> >
> > Fixes: 764dd163ac92 ("netfilter: nf_conntrack_bridge: add support for IPv6")
> > Reported-by: AutonomousCodeSecurity@microsoft.com
> > Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
> > ---
> > net/ipv6/netfilter.c | 4 ++++
> > 1 file changed, 4 insertions(+)
> >
> > diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
> > index 6d80f85e55fa..547879da9532 100644
> > --- a/net/ipv6/netfilter.c
> > +++ b/net/ipv6/netfilter.c
> > @@ -147,6 +147,10 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
> > (err = skb_checksum_help(skb)))
> > goto blackhole;
> >
> > + err = ip6_find_1stfragopt(skb, &prevhdr);
> > + if (err < 0)
> > + goto blackhole;
>
> Would you mind sending a v2 that solves this the same way that it was
> fixed in ipv6 output engine?
>
Thanks for pointing me to the better fix. We have tested v2 on the poc
and sent v2.
Xiang
> See
> ef0efcd3bd3f ("ipv6: Fix dangling pointer when ipv6 fragment")
>
> Thanks!
^ permalink raw reply
* Re: [PATCH 1/2] umh, treewide: Explicitly include linux/umh.h where needed
From: Michal Koutný @ 2026-07-08 18:13 UTC (permalink / raw)
To: Petr Pavlu
Cc: Tony Luck, Borislav Petkov, Thomas Gleixner, Ingo Molnar,
Dave Hansen, x86, H. Peter Anvin, Philipp Reisner, Lars Ellenberg,
Christoph Böhmwalder, Jens Axboe, Johan Hovold, Alex Elder,
Greg Kroah-Hartman, Rafael J. Wysocki, Michal Januszewski,
Helge Deller, Alexander Viro, Christian Brauner, Jan Kara,
Trond Myklebust, Anna Schumaker, Chuck Lever, Jeff Layton,
NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, Mark Fasheh,
Joel Becker, Joseph Qi, Tejun Heo, Johannes Weiner,
Luis Chamberlain, Daniel Gomez, Sami Tolvanen, Aaron Tomlin,
Pavel Machek, Len Brown, Andrew Morton, Danilo Krummrich,
Nikolay Aleksandrov, Ido Schimmel, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, David Howells,
Jarkko Sakkinen, Paul Moore, James Morris, Serge E. Hallyn,
Kentaro Takeda, Tetsuo Handa, linux-edac, linux-kernel, drbd-dev,
linux-block, greybus-dev, linuxppc-dev, linux-acpi, linux-fbdev,
dri-devel, linux-fsdevel, linux-nfs, ocfs2-devel, cgroups,
linux-modules, linux-pm, driver-core, bridge, netdev, keyrings,
linux-security-module
In-Reply-To: <20260708154510.6794-2-petr.pavlu@suse.com>
[-- Attachment #1: Type: text/plain, Size: 583 bytes --]
Hi Petr.
On Wed, Jul 08, 2026 at 05:44:29PM +0200, Petr Pavlu <petr.pavlu@suse.com> wrote:
> diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c
> index a4337c9b5287..60eb994c32ae 100644
> --- a/kernel/cgroup/cgroup-v1.c
> +++ b/kernel/cgroup/cgroup-v1.c
> @@ -16,6 +16,7 @@
> #include <linux/pid_namespace.h>
> #include <linux/cgroupstats.h>
> #include <linux/fs_parser.h>
> +#include <linux/umh.h>
>
> #include <trace/events/cgroup.h>
There is kmod.h in here too but it's unnecessary, no module lazy loading
in this code.
Thanks,
Michal
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 265 bytes --]
^ permalink raw reply
* [PATCH net v2] netfilter: bridge: fix stale prevhdr pointer in br_ip6_fragment()
From: Xiang Mei (Microsoft) @ 2026-07-08 18:11 UTC (permalink / raw)
To: Florian Westphal, Pablo Neira Ayuso, Phil Sutter,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman
Cc: netfilter-devel, coreteam, netdev, linux-kernel,
AutonomousCodeSecurity, tgopinath, kys, Xiang Mei (Microsoft),
stable
br_ip6_fragment() gets prevhdr, a pointer into the skb head, from
ip6_find_1stfragopt(), then calls skb_checksum_help(). For a cloned skb
skb_checksum_help() reallocates the head via pskb_expand_head(), leaving
prevhdr dangling. It is later dereferenced in ip6_frag_next(), causing a
use-after-free write.
Save prevhdr's offset before skb_checksum_help() and recompute it after,
like commit ef0efcd3bd3f ("ipv6: Fix dangling pointer when ipv6
fragment").
BUG: KASAN: slab-use-after-free in ip6_frag_next (net/ipv6/ip6_output.c:857)
Write of size 1 at addr ffff888013ff5016 by task exploit/141
Call Trace:
...
kasan_report (mm/kasan/report.c:595)
ip6_frag_next (net/ipv6/ip6_output.c:857)
br_ip6_fragment (net/ipv6/netfilter.c:212)
nf_ct_bridge_post (net/bridge/netfilter/nf_conntrack_bridge.c:407)
nf_hook_slow (net/netfilter/core.c:619)
br_forward_finish (net/bridge/br_forward.c:66)
__br_forward (net/bridge/br_forward.c:115)
maybe_deliver (net/bridge/br_forward.c:191)
br_flood (net/bridge/br_forward.c:245)
br_handle_frame_finish (net/bridge/br_input.c:229)
br_handle_frame (net/bridge/br_input.c:442)
...
packet_sendmsg (net/packet/af_packet.c:3114)
...
do_syscall_64 (arch/x86/entry/syscall_64.c:94)
entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
Kernel panic - not syncing: Fatal exception in interrupt
Fixes: 764dd163ac92 ("netfilter: nf_conntrack_bridge: add support for IPv6")
Cc: stable@vger.kernel.org
Reported-by: AutonomousCodeSecurity@microsoft.com
Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu>
---
net/ipv6/netfilter.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
index 6d80f85e55fa..a7025ec87035 100644
--- a/net/ipv6/netfilter.c
+++ b/net/ipv6/netfilter.c
@@ -120,7 +120,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
ktime_t tstamp = skb->tstamp;
struct ip6_frag_state state;
u8 *prevhdr, nexthdr = 0;
- unsigned int mtu, hlen;
+ unsigned int mtu, hlen, nexthdr_offset;
int hroom, err = 0;
__be32 frag_id;
@@ -129,6 +129,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
goto blackhole;
hlen = err;
nexthdr = *prevhdr;
+ nexthdr_offset = prevhdr - skb_network_header(skb);
mtu = skb->dev->mtu;
if (frag_max_size > mtu ||
@@ -147,6 +148,7 @@ int br_ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
(err = skb_checksum_help(skb)))
goto blackhole;
+ prevhdr = skb_network_header(skb) + nexthdr_offset;
hroom = LL_RESERVED_SPACE(skb->dev);
if (skb_has_frag_list(skb)) {
unsigned int first_len = skb_pagelen(skb);
--
2.43.0
^ permalink raw reply related
* Re: zl3073x flash: utility "family" register — 0x7C (driver) vs 0x7D (Microchip ZLS30733 reference)?
From: Ivan Vecera @ 2026-07-08 18:09 UTC (permalink / raw)
To: Mike Frandsen; +Cc: Vadim Fedorenko, Arkadiusz Kubalewski, netdev
In-Reply-To: <CANEVyMq03cwnhjc_H5hWMXwC1CH=V8OZDQFNT+ufzpHOOYJaaw@mail.gmail.com>
Hi Mike,
On 7/8/26 7:08 PM, Mike Frandsen wrote:
> Hi Ivan,
>
> While porting the zl3073x flash sequence to a small userspace tool for the
> ZL80732 (Azurite), we noticed a discrepancy between the mainline driver and
> Microchip's own reference flasher about which register holds the flash
> utility's "family" byte:
>
> - drivers/dpll/zl3073x/flash.c (as of 01e0e8b6a2d7) reads the family from
> register 0x7C and checks for 0x21;
> - Microchip's ZLS30733 reference code (shipped in their AZFB firmware
> bundle) reads the family from 0x7D (masking the low 7 bits) and treats
> 0x7C as a different field.
In the reference code there is a confusion... There are the following
constants defined:
static const Uint32T HOST_REG_VERSION = 0x007C;
static const Uint32T HOST_REG_FAMILY = 0x007D;
These constants are nowhere referenced and the code uses hardcoded
values:
utilityHash = HostRegister_read(pContext, 0x78, 4);
utilityFam = HostRegister_read(pContext, 0x7c, 1);
utilityRel = HostRegister_read(pContext, 0x7d, 1);
...
if (utilityFam != 0x21)
{
status = ZL303XX_INVALID_OPERATION;
...
}
So the family code reported by the utility at register address 0x7c and
should contain the value of 0x21.
> We have not yet confirmed on hardware which location reports 0x21 after
> the utility loads (our port defensively accepts either). Do you know
> which location is authoritative per Microchip's documentation — and if it
> is 0x7D, whether flash.c's check works today by coincidence of the utility
> images it has been used with?
There is no official documentation for this :-( but according my test,
all known utility versions reports 0x21 in reg 0x7c.
Thanks,
Ivan
^ permalink raw reply
* [PATCH net-next] net/tcp: Prevent inlining tcp_syn_ack_timeout()
From: Emil Tsalapatis @ 2026-07-08 18:08 UTC (permalink / raw)
To: netdev; +Cc: edumazet, ncardwell, kuniyu, davem, kuba, pabeni, Emil Tsalapatis
The tcp_syn_ack_timeout() function gets inlined by Clang,
preventing tracing. Since the call is not in the fast
path, prevent it from being inlined.
Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com>
---
net/ipv4/tcp_timer.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index bf171b5e1eb3..f7215d53bbda 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -748,7 +748,7 @@ static void tcp_write_timer(struct timer_list *t)
sock_put(sk);
}
-void tcp_syn_ack_timeout(const struct request_sock *req)
+noinline_for_tracing void tcp_syn_ack_timeout(const struct request_sock *req)
{
struct net *net = read_pnet(&inet_rsk(req)->ireq_net);
--
2.54.0
^ permalink raw reply related
* [PATCH net v3] tipc: fix u16 MTU truncation in media and bearer MTU validation
From: Cen Zhang (Microsoft) @ 2026-07-08 18:02 UTC (permalink / raw)
To: jmaloy, davem, edumazet, kuba, pabeni, horms
Cc: netdev, tipc-discussion, linux-kernel, vadim.fedorenko,
tung.quang.nguyen, AutonomousCodeSecurity, tgopinath, kys,
blbllhy
Both TIPC_NL_MEDIA_SET and TIPC_NL_BEARER_SET accept user-supplied
MTU values but only enforce a minimum bound, not a maximum. When a user
sets the MTU to a value exceeding U16_MAX (65535), it passes validation
but is silently truncated when assigned to u16 fields l->mtu and
l->advertised_mtu in tipc_link_create(). Values like 65536 (0x10000)
truncate to 0, causing a division by zero in tipc_link_set_queue_limits()
which computes TIPC_MAX_PUBL / (l->mtu / ITEM_SIZE). Other overflowing
values (e.g. 65537-131071) produce small incorrect MTU values, resulting
in link malfunction behaviors.
Crash stack (triggered as unprivileged user via user namespace):
tipc_link_set_queue_limits net/tipc/link.c:2531
tipc_link_create net/tipc/link.c:520
tipc_node_check_dest net/tipc/node.c:1279
tipc_disc_rcv net/tipc/discover.c:252
tipc_rcv net/tipc/node.c:2129
tipc_udp_recv net/tipc/udp_media.c:392
Two independent paths lack the upper bound check:
1. tipc_udp_mtu_bad() -- called from __tipc_nl_media_set() (MEDIA_SET)
2. inline check in __tipc_nl_bearer_set() at bearer.c:1160 (BEARER_SET)
Fix both by rejecting MTU values above U16_MAX.
Fixes: 901271e0403a ("tipc: implement configuration of UDP media MTU")
Reported-by: AutonomousCodeSecurity@microsoft.com
Closes: https://lore.kernel.org/all/CAB8m9WgETt0AjmFwE=F-CKjGXsK6_WDv0=kbYRcC8-noo+amnA@mail.gmail.com
Signed-off-by: Cen Zhang (Microsoft) <blbllhy@gmail.com>
---
v3: Use nla_policy check to limit MTU max value as suggested by Vadim
v2: Solved format issue
Link: https://lore.kernel.org/all/CAB8m9WgETt0AjmFwE=F-CKjGXsK6_WDv0=kbYRcC8-noo+amnA@mail.gmail.com
net/tipc/netlink.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c
index 8336a9664703..1307dd1a9613 100644
--- a/net/tipc/netlink.c
+++ b/net/tipc/netlink.c
@@ -113,12 +113,16 @@ const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = {
};
/* Properties valid for media, bearer and link */
+static const struct netlink_range_validation tipc_nl_mtu_range = {
+ .max = U16_MAX,
+};
+
const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = {
[TIPC_NLA_PROP_UNSPEC] = { .type = NLA_UNSPEC },
[TIPC_NLA_PROP_PRIO] = { .type = NLA_U32 },
[TIPC_NLA_PROP_TOL] = { .type = NLA_U32 },
[TIPC_NLA_PROP_WIN] = { .type = NLA_U32 },
- [TIPC_NLA_PROP_MTU] = { .type = NLA_U32 },
+ [TIPC_NLA_PROP_MTU] = NLA_POLICY_FULL_RANGE(NLA_U32, &tipc_nl_mtu_range),
[TIPC_NLA_PROP_BROADCAST] = { .type = NLA_U32 },
[TIPC_NLA_PROP_BROADCAST_RATIO] = { .type = NLA_U32 }
};
--
2.53.0
^ permalink raw reply related
* Re: [PATCH net-next v6 4/6] pds_core: add PLDM component info display
From: Rao, Nikhil @ 2026-07-08 17:55 UTC (permalink / raw)
To: Paolo Abeni
Cc: netdev, kuba, brett.creeley, eric.joyner, andrew+netdev, davem,
edumazet, jacob.e.keller, nikhil.rao
In-Reply-To: <20260702091115.93857-1-pabeni@redhat.com>
On 7/2/2026 2:11 AM, Paolo Abeni wrote:
> From: AI Reviewer <ai@example.com>
>
> 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://netdev-ai.bots.linux.dev/sashiko/
> ---
> pds_core: add PLDM component info display
>
> This change adds detailed PLDM component info to devlink info on
> identity v2 devices, introduces a pdsc_fw_components_invalidate()
> helper used during firmware recovery and after firmware update, and
> maps PDS_CORE_FW_TYPE_MAIN to the generic "fw" name in the PLDM
> flash path.
>
>> diff --git a/drivers/net/ethernet/amd/pds_core/core.c b/drivers/net/ethernet/amd/pds_core/core.c
>> index 38a2446571af..4d9ab8b74ec6 100644
>> --- a/drivers/net/ethernet/amd/pds_core/core.c
>> +++ b/drivers/net/ethernet/amd/pds_core/core.c
>> @@ -580,6 +580,8 @@ void pdsc_fw_up(struct pdsc *pdsc)
>> return;
>> }
>>
>> + pdsc_fw_components_invalidate(pdsc);
>> +
>> err = pdsc_setup(pdsc, PDSC_SETUP_RECOVERY);
>> if (err)
>> goto err_out;
>
> [Medium]
> Can concurrent access to pdsc->fw_components race here?
> pdsc_fw_up() runs from pdsc_health_thread (workqueue, holding only
> pdsc->config_lock) and from pdsc_reset_done (PCI reset handler, holding
> neither), so pdsc_fw_components_invalidate() writes num_components = 0
> without the devlink instance lock.
>
> Meanwhile, pdsc_dl_component_info_get() reads pdsc->fw_components from
> the devlink info_get callback under only the devlink lock, never
> config_lock. The two domains do not exclude each other.
>
> In pdsc_dl_component_info_get():
>
> if (!pdsc->fw_components.num_components) {
> err = pdsc_get_component_info(pdsc);
> if (err || !pdsc->fw_components.num_components)
> return pdsc_dl_report_fw_ver(req,
> pdsc->dev_info.fw_version);
> }
>
> list_info = &pdsc->fw_components;
> num_components = min_t(u16, list_info->num_components,
> le16_to_cpu(pdsc->dev_ident.max_fw_slots));
>
> The reader can observe num_components > 0 on the first load, then
> re-read it inside min_t() and observe 0 after pdsc_fw_up() invalidates
> the cache, producing zero iterations and no fallback. Conversely, a
> stale non-zero count could be iterated over info[] entries describing
> the previous firmware.
>
> No READ_ONCE/WRITE_ONCE or memory barriers are used. Would a lock or
> RCU be a better fit here?
Will fix in v7 using READ_ONCE/WRITE_ONCE pairing for num_components.
>
>> diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c
>> index 3b763ee1715e..bd234c3c50b2 100644
>> --- a/drivers/net/ethernet/amd/pds_core/devlink.c
>> +++ b/drivers/net/ethernet/amd/pds_core/devlink.c
>> @@ -93,14 +93,110 @@ int pdsc_dl_flash_update(struct devlink *dl,
>> return pdsc_firmware_update(pdsc, params, extack);
>> }
>>
>> +static int pdsc_dl_report_component(struct devlink_info_req *req,
>> + struct pds_core_fw_component_info *info)
>> +{
>> + enum devlink_info_version_type ver_type;
>> + u16 flags = le16_to_cpu(info->flags);
>> + char *ver = info->version;
>> + const char *name;
>> + char buf[32];
>> +
>> + /* Main firmware is reported as generic "fw" */
>> + if (info->component_type == PDS_CORE_FW_TYPE_MAIN) {
>> + if (info->slot_id == PDS_CORE_FW_SLOT_GOLD)
>> + snprintf(buf, sizeof(buf), "fw.gold");
>> + else
>> + snprintf(buf, sizeof(buf), "fw");
>> + } else {
>> + name = pdsc_fw_type_to_name(info->component_type);
>> + if (!name)
>> + return 0;
>> +
>> + if (info->slot_id == PDS_CORE_FW_SLOT_GOLD)
>> + snprintf(buf, sizeof(buf), "fw.%s.gold", name);
>> + else
>> + snprintf(buf, sizeof(buf), "fw.%s", name);
>> + }
>
> [Medium]
> For PDS_CORE_FW_TYPE_MAIN, only PDS_CORE_FW_SLOT_GOLD is special-cased.
> PDS_CORE_FW_SLOT_INVALID, PDS_CORE_FW_SLOT_A, and PDS_CORE_FW_SLOT_B all
> end up emitting the bare name "fw".
>
> The legacy v1 path used distinct names fw.mainfwa and fw.mainfwb for
> these two slots, and devlink_info_version_*_put() does not deduplicate
> names. If firmware reports two MAIN components (which the v1
> pds_core_fw_list_info already exposed as A/B), will userspace see two
> entries both named "fw" with no way to distinguish them?
Both slots emit "fw" but are distinguished by version type: one is
reported as "running", the other as "stored" if there's a pending
update. If no pending update, the active slot is reported as both
"running" and "stored".
V1 and V2 use different naming conventions - this is expected.
>
> [Medium]
> Are the new component names documented anywhere? The driver introduces
> fw.bootloader, fw.cpld, fw.secure, fw.fpga, fw.suc, fw.suc.bootloader,
> fw.uboot, plus a .gold variant of each, and fw.gold.
>
> Documentation/networking/devlink/devlink-info.rst says:
>
> It is expected that drivers use the following generic names ... As
> last resort, if a component is truly unique, using driver-specific
> names is allowed, but these should be documented in the driver-specific
> file.
>
> Of those names only fw and fw.bootloader correspond to documented
> generic suffixes; the rest appear to be driver-invented, and there is
> no Documentation/networking/devlink/pds_core.rst introduced by this
> patch.
Accepted. Will move devlink documentation to
Documentation/networking/devlink/pds_core.rst in a follow-up patch per
devlink-info.rst requirements.
>The .gold suffix in particular is not part of the generic
> vocabulary (different copies of the same component are normally handled
> via bundle_id or by reporting the same name under both running and
> stored).
We do report the same name under both running and stored for main
firmware (fw appears in both sections). The .gold suffix is for recovery
firmware, which is a different component - not another copy of the same
firmware.
> Composite names like fw.suc.bootloader.gold do not have any
> documented dot-separation semantics either.
The .gold suffix is documented as indicating recovery firmware.
Composite names like fw.suc.bootloader use dots to separate hierarchical
components (SUC's bootloader).
>
>> +
>> + ver_type = DEVLINK_INFO_VERSION_TYPE_NONE;
>> + if (flags & PDS_CORE_FW_COMPONENT_INFO_F_UPDATE_BY_NAME)
>> + ver_type = DEVLINK_INFO_VERSION_TYPE_COMPONENT;
>> +
>> + if (flags & PDS_CORE_FW_COMPONENT_INFO_F_FIXED) {
>> + int err;
>> +
>> + err = devlink_info_version_fixed_put(req, buf, ver);
>> + if (err)
>> + return err;
>> + }
>> +
>> + if (flags & PDS_CORE_FW_COMPONENT_INFO_F_RUNNING) {
>> + int err;
>> +
>> + err = devlink_info_version_running_put_ext(req, buf,
>> + ver, ver_type);
>> + if (err)
>> + return err;
>> + }
>> +
>> + if (flags & PDS_CORE_FW_COMPONENT_INFO_F_STARTUP) {
>> + int err;
>> +
>> + err = devlink_info_version_stored_put_ext(req, buf,
>> + ver, ver_type);
>> + if (err)
>> + return err;
>> + }
>> +
>> + return 0;
>> +}
>
> [Medium]
> Is the generic running "fw" entry still guaranteed on the v2 path?
> It is produced only as a side effect of pdsc_dl_report_component()
> seeing component_type == PDS_CORE_FW_TYPE_MAIN with
> PDS_CORE_FW_COMPONENT_INFO_F_RUNNING set.
>
> The v1 path always called:
>
> devlink_info_version_running_put(req,
> DEVLINK_INFO_VERSION_GENERIC_FW,
> pdsc->dev_info.fw_version);
>
> unconditionally. If firmware reports MAIN only with F_STARTUP/F_FIXED
> (no F_RUNNING), or reports no MAIN component at all, does the user
> silently lose the generic running fw version that monitoring tools rely
> on to identify the active firmware?
No, Firmware always reports F_RUNNING for the currently running slot.
>
>> +
>> +static int pdsc_dl_report_fw_ver(struct devlink_info_req *req, char *fw_ver)
>> +{
>> + return devlink_info_version_running_put(req,
>> + DEVLINK_INFO_VERSION_GENERIC_FW,
>> + fw_ver);
>> +}
>> +
>> +static int pdsc_dl_component_info_get(struct devlink *dl,
>> + struct devlink_info_req *req,
>> + struct netlink_ext_ack *extack)
>> +{
>> + struct pds_core_component_list_info *list_info;
>> + struct pdsc *pdsc = devlink_priv(dl);
>> + u8 num_components;
>> + int err;
>> + int i;
>> +
>> + if (!pdsc->fw_components.num_components) {
>> + err = pdsc_get_component_info(pdsc);
>> + if (err || !pdsc->fw_components.num_components)
>> + return pdsc_dl_report_fw_ver(req,
>> + pdsc->dev_info.fw_version);
>> + }
>
> [High]
> Can devlink info polling accumulate deferred DMA buffers when the
> firmware is hung?
>
> pdsc_dl_component_info_get() is reachable from any devlink dev info
> request. On -EAGAIN or -ETIMEDOUT, pdsc_get_component_info() calls
> pdsc_deferred_dma_add() and returns the error without setting
> pdsc->fw_components.num_components:
>
> err = pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout * 2);
> if (err == -ETIMEDOUT || err == -EAGAIN) {
> pdsc_deferred_dma_add(pdsc, deferred, dma_addr, list_info,
> PDS_PAGE_SIZE, DMA_FROM_DEVICE);
> return err;
> }
>
> The cached count therefore stays 0, so the next devlink info request
> re-enters the same path and queues another PDS_PAGE_SIZE allocation
> plus DMA mapping. The deferred list is only drained by
> pdsc_deferred_dma_free() in __pdsc_devcmd_locked() on a non-EAGAIN
> return.
>
> Before this patch, pdsc_get_component_info() was reachable only from
> the privileged flash path. Is it intentional that any monitoring agent
> polling devlink dev info while firmware is wedged can grow the deferred
> DMA list unbounded?
If firmware is hung, health_work triggers recovery. Deferred buffers are
cleaned up in pdsc_teardown() during recovery. Each buffer is ~4KB, and
repeated polling during a hang accumulates minimal memory before
recovery kicks in and cleans up.
>
> [Medium]
> On the v2 branch, the function falls back to a single
> pdsc_dl_report_fw_ver() call whenever pdsc_get_component_info() returns
> an error or num_components is still 0 (which also happens when the
> firmware reports comp.get_component_info.ver == 0 and the function
> returns 0).
>
> Before this patch, pdsc_dl_info_get() unconditionally invoked
> PDS_CORE_FW_GET_LIST and emitted fw.goldfw, fw.mainfwa, fw.mainfwb.
> On v2 devices that branch is unreachable, since pdsc_dl_fw_list_info_get()
> is only called from pdsc_dl_info_get_v1().
>
> For transitional firmware that advertises identity v2 but does not yet
> support PLDM/GET_COMPONENT_INFO, would calling pdsc_dl_fw_list_info_get()
> as a v2 fallback preserve the previous four-version output rather than
> silently collapsing it to one?
V2 firmware always supports GET_COMPONENT_INFO. There is no transitional
v2 firmware without this support
>
>> +
>> + list_info = &pdsc->fw_components;
>> + num_components = min_t(u16, list_info->num_components,
>> + le16_to_cpu(pdsc->dev_ident.max_fw_slots));
>> + for (i = 0; i < num_components; i++) {
>> + err = pdsc_dl_report_component(req, &list_info->info[i]);
>> + if (err)
>> + return err;
>> + }
>> +
>> + return 0;
>> +}
>
> [Medium]
> What happens if dev_ident.max_fw_slots is 0? pdsc_get_component_info()
> populates pdsc->fw_components.num_components from the firmware list
> (bounded by PDS_CORE_FW_COMPONENT_LIST_LEN), but the loop bound clamps
> that against the separate max_fw_slots identity field:
>
> num_components = min_t(u16, list_info->num_components,
> le16_to_cpu(pdsc->dev_ident.max_fw_slots));
>
> If firmware advertises identity v2 but reports max_fw_slots == 0, the
> earlier !pdsc->fw_components.num_components check does not fire (the
> count is non-zero), min_t() yields 0, the loop body is skipped, and
> pdsc_dl_info_get() proceeds straight to ASIC ID/REV/serial. Would
> adding a fallback to pdsc_dl_report_fw_ver() in this case avoid
> emitting devlink info with no fw entry at all?
"max_fw_slots" is the component count from firmware identity.A v2 device
reporting max_fw_slots == 0 would be invalid firmware configuration
>> +
>> static char *fw_slotnames[] = {
>> "fw.goldfw",
>> "fw.mainfwa",
>> "fw.mainfwb",
>> };
>>
>> -int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
>> - struct netlink_ext_ack *extack)
>> +static int pdsc_dl_fw_list_info_get(struct devlink *dl,
>> + struct devlink_info_req *req,
>> + struct netlink_ext_ack *extack)
>> {
>
> [ ... ]
>
>> @@ -134,12 +230,41 @@ int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
>> return err;
>> }
>>
>> - err = devlink_info_version_running_put(req,
>> - DEVLINK_INFO_VERSION_GENERIC_FW,
>> - pdsc->dev_info.fw_version);
>> + return 0;
>> +}
>> +
>> +static int pdsc_dl_info_get_v1(struct devlink *dl,
>> + struct devlink_info_req *req,
>> + struct netlink_ext_ack *extack)
>> +{
>> + struct pdsc *pdsc = devlink_priv(dl);
>> + int err;
>> +
>> + err = pdsc_dl_fw_list_info_get(dl, req, extack);
>> if (err)
>> return err;
>>
>> + /* Version 1: report fw from dev_info (running only) */
>> + return pdsc_dl_report_fw_ver(req, pdsc->dev_info.fw_version);
>> +}
>> +
>> +int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
>> + struct netlink_ext_ack *extack)
>> +{
>> + struct pdsc *pdsc = devlink_priv(dl);
>> + char buf[32];
>> + int err;
>> +
>> + if (pdsc->dev_ident.version >= PDS_CORE_IDENTITY_VERSION_2) {
>> + err = pdsc_dl_component_info_get(dl, req, extack);
>> + if (err)
>> + return err;
>> + } else {
>> + err = pdsc_dl_info_get_v1(dl, req, extack);
>> + if (err)
>> + return err;
>> + }
>> +
>
> [Medium]
> Is the devlink-info naming change across the identity-version boundary
> intentional, and is it captured in the commit message or a Documentation
> update?
Yes, the naming change is intentional. This is documented in the "Info
versions (PLDM)" section of pds_core.rst, which lists all the V2
component names separately from the legacy V1 names.
> Before this patch every device emitted stored slot names from
> fw_slotnames[] (fw.goldfw, fw.mainfwa, fw.mainfwb) via the unconditional
> PDS_CORE_FW_GET_LIST devcmd. After this patch, devices that advertise
> identity >= PDS_CORE_IDENTITY_VERSION_2 take the
> pdsc_dl_component_info_get() branch and emit a different set of names
> (fw, fw.gold, fw.bootloader, fw.uboot, fw.cpld, ...), with no alias for
> the old names.
>
> A firmware-only upgrade can therefore silently rename the user-visible
> stored entries. Should the commit message call out this UAPI rename,
> and should the legacy names remain emitted as aliases for compatibility
> with existing fleet tooling that parses devlink dev info output?
V1 named specific physical slots (fw.mainfwa, fw.mainfwb). V2 uses a
single fw name that appears in either the running: or stored: section.
The concepts don't map 1:1, emitting legacy names as aliases doesn't
make sense. Fleet tooling that upgrades firmware to PLDM-capable version
should expect the new format.
Nikhil
^ permalink raw reply
* Re: [PATCH v3 4/5] net: qrtr: ns: Limit the total number of nodes
From: Youssef Samir @ 2026-07-08 17:49 UTC (permalink / raw)
To: Manivannan Sadhasivam, Manivannan Sadhasivam, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman
Cc: linux-arm-msm, netdev, linux-kernel, stable, jeff.hugo
In-Reply-To: <20260409-qrtr-fix-v3-4-00a8a5ff2b51@oss.qualcomm.com>
On 4/9/2026 6:34 PM, Manivannan Sadhasivam wrote:
> Currently, the nameserver doesn't limit the number of nodes it handles.
> This can be an attack vector if a malicious client starts registering
> random nodes, leading to memory exhaustion.
>
> Hence, limit the maximum number of nodes to 64. Note that, limit of 64 is
> chosen based on the current platform requirements. If requirement changes
> in the future, this limit can be increased.
Hi Mani,
There are AI200 setups that can reach 384 nodes (192 * (AI200PF + AI200VF)).
I'm not sure about limiting the number of nodes, but if there's a use-case
that led to enforcing that limit, could we increase it to something like 512?
Thanks,
Youssef
^ permalink raw reply
* [PATCH net] tipc: fix NULL deref in tipc_lxc_xmit() on node up
From: Weiming Shi @ 2026-07-08 17:30 UTC (permalink / raw)
To: Jon Maloy, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman
Cc: Xiang Mei, netdev, tipc-discussion, linux-kernel, stable,
Weiming Shi
tipc_named_node_up() builds a bulk of this node's cluster-scope service
bindings for a peer that just came up and sends it with tipc_node_xmit().
When cluster_scope is empty the bulk is an empty skb chain, and both
consumers dereference the head unconditionally: named_distribute() reads
buf_msg(skb_peek_tail(list)) to tag the last message, and for a same-host
peer tipc_node_xmit() routes into tipc_lxc_xmit(), which reads
buf_msg(skb_peek(list)). skb_peek*() returns NULL on an empty chain, so
buf_msg(NULL) faults.
cluster_scope is legitimately empty during the window in
tipc_net_finalize() between setting the node address, after which peers
can link up and trigger tipc_named_node_up(), and tipc_nametbl_publish()
inserting the first self-binding. A peer linking in that window crashes
the node. It is reachable by an unprivileged user, who can gain
CAP_NET_ADMIN in a private net namespace and drive TIPC there.
Oops: general protection fault, probably for non-canonical address
KASAN: null-ptr-deref in range [0x00000000000000d8-0x00000000000000df]
RIP: 0010:tipc_lxc_xmit (net/tipc/node.c:1629 net/tipc/msg.h:202)
tipc_node_xmit (net/tipc/node.c:1718)
tipc_named_node_up (net/tipc/name_distr.c:222)
tipc_node_write_unlock (net/tipc/node.c:428)
tipc_rcv (net/tipc/node.c:2185)
tipc_l2_rcv_msg (net/tipc/bearer.c:669)
Skip the distribution when cluster_scope is empty; an empty bulk carries
no bindings, so not sending it changes nothing.
Fixes: cad2929dc432 ("tipc: update a binding service via broadcast")
Reported-by: Xiang Mei <xmei5@asu.edu>
Assisted-by: Claude:claude-opus-4-8
Cc: stable@vger.kernel.org
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
---
net/tipc/name_distr.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c
index ba4f4906e13b..495e46defddb 100644
--- a/net/tipc/name_distr.c
+++ b/net/tipc/name_distr.c
@@ -218,8 +218,10 @@ void tipc_named_node_up(struct net *net, u32 dnode, u16 capabilities)
spin_unlock_bh(&tn->nametbl_lock);
read_lock_bh(&nt->cluster_scope_lock);
- named_distribute(net, &head, dnode, &nt->cluster_scope, seqno);
- tipc_node_xmit(net, &head, dnode, 0);
+ if (!list_empty(&nt->cluster_scope)) {
+ named_distribute(net, &head, dnode, &nt->cluster_scope, seqno);
+ tipc_node_xmit(net, &head, dnode, 0);
+ }
read_unlock_bh(&nt->cluster_scope_lock);
}
--
2.43.0
^ permalink raw reply related
* Re: [PATCH RFC net-next 3/3] net: dsa: mxl862xx: add devlink flash_update and info_get
From: Andrew Lunn @ 2026-07-08 17:27 UTC (permalink / raw)
To: Daniel Golle
Cc: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, linux-kernel, netdev
In-Reply-To: <ak0KOrM5E2_sPIHf@makrotopia.org>
> + * The flash process takes approximately 15 minutes. Progress is
> + * reported via devlink status notifications. After a successful (or
> + * failed) flash the driver reprobes the device automatically.
Have you tested the failed use case?
I assume if the firmware in the flash is invalid, the bootloader does
not boot it, and it remains in the bootloader waiting for another
attempt. Does this DSA driver still load, so devlink can be used to
try again?
Andrew
^ permalink raw reply
* RE: [PATCH net-next v5 07/13] net: ethernet: oa_tc6: Add read_mms/write_mms register access functions
From: Selvamani Rajagopal @ 2026-07-08 17:23 UTC (permalink / raw)
To: ciprian.regus@analog.com, Parthiban Veerasooran, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Shuah Khan, Andrew Lunn,
Heiner Kallweit, Russell King, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-doc@vger.kernel.org, devicetree@vger.kernel.org
In-Reply-To: <20260708-adin1140-driver-v5-7-4aca7b51a58b@analog.com>
> -----Original Message-----
> From: Ciprian Regus via B4 Relay <devnull+ciprian.regus.analog.com@kernel.org>
> Sent: Tuesday, July 7, 2026 3:34 PM
> Subject: [PATCH net-next v5 07/13] net: ethernet: oa_tc6: Add read_mms/write_mms
> register access functions
>
>
> From: Ciprian Regus <ciprian.regus@analog.com>
>
> + */
> +int oa_tc6_read_register_mms(struct oa_tc6 *tc6, u8 mms, u16 address,
> + u32 *value)
> +{
Ciprian,
I hope you noticed that I have similar function in my patch where "mms" comes after "address".
I thought of aligning my patch with yours. Then I realized, if your code gets merged first, this won't be
the only change I would be doing. That's the reason I decided to wait for one of our patches
get merged so that we can update the patch accordingly.
Hope it makes sense.
Sincerely
Selva
^ permalink raw reply
* Re: [PATCH RFC net-next 2/3] net: dsa: mxl862xx: add SMDIO clause-22 register access
From: Andrew Lunn @ 2026-07-08 17:22 UTC (permalink / raw)
To: Daniel Golle
Cc: Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, linux-kernel, netdev
In-Reply-To: <ak0KJ6gUk2l_IwoI@makrotopia.org>
On Tue, Jul 07, 2026 at 04:16:07PM +0200, Daniel Golle wrote:
> Add mxl862xx_smdio_read() and mxl862xx_smdio_write() for clause-22
> SMDIO register access. MCUboot rescue mode only exposes clause-22
> registers; the existing clause-45 MMD interface is unavailable during
> firmware transfer. The MDIO bus lock is held per-transaction (not
> across polls) so that SB PDI polling during flash erase does not
> starve other MDIO users.
What other MDIO users are there? It sounds like once the switch is in
rescue mode, switch management is dead. So how can there be users?
Andrew
^ permalink raw reply
* Re: [PATCH v5 net 2/7] i40e: avoid null ptr dereference in i40e_ptp_stop()
From: Maciej Fijalkowski @ 2026-07-08 17:22 UTC (permalink / raw)
To: Simon Horman
Cc: intel-wired-lan, netdev, magnus.karlsson, kuba, pabeni,
przemyslaw.kitszel, jacob.e.keller
In-Reply-To: <20260708150455.1498611-2-horms@kernel.org>
On Wed, Jul 08, 2026 at 04:04:56PM +0100, Simon Horman wrote:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://sashiko.dev
> ---
> i40e: avoid null ptr dereference in i40e_ptp_stop()
>
> Check if main_vsi is not null before calling dev_info() to avoid a null
> pointer dereference when i40e_vsi_clear() frees the main VSI during rebuild.
>
> > diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
> > index ff62b5f2c8150..ca93df4d67859 100644
> > --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c
> > +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c
> > @@ -1556,8 +1556,9 @@ void i40e_ptp_stop(struct i40e_pf *pf)
> > if (pf->ptp_clock) {
> > ptp_clock_unregister(pf->ptp_clock);
> > pf->ptp_clock = NULL;
> > - dev_info(&pf->pdev->dev, "%s: removed PHC on %s\n", __func__,
> > - main_vsi->netdev->name);
> > + if (main_vsi)
> > + dev_info(&pf->pdev->dev, "%s: removed PHC on %s\n", __func__,
> > + main_vsi->netdev->name);
>
> [Severity: High]
> This isn't a bug introduced by this patch, but this lockless check appears
> to introduce a time-of-check to time-of-use race condition.
Thanks Simon I saw these, however since all of the issues are classified
as pre-existing issues IMHO these changes are okay to be taken and then
we could go with another round of fixes.
>
> When i40e_remove() calls i40e_ptp_stop(pf), it does so unsynchronized from
> the background i40e_rebuild() task. If i40e_rebuild() fails during setup, it
> calls i40e_vsi_clear(main_vsi), which frees the VSI and its netdev.
>
> If i40e_ptp_stop() caches main_vsi right before it is cleared, couldn't the
> if (main_vsi) check pass, but the subsequent main_vsi->netdev->name access
> dereference freed memory, causing a use-after-free?
>
> [Severity: High]
> This is another pre-existing issue, but I noticed that i40e_ptp_stop() doesn't
> seem to cancel the background workqueue task ptp_extts0_work.
>
> During device removal, i40e_remove() calls i40e_ptp_stop() and then
> subsequently frees the i40e_pf structure. If the work executes after the PF
> is freed, will it access the deallocated i40e_pf structure and trigger a
> use-after-free?
>
> [Severity: High]
> This is also a pre-existing issue, but I am looking at how pf->ptp_tx_skb is
> cleared earlier in i40e_ptp_stop(). The teardown uses I40E_FLAG_PTP_ENA as a
> gate guard:
>
> i40e_ptp_stop() clears I40E_FLAG_PTP_ENA, then locklessly checks and frees
> pf->ptp_tx_skb using dev_kfree_skb_any().
>
> The interrupt handler i40e_ptp_tx_hwtstamp() uses the same bit as a guard:
>
> if (!test_bit(I40E_FLAG_PTP_ENA, pf->flags))
> return;
>
> If the interrupt handler passes the test_bit check just before i40e_ptp_stop()
> executes clear_bit(), both contexts could concurrently read the same
> pf->ptp_tx_skb pointer into local variables before it is NULLed out. Could
> this result in a double free of the SKB?
>
> [Severity: High]
> There is a similar pre-existing issue related to the teardown of the
> timestamping state in i40e_ptp_stop() racing with the TX datapath.
>
> The TX datapath i40e_tstamp() protects the state using a bit lock:
>
> if (pf->ptp_tx &&
> !test_and_set_bit_lock(__I40E_PTP_TX_IN_PROGRESS, pf->state)) {
> ...
> pf->ptp_tx_skb = skb_get(skb);
> }
>
> However, i40e_ptp_stop() checks pf->ptp_tx_skb locklessly and only clears
> the bit lock if the SKB was present. If the TX datapath acquires the lock
> and is preempted right before assigning pf->ptp_tx_skb, i40e_ptp_stop()
> might execute, see a NULL ptp_tx_skb, and skip the cleanup block.
>
> When the TX datapath resumes, it assigns the SKB pointer. Since the device
> is subsequently destroyed, does this cause the SKB to be permanently leaked?
>
> > }
> >
> > if (i40e_is_ptp_pin_dev(&pf->hw)) {
>
> [Severity: High]
> This isn't a bug introduced by this patch, but while reviewing the driver's
> cleanup paths, I noticed a potential leak in the i40e_probe() error handling:
>
> drivers/net/ethernet/intel/i40e/i40e_main.c:i40e_probe() {
> ...
> err_vsis:
> set_bit(__I40E_DOWN, pf->state);
> i40e_ptp_stop(pf);
> i40e_clear_interrupt_scheme(pf);
> kfree(pf->vsi);
> ...
> }
>
> If initialization fails after VSIs are successfully created, this cleanup
> path calls kfree(pf->vsi) which frees the pointer array, but does not appear
> to unregister the netdev or free the actual VSI structs and hardware rings.
>
> Could this leave an active, registered netdev with dangling pointers to the
> subsequently freed PF, potentially triggering a use-after-free upon network
> traffic or userspace access?
>
> [Severity: High]
> This is a pre-existing issue, but I noticed an ad-hoc locking pattern in
> i40e_remove():
>
> drivers/net/ethernet/intel/i40e/i40e_main.c:i40e_remove() {
> ...
> while (test_and_set_bit(__I40E_RESET_RECOVERY_PENDING, pf->state))
> usleep_range(1000, 2000);
> ...
> }
>
> This attempts to synchronize with the background reset path by spinning on
> a bit flag, which acts as a sleepable lock without the standard fairness or
> deadlock detection guarantees.
>
> Since this ad-hoc locking scheme fails to provide proper mutual exclusion
> earlier in the function, could this be the direct cause of why i40e_ptp_stop()
> executes unsynchronized from i40e_rebuild()? Should a proper mutex be used
> here instead?
^ permalink raw reply
* [PATCH net-next v7 15/15] onsemi: s2500: Added selftest support to onsemi's S2500 driver
From: Selvamani Rajagopal via B4 Relay @ 2026-07-08 17:12 UTC (permalink / raw)
To: Andrew Lunn, Piergiorgio Beruto, Heiner Kallweit, Russell King,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn, Parthiban Veerasooran, Selva Rajagopal,
Richard Cochran, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Simon Horman, Jonathan Corbet, Shuah Khan
Cc: netdev, linux-kernel, devicetree, linux-doc, Jerry Ray,
Selvamani Rajagopal
In-Reply-To: <20260708-s2500-mac-phy-support-v7-0-478c877aa1a9@onsemi.com>
From: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
Adds selftest support for onsemi S2500 MAC-PHY. Added as a separate
patch for the ease of review.
Signed-off-by: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
---
changes in v7
- No change
changes in v6
- Moved the signed off information to the correct place.
changes in v5
- No change
changes in v4:
- Added a selftest as a separate patch
- First patch
---
drivers/net/ethernet/onsemi/s2500/Kconfig | 1 +
drivers/net/ethernet/onsemi/s2500/s2500_ethtool.c | 6 ++++++
2 files changed, 7 insertions(+)
diff --git a/drivers/net/ethernet/onsemi/s2500/Kconfig b/drivers/net/ethernet/onsemi/s2500/Kconfig
index 7786f702d6ec..141fbaf1b641 100644
--- a/drivers/net/ethernet/onsemi/s2500/Kconfig
+++ b/drivers/net/ethernet/onsemi/s2500/Kconfig
@@ -6,6 +6,7 @@
config S2500_MACPHY
tristate "S2500 support"
depends on SPI
+ imply NET_SELFTESTS
select NCN26000_PHY
select OA_TC6
help
diff --git a/drivers/net/ethernet/onsemi/s2500/s2500_ethtool.c b/drivers/net/ethernet/onsemi/s2500/s2500_ethtool.c
index 7e96d9ad563f..0b7ef490d7ca 100644
--- a/drivers/net/ethernet/onsemi/s2500/s2500_ethtool.c
+++ b/drivers/net/ethernet/onsemi/s2500/s2500_ethtool.c
@@ -5,6 +5,7 @@
*/
#include <linux/ethtool.h>
+#include <net/selftests.h>
#include <linux/phy.h>
#include "s2500_hw_def.h"
@@ -229,6 +230,8 @@ static int s2500_get_sset_count(struct net_device *ndev, int sset)
switch (sset) {
case ETH_SS_STATS:
return S2500_MAC_STATS_LEN;
+ case ETH_SS_TEST:
+ return net_selftest_get_count();
default:
return -EOPNOTSUPP;
}
@@ -242,6 +245,9 @@ static void s2500_get_strings(struct net_device *ndev, u32 stringset,
memcpy(buf, s2500_mac_stat_strings,
S2500_MAC_STATS_LEN * ETH_GSTRING_LEN);
break;
+ case ETH_SS_TEST:
+ net_selftest_get_strings(buf);
+ break;
}
}
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v7 08/15] net: ethernet: oa_tc6: Support for hardware timestamp
From: Selvamani Rajagopal via B4 Relay @ 2026-07-08 17:12 UTC (permalink / raw)
To: Andrew Lunn, Piergiorgio Beruto, Heiner Kallweit, Russell King,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn, Parthiban Veerasooran, Selva Rajagopal,
Richard Cochran, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Simon Horman, Jonathan Corbet, Shuah Khan
Cc: netdev, linux-kernel, devicetree, linux-doc, Jerry Ray,
Selvamani Rajagopal
In-Reply-To: <20260708-s2500-mac-phy-support-v7-0-478c877aa1a9@onsemi.com>
From: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
PTP register/unregister calls are implemented in oa_tc6_ptp.c.
The APIs that work with the hardware for timestamp is provided
by vendor code as it may be vendor dependent.
Interface for ndo_hwtstamp_set/get, ioctl, control and status
callback for ethtool are provided to support hardware timestamp
feature.
Besides ioctl interface, hardware timestamp functions that handles
header and footer data are in oa_tc6.c. Helper functions are in
oa_tc6_tstamp.c.
Signed-off-by: Selvamani Rajagopal <Selvamani.Rajagopal@onsemi.com>
---
changes in v7
- Fixed the parameter name mismatch in function protoype and
definition
- CONFIG0 is set with 64 bit timestamp support by default.
- Added information about return value for API documentation.
changes in v6
- Fixed the issue of function parameter in oa_tc6_get_ts_stats
not described in comments section for documentation.
- Avoided typecasting __be32 as u32
changes in v5
- As subtracting skb len by FCS size is considered bug, changes
are removed. Will be fixed in stable branch (net repo)
changes in v4
- Fixed the condition check for subtracting the FCS size
from skb len.
changes in v3
- Replaced warning printk with ratelimited printk
- Checking the hardware register before enabling hardware
timestamp
changes in v1
- Added hardware timestamp support to the OA TC6 framework.
---
MAINTAINERS | 1 +
drivers/net/ethernet/oa_tc6/Makefile | 2 +-
drivers/net/ethernet/oa_tc6/oa_tc6.c | 218 +++++++++++++++++++++++++--
drivers/net/ethernet/oa_tc6/oa_tc6_ptp.c | 70 +++++++++
drivers/net/ethernet/oa_tc6/oa_tc6_std_def.h | 34 +++++
drivers/net/ethernet/oa_tc6/oa_tc6_tstamp.c | 205 +++++++++++++++++++++++++
include/linux/oa_tc6.h | 12 ++
7 files changed, 527 insertions(+), 15 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index c9b101d8e1fd..fb19a6431951 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20216,6 +20216,7 @@ F: drivers/rtc/rtc-optee.c
OPEN ALLIANCE 10BASE-T1S MACPHY SERIAL INTERFACE FRAMEWORK
M: Parthiban Veerasooran <parthiban.veerasooran@microchip.com>
+M: Selva Rajagopal <selvamani.rajagopal@onsemi.com> (timestamp support)
L: netdev@vger.kernel.org
S: Maintained
F: Documentation/networking/oa-tc6-framework.rst
diff --git a/drivers/net/ethernet/oa_tc6/Makefile b/drivers/net/ethernet/oa_tc6/Makefile
index f24aae852ef2..964f668efc2d 100644
--- a/drivers/net/ethernet/oa_tc6/Makefile
+++ b/drivers/net/ethernet/oa_tc6/Makefile
@@ -4,4 +4,4 @@
#
obj-$(CONFIG_OA_TC6) := oa_tc6_mod.o
-oa_tc6_mod-objs := oa_tc6.o
+oa_tc6_mod-objs := oa_tc6.o oa_tc6_ptp.o oa_tc6_tstamp.o
diff --git a/drivers/net/ethernet/oa_tc6/oa_tc6.c b/drivers/net/ethernet/oa_tc6/oa_tc6.c
index bf96e8d1ccb9..939935cb170d 100644
--- a/drivers/net/ethernet/oa_tc6/oa_tc6.c
+++ b/drivers/net/ethernet/oa_tc6/oa_tc6.c
@@ -14,6 +14,15 @@
#include "oa_tc6_std_def.h"
+struct oa_tc6_ts_info_rx {
+ bool rtsa;
+ bool rtsp;
+};
+
+struct oa_tc6_ts_info_tx {
+ u8 tsc;
+};
+
static int oa_tc6_spi_transfer(struct oa_tc6 *tc6,
enum oa_tc6_header_type header_type, u16 length)
{
@@ -48,6 +57,156 @@ static int oa_tc6_get_parity(u32 p)
return !((p >> 28) & 1);
}
+static struct oa_tc6_ts_info_tx *oa_tc6_tsinfo_tx(struct sk_buff *skb)
+{
+ return (struct oa_tc6_ts_info_tx *)(skb->cb);
+}
+
+static struct oa_tc6_ts_info_rx *oa_tc6_tsinfo_rx(struct sk_buff *skb)
+{
+ return (struct oa_tc6_ts_info_rx *)(skb->cb);
+}
+
+static void oa_tc6_defer_for_hwtstamp(struct oa_tc6 *tc6,
+ struct sk_buff *skb)
+{
+ if (!tc6->hw_tstamp_enabled)
+ return;
+ if (!skb || (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) == 0)
+ return;
+ if (tc6->ts_config.tx_type != HWTSTAMP_TX_ON) {
+ tc6->tx_hwtstamp_lost++;
+ return;
+ }
+
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+ u8 ret = tc6->tx_ts_idx++;
+
+ if (ret == OA_TC6_TTSCC_REG_ID)
+ tc6->tx_ts_idx = OA_TC6_TTSCA_REG_ID;
+ oa_tc6_tsinfo_tx(skb)->tsc = ret;
+
+ list_add_tail(&skb->list, &tc6->tx_ts_skb_q);
+}
+
+static int oa_tc6_process_deferred_skb(struct oa_tc6 *tc6, u8 tsc)
+{
+ struct skb_shared_hwtstamps tstamp;
+ struct oa_tc6_ts_info_tx *ski;
+ struct sk_buff *skb, *tmp;
+ bool found = false;
+ int ret = 0;
+
+ /* Size of data must match OA_TC6_TSTAMP_SZ */
+ u32 data[2];
+
+ list_for_each_entry_safe(skb, tmp, &tc6->tx_ts_skb_q, list) {
+ ski = oa_tc6_tsinfo_tx(skb);
+ if (ski->tsc != tsc)
+ continue;
+ if (found) {
+ dev_warn_ratelimited(&tc6->spi->dev,
+ "Multiple skbs. tsc = %d\n",
+ tsc);
+ tc6->tx_hwtstamp_err++;
+ }
+ found = true;
+ list_del(&skb->list);
+
+ /* Retrieve the timestamping info */
+ ret = oa_tc6_read_registers(tc6,
+ OA_TC6_REG_TTSCA_HIGH +
+ 2 * (tsc - 1), &data[0], 2);
+
+ if (!ret) {
+ tstamp.hwtstamp = ktime_set(data[0], data[1]);
+ skb_tstamp_tx(skb, &tstamp);
+ tc6->tx_hwtstamp_pkts++;
+ }
+
+ dev_kfree_skb(skb);
+ }
+ return ret;
+}
+
+static void oa_tc6_events_handle(struct oa_tc6 *tc6, u32 val)
+{
+ /* Check TX timestamping */
+ if (val & STATUS0_TTSCAA)
+ oa_tc6_process_deferred_skb(tc6, OA_TC6_TTSCA_REG_ID);
+
+ if (val & STATUS0_TTSCAB)
+ oa_tc6_process_deferred_skb(tc6, OA_TC6_TTSCB_REG_ID);
+
+ if (val & STATUS0_TTSCAC)
+ oa_tc6_process_deferred_skb(tc6, OA_TC6_TTSCC_REG_ID);
+}
+
+static void oa_tc6_update_ts_in_rx_skb(struct oa_tc6 *tc6)
+{
+ struct sk_buff *skb = tc6->rx_skb;
+ struct oa_tc6_ts_info_rx *ski;
+ __be32 ts_val[2];
+ u32 ts[2];
+
+ if (!tc6->hw_tstamp_enabled)
+ return;
+ ski = oa_tc6_tsinfo_rx(skb);
+ if (!ski->rtsa)
+ return;
+
+ memcpy(&ts_val[0], skb->data, 4);
+ memcpy(&ts_val[1], (u32 *)skb->data + 1, 4);
+
+ ts[0] = be32_to_cpu(ts_val[0]);
+ ts[1] = be32_to_cpu(ts_val[1]);
+
+ /* Check parity */
+ if ((oa_tc6_get_parity(ts[0]) ^ oa_tc6_get_parity(ts[1])) ==
+ !ski->rtsp) {
+ struct skb_shared_hwtstamps *hw_ts;
+
+ /* Report timestamp to the upper layers */
+ hw_ts = skb_hwtstamps(skb);
+ memset(hw_ts, 0, sizeof(*hw_ts));
+ hw_ts->hwtstamp = ktime_set(ts[0], ts[1]);
+ }
+ skb_pull(skb, sizeof(ts));
+}
+
+static int oa_tc6_update_standard_capability(struct oa_tc6 *tc6)
+{
+ u32 regval = 0;
+ int ret;
+
+ ret = oa_tc6_read_register(tc6, OA_TC6_REG_STDCAP, ®val);
+ if (ret)
+ return ret;
+ if (regval & STDCAP_FRAME_TIMESTAMP_CAPABILITY)
+ tc6->hw_tstamp_supported = true;
+ return 0;
+}
+
+/**
+ * oa_tc6_ioctl - generic ioctl interface for MAC-PHY drivers.
+ * @tc6: oa_tc6 struct.
+ * @rq: request from socket interface
+ * @cmd: value to set/get timestamp configuration
+ *
+ * Return: 0 on success otherwise failed.
+ */
+int oa_tc6_ioctl(struct oa_tc6 *tc6, struct ifreq *rq, int cmd)
+{
+ if (!netif_running(tc6->netdev))
+ return -EINVAL;
+
+ if (cmd == SIOCSHWTSTAMP || cmd == SIOCGHWTSTAMP)
+ return oa_tc6_tstamp_ioctl(tc6, rq, cmd);
+ else
+ return phy_do_ioctl_running(tc6->netdev, rq, cmd);
+}
+EXPORT_SYMBOL_GPL(oa_tc6_ioctl);
+
static __be32 oa_tc6_prepare_ctrl_header(u32 addr, u8 length,
enum oa_tc6_register_op reg_op)
{
@@ -571,6 +730,9 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)
return ret;
}
+ if ((value & STATUS0_TTSCA_MASK) != 0)
+ oa_tc6_events_handle(tc6, value & STATUS0_TTSCA_MASK);
+
/* Clear the error interrupts status */
ret = oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, value);
if (ret) {
@@ -653,6 +815,7 @@ static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6)
tc6->rx_skb->len > ETH_FCS_LEN)
skb_trim(tc6->rx_skb, tc6->rx_skb->len - ETH_FCS_LEN);
+ oa_tc6_update_ts_in_rx_skb(tc6);
tc6->rx_skb->protocol = eth_type_trans(tc6->rx_skb, tc6->netdev);
tc6->netdev->stats.rx_packets++;
tc6->netdev->stats.rx_bytes += tc6->rx_skb->len;
@@ -667,24 +830,29 @@ static void oa_tc6_update_rx_skb(struct oa_tc6 *tc6, u8 *payload, u8 length)
memcpy(skb_put(tc6->rx_skb, length), payload, length);
}
-static int oa_tc6_allocate_rx_skb(struct oa_tc6 *tc6)
+static int oa_tc6_allocate_rx_skb(struct oa_tc6 *tc6, u32 footer)
{
+ struct oa_tc6_ts_info_rx *ski;
+
tc6->rx_skb = netdev_alloc_skb_ip_align(tc6->netdev, tc6->netdev->mtu +
- ETH_HLEN + ETH_FCS_LEN);
+ ETH_HLEN + ETH_FCS_LEN + OA_TC6_TSTAMP_SZ);
if (!tc6->rx_skb) {
tc6->netdev->stats.rx_dropped++;
return -ENOMEM;
}
+ ski = oa_tc6_tsinfo_rx(tc6->rx_skb);
+ ski->rtsa = FIELD_GET(OA_TC6_DATA_FOOTER_RTSA_VALID, footer);
+ ski->rtsp = FIELD_GET(OA_TC6_DATA_FOOTER_RTSP_VALID, footer);
return 0;
}
static int oa_tc6_prcs_complete_rx_frame(struct oa_tc6 *tc6, u8 *payload,
- u16 size)
+ u16 size, u32 footer)
{
int ret;
- ret = oa_tc6_allocate_rx_skb(tc6);
+ ret = oa_tc6_allocate_rx_skb(tc6, footer);
if (ret)
return ret;
@@ -695,11 +863,11 @@ static int oa_tc6_prcs_complete_rx_frame(struct oa_tc6 *tc6, u8 *payload,
return 0;
}
-static int oa_tc6_prcs_rx_frame_start(struct oa_tc6 *tc6, u8 *payload, u16 size)
+static int oa_tc6_prcs_rx_frame_start(struct oa_tc6 *tc6, u8 *payload, u16 size, u32 footer)
{
int ret;
- ret = oa_tc6_allocate_rx_skb(tc6);
+ ret = oa_tc6_allocate_rx_skb(tc6, footer);
if (ret)
return ret;
@@ -744,7 +912,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
size = end_byte_offset + 1 - start_byte_offset;
return oa_tc6_prcs_complete_rx_frame(tc6,
&data[start_byte_offset],
- size);
+ size, footer);
}
/* Process the chunk with only rx frame start */
@@ -752,7 +920,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
size = OA_TC6_CHUNK_PAYLOAD_SIZE - start_byte_offset;
return oa_tc6_prcs_rx_frame_start(tc6,
&data[start_byte_offset],
- size);
+ size, footer);
}
/* Process the chunk with only rx frame end */
@@ -777,7 +945,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
size = OA_TC6_CHUNK_PAYLOAD_SIZE - start_byte_offset;
return oa_tc6_prcs_rx_frame_start(tc6,
&data[start_byte_offset],
- size);
+ size, footer);
}
/* Process the chunk with ongoing rx frame data */
@@ -831,13 +999,15 @@ static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length)
}
static __be32 oa_tc6_prepare_data_header(bool data_valid, bool start_valid,
- bool end_valid, u8 end_byte_offset)
+ bool end_valid, u8 end_byte_offset,
+ u8 tsc)
{
u32 header = FIELD_PREP(OA_TC6_DATA_HEADER_DATA_NOT_CTRL,
OA_TC6_DATA_HEADER) |
FIELD_PREP(OA_TC6_DATA_HEADER_DATA_VALID, data_valid) |
FIELD_PREP(OA_TC6_DATA_HEADER_START_VALID, start_valid) |
FIELD_PREP(OA_TC6_DATA_HEADER_END_VALID, end_valid) |
+ FIELD_PREP(OA_TC6_DATA_HEADER_TSC_OFFSET, tsc) |
FIELD_PREP(OA_TC6_DATA_HEADER_END_BYTE_OFFSET,
end_byte_offset);
@@ -856,6 +1026,7 @@ static void oa_tc6_add_tx_skb_to_spi_buf(struct oa_tc6 *tc6)
enum oa_tc6_data_start_valid_info start_valid;
u8 end_byte_offset = 0;
u16 length_to_copy;
+ u8 tsc = 0;
/* Initial value is assigned here to avoid more than 80 characters in
* the declaration place.
@@ -865,8 +1036,10 @@ static void oa_tc6_add_tx_skb_to_spi_buf(struct oa_tc6 *tc6)
/* Set start valid if the current tx chunk contains the start of the tx
* ethernet frame.
*/
- if (!tc6->tx_skb_offset)
+ if (!tc6->tx_skb_offset) {
start_valid = OA_TC6_DATA_START_VALID;
+ tsc = oa_tc6_tsinfo_tx(tc6->ongoing_tx_skb)->tsc;
+ }
/* If the remaining tx skb length is more than the chunk payload size of
* 64 bytes then copy only 64 bytes and leave the ongoing tx skb for
@@ -887,12 +1060,18 @@ static void oa_tc6_add_tx_skb_to_spi_buf(struct oa_tc6 *tc6)
tc6->tx_skb_offset = 0;
tc6->netdev->stats.tx_bytes += tc6->ongoing_tx_skb->len;
tc6->netdev->stats.tx_packets++;
- kfree_skb(tc6->ongoing_tx_skb);
+
+ /* Free the ones that are not saved for later processing,
+ * like timestamping.
+ */
+ if (!(skb_shinfo(tc6->ongoing_tx_skb)->tx_flags &
+ SKBTX_IN_PROGRESS))
+ kfree_skb(tc6->ongoing_tx_skb);
tc6->ongoing_tx_skb = NULL;
}
*tx_buf = oa_tc6_prepare_data_header(OA_TC6_DATA_VALID, start_valid,
- end_valid, end_byte_offset);
+ end_valid, end_byte_offset, tsc);
tc6->spi_data_tx_buf_offset += OA_TC6_CHUNK_SIZE;
}
@@ -910,6 +1089,8 @@ static u16 oa_tc6_prepare_spi_tx_buf_for_tx_skbs(struct oa_tc6 *tc6)
tc6->ongoing_tx_skb = tc6->waiting_tx_skb;
tc6->waiting_tx_skb = NULL;
spin_unlock_bh(&tc6->tx_skb_lock);
+ oa_tc6_defer_for_hwtstamp(tc6,
+ tc6->ongoing_tx_skb);
}
if (!tc6->ongoing_tx_skb)
break;
@@ -926,7 +1107,7 @@ static void oa_tc6_add_empty_chunks_to_spi_buf(struct oa_tc6 *tc6,
header = oa_tc6_prepare_data_header(OA_TC6_DATA_INVALID,
OA_TC6_DATA_START_INVALID,
- OA_TC6_DATA_END_INVALID, 0);
+ OA_TC6_DATA_END_INVALID, 0, false);
while (needed_empty_chunks--) {
__be32 *tx_buf = tc6->spi_data_tx_buf +
@@ -1118,6 +1299,7 @@ netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb)
return NETDEV_TX_OK;
}
+ oa_tc6_tsinfo_tx(skb)->tsc = 0;
spin_lock_bh(&tc6->tx_skb_lock);
tc6->waiting_tx_skb = skb;
spin_unlock_bh(&tc6->tx_skb_lock);
@@ -1151,6 +1333,8 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev)
SET_NETDEV_DEV(netdev, &spi->dev);
mutex_init(&tc6->spi_ctrl_lock);
spin_lock_init(&tc6->tx_skb_lock);
+ tc6->tx_ts_idx = OA_TC6_TTSCA_REG_ID;
+ INIT_LIST_HEAD(&tc6->tx_ts_skb_q);
/* Set the SPI controller to pump at realtime priority */
tc6->spi->rt = true;
@@ -1216,6 +1400,12 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev)
goto phy_exit;
}
+ ret = oa_tc6_update_standard_capability(tc6);
+ if (ret) {
+ dev_err(&tc6->spi->dev, "Failed to read capability\n");
+ goto phy_exit;
+ }
+
ret = devm_request_threaded_irq(&tc6->spi->dev, tc6->spi->irq,
oa_tc6_macphy_isr,
oa_tc6_macphy_threaded_irq,
diff --git a/drivers/net/ethernet/oa_tc6/oa_tc6_ptp.c b/drivers/net/ethernet/oa_tc6/oa_tc6_ptp.c
new file mode 100644
index 000000000000..0cde5545ee27
--- /dev/null
+++ b/drivers/net/ethernet/oa_tc6/oa_tc6_ptp.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Support for hardware timestamping feature for OPEN Alliance
+ * 10BASE‑T1x MAC‑PHY Serial Interface framework
+ *
+ * Author: Selva Rajagopal <selvamani.rajagopal@onsemi.com>
+ */
+
+#include <linux/hrtimer.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/phylink.h>
+#include <linux/spi/spi.h>
+#include <linux/oa_tc6.h>
+#include <linux/net_tstamp.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/ktime.h>
+#include <linux/errno.h>
+
+#include "oa_tc6_std_def.h"
+
+/**
+ * oa_tc6_ptp_register - Registers clock related callbacks
+ * @tc6: oa_tc6 struct.
+ * @info: Describes a PTP hardware clock
+ *
+ * Description: Vendors are expected to set the hardware timestamp
+ * related callbacks before calling this function.
+ *
+ * Return: 0 on success otherwise failed.
+ *
+ */
+int oa_tc6_ptp_register(struct oa_tc6 *tc6, struct ptp_clock_info *info)
+{
+ /* Not supporting hardware timestamp isn't an error */
+ if (!tc6->hw_tstamp_supported)
+ return 0;
+
+ snprintf(info->name, sizeof(info->name), "%s",
+ "OA TC6 PTP clock");
+ tc6->ptp_clock = ptp_clock_register(info, &tc6->spi->dev);
+ if (IS_ERR(tc6->ptp_clock)) {
+ dev_err(&tc6->spi->dev, "Registration of %s failed",
+ info->name);
+ return -EFAULT;
+ }
+ dev_info(&tc6->spi->dev, "%s registered. index %d", info->name,
+ ptp_clock_index(tc6->ptp_clock));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(oa_tc6_ptp_register);
+
+/**
+ * oa_tc6_ptp_unregister - Unregisters clock related callbacks
+ * @tc6: oa_tc6 struct.
+ */
+void oa_tc6_ptp_unregister(struct oa_tc6 *tc6)
+{
+ if (tc6->ptp_clock)
+ ptp_clock_unregister(tc6->ptp_clock);
+}
+EXPORT_SYMBOL_GPL(oa_tc6_ptp_unregister);
+
+MODULE_DESCRIPTION("OPEN Alliance 10BASE‑T1x MAC‑PHY Serial Interface Lib");
+MODULE_AUTHOR("Selva Rajagopal <selvamani.rajagopal@onsemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/oa_tc6/oa_tc6_std_def.h b/drivers/net/ethernet/oa_tc6/oa_tc6_std_def.h
index 6bacf254a36a..3781503be7f5 100644
--- a/drivers/net/ethernet/oa_tc6/oa_tc6_std_def.h
+++ b/drivers/net/ethernet/oa_tc6/oa_tc6_std_def.h
@@ -22,6 +22,7 @@
/* Standard Capabilities Register */
#define OA_TC6_REG_STDCAP 0x0002
#define STDCAP_DIRECT_PHY_REG_ACCESS BIT(8)
+#define STDCAP_FRAME_TIMESTAMP_CAPABILITY BIT(6)
/* Reset Control and Status Register */
#define OA_TC6_REG_RESET 0x0003
@@ -31,9 +32,15 @@
#define OA_TC6_REG_CONFIG0 0x0004
#define CONFIG0_SYNC BIT(15)
#define CONFIG0_ZARFE_ENABLE BIT(12)
+#define CONFIG0_FTSE_ENABLE BIT(7)
+#define CONFIG0_FTSS_64BIT_TSTAMP BIT(6)
/* Status Register #0 */
#define OA_TC6_REG_STATUS0 0x0008
+#define STATUS0_TTSCAC BIT(10)
+#define STATUS0_TTSCAB BIT(9)
+#define STATUS0_TTSCAA BIT(8)
+#define STATUS0_TTSCA_MASK GENMASK(10, 8)
#define STATUS0_RESETC BIT(6)
#define STATUS0_HEADER_ERROR BIT(5)
#define STATUS0_LOSS_OF_FRAME_ERROR BIT(4)
@@ -47,6 +54,7 @@
/* Interrupt Mask Register #0 */
#define OA_TC6_REG_INT_MASK0 0x000C
+#define INT_MASK0_TTSCA_MASK GENMASK(10, 8)
#define INT_MASK0_HEADER_ERR_MASK BIT(5)
#define INT_MASK0_LOSS_OF_FRAME_ERR_MASK BIT(4)
#define INT_MASK0_RX_BUFFER_OVERFLOW_ERR_MASK BIT(3)
@@ -58,6 +66,9 @@
#define OA_TC6_PHY_STD_REG_ADDR_BASE 0xFF00
#define OA_TC6_PHY_STD_REG_ADDR_MASK 0x1F
+/* Tx timestamp capture register A (high) */
+#define OA_TC6_REG_TTSCA_HIGH (0x10)
+
/* Control command header */
#define OA_TC6_CTRL_HEADER_DATA_NOT_CTRL BIT(31)
#define OA_TC6_CTRL_HEADER_WRITE_NOT_READ BIT(29)
@@ -73,6 +84,7 @@
#define OA_TC6_DATA_HEADER_START_WORD_OFFSET GENMASK(19, 16)
#define OA_TC6_DATA_HEADER_END_VALID BIT(14)
#define OA_TC6_DATA_HEADER_END_BYTE_OFFSET GENMASK(13, 8)
+#define OA_TC6_DATA_HEADER_TSC_OFFSET GENMASK(7, 6)
#define OA_TC6_DATA_HEADER_PARITY BIT(0)
/* Data footer */
@@ -84,6 +96,8 @@
#define OA_TC6_DATA_FOOTER_START_VALID BIT(20)
#define OA_TC6_DATA_FOOTER_START_WORD_OFFSET GENMASK(19, 16)
#define OA_TC6_DATA_FOOTER_END_VALID BIT(14)
+#define OA_TC6_DATA_FOOTER_RTSA_VALID BIT(7)
+#define OA_TC6_DATA_FOOTER_RTSP_VALID BIT(6)
#define OA_TC6_DATA_FOOTER_END_BYTE_OFFSET GENMASK(13, 8)
#define OA_TC6_DATA_FOOTER_TX_CREDITS GENMASK(5, 1)
@@ -105,6 +119,12 @@
#define STATUS0_RESETC_POLL_DELAY 1000
#define STATUS0_RESETC_POLL_TIMEOUT 1000000
+#define OA_TC6_TSTAMP_SZ 8
+
+#define OA_TC6_TTSCA_REG_ID 1
+#define OA_TC6_TTSCB_REG_ID 2
+#define OA_TC6_TTSCC_REG_ID 3
+
/* Internal structure for MAC-PHY drivers */
struct oa_tc6 {
struct net_device *netdev;
@@ -127,6 +147,17 @@ struct oa_tc6 {
bool rx_buf_overflow;
bool int_flag;
bool disable_traffic;
+ struct ptp_clock_info ptp_clock_info;
+ struct hwtstamp_config ts_config;
+ struct list_head tx_ts_skb_q;
+ struct ptp_clock *ptp_clock;
+ bool hw_tstamp_supported;
+ bool hw_tstamp_enabled;
+ u32 tx_hwtstamp_pkts;
+ u32 tx_hwtstamp_lost;
+ u32 tx_hwtstamp_err;
+ int vend1_mms;
+ u8 tx_ts_idx;
};
enum oa_tc6_header_type {
@@ -153,5 +184,8 @@ enum oa_tc6_data_end_valid_info {
OA_TC6_DATA_END_INVALID,
OA_TC6_DATA_END_VALID,
};
+
+int oa_tc6_tstamp_ioctl(struct oa_tc6 *tc6, struct ifreq *rq, int cmd);
+
#endif /* OA_TC6_STD_DEF_H */
diff --git a/drivers/net/ethernet/oa_tc6/oa_tc6_tstamp.c b/drivers/net/ethernet/oa_tc6/oa_tc6_tstamp.c
new file mode 100644
index 000000000000..f80f1a4916a3
--- /dev/null
+++ b/drivers/net/ethernet/oa_tc6/oa_tc6_tstamp.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * OPEN Alliance 10BASE‑T1x MAC‑PHY Serial Interface framework
+ *
+ * Author: Selva Rajagopal <selvamani.rajagopal@onsemi.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <linux/mdio.h>
+#include <linux/phy.h>
+#include <linux/oa_tc6.h>
+
+#include "oa_tc6_std_def.h"
+
+static int oa_tc6_set_hwtstamp_settings(struct oa_tc6 *tc6)
+{
+ u32 cfg0, irqm, status0;
+ int ret;
+
+ ret = oa_tc6_read_register(tc6, OA_TC6_REG_CONFIG0, &cfg0);
+ if (ret) {
+ dev_err(&tc6->spi->dev, "Failed to read CFG0 register\n");
+ goto out;
+ }
+
+ ret = oa_tc6_read_register(tc6, OA_TC6_REG_INT_MASK0, &irqm);
+ if (ret) {
+ dev_err(&tc6->spi->dev, "failed to read IRQM register\n");
+ goto out;
+ }
+
+ /* Hardware timestamp feature supports 64-bit timestamp only */
+ if (tc6->ts_config.tx_type == HWTSTAMP_TX_ON ||
+ tc6->ts_config.rx_filter == HWTSTAMP_FILTER_ALL)
+ cfg0 |= (CONFIG0_FTSE_ENABLE |
+ CONFIG0_FTSS_64BIT_TSTAMP);
+ else
+ cfg0 &= ~CONFIG0_FTSE_ENABLE;
+
+ if (tc6->ts_config.tx_type == HWTSTAMP_TX_ON)
+ irqm &= ~INT_MASK0_TTSCA_MASK;
+ else
+ irqm |= INT_MASK0_TTSCA_MASK;
+
+ /* Clear timestamp related IRQs */
+ status0 = STATUS0_TTSCA_MASK;
+ ret = oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, status0);
+ if (ret) {
+ dev_err(&tc6->spi->dev, "failed to write STATUS0 register\n");
+ goto out;
+ }
+
+ ret = oa_tc6_write_register(tc6, OA_TC6_REG_INT_MASK0, irqm);
+ if (ret) {
+ dev_err(&tc6->spi->dev, "failed to write IRQM register\n");
+ goto out;
+ }
+
+ ret = oa_tc6_write_register(tc6, OA_TC6_REG_CONFIG0, cfg0);
+ if (ret) {
+ dev_err(&tc6->spi->dev, "failed to write CFG0 register\n");
+ goto out;
+ }
+ if (cfg0 & CONFIG0_FTSE_ENABLE)
+ tc6->hw_tstamp_enabled = true;
+ else
+ tc6->hw_tstamp_enabled = false;
+out:
+ return ret;
+}
+
+/**
+ * oa_tc6_hwtstamp_get - gets hardware timestamp config
+ * @tc6: oa_tc6 struct.
+ * @cfg: kernel copy of hardware timestamp config
+ */
+void oa_tc6_hwtstamp_get(struct oa_tc6 *tc6,
+ struct kernel_hwtstamp_config *cfg)
+{
+ hwtstamp_config_to_kernel(cfg, &tc6->ts_config);
+}
+EXPORT_SYMBOL_GPL(oa_tc6_hwtstamp_get);
+
+/**
+ * oa_tc6_hwtstamp_set - sets hardware timestamp config
+ * @tc6: oa_tc6 struct.
+ * @cfg: kernel copy of hardware timestamp config
+ *
+ * Return: 0 on success otherwise failed.
+ */
+int oa_tc6_hwtstamp_set(struct oa_tc6 *tc6,
+ struct kernel_hwtstamp_config *cfg)
+{
+ if (!netif_running(tc6->netdev))
+ return -EIO;
+
+ if (!tc6->hw_tstamp_supported)
+ return -EOPNOTSUPP;
+
+ switch (cfg->tx_type) {
+ case HWTSTAMP_TX_OFF:
+ case HWTSTAMP_TX_ON:
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ switch (cfg->rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ case HWTSTAMP_FILTER_ALL:
+ case HWTSTAMP_FILTER_SOME:
+ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+ case HWTSTAMP_FILTER_NTP_ALL:
+ break;
+ default:
+ return -ERANGE;
+ }
+ hwtstamp_config_from_kernel(&tc6->ts_config, cfg);
+
+ /* Supports timestamping all traffic */
+ if (cfg->rx_filter != HWTSTAMP_FILTER_NONE)
+ tc6->ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
+ return oa_tc6_set_hwtstamp_settings(tc6);
+}
+EXPORT_SYMBOL_GPL(oa_tc6_hwtstamp_set);
+
+/**
+ * oa_tc6_get_ts_stats - Provides timestamping stats
+ * @tc6: oa_tc6 struct.
+ * @stats: ethtool data structure to fill in
+ */
+void oa_tc6_get_ts_stats(struct oa_tc6 *tc6,
+ struct ethtool_ts_stats *stats)
+{
+ stats->pkts = tc6->tx_hwtstamp_pkts;
+ stats->err = tc6->tx_hwtstamp_err;
+ stats->lost = tc6->tx_hwtstamp_lost;
+}
+EXPORT_SYMBOL_GPL(oa_tc6_get_ts_stats);
+
+int oa_tc6_tstamp_ioctl(struct oa_tc6 *tc6, struct ifreq *rq, int cmd)
+{
+ struct kernel_hwtstamp_config kcfg;
+ struct hwtstamp_config tscfg;
+ int ret = 0;
+
+ if (!tc6->hw_tstamp_supported)
+ return -EOPNOTSUPP;
+
+ if (cmd == SIOCSHWTSTAMP) {
+ if (copy_from_user(&tscfg, rq->ifr_data,
+ sizeof(tscfg)))
+ return -EFAULT;
+
+ if (tscfg.flags)
+ return -EINVAL;
+ hwtstamp_config_to_kernel(&kcfg, &tscfg);
+ ret = oa_tc6_hwtstamp_set(tc6, &kcfg);
+ if (ret)
+ return ret;
+ }
+ if (copy_to_user(rq->ifr_data, &tc6->ts_config,
+ sizeof(tc6->ts_config)))
+ ret = -EFAULT;
+ return ret;
+}
+
+/**
+ * oa_tc6_get_ts_info - Provides timestamp info for ethtool
+ * @tc6: oa_tc6 struct.
+ * @info: ethtool timestamping info structure
+ *
+ * Return: 0 on success otherwise failed.
+ */
+int oa_tc6_get_ts_info(struct oa_tc6 *tc6,
+ struct kernel_ethtool_ts_info *info)
+{
+ if (!tc6->ptp_clock)
+ return ethtool_op_get_ts_info(tc6->netdev, info);
+
+ info->so_timestamping = SOF_TIMESTAMPING_RAW_HARDWARE |
+ SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE;
+ info->phc_index = ptp_clock_index(tc6->ptp_clock);
+ info->tx_types = BIT(HWTSTAMP_TX_ON);
+ info->rx_filters = BIT(HWTSTAMP_FILTER_ALL);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(oa_tc6_get_ts_info);
+
+MODULE_DESCRIPTION("OPEN Alliance 10BASE‑T1x MAC‑PHY Serial Interface Lib");
+MODULE_AUTHOR("Selva Rajagopal <selvamani.rajagopal@onsemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/oa_tc6.h b/include/linux/oa_tc6.h
index 39b80033dfa9..e678a12aedf1 100644
--- a/include/linux/oa_tc6.h
+++ b/include/linux/oa_tc6.h
@@ -12,6 +12,7 @@
#include <linux/etherdevice.h>
#include <linux/spi/spi.h>
+#include <linux/ptp_clock_kernel.h>
/* PHY – Clause 45 registers memory map selector (MMS) as per table 6 in
* the OPEN Alliance specification.
@@ -36,4 +37,15 @@ int oa_tc6_read_registers(struct oa_tc6 *tc6, u32 address, u32 value[],
u8 length);
netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb);
int oa_tc6_zero_align_receive_frame_enable(struct oa_tc6 *tc6);
+int oa_tc6_ptp_register(struct oa_tc6 *tc6, struct ptp_clock_info *info);
+int oa_tc6_ioctl(struct oa_tc6 *tc6, struct ifreq *rq, int cmd);
+int oa_tc6_get_ts_info(struct oa_tc6 *tc6,
+ struct kernel_ethtool_ts_info *info);
+void oa_tc6_hwtstamp_get(struct oa_tc6 *tc6,
+ struct kernel_hwtstamp_config *cfg);
+void oa_tc6_get_ts_stats(struct oa_tc6 *tc6,
+ struct ethtool_ts_stats *stats);
+int oa_tc6_hwtstamp_set(struct oa_tc6 *tc6,
+ struct kernel_hwtstamp_config *cfg);
+void oa_tc6_ptp_unregister(struct oa_tc6 *tc6);
#endif /* _LINUX_OA_TC6_H */
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox