mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: MD Danish Anwar <danishanwar@ti.com>
To: <netdev-bot+sashiko@kernel.org>
Cc: <andrew+netdev@lunn.ch>, <davem@davemloft.net>,
	<edumazet@google.com>, <kuba@kernel.org>, <pabeni@redhat.com>,
	<s-vadapalli@ti.com>, <rogerq@kernel.org>, <kees@kernel.org>,
	<arnd@arndb.de>, <stefan.wiehler@nokia.com>, <c-vankar@ti.com>,
	<vigneshr@ti.com>, <netdev@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>, <linux-omap@vger.kernel.org>
Subject: Re: [PATCH net v2] net: ti: am65-cpsw-switchdev: flush dynamic FDB entries by port on delete
Date: Tue, 29 Sep 2026 15:18:40 +0530	[thread overview]
Message-ID: <10454dbe-1d94-48ed-afa5-5910e6df21ad@ti.com> (raw)
In-Reply-To: <179057463349.3145.13175831323497920715@kernel.org>



On 28/09/26 11:20 am, netdev-bot+sashiko@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential
> issue(s) to consider.
> 
> Critical: 0 · High: 0 (1) · Medium: 2 (1) · Low: 0
> 
> 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).
> 
> - [Medium] The patch removes the `!fdb->added_by_user` gate from the
>    SWITCHDEV_FDB_DEL_TO_DEVICE branch of am65_cpsw_switchdev_event_work().
> - [Medium] Before this patch, dynamic DEL_TO_DEVICE events hit the
>    `!fdb->added_by_user` break and never touched hardware.
> 
> Pre-existing issues:
> - [High] cpsw_ale has no internal lock (no spinlock, mutex or lockdep
>    assert in cpsw_ale.c).
> - [Medium] This problem wasn't introduced by this patch, but the older
>    CPSW switchdev driver has the same flush defect.
> 
> --
> 
> net: ti: am65-cpsw-switchdev: flush dynamic FDB entries by port on delete
> 
> This patch drops the added_by_user gate on SWITCHDEV_FDB_DEL_TO_DEVICE in
> am65-cpsw-switchdev, so dynamic FDB deletes now reach the hardware. It adds
> cpsw_ale_del_ucast_dynamic_by_port(), which walks the ALE and frees
> non-persistent unicast rows that match (addr, port). When vid is 0 it frees
> matching rows in every vid.
> 
>> diff --git a/drivers/net/ethernet/ti/am65-cpsw-switchdev.c b/drivers/net/ethernet/ti/am65-cpsw-switchdev.c
>> index 53cdac272b58..0dc681748b0e 100644
>> --- a/drivers/net/ethernet/ti/am65-cpsw-switchdev.c
>> +++ b/drivers/net/ethernet/ti/am65-cpsw-switchdev.c
>> @@ -397,13 +397,19 @@ static void am65_cpsw_switchdev_event_work(struct work_struct *work)
>>   			   fdb->addr, fdb->vid, fdb->added_by_user,
>>   			   fdb->offloaded, port_id);
>>   
>> -		if (!fdb->added_by_user || fdb->is_local)
>> +		if (fdb->is_local)
>>   			break;
> 
> [Severity: Medium]
> 
> With the added_by_user gate removed, does routine bridge software ageing
> now remove live ALE rows as well?
> 
> br_fdb_cleanup() ages out dynamic entries like this:
> 
> br_fdb_cleanup()
>    fdb_delete(br, f, true)
>      fdb_notify(br, f, RTM_DELNEIGH, true)
>        br_switchdev_fdb_notify()
>          -> SWITCHDEV_FDB_DEL_TO_DEVICE, added_by_user = 0
> 
> This driver never reports hardware-learned entries to the bridge (there is
> no SWITCHDEV_FDB_ADD_TO_BRIDGE). Also, am65_cpsw_port_attr_set() does not
> handle SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME. As a result, the bridge only
> refreshes an entry when a frame from that station reaches the CPU.
> 

CPSW driver doesn't support any mechanism to indicate that an entry was 
learned by ALE so SWITCHDEV_FDB_ADD_TO_BRIDGE can't be implemented.

> Take a station whose unicast traffic is switched only in hardware. Its
> bridge entry expires after the default 300 seconds, while traffic keeps
> refreshing its ALE row. The ALE ages out untouched rows after about 30
> seconds (AM65_CPSW_ALE_AGEOUT_DEFAULT with CPSW_ALE_F_HW_AUTOAGING), so any
> row still present at that point is in use.
> 
> cpsw_ale_del_ucast_dynamic_by_port() does not check touched/untouched
> state. With vid == 0 it frees every matching row for that MAC on the port.
> 
> Would traffic to that station then be flooded as unknown unicast until the
> station transmits again? That would happen once per bridge ageing interval,
> with a full ALE walk each time.

Yes, for stations whose traffic never reaches the CPU (pure hardware 
forwarding). The flush fix is the primary goal of this patch; the ageing 
side-effect is a trade-off.

> 
> The commit message only describes this as a flush fix. Should it mention
> the change in ageing behaviour, or should the ageing path be filtered out?
> 

I will update the commit message to mention this change in ageing 
behavior. No code change regarding this in v3.

>>   		if (memcmp(port->slave.mac_addr, (u8 *)fdb->addr, ETH_ALEN) == 0)
>>   			port_id = HOST_PORT_NUM;
>>   
>> -		cpsw_ale_del_ucast(cpsw->ale, (u8 *)fdb->addr, port_id,
>> -				   fdb->vid ? ALE_VLAN : 0, fdb->vid);
>> +		if (!fdb->added_by_user)
>> +			cpsw_ale_del_ucast_dynamic_by_port(cpsw->ale,
>> +							   (u8 *)fdb->addr,
>> +							   port_id,
>> +							   fdb->vid);
> 
> [Severity: Medium]
> 
> Before this patch, dynamic DEL_TO_DEVICE events hit the break before any
> hardware access. Now each one does a full ALE MMIO walk. Does this path
> need a runtime PM reference?
> 
> On port-down the sequence looks like this:
> 
> am65_cpsw_nuss_ndo_slave_stop()
>    am65_cpsw_nuss_common_stop()
>      cpsw_ale_stop()
>    pm_runtime_put(common->dev)
> 
> NETDEV_DOWN then triggers:
> 
> br_stp_disable_port()
>    br_fdb_delete_by_port()
>      -> one SWITCHDEV_FDB_DEL_TO_DEVICE per dynamic entry, added_by_user = 0
> 
> am65_cpsw_switchdev_event_work()
>    cpsw_ale_del_ucast_dynamic_by_port()
>      cpsw_ale_read()   /* writel_relaxed/readl_relaxed of ALE regs */
> 
> When the last port goes down, these work items access the ALE registers
> after the last runtime PM reference has been dropped. Other ALE users in
> am65-cpsw-nuss.c take pm_runtime_resume_and_get() first, for example the
> VLAN add/kill and set_mac_address paths.
> 
> With CONFIG_TI_K3_AM65_CPTS disabled, am65_cpsw_init_cpts() returns before
> it calls pm_runtime_forbid(), so the device can runtime suspend. The comment
> there notes that K3 CPSWxG modules may lose context on ON->OFF transitions.
> 
> Could this access a suspended module?
> 

I will add runtime PM reference in v3.

>> +		else
>> +			cpsw_ale_del_ucast(cpsw->ale, (u8 *)fdb->addr, port_id,
>> +					   fdb->vid ? ALE_VLAN : 0, fdb->vid);
>>   		break;
> 
> [Severity: Medium]
> 
> This is a pre-existing issue that was not introduced by this patch. The
> older CPSW switchdev driver seems to have the same flush problem.
> cpsw_switchdev_event_work() in drivers/net/ethernet/ti/cpsw_switchdev.c
> still has:
> 
> 	if (!fdb->added_by_user || fdb->is_local)
> 		break;
> 	...
> 	cpsw_ale_del_ucast(cpsw->ale, (u8 *)fdb->addr, port,
> 			   fdb->vid ? ALE_VLAN : 0, fdb->vid);
> 
> cpsw_init_host_port() in cpsw_new.c enables ALE_VLAN_AWARE, so the nonzero
> vid mismatch described in the commit message applies there too.
> cpsw_switchdev_event() queues DEL_TO_DEVICE work without any filtering.
> 
> Should cpsw_switchdev.c also use the new
> cpsw_ale_del_ucast_dynamic_by_port() helper, or is that planned as a
> separate fix?
> 

Sure, I will extend the fix to cpsw_switchdev.c driver as well. This 
will be fixed in v3.

>> diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c
>> index e202bba49480..86bcea5744e0 100644
>> --- a/drivers/net/ethernet/ti/cpsw_ale.c
>> +++ b/drivers/net/ethernet/ti/cpsw_ale.c
> 
> [ ... ]
> 
>> @@ -441,6 +441,48 @@ static int cpsw_ale_find_ageable(struct cpsw_ale *ale)
>>   	return -ENOENT;
>>   }
>>   
>> +int cpsw_ale_del_ucast_dynamic_by_port(struct cpsw_ale *ale, const u8 *addr,
>> +				       int port, u16 vid)
>> +{
>> +	u32 ale_entry[ALE_ENTRY_WORDS];
>> +	int type, ucast_type, idx;
>> +	u8 entry_addr[6];
>> +	int deleted = 0;
>> +	int entry_vid;
>> +
>> +	for (idx = 0; idx < ale->params.ale_entries; idx++) {
>> +		cpsw_ale_read(ale, idx, ale_entry);
> 
> [ ... ]
> 
>> +		memset(ale_entry, 0, sizeof(ale_entry));
>> +		cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
>> +		cpsw_ale_write(ale, idx, ale_entry);
> 
> [Severity: High]
> 
> This is a pre-existing issue: the existing switchdev ucast add/del and VLAN
> ops can already race here. This patch adds a new full-table read/write
> walker and runs it much more often.
> 
> ALE table accesses go through a shared indirect window: the index goes into
> ALE_TABLE_CONTROL, then the ALE_TABLE words are read or written.
> cpsw_ale.c has no internal lock. The only thing serializing this loop is
> rtnl_lock() taken by the switchdev work.
> 
> am65_cpsw_nuss_ndo_slave_set_rx_mode() is called on the legacy
> ndo_set_rx_mode path in __dev_set_rx_mode(). It runs under
> netif_addr_lock_bh() without rtnl. It calls cpsw_ale_flush_multicast(),
> which reads every ALE_TYPE_ADDR/ALE_TYPE_VLAN_ADDR row, including unicast
> rows, and always writes it back:
> 
> 	for (idx = 0; idx < ale->params.ale_entries; idx++) {
> 		cpsw_ale_read(ale, idx, ale_entry);
> 		...
> 		cpsw_ale_write(ale, idx, ale_entry);
> 	}
> 
> An unprivileged user can reach that path without rtnl:
> 
> setsockopt(IPV6_ADD_MEMBERSHIP)
>    __ipv6_sock_mc_join()
>      __ipv6_dev_mc_inc()
>        igmp6_group_added()
>          dev_mc_add()
>            __dev_mc_add()
>              __dev_set_rx_mode()
>                am65_cpsw_nuss_ndo_slave_set_rx_mode()
>                  cpsw_ale_flush_multicast()
> 
> Suppose cpsw_ale_flush_multicast() reads row N, this function then frees
> row N, and cpsw_ale_flush_multicast() writes its stale copy back. Doesn't
> the deleted dynamic entry come back?
> 
> The ALE_TABLE_CONTROL writes from the two paths could also interleave.
> Could one path then read another row's contents, or write its words to the
> wrong index? That would free or overwrite an unrelated VLAN, persistent or
> multicast row.
> 
> Should ALE table accesses be serialized by a lock inside cpsw_ale?
> 

This is a valid pre-existing race. Adding a mutex to cpsw_ale would be a 
separate cleanup. The current patch scope is limited to the flush fix 
and pm_runtime. The ALE locking can be addressed in a follow-up patch 
since the race exists independently of this change.

This will not be fixed in v3.

pw-bot: cr

>> +		deleted++;
>> +
>> +		if (vid)
>> +			return 0;
>> +	}
>> +
>> +	return deleted ? 0 : -ENOENT;
>> +}
>> +EXPORT_SYMBOL_GPL(cpsw_ale_del_ucast_dynamic_by_port);
> 
> [ ... ]
> 


      reply	other threads:[~2026-09-29  9:49 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-24  5:21 MD Danish Anwar
2026-09-28  5:50 ` netdev-bot+sashiko
2026-09-29  9:48   ` MD Danish Anwar [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=10454dbe-1d94-48ed-afa5-5910e6df21ad@ti.com \
    --to=danishanwar@ti.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=arnd@arndb.de \
    --cc=c-vankar@ti.com \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=kees@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-omap@vger.kernel.org \
    --cc=netdev-bot+sashiko@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=rogerq@kernel.org \
    --cc=s-vadapalli@ti.com \
    --cc=stefan.wiehler@nokia.com \
    --cc=vigneshr@ti.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®