* [PATCH bpf v3 1/2] bpf: tcp: Reject non-TCP skb in bpf_sk_assign_tcp_reqsk()
From: Jiayuan Chen @ 2026-03-27 13:38 UTC (permalink / raw)
To: bpf
Cc: Jiayuan Chen, Jiayuan Chen, Kuniyuki Iwashima, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau,
Eduard Zingerman, Song Liu, Yonghong Song, John Fastabend,
KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Shuah Khan, netdev, linux-kernel, linux-kselftest
In-Reply-To: <20260327133915.286037-1-jiayuan.chen@linux.dev>
From: Jiayuan Chen <jiayuan.chen@shopee.com>
bpf_sk_assign_tcp_reqsk() only validates skb->protocol (L3) but does not
check the L4 protocol in the IP header. A BPF program can call this kfunc
on a UDP skb with a valid TCP listener socket, which will succeed and
attach a TCP reqsk to the UDP skb.
When the UDP skb enters the UDP receive path, skb_steal_sock() returns
the TCP listener from the reqsk. The UDP code then passes this TCP socket
to udp_unicast_rcv_skb() -> __udp_enqueue_schedule_skb(), which casts
it to udp_sock and accesses UDP-specific fields at invalid offsets,
causing a null pointer dereference and kernel panic:
BUG: KASAN: null-ptr-deref in __udp_enqueue_schedule_skb+0x19d/0x1df0
Read of size 4 at addr 0000000000000008 by task test_progs/537
CPU: 1 UID: 0 PID: 537 Comm: test_progs Not tainted 7.0.0-rc4+ #46 PREEMPT
Call Trace:
<IRQ>
dump_stack_lvl (lib/dump_stack.c:123)
print_report (mm/kasan/report.c:487)
kasan_report (mm/kasan/report.c:597)
__kasan_check_read (mm/kasan/shadow.c:32)
__udp_enqueue_schedule_skb (net/ipv4/udp.c:1719)
udp_queue_rcv_one_skb (net/ipv4/udp.c:2370 net/ipv4/udp.c:2500)
udp_queue_rcv_skb (net/ipv4/udp.c:2532)
udp_unicast_rcv_skb (net/ipv4/udp.c:2684)
__udp4_lib_rcv (net/ipv4/udp.c:2742)
udp_rcv (net/ipv4/udp.c:2937)
ip_protocol_deliver_rcu (net/ipv4/ip_input.c:209)
ip_local_deliver_finish (./include/linux/rcupdate.h:879 net/ipv4/ip_input.c:242)
ip_local_deliver (net/ipv4/ip_input.c:265)
__netif_receive_skb_one_core (net/core/dev.c:6164 (discriminator 4))
__netif_receive_skb (net/core/dev.c:6280)
Fix this by checking the IP header's protocol field in
bpf_sk_assign_tcp_reqsk() and rejecting non-TCP skbs with -EINVAL.
Fixes: e472f88891ab ("bpf: tcp: Support arbitrary SYN Cookie.")
Cc: Jiayuan Chen <jiayuan.chen@linux.dev>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
---
net/core/filter.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/net/core/filter.c b/net/core/filter.c
index 78b548158fb0..fb975bcce804 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -12248,11 +12248,17 @@ __bpf_kfunc int bpf_sk_assign_tcp_reqsk(struct __sk_buff *s, struct sock *sk,
switch (skb->protocol) {
case htons(ETH_P_IP):
+ if (ip_hdr(skb)->protocol != IPPROTO_TCP)
+ return -EINVAL;
+
ops = &tcp_request_sock_ops;
min_mss = 536;
break;
#if IS_BUILTIN(CONFIG_IPV6)
case htons(ETH_P_IPV6):
+ if (ipv6_hdr(skb)->nexthdr != IPPROTO_TCP)
+ return -EINVAL;
+
ops = &tcp6_request_sock_ops;
min_mss = IPV6_MIN_MTU - 60;
break;
--
2.43.0
^ permalink raw reply related
* [PATCH bpf v3 2/2] selftests/bpf: Add protocol check test for bpf_sk_assign_tcp_reqsk()
From: Jiayuan Chen @ 2026-03-27 13:38 UTC (permalink / raw)
To: bpf
Cc: Jiayuan Chen, Jiayuan Chen, Martin KaFai Lau, Daniel Borkmann,
John Fastabend, Stanislav Fomichev, Alexei Starovoitov,
Andrii Nakryiko, Eduard Zingerman, Song Liu, Yonghong Song,
KP Singh, Hao Luo, Jiri Olsa, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Shuah Khan,
Kuniyuki Iwashima, netdev, linux-kernel, linux-kselftest
In-Reply-To: <20260327133915.286037-1-jiayuan.chen@linux.dev>
From: Jiayuan Chen <jiayuan.chen@shopee.com>
Add test_tcp_custom_syncookie_protocol_check to verify that
bpf_sk_assign_tcp_reqsk() rejects non-TCP skbs. The test sends a UDP
packet through TC ingress where a BPF program calls
bpf_sk_assign_tcp_reqsk() on it and checks that the kfunc returns an
error. A UDP server recv() is used as synchronization to ensure the
BPF program has finished processing before checking the result.
Without the fix in bpf_sk_assign_tcp_reqsk(), the kfunc succeeds and
attaches a TCP reqsk to the UDP skb, which causes a null pointer
dereference panic when the kernel processes it through the UDP receive
path.
Test result:
./test_progs -a tcp_custom_syncookie_protocol_check -v
setup_netns:PASS:create netns 0 nsec
setup_netns:PASS:ip 0 nsec
write_sysctl:PASS:open sysctl 0 nsec
write_sysctl:PASS:write sysctl 0 nsec
setup_netns:PASS:write_sysctl 0 nsec
test_tcp_custom_syncookie_protocol_check:PASS:open_and_load 0 nsec
setup_tc:PASS:qdisc add dev lo clsact 0 nsec
setup_tc:PASS:filter add dev lo ingress 0 nsec
run_protocol_check:PASS:start tcp_server 0 nsec
run_protocol_check:PASS:start udp_server 0 nsec
run_protocol_check:PASS:connect udp_client 0 nsec
run_protocol_check:PASS:send udp 0 nsec
run_protocol_check:PASS:recv udp 0 nsec
run_protocol_check:PASS:udp_intercepted 0 nsec
run_protocol_check:PASS:assign_ret 0 nsec
#471/1 tcp_custom_syncookie_protocol_check/IPv4 TCP:OK
run_protocol_check:PASS:start tcp_server 0 nsec
run_protocol_check:PASS:start udp_server 0 nsec
run_protocol_check:PASS:connect udp_client 0 nsec
run_protocol_check:PASS:send udp 0 nsec
run_protocol_check:PASS:recv udp 0 nsec
run_protocol_check:PASS:udp_intercepted 0 nsec
run_protocol_check:PASS:assign_ret 0 nsec
#471/2 tcp_custom_syncookie_protocol_check/IPv6 TCP:OK
#471 tcp_custom_syncookie_protocol_check:OK
Summary: 1/2 PASSED, 0 SKIPPED, 0 FAILED
Cc: Jiayuan Chen <jiayuan.chen@linux.dev>
Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
---
.../bpf/prog_tests/tcp_custom_syncookie.c | 94 ++++++++++++++-
.../bpf/progs/test_tcp_custom_syncookie.c | 109 ++++++++++++++++++
2 files changed, 199 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/tcp_custom_syncookie.c b/tools/testing/selftests/bpf/prog_tests/tcp_custom_syncookie.c
index eaf441dc7e79..e46031d0786b 100644
--- a/tools/testing/selftests/bpf/prog_tests/tcp_custom_syncookie.c
+++ b/tools/testing/selftests/bpf/prog_tests/tcp_custom_syncookie.c
@@ -5,6 +5,7 @@
#include <sched.h>
#include <stdlib.h>
#include <net/if.h>
+#include <netinet/in.h>
#include "test_progs.h"
#include "cgroup_helpers.h"
@@ -47,11 +48,10 @@ static int setup_netns(void)
return -1;
}
-static int setup_tc(struct test_tcp_custom_syncookie *skel)
+static int setup_tc(int prog_fd)
{
LIBBPF_OPTS(bpf_tc_hook, qdisc_lo, .attach_point = BPF_TC_INGRESS);
- LIBBPF_OPTS(bpf_tc_opts, tc_attach,
- .prog_fd = bpf_program__fd(skel->progs.tcp_custom_syncookie));
+ LIBBPF_OPTS(bpf_tc_opts, tc_attach, .prog_fd = prog_fd);
qdisc_lo.ifindex = if_nametoindex("lo");
if (!ASSERT_OK(bpf_tc_hook_create(&qdisc_lo), "qdisc add dev lo clsact"))
@@ -127,7 +127,7 @@ void test_tcp_custom_syncookie(void)
if (!ASSERT_OK_PTR(skel, "open_and_load"))
return;
- if (setup_tc(skel))
+ if (setup_tc(bpf_program__fd(skel->progs.tcp_custom_syncookie)))
goto destroy_skel;
for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
@@ -145,6 +145,92 @@ void test_tcp_custom_syncookie(void)
destroy_skel:
system("tc qdisc del dev lo clsact");
+ test_tcp_custom_syncookie__destroy(skel);
+}
+
+/* Test: bpf_sk_assign_tcp_reqsk() should reject non-TCP skb.
+ *
+ * Send a UDP packet through TC ingress where a BPF program calls
+ * bpf_sk_assign_tcp_reqsk() on it. The kfunc should return an error
+ * because the skb carries UDP, not TCP.
+ *
+ * TCP and UDP servers share the same port. The BPF program intercepts
+ * the UDP packet, looks up the TCP listener via the dest port, and
+ * attempts to assign a TCP reqsk to the UDP skb.
+ */
+static void run_protocol_check(struct test_tcp_custom_syncookie *skel,
+ int family, const char *addr)
+{
+ int tcp_server = -1, udp_server = -1, udp_client = -1;
+ char buf[32] = "test";
+ int port, ret;
+
+ tcp_server = start_server(family, SOCK_STREAM, addr, 0, 0);
+ if (!ASSERT_NEQ(tcp_server, -1, "start tcp_server"))
+ return;
+
+ port = ntohs(get_socket_local_port(tcp_server));
+
+ /* UDP server on same port for synchronization and port sharing */
+ udp_server = start_server(family, SOCK_DGRAM, addr, port, 0);
+ if (!ASSERT_NEQ(udp_server, -1, "start udp_server"))
+ goto close_tcp;
+
+ skel->bss->udp_intercepted = false;
+ skel->data->assign_ret = -1;
+
+ udp_client = connect_to_fd(udp_server, 0);
+ if (!ASSERT_NEQ(udp_client, -1, "connect udp_client"))
+ goto close_udp_server;
+ ret = send(udp_client, buf, sizeof(buf), 0);
+ if (!ASSERT_EQ(ret, sizeof(buf), "send udp"))
+ goto close_udp_client;
+
+ /* recv() ensures TC ingress BPF has processed the skb */
+ ret = recv(udp_server, buf, sizeof(buf), 0);
+ if (!ASSERT_EQ(ret, sizeof(buf), "recv udp"))
+ goto close_udp_client;
+
+ ASSERT_EQ(skel->bss->udp_intercepted, true, "udp_intercepted");
+
+ /* assign_ret == 0 means kfunc accepted UDP skb (bug).
+ * assign_ret < 0 means kfunc correctly rejected it (fixed).
+ */
+ ASSERT_NEQ(skel->data->assign_ret, 0, "assign_ret");
+
+close_udp_client:
+ close(udp_client);
+close_udp_server:
+ close(udp_server);
+close_tcp:
+ close(tcp_server);
+}
+
+void test_tcp_custom_syncookie_protocol_check(void)
+{
+ struct test_tcp_custom_syncookie *skel;
+ int i;
+
+ if (setup_netns())
+ return;
+
+ skel = test_tcp_custom_syncookie__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open_and_load"))
+ return;
+
+ if (setup_tc(bpf_program__fd(skel->progs.tcp_custom_syncookie_badproto)))
+ goto destroy_skel;
+
+ for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
+ if (!test__start_subtest(test_cases[i].name))
+ continue;
+
+ run_protocol_check(skel, test_cases[i].family,
+ test_cases[i].addr);
+ }
+
+destroy_skel:
+ system("tc qdisc del dev lo clsact");
test_tcp_custom_syncookie__destroy(skel);
}
diff --git a/tools/testing/selftests/bpf/progs/test_tcp_custom_syncookie.c b/tools/testing/selftests/bpf/progs/test_tcp_custom_syncookie.c
index 7d5293de1952..c4834abea9c5 100644
--- a/tools/testing/selftests/bpf/progs/test_tcp_custom_syncookie.c
+++ b/tools/testing/selftests/bpf/progs/test_tcp_custom_syncookie.c
@@ -588,4 +588,113 @@ int tcp_custom_syncookie(struct __sk_buff *skb)
return tcp_handle_ack(&ctx);
}
+/* Test: call bpf_sk_assign_tcp_reqsk() on a UDP skb.
+ * The kfunc should reject it because the skb is not TCP.
+ *
+ * TCP and UDP servers share the same port. The BPF program intercepts
+ * UDP packets, looks up the TCP listener on the same port, and tries
+ * to assign a TCP reqsk to the UDP skb.
+ */
+int assign_ret = -1;
+bool udp_intercepted = false;
+
+static int badproto_lookup_assign(struct __sk_buff *skb, struct udphdr *udp,
+ struct bpf_sock_tuple *tuple, u32 tuple_size)
+{
+ struct bpf_tcp_req_attrs attrs = {};
+ struct bpf_sock *skc;
+ struct sock *sk;
+
+ skc = bpf_skc_lookup_tcp(skb, tuple, tuple_size, -1, 0);
+ if (!skc)
+ return TC_ACT_OK;
+
+ if (skc->state != TCP_LISTEN) {
+ bpf_sk_release(skc);
+ return TC_ACT_OK;
+ }
+
+ sk = (struct sock *)bpf_skc_to_tcp_sock(skc);
+ if (!sk) {
+ bpf_sk_release(skc);
+ return TC_ACT_OK;
+ }
+
+ attrs.mss = 1460;
+ attrs.wscale_ok = 1;
+ attrs.snd_wscale = 7;
+ attrs.rcv_wscale = 7;
+ attrs.sack_ok = 1;
+
+ assign_ret = bpf_sk_assign_tcp_reqsk(skb, sk, &attrs, sizeof(attrs));
+
+ bpf_sk_release(skc);
+ return TC_ACT_OK;
+}
+
+SEC("tc")
+int tcp_custom_syncookie_badproto(struct __sk_buff *skb)
+{
+ void *data = (void *)(long)skb->data;
+ void *data_end = (void *)(long)skb->data_end;
+ struct bpf_sock_tuple tuple = {};
+ struct ethhdr *eth;
+ struct iphdr *iph;
+ struct ipv6hdr *ip6h;
+ struct udphdr *udp;
+
+ eth = (struct ethhdr *)data;
+ if (eth + 1 > data_end)
+ return TC_ACT_OK;
+
+ switch (bpf_ntohs(eth->h_proto)) {
+ case ETH_P_IP:
+ iph = (struct iphdr *)(eth + 1);
+ if (iph + 1 > data_end)
+ return TC_ACT_OK;
+
+ if (iph->protocol != IPPROTO_UDP)
+ return TC_ACT_OK;
+
+ udp = (struct udphdr *)(iph + 1);
+ if (udp + 1 > data_end)
+ return TC_ACT_OK;
+
+ udp_intercepted = true;
+
+ tuple.ipv4.saddr = iph->saddr;
+ tuple.ipv4.daddr = iph->daddr;
+ tuple.ipv4.sport = udp->source;
+ tuple.ipv4.dport = udp->dest;
+
+ return badproto_lookup_assign(skb, udp, &tuple,
+ sizeof(tuple.ipv4));
+ case ETH_P_IPV6:
+ ip6h = (struct ipv6hdr *)(eth + 1);
+ if (ip6h + 1 > data_end)
+ return TC_ACT_OK;
+
+ if (ip6h->nexthdr != IPPROTO_UDP)
+ return TC_ACT_OK;
+
+ udp = (struct udphdr *)(ip6h + 1);
+ if (udp + 1 > data_end)
+ return TC_ACT_OK;
+
+ udp_intercepted = true;
+
+ __builtin_memcpy(tuple.ipv6.saddr, &ip6h->saddr,
+ sizeof(tuple.ipv6.saddr));
+ __builtin_memcpy(tuple.ipv6.daddr, &ip6h->daddr,
+ sizeof(tuple.ipv6.daddr));
+ tuple.ipv6.sport = udp->source;
+ tuple.ipv6.dport = udp->dest;
+
+ return badproto_lookup_assign(skb, udp, &tuple,
+ sizeof(tuple.ipv6));
+ default:
+ return TC_ACT_OK;
+ }
+}
+
char _license[] SEC("license") = "GPL";
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v2 02/15] firmware: qcom: Add a generic PAS service
From: Krzysztof Kozlowski @ 2026-03-27 13:56 UTC (permalink / raw)
To: Konrad Dybcio, Sumit Garg
Cc: linux-arm-msm, devicetree, dri-devel, freedreno, linux-media,
netdev, linux-wireless, ath12k, linux-remoteproc, andersson,
konradybcio, robh, krzk+dt, conor+dt, robin.clark, sean, akhilpo,
lumag, abhinav.kumar, jesszhan0024, marijn.suijten, airlied,
simona, vikash.garodia, dikshita.agarwal, bod, mchehab, elder,
andrew+netdev, davem, edumazet, kuba, pabeni, jjohnson,
mathieu.poirier, trilokkumar.soni, mukesh.ojha, pavan.kondeti,
jorge.ramirez, tonyh, vignesh.viswanathan, srinivas.kandagatla,
amirreza.zarrabi, jens.wiklander, op-tee, apurupa, skare,
linux-kernel, Sumit Garg
In-Reply-To: <2e55bdc3-54a1-4f18-b9ad-fe03f21fc4da@oss.qualcomm.com>
On 23/03/2026 15:26, Konrad Dybcio wrote:
>>>
>>> This pattern has been carried from the PAS API contract among kernel
>>> clients and the SCM PAS service earlier. The clients don't hold a
>>> reference to the PAS data like underlying platform or TEE device etc.
>>> Hence the need to have a global data pointer to hold reference to the
>>> ops data structure registered by drivers having different lifetime of
>>> devices. Also, the PAS APIs can be called from very different client
>>> driver contexts.
>>>
>>> Surely, avoiding global data is always better given a better alternative
>>> is there. Do you have any better alternative proposal here?
>>
>> Why it cannot be part of the context?
>>
>> Look at your API, e.g.:
>> qcom_pas_init_image(). It takes struct qcom_pas_context which should
>> contain the ops.
>
> This would make the client have to select the ops. The whole point is to
> avoid that, since the client has no clue (and is supposed not to have any).
Yeah, I see. The problem is that this patchset just keeps growing the
singletons so except existing 'struct qcom_scm *__scm' in qcom_scm.c,
this one brings at least three new: 'ops_ptr', 'qcom_pas_ops_scm' and
'qcom_pas_ops_tee'.
I don't think you need all four in total, but only one which will hold
whatever pointers are necessary.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH net] net/sched: Mark qdisc for deletion if graft cannot delete
From: Jamal Hadi Salim @ 2026-03-27 13:58 UTC (permalink / raw)
To: Jakub Kicinski
Cc: netdev, davem, edumazet, pabeni, horms, jiri, toke,
vinicius.gomes, stephen, vladbu, cake, bpf, ghandatmanas,
km.kim1503, security, Victor Nogueira
In-Reply-To: <CAM0EoMnY7b+wzxCP9Gqso-v7xBKjaA_9FN4OZc2YjifmCvAVsA@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 6549 bytes --]
On Sun, Mar 15, 2026 at 11:56 AM Jamal Hadi Salim <jhs@mojatatu.com> wrote:
>
> On Sat, Mar 14, 2026 at 11:00 AM Jakub Kicinski <kuba@kernel.org> wrote:
> >
> > On Fri, 13 Mar 2026 15:36:28 -0400 Jamal Hadi Salim wrote:
> > > In this specific example, the issue is that the classifier code path
> > > can't release the rtnl_lock while the qdisc's refcnt is bigger than 1.
> > >
> > > Does this make more sense?
> > > The reason we went with the "mark for delete" approach is at time x+3
> > > the "qdisc add" wont be able to find this qdisc. This is the common
> > > observed pattern - for example described in the commit message where
> > > we get have a slightly different flow with "qdisc del" before "filter
> > > add".
> >
> > Maybe a (completely untested) diff will help illustrate my thinking
> > better than words:
> >
>
> Dont have much time today - but will try in the upcoming days.
>
Sorry - was a bit loaded with work. So finally tried this idea - see
attached. Comments:
The bigger challenge is when we encounter a phony action (one that
does not exist). This gets exacerbated when you have a list of actions
and the phony one is in the middle.
One attempt to resolve this involves keeping the transaction state of
the last failed action loading; see the variable rtnl_failed_act_num
(another global variable).
The global variables leave a stench (and we should not even consider
such an approach), but this seems resolvable by returning back a
"retry context".
tbh, I am not sure about this approach; while we can make it work, we
are adding unnecessary complexity to an already complex piece of code.
cheers,
jamal
> cheers,
> jamal
>
> > diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
> > index 99ac747b7906..c13c9e8619e4 100644
> > --- a/include/net/pkt_cls.h
> > +++ b/include/net/pkt_cls.h
> > @@ -27,6 +27,9 @@ void unregister_tcf_proto_ops(struct tcf_proto_ops *ops);
> > #define NET_CLS_ALIAS_PREFIX "net-cls-"
> > #define MODULE_ALIAS_NET_CLS(kind) MODULE_ALIAS(NET_CLS_ALIAS_PREFIX kind)
> >
> > +extern char *rtnl_load_mod;
> > +void rtnl_load_mod_check(void);
> > +
> > struct tcf_block_ext_info {
> > enum flow_block_binder_type binder_type;
> > tcf_chain_head_change_t *chain_head_change;
> > diff --git a/net/sched/act_api.c b/net/sched/act_api.c
> > index 332fd9695e54..c21dd2e36592 100644
> > --- a/net/sched/act_api.c
> > +++ b/net/sched/act_api.c
> > @@ -1368,11 +1368,15 @@ struct tc_action_ops *tc_action_load_ops(struct nlattr *nla, u32 flags,
> > #ifdef CONFIG_MODULES
> > bool rtnl_held = !(flags & TCA_ACT_FLAGS_NO_RTNL);
> >
> > - if (rtnl_held)
> > - rtnl_unlock();
> > + if (rtnl_held) {
> > + if (WARN_ON_ONCE(rtnl_load_mod))
> > + return ERR_PTR(-EINVAL);
> > + rtnl_load_mod = kasprintf(GFP_KERNEL,
> > + NET_ACT_ALIAS_PREFIX "%s",
> > + act_name);
> > + return ERR_PTR(-EAGAIN);
> > + }
> > request_module(NET_ACT_ALIAS_PREFIX "%s", act_name);
> > - if (rtnl_held)
> > - rtnl_lock();
> >
> > a_o = tc_lookup_action_n(act_name);
> >
> > @@ -2107,6 +2111,9 @@ static int tcf_action_add(struct net *net, struct nlattr *nla,
> > &attr_size, flags, 0, extack);
> > if (ret != -EAGAIN)
> > break;
> > + rtnl_unlock();
> > + rtnl_load_mod_check();
> > + rtnl_lock();
> > }
> >
> > if (ret < 0)
> > diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
> > index 4829c27446e3..1b0f762d6e4b 100644
> > --- a/net/sched/cls_api.c
> > +++ b/net/sched/cls_api.c
> > @@ -46,6 +46,19 @@
> > /* The list of all installed classifier types */
> > static LIST_HEAD(tcf_proto_base);
> >
> > +char *rtnl_load_mod;
> > +
> > +void rtnl_load_mod_check(void)
> > +{
> > + char *mod = rtnl_load_mod;
> > +
> > + if (mod) {
> > + rtnl_load_mod = NULL;
> > + request_module("%s", mod);
> > + kfree(mod);
> > + }
> > +}
> > +
> > /* Protects list of registered TC modules. It is pure SMP lock. */
> > static DEFINE_RWLOCK(cls_mod_lock);
> >
> > @@ -255,17 +268,15 @@ tcf_proto_lookup_ops(const char *kind, bool rtnl_held,
> > if (ops)
> > return ops;
> > #ifdef CONFIG_MODULES
> > - if (rtnl_held)
> > - rtnl_unlock();
> > + if (rtnl_held) {
> > + if (WARN_ON_ONCE(rtnl_load_mod))
> > + return ERR_PTR(-EINVAL);
> > + rtnl_load_mod = kasprintf(GFP_KERNEL,
> > + NET_CLS_ALIAS_PREFIX "%s", kind);
> > + return ERR_PTR(-EAGAIN);
> > + }
> > request_module(NET_CLS_ALIAS_PREFIX "%s", kind);
> > - if (rtnl_held)
> > - rtnl_lock();
> > ops = __tcf_proto_lookup_ops(kind);
> > - /* We dropped the RTNL semaphore in order to perform
> > - * the module load. So, even if we succeeded in loading
> > - * the module we have to replay the request. We indicate
> > - * this using -EAGAIN.
> > - */
> > if (ops) {
> > module_put(ops->owner);
> > return ERR_PTR(-EAGAIN);
> > @@ -2459,6 +2470,7 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
> > * of target chain.
> > */
> > rtnl_held = true;
> > + rtnl_load_mod_check();
> > /* Replay the request. */
> > goto replay;
> > }
> > @@ -3230,9 +3242,13 @@ static int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n,
> > tcf_chain_put(chain);
> > errout_block:
> > tcf_block_release(q, block, true);
> > - if (err == -EAGAIN)
> > + if (err == -EAGAIN) {
> > + rtnl_unlock();
> > + rtnl_load_mod_check();
> > + rtnl_lock();
> > /* Replay the request. */
> > goto replay;
> > + }
> > return err;
> >
> > errout_block_locked:
[-- Attachment #2: p2 --]
[-- Type: application/octet-stream, Size: 10999 bytes --]
diff --git a/include/net/act_api.h b/include/net/act_api.h
index d11b79107930..7e2d151addfe 100644
--- a/include/net/act_api.h
+++ b/include/net/act_api.h
@@ -217,6 +217,7 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
struct tc_action *actions[], int init_res[], size_t *attr_size,
u32 flags, u32 fl_flags, struct netlink_ext_ack *extack);
struct tc_action_ops *tc_action_load_ops(struct nlattr *nla, u32 flags,
+ int act_num,
struct netlink_ext_ack *extack);
struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
struct nlattr *nla, struct nlattr *est,
diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index 99ac747b7906..e8e722a9e538 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -27,6 +27,9 @@ void unregister_tcf_proto_ops(struct tcf_proto_ops *ops);
#define NET_CLS_ALIAS_PREFIX "net-cls-"
#define MODULE_ALIAS_NET_CLS(kind) MODULE_ALIAS(NET_CLS_ALIAS_PREFIX kind)
+extern char *rtnl_load_mod;
+extern int rtnl_failed_act_num;
+
struct tcf_block_ext_info {
enum flow_block_binder_type binder_type;
tcf_chain_head_change_t *chain_head_change;
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index 332fd9695e54..40c350c8ce1d 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -1332,7 +1332,7 @@ void tcf_idr_insert_many(struct tc_action *actions[], int init_res[])
}
struct tc_action_ops *tc_action_load_ops(struct nlattr *nla, u32 flags,
- struct netlink_ext_ack *extack)
+ int act_num, struct netlink_ext_ack *extack)
{
bool police = flags & TCA_ACT_FLAGS_POLICE;
struct nlattr *tb[TCA_ACT_MAX + 1];
@@ -1367,9 +1367,39 @@ struct tc_action_ops *tc_action_load_ops(struct nlattr *nla, u32 flags,
if (a_o == NULL) {
#ifdef CONFIG_MODULES
bool rtnl_held = !(flags & TCA_ACT_FLAGS_NO_RTNL);
+ bool bind = flags & TCA_ACT_FLAGS_BIND;
- if (rtnl_held)
+ if (rtnl_held) {
+ /* bind means we are coming in from a code path which
+ * has a filter refering to an action (cls_api)
+ */
+ if (bind) {
+ /* We already tried to load the action module
+ * in a previous call from cls_api but even
+ * this subsequent call (tc_lookup_action_n)
+ * failed to find the action
+ * rtnl_failed_act_num will catch this issue
+ * of subsequent failures...
+ */
+ if (rtnl_failed_act_num &&
+ rtnl_failed_act_num >= act_num) {
+ kfree(rtnl_load_mod);
+ rtnl_load_mod = NULL;
+ rtnl_failed_act_num = 0;
+ goto act_not_found;
+ }
+ rtnl_load_mod = kasprintf(GFP_KERNEL,
+ NET_ACT_ALIAS_PREFIX "%s",
+ act_name);
+ if (!rtnl_load_mod) {
+ rtnl_failed_act_num = 0;
+ return ERR_PTR(-ENOMEM);
+ }
+ rtnl_failed_act_num = act_num;
+ return ERR_PTR(-EAGAIN);
+ }
rtnl_unlock();
+ }
request_module(NET_ACT_ALIAS_PREFIX "%s", act_name);
if (rtnl_held)
rtnl_lock();
@@ -1387,6 +1417,7 @@ struct tc_action_ops *tc_action_load_ops(struct nlattr *nla, u32 flags,
return ERR_PTR(-EAGAIN);
}
#endif
+act_not_found:
NL_SET_ERR_MSG(extack, "Failed to load TC action module");
return ERR_PTR(-ENOENT);
}
@@ -1496,7 +1527,7 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
struct tc_action_ops *a_o;
- a_o = tc_action_load_ops(tb[i], flags, extack);
+ a_o = tc_action_load_ops(tb[i], flags, i, extack);
if (IS_ERR(a_o)) {
err = PTR_ERR(a_o);
goto err_mod;
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 4829c27446e3..34bfe9a63eda 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -46,6 +46,9 @@
/* The list of all installed classifier types */
static LIST_HEAD(tcf_proto_base);
+char *rtnl_load_mod;
+int rtnl_failed_act_num;
+
/* Protects list of registered TC modules. It is pure SMP lock. */
static DEFINE_RWLOCK(cls_mod_lock);
@@ -349,7 +352,7 @@ static bool tcf_proto_check_kind(struct nlattr *kind, char *name)
return false;
}
-static bool tcf_proto_is_unlocked(const char *kind)
+static bool tcf_proto_is_unlocked(const char *kind, bool rtnl_held)
{
const struct tcf_proto_ops *ops;
bool ret;
@@ -357,7 +360,7 @@ static bool tcf_proto_is_unlocked(const char *kind)
if (strlen(kind) == 0)
return false;
- ops = tcf_proto_lookup_ops(kind, false, NULL);
+ ops = tcf_proto_lookup_ops(kind, rtnl_held, NULL);
/* On error return false to take rtnl lock. Proto lookup/create
* functions will perform lookup again and properly handle errors.
*/
@@ -2228,6 +2231,12 @@ static bool is_qdisc_ingress(__u32 classid)
return (TC_H_MIN(classid) == TC_H_MIN(TC_H_MIN_INGRESS));
}
+static bool parent_is_ingress_or_egress(__u32 parentid)
+{
+ return (TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS) == parentid) ||
+ (TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_EGRESS) == parentid);
+}
+
static bool is_ingress_or_clsact(struct tcf_block *block, struct Qdisc *q)
{
return tcf_block_shared(block) || (q && !!(q->flags & TCQ_F_INGRESS));
@@ -2255,6 +2264,7 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
int err;
int tp_created;
bool rtnl_held = false;
+ bool proto_is_unlocked;
u32 flags;
replay:
@@ -2290,25 +2300,30 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
}
}
- /* Find head of filter chain. */
-
- err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack);
- if (err)
- return err;
-
if (tcf_proto_check_kind(tca[TCA_KIND], name)) {
NL_SET_ERR_MSG(extack, "Specified TC filter name too long");
- err = -EINVAL;
- goto errout;
+ return -EINVAL;
}
+ proto_is_unlocked = tcf_proto_is_unlocked(name, rtnl_held);
- /* Take rtnl mutex if rtnl_held was set to true on previous iteration,
- * block is shared (no qdisc found), qdisc is not unlocked, classifier
- * type is not specified, classifier is not unlocked.
- */
- if (rtnl_held ||
- (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) ||
- !tcf_proto_is_unlocked(name)) {
+ if (!rtnl_held && !parent_is_ingress_or_egress(parent)) {
+ rtnl_held = true;
+ rtnl_lock();
+ }
+
+ /* Find head of filter chain. */
+ err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, rtnl_held,
+ extack);
+ if (err) {
+ if (rtnl_held)
+ goto errout;
+ return err;
+ }
+
+ /* Take rtnl mutex if classifier type is not specified, or classifier
+ * is not unlocked.
+ */
+ if (!rtnl_held && !proto_is_unlocked) {
rtnl_held = true;
rtnl_lock();
}
@@ -2451,17 +2466,43 @@ static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
}
tcf_block_release(q, block, rtnl_held);
- if (rtnl_held)
- rtnl_unlock();
-
if (err == -EAGAIN) {
/* Take rtnl lock in case EAGAIN is caused by concurrent flush
* of target chain.
*/
- rtnl_held = true;
+ if (rtnl_held) {
+ if (rtnl_load_mod) {
+ int failed_act_num = rtnl_failed_act_num;
+ char mod[MODULE_NAME_LEN];
+
+ /* Saving these variables so they dont get lost
+ * when rtnl lock is released
+ */
+ strscpy(mod, rtnl_load_mod);
+ kfree(rtnl_load_mod);
+ rtnl_load_mod = NULL;
+ rtnl_failed_act_num = 0;
+
+ rtnl_unlock();
+ request_module("%s", mod);
+ rtnl_lock();
+ rtnl_failed_act_num = failed_act_num;
+ }
+ } else {
+ rtnl_lock();
+ rtnl_held = true;
+ }
/* Replay the request. */
goto replay;
}
+
+ if (rtnl_held) {
+ kfree(rtnl_load_mod);
+ rtnl_load_mod = NULL;
+ rtnl_failed_act_num = 0;
+ rtnl_unlock();
+ }
+
return err;
errout_locked:
@@ -2488,6 +2529,7 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
unsigned long cl = 0;
void *fh = NULL;
int err;
+ bool proto_is_unlocked = true;
bool rtnl_held = false;
err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX,
@@ -2505,24 +2547,29 @@ static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
return -ENOENT;
}
- /* Find head of filter chain. */
-
- err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack);
- if (err)
- return err;
-
if (tcf_proto_check_kind(tca[TCA_KIND], name)) {
NL_SET_ERR_MSG(extack, "Specified TC filter name too long");
- err = -EINVAL;
- goto errout;
+ return -EINVAL;
}
- /* Take rtnl mutex if flushing whole chain, block is shared (no qdisc
- * found), qdisc is not unlocked, classifier type is not specified,
- * classifier is not unlocked.
+ proto_is_unlocked = tcf_proto_is_unlocked(name, rtnl_held);
+
+ if (!parent_is_ingress_or_egress(parent)) {
+ rtnl_held = true;
+ rtnl_lock();
+ }
+
+ /* Find head of filter chain. */
+ err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, rtnl_held,
+ extack);
+ if (err) {
+ if (rtnl_held)
+ rtnl_unlock();
+ return err;
+ }
+ /* Take rtnl mutex if flushing whole chain, classifier type is not
+ * specified, classifier is not unlocked.
*/
- if (!prio ||
- (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) ||
- !tcf_proto_is_unlocked(name)) {
+ if (!rtnl_held && (!prio || !proto_is_unlocked)) {
rtnl_held = true;
rtnl_lock();
}
@@ -2648,6 +2695,7 @@ static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
void *fh = NULL;
int err;
bool rtnl_held = false;
+ bool proto_is_unlocked;
err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX,
rtm_tca_policy, extack);
@@ -2664,23 +2712,29 @@ static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
return -ENOENT;
}
- /* Find head of filter chain. */
-
- err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack);
- if (err)
- return err;
-
if (tcf_proto_check_kind(tca[TCA_KIND], name)) {
NL_SET_ERR_MSG(extack, "Specified TC filter name too long");
- err = -EINVAL;
- goto errout;
+ return -EINVAL;
}
- /* Take rtnl mutex if block is shared (no qdisc found), qdisc is not
- * unlocked, classifier type is not specified, classifier is not
- * unlocked.
- */
- if ((q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) ||
- !tcf_proto_is_unlocked(name)) {
+ proto_is_unlocked = tcf_proto_is_unlocked(name, false);
+
+ if (!parent_is_ingress_or_egress(parent)) {
+ rtnl_held = true;
+ rtnl_lock();
+ }
+
+ /* Find head of filter chain. */
+ err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, rtnl_held,
+ extack);
+ if (err) {
+ if (rtnl_held)
+ rtnl_unlock();
+ return err;
+ }
+ /* Take rtnl mutex if classifier type is not specified, or classifier is
+ * not unlocked.
+ */
+ if (!rtnl_held && !proto_is_unlocked) {
rtnl_held = true;
rtnl_lock();
}
@@ -3403,7 +3457,7 @@ int tcf_exts_validate_ex(struct net *net, struct tcf_proto *tp, struct nlattr **
flags |= TCA_ACT_FLAGS_POLICE | TCA_ACT_FLAGS_BIND;
a_o = tc_action_load_ops(tb[exts->police], flags,
- extack);
+ 1, extack);
if (IS_ERR(a_o))
return PTR_ERR(a_o);
act = tcf_action_init_1(net, tp, tb[exts->police],
^ permalink raw reply related
* Re: [PATCH net-next v2 3/5] ice: migrate to netdev ops lock
From: Simon Horman @ 2026-03-27 13:57 UTC (permalink / raw)
To: anthony.l.nguyen
Cc: Simon Horman, magnus.karlsson, maciej.fijalkowski, sdf,
nxne.cnse.osdt.itp.upstreaming, daniel, davem, aleksander.lobakin,
kohei, pabeni, hawk, ast, andrew+netdev, netdev, kuba, edumazet,
aleksandr.loktionov, john.fastabend, bpf, jacob.e.keller
In-Reply-To: <20260325200644.2528726-4-anthony.l.nguyen@intel.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
For local reproduction steps see https://netdev-ai.bots.linux.dev/ai-local.html
---
ice: migrate to netdev ops lock
Queue management ops unconditionally enable netdev locking. The same
lock is taken by default by several NAPI configuration functions, such
as napi_enable() and netif_napi_set_irq(). Request ops locking in
advance and make sure we use the _locked counterparts of those functions
to avoid deadlocks, taking the lock manually where needed (suspend/resume,
queue rebuild and resets).
> diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
> index bd77f1c001ee..2ca3fc6e8abd 100644
> --- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
> +++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
[ ... ]
> @@ -273,14 +273,13 @@ void ice_vsi_cfg_dcb_rings(struct ice_vsi *vsi)
> /**
> * ice_dcb_ena_dis_vsi - disable certain VSIs for DCB config/reconfig
> * @pf: pointer to the PF instance
> * @ena: true to enable VSIs, false to disable
> - * @locked: true if caller holds RTNL lock, false otherwise
> *
> * Before a new DCB configuration can be applied, VSIs of type PF, SWITCHDEV
> * and CHNL need to be brought down. Following completion of DCB configuration
> * the VSIs that were downed need to be brought up again. This helper function
> * does both.
> */
> -static void ice_dcb_ena_dis_vsi(struct ice_pf *pf, bool ena, bool locked)
> +static void ice_dcb_ena_dis_vsi(struct ice_pf *pf, bool ena)
> {
> int i;
>
> @@ -294,9 +293,9 @@ static void ice_dcb_ena_dis_vsi(struct ice_pf *pf, bool ena, bool locked)
> case ICE_VSI_CHNL:
> case ICE_VSI_PF:
> if (ena)
> - ice_ena_vsi(vsi, locked);
> + ice_ena_vsi(vsi);
> else
> - ice_dis_vsi(vsi, locked);
> + ice_dis_vsi(vsi);
> break;
> default:
> continue;
[Simon says: this seems to be a false positive and what the AI
suggests seems to be the exact opposite of what is correct]
The DCB callers all hold the RTNL lock rather than the netdev lock. While
ice_ena_vsi() and ice_dis_vsi() now take the netdev lock internally, their
_locked counterparts (ice_ena_vsi_locked and ice_dis_vsi_locked) would be
more appropriate here since the netdev lock is not held by ice_pf_dcb_cfg()
or ice_dcb_process_lldp_set_mib_change().
> diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
> index e7308e381e2f..82f6523bc726 100644
> --- a/drivers/net/ethernet/intel/ice/ice_main.c
> +++ b/drivers/net/ethernet/intel/ice/ice_main.c
[ ... ]
> @@ -7263,7 +7271,7 @@ static void ice_napi_disable_all(struct ice_vsi *vsi)
> struct ice_q_vector *q_vector = vsi->q_vectors[q_idx];
>
> if (q_vector->rx.rx_ring || q_vector->tx.tx_ring)
> - napi_disable(&q_vector->napi);
> + napi_disable_locked(&q_vector->napi);
>
> cancel_work_sync(&q_vector->tx.dim.work);
> cancel_work_sync(&q_vector->rx.dim.work);
[Simon says: this one seems plausible to me. Could you take a look?]
Does this introduce a lockdep warning in the eswitch/switchdev path?
ice_napi_disable_all() is called via ice_down(), and ice_down() can be
reached through the eswitch path without holding the netdev lock:
ice_eswitch_attach()
-> ice_eswitch_enable_switchdev()
-> ice_eswitch_setup_env()
-> ice_down(uplink_vsi)
-> ice_napi_disable_all()
-> napi_disable_locked()
At this point napi_disable_locked() will assert that the netdev lock is
held, but the eswitch code path doesn't take it. The uplink_vsi is the
main PF VSI with a valid netdev, so the early return won't trigger.
Should ice_eswitch_setup_env() take the netdev lock before calling
ice_down(), or should ice_down() have both locked and unlocked variants?
^ permalink raw reply
* Re: [PATCH net v3 1/2] net/sched: sch_hfsc: fix divide-by-zero in rtsc_min()
From: Jamal Hadi Salim @ 2026-03-27 14:01 UTC (permalink / raw)
To: Xiang Mei
Cc: security, netdev, pabeni, jiri, davem, edumazet, kuba, horms,
bestswngs
In-Reply-To: <20260326204310.1549327-1-xmei5@asu.edu>
On Thu, Mar 26, 2026 at 4:43 PM Xiang Mei <xmei5@asu.edu> wrote:
>
> m2sm() converts a u32 slope to a u64 scaled value. For large inputs
> (e.g. m1=4000000000), the result can reach 2^32. rtsc_min() stores
> the difference of two such u64 values in a u32 variable `dsm` and
> uses it as a divisor. When the difference is exactly 2^32 the
> truncation yields zero, causing a divide-by-zero oops in the
> concave-curve intersection path:
>
> Oops: divide error: 0000
> RIP: 0010:rtsc_min (net/sched/sch_hfsc.c:601)
> Call Trace:
> init_ed (net/sched/sch_hfsc.c:629)
> hfsc_enqueue (net/sched/sch_hfsc.c:1569)
> [...]
>
> Widen `dsm` to u64 and replace do_div() with div64_u64() so the full
> difference is preserved.
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Reported-by: Weiming Shi <bestswngs@gmail.com>
> Signed-off-by: Xiang Mei <xmei5@asu.edu>
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
cheers,
jamal
> ---
> v2: resend to netdev ML
> v3: append selftest in 2/2
>
> net/sched/sch_hfsc.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c
> index b5657ffbbf84..83b2ca2e37fc 100644
> --- a/net/sched/sch_hfsc.c
> +++ b/net/sched/sch_hfsc.c
> @@ -555,7 +555,7 @@ static void
> rtsc_min(struct runtime_sc *rtsc, struct internal_sc *isc, u64 x, u64 y)
> {
> u64 y1, y2, dx, dy;
> - u32 dsm;
> + u64 dsm;
>
> if (isc->sm1 <= isc->sm2) {
> /* service curve is convex */
> @@ -598,7 +598,7 @@ rtsc_min(struct runtime_sc *rtsc, struct internal_sc *isc, u64 x, u64 y)
> */
> dx = (y1 - y) << SM_SHIFT;
> dsm = isc->sm1 - isc->sm2;
> - do_div(dx, dsm);
> + dx = div64_u64(dx, dsm);
> /*
> * check if (x, y1) belongs to the 1st segment of rtsc.
> * if so, add the offset.
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH net v3 2/2] selftests/tc-testing: add test for HFSC divide-by-zero in rtsc_min()
From: Jamal Hadi Salim @ 2026-03-27 14:02 UTC (permalink / raw)
To: Xiang Mei
Cc: security, netdev, pabeni, jiri, davem, edumazet, kuba, horms,
bestswngs
In-Reply-To: <20260326204310.1549327-2-xmei5@asu.edu>
On Thu, Mar 26, 2026 at 4:43 PM Xiang Mei <xmei5@asu.edu> wrote:
>
> Add a regression test for the divide-by-zero in rtsc_min() triggered
> when m2sm() converts a large m1 value (e.g. 32gbit) to a u64 scaled
> slope reaching 2^32. rtsc_min() stores the difference of two such u64
> values (sm1 - sm2) in a u32 variable `dsm`, truncating 2^32 to zero
> and causing a divide-by-zero oops in the concave-curve intersection
> path. The test configures an HFSC class with m1=32gbit d=1ms m2=0bit,
> sends a packet to activate the class, waits for it to drain and go
> idle, then sends another packet to trigger reactivation through
> rtsc_min().
>
> Signed-off-by: Xiang Mei <xmei5@asu.edu>
Thanks for doing this.
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
cheers,
jamal
> ---
> v3: append selftest in 2/2
>
> .../tc-testing/tc-tests/infra/qdiscs.json | 25 +++++++++++++++++++
> 1 file changed, 25 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 6a39640aa2a8..1e5efb2a31eb 100644
> --- a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json
> +++ b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json
> @@ -1111,5 +1111,30 @@
> "teardown": [
> "$TC qdisc del dev $DUMMY root handle 1:"
> ]
> + },
> + {
> + "id": "a3d7",
> + "name": "HFSC with large m1 - no divide-by-zero on class reactivation",
> + "category": [
> + "qdisc",
> + "hfsc"
> + ],
> + "plugins": {
> + "requires": "nsPlugin"
> + },
> + "setup": [
> + "$TC qdisc replace dev $DUMMY root handle 1: hfsc default 1",
> + "$TC class replace dev $DUMMY parent 1: classid 1:1 hfsc rt m1 32gbit d 1ms m2 0bit ls m1 32gbit d 1ms m2 0bit",
> + "ping -I$DUMMY -f -c1 -s64 -W1 10.10.10.1 || true",
> + "sleep 1"
> + ],
> + "cmdUnderTest": "ping -I$DUMMY -f -c1 -s64 -W1 10.10.10.1 || true",
> + "expExitCode": "0",
> + "verifyCmd": "$TC qdisc show dev $DUMMY",
> + "matchPattern": "qdisc hfsc 1: root",
> + "matchCount": "1",
> + "teardown": [
> + "$TC qdisc del dev $DUMMY handle 1: root"
> + ]
> }
> ]
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH net v3 2/2] selftests/tc-testing: add test for HFSC divide-by-zero in rtsc_min()
From: Victor Nogueira @ 2026-03-27 14:03 UTC (permalink / raw)
To: Xiang Mei, security
Cc: netdev, pabeni, jhs, jiri, davem, edumazet, kuba, horms,
bestswngs
In-Reply-To: <20260326204310.1549327-2-xmei5@asu.edu>
On 26/03/2026 17:43, Xiang Mei wrote:
> Add a regression test for the divide-by-zero in rtsc_min() triggered
> when m2sm() converts a large m1 value (e.g. 32gbit) to a u64 scaled
> slope reaching 2^32. rtsc_min() stores the difference of two such u64
> values (sm1 - sm2) in a u32 variable `dsm`, truncating 2^32 to zero
> and causing a divide-by-zero oops in the concave-curve intersection
> path. The test configures an HFSC class with m1=32gbit d=1ms m2=0bit,
> sends a packet to activate the class, waits for it to drain and go
> idle, then sends another packet to trigger reactivation through
> rtsc_min().
>
> Signed-off-by: Xiang Mei <xmei5@asu.edu>
Reviewed-by: Victor Nogueira <victor@mojatatu.com>
^ permalink raw reply
* [PATCH net-next v2] seg6: enable route leak for encap routes
From: Nicolas Dichtel @ 2026-03-27 14:06 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni, Eric Dumazet,
David Lebrun, Andrea Mayer, Paolo Lungaroni, David Ahern
Cc: netdev, Nicolas Dichtel
The goal is to support x-vrf route. To avoid breaking existing setup, a new
flag is introduced: nh-vrf.
The dev parameter is mandatory when a seg6 encap route is configured, but
before this commit, it is ignored/not used. After the srv6 encapsulation, a
second route lookup in the same vrf is performed.
The new nh-vrf flag specifies to use the vrf associated with the dev
parameter to perform this second route lookup.
The l3vpn tests show the inconsistency: the specified nexthop dev is
ignored and a second route is added in the same vrf for the segment address
(before the commit, the route to 'fc00:21:100::6046' was put in the vrf-100
table while the encap route was pointing to veth0, which is not associated
with a vrf).
The tests are updated to use the nh-vrf flag when available.
Before:
> $ ip -n rt_2-Rh5GP7 -6 r list vrf vrf-100 | grep fc00:21:100::6046
> cafe::1 encap seg6 mode encap segs 1 [ fc00:21:100::6046 ] dev veth0 metric 1024 pref medium
> fc00:21:100::6046 via fd00::1 dev veth0 metric 1024 pref medium
After:
> $ ip -n rt_2-Rh5GP7 -6 r list vrf vrf-100 | grep fc00:21:100::6046
> cafe::1 encap seg6 mode encap segs 1 [ fc00:21:100::6046 ] nh-vrf dev veth0 metric 1024 pref medium
> $ ip -n rt_2-Rh5GP7 -6 r list | grep fc00:21:100::6046
> fc00:21:100::6046 via fd00::1 dev veth0 metric 1024 pref medium
Fixes: 6c8702c60b88 ("ipv6: sr: add support for SRH encapsulation and injection with lwtunnels")
Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
---
v1 -> v2:
- target net-next instead of net
- add a new attribute to avoid breaking the legacy behavior
- use dst_dev_rcu()
include/uapi/linux/seg6_iptunnel.h | 1 +
net/ipv6/seg6_iptunnel.c | 23 ++++++++++--
.../selftests/net/srv6_end_dt46_l3vpn_test.sh | 35 +++++++++++++++++--
.../selftests/net/srv6_end_dt4_l3vpn_test.sh | 33 +++++++++++++++--
.../selftests/net/srv6_end_dt6_l3vpn_test.sh | 33 +++++++++++++++--
5 files changed, 116 insertions(+), 9 deletions(-)
diff --git a/include/uapi/linux/seg6_iptunnel.h b/include/uapi/linux/seg6_iptunnel.h
index 485889b19900..d7d6aa2f72c5 100644
--- a/include/uapi/linux/seg6_iptunnel.h
+++ b/include/uapi/linux/seg6_iptunnel.h
@@ -21,6 +21,7 @@ enum {
SEG6_IPTUNNEL_UNSPEC,
SEG6_IPTUNNEL_SRH,
SEG6_IPTUNNEL_SRC, /* struct in6_addr */
+ SEG6_IPTUNNEL_NH_VRF,
__SEG6_IPTUNNEL_MAX,
};
#define SEG6_IPTUNNEL_MAX (__SEG6_IPTUNNEL_MAX - 1)
diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
index e76cc0cc481e..f8c6f0d719be 100644
--- a/net/ipv6/seg6_iptunnel.c
+++ b/net/ipv6/seg6_iptunnel.c
@@ -50,6 +50,7 @@ static size_t seg6_lwt_headroom(struct seg6_iptunnel_encap *tuninfo)
struct seg6_lwt {
struct dst_cache cache;
struct in6_addr tunsrc;
+ bool nh_vrf;
struct seg6_iptunnel_encap tuninfo[];
};
@@ -67,6 +68,7 @@ seg6_encap_lwtunnel(struct lwtunnel_state *lwt)
static const struct nla_policy seg6_iptunnel_policy[SEG6_IPTUNNEL_MAX + 1] = {
[SEG6_IPTUNNEL_SRH] = { .type = NLA_BINARY },
[SEG6_IPTUNNEL_SRC] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)),
+ [SEG6_IPTUNNEL_NH_VRF] = { .type = NLA_FLAG },
};
static int nla_put_srh(struct sk_buff *skb, int attrtype,
@@ -499,9 +501,15 @@ static int seg6_input_core(struct net *net, struct sock *sk,
* now and use it later as a comparison.
*/
lwtst = orig_dst->lwtstate;
-
slwt = seg6_lwt_lwtunnel(lwtst);
+ if (slwt->nh_vrf) {
+ rcu_read_lock();
+ skb->dev = l3mdev_master_dev_rcu(dst_dev_rcu(orig_dst)) ?:
+ dev_net(skb->dev)->loopback_dev;
+ rcu_read_unlock();
+ }
+
local_bh_disable();
dst = dst_cache_get(&slwt->cache);
local_bh_enable();
@@ -724,6 +732,7 @@ static int seg6_build_state(struct net *net, struct nlattr *nla,
if (err)
goto free_lwt_state;
+ slwt->nh_vrf = !!tb[SEG6_IPTUNNEL_NH_VRF];
memcpy(&slwt->tuninfo, tuninfo, tuninfo_len);
if (tb[SEG6_IPTUNNEL_SRC]) {
@@ -775,6 +784,10 @@ static int seg6_fill_encap_info(struct sk_buff *skb,
nla_put_in6_addr(skb, SEG6_IPTUNNEL_SRC, &slwt->tunsrc))
return -EMSGSIZE;
+ if (slwt->nh_vrf &&
+ nla_put_flag(skb, SEG6_IPTUNNEL_NH_VRF))
+ return -EMSGSIZE;
+
return 0;
}
@@ -786,9 +799,14 @@ static int seg6_encap_nlsize(struct lwtunnel_state *lwtstate)
nlsize = nla_total_size(SEG6_IPTUN_ENCAP_SIZE(tuninfo));
+ /* SEG6_IPTUNNEL_SRC */
if (!ipv6_addr_any(&slwt->tunsrc))
nlsize += nla_total_size(sizeof(slwt->tunsrc));
+ /* SEG6_IPTUNNEL_NH_VRF */
+ if (slwt->nh_vrf)
+ nlsize += nla_total_size(0);
+
return nlsize;
}
@@ -803,7 +821,8 @@ static int seg6_encap_cmp(struct lwtunnel_state *a, struct lwtunnel_state *b)
if (len != SEG6_IPTUN_ENCAP_SIZE(b_hdr))
return 1;
- if (!ipv6_addr_equal(&a_slwt->tunsrc, &b_slwt->tunsrc))
+ if (!ipv6_addr_equal(&a_slwt->tunsrc, &b_slwt->tunsrc) ||
+ a_slwt->nh_vrf != b_slwt->nh_vrf)
return 1;
return memcmp(a_hdr, b_hdr, len);
diff --git a/tools/testing/selftests/net/srv6_end_dt46_l3vpn_test.sh b/tools/testing/selftests/net/srv6_end_dt46_l3vpn_test.sh
index a5e959a080bb..abf0a523518a 100755
--- a/tools/testing/selftests/net/srv6_end_dt46_l3vpn_test.sh
+++ b/tools/testing/selftests/net/srv6_end_dt46_l3vpn_test.sh
@@ -201,6 +201,7 @@ readonly IPv6_HS_NETWORK=cafe
readonly IPv4_HS_NETWORK=10.0.0
readonly VPN_LOCATOR_SERVICE=fc00
PING_TIMEOUT_SEC=4
+NH_VRF_FLAG=
ret=0
@@ -319,6 +320,7 @@ setup_vpn_config()
local hsdst=$3
local rtdst=$4
local tid=$5
+ local vrf=
eval local rtsrc_name=\${rt_${rtsrc}}
eval local rtdst_name=\${rt_${rtdst}}
@@ -330,10 +332,11 @@ setup_vpn_config()
# set the encap route for encapsulating packets which arrive from the
# host hssrc and destined to the access router rtsrc.
ip -netns ${rtsrc_name} -6 route add ${IPv6_HS_NETWORK}::${hsdst}/128 vrf vrf-${tid} \
- encap seg6 mode encap segs ${vpn_sid} dev veth0
+ encap seg6 mode encap segs ${vpn_sid} ${NH_VRF_FLAG} dev veth0
ip -netns ${rtsrc_name} -4 route add ${IPv4_HS_NETWORK}.${hsdst}/32 vrf vrf-${tid} \
- encap seg6 mode encap segs ${vpn_sid} dev veth0
- ip -netns ${rtsrc_name} -6 route add ${vpn_sid}/128 vrf vrf-${tid} \
+ encap seg6 mode encap segs ${vpn_sid} ${NH_VRF_FLAG} dev veth0
+ [ -z ${NH_VRF_FLAG} ] && vrf="vrf vrf-${tid}"
+ ip -netns ${rtsrc_name} -6 route add ${vpn_sid}/128 ${vrf} \
via fd00::${rtdst} dev veth0
# set the decap route for decapsulating packets which arrive from
@@ -536,6 +539,26 @@ host_vpn_isolation_tests()
done
}
+# check if the nh-vrf flag is supported
+check_nh_vrf_support()
+{
+ setup_ns nh_vrf_ns
+
+ if ! ip -netns "${nh_vrf_ns}" nexthop add id 1235 encap seg6 \
+ mode encap nh-vrf segs 2001:db8:1:1:1::2 dev lo &>/dev/null; then
+ cleanup_ns "${nh_vrf_ns}"
+ return
+ fi
+
+ if ! ip -netns "${nh_vrf_ns}" nexthop get id 1235 &>/dev/null; then
+ cleanup_ns "${nh_vrf_ns}"
+ return
+ fi
+
+ cleanup_ns "${nh_vrf_ns}"
+ NH_VRF_FLAG="nh-vrf"
+}
+
if [ "$(id -u)" -ne 0 ];then
echo "SKIP: Need root privileges"
exit $ksft_skip
@@ -554,6 +577,12 @@ fi
cleanup &>/dev/null
+check_nh_vrf_support
+if [ -z ${NH_VRF_FLAG} ]; then
+ echo "nh-vrf support: no"
+else
+ echo "nh-vrf support: yes"
+fi
setup
router_tests
diff --git a/tools/testing/selftests/net/srv6_end_dt4_l3vpn_test.sh b/tools/testing/selftests/net/srv6_end_dt4_l3vpn_test.sh
index a649dba3cb77..e1ae80bcb86d 100755
--- a/tools/testing/selftests/net/srv6_end_dt4_l3vpn_test.sh
+++ b/tools/testing/selftests/net/srv6_end_dt4_l3vpn_test.sh
@@ -170,6 +170,7 @@ readonly IPv6_RT_NETWORK=fd00
readonly IPv4_HS_NETWORK=10.0.0
readonly VPN_LOCATOR_SERVICE=fc00
PING_TIMEOUT_SEC=4
+NH_VRF_FLAG=
ret=0
@@ -278,6 +279,7 @@ setup_vpn_config()
local hsdst=$3
local rtdst=$4
local tid=$5
+ local vrf=
eval local rtsrc_name=\${rt_${rtsrc}}
eval local rtdst_name=\${rt_${rtdst}}
@@ -286,8 +288,9 @@ setup_vpn_config()
# set the encap route for encapsulating packets which arrive from the
# host hssrc and destined to the access router rtsrc.
ip -netns ${rtsrc_name} -4 route add ${IPv4_HS_NETWORK}.${hsdst}/32 vrf vrf-${tid} \
- encap seg6 mode encap segs ${vpn_sid} dev veth0
- ip -netns ${rtsrc_name} -6 route add ${vpn_sid}/128 vrf vrf-${tid} \
+ encap seg6 mode encap segs ${vpn_sid} ${NH_VRF_FLAG} dev veth0
+ [ -z ${NH_VRF_FLAG} ] && vrf="vrf vrf-${tid}"
+ ip -netns ${rtsrc_name} -6 route add ${vpn_sid}/128 ${vrf} \
via fd00::${rtdst} dev veth0
# set the decap route for decapsulating packets which arrive from
@@ -459,6 +462,26 @@ host_vpn_isolation_tests()
done
}
+# check if the nh-vrf flag is supported
+check_nh_vrf_support()
+{
+ setup_ns nh_vrf_ns
+
+ if ! ip -netns "${nh_vrf_ns}" nexthop add id 1235 encap seg6 \
+ mode encap nh-vrf segs 2001:db8:1:1:1::2 dev lo &>/dev/null; then
+ cleanup_ns "${nh_vrf_ns}"
+ return
+ fi
+
+ if ! ip -netns "${nh_vrf_ns}" nexthop get id 1235 &>/dev/null; then
+ cleanup_ns "${nh_vrf_ns}"
+ return
+ fi
+
+ cleanup_ns "${nh_vrf_ns}"
+ NH_VRF_FLAG="nh-vrf"
+}
+
if [ "$(id -u)" -ne 0 ];then
echo "SKIP: Need root privileges"
exit $ksft_skip
@@ -477,6 +500,12 @@ fi
cleanup &>/dev/null
+check_nh_vrf_support
+if [ -z ${NH_VRF_FLAG} ]; then
+ echo "nh-vrf support: no"
+else
+ echo "nh-vrf support: yes"
+fi
setup
router_tests
diff --git a/tools/testing/selftests/net/srv6_end_dt6_l3vpn_test.sh b/tools/testing/selftests/net/srv6_end_dt6_l3vpn_test.sh
index e408406d8489..3a8ff96a78ef 100755
--- a/tools/testing/selftests/net/srv6_end_dt6_l3vpn_test.sh
+++ b/tools/testing/selftests/net/srv6_end_dt6_l3vpn_test.sh
@@ -171,6 +171,7 @@ readonly IPv6_RT_NETWORK=fd00
readonly IPv6_HS_NETWORK=cafe
readonly VPN_LOCATOR_SERVICE=fc00
PING_TIMEOUT_SEC=4
+NH_VRF_FLAG=
ret=0
@@ -285,6 +286,7 @@ setup_vpn_config()
local hsdst=$3
local rtdst=$4
local tid=$5
+ local vrf=
eval local rtsrc_name=\${rt_${rtsrc}}
eval local rtdst_name=\${rt_${rtdst}}
@@ -296,8 +298,9 @@ setup_vpn_config()
# set the encap route for encapsulating packets which arrive from the
# host hssrc and destined to the access router rtsrc.
ip -netns ${rtsrc_name} -6 route add ${IPv6_HS_NETWORK}::${hsdst}/128 vrf vrf-${tid} \
- encap seg6 mode encap segs ${vpn_sid} dev veth0
- ip -netns ${rtsrc_name} -6 route add ${vpn_sid}/128 vrf vrf-${tid} \
+ encap seg6 mode encap segs ${vpn_sid} ${NH_VRF_FLAG} dev veth0
+ [ -z ${NH_VRF_FLAG} ] && vrf="vrf vrf-${tid}"
+ ip -netns ${rtsrc_name} -6 route add ${vpn_sid}/128 ${vrf} \
via fd00::${rtdst} dev veth0
# set the decap route for decapsulating packets which arrive from
@@ -469,6 +472,26 @@ host_vpn_isolation_tests()
done
}
+# check if the nh-vrf flag is supported
+check_nh_vrf_support()
+{
+ setup_ns nh_vrf_ns
+
+ if ! ip -netns "${nh_vrf_ns}" nexthop add id 1235 encap seg6 \
+ mode encap nh-vrf segs 2001:db8:1:1:1::2 dev lo &>/dev/null; then
+ cleanup_ns "${nh_vrf_ns}"
+ return
+ fi
+
+ if ! ip -netns "${nh_vrf_ns}" nexthop get id 1235 &>/dev/null; then
+ cleanup_ns "${nh_vrf_ns}"
+ return
+ fi
+
+ cleanup_ns "${nh_vrf_ns}"
+ NH_VRF_FLAG="nh-vrf"
+}
+
if [ "$(id -u)" -ne 0 ];then
echo "SKIP: Need root privileges"
exit $ksft_skip
@@ -487,6 +510,12 @@ fi
cleanup &>/dev/null
+check_nh_vrf_support
+if [ -z ${NH_VRF_FLAG} ]; then
+ echo "nh-vrf support: no"
+else
+ echo "nh-vrf support: yes"
+fi
setup
router_tests
--
2.52.0
^ permalink raw reply related
* [RFC PATCH iproute2-next] seg6: add support of 'nh-vrf' flag
From: Nicolas Dichtel @ 2026-03-27 14:07 UTC (permalink / raw)
To: David Ahern, Stephen Hemminger, David Lebrun, Andrea Mayer,
Paolo Lungaroni
Cc: netdev, Nicolas Dichtel
This new flag enables to have x-vrf routes.
Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
---
A formal submission will be done once the kernel patch is merged.
include/uapi/linux/seg6_iptunnel.h | 2 ++
ip/iproute_lwtunnel.c | 10 ++++++++++
man/man8/ip-route.8.in | 9 ++++++++-
3 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/include/uapi/linux/seg6_iptunnel.h b/include/uapi/linux/seg6_iptunnel.h
index e1929d21c0b6..81e5c699d157 100644
--- a/include/uapi/linux/seg6_iptunnel.h
+++ b/include/uapi/linux/seg6_iptunnel.h
@@ -20,6 +20,8 @@
enum {
SEG6_IPTUNNEL_UNSPEC,
SEG6_IPTUNNEL_SRH,
+ SEG6_IPTUNNEL_SRC, /* struct in6_addr */
+ SEG6_IPTUNNEL_NH_VRF,
__SEG6_IPTUNNEL_MAX,
};
#define SEG6_IPTUNNEL_MAX (__SEG6_IPTUNNEL_MAX - 1)
diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c
index a7885dba28c1..e6a61c0d5046 100644
--- a/ip/iproute_lwtunnel.c
+++ b/ip/iproute_lwtunnel.c
@@ -267,6 +267,9 @@ static void print_encap_seg6(FILE *fp, struct rtattr *encap)
"mode %s ", format_seg6mode_type(tuninfo->mode));
print_srh(fp, tuninfo->srh);
+
+ if (tb[SEG6_IPTUNNEL_NH_VRF])
+ print_string(PRINT_ANY, NULL, "%s ", "nh-vrf");
}
static void print_rpl_srh(FILE *fp, struct ipv6_rpl_sr_hdr *srh)
@@ -953,6 +956,7 @@ static int parse_encap_seg6(struct rtattr *rta, size_t len, int *argcp,
struct ipv6_sr_hdr *srh;
char **argv = *argvp;
char segbuf[1024] = "";
+ bool nh_vrf = false;
int argc = *argcp;
int encap = -1;
__u32 hmac = 0;
@@ -981,6 +985,8 @@ static int parse_encap_seg6(struct rtattr *rta, size_t len, int *argcp,
if (hmac_ok++)
duparg2("hmac", *argv);
get_u32(&hmac, *argv, 0);
+ } else if (strcmp(*argv, "nh-vrf") == 0) {
+ nh_vrf = true;
} else {
break;
}
@@ -1005,6 +1011,10 @@ static int parse_encap_seg6(struct rtattr *rta, size_t len, int *argcp,
sizeof(*tuninfo) + srhlen))
goto out;
+ if (nh_vrf &&
+ rta_addattr_l(rta, len, SEG6_IPTUNNEL_NH_VRF, NULL, 0))
+ goto out;
+
*argcp = argc + 1;
*argvp = argv - 1;
ret = 0;
diff --git a/man/man8/ip-route.8.in b/man/man8/ip-route.8.in
index aafa6d98a4c2..37dcaae49195 100644
--- a/man/man8/ip-route.8.in
+++ b/man/man8/ip-route.8.in
@@ -240,7 +240,9 @@ throw " | " unreachable " | " prohibit " | " blackhole " | " nat " ]"
.IR ENCAP_SEG6 " := "
.B seg6
.BR mode " [ "
-.BR encap " | " encap.red " | " inline " | " l2encap " | " l2encap.red " ] "
+.BR encap " | " encap.red " | " inline " | " l2encap " | " l2encap.red " ] ["
+.B nh-vrf
+.IR "]"
.B segs
.IR SEGMENTS " [ "
.B hmac
@@ -893,6 +895,11 @@ applying the reduced segment list. When there is only one segment
and the HMAC is not present, the SRH is omitted.
.sp
+.B nh-vrf
+- use the vrf attached to the next hop device to route the encapsulated
+packet.
+.sp
+
.I SEGMENTS
- List of comma-separated IPv6 addresses
.sp
--
2.52.0
^ permalink raw reply related
* Re: [PATCH net 1/3] net/sched: cls_fw: fix NULL pointer dereference on shared blocks
From: Jamal Hadi Salim @ 2026-03-27 14:11 UTC (permalink / raw)
To: Xiang Mei; +Cc: netdev, jiri, davem, edumazet, kuba, horms, shuah, bestswngs
In-Reply-To: <20260327061300.3066844-1-xmei5@asu.edu>
On Fri, Mar 27, 2026 at 2:13 AM Xiang Mei <xmei5@asu.edu> wrote:
>
> The old-method path in fw_classify() calls tcf_block_q() and
> dereferences q->handle. Shared blocks leave block->q NULL, causing a
> NULL deref when an empty cls_fw filter is attached to a shared block
> and a packet with a nonzero major skb mark is classified.
>
> Check tcf_block_shared() before accessing block->q and return -1 (no
> match) for shared blocks, consistent with cls_u32's tc_u_common_ptr().
>
> The fixed null-ptr-deref calling stack:
> KASAN: null-ptr-deref in range [0x0000000000000038-0x000000000000003f]
> RIP: 0010:fw_classify (net/sched/cls_fw.c:81)
> Call Trace:
> tcf_classify (./include/net/tc_wrapper.h:197 net/sched/cls_api.c:1764 net/sched/cls_api.c:1860)
> tc_run (net/core/dev.c:4401)
> __dev_queue_xmit (net/core/dev.c:4535 net/core/dev.c:4790)
>
> Fixes: 1abf272022cf ("net: sched: tcindex, fw, flow: use tcf_block_q helper to get struct Qdisc")
> Reported-by: Weiming Shi <bestswngs@gmail.com>
> Signed-off-by: Xiang Mei <xmei5@asu.edu>
Thanks for also providing the tdc tests. Looks like a bizarre bug—and
in my mind the question is does fw or flow even factor into tc blocks?
Please give me time to review if the approach makes sense - perhaps
this weekend.
cheers,
jamal
> ---
> net/sched/cls_fw.c | 7 ++++++-
> 1 file changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
> index be81c108179d..caf17ab3be87 100644
> --- a/net/sched/cls_fw.c
> +++ b/net/sched/cls_fw.c
> @@ -74,9 +74,14 @@ TC_INDIRECT_SCOPE int fw_classify(struct sk_buff *skb,
> }
> }
> } else {
> - struct Qdisc *q = tcf_block_q(tp->chain->block);
> + struct tcf_block *block = tp->chain->block;
> + struct Qdisc *q;
> +
> + if (tcf_block_shared(block))
> + return -1;
>
> /* Old method: classify the packet using its skb mark. */
> + q = tcf_block_q(block);
> if (id && (TC_H_MAJ(id) == 0 ||
> !(TC_H_MAJ(id ^ q->handle)))) {
> res->classid = id;
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH v3 net 0/8] xsk: tailroom reservation and MTU validation
From: Maciej Fijalkowski @ 2026-03-27 14:20 UTC (permalink / raw)
To: Jakub Kicinski
Cc: netdev, bpf, magnus.karlsson, stfomichev, pabeni, horms,
larysa.zaremba, aleksander.lobakin, bjorn, Ihor Solodrai
In-Reply-To: <20260326202721.37382070@kernel.org>
On Thu, Mar 26, 2026 at 08:27:21PM -0700, Jakub Kicinski wrote:
> On Mon, 23 Mar 2026 20:24:49 +0100 Maciej Fijalkowski wrote:
> > here we fix a long-standing issue regarding multi-buffer scenario in ZC
> > mode - we have not been providing space at the end of the buffer where
> > multi-buffer XDP works on skb_shared_info. This has been brought to our
> > attention via [0].
> >
> > Unaligned mode does not get any specific treatment, it is user's
> > responsibility to properly handle XSK addresses in queues.
> >
> > With adjustments included here in this set against xskxceiver I have
> > been able to pass the full test suite on ice.
>
> Please scream at me if I'm wrong but I _think_ this is somehow
> breaking BPF CI ?? It's the only BPF change we have AFAICT and
> CI has been failing like this all day:
>
> Root directory input is valid!
> Error: The path for one of the files in artifact is not valid: /packets-132-3-net_timestamping__INET4:_bpf_timestamping-net_timestamping_ns.log. Contains the following character: Colon :
>
> Invalid characters include: Double quote ", Colon :, Less than <, Greater than >, Vertical bar |, Asterisk *, Question mark ?, Carriage return \r, Line feed \n
>
> https://github.com/kernel-patches/bpf/actions/runs/23624136123/job/68809559810
That is probably due to this hang:
WATCHDOG: test case ns_xsk_drv/STAT_RX_DROPPED executes for 10 seconds...
Sorry for that! Please toss away v3 and v4 and let me resolve this.
I was too focused on the outcome of HW tests which I have been running via
xskxceiver. I need to see how to satisfy both sides.
Maciej
^ permalink raw reply
* Re: [PATCH net-next v5] net: pppoe: implement GRO/GSO support
From: Qingfang Deng @ 2026-03-27 14:42 UTC (permalink / raw)
To: Xin Long
Cc: Jakub Kicinski, Felix Fietkau, linux-ppp, Andrew Lunn,
David S. Miller, Eric Dumazet, Paolo Abeni, David Ahern,
Simon Horman, netdev, linux-kernel, Richard Gobert
In-Reply-To: <CADvbK_fug3vckLkfufLTtqTEKzywRUw8C_dzfW032_cb6vLpZg@mail.gmail.com>
Hi, Xin Long,
On Fri, Mar 27, 2026 at 9:23 PM Xin Long <lucien.xin@gmail.com> wrote:
>
> On Thu, Mar 26, 2026 at 9:49 PM Qingfang Deng <dqfext@gmail.com> wrote:
> >
> > In my v6 patch, I set phdr->length to 0xFFFF. Is that okay?
> >
> > https://lore.kernel.org/netdev/20260326081127.61229-1-dqfext@gmail.com/
>
> I think you need to check how the PPPoE stack handles this value. If
> it recalculates the actual length using skb->len when phdr->length ==
> 0xFFFF, then this approach might be fine.
>
> Also, how does the PPPoE stack distinguish between 0xFFFF being the
> actual payload length versus it being used as a marker to indicate a
> jumbo packet (similar to BIG TCP)?
In the receive function 'pppoe_rcv()': if a skb's actual payload
length is less than phdr->length, it will be dropped; if greater than
phdr->length, it will be trimmed to match phdr->length. After that,
the phdr is popped and the skb is passed to the generic PPP layer.
There is also a fastpath in Netfilter flowtable for IPv4/IPv6 that
bypasses pppoe_rcv(): it does not check phdr->length at all, and only
relies on the length field in the network header.
In the v2 version of this patch, phdr->length is not updated in
gro_complete(), so an additional check 'skb_is_gso()' is added to the
function to avoid trimming a GRO skb. Then Richard Gobert suggested
that the length field needs to be updated, and if the updated field
matches the actual length, the 'skb_is_gso()' is not necessary, but
only if the length fits in the u16 field.
^ permalink raw reply
* Re: [PATCH v3] selftests: net: broadcast_pmtu: Fix false failure from incorrect ping exit code logic
From: Jakub Kicinski @ 2026-03-27 14:47 UTC (permalink / raw)
To: fffsqian
Cc: davem, edumazet, pabeni, horms, shuah, netdev, linux-kselftest,
linux-kernel, Qingshuang Fu
In-Reply-To: <20260327012705.2389814-1-fffsqian@163.com>
On Fri, 27 Mar 2026 09:27:05 +0800 fffsqian@163.com wrote:
> The broadcast_pmtu.sh test verifies that broadcast route MTU is respected,
> but it uses an incorrect criteria for test success: it relies solely on
> the ping command's exit code, which leads to false failures.
>
> When the kernel correctly blocks oversized broadcast packets due to the
> configured MTU limit (1500), ping fails to transmit any packets and returns
> exit code 1. The original script interprets this as a test failure, even
> though the kernel is properly enforcing the MTU (the core goal of the test).
>
> Fix this by checking ping's output for "0 packets transmitted" or
> "message too long" instead of relying on exit code.
Still failing, are you testing this locally before you repost it?
Does it pass for you?
Please do not post another version until someone asks for it.
^ permalink raw reply
* Re: [PATCH 3/3] net: dsa: microchip: implement KSZ87xx Module 3 low-loss cable errata
From: Bastien Curutchet @ 2026-03-27 14:47 UTC (permalink / raw)
To: Vladimir Oltean, Fidelio Lawson
Cc: Woojung Huh, UNGLinuxDriver, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Marek Vasut, Maxime Chevallier,
netdev, devicetree, linux-kernel, Fidelio Lawson
In-Reply-To: <20260326094211.hdaf4tz7lbjyjznn@skbuf>
Hi Vladimir, Hi Fidelio
On 3/26/26 10:42 AM, Vladimir Oltean wrote:
> On Thu, Mar 26, 2026 at 10:10:23AM +0100, Fidelio Lawson wrote:
>> Implement the "Module 3: Equalizer fix for short cables" erratum from
>> Microchip document DS80000687C for KSZ87xx switches.
>>
>> The issue affects short or low-loss cable links (e.g. CAT5e/CAT6),
>> where the PHY receiver equalizer may amplify high-amplitude signals
>> excessively, resulting in internal distortion and link establishment
>> failures.
>>
>> Depending on the selected workaround (1 or 2), the driver writes a
>> specific value to the indirect PHY register
>> using the 6E/6F/A0 indirect access mechanism.
>>
>> The errata fix is applied during global switch initialization when
>> enabled via device tree.
>>
>> Signed-off-by: Fidelio Lawson <fidelio.lawson@exotec.com>
>> ---
>> drivers/net/dsa/microchip/ksz8.c | 46 ++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 46 insertions(+)
>>
>> diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
>> index 78b42cf50ce2..b6f3a1ce85fc 100644
>> --- a/drivers/net/dsa/microchip/ksz8.c
>> +++ b/drivers/net/dsa/microchip/ksz8.c
>> @@ -1901,6 +1901,41 @@ void ksz8_phylink_mac_link_up(struct phylink_config *config,
>> ksz8_phy_port_link_up(dev, port, duplex, tx_pause, rx_pause);
>> }
>>
>> +static int ksz8_handle_module3_errata(struct ksz_device *dev)
>> +{
>> + int ret = 0;
>
> "ret" does not need to be zero-initialized. It is unconditionally
> overwritten by ksz_write8().
>
> And please sort lines with variable declarations in decreasing line
> length order. Netdev calls this "reverse Christmas tree" ordering and is
> the preferred coding style.
>
>> + const u16 *regs = dev->info->regs;
>> + u16 indir_reg = 0x0000;
>> + u8 indir_val = 0x00;
>> +
>> + switch (dev->low_loss_wa_mode) {
>> + case KSZ_LOW_LOSS_WA_1:
>> + indir_reg = 0x3C;
>> + indir_val = 0x15;
>> + break;
>> + case KSZ_LOW_LOSS_WA_2:
>> + indir_reg = 0x4C;
>> + indir_val = 0x40;
>
> Do the 3c and 4c registers have any associated documentation? Do we know
> what they are or what they do? We should have some macros for them,
> instead of magic numbers.
>
>> + break;
>> + default:
>> + break;
>
> Is it expected that in the default case (no workaround), the code flow
> writes indir_val = 0x00 to indir_reg = 0x0000? Or would it be better to
> just exit early without making any change?
>
>> + }
>> +
>> + mutex_lock(&dev->alu_mutex);
>> +
>> + ret = ksz_write8(dev, regs[REG_IND_CTRL_0], 0xA0);
>> +
>> + if (!ret)
>> + ret = ksz_write8(dev, 0x6F, indir_reg);
>> +
>> + if (!ret)
>> + ret = ksz_write8(dev, regs[REG_IND_BYTE], indir_val);
>
> Is this sequence better suited for ksz8_ind_write8()? Perhaps wrapped in
> another layer similar to ksz8_pme_write8(), once we know what the magic
> numbers represent in terms of indirect table?
>
>> +
>> + mutex_unlock(&dev->alu_mutex);
>> +
>> + return ret;
>> +}
>> +
>> static int ksz8_handle_global_errata(struct dsa_switch *ds)
>> {
>> struct ksz_device *dev = ds->priv;
>> @@ -1915,6 +1950,17 @@ static int ksz8_handle_global_errata(struct dsa_switch *ds)
>> if (dev->info->ksz87xx_eee_link_erratum)
>> ret = ksz8_ind_write8(dev, TABLE_EEE, REG_IND_EEE_GLOB2_HI, 0);
>>
>> + /* KSZ87xx Errata DS80000687C.
>> + * Module 3: Equalizer fix for short cables
>> + * The receiver of the embedded PHYs is tuned by default
>> + * to support long cable length applications.
>> + * Because of this, the equalizer in the PHY may amplify
>> + * high amplitude receiver signals to the point that
>> + * the signal is distorted internally
>> + */
>> + if (!ret && dev->low_loss_wa_enable && ksz_is_ksz87xx(dev))
>> + ret = ksz8_handle_module3_errata(dev);
>> +
>> return ret;
>> }
>>
>>
>> --
>> 2.53.0
>>
>
> FYI, the driver is in a restructuring process. The ksz88xx_switch_ops
> will be split out of the common ksz_switch_ops. This will conflict with
> your series, so you should rebase on top of that much larger set.
> You can coordinate with Bastien Curutchet to see what is the status:
> https://lore.kernel.org/netdev/20260313153849.qkfzv5c2u6fepjku@skbuf
Indeed, I'm currently polishing a first iteration for it. I'll send it
ASAP.
Best regards,
Bastien
^ permalink raw reply
* Re: [PATCH net-next] net: add sysctl to toggle napi_consume_skb() alien skb defer
From: Kuniyuki Iwashima @ 2026-03-27 14:47 UTC (permalink / raw)
To: kerneljasonxing; +Cc: davem, edumazet, horms, kuba, netdev, pabeni
In-Reply-To: <CAL+tcoDZx9j_HuzTge3u11_kz8FmEVfw_DMzMRzRd9hv+gjTdA@mail.gmail.com>
From: Jason Xing <kerneljasonxing@gmail.com>
Date: Fri, 27 Mar 2026 18:37:19 +0800
> On Fri, Mar 27, 2026 at 12:21 AM Jason Xing <kerneljasonxing@gmail.com> wrote:
> ...
> > > Seems a copy/paste of proc_do_static_key() ?
> >
> > Thanks, Eric. Much simpler right now:
> > diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
> > index b508618bfc12..a6a1b2c3f8e1 100644
> > --- a/net/core/sysctl_net_core.c
> > +++ b/net/core/sysctl_net_core.c
> > @@ -676,6 +676,16 @@ static struct ctl_table net_core_table[] = {
> > .proc_handler = proc_do_skb_defer_max,
> > .extra1 = SYSCTL_ZERO,
> > },
> > + {
> > + .procname = "napi_consume_skb_defer",
> > + .data = &napi_consume_skb_defer_key.key,
> > + .maxlen = sizeof(napi_consume_skb_defer_key),
> > + .mode = 0644,
> > + .proc_handler = proc_do_static_key,
> > + },
> > };
>
> Eric, when I'm investigating the static key feature, I wonder if we
> can replace some sysctl knob that only carries either 1 or 2 with a
> corresponding static key? The merits are 1) we can minimize the size
> of net.core.xxx or net_hotdata which helps put the structure into one
> cache line, 2) in hot paths, the static key can be more efficient.
>
> Here is the list that I've found so far:
> 1. sysctl_bypass_prot_mem
> 2. sysctl_tstamp_allow_data
> 3. net_hotdata.tstamp_prequeue
> 4. sysctl_tcp_early_demux
> 5. sysctl_ip_early_demux
> 6. sysctl_tcp_l3mdev_accept
> 7. sysctl_tcp_moderate_rcvbuf
> 8. sysctl_tcp_nometrics_save
> 9. sysctl_tcp_no_ssthresh_metrics_save
> 10. sysctl_tcp_dsack
> 11. sysctl_tcp_abort_on_overflow
> 12. sysctl_tcp_slow_start_after_idle
> 13. sysctl_tcp_timestamps
> ...(some other tcp sysctls)...
>
> Some of them can be accessed in multiple places, which means they can
> be hot. Maybe we can try some relatively hot knobs first, like
> sysctl_tcp_timestamps, sysctl_tcp_early_demux...? Do you think it's
> feasible?
Note that a static key is only avaiable for global knobs
while most knobs you mentioned above are per-netns.
^ permalink raw reply
* Re: [PATCH 3/3] net: dsa: microchip: implement KSZ87xx Module 3 low-loss cable errata
From: Marek Vasut @ 2026-03-27 14:55 UTC (permalink / raw)
To: Bastien Curutchet, Vladimir Oltean, Fidelio Lawson
Cc: Woojung Huh, UNGLinuxDriver, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Marek Vasut, Maxime Chevallier,
netdev, devicetree, linux-kernel, Fidelio Lawson
In-Reply-To: <2848158c-a807-4d26-be87-248d4107c5b4@bootlin.com>
On 3/27/26 3:47 PM, Bastien Curutchet wrote:
Hello Bastien,
>> FYI, the driver is in a restructuring process. The ksz88xx_switch_ops
>> will be split out of the common ksz_switch_ops. This will conflict with
>> your series, so you should rebase on top of that much larger set.
>> You can coordinate with Bastien Curutchet to see what is the status:
>> https://lore.kernel.org/netdev/20260313153849.qkfzv5c2u6fepjku@skbuf
>
> Indeed, I'm currently polishing a first iteration for it. I'll send it
> ASAP.
I would argue that this low-loss cable errata is a bugfix, so maybe that
should have a priority over restructuring ?
^ permalink raw reply
* Re: [PATCH net-next v2 3/3] vrf: Remove unnecessary RCU protection around dst entries
From: David Ahern @ 2026-03-27 14:56 UTC (permalink / raw)
To: Ido Schimmel, netdev
Cc: davem, kuba, pabeni, edumazet, andrew+netdev, petrm, lirongqing
In-Reply-To: <20260326203233.1128554-4-idosch@nvidia.com>
On 3/26/26 2:32 PM, Ido Schimmel wrote:
> During initialization of a VRF device, the VRF driver creates two dst
> entries (for IPv4 and IPv6). They are attached to locally generated
> packets that are transmitted out of the VRF ports (via the
> l3mdev_l3_out() hook). Their purpose is to redirect packets towards the
> VRF device instead of having the packets egress directly out of the VRF
> ports. This is useful, for example, when a queuing discipline is
> configured on the VRF device.
>
> In order to avoid a NULL pointer dereference, commit b0e95ccdd775 ("net:
> vrf: protect changes to private data with rcu") made the pointers to the
> dst entries RCU protected. As far as I can tell, this was needed because
> back then the dst entries were released (and the pointers reset to NULL)
> before removing the VRF ports.
>
> Later on, commit f630c38ef0d7 ("vrf: fix bug_on triggered by rx when
> destroying a vrf") moved the removal of the VRF ports to the VRF
> device's dellink() callback. As such, the tear down sequence of a VRF
> device looks as follows:
>
> 1. VRF ports are removed.
> 2. VRF device is unregistered.
> a. Device is closed.
> b. An RCU grace period passes.
> c. ndo_uninit() is called.
> i. dst entries are released.
>
> Given the above, the Tx path will always see the same fully initialized
> dst entries and will never race with the ndo_uninit() callback.
>
> Therefore, there is no need to make the pointers to the dst entries RCU
> protected. Remove it as well as the unnecessary NULL checks in the Tx
> path.
>
> Signed-off-by: Ido Schimmel <idosch@nvidia.com>
> ---
> drivers/net/vrf.c | 56 ++++++++++-------------------------------------
> 1 file changed, 12 insertions(+), 44 deletions(-)
>
Reviewed-by: David Ahern <dsahern@kernel.org>
^ permalink raw reply
* Re: [PATCH net-next v2 00/15] net: stmmac: qcom-ethqos: more cleanups
From: Mohd Ayaan Anwar @ 2026-03-27 15:20 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Andrew Lunn, Alexandre Torgue, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, linux-arm-kernel, linux-arm-msm,
linux-stm32, netdev, Paolo Abeni
In-Reply-To: <acZDEg9wdjhBTHlL@shell.armlinux.org.uk>
Hi Russell,
On Fri, Mar 27, 2026 at 08:42:58AM +0000, Russell King (Oracle) wrote:
> Further cleanups to qcom-ethqos, mainly concentrating on the RGMII
> code, making it clearer what the differences are for each speed, thus
> making the code more readable.
>
> I'm still not really happy with this. The speed specific configuration
> remains split between ethqos_fix_mac_speed_rgmii() and
> ethqos_rgmii_macro_init(), where the latter is only ever called from
> the former. So, I think further work is needed here - maybe it needs
> restructuring into the various componenet parts of the RGMII block?
>
> v2:
> - patch 2: fix typo in commit message
> - patch 3: fix ethqos_fix_mac_speed() comment
>
> .../ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c | 220 ++++++++-------------
> 1 file changed, 87 insertions(+), 133 deletions(-)
>
No issues found at 100M and 1G on the QCS615 Ride board with the KSZ9031
RGMII PHY. As noted earlier, Ethernet support for this board is not yet
upstream, but I have some local changes to make it work.
10M could not be tested due to limitations of the link partner. But with
100M working fine, I am fairly certain that this series will not
introduce any new issues at 10M.
Please feel free to add my:
Tested-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
Ayaan
^ permalink raw reply
* RE: [PATCH net-next 0/5] dpll: zl3073x: add ref-sync pair support
From: Prathosh.Satish @ 2026-03-27 15:28 UTC (permalink / raw)
To: ivecera, netdev
Cc: arkadiusz.kubalewski, jiri, mschmidt, poros, horms,
vadim.fedorenko, linux-kernel, conor+dt, krzk+dt, robh,
devicetree, pvaanane
In-Reply-To: <20260319174826.7623-1-ivecera@redhat.com>
Reviewed-by: prathosh.satish@microchip.com
-----Original Message-----
From: Ivan Vecera <ivecera@redhat.com>
Sent: Thursday, March 19, 2026 5:48 PM
To: netdev@vger.kernel.org
Cc: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>; Jiri Pirko <jiri@resnulli.us>; Michal Schmidt <mschmidt@redhat.com>; Petr Oros <poros@redhat.com>; Prathosh Satish - M66066 <Prathosh.Satish@microchip.com>; Simon Horman <horms@kernel.org>; Vadim Fedorenko <vadim.fedorenko@linux.dev>; linux-kernel@vger.kernel.org; Conor Dooley <conor+dt@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Rob Herring <robh@kernel.org>; devicetree@vger.kernel.org; Pasi Vaananen <pvaanane@redhat.com>
Subject: [PATCH net-next 0/5] dpll: zl3073x: add ref-sync pair support
EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
This series adds Reference-Sync pair support to the ZL3073x DPLL driver.
A Ref-Sync pair consists of a clock reference and a low-frequency sync signal (e.g. 1 PPS) where the DPLL locks to the clock reference but phase-aligns to the sync reference.
Patches 1-3 are preparatory cleanups and helper additions:
- Clean up esync get/set callbacks with early returns and use the
zl3073x_out_is_ndiv() helper
- Convert open-coded clear-and-set bitfield patterns to FIELD_MODIFY()
- Add ref sync control and output clock type accessor helpers
Patch 4 adds the 'ref-sync-sources' phandle-array property to the dpll-pin device tree binding schema and updates the ZL3073x binding examples.
Patch 5 implements the driver support:
- ref_sync_get/set callbacks with frequency validation
- Automatic sync source exclusion from reference selection
- Device tree based ref-sync pair registration
Tested and verified on Microchip EDS2 (pcb8385) development board.
Ivan Vecera (5):
dpll: zl3073x: clean up esync get/set and use zl3073x_out_is_ndiv()
dpll: zl3073x: use FIELD_MODIFY() for clear-and-set patterns
dpll: zl3073x: add ref sync and output clock type helpers
dt-bindings: dpll: add ref-sync-sources property
dpll: zl3073x: add ref-sync pair support
.../devicetree/bindings/dpll/dpll-pin.yaml | 11 +
.../bindings/dpll/microchip,zl30731.yaml | 30 +-
drivers/dpll/zl3073x/chan.h | 17 +-
drivers/dpll/zl3073x/core.c | 3 +-
drivers/dpll/zl3073x/dpll.c | 295 ++++++++++++++----
drivers/dpll/zl3073x/flash.c | 3 +-
drivers/dpll/zl3073x/out.h | 22 ++
drivers/dpll/zl3073x/ref.h | 46 +++
drivers/dpll/zl3073x/regs.h | 2 +
9 files changed, 348 insertions(+), 81 deletions(-)
--
2.52.0
^ permalink raw reply
* Re: [PATCH net-next] net: add sysctl to toggle napi_consume_skb() alien skb defer
From: Jason Xing @ 2026-03-27 15:29 UTC (permalink / raw)
To: Kuniyuki Iwashima; +Cc: davem, edumazet, horms, kuba, netdev, pabeni
In-Reply-To: <20260327144723.3344613-1-kuniyu@google.com>
On Fri, Mar 27, 2026 at 10:47 PM Kuniyuki Iwashima <kuniyu@google.com> wrote:
>
> From: Jason Xing <kerneljasonxing@gmail.com>
> Date: Fri, 27 Mar 2026 18:37:19 +0800
> > On Fri, Mar 27, 2026 at 12:21 AM Jason Xing <kerneljasonxing@gmail.com> wrote:
> > ...
> > > > Seems a copy/paste of proc_do_static_key() ?
> > >
> > > Thanks, Eric. Much simpler right now:
> > > diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
> > > index b508618bfc12..a6a1b2c3f8e1 100644
> > > --- a/net/core/sysctl_net_core.c
> > > +++ b/net/core/sysctl_net_core.c
> > > @@ -676,6 +676,16 @@ static struct ctl_table net_core_table[] = {
> > > .proc_handler = proc_do_skb_defer_max,
> > > .extra1 = SYSCTL_ZERO,
> > > },
> > > + {
> > > + .procname = "napi_consume_skb_defer",
> > > + .data = &napi_consume_skb_defer_key.key,
> > > + .maxlen = sizeof(napi_consume_skb_defer_key),
> > > + .mode = 0644,
> > > + .proc_handler = proc_do_static_key,
> > > + },
> > > };
> >
> > Eric, when I'm investigating the static key feature, I wonder if we
> > can replace some sysctl knob that only carries either 1 or 2 with a
> > corresponding static key? The merits are 1) we can minimize the size
> > of net.core.xxx or net_hotdata which helps put the structure into one
> > cache line, 2) in hot paths, the static key can be more efficient.
> >
> > Here is the list that I've found so far:
> > 1. sysctl_bypass_prot_mem
> > 2. sysctl_tstamp_allow_data
> > 3. net_hotdata.tstamp_prequeue
> > 4. sysctl_tcp_early_demux
> > 5. sysctl_ip_early_demux
> > 6. sysctl_tcp_l3mdev_accept
> > 7. sysctl_tcp_moderate_rcvbuf
> > 8. sysctl_tcp_nometrics_save
> > 9. sysctl_tcp_no_ssthresh_metrics_save
> > 10. sysctl_tcp_dsack
> > 11. sysctl_tcp_abort_on_overflow
> > 12. sysctl_tcp_slow_start_after_idle
> > 13. sysctl_tcp_timestamps
> > ...(some other tcp sysctls)...
> >
> > Some of them can be accessed in multiple places, which means they can
> > be hot. Maybe we can try some relatively hot knobs first, like
> > sysctl_tcp_timestamps, sysctl_tcp_early_demux...? Do you think it's
> > feasible?
>
> Note that a static key is only avaiable for global knobs
> while most knobs you mentioned above are per-netns.
Thanks for the reminder. Let me rethink it then.
Thanks,
Jason
^ permalink raw reply
* RE: [PATCH net-next 4/5] dt-bindings: dpll: add ref-sync-sources property
From: Prathosh.Satish @ 2026-03-27 15:33 UTC (permalink / raw)
To: ivecera, netdev
Cc: arkadiusz.kubalewski, jiri, mschmidt, poros, horms,
vadim.fedorenko, linux-kernel, conor+dt, krzk+dt, robh,
devicetree, pvaanane
In-Reply-To: <20260319174826.7623-5-ivecera@redhat.com>
Reviewed-by: prathosh.satish@microchip.com
-----Original Message-----
From: Ivan Vecera <ivecera@redhat.com>
Sent: Thursday, March 19, 2026 5:48 PM
To: netdev@vger.kernel.org
Cc: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>; Jiri Pirko <jiri@resnulli.us>; Michal Schmidt <mschmidt@redhat.com>; Petr Oros <poros@redhat.com>; Prathosh Satish - M66066 <Prathosh.Satish@microchip.com>; Simon Horman <horms@kernel.org>; Vadim Fedorenko <vadim.fedorenko@linux.dev>; linux-kernel@vger.kernel.org; Conor Dooley <conor+dt@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Rob Herring <robh@kernel.org>; devicetree@vger.kernel.org; Pasi Vaananen <pvaanane@redhat.com>
Subject: [PATCH net-next 4/5] dt-bindings: dpll: add ref-sync-sources property
EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
Add ref-sync-sources phandle-array property to the dpll-pin schema allowing board designers to declare which input pins can serve as sync sources in a Reference-Sync pair. A Ref-Sync pair consists of a clock reference and a low-frequency sync signal where the DPLL locks to the clock but phase-aligns to the sync reference.
Update both examples in the Microchip ZL3073x binding to demonstrate the new property with a 1 PPS sync source paired to a clock source.
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
.../devicetree/bindings/dpll/dpll-pin.yaml | 11 +++++++
.../bindings/dpll/microchip,zl30731.yaml | 30 ++++++++++++++-----
2 files changed, 34 insertions(+), 7 deletions(-)
diff --git a/Documentation/devicetree/bindings/dpll/dpll-pin.yaml b/Documentation/devicetree/bindings/dpll/dpll-pin.yaml
index 51db93b77306f..7084f102e274c 100644
--- a/Documentation/devicetree/bindings/dpll/dpll-pin.yaml
+++ b/Documentation/devicetree/bindings/dpll/dpll-pin.yaml
@@ -36,6 +36,17 @@ properties:
description: String exposed as the pin board label
$ref: /schemas/types.yaml#/definitions/string
+ ref-sync-sources:
+ description: |
+ List of phandles to input pins that can serve as the sync source
+ in a Reference-Sync pair with this pin acting as the clock source.
+ A Ref-Sync pair consists of a clock reference and a low-frequency
+ sync signal. The DPLL locks to the clock reference but
+ phase-aligns to the sync reference.
+ Only valid for input pins. Each referenced pin must be a
+ different input pin on the same device.
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+
supported-frequencies-hz:
description: List of supported frequencies for this pin, expressed in Hz.
diff --git a/Documentation/devicetree/bindings/dpll/microchip,zl30731.yaml b/Documentation/devicetree/bindings/dpll/microchip,zl30731.yaml
index 17747f754b845..fa5a8f8e390cd 100644
--- a/Documentation/devicetree/bindings/dpll/microchip,zl30731.yaml
+++ b/Documentation/devicetree/bindings/dpll/microchip,zl30731.yaml
@@ -52,11 +52,19 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
- pin@0 { /* REF0P */
+ sync0: pin@0 { /* REF0P - 1 PPS sync source */
reg = <0>;
connection-type = "ext";
- label = "Input 0";
- supported-frequencies-hz = /bits/ 64 <1 1000>;
+ label = "SMA1";
+ supported-frequencies-hz = /bits/ 64 <1>;
+ };
+
+ pin@1 { /* REF0N - clock source, can pair with sync0 */
+ reg = <1>;
+ connection-type = "ext";
+ label = "SMA2";
+ supported-frequencies-hz = /bits/ 64 <10000 10000000>;
+ ref-sync-sources = <&sync0>;
};
};
@@ -90,11 +98,19 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
- pin@0 { /* REF0P */
+ sync1: pin@0 { /* REF0P - 1 PPS sync source */
reg = <0>;
- connection-type = "ext";
- label = "Input 0";
- supported-frequencies-hz = /bits/ 64 <1 1000>;
+ connection-type = "gnss";
+ label = "GNSS_1PPS_IN";
+ supported-frequencies-hz = /bits/ 64 <1>;
+ };
+
+ pin@1 { /* REF0N - clock source */
+ reg = <1>;
+ connection-type = "gnss";
+ label = "GNSS_10M_IN";
+ supported-frequencies-hz = /bits/ 64 <10000000>;
+ ref-sync-sources = <&sync1>;
};
};
--
2.52.0
^ permalink raw reply related
* [PATCH v2 net-next] net: add sysctl to toggle napi_consume_skb() alien skb defer
From: Jason Xing @ 2026-03-27 15:33 UTC (permalink / raw)
To: davem, edumazet, kuba, pabeni, horms, kuniyu, stfomichev
Cc: netdev, Jason Xing
Commit e20dfbad8aab ("net: fix napi_consume_skb() with alien skbs")
defers freeing of alien SKBs (alloc_cpu != current cpu) via
skb_attempt_defer_free() on the TX completion path to reduce cross-NUMA
SLUB spinlock contention to improve multi-queue UDP workloads.
However, this unconditionally impacts the napi_skb_cache fast recycle
path for single-flow / few-flow workloads (e.g. AF_XDP benchmarks[1]):
when the TX completion NAPI CPU differs from the SKB allocation CPU,
SKBs are deferred instead of being returned to the local napi_skb_cache,
forcing RX allocations back to the slow slab path.
The existing net.core.skb_defer_max=0 could disable this, but it is a
global switch that also disables the defer mechanism in TCP/UDP/MPTCP
recvmsg paths, losing its positive SLUB locality benefits there. AF_XDP
can co-exist with other protocols. That's the reason why I gave up
reusing skb_defer_disable_key. Besides, if the defer path is disabled,
that means TCP/UDP/MPTCP in process path will trigger directly freeing
skb with enabling/disabling bottom half(in kfree_skb_napi_cache())
which could affect others. So my thinking is not to touch this path.
Add a dedicated sysctl net.core.napi_consume_skb_defer backed by a
static key to selectively control the alien skb defer feature. Let
users decide which is the best fit for their own requirements.
This patch also avoids touching local_bh* pair(in kfree_skb_napi_cache())
to minimize the overhead.
[1]: taskset -c 0 ./xdpsock -i enp2s0f1 -q 1 -t -S -s 64
1) sysctl -w net.core.napi_consume_skb_defer=1 (as default)
sock0@enp2s0f1:1 txonly xdp-skb
pps pkts 1.00
rx 0 0
tx 1,851,950 20,397,952
2)sysctl -w net.core.napi_consume_skb_defer=0
sock0@enp2s0f1:1 txonly xdp-skb
pps pkts 1.00
rx 0 0
tx 1,985,067 25,530,432
For AF_XDP scenario, it turns out to be around 6.6% improvement.
Signed-off-by: Jason Xing <kerneljasonxing@gmail.com>
---
V2
Link: https://lore.kernel.org/all/20260326144249.97213-1-kerneljasonxing@gmail.com/
1. reuse proc_do_static_key() (Eric)
2. add doc (Stan)
---
Documentation/admin-guide/sysctl/net.rst | 13 +++++++++++++
net/core/net-sysfs.h | 1 +
net/core/skbuff.c | 5 ++++-
net/core/sysctl_net_core.c | 7 +++++++
4 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/Documentation/admin-guide/sysctl/net.rst b/Documentation/admin-guide/sysctl/net.rst
index 0724a793798f..42e06f93306f 100644
--- a/Documentation/admin-guide/sysctl/net.rst
+++ b/Documentation/admin-guide/sysctl/net.rst
@@ -368,6 +368,19 @@ by the cpu which allocated them.
Default: 128
+napi_consume_skb_defer
+----------------------
+When set to 1 (default), napi_consume_skb() defers freeing SKBs whose
+allocation CPU differs from the current CPU via skb_attempt_defer_free().
+This reduces cross-NUMA SLUB spinlock contention for multi-queue workloads.
+
+Setting this to 0 disables the defer path in napi_consume_skb() only,
+allowing SKBs to be returned to the local napi_skb_cache immediately.
+This can benefit single-flow or few-flow workloads (e.g. AF_XDP TX)
+where the defer detour hurts the fast recycle path.
+
+Default: 1
+
optmem_max
----------
diff --git a/net/core/net-sysfs.h b/net/core/net-sysfs.h
index 38e2e3ffd0bd..a026f757867e 100644
--- a/net/core/net-sysfs.h
+++ b/net/core/net-sysfs.h
@@ -14,4 +14,5 @@ int netdev_change_owner(struct net_device *, const struct net *net_old,
extern struct mutex rps_default_mask_mutex;
DECLARE_STATIC_KEY_FALSE(skb_defer_disable_key);
+DECLARE_STATIC_KEY_TRUE(napi_consume_skb_defer_key);
#endif
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 3d6978dd0aa8..3db90a9aa61d 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -94,6 +94,7 @@
#include "dev.h"
#include "devmem.h"
+#include "net-sysfs.h"
#include "netmem_priv.h"
#include "sock_destructor.h"
@@ -1519,7 +1520,8 @@ void napi_consume_skb(struct sk_buff *skb, int budget)
DEBUG_NET_WARN_ON_ONCE(!in_softirq());
- if (skb->alloc_cpu != smp_processor_id() && !skb_shared(skb)) {
+ if (static_branch_likely(&napi_consume_skb_defer_key) &&
+ skb->alloc_cpu != smp_processor_id() && !skb_shared(skb)) {
skb_release_head_state(skb);
return skb_attempt_defer_free(skb);
}
@@ -7257,6 +7259,7 @@ static void kfree_skb_napi_cache(struct sk_buff *skb)
}
DEFINE_STATIC_KEY_FALSE(skb_defer_disable_key);
+DEFINE_STATIC_KEY_TRUE(napi_consume_skb_defer_key);
/**
* skb_attempt_defer_free - queue skb for remote freeing
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index b508618bfc12..e85ce10afa1f 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -676,6 +676,13 @@ static struct ctl_table net_core_table[] = {
.proc_handler = proc_do_skb_defer_max,
.extra1 = SYSCTL_ZERO,
},
+ {
+ .procname = "napi_consume_skb_defer",
+ .data = &napi_consume_skb_defer_key.key,
+ .maxlen = sizeof(napi_consume_skb_defer_key),
+ .mode = 0644,
+ .proc_handler = proc_do_static_key,
+ },
};
static struct ctl_table netns_core_table[] = {
--
2.41.3
^ permalink raw reply related
* RE: [PATCH net-next 3/5] dpll: zl3073x: add ref sync and output clock type helpers
From: Prathosh.Satish @ 2026-03-27 15:35 UTC (permalink / raw)
To: ivecera, netdev
Cc: arkadiusz.kubalewski, jiri, mschmidt, poros, horms,
vadim.fedorenko, linux-kernel, conor+dt, krzk+dt, robh,
devicetree, pvaanane
In-Reply-To: <20260319174826.7623-4-ivecera@redhat.com>
Reviewed-by: prathosh.satish@microchip.com
-----Original Message-----
From: Ivan Vecera <ivecera@redhat.com>
Sent: Thursday, March 19, 2026 5:48 PM
To: netdev@vger.kernel.org
Cc: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>; Jiri Pirko <jiri@resnulli.us>; Michal Schmidt <mschmidt@redhat.com>; Petr Oros <poros@redhat.com>; Prathosh Satish - M66066 <Prathosh.Satish@microchip.com>; Simon Horman <horms@kernel.org>; Vadim Fedorenko <vadim.fedorenko@linux.dev>; linux-kernel@vger.kernel.org; Conor Dooley <conor+dt@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Rob Herring <robh@kernel.org>; devicetree@vger.kernel.org; Pasi Vaananen <pvaanane@redhat.com>
Subject: [PATCH net-next 3/5] dpll: zl3073x: add ref sync and output clock type helpers
EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
Add ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR and ZL_REF_SYNC_CTRL_PAIR register definitions.
Add inline helpers to get and set the sync control mode and sync pair fields of the reference sync control register:
zl3073x_ref_sync_mode_get/set() - ZL_REF_SYNC_CTRL_MODE field
zl3073x_ref_sync_pair_get/set() - ZL_REF_SYNC_CTRL_PAIR field
Add inline helpers to get and set the clock type field of the output mode register:
zl3073x_out_clock_type_get/set() - ZL_OUTPUT_MODE_CLOCK_TYPE field
Convert existing esync callbacks to use the new helpers.
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
---
drivers/dpll/zl3073x/dpll.c | 24 ++++++++----------- drivers/dpll/zl3073x/out.h | 22 ++++++++++++++++++ drivers/dpll/zl3073x/ref.h | 46 +++++++++++++++++++++++++++++++++++++
drivers/dpll/zl3073x/regs.h | 2 ++
4 files changed, 80 insertions(+), 14 deletions(-)
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index 79ef62d69a32d..276f0a92db0b1 100644
--- a/drivers/dpll/zl3073x/dpll.c
+++ b/drivers/dpll/zl3073x/dpll.c
@@ -137,7 +137,7 @@ zl3073x_dpll_input_pin_esync_get(const struct dpll_pin *dpll_pin,
esync->range = esync_freq_ranges;
esync->range_num = ARRAY_SIZE(esync_freq_ranges);
- switch (FIELD_GET(ZL_REF_SYNC_CTRL_MODE, ref->sync_ctrl)) {
+ switch (zl3073x_ref_sync_mode_get(ref)) {
case ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75:
esync->freq = ref->esync_n_div == ZL_REF_ESYNC_DIV_1HZ ? 1 : 0;
esync->pulse = 25;
@@ -173,8 +173,7 @@ zl3073x_dpll_input_pin_esync_set(const struct dpll_pin *dpll_pin,
else
sync_mode = ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75;
- ref.sync_ctrl &= ~ZL_REF_SYNC_CTRL_MODE;
- ref.sync_ctrl |= FIELD_PREP(ZL_REF_SYNC_CTRL_MODE, sync_mode);
+ zl3073x_ref_sync_mode_set(&ref, sync_mode);
if (freq) {
/* 1 Hz is only supported frequency now */ @@ -578,7 +577,7 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
const struct zl3073x_synth *synth;
const struct zl3073x_out *out;
u32 synth_freq, out_freq;
- u8 clock_type, out_id;
+ u8 out_id;
out_id = zl3073x_output_pin_out_get(pin->id);
out = zl3073x_out_state_get(zldev, out_id); @@ -601,8 +600,7 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
esync->range = esync_freq_ranges;
esync->range_num = ARRAY_SIZE(esync_freq_ranges);
- clock_type = FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, out->mode);
- if (clock_type != ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC) {
+ if (zl3073x_out_clock_type_get(out) !=
+ ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC) {
/* No need to read esync data if it is not enabled */
esync->freq = 0;
esync->pulse = 0;
@@ -635,8 +633,8 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
struct zl3073x_dpll_pin *pin = pin_priv;
const struct zl3073x_synth *synth;
struct zl3073x_out out;
- u8 clock_type, out_id;
u32 synth_freq;
+ u8 out_id;
out_id = zl3073x_output_pin_out_get(pin->id);
out = *zl3073x_out_state_get(zldev, out_id); @@ -648,15 +646,13 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
if (zl3073x_out_is_ndiv(&out))
return -EOPNOTSUPP;
- /* Select clock type */
+ /* Update clock type in output mode */
if (freq)
- clock_type = ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC;
+ zl3073x_out_clock_type_set(&out,
+
+ ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC);
else
- clock_type = ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL;
-
- /* Update clock type in output mode */
- out.mode &= ~ZL_OUTPUT_MODE_CLOCK_TYPE;
- out.mode |= FIELD_PREP(ZL_OUTPUT_MODE_CLOCK_TYPE, clock_type);
+ zl3073x_out_clock_type_set(&out,
+
+ ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL);
/* If esync is being disabled just write mailbox and finish */
if (!freq)
diff --git a/drivers/dpll/zl3073x/out.h b/drivers/dpll/zl3073x/out.h index edf40432bba5f..660889c57bffa 100644
--- a/drivers/dpll/zl3073x/out.h
+++ b/drivers/dpll/zl3073x/out.h
@@ -42,6 +42,28 @@ const struct zl3073x_out *zl3073x_out_state_get(struct zl3073x_dev *zldev, int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
const struct zl3073x_out *out);
+/**
+ * zl3073x_out_clock_type_get - get output clock type
+ * @out: pointer to out state
+ *
+ * Return: clock type of given output (ZL_OUTPUT_MODE_CLOCK_TYPE_*) */
+static inline u8 zl3073x_out_clock_type_get(const struct zl3073x_out
+*out) {
+ return FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, out->mode); }
+
+/**
+ * zl3073x_out_clock_type_set - set output clock type
+ * @out: pointer to out state
+ * @type: clock type (ZL_OUTPUT_MODE_CLOCK_TYPE_*) */ static inline
+void zl3073x_out_clock_type_set(struct zl3073x_out *out, u8 type) {
+ FIELD_MODIFY(ZL_OUTPUT_MODE_CLOCK_TYPE, &out->mode, type); }
+
/**
* zl3073x_out_signal_format_get - get output signal format
* @out: pointer to out state
diff --git a/drivers/dpll/zl3073x/ref.h b/drivers/dpll/zl3073x/ref.h index 06d8d4d97ea26..09fab97a71d7e 100644
--- a/drivers/dpll/zl3073x/ref.h
+++ b/drivers/dpll/zl3073x/ref.h
@@ -106,6 +106,52 @@ zl3073x_ref_freq_set(struct zl3073x_ref *ref, u32 freq)
return 0;
}
+/**
+ * zl3073x_ref_sync_mode_get - get sync control mode
+ * @ref: pointer to ref state
+ *
+ * Return: sync control mode (ZL_REF_SYNC_CTRL_MODE_*) */ static
+inline u8 zl3073x_ref_sync_mode_get(const struct zl3073x_ref *ref) {
+ return FIELD_GET(ZL_REF_SYNC_CTRL_MODE, ref->sync_ctrl); }
+
+/**
+ * zl3073x_ref_sync_mode_set - set sync control mode
+ * @ref: pointer to ref state
+ * @mode: sync control mode (ZL_REF_SYNC_CTRL_MODE_*) */ static inline
+void zl3073x_ref_sync_mode_set(struct zl3073x_ref *ref, u8 mode) {
+ FIELD_MODIFY(ZL_REF_SYNC_CTRL_MODE, &ref->sync_ctrl, mode); }
+
+/**
+ * zl3073x_ref_sync_pair_get - get sync pair reference index
+ * @ref: pointer to ref state
+ *
+ * Return: paired reference index
+ */
+static inline u8
+zl3073x_ref_sync_pair_get(const struct zl3073x_ref *ref) {
+ return FIELD_GET(ZL_REF_SYNC_CTRL_PAIR, ref->sync_ctrl); }
+
+/**
+ * zl3073x_ref_sync_pair_set - set sync pair reference index
+ * @ref: pointer to ref state
+ * @pair: paired reference index
+ */
+static inline void
+zl3073x_ref_sync_pair_set(struct zl3073x_ref *ref, u8 pair) {
+ FIELD_MODIFY(ZL_REF_SYNC_CTRL_PAIR, &ref->sync_ctrl, pair); }
+
/**
* zl3073x_ref_is_diff - check if the given input reference is differential
* @ref: pointer to ref state
diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h index 5ae50cb761a97..d425dc67250fe 100644
--- a/drivers/dpll/zl3073x/regs.h
+++ b/drivers/dpll/zl3073x/regs.h
@@ -213,7 +213,9 @@
#define ZL_REG_REF_SYNC_CTRL ZL_REG(10, 0x2e, 1)
#define ZL_REF_SYNC_CTRL_MODE GENMASK(2, 0)
#define ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF 0
+#define ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR 1
#define ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75 2
+#define ZL_REF_SYNC_CTRL_PAIR GENMASK(7, 4)
#define ZL_REG_REF_ESYNC_DIV ZL_REG(10, 0x30, 4)
#define ZL_REF_ESYNC_DIV_1HZ 0
--
2.52.0
^ permalink raw reply
* Re: [PATCH net-next 1/4] dt-bindings: net: dsa: add MT7628 ESW
From: Joris Vaisvila @ 2026-03-27 15:35 UTC (permalink / raw)
To: Daniel Golle
Cc: netdev, horms, pabeni, kuba, edumazet, davem, olteanv,
Andrew Lunn, devicetree, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
In-Reply-To: <acZqvZfYXR_4sMlT@makrotopia.org>
On Fri, Mar 27, 2026 at 11:32:13AM +0000, Daniel Golle wrote:
> > [...]
> > On the hardware I'm testing on, it works fine with the port set to
> > "internal" or "rgmii". Would it make more sense to set "internal" then?
>
> "internal" then. It's a single-die SoC, the switch sharing the same
> memory space, clocking domain, ... with all the rest of the SoC makes
> it very unlikely that RGMII would be used as an on-die connection
> type. (unlike eg. MT7621 or MT7623A which are using multiple dies in
> the same package, and actually RGMII or TRGMII to connect the
> MDIO-managed switch part to the main SoC, see "MCM" / "multi-chip
> module" in the mt7530 driver...)
Makes sense, will change CPU port to internal in v2.
Thanks,
Joris
^ 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