In 2017, Paolo Abeni introduced the hinting mechanism [1] to the routing
sub-system. The hinting optimization improves performance by reusing
previously found dsts instead of looking them up for each skb.
This patch series introduces a generalized version of the hinting mechanism that
can "remember" a larger number of dsts. This reduces the number of dst
lookups for frequently encountered daddrs.
Before diving into the code and the benchmarking results, it's important
to address the deletion of the old route cache [2] and why
this solution is different. The original cache was complicated,
vulnerable to DOS attacks and had unstable performance.
The new input dst_cache is much simpler thanks to its lazy approach,
improving performance without the overhead of the removed cache
implementation. Instead of using timers and GC, the deletion of invalid
entries is performed lazily during their lookups.
The dsts are stored in a simple, lightweight, static hash table. This
keeps the lookup times fast yet stable, preventing DOS upon cache misses.
The new input dst_cache implementation is built over the existing
dst_cache code which supplies a fast lockless percpu behavior.
The measurement setup is comprised of 2 machines with mlx5 100Gbit NIC.
I sent small UDP packets with 5000 daddrs (10x of cache size) from one
machine to the other while also varying the saddr and the tos. I set
an iptables rule to drop the packets after routing. the receiving
machine's CPU (i9) was saturated.
Thanks a lot to David Ahern for all the help and guidance!
I measured the rx PPS using ifpps and the per-queue PPS using ethtool -S.
These are the results:
Total PPS:
mainline patched delta
Kpps Kpps %
6903 8105 17.41
Per-Queue PPS:
Queue mainline patched
0 345775 411780
1 345252 414387
2 347724 407501
3 346232 413456
4 347271 412088
5 346808 400910
6 346243 406699
7 346484 409104
8 342731 404612
9 344068 407558
10 345832 409558
11 346296 409935
12 346900 399084
13 345980 404513
14 347244 405136
15 346801 408752
16 345984 410865
17 346632 405752
18 346064 407539
19 344861 408364
total 6921182 8157593
I also verified that the number of packets caught by the iptables rule
matches the measured PPS.
TCP throughput was not affected by the patch, below is iperf3 output:
mainline patched
15.4 GBytes 13.2 Gbits/sec 15.5 GBytes 13.2 Gbits/sec
[1] https://lore.kernel.org/netdev/[email protected]/
[2] https://lore.kernel.org/netdev/[email protected]/
v1->v2:
- fix bitwise cast warning
- improved measurements setup
v1:
- fix typo while allocating per-cpu cache
- while using dst from the dst_cache set IPSKB_DOREDIRECT correctly
- always compile dst_cache
RFC-v2:
- remove unnecessary macro
- move inline to .h file
RFC-v1: https://lore.kernel.org/netdev/[email protected]/
RFC-v2: https://lore.kernel.org/netdev/[email protected]/
Leone Fernando (4):
net: route: expire rt if the dst it holds is expired
net: dst_cache: add input_dst_cache API
net: route: always compile dst_cache
net: route: replace route hints with input_dst_cache
drivers/net/Kconfig | 1 -
include/net/dst_cache.h | 68 +++++++++++++++++++
include/net/dst_metadata.h | 2 -
include/net/ip_tunnels.h | 2 -
include/net/route.h | 6 +-
net/Kconfig | 4 --
net/core/Makefile | 3 +-
net/core/dst.c | 4 --
net/core/dst_cache.c | 132 +++++++++++++++++++++++++++++++++++++
net/ipv4/Kconfig | 1 -
net/ipv4/ip_input.c | 58 ++++++++--------
net/ipv4/ip_tunnel_core.c | 4 --
net/ipv4/route.c | 75 +++++++++++++++------
net/ipv4/udp_tunnel_core.c | 4 --
net/ipv6/Kconfig | 4 --
net/ipv6/ip6_udp_tunnel.c | 4 --
net/netfilter/nft_tunnel.c | 2 -
net/openvswitch/Kconfig | 1 -
net/sched/act_tunnel_key.c | 2 -
19 files changed, 291 insertions(+), 86 deletions(-)
--
2.34.1
make dst_cache to always compile, delete all relevant ifdefs
Signed-off-by: Leone Fernando <[email protected]>
---
drivers/net/Kconfig | 1 -
include/net/dst_metadata.h | 2 --
include/net/ip_tunnels.h | 2 --
net/Kconfig | 4 ----
net/core/Makefile | 3 +--
net/core/dst.c | 4 ----
net/ipv4/Kconfig | 1 -
net/ipv4/ip_tunnel_core.c | 4 ----
net/ipv4/udp_tunnel_core.c | 4 ----
net/ipv6/Kconfig | 4 ----
net/ipv6/ip6_udp_tunnel.c | 4 ----
net/netfilter/nft_tunnel.c | 2 --
net/openvswitch/Kconfig | 1 -
net/sched/act_tunnel_key.c | 2 --
14 files changed, 1 insertion(+), 37 deletions(-)
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 9920b3a68ed1..6819ce6db863 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -78,7 +78,6 @@ config WIREGUARD
depends on IPV6 || !IPV6
depends on !KMSAN # KMSAN doesn't support the crypto configs below
select NET_UDP_TUNNEL
- select DST_CACHE
select CRYPTO
select CRYPTO_LIB_CURVE25519
select CRYPTO_LIB_CHACHA20POLY1305
diff --git a/include/net/dst_metadata.h b/include/net/dst_metadata.h
index 4160731dcb6e..46cebd8ea374 100644
--- a/include/net/dst_metadata.h
+++ b/include/net/dst_metadata.h
@@ -165,7 +165,6 @@ static inline struct metadata_dst *tun_dst_unclone(struct sk_buff *skb)
memcpy(&new_md->u.tun_info, &md_dst->u.tun_info,
sizeof(struct ip_tunnel_info) + md_size);
-#ifdef CONFIG_DST_CACHE
/* Unclone the dst cache if there is one */
if (new_md->u.tun_info.dst_cache.cache) {
int ret;
@@ -176,7 +175,6 @@ static inline struct metadata_dst *tun_dst_unclone(struct sk_buff *skb)
return ERR_PTR(ret);
}
}
-#endif
skb_dst_drop(skb);
skb_dst_set(skb, &new_md->dst);
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
index 9a6a08ec7713..ed1b36a5ae52 100644
--- a/include/net/ip_tunnels.h
+++ b/include/net/ip_tunnels.h
@@ -102,9 +102,7 @@ struct ip_tunnel_encap {
struct ip_tunnel_info {
struct ip_tunnel_key key;
struct ip_tunnel_encap encap;
-#ifdef CONFIG_DST_CACHE
struct dst_cache dst_cache;
-#endif
u8 options_len;
u8 mode;
};
diff --git a/net/Kconfig b/net/Kconfig
index d5ab791f7afa..cd5304e6cba2 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -441,10 +441,6 @@ config LWTUNNEL_BPF
Allows to run BPF programs as a nexthop action following a route
lookup for incoming and outgoing packets.
-config DST_CACHE
- bool
- default n
-
config GRO_CELLS
bool
default n
diff --git a/net/core/Makefile b/net/core/Makefile
index 21d6fbc7e884..cfd327ae389e 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -13,7 +13,7 @@ obj-y += dev.o dev_addr_lists.o dst.o netevent.o \
neighbour.o rtnetlink.o utils.o link_watch.o filter.o \
sock_diag.o dev_ioctl.o tso.o sock_reuseport.o \
fib_notifier.o xdp.o flow_offload.o gro.o \
- netdev-genl.o netdev-genl-gen.o gso.o
+ netdev-genl.o netdev-genl-gen.o gso.o dst_cache.o
obj-$(CONFIG_NETDEV_ADDR_LIST_TEST) += dev_addr_lists_test.o
@@ -33,7 +33,6 @@ obj-$(CONFIG_CGROUP_NET_PRIO) += netprio_cgroup.o
obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o
obj-$(CONFIG_LWTUNNEL) += lwtunnel.o
obj-$(CONFIG_LWTUNNEL_BPF) += lwt_bpf.o
-obj-$(CONFIG_DST_CACHE) += dst_cache.o
obj-$(CONFIG_HWBM) += hwbm.o
obj-$(CONFIG_GRO_CELLS) += gro_cells.o
obj-$(CONFIG_FAILOVER) += failover.o
diff --git a/net/core/dst.c b/net/core/dst.c
index 95f533844f17..f035c39be104 100644
--- a/net/core/dst.c
+++ b/net/core/dst.c
@@ -291,10 +291,8 @@ EXPORT_SYMBOL_GPL(metadata_dst_alloc);
void metadata_dst_free(struct metadata_dst *md_dst)
{
-#ifdef CONFIG_DST_CACHE
if (md_dst->type == METADATA_IP_TUNNEL)
dst_cache_destroy(&md_dst->u.tun_info.dst_cache);
-#endif
if (md_dst->type == METADATA_XFRM)
dst_release(md_dst->u.xfrm_info.dst_orig);
kfree(md_dst);
@@ -326,10 +324,8 @@ void metadata_dst_free_percpu(struct metadata_dst __percpu *md_dst)
for_each_possible_cpu(cpu) {
struct metadata_dst *one_md_dst = per_cpu_ptr(md_dst, cpu);
-#ifdef CONFIG_DST_CACHE
if (one_md_dst->type == METADATA_IP_TUNNEL)
dst_cache_destroy(&one_md_dst->u.tun_info.dst_cache);
-#endif
if (one_md_dst->type == METADATA_XFRM)
dst_release(one_md_dst->u.xfrm_info.dst_orig);
}
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 8e94ed7c56a0..189f716b03e8 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -185,7 +185,6 @@ config NET_IPGRE_DEMUX
config NET_IP_TUNNEL
tristate
- select DST_CACHE
select GRO_CELLS
default n
diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c
index a3676155be78..05fbc40c5d16 100644
--- a/net/ipv4/ip_tunnel_core.c
+++ b/net/ipv4/ip_tunnel_core.c
@@ -683,13 +683,11 @@ static int ip_tun_build_state(struct net *net, struct nlattr *attr,
return err;
}
-#ifdef CONFIG_DST_CACHE
err = dst_cache_init(&tun_info->dst_cache, GFP_KERNEL);
if (err) {
lwtstate_free(new_state);
return err;
}
-#endif
if (tb[LWTUNNEL_IP_ID])
tun_info->key.tun_id = nla_get_be64(tb[LWTUNNEL_IP_ID]);
@@ -727,11 +725,9 @@ static int ip_tun_build_state(struct net *net, struct nlattr *attr,
static void ip_tun_destroy_state(struct lwtunnel_state *lwtstate)
{
-#ifdef CONFIG_DST_CACHE
struct ip_tunnel_info *tun_info = lwt_tun_info(lwtstate);
dst_cache_destroy(&tun_info->dst_cache);
-#endif
}
static int ip_tun_fill_encap_opts_geneve(struct sk_buff *skb,
diff --git a/net/ipv4/udp_tunnel_core.c b/net/ipv4/udp_tunnel_core.c
index e4e0fa869fa4..576ab973d1f4 100644
--- a/net/ipv4/udp_tunnel_core.c
+++ b/net/ipv4/udp_tunnel_core.c
@@ -216,13 +216,11 @@ struct rtable *udp_tunnel_dst_lookup(struct sk_buff *skb,
struct rtable *rt = NULL;
struct flowi4 fl4;
-#ifdef CONFIG_DST_CACHE
if (dst_cache) {
rt = dst_cache_get_ip4(dst_cache, saddr);
if (rt)
return rt;
}
-#endif
memset(&fl4, 0, sizeof(fl4));
fl4.flowi4_mark = skb->mark;
@@ -245,10 +243,8 @@ struct rtable *udp_tunnel_dst_lookup(struct sk_buff *skb,
ip_rt_put(rt);
return ERR_PTR(-ELOOP);
}
-#ifdef CONFIG_DST_CACHE
if (dst_cache)
dst_cache_set_ip4(dst_cache, &rt->dst, fl4.saddr);
-#endif
*saddr = fl4.saddr;
return rt;
}
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index 08d4b7132d4c..093c768d41ab 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -124,7 +124,6 @@ config IPV6_MIP6
config IPV6_ILA
tristate "IPv6: Identifier Locator Addressing (ILA)"
depends on NETFILTER
- select DST_CACHE
select LWTUNNEL
help
Support for IPv6 Identifier Locator Addressing (ILA).
@@ -203,7 +202,6 @@ config IPV6_NDISC_NODETYPE
config IPV6_TUNNEL
tristate "IPv6: IP-in-IPv6 tunnel (RFC2473)"
select INET6_TUNNEL
- select DST_CACHE
select GRO_CELLS
help
Support for IPv6-in-IPv6 and IPv4-in-IPv6 tunnels described in
@@ -291,7 +289,6 @@ config IPV6_SEG6_LWTUNNEL
bool "IPv6: Segment Routing Header encapsulation support"
depends on IPV6
select LWTUNNEL
- select DST_CACHE
select IPV6_MULTIPLE_TABLES
help
Support for encapsulation of packets within an outer IPv6
@@ -333,7 +330,6 @@ config IPV6_IOAM6_LWTUNNEL
bool "IPv6: IOAM Pre-allocated Trace insertion support"
depends on IPV6
select LWTUNNEL
- select DST_CACHE
help
Support for the insertion of IOAM Pre-allocated Trace
Header using the lightweight tunnels mechanism.
diff --git a/net/ipv6/ip6_udp_tunnel.c b/net/ipv6/ip6_udp_tunnel.c
index c99053189ea8..de92aea01cfc 100644
--- a/net/ipv6/ip6_udp_tunnel.c
+++ b/net/ipv6/ip6_udp_tunnel.c
@@ -145,13 +145,11 @@ struct dst_entry *udp_tunnel6_dst_lookup(struct sk_buff *skb,
struct dst_entry *dst = NULL;
struct flowi6 fl6;
-#ifdef CONFIG_DST_CACHE
if (dst_cache) {
dst = dst_cache_get_ip6(dst_cache, saddr);
if (dst)
return dst;
}
-#endif
memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_mark = skb->mark;
fl6.flowi6_proto = IPPROTO_UDP;
@@ -173,10 +171,8 @@ struct dst_entry *udp_tunnel6_dst_lookup(struct sk_buff *skb,
dst_release(dst);
return ERR_PTR(-ELOOP);
}
-#ifdef CONFIG_DST_CACHE
if (dst_cache)
dst_cache_set_ip6(dst_cache, dst, &fl6.saddr);
-#endif
*saddr = fl6.saddr;
return dst;
}
diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c
index 60a76e6e348e..aa4a872edae2 100644
--- a/net/netfilter/nft_tunnel.c
+++ b/net/netfilter/nft_tunnel.c
@@ -514,13 +514,11 @@ static int nft_tunnel_obj_init(const struct nft_ctx *ctx,
return -ENOMEM;
memcpy(&md->u.tun_info, &info, sizeof(info));
-#ifdef CONFIG_DST_CACHE
err = dst_cache_init(&md->u.tun_info.dst_cache, GFP_KERNEL);
if (err < 0) {
metadata_dst_free(md);
return err;
}
-#endif
ip_tunnel_info_opts_set(&md->u.tun_info, &priv->opts.u, priv->opts.len,
priv->opts.flags);
priv->md = md;
diff --git a/net/openvswitch/Kconfig b/net/openvswitch/Kconfig
index 29a7081858cd..b7a5ab6374b8 100644
--- a/net/openvswitch/Kconfig
+++ b/net/openvswitch/Kconfig
@@ -13,7 +13,6 @@ config OPENVSWITCH
select LIBCRC32C
select MPLS
select NET_MPLS_GSO
- select DST_CACHE
select NET_NSH
select NF_CONNTRACK_OVS if NF_CONNTRACK
select NF_NAT_OVS if NF_NAT
diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c
index af7c99845948..9d673b642b7c 100644
--- a/net/sched/act_tunnel_key.c
+++ b/net/sched/act_tunnel_key.c
@@ -476,11 +476,9 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
goto err_out;
}
-#ifdef CONFIG_DST_CACHE
ret = dst_cache_init(&metadata->u.tun_info.dst_cache, GFP_KERNEL);
if (ret)
goto release_tun_meta;
-#endif
if (opts_len) {
ret = tunnel_key_opts_set(tb[TCA_TUNNEL_KEY_ENC_OPTS],
--
2.34.1
> On Tue, May 7, 2024 at 2:43 PM Leone Fernando <[email protected]> wrote:
>>
>> In 2017, Paolo Abeni introduced the hinting mechanism [1] to the routing
>> sub-system. The hinting optimization improves performance by reusing
>> previously found dsts instead of looking them up for each skb.
>>
>> This patch series introduces a generalized version of the hinting mechanism that
>> can "remember" a larger number of dsts. This reduces the number of dst
>> lookups for frequently encountered daddrs.
>>
>> Before diving into the code and the benchmarking results, it's important
>> to address the deletion of the old route cache [2] and why
>> this solution is different. The original cache was complicated,
>> vulnerable to DOS attacks and had unstable performance.
>>
>> The new input dst_cache is much simpler thanks to its lazy approach,
>> improving performance without the overhead of the removed cache
>> implementation. Instead of using timers and GC, the deletion of invalid
>> entries is performed lazily during their lookups.
>> The dsts are stored in a simple, lightweight, static hash table. This
>> keeps the lookup times fast yet stable, preventing DOS upon cache misses.
>> The new input dst_cache implementation is built over the existing
>> dst_cache code which supplies a fast lockless percpu behavior.
>>
>> The measurement setup is comprised of 2 machines with mlx5 100Gbit NIC.
>> I sent small UDP packets with 5000 daddrs (10x of cache size) from one
>> machine to the other while also varying the saddr and the tos. I set
>> an iptables rule to drop the packets after routing. the receiving
>> machine's CPU (i9) was saturated.
>>
>> Thanks a lot to David Ahern for all the help and guidance!
>>
>> I measured the rx PPS using ifpps and the per-queue PPS using ethtool -S.
>> These are the results:
>
> How device dismantles are taken into account ?
>
> I am currently tracking a bug in dst_cache, triggering sometimes when
> running pmtu.sh selftest.
>
> Apparently, dst_cache_per_cpu_dst_set() can cache dst that have no
> dst->rt_uncached
> linkage.
The dst_cache_input that was introduced in this series caches input
routes that are owned by the fib tree.
These routes have a rt_uncached linkage. So I think this bug will not
replicate to dst_cache_input.
> There is no cleanup (at least in vxlan) to make sure cached dst are
> either freed or
> their dst->dev changed.
>
>
> TEST: ipv6: cleanup of cached exceptions - nexthop objects [ OK ]
> [ 1001.344490] vxlan: __vxlan_fdb_free calling
> dst_cache_destroy(ffff8f12422cbb90)
> [ 1001.345253] dst_cache_destroy dst_cache=ffff8f12422cbb90
> ->cache=0000417580008d30
> [ 1001.378615] vxlan: __vxlan_fdb_free calling
> dst_cache_destroy(ffff8f12471e31d0)
> [ 1001.379260] dst_cache_destroy dst_cache=ffff8f12471e31d0
> ->cache=0000417580008608
> [ 1011.349730] unregister_netdevice: waiting for veth_A-R1 to become
> free. Usage count = 7
> [ 1011.350562] ref_tracker: veth_A-R1@000000009392ed3b has 1/6 users at
> [ 1011.350562] dst_alloc+0x76/0x160
> [ 1011.350562] ip6_dst_alloc+0x25/0x80
> [ 1011.350562] ip6_pol_route+0x2a8/0x450
> [ 1011.350562] ip6_pol_route_output+0x1f/0x30
> [ 1011.350562] fib6_rule_lookup+0x163/0x270
> [ 1011.350562] ip6_route_output_flags+0xda/0x190
> [ 1011.350562] ip6_dst_lookup_tail.constprop.0+0x1d0/0x260
> [ 1011.350562] ip6_dst_lookup_flow+0x47/0xa0
> [ 1011.350562] udp_tunnel6_dst_lookup+0x158/0x210
> [ 1011.350562] vxlan_xmit_one+0x4c6/0x1550 [vxlan]
> [ 1011.350562] vxlan_xmit+0x535/0x1500 [vxlan]
> [ 1011.350562] dev_hard_start_xmit+0x7b/0x1e0
> [ 1011.350562] __dev_queue_xmit+0x20c/0xe40
> [ 1011.350562] arp_xmit+0x1d/0x50
> [ 1011.350562] arp_send_dst+0x7f/0xa0
> [ 1011.350562] arp_solicit+0xf6/0x2f0
> [ 1011.350562]
> [ 1011.350562] ref_tracker: veth_A-R1@000000009392ed3b has 3/6 users at
> [ 1011.350562] dst_alloc+0x76/0x160
> [ 1011.350562] ip6_dst_alloc+0x25/0x80
> [ 1011.350562] ip6_pol_route+0x2a8/0x450
> [ 1011.350562] ip6_pol_route_output+0x1f/0x30
> [ 1011.350562] fib6_rule_lookup+0x163/0x270
> [ 1011.350562] ip6_route_output_flags+0xda/0x190
> [ 1011.350562] ip6_dst_lookup_tail.constprop.0+0x1d0/0x260
> [ 1011.350562] ip6_dst_lookup_flow+0x47/0xa0
> [ 1011.350562] udp_tunnel6_dst_lookup+0x158/0x210
> [ 1011.350562] vxlan_xmit_one+0x4c6/0x1550 [vxlan]
> [ 1011.350562] vxlan_xmit+0x535/0x1500 [vxlan]
> [ 1011.350562] dev_hard_start_xmit+0x7b/0x1e0
> [ 1011.350562] __dev_queue_xmit+0x20c/0xe40
> [ 1011.350562] ip6_finish_output2+0x2ea/0x6e0
> [ 1011.350562] ip6_finish_output+0x143/0x320
> [ 1011.350562] ip6_output+0x74/0x140
> [ 1011.350562]
> [ 1011.350562] ref_tracker: veth_A-R1@000000009392ed3b has 1/6 users at
> [ 1011.350562] netdev_get_by_index+0xc0/0xe0
> [ 1011.350562] fib6_nh_init+0x1a9/0xa90
> [ 1011.350562] rtm_new_nexthop+0x6fa/0x1580
> [ 1011.350562] rtnetlink_rcv_msg+0x155/0x3e0
> [ 1011.350562] netlink_rcv_skb+0x61/0x110
> [ 1011.350562] rtnetlink_rcv+0x19/0x20
> [ 1011.350562] netlink_unicast+0x23f/0x380
> [ 1011.350562] netlink_sendmsg+0x1fc/0x430
> [ 1011.350562] ____sys_sendmsg+0x2ef/0x320
> [ 1011.350562] ___sys_sendmsg+0x86/0xd0
> [ 1011.350562] __sys_sendmsg+0x67/0xc0
> [ 1011.350562] __x64_sys_sendmsg+0x21/0x30
> [ 1011.350562] x64_sys_call+0x252/0x2030
> [ 1011.350562] do_syscall_64+0x6c/0x190
> [ 1011.350562] entry_SYSCALL_64_after_hwframe+0x76/0x7e
> [ 1011.350562]
> [ 1011.350562] ref_tracker: veth_A-R1@000000009392ed3b has 1/6 users at
> [ 1011.350562] ipv6_add_dev+0x136/0x530
> [ 1011.350562] addrconf_notify+0x19d/0x770
> [ 1011.350562] notifier_call_chain+0x65/0xd0
> [ 1011.350562] raw_notifier_call_chain+0x1a/0x20
> [ 1011.350562] call_netdevice_notifiers_info+0x54/0x90
> [ 1011.350562] register_netdevice+0x61e/0x790
> [ 1011.350562] veth_newlink+0x230/0x440
> [ 1011.350562] __rtnl_newlink+0x7d2/0xaa0
> [ 1011.350562] rtnl_newlink+0x4c/0x70
> [ 1011.350562] rtnetlink_rcv_msg+0x155/0x3e0
> [ 1011.350562] netlink_rcv_skb+0x61/0x110
> [ 1011.350562] rtnetlink_rcv+0x19/0x20
> [ 1011.350562] netlink_unicast+0x23f/0x380
> [ 1011.350562] netlink_sendmsg+0x1fc/0x430
> [ 1011.350562] ____sys_sendmsg+0x2ef/0x320
> [ 1011.350562] ___sys_sendmsg+0x86/0xd0
> [ 1011.350562]