diff options
author | John Fastabend <john.fastabend@gmail.com> | 2017-09-08 17:00:30 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-09-09 00:11:00 -0400 |
commit | bbbe211c295ffb309247adb7b871dda60d92d2d5 (patch) | |
tree | 7d4b7d13d3455fd2e2288296a775792f04cea3cd | |
parent | 109980b894e9dae66c37c3d804a415aa68b19c7e (diff) |
net: rcu lock and preempt disable missing around generic xdp
do_xdp_generic must be called inside rcu critical section with preempt
disabled to ensure BPF programs are valid and per-cpu variables used
for redirect operations are consistent. This patch ensures this is true
and fixes the splat below.
The netif_receive_skb_internal() code path is now broken into two rcu
critical sections. I decided it was better to limit the preempt_enable/disable
block to just the xdp static key portion and the fallout is more
rcu_read_lock/unlock calls. Seems like the best option to me.
[ 607.596901] =============================
[ 607.596906] WARNING: suspicious RCU usage
[ 607.596912] 4.13.0-rc4+ #570 Not tainted
[ 607.596917] -----------------------------
[ 607.596923] net/core/dev.c:3948 suspicious rcu_dereference_check() usage!
[ 607.596927]
[ 607.596927] other info that might help us debug this:
[ 607.596927]
[ 607.596933]
[ 607.596933] rcu_scheduler_active = 2, debug_locks = 1
[ 607.596938] 2 locks held by pool/14624:
[ 607.596943] #0: (rcu_read_lock_bh){......}, at: [<ffffffff95445ffd>] ip_finish_output2+0x14d/0x890
[ 607.596973] #1: (rcu_read_lock_bh){......}, at: [<ffffffff953c8e3a>] __dev_queue_xmit+0x14a/0xfd0
[ 607.597000]
[ 607.597000] stack backtrace:
[ 607.597006] CPU: 5 PID: 14624 Comm: pool Not tainted 4.13.0-rc4+ #570
[ 607.597011] Hardware name: Dell Inc. Precision Tower 5810/0HHV7N, BIOS A17 03/01/2017
[ 607.597016] Call Trace:
[ 607.597027] dump_stack+0x67/0x92
[ 607.597040] lockdep_rcu_suspicious+0xdd/0x110
[ 607.597054] do_xdp_generic+0x313/0xa50
[ 607.597068] ? time_hardirqs_on+0x5b/0x150
[ 607.597076] ? mark_held_locks+0x6b/0xc0
[ 607.597088] ? netdev_pick_tx+0x150/0x150
[ 607.597117] netif_rx_internal+0x205/0x3f0
[ 607.597127] ? do_xdp_generic+0xa50/0xa50
[ 607.597144] ? lock_downgrade+0x2b0/0x2b0
[ 607.597158] ? __lock_is_held+0x93/0x100
[ 607.597187] netif_rx+0x119/0x190
[ 607.597202] loopback_xmit+0xfd/0x1b0
[ 607.597214] dev_hard_start_xmit+0x127/0x4e0
Fixes: d445516966dc ("net: xdp: support xdp generic on virtual devices")
Fixes: b5cdae3291f7 ("net: Generic XDP")
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: John Fastabend <john.fastabend@gmail.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/core/dev.c | 25 |
1 files changed, 16 insertions, 9 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 6f845e4fec17..fb766d906148 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -3981,8 +3981,13 @@ static int netif_rx_internal(struct sk_buff *skb) | |||
3981 | trace_netif_rx(skb); | 3981 | trace_netif_rx(skb); |
3982 | 3982 | ||
3983 | if (static_key_false(&generic_xdp_needed)) { | 3983 | if (static_key_false(&generic_xdp_needed)) { |
3984 | int ret = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog), | 3984 | int ret; |
3985 | skb); | 3985 | |
3986 | preempt_disable(); | ||
3987 | rcu_read_lock(); | ||
3988 | ret = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog), skb); | ||
3989 | rcu_read_unlock(); | ||
3990 | preempt_enable(); | ||
3986 | 3991 | ||
3987 | /* Consider XDP consuming the packet a success from | 3992 | /* Consider XDP consuming the packet a success from |
3988 | * the netdev point of view we do not want to count | 3993 | * the netdev point of view we do not want to count |
@@ -4500,18 +4505,20 @@ static int netif_receive_skb_internal(struct sk_buff *skb) | |||
4500 | if (skb_defer_rx_timestamp(skb)) | 4505 | if (skb_defer_rx_timestamp(skb)) |
4501 | return NET_RX_SUCCESS; | 4506 | return NET_RX_SUCCESS; |
4502 | 4507 | ||
4503 | rcu_read_lock(); | ||
4504 | |||
4505 | if (static_key_false(&generic_xdp_needed)) { | 4508 | if (static_key_false(&generic_xdp_needed)) { |
4506 | int ret = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog), | 4509 | int ret; |
4507 | skb); | ||
4508 | 4510 | ||
4509 | if (ret != XDP_PASS) { | 4511 | preempt_disable(); |
4510 | rcu_read_unlock(); | 4512 | rcu_read_lock(); |
4513 | ret = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog), skb); | ||
4514 | rcu_read_unlock(); | ||
4515 | preempt_enable(); | ||
4516 | |||
4517 | if (ret != XDP_PASS) | ||
4511 | return NET_RX_DROP; | 4518 | return NET_RX_DROP; |
4512 | } | ||
4513 | } | 4519 | } |
4514 | 4520 | ||
4521 | rcu_read_lock(); | ||
4515 | #ifdef CONFIG_RPS | 4522 | #ifdef CONFIG_RPS |
4516 | if (static_key_false(&rps_needed)) { | 4523 | if (static_key_false(&rps_needed)) { |
4517 | struct rps_dev_flow voidflow, *rflow = &voidflow; | 4524 | struct rps_dev_flow voidflow, *rflow = &voidflow; |