Re: [PATCH] mlxsw: spectrum_ptp: release parsing depth on MTPPPC write failure

From: netdev-bot+sashiko

Date: Sat Nov 07 2026 - 07:07:55 EST


Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider.

Critical: 0 · High: 1 (1) · Medium: 0 · Low: 0 (1)

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [High] The patch fixes the enable-path leak but adds the same leak on
the port-removal path.

Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but
mlxsw_sp1_ptp_mtpppc_update() has no serialization between its two…
- [Low] The new rollback in mlxsw_sp1_ptp_mtpppc_update()
(spectrum_ptp.c:1223-1226) calls mlxsw_sp_parsing_depth_dec().

--

mlxsw: spectrum_ptp: release parsing depth on MTPPPC write failure

This patch reorders mlxsw_sp1_ptp_mtpppc_update() so the MTPPPC register is
written before the parsing depth reference is adjusted. If the write fails,
the reference taken on the first enable is released. On the disable
transition, the reference is now dropped only after the write succeeds.

> diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c
> index 9c5862f4e16a3..0db77af4828f1 100644
> --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c
> +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ptp.c

[ ... ]

> @@ -1214,12 +1215,21 @@ static int mlxsw_sp1_ptp_mtpppc_update(struct mlxsw_sp_port *mlxsw_sp_port,
> netdev_err(mlxsw_sp_port->dev, "Failed to increase parsing depth");
> return err;
> }
> + depth_inc = true;
> }
> +
> + err = mlxsw_sp1_ptp_mtpppc_set(mlxsw_sp_port->mlxsw_sp,
> + ing_types, egr_types);
> + if (err) {
> + if (depth_inc)
> + mlxsw_sp_parsing_depth_dec(mlxsw_sp);

[Severity: Low]
This is a pre-existing issue, but mlxsw_sp_parsing_depth_dec() ignores the
result of the MPRS write and sets the cached depth anyway:

drivers/net/ethernet/mellanox/mlxsw/spectrum.c:mlxsw_sp_parsing_depth_dec() {
...
mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mprs), mprs_pl);
mlxsw_sp->parsing.parsing_depth = MLXSW_SP_DEFAULT_PARSING_DEPTH;
...
}

Suppose the MPRS write also fails on this new rollback path. Would the ASIC
then stay at the increased parsing depth while software records the default
depth with no references held?

The existing VXLAN and IPIP callers have the same behaviour. The next MPRS
write would bring hardware and software back in line.

> + return err;
> + }
> +
> if (!(ing_types || egr_types) && (orig_ing_types || orig_egr_types))
> mlxsw_sp_parsing_depth_dec(mlxsw_sp);

[Severity: High]
Can this leak the parsing depth reference when a port is removed?

mlxsw_sp_port_remove() calls mlxsw_sp_port_ptp_clear(), and that helper
ignores the return value of hwtstamp_set():

drivers/net/ethernet/mellanox/mlxsw/spectrum.c:mlxsw_sp_port_ptp_clear() {
struct kernel_hwtstamp_config config = {};

mlxsw_sp_port->mlxsw_sp->ptp_ops->hwtstamp_set(mlxsw_sp_port, &config,
NULL);
}

Take the case where the removed port is the last one with timestamping
enabled and the MTPPPC write fails during this clear. This can happen on
devlink reload, port split/unsplit, line card removal or driver unbind.

mlxsw_sp1_ptp_mtpppc_update() now returns before the disable-path
mlxsw_sp_parsing_depth_dec(). mlxsw_sp1_ptp_hwtstamp_set() then returns
before it clears ptp.ing_types and ptp.egr_types.

mlxsw_sp_port_remove() carries on regardless:

mlxsw_sp_port_ptp_clear(mlxsw_sp_port);
mlxsw_sp_port_vlan_classification_set(mlxsw_sp_port, true, true);
mlxsw_sp->ports[local_port] = NULL;
...
free_netdev(mlxsw_sp_port->dev);

After the port leaves mlxsw_sp->ports[], no later orig_ing_types or
orig_egr_types scan can see it, so nothing ever drops that reference.

The next first enable on another port sees orig == 0 and takes the
reference again, so the count goes to 2. Disabling that port only brings it
back to 1, and MPRS stays at the increased depth even though MTPPPC has
been written to 0.

mlxsw_sp1_ptp_fini() then writes MTPPPC to 0 without releasing anything,
and mlxsw_sp_parsing_fini() would hit:

WARN_ON_ONCE(refcount_read(&mlxsw_sp->parsing.parsing_depth_ref));

The old code dropped the reference before the write. That is the behaviour
this caller depends on, since it throws away the port's software state
whatever the result.

The commit message says:

so that the reference count always matches the programmed state.

Would the removal path need a forced release, for example in
mlxsw_sp_port_ptp_clear(), for this to hold?

>
> - return mlxsw_sp1_ptp_mtpppc_set(mlxsw_sp_port->mlxsw_sp,
> - ing_types, egr_types);
> + return 0;
> }

[Severity: High]
This is a pre-existing issue, but is mlxsw_sp1_ptp_mtpppc_update()
serialized against port removal at all?

The ndo_hwtstamp_set path runs under RTNL only: dev_ioctl() takes
rtnl_net_lock() for SIOCSHWTSTAMP. mlxsw_sp_port_remove() calls
mlxsw_sp_port_ptp_clear() after unregister_netdev() has already released
RTNL, so that path holds only the devlink instance lock.

The function reads every port's types with no common lock held:

for (i = 1; i < mlxsw_core_max_ports(mlxsw_sp->core); i++) {
tmp = mlxsw_sp->ports[i];
if (tmp) {
orig_ing_types |= tmp->ptp.ing_types;
...

It then decides whether to inc or dec and writes the global MTPPPC
register. The per-port types are published only afterwards, in
mlxsw_sp1_ptp_hwtstamp_set(). parsing.lock is taken only inside the inc/dec
helpers, so it does not cover the check-then-act sequence.

Start with port A as the only timestamping port (ref = 1):

CPU0 (removing port A) CPU1 (enabling port B)
mtpppc_update()
orig != 0, new == 0
mtpppc_update()
sees A's types, skips inc
MTPPPC = A|B
MTPPPC = 0
parsing_depth_dec() -> ref = 0

Port B now believes timestamping is enabled, but MTPPPC is 0 and the parsing
depth is back at the default. When B is disabled later it decrements again.
That either underflows the refcount or drops a reference held by VXLAN or
IPIP.

There is also a lifetime problem. CPU1 loads tmp = mlxsw_sp->ports[i] and
then dereferences tmp->ptp.*. In the meantime CPU0 can set
mlxsw_sp->ports[local_port] = NULL and call free_netdev(). The port is
netdev_priv(), so can this read freed memory?

Under this concurrency, the "always matches the programmed state" claim in
the commit message doesn't seem to hold either.

--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261103102851.1340480-1-vulab%40iscas.ac.cn