* Re: [PATCH 2/3 net-next v3] ipv4: handle devconf post-set actions on netlink updates
From: Nicolas Dichtel @ 2026-05-05 12:46 UTC (permalink / raw)
To: Fernando Fernandez Mancera, netdev
Cc: linux-kselftest, horms, pabeni, kuba, edumazet, davem, idosch,
dsahern
In-Reply-To: <20260504123143.6284-2-fmancera@suse.de>
Le 04/05/2026 à 14:31, Fernando Fernandez Mancera a écrit :
> When IPv4 device configuration parameters are updated via netlink, the
> kernel currently only updates the value. This bypasses several
> post-modification actions that occur when these same parameters are
> updated via sysctl, such as flushing the routing cache or emitting
> RTM_NEWNETCONF notifications.
>
> This patch addresses the inconsistency by calling the
> devinet_conf_post_set() helper inside inet_set_link_af(). If a flush is
> required, we defer it until the netlink attribute parsing loop
> completes.
>
> This ensures consistent behavior and side-effects for devconf changes,
> regardless of whether they are initiated via sysctl or netlink.
>
> Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
Reviewed-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
^ permalink raw reply
* [PATCH net-next v5 0/2] selftests: openvswitch: add pop_vlan test
From: Minxi Hou @ 2026-05-05 12:49 UTC (permalink / raw)
To: netdev
Cc: aconole, echaudro, i.maximets, davem, edumazet, kuba, pabeni,
horms, shuah, dev, linux-kselftest, linux-kernel, Minxi Hou
Add test_pop_vlan() to verify OVS kernel datapath pop_vlan action
correctly strips 802.1Q VLAN tags from frames.
Patch 1 extends ovs-dpctl.py with vlan(vid=X,pcp=Y,cfi=Z) formatting
and parsing, plus an encap_ovskey subclass for safe ENCAP NLA decoding.
It also adds push_vlan action support (parse/format with range
validation) and removes the unnecessary MAX_ENCAP_DEPTH limit.
Patch 2 adds the selftest using purely ping-based verification with
a push_vlan return flow for symmetric bidirectional testing.
Tested with vng on x86_64, all OVS selftests pass (including new
test_pop_vlan).
v5:
- add push_vlan action class, dpstr format and parse with range
validation (vid 0-4095, pcp 0-7, tpid 0-0xFFFF, CFI forced to 1)
- remove MAX_ENCAP_DEPTH constant and depth tracking (bracket-depth
counter in encap parser already handles nesting)
- remove start_capture/stop_capture helpers and tcpdump/pcap
verification — use ping success/failure instead
- remove modprobe/netns pre-flight checks (other tests don't do this)
- remove ethtool VLAN offload disable (unnecessary for veth)
- add push_vlan return flow for symmetric bidirectional ping
- use ovs_sbx wrapper for ping commands (consistent with siblings)
v4: https://lore.kernel.org/netdev/20260504123713.555461-1-houminxi@gmail.com/
- fix all checkpatch line-length warnings in new code
- fix pylint W0707: use explicit exception chaining (from exc)
v3: https://lore.kernel.org/netdev/20260503120946.51869-1-houminxi@gmail.com/
- encap_ovskey: MPLS type "ovs_key_mpls" -> "array(ovs_key_mpls)"
- encap_ovskey: PRIORITY/IN_PORT set to "none" (metadata, not in ENCAP)
- _vlan_dpstr: cfi=0 falls back to tci=0x%04x for round-trip safety
- encap parse(): check return value for unrecognized trailing content
- vlan parser: boundary check + raise-from for exception chaining
- start_capture: || return $? to propagate ksft_skip correctly
- on_exit: moved after resource creation, not before
- ping success: changed from NOTE to FAIL + return 1
- VLAN interface creation: added || return 1 error propagation
- netns probe: distinguish EEXIST from missing CONFIG_NET_NS
- sbx_add: || return $ksft_skip -> || return $? (match sibling tests)
v2: https://lore.kernel.org/netdev/20260501133924.3100680-1-houminxi@gmail.com/
Minxi Hou (2):
selftests: openvswitch: add vlan() and encap() flow string parsing
selftests: openvswitch: add pop_vlan test
.../selftests/net/openvswitch/openvswitch.sh | 77 +++++
.../selftests/net/openvswitch/ovs-dpctl.py | 322 +++++++++++++++++-
2 files changed, 389 insertions(+), 10 deletions(-)
--
2.53.0
^ permalink raw reply
* [PATCH net-next v5 1/2] selftests: openvswitch: add vlan() and encap() flow string parsing
From: Minxi Hou @ 2026-05-05 12:49 UTC (permalink / raw)
To: netdev
Cc: aconole, echaudro, i.maximets, davem, edumazet, kuba, pabeni,
horms, shuah, dev, linux-kselftest, linux-kernel, Minxi Hou
In-Reply-To: <20260505124957.1239812-1-houminxi@gmail.com>
Add VLAN TCI formatting and parsing support to ovs-dpctl.py:
- Add _vlan_dpstr() to decompose TCI into vid/pcp/cfi fields,
with raw tci=0x%04x fallback when cfi=0 for round-trip safety.
- Add _parse_vlan_from_flowstr() boundary check for missing ')'.
- Add encap_ovskey subclass restricting nla_map to L2-L4 attributes
(slots 0-21) that appear inside 802.1Q ENCAP, with metadata
attributes set to "none".
- Check parse() return value for unrecognized trailing content.
- Support callable format functions in dpstr() output.
- Add push_vlan action class with fields matching kernel struct
ovs_action_push_vlan (vlan_tpid, vlan_tci as network-order u16).
- Add push_vlan dpstr format and parse with range validation
(vid 0-4095, pcp 0-7, tpid 0-0xFFFF) and CFI forced to 1.
- Remove MAX_ENCAP_DEPTH constant and depth tracking — the
bracket-depth counter in the encap parser already handles
nesting; the global depth limit was unnecessary.
Signed-off-by: Minxi Hou <houminxi@gmail.com>
---
.../selftests/net/openvswitch/ovs-dpctl.py | 322 +++++++++++++++++-
1 file changed, 312 insertions(+), 10 deletions(-)
diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
index 848f61fdcee0..50551d4fa7c7 100644
--- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
+++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
@@ -370,7 +370,7 @@ class ovsactions(nla):
("OVS_ACTION_ATTR_OUTPUT", "uint32"),
("OVS_ACTION_ATTR_USERSPACE", "userspace"),
("OVS_ACTION_ATTR_SET", "ovskey"),
- ("OVS_ACTION_ATTR_PUSH_VLAN", "none"),
+ ("OVS_ACTION_ATTR_PUSH_VLAN", "push_vlan"),
("OVS_ACTION_ATTR_POP_VLAN", "flag"),
("OVS_ACTION_ATTR_SAMPLE", "sample"),
("OVS_ACTION_ATTR_RECIRC", "uint32"),
@@ -427,6 +427,9 @@ class ovsactions(nla):
return actstr
+ class push_vlan(nla):
+ fields = (("vlan_tpid", "!H"), ("vlan_tci", "!H"))
+
class sample(nla):
nla_flags = NLA_F_NESTED
@@ -633,6 +636,14 @@ class ovsactions(nla):
print_str += "ct_clear"
elif field[0] == "OVS_ACTION_ATTR_POP_VLAN":
print_str += "pop_vlan"
+ elif field[0] == "OVS_ACTION_ATTR_PUSH_VLAN":
+ datum = self.get_attr(field[0])
+ tpid = datum["vlan_tpid"]
+ tci = datum["vlan_tci"]
+ vid = tci & 0x0FFF
+ pcp = (tci >> 13) & 0x7
+ print_str += "push_vlan(vid=%d,pcp=%d" \
+ ",tpid=0x%04x)" % (vid, pcp, tpid)
elif field[0] == "OVS_ACTION_ATTR_POP_ETH":
print_str += "pop_eth"
elif field[0] == "OVS_ACTION_ATTR_POP_NSH":
@@ -726,7 +737,57 @@ class ovsactions(nla):
actstr = actstr[strspn(actstr, ", ") :]
parsed = True
- if parse_starts_block(actstr, "clone(", False):
+ if parse_starts_block(actstr, "push_vlan(", False):
+ actstr = actstr[len("push_vlan("):]
+ vid = 0
+ pcp = 0
+ tpid = 0x8100
+ if ")" not in actstr:
+ raise ValueError(
+ "push_vlan: missing ')'")
+ paren = actstr.index(")")
+ if not actstr[:paren].strip():
+ raise ValueError("push_vlan: no fields")
+ for kv in actstr[:paren].split(","):
+ if "=" not in kv:
+ raise ValueError(
+ "push_vlan: bad field '%s'"
+ % kv.strip())
+ k = kv[:kv.index("=")].strip()
+ v = kv[kv.index("=") + 1:].strip()
+ if k == "vid":
+ vid = int(v, 0)
+ if vid < 0 or vid > 0xFFF:
+ raise ValueError(
+ "push_vlan: vid=%d out of "
+ "range (0-4095)" % vid)
+ elif k == "pcp":
+ pcp = int(v, 0)
+ if pcp < 0 or pcp > 7:
+ raise ValueError(
+ "push_vlan: pcp=%d out of "
+ "range (0-7)" % pcp)
+ elif k == "tpid":
+ tpid = int(v, 0)
+ if tpid < 0 or tpid > 0xFFFF:
+ raise ValueError(
+ "push_vlan: tpid=0x%x out "
+ "of range (0-0xffff)" % tpid)
+ else:
+ raise ValueError(
+ "push_vlan: unknown key '%s'"
+ % k)
+ tci = (vid & 0x0FFF) | ((pcp & 0x7) << 13) \
+ | 0x1000
+ pvact = self.push_vlan()
+ pvact["vlan_tpid"] = tpid
+ pvact["vlan_tci"] = tci
+ self["attrs"].append(
+ ["OVS_ACTION_ATTR_PUSH_VLAN", pvact])
+ actstr = actstr[paren + 1:]
+ parsed = True
+
+ elif parse_starts_block(actstr, "clone(", False):
parencount += 1
subacts = ovsactions()
actstr = actstr[len("clone("):]
@@ -901,11 +962,11 @@ class ovskey(nla):
nla_flags = NLA_F_NESTED
nla_map = (
("OVS_KEY_ATTR_UNSPEC", "none"),
- ("OVS_KEY_ATTR_ENCAP", "none"),
+ ("OVS_KEY_ATTR_ENCAP", "encap_ovskey"),
("OVS_KEY_ATTR_PRIORITY", "uint32"),
("OVS_KEY_ATTR_IN_PORT", "uint32"),
("OVS_KEY_ATTR_ETHERNET", "ethaddr"),
- ("OVS_KEY_ATTR_VLAN", "uint16"),
+ ("OVS_KEY_ATTR_VLAN", "be16"),
("OVS_KEY_ATTR_ETHERTYPE", "be16"),
("OVS_KEY_ATTR_IPV4", "ovs_key_ipv4"),
("OVS_KEY_ATTR_IPV6", "ovs_key_ipv6"),
@@ -1636,6 +1697,194 @@ class ovskey(nla):
class ovs_key_mpls(nla):
fields = (("lse", ">I"),)
+ # 802.1Q CFI (Canonical Format Indicator) bit, always set for Ethernet
+ _VLAN_CFI_MASK = 0x1000
+
+ @staticmethod
+ def _vlan_dpstr(tci):
+ """Format VLAN TCI as vid=X,pcp=Y,cfi=Z or tci=0xNNNN.
+
+ When cfi=1 (standard Ethernet VLAN), outputs decomposed
+ vid/pcp/cfi fields. When cfi=0 (truncated VLAN header),
+ falls back to raw tci=0x%04x to ensure round-trip
+ correctness: the parser auto-adds cfi=1 for vid/pcp
+ format, so cfi=0 would be lost on re-parse."""
+ vid = tci & 0x0FFF
+ pcp = (tci >> 13) & 0x7
+ cfi = (tci >> 12) & 0x1
+ if cfi:
+ return "vid=%d,pcp=%d,cfi=%d" % (vid, pcp, cfi)
+ return "tci=0x%04x" % tci
+
+ @staticmethod
+ def _parse_vlan_from_flowstr(flowstr):
+ """Parse vlan(tci=X) or vlan(vid=X[,pcp=Y,cfi=Z]) from flowstr.
+
+ Returns (remaining_flowstr, key_tci, mask_tci).
+ TCI values use standard bit layout (VID bits 0-11,
+ CFI bit 12, PCP bits 13-15); byte order conversion to
+ big-endian happens in pyroute2 be16 NLA serialization.
+ The mask covers only the fields the caller specified:
+ vid -> 0x0FFF, pcp -> 0xE000, cfi -> 0x1000, tci -> 0xFFFF.
+
+ The tci= key sets the raw TCI bitfield (no CFI validation) to allow
+ non-Ethernet use cases. Use cfi=1 for standard Ethernet VLAN matching.
+ """
+ tci = 0
+ mask = 0
+ has_tci = False
+ has_vid = has_pcp = has_cfi = False
+ _tci_mix_err = "vlan(): 'tci' cannot be mixed " \
+ "with 'vid'/'pcp'/'cfi'"
+ first = True
+ while True:
+ flowstr = flowstr.lstrip()
+ if not flowstr:
+ raise ValueError("vlan(): missing ')'")
+ if flowstr[0] == ')':
+ break
+ if not first:
+ flowstr = flowstr[1:] # skip ','
+ if not flowstr:
+ raise ValueError("vlan(): missing ')' after trailing comma")
+ flowstr = flowstr.lstrip()
+ if flowstr and flowstr[0] == ')':
+ break
+ if flowstr and flowstr[0] == ',':
+ raise ValueError(
+ "vlan(): empty or extra comma in field list")
+ first = False
+
+ eq = flowstr.find('=')
+ if eq == -1:
+ raise ValueError(
+ "vlan(): expected key=value, got '%s'" % flowstr)
+ key = flowstr[:eq].strip()
+ flowstr = flowstr[eq + 1:]
+
+ end = flowstr.find(',')
+ end2 = flowstr.find(')')
+ if end == -1 and end2 == -1:
+ raise ValueError("vlan(): missing ')'")
+ if end == -1 or (end2 != -1 and end2 < end):
+ end = end2
+ val = flowstr[:end].strip()
+ flowstr = flowstr[end:]
+
+ if not val:
+ raise ValueError("vlan(): empty value for key '%s'" % key)
+ try:
+ v = int(val, 16) if val.startswith(('0x', '0X')) else int(val)
+ except ValueError as exc:
+ raise ValueError(
+ "vlan(): invalid value '%s' for key '%s'"
+ % (val, key)) from exc
+
+ if key == 'tci':
+ if has_tci:
+ raise ValueError("vlan(): duplicate 'tci'")
+ if has_vid or has_pcp or has_cfi:
+ raise ValueError(_tci_mix_err)
+ if v > 0xFFFF or v < 0:
+ raise ValueError("vlan(): tci=0x%x out of range" % v)
+ tci = v
+ mask = 0xFFFF
+ has_tci = True
+ elif key == 'vid':
+ if has_tci:
+ raise ValueError(_tci_mix_err)
+ if has_vid:
+ raise ValueError("vlan(): duplicate 'vid'")
+ if v < 0 or v > 0xFFF:
+ raise ValueError("vlan(): vid=%d out of range (0-4095)" % v)
+ tci |= v
+ mask |= 0x0FFF
+ has_vid = True
+ elif key == 'pcp':
+ if has_tci:
+ raise ValueError(_tci_mix_err)
+ if has_pcp:
+ raise ValueError("vlan(): duplicate 'pcp'")
+ if v < 0 or v > 7:
+ raise ValueError("vlan(): pcp=%d out of range (0-7)" % v)
+ tci |= (v & 0x7) << 13
+ mask |= 0xE000
+ has_pcp = True
+ elif key == 'cfi':
+ if has_tci:
+ raise ValueError(_tci_mix_err)
+ if has_cfi:
+ raise ValueError("vlan(): duplicate 'cfi'")
+ if v != 1:
+ raise ValueError("vlan(): cfi must be 1 for Ethernet")
+ tci |= ovskey._VLAN_CFI_MASK
+ mask |= ovskey._VLAN_CFI_MASK
+ has_cfi = True
+ else:
+ raise ValueError("vlan(): unknown key '%s'" % key)
+
+ flowstr = flowstr[1:] # skip ')'
+ # Catch immediate '))' (user error). A ')' after ',' is consumed
+ # by parse()'s strspn(flowstr, "), ") inter-field separator stripping.
+ if flowstr.lstrip().startswith(')'):
+ raise ValueError("vlan(): unmatched ')'")
+ # parse() strips trailing ',', ')', ' ' as inter-field separators,
+ # so we do not need to call strspn here.
+
+ if mask == 0:
+ raise ValueError("vlan(): no fields specified, "
+ "use vlan(vid=X[,pcp=Y,cfi=Z]) or vlan(tci=X)")
+ if not has_tci:
+ tci |= ovskey._VLAN_CFI_MASK
+ mask |= ovskey._VLAN_CFI_MASK
+ return flowstr, tci, mask
+
+ @staticmethod
+ def _parse_encap_from_flowstr(flowstr):
+ """Parse encap(inner_flow) from flowstr.
+
+ Returns (remaining_flowstr, inner_key_dict, inner_mask_dict)
+ where each dict has an 'attrs' key for recursive NLA encoding.
+ Parenthesis-depth tracking handles nested encap() calls but not
+ quoted strings containing literal parentheses.
+ """
+ depth = 1
+ end = -1
+ for i, c in enumerate(flowstr):
+ if c == '(':
+ depth += 1
+ elif c == ')':
+ depth -= 1
+ if depth < 0:
+ raise ValueError(
+ "encap(): unmatched ')' at position %d" % i)
+ if depth == 0:
+ end = i
+ break
+
+ if end == -1:
+ if depth > 1:
+ raise ValueError("encap(): missing ')' at end")
+ raise ValueError("encap(): missing closing ')'")
+
+ inner_str = flowstr[:end].strip()
+ if not inner_str:
+ raise ValueError("encap(): empty inner flow")
+
+ flowstr = flowstr[end + 1:]
+ if flowstr.lstrip().startswith(')'):
+ raise ValueError("encap(): unmatched ')' after encap()")
+
+ inner_key = encap_ovskey()
+ inner_mask = encap_ovskey()
+ remaining = inner_key.parse(inner_str, inner_mask)
+ if remaining and re.search(r'[^\s,)]', remaining):
+ raise ValueError(
+ "encap(): unrecognized trailing "
+ "content '%s'" % remaining.strip())
+
+ return flowstr, inner_key, inner_mask
+
def parse(self, flowstr, mask=None):
for field in (
("OVS_KEY_ATTR_PRIORITY", "skb_priority", intparse),
@@ -1657,6 +1906,16 @@ class ovskey(nla):
"eth_type",
lambda x: intparse(x, "0xffff"),
),
+ (
+ "OVS_KEY_ATTR_VLAN",
+ "vlan",
+ ovskey._parse_vlan_from_flowstr,
+ ),
+ (
+ "OVS_KEY_ATTR_ENCAP",
+ "encap",
+ ovskey._parse_encap_from_flowstr,
+ ),
(
"OVS_KEY_ATTR_IPV4",
"ipv4",
@@ -1794,6 +2053,9 @@ class ovskey(nla):
True,
),
("OVS_KEY_ATTR_ETHERNET", None, None, False, False),
+ ("OVS_KEY_ATTR_VLAN", "vlan", ovskey._vlan_dpstr,
+ lambda x: False, True),
+ ("OVS_KEY_ATTR_ENCAP", None, None, False, False),
(
"OVS_KEY_ATTR_ETHERTYPE",
"eth_type",
@@ -1821,22 +2083,61 @@ class ovskey(nla):
v = self.get_attr(field[0])
if v is not None:
m = None if mask is None else mask.get_attr(field[0])
+ fmt = field[2] # str format or callable
if field[4] is False:
print_str += v.dpstr(m, more)
print_str += ","
else:
if m is None or field[3](m):
- print_str += field[1] + "("
- print_str += field[2] % v
- print_str += "),"
+ val = fmt(v) if callable(fmt) else fmt % v
+ print_str += field[1] + "(" + val + "),"
elif more or m != 0:
- print_str += field[1] + "("
- print_str += (field[2] % v) + "/" + (field[2] % m)
- print_str += "),"
+ if callable(fmt):
+ val = fmt(v) + "/" + fmt(m)
+ else:
+ val = (fmt % v) + "/" + (fmt % m)
+ print_str += field[1] + "(" + val + "),"
return print_str
+class encap_ovskey(ovskey):
+ """Inner flow key attributes valid inside 802.1Q ENCAP.
+
+ Only L2-L4 key attributes (slots 0-21) appear inside ENCAP.
+ Metadata-only attributes (SKB_MARK, DP_HASH, RECIRC_ID, etc.)
+ are set to "none" — they never appear inside ENCAP per
+ ovs_nla_put_vlan() in net/openvswitch/flow_netlink.c.
+
+ nla_map indexes must match OVS_KEY_ATTR_* enum values in
+ include/uapi/linux/openvswitch.h.
+ """
+ nla_map = (
+ ("OVS_KEY_ATTR_UNSPEC", "none"), # 0
+ ("OVS_KEY_ATTR_ENCAP", "none"), # 1 — placeholder, no recursion
+ ("OVS_KEY_ATTR_PRIORITY", "none"), # 2 — skb metadata, not in ENCAP
+ ("OVS_KEY_ATTR_IN_PORT", "none"), # 3 — skb metadata, not in ENCAP
+ ("OVS_KEY_ATTR_ETHERNET", "ethaddr"), # 4
+ ("OVS_KEY_ATTR_VLAN", "be16"), # 5
+ ("OVS_KEY_ATTR_ETHERTYPE", "be16"), # 6
+ ("OVS_KEY_ATTR_IPV4", "ovs_key_ipv4"), # 7
+ ("OVS_KEY_ATTR_IPV6", "ovs_key_ipv6"), # 8
+ ("OVS_KEY_ATTR_TCP", "ovs_key_tcp"), # 9
+ ("OVS_KEY_ATTR_UDP", "ovs_key_udp"), # 10
+ ("OVS_KEY_ATTR_ICMP", "ovs_key_icmp"), # 11
+ ("OVS_KEY_ATTR_ICMPV6", "ovs_key_icmpv6"), # 12
+ ("OVS_KEY_ATTR_ARP", "ovs_key_arp"), # 13
+ ("OVS_KEY_ATTR_ND", "ovs_key_nd"), # 14
+ ("OVS_KEY_ATTR_SKB_MARK", "none"), # 15 — metadata, not in ENCAP
+ ("OVS_KEY_ATTR_TUNNEL", "none"), # 16 — tunnel metadata, not in ENCAP
+ ("OVS_KEY_ATTR_SCTP", "ovs_key_sctp"), # 17
+ ("OVS_KEY_ATTR_TCP_FLAGS", "be16"), # 18
+ ("OVS_KEY_ATTR_DP_HASH", "none"), # 19 — metadata, not in ENCAP
+ ("OVS_KEY_ATTR_RECIRC_ID", "none"), # 20 — metadata, not in ENCAP
+ ("OVS_KEY_ATTR_MPLS", "array(ovs_key_mpls)"), # 21
+ )
+
+
class OvsPacket(GenericNetlinkSocket):
OVS_PACKET_CMD_MISS = 1 # Flow table miss
OVS_PACKET_CMD_ACTION = 2 # USERSPACE action
@@ -2576,6 +2877,7 @@ def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):
def main(argv):
+ nlmsg_atoms.encap_ovskey = encap_ovskey
nlmsg_atoms.ovskey = ovskey
nlmsg_atoms.ovsactions = ovsactions
--
2.53.0
^ permalink raw reply related
* [PATCH net-next v5 2/2] selftests: openvswitch: add pop_vlan test
From: Minxi Hou @ 2026-05-05 12:49 UTC (permalink / raw)
To: netdev
Cc: aconole, echaudro, i.maximets, davem, edumazet, kuba, pabeni,
horms, shuah, dev, linux-kselftest, linux-kernel, Minxi Hou
In-Reply-To: <20260505124957.1239812-1-houminxi@gmail.com>
Add test_pop_vlan() to verify OVS kernel datapath pop_vlan action
correctly strips 802.1Q VLAN tags from frames.
Test structure:
- Baseline: untagged forwarding validates basic connectivity.
- Negative: forward without pop_vlan, tagged frame is invisible
to ns2 (no VLAN sub-interface), ping fails.
- Positive: pop_vlan strips tag on forward path, push_vlan
restores tag on return path, ping succeeds.
Use static ARP entries to avoid VLAN-tagged ARP complexity.
Rely on ping success/failure for verification — no tcpdump or
pcap files needed.
Signed-off-by: Minxi Hou <houminxi@gmail.com>
---
.../selftests/net/openvswitch/openvswitch.sh | 77 +++++++++++++++++++
1 file changed, 77 insertions(+)
diff --git a/tools/testing/selftests/net/openvswitch/openvswitch.sh b/tools/testing/selftests/net/openvswitch/openvswitch.sh
index b327d3061ed5..a64f4d515e83 100755
--- a/tools/testing/selftests/net/openvswitch/openvswitch.sh
+++ b/tools/testing/selftests/net/openvswitch/openvswitch.sh
@@ -27,6 +27,7 @@ tests="
upcall_interfaces ovs: test the upcall interfaces
tunnel_metadata ovs: test extraction of tunnel metadata
drop_reason drop: test drop reasons are emitted
+ pop_vlan vlan: POP_VLAN action strips tag
psample psample: Sampling packets with psample"
info() {
@@ -830,6 +831,82 @@ test_tunnel_metadata() {
return 0
}
+test_pop_vlan() {
+ modprobe -q 8021q 2>/dev/null || true
+ [ -d /sys/module/8021q ] || \
+ { info "CONFIG_VLAN_8021Q missing"; return $ksft_skip; }
+
+ local sbx="test_pop_vlan"
+ sbx_add "$sbx" || return $?
+ ovs_add_dp "$sbx" vlandp || return 1
+
+ ovs_add_netns_and_veths "$sbx" vlandp \
+ ns1 veth1 ns1veth 192.0.2.1/24 || return 1
+ ovs_add_netns_and_veths "$sbx" vlandp \
+ ns2 veth2 ns2veth 192.0.2.2/24 || return 1
+
+ # Baseline: untagged bidirectional forwarding
+ ovs_add_flow "$sbx" vlandp \
+ 'in_port(1),eth(),eth_type(0x0806),arp()' '2' || return 1
+ ovs_add_flow "$sbx" vlandp \
+ 'in_port(2),eth(),eth_type(0x0806),arp()' '1' || return 1
+ ovs_add_flow "$sbx" vlandp \
+ 'in_port(1),eth(),eth_type(0x0800),ipv4()' '2' || return 1
+ ovs_add_flow "$sbx" vlandp \
+ 'in_port(2),eth(),eth_type(0x0800),ipv4()' '1' || return 1
+ ovs_sbx "$sbx" ip netns exec ns1 ping -c 3 -W 2 \
+ 192.0.2.2 || return 1
+
+ # VLAN topology: ns1 uses VLAN sub-interface, ns2 is plain
+ ip -n ns1 link add link ns1veth name ns1veth.10 \
+ type vlan id 10 || return 1
+ on_exit "ip -n ns1 link del ns1veth.10 2>/dev/null"
+ ip -n ns1 addr add 198.51.100.1/24 dev ns1veth.10 || return 1
+ ip -n ns1 link set ns1veth.10 up || return 1
+ ip -n ns2 addr add 198.51.100.2/24 dev ns2veth || return 1
+
+ ovs_del_flows "$sbx" vlandp
+
+ # Static ARP: avoids VLAN-tagged ARP complexity
+ local ns1veth10mac ns2mac
+ ns1veth10mac=$(ip -n ns1 link show ns1veth.10 \
+ | awk '/link\/ether/ {print $2}')
+ ns2mac=$(ip -n ns2 link show ns2veth \
+ | awk '/link\/ether/ {print $2}')
+ ip -n ns1 neigh replace 198.51.100.2 lladdr "$ns2mac" \
+ dev ns1veth.10 nud permanent || return 1
+ ip -n ns2 neigh replace 198.51.100.1 \
+ lladdr "$ns1veth10mac" \
+ dev ns2veth nud permanent || return 1
+
+ local vlan_match='in_port(1),eth(),eth_type(0x8100),'
+ vlan_match+='vlan(vid=10),'
+ vlan_match+='encap(eth_type(0x0800),'
+ vlan_match+='ipv4(src=198.51.100.1,proto=1),icmp())'
+
+ # Negative: forward without pop_vlan — tagged frame
+ # is invisible to ns2 (no VLAN sub-interface), ping fails
+ ovs_add_flow "$sbx" vlandp "$vlan_match" '2' || return 1
+ ovs_sbx "$sbx" ip netns exec ns1 ping -I ns1veth.10 \
+ -c 3 -W 1 198.51.100.2 >/dev/null 2>&1 \
+ && { info "FAIL: ping should fail without pop_vlan"
+ return 1; }
+
+ ovs_del_flows "$sbx" vlandp
+
+ # Positive: pop_vlan strips tag on forward path,
+ # push_vlan restores tag on return path — ping succeeds
+ ovs_add_flow "$sbx" vlandp \
+ "$vlan_match" 'pop_vlan,2' || return 1
+ ovs_add_flow "$sbx" vlandp \
+ 'in_port(2),eth(),eth_type(0x0800),ipv4()' \
+ 'push_vlan(vid=10,pcp=0,tpid=0x8100),1' || return 1
+ ovs_sbx "$sbx" ip netns exec ns1 ping -I ns1veth.10 \
+ -c 3 -W 2 198.51.100.2 || return 1
+
+ return 0
+}
+
run_test() {
(
tname="$1"
--
2.53.0
^ permalink raw reply related
* Re: [PATCH net-next v3 0/4] irst series for xpcs based rsfec configuration
From: patchwork-bot+netdevbpf @ 2026-05-05 12:50 UTC (permalink / raw)
To: Mike Marciniszyn
Cc: alexanderduyck, kuba, kernel-team, andrew+netdev, davem, edumazet,
pabeni, hkallweit1, linux, jacob.e.keller, mohsin.bashr, horms,
lee, andrew, netdev, linux-kernel
In-Reply-To: <20260504135815.44226-1-mike.marciniszyn@gmail.com>
Hello:
This series was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Mon, 4 May 2026 09:58:11 -0400 you wrote:
> From: "Mike Marciniszyn (Meta)" <mike.marciniszyn@gmail.com>
>
> The series:
> - Fixes an addr validation error
> - Adds MDIO defines associated with RS-FEC
> - consolidates the handling of the boilerplat ID registers
> into a routine to report id'ish registers and reduces the lines
> of code across the entire set of c45 routines.
> - adds PMA read/write routines
>
> [...]
Here is the summary with links:
- [net-next,v3,1/4] net: eth: fbnic: Fix addr validation in pcs write
(no matching commit)
- [net-next,v3,2/4] net: mdio: Add support for RSFEC Control register for PMA
https://git.kernel.org/netdev/net-next/c/ca283942e5b9
- [net-next,v3,3/4] net: eth: fbnic: Consolidate register reads for ids and devs
https://git.kernel.org/netdev/net-next/c/d7dbf00b4a55
- [net-next,v3,4/4] net: eth: fbnic: Add pma read and write access
https://git.kernel.org/netdev/net-next/c/3877097c3c9e
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH net-next v2 0/3] first series for xpcs based rsfec configuration
From: patchwork-bot+netdevbpf @ 2026-05-05 12:50 UTC (permalink / raw)
To: Mike Marciniszyn
Cc: alexanderduyck, kuba, kernel-team, andrew+netdev, davem, edumazet,
pabeni, hkallweit1, linux, jacob.e.keller, mohsin.bashr, lee,
andrew, netdev, linux-kernel
In-Reply-To: <20260430150802.3521-1-mike.marciniszyn@gmail.com>
Hello:
This series was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Thu, 30 Apr 2026 11:07:59 -0400 you wrote:
> From: "Mike Marciniszyn (Meta)" <mike.marciniszyn@gmail.com>
>
> The series:
> - Fixes an addr validation error
> - Adds MDIO defines associated with RS-FEC
> - consolidates the handling of the boilerplat ID registers
> into a routine to report id'ish registers and reduces the lines
> of code across the entire set of c45 routines.
> - adds PMA read/write routines
>
> [...]
Here is the summary with links:
- [net-next,v2,1/3] net: mdio: Add support for RSFEC Control register for PMA
https://git.kernel.org/netdev/net-next/c/ca283942e5b9
- [net-next,v2,2/3] net: eth: fbnic: Consolidate register reads for ids and devs
(no matching commit)
- [net-next,v2,3/3] net: eth: fbnic: Add pma read and write access
(no matching commit)
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* [PATCH iwl-net v2] ice: fix missing priority callbacks for U.FL DPLL pins
From: Petr Oros @ 2026-05-05 12:51 UTC (permalink / raw)
To: netdev
Cc: Petr Oros, Aleksandr Loktionov, Paul Menzel, Tony Nguyen,
Przemek Kitszel, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Arkadiusz Kubalewski,
intel-wired-lan, linux-kernel
The U.FL2 input pin advertises DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE
in its capability mask, but ice_dpll_pin_ufl_ops does not provide
.prio_get and .prio_set callbacks. As a result the DPLL subsystem
cannot report or accept priority for U.FL pins: pin-get omits the prio
field on U.FL2 and pin-set with prio is rejected as invalid, even
though the capability is present. This prevents user space from using
priority to select or disable U.FL2 as a DPLL input source.
Reproducer with iproute2 (dpll command):
# dpll pin show board-label U.FL2
pin id 16:
module-name ice
board-label U.FL2
type ext
capabilities priority-can-change|state-can-change
parent-device:
id 0 direction input state selectable phase-offset 0
/* note: no "prio" between "direction" and "state",
even though priority-can-change is advertised */
# dpll pin set id 16 parent-device 0 prio 5
RTNETLINK answers: Operation not supported
After the fix the prio field is reported by pin show and pin set with
prio is accepted on U.FL2.
Add the missing .prio_get and .prio_set callbacks to
ice_dpll_pin_ufl_ops, reusing ice_dpll_sw_input_prio_{get,set}. The
same ops struct is shared by U.FL1 and U.FL2: U.FL2 (input) delegates
to the backing hardware input pin, while U.FL1 (output) does not
advertise DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE so the dpll core
capability gate never invokes prio_set for it, and prio_get reports
the OUTPUT sentinel (ICE_DPLL_PIN_PRIO_OUTPUT) on the output side
exactly like the SMA path does today.
Fixes: 2dd5d03c77e2 ("ice: redesign dpll sma/u.fl pins control")
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
Signed-off-by: Petr Oros <poros@redhat.com>
---
v2:
- describe the userspace reproducer (dpll pin show / dpll pin set)
in the commit message, suggested by Paul Menzel
- collect Reviewed-by tags from v1
v1: https://lore.kernel.org/all/20260504121603.1702674-1-poros@redhat.com/
---
drivers/net/ethernet/intel/ice/ice_dpll.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
index 27b460926baced..be72a076f7a15c 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.c
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -2628,6 +2628,8 @@ static const struct dpll_pin_ops ice_dpll_pin_ufl_ops = {
.state_on_dpll_set = ice_dpll_ufl_pin_state_set,
.state_on_dpll_get = ice_dpll_sw_pin_state_get,
.direction_get = ice_dpll_pin_sw_direction_get,
+ .prio_get = ice_dpll_sw_input_prio_get,
+ .prio_set = ice_dpll_sw_input_prio_set,
.frequency_get = ice_dpll_sw_pin_frequency_get,
.frequency_set = ice_dpll_sw_pin_frequency_set,
.esync_set = ice_dpll_sw_esync_set,
--
2.53.0
^ permalink raw reply related
* Re: [PATCH RESEND] net: stmmac: fix RX DMA leak on TX alloc failure
From: Andrew Lunn @ 2026-05-05 12:57 UTC (permalink / raw)
To: dev.taqnialabs
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Maxime Coquelin, Alexandre Torgue, netdev,
linux-stm32, linux-arm-kernel, linux-kernel
In-Reply-To: <20260505-stmmac-rx-desc-cleanup-v1-1-df85cd095ebc@gmail.com>
On Tue, May 05, 2026 at 08:23:07AM +0000, Abid Ali via B4 Relay wrote:
> From: Abid Ali <dev.taqnialabs@gmail.com>
>
> Free RX DMA resources when alloc_dma_tx_desc_resources() fails in
> alloc_dma_desc_resources().
>
> Signed-off-by: Abid Ali <dev.taqnialabs@gmail.com>
> ---
> drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 6 +++++-
> 1 file changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> index 13d3cac05..8bb843b55 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> @@ -2370,8 +2370,12 @@ static int alloc_dma_desc_resources(struct stmmac_priv *priv,
> return ret;
>
> ret = alloc_dma_tx_desc_resources(priv, dma_conf);
> + if (ret) {
> + free_dma_rx_desc_resources(priv, dma_conf);
> + return ret;
> + }
>
> - return ret;
> + return 0;
You could keep the return ret, and simplify the code:
ret = alloc_dma_tx_desc_resources(priv, dma_conf);
if (ret)
free_dma_rx_desc_resources(priv, dma_conf);
return ret;
Andrew
---
pw-bot: cr
^ permalink raw reply
* [PATCH net] ipv6: fix potential UAF caused by ip6_forward_proxy_check()
From: Eric Dumazet @ 2026-05-05 13:00 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Ido Schimmel, David Ahern, netdev, eric.dumazet,
Eric Dumazet, Damiano Melotti
ip6_forward_proxy_check() calls pskb_may_pull() which might re-allocate
skb->head.
Reload ipv6_hdr() after the pskb_may_pull() call to avoid using
the freed memory.
Fixes: e21e0b5f19ac ("[IPV6] NDISC: Handle NDP messages to proxied addresses.")
Reported-by: Damiano Melotti <melotti@google.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net/ipv6/ip6_output.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 7e92909ab5be3f86b10bf9527e97f3bfe794e9e9..aee91ba04130527ceb19881dc1e7232a5d2c8580 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -468,6 +468,7 @@ static int ip6_forward_proxy_check(struct sk_buff *skb)
default:
break;
}
+ hdr = ipv6_hdr(skb);
}
/*
@@ -582,6 +583,8 @@ int ip6_forward(struct sk_buff *skb)
if (READ_ONCE(net->ipv6.devconf_all->proxy_ndp) &&
pneigh_lookup(&nd_tbl, net, &hdr->daddr, skb->dev)) {
int proxied = ip6_forward_proxy_check(skb);
+
+ hdr = ipv6_hdr(skb);
if (proxied > 0) {
/* It's tempting to decrease the hop limit
* here by 1, as we do at the end of the
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* Re: [PATCH net v3] tipc: fix UAF race in tipc_mon_peer_up/down/remove_peer vs bearer teardown
From: Paolo Abeni @ 2026-05-05 13:01 UTC (permalink / raw)
To: SnailSploit | Kai Aizen, netdev
Cc: stable, jmaloy, ying.xue, kuba, tipc-discussion, tung.q.nguyen,
lkp, oe-kbuild-all, syzkaller-bugs, SnailSploit | Kai Aizen,
syzbot ci
In-Reply-To: <80ae67e96de2f702028e5bacc89db4575e1531ca.1777559945.git.kai.aizen.dev@gmail.com>
On 4/30/26 5:26 PM, SnailSploit | Kai Aizen wrote:
> @@ -422,9 +422,12 @@ static bool tipc_mon_add_peer(struct tipc_monitor *mon, u32 addr,
> void tipc_mon_peer_up(struct net *net, u32 addr, int bearer_id)
> {
> struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
> - struct tipc_peer *self = get_self(net, bearer_id);
> + struct tipc_peer *self;
> struct tipc_peer *peer, *head;
Minor nit: please respect the reverse christmas tree order above.
>
> + if (!mon)
> + return;
Also an empty line here (and other similar places in the patch) will
make the code more readable.
> @@ -663,7 +666,7 @@ int tipc_mon_create(struct net *net, int bearer_id)
> kfree(dom);
> return -ENOMEM;
> }
> - tn->monitors[bearer_id] = mon;
> + rcu_assign_pointer(tn->monitors[bearer_id], mon);
> rwlock_init(&mon->lock);
> mon->net = net;
> mon->peer_cnt = 1;
Sashiko says:
Does rcu_assign_pointer() publish the mon object before its lock
and fields are fully initialized?
Since rcu_assign_pointer() provides a release barrier, a concurrent
lockless RCU reader (like tipc_mon_peer_up()) could observe the new
mon pointer and attempt to acquire write_lock_bh(&mon->lock) before
rwlock_init(&mon->lock) has executed, or dereference a still-NULL
mon->self.
Should the publication step be moved to the absolute end of the
initialization sequence?
Note that sashiko has more remarks, even if they looks like pre-existing
issues to me.
/P
^ permalink raw reply
* Re: Vulnerability Report: Logical Error in 6LoWPAN Multicast Context Address Compression
From: Andrew Lunn @ 2026-05-05 13:01 UTC (permalink / raw)
To: Quan Sun; +Cc: linux-wpan, netdev, alex.aring, davem, edumazet
In-Reply-To: <15e69058-da2e-4a4d-8bda-ad89da0ae6f7@std.uestc.edu.cn>
On Tue, May 05, 2026 at 05:18:34PM +0800, Quan Sun wrote:
> ## 1. Summary
> A logical vulnerability exists in the 6LoWPAN IPHC (IP Header Compression)
> subsystem of the Linux kernel, specifically within the
> `lowpan_iphc_mcast_ctx_addr_compress` function in `net/6lowpan/iphc.c`.
>
> The function uses incorrect memory offsets during the `memcpy` operations
> intended to compress an IPv6 multicast address. This mismatch in offsets
> results in an incorrectly formed compressed address being transmitted over
> the network, which is incompatible with the corresponding decompression
> logic. Consequently, context-based multicast address compression in 6LoWPAN
> is broken and fails to operate as defined by the protocol.
>
> ## 2. Vulnerability Details
>
> According to 6LoWPAN address compression standards (and aligning with the
> decompression function `lowpan_uncompress_multicast_ctx_daddr`), a
> context-based compressed multicast address should be represented by exactly
> 6 bytes:
> * **Bytes 0-1:** Derived from `s6_addr[1]` and `s6_addr[2]` (Flags, Scope,
> and Reserved bits).
> * **Bytes 2-5:** Derived from `s6_addr[12]` to `s6_addr[15]` (The 4-byte
> Group ID).
>
> However, in the compression function `lowpan_iphc_mcast_ctx_addr_compress`,
> the offsets provided to the `memcpy` calls are flawed:
>
> ```c
> static u8 lowpan_iphc_mcast_ctx_addr_compress(u8 **hc_ptr,
> const struct lowpan_iphc_ctx *ctx,
> const struct in6_addr *ipaddr)
> {
> u8 data[6];
>
> /* flags/scope, reserved (RIID) */
> memcpy(data, &ipaddr->s6_addr[1], 2);
> /* group ID */
> memcpy(&data[1], &ipaddr->s6_addr[11], 4);
> lowpan_push_hc_data(hc_ptr, data, 6);
>
> return LOWPAN_IPHC_DAM_00;
> }
> ```
>
> ### Analysis of the Error:
> 1. **Incorrect Destination Offset:** The second `memcpy` writes to
> `&data[1]` instead of `&data[2]`. This overwrites the byte previously copied
> from `s6_addr[2]` into `data[1]`.
> 2. **Incorrect Source Offset:** The source address is specified as
> `&ipaddr->s6_addr[11]` instead of `&ipaddr->s6_addr[12]`. This means it
> begins reading from the last byte of the network prefix rather than the
> start of the 4-byte Group ID.
>
> Because the compression formatting does not match the expected structure
> required by the decompression function, multicast packets utilizing
> context-based compression will be corrupted upon transmission.
>
> ## 3. Impact
> This vulnerability breaks the Context-Based Multicast Address Compression
> feature (`LOWPAN_IPHC_DAM_00` when `M` and `DAC` bits are set) in 6LoWPAN
> networks. Nodes receiving these packets will incorrectly decompress the
> destination multicast address, leading to dropped packets and communication
> failures within the multicast group.
>
> ## 4. Suggested Fix
> The fix requires adjusting both the destination and source offsets in the
> second `memcpy` call to correctly place the 4-byte Group ID into the
> compressed `data` buffer.
>
> ### Proposed Patch:
>
> ```diff
> --- a/net/6lowpan/iphc.c
> +++ b/net/6lowpan/iphc.c
> @@ -1084,9 +1084,9 @@ static u8 lowpan_iphc_mcast_ctx_addr_compress(u8
> **hc_ptr,
> u8 data[6];
>
> /* flags/scope, reserved (RIID) */
> memcpy(data, &ipaddr->s6_addr[1], 2);
> /* group ID */
> - memcpy(&data[1], &ipaddr->s6_addr[11], 4);
> + memcpy(&data[2], &ipaddr->s6_addr[12], 4);
> lowpan_push_hc_data(hc_ptr, data, 6);
>
> return LOWPAN_IPHC_DAM_00;
> }
Since you have a fix, why not just submit a proper patch in the usual
way?
https://www.kernel.org/doc/html/latest/process/maintainer-netdev.html
https://docs.kernel.org/process/submitting-patches.html
Andrew
---
pw-bot: cr
^ permalink raw reply
* Re: [PATCH net-next v2] net: phy: broadcom: Save PHY counters during suspend
From: Paolo Abeni @ 2026-05-05 13:08 UTC (permalink / raw)
To: Justin Chen, netdev
Cc: kuba, edumazet, davem, linux, hkallweit1, andrew,
bcm-kernel-feedback-list, florian.fainelli
In-Reply-To: <20260430211117.2348478-1-justin.chen@broadcom.com>
On 4/30/26 11:11 PM, Justin Chen wrote:
> The PHY counters can be lost if the PHY is reset during suspend. We
> need to save the values into the shadow counters or the accounting
> will be incorrect over multiple suspend and resume cycles.
>
> Signed-off-by: Justin Chen <justin.chen@broadcom.com>
The patch LGTM, but why are you targeting net-next? This looks a fix for
net. Please share a relevant fixes tag, or explain why you prefer otherwise.
Thanks,
Paolo
^ permalink raw reply
* Re: assert in phylink.c with lan7801 and dp83tc811 since kernel 6.18
From: Andrew Lunn @ 2026-05-05 13:13 UTC (permalink / raw)
To: Sven Schuchmann; +Cc: netdev@vger.kernel.org
In-Reply-To: <BEZP281MB224533D524B27CFC6E47B447D93E2@BEZP281MB2245.DEUP281.PROD.OUTLOOK.COM>
On Tue, May 05, 2026 at 09:53:06AM +0000, Sven Schuchmann wrote:
> Hello,
> I have a raspberrypi and switched from kernel 6.12 to 6.18 and now I have a crash in phylink.c.
Please could you try 7.0.3, or better still, 7.1-rcX. We need to
determine if the problem has already been fixed and just needs
backporting, or is it a new problem.
> I am using a MAC:lan7801 and PHY:dp83tc811 and now it crashes like this:
>
> [ 3.019174] usb 1-1.3: New USB device found, idVendor=0424, idProduct=7801, bcdDevice= 3.00
> [ 3.021152] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
> [ 3.023192] usb 1-1.3: Product: LAN7801
> [ 3.025019] usb 1-1.3: Manufacturer: Microchip
> [ 3.026772] usb 1-1.3: SerialNumber: 00800F780100
> [ 3.078542] lan78xx 1-1.3:1.0 (unnamed net_device) (uninitialized): int urb period 64
> [ 3.091434] lan78xx 1-1.3:1.0 (unnamed net_device) (uninitialized): validation of rgmii-id with support 0000000,00000000,00000000,00006280 and advertis ement 0000000,00000000,00000000,00006280 failed: -EINVAL
This appears to be the real problem.
> [ 3.098928] lan78xx 1-1.3:1.0 (unnamed net_device) (uninitialized): can't attach PHY to usb-001:004, error -EINVAL
> [ 3.101094] ------------[ cut here ]------------
> [ 3.103163] RTNL: assertion failed at drivers/net/phy/phylink.c (2337)
and this is a knock on problem, which normally you don't see.
Please could you make line 1 of drivers/net/phy/phylink.c.
#define DEBUG 1
That will give us additional debug info.
Andrew
^ permalink raw reply
* Re: [RFC PATCH net-next 1/3] net: macb: flush PCIe posted write after TSTART doorbell
From: Andrea della Porta @ 2026-05-05 13:17 UTC (permalink / raw)
To: Lukasz Raczylo
Cc: netdev, Nicolas Ferre, Claudiu Beznea, Andrew Lunn,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-kernel, linux-arm-kernel, linux-rpi-kernel
In-Reply-To: <3106d546d494f2f52ec832e7f7d04f534286e254.1777064117.git.lukasz@raczylo.com>
Hi Lukasz,
On 23:38 Fri 24 Apr , Lukasz Raczylo wrote:
> macb_start_xmit() and macb_tx_restart() kick transmission by
> OR-ing MACB_BIT(TSTART) into NCR. On PCIe-attached macb instances
> (BCM2712 + RP1 PCIe south bridge on Raspberry Pi 5 is the setup we
> have in front of us), writes to NCR are posted PCIe writes: they
> are not guaranteed to reach the device before the issuing CPU
> returns. If the TSTART doorbell does not reach the MAC, no TX
> begins, no TCOMP completion arrives, and the ring remains
> quiescent without any kernel-visible indication.
>
> Note that the raspberrypi/linux vendor fork carries a local patch
> around the TSTART site (a queue->tx_pending breadcrumb that is
> promoted to queue->txubr_pending by the next TCOMP interrupt,
> triggering macb_tx_restart()). That workaround makes the loss
> recoverable under traffic, but it cannot help if TCOMP itself is
> not raised because no TX started -- which is exactly the case we
> are targeting here. The handshake is not present in mainline.
>
> Add a read-back of NCR after each TSTART write in macb_start_xmit()
> and macb_tx_restart(). The read is an architected PCIe read
> barrier for earlier posted writes on the same path; it ensures the
> doorbell has reached the MAC before the functions return.
>
> We do not yet have direct hardware evidence that TSTART is being
> lost on the RP1 path (that would require a PCIe protocol analyser,
> or at minimum a before/after counter on queue->tx_stall_last_tail
> with and without this patch applied in isolation). This patch is
> one of a three-patch series ("candidate fixes for silent TX stall
> on BCM2712/RP1"); see the cover letter for context. We have
> verified the series compiles and applies cleanly against mainline
> HEAD and against raspberrypi/linux rpi-6.18.y @ f2f68e79f16f;
> runtime verification is pending.
>
> Link: https://github.com/cilium/cilium/issues/43198
> Link: https://bugs.launchpad.net/ubuntu/+source/linux-raspi/+bug/2133877
> Signed-off-by: Lukasz Raczylo <lukasz@raczylo.com>
> ---
> drivers/net/ethernet/cadence/macb_main.c | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
> index a12aa2124..b6cca55ad 100644
> --- a/drivers/net/ethernet/cadence/macb_main.c
> +++ b/drivers/net/ethernet/cadence/macb_main.c
> @@ -1922,6 +1922,13 @@ static void macb_tx_restart(struct macb_queue *queue)
>
> spin_lock(&bp->lock);
> macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART));
> + /*
> + * Flush the PCIe posted-write queue so the TSTART doorbell
> + * reliably reaches the MAC. Without this, the write can sit
> + * in the fabric and the MAC never advances, causing a silent
> + * TX stall.
> + */
> + (void)macb_readl(bp, NCR);
Do you expect it to be a temporal issue (in the comment you stated that the write should be delivered
before the function returns) or is it just a dropped packet error? In the former case can you please
explain why? If it's the latter, I'd expect the dropped packet to increment the counter in the AER,
and I'm pretty sure it didn't when I performed my tests.
Many thanks,
Andrea
> spin_unlock(&bp->lock);
>
> out_tx_ptr_unlock:
> @@ -2560,6 +2567,11 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
> spin_lock(&bp->lock);
> macb_tx_lpi_wake(bp);
> macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART));
> + /*
> + * Flush the PCIe posted-write queue; see the comment in
> + * macb_tx_restart() for the reasoning.
> + */
> + (void)macb_readl(bp, NCR);
> spin_unlock(&bp->lock);
>
> if (CIRC_SPACE(queue->tx_head, queue->tx_tail, bp->tx_ring_size) < 1)
> --
> 2.53.0
>
^ permalink raw reply
* [PATCH net 0/2] Introduce CBS reset ops
From: Jamal Hadi Salim @ 2026-05-05 13:16 UTC (permalink / raw)
To: netdev
Cc: Jamal Hadi Salim, davem, kuba, edumazet, pabeni, jiri, horms,
vinicius.gomes, graypanda.inzag, victor
QFQ uses qlen to check whether it should reset pointer q->in_serv_agg.
When QFQ has a CBS child which has a netem child it is possible to create a
scenario where QFQ's qlen goes to 0 but the computation makes it underflow.
This then aggrevates a null pointer deref.
Jamal Hadi Salim (1):
net/sched: sch_cbs: Call qdisc_reset for child qdisc
Victor Nogueira (1):
selftests/tc-testing: Add QFQ/CBS qlen underflow test
net/sched/sch_cbs.c | 12 +++++-
.../tc-testing/tc-tests/infra/qdiscs.json | 41 +++++++++++++++++++
2 files changed, 52 insertions(+), 1 deletion(-)
--
2.34.1
^ permalink raw reply
* Re: [PATCH net] openvswitch: vport: fix race between tunnel creation and linking
From: patchwork-bot+netdevbpf @ 2026-05-05 13:20 UTC (permalink / raw)
To: Ilya Maximets
Cc: netdev, aconole, echaudro, davem, edumazet, kuba, pabeni, horms,
dev, linux-kernel, tanyuan98, yifanwucs, tomapufckgml, bird,
n05ec
In-Reply-To: <20260430213349.407991-1-i.maximets@ovn.org>
Hello:
This patch was applied to netdev/net.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Thu, 30 Apr 2026 23:32:50 +0200 you wrote:
> When a tunnel vport is created it first creates the tunnel device, e.g.,
> with geneve_dev_create_fb(), then it calls ovs_netdev_link() to take a
> reference and link it to the device that represents openvswitch datapath.
>
> The creation of the device is happening under RTNL, but then RTNL is
> released and re-acquired to find the device by name. It is technically
> possible for the tunnel device to be re-named or deleted within that
> window while RTNL is not held, and some other device created in its
> place. This will cause a non-tunnel device to be referenced in the
> vport and tunnel-specific functions used on it, e.g. vxlan_get_options()
> that directly casts the private netdev data into a struct vxlan_dev
> causing an invalid memory access:
>
> [...]
Here is the summary with links:
- [net] openvswitch: vport: fix race between tunnel creation and linking
https://git.kernel.org/netdev/net/c/83861c48ba12
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* [PATCH net 1/2] net/sched: sch_cbs: Call qdisc_reset for child qdisc
From: Jamal Hadi Salim @ 2026-05-05 13:21 UTC (permalink / raw)
To: netdev
Cc: Jamal Hadi Salim, davem, kuba, edumazet, pabeni, jiri, horms,
vinicius.gomes, graypanda.inzag, victor, security
During a reset, CBS is not calling reset on its child qdisc, which
might cause qlen/backlog accounting issues. For example, if we have CBS
with a QFQ parent and a netem child with delay, we can create a scenario
where the parent's qlen underflows. QFQ, specifically, uses qlen to
check whether it should dereference a pointer, so this scenario may cause
a null-ptr deref in QFQ:
[ 43.875639][ T319] Oops: general protection fault, probably for non-canonical address 0xdffffc0000000009: 0000 [#1] SMP KASAN NOPTI
[ 43.876124][ T319] KASAN: null-ptr-deref in range [0x0000000000000048-0x000000000000004f]
[ 43.876417][ T319] CPU: 10 UID: 0 PID: 319 Comm: ping Not tainted 7.0.0-13039-ge728258debd5 #773 PREEMPT(full)
[ 43.876751][ T319] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
[ 43.876949][ T319] RIP: 0010:qfq_dequeue+0x35c/0x1650
[ 43.877123][ T319] Code: 00 fc ff df 80 3c 02 00 0f 85 17 0e 00 00 4c 8d 73 48 48 89 9d b8 02 00 00 48 b8 00 00 00 00 00 fc ff df 4c 89 f2 48 c1 ea 03 <80> 3c 02 00 0f 85 76 0c 00 00 48 b8 00 00 00 00 00 fc ff df 4c 8b
[ 43.877648][ T319] RSP: 0018:ffff8881017ef4f0 EFLAGS: 00010216
[ 43.877845][ T319] RAX: dffffc0000000000 RBX: 0000000000000000 RCX: dffffc0000000000
[ 43.878073][ T319] RDX: 0000000000000009 RSI: 0000000c40000000 RDI: ffff88810eef02b0
[ 43.878306][ T319] RBP: ffff88810eef0000 R08: ffff88810eef0280 R09: 1ffff1102120fd63
[ 43.878523][ T319] R10: 1ffff1102120fd66 R11: 1ffff1102120fd67 R12: 0000000c40000000
[ 43.878742][ T319] R13: ffff88810eef02b8 R14: 0000000000000048 R15: 0000000020000000
[ 43.878959][ T319] FS: 00007f9c51c47c40(0000) GS:ffff88817a0be000(0000) knlGS:0000000000000000
[ 43.879214][ T319] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 43.879403][ T319] CR2: 000055e69a2230a8 CR3: 000000010c07a000 CR4: 0000000000750ef0
[ 43.879621][ T319] PKRU: 55555554
[ 43.879735][ T319] Call Trace:
[ 43.879844][ T319] <TASK>
[ 43.879924][ T319] __qdisc_run+0x169/0x1900
[ 43.880075][ T319] ? dev_qdisc_enqueue+0x8b/0x210
[ 43.880222][ T319] __dev_queue_xmit+0x2346/0x37a0
[ 43.880376][ T319] ? register_lock_class+0x3f/0x800
[ 43.880531][ T319] ? srso_alias_return_thunk+0x5/0xfbef5
[ 43.880684][ T319] ? __pfx___dev_queue_xmit+0x10/0x10
[ 43.880834][ T319] ? srso_alias_return_thunk+0x5/0xfbef5
[ 43.880977][ T319] ? __lock_acquire+0x819/0x1df0
[ 43.881124][ T319] ? srso_alias_return_thunk+0x5/0xfbef5
[ 43.881275][ T319] ? srso_alias_return_thunk+0x5/0xfbef5
[ 43.881418][ T319] ? __asan_memcpy+0x3c/0x60
[ 43.881563][ T319] ? srso_alias_return_thunk+0x5/0xfbef5
[ 43.881708][ T319] ? eth_header+0x165/0x1a0
[ 43.881853][ T319] ? lockdep_hardirqs_on_prepare+0xdb/0x1a0
[ 43.882031][ T319] ? srso_alias_return_thunk+0x5/0xfbef5
[ 43.882174][ T319] ? neigh_resolve_output+0x3cc/0x7e0
[ 43.882325][ T319] ? srso_alias_return_thunk+0x5/0xfbef5
[ 43.882471][ T319] ip_finish_output2+0x6b6/0x1e10
Fix this by calling qdisc_reset for CBS' child qdisc
Fixes: 585d763af09c ("net/sched: Introduce Credit Based Shaper (CBS) qdisc")
Reported-by: Junyoung Jang <graypanda.inzag@gmail.com>
Acked-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
Tested-by: Junyoung Jang <graypanda.inzag@gmail.com>
Tested-by: Victor Nogueira <victor@mojatatu.com>
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
---
net/sched/sch_cbs.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/net/sched/sch_cbs.c b/net/sched/sch_cbs.c
index 8c9a0400c862..1efd72581614 100644
--- a/net/sched/sch_cbs.c
+++ b/net/sched/sch_cbs.c
@@ -243,6 +243,16 @@ static struct sk_buff *cbs_dequeue(struct Qdisc *sch)
return q->dequeue(sch);
}
+static void cbs_reset(struct Qdisc *sch)
+{
+ struct cbs_sched_data *q = qdisc_priv(sch);
+
+ qdisc_reset(q->qdisc);
+ qdisc_watchdog_cancel(&q->watchdog);
+ q->credits = 0;
+ q->last = 0;
+}
+
static const struct nla_policy cbs_policy[TCA_CBS_MAX + 1] = {
[TCA_CBS_PARMS] = { .len = sizeof(struct tc_cbs_qopt) },
};
@@ -540,7 +550,7 @@ static struct Qdisc_ops cbs_qdisc_ops __read_mostly = {
.dequeue = cbs_dequeue,
.peek = qdisc_peek_dequeued,
.init = cbs_init,
- .reset = qdisc_reset_queue,
+ .reset = cbs_reset,
.destroy = cbs_destroy,
.change = cbs_change,
.dump = cbs_dump,
--
2.34.1
^ permalink raw reply related
* [PATCH net 2/2] selftests/tc-testing: Add QFQ/CBS qlen underflow test
From: Jamal Hadi Salim @ 2026-05-05 13:21 UTC (permalink / raw)
To: netdev
Cc: Victor Nogueira, davem, kuba, edumazet, pabeni, jiri, horms,
vinicius.gomes, graypanda.inzag, security
In-Reply-To: <20260505132102.128903-1-jhs@mojatatu.com>
From: Victor Nogueira <victor@mojatatu.com>
Since CBS was not calling reset for its child qdisc, there are scenarios
where it could cause an underflow on its parent's qlen/backlog. When the
parent is QFQ, a null-ptr deref could occur.
Add a test case that reproduces the underflow followed by a null-ptr
deref scenario.
Signed-off-by: Victor Nogueira <victor@mojatatu.com>
Acked-by: Victor Nogueira <victor@mojatatu.com>
---
.../tc-testing/tc-tests/infra/qdiscs.json | 41 +++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json
index b1f856cf62c1..848696c373fc 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json
@@ -1284,5 +1284,46 @@
"teardown": [
"$TC qdisc del dev $DUMMY handle 1: root"
]
+ },
+ {
+ "id": "3a62",
+ "name": "Try to create a qlen underflow with QFQ/CBS",
+ "category": [
+ "qdisc",
+ "qfq",
+ "cbs"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "$IP link set dev $DUMMY up || true",
+ "$IP addr add 10.10.10.10/24 dev $DUMMY || true",
+ "$TC qdisc add dev $DUMMY root handle 1: qfq",
+ "$TC class add dev $DUMMY classid 1:1 parent 1: qfq",
+ "$TC class add dev $DUMMY classid 1:2 parent 1: qfq",
+ "$TC qdisc add dev $DUMMY handle 2: parent 1:1 cbs",
+ "$TC qdisc add dev $DUMMY handle 3: parent 2: netem delay 5000000000",
+ "$TC filter add dev $DUMMY parent 1: prio 1 u32 match ip dst 10.10.10.1 classid 1:1 action ok",
+ "$TC filter add dev $DUMMY parent 1: prio 2 u32 match ip dst 10.10.10.2 classid 1:2 action ok",
+ "ping -c 1 10.10.10.1 -W0.01 -I$DUMMY || true",
+ "$IP l set $DUMMY down",
+ "$IP l set $DUMMY up",
+ "$TC qdisc replace dev $DUMMY handle 4: parent 2: pfifo"
+ ],
+ "cmdUnderTest": "ping -c 1 10.10.10.2 -W0.01 -I$DUMMY",
+ "expExitCode": "1",
+ "verifyCmd": "$TC -s -j qdisc ls dev $DUMMY parent 1:1",
+ "matchJSON": [
+ {
+ "kind": "cbs",
+ "handle": "2:",
+ "bytes": 0,
+ "packets": 0
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root"
+ ]
}
]
--
2.34.1
^ permalink raw reply related
* [PATCH net-next v5 0/5] veth: add Byte Queue Limits (BQL) support
From: hawk @ 2026-05-05 13:21 UTC (permalink / raw)
To: netdev
Cc: hawk, kernel-team, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Chris Arges, Mike Freemon,
Toke Høiland-Jørgensen, Jonas Köppeler,
Breno Leitao, Simon Schippers, Alexei Starovoitov,
Daniel Borkmann, John Fastabend, Stanislav Fomichev, bpf
From: Jesper Dangaard Brouer <hawk@kernel.org>
This series adds BQL (Byte Queue Limits) to the veth driver, reducing
latency by dynamically limiting in-flight packets in the ptr_ring and
moving buffering into the qdisc where AQM algorithms can act on it.
Problem:
veth's 256-entry ptr_ring acts as a "dark buffer" -- packets queued
there are invisible to the qdisc's AQM. Under load, the ring fills
completely (DRV_XOFF backpressure), adding up to 256 packets of
unmanaged latency before the qdisc even sees congestion.
Solution:
BQL (STACK_XOFF) dynamically limits in-flight packets, stopping the
queue before the ring fills. This keeps the ring shallow and pushes
excess packets into the qdisc, where sojourn-based AQM can measure
and drop them.
Test setup: veth pair, UDP flood, 13000 iptables rules in consumer
namespace (slows NAPI-64 cycle to ~6-7ms), ping measures RTT under load.
BQL off BQL on
fq_codel: RTT ~22ms, 4% loss RTT ~1.3ms, 0% loss
sfq: RTT ~24ms, 0% loss RTT ~1.5ms, 0% loss
BQL reduces ping RTT by ~17x for both qdiscs. Consumer throughput
is unchanged (~10K pps) -- BQL adds no overhead.
CoDel bug discovered during BQL development:
Our original motivation for BQL was fq_codel ping loss observed under
load (4-26% depending on NAPI cycle time). Investigating this led us
to discover a bug in the CoDel implementation: codel_dequeue() does
not reset vars->first_above_time when a flow goes empty, contrary to
the reference algorithm. This causes stale CoDel state to persist
across empty periods in fq_codel's per-flow queues, penalizing sparse
flows like ICMP ping. A fix for this has been applied to the net tree
815980fe6dbb ("net_sched: codel: fix stale state for empty flows in fq_codel")
BQL remains valuable independently: it reduces RTT by ~17x by moving
buffering from the dark ptr_ring into the qdisc. Additionally, BQL
clears STACK_XOFF per-SKB as each packet completes, rather than
batch-waking after 64 packets (DRV_XOFF). This keeps sojourn times
below fq_codel's target, preventing CoDel from entering dropping
state on non-congested flows in the first place.
Key design decisions:
- Charge-under-lock in veth_xdp_rx(): The BQL charge must precede
the ptr_ring produce, because the NAPI consumer can run on another
CPU and complete the SKB immediately after it becomes visible. To
avoid a pre-charge/undo pattern, the charge is done under the
ptr_ring producer_lock after confirming the ring is not full. BQL
is only charged when produce is guaranteed to succeed, keeping
num_queued monotonically increasing. HARD_TX_LOCK already
serializes dql_queued() (veth requires a qdisc for BQL); the
ptr_ring lock additionally would allow noqueue to work correctly.
- Per-SKB BQL tracking via pointer tag: A VETH_BQL_FLAG bit in the
ptr_ring pointer records whether each SKB was BQL-charged. This is
necessary because the qdisc can be replaced live (noqueue->sfq or
vice versa) while SKBs are in-flight -- the completion side must
know the charge state that was decided at enqueue time.
- IFF_NO_QUEUE + BQL coexistence: A new dev->bql flag enables BQL
sysfs exposure for IFF_NO_QUEUE devices that opt in to DQL
accounting, without changing IFF_NO_QUEUE semantics.
Background and acknowledgments:
Mike Freemon reported the veth dark buffer problem internally at
Cloudflare and showed that recompiling the kernel with a ptr_ring
size of 30 (down from 256) made fq_codel work dramatically better.
This was the primary motivation for a proper BQL solution that
achieves the same effect dynamically without a kernel rebuild.
Chris Arges wrote a reproducer for the dark buffer latency problem:
https://github.com/netoptimizer/veth-backpressure-performance-testing
This is where we first observed ping packets being dropped under
fq_codel, which became our secondary motivation for BQL. In
production we switched to SFQ on veth devices as a workaround.
Jonas Koeppeler provided extensive testing and code review.
Together we discovered that the fq_codel ping loss was actually a
12-year-old CoDel bug (stale first_above_time in empty flows), not
caused by the dark buffer itself.
Patch overview:
1. veth: fix OOB txq access in veth_poll() with asymmetric queue counts
2. net: add dev->bql flag to allow BQL sysfs for IFF_NO_QUEUE devices
3. veth: implement Byte Queue Limits (BQL) for latency reduction
4. veth: add tx_timeout watchdog as BQL safety net
5. net: sched: add timeout count to NETDEV WATCHDOG message
Jesper Dangaard Brouer (5):
veth: fix OOB txq access in veth_poll() with asymmetric queue counts
net: add dev->bql flag to allow BQL sysfs for IFF_NO_QUEUE devices
veth: implement Byte Queue Limits (BQL) for latency reduction
veth: add tx_timeout watchdog as BQL safety net
net: sched: add timeout count to NETDEV WATCHDOG message
.../networking/net_cachelines/net_device.rst | 1 +
drivers/net/veth.c | 107 ++++++++++++++++--
include/linux/netdevice.h | 2 +
net/core/net-sysfs.c | 8 +-
net/sched/sch_generic.c | 8 +-
5 files changed, 109 insertions(+), 17 deletions(-)
V4: https://lore.kernel.org/all/20260501071633.644353-1-hawk@kernel.org/
Changes since V4:
- New patch 1: fix OOB txq access in veth_poll() when veth peers have
asymmetric RX/TX queue counts. XDP redirect can deliver frames to
an RX queue index that exceeds the peer's TX queue count, causing
an out-of-bounds netdev_get_tx_queue() access. Found by sashiko-bot.
- Patch 3 (veth BQL): wake stopped peer txqs in veth_napi_del_range()
to clear DRV_XOFF after NAPI teardown. A concurrent veth_xmit()
can set DRV_XOFF between rcu_assign_pointer(napi, NULL) and
synchronize_net(); with NAPI gone, no veth_poll() clears it.
Guarded by netif_running() to skip during device close.
V3: https://lore.kernel.org/all/20260429172036.1028526-1-hawk@kernel.org/
Changes since V3:
- Drop selftest patch (patch 5 from V3) per maintainer request.
- Rebase on latest net-next.
V2: https://lore.kernel.org/all/20260413094442.1376022-1-hawk@kernel.org/
Changes since V2:
- Patch 2 (veth BQL): fix syzbot WARNING in veth_napi_del_range():
clamp BQL reset loop to peer's real_num_tx_queues. The loop was
iterating dev->real_num_rx_queues but indexing peer's txq[], which
goes out of bounds when the peer has fewer TX queues (e.g. veth
enslaved to a bond with XDP attached).
V1: https://lore.kernel.org/all/20260324174719.1224337-1-hawk@kernel.org/
Changes since V1:
- Patch 1 (dev->bql flag): add kdoc entry for @bql in struct net_device.
- Patch 2 (veth BQL): charge fixed VETH_BQL_UNIT (1) per packet instead
of skb->len. veth has no link speed; the ptr_ring is packet-indexed.
Byte-based charging lets small packets sneak many entries into the ring.
Testing: min-size packet flood causes 3.7x ping RTT degradation with
skb->len vs no change with fixed-unit charging.
- Patch 3 (tx_timeout watchdog): fix race with peer NAPI: replace
netdev_tx_reset_queue() with clear_bit(STACK_XOFF) + netif_tx_wake_queue()
to avoid dql_reset() racing with concurrent dql_completed().
- Cover letter: update CoDel fix reference to merged commit in net tree.
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Paolo Abeni <pabeni@redhat.com>
Cc: Simon Horman <horms@kernel.org>
Cc: Chris Arges <carges@cloudflare.com>
Cc: Mike Freemon <mfreemon@cloudflare.com>
Cc: Toke Høiland-Jørgensen <toke@toke.dk>
Cc: Jonas Köppeler <j.koeppeler@tu-berlin.de>
Cc: Breno Leitao <leitao@debian.org>
Cc: Simon Schippers <simon.schippers@tu-dortmund.de>
Cc: kernel-team@cloudflare.com
--
2.43.0
^ permalink raw reply
* [PATCH net-next v5 1/5] veth: fix OOB txq access in veth_poll() with asymmetric queue counts
From: hawk @ 2026-05-05 13:21 UTC (permalink / raw)
To: netdev
Cc: hawk, kernel-team, Sashiko, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Alexei Starovoitov,
Daniel Borkmann, John Fastabend, Stanislav Fomichev,
Toshiaki Makita, linux-kernel, bpf
In-Reply-To: <20260505132159.241305-1-hawk@kernel.org>
From: Jesper Dangaard Brouer <hawk@kernel.org>
XDP redirect into a veth device (via bpf_redirect()) calls
veth_xdp_xmit(), which enqueues frames into the peer's ptr_ring using
smp_processor_id() % peer->real_num_rx_queues
as the ring index. With an asymmetric veth pair where the peer has
fewer TX queues than RX queues, that index can exceed
peer->real_num_tx_queues.
veth_poll() then resolves peer_txq for the ring via:
peer_txq = peer_dev ? netdev_get_tx_queue(peer_dev, queue_idx) : NULL;
where queue_idx = rq->xdp_rxq.queue_index. When queue_idx exceeds
peer_dev->real_num_tx_queues this is an out-of-bounds (OOB) access
into the peer's netdev_queue array, triggering DEBUG_NET_WARN_ON_ONCE
in netdev_get_tx_queue().
The normal ndo_start_xmit path is not affected: the stack clamps
skb->queue_mapping via netdev_cap_txqueue() before invoking
ndo_start_xmit, so rxq in veth_xmit() never exceeds real_num_tx_queues.
Fix veth_poll() by clamping: only dereference peer_txq when queue_idx is
within bounds, otherwise set it to NULL. The out-of-range rings are fed
exclusively via XDP redirect (veth_xdp_xmit), never via ndo_start_xmit
(veth_xmit), so the peer txq was never stopped and there is nothing to
wake; NULL is the correct fallback.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/20260502071828.616C3C19425@smtp.kernel.org/
Fixes: dc82a33297fc ("veth: apply qdisc backpressure on full ptr_ring to reduce TX drops")
Signed-off-by: Jesper Dangaard Brouer <hawk@kernel.org>
---
drivers/net/veth.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index e35df717e65e..0cfb19b760dd 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -972,7 +972,8 @@ static int veth_poll(struct napi_struct *napi, int budget)
/* NAPI functions as RCU section */
peer_dev = rcu_dereference_check(priv->peer, rcu_read_lock_bh_held());
- peer_txq = peer_dev ? netdev_get_tx_queue(peer_dev, queue_idx) : NULL;
+ peer_txq = (peer_dev && queue_idx < peer_dev->real_num_tx_queues) ?
+ netdev_get_tx_queue(peer_dev, queue_idx) : NULL;
xdp_set_return_frame_no_direct();
done = veth_xdp_rcv(rq, budget, &bq, &stats);
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v5 2/5] net: add dev->bql flag to allow BQL sysfs for IFF_NO_QUEUE devices
From: hawk @ 2026-05-05 13:21 UTC (permalink / raw)
To: netdev
Cc: hawk, kernel-team, Jonas Köppeler, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Jonathan Corbet, Shuah Khan, Kuniyuki Iwashima,
Stanislav Fomichev, Christian Brauner, Yury Norov, Krishna Kumar,
Yajun Deng, linux-doc, linux-kernel
In-Reply-To: <20260505132159.241305-1-hawk@kernel.org>
From: Jesper Dangaard Brouer <hawk@kernel.org>
Virtual devices with IFF_NO_QUEUE or lltx are excluded from BQL sysfs
by netdev_uses_bql(), since they traditionally lack real hardware
queues. However, some virtual devices like veth implement a real
ptr_ring FIFO with NAPI processing and benefit from BQL to limit
in-flight bytes and reduce latency.
Add a per-device 'bql' bitfield boolean in the priv_flags_slow section
of struct net_device. When set, it overrides the IFF_NO_QUEUE/lltx
exclusion and exposes BQL sysfs entries (/sys/class/net/<dev>/queues/
tx-<n>/byte_queue_limits/). The flag is still gated on CONFIG_BQL.
This allows drivers that use BQL despite being IFF_NO_QUEUE to opt in
to sysfs visibility for monitoring and debugging.
Signed-off-by: Jesper Dangaard Brouer <hawk@kernel.org>
Tested-by: Jonas Köppeler <j.koeppeler@tu-berlin.de>
---
Documentation/networking/net_cachelines/net_device.rst | 1 +
include/linux/netdevice.h | 2 ++
net/core/net-sysfs.c | 8 +++++++-
3 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/Documentation/networking/net_cachelines/net_device.rst b/Documentation/networking/net_cachelines/net_device.rst
index 1c19bb7705df..b775d3235a2d 100644
--- a/Documentation/networking/net_cachelines/net_device.rst
+++ b/Documentation/networking/net_cachelines/net_device.rst
@@ -170,6 +170,7 @@ unsigned_long:1 see_all_hwtstamp_requests
unsigned_long:1 change_proto_down
unsigned_long:1 netns_immutable
unsigned_long:1 fcoe_mtu
+unsigned_long:1 bql netdev_uses_bql(net-sysfs.c)
struct list_head net_notifier_list
struct macsec_ops* macsec_ops
struct udp_tunnel_nic_info* udp_tunnel_nic_info
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 744ffa243501..d4c7b020b6a7 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2065,6 +2065,7 @@ enum netdev_reg_state {
* @change_proto_down: device supports setting carrier via IFLA_PROTO_DOWN
* @netns_immutable: interface can't change network namespaces
* @fcoe_mtu: device supports maximum FCoE MTU, 2158 bytes
+ * @bql: device uses BQL (DQL sysfs) despite having IFF_NO_QUEUE
*
* @net_notifier_list: List of per-net netdev notifier block
* that follow this device when it is moved
@@ -2479,6 +2480,7 @@ struct net_device {
unsigned long change_proto_down:1;
unsigned long netns_immutable:1;
unsigned long fcoe_mtu:1;
+ unsigned long bql:1;
struct list_head net_notifier_list;
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 3318b5666e43..82833e5dae03 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -1945,10 +1945,16 @@ static const struct kobj_type netdev_queue_ktype = {
static bool netdev_uses_bql(const struct net_device *dev)
{
+ if (!IS_ENABLED(CONFIG_BQL))
+ return false;
+
+ if (dev->bql)
+ return true;
+
if (dev->lltx || (dev->priv_flags & IFF_NO_QUEUE))
return false;
- return IS_ENABLED(CONFIG_BQL);
+ return true;
}
static int netdev_queue_add_kobject(struct net_device *dev, int index)
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v5 3/5] veth: implement Byte Queue Limits (BQL) for latency reduction
From: hawk @ 2026-05-05 13:21 UTC (permalink / raw)
To: netdev
Cc: hawk, kernel-team, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Alexei Starovoitov, Daniel Borkmann,
John Fastabend, Stanislav Fomichev, linux-kernel, bpf
In-Reply-To: <20260505132159.241305-1-hawk@kernel.org>
From: Jesper Dangaard Brouer <hawk@kernel.org>
Commit dc82a33297fc ("veth: apply qdisc backpressure on full ptr_ring to
reduce TX drops") gave qdiscs control over veth by returning
NETDEV_TX_BUSY when the ptr_ring is full (DRV_XOFF). That commit noted
a known limitation: the 256-entry ptr_ring sits in front of the qdisc as
a dark buffer, adding base latency because the qdisc has no visibility
into how many bytes are already queued there.
Add BQL support so the qdisc gets feedback and can begin shaping traffic
before the ring fills. In testing with fq_codel, BQL reduces ping RTT
under UDP load from ~6.61ms to ~0.36ms (18x).
Charge a fixed VETH_BQL_UNIT (1) per packet rather than skb->len, so
the DQL limit tracks packets-in-flight. Unlike a physical NIC, veth
has no link speed -- the ptr_ring drains at CPU speed and is
packet-indexed, not byte-indexed, so bytes are not the natural unit.
With byte-based charging, small packets sneak many more entries into
the ring before STACK_XOFF fires, deepening the dark buffer under
mixed-size workloads. Testing with a concurrent min-size packet flood
shows 3.7x ping RTT degradation with skb->len charging versus no
change with fixed-unit charging.
Charge BQL inside veth_xdp_rx() under the ptr_ring producer_lock, after
confirming the ring is not full. The charge must precede the produce
because the NAPI consumer can run on another CPU and complete the SKB
the instant it becomes visible in the ring. Doing both under the same
lock avoids a pre-charge/undo pattern -- BQL is only charged when
produce is guaranteed to succeed.
BQL is enabled only when a real qdisc is attached (guarded by
!qdisc_txq_has_no_queue), as HARD_TX_LOCK provides serialization
for TXQ modification like dql_queued(). For lltx devices, like veth,
this HARD_TX_LOCK serialization isn't provided. The ptr_ring
producer_lock provides additional serialization that would allow
BQL to work correctly even with noqueue, though that combination
is not currently enabled, as the netstack will drop and warn.
Track per-SKB BQL state via a VETH_BQL_FLAG pointer tag in the ptr_ring
entry. This is necessary because the qdisc can be replaced live while
SKBs are in-flight -- each SKB must carry the charge decision made at
enqueue time rather than re-checking the peer's qdisc at completion.
Complete per-SKB in veth_xdp_rcv() rather than in bulk, so STACK_XOFF
clears promptly when producer and consumer run on different CPUs.
BQL introduces a second independent queue-stop mechanism (STACK_XOFF)
alongside the existing DRV_XOFF (ring full). Both must be clear for
the queue to transmit. Reset BQL state in veth_napi_del_range() after
synchronize_net() to avoid racing with in-flight veth_poll() calls.
Clamp the reset loop to the peer's real_num_tx_queues, since the peer
may have fewer TX queues than the local device has RX queues (e.g. when
veth is enslaved to a bond with XDP attached).
Signed-off-by: Jesper Dangaard Brouer <hawk@kernel.org>
---
drivers/net/veth.c | 86 ++++++++++++++++++++++++++++++++++++++++------
1 file changed, 75 insertions(+), 11 deletions(-)
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 0cfb19b760dd..86b78900c48e 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -34,9 +34,13 @@
#define DRV_VERSION "1.0"
#define VETH_XDP_FLAG BIT(0)
+#define VETH_BQL_FLAG BIT(1)
#define VETH_RING_SIZE 256
#define VETH_XDP_HEADROOM (XDP_PACKET_HEADROOM + NET_IP_ALIGN)
+/* Fixed BQL charge: DQL limit tracks packets-in-flight, not bytes */
+#define VETH_BQL_UNIT 1
+
#define VETH_XDP_TX_BULK_SIZE 16
#define VETH_XDP_BATCH 16
@@ -280,6 +284,21 @@ static bool veth_is_xdp_frame(void *ptr)
return (unsigned long)ptr & VETH_XDP_FLAG;
}
+static bool veth_ptr_is_bql(void *ptr)
+{
+ return (unsigned long)ptr & VETH_BQL_FLAG;
+}
+
+static struct sk_buff *veth_ptr_to_skb(void *ptr)
+{
+ return (void *)((unsigned long)ptr & ~VETH_BQL_FLAG);
+}
+
+static void *veth_skb_to_ptr(struct sk_buff *skb, bool bql)
+{
+ return bql ? (void *)((unsigned long)skb | VETH_BQL_FLAG) : skb;
+}
+
static struct xdp_frame *veth_ptr_to_xdp(void *ptr)
{
return (void *)((unsigned long)ptr & ~VETH_XDP_FLAG);
@@ -295,7 +314,7 @@ static void veth_ptr_free(void *ptr)
if (veth_is_xdp_frame(ptr))
xdp_return_frame(veth_ptr_to_xdp(ptr));
else
- kfree_skb(ptr);
+ kfree_skb(veth_ptr_to_skb(ptr));
}
static void __veth_xdp_flush(struct veth_rq *rq)
@@ -309,19 +328,33 @@ static void __veth_xdp_flush(struct veth_rq *rq)
}
}
-static int veth_xdp_rx(struct veth_rq *rq, struct sk_buff *skb)
+static int veth_xdp_rx(struct veth_rq *rq, struct sk_buff *skb, bool do_bql,
+ struct netdev_queue *txq)
{
- if (unlikely(ptr_ring_produce(&rq->xdp_ring, skb)))
+ struct ptr_ring *ring = &rq->xdp_ring;
+
+ spin_lock(&ring->producer_lock);
+ if (unlikely(!ring->size) || __ptr_ring_full(ring)) {
+ spin_unlock(&ring->producer_lock);
return NETDEV_TX_BUSY; /* signal qdisc layer */
+ }
+
+ /* BQL charge before produce; consumer cannot see entry yet */
+ if (do_bql)
+ netdev_tx_sent_queue(txq, VETH_BQL_UNIT);
+
+ __ptr_ring_produce(ring, veth_skb_to_ptr(skb, do_bql));
+ spin_unlock(&ring->producer_lock);
return NET_RX_SUCCESS; /* same as NETDEV_TX_OK */
}
static int veth_forward_skb(struct net_device *dev, struct sk_buff *skb,
- struct veth_rq *rq, bool xdp)
+ struct veth_rq *rq, bool xdp, bool do_bql,
+ struct netdev_queue *txq)
{
return __dev_forward_skb(dev, skb) ?: xdp ?
- veth_xdp_rx(rq, skb) :
+ veth_xdp_rx(rq, skb, do_bql, txq) :
__netif_rx(skb);
}
@@ -348,10 +381,11 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct veth_priv *rcv_priv, *priv = netdev_priv(dev);
struct veth_rq *rq = NULL;
- struct netdev_queue *txq;
+ struct netdev_queue *txq = NULL;
struct net_device *rcv;
int length = skb->len;
bool use_napi = false;
+ bool do_bql = false;
int ret, rxq;
rcu_read_lock();
@@ -375,8 +409,12 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
}
skb_tx_timestamp(skb);
-
- ret = veth_forward_skb(rcv, skb, rq, use_napi);
+ if (rxq < dev->real_num_tx_queues) {
+ txq = netdev_get_tx_queue(dev, rxq);
+ /* BQL charge happens inside veth_xdp_rx() under producer_lock */
+ do_bql = use_napi && !qdisc_txq_has_no_queue(txq);
+ }
+ ret = veth_forward_skb(rcv, skb, rq, use_napi, do_bql, txq);
switch (ret) {
case NET_RX_SUCCESS: /* same as NETDEV_TX_OK */
if (!use_napi)
@@ -412,6 +450,7 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
net_crit_ratelimited("%s(%s): Invalid return code(%d)",
__func__, dev->name, ret);
}
+
rcu_read_unlock();
return ret;
@@ -900,7 +939,8 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq,
static int veth_xdp_rcv(struct veth_rq *rq, int budget,
struct veth_xdp_tx_bq *bq,
- struct veth_stats *stats)
+ struct veth_stats *stats,
+ struct netdev_queue *peer_txq)
{
int i, done = 0, n_xdpf = 0;
void *xdpf[VETH_XDP_BATCH];
@@ -928,9 +968,13 @@ static int veth_xdp_rcv(struct veth_rq *rq, int budget,
}
} else {
/* ndo_start_xmit */
- struct sk_buff *skb = ptr;
+ bool bql_charged = veth_ptr_is_bql(ptr);
+ struct sk_buff *skb = veth_ptr_to_skb(ptr);
stats->xdp_bytes += skb->len;
+ if (peer_txq && bql_charged)
+ netdev_tx_completed_queue(peer_txq, 1, VETH_BQL_UNIT);
+
skb = veth_xdp_rcv_skb(rq, skb, bq, stats);
if (skb) {
if (skb_shared(skb) || skb_unclone(skb, GFP_ATOMIC))
@@ -976,7 +1020,7 @@ static int veth_poll(struct napi_struct *napi, int budget)
netdev_get_tx_queue(peer_dev, queue_idx) : NULL;
xdp_set_return_frame_no_direct();
- done = veth_xdp_rcv(rq, budget, &bq, &stats);
+ done = veth_xdp_rcv(rq, budget, &bq, &stats, peer_txq);
if (stats.xdp_redirect > 0)
xdp_do_flush();
@@ -1074,6 +1118,7 @@ static int __veth_napi_enable(struct net_device *dev)
static void veth_napi_del_range(struct net_device *dev, int start, int end)
{
struct veth_priv *priv = netdev_priv(dev);
+ struct net_device *peer;
int i;
for (i = start; i < end; i++) {
@@ -1092,6 +1137,24 @@ static void veth_napi_del_range(struct net_device *dev, int start, int end)
ptr_ring_cleanup(&rq->xdp_ring, veth_ptr_free);
}
+ /* Reset BQL and wake stopped peer txqs. A concurrent veth_xmit()
+ * may have set DRV_XOFF between rcu_assign_pointer(napi, NULL) and
+ * synchronize_net(), and NAPI can no longer clear it.
+ * Only wake when the device is still up.
+ */
+ peer = rtnl_dereference(priv->peer);
+ if (peer) {
+ int peer_end = min_t(int, end, peer->real_num_tx_queues);
+
+ for (i = start; i < peer_end; i++) {
+ struct netdev_queue *txq = netdev_get_tx_queue(peer, i);
+
+ netdev_tx_reset_queue(txq);
+ if (netif_running(dev))
+ netif_tx_wake_queue(txq);
+ }
+ }
+
for (i = start; i < end; i++) {
page_pool_destroy(priv->rq[i].page_pool);
priv->rq[i].page_pool = NULL;
@@ -1741,6 +1804,7 @@ static void veth_setup(struct net_device *dev)
dev->priv_flags |= IFF_PHONY_HEADROOM;
dev->priv_flags |= IFF_DISABLE_NETPOLL;
dev->lltx = true;
+ dev->bql = true;
dev->netdev_ops = &veth_netdev_ops;
dev->xdp_metadata_ops = &veth_xdp_metadata_ops;
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v5 4/5] veth: add tx_timeout watchdog as BQL safety net
From: hawk @ 2026-05-05 13:21 UTC (permalink / raw)
To: netdev
Cc: hawk, kernel-team, Jonas Köppeler, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-kernel
In-Reply-To: <20260505132159.241305-1-hawk@kernel.org>
From: Jesper Dangaard Brouer <hawk@kernel.org>
With the introduction of BQL (Byte Queue Limits) for veth, there are
now two independent mechanisms that can stop a transmit queue:
- DRV_XOFF: set by netif_tx_stop_queue() when the ptr_ring is full
- STACK_XOFF: set by BQL when the byte-in-flight limit is reached
If either mechanism stalls without a corresponding wake/completion,
the queue stops permanently. Enable the net device watchdog timer and
implement ndo_tx_timeout as a failsafe recovery.
The timeout handler resets BQL state (clearing STACK_XOFF) and wakes
the queue (clearing DRV_XOFF), covering both stop mechanisms. The
watchdog fires after 16 seconds, which accommodates worst-case NAPI
processing (budget=64 packets x 250ms per-packet consumer delay)
without false positives under normal backpressure.
Signed-off-by: Jesper Dangaard Brouer <hawk@kernel.org>
Tested-by: Jonas Köppeler <j.koeppeler@tu-berlin.de>
---
drivers/net/veth.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 86b78900c48e..4103d298aa9b 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -1442,6 +1442,22 @@ static int veth_set_channels(struct net_device *dev,
goto out;
}
+static void veth_tx_timeout(struct net_device *dev, unsigned int txqueue)
+{
+ struct netdev_queue *txq = netdev_get_tx_queue(dev, txqueue);
+
+ netdev_err(dev,
+ "veth backpressure(0x%lX) stalled(n:%ld) TXQ(%u) re-enable\n",
+ txq->state, atomic_long_read(&txq->trans_timeout), txqueue);
+
+ /* Cannot call netdev_tx_reset_queue(): dql_reset() races with
+ * peer NAPI calling dql_completed() concurrently.
+ * Just clear the stop bits; the qdisc will re-stop if still stuck.
+ */
+ clear_bit(__QUEUE_STATE_STACK_XOFF, &txq->state);
+ netif_tx_wake_queue(txq);
+}
+
static int veth_open(struct net_device *dev)
{
struct veth_priv *priv = netdev_priv(dev);
@@ -1780,6 +1796,7 @@ static const struct net_device_ops veth_netdev_ops = {
.ndo_bpf = veth_xdp,
.ndo_xdp_xmit = veth_ndo_xdp_xmit,
.ndo_get_peer_dev = veth_peer_dev,
+ .ndo_tx_timeout = veth_tx_timeout,
};
static const struct xdp_metadata_ops veth_xdp_metadata_ops = {
@@ -1819,6 +1836,7 @@ static void veth_setup(struct net_device *dev)
dev->priv_destructor = veth_dev_free;
dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
dev->max_mtu = ETH_MAX_MTU;
+ dev->watchdog_timeo = msecs_to_jiffies(16000);
dev->hw_features = VETH_FEATURES;
dev->hw_enc_features = VETH_FEATURES;
--
2.43.0
^ permalink raw reply related
* [PATCH net-next v5 5/5] net: sched: add timeout count to NETDEV WATCHDOG message
From: hawk @ 2026-05-05 13:21 UTC (permalink / raw)
To: netdev
Cc: hawk, kernel-team, Jakub Kicinski, Jonas Köppeler,
Jamal Hadi Salim, Jiri Pirko, David S. Miller, Eric Dumazet,
Paolo Abeni, Simon Horman, linux-kernel
In-Reply-To: <20260505132159.241305-1-hawk@kernel.org>
From: Jesper Dangaard Brouer <hawk@kernel.org>
Add the per-queue timeout counter (trans_timeout) to the core NETDEV
WATCHDOG log message. This makes it easy to determine how frequently
a particular queue is stalling from a single log line, without having
to search through and correlate spaced-out log entries.
Useful for production monitoring where timeouts are spaced by the
watchdog interval, making frequency hard to judge.
Suggested-by: Jakub Kicinski <kuba@kernel.org>
Link: https://lore.kernel.org/all/20251107175445.58eba452@kernel.org/
Signed-off-by: Jesper Dangaard Brouer <hawk@kernel.org>
Tested-by: Jonas Köppeler <j.koeppeler@tu-berlin.de>
---
net/sched/sch_generic.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index a93321db8fd7..3e2e2e887a86 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -533,13 +533,12 @@ static void dev_watchdog(struct timer_list *t)
netif_running(dev) &&
netif_carrier_ok(dev)) {
unsigned int timedout_ms = 0;
+ struct netdev_queue *txq;
unsigned int i;
unsigned long trans_start;
unsigned long oldest_start = jiffies;
for (i = 0; i < dev->num_tx_queues; i++) {
- struct netdev_queue *txq;
-
txq = netdev_get_tx_queue(dev, i);
if (!netif_xmit_stopped(txq))
continue;
@@ -561,9 +560,10 @@ static void dev_watchdog(struct timer_list *t)
if (unlikely(timedout_ms)) {
trace_net_dev_xmit_timeout(dev, i);
- netdev_crit(dev, "NETDEV WATCHDOG: CPU: %d: transmit queue %u timed out %u ms\n",
+ netdev_crit(dev, "NETDEV WATCHDOG: CPU: %d: transmit queue %u timed out %u ms (n:%ld)\n",
raw_smp_processor_id(),
- i, timedout_ms);
+ i, timedout_ms,
+ atomic_long_read(&txq->trans_timeout));
netif_freeze_queues(dev);
dev->netdev_ops->ndo_tx_timeout(dev, i);
netif_unfreeze_queues(dev);
--
2.43.0
^ permalink raw reply related
* [PATCH 0/8] pull request (net): ipsec 2026-05-05
From: Steffen Klassert @ 2026-05-05 13:22 UTC (permalink / raw)
To: David Miller, Jakub Kicinski; +Cc: Herbert Xu, Steffen Klassert, netdev
1. Fix an IPv6 encapsulation error path that leaked route references
when UDPv6 ESP decapsulation resolved to an error route.
From Yilin Zhu.
2. Fix AH with ESN on async crypto paths by accounting for the extra
high-order sequence number when reconstructing the temporary
authentication layout in the completion callbacks.
From Michael Bomarito.
3. Fix XFRM output so it does not overwrite already-correct inner header
pointers when a tunnel layer such as VXLAN has already saved them.
The fix comes with new selftests. From Cosmin Ratiu.
4. Add the missing native payload size entry for XFRM_MSG_MAPPING in the
compat translation path. From Ruijie Li.
5. Harden __xfrm_state_delete() against repeated or inconsistent unhashing
of state list nodes by keying the removal on actual list membership and
using delete-and-init helpers. From Michal Kosiorek.
6. Prevent ESP from decrypting shared splice-backed skb fragments in place
by marking UDP splice frags as shared and forcing copy-on-write in ESP
input when needed. From Kuan-Ting Chen.
Please pull or let me know if there are problems.
Thanks!
The following changes since commit 1f5ffc672165ff851063a5fd044b727ab2517ae3:
Fix mismerge of the arm64 / timer-core interrupt handling changes (2026-04-14 23:03:02 -0700)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec.git tags/ipsec-2026-05-05
for you to fetch changes up to f4c50a4034e62ab75f1d5cdd191dd5f9c77fdff4:
xfrm: esp: avoid in-place decrypt on shared skb frags (2026-05-05 06:38:30 +0200)
----------------------------------------------------------------
ipsec-2026-05-05
----------------------------------------------------------------
Cosmin Ratiu (3):
tools/selftests: Use a sensible timeout value for iperf3 client
tools/selftests: Add a VXLAN+IPsec traffic test
xfrm: Don't clobber inner headers when already set
Kuan-Ting Chen (1):
xfrm: esp: avoid in-place decrypt on shared skb frags
Michael Bommarito (1):
xfrm: ah: account for ESN high bits in async callbacks
Michal Kosiorek (1):
xfrm: defensively unhash xfrm_state lists in __xfrm_state_delete
Ruijie Li (1):
xfrm: provide message size for XFRM_MSG_MAPPING
Yilin Zhu (1):
ipv6: xfrm6: release dst on error in xfrm6_rcv_encap()
net/ipv4/ah4.c | 14 +-
net/ipv4/esp4.c | 3 +-
net/ipv4/ip_output.c | 2 +
net/ipv6/ah6.c | 14 +-
net/ipv6/esp6.c | 3 +-
net/ipv6/ip6_output.c | 2 +
net/ipv6/xfrm6_protocol.c | 4 +-
net/xfrm/xfrm_output.c | 20 +-
net/xfrm/xfrm_state.c | 12 +-
net/xfrm/xfrm_user.c | 1 +
tools/testing/selftests/drivers/net/hw/Makefile | 1 +
tools/testing/selftests/drivers/net/hw/config | 5 +
.../selftests/drivers/net/hw/ipsec_vxlan.py | 204 +++++++++++++++++++++
tools/testing/selftests/drivers/net/lib/py/load.py | 5 +-
14 files changed, 270 insertions(+), 20 deletions(-)
create mode 100755 tools/testing/selftests/drivers/net/hw/ipsec_vxlan.py
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox