Merge branch 'net-mscc-ocelot-fix-missing-lock-in-ocelot_port_xmit'

Ziyi Guo says:

====================
net: mscc: ocelot: fix missing lock in ocelot_port_xmit()

ocelot_port_xmit() calls ocelot_can_inject() and
ocelot_port_inject_frame() without holding the injection group lock.
Both functions contain lockdep_assert_held() for the injection lock,
and the correct caller felix_port_deferred_xmit() properly acquires
the lock using ocelot_lock_inj_grp() before calling these functions.

this v3 splits the fix into a 3-patch series to separate
refactoring from the behavioral change:

  1/3: Extract the PTP timestamp handling into an ocelot_xmit_timestamp()
       helper so the logic isn't duplicated when the function is split.

  2/3: Split ocelot_port_xmit() into ocelot_port_xmit_fdma() and
       ocelot_port_xmit_inj(), keeping the FDMA and register injection
       code paths fully separate.

  3/3: Add ocelot_lock_inj_grp()/ocelot_unlock_inj_grp() in
       ocelot_port_xmit_inj() to fix the missing lock protection.

Patches 1-2 are pure refactors with no behavioral change.
Patch 3 is the actual bug fix.
====================

Link: https://patch.msgid.link/20260208225602.1339325-1-n7l8m4@u.northwestern.edu
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-02-12 19:01:17 -08:00
commit 8383522821

View File

@ -551,7 +551,28 @@ static int ocelot_port_stop(struct net_device *dev)
return 0;
}
static netdev_tx_t ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
static bool ocelot_xmit_timestamp(struct ocelot *ocelot, int port,
struct sk_buff *skb, u32 *rew_op)
{
if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
struct sk_buff *clone = NULL;
if (ocelot_port_txtstamp_request(ocelot, port, skb, &clone)) {
kfree_skb(skb);
return false;
}
if (clone)
OCELOT_SKB_CB(skb)->clone = clone;
*rew_op = ocelot_ptp_rew_op(skb);
}
return true;
}
static netdev_tx_t ocelot_port_xmit_fdma(struct sk_buff *skb,
struct net_device *dev)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
@ -559,36 +580,52 @@ static netdev_tx_t ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
int port = priv->port.index;
u32 rew_op = 0;
if (!static_branch_unlikely(&ocelot_fdma_enabled) &&
!ocelot_can_inject(ocelot, 0))
return NETDEV_TX_BUSY;
if (!ocelot_xmit_timestamp(ocelot, port, skb, &rew_op))
return NETDEV_TX_OK;
/* Check if timestamping is needed */
if (ocelot->ptp && (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) {
struct sk_buff *clone = NULL;
if (ocelot_port_txtstamp_request(ocelot, port, skb, &clone)) {
kfree_skb(skb);
return NETDEV_TX_OK;
}
if (clone)
OCELOT_SKB_CB(skb)->clone = clone;
rew_op = ocelot_ptp_rew_op(skb);
}
if (static_branch_unlikely(&ocelot_fdma_enabled)) {
ocelot_fdma_inject_frame(ocelot, port, rew_op, skb, dev);
} else {
ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb);
consume_skb(skb);
}
ocelot_fdma_inject_frame(ocelot, port, rew_op, skb, dev);
return NETDEV_TX_OK;
}
static netdev_tx_t ocelot_port_xmit_inj(struct sk_buff *skb,
struct net_device *dev)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
int port = priv->port.index;
u32 rew_op = 0;
ocelot_lock_inj_grp(ocelot, 0);
if (!ocelot_can_inject(ocelot, 0)) {
ocelot_unlock_inj_grp(ocelot, 0);
return NETDEV_TX_BUSY;
}
if (!ocelot_xmit_timestamp(ocelot, port, skb, &rew_op)) {
ocelot_unlock_inj_grp(ocelot, 0);
return NETDEV_TX_OK;
}
ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb);
ocelot_unlock_inj_grp(ocelot, 0);
consume_skb(skb);
return NETDEV_TX_OK;
}
static netdev_tx_t ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
{
if (static_branch_unlikely(&ocelot_fdma_enabled))
return ocelot_port_xmit_fdma(skb, dev);
return ocelot_port_xmit_inj(skb, dev);
}
enum ocelot_action_type {
OCELOT_MACT_LEARN,
OCELOT_MACT_FORGET,