* Re: [PATCH bpf v2 2/2] selftests/bpf: Add protocol check test for bpf_sk_assign_tcp_reqsk()
From: Kuniyuki Iwashima @ 2026-03-26 17:33 UTC (permalink / raw)
To: Jiayuan Chen
Cc: netdev, 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, bpf,
linux-kernel, linux-kselftest
In-Reply-To: <20260326062657.88446-3-jiayuan.chen@linux.dev>
On Wed, Mar 25, 2026 at 11:27 PM Jiayuan Chen <jiayuan.chen@linux.dev> wrote:
>
> 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
> test_tcp_custom_syncookie_protocol_check:PASS:start tcp_server 0 nsec
> test_tcp_custom_syncookie_protocol_check:PASS:start udp_server 0 nsec
> setup_tc:PASS:qdisc add dev lo clsact 0 nsec
> setup_tc:PASS:filter add dev lo ingress 0 nsec
> test_tcp_custom_syncookie_protocol_check:PASS:udp socket 0 nsec
> test_tcp_custom_syncookie_protocol_check:PASS:sendto udp 0 nsec
> test_tcp_custom_syncookie_protocol_check:PASS:recv udp 0 nsec
> test_tcp_custom_syncookie_protocol_check:PASS:udp_intercepted 0 nsec
> test_tcp_custom_syncookie_protocol_check:PASS:assign_ret 0 nsec
> #471 tcp_custom_syncookie_protocol_check:OK
> Summary: 1/0 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 | 84 ++++++++++++++++++-
> .../bpf/progs/test_tcp_custom_syncookie.c | 79 +++++++++++++++++
> 2 files changed, 159 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..c50b76f70988 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,82 @@ 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.
> + */
> +void test_tcp_custom_syncookie_protocol_check(void)
> +{
> + int tcp_server = -1, udp_server = -1, udp_client = -1;
> + struct test_tcp_custom_syncookie *skel;
> + struct sockaddr_in udp_addr;
> + char buf[32] = "test";
> + int udp_port, ret;
> +
> + if (setup_netns())
> + return;
> +
> + skel = test_tcp_custom_syncookie__open_and_load();
> + if (!ASSERT_OK_PTR(skel, "open_and_load"))
> + return;
> +
> + /* Create a TCP listener so the BPF can find a LISTEN socket */
> + tcp_server = start_server(AF_INET, SOCK_STREAM, "127.0.0.1", 0, 0);
Can you add IPv6 test as well ?
You can reuse test_tcp_custom_syncookie_case[].
> + if (!ASSERT_NEQ(tcp_server, -1, "start tcp_server"))
> + goto destroy_skel;
> +
> + /* Create a UDP server to receive the packet as synchronization */
> + udp_server = start_server(AF_INET, SOCK_DGRAM, "127.0.0.1", 0, 0);
You can specify the port to get_socket_local_port(tcp_server),
> + if (!ASSERT_NEQ(udp_server, -1, "start udp_server"))
> + goto close_tcp;
> +
> + skel->bss->tcp_listener_port = ntohs(get_socket_local_port(tcp_server));
> + udp_port = ntohs(get_socket_local_port(udp_server));
> + skel->bss->udp_test_port = udp_port;
then the 3 lines above will be unnecessary,
> +
> + ret = bpf_program__fd(skel->progs.tcp_custom_syncookie_badproto);
> + if (setup_tc(ret))
> + goto close_udp_server;
> +
> + udp_client = socket(AF_INET, SOCK_DGRAM, 0);
> + if (!ASSERT_NEQ(udp_client, -1, "udp socket"))
> + goto cleanup_tc;
> +
> + memset(&udp_addr, 0, sizeof(udp_addr));
> + udp_addr.sin_family = AF_INET;
> + udp_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
> + udp_addr.sin_port = htons(udp_port);
and you can reuse get_socket_local_port(tcp_server) here too.
> +
> + ret = sendto(udp_client, buf, sizeof(buf), 0,
> + (struct sockaddr *)&udp_addr, sizeof(udp_addr));
> + if (!ASSERT_EQ(ret, sizeof(buf), "sendto udp"))
> + goto cleanup_tc;
> +
> + /* 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 cleanup_tc;
> +
> + 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");
> +
> +cleanup_tc:
> + system("tc qdisc del dev lo clsact");
> + if (udp_client >= 0)
> + close(udp_client);
> +close_udp_server:
> + close(udp_server);
> +close_tcp:
> + close(tcp_server);
> +destroy_skel:
> 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..386705b6c9f2 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,83 @@ 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, but currently it doesn't check L4 protocol.
> + */
> +__u16 tcp_listener_port = 0;
> +__u16 udp_test_port = 0;
> +int assign_ret = -1;
> +bool udp_intercepted = false;
> +
> +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 bpf_tcp_req_attrs attrs = {};
> + struct ethhdr *eth;
> + struct iphdr *iph;
> + struct udphdr *udp;
> + struct bpf_sock *skc;
> + struct sock *sk;
> +
> + eth = (struct ethhdr *)data;
> + if (eth + 1 > data_end)
> + return TC_ACT_OK;
> +
> + if (bpf_ntohs(eth->h_proto) != ETH_P_IP)
> + return TC_ACT_OK;
> +
> + 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;
> +
> + if (bpf_ntohs(udp->dest) != udp_test_port)
> + return TC_ACT_OK;
You don't need to worry about other program sending UDP
packets in this netns created by unshare().
> +
> + udp_intercepted = true;
> +
> + tuple.ipv4.saddr = iph->saddr;
> + tuple.ipv4.daddr = iph->daddr;
> + tuple.ipv4.sport = udp->source;
> + tuple.ipv4.dport = bpf_htons(tcp_listener_port);
and you can simply reuse dport here too.
> +
> + skc = bpf_skc_lookup_tcp(skb, &tuple, sizeof(tuple.ipv4), -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;
> +
> + /* Call bpf_sk_assign_tcp_reqsk on a UDP skb. */
> + assign_ret = bpf_sk_assign_tcp_reqsk(skb, sk, &attrs, sizeof(attrs));
> +
> + bpf_sk_release(skc);
> +
> + /* Let the packet continue into the kernel */
> + return TC_ACT_OK;
> +}
> +
> char _license[] SEC("license") = "GPL";
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH net-next v4 1/2] net: stmmac: provide flag to disable EEE
From: Kieran Bingham @ 2026-03-26 17:31 UTC (permalink / raw)
To: Laurent Pinchart, imx, netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Fabio Estevam,
Francesco Dolcini, Frank Li, Jakub Kicinski, Joy Zou,
Marco Felsch, Paolo Abeni, Pengutronix Kernel Team,
Russell King (Oracle), Stefan Klug, linux-arm-kernel
In-Reply-To: <20260325210003.2752013-2-laurent.pinchart@ideasonboard.com>
Quoting Laurent Pinchart (2026-03-25 21:00:02)
> From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk>
>
> Some platforms have problems when EEE is enabled, and thus need a way
> to disable stmmac EEE support. Add a flag before the other LPI related
> flags which tells stmmac to avoid populating the phylink LPI
> capabilities, which causes phylink to call phy_disable_eee() for any
> PHY that is attached to the affected phylink instance.
>
> iMX8MP is an example - the lpi_intr_o signal is wired to an OR gate
> along with the main dwmac interrupts. Since lpi_intr_o is synchronous
> to the receive clock domain, and takes four clock cycles to clear, this
> leads to interrupt storms as the interrupt remains asserted for some
> time after the LPI control and status register is read.
>
> This problem becomes worse when the receive clock from the PHY stops
> when the receive path enters LPI state - which means that lpi_intr_o
> can not deassert until the clock restarts. Since the LPI state of the
> receive path depends on the link partner, this is out of our control.
> We could disable RX clock stop at the PHY, but that doesn't get around
> the slow-to-deassert lpi_intr_o mentioned in the above paragraph.
>
> Previously, iMX8MP worked around this by disabling gigabit EEE, but
> this is insufficient - the problem is also visible at 100M speeds,
> where the receive clock is slower.
>
> There is extensive discussion and investigation in the thread linked
> below, the result of which is summarised in this commit message.
This one has been a roller coaster, but I'm glad we got to the bottom of
it.
I hope someone from synopsis takes note and and documents this for
future silicon integrations.
Can't wait to see where else this pops up.
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
>
> Reported-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Closes: https://lore.kernel.org/r/20251026122905.29028-1-laurent.pinchart@ideasonboard.com
> Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
> Tested-by: Ovidiu Panait <ovidiu.panait.rb@renesas.com>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
> drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 7 ++++++-
> include/linux/stmmac.h | 13 +++++++------
> 2 files changed, 13 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> index 9b6b49331639..ce51b9c22129 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
> @@ -1438,7 +1438,12 @@ static int stmmac_phylink_setup(struct stmmac_priv *priv)
> config->supported_interfaces,
> pcs->supported_interfaces);
>
> - if (priv->dma_cap.eee) {
> + /* Some platforms, e.g. iMX8MP, wire lpi_intr_o to the same interrupt
> + * used for stmmac's main interrupts, which leads to interrupt storms.
> + * STMMAC_FLAG_EEE_DISABLE allows EEE to be disabled on such platforms.
> + */
> + if (priv->dma_cap.eee &&
> + !(priv->plat->flags & STMMAC_FLAG_EEE_DISABLE)) {
> /* The GMAC 3.74a databook states that EEE is only supported
> * in MII, GMII, and RGMII interfaces.
> */
> diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
> index 5b2bece81448..e62d21afd56d 100644
> --- a/include/linux/stmmac.h
> +++ b/include/linux/stmmac.h
> @@ -207,12 +207,13 @@ enum dwmac_core_type {
> #define STMMAC_FLAG_MULTI_MSI_EN BIT(7)
> #define STMMAC_FLAG_EXT_SNAPSHOT_EN BIT(8)
> #define STMMAC_FLAG_INT_SNAPSHOT_EN BIT(9)
> -#define STMMAC_FLAG_RX_CLK_RUNS_IN_LPI BIT(10)
> -#define STMMAC_FLAG_EN_TX_LPI_CLOCKGATING BIT(11)
> -#define STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP BIT(12)
> -#define STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY BIT(13)
> -#define STMMAC_FLAG_KEEP_PREAMBLE_BEFORE_SFD BIT(14)
> -#define STMMAC_FLAG_SERDES_SUPPORTS_2500M BIT(15)
> +#define STMMAC_FLAG_EEE_DISABLE BIT(10)
> +#define STMMAC_FLAG_RX_CLK_RUNS_IN_LPI BIT(11)
> +#define STMMAC_FLAG_EN_TX_LPI_CLOCKGATING BIT(12)
> +#define STMMAC_FLAG_EN_TX_LPI_CLK_PHY_CAP BIT(13)
> +#define STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY BIT(14)
> +#define STMMAC_FLAG_KEEP_PREAMBLE_BEFORE_SFD BIT(15)
> +#define STMMAC_FLAG_SERDES_SUPPORTS_2500M BIT(16)
>
> struct mac_device_info;
>
> --
> Regards,
>
> Laurent Pinchart
>
^ permalink raw reply
* Re: [PATCH v2 4/5] bpf: allow new DECAP flags and add guard rails
From: Hudson, Nick @ 2026-03-26 17:02 UTC (permalink / raw)
To: Martin KaFai Lau
Cc: Willem de Bruijn, Tottenham, Max, Glasgall, Anna, Daniel Borkmann,
Alexei Starovoitov, Andrii Nakryiko, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, bpf@vger.kernel.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <60a0b32d-b02b-419d-9d71-3293261333cb@linux.dev>
[-- Attachment #1: Type: text/plain, Size: 3964 bytes --]
> On Mar 24, 2026, at 6:30 PM, Martin KaFai Lau <martin.lau@linux.dev> wrote:
>
> !-------------------------------------------------------------------|
> This Message Is From an External Sender
> This message came from outside your organization.
> |-------------------------------------------------------------------!
>
>
>
> On 3/18/26 6:42 AM, Nick Hudson wrote:
>> Add checks to require shrink-only decap, reject conflicting decap flag
>> combinations, and verify removed length is sufficient for claimed header
>> decapsulation.
>> Co-developed-by: Max Tottenham <mtottenh@akamai.com>
>> Signed-off-by: Max Tottenham <mtottenh@akamai.com>
>> Co-developed-by: Anna Glasgall <aglasgal@akamai.com>
>> Signed-off-by: Anna Glasgall <aglasgal@akamai.com>
>> Signed-off-by: Nick Hudson <nhudson@akamai.com>
>> ---
>> net/core/filter.c | 47 +++++++++++++++++++++++++++++++++++++----------
>> 1 file changed, 37 insertions(+), 10 deletions(-)
>> diff --git a/net/core/filter.c b/net/core/filter.c
>> index 7c2871b40fe4..47aec44a9cd3 100644
>> --- a/net/core/filter.c
>> +++ b/net/core/filter.c
>> @@ -56,6 +56,7 @@
>> #include <net/sock_reuseport.h>
>> #include <net/busy_poll.h>
>> #include <net/tcp.h>
>> +#include <net/gre.h>
>> #include <net/xfrm.h>
>> #include <net/udp.h>
>> #include <linux/bpf_trace.h>
>> @@ -3496,7 +3497,9 @@ static u32 bpf_skb_net_base_len(const struct sk_buff *skb)
>> BPF_F_ADJ_ROOM_ENCAP_L2( \
>> BPF_ADJ_ROOM_ENCAP_L2_MASK))
>> -#define BPF_F_ADJ_ROOM_DECAP_MASK (BPF_F_ADJ_ROOM_DECAP_L3_MASK)
>> +#define BPF_F_ADJ_ROOM_DECAP_MASK (BPF_F_ADJ_ROOM_DECAP_L3_MASK | \
>> + BPF_F_ADJ_ROOM_DECAP_L4_MASK | \
>> + BPF_F_ADJ_ROOM_DECAP_IPXIP_MASK)
>> #define BPF_F_ADJ_ROOM_MASK (BPF_F_ADJ_ROOM_FIXED_GSO | \
>> BPF_F_ADJ_ROOM_ENCAP_MASK | \
>> @@ -3743,20 +3746,44 @@ BPF_CALL_4(bpf_skb_adjust_room, struct sk_buff *, skb, s32, len_diff,
>> return -ENOTSUPP;
>> }
>> - if (flags & BPF_F_ADJ_ROOM_DECAP_L3_MASK) {
>> + if (flags & BPF_F_ADJ_ROOM_DECAP_MASK) {
>
> This change should be done together with the macro refactoring patch mentioned in patch 3.
OK, will send a new version with it done this way.
>
>> + u32 len_decap_min = 0;
>> +
>> if (!shrink)
>> return -EINVAL;
>> - switch (flags & BPF_F_ADJ_ROOM_DECAP_L3_MASK) {
>> - case BPF_F_ADJ_ROOM_DECAP_L3_IPV4:
>> + /* Reject mutually exclusive decap flag pairs. */
>> + if ((flags & BPF_F_ADJ_ROOM_DECAP_L3_MASK) ==
>> + BPF_F_ADJ_ROOM_DECAP_L3_MASK)
>
> iiuc, this 'if' and the len_min assignment changes below replace the existing switch case. Please separate this no-op change from the new flag validation logic. It is small enough to be done together in the macro refactoring patch also.
>
>> + return -EINVAL;
>> +
>> + if ((flags & BPF_F_ADJ_ROOM_DECAP_L4_MASK) ==
>> + BPF_F_ADJ_ROOM_DECAP_L4_MASK)
>> + return -EINVAL;
>> +
>> + if ((flags & BPF_F_ADJ_ROOM_DECAP_IPXIP_MASK) ==
>> + BPF_F_ADJ_ROOM_DECAP_IPXIP_MASK)
>> + return -EINVAL;
>> +
>> + /* Reject mutually exclusive decap tunnel type flags. */
>> + if ((flags & BPF_F_ADJ_ROOM_DECAP_L4_MASK) &&
>> + (flags & BPF_F_ADJ_ROOM_DECAP_IPXIP_MASK))
>> + return -EINVAL;
>> +
>> + if (flags & BPF_F_ADJ_ROOM_DECAP_L4_UDP)
>> + len_decap_min += sizeof(struct udphdr);
>> +
>> + if (flags & BPF_F_ADJ_ROOM_DECAP_L4_GRE)
>> + len_decap_min += sizeof(struct gre_base_hdr);
>> +
>> + if (len_diff_abs < len_decap_min)
>> + return -EINVAL;
>> +
>> + if (flags & BPF_F_ADJ_ROOM_DECAP_L3_IPV4)
>> len_min = sizeof(struct iphdr);
>> - break;
>> - case BPF_F_ADJ_ROOM_DECAP_L3_IPV6:
>> +
>> + if (flags & BPF_F_ADJ_ROOM_DECAP_L3_IPV6)
>> len_min = sizeof(struct ipv6hdr);
>> - break;
>> - default:
>> - return -EINVAL;
>> - }
>> }
>> len_cur = skb->len - skb_network_offset(skb);
>
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3066 bytes --]
^ permalink raw reply
* Re: [RFC PATCH net-next 0/3] seg6: SRv6 L2 VPN with End.DT2U and srl2 device
From: Stefano Salsano @ 2026-03-26 17:30 UTC (permalink / raw)
To: nicolas.dichtel, Justin Iurman, Andrea Mayer
Cc: netdev, David S . Miller, David Ahern, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Paolo Lungaroni,
Ahmed Abdelsalam, linux-kernel
In-Reply-To: <dfc4fb29-7f27-4e9f-b6c9-bd37cbddbf1e@6wind.com>
Il 26/03/2026 17:30, Nicolas Dichtel ha scritto:
>
>
> Le 25/03/2026 à 08:10, Stefano Salsano a écrit :
>> Il 24/03/2026 17:00, Justin Iurman ha scritto:
>>> On Sun, Mar 22, 2026 at 12:06 AM Andrea Mayer <andrea.mayer@uniroma2.it> wrote:
>>>>
>>>> Hi all,
>>>>
>>>> this RFC series adds support for the SRv6 End.DT2U behavior and
>>>> introduces the srl2 Ethernet pseudowire device, together with
>>>> corresponding selftests.
>>>
>>> Hi Andrea,
>>>
>>> Thanks for the work you (and Stefano) put into this series! As
>>> discussed offline, there is interest in such a solution.
>>
>> Hi Justin,
>>
>> thx for your interest!
>>
>>> A general comment: I'm not sure about the name "srl2", as we're
>>> talking about SRv6 specifically here.
>>
>> fair point
>>
>>> I would personally love to have
>>> "l2srv6", although I suspect we may end up with "l2seg6" to remain
>>> consistent.
>>
>> I'd like to keep it as short as possible, our initial idea was seg6l2, very
>> close to your last suggestion, then we opted for srl2 losing the "6" concept
>>
>> now it comes to my mind that l2 is somehow redudant in an interface type name,
>> as an interface is an l2 concept per se, so my preferred option becomes:
>>
>> sr6
> Note that the interface name is defined to kind + a number when the IFLA_IFNAME
> attribute is not specified:
> https://urldefense.com/v3/__https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/core/rtnetlink.c?h=v7.0-rc5*n3823__;Iw!!O5Bi4QcV!Bw3h__JfgzAoQlG3DyfQsdX5VEQi5Zx9dXbROUeKuwomkISAWgGMwNxjv059RhKI5xV9MpHHknEXDTb9diS16akPWyJi_dCP7aE$
>
> Here, the default name will be 'sr60' :)
+1
> 'vti6' is already in this case, so it's probably ok. I vote for 'sr6' too ;-)
>
> My two cents,
> Nicolas
--
*******************************************************************
Prof. Stefano Salsano
Dipartimento Ingegneria Elettronica
Universita' di Roma Tor Vergata
Viale Politecnico, 1 - 00133 Roma - ITALY
http://netgroup.uniroma2.it/Stefano_Salsano/
E-mail : stefano.salsano@uniroma2.it
Office : (Tel.) +39 06 72597770 (Fax.) +39 06 72597435
*******************************************************************
^ permalink raw reply
* [PATCH net-next] net: mana: hardening: Validate adapter_mtu from MANA_QUERY_DEV_CONFIG
From: Erni Sri Satya Vennela @ 2026-03-26 17:30 UTC (permalink / raw)
To: kys, haiyangz, wei.liu, decui, longli, andrew+netdev, davem,
edumazet, kuba, pabeni, ernis, ssengar, dipayanroy, gargaditya,
shirazsaleem, kees, linux-hyperv, netdev, linux-kernel
As a part of MANA hardening for CVM, validate the adapter_mtu value
returned from the MANA_QUERY_DEV_CONFIG HWC command.
The adapter_mtu value is used to compute ndev->max_mtu via:
gc->adapter_mtu - ETH_HLEN. If hardware returns a bogus adapter_mtu
smaller than ETH_HLEN (e.g. 0), the unsigned subtraction wraps to a
huge value, silently allowing oversized MTU settings.
Add a validation check to reject adapter_mtu values below
ETH_MIN_MTU + ETH_HLEN, returning -EPROTO to fail the device
configuration early with a clear error message.
Signed-off-by: Erni Sri Satya Vennela <ernis@linux.microsoft.com>
---
drivers/net/ethernet/microsoft/mana/mana_en.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index b39e8b920791..bd07d17a6017 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -1207,10 +1207,16 @@ static int mana_query_device_cfg(struct mana_context *ac, u32 proto_major_ver,
*max_num_vports = resp.max_num_vports;
- if (resp.hdr.response.msg_version >= GDMA_MESSAGE_V2)
+ if (resp.hdr.response.msg_version >= GDMA_MESSAGE_V2) {
+ if (resp.adapter_mtu < ETH_MIN_MTU + ETH_HLEN) {
+ dev_err(dev, "Adapter MTU too small: %u\n",
+ resp.adapter_mtu);
+ return -EPROTO;
+ }
gc->adapter_mtu = resp.adapter_mtu;
- else
+ } else {
gc->adapter_mtu = ETH_FRAME_LEN;
+ }
if (resp.hdr.response.msg_version >= GDMA_MESSAGE_V3)
*bm_hostmode = resp.bm_hostmode;
--
2.34.1
^ permalink raw reply related
* Re: [GIT PULL] Networking for v7.0-rc6
From: pr-tracker-bot @ 2026-03-26 17:30 UTC (permalink / raw)
To: Paolo Abeni; +Cc: torvalds, kuba, davem, netdev, linux-kernel
In-Reply-To: <20260326163029.350902-1-pabeni@redhat.com>
The pull request you sent on Thu, 26 Mar 2026 17:30:29 +0100:
> git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git net-7.0-rc6
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/453a4a5f97f0c95b7df458e6afb98d4ab057d90b
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply
* Re: [RFC PATCH net-next 2/3] seg6: add SRv6 L2 tunnel device (srl2)
From: Stefano Salsano @ 2026-03-26 17:29 UTC (permalink / raw)
To: Justin Iurman, Andrea Mayer
Cc: netdev, David S . Miller, David Ahern, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Paolo Lungaroni,
Ahmed Abdelsalam, linux-kernel
In-Reply-To: <CAOY2Bqxb1C6myf7DBmfLM_3wZSweYQaQSAVSj_wDihf-N9vQJg@mail.gmail.com>
Il 25/03/2026 14:43, Justin Iurman ha scritto:
> On Sun, Mar 22, 2026 at 12:06 AM Andrea Mayer <andrea.mayer@uniroma2.it> wrote:
>>
>> Introduce srl2, an Ethernet pseudowire device over SRv6. It
>> encapsulates L2 frames in IPv6 with a Segment Routing Header for
>> transmission across an SRv6 network.
>>
>> The encapsulation logic reuses seg6_do_srh_encap() with
>> IPPROTO_ETHERNET. The transmit path uses the standard IPv6 tunnel
>> infrastructure (dst_cache, ip6_route_output, ip6tunnel_xmit).
>>
>> The device is configured with a segment list for point-to-point
>> L2 encapsulation.
>>
>> Usage:
>>
>> ip link add srl2-0 type srl2 segs fc00::a,fc00::b
>
> Thinking out loud...
>
> We'll likely need SRv6 encap configurations specific to each entry
> (i.e., MAC address), rather than (or in addition to) per interface.
agreed, this is the natural next step towards multipoint
we've started exploring the design for this, happy to collaborate if
you're interested
> We could also add a "mode" (e.g., "normal" mode or reduced mode?).
good point, the infrastructure is already in place, so adding a "mode"
parameter to sr6 to support reduced encap should be straightforward
we suggest to address it as an immediate follow-up to this baseline
series and we have already some prototype code ready
ciao
Stefano and Andrea
--
*******************************************************************
Prof. Stefano Salsano
Dipartimento Ingegneria Elettronica
Universita' di Roma Tor Vergata
Viale Politecnico, 1 - 00133 Roma - ITALY
http://netgroup.uniroma2.it/Stefano_Salsano/
E-mail : stefano.salsano@uniroma2.it
Office : (Tel.) +39 06 72597770 (Fax.) +39 06 72597435
*******************************************************************
^ permalink raw reply
* Re: [GIT PULL] wireless-2026-03-26
From: Johannes Berg @ 2026-03-26 17:29 UTC (permalink / raw)
To: Paolo Abeni, netdev; +Cc: linux-wireless
In-Reply-To: <936e4100-5e3e-4c3a-8598-6724d904deac@redhat.com>
On Thu, 2026-03-26 at 18:24 +0100, Paolo Abeni wrote:
> On 3/26/26 10:31 AM, Johannes Berg wrote:
> > And ... more fixes from drivers came in, notably iwlwifi with
> > a bunch of things (their maintainer had been on vacation).
> >
> > I'm going to be on vacation next week, but I'm also not aware
> > of anything missing from drivers etc. right now, so we'll see.
> > I doubt this is the _last_ pull request for 7.0, but I think
> > not much more will come in.
> >
> > Please pull and let us know if there's any problem.
>
> Blame on me, I left this one out of my bag, and it missed today net PR
> for Linus.
No worries, I just wanted to get it out before I go mostly offline for
the next week, so it's not stuck on me :)
johannes
^ permalink raw reply
* Re: [PATCH net-next 1/2] net: stmmac: remove axi_kbbe, axi_mb and axi_rb members
From: Simon Horman @ 2026-03-26 17:29 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Andrew Lunn, Alexandre Torgue, Andrew Lunn, Conor Dooley,
David S. Miller, devicetree, Eric Dumazet, Giuseppe Cavallaro,
Jakub Kicinski, Jose Abreu, Krzysztof Kozlowski, linux-arm-kernel,
linux-stm32, netdev, Paolo Abeni, Rob Herring, Yao Zi
In-Reply-To: <E1w4ydo-0000000Dlpb-34jd@rmk-PC.armlinux.org.uk>
On Tue, Mar 24, 2026 at 10:05:40AM +0000, Russell King (Oracle) wrote:
> axi_kbbe, axi_mb and axi_rb are all written, but nothing ever reads
> their values. Remove the code that sets these and the struct members.
>
> Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Hi Russell,
FYI, AI review suggests that these fields should also be removed from
Documentation/networking/device_drivers/ethernet/stmicro/stmmac.rst
^ permalink raw reply
* Re: [PATCH net-next] ipv6: move IFA_F_PERMANENT percpu allocation in process scope
From: David Ahern @ 2026-03-26 17:26 UTC (permalink / raw)
To: Paolo Abeni, netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Simon Horman
In-Reply-To: <8c8bfe2e1a324e501f0e15fef404a77443fd8caf.1774365668.git.pabeni@redhat.com>
On 3/24/26 9:22 AM, Paolo Abeni wrote:
> Observed at boot time:
>
> CPU: 43 UID: 0 PID: 3595 Comm: (t-daemon) Not tainted 6.12.0 #1
6.12 ???
> IFA_F_PERMANENT addresses require the allocation of a bunch of percpu
> pointers, currently in atomic scope.
>
> Similar to commit 51454ea42c1a ("ipv6: fix locking issues with loops
> over idev->addr_list"), move fixup_permanent_addr() outside the
> &idev->lock scope, and do the allocations with GFP_KERNEL. With such
> change fixup_permanent_addr() is invoked with the BH enabled, and the
> ifp lock acquired there needs the BH variant.
>
> Note that we don't need to acquire a reference to the permanent
> addresses before releasing the mentioned write lock, because
> addrconf_permanent_addr() runs under RTNL and ifa removal always happens
> under RTNL, too.
>
> Also the PERMANENT flag is constant in the relevant scope, as it can be
> cleared only by inet6_addr_modify() under the RTNL lock.
>
> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
> ---
> explicitly targeting net-next as this is IMHO an improvement more than a
> bug fix
> ---
> net/ipv6/addrconf.c | 32 ++++++++++++++++++++------------
> 1 file changed, 20 insertions(+), 12 deletions(-)
>
Reviewed-by: David Ahern <dsahern@kernel.org>
^ permalink raw reply
* Re: [PATCH net-next v2 4/4] net: phy: Introduce Airoha AN8801/R Gigabit Ethernet PHY driver
From: Maxime Chevallier @ 2026-03-26 17:25 UTC (permalink / raw)
To: Andrew Lunn, Russell King (Oracle)
Cc: Andrew Lunn, Louis-Alexis Eyraud, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, AngeloGioacchino Del Regno, Heiner Kallweit,
kevin-kw.huang, macpaul.lin, matthias.bgg, kernel, netdev,
devicetree, linux-arm-kernel, linux-mediatek, linux-kernel
In-Reply-To: <425217fd-f74c-455c-9aa7-5b6682d887fa@lunn.ch>
On 26/03/2026 17:56, Andrew Lunn wrote:
>> What is the timing requirements for a system going into suspend vs a WoL
>> packet being sent? Should a WoL packet abort entry into suspend? If yes,
>> then we need to program the MAC before the PHY is suspended, because
>> suspend could already be in progress.
>
> We would then need to hook into the NETDEV_CHANGEADDR notifier, and
> call into the PHY driver to let it reprogram the MAC address.
We would also probably have to re-do that MAC addr programming at phy
attach time ? If the PHY isn't attached (e.g. link admin-down on some
boards), we can't use that notifier as we don't know what netdev to
listen to. Or I'm missing something ?
Maxime
>
> Andrew
>
^ permalink raw reply
* Re: [GIT PULL] wireless-2026-03-26
From: Paolo Abeni @ 2026-03-26 17:24 UTC (permalink / raw)
To: Johannes Berg, netdev; +Cc: linux-wireless
In-Reply-To: <20260326093329.77815-3-johannes@sipsolutions.net>
On 3/26/26 10:31 AM, Johannes Berg wrote:
> And ... more fixes from drivers came in, notably iwlwifi with
> a bunch of things (their maintainer had been on vacation).
>
> I'm going to be on vacation next week, but I'm also not aware
> of anything missing from drivers etc. right now, so we'll see.
> I doubt this is the _last_ pull request for 7.0, but I think
> not much more will come in.
>
> Please pull and let us know if there's any problem.
Blame on me, I left this one out of my bag, and it missed today net PR
for Linus.
/P
^ permalink raw reply
* Re: Re: [PATCH net-next v5 3/3] riscv: dts: eswin: eic7700-hifive-premier-p550: enable Ethernet controller
From: Simon Horman @ 2026-03-26 17:21 UTC (permalink / raw)
To: 李志
Cc: devicetree, andrew+netdev, davem, edumazet, kuba, robh, krzk+dt,
conor+dt, netdev, pabeni, mcoquelin.stm32, alexandre.torgue,
rmk+kernel, pjw, palmer, aou, alex, linux-riscv, linux-stm32,
linux-arm-kernel, linux-kernel, maxime.chevallier, ningyu, linmin,
pinkesh.vaghela, pritesh.patel, weishangjuan
In-Reply-To: <2a12c839.5e64.19d28232537.Coremail.lizhi2@eswincomputing.com>
On Thu, Mar 26, 2026 at 11:14:45AM +0800, 李志 wrote:
...
> Hi Simon,
>
> Thanks for your review.
>
> You're right, this build failure is due to an invalid clock reference
> ("clk") in the Ethernet node, which does not correspond to an existing
> clock provider label in the current DTS.
>
> For context, this was discussed during an earlier revision:
> https://lore.kernel.org/lkml/5dea8ce0.4435.19c471231f5.Coremail.lizhi2@eswincomputing.com/
>
> The EIC7700 clock controller support has since been applied, so I will
> update the DTS to reference the correct clock provider and ensure the
> build passes cleanly.
>
> I will fix this in the next revision (v6).
Thanks.
Please be aware that if the patch is routed via net-next,
then the dependency will need to be present in net-next.
^ permalink raw reply
* Re: [net-next v5 09/12] net: bnxt: Add SW GSO completion and teardown support
From: Paolo Abeni @ 2026-03-26 17:20 UTC (permalink / raw)
To: Joe Damato, netdev, Michael Chan, Pavan Chebbi, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, horms,
linux-kernel, leon
In-Reply-To: <acVmr9gxjm6mJ0IJ@devvm20253.cco0.facebook.com>
On 3/26/26 6:02 PM, Joe Damato wrote:
> On Thu, Mar 26, 2026 at 01:39:17PM +0100, Paolo Abeni wrote:
>> On 3/23/26 7:38 PM, Joe Damato wrote:
>>> Update __bnxt_tx_int and bnxt_free_one_tx_ring_skbs to handle SW GSO
>>> segments:
>>>
>>> - MID segments: adjust tx_pkts/tx_bytes accounting and skip skb free
>>> (the skb is shared across all segments and freed only once)
>>>
>>> - LAST segments: if the DMA IOVA path was used, use dma_iova_destroy to
>>> tear down the contiguous mapping. On the fallback path, payload DMA
>>> unmapping is handled by the existing per-BD dma_unmap_len walk.
>>>
>>> Both MID and LAST completions advance tx_inline_cons to release the
>>> segment's inline header slot back to the ring.
>>>
>>> is_sw_gso is initialized to zero, so the new code paths are not run.
>>>
>>> Suggested-by: Jakub Kicinski <kuba@kernel.org>
>>> Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
>>> Signed-off-by: Joe Damato <joe@dama.to>
>>> ---
>>> v5:
>>> - Added Pavan's Reviewed-by. No functional changes.
>>>
>>> v3:
>>> - completion paths updated to use DMA IOVA APIs to teardown mappings.
>>>
>>> rfcv2:
>>> - Update the shared header buffer consumer on TX completion.
>>>
>>> drivers/net/ethernet/broadcom/bnxt/bnxt.c | 82 +++++++++++++++++--
>>> .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 19 ++++-
>>> 2 files changed, 91 insertions(+), 10 deletions(-)
>>>
>>> diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
>>> index 2759a4e2b148..40a16f96feba 100644
>>> --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
>>> +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
>>> @@ -74,6 +74,8 @@
>>> #include "bnxt_debugfs.h"
>>> #include "bnxt_coredump.h"
>>> #include "bnxt_hwmon.h"
>>> +#include "bnxt_gso.h"
>>> +#include <net/tso.h>
>>>
>>> #define BNXT_TX_TIMEOUT (5 * HZ)
>>> #define BNXT_DEF_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_HW | \
>>> @@ -817,12 +819,13 @@ static bool __bnxt_tx_int(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
>>> bool rc = false;
>>>
>>> while (RING_TX(bp, cons) != hw_cons) {
>>> - struct bnxt_sw_tx_bd *tx_buf;
>>> + struct bnxt_sw_tx_bd *tx_buf, *head_buf;
>>> struct sk_buff *skb;
>>> bool is_ts_pkt;
>>> int j, last;
>>>
>>> tx_buf = &txr->tx_buf_ring[RING_TX(bp, cons)];
>>> + head_buf = tx_buf;
>>> skb = tx_buf->skb;
>>>
>>> if (unlikely(!skb)) {
>>> @@ -869,6 +872,23 @@ static bool __bnxt_tx_int(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
>>> DMA_TO_DEVICE, 0);
>>> }
>>> }
>>> +
>>> + if (unlikely(head_buf->is_sw_gso)) {
>>> + txr->tx_inline_cons++;
>>> + if (head_buf->is_sw_gso == BNXT_SW_GSO_LAST) {
>>> + if (dma_use_iova(&head_buf->iova_state))
>>
>> I'm likely lost, but AFAICS the previous patch/bnxt_sw_udp_gso_xmit()
>> initialize head_buf->iova_state only when
>> `dma_use_iova(&head_buf->iova_state) == true`. I.e. in fallback scenario
>> the previous iova_state is maintained.
>
> Note that calling dma_iova_try_alloc zeroes the state before returning whether
> the IOVA DMA API can be used or not and I call that uncoditionally (see
> below).
>
>> Additionally AFAICS dma_iova_destroy does not clear `head_buf->iova_state`.
>
> That's my understanding, too, that dma_iova_destroy doesn't clear the state.
>
>> It looks like that 2 consecutive skb hitting the same slot use a
>> different dma mapping strategy (fallback vs iova) bat things will
>> happen?!? should the previous patch always initializing
>> head_buf->iova_state?
>
> AFAICT, to switch the IOMMU domain would require unbind the device, changing
> the IOMMU type, and re-binding the device... which would destroy all the rings
> in the process and thus this wouldn't happen.
>
> The only way I could potentially imagine this happening would be in extreme
> IOVA pressure (maybe?):
> - packet A in slot N, dma_iova_try_alloc suceeds -> head_buf->iova_state
> copied
> - completion the packet occurs, dma_iova_destroy is called,
> head_buf->iova_state is not cleared
> - packet B in slot N, dma_iova_try_alloc fails due to IOVA pressure...
> head_buf->iova_state is stale
>
> I'm pretty skeptical that this is a realistic case, TBH.
>
> That said and since it seems my v5 got CR, I can send a v6 with this slight
> change to address the case you've mentioned above.
>
> I'll send in a couple hours unless I hear otherwise:
>
> diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
> index 9c30ee063ef5..7c198847a771 100644
> --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
> +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
> @@ -142,8 +142,12 @@ netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,
>
> tx_buf->is_sw_gso = last ? BNXT_SW_GSO_LAST : BNXT_SW_GSO_MID;
>
> - /* Store IOVA state on the last segment for completion */
> - if (last && tso_dma_map_use_iova(&map)) {
> + /* Store IOVA state on the last segment for completion.
> + * Always copy so that a stale iova_state from a prior
> + * occupant of this ring slot cannot be misread by
> + * dma_use_iova() in the completion path.
> + */
> + if (last) {
> tx_buf->iova_state = map.iova_state;
> tx_buf->iova_total_len = map.total_len;
> }
>
Since tso_dma_map_use_iova(&map) is the likely option, I tend to think
that the above change is worthy even if the problem I feared about is
extremely unlikely if possible at all: the code is IMHO easier to
follow, and FWIW does not overoptimize an unlikely scenario.
/P
^ permalink raw reply
* Re: [EXTERNAL] Re: [PATCH net-next v5 0/6] net: mana: Per-vPort EQ and MSI-X interrupt management
From: Simon Horman @ 2026-03-26 17:19 UTC (permalink / raw)
To: Long Li
Cc: Konstantin Taranov, Jakub Kicinski, David S . Miller, Paolo Abeni,
Eric Dumazet, Andrew Lunn, Jason Gunthorpe, Leon Romanovsky,
Haiyang Zhang, KY Srinivasan, Wei Liu, Dexuan Cui,
netdev@vger.kernel.org, linux-rdma@vger.kernel.org,
linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <SA1PR21MB6683FB2D67A3BBCC74B62D45CE49A@SA1PR21MB6683.namprd21.prod.outlook.com>
On Wed, Mar 25, 2026 at 08:47:35PM +0000, Long Li wrote:
> > > On Mon, Mar 23, 2026 at 12:59:46PM -0700, Long Li wrote:
...
> > Hi Simon,
> >
> > This patch set should apply after this patch: (which is also pending net-next)
> > net: mana: Set default number of queues to 16
> >
> > Can you apply the patch set after this patch, or should I wait for the next patch
> > merge window?
> >
> > Thank you,
> > Long
>
>
> I'll send it over in the next patch merging window.
Thanks,
The way I understand things net-next, and in particular the CI,
can only handle patches where all the dependencies are already
present in net-next.
^ permalink raw reply
* Re: [PATCH net] net: ethernet: mtk_ppe: avoid NULL deref when gmac0 is disabled
From: Simon Horman @ 2026-03-26 17:15 UTC (permalink / raw)
To: Sven Eckelmann (Plasma Cloud)
Cc: Felix Fietkau, Lorenzo Bianconi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Matthias Brugger,
AngeloGioacchino Del Regno, Elad Yifee, netdev, linux-kernel,
linux-arm-kernel, linux-mediatek, stable
In-Reply-To: <20260324-wed-crash-gmac0-disabled-v1-1-3bc388aee565@simonwunderlich.de>
On Tue, Mar 24, 2026 at 09:36:01AM +0100, Sven Eckelmann (Plasma Cloud) wrote:
> If the gmac0 is disabled, the precheck for a valid ingress device will
> cause a NULL pointer deref and crash the system. This happens because
> eth->netdev[0] will be NULL but the code will directly try to access
> netdev_ops.
>
> Instead of just checking for the first net_device, it must be checked if
> any of the mtk_eth net_devices is matching the netdev_ops of the ingress
> device.
>
> Cc: stable@vger.kernel.org
> Fixes: 73cfd947dbdb ("net: ethernet: mtk_eth_soc: ppe: prevent ppe update for non-mtk devices")
> Signed-off-by: Sven Eckelmann (Plasma Cloud) <se@simonwunderlich.de>
Reviewed-by: Simon Horman <horms@kernel.org>
^ permalink raw reply
* Re: [PATCH v2 3/5] bpf: add helper masks for ADJ_ROOM flags and encap validation
From: Hudson, Nick @ 2026-03-26 17:02 UTC (permalink / raw)
To: Martin KaFai Lau
Cc: Willem de Bruijn, Tottenham, Max, Glasgall, Anna,
Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
bpf@vger.kernel.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <f0f271ae-4d4c-4d52-8670-a14204da437d@linux.dev>
[-- Attachment #1: Type: text/plain, Size: 4768 bytes --]
> On Mar 24, 2026, at 6:12 PM, Martin KaFai Lau <martin.lau@linux.dev> wrote:
>
> !-------------------------------------------------------------------|
> This Message Is From an External Sender
> This message came from outside your organization.
> |-------------------------------------------------------------------!
>
> On 3/18/26 6:42 AM, Nick Hudson wrote:
>> Introduce helper masks for bpf_skb_adjust_room() flags to simplify
>> validation logic:
>> - BPF_F_ADJ_ROOM_DECAP_L4_MASK
>> - BPF_F_ADJ_ROOM_DECAP_IPXIP_MASK
>> - BPF_F_ADJ_ROOM_ENCAP_MASK
>> - BPF_F_ADJ_ROOM_DECAP_MASK
>> Add flag validation to bpf_skb_net_grow() to reject invalid encap
>> flags early. Refactor existing validation checks in bpf_skb_net_shrink()
>> and bpf_skb_adjust_room() to use the new masks (no behavior change).
>> Co-developed-by: Max Tottenham <mtottenh@akamai.com>
>> Signed-off-by: Max Tottenham <mtottenh@akamai.com>
>> Co-developed-by: Anna Glasgall <aglasgal@akamai.com>
>> Signed-off-by: Anna Glasgall <aglasgal@akamai.com>
>> Signed-off-by: Nick Hudson <nhudson@akamai.com>
>> ---
>> net/core/filter.c | 31 +++++++++++++++++++++++--------
>> 1 file changed, 23 insertions(+), 8 deletions(-)
>> diff --git a/net/core/filter.c b/net/core/filter.c
>> index 0d5d5a17acb2..7c2871b40fe4 100644
>> --- a/net/core/filter.c
>> +++ b/net/core/filter.c
>> @@ -3483,14 +3483,25 @@ static u32 bpf_skb_net_base_len(const struct sk_buff *skb)
>> #define BPF_F_ADJ_ROOM_DECAP_L3_MASK (BPF_F_ADJ_ROOM_DECAP_L3_IPV4 | \
>> BPF_F_ADJ_ROOM_DECAP_L3_IPV6)
>> -#define BPF_F_ADJ_ROOM_MASK (BPF_F_ADJ_ROOM_FIXED_GSO | \
>> - BPF_F_ADJ_ROOM_ENCAP_L3_MASK | \
>> +#define BPF_F_ADJ_ROOM_DECAP_L4_MASK (BPF_F_ADJ_ROOM_DECAP_L4_UDP | \
>> + BPF_F_ADJ_ROOM_DECAP_L4_GRE)
>> +
>> +#define BPF_F_ADJ_ROOM_DECAP_IPXIP_MASK (BPF_F_ADJ_ROOM_DECAP_IPXIP4 | \
>> + BPF_F_ADJ_ROOM_DECAP_IPXIP6)
>> +
>> +#define BPF_F_ADJ_ROOM_ENCAP_MASK (BPF_F_ADJ_ROOM_ENCAP_L3_MASK | \
>> BPF_F_ADJ_ROOM_ENCAP_L4_GRE | \
>> BPF_F_ADJ_ROOM_ENCAP_L4_UDP | \
>> BPF_F_ADJ_ROOM_ENCAP_L2_ETH | \
>> BPF_F_ADJ_ROOM_ENCAP_L2( \
>> - BPF_ADJ_ROOM_ENCAP_L2_MASK) | \
>> - BPF_F_ADJ_ROOM_DECAP_L3_MASK)
>> + BPF_ADJ_ROOM_ENCAP_L2_MASK))
>> +
>> +#define BPF_F_ADJ_ROOM_DECAP_MASK (BPF_F_ADJ_ROOM_DECAP_L3_MASK)
>> +
>> +#define BPF_F_ADJ_ROOM_MASK (BPF_F_ADJ_ROOM_FIXED_GSO | \
>> + BPF_F_ADJ_ROOM_ENCAP_MASK | \
>> + BPF_F_ADJ_ROOM_DECAP_MASK | \
>> + BPF_F_ADJ_ROOM_NO_CSUM_RESET)
>
> The patch does two things: refactoring of existing macros (BPF_F_ADJ_ROOM_ENCAP_MASK, BPF_F_ADJ_ROOM_DECAP_MASK) and new additions (BPF_F_ADJ_ROOM_DECAP_L4_MASK, BPF_F_ADJ_ROOM_DECAP_IPXIP_MASK) that depend on the new flags from the UAPI changes in patch 2.
>
> The refactoring does not depend on the new UAPI flags and could be a separate patch placed earlier in the series. That way a reviewer can verify it is a no-op without the new flag additions getting in
> the way. The (BPF_F_ADJ_ROOM_DECAP_L4_MASK, BPF_F_ADJ_ROOM_DECAP_IPXIP_MASK) can be introduced together in patch 4 when it is first used.
OK, will split further.
>
>> static int bpf_skb_net_grow(struct sk_buff *skb, u32 off, u32 len_diff,
>> u64 flags)
>> @@ -3502,6 +3513,11 @@ static int bpf_skb_net_grow(struct sk_buff *skb, u32 off, u32 len_diff,
>> unsigned int gso_type = SKB_GSO_DODGY;
>> int ret;
>> + if (unlikely(flags & ~(BPF_F_ADJ_ROOM_ENCAP_MASK |
>> + BPF_F_ADJ_ROOM_NO_CSUM_RESET |
>> + BPF_F_ADJ_ROOM_FIXED_GSO)))
>
> Under which case this new check will be hit?
If a user supplies +ve len_diff and attempts to pass a DECAP flag.
The commit message had
Add flag validation to bpf_skb_net_grow() to reject invalid encap
flags early.
>
>> + return -EINVAL;
>> +
>> if (skb_is_gso(skb) && !skb_is_gso_tcp(skb)) {
>> /* udp gso_size delineates datagrams, only allow if fixed */
>> if (!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) ||
>> @@ -3611,8 +3627,8 @@ static int bpf_skb_net_shrink(struct sk_buff *skb, u32 off, u32 len_diff,
>> {
>> int ret;
>> - if (unlikely(flags & ~(BPF_F_ADJ_ROOM_FIXED_GSO |
>> - BPF_F_ADJ_ROOM_DECAP_L3_MASK |
>> + if (unlikely(flags & ~(BPF_F_ADJ_ROOM_DECAP_MASK |
>> + BPF_F_ADJ_ROOM_FIXED_GSO |
>> BPF_F_ADJ_ROOM_NO_CSUM_RESET)))
>> return -EINVAL;
>> @@ -3708,8 +3724,7 @@ BPF_CALL_4(bpf_skb_adjust_room, struct sk_buff *, skb, s32, len_diff,
>> u32 off;
>> int ret;
>> - if (unlikely(flags & ~(BPF_F_ADJ_ROOM_MASK |
>> - BPF_F_ADJ_ROOM_NO_CSUM_RESET)))
>> + if (unlikely(flags & ~BPF_F_ADJ_ROOM_MASK))
>> return -EINVAL;
>> if (unlikely(len_diff_abs > 0xfffU))
>> return -EFAULT;
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 3066 bytes --]
^ permalink raw reply
* Re: [net-next v5 09/12] net: bnxt: Add SW GSO completion and teardown support
From: Joe Damato @ 2026-03-26 17:02 UTC (permalink / raw)
To: Paolo Abeni
Cc: netdev, Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, horms, linux-kernel, leon
In-Reply-To: <ab1f764b-de03-48f5-a781-356495257d25@redhat.com>
On Thu, Mar 26, 2026 at 01:39:17PM +0100, Paolo Abeni wrote:
> On 3/23/26 7:38 PM, Joe Damato wrote:
> > Update __bnxt_tx_int and bnxt_free_one_tx_ring_skbs to handle SW GSO
> > segments:
> >
> > - MID segments: adjust tx_pkts/tx_bytes accounting and skip skb free
> > (the skb is shared across all segments and freed only once)
> >
> > - LAST segments: if the DMA IOVA path was used, use dma_iova_destroy to
> > tear down the contiguous mapping. On the fallback path, payload DMA
> > unmapping is handled by the existing per-BD dma_unmap_len walk.
> >
> > Both MID and LAST completions advance tx_inline_cons to release the
> > segment's inline header slot back to the ring.
> >
> > is_sw_gso is initialized to zero, so the new code paths are not run.
> >
> > Suggested-by: Jakub Kicinski <kuba@kernel.org>
> > Reviewed-by: Pavan Chebbi <pavan.chebbi@broadcom.com>
> > Signed-off-by: Joe Damato <joe@dama.to>
> > ---
> > v5:
> > - Added Pavan's Reviewed-by. No functional changes.
> >
> > v3:
> > - completion paths updated to use DMA IOVA APIs to teardown mappings.
> >
> > rfcv2:
> > - Update the shared header buffer consumer on TX completion.
> >
> > drivers/net/ethernet/broadcom/bnxt/bnxt.c | 82 +++++++++++++++++--
> > .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 19 ++++-
> > 2 files changed, 91 insertions(+), 10 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
> > index 2759a4e2b148..40a16f96feba 100644
> > --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
> > +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
> > @@ -74,6 +74,8 @@
> > #include "bnxt_debugfs.h"
> > #include "bnxt_coredump.h"
> > #include "bnxt_hwmon.h"
> > +#include "bnxt_gso.h"
> > +#include <net/tso.h>
> >
> > #define BNXT_TX_TIMEOUT (5 * HZ)
> > #define BNXT_DEF_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_HW | \
> > @@ -817,12 +819,13 @@ static bool __bnxt_tx_int(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
> > bool rc = false;
> >
> > while (RING_TX(bp, cons) != hw_cons) {
> > - struct bnxt_sw_tx_bd *tx_buf;
> > + struct bnxt_sw_tx_bd *tx_buf, *head_buf;
> > struct sk_buff *skb;
> > bool is_ts_pkt;
> > int j, last;
> >
> > tx_buf = &txr->tx_buf_ring[RING_TX(bp, cons)];
> > + head_buf = tx_buf;
> > skb = tx_buf->skb;
> >
> > if (unlikely(!skb)) {
> > @@ -869,6 +872,23 @@ static bool __bnxt_tx_int(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
> > DMA_TO_DEVICE, 0);
> > }
> > }
> > +
> > + if (unlikely(head_buf->is_sw_gso)) {
> > + txr->tx_inline_cons++;
> > + if (head_buf->is_sw_gso == BNXT_SW_GSO_LAST) {
> > + if (dma_use_iova(&head_buf->iova_state))
>
> I'm likely lost, but AFAICS the previous patch/bnxt_sw_udp_gso_xmit()
> initialize head_buf->iova_state only when
> `dma_use_iova(&head_buf->iova_state) == true`. I.e. in fallback scenario
> the previous iova_state is maintained.
Note that calling dma_iova_try_alloc zeroes the state before returning whether
the IOVA DMA API can be used or not and I call that uncoditionally (see
below).
> Additionally AFAICS dma_iova_destroy does not clear `head_buf->iova_state`.
That's my understanding, too, that dma_iova_destroy doesn't clear the state.
> It looks like that 2 consecutive skb hitting the same slot use a
> different dma mapping strategy (fallback vs iova) bat things will
> happen?!? should the previous patch always initializing
> head_buf->iova_state?
AFAICT, to switch the IOMMU domain would require unbind the device, changing
the IOMMU type, and re-binding the device... which would destroy all the rings
in the process and thus this wouldn't happen.
The only way I could potentially imagine this happening would be in extreme
IOVA pressure (maybe?):
- packet A in slot N, dma_iova_try_alloc suceeds -> head_buf->iova_state
copied
- completion the packet occurs, dma_iova_destroy is called,
head_buf->iova_state is not cleared
- packet B in slot N, dma_iova_try_alloc fails due to IOVA pressure...
head_buf->iova_state is stale
I'm pretty skeptical that this is a realistic case, TBH.
That said and since it seems my v5 got CR, I can send a v6 with this slight
change to address the case you've mentioned above.
I'll send in a couple hours unless I hear otherwise:
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
index 9c30ee063ef5..7c198847a771 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_gso.c
@@ -142,8 +142,12 @@ netdev_tx_t bnxt_sw_udp_gso_xmit(struct bnxt *bp,
tx_buf->is_sw_gso = last ? BNXT_SW_GSO_LAST : BNXT_SW_GSO_MID;
- /* Store IOVA state on the last segment for completion */
- if (last && tso_dma_map_use_iova(&map)) {
+ /* Store IOVA state on the last segment for completion.
+ * Always copy so that a stale iova_state from a prior
+ * occupant of this ring slot cannot be misread by
+ * dma_use_iova() in the completion path.
+ */
+ if (last) {
tx_buf->iova_state = map.iova_state;
tx_buf->iova_total_len = map.total_len;
}
^ permalink raw reply related
* Re: [PATCH net-next v8 5/6] Fix error handling in probe function.
From: Russell King (Oracle) @ 2026-03-26 16:57 UTC (permalink / raw)
To: Jitendra Vegiraju
Cc: netdev, alexandre.torgue, davem, edumazet, kuba, pabeni,
mcoquelin.stm32, bcm-kernel-feedback-list, richardcochran, ast,
daniel, hawk, john.fastabend, rohan.g.thomas, linux-kernel,
linux-stm32, linux-arm-kernel, bpf, andrew+netdev, horms, sdf, me,
siyanteng, prabhakar.mahadev-lad.rj, weishangjuan, wens,
vladimir.oltean, lizhi2, boon.khai.ng, maxime.chevallier,
chenchuangyu, yangtiezhu, ovidiu.panait.rb, chenhuacai,
florian.fainelli, quic_abchauha
In-Reply-To: <20260320211921.1202058-6-jitendra.vegiraju@broadcom.com>
On Fri, Mar 20, 2026 at 02:19:20PM -0700, Jitendra Vegiraju wrote:
> From: Jitendra Vegiraju <jitendra.vegiraju@broadcom.com>
>
> Software node created in probe function is not being cleaned up if
> the probe function returns an error.
> The stmmac core provides mechanism to handle this error condition
> with plat->init, plat->exit helper functions.
> Move glue driver's initialization code to plat->init function.
> If the probe function returns an error, plat->exit function is
> called. Handle any glue driver level cleanup in the plat->exit
> handler.
> Use devm_add_action_or_reset() to register a callback to free
> irq vectors automatically, simplifying error handling in probe().
>
> Suggested-by: Russell King (Oracle) <linux@armlinux.org.uk>
> Signed-off-by: Jitendra Vegiraju <jitendra.vegiraju@broadcom.com>
Oh, you did fix it. Please merge this into patch 4, there is no need
to have this fix seperate.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply
* Re: [PATCH net-next v2 4/4] net: phy: Introduce Airoha AN8801/R Gigabit Ethernet PHY driver
From: Andrew Lunn @ 2026-03-26 16:56 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Andrew Lunn, Louis-Alexis Eyraud, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, AngeloGioacchino Del Regno, Heiner Kallweit,
kevin-kw.huang, macpaul.lin, matthias.bgg, kernel, netdev,
devicetree, linux-arm-kernel, linux-mediatek, linux-kernel
In-Reply-To: <acVQQQiciPQBkQOG@shell.armlinux.org.uk>
> What is the timing requirements for a system going into suspend vs a WoL
> packet being sent? Should a WoL packet abort entry into suspend? If yes,
> then we need to program the MAC before the PHY is suspended, because
> suspend could already be in progress.
We would then need to hook into the NETDEV_CHANGEADDR notifier, and
call into the PHY driver to let it reprogram the MAC address.
Andrew
^ permalink raw reply
* Re: [PATCH net-next v8 4/6] Add PCI driver support for BCM8958x
From: Russell King (Oracle) @ 2026-03-26 16:55 UTC (permalink / raw)
To: Jitendra Vegiraju
Cc: netdev, alexandre.torgue, davem, edumazet, kuba, pabeni,
mcoquelin.stm32, bcm-kernel-feedback-list, richardcochran, ast,
daniel, hawk, john.fastabend, rohan.g.thomas, linux-kernel,
linux-stm32, linux-arm-kernel, bpf, andrew+netdev, horms, sdf, me,
siyanteng, prabhakar.mahadev-lad.rj, weishangjuan, wens,
vladimir.oltean, lizhi2, boon.khai.ng, maxime.chevallier,
chenchuangyu, yangtiezhu, ovidiu.panait.rb, chenhuacai,
florian.fainelli, quic_abchauha
In-Reply-To: <20260320211921.1202058-5-jitendra.vegiraju@broadcom.com>
On Fri, Mar 20, 2026 at 02:19:19PM -0700, Jitendra Vegiraju wrote:
> +static const struct property_entry fixed_link_properties[] = {
> + PROPERTY_ENTRY_U32("speed", 10000),
> + PROPERTY_ENTRY_BOOL("full-duplex"),
> + PROPERTY_ENTRY_BOOL("pause"),
> + { }
> +};
> +
> +static const struct software_node parent_swnode = {
> + .name = "phy-device",
> +};
> +
> +static const struct software_node fixed_link_swnode = {
> + .name = "fixed-link", /* MUST be named "fixed-link" */
> + .parent = &parent_swnode,
> + .properties = fixed_link_properties,
> +};
> +
> +static const struct software_node *brcm_swnodes[] = {
> + &parent_swnode,
> + &fixed_link_swnode,
> + NULL
> +};
Looking at this structure, I'm not sure it's correct. You seem to have:
pci_device
- "phy-device" swnode attached here (which describes the PCI device,
which isn't any kind of PHY)
- "fixed-link" attached as a child
The "fixed-link" is a property for the local network device which
signifies that there isn't a PHY attached or there's an inaccessible
PHY that only operates with one set of settings.
Maybe rename "phy-device" to "ethernet"?
> +
> +struct brcm_priv_data {
> + void __iomem *mbox_regs; /* MBOX Registers*/
> + void __iomem *misc_regs; /* MISC Registers*/
> + void __iomem *xgmac_regs; /* XGMAC Registers*/
> +};
> +
> +struct dwxgmac_brcm_pci_info {
> + int (*setup)(struct pci_dev *pdev, struct plat_stmmacenet_data *plat);
> +};
> +
> +static void misc_iowrite(struct brcm_priv_data *brcm_priv,
> + u32 reg, u32 val)
> +{
> + iowrite32(val, brcm_priv->misc_regs + reg);
> +}
> +
> +static void dwxgmac_brcm_common_default_data(struct plat_stmmacenet_data *plat)
> +{
> + int i;
> +
> + plat->force_sf_dma_mode = true;
> + plat->mac_port_sel_speed = SPEED_10000;
> + plat->clk_ptp_rate = 125000000;
> + plat->clk_ref_rate = 250000000;
> + plat->tx_coe = true;
> + plat->rx_coe = STMMAC_RX_COE_TYPE1;
> + plat->rss_en = 1;
> + plat->max_speed = SPEED_10000;
> +
> + /* Set default value for multicast hash bins */
> + plat->multicast_filter_bins = HASH_TABLE_SIZE;
Already the default setup by stmmac_plat_dat_alloc().
> +
> + /* Set default value for unicast filter entries */
> + plat->unicast_filter_entries = 1;
Already the default setup by stmmac_plat_dat_alloc().
> +
> + /* Set the maxmtu to device's default */
> + plat->maxmtu = BRCM_MAX_MTU;
> +
> + /* Set default number of RX and TX queues to use */
> + plat->tx_queues_to_use = BRCM_TX_Q_COUNT;
> + plat->rx_queues_to_use = BRCM_RX_Q_COUNT;
> +
> + plat->tx_sched_algorithm = MTL_TX_ALGORITHM_SP;
> + for (i = 0; i < plat->tx_queues_to_use; i++) {
> + plat->tx_queues_cfg[i].use_prio = false;
Already false.
> + plat->tx_queues_cfg[i].prio = 0;
Already zero.
> + plat->tx_queues_cfg[i].mode_to_use = MTL_QUEUE_AVB;
Since MTL_QUEUE_AVB is zero, this is already the case.
> + }
All three points taken together mean that this loop is not required
as all these members are being explicitly set to values of zero,
which they already hold.
> +
> + plat->rx_sched_algorithm = MTL_RX_ALGORITHM_SP;
> + for (i = 0; i < plat->rx_queues_to_use; i++) {
> + plat->rx_queues_cfg[i].use_prio = false;
Already false.
> + plat->rx_queues_cfg[i].mode_to_use = MTL_QUEUE_AVB;
Since MTL_QUEUE_AVB is zero, this is already the case.
> + plat->rx_queues_cfg[i].pkt_route = 0x0;
Already zero.
> + plat->rx_queues_cfg[i].chan = i;
stmmac_plat_dat_alloc() already initialises plat->rx_queues_cfg[].chan.
> + }
Taking all these points together, it means that this loop also isn't
required, since you're not changing anything that hasn't already been
setup.
> +}
> +
> +static int dwxgmac_brcm_default_data(struct pci_dev *pdev,
> + struct plat_stmmacenet_data *plat)
> +{
> + /* Set common default data first */
> + dwxgmac_brcm_common_default_data(plat);
> + plat->core_type = DWMAC_CORE_25GMAC;
> + plat->bus_id = 0;
The underlying devm_kzalloc() which allocates "plat" will clear the
struct to zeros, so this assignment to bus_id shouldn't be necessary.
> + plat->phy_addr = 0;
You said there's no MDIO bus, so I don't think you need to initialise
plat->phy_addr. stmmac_plat_dat_alloc() will set this to -1.
> + plat->phy_interface = PHY_INTERFACE_MODE_XGMII;
> +
> + plat->dma_cfg->pbl = DEFAULT_DMA_PBL;
> + plat->dma_cfg->pblx8 = true;
> + plat->dma_cfg->aal = false;
> + plat->dma_cfg->eame = true;
> +
> + plat->axi->axi_wr_osr_lmt = 31;
> + plat->axi->axi_rd_osr_lmt = 31;
> + plat->axi->axi_fb = false;
devm_kzalloc() which is used to allocate plat->axi in the probe function
will zero out this structure, so axi_fb will already be false.
> + plat->axi->axi_blen_regval = DMA_AXI_BLEN64;
> + return 0;
> +}
> +
> +static struct dwxgmac_brcm_pci_info dwxgmac_brcm_pci_info = {
> + .setup = dwxgmac_brcm_default_data,
> +};
It looks to me like this is a copy of stmmac_pci.c / dwmac-intel.c etc.
Do you know for certain that you're going to need to do different
setups depending on the PCI device?
What's the reasoning for the split between
dwxgmac_brcm_common_default_data() and dwxgmac_brcm_default_data() ?
> +
> +static void brcm_config_misc_regs(struct pci_dev *pdev,
> + struct brcm_priv_data *brcm_priv)
> +{
> + pci_write_config_dword(pdev, XGMAC_PCIE_CFG_MSIX_ADDR_MATCH_LOW,
> + XGMAC_PCIE_CFG_MSIX_ADDR_MATCH_LO_VALUE);
> + pci_write_config_dword(pdev, XGMAC_PCIE_CFG_MSIX_ADDR_MATCH_HIGH,
> + XGMAC_PCIE_CFG_MSIX_ADDR_MATCH_HI_VALUE);
> +
> + misc_iowrite(brcm_priv, XGMAC_PCIE_MISC_MSIX_ADDR_MATCH_LO_OFFSET,
> + XGMAC_PCIE_MISC_MSIX_ADDR_MATCH_LO_VALUE);
> + misc_iowrite(brcm_priv, XGMAC_PCIE_MISC_MSIX_ADDR_MATCH_HI_OFFSET,
> + XGMAC_PCIE_MISC_MSIX_ADDR_MATCH_HI_VALUE);
> +
> + /* Enable Switch Link */
> + misc_iowrite(brcm_priv, XGMAC_PCIE_MISC_MII_CTRL_OFFSET,
> + XGMAC_PCIE_MISC_MII_CTRL_PAUSE_RX |
> + XGMAC_PCIE_MISC_MII_CTRL_PAUSE_TX |
> + XGMAC_PCIE_MISC_MII_CTRL_LINK_UP);
> +}
> +
> +static int brcm_config_multi_msi(struct pci_dev *pdev,
> + struct plat_stmmacenet_data *plat,
> + struct stmmac_resources *res)
> +{
> + int ret;
> + int i;
> +
> + ret = pci_alloc_irq_vectors(pdev, BRCM_XGMAC_MSI_VECTOR_MAX,
> + BRCM_XGMAC_MSI_VECTOR_MAX,
> + PCI_IRQ_MSI | PCI_IRQ_MSIX);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "%s: multi MSI enablement failed\n",
> + __func__);
> + return ret;
> + }
> +
> + /* For RX MSI */
> + for (i = 0; i < plat->rx_queues_to_use; i++)
> + res->rx_irq[i] =
> + pci_irq_vector(pdev,
> + BRCM_XGMAC_MSI_RX_VECTOR_START + i * 2);
> +
> + /* For TX MSI */
> + for (i = 0; i < plat->tx_queues_to_use; i++)
> + res->tx_irq[i] =
> + pci_irq_vector(pdev,
> + BRCM_XGMAC_MSI_TX_VECTOR_START + i * 2);
> +
> + res->irq = pci_irq_vector(pdev, BRCM_XGMAC_MSI_MAC_VECTOR);
> +
> + plat->flags |= STMMAC_FLAG_MULTI_MSI_EN;
> + plat->flags |= STMMAC_FLAG_TSO_EN;
> + plat->flags |= STMMAC_FLAG_SPH_DISABLE;
> + return 0;
> +}
> +
> +static int brcm_pci_resume(struct device *dev, void *bsp_priv)
> +{
> + struct pci_dev *pdev = to_pci_dev(dev);
> +
> + brcm_config_misc_regs(pdev, bsp_priv);
Is it worth declaring struct pdev for one place that it's used?
brcm_config_misc_regs(to_pci_dev(dev), bsp_priv);
should work just as well.
> +
> + return stmmac_pci_plat_resume(dev, bsp_priv);
> +}
> +
> +static int dwxgmac_brcm_pci_probe(struct pci_dev *pdev,
> + const struct pci_device_id *id)
> +{
> + struct dwxgmac_brcm_pci_info *info =
> + (struct dwxgmac_brcm_pci_info *)id->driver_data;
> + struct plat_stmmacenet_data *plat;
> + struct brcm_priv_data *brcm_priv;
> + struct stmmac_resources res;
> + struct device *dev;
> + int rx_offset;
> + int tx_offset;
> + int vector;
> + int ret;
> +
> + dev = &pdev->dev;
As you go to the effort of declaring a struct device pointer, and
assign it, do you think it would be a good idea to either use it for
all &pdev->dev instances below, or just get rid of the two instances
that you actually use "dev" ?
I count six instances of "&pdev->dev" below vs two making use of "dev"
directly.
> +
> + brcm_priv = devm_kzalloc(&pdev->dev, sizeof(*brcm_priv), GFP_KERNEL);
> + if (!brcm_priv)
> + return -ENOMEM;
> +
> + plat = stmmac_plat_dat_alloc(dev);
> + if (!plat)
> + return -ENOMEM;
> +
> + plat->axi = devm_kzalloc(&pdev->dev, sizeof(*plat->axi), GFP_KERNEL);
> + if (!plat->axi)
> + return -ENOMEM;
> +
> + /* This device is directly attached to the switch chip internal to the
> + * SoC using XGMII interface. Since no MDIO is present, register
> + * fixed-link software_node to create phylink.
> + */
> + software_node_register_node_group(brcm_swnodes);
> + device_set_node(dev, software_node_fwnode(&parent_swnode));
> +
> + /* Disable D3COLD as our device does not support it */
> + pci_d3cold_disable(pdev);
> +
> + /* Enable PCI device */
> + ret = pcim_enable_device(pdev);
> + if (ret) {
> + dev_err(&pdev->dev, "%s: ERROR: failed to enable device\n",
> + __func__);
> + return ret;
What about cleaning up the swnodes ?
> + }
> +
> + pci_set_master(pdev);
> +
> + memset(&res, 0, sizeof(res));
> + res.addr = pcim_iomap_region(pdev, 0, pci_name(pdev));
> + if (IS_ERR(res.addr))
> + return dev_err_probe(&pdev->dev, PTR_ERR(res.addr),
> + "failed to map IO region\n");
Convention is to have a blank line here.
> + /* MISC Regs */
> + brcm_priv->misc_regs = res.addr + BRCM_XGMAC_IOMEM_MISC_REG_OFFSET;
> + /* MBOX Regs */
> + brcm_priv->mbox_regs = res.addr + BRCM_XGMAC_IOMEM_MBOX_REG_OFFSET;
> + /* XGMAC config Regs */
> + res.addr += BRCM_XGMAC_IOMEM_CFG_REG_OFFSET;
> + brcm_priv->xgmac_regs = res.addr;
> +
> + plat->suspend = stmmac_pci_plat_suspend;
> + plat->resume = brcm_pci_resume;
> + plat->bsp_priv = brcm_priv;
> +
> + ret = info->setup(pdev, plat);
> + if (ret)
> + return ret;
What about cleaning up the swnodes ?
> +
> + pci_write_config_dword(pdev, XGMAC_PCIE_CFG_MSIX_ADDR_MATCH_LOW,
> + XGMAC_PCIE_CFG_MSIX_ADDR_MATCH_LO_VALUE);
> + pci_write_config_dword(pdev, XGMAC_PCIE_CFG_MSIX_ADDR_MATCH_HIGH,
> + XGMAC_PCIE_CFG_MSIX_ADDR_MATCH_HI_VALUE);
> +
> + misc_iowrite(brcm_priv, XGMAC_PCIE_MISC_MSIX_ADDR_MATCH_LO_OFFSET,
> + XGMAC_PCIE_MISC_MSIX_ADDR_MATCH_LO_VALUE);
> + misc_iowrite(brcm_priv, XGMAC_PCIE_MISC_MSIX_ADDR_MATCH_HI_OFFSET,
> + XGMAC_PCIE_MISC_MSIX_ADDR_MATCH_HI_VALUE);
> +
> + /* SBD Interrupt */
> + misc_iowrite(brcm_priv, XGMAC_PCIE_MISC_MSIX_VECTOR_MAP_SBD_ALL_OFFSET,
> + XGMAC_PCIE_MISC_MSIX_VECTOR_MAP_SBD_ALL_VALUE);
> + /* EP_DOORBELL Interrupt */
> + misc_iowrite(brcm_priv,
> + XGMAC_PCIE_MISC_MSIX_VECTOR_MAP_EP2HOST_DBELL_OFFSET,
> + XGMAC_PCIE_MISC_MSIX_VECTOR_MAP_EP2HOST_DBELL_VALUE);
> + /* EP_H0 Interrupt */
> + misc_iowrite(brcm_priv,
> + XGMAC_PCIE_MISC_MSIX_VECTOR_MAP_EP2HOST0_OFFSET,
> + XGMAC_PCIE_MISC_MSIX_VECTOR_MAP_EP2HOST0_VALUE);
> + /* EP_H1 Interrupt */
> + misc_iowrite(brcm_priv,
> + XGMAC_PCIE_MISC_MSIX_VECTOR_MAP_EP2HOST1_OFFSET,
> + XGMAC_PCIE_MISC_MSIX_VECTOR_MAP_EP2HOST1_VALUE);
> +
> + rx_offset = XGMAC_PCIE_MISC_MSIX_VECTOR_MAP_RX0_PF0_OFFSET;
> + tx_offset = XGMAC_PCIE_MISC_MSIX_VECTOR_MAP_TX0_PF0_OFFSET;
> + vector = BRCM_XGMAC_MSI_RX_VECTOR_START;
> + for (int i = 0; i < BRCM_MAX_DMA_CHANNEL_PAIRS; i++) {
> + /* RX Interrupt */
> + misc_iowrite(brcm_priv, rx_offset, vector++);
> + /* TX Interrupt */
> + misc_iowrite(brcm_priv, tx_offset, vector++);
> + rx_offset += 4;
> + tx_offset += 4;
> + }
It looks like this device can program the MSI vector numbers. Does
it make sense to interleave them, or would it be simpler to have
all the receive vectors and then all the transmit vectors?
This also hard-codes the fact that BRCM_XGMAC_MSI_TX_VECTOR_START
is one more than BRCM_XGMAC_MSI_RX_VECTOR_START, which isn't nice
given that you use these macros when claiming the MSI vectors.
> +
> + /* Enable Switch Link */
> + misc_iowrite(brcm_priv, XGMAC_PCIE_MISC_MII_CTRL_OFFSET,
> + XGMAC_PCIE_MISC_MII_CTRL_PAUSE_RX |
> + XGMAC_PCIE_MISC_MII_CTRL_PAUSE_TX |
> + XGMAC_PCIE_MISC_MII_CTRL_LINK_UP);
> + /* Enable MSI-X */
> + misc_iowrite(brcm_priv, XGMAC_PCIE_MISC_PCIESS_CTRL_OFFSET,
> + XGMAC_PCIE_MISC_PCIESS_CTRL_EN_MSI_MSIX);
> +
> + ret = brcm_config_multi_msi(pdev, plat, &res);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "%s: ERROR: failed to enable IRQ\n", __func__);
> + goto err_disable_msi;
> + }
> +
> + ret = stmmac_dvr_probe(&pdev->dev, plat, &res);
> + if (ret)
> + goto err_disable_msi;
> +
> + return ret;
> +
> +err_disable_msi:
> + pci_free_irq_vectors(pdev);
This is still buggy. What about cleaning up the swnodes?
> +
> + return ret;
> +}
> +
> +static void dwxgmac_brcm_pci_remove(struct pci_dev *pdev)
> +{
> + stmmac_dvr_remove(&pdev->dev);
> + pci_free_irq_vectors(pdev);
> + device_set_node(&pdev->dev, NULL);
> + software_node_unregister_node_group(brcm_swnodes);
As the remove function does way more cleanup than the probe function,
this is a sign that the probe function is buggy. This is exactly why
I suggested using ->init and ->exit in the previous review. I seem
to have been ignored on that though... and the problem I already
pointed out remains.
Thanks.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply
* Re: [RFC PATCH net-next 2/3] seg6: add SRv6 L2 tunnel device (srl2)
From: Nicolas Dichtel @ 2026-03-26 16:44 UTC (permalink / raw)
To: Andrea Mayer, netdev
Cc: David S . Miller, David Ahern, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Stefano Salsano, Paolo Lungaroni,
Ahmed Abdelsalam, Justin Iurman, linux-kernel
In-Reply-To: <20260322000557.12559-3-andrea.mayer@uniroma2.it>
Le 22/03/2026 à 01:05, Andrea Mayer a écrit :
> Introduce srl2, an Ethernet pseudowire device over SRv6. It
> encapsulates L2 frames in IPv6 with a Segment Routing Header for
> transmission across an SRv6 network.
>
> The encapsulation logic reuses seg6_do_srh_encap() with
> IPPROTO_ETHERNET. The transmit path uses the standard IPv6 tunnel
> infrastructure (dst_cache, ip6_route_output, ip6tunnel_xmit).
>
> The device is configured with a segment list for point-to-point
> L2 encapsulation.
>
> Usage:
>
> ip link add srl2-0 type srl2 segs fc00::a,fc00::b
>
> Co-developed-by: Stefano Salsano <stefano.salsano@uniroma2.it>
> Signed-off-by: Stefano Salsano <stefano.salsano@uniroma2.it>
> Signed-off-by: Andrea Mayer <andrea.mayer@uniroma2.it>
> ---
> include/linux/srl2.h | 7 +
> include/uapi/linux/srl2.h | 20 +++
> net/ipv6/Kconfig | 16 +++
> net/ipv6/Makefile | 1 +
> net/ipv6/seg6.c | 1 +
> net/ipv6/srl2.c | 269 ++++++++++++++++++++++++++++++++++++++
> 6 files changed, 314 insertions(+)
> create mode 100644 include/linux/srl2.h
> create mode 100644 include/uapi/linux/srl2.h
> create mode 100644 net/ipv6/srl2.c
>
> diff --git a/include/linux/srl2.h b/include/linux/srl2.h
> new file mode 100644
> index 000000000000..c1342b979402
> --- /dev/null
> +++ b/include/linux/srl2.h
> @@ -0,0 +1,7 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +#ifndef _LINUX_SRL2_H
> +#define _LINUX_SRL2_H
> +
> +#include <uapi/linux/srl2.h>
> +
> +#endif
Is this really needed?
> diff --git a/include/uapi/linux/srl2.h b/include/uapi/linux/srl2.h
> new file mode 100644
> index 000000000000..e7c8f6fc0791
> --- /dev/null
> +++ b/include/uapi/linux/srl2.h
> @@ -0,0 +1,20 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */
> +/*
> + * SRv6 L2 tunnel device
> + *
> + * Author:
> + * Andrea Mayer <andrea.mayer@uniroma2.it>
> + */
> +
> +#ifndef _UAPI_LINUX_SRL2_H
> +#define _UAPI_LINUX_SRL2_H
> +
> +enum {
> + IFLA_SRL2_UNSPEC,
> + IFLA_SRL2_SRH, /* binary: struct ipv6_sr_hdr + segments */
> + __IFLA_SRL2_MAX,
> +};
> +
> +#define IFLA_SRL2_MAX (__IFLA_SRL2_MAX - 1)
It should probably be generated automatically from specs, see
https://docs.kernel.org/userspace-api/netlink/intro-specs.html
> +
> +#endif
> diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
> index b8f9a8c0302e..9c8f7e254435 100644
> --- a/net/ipv6/Kconfig
> +++ b/net/ipv6/Kconfig
> @@ -318,6 +318,22 @@ config IPV6_SEG6_BPF
> depends on IPV6_SEG6_LWTUNNEL
> depends on IPV6 = y
>
> +config IPV6_SRL2
> + tristate "IPv6: SRv6 L2 tunnel device"
> + depends on IPV6_SEG6_LWTUNNEL
> + select DST_CACHE
> + help
> + SRv6 virtual Ethernet device that encapsulates L2 frames in
> + IPv6 with a Segment Routing Header (SRH) for transmission
> + over an SRv6 network.
> + Intended for use with a remote seg6local L2 decapsulation
> + behavior, such as End.DT2U or End.DX2.
> +
> + To compile this as a module, choose M here: the module will
> + be called srl2.
> +
> + If unsure, say N.
> +
> config IPV6_RPL_LWTUNNEL
> bool "IPv6: RPL Source Routing Header support"
> depends on IPV6
> diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
> index 2c9ce2ccbde1..a7e81d0293ca 100644
> --- a/net/ipv6/Makefile
> +++ b/net/ipv6/Makefile
> @@ -24,6 +24,7 @@ ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o
> ipv6-$(CONFIG_NETLABEL) += calipso.o
> ipv6-$(CONFIG_IPV6_SEG6_LWTUNNEL) += seg6_iptunnel.o seg6_local.o
> ipv6-$(CONFIG_IPV6_SEG6_HMAC) += seg6_hmac.o
> +obj-$(CONFIG_IPV6_SRL2) += srl2.o
> ipv6-$(CONFIG_IPV6_RPL_LWTUNNEL) += rpl_iptunnel.o
> ipv6-$(CONFIG_IPV6_IOAM6_LWTUNNEL) += ioam6_iptunnel.o
>
> diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c
> index 1c3ad25700c4..23213ab4fefd 100644
> --- a/net/ipv6/seg6.c
> +++ b/net/ipv6/seg6.c
> @@ -72,6 +72,7 @@ bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len, bool reduced)
>
> return true;
> }
> +EXPORT_SYMBOL_GPL(seg6_validate_srh);
>
> struct ipv6_sr_hdr *seg6_get_srh(struct sk_buff *skb, int flags)
> {
> diff --git a/net/ipv6/srl2.c b/net/ipv6/srl2.c
> new file mode 100644
> index 000000000000..66aa5375d218
> --- /dev/null
> +++ b/net/ipv6/srl2.c
> @@ -0,0 +1,269 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * SRv6 L2 tunnel device (srl2)
> + *
> + * A virtual Ethernet device that encapsulates L2 frames in IPv6 with a
> + * Segment Routing Header (SRH) for transmission over an SRv6 network.
> + * On the remote side, a seg6_local behavior such as End.DT2U or End.DX2
> + * decapsulates the inner Ethernet frame for L2 delivery.
> + *
> + * The encapsulation logic reuses seg6_do_srh_encap() from seg6_iptunnel.c
> + * with IPPROTO_ETHERNET (143). The transmit path uses the standard IPv6
> + * tunnel infrastructure (dst_cache, ip6_route_output, ip6tunnel_xmit).
> + *
> + * Authors:
> + * Andrea Mayer <andrea.mayer@uniroma2.it>
> + * Stefano Salsano <stefano.salsano@uniroma2.it>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <net/dst_cache.h>
> +#include <net/ip6_route.h>
> +#include <net/ip_tunnels.h>
> +#include <net/ip6_tunnel.h>
> +#include <net/seg6.h>
> +#include <linux/seg6.h>
> +#include <linux/srl2.h>
> +
> +/* Conservative initial estimate for SRH size before newlink provides
> + * the actual value. 256 bytes accommodates up to 15 SIDs.
> + */
> +#define SRL2_SRH_HEADROOM_EST 256
> +
> +struct srl2_priv {
> + struct ipv6_sr_hdr *srh;
> + struct dst_cache dst_cache;
> +};
> +
> +/*
> + * srl2_xmit - encapsulate an L2 frame in IPv6+SRH and transmit
> + *
> + * When the bridge (or local stack) sends a frame through this device,
> + * skb->data points to the inner Ethernet header. We look up a route
> + * towards the first SID, prepend the outer IPv6+SRH via
> + * seg6_do_srh_encap(), and transmit via ip6tunnel_xmit().
> + *
> + * The route lookup result is cached per-cpu in dst_cache. Since the
> + * first SID is constant for the lifetime of the device, the cache
> + * avoids repeated route lookups in the common case.
> + */
> +static netdev_tx_t srl2_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> + struct srl2_priv *priv = netdev_priv(dev);
> + struct net *net = dev_net(dev);
> + struct dst_entry *dst;
> + struct flowi6 fl6;
> + int err;
> +
> + local_bh_disable();
> + dst = dst_cache_get(&priv->dst_cache);
> + local_bh_enable();
> +
> + if (unlikely(!dst)) {
> + memset(&fl6, 0, sizeof(fl6));
> + fl6.daddr = priv->srh->segments[priv->srh->first_segment];
> +
> + dst = ip6_route_output(net, NULL, &fl6);
> + if (dst->error) {
> + dst_release(dst);
> + DEV_STATS_INC(dev, tx_carrier_errors);
> + goto drop;
> + }
> +
> + if (dst_dev(dst) == dev) {
> + dst_release(dst);
> + DEV_STATS_INC(dev, collisions);
> + goto drop;
> + }
> +
> + local_bh_disable();
> + /* saddr is unused */
> + dst_cache_set_ip6(&priv->dst_cache, dst, &fl6.saddr);
> + local_bh_enable();
> + }
> +
> + skb_scrub_packet(skb, false);
> +
> + skb_dst_set(skb, dst);
> +
> + err = seg6_do_srh_encap(skb, priv->srh, IPPROTO_ETHERNET);
> + if (unlikely(err)) {
> + DEV_STATS_INC(dev, tx_errors);
> + kfree_skb(skb);
> + return NETDEV_TX_OK;
> + }
> +
> + skb->protocol = htons(ETH_P_IPV6);
> +
> + ip6tunnel_xmit(NULL, skb, dev, 0);
> +
> + return NETDEV_TX_OK;
> +
> +drop:
> + DEV_STATS_INC(dev, tx_dropped);
> + kfree_skb(skb);
> + return NETDEV_TX_OK;
> +}
> +
> +static int srl2_dev_init(struct net_device *dev)
> +{
> + struct srl2_priv *priv = netdev_priv(dev);
> +
> + return dst_cache_init(&priv->dst_cache, GFP_KERNEL);
> +}
> +
> +static void srl2_dev_uninit(struct net_device *dev)
> +{
> + struct srl2_priv *priv = netdev_priv(dev);
> +
> + dst_cache_destroy(&priv->dst_cache);
> +}
> +
> +static void srl2_dev_free(struct net_device *dev)
> +{
> + struct srl2_priv *priv = netdev_priv(dev);
> +
> + kfree(priv->srh);
> +}
> +
> +static const struct net_device_ops srl2_netdev_ops = {
> + .ndo_init = srl2_dev_init,
> + .ndo_uninit = srl2_dev_uninit,
> + .ndo_start_xmit = srl2_xmit,
> + .ndo_set_mac_address = eth_mac_addr,
> + .ndo_validate_addr = eth_validate_addr,
> +};
> +
> +static void srl2_setup(struct net_device *dev)
> +{
> + ether_setup(dev);
> +
> + dev->netdev_ops = &srl2_netdev_ops;
> + dev->needs_free_netdev = true;
> + dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS;
> + dev->needed_headroom = LL_MAX_HEADER + sizeof(struct ipv6hdr) +
> + SRL2_SRH_HEADROOM_EST;
> +
> + dev->priv_flags &= ~IFF_TX_SKB_SHARING;
> + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE;
> + dev->lltx = true;
> +
Maybe setting dev->netns_immutable to true ?
Regards,
Nicolas
> + eth_hw_addr_random(dev);
> +}
> +
> +static const struct nla_policy srl2_policy[IFLA_SRL2_MAX + 1] = {
> + [IFLA_SRL2_SRH] = { .type = NLA_BINARY },
> +};
> +
> +static int srl2_validate(struct nlattr *tb[], struct nlattr *data[],
> + struct netlink_ext_ack *extack)
> +{
> + if (!data || !data[IFLA_SRL2_SRH]) {
> + NL_SET_ERR_MSG(extack, "SRH with segment list is required");
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int srl2_newlink(struct net_device *dev,
> + struct rtnl_newlink_params *params,
> + struct netlink_ext_ack *extack)
> +{
> + struct srl2_priv *priv = netdev_priv(dev);
> + struct nlattr **data = params->data;
> + struct ipv6_sr_hdr *srh;
> + int srhlen;
> + int len;
> +
> + srh = nla_data(data[IFLA_SRL2_SRH]);
> + len = nla_len(data[IFLA_SRL2_SRH]);
> +
> + if (len < sizeof(*srh) + sizeof(struct in6_addr)) {
> + NL_SET_ERR_MSG(extack, "SRH too short");
> + return -EINVAL;
> + }
> +
> + if (!seg6_validate_srh(srh, len, false)) {
> + NL_SET_ERR_MSG(extack, "Invalid SRH");
> + return -EINVAL;
> + }
> +
> + priv->srh = kmemdup(srh, len, GFP_KERNEL);
> + if (!priv->srh)
> + return -ENOMEM;
> +
> + srhlen = ipv6_optlen(srh);
> +
> + dev->needed_headroom = LL_MAX_HEADER + sizeof(struct ipv6hdr) + srhlen;
> +
> + /* dev->mtu is the inner L3 payload size. Since SRv6 encapsulation
> + * carries the full inner Ethernet frame, subtract both the outer
> + * IPv6+SRH overhead and ETH_HLEN from ETH_DATA_LEN.
> + */
> + dev->mtu = ETH_DATA_LEN - sizeof(struct ipv6hdr) - srhlen - ETH_HLEN;
> + dev->min_mtu = ETH_MIN_MTU;
> + dev->max_mtu = IP_MAX_MTU - sizeof(struct ipv6hdr) - srhlen - ETH_HLEN;
> +
> + dev->priv_destructor = srl2_dev_free;
> +
> + return register_netdevice(dev);
> +}
> +
> +static void srl2_dellink(struct net_device *dev, struct list_head *head)
> +{
> + unregister_netdevice_queue(dev, head);
> +}
> +
> +static size_t srl2_get_size(const struct net_device *dev)
> +{
> + const struct srl2_priv *priv = netdev_priv(dev);
> + int srhlen = ipv6_optlen(priv->srh);
> +
> + return nla_total_size(srhlen);
> +}
> +
> +static int srl2_fill_info(struct sk_buff *skb, const struct net_device *dev)
> +{
> + const struct srl2_priv *priv = netdev_priv(dev);
> + int srhlen = ipv6_optlen(priv->srh);
> +
> + if (nla_put(skb, IFLA_SRL2_SRH, srhlen, priv->srh))
> + return -EMSGSIZE;
> +
> + return 0;
> +}
> +
> +static struct rtnl_link_ops srl2_link_ops __read_mostly = {
> + .kind = "srl2",
> + .maxtype = IFLA_SRL2_MAX,
> + .policy = srl2_policy,
> + .priv_size = sizeof(struct srl2_priv),
> + .setup = srl2_setup,
> + .validate = srl2_validate,
> + .newlink = srl2_newlink,
> + .dellink = srl2_dellink,
> + .get_size = srl2_get_size,
> + .fill_info = srl2_fill_info,
> +};
> +
> +static int __init srl2_init(void)
> +{
> + return rtnl_link_register(&srl2_link_ops);
> +}
> +
> +static void __exit srl2_exit(void)
> +{
> + rtnl_link_unregister(&srl2_link_ops);
> +}
> +
> +module_init(srl2_init);
> +module_exit(srl2_exit);
> +
> +MODULE_AUTHOR("Andrea Mayer <andrea.mayer@uniroma2.it>");
> +MODULE_AUTHOR("Stefano Salsano <stefano.salsano@uniroma2.it>");
> +MODULE_DESCRIPTION("SRv6 L2 tunnel device");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS_RTNL_LINK("srl2");
^ permalink raw reply
* Re: [PATCH v6 1/2] PCI: AtomicOps: Do not enable without support in root complex
From: Bjorn Helgaas @ 2026-03-26 16:40 UTC (permalink / raw)
To: Gerd Bayer
Cc: Bjorn Helgaas, Jay Cornwall, Felix Kuehling,
Christian Borntraeger, Niklas Schnelle, Gerald Schaefer,
Heiko Carstens, Vasily Gorbik, Alexander Gordeev, Sven Schnelle,
Leon Romanovsky, Alexander Schmidt, linux-s390, linux-pci,
linux-kernel, netdev, linux-rdma, stable, Gerd Bayer
In-Reply-To: <4183b471777efa949ce6f7b860c81282e91666ef.camel@linux.ibm.com>
On Thu, Mar 26, 2026 at 10:51:19AM +0100, Gerd Bayer wrote:
> On Wed, 2026-03-25 at 15:08 -0500, Bjorn Helgaas wrote:
> > On Wed, Mar 25, 2026 at 04:16:17PM +0100, Gerd Bayer wrote:
> > > When inspecting the config space of a Connect-X physical function in an
> > > s390 system after it was initialized by the mlx5_core device driver, we
> > > found the function to be enabled to request AtomicOps despite the
> > > system's root-complex lacking support for completing them:
> > >
> > > 1ed0:00:00.1 Ethernet controller: Mellanox Technologies MT2894 Family [ConnectX-6 Lx]
> > > Subsystem: Mellanox Technologies Device 0002
> > > [...]
> > > DevCtl2: Completion Timeout: 50us to 50ms, TimeoutDis-
> > > AtomicOpsCtl: ReqEn+
> > > IDOReq- IDOCompl- LTR- EmergencyPowerReductionReq-
> > > 10BitTagReq- OBFF Disabled, EETLPPrefixBlk-
> > >
> > > Turns out the device driver calls pci_enable_atomic_ops_to_root() which
> > > defaulted to enable AtomicOps requests even if it had no information
> > > about the root-port that the PCIe device is attached to. Similarly,
> > > AtomicOps requests are enabled for root complex integrated endpoints
> > > (RCiEPs) unconditionally.
> > >
> > > Change the logic of pci_enable_atomic_ops_to_root() to fully traverse the
> > > PCIe tree upwards, check that the bridge devices support delivering
> > > AtomicOps transactions, and finally check that there is a root port at
> > > the end that does support completing AtomicOps - or that the support for
> > > completing AtomicOps at the root complex is announced through some other
> > > arch specific way.
> > >
> > > Introduce a new pcibios_connects_to_atomicops_capable_rc() function to
> > > implement the check - and default to always "true". This leaves the
> > > semantics for today's RCiEPs intact. Pass in the device in question and
> > > the requested capabilities for future expansions.
> > > For s390, override pcibios_connects_to_atomicops_capable_rc() to
> > > always return "false".
> > >
> > > Do not change the enablement of AtomicOps requests if there is no
> > > positive confirmation that the root complex can complete PCIe AtomicOps.
> > >
> > > Reported-by: Alexander Schmidt <alexs@linux.ibm.com>
> > > Cc: stable@vger.kernel.org
> > > Fixes: 430a23689dea ("PCI: Add pci_enable_atomic_ops_to_root()")
> > > Signed-off-by: Gerd Bayer <gbayer@linux.ibm.com>
> > > ---
> > > arch/s390/pci/pci.c | 5 +++++
> > > drivers/pci/pci.c | 48 +++++++++++++++++++++++++++++++-----------------
> > > include/linux/pci.h | 1 +
> > > 3 files changed, 37 insertions(+), 17 deletions(-)
> > >
> > > diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
> > > index 2a430722cbe415dd56c92fed2e513e524f46481a..a0bef77082a153a258fbe4abb1070b22e020888e 100644
> > > --- a/arch/s390/pci/pci.c
> > > +++ b/arch/s390/pci/pci.c
> > > @@ -265,6 +265,11 @@ static int zpci_cfg_store(struct zpci_dev *zdev, int offset, u32 val, u8 len)
> > > return rc;
> > > }
> > >
> > > +bool pcibios_connects_to_atomicops_capable_rc(struct pci_dev *dev, u32 cap_mask)
> > > +{
> > > + return false;
> > > +}
> > > +
> > > resource_size_t pcibios_align_resource(void *data, const struct resource *res,
> > > resource_size_t size,
> > > resource_size_t align)
> > > diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
> > > index 8479c2e1f74f1044416281aba11bf071ea89488a..006aa589926cb290de43f152100ddaf9961407d1 100644
> > > --- a/drivers/pci/pci.c
> > > +++ b/drivers/pci/pci.c
> > > @@ -3660,6 +3660,19 @@ void pci_acs_init(struct pci_dev *dev)
> > > pci_disable_broken_acs_cap(dev);
> > > }
> > >
> > > +static bool pci_is_atomicops_capable_rp(struct pci_dev *dev, u32 cap, u32 cap_mask)
> > > +{
> > > + if (!dev || !(pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT))
> > > + return false;
> > > +
> > > + return (cap & cap_mask) == cap_mask;
> > > +}
> > > +
> > > +bool __weak pcibios_connects_to_atomicops_capable_rc(struct pci_dev *dev, u32 cap_mask)
> > > +{
> > > + return true;
> > > +}
> > > +
> > > /**
> > > * pci_enable_atomic_ops_to_root - enable AtomicOp requests to root port
> > > * @dev: the PCI device
> > > @@ -3676,8 +3689,9 @@ void pci_acs_init(struct pci_dev *dev)
> > > int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask)
> > > {
> > > struct pci_bus *bus = dev->bus;
> > > - struct pci_dev *bridge;
> > > - u32 cap, ctl2;
> > > + struct pci_dev *bridge = NULL;
> > > + u32 cap = 0;
> > > + u32 ctl2;
> > >
> > > /*
> > > * Per PCIe r5.0, sec 9.3.5.10, the AtomicOp Requester Enable bit
> > > @@ -3714,29 +3728,29 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask)
> > > switch (pci_pcie_type(bridge)) {
> > > /* Ensure switch ports support AtomicOp routing */
> > > case PCI_EXP_TYPE_UPSTREAM:
> > > - case PCI_EXP_TYPE_DOWNSTREAM:
> > > - if (!(cap & PCI_EXP_DEVCAP2_ATOMIC_ROUTE))
> > > - return -EINVAL;
> > > - break;
> > > -
> > > - /* Ensure root port supports all the sizes we care about */
> > > - case PCI_EXP_TYPE_ROOT_PORT:
> > > - if ((cap & cap_mask) != cap_mask)
> > > - return -EINVAL;
> > > - break;
> > > - }
> > > -
> > > - /* Ensure upstream ports don't block AtomicOps on egress */
> > > - if (pci_pcie_type(bridge) == PCI_EXP_TYPE_UPSTREAM) {
> > > + /* Upstream ports must not block AtomicOps on egress */
> > > pcie_capability_read_dword(bridge, PCI_EXP_DEVCTL2,
> > > &ctl2);
> > > if (ctl2 & PCI_EXP_DEVCTL2_ATOMIC_EGRESS_BLOCK)
> > > return -EINVAL;
> > > + fallthrough;
> > > + /* All switch ports need to route AtomicOps */
> > > + case PCI_EXP_TYPE_DOWNSTREAM:
> > > + if (!(cap & PCI_EXP_DEVCAP2_ATOMIC_ROUTE))
> > > + return -EINVAL;
> > > + break;
> > > }
> > > -
> > > bus = bus->parent;
> > > }
> > >
> > > + /*
> > > + * Finally, last bridge must be root port and support requested sizes
> > > + * or firmware asserts support
> > > + */
> > > + if (!(pci_is_atomicops_capable_rp(bridge, cap, cap_mask) ||
> > > + pcibios_connects_to_atomicops_capable_rc(dev, cap_mask)))
> > > + return -EINVAL;
> >
> > Sashiko says:
> >
> > Since the generic weak implementation of
> > pcibios_connects_to_atomicops_capable_rc() unconditionally returns
> > true, the logical OR expression pci_is_atomicops_capable_rp(...) ||
> > true will always evaluate to true. This makes the entire if
> > condition evaluate to false.
> >
> > Because of this, it appears -EINVAL is never returned here, and any
> > standard endpoint behind a Root Port will successfully be granted
> > AtomicOps even if the Root Port lacks the capability in its
> > PCI_EXP_DEVCAP2 register.
>
> I've made the generic implementation of
> pcibios_connects_to_atomicops_capable_rc() default to return "true" to
> preserve the current code's handling of RCiEPs: Since they are not
> attached to a root port, their dev->bus->parent is NULL and the entire
> while-loop is bypassed - before this patch and after. (Sashiko was
> pointing at that being regressed with v4.)
The v4 patch definitely changed the behavior for RCiEPs: the current
v7.0-rc1 code always enables AtomicOps for RCiEPs, and the v4 patch
never enables AtomicOps for RCiEPs. But I'm not sure this is a
regression. It definitely *could* break an RCiEP, but AFAIK we have
no information about whether the RC supports AtomicOps, so enabling
them and telling the driver that AtomicOps work might be a lie.
IIUC, the motivation for this series was to avoid enabling AtomicOps
on s390 where there is no visible Root Port, and you have platform
knowledg that whatever is upstream from the endpoint in fact does not
support them.
I think we should avoid enabling AtomicOps unless we know for certain
that the completer (Root Port or RC) supports them. To me that sounds
like:
1) Never enable AtomicOps for RCiEPs.
2) Only enable AtomicOps for endpoints below a Root Port that
supports AtomicOps.
This could be two separate patches, where the second would fix the
s390 issue reported by Alexander.
If we come across RCiEPs that need AtomicOps and we somehow know that
the RC supports them, we can add a quirk or something to take
advantage of it.
We are still hand-waving about peer-to-peer transactions; we don't
even try to account for that because we don't know what peer might be
the completer.
> The whole point of pcibios_connects_to_atomicops_capable_rc() is to
> allow different architectures to implement a discriminator outside of
> PCIe's structure - potentially depending on CPU model or more.
>
> The only point I wonder about: Should
> pcibios_connects_to_atomicops_capable_rc() default to return "false"
> and deliberately change the behavior for today's RCiEP's (if there are
> any...)?
>
> >
> > > +
> > > pcie_capability_set_word(dev, PCI_EXP_DEVCTL2,
> > > PCI_EXP_DEVCTL2_ATOMIC_REQ);
> > > return 0;
> > > diff --git a/include/linux/pci.h b/include/linux/pci.h
> > > index 1c270f1d512301de4d462fe7e5097c32af5c6f8d..ef90604c39859ea8e61e5392d0bdaa1b0e43874b 100644
> > > --- a/include/linux/pci.h
> > > +++ b/include/linux/pci.h
> > > @@ -692,6 +692,7 @@ void pci_set_host_bridge_release(struct pci_host_bridge *bridge,
> > > void *release_data);
> > >
> > > int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge);
> > > +bool pcibios_connects_to_atomicops_capable_rc(struct pci_dev *dev, u32 cap_mask);
> > >
> > > #define PCI_REGION_FLAG_MASK 0x0fU /* These bits of resource flags tell us the PCI region flags */
> > >
> > >
> > > --
> > > 2.51.0
> > >
>
> Thanks,
> Gerd
^ permalink raw reply
* [PATCH v4 net-next 8/8] ice: add TX reference clock (tx_clk) control for E825 devices
From: Grzegorz Nitka @ 2026-03-26 16:28 UTC (permalink / raw)
To: netdev
Cc: linux-kernel, intel-wired-lan, poros, richardcochran,
andrew+netdev, przemyslaw.kitszel, anthony.l.nguyen,
Prathosh.Satish, ivecera, jiri, arkadiusz.kubalewski,
vadim.fedorenko, donald.hunter, horms, pabeni, kuba, davem,
edumazet, Grzegorz Nitka
In-Reply-To: <20260326162832.3135857-1-grzegorz.nitka@intel.com>
Add full support for selecting and controlling the TX SERDES reference
clock on E825C hardware. E825C devicede supports selecting among
multiple SERDES transmit reference clock sources (ENET, SyncE, EREF0),
but imposes several routing constraints: on some paths a reference
must be enabled on both PHY complexes, and ports sharing a PHY must
coordinate usage so that a reference is not disabled while still in
active use. Until now the driver did not expose this domain through
the DPLL API, nor did it provide a coherent control layer for enabling,
switching, or tracking TX reference clocks.
This patch implements full TX reference clock management for E825
devices. Compared to previous iterations, the logic is now separated
into a dedicated module (ice_txclk.c) which encapsulates all
clock-selection rules, cross‑PHY dependencies, and the bookkeeping
needed to ensure safe transitions. This allows the DPLL layer and
the PTP code to remain focused on their respective roles.
Key additions:
* A new txclk control module (`ice_txclk.c`) implementing:
- software usage tracking for each reference clock per PHY,
- peer‑PHY enable rules (SyncE required on both PHYs when used on
PHY0, EREF0 required on both when used on PHY1),
- safe disabling of unused reference clocks after switching,
- a single, driver‑internal entry point for clock changes.
* Integration with the DPLL pin ops:
- pin‑set now calls into `ice_txclk_set_clk()` to request a
hardware switch,
- pin‑get reports the current SERDES reference by reading back the
active selector (`ice_get_serdes_ref_sel_e825c()`).
* Wiring the requested reference clock into Auto‑Negotiation restart
through the already‑extended `ice_aq_set_link_restart_an()`.
* After each link-up the driver verifies the effective hardware state
(`ice_txclk_verify()`) and updates its per‑PHY usage bitmaps,
correcting the requested/active state if the FW or AN flow applied a
different reference.
* PTP PF initialization now seeds the ENET reference clock as enabled
by default for its port.
All reference clock transitions are serialized through the DPLL lock,
and usage information is shared across all PFs belonging to the
same E825C controller PF. This ensures that concurrent changes are
coordinated and that shared PHYs never see an unexpected disable.
With this patch, E825 devices gain full userspace‑driven TXC reference
clock selection via the DPLL subsystem, enabling complete SyncE support,
precise multi‑clock setups, and predictable clock routing behavior.
Reviewed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
---
drivers/net/ethernet/intel/ice/Makefile | 2 +-
drivers/net/ethernet/intel/ice/ice.h | 12 +
drivers/net/ethernet/intel/ice/ice_dpll.c | 53 +++-
drivers/net/ethernet/intel/ice/ice_ptp.c | 27 ++-
drivers/net/ethernet/intel/ice/ice_ptp.h | 7 +
drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 37 +++
drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 27 +++
drivers/net/ethernet/intel/ice/ice_txclk.c | 256 ++++++++++++++++++++
drivers/net/ethernet/intel/ice/ice_txclk.h | 41 ++++
9 files changed, 445 insertions(+), 17 deletions(-)
create mode 100644 drivers/net/ethernet/intel/ice/ice_txclk.c
create mode 100644 drivers/net/ethernet/intel/ice/ice_txclk.h
diff --git a/drivers/net/ethernet/intel/ice/Makefile b/drivers/net/ethernet/intel/ice/Makefile
index 38db476ab2ec..95fd0c49800f 100644
--- a/drivers/net/ethernet/intel/ice/Makefile
+++ b/drivers/net/ethernet/intel/ice/Makefile
@@ -54,7 +54,7 @@ ice-$(CONFIG_PCI_IOV) += \
ice_vf_mbx.o \
ice_vf_vsi_vlan_ops.o \
ice_vf_lib.o
-ice-$(CONFIG_PTP_1588_CLOCK) += ice_ptp.o ice_ptp_hw.o ice_dpll.o ice_tspll.o ice_cpi.o
+ice-$(CONFIG_PTP_1588_CLOCK) += ice_ptp.o ice_ptp_hw.o ice_dpll.o ice_tspll.o ice_cpi.o ice_txclk.o
ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o
ice-$(CONFIG_RFS_ACCEL) += ice_arfs.o
ice-$(CONFIG_XDP_SOCKETS) += ice_xsk.o
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 2b2b22af42be..b42df789295f 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -1133,4 +1133,16 @@ static inline struct ice_hw *ice_get_primary_hw(struct ice_pf *pf)
else
return &pf->adapter->ctrl_pf->hw;
}
+
+/**
+ * ice_get_ctrl_pf - Get pointer to Control PF of the adapter
+ * @pf: pointer to the current PF structure
+ *
+ * Return: A pointer to ice_pf structure which is Control PF,
+ * NULL if it's not initialized yet.
+ */
+static inline struct ice_pf *ice_get_ctrl_pf(struct ice_pf *pf)
+{
+ return !pf->adapter ? NULL : pf->adapter->ctrl_pf;
+}
#endif /* _ICE_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c
index 72518fd1398d..ac0bfc2f0f0e 100644
--- a/drivers/net/ethernet/intel/ice/ice_dpll.c
+++ b/drivers/net/ethernet/intel/ice/ice_dpll.c
@@ -4,6 +4,7 @@
#include "ice.h"
#include "ice_lib.h"
#include "ice_trace.h"
+#include "ice_txclk.h"
#include <linux/dpll.h>
#include <linux/property.h>
@@ -2538,7 +2539,9 @@ ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv,
*
* Dpll subsystem callback, set a state of a Tx reference clock pin
*
+ * Context: Acquires and releases pf->dplls.lock
* Return:
+ * * 0 - success
* * negative - failure
*/
static int
@@ -2547,11 +2550,24 @@ ice_dpll_txclk_state_on_dpll_set(const struct dpll_pin *pin, void *pin_priv,
void *dpll_priv, enum dpll_pin_state state,
struct netlink_ext_ack *extack)
{
- /*
- * TODO: set HW accordingly to selected TX reference clock.
- * To be added in the follow up patches.
- */
- return -EOPNOTSUPP;
+ struct ice_dpll_pin *p = pin_priv;
+ struct ice_pf *pf = p->pf;
+ enum ice_e825c_ref_clk new_clk;
+ int ret = 0;
+
+ if (ice_dpll_is_reset(pf, extack))
+ return -EBUSY;
+
+ mutex_lock(&pf->dplls.lock);
+ new_clk = (state == DPLL_PIN_STATE_DISCONNECTED) ? ICE_REF_CLK_ENET :
+ p->tx_ref_src;
+ if (new_clk == pf->ptp.port.tx_clk)
+ goto unlock;
+
+ ret = ice_txclk_set_clk(pf, new_clk);
+unlock:
+ mutex_unlock(&pf->dplls.lock);
+ return ret;
}
/**
@@ -2565,8 +2581,10 @@ ice_dpll_txclk_state_on_dpll_set(const struct dpll_pin *pin, void *pin_priv,
*
* dpll subsystem callback, get a state of a TX clock reference pin.
*
+ * Context: Acquires and releases pf->dplls.lock
* Return:
* * 0 - success
+ * * negative - failure
*/
static int
ice_dpll_txclk_state_on_dpll_get(const struct dpll_pin *pin, void *pin_priv,
@@ -2575,13 +2593,26 @@ ice_dpll_txclk_state_on_dpll_get(const struct dpll_pin *pin, void *pin_priv,
enum dpll_pin_state *state,
struct netlink_ext_ack *extack)
{
- /*
- * TODO: query HW status to determine if the TX reference is selected.
- * To be added in the follow up patches.
- */
- *state = DPLL_PIN_STATE_DISCONNECTED;
+ struct ice_dpll_pin *p = pin_priv;
+ enum ice_e825c_ref_clk clk;
+ struct ice_pf *pf = p->pf;
+ int ret;
- return 0;
+ if (ice_dpll_is_reset(pf, extack))
+ return -EBUSY;
+
+ mutex_lock(&pf->dplls.lock);
+ ret = ice_get_serdes_ref_sel_e825c(&pf->hw, pf->ptp.port.port_num,
+ &clk);
+ if (ret)
+ goto unlock;
+
+ *state = (clk == p->tx_ref_src) ? DPLL_PIN_STATE_CONNECTED :
+ DPLL_PIN_STATE_DISCONNECTED;
+unlock:
+ mutex_unlock(&pf->dplls.lock);
+
+ return ret;
}
static const struct dpll_pin_ops ice_dpll_rclk_ops = {
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index 094e96219f45..a75a1380097b 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -4,6 +4,7 @@
#include "ice.h"
#include "ice_lib.h"
#include "ice_trace.h"
+#include "ice_txclk.h"
static const char ice_pin_names[][64] = {
"SDP0",
@@ -54,11 +55,6 @@ static const struct ice_ptp_pin_desc ice_pin_desc_dpll[] = {
{ SDP3, { 3, -1 }, { 0, 0 }},
};
-static struct ice_pf *ice_get_ctrl_pf(struct ice_pf *pf)
-{
- return !pf->adapter ? NULL : pf->adapter->ctrl_pf;
-}
-
static struct ice_ptp *ice_get_ctrl_ptp(struct ice_pf *pf)
{
struct ice_pf *ctrl_pf = ice_get_ctrl_pf(pf);
@@ -1325,6 +1321,10 @@ void ice_ptp_link_change(struct ice_pf *pf, bool linkup)
return;
}
}
+
+ if (linkup)
+ ice_txclk_verify(pf);
+
mutex_unlock(&pf->dplls.lock);
}
@@ -3079,6 +3079,7 @@ static int ice_ptp_setup_pf(struct ice_pf *pf)
{
struct ice_ptp *ctrl_ptp = ice_get_ctrl_ptp(pf);
struct ice_ptp *ptp = &pf->ptp;
+ u8 port_num, phy;
if (WARN_ON(!ctrl_ptp) || pf->hw.mac_type == ICE_MAC_UNKNOWN)
return -ENODEV;
@@ -3090,6 +3091,10 @@ static int ice_ptp_setup_pf(struct ice_pf *pf)
&pf->adapter->ports.ports);
mutex_unlock(&pf->adapter->ports.lock);
+ port_num = ptp->port.port_num;
+ phy = port_num / pf->hw.ptp.ports_per_phy;
+ set_bit(port_num, &ctrl_ptp->tx_refclks[phy][pf->ptp.port.tx_clk]);
+
return 0;
}
@@ -3290,6 +3295,7 @@ static void ice_ptp_init_tx_interrupt_mode(struct ice_pf *pf)
*/
void ice_ptp_init(struct ice_pf *pf)
{
+ enum ice_e825c_ref_clk tx_ref_clk;
struct ice_ptp *ptp = &pf->ptp;
struct ice_hw *hw = &pf->hw;
int err;
@@ -3318,6 +3324,17 @@ void ice_ptp_init(struct ice_pf *pf)
goto err_exit;
}
+ ptp->port.tx_clk = ICE_REF_CLK_ENET;
+ ptp->port.tx_clk_req = ICE_REF_CLK_ENET;
+ if (hw->mac_type == ICE_MAC_GENERIC_3K_E825) {
+ err = ice_get_serdes_ref_sel_e825c(hw, ptp->port.port_num,
+ &tx_ref_clk);
+ if (!err) {
+ ptp->port.tx_clk = tx_ref_clk;
+ ptp->port.tx_clk_req = tx_ref_clk;
+ }
+ }
+
err = ice_ptp_setup_pf(pf);
if (err)
goto err_exit;
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h
index 8c44bd758a4f..8b385271ab36 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.h
@@ -144,6 +144,8 @@ struct ice_ptp_tx {
* @link_up: indicates whether the link is up
* @tx_fifo_busy_cnt: number of times the Tx FIFO was busy
* @port_num: the port number this structure represents
+ * @tx_clk: currently active Tx reference clock source
+ * @tx_clk_req: requested Tx reference clock source (new target)
*/
struct ice_ptp_port {
struct list_head list_node;
@@ -153,6 +155,8 @@ struct ice_ptp_port {
bool link_up;
u8 tx_fifo_busy_cnt;
u8 port_num;
+ enum ice_e825c_ref_clk tx_clk;
+ enum ice_e825c_ref_clk tx_clk_req;
};
enum ice_ptp_tx_interrupt {
@@ -236,6 +240,7 @@ struct ice_ptp_pin_desc {
* @info: structure defining PTP hardware capabilities
* @clock: pointer to registered PTP clock device
* @tstamp_config: hardware timestamping configuration
+ * @tx_refclks: bitmaps table to store the information about TX reference clocks
* @reset_time: kernel time after clock stop on reset
* @tx_hwtstamp_good: number of completed Tx timestamp requests
* @tx_hwtstamp_skipped: number of Tx time stamp requests skipped
@@ -261,6 +266,8 @@ struct ice_ptp {
struct ptp_clock_info info;
struct ptp_clock *clock;
struct kernel_hwtstamp_config tstamp_config;
+#define ICE_E825_MAX_PHYS 2
+ unsigned long tx_refclks[ICE_E825_MAX_PHYS][ICE_REF_CLK_MAX];
u64 reset_time;
u64 tx_hwtstamp_good;
u32 tx_hwtstamp_skipped;
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
index 61c0a0d93ea8..c0720525ac49 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c
@@ -461,6 +461,43 @@ static int ice_read_phy_eth56g(struct ice_hw *hw, u8 port, u32 addr, u32 *val)
return err;
}
+/**
+ * ice_get_serdes_ref_sel_e825c - Read current Tx ref clock source
+ * @hw: pointer to the HW struct
+ * @port: port number for which Tx reference clock is read
+ * @clk: Tx reference clock value (output)
+ *
+ * Return: 0 on success, other error codes when failed to read from PHY
+ */
+int ice_get_serdes_ref_sel_e825c(struct ice_hw *hw, u8 port,
+ enum ice_e825c_ref_clk *clk)
+{
+ u8 lane = port % hw->ptp.ports_per_phy;
+ u32 serdes_rx_nt, serdes_tx_nt;
+ u32 val;
+ int ret;
+
+ ret = ice_read_phy_eth56g(hw, port,
+ SERDES_IP_IF_LN_FLXM_GENERAL(lane, 0),
+ &val);
+ if (ret)
+ return ret;
+
+ serdes_rx_nt = FIELD_GET(CFG_ICTL_PCS_REF_SEL_RX_NT, val);
+ serdes_tx_nt = FIELD_GET(CFG_ICTL_PCS_REF_SEL_TX_NT, val);
+
+ if (serdes_tx_nt == REF_SEL_NT_SYNCE &&
+ serdes_rx_nt == REF_SEL_NT_SYNCE)
+ *clk = ICE_REF_CLK_SYNCE;
+ else if (serdes_tx_nt == REF_SEL_NT_EREF0 &&
+ serdes_rx_nt == REF_SEL_NT_EREF0)
+ *clk = ICE_REF_CLK_EREF0;
+ else
+ *clk = ICE_REF_CLK_ENET;
+
+ return 0;
+}
+
/**
* ice_phy_res_address_eth56g - Calculate a PHY port register address
* @hw: pointer to the HW struct
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
index cbc9693179a1..820ba953ea01 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h
@@ -381,6 +381,8 @@ int ice_stop_phy_timer_eth56g(struct ice_hw *hw, u8 port, bool soft_reset);
int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port);
int ice_phy_cfg_intr_eth56g(struct ice_hw *hw, u8 port, bool ena, u8 threshold);
int ice_phy_cfg_ptp_1step_eth56g(struct ice_hw *hw, u8 port);
+int ice_get_serdes_ref_sel_e825c(struct ice_hw *hw, u8 port,
+ enum ice_e825c_ref_clk *clk);
#define ICE_ETH56G_NOMINAL_INCVAL 0x140000000ULL
#define ICE_ETH56G_NOMINAL_PCS_REF_TUS 0x100000000ULL
@@ -790,4 +792,29 @@ static inline u64 ice_get_base_incval(struct ice_hw *hw)
#define PHY_PTP_1STEP_PD_DELAY_M GENMASK(30, 1)
#define PHY_PTP_1STEP_PD_DLY_V_M BIT(31)
+#define SERDES_IP_IF_LN_FLXM_GENERAL(n, m) \
+ (0x32B800 + (m) * 0x100000 + (n) * 0x8000)
+#define CFG_RESERVED0_1 GENMASK(1, 0)
+#define CFG_ICTL_PCS_MODE_NT BIT(2)
+#define CFG_ICTL_PCS_RCOMP_SLAVE_EN_NT BIT(3)
+#define CFG_ICTL_PCS_CMN_FORCE_PUP_A BIT(4)
+#define CFG_ICTL_PCS_RCOMP_SLAVE_VALID_A BIT(5)
+#define CFG_ICTL_PCS_REF_SEL_RX_NT GENMASK(9, 6)
+#define REF_SEL_NT_ENET 0
+#define REF_SEL_NT_EREF0 1
+#define REF_SEL_NT_SYNCE 2
+#define CFG_IDAT_DFX_OBS_DIG_ GENMASK(11, 10)
+#define CFG_IRST_APB_MEM_B BIT(12)
+#define CFG_ICTL_PCS_DISCONNECT_NT BIT(13)
+#define CFG_ICTL_PCS_ISOLATE_NT BIT(14)
+#define CFG_RESERVED15_15 BIT(15)
+#define CFG_IRST_PCS_TSTBUS_B_A BIT(16)
+#define CFG_ICTL_PCS_REF_TERM_HIZ_EN_NT BIT(17)
+#define CFG_RESERVED18_19 GENMASK(19, 18)
+#define CFG_ICTL_PCS_SYNTHLCSLOW_FORCE_PUP_A BIT(20)
+#define CFG_ICTL_PCS_SYNTHLCFAST_FORCE_PUP_A BIT(21)
+#define CFG_RESERVED22_24 GENMASK(24, 22)
+#define CFG_ICTL_PCS_REF_SEL_TX_NT GENMASK(28, 25)
+#define CFG_RESERVED29_31 GENMASK(31, 29)
+
#endif /* _ICE_PTP_HW_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_txclk.c b/drivers/net/ethernet/intel/ice/ice_txclk.c
new file mode 100644
index 000000000000..ccb8411dca94
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_txclk.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2026 Intel Corporation */
+
+#include "ice.h"
+#include "ice_cpi.h"
+#include "ice_txclk.h"
+
+#define ICE_PHY0 0
+#define ICE_PHY1 1
+
+/**
+ * ice_txclk_enable_peer - Enable required TX reference clock on peer PHY
+ * @pf: pointer to the PF structure
+ * @clk: TX reference clock that must be enabled
+ *
+ * Some TX reference clocks on E825-class devices (SyncE and EREF0) must
+ * be enabled on both PHY complexes to allow proper routing:
+ *
+ * - SyncE must be enabled on both PHYs when used by PHY0
+ * - EREF0 must be enabled on both PHYs when used by PHY1
+ *
+ * If the requested clock is not yet enabled on the peer PHY, enable it.
+ * ENET does not require duplication and is ignored.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+static int ice_txclk_enable_peer(struct ice_pf *pf, enum ice_e825c_ref_clk clk)
+{
+ struct ice_pf *ctrl_pf = ice_get_ctrl_pf(pf);
+ u8 port_num, phy;
+ int err;
+
+ if (clk == ICE_REF_CLK_ENET)
+ return 0;
+
+ if (IS_ERR_OR_NULL(ctrl_pf)) {
+ dev_err(ice_pf_to_dev(pf),
+ "Can't enable tx-clk on peer: no controlling PF\n");
+ return -EINVAL;
+ }
+
+ port_num = pf->ptp.port.port_num;
+ phy = port_num / pf->hw.ptp.ports_per_phy;
+
+ if ((clk == ICE_REF_CLK_SYNCE && phy == ICE_PHY0 &&
+ !ice_txclk_any_port_uses(ctrl_pf, ICE_PHY1, clk)) ||
+ (clk == ICE_REF_CLK_EREF0 && phy == ICE_PHY1 &&
+ !ice_txclk_any_port_uses(ctrl_pf, ICE_PHY0, clk))) {
+ u8 peer_phy = phy ? ICE_PHY0 : ICE_PHY1;
+
+ err = ice_cpi_ena_dis_clk_ref(&pf->hw, peer_phy, clk, true);
+ if (err) {
+ dev_err(ice_hw_to_dev(&pf->hw),
+ "Failed to enable the %u TX clock for the %u PHY\n",
+ clk, peer_phy);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * ice_txclk_disable_unused - Disable TX reference clock no longer in use
+ * @pf: pointer to the PF structure
+ * @ref_clk: TX reference clock source to evaluate for disabling
+ *
+ * Disable (from power-saving reasons) a TX reference clock after a clock
+ * switch, provided that:
+ * - no port on the local PHY uses this clock, and
+ * - for SyncE: no port on PHY0 or PHY1 requires the clock, depending on
+ * where it must remain enabled for routing.
+ * - for EREF0: same logic as above but inverted PHY roles.
+ *
+ * Some reference clocks must be enabled on both PHY complexes when used
+ * (SyncE for PHY0, EREF0 for PHY1). The function therefore also attempts
+ * to implicitly disable the peer PHY copy when no port requires it.
+ *
+ * Return: 0 on success or negative error code if disabling fails.
+ */
+static int
+ice_txclk_disable_unused(struct ice_pf *pf, enum ice_e825c_ref_clk ref_clk)
+{
+ struct ice_pf *ctrl_pf = ice_get_ctrl_pf(pf);
+ struct ice_hw *hw = &pf->hw;
+ int err = 0;
+ u8 cur_phy;
+
+ if (IS_ERR_OR_NULL(ctrl_pf)) {
+ dev_err(ice_pf_to_dev(pf),
+ "Can't disable unused tx-clk: no controlling PF\n");
+ return -EINVAL;
+ }
+
+ cur_phy = pf->ptp.port.port_num / hw->ptp.ports_per_phy;
+
+ if (ref_clk == ICE_REF_CLK_SYNCE) {
+ /* Don't disable SyncE clock if it's still in use on PHY 0 */
+ if (ice_txclk_any_port_uses(ctrl_pf, ICE_PHY0, ref_clk))
+ return 0;
+ if (cur_phy == ICE_PHY0 &&
+ !ice_txclk_any_port_uses(ctrl_pf, ICE_PHY1, ref_clk)) {
+ err = ice_cpi_ena_dis_clk_ref(hw, ICE_PHY1, ref_clk,
+ false);
+ if (err) {
+ cur_phy = ICE_PHY1;
+ goto err;
+ }
+ }
+ } else if (ref_clk == ICE_REF_CLK_EREF0) {
+ /* Don't disable EREF0 clock if it's still in use on PHY 1 */
+ if (ice_txclk_any_port_uses(ctrl_pf, ICE_PHY1, ref_clk))
+ return 0;
+ if (cur_phy == ICE_PHY1 &&
+ !ice_txclk_any_port_uses(ctrl_pf, ICE_PHY0, ref_clk)) {
+ err = ice_cpi_ena_dis_clk_ref(hw, ICE_PHY0, ref_clk,
+ false);
+ if (err) {
+ cur_phy = ICE_PHY0;
+ goto err;
+ }
+ }
+ }
+
+ if (!ice_txclk_any_port_uses(ctrl_pf, cur_phy, ref_clk))
+ err = ice_cpi_ena_dis_clk_ref(hw, cur_phy, ref_clk, false);
+err:
+ if (err)
+ dev_warn(ice_pf_to_dev(pf), "Failed to disable the %u TX clock for the %u PHY\n",
+ ref_clk, cur_phy);
+
+ return err;
+}
+
+#define ICE_REFCLK_USER_TO_AQ_IDX(x) ((x) + 1)
+
+/**
+ * ice_txclk_set_clk - Set Tx reference clock
+ * @pf: pointer to pf structure
+ * @clk: new Tx clock
+ *
+ * Return: 0 on success, negative value otherwise.
+ */
+int ice_txclk_set_clk(struct ice_pf *pf, enum ice_e825c_ref_clk clk)
+{
+ struct ice_pf *ctrl_pf = ice_get_ctrl_pf(pf);
+ struct ice_port_info *port_info;
+ u8 port_num, phy;
+ int err;
+
+ if (pf->ptp.port.tx_clk == clk)
+ return 0;
+
+ if (IS_ERR_OR_NULL(ctrl_pf)) {
+ dev_err(ice_pf_to_dev(pf),
+ "Can't set tx-clk: no controlling PF\n");
+ return -EINVAL;
+ }
+
+ port_num = pf->ptp.port.port_num;
+ phy = port_num / pf->hw.ptp.ports_per_phy;
+ port_info = pf->hw.port_info;
+
+ /* Check if the TX clk is enabled for this PHY, if not - enable it */
+ if (!ice_txclk_any_port_uses(ctrl_pf, phy, clk)) {
+ err = ice_cpi_ena_dis_clk_ref(&pf->hw, phy, clk, true);
+ if (err) {
+ dev_err(ice_hw_to_dev(&pf->hw), "Failed to enable the %u TX clock for the %u PHY\n",
+ clk, phy);
+ return err;
+ }
+ err = ice_txclk_enable_peer(pf, clk);
+ if (err)
+ return err;
+ }
+
+ pf->ptp.port.tx_clk_req = clk;
+
+ /* We are ready to switch to the new TX clk. */
+ err = ice_aq_set_link_restart_an(port_info, true, NULL,
+ ICE_REFCLK_USER_TO_AQ_IDX(clk));
+ if (err)
+ dev_err(ice_hw_to_dev(&pf->hw), "Failed to switch to %u TX clock for the %u PHY\n",
+ clk, phy);
+
+ return err;
+}
+
+/**
+ * ice_txclk_verify - Validate TX reference clock switch and update usage state
+ * @pf: pointer to PF structure
+ *
+ * After a link-up event, verify whether the previously requested TX reference
+ * clock transition actually succeeded. The SERDES reference selector reflects
+ * the effective hardware choice, which may differ from the requested clock
+ * when Auto-Negotiation or firmware applies additional policy.
+ *
+ * If the hardware-selected clock differs from the requested one, update the
+ * software state accordingly and stop further processing.
+ *
+ * When the switch is successful, update the per‑PHY usage bitmaps so that the
+ * driver knows which reference clock is currently in use by this port. Using
+ * these bitmaps, disable any reference clocks that are no longer required by
+ * any port on the local PHY or, when applicable, on the peer PHY (according
+ * to E825 clock‑routing rules).
+ *
+ * This function does not initiate a clock switch; it only validates the result
+ * of a previously triggered transition and performs cleanup of unused clocks.
+ */
+void ice_txclk_verify(struct ice_pf *pf)
+{
+ struct ice_ptp_port *ptp_port = &pf->ptp.port;
+ struct ice_pf *ctrl_pf = ice_get_ctrl_pf(pf);
+ struct ice_hw *hw = &pf->hw;
+ enum ice_e825c_ref_clk clk;
+ int err;
+ u8 phy;
+
+ phy = ptp_port->port_num / hw->ptp.ports_per_phy;
+
+ /* verify current Tx reference settings */
+ err = ice_get_serdes_ref_sel_e825c(hw,
+ ptp_port->port_num,
+ &clk);
+ if (err)
+ return;
+
+ if (clk != pf->ptp.port.tx_clk_req) {
+ dev_warn(ice_pf_to_dev(pf),
+ "Failed to switch tx-clk for phy %d and clk %u (current: %u)\n",
+ phy, pf->ptp.port.tx_clk_req, clk);
+ pf->ptp.port.tx_clk = clk;
+ pf->ptp.port.tx_clk_req = clk;
+ return;
+ }
+
+ if (IS_ERR_OR_NULL(ctrl_pf)) {
+ dev_err(ice_pf_to_dev(pf),
+ "Can't set tx-clk: no controlling PF\n");
+ return;
+ }
+
+ /* update Tx reference clock usage map */
+ for (int i = 0; i < ICE_REF_CLK_MAX; i++)
+ (clk == i) ?
+ set_bit(ptp_port->port_num,
+ &ctrl_pf->ptp.tx_refclks[phy][i]) :
+ clear_bit(ptp_port->port_num,
+ &ctrl_pf->ptp.tx_refclks[phy][i]);
+
+ ice_txclk_disable_unused(pf, pf->ptp.port.tx_clk);
+
+ pf->ptp.port.tx_clk = clk;
+ pf->ptp.port.tx_clk_req = clk;
+}
+
diff --git a/drivers/net/ethernet/intel/ice/ice_txclk.h b/drivers/net/ethernet/intel/ice/ice_txclk.h
new file mode 100644
index 000000000000..6423b448841e
--- /dev/null
+++ b/drivers/net/ethernet/intel/ice/ice_txclk.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2026 Intel Corporation */
+
+#ifndef _ICE_TXCLK_H_
+#define _ICE_TXCLK_H_
+
+/**
+ * ice_txclk_any_port_uses - check if any port on a PHY uses this TX refclk
+ * @ctrl_pf: control PF (owner of the shared tx_refclks map)
+ * @phy: PHY index
+ * @clk: TX reference clock
+ *
+ * Return: true if any bit (port) is set for this clock on this PHY
+ */
+static inline bool
+ice_txclk_any_port_uses(const struct ice_pf *ctrl_pf, u8 phy,
+ enum ice_e825c_ref_clk clk)
+{
+ return find_first_bit(&ctrl_pf->ptp.tx_refclks[phy][clk],
+ BITS_PER_LONG) < BITS_PER_LONG;
+}
+
+/**
+ * ice_txclk_port_uses - check if a specific port uses this TX refclk
+ * @ctrl_pf: control PF
+ * @phy: PHY index
+ * @clk: TX reference clock
+ * @port: port number to test
+ *
+ * Return: true if this port uses the given clock
+ */
+static inline bool
+ice_txclk_port_uses(const struct ice_pf *ctrl_pf, u8 phy,
+ enum ice_e825c_ref_clk clk, u8 port)
+{
+ return test_bit(port, &ctrl_pf->ptp.tx_refclks[phy][clk]);
+}
+
+int ice_txclk_set_clk(struct ice_pf *pf, enum ice_e825c_ref_clk clk);
+void ice_txclk_verify(struct ice_pf *pf);
+#endif /* _ICE_TXCLK_H_ */
--
2.39.3
^ permalink raw reply related
* [PATCH v4 net-next 7/8] ice: add Tx reference clock index handling to AN restart command
From: Grzegorz Nitka @ 2026-03-26 16:28 UTC (permalink / raw)
To: netdev
Cc: linux-kernel, intel-wired-lan, poros, richardcochran,
andrew+netdev, przemyslaw.kitszel, anthony.l.nguyen,
Prathosh.Satish, ivecera, jiri, arkadiusz.kubalewski,
vadim.fedorenko, donald.hunter, horms, pabeni, kuba, davem,
edumazet, Grzegorz Nitka
In-Reply-To: <20260326162832.3135857-1-grzegorz.nitka@intel.com>
Extend the Restart Auto-Negotiation (AN) AdminQ command with a new
parameter allowing software to specify the Tx reference clock index to
be used during link restart.
This patch:
- adds REFCLK field definitions to ice_aqc_restart_an
- updates ice_aq_set_link_restart_an() to take a new refclk parameter
and properly encode it into the command
- keeps legacy behavior by passing REFCLK_NOCHANGE where appropriate
This prepares the driver for configurations requiring dynamic selection
of the Tx reference clock as part of the AN flow.
Reviewed-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Signed-off-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
---
drivers/net/ethernet/intel/ice/ice_adminq_cmd.h | 2 ++
drivers/net/ethernet/intel/ice/ice_common.c | 5 ++++-
drivers/net/ethernet/intel/ice/ice_common.h | 2 +-
drivers/net/ethernet/intel/ice/ice_lib.c | 3 ++-
4 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index 859e9c66f3e7..a24a0613d887 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -1169,6 +1169,8 @@ struct ice_aqc_restart_an {
u8 cmd_flags;
#define ICE_AQC_RESTART_AN_LINK_RESTART BIT(1)
#define ICE_AQC_RESTART_AN_LINK_ENABLE BIT(2)
+#define ICE_AQC_RESTART_AN_REFCLK_M GENMASK(4, 3)
+#define ICE_AQC_RESTART_AN_REFCLK_NOCHANGE 0
u8 reserved2[13];
};
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
index ce11fea122d0..de88aec9137c 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -4126,12 +4126,13 @@ int ice_get_link_status(struct ice_port_info *pi, bool *link_up)
* @pi: pointer to the port information structure
* @ena_link: if true: enable link, if false: disable link
* @cd: pointer to command details structure or NULL
+ * @refclk: the new TX reference clock, 0 if no change
*
* Sets up the link and restarts the Auto-Negotiation over the link.
*/
int
ice_aq_set_link_restart_an(struct ice_port_info *pi, bool ena_link,
- struct ice_sq_cd *cd)
+ struct ice_sq_cd *cd, u8 refclk)
{
struct ice_aqc_restart_an *cmd;
struct libie_aq_desc desc;
@@ -4147,6 +4148,8 @@ ice_aq_set_link_restart_an(struct ice_port_info *pi, bool ena_link,
else
cmd->cmd_flags &= ~ICE_AQC_RESTART_AN_LINK_ENABLE;
+ cmd->cmd_flags |= FIELD_PREP(ICE_AQC_RESTART_AN_REFCLK_M, refclk);
+
return ice_aq_send_cmd(pi->hw, &desc, NULL, 0, cd);
}
diff --git a/drivers/net/ethernet/intel/ice/ice_common.h b/drivers/net/ethernet/intel/ice/ice_common.h
index e700ac0dc347..9f5344212195 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.h
+++ b/drivers/net/ethernet/intel/ice/ice_common.h
@@ -215,7 +215,7 @@ ice_cfg_phy_fec(struct ice_port_info *pi, struct ice_aqc_set_phy_cfg_data *cfg,
enum ice_fec_mode fec);
int
ice_aq_set_link_restart_an(struct ice_port_info *pi, bool ena_link,
- struct ice_sq_cd *cd);
+ struct ice_sq_cd *cd, u8 refclk);
int
ice_aq_set_mac_cfg(struct ice_hw *hw, u16 max_frame_size, struct ice_sq_cd *cd);
int
diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c
index 689c6025ea82..c2c7f186bcc7 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -3769,7 +3769,8 @@ int ice_set_link(struct ice_vsi *vsi, bool ena)
if (vsi->type != ICE_VSI_PF)
return -EINVAL;
- status = ice_aq_set_link_restart_an(pi, ena, NULL);
+ status = ice_aq_set_link_restart_an(pi, ena, NULL,
+ ICE_AQC_RESTART_AN_REFCLK_NOCHANGE);
/* if link is owned by manageability, FW will return LIBIE_AQ_RC_EMODE.
* this is not a fatal error, so print a warning message and return
--
2.39.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox