Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net/sched: act_mirred: add loop detection

Commit 0f022d32c3ec ("net/sched: Fix mirred deadlock on device recursion")
added code in the fast path, even when act_mirred is not used.

Prepare its revert by implementing loop detection in act_mirred.

Adds an array of device pointers in struct netdev_xmit.

tcf_mirred_is_act_redirect() can detect if the array
already contains the target device.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Toke Høiland-Jørgensen <toke@redhat.com>
Tested-by: Jamal Hadi Salim <jhs@mojatatu.com>
Acked-by: Jamal Hadi Salim <jhs@mojatatu.com>
Link: https://patch.msgid.link/20251014171907.3554413-4-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Eric Dumazet and committed by
Jakub Kicinski
fe946a75 5b2b7dec

+31 -40
+8 -1
include/linux/netdevice_xmit.h
··· 2 2 #ifndef _LINUX_NETDEVICE_XMIT_H 3 3 #define _LINUX_NETDEVICE_XMIT_H 4 4 5 + #if IS_ENABLED(CONFIG_NET_ACT_MIRRED) 6 + #define MIRRED_NEST_LIMIT 4 7 + #endif 8 + 9 + struct net_device; 10 + 5 11 struct netdev_xmit { 6 12 u16 recursion; 7 13 u8 more; ··· 15 9 u8 skip_txqueue; 16 10 #endif 17 11 #if IS_ENABLED(CONFIG_NET_ACT_MIRRED) 18 - u8 sched_mirred_nest; 12 + u8 sched_mirred_nest; 13 + struct net_device *sched_mirred_dev[MIRRED_NEST_LIMIT]; 19 14 #endif 20 15 #if IS_ENABLED(CONFIG_NF_DUP_NETDEV) 21 16 u8 nf_dup_skb_recursion;
+23 -39
net/sched/act_mirred.c
··· 29 29 static LIST_HEAD(mirred_list); 30 30 static DEFINE_SPINLOCK(mirred_list_lock); 31 31 32 - #define MIRRED_NEST_LIMIT 4 33 - 34 - #ifndef CONFIG_PREEMPT_RT 35 - static u8 tcf_mirred_nest_level_inc_return(void) 36 - { 37 - return __this_cpu_inc_return(softnet_data.xmit.sched_mirred_nest); 38 - } 39 - 40 - static void tcf_mirred_nest_level_dec(void) 41 - { 42 - __this_cpu_dec(softnet_data.xmit.sched_mirred_nest); 43 - } 44 - 45 - #else 46 - static u8 tcf_mirred_nest_level_inc_return(void) 47 - { 48 - return current->net_xmit.sched_mirred_nest++; 49 - } 50 - 51 - static void tcf_mirred_nest_level_dec(void) 52 - { 53 - current->net_xmit.sched_mirred_nest--; 54 - } 55 - #endif 56 - 57 32 static bool tcf_mirred_is_act_redirect(int action) 58 33 { 59 34 return action == TCA_EGRESS_REDIR || action == TCA_INGRESS_REDIR; ··· 414 439 { 415 440 struct tcf_mirred *m = to_mirred(a); 416 441 int retval = READ_ONCE(m->tcf_action); 417 - unsigned int nest_level; 442 + struct netdev_xmit *xmit; 418 443 bool m_mac_header_xmit; 419 444 struct net_device *dev; 420 - int m_eaction; 445 + int i, m_eaction; 421 446 u32 blockid; 422 447 423 - nest_level = tcf_mirred_nest_level_inc_return(); 424 - if (unlikely(nest_level > MIRRED_NEST_LIMIT)) { 448 + #ifdef CONFIG_PREEMPT_RT 449 + xmit = &current->net_xmit; 450 + #else 451 + xmit = this_cpu_ptr(&softnet_data.xmit); 452 + #endif 453 + if (unlikely(xmit->sched_mirred_nest >= MIRRED_NEST_LIMIT)) { 425 454 net_warn_ratelimited("Packet exceeded mirred recursion limit on dev %s\n", 426 455 netdev_name(skb->dev)); 427 - retval = TC_ACT_SHOT; 428 - goto dec_nest_level; 456 + return TC_ACT_SHOT; 429 457 } 430 458 431 459 tcf_lastuse_update(&m->tcf_tm); 432 460 tcf_action_update_bstats(&m->common, skb); 433 461 434 462 blockid = READ_ONCE(m->tcfm_blockid); 435 - if (blockid) { 436 - retval = tcf_blockcast(skb, m, blockid, res, retval); 437 - goto dec_nest_level; 438 - } 463 + if (blockid) 464 + return tcf_blockcast(skb, m, blockid, res, retval); 439 465 440 466 dev = rcu_dereference_bh(m->tcfm_dev); 441 467 if (unlikely(!dev)) { 442 468 pr_notice_once("tc mirred: target device is gone\n"); 443 469 tcf_action_inc_overlimit_qstats(&m->common); 444 - goto dec_nest_level; 470 + return retval; 445 471 } 472 + for (i = 0; i < xmit->sched_mirred_nest; i++) { 473 + if (xmit->sched_mirred_dev[i] != dev) 474 + continue; 475 + pr_notice_once("tc mirred: loop on device %s\n", 476 + netdev_name(dev)); 477 + tcf_action_inc_overlimit_qstats(&m->common); 478 + return retval; 479 + } 480 + 481 + xmit->sched_mirred_dev[xmit->sched_mirred_nest++] = dev; 446 482 447 483 m_mac_header_xmit = READ_ONCE(m->tcfm_mac_header_xmit); 448 484 m_eaction = READ_ONCE(m->tcfm_eaction); 449 485 450 486 retval = tcf_mirred_to_dev(skb, m, dev, m_mac_header_xmit, m_eaction, 451 487 retval); 452 - 453 - dec_nest_level: 454 - tcf_mirred_nest_level_dec(); 488 + xmit->sched_mirred_nest--; 455 489 456 490 return retval; 457 491 }