* Re: [PATCH net-next v4 1/2] net: dsa: realtek: rtl8365mb: add SGMII support for RTL8367S
From: Maxime Chevallier @ 2026-07-08 6:33 UTC (permalink / raw)
To: Johan Alvarado
Cc: linusw, alsi, andrew, olteanv, kuba, davem, edumazet, pabeni,
linux, luizluca, namiltd, netdev, linux-kernel
In-Reply-To: <0100019f3f45ab47-bab143e1-1dee-49f0-9b38-16fd0d84175e-000000@email.amazonses.com>
Hi,
On 7/8/26 03:09, Johan Alvarado wrote:
>
> [...]
>
>>> + /* The SerDes has its own pause controls; program them from
>>> + * the resolved pause modes, as the vendor driver does when
>>> + * forcing the link on a SerDes external interface. This is
>>> + * done here rather than in rtl8365mb_pcs_link_up() because
>>> + * pcs_link_up() carries no pause information.
>>> + */
>>> + if (rtl8365mb_interface_is_serdes(interface)) {
>>> + u32 val = 0;
>>> +
>>> + if (tx_pause)
>>> + val |= RTL8365MB_SDS_MISC_SGMII_TXFC_MASK;
>>> + if (rx_pause)
>>> + val |= RTL8365MB_SDS_MISC_SGMII_RXFC_MASK;
>>
>> Do you know what this does in HW ? Is this so that the PCS lets the Pause frames through
>> in either directions ?
>>
>> I suspect this is something that would be only used for inband advertising
>> of pause settings (in such case, you don't even need that), but ofc I'm not sure :)
>>
>> You already configure the MAC pause settings, can you test that these bits actually do
>> anything by exercising a bit flow control and checking if these registers are used ?
>
> I tested it on the hardware, and it turns out these bits are the
> operative pause controls for the SerDes port: pause frames are only
> ever emitted when SDS_MISC_SGMII_TXFC is set, and the MAC force-mode
> pause bits alone have no observable effect on this port. They are not
> tied to in-band advertisement either - they act with the link fully
> forced.
>
> For context, there is no public documentation for these bits, but in
> the vendor GPL code every write to them lives in
> rtl8367c_setAsicPortForceLinkExt(), the forced-link path, where they
> are programmed from the resolved tx/rx pause settings right alongside
> the MAC force-mode pause bits - never from the nway/in-band paths.
>
> Test setup: RTL8367S (Mercusys MR80X), CPU port 6 on HSGMII,
> 2500base-x fixed link to an IPQ5018. I enabled regmap debugfs writes
> so I could flip the bits live, and forced congestion by L2-forwarding
> ~350 Mbit/s of SoC-generated UDP in through the CPU port and out of a
> 100M user port (~3.5x oversubscription, ~135 MB per run), watching the
> CPU port's dot3OutPauseFrames MIB counter:
>
> everything cleared (baseline): 0 pause frames
> MAC force pause only (DIGITAL_INTERFACE_FORCE
> TXPAUSE|RXPAUSE, reg 0x1311): 0 pause frames
> MAC force pause + SDS TXFC|RXFC: 2396 pause frames
> SDS TXFC|RXFC only (MAC pause cleared): 2362 pause frames
> SDS TXFC only: 2934 pause frames
> SDS RXFC only: 0 pause frames
>
> RXFC did not influence pause emission, so by symmetry it presumably
> gates honouring received pause frames; I could not exercise that
> direction because the SoC MAC on this board never transmits pause.
>
> So the write has to stay - without it flow control simply does not
> work on SGMII/HSGMII. I will expand the comment in v5 to say that
> these are the functional pause enables for the SerDes, rather than
> describing it as mirroring the vendor forced-link sequence.
>
Thanks a lot for the investigation ! Ok so you do need to configure that
in the PCS. Maybe at some point we'll have to pass tx/rx negotiated pause
settings to the PCS, just like we to for the MAC, but it's strange as the
PCS isn't supposed to be the block handling pause :/
I'm ok leaving this as-is right now, you've added a nice comment on why
PCS regs are accessed in the MAC's link_up(). If we find other devices
that behave the same w.r.t. pause, we may have to pass these to the pcs_link_up,
but it's rather unusual I think.
Thanks :)
Maxime
^ permalink raw reply
* Re: [PATCH nf] ipvs: make destination flags atomic
From: Yizhou Zhao @ 2026-07-08 6:11 UTC (permalink / raw)
To: Julian Anastasov
Cc: Simon Horman, David Ahern, Ido Schimmel, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Pablo Neira Ayuso,
Florian Westphal, Phil Sutter, Alexander Frolkin, netdev,
lvs-devel, linux-kernel, netfilter-devel, coreteam, stable,
Yuxiang Yang, Ao Wang, Xuewei Feng, Qi Li, Ke Xu, zhaoyz24
In-Reply-To: <41c3d792-af7d-5582-5057-ac3df5f7bfd6@ssi.bg>
Hi Julian,
> On Jul 8, 2026, at 03:18, Julian Anastasov <ja@ssi.bg> wrote:
>
>
> Hello,
>
> On Tue, 7 Jul 2026, Yizhou Zhao wrote:
>
>> is_unavailable() in the SH scheduler reads dest->flags from the packet
>> scheduling path while holding only the RCU read lock. The same word is
>> updated by read-modify-write operations from connection accounting and
>> destination update paths, for example ip_vs_bind_dest(),
>> ip_vs_unbind_dest(), and __ip_vs_update_dest().
>>
>> The RCU read lock only protects the destination lifetime; it does not
>> serialize accesses to dest->flags. A racing plain load or RMW update can
>> therefore observe stale state or lose an AVAILABLE/OVERLOAD bit update,
>> which can make the scheduler choose an overloaded destination or report no
>> available destination even though one should be usable.
>
> While the patch correctly serializes the concurrent
> modifications for the flags, we can not claim that the scheduler
> will not choose an overloaded or unavailable destination.
> The patch does not change the fact that we can work with
> stale data.
>
> We can compare 3 solutions, from fast to slow:
>
> 1. atomic_read or test_bit
> - no memory barriers for the readers
> - no memory ordering (=> stale data)
>
> PRO:
> - serializes RMW operations
>
> CON:
> - readers can use old values
> - writers may need to synchronize while changing
> the flags, eg. to check the thresholds and update the
> flags in atomic way. We do not do this.
>
> 2. Use refcount_inc_not_zero(&dest->available) from readers
>
> - and put the ref immediately or later:
>
> smp_mb__before_atomic();
> refcount_dec(&dest->available);
>
> - alternative: RMW such as atomic_fetch_add
>
> - writers can synchronize by using the IP_VS_DEST_F_AVAILABLE
> flag and then to inc/dec &dest->available when the
> flag changes
> - the same can be done for &dest->not_overloaded and
> IP_VS_DEST_F_OVERLOAD
> - PRO: readers are serialized perfectly with the
> changed value, new packets will detect the changes
> immediately
> - CON:
> - 2 full memory barriers for the readers
> - writers may need to synchronize while changing
> the flags
>
> 3. read_lock/write_lock
> - PRO: can modify more things under write lock
> - CON: full memory barriers
>
> With this patch you choose solution 1.
> The other solutions can be expensive for the fast path.
> Lets fix the commit message. Also, it is better to fix the
> scripts/checkpatch.pl warnings about the 'if' conditions,
> even if they are not introduced now.
>
> Regards
>
> --
> Julian Anastasov <ja@ssi.bg>
Thank you for your suggestions.
We have posted a v2 patch at:
https://lore.kernel.org/netfilter-devel/20260708060454.20534-1-zhaoyz24@mails.tsinghua.edu.cn/
The v2 patch updates the commit message with more conservative
wording, and fixes the checkpatch logical-continuation warnings.
Regards,
Yizhou
^ permalink raw reply
* [PATCH net-next v3 2/2] ipv4: snapshot dst.dev in ip_rt_send_redirect() and ip_rt_get_source()
From: xuanqiang.luo @ 2026-07-08 6:05 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
David Ahern, Ido Schimmel
Cc: Simon Horman, Kuniyuki Iwashima, netdev, linux-kernel,
Xuanqiang Luo
In-Reply-To: <20260708060537.17188-1-xuanqiang.luo@linux.dev>
From: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
rt_flush_dev() can replace rt->dst.dev with blackhole_netdev while RCU
readers are running. ip_rt_send_redirect() and ip_rt_get_source() both
read rt->dst.dev more than once and use the results in one operation.
If rt->dst.dev changes between those reads, the operation can use values
from two devices. For example, ip_rt_send_redirect() can use in_dev from
the old device and the L3 master ifindex from blackhole_netdev.
Read rt->dst.dev once in these two functions and use the snapshot for the
later device accesses.
Signed-off-by: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
---
net/ipv4/route.c | 31 ++++++++++++++++++-------------
1 file changed, 18 insertions(+), 13 deletions(-)
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index b668375df71e2..4fed07cae7da2 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -874,21 +874,23 @@ void ip_rt_send_redirect(struct sk_buff *skb)
{
struct rtable *rt = skb_rtable(skb);
struct in_device *in_dev;
+ struct net_device *dev;
struct inet_peer *peer;
- struct net *net;
int log_martians;
+ struct net *net;
int vif;
rcu_read_lock();
- in_dev = __in_dev_get_rcu(rt->dst.dev);
+ dev = dst_dev_rcu(&rt->dst);
+ in_dev = __in_dev_get_rcu(dev);
if (!in_dev || !IN_DEV_TX_REDIRECTS(in_dev)) {
rcu_read_unlock();
return;
}
log_martians = IN_DEV_LOG_MARTIANS(in_dev);
- vif = l3mdev_master_ifindex_rcu(rt->dst.dev);
+ vif = l3mdev_master_ifindex_rcu(dev);
- net = dev_net(rt->dst.dev);
+ net = dev_net_rcu(dev);
peer = inet_getpeer_v4(net->ipv4.peers, ip_hdr(skb)->saddr, vif);
if (!peer) {
rcu_read_unlock();
@@ -1287,29 +1289,32 @@ void ip_rt_get_source(u8 *addr, struct sk_buff *skb, struct rtable *rt)
{
__be32 src;
- if (rt_is_output_route(rt))
+ rcu_read_lock();
+ if (rt_is_output_route(rt)) {
src = ip_hdr(skb)->saddr;
- else {
- struct fib_result res;
+ } else {
+ struct net_device *dev = dst_dev_rcu(&rt->dst);
+ struct net *net = dev_net_rcu(dev);
struct iphdr *iph = ip_hdr(skb);
+ struct fib_result res;
struct flowi4 fl4 = {
.daddr = iph->daddr,
.saddr = iph->saddr,
.flowi4_dscp = ip4h_dscp(iph),
- .flowi4_oif = rt->dst.dev->ifindex,
+ .flowi4_oif = dev->ifindex,
.flowi4_iif = skb->dev->ifindex,
.flowi4_mark = skb->mark,
};
- rcu_read_lock();
- if (fib_lookup(dev_net(rt->dst.dev), &fl4, &res, 0) == 0)
- src = fib_result_prefsrc(dev_net(rt->dst.dev), &res);
+ if (fib_lookup(net, &fl4, &res, 0) == 0)
+ src = fib_result_prefsrc(net, &res);
else
- src = inet_select_addr(rt->dst.dev,
+ src = inet_select_addr(dev,
rt_nexthop(rt, iph->daddr),
RT_SCOPE_UNIVERSE);
- rcu_read_unlock();
}
+ rcu_read_unlock();
+
memcpy(addr, &src, 4);
}
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v3 1/2] ipv4: use rcu_assign_pointer() in rt_flush_dev()
From: xuanqiang.luo @ 2026-07-08 6:05 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
David Ahern, Ido Schimmel
Cc: Simon Horman, Kuniyuki Iwashima, netdev, linux-kernel,
Xuanqiang Luo
In-Reply-To: <20260708060537.17188-1-xuanqiang.luo@linux.dev>
From: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
rt_flush_dev() replaces rt->dst.dev with blackhole_netdev on uncached
routes. The field is also exposed as dst.dev_rcu, and existing readers
use dst_dev_rcu().
Use rcu_assign_pointer() for the replacement, as dst_dev_put() already
does for the same field.
Signed-off-by: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
---
net/ipv4/route.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 3f3de5164d6e5..b668375df71e2 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1565,7 +1565,7 @@ void rt_flush_dev(struct net_device *dev)
list_for_each_entry_safe(rt, safe, &ul->head, dst.rt_uncached) {
if (rt->dst.dev != dev)
continue;
- rt->dst.dev = blackhole_netdev;
+ rcu_assign_pointer(rt->dst.dev_rcu, blackhole_netdev);
netdev_ref_replace(dev, blackhole_netdev,
&rt->dst.dev_tracker, GFP_ATOMIC);
list_del_init(&rt->dst.rt_uncached);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v3 0/2] ipv4: update rt_flush_dev() and two dst.dev readers
From: xuanqiang.luo @ 2026-07-08 6:05 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
David Ahern, Ido Schimmel
Cc: Simon Horman, Kuniyuki Iwashima, netdev, linux-kernel,
Xuanqiang Luo
From: Xuanqiang Luo <xuanqiang.luo@linux.dev>
Patch 1 makes the rt_flush_dev() write to rt->dst.dev use
rcu_assign_pointer(), matching the existing dst_dev_rcu() readers.
Patch 2 makes ip_rt_send_redirect() and ip_rt_get_source() use one
dst.dev snapshot throughout each operation, so a concurrent rt_flush_dev()
update cannot make them use values from two devices.
v3:
- Split the rt_flush_dev() change into a separate patch.
- Update PATCH 2 subject and commit message to describe the actual scope
more clearly.
- Drop the Fixes tag.
- Fix local variable ordering for netdev reverse xmas tree style.
Thanks to Ido Schimmel for the feedback.
v2: https://lore.kernel.org/lkml/20260701032434.17500-1-xuanqiang.luo@linux.dev/
- Use dst_dev_rcu() and dev_net_rcu() for the RCU readers.
- Use rcu_assign_pointer() when publishing the uncached route device
replacement.
- Slightly adjust the commit message wording because this issue was found
by inspection, not from an observed user-visible failure.
v1: https://lore.kernel.org/all/20260630094250.29386-1-xuanqiang.luo@linux.dev/
Xuanqiang Luo (2):
ipv4: use rcu_assign_pointer() in rt_flush_dev()
ipv4: snapshot dst.dev in ip_rt_send_redirect() and ip_rt_get_source()
net/ipv4/route.c | 33 +++++++++++++++++++--------------
1 file changed, 19 insertions(+), 14 deletions(-)
--
2.43.0
^ permalink raw reply
* [PATCH nf v2] ipvs: make destination flags atomic
From: Yizhou Zhao @ 2026-07-08 6:04 UTC (permalink / raw)
To: Simon Horman, Julian Anastasov, David Ahern, Ido Schimmel,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
Alexander Frolkin
Cc: Yizhou Zhao, netdev, lvs-devel, linux-kernel, netfilter-devel,
coreteam, stable, Yuxiang Yang, Ao Wang, Xuewei Feng, Qi Li,
Ke Xu
IPVS destination schedulers read dest->flags from packet processing paths
while holding only the RCU read lock. The same word is updated by plain
read-modify-write operations from connection accounting and destination
update paths, for example ip_vs_bind_dest(), ip_vs_unbind_dest(), and
__ip_vs_update_dest().
The RCU read lock protects the destination lifetime, but it does not
serialize accesses to dest->flags. A plain load can therefore race with a
plain write, and concurrent plain read-modify-write updates can lose an
AVAILABLE or OVERLOAD bit update.
KCSAN reports the race with a standard IPVS configuration using the SH
scheduler and a destination with u_threshold set:
BUG: KCSAN: data-race in __ip_vs_update_dest / ip_vs_sh_schedule
write to ... of 4 bytes by task ipvs_cfg:
__ip_vs_update_dest
ip_vs_edit_dest
do_ip_vs_set_ctl
__x64_sys_setsockopt
read to ... of 4 bytes by task ipvs_churn:
ip_vs_sh_schedule
ip_vs_schedule
tcp_conn_schedule
ip_vs_in_hook
tcp_connect
__x64_sys_connect
value changed: 0x00000003 -> 0x00000001
Convert dest->flags to atomic_t and use atomic_read(), atomic_or(), and
atomic_and() for all destination flag tests and updates. This preserves
the existing 32-bit field size while making the flag updates atomic RMW
operations and making readers use atomic accesses. Valid minimum-sized
IPVS configuration and scheduling paths are unchanged; only the
synchronization of the destination status flags changes.
This is limited to synchronizing the flags word itself. It does not add
ordering for readers, and it does not make scheduler decisions operate on a
fresh snapshot of all destination state; readers may still observe stale
state in the usual IPVS fast path. This keeps the packet fast path free
of additional barriers or locks.
Fixes: eba3b5a78799d ("ipvs: SH fallback and L4 hashing")
Cc: stable@vger.kernel.org
Reported-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
Reported-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn>
Reported-by: Ao Wang <wangao@seu.edu.cn>
Reported-by: Xuewei Feng <fengxw06@126.com>
Reported-by: Qi Li <qli01@tsinghua.edu.cn>
Reported-by: Ke Xu <xuke@tsinghua.edu.cn>
Assisted-by: Claude-Code:GLM-5.2
Signed-off-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
---
Changes in v2:
- Clarify that the patch fixes the flags data race and RMW lost updates,
but does not prevent readers from observing stale scheduling state.
- Fix checkpatch logical-continuation warnings.
- Suggested by Julian Anastasov.
- Link to v1: https://lore.kernel.org/netfilter-devel/20260707085706.96322-1-zhaoyz24@mails.tsinghua.edu.cn/
---
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
index 49297fec448a..bb969738ed73 100644
--- a/include/net/ip_vs.h
+++ b/include/net/ip_vs.h
@@ -972,7 +972,7 @@ struct ip_vs_dest {
u16 af; /* address family */
__be16 port; /* port number of the server */
union nf_inet_addr addr; /* IP address of the server */
- volatile unsigned int flags; /* dest status flags */
+ atomic_t flags; /* dest status flags */
atomic_t conn_flags; /* flags to copy to conn */
atomic_t weight; /* server weight */
atomic_t last_weight; /* server latest weight */
diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c
index cb36641f8d1c..539f603f38b7 100644
--- a/net/netfilter/ipvs/ip_vs_conn.c
+++ b/net/netfilter/ipvs/ip_vs_conn.c
@@ -1055,7 +1055,7 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
if (dest->u_threshold != 0 &&
ip_vs_dest_totalconns(dest) >= dest->u_threshold)
- dest->flags |= IP_VS_DEST_F_OVERLOAD;
+ atomic_or(IP_VS_DEST_F_OVERLOAD, &dest->flags);
}
@@ -1151,13 +1151,13 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp)
if (dest->l_threshold != 0) {
if (ip_vs_dest_totalconns(dest) < dest->l_threshold)
- dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
+ atomic_and(~IP_VS_DEST_F_OVERLOAD, &dest->flags);
} else if (dest->u_threshold != 0) {
if (ip_vs_dest_totalconns(dest) * 4 < dest->u_threshold * 3)
- dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
+ atomic_and(~IP_VS_DEST_F_OVERLOAD, &dest->flags);
} else {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
- dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
+ atomic_and(~IP_VS_DEST_F_OVERLOAD, &dest->flags);
}
ip_vs_dest_put(dest);
@@ -1188,7 +1188,7 @@ int ip_vs_check_template(struct ip_vs_conn *ct, struct ip_vs_dest *cdest)
* Checking the dest server status.
*/
if ((dest == NULL) ||
- !(dest->flags & IP_VS_DEST_F_AVAILABLE) ||
+ !(atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE) ||
expire_quiescent_template(ipvs, dest) ||
(cdest && (dest != cdest))) {
IP_VS_DBG_BUF(9, "check_template: dest not available for "
@@ -1929,7 +1929,7 @@ void ip_vs_expire_nodest_conn_flush(struct netns_ipvs *ipvs)
cp = ip_vs_hn0_to_conn(hn);
resched_score++;
dest = cp->dest;
- if (!dest || (dest->flags & IP_VS_DEST_F_AVAILABLE))
+ if (!dest || (atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE))
continue;
if (atomic_read(&cp->n_control))
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
index d40b404c1bf6..ca778937facf 100644
--- a/net/netfilter/ipvs/ip_vs_core.c
+++ b/net/netfilter/ipvs/ip_vs_core.c
@@ -302,7 +302,7 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
struct ip_vs_dest *dest = cp->dest;
struct netns_ipvs *ipvs = cp->ipvs;
- if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
+ if (dest && (atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE)) {
struct ip_vs_cpu_stats *s;
struct ip_vs_service *svc;
@@ -338,7 +338,7 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb)
struct ip_vs_dest *dest = cp->dest;
struct netns_ipvs *ipvs = cp->ipvs;
- if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
+ if (dest && (atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE)) {
struct ip_vs_cpu_stats *s;
struct ip_vs_service *svc;
@@ -2204,7 +2204,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
}
/* Check the server status */
- if (cp && cp->dest && !(cp->dest->flags & IP_VS_DEST_F_AVAILABLE)) {
+ if (cp && cp->dest && !(atomic_read(&cp->dest->flags) & IP_VS_DEST_F_AVAILABLE)) {
/* the destination server is not available */
if (sysctl_expire_nodest_conn(ipvs)) {
bool old_ct = ip_vs_conn_uses_old_conntrack(cp, skb);
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index bcf40b8c41cf..685b2675b6e0 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -1368,10 +1368,10 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
}
/* set the dest status flags */
- dest->flags |= IP_VS_DEST_F_AVAILABLE;
+ atomic_or(IP_VS_DEST_F_AVAILABLE, &dest->flags);
if (udest->u_threshold == 0 || udest->u_threshold > dest->u_threshold)
- dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
+ atomic_and(~IP_VS_DEST_F_OVERLOAD, &dest->flags);
dest->u_threshold = udest->u_threshold;
dest->l_threshold = udest->l_threshold;
@@ -1613,7 +1613,7 @@ static void __ip_vs_unlink_dest(struct ip_vs_service *svc,
struct ip_vs_dest *dest,
int svcupd)
{
- dest->flags &= ~IP_VS_DEST_F_AVAILABLE;
+ atomic_and(~IP_VS_DEST_F_AVAILABLE, &dest->flags);
spin_lock_bh(&dest->dst_lock);
__ip_vs_dst_cache_reset(dest);
diff --git a/net/netfilter/ipvs/ip_vs_dh.c b/net/netfilter/ipvs/ip_vs_dh.c
index e1f62f6b25e2..82492e824f02 100644
--- a/net/netfilter/ipvs/ip_vs_dh.c
+++ b/net/netfilter/ipvs/ip_vs_dh.c
@@ -201,7 +201,7 @@ static int ip_vs_dh_dest_changed(struct ip_vs_service *svc,
*/
static inline int is_overloaded(struct ip_vs_dest *dest)
{
- return dest->flags & IP_VS_DEST_F_OVERLOAD;
+ return atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD;
}
@@ -219,10 +219,10 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
s = (struct ip_vs_dh_state *) svc->sched_data;
dest = ip_vs_dh_get(svc->af, s, &iph->daddr);
- if (!dest
- || !(dest->flags & IP_VS_DEST_F_AVAILABLE)
- || atomic_read(&dest->weight) <= 0
- || is_overloaded(dest)) {
+ if (!dest ||
+ !(atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE) ||
+ atomic_read(&dest->weight) <= 0 ||
+ is_overloaded(dest)) {
ip_vs_scheduler_err(svc, "no destination available");
return NULL;
}
diff --git a/net/netfilter/ipvs/ip_vs_fo.c b/net/netfilter/ipvs/ip_vs_fo.c
index d657b47c6511..5231e518c07c 100644
--- a/net/netfilter/ipvs/ip_vs_fo.c
+++ b/net/netfilter/ipvs/ip_vs_fo.c
@@ -29,7 +29,7 @@ ip_vs_fo_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
* Find virtual server with highest weight and send it traffic
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
+ if (!(atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD) &&
atomic_read(&dest->weight) > hw) {
hweight = dest;
hw = atomic_read(&dest->weight);
diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c
index 15ccb2b2fa1f..d2eb5dda5b68 100644
--- a/net/netfilter/ipvs/ip_vs_lblc.c
+++ b/net/netfilter/ipvs/ip_vs_lblc.c
@@ -414,7 +414,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc)
* new connection.
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
if (atomic_read(&dest->weight) > 0) {
least = dest;
@@ -429,7 +429,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc)
*/
nextstage:
list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
doh = ip_vs_dest_conn_overhead(dest);
@@ -502,7 +502,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
dest = en->dest;
- if ((dest->flags & IP_VS_DEST_F_AVAILABLE) &&
+ if ((atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE) &&
atomic_read(&dest->weight) > 0 && !is_overloaded(dest, svc))
goto out;
}
diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c
index c90ea897c3f7..48f02453a5be 100644
--- a/net/netfilter/ipvs/ip_vs_lblcr.c
+++ b/net/netfilter/ipvs/ip_vs_lblcr.c
@@ -166,11 +166,11 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
/* select the first destination server, whose weight > 0 */
list_for_each_entry_rcu(e, &set->list, list) {
least = e->dest;
- if (least->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&least->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
- if ((atomic_read(&least->weight) > 0)
- && (least->flags & IP_VS_DEST_F_AVAILABLE)) {
+ if ((atomic_read(&least->weight) > 0) &&
+ (atomic_read(&least->flags) & IP_VS_DEST_F_AVAILABLE)) {
loh = ip_vs_dest_conn_overhead(least);
goto nextstage;
}
@@ -181,13 +181,13 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set)
nextstage:
list_for_each_entry_continue_rcu(e, &set->list, list) {
dest = e->dest;
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
doh = ip_vs_dest_conn_overhead(dest);
if (((__s64)loh * atomic_read(&dest->weight) >
- (__s64)doh * atomic_read(&least->weight))
- && (dest->flags & IP_VS_DEST_F_AVAILABLE)) {
+ (__s64)doh * atomic_read(&least->weight)) &&
+ (atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE)) {
least = dest;
loh = doh;
}
@@ -577,7 +577,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc)
* new connection.
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
if (atomic_read(&dest->weight) > 0) {
@@ -593,7 +593,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc)
*/
nextstage:
list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
doh = ip_vs_dest_conn_overhead(dest);
diff --git a/net/netfilter/ipvs/ip_vs_lc.c b/net/netfilter/ipvs/ip_vs_lc.c
index 38cc38c5d8bb..6acb3c904af5 100644
--- a/net/netfilter/ipvs/ip_vs_lc.c
+++ b/net/netfilter/ipvs/ip_vs_lc.c
@@ -38,7 +38,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if ((dest->flags & IP_VS_DEST_F_OVERLOAD) ||
+ if ((atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD) ||
atomic_read(&dest->weight) == 0)
continue;
doh = ip_vs_dest_conn_overhead(dest);
diff --git a/net/netfilter/ipvs/ip_vs_mh.c b/net/netfilter/ipvs/ip_vs_mh.c
index 020863047562..c322ed1754b7 100644
--- a/net/netfilter/ipvs/ip_vs_mh.c
+++ b/net/netfilter/ipvs/ip_vs_mh.c
@@ -80,7 +80,7 @@ static inline void generate_hash_secret(hsiphash_key_t *hash1,
static inline bool is_unavailable(struct ip_vs_dest *dest)
{
return atomic_read(&dest->weight) <= 0 ||
- dest->flags & IP_VS_DEST_F_OVERLOAD;
+ atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD;
}
/* Returns hash value for IPVS MH entry */
diff --git a/net/netfilter/ipvs/ip_vs_nq.c b/net/netfilter/ipvs/ip_vs_nq.c
index ada158c610ce..ffa4bfeb21d9 100644
--- a/net/netfilter/ipvs/ip_vs_nq.c
+++ b/net/netfilter/ipvs/ip_vs_nq.c
@@ -72,7 +72,7 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD ||
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD ||
!atomic_read(&dest->weight))
continue;
diff --git a/net/netfilter/ipvs/ip_vs_ovf.c b/net/netfilter/ipvs/ip_vs_ovf.c
index c5c67df80a0b..f7f17dddbb05 100644
--- a/net/netfilter/ipvs/ip_vs_ovf.c
+++ b/net/netfilter/ipvs/ip_vs_ovf.c
@@ -33,7 +33,7 @@ ip_vs_ovf_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
w = atomic_read(&dest->weight);
- if ((dest->flags & IP_VS_DEST_F_OVERLOAD) ||
+ if ((atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD) ||
atomic_read(&dest->activeconns) > w ||
w == 0)
continue;
diff --git a/net/netfilter/ipvs/ip_vs_rr.c b/net/netfilter/ipvs/ip_vs_rr.c
index 4125ee561cdc..98453d205d6f 100644
--- a/net/netfilter/ipvs/ip_vs_rr.c
+++ b/net/netfilter/ipvs/ip_vs_rr.c
@@ -66,7 +66,7 @@ ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
list_for_each_entry_continue_rcu(dest,
&svc->destinations,
n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
+ if (!(atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD) &&
atomic_read(&dest->weight) > 0)
/* HIT */
goto out;
diff --git a/net/netfilter/ipvs/ip_vs_sed.c b/net/netfilter/ipvs/ip_vs_sed.c
index 245a323c84cd..0249062d1360 100644
--- a/net/netfilter/ipvs/ip_vs_sed.c
+++ b/net/netfilter/ipvs/ip_vs_sed.c
@@ -75,7 +75,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
+ if (!(atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD) &&
atomic_read(&dest->weight) > 0) {
least = dest;
loh = ip_vs_sed_dest_overhead(least);
@@ -90,7 +90,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
nextstage:
list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
doh = ip_vs_sed_dest_overhead(dest);
if ((__s64)loh * atomic_read(&dest->weight) >
diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c
index cd67066e3b26..343780b82c95 100644
--- a/net/netfilter/ipvs/ip_vs_sh.c
+++ b/net/netfilter/ipvs/ip_vs_sh.c
@@ -73,7 +73,7 @@ struct ip_vs_sh_state {
static inline bool is_unavailable(struct ip_vs_dest *dest)
{
return atomic_read(&dest->weight) <= 0 ||
- dest->flags & IP_VS_DEST_F_OVERLOAD;
+ atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD;
}
/*
diff --git a/net/netfilter/ipvs/ip_vs_twos.c b/net/netfilter/ipvs/ip_vs_twos.c
index dbb7f5fd4688..35fa4c6dc5cf 100644
--- a/net/netfilter/ipvs/ip_vs_twos.c
+++ b/net/netfilter/ipvs/ip_vs_twos.c
@@ -52,7 +52,7 @@ static struct ip_vs_dest *ip_vs_twos_schedule(struct ip_vs_service *svc,
/* Generate a random weight between [0,sum of all weights) */
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD)) {
+ if (!(atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)) {
weight = atomic_read(&dest->weight);
if (weight > 0) {
total_weight += weight;
@@ -75,7 +75,7 @@ static struct ip_vs_dest *ip_vs_twos_schedule(struct ip_vs_service *svc,
/* Pick two weighted servers */
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
weight = atomic_read(&dest->weight);
diff --git a/net/netfilter/ipvs/ip_vs_wlc.c b/net/netfilter/ipvs/ip_vs_wlc.c
index 9da445ca09a1..c2d09ac96fe8 100644
--- a/net/netfilter/ipvs/ip_vs_wlc.c
+++ b/net/netfilter/ipvs/ip_vs_wlc.c
@@ -47,7 +47,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
+ if (!(atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD) &&
atomic_read(&dest->weight) > 0) {
least = dest;
loh = ip_vs_dest_conn_overhead(least);
@@ -62,7 +62,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
*/
nextstage:
list_for_each_entry_continue_rcu(dest, &svc->destinations, n_list) {
- if (dest->flags & IP_VS_DEST_F_OVERLOAD)
+ if (atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD)
continue;
doh = ip_vs_dest_conn_overhead(dest);
if ((__s64)loh * atomic_read(&dest->weight) >
diff --git a/net/netfilter/ipvs/ip_vs_wrr.c b/net/netfilter/ipvs/ip_vs_wrr.c
index 2dcff1040da5..f21a75284971 100644
--- a/net/netfilter/ipvs/ip_vs_wrr.c
+++ b/net/netfilter/ipvs/ip_vs_wrr.c
@@ -176,7 +176,7 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
list_for_each_entry_continue_rcu(dest,
&svc->destinations,
n_list) {
- if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
+ if (!(atomic_read(&dest->flags) & IP_VS_DEST_F_OVERLOAD) &&
atomic_read(&dest->weight) >= mark->cw)
goto found;
if (dest == stop)
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index ce542ed4b013..37b9671e4960 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -351,7 +351,7 @@ __ip_vs_get_out_rt(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
* stored in dest_trash.
*/
if (!rt_dev_is_down(dst_dev_rcu(&rt->dst)) &&
- dest->flags & IP_VS_DEST_F_AVAILABLE)
+ atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE)
__ip_vs_dst_set(dest, dest_dst, &rt->dst, 0);
else
noref = 0;
@@ -530,7 +530,7 @@ __ip_vs_get_out_rt_v6(struct netns_ipvs *ipvs, int skb_af, struct sk_buff *skb,
* stored in dest_trash.
*/
if (!rt_dev_is_down(dst_dev_rcu(&rt->dst)) &&
- dest->flags & IP_VS_DEST_F_AVAILABLE)
+ atomic_read(&dest->flags) & IP_VS_DEST_F_AVAILABLE)
__ip_vs_dst_set(dest, dest_dst, &rt->dst, cookie);
else
noref = 0;
^ permalink raw reply related
* Re: [PATCH net-next v14 0/9] tls: Add TLS 1.3 hardware offload support
From: Nils Juenemann @ 2026-07-08 6:03 UTC (permalink / raw)
To: rjethwani
Cc: borisp, davem, edumazet, john.fastabend, kuba, leon, mbloch,
netdev, nils.juenemann, pabeni, saeedm, sd, tariqt
In-Reply-To: <CAKaoeS3RbLP=D1G6HHaSfQLj4VSi_W=HZ9EnKxKia_EjcT1vSw@mail.gmail.com>
On Tue, Jul 7, 2026 at 5:31 PM Rishikesh Jethwani <rjethwani@everpuredata.com> wrote:
>
> Could you post the disassembly [...] count how many distinct loads of
> sk+0x4f8 the compiler emitted in that region.
objdump of mlx5e_ktls_handle_rx_skb, CQE_TLS_OFFLOAD_RESYNC branch
(+0x63) through RIP+0x10a; bytes match the oops Code around the fault:
11bd03: 48 83 82 90 01 00 00 addq $0x1,0x190(%rdx)
11bd0a: 01
11bd0b: 48 8b b6 d0 00 00 00 mov 0xd0(%rsi),%rsi
11bd12: 49 89 fc mov %rdi,%r12
11bd15: 4c 8b b7 00 01 00 00 mov 0x100(%rdi),%r14
11bd1c: 0f b7 46 0c movzwl 0xc(%rsi),%eax
11bd20: 4d 8b be 08 01 00 00 mov 0x108(%r14),%r15
11bd27: 66 3d 81 00 cmp $0x81,%ax
11bd2b: 0f 84 a4 01 00 00 je 11bed5 <mlx5e_ktls_handle_rx_skb+0x235>
11bd31: 66 3d 88 a8 cmp $0xa888,%ax
11bd35: 0f 84 9a 01 00 00 je 11bed5 <mlx5e_ktls_handle_rx_skb+0x235>
11bd3b: 0f b7 43 78 movzwl 0x78(%rbx),%eax
11bd3f: 49 89 c5 mov %rax,%r13
11bd42: 48 01 c6 add %rax,%rsi
11bd45: 45 8b 8e e0 00 00 00 mov 0xe0(%r14),%r9d
11bd4c: 0f b6 06 movzbl (%rsi),%eax
11bd4f: 83 e0 f0 and $0xfffffff0,%eax
11bd52: 3c 40 cmp $0x40,%al
11bd54: 0f 84 a8 01 00 00 je 11bf02 <mlx5e_ktls_handle_rx_skb+0x262>
11bd5a: 44 0f b7 46 2a movzwl 0x2a(%rsi),%r8d
11bd5f: 41 8d 45 28 lea 0x28(%r13),%eax
11bd63: 0f b7 56 28 movzwl 0x28(%rsi),%edx
11bd67: 4c 89 ff mov %r15,%rdi
11bd6a: 89 45 d4 mov %eax,-0x2c(%rbp)
11bd6d: 48 8d 4e 18 lea 0x18(%rsi),%rcx
11bd71: 4c 8d 76 28 lea 0x28(%rsi),%r14
11bd75: 48 83 c6 08 add $0x8,%rsi
11bd79: 6a 00 push $0x0
11bd7b: 66 41 c1 c0 08 rol $0x8,%r8w
11bd80: 45 0f b7 c0 movzwl %r8w,%r8d
11bd84: e8 00 00 00 00 call 11bd89 <mlx5e_ktls_handle_rx_skb+0xe9>
11bd89: 49 89 c7 mov %rax,%r15
11bd8c: 58 pop %rax
11bd8d: 4d 85 ff test %r15,%r15
11bd90: 0f 84 4a ff ff ff je 11bce0 <mlx5e_ktls_handle_rx_skb+0x40>
11bd96: 41 0f b6 47 12 movzbl 0x12(%r15),%eax
11bd9b: 3c 06 cmp $0x6,%al
11bd9d: 0f 84 cc 00 00 00 je 11be6f <mlx5e_ktls_handle_rx_skb+0x1cf>
11bda3: 49 8b 87 f8 04 00 00 mov 0x4f8(%r15),%rax
11bdaa: 48 8b 40 30 mov 0x30(%rax),%rax
One load of sk+0x4f8 in this range, at 11bda3 (the fault). r15 only
becomes sk at 11bd89 (mov %rax,%r15), so there is no sk+0x4f8
dereference before 11bda3 in this range. The only other sk+0x4f8
load in the function is 11be20, after the fault and not reached. So my
earlier "two independent loads, second reading NULL" was wrong.
Thanks,
Nils
^ permalink raw reply
* [PATCH net-next v2] net: skbuff: optimization of net_zcopy_get() call in pskb_carve helpers
From: Yun Lu @ 2026-07-08 5:54 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, horms, kerneljasonxing, kuniyu,
willemdebruijn.kernel
Cc: mhal, bjorn, jiayuan.chen, netdev
From: Yun Lu <luyun@kylinos.cn>
Commit 98d0912e9f84 ("net: skbuff: fix missing zerocopy reference in
pskb_carve helpers") introduced two calls of net_zcopy_get(skb_zcopy(skb)).
In fact, skb_zcopy() has already been executed once before. When calling
net_zcopy_get(), skb_zcopy() always returns skb_uarg(skb), which results
in adding some unnecessary instructions in skb_zcopy. So, change these
two calls to directly use skb_uarg(skb) instead of skb_zcopy.
In addition, also use net_zcopy_get() instead of refcount_inc() in
pskb_expand_head() for code consistency.
No functional change intended.
Signed-off-by: Yun Lu <luyun@kylinos.cn>
---
Changes in v2:
- Directly use skb_uarg(skb) instead of skb_zcopy.
Thanks: Willem de Bruijn
- Links to v1: https://lore.kernel.org/all/20260706100229.13812-1-luyun_611@163.com/
---
net/core/skbuff.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 18dabb4e9cfa..d798fbdc3da7 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2326,7 +2326,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
if (skb_orphan_frags(skb, gfp_mask))
goto nofrags;
if (skb_zcopy(skb))
- refcount_inc(&skb_uarg(skb)->refcnt);
+ net_zcopy_get(skb_uarg(skb));
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
skb_frag_ref(skb, i);
@@ -6842,7 +6842,7 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off,
return -ENOMEM;
}
if (skb_zcopy(skb))
- net_zcopy_get(skb_zcopy(skb));
+ net_zcopy_get(skb_uarg(skb));
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
skb_frag_ref(skb, i);
if (skb_has_frag_list(skb))
@@ -6992,7 +6992,7 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off,
return -ENOMEM;
}
if (skb_zcopy(skb))
- net_zcopy_get(skb_zcopy(skb));
+ net_zcopy_get(skb_uarg(skb));
skb_release_data(skb, SKB_CONSUMED);
skb->head = data;
--
2.43.0
^ permalink raw reply related
* Re: [PATCH nf] netfilter: nf_conncount: fix zone comparison in tuple dedup
From: Yizhou Zhao @ 2026-07-08 5:30 UTC (permalink / raw)
To: Florian Westphal
Cc: netfilter-devel, Pablo Neira Ayuso, Phil Sutter, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman, coreteam,
netdev, linux-kernel, Yuxiang Yang, Ao Wang, Xuewei Feng, Qi Li,
Ke Xu, stable
In-Reply-To: <ak0UDVc4gZbfzrtM@strlen.de>
Hi Florian,
> On Jul 7, 2026, at 22:58, Florian Westphal <fw@strlen.de> wrote:
>
> Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn> wrote:
>> The "already exists" dedup logic in __nf_conncount_add() decides
>> whether a connection has already been counted and can be skipped instead
>> of incrementing the connlimit count. It compares the conntrack zone of a
>> list entry with the zone of the connection being added using
>> nf_ct_zone_id() and nf_ct_zone_equal(), passing conn->zone.dir or
>> zone->dir as the direction argument.
>
> Right, thats bogus.
>
>> @@ -211,8 +220,10 @@ static int __nf_conncount_add(struct net *net,
>> /* Not found, but might be about to be confirmed */
>> if (PTR_ERR(found) == -EAGAIN) {
>> if (nf_ct_tuple_equal(&conn->tuple, &tuple) &&
>> - nf_ct_zone_id(&conn->zone, conn->zone.dir) ==
>> - nf_ct_zone_id(zone, zone->dir))
>> + nf_ct_zone_id(&conn->zone,
>> + nf_conncount_zone_dir(&conn->zone)) ==
>> + nf_ct_zone_id(zone,
>> + nf_conncount_zone_dir(zone)))
>
> Should this be a simpler:
>
> if (nf_ct_tuple_equal(&conn->tuple, &tuple) &&
> - nf_ct_zone_id(&conn->zone, conn->zone.dir) ==
> - nf_ct_zone_id(zone, zone->dir))
> + nf_ct_zone_equal(&conn->zone, &zone), IP_CT_DIR_ORIGINAL)
>
> ?
>
> The tuple is always the 'original' direction, so it would follow that
> we should not care about reply zone dir.
>
> Also see:
> https://sashiko.dev/#/patchset/20260706114820.74006-1-zhaoyz24%40mails.tsinghua.edu.cn
Thank you for pointing out this.
We have published a v2 patch following your suggestions:
https://lore.kernel.org/netfilter-devel/20260708052730.18354-1-zhaoyz24@mails.tsinghua.edu.cn/
Thanks,
Yizhou
^ permalink raw reply
* [PATCH nf v2] netfilter: nf_conncount: fix zone comparison in tuple dedup
From: Yizhou Zhao @ 2026-07-08 5:27 UTC (permalink / raw)
To: netfilter-devel
Cc: Yizhou Zhao, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, coreteam, netdev, linux-kernel, Yuxiang Yang,
Ao Wang, Xuewei Feng, Qi Li, Ke Xu, stable
The "already exists" dedup logic in __nf_conncount_add() decides
whether a connection has already been counted and can be skipped instead
of incrementing the connlimit count. It compares the conntrack zone of a
list entry with the zone of the connection being added using
nf_ct_zone_id() and nf_ct_zone_equal(), passing conn->zone.dir or
zone->dir as the direction argument.
Those helpers take enum ip_conntrack_dir values: IP_CT_DIR_ORIGINAL is 0
and IP_CT_DIR_REPLY is 1. However, zone->dir is a u8 bitmask:
NF_CT_ZONE_DIR_ORIG is 1, NF_CT_ZONE_DIR_REPL is 2 and
NF_CT_DEFAULT_ZONE_DIR is 3. Passing that bitmask as the enum direction
shifts the meaning of every non-zero value. An ORIG-only zone passes 1
and is tested as REPLY, while REPL-only and default zones pass 2 or 3 and
test bits beyond the valid direction range. In those cases
nf_ct_zone_id() can fall back to NF_CT_DEFAULT_ZONE_ID instead of using
the real zone id, so different zones can be treated as equal and dedup
collapses to tuple equality alone.
nf_conncount stores and compares the original-direction tuple for a
connection. If an skb already has an attached conntrack entry,
get_ct_or_tuple_from_skb() explicitly copies
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, regardless of the packet's
ctinfo. Therefore the zone comparison in the tuple dedup path must use
IP_CT_DIR_ORIGINAL as well; the zone direction bitmask describes where a
zone id applies, not which direction this conncount tuple represents.
Fix the two dedup comparisons by passing IP_CT_DIR_ORIGINAL directly.
Do not special-case NF_CT_DEFAULT_ZONE_DIR and do not compare raw zone
ids: using the existing helpers with IP_CT_DIR_ORIGINAL preserves the
direction-aware NF_CT_DEFAULT_ZONE_ID fallback. A default bidirectional
zone contains the ORIG bit, so it naturally returns the real zone id;
reply-only zones continue to fall back for original-direction tuple
comparisons.
Fixes: 21ba8847f857 ("netfilter: nf_conncount: Fix garbage collection with zones")
Fixes: b36e4523d4d5 ("netfilter: nf_conncount: fix garbage collection confirm race")
Cc: stable@vger.kernel.org
Reported-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
Reported-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn>
Reported-by: Ao Wang <wangao@seu.edu.cn>
Reported-by: Xuewei Feng <fengxw06@126.com>
Reported-by: Qi Li <qli01@tsinghua.edu.cn>
Reported-by: Ke Xu <xuke@tsinghua.edu.cn>
Assisted-by: Claude-Code:GLM-5.2
Signed-off-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
---
Changes in v2:
- Use IP_CT_DIR_ORIGINAL directly instead of adding a helper to map
zone->dir to an enum direction, suggested by Florian Westphal.
- Link to v1: https://lore.kernel.org/netfilter-devel/20260706114820.74006-1-zhaoyz24@mails.tsinghua.edu.cn/
---
diff --git a/net/netfilter/nf_conncount.c b/net/netfilter/nf_conncount.c
index 91582069f6d2..e9ea6d9466e7 100644
--- a/net/netfilter/nf_conncount.c
+++ b/net/netfilter/nf_conncount.c
@@ -211,8 +211,8 @@ static int __nf_conncount_add(struct net *net,
/* Not found, but might be about to be confirmed */
if (PTR_ERR(found) == -EAGAIN) {
if (nf_ct_tuple_equal(&conn->tuple, &tuple) &&
- nf_ct_zone_id(&conn->zone, conn->zone.dir) ==
- nf_ct_zone_id(zone, zone->dir))
+ nf_ct_zone_id(&conn->zone, IP_CT_DIR_ORIGINAL) ==
+ nf_ct_zone_id(zone, IP_CT_DIR_ORIGINAL))
goto out_put; /* already exists */
} else {
collect++;
@@ -223,7 +223,7 @@ static int __nf_conncount_add(struct net *net,
found_ct = nf_ct_tuplehash_to_ctrack(found);
if (nf_ct_tuple_equal(&conn->tuple, &tuple) &&
- nf_ct_zone_equal(found_ct, zone, zone->dir)) {
+ nf_ct_zone_equal(found_ct, zone, IP_CT_DIR_ORIGINAL)) {
/*
* We should not see tuples twice unless someone hooks
* this into a table without "-p tcp --syn".
--
2.47.3
^ permalink raw reply related
* Re: [PATCH bpf] bpf, sockmap: Account for receive queue in FIONREAD without a verdict program
From: Kumar Kartikeya Dwivedi @ 2026-07-08 4:43 UTC (permalink / raw)
To: mattia.meleleo, John Fastabend, Jakub Sitnicki, Jiayuan Chen; +Cc: netdev, bpf
In-Reply-To: <20260707-fionread-no-verdict-v1-1-ce94a72357ec@coralogix.com>
On Tue Jul 7, 2026 at 6:15 PM CEST, Mattia Meleleo via B4 Relay wrote:
> From: Mattia Meleleo <mattia.meleleo@coralogix.com>
>
> tcp_bpf_ioctl() answers SIOCINQ from psock->msg_tot_len, which only
> counts bytes in ingress_msg. Without a stream/skb verdict program
> nothing is diverted there: data stays in sk_receive_queue, so FIONREAD
> returns 0 even though read() returns data.
>
> Add tcp_inq() to the reported value when the psock has no verdict
> program. The two queues are disjoint, so bytes redirected into
> ingress_msg from other sockets stay correctly accounted through
> msg_tot_len.
>
> Add a selftest covering FIONREAD without a verdict program.
>
> Fixes: 929e30f93125 ("bpf, sockmap: Fix FIONREAD for sockmap")
> Signed-off-by: Mattia Meleleo <mattia.meleleo@coralogix.com>
> ---
Please respin with the fix and selftest as separate patches, but still targeting
bpf tree. You can add the Reviewed-by: tags to both.
pw-bot: cr
> [...]
^ permalink raw reply
* Re: [PATCH bpf] bpf, sockmap: Account for receive queue in FIONREAD without a verdict program
From: Kumar Kartikeya Dwivedi @ 2026-07-08 4:41 UTC (permalink / raw)
To: Emil Tsalapatis, mattia.meleleo, John Fastabend, Jakub Sitnicki,
Jiayuan Chen
Cc: netdev, bpf
In-Reply-To: <DJSIEQSX3AFI.341HNU79LHFEF@etsalapatis.com>
On Tue Jul 7, 2026 at 7:19 PM CEST, Emil Tsalapatis wrote:
> On Tue Jul 7, 2026 at 12:15 PM EDT, Mattia Meleleo via B4 Relay wrote:
>> From: Mattia Meleleo <mattia.meleleo@coralogix.com>
>>
>> tcp_bpf_ioctl() answers SIOCINQ from psock->msg_tot_len, which only
>> counts bytes in ingress_msg. Without a stream/skb verdict program
>> nothing is diverted there: data stays in sk_receive_queue, so FIONREAD
>> returns 0 even though read() returns data.
>>
>> Add tcp_inq() to the reported value when the psock has no verdict
>> program. The two queues are disjoint, so bytes redirected into
>> ingress_msg from other sockets stay correctly accounted through
>> msg_tot_len.
>>
>> Add a selftest covering FIONREAD without a verdict program.
>>
>> Fixes: 929e30f93125 ("bpf, sockmap: Fix FIONREAD for sockmap")
>> Signed-off-by: Mattia Meleleo <mattia.meleleo@coralogix.com>
>
> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
>
> I don't think the Sashiko READ_ONCE() recommendation is that important,
> we're just checking for the pointers' existence and I don't see how the
> reads can be moved/optimized out/merged in a way that breaks this code.
>
It would be necessary if the xchg() can happen even when the lock is held, at
the very least, to suppress potential KCSAN warnings, I think. Even for the
theoretical load tearing that causes false positive, it would be benign due to
wrong accounting.
>> ---
>> net/ipv4/tcp_bpf.c | 16 ++++++++-
>> .../selftests/bpf/prog_tests/sockmap_basic.c | 39 ++++++++++++++++++++++
>> 2 files changed, 54 insertions(+), 1 deletion(-)
>>
>> diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c
>> index cc0bd73f3..a001b1fff 100644
>> --- a/net/ipv4/tcp_bpf.c
>> +++ b/net/ipv4/tcp_bpf.c
>> @@ -334,6 +334,7 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
>>
>> static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg)
>> {
>> + struct sk_psock *psock;
>> bool slow;
>>
>> if (cmd != SIOCINQ)
>> @@ -344,7 +345,20 @@ static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg)
>> return -EINVAL;
>>
>> slow = lock_sock_fast(sk);
>> - *karg = sk_psock_msg_inq(sk);
>> + psock = sk_psock_get(sk);
>> + if (unlikely(!psock)) {
>> + unlock_sock_fast(sk, slow);
>> + return tcp_ioctl(sk, cmd, karg);
>> + }
>> + *karg = sk_psock_get_msg_len_nolock(psock);
>> + /* Without a verdict program, ingress data is never diverted to
>> + * ingress_msg: it stays in sk_receive_queue and is read through
>> + * the fallback to tcp_recvmsg(), so account for it like
>> + * tcp_ioctl() does.
>> + */
>> + if (!psock->progs.stream_verdict && !psock->progs.skb_verdict)
>> + *karg += tcp_inq(sk);
>> + sk_psock_put(sk, psock);
>> unlock_sock_fast(sk, slow);
>>
>> return 0;
>> diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
>> index cb3229711..f0f368201 100644
>> --- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
>> +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
>> @@ -1373,6 +1373,43 @@ static void test_sockmap_multi_channels(int sotype)
>> test_sockmap_pass_prog__destroy(skel);
>> }
>>
>> +/* A socket in a sockmap without a verdict program keeps its ingress data
>> + * in sk_receive_queue: FIONREAD must account for it.
>> + */
>> +static void test_sockmap_no_verdict_fionread(void)
>> +{
>> + int err, map, zero = 0, sent, avail;
>> + int c0 = -1, c1 = -1, p0 = -1, p1 = -1;
>> + struct test_sockmap_pass_prog *skel;
>> + char buf[256] = "0123456789";
>> +
>> + skel = test_sockmap_pass_prog__open_and_load();
>> + if (!ASSERT_OK_PTR(skel, "open_and_load"))
>> + return;
>> + map = bpf_map__fd(skel->maps.sock_map_rx);
>> +
>> + err = create_socket_pairs(AF_INET, SOCK_STREAM, &c0, &c1, &p0, &p1);
>> + if (!ASSERT_OK(err, "create_socket_pairs()"))
>> + goto out;
>> +
>> + err = bpf_map_update_elem(map, &zero, &c1, BPF_NOEXIST);
>> + if (!ASSERT_OK(err, "bpf_map_update_elem(c1)"))
>> + goto out_close;
>> +
>> + sent = xsend(p1, &buf, sizeof(buf), 0);
>> + ASSERT_EQ(sent, sizeof(buf), "xsend(p1)");
>> + avail = wait_for_fionread(c1, sizeof(buf), IO_TIMEOUT_SEC);
>> + ASSERT_EQ(avail, sizeof(buf), "ioctl(FIONREAD)");
>> +
>> +out_close:
>> + close(c0);
>> + close(p0);
>> + close(c1);
>> + close(p1);
>> +out:
>> + test_sockmap_pass_prog__destroy(skel);
>> +}
>> +
>> void test_sockmap_basic(void)
>> {
>> if (test__start_subtest("sockmap create_update_free"))
>> @@ -1415,6 +1452,8 @@ void test_sockmap_basic(void)
>> test_sockmap_skb_verdict_shutdown();
>> if (test__start_subtest("sockmap skb_verdict fionread"))
>> test_sockmap_skb_verdict_fionread(true);
>> + if (test__start_subtest("sockmap no_verdict fionread"))
>> + test_sockmap_no_verdict_fionread();
>> if (test__start_subtest("sockmap skb_verdict fionread on drop"))
>> test_sockmap_skb_verdict_fionread(false);
>> if (test__start_subtest("sockmap skb_verdict change tail"))
>>
>> ---
>> base-commit: d2c9a99135da931377240942d44f3dea104cedb8
>> change-id: 20260707-fionread-no-verdict-a4f8697ac9f9
>>
>> Best regards,
>> --
>> Mattia Meleleo <mattia.meleleo@coralogix.com>
^ permalink raw reply
* Re: [PATCH net v2] gtp: parse extension headers before reading inner protocol
From: Zhixing Chen @ 2026-07-08 4:30 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: Harald Welte, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, osmocom-net-gprs, netdev
In-Reply-To: <ak0OCusCIcf0DH7y@chamomile>
> Patch is not good, it needs to refetch gtp header after
> pskb_may_pull().
Thanks for the catch. I’ve sent v3; it refetches gtp header after
pskb_may_pull().
^ permalink raw reply
* Re: [PATCH bpf] bpf, sockmap: Account for receive queue in FIONREAD without a verdict program
From: Jiayuan Chen @ 2026-07-08 4:30 UTC (permalink / raw)
To: mattia.meleleo, John Fastabend, Jakub Sitnicki; +Cc: netdev, bpf
In-Reply-To: <20260707-fionread-no-verdict-v1-1-ce94a72357ec@coralogix.com>
On 7/8/26 12:15 AM, Mattia Meleleo via B4 Relay wrote:
> From: Mattia Meleleo <mattia.meleleo@coralogix.com>
>
> tcp_bpf_ioctl() answers SIOCINQ from psock->msg_tot_len, which only
> counts bytes in ingress_msg. Without a stream/skb verdict program
> nothing is diverted there: data stays in sk_receive_queue, so FIONREAD
> returns 0 even though read() returns data.
>
> Add tcp_inq() to the reported value when the psock has no verdict
> program. The two queues are disjoint, so bytes redirected into
> ingress_msg from other sockets stay correctly accounted through
> msg_tot_len.
>
> Add a selftest covering FIONREAD without a verdict program.
>
> Fixes: 929e30f93125 ("bpf, sockmap: Fix FIONREAD for sockmap")
> Signed-off-by: Mattia Meleleo <mattia.meleleo@coralogix.com>
Reviewed-by: Jiayuan Chen <jiayuan.chen@linux.dev>
I guess udp_bpf_ioctl has same issue. You can fix it if you want.
> ---
> net/ipv4/tcp_bpf.c | 16 ++++++++-
> .../selftests/bpf/prog_tests/sockmap_basic.c | 39 ++++++++++++++++++++++
> 2 files changed, 54 insertions(+), 1 deletion(-)
>
> diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c
> index cc0bd73f3..a001b1fff 100644
> --- a/net/ipv4/tcp_bpf.c
> +++ b/net/ipv4/tcp_bpf.c
> @@ -334,6 +334,7 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
>
> static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg)
> {
> + struct sk_psock *psock;
> bool slow;
>
> if (cmd != SIOCINQ)
> @@ -344,7 +345,20 @@ static int tcp_bpf_ioctl(struct sock *sk, int cmd, int *karg)
> return -EINVAL;
>
> slow = lock_sock_fast(sk);
> - *karg = sk_psock_msg_inq(sk);
> + psock = sk_psock_get(sk);
> + if (unlikely(!psock)) {
> + unlock_sock_fast(sk, slow);
> + return tcp_ioctl(sk, cmd, karg);
> + }
> + *karg = sk_psock_get_msg_len_nolock(psock);
> + /* Without a verdict program, ingress data is never diverted to
> + * ingress_msg: it stays in sk_receive_queue and is read through
> + * the fallback to tcp_recvmsg(), so account for it like
> + * tcp_ioctl() does.
> + */
> + if (!psock->progs.stream_verdict && !psock->progs.skb_verdict)
> + *karg += tcp_inq(sk);
> + sk_psock_put(sk, psock);
> unlock_sock_fast(sk, slow);
>
> return 0;
> diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
> index cb3229711..f0f368201 100644
> --- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
> +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
> @@ -1373,6 +1373,43 @@ static void test_sockmap_multi_channels(int sotype)
> test_sockmap_pass_prog__destroy(skel);
> }
>
> +/* A socket in a sockmap without a verdict program keeps its ingress data
> + * in sk_receive_queue: FIONREAD must account for it.
> + */
> +static void test_sockmap_no_verdict_fionread(void)
> +{
> + int err, map, zero = 0, sent, avail;
> + int c0 = -1, c1 = -1, p0 = -1, p1 = -1;
> + struct test_sockmap_pass_prog *skel;
> + char buf[256] = "0123456789";
> +
> + skel = test_sockmap_pass_prog__open_and_load();
> + if (!ASSERT_OK_PTR(skel, "open_and_load"))
> + return;
> + map = bpf_map__fd(skel->maps.sock_map_rx);
> +
> + err = create_socket_pairs(AF_INET, SOCK_STREAM, &c0, &c1, &p0, &p1);
> + if (!ASSERT_OK(err, "create_socket_pairs()"))
> + goto out;
> +
> + err = bpf_map_update_elem(map, &zero, &c1, BPF_NOEXIST);
> + if (!ASSERT_OK(err, "bpf_map_update_elem(c1)"))
> + goto out_close;
> +
> + sent = xsend(p1, &buf, sizeof(buf), 0);
> + ASSERT_EQ(sent, sizeof(buf), "xsend(p1)");
> + avail = wait_for_fionread(c1, sizeof(buf), IO_TIMEOUT_SEC);
> + ASSERT_EQ(avail, sizeof(buf), "ioctl(FIONREAD)");
> +
> +out_close:
> + close(c0);
> + close(p0);
> + close(c1);
> + close(p1);
> +out:
> + test_sockmap_pass_prog__destroy(skel);
> +}
> +
> void test_sockmap_basic(void)
> {
> if (test__start_subtest("sockmap create_update_free"))
> @@ -1415,6 +1452,8 @@ void test_sockmap_basic(void)
> test_sockmap_skb_verdict_shutdown();
> if (test__start_subtest("sockmap skb_verdict fionread"))
> test_sockmap_skb_verdict_fionread(true);
> + if (test__start_subtest("sockmap no_verdict fionread"))
> + test_sockmap_no_verdict_fionread();
> if (test__start_subtest("sockmap skb_verdict fionread on drop"))
> test_sockmap_skb_verdict_fionread(false);
> if (test__start_subtest("sockmap skb_verdict change tail"))
>
> ---
> base-commit: d2c9a99135da931377240942d44f3dea104cedb8
> change-id: 20260707-fionread-no-verdict-a4f8697ac9f9
>
> Best regards,
> --
> Mattia Meleleo <mattia.meleleo@coralogix.com>
>
>
^ permalink raw reply
* [PATCH net v3] gtp: parse extension headers before reading inner protocol
From: Zhixing Chen @ 2026-07-08 4:22 UTC (permalink / raw)
To: Pablo Neira Ayuso, Harald Welte
Cc: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, osmocom-net-gprs, netdev, Zhixing Chen
GTPv1-U packets may carry a chain of extension headers before the inner
IP packet. The receive path already parses and skips these extension
headers, but it currently reads the inner protocol before doing so.
As a result, the first extension header byte is interpreted as the inner
IP version. Packets with extension headers are then dropped before PDP
lookup.
Parse the extension header chain before calling gtp_inner_proto(), so the
inner protocol is read from the actual inner IP header.
Fixes: c75fc0b9e5be ("gtp: identify tunnel via GTP device + GTP version + TEID + family")
Signed-off-by: Zhixing Chen <running910@gmail.com>
---
Changes in v3:
- Refetch the GTP header after pskb_may_pull() before checking the
extension-header flag.
Changes in v2:
- Add missing Fixes tag.
v2: https://lore.kernel.org/netdev/20260703093708.18141-1-running910@gmail.com/T/
v1: https://lore.kernel.org/netdev/20260703084244.59077-1-running910@gmail.com/T/
---
drivers/net/gtp.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index a60ef32b35b8..c0e38878af51 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -826,13 +826,17 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
if (!pskb_may_pull(skb, hdrlen))
return -1;
+ gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr));
+
+ if (gtp1->flags & GTP1_F_EXTHDR &&
+ gtp_parse_exthdrs(skb, &hdrlen) < 0)
+ return -1;
+
if (gtp_inner_proto(skb, hdrlen, &inner_proto) < 0) {
netdev_dbg(gtp->dev, "GTP packet does not encapsulate an IP packet\n");
return -1;
}
- gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr));
-
pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid),
gtp_proto_to_family(inner_proto));
if (!pctx) {
@@ -840,10 +844,6 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
return 1;
}
- if (gtp1->flags & GTP1_F_EXTHDR &&
- gtp_parse_exthdrs(skb, &hdrlen) < 0)
- return -1;
-
return gtp_rx(pctx, skb, hdrlen, gtp->role, inner_proto);
}
--
2.34.1
^ permalink raw reply related
* Re: [PATCH bpf 1/2] bpf, x86: Fix FineIBT #CP in BPF dispatcher's indirect-jump fallback
From: David Windsor @ 2026-07-08 3:40 UTC (permalink / raw)
To: Leon Hwang
Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H . Peter Anvin, David S . Miller, Jakub Kicinski,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Shuah Khan, Peter Zijlstra, netdev, linux-kernel, linux-kselftest
In-Reply-To: <6702d2f1-dcd4-4b68-b1ac-daa8661235a0@linux.dev>
On Tue, Jul 7, 2026 at 11:30 PM Leon Hwang <leon.hwang@linux.dev> wrote:
>
> Comment style: /* should stay at its own line.
>
> > + if (cfi_mode == CFI_FINEIBT) {
> > + EMIT1_off32(0xb8, cfi_bpf_hash); /* mov $cfi_bpf_hash, %eax */
> > + EMIT1(add_1mod(0x48, BPF_REG_3)); /* sub rdx, cfi_get_offset() */
> > + EMIT2_off32(0x81, add_1reg(0xE8, BPF_REG_3), cfi_get_offset());
> > + }
>
>
> Better to emit_cfi()?
>
Yes, thanks. Will do this for v2.
^ permalink raw reply
* Re: [PATCH bpf 1/2] bpf, x86: Fix FineIBT #CP in BPF dispatcher's indirect-jump fallback
From: Leon Hwang @ 2026-07-08 3:30 UTC (permalink / raw)
To: David Windsor, bpf
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H . Peter Anvin, David S . Miller, Jakub Kicinski,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Shuah Khan, Peter Zijlstra, netdev, linux-kernel, linux-kselftest
In-Reply-To: <c5a6bff16c0885d3ec756a1cdfb623636d0bdb4a.1783479101.git.dwindsor@gmail.com>
On 8/7/26 10:57, David Windsor wrote:
> commit 4f9087f16651 ("x86/cfi,bpf: Fix BPF JIT call") updated
> emit_cfi() to emit FineIBT preambles for JIT-compiled BPF programs, but
> did not update emit_bpf_dispatcher(). When prog->bpf_func is not in the
> dispatcher table (e.g. an XDP_REDIRECT target program in a CPUMAP or
> DEVMAP, or BPF_PROG_TEST_RUN of an unattached prog), the dispatcher
> still jumps directly to prog->bpf_func. Without a FineIBT caller
> sequence, under FineIBT this raises #CP:
>
> Missing ENDBR: __cfi_bpf_prog_..._xdp_dispatcher_unattached+0x10/0x10
> ------------[ cut here ]------------
> kernel BUG at arch/x86/kernel/cet.c:133!
> Oops: invalid opcode: 0000 [#1] SMP NOPTI
> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014
> RIP: 0010:do_kernel_cp_fault+0x126/0x130
> Call Trace:
> <TASK>
> exc_control_protection+0x46/0x80
> asm_exc_control_protection+0x2b/0x30
> RIP: 0010:__cfi_bpf_prog_..._xdp_dispatcher_unattached+0x10/0x10
> bpf_prog_test_run+0xda/0x1b0
> __sys_bpf+0x70b/0x990
> __x64_sys_bpf+0x2d/0x50
> x64_sys_call+0x1079/0x2d40
> do_syscall_64+0x12a/0x3c0
> entry_SYSCALL_64_after_hwframe+0x76/0x7e
> </TASK>
>
> To fix this, we precede the indirect jump in emit_bpf_dispatcher() with
> the FineIBT caller sequence: load cfi_bpf_hash into %eax and subtract
> cfi_get_offset() from the target so it enters the preamble's real ENDBR.
> Only needed for CFI_FINEIBT.
>
> Fixes: 4f9087f16651 ("x86/cfi,bpf: Fix BPF JIT call")
> Cc: stable@vger.kernel.org
> Signed-off-by: David Windsor <dwindsor@gmail.com>
> ---
> arch/x86/net/bpf_jit_comp.c | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
> index de7515ea1bea..1c8249d8ca3b 100644
> --- a/arch/x86/net/bpf_jit_comp.c
> +++ b/arch/x86/net/bpf_jit_comp.c
> @@ -3740,6 +3740,15 @@ static int emit_bpf_dispatcher(u8 **pprog, int a, int b, s64 *progs, u8 *image,
> if (err)
> return err;
>
> + /* If running under FineIBT, enter the preamble so the following
> + * indirect jump lands on a real ENDBR instead of the poison.
> + */
Comment style: /* should stay at its own line.
> + if (cfi_mode == CFI_FINEIBT) {
> + EMIT1_off32(0xb8, cfi_bpf_hash); /* mov $cfi_bpf_hash, %eax */
> + EMIT1(add_1mod(0x48, BPF_REG_3)); /* sub rdx, cfi_get_offset() */
> + EMIT2_off32(0x81, add_1reg(0xE8, BPF_REG_3), cfi_get_offset());
> + }
Better to emit_cfi()?
Thanks,
Leon
> +
> emit_indirect_jump(&prog, BPF_REG_3 /* R3 -> rdx */, image + (prog - buf));
>
> *pprog = prog;
^ permalink raw reply
* Re: [PATCH bpf 2/2] selftests/bpf: Add XDP dispatcher FineIBT regression test
From: Leon Hwang @ 2026-07-08 3:22 UTC (permalink / raw)
To: David Windsor, bpf
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H . Peter Anvin, David S . Miller, Jakub Kicinski,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Shuah Khan, Peter Zijlstra, netdev, linux-kernel, linux-kselftest
In-Reply-To: <f6df1bf381517f7ff6927d82de7680e7826adf94.1783479101.git.dwindsor@gmail.com>
On 8/7/26 10:57, David Windsor wrote:
> Add a regression test for the x86 BPF dispatcher's indirect-jump fallback
> under FineIBT.
>
> This test can crash the kernel on an unfixed FineIBT build and so should
> be run in a VM.
>
> Signed-off-by: David Windsor <dwindsor@gmail.com>
> ---
> .../bpf/prog_tests/xdp_dispatcher_fineibt.c | 47 +++++++++++++++++++
> .../bpf/progs/xdp_dispatcher_fineibt.c | 18 +++++++
> 2 files changed, 65 insertions(+)
> create mode 100644 tools/testing/selftests/bpf/prog_tests/xdp_dispatcher_fineibt.c
> create mode 100644 tools/testing/selftests/bpf/progs/xdp_dispatcher_fineibt.c
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_dispatcher_fineibt.c b/tools/testing/selftests/bpf/prog_tests/xdp_dispatcher_fineibt.c
> new file mode 100644
> index 000000000000..2d3fc4034eb9
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/xdp_dispatcher_fineibt.c
> @@ -0,0 +1,47 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2026 David Windsor */
> +
> +/*
> + * Regression test for the x86 BPF dispatcher's indirect-jump fallback
> + * under FineIBT.
> + *
> + * WARNING
> + * -------
> + * This test can crash the kernel, thus should be run in a VM.
> + */
> +#include <uapi/linux/if_link.h>
> +#include <test_progs.h>
> +#include <network_helpers.h>
> +#include "xdp_dispatcher_fineibt.skel.h"
> +
> +#define IFINDEX_LO 1
> +
> +void test_xdp_dispatcher_fineibt(void)
> +{
> + struct xdp_dispatcher_fineibt *skel;
> + int err, attached_fd, unattached_fd;
> +
> + LIBBPF_OPTS(bpf_test_run_opts, topts,
> + .data_in = &pkt_v4,
> + .data_size_in = sizeof(pkt_v4),
> + );
> +
> + skel = xdp_dispatcher_fineibt__open_and_load();
> + if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
> + return;
> +
> + attached_fd = bpf_program__fd(skel->progs.xdp_dispatcher_attached);
> + unattached_fd = bpf_program__fd(skel->progs.xdp_dispatcher_unattached);
> +
> + err = bpf_xdp_attach(IFINDEX_LO, attached_fd, XDP_FLAGS_SKB_MODE, NULL);
> + if (!ASSERT_OK(err, "attach_prog"))
> + goto out;
> +
> + err = bpf_prog_test_run_opts(unattached_fd, &topts);
> + ASSERT_OK(err, "unattached_test_run");
> + ASSERT_EQ(topts.retval, XDP_PASS, "unattached_retval");
> +
> + bpf_xdp_detach(IFINDEX_LO, XDP_FLAGS_SKB_MODE, NULL);
> +out:
> + xdp_dispatcher_fineibt__destroy(skel);
> +}
> diff --git a/tools/testing/selftests/bpf/progs/xdp_dispatcher_fineibt.c b/tools/testing/selftests/bpf/progs/xdp_dispatcher_fineibt.c
> new file mode 100644
> index 000000000000..b49b84f01c8e
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/xdp_dispatcher_fineibt.c
> @@ -0,0 +1,18 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2026 David Windsor */
> +#include <linux/bpf.h>
> +#include <bpf/bpf_helpers.h>
> +
> +SEC("xdp")
> +int xdp_dispatcher_attached(struct xdp_md *ctx)
> +{
> + return XDP_PASS;
> +}
> +
> +SEC("xdp")
> +int xdp_dispatcher_unattached(struct xdp_md *ctx)
> +{
> + return XDP_PASS;
> +}
> +
> +char _license[] SEC("license") = "GPL";
Instead of creating xdp_dispatcher_fineibt.c, better to reuse
xdp_dummy.c. Then, in xdp_dispatcher_fineibt.c, you can create two
xdp_dummy skels, one for attaching, one for running.
And, add the test to xdp_attach.c instead of new file
prog_tests/xdp_dispatcher_fineibt.c?
Thanks,
Leon
^ permalink raw reply
* Re: [PATCH net 1/2] net: macb: reprogram TBQP after shuffling the TX ring on link-up
From: Kevin Hao @ 2026-07-08 3:05 UTC (permalink / raw)
To: Taedcke, Christian
Cc: christian.taedcke, Théo Lebrun, Conor Dooley, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Sebastian Andrzej Siewior, Clark Williams,
Steven Rostedt, Robert Hancock, netdev, linux-kernel,
linux-rt-devel, stable
In-Reply-To: <8d53c3d9-7918-456c-8c27-e9d73c896452@weidmueller.com>
[-- Attachment #1: Type: text/plain, Size: 1166 bytes --]
> I agree that the TRM says the transmit pointer is reset while TE is low. My
> question is whether this describes an internal pointer being reloaded from TBQP,
> or whether TBQP itself is restored to the original ring base.
The Zynq UltraScale TRM [1] describes the receive-buffer queue pointer as follows:
An internal counter represents the receive-buffer queue pointer and it is not
visible through the CPU interface.
I could not find a similar description for the transmit-buffer queue pointer,
but I believe it behaves the same way. From a software perspective, it should
be safe to assume that the TBQP is reset to point to the start of the transmit
descriptor list upon reset. This assumption is supported by the description
of the transmit_q_ptr (GEM) Register [2]:
Reading this register returns the location of the descriptor currently being accessed.
Since the DMA handles two frames at once, this may not necessarily be pointing to the
current frame being transmitted.
[1] https://docs.amd.com/v/u/en-US/ug1085-zynq-ultrascale-trm
[2] https://docs.amd.com/r/en-US/ug1087-zynq-ultrascale-registers/transmit_q_ptr-GEM-Register
Thanks,
Kevin
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* [PATCH bpf 2/2] selftests/bpf: Add XDP dispatcher FineIBT regression test
From: David Windsor @ 2026-07-08 2:57 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H . Peter Anvin, David S . Miller, Jakub Kicinski,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Shuah Khan, Peter Zijlstra, netdev, linux-kernel, linux-kselftest,
David Windsor
In-Reply-To: <c5a6bff16c0885d3ec756a1cdfb623636d0bdb4a.1783479101.git.dwindsor@gmail.com>
Add a regression test for the x86 BPF dispatcher's indirect-jump fallback
under FineIBT.
This test can crash the kernel on an unfixed FineIBT build and so should
be run in a VM.
Signed-off-by: David Windsor <dwindsor@gmail.com>
---
.../bpf/prog_tests/xdp_dispatcher_fineibt.c | 47 +++++++++++++++++++
.../bpf/progs/xdp_dispatcher_fineibt.c | 18 +++++++
2 files changed, 65 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/xdp_dispatcher_fineibt.c
create mode 100644 tools/testing/selftests/bpf/progs/xdp_dispatcher_fineibt.c
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_dispatcher_fineibt.c b/tools/testing/selftests/bpf/prog_tests/xdp_dispatcher_fineibt.c
new file mode 100644
index 000000000000..2d3fc4034eb9
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_dispatcher_fineibt.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 David Windsor */
+
+/*
+ * Regression test for the x86 BPF dispatcher's indirect-jump fallback
+ * under FineIBT.
+ *
+ * WARNING
+ * -------
+ * This test can crash the kernel, thus should be run in a VM.
+ */
+#include <uapi/linux/if_link.h>
+#include <test_progs.h>
+#include <network_helpers.h>
+#include "xdp_dispatcher_fineibt.skel.h"
+
+#define IFINDEX_LO 1
+
+void test_xdp_dispatcher_fineibt(void)
+{
+ struct xdp_dispatcher_fineibt *skel;
+ int err, attached_fd, unattached_fd;
+
+ LIBBPF_OPTS(bpf_test_run_opts, topts,
+ .data_in = &pkt_v4,
+ .data_size_in = sizeof(pkt_v4),
+ );
+
+ skel = xdp_dispatcher_fineibt__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
+ return;
+
+ attached_fd = bpf_program__fd(skel->progs.xdp_dispatcher_attached);
+ unattached_fd = bpf_program__fd(skel->progs.xdp_dispatcher_unattached);
+
+ err = bpf_xdp_attach(IFINDEX_LO, attached_fd, XDP_FLAGS_SKB_MODE, NULL);
+ if (!ASSERT_OK(err, "attach_prog"))
+ goto out;
+
+ err = bpf_prog_test_run_opts(unattached_fd, &topts);
+ ASSERT_OK(err, "unattached_test_run");
+ ASSERT_EQ(topts.retval, XDP_PASS, "unattached_retval");
+
+ bpf_xdp_detach(IFINDEX_LO, XDP_FLAGS_SKB_MODE, NULL);
+out:
+ xdp_dispatcher_fineibt__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/xdp_dispatcher_fineibt.c b/tools/testing/selftests/bpf/progs/xdp_dispatcher_fineibt.c
new file mode 100644
index 000000000000..b49b84f01c8e
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/xdp_dispatcher_fineibt.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 David Windsor */
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+SEC("xdp")
+int xdp_dispatcher_attached(struct xdp_md *ctx)
+{
+ return XDP_PASS;
+}
+
+SEC("xdp")
+int xdp_dispatcher_unattached(struct xdp_md *ctx)
+{
+ return XDP_PASS;
+}
+
+char _license[] SEC("license") = "GPL";
--
2.53.0
^ permalink raw reply related
* [PATCH bpf 1/2] bpf, x86: Fix FineIBT #CP in BPF dispatcher's indirect-jump fallback
From: David Windsor @ 2026-07-08 2:57 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H . Peter Anvin, David S . Miller, Jakub Kicinski,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Shuah Khan, Peter Zijlstra, netdev, linux-kernel, linux-kselftest,
David Windsor
commit 4f9087f16651 ("x86/cfi,bpf: Fix BPF JIT call") updated
emit_cfi() to emit FineIBT preambles for JIT-compiled BPF programs, but
did not update emit_bpf_dispatcher(). When prog->bpf_func is not in the
dispatcher table (e.g. an XDP_REDIRECT target program in a CPUMAP or
DEVMAP, or BPF_PROG_TEST_RUN of an unattached prog), the dispatcher
still jumps directly to prog->bpf_func. Without a FineIBT caller
sequence, under FineIBT this raises #CP:
Missing ENDBR: __cfi_bpf_prog_..._xdp_dispatcher_unattached+0x10/0x10
------------[ cut here ]------------
kernel BUG at arch/x86/kernel/cet.c:133!
Oops: invalid opcode: 0000 [#1] SMP NOPTI
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014
RIP: 0010:do_kernel_cp_fault+0x126/0x130
Call Trace:
<TASK>
exc_control_protection+0x46/0x80
asm_exc_control_protection+0x2b/0x30
RIP: 0010:__cfi_bpf_prog_..._xdp_dispatcher_unattached+0x10/0x10
bpf_prog_test_run+0xda/0x1b0
__sys_bpf+0x70b/0x990
__x64_sys_bpf+0x2d/0x50
x64_sys_call+0x1079/0x2d40
do_syscall_64+0x12a/0x3c0
entry_SYSCALL_64_after_hwframe+0x76/0x7e
</TASK>
To fix this, we precede the indirect jump in emit_bpf_dispatcher() with
the FineIBT caller sequence: load cfi_bpf_hash into %eax and subtract
cfi_get_offset() from the target so it enters the preamble's real ENDBR.
Only needed for CFI_FINEIBT.
Fixes: 4f9087f16651 ("x86/cfi,bpf: Fix BPF JIT call")
Cc: stable@vger.kernel.org
Signed-off-by: David Windsor <dwindsor@gmail.com>
---
arch/x86/net/bpf_jit_comp.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index de7515ea1bea..1c8249d8ca3b 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -3740,6 +3740,15 @@ static int emit_bpf_dispatcher(u8 **pprog, int a, int b, s64 *progs, u8 *image,
if (err)
return err;
+ /* If running under FineIBT, enter the preamble so the following
+ * indirect jump lands on a real ENDBR instead of the poison.
+ */
+ if (cfi_mode == CFI_FINEIBT) {
+ EMIT1_off32(0xb8, cfi_bpf_hash); /* mov $cfi_bpf_hash, %eax */
+ EMIT1(add_1mod(0x48, BPF_REG_3)); /* sub rdx, cfi_get_offset() */
+ EMIT2_off32(0x81, add_1reg(0xE8, BPF_REG_3), cfi_get_offset());
+ }
+
emit_indirect_jump(&prog, BPF_REG_3 /* R3 -> rdx */, image + (prog - buf));
*pprog = prog;
--
2.53.0
^ permalink raw reply related
* [PATCH net] rds: drop incoming messages that cross network namespace boundaries
From: Allison Henderson @ 2026-07-08 2:43 UTC (permalink / raw)
To: netdev, pabeni, edumazet, kuba, horms; +Cc: achender, qwe.aldo
From: Aldo Ariel Panzardo <qwe.aldo@gmail.com>
rds_find_bound() looks up the destination socket using a global
rhashtable keyed solely on (addr, port, scope_id). Network namespaces
are not part of the key, so a sender in netns A can deliver an incoming
message (inc) to a socket that lives in a different netns B.
When this happens, inc->i_conn points to an rds_connection whose c_net
is netns A, but the receiving rs lives in netns B. Once the child
process that created netns A exits, cleanup_net() calls
rds_loop_exit_net() -> rds_loop_kill_conns() -> rds_conn_destroy(),
freeing that connection. If the survivor socket in netns B still holds
the inc, any subsequent dereference of inc->i_conn is a use-after-free.
There are two dangerous sites in rds_clear_recv_queue():
1. inc->i_conn->c_lcong (offset 88 of freed rds_connection, size 200)
read via rds_recv_rcvbuf_delta() -- confirmed by KASAN.
2. inc->i_conn->c_trans->inc_free(inc) (function pointer at offset 80)
called via rds_inc_put() when the inc refcount reaches zero -- same
race window, potential call-through-freed-object primitive.
The bug is reachable from unprivileged user namespaces
(CLONE_NEWUSER + CLONE_NEWNET), available since Linux 3.8.
Fix this by rejecting the delivery in rds_recv_incoming() when the
socket returned by rds_find_bound() belongs to a different network
namespace than the connection that carried the message. Use the
existing rds_conn_net() / sock_net() helpers and net_eq() for the
comparison.
Fixes: c809195f5523 ("rds: clean up loopback rds_connections on netns deletion")
Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@gmail.com>
Reviewed-by: Allison Henderson <achender@kernel.org>
Tested-by: Allison Henderson <achender@kernel.org>
Signed-off-by: Allison Henderson <achender@kernel.org>
---
net/rds/recv.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/net/rds/recv.c b/net/rds/recv.c
index 4b3f9e4a8bfd..cf3884d87931 100644
--- a/net/rds/recv.c
+++ b/net/rds/recv.c
@@ -399,6 +399,21 @@ void rds_recv_incoming(struct rds_connection *conn, struct in6_addr *saddr,
goto out;
}
+ /*
+ * rds_find_bound() uses a global (netns-agnostic) hash table.
+ * An RDS connection created in netns A can match a socket bound
+ * in the init netns, delivering inc cross-netns with inc->i_conn
+ * pointing into netns A. When cleanup_net() then frees that conn,
+ * any subsequent dereference of inc->i_conn is a use-after-free.
+ * Drop the inc if the receiving socket lives in a different netns.
+ */
+ if (!net_eq(sock_net(rds_rs_to_sk(rs)), rds_conn_net(conn))) {
+ rds_stats_inc(s_recv_drop_no_sock);
+ rds_sock_put(rs);
+ rs = NULL;
+ goto out;
+ }
+
/* Process extension headers */
rds_recv_incoming_exthdrs(inc, rs);
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v6] net: gro: fix double aggregation of flush-marked skbs
From: Shiming Cheng (成诗明) @ 2026-07-08 1:41 UTC (permalink / raw)
To: linux-kernel@vger.kernel.org, dsahern@kernel.org,
imv4bel@gmail.com, linux-mediatek@lists.infradead.org,
alice@isovalent.com, daniel.zahka@gmail.com,
eilaimemedsnaimel@gmail.com, nbd@nbd.name, horms@kernel.org,
kuba@kernel.org, pabeni@redhat.com, edumazet@google.com,
willemdebruijn.kernel@gmail.com, willemb@google.com,
netdev@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
matthias.bgg@gmail.com, davem@davemloft.net,
AngeloGioacchino Del Regno, sd@queasysnail.net
Cc: steffen.klassert@secunet.com, stable@vger.kernel.org,
Lena Wang (王娜)
In-Reply-To: <willemdebruijn.kernel.39a3b0237ed2@gmail.com>
On Tue, 2026-07-07 at 11:16 -0400, Willem de Bruijn wrote:
> External email : Please do not click links or open attachments until
> you have verified the sender or the content.
>
>
> Shiming Cheng wrote:
> > The skb_gro_receive_list() function is missing a critical safety
> > check
> > that exists in the skb_gro_receive() implementation. Specifically,
> > it
> > does not validate NAPI_GRO_CB(skb)->flush before allowing packet
> > aggregation, as of commit 0ab03f353d36 ("net-gro: Fix GRO flush
> > when receiving a GSO packet.").
>
> It does not check .. as of commit .. ?
>
> No, skb_gro_receive checkos NAP_GRO_CB(skb)->flush as of that commit.
>
Is this wording okay?
Commit 0ab03f353d36 ("net-gro: Fix GRO flush when receiving a GSO
packet.") added a flush check to skb_gro_receive(), but
skb_gro_receive_list() lacks the same validation.
As a result, packets marked with NAPI_GRO_CB(skb)->flush may still be
re-aggregated.
> > This allows already-GRO'd packets with existing frag_list to be
> > re-aggregated into a new GRO session, corrupting the frag_list
> > chain
> > structure. When skb_segment() attempts to unpack these malformed
> > packets,
> > it encounters invalid state and triggers a kernel panic.
> >
> > Scenario (Tethering/Device forwarding):
> > 1. Driver: Generated aggregated packet P1 via LRO with frag_list
> > 2. Dev A: Receives aggregated fraglist packet and flush flag set
> > 3. Dev A: Re-enters GRO, skb_gro_receive_list() is called
> > 4. Missing flush check allows re-aggregation despite flush flag
> > 5. Frag_list chain becomes corrupted (loops or dangling refs)
> > 6. Dev B: TX path calls skb_segment(), crashes on corrupted
> > frag_list
> >
> > Root cause in skb_segment():
> > The check at line ~4891:
> > if (hsize <= 0 && i >= nfrags && skb_headlen(list_skb) &&
> > (skb_headlen(list_skb) == len || sg)) {
> >
> > When frag_list is corrupted by double aggregation, when list_skb
> > is
> > a NULL pointer from skb->next, skb_headlen(list_skb) dereference
> > NULL/corrupted pointers occurs.
> >
> > Call Trace:
> > skb_headlen(NULL skb)
> > skb_segment
> > tcp_gso_segment
> > tcp4_gso_segment
> > inet_gso_segment
> > skb_mac_gso_segment
> > __skb_gso_segment
> > skb_gso_segment
> > validate_xmit_skb
> > validate_xmit_skb_list
> > sch_direct_xmit
> > qdisc_restart
> > __qdisc_run
> > qdisc_run
> > net_tx_action
> >
> > Fix: Add NAPI_GRO_CB(skb)->flush validation to the early-return
> > check in
> > skb_gro_receive_list(), matching the defensive programming pattern
> > of
> > skb_gro_receive().
> >
> > Fixes: 3a1296a38d0c ("net: Support GRO/GSO fraglist chaining.")
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Shiming Cheng <shiming.cheng@mediatek.com>
> > ---
> > net/core/gro.c | 4 +++-
> > 1 file changed, 3 insertions(+), 1 deletion(-)
> >
> > diff --git a/net/core/gro.c b/net/core/gro.c
> > index 35f2f708f010..b413f4a6462b 100644
> > --- a/net/core/gro.c
> > +++ b/net/core/gro.c
> > @@ -229,7 +229,9 @@ int skb_gro_receive(struct sk_buff *p, struct
> > sk_buff *skb)
> >
> > int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
> > {
> > - if (unlikely(p->len + skb->len >= 65536))
> > + /* make sure to check flush flag and to not merge */
> > + if (unlikely(p->len + skb->len >= 65536 ||
> > + NAPI_GRO_CB(skb)->flush))
> > return -E2BIG;
> >
> > if (!pskb_may_pull(skb, skb_gro_offset(skb))) {
> > --
> > 2.45.2
> >
>
>
^ permalink raw reply
* Re: [PATCH net-next v4 1/2] net: dsa: realtek: rtl8365mb: add SGMII support for RTL8367S
From: Johan Alvarado @ 2026-07-08 1:09 UTC (permalink / raw)
To: Maxime Chevallier
Cc: linusw, alsi, andrew, olteanv, kuba, davem, edumazet, pabeni,
linux, luizluca, namiltd, netdev, linux-kernel
In-Reply-To: <e4e1d392-2430-4403-91b1-5be59d6a9895@bootlin.com>
Hi Maxime,
On 7/3/26 09:29, Maxime Chevallier wrote:
> Ah nice conversion to phylink PCS :) I have a few comments below
Thanks for the review!
[...]
>> + /* This driver does not implement SGMII in-band autonegotiation yet, so
>> + * the link parameters are forced from rtl8365mb_pcs_link_up() instead.
>> + * rtl8365mb_pcs_inband_caps() reports this to phylink, which should
>> + * therefore never select an in-band-enabled negotiation mode.
>> + */
>> + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
>> + return -EOPNOTSUPP;
>
> As you implement the .pcs_inband_caps() method, phylink will pass you a valid
> mode, no need for that check :)
Indeed. I went through phylink_pcs_neg_mode() again and with
pcs_inband_caps() returning LINK_INBAND_DISABLE every path resolves to
an outband (or inband-disabled, for the 802.3z no-PHY case) negotiation
mode, so this is dead code. I will drop it in v5.
[...]
>> + /* The SerDes has its own pause controls; program them from
>> + * the resolved pause modes, as the vendor driver does when
>> + * forcing the link on a SerDes external interface. This is
>> + * done here rather than in rtl8365mb_pcs_link_up() because
>> + * pcs_link_up() carries no pause information.
>> + */
>> + if (rtl8365mb_interface_is_serdes(interface)) {
>> + u32 val = 0;
>> +
>> + if (tx_pause)
>> + val |= RTL8365MB_SDS_MISC_SGMII_TXFC_MASK;
>> + if (rx_pause)
>> + val |= RTL8365MB_SDS_MISC_SGMII_RXFC_MASK;
>
> Do you know what this does in HW ? Is this so that the PCS lets the Pause frames through
> in either directions ?
>
> I suspect this is something that would be only used for inband advertising
> of pause settings (in such case, you don't even need that), but ofc I'm not sure :)
>
> You already configure the MAC pause settings, can you test that these bits actually do
> anything by exercising a bit flow control and checking if these registers are used ?
I tested it on the hardware, and it turns out these bits are the
operative pause controls for the SerDes port: pause frames are only
ever emitted when SDS_MISC_SGMII_TXFC is set, and the MAC force-mode
pause bits alone have no observable effect on this port. They are not
tied to in-band advertisement either - they act with the link fully
forced.
For context, there is no public documentation for these bits, but in
the vendor GPL code every write to them lives in
rtl8367c_setAsicPortForceLinkExt(), the forced-link path, where they
are programmed from the resolved tx/rx pause settings right alongside
the MAC force-mode pause bits - never from the nway/in-band paths.
Test setup: RTL8367S (Mercusys MR80X), CPU port 6 on HSGMII,
2500base-x fixed link to an IPQ5018. I enabled regmap debugfs writes
so I could flip the bits live, and forced congestion by L2-forwarding
~350 Mbit/s of SoC-generated UDP in through the CPU port and out of a
100M user port (~3.5x oversubscription, ~135 MB per run), watching the
CPU port's dot3OutPauseFrames MIB counter:
everything cleared (baseline): 0 pause frames
MAC force pause only (DIGITAL_INTERFACE_FORCE
TXPAUSE|RXPAUSE, reg 0x1311): 0 pause frames
MAC force pause + SDS TXFC|RXFC: 2396 pause frames
SDS TXFC|RXFC only (MAC pause cleared): 2362 pause frames
SDS TXFC only: 2934 pause frames
SDS RXFC only: 0 pause frames
RXFC did not influence pause emission, so by symmetry it presumably
gates honouring received pause frames; I could not exercise that
direction because the SoC MAC on this board never transmits pause.
So the write has to stay - without it flow control simply does not
work on SGMII/HSGMII. I will expand the comment in v5 to say that
these are the functional pause enables for the SerDes, rather than
describing it as mirroring the vendor forced-link sequence.
Best regards,
Johan
^ permalink raw reply
* [PATCH net-next v6 2/2] selftests: net: add FOU multicast encapsulation resubmit test
From: Anton Danilov @ 2026-07-08 0:35 UTC (permalink / raw)
To: netdev
Cc: Willem de Bruijn, David S . Miller, David Ahern, Eric Dumazet,
Kuniyuki Iwashima, Jakub Kicinski, Paolo Abeni, Simon Horman,
Shuah Khan, linux-kselftest
In-Reply-To: <cover.1783372173.git.littlesmilingcloud@gmail.com>
Add a selftest to verify that FOU-encapsulated packets addressed to a
multicast destination are correctly resubmitted to the inner protocol
handler (GRE) via the UDP multicast delivery path. Both IPv4 and IPv6
paths are tested.
The test creates two network namespaces connected by a veth pair with
a FOU/GRETAP (IPv4) and FOU/ip6gretap (IPv6) tunnel using multicast
remote addresses (239.0.0.1 and ff0e::1). Ping is sent through each
tunnel and received packets are counted on the receiver's tunnel
interface.
The veth pair is created directly inside the namespaces to avoid
possible name collisions with devices in the root namespace.
Static neighbor entries are configured on the sender because ARP/ND
replies from the receiver cannot traverse the unidirectional multicast
tunnel back to the sender.
The early demux optimization (net.ipv4.ip_early_demux, which controls
both IPv4 and IPv6) is disabled on the receiver to force packets
through __udp4_lib_mcast_deliver() / __udp6_lib_mcast_deliver(), which
is the code path being tested.
Signed-off-by: Anton Danilov <littlesmilingcloud@gmail.com>
Assisted-by: Claude:claude-opus-4-6
---
tools/testing/selftests/net/Makefile | 1 +
tools/testing/selftests/net/config | 2 +
.../testing/selftests/net/fou_mcast_encap.sh | 172 ++++++++++++++++++
3 files changed, 175 insertions(+)
create mode 100755 tools/testing/selftests/net/fou_mcast_encap.sh
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 708d960ae07d..7e9ae937cffa 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -39,6 +39,7 @@ TEST_PROGS := \
fib_rule_tests.sh \
fib_tests.sh \
fin_ack_lat.sh \
+ fou_mcast_encap.sh \
fq_band_pktlimit.sh \
gre_gso.sh \
gre_ipv6_lladdr.sh \
diff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/net/config
index e1ce35c2abbe..96fffca6547c 100644
--- a/tools/testing/selftests/net/config
+++ b/tools/testing/selftests/net/config
@@ -38,6 +38,8 @@ CONFIG_IP_NF_TARGET_REJECT=m
CONFIG_IP_NF_TARGET_TTL=m
CONFIG_IP_SCTP=m
CONFIG_IPV6=y
+CONFIG_IPV6_FOU=m
+CONFIG_IPV6_FOU_TUNNEL=m
CONFIG_IPV6_GRE=m
CONFIG_IPV6_ILA=m
CONFIG_IPV6_IOAM6_LWTUNNEL=y
diff --git a/tools/testing/selftests/net/fou_mcast_encap.sh b/tools/testing/selftests/net/fou_mcast_encap.sh
new file mode 100755
index 000000000000..70210d39fba3
--- /dev/null
+++ b/tools/testing/selftests/net/fou_mcast_encap.sh
@@ -0,0 +1,172 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test that UDP encapsulation (FOU) correctly handles packet resubmit
+# when packets are delivered via the multicast UDP delivery path.
+#
+# When a FOU-encapsulated packet arrives with a multicast destination IP,
+# __udp4_lib_mcast_deliver() / __udp6_lib_mcast_deliver() must resubmit
+# it to the inner protocol handler (e.g., GRE) rather than consuming it.
+# This test verifies both IPv4 and IPv6 paths by creating a FOU/GRETAP
+# tunnel with a multicast remote address and sending ping through it.
+#
+# The early demux optimization can mask this issue by routing packets via
+# the unicast path (udp[6]_unicast_rcv_skb), so we disable it to force
+# packets through the multicast delivery function.
+
+source lib.sh
+
+NSENDER=""
+NRECV=""
+
+FOU_PORT4=4797
+FOU_PORT6=4798
+MCAST4=239.0.0.1
+MCAST6=ff0e::1
+
+TUN4_S=192.168.99.1
+TUN4_R=192.168.99.2
+TUN6_S=2001:db8:99::1
+TUN6_R=2001:db8:99::2
+
+cleanup() {
+ cleanup_all_ns
+}
+
+trap cleanup EXIT
+
+setup_common() {
+ setup_ns NSENDER NRECV
+
+ # Create veth pair directly inside namespaces to avoid name
+ # collisions with devices in the root namespace.
+ ip link add veth_s netns "$NSENDER" type veth \
+ peer name veth_r netns "$NRECV"
+
+ ip -n "$NSENDER" link set veth_s up
+ ip -n "$NRECV" link set veth_r up
+
+ # Same sysctl controls early demux for both IPv4 and IPv6.
+ ip netns exec "$NRECV" sysctl -wq net.ipv4.ip_early_demux=0
+}
+
+setup_ipv4() {
+ # IPv4 FOU (CONFIG_NET_FOU) is built in on kernels configured for
+ # these tests, so no module load is needed here.
+ ip -n "$NSENDER" addr add 10.0.0.1/24 dev veth_s
+ ip -n "$NRECV" addr add 10.0.0.2/24 dev veth_r
+
+ # Join multicast group on receiver
+ ip -n "$NRECV" addr add "$MCAST4/32" dev veth_r autojoin
+
+ ip -n "$NSENDER" route add 239.0.0.0/8 dev veth_s
+ ip -n "$NRECV" route add 239.0.0.0/8 dev veth_r
+
+ # Sender: GRETAP with FOU encap (no FOU listener needed on TX side)
+ ip -n "$NSENDER" link add eoudp4 type gretap \
+ remote "$MCAST4" local 10.0.0.1 \
+ encap fou encap-sport "$FOU_PORT4" encap-dport "$FOU_PORT4" \
+ key "$MCAST4"
+ ip -n "$NSENDER" link set eoudp4 up
+ ip -n "$NSENDER" addr add "$TUN4_S/24" dev eoudp4
+
+ # Receiver: FOU listener + GRETAP
+ ip netns exec "$NRECV" ip fou add port "$FOU_PORT4" ipproto 47
+ ip -n "$NRECV" link add eoudp4 type gretap \
+ remote "$MCAST4" local 10.0.0.2 \
+ encap fou encap-sport "$FOU_PORT4" encap-dport "$FOU_PORT4" \
+ key "$MCAST4"
+ ip -n "$NRECV" link set eoudp4 up
+ ip -n "$NRECV" addr add "$TUN4_R/24" dev eoudp4
+
+ # Static neigh on sender: ARP replies cannot traverse the
+ # unidirectional multicast tunnel.
+ local recv_mac
+ recv_mac=$(ip -n "$NRECV" link show eoudp4 | awk '/ether/{print $2}')
+ ip -n "$NSENDER" neigh add "$TUN4_R" lladdr "$recv_mac" dev eoudp4
+}
+
+setup_ipv6() {
+ # Skip cleanly if IPv6 or the fou6 module is not available.
+ [ -e /proc/sys/net/ipv6 ] || return "$ksft_skip"
+ modprobe -q fou6 || return "$ksft_skip"
+
+ ip -n "$NSENDER" addr add 2001:db8::1/64 dev veth_s nodad
+ ip -n "$NRECV" addr add 2001:db8::2/64 dev veth_r nodad
+
+ # Join multicast group on receiver
+ ip -n "$NRECV" addr add "$MCAST6/128" dev veth_r autojoin
+
+ ip -n "$NSENDER" -6 route add ff00::/8 dev veth_s
+ ip -n "$NRECV" -6 route add ff00::/8 dev veth_r
+
+ # Sender: ip6gretap with FOU encap
+ ip -n "$NSENDER" link add eoudp6 type ip6gretap \
+ remote "$MCAST6" local 2001:db8::1 \
+ encap fou encap-sport "$FOU_PORT6" encap-dport "$FOU_PORT6" \
+ key 42
+ ip -n "$NSENDER" link set eoudp6 up
+ ip -n "$NSENDER" addr add "$TUN6_S/64" dev eoudp6 nodad
+
+ # Receiver: FOU listener (IPv6) + ip6gretap
+ ip netns exec "$NRECV" ip fou add port "$FOU_PORT6" ipproto 47 -6
+ ip -n "$NRECV" link add eoudp6 type ip6gretap \
+ remote "$MCAST6" local 2001:db8::2 \
+ encap fou encap-sport "$FOU_PORT6" encap-dport "$FOU_PORT6" \
+ key 42
+ ip -n "$NRECV" link set eoudp6 up
+ ip -n "$NRECV" addr add "$TUN6_R/64" dev eoudp6 nodad
+
+ # Static neigh on sender: neighbor discovery cannot traverse the
+ # unidirectional multicast tunnel.
+ local recv_mac
+ recv_mac=$(ip -n "$NRECV" link show eoudp6 | awk '/ether/{print $2}')
+ ip -n "$NSENDER" neigh add "$TUN6_R" lladdr "$recv_mac" dev eoudp6
+}
+
+get_rx_packets() {
+ local dev="$1"
+
+ ip -n "$NRECV" -s link show "$dev" | awk '/RX:/{getline; print $2}'
+}
+
+run_ping_test() {
+ local family="$1"
+ local dev="$2"
+ local dst="$3"
+ local name="$4"
+ local count=100
+ local rx_before rx_after rx_delta
+
+ # Warmup: let any initial broadcast/ND traffic settle
+ ip netns exec "$NSENDER" ping "$family" -c 1 -W 1 "$dst" \
+ >/dev/null 2>&1
+ sleep 1
+
+ rx_before=$(get_rx_packets "$dev")
+ ip netns exec "$NSENDER" ping "$family" -i 0.01 -c $count -W 1 "$dst" \
+ >/dev/null 2>&1
+ sleep 1
+ rx_after=$(get_rx_packets "$dev")
+
+ rx_delta=$((rx_after - rx_before))
+
+ if [ "$rx_delta" -ge "$count" ]; then
+ RET=$ksft_pass
+ else
+ RET=$ksft_fail
+ fi
+ log_test "$name (received $rx_delta/$count)"
+}
+
+setup_common
+setup_ipv4
+run_ping_test -4 eoudp4 "$TUN4_R" "FOU/GRETAP IPv4 multicast encap resubmit"
+
+if setup_ipv6; then
+ run_ping_test -6 eoudp6 "$TUN6_R" "FOU/ip6gretap IPv6 multicast encap resubmit"
+else
+ log_test_skip "FOU/ip6gretap IPv6 multicast encap resubmit"
+fi
+
+exit "$EXIT_STATUS"
--
2.47.3
^ permalink raw reply related
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