* [PATCH net-next v2 4/4] net: hsr: reject unresolved interlink ifindex
From: luka.gejak @ 2026-03-26 15:47 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, netdev
Cc: horms, fmaurer, liuhangbin, linux-kernel, luka.gejak
In-Reply-To: <20260326154715.38405-1-luka.gejak@linux.dev>
From: Luka Gejak <luka.gejak@linux.dev>
In hsr_newlink(), a provided but invalid IFLA_HSR_INTERLINK attribute
was silently ignored if __dev_get_by_index() returned NULL. This leads
to incorrect RedBox topology creation without notifying the user.
Fix this by returning -EINVAL and an extack message when the
interlink attribute is present but cannot be resolved.
Reviewed-by: Felix Maurer <fmaurer@redhat.com>
Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
---
net/hsr/hsr_netlink.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c
index db0b0af7a692..f0ca23da3ab9 100644
--- a/net/hsr/hsr_netlink.c
+++ b/net/hsr/hsr_netlink.c
@@ -76,9 +76,14 @@ static int hsr_newlink(struct net_device *dev,
return -EINVAL;
}
- if (data[IFLA_HSR_INTERLINK])
+ if (data[IFLA_HSR_INTERLINK]) {
interlink = __dev_get_by_index(link_net,
nla_get_u32(data[IFLA_HSR_INTERLINK]));
+ if (!interlink) {
+ NL_SET_ERR_MSG_MOD(extack, "Interlink does not exist");
+ return -EINVAL;
+ }
+ }
if (interlink && interlink == link[0]) {
NL_SET_ERR_MSG_MOD(extack, "Interlink and Slave1 are the same");
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v2 3/4] net: hsr: require valid EOT supervision TLV
From: luka.gejak @ 2026-03-26 15:47 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, netdev
Cc: horms, fmaurer, liuhangbin, linux-kernel, luka.gejak
In-Reply-To: <20260326154715.38405-1-luka.gejak@linux.dev>
From: Luka Gejak <luka.gejak@linux.dev>
Supervision frames are only valid if terminated with a zero-length EOT
TLV. The current check fails to reject non-EOT entries as the terminal
TLV, potentially allowing malformed supervision traffic.
Fix this by strictly requiring the terminal TLV to be HSR_TLV_EOT
with a length of zero.
Reviewed-by: Felix Maurer <fmaurer@redhat.com>
Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
---
net/hsr/hsr_forward.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
index aefc9b6936ba..d26c7d0e8109 100644
--- a/net/hsr/hsr_forward.c
+++ b/net/hsr/hsr_forward.c
@@ -110,7 +110,7 @@ static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb)
}
/* end of tlvs must follow at the end */
- if (hsr_sup_tlv->HSR_TLV_type == HSR_TLV_EOT &&
+ if (hsr_sup_tlv->HSR_TLV_type != HSR_TLV_EOT ||
hsr_sup_tlv->HSR_TLV_length != 0)
return false;
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v2 2/4] net: hsr: fix VLAN add unwind on slave errors
From: luka.gejak @ 2026-03-26 15:47 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, netdev
Cc: horms, fmaurer, liuhangbin, linux-kernel, luka.gejak
In-Reply-To: <20260326154715.38405-1-luka.gejak@linux.dev>
From: Luka Gejak <luka.gejak@linux.dev>
When vlan_vid_add() fails for a secondary slave, the error path calls
vlan_vid_del() on the failing port instead of the peer slave that had
already succeeded. This results in asymmetric VLAN state across the HSR
pair.
Fix this by switching to a centralized unwind path that removes the VID
from any slave device that was already programmed.
Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
---
net/hsr/hsr_device.c | 46 ++++++++++++++++++++++++++------------------
1 file changed, 27 insertions(+), 19 deletions(-)
diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
index 5c3eca2235ce..75c491279df8 100644
--- a/net/hsr/hsr_device.c
+++ b/net/hsr/hsr_device.c
@@ -532,8 +532,8 @@ static void hsr_change_rx_flags(struct net_device *dev, int change)
static int hsr_ndo_vlan_rx_add_vid(struct net_device *dev,
__be16 proto, u16 vid)
{
- bool is_slave_a_added = false;
- bool is_slave_b_added = false;
+ struct net_device *slave_a_dev = NULL;
+ struct net_device *slave_b_dev = NULL;
struct hsr_port *port;
struct hsr_priv *hsr;
int ret = 0;
@@ -546,29 +546,28 @@ static int hsr_ndo_vlan_rx_add_vid(struct net_device *dev,
continue;
ret = vlan_vid_add(port->dev, proto, vid);
- switch (port->type) {
- case HSR_PT_SLAVE_A:
- if (ret) {
- /* clean up Slave-B */
+ if (ret) {
+ switch (port->type) {
+ case HSR_PT_SLAVE_A:
netdev_err(dev, "add vid failed for Slave-A\n");
- if (is_slave_b_added)
- vlan_vid_del(port->dev, proto, vid);
- return ret;
+ break;
+ case HSR_PT_SLAVE_B:
+ netdev_err(dev, "add vid failed for Slave-B\n");
+ break;
+ default:
+ break;
}
- is_slave_a_added = true;
+ goto unwind;
+ }
+
+ switch (port->type) {
+ case HSR_PT_SLAVE_A:
+ slave_a_dev = port->dev;
break;
case HSR_PT_SLAVE_B:
- if (ret) {
- /* clean up Slave-A */
- netdev_err(dev, "add vid failed for Slave-B\n");
- if (is_slave_a_added)
- vlan_vid_del(port->dev, proto, vid);
- return ret;
- }
-
- is_slave_b_added = true;
+ slave_b_dev = port->dev;
break;
default:
break;
@@ -576,6 +575,15 @@ static int hsr_ndo_vlan_rx_add_vid(struct net_device *dev,
}
return 0;
+
+unwind:
+ if (slave_a_dev)
+ vlan_vid_del(slave_a_dev, proto, vid);
+
+ if (slave_b_dev)
+ vlan_vid_del(slave_b_dev, proto, vid);
+
+ return ret;
}
static int hsr_ndo_vlan_rx_kill_vid(struct net_device *dev,
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v2 1/4] net: hsr: serialize seq_blocks merge across nodes
From: luka.gejak @ 2026-03-26 15:47 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, netdev
Cc: horms, fmaurer, liuhangbin, linux-kernel, luka.gejak
In-Reply-To: <20260326154715.38405-1-luka.gejak@linux.dev>
From: Luka Gejak <luka.gejak@linux.dev>
During node merging, hsr_handle_sup_frame() walks node_curr->seq_blocks
to update node_real without holding node_curr->seq_out_lock. This
allows concurrent mutations from duplicate registration paths, risking
inconsistent state or XArray/bitmap corruption.
Fix this by locking both nodes' seq_out_lock during the merge.
To prevent ABBA deadlocks, locks are acquired in order of memory
address.
Reviewed-by: Felix Maurer <fmaurer@redhat.com>
Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
---
net/hsr/hsr_framereg.c | 38 ++++++++++++++++++++++++++++++++++++--
1 file changed, 36 insertions(+), 2 deletions(-)
diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
index 577fb588bc2f..d09875b33588 100644
--- a/net/hsr/hsr_framereg.c
+++ b/net/hsr/hsr_framereg.c
@@ -123,6 +123,40 @@ static void hsr_free_node_rcu(struct rcu_head *rn)
hsr_free_node(node);
}
+static void hsr_lock_seq_out_pair(struct hsr_node *node_a,
+ struct hsr_node *node_b)
+{
+ if (node_a == node_b) {
+ spin_lock_bh(&node_a->seq_out_lock);
+ return;
+ }
+
+ if (node_a < node_b) {
+ spin_lock_bh(&node_a->seq_out_lock);
+ spin_lock_nested(&node_b->seq_out_lock, SINGLE_DEPTH_NESTING);
+ } else {
+ spin_lock_bh(&node_b->seq_out_lock);
+ spin_lock_nested(&node_a->seq_out_lock, SINGLE_DEPTH_NESTING);
+ }
+}
+
+static void hsr_unlock_seq_out_pair(struct hsr_node *node_a,
+ struct hsr_node *node_b)
+{
+ if (node_a == node_b) {
+ spin_unlock_bh(&node_a->seq_out_lock);
+ return;
+ }
+
+ if (node_a < node_b) {
+ spin_unlock(&node_b->seq_out_lock);
+ spin_unlock_bh(&node_a->seq_out_lock);
+ } else {
+ spin_unlock(&node_a->seq_out_lock);
+ spin_unlock_bh(&node_b->seq_out_lock);
+ }
+}
+
void hsr_del_nodes(struct list_head *node_db)
{
struct hsr_node *node;
@@ -432,7 +466,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
}
ether_addr_copy(node_real->macaddress_B, ethhdr->h_source);
- spin_lock_bh(&node_real->seq_out_lock);
+ hsr_lock_seq_out_pair(node_real, node_curr);
for (i = 0; i < HSR_PT_PORTS; i++) {
if (!node_curr->time_in_stale[i] &&
time_after(node_curr->time_in[i], node_real->time_in[i])) {
@@ -455,7 +489,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
src_blk->seq_nrs[i], HSR_SEQ_BLOCK_SIZE);
}
}
- spin_unlock_bh(&node_real->seq_out_lock);
+ hsr_unlock_seq_out_pair(node_real, node_curr);
node_real->addr_B_port = port_rcv->type;
spin_lock_bh(&hsr->list_lock);
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v2 0/4] net: hsr: address functional and concurrency bugs
From: luka.gejak @ 2026-03-26 15:47 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, netdev
Cc: horms, fmaurer, liuhangbin, linux-kernel, luka.gejak
From: Luka Gejak <luka.gejak@linux.dev>
Changes in v2:
- picked up Reviewed-by tags on patches 1, 3 and 4
- changes in patch 2 per advice of Felix Maurer
This series addresses four logic bugs in the HSR/PRP implementation
identified during a protocol audit.
The primary change resolves a race condition in the node merging path by
implementing address-based lock ordering. This ensures that concurrent
mutations of sequence blocks do not lead to state corruption or
deadlocks.
Additional fixes include correcting asymmetric VLAN error unwinding,
enforcing strict supervision frame TLV validation, and improving Netlink
error reporting for invalid interlink attributes.
Luka Gejak (4):
net: hsr: serialize seq_blocks merge across nodes
net: hsr: fix VLAN add unwind on slave errors
net: hsr: require valid EOT supervision TLV
net: hsr: reject unresolved interlink ifindex
net/hsr/hsr_device.c | 46 +++++++++++++++++++++++++-----------------
net/hsr/hsr_forward.c | 2 +-
net/hsr/hsr_framereg.c | 38 ++++++++++++++++++++++++++++++++--
net/hsr/hsr_netlink.c | 7 ++++++-
4 files changed, 70 insertions(+), 23 deletions(-)
--
2.53.0
^ permalink raw reply
* Re: [PATCH net 2/2] net: xilinx: axienet: Fix BQL accounting for multi-BD TX packets
From: Sean Anderson @ 2026-03-26 15:38 UTC (permalink / raw)
To: Gupta, Suraj, andrew+netdev@lunn.ch, davem@davemloft.net,
edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
Simek, Michal, Pandey, Radhey Shyam, horms@kernel.org
Cc: netdev@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, Katakam, Harini
In-Reply-To: <SA1PR12MB6798E4EB242D58AEB86661B9C949A@SA1PR12MB6798.namprd12.prod.outlook.com>
On 3/25/26 01:30, Gupta, Suraj wrote:
> [Public]
>
>> -----Original Message-----
>> From: Sean Anderson <sean.anderson@linux.dev>
>> Sent: Tuesday, March 24, 2026 9:39 PM
>> To: Gupta, Suraj <Suraj.Gupta2@amd.com>; andrew+netdev@lunn.ch;
>> davem@davemloft.net; edumazet@google.com; kuba@kernel.org;
>> pabeni@redhat.com; Simek, Michal <michal.simek@amd.com>; Pandey,
>> Radhey Shyam <radhey.shyam.pandey@amd.com>; horms@kernel.org
>> Cc: netdev@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux-
>> kernel@vger.kernel.org; Katakam, Harini <harini.katakam@amd.com>
>> Subject: Re: [PATCH net 2/2] net: xilinx: axienet: Fix BQL accounting for multi-BD
>> TX packets
>>
>> Caution: This message originated from an External Source. Use proper caution
>> when opening attachments, clicking links, or responding.
>>
>>
>> On 3/24/26 10:53, Suraj Gupta wrote:
>> > When a TX packet spans multiple buffer descriptors (scatter-gather),
>> > the per-BD byte count is accumulated into a local variable that resets
>> > on each NAPI poll. If the BDs for a single packet complete across
>> > different polls, the earlier bytes are lost and never credited to BQL.
>> > This causes BQL to think bytes are permanently in-flight, eventually
>> > stalling the TX queue.
>> >
>> > Fix this by replacing the local accumulator with a persistent counter
>> > (tx_compl_bytes) that survives across polls and is reset only after
>> > updating BQL and stats.
>>
>> Do we need this? Can't we just do something like
>>
>
> Nope, the 'size' variable passed to axienet_free_tx_chain() is local
> to axienet_tx_poll() and goes out of scope between different polls.
> This means it can't track completion bytes across multiple NAPI polls.
Yes, but that's fine since we only update completed bytes when we retire at least one packet:
packets = axienet_free_tx_chain(lp, &size, budget);
if (packets) {
netdev_completed_queue(ndev, packets, size);
u64_stats_update_begin(&lp->tx_stat_sync);
u64_stats_add(&lp->tx_packets, packets);
u64_stats_add(&lp->tx_bytes, size);
u64_stats_update_end(&lp->tx_stat_sync);
/* Matches barrier in axienet_start_xmit */
smp_mb();
if (((tail - ci) & (lp->rx_bd_num - 1)) >= MAX_SKB_FRAGS + 1)
netif_wake_queue(ndev);
}
and this matches the value we use when enqueuing a packet
tail_p = lp->tx_bd_p + sizeof(*lp->tx_bd_v) * (last_p - lp->tx_bd_v);
smp_store_release(&lp->tx_bd_tail, new_tail_ptr);
netdev_sent_queue(ndev, skb->len);
> Regards,
> Suraj
>
>> diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
>> b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
>> index 415e9bc252527..1ea8a6592bce1 100644
>> --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
>> +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
>> @@ -768,6 +768,7 @@ static int axienet_free_tx_chain(struct axienet_local
>> *lp, u32 *sizep, int budge
>> if (cur_p->skb) {
>> struct axienet_cb *cb = (void *)cur_p->skb->cb;
>>
>> + *sizep += skb->len;
>> dma_unmap_sgtable(lp->dev, &cb->sgt, DMA_TO_DEVICE, 0);
>> sg_free_table_chained(&cb->sgt, XAE_INLINE_SG_CNT);
>> napi_consume_skb(cur_p->skb, budget); @@ -783,8 +784,6 @@
>> static int axienet_free_tx_chain(struct axienet_local *lp, u32 *sizep, int budge
>> wmb();
>> cur_p->cntrl = 0;
>> cur_p->status = 0;
>> -
>> - *sizep += status & XAXIDMA_BD_STS_ACTUAL_LEN_MASK;
>> }
>>
>> smp_store_release(&lp->tx_bd_ci, (ci + i) & (lp->tx_bd_num - 1));
>>
>> > Fixes: c900e49d58eb ("net: xilinx: axienet: Implement BQL")
>> > Signed-off-by: Suraj Gupta <suraj.gupta2@amd.com>
>> > ---
>> > drivers/net/ethernet/xilinx/xilinx_axienet.h | 3 +++
>> > .../net/ethernet/xilinx/xilinx_axienet_main.c | 20 +++++++++----------
>> > 2 files changed, 13 insertions(+), 10 deletions(-)
>> >
>> > diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h
>> > b/drivers/net/ethernet/xilinx/xilinx_axienet.h
>> > index 602389843342..a4444c939451 100644
>> > --- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
>> > +++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
>> > @@ -509,6 +509,8 @@ struct skbuf_dma_descriptor {
>> > * complete. Only updated at runtime by TX NAPI poll.
>> > * @tx_bd_tail: Stores the index of the next Tx buffer descriptor in the ring
>> > * to be populated.
>> > + * @tx_compl_bytes: Accumulates TX completion length until a full packet is
>> > + * reported to the stack.
>> > * @tx_packets: TX packet count for statistics
>> > * @tx_bytes: TX byte count for statistics
>> > * @tx_stat_sync: Synchronization object for TX stats @@ -592,6
>> > +594,7 @@ struct axienet_local {
>> > u32 tx_bd_num;
>> > u32 tx_bd_ci;
>> > u32 tx_bd_tail;
>> > + u32 tx_compl_bytes;
>> > u64_stats_t tx_packets;
>> > u64_stats_t tx_bytes;
>> > struct u64_stats_sync tx_stat_sync; diff --git
>> > a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
>> > b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
>> > index b06e4c37ff61..95bf61986cb7 100644
>> > --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
>> > +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
>> > @@ -692,6 +692,8 @@ static void axienet_dma_stop(struct axienet_local
>> *lp)
>> > axienet_lock_mii(lp);
>> > __axienet_device_reset(lp);
>> > axienet_unlock_mii(lp);
>> > +
>> > + lp->tx_compl_bytes = 0;
>> > }
>> >
>> > /**
>> > @@ -770,8 +772,6 @@ static int axienet_device_reset(struct net_device
>> *ndev)
>> > * @first_bd: Index of first descriptor to clean up
>> > * @nr_bds: Max number of descriptors to clean up
>> > * @force: Whether to clean descriptors even if not complete
>> > - * @sizep: Pointer to a u32 filled with the total sum of all bytes
>> > - * in all cleaned-up descriptors. Ignored if NULL.
>> > * @budget: NAPI budget (use 0 when not called from NAPI poll)
>> > *
>> > * Would either be called after a successful transmit operation, or
>> > after @@ -780,7 +780,7 @@ static int axienet_device_reset(struct net_device
>> *ndev)
>> > * Return: The number of packets handled.
>> > */
>> > static int axienet_free_tx_chain(struct axienet_local *lp, u32 first_bd,
>> > - int nr_bds, bool force, u32 *sizep, int budget)
>> > + int nr_bds, bool force, int budget)
>> > {
>> > struct axidma_bd *cur_p;
>> > unsigned int status;
>> > @@ -819,8 +819,8 @@ static int axienet_free_tx_chain(struct axienet_local
>> *lp, u32 first_bd,
>> > cur_p->cntrl = 0;
>> > cur_p->status = 0;
>> >
>> > - if (sizep)
>> > - *sizep += status & XAXIDMA_BD_STS_ACTUAL_LEN_MASK;
>> > + if (!force)
>> > + lp->tx_compl_bytes += status &
>> > + XAXIDMA_BD_STS_ACTUAL_LEN_MASK;
>> > }
>> >
>> > if (!force) {
>> > @@ -999,18 +999,18 @@ static int axienet_tx_poll(struct napi_struct
>> > *napi, int budget) {
>> > struct axienet_local *lp = container_of(napi, struct axienet_local, napi_tx);
>> > struct net_device *ndev = lp->ndev;
>> > - u32 size = 0;
>> > int packets;
>> >
>> > packets = axienet_free_tx_chain(lp, lp->tx_bd_ci, lp->tx_bd_num, false,
>> > - &size, budget);
>> > + budget);
>> >
>> > if (packets) {
>> > - netdev_completed_queue(ndev, packets, size);
>> > + netdev_completed_queue(ndev, packets,
>> > + lp->tx_compl_bytes);
>> > u64_stats_update_begin(&lp->tx_stat_sync);
>> > u64_stats_add(&lp->tx_packets, packets);
>> > - u64_stats_add(&lp->tx_bytes, size);
>> > + u64_stats_add(&lp->tx_bytes, lp->tx_compl_bytes);
>> > u64_stats_update_end(&lp->tx_stat_sync);
>> > + lp->tx_compl_bytes = 0;
>> >
>> > /* Matches barrier in axienet_start_xmit */
>> > smp_mb();
>> > @@ -1115,7 +1115,7 @@ axienet_start_xmit(struct sk_buff *skb, struct
>> net_device *ndev)
>> > netdev_err(ndev, "TX DMA mapping error\n");
>> > ndev->stats.tx_dropped++;
>> > axienet_free_tx_chain(lp, orig_tail_ptr, ii + 1,
>> > - true, NULL, 0);
>> > + true, 0);
>> > dev_kfree_skb_any(skb);
>> > return NETDEV_TX_OK;
>> > }
^ permalink raw reply
* [PATCH v2] net: phy: air_en8811h: add AN8811HB MCU assert/deassert support
From: Lucien.Jheng @ 2026-03-26 15:35 UTC (permalink / raw)
To: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, netdev,
linux-kernel, bjorn
Cc: ericwouds, frank-w, daniel, lucien.jheng, Lucien.Jheng
AN8811HB needs a MCU soft-reset cycle before firmware loading begins.
Assert the MCU (hold it in reset) and immediately deassert (release)
via a dedicated PBUS register pair (0x5cf9f8 / 0x5cf9fc), accessed
through the PHY-addr+8 MDIO bus node rather than the BUCKPBUS indirect
path. This clears the MCU state before the firmware loading sequence
starts.
Add __air_pbus_reg_write() as a low-level helper for this access, then
implement an8811hb_mcu_assert() / _deassert() on top of it. Wire both
into an8811hb_load_firmware() and en8811h_restart_mcu() so every
firmware load or MCU restart on AN8811HB correctly sequences the reset
control registers.
Fixes: 0a55766b7711 ("net: phy: air_en8811h: add Airoha AN8811HB support")
Signed-off-by: Lucien Jheng <lucienzx159@gmail.com>
---
Changes in v2:
- Rewrite commit message: The previous wording was ambiguous,
as it suggested the MCU remains asserted during the entire
firmware loading process, rather than a quick reset cycle
before it starts.
Clarify that assert and deassert is an immediate soft-reset
cycle to clear MCU state before firmware loading begins.
- Add Fixes: 0a55766b7711 ("net: phy: air_en8811h: add Airoha AN8811HB
support").
- Change phydev_info() to phydev_dbg() in an8811hb_mcu_assert() and
an8811hb_mcu_deassert() to avoid noise during normal boot.
drivers/net/phy/air_en8811h.c | 105 ++++++++++++++++++++++++++++++++++
1 file changed, 105 insertions(+)
diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c
index 29ae73e65caa..01fce1b93618 100644
--- a/drivers/net/phy/air_en8811h.c
+++ b/drivers/net/phy/air_en8811h.c
@@ -170,6 +170,16 @@
#define AN8811HB_CLK_DRV_CKO_LDPWD BIT(13)
#define AN8811HB_CLK_DRV_CKO_LPPWD BIT(14)
+#define AN8811HB_MCU_SW_RST 0x5cf9f8
+#define AN8811HB_MCU_SW_RST_HOLD BIT(16)
+#define AN8811HB_MCU_SW_RST_RUN (BIT(16) | BIT(0))
+#define AN8811HB_MCU_SW_START 0x5cf9fc
+#define AN8811HB_MCU_SW_START_EN BIT(16)
+
+/* MII register constants for PBUS access (PHY addr + 8) */
+#define AIR_PBUS_ADDR_HIGH 0x1c
+#define AIR_PBUS_DATA_HIGH 0x10
+
/* Led definitions */
#define EN8811H_LED_COUNT 3
@@ -254,6 +264,36 @@ static int air_phy_write_page(struct phy_device *phydev, int page)
return __phy_write(phydev, AIR_EXT_PAGE_ACCESS, page);
}
+static int __air_pbus_reg_write(struct phy_device *phydev,
+ u32 pbus_reg, u32 pbus_data)
+{
+ struct mii_bus *bus = phydev->mdio.bus;
+ int pbus_addr = phydev->mdio.addr + 8;
+ int ret;
+
+ ret = __mdiobus_write(bus, pbus_addr, AIR_EXT_PAGE_ACCESS,
+ upper_16_bits(pbus_reg));
+ if (ret < 0)
+ return ret;
+
+ ret = __mdiobus_write(bus, pbus_addr, AIR_PBUS_ADDR_HIGH,
+ (pbus_reg & GENMASK(15, 6)) >> 6);
+ if (ret < 0)
+ return ret;
+
+ ret = __mdiobus_write(bus, pbus_addr, (pbus_reg & GENMASK(5, 2)) >> 2,
+ lower_16_bits(pbus_data));
+ if (ret < 0)
+ return ret;
+
+ ret = __mdiobus_write(bus, pbus_addr, AIR_PBUS_DATA_HIGH,
+ upper_16_bits(pbus_data));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int __air_buckpbus_reg_write(struct phy_device *phydev,
u32 pbus_address, u32 pbus_data)
{
@@ -570,10 +610,65 @@ static int an8811hb_load_file(struct phy_device *phydev, const char *name,
return ret;
}
+static int an8811hb_mcu_assert(struct phy_device *phydev)
+{
+ int ret;
+
+ phy_lock_mdio_bus(phydev);
+
+ ret = __air_pbus_reg_write(phydev, AN8811HB_MCU_SW_RST,
+ AN8811HB_MCU_SW_RST_HOLD);
+ if (ret < 0)
+ goto unlock;
+
+ ret = __air_pbus_reg_write(phydev, AN8811HB_MCU_SW_START, 0);
+ if (ret < 0)
+ goto unlock;
+
+ msleep(50);
+ phydev_dbg(phydev, "MCU asserted\n");
+
+unlock:
+ phy_unlock_mdio_bus(phydev);
+ return ret;
+}
+
+static int an8811hb_mcu_deassert(struct phy_device *phydev)
+{
+ int ret;
+
+ phy_lock_mdio_bus(phydev);
+
+ ret = __air_pbus_reg_write(phydev, AN8811HB_MCU_SW_START,
+ AN8811HB_MCU_SW_START_EN);
+ if (ret < 0)
+ goto unlock;
+
+ ret = __air_pbus_reg_write(phydev, AN8811HB_MCU_SW_RST,
+ AN8811HB_MCU_SW_RST_RUN);
+ if (ret < 0)
+ goto unlock;
+
+ msleep(50);
+ phydev_dbg(phydev, "MCU deasserted\n");
+
+unlock:
+ phy_unlock_mdio_bus(phydev);
+ return ret;
+}
+
static int an8811hb_load_firmware(struct phy_device *phydev)
{
int ret;
+ ret = an8811hb_mcu_assert(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = an8811hb_mcu_deassert(phydev);
+ if (ret < 0)
+ return ret;
+
ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
EN8811H_FW_CTRL_1_START);
if (ret < 0)
@@ -662,6 +757,16 @@ static int en8811h_restart_mcu(struct phy_device *phydev)
{
int ret;
+ if (phy_id_compare_model(phydev->phy_id, AN8811HB_PHY_ID)) {
+ ret = an8811hb_mcu_assert(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = an8811hb_mcu_deassert(phydev);
+ if (ret < 0)
+ return ret;
+ }
+
ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
EN8811H_FW_CTRL_1_START);
if (ret < 0)
--
2.34.1
^ permalink raw reply related
* Re: [PATCH net v3] bnxt_en: validate firmware backing store types
From: Michael Chan @ 2026-03-26 15:31 UTC (permalink / raw)
To: Pengpeng Hou
Cc: pavan.chebbi, andrew+netdev, davem, edumazet, kuba, pabeni,
netdev, linux-kernel
In-Reply-To: <20260326142033.82313-1-pengpeng@iscas.ac.cn>
[-- Attachment #1: Type: text/plain, Size: 1633 bytes --]
On Thu, Mar 26, 2026 at 7:21 AM Pengpeng Hou <pengpeng@iscas.ac.cn> wrote:
> @@ -8708,12 +8709,21 @@ static int bnxt_hwrm_func_backing_store_qcaps_v2(struct bnxt *bp)
> entry_size = le16_to_cpu(resp->entry_size);
> max_entries = le32_to_cpu(resp->max_num_entries);
> if (ctxm->mem_valid) {
> - if (!(flags & BNXT_CTX_MEM_PERSIST) ||
> - ctxm->entry_size != entry_size ||
> - ctxm->max_entries != max_entries)
> - bnxt_free_one_ctx_mem(bp, ctxm, true);
> - else
> + if ((flags & BNXT_CTX_MEM_PERSIST) &&
> + ctxm->entry_size == entry_size &&
> + ctxm->max_entries == max_entries) {
> + type = next_type;
> continue;
> + }
> +
> + bnxt_free_one_ctx_mem(bp, ctxm, true);
> + }
> + if (le16_to_cpu(resp->type) >= BNXT_CTX_V2_MAX) {
The type in the FW response (resp->type) is defined to be the type in
the FW input message (req->type). If you want to validate it, it
should be equal to the loop variable type or req->type. Thanks.
> + netdev_warn(bp->dev,
> + "invalid backing store type %u returned by firmware\n",
> + le16_to_cpu(resp->type));
> + rc = -EINVAL;
> + goto ctx_done;
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5469 bytes --]
^ permalink raw reply
* Re: [PATCH net-next v8 4/4] tun/tap & vhost-net: avoid ptr_ring tail-drop when a qdisc is present
From: Simon Schippers @ 2026-03-26 15:30 UTC (permalink / raw)
To: Jason Wang
Cc: willemdebruijn.kernel, andrew+netdev, davem, edumazet, kuba,
pabeni, mst, eperezma, leiyang, stephen, jon, tim.gebauer, netdev,
linux-kernel, kvm, virtualization
In-Reply-To: <CACGkMEuLqxnz=GtdKp3-u0egkGT_eZUgBBwvvtHcgAYpLJv-UA@mail.gmail.com>
On 3/26/26 03:41, Jason Wang wrote:
> On Wed, Mar 25, 2026 at 10:48 PM Simon Schippers
> <simon.schippers@tu-dortmund.de> wrote:
>>
>> On 3/24/26 11:14, Simon Schippers wrote:
>>> On 3/24/26 02:47, Jason Wang wrote:
>>>> On Thu, Mar 12, 2026 at 9:07 PM Simon Schippers
>>>> <simon.schippers@tu-dortmund.de> wrote:
>>>>>
>>>>> This commit prevents tail-drop when a qdisc is present and the ptr_ring
>>>>> becomes full. Once an entry is successfully produced and the ptr_ring
>>>>> reaches capacity, the netdev queue is stopped instead of dropping
>>>>> subsequent packets.
>>>>>
>>>>> If producing an entry fails anyways due to a race, tun_net_xmit returns
>>>>> NETDEV_TX_BUSY, again avoiding a drop. Such races are expected because
>>>>> LLTX is enabled and the transmit path operates without the usual locking.
>>>>>
>>>>> The existing __tun_wake_queue() function wakes the netdev queue. Races
>>>>> between this wakeup and the queue-stop logic could leave the queue
>>>>> stopped indefinitely. To prevent this, a memory barrier is enforced
>>>>> (as discussed in a similar implementation in [1]), followed by a recheck
>>>>> that wakes the queue if space is already available.
>>>>>
>>>>> If no qdisc is present, the previous tail-drop behavior is preserved.
>>>>
>>>> I wonder if we need a dedicated TUN flag to enable this. With this new
>>>> flag, we can even prevent TUN from using noqueue (not sure if it's
>>>> possible or not).
>>>>
>>>
>>> Except of the slight regressions because of this patchset I do not see
>>> a reason for such a flag.
>>>
>>> I have never seen that the driver prevents noqueue. For example you can
>>> set noqueue to your ethernet interface and under load you soon get
>>>
>>> net_crit_ratelimited("Virtual device %s asks to queue packet!\n",
>>> dev->name);
>>>
>>> followed by a -ENETDOWN. And this is not prevented even though it is
>>> clearly not something a user wants.
>>>
>>>>>
>>>>> Benchmarks:
>>>>> The benchmarks show a slight regression in raw transmission performance,
>>>>> though no packets are lost anymore.
>>>>>
>>>>> The previously introduced threshold to only wake after the queue stopped
>>>>> and half of the ring was consumed showed to be a descent choice:
>>>>> Waking the queue whenever a consume made space in the ring strongly
>>>>> degrades performance for tap, while waking only when the ring is empty
>>>>> is too late and also hurts throughput for tap & tap+vhost-net.
>>>>> Other ratios (3/4, 7/8) showed similar results (not shown here), so
>>>>> 1/2 was chosen for the sake of simplicity for both tun/tap and
>>>>> tun/tap+vhost-net.
>>>>>
>>>>> Test setup:
>>>>> AMD Ryzen 5 5600X at 4.3 GHz, 3200 MHz RAM, isolated QEMU threads;
>>>>> Average over 20 runs @ 100,000,000 packets. SRSO and spectre v2
>>>>> mitigations disabled.
>>>>>
>>>>> Note for tap+vhost-net:
>>>>> XDP drop program active in VM -> ~2.5x faster, slower for tap due to
>>>>> more syscalls (high utilization of entry_SYSRETQ_unsafe_stack in perf)
>>>>>
>>>>> +--------------------------+--------------+----------------+----------+
>>>>> | 1 thread | Stock | Patched with | diff |
>>>>> | sending | | fq_codel qdisc | |
>>>>> +------------+-------------+--------------+----------------+----------+
>>>>> | TAP | Transmitted | 1.151 Mpps | 1.139 Mpps | -1.1% |
>>>>> | +-------------+--------------+----------------+----------+
>>>>> | | Lost/s | 3.606 Mpps | 0 pps | |
>>>>> +------------+-------------+--------------+----------------+----------+
>>>>> | TAP | Transmitted | 3.948 Mpps | 3.738 Mpps | -5.3% |
>>>>> | +-------------+--------------+----------------+----------+
>>>>> | +vhost-net | Lost/s | 496.5 Kpps | 0 pps | |
>>>>> +------------+-------------+--------------+----------------+----------+
>>>>>
>>>>> +--------------------------+--------------+----------------+----------+
>>>>> | 2 threads | Stock | Patched with | diff |
>>>>> | sending | | fq_codel qdisc | |
>>>>> +------------+-------------+--------------+----------------+----------+
>>>>> | TAP | Transmitted | 1.133 Mpps | 1.109 Mpps | -2.1% |
>>>>> | +-------------+--------------+----------------+----------+
>>>>> | | Lost/s | 8.269 Mpps | 0 pps | |
>>>>> +------------+-------------+--------------+----------------+----------+
>>>>> | TAP | Transmitted | 3.820 Mpps | 3.513 Mpps | -8.0% |
>>>>> | +-------------+--------------+----------------+----------+
>>>>> | +vhost-net | Lost/s | 4.961 Mpps | 0 pps | |
>>>>> +------------+-------------+--------------+----------------+----------+
>>>>>
>>>>> [1] Link: https://lore.kernel.org/all/20250424085358.75d817ae@kernel.org/
>>>>>
>>>>> Co-developed-by: Tim Gebauer <tim.gebauer@tu-dortmund.de>
>>>>> Signed-off-by: Tim Gebauer <tim.gebauer@tu-dortmund.de>
>>>>> Signed-off-by: Simon Schippers <simon.schippers@tu-dortmund.de>
>>>>> ---
>>>>> drivers/net/tun.c | 30 ++++++++++++++++++++++++++++--
>>>>> 1 file changed, 28 insertions(+), 2 deletions(-)
>>>>>
>>>>> diff --git a/drivers/net/tun.c b/drivers/net/tun.c
>>>>> index b86582cc6cb6..9b7daec69acd 100644
>>>>> --- a/drivers/net/tun.c
>>>>> +++ b/drivers/net/tun.c
>>>>> @@ -1011,6 +1011,8 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
>>>>> struct netdev_queue *queue;
>>>>> struct tun_file *tfile;
>>>>> int len = skb->len;
>>>>> + bool qdisc_present;
>>>>> + int ret;
>>>>>
>>>>> rcu_read_lock();
>>>>> tfile = rcu_dereference(tun->tfiles[txq]);
>>>>> @@ -1063,13 +1065,37 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
>>>>>
>>>>> nf_reset_ct(skb);
>>>>>
>>>>> - if (ptr_ring_produce(&tfile->tx_ring, skb)) {
>>>>> + queue = netdev_get_tx_queue(dev, txq);
>>>>> + qdisc_present = !qdisc_txq_has_no_queue(queue);
>>>>> +
>>>>> + spin_lock(&tfile->tx_ring.producer_lock);
>>>>> + ret = __ptr_ring_produce(&tfile->tx_ring, skb);
>>>>> + if (__ptr_ring_produce_peek(&tfile->tx_ring) && qdisc_present) {
>>>>
>>>> So, it's possible that the administrator is switching between noqueue
>>>> and another qdisc. So ptr_ring_produce() can fail here, do we need to
>>>> check that or not?
>>>>
>>>
>>> Do you mean that? My thoughts:
>>>
>>> Switching from noqueue to some qdisc can cause a
>>>
>>> net_crit_ratelimited("Virtual device %s asks to queue packet!\n",
>>> dev->name);
>>>
>>> followed by a return of -ENETDOWN in __dev_queue_xmit().
>>> This is because tun_net_xmit detects some qdisc with
>>>
>>> qdisc_present = !qdisc_txq_has_no_queue(queue);
>>>
>>> and returns NETDEV_TX_BUSY even though __dev_queue_xmit() did still
>>> detect noqueue.
>>>
>>> I am not sure how to solve this/if this has to be solved.
>>> I do not see a proper way to avoid parallel execution of ndo_start_xmit
>>> and a qdisc change (dev_graft_qdisc only takes qdisc_skb_head lock).
>>>
>>> And from my understanding the veth implementation faces the same issue.
>>
>> How about rechecking if a qdisc is connected?
>> This would avoid -ENETDOWN.
>>
>> diff --git a/net/core/dev.c b/net/core/dev.c
>> index f48dc299e4b2..2731a1a70732 100644
>> --- a/net/core/dev.c
>> +++ b/net/core/dev.c
>> @@ -4845,10 +4845,17 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
>> if (is_list)
>> rc = NETDEV_TX_OK;
>> }
>> + bool qdisc_present = !qdisc_txq_has_no_queue(txq);
>> HARD_TX_UNLOCK(dev, txq);
>> if (!skb) /* xmit completed */
>> goto out;
>>
>> + /* Maybe a qdisc was connected in the meantime */
>> + if (qdisc_present) {
>> + kfree_skb(skb);
>> + goto out;
>> + }
>> +
>> net_crit_ratelimited("Virtual device %s asks to queue packet!\n",
>> dev->name);
>> /* NETDEV_TX_BUSY or queue was stopped */
>>
>
> Probably not, and we likely won't hit this warning because qdisc could
> not be changed during ndo_start_xmit().
Okay.
>
> I meant something like this:
>
> 1) set noqueue to tuntap
> 2) produce packets so tuntap is full
> 3) set e.g fq_codel to tuntap
> 4) then we can hit the failure of __ptr_ring_produce()
>
> Rethink of the code, it looks just fine.
Yes, in this case it just returns NETDEV_TX_BUSY which is fine with a
qdisc attached.
>
>>
>>>
>>>
>>> Switching from some qdisc to noqueue is no problem I think.
>>>
>>>>> + netif_tx_stop_queue(queue);
>>>>> + /* Avoid races with queue wake-ups in __tun_wake_queue by
>>>>> + * waking if space is available in a re-check.
>>>>> + * The barrier makes sure that the stop is visible before
>>>>> + * we re-check.
>>>>> + */
>>>>> + smp_mb__after_atomic();
>>>>
>>>> Let's document which barrier is paired with this.
>>>>
>>>
>>> I am basically copying the (old) logic of veth [1] proposed by
>>> Jakub Kicinski. I must admit I am not 100% sure what it pairs with.
>>>
>>> [1] Link: https://lore.kernel.org/all/20250424085358.75d817ae@kernel.org/
>
> So it looks like it implicitly tries to pair with tun_ring_consume():
>
> 1) spinlock(consumer_lock)
> 2) store NULL to ptr_ring // STORE
> 3) spinunlock(consumer_lock) // RELEASE
> 4) spinlock(consumer_lock) // ACQURE
> 5) check empty
> 6) spinunlock(consumer_lock)
> 7) netif_wakeup_queue() // test_and_set() which is an RMW
>
> RELEASE + ACQUIRE implies a full barrier
Thanks.
>
> I see several problems
>
> 1) Due to batch consumption, we may get spurious wakeups under heavy
> load (we can try disabling batch consuming to see if it helps).
I assume that you mean the waking in the recheck of the producer happens
too often and then wakes too often. But this would just take slightly
more producer cpu as the SOFTIRQ runs on the producer cpu and not slow
down the consumer?
Why would disabling batch consume help here?
Wouldn't it just decrease the consumer speed?
Apart from that I do not see a different method to do this recheck.
The ring producer is only safely able to do a !produce_peek (so a check
for !full).
The normal waking (after consuming half of the ring) should be fine IMO.
> 2) So the barriers don't help but would slow down the consuming
> 3) Two spinlocks were used instead of one, this is another reason we
> will see a performance regression
You are right, I can change it to a single spin_lock. Apart from that
I do not see how the barriers/locking could be reduced further.
> 4) Tricky code that needs to be understood or at least requires a comment tweak.
>
> Note that due to ~IFF_TX_SKB_SHARING, pktgen can't clone skbs, so we
> may not notice the real degradation.
So run pktgen with pg_set SHARED? I am pretty sure that the vhost
thread was always at 100% CPU so pktgen was not the bottleneck. And when
I had perf enabled I always saw that in my patched version not the
creation of SKB's took most CPU in pktgen but a different unnamed
function (I assume this is a waiting function).
Thank you!
>
>>>
>>>>> + if (!__ptr_ring_produce_peek(&tfile->tx_ring))
>>>>> + netif_tx_wake_queue(queue);
>>>>> + }
>>>>> + spin_unlock(&tfile->tx_ring.producer_lock);
>>>>> +
>>>>> + if (ret) {
>>>>> + /* If a qdisc is attached to our virtual device,
>>>>> + * returning NETDEV_TX_BUSY is allowed.
>>>>> + */
>>>>> + if (qdisc_present) {
>>>>> + rcu_read_unlock();
>>>>> + return NETDEV_TX_BUSY;
>>>>> + }
>>>>> drop_reason = SKB_DROP_REASON_FULL_RING;
>>>>> goto drop;
>>>>> }
>>>>>
>>>>> /* dev->lltx requires to do our own update of trans_start */
>>>>> - queue = netdev_get_tx_queue(dev, txq);
>>>>> txq_trans_cond_update(queue);
>>>>>
>>>>> /* Notify and wake up reader process */
>>>>> --
>>>>> 2.43.0
>>>>>
>>>>
>>>> Thanks
>>>>
>>
>
> Thanks
>
^ permalink raw reply
* Re: [PATCH] netfilter: ctnetlink: use netlink policy range checks
From: patchwork-bot+netdevbpf @ 2026-03-26 15:30 UTC (permalink / raw)
To: David Carlier; +Cc: pablo, fw, phil, edumazet, netdev
In-Reply-To: <20260324171259.318041-1-devnexen@gmail.com>
Hello:
This patch was applied to netdev/net.git (main)
by Pablo Neira Ayuso <pablo@netfilter.org>:
On Tue, 24 Mar 2026 17:12:59 +0000 you wrote:
> Replace manual range and mask validations with netlink policy
> annotations in ctnetlink code paths, so that the netlink core rejects
> invalid values early and can generate extack errors.
>
> - CTA_PROTOINFO_TCP_STATE: reject values > TCP_CONNTRACK_SYN_SENT2 at
> policy level, removing the manual >= TCP_CONNTRACK_MAX check.
> - CTA_PROTOINFO_TCP_WSCALE_ORIGINAL/REPLY: reject values > TCP_MAX_WSCALE
> (14). The normal TCP option parsing path already clamps to this value,
> but the ctnetlink path accepted 0-255, causing undefined behavior when
> used as a u32 shift count.
> - CTA_FILTER_ORIG_FLAGS/REPLY_FLAGS: use NLA_POLICY_MASK with
> CTA_FILTER_F_ALL, removing the manual mask checks.
> - CTA_EXPECT_FLAGS: use NLA_POLICY_MASK with NF_CT_EXPECT_MASK, adding
> a new mask define grouping all valid expect flags.
>
> [...]
Here is the summary with links:
- netfilter: ctnetlink: use netlink policy range checks
https://git.kernel.org/netdev/net/c/8f15b5071b45
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net 01/12] netfilter: nft_set_pipapo_avx2: don't return non-matching entry on expiry
From: patchwork-bot+netdevbpf @ 2026-03-26 15:30 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: netfilter-devel, davem, netdev, kuba, pabeni, edumazet, fw, horms
In-Reply-To: <20260326125153.685915-2-pablo@netfilter.org>
Hello:
This series was applied to netdev/net.git (main)
by Pablo Neira Ayuso <pablo@netfilter.org>:
On Thu, 26 Mar 2026 13:51:42 +0100 you wrote:
> From: Florian Westphal <fw@strlen.de>
>
> New test case fails unexpectedly when avx2 matching functions are used.
>
> The test first loads a ranomly generated pipapo set
> with 'ipv4 . port' key, i.e. nft -f foo.
>
> [...]
Here is the summary with links:
- [net,01/12] netfilter: nft_set_pipapo_avx2: don't return non-matching entry on expiry
https://git.kernel.org/netdev/net/c/d3c0037ffe12
- [net,02/12] selftests: netfilter: nft_concat_range.sh: add check for flush+reload bug
https://git.kernel.org/netdev/net/c/6caefcd9491c
- [net,03/12] netfilter: nfnetlink_log: fix uninitialized padding leak in NFULA_PAYLOAD
https://git.kernel.org/netdev/net/c/52025ebaa29f
- [net,04/12] netfilter: ip6t_rt: reject oversized addrnr in rt_mt6_check()
https://git.kernel.org/netdev/net/c/9d3f027327c2
- [net,05/12] netfilter: nft_set_rbtree: revisit array resize logic
https://git.kernel.org/netdev/net/c/fafdd92b9e30
- [net,06/12] netfilter: nf_conntrack_expect: honor expectation helper field
https://git.kernel.org/netdev/net/c/9c42bc9db90a
- [net,07/12] netfilter: nf_conntrack_expect: use expect->helper
https://git.kernel.org/netdev/net/c/f01794106042
- [net,08/12] netfilter: ctnetlink: ensure safe access to master conntrack
https://git.kernel.org/netdev/net/c/bffcaad9afdf
- [net,09/12] netfilter: nf_conntrack_expect: store netns and zone in expectation
https://git.kernel.org/netdev/net/c/02a3231b6d82
- [net,10/12] netfilter: nf_conntrack_expect: skip expectations in other netns via proc
https://git.kernel.org/netdev/net/c/3db5647984de
- [net,11/12] netfilter: nf_conntrack_sip: fix use of uninitialized rtp_addr in process_sdp
https://git.kernel.org/netdev/net/c/6a2b724460cb
- [net,12/12] netfilter: ctnetlink: use netlink policy range checks
https://git.kernel.org/netdev/net/c/8f15b5071b45
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net-next v2 4/4] net: phy: Introduce Airoha AN8801/R Gigabit Ethernet PHY driver
From: Russell King (Oracle) @ 2026-03-26 15:26 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, Louis-Alexis Eyraud, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, AngeloGioacchino Del Regno, Heiner Kallweit,
kevin-kw.huang, macpaul.lin, matthias.bgg, kernel, netdev,
devicetree, linux-arm-kernel, linux-mediatek, linux-kernel
In-Reply-To: <044110c5-da1e-48c0-93fd-35553e86b271@lunn.ch>
On Thu, Mar 26, 2026 at 04:24:23PM +0100, Andrew Lunn wrote:
> On Thu, Mar 26, 2026 at 03:13:19PM +0000, Russell King (Oracle) wrote:
> > On Thu, Mar 26, 2026 at 01:04:15PM +0100, Louis-Alexis Eyraud wrote:
> > > +static int an8801r_set_wol(struct phy_device *phydev,
> > > + struct ethtool_wolinfo *wol)
> > > +{
> > > + struct net_device *attach_dev = phydev->attached_dev;
> > > + const unsigned char *macaddr = attach_dev->dev_addr;
> >
> > This isn't a criticism for this patch, but a discussion point for
> > phylib itself.
> >
> > It occurs to me that there's a weakness in the phylib interface for WoL,
> > specifically when WoL is enabled at the PHY, but someone then changes
> > the interface's MAC address - there doesn't seem to be a way for the
> > address programmed into the PHY to be updated. Should there be?
> >
> > Do we instead expect users to disable WoL before changing the MAC for
> > a network interface?
>
> Program the MAC address during suspend? I assume userspace is no
> longer active at this point, so the address should be stable.
What is the timing requirements for a system going into suspend vs a WoL
packet being sent? Should a WoL packet abort entry into suspend? If yes,
then we need to program the MAC before the PHY is suspended, because
suspend could already be in progress.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply
* Re: [PATCH net-next v2 4/4] net: phy: Introduce Airoha AN8801/R Gigabit Ethernet PHY driver
From: Andrew Lunn @ 2026-03-26 15:24 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Andrew Lunn, Louis-Alexis Eyraud, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, AngeloGioacchino Del Regno, Heiner Kallweit,
kevin-kw.huang, macpaul.lin, matthias.bgg, kernel, netdev,
devicetree, linux-arm-kernel, linux-mediatek, linux-kernel
In-Reply-To: <acVND6DdpM9czIwU@shell.armlinux.org.uk>
On Thu, Mar 26, 2026 at 03:13:19PM +0000, Russell King (Oracle) wrote:
> On Thu, Mar 26, 2026 at 01:04:15PM +0100, Louis-Alexis Eyraud wrote:
> > +static int an8801r_set_wol(struct phy_device *phydev,
> > + struct ethtool_wolinfo *wol)
> > +{
> > + struct net_device *attach_dev = phydev->attached_dev;
> > + const unsigned char *macaddr = attach_dev->dev_addr;
>
> This isn't a criticism for this patch, but a discussion point for
> phylib itself.
>
> It occurs to me that there's a weakness in the phylib interface for WoL,
> specifically when WoL is enabled at the PHY, but someone then changes
> the interface's MAC address - there doesn't seem to be a way for the
> address programmed into the PHY to be updated. Should there be?
>
> Do we instead expect users to disable WoL before changing the MAC for
> a network interface?
Program the MAC address during suspend? I assume userspace is no
longer active at this point, so the address should be stable.
Andrew
^ permalink raw reply
* [GIT PULL] wireless-next-2026-03-26
From: Johannes Berg @ 2026-03-26 15:18 UTC (permalink / raw)
To: netdev; +Cc: linux-wireless
Hi,
So couple more things for now, including the NAN (Neighbor
Awareness Networking) APIs that we spent a lot of time on
trying to get right :) Other than that changes all over.
We'll surely have more changes for NAN and also ranging
(for proximity detection) is coming, it's looking to be
a big 7.1 from a wifi perspective, but we'll see how it
all comes together.
Please pull and let us know if there's any problem.
Thanks,
johannes
The following changes since commit 9ac76f3d0bb2940db3a9684d596b9c8f301ef315:
Merge tag 'wireless-next-2026-03-19' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next (2026-03-19 15:30:20 +0100)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git tags/wireless-next-2026-03-26
for you to fetch changes up to 7dd6f81f4ef801b57f6ce7b0eee32aef5c488538:
wifi: mac80211: ignore reserved bits in reconfiguration status (2026-03-25 21:22:02 +0100)
----------------------------------------------------------------
A fairly big set of changes all over, notably with:
- cfg80211: new APIs for NAN (Neighbor Aware Networking,
aka Wi-Fi Aware) so less work must be in firmware
- mt76:
- mt7996/mt7925 MLO fixes/improvements
- mt7996 NPU support (HW eth/wifi traffic offload)
- iwlwifi: UNII-9 and continuing UHR work
----------------------------------------------------------------
Aaradhana Sahu (1):
wifi: ath12k: Use .mbn firmware for AHB devices
Allen Ye (1):
wifi: mt76: fix backoff fields and max_power calculation
Alok Tiwari (1):
wifi: mt76: mt7996: fix FCS error flag check in RX descriptor
Avinash Bhatt (3):
wifi: iwlwifi: add CQM event support for per-link RSSI changes
wifi: iwlwifi: handle NULL/ERR returns from ptp_clock_register()
wifi: iwlwifi: mld: block EMLSR during TDLS connections
Avraham Stern (1):
wifi: cfg80211: allow protected action frame TX for NAN
Benjamin Berg (1):
wifi: mac80211: ignore reserved bits in reconfiguration status
Chad Monroe (5):
wifi: mt76: fix deadlock in remain-on-channel
wifi: mt76: mt7996: reset device after MCU message timeout
wifi: mt76: mt7996: increase txq memory limit to 32 MiB
wifi: mt76: fix multi-radio on-channel scanning
wifi: mt76: support upgrading passive scans to active
Christian Hewitt (1):
wifi: mt7601u: check multiple firmware paths
Colin Ian King (1):
wifi: mt76: mt7996: Fix spelling mistake "retriving" -> "retrieving"
Daniel Gabay (1):
wifi: cfg80211: allow ToDS=0/FromDS=0 data frames on NAN data interfaces
David Bauer (2):
wifi: mt76: mt76x02: wake queues after reconfig
wifi: mt76: don't return TXQ when exceeding max non-AQL packets
Duoming Zhou (2):
wifi: mt76: mt7915: fix use-after-free bugs in mt7915_mac_dump_work()
wifi: mt76: mt7996: fix use-after-free bugs in mt7996_mac_dump_work()
Emmanuel Grumbach (28):
wifi: cfg80211: support UNII-9 channels in ieee80211_channel_to_freq_khz
wifi: iwlwifi: mld: add support for iwl_mcc_allowed_ap_type_cmd v2
wifi: iwlwifi: ensure we don't read SAR values past the limit
wifi: iwlwifi: uefi: decouple UEFI and firmware APIs
wifi: iwlwifi: acpi: better use ARRAY_SIZE than a define
wifi: iwlwifi: uefi: open code the PPAG table store operation
wifi: iwlwifi: bring iwl_fill_ppag_table to the iwlmvm
wifi: iwlwifi: regulatory: support a new command for PPAG
wifi: iwlwifi: acpi: check the size of the ACPI PPAG tables
wifi: iwlwifi: acpi: add support for PPAG rev5
wifi: iwlwifi: uefi: add support for PPAG table rev5
wifi: iwlwifi: mvm: zero iwl_geo_tx_power_profiles_cmd before sending
wifi: iwlwifi: uefi: support the new WRDS and EWRD tables
wifi: iwlwifi: acpi: add support for WRDS rev 3 table
wifi: iwlwifi: acpi: add support for EWRD rev 3 table
wifi: iwlwifi: mld: support version 11 of REDUCE_TX_POWER_CMD
wifi: iwlwifi: uefi: open code the parsing of the WGDS table
wifi: iwlwifi: uefi: add support for WGDS rev4
wifi: iwlwifi: acpi: validate the WGDS table
wifi: iwlwifi: acpi: add support for WGDS revision 4
wifi: iwlwifi: support PER_CHAIN_LIMIT_OFFSET_CMD v6
wifi: iwlwifi: uefi: mode the comments valid kerneldoc comments
wifi: iwlwifi: remove IWL_MAX_WD_TIMEOUT
wifi: iwlwifi: mld: remove SCAN_TIMEOUT_MSEC
wifi: iwlwifi: mld: update the TLC when we deactivate a link
wifi: iwlwifi: TLC_MNG_CONFIG_CMD can use several structures
wifi: iwlwifi: fix the description of SESSION_PROTECTION_CMD
wifi: iwlwifi: reduce the number of prints upon firmware crash
Felix Fietkau (9):
wifi: mt76: add offchannel check to mt76_roc_complete
wifi: mt76: check chanctx before restoring channel after ROC
wifi: mt76: abort ROC on chanctx changes
wifi: mt76: optimize ROC for same-channel case
wifi: mt76: send nullfunc PS frames on offchannel transitions
wifi: mt76: flush pending TX before channel switch
wifi: mt76: route nullfunc frames to PSD/ALTX queue
wifi: mt76: wait for firmware TX completion of mgmt frames before channel switch
wifi: mt76: add per-link beacon monitoring for MLO
Gustavo A. R. Silva (1):
wifi: ath6kl: wmi: Avoid -Wflex-array-member-not-at-end warning
Howard Hsu (1):
wifi: mt76: mt7996: support critical packet mode for MT7990 chipsets
Ilan Peer (4):
wifi: cfg80211: Add support for additional 7 GHz channels
wifi: iwlwifi: mld: Refactor scan command handling
wifi: iwlwifi: mld: Introduce scan command version 18
wifi: ieee80211: Add some missing NAN definitions
Jeff Johnson (2):
wifi: ath12k: Clean up the WMI Unit Test command interface
wifi: ath12k: Remove unused DFS Unit Test definitions
Johan Hovold (5):
wifi: ath6kl: drop redundant device reference
wifi: ath6kl: rename disconnect callback
wifi: ath9k: drop redundant device reference
wifi: ath10k: drop redundant device reference
wifi: ath10k: rename disconnect callback
Johannes Berg (17):
Merge tag 'ath-next-20260324' of git://git.kernel.org/pub/scm/linux/kernel/git/ath/ath
wifi: mac80211_hwsim: advertise basic UHR support
Merge tag 'mt76-next-2026-03-23' of https://github.com/nbd168/wireless
wifi: iwlwifi: restrict TOP reset to some devices
wifi: iwlwifi: mld: enable UHR in TLC
wifi: iwlwifi: mld: set UHR MCS in RX status
wifi: iwlwifi: mld: support changing iftype at runtime
wifi: iwlwifi: use IWL_FW_CHECK for sync timeout
wifi: iwlwifi: pcie: don't dump on reset handshake in dump
wifi: iwlwifi: mld: make iwl_mld_mac80211_iftype_to_fw() static
wifi: iwlwifi: mld: remove type argument from iwl_mld_add_sta()
wifi: iwlwifi: mld: rename iwl_mld_phy_from_mac80211() argument
wifi: iwlwifi: mld: make alloc functions not forced static
wifi: iwlwifi: mld: add double-include guards to nan.h
wifi: iwlwifi: add MAC context command version 4
wifi: iwlwifi: mld: set RX_FLAG_RADIOTAP_TLV_AT_END generically
Merge tag 'iwlwifi-next-2026-03-25' of https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next into wireless-next
Kees Cook (1):
wifi: mac80211: Replace strncpy() with strscpy_pad() in drv_switch_vif_chanctx tracepoint
Leon Yen (5):
wifi: mt76: mt7925: introduce CSA support in non-MLO mode
wifi: mt76: mt7925: Fix incorrect MLO mode in firmware control
wifi: mt76: mt792x: Fix a potential deadlock in high-load situations
wifi: mt76: mt7921: fix a potential clc buffer length underflow
wifi: mt76: mt7925: fix tx power setting failure after chip reset
Lorenzo Bianconi (30):
wifi: mt76: mt7996: Set mtxq->wcid just for primary link
wifi: mt76: mt7996: Reset mtxq->idx if primary link is removed in mt7996_vif_link_remove()
wifi: mt76: mt7996: Switch to the secondary link if the default one is removed
wifi: mt76: mt7996: Clear wcid pointer in mt7996_mac_sta_deinit_link()
wifi: mt76: mt7996: Reset ampdu_state state in case of failure in mt7996_tx_check_aggr()
wifi: mt76: Fix memory leak destroying device
wifi: mt76: mt7996: Fix NPU stop procedure
wifi: mt76: npu: Add missing rx_token_size initialization
wifi: mt76: always enable RRO queues for non-MT7992 chipset
wifi: mt76: mt7996: Fix BAND2 tx queues initialization when NPU is enabled
wifi: mt76: mt7996: Fix wdma_idx for MT7996 device if NPU is enabled
wifi: mt76: mt7996: Add mt7992_npu_txrx_offload_init routine
wifi: mt76: mt7996: Rename mt7996_npu_rxd_init() in mt7992_npu_rxd_init()
wifi: mt76: mt7996: Add NPU support for MT7990 chipset
wifi: mt76: mt7996: Integrate NPU in RRO session management
wifi: mt76: mt7996: Integrate MT7990 init configuration for NPU
wifi: mt76: mt7996: Integrate MT7990 dma configuration for NPU
wifi: mt76: mt7996: Add __mt7996_npu_hw_init routine
wifi: mt76: mt7996: Move RRO dma start in a dedicated routine
wifi: mt76: Do not reset idx for NPU tx queues during reset
wifi: mt76: mt7996: Do not schedule RRO and TxFree queues during reset for NPU
wifi: mt76: mt7996: Store DMA mapped buffer addresses in mt7996_npu_hw_init()
wifi: mt76: Enable NPU support for MT7996 devices
wifi: mt76: mt7996: Add missing CHANCTX_STA_CSA property
wifi: mt76: mt7996: Remove link pointer dependency in mt7996_mac_sta_remove_links()
wifi: mt76: mt7996: Remove unnecessary phy filed in mt7996_vif_link struct
wifi: mt76: mt7996: Decrement sta counter removing the link in mt7996_mac_reset_sta_iter()
wifi: mt76: mt7996: Rely on msta_link link_id in mt7996_vif_link_remove()
wifi: mt76: mt7996: Destroy vif active links in mt7996_remove_interface()
wifi: mt76: mt7996: Destroy active sta links in mt7996_mac_sta_remove()
Madhur Kumar (1):
wifi: mt76: mt7921: Replace deprecated PCI function
Manish Dharanenthiran (1):
wifi: ath12k: Fix the assignment of logical link index
Marco Crivellari (3):
wifi: iwlwifi: replace use of system_unbound_wq with system_dfl_wq
wifi: iwlwifi: fw: replace use of system_unbound_wq with system_dfl_wq
wifi: iwlwifi: mvm: replace use of system_wq with system_percpu_wq
MeiChia Chiu (1):
wifi: mt76: mt7996: Add eMLSR support
Michael Lo (2):
wifi: mt76: mt7925: Skip scan process during suspend.
wifi: mt76: mt7921: fix 6GHz regulatory update on connection
Ming Yen Hsieh (3):
wifi: mt76: mt7925: fix incorrect length field in txpower command
wifi: mt76: mt7925: prevent NULL pointer dereference in mt7925_tx_check_aggr()
wifi: mt76: mt7925: prevent NULL vif dereference in mt7925_mac_write_txwi
Miri Korenblit (22):
wifi: mac80211: use for_each_chanctx_user_* in one more place
wifi: mac80211: make ieee80211_find_chanctx link-unaware
wifi: mac80211: properly handle error in ieee80211_add_virtual_monitor
wifi: mac80211: don't consider the sband when processing capabilities
wifi: iwlwifi: mld: add support for sta command version 3
wifi: iwlwifi: bump core version for BZ/SC/DR
wifi: iwlwifi: validate the channels received in iwl_mcc_update_resp_v*
wifi: iwlwifi: mld: use the dedicated helper to extract a link
wifi: iwlwifi: mld: always assign a fw id to a vif
wifi: iwlwifi: add a macro for max FW links
wifi: iwlwifi: mld: introduce iwl_mld_vif_fw_id_valid
wifi: mac80211: extract channel logic from link logic
wifi: mac80211: cleanup error path of ieee80211_do_open
wifi: cfg80211: Add an API to configure local NAN schedule
wifi: cfg80211: make sure NAN chandefs are valid
wifi: cfg80211: add support for NAN data interface
wifi: cfg80211: separately store HT, VHT and HE capabilities for NAN
wifi: nl80211: add support for NAN stations
wifi: nl80211: define an API for configuring the NAN peer's schedule
wifi: nl80211: allow reporting spurious NAN Data frames
wifi: nl80211: add NL80211_CMD_NAN_ULW_UPDATE notification
wifi: nl80211: Add a notification to notify NAN channel evacuation
Nidhish A N (1):
wifi: iwlwifi: mvm: cleanup some more MLO code
P Praneesh (1):
wifi: ath12k: Fix legacy rate mapping for monitor mode capture
Pagadala Yesu Anjaneyulu (4):
wifi: iwlwifi: mld: remove unused scan expire time constants
wifi: iwlwifi: fw: Add TLV support for BIOS revision of command
wifi: iwlwifi: mld: eliminate duplicate WIDE_ID in PPAG command handling
wifi: iwlwifi: mld: add BIOS revision compatibility check for PPAG command
Peter Chiu (3):
wifi: mt76: mt7996: fix RRO EMU configuration
wifi: mt76: mt7996: update WFSYS reset flow for MT7990 chipsets
wifi: mt76: mt7996: fix frequency separation for station STR mode
Quan Zhou (3):
wifi: mt76: mt7925: fix AMPDU state handling in mt7925_tx_check_aggr
wifi: mt76: mt7921: fix ROC abort flow interruption in mt7921_roc_work
wifi: mt76: mt7925: fix incorrect TLV length in CLC command
Rex Lu (1):
wifi: mt76: mt7996: adjust timeout value for boot-up calibration commands
Rory Little (1):
wifi: mt76: mt7921: Place upper limit on station AID
Rosen Penev (1):
wifi: b43: kzalloc + kcalloc to kzalloc_flex
Ryder Lee (4):
wifi: mt76: mt7615: fix use_cts_prot support
wifi: mt76: mt7915: fix use_cts_prot support
wifi: mt76: mt7996: add support for ERP CTS & HT protection
wifi: mt76: mt7996: Disable Rx hdr_trans in monitor mode
Sarika Sharma (1):
wifi: ath12k: account TX stats only when ACK/BA status is present
Sean Wang (36):
wifi: mt76: mt7921: Reset ampdu_state state in case of failure in mt76_connac2_tx_check_aggr()
wifi: mt76: mt7925: drop puncturing handling from BSS change path
wifi: mt76: mt7925: fix potential deadlock in mt7925_roc_abort_sync
wifi: mt76: mt7921: fix potential deadlock in mt7921_roc_abort_sync
wifi: mt76: connac: use is_connac2() to replace is_mt7921() checks
wifi: mt76: mt7921: use mt76_for_each_q_rx() in reset path
wifi: mt76: mt7921: handle MT7902 irq_map quirk with mutable copy
wifi: mt76: mt7921: add MT7902e DMA layout support
wifi: mt76: connac: mark MT7902 as hw txp devices
wifi: mt76: mt792x: add PSE handling barrier for the large MCU cmd
wifi: mt76: mt792x: ensure MCU ready before ROM patch download
wifi: mt76: mt7921: add MT7902 MCU support
wifi: mt76: mt792x: add MT7902 WFDMA prefetch configuration
wifi: mt76: mt7921: add MT7902 PCIe device support
wifi: mt76: mt7921: add MT7902 SDIO device support
wifi: mt76: mt792x: describe USB WFSYS reset with a descriptor
wifi: mt76: mt792x: fix mt7925u USB WFSYS reset handling
wifi: mt76: mt7925: pass mlink to sta_amsdu_tlv()
wifi: mt76: mt7925: pass WCID indices to bss_basic_tlv()
wifi: mt76: mt7925: pass mlink and mconf to sta_mld_tlv()
wifi: mt76: mt7925: pass mlink to mcu_sta_update()
wifi: mt76: mt7925: resolve primary mlink via def_wcid
wifi: mt76: mt7925: pass mlink to mac_link_sta_remove()
wifi: mt76: mt7925: pass mlink to sta_hdr_trans_tlv()
wifi: mt76: mt7925: validate mlink in sta_hdr_trans_tlv()
wifi: mt76: mt7925: pass mlink to wtbl_update_hdr_trans()
wifi: mt76: mt7925: pass mlink to set_link_key()
wifi: mt76: mt7925: resolve link after acquiring mt76 mutex
wifi: mt76: mt7925: pass mconf and mlink to wtbl_update_hdr_trans()
wifi: mt76: mt7925: make WCID cleanup unconditional in sta_remove_links()
wifi: mt76: mt7925: unwind WCID setup on link STA add failure
wifi: mt76: mt7925: drop WCID reinit after publish
wifi: mt76: mt7925: move WCID teardown into link_sta_remove()
wifi: mt76: mt7925: switch link STA allocation to RCU lifetime
wifi: mt76: mt7925: publish msta->link after successful link add
wifi: mt76: mt7925: host-only unwind published links on add failure
Shayne Chen (8):
wifi: mt76: mt7996: extend CSA and CCA support for MLO
wifi: mt76: mt7996: add duplicated WTBL command
wifi: mt76: mt7996: fix iface combination for different chipsets
wifi: mt76: mt7996: add variant for MT7992 chipsets
wifi: mt76: mt7996: fix wrong DMAD length when using MAC TXP
wifi: mt76: mt7996: Account active links in valid_links fields
wifi: mt76: mt7996: Move mlink deallocation in mt7996_vif_link_remove()
wifi: mt76: mt7996: Add mcu APIs to enable/disable vif links.
StanleyYP Wang (10):
wifi: mt76: mt7996: fix the behavior of radar detection
wifi: mt76: mt7996: set specific BSSINFO and STAREC commands after channel switch
wifi: mt76: mt7996: abort CCA when CSA is starting
wifi: mt76: mt7996: offload radar threshold initialization
wifi: mt76: add external EEPROM support for mt799x chipsets
wifi: mt76: mt7996: apply calibration-free data from OTP
wifi: mt76: mt7996: fix struct mt7996_mcu_uni_event
wifi: mt76: avoid to set ACK for MCU command if wait_resp is not set
wifi: mt76: mt7996: fix queue pause after scan due to wrong channel switch reason
wifi: mt76: mt7996: fix issues with manually triggered radar detection
Zac Bowling (1):
wifi: mt76: fix list corruption in mt76_wcid_cleanup
Zilin Guan (1):
wifi: mt76: Fix memory leak after mt76_connac_mcu_alloc_sta_req()
Ziyi Guo (1):
wifi: mt76: add missing lock protection in mt76_sta_state for sta_event callback
drivers/net/wireless/ath/ath10k/usb.c | 8 +-
drivers/net/wireless/ath/ath12k/ahb.h | 4 +-
drivers/net/wireless/ath/ath12k/core.h | 2 +-
drivers/net/wireless/ath/ath12k/dp_htt.c | 24 +-
drivers/net/wireless/ath/ath12k/hal.h | 31 +-
drivers/net/wireless/ath/ath12k/mac.c | 67 +-
drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c | 76 +-
drivers/net/wireless/ath/ath12k/wmi.c | 58 +-
drivers/net/wireless/ath/ath12k/wmi.h | 14 +-
drivers/net/wireless/ath/ath6kl/usb.c | 16 +-
drivers/net/wireless/ath/ath6kl/wmi.h | 11 -
drivers/net/wireless/ath/ath9k/hif_usb.c | 4 -
drivers/net/wireless/broadcom/b43/dma.c | 18 +-
drivers/net/wireless/broadcom/b43/dma.h | 4 +-
drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 2 +-
drivers/net/wireless/intel/iwlwifi/cfg/dr.c | 2 +-
drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 2 +-
drivers/net/wireless/intel/iwlwifi/fw/acpi.c | 132 ++-
drivers/net/wireless/intel/iwlwifi/fw/acpi.h | 28 +-
.../net/wireless/intel/iwlwifi/fw/api/datapath.h | 3 +-
.../net/wireless/intel/iwlwifi/fw/api/mac-cfg.h | 167 +++-
drivers/net/wireless/intel/iwlwifi/fw/api/mac.h | 6 +-
.../net/wireless/intel/iwlwifi/fw/api/nvm-reg.h | 18 +-
drivers/net/wireless/intel/iwlwifi/fw/api/power.h | 37 +-
drivers/net/wireless/intel/iwlwifi/fw/api/scan.h | 45 +
drivers/net/wireless/intel/iwlwifi/fw/api/stats.h | 5 +-
drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 4 +-
drivers/net/wireless/intel/iwlwifi/fw/dump.c | 69 +-
drivers/net/wireless/intel/iwlwifi/fw/file.h | 15 +
drivers/net/wireless/intel/iwlwifi/fw/img.c | 32 +-
drivers/net/wireless/intel/iwlwifi/fw/img.h | 8 +
drivers/net/wireless/intel/iwlwifi/fw/regulatory.c | 151 +---
drivers/net/wireless/intel/iwlwifi/fw/regulatory.h | 14 +-
drivers/net/wireless/intel/iwlwifi/fw/runtime.h | 10 +-
drivers/net/wireless/intel/iwlwifi/fw/uefi.c | 238 +++++-
drivers/net/wireless/intel/iwlwifi/fw/uefi.h | 141 +++-
drivers/net/wireless/intel/iwlwifi/iwl-config.h | 1 -
drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 23 +-
drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 9 +-
drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 10 +-
drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 20 +-
drivers/net/wireless/intel/iwlwifi/mld/constants.h | 1 -
drivers/net/wireless/intel/iwlwifi/mld/fw.c | 2 +-
drivers/net/wireless/intel/iwlwifi/mld/iface.c | 22 +-
drivers/net/wireless/intel/iwlwifi/mld/iface.h | 15 +-
drivers/net/wireless/intel/iwlwifi/mld/link.c | 2 +-
drivers/net/wireless/intel/iwlwifi/mld/link.h | 2 +
.../net/wireless/intel/iwlwifi/mld/low_latency.c | 13 +-
drivers/net/wireless/intel/iwlwifi/mld/mac80211.c | 52 +-
drivers/net/wireless/intel/iwlwifi/mld/mld.h | 6 +-
drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 4 +-
drivers/net/wireless/intel/iwlwifi/mld/nan.h | 5 +-
drivers/net/wireless/intel/iwlwifi/mld/phy.h | 4 +-
drivers/net/wireless/intel/iwlwifi/mld/power.c | 5 +-
drivers/net/wireless/intel/iwlwifi/mld/ptp.c | 4 +-
.../net/wireless/intel/iwlwifi/mld/regulatory.c | 178 +++-
.../net/wireless/intel/iwlwifi/mld/regulatory.h | 2 +-
drivers/net/wireless/intel/iwlwifi/mld/rx.c | 25 +-
drivers/net/wireless/intel/iwlwifi/mld/scan.c | 224 +++--
drivers/net/wireless/intel/iwlwifi/mld/scan.h | 2 +
drivers/net/wireless/intel/iwlwifi/mld/sta.c | 50 +-
drivers/net/wireless/intel/iwlwifi/mld/sta.h | 4 +-
drivers/net/wireless/intel/iwlwifi/mld/stats.c | 31 +-
.../net/wireless/intel/iwlwifi/mld/tests/utils.c | 8 +-
drivers/net/wireless/intel/iwlwifi/mld/tlc.c | 78 +-
drivers/net/wireless/intel/iwlwifi/mld/tlc.h | 3 +
drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 157 +++-
drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 7 +-
drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c | 46 --
drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c | 18 +-
.../net/wireless/intel/iwlwifi/mvm/mld-mac80211.c | 132 ---
drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c | 291 +------
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 5 -
drivers/net/wireless/intel/iwlwifi/mvm/ptp.c | 4 +-
drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 4 -
drivers/net/wireless/intel/iwlwifi/mvm/tdls.c | 6 +-
.../intel/iwlwifi/pcie/gen1_2/trans-gen2.c | 13 +-
.../net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c | 2 +-
drivers/net/wireless/mediatek/mt76/channel.c | 39 +-
drivers/net/wireless/mediatek/mt76/dma.c | 33 +-
drivers/net/wireless/mediatek/mt76/dma.h | 4 +-
drivers/net/wireless/mediatek/mt76/eeprom.c | 154 +++-
drivers/net/wireless/mediatek/mt76/mac80211.c | 230 +++++-
drivers/net/wireless/mediatek/mt76/mcu.c | 2 +-
drivers/net/wireless/mediatek/mt76/mt76.h | 47 +-
drivers/net/wireless/mediatek/mt76/mt7615/mac.c | 15 -
drivers/net/wireless/mediatek/mt76/mt7615/main.c | 7 +-
drivers/net/wireless/mediatek/mt76/mt7615/mcu.c | 47 ++
drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h | 5 +-
drivers/net/wireless/mediatek/mt76/mt7615/regs.h | 2 -
drivers/net/wireless/mediatek/mt76/mt76_connac.h | 11 +-
.../net/wireless/mediatek/mt76/mt76_connac_mac.c | 28 +-
.../net/wireless/mediatek/mt76/mt76_connac_mcu.c | 46 +-
.../net/wireless/mediatek/mt76/mt76_connac_mcu.h | 15 +-
drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c | 1 +
drivers/net/wireless/mediatek/mt76/mt7915/init.c | 1 +
drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 13 -
drivers/net/wireless/mediatek/mt76/mt7915/main.c | 9 +-
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 66 +-
drivers/net/wireless/mediatek/mt76/mt7915/mcu.h | 11 +
drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h | 4 +
drivers/net/wireless/mediatek/mt76/mt7921/init.c | 4 +-
drivers/net/wireless/mediatek/mt76/mt7921/main.c | 29 +-
drivers/net/wireless/mediatek/mt76/mt7921/mcu.c | 3 +
drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h | 16 +
drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 70 +-
.../net/wireless/mediatek/mt76/mt7921/pci_mac.c | 6 +-
drivers/net/wireless/mediatek/mt76/mt7921/sdio.c | 4 +
drivers/net/wireless/mediatek/mt76/mt7925/init.c | 2 +
drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 18 +-
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 394 +++++++--
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 194 +++--
drivers/net/wireless/mediatek/mt76/mt7925/mcu.h | 7 +
drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h | 13 +-
drivers/net/wireless/mediatek/mt76/mt7925/regd.c | 3 +-
drivers/net/wireless/mediatek/mt76/mt792x.h | 7 +
drivers/net/wireless/mediatek/mt76/mt792x_core.c | 14 +-
drivers/net/wireless/mediatek/mt76/mt792x_dma.c | 18 +-
drivers/net/wireless/mediatek/mt76/mt792x_mac.c | 2 +-
drivers/net/wireless/mediatek/mt76/mt792x_regs.h | 6 +
drivers/net/wireless/mediatek/mt76/mt792x_usb.c | 51 +-
.../net/wireless/mediatek/mt76/mt7996/debugfs.c | 36 +-
drivers/net/wireless/mediatek/mt76/mt7996/dma.c | 208 +++--
drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c | 64 +-
drivers/net/wireless/mediatek/mt76/mt7996/init.c | 110 ++-
drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 161 ++--
drivers/net/wireless/mediatek/mt76/mt7996/mac.h | 5 -
drivers/net/wireless/mediatek/mt76/mt7996/main.c | 439 +++++++---
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 823 +++++++++++++++----
drivers/net/wireless/mediatek/mt76/mt7996/mcu.h | 112 ++-
drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h | 70 +-
drivers/net/wireless/mediatek/mt76/mt7996/npu.c | 469 ++++++++---
drivers/net/wireless/mediatek/mt76/mt7996/regs.h | 11 +
drivers/net/wireless/mediatek/mt76/npu.c | 37 +-
drivers/net/wireless/mediatek/mt76/scan.c | 70 +-
drivers/net/wireless/mediatek/mt76/tx.c | 34 +-
drivers/net/wireless/mediatek/mt7601u/mcu.c | 15 +-
drivers/net/wireless/mediatek/mt7601u/usb.h | 1 +
drivers/net/wireless/virtual/mac80211_hwsim.c | 35 +
include/linux/ieee80211-nan.h | 7 +-
include/linux/ieee80211.h | 7 +
include/net/cfg80211.h | 265 +++++-
include/uapi/linux/nl80211.h | 232 +++++-
net/mac80211/cfg.c | 4 +-
net/mac80211/chan.c | 199 +++--
net/mac80211/he.c | 37 +-
net/mac80211/ht.c | 6 +-
net/mac80211/ibss.c | 4 +-
net/mac80211/ieee80211_i.h | 12 +-
net/mac80211/iface.c | 28 +-
net/mac80211/mesh_plink.c | 3 +-
net/mac80211/mlme.c | 17 +-
net/mac80211/rx.c | 2 +
net/mac80211/trace.h | 5 +-
net/mac80211/util.c | 1 +
net/mac80211/vht.c | 33 +-
net/wireless/chan.c | 6 +-
net/wireless/core.c | 130 ++-
net/wireless/core.h | 10 +
net/wireless/mlme.c | 13 +-
net/wireless/nl80211.c | 905 ++++++++++++++++++++-
net/wireless/rdev-ops.h | 32 +
net/wireless/reg.c | 27 +-
net/wireless/sysfs.c | 27 +-
net/wireless/trace.h | 105 +++
net/wireless/util.c | 28 +-
166 files changed, 7131 insertions(+), 2321 deletions(-)
^ permalink raw reply
* Re: [PATCH net] net: sfp: Fix Ubiquiti U-Fiber Instant SFP module on mvneta
From: Russell King (Oracle) @ 2026-03-26 15:19 UTC (permalink / raw)
To: Marek Behún
Cc: netdev, Paolo Abeni, Jakub Kicinski, Eric Dumazet,
David S. Miller, Heiner Kallweit, Andrew Lunn, Maxime Chevallier
In-Reply-To: <20260326122038.2489589-1-kabel@kernel.org>
On Thu, Mar 26, 2026 at 01:20:38PM +0100, Marek Behún wrote:
> In commit 8110633db49d7de2 ("net: sfp-bus: allow SFP quirks to override
> Autoneg and pause bits") we moved the setting of Autoneg and pause bits
> before the call to SFP quirk when parsing SFP module support.
>
> Since the quirk for Ubiquiti U-Fiber Instant SFP module zeroes the
> support bits and sets 1000baseX_Full only, the above mentioned commit
> changed the overall computed support from
> 1000baseX_Full, Autoneg, Pause, Asym_Pause
> to just
> 1000baseX_Full.
>
> This broke the SFP module for mvneta, which requires Autoneg for
> 1000baseX since commit c762b7fac1b249a9 ("net: mvneta: deny disabling
> autoneg for 802.3z modes").
>
> Fix this by setting back the Autoneg, Pause and Asym_Pause bits in the
> quirk.
>
> Fixes: 8110633db49d7de2 ("net: sfp-bus: allow SFP quirks to override Autoneg and pause bits")
> Signed-off-by: Marek Behún <kabel@kernel.org>
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Thanks!
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply
* Re: [PATCH net-next v2 4/4] net: phy: Introduce Airoha AN8801/R Gigabit Ethernet PHY driver
From: Russell King (Oracle) @ 2026-03-26 15:13 UTC (permalink / raw)
To: Andrew Lunn
Cc: Louis-Alexis Eyraud, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, AngeloGioacchino Del Regno, Andrew Lunn,
Heiner Kallweit, kevin-kw.huang, macpaul.lin, matthias.bgg,
kernel, netdev, devicetree, linux-arm-kernel, linux-mediatek,
linux-kernel
In-Reply-To: <20260326-add-airoha-an8801-support-v2-4-1a42d6b6050f@collabora.com>
On Thu, Mar 26, 2026 at 01:04:15PM +0100, Louis-Alexis Eyraud wrote:
> +static int an8801r_set_wol(struct phy_device *phydev,
> + struct ethtool_wolinfo *wol)
> +{
> + struct net_device *attach_dev = phydev->attached_dev;
> + const unsigned char *macaddr = attach_dev->dev_addr;
This isn't a criticism for this patch, but a discussion point for
phylib itself.
It occurs to me that there's a weakness in the phylib interface for WoL,
specifically when WoL is enabled at the PHY, but someone then changes
the interface's MAC address - there doesn't seem to be a way for the
address programmed into the PHY to be updated. Should there be?
Do we instead expect users to disable WoL before changing the MAC for
a network interface?
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply
* Re: [PATCH net-next v11 12/15] quic: add crypto packet encryption and decryption
From: Xin Long @ 2026-03-26 15:10 UTC (permalink / raw)
To: network dev, quic
Cc: davem, kuba, Eric Dumazet, Paolo Abeni, Simon Horman,
Stefan Metzmacher, Moritz Buhl, Tyler Fanelli, Pengtao He,
Thomas Dreibholz, linux-cifs, Steve French, Namjae Jeon,
Paulo Alcantara, Tom Talpey, kernel-tls-handshake, Chuck Lever,
Jeff Layton, Steve Dickson, Hannes Reinecke, Alexander Aring,
David Howells, Matthieu Baerts, John Ericson, Cong Wang,
D . Wythe, Jason Baron, illiliti, Sabrina Dubroca,
Marcelo Ricardo Leitner, Daniel Stenberg, Andy Gospodarek,
Marc E . Fiuczynski
In-Reply-To: <d86910330339da257534ad93db71949176961522.1774410440.git.lucien.xin@gmail.com>
On Tue, Mar 24, 2026 at 11:49 PM Xin Long <lucien.xin@gmail.com> wrote:
>
> This patch adds core support for packet-level encryption and decryption
> using AEAD, including both payload protection and QUIC header protection.
> It introduces helpers to encrypt packets before transmission and to
> remove header protection and decrypt payloads upon reception, in line
> with QUIC's cryptographic requirements.
>
> - quic_crypto_encrypt(): Perform header protection and payload
> encryption (TX).
>
> - quic_crypto_decrypt(): Perform header protection removal and
> payload decryption (RX).
>
> The patch also includes support for Retry token handling. It provides
> helpers to compute the Retry integrity tag, generate tokens for address
> validation, and verify tokens received from clients during the
> handshake phase.
>
> - quic_crypto_get_retry_tag(): Compute tag for Retry packets.
>
> - quic_crypto_generate_token(): Generate retry token.
>
> - quic_crypto_verify_token(): Verify retry token.
>
> These additions establish the cryptographic primitives necessary for
> secure QUIC packet exchange and address validation.
>
> Signed-off-by: Xin Long <lucien.xin@gmail.com>
> ---
> v3:
> - quic_crypto_decrypt(): return -EKEYREVOKED to defer key updates to
> the workqueue when the packet is not marked backlog, since
> quic_crypto_key_update()/crypto_aead_setkey() must run in process
> context.
> - Only perform header decryption if !cb->number_len to avoid double
> decryption when a key-update packet (with flipped key_phase)
> re-enters quic_crypto_decrypt() from the workqueue.
> v4:
> - skb_dst_force() is already called in in quic_udp_rcv() on recv path,
> so remove its call from quic_crypto_payload_decrypt(), which may be
> called without RCU protection.
> - Remove the incorrect (void *) cast to quic_crypto_done.
> v5:
> - Use skb_cb->crypto_ctx for async crypto context freeing, which is
> safer than using skb_shinfo(skb)->destructor_arg.
> - skb_cb->number_max is removed and number is reused as the largest
> previously seen and update quic_crypto_get_header() accordingly.
> - Change timestamp variables from u32 to u64 and use quic_ktime_get_us()
> for current timestamps, as jiffies_to_usecs() is not accurate enough.
> v6:
> - Rename quic_crypto_get_header() to quic_crypto_get_number(), move
> key_phase parsing out of it, check cb->length when parsing packet
> number, and update all callers.
> - Use hdr->pnl + 1 instead of (*p & QUIC_PN_LEN_BITS_MASK) + 1 to get
> packet number length, and remove the unnecessary the len variable
> and QUIC_PN_LEN_BITS_MASK macro from quic_crypto_header_decrypt().
> v8:
> - Move skb_cow_data() from quic_crypto_payload_decrypt() to
> quic_crypto_header_decrypt(), as header decryption also writes to
> the skb and is invoked earlier than payload decryption on RX path.
> v10:
> - Fix double period at end of the comment for quic_crypto_encrypt().
> v11:
> - Note for AI review: call quic_crypto_aead_mem_alloc() after
> skb_cow_data() as it uses the return value for nsg. If the allocation
> fails, skb is dropped, so no rollback of prior skb modifications is
> needed.
The AI report on
https://netdev-ai.bots.linux.dev/ai-review.html?id=1624d906-c0b6-4e12-a63f-5cbfc51b660e#patch-11
is already explained by this "Note for AI review", not an issue.
^ permalink raw reply
* [PATCH 4/4] ipv6: mld: encode multicast exponential fields
From: Ujjal Roy @ 2026-03-26 15:07 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Nikolay Aleksandrov, Ido Schimmel, David Ahern
Cc: Ujjal Roy, bridge, netdev, linux-kernel
In-Reply-To: <20260326150742.50289-1-royujjal@gmail.com>
In MLD, QQIC and MRC fields are not currently encoded when
generating query packets. Since the receiver of the query
interprets these fields using the MLDv2 floating-point
decoding logic, any raw interval value that exceeds the
linear threshold is currently parsed incorrectly as an
exponential value, leading to an incorrect interval
calculation.
Encode and assign the corresponding protocol fields during
query generation. Introduce the logic to dynamically
calculate the exponent and mantissa using bit-scan (fls).
This ensures QQIC (8-bit) and MRC (16-bit) fields are
properly encoded when transmitting query packets with
intervals that exceed their respective linear thresholds
(128 for QQI; 32768 for MRD).
RFC3810: If QQIC >= 128, the QQIC field represents a
floating-point value as follows:
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|1| exp | mant |
+-+-+-+-+-+-+-+-+
RFC3810: If Maximum Response Code >= 32768, the Maximum
Response Code field represents a floating-point value as
follows:
0 1 2 3 4 5 6 7 8 9 A B C D E F
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|1| exp | mant |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Signed-off-by: Ujjal Roy <royujjal@gmail.com>
---
include/net/mld.h | 117 ++++++++++++++++++++++++++++++++++++++
net/bridge/br_multicast.c | 4 +-
2 files changed, 119 insertions(+), 2 deletions(-)
diff --git a/include/net/mld.h b/include/net/mld.h
index 6373eb76efe5..0962312d29b9 100644
--- a/include/net/mld.h
+++ b/include/net/mld.h
@@ -90,12 +90,129 @@ struct mld2_query {
#define MLDV2_QQIC_MAN(value) ((value) & 0x0f)
#define MLD_QQIC_MIN_THRESHOLD 128
+/* Max representable (mant = 0xF, exp = 7) -> 31744 */
+#define MLD_QQIC_MAX_THRESHOLD 31744
#define MLD_MRC_MIN_THRESHOLD 32768UL
+/* Max representable (mant = 0xFFF, exp = 7) -> 8387584 */
+#define MLD_MRC_MAX_THRESHOLD 8387584
#define MLDV1_MRD_MAX_COMPAT (MLD_MRC_MIN_THRESHOLD - 1)
#define MLD_MAX_QUEUE 8
#define MLD_MAX_SKBS 32
+/* V2 exponential field encoding */
+
+/*
+ * Calculate Maximum Response Code from Maximum Response Delay
+ *
+ * MLDv2 Maximum Response Code 16-bit encoding (RFC3810).
+ *
+ * RFC3810 defines only the decoding formula:
+ * Maximum Response Delay = (mant | 0x1000) << (exp + 3)
+ *
+ * but does NOT define the encoding procedure. To derive exponent:
+ *
+ * For the 16-bit MRC, the "hidden bit" (0x1000) is left shifted by 12
+ * to sit above the 12-bit mantissa. The RFC then shifts this entire
+ * block left by (exp + 3) to reconstruct the value.
+ * So, 'hidden bit' is the MSB which is shifted by (12 + exp + 3).
+ *
+ * - Total left shift of the hidden bit = 12 + (exp + 3) = exp + 15.
+ * - This is the MSB at the 0-based bit position: (exp + 15).
+ * - Since fls() is 1-based, fls(value) - 1 = exp + 15.
+ *
+ * Therefore:
+ * exp = fls(value) - 16
+ * mant = (value >> (exp + 3)) & 0x0FFF
+ *
+ * Final encoding formula:
+ * 0x8000 | (exp << 12) | mant
+ *
+ * Example (value = 1311744):
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0| 1311744
+ * | ^-^--------mant---------^ ^...(exp+3)...^| exp=5
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Encoded:
+ * 0x8000 | (5 << 12) | 0x404 = 0xD404
+ */
+static inline u16 mldv2_mrc(unsigned long mrd)
+{
+ u16 mc_man, mc_exp;
+
+ /* RFC3810: MRC < 32768 is literal */
+ if (mrd < MLD_MRC_MIN_THRESHOLD)
+ return (u16)mrd;
+
+ /* Saturate at max representable (mant = 0xFFF, exp = 7) -> 8387584 */
+ if (mrd >= MLD_MRC_MAX_THRESHOLD)
+ return 0xFFFF;
+
+ mc_exp = (u16)(fls(mrd) - 16);
+ mc_man = (u16)((mrd >> (mc_exp + 3)) & 0x0FFF);
+
+ return (0x8000 | (mc_exp << 12) | mc_man);
+}
+
+/*
+ * Calculate Querier's Query Interval Code from Query Interval
+ *
+ * MLDv2 QQIC 8-bit floating-point encoding (RFC3810).
+ *
+ * RFC3810 defines only the decoding formula:
+ * QQI = (mant | 0x10) << (exp + 3)
+ *
+ * but does NOT define the encoding procedure. To derive exponent:
+ *
+ * For any value of mantissa and exponent, the decoding formula
+ * indicates that the "hidden bit" (0x10) is shifted 4 bits left
+ * to sit above the 4-bit mantissa. The RFC again shifts this
+ * entire block left by (exp + 3) to reconstruct the value.
+ * So, 'hidden bit' is the MSB which is shifted by (4 + exp + 3).
+ *
+ * Total left shift of the 'hidden bit' = 4 + (exp + 3) = exp + 7.
+ * This is the MSB at the 0-based bit position: (exp + 7).
+ * Since fls() is 1-based, fls(value) - 1 = exp + 7.
+ *
+ * Therefore:
+ * exp = fls(value) - 8
+ * mant = (value >> (exp + 3)) & 0x0F
+ *
+ * Final encoding formula:
+ * 0x80 | (exp << 4) | mant
+ *
+ * Example (value = 3200):
+ * 0 1
+ * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0| (value = 3200)
+ * | ^-^-mant^ ^..(exp+3)..^| exp = 4, mant = 9
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Encoded:
+ * 0x80 | (4 << 4) | 9 = 0xC9
+ */
+static inline u8 mldv2_qqic(unsigned long value)
+{
+ u8 mc_man, mc_exp;
+
+ /* RFC3810: QQIC < 128 is literal */
+ if (value < MLD_QQIC_MIN_THRESHOLD)
+ return (u8)value;
+
+ /* Saturate at max representable (mant = 0xF, exp = 7) -> 31744 */
+ if (value >= MLD_QQIC_MAX_THRESHOLD)
+ return 0xFF;
+
+ mc_exp = (u8)(fls(value) - 8);
+ mc_man = (u8)((value >> (mc_exp + 3)) & 0x0F);
+
+ return (0x80 | (mc_exp << 4) | mc_man);
+}
+
/* V2 exponential field decoding */
/* Calculate Maximum Response Delay from Maximum Response Code
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 1de6242413e0..c59dab6fff4e 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -1181,7 +1181,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm
break;
case 2:
mld2q = (struct mld2_query *)icmp6_hdr(skb);
- mld2q->mld2q_mrc = htons((u16)jiffies_to_msecs(interval));
+ mld2q->mld2q_mrc = htons((u16)jiffies_to_msecs(mldv2_mrc(interval)));
mld2q->mld2q_type = ICMPV6_MGM_QUERY;
mld2q->mld2q_code = 0;
mld2q->mld2q_cksum = 0;
@@ -1190,7 +1190,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm
mld2q->mld2q_suppress = sflag;
mld2q->mld2q_qrv = 2;
mld2q->mld2q_nsrcs = htons(llqt_srcs);
- mld2q->mld2q_qqic = brmctx->multicast_query_interval / HZ;
+ mld2q->mld2q_qqic = mldv2_qqic(brmctx->multicast_query_interval / HZ);
mld2q->mld2q_mca = *group;
csum = &mld2q->mld2q_cksum;
csum_start = (void *)mld2q;
--
2.43.0
^ permalink raw reply related
* [PATCH 3/4] ipv4: igmp: encode multicast exponential fields
From: Ujjal Roy @ 2026-03-26 15:07 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Nikolay Aleksandrov, Ido Schimmel, David Ahern
Cc: Ujjal Roy, bridge, netdev, linux-kernel
In-Reply-To: <20260326150742.50289-1-royujjal@gmail.com>
In IGMP, QQIC and MRC fields are not currently encoded
when generating query packets. Since the receiver of the
query interprets these fields using the IGMPv3 floating-
point decoding logic, any raw interval value that exceeds
the linear threshold is currently parsed incorrectly as
an exponential value, leading to an incorrect interval
calculation.
Encode and assign the corresponding protocol fields during
query generation. Introduce the logic to dynamically
calculate the exponent and mantissa using bit-scan (fls).
This ensures QQIC and MRC fields (8-bit) are properly
encoded when transmitting query packets with intervals
that exceed their respective linear threshold value of
128 (for QQI/MRT).
RFC 3376: if QQIC/MRC >= 128, the QQIC/MRC field represents
a floating-point value as follows:
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|1| exp | mant |
+-+-+-+-+-+-+-+-+
Signed-off-by: Ujjal Roy <royujjal@gmail.com>
---
include/linux/igmp.h | 80 +++++++++++++++++++++++++++++++++++++++
net/bridge/br_multicast.c | 14 +++----
2 files changed, 86 insertions(+), 8 deletions(-)
diff --git a/include/linux/igmp.h b/include/linux/igmp.h
index 3c12c0a63492..99fce6b0625f 100644
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -110,6 +110,86 @@ struct ip_mc_list {
/* IGMPV3 floating-point exponential field threshold */
#define IGMPV3_EXP_MIN_THRESHOLD 128
+/* Max representable (mant = 0xF, exp = 7) -> 31744 */
+#define IGMPV3_EXP_MAX_THRESHOLD 31744
+
+/* V3 exponential field encoding */
+
+/*
+ * IGMPv3 QQIC/MRC 8-bit exponential field encode.
+ *
+ * RFC3376 defines only the decoding formula:
+ * QQI/MRT = (mant | 0x10) << (exp + 3)
+ *
+ * but does NOT define the encoding procedure. To derive exponent:
+ *
+ * For any value of mantissa and exponent, the decoding formula
+ * indicates that the "hidden bit" (0x10) is shifted 4 bits left
+ * to sit above the 4-bit mantissa. The RFC again shifts this
+ * entire block left by (exp + 3) to reconstruct the value.
+ * So, 'hidden bit' is the MSB which is shifted by (4 + exp + 3).
+ *
+ * Total left shift of the 'hidden bit' = 4 + (exp + 3) = exp + 7.
+ * This is the MSB at the 0-based bit position: (exp + 7).
+ * Since fls() is 1-based, fls(value) - 1 = exp + 7.
+ *
+ * Therefore:
+ * exp = fls(value) - 8
+ * mant = (value >> (exp + 3)) & 0x0F
+ *
+ * Final encoding formula:
+ * 0x80 | (exp << 4) | mant
+ *
+ * Example (value = 3200):
+ * 0 1
+ * 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0| (value = 3200)
+ * | ^-^-mant^ ^..(exp+3)..^| exp = 4, mant = 9
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Encoded:
+ * 0x80 | (4 << 4) | 9 = 0xC9
+ */
+static inline u8 igmpv3_exp_field_encode(unsigned long value)
+{
+ u8 mc_exp, mc_man;
+
+ /* RFC3376: QQIC/MRC < 128 is literal */
+ if (value < IGMPV3_EXP_MIN_THRESHOLD)
+ return (u8)value;
+
+ /* Saturate at max representable (mant = 0xF, exp = 7) -> 31744 */
+ if (value >= IGMPV3_EXP_MAX_THRESHOLD)
+ return 0xFF;
+
+ mc_exp = (u8)(fls(value) - 8);
+ mc_man = (u8)((value >> (mc_exp + 3)) & 0x0F);
+
+ return 0x80 | (mc_exp << 4) | mc_man;
+}
+
+/* Calculate Maximum Response Code from Max Resp Time */
+static inline u8 igmpv3_mrc(unsigned long mrt)
+{
+ /* RFC3376, relevant sections:
+ * - 4.1.1. Maximum Response Code
+ * - 8.3. Query Response Interval
+ */
+ return igmpv3_exp_field_encode(mrt);
+}
+
+/* Calculate Querier's Query Interval Code from Query Interval */
+static inline u8 igmpv3_qqic(unsigned long qi)
+{
+ /* RFC3376, relevant sections:
+ * - 4.1.7. QQIC (Querier's Query Interval Code)
+ * - 8.2. Query Interval
+ * - 8.12. Older Version Querier Present Timeout
+ * (the [Query Interval] in the last Query received)
+ */
+ return igmpv3_exp_field_encode(qi);
+}
/* V3 exponential field decoding */
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 1438c023db62..1de6242413e0 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -939,7 +939,7 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
struct sk_buff *skb;
struct igmphdr *ih;
struct ethhdr *eth;
- unsigned long lmqt;
+ unsigned long lmqt, mrt;
struct iphdr *iph;
u16 lmqt_srcs = 0;
@@ -1004,15 +1004,15 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
skb_put(skb, 24);
skb_set_transport_header(skb, skb->len);
+ mrt = group ? brmctx->multicast_last_member_interval :
+ brmctx->multicast_query_response_interval;
*igmp_type = IGMP_HOST_MEMBERSHIP_QUERY;
switch (brmctx->multicast_igmp_version) {
case 2:
ih = igmp_hdr(skb);
ih->type = IGMP_HOST_MEMBERSHIP_QUERY;
- ih->code = (group ? brmctx->multicast_last_member_interval :
- brmctx->multicast_query_response_interval) /
- (HZ / IGMP_TIMER_SCALE);
+ ih->code = mrt / (HZ / IGMP_TIMER_SCALE);
ih->group = group;
ih->csum = 0;
csum = &ih->csum;
@@ -1021,11 +1021,9 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge_mcast *brm
case 3:
ihv3 = igmpv3_query_hdr(skb);
ihv3->type = IGMP_HOST_MEMBERSHIP_QUERY;
- ihv3->code = (group ? brmctx->multicast_last_member_interval :
- brmctx->multicast_query_response_interval) /
- (HZ / IGMP_TIMER_SCALE);
+ ihv3->code = igmpv3_mrc(mrt / (HZ / IGMP_TIMER_SCALE));
ihv3->group = group;
- ihv3->qqic = brmctx->multicast_query_interval / HZ;
+ ihv3->qqic = igmpv3_qqic(brmctx->multicast_query_interval / HZ);
ihv3->nsrcs = htons(lmqt_srcs);
ihv3->resv = 0;
ihv3->suppress = sflag;
--
2.43.0
^ permalink raw reply related
* [PATCH 2/4] ipv6: mld: rename mldv2_mrc() and add mldv2_qqi()
From: Ujjal Roy @ 2026-03-26 15:07 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Nikolay Aleksandrov, Ido Schimmel, David Ahern
Cc: Ujjal Roy, bridge, netdev, linux-kernel
In-Reply-To: <20260326150742.50289-1-royujjal@gmail.com>
Rename mldv2_mrc() to mldv2_mrd() as it used to calculate
the Maximum Response Delay from the Maximum Response Code.
Introduce a new API mldv2_qqi() to define the existing
calculation logic of QQI from QQIC. This also organizes
the existing mld_update_qi() API.
Added by e3f5b1704 ("net: ipv6: mld: get rid of MLDV2_MRC and simplify calculation").
Signed-off-by: Ujjal Roy <royujjal@gmail.com>
---
include/net/mld.h | 64 +++++++++++++++++++++++++++++++++------
net/bridge/br_multicast.c | 2 +-
net/ipv6/mcast.c | 19 ++----------
3 files changed, 58 insertions(+), 27 deletions(-)
diff --git a/include/net/mld.h b/include/net/mld.h
index c07359808493..6373eb76efe5 100644
--- a/include/net/mld.h
+++ b/include/net/mld.h
@@ -89,29 +89,73 @@ struct mld2_query {
#define MLDV2_QQIC_EXP(value) (((value) >> 4) & 0x07)
#define MLDV2_QQIC_MAN(value) ((value) & 0x0f)
-#define MLD_EXP_MIN_LIMIT 32768UL
-#define MLDV1_MRD_MAX_COMPAT (MLD_EXP_MIN_LIMIT - 1)
+#define MLD_QQIC_MIN_THRESHOLD 128
+#define MLD_MRC_MIN_THRESHOLD 32768UL
+#define MLDV1_MRD_MAX_COMPAT (MLD_MRC_MIN_THRESHOLD - 1)
#define MLD_MAX_QUEUE 8
#define MLD_MAX_SKBS 32
-static inline unsigned long mldv2_mrc(const struct mld2_query *mlh2)
-{
- /* RFC3810, 5.1.3. Maximum Response Code */
- unsigned long ret, mc_mrc = ntohs(mlh2->mld2q_mrc);
+/* V2 exponential field decoding */
- if (mc_mrc < MLD_EXP_MIN_LIMIT) {
- ret = mc_mrc;
+/* Calculate Maximum Response Delay from Maximum Response Code
+ *
+ * RFC3810, 5.1.3. defines the decoding formula:
+ * 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |1| exp | mant |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Maximum Response Delay = (mant | 0x1000) << (exp+3)
+ */
+static inline unsigned long mldv2_mrd(const struct mld2_query *mlh2)
+{
+ /* RFC3810, relevant sections:
+ * - 5.1.3. Maximum Response Code
+ * - 9.3. Query Response Interval
+ */
+ unsigned long mc_mrc = ntohs(mlh2->mld2q_mrc);
+
+ if (mc_mrc < MLD_MRC_MIN_THRESHOLD) {
+ return mc_mrc;
} else {
unsigned long mc_man, mc_exp;
mc_exp = MLDV2_MRC_EXP(mc_mrc);
mc_man = MLDV2_MRC_MAN(mc_mrc);
- ret = (mc_man | 0x1000) << (mc_exp + 3);
+ return ((mc_man | 0x1000) << (mc_exp + 3));
}
+}
- return ret;
+/* Calculate Querier's Query Interval from Querier's Query Interval Code
+ *
+ * RFC3810, 5.1.9. defines the decoding formula:
+ * 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+
+ * |1| exp | mant |
+ * +-+-+-+-+-+-+-+-+
+ * QQI = (mant | 0x10) << (exp + 3)
+ */
+static inline unsigned long mldv2_qqi(const struct mld2_query *mlh2)
+{
+ /* RFC3810, relevant sections:
+ * - 5.1.9. QQIC (Querier's Query Interval Code)
+ * - 9.2. Query Interval
+ * - 9.12. Older Version Querier Present Timeout
+ * (the [Query Interval] in the last Query received)
+ */
+ unsigned long qqic = ntohs(mlh2->mld2q_qqic);
+
+ if (qqic < MLD_QQIC_MIN_THRESHOLD) {
+ return qqic;
+ } else {
+ unsigned long mc_man, mc_exp;
+
+ mc_exp = MLDV2_QQIC_EXP(qqic);
+ mc_man = MLDV2_QQIC_MAN(qqic);
+
+ return ((mc_man | 0x10) << (mc_exp + 3));
+ }
}
#endif
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 9fec76e887bc..1438c023db62 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -3606,7 +3606,7 @@ static int br_ip6_multicast_query(struct net_bridge_mcast *brmctx,
mld2q->mld2q_suppress)
goto out;
- max_delay = max(msecs_to_jiffies(mldv2_mrc(mld2q)), 1UL);
+ max_delay = max(msecs_to_jiffies(mldv2_mrd(mld2q)), 1UL);
}
is_general_query = group && ipv6_addr_any(group);
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 3330adcf26db..6ddc18ac59b9 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1315,20 +1315,7 @@ static void mld_update_qi(struct inet6_dev *idev,
* - 9.12. Older Version Querier Present Timeout
* (the [Query Interval] in the last Query received)
*/
- unsigned long mc_qqi;
-
- if (mlh2->mld2q_qqic < 128) {
- mc_qqi = mlh2->mld2q_qqic;
- } else {
- unsigned long mc_man, mc_exp;
-
- mc_exp = MLDV2_QQIC_EXP(mlh2->mld2q_qqic);
- mc_man = MLDV2_QQIC_MAN(mlh2->mld2q_qqic);
-
- mc_qqi = (mc_man | 0x10) << (mc_exp + 3);
- }
-
- idev->mc_qi = mc_qqi * HZ;
+ idev->mc_qi = mldv2_qqi(mlh2) * HZ;
}
static void mld_update_qri(struct inet6_dev *idev,
@@ -1338,7 +1325,7 @@ static void mld_update_qri(struct inet6_dev *idev,
* - 5.1.3. Maximum Response Code
* - 9.3. Query Response Interval
*/
- idev->mc_qri = msecs_to_jiffies(mldv2_mrc(mlh2));
+ idev->mc_qri = msecs_to_jiffies(mldv2_mrd(mlh2));
}
static int mld_process_v1(struct inet6_dev *idev, struct mld_msg *mld,
@@ -1390,7 +1377,7 @@ static int mld_process_v1(struct inet6_dev *idev, struct mld_msg *mld,
static void mld_process_v2(struct inet6_dev *idev, struct mld2_query *mld,
unsigned long *max_delay)
{
- *max_delay = max(msecs_to_jiffies(mldv2_mrc(mld)), 1UL);
+ *max_delay = max(msecs_to_jiffies(mldv2_mrd(mld)), 1UL);
mld_update_qrv(idev, mld);
mld_update_qi(idev, mld);
--
2.43.0
^ permalink raw reply related
* [PATCH 1/4] ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation
From: Ujjal Roy @ 2026-03-26 15:07 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Nikolay Aleksandrov, Ido Schimmel, David Ahern
Cc: Ujjal Roy, bridge, netdev, linux-kernel
In-Reply-To: <20260326150742.50289-1-royujjal@gmail.com>
Get rid of the IGMPV3_MRC macro and use the igmpv3_mrt() API to
calculate the Max Resp Time from the Maximum Response Code.
Similarly, for IGMPV3_QQIC, use the igmpv3_qqi() API to calculate
the Querier's Query Interval from the QQIC field.
Signed-off-by: Ujjal Roy <royujjal@gmail.com>
---
include/linux/igmp.h | 78 +++++++++++++++++++++++++++++++++++----
net/bridge/br_multicast.c | 2 +-
net/ipv4/igmp.c | 6 +--
3 files changed, 74 insertions(+), 12 deletions(-)
diff --git a/include/linux/igmp.h b/include/linux/igmp.h
index 073b30a9b850..3c12c0a63492 100644
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -92,15 +92,77 @@ struct ip_mc_list {
struct rcu_head rcu;
};
+/* RFC3376, relevant sections:
+ * - 4.1.1. Maximum Response Code
+ * - 4.1.7. QQIC (Querier's Query Interval Code)
+ *
+ * If Max Resp Code >= 128, Max Resp Code represents a floating-point
+ * value as follows:
+ * If QQIC >= 128, QQIC represents a floating-point value as follows:
+ *
+ * 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+
+ * |1| exp | mant |
+ * +-+-+-+-+-+-+-+-+
+ */
+#define IGMPV3_FP_EXP(value) (((value) >> 4) & 0x07)
+#define IGMPV3_FP_MAN(value) ((value) & 0x0f)
+
+/* IGMPV3 floating-point exponential field threshold */
+#define IGMPV3_EXP_MIN_THRESHOLD 128
+
/* V3 exponential field decoding */
-#define IGMPV3_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value))
-#define IGMPV3_EXP(thresh, nbmant, nbexp, value) \
- ((value) < (thresh) ? (value) : \
- ((IGMPV3_MASK(value, nbmant) | (1<<(nbmant))) << \
- (IGMPV3_MASK((value) >> (nbmant), nbexp) + (nbexp))))
-
-#define IGMPV3_QQIC(value) IGMPV3_EXP(0x80, 4, 3, value)
-#define IGMPV3_MRC(value) IGMPV3_EXP(0x80, 4, 3, value)
+
+/*
+ * IGMPv3 QQI/MRT 8-bit exponential field decode.
+ *
+ * RFC3376, 4.1.1 & 4.1.7. defines the decoding formula:
+ * 0 1 2 3 4 5 6 7
+ * +-+-+-+-+-+-+-+-+
+ * |1| exp | mant |
+ * +-+-+-+-+-+-+-+-+
+ * Max Resp Time = (mant | 0x10) << (exp + 3)
+ * QQI = (mant | 0x10) << (exp + 3)
+ */
+static inline unsigned long igmpv3_exp_field_decode(const u8 code)
+{
+ /* RFC3376, relevant sections:
+ * - 4.1.1. Maximum Response Code
+ * - 4.1.7. QQIC (Querier's Query Interval Code)
+ */
+ if (code < IGMPV3_EXP_MIN_THRESHOLD) {
+ return (unsigned long)code;
+ } else {
+ unsigned long mc_man, mc_exp;
+
+ mc_exp = IGMPV3_FP_EXP(code);
+ mc_man = IGMPV3_FP_MAN(code);
+
+ return ((mc_man | 0x10) << (mc_exp + 3));
+ }
+}
+
+/* Calculate Max Resp Time from Maximum Response Code */
+static inline unsigned long igmpv3_mrt(const struct igmpv3_query *ih3)
+{
+ /* RFC3376, relevant sections:
+ * - 4.1.1. Maximum Response Code
+ * - 8.3. Query Response Interval
+ */
+ return igmpv3_exp_field_decode(ih3->code);
+}
+
+/* Calculate Querier's Query Interval from Querier's Query Interval Code */
+static inline unsigned long igmpv3_qqi(const struct igmpv3_query *ih3)
+{
+ /* RFC3376, relevant sections:
+ * - 4.1.7. QQIC (Querier's Query Interval Code)
+ * - 8.2. Query Interval
+ * - 8.12. Older Version Querier Present Timeout
+ * (the [Query Interval] in the last Query received)
+ */
+ return igmpv3_exp_field_decode(ih3->qqic);
+}
static inline int ip_mc_may_pull(struct sk_buff *skb, unsigned int len)
{
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 881d866d687a..9fec76e887bc 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -3518,7 +3518,7 @@ static void br_ip4_multicast_query(struct net_bridge_mcast *brmctx,
goto out;
max_delay = ih3->code ?
- IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;
+ igmpv3_mrt(ih3) * (HZ / IGMP_TIMER_SCALE) : 1;
} else {
goto out;
}
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index a674fb44ec25..8c6102737096 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -991,7 +991,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
* different encoding. We use the v3 encoding as more likely
* to be intended in a v3 query.
*/
- max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);
+ max_delay = igmpv3_mrt(ih3)*(HZ/IGMP_TIMER_SCALE);
if (!max_delay)
max_delay = 1; /* can't mod w/ 0 */
} else { /* v3 */
@@ -1006,7 +1006,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
ih3 = igmpv3_query_hdr(skb);
}
- max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);
+ max_delay = igmpv3_mrt(ih3)*(HZ/IGMP_TIMER_SCALE);
if (!max_delay)
max_delay = 1; /* can't mod w/ 0 */
WRITE_ONCE(in_dev->mr_maxdelay, max_delay);
@@ -1016,7 +1016,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
* configured value.
*/
in_dev->mr_qrv = ih3->qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
- in_dev->mr_qi = IGMPV3_QQIC(ih3->qqic)*HZ ?: IGMP_QUERY_INTERVAL;
+ in_dev->mr_qi = igmpv3_qqi(ih3)*HZ ?: IGMP_QUERY_INTERVAL;
/* RFC3376, 8.3. Query Response Interval:
* The number of seconds represented by the [Query Response
--
2.43.0
^ permalink raw reply related
* [PATCH 0/4] net: bridge: mcast: add multicast exponential field encoding
From: Ujjal Roy @ 2026-03-26 15:07 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Nikolay Aleksandrov, Ido Schimmel, David Ahern
Cc: Ujjal Roy, bridge, netdev, linux-kernel
Description:
This series addresses a mismatch in how multicast query
intervals and response codes are handled across IPv4 (IGMPv3)
and IPv6 (MLDv2). While decoding logic currently exists,
the corresponding encoding logic is missing during query
packet generation. This leads to incorrect intervals being
transmitted when values exceed their linear thresholds.
The patches introduce a unified floating-point encoding
approach based on RFC3376 and RFC3810, ensuring that large
intervals are correctly represented in QQIC and MRC fields
using the exponent-mantissa format.
Key Changes:
* ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation
Removes legacy macros in favor of a cleaner, unified
calculation for retrieving intervals from encoded fields,
improving code maintainability.
* ipv6: mld: rename mldv2_mrc() and add mldv2_qqi()
Standardizes MLDv2 terminology by renaming mldv2_mrc()
to mldv2_mrd() (Maximum Response Delay) and introducing
a new API mldv2_qqi for QQI calculation, improving code
readability.
* ipv4: igmp: encode multicast exponential fields
Introduces the logic to dynamically calculate the exponent
and mantissa using bit-scan (fls). This ensures QQIC and
MRC fields (8-bit) are properly encoded when transmitting
query packets with intervals that exceed their respective
linear threshold value of 128 (for QQI/MRT).
* ipv6: mld: encode multicast exponential fields
Applies similar encoding logic for MLDv2. This ensures
QQIC (8-bit) and MRC (16-bit) fields are properly encoded
when transmitting query packets with intervals that exceed
their respective linear thresholds (128 for QQI; 32768
for MRD).
Impact:
These changes ensure that multicast queriers and listeners
stay synchronized on timing intervals, preventing protocol
timeouts or premature group membership expiration caused
by incorrectly formatted packet headers.
Ujjal Roy (4):
ipv4: igmp: get rid of IGMPV3_{QQIC,MRC} and simplify calculation
ipv6: mld: rename mldv2_mrc() and add mldv2_qqi()
ipv4: igmp: encode multicast exponential fields
ipv6: mld: encode multicast exponential fields
include/linux/igmp.h | 158 +++++++++++++++++++++++++++++++--
include/net/mld.h | 179 ++++++++++++++++++++++++++++++++++++--
net/bridge/br_multicast.c | 22 +++--
net/ipv4/igmp.c | 6 +-
net/ipv6/mcast.c | 19 +---
5 files changed, 336 insertions(+), 48 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH net-next v11 06/15] quic: add stream management
From: Xin Long @ 2026-03-26 15:06 UTC (permalink / raw)
To: network dev, quic
Cc: davem, kuba, Eric Dumazet, Paolo Abeni, Simon Horman,
Stefan Metzmacher, Moritz Buhl, Tyler Fanelli, Pengtao He,
Thomas Dreibholz, linux-cifs, Steve French, Namjae Jeon,
Paulo Alcantara, Tom Talpey, kernel-tls-handshake, Chuck Lever,
Jeff Layton, Steve Dickson, Hannes Reinecke, Alexander Aring,
David Howells, Matthieu Baerts, John Ericson, Cong Wang,
D . Wythe, Jason Baron, illiliti, Sabrina Dubroca,
Marcelo Ricardo Leitner, Daniel Stenberg, Andy Gospodarek,
Marc E . Fiuczynski
In-Reply-To: <91c6fd0a44eb15a98f945171ead8062badb89a60.1774410440.git.lucien.xin@gmail.com>
On Tue, Mar 24, 2026 at 11:49 PM Xin Long <lucien.xin@gmail.com> wrote:
>
> This patch introduces 'struct quic_stream_table' for managing QUIC streams,
> each represented by 'struct quic_stream'.
>
> It implements mechanisms for acquiring and releasing streams on both the
> send and receive paths, ensuring efficient lifecycle management during
> transmission and reception.
>
> - quic_stream_get(): Acquire a send-side stream by ID and flags during
> TX path, or a receive-side stream by ID during RX path.
>
> - quic_stream_put(): Release a send-side stream when sending is done,
> or a receive-side stream when receiving is done.
>
> It includes logic to detect when stream ID limits are reached and when
> control frames should be sent to update or request limits from the peer.
>
> - quic_stream_id_exceeds(): Check a stream ID would exceed local (recv)
> or peer (send) limits.
>
> - quic_stream_max_streams_update(): Determines whether a
> MAX_STREAMS_UNI/BIDI frame should be sent to the peer.
>
> Note stream hash table is per socket, the operations on it are always
> protected by the sock lock.
>
> Signed-off-by: Xin Long <lucien.xin@gmail.com>
> Acked-by: Paolo Abeni <pabeni@redhat.com>
> ---
> v3:
> - Merge send/recv stream helpers into unified functions to reduce code:
> * quic_stream_id_send/recv() → quic_stream_id_valid()
> * quic_stream_id_send/recv_closed() → quic_stream_id_closed()
> * quic_stream_id_send/recv_exceeds() → quic_stream_id_exceeds()
> (pointed out by Paolo).
> - Clarify in changelog that stream hash table is always protected by sock
> lock (suggested by Paolo).
> - quic_stream_init/free(): adjust for new hashtable type; call
> quic_stream_delete() in quic_stream_free() to avoid open-coded logic.
> - Receiving streams: delete stream only when fully read or reset, instead
> of when no data was received. Prevents freeing a stream while a FIN
> with no data is still queued.
> v4:
> - Replace struct quic_shash_table with struct hlist_head for the
> stream hashtable. Since they are protected by the socket lock,
> no per-chain lock is needed.
> - Initialize stream to NULL in stream creation functions to avoid
> warnings from Smatch (reported by Simon).
> - Allocate send streams with GFP_KERNEL_ACCOUNT and receive streams
> with GFP_ATOMIC | __GFP_ACCOUNT for memory accounting (suggested
> by Paolo).
> v5:
> - Introduce struct quic_stream_limits to merge quic_stream_send_create()
> and quic_stream_recv_create(), and to simplify quic_stream_get_param()
> (suggested by Paolo).
> - Annotate the sock-lock requirement for quic_stream_send/recv_get()
> and quic_stream_send/recv_put() (notied by Paolo).
> - Add quic_stream_bidi_put() to deduplicate the common logic between
> quic_stream_send_put() and quic_stream_recv_put().
> - Remove the unnecessary check when incrementing
> streams->send.next_bidi/uni_stream_id in quic_stream_create().
> - Remove the unused 'is_serv' parameter from quic_stream_get_param().
> v7:
> - Free the allocated streams on error path in quic_stream_create() (noted
> by Paolo).
> - Merge quic_stream_send_get/put() and quic_stream_recv_get/put() helpers
> to quic_stream_get/put() (suggested by Paolo).
> - Add more comments in quic_stream_id_exceeds() and quic_stream_create().
> v8:
> - Replace bitfields with plain u8 in struct quic_stream_limits and struct
> quic_stream (suggested by Paolo).
> v9:
> - Fix grammar in the comment for quic_stream::send.window.
> v10:
> - Move quic_stream_init() to after sock_prot_inuse_add() ensure counters
> are incremented before any early return paths in quic_init_sock(),
> preventing underflow in quic_destroy_sock() (noted by AI review).
> - Initialize the output parameters '*max_uni' and '*max_bidi' to 0 at the
> start of quic_stream_max_streams_update()
> - Use 'stream->recv.state > QUIC_STREAM_RECV_STATE_RECVD' instead of '!='
> for clearer intent.
> - Simplify some state checks in quic_stream_put() by using range
> comparisons (> or <) instead of multiple != conditions.
> - streams_uni/bidi are u16 type, and their overflow is already prevented
> by QUIC_MAX_STREAMS indirectly. Update comment in quic_stream_create().
> - Replace open-coded kzalloc(sizeof(*stream)) with kzalloc_obj(*stream)
> in quic_stream_create().
> v11:
> - Set maximum line length to 80 characters.
> - Change is_serv parameter type to bool in quic_stream_id_local().
> ---
> net/quic/Makefile | 2 +-
> net/quic/socket.c | 5 +
> net/quic/socket.h | 8 +
> net/quic/stream.c | 444 ++++++++++++++++++++++++++++++++++++++++++++++
> net/quic/stream.h | 133 ++++++++++++++
> 5 files changed, 591 insertions(+), 1 deletion(-)
> create mode 100644 net/quic/stream.c
> create mode 100644 net/quic/stream.h
>
> diff --git a/net/quic/Makefile b/net/quic/Makefile
> index 13bf4a4e5442..094e9da5d739 100644
> --- a/net/quic/Makefile
> +++ b/net/quic/Makefile
> @@ -5,4 +5,4 @@
>
> obj-$(CONFIG_IP_QUIC) += quic.o
>
> -quic-y := common.o family.o protocol.o socket.o
> +quic-y := common.o family.o protocol.o socket.o stream.o
> diff --git a/net/quic/socket.c b/net/quic/socket.c
> index 8dc2cb7628db..0006668551f4 100644
> --- a/net/quic/socket.c
> +++ b/net/quic/socket.c
> @@ -45,11 +45,16 @@ static int quic_init_sock(struct sock *sk)
> sk_sockets_allocated_inc(sk);
> sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
>
> + if (quic_stream_init(quic_streams(sk)))
> + return -ENOMEM;
> +
> return 0;
> }
>
> static void quic_destroy_sock(struct sock *sk)
> {
> + quic_stream_free(quic_streams(sk));
> +
> quic_data_free(quic_ticket(sk));
> quic_data_free(quic_token(sk));
> quic_data_free(quic_alpn(sk));
> diff --git a/net/quic/socket.h b/net/quic/socket.h
> index 61df0c5867be..e76737b9b74b 100644
> --- a/net/quic/socket.h
> +++ b/net/quic/socket.h
> @@ -13,6 +13,7 @@
>
> #include "common.h"
> #include "family.h"
> +#include "stream.h"
>
> #include "protocol.h"
>
> @@ -33,6 +34,8 @@ struct quic_sock {
> struct quic_data ticket;
> struct quic_data token;
> struct quic_data alpn;
> +
> + struct quic_stream_table streams;
> };
>
> struct quic6_sock {
> @@ -65,6 +68,11 @@ static inline struct quic_data *quic_alpn(const struct sock *sk)
> return &quic_sk(sk)->alpn;
> }
>
> +static inline struct quic_stream_table *quic_streams(const struct sock *sk)
> +{
> + return &quic_sk(sk)->streams;
> +}
> +
> static inline bool quic_is_serv(const struct sock *sk)
> {
> return !!sk->sk_max_ack_backlog;
> diff --git a/net/quic/stream.c b/net/quic/stream.c
> new file mode 100644
> index 000000000000..4d980f9b03ce
> --- /dev/null
> +++ b/net/quic/stream.c
> @@ -0,0 +1,444 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/* QUIC kernel implementation
> + * (C) Copyright Red Hat Corp. 2023
> + *
> + * This file is part of the QUIC kernel implementation
> + *
> + * Initialization/cleanup for QUIC protocol support.
> + *
> + * Written or modified by:
> + * Xin Long <lucien.xin@gmail.com>
> + */
> +
> +#include <linux/quic.h>
> +
> +#include "common.h"
> +#include "stream.h"
> +
> +/* Check if a stream ID is valid for sending or receiving. */
> +static bool quic_stream_id_valid(s64 stream_id, bool is_serv, bool send)
> +{
> + u8 type = (stream_id & QUIC_STREAM_TYPE_MASK);
> +
> + if (send) {
> + if (is_serv)
> + return type != QUIC_STREAM_TYPE_CLIENT_UNI;
> + return type != QUIC_STREAM_TYPE_SERVER_UNI;
> + }
> + if (is_serv)
> + return type != QUIC_STREAM_TYPE_SERVER_UNI;
> + return type != QUIC_STREAM_TYPE_CLIENT_UNI;
> +}
> +
> +/* Check if a stream ID was initiated locally. */
> +static bool quic_stream_id_local(s64 stream_id, bool is_serv)
> +{
> + return is_serv ^ !(stream_id & QUIC_STREAM_TYPE_SERVER_MASK);
> +}
> +
> +/* Check if a stream ID represents a unidirectional stream. */
> +static bool quic_stream_id_uni(s64 stream_id)
> +{
> + return stream_id & QUIC_STREAM_TYPE_UNI_MASK;
> +}
> +
> +#define QUIC_STREAM_HT_SIZE 64
> +
> +static struct hlist_head *quic_stream_head(struct quic_stream_table *streams,
> + s64 stream_id)
> +{
> + return &streams->head[stream_id & (QUIC_STREAM_HT_SIZE - 1)];
> +}
> +
> +struct quic_stream *quic_stream_find(struct quic_stream_table *streams,
> + s64 stream_id)
> +{
> + struct hlist_head *head = quic_stream_head(streams, stream_id);
> + struct quic_stream *stream;
> +
> + hlist_for_each_entry(stream, head, node) {
> + if (stream->id == stream_id)
> + break;
> + }
> + return stream;
> +}
> +
> +static void quic_stream_add(struct quic_stream_table *streams,
> + struct quic_stream *stream)
> +{
> + struct hlist_head *head;
> +
> + head = quic_stream_head(streams, stream->id);
> + hlist_add_head(&stream->node, head);
> +}
> +
> +static void quic_stream_delete(struct quic_stream *stream)
> +{
> + hlist_del_init(&stream->node);
> + kfree(stream);
> +}
> +
> +/* Create and register new streams for sending or receiving. */
> +static struct quic_stream *quic_stream_create(struct quic_stream_table *streams,
> + s64 max_stream_id, bool send,
> + bool is_serv)
> +{
> + struct quic_stream_limits *limits = &streams->send;
> + struct quic_stream *pos, *stream = NULL;
> + gfp_t gfp = GFP_KERNEL_ACCOUNT;
> + struct hlist_node *tmp;
> + HLIST_HEAD(head);
> + s64 stream_id;
> + u32 count = 0;
> +
> + if (!send) {
> + limits = &streams->recv;
> + gfp = GFP_ATOMIC | __GFP_ACCOUNT;
> + }
> + stream_id = limits->next_bidi_stream_id;
> + if (quic_stream_id_uni(max_stream_id))
> + stream_id = limits->next_uni_stream_id;
> +
> + /* rfc9000#section-2.1: A stream ID that is used out of order results in
> + * all streams of that type with lower-numbered stream IDs also being
> + * opened.
> + */
> + while (stream_id <= max_stream_id) {
> + stream = kzalloc_obj(*stream, gfp);
> + if (!stream)
> + goto free;
> +
> + stream->id = stream_id;
> + if (quic_stream_id_uni(stream_id)) {
> + if (send) {
> + stream->send.max_bytes =
> + limits->max_stream_data_uni;
> + } else {
> + stream->recv.max_bytes =
> + limits->max_stream_data_uni;
> + stream->recv.window = stream->recv.max_bytes;
> + }
> + hlist_add_head(&stream->node, &head);
> + stream_id += QUIC_STREAM_ID_STEP;
> + continue;
> + }
> +
> + if (quic_stream_id_local(stream_id, is_serv)) {
> + stream->send.max_bytes =
> + streams->send.max_stream_data_bidi_remote;
> + stream->recv.max_bytes =
> + streams->recv.max_stream_data_bidi_local;
> + } else {
> + stream->send.max_bytes =
> + streams->send.max_stream_data_bidi_local;
> + stream->recv.max_bytes =
> + streams->recv.max_stream_data_bidi_remote;
> + }
> + stream->recv.window = stream->recv.max_bytes;
> + hlist_add_head(&stream->node, &head);
> + stream_id += QUIC_STREAM_ID_STEP;
> + }
> +
> + hlist_for_each_entry_safe(pos, tmp, &head, node) {
> + hlist_del_init(&pos->node);
> + quic_stream_add(streams, pos);
> + count++;
> + }
> +
> + /* Streams must be opened sequentially. Update the next stream ID so the
> + * correct starting point is known if an out-of-order open is requested.
> + * Note overflow of next_uni/bidi_stream_id is impossible with s64.
> + */
> + if (quic_stream_id_uni(stream_id)) {
> + limits->next_uni_stream_id = stream_id;
> + limits->streams_uni += count;
> + return stream;
> + }
> +
> + limits->next_bidi_stream_id = stream_id;
> + limits->streams_bidi += count;
> + return stream;
> +
> +free:
> + hlist_for_each_entry_safe(pos, tmp, &head, node) {
> + hlist_del_init(&pos->node);
> + kfree(pos);
> + }
> + return NULL;
> +}
> +
> +/* Check if a send or receive stream ID is already closed. */
> +static bool quic_stream_id_closed(struct quic_stream_table *streams,
> + s64 stream_id, bool send)
> +{
> + struct quic_stream_limits *limits = send ? &streams->send :
> + &streams->recv;
> +
> + if (quic_stream_id_uni(stream_id))
> + return stream_id < limits->next_uni_stream_id;
> + return stream_id < limits->next_bidi_stream_id;
> +}
> +
> +/* Check if a stream ID would exceed local (recv) or peer (send) limits. */
> +bool quic_stream_id_exceeds(struct quic_stream_table *streams, s64 stream_id,
> + bool send)
> +{
> + u64 nstreams;
> +
> + if (!send) {
> + /* recv.max_uni_stream_id is updated in
> + * quic_stream_max_streams_update() already based on
> + * next_uni/bidi_stream_id, max_streams_uni/bidi, and
> + * streams_uni/bidi, so only recv.max_uni_stream_id needs to be
> + * checked.
> + */
> + if (quic_stream_id_uni(stream_id))
> + return stream_id > streams->recv.max_uni_stream_id;
> +
> + return stream_id > streams->recv.max_bidi_stream_id;
> + }
> +
> + if (quic_stream_id_uni(stream_id)) {
> + if (stream_id > streams->send.max_uni_stream_id)
> + return true;
> + stream_id -= streams->send.next_uni_stream_id;
> + nstreams = quic_stream_id_to_streams(stream_id);
> +
> + return nstreams + streams->send.streams_uni >
> + streams->send.max_streams_uni;
> + }
> +
> + if (stream_id > streams->send.max_bidi_stream_id)
> + return true;
> + stream_id -= streams->send.next_bidi_stream_id;
> + nstreams = quic_stream_id_to_streams(stream_id);
> +
> + return nstreams + streams->send.streams_bidi >
> + streams->send.max_streams_bidi;
> +}
> +
> +/* Get or create a send or recv stream by ID. Requires sock lock held. */
> +struct quic_stream *quic_stream_get(struct quic_stream_table *streams,
> + s64 stream_id, u32 flags, bool is_serv,
> + bool send)
> +{
> + struct quic_stream *stream;
> +
> + if (!quic_stream_id_valid(stream_id, is_serv, send))
> + return ERR_PTR(-EINVAL);
> +
> + stream = quic_stream_find(streams, stream_id);
> + if (stream) {
> + if (send && (flags & MSG_QUIC_STREAM_NEW) &&
> + stream->send.state != QUIC_STREAM_SEND_STATE_READY)
> + return ERR_PTR(-EINVAL);
> + return stream;
> + }
> +
> + if (!send && quic_stream_id_local(stream_id, is_serv)) {
> + if (quic_stream_id_closed(streams, stream_id, !send))
> + return ERR_PTR(-ENOSTR);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + if (quic_stream_id_closed(streams, stream_id, send))
> + return ERR_PTR(-ENOSTR);
> +
> + if (send && !(flags & MSG_QUIC_STREAM_NEW))
> + return ERR_PTR(-EINVAL);
> +
> + if (quic_stream_id_exceeds(streams, stream_id, send))
> + return ERR_PTR(-EAGAIN);
> +
> + stream = quic_stream_create(streams, stream_id, send, is_serv);
> + if (!stream)
> + return ERR_PTR(-ENOSTR);
> +
> + if (send || quic_stream_id_valid(stream_id, is_serv, !send))
> + streams->send.active_stream_id = stream_id;
> +
> + return stream;
> +}
> +
> +/* Release or clean up a send or recv stream. This function updates stream
> + * counters and state when a send stream has either successfully sent all data
> + * or has been reset, or when a recv stream has either consumed all data or has
> + * been reset. Requires sock lock held.
> + */
> +void quic_stream_put(struct quic_stream_table *streams,
> + struct quic_stream *stream, bool is_serv, bool send)
> +{
> + if (quic_stream_id_uni(stream->id)) {
> + if (send) {
> + /* For uni streams, decrement uni count and delete
> + * immediately.
> + */
> + streams->send.streams_uni--;
> + quic_stream_delete(stream);
> + return;
> + }
> + /* For uni streams, decrement uni count and mark done. */
> + if (!stream->recv.done) {
> + stream->recv.done = 1;
> + streams->recv.streams_uni--;
> + streams->recv.uni_pending = 1;
> + }
> + /* Delete stream if fully read or reset. */
> + if (stream->recv.state > QUIC_STREAM_RECV_STATE_RECVD)
> + quic_stream_delete(stream);
> + return;
> + }
> +
> + if (send) {
> + /* For bidi streams, only proceed if receive side is in a final
> + * state.
> + */
> + if (stream->recv.state < QUIC_STREAM_RECV_STATE_RECVD)
> + return;
> + } else {
> + /* For bidi streams, only proceed if send side is in a final
> + * state.
> + */
> + if (stream->send.state != QUIC_STREAM_SEND_STATE_RECVD &&
> + stream->send.state != QUIC_STREAM_SEND_STATE_RESET_RECVD)
> + return;
> + }
> +
> + if (quic_stream_id_local(stream->id, is_serv)) {
> + /* Local-initiated stream: mark send done and decrement
> + * send.bidi count.
> + */
> + if (!stream->send.done) {
> + stream->send.done = 1;
> + streams->send.streams_bidi--;
> + }
> + } else {
> + /* Remote-initiated stream: mark recv done and decrement recv
> + * bidi count.
> + */
> + if (!stream->recv.done) {
> + stream->recv.done = 1;
> + streams->recv.streams_bidi--;
> + streams->recv.bidi_pending = 1;
> + }
> + }
> +
> + /* Delete stream if fully read or reset. */
> + if (stream->recv.state > QUIC_STREAM_RECV_STATE_RECVD)
> + quic_stream_delete(stream);
> +}
> +
> +/* Updates the maximum allowed incoming stream IDs if any streams were recently
> + * closed. Recalculates the max_uni and max_bidi stream ID limits based on the
> + * number of open streams and whether any were marked for deletion.
> + *
> + * Returns true if either max_uni or max_bidi was updated, indicating that a
> + * MAX_STREAMS_UNI or MAX_STREAMS_BIDI frame should be sent to the peer.
> + */
> +bool quic_stream_max_streams_update(struct quic_stream_table *streams,
> + s64 *max_uni, s64 *max_bidi)
> +{
> + s64 max, rem;
> +
> + *max_uni = 0;
> + *max_bidi = 0;
> + if (streams->recv.uni_pending) {
> + rem = streams->recv.max_streams_uni - streams->recv.streams_uni;
> + max = streams->recv.next_uni_stream_id - QUIC_STREAM_ID_STEP +
> + (rem << QUIC_STREAM_TYPE_BITS);
> +
> + streams->recv.max_uni_stream_id = max;
> + *max_uni = quic_stream_id_to_streams(max);
> + streams->recv.uni_pending = 0;
> + }
> + if (streams->recv.bidi_pending) {
> + rem = streams->recv.max_streams_bidi -
> + streams->recv.streams_bidi;
> + max = streams->recv.next_bidi_stream_id - QUIC_STREAM_ID_STEP +
> + (rem << QUIC_STREAM_TYPE_BITS);
> +
> + streams->recv.max_bidi_stream_id = max;
> + *max_bidi = quic_stream_id_to_streams(max);
> + streams->recv.bidi_pending = 0;
> + }
> +
> + return *max_uni || *max_bidi;
> +}
> +
> +int quic_stream_init(struct quic_stream_table *streams)
> +{
> + struct hlist_head *head;
> + int i;
> +
> + head = kmalloc_array(QUIC_STREAM_HT_SIZE, sizeof(*head), GFP_KERNEL);
> + if (!head)
> + return -ENOMEM;
> + for (i = 0; i < QUIC_STREAM_HT_SIZE; i++)
> + INIT_HLIST_HEAD(&head[i]);
> + streams->head = head;
> + return 0;
> +}
> +
> +void quic_stream_free(struct quic_stream_table *streams)
> +{
> + struct quic_stream *stream;
> + struct hlist_head *head;
> + struct hlist_node *tmp;
> + int i;
> +
> + if (!streams->head)
> + return;
> +
> + for (i = 0; i < QUIC_STREAM_HT_SIZE; i++) {
> + head = &streams->head[i];
> + hlist_for_each_entry_safe(stream, tmp, head, node)
> + quic_stream_delete(stream);
> + }
> + kfree(streams->head);
The AI report on
https://netdev-ai.bots.linux.dev/ai-review.html?id=1624d906-c0b6-4e12-a63f-5cbfc51b660e#patch-5
is false positive.
As the sk_alloc() calls sk_prot_alloc() with __GFP_ZERO, and the streams->head
is always initialized to NULL.
^ permalink raw reply
* RE: [PATCH net,v2] net: mana: Fix RX skb truesize accounting
From: Haiyang Zhang @ 2026-03-26 15:04 UTC (permalink / raw)
To: Dipayaan Roy, KY Srinivasan, wei.liu@kernel.org, Dexuan Cui,
andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, leon@kernel.org, Long Li,
Konstantin Taranov, horms@kernel.org,
shradhagupta@linux.microsoft.com, ssengar@linux.microsoft.com,
ernis@linux.microsoft.com, Shiraz Saleem,
linux-hyperv@vger.kernel.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-rdma@vger.kernel.org,
stephen@networkplumber.org, jacob.e.keller@intel.com,
Dipayaan Roy
In-Reply-To: <acLUhLpLum6qrD/N@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net>
> -----Original Message-----
> From: Dipayaan Roy <dipayanroy@linux.microsoft.com>
> Sent: Tuesday, March 24, 2026 2:14 PM
> To: KY Srinivasan <kys@microsoft.com>; Haiyang Zhang
> <haiyangz@microsoft.com>; wei.liu@kernel.org; Dexuan Cui
> <DECUI@microsoft.com>; andrew+netdev@lunn.ch; davem@davemloft.net;
> edumazet@google.com; kuba@kernel.org; pabeni@redhat.com; leon@kernel.org;
> Long Li <longli@microsoft.com>; Konstantin Taranov
> <kotaranov@microsoft.com>; horms@kernel.org;
> shradhagupta@linux.microsoft.com; ssengar@linux.microsoft.com;
> ernis@linux.microsoft.com; Shiraz Saleem <shirazsaleem@microsoft.com>;
> linux-hyperv@vger.kernel.org; netdev@vger.kernel.org; linux-
> kernel@vger.kernel.org; linux-rdma@vger.kernel.org;
> stephen@networkplumber.org; jacob.e.keller@intel.com; Dipayaan Roy
> <dipayanroy@microsoft.com>
> Subject: [PATCH net,v2] net: mana: Fix RX skb truesize accounting
>
> MANA passes rxq->alloc_size to napi_build_skb() for all RX buffers.
> It is correct for fragment-backed RX buffers, where alloc_size matches
> the actual backing allocation used for each packet buffer. However, in
> the non-fragment RX path mana allocates a full page, or a higher-order
> page, per RX buffer. In that case alloc_size only reflects the usable
> packet area and not the actual backing memory.
>
> This causes napi_build_skb() to underestimate the skb backing allocation
> in the single-buffer RX path, so skb->truesize is derived from a value
> smaller than the real RX buffer allocation.
>
> Fix this by updating alloc_size in the non-fragment RX path to the
> actual backing allocation size before it is passed to napi_build_skb().
>
> Fixes: 730ff06d3f5c ("net: mana: Use page pool fragments for RX buffers
> instead of full pages to improve memory efficiency.")
> Signed-off-by: Dipayaan Roy <dipayanroy@linux.microsoft.com>
Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
> ---
> Changes in v2:
> - Added maintainers missed in v1.
> ---
> drivers/net/ethernet/microsoft/mana/mana_en.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c
> b/drivers/net/ethernet/microsoft/mana/mana_en.c
> index ea71de39f996..884f8e548174 100644
> --- a/drivers/net/ethernet/microsoft/mana/mana_en.c
> +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
> @@ -766,6 +766,13 @@ static void mana_get_rxbuf_cfg(struct
> mana_port_context *apc,
> }
>
> *frag_count = 1;
> +
> + /* In the single-buffer path, napi_build_skb() must see the
> + * actual backing allocation size so skb->truesize reflects
> + * the full page (or higher-order page), not just the usable
> + * packet area.
> + */
> + *alloc_size = PAGE_SIZE << get_order(*alloc_size);
> return;
> }
>
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH v2] selftests: net: broadcast_pmtu: Fix false failure from incorrect ping exit code logic
From: Jakub Kicinski @ 2026-03-26 14:58 UTC (permalink / raw)
To: fffsqian
Cc: davem, edumazet, pabeni, horms, shuah, netdev, linux-kselftest,
linux-kernel, Qingshuang Fu
In-Reply-To: <20260326094219.2160239-1-fffsqian@163.com>
On Thu, 26 Mar 2026 17:42:19 +0800 fffsqian@163.com wrote:
> The broadcast_pmtu.sh test verifies that broadcast route MTU is respected,
> but it uses an incorrect criteria for test success: it relies solely on
> the ping command's exit code, which leads to false failures.
Still failing, does it pass for you?
Also please read this -
https://www.kernel.org/doc/html/next/process/maintainer-netdev.html#tl-dr
^ 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