* [PATCH net] net: flow_offload: protect driver_block_list in flow_block_cb_setup_simple()
@ 2026-02-08 11:00 Shigeru Yoshida
2026-02-11 12:06 ` Florian Westphal
0 siblings, 1 reply; 8+ messages in thread
From: Shigeru Yoshida @ 2026-02-08 11:00 UTC (permalink / raw)
To: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
Shigeru Yoshida
Cc: syzbot+5a66db916cdde0dbcc1c, netdev, linux-kernel,
netfilter-devel, coreteam
syzbot reported a list_del corruption in flow_block_cb_setup_simple(). [0]
flow_block_cb_setup_simple() accesses the driver_block_list (e.g.,
netdevsim's nsim_block_cb_list) without any synchronization. The
nftables offload path calls into this function via ndo_setup_tc while
holding the per-netns commit_mutex, but this mutex does not prevent
concurrent access from tasks in different network namespaces that
share the same driver_block_list, leading to list corruption:
- Task A (FLOW_BLOCK_BIND) calls list_add_tail() to insert a new
flow_block_cb into driver_block_list.
- Task B (FLOW_BLOCK_UNBIND) concurrently calls list_del() on another
flow_block_cb from the same list.
- The concurrent modifications corrupt the list pointers.
Fix this by adding a static mutex (flow_block_cb_list_lock) that
protects all driver_block_list operations within
flow_block_cb_setup_simple(). Also add a flow_block_cb_remove_driver()
helper for external callers that need to remove a block_cb from the
driver list under the same lock, and convert nft_indr_block_cleanup()
to use it.
[0]:
list_del corruption. prev->next should be ffff888028878200, but was ffffffff8e940fc0. (prev=ffffffff8e940fc0)
------------[ cut here ]------------
kernel BUG at lib/list_debug.c:64!
Oops: invalid opcode: 0000 [#1] SMP KASAN PTI
CPU: 1 UID: 0 PID: 6308 Comm: syz.3.231 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 08/18/2025
RIP: 0010:__list_del_entry_valid_or_report+0x15a/0x190 lib/list_debug.c:62
[...]
Call Trace:
<TASK>
__list_del_entry_valid include/linux/list.h:124 [inline]
__list_del_entry include/linux/list.h:215 [inline]
list_del include/linux/list.h:229 [inline]
flow_block_cb_setup_simple+0x62d/0x740 net/core/flow_offload.c:369
nft_block_offload_cmd net/netfilter/nf_tables_offload.c:397 [inline]
nft_chain_offload_cmd+0x293/0x660 net/netfilter/nf_tables_offload.c:451
nft_flow_block_chain net/netfilter/nf_tables_offload.c:471 [inline]
nft_flow_offload_chain net/netfilter/nf_tables_offload.c:513 [inline]
nft_flow_rule_offload_commit+0x40d/0x1b60 net/netfilter/nf_tables_offload.c:592
nf_tables_commit+0x675/0x8710 net/netfilter/nf_tables_api.c:10925
nfnetlink_rcv_batch net/netfilter/nfnetlink.c:576 [inline]
nfnetlink_rcv_skb_batch net/netfilter/nfnetlink.c:649 [inline]
nfnetlink_rcv+0x1ac9/0x2590 net/netfilter/nfnetlink.c:667
netlink_unicast_kernel net/netlink/af_netlink.c:1320 [inline]
netlink_unicast+0x82c/0x9e0 net/netlink/af_netlink.c:1346
netlink_sendmsg+0x805/0xb30 net/netlink/af_netlink.c:1896
sock_sendmsg_nosec net/socket.c:727 [inline]
__sock_sendmsg+0x219/0x270 net/socket.c:742
____sys_sendmsg+0x505/0x830 net/socket.c:2630
___sys_sendmsg+0x21f/0x2a0 net/socket.c:2684
__sys_sendmsg net/socket.c:2716 [inline]
__do_sys_sendmsg net/socket.c:2721 [inline]
__se_sys_sendmsg net/socket.c:2719 [inline]
__x64_sys_sendmsg+0x19b/0x260 net/socket.c:2719
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xfa/0x3b0 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Fixes: 955bcb6ea0df ("drivers: net: use flow block API")
Reported-by: syzbot+5a66db916cdde0dbcc1c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=5a66db916cdde0dbcc1c
Tested-by: syzbot+5a66db916cdde0dbcc1c@syzkaller.appspotmail.com
Signed-off-by: Shigeru Yoshida <syoshida@redhat.com>
---
include/net/flow_offload.h | 2 ++
net/core/flow_offload.c | 41 ++++++++++++++++++++++++-------
net/netfilter/nf_tables_offload.c | 2 +-
3 files changed, 35 insertions(+), 10 deletions(-)
diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h
index 596ab9791e4d..ff6d2bcb2cca 100644
--- a/include/net/flow_offload.h
+++ b/include/net/flow_offload.h
@@ -673,6 +673,8 @@ int flow_block_cb_setup_simple(struct flow_block_offload *f,
flow_setup_cb_t *cb,
void *cb_ident, void *cb_priv, bool ingress_only);
+void flow_block_cb_remove_driver(struct flow_block_cb *block_cb);
+
enum flow_cls_command {
FLOW_CLS_REPLACE,
FLOW_CLS_DESTROY,
diff --git a/net/core/flow_offload.c b/net/core/flow_offload.c
index bc5169482710..137a44af5e1c 100644
--- a/net/core/flow_offload.c
+++ b/net/core/flow_offload.c
@@ -334,6 +334,8 @@ bool flow_block_cb_is_busy(flow_setup_cb_t *cb, void *cb_ident,
}
EXPORT_SYMBOL(flow_block_cb_is_busy);
+static DEFINE_MUTEX(flow_block_cb_list_lock);
+
int flow_block_cb_setup_simple(struct flow_block_offload *f,
struct list_head *driver_block_list,
flow_setup_cb_t *cb,
@@ -341,6 +343,7 @@ int flow_block_cb_setup_simple(struct flow_block_offload *f,
bool ingress_only)
{
struct flow_block_cb *block_cb;
+ int err = 0;
if (ingress_only &&
f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
@@ -348,32 +351,52 @@ int flow_block_cb_setup_simple(struct flow_block_offload *f,
f->driver_block_list = driver_block_list;
+ mutex_lock(&flow_block_cb_list_lock);
+
switch (f->command) {
case FLOW_BLOCK_BIND:
- if (flow_block_cb_is_busy(cb, cb_ident, driver_block_list))
- return -EBUSY;
+ if (flow_block_cb_is_busy(cb, cb_ident, driver_block_list)) {
+ err = -EBUSY;
+ break;
+ }
block_cb = flow_block_cb_alloc(cb, cb_ident, cb_priv, NULL);
- if (IS_ERR(block_cb))
- return PTR_ERR(block_cb);
+ if (IS_ERR(block_cb)) {
+ err = PTR_ERR(block_cb);
+ break;
+ }
flow_block_cb_add(block_cb, f);
list_add_tail(&block_cb->driver_list, driver_block_list);
- return 0;
+ break;
case FLOW_BLOCK_UNBIND:
block_cb = flow_block_cb_lookup(f->block, cb, cb_ident);
- if (!block_cb)
- return -ENOENT;
+ if (!block_cb) {
+ err = -ENOENT;
+ break;
+ }
flow_block_cb_remove(block_cb, f);
list_del(&block_cb->driver_list);
- return 0;
+ break;
default:
- return -EOPNOTSUPP;
+ err = -EOPNOTSUPP;
+ break;
}
+
+ mutex_unlock(&flow_block_cb_list_lock);
+ return err;
}
EXPORT_SYMBOL(flow_block_cb_setup_simple);
+void flow_block_cb_remove_driver(struct flow_block_cb *block_cb)
+{
+ mutex_lock(&flow_block_cb_list_lock);
+ list_del(&block_cb->driver_list);
+ mutex_unlock(&flow_block_cb_list_lock);
+}
+EXPORT_SYMBOL(flow_block_cb_remove_driver);
+
static DEFINE_MUTEX(flow_indr_block_lock);
static LIST_HEAD(flow_block_indr_list);
static LIST_HEAD(flow_block_indr_dev_list);
diff --git a/net/netfilter/nf_tables_offload.c b/net/netfilter/nf_tables_offload.c
index fd30e205de84..d60838bceafb 100644
--- a/net/netfilter/nf_tables_offload.c
+++ b/net/netfilter/nf_tables_offload.c
@@ -414,7 +414,7 @@ static void nft_indr_block_cleanup(struct flow_block_cb *block_cb)
basechain, &extack);
nft_net = nft_pernet(net);
mutex_lock(&nft_net->commit_mutex);
- list_del(&block_cb->driver_list);
+ flow_block_cb_remove_driver(block_cb);
list_move(&block_cb->list, &bo.cb_list);
nft_flow_offload_unbind(&bo, basechain);
mutex_unlock(&nft_net->commit_mutex);
--
2.52.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH net] net: flow_offload: protect driver_block_list in flow_block_cb_setup_simple()
2026-02-08 11:00 [PATCH net] net: flow_offload: protect driver_block_list in flow_block_cb_setup_simple() Shigeru Yoshida
@ 2026-02-11 12:06 ` Florian Westphal
2026-02-13 2:34 ` Jakub Kicinski
0 siblings, 1 reply; 8+ messages in thread
From: Florian Westphal @ 2026-02-11 12:06 UTC (permalink / raw)
To: Shigeru Yoshida
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Pablo Neira Ayuso, Phil Sutter,
syzbot+5a66db916cdde0dbcc1c, netdev, linux-kernel,
netfilter-devel, coreteam
Shigeru Yoshida <syoshida@redhat.com> wrote:
> syzbot reported a list_del corruption in flow_block_cb_setup_simple(). [0]
>
> flow_block_cb_setup_simple() accesses the driver_block_list (e.g.,
> netdevsim's nsim_block_cb_list) without any synchronization. The
> nftables offload path calls into this function via ndo_setup_tc while
> holding the per-netns commit_mutex, but this mutex does not prevent
> concurrent access from tasks in different network namespaces that
> share the same driver_block_list, leading to list corruption:
>
> - Task A (FLOW_BLOCK_BIND) calls list_add_tail() to insert a new
> flow_block_cb into driver_block_list.
>
> - Task B (FLOW_BLOCK_UNBIND) concurrently calls list_del() on another
> flow_block_cb from the same list.
Looking at the *upper layer*, I don't think it expected drivers to use
a single global list for this bit something that is scoped to the
net_device.
As drivers do use shared lists everywhere I think this fix is correct, so
Acked-by: Florian Westphal <fw@strlen.de>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net] net: flow_offload: protect driver_block_list in flow_block_cb_setup_simple()
2026-02-11 12:06 ` Florian Westphal
@ 2026-02-13 2:34 ` Jakub Kicinski
2026-02-13 11:30 ` Florian Westphal
0 siblings, 1 reply; 8+ messages in thread
From: Jakub Kicinski @ 2026-02-13 2:34 UTC (permalink / raw)
To: Florian Westphal
Cc: Shigeru Yoshida, David S. Miller, Eric Dumazet, Paolo Abeni,
Simon Horman, Pablo Neira Ayuso, Phil Sutter,
syzbot+5a66db916cdde0dbcc1c, netdev, linux-kernel,
netfilter-devel, coreteam
On Wed, 11 Feb 2026 13:06:48 +0100 Florian Westphal wrote:
> Shigeru Yoshida <syoshida@redhat.com> wrote:
> > syzbot reported a list_del corruption in flow_block_cb_setup_simple(). [0]
> >
> > flow_block_cb_setup_simple() accesses the driver_block_list (e.g.,
> > netdevsim's nsim_block_cb_list) without any synchronization. The
> > nftables offload path calls into this function via ndo_setup_tc while
> > holding the per-netns commit_mutex, but this mutex does not prevent
> > concurrent access from tasks in different network namespaces that
> > share the same driver_block_list, leading to list corruption:
> >
> > - Task A (FLOW_BLOCK_BIND) calls list_add_tail() to insert a new
> > flow_block_cb into driver_block_list.
> >
> > - Task B (FLOW_BLOCK_UNBIND) concurrently calls list_del() on another
> > flow_block_cb from the same list.
>
> Looking at the *upper layer*, I don't think it expected drivers to use
> a single global list for this bit something that is scoped to the
> net_device.
Maybe subjective but the fix seems a little off to me.
Isn't flow_block_cb_setup_simple() just a "simple" implementation
for reuse in drivers locking in there doesn't really guarantee much?
If we think netdevsim is doing something odd, let's make it work
like real drivers.
TBH I thought block setup was always under rtnl_lock.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net] net: flow_offload: protect driver_block_list in flow_block_cb_setup_simple()
2026-02-13 2:34 ` Jakub Kicinski
@ 2026-02-13 11:30 ` Florian Westphal
2026-02-13 16:17 ` Jakub Kicinski
0 siblings, 1 reply; 8+ messages in thread
From: Florian Westphal @ 2026-02-13 11:30 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Shigeru Yoshida, David S. Miller, Eric Dumazet, Paolo Abeni,
Simon Horman, Pablo Neira Ayuso, Phil Sutter,
syzbot+5a66db916cdde0dbcc1c, netdev, linux-kernel,
netfilter-devel, coreteam
Jakub Kicinski <kuba@kernel.org> wrote:
> On Wed, 11 Feb 2026 13:06:48 +0100 Florian Westphal wrote:
> > Shigeru Yoshida <syoshida@redhat.com> wrote:
> > > syzbot reported a list_del corruption in flow_block_cb_setup_simple(). [0]
> > >
> > > flow_block_cb_setup_simple() accesses the driver_block_list (e.g.,
> > > netdevsim's nsim_block_cb_list) without any synchronization. The
> > > nftables offload path calls into this function via ndo_setup_tc while
> > > holding the per-netns commit_mutex, but this mutex does not prevent
> > > concurrent access from tasks in different network namespaces that
> > > share the same driver_block_list, leading to list corruption:
> > >
> > > - Task A (FLOW_BLOCK_BIND) calls list_add_tail() to insert a new
> > > flow_block_cb into driver_block_list.
> > >
> > > - Task B (FLOW_BLOCK_UNBIND) concurrently calls list_del() on another
> > > flow_block_cb from the same list.
> >
> > Looking at the *upper layer*, I don't think it expected drivers to use
> > a single global list for this bit something that is scoped to the
> > net_device.
>
> Maybe subjective but the fix seems a little off to me.
> Isn't flow_block_cb_setup_simple() just a "simple" implementation
> for reuse in drivers locking in there doesn't really guarantee much?
Not sure what you mean. I see the same pattern as netdevsim in all
drivers using this API. Random example:
static LIST_HEAD(ice_repr_block_cb_list);
[..]
return flow_block_cb_setup_simple((struct flow_block_offload *)
type_data,
&ice_repr_block_cb_list,
ice_repr_setup_tc_block_cb,
np, np, true);
This is safe only as long as all ice_repr_setup_tc() calls happen
in same net namespace. I don't think we can rely on this.
> If we think netdevsim is doing something odd, let's make it work
> like real drivers.
I fear fixing netdevsim to not use single list will resolve the
syzbot report but AFAICS this pattern is in many drivers.
> TBH I thought block setup was always under rtnl_lock.
netdevices.rst says:
"``TC_SETUP_BLOCK`` and ``TC_SETUP_FT`` are running under NFT locks
(i.e. no ``rtnl_lock`` and no device instance lock)."
I don't think it will be possible to change it.
nf_tables_netdev_event is called with rtnl_lock and it can then
take the pernet nf_tables transaction mutex.
Maybe it would be possible to rework flow_block_cb_setup_simple()
to not depend on an external list_head argument, but its not easy to
test such a patch nor do I think its going to be -net material let
alone something that -stable likes to digest.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net] net: flow_offload: protect driver_block_list in flow_block_cb_setup_simple()
2026-02-13 11:30 ` Florian Westphal
@ 2026-02-13 16:17 ` Jakub Kicinski
2026-02-15 13:06 ` Florian Westphal
0 siblings, 1 reply; 8+ messages in thread
From: Jakub Kicinski @ 2026-02-13 16:17 UTC (permalink / raw)
To: Florian Westphal
Cc: Shigeru Yoshida, David S. Miller, Eric Dumazet, Paolo Abeni,
Simon Horman, Pablo Neira Ayuso, Phil Sutter,
syzbot+5a66db916cdde0dbcc1c, netdev, linux-kernel,
netfilter-devel, coreteam
On Fri, 13 Feb 2026 12:30:58 +0100 Florian Westphal wrote:
> > > Looking at the *upper layer*, I don't think it expected drivers to use
> > > a single global list for this bit something that is scoped to the
> > > net_device.
> >
> > Maybe subjective but the fix seems a little off to me.
> > Isn't flow_block_cb_setup_simple() just a "simple" implementation
> > for reuse in drivers locking in there doesn't really guarantee much?
>
> Not sure what you mean. I see the same pattern as netdevsim in all
> drivers using this API.
Grep for flow_block_cb_add(). Not all drivers use
the flow_block_cb_setup_simple() helper, it's just a convenience helper,
not a mandatory part of the flow. We should probably add a helper for
add like the one added for flow_block_cb_remove_driver() instead of
taking the lock directly in flow_block_cb_setup_simple()?
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net] net: flow_offload: protect driver_block_list in flow_block_cb_setup_simple()
2026-02-13 16:17 ` Jakub Kicinski
@ 2026-02-15 13:06 ` Florian Westphal
2026-02-17 11:42 ` Pablo Neira Ayuso
0 siblings, 1 reply; 8+ messages in thread
From: Florian Westphal @ 2026-02-15 13:06 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Shigeru Yoshida, David S. Miller, Eric Dumazet, Paolo Abeni,
Simon Horman, Pablo Neira Ayuso, Phil Sutter,
syzbot+5a66db916cdde0dbcc1c, netdev, linux-kernel,
netfilter-devel, coreteam
Jakub Kicinski <kuba@kernel.org> wrote:
> On Fri, 13 Feb 2026 12:30:58 +0100 Florian Westphal wrote:
> > > > Looking at the *upper layer*, I don't think it expected drivers to use
> > > > a single global list for this bit something that is scoped to the
> > > > net_device.
> > >
> > > Maybe subjective but the fix seems a little off to me.
> > > Isn't flow_block_cb_setup_simple() just a "simple" implementation
> > > for reuse in drivers locking in there doesn't really guarantee much?
> >
> > Not sure what you mean. I see the same pattern as netdevsim in all
> > drivers using this API.
>
> Grep for flow_block_cb_add(). Not all drivers use
static int
mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f)
{
struct mtk_mac *mac = netdev_priv(dev);
struct mtk_eth *eth = mac->hw;
static LIST_HEAD(block_cb_list);
~~~~~~
I have a question.
[..]
f->driver_block_list = &block_cb_list;
Now I have many questions!
How is this supposed to work?
How is ownership handled, what locks protect, or what locks are supposed
to protect this?
> the flow_block_cb_setup_simple() helper, it's just a convenience helper,
> not a mandatory part of the flow. We should probably add a helper for
> add like the one added for flow_block_cb_remove_driver() instead of
> taking the lock directly in flow_block_cb_setup_simple()?
No idea, I don't understand how any of this is supposed to work.
I will try to play with this next week but I'm not sure I will send
patches. AFAICS there are not even netdevsim based tests for this
feature anywhere.
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net] net: flow_offload: protect driver_block_list in flow_block_cb_setup_simple()
2026-02-15 13:06 ` Florian Westphal
@ 2026-02-17 11:42 ` Pablo Neira Ayuso
2026-02-17 22:05 ` Jakub Kicinski
0 siblings, 1 reply; 8+ messages in thread
From: Pablo Neira Ayuso @ 2026-02-17 11:42 UTC (permalink / raw)
To: Florian Westphal
Cc: Jakub Kicinski, Shigeru Yoshida, David S. Miller, Eric Dumazet,
Paolo Abeni, Simon Horman, Phil Sutter,
syzbot+5a66db916cdde0dbcc1c, netdev, linux-kernel,
netfilter-devel, coreteam
[-- Attachment #1: Type: text/plain, Size: 1752 bytes --]
On Sun, Feb 15, 2026 at 02:06:42PM +0100, Florian Westphal wrote:
> Jakub Kicinski <kuba@kernel.org> wrote:
> > On Fri, 13 Feb 2026 12:30:58 +0100 Florian Westphal wrote:
> > > > > Looking at the *upper layer*, I don't think it expected drivers to use
> > > > > a single global list for this bit something that is scoped to the
> > > > > net_device.
> > > >
> > > > Maybe subjective but the fix seems a little off to me.
> > > > Isn't flow_block_cb_setup_simple() just a "simple" implementation
> > > > for reuse in drivers locking in there doesn't really guarantee much?
> > >
> > > Not sure what you mean. I see the same pattern as netdevsim in all
> > > drivers using this API.
> >
> > Grep for flow_block_cb_add(). Not all drivers use
>
> static int
> mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f)
> {
> struct mtk_mac *mac = netdev_priv(dev);
> struct mtk_eth *eth = mac->hw;
> static LIST_HEAD(block_cb_list);
> ~~~~~~
> I have a question.
>
> [..]
> f->driver_block_list = &block_cb_list;
>
> Now I have many questions!
>
> How is this supposed to work?
Last time I met people, I asked how is hw offload actually working
with netns (6 years ago?), someone told me: "maybe there is a driver
that supports it...". I have never seen one, but I am very much
outdates on how this has evolved TBH, I might be wrong.
I don't think any driver really supports netns + hardware offload, so
I suggest to restrict it, see attached patch.
It would be better to add a helper function such as int net_setup_tc()
for the myriad of ->ndo_setup_tc() calls in the code, then move this
check in it to consolidate, but I think you want something you can
pass to -stable at this stage?
[-- Attachment #2: restrict-hw-offload-to-init_netns.patch --]
[-- Type: text/x-diff, Size: 3332 bytes --]
diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index 99ac747b7906..a97838c1c56d 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -692,6 +692,9 @@ struct tc_cls_u32_offload {
static inline bool tc_can_offload(const struct net_device *dev)
{
+ if (!net_eq(dev_net(dev), &init_net))
+ return false;
+
return dev->features & NETIF_F_HW_TC;
}
diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
index b1966b68c48a..c6d426052765 100644
--- a/net/netfilter/nf_flow_table_offload.c
+++ b/net/netfilter/nf_flow_table_offload.c
@@ -1173,6 +1173,9 @@ static int nf_flow_table_offload_cmd(struct flow_block_offload *bo,
{
int err;
+ if (!net_eq(dev_net(dev), &init_net))
+ return -EOPNOTSUPP;
+
nf_flow_table_block_offload_init(bo, dev_net(dev), cmd, flowtable,
extack);
down_write(&flowtable->flow_block_lock);
diff --git a/net/netfilter/nf_tables_offload.c b/net/netfilter/nf_tables_offload.c
index fd30e205de84..048fa5f356d9 100644
--- a/net/netfilter/nf_tables_offload.c
+++ b/net/netfilter/nf_tables_offload.c
@@ -233,6 +233,9 @@ bool nft_chain_offload_support(const struct nft_base_chain *basechain)
ops->hooknum != NF_NETDEV_INGRESS)
return false;
+ if (!net_eq(dev_net(dev), &init_net))
+ return false;
+
dev = ops->dev;
if (!dev->netdev_ops->ndo_setup_tc &&
!flow_indr_dev_exists())
diff --git a/net/sched/sch_cbs.c b/net/sched/sch_cbs.c
index 8c9a0400c862..6daa9f702a59 100644
--- a/net/sched/sch_cbs.c
+++ b/net/sched/sch_cbs.c
@@ -281,7 +281,7 @@ static int cbs_enable_offload(struct net_device *dev, struct cbs_sched_data *q,
struct tc_cbs_qopt_offload cbs = { };
int err;
- if (!ops->ndo_setup_tc) {
+ if (!tc_can_offload(dev) || !ops->ndo_setup_tc) {
NL_SET_ERR_MSG(extack, "Specified device does not support cbs offload");
return -EOPNOTSUPP;
}
diff --git a/net/sched/sch_etf.c b/net/sched/sch_etf.c
index c74d778c32a1..7801e009e025 100644
--- a/net/sched/sch_etf.c
+++ b/net/sched/sch_etf.c
@@ -323,7 +323,7 @@ static int etf_enable_offload(struct net_device *dev, struct etf_sched_data *q,
struct tc_etf_qopt_offload etf = { };
int err;
- if (!ops->ndo_setup_tc) {
+ if (!tc_can_offload(dev) || !ops->ndo_setup_tc) {
NL_SET_ERR_MSG(extack, "Specified device does not support ETF offload");
return -EOPNOTSUPP;
}
diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c
index f3e5ef9a9592..d853f3b60121 100644
--- a/net/sched/sch_mqprio.c
+++ b/net/sched/sch_mqprio.c
@@ -410,7 +410,7 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt,
* the queue mapping then run ndo_setup_tc otherwise use the
* supplied and verified mapping
*/
- if (qopt->hw) {
+ if (qopt->hw && tc_can_offload(dev)) {
err = mqprio_enable_offload(sch, qopt, extack);
if (err)
return err;
diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
index 300d577b3286..7451d74af91f 100644
--- a/net/sched/sch_taprio.c
+++ b/net/sched/sch_taprio.c
@@ -1522,7 +1522,7 @@ static int taprio_enable_offload(struct net_device *dev,
struct tc_taprio_caps caps;
int tc, err = 0;
- if (!ops->ndo_setup_tc) {
+ if (!tc_can_offload(dev) || !ops->ndo_setup_tc) {
NL_SET_ERR_MSG(extack,
"Device does not support taprio offload");
return -EOPNOTSUPP;
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH net] net: flow_offload: protect driver_block_list in flow_block_cb_setup_simple()
2026-02-17 11:42 ` Pablo Neira Ayuso
@ 2026-02-17 22:05 ` Jakub Kicinski
0 siblings, 0 replies; 8+ messages in thread
From: Jakub Kicinski @ 2026-02-17 22:05 UTC (permalink / raw)
To: Pablo Neira Ayuso
Cc: Florian Westphal, Shigeru Yoshida, David S. Miller, Eric Dumazet,
Paolo Abeni, Simon Horman, Phil Sutter,
syzbot+5a66db916cdde0dbcc1c, netdev, linux-kernel,
netfilter-devel, coreteam
On Tue, 17 Feb 2026 12:42:13 +0100 Pablo Neira Ayuso wrote:
> > static int
> > mtk_eth_setup_tc_block(struct net_device *dev, struct flow_block_offload *f)
> > {
> > struct mtk_mac *mac = netdev_priv(dev);
> > struct mtk_eth *eth = mac->hw;
> > static LIST_HEAD(block_cb_list);
> > ~~~~~~
> > I have a question.
> >
> > [..]
> > f->driver_block_list = &block_cb_list;
> >
> > Now I have many questions!
> >
> > How is this supposed to work?
>
> Last time I met people, I asked how is hw offload actually working
> with netns (6 years ago?), someone told me: "maybe there is a driver
> that supports it...". I have never seen one, but I am very much
> outdates on how this has evolved TBH, I might be wrong.
>
> I don't think any driver really supports netns + hardware offload, so
> I suggest to restrict it, see attached patch.
>
> It would be better to add a helper function such as int net_setup_tc()
> for the myriad of ->ndo_setup_tc() calls in the code, then move this
> check in it to consolidate, but I think you want something you can
> pass to -stable at this stage?
I don't think we need this restriction for TC side, it should be under
rtnl_lock, IIUC. I have a... strong suspicion(?) that TC works, some
drivers explicitly support devlink reload into a specified netns.
I always assumed this is for the cases where something like OvS offload
is supposed to be handled outside init_net to avoid netdev explosion.
I could be wrong, tho, could be an RDMA thing.
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-02-17 22:05 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-08 11:00 [PATCH net] net: flow_offload: protect driver_block_list in flow_block_cb_setup_simple() Shigeru Yoshida
2026-02-11 12:06 ` Florian Westphal
2026-02-13 2:34 ` Jakub Kicinski
2026-02-13 11:30 ` Florian Westphal
2026-02-13 16:17 ` Jakub Kicinski
2026-02-15 13:06 ` Florian Westphal
2026-02-17 11:42 ` Pablo Neira Ayuso
2026-02-17 22:05 ` Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox