* Re: [PATCH] net: bnx2x: validate firmware section bounds without overflow
From: Andrew Lunn @ 2026-07-06 15:30 UTC (permalink / raw)
To: Pengpeng Hou
Cc: Sudarsana Kalluru, Manish Chopra, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, netdev, linux-kernel
In-Reply-To: <20260706093403.81017-1-pengpeng@iscas.ac.cn>
On Mon, Jul 06, 2026 at 05:34:03PM +0800, Pengpeng Hou wrote:
> The firmware loader validates each section with offset + length before it
> uses section offsets to read data from the firmware image. Both fields are
> 32-bit values from the firmware header, so the addition can wrap before it
> is compared with the firmware size.
>
> Validate sections as offset <= size and length <= size - offset instead.
> Also require the firmware version section to contain the four bytes that
> bnx2x_check_firmware() reads later.
>
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
Please stop posting patches. I gets very frustrating having to point
out again and again the same errors.
Please work on one patch. Make all the basic errors once, on one
patch, until you get it merged. Then post the next patch, hopefully
correctly.
Please reply to all your patches with:
pw-bot: cr
so that they get dropped from the review system, so they don't waste
our time.
Andrew
^ permalink raw reply
* Re: [PATCH v2 net 3/3] ipv4: igmp: Fix potential memory leaks in igmp_mod_timer() and igmp_stop_timer()
From: Ido Schimmel @ 2026-07-06 15:30 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Kuniyuki Iwashima, David Ahern, netdev, eric.dumazet
In-Reply-To: <20260705181756.963063-4-edumazet@google.com>
On Sun, Jul 05, 2026 at 06:17:56PM +0000, Eric Dumazet wrote:
> When a timer is deleted and not re-armed in igmp_mod_timer(), or stopped
> in igmp_stop_timer(), the code currently decrements the reference counter
> of the multicast list entry @im using refcount_dec(&im->refcnt).
>
> However, both functions can be called from the RCU reader path:
> - igmp_mod_timer() via igmp_heard_query() -> for_each_pmc_rcu()
> - igmp_stop_timer() via igmp_rcv() -> igmp_heard_report()
>
> If the group im was concurrently removed from the list by ip_mc_dec_group(),
> its reference count might have already been decremented to 1.
>
> In this case, timer_delete() succeeds, and refcount_dec() decrements
> the refcount from 1 to 0. Since refcount_dec() does not free the object
> when it hits 0 (unlike ip_ma_put()), the im structure is leaked.
>
> Fix this by using ip_ma_put(im) instead of refcount_dec(&im->refcnt),
> and deferring the put until after the spinlock is released.
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
^ permalink raw reply
* Re: [PATCH net v2] tun/tap & vhost-net: make qdisc backpressure opt-in via IFF_BACKPRESSURE
From: Simon Schippers @ 2026-07-06 15:33 UTC (permalink / raw)
To: Michael S. Tsirkin, Brett A C Sheffield
Cc: Willem de Bruijn, Jason Wang, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev, Simon Horman,
Jonathan Corbet, Shuah Khan, Andrew Lunn, Tim Gebauer, linux-doc,
linux-kernel
In-Reply-To: <20260706091706-mutt-send-email-mst@kernel.org>
On 7/6/26 15:23, Michael S. Tsirkin wrote:
> On Mon, Jul 06, 2026 at 12:11:15PM +0200, Brett A C Sheffield wrote:
>> On 2026-07-06 11:42, Simon Schippers wrote:
>>> Commit 1d6e569b7d0c ("tun/tap & vhost-net: avoid ptr_ring tail-drop
>>> when a qdisc is present") did not show a relevant performance regression
>>> in my testing but on Brett Sheffield's librecast testbed it shows a
>>> significant performance drop in a IPv6 multicast testcase. The regression
>>> can be pinpointed when multiple iperf3 UDP threads are sending. For 8
>>> threads the performance dropped from 13.5 Gbit/s to 9.13 Gbit/s. This is
>>> the reason why this patch makes the qdisc backpressure behavior opt-in.
>>
>> Your v1 commit message was correct. The iperf3 tests were TCP, not UDP.
>>
>> The original failing test that alerted me to the problem was IPv6 multicast
>> (UDP), but the reproducer tests I provided stats for in the regression report
>> were TCP "To eliminate my code and any multicast weirdness" and also to verify
>> that this also affected TCP.
>>
>> Sorry for the confusion. The command lines used are in the regression report.
>>
>> I've tested the v2 patch (with IPv6 multicast), and verified the
>> previously failing test passes.
>>
>> Tested-by: Brett A C Sheffield <bacs@librecast.net>
>>
>> Cheers,
>
>
> Just to clarify, it's more of a work-around, not a fix.
>
> It's not really great to have a flag that says "change something
> opaque in the internals of the device, it affects performance
> in some way, we can't predict how".
Yes, I agree, but in the end I am just using netif_tx_stop_queue() and
netif_tx_wake_queue()...
>
> So maybe we really should revert for now, and work on something
> more coherent for the next linux.
But even if we could perfectly fix the performance issues, maybe users
even users rely on the dropping behavior. From Brett [1]:
"In our multicast use case data is sent by multiple threads to multiple
groups simultaneously, this just breaks things to the extent that a
<2 second test times out after 5 minutes."
We are *not* factor 5min * 60sec/min / 2s = 150 times slower than without
the patchset. My theory is that the sender sends a fixed amount of data
of which most is dropped without backpressure, which is much faster then
the real processing, and so the test *relies* on the tail-dropping to
work.
@Brett can you maybe support this theory?
Thank you both very much! :)
[1] Link: https://lore.kernel.org/netdev/akVnoOYQOrt8k-Gu@karahi.librecast.net/
>
>
>>
>> Brett
>> --
>> Brett Sheffield (he/him)
>> Librecast - Decentralising the Internet with Multicast
>> https://librecast.net/
>> https://blog.brettsheffield.com/
>
^ permalink raw reply
* Re: [PATCHv2 3/4] mmc: sdhci-esdhc-mcf: do not use readl()/writel() on ColdFire
From: Ulf Hansson @ 2026-07-06 15:46 UTC (permalink / raw)
To: Greg Ungerer
Cc: linux-m68k, linux-kernel, arnd, wei.fang, frank.li, shenwei.wang,
imx, netdev, nico, adureghello, ulfh, linux-mmc, linux-can,
linux-spi, olteanv
In-Reply-To: <20260609142139.1563360-5-gerg@linux-m68k.org>
On Tue, Jun 9, 2026 at 4:27 PM Greg Ungerer <gerg@linux-m68k.org> wrote:
>
> The implementation of the readX() and writeX() family of IO access
> functions is non-standard on ColdFire platforms. They check the supplied
> IO address and will return either big or little endian results based on
> that check. This is non-standard, they are expected to always return
> little-endian byte ordered data. Unfortunately this behavior also means
> that ioreadX()/iowroteX() and their big-endian counter parts
> ioreadXbe()/iowriteXbe() are wrong. This is now in the process of being
> cleaned up and fixed.
>
> Change the use of the readX() and writeX() access functions in this driver
> to use the recently defined specific ColdFire internal SoC hardware IO
> access functions mcf_read8()/mcf_read16()/mcf_read32() and
> mcf_write8()/mcf_write16()/mcf_write32().
>
> There is no functional change to the driver. Though it does have the
> effect of making the IO access slightly more efficient, since there is
> no longer a need to do the address check at every register access.
>
> Acked-by: Angelo Dureghello <adureghello@baylibre.com>
> Tested-by: Angelo Dureghello <adureghello@baylibre.com>
> Signed-off-by: Greg Ungerer <gerg@linux-m68k.org>
Applied for next, thanks!
Kind regards
Uffe
> ---
> v2: moved from RFC to PATCH
>
> drivers/mmc/host/sdhci-esdhc-mcf.c | 24 ++++++++++++------------
> 1 file changed, 12 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-esdhc-mcf.c b/drivers/mmc/host/sdhci-esdhc-mcf.c
> index 375fce5639d7..6853521e8b2c 100644
> --- a/drivers/mmc/host/sdhci-esdhc-mcf.c
> +++ b/drivers/mmc/host/sdhci-esdhc-mcf.c
> @@ -55,7 +55,7 @@ static inline void esdhc_clrset_be(struct sdhci_host *host,
> if (reg == SDHCI_HOST_CONTROL)
> val |= ESDHC_PROCTL_D3CD;
>
> - writel((readl(base) & ~mask) | val, base);
> + mcf_write32((mcf_read32(base) & ~mask) | val, base);
> }
>
> /*
> @@ -71,7 +71,7 @@ static void esdhc_mcf_writeb_be(struct sdhci_host *host, u8 val, int reg)
> if (reg == SDHCI_HOST_CONTROL) {
> u32 host_ctrl = ESDHC_DEFAULT_HOST_CONTROL;
> u8 dma_bits = (val & SDHCI_CTRL_DMA_MASK) >> 3;
> - u8 tmp = readb(host->ioaddr + SDHCI_HOST_CONTROL + 1);
> + u8 tmp = mcf_read8(host->ioaddr + SDHCI_HOST_CONTROL + 1);
>
> tmp &= ~0x03;
> tmp |= dma_bits;
> @@ -82,12 +82,12 @@ static void esdhc_mcf_writeb_be(struct sdhci_host *host, u8 val, int reg)
> */
> host_ctrl |= val;
> host_ctrl |= (dma_bits << 8);
> - writel(host_ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
> + mcf_write32(host_ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
>
> return;
> }
>
> - writel((readl(base) & mask) | (val << shift), base);
> + mcf_write32((mcf_read32(base) & mask) | (val << shift), base);
> }
>
> static void esdhc_mcf_writew_be(struct sdhci_host *host, u16 val, int reg)
> @@ -110,24 +110,24 @@ static void esdhc_mcf_writew_be(struct sdhci_host *host, u16 val, int reg)
> * As for the fsl driver,
> * we have to set the mode in a single write here.
> */
> - writel(val << 16 | mcf_data->aside,
> + mcf_write32(val << 16 | mcf_data->aside,
> host->ioaddr + SDHCI_TRANSFER_MODE);
> return;
> }
>
> - writel((readl(base) & mask) | (val << shift), base);
> + mcf_write32((mcf_read32(base) & mask) | (val << shift), base);
> }
>
> static void esdhc_mcf_writel_be(struct sdhci_host *host, u32 val, int reg)
> {
> - writel(val, host->ioaddr + reg);
> + mcf_write32(val, host->ioaddr + reg);
> }
>
> static u8 esdhc_mcf_readb_be(struct sdhci_host *host, int reg)
> {
> if (reg == SDHCI_HOST_CONTROL) {
> u8 __iomem *base = host->ioaddr + (reg & ~3);
> - u16 val = readw(base + 2);
> + u16 val = mcf_read16(base + 2);
> u8 dma_bits = (val >> 5) & SDHCI_CTRL_DMA_MASK;
> u8 host_ctrl = val & 0xff;
>
> @@ -137,7 +137,7 @@ static u8 esdhc_mcf_readb_be(struct sdhci_host *host, int reg)
> return host_ctrl;
> }
>
> - return readb(host->ioaddr + (reg ^ 0x3));
> + return mcf_read8(host->ioaddr + (reg ^ 0x3));
> }
>
> static u16 esdhc_mcf_readw_be(struct sdhci_host *host, int reg)
> @@ -149,14 +149,14 @@ static u16 esdhc_mcf_readw_be(struct sdhci_host *host, int reg)
> if (reg == SDHCI_HOST_VERSION)
> reg -= 2;
>
> - return readw(host->ioaddr + (reg ^ 0x2));
> + return mcf_read16(host->ioaddr + (reg ^ 0x2));
> }
>
> static u32 esdhc_mcf_readl_be(struct sdhci_host *host, int reg)
> {
> u32 val;
>
> - val = readl(host->ioaddr + reg);
> + val = mcf_read32(host->ioaddr + reg);
>
> /*
> * RM (25.3.9) sd pin clock must never exceed 25Mhz.
> @@ -245,7 +245,7 @@ static void esdhc_mcf_pltfm_set_clock(struct sdhci_host *host,
> * fvco = fsys * outdvi1 + 1
> * fshdc = fvco / outdiv3 + 1
> */
> - temp = readl(pll_dr);
> + temp = mcf_read32(pll_dr);
> fsys = pltfm_host->clock;
> fvco = fsys * ((temp & 0x1f) + 1);
> fesdhc = fvco / (((temp >> 10) & 0x1f) + 1);
> --
> 2.54.0
>
^ permalink raw reply
* [PATCH net v4] net: airoha: fix MIB stats collection to be lossless
From: Aniket Negi @ 2026-07-06 15:47 UTC (permalink / raw)
To: netdev, lorenzo
Cc: matthias.bgg, angelogioacchino.delregno, aniket.negi,
kuldeep.malik, Aniket Negi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Christian Marangi, linux-arm-kernel, linux-mediatek, linux-kernel
REG_FE_GDM_MIB_CLEAR after every read creates a race window where
packets arriving between read and clear are lost from statistics.
Switch to a delta-based approach instead:
- 64-bit H+L registers (ok pkts/bytes, E64..L1023): read absolute
hardware total directly; use a local variable and max(new, old)
clamping to prevent intermediate visibility and torn-read regression.
- 32-bit registers (drops, bc, mc, errors, runt, long): accumulate
(u32)(curr - prev) into a 64-bit software counter; unsigned
subtraction handles wrap-around transparently.
- tx/rx_len[0] ([0,64] bucket): combines RUNT_CNT (32-bit, delta via
tx_runt/rx_runt) and E64_CNT (64-bit, absolute) into a single
assignment using a local accumulator to avoid double-counting.
Clear MIB counters once in airoha_fe_init() to establish a clean
baseline, preventing spurious stats from pre-driver activity (kexec,
driver rebind, warm reboot).
Merge airoha_dev_get_hw_stats() into airoha_update_hw_stats() and
move stats_lock inside. Plain spin_lock() is correct: the function
is only called from ndo_get_stats64() in process context. Each dev
refreshes only its own MIB counters; sibling devs on a shared GDM3/4
port are polled when their own netdev is queried.
Fixes: 8f4695fb67b2 ("net: airoha: better handle MIBs for GDM ports with multiple devs attached")
Signed-off-by: Aniket Negi <aniket.negi03@gmail.com>
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
Changes in v4:
- Add max(new, old) clamping for 64-bit H+L register pairs to ensure
monotonically non-decreasing stats despite torn reads between H and L
- Use local variable (tmp) for all 64-bit H+L computations to prevent
lockless readers from seeing intermediate values during piecewise write
- Add one-shot MIB counter clear in airoha_fe_init() to establish a
clean baseline (kexec, driver rebind, warm reboot)
- Document sibling dev polling design in commit message
Changes in v3:
- Link to V2: https://lore.kernel.org/20260701173941.314795-1-aniket.negi03@gmail.com/
- Add Acked-by tag from Lorenzo
- Rename from tx_runt_cnt to tx_runt, tx_long_cnt to tx_long,
tx_runt_accum64 to tx_runt64
- Rename from rx_runt_cnt to rx_runt, rx_long_cnt to rx_long,
rx_runt_accum64 to rx_runt64
- Condense the marked comments in V2, remove new line after comment
Changes in v2:
- Store _CNT_L register reads in val before adding to stats, improving
readability (suggested by Lorenzo Bianconi)
- Fix double-counting bug in the RUNT+E64 combined bucket: previously
"+=" for E64 re-added the full absolute counter each poll; now a
dedicated tx_runt_accum64/rx_runt_accum64 accumulator holds the
running RUNT delta, and tx_len[0] is assigned (not accumulated) each
poll as runt_accum64 + E64_abs
- Replace 7-element tx_len[]/rx_len[] shadow arrays in mib_prev with
focused tx_runt_cnt/tx_long_cnt and rx_runt_cnt/rx_long_cnt fields;
only RUNT and LONG are 32-bit and need wrap-around tracking
- Rename inner struct hw_prev_stats to mib_prev; rename accumulator
fields to tx_runt_accum64/rx_runt_accum64 for clarity
- Fix comment alignment in mib_prev struct block
- Rename airoha_dev_get_hw_stats() to airoha_update_hw_stats() and
move the port spin_lock inside, removing the separate wrapper
drivers/net/ethernet/airoha/airoha_eth.c | 183 +++++++++++++++--------
drivers/net/ethernet/airoha/airoha_eth.h | 27 ++++
2 files changed, 144 insertions(+), 66 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 59001fd4b6f7..2d032cc6dfca 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -493,6 +493,8 @@ static void airoha_fe_crsn_qsel_init(struct airoha_eth *eth)
static int airoha_fe_init(struct airoha_eth *eth)
{
+ int i;
+
airoha_fe_maccr_init(eth);
/* PSE IQ reserve */
@@ -586,6 +588,14 @@ static int airoha_fe_init(struct airoha_eth *eth)
/* enable 1:N vlan action, init vlan table */
airoha_fe_set(eth, REG_MC_VLAN_EN, MC_VLAN_EN_MASK);
+ /* Clear MIB counters to establish clean baseline for delta tracking.
+ * This prevents spurious statistics from pre-driver activity (e.g.,
+ * kexec, driver rebind, warm reboot) on first poll.
+ */
+ for (i = AIROHA_GDM1_IDX; i <= AIROHA_GDM4_IDX; i++)
+ airoha_fe_set(eth, REG_FE_GDM_MIB_CLEAR(i),
+ FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK);
+
return airoha_fe_mc_vlan_clear(eth);
}
@@ -1686,11 +1696,14 @@ static void airoha_qdma_stop_napi(struct airoha_qdma *qdma)
}
}
-static void airoha_dev_get_hw_stats(struct airoha_gdm_dev *dev)
+static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
{
struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = dev->eth;
u32 val, i = 0;
+ u64 tmp, prev;
+
+ spin_lock(&port->stats_lock);
/* Read relevant MIB for GDM with multiple port attached */
if (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)
@@ -1701,152 +1714,190 @@ static void airoha_dev_get_hw_stats(struct airoha_gdm_dev *dev)
u64_stats_update_begin(&dev->stats.syncp);
- /* TX */
+ /* TX - 64-bit H+L registers: hw accumulates the total, read directly.
+ * Use local variable to prevent readers from seeing intermediate values.
+ * Clamp to prevent regression from torn reads between H and L.
+ */
+ prev = dev->stats.tx_ok_pkts;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id));
- dev->stats.tx_ok_pkts += ((u64)val << 32);
+ tmp = ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id));
- dev->stats.tx_ok_pkts += val;
+ tmp |= val;
+ dev->stats.tx_ok_pkts = max(tmp, prev);
+ prev = dev->stats.tx_ok_bytes;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id));
- dev->stats.tx_ok_bytes += ((u64)val << 32);
+ tmp = ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id));
- dev->stats.tx_ok_bytes += val;
+ tmp |= val;
+ dev->stats.tx_ok_bytes = max(tmp, prev);
+ /* TX - 32-bit registers: accumulate delta to handle wrap-around. */
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id));
- dev->stats.tx_drops += val;
+ dev->stats.tx_drops += (u32)(val - dev->stats.mib_prev.tx_drops);
+ dev->stats.mib_prev.tx_drops = val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id));
- dev->stats.tx_broadcast += val;
+ dev->stats.tx_broadcast += (u32)(val - dev->stats.mib_prev.tx_broadcast);
+ dev->stats.mib_prev.tx_broadcast = val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id));
- dev->stats.tx_multicast += val;
+ dev->stats.tx_multicast += (u32)(val - dev->stats.mib_prev.tx_multicast);
+ dev->stats.mib_prev.tx_multicast = val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id));
- dev->stats.tx_len[i] += val;
+ dev->stats.mib_prev.tx_runt64 +=
+ (u32)(val - dev->stats.mib_prev.tx_runt);
+ dev->stats.mib_prev.tx_runt = val;
+ /* tx_len[0]: RUNT (32-bit, delta) + E64 (64-bit, absolute). */
+ tmp = dev->stats.mib_prev.tx_runt64;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id));
- dev->stats.tx_len[i] += ((u64)val << 32);
+ tmp += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id));
- dev->stats.tx_len[i++] += val;
+ tmp += val;
+ dev->stats.tx_len[i++] = tmp;
+ prev = dev->stats.tx_len[i];
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id));
- dev->stats.tx_len[i] += ((u64)val << 32);
+ tmp = ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id));
- dev->stats.tx_len[i++] += val;
+ tmp |= val;
+ dev->stats.tx_len[i++] = max(tmp, prev);
+ prev = dev->stats.tx_len[i];
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id));
- dev->stats.tx_len[i] += ((u64)val << 32);
+ tmp = ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id));
- dev->stats.tx_len[i++] += val;
+ tmp |= val;
+ dev->stats.tx_len[i++] = max(tmp, prev);
+ prev = dev->stats.tx_len[i];
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id));
- dev->stats.tx_len[i] += ((u64)val << 32);
+ tmp = ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id));
- dev->stats.tx_len[i++] += val;
+ tmp |= val;
+ dev->stats.tx_len[i++] = max(tmp, prev);
+ prev = dev->stats.tx_len[i];
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id));
- dev->stats.tx_len[i] += ((u64)val << 32);
+ tmp = ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id));
- dev->stats.tx_len[i++] += val;
+ tmp |= val;
+ dev->stats.tx_len[i++] = max(tmp, prev);
+ prev = dev->stats.tx_len[i];
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id));
- dev->stats.tx_len[i] += ((u64)val << 32);
+ tmp = ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id));
- dev->stats.tx_len[i++] += val;
+ tmp |= val;
+ dev->stats.tx_len[i++] = max(tmp, prev);
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id));
- dev->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += (u32)(val - dev->stats.mib_prev.tx_long);
+ dev->stats.mib_prev.tx_long = val;
/* RX */
+ prev = dev->stats.rx_ok_pkts;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id));
- dev->stats.rx_ok_pkts += ((u64)val << 32);
+ tmp = ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id));
- dev->stats.rx_ok_pkts += val;
+ tmp |= val;
+ dev->stats.rx_ok_pkts = max(tmp, prev);
+ prev = dev->stats.rx_ok_bytes;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id));
- dev->stats.rx_ok_bytes += ((u64)val << 32);
+ tmp = ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id));
- dev->stats.rx_ok_bytes += val;
+ tmp |= val;
+ dev->stats.rx_ok_bytes = max(tmp, prev);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id));
- dev->stats.rx_drops += val;
+ dev->stats.rx_drops += (u32)(val - dev->stats.mib_prev.rx_drops);
+ dev->stats.mib_prev.rx_drops = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id));
- dev->stats.rx_broadcast += val;
+ dev->stats.rx_broadcast += (u32)(val - dev->stats.mib_prev.rx_broadcast);
+ dev->stats.mib_prev.rx_broadcast = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id));
- dev->stats.rx_multicast += val;
+ dev->stats.rx_multicast += (u32)(val - dev->stats.mib_prev.rx_multicast);
+ dev->stats.mib_prev.rx_multicast = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id));
- dev->stats.rx_errors += val;
+ dev->stats.rx_errors += (u32)(val - dev->stats.mib_prev.rx_errors);
+ dev->stats.mib_prev.rx_errors = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id));
- dev->stats.rx_crc_error += val;
+ dev->stats.rx_crc_error += (u32)(val - dev->stats.mib_prev.rx_crc_error);
+ dev->stats.mib_prev.rx_crc_error = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id));
- dev->stats.rx_over_errors += val;
+ dev->stats.rx_over_errors += (u32)(val - dev->stats.mib_prev.rx_over_errors);
+ dev->stats.mib_prev.rx_over_errors = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id));
- dev->stats.rx_fragment += val;
+ dev->stats.rx_fragment += (u32)(val - dev->stats.mib_prev.rx_fragment);
+ dev->stats.mib_prev.rx_fragment = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id));
- dev->stats.rx_jabber += val;
+ dev->stats.rx_jabber += (u32)(val - dev->stats.mib_prev.rx_jabber);
+ dev->stats.mib_prev.rx_jabber = val;
i = 0;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id));
- dev->stats.rx_len[i] += val;
+ dev->stats.mib_prev.rx_runt64 +=
+ (u32)(val - dev->stats.mib_prev.rx_runt);
+ dev->stats.mib_prev.rx_runt = val;
+ /* rx_len[0]: RUNT (32-bit, delta) + E64 (64-bit, absolute). */
+ tmp = dev->stats.mib_prev.rx_runt64;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id));
- dev->stats.rx_len[i] += ((u64)val << 32);
+ tmp += ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id));
- dev->stats.rx_len[i++] += val;
+ tmp += val;
+ dev->stats.rx_len[i++] = tmp;
+ prev = dev->stats.rx_len[i];
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id));
- dev->stats.rx_len[i] += ((u64)val << 32);
+ tmp = ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id));
- dev->stats.rx_len[i++] += val;
+ tmp |= val;
+ dev->stats.rx_len[i++] = max(tmp, prev);
+ prev = dev->stats.rx_len[i];
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id));
- dev->stats.rx_len[i] += ((u64)val << 32);
+ tmp = ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id));
- dev->stats.rx_len[i++] += val;
+ tmp |= val;
+ dev->stats.rx_len[i++] = max(tmp, prev);
+ prev = dev->stats.rx_len[i];
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id));
- dev->stats.rx_len[i] += ((u64)val << 32);
+ tmp = ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id));
- dev->stats.rx_len[i++] += val;
+ tmp |= val;
+ dev->stats.rx_len[i++] = max(tmp, prev);
+ prev = dev->stats.rx_len[i];
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id));
- dev->stats.rx_len[i] += ((u64)val << 32);
+ tmp = ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id));
- dev->stats.rx_len[i++] += val;
+ tmp |= val;
+ dev->stats.rx_len[i++] = max(tmp, prev);
+ prev = dev->stats.rx_len[i];
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id));
- dev->stats.rx_len[i] += ((u64)val << 32);
+ tmp = ((u64)val << 32);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id));
- dev->stats.rx_len[i++] += val;
+ tmp |= val;
+ dev->stats.rx_len[i++] = max(tmp, prev);
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id));
- dev->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i] += (u32)(val - dev->stats.mib_prev.rx_long);
+ dev->stats.mib_prev.rx_long = val;
u64_stats_update_end(&dev->stats.syncp);
-}
-
-static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
-{
- struct airoha_gdm_port *port = dev->port;
- int i;
-
- spin_lock(&port->stats_lock);
-
- for (i = 0; i < ARRAY_SIZE(port->devs); i++) {
- if (port->devs[i])
- airoha_dev_get_hw_stats(port->devs[i]);
- }
-
- /* Reset MIB counters */
- airoha_fe_set(dev->eth, REG_FE_GDM_MIB_CLEAR(port->id),
- FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK);
spin_unlock(&port->stats_lock);
}
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index f6d01a8e8da1..fe934f9ffe8a 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -245,6 +245,33 @@ struct airoha_hw_stats {
u64 rx_fragment;
u64 rx_jabber;
u64 rx_len[7];
+
+ struct {
+ /* Previous HW register values for 32-bit counter delta
+ * tracking. Storing the last seen value and accumulating
+ * (u32)(curr - prev) into the 64-bit software counter
+ * handles wrap-around transparently via unsigned arithmetic.
+ * tx_runt64/rx_runt64 hold the running sum of runt deltas.
+ * These fields are never reported to userspace.
+ */
+ u32 tx_drops;
+ u32 tx_broadcast;
+ u32 tx_multicast;
+ u32 tx_runt;
+ u32 tx_long;
+ u64 tx_runt64;
+ u32 rx_drops;
+ u32 rx_broadcast;
+ u32 rx_multicast;
+ u32 rx_errors;
+ u32 rx_crc_error;
+ u32 rx_over_errors;
+ u32 rx_fragment;
+ u32 rx_jabber;
+ u32 rx_runt;
+ u32 rx_long;
+ u64 rx_runt64;
+ } mib_prev;
};
enum {
base-commit: a225f8c20712713406ae47024b8df42deacddd4a
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v3 02/20] driver core: platform: provide platform_device_set_of_node()
From: Andy Shevchenko @ 2026-07-06 15:49 UTC (permalink / raw)
To: Manuel Ebner, Mark Brown
Cc: Bartosz Golaszewski, Lee Jones, Thierry Reding,
Sebastian Hesselbarth, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Srinivas Kandagatla,
Greg Kroah-Hartman, Vinod Koul, Rafael J. Wysocki,
Danilo Krummrich, Rob Herring, Saravana Kannan,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel,
Will Deacon, Robin Murphy, Doug Berger, Florian Fainelli,
Broadcom internal kernel review list, Ulf Hansson, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Matthew Brost, Thomas Hellström, Rodrigo Vivi, David Airlie,
Simona Vetter, Peter Chen, Paul Cercueil, Bin Liu, Philipp Zabel,
Maximilian Luz, Hans de Goede, Ilpo Järvinen,
Krzysztof Kozlowski, Benjamin Herrenschmidt, brgl, linux-kernel,
netdev, linux-arm-msm, linux-sound, driver-core, devicetree,
linuxppc-dev, linux-i2c, iommu, linux-pm, imx, linux-arm-kernel,
intel-xe, dri-devel, linux-usb, linux-mips, platform-driver-x86,
mfd
In-Reply-To: <fbc50f89e0c3bb148656a3b8d96974f591576dec.camel@mailbox.org>
On Mon, Jul 06, 2026 at 04:39:00PM +0200, Manuel Ebner wrote:
> On Mon, 2026-07-06 at 14:44 +0200, Bartosz Golaszewski wrote:
> I removed Mark Brown <broonie@opensource.wolfsonmicro.com> from recipients because:
> “RCPT TO <broonie@opensource.wolfsonmicro.com> failed:
> <broonie@opensource.wolfsonmicro.com>: Recipient address rejected: Domain not found”.
Perhaps he needs to send a patch to update .mailmap?
Cc'ed to Mark.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH net-next 2/2] net: phy: qt2025: Use vertical import style
From: Andrew Lunn @ 2026-07-06 15:51 UTC (permalink / raw)
To: Gary Guo
Cc: Miguel Ojeda, Guru Das Srinagesh, FUJITA Tomonori, Trevor Gross,
Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev, rust-for-linux, linux-kernel
In-Reply-To: <DJRKZD8LBXXH.MCISM5ZLNVMP@garyguo.net>
> This is one of the quirks of rustfmt. Before style edition 2024, if an
> identifier is all caps, it sorts it after non-all-caps identifiers. The
> motivation was to ensure constants are ordered after types, however for things
> like C45 it is actually a type, causing this weirdness.
How often does the style edition change? Is there a 2026? How much
changes in each one?
At least in netdev, we tend to reject checkpatch.pl patches, which are
mostly about style, unless they are part of a patchset adding new
features. Style patches are mostly a waste of reviewer/maintainer
time, because some actually break stuff, so need to be reviewed just
as much as code adding new features. But style changes have a low ROI.
So while there is currently not much rust code in the kernel, making
style changes is not wasting too much reviewer/maintainer time, but as
the amount of rust code goes up, you might see some subsystems
rejecting such changes, or wanting to pin to a specific style edition,
to reduce churn.
Andrew
^ permalink raw reply
* Re: [PATCH net v4] net: airoha: fix MIB stats collection to be lossless
From: Aniket Negi @ 2026-07-06 16:05 UTC (permalink / raw)
To: netdev, lorenzo; +Cc: aniket.negi
In-Reply-To: <20260706154730.36949-1-aniket.negi03@gmail.com>
Hi Lorenzo,
I noticed I forgot to include the link to V3 in the v4 changelog.
V3 can be found at:
https://lore.kernel.org/20260702171814.11165-1-aniket.negi03@gmail.com/
Sorry for the oversight.
Regards,
Aniket
^ permalink raw reply
* Re: [PATCH net-next 2/2] net: phy: qt2025: Use vertical import style
From: Miguel Ojeda @ 2026-07-06 16:08 UTC (permalink / raw)
To: Andrew Lunn
Cc: Gary Guo, Miguel Ojeda, Guru Das Srinagesh, FUJITA Tomonori,
Trevor Gross, Heiner Kallweit, Russell King, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, netdev, rust-for-linux,
linux-kernel
In-Reply-To: <c1d24c8b-1c1d-4784-88a6-3cbb519d0406@lunn.ch>
On Mon, Jul 6, 2026 at 5:51 PM Andrew Lunn <andrew@lunn.ch> wrote:
>
> How often does the style edition change? Is there a 2026? How much
> changes in each one?
Every 3 years or so. For 2024, not much changes -- you can easily try
yourself passing the `--style-edition` flag. From a quick look, it is
almost all just the import sorting:
https://doc.rust-lang.org/style-guide/editions.html
> features. Style patches are mostly a waste of reviewer/maintainer
> time, because some actually break stuff, so need to be reviewed just
Yes, we don't particularly like those either, but this is not like
those: it is not a manual process, and very, very unlikely to
functionally break anything (and trivially reviewed, from my
inspection above).
Just to clarify further: if we decided to do this, which is not a
given, this would be a treewide change done at once (because
formatting is enforced treewide), and nothing you would need to
review.
Cheers,
Miguel
^ permalink raw reply
* Re: [Intel-wired-lan] [PATCH iwl-next v2] ixgbe: E610: force phy link to get down when interface is down
From: kernel test robot @ 2026-07-06 16:09 UTC (permalink / raw)
To: Jedrzej Jagielski, intel-wired-lan
Cc: oe-kbuild-all, anthony.l.nguyen, netdev, Jedrzej Jagielski,
Aleksandr Loktionov
In-Reply-To: <20260706094330.186341-1-jedrzej.jagielski@intel.com>
Hi Jedrzej,
kernel test robot noticed the following build errors:
[auto build test ERROR on tnguy-next-queue/dev-queue]
url: https://github.com/intel-lab-lkp/linux/commits/Jedrzej-Jagielski/ixgbe-E610-force-phy-link-to-get-down-when-interface-is-down/20260706-180351
base: https://git.kernel.org/pub/scm/linux/kernel/git/tnguy/next-queue.git dev-queue
patch link: https://lore.kernel.org/r/20260706094330.186341-1-jedrzej.jagielski%40intel.com
patch subject: [Intel-wired-lan] [PATCH iwl-next v2] ixgbe: E610: force phy link to get down when interface is down
config: alpha-allyesconfig (https://download.01.org/0day-ci/archive/20260707/202607070035.qAAqVjGH-lkp@intel.com/config)
compiler: alpha-linux-gcc (GCC) 16.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260707/202607070035.qAAqVjGH-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202607070035.qAAqVjGH-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c: In function 'ixgbe_close':
>> drivers/net/ethernet/intel/ixgbe/ixgbe_main.c:7572:25: error: implicit declaration of function 'e_error'; did you mean 'e_err'? [-Wimplicit-function-declaration]
7572 | e_error(drv, "Cannot set PHY link down\n");
| ^~~~~~~
| e_err
>> drivers/net/ethernet/intel/ixgbe/ixgbe_main.c:7572:33: error: 'drv' undeclared (first use in this function)
7572 | e_error(drv, "Cannot set PHY link down\n");
| ^~~
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c:7572:33: note: each undeclared identifier is reported only once for each function it appears in
vim +7572 drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
7544
7545 /**
7546 * ixgbe_close - Disables a network interface
7547 * @netdev: network interface device structure
7548 *
7549 * Returns 0, this is not allowed to fail
7550 *
7551 * The close entry point is called when an interface is de-activated
7552 * by the OS. The hardware is still under the drivers control, but
7553 * needs to be disabled. A global MAC reset is issued to stop the
7554 * hardware, and all transmit and receive resources are freed.
7555 **/
7556 int ixgbe_close(struct net_device *netdev)
7557 {
7558 struct ixgbe_adapter *adapter = ixgbe_from_netdev(netdev);
7559
7560 ixgbe_ptp_stop(adapter);
7561
7562 if (netif_device_present(netdev))
7563 ixgbe_close_suspend(adapter);
7564
7565 ixgbe_fdir_filter_exit(adapter);
7566
7567 if (adapter->flags2 & IXGBE_FLAG2_LINK_DOWN_ON_CLOSE) {
7568 int err;
7569
7570 err = ixgbe_disable_phy_link(&adapter->hw);
7571 if (err)
> 7572 e_error(drv, "Cannot set PHY link down\n");
7573
7574 ixgbe_handle_link_down(adapter);
7575 }
7576
7577 ixgbe_release_hw_control(adapter);
7578
7579 return 0;
7580 }
7581
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH net v4 2/3] octeon_ep_vf: Fix RX page leak on napi_build_skb() failure
From: Maciej Fijalkowski @ 2026-07-06 16:10 UTC (permalink / raw)
To: Maoyi Xie
Cc: Veerasenareddy Burru, Sathesh Edara, Satananda Burla,
Shinas Rasheed, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Guangshuo Li, David Carlier, netdev,
linux-kernel
In-Reply-To: <20260706150208.2944898-3-maoyixie.tju@gmail.com>
On Mon, Jul 06, 2026 at 11:02:07PM +0800, Maoyi Xie wrote:
> From: Guangshuo Li <lgs201920130244@gmail.com>
>
> __octep_vf_oq_process_rx() clears buff_info->page before building an skb
> from the RX page. On the success path the page is consumed by the skb,
> either as the skb head or as an RX fragment.
>
> If napi_build_skb() fails, however, the page is not consumed by an skb.
> The error path advances the descriptor and leaves the ring slot cleared,
> so the page is no longer tracked and is leaked. In the multi-fragment
> case, the remaining fragment pages are also unmapped and removed from
> their ring slots without being released.
>
> Release the head page when napi_build_skb() fails, and release each
> remaining fragment page before clearing its ring slot.
>
> Fixes: dd66b4285470 ("octeon_ep_vf: add NULL check for napi_build_skb()")
> Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
Reviewed-by: Maciej Fijalkowski <maciej.fijalkowski@intel.com>
> ---
> drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
> index d982474082..302559b16b 100644
> --- a/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
> +++ b/drivers/net/ethernet/marvell/octeon_ep_vf/octep_vf_rx.c
> @@ -418,6 +418,7 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
> skb = napi_build_skb((void *)resp_hw, PAGE_SIZE);
> if (!skb) {
> oq->stats->alloc_failures++;
> + put_page(virt_to_page(resp_hw));
> desc_used++;
> read_idx = octep_vf_oq_next_idx(oq, read_idx);
> continue;
> @@ -434,6 +435,7 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
> skb = napi_build_skb((void *)resp_hw, PAGE_SIZE);
> if (!skb) {
> oq->stats->alloc_failures++;
> + put_page(virt_to_page(resp_hw));
> desc_used++;
> read_idx = octep_vf_oq_next_idx(oq, read_idx);
> data_len = buff_info->len - oq->max_single_buffer_size;
> @@ -442,6 +444,7 @@ static int __octep_vf_oq_process_rx(struct octep_vf_device *oct,
> PAGE_SIZE, DMA_FROM_DEVICE);
> buff_info = (struct octep_vf_rx_buffer *)
> &oq->buff_info[read_idx];
> + put_page(buff_info->page);
> buff_info->page = NULL;
> if (data_len < oq->buffer_size)
> data_len = 0;
> --
> 2.34.1
>
^ permalink raw reply
* Re: [PATCH net-next 2/2] net: phy: qt2025: Use vertical import style
From: Gary Guo @ 2026-07-06 16:15 UTC (permalink / raw)
To: Andrew Lunn, Gary Guo
Cc: Miguel Ojeda, Guru Das Srinagesh, FUJITA Tomonori, Trevor Gross,
Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev, rust-for-linux, linux-kernel
In-Reply-To: <c1d24c8b-1c1d-4784-88a6-3cbb519d0406@lunn.ch>
On Mon Jul 6, 2026 at 4:51 PM BST, Andrew Lunn wrote:
>> This is one of the quirks of rustfmt. Before style edition 2024, if an
>> identifier is all caps, it sorts it after non-all-caps identifiers. The
>> motivation was to ensure constants are ordered after types, however for things
>> like C45 it is actually a type, causing this weirdness.
>
> How often does the style edition change? Is there a 2026? How much
> changes in each one?
So far it has only changed once, with the only major change being the identifier
sorting that we discussed.
Rustfmt has quite strict backward compatibility, so misformatted code stays
misformatted. They use editions as boundaries to do bugfixes/improvements that
can re-format previously badly formatted code. There'll be new editions coming
but I don't expect it to be as major as 2026.
> At least in netdev, we tend to reject checkpatch.pl patches, which are
> mostly about style, unless they are part of a patchset adding new
> features. Style patches are mostly a waste of reviewer/maintainer
> time, because some actually break stuff, so need to be reviewed just
> as much as code adding new features. But style changes have a low ROI.
>
> So while there is currently not much rust code in the kernel, making
> style changes is not wasting too much reviewer/maintainer time, but as
> the amount of rust code goes up, you might see some subsystems
> rejecting such changes, or wanting to pin to a specific style edition,
> to reduce churn.
We can control what edition of rustfmt we use (even per subsystem level). So
there's no obligation to bump edition unless we want to do it (e.g. it formats
certain code better).
That said, like Miguel said, this can be pretty trivially reformatted treewide.
Best,
Gary
^ permalink raw reply
* [PATCH] net: stmmac: Avoid freeing and re-requesting IRQ during XDP set prog
From: Daniel Thompson @ 2026-07-06 16:17 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Maxime Coquelin, Alexandre Torgue,
Alexei Starovoitov, Daniel Borkmann, Jesper Dangaard Brouer,
John Fastabend, Stanislav Fomichev
Cc: Alex Elder, netdev, linux-stm32, linux-arm-kernel, linux-kernel,
bpf, Daniel Thompson
Currently stmmac will run a full cycle of IRQ tear down and setup when
setting up a new XDP program. This makes tuning TSN systems difficult
because whenever a new XDP program is installed then the irq threads will
be stopped and restarted which will undo any thread tuning.
The problem is avoided by removing stmmac_free_irq()/stmmac_request_irq()
from stmmac_xdp_release()/stmmac_xdp_open().
stmmac_free_irq() implicitly synchronizes interrupts and, with that
removed, I was unable to prove that later actions in
stmmac_xdp_release() are safe when there are concurrent interrupts. To
avoid problems let's also move the code to disable DMA interrupts earlier
in the sequence and explicitly sync the interrupts handler(s).
Signed-off-by: Daniel Thompson <daniel@riscstar.com>
---
This patch was developed (and tested) as part of the TC956x work that
Alex Elder and I have been working on. However the change should work with
any driver that uses the stmmac core so it makes sense to submit it as
a separate patch.
---
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 61 ++++++++++++++++++-----
1 file changed, 48 insertions(+), 13 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 2a0d7eff88d3ff1ffa57e224c25f9e77bc28ed10..acd145f1f3069fde6043a9118c793758c5c8f4c0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -2531,6 +2531,23 @@ static void stmmac_enable_all_dma_irq(struct stmmac_priv *priv)
}
}
+static void stmmac_disable_all_dma_irq(struct stmmac_priv *priv)
+{
+ u8 rx_channels_count = priv->plat->rx_queues_to_use;
+ u8 tx_channels_count = priv->plat->tx_queues_to_use;
+ u8 dma_csr_ch = max(rx_channels_count, tx_channels_count);
+ u8 chan;
+
+ for (chan = 0; chan < dma_csr_ch; chan++) {
+ struct stmmac_channel *ch = &priv->channel[chan];
+ unsigned long flags;
+
+ spin_lock_irqsave(&ch->lock, flags);
+ stmmac_disable_dma_irq(priv, priv->ioaddr, chan, 1, 1);
+ spin_unlock_irqrestore(&ch->lock, flags);
+ }
+}
+
/**
* stmmac_start_all_dma - start all RX and TX DMA channels
* @priv: driver private structure
@@ -3814,6 +3831,33 @@ static void stmmac_free_irq(struct net_device *dev,
}
}
+static void stmmac_synchronize_irq(struct net_device *dev)
+{
+ struct stmmac_priv *priv = netdev_priv(dev);
+ struct stmmac_msi *msi = priv->msi;
+ int j;
+
+ for (j = priv->plat->tx_queues_to_use - 1; msi && j >= 0; j--) {
+ if (msi->tx_irq[j] > 0)
+ synchronize_irq(msi->tx_irq[j]);
+ }
+
+ for (j = priv->plat->rx_queues_to_use - 1; msi && j >= 0; j--) {
+ if (msi->rx_irq[j] > 0)
+ synchronize_irq(msi->rx_irq[j]);
+ }
+
+ if (msi && msi->sfty_ue_irq > 0 && msi->sfty_ue_irq != dev->irq)
+ synchronize_irq(msi->sfty_ue_irq);
+ if (msi && msi->sfty_ce_irq > 0 && msi->sfty_ce_irq != dev->irq)
+ synchronize_irq(msi->sfty_ce_irq);
+ if (priv->wol_irq > 0 && priv->wol_irq != dev->irq)
+ synchronize_irq(priv->wol_irq);
+ if (priv->sfty_irq > 0 && priv->sfty_irq != dev->irq)
+ synchronize_irq(priv->sfty_irq);
+ synchronize_irq(dev->irq);
+}
+
static int stmmac_msi_init(struct stmmac_priv *priv,
struct stmmac_resources *res)
{
@@ -7108,8 +7152,9 @@ void stmmac_xdp_release(struct net_device *dev)
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
- /* Free the IRQ lines */
- stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0);
+ /* Silence DMA interrupts */
+ stmmac_disable_all_dma_irq(priv);
+ stmmac_synchronize_irq(dev);
/* Stop TX/RX DMA channels */
stmmac_stop_all_dma(priv);
@@ -7156,10 +7201,8 @@ int stmmac_xdp_open(struct net_device *dev)
stmmac_reset_queues_param(priv);
/* DMA CSR Channel configuration */
- for (chan = 0; chan < dma_csr_ch; chan++) {
+ for (chan = 0; chan < dma_csr_ch; chan++)
stmmac_init_chan(priv, priv->ioaddr, priv->plat->dma_cfg, chan);
- stmmac_disable_dma_irq(priv, priv->ioaddr, chan, 1, 1);
- }
/* Adjust Split header */
sph_en = (priv->hw->rx_csum > 0) && priv->sph_active;
@@ -7197,10 +7240,6 @@ int stmmac_xdp_open(struct net_device *dev)
/* Start Rx & Tx DMA Channels */
stmmac_start_all_dma(priv);
- ret = stmmac_request_irq(dev);
- if (ret)
- goto irq_error;
-
/* Enable NAPI process*/
stmmac_enable_all_queues(priv);
netif_carrier_on(dev);
@@ -7209,10 +7248,6 @@ int stmmac_xdp_open(struct net_device *dev)
return 0;
-irq_error:
- for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
- hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
-
init_error:
free_dma_desc_resources(priv, &priv->dma_conf);
dma_desc_error:
---
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
change-id: 20260706-tc956x-stmmac-no_irq_teardown-8a7592b9f8c1
Best regards,
--
Daniel Thompson <daniel@riscstar.com>
^ permalink raw reply related
* Re: [PATCH v2 net-next 03/14] rtnetlink: Add per-netns rtnl_work.
From: Kuniyuki Iwashima @ 2026-07-06 16:23 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, netdev
In-Reply-To: <20260703001009.1572444-4-kuniyu@google.com>
Replying to Sashiko review,
On Thu, Jul 2, 2026 at 5:10 PM Kuniyuki Iwashima <kuniyu@google.com> wrote:
[...]
> diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
> index 7207da002fb5..7959519e7375 100644
> --- a/net/core/rtnetlink.c
> +++ b/net/core/rtnetlink.c
> @@ -273,6 +273,26 @@ bool lockdep_rtnl_net_is_held(struct net *net)
> return lockdep_rtnl_is_held() && lockdep_is_held(&net->rtnl_mutex);
> }
> EXPORT_SYMBOL(lockdep_rtnl_net_is_held);
> +
> +static struct workqueue_struct *rtnl_net_wq;
> +
> +void rtnl_net_queue_work(struct net *net)
> +{
> + queue_work(rtnl_net_wq, &net->rtnl_work);
> +}
---8<---
> + queue_work(rtnl_net_wq, &net->rtnl_work);
Can this queue work on a freed struct net object, leading to a
use-after-free?
If a thread in netns B deletes a cross-netns device (such as veth) whose peer
is in netns A, it can nullify the cross-reference pointers (e.g., in
veth_dellink()) but get preempted just before calling
unregister_netdevice_queue_net() for netns A.
---8<---
No, the concerned veth will get a new netdev_hold() in patch 8
to prevent peer (and its netns) from being freed.
^ permalink raw reply
* [PATCH net] selftests/net: Fix tun IPv6 test addresses to avoid 6to4 range
From: Ricardo B. Marlière @ 2026-07-06 16:24 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Shuah Khan, Xu Du
Cc: netdev, linux-kselftest, linux-kernel, Ricardo B. Marlière
The IPv6 addresses used for the tun_vnet_udptnl fixture currently fall in
the 2002::/16 prefix, which is reserved for the 6to4 transition mechanism
(RFC 3056).
On systems where the sit module is loaded, the kernel automatically claims
2002::/16 as a 6to4 tunnel prefix. When the test assigns a 2002:: address
to a TUN interface, sit registers a competing local route for the same
address. This ambiguity breaks the GENEVE decapsulation path: packets
injected via the TUN fd are not delivered to the test socket, causing the
IPv6-outer gtgso send_gso_packet variants to fail.
Replace all four IPv6 test addresses with addresses from the fd00:db8::/32
range, which is part of the ULA space (fc00::/7, RFC 4193) and carries no
special kernel semantics.
Fixes: 24e59f26eef2 ("selftest: tun: Add helpers for GSO over UDP tunnel")
Signed-off-by: Ricardo B. Marlière <rbm@suse.com>
---
tools/testing/selftests/net/tun.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/net/tun.c b/tools/testing/selftests/net/tun.c
index cf106a49b55e..abe488bac50b 100644
--- a/tools/testing/selftests/net/tun.c
+++ b/tools/testing/selftests/net/tun.c
@@ -42,19 +42,19 @@ static struct in_addr param_ipaddr4_inner_src = {
};
static struct in6_addr param_ipaddr6_outer_dst = {
- { { 0x20, 0x02, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
+ { { 0xfd, 0x00, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
};
static struct in6_addr param_ipaddr6_outer_src = {
- { { 0x20, 0x02, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } },
+ { { 0xfd, 0x00, 0x0d, 0xb8, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } },
};
static struct in6_addr param_ipaddr6_inner_dst = {
- { { 0x20, 0x02, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
+ { { 0xfd, 0x00, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } },
};
static struct in6_addr param_ipaddr6_inner_src = {
- { { 0x20, 0x02, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } },
+ { { 0xfd, 0x00, 0x0d, 0xb8, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 } },
};
#ifndef BIT
---
base-commit: 56abdaebbf0da304b860bed1f2b5a85f5a6a16a0
change-id: 20260706-b4-net_tun_addr-055d454ab810
Best regards,
--
Ricardo B. Marlière <rbm@suse.com>
^ permalink raw reply related
* Re: [PATCH net v4] octeontx2-pf: check DMAC extraction support before filtering
From: Harshitha Ramamurthy @ 2026-07-06 16:28 UTC (permalink / raw)
To: nshettyj
Cc: netdev, linux-kernel, sgoutham, gakula, sbhatta, hkelam,
bbhushan2, andrew+netdev, davem, edumazet, kuba, pabeni, naveenm,
tduszynski, sumang, rkannoth
In-Reply-To: <20260702033451.2969880-1-nshettyj@marvell.com>
On Wed, Jul 1, 2026 at 8:35 PM <nshettyj@marvell.com> wrote:
>
> From: Suman Ghosh <sumang@marvell.com>
>
> Currently, configuring a VF MAC address via the PF (e.g., 'ip link
> set <pf> vf 0 mac <mac>') blindly attempts to install a DMAC-based
> hardware filter. However, the hardware parser profile might not
> support DMAC extraction.
>
> Check if the hardware parsing profile supports DMAC extraction
> before adding the filter. Additionally, emit a warning message
> to inform the operator if the MAC filter installation fails due
> to missing DMAC extraction support. Update config->mac only
> after hardware programming succeeds in otx2_set_vf_mac().
>
> Fixes: f0c2982aaf98 ("octeontx2-pf: Add support for SR-IOV management functions")
> Signed-off-by: Suman Ghosh <sumang@marvell.com>
> Signed-off-by: Nitin Shetty J <nshettyj@marvell.com>
Reviewed-by: Harshitha Ramamurthy <hramamurthy@google.com>
>
> ---
> v4:
> - Return the real mailbox error from the NPC_DMAC support query instead
> of masking it as -EINVAL.
> - Updated commit message.
> v3:
> - Update config->mac only after hardware programming succeeds in
> otx2_set_vf_mac().
> v2:
> - Move the DMAC extraction check from otx2_set_vf_mac() into
> otx2_do_set_vf_mac() which already holds pf->mbox.lock, so all
> mbox operations are under a single lock/unlock pair. All error
> paths now use the existing goto-out pattern, eliminating the
> scattered mutex_unlock() + return calls from v1.
> - Return -EOPNOTSUPP instead of 0 when DMAC extraction is not
> supported, so the caller gets an explicit error rather than a
> silent success.
> ---
> .../ethernet/marvell/octeontx2/nic/otx2_pf.c | 43 ++++++++++++++++---
> 1 file changed, 37 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
> index b63df5737ff2..888ebd6cef39 100644
> --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
> +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
> @@ -2517,10 +2517,42 @@ EXPORT_SYMBOL(otx2_config_hwtstamp_set);
>
> static int otx2_do_set_vf_mac(struct otx2_nic *pf, int vf, const u8 *mac)
> {
> + struct npc_get_field_status_req *freq;
> + struct npc_get_field_status_rsp *frsp;
> struct npc_install_flow_req *req;
> int err;
>
> mutex_lock(&pf->mbox.lock);
> +
> + /* Skip installing the DMAC filter if the hardware parser profile
> + * does not support DMAC extraction.
> + */
> + freq = otx2_mbox_alloc_msg_npc_get_field_status(&pf->mbox);
> + if (!freq) {
> + err = -ENOMEM;
> + goto out;
> + }
> +
> + freq->field = NPC_DMAC;
> + err = otx2_sync_mbox_msg(&pf->mbox);
> + if (err)
> + goto out;
> +
> + frsp = (struct npc_get_field_status_rsp *)otx2_mbox_get_rsp
> + (&pf->mbox.mbox, 0, &freq->hdr);
> + if (IS_ERR(frsp)) {
> + err = PTR_ERR(frsp);
> + goto out;
> + }
> +
> + if (!frsp->enable) {
> + netdev_warn(pf->netdev,
> + "VF %d MAC filter not installed: DMAC extraction not supported by parser profile\n",
> + vf);
> + err = -EOPNOTSUPP;
> + goto out;
> + }
> +
> req = otx2_mbox_alloc_msg_npc_install_flow(&pf->mbox);
> if (!req) {
> err = -ENOMEM;
> @@ -2559,13 +2591,12 @@ static int otx2_set_vf_mac(struct net_device *netdev, int vf, u8 *mac)
> if (!is_valid_ether_addr(mac))
> return -EINVAL;
>
> - config = &pf->vf_configs[vf];
> - ether_addr_copy(config->mac, mac);
> -
> ret = otx2_do_set_vf_mac(pf, vf, mac);
> - if (ret == 0)
> - dev_info(&pdev->dev,
> - "Load/Reload VF driver\n");
> + if (ret == 0) {
> + config = &pf->vf_configs[vf];
> + ether_addr_copy(config->mac, mac);
> + dev_info(&pdev->dev, "Load/Reload VF driver\n");
> + }
>
> return ret;
> }
> --
> 2.48.1
>
^ permalink raw reply
* Re: [PATCH v2 net-next 05/14] net: Hold __rtnl_net_lock() in netdev_wait_allrefs_any().
From: Kuniyuki Iwashima @ 2026-07-06 16:30 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, netdev
In-Reply-To: <20260703001009.1572444-6-kuniyu@google.com>
Replying to Sashiko review,
On Thu, Jul 2, 2026 at 5:10 PM Kuniyuki Iwashima <kuniyu@google.com> wrote:
[...]
> diff --git a/net/core/dev.c b/net/core/dev.c
> index c477c4f84ed9..48818a194fa5 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -11608,8 +11608,13 @@ static struct net_device *netdev_wait_allrefs_any(struct list_head *list)
> rtnl_lock();
>
> /* Rebroadcast unregister notification */
> - list_for_each_entry(dev, list, todo_list)
> + list_for_each_entry(dev, list, todo_list) {
> + struct net *net = dev_net(dev);
> +
> + __rtnl_net_lock(net);
---8<---
This is a pre-existing issue, but can this cause a use-after-free on the net
namespace memory?
---8<---
No, netdev_wait_allrefs_any() must be done for all netdev in
dying netns.
> call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
> + __rtnl_net_unlock(net);
> + }
>
> __rtnl_unlock();
---8<---
Can __rtnl_net_unlock() leave items on net_todo_list and trigger the
WARN_ON in __rtnl_unlock()?
If a concurrent thread queues unregistrations to net->dev_unreg_head,
---8<---
Once RTNL is removed, yes, but before that, we need to revisit here
as mentioned in the commit message.
^ permalink raw reply
* [PATCH net v2] tipc: fix NULL deref in tipc_named_node_up() on empty publication list
From: Weiming Shi @ 2026-07-06 16:30 UTC (permalink / raw)
To: Jon Maloy, netdev, tipc-discussion
Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Hoang Huu Le, Xiang Mei, linux-kernel, Weiming Shi,
Tung Quang Nguyen
named_distribute() builds the bulk messages for @pls into @list and then
dereferences the tail skb:
hdr = buf_msg(skb_peek_tail(list));
msg_set_last_bulk(hdr);
If @pls is empty no skb is enqueued, skb_peek_tail() returns NULL, and
msg_set_last_bulk() writes through buf_msg(NULL).
tipc_named_node_up() passes &nt->cluster_scope. With a node-id
configuration the TIPC_NODE_STATE name is published by tipc_net_finalize()
from a work item, which sets the node address before publishing the name.
The node accepts links once the address is set, so a link that comes up
before the publish runs named_distribute() on an empty cluster_scope:
KASAN: null-ptr-deref in range [0x00000000000000d8-0x00000000000000df]
RIP: 0010:tipc_named_node_up (net/tipc/name_distr.c:196)
tipc_named_node_up (net/tipc/name_distr.c:196 net/tipc/name_distr.c:221)
tipc_node_write_unlock (net/tipc/node.c:428)
tipc_rcv (net/tipc/node.c:2185)
tipc_udp_recv (net/tipc/udp_media.c:392)
Kernel panic - not syncing: Fatal exception in interrupt
TIPC genl ops use GENL_UNS_ADMIN_PERM, so an unprivileged user can reach
this from a user+net namespace.
Return early in tipc_named_node_up() when cluster_scope is empty, so
named_distribute() is not called with an empty list.
Fixes: cad2929dc432 ("tipc: update a binding service via broadcast")
Reported-by: Xiang Mei <xmei5@asu.edu>
Suggested-by: Tung Quang Nguyen <tung.quang.nguyen@est.tech>
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
---
v2:
- Return early in tipc_named_node_up() when cluster_scope is empty,
instead of guarding the tail access inside named_distribute().
net/tipc/name_distr.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c
index ba4f4906e13b..60ccaa862162 100644
--- a/net/tipc/name_distr.c
+++ b/net/tipc/name_distr.c
@@ -218,6 +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);
+ if (list_empty(&nt->cluster_scope)) {
+ read_unlock_bh(&nt->cluster_scope_lock);
+ return;
+ }
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 net] tipc: fix NULL deref in tipc_named_node_up() on empty publication list
From: Weiming Shi @ 2026-07-06 16:31 UTC (permalink / raw)
To: Tung Quang Nguyen
Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Xiang Mei, linux-kernel@vger.kernel.org, Jon Maloy,
netdev@vger.kernel.org, tipc-discussion@lists.sourceforge.net
In-Reply-To: <GV1P189MB198844ED09B014636A8EB7C8C6F12@GV1P189MB1988.EURP189.PROD.OUTLOOK.COM>
Tung Quang Nguyen <tung.quang.nguyen@est.tech> 于2026年7月6日周一 17:21写道:
>
> >Subject: [PATCH net] tipc: fix NULL deref in tipc_named_node_up() on empty
> >publication list
> >
> >named_distribute() builds the bulk messages for @pls into @list and then
> >dereferences the tail skb:
> >
> > hdr = buf_msg(skb_peek_tail(list));
> > msg_set_last_bulk(hdr);
> >
> >If @pls is empty no skb is enqueued, skb_peek_tail() returns NULL, and
> >msg_set_last_bulk() writes through buf_msg(NULL).
> >
> >tipc_named_node_up() passes &nt->cluster_scope. With a node-id
> >configuration the TIPC_NODE_STATE name is published by tipc_net_finalize()
> >from a work item, which sets the node address before publishing the name.
> >The node accepts links once the address is set, so a link that comes up before
> >the publish runs named_distribute() on an empty cluster_scope:
> >
> > KASAN: null-ptr-deref in range [0x00000000000000d8-0x00000000000000df]
> > RIP: 0010:tipc_named_node_up (net/tipc/name_distr.c:196)
> > tipc_named_node_up (net/tipc/name_distr.c:196 net/tipc/name_distr.c:221)
> > tipc_node_write_unlock (net/tipc/node.c:428)
> > tipc_rcv (net/tipc/node.c:2185)
> > tipc_udp_recv (net/tipc/udp_media.c:392) Kernel panic - not syncing: Fatal
> >exception in interrupt
> >
> >TIPC genl ops use GENL_UNS_ADMIN_PERM, so an unprivileged user can reach
> >this from a user+net namespace.
> >
> >Return early from named_distribute() when the list is empty, and skip
> >tipc_node_xmit() for an empty chain. The empty chain would otherwise hit
> >tipc_lxc_xmit() -> buf_msg(skb_peek(list)), the same zero-skb case fixed for
> >tipc_link_xmit() in commit b77413446408 ("tipc: fix NULL deref in
> >tipc_link_xmit()").
>
> It needs to return earlier in tipc_named_node_up() like this:
>
> diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c
> index ba4f4906e13b..60ccaa862162 100644
> --- a/net/tipc/name_distr.c
> +++ b/net/tipc/name_distr.c
> @@ -218,6 +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);
> + if (list_empty(&nt->cluster_scope)) {
> + read_unlock_bh(&nt->cluster_scope_lock);
> + return;
> + }
> named_distribute(net, &head, dnode, &nt->cluster_scope, seqno);
> tipc_node_xmit(net, &head, dnode, 0);
> read_unlock_bh(&nt->cluster_scope_lock);
>
> >
> >Fixes: cad2929dc432 ("tipc: update a binding service via broadcast")
> >Reported-by: Xiang Mei <xmei5@asu.edu>
> >Assisted-by: Claude:claude-opus-4-8
> >Signed-off-by: Weiming Shi <bestswngs@gmail.com>
> >---
> > net/tipc/name_distr.c | 8 ++++++--
> > 1 file changed, 6 insertions(+), 2 deletions(-)
> >
> >diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c index
> >ba4f4906e13b..c04fea4650a5 100644
> >--- a/net/tipc/name_distr.c
> >+++ b/net/tipc/name_distr.c
> >@@ -192,7 +192,10 @@ static void named_distribute(struct net *net, struct
> >sk_buff_head *list,
> > skb_trim(skb, INT_H_SIZE + (msg_dsz - msg_rem));
> > __skb_queue_tail(list, skb);
> > }
> >- hdr = buf_msg(skb_peek_tail(list));
> >+ skb = skb_peek_tail(list);
> >+ if (!skb)
> >+ return;
> >+ hdr = buf_msg(skb);
> > msg_set_last_bulk(hdr);
> > msg_set_named_seqno(hdr, seqno);
> > }
> >@@ -219,7 +222,8 @@ void tipc_named_node_up(struct net *net, u32 dnode,
> >u16 capabilities)
> >
> > read_lock_bh(&nt->cluster_scope_lock);
> > named_distribute(net, &head, dnode, &nt->cluster_scope, seqno);
> >- tipc_node_xmit(net, &head, dnode, 0);
> >+ if (!skb_queue_empty(&head))
> >+ tipc_node_xmit(net, &head, dnode, 0);
> > read_unlock_bh(&nt->cluster_scope_lock);
> > }
> >
> >--
> >2.43.0
> >
>
Thanks for your review. v2 sent.
^ permalink raw reply
* Re: [PATCH net] psp: fix NULL genl_sock deref race with concurrent netns teardown
From: Wei Wang @ 2026-07-06 16:32 UTC (permalink / raw)
To: Daniel Zahka
Cc: Kiran Kella, kuba, willemdebruijn.kernel, davem, edumazet, pabeni,
horms, weibunny, netdev, linux-kernel, jayakrishnan.udayavarma,
ajit.khaparde, akhilesh.samineni, Vikas Gupta, Bhargava Marreddy
In-Reply-To: <c2f1e433-99bf-4712-a31b-6b2360f8fb6e@gmail.com>
On Mon, Jul 6, 2026 at 6:46 AM Daniel Zahka <daniel.zahka@gmail.com> wrote:
>
> >
>
> On 7/3/26 7:24 AM, Kiran Kella wrote:
> > The race occurs between network namespace removal and PSP device
> > unregistration. When a netns is deleted while a PSP device associated
> > with that netns is concurrently being removed, psp_dev_unregister()
> > triggers psp_nl_notify_dev() to send a device change notification.
> > Concurrently, cleanup_net() running in the netns workqueue calls
> > genl_pernet_exit(), which sets net->genl_sock to NULL. If
> > genl_pernet_exit() wins the race, two sites in psp_nl_multicast_per_ns()
> > then dereference the NULL socket and crash:
> >
> > CPU 0 (netns teardown) CPU 1 (PSP device unregister)
> > ====================== =============================
> > cleanup_net [workqueue]
> > genl_pernet_exit() psp_dev_unregister()
> > net->genl_sock = NULL psp_nl_notify_dev()
> > psp_nl_multicast_per_ns()
> > build_ntf()
> > -> netlink_has_listeners(NULL)
> > /* crash */
> > genlmsg_multicast_netns()
> > -> nlmsg_multicast_filtered(NULL)
> > /* crash */
> >
> > Both the main_net path (derived from psd->main_netdev) and each
> > assoc_net entry in psd->assoc_dev_list are affected.
> >
> > Fix by replacing the bare dev_net() calls with maybe_get_net().
> > maybe_get_net() returns NULL if the namespace is already dying.
> > Holding the reference ensures genl_sock remains valid across both the
> > build_ntf() and genlmsg_multicast_netns() calls.
> >
> > Fixes: 00c94ca2b99e ("psp: base PSP device support")
> > Fixes: 06c2dce2d0f6 ("psp: add new netlink cmd for dev-assoc and dev-disassoc")
> > Signed-off-by: Kiran Kella <kiran.kella@broadcom.com>
> > Reviewed-by: Ajit Khaparde <ajit.khaparde@broadcom.com>
> > Reviewed-by: Vikas Gupta <vikas.gupta@broadcom.com>
> > Reviewed-by: Bhargava Marreddy <bhargava.marreddy@broadcom.com>
>
>
> Thanks. I was able to repro myself by inserting a delay after the old:
>
> main_net = dev_net(psd->main_netdev);
>
> and then executing nsim_psp_rereg_write() in parallel with destroying
> the netns that I placed a netdevsim dev into.
>
> Tested-by: Daniel Zahka <daniel.zahka@gmail.com>
>
> Reviewed-by: Daniel Zahka <daniel.zahka@gmail.com>
>
Thanks for the fix!
Reviewed-by: Wei Wang <weibunny@fb.com>
> > ---
> > net/psp/psp_nl.c | 33 +++++++++++++++++++++------------
> > 1 file changed, 21 insertions(+), 12 deletions(-)
> >
> > diff --git a/net/psp/psp_nl.c b/net/psp/psp_nl.c
> > index 9610d8c456ff..24ab626a9e8a 100644
> > --- a/net/psp/psp_nl.c
> > +++ b/net/psp/psp_nl.c
> > @@ -62,36 +62,45 @@ psp_nl_multicast_per_ns(struct psp_dev *psd, unsigned int group,
> > struct net *main_net;
> > struct sk_buff *ntf;
> >
> > - main_net = dev_net(psd->main_netdev);
> > + main_net = maybe_get_net(dev_net(psd->main_netdev));
> > + if (!main_net)
> > + return;
> > +
> > xa_init(&sent_nets);
> >
> > list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) {
> > struct net *assoc_net = dev_net(entry->assoc_dev);
> > + struct net *net;
> > int ret;
> >
> > if (net_eq(assoc_net, main_net))
> > continue;
> >
> > - ret = xa_insert(&sent_nets, (unsigned long)assoc_net, assoc_net,
> > - GFP_KERNEL);
> > - if (ret == -EBUSY)
> > + net = maybe_get_net(assoc_net);
> > + if (!net)
> > continue;
> >
> > - ntf = build_ntf(psd, assoc_net, ctx);
> > - if (!ntf)
> > + ret = xa_insert(&sent_nets, (unsigned long)assoc_net, assoc_net,
> > + GFP_KERNEL);
> > + if (ret == -EBUSY) {
> > + put_net(net);
> > continue;
> > + }
> >
> > - genlmsg_multicast_netns(&psp_nl_family, assoc_net, ntf, 0,
> > - group, GFP_KERNEL);
> > + ntf = build_ntf(psd, net, ctx);
> > + if (ntf)
> > + genlmsg_multicast_netns(&psp_nl_family, net, ntf, 0,
> > + group, GFP_KERNEL);
> > + put_net(net);
> > }
>
> some optional nits if you wanted to respin:
>
> You could eliminate the extra struct net *net, by just doing something
> like if (!maybe_get_net(assoc_net)) directly.
>
> You could get away with a single put_net(net); call site, if you reorder
> the ops that could fail so that maybe_get_net(assoc_net) happens last
> before the build_ntf(psd, net, ctx)
>
> > xa_destroy(&sent_nets);
> >
> > /* Send to main device netns */
> > ntf = build_ntf(psd, main_net, ctx);
> > - if (!ntf)
> > - return;
> > - genlmsg_multicast_netns(&psp_nl_family, main_net, ntf, 0, group,
> > - GFP_KERNEL);
> > + if (ntf)
> > + genlmsg_multicast_netns(&psp_nl_family, main_net, ntf, 0, group,
> > + GFP_KERNEL);
> > + put_net(main_net);
> > }
> >
> > static struct sk_buff *psp_nl_clone_ntf(struct psp_dev *psd, struct net *net,
^ permalink raw reply
* Re: [PATCH v2 net-next 06/14] net: Add per-netns netdev unregistration infra.
From: Kuniyuki Iwashima @ 2026-07-06 16:34 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, netdev
In-Reply-To: <20260703001009.1572444-7-kuniyu@google.com>
Replying to Sashiko review,
On Thu, Jul 2, 2026 at 5:10 PM Kuniyuki Iwashima <kuniyu@google.com> wrote:
[...]
>
> Note that unregister_netdevice_move_net() does not need to call
> rtnl_net_queue_work() because __dev_change_net_namespace() is
> (supposed to be) called with rtnl_net_lock(). (Not all callers
> hold it yet, but the race does not happen until all callers
> are converted and RTNL is removed.)
[...]
> +static void unregister_netdevice_move_net(struct net *net_old,
> + struct net *net,
> + struct net_device *dev)
> +{
> + if (net_old > net) {
> + spin_lock(&net->dev_unreg_lock);
> + spin_lock_nested(&net_old->dev_unreg_lock, SINGLE_DEPTH_NESTING);
> + } else {
> + spin_lock(&net_old->dev_unreg_lock);
> + spin_lock_nested(&net->dev_unreg_lock, SINGLE_DEPTH_NESTING);
> + }
> +
> + if (!list_empty(&dev->unreg_list_net)) {
> + list_del(&dev->unreg_list_net);
> + list_add_tail(&dev->unreg_list_net, &net->dev_unreg_head);
> + }
> +
> + spin_unlock(&net_old->dev_unreg_lock);
> + spin_unlock(&net->dev_unreg_lock);
> +}
> +
> +void unregister_netdevice_many_net(struct net *net)
> +{
> + struct net_device *dev, *tmp;
> + LIST_HEAD(unreg_head_net);
> + LIST_HEAD(unreg_head);
> +
> + spin_lock(&net->dev_unreg_lock);
> + list_splice_init(&net->dev_unreg_head, &unreg_head_net);
> + spin_unlock(&net->dev_unreg_lock);
> +
> + list_for_each_entry_safe(dev, tmp, &unreg_head_net, unreg_list_net) {
> + list_del_init(&dev->unreg_list_net);
> + list_add_tail(&dev->unreg_list, &unreg_head);
> + }
---8<---
Will this result in a stalled unregistration if a cross-netns device is deleted
and concurrently moved to a new namespace?
If a user triggers deletion, unregister_netdevice_queue_net() queues the device
in dev_unreg_head and schedules the async workqueue. Because unregistration
runs asynchronously, dev->reg_state remains NETREG_REGISTERED initially.
If the pending device is concurrently moved to a new netns via
dev_change_net_namespace(), unregister_netdevice_move_net() correctly transfers
the device to the new netns's dev_unreg_head list.
However, does this block miss a call to rtnl_net_queue_work(net) to schedule
processing for the new netns?
---8<---
I didn't add rtnl_net_queue_work() intentionally as mentioned in the commit
message.
For now, such a race does not occur thanks to RTNL.
Once RTNL is removed, all dev_change_net_namespace() callers must be
called under per-netns RTNL, so even if the race occurred, the dev would be
removed at __rtnl_net_unlock().
^ permalink raw reply
* [PATCH v2 net-next] tun: no longer rely on RTNL in tun_fill_info()
From: Eric Dumazet @ 2026-07-06 16:35 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Andrew Lunn, Kuniyuki Iwashima, netdev,
eric.dumazet, Eric Dumazet, Willem de Bruijn
Update tun_fill_info() to read device configuration fields (flags, owner,
group, numqueues, numdisabled) locklessly using READ_ONCE().
Annotate all writes to these fields in the control paths with WRITE_ONCE()
to prevent data races, as these fields can be modified concurrently via
ioctls (TUNSETPERSIST, TUNSETOWNER, TUNSETGROUP, TUNSETIFF) or queue
attaching/detaching.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
---
v2: addressed Kuniyuki and sashiko feedback.
v1: https://lore.kernel.org/netdev/20260701125112.3652880-1-edumazet@google.com/
drivers/net/tun.c | 60 ++++++++++++++++++++++--------------------
drivers/net/tun_vnet.h | 8 +++---
2 files changed, 36 insertions(+), 32 deletions(-)
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index ffbe6f13fb1fa2333227a6b085806c82a362c0a4..9e2761887896955a243649b75d35a3e2a4c67529 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -532,7 +532,7 @@ static void tun_disable_queue(struct tun_struct *tun, struct tun_file *tfile)
{
tfile->detached = tun;
list_add_tail(&tfile->next, &tun->disabled);
- ++tun->numdisabled;
+ WRITE_ONCE(tun->numdisabled, tun->numdisabled + 1);
}
static struct tun_struct *tun_enable_queue(struct tun_file *tfile)
@@ -541,7 +541,7 @@ static struct tun_struct *tun_enable_queue(struct tun_file *tfile)
tfile->detached = NULL;
list_del_init(&tfile->next);
- --tun->numdisabled;
+ WRITE_ONCE(tun->numdisabled, tun->numdisabled - 1);
return tun;
}
@@ -600,7 +600,7 @@ static void __tun_detach(struct tun_file *tfile, bool clean)
rcu_assign_pointer(tun->tfiles[tun->numqueues - 1],
NULL);
- --tun->numqueues;
+ WRITE_ONCE(tun->numqueues, tun->numqueues - 1);
if (clean) {
RCU_INIT_POINTER(tfile->tun, NULL);
sock_put(&tfile->sk);
@@ -663,7 +663,7 @@ static void tun_detach_all(struct net_device *dev)
tfile->socket.sk->sk_shutdown = RCV_SHUTDOWN;
tfile->socket.sk->sk_data_ready(tfile->socket.sk);
RCU_INIT_POINTER(tfile->tun, NULL);
- --tun->numqueues;
+ WRITE_ONCE(tun->numqueues, tun->numqueues - 1);
}
list_for_each_entry(tfile, &tun->disabled, next) {
tfile->socket.sk->sk_shutdown = RCV_SHUTDOWN;
@@ -786,7 +786,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file,
if (publish_tun)
rcu_assign_pointer(tfile->tun, tun);
rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile);
- tun->numqueues++;
+ WRITE_ONCE(tun->numqueues, tun->numqueues + 1);
tun_set_real_num_queues(tun);
out:
return err;
@@ -2370,32 +2370,36 @@ static size_t tun_get_size(const struct net_device *dev)
static int tun_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
- struct tun_struct *tun = netdev_priv(dev);
+ const struct tun_struct *tun = netdev_priv(dev);
+ unsigned int flags = READ_ONCE(tun->flags);
+ kuid_t owner = READ_ONCE(tun->owner);
+ kgid_t group = READ_ONCE(tun->group);
- if (nla_put_u8(skb, IFLA_TUN_TYPE, tun->flags & TUN_TYPE_MASK))
+ if (nla_put_u8(skb, IFLA_TUN_TYPE, flags & TUN_TYPE_MASK))
goto nla_put_failure;
- if (uid_valid(tun->owner) &&
+ if (uid_valid(owner) &&
nla_put_u32(skb, IFLA_TUN_OWNER,
- from_kuid_munged(current_user_ns(), tun->owner)))
+ from_kuid_munged(current_user_ns(), owner)))
goto nla_put_failure;
- if (gid_valid(tun->group) &&
+ if (gid_valid(group) &&
nla_put_u32(skb, IFLA_TUN_GROUP,
- from_kgid_munged(current_user_ns(), tun->group)))
+ from_kgid_munged(current_user_ns(), group)))
goto nla_put_failure;
- if (nla_put_u8(skb, IFLA_TUN_PI, !(tun->flags & IFF_NO_PI)))
+ if (nla_put_u8(skb, IFLA_TUN_PI, !(flags & IFF_NO_PI)))
goto nla_put_failure;
- if (nla_put_u8(skb, IFLA_TUN_VNET_HDR, !!(tun->flags & IFF_VNET_HDR)))
+ if (nla_put_u8(skb, IFLA_TUN_VNET_HDR, !!(flags & IFF_VNET_HDR)))
goto nla_put_failure;
- if (nla_put_u8(skb, IFLA_TUN_PERSIST, !!(tun->flags & IFF_PERSIST)))
+ if (nla_put_u8(skb, IFLA_TUN_PERSIST, !!(flags & IFF_PERSIST)))
goto nla_put_failure;
if (nla_put_u8(skb, IFLA_TUN_MULTI_QUEUE,
- !!(tun->flags & IFF_MULTI_QUEUE)))
+ !!(flags & IFF_MULTI_QUEUE)))
goto nla_put_failure;
- if (tun->flags & IFF_MULTI_QUEUE) {
- if (nla_put_u32(skb, IFLA_TUN_NUM_QUEUES, tun->numqueues))
+ if (flags & IFF_MULTI_QUEUE) {
+ if (nla_put_u32(skb, IFLA_TUN_NUM_QUEUES,
+ READ_ONCE(tun->numqueues)))
goto nla_put_failure;
if (nla_put_u32(skb, IFLA_TUN_NUM_DISABLED_QUEUES,
- tun->numdisabled))
+ READ_ONCE(tun->numdisabled)))
goto nla_put_failure;
}
@@ -2814,8 +2818,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
return 0;
}
- tun->flags = (tun->flags & ~TUN_FEATURES) |
- (ifr->ifr_flags & TUN_FEATURES);
+ WRITE_ONCE(tun->flags, (tun->flags & ~TUN_FEATURES) |
+ (ifr->ifr_flags & TUN_FEATURES));
netdev_state_change(dev);
} else {
@@ -3213,13 +3217,13 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
/* Disable/Enable persist mode. Keep an extra reference to the
* module to prevent the module being unprobed.
*/
- if (arg && !(tun->flags & IFF_PERSIST)) {
- tun->flags |= IFF_PERSIST;
+ if (arg && !(READ_ONCE(tun->flags) & IFF_PERSIST)) {
+ WRITE_ONCE(tun->flags, READ_ONCE(tun->flags) | IFF_PERSIST);
__module_get(THIS_MODULE);
do_notify = true;
}
- if (!arg && (tun->flags & IFF_PERSIST)) {
- tun->flags &= ~IFF_PERSIST;
+ if (!arg && (READ_ONCE(tun->flags) & IFF_PERSIST)) {
+ WRITE_ONCE(tun->flags, READ_ONCE(tun->flags) & ~IFF_PERSIST);
module_put(THIS_MODULE);
do_notify = true;
}
@@ -3235,10 +3239,10 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
ret = -EINVAL;
break;
}
- tun->owner = owner;
+ WRITE_ONCE(tun->owner, owner);
do_notify = true;
netif_info(tun, drv, tun->dev, "owner set to %u\n",
- from_kuid(&init_user_ns, tun->owner));
+ from_kuid(&init_user_ns, owner));
break;
case TUNSETGROUP:
@@ -3248,10 +3252,10 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
ret = -EINVAL;
break;
}
- tun->group = group;
+ WRITE_ONCE(tun->group, group);
do_notify = true;
netif_info(tun, drv, tun->dev, "group set to %u\n",
- from_kgid(&init_user_ns, tun->group));
+ from_kgid(&init_user_ns, group));
break;
case TUNSETLINK:
diff --git a/drivers/net/tun_vnet.h b/drivers/net/tun_vnet.h
index fa5cab9d3e55c5a6e70afe4c76582c8affe0b73c..f4c652b1fa44d82ae23243857809619bfb5290d2 100644
--- a/drivers/net/tun_vnet.h
+++ b/drivers/net/tun_vnet.h
@@ -40,9 +40,9 @@ static inline long tun_set_vnet_be(unsigned int *flags, int __user *argp)
return -EFAULT;
if (be)
- *flags |= TUN_VNET_BE;
+ WRITE_ONCE(*flags, *flags | TUN_VNET_BE);
else
- *flags &= ~TUN_VNET_BE;
+ WRITE_ONCE(*flags, *flags & ~TUN_VNET_BE);
return 0;
}
@@ -93,9 +93,9 @@ static inline long tun_vnet_ioctl(int *vnet_hdr_sz, unsigned int *flags,
if (get_user(s, sp))
return -EFAULT;
if (s)
- *flags |= TUN_VNET_LE;
+ WRITE_ONCE(*flags, *flags | TUN_VNET_LE);
else
- *flags &= ~TUN_VNET_LE;
+ WRITE_ONCE(*flags, *flags & ~TUN_VNET_LE);
return 0;
case TUNGETVNETBE:
--
2.55.0.795.g602f6c329a-goog
^ permalink raw reply related
* Re: [PATCH v5] net: gro: fix double aggregation of flush-marked skbs
From: Willem de Bruijn @ 2026-07-06 16:36 UTC (permalink / raw)
To: Shiming Cheng, davem, edumazet, kuba, pabeni, horms, matthias.bgg,
angelogioacchino.delregno, willemb, daniel.zahka, alice, sd,
eilaimemedsnaimel, imv4bel, nbd, dsahern, netdev, linux-kernel,
linux-arm-kernel, linux-mediatek, steffen.klassert
Cc: stable, lena.wang, shiming.cheng
In-Reply-To: <20260706034611.360-1-shiming.cheng@mediatek.com>
[PATCH net v5]
Shiming Cheng wrote:
> The skb_gro_receive_list() function is missing a critical safety check
> that exists in the skb_gro_receive() implementation. Specifically, it
> does not validate NAPI_GRO_CB(skb)->flush before allowing packet
> aggregation
In v3 I requested referring to the commit that fixed this in
skb_gro_receive: commit 0ab03f353d36 ("net-gro: Fix GRO flush when
receiving a GSO packet."). That explains the issue well.
>
> This allows already-GRO'd packets with existing frag_list to be
> re-aggregated into a new GRO session, corrupting the frag_list chain
> structure. When skb_segment() attempts to unpack these malformed packets,
> it encounters invalid state and triggers a kernel panic.
>
> Scenario (Tethering/Device forwarding):
> 1. Driver: Generated aggregated packet P1 via LRO with frag_list
> 2. Dev A: Receives aggregated fraglist packet and flush flag set
> 3. Dev A: Re-enters GRO, skb_gro_receive_list() is called
> 4. Missing flush check allows re-aggregation despite flush flag
> 5. Frag_list chain becomes corrupted (loops or dangling refs)
> 6. Dev B: TX path calls skb_segment(), crashes on corrupted frag_list
>
> Root cause in skb_segment():
> The check at line ~4891:
> if (hsize <= 0 && i >= nfrags && skb_headlen(list_skb) &&
> (skb_headlen(list_skb) == len || sg)) {
>
> When frag_list is corrupted by double aggregation, when list_skb is
> a NULL pointer from skb->next, skb_headlen(list_skb) dereference
> NULL/corrupted pointers occurs.
>
> Call Trace:
> skb_headlen(NULL skb)
> skb_segment
> tcp_gso_segment
> tcp4_gso_segment
> inet_gso_segment
> skb_mac_gso_segment
> __skb_gso_segment
> skb_gso_segment
> validate_xmit_skb
> validate_xmit_skb_list
> sch_direct_xmit
> qdisc_restart
> __qdisc_run
> qdisc_run
> net_tx_action
>
> Fix: Add NAPI_GRO_CB(skb)->flush validation to the early-return check in
> skb_gro_receive_list(), matching the defensive programming pattern of
> skb_gro_receive().
>
> Fixes: 3a1296a38d0c ("net: Support GRO/GSO fraglist chaining.")
> Cc: stable@vger.kernel.org
> Signed-off-by: Shiming Cheng <shiming.cheng@mediatek.com>
> ---
> net/core/gro.c | 9 ++++++++-
> 1 file changed, 8 insertions(+), 1 deletion(-)
>
> diff --git a/net/core/gro.c b/net/core/gro.c
> index 35f2f708f010..b1573d98f3a5 100644
> --- a/net/core/gro.c
> +++ b/net/core/gro.c
> @@ -229,7 +229,14 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb)
>
> int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
> {
> - if (unlikely(p->len + skb->len >= 65536))
> + /*
> + * Packets marked with NAPI_GRO_CB(skb)->flush have already gone
> + * through GRO/LRO processing and must not be aggregated again.
> + * Re-entering frag_list GRO may corrupt the frag_list chain and
> + * later crash during GSO segmentaiont.
> + */
Such a verbose comment is not needed. Code would be overwhelmed by
comments if done everywhere.
> + if (unlikely(p->len + skb->len >= 65536 ||
> + NAPI_GRO_CB(skb)->flush))
> return -E2BIG;
>
> if (!pskb_may_pull(skb, skb_gro_offset(skb))) {
> --
> 2.45.2
>
^ permalink raw reply
* Re: [PATCH v2 net-next 08/14] veth: Support per-netns device unregistration.
From: Kuniyuki Iwashima @ 2026-07-06 16:43 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, netdev
In-Reply-To: <20260703001009.1572444-9-kuniyu@google.com>
Replying to Sashiko review,
On Thu, Jul 2, 2026 at 5:10 PM Kuniyuki Iwashima <kuniyu@google.com> wrote:
[...]
> @@ -1901,15 +1902,17 @@ static int veth_newlink(struct net_device *dev,
>
> priv = netdev_priv(dev);
> rcu_assign_pointer(priv->peer, peer);
> + netdev_hold(peer, &priv->peer_tracker, GFP_KERNEL);
---8<---
Because the devices are already registered and visible to the system at this
point, could a concurrent ip link del trigger veth_dellink() right after
rcu_assign_pointer() publishes priv->peer, but before netdev_hold() is
called?
---8<---
No, ->newlink() and ->dellink() are serialised with the same rtnl_net_lock().
> err = veth_init_queues(dev, tb);
> if (err)
> goto err_queues;
>
> priv = netdev_priv(peer);
> rcu_assign_pointer(priv->peer, dev);
> + netdev_hold(dev, &priv->peer_tracker, GFP_KERNEL);
> err = veth_init_queues(peer, tb);
> if (err)
> - goto err_queues;
> + goto err_peer_queues;
>
> veth_disable_gro(dev);
> /* update XDP supported features */
> @@ -1918,7 +1921,11 @@ static int veth_newlink(struct net_device *dev,
>
> return 0;
>
> +err_peer_queues:
> + netdev_put(dev, &priv->peer_tracker);
> + priv = netdev_priv(dev);
> err_queues:
> + netdev_put(peer, &priv->peer_tracker);
> unregister_netdevice(dev);
---8<---
If veth_init_queues() fails, does this error path safely handle a concurrent
veth_dellink()?
---8<---
This depends on the same wrong assumption, so no, ->newlink() holds
multiple rtnl_net_lock() so that the concurrent ->dellink() never occurs.
> err_register_dev:
> /* nothing to do */
> @@ -1933,24 +1940,25 @@ static int veth_newlink(struct net_device *dev,
>
> static void veth_dellink(struct net_device *dev, struct list_head *head)
> {
> - struct veth_priv *priv;
> + netdevice_tracker *peer_tracker;
> struct net_device *peer;
> + struct veth_priv *priv;
>
> priv = netdev_priv(dev);
> - peer = rtnl_dereference(priv->peer);
> + peer_tracker = &priv->peer_tracker;
> + peer = unrcu_pointer(xchg(&priv->peer, NULL));
---8<---
Does changing this to use xchg() introduce a data race with read paths?
Functions like veth_set_xdp_features() still use rtnl_dereference(),
...
Might this need READ_ONCE() or rcu_dereference() to safely handle the
concurrent xchg() writes?
---8<---
Yes, but with another series before removing RTNL.
As mentioned in the cover letter, the device unreg path is the only target
of this series, and we need more changes in other paths.
^ permalink raw reply
* Re: [PATCH v2 net-next 11/14] ipvlan: Convert ipvl_port.count to refcount_t.
From: Kuniyuki Iwashima @ 2026-07-06 16:50 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, netdev
In-Reply-To: <20260703001009.1572444-12-kuniyu@google.com>
Replying to Sashiko review,
On Thu, Jul 2, 2026 at 5:10 PM Kuniyuki Iwashima <kuniyu@google.com> wrote:
[...]
> -static void ipvlan_port_destroy(struct net_device *dev)
> +static void ipvlan_port_destroy(struct ipvl_port *port)
> {
> - struct ipvl_port *port = ipvlan_port_get_rtnl(dev);
> + struct net_device *dev = port->dev;
> struct sk_buff *skb;
>
> - netdev_put(dev, &port->dev_tracker);
> if (port->mode == IPVLAN_MODE_L3S)
> ipvlan_l3s_unregister(port);
> +
> netdev_rx_handler_unregister(dev);
---8<---
Can this trigger a lockdep warning due to a missing global RTNL lock?
---8<--
Once RTNL is removed, yes, but out-of-scope of this series as
mentioned in the cover letter.
> cancel_work_sync(&port->wq);
> + netdev_put(dev, &port->dev_tracker);
> +
> while ((skb = __skb_dequeue(&port->backlog)) != NULL) {
> dev_put(skb->dev);
> kfree_skb(skb);
> @@ -111,6 +114,27 @@ static void ipvlan_port_destroy(struct net_device *dev)
> kfree(port);
> }
>
> +static void ipvlan_port_put(struct ipvl_port *port)
> +{
> + if (refcount_dec_and_test(&port->count))
> + ipvlan_port_destroy(port);
> +}
> +
> +static struct ipvl_port *ipvlan_port_get(struct net_device *dev)
> +{
> + struct ipvl_port *port = NULL;
> +
> + rcu_read_lock();
> + if (netif_is_ipvlan_port(dev)) {
> + port = ipvlan_port_get_rcu(dev);
> + if (!refcount_inc_not_zero(&port->count))
---8<---
Is there a potential NULL pointer dereference here due to CPU read-read
reordering?
In ipvlan_port_get(), netif_is_ipvlan_port(dev) checks dev->rx_handler,
and then ipvlan_port_get_rcu(dev) fetches dev->rx_handler_data. Because
there is only a control dependency and no data dependency or memory barrier
between these reads, the CPU might speculatively reorder them.
If this races with netdev_rx_handler_register() on another CPU during
interface creation, could the reader fetch the old NULL rx_handler_data
along with the new non-NULL rx_handler, resulting in a NULL port pointer
being passed to refcount_inc_not_zero()?
---8<---
No, ipvlan_port_get() is called from ipvlan_device_event(), and
netdev_rx_handler_register() is called from ->newlink(), so they are
under the same rtnl_net_lock().
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox