Skip to content

Commit fe946a7

Browse files
edumazetkuba-moo
authored andcommitted
net/sched: act_mirred: add loop detection
Commit 0f022d3 ("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 <[email protected]> Reviewed-by: Kuniyuki Iwashima <[email protected]> Reviewed-by: Toke Høiland-Jørgensen <[email protected]> Tested-by: Jamal Hadi Salim <[email protected]> Acked-by: Jamal Hadi Salim <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 5b2b7de commit fe946a7

File tree

2 files changed

+31
-40
lines changed

2 files changed

+31
-40
lines changed

include/linux/netdevice_xmit.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,21 @@
22
#ifndef _LINUX_NETDEVICE_XMIT_H
33
#define _LINUX_NETDEVICE_XMIT_H
44

5+
#if IS_ENABLED(CONFIG_NET_ACT_MIRRED)
6+
#define MIRRED_NEST_LIMIT 4
7+
#endif
8+
9+
struct net_device;
10+
511
struct netdev_xmit {
612
u16 recursion;
713
u8 more;
814
#ifdef CONFIG_NET_EGRESS
915
u8 skip_txqueue;
1016
#endif
1117
#if IS_ENABLED(CONFIG_NET_ACT_MIRRED)
12-
u8 sched_mirred_nest;
18+
u8 sched_mirred_nest;
19+
struct net_device *sched_mirred_dev[MIRRED_NEST_LIMIT];
1320
#endif
1421
#if IS_ENABLED(CONFIG_NF_DUP_NETDEV)
1522
u8 nf_dup_skb_recursion;

net/sched/act_mirred.c

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -29,31 +29,6 @@
2929
static LIST_HEAD(mirred_list);
3030
static DEFINE_SPINLOCK(mirred_list_lock);
3131

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-
5732
static bool tcf_mirred_is_act_redirect(int action)
5833
{
5934
return action == TCA_EGRESS_REDIR || action == TCA_INGRESS_REDIR;
@@ -439,44 +414,53 @@ TC_INDIRECT_SCOPE int tcf_mirred_act(struct sk_buff *skb,
439414
{
440415
struct tcf_mirred *m = to_mirred(a);
441416
int retval = READ_ONCE(m->tcf_action);
442-
unsigned int nest_level;
417+
struct netdev_xmit *xmit;
443418
bool m_mac_header_xmit;
444419
struct net_device *dev;
445-
int m_eaction;
420+
int i, m_eaction;
446421
u32 blockid;
447422

448-
nest_level = tcf_mirred_nest_level_inc_return();
449-
if (unlikely(nest_level > MIRRED_NEST_LIMIT)) {
423+
#ifdef CONFIG_PREEMPT_RT
424+
xmit = &current->net_xmit;
425+
#else
426+
xmit = this_cpu_ptr(&softnet_data.xmit);
427+
#endif
428+
if (unlikely(xmit->sched_mirred_nest >= MIRRED_NEST_LIMIT)) {
450429
net_warn_ratelimited("Packet exceeded mirred recursion limit on dev %s\n",
451430
netdev_name(skb->dev));
452-
retval = TC_ACT_SHOT;
453-
goto dec_nest_level;
431+
return TC_ACT_SHOT;
454432
}
455433

456434
tcf_lastuse_update(&m->tcf_tm);
457435
tcf_action_update_bstats(&m->common, skb);
458436

459437
blockid = READ_ONCE(m->tcfm_blockid);
460-
if (blockid) {
461-
retval = tcf_blockcast(skb, m, blockid, res, retval);
462-
goto dec_nest_level;
463-
}
438+
if (blockid)
439+
return tcf_blockcast(skb, m, blockid, res, retval);
464440

465441
dev = rcu_dereference_bh(m->tcfm_dev);
466442
if (unlikely(!dev)) {
467443
pr_notice_once("tc mirred: target device is gone\n");
468444
tcf_action_inc_overlimit_qstats(&m->common);
469-
goto dec_nest_level;
445+
return retval;
470446
}
447+
for (i = 0; i < xmit->sched_mirred_nest; i++) {
448+
if (xmit->sched_mirred_dev[i] != dev)
449+
continue;
450+
pr_notice_once("tc mirred: loop on device %s\n",
451+
netdev_name(dev));
452+
tcf_action_inc_overlimit_qstats(&m->common);
453+
return retval;
454+
}
455+
456+
xmit->sched_mirred_dev[xmit->sched_mirred_nest++] = dev;
471457

472458
m_mac_header_xmit = READ_ONCE(m->tcfm_mac_header_xmit);
473459
m_eaction = READ_ONCE(m->tcfm_eaction);
474460

475461
retval = tcf_mirred_to_dev(skb, m, dev, m_mac_header_xmit, m_eaction,
476462
retval);
477-
478-
dec_nest_level:
479-
tcf_mirred_nest_level_dec();
463+
xmit->sched_mirred_nest--;
480464

481465
return retval;
482466
}

0 commit comments

Comments
 (0)