* [PATCH v1 net-next 03/15] udp_tunnel: Pass struct sock to udp_tunnel6_dst_lookup().
From: Kuniyuki Iwashima @ 2026-05-02 3:12 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260502031401.3557229-1-kuniyu@google.com>
None of the udp_tunnel users need struct socket in their
fast paths; it is only used for tunnel setup / teardown.
Even udp_tunnel6_dst_lookup() does not need struct socket.
Let's change udp_tunnel6_dst_lookup() to take struct sock
instead of struct socket.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
drivers/net/bareudp.c | 4 ++--
drivers/net/geneve.c | 4 ++--
drivers/net/vxlan/vxlan_core.c | 4 ++--
include/net/udp_tunnel.h | 2 +-
net/ipv6/ip6_udp_tunnel.c | 6 +++---
5 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c
index 169ab90393cc..073ac8a15354 100644
--- a/drivers/net/bareudp.c
+++ b/drivers/net/bareudp.c
@@ -396,7 +396,7 @@ static int bareudp6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
sport = udp_flow_src_port(bareudp->net, skb,
bareudp->sport_min, USHRT_MAX,
true);
- dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sock, 0, &saddr,
+ dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sock->sk, 0, &saddr,
key, sport, bareudp->port, key->tos,
use_cache ?
(struct dst_cache *) &info->dst_cache : NULL);
@@ -532,7 +532,7 @@ static int bareudp_fill_metadata_dst(struct net_device *dev,
if (!sock)
return -ESHUTDOWN;
- dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sock,
+ dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sock->sk,
0, &saddr, &info->key,
sport, bareudp->port, info->key.tos,
use_cache ? &info->dst_cache : NULL);
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index c3a7736cd6fc..4b7081b97015 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -1424,7 +1424,7 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
geneve->cfg.port_min,
geneve->cfg.port_max, true);
- dst = udp_tunnel6_dst_lookup(skb, dev, geneve->net, gs6->sock, 0,
+ dst = udp_tunnel6_dst_lookup(skb, dev, geneve->net, gs6->sock->sk, 0,
&saddr, key, sport,
geneve->cfg.info.key.tp_dst, prio,
use_cache ?
@@ -1592,7 +1592,7 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
geneve->cfg.port_min,
geneve->cfg.port_max, true);
- dst = udp_tunnel6_dst_lookup(skb, dev, geneve->net, gs6->sock, 0,
+ dst = udp_tunnel6_dst_lookup(skb, dev, geneve->net, gs6->sock->sk, 0,
&saddr, &info->key, sport,
geneve->cfg.info.key.tp_dst, prio,
use_cache ? &info->dst_cache : NULL);
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 394801c068b3..a19f951e05f1 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -2559,7 +2559,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
if (!ifindex)
ifindex = sock6->sock->sk->sk_bound_dev_if;
- ndst = udp_tunnel6_dst_lookup(skb, dev, vxlan->net, sock6->sock,
+ ndst = udp_tunnel6_dst_lookup(skb, dev, vxlan->net, sock6->sock->sk,
ifindex, &saddr, pkey,
src_port, dst_port, tos,
use_cache ? dst_cache : NULL);
@@ -3254,7 +3254,7 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
if (!sock6)
return -EIO;
- ndst = udp_tunnel6_dst_lookup(skb, dev, vxlan->net, sock6->sock,
+ ndst = udp_tunnel6_dst_lookup(skb, dev, vxlan->net, sock6->sock->sk,
0, &info->key.u.ipv6.src,
&info->key,
sport, dport, info->key.tos,
diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h
index 49324e28ec27..14a9c5155608 100644
--- a/include/net/udp_tunnel.h
+++ b/include/net/udp_tunnel.h
@@ -188,7 +188,7 @@ struct rtable *udp_tunnel_dst_lookup(struct sk_buff *skb,
struct dst_entry *udp_tunnel6_dst_lookup(struct sk_buff *skb,
struct net_device *dev,
struct net *net,
- struct socket *sock, int oif,
+ struct sock *sk, int oif,
struct in6_addr *saddr,
const struct ip_tunnel_key *key,
__be16 sport, __be16 dport, u8 dsfield,
diff --git a/net/ipv6/ip6_udp_tunnel.c b/net/ipv6/ip6_udp_tunnel.c
index 405ef1cb8864..9adb5775487f 100644
--- a/net/ipv6/ip6_udp_tunnel.c
+++ b/net/ipv6/ip6_udp_tunnel.c
@@ -118,7 +118,7 @@ EXPORT_SYMBOL_GPL(udp_tunnel6_xmit_skb);
* @skb: Packet for which lookup is done
* @dev: Tunnel device
* @net: Network namespace of tunnel device
- * @sock: Socket which provides route info
+ * @sk: Socket which provides route info
* @oif: Index of the output interface
* @saddr: Memory to store the src ip address
* @key: Tunnel information
@@ -135,7 +135,7 @@ EXPORT_SYMBOL_GPL(udp_tunnel6_xmit_skb);
struct dst_entry *udp_tunnel6_dst_lookup(struct sk_buff *skb,
struct net_device *dev,
struct net *net,
- struct socket *sock,
+ struct sock *sk,
int oif,
struct in6_addr *saddr,
const struct ip_tunnel_key *key,
@@ -162,7 +162,7 @@ struct dst_entry *udp_tunnel6_dst_lookup(struct sk_buff *skb,
fl6.fl6_dport = dport;
fl6.flowlabel = ip6_make_flowinfo(dsfield, key->label);
- dst = ip6_dst_lookup_flow(net, sock->sk, &fl6, NULL);
+ dst = ip6_dst_lookup_flow(net, sk, &fl6, NULL);
if (IS_ERR(dst)) {
netdev_dbg(dev, "no route to %pI6\n", &fl6.daddr);
return ERR_PTR(-ENETUNREACH);
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v1 net-next 04/15] udp_tunnel: Pass struct sock to udp_tunnel_{push,drop}_rx_port().
From: Kuniyuki Iwashima @ 2026-05-02 3:12 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260502031401.3557229-1-kuniyu@google.com>
None of the udp_tunnel users need struct socket in their
fast paths; it is only used for tunnel setup / teardown.
Even udp_tunnel_{push,drop}_rx_port() do not need struct socket.
Let's change udp_tunnel_{push,drop}_rx_port() to take struct
sock instead of struct socket.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
drivers/net/geneve.c | 4 ++--
drivers/net/vxlan/vxlan_core.c | 4 ++--
include/net/udp_tunnel.h | 4 ++--
net/ipv4/udp_tunnel_core.c | 6 ++----
4 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 4b7081b97015..16df8d5c42c9 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -1654,10 +1654,10 @@ static void geneve_offload_rx_ports(struct net_device *dev, bool push)
list_for_each_entry(gs, &gn->sock_list, list) {
if (push) {
- udp_tunnel_push_rx_port(dev, gs->sock,
+ udp_tunnel_push_rx_port(dev, gs->sock->sk,
UDP_TUNNEL_TYPE_GENEVE);
} else {
- udp_tunnel_drop_rx_port(dev, gs->sock,
+ udp_tunnel_drop_rx_port(dev, gs->sock->sk,
UDP_TUNNEL_TYPE_GENEVE);
}
}
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index a19f951e05f1..184df57bc705 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -3332,9 +3332,9 @@ static void vxlan_offload_rx_ports(struct net_device *dev, bool push)
type = UDP_TUNNEL_TYPE_VXLAN;
if (push)
- udp_tunnel_push_rx_port(dev, vs->sock, type);
+ udp_tunnel_push_rx_port(dev, vs->sock->sk, type);
else
- udp_tunnel_drop_rx_port(dev, vs->sock, type);
+ udp_tunnel_drop_rx_port(dev, vs->sock->sk, type);
}
}
}
diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h
index 14a9c5155608..29ead6a38ef6 100644
--- a/include/net/udp_tunnel.h
+++ b/include/net/udp_tunnel.h
@@ -127,9 +127,9 @@ struct udp_tunnel_info {
};
/* Notify network devices of offloadable types */
-void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock,
+void udp_tunnel_push_rx_port(struct net_device *dev, struct sock *sk,
unsigned short type);
-void udp_tunnel_drop_rx_port(struct net_device *dev, struct socket *sock,
+void udp_tunnel_drop_rx_port(struct net_device *dev, struct sock *sk,
unsigned short type);
void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type);
void udp_tunnel_notify_del_rx_port(struct socket *sock, unsigned short type);
diff --git a/net/ipv4/udp_tunnel_core.c b/net/ipv4/udp_tunnel_core.c
index 3090b4745d47..857b51d62ce1 100644
--- a/net/ipv4/udp_tunnel_core.c
+++ b/net/ipv4/udp_tunnel_core.c
@@ -97,10 +97,9 @@ void setup_udp_tunnel_sock(struct net *net, struct sock *sk,
}
EXPORT_SYMBOL_GPL(setup_udp_tunnel_sock);
-void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock,
+void udp_tunnel_push_rx_port(struct net_device *dev, struct sock *sk,
unsigned short type)
{
- struct sock *sk = sock->sk;
struct udp_tunnel_info ti;
ti.type = type;
@@ -111,10 +110,9 @@ void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock,
}
EXPORT_SYMBOL_GPL(udp_tunnel_push_rx_port);
-void udp_tunnel_drop_rx_port(struct net_device *dev, struct socket *sock,
+void udp_tunnel_drop_rx_port(struct net_device *dev, struct sock *sk,
unsigned short type)
{
- struct sock *sk = sock->sk;
struct udp_tunnel_info ti;
ti.type = type;
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v1 net-next 05/15] udp_tunnel: Pass struct sock to udp_tunnel_notify_{add,del}_rx_port().
From: Kuniyuki Iwashima @ 2026-05-02 3:12 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260502031401.3557229-1-kuniyu@google.com>
None of the udp_tunnel users need struct socket in their
fast paths; it is only used for tunnel setup / teardown.
Even udp_tunnel_notify_{add,del}_rx_port() do not need
struct socket.
Let's change udp_tunnel_notify_{add,del}_rx_port() to take
struct sock instead of struct socket.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
drivers/net/geneve.c | 4 ++--
drivers/net/vxlan/vxlan_core.c | 4 ++--
include/net/udp_tunnel.h | 4 ++--
net/ipv4/udp_tunnel_core.c | 6 ++----
4 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 16df8d5c42c9..9cf62d3ee471 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -995,7 +995,7 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
INIT_HLIST_HEAD(&gs->vni_list[h]);
/* Initialize the geneve udp offloads structure */
- udp_tunnel_notify_add_rx_port(gs->sock, UDP_TUNNEL_TYPE_GENEVE);
+ udp_tunnel_notify_add_rx_port(gs->sock->sk, UDP_TUNNEL_TYPE_GENEVE);
/* Mark socket as an encapsulation socket */
memset(&tunnel_cfg, 0, sizeof(tunnel_cfg));
@@ -1017,7 +1017,7 @@ static void __geneve_sock_release(struct geneve_sock *gs)
return;
list_del(&gs->list);
- udp_tunnel_notify_del_rx_port(gs->sock, UDP_TUNNEL_TYPE_GENEVE);
+ udp_tunnel_notify_del_rx_port(gs->sock->sk, UDP_TUNNEL_TYPE_GENEVE);
udp_tunnel_sock_release(gs->sock->sk);
kfree_rcu(gs, rcu);
}
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 184df57bc705..0ea88232b985 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -1493,7 +1493,7 @@ static bool __vxlan_sock_release_prep(struct vxlan_sock *vs)
return false;
hlist_del_rcu(&vs->hlist);
- udp_tunnel_notify_del_rx_port(vs->sock,
+ udp_tunnel_notify_del_rx_port(vs->sock->sk,
(vs->flags & VXLAN_F_GPE) ?
UDP_TUNNEL_TYPE_VXLAN_GPE :
UDP_TUNNEL_TYPE_VXLAN);
@@ -3600,7 +3600,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
vs->flags = (flags & VXLAN_F_RCV_FLAGS);
hlist_add_head_rcu(&vs->hlist, vs_head(net, port));
- udp_tunnel_notify_add_rx_port(sock,
+ udp_tunnel_notify_add_rx_port(sock->sk,
(vs->flags & VXLAN_F_GPE) ?
UDP_TUNNEL_TYPE_VXLAN_GPE :
UDP_TUNNEL_TYPE_VXLAN);
diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h
index 29ead6a38ef6..498b7b262fa9 100644
--- a/include/net/udp_tunnel.h
+++ b/include/net/udp_tunnel.h
@@ -131,8 +131,8 @@ void udp_tunnel_push_rx_port(struct net_device *dev, struct sock *sk,
unsigned short type);
void udp_tunnel_drop_rx_port(struct net_device *dev, struct sock *sk,
unsigned short type);
-void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type);
-void udp_tunnel_notify_del_rx_port(struct socket *sock, unsigned short type);
+void udp_tunnel_notify_add_rx_port(struct sock *sk, unsigned short type);
+void udp_tunnel_notify_del_rx_port(struct sock *sk, unsigned short type);
/* Transmit the skb using UDP encapsulation. */
void udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb,
diff --git a/net/ipv4/udp_tunnel_core.c b/net/ipv4/udp_tunnel_core.c
index 857b51d62ce1..44788b95c823 100644
--- a/net/ipv4/udp_tunnel_core.c
+++ b/net/ipv4/udp_tunnel_core.c
@@ -124,9 +124,8 @@ void udp_tunnel_drop_rx_port(struct net_device *dev, struct sock *sk,
EXPORT_SYMBOL_GPL(udp_tunnel_drop_rx_port);
/* Notify netdevs that UDP port started listening */
-void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type)
+void udp_tunnel_notify_add_rx_port(struct sock *sk, unsigned short type)
{
- struct sock *sk = sock->sk;
struct net *net = sock_net(sk);
struct udp_tunnel_info ti;
struct net_device *dev;
@@ -146,9 +145,8 @@ void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type)
EXPORT_SYMBOL_GPL(udp_tunnel_notify_add_rx_port);
/* Notify netdevs that UDP port is no more listening */
-void udp_tunnel_notify_del_rx_port(struct socket *sock, unsigned short type)
+void udp_tunnel_notify_del_rx_port(struct sock *sk, unsigned short type)
{
- struct sock *sk = sock->sk;
struct net *net = sock_net(sk);
struct udp_tunnel_info ti;
struct net_device *dev;
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v1 net-next 06/15] vxlan: Fix potential null-ptr-deref in vxlan_gro_prepare_receive().
From: Kuniyuki Iwashima @ 2026-05-02 3:12 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260502031401.3557229-1-kuniyu@google.com>
udp_tunnel_sock_release() could set sk->sk_user_data to NULL
while vxlan_gro_prepare_receive() is running.
Let's check if rcu_dereference_sk_user_data() is NULL after
skb_gro_remcsum_init().
Fixes: 5602c48cf875 ("vxlan: change vxlan to use UDP socket GRO")
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
This is 10 years old bug...
---
drivers/net/vxlan/vxlan_core.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 0ea88232b985..abf3ae04d75b 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -657,14 +657,18 @@ static struct vxlanhdr *vxlan_gro_prepare_receive(struct sock *sk,
struct sk_buff *skb,
struct gro_remcsum *grc)
{
- struct sk_buff *p;
struct vxlanhdr *vh, *vh2;
unsigned int hlen, off_vx;
- struct vxlan_sock *vs = rcu_dereference_sk_user_data(sk);
+ struct vxlan_sock *vs;
+ struct sk_buff *p;
__be32 flags;
skb_gro_remcsum_init(grc);
+ vs = rcu_dereference_sk_user_data(sk);
+ if (!vs)
+ return NULL;
+
off_vx = skb_gro_offset(skb);
hlen = off_vx + sizeof(*vh);
vh = skb_gro_header(skb, hlen, off_vx);
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v1 net-next 07/15] vxlan: Store struct sock in struct vxlan_sock.
From: Kuniyuki Iwashima @ 2026-05-02 3:13 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260502031401.3557229-1-kuniyu@google.com>
Commit 3cf7203ca620 ("net/tunnel: wait until all sk_user_data
reader finish before releasing the sock") added synchronize_rcu()
in udp_tunnel_sock_release().
This was intended to protect the fast path of a dying vxlan device
from dereferencing vxlan_sock->sock->sk after sock_orphan() has set
sock->sk to NULL.
However, vxlan does not need to access struct socket itself in the
fast path; it only reads struct sock, and struct socket is only
used for tunnel setup and teardown.
Let's store struct sock directly in struct vxlan_sock.
In the next patch, we will free vxlan_sock with kfree_rcu(), then
vxlan no longer needs synchronize_rcu() in udp_tunnel_sock_release().
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
drivers/net/vxlan/vxlan_core.c | 48 ++++++++++++++---------------
drivers/net/vxlan/vxlan_multicast.c | 8 ++---
include/net/vxlan.h | 4 +--
3 files changed, 30 insertions(+), 30 deletions(-)
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index abf3ae04d75b..ce99da44ea7d 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -88,10 +88,10 @@ static struct vxlan_sock *vxlan_find_sock(struct net *net, sa_family_t family,
flags &= VXLAN_F_RCV_FLAGS;
hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) {
- if (inet_sk(vs->sock->sk)->inet_sport == port &&
+ if (inet_sk(vs->sk)->inet_sport == port &&
vxlan_get_sk_family(vs) == family &&
vs->flags == flags &&
- vs->sock->sk->sk_bound_dev_if == ifindex)
+ vs->sk->sk_bound_dev_if == ifindex)
return vs;
}
return NULL;
@@ -1497,7 +1497,7 @@ static bool __vxlan_sock_release_prep(struct vxlan_sock *vs)
return false;
hlist_del_rcu(&vs->hlist);
- udp_tunnel_notify_del_rx_port(vs->sock->sk,
+ udp_tunnel_notify_del_rx_port(vs->sk,
(vs->flags & VXLAN_F_GPE) ?
UDP_TUNNEL_TYPE_VXLAN_GPE :
UDP_TUNNEL_TYPE_VXLAN);
@@ -1523,13 +1523,13 @@ static void vxlan_sock_release(struct vxlan_dev *vxlan)
vxlan_vs_del_dev(vxlan);
if (__vxlan_sock_release_prep(sock4)) {
- udp_tunnel_sock_release(sock4->sock->sk);
+ udp_tunnel_sock_release(sock4->sk);
kfree(sock4);
}
#if IS_ENABLED(CONFIG_IPV6)
if (__vxlan_sock_release_prep(sock6)) {
- udp_tunnel_sock_release(sock6->sock->sk);
+ udp_tunnel_sock_release(sock6->sk);
kfree(sock6);
}
#endif
@@ -2477,7 +2477,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
}
if (!ifindex)
- ifindex = sock4->sock->sk->sk_bound_dev_if;
+ ifindex = sock4->sk->sk_bound_dev_if;
rt = udp_tunnel_dst_lookup(skb, dev, vxlan->net, ifindex,
&saddr, pkey, src_port, dst_port,
@@ -2544,7 +2544,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
goto tx_error;
}
- udp_tunnel_xmit_skb(rt, sock4->sock->sk, skb, saddr,
+ udp_tunnel_xmit_skb(rt, sock4->sk, skb, saddr,
pkey->u.ipv4.dst, tos, ttl, df,
src_port, dst_port, xnet, !udp_sum,
ipcb_flags);
@@ -2561,9 +2561,9 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
}
if (!ifindex)
- ifindex = sock6->sock->sk->sk_bound_dev_if;
+ ifindex = sock6->sk->sk_bound_dev_if;
- ndst = udp_tunnel6_dst_lookup(skb, dev, vxlan->net, sock6->sock->sk,
+ ndst = udp_tunnel6_dst_lookup(skb, dev, vxlan->net, sock6->sk,
ifindex, &saddr, pkey,
src_port, dst_port, tos,
use_cache ? dst_cache : NULL);
@@ -2619,7 +2619,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
goto tx_error;
}
- udp_tunnel6_xmit_skb(ndst, sock6->sock->sk, skb, dev,
+ udp_tunnel6_xmit_skb(ndst, sock6->sk, skb, dev,
&saddr, &pkey->u.ipv6.dst, tos, ttl,
pkey->label, src_port, dst_port, !udp_sum,
ip6cb_flags);
@@ -3258,7 +3258,7 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
if (!sock6)
return -EIO;
- ndst = udp_tunnel6_dst_lookup(skb, dev, vxlan->net, sock6->sock->sk,
+ ndst = udp_tunnel6_dst_lookup(skb, dev, vxlan->net, sock6->sk,
0, &info->key.u.ipv6.src,
&info->key,
sport, dport, info->key.tos,
@@ -3336,9 +3336,9 @@ static void vxlan_offload_rx_ports(struct net_device *dev, bool push)
type = UDP_TUNNEL_TYPE_VXLAN;
if (push)
- udp_tunnel_push_rx_port(dev, vs->sock->sk, type);
+ udp_tunnel_push_rx_port(dev, vs->sk, type);
else
- udp_tunnel_drop_rx_port(dev, vs->sock->sk, type);
+ udp_tunnel_drop_rx_port(dev, vs->sk, type);
}
}
}
@@ -3544,8 +3544,8 @@ static const struct ethtool_ops vxlan_ethtool_ops = {
.get_link_ksettings = vxlan_get_link_ksettings,
};
-static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
- __be16 port, u32 flags, int ifindex)
+static struct sock *vxlan_create_sock(struct net *net, bool ipv6,
+ __be16 port, u32 flags, int ifindex)
{
struct socket *sock;
struct udp_port_cfg udp_conf;
@@ -3571,7 +3571,7 @@ static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
return ERR_PTR(err);
udp_allow_gso(sock->sk);
- return sock;
+ return sock->sk;
}
/* Create new listen socket if needed */
@@ -3579,10 +3579,10 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
__be16 port, u32 flags,
int ifindex)
{
+ struct udp_tunnel_sock_cfg tunnel_cfg;
struct vxlan_sock *vs;
- struct socket *sock;
+ struct sock *sk;
unsigned int h;
- struct udp_tunnel_sock_cfg tunnel_cfg;
ASSERT_RTNL();
@@ -3593,18 +3593,18 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
for (h = 0; h < VNI_HASH_SIZE; ++h)
INIT_HLIST_HEAD(&vs->vni_list[h]);
- sock = vxlan_create_sock(net, ipv6, port, flags, ifindex);
- if (IS_ERR(sock)) {
+ sk = vxlan_create_sock(net, ipv6, port, flags, ifindex);
+ if (IS_ERR(sk)) {
kfree(vs);
- return ERR_CAST(sock);
+ return ERR_CAST(sk);
}
- vs->sock = sock;
+ vs->sk = sk;
refcount_set(&vs->refcnt, 1);
vs->flags = (flags & VXLAN_F_RCV_FLAGS);
hlist_add_head_rcu(&vs->hlist, vs_head(net, port));
- udp_tunnel_notify_add_rx_port(sock->sk,
+ udp_tunnel_notify_add_rx_port(sk,
(vs->flags & VXLAN_F_GPE) ?
UDP_TUNNEL_TYPE_VXLAN_GPE :
UDP_TUNNEL_TYPE_VXLAN);
@@ -3624,7 +3624,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, bool ipv6,
tunnel_cfg.gro_complete = vxlan_gro_complete;
}
- setup_udp_tunnel_sock(net, sock->sk, &tunnel_cfg);
+ setup_udp_tunnel_sock(net, sk, &tunnel_cfg);
return vs;
}
diff --git a/drivers/net/vxlan/vxlan_multicast.c b/drivers/net/vxlan/vxlan_multicast.c
index b0e80bca855c..3b75b48dc726 100644
--- a/drivers/net/vxlan/vxlan_multicast.c
+++ b/drivers/net/vxlan/vxlan_multicast.c
@@ -29,7 +29,7 @@ int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip,
.imr_ifindex = ifindex,
};
- sk = sock4->sock->sk;
+ sk = sock4->sk;
lock_sock(sk);
ret = ip_mc_join_group(sk, &mreq);
release_sock(sk);
@@ -37,7 +37,7 @@ int vxlan_igmp_join(struct vxlan_dev *vxlan, union vxlan_addr *rip,
} else {
struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
- sk = sock6->sock->sk;
+ sk = sock6->sk;
lock_sock(sk);
ret = ipv6_sock_mc_join(sk, ifindex, &ip->sin6.sin6_addr);
release_sock(sk);
@@ -62,7 +62,7 @@ int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip,
.imr_ifindex = ifindex,
};
- sk = sock4->sock->sk;
+ sk = sock4->sk;
lock_sock(sk);
ret = ip_mc_leave_group(sk, &mreq);
release_sock(sk);
@@ -70,7 +70,7 @@ int vxlan_igmp_leave(struct vxlan_dev *vxlan, union vxlan_addr *rip,
} else {
struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
- sk = sock6->sock->sk;
+ sk = sock6->sk;
lock_sock(sk);
ret = ipv6_sock_mc_drop(sk, ifindex, &ip->sin6.sin6_addr);
release_sock(sk);
diff --git a/include/net/vxlan.h b/include/net/vxlan.h
index 0ee50785f4f1..8b52294b2902 100644
--- a/include/net/vxlan.h
+++ b/include/net/vxlan.h
@@ -185,7 +185,7 @@ struct vxlan_metadata {
/* per UDP socket information */
struct vxlan_sock {
struct hlist_node hlist;
- struct socket *sock;
+ struct sock *sk;
struct hlist_head vni_list[VNI_HASH_SIZE];
refcount_t refcnt;
u32 flags;
@@ -448,7 +448,7 @@ static inline __be32 vxlan_compute_rco(unsigned int start, unsigned int offset)
static inline unsigned short vxlan_get_sk_family(struct vxlan_sock *vs)
{
- return vs->sock->sk->sk_family;
+ return vs->sk->sk_family;
}
#if IS_ENABLED(CONFIG_IPV6)
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v1 net-next 08/15] vxlan: Free vxlan_sock with kfree_rcu().
From: Kuniyuki Iwashima @ 2026-05-02 3:13 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260502031401.3557229-1-kuniyu@google.com>
We will remove synchronize_rcu() in udp_tunnel_sock_release().
We must ensure that vxlan_sock is freed after inflight RX fast path.
Let's free vxlan_sock with kfree_rcu().
Note that vxlan_sock.vni_list[] is 8K and struct rcu_head must
be placed before it.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
drivers/net/vxlan/vxlan_core.c | 4 ++--
include/net/vxlan.h | 1 +
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index ce99da44ea7d..00facbfabced 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -1524,13 +1524,13 @@ static void vxlan_sock_release(struct vxlan_dev *vxlan)
if (__vxlan_sock_release_prep(sock4)) {
udp_tunnel_sock_release(sock4->sk);
- kfree(sock4);
+ kfree_rcu(sock4, rcu);
}
#if IS_ENABLED(CONFIG_IPV6)
if (__vxlan_sock_release_prep(sock6)) {
udp_tunnel_sock_release(sock6->sk);
- kfree(sock6);
+ kfree_rcu(sock6, rcu);
}
#endif
}
diff --git a/include/net/vxlan.h b/include/net/vxlan.h
index 8b52294b2902..dfba89695efc 100644
--- a/include/net/vxlan.h
+++ b/include/net/vxlan.h
@@ -186,6 +186,7 @@ struct vxlan_metadata {
struct vxlan_sock {
struct hlist_node hlist;
struct sock *sk;
+ struct rcu_head rcu;
struct hlist_head vni_list[VNI_HASH_SIZE];
refcount_t refcnt;
u32 flags;
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v1 net-next 09/15] geneve: Store struct sock in struct geneve_sock.
From: Kuniyuki Iwashima @ 2026-05-02 3:13 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260502031401.3557229-1-kuniyu@google.com>
geneve does not need to access struct socket itself in the fast
path; it only reads struct sock, and struct socket is only used for
tunnel setup and teardown.
Let's store struct sock directly in struct geneve_sock.
__geneve_sock_release() frees geneve_sock with kfree_rcu(), so
geneve no longer needs synchronize_rcu() in udp_tunnel_sock_release().
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
drivers/net/geneve.c | 54 ++++++++++++++++++++++----------------------
1 file changed, 27 insertions(+), 27 deletions(-)
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 9cf62d3ee471..b36fad833724 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -106,7 +106,7 @@ struct geneve_sock {
bool collect_md;
bool gro_hint;
struct list_head list;
- struct socket *sock;
+ struct sock *sk;
struct rcu_head rcu;
int refcnt;
struct hlist_head vni_list[VNI_HASH_SIZE];
@@ -167,7 +167,7 @@ static bool eq_tun_id_and_vni(u8 *tun_id, u8 *vni)
static sa_family_t geneve_get_sk_family(struct geneve_sock *gs)
{
- return gs->sock->sk->sk_family;
+ return gs->sk->sk_family;
}
static struct geneve_dev *geneve_lookup(struct geneve_sock *gs,
@@ -760,11 +760,11 @@ static int geneve_udp_encap_err_lookup(struct sock *sk, struct sk_buff *skb)
return -EPFNOSUPPORT;
}
-static struct socket *geneve_create_sock(struct net *net, bool ipv6,
- __be16 port, bool ipv6_rx_csum)
+static struct sock *geneve_create_sock(struct net *net, bool ipv6,
+ __be16 port, bool ipv6_rx_csum)
{
- struct socket *sock;
struct udp_port_cfg udp_conf;
+ struct socket *sock;
int err;
memset(&udp_conf, 0, sizeof(udp_conf));
@@ -786,7 +786,7 @@ static struct socket *geneve_create_sock(struct net *net, bool ipv6,
return ERR_PTR(err);
udp_allow_gso(sock->sk);
- return sock;
+ return sock->sk;
}
static bool geneve_hdr_match(struct sk_buff *skb,
@@ -974,28 +974,28 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
bool ipv6, bool ipv6_rx_csum)
{
struct geneve_net *gn = net_generic(net, geneve_net_id);
- struct geneve_sock *gs;
- struct socket *sock;
struct udp_tunnel_sock_cfg tunnel_cfg;
+ struct geneve_sock *gs;
+ struct sock *sk;
int h;
gs = kzalloc_obj(*gs);
if (!gs)
return ERR_PTR(-ENOMEM);
- sock = geneve_create_sock(net, ipv6, port, ipv6_rx_csum);
- if (IS_ERR(sock)) {
+ sk = geneve_create_sock(net, ipv6, port, ipv6_rx_csum);
+ if (IS_ERR(sk)) {
kfree(gs);
- return ERR_CAST(sock);
+ return ERR_CAST(sk);
}
- gs->sock = sock;
+ gs->sk = sk;
gs->refcnt = 1;
for (h = 0; h < VNI_HASH_SIZE; ++h)
INIT_HLIST_HEAD(&gs->vni_list[h]);
/* Initialize the geneve udp offloads structure */
- udp_tunnel_notify_add_rx_port(gs->sock->sk, UDP_TUNNEL_TYPE_GENEVE);
+ udp_tunnel_notify_add_rx_port(sk, UDP_TUNNEL_TYPE_GENEVE);
/* Mark socket as an encapsulation socket */
memset(&tunnel_cfg, 0, sizeof(tunnel_cfg));
@@ -1006,7 +1006,7 @@ static struct geneve_sock *geneve_socket_create(struct net *net, __be16 port,
tunnel_cfg.encap_rcv = geneve_udp_encap_recv;
tunnel_cfg.encap_err_lookup = geneve_udp_encap_err_lookup;
tunnel_cfg.encap_destroy = NULL;
- setup_udp_tunnel_sock(net, sock->sk, &tunnel_cfg);
+ setup_udp_tunnel_sock(net, sk, &tunnel_cfg);
list_add(&gs->list, &gn->sock_list);
return gs;
}
@@ -1017,8 +1017,8 @@ static void __geneve_sock_release(struct geneve_sock *gs)
return;
list_del(&gs->list);
- udp_tunnel_notify_del_rx_port(gs->sock->sk, UDP_TUNNEL_TYPE_GENEVE);
- udp_tunnel_sock_release(gs->sock->sk);
+ udp_tunnel_notify_del_rx_port(gs->sk, UDP_TUNNEL_TYPE_GENEVE);
+ udp_tunnel_sock_release(gs->sk);
kfree_rcu(gs, rcu);
}
@@ -1048,7 +1048,7 @@ static struct geneve_sock *geneve_find_sock(struct geneve_net *gn,
struct geneve_sock *gs;
list_for_each_entry(gs, &gn->sock_list, list) {
- if (inet_sk(gs->sock->sk)->inet_sport == dst_port &&
+ if (inet_sk(gs->sk)->inet_sport == dst_port &&
geneve_get_sk_family(gs) == family &&
gs->gro_hint == gro_hint) {
return gs;
@@ -1390,7 +1390,7 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (unlikely(err))
return err;
- udp_tunnel_xmit_skb(rt, gs4->sock->sk, skb, saddr, info->key.u.ipv4.dst,
+ udp_tunnel_xmit_skb(rt, gs4->sk, skb, saddr, info->key.u.ipv4.dst,
tos, ttl, df, sport, geneve->cfg.info.key.tp_dst,
!net_eq(geneve->net, dev_net(geneve->dev)),
!test_bit(IP_TUNNEL_CSUM_BIT, info->key.tun_flags),
@@ -1424,7 +1424,7 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
geneve->cfg.port_min,
geneve->cfg.port_max, true);
- dst = udp_tunnel6_dst_lookup(skb, dev, geneve->net, gs6->sock->sk, 0,
+ dst = udp_tunnel6_dst_lookup(skb, dev, geneve->net, gs6->sk, 0,
&saddr, key, sport,
geneve->cfg.info.key.tp_dst, prio,
use_cache ?
@@ -1480,7 +1480,7 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (unlikely(err))
return err;
- udp_tunnel6_xmit_skb(dst, gs6->sock->sk, skb, dev,
+ udp_tunnel6_xmit_skb(dst, gs6->sk, skb, dev,
&saddr, &key->u.ipv6.dst, prio, ttl,
info->key.label, sport, geneve->cfg.info.key.tp_dst,
!test_bit(IP_TUNNEL_CSUM_BIT,
@@ -1592,7 +1592,7 @@ static int geneve_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
geneve->cfg.port_min,
geneve->cfg.port_max, true);
- dst = udp_tunnel6_dst_lookup(skb, dev, geneve->net, gs6->sock->sk, 0,
+ dst = udp_tunnel6_dst_lookup(skb, dev, geneve->net, gs6->sk, 0,
&saddr, &info->key, sport,
geneve->cfg.info.key.tp_dst, prio,
use_cache ? &info->dst_cache : NULL);
@@ -1654,10 +1654,10 @@ static void geneve_offload_rx_ports(struct net_device *dev, bool push)
list_for_each_entry(gs, &gn->sock_list, list) {
if (push) {
- udp_tunnel_push_rx_port(dev, gs->sock->sk,
+ udp_tunnel_push_rx_port(dev, gs->sk,
UDP_TUNNEL_TYPE_GENEVE);
} else {
- udp_tunnel_drop_rx_port(dev, gs->sock->sk,
+ udp_tunnel_drop_rx_port(dev, gs->sk,
UDP_TUNNEL_TYPE_GENEVE);
}
}
@@ -2183,12 +2183,12 @@ static void geneve_quiesce(struct geneve_dev *geneve, struct geneve_sock **gs4,
*gs4 = rtnl_dereference(geneve->sock4);
rcu_assign_pointer(geneve->sock4, NULL);
if (*gs4)
- rcu_assign_sk_user_data((*gs4)->sock->sk, NULL);
+ rcu_assign_sk_user_data((*gs4)->sk, NULL);
#if IS_ENABLED(CONFIG_IPV6)
*gs6 = rtnl_dereference(geneve->sock6);
rcu_assign_pointer(geneve->sock6, NULL);
if (*gs6)
- rcu_assign_sk_user_data((*gs6)->sock->sk, NULL);
+ rcu_assign_sk_user_data((*gs6)->sk, NULL);
#else
*gs6 = NULL;
#endif
@@ -2201,11 +2201,11 @@ static void geneve_unquiesce(struct geneve_dev *geneve, struct geneve_sock *gs4,
{
rcu_assign_pointer(geneve->sock4, gs4);
if (gs4)
- rcu_assign_sk_user_data(gs4->sock->sk, gs4);
+ rcu_assign_sk_user_data(gs4->sk, gs4);
#if IS_ENABLED(CONFIG_IPV6)
rcu_assign_pointer(geneve->sock6, gs6);
if (gs6)
- rcu_assign_sk_user_data(gs6->sock->sk, gs6);
+ rcu_assign_sk_user_data(gs6->sk, gs6);
#endif
synchronize_net();
}
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v1 net-next 10/15] bareudp: Store struct sock in struct bareudp_dev.
From: Kuniyuki Iwashima @ 2026-05-02 3:13 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260502031401.3557229-1-kuniyu@google.com>
bareudp does not need to access struct socket itself in the fast
path; it only reads struct sock, and struct socket is only used
for tunnel setup and teardown.
Let's store struct sock directly in struct bareudp_dev.
bareudp_sock_release() is called from dev->netdev_ops->ndo_stop().
synchronize_net() in unregister_netdevice_many_notify() ensures that
inflight bareudp RX fast paths finish before bareudp_dev is freed.
bareudp no longer needs synchronize_rcu() in udp_tunnel_sock_release().
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
drivers/net/bareudp.c | 51 +++++++++++++++++++++----------------------
1 file changed, 25 insertions(+), 26 deletions(-)
diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c
index 073ac8a15354..890a0650d9cf 100644
--- a/drivers/net/bareudp.c
+++ b/drivers/net/bareudp.c
@@ -53,7 +53,7 @@ struct bareudp_dev {
__be16 port;
u16 sport_min;
bool multi_proto_mode;
- struct socket __rcu *sock;
+ struct sock __rcu *sk;
struct list_head next; /* bareudp node on namespace list */
struct gro_cells gro_cells;
};
@@ -228,7 +228,7 @@ static void bareudp_uninit(struct net_device *dev)
gro_cells_destroy(&bareudp->gro_cells);
}
-static struct socket *bareudp_create_sock(struct net *net, __be16 port)
+static struct sock *bareudp_create_sock(struct net *net, __be16 port)
{
struct udp_port_cfg udp_conf;
struct socket *sock;
@@ -248,18 +248,18 @@ static struct socket *bareudp_create_sock(struct net *net, __be16 port)
return ERR_PTR(err);
udp_allow_gso(sock->sk);
- return sock;
+ return sock->sk;
}
/* Create new listen socket if needed */
static int bareudp_socket_create(struct bareudp_dev *bareudp, __be16 port)
{
struct udp_tunnel_sock_cfg tunnel_cfg;
- struct socket *sock;
+ struct sock *sk;
- sock = bareudp_create_sock(bareudp->net, port);
- if (IS_ERR(sock))
- return PTR_ERR(sock);
+ sk = bareudp_create_sock(bareudp->net, port);
+ if (IS_ERR(sk))
+ return PTR_ERR(sk);
/* Mark socket as an encapsulation socket */
memset(&tunnel_cfg, 0, sizeof(tunnel_cfg));
@@ -268,29 +268,27 @@ static int bareudp_socket_create(struct bareudp_dev *bareudp, __be16 port)
tunnel_cfg.encap_rcv = bareudp_udp_encap_recv;
tunnel_cfg.encap_err_lookup = bareudp_err_lookup;
tunnel_cfg.encap_destroy = NULL;
- setup_udp_tunnel_sock(bareudp->net, sock->sk, &tunnel_cfg);
+ setup_udp_tunnel_sock(bareudp->net, sk, &tunnel_cfg);
- rcu_assign_pointer(bareudp->sock, sock);
+ rcu_assign_pointer(bareudp->sk, sk);
return 0;
}
static int bareudp_open(struct net_device *dev)
{
struct bareudp_dev *bareudp = netdev_priv(dev);
- int ret = 0;
- ret = bareudp_socket_create(bareudp, bareudp->port);
- return ret;
+ return bareudp_socket_create(bareudp, bareudp->port);
}
static void bareudp_sock_release(struct bareudp_dev *bareudp)
{
- struct socket *sock;
+ struct sock *sk;
- sock = bareudp->sock;
- rcu_assign_pointer(bareudp->sock, NULL);
+ sk = bareudp->sk;
+ rcu_assign_pointer(bareudp->sk, NULL);
synchronize_net();
- udp_tunnel_sock_release(sock->sk);
+ udp_tunnel_sock_release(sk);
}
static int bareudp_stop(struct net_device *dev)
@@ -308,7 +306,7 @@ static int bareudp_xmit_skb(struct sk_buff *skb, struct net_device *dev,
bool udp_sum = test_bit(IP_TUNNEL_CSUM_BIT, info->key.tun_flags);
bool xnet = !net_eq(bareudp->net, dev_net(bareudp->dev));
bool use_cache = ip_tunnel_dst_cache_usable(skb, info);
- struct socket *sock = rcu_dereference(bareudp->sock);
+ struct sock *sk = rcu_dereference(bareudp->sk);
const struct ip_tunnel_key *key = &info->key;
struct rtable *rt;
__be16 sport, df;
@@ -320,7 +318,7 @@ static int bareudp_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (skb_vlan_inet_prepare(skb, skb->protocol != htons(ETH_P_TEB)))
return -EINVAL;
- if (!sock)
+ if (!sk)
return -ESHUTDOWN;
sport = udp_flow_src_port(bareudp->net, skb,
@@ -359,7 +357,7 @@ static int bareudp_xmit_skb(struct sk_buff *skb, struct net_device *dev,
goto free_dst;
skb_set_inner_protocol(skb, bareudp->ethertype);
- udp_tunnel_xmit_skb(rt, sock->sk, skb, saddr, info->key.u.ipv4.dst,
+ udp_tunnel_xmit_skb(rt, sk, skb, saddr, info->key.u.ipv4.dst,
tos, ttl, df, sport, bareudp->port,
!net_eq(bareudp->net, dev_net(bareudp->dev)),
!test_bit(IP_TUNNEL_CSUM_BIT, info->key.tun_flags),
@@ -378,7 +376,7 @@ static int bareudp6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
bool udp_sum = test_bit(IP_TUNNEL_CSUM_BIT, info->key.tun_flags);
bool xnet = !net_eq(bareudp->net, dev_net(bareudp->dev));
bool use_cache = ip_tunnel_dst_cache_usable(skb, info);
- struct socket *sock = rcu_dereference(bareudp->sock);
+ struct sock *sk = rcu_dereference(bareudp->sk);
const struct ip_tunnel_key *key = &info->key;
struct dst_entry *dst = NULL;
struct in6_addr saddr, daddr;
@@ -390,13 +388,13 @@ static int bareudp6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (skb_vlan_inet_prepare(skb, skb->protocol != htons(ETH_P_TEB)))
return -EINVAL;
- if (!sock)
+ if (!sk)
return -ESHUTDOWN;
sport = udp_flow_src_port(bareudp->net, skb,
bareudp->sport_min, USHRT_MAX,
true);
- dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sock->sk, 0, &saddr,
+ dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sk, 0, &saddr,
key, sport, bareudp->port, key->tos,
use_cache ?
(struct dst_cache *) &info->dst_cache : NULL);
@@ -427,7 +425,7 @@ static int bareudp6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
goto free_dst;
daddr = info->key.u.ipv6.dst;
- udp_tunnel6_xmit_skb(dst, sock->sk, skb, dev,
+ udp_tunnel6_xmit_skb(dst, sk, skb, dev,
&saddr, &daddr, prio, ttl,
info->key.label, sport, bareudp->port,
!test_bit(IP_TUNNEL_CSUM_BIT,
@@ -527,12 +525,13 @@ static int bareudp_fill_metadata_dst(struct net_device *dev,
} else if (ip_tunnel_info_af(info) == AF_INET6) {
struct dst_entry *dst;
struct in6_addr saddr;
- struct socket *sock = rcu_dereference(bareudp->sock);
+ struct sock *sk;
- if (!sock)
+ sk = rcu_dereference(bareudp->sk);
+ if (!sk)
return -ESHUTDOWN;
- dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sock->sk,
+ dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sk,
0, &saddr, &info->key,
sport, bareudp->port, info->key.tos,
use_cache ? &info->dst_cache : NULL);
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v1 net-next 11/15] fou: Store struct sock in struct fou.
From: Kuniyuki Iwashima @ 2026-05-02 3:13 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260502031401.3557229-1-kuniyu@google.com>
fou does not need to access struct socket itself in the fast
path; it only reads struct sock, and struct socket is only used
for tunnel setup and teardown.
Let's store struct sock directly in struct fou.
fou_release() frees struct fou with kfree_rcu(), so fou no
longer needs synchronize_rcu() in udp_tunnel_sock_release().
Note that the error path in fou_create() looks buggy; once the
tunnel is set up and fou_add_to_port_list() fails, struct fou
should be freed with kfree_rcu() _after_ udp_tunnel_sock_release().
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
net/ipv4/fou_core.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/net/ipv4/fou_core.c b/net/ipv4/fou_core.c
index 6bed0e1dbe0e..865bd7205122 100644
--- a/net/ipv4/fou_core.c
+++ b/net/ipv4/fou_core.c
@@ -22,7 +22,7 @@
#include "fou_nl.h"
struct fou {
- struct socket *sock;
+ struct sock *sk;
u8 protocol;
u8 flags;
__be16 port;
@@ -508,8 +508,8 @@ static int gue_gro_complete(struct sock *sk, struct sk_buff *skb, int nhoff)
static bool fou_cfg_cmp(struct fou *fou, struct fou_cfg *cfg)
{
- struct sock *sk = fou->sock->sk;
struct udp_port_cfg *udp_cfg = &cfg->udp_config;
+ struct sock *sk = fou->sk;
if (fou->family != udp_cfg->family ||
fou->port != udp_cfg->local_udp_port ||
@@ -559,7 +559,7 @@ static int fou_add_to_port_list(struct net *net, struct fou *fou,
static void fou_release(struct fou *fou)
{
list_del(&fou->list);
- udp_tunnel_sock_release(fou->sock->sk);
+ udp_tunnel_sock_release(fou->sk);
kfree_rcu(fou, rcu);
}
@@ -590,7 +590,7 @@ static int fou_create(struct net *net, struct fou_cfg *cfg,
fou->family = cfg->udp_config.family;
fou->flags = cfg->flags;
fou->type = cfg->type;
- fou->sock = sock;
+ fou->sk = sk;
memset(&tunnel_cfg, 0, sizeof(tunnel_cfg));
tunnel_cfg.encap_type = 1;
@@ -776,9 +776,9 @@ int fou_nl_del_doit(struct sk_buff *skb, struct genl_info *info)
static int fou_fill_info(struct fou *fou, struct sk_buff *msg)
{
- struct sock *sk = fou->sock->sk;
+ struct sock *sk = fou->sk;
- if (nla_put_u8(msg, FOU_ATTR_AF, fou->sock->sk->sk_family) ||
+ if (nla_put_u8(msg, FOU_ATTR_AF, sk->sk_family) ||
nla_put_be16(msg, FOU_ATTR_PORT, fou->port) ||
nla_put_be16(msg, FOU_ATTR_PEER_PORT, sk->sk_dport) ||
nla_put_u8(msg, FOU_ATTR_IPPROTO, fou->protocol) ||
@@ -790,7 +790,7 @@ static int fou_fill_info(struct fou *fou, struct sk_buff *msg)
if (nla_put_flag(msg, FOU_ATTR_REMCSUM_NOPARTIAL))
return -1;
- if (fou->sock->sk->sk_family == AF_INET) {
+ if (sk->sk_family == AF_INET) {
if (nla_put_in_addr(msg, FOU_ATTR_LOCAL_V4, sk->sk_rcv_saddr))
return -1;
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v1 net-next 12/15] amt: Store struct sock in struct amt_dev.
From: Kuniyuki Iwashima @ 2026-05-02 3:13 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev,
Taehee Yoo
In-Reply-To: <20260502031401.3557229-1-kuniyu@google.com>
amt does not need to access struct socket itself in the fast path;
it only reads struct sock, and struct socket is only used for tunnel
setup and teardown.
Let's store struct sock directly in struct amt.
amt_dev_stop() is called as dev->netdev_ops->ndo_stop().
synchronize_net() in unregister_netdevice_many_notify() ensures
that inflight amt RX fast paths finish before amt_dev is freed.
amt no longer needs synchronize_rcu() in udp_tunnel_sock_release().
Note that amt_dev_stop() looks buggy; cancel_delayed_work_sync()
should be called after udp_tunnel_sock_release().
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
CC: Taehee Yoo <ap420073@gmail.com>
---
drivers/net/amt.c | 80 +++++++++++++++++++++++------------------------
include/net/amt.h | 2 +-
2 files changed, 41 insertions(+), 41 deletions(-)
diff --git a/drivers/net/amt.c b/drivers/net/amt.c
index c03aa7c207e6..724a8163a514 100644
--- a/drivers/net/amt.c
+++ b/drivers/net/amt.c
@@ -614,24 +614,24 @@ static void amt_send_discovery(struct amt_dev *amt)
{
struct amt_header_discovery *amtd;
int hlen, tlen, offset;
- struct socket *sock;
struct udphdr *udph;
struct sk_buff *skb;
struct iphdr *iph;
struct rtable *rt;
struct flowi4 fl4;
+ struct sock *sk;
u32 len;
int err;
rcu_read_lock();
- sock = rcu_dereference(amt->sock);
- if (!sock)
+ sk = rcu_dereference(amt->sk);
+ if (!sk)
goto out;
if (!netif_running(amt->stream_dev) || !netif_running(amt->dev))
goto out;
- rt = ip_route_output_ports(amt->net, &fl4, sock->sk,
+ rt = ip_route_output_ports(amt->net, &fl4, sk,
amt->discovery_ip, amt->local_ip,
amt->gw_port, amt->relay_port,
IPPROTO_UDP, 0,
@@ -690,7 +690,7 @@ static void amt_send_discovery(struct amt_dev *amt)
skb->ip_summed = CHECKSUM_NONE;
ip_select_ident(amt->net, skb, NULL);
ip_send_check(iph);
- err = ip_local_out(amt->net, sock->sk, skb);
+ err = ip_local_out(amt->net, sk, skb);
if (unlikely(net_xmit_eval(err)))
amt->dev->stats.tx_errors++;
@@ -703,24 +703,24 @@ static void amt_send_request(struct amt_dev *amt, bool v6)
{
struct amt_header_request *amtrh;
int hlen, tlen, offset;
- struct socket *sock;
struct udphdr *udph;
struct sk_buff *skb;
struct iphdr *iph;
struct rtable *rt;
struct flowi4 fl4;
+ struct sock *sk;
u32 len;
int err;
rcu_read_lock();
- sock = rcu_dereference(amt->sock);
- if (!sock)
+ sk = rcu_dereference(amt->sk);
+ if (!sk)
goto out;
if (!netif_running(amt->stream_dev) || !netif_running(amt->dev))
goto out;
- rt = ip_route_output_ports(amt->net, &fl4, sock->sk,
+ rt = ip_route_output_ports(amt->net, &fl4, sk,
amt->remote_ip, amt->local_ip,
amt->gw_port, amt->relay_port,
IPPROTO_UDP, 0,
@@ -781,7 +781,7 @@ static void amt_send_request(struct amt_dev *amt, bool v6)
skb->ip_summed = CHECKSUM_NONE;
ip_select_ident(amt->net, skb, NULL);
ip_send_check(iph);
- err = ip_local_out(amt->net, sock->sk, skb);
+ err = ip_local_out(amt->net, sk, skb);
if (unlikely(net_xmit_eval(err)))
amt->dev->stats.tx_errors++;
@@ -1000,14 +1000,14 @@ static bool amt_send_membership_update(struct amt_dev *amt,
bool v6)
{
struct amt_header_membership_update *amtmu;
- struct socket *sock;
struct iphdr *iph;
struct flowi4 fl4;
struct rtable *rt;
+ struct sock *sk;
int err;
- sock = rcu_dereference_bh(amt->sock);
- if (!sock)
+ sk = rcu_dereference_bh(amt->sk);
+ if (!sk)
return true;
err = skb_cow_head(skb, LL_RESERVED_SPACE(amt->dev) + sizeof(*amtmu) +
@@ -1039,7 +1039,7 @@ static bool amt_send_membership_update(struct amt_dev *amt,
skb_set_inner_protocol(skb, htons(ETH_P_IP));
else
skb_set_inner_protocol(skb, htons(ETH_P_IPV6));
- udp_tunnel_xmit_skb(rt, sock->sk, skb,
+ udp_tunnel_xmit_skb(rt, sk, skb,
fl4.saddr,
fl4.daddr,
AMT_TOS,
@@ -1060,14 +1060,14 @@ static void amt_send_multicast_data(struct amt_dev *amt,
bool v6)
{
struct amt_header_mcast_data *amtmd;
- struct socket *sock;
struct sk_buff *skb;
struct iphdr *iph;
struct flowi4 fl4;
struct rtable *rt;
+ struct sock *sk;
- sock = rcu_dereference_bh(amt->sock);
- if (!sock)
+ sk = rcu_dereference_bh(amt->sk);
+ if (!sk)
return;
skb = skb_copy_expand(oskb, sizeof(*amtmd) + sizeof(*iph) +
@@ -1097,7 +1097,7 @@ static void amt_send_multicast_data(struct amt_dev *amt,
skb_set_inner_protocol(skb, htons(ETH_P_IP));
else
skb_set_inner_protocol(skb, htons(ETH_P_IPV6));
- udp_tunnel_xmit_skb(rt, sock->sk, skb,
+ udp_tunnel_xmit_skb(rt, sk, skb,
fl4.saddr,
fl4.daddr,
AMT_TOS,
@@ -1116,13 +1116,13 @@ static bool amt_send_membership_query(struct amt_dev *amt,
bool v6)
{
struct amt_header_membership_query *amtmq;
- struct socket *sock;
struct rtable *rt;
struct flowi4 fl4;
+ struct sock *sk;
int err;
- sock = rcu_dereference_bh(amt->sock);
- if (!sock)
+ sk = rcu_dereference_bh(amt->sk);
+ if (!sk)
return true;
err = skb_cow_head(skb, LL_RESERVED_SPACE(amt->dev) + sizeof(*amtmq) +
@@ -1156,7 +1156,7 @@ static bool amt_send_membership_query(struct amt_dev *amt,
skb_set_inner_protocol(skb, htons(ETH_P_IP));
else
skb_set_inner_protocol(skb, htons(ETH_P_IPV6));
- udp_tunnel_xmit_skb(rt, sock->sk, skb,
+ udp_tunnel_xmit_skb(rt, sk, skb,
fl4.saddr,
fl4.daddr,
AMT_TOS,
@@ -2554,24 +2554,24 @@ static void amt_send_advertisement(struct amt_dev *amt, __be32 nonce,
{
struct amt_header_advertisement *amta;
int hlen, tlen, offset;
- struct socket *sock;
struct udphdr *udph;
struct sk_buff *skb;
struct iphdr *iph;
struct rtable *rt;
struct flowi4 fl4;
+ struct sock *sk;
u32 len;
int err;
rcu_read_lock();
- sock = rcu_dereference(amt->sock);
- if (!sock)
+ sk = rcu_dereference(amt->sk);
+ if (!sk)
goto out;
if (!netif_running(amt->stream_dev) || !netif_running(amt->dev))
goto out;
- rt = ip_route_output_ports(amt->net, &fl4, sock->sk,
+ rt = ip_route_output_ports(amt->net, &fl4, sk,
daddr, amt->local_ip,
dport, amt->relay_port,
IPPROTO_UDP, 0,
@@ -2631,7 +2631,7 @@ static void amt_send_advertisement(struct amt_dev *amt, __be32 nonce,
skb->ip_summed = CHECKSUM_NONE;
ip_select_ident(amt->net, skb, NULL);
ip_send_check(iph);
- err = ip_local_out(amt->net, sock->sk, skb);
+ err = ip_local_out(amt->net, sk, skb);
if (unlikely(net_xmit_eval(err)))
amt->dev->stats.tx_errors++;
@@ -2944,7 +2944,7 @@ static int amt_err_lookup(struct sock *sk, struct sk_buff *skb)
return 0;
}
-static struct socket *amt_create_sock(struct net *net, __be16 port)
+static struct sock *amt_create_sock(struct net *net, __be16 port)
{
struct udp_port_cfg udp_conf;
struct socket *sock;
@@ -2960,17 +2960,17 @@ static struct socket *amt_create_sock(struct net *net, __be16 port)
if (err < 0)
return ERR_PTR(err);
- return sock;
+ return sock->sk;
}
static int amt_socket_create(struct amt_dev *amt)
{
struct udp_tunnel_sock_cfg tunnel_cfg;
- struct socket *sock;
+ struct sock *sk;
- sock = amt_create_sock(amt->net, amt->relay_port);
- if (IS_ERR(sock))
- return PTR_ERR(sock);
+ sk = amt_create_sock(amt->net, amt->relay_port);
+ if (IS_ERR(sk))
+ return PTR_ERR(sk);
/* Mark socket as an encapsulation socket */
memset(&tunnel_cfg, 0, sizeof(tunnel_cfg));
@@ -2979,9 +2979,9 @@ static int amt_socket_create(struct amt_dev *amt)
tunnel_cfg.encap_rcv = amt_rcv;
tunnel_cfg.encap_err_lookup = amt_err_lookup;
tunnel_cfg.encap_destroy = NULL;
- setup_udp_tunnel_sock(amt->net, sock->sk, &tunnel_cfg);
+ setup_udp_tunnel_sock(amt->net, sk, &tunnel_cfg);
- rcu_assign_pointer(amt->sock, sock);
+ rcu_assign_pointer(amt->sk, sk);
return 0;
}
@@ -3019,8 +3019,8 @@ static int amt_dev_stop(struct net_device *dev)
{
struct amt_dev *amt = netdev_priv(dev);
struct amt_tunnel_list *tunnel, *tmp;
- struct socket *sock;
struct sk_buff *skb;
+ struct sock *sk;
int i;
cancel_delayed_work_sync(&amt->req_wq);
@@ -3028,11 +3028,11 @@ static int amt_dev_stop(struct net_device *dev)
cancel_delayed_work_sync(&amt->secret_wq);
/* shutdown */
- sock = rtnl_dereference(amt->sock);
- RCU_INIT_POINTER(amt->sock, NULL);
+ sk = rtnl_dereference(amt->sk);
+ RCU_INIT_POINTER(amt->sk, NULL);
synchronize_net();
- if (sock)
- udp_tunnel_sock_release(sock->sk);
+ if (sk)
+ udp_tunnel_sock_release(sk);
cancel_work_sync(&amt->event_wq);
for (i = 0; i < AMT_MAX_EVENTS; i++) {
diff --git a/include/net/amt.h b/include/net/amt.h
index c881bc8b673b..a0255491f5b0 100644
--- a/include/net/amt.h
+++ b/include/net/amt.h
@@ -331,7 +331,7 @@ struct amt_dev {
enum amt_status status;
/* Generated key */
siphash_key_t key;
- struct socket __rcu *sock;
+ struct sock __rcu *sk;
u32 max_groups;
u32 max_sources;
u32 hash_buckets;
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v1 net-next 13/15] pfcp: Store struct sock in struct pfcp_dev.
From: Kuniyuki Iwashima @ 2026-05-02 3:13 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260502031401.3557229-1-kuniyu@google.com>
pfcp does not need to access struct socket itself in the fast
path; it only reads struct sock, and struct socket is only used
for tunnel setup and teardown.
Let's store struct sock directly in struct pfcp_dev.
pfcp_del_sock() is called from dev->netdev_ops->ndo_uninit().
The 2nd synchronize_net() in unregister_netdevice_many_notify()
ensures that inflight pfcp RX fast paths finish before pfcp_dev
is freed.
Note that synchronize_rcu() is added in the error path of
pfcp_newlink() since free_netdev() will free pfcp_dev immediately
once we remove synchronize_rcu() in udp_tunnel_sock_release().
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
drivers/net/pfcp.c | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/drivers/net/pfcp.c b/drivers/net/pfcp.c
index 870137695e8a..5f1c9d2c0b49 100644
--- a/drivers/net/pfcp.c
+++ b/drivers/net/pfcp.c
@@ -18,7 +18,7 @@
struct pfcp_dev {
struct list_head list;
- struct socket *sock;
+ struct sock *sk;
struct net_device *dev;
struct net *net;
@@ -104,8 +104,8 @@ static int pfcp_encap_recv(struct sock *sk, struct sk_buff *skb)
static void pfcp_del_sock(struct pfcp_dev *pfcp)
{
- udp_tunnel_sock_release(pfcp->sock->sk);
- pfcp->sock = NULL;
+ udp_tunnel_sock_release(pfcp->sk);
+ pfcp->sk = NULL;
}
static void pfcp_dev_uninit(struct net_device *dev)
@@ -151,7 +151,7 @@ static void pfcp_link_setup(struct net_device *dev)
netif_keep_dst(dev);
}
-static struct socket *pfcp_create_sock(struct pfcp_dev *pfcp)
+static struct sock *pfcp_create_sock(struct pfcp_dev *pfcp)
{
struct udp_tunnel_sock_cfg tuncfg = {};
struct udp_port_cfg udp_conf = {
@@ -174,14 +174,14 @@ static struct socket *pfcp_create_sock(struct pfcp_dev *pfcp)
setup_udp_tunnel_sock(net, sock->sk, &tuncfg);
- return sock;
+ return sock->sk;
}
static int pfcp_add_sock(struct pfcp_dev *pfcp)
{
- pfcp->sock = pfcp_create_sock(pfcp);
+ pfcp->sk = pfcp_create_sock(pfcp);
- return PTR_ERR_OR_ZERO(pfcp->sock);
+ return PTR_ERR_OR_ZERO(pfcp->sk);
}
static int pfcp_newlink(struct net_device *dev,
@@ -216,6 +216,7 @@ static int pfcp_newlink(struct net_device *dev,
exit_del_pfcp_sock:
pfcp_del_sock(pfcp);
+ synchronize_rcu();
exit_err:
pfcp->net = NULL;
return err;
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v1 net-next 14/15] tipc: Store struct sock in struct udp_bearer.
From: Kuniyuki Iwashima @ 2026-05-02 3:13 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev,
Jon Maloy
In-Reply-To: <20260502031401.3557229-1-kuniyu@google.com>
tipc udp_bearer does not need to access struct socket itself in
the fast path; it only reads struct sock, and struct socket is
only used for tunnel setup and teardown.
Let's store struct sock directly in struct udp_bearer.
Note that cleanup_bearer() calls synchronize_net() after
udp_tunnel_sock_release(), so udp_bearer is not freed until
inflight fast paths finish.
Note also that synchronize_rcu() is added in the error path
of tipc_udp_enable() since udp_bearer will be kfree()d
immediately once we remove synchronize_rcu() in
udp_tunnel_sock_release().
This can be later converted to kfree_rcu().
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
Cc: Jon Maloy <jmaloy@redhat.com>
---
net/tipc/udp_media.c | 32 ++++++++++++++++++--------------
1 file changed, 18 insertions(+), 14 deletions(-)
diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c
index 0db172f1a41a..988b8a7f953a 100644
--- a/net/tipc/udp_media.c
+++ b/net/tipc/udp_media.c
@@ -89,14 +89,14 @@ struct udp_replicast {
/**
* struct udp_bearer - ip/udp bearer data structure
* @bearer: associated generic tipc bearer
- * @ubsock: bearer associated socket
+ * @sk: bearer associated socket
* @ifindex: local address scope
* @work: used to schedule deferred work on a bearer
* @rcast: associated udp_replicast container
*/
struct udp_bearer {
struct tipc_bearer __rcu *bearer;
- struct socket *ubsock;
+ struct sock *sk;
u32 ifindex;
struct work_struct work;
struct udp_replicast rcast;
@@ -194,7 +194,7 @@ static int tipc_udp_xmit(struct net *net, struct sk_buff *skb,
}
ttl = ip4_dst_hoplimit(&rt->dst);
- udp_tunnel_xmit_skb(rt, ub->ubsock->sk, skb, src->ipv4.s_addr,
+ udp_tunnel_xmit_skb(rt, ub->sk, skb, src->ipv4.s_addr,
dst->ipv4.s_addr, 0, ttl, 0, src->port,
dst->port, false, true, 0);
#if IS_ENABLED(CONFIG_IPV6)
@@ -206,7 +206,7 @@ static int tipc_udp_xmit(struct net *net, struct sk_buff *skb,
.saddr = src->ipv6,
.flowi6_proto = IPPROTO_UDP
};
- ndst = ip6_dst_lookup_flow(net, ub->ubsock->sk,
+ ndst = ip6_dst_lookup_flow(net, ub->sk,
&fl6, NULL);
if (IS_ERR(ndst)) {
err = PTR_ERR(ndst);
@@ -215,7 +215,7 @@ static int tipc_udp_xmit(struct net *net, struct sk_buff *skb,
dst_cache_set_ip6(cache, ndst, &fl6.saddr);
}
ttl = ip6_dst_hoplimit(ndst);
- udp_tunnel6_xmit_skb(ndst, ub->ubsock->sk, skb, NULL,
+ udp_tunnel6_xmit_skb(ndst, ub->sk, skb, NULL,
&src->ipv6, &dst->ipv6, 0, ttl, 0,
src->port, dst->port, false, 0);
#endif
@@ -405,9 +405,9 @@ static int tipc_udp_recv(struct sock *sk, struct sk_buff *skb)
static int enable_mcast(struct udp_bearer *ub, struct udp_media_addr *remote)
{
- int err = 0;
+ struct sock *sk = ub->sk;
struct ip_mreqn mreqn;
- struct sock *sk = ub->ubsock->sk;
+ int err = 0;
if (ntohs(remote->proto) == ETH_P_IP) {
mreqn.imr_multiaddr = remote->ipv4;
@@ -670,6 +670,7 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
struct nlattr *opts[TIPC_NLA_UDP_MAX + 1];
u8 node_id[NODE_ID_LEN] = {0,};
struct net_device *dev;
+ struct socket *sock;
int rmcast = 0;
ub = kzalloc_obj(*ub, GFP_ATOMIC);
@@ -764,14 +765,16 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
goto err;
}
udp_conf.local_udp_port = local.port;
- err = udp_sock_create(net, &udp_conf, &ub->ubsock);
+ err = udp_sock_create(net, &udp_conf, &sock);
if (err)
goto err;
+
+ ub->sk = sock->sk;
tuncfg.sk_user_data = ub;
tuncfg.encap_type = 1;
tuncfg.encap_rcv = tipc_udp_recv;
tuncfg.encap_destroy = NULL;
- setup_udp_tunnel_sock(net, ub->ubsock->sk, &tuncfg);
+ setup_udp_tunnel_sock(net, ub->sk, &tuncfg);
err = dst_cache_init(&ub->rcast.dst_cache, GFP_ATOMIC);
if (err)
@@ -793,7 +796,8 @@ static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
free:
dst_cache_destroy(&ub->rcast.dst_cache);
- udp_tunnel_sock_release(ub->ubsock->sk);
+ udp_tunnel_sock_release(ub->sk);
+ synchronize_rcu();
err:
kfree(ub);
return err;
@@ -812,10 +816,10 @@ static void cleanup_bearer(struct work_struct *work)
kfree_rcu(rcast, rcu);
}
- tn = tipc_net(sock_net(ub->ubsock->sk));
+ tn = tipc_net(sock_net(ub->sk));
dst_cache_destroy(&ub->rcast.dst_cache);
- udp_tunnel_sock_release(ub->ubsock->sk);
+ udp_tunnel_sock_release(ub->sk);
/* Note: could use a call_rcu() to avoid another synchronize_net() */
synchronize_net();
@@ -833,11 +837,11 @@ static void tipc_udp_disable(struct tipc_bearer *b)
pr_err("UDP bearer instance not found\n");
return;
}
- sock_set_flag(ub->ubsock->sk, SOCK_DEAD);
+ sock_set_flag(ub->sk, SOCK_DEAD);
RCU_INIT_POINTER(ub->bearer, NULL);
/* sock_release need to be done outside of rtnl lock */
- atomic_inc(&tipc_net(sock_net(ub->ubsock->sk))->wq_count);
+ atomic_inc(&tipc_net(sock_net(ub->sk))->wq_count);
INIT_WORK(&ub->work, cleanup_bearer);
schedule_work(&ub->work);
}
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* [PATCH v1 net-next 15/15] udp_tunnel: Remove synchronize_rcu() in udp_tunnel_sock_release().
From: Kuniyuki Iwashima @ 2026-05-02 3:13 UTC (permalink / raw)
To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Andrew Lunn
Cc: Simon Horman, Kuniyuki Iwashima, Kuniyuki Iwashima, netdev
In-Reply-To: <20260502031401.3557229-1-kuniyu@google.com>
Commit 3cf7203ca620 ("net/tunnel: wait until all sk_user_data
reader finish before releasing the sock") added synchronize_rcu()
in udp_tunnel_sock_release().
This was intended to protect the fast path of a dying vxlan device
from dereferencing vxlan_sock->sock->sk after sock_orphan() has set
sock->sk to NULL.
However, vxlan does not need to access struct socket itself
in the fast path; it only reads struct sock, and struct socket
is only used for tunnel setup and teardown.
This applies to all other UDP tunnel users, and they have been
converted to access struct sock directly.
In addition, each device-specific struct used in their fast paths
is freed after one RCU grace period. Since this occurs after
udp_tunnel_sock_release(), the struct is guaranteed to be freed
after struct udp_sock.
Therefore, synchronize_rcu() in udp_tunnel_sock_release() is
now redundant.
Let's remove it.
Tested:
A script creating/upping vxlan devices in 4000 netns runs 10x
faster with this change. We can see the same improvement with
other UDP tunnel devices as well.
$ cat vxlan.sh
for i in `seq 1 40`
do
(for j in `seq 1 100` ; do
unshare -n bash -c "ip link add vxlan0 type vxlan id 100 local 127.0.0.1 dstport 4789 && ip link set vxlan0 up";
done) &
done
wait
With bpftrace, we can see vxlan_stop() is significantly faster.
bpftrace -e '
kprobe:vxlan_stop {
@start[tid] = nsecs;
}
kretprobe:vxlan_stop /@start[tid]/ {
@duration_us = hist((nsecs - @start[tid]) / 1000);
delete(@start[tid]);
}
END {
printf("\nExecution time of vxlan_stop (us):\n");
}'
Before:
# time ./vxlan.sh // without bpftrace
real 0m50.615s
user 0m8.171s
sys 1m45.101s
@duration_us:
[4K, 8K) 1266 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
[8K, 16K) 1957 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[16K, 32K) 764 |@@@@@@@@@@@@@@@@@@@@ |
[32K, 64K) 6 | |
[64K, 128K) 4 | |
[128K, 256K) 3 | |
After:
# time ./vxlan.sh // without bpftrace
real 0m5.247s
user 0m7.956s
sys 1m47.404s
@duration_us:
[16, 32) 3411 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[32, 64) 383 |@@@@@ |
[64, 128) 107 |@ |
[128, 256) 79 |@ |
[256, 512) 16 | |
[512, 1K) 2 | |
[1K, 2K) 2 | |
Next step is to remove another synchronize_net() in vxlan_stop()
and variants in other devices.
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
net/ipv4/udp_tunnel_core.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/net/ipv4/udp_tunnel_core.c b/net/ipv4/udp_tunnel_core.c
index 44788b95c823..9ab3728f9630 100644
--- a/net/ipv4/udp_tunnel_core.c
+++ b/net/ipv4/udp_tunnel_core.c
@@ -194,7 +194,6 @@ void udp_tunnel_sock_release(struct sock *sk)
struct socket *sock = sk->sk_socket;
rcu_assign_sk_user_data(sk, NULL);
- synchronize_rcu();
kernel_sock_shutdown(sock, SHUT_RDWR);
sock_release(sock);
}
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related
* RE: [PATCH net v3] tipc: fix UAF race in tipc_mon_peer_up/down/remove_peer vs bearer teardown
From: Tung Quang Nguyen @ 2026-05-02 3:35 UTC (permalink / raw)
To: SnailSploit | Kai Aizen
Cc: stable@vger.kernel.org, jmaloy@redhat.com, ying.xue@windriver.com,
kuba@kernel.org, pabeni@redhat.com,
tipc-discussion@lists.sourceforge.net,
tung.q.nguyen@dektech.com.au, lkp@intel.com,
oe-kbuild-all@lists.linux.dev, syzkaller-bugs@googlegroups.com,
netdev@vger.kernel.org, SnailSploit | Kai Aizen, syzbot ci
In-Reply-To: <80ae67e96de2f702028e5bacc89db4575e1531ca.1777559945.git.kai.aizen.dev@gmail.com>
><syzbot+ci779e8ed86620f383@syzkaller.appspotmail.com>
>Subject: [PATCH net v3] tipc: fix UAF race in
>tipc_mon_peer_up/down/remove_peer vs bearer teardown
>
>From: "SnailSploit | Kai Aizen"
><95986478+SnailSploit@users.noreply.github.com>
>
>CVE-2025-40280 fixed tipc_mon_reinit_self() accessing monitors[] from a
>workqueue without RTNL. That patch closed the workqueue path by adding
>rtnl_lock() around the call.
>
>However, three additional functions in the same subsystem access tipc_net-
>>monitors[] from softirq context with no RCU protection at all:
>
> tipc_mon_peer_up() - called from tipc_node_write_unlock()
> tipc_mon_peer_down() - called from tipc_node_write_unlock()
> tipc_mon_remove_peer() - called from tipc_node_link_down()
>
>These are invoked from the packet receive path (tipc_rcv ->
>tipc_node_write_unlock / tipc_node_link_down) and hold only the per-node
>rwlock, not RTNL.
>
>Concurrently, bearer_disable() -- which always holds RTNL -- calls
>tipc_mon_delete(), which sets tn->monitors[bearer_id] = NULL and then
>kfree(mon) without an RCU grace period. A softirq reader can observe the
>non-NULL slot, take a reference, get preempted, and resume after
>kfree(mon) on another CPU, dereferencing freed memory.
>
>Convert monitors[] to __rcu, use rcu_assign_pointer() on creation,
>RCU_INIT_POINTER() + synchronize_rcu() on deletion before kfree(), and the
>appropriate dereference variant at each read site:
>
> - tipc_monitor() returns rcu_dereference_bh(...) for softirq callers
> (tipc_mon_peer_up/down/remove_peer/rcv/prep/get_state).
> - tipc_monitor_rtnl() returns rtnl_dereference(...) for RTNL-held
> callers (tipc_mon_delete via bearer_disable, tipc_mon_reinit_self
> via tipc_net_finalize_work which wraps in rtnl_lock(), and the
> netlink dump handlers tipc_nl_add_monitor_peer /
> __tipc_nl_add_monitor).
>
>Also, get_self() was a thin wrapper over tipc_monitor() + ->self deref,
>duplicating the RCU-checked load that callers already perform on entry.
>With monitors[] becoming __rcu, get_self()'s use of tipc_monitor() generates a
>lockdep splat in tipc_mon_delete() (RTNL context) because the inner load is
>rcu_dereference_bh(). syzbot CI reported this on
>v1/v2 of this patch:
>
> WARNING: suspicious RCU usage in tipc_mon_delete
> net/tipc/monitor.c:108 suspicious rcu_dereference_check() usage!
> ...
> tipc_monitor_rcu_bh+0xf5/0x110 net/tipc/monitor.c:108
> get_self net/tipc/monitor.c:209
> tipc_mon_delete+0x10b/0x4d0 net/tipc/monitor.c:704
>
>Drop get_self() entirely. Each existing caller already has a valid mon pointer
>from its initial RCU-correct load, and mon->self is the result get_self() was
>returning. Replace each "self = get_self(...)"
>with "self = mon->self;". This both removes the duplicate dereference and
>fixes the lockdep splat.
>
>synchronize_rcu() in tipc_mon_delete() is placed after
>write_unlock_bh() and before timer_shutdown_sync() + kfree() so all softirq
>readers that already observed the old pointer have completed before the
>memory is freed.
>
>Fixes: 35c55c9877f8 ("tipc: add neighbor monitoring framework")
>Cc: stable@vger.kernel.org
>Reported-by: kernel test robot <lkp@intel.com>
>Closes: https://lore.kernel.org/oe-kbuild-all/202604301148.jfXKC9HF-
>lkp@intel.com/
>Reported-by: syzbot ci
><syzbot+ci779e8ed86620f383@syzkaller.appspotmail.com>
>Closes: https://ci.syzbot.org/series/6267bc07-4172-4821-b3e5-dac381479d9d
>Signed-off-by: SnailSploit | Kai Aizen
><95986478+SnailSploit@users.noreply.github.com>
>---
> net/tipc/core.h | 2 +-
> net/tipc/monitor.c | 42 +++++++++++++++++++++++-------------------
> 2 files changed, 24 insertions(+), 20 deletions(-)
>
>diff --git a/net/tipc/core.h b/net/tipc/core.h index 9ce5f9ff6..cd582f7a2 100644
>--- a/net/tipc/core.h
>+++ b/net/tipc/core.h
>@@ -109,7 +109,7 @@ struct tipc_net {
> u32 num_links;
>
> /* Neighbor monitoring list */
>- struct tipc_monitor *monitors[MAX_BEARERS];
>+ struct tipc_monitor __rcu *monitors[MAX_BEARERS];
> int mon_threshold;
>
> /* Bearer list */
>diff --git a/net/tipc/monitor.c b/net/tipc/monitor.c index
>a94b9b36a..0095a62ae 100644
>--- a/net/tipc/monitor.c
>+++ b/net/tipc/monitor.c
>@@ -99,7 +99,14 @@ struct tipc_monitor {
>
> static struct tipc_monitor *tipc_monitor(struct net *net, int bearer_id) {
>- return tipc_net(net)->monitors[bearer_id];
>+ return rcu_dereference_bh(tipc_net(net)->monitors[bearer_id]);
Please use rcu_ dereference() because the read-side does not use RCU_bh markers.
>+}
>+
>+/* tipc_monitor_rtnl - dereference monitors[] from RTNL-held control
>+path. */ static struct tipc_monitor * __maybe_unused
>+tipc_monitor_rtnl(struct net *net, int bearer_id) {
Please use simple form like this for readability:
static struct tipc_monitor* tipc_monitor_rtnl(struct net *net,
int bearer_id)
>+ return rtnl_dereference(tipc_net(net)->monitors[bearer_id]);
> }
>
> const int tipc_max_domain_size = sizeof(struct tipc_mon_domain); @@ -
>192,13 +199,6 @@ static struct tipc_peer *get_peer(struct tipc_monitor *mon,
>u32 addr)
> return NULL;
> }
>
>-static struct tipc_peer *get_self(struct net *net, int bearer_id) -{
>- struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
>-
>- return mon->self;
>-}
>-
> static inline bool tipc_mon_is_active(struct net *net, struct tipc_monitor
>*mon) {
> struct tipc_net *tn = tipc_net(net);
>@@ -358,7 +358,7 @@ void tipc_mon_remove_peer(struct net *net, u32 addr,
>int bearer_id)
> if (!mon)
> return;
>
>- self = get_self(net, bearer_id);
>+ self = mon->self;
> write_lock_bh(&mon->lock);
> peer = get_peer(mon, addr);
> if (!peer)
>@@ -422,9 +422,12 @@ static bool tipc_mon_add_peer(struct tipc_monitor
>*mon, u32 addr, void tipc_mon_peer_up(struct net *net, u32 addr, int
>bearer_id) {
> struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
>- struct tipc_peer *self = get_self(net, bearer_id);
>+ struct tipc_peer *self;
> struct tipc_peer *peer, *head;
>
>+ if (!mon)
>+ return;
>+ self = mon->self;
> write_lock_bh(&mon->lock);
> peer = get_peer(mon, addr);
> if (!peer && !tipc_mon_add_peer(mon, addr, &peer)) @@ -449,7
>+452,7 @@ void tipc_mon_peer_down(struct net *net, u32 addr, int
>bearer_id)
> if (!mon)
> return;
>
>- self = get_self(net, bearer_id);
>+ self = mon->self;
> write_lock_bh(&mon->lock);
> peer = get_peer(mon, addr);
> if (!peer) {
>@@ -651,7 +654,7 @@ int tipc_mon_create(struct net *net, int bearer_id)
> struct tipc_peer *self;
> struct tipc_mon_domain *dom;
>
>- if (tn->monitors[bearer_id])
>+ if (rtnl_dereference(tn->monitors[bearer_id]))
> return 0;
>
> mon = kzalloc_obj(*mon, GFP_ATOMIC);
>@@ -663,7 +666,7 @@ int tipc_mon_create(struct net *net, int bearer_id)
> kfree(dom);
> return -ENOMEM;
> }
>- tn->monitors[bearer_id] = mon;
>+ rcu_assign_pointer(tn->monitors[bearer_id], mon);
> rwlock_init(&mon->lock);
> mon->net = net;
> mon->peer_cnt = 1;
>@@ -682,16 +685,16 @@ int tipc_mon_create(struct net *net, int bearer_id)
>void tipc_mon_delete(struct net *net, int bearer_id) {
> struct tipc_net *tn = tipc_net(net);
>- struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
>+ struct tipc_monitor *mon = tipc_monitor_rtnl(net, bearer_id);
> struct tipc_peer *self;
> struct tipc_peer *peer, *tmp;
>
> if (!mon)
> return;
>
>- self = get_self(net, bearer_id);
>+ self = mon->self;
>+ RCU_INIT_POINTER(tn->monitors[bearer_id], NULL);
> write_lock_bh(&mon->lock);
>- tn->monitors[bearer_id] = NULL;
> list_for_each_entry_safe(peer, tmp, &self->list, list) {
> list_del(&peer->list);
> hlist_del(&peer->hash);
>@@ -700,6 +703,7 @@ void tipc_mon_delete(struct net *net, int bearer_id)
> }
> mon->self = NULL;
> write_unlock_bh(&mon->lock);
>+ synchronize_rcu();
Please use kfree_rcu() instead.
> timer_shutdown_sync(&mon->timer);
> kfree(self->domain);
> kfree(self);
>@@ -712,7 +716,7 @@ void tipc_mon_reinit_self(struct net *net)
> int bearer_id;
>
> for (bearer_id = 0; bearer_id < MAX_BEARERS; bearer_id++) {
>- mon = tipc_monitor(net, bearer_id);
>+ mon = tipc_monitor_rtnl(net, bearer_id);
> if (!mon)
> continue;
> write_lock_bh(&mon->lock);
>@@ -798,7 +802,7 @@ static int __tipc_nl_add_monitor_peer(struct tipc_peer
>*peer, int tipc_nl_add_monitor_peer(struct net *net, struct tipc_nl_msg *msg,
> u32 bearer_id, u32 *prev_node)
> {
>- struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
>+ struct tipc_monitor *mon = tipc_monitor_rtnl(net, bearer_id);
> struct tipc_peer *peer;
>
> if (!mon)
>@@ -827,7 +831,7 @@ int tipc_nl_add_monitor_peer(struct net *net, struct
>tipc_nl_msg *msg, int __tipc_nl_add_monitor(struct net *net, struct
>tipc_nl_msg *msg,
> u32 bearer_id)
> {
>- struct tipc_monitor *mon = tipc_monitor(net, bearer_id);
>+ struct tipc_monitor *mon = tipc_monitor_rtnl(net, bearer_id);
> char bearer_name[TIPC_MAX_BEARER_NAME];
> struct nlattr *attrs;
> void *hdr;
>--
>2.43.0
>
^ permalink raw reply
* Re: [PATCH net-next v2 1/4] r8152: Add support for 10Gbit Link Speeds and EEE
From: Birger Koblitz @ 2026-05-02 4:18 UTC (permalink / raw)
To: Jakub Kicinski
Cc: andrew+netdev, davem, edumazet, pabeni, linux-usb, netdev,
linux-kernel, hsu.chih.kai
In-Reply-To: <20260501011556.3154166-1-kuba@kernel.org>
Thanks a lot for reviewing this patch, Jakub!
On 01/05/2026 3:15 am, Jakub Kicinski wrote:
>> + if (enable && (tp->eee_adv2 & MDIO_EEE_10GT))
>> + config |= MDIO_EEE_10GT;
>> + else
>> + config &= ~MDIO_EEE_10GT;
>> +
>> ocp_reg_write(tp, OCP_EEE_ADV2, config);
>> }
>
> Is MDIO_EEE_10GT actually a bit of the EEE advertisement 2 register
> (7.62, cap2 / OCP_EEE_ADV2)?
>
No, and it should have been clear to me. The entire bit of code is
actually unneccessary, as the MDIO_EEE_10GT-BIT is part of OCP_EEE_ADV
and is set in tp->eee_adv, so that rtl_eee_enable() correctly already
sets it:
static void rtl_eee_enable(struct r8152 *tp, bool enable)
{
case RTL_VER_17:
if (enable) {
r8156_eee_en(tp, true);
ocp_reg_write(tp, OCP_EEE_ADV, tp->eee_adv);
}
...
}
Will be fixed in v3.
>
> [ ... ]
>
>> @@ -8968,6 +9008,13 @@ static int r8153_get_eee(struct r8152 *tp, struct ethtool_keee *eee)
>> linkmode_set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, common);
>> }
>>
>> + if (tp->support_10000full) {
>> + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, eee->supported);
>> +
>> + if (speed & _10000bps)
>> + linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, common);
>> + }
>> +
>> eee->eee_enabled = tp->eee_en;
>
> Should this hunk also extend the earlier gating inside r8153_get_eee()?
>
> A few lines above, the cap2 register reads are still gated on:
>
> if (tp->support_2500full || tp->support_5000full) {
> val = ocp_reg_read(tp, OCP_EEE_ADV2);
> mii_eee_cap2_mod_linkmode_adv_t(eee->advertised, val);
>
> val = ocp_reg_read(tp, OCP_EEE_LPABLE2);
> mii_eee_cap2_mod_linkmode_adv_t(eee->lp_advertised, val);
> }
>
> The equivalent disjunction in rtl8152_set_speed() and
> rtl8152_get_link_ksettings() was updated to include support_10000full,
> but r8153_get_eee() (and r8152_set_eee() at the analogous spot) still
> only checks support_2500full || support_5000full.
>
> A hypothetical chip that sets only support_10000full would bypass the
> OCP_EEE_ADV2 / OCP_EEE_LPABLE2 reads and never reflect 10G EEE into
> eee->advertised. Should the condition be made consistent with the
> other sites?
The AI here actually contradicts itself with the previous issue. The
10GBit modes are part of OCP_EEE_ADV (MMD 7.0x003C) and OCP_EEE_LPABLE
(MMD 7.0x003D). A hypothetical chip that sets only support_10000full
would still reflect the correct LP-ability 10GBit setting.
>
>> @@ -9982,6 +10029,11 @@ static int rtl8152_probe_once(struct usb_interface *intf,
>> tp->speed = SPEED_5000;
>> tp->advertising |= RTL_ADVERTISED_5000_FULL;
>> }
>> + if (tp->support_10000full &&
>> + tp->udev->speed >= USB_SPEED_SUPER) {
>> + tp->speed = SPEED_10000;
>> + tp->advertising |= RTL_ADVERTISED_10000_FULL;
>> + }
>> tp->advertising |= RTL_ADVERTISED_1000_FULL;
>> }
>> tp->duplex = DUPLEX_FULL;
>
> Is USB_SPEED_SUPER the correct gate for defaulting to a 10 Gbit link?
I believe this is the correct gate. In order to get the full speed out
of the 10GBit Ethernet connection, a 20GBit USB link would be needed,
anyway, with 10GBit USB-C, i.e. USB_SPEED_SUPER_PLUS, I see only about
7.6GBit throughput. The difference of 10GBit wire-speed to 5GBit Speed
for USB 3.0 is not that bad, and the overhead does not seem to make a
problem.
But I think this is not the point: an important aspect of the 10GBit
USB-Ethernet adapters is that they are used for testing 10GBit
connections with network equipment. The RTL8159 is even put in
connection with a media converter and an SFP+ cage in the same housing
and advertised for testing 10GBit fiber lines. It should be possible to
test and use 10GBit connectivity even on a 5GBit USB port.
Birger
^ permalink raw reply
* Re: [PATCH 00/11] mlx5 support for VFIO self test
From: Alex Williamson @ 2026-05-02 4:31 UTC (permalink / raw)
To: Jason Gunthorpe, David Matlack, kvm, Leon Romanovsky,
linux-kselftest, linux-rdma, Mark Bloch, netdev, Saeed Mahameed,
Shuah Khan, Tariq Toukan
Cc: patches
In-Reply-To: <0-v1-dc5fa250ca1d+3213-mlx5st_jgg@nvidia.com>
On Thu, Apr 30, 2026, at 6:08 PM, Jason Gunthorpe wrote:
> Add an mlx5 driver to VFIO self test. This is largely a remix of the
> existing VFIO mlx5 driver in rdma-core. It uses an RDMA loopback QP
> to issue RDMA WRITE operations which effectively perform memory
> copies using DMA. Since mlx5 has a stable programming ABI this
> should work on devices from CX5 to current HW. The device FW must
> support the QP loopback configuration.
Does the PCI ID table in the series need some pruning then? It includes CX4.
Thanks,
Alex
^ permalink raw reply
* Re: [PATCH net-next v2 2/4] r8152: Add support for the RTL8159 chip
From: Birger Koblitz @ 2026-05-02 4:39 UTC (permalink / raw)
To: Jakub Kicinski
Cc: andrew+netdev, davem, edumazet, pabeni, linux-usb, netdev,
linux-kernel, hsu.chih.kai
In-Reply-To: <20260501011558.3154185-1-kuba@kernel.org>
On 01/05/2026 3:15 am, Jakub Kicinski wrote:
> The commit subject says RTL8159 is being added, but the body reads
> "The RTL8157 re-uses the packet descriptor format introduced with the
> RTL8157 ..." and later "Add support for the USB device ID of Realtek
> RTL8157-based adapters." Should these occurrences of RTL8157 actually
> be RTL8159?
Both are typos and RTL8159 was meant. Will be fixed in v3.
>
>> Most of the addiitonal code is added in r8157_hw_phy_cfg() to configure
> ^^^^^^^^^^
>
> Typo: addiitonal.
Will fix.
>
>> Add support for the USB device ID of Realtek RTL8157-based adapters. Detect
>> the RTL8159 as RTL_VER_17 and set it up.
>
> The new entry in rtl8152_table is 0x815a, which breaks the earlier
> 0x815N-for-RTL815N naming convention (0x8157 for RTL8157, etc.). Could
> the commit message explain why the RTL8159 ships with product id
> 0x815a?
Will fix.
>
>> diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
>> index 01e65d845f87..2a07dde289e2 100644
>> --- a/drivers/net/usb/r8152.c
>> +++ b/drivers/net/usb/r8152.c
>
> [ ... ]
>
>> @@ -6894,7 +6903,7 @@ static void rtl8156_down(struct r8152 *tp)
>> PLA_MCU_SPDWN_EN);
>>
>> r8153b_u1u2en(tp, false);
>> - if (tp->version != RTL_VER_16) {
>> + if (tp->version < RTL_VER_16) {
>> r8153_u2p3en(tp, false);
>> r8153b_power_cut_en(tp, false);
>> }
>
> rtl8156_down() is converted from "!= RTL_VER_16" to "< RTL_VER_16" so
> that RTL_VER_17 takes the same path as RTL_VER_16. rtl8156_up() is
> wired as ops->up for RTL_VER_17 in rtl_ops_init() but still uses
> "!= RTL_VER_16" / "== RTL_VER_16" checks that were not converted.
This was simply forgotten. The code-path for the RTL8159, i.e.
RTL_VER_17 should be the same as for the RTL8157 (RTL_VER_16), will fix
rtl8156_up() accordingly.
> The rest of the driver organizes helpers by chip family (r8152_,
> r8153_, r8156_, r8157_) and the commit message describes the RTL8159
> as reusing the RTL8157 scaffolding. Is the r8159_ prefix on this
> single helper intentional, or would r8157_wait_backup_restore() (or
> similar) fit better?
The prefix is intentional, since the function is only used for the
RTL8159.
>
> Also, poll_timeout_us() is invoked with a 200 us poll interval and a
> 2000 us overall timeout, giving roughly ten iterations before the
> error path fires. Other GPHY-related waits in the driver allow up to
> the order of seconds. Is 2 ms known to be sufficient for the
> backup-restore handshake on RTL8159 across suspend/resume and on
> slower hosts?
Experimentally, the function returns always on the first iteration,
which is the reason for the short poll interval. A safety factor of 10
appears to be relatively generous. The out-of-tree code was busy waiting
for 100 iterations without any delay. The polling in wait_cmd_ready()
waits for up-to 20ms. Since it does not hurt and to be on the safe side,
I will change the total poll time to 20ms, here, too.
>
>> @@ -8184,14 +8389,14 @@ static void r8156_init(struct r8152 *tp)
> Later in the same function:
>
> if (tp->version != RTL_VER_16)
> ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3,
> PLA_MCU_SPDWN_EN);
>
> Was this site meant to be converted to "< RTL_VER_16" along with the
> others? As written, RTL_VER_17 clears PLA_MCU_SPDWN_EN during init,
> while RTL_VER_16 does not.
Indeed, this was an oversight, it should have been also converted to
"<". Will fix.
>
> [ ... ]
>
>> @@ -8294,7 +8505,10 @@ static void r8156_init(struct r8152 *tp)
>> set_bit(GREEN_ETHERNET, &tp->flags);
>>
>> /* rx aggregation / 16 bytes Rx descriptor */
>> - if (tp->version == RTL_VER_16)
>> + if (tp->version == RTL_VER_17)
>> + ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL,
>> + RX_AGG_DISABLE | RX_DESC_16B | BIT(11));
>> + else if (tp->version == RTL_VER_16)
>> ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL, RX_AGG_DISABLE | RX_DESC_16B);
>
> Every other USB_USB_CTRL bit written in this driver uses a named
> macro (CDC_ECM_EN, RX_AGG_DISABLE, RX_ZERO_EN, RX_DESC_16B). Could
> BIT(11) be given a symbolic name so future multi-version handling of
> this register stays self-describing?
Unfortunately, BIT(11) just appears here, for the RTL8159 in the
out-of-tree code, without any further explanation. For BIT(10) =
RX_DESC_16B I guessed the name from the comment. That bit appears for
both chips, but BIT(11) just is there for the RTL8159, while the comment
stays the same. Any guess could turn out to be misleading.
>
> [ ... ]
>
>> @@ -9690,6 +9904,29 @@ static int rtl_ops_init(struct r8152 *tp)
>> r8157_desc_init(tp);
>> break;
>>
>> + case RTL_VER_17:
>> + tp->eee_en = true;
>> + tp->eee_adv = MDIO_EEE_100TX | MDIO_EEE_1000T | MDIO_EEE_10GT;
>> + tp->eee_adv2 = MDIO_EEE_2_5GT | MDIO_EEE_5GT;
>> + ops->init = r8156_init;
>> + ops->enable = rtl8156_enable;
>> + ops->disable = rtl8153_disable;
>> + ops->up = rtl8156_up;
>> + ops->down = rtl8156_down;
>
> ops->up is wired to rtl8156_up() here, but rtl8156_up() still checks
> for RTL_VER_16 exclusively at the sites noted above. See the
> rtl8156_down() hunk for the pattern that appears to be intended.
This is fixed, see above.
> RTL_VER_17 sets rx_buf_sz to 48 KiB and enables support_10000full,
> but the max_mtu switch in rtl8152_probe() does not include
> RTL_VER_17:
>
> case RTL_VER_12:
> case RTL_VER_13:
> case RTL_VER_15:
> case RTL_VER_16:
> netdev->max_mtu = size_to_mtu(16 * 1024);
> break;
> ...
> default:
> netdev->max_mtu = ETH_DATA_LEN;
> break;
>
> Should RTL_VER_17 also select the 16 KiB max_mtu case so jumbo frames
> are available on the new 10 Gbit chip?
Yes, this was an oversight. Will be fixed.
>
> Separately, rtl8156_enable() is set as ops->enable for RTL_VER_17 but
> the USB_RX_AGGR_NUM reset is guarded by
> "tp->version >= RTL_VER_12 && tp->version <= RTL_VER_16", so
> RTL_VER_17 does not execute it. Is that intended given RTL_VER_17
> otherwise shares the RTL_VER_16 descriptor/aggregation plumbing
> (r8157_desc_init(), and the USB_USB_CTRL aggregation bits cleared in
> r8156_init() for RTL_VER_17)?
>
> [ ... ]
RTL_VER_17 should be included in that range. Will fix.
^ permalink raw reply
* Re: [PATCH net-next v2 3/4] r8152: Add irq mitigation for RTL8157/9
From: Birger Koblitz @ 2026-05-02 4:41 UTC (permalink / raw)
To: Jakub Kicinski
Cc: andrew+netdev, davem, edumazet, pabeni, linux-usb, netdev,
linux-kernel, hsu.chih.kai, Michal Pecio
In-Reply-To: <20260501011600.3154202-1-kuba@kernel.org>
On 01/05/2026 3:16 am, Jakub Kicinski wrote:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> ---
> r8152: Add irq mitigation for RTL8157/9
>
> This commit clears bits 0, 1, 2, and 7 of USB register 0xcf04 in
> r8156_init() for RTL_VER_16 and later, with the stated intent of avoiding
> intr URB completions with urb->status == -ESHUTDOWN on RTL8157 and
> RTL8159.
Based on the discussion in the parallel thread with Andrew and Michal,
I will drop this part of the series, as the issue this patch tries to
address is harmless, better solved differently, and it affects
performance. I will submit a separate patch as bug-fix to remove the
same register settings done in rtl8153_unload(), as this also applies to
the RTL8157.
^ permalink raw reply
* [PATCH net-next v3 1/3] r8152: Add support for 10Gbit Link Speeds and EEE
From: Birger Koblitz @ 2026-05-02 4:50 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: linux-usb, netdev, linux-kernel, Chih Kai Hsu, Birger Koblitz,
Andrew Lunn, Aleksander Jan Bajkowski
In-Reply-To: <20260502-rtl8159_net_next-v3-0-12c895d46cd6@birger-koblitz.de>
The RTL8159 supports 10GBit Link speeds. Add support for this speed
in the setup and setting/getting through ethtool. Also add 10GBit EEE.
Add functionality for setup and ethtool get/set methods.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Tested-by: Aleksander Jan Bajkowski <olek2@wp.pl>
---
drivers/net/usb/r8152.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 50 insertions(+), 3 deletions(-)
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index ae834876aa1acc7a9af08a4c01b7a984c4ab8433..05abfab1df94a571347196bfce7c232865e1058e 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -621,6 +621,7 @@ enum spd_duplex {
FORCE_1000M_FULL,
NWAY_2500M_FULL,
NWAY_5000M_FULL,
+ NWAY_10000M_FULL,
};
/* OCP_ALDPS_CONFIG */
@@ -742,6 +743,7 @@ enum spd_duplex {
#define BP4_SUPER_ONLY 0x1578 /* RTL_VER_04 only */
enum rtl_register_content {
+ _10000bps = BIT(14),
_5000bps = BIT(12),
_2500bps = BIT(10),
_1250bps = BIT(9),
@@ -757,6 +759,8 @@ enum rtl_register_content {
#define is_speed_2500(_speed) (((_speed) & (_2500bps | LINK_STATUS)) == (_2500bps | LINK_STATUS))
#define is_speed_5000(_speed) (((_speed) & (_5000bps | LINK_STATUS)) == (_5000bps | LINK_STATUS))
+#define is_speed_10000(_speed) (((_speed) & (_10000bps | LINK_STATUS)) \
+ == (_10000bps | LINK_STATUS))
#define is_flow_control(_speed) (((_speed) & (_tx_flow | _rx_flow)) == (_tx_flow | _rx_flow))
#define RTL8152_MAX_TX 4
@@ -1008,6 +1012,7 @@ struct r8152 {
u32 support_2500full:1;
u32 support_5000full:1;
+ u32 support_10000full:1;
u32 lenovo_macpassthru:1;
u32 dell_tb_rx_agg_bug:1;
u16 ocp_base;
@@ -1260,6 +1265,7 @@ enum tx_csum_stat {
#define RTL_ADVERTISED_1000_FULL BIT(5)
#define RTL_ADVERTISED_2500_FULL BIT(6)
#define RTL_ADVERTISED_5000_FULL BIT(7)
+#define RTL_ADVERTISED_10000_FULL BIT(8)
/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
* The RTL chips use a 64 element hash table based on the Ethernet CRC.
@@ -6513,6 +6519,9 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex,
if (tp->support_5000full)
support |= RTL_ADVERTISED_5000_FULL;
+
+ if (tp->support_10000full)
+ support |= RTL_ADVERTISED_10000_FULL;
}
advertising &= support;
@@ -6559,9 +6568,10 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex,
r8152_mdio_write(tp, MII_CTRL1000, new1);
}
- if (tp->support_2500full || tp->support_5000full) {
+ if (tp->support_2500full || tp->support_5000full || tp->support_10000full) {
orig = ocp_reg_read(tp, OCP_10GBT_CTRL);
- new1 = orig & ~(MDIO_AN_10GBT_CTRL_ADV2_5G | MDIO_AN_10GBT_CTRL_ADV5G);
+ new1 = orig & ~(MDIO_AN_10GBT_CTRL_ADV2_5G | MDIO_AN_10GBT_CTRL_ADV5G
+ | MDIO_AN_10GBT_CTRL_ADV10G);
if (advertising & RTL_ADVERTISED_2500_FULL) {
new1 |= MDIO_AN_10GBT_CTRL_ADV2_5G;
@@ -6573,6 +6583,11 @@ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u32 speed, u8 duplex,
tp->ups_info.speed_duplex = NWAY_5000M_FULL;
}
+ if (advertising & RTL_ADVERTISED_10000_FULL) {
+ new1 |= MDIO_AN_10GBT_CTRL_ADV10G;
+ tp->ups_info.speed_duplex = NWAY_10000M_FULL;
+ }
+
if (orig != new1)
ocp_reg_write(tp, OCP_10GBT_CTRL, new1);
}
@@ -8708,7 +8723,10 @@ int rtl8152_get_link_ksettings(struct net_device *netdev,
linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
cmd->link_modes.supported, tp->support_5000full);
- if (tp->support_2500full || tp->support_5000full) {
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+ cmd->link_modes.supported, tp->support_10000full);
+
+ if (tp->support_2500full || tp->support_5000full || tp->support_10000full) {
u16 ocp_10gbt_ctrl = ocp_reg_read(tp, OCP_10GBT_CTRL);
u16 ocp_10gbt_stat = ocp_reg_read(tp, OCP_10GBT_STAT);
@@ -8737,6 +8755,19 @@ int rtl8152_get_link_ksettings(struct net_device *netdev,
if (is_speed_5000(rtl8152_get_speed(tp)))
cmd->base.speed = SPEED_5000;
}
+
+ if (tp->support_10000full) {
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+ cmd->link_modes.advertising,
+ ocp_10gbt_ctrl & MDIO_AN_10GBT_CTRL_ADV10G);
+
+ linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+ cmd->link_modes.lp_advertising,
+ ocp_10gbt_stat & MDIO_AN_10GBT_STAT_LP10G);
+
+ if (is_speed_10000(rtl8152_get_speed(tp)))
+ cmd->base.speed = SPEED_10000;
+ }
}
mutex_unlock(&tp->control);
@@ -8790,6 +8821,10 @@ static int rtl8152_set_link_ksettings(struct net_device *dev,
cmd->link_modes.advertising))
advertising |= RTL_ADVERTISED_5000_FULL;
+ if (test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+ cmd->link_modes.advertising))
+ advertising |= RTL_ADVERTISED_10000_FULL;
+
mutex_lock(&tp->control);
ret = rtl8152_set_speed(tp, cmd->base.autoneg, cmd->base.speed,
@@ -8953,6 +8988,13 @@ static int r8153_get_eee(struct r8152 *tp, struct ethtool_keee *eee)
linkmode_set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, common);
}
+ if (tp->support_10000full) {
+ linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, eee->supported);
+
+ if (speed & _10000bps)
+ linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, common);
+ }
+
eee->eee_enabled = tp->eee_en;
if (speed & _1000bps)
@@ -9967,6 +10009,11 @@ static int rtl8152_probe_once(struct usb_interface *intf,
tp->speed = SPEED_5000;
tp->advertising |= RTL_ADVERTISED_5000_FULL;
}
+ if (tp->support_10000full &&
+ tp->udev->speed >= USB_SPEED_SUPER) {
+ tp->speed = SPEED_10000;
+ tp->advertising |= RTL_ADVERTISED_10000_FULL;
+ }
tp->advertising |= RTL_ADVERTISED_1000_FULL;
}
tp->duplex = DUPLEX_FULL;
--
2.47.3
^ permalink raw reply related
* [PATCH net-next v3 0/3] r8152: Add support for the RTL8159 10Gbit USB Ethernet chip
From: Birger Koblitz @ 2026-05-02 4:50 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: linux-usb, netdev, linux-kernel, Chih Kai Hsu, Birger Koblitz,
Andrew Lunn, Aleksander Jan Bajkowski
Add support for the RTL8159, which is a 10GBit USB-Ethernet adapter
chip in the RTL815x family of chips.
The RTL8159 re-uses the frame descriptor format and SRAM2 access introduced
with the RTL8157 as well as most of the setup and PM logic of the RTL8157.
The module was tested with a Lekuo DR59R11 USB-C 10GbE Ethernet Adapter:
[ 2502.906947] usb 2-1: new SuperSpeed USB device number 3 using xhci_hcd
[ 2502.927859] usb 2-1: New USB device found, idVendor=0bda, idProduct=815a, bcdDevice=30.00
[ 2502.927867] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=7
[ 2502.927871] usb 2-1: Product: USB 10/100/1G/2.5G/5G/10G LAN
[ 2502.927873] usb 2-1: Manufacturer: Realtek
[ 2502.927875] usb 2-1: SerialNumber: 000388C9B3B5XXXX
[ 2503.063745] r8152-cfgselector 2-1: reset SuperSpeed USB device number 3 using xhci_hcd
[ 2503.123876] r8152 2-1:1.0: Requesting firmware: rtl_nic/rtl8159-1.fw
[ 2503.126267] r8152 2-1:1.0: PHY firmware installed 0 to be loaded: 20
[ 2503.156265] r8152 2-1:1.0: load rtl8159-1 v1 2026/01/01 successfully
[ 2503.270729] r8152 2-1:1.0 eth0: v1.12.13
[ 2503.289349] r8152 2-1:1.0 enx88c9b3b5xxxx: renamed from eth0
[ 2507.777055] r8152 2-1:1.0 enx88c9b3b5xxxx: carrier on
The RTL8159 adapter was tested against an AQC107 PCIe-card supporting
10GBit/s and an RTL8157 5Gbit USB-Ethernet adapter supporting 5GBit/s for
performance, link speed and EEE negotiation. Using USB3.2 Gen 2 (20GBit) with
the RTL8159 USB adapter and running iperf3 against the AQC107 PCIe
card resulted in 8.96 Gbits/sec transfer speed.
The code is based on the out-of-tree r8152 driver published by Realtek under
the GPL.
The RTL8159 requires firmware for the PHY in order to achieve a 10GBit link
speed. Without firmware, only 5GBit were achieved. The firmware can be
extracted from the out-of-tree r8152 driver-code where it is stored in the
ram17 u8-array. Code is added to use the existing firmware upload mechanism
of the driver for the RTL8157/9 PHY firmware code. The firmware will be
submitted separately to linux-firmware.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
Changes in v3:
- Various references to RTL8157 in the commit description changed
to RTL8159
- Fix incorrect use of OCP_EEE_ADV2 register for 10GBit
- Fix various instances where a gate on RTL_VER_16 was not extended
to RTL_VER_17, although RTL8159 needs the same code-path as RTL8157
- Explain use of 0x815a device-ID for RTL8159 in commit message
- Extend poll-timeout for backup-restore to 20ms
- Drop patch for IRQ mitigation
- Link to v2: https://lore.kernel.org/r/20260429-rtl8159_net_next-v2-0-bab3cd4e4c66@birger-koblitz.de
Changes in v2:
- Correct formatting of comments
- Order case statement values correctly
- Add error message when backup-restore fails
- Correct commit message of support for firmware upload
- Link to v1: https://lore.kernel.org/r/20260428-rtl8159_net_next-v1-0-52d03927b46f@birger-koblitz.de
---
Birger Koblitz (3):
r8152: Add support for 10Gbit Link Speeds and EEE
r8152: Add support for the RTL8159 chip
r8152: Add firmware upload capability for RTL8157/RTL8159
drivers/net/usb/r8152.c | 340 +++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 321 insertions(+), 19 deletions(-)
---
base-commit: 0f19519fdd22bc0f5429377da3a52327c5eee166
change-id: 20260427-rtl8159_net_next-4f778a614fa7
Best regards,
--
Birger Koblitz <mail@birger-koblitz.de>
^ permalink raw reply
* [PATCH net-next v3 3/3] r8152: Add firmware upload capability for RTL8157/RTL8159
From: Birger Koblitz @ 2026-05-02 4:50 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: linux-usb, netdev, linux-kernel, Chih Kai Hsu, Birger Koblitz,
Aleksander Jan Bajkowski
In-Reply-To: <20260502-rtl8159_net_next-v3-0-12c895d46cd6@birger-koblitz.de>
The RTL8159 (RTL_VER_17) requires firmware for its PHY in order to work
at connection speeds > 5GBit. Add support for uploading firmware for
the PHY using the existing rtl8152_apply_firmware() function
in r8157_hw_phy_cfg() and set up the correct names for the firmware
files.
This also adds support for uploading firmware for the RTL8157
(RTL_VER_16) PHY, for which firmware is however not strictly necessary
to work. Still, this allows to upload newer versions of the firmware used
by this chip, e.g. to improve interoperability.
If no firmware is found, both the RTL8157 and the RTL8159 will continue
to work.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
Tested-by: Aleksander Jan Bajkowski <olek2@wp.pl>
---
drivers/net/usb/r8152.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index a2cdd57558c78b025ea61d33b99eca72c19f535f..7efba20de03deb06ac75c3b57a0d0e27b30b8e3a 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -4663,10 +4663,11 @@ static bool rtl8152_is_fw_phy_speed_up_ok(struct r8152 *tp, struct fw_phy_speed_
case RTL_VER_11:
case RTL_VER_12:
case RTL_VER_14:
- case RTL_VER_16:
goto out;
case RTL_VER_13:
case RTL_VER_15:
+ case RTL_VER_16:
+ case RTL_VER_17:
default:
break;
}
@@ -7982,12 +7983,14 @@ static void r8157_hw_phy_cfg(struct r8152 *tp)
data = r8153_phy_status(tp, 0);
switch (data) {
case PHY_STAT_EXT_INIT:
+ rtl8152_apply_firmware(tp, true);
ocp_reg_clr_bits(tp, 0xa466, BIT(0));
ocp_reg_clr_bits(tp, 0xa468, BIT(3) | BIT(1));
break;
case PHY_STAT_LAN_ON:
case PHY_STAT_PWRDN:
default:
+ rtl8152_apply_firmware(tp, false);
break;
}
@@ -9923,6 +9926,8 @@ static int rtl_ops_init(struct r8152 *tp)
#define FIRMWARE_8153C_1 "rtl_nic/rtl8153c-1.fw"
#define FIRMWARE_8156A_2 "rtl_nic/rtl8156a-2.fw"
#define FIRMWARE_8156B_2 "rtl_nic/rtl8156b-2.fw"
+#define FIRMWARE_8157_1 "rtl_nic/rtl8157-1.fw"
+#define FIRMWARE_8159_1 "rtl_nic/rtl8159-1.fw"
MODULE_FIRMWARE(FIRMWARE_8153A_2);
MODULE_FIRMWARE(FIRMWARE_8153A_3);
@@ -9931,6 +9936,8 @@ MODULE_FIRMWARE(FIRMWARE_8153B_2);
MODULE_FIRMWARE(FIRMWARE_8153C_1);
MODULE_FIRMWARE(FIRMWARE_8156A_2);
MODULE_FIRMWARE(FIRMWARE_8156B_2);
+MODULE_FIRMWARE(FIRMWARE_8157_1);
+MODULE_FIRMWARE(FIRMWARE_8159_1);
static int rtl_fw_init(struct r8152 *tp)
{
@@ -9969,6 +9976,12 @@ static int rtl_fw_init(struct r8152 *tp)
rtl_fw->pre_fw = r8153b_pre_firmware_1;
rtl_fw->post_fw = r8153c_post_firmware_1;
break;
+ case RTL_VER_16:
+ rtl_fw->fw_name = FIRMWARE_8157_1;
+ break;
+ case RTL_VER_17:
+ rtl_fw->fw_name = FIRMWARE_8159_1;
+ break;
default:
break;
}
--
2.47.3
^ permalink raw reply related
* [PATCH net-next v3 2/3] r8152: Add support for the RTL8159 chip
From: Birger Koblitz @ 2026-05-02 4:50 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: linux-usb, netdev, linux-kernel, Chih Kai Hsu, Birger Koblitz,
Aleksander Jan Bajkowski
In-Reply-To: <20260502-rtl8159_net_next-v3-0-12c895d46cd6@birger-koblitz.de>
The RTL8159 re-uses the packet descriptor format introduced with the
RTL8157 and other hardware features of the RTL8157 (RTL_VER_16) such
as the SRAM access. The support therefore consists in expanding the
existing RTL8157 code for initialization and USB power management
to also be used for the RTL8159 (RTL_VER_17).
Most of the additional code is added in r8157_hw_phy_cfg() to configure
the RTL8159 PHY.
Add support for the USB device ID of Realtek RTL8159-based adapters,
for which the product ID is 0x815a. Detect the RTL8159 as RTL_VER_17
and set it up.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
Tested-by: Aleksander Jan Bajkowski <olek2@wp.pl>
---
drivers/net/usb/r8152.c | 272 +++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 257 insertions(+), 15 deletions(-)
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index 05abfab1df94a571347196bfce7c232865e1058e..a2cdd57558c78b025ea61d33b99eca72c19f535f 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -1247,6 +1247,7 @@ enum rtl_version {
RTL_VER_14,
RTL_VER_15,
RTL_VER_16,
+ RTL_VER_17,
RTL_VER_MAX
};
@@ -3432,6 +3433,7 @@ static void rtl8152_nic_reset(struct r8152 *tp)
break;
case RTL_VER_16:
+ case RTL_VER_17:
ocp_byte_clr_bits(tp, MCU_TYPE_PLA, PLA_CR, CR_RE | CR_TE);
break;
@@ -3471,6 +3473,9 @@ static void rtl_eee_plus_en(struct r8152 *tp, bool enable)
static void rtl_set_eee_plus(struct r8152 *tp)
{
+ if (tp->version == RTL_VER_17)
+ return rtl_eee_plus_en(tp, false);
+
if (rtl8152_get_speed(tp) & _10bps)
rtl_eee_plus_en(tp, true);
else
@@ -3656,6 +3661,7 @@ static void r8153_set_rx_early_timeout(struct r8152 *tp)
case RTL_VER_13:
case RTL_VER_15:
case RTL_VER_16:
+ case RTL_VER_17:
ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT,
640 / 8);
ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EXTRA_AGGR_TMR,
@@ -3700,6 +3706,7 @@ static void r8153_set_rx_early_size(struct r8152 *tp)
ocp_data / 8);
break;
case RTL_VER_16:
+ case RTL_VER_17:
ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE,
ocp_data / 16);
break;
@@ -4548,6 +4555,7 @@ static void rtl_clear_bp(struct r8152 *tp, u16 type)
break;
case RTL_VER_14:
case RTL_VER_16:
+ case RTL_VER_17:
default:
ocp_write_word(tp, type, USB_BP2_EN, 0);
bp_num = 16;
@@ -5818,6 +5826,7 @@ static void rtl_eee_enable(struct r8152 *tp, bool enable)
case RTL_VER_13:
case RTL_VER_15:
case RTL_VER_16:
+ case RTL_VER_17:
if (enable) {
r8156_eee_en(tp, true);
ocp_reg_write(tp, OCP_EEE_ADV, tp->eee_adv);
@@ -6408,7 +6417,7 @@ static int rtl8156_enable(struct r8152 *tp)
set_tx_qlen(tp);
rtl_set_eee_plus(tp);
- if (tp->version >= RTL_VER_12 && tp->version <= RTL_VER_16)
+ if (tp->version >= RTL_VER_12 && tp->version <= RTL_VER_17)
ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_RX_AGGR_NUM, RX_AGGR_NUM_MASK);
r8153_set_rx_early_timeout(tp);
@@ -6817,7 +6826,7 @@ static void rtl8156_up(struct r8152 *tp)
return;
r8153b_u1u2en(tp, false);
- if (tp->version != RTL_VER_16)
+ if (tp->version < RTL_VER_16)
r8153_u2p3en(tp, false);
r8153_aldps_en(tp, false);
@@ -6831,7 +6840,7 @@ static void rtl8156_up(struct r8152 *tp)
ocp_byte_clr_bits(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, NOW_IS_OOB);
- if (tp->version == RTL_VER_16)
+ if (tp->version >= RTL_VER_16)
ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_RCR1, BIT(3));
ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, MCU_BORW_EN);
@@ -6856,7 +6865,7 @@ static void rtl8156_up(struct r8152 *tp)
ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, PLA_MCU_SPDWN_EN);
- if (tp->version != RTL_VER_16)
+ if (tp->version < RTL_VER_16)
ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_SPEED_OPTION,
RG_PWRDN_EN | ALL_SPEED_OFF);
@@ -6868,10 +6877,10 @@ static void rtl8156_up(struct r8152 *tp)
}
r8153_aldps_en(tp, true);
- if (tp->version != RTL_VER_16)
+ if (tp->version < RTL_VER_16)
r8153_u2p3en(tp, true);
- if (tp->version != RTL_VER_16 && tp->udev->speed >= USB_SPEED_SUPER)
+ if (tp->version < RTL_VER_16 && tp->udev->speed >= USB_SPEED_SUPER)
r8153b_u1u2en(tp, true);
}
@@ -6886,7 +6895,7 @@ static void rtl8156_down(struct r8152 *tp)
PLA_MCU_SPDWN_EN);
r8153b_u1u2en(tp, false);
- if (tp->version != RTL_VER_16) {
+ if (tp->version < RTL_VER_16) {
r8153_u2p3en(tp, false);
r8153b_power_cut_en(tp, false);
}
@@ -7996,7 +8005,7 @@ static void r8157_hw_phy_cfg(struct r8152 *tp)
/* Advanced Power Saving parameter */
ocp_reg_set_bits(tp, 0xa430, BIT(0) | BIT(1));
- /* aldpsce force mode */
+ /* Disable ALDPS force mode */
ocp_reg_clr_bits(tp, 0xa44a, BIT(2));
switch (tp->version) {
@@ -8120,6 +8129,190 @@ static void r8157_hw_phy_cfg(struct r8152 *tp)
sram2_write_w0w1(tp, 0x807c, 0xff00, 0x5000);
sram2_write_w0w1(tp, 0x809d, 0xff00, 0x5000);
break;
+
+ case RTL_VER_17:
+ /* Disable bypass turn off clk in ALDPS */
+ ocp_byte_clr_bits(tp, MCU_TYPE_PLA, 0xd3c8, BIT(0));
+
+ /* Power level tuning
+ * test mode power level
+ */
+ sram_write_w0w1(tp, 0x8415, 0xff00, 0x9300);
+ /* normal link power level 10G, 5G, 2.5G */
+ sram_write_w0w1(tp, 0x81a3, 0xff00, 0x0f00);
+ sram_write_w0w1(tp, 0x81ae, 0xff00, 0x0f00);
+ sram_write_w0w1(tp, 0x81b9, 0xff00, 0xb900);
+ /* nomal link TX filter */
+ sram2_write_w0w1(tp, 0x83b0, 0x0e00, 0);
+ sram2_write_w0w1(tp, 0x83c5, 0x0e00, 0);
+ sram2_write_w0w1(tp, 0x83da, 0x0e00, 0);
+ sram2_write_w0w1(tp, 0x83ef, 0x0e00, 0);
+
+ /* AFE power saving for 2.5G & 5G */
+ sram_write(tp, 0x8173, 0x8620);
+ sram_write(tp, 0x8175, 0x8671);
+
+ sram_write_w0w1(tp, 0x817c, 0, BIT(13));
+ sram_write_w0w1(tp, 0x8187, 0, BIT(13));
+ sram_write_w0w1(tp, 0x8192, 0, BIT(13));
+ sram_write_w0w1(tp, 0x819d, 0, BIT(13));
+ sram_write_w0w1(tp, 0x81a8, BIT(13), 0);
+ sram_write_w0w1(tp, 0x81b3, BIT(13), 0);
+ sram_write_w0w1(tp, 0x81be, 0, BIT(13));
+
+ sram_write_w0w1(tp, 0x817d, 0xff00, 0xa600);
+ sram_write_w0w1(tp, 0x8188, 0xff00, 0xa600);
+ sram_write_w0w1(tp, 0x8193, 0xff00, 0xa600);
+ sram_write_w0w1(tp, 0x819e, 0xff00, 0xa600);
+ sram_write_w0w1(tp, 0x81a9, 0xff00, 0x1400);
+ sram_write_w0w1(tp, 0x81b4, 0xff00, 0x1400);
+ sram_write_w0w1(tp, 0x81bf, 0xff00, 0xa600);
+
+ /* RFI parameter
+ * disable preset FBE
+ */
+ ocp_reg_clr_bits(tp, 0xaeaa, BIT(5) | BIT(3));
+ /* modify PGA for 5G&10G */
+ sram2_write(tp, 0x84f0, 0x201c);
+ sram2_write(tp, 0x84f2, 0x3117);
+ /* RFI parameter */
+ ocp_reg_write(tp, 0xaec6, 0x0000);
+ ocp_reg_write(tp, 0xae20, 0xffff);
+ ocp_reg_write(tp, 0xaece, 0xffff);
+ ocp_reg_write(tp, 0xaed2, 0xffff);
+ ocp_reg_write(tp, 0xaec8, 0x0000);
+ ocp_reg_clr_bits(tp, 0xaed0, BIT(0));
+ ocp_reg_write(tp, 0xadb8, 0x0150);
+ sram2_write_w0w1(tp, 0x8197, 0xff00, 0x5000);
+ sram2_write_w0w1(tp, 0x8231, 0xff00, 0x5000);
+ sram2_write_w0w1(tp, 0x82cb, 0xff00, 0x5000);
+ sram2_write_w0w1(tp, 0x82cd, 0xff00, 0x5700);
+ sram2_write_w0w1(tp, 0x8233, 0xff00, 0x5700);
+ sram2_write_w0w1(tp, 0x8199, 0xff00, 0x5700);
+
+ sram2_write(tp, 0x815a, 0x0150);
+ sram2_write(tp, 0x81f4, 0x0150);
+ sram2_write(tp, 0x828e, 0x0150);
+ sram2_write(tp, 0x81b1, 0x0000);
+ sram2_write(tp, 0x824b, 0x0000);
+ sram2_write(tp, 0x82e5, 0x0000);
+
+ sram2_write_w0w1(tp, 0x84f7, 0xff00, 0x2800);
+ ocp_reg_set_bits(tp, 0xaec2, BIT(12));
+ sram2_write_w0w1(tp, 0x81b3, 0xff00, 0xad00);
+ sram2_write_w0w1(tp, 0x824d, 0xff00, 0xad00);
+ sram2_write_w0w1(tp, 0x82e7, 0xff00, 0xad00);
+ ocp_reg_w0w1(tp, 0xae4e, 0x000f, 0x0001);
+ sram2_write_w0w1(tp, 0x82ce, 0xf000, 0x4000);
+
+ /* 5G shift sel, default = '04'
+ * 10G shift sel, default = '03'
+ */
+ sram2_write_w0w1(tp, 0x83a5, 0xff00, 0x0400);
+ sram2_write_w0w1(tp, 0x83a6, 0xff00, 0x0400);
+ sram2_write_w0w1(tp, 0x83a7, 0xff00, 0x0400);
+ sram2_write_w0w1(tp, 0x83a8, 0xff00, 0x0400);
+
+ /* XG INRX parameters
+ * RC coefficients
+ */
+ sram2_write(tp, 0x84ac, 0x0000);
+ sram2_write(tp, 0x84ae, 0x0000);
+ sram2_write(tp, 0x84b0, 0xf818);
+ sram2_write_w0w1(tp, 0x84b2, 0xff00, 0x6000);
+ /* Training AAGC PAR (with uc2 patch) */
+ sram2_write(tp, 0x8ffc, 0x6008);
+ sram2_write(tp, 0x8ffe, 0xf450);
+ /* DAC BGK */
+ sram2_write_w0w1(tp, 0x8015, 0, BIT(9));
+ sram2_write_w0w1(tp, 0x8016, 0, BIT(11));
+ sram2_write_w0w1(tp, 0x8fe6, 0xff00, 0x0800);
+ sram2_write(tp, 0x8fe4, 0x2114);
+ /* 10G PBO table */
+ sram2_write(tp, 0x8647, 0xa7b1);
+ sram2_write(tp, 0x8649, 0xbbca);
+ sram2_write_w0w1(tp, 0x864b, 0xff00, 0xdc00);
+ /* 2.5G ado power window size */
+ sram2_write_w0w1(tp, 0x8154, 0xc000, 0x4000);
+ sram2_write_w0w1(tp, 0x8158, 0xc000, 0);
+ /* 10G lock far */
+ sram2_write(tp, 0x826c, 0xffff);
+ sram2_write(tp, 0x826e, 0xffff);
+ /* XG INRX parameter */
+ sram2_write_w0w1(tp, 0x8872, 0xff00, 0x0e00);
+ sram_write_w0w1(tp, 0x8012, 0, BIT(11));
+ sram_write_w0w1(tp, 0x8012, 0, BIT(14));
+ ocp_reg_set_bits(tp, 0xb576, BIT(0));
+ sram_write_w0w1(tp, 0x834a, 0xff00, 0x0700);
+ sram2_write_w0w1(tp, 0x8217, 0x3f00, 0x2a00);
+ sram_write_w0w1(tp, 0x81b1, 0xff00, 0x0b00);
+ sram2_write_w0w1(tp, 0x8fed, 0xff00, 0x4e00);
+ /* Slave about EC mu of datamode AAGC and DAC BG */
+ sram2_write_w0w1(tp, 0x88ac, 0xff00, 0x2300);
+ /* improve UBE */
+ ocp_reg_set_bits(tp, 0xbf0c, 0x7 << 11);
+ /* close Sparse NEC, improve connect 5EUU calble performace */
+ sram2_write_w0w1(tp, 0x88de, 0xff00, 0);
+ /* 5G slave compatibility issue (will include in v10) */
+ sram2_write(tp, 0x80b4, 0x5195);
+
+ /* XG Test Mode
+ * xgtstm_map_tbl for mdi_cap_sel
+ */
+ sram_write(tp, 0x8370, 0x8671);
+ sram_write(tp, 0x8372, 0x86c8);
+ /* xgtstm_amp_map_tbl for REG_IBX_UP_SHIFT_L */
+ sram_write(tp, 0x8401, 0x86c8);
+ sram_write(tp, 0x8403, 0x86da);
+ sram_write_w0w1(tp, 0x8406, 0x1800, 0x1000);
+ sram_write_w0w1(tp, 0x8408, 0x1800, 0x1000);
+ sram_write_w0w1(tp, 0x840a, 0x1800, 0x1000);
+ sram_write_w0w1(tp, 0x840c, 0x1800, 0x1000);
+ sram_write_w0w1(tp, 0x840e, 0x1800, 0x1000);
+ sram_write_w0w1(tp, 0x8410, 0x1800, 0x1000);
+ sram_write_w0w1(tp, 0x8412, 0x1800, 0x1000);
+ sram_write_w0w1(tp, 0x8414, 0x1800, 0x1000);
+ sram_write_w0w1(tp, 0x8416, 0x1800, 0x1000);
+
+ /* Cable Test Patch */
+ sram_write(tp, 0x82bd, 0x1f40);
+
+ /* Thermal sensor parameters */
+ ocp_reg_w0w1(tp, 0xbfb4, 0x07ff, 0x0328);
+ ocp_reg_write(tp, 0xbfb6, 0x3e14);
+
+ /* spdchg_gtx_shape_100M */
+ ocp_reg_write(tp, OCP_SRAM_ADDR, 0x81c4);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x003b);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x0086);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x00b7);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x00db);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x00fe);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x00fe);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x00fe);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x00fe);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x00c3);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x0078);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x0047);
+ ocp_reg_write(tp, OCP_SRAM_DATA, 0x0023);
+
+ /* lsbmsk_parameters
+ * RL6961_lsbmsk_parameter_250207
+ */
+ sram2_write(tp, 0x88d7, 0x01a0);
+ sram2_write(tp, 0x88d9, 0x01a0);
+ sram2_write(tp, 0x8ffa, 0x002a);
+
+ sram2_write(tp, 0x8fee, 0xffdf);
+ sram2_write(tp, 0x8ff0, 0xffff);
+ sram2_write(tp, 0x8ff2, 0x0a4a);
+ sram2_write(tp, 0x8ff4, 0xaa5a);
+ sram2_write(tp, 0x8ff6, 0x0a4a);
+ sram2_write(tp, 0x8ff8, 0xaa5a);
+
+ sram2_write_w0w1(tp, 0x88d5, 0xff00, 0x0200);
+ break;
+
default:
break;
}
@@ -8155,6 +8348,18 @@ static void r8157_hw_phy_cfg(struct r8152 *tp)
set_bit(PHY_RESET, &tp->flags);
}
+static int r8159_wait_backup_restore(struct r8152 *tp)
+{
+ u32 ocp_data;
+
+ ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
+ if (!(ocp_data & PCUT_STATUS))
+ return 0;
+
+ return poll_timeout_us(ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_GPHY_CTRL),
+ ocp_data & BACKUP_RESTRORE, 200, 20000, false);
+}
+
static void r8156_init(struct r8152 *tp)
{
u32 ocp_data;
@@ -8164,14 +8369,14 @@ static void r8156_init(struct r8152 *tp)
if (test_bit(RTL8152_INACCESSIBLE, &tp->flags))
return;
- if (tp->version == RTL_VER_16) {
+ if (tp->version == RTL_VER_16 || tp->version == RTL_VER_17) {
ocp_byte_set_bits(tp, MCU_TYPE_USB, 0xcffe, BIT(3));
ocp_byte_clr_bits(tp, MCU_TYPE_USB, 0xd3ca, BIT(0));
}
ocp_byte_clr_bits(tp, MCU_TYPE_USB, USB_ECM_OP, EN_ALL_SPEED);
- if (tp->version != RTL_VER_16)
+ if (tp->version < RTL_VER_16)
ocp_write_word(tp, MCU_TYPE_USB, USB_SPEED_OPTION, 0);
ocp_word_set_bits(tp, MCU_TYPE_USB, USB_ECM_OPTION, BYPASS_MAC_RESET);
@@ -8185,6 +8390,7 @@ static void r8156_init(struct r8152 *tp)
case RTL_VER_13:
case RTL_VER_15:
case RTL_VER_16:
+ case RTL_VER_17:
r8156b_wait_loading_flash(tp);
break;
default:
@@ -8201,6 +8407,11 @@ static void r8156_init(struct r8152 *tp)
return;
}
+ if (tp->version == RTL_VER_17 && r8159_wait_backup_restore(tp)) {
+ dev_err(&tp->intf->dev, "init failed, backup-restore timed out\n");
+ return;
+ }
+
data = r8153_phy_status(tp, 0);
if (data == PHY_STAT_EXT_INIT) {
ocp_reg_clr_bits(tp, 0xa468, BIT(3) | BIT(1));
@@ -8216,7 +8427,7 @@ static void r8156_init(struct r8152 *tp)
data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
- if (tp->version == RTL_VER_16)
+ if (tp->version >= RTL_VER_16)
r8157_u2p3en(tp, false);
else
r8153_u2p3en(tp, false);
@@ -8227,7 +8438,7 @@ static void r8156_init(struct r8152 *tp)
/* U1/U2/L1 idle timer. 500 us */
ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500);
- if (tp->version == RTL_VER_16)
+ if (tp->version >= RTL_VER_16)
r8157_power_cut_en(tp, false);
else
r8153b_power_cut_en(tp, false);
@@ -8260,7 +8471,7 @@ static void r8156_init(struct r8152 *tp)
r8156_mac_clk_spd(tp, true);
- if (tp->version != RTL_VER_16)
+ if (tp->version < RTL_VER_16)
ocp_word_clr_bits(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, PLA_MCU_SPDWN_EN);
ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS);
@@ -8274,7 +8485,10 @@ static void r8156_init(struct r8152 *tp)
set_bit(GREEN_ETHERNET, &tp->flags);
/* rx aggregation / 16 bytes Rx descriptor */
- if (tp->version == RTL_VER_16)
+ if (tp->version == RTL_VER_17)
+ ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL,
+ RX_AGG_DISABLE | RX_DESC_16B | BIT(11));
+ else if (tp->version == RTL_VER_16)
ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL, RX_AGG_DISABLE | RX_DESC_16B);
else
ocp_word_clr_bits(tp, MCU_TYPE_USB, USB_USB_CTRL, RX_AGG_DISABLE | RX_ZERO_EN);
@@ -8282,7 +8496,7 @@ static void r8156_init(struct r8152 *tp)
if (tp->version < RTL_VER_12)
ocp_byte_set_bits(tp, MCU_TYPE_USB, USB_BMU_CONFIG, ACT_ODMA);
- if (tp->version == RTL_VER_16) {
+ if (tp->version >= RTL_VER_16) {
/* Disable Rx Zero Len */
rtl_bmu_clr_bits(tp, 0x2300, BIT(3));
/* TX descriptor Signature */
@@ -9670,6 +9884,29 @@ static int rtl_ops_init(struct r8152 *tp)
r8157_desc_init(tp);
break;
+ case RTL_VER_17:
+ tp->eee_en = true;
+ tp->eee_adv = MDIO_EEE_100TX | MDIO_EEE_1000T | MDIO_EEE_10GT;
+ tp->eee_adv2 = MDIO_EEE_2_5GT | MDIO_EEE_5GT;
+ ops->init = r8156_init;
+ ops->enable = rtl8156_enable;
+ ops->disable = rtl8153_disable;
+ ops->up = rtl8156_up;
+ ops->down = rtl8156_down;
+ ops->unload = rtl8153_unload;
+ ops->eee_get = r8153_get_eee;
+ ops->eee_set = r8152_set_eee;
+ ops->in_nway = rtl8153_in_nway;
+ ops->hw_phy_cfg = r8157_hw_phy_cfg;
+ ops->autosuspend_en = rtl8157_runtime_enable;
+ ops->change_mtu = rtl8156_change_mtu;
+ tp->rx_buf_sz = 48 * 1024;
+ tp->support_2500full = 1;
+ tp->support_5000full = 1;
+ tp->support_10000full = 1;
+ r8157_desc_init(tp);
+ break;
+
default:
ret = -ENODEV;
dev_err(&tp->intf->dev, "Unknown Device\n");
@@ -9823,6 +10060,9 @@ static u8 __rtl_get_hw_ver(struct usb_device *udev)
case 0x1030:
version = RTL_VER_16;
break;
+ case 0x2020:
+ version = RTL_VER_17;
+ break;
default:
version = RTL_VER_UNKNOWN;
dev_info(&udev->dev, "Unknown version 0x%04x\n", ocp_data);
@@ -9975,6 +10215,7 @@ static int rtl8152_probe_once(struct usb_interface *intf,
case RTL_VER_13:
case RTL_VER_15:
case RTL_VER_16:
+ case RTL_VER_17:
netdev->max_mtu = size_to_mtu(16 * 1024);
break;
case RTL_VER_01:
@@ -10140,6 +10381,7 @@ static const struct usb_device_id rtl8152_table[] = {
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x8155) },
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x8156) },
{ USB_DEVICE(VENDOR_ID_REALTEK, 0x8157) },
+ { USB_DEVICE(VENDOR_ID_REALTEK, 0x815a) },
/* Microsoft */
{ USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07ab) },
--
2.47.3
^ permalink raw reply related
* [PATCH net v5] ipv6: flowlabel: enforce per-netns limit for unprivileged callers
From: Maoyi Xie @ 2026-05-02 5:00 UTC (permalink / raw)
To: netdev
Cc: willemdebruijn.kernel, willemb, edumazet, pabeni, kuba, davem,
dsahern, kuznet, linux-kernel, stable
fl_size, fl_ht and ip6_fl_lock in net/ipv6/ip6_flowlabel.c are file
scope and shared across netns. mem_check() reads fl_size to decide
whether to deny non-CAP_NET_ADMIN callers; capable() runs against
init_user_ns, so an unprivileged user in any non-init userns can
push fl_size past FL_MAX_SIZE - FL_MAX_SIZE/4 and starve every
other unprivileged userns on the host.
Add struct netns_ipv6::flowlabel_count, bumped and decremented next
to fl_size in fl_intern, ip6_fl_gc and ip6_fl_purge. The new field
is placed in the existing 4-byte hole after ipmr_seq, so struct
netns_ipv6 stays the same size on 64-bit builds.
Bump FL_MAX_SIZE from 4096 to 8192. It has been 4096 since the file
was added; machines and connection counts have grown.
mem_check() folds an extra per-netns ceiling into the existing
non-CAP_NET_ADMIN conditional. The ceiling is half of the total
budget that unprivileged callers have ever been able to use, i.e.
(FL_MAX_SIZE - FL_MAX_SIZE/4) / 2 = 3072 entries. With FL_MAX_SIZE
doubled, this preserves the original per-user reach (~3K, what an
unprivileged caller could already obtain before this change) while
forcing an attacker to spread allocations across at least two
netns to exhaust the global non-CAP_NET_ADMIN budget.
CAP_NET_ADMIN against init_user_ns still bypasses both caps.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Suggested-by: Willem de Bruijn <willemb@google.com>
Cc: stable@vger.kernel.org # v5.15+
Signed-off-by: Maoyi Xie <maoyi.xie@ntu.edu.sg>
---
v5 (this submission, addressing v4 review by Willem):
- Replaced the per-netns ceiling FL_MAX_SIZE/8 with the
computed unpriv_user_limit = (FL_MAX_SIZE - FL_MAX_SIZE/4)/2,
which evaluates to 3072. v4's FL_MAX_SIZE/8 = 1024 would have
reduced the per-user budget below the ~3K an unprivileged
caller could already obtain before any of this work, defeating
the reason FL_MAX_SIZE was doubled in the first place. The new
ceiling preserves the original per-user reach while still
requiring an attacker to spread across at least two netns to
drain the global non-CAP_NET_ADMIN budget.
- Reworded the corresponding paragraph in the commit body.
v4: addressed Willem's v3 review on netdev. Dropped the
flowlabel_has_excl cacheline argument in favour of "fills the
existing 4-byte hole after ipmr_seq", and reordered
atomic_dec(&...flowlabel_count) to sit immediately after
atomic_dec(&fl_size) in ip6_fl_gc and ip6_fl_purge.
v3: addressed Willem's review on the private security@ thread.
Merged FL_MAX_SIZE doubling, dropped test data, moved
flowlabel_count near ipmr_seq, inlined fl->fl_net in ip6_fl_gc.
v2: per-netns counter + cap, sent to security@ as a 2-patch series.
v1: fix-shape sketch in original disclosure.
include/net/netns/ipv6.h | 1 +
net/ipv6/ip6_flowlabel.c | 16 ++++++++++++----
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h
index 34bdb1308..329482373 100644
--- a/include/net/netns/ipv6.h
+++ b/include/net/netns/ipv6.h
@@ -119,6 +119,7 @@ struct netns_ipv6 {
struct fib_notifier_ops *notifier_ops;
struct fib_notifier_ops *ip6mr_notifier_ops;
unsigned int ipmr_seq; /* protected by rtnl_mutex */
+ atomic_t flowlabel_count;
struct {
struct hlist_head head;
spinlock_t lock;
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index c92f98c6f..758a2fc4d 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -36,7 +36,7 @@
/* FL hash table */
#define FL_MAX_PER_SOCK 32
-#define FL_MAX_SIZE 4096
+#define FL_MAX_SIZE 8192
#define FL_HASH_MASK 255
#define FL_HASH(l) (ntohl(l)&FL_HASH_MASK)
@@ -162,8 +162,9 @@ static void ip6_fl_gc(struct timer_list *unused)
ttd = fl->expires;
if (time_after_eq(now, ttd)) {
*flp = fl->next;
- fl_free(fl);
atomic_dec(&fl_size);
+ atomic_dec(&fl->fl_net->ipv6.flowlabel_count);
+ fl_free(fl);
continue;
}
if (!sched || time_before(ttd, sched))
@@ -195,8 +196,9 @@ static void __net_exit ip6_fl_purge(struct net *net)
if (net_eq(fl->fl_net, net) &&
atomic_read(&fl->users) == 0) {
*flp = fl->next;
- fl_free(fl);
atomic_dec(&fl_size);
+ atomic_dec(&net->ipv6.flowlabel_count);
+ fl_free(fl);
continue;
}
flp = &fl->next;
@@ -245,6 +247,7 @@ static struct ip6_flowlabel *fl_intern(struct net *net,
fl->next = fl_ht[FL_HASH(fl->label)];
rcu_assign_pointer(fl_ht[FL_HASH(fl->label)], fl);
atomic_inc(&fl_size);
+ atomic_inc(&net->ipv6.flowlabel_count);
spin_unlock_bh(&ip6_fl_lock);
rcu_read_unlock();
return NULL;
@@ -464,6 +467,9 @@ fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq,
static int mem_check(struct sock *sk)
{
+ const int unpriv_total_limit = FL_MAX_SIZE - (FL_MAX_SIZE / 4);
+ const int unpriv_user_limit = unpriv_total_limit / 2;
+ struct net *net = sock_net(sk);
int room = FL_MAX_SIZE - atomic_read(&fl_size);
struct ipv6_fl_socklist *sfl;
int count = 0;
@@ -478,7 +484,9 @@ static int mem_check(struct sock *sk)
if (room <= 0 ||
((count >= FL_MAX_PER_SOCK ||
- (count > 0 && room < FL_MAX_SIZE/2) || room < FL_MAX_SIZE/4) &&
+ (count > 0 && room < FL_MAX_SIZE/2) ||
+ room < FL_MAX_SIZE/4 ||
+ atomic_read(&net->ipv6.flowlabel_count) >= unpriv_user_limit) &&
!capable(CAP_NET_ADMIN)))
return -ENOBUFS;
--
2.34.1
^ permalink raw reply related
* [PATCH v2] net: lan966x: avoid unregistering netdev on register failure
From: Myeonghun Pak @ 2026-05-02 5:07 UTC (permalink / raw)
To: Horatiu Vultur, UNGLinuxDriver, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Ijae Kim
Cc: Myeonghun Pak, netdev, linux-kernel
lan966x_probe_port() stores the newly allocated net_device in the
port before calling register_netdev(). If register_netdev() fails,
the probe error path calls lan966x_cleanup_ports(), which sees
port->dev and calls unregister_netdev() for a device that was never
registered.
Destroy the phylink instance created for this port and clear port->dev
before returning the registration error, matching the existing guard
used by the common cleanup path. Also require port->dev to be non-NULL
before matching it against the FDMA netdev, so the error path cannot treat
an uninitialized FDMA netdev and a failed port as a valid match.
Fixes: d28d6d2e37d1 ("net: lan966x: add port module support")
Co-developed-by: Ijae Kim <ae878000@gmail.com>
Signed-off-by: Ijae Kim <ae878000@gmail.com>
Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
---
Changes in v2:
- Require port->dev to be non-NULL before calling the FDMA netdev cleanup
path, avoiding a NULL == NULL match when register_netdev() fails before
FDMA NAPI has been initialized.
drivers/net/ethernet/microchip/lan966x/lan966x_main.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
index 47752d3fde..22c496f588 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
@@ -756,7 +756,7 @@ static void lan966x_cleanup_ports(struct lan966x *lan966x)
unregister_netdev(port->dev);
lan966x_xdp_port_deinit(port);
- if (lan966x->fdma && lan966x->fdma_ndev == port->dev)
+ if (lan966x->fdma && port->dev && lan966x->fdma_ndev == port->dev)
lan966x_fdma_netdev_deinit(lan966x, port->dev);
if (port->phylink) {
@@ -873,6 +873,9 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p,
err = register_netdev(dev);
if (err) {
dev_err(lan966x->dev, "register_netdev failed\n");
+ phylink_destroy(phylink);
+ port->phylink = NULL;
+ port->dev = NULL;
return err;
}
^ permalink raw reply related
* Re: [PATCH net-next v2 6/7] selftests: rds: Collect pcaps on timeout
From: Allison Henderson @ 2026-05-02 5:41 UTC (permalink / raw)
To: Jakub Kicinski
Cc: netdev, pabeni, edumazet, horms, linux-rdma, linux-kselftest,
shuah
In-Reply-To: <20260430024207.2452372-1-kuba@kernel.org>
On Wed, 2026-04-29 at 19:42 -0700, Jakub Kicinski wrote:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> ---
> selftests: rds: Collect pcaps on timeout
>
> This commit updates the RDS selftest to collect network packet captures
> when a timeout occurs. It moves the packet capture cleanup logic into a
> new helper function and calls it from the signal handler before exiting.
>
> > diff --git a/tools/testing/selftests/net/rds/test.py b/tools/testing/selftests/net/rds/test.py
> > --- a/tools/testing/selftests/net/rds/test.py
> > +++ b/tools/testing/selftests/net/rds/test.py
> [ ... ]
> > @@ -70,11 +70,21 @@ def netns_socket(netns, *sock_args):
> > u1.close()
> > return socket.fromfd(fds[0], *sock_args)
> >
> > +def collect_pcaps():
> > + """Stop tcpdump processes and move their pcaps into the log dir."""
> > + print("Stopping network packet captures")
> > + for proc, tmp_path, dest_path, fno in tcpdump_procs:
> > + proc.terminate()
> > + proc.wait()
>
> Could this proc.wait() block indefinitely and defeat the test timeout?
>
> If a child process is unresponsive, the timeout signal handler will get
> stuck waiting for it. Using proc.wait(timeout=...) might ensure the
> timeout path remains robust and doesn't hang the test infrastructure.
Good catch, I will add a timeout
>
> > + os.close(fno)
> > + shutil.move(tmp_path, dest_path)
>
> Can this cause unhandled exceptions if the timeout triggers during or
> after the normal script cleanup phase?
>
> Because collect_pcaps() is called from both the normal execution path and
> the timeout signal handler, it might be executed twice. Since it loops
> over the global tcpdump_procs list without clearing processed items, a
> second run will attempt to close already closed file descriptors and move
> already moved files.
>
> In Python, this raises OSError (EBADF) and FileNotFoundError, which would
> abort the signal handler with a traceback instead of executing the clean
> sys.exit(1).
I see, I think if we change the for loop to a while loop that pop() items out of tcpdump_procs, that should make it safe
to call twice, if the timeout fires during or after normal cleanup.
If that sounds good, I can send that in a v3.
Thanks!
Allison
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox