* [PATCH net 3/5] rose: fix race between loopback timer and module removal
From: Bernard Pidoux @ 2026-04-26 14:43 UTC (permalink / raw)
To: netdev
Cc: linux-hams, linux-kernel, davem, edumazet, kuba, pabeni, horms,
Bernard Pidoux
In-Reply-To: <20260426144305.984349-1-bernard.f6bvp@gmail.com>
rose_loopback_clear() called timer_delete() which returns immediately
without waiting for any running callback to complete. If the timer
fired concurrently with module removal, rose_loopback_timer() could
re-arm the timer after timer_delete() returned and then access
rose_loopback_neigh after it was freed.
Two complementary changes close the race:
1. Add a loopback_stopping atomic flag. rose_loopback_timer() checks
it at entry (before acquiring a reference) and again inside the
loop; when set it drains the queue and exits without re-arming the
timer.
2. Switch rose_loopback_clear() to timer_delete_sync() so it blocks
until any in-flight callback has returned before freeing resources.
The smp_mb() between setting the flag and calling timer_delete_sync()
ensures the flag is visible to any callback that is about to run.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Tested-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
---
net/rose/rose_loopback.c | 31 ++++++++++++++++++++++++-------
1 file changed, 24 insertions(+), 7 deletions(-)
diff --git a/net/rose/rose_loopback.c b/net/rose/rose_loopback.c
index d66913df360d..80d7879ef36a 100644
--- a/net/rose/rose_loopback.c
+++ b/net/rose/rose_loopback.c
@@ -12,13 +12,15 @@
#include <net/rose.h>
#include <linux/init.h>
-static struct sk_buff_head loopback_queue;
#define ROSE_LOOPBACK_LIMIT 1000
-static struct timer_list loopback_timer;
+static struct timer_list loopback_timer;
+static struct sk_buff_head loopback_queue;
static void rose_set_loopback_timer(void);
static void rose_loopback_timer(struct timer_list *unused);
+static atomic_t loopback_stopping = ATOMIC_INIT(0);
+
void rose_loopback_init(void)
{
skb_queue_head_init(&loopback_queue);
@@ -66,6 +68,9 @@ static void rose_loopback_timer(struct timer_list *unused)
unsigned int lci_i, lci_o;
int count;
+ if (atomic_read(&loopback_stopping))
+ return;
+
if (rose_loopback_neigh)
rose_neigh_hold(rose_loopback_neigh);
else
@@ -75,6 +80,13 @@ static void rose_loopback_timer(struct timer_list *unused)
skb = skb_dequeue(&loopback_queue);
if (!skb)
goto out;
+
+ if (atomic_read(&loopback_stopping)) {
+ kfree_skb(skb);
+ skb_queue_purge(&loopback_queue);
+ goto out;
+ }
+
if (skb->len < ROSE_MIN_LEN) {
kfree_skb(skb);
continue;
@@ -118,7 +130,7 @@ static void rose_loopback_timer(struct timer_list *unused)
out:
rose_neigh_put(rose_loopback_neigh);
- if (!skb_queue_empty(&loopback_queue))
+ if (!atomic_read(&loopback_stopping) && !skb_queue_empty(&loopback_queue))
mod_timer(&loopback_timer, jiffies + 1);
}
@@ -126,10 +138,15 @@ void __exit rose_loopback_clear(void)
{
struct sk_buff *skb;
- timer_delete(&loopback_timer);
+ atomic_set(&loopback_stopping, 1);
+ /* Pairs with atomic_read() in rose_loopback_timer(): ensure the
+ * stopping flag is visible before we cancel, so a concurrent
+ * callback aborts its loop early rather than re-arming the timer.
+ */
+ smp_mb();
+
+ timer_delete_sync(&loopback_timer);
- while ((skb = skb_dequeue(&loopback_queue)) != NULL) {
- skb->sk = NULL;
+ while ((skb = skb_dequeue(&loopback_queue)) != NULL)
kfree_skb(skb);
- }
}
--
2.51.0
^ permalink raw reply related
* [PATCH net 4/5] rose: clear neighbour pointer after rose_neigh_put() in state machines
From: Bernard Pidoux @ 2026-04-26 14:43 UTC (permalink / raw)
To: netdev
Cc: linux-hams, linux-kernel, davem, edumazet, kuba, pabeni, horms,
Bernard Pidoux
In-Reply-To: <20260426144305.984349-1-bernard.f6bvp@gmail.com>
After calling rose_neigh_put() in rose_state1_machine() through
rose_state5_machine(), rose->neighbour was left pointing at the
potentially freed neighbour structure. A subsequent timer expiry or
concurrent teardown path could dereference the stale pointer, causing
a use-after-free.
Set rose->neighbour to NULL immediately after each rose_neigh_put()
call in the state machine functions.
Fixes: d860d1faa6b2 ("net: rose: convert 'use' field to refcount_t")
Tested-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
---
net/rose/rose_in.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c
index 0276b393f0e5..622527f1354f 100644
--- a/net/rose/rose_in.c
+++ b/net/rose/rose_in.c
@@ -57,6 +57,7 @@ static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int framety
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
rose_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]);
rose_neigh_put(rose->neighbour);
+ rose->neighbour = NULL;
break;
default:
@@ -80,11 +81,13 @@ static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int framety
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
rose_neigh_put(rose->neighbour);
+ rose->neighbour = NULL;
break;
case ROSE_CLEAR_CONFIRMATION:
rose_disconnect(sk, 0, -1, -1);
rose_neigh_put(rose->neighbour);
+ rose->neighbour = NULL;
break;
default:
@@ -122,6 +125,7 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
rose_neigh_put(rose->neighbour);
+ rose->neighbour = NULL;
break;
case ROSE_RR:
@@ -235,6 +239,7 @@ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int framety
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
rose_neigh_put(rose->neighbour);
+ rose->neighbour = NULL;
break;
default:
@@ -255,6 +260,7 @@ static int rose_state5_machine(struct sock *sk, struct sk_buff *skb, int framety
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
rose_neigh_put(rose_sk(sk)->neighbour);
+ rose_sk(sk)->neighbour = NULL;
}
return 0;
--
2.51.0
^ permalink raw reply related
* [PATCH net 5/5] rose: guard rose_neigh_put() against NULL in timer expiry
From: Bernard Pidoux @ 2026-04-26 14:43 UTC (permalink / raw)
To: netdev
Cc: linux-hams, linux-kernel, davem, edumazet, kuba, pabeni, horms,
Bernard Pidoux
In-Reply-To: <20260426144305.984349-1-bernard.f6bvp@gmail.com>
In rose_timer_expiry(), the ROSE_STATE_2 branch calls
rose_neigh_put(rose->neighbour) without first checking whether the
pointer is NULL. After commit 5de7665e0a07 ("net: rose: fix timer
races against user threads") the timer is re-armed when the socket is
owned by a user thread; between the re-arm and the next firing, a
device-down event or concurrent teardown via rose_kill_by_device() can
set rose->neighbour to NULL, leading to a NULL-pointer dereference
inside rose_neigh_put().
Add a NULL check before the put and clear the pointer afterwards.
Fixes: 5de7665e0a07 ("net: rose: fix timer races against user threads")
Tested-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
---
net/rose/rose_timer.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/net/rose/rose_timer.c b/net/rose/rose_timer.c
index bb60a1654d61..d997d24ab081 100644
--- a/net/rose/rose_timer.c
+++ b/net/rose/rose_timer.c
@@ -180,7 +180,10 @@ static void rose_timer_expiry(struct timer_list *t)
break;
case ROSE_STATE_2: /* T3 */
- rose_neigh_put(rose->neighbour);
+ if (rose->neighbour) {
+ rose_neigh_put(rose->neighbour);
+ rose->neighbour = NULL;
+ }
rose_disconnect(sk, ETIMEDOUT, -1, -1);
break;
--
2.51.0
^ permalink raw reply related
* [PATCH net v2 0/2] sctp: fix a vtag verification failure caused by stale INITs
From: Xin Long @ 2026-04-26 14:46 UTC (permalink / raw)
To: network dev, netfilter-devel, linux-sctp
Cc: davem, kuba, Eric Dumazet, Paolo Abeni, Simon Horman,
Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
Marcelo Ricardo Leitner, Yi Chen
Similar to Scenario B in commit 8e56b063c865 ( netfilter: handle the
connecting collision properly in nf_conntrack_proto_sctp"):
Scenario B: INIT_ACK is delayed until the peer completes its own handshake
192.168.1.2 > 192.168.1.1: sctp (1) [INIT] [init tag: 3922216408]
192.168.1.1 > 192.168.1.2: sctp (1) [INIT] [init tag: 144230885]
192.168.1.2 > 192.168.1.1: sctp (1) [INIT ACK] [init tag: 3922216408]
192.168.1.1 > 192.168.1.2: sctp (1) [COOKIE ECHO]
192.168.1.2 > 192.168.1.1: sctp (1) [COOKIE ACK]
192.168.1.1 > 192.168.1.2: sctp (1) [INIT ACK] [init tag: 3914796021] *
There is another case:
Scenario F: INIT is delayed until the peer completes its own handshake
192.168.1.2 > 192.168.1.1: sctp (1) [INIT] [init tag: 3922216408]
(OVS upcall)
192.168.1.1 > 192.168.1.2: sctp (1) [INIT] [init tag: 144230885]
192.168.1.2 > 192.168.1.1: sctp (1) [INIT ACK] [init tag: 3922216408]
192.168.1.1 > 192.168.1.2: sctp (1) [COOKIE ECHO]
192.168.1.2 > 192.168.1.1: sctp (1) [COOKIE ACK]
192.168.1.2 > 192.168.1.1: sctp (1) [INIT] [init tag: 3922216408]
(delayed)
192.168.1.1 > 192.168.1.2: sctp (1) [INIT ACK] [init tag: 3914796021] *
In this case, the delayed INIT (e.g. due to OVS upcall) is recorded by
conntrack, which prevents vtag verification from dropping the unexpected
INIT-ACK in nf_conntrack_sctp_packet():
vtag = ct->proto.sctp.vtag[!dir];
if (!ct->proto.sctp.init[!dir] && vtag && vtag != ih->init_tag)
goto out_unlock;
This happens because ct->proto.sctp.init[!dir] is set by the delayed INIT,
even though it is stale.
Fix this in two parts:
- In netfilter: Do not record INITs whose init_tag matches the peer vtag,
as they carry no new handshake state in the 1st patch.
- In SCTP: Prevent endpoints from responding to such INITs with INIT-ACK,
ensuring correctness even when middleboxes lack the netfilter fix in
the 2nd patch.
A follow-up selftest for this scenario will be posted in a separate patch
by Yi Chen.
Xin Long (2):
netfilter: skip recording stale or retransmitted INIT
sctp: discard stale INIT after handshake completion
net/netfilter/nf_conntrack_proto_sctp.c | 10 +++++++---
net/sctp/sm_statefuns.c | 6 ++++++
2 files changed, 13 insertions(+), 3 deletions(-)
--
2.47.1
^ permalink raw reply
* [PATCH net v2 1/2] netfilter: skip recording stale or retransmitted INIT
From: Xin Long @ 2026-04-26 14:46 UTC (permalink / raw)
To: network dev, netfilter-devel, linux-sctp
Cc: davem, kuba, Eric Dumazet, Paolo Abeni, Simon Horman,
Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
Marcelo Ricardo Leitner, Yi Chen
In-Reply-To: <cover.1777214801.git.lucien.xin@gmail.com>
An INIT whose init_tag matches the peer's vtag does not provide new state
information. It indicates either:
- a stale INIT (after INIT-ACK has already been seen on the same side), or
- a retransmitted INIT (after INIT has already been recorded on the same
side).
In both cases, the INIT must not update ct->proto.sctp.init[] state, since
it does not advance the handshake tracking and may otherwise corrupt
INIT/INIT-ACK validation logic.
Allow INIT processing only when the conntrack entry is newly created
(SCTP_CONNTRACK_NONE), or when the init_tag differs from the stored peer
vtag.
Note it skips the check for the ct with old_state SCTP_CONNTRACK_NONE in
nf_conntrack_sctp_packet(), as it is just created in sctp_new() where it
set ct->proto.sctp.vtag[IP_CT_DIR_REPLY] = ih->init_tag.
Fixes: 9fb9cbb1082d ("[NETFILTER]: Add nf_conntrack subsystem.")
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Reviewed-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Acked-by: Florian Westphal <fw@strlen.de>
---
net/netfilter/nf_conntrack_proto_sctp.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c
index 645d2c43ebf7..7e10fa65cbdd 100644
--- a/net/netfilter/nf_conntrack_proto_sctp.c
+++ b/net/netfilter/nf_conntrack_proto_sctp.c
@@ -466,9 +466,13 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
if (!ih)
goto out_unlock;
- if (ct->proto.sctp.init[dir] && ct->proto.sctp.init[!dir])
- ct->proto.sctp.init[!dir] = 0;
- ct->proto.sctp.init[dir] = 1;
+ /* Do not record INIT matching peer vtag (stale or retransmitted INIT). */
+ if (old_state == SCTP_CONNTRACK_NONE ||
+ ct->proto.sctp.vtag[!dir] != ih->init_tag) {
+ if (ct->proto.sctp.init[dir] && ct->proto.sctp.init[!dir])
+ ct->proto.sctp.init[!dir] = 0;
+ ct->proto.sctp.init[dir] = 1;
+ }
pr_debug("Setting vtag %x for dir %d\n", ih->init_tag, !dir);
ct->proto.sctp.vtag[!dir] = ih->init_tag;
--
2.47.1
^ permalink raw reply related
* [PATCH net v2 2/2] sctp: discard stale INIT after handshake completion
From: Xin Long @ 2026-04-26 14:46 UTC (permalink / raw)
To: network dev, netfilter-devel, linux-sctp
Cc: davem, kuba, Eric Dumazet, Paolo Abeni, Simon Horman,
Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
Marcelo Ricardo Leitner, Yi Chen
In-Reply-To: <cover.1777214801.git.lucien.xin@gmail.com>
After an association reaches ESTABLISHED, the peer’s init_tag is already
known from the handshake. Any subsequent INIT with the same init_tag is
not a valid restart, but a delayed or duplicate INIT.
Drop such INIT chunks in sctp_sf_do_unexpected_init() instead of
processing them as new association attempts.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
---
v2:
- Fix INIT tag comparison by converting the on-wire init_tag to host byte
order before comparing it with asoc->peer.i.init_tag.
---
net/sctp/sm_statefuns.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 7b823d759141..8e89a870780c 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -1556,6 +1556,12 @@ static enum sctp_disposition sctp_sf_do_unexpected_init(
/* Tag the variable length parameters. */
chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(struct sctp_inithdr));
+ if (asoc->state >= SCTP_STATE_ESTABLISHED) {
+ /* Discard INIT matching peer vtag after handshake completion (stale INIT). */
+ if (ntohl(chunk->subh.init_hdr->init_tag) == asoc->peer.i.init_tag)
+ return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+ }
+
/* Verify the INIT chunk before processing it. */
err_chunk = NULL;
if (!sctp_verify_init(net, ep, asoc, chunk->chunk_hdr->type,
--
2.47.1
^ permalink raw reply related
* Re: [PATCH net v2] ipv6: Implement limits on extension header parsing
From: Justin Iurman @ 2026-04-26 15:47 UTC (permalink / raw)
To: Ido Schimmel, Daniel Borkmann, tom
Cc: kuba, edumazet, dsahern, willemdebruijn.kernel, pabeni, netdev
In-Reply-To: <20260426131714.GA180947@shredder>
On 4/26/26 15:17, Ido Schimmel wrote:
> On Sun, Apr 26, 2026 at 12:38:31PM +0200, Daniel Borkmann wrote:
>> On 4/25/26 12:19 PM, Justin Iurman wrote:
>>> I've given it a lot of thought. I came to the conclusion that we
>>> should use a hard-coded value here as well (just like we did for
>>> 076b8cad77aa, with the same logic), not a sysctl. IMO, the main
>>> reason is that it provides as is a suitable security fix to be
>>> backported, i.e., the max value is the max number of EHs allowed by
>>> RFC 8200, Section 4.1. Also, we remain consistent with
>>> draft-iurman-6man-eh-occurrences (I think Tom is about to send a
>>> revision of the series soon for net-next). What this series does is
>>> not only enforcing ordering, but also verifying the specific number
>>> of occurrences for each type of Extension Header. Which is totally
>>> compatible with what this patch does, i.e., limiting the total
>>> number of Extension Headers (regardless of their types) to 8. I
>>> guess what I'm trying to say is that it seems like a good
>>> plan/compromise and that the aforementioned series would build
>>> perfectly on top of this fix.
>>
>> Initially, I had a hard-coded constant (when it was still 32), but Eric's comment
>> was to rather go with a sysctl, such that if someone unexpectedly complains, then
>> there is still a chance for that person to fix it up via sysctl without having to
>> rebuild the kernel. I'm okay either way, but presumably given we're now being more
>> "aggressive" into lowering the default to 8 rather than 32 then having such a fall-
>> back is probably better.
>
> I also think that 32 without a sysctl knob is fine (just so that we have
> some upper bound), but if we go with a sysctl then let's make sure that
> it's compatible with Tom's series [1] (I assume he is going to send a
> new version).
>
> AFAICT it's possible to create conflicting configuration with both
> sysctls (e.g., "enforce_ext_hdr_order" is set to 1 and
> "max_ext_hdrs_number" configured to less than 8). The documentation
> should make the relation between both sysctls clear to users. It can
> also mention that "max_ext_hdrs_number" might be useful when users are
> forced to turn "enforce_ext_hdr_order" off when dealing with hosts that
> send extension headers in an unexpected order. That way, they still have
> an upper bound on the maximum number of extension headers.
>
> [1] https://lore.kernel.org/netdev/20260314175124.47010-1-tom@herbertland.com/
Ido, Daniel,
As I said, my vote would definitely go to the solution without a sysctl
for the very reason Ido mentioned. Note that an upper bound of 32 is
kind of unrealistic, although super (SUPER!) safe. Sending more than 8
Extension Headers (assuming a different type for each) is not standard
behavior and would make you non-RFC-compliant anyway. But I'm happy with
32, as long as we don't define a sysctl for that.
^ permalink raw reply
* Re: [PATCH net] vrf: Fix a potential NPD when removing a port from a VRF
From: David Ahern @ 2026-04-26 16:32 UTC (permalink / raw)
To: Ido Schimmel, netdev
Cc: davem, kuba, pabeni, edumazet, jiri, andrew+netdev, royenheart,
yifanwucs, tomapufckgml, yuantan098
In-Reply-To: <20260423063607.1208202-1-idosch@nvidia.com>
On 4/23/26 12:36 AM, Ido Schimmel wrote:
> RCU readers that identified a net device as a VRF port using
> netif_is_l3_slave() assume that a subsequent call to
> netdev_master_upper_dev_get_rcu() will return a VRF device. They then
> continue to dereference its l3mdev operations.
>
> This assumption is not always correct and can result in a NPD [1]. There
> is no RCU synchronization when removing a port from a VRF, so it is
> possible for an RCU reader to see a new master device (e.g., a bridge)
> that does not have l3mdev operations.
>
> Fix by adding RCU synchronization after clearing the IFF_L3MDEV_SLAVE
> flag. Skip this synchronization when a net device is removed from a VRF
> as part of its deletion and when the VRF device itself is deleted. In
> the latter case an RCU grace period will pass by the time RTNL is
> released.
>
> [1]
> BUG: kernel NULL pointer dereference, address: 0000000000000000
> [...]
> RIP: 0010:l3mdev_fib_table_rcu (net/l3mdev/l3mdev.c:181)
> [...]
> Call Trace:
> <TASK>
> l3mdev_fib_table_by_index (net/l3mdev/l3mdev.c:201 net/l3mdev/l3mdev.c:189)
> __inet_bind (net/ipv4/af_inet.c:499 (discriminator 3))
> inet_bind_sk (net/ipv4/af_inet.c:469)
> __sys_bind (./include/linux/file.h:62 (discriminator 1) ./include/linux/file.h:83 (discriminator 1) net/socket.c:1951 (discriminator 1))
> __x64_sys_bind (net/socket.c:1969 (discriminator 1) net/socket.c:1967 (discriminator 1) net/socket.c:1967 (discriminator 1))
> do_syscall_64 (arch/x86/entry/syscall_64.c:63 (discriminator 1) arch/x86/entry/syscall_64.c:94 (discriminator 1))
> entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130)
>
> Fixes: fdeea7be88b1 ("net: vrf: Set slave's private flag before linking")
> Reported-by: Haoze Xie <royenheart@gmail.com>
> Reported-by: Yifan Wu <yifanwucs@gmail.com>
> Reported-by: Juefei Pu <tomapufckgml@gmail.com>
> Reported-by: Yuan Tan <yuantan098@gmail.com>
> Closes: https://lore.kernel.org/netdev/20260419145332.3988923-1-n05ec@lzu.edu.cn/
> Signed-off-by: Ido Schimmel <idosch@nvidia.com>
> ---
> drivers/net/vrf.c | 15 +++++++++++----
> 1 file changed, 11 insertions(+), 4 deletions(-)
>
Reviewed-by: David Ahern <dsahern@kernel.org>
^ permalink raw reply
* [PATCH net] bareudp: fix NULL pointer dereference in bareudp_fill_metadata_dst()
From: Weiming Shi @ 2026-04-26 16:53 UTC (permalink / raw)
To: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Willem de Bruijn, Martin Varghese, netdev, Xiang Mei, Weiming Shi
bareudp_fill_metadata_dst() passes bareudp->sock to
udp_tunnel6_dst_lookup() in the IPv6 path without a NULL check.
The socket is only created in bareudp_open() and NULLed in
bareudp_stop(), so calling this function while the device is down
triggers a NULL dereference via sock->sk.
BUG: kernel NULL pointer dereference, address: 0000000000000018
RIP: 0010:udp_tunnel6_dst_lookup (net/ipv6/ip6_udp_tunnel.c:160)
Call Trace:
<TASK>
bareudp_fill_metadata_dst (drivers/net/bareudp.c:532)
do_execute_actions (net/openvswitch/actions.c:901)
ovs_execute_actions (net/openvswitch/actions.c:1589)
ovs_packet_cmd_execute (net/openvswitch/datapath.c:700)
genl_family_rcv_msg_doit (net/netlink/genetlink.c:1114)
genl_rcv_msg (net/netlink/genetlink.c:1209)
netlink_rcv_skb (net/netlink/af_netlink.c:2550)
</TASK>
Add a NULL check returning -ESHUTDOWN, consistent with the xmit paths
in the same driver.
Fixes: 571912c69f0e ("net: UDP tunnel encapsulation module for tunnelling different protocols like MPLS, IP, NSH etc.")
Reported-by: Xiang Mei <xmei5@asu.edu>
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
---
drivers/net/bareudp.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c
index 0df3208783ad..da5866ba0699 100644
--- a/drivers/net/bareudp.c
+++ b/drivers/net/bareudp.c
@@ -529,6 +529,9 @@ static int bareudp_fill_metadata_dst(struct net_device *dev,
struct in6_addr saddr;
struct socket *sock = rcu_dereference(bareudp->sock);
+ if (!sock)
+ return -ESHUTDOWN;
+
dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sock,
0, &saddr, &info->key,
sport, bareudp->port, info->key.tos,
--
2.43.0
^ permalink raw reply related
* Re: [RFC Patch net-next v1 1/9] r8169: add some register definitions
From: Subbaraya Sundeep @ 2026-04-26 18:08 UTC (permalink / raw)
To: javen
Cc: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms, netdev, linux-kernel
In-Reply-To: <20260420021957.1756-2-javen_xu@realsil.com.cn>
On 2026-04-20 at 07:49:49, javen (javen_xu@realsil.com.cn) wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
>
> To support rss, this patch adds some macro definitions and register
> definitions.
>
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
> ---
> drivers/net/ethernet/realtek/r8169_main.c | 75 +++++++++++++++++++++++
> 1 file changed, 75 insertions(+)
>
> diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
> index 791277e750ba..0fbec27e4a0d 100644
> --- a/drivers/net/ethernet/realtek/r8169_main.c
> +++ b/drivers/net/ethernet/realtek/r8169_main.c
> @@ -77,6 +77,23 @@
> #define R8169_RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc))
> #define R8169_TX_STOP_THRS (MAX_SKB_FRAGS + 1)
> #define R8169_TX_START_THRS (2 * R8169_TX_STOP_THRS)
> +#define R8169_MAX_RX_QUEUES 8
> +#define R8169_MAX_TX_QUEUES 1
> +#define R8169_MAX_MSIX_VEC 32
> +#define R8127_MAX_TX_QUEUES 1
> +#define R8127_MAX_RX_QUEUES 8
> +#define R8127_MAX_IRQ 32
> +#define R8127_MIN_IRQ 30
> +#define RTL8127_RSS_KEY_SIZE 40
> +#define RSS_CPU_NUM_OFFSET 16
> +#define RSS_MASK_BITS_OFFSET 8
> +#define RTL8127_MAX_INDIRECTION_TABLE_ENTRIES 128
> +#define RXS_8125B_RSS_UDP_V4 BIT(27)
> +#define RXS_8125_RSS_IPV4_V4 BIT(28)
> +#define RXS_8125_RSS_IPV6_V4 BIT(29)
> +#define RXS_8125_RSS_TCP_V4 BIT(30)
> +#define RTL8127_RXS_RSS_L3_TYPE_MASK_V4 (RXS_8125_RSS_IPV4_V4 | RXS_8125_RSS_IPV6_V4)
> +#define RTL8127_RXS_RSS_L4_TYPE_MASK_V4 (RXS_8125_RSS_TCP_V4 | RXS_8125B_RSS_UDP_V4)
>
> #define OCP_STD_PHY_BASE 0xa400
>
> @@ -435,6 +452,8 @@ enum rtl8125_registers {
> #define INT_CFG0_CLKREQEN BIT(3)
> IntrMask_8125 = 0x38,
> IntrStatus_8125 = 0x3c,
> + IntrMask1_8125 = 0x800,
> + IntrStatus1_8125 = 0x802,
> INT_CFG1_8125 = 0x7a,
> LEDSEL2 = 0x84,
> LEDSEL1 = 0x86,
> @@ -444,6 +463,36 @@ enum rtl8125_registers {
> RSS_CTRL_8125 = 0x4500,
> Q_NUM_CTRL_8125 = 0x4800,
> EEE_TXIDLE_TIMER_8125 = 0x6048,
> + TNPDS_Q1_LOW = 0x2100,
> + RDSAR_Q1_LOW = 0x4000,
> + IMR_V2_SET_REG_8125 = 0x0d0c,
> + IMR_V2_CLEAR_REG_8125 = 0x0d00,
> + IMR_V4_L2_CLEAR_REG_8125 = 0x0d10,
> + ISR_V2_8125 = 0x0d04,
> + ISR_V4_L2_8125 = 0x0d14,
> +};
> +
> +enum rtl8127_msix_id {
> + MSIX_ID_V4_LINKCHG = 29,
> +};
> +
> +enum rtl8127_rss_register_content {
> + RSS_CTRL_TCP_IPV4_SUPP = (1 << 0),
> + RSS_CTRL_IPV4_SUPP = (1 << 1),
> + RSS_CTRL_TCP_IPV6_SUPP = (1 << 2),
> + RSS_CTRL_IPV6_SUPP = (1 << 3),
> + RSS_CTRL_IPV6_EXT_SUPP = (1 << 4),
> + RSS_CTRL_TCP_IPV6_EXT_SUPP = (1 << 5),
> + RSS_CTRL_UDP_IPV4_SUPP = (1 << 11),
> + RSS_CTRL_UDP_IPV6_SUPP = (1 << 12),
> + RSS_CTRL_UDP_IPV6_EXT_SUPP = (1 << 13),
> + RSS_INDIRECTION_TBL_8125_V2 = 0x4700,
> + RSS_KEY_8125 = 0x4600,
> +};
> +
> +enum rtl8127_rss_flag {
> + RTL_8125_RSS_FLAG_HASH_UDP_IPV4 = (1 << 0),
> + RTL_8125_RSS_FLAG_HASH_UDP_IPV6 = (1 << 1),
> };
>
> #define LEDSEL_MASK_8125 0x23f
> @@ -474,6 +523,10 @@ enum rtl_register_content {
> RxRUNT = (1 << 20),
> RxCRC = (1 << 19),
>
> + RxRES_RSS = (1 << 22),
> + RxRUNT_RSS = (1 << 21),
> + RxCRC_RSS = (1 << 20),
> +
> /* ChipCmdBits */
> StopReq = 0x80,
> CmdReset = 0x10,
> @@ -576,6 +629,9 @@ enum rtl_register_content {
>
> /* magic enable v2 */
> MagicPacket_v2 = (1 << 16), /* Wake up when receives a Magic Packet */
> + ISRIMR_V6_LINKCHG = (1 << 29),
> + ISRIMR_V6_TOK_Q0 = (1 << 8),
> + ISRIMR_V6_ROK_Q0 = (1 << 0),
> };
>
> enum rtl_desc_bit {
> @@ -633,6 +689,11 @@ enum rtl_rx_desc_bit {
> #define RxProtoIP (PID1 | PID0)
> #define RxProtoMask RxProtoIP
>
> + RxUDPT_v4 = (1 << 19),
> + RxTCPT_v4 = (1 << 18),
> + RxUDPF_v4 = (1 << 16), /* UDP/IP checksum failed */
> + RxTCPF_v4 = (1 << 15), /* TCP/IP checksum failed */
> +
> IPFail = (1 << 16), /* IP checksum failed */
> UDPFail = (1 << 15), /* UDP/IP checksum failed */
> TCPFail = (1 << 14), /* TCP/IP checksum failed */
> @@ -659,6 +720,11 @@ struct RxDesc {
> __le64 addr;
> };
>
> +enum features {
> + RTL_FEATURE_MSI = (1 << 1),
> + RTL_FEATURE_MSIX = (1 << 2),
> +};
> +
> struct ring_info {
> struct sk_buff *skb;
> u32 len;
> @@ -728,6 +794,13 @@ enum rtl_dash_type {
> RTL_DASH_25_BP,
> };
>
> +enum rx_desc_ring_type {
> + RX_DESC_RING_TYPE_UNKNOWN = 0,
> + RX_DESC_RING_TYPE_DEAFULT,
typo DEFAULT
Thanks,
Sundeep
> + RX_DESC_RING_TYPE_RSS,
> + RX_DESC_RING_TYPE_MAX
> +};
> +
> struct rtl8169_private {
> void __iomem *mmio_addr; /* memory map physical address */
> struct pci_dev *pci_dev;
> @@ -763,6 +836,8 @@ struct rtl8169_private {
> unsigned aspm_manageable:1;
> unsigned dash_enabled:1;
> bool sfp_mode:1;
> + bool rss_support:1;
> + bool rss_enable:1;
> dma_addr_t counters_phys_addr;
> struct rtl8169_counters *counters;
> struct rtl8169_tc_offsets tc_offset;
> --
> 2.43.0
>
^ permalink raw reply
* Re: [RFC Patch net-next v1 9/9] r8169: add support for ethtool
From: Subbaraya Sundeep @ 2026-04-26 18:05 UTC (permalink / raw)
To: javen
Cc: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms, netdev, linux-kernel
In-Reply-To: <20260420021957.1756-10-javen_xu@realsil.com.cn>
Hi,
On 2026-04-20 at 07:49:57, javen (javen_xu@realsil.com.cn) wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
>
> This patch add support for changing rx queues by ethtool. We can set rx
> 1, 2, 4, 8 by ethtool -L eth1 rx num.
>
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
> ---
> drivers/net/ethernet/realtek/r8169_main.c | 68 +++++++++++++++++++++++
> 1 file changed, 68 insertions(+)
>
> diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
> index 6b574fc336d6..57087abe7d88 100644
> --- a/drivers/net/ethernet/realtek/r8169_main.c
> +++ b/drivers/net/ethernet/realtek/r8169_main.c
> @@ -6518,6 +6518,72 @@ static void r8169_init_napi(struct rtl8169_private *tp)
> }
> }
>
> +static void rtl8169_get_channels(struct net_device *dev,
> + struct ethtool_channels *ch)
> +{
> + struct rtl8169_private *tp = netdev_priv(dev);
> +
> + ch->max_rx = tp->HwSuppNumRxQueues ? tp->HwSuppNumRxQueues : 1;
> + ch->max_tx = tp->HwSuppNumTxQueues ? tp->HwSuppNumTxQueues : 1;
> + ch->max_other = 0;
> + ch->max_combined = 0;
> +
> + ch->rx_count = tp->num_rx_rings;
> + ch->tx_count = tp->num_tx_rings;
> + ch->other_count = 0;
> + ch->combined_count = 0;
> +}
> +
> +static int rtl8169_set_channels(struct net_device *dev,
> + struct ethtool_channels *ch)
> +{
> + struct rtl8169_private *tp = netdev_priv(dev);
> + bool if_running = netif_running(dev);
> + int i;
> +
> + if (!tp->rss_support && (ch->rx_count > 1 || ch->tx_count > 1)) {
> + netdev_warn(dev, "This chip does not support multiple channels/RSS.\n");
> + return -EOPNOTSUPP;
> + }
> +
> + if (ch->rx_count == 0 || ch->tx_count == 0)
> + return -EINVAL;
> + if (ch->rx_count > tp->HwSuppNumRxQueues ||
> + ch->tx_count > tp->HwSuppNumTxQueues)
> + return -EINVAL;
> + if (ch->other_count || ch->combined_count)
> + return -EINVAL;
> +
> + if (ch->rx_count == tp->num_rx_rings &&
> + ch->tx_count == tp->num_tx_rings)
> + return 0;
> +
Revisit the above checks, they are not needed since ethtool code does all these.
> + if (if_running)
> + rtl8169_close(dev);
> +
> + tp->num_rx_rings = ch->rx_count;
> + tp->num_tx_rings = ch->tx_count;
> +
> + tp->rss_enable = (tp->num_rx_rings > 1 && tp->rss_support);
Please help me understand your HW..Is there a condition where there are
multi Rx queues but HW do not support RSS? I dont know how traffic is distributed
across queues in that case (maybe - pinning flows to individual queues via
ntuple or TC ?)
Thanks,
Sundeep
> +
> + for (i = 0; i < tp->HwSuppIndirTblEntries; i++) {
> + if (tp->rss_enable)
> + tp->rss_indir_tbl[i] = ethtool_rxfh_indir_default(i, tp->num_rx_rings);
> + else
> + tp->rss_indir_tbl[i] = 0;
> + }
> +
> + if (tp->rss_enable)
> + tp->InitRxDescType = RX_DESC_RING_TYPE_RSS;
> + else
> + tp->InitRxDescType = RX_DESC_RING_TYPE_DEAFULT;
> +
> + if (if_running)
> + return rtl_open(dev);
> +
> + return 0;
> +}
> +
> static const struct ethtool_ops rtl8169_ethtool_ops = {
> .supported_coalesce_params = ETHTOOL_COALESCE_USECS |
> ETHTOOL_COALESCE_MAX_FRAMES,
> @@ -6536,6 +6602,8 @@ static const struct ethtool_ops rtl8169_ethtool_ops = {
> .nway_reset = phy_ethtool_nway_reset,
> .get_eee = rtl8169_get_eee,
> .set_eee = rtl8169_set_eee,
> + .get_channels = rtl8169_get_channels,
> + .set_channels = rtl8169_set_channels,
> .get_link_ksettings = phy_ethtool_get_link_ksettings,
> .set_link_ksettings = rtl8169_set_link_ksettings,
> .get_ringparam = rtl8169_get_ringparam,
> --
> 2.43.0
>
^ permalink raw reply
* Re: [RFC Patch net-next v1 2/9] r8169: add napi and irq support
From: Subbaraya Sundeep @ 2026-04-26 18:35 UTC (permalink / raw)
To: javen
Cc: hkallweit1, nic_swsd, andrew+netdev, davem, edumazet, kuba,
pabeni, horms, netdev, linux-kernel
In-Reply-To: <20260420021957.1756-3-javen_xu@realsil.com.cn>
On 2026-04-20 at 07:49:50, javen (javen_xu@realsil.com.cn) wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
>
> Multi tx queues and rx queues require multi irq support. Each irq pairs
> with one napi.
>
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
> ---
> drivers/net/ethernet/realtek/r8169_main.c | 205 ++++++++++++++++++++--
> 1 file changed, 187 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
> index 0fbec27e4a0d..d1f3a208bd1b 100644
> --- a/drivers/net/ethernet/realtek/r8169_main.c
> +++ b/drivers/net/ethernet/realtek/r8169_main.c
> @@ -794,6 +794,19 @@ enum rtl_dash_type {
> RTL_DASH_25_BP,
> };
>
> +struct rtl8169_napi {
> + struct napi_struct napi;
> + void *priv;
> + int index;
> +};
> +
> +struct rtl8169_irq {
> + irq_handler_t handler;
> + unsigned int vector;
> + u8 requested;
> + char name[IFNAMSIZ + 10];
> +};
> +
> enum rx_desc_ring_type {
> RX_DESC_RING_TYPE_UNKNOWN = 0,
> RX_DESC_RING_TYPE_DEAFULT,
> @@ -818,9 +831,20 @@ struct rtl8169_private {
> dma_addr_t RxPhyAddr;
> struct page *Rx_databuff[NUM_RX_DESC]; /* Rx data buffers */
> struct ring_info tx_skb[NUM_TX_DESC]; /* Tx data buffers */
> + struct rtl8169_irq irq_tbl[R8169_MAX_MSIX_VEC];
> + struct rtl8169_napi r8169napi[R8169_MAX_MSIX_VEC];
> + u16 isr_reg[R8169_MAX_MSIX_VEC];
> + u16 imr_reg[R8169_MAX_MSIX_VEC];
> + unsigned int num_tx_rings;
> + unsigned int num_rx_rings;
> u16 cp_cmd;
> u16 tx_lpi_timer;
> u32 irq_mask;
> + u8 min_irq_nvecs;
> + u8 max_irq_nvecs;
> + u8 HwSuppIsrVer;
> + u8 HwCurrIsrVer;
> + u8 irq_nvecs;
> int irq;
> struct clk *clk;
>
> @@ -2755,6 +2779,45 @@ static void rtl_hw_reset(struct rtl8169_private *tp)
> rtl_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100);
> }
>
> +static void rtl_setup_mqs_reg(struct rtl8169_private *tp)
> +{
> + if (tp->mac_version <= RTL_GIGA_MAC_VER_52) {
> + tp->isr_reg[0] = IntrStatus;
> + tp->imr_reg[0] = IntrMask;
> + } else {
> + tp->isr_reg[0] = IntrStatus_8125;
> + tp->imr_reg[0] = IntrMask_8125;
> + }
> +
> + for (int i = 1; i < tp->max_irq_nvecs; i++)
> + tp->isr_reg[i] = (u16)(IntrStatus1_8125 + (i - 1) * 4);
> +
> + for (int i = 1; i < tp->max_irq_nvecs; i++)
> + tp->imr_reg[i] = (u16)(IntrMask1_8125 + (i - 1) * 4);
> +}
> +
> +static void rtl_software_parameter_initialize(struct rtl8169_private *tp)
> +{
> + tp->num_rx_rings = 1;
> + tp->num_tx_rings = 1;
> +
> + switch (tp->mac_version) {
> + case RTL_GIGA_MAC_VER_80:
> + tp->min_irq_nvecs = R8127_MIN_IRQ;
> + tp->max_irq_nvecs = R8127_MAX_IRQ;
> + tp->HwSuppIsrVer = 6;
> + break;
> + default:
> + tp->min_irq_nvecs = 1;
> + tp->max_irq_nvecs = 1;
> + tp->HwSuppIsrVer = 1;
> + break;
> + }
> + tp->HwCurrIsrVer = tp->HwSuppIsrVer;
> +
> + rtl_setup_mqs_reg(tp);
> +}
> +
> static void rtl_request_firmware(struct rtl8169_private *tp)
> {
> struct rtl_fw *rtl_fw;
> @@ -4294,6 +4357,7 @@ static int rtl8169_rx_fill(struct rtl8169_private *tp)
> return 0;
> }
>
> +
> static int rtl8169_init_ring(struct rtl8169_private *tp)
> {
> rtl8169_init_ring_indexes(tp);
> @@ -4313,6 +4377,7 @@ static void rtl8169_unmap_tx_skb(struct rtl8169_private *tp, unsigned int entry)
> DMA_TO_DEVICE);
> memset(desc, 0, sizeof(*desc));
> memset(tx_skb, 0, sizeof(*tx_skb));
> +
> }
>
> static void rtl8169_tx_clear_range(struct rtl8169_private *tp, u32 start,
> @@ -4341,9 +4406,21 @@ static void rtl8169_tx_clear(struct rtl8169_private *tp)
> netdev_reset_queue(tp->dev);
> }
>
> +static void rtl8169_napi_disable(struct rtl8169_private *tp)
> +{
> + for (int i = 0; i < tp->irq_nvecs; i++)
> + napi_disable(&tp->r8169napi[i].napi);
> +}
> +
> +static void rtl8169_napi_enable(struct rtl8169_private *tp)
> +{
> + for (int i = 0; i < tp->irq_nvecs; i++)
> + napi_enable(&tp->r8169napi[i].napi);
> +}
> +
> static void rtl8169_cleanup(struct rtl8169_private *tp)
> {
> - napi_disable(&tp->napi);
> + rtl8169_napi_disable(tp);
>
> /* Give a racing hard_start_xmit a few cycles to complete. */
> synchronize_net();
> @@ -4389,7 +4466,8 @@ static void rtl_reset_work(struct rtl8169_private *tp)
> for (i = 0; i < NUM_RX_DESC; i++)
> rtl8169_mark_to_asic(tp->RxDescArray + i);
>
> - napi_enable(&tp->napi);
> + rtl8169_napi_enable(tp);
> +
> rtl_hw_start(tp);
> }
>
> @@ -4895,7 +4973,7 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
> goto release_descriptor;
> }
>
> - skb = napi_alloc_skb(&tp->napi, pkt_size);
> + skb = napi_alloc_skb(&tp->r8169napi[0].napi, pkt_size);
> if (unlikely(!skb)) {
> dev->stats.rx_dropped++;
> goto release_descriptor;
> @@ -4919,7 +4997,7 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
> if (skb->pkt_type == PACKET_MULTICAST)
> dev->stats.multicast++;
>
> - napi_gro_receive(&tp->napi, skb);
> + napi_gro_receive(&tp->r8169napi[0].napi, skb);
>
> dev_sw_netstats_rx_add(dev, pkt_size);
> release_descriptor:
> @@ -4931,7 +5009,8 @@ static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget
>
> static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
> {
> - struct rtl8169_private *tp = dev_instance;
> + struct rtl8169_napi *napi = dev_instance;
> + struct rtl8169_private *tp = napi->priv;
> u32 status = rtl_get_events(tp);
>
> if ((status & 0xffff) == 0xffff || !(status & tp->irq_mask))
> @@ -4948,13 +5027,53 @@ static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
> phy_mac_interrupt(tp->phydev);
>
> rtl_irq_disable(tp);
> - napi_schedule(&tp->napi);
> + napi_schedule(&napi->napi);
> out:
> rtl_ack_events(tp, status);
>
> return IRQ_HANDLED;
> }
>
> +static void rtl8169_free_irq(struct rtl8169_private *tp)
> +{
> + for (int i = 0; i < tp->irq_nvecs; i++) {
> + struct rtl8169_irq *irq = &tp->irq_tbl[i];
> + struct rtl8169_napi *napi = &tp->r8169napi[i];
> +
> + if (irq->requested) {
> + irq->requested = 0;
> + pci_free_irq(tp->pci_dev, i, napi);
> + }
> + }
> +}
> +
> +static int rtl8169_request_irq(struct rtl8169_private *tp)
> +{
> + struct net_device *dev = tp->dev;
> + int rc = 0;
> + struct rtl8169_irq *irq;
> + struct rtl8169_napi *napi;
> + const int len = sizeof(tp->irq_tbl[0].name);
Follow reverse xmas tree order for variable declarations
> +
> + for (int i = 0; i < tp->irq_nvecs; i++) {
> + irq = &tp->irq_tbl[i];
> +
> + napi = &tp->r8169napi[i];
> + snprintf(irq->name, len, "%s-%d", dev->name, i);
> + rc = pci_request_irq(tp->pci_dev, i, rtl8169_interrupt, NULL, napi, irq->name);
> +
No need of extra line above.
> + if (rc)
> + break;
> +
> + irq->vector = pci_irq_vector(tp->pci_dev, i);
> + irq->requested = 1;
> + }
> +
> + if (rc)
> + rtl8169_free_irq(tp);
> + return rc;
> +}
> +
> static void rtl_task(struct work_struct *work)
> {
> struct rtl8169_private *tp =
> @@ -4989,9 +5108,10 @@ static void rtl_task(struct work_struct *work)
>
> static int rtl8169_poll(struct napi_struct *napi, int budget)
> {
> - struct rtl8169_private *tp = container_of(napi, struct rtl8169_private, napi);
> + struct rtl8169_napi *r8169_napi = container_of(napi, struct rtl8169_napi, napi);
> + struct rtl8169_private *tp = r8169_napi->priv;
> struct net_device *dev = tp->dev;
> - int work_done;
> + int work_done = 0;
>
> rtl_tx(dev, tp, budget);
>
> @@ -5110,7 +5230,7 @@ static void rtl8169_up(struct rtl8169_private *tp)
> phy_init_hw(tp->phydev);
> phy_resume(tp->phydev);
> rtl8169_init_phy(tp);
> - napi_enable(&tp->napi);
> + rtl8169_napi_enable(tp);
> enable_work(&tp->wk.work);
> rtl_reset_work(tp);
>
> @@ -5125,10 +5245,12 @@ static int rtl8169_close(struct net_device *dev)
> pm_runtime_get_sync(&pdev->dev);
>
> netif_stop_queue(dev);
> +
> rtl8169_down(tp);
> rtl8169_rx_clear(tp);
>
> - free_irq(tp->irq, tp);
> +
> + rtl8169_free_irq(tp);
>
> phy_disconnect(tp->phydev);
>
> @@ -5183,14 +5305,14 @@ static int rtl_open(struct net_device *dev)
> rtl_request_firmware(tp);
>
> irqflags = pci_dev_msi_enabled(pdev) ? IRQF_NO_THREAD : IRQF_SHARED;
> - retval = request_irq(tp->irq, rtl8169_interrupt, irqflags, dev->name, tp);
> +
> + retval = rtl8169_request_irq(tp);
> if (retval < 0)
> goto err_release_fw_2;
>
> retval = r8169_phy_connect(tp);
> if (retval)
> goto err_free_irq;
> -
> rtl8169_up(tp);
> rtl8169_init_counter_offsets(tp);
> netif_start_queue(dev);
> @@ -5200,7 +5322,7 @@ static int rtl_open(struct net_device *dev)
> return retval;
>
> err_free_irq:
> - free_irq(tp->irq, tp);
> + rtl8169_free_irq(tp);
> err_release_fw_2:
> rtl_release_firmware(tp);
> rtl8169_rx_clear(tp);
> @@ -5265,7 +5387,7 @@ static int rtl8169_runtime_resume(struct device *dev)
> rtl_rar_set(tp, tp->dev->dev_addr);
> __rtl8169_set_wol(tp, tp->saved_wolopts);
>
> - if (tp->TxDescArray)
> + if (netif_running(tp->dev))
> rtl8169_up(tp);
>
> netif_device_attach(tp->dev);
> @@ -5303,7 +5425,7 @@ static int rtl8169_runtime_suspend(struct device *device)
> {
> struct rtl8169_private *tp = dev_get_drvdata(device);
>
> - if (!tp->TxDescArray) {
> + if (!netif_running(tp->dev)) {
> netif_device_detach(tp->dev);
> return 0;
> }
> @@ -5403,7 +5525,9 @@ static void rtl_set_irq_mask(struct rtl8169_private *tp)
>
> static int rtl_alloc_irq(struct rtl8169_private *tp)
> {
> + struct pci_dev *pdev = tp->pci_dev;
> unsigned int flags;
> + int nvecs = 1;
>
> switch (tp->mac_version) {
> case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06:
> @@ -5419,7 +5543,15 @@ static int rtl_alloc_irq(struct rtl8169_private *tp)
> break;
> }
>
> - return pci_alloc_irq_vectors(tp->pci_dev, 1, 1, flags);
> + nvecs = pci_alloc_irq_vectors(pdev, tp->min_irq_nvecs, tp->max_irq_nvecs, flags);
> +
> + if (nvecs < 0)
> + nvecs = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
> +
> + tp->irq = pdev->irq;
> + tp->irq_nvecs = 1;
> +
> + return nvecs;
> }
>
> static void rtl_read_mac_address(struct rtl8169_private *tp,
> @@ -5614,6 +5746,18 @@ static void rtl_hw_initialize(struct rtl8169_private *tp)
> }
> }
>
> +static int rtl8169_set_real_num_queue(struct rtl8169_private *tp)
> +{
> + int retval;
> +
> + retval = netif_set_real_num_tx_queues(tp->dev, tp->num_tx_rings);
> + if (retval < 0)
> + return retval;
> +
> + retval = netif_set_real_num_rx_queues(tp->dev, tp->num_rx_rings);
> + return retval;
return netif_set_real_num_rx_queues(tp->dev, tp->num_rx_rings);
Also AI review generated for this patch "When rtl8169_interrupt interprets tp as a
struct rtl8169_napi and dereferences napi->priv.." looks valid.
Thanks,
Sundeep
> +}
> +
> static int rtl_jumbo_max(struct rtl8169_private *tp)
> {
> /* Non-GBit versions don't support jumbo frames */
> @@ -5674,6 +5818,20 @@ static bool rtl_aspm_is_safe(struct rtl8169_private *tp)
> return false;
> }
>
> +
> +static void r8169_init_napi(struct rtl8169_private *tp)
> +{
> + for (int i = 0; i < tp->irq_nvecs; i++) {
> + struct rtl8169_napi *r8169napi = &tp->r8169napi[i];
> + int (*poll)(struct napi_struct *napi, int budget);
> +
> + poll = rtl8169_poll;
> + netif_napi_add(tp->dev, &r8169napi->napi, poll);
> + r8169napi->priv = tp;
> + r8169napi->index = i;
> + }
> +}
> +
> static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
> {
> const struct rtl_chip_info *chip;
> @@ -5778,11 +5936,12 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
>
> rtl_hw_reset(tp);
>
> + rtl_software_parameter_initialize(tp);
> +
> rc = rtl_alloc_irq(tp);
> if (rc < 0)
> return dev_err_probe(&pdev->dev, rc, "Can't allocate interrupt\n");
>
> - tp->irq = pci_irq_vector(pdev, 0);
>
> INIT_WORK(&tp->wk.work, rtl_task);
> disable_work(&tp->wk.work);
> @@ -5791,7 +5950,13 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
>
> dev->ethtool_ops = &rtl8169_ethtool_ops;
>
> - netif_napi_add(dev, &tp->napi, rtl8169_poll);
> + if (!tp->rss_support) {
> + netif_napi_add(dev, &tp->r8169napi[0].napi, rtl8169_poll);
> + tp->r8169napi[0].priv = tp;
> + tp->r8169napi[0].index = 0;
> + } else {
> + r8169_init_napi(tp);
> + }
>
> dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
> NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
> @@ -5853,6 +6018,10 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
> if (jumbo_max)
> dev->max_mtu = jumbo_max;
>
> + rc = rtl8169_set_real_num_queue(tp);
> + if (rc < 0)
> + return dev_err_probe(&pdev->dev, rc, "set tx/rx num failure\n");
> +
> rtl_set_irq_mask(tp);
>
> tp->counters = dmam_alloc_coherent (&pdev->dev, sizeof(*tp->counters),
> --
> 2.43.0
>
^ permalink raw reply
* [PATCH net 0/9] net/sched: Fix packet loops in mirred and netem
From: Jamal Hadi Salim @ 2026-04-26 19:09 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, jiri, stephen, victor, savy,
will, xmei5, pctammela, kuniyu, toke, willemdebruijnkernel,
hxzene, Jamal Hadi Salim
This patchset adds a 2-bit per-skb tc_depth counter that travels with
the packet to help fixes packet loops caused by mirred ingress
redirects and netem duplication in stacked qdisc trees.
The existing per-CPU mirred nest tracking loses state when a packet is
deferred through the backlog or moves between CPUs via XPS/RPS.
A per-skb field covers both cases.
Patch 1 adds the tc_depth field in a padding hole in sk_buff.
Patches 2-3 revert the check_netem_in_tree() fix and its tests,
which broke legitimate multi-netem configurations.
Patch 4 uses tc_depth to stop netem duplicate recursion.
Patch 5 uses tc_depth to catch mirred ingress redirect loops.
Patch 6 fixes the infinite loop in the mirred egress blockcast case.
Patch 7 fixes an skb leak in early return error scenarios in tcf_mirred_act
for redirect (caught by Sashiko [1]).
Patches 8-9 add mirred and netem test cases.
Changes in v4:
- Provide better context in patch 1 commit to appease Sashiko
We'll see if it will stop yapping about corner cases! I believe
I am making history being the first human to talk directly to Sashiko
in a patch submission;->
- Fix infinite loop for the mirred egress blockcast case (patch 6)
- Fix skb leak in tcf_mirred_act caught by Sashiko (patch 7)
Changes in v3:
- Renamed skb->ttl to skb->tc_depth to avoid confusion with IP TTL
- Expanded commit messages
- Split mirred and netem test cases into separate patches
- No code changes from v2
Changes in v2:
- Do not reuse skb->from_ingress (which was moved to skb->cb)
[1] https://sashiko.dev/#/patchset/20260413082027.2244884-1-hxzene%40gmail.com
Jamal Hadi Salim (5):
net: Introduce skb tc depth field to track packet loops
net/sched: Revert "net/sched: Restrict conditions for adding
duplicating netems to qdisc tree"
Revert "selftests/tc-testing: Add tests for restrictions on netem
duplication"
net/sched: fix packet loop on netem when duplicate is on
net/sched: Fix ethx:ingress -> ethy:egress -> ethx:ingress mirred loop
Kito Xu (veritas501) (1):
net/sched: act_mirred: Fix blockcast recursion bypass leading to stack
overflow
Victor Nogueira (3):
net/sched: act_mirred: Fix skb leak in early mirred redirect returns
selftests/tc-testing: Add mirred test cases exercising loops
selftests/tc-testing: Add netem test case exercising loops
include/linux/skbuff.h | 2 +
net/sched/act_mirred.c | 67 +-
net/sched/sch_netem.c | 47 +-
.../tc-testing/tc-tests/actions/mirred.json | 616 +++++++++++++++++-
.../tc-testing/tc-tests/infra/qdiscs.json | 5 +-
.../tc-testing/tc-tests/qdiscs/netem.json | 96 +--
6 files changed, 691 insertions(+), 142 deletions(-)
--
2.34.1
^ permalink raw reply
* [PATCH net 1/9] net: Introduce skb tc depth field to track packet loops
From: Jamal Hadi Salim @ 2026-04-26 19:09 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, jiri, stephen, victor, savy,
will, xmei5, pctammela, kuniyu, toke, willemdebruijnkernel,
hxzene, Jamal Hadi Salim
In-Reply-To: <20260426190916.128489-1-jhs@mojatatu.com>
Add a 2-bit per-skb tc depth field to track packet loops across the stack.
The previous per-CPU loop counters like MIRRED_NEST_LIMIT
assume a single call stack and lose state in two cases:
1) When a packet is queued and reprocessed later (e.g., egress->ingress
via backlog), the per-cpu state is gone by the time it is dequeued.
2) With XPS/RPS a packet may arrive on one CPU and be processed on
another.
A per-skb field solves both by travelling with the packet itself.
The field fits in existing padding, using 2 bits that were previously a
hole:
pahole before(-) and after (+) diff looks like:
__u8 slow_gro:1; /* 132: 3 1 */
__u8 csum_not_inet:1; /* 132: 4 1 */
__u8 unreadable:1; /* 132: 5 1 */
+ __u8 tc_depth:2; /* 132: 6 1 */
- /* XXX 2 bits hole, try to pack */
/* XXX 1 byte hole, try to pack */
__u16 tc_index; /* 134 2 */
There used to be a ttl field which was removed as part of tc_verd in commit
aec745e2c520 ("net-tc: remove unused tc_verd fields"). It was already
unused by that time, due to remove earlier in commit c19ae86a510c
("tc: remove unused redirect ttl").
The first user of this field is netem, which increments tc_depth on
duplicated packets before re-enqueueing them at the root qdisc. On
re-entry, netem skips duplication for any skb with tc_depth already set,
bounding recursion to a single level regardless of tree topology.
The other user is mirred which increments it on each pass
and limits to depth to MIRRED_DEFER_LIMIT (3).
The new field was called ttl in earlier versions of this patch
but renamed to tc_depth to avoid confusion with IP ttl.
Note (looking at you Sashiko!):
1. Since both mirred and netem utilize the same 2-bit tc_depth field it is
possible when netem and mirred are used together that netem qdisc to skip
the duplication step. This is a known trade-off, as a 2-bit field cannot
independently track both features' recursion depths and it is not considered
sane to have a setup that addresses both features on at the same time.
2. skb_scrub_packet does not clear tc_depth. This means a packet's loop history
is preserved even across namespaces. While this might be restrictive for
some topologies, it is also design intent to provide robustness against loops
across namespaces.
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
---
include/linux/skbuff.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 2bcf78a4de7b..3f06254ab1b7 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -821,6 +821,7 @@ enum skb_tstamp_type {
* @_sk_redir: socket redirection information for skmsg
* @_nfct: Associated connection, if any (with nfctinfo bits)
* @skb_iif: ifindex of device we arrived on
+ * @tc_depth: counter for packet duplication
* @tc_index: Traffic control index
* @hash: the packet hash
* @queue_mapping: Queue mapping for multiqueue devices
@@ -1030,6 +1031,7 @@ struct sk_buff {
__u8 csum_not_inet:1;
#endif
__u8 unreadable:1;
+ __u8 tc_depth:2;
#if defined(CONFIG_NET_SCHED) || defined(CONFIG_NET_XGRESS)
__u16 tc_index; /* traffic control index */
#endif
--
2.34.1
^ permalink raw reply related
* [PATCH net 2/9] net/sched: Revert "net/sched: Restrict conditions for adding duplicating netems to qdisc tree"
From: Jamal Hadi Salim @ 2026-04-26 19:09 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, jiri, stephen, victor, savy,
will, xmei5, pctammela, kuniyu, toke, willemdebruijnkernel,
hxzene, Jamal Hadi Salim, Ji-Soo Chung, Gerlinde, zyc zyc,
Manas Ghandat
In-Reply-To: <20260426190916.128489-1-jhs@mojatatu.com>
This reverts commit ec8e0e3d7adef940cdf9475e2352c0680189d14e.
The original patch rejects any tree containing two netems when
either has duplication set, even when they sit on unrelated classes
of the same classful parent. That broke configurations that have
worked since netem was introduced.
The re-entrancy problem the original commit was trying to solve is
handled by later patch using tc_depth flag.
Doing this revert will (re)expose the original bug with multiple
netem duplication. When this patch is backported make sure
and get the full series.
Fixes: ec8e0e3d7ade ("net/sched: Restrict conditions for adding duplicating netems to qdisc tree")
Reported-by: Ji-Soo Chung <jschung2@proton.me>
Reported-by: Gerlinde <lrGerlinde@mailfence.com>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220774
Reported-by: zyc zyc <zyc199902@zohomail.cn>
Closes: https://lore.kernel.org/all/19adda5a1e2.12410b78222774.9191120410578703463@zohomail.cn/
Reported-by: Manas Ghandat <ghandatmanas@gmail.com>
Closes: https://lore.kernel.org/netdev/f69b2c8f-8325-4c2e-a011-6dbc089f30e4@gmail.com/
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/sch_netem.c | 40 ----------------------------------------
1 file changed, 40 deletions(-)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 20df1c08b1e9..8c6dea196089 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -975,41 +975,6 @@ static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla,
return 0;
}
-static const struct Qdisc_class_ops netem_class_ops;
-
-static int check_netem_in_tree(struct Qdisc *sch, bool duplicates,
- struct netlink_ext_ack *extack)
-{
- struct Qdisc *root, *q;
- unsigned int i;
-
- root = qdisc_root_sleeping(sch);
-
- if (sch != root && root->ops->cl_ops == &netem_class_ops) {
- if (duplicates ||
- ((struct netem_sched_data *)qdisc_priv(root))->duplicate)
- goto err;
- }
-
- if (!qdisc_dev(root))
- return 0;
-
- hash_for_each(qdisc_dev(root)->qdisc_hash, i, q, hash) {
- if (sch != q && q->ops->cl_ops == &netem_class_ops) {
- if (duplicates ||
- ((struct netem_sched_data *)qdisc_priv(q))->duplicate)
- goto err;
- }
- }
-
- return 0;
-
-err:
- NL_SET_ERR_MSG(extack,
- "netem: cannot mix duplicating netems with other netems in tree");
- return -EINVAL;
-}
-
/* Parse netlink message to set options */
static int netem_change(struct Qdisc *sch, struct nlattr *opt,
struct netlink_ext_ack *extack)
@@ -1068,11 +1033,6 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt,
q->gap = qopt->gap;
q->counter = 0;
q->loss = qopt->loss;
-
- ret = check_netem_in_tree(sch, qopt->duplicate, extack);
- if (ret)
- goto unlock;
-
q->duplicate = qopt->duplicate;
/* for compatibility with earlier versions.
--
2.34.1
^ permalink raw reply related
* [PATCH net 3/9] Revert "selftests/tc-testing: Add tests for restrictions on netem duplication"
From: Jamal Hadi Salim @ 2026-04-26 19:09 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, jiri, stephen, victor, savy,
will, xmei5, pctammela, kuniyu, toke, willemdebruijnkernel,
hxzene, Jamal Hadi Salim
In-Reply-To: <20260426190916.128489-1-jhs@mojatatu.com>
This reverts commit ecdec65ec78d67d3ebd17edc88b88312054abe0d.
The tests added were related to check_netem_in_tree() which was
just reverted in the previous patch.
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
---
.../tc-testing/tc-tests/infra/qdiscs.json | 5 +-
.../tc-testing/tc-tests/qdiscs/netem.json | 81 -------------------
2 files changed, 3 insertions(+), 83 deletions(-)
diff --git a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json
index eefadd0546d3..d32616a46a71 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json
@@ -702,6 +702,7 @@
"$TC qdisc add dev $DUMMY parent 1:1 handle 2:0 netem duplicate 100%",
"$TC filter add dev $DUMMY parent 1:0 protocol ip prio 1 u32 match ip dst 10.10.10.1/32 flowid 1:1",
"$TC class add dev $DUMMY parent 1:0 classid 1:2 hfsc ls m2 10Mbit",
+ "$TC qdisc add dev $DUMMY parent 1:2 handle 3:0 netem duplicate 100%",
"$TC filter add dev $DUMMY parent 1:0 protocol ip prio 2 u32 match ip dst 10.10.10.2/32 flowid 1:2",
"ping -c 1 10.10.10.1 -I$DUMMY > /dev/null || true",
"$TC filter del dev $DUMMY parent 1:0 protocol ip prio 1",
@@ -714,8 +715,8 @@
{
"kind": "hfsc",
"handle": "1:",
- "bytes": 294,
- "packets": 3
+ "bytes": 392,
+ "packets": 4
}
],
"matchCount": "1",
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json
index 718d2df2aafa..3c4444961488 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json
@@ -336,86 +336,5 @@
"teardown": [
"$TC qdisc del dev $DUMMY handle 1: root"
]
- },
- {
- "id": "d34d",
- "name": "NETEM test qdisc duplication restriction in qdisc tree in netem_change root",
- "category": ["qdisc", "netem"],
- "plugins": {
- "requires": "nsPlugin"
- },
- "setup": [
- "$TC qdisc add dev $DUMMY root handle 1: netem limit 1",
- "$TC qdisc add dev $DUMMY parent 1: handle 2: netem limit 1"
- ],
- "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 1: netem duplicate 50%",
- "expExitCode": "2",
- "verifyCmd": "$TC -s qdisc show dev $DUMMY",
- "matchPattern": "qdisc netem",
- "matchCount": "2",
- "teardown": [
- "$TC qdisc del dev $DUMMY handle 1:0 root"
- ]
- },
- {
- "id": "b33f",
- "name": "NETEM test qdisc duplication restriction in qdisc tree in netem_change non-root",
- "category": ["qdisc", "netem"],
- "plugins": {
- "requires": "nsPlugin"
- },
- "setup": [
- "$TC qdisc add dev $DUMMY root handle 1: netem limit 1",
- "$TC qdisc add dev $DUMMY parent 1: handle 2: netem limit 1"
- ],
- "cmdUnderTest": "$TC qdisc change dev $DUMMY handle 2: netem duplicate 50%",
- "expExitCode": "2",
- "verifyCmd": "$TC -s qdisc show dev $DUMMY",
- "matchPattern": "qdisc netem",
- "matchCount": "2",
- "teardown": [
- "$TC qdisc del dev $DUMMY handle 1:0 root"
- ]
- },
- {
- "id": "cafe",
- "name": "NETEM test qdisc duplication restriction in qdisc tree",
- "category": ["qdisc", "netem"],
- "plugins": {
- "requires": "nsPlugin"
- },
- "setup": [
- "$TC qdisc add dev $DUMMY root handle 1: netem limit 1 duplicate 100%"
- ],
- "cmdUnderTest": "$TC qdisc add dev $DUMMY parent 1: handle 2: netem duplicate 100%",
- "expExitCode": "2",
- "verifyCmd": "$TC -s qdisc show dev $DUMMY",
- "matchPattern": "qdisc netem",
- "matchCount": "1",
- "teardown": [
- "$TC qdisc del dev $DUMMY handle 1:0 root"
- ]
- },
- {
- "id": "1337",
- "name": "NETEM test qdisc duplication restriction in qdisc tree across branches",
- "category": ["qdisc", "netem"],
- "plugins": {
- "requires": "nsPlugin"
- },
- "setup": [
- "$TC qdisc add dev $DUMMY parent root handle 1:0 hfsc",
- "$TC class add dev $DUMMY parent 1:0 classid 1:1 hfsc rt m2 10Mbit",
- "$TC qdisc add dev $DUMMY parent 1:1 handle 2:0 netem",
- "$TC class add dev $DUMMY parent 1:0 classid 1:2 hfsc rt m2 10Mbit"
- ],
- "cmdUnderTest": "$TC qdisc add dev $DUMMY parent 1:2 handle 3:0 netem duplicate 100%",
- "expExitCode": "2",
- "verifyCmd": "$TC -s qdisc show dev $DUMMY",
- "matchPattern": "qdisc netem",
- "matchCount": "1",
- "teardown": [
- "$TC qdisc del dev $DUMMY handle 1:0 root"
- ]
}
]
--
2.34.1
^ permalink raw reply related
* [PATCH net 4/9] net/sched: fix packet loop on netem when duplicate is on
From: Jamal Hadi Salim @ 2026-04-26 19:09 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, jiri, stephen, victor, savy,
will, xmei5, pctammela, kuniyu, toke, willemdebruijnkernel,
hxzene, Jamal Hadi Salim
In-Reply-To: <20260426190916.128489-1-jhs@mojatatu.com>
When netem duplicates a packet it re-enqueues the copy at the root qdisc.
If another netem sits in the tree the copy can be duplicated
again, recursing until the stack or memory is exhausted.
The original duplication guard temporarily zeroed q->duplicate around
the re-enqueue, but that does not cover all cases because it is
per-qdisc state shared across all concurrent enqueue paths
and is not safe without additional locking.
Use the skb tc_depth field introduced in an earlier patch:
- increment it on the duplicate before re-enqueue
- skip duplication for any skb whose tc_depth is already non-zero.
This marks the packet itself rather than mutating qdisc state,
therefore it is safe regardless of tree topology or concurrency.
Fixes: 0afb51e72855 ("[PKT_SCHED]: netem: reinsert for duplication")
Reported-by: William Liu <will@willsroot.io>
Reported-by: Savino Dicanosa <savy@syst3mfailure.io>
Closes: https://lore.kernel.org/netdev/8DuRWwfqjoRDLDmBMlIfbrsZg9Gx50DHJc1ilxsEBNe2D6NMoigR_eIRIG0LOjMc3r10nUUZtArXx4oZBIdUfZQrwjcQhdinnMis_0G7VEk=@willsroot.io/
Co-developed-by: Victor Nogueira <victor@mojatatu.com>
Signed-off-by: Victor Nogueira <victor@mojatatu.com>
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/sch_netem.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index 8c6dea196089..d595eb03dca8 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -461,7 +461,8 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
skb->prev = NULL;
/* Random duplication */
- if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor, &q->prng))
+ if (q->duplicate && skb->tc_depth == 0 &&
+ q->duplicate >= get_crandom(&q->dup_cor, &q->prng))
++count;
/* Drop packet? */
@@ -540,11 +541,9 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
*/
if (skb2) {
struct Qdisc *rootq = qdisc_root_bh(sch);
- u32 dupsave = q->duplicate; /* prevent duplicating a dup... */
- q->duplicate = 0;
+ skb2->tc_depth++; /* prevent duplicating a dup... */
rootq->enqueue(skb2, rootq, to_free);
- q->duplicate = dupsave;
skb2 = NULL;
}
--
2.34.1
^ permalink raw reply related
* [PATCH net 5/9] net/sched: Fix ethx:ingress -> ethy:egress -> ethx:ingress mirred loop
From: Jamal Hadi Salim @ 2026-04-26 19:09 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, jiri, stephen, victor, savy,
will, xmei5, pctammela, kuniyu, toke, willemdebruijnkernel,
hxzene, Jamal Hadi Salim
In-Reply-To: <20260426190916.128489-1-jhs@mojatatu.com>
When mirred redirects to ingress (from either ingress or egress) the loop
state from sched_mirred_dev array dev is lost because of 1) the packet
deferral into the backlog and 2) the fact the sched_mirred_dev array is
cleared. In such cases, if there was a loop we won't discover it.
Here's a simple test to reproduce:
ip a add dev port0 10.10.10.11/24
tc qdisc add dev port0 clsact
tc filter add dev port0 egress protocol ip \
prio 10 matchall action mirred ingress redirect dev port1
tc qdisc add dev port1 clsact
tc filter add dev port1 ingress protocol ip \
prio 10 matchall action mirred egress redirect dev port0
ping -c 1 -W0.01 10.10.10.10
Fixes: fe946a751d9b ("net/sched: act_mirred: add loop detection")
Tested-by: Victor Nogueira <victor@mojatatu.com>
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
---
net/sched/act_mirred.c | 47 +++++++++++++++++++++++++++---------------
1 file changed, 30 insertions(+), 17 deletions(-)
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index 2c5a7a321a94..dd5e7ea7ef26 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -26,6 +26,10 @@
#include <net/tc_act/tc_mirred.h>
#include <net/tc_wrapper.h>
+#define MIRRED_DEFER_LIMIT 3
+_Static_assert(MIRRED_DEFER_LIMIT <= 3,
+ "MIRRED_DEFER_LIMIT exceeds tc_depth bitfield width");
+
static LIST_HEAD(mirred_list);
static DEFINE_SPINLOCK(mirred_list_lock);
@@ -234,12 +238,15 @@ tcf_mirred_forward(bool at_ingress, bool want_ingress, struct sk_buff *skb)
{
int err;
- if (!want_ingress)
+ if (!want_ingress) {
err = tcf_dev_queue_xmit(skb, dev_queue_xmit);
- else if (!at_ingress)
- err = netif_rx(skb);
- else
- err = netif_receive_skb(skb);
+ } else {
+ skb->tc_depth++;
+ if (!at_ingress)
+ err = netif_rx(skb);
+ else
+ err = netif_receive_skb(skb);
+ }
return err;
}
@@ -426,6 +433,7 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
struct netdev_xmit *xmit;
bool m_mac_header_xmit;
struct net_device *dev;
+ bool want_ingress;
int i, m_eaction;
u32 blockid;
@@ -434,7 +442,8 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
#else
xmit = this_cpu_ptr(&softnet_data.xmit);
#endif
- if (unlikely(xmit->sched_mirred_nest >= MIRRED_NEST_LIMIT)) {
+ if (unlikely(xmit->sched_mirred_nest >= MIRRED_NEST_LIMIT ||
+ skb->tc_depth >= MIRRED_DEFER_LIMIT)) {
net_warn_ratelimited("Packet exceeded mirred recursion limit on dev %s\n",
netdev_name(skb->dev));
return TC_ACT_SHOT;
@@ -453,23 +462,27 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
tcf_action_inc_overlimit_qstats(&m->common);
return retval;
}
- for (i = 0; i < xmit->sched_mirred_nest; i++) {
- if (xmit->sched_mirred_dev[i] != dev)
- continue;
- pr_notice_once("tc mirred: loop on device %s\n",
- netdev_name(dev));
- tcf_action_inc_overlimit_qstats(&m->common);
- return retval;
- }
- xmit->sched_mirred_dev[xmit->sched_mirred_nest++] = dev;
+ m_eaction = READ_ONCE(m->tcfm_eaction);
+ want_ingress = tcf_mirred_act_wants_ingress(m_eaction);
+ if (!want_ingress) {
+ for (i = 0; i < xmit->sched_mirred_nest; i++) {
+ if (xmit->sched_mirred_dev[i] != dev)
+ continue;
+ pr_notice_once("tc mirred: loop on device %s\n",
+ netdev_name(dev));
+ tcf_action_inc_overlimit_qstats(&m->common);
+ return retval;
+ }
+ xmit->sched_mirred_dev[xmit->sched_mirred_nest++] = dev;
+ }
m_mac_header_xmit = READ_ONCE(m->tcfm_mac_header_xmit);
- m_eaction = READ_ONCE(m->tcfm_eaction);
retval = tcf_mirred_to_dev(skb, m, dev, m_mac_header_xmit, m_eaction,
retval);
- xmit->sched_mirred_nest--;
+ if (!want_ingress)
+ xmit->sched_mirred_nest--;
return retval;
}
--
2.34.1
^ permalink raw reply related
* [PATCH net 6/9] net/sched: act_mirred: Fix blockcast recursion bypass leading to stack overflow
From: Jamal Hadi Salim @ 2026-04-26 19:09 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, jiri, stephen, victor, savy,
will, xmei5, pctammela, kuniyu, toke, willemdebruijnkernel,
hxzene, Jamal Hadi Salim
In-Reply-To: <20260426190916.128489-1-jhs@mojatatu.com>
From: "Kito Xu (veritas501)" <hxzene@gmail.com>
tcf_mirred_act() checks sched_mirred_nest against MIRRED_NEST_LIMIT (4)
to prevent deep recursion. However, when the action uses blockcast
(tcfm_blockid != 0), the function returns at the tcf_blockcast() call
BEFORE reaching the counter increment. As a result, the recursion
counter never advances and the limit check is entirely bypassed.
When two devices share a TC egress block with a mirred blockcast rule,
a packet egressing on device A is mirrored to device B via blockcast;
device B's egress TC re-enters tcf_mirred_act() via blockcast and
mirrors back to A, creating an unbounded recursion loop:
tcf_mirred_act -> tcf_blockcast -> tcf_mirred_to_dev -> dev_queue_xmit
-> sch_handle_egress -> tcf_classify -> tcf_mirred_act -> (repeat)
This recursion continues until the kernel stack overflows.
The bug is reachable from an unprivileged user via
unshare(CLONE_NEWUSER | CLONE_NEWNET): user namespaces grant
CAP_NET_ADMIN in the new network namespace, which is sufficient to
create dummy devices, attach clsact qdiscs with shared blocks, and
install mirred blockcast filters.
BUG: TASK stack guard page was hit at ffffc90000b7fff8
Oops: stack guard page: 0000 [#1] SMP KASAN NOPTI
CPU: 2 UID: 1000 PID: 169 Comm: poc Not tainted 7.0.0-rc7-next-20260410
RIP: 0010:xas_find+0x17/0x480
Call Trace:
xa_find+0x17b/0x1d0
tcf_mirred_act+0x640/0x1060
tcf_action_exec+0x400/0x530
basic_classify+0x128/0x1d0
tcf_classify+0xd83/0x1150
tc_run+0x328/0x620
__dev_queue_xmit+0x797/0x3100
tcf_mirred_to_dev+0x7b1/0xf70
tcf_mirred_act+0x68a/0x1060
[repeating ~30+ times until stack overflow]
Kernel panic - not syncing: Fatal exception in interrupt
Fix this by incrementing sched_mirred_nest before calling
tcf_blockcast() and decrementing it on return, mirroring the
non-blockcast path. This ensures subsequent recursive entries see the
updated counter and are correctly limited by MIRRED_NEST_LIMIT.
Fixes: fe946a751d9b ("net/sched: act_mirred: add loop detection")
Tested-by: Victor Nogueira <victor@mojatatu.com>
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: Kito Xu (veritas501) <hxzene@gmail.com>
---
net/sched/act_mirred.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index dd5e7ea7ef26..ea64faf7f469 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -453,8 +453,12 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
tcf_action_update_bstats(&m->common, skb);
blockid = READ_ONCE(m->tcfm_blockid);
- if (blockid)
- return tcf_blockcast(skb, m, blockid, res, retval);
+ if (blockid) {
+ xmit->sched_mirred_dev[xmit->sched_mirred_nest++] = NULL;
+ retval = tcf_blockcast(skb, m, blockid, res, retval);
+ xmit->sched_mirred_nest--;
+ return retval;
+ }
dev = rcu_dereference_bh(m->tcfm_dev);
if (unlikely(!dev)) {
--
2.34.1
^ permalink raw reply related
* [PATCH net 7/9] net/sched: act_mirred: Fix skb leak in early mirred redirect returns
From: Jamal Hadi Salim @ 2026-04-26 19:09 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, jiri, stephen, victor, savy,
will, xmei5, pctammela, kuniyu, toke, willemdebruijnkernel,
hxzene, Sashiko, Jamal Hadi Salim
In-Reply-To: <20260426190916.128489-1-jhs@mojatatu.com>
From: Victor Nogueira <victor@mojatatu.com>
Since retval is set as TC_ACT_STOLEN in the mirred redirect case,
returning retval in cases where redirect failed will make the core
code not free the skb and thus cause a leak.
Fix this by returning TC_ACT_SHOT instead in such scenarios.
Fixes: 16085e48cb48 ("net/sched: act_mirred: Create function tcf_mirred_to_dev and improve readability")
Reported-by: Sashiko <sashiko-dev@lists.linux.dev>
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: Victor Nogueira <victor@mojatatu.com>
---
net/sched/act_mirred.c | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index ea64faf7f469..4c7af7bd7c0d 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -412,7 +412,7 @@ static int tcf_blockcast(struct sk_buff *skb, struct tcf_mirred *m,
block = tcf_block_lookup(dev_net(skb->dev), blockid);
if (!block || xa_empty(&block->ports)) {
tcf_action_inc_overlimit_qstats(&m->common);
- return retval;
+ return is_redirect ? TC_ACT_SHOT : retval;
}
if (is_redirect)
@@ -430,8 +430,8 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
{
struct tcf_mirred *m = to_mirred(a);
int retval = READ_ONCE(m->tcf_action);
+ bool m_mac_header_xmit, is_redirect;
struct netdev_xmit *xmit;
- bool m_mac_header_xmit;
struct net_device *dev;
bool want_ingress;
int i, m_eaction;
@@ -460,14 +460,15 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
return retval;
}
+ m_eaction = READ_ONCE(m->tcfm_eaction);
+ is_redirect = tcf_mirred_is_act_redirect(m_eaction);
+
dev = rcu_dereference_bh(m->tcfm_dev);
if (unlikely(!dev)) {
pr_notice_once("tc mirred: target device is gone\n");
tcf_action_inc_overlimit_qstats(&m->common);
- return retval;
+ goto err_out;
}
-
- m_eaction = READ_ONCE(m->tcfm_eaction);
want_ingress = tcf_mirred_act_wants_ingress(m_eaction);
if (!want_ingress) {
for (i = 0; i < xmit->sched_mirred_nest; i++) {
@@ -476,7 +477,7 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
pr_notice_once("tc mirred: loop on device %s\n",
netdev_name(dev));
tcf_action_inc_overlimit_qstats(&m->common);
- return retval;
+ goto err_out;
}
xmit->sched_mirred_dev[xmit->sched_mirred_nest++] = dev;
}
@@ -489,6 +490,11 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
xmit->sched_mirred_nest--;
return retval;
+
+err_out:
+ if (is_redirect)
+ retval = TC_ACT_SHOT;
+ return retval;
}
static void tcf_stats_update(struct tc_action *a, u64 bytes, u64 packets,
--
2.34.1
^ permalink raw reply related
* [PATCH net 8/9] selftests/tc-testing: Add mirred test cases exercising loops
From: Jamal Hadi Salim @ 2026-04-26 19:09 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, jiri, stephen, victor, savy,
will, xmei5, pctammela, kuniyu, toke, willemdebruijnkernel,
hxzene, Jamal Hadi Salim
In-Reply-To: <20260426190916.128489-1-jhs@mojatatu.com>
From: Victor Nogueira <victor@mojatatu.com>
Add mirred loop test cases to validate that those will be caught and other
test cases that were previously misinterpreted as loops by mirred.
This commit adds 12 test cases:
- Redirect multiport: dummy egress -> dev1 ingress -> dummy egress (Loop)
- Redirect singleport: dev1 ingress -> dev1 egress -> dev1 ingress (Loop)
- Redirect multiport: dev1 ingress -> dummy ingress -> dev1 egress (No Loop)
- Redirect multiport: dev1 ingress -> dummy ingress -> dev1 ingress (Loop)
- Redirect multiport: dev1 ingress -> dummy egress -> dev1 ingress (Loop)
- Redirect multiport: dummy egress -> dev1 ingress -> dummy egress, different prios (Loop)
- Redirect multiport: dev1 ingress -> dummy ingress -> dummy egress -> dev1 egress (No Loop)
- Redirect multiport: dev1 ingress -> dummy egress -> dev1 egress (No Loop)
- Redirect multiport: dev1 ingress -> dummy egress -> dummy ingress (No Loop)
- Redirect singleport: dev1 ingress -> dev1 ingress (Loop)
- Redirect singleport: dummy egress -> dummy ingress (No Loop)
- Redirect multiport: dev1 ingress -> dummy ingress -> dummy egress (No Loop)
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Acked-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Victor Nogueira <victor@mojatatu.com>
---
.../tc-testing/tc-tests/actions/mirred.json | 616 +++++++++++++++++-
1 file changed, 615 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json b/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json
index b056eb966871..d0cad6571691 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/actions/mirred.json
@@ -1144,6 +1144,620 @@
"teardown": [
"$TC qdisc del dev $DUMMY clsact"
]
+ },
+ {
+ "id": "531c",
+ "name": "Redirect multiport: dummy egress -> dev1 ingress -> dummy egress (Loop)",
+ "category": [
+ "filter",
+ "mirred"
+ ],
+ "plugins": {
+ "requires": [
+ "nsPlugin"
+ ]
+ },
+ "setup": [
+ "$IP link set dev $DUMMY up || true",
+ "$IP addr add 10.10.10.10/24 dev $DUMMY || true",
+ "$TC qdisc add dev $DUMMY clsact",
+ "$TC filter add dev $DUMMY egress protocol ip prio 10 matchall action mirred ingress redirect dev $DEV1 index 1",
+ "$TC qdisc add dev $DEV1 clsact",
+ "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred egress redirect dev $DUMMY index 2"
+ ],
+ "cmdUnderTest": "ping -c1 -W0.01 -I $DUMMY 10.10.10.1",
+ "expExitCode": "1",
+ "verifyCmd": "$TC -j -s actions get action mirred index 1",
+ "matchJSON": [
+ {
+ "total acts": 0
+ },
+ {
+ "actions": [
+ {
+ "order": 1,
+ "kind": "mirred",
+ "mirred_action": "redirect",
+ "direction": "ingress",
+ "index": 1,
+ "stats": {
+ "packets": 3
+ },
+ "not_in_hw": true
+ }
+ ]
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DUMMY clsact",
+ "$TC qdisc del dev $DEV1 clsact"
+ ]
+ },
+ {
+ "id": "b1d7",
+ "name": "Redirect singleport: dev1 ingress -> dev1 egress -> dev1 ingress (Loop)",
+ "category": [
+ "filter",
+ "mirred"
+ ],
+ "plugins": {
+ "requires": [
+ "nsPlugin",
+ "scapyPlugin"
+ ]
+ },
+ "setup": [
+ "$TC qdisc add dev $DEV1 clsact",
+ "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred egress redirect dev $DEV1 index 1"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DEV1 egress protocol ip prio 11 matchall action mirred ingress redirect dev $DEV1 index 2",
+ "scapy": [
+ {
+ "iface": "$DEV0",
+ "count": 1,
+ "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
+ }
+ ],
+ "expExitCode": "0",
+ "verifyCmd": "$TC -j -s actions get action mirred index 1",
+ "matchJSON": [
+ {
+ "total acts": 0
+ },
+ {
+ "actions": [
+ {
+ "order": 1,
+ "kind": "mirred",
+ "mirred_action": "redirect",
+ "direction": "egress",
+ "index": 1,
+ "stats": {
+ "packets": 3
+ },
+ "not_in_hw": true
+ }
+ ]
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DEV1 clsact"
+ ]
+ },
+ {
+ "id": "c66d",
+ "name": "Redirect multiport: dev1 ingress -> dummy ingress -> dev1 egress (No Loop)",
+ "category": [
+ "filter",
+ "mirred"
+ ],
+ "plugins": {
+ "requires": [
+ "nsPlugin",
+ "scapyPlugin"
+ ]
+ },
+ "setup": [
+ "$TC qdisc add dev $DEV1 clsact",
+ "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred ingress redirect dev $DUMMY index 1",
+ "$TC qdisc add dev $DUMMY clsact"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY ingress protocol ip prio 11 matchall action mirred egress redirect dev $DEV1 index 2",
+ "scapy": [
+ {
+ "iface": "$DEV0",
+ "count": 1,
+ "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
+ }
+ ],
+ "expExitCode": "0",
+ "verifyCmd": "$TC -j -s actions get action mirred index 1",
+ "matchJSON": [
+ {
+ "total acts": 0
+ },
+ {
+ "actions": [
+ {
+ "order": 1,
+ "kind": "mirred",
+ "mirred_action": "redirect",
+ "direction": "ingress",
+ "index": 1,
+ "stats": {
+ "packets": 1
+ },
+ "not_in_hw": true
+ }
+ ]
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DEV1 clsact",
+ "$TC qdisc del dev $DUMMY clsact"
+ ]
+ },
+ {
+ "id": "aa99",
+ "name": "Redirect multiport: dev1 ingress -> dummy ingress -> dev1 ingress (Loop)",
+ "category": [
+ "filter",
+ "mirred"
+ ],
+ "plugins": {
+ "requires": [
+ "nsPlugin",
+ "scapyPlugin"
+ ]
+ },
+ "setup": [
+ "$TC qdisc add dev $DEV1 clsact",
+ "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred ingress redirect dev $DUMMY index 1",
+ "$TC qdisc add dev $DUMMY clsact"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY ingress protocol ip prio 11 matchall action mirred ingress redirect dev $DEV1 index 2",
+ "scapy": [
+ {
+ "iface": "$DEV0",
+ "count": 1,
+ "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
+ }
+ ],
+ "expExitCode": "0",
+ "verifyCmd": "$TC -j -s actions get action mirred index 1",
+ "matchJSON": [
+ {
+ "total acts": 0
+ },
+ {
+ "actions": [
+ {
+ "order": 1,
+ "kind": "mirred",
+ "mirred_action": "redirect",
+ "direction": "ingress",
+ "index": 1,
+ "stats": {
+ "packets": 2,
+ "overlimits": 1
+ },
+ "not_in_hw": true
+ }
+ ]
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DEV1 clsact",
+ "$TC qdisc del dev $DUMMY clsact"
+ ]
+ },
+ {
+ "id": "37d7",
+ "name": "Redirect multiport: dev1 ingress -> dummy egress -> dev1 ingress (Loop)",
+ "category": [
+ "filter",
+ "mirred"
+ ],
+ "plugins": {
+ "requires": [
+ "nsPlugin",
+ "scapyPlugin"
+ ]
+ },
+ "setup": [
+ "$TC qdisc add dev $DEV1 clsact",
+ "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred egress redirect dev $DUMMY index 1",
+ "$TC qdisc add dev $DUMMY clsact"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY egress protocol ip prio 11 matchall action mirred ingress redirect dev $DEV1 index 2",
+ "scapy": [
+ {
+ "iface": "$DEV0",
+ "count": 1,
+ "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
+ }
+ ],
+ "expExitCode": "0",
+ "verifyCmd": "$TC -j -s actions get action mirred index 1",
+ "matchJSON": [
+ {
+ "total acts": 0
+ },
+ {
+ "actions": [
+ {
+ "order": 1,
+ "kind": "mirred",
+ "mirred_action": "redirect",
+ "direction": "egress",
+ "index": 1,
+ "stats": {
+ "packets": 3
+ },
+ "not_in_hw": true
+ }
+ ]
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DEV1 clsact",
+ "$TC qdisc del dev $DUMMY clsact"
+ ]
+ },
+ {
+ "id": "6d02",
+ "name": "Redirect multiport: dummy egress -> dev1 ingress -> dummy egress, different prios (Loop)",
+ "category": [
+ "filter",
+ "mirred"
+ ],
+ "plugins": {
+ "requires": [
+ "nsPlugin"
+ ]
+ },
+ "setup": [
+ "$IP link set dev $DUMMY up || true",
+ "$IP addr add 10.10.10.10/24 dev $DUMMY || true",
+ "$TC qdisc add dev $DUMMY clsact",
+ "$TC filter add dev $DUMMY egress protocol ip prio 10 matchall action mirred ingress redirect dev $DEV1 index 1",
+ "$TC qdisc add dev $DEV1 clsact",
+ "$TC filter add dev $DEV1 ingress protocol ip prio 11 matchall action mirred egress redirect dev $DUMMY index 2"
+ ],
+ "cmdUnderTest": "ping -c1 -W0.01 -I $DUMMY 10.10.10.1",
+ "expExitCode": "1",
+ "verifyCmd": "$TC -j -s actions get action mirred index 1",
+ "matchJSON": [
+ {
+ "total acts": 0
+ },
+ {
+ "actions": [
+ {
+ "order": 1,
+ "kind": "mirred",
+ "mirred_action": "redirect",
+ "direction": "ingress",
+ "index": 1,
+ "stats": {
+ "packets": 3
+ },
+ "not_in_hw": true
+ }
+ ]
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DUMMY clsact",
+ "$TC qdisc del dev $DEV1 clsact"
+ ]
+ },
+ {
+ "id": "8115",
+ "name": "Redirect multiport: dev1 ingress -> dummy ingress -> dummy egress -> dev1 egress (No Loop)",
+ "category": [
+ "filter",
+ "mirred"
+ ],
+ "plugins": {
+ "requires": [
+ "nsPlugin",
+ "scapyPlugin"
+ ]
+ },
+ "setup": [
+ "$TC qdisc add dev $DEV1 clsact",
+ "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred ingress redirect dev $DUMMY index 1",
+ "$TC qdisc add dev $DUMMY clsact",
+ "$TC filter add dev $DUMMY ingress protocol ip prio 11 matchall action mirred egress redirect dev $DUMMY index 2"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY egress protocol ip prio 12 matchall action mirred egress redirect dev $DEV1 index 3",
+ "scapy": [
+ {
+ "iface": "$DEV0",
+ "count": 1,
+ "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
+ }
+ ],
+ "expExitCode": "0",
+ "verifyCmd": "$TC -j -s actions get action mirred index 1",
+ "matchJSON": [
+ {
+ "total acts": 0
+ },
+ {
+ "actions": [
+ {
+ "order": 1,
+ "kind": "mirred",
+ "mirred_action": "redirect",
+ "direction": "ingress",
+ "index": 1,
+ "stats": {
+ "packets": 1
+ },
+ "not_in_hw": true
+ }
+ ]
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DEV1 clsact",
+ "$TC qdisc del dev $DUMMY clsact"
+ ]
+ },
+ {
+ "id": "9eb3",
+ "name": "Redirect multiport: dev1 ingress -> dummy egress -> dev1 egress (No Loop)",
+ "category": [
+ "filter",
+ "mirred"
+ ],
+ "plugins": {
+ "requires": [
+ "nsPlugin",
+ "scapyPlugin"
+ ]
+ },
+ "setup": [
+ "$TC qdisc add dev $DEV1 clsact",
+ "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred egress redirect dev $DUMMY index 1",
+ "$TC qdisc add dev $DUMMY clsact"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY egress protocol ip prio 11 matchall action mirred egress redirect dev $DEV1 index 2",
+ "scapy": [
+ {
+ "iface": "$DEV0",
+ "count": 1,
+ "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
+ }
+ ],
+ "expExitCode": "0",
+ "verifyCmd": "$TC -j -s actions get action mirred index 1",
+ "matchJSON": [
+ {
+ "total acts": 0
+ },
+ {
+ "actions": [
+ {
+ "order": 1,
+ "kind": "mirred",
+ "mirred_action": "redirect",
+ "direction": "egress",
+ "index": 1,
+ "stats": {
+ "packets": 1
+ },
+ "not_in_hw": true
+ }
+ ]
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DEV1 clsact",
+ "$TC qdisc del dev $DUMMY clsact"
+ ]
+ },
+ {
+ "id": "d837",
+ "name": "Redirect multiport: dev1 ingress -> dummy egress -> dummy ingress (No Loop)",
+ "category": [
+ "filter",
+ "mirred"
+ ],
+ "plugins": {
+ "requires": [
+ "nsPlugin",
+ "scapyPlugin"
+ ]
+ },
+ "setup": [
+ "$TC qdisc add dev $DEV1 clsact",
+ "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred egress redirect dev $DUMMY index 1",
+ "$TC qdisc add dev $DUMMY clsact"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY egress protocol ip prio 11 matchall action mirred ingress redirect dev $DUMMY index 2",
+ "scapy": [
+ {
+ "iface": "$DEV0",
+ "count": 1,
+ "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
+ }
+ ],
+ "expExitCode": "0",
+ "verifyCmd": "$TC -j -s actions get action mirred index 1",
+ "matchJSON": [
+ {
+ "total acts": 0
+ },
+ {
+ "actions": [
+ {
+ "order": 1,
+ "kind": "mirred",
+ "mirred_action": "redirect",
+ "direction": "egress",
+ "index": 1,
+ "stats": {
+ "packets": 1
+ },
+ "not_in_hw": true
+ }
+ ]
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DEV1 clsact",
+ "$TC qdisc del dev $DUMMY clsact"
+ ]
+ },
+ {
+ "id": "2071",
+ "name": "Redirect singleport: dev1 ingress -> dev1 ingress (Loop)",
+ "category": [
+ "filter",
+ "mirred"
+ ],
+ "plugins": {
+ "requires": [
+ "nsPlugin",
+ "scapyPlugin"
+ ]
+ },
+ "setup": [
+ "$TC qdisc add dev $DEV1 clsact"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred ingress redirect dev $DEV1 index 1",
+ "scapy": [
+ {
+ "iface": "$DEV0",
+ "count": 1,
+ "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
+ }
+ ],
+ "expExitCode": "0",
+ "verifyCmd": "$TC -j -s actions get action mirred index 1",
+ "matchJSON": [
+ {
+ "total acts": 0
+ },
+ {
+ "actions": [
+ {
+ "order": 1,
+ "kind": "mirred",
+ "mirred_action": "redirect",
+ "direction": "ingress",
+ "index": 1,
+ "stats": {
+ "packets": 1,
+ "overlimits": 1
+ },
+ "not_in_hw": true
+ }
+ ]
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DEV1 clsact"
+ ]
+ },
+ {
+ "id": "0101",
+ "name": "Redirect singleport: dummy egress -> dummy ingress (No Loop)",
+ "category": [
+ "filter",
+ "mirred"
+ ],
+ "plugins": {
+ "requires": [
+ "nsPlugin"
+ ]
+ },
+ "setup": [
+ "$IP addr add 10.10.10.10/24 dev $DUMMY || true",
+ "$TC qdisc add dev $DUMMY clsact",
+ "$TC filter add dev $DUMMY egress protocol ip prio 11 matchall action mirred ingress redirect dev $DUMMY index 1"
+ ],
+ "cmdUnderTest": "ping -c1 -W0.01 -I $DUMMY 10.10.10.1",
+ "expExitCode": "1",
+ "verifyCmd": "$TC -j -s actions get action mirred index 1",
+ "matchJSON": [
+ {
+ "total acts": 0
+ },
+ {
+ "actions": [
+ {
+ "order": 1,
+ "kind": "mirred",
+ "mirred_action": "redirect",
+ "direction": "ingress",
+ "index": 1,
+ "stats": {
+ "packets": 1
+ },
+ "not_in_hw": true
+ }
+ ]
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DUMMY clsact"
+ ]
+ },
+ {
+ "id": "cf97",
+ "name": "Redirect multiport: dev1 ingress -> dummy ingress -> dummy egress (No Loop)",
+ "category": [
+ "filter",
+ "mirred"
+ ],
+ "plugins": {
+ "requires": [
+ "nsPlugin",
+ "scapyPlugin"
+ ]
+ },
+ "setup": [
+ "$TC qdisc add dev $DEV1 clsact",
+ "$TC filter add dev $DEV1 ingress protocol ip prio 10 matchall action mirred ingress redirect dev $DUMMY index 1",
+ "$TC qdisc add dev $DUMMY clsact"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY ingress protocol ip prio 11 matchall action mirred egress redirect dev $DUMMY index 2",
+ "scapy": [
+ {
+ "iface": "$DEV0",
+ "count": 1,
+ "packet": "Ether()/IP(dst='10.10.10.1', src='10.10.10.10')/ICMP()"
+ }
+ ],
+ "expExitCode": "0",
+ "verifyCmd": "$TC -j -s actions get action mirred index 1",
+ "matchJSON": [
+ {
+ "total acts": 0
+ },
+ {
+ "actions": [
+ {
+ "order": 1,
+ "kind": "mirred",
+ "mirred_action": "redirect",
+ "direction": "ingress",
+ "index": 1,
+ "stats": {
+ "packets": 1
+ },
+ "not_in_hw": true
+ }
+ ]
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DEV1 clsact",
+ "$TC qdisc del dev $DUMMY clsact"
+ ]
}
-
]
--
2.34.1
^ permalink raw reply related
* [PATCH net 9/9] selftests/tc-testing: Add netem test case exercising loops
From: Jamal Hadi Salim @ 2026-04-26 19:09 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, jiri, stephen, victor, savy,
will, xmei5, pctammela, kuniyu, toke, willemdebruijnkernel,
hxzene, Jamal Hadi Salim
In-Reply-To: <20260426190916.128489-1-jhs@mojatatu.com>
From: Victor Nogueira <victor@mojatatu.com>
Add a netem nested duplicate test case to validate that it won't
cause an infinite loop
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Acked-by: Stephen Hemminger <stephen@networkplumber.org>
Signed-off-by: Victor Nogueira <victor@mojatatu.com>
---
.../tc-testing/tc-tests/qdiscs/netem.json | 33 ++++++++++++++++++-
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json
index 3c4444961488..7c954989069d 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/netem.json
@@ -336,5 +336,36 @@
"teardown": [
"$TC qdisc del dev $DUMMY handle 1: root"
]
- }
+ },
+ {
+ "id": "8c17",
+ "name": "Test netem's recursive duplicate",
+ "category": [
+ "qdisc",
+ "netem"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "$IP link set dev $DUMMY up || true",
+ "$IP addr add 10.10.11.10/24 dev $DUMMY || true",
+ "$TC qdisc add dev $DUMMY root handle 1: netem limit 1 duplicate 100%",
+ "$TC qdisc add dev $DUMMY parent 1: handle 2: netem duplicate 100%"
+ ],
+ "cmdUnderTest": "ping -c 1 10.10.11.11 -W 0.01",
+ "expExitCode": "1",
+ "verifyCmd": "$TC -s -j qdisc ls dev $DUMMY root",
+ "matchJSON": [
+ {
+ "kind": "netem",
+ "handle": "1:",
+ "bytes": 294,
+ "packets": 3
+ }
+ ],
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root"
+ ]
+ }
]
--
2.34.1
^ permalink raw reply related
* Re: [PATCH net 4/9] net/sched: fix packet loop on netem when duplicate is on
From: William Liu @ 2026-04-26 19:43 UTC (permalink / raw)
To: Jamal Hadi Salim
Cc: netdev, davem, edumazet, kuba, pabeni, horms, jiri, stephen,
victor, savy, xmei5, pctammela, kuniyu, toke,
willemdebruijnkernel, hxzene
In-Reply-To: <20260426190916.128489-5-jhs@mojatatu.com>
Reviewed-by: William Liu <will@willsroot.io>
On Sunday, April 26th, 2026 at 7:09 PM, Jamal Hadi Salim <jhs@mojatatu.com> wrote:
> When netem duplicates a packet it re-enqueues the copy at the root qdisc.
> If another netem sits in the tree the copy can be duplicated
> again, recursing until the stack or memory is exhausted.
>
> The original duplication guard temporarily zeroed q->duplicate around
> the re-enqueue, but that does not cover all cases because it is
> per-qdisc state shared across all concurrent enqueue paths
> and is not safe without additional locking.
>
> Use the skb tc_depth field introduced in an earlier patch:
> - increment it on the duplicate before re-enqueue
> - skip duplication for any skb whose tc_depth is already non-zero.
>
> This marks the packet itself rather than mutating qdisc state,
> therefore it is safe regardless of tree topology or concurrency.
>
> Fixes: 0afb51e72855 ("[PKT_SCHED]: netem: reinsert for duplication")
> Reported-by: William Liu <will@willsroot.io>
> Reported-by: Savino Dicanosa <savy@syst3mfailure.io>
> Closes: https://lore.kernel.org/netdev/8DuRWwfqjoRDLDmBMlIfbrsZg9Gx50DHJc1ilxsEBNe2D6NMoigR_eIRIG0LOjMc3r10nUUZtArXx4oZBIdUfZQrwjcQhdinnMis_0G7VEk=@willsroot.io/
> Co-developed-by: Victor Nogueira <victor@mojatatu.com>
> Signed-off-by: Victor Nogueira <victor@mojatatu.com>
> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
> Reviewed-by: Stephen Hemminger <stephen@networkplumber.org>
> ---
> net/sched/sch_netem.c | 7 +++----
> 1 file changed, 3 insertions(+), 4 deletions(-)
>
> diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
> index 8c6dea196089..d595eb03dca8 100644
> --- a/net/sched/sch_netem.c
> +++ b/net/sched/sch_netem.c
> @@ -461,7 +461,8 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
> skb->prev = NULL;
>
> /* Random duplication */
> - if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor, &q->prng))
> + if (q->duplicate && skb->tc_depth == 0 &&
> + q->duplicate >= get_crandom(&q->dup_cor, &q->prng))
> ++count;
>
> /* Drop packet? */
> @@ -540,11 +541,9 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch,
> */
> if (skb2) {
> struct Qdisc *rootq = qdisc_root_bh(sch);
> - u32 dupsave = q->duplicate; /* prevent duplicating a dup... */
>
> - q->duplicate = 0;
> + skb2->tc_depth++; /* prevent duplicating a dup... */
> rootq->enqueue(skb2, rootq, to_free);
> - q->duplicate = dupsave;
> skb2 = NULL;
> }
>
> --
> 2.34.1
>
>
^ permalink raw reply
* Re: [RFC Patch net-next v1 3/9] r8169: add support for multi tx queues
From: Heiner Kallweit @ 2026-04-26 19:48 UTC (permalink / raw)
To: javen, nic_swsd, andrew+netdev, davem, edumazet, kuba, pabeni,
horms
Cc: netdev, linux-kernel
In-Reply-To: <20260420021957.1756-4-javen_xu@realsil.com.cn>
On 20.04.2026 04:19, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
>
> This patch support for multi tx queues. But we still use one tx queue
> here. More support will be available in rss support patch.
>
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
> ---
> drivers/net/ethernet/realtek/r8169_main.c | 278 ++++++++++++++++------
> 1 file changed, 199 insertions(+), 79 deletions(-)
>
Series is named "add RSS support". So, to reduce complexity of the series,
multi tx queue should be removed here and may come as a follow-up to
RSS support.
^ permalink raw reply
* Re: [RFC Patch net-next v1 1/9] r8169: add some register definitions
From: Heiner Kallweit @ 2026-04-26 20:12 UTC (permalink / raw)
To: javen, nic_swsd, andrew+netdev, davem, edumazet, kuba, pabeni,
horms
Cc: netdev, linux-kernel
In-Reply-To: <20260420021957.1756-2-javen_xu@realsil.com.cn>
On 20.04.2026 04:19, javen wrote:
> From: Javen Xu <javen_xu@realsil.com.cn>
>
> To support rss, this patch adds some macro definitions and register
> definitions.
>
> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn>
> ---
> drivers/net/ethernet/realtek/r8169_main.c | 75 +++++++++++++++++++++++
> 1 file changed, 75 insertions(+)
>
> diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
> index 791277e750ba..0fbec27e4a0d 100644
> --- a/drivers/net/ethernet/realtek/r8169_main.c
> +++ b/drivers/net/ethernet/realtek/r8169_main.c
> @@ -77,6 +77,23 @@
> #define R8169_RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc))
> #define R8169_TX_STOP_THRS (MAX_SKB_FRAGS + 1)
> #define R8169_TX_START_THRS (2 * R8169_TX_STOP_THRS)
> +#define R8169_MAX_RX_QUEUES 8
> +#define R8169_MAX_TX_QUEUES 1
> +#define R8169_MAX_MSIX_VEC 32
> +#define R8127_MAX_TX_QUEUES 1
> +#define R8127_MAX_RX_QUEUES 8
> +#define R8127_MAX_IRQ 32
> +#define R8127_MIN_IRQ 30
Why do you need at least 30 irq vecs?
> +#define RTL8127_RSS_KEY_SIZE 40
> +#define RSS_CPU_NUM_OFFSET 16
> +#define RSS_MASK_BITS_OFFSET 8
> +#define RTL8127_MAX_INDIRECTION_TABLE_ENTRIES 128
> +#define RXS_8125B_RSS_UDP_V4 BIT(27)
This register naming is unfortunate. What stands 8125B for, and what V4?
Does V4 stand for a global version of the Realtek RSS IP block?
Then the 8125B would be redundant.
> +#define RXS_8125_RSS_IPV4_V4 BIT(28)
> +#define RXS_8125_RSS_IPV6_V4 BIT(29)
> +#define RXS_8125_RSS_TCP_V4 BIT(30)
> +#define RTL8127_RXS_RSS_L3_TYPE_MASK_V4 (RXS_8125_RSS_IPV4_V4 | RXS_8125_RSS_IPV6_V4)
> +#define RTL8127_RXS_RSS_L4_TYPE_MASK_V4 (RXS_8125_RSS_TCP_V4 | RXS_8125B_RSS_UDP_V4)
>
> #define OCP_STD_PHY_BASE 0xa400
>
> @@ -435,6 +452,8 @@ enum rtl8125_registers {
> #define INT_CFG0_CLKREQEN BIT(3)
> IntrMask_8125 = 0x38,
> IntrStatus_8125 = 0x3c,
> + IntrMask1_8125 = 0x800,
The driver has camel case for historic reasons. Camel case shouldn't
be used for new constants. Checkpatch would have warned. There are
several other places in the series where checkpatch would complain.
Therefore, use checkpatch and fix all warnings/errors.
> + IntrStatus1_8125 = 0x802,
> INT_CFG1_8125 = 0x7a,
> LEDSEL2 = 0x84,
> LEDSEL1 = 0x86,
> @@ -444,6 +463,36 @@ enum rtl8125_registers {
> RSS_CTRL_8125 = 0x4500,
> Q_NUM_CTRL_8125 = 0x4800,
> EEE_TXIDLE_TIMER_8125 = 0x6048,
> + TNPDS_Q1_LOW = 0x2100,
> + RDSAR_Q1_LOW = 0x4000,
> + IMR_V2_SET_REG_8125 = 0x0d0c,
> + IMR_V2_CLEAR_REG_8125 = 0x0d00,
> + IMR_V4_L2_CLEAR_REG_8125 = 0x0d10,
> + ISR_V2_8125 = 0x0d04,
> + ISR_V4_L2_8125 = 0x0d14,
There are registers with at least V2, V4, V6.
And like in a previous comment: Which benefit has the
8125 here (at least if the versioning is global)?
> +};
> +
> +enum rtl8127_msix_id {
> + MSIX_ID_V4_LINKCHG = 29,
> +};
> +
> +enum rtl8127_rss_register_content {
> + RSS_CTRL_TCP_IPV4_SUPP = (1 << 0),
BIT() macro can be used. And any specific reasons (except historic ones)
why you define an enum that is never used instead of defines?
> + RSS_CTRL_IPV4_SUPP = (1 << 1),
> + RSS_CTRL_TCP_IPV6_SUPP = (1 << 2),
> + RSS_CTRL_IPV6_SUPP = (1 << 3),
> + RSS_CTRL_IPV6_EXT_SUPP = (1 << 4),
> + RSS_CTRL_TCP_IPV6_EXT_SUPP = (1 << 5),
> + RSS_CTRL_UDP_IPV4_SUPP = (1 << 11),
> + RSS_CTRL_UDP_IPV6_SUPP = (1 << 12),
> + RSS_CTRL_UDP_IPV6_EXT_SUPP = (1 << 13),
> + RSS_INDIRECTION_TBL_8125_V2 = 0x4700,
> + RSS_KEY_8125 = 0x4600,
> +};
> +
> +enum rtl8127_rss_flag {
> + RTL_8125_RSS_FLAG_HASH_UDP_IPV4 = (1 << 0),
> + RTL_8125_RSS_FLAG_HASH_UDP_IPV6 = (1 << 1),
> };
>
> #define LEDSEL_MASK_8125 0x23f
> @@ -474,6 +523,10 @@ enum rtl_register_content {
> RxRUNT = (1 << 20),
> RxCRC = (1 << 19),
>
> + RxRES_RSS = (1 << 22),
> + RxRUNT_RSS = (1 << 21),
> + RxCRC_RSS = (1 << 20),
> +
> /* ChipCmdBits */
> StopReq = 0x80,
> CmdReset = 0x10,
> @@ -576,6 +629,9 @@ enum rtl_register_content {
>
> /* magic enable v2 */
> MagicPacket_v2 = (1 << 16), /* Wake up when receives a Magic Packet */
> + ISRIMR_V6_LINKCHG = (1 << 29),
> + ISRIMR_V6_TOK_Q0 = (1 << 8),
> + ISRIMR_V6_ROK_Q0 = (1 << 0),
> };
>
> enum rtl_desc_bit {
> @@ -633,6 +689,11 @@ enum rtl_rx_desc_bit {
> #define RxProtoIP (PID1 | PID0)
> #define RxProtoMask RxProtoIP
>
> + RxUDPT_v4 = (1 << 19),
> + RxTCPT_v4 = (1 << 18),
> + RxUDPF_v4 = (1 << 16), /* UDP/IP checksum failed */
> + RxTCPF_v4 = (1 << 15), /* TCP/IP checksum failed */
> +
> IPFail = (1 << 16), /* IP checksum failed */
> UDPFail = (1 << 15), /* UDP/IP checksum failed */
> TCPFail = (1 << 14), /* TCP/IP checksum failed */
> @@ -659,6 +720,11 @@ struct RxDesc {
> __le64 addr;
> };
>
> +enum features {
> + RTL_FEATURE_MSI = (1 << 1),
> + RTL_FEATURE_MSIX = (1 << 2),
> +};
> +
> struct ring_info {
> struct sk_buff *skb;
> u32 len;
> @@ -728,6 +794,13 @@ enum rtl_dash_type {
> RTL_DASH_25_BP,
> };
>
> +enum rx_desc_ring_type {
> + RX_DESC_RING_TYPE_UNKNOWN = 0,
> + RX_DESC_RING_TYPE_DEAFULT,
> + RX_DESC_RING_TYPE_RSS,
> + RX_DESC_RING_TYPE_MAX
> +};
> +
> struct rtl8169_private {
> void __iomem *mmio_addr; /* memory map physical address */
> struct pci_dev *pci_dev;
> @@ -763,6 +836,8 @@ struct rtl8169_private {
> unsigned aspm_manageable:1;
> unsigned dash_enabled:1;
> bool sfp_mode:1;
> + bool rss_support:1;
> + bool rss_enable:1;
> dma_addr_t counters_phys_addr;
> struct rtl8169_counters *counters;
> struct rtl8169_tc_offsets tc_offset;
^ 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