* [PATCH nf-next v5 0/6] Add IPv4 over IPv6 and SIT flowtable SW acceleration
From: Lorenzo Bianconi @ 2026-07-09 8:52 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Felix Fietkau, Matthias Brugger,
AngeloGioacchino Del Regno, Simon Horman, David Ahern,
Ido Schimmel, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
Shuah Khan, Lorenzo Bianconi
Cc: linux-arm-kernel, linux-mediatek, netdev, netfilter-devel,
coreteam, linux-kselftest
Similar to IPIP and IP6I6 tunnels, introduce sw acceleration for IPv4 over
IPv6 and SIT tunnels in the netfilter flowtable infrastructure.
---
Changes in v5:
- Fix ipip6_tunnel_fill_forward_path() to take into account not only
IPv6 packet since SIT tunnels can encapsulate even IPv4/MPLS traffic.
- Return an error in ipip6_tunnel_fill_forward_path() if tunnel daddr is
not set since NBMA tunnels are not supported yet.
- Return an error in ipip6_tunnel_fill_forward_path() if encap.type is
not TUNNEL_ENCAP_NONE
- cosmetics
- Link to v4: https://lore.kernel.org/r/20260703-b4-flowtable-sw-accel-ip6ip-v4-0-00398cd12382@kernel.org
Changes in v4:
- Rebase on top of nf-next and fixed conflicts.
- Link to v3: https://lore.kernel.org/r/20260531-b4-flowtable-sw-accel-ip6ip-v3-0-56a2826f3279@kernel.org
Changes in v3:
- Drop nf_flow_tunnel_v4_push and nf_flow_tunnel_v6_push routines
- Rebase on top of net-next tree.
- Link to v2: https://lore.kernel.org/r/20260506-b4-flowtable-sw-accel-ip6ip-v2-0-439fd427726e@kernel.org
Changes in v2:
- Fix MTU check in nf_flow_offload_forward() and in
nf_flow_offload_ipv6_forward()
- Add SIT sw acceleration support
- Link to v1: https://lore.kernel.org/r/20260505-b4-flowtable-sw-accel-ip6ip-v1-0-9ac39ccc9ea9@kernel.org
---
Lorenzo Bianconi (6):
net: netfilter: add ether_type to net_device_path_ctx
net: netfilter: add encap_proto to flow_offload_tunnel
net: netfilter: add IPv4 over IPv6 tunnel flowtable acceleration
selftests: netfilter: nft_flowtable.sh: add IPv4 over IPv6 flowtable selftest
net: netfilter: add SIT tunnel flowtable acceleration
selftests: netfilter: nft_flowtable.sh: add SIT flowtable selftest
drivers/net/ethernet/airoha/airoha_ppe.c | 13 +-
drivers/net/ethernet/mediatek/mtk_ppe_offload.c | 13 +-
include/linux/netdevice.h | 5 +-
include/net/netfilter/nf_flow_table.h | 1 +
net/core/dev.c | 6 +-
net/ipv4/ipip.c | 1 +
net/ipv6/ip6_tunnel.c | 6 +-
net/ipv6/sit.c | 51 +++
net/netfilter/nf_flow_table_core.c | 16 +-
net/netfilter/nf_flow_table_ip.c | 411 +++++++++++++--------
net/netfilter/nf_flow_table_path.c | 20 +-
tools/testing/selftests/net/netfilter/config | 1 +
.../selftests/net/netfilter/nft_flowtable.sh | 78 +++-
13 files changed, 434 insertions(+), 188 deletions(-)
---
base-commit: a88e11651a59b616a8e614e178f68cd730eed0fe
change-id: 20260505-b4-flowtable-sw-accel-ip6ip-7101034cd147
Best regards,
--
Lorenzo Bianconi <lorenzo@kernel.org>
^ permalink raw reply
* [PATCH nf-next v5 1/6] net: netfilter: add ether_type to net_device_path_ctx
From: Lorenzo Bianconi @ 2026-07-09 8:52 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Felix Fietkau, Matthias Brugger,
AngeloGioacchino Del Regno, Simon Horman, David Ahern,
Ido Schimmel, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
Shuah Khan, Lorenzo Bianconi
Cc: linux-arm-kernel, linux-mediatek, netdev, netfilter-devel,
coreteam, linux-kselftest
In-Reply-To: <20260709-b4-flowtable-sw-accel-ip6ip-v5-0-828ceaf85bab@kernel.org>
Add an ether_type field to struct net_device_path_ctx to allow IPv6
tunnel drivers to select the appropriate L3 protocol based on the
encapsulated traffic.
Update the airoha and mtk Ethernet drivers to use the new
dev_fill_forward_path() signature.
This is a preliminary patch to enable sw flowtable acceleration for
IPv4 over IPv6 tunnels.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
drivers/net/ethernet/airoha/airoha_ppe.c | 13 ++++++++-----
drivers/net/ethernet/mediatek/mtk_ppe_offload.c | 13 ++++++++-----
include/linux/netdevice.h | 4 +++-
net/core/dev.c | 6 ++++--
net/ipv6/ip6_tunnel.c | 5 ++++-
net/netfilter/nf_flow_table_path.c | 8 +++++---
6 files changed, 32 insertions(+), 17 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index e7c78293002a..06d128c67c8c 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -276,7 +276,8 @@ static int airoha_ppe_flow_mangle_ipv4(const struct flow_action_entry *act,
return 0;
}
-static int airoha_ppe_get_wdma_info(struct net_device *dev, const u8 *addr,
+static int airoha_ppe_get_wdma_info(struct net_device *dev,
+ const u8 *addr, __be16 ether_type,
struct airoha_wdma_info *info)
{
struct net_device_path_stack stack;
@@ -287,7 +288,7 @@ static int airoha_ppe_get_wdma_info(struct net_device *dev, const u8 *addr,
return -ENODEV;
rcu_read_lock();
- err = dev_fill_forward_path(dev, addr, &stack);
+ err = dev_fill_forward_path(dev, addr, ether_type, &stack);
rcu_read_unlock();
if (err)
return err;
@@ -331,7 +332,7 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
struct airoha_foe_entry *hwe,
struct net_device *netdev, int type,
struct airoha_flow_data *data,
- int l4proto)
+ __be16 ether_type, int l4proto)
{
u32 qdata = FIELD_PREP(AIROHA_FOE_SHAPER_ID, 0x7f), ports_pad, val;
int wlan_etype = -EINVAL, dsa_port = airoha_get_dsa_port(&netdev);
@@ -354,7 +355,7 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
struct airoha_wdma_info info = {};
if (!airoha_ppe_get_wdma_info(netdev, data->eth.h_dest,
- &info)) {
+ ether_type, &info)) {
val |= FIELD_PREP(AIROHA_FOE_IB2_NBQ, info.idx) |
FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT,
FE_PSE_PORT_CDM4);
@@ -1081,6 +1082,7 @@ static int airoha_ppe_flow_offload_replace(struct airoha_eth *eth,
struct flow_action_entry *act;
struct airoha_foe_entry hwe;
int err, i, offload_type;
+ __be16 ether_type = 0;
u16 addr_type = 0;
u8 l4proto = 0;
@@ -1107,6 +1109,7 @@ static int airoha_ppe_flow_offload_replace(struct airoha_eth *eth,
struct flow_match_basic match;
flow_rule_match_basic(rule, &match);
+ ether_type = match.key->n_proto;
l4proto = match.key->ip_proto;
} else {
return -EOPNOTSUPP;
@@ -1177,7 +1180,7 @@ static int airoha_ppe_flow_offload_replace(struct airoha_eth *eth,
return -EINVAL;
err = airoha_ppe_foe_entry_prepare(eth, &hwe, odev, offload_type,
- &data, l4proto);
+ &data, ether_type, l4proto);
if (err)
return err;
diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
index cc8c4ef8038f..2601c17b29c8 100644
--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
@@ -89,7 +89,8 @@ mtk_flow_offload_mangle_eth(const struct flow_action_entry *act, void *eth)
}
static int
-mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_info *info)
+mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr,
+ __be16 ether_type, struct mtk_wdma_info *info)
{
struct net_device_path_stack stack;
struct net_device_path *path;
@@ -102,7 +103,7 @@ mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_i
return -1;
rcu_read_lock();
- err = dev_fill_forward_path(dev, addr, &stack);
+ err = dev_fill_forward_path(dev, addr, ether_type, &stack);
rcu_read_unlock();
if (err)
return err;
@@ -190,12 +191,12 @@ mtk_flow_get_dsa_port(struct net_device **dev)
static int
mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe,
struct net_device *dev, const u8 *dest_mac,
- int *wed_index)
+ __be16 ether_type, int *wed_index)
{
struct mtk_wdma_info info = {};
int pse_port, dsa_port, queue;
- if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) {
+ if (mtk_flow_get_wdma_info(dev, dest_mac, ether_type, &info) == 0) {
mtk_foe_entry_set_wdma(eth, foe, info.wdma_idx, info.queue,
info.bss, info.wcid, info.amsdu);
if (mtk_is_netsys_v2_or_greater(eth)) {
@@ -273,6 +274,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f,
struct mtk_flow_data data = {};
struct mtk_foe_entry foe;
struct mtk_flow_entry *entry;
+ __be16 ether_type = 0;
int offload_type = 0;
int wed_index = -1;
u16 addr_type = 0;
@@ -319,6 +321,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f,
struct flow_match_basic match;
flow_rule_match_basic(rule, &match);
+ ether_type = match.key->n_proto;
l4proto = match.key->ip_proto;
} else {
return -EOPNOTSUPP;
@@ -481,7 +484,7 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f,
mtk_foe_entry_set_pppoe(eth, &foe, data.pppoe.sid);
err = mtk_flow_set_output_device(eth, &foe, odev, data.eth.h_dest,
- &wed_index);
+ ether_type, &wed_index);
if (err)
return err;
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 9981d637f8b5..a97aee0e49b2 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -938,6 +938,7 @@ struct net_device_path_stack {
struct net_device_path_ctx {
const struct net_device *dev;
u8 daddr[ETH_ALEN];
+ __be16 ether_type;
int num_vlans;
struct {
@@ -3420,7 +3421,8 @@ void dev_remove_offload(struct packet_offload *po);
int dev_get_iflink(const struct net_device *dev);
int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
-int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
+int dev_fill_forward_path(const struct net_device *dev,
+ const u8 *daddr, __be16 ether_type,
struct net_device_path_stack *stack);
struct net_device *dev_get_by_name(struct net *net, const char *name);
struct net_device *dev_get_by_name_rcu(struct net *net, const char *name);
diff --git a/net/core/dev.c b/net/core/dev.c
index 714d05283500..b0e2de0e95f8 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -750,12 +750,14 @@ static struct net_device_path *dev_fwd_path(struct net_device_path_stack *stack)
return &stack->path[k];
}
-int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
+int dev_fill_forward_path(const struct net_device *dev,
+ const u8 *daddr, __be16 ether_type,
struct net_device_path_stack *stack)
{
const struct net_device *last_dev;
struct net_device_path_ctx ctx = {
- .dev = dev,
+ .dev = dev,
+ .ether_type = ether_type,
};
struct net_device_path *path;
int ret = 0;
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index bf8e40af60b0..38da07101601 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1863,7 +1863,10 @@ static int ip6_tnl_fill_forward_path(struct net_device_path_ctx *ctx,
path->type = DEV_PATH_TUN;
path->tun.src_v6 = t->parms.laddr;
path->tun.dst_v6 = t->parms.raddr;
- path->tun.l3_proto = IPPROTO_IPV6;
+ if (ctx->ether_type == cpu_to_be16(ETH_P_IP))
+ path->tun.l3_proto = IPPROTO_IPIP;
+ else
+ path->tun.l3_proto = IPPROTO_IPV6;
path->dev = ctx->dev;
ctx->dev = dst->dev;
}
diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c
index 98c03b487f52..c8011ec36532 100644
--- a/net/netfilter/nf_flow_table_path.c
+++ b/net/netfilter/nf_flow_table_path.c
@@ -45,7 +45,8 @@ static bool nft_is_valid_ether_device(const struct net_device *dev)
static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
const struct dst_entry *dst_cache,
const struct nf_conn *ct,
- enum ip_conntrack_dir dir, u8 *ha,
+ enum ip_conntrack_dir dir,
+ u8 *ha, __be16 ether_type,
struct net_device_path_stack *stack)
{
const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
@@ -72,7 +73,7 @@ static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
return -1;
out:
- return dev_fill_forward_path(dev, ha, stack);
+ return dev_fill_forward_path(dev, ha, ether_type, stack);
}
struct nft_forward_info {
@@ -255,7 +256,8 @@ static int nft_dev_forward_path(const struct nft_pktinfo *pkt,
unsigned char ha[ETH_ALEN];
int i;
- if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) < 0 ||
+ if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, pkt->ethertype,
+ &stack) < 0 ||
nft_dev_path_info(&stack, &info, ha, &ft->data) < 0)
return -ENOENT;
--
2.55.0
^ permalink raw reply related
* [PATCH nf-next v5 2/6] net: netfilter: add encap_proto to flow_offload_tunnel
From: Lorenzo Bianconi @ 2026-07-09 8:52 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Felix Fietkau, Matthias Brugger,
AngeloGioacchino Del Regno, Simon Horman, David Ahern,
Ido Schimmel, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
Shuah Khan, Lorenzo Bianconi
Cc: linux-arm-kernel, linux-mediatek, netdev, netfilter-devel,
coreteam, linux-kselftest
In-Reply-To: <20260709-b4-flowtable-sw-accel-ip6ip-v5-0-828ceaf85bab@kernel.org>
Add encap_proto (AF_INET or AF_INET6) to struct flow_offload_tunnel
to allow its use as part of the hash table key during flowtable entry
lookup.
This is a preliminary change to support IPv4 over IPv6 tunneling via
the flowtable infrastructure for software acceleration.
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
include/linux/netdevice.h | 1 +
include/net/netfilter/nf_flow_table.h | 1 +
net/ipv4/ipip.c | 1 +
net/ipv6/ip6_tunnel.c | 1 +
net/netfilter/nf_flow_table_ip.c | 2 ++
net/netfilter/nf_flow_table_path.c | 2 ++
6 files changed, 8 insertions(+)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index a97aee0e49b2..45d99e11b06e 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -902,6 +902,7 @@ struct net_device_path {
};
u8 l3_proto;
+ u8 encap_proto;
} tun;
struct {
enum {
diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
index 7b23b245a5a8..4d406801ec90 100644
--- a/include/net/netfilter/nf_flow_table.h
+++ b/include/net/netfilter/nf_flow_table.h
@@ -118,6 +118,7 @@ struct flow_offload_tunnel {
};
u8 l3_proto;
+ u8 encap_proto;
};
struct flow_offload_tuple {
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index d1aa048a6099..4d2195b4ae3e 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -370,6 +370,7 @@ static int ipip_fill_forward_path(struct net_device_path_ctx *ctx,
path->tun.src_v4.s_addr = tiph->saddr;
path->tun.dst_v4.s_addr = tiph->daddr;
path->tun.l3_proto = IPPROTO_IPIP;
+ path->tun.encap_proto = AF_INET;
path->dev = ctx->dev;
ctx->dev = rt->dst.dev;
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 38da07101601..a121f715afd2 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1863,6 +1863,7 @@ static int ip6_tnl_fill_forward_path(struct net_device_path_ctx *ctx,
path->type = DEV_PATH_TUN;
path->tun.src_v6 = t->parms.laddr;
path->tun.dst_v6 = t->parms.raddr;
+ path->tun.encap_proto = AF_INET6;
if (ctx->ether_type == cpu_to_be16(ETH_P_IP))
path->tun.l3_proto = IPPROTO_IPIP;
else
diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
index 29e93ac1e2e4..cf2c74e3fd56 100644
--- a/net/netfilter/nf_flow_table_ip.c
+++ b/net/netfilter/nf_flow_table_ip.c
@@ -198,6 +198,7 @@ static void nf_flow_tuple_encap(struct nf_flowtable_ctx *ctx,
tuple->tun.dst_v4.s_addr = iph->daddr;
tuple->tun.src_v4.s_addr = iph->saddr;
tuple->tun.l3_proto = IPPROTO_IPIP;
+ tuple->tun.encap_proto = AF_INET;
}
break;
case htons(ETH_P_IPV6):
@@ -206,6 +207,7 @@ static void nf_flow_tuple_encap(struct nf_flowtable_ctx *ctx,
tuple->tun.dst_v6 = ip6h->daddr;
tuple->tun.src_v6 = ip6h->saddr;
tuple->tun.l3_proto = IPPROTO_IPV6;
+ tuple->tun.encap_proto = AF_INET6;
}
break;
default:
diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c
index c8011ec36532..caaf48c5fd2a 100644
--- a/net/netfilter/nf_flow_table_path.c
+++ b/net/netfilter/nf_flow_table_path.c
@@ -129,6 +129,7 @@ static int nft_dev_path_info(const struct net_device_path_stack *stack,
info->tun.src_v6 = path->tun.src_v6;
info->tun.dst_v6 = path->tun.dst_v6;
info->tun.l3_proto = path->tun.l3_proto;
+ info->tun.encap_proto = path->tun.encap_proto;
info->num_tuns++;
} else {
if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX)
@@ -278,6 +279,7 @@ static int nft_dev_forward_path(const struct nft_pktinfo *pkt,
route->tuple[!dir].in.tun.src_v6 = info.tun.dst_v6;
route->tuple[!dir].in.tun.dst_v6 = info.tun.src_v6;
route->tuple[!dir].in.tun.l3_proto = info.tun.l3_proto;
+ route->tuple[!dir].in.tun.encap_proto = info.tun.encap_proto;
route->tuple[!dir].in.num_tuns = info.num_tuns;
}
--
2.55.0
^ permalink raw reply related
* [PATCH nf-next v5 3/6] net: netfilter: add IPv4 over IPv6 tunnel flowtable acceleration
From: Lorenzo Bianconi @ 2026-07-09 8:52 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Felix Fietkau, Matthias Brugger,
AngeloGioacchino Del Regno, Simon Horman, David Ahern,
Ido Schimmel, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
Shuah Khan, Lorenzo Bianconi
Cc: linux-arm-kernel, linux-mediatek, netdev, netfilter-devel,
coreteam, linux-kselftest
In-Reply-To: <20260709-b4-flowtable-sw-accel-ip6ip-v5-0-828ceaf85bab@kernel.org>
Introduce sw flowtable acceleration for the TX/RX paths of
IPv4 over IPv6 tunnels, relying on the netfilter flowtable
infrastructure.
The feature can be tested with a forwarding scenario between two
NICs (eth0 and eth1), where an IPv4 over IPv6 tunnel is used to
reach a remote site via eth1 as the underlay device:
ETH0 -- TUN0 <==> ETH1 -- [IP network] -- TUN1 (2001:db8:2::2)
[IP configuration]
6: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 00:00:22:33:11:55 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.2/24 scope global eth0
valid_lft forever preferred_lft forever
7: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 00:11:22:33:11:55 brd ff:ff:ff:ff:ff:ff
inet6 2001:db8:2::1/64 scope global nodad
valid_lft forever preferred_lft forever
8: tun0@NONE: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN group default qlen 1000
link/tunnel6 2001:db8:2::1 peer 2001:db8:2::2 permaddr ce9c:2940:7dcc::
inet 192.168.100.1/24 scope global tun0
valid_lft forever preferred_lft forever
$ ip route show
default via 192.168.100.2 dev tun0
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.2
192.168.100.0/24 dev tun0 proto kernel scope link src 192.168.100.1
$ ip -6 route show
2001:db8:2::/64 dev eth1 proto kernel metric 256 pref medium
$ nft list ruleset
table inet filter {
flowtable ft {
hook ingress priority filter
devices = { eth0, eth1 }
}
chain forward {
type filter hook forward priority filter; policy accept;
meta l4proto { tcp, udp } flow add @ft
}
}
When reproducing this scenario using veth interfaces, the following
results were observed:
- TCP stream received from IPv4 over IPv6 tunnel:
- net-next (baseline): ~126 Gbps
- net-next + IP6IP flowtable support: ~138 Gbps
- TCP stream transmitted to IPv4 over IPv6 tunnel:
- net-next (baseline): ~127 Gbps
- net-next + IP6IP flowtable support: ~140 Gbps
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
net/netfilter/nf_flow_table_core.c | 16 +++-
net/netfilter/nf_flow_table_ip.c | 177 +++++++++++++++++++++++++------------
net/netfilter/nf_flow_table_path.c | 10 ++-
3 files changed, 137 insertions(+), 66 deletions(-)
diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
index 99c5b9d671a0..18f89e6fb435 100644
--- a/net/netfilter/nf_flow_table_core.c
+++ b/net/netfilter/nf_flow_table_core.c
@@ -76,9 +76,14 @@ struct flow_offload *flow_offload_alloc(struct nf_conn *ct)
}
EXPORT_SYMBOL_GPL(flow_offload_alloc);
-static u32 flow_offload_dst_cookie(struct flow_offload_tuple *flow_tuple)
+static u32 flow_offload_dst_cookie(struct flow_offload_tuple *flow_tuple,
+ u8 tun_encap_proto)
{
- if (flow_tuple->l3proto == NFPROTO_IPV6)
+ bool dst_v6;
+
+ dst_v6 = tun_encap_proto ? tun_encap_proto == NFPROTO_IPV6
+ : flow_tuple->l3proto == NFPROTO_IPV6;
+ if (dst_v6)
return rt6_get_cookie(dst_rt6_info(flow_tuple->dst_cache));
return 0;
@@ -99,10 +104,12 @@ static int flow_offload_fill_route(struct flow_offload *flow,
enum flow_offload_tuple_dir dir)
{
struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
+ u8 l3proto, encap_proto = route->tuple[!dir].in.tun.encap_proto;
struct dst_entry *dst = nft_route_dst_fetch(route, dir);
int i, j = 0;
- switch (flow_tuple->l3proto) {
+ l3proto = encap_proto ? encap_proto : flow_tuple->l3proto;
+ switch (l3proto) {
case NFPROTO_IPV4:
flow_tuple->mtu = ip_dst_mtu_maybe_forward(dst, true);
break;
@@ -138,7 +145,8 @@ static int flow_offload_fill_route(struct flow_offload *flow,
case FLOW_OFFLOAD_XMIT_NEIGH:
flow_tuple->ifidx = route->tuple[dir].out.ifindex;
flow_tuple->dst_cache = dst;
- flow_tuple->dst_cookie = flow_offload_dst_cookie(flow_tuple);
+ flow_tuple->dst_cookie = flow_offload_dst_cookie(flow_tuple,
+ encap_proto);
break;
default:
WARN_ON_ONCE(1);
diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
index cf2c74e3fd56..26a50e92459d 100644
--- a/net/netfilter/nf_flow_table_ip.c
+++ b/net/netfilter/nf_flow_table_ip.c
@@ -191,27 +191,27 @@ static void nf_flow_tuple_encap(struct nf_flowtable_ctx *ctx,
break;
}
- switch (inner_proto) {
- case htons(ETH_P_IP):
- iph = (struct iphdr *)(skb_network_header(skb) + offset);
- if (ctx->tun.proto == IPPROTO_IPIP) {
+ if (ctx->tun.proto == IPPROTO_IPIP || ctx->tun.proto == IPPROTO_IPV6) {
+ switch (inner_proto) {
+ case htons(ETH_P_IP):
+ iph = (struct iphdr *)(skb_network_header(skb) +
+ offset);
tuple->tun.dst_v4.s_addr = iph->daddr;
tuple->tun.src_v4.s_addr = iph->saddr;
- tuple->tun.l3_proto = IPPROTO_IPIP;
+ tuple->tun.l3_proto = ctx->tun.proto;
tuple->tun.encap_proto = AF_INET;
- }
- break;
- case htons(ETH_P_IPV6):
- ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);
- if (ctx->tun.proto == IPPROTO_IPV6) {
+ break;
+ case htons(ETH_P_IPV6):
+ ip6h = (struct ipv6hdr *)(skb_network_header(skb) +
+ offset);
tuple->tun.dst_v6 = ip6h->daddr;
tuple->tun.src_v6 = ip6h->saddr;
- tuple->tun.l3_proto = IPPROTO_IPV6;
+ tuple->tun.l3_proto = ctx->tun.proto;
tuple->tun.encap_proto = AF_INET6;
+ break;
+ default:
+ break;
}
- break;
- default:
- break;
}
}
@@ -363,7 +363,7 @@ static bool nf_flow_ip6_tunnel_proto(struct nf_flowtable_ctx *ctx,
if (ipv6_ext_hdr(ip6h->nexthdr))
return false;
- if (ip6h->nexthdr == IPPROTO_IPV6) {
+ if (ip6h->nexthdr == IPPROTO_IPIP || ip6h->nexthdr == IPPROTO_IPV6) {
ctx->tun.proto = ip6h->nexthdr;
ctx->tun.hdr_size = sizeof(*ip6h);
ctx->offset += ctx->tun.hdr_size;
@@ -384,6 +384,10 @@ static void nf_flow_ip_tunnel_pop(struct nf_flowtable_ctx *ctx,
skb_pull(skb, ctx->tun.hdr_size);
skb_reset_network_header(skb);
+ if (ctx->tun.proto == IPPROTO_IPIP)
+ skb->protocol = htons(ETH_P_IP);
+ else
+ skb->protocol = htons(ETH_P_IPV6);
}
static bool nf_flow_skb_encap_protocol(struct nf_flowtable_ctx *ctx,
@@ -489,8 +493,16 @@ static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx,
flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
mtu = flow->tuplehash[dir].tuple.mtu + ctx->offset;
- if (flow->tuplehash[!dir].tuple.tun_num)
+ switch (flow->tuplehash[!dir].tuple.tun.encap_proto) {
+ case AF_INET:
mtu -= sizeof(*iph);
+ break;
+ case AF_INET6:
+ mtu -= sizeof(struct ipv6hdr);
+ break;
+ default:
+ break;
+ }
if (unlikely(nf_flow_exceeds_mtu(skb, mtu)))
return 0;
@@ -617,6 +629,7 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
/* Push down and install the IP header. */
skb_push(skb, sizeof(*iph));
skb_reset_network_header(skb);
+ skb->protocol = htons(ETH_P_IP);
iph = ip_hdr(skb);
iph->version = 4;
@@ -636,56 +649,57 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
return 0;
}
-static int nf_flow_tunnel_v4_push(struct net *net, struct sk_buff *skb,
- struct flow_offload_tuple *tuple,
- __be32 *ip_daddr)
-{
- if (tuple->tun_num)
- return nf_flow_tunnel_ipip_push(net, skb, tuple, ip_daddr);
-
- return 0;
-}
-
-static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb,
- struct flow_offload_tuple *tuple,
- struct in6_addr **ip6_daddr)
+static int nf_flow_tunnel_ip6_push(struct net *net, struct sk_buff *skb,
+ struct flow_offload_tuple *tuple,
+ struct in6_addr **ip6_daddr)
{
- struct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb);
- struct rtable *rt = dst_rtable(tuple->dst_cache);
- __u8 dsfield = ipv6_get_dsfield(ip6h);
+ struct dst_entry *dst = tuple->dst_cache;
struct flowi6 fl6 = {
.daddr = tuple->tun.src_v6,
.saddr = tuple->tun.dst_v6,
.flowi6_proto = IPPROTO_IPV6,
};
- u8 hop_limit = ip6h->hop_limit;
+ u8 hop_limit, dsfield;
+ struct ipv6hdr *ip6h;
int err, mtu;
u32 headroom;
+ if (tuple->tun.l3_proto == IPPROTO_IPIP) {
+ struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
+
+ dsfield = ipv4_get_dsfield(iph);
+ hop_limit = iph->ttl;
+ } else {
+ ip6h = (struct ipv6hdr *)skb_network_header(skb);
+ dsfield = ipv6_get_dsfield(ip6h);
+ hop_limit = ip6h->hop_limit;
+ }
+
err = iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6);
if (err)
return err;
- skb_set_inner_ipproto(skb, IPPROTO_IPV6);
- headroom = sizeof(*ip6h) + LL_RESERVED_SPACE(rt->dst.dev) +
- rt->dst.header_len;
+ skb_set_inner_ipproto(skb, tuple->tun.l3_proto);
+ headroom = sizeof(*ip6h) + LL_RESERVED_SPACE(dst->dev) +
+ dst->header_len;
err = skb_cow_head(skb, headroom);
if (err)
return err;
skb_scrub_packet(skb, true);
- mtu = dst_mtu(&rt->dst) - sizeof(*ip6h);
+ mtu = dst_mtu(dst) - sizeof(*ip6h);
mtu = max(mtu, IPV6_MIN_MTU);
skb_dst_update_pmtu_no_confirm(skb, mtu);
skb_push(skb, sizeof(*ip6h));
skb_reset_network_header(skb);
+ skb->protocol = htons(ETH_P_IPV6);
ip6h = ipv6_hdr(skb);
ip6_flow_hdr(ip6h, dsfield,
ip6_make_flowlabel(net, skb, fl6.flowlabel, true, &fl6));
ip6h->hop_limit = hop_limit;
- ip6h->nexthdr = IPPROTO_IPV6;
+ ip6h->nexthdr = tuple->tun.l3_proto;
ip6h->daddr = tuple->tun.src_v6;
ip6h->saddr = tuple->tun.dst_v6;
ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(*ip6h));
@@ -696,14 +710,18 @@ static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb,
return 0;
}
-static int nf_flow_tunnel_v6_push(struct net *net, struct sk_buff *skb,
- struct flow_offload_tuple *tuple,
- struct in6_addr **ip6_daddr)
+static int nf_flow_tunnel_push(struct net *net, struct sk_buff *skb,
+ struct flow_offload_tuple *tuple,
+ __be32 *ip_daddr, struct in6_addr **ip6_daddr)
{
- if (tuple->tun_num)
- return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, ip6_daddr);
-
- return 0;
+ switch (tuple->tun.encap_proto) {
+ case AF_INET:
+ return nf_flow_tunnel_ipip_push(net, skb, tuple, ip_daddr);
+ case AF_INET6:
+ return nf_flow_tunnel_ip6_push(net, skb, tuple, ip6_daddr);
+ default:
+ return 0;
+ }
}
static int nf_flow_encap_push(struct sk_buff *skb,
@@ -815,6 +833,7 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
.in = state->in,
};
struct nf_flow_xmit xmit = {};
+ struct in6_addr *ip6_daddr;
struct flow_offload *flow;
struct neighbour *neigh;
struct rtable *rt;
@@ -843,26 +862,43 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
other_tuple = &flow->tuplehash[!dir].tuple;
ip_daddr = other_tuple->src_v4.s_addr;
+ ip6_daddr = &other_tuple->src_v6;
- if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple, &ip_daddr) < 0)
+ if (nf_flow_tunnel_push(state->net, skb, other_tuple, &ip_daddr,
+ &ip6_daddr) < 0)
return NF_DROP;
switch (tuplehash->tuple.xmit_type) {
- case FLOW_OFFLOAD_XMIT_NEIGH:
- rt = dst_rtable(tuplehash->tuple.dst_cache);
+ case FLOW_OFFLOAD_XMIT_NEIGH: {
+ struct dst_entry *dst;
+
xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.ifidx);
if (!xmit.outdev) {
flow_offload_teardown(flow);
return NF_DROP;
}
- neigh = ip_neigh_gw4(rt->dst.dev, rt_nexthop(rt, ip_daddr));
+ if (other_tuple->tun.encap_proto == AF_INET6 ||
+ ctx.tun.proto == IPPROTO_IPV6) {
+ struct rt6_info *rt6;
+
+ rt6 = dst_rt6_info(tuplehash->tuple.dst_cache);
+ neigh = ip_neigh_gw6(rt6->dst.dev,
+ rt6_nexthop(rt6, ip6_daddr));
+ dst = &rt6->dst;
+ } else {
+ rt = dst_rtable(tuplehash->tuple.dst_cache);
+ neigh = ip_neigh_gw4(rt->dst.dev,
+ rt_nexthop(rt, ip_daddr));
+ dst = &rt->dst;
+ }
if (IS_ERR(neigh)) {
flow_offload_teardown(flow);
return NF_DROP;
}
xmit.dest = neigh->ha;
- skb_dst_set_noref(skb, &rt->dst);
+ skb_dst_set_noref(skb, dst);
break;
+ }
case FLOW_OFFLOAD_XMIT_DIRECT:
xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.out.ifidx);
if (!xmit.outdev) {
@@ -1112,8 +1148,12 @@ nf_flow_offload_ipv6_lookup(struct nf_flowtable_ctx *ctx,
if (!nf_flow_skb_encap_protocol(ctx, skb, htons(ETH_P_IPV6)))
return NULL;
- if (nf_flow_tuple_ipv6(ctx, skb, &tuple) < 0)
+ if (ctx->tun.proto == IPPROTO_IPIP) {
+ if (nf_flow_tuple_ip(ctx, skb, &tuple) < 0)
+ return NULL;
+ } else if (nf_flow_tuple_ipv6(ctx, skb, &tuple) < 0) {
return NULL;
+ }
return flow_offload_lookup(flow_table, &tuple);
}
@@ -1134,13 +1174,18 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
struct flow_offload *flow;
struct neighbour *neigh;
struct rt6_info *rt;
+ __be32 ip_daddr;
int ret;
tuplehash = nf_flow_offload_ipv6_lookup(&ctx, flow_table, skb);
if (tuplehash == NULL)
return NF_ACCEPT;
- ret = nf_flow_offload_ipv6_forward(&ctx, flow_table, tuplehash, skb);
+ if (ctx.tun.proto == IPPROTO_IPIP)
+ ret = nf_flow_offload_forward(&ctx, flow_table, tuplehash, skb);
+ else
+ ret = nf_flow_offload_ipv6_forward(&ctx, flow_table, tuplehash,
+ skb);
if (ret < 0)
return NF_DROP;
else if (ret == 0)
@@ -1157,28 +1202,44 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
dir = tuplehash->tuple.dir;
flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
other_tuple = &flow->tuplehash[!dir].tuple;
+ ip_daddr = other_tuple->src_v4.s_addr;
ip6_daddr = &other_tuple->src_v6;
- if (nf_flow_tunnel_v6_push(state->net, skb, other_tuple,
- &ip6_daddr) < 0)
+ if (nf_flow_tunnel_push(state->net, skb, other_tuple, &ip_daddr,
+ &ip6_daddr) < 0)
return NF_DROP;
switch (tuplehash->tuple.xmit_type) {
- case FLOW_OFFLOAD_XMIT_NEIGH:
- rt = dst_rt6_info(tuplehash->tuple.dst_cache);
+ case FLOW_OFFLOAD_XMIT_NEIGH: {
+ struct dst_entry *dst;
+
xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.ifidx);
if (!xmit.outdev) {
flow_offload_teardown(flow);
return NF_DROP;
}
- neigh = ip_neigh_gw6(rt->dst.dev, rt6_nexthop(rt, ip6_daddr));
+ if (other_tuple->tun.encap_proto == AF_INET ||
+ ctx.tun.proto == IPPROTO_IPIP) {
+ struct rtable *rt4;
+
+ rt4 = dst_rtable(tuplehash->tuple.dst_cache);
+ neigh = ip_neigh_gw4(rt4->dst.dev,
+ rt_nexthop(rt4, ip_daddr));
+ dst = &rt4->dst;
+ } else {
+ rt = dst_rt6_info(tuplehash->tuple.dst_cache);
+ neigh = ip_neigh_gw6(rt->dst.dev,
+ rt6_nexthop(rt, ip6_daddr));
+ dst = &rt->dst;
+ }
if (IS_ERR(neigh)) {
flow_offload_teardown(flow);
return NF_DROP;
}
xmit.dest = neigh->ha;
- skb_dst_set_noref(skb, &rt->dst);
+ skb_dst_set_noref(skb, dst);
break;
+ }
case FLOW_OFFLOAD_XMIT_DIRECT:
xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.out.ifidx);
if (!xmit.outdev) {
diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c
index caaf48c5fd2a..5e84b7f18a26 100644
--- a/net/netfilter/nf_flow_table_path.c
+++ b/net/netfilter/nf_flow_table_path.c
@@ -216,12 +216,13 @@ static int nft_flow_tunnel_update_route(const struct nft_pktinfo *pkt,
struct dst_entry *tun_dst = NULL;
struct flowi fl = {};
- switch (nft_pf(pkt)) {
+ switch (tun->encap_proto) {
case NFPROTO_IPV4:
fl.u.ip4.daddr = tun->dst_v4.s_addr;
fl.u.ip4.saddr = tun->src_v4.s_addr;
fl.u.ip4.flowi4_iif = nft_in(pkt)->ifindex;
- fl.u.ip4.flowi4_dscp = ip4h_dscp(ip_hdr(pkt->skb));
+ if (nft_pf(pkt) == NFPROTO_IPV4)
+ fl.u.ip4.flowi4_dscp = ip4h_dscp(ip_hdr(pkt->skb));
fl.u.ip4.flowi4_mark = pkt->skb->mark;
fl.u.ip4.flowi4_flags = FLOWI_FLAG_ANYSRC;
break;
@@ -229,13 +230,14 @@ static int nft_flow_tunnel_update_route(const struct nft_pktinfo *pkt,
fl.u.ip6.daddr = tun->dst_v6;
fl.u.ip6.saddr = tun->src_v6;
fl.u.ip6.flowi6_iif = nft_in(pkt)->ifindex;
- fl.u.ip6.flowlabel = ip6_flowinfo(ipv6_hdr(pkt->skb));
+ if (nft_pf(pkt) == NFPROTO_IPV6)
+ fl.u.ip6.flowlabel = ip6_flowinfo(ipv6_hdr(pkt->skb));
fl.u.ip6.flowi6_mark = pkt->skb->mark;
fl.u.ip6.flowi6_flags = FLOWI_FLAG_ANYSRC;
break;
}
- nf_route(nft_net(pkt), &tun_dst, &fl, false, nft_pf(pkt));
+ nf_route(nft_net(pkt), &tun_dst, &fl, false, tun->encap_proto);
if (!tun_dst)
return -ENOENT;
--
2.55.0
^ permalink raw reply related
* [PATCH nf-next v5 4/6] selftests: netfilter: nft_flowtable.sh: add IPv4 over IPv6 flowtable selftest
From: Lorenzo Bianconi @ 2026-07-09 8:52 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Felix Fietkau, Matthias Brugger,
AngeloGioacchino Del Regno, Simon Horman, David Ahern,
Ido Schimmel, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
Shuah Khan, Lorenzo Bianconi
Cc: linux-arm-kernel, linux-mediatek, netdev, netfilter-devel,
coreteam, linux-kselftest
In-Reply-To: <20260709-b4-flowtable-sw-accel-ip6ip-v5-0-828ceaf85bab@kernel.org>
Similar to IPIP and IP6IP6, introduce specific selftest for IPv4 over IPv6
flowtable sw acceleration in nft_flowtable.sh
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
.../selftests/net/netfilter/nft_flowtable.sh | 33 +++++++++++++++++++---
1 file changed, 29 insertions(+), 4 deletions(-)
diff --git a/tools/testing/selftests/net/netfilter/nft_flowtable.sh b/tools/testing/selftests/net/netfilter/nft_flowtable.sh
index 08ad07500e8a..b14c80c6e372 100755
--- a/tools/testing/selftests/net/netfilter/nft_flowtable.sh
+++ b/tools/testing/selftests/net/netfilter/nft_flowtable.sh
@@ -579,9 +579,8 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 ""; then
ip netns exec "$nsr1" nft list ruleset
fi
-# IPIP tunnel test:
-# Add IPIP tunnel interfaces and check flowtable acceleration.
-test_ipip() {
+# IP tunnel tests:
+test_ip_tnls() {
if ! ip -net "$nsr1" link add name tun0 type ipip \
local 192.168.10.1 remote 192.168.10.2 >/dev/null;then
echo "SKIP: could not add ipip tunnel"
@@ -594,7 +593,9 @@ ip netns exec "$nsr1" sysctl net.ipv4.conf.tun0.forwarding=1 > /dev/null
ip -net "$nsr1" link add name tun6 type ip6tnl local fee1:2::1 remote fee1:2::2 encaplimit none
ip -net "$nsr1" link set tun6 up
+ip -net "$nsr1" addr add 192.168.210.1/24 dev tun6
ip -net "$nsr1" addr add fee1:3::1/64 dev tun6 nodad
+ip netns exec "$nsr1" sysctl net.ipv4.conf.tun6.forwarding=1 > /dev/null
ip -net "$nsr2" link add name tun0 type ipip local 192.168.10.2 remote 192.168.10.1
ip -net "$nsr2" link set tun0 up
@@ -603,7 +604,9 @@ ip netns exec "$nsr2" sysctl net.ipv4.conf.tun0.forwarding=1 > /dev/null
ip -net "$nsr2" link add name tun6 type ip6tnl local fee1:2::2 remote fee1:2::1 encaplimit none || ret=1
ip -net "$nsr2" link set tun6 up
+ip -net "$nsr2" addr add 192.168.210.2/24 dev tun6
ip -net "$nsr2" addr add fee1:3::2/64 dev tun6 nodad
+ip netns exec "$nsr2" sysctl net.ipv4.conf.tun6.forwarding=1 > /dev/null
ip -net "$nsr1" route change default via 192.168.100.2
ip -net "$nsr2" route change default via 192.168.100.1
@@ -636,6 +639,15 @@ else
ret=1
fi
+ip -net "$nsr1" route change default via 192.168.210.2
+ip -net "$nsr2" route change default via 192.168.210.1
+
+if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IP4IP6 tunnel"; then
+ echo "FAIL: flow offload for ns1/ns2 with IP4IP6 tunnel" 1>&2
+ ip netns exec "$nsr1" nft list ruleset
+ ret=1
+fi
+
# Create vlan tagged devices for IPIP traffic.
ip -net "$nsr1" link add link veth1 name veth1.10 type vlan id 10
ip -net "$nsr1" link set veth1.10 up
@@ -653,7 +665,9 @@ ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun0.10 a
ip -net "$nsr1" link add name tun6.10 type ip6tnl local fee1:4::1 remote fee1:4::2 encaplimit none
ip -net "$nsr1" link set tun6.10 up
+ip -net "$nsr1" addr add 192.168.220.1/24 dev tun6.10
ip -net "$nsr1" addr add fee1:5::1/64 dev tun6.10 nodad
+ip netns exec "$nsr1" sysctl net.ipv4.conf.tun6/10.forwarding=1 > /dev/null
ip -6 -net "$nsr1" route delete default
ip -6 -net "$nsr1" route add default via fee1:5::2
ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun6.10 accept'
@@ -672,7 +686,9 @@ ip netns exec "$nsr2" sysctl net.ipv4.conf.tun0/10.forwarding=1 > /dev/null
ip -net "$nsr2" link add name tun6.10 type ip6tnl local fee1:4::2 remote fee1:4::1 encaplimit none || ret=1
ip -net "$nsr2" link set tun6.10 up
+ip -net "$nsr2" addr add 192.168.220.2/24 dev tun6.10
ip -net "$nsr2" addr add fee1:5::2/64 dev tun6.10 nodad
+ip netns exec "$nsr2" sysctl net.ipv4.conf.tun6/10.forwarding=1 > /dev/null
ip -6 -net "$nsr2" route delete default
ip -6 -net "$nsr2" route add default via fee1:5::1
@@ -690,6 +706,15 @@ else
ret=1
fi
+ip -net "$nsr1" route change default via 192.168.220.2
+ip -net "$nsr2" route change default via 192.168.220.1
+
+if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IP4IP6 tunnel over vlan"; then
+ echo "FAIL: flow offload for ns1/ns2 with IP4IP6 tunnel over vlan" 1>&2
+ ip netns exec "$nsr1" nft list ruleset
+ ret=1
+fi
+
# Restore the previous configuration
ip -net "$nsr1" route change default via 192.168.10.2
ip -net "$nsr2" route change default via 192.168.10.1
@@ -782,7 +807,7 @@ ip -net "$nsr1" addr add dead:1::1/64 dev veth0 nodad
ip -net "$nsr1" link set up dev veth0
}
-test_ipip
+test_ip_tnls
test_bridge
--
2.55.0
^ permalink raw reply related
* [PATCH nf-next v5 5/6] net: netfilter: add SIT tunnel flowtable acceleration
From: Lorenzo Bianconi @ 2026-07-09 8:52 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Felix Fietkau, Matthias Brugger,
AngeloGioacchino Del Regno, Simon Horman, David Ahern,
Ido Schimmel, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
Shuah Khan, Lorenzo Bianconi
Cc: linux-arm-kernel, linux-mediatek, netdev, netfilter-devel,
coreteam, linux-kselftest
In-Reply-To: <20260709-b4-flowtable-sw-accel-ip6ip-v5-0-828ceaf85bab@kernel.org>
Introduce sw flowtable acceleration for the TX/RX paths of
SIT tunnels, relying on the netfilter flowtable infrastructure.
The feature can be tested with a forwarding scenario between two
NICs (eth0 and eth1), where a SIT tunnel is used to reach a remote
site via eth1 as the underlay device:
ETH0 -- TUN0 <==> ETH1 -- [IP network] -- TUN1 (192.168.2.2)
[IP configuration]
6: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 00:00:22:33:11:55 brd ff:ff:ff:ff:ff:ff
inet6 2001:db8:1::2/64 scope global nodad
valid_lft forever preferred_lft forever
7: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 00:11:22:33:11:55 brd ff:ff:ff:ff:ff:ff
inet 192.168.2.1/24 scope global eth1
valid_lft forever preferred_lft forever
8: tun0@NONE: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN group default qlen 1000
link/sit 192.168.2.1 peer 192.168.2.2
inet6 2001:db8:200::1/64 scope global nodad
valid_lft forever preferred_lft forever
$ ip route show
192.168.2.0/24 dev eth1 proto kernel scope link src 192.168.2.1
$ ip -6 route show
2001:db8:1::/64 dev eth0 proto kernel metric 256 pref medium
2001:db8:200::/64 dev tun0 proto kernel metric 256 pref medium
default via 2001:db8:200::2 dev tun0 metric 1024 pref medium
$ nft list ruleset
table inet filter {
flowtable ft {
hook ingress priority filter
devices = { eth0, eth1 }
}
chain forward {
type filter hook forward priority filter; policy accept;
meta l4proto { tcp, udp } flow add @ft
}
}
When reproducing this scenario using veth interfaces, the following
results were observed:
- TCP stream received from SIT tunnel:
- net-next (baseline): ~118 Gbps
- net-next + SIT flowtable support: ~148 Gbps
- TCP stream transmitted to SIT tunnel:
- net-next (baseline): ~131 Gbps
- net-next + SIT flowtable support: ~147 Gbps
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
net/ipv6/sit.c | 51 +++++++
net/netfilter/nf_flow_table_ip.c | 280 ++++++++++++++++++++++-----------------
2 files changed, 206 insertions(+), 125 deletions(-)
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index a38b24fb8384..ead327c32df2 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -1365,6 +1365,56 @@ ipip6_tunnel_ctl(struct net_device *dev, struct ip_tunnel_parm_kern *p,
}
}
+static int ipip6_tunnel_fill_forward_path(struct net_device_path_ctx *ctx,
+ struct net_device_path *path)
+{
+ struct ip_tunnel *tunnel = netdev_priv(ctx->dev);
+ const struct iphdr *tiph = &tunnel->parms.iph;
+ struct rtable *rt;
+
+ /* NBMA tunnels (ISATAP, 6to4, 6rd) resolve the outer destination
+ * per-packet from the inner IPv6 address; not offloadable.
+ */
+ if (!tiph->daddr)
+ return -EOPNOTSUPP;
+
+ /* FOU/GUE encapsulation is handled in the slow path by
+ * ip_tunnel_encap(); the fast path only pushes a plain
+ * IPv4 header and would skip the UDP encapsulation.
+ */
+ if (tunnel->encap.type != TUNNEL_ENCAP_NONE)
+ return -EOPNOTSUPP;
+
+ rt = ip_route_output(dev_net(ctx->dev), tiph->daddr, tiph->saddr,
+ inet_dsfield_to_dscp(tiph->tos),
+ tunnel->parms.link, RT_SCOPE_UNIVERSE);
+ if (IS_ERR(rt))
+ return PTR_ERR(rt);
+
+ path->type = DEV_PATH_TUN;
+ path->tun.src_v4.s_addr = tiph->saddr;
+ path->tun.dst_v4.s_addr = tiph->daddr;
+ path->tun.encap_proto = AF_INET;
+ path->dev = ctx->dev;
+
+ switch (ctx->ether_type) {
+ case cpu_to_be16(ETH_P_IP):
+ path->tun.l3_proto = IPPROTO_IPIP;
+ break;
+ case cpu_to_be16(ETH_P_IPV6):
+ path->tun.l3_proto = IPPROTO_IPV6;
+ break;
+ default:
+ ip_rt_put(rt);
+ return -EOPNOTSUPP;
+ }
+
+ ctx->dev = rt->dst.dev;
+ ip_rt_put(rt);
+
+ return 0;
+}
+
static int
ipip6_tunnel_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
void __user *data, int cmd)
@@ -1401,6 +1451,7 @@ static const struct net_device_ops ipip6_netdev_ops = {
.ndo_siocdevprivate = ipip6_tunnel_siocdevprivate,
.ndo_get_iflink = ip_tunnel_get_iflink,
.ndo_tunnel_ctl = ipip6_tunnel_ctl,
+ .ndo_fill_forward_path = ipip6_tunnel_fill_forward_path,
};
static void ipip6_dev_free(struct net_device *dev)
diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
index 26a50e92459d..449818dcef3c 100644
--- a/net/netfilter/nf_flow_table_ip.c
+++ b/net/netfilter/nf_flow_table_ip.c
@@ -338,7 +338,7 @@ static bool nf_flow_ip4_tunnel_proto(struct nf_flowtable_ctx *ctx,
if (iph->ttl <= 1)
return false;
- if (iph->protocol == IPPROTO_IPIP) {
+ if (iph->protocol == IPPROTO_IPIP || iph->protocol == IPPROTO_IPV6) {
ctx->tun.proto = iph->protocol;
ctx->tun.hdr_size = size;
ctx->offset += ctx->tun.hdr_size;
@@ -464,21 +464,6 @@ static void nf_flow_encap_pop(struct nf_flowtable_ctx *ctx,
nf_flow_ip_tunnel_pop(ctx, skb);
}
-static struct flow_offload_tuple_rhash *
-nf_flow_offload_lookup(struct nf_flowtable_ctx *ctx,
- struct nf_flowtable *flow_table, struct sk_buff *skb)
-{
- struct flow_offload_tuple tuple = {};
-
- if (!nf_flow_skb_encap_protocol(ctx, skb, htons(ETH_P_IP)))
- return NULL;
-
- if (nf_flow_tuple_ip(ctx, skb, &tuple) < 0)
- return NULL;
-
- return flow_offload_lookup(flow_table, &tuple);
-}
-
static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx,
struct nf_flowtable *flow_table,
struct flow_offload_tuple_rhash *tuplehash,
@@ -602,23 +587,37 @@ static int nf_flow_pppoe_push(struct sk_buff *skb, u16 id,
return 0;
}
-static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
- struct flow_offload_tuple *tuple,
- __be32 *ip_daddr)
+static int nf_flow_tunnel_ip_push(struct net *net, struct sk_buff *skb,
+ struct flow_offload_tuple *tuple,
+ __be32 *ip_daddr)
{
- struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
struct rtable *rt = dst_rtable(tuple->dst_cache);
- u8 tos = iph->tos, ttl = iph->ttl;
- __be16 frag_off = iph->frag_off;
- u32 headroom = sizeof(*iph);
+ __be16 frag_off = 0;
+ struct iphdr *iph;
+ u8 tos = 0, ttl;
+ u32 headroom;
int err;
+ if (tuple->tun.l3_proto == IPPROTO_IPV6) {
+ struct ipv6hdr *ip6h;
+
+ ip6h = (struct ipv6hdr *)skb_network_header(skb);
+ tos = ipv6_get_dsfield(ip6h);
+ ttl = ip6h->hop_limit;
+ } else {
+ iph = (struct iphdr *)skb_network_header(skb);
+ frag_off = iph->frag_off;
+ tos = iph->tos;
+ ttl = iph->ttl;
+ }
+
err = iptunnel_handle_offloads(skb, SKB_GSO_IPXIP4);
if (err)
return err;
- skb_set_inner_ipproto(skb, IPPROTO_IPIP);
- headroom += LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len;
+ skb_set_inner_ipproto(skb, tuple->tun.l3_proto);
+ headroom = sizeof(*iph) + LL_RESERVED_SPACE(rt->dst.dev) +
+ rt->dst.header_len;
err = skb_cow_head(skb, headroom);
if (err)
return err;
@@ -716,7 +715,7 @@ static int nf_flow_tunnel_push(struct net *net, struct sk_buff *skb,
{
switch (tuple->tun.encap_proto) {
case AF_INET:
- return nf_flow_tunnel_ipip_push(net, skb, tuple, ip_daddr);
+ return nf_flow_tunnel_ip_push(net, skb, tuple, ip_daddr);
case AF_INET6:
return nf_flow_tunnel_ip6_push(net, skb, tuple, ip6_daddr);
default:
@@ -821,104 +820,6 @@ static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb,
return NF_STOLEN;
}
-unsigned int
-nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
- const struct nf_hook_state *state)
-{
- struct flow_offload_tuple_rhash *tuplehash;
- struct nf_flowtable *flow_table = priv;
- struct flow_offload_tuple *other_tuple;
- enum flow_offload_tuple_dir dir;
- struct nf_flowtable_ctx ctx = {
- .in = state->in,
- };
- struct nf_flow_xmit xmit = {};
- struct in6_addr *ip6_daddr;
- struct flow_offload *flow;
- struct neighbour *neigh;
- struct rtable *rt;
- __be32 ip_daddr;
- int ret;
-
- tuplehash = nf_flow_offload_lookup(&ctx, flow_table, skb);
- if (!tuplehash)
- return NF_ACCEPT;
-
- ret = nf_flow_offload_forward(&ctx, flow_table, tuplehash, skb);
- if (ret < 0)
- return NF_DROP;
- else if (ret == 0)
- return NF_ACCEPT;
-
- if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
- rt = dst_rtable(tuplehash->tuple.dst_cache);
- memset(skb->cb, 0, sizeof(struct inet_skb_parm));
- IPCB(skb)->iif = skb->dev->ifindex;
- IPCB(skb)->flags = IPSKB_FORWARDED;
- return nf_flow_xmit_xfrm(skb, state, &rt->dst);
- }
-
- dir = tuplehash->tuple.dir;
- flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
- other_tuple = &flow->tuplehash[!dir].tuple;
- ip_daddr = other_tuple->src_v4.s_addr;
- ip6_daddr = &other_tuple->src_v6;
-
- if (nf_flow_tunnel_push(state->net, skb, other_tuple, &ip_daddr,
- &ip6_daddr) < 0)
- return NF_DROP;
-
- switch (tuplehash->tuple.xmit_type) {
- case FLOW_OFFLOAD_XMIT_NEIGH: {
- struct dst_entry *dst;
-
- xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.ifidx);
- if (!xmit.outdev) {
- flow_offload_teardown(flow);
- return NF_DROP;
- }
- if (other_tuple->tun.encap_proto == AF_INET6 ||
- ctx.tun.proto == IPPROTO_IPV6) {
- struct rt6_info *rt6;
-
- rt6 = dst_rt6_info(tuplehash->tuple.dst_cache);
- neigh = ip_neigh_gw6(rt6->dst.dev,
- rt6_nexthop(rt6, ip6_daddr));
- dst = &rt6->dst;
- } else {
- rt = dst_rtable(tuplehash->tuple.dst_cache);
- neigh = ip_neigh_gw4(rt->dst.dev,
- rt_nexthop(rt, ip_daddr));
- dst = &rt->dst;
- }
- if (IS_ERR(neigh)) {
- flow_offload_teardown(flow);
- return NF_DROP;
- }
- xmit.dest = neigh->ha;
- skb_dst_set_noref(skb, dst);
- break;
- }
- case FLOW_OFFLOAD_XMIT_DIRECT:
- xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.out.ifidx);
- if (!xmit.outdev) {
- flow_offload_teardown(flow);
- return NF_DROP;
- }
- xmit.dest = tuplehash->tuple.out.h_dest;
- xmit.source = tuplehash->tuple.out.h_source;
- break;
- default:
- WARN_ON_ONCE(1);
- return NF_DROP;
- }
- xmit.tuple = other_tuple;
- xmit.needs_gso_segment = tuplehash->tuple.needs_gso_segment;
-
- return nf_flow_queue_xmit(state->net, skb, &xmit);
-}
-EXPORT_SYMBOL_GPL(nf_flow_offload_ip_hook);
-
static void nf_flow_nat_ipv6_tcp(struct sk_buff *skb, unsigned int thoff,
struct in6_addr *addr,
struct in6_addr *new_addr,
@@ -1103,8 +1004,16 @@ static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx,
flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
mtu = flow->tuplehash[dir].tuple.mtu + ctx->offset;
- if (flow->tuplehash[!dir].tuple.tun_num)
+ switch (flow->tuplehash[!dir].tuple.tun.encap_proto) {
+ case AF_INET:
+ mtu -= sizeof(struct iphdr);
+ break;
+ case AF_INET6:
mtu -= sizeof(*ip6h);
+ break;
+ default:
+ break;
+ }
if (unlikely(nf_flow_exceeds_mtu(skb, mtu)))
return 0;
@@ -1138,6 +1047,25 @@ static int nf_flow_offload_ipv6_forward(struct nf_flowtable_ctx *ctx,
return 1;
}
+static struct flow_offload_tuple_rhash *
+nf_flow_offload_lookup(struct nf_flowtable_ctx *ctx,
+ struct nf_flowtable *flow_table, struct sk_buff *skb)
+{
+ struct flow_offload_tuple tuple = {};
+
+ if (!nf_flow_skb_encap_protocol(ctx, skb, htons(ETH_P_IP)))
+ return NULL;
+
+ if (ctx->tun.proto == IPPROTO_IPV6) {
+ if (nf_flow_tuple_ipv6(ctx, skb, &tuple) < 0)
+ return NULL;
+ } else if (nf_flow_tuple_ip(ctx, skb, &tuple) < 0) {
+ return NULL;
+ }
+
+ return flow_offload_lookup(flow_table, &tuple);
+}
+
static struct flow_offload_tuple_rhash *
nf_flow_offload_ipv6_lookup(struct nf_flowtable_ctx *ctx,
struct nf_flowtable *flow_table,
@@ -1158,6 +1086,108 @@ nf_flow_offload_ipv6_lookup(struct nf_flowtable_ctx *ctx,
return flow_offload_lookup(flow_table, &tuple);
}
+unsigned int
+nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
+ const struct nf_hook_state *state)
+{
+ struct flow_offload_tuple_rhash *tuplehash;
+ struct nf_flowtable *flow_table = priv;
+ struct flow_offload_tuple *other_tuple;
+ enum flow_offload_tuple_dir dir;
+ struct nf_flowtable_ctx ctx = {
+ .in = state->in,
+ };
+ struct nf_flow_xmit xmit = {};
+ struct in6_addr *ip6_daddr;
+ struct flow_offload *flow;
+ struct neighbour *neigh;
+ struct rtable *rt;
+ __be32 ip_daddr;
+ int ret;
+
+ tuplehash = nf_flow_offload_lookup(&ctx, flow_table, skb);
+ if (!tuplehash)
+ return NF_ACCEPT;
+
+ if (ctx.tun.proto == IPPROTO_IPV6)
+ ret = nf_flow_offload_ipv6_forward(&ctx, flow_table, tuplehash,
+ skb);
+ else
+ ret = nf_flow_offload_forward(&ctx, flow_table, tuplehash, skb);
+ if (ret < 0)
+ return NF_DROP;
+ else if (ret == 0)
+ return NF_ACCEPT;
+
+ if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
+ rt = dst_rtable(tuplehash->tuple.dst_cache);
+ memset(skb->cb, 0, sizeof(struct inet_skb_parm));
+ IPCB(skb)->iif = skb->dev->ifindex;
+ IPCB(skb)->flags = IPSKB_FORWARDED;
+ return nf_flow_xmit_xfrm(skb, state, &rt->dst);
+ }
+
+ dir = tuplehash->tuple.dir;
+ flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
+ other_tuple = &flow->tuplehash[!dir].tuple;
+ ip_daddr = other_tuple->src_v4.s_addr;
+ ip6_daddr = &other_tuple->src_v6;
+
+ if (nf_flow_tunnel_push(state->net, skb, other_tuple, &ip_daddr,
+ &ip6_daddr) < 0)
+ return NF_DROP;
+
+ switch (tuplehash->tuple.xmit_type) {
+ case FLOW_OFFLOAD_XMIT_NEIGH: {
+ struct dst_entry *dst;
+
+ xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.ifidx);
+ if (!xmit.outdev) {
+ flow_offload_teardown(flow);
+ return NF_DROP;
+ }
+ if (other_tuple->tun.encap_proto == AF_INET6 ||
+ ctx.tun.proto == IPPROTO_IPV6) {
+ struct rt6_info *rt6;
+
+ rt6 = dst_rt6_info(tuplehash->tuple.dst_cache);
+ neigh = ip_neigh_gw6(rt6->dst.dev,
+ rt6_nexthop(rt6, ip6_daddr));
+ dst = &rt6->dst;
+ } else {
+ rt = dst_rtable(tuplehash->tuple.dst_cache);
+ neigh = ip_neigh_gw4(rt->dst.dev,
+ rt_nexthop(rt, ip_daddr));
+ dst = &rt->dst;
+ }
+ if (IS_ERR(neigh)) {
+ flow_offload_teardown(flow);
+ return NF_DROP;
+ }
+ xmit.dest = neigh->ha;
+ skb_dst_set_noref(skb, dst);
+ break;
+ }
+ case FLOW_OFFLOAD_XMIT_DIRECT:
+ xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.out.ifidx);
+ if (!xmit.outdev) {
+ flow_offload_teardown(flow);
+ return NF_DROP;
+ }
+ xmit.dest = tuplehash->tuple.out.h_dest;
+ xmit.source = tuplehash->tuple.out.h_source;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return NF_DROP;
+ }
+ xmit.tuple = other_tuple;
+ xmit.needs_gso_segment = tuplehash->tuple.needs_gso_segment;
+
+ return nf_flow_queue_xmit(state->net, skb, &xmit);
+}
+EXPORT_SYMBOL_GPL(nf_flow_offload_ip_hook);
+
unsigned int
nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
--
2.55.0
^ permalink raw reply related
* [PATCH nf-next v5 6/6] selftests: netfilter: nft_flowtable.sh: add SIT flowtable selftest
From: Lorenzo Bianconi @ 2026-07-09 8:52 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Felix Fietkau, Matthias Brugger,
AngeloGioacchino Del Regno, Simon Horman, David Ahern,
Ido Schimmel, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
Shuah Khan, Lorenzo Bianconi
Cc: linux-arm-kernel, linux-mediatek, netdev, netfilter-devel,
coreteam, linux-kselftest
In-Reply-To: <20260709-b4-flowtable-sw-accel-ip6ip-v5-0-828ceaf85bab@kernel.org>
Similar to IPIP, IP6IP6 and IPv4 over IPv6, introduce specific selftest
for SIT flowtable sw acceleration in nft_flowtable.sh
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
tools/testing/selftests/net/netfilter/config | 1 +
.../selftests/net/netfilter/nft_flowtable.sh | 45 ++++++++++++++++++++++
2 files changed, 46 insertions(+)
diff --git a/tools/testing/selftests/net/netfilter/config b/tools/testing/selftests/net/netfilter/config
index c3c121b6f300..a212b9edbcd9 100644
--- a/tools/testing/selftests/net/netfilter/config
+++ b/tools/testing/selftests/net/netfilter/config
@@ -35,6 +35,7 @@ CONFIG_IP_SCTP=m
CONFIG_IPV6=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_TUNNEL=m
+CONFIG_IPV6_SIT=m
CONFIG_IP_VS=m
CONFIG_IP_VS_PROTO_TCP=y
CONFIG_IP_VS_RR=m
diff --git a/tools/testing/selftests/net/netfilter/nft_flowtable.sh b/tools/testing/selftests/net/netfilter/nft_flowtable.sh
index b14c80c6e372..46bd080108be 100755
--- a/tools/testing/selftests/net/netfilter/nft_flowtable.sh
+++ b/tools/testing/selftests/net/netfilter/nft_flowtable.sh
@@ -597,6 +597,10 @@ ip -net "$nsr1" addr add 192.168.210.1/24 dev tun6
ip -net "$nsr1" addr add fee1:3::1/64 dev tun6 nodad
ip netns exec "$nsr1" sysctl net.ipv4.conf.tun6.forwarding=1 > /dev/null
+ip -net "$nsr1" link add name sit1 type sit local 192.168.10.1 remote 192.168.10.2 ttl 255
+ip -net "$nsr1" link set sit1 up
+ip -net "$nsr1" addr add fe01:3::1/64 dev sit1 nodad
+
ip -net "$nsr2" link add name tun0 type ipip local 192.168.10.2 remote 192.168.10.1
ip -net "$nsr2" link set tun0 up
ip -net "$nsr2" addr add 192.168.100.2/24 dev tun0
@@ -608,6 +612,10 @@ ip -net "$nsr2" addr add 192.168.210.2/24 dev tun6
ip -net "$nsr2" addr add fee1:3::2/64 dev tun6 nodad
ip netns exec "$nsr2" sysctl net.ipv4.conf.tun6.forwarding=1 > /dev/null
+ip -net "$nsr2" link add name sit1 type sit local 192.168.10.2 remote 192.168.10.1 ttl 255
+ip -net "$nsr2" link set sit1 up
+ip -net "$nsr2" addr add fe01:3::2/64 dev sit1 nodad
+
ip -net "$nsr1" route change default via 192.168.100.2
ip -net "$nsr2" route change default via 192.168.100.1
@@ -622,6 +630,7 @@ ip -6 -net "$ns2" route add default via dead:2::1
ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun0 accept'
ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun6 accept'
+ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif sit1 accept'
ip netns exec "$nsr1" nft -a insert rule inet filter forward \
'meta oif "veth0" tcp sport 12345 ct mark set 1 flow add @f1 counter name routed_repl accept'
@@ -648,6 +657,19 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IP4IP6 tunnel"; then
ret=1
fi
+ip -6 -net "$nsr1" route delete default
+ip -6 -net "$nsr1" route add default via fe01:3::2
+ip -6 -net "$nsr2" route delete default
+ip -6 -net "$nsr2" route add default via fe01:3::1
+
+if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then
+ echo "PASS: flow offload for ns1/ns2 SIT tunnel"
+else
+ echo "FAIL: flow offload for ns1/ns2 with SIT tunnel" 1>&2
+ ip netns exec "$nsr1" nft list ruleset
+ ret=1
+fi
+
# Create vlan tagged devices for IPIP traffic.
ip -net "$nsr1" link add link veth1 name veth1.10 type vlan id 10
ip -net "$nsr1" link set veth1.10 up
@@ -672,6 +694,11 @@ ip -6 -net "$nsr1" route delete default
ip -6 -net "$nsr1" route add default via fee1:5::2
ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif tun6.10 accept'
+ip -net "$nsr1" link add name sit1.10 type sit local 192.168.20.1 remote 192.168.20.2 ttl 255
+ip -net "$nsr1" link set sit1.10 up
+ip -net "$nsr1" addr add fe01:5::1/64 dev sit1.10 nodad
+ip netns exec "$nsr1" nft -a insert rule inet filter forward 'meta oif sit1.10 accept'
+
ip -net "$nsr2" link add link veth0 name veth0.10 type vlan id 10
ip -net "$nsr2" link set veth0.10 up
ip -net "$nsr2" addr add 192.168.20.2/24 dev veth0.10
@@ -689,6 +716,11 @@ ip -net "$nsr2" link set tun6.10 up
ip -net "$nsr2" addr add 192.168.220.2/24 dev tun6.10
ip -net "$nsr2" addr add fee1:5::2/64 dev tun6.10 nodad
ip netns exec "$nsr2" sysctl net.ipv4.conf.tun6/10.forwarding=1 > /dev/null
+
+ip -net "$nsr2" link add name sit1.10 type sit local 192.168.20.2 remote 192.168.20.1 ttl 255
+ip -net "$nsr2" link set sit1.10 up
+ip -net "$nsr2" addr add fe01:5::2/64 dev sit1.10 nodad
+
ip -6 -net "$nsr2" route delete default
ip -6 -net "$nsr2" route add default via fee1:5::1
@@ -715,6 +747,19 @@ if ! test_tcp_forwarding_nat "$ns1" "$ns2" 1 "IP4IP6 tunnel over vlan"; then
ret=1
fi
+ip -6 -net "$nsr1" route delete default
+ip -6 -net "$nsr1" route add default via fe01:5::2
+ip -6 -net "$nsr2" route delete default
+ip -6 -net "$nsr2" route add default via fe01:5::1
+
+if test_tcp_forwarding "$ns1" "$ns2" 1 6 "[dead:2::99]" 12345; then
+ echo "PASS: flow offload for ns1/ns2 SIT tunnel over vlan"
+else
+ echo "FAIL: flow offload for ns1/ns2 with SIT tunnel over vlan" 1>&2
+ ip netns exec "$nsr1" nft list ruleset
+ ret=1
+fi
+
# Restore the previous configuration
ip -net "$nsr1" route change default via 192.168.10.2
ip -net "$nsr2" route change default via 192.168.10.1
--
2.55.0
^ permalink raw reply related
* Re: [PATCH net v2 1/2] vsock/virtio: collapse receive queue under memory pressure
From: Stefano Garzarella @ 2026-07-09 8:54 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: netdev, Jason Wang, Xuan Zhuo, Eric Dumazet, Eugenio Pérez,
Simon Horman, Stefan Hajnoczi, David S. Miller, linux-kernel, kvm,
Paolo Abeni, virtualization, Jakub Kicinski, Jason Wang, stable,
Brien Oberstein
In-Reply-To: <20260708065947-mutt-send-email-mst@kernel.org>
On Wed, Jul 08, 2026 at 07:00:00AM -0400, Michael S. Tsirkin wrote:
>On Wed, Jul 08, 2026 at 12:29:03PM +0200, Stefano Garzarella wrote:
>> From: Stefano Garzarella <sgarzare@redhat.com>
>>
>> When many small packets accumulate in the receive queue, the skb overhead
>> can exceed buf_alloc even while the payload is within bounds. This causes
>> virtio_transport_inc_rx_pkt() to reject packets, leading to connection
>> resets during large transfers under backpressure.
>>
>> The issue was reported by Brien, who has a reproducer, but it is also
>> easily reproducible with iperf-vsock [1] using a small packet size:
>>
>> iperf3 --vsock -c $CID -l 129
>>
>> which fails immediately without this patch but with commit 059b7dbd20a6
>> ("vsock/virtio: fix potential unbounded skb queue").
>>
>> Inspired by TCP's tcp_collapse() which solves a similar problem, add
>> virtio_transport_collapse_rx_queue() that walks the receive queue and
>> re-copies data into compact linear skbs to reduce the overhead.
>>
>> The collapse is triggered proactively from when the number of skb queued
>> is close to exceeding the overhead budget.
>>
>> A pre-scan counts the eligible bytes to size each allocation precisely,
>> avoiding waste for isolated small packets. Partially consumed skbs are
>> kept as-is to preserve buf_used/fwd_cnt accounting, EOM-marked skbs to
>> maintain SEQPACKET message boundaries, and skbs already larger than the
>> collapse target because they already have a good data-to-overhead ratio.
>>
>> Walking a large queue may take a significant amount of time and cache
>> misses, causing traffic burstiness. To limit this, the collapse stops
>> once enough room is freed for this packet and the next one, but may
>> opportunistically free more to fill each collapsed skb to capacity.
>>
>> [1] https://github.com/stefano-garzarella/iperf-vsock
>>
>> Fixes: 059b7dbd20a6 ("vsock/virtio: fix potential unbounded skb queue")
>> Cc: stable@vger.kernel.org
>> Reported-by: Brien Oberstein <brienpub@gmail.com>
>> Closes: https://lore.kernel.org/netdev/618701dd023e$063de350$12b9a9f0$@gmail.com/
>> Tested-by: Brien Oberstein <brienpub@gmail.com>
>> Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
>
>
>this is the right approach
Yeah, I have a follow up to start to use skb->truesize, etc. but I guess
more net-next material.
>
>Acked-by: Michael S. Tsirkin <mst@redhat.com>
Thanks,
Stefano
^ permalink raw reply
* Re: [PATCH RESEND 1/1] macvlan: allow source mode devices along with passthru
From: Thomas Martitz @ 2026-07-09 8:49 UTC (permalink / raw)
To: Simon Horman
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, netdev,
linux-kernel
In-Reply-To: <20260708164350.1513604-1-horms@kernel.org>
Hi Simon,
Am 08.07.26 um 18:43 schrieb Simon Horman:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://sashiko.dev
Thanks for feedback. The remarks are valid, I agree. I also begin to better
understand the macvlan code so I'm a bit more confident now about which of
the existing "macvlan_passthru" checks need treatment. I'll send an updated
patch.
General question though: The initial posting triggered an AI review (I see
that now, I didn't know about https://sashiko.dev before). But it did not
trigger an email. Can you explain why? Because I accidentally dropped the RFC
tag from the mail subject?
Best regards.
> ---
> macvlan: allow source mode devices along with passthru
>
> This allows for configurations where there are a few
> known senders in the system (e.g. multiple SoCs on the same
> board) along with unlimited external senders.
>
>> diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
>> index c40fa331836bb..d28f9d905a84d 100644
>> --- a/drivers/net/macvlan.c
>> +++ b/drivers/net/macvlan.c
>
> [ ... ]
>
>> @@ -1523,10 +1514,30 @@ int macvlan_common_newlink(struct net_device *dev,
>> if (data && data[IFLA_MACVLAN_FLAGS])
>> vlan->flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]);
>>
>> + /* Only 1 macvlan device can be created in passthru mode. There may be
>> + * additional source mode devices but nothing else at the moment.
>> + *
>> + * First check if adding a source mode device to an existing passthru vlan.
>> + */
>> + if (macvlan_passthru(port) && vlan->mode != MACVLAN_MODE_SOURCE) {
>> + /* The macvlan port must be not created this time,
>> + * still goto destroy_macvlan_port for readability.
>> + */
>> + err = -EINVAL;
>> + goto destroy_macvlan_port;
>> + }
>
> [Severity: High]
> While macvlan_common_newlink() prevents creating non-SOURCE devices on a
> passthru port, macvlan_changelink() does not seem to have a similar check.
>
> Can a user bypass this restriction by creating a SOURCE device and then using
> changelink to modify its mode to VEPA, bypassing MAC isolation?
>
> [Severity: High]
> With source devices and passthru devices now coexisting on the same port,
> functions like macvlan_open(), macvlan_stop(), and macvlan_sync_address()
> check the port-wide macvlan_passthru(vlan->port) flag instead of the device
> mode.
>
> If a user brings down a SOURCE device on a passthru port, will macvlan_stop()
> incorrectly take the passthru teardown path? This appears to skip dev_uc_del()
> and call dev_set_promiscuity(..., -1) for a device that never incremented it.
>
>> +
>> + /* Now check if adding a passthru device to an existing set of source mode
>> + * devices.
>> + */
>> if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
>> - if (port->count) {
>> - err = -EINVAL;
>> - goto destroy_macvlan_port;
>> + struct macvlan_dev *p;
>> +
>> + list_for_each_entry(p, &port->vlans, list) {
>> + if (p->mode != MACVLAN_MODE_SOURCE) {
>> + err = -EINVAL;
>> + goto destroy_macvlan_port;
>> + }
>> }
>> macvlan_set_passthru(port);
>
> [Severity: High]
> If a PASSTHRU device is deleted while SOURCE devices still exist on the port,
> macvlan_uninit() decrements port->count but does not clear MACVLAN_F_PASSTHRU.
>
> Does this leave the port in a permanently broken state where new PASSTHRU
> devices cannot be created, and remaining SOURCE devices continue to be
> mistakenly treated as passthru?
>
>> eth_hw_addr_inherit(dev, lowerdev);
>
--
Thomas Martitz <t.martitz@fritz.com>
FRITZ! Technology GmbH, Berlin (Germany)
^ permalink raw reply
* Re: [Intel-wired-lan] [PATCH iwl-net v4 1/2] ice: skip per-VLAN promisc rules when default VSI Rx rule is set
From: Marcin Szycik @ 2026-07-09 8:56 UTC (permalink / raw)
To: Petr Oros, netdev
Cc: Ivan Vecera, Alice Michael, Przemek Kitszel, Eric Dumazet,
linux-kernel, Martyna Szapar-Mudlaw, Andrew Lunn, Tony Nguyen,
Simon Horman, intel-wired-lan, Jacob Keller, Jakub Kicinski,
Paolo Abeni, David S. Miller
In-Reply-To: <20260708125755.706263-2-poros@redhat.com>
On 08/07/2026 14:57, Petr Oros wrote:
> When an ice port in a vlan-filtering bridge goes promiscuous (typical for
> bond slaves), the driver installs a per-VLAN ICE_SW_LKUP_PROMISC_VLAN rule
> for every VID on top of the broad ICE_SW_LKUP_DFLT VSI Rx rule. Each rule
> consumes one of the ~32K Flow Lookup Unit (FLU) entries the device shares
> across PFs, so a wide trunk (vid 2-4094) over several PFs overruns the
> pool: firmware rejects further Add Switch Rules with ENOSPC (AQ 0x10) and
> the DFLT Rx rule itself fails to install:
>
> ice 0000:5c:00.1: Failed to set VSI 14 as the default forwarding
> VSI, error -5
> ice 0000:5c:00.1 ens1f1: Error -5 setting default VSI 14 Rx rule
>
> Once a switch context is overrun the retries can also come back as ENOENT
> (AQ 0x2), which has misled triage toward a perceived recipe binding defect
> rather than a capacity issue.
>
> The DFLT rule already catches every packet on the port regardless of VLAN
> tag, so the per-VLAN promisc expansion is redundant while it is installed.
> Skip it at the two sites that drive it, ice_set_promisc() and
> ice_vlan_rx_add_vid(), keyed on ice_is_vsi_dflt_vsi() rather than the
> netdev IFF_PROMISC flag so a failed or LAG-suppressed DFLT install still
> falls back to the per-VLAN rules.
>
> IFF_ALLMULTI and IFF_PROMISC can reach ice_vsi_sync_fltr() in separate
> passes (a bridge join sets them through separate calls), so the allmulti
> pass may expand the per-VID rules before the DFLT rule exists. Drop those
> now-redundant rules right after ice_set_dflt_vsi() installs the DFLT rule;
> ice_vsi_exit_dflt_promisc() reinstates them when promisc is cleared.
>
> ice_vsi_sync_fltr() subscribed multicast promiscuity only inside the
> "default VSI not yet in use" branch, so a promiscuous VSI that finds the
> default VSI rule already present (owned by another VSI, or preserved
> across a switchdev session) ended up in unicast promisc with no multicast
> subscription. Issue ice_set_promisc(ICE_MCAST_PROMISC_BITS) whenever the
> netdev is promiscuous; it is idempotent and returns 0 if the rule is
> already present.
>
> Fixes: 1273f89578f2 ("ice: Fix broken IFF_ALLMULTI handling")
> Signed-off-by: Petr Oros <poros@redhat.com>
LGTM
Reviewed-by: Marcin Szycik <marcin.szycik@linux.intel.com>
> ---
> v4 (review comments from Marcin Szycik):
> - Reworded the ice_clear_promisc() and the per-VID cleanup comments.
> - Flipped the ice_vsi_clear_vlan_mc_promisc() guard to an early return to
> reduce indentation, and noted that the clear is best-effort.
> No functional change.
>
> v3: https://lore.kernel.org/all/20260701133601.2118382-2-poros@redhat.com/
> v2: https://lore.kernel.org/all/20260622113428.2565255-2-poros@redhat.com/
> v1: https://lore.kernel.org/all/89efbea9831175e6f57e9fe8557f7a0e48e050b7.1781786935.git.poros@redhat.com/
> ---
> drivers/net/ethernet/intel/ice/ice_main.c | 109 +++++++++++++++++-----
> 1 file changed, 88 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
> index b43d420ece99ca..f1fef26eeadac9 100644
> --- a/drivers/net/ethernet/intel/ice/ice_main.c
> +++ b/drivers/net/ethernet/intel/ice/ice_main.c
> @@ -274,7 +274,8 @@ static int ice_set_promisc(struct ice_vsi *vsi, u8 promisc_m)
> if (vsi->type != ICE_VSI_PF)
> return 0;
>
> - if (ice_vsi_has_non_zero_vlans(vsi)) {
> + /* skip per-VID expansion; the DFLT Rx rule already covers every VID */
> + if (ice_vsi_has_non_zero_vlans(vsi) && !ice_is_vsi_dflt_vsi(vsi)) {
> promisc_m |= (ICE_PROMISC_VLAN_RX | ICE_PROMISC_VLAN_TX);
> status = ice_fltr_set_vlan_vsi_promisc(&vsi->back->hw, vsi,
> promisc_m);
> @@ -304,9 +305,20 @@ static int ice_clear_promisc(struct ice_vsi *vsi, u8 promisc_m)
> return 0;
>
> if (ice_vsi_has_non_zero_vlans(vsi)) {
> - promisc_m |= (ICE_PROMISC_VLAN_RX | ICE_PROMISC_VLAN_TX);
> + u8 vlan_promisc_m = promisc_m | ICE_PROMISC_VLAN_RX |
> + ICE_PROMISC_VLAN_TX;
> + int vid0_status;
> +
> + /* the vid=0 rule may be in either recipe (the recipe used to
> + * set it is not recorded), so clear both; clearing an absent
> + * rule returns 0
> + */
> status = ice_fltr_clear_vlan_vsi_promisc(&vsi->back->hw, vsi,
> - promisc_m);
> + vlan_promisc_m);
> + vid0_status = ice_fltr_clear_vsi_promisc(&vsi->back->hw,
> + vsi->idx, promisc_m, 0);
> + if (!status)
> + status = vid0_status;
> } else {
> status = ice_fltr_clear_vsi_promisc(&vsi->back->hw, vsi->idx,
> promisc_m, 0);
> @@ -317,6 +329,61 @@ static int ice_clear_promisc(struct ice_vsi *vsi, u8 promisc_m)
> return status;
> }
>
> +/**
> + * ice_vsi_exit_dflt_promisc - drop the default VSI Rx rule on promisc off
> + * @vsi: the VSI leaving promiscuous mode
> + *
> + * For an IFF_ALLMULTI VSI with VLANs the per-VID multicast rules are
> + * reinstated before the default rule is cleared so coverage never lapses;
> + * the then redundant vid=0 rule is dropped best-effort. The callees log
> + * their own failures, so error returns are not re-logged here.
> + *
> + * Return: 0 on success, negative on error with the default rule left in place.
> + */
> +static int ice_vsi_exit_dflt_promisc(struct ice_vsi *vsi)
> +{
> + struct ice_vsi_vlan_ops *vlan_ops = ice_get_compat_vsi_vlan_ops(vsi);
> + struct net_device *netdev = vsi->netdev;
> + struct ice_hw *hw = &vsi->back->hw;
> + bool restore_mc;
> + int err;
> +
> + restore_mc = (vsi->current_netdev_flags & IFF_ALLMULTI) &&
> + ice_vsi_has_non_zero_vlans(vsi);
> +
> + if (restore_mc) {
> + err = ice_fltr_set_vlan_vsi_promisc(hw, vsi,
> + ICE_MCAST_VLAN_PROMISC_BITS);
> + if (err && err != -EEXIST)
> + return err;
> + }
> +
> + err = ice_clear_dflt_vsi(vsi);
> + if (err)
> + return err;
> +
> + if (netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
> + vlan_ops->ena_rx_filtering(vsi);
> +
> + if (restore_mc)
> + ice_fltr_clear_vsi_promisc(hw, vsi->idx, ICE_MCAST_PROMISC_BITS,
> + 0);
> +
> + return 0;
> +}
> +
> +/* Drop the per-VID multicast promisc rules made redundant by the default
> + * VSI Rx rule; best-effort, a leftover is harmless while that rule stands.
> + */
> +static void ice_vsi_clear_vlan_mc_promisc(struct ice_vsi *vsi)
> +{
> + if (!ice_vsi_has_non_zero_vlans(vsi))
> + return;
> +
> + ice_fltr_clear_vlan_vsi_promisc(&vsi->back->hw, vsi,
> + ICE_MCAST_VLAN_PROMISC_BITS);
> +}
> +
> /**
> * ice_vsi_sync_fltr - Update the VSI filter list to the HW
> * @vsi: ptr to the VSI
> @@ -429,30 +496,31 @@ static int ice_vsi_sync_fltr(struct ice_vsi *vsi)
> err = 0;
> vlan_ops->dis_rx_filtering(vsi);
>
> - /* promiscuous mode implies allmulticast so
> - * that VSIs that are in promiscuous mode are
> - * subscribed to multicast packets coming to
> - * the port
> + /* a prior allmulti pass may have added per-VID
> + * rules now covered by the DFLT rule
> */
> - err = ice_set_promisc(vsi,
> - ICE_MCAST_PROMISC_BITS);
> - if (err)
> - goto out_promisc;
> + ice_vsi_clear_vlan_mc_promisc(vsi);
> }
> +
> + /* Promiscuous mode implies allmulticast. Subscribe
> + * the VSI to all multicast even when the default VSI
> + * rule is already in use and the block above is
> + * skipped (it may be owned by another VSI, or
> + * preserved across a switchdev session); the unicast
> + * catch-all does not cover the multicast subscription.
> + */
> + err = ice_set_promisc(vsi, ICE_MCAST_PROMISC_BITS);
> + if (err)
> + goto out_promisc;
> } else {
> /* Clear Rx filter to remove traffic from wire */
> if (ice_is_vsi_dflt_vsi(vsi)) {
> - err = ice_clear_dflt_vsi(vsi);
> + err = ice_vsi_exit_dflt_promisc(vsi);
> if (err) {
> - netdev_err(netdev, "Error %d clearing default VSI %i Rx rule\n",
> - err, vsi->vsi_num);
> vsi->current_netdev_flags |=
> IFF_PROMISC;
> goto out_promisc;
> }
> - if (vsi->netdev->features &
> - NETIF_F_HW_VLAN_CTAG_FILTER)
> - vlan_ops->ena_rx_filtering(vsi);
> }
>
> /* disable allmulti here, but only if allmulti is not
> @@ -3676,10 +3744,9 @@ int ice_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid)
> while (test_and_set_bit(ICE_CFG_BUSY, vsi->state))
> usleep_range(1000, 2000);
>
> - /* Add multicast promisc rule for the VLAN ID to be added if
> - * all-multicast is currently enabled.
> - */
> - if (vsi->current_netdev_flags & IFF_ALLMULTI) {
> + /* skip the per-VID rule when the DFLT Rx rule already covers this VID */
> + if ((vsi->current_netdev_flags & IFF_ALLMULTI) &&
> + !ice_is_vsi_dflt_vsi(vsi)) {
> ret = ice_fltr_set_vsi_promisc(&vsi->back->hw, vsi->idx,
> ICE_MCAST_VLAN_PROMISC_BITS,
> vid);
^ permalink raw reply
* Re: [PATCH net-next v5] net: mana: Add Interrupt Moderation support
From: patchwork-bot+netdevbpf @ 2026-07-09 9:00 UTC (permalink / raw)
To: Haiyang Zhang
Cc: linux-hyperv, netdev, kys, haiyangz, wei.liu, decui, longli,
andrew+netdev, davem, edumazet, kuba, pabeni, kotaranov, horms,
ernis, dipayanroy, shradhagupta, gargaditya, sdf, leitao,
linux-kernel, linux-rdma, paulros
In-Reply-To: <20260702220123.815018-1-haiyangz@linux.microsoft.com>
Hello:
This patch was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Thu, 2 Jul 2026 15:01:04 -0700 you wrote:
> From: Haiyang Zhang <haiyangz@microsoft.com>
>
> Add Static and Dynamic Interrupt Moderation (DIM) support for
> Rx and Tx.
> Update queue creation procedure with new data struct with the related
> settings.
> Add functions to collect stat for DIM, and workers to update DIM data
> and settings.
> Update ethtool handler to get/set the moderation settings from a user.
> To avoid detach/re-attach ops, ring DIM doorbell to change settings
> at run time.
> By default, adaptive-rx/tx (DIM) are enabled if supported by HW.
>
> [...]
Here is the summary with links:
- [net-next,v5] net: mana: Add Interrupt Moderation support
https://git.kernel.org/netdev/net-next/c/433f482add31
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH iwl-next] idpf: add flow-based XDP fallback for FWs without Tx FIFO support
From: Przemek Kitszel @ 2026-07-09 9:13 UTC (permalink / raw)
To: Alexander Lobakin
Cc: Tony Nguyen, intel-wired-lan, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
nxne.cnse.osdt.itp.upstreaming, netdev, linux-kernel
In-Reply-To: <20260708151327.1091570-1-aleksander.lobakin@intel.com>
On 7/8/26 17:13, Alexander Lobakin wrote:
> From the first days of XDP implementation in idpf, it relied and
> worked solely on top of the queue-based scheduling Tx mode, which
> basically means simple FIFO. However, turned out not every firmware
> supports this mode and XDP doesn't work there at all.
>
> Since the flow-based scheduling Tx mode is mandatory and supported
> by every FW, introduce a simple fallback guarded by a static key
> to not hurt the more performant mode. The FB mode generates a
> completion for each Tx descriptor and never guarantees that there
> won't be any out-of-order completions. Serialize that using a
> bitmap of completed descriptors and report contiguous blocks of
> free bits to match XDP and XSk expectations and avoid further
> code complication.
>
> The usage of a bitmap on hotpath might sound scary, but this
> fallback is able to reach around 70% of the QB mode's performance,
> which is comparable to what ice gives us. The main bottlenecks are
> unlikely()s and one completion per each descriptor, while in the QB
> mode we have one completion per batch (which might contain 64 or
> even 128 frames), plus the size of the completion descriptor is
> 8 bytes in this mode (4 bytes in the QB mode), which means a lot
> of additional PCI traffic.
>
> bloat-o-meter shows .text increase in about 2 Kb without adding new
> functions or uninlining any of the existing ones. I played a bunch
> with inlining and uninlining certain pieces or the whole fallback,
> but the compiler collapses and optimizes libeth templates so hardly
> so that each additional external call only makes things worse.
>
> Signed-off-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
^ permalink raw reply
* [PATCH v2 0/1] macvlan: allow source mode devices along with passthru
From: Thomas Martitz @ 2026-07-09 9:11 UTC (permalink / raw)
To: Simon Horman, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, open list:NETWORKING DRIVERS,
open list
Cc: Thomas Martitz, open list:NETWORKING DRIVERS, open list
In-Reply-To: <20260702065642.1783764-1-t.martitz@fritz.com>
Hello,
we're trying to solve a use case on our devices where two SoC are
connected on the same board, using the only available high-speed interface.
One SoC runs the main Linux system including the full routing stack
(FRITZ!OS) and the other SoC implements most of the GPON ONT side.
The high-speed interface is of course also used for the user traffic.
Therefore we must tell the inter-SoC traffic apart from the user traffic.
We achieve this by matching the well-known MAC address of the ONT SoC.
The user traffic passes through the ONT SoC without modifying MAC headers.
Now we would like to use macvlan (with source mode devices) on the main
SoC side for this but our routing stack requires the rx_handler to be
available. Therefore macvlan is currently not an option.
With this patch macvlan becomes an option because the current limitation
of either "one passthru device" or "any other configuration" is relaxed
for the combination of passthru and any number of source mode devices.
This allows us to configure a source mode device for the other SoC and
register an rx_handler for further processing on the passthru device.
Thanks in advance!
Thomas Martitz (1):
macvlan: allow source mode devices along with passthru
drivers/net/macvlan.c | 107 ++++++++++++++++++++++++++++--------------
1 file changed, 73 insertions(+), 34 deletions(-)
--
2.54.0
^ permalink raw reply
* [PATCH v2 1/1] macvlan: allow source mode devices along with passthru
From: Thomas Martitz @ 2026-07-09 9:11 UTC (permalink / raw)
To: Simon Horman, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, open list:NETWORKING DRIVERS,
open list
Cc: Thomas Martitz, open list:NETWORKING DRIVERS, open list
In-Reply-To: <20260709091138.170348-1-t.martitz@fritz.com>
This allows for configurations where there are a few
known senders in the system (e.g. multiple SoCs on the same
board) along with unlimited external senders.
The source mode devices represent the known senders while
all external senders terminate on passthru device.
Although you could still receive packets on the lower device
without the need for the passthru vlan device, there
are use cases where you need additional packet processing
in the pipeline that hooks via rx_handler. But the rx_handler
is already bound to the macvlan port.
With this patch the rx_handler can be attached to the passthru
device while macvlan itself remains attached to the lower device.
We use this to use the share the only available high-speed interface
for inter-SoC networking and external networking (user traffic).
Inter-SoC packets are received on the source mode interface, identified
by a well-known source address. Some of our chips have simply no other
viable link for inter-SoC traffic.
Signed-off-by: Thomas Martitz <t.martitz@fritz.com>
---
drivers/net/macvlan.c | 107 ++++++++++++++++++++++++++++--------------
1 file changed, 73 insertions(+), 34 deletions(-)
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 9a4bc99dbf53b..2505f5f2e9d30 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -83,6 +83,11 @@ static inline void macvlan_set_passthru(struct macvlan_port *port)
port->flags |= MACVLAN_F_PASSTHRU;
}
+static inline void macvlan_clear_passthru(struct macvlan_port *port)
+{
+ port->flags &= ~MACVLAN_F_PASSTHRU;
+}
+
static inline bool macvlan_addr_change(const struct macvlan_port *port)
{
return port->flags & MACVLAN_F_ADDRCHANGE;
@@ -637,7 +642,7 @@ static int macvlan_open(struct net_device *dev)
struct net_device *lowerdev = vlan->lowerdev;
int err;
- if (macvlan_passthru(vlan->port)) {
+ if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
if (!(vlan->flags & MACVLAN_FLAG_NOPROMISC)) {
err = dev_set_promiscuity(lowerdev, 1);
if (err < 0)
@@ -712,7 +717,7 @@ static int macvlan_stop(struct net_device *dev)
dev_uc_unsync(lowerdev, dev);
dev_mc_unsync(lowerdev, dev);
- if (macvlan_passthru(vlan->port)) {
+ if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
if (!(vlan->flags & MACVLAN_FLAG_NOPROMISC))
dev_set_promiscuity(lowerdev, -1);
goto hash_del;
@@ -792,6 +797,23 @@ static int macvlan_set_mac_address(struct net_device *dev, void *p)
return macvlan_sync_address(dev, addr->__data);
}
+static void macvlan_port_release_mac(struct net_device *dev)
+{
+ struct macvlan_port *port = macvlan_port_get_rtnl(dev);
+
+ /* If the lower device address has been changed by passthru
+ * macvlan, put it back.
+ */
+ if (macvlan_passthru(port) &&
+ !ether_addr_equal(port->dev->dev_addr, port->perm_addr)) {
+ struct sockaddr_storage ss;
+
+ ss.ss_family = port->dev->type;
+ memcpy(&ss.__data, port->perm_addr, port->dev->addr_len);
+ dev_set_mac_address(port->dev, &ss, NULL);
+ }
+}
+
static void macvlan_change_rx_flags(struct net_device *dev, int change)
{
struct macvlan_dev *vlan = netdev_priv(dev);
@@ -977,8 +999,18 @@ static void macvlan_uninit(struct net_device *dev)
macvlan_flush_sources(port, vlan);
port->count -= 1;
- if (!port->count)
- macvlan_port_destroy(port->dev);
+ if (port->count) {
+ /* In case of remaining source interfaces undo
+ * passthru-specific properties.
+ */
+ if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
+ macvlan_port_release_mac(dev);
+ macvlan_clear_passthru(vlan->port);
+ }
+ return;
+ }
+
+ macvlan_port_destroy(port->dev);
}
static void macvlan_dev_get_stats64(struct net_device *dev,
@@ -1052,7 +1084,7 @@ static int macvlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
/* Support unicast filter only on passthru devices.
* Multicast filter should be allowed on all devices.
*/
- if (!macvlan_passthru(vlan->port) && is_unicast_ether_addr(addr))
+ if (vlan->mode != MACVLAN_MODE_PASSTHRU && is_unicast_ether_addr(addr))
return -EOPNOTSUPP;
if (flags & NLM_F_REPLACE)
@@ -1077,7 +1109,7 @@ static int macvlan_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
/* Support unicast filter only on passthru devices.
* Multicast filter should be allowed on all devices.
*/
- if (!macvlan_passthru(vlan->port) && is_unicast_ether_addr(addr))
+ if (vlan->mode != MACVLAN_MODE_PASSTHRU && is_unicast_ether_addr(addr))
return -EOPNOTSUPP;
if (is_unicast_ether_addr(addr))
@@ -1308,17 +1340,7 @@ static void macvlan_port_destroy(struct net_device *dev)
kfree_skb(skb);
}
- /* If the lower device address has been changed by passthru
- * macvlan, put it back.
- */
- if (macvlan_passthru(port) &&
- !ether_addr_equal(port->dev->dev_addr, port->perm_addr)) {
- struct sockaddr_storage ss;
-
- ss.ss_family = port->dev->type;
- memcpy(&ss.__data, port->perm_addr, port->dev->addr_len);
- dev_set_mac_address(port->dev, &ss, NULL);
- }
+ macvlan_port_release_mac(dev);
kfree(port);
}
@@ -1506,15 +1528,6 @@ int macvlan_common_newlink(struct net_device *dev,
}
port = macvlan_port_get_rtnl(lowerdev);
- /* Only 1 macvlan device can be created in passthru mode */
- if (macvlan_passthru(port)) {
- /* The macvlan port must be not created this time,
- * still goto destroy_macvlan_port for readability.
- */
- err = -EINVAL;
- goto destroy_macvlan_port;
- }
-
vlan->lowerdev = lowerdev;
vlan->dev = dev;
vlan->port = port;
@@ -1527,10 +1540,30 @@ int macvlan_common_newlink(struct net_device *dev,
if (data && data[IFLA_MACVLAN_FLAGS])
vlan->flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]);
+ /* Only 1 macvlan device can be created in passthru mode. There may be
+ * additional source mode devices but nothing else at the moment.
+ *
+ * First check if adding a source mode device to an existing passthru vlan.
+ */
+ if (macvlan_passthru(port) && vlan->mode != MACVLAN_MODE_SOURCE) {
+ /* The macvlan port must be not created this time,
+ * still goto destroy_macvlan_port for readability.
+ */
+ err = -EINVAL;
+ goto destroy_macvlan_port;
+ }
+
+ /* Now check if adding a passthru device to an existing set of source mode
+ * devices.
+ */
if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
- if (port->count) {
- err = -EINVAL;
- goto destroy_macvlan_port;
+ struct macvlan_dev *p;
+
+ list_for_each_entry(p, &port->vlans, list) {
+ if (p->mode != MACVLAN_MODE_SOURCE) {
+ err = -EINVAL;
+ goto destroy_macvlan_port;
+ }
}
macvlan_set_passthru(port);
eth_hw_addr_inherit(dev, lowerdev);
@@ -1564,7 +1597,11 @@ int macvlan_common_newlink(struct net_device *dev,
if (err)
goto unregister_netdev;
- list_add_tail_rcu(&vlan->list, &port->vlans);
+ /* macvlan_handle_frame expects the (one and only) passthru device first. */
+ if (vlan->mode == MACVLAN_MODE_PASSTHRU)
+ list_add_rcu(&vlan->list, &port->vlans);
+ else
+ list_add_tail_rcu(&vlan->list, &port->vlans);
update_port_bc_queue_len(vlan->port);
netif_stacked_transfer_operstate(lowerdev, dev);
linkwatch_fire_event(dev);
@@ -1627,9 +1664,11 @@ static int macvlan_changelink(struct net_device *dev,
if (data && data[IFLA_MACVLAN_MODE]) {
set_mode = true;
mode = nla_get_u32(data[IFLA_MACVLAN_MODE]);
- /* Passthrough mode can't be set or cleared dynamically */
- if ((mode == MACVLAN_MODE_PASSTHRU) !=
- (vlan->mode == MACVLAN_MODE_PASSTHRU))
+ /* Passthrough mode can't be set or cleared dynamically,
+ * regardless of existing source interfaces. Furthermore, source
+ * interfaces can't switch modes within a passhtrough port.
+ */
+ if (macvlan_passthru(vlan->port) && mode != vlan->mode)
return -EINVAL;
if (vlan->mode == MACVLAN_MODE_SOURCE &&
vlan->mode != mode)
@@ -1639,7 +1678,7 @@ static int macvlan_changelink(struct net_device *dev,
if (data && data[IFLA_MACVLAN_FLAGS]) {
__u16 flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]);
bool promisc = (flags ^ vlan->flags) & MACVLAN_FLAG_NOPROMISC;
- if (macvlan_passthru(vlan->port) && promisc) {
+ if (vlan->mode == MACVLAN_MODE_PASSTHRU && promisc) {
int err;
if (flags & MACVLAN_FLAG_NOPROMISC)
--
2.54.0
^ permalink raw reply related
* Re: [PATCH net v2 2/2] vsock/test: add test for small packets under pressure
From: Stefano Garzarella @ 2026-07-09 9:17 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: netdev, Jason Wang, Xuan Zhuo, Eric Dumazet, Eugenio Pérez,
Simon Horman, Stefan Hajnoczi, David S. Miller, linux-kernel, kvm,
Paolo Abeni, virtualization, Jakub Kicinski, Jason Wang
In-Reply-To: <20260708065853-mutt-send-email-mst@kernel.org>
On Wed, Jul 08, 2026 at 06:59:41AM -0400, Michael S. Tsirkin wrote:
>On Wed, Jul 08, 2026 at 12:29:04PM +0200, Stefano Garzarella wrote:
>> From: Stefano Garzarella <sgarzare@redhat.com>
>>
>> Add a test that sends 2 MB of data using randomly sized small packets
>> (129-512 bytes) over a SOCK_STREAM connection. Packets above
>> GOOD_COPY_LEN (128) bypass the in-place coalescing in recv_enqueue(),
>> forcing each one into its own skb.
>>
>> Without receive queue collapsing, the per-skb overhead eventually
>> exceeds buf_alloc and the connection is reset. The test verifies
>> that all data arrives and that content integrity is preserved.
>>
>> Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
>
>maybe cut down SO_VM_SOCKETS_BUFFER_SIZE? will make it easier to
>trigger?
Currently, with the default value, the trigger is practically immediate
for packets between 129 and 512 bytes, but yes, a smaller buffer size
certainly makes this effect even more pronounced.
>
>anyway
>
>Acked-by: Michael S. Tsirkin <mst@redhat.com>
Thanks!
If I need to post a v3, I'll add it; otherwise, I guess we can leave it
as is or send a follow-up for net-next.
Thanks,
Stefano
^ permalink raw reply
* Re: [PATCH] net: erspan: set lltx to avoid sch_direct_xmit deadlock
From: Zhou, Yun @ 2026-07-09 9:18 UTC (permalink / raw)
To: Paolo Abeni, dsahern, idosch, davem, edumazet, kuba, horms
Cc: netdev, linux-kernel
In-Reply-To: <695fc2fb-606e-4d5a-a4d6-41ba5d1cfadb@redhat.com>
On 7/9/26 16:16, Paolo Abeni wrote:
> CAUTION: This email comes from a non Wind River email account!
> Do not click links or open attachments unless you recognize the sender and know the content is safe.
>
> On 7/9/26 8:56 AM, Yun Zhou wrote:
>> erspan_xmit() re-enters the network stack via ip_tunnel_xmit(), causing
>> nested acquisition of _xmit_lock on the underlay device while already
>> holding the ERSPAN device's _xmit_lock. Both are ARPHRD_ETHER and share
>> the same lockdep class, creating an ABBA deadlock:
>>
>> sch_direct_xmit [lock erspan] -> erspan_xmit -> ip_tunnel_xmit ->
>> ip_output -> __dev_queue_xmit -> sch_direct_xmit [lock underlay]
>>
>> Set dev->lltx = true so HARD_TX_LOCK() skips the spinlock for ERSPAN.
>> This is safe as erspan_xmit() has no shared mutable state: o_seqno is
>> atomic, stats use atomic_long_inc, and dst_cache is per-CPU. GRETAP,
>> the sibling device with identical xmit structure, already sets lltx.
>>
>> Closes: https://syzkaller.appspot.com/bug?extid=9bda1b9fbb7fbdf9b62b
>> Reported-by: syzbot+9bda1b9fbb7fbdf9b62b@syzkaller.appspotmail.com
>> Fixes: 84e54fe0a5ea ("gre: introduce native tunnel support for ERSPAN")
>> Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
> So this is actually a v2 of:
>
> 20260630183702.170798-2-hemendranaik@gmail.com/20260630183702.170798-2-hemendranaik@gmail.com/
>
> right?
>
> please have a look at:
>
> https://elixir.bootlin.com/linux/v7.1/source/Documentation/process/maintainer-netdev.rst#L434
>
> any kind of re-submission matters, no matter how trivial is the reason.
>
> /P
>
I sincerely apologize for the duplicate patch submission. It was a minor
oversight caused by a misconfigured Git in my new environment. However,
it seems this email was sent to hemendranaik@gmail.com, right?
BR,
Yun
^ permalink raw reply
* Re: [PATCH net 1/9] batman-adv: ensure minimal ethernet header on TX
From: patchwork-bot+netdevbpf @ 2026-07-09 9:20 UTC (permalink / raw)
To: Simon Wunderlich
Cc: netdev, davem, edumazet, kuba, pabeni, horms, b.a.t.m.a.n, sven,
stable, sashiko-bot
In-Reply-To: <20260708091821.314516-2-sw@simonwunderlich.de>
Hello:
This series was applied to netdev/net.git (main)
by Sven Eckelmann <sven@narfation.org>:
On Wed, 8 Jul 2026 11:18:13 +0200 you wrote:
> From: Sven Eckelmann <sven@narfation.org>
>
> As documented in commit 8bd67ebb50c0 ("net: bridge: xmit: make sure we have
> at least eth header len bytes"), it is possible by for a local user with
> eBPF TC hook access to attach a tc filter which truncates the packet and
> redirects to an batadv interface. But the code assumes that at least
> ETH_HLEN bytes are available and thus might read outside of the available
> buffer.
>
> [...]
Here is the summary with links:
- [net,1/9] batman-adv: ensure minimal ethernet header on TX
https://git.kernel.org/netdev/net/c/49df66b7993c
- [net,2/9] batman-adv: fix VLAN priority offset
https://git.kernel.org/netdev/net/c/fdb3be00ba4d
- [net,3/9] batman-adv: clean untagged VLAN on netdev registration failure
https://git.kernel.org/netdev/net/c/8669a550c752
- [net,4/9] batman-adv: tt: avoid request storms during pending request
https://git.kernel.org/netdev/net/c/27c7d4000823
- [net,5/9] batman-adv: tt: prevent TVLV OOB check overflow
https://git.kernel.org/netdev/net/c/7a581d9aaba8
- [net,6/9] batman-adv: frag: free unfragmentable packet
https://git.kernel.org/netdev/net/c/6b628425aed4
- [net,7/9] batman-adv: frag: fix primary_if leak on failed linearization
https://git.kernel.org/netdev/net/c/353d2c1d5492
- [net,8/9] batman-adv: mcast: avoid OOB read of num_dests header
https://git.kernel.org/netdev/net/c/38eaed28e250
- [net,9/9] batman-adv: dat: fix tie-break for candidate selection
https://git.kernel.org/netdev/net/c/98052bdaf6ac
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v1 net-next] net: phy: Drop #inclusion of <linux/mod_devicetable.h> from <linux/mdio.h>
From: patchwork-bot+netdevbpf @ 2026-07-09 9:20 UTC (permalink / raw)
To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig_=28The_Capable_Hub=29_=3Cu=2Ekleine-koenig?=,
=?utf-8?q?=40baylibre=2Ecom=3E?=
Cc: andrew, hkallweit1, linux, netdev, linux-kernel
In-Reply-To: <ca270a534d0f230a939a3fb4a661808b35d6436d.1783329817.git.u.kleine-koenig@baylibre.com>
Hello:
This patch was applied to netdev/net-next.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Mon, 6 Jul 2026 11:29:19 +0200 you wrote:
> <linux/mdio.h> itself doesn't use any of the device id structures. The
> files #including <linux/mdio.h> use a variety of them:
>
> $ git grep -l '<linux/mdio.h>' | xargs grep --color -E '\<(acpi_device_id|amba_id|ap_device_id|apr_device_id|auxiliary_device_id|bcma_device_id|ccw_device_id|cdx_device_id|coreboot_device_id|css_device_id|dfl_device_id|dmi_(device|system)_id|eisa_device_id|fsl_mc_device_id|hda_device_id|hid_device_id|hv_vmbus_device_id|i2c_device_id|i3c_device_id|ieee1394_device_id|input_device_id|ipack_device_id|isapnp_device_id|ishtp_device_id|mcb_device_id|mdio_device_id|mei_cl_device_id|mhi_device_id|mips_cdmm_device_id|of_device_id|parisc_device_id|pci_device_id|pci_epf_device_id|pcmcia_device_id|platform_device_id|pnp_(card_)?device_id|rio_device_id|rpmsg_device_id|sdio_device_id|sdw_device_id|serio_device_id|slim_device_id|spi_device_id|spmi_device_id|ssam_device_id|ssb_device_id|tb_service_id|tee_client_device_id|typec_device_id|ulpi_device_id|usb_device_id|vchiq_device_id|virtio_device_id|wmi_device_id|x86_(cpu|device)_id|zorro_device_id|cpu_feature)\>'
> ...
>
> but none of them relies on <linux/mdio.h>'s #include.
>
> [...]
Here is the summary with links:
- [v1,net-next] net: phy: Drop #inclusion of <linux/mod_devicetable.h> from <linux/mdio.h>
https://git.kernel.org/netdev/net-next/c/6d86ce0da0d5
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v4 1/3] drm/drm_ras: Add drm_ras netlink error event
From: Raag Jadav @ 2026-07-09 9:22 UTC (permalink / raw)
To: Tauro, Riana
Cc: Rodrigo Vivi, kuba, aravind.iddamsetty, intel-xe, anshuman.gupta,
joonas.lahtinen, simona.vetter, airlied, pratik.bari,
joshua.santosh.ranjan, ashwin.kumar.kulkarni, shubham.kumar,
ravi.kishore.koppuravuri, maarten.lankhorst, mallesh.koujalagi,
soham.purkait, Zack McKevitt, Lijo Lazar, Hawking Zhang,
David S. Miller, Paolo Abeni, Eric Dumazet, dri-devel, netdev
In-Reply-To: <d96a4c3f-e0e7-42d7-bb8a-994b2f408dea@intel.com>
On Thu, Jul 09, 2026 at 10:45:27AM +0530, Tauro, Riana wrote:
> On 09-07-2026 01:51, Rodrigo Vivi wrote:
> > On Tue, Jul 07, 2026 at 12:02:11PM +0530, Tauro, Riana wrote:
> > > Hi Rodrigo/Jakub/Aravind
> > >
> > > Please let me know if you have any feedback for this patch or can you please
> > > ack this if it looks good to you.
> > I looks good to me, but could you please double check the sashiko's comments?
>
> Sashiko has a comment regarding namespaces
>
> [Severity: Medium]Since the generic netlink family explicitly supports
> multiple networknamespaces by setting .netnsok = true,
> will hardcoding init_net hereprevent listeners in non-init namespaces from
> receiving error events
>
> .netnsok = true is auto generated not explicitly added in code.
Because that's what ynl_gen_c.py does without it being parsed from
anywhere.
> But from what i see, drm device is present in the host and not replicated
> across namespaces
> and most of the non-network implementations use inet. (ex: Binder)
> If this really needs a fix, we can drop the has_listeners suggested by raag
> in previous patch.
> I don't think in an error path, allocating a new buffer would be a big
> overhead if listeners are not present.
I don't know enough about namespaces to comment on this. I'll rely on
Jakub's disposition if netnsok is relevant here, or needed at all for
our usecase.
Raag
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH iwl-next] idpf: add flow-based XDP fallback for FWs without Tx FIFO support
From: Loktionov, Aleksandr @ 2026-07-09 9:29 UTC (permalink / raw)
To: Lobakin, Aleksander, intel-wired-lan@lists.osuosl.org
Cc: Lobakin, Aleksander, Nguyen, Anthony L, Kitszel, Przemyslaw,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, NXNE CNSE OSDT ITP Upstreaming,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20260708151327.1091570-1-aleksander.lobakin@intel.com>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> Of Alexander Lobakin
> Sent: Wednesday, July 8, 2026 5:13 PM
> To: intel-wired-lan@lists.osuosl.org
> Cc: Lobakin, Aleksander <aleksander.lobakin@intel.com>; Nguyen,
> Anthony L <anthony.l.nguyen@intel.com>; Kitszel, Przemyslaw
> <przemyslaw.kitszel@intel.com>; Andrew Lunn <andrew+netdev@lunn.ch>;
> David S. Miller <davem@davemloft.net>; Eric Dumazet
> <edumazet@google.com>; Jakub Kicinski <kuba@kernel.org>; Paolo Abeni
> <pabeni@redhat.com>; Simon Horman <horms@kernel.org>; NXNE CNSE OSDT
> ITP Upstreaming <nxne.cnse.osdt.itp.upstreaming@intel.com>;
> netdev@vger.kernel.org; linux-kernel@vger.kernel.org
> Subject: [Intel-wired-lan] [PATCH iwl-next] idpf: add flow-based XDP
> fallback for FWs without Tx FIFO support
>
> From the first days of XDP implementation in idpf, it relied and
> worked solely on top of the queue-based scheduling Tx mode, which
> basically means simple FIFO. However, turned out not every firmware
> supports this mode and XDP doesn't work there at all.
>
> Since the flow-based scheduling Tx mode is mandatory and supported by
> every FW, introduce a simple fallback guarded by a static key to not
> hurt the more performant mode. The FB mode generates a completion for
> each Tx descriptor and never guarantees that there won't be any out-
> of-order completions. Serialize that using a bitmap of completed
> descriptors and report contiguous blocks of free bits to match XDP and
> XSk expectations and avoid further code complication.
>
> The usage of a bitmap on hotpath might sound scary, but this fallback
> is able to reach around 70% of the QB mode's performance, which is
> comparable to what ice gives us. The main bottlenecks are unlikely()s
> and one completion per each descriptor, while in the QB mode we have
> one completion per batch (which might contain 64 or even 128 frames),
> plus the size of the completion descriptor is
> 8 bytes in this mode (4 bytes in the QB mode), which means a lot of
> additional PCI traffic.
>
> bloat-o-meter shows .text increase in about 2 Kb without adding new
> functions or uninlining any of the existing ones. I played a bunch
> with inlining and uninlining certain pieces or the whole fallback, but
> the compiler collapses and optimizes libeth templates so hardly so
> that each additional external call only makes things worse.
>
> Signed-off-by: Alexander Lobakin <aleksander.lobakin@intel.com>
> ---
> drivers/net/ethernet/intel/idpf/idpf.h | 1 +
> drivers/net/ethernet/intel/idpf/idpf_txrx.h | 11 +-
> drivers/net/ethernet/intel/idpf/xdp.h | 60 +++++++++-
> include/net/libeth/xdp.h | 13 +++
> drivers/net/ethernet/intel/idpf/idpf_txrx.c | 10 +-
> drivers/net/ethernet/intel/idpf/xdp.c | 115
> +++++++++++++++++++-
> 6 files changed, 201 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/idpf/idpf.h
> b/drivers/net/ethernet/intel/idpf/idpf.h
> index ec1b75f039bb..5414bbbee07a 100644
> --- a/drivers/net/ethernet/intel/idpf/idpf.h
> +++ b/drivers/net/ethernet/intel/idpf/idpf.h
> @@ -384,6 +384,7 @@ struct idpf_vport {
> struct idpf_tx_queue **txqs;
> u16 num_txq;
> u16 num_xdp_txq;
...
> old = cfg->user_config.xdp_prog;
> cfg->user_config.xdp_prog = prog;
>
> --
> 2.55.0
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
^ permalink raw reply
* Re: [PATCH net v3] tipc: fix u16 MTU truncation in media and bearer MTU validation
From: Vadim Fedorenko @ 2026-07-09 9:29 UTC (permalink / raw)
To: Cen Zhang (Microsoft)
Cc: AutonomousCodeSecurity, davem, edumazet, horms, jmaloy, kuba, kys,
linux-kernel, netdev, pabeni, tgopinath, tipc-discussion,
tung.quang.nguyen
In-Reply-To: <20260708224129.3722-1-blbllhy@gmail.com>
On 08/07/2026 23:41, Cen Zhang (Microsoft) wrote:
> Sadly NLA_POLICY_MAX() cannot be used here -- its .max field
> is s16 instead of u16 in struct nla_policy, which will
> overflow to -1 during my testing.
>
> Please let me know if we have any other better choices.
> Otherwise, I'll prepare a patch adding .min check (
> TIPC_MIN_BEARER_MTU).
Ah, yeah, you are right, let's keep full range check
^ permalink raw reply
* Re: [RESEND PATCH] net/mlx5: Fix L3 tunnel entropy refcount leak
From: patchwork-bot+netdevbpf @ 2026-07-09 9:30 UTC (permalink / raw)
To: lirongqing
Cc: saeedm, leon, tariqt, mbloch, andrew+netdev, davem, edumazet,
kuba, pabeni, horms, elibr, roid, eli, netdev, linux-rdma,
linux-kernel
In-Reply-To: <20260703141423.1723-1-lirongqing@baidu.com>
Hello:
This patch was applied to netdev/net.git (main)
by Paolo Abeni <pabeni@redhat.com>:
On Fri, 3 Jul 2026 22:14:23 +0800 you wrote:
> From: Li RongQing <lirongqing@baidu.com>
>
> mlx5_tun_entropy_refcount_inc() counts both VXLAN and L2-to-L3
> tunnel reformat entries as entropy-enabling users. The matching
> decrement path only handled VXLAN, leaving L2-to-L3 tunnel entries
> counted after release.
>
> [...]
Here is the summary with links:
- [RESEND] net/mlx5: Fix L3 tunnel entropy refcount leak
https://git.kernel.org/netdev/net/c/c914307e1d41
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v12 nf-next 0/7] netfilter: Add bridge-fastpath
From: Nikolay Aleksandrov @ 2026-07-09 9:34 UTC (permalink / raw)
To: Pablo Neira Ayuso, Eric Woudstra
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Florian Westphal, Phil Sutter,
Ido Schimmel, Kuniyuki Iwashima, Stanislav Fomichev,
Samiullah Khawaja, Hangbin Liu, Krishna Kumar, Martin Karsten,
netdev, netfilter-devel, bridge
In-Reply-To: <ak4culZgTe8CNSva@chamomile>
On 08/07/2026 12:47, Pablo Neira Ayuso wrote:
> Hi Eric,
>
> On Tue, Jul 07, 2026 at 11:10:38AM +0200, Eric Woudstra wrote:
>> This patchset makes it possible to set up a software fastpath between
>> bridged interfaces. One patch adds the flow rule for the hardware
>> fastpath. This creates the possibility to have a hardware offloaded
>> fastpath between bridged interfaces. More patches are added to solve
>> issues found with the existing code.
>
> Thanks for your series.
>
> I posted an alternative series, including one of your patches for the
> bridge vlan filtering support (which is still untested on my side):
>
> https://lore.kernel.org/netfilter-devel/20260708093250.1187068-1-pablo@netfilter.org/T/#m270aedab59bf39f1bc4452d1d8d739a2b1b0bc45
Hi Pablo,
I think I haven't been CCed on that posting, can't find it in my inbox.
Anyway, I know I've acked the patch but taking a second look I think there might
be a problem, specifically at patch 01:
+ if (netif_is_bridge_port(ctx->dev)) {
+ struct net_device *br_dev;
+
+ br_dev = netdev_master_upper_dev_get_rcu((struct net_device *)ctx->dev);
+ if (!br_dev)
+ return -1;
- br = netdev_priv(ctx->dev);
+ src = br_port_get_rcu(ctx->dev);
+ br = netdev_priv(br_dev);
+ } else {
+ src = NULL;
+ br = netdev_priv(ctx->dev);
+ }
If ndo_fill_forward_path can be called while a port is being removed from the
bridge, then we might reach this call and netif_is_bridge_port() can be false
since the flag is removed before the synchronize_net() done by rx handler
unregistering. Specifically if CONFIG_BRIDGE_VLAN_FILTERING is not defined
then the previous synchronize_net/rcu are not done and I think we can observe
a port which is being dismantled in ndo_fill_forward_path without the flag and
erroneously categorized as a bridge device. I think a safer and correct approach
would be to check if the device is a bridge master:
} else if (netif_is_bridge_master(ctx->dev)) {
...
Cheers,
Nik
^ permalink raw reply
* RE: [PATCH iwl-next v1 3/6] ixgbe: E610: do not disable LSE on driver down/remove
From: Jagielski, Jedrzej @ 2026-07-09 9:36 UTC (permalink / raw)
To: Simon Horman
Cc: intel-wired-lan@lists.osuosl.org, Nguyen, Anthony L,
netdev@vger.kernel.org
In-Reply-To: <20260708170145.1514345-1-horms@kernel.org>
From: Simon Horman <horms@kernel.org>
Sent: Wednesday, July 8, 2026 7:02 PM
>This is an AI-generated review of your patch. The human sending this
>email has considered the AI review valid, or at least plausible.
>Full review at: https://sashiko.dev
>---
Hi Simon,
yeah i saw most of these on sashiko local run and i believe we should
not be worried about them.
I see that sashiko does not understand some of the FW/NIC <-> driver
communication concepts for this series
>ixgbe: E610: do not disable LSE on driver down/remove
>
>This patch removes the calls to ixgbe_disable_link_status_events() in
>ixgbe_down() and ixgbe_remove() for the E610 MAC. The intent is to keep
>Link Status Events (LSE) enabled when the interface is down or unloading.
>
>> diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
>> index 882021c436d0f..83098bf522e42 100644
>> --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
>> +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
>[ ... ]
>> @@ -6733,8 +6720,6 @@ void ixgbe_down(struct ixgbe_adapter *adapter)
>>
>> ixgbe_clean_all_tx_rings(adapter);
>> ixgbe_clean_all_rx_rings(adapter);
>> - if (adapter->hw.mac.type == ixgbe_mac_e610)
>> - ixgbe_disable_link_status_events(adapter);
>
>[Severity: High]
>Does removing this call allow unhandled asynchronous LSE events to permanently
>block the ACI mailbox?
No, removing this call does the opposite - it let driver to handle events
which without that step would not be received at all.
>
>If a link event occurs while the driver is down, the firmware can queue an
>event in the ACI queue. Upon the next driver load, before the service task
>can clear events, commands like ixgbe_get_flash_data() are called.
That's not true and actually it's opposite to the idea of async events.
Events are not queued when driver is down, that's the period when it
may required to be notified by some sort of event, like eg link state change
so simply its send then
"before the service task can clear events"
they are handled in an async way, not synchronously by servise task
>
>In ixgbe_aci_send_cmd_execute(), if the EV bit is asserted, the synchronous
>response is overwritten by the unhandled asynchronous event from HIDA_2:
It still bases on assumption that for some reason event has not been handled
That's mislesading, handling mechanism is implemented and events are handled
one by one. HIDA_2 corresponds to the cmd which is proceesed in given moment.
>
> if ((hicr & IXGBE_PF_HICR_EV) && !(hicr & IXGBE_PF_HICR_C))
> for (i = 0; i < IXGBE_ACI_DESC_SIZE_IN_DWORDS; i++)
> raw_desc[i] = IXGBE_READ_REG(hw, IXGBE_PF_HIDA_2(i));
>
> if (desc->opcode != cpu_to_le16(opcode) &&
> opcode != ixgbe_aci_opc_get_fw_event)
> return -EIO;
>
>Could this result in an opcode mismatch and return -EIO for all future
>commands, causing device initialization to fail?
I really do not see such possibility
>
>> }
>>
>> /**
>[ ... ]
>> @@ -12080,9 +12065,6 @@ static void ixgbe_remove(struct pci_dev *pdev)
>> set_bit(__IXGBE_REMOVING, &adapter->state);
>> cancel_work_sync(&adapter->service_task);
>>
>> - if (adapter->hw.mac.type == ixgbe_mac_e610)
>> - ixgbe_disable_link_status_events(adapter);
>> -
>
>[Severity: High]
>Can the same mailbox blockage happen here during driver unload?
>
>If LSE is not disabled on remove, a link state change (such as a cable
>unplug) after unload could leave the ACI queue with pending events.
>
>Would this cause the same -EIO failure during the subsequent ixgbe_probe()
>when the driver is loaded again?
That's even more odd. Event is not something what is kept to be send
between driver reloads / FW reinit
>
>> if (adapter->mii_bus)
>> mdiobus_unregister(adapter->mii_bus);
>>
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH iwl-next v1 5/6] ixgbe: E610: add MAC address runtime refresh
From: Jagielski, Jedrzej @ 2026-07-09 9:37 UTC (permalink / raw)
To: Simon Horman
Cc: intel-wired-lan@lists.osuosl.org, Nguyen, Anthony L,
netdev@vger.kernel.org
In-Reply-To: <20260708170223.1514429-1-horms@kernel.org>
From: Simon Horman <horms@kernel.org>
Sent: Wednesday, July 8, 2026 7:02 PM
>This is an AI-generated review of your patch. The human sending this
>email has considered the AI review valid, or at least plausible.
>Full review at: https://netdev-ai.bots.linux.dev/sashiko/
>---
>ixgbe: E610: add MAC address runtime refresh
>
>This change polls RAR0 each service-task cycle on E610 and updates
>netdev->dev_addr, hw->mac.addr and mac_table[0].addr when firmware has
>altered the MAC. A refresh is also invoked from ixgbe_reset() so the
>address survives RAR0 clearing during init_hw().
>
>> diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
>> index df698ddd08be..eb05c2f982e8 100644
>> --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
>> +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
>> @@ -6464,6 +6464,36 @@ void ixgbe_disable_tx(struct ixgbe_adapter *adapter)
>> }
>> }
>>
>> +static void ixgbe_mac_addr_refresh(struct ixgbe_adapter *adapter)
>> +{
>> + struct net_device *netdev = adapter->netdev;
>> + struct ixgbe_hw *hw = &adapter->hw;
>> + int err;
>> +
>> + if (hw->mac.type != ixgbe_mac_e610)
>> + return;
>> +
>> + /* fetch address stored currently in RAR0 in case the addr has been
>> + * altered by FW; if so, use it as the default one
>> + */
>> + err = hw->mac.ops.get_mac_addr(hw, hw->mac.addr);
>> + if (err) {
>> + e_dev_warn("Cannot get MAC address\n");
>> + return;
>> + }
>
>[High]
>Can this overwrite hw->mac.addr with transient/garbage RAR0 contents
>before validation? ixgbe_get_mac_addr_generic() unconditionally copies
>the raw IXGBE_RAH(0)/IXGBE_RAL(0) values into the buffer passed by the
>caller and always returns 0. Here the caller passes hw->mac.addr
>itself as the destination:
>
> err = hw->mac.ops.get_mac_addr(hw, hw->mac.addr);
>
>so any transient RAR0 value (all zeros while firmware is rewriting the
>register, garbage during the PCI slot-reset/resume window, a
>multicast/zero pattern in flight) silently clobbers hw->mac.addr.
>
>The is_valid_ether_addr() check below prevents publishing to netdev,
>but it does not restore the previous hw->mac.addr.
Looking this way mac.addr ca be treated as a sort of transient buf.
It is not propagated further without addr check, what was noticed in that
report.
Then if transient value would be really fetched from the drvice register,
it is instantly overwritten in next service task run with correct addr once
register is written with it
>
>Consider reading into a local u8 buf[ETH_ALEN], validating that, and
>only then copying into hw->mac.addr.
>
>Compounding that, ixgbe_get_mac_addr_generic()'s own comment states "A
>reset of the adapter must be performed prior to calling this function
>in order for the MAC address to have been loaded from the EEPROM into
>RAR0", but in ixgbe_reset() the refresh runs before init_hw(). Can
>this ordering yield a stale/invalid RAR0 read on the reset path?
This comment is not longer valid as previous families did not support
mac addr runtime change from the mgmt side. In such case content of the
register is established.
>
>The corruption is then observable: after the refresh returns,
>ixgbe_reset() proceeds through init_hw() and ixgbe_mac_set_default_filter()
>memcpy()s hw->mac.addr into mac_table[0].addr and programs it via
>set_rar() — a corrupted hw->mac.addr can therefore end up written to
>hardware.
It was already in the HW - that's where the mac addr is taken from.
>
>> +
>> + if (ether_addr_equal(netdev->dev_addr, hw->mac.addr) ||
>> + !is_valid_ether_addr(hw->mac.addr))
>> + return;
>> +
>> + ASSERT_RTNL();
>> +
>> + eth_hw_addr_set(netdev, hw->mac.addr);
>> + ether_addr_copy(adapter->mac_table[0].addr, hw->mac.addr);
>> +
>> + call_netdevice_notifiers(NETDEV_CHANGEADDR, netdev);
>> +}
>
>[Medium]
>Does this leave hw->mac.perm_addr, netdev->perm_addr and
>netdev->addr_assign_type inconsistent with the newly published
>address? Every other MAC-handling site in ixgbe (probe,
>ixgbe_reset_hw_e610, and the equivalent flows on 82598/82599/X540/X550)
>writes RAR0 into hw->mac.perm_addr. After firmware alters the MAC at
>runtime, perm_addr will keep reporting the pre-change value, so
>consumers such as ethtool -P, bonding and netlink perm-address
>attributes will report the stale address while NET_ADDR_PERM is still
>advertised for what is now effectively a runtime-assigned MAC.
IMHO perm_address should not be changed as the name states; it can be
still restored if needed
Even after mac addr change using some mgmt tool, the fabric mac addr
still stays the same
>
>[Medium]
>This unconditionally treats RAR0 (owned by firmware/MGMT) as
>authoritative and, on the next service-task tick, silently overwrites
>any MAC previously configured via "ip link set dev X address ...":
>
> eth_hw_addr_set(netdev, hw->mac.addr);
> ether_addr_copy(adapter->mac_table[0].addr, hw->mac.addr);
> call_netdevice_notifiers(NETDEV_CHANGEADDR, netdev);
>
>No log line, no arbitration with an admin-set address, no opt-out.
>NETDEV_CHANGEADDR is also emitted from a polling context, which means
>userspace (udev, NetworkManager, bridging, bonding) can receive
>spontaneous address-change events on E610 with no correlating
>administrative action. Should the commit message describe this uAPI
>behavior change ("ip link" no longer produces a persistent MAC on
>E610), and should there be some form of arbitration or logging when
>firmware overrides an admin-configured address?
mac addr assigned by the mgmt is indeed authoritative
arbitration in that case is not applicable
>
>> +
>> void ixgbe_reset(struct ixgbe_adapter *adapter)
>> {
>> struct ixgbe_hw *hw = &adapter->hw;
>> @@ -6481,6 +6511,8 @@ void ixgbe_reset(struct ixgbe_adapter *adapter)
>> IXGBE_FLAG2_SFP_NEEDS_RESET);
>> adapter->flags &= ~IXGBE_FLAG_NEED_LINK_CONFIG;
>>
>> + ixgbe_mac_addr_refresh(adapter);
>> +
>
>[High]
>Is ixgbe_reset() always called with RTNL held? ixgbe_mac_addr_refresh()
>uses ASSERT_RTNL() and then dispatches eth_hw_addr_set() and
>call_netdevice_notifiers(NETDEV_CHANGEADDR, netdev), both of which
>require RTNL.
>
>The follow-up commit "ixgbe: take rtnl lock before ixgbe_reset() is
>called" only converts the ixgbe_resume() path. The PCI error-recovery
>callback ixgbe_io_slot_reset() still calls ixgbe_reset() without
>acquiring rtnl_lock(), and .slot_reset callbacks are not guaranteed to
>run under RTNL.
i believe ixgbe_resume() path covers all real scenarios
ixgbe_io_slot_reset() path is something purely theoretical imho
>
>On that path, if RAR0 differs from netdev->dev_addr on an E610,
>ASSERT_RTNL() will WARN and the subsequent netdev address publication
>and notifier-chain traversal race with concurrent RTNL-protected
>users (SIOCSIFHWADDR, notifier chain (un)registration, etc). Should
>ixgbe_io_slot_reset() also take rtnl_lock() around ixgbe_reset(), or
>should the refresh be moved to a caller-side location that guarantees
>RTNL?
i do not see such necessity; all race potential paths are coverd
despite the one which imho is possible to happen almost only
in theoretical thinking - netdev such be detached in that path so
any potentially racing second side should not be able to operate
>
>> err = hw->mac.ops.init_hw(hw);
>> switch (err) {
>> case 0:
>> @@ -8651,6 +8683,11 @@ static void ixgbe_service_task(struct work_struct *work)
>> ixgbe_handle_fw_event(adapter);
>> ixgbe_check_media_subtask(adapter);
>> }
>> +
>> + rtnl_lock();
>> + ixgbe_mac_addr_refresh(adapter);
>> + rtnl_unlock();
>> +
>
>[Medium]
>Would it make sense to hoist the E610 gating out of
>ixgbe_mac_addr_refresh() and skip the rtnl_lock()/rtnl_unlock()
>entirely for non-E610 devices?
>
>The type check lives inside the helper:
>
> static void ixgbe_mac_addr_refresh(struct ixgbe_adapter *adapter)
> {
> ...
> if (hw->mac.type != ixgbe_mac_e610)
> return;
> ...
> }
>
>so 82598, 82599, X540 and X550 adapters (which comprise most of the
>ixgbe device family) acquire the global rtnl_mutex every service-task
>cycle only to return immediately. Something like:
>
> if (adapter->hw.mac.type == ixgbe_mac_e610) {
> rtnl_lock();
> ixgbe_mac_addr_refresh(adapter);
> rtnl_unlock();
> }
Cannot introduce rtnl locking into ixgbe_mac_addr_refresh() as this
can be already called within rtnl-locked path
>
>would avoid the rtnl_mutex contention on non-E610 hardware.
>
>> ixgbe_reset_subtask(adapter);
>> ixgbe_phy_interrupt_subtask(adapter);
>> ixgbe_sfp_detection_subtask(adapter);
^ 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