History

net sched: fix race in mirred device removal

This fixes hang when target device of mirred packet classifier action is removed. If a mirror or redirection action is configured to cause packets to go to another device, the classifier holds a ref count, but was assuming the adminstrator cleaned up all redirections before removing. The fix is to add a notifier and cleanup during unregister. The new list is implicitly protected by RTNL mutex because it is held during filter add/delete as well as notifier.

stephen hemminger committed 16 years ago

Commit 3b879561 parent 76ac21f2 files · +41 −3Browse files

Unified · Split

2 files
  1. include/net/tc_act/tc_mirred.h +1 −0
  2. net/sched/act_mirred.c +40 −3
include/net/tc_act/tc_mirred.h+1−0
@@ -9,6 +9,7 @@ struct tcf_mirred { · 8 unmodified lines
99 int tcfm_ifindex;
1010 int tcfm_ok_push;
1111 struct net_device *tcfm_dev;
12+ struct list_head tcfm_list;
1213 };
1314 #define to_mirred(pc) \
1415 container_of(pc, struct tcf_mirred, common)
net/sched/act_mirred.c+40−3
@@ -33,6 +33,7 @@ · 32 unmodified lines
3333 static struct tcf_common *tcf_mirred_ht[MIRRED_TAB_MASK + 1];
3434 static u32 mirred_idx_gen;
3535 static DEFINE_RWLOCK(mirred_lock);
36+static LIST_HEAD(mirred_list);
3637
3738 static struct tcf_hashinfo mirred_hash_info = {
3839 .htab = tcf_mirred_ht,
@@ -47,7 +48,9 @@ static inline int tcf_mirred_release(struct tcf_mirred *m, int bind) · 8 unmodified lines
4748 m->tcf_bindcnt--;
4849 m->tcf_refcnt--;
4950 if(!m->tcf_bindcnt && m->tcf_refcnt <= 0) {
50− dev_put(m->tcfm_dev);
51+ list_del(&m->tcfm_list);
52+ if (m->tcfm_dev)
53+ dev_put(m->tcfm_dev);
5154 tcf_hash_destroy(&m->common, &mirred_hash_info);
5255 return 1;
5356 }
@@ -134,8 +137,10 @@ static int tcf_mirred_init(struct nlattr *nla, struct nlattr *est, · 80 unmodified lines
134137 m->tcfm_ok_push = ok_push;
135138 }
136139 spin_unlock_bh(&m->tcf_lock);
137− if (ret == ACT_P_CREATED)
140+ if (ret == ACT_P_CREATED) {
141+ list_add(&m->tcfm_list, &mirred_list);
138142 tcf_hash_insert(pc, &mirred_hash_info);
143+ }
139144
140145 return ret;
141146 }
@@ -162,9 +167,14 @@ static int tcf_mirred(struct sk_buff *skb, struct tc_action *a, · 20 unmodified lines
162167 m->tcf_tm.lastuse = jiffies;
163168
164169 dev = m->tcfm_dev;
170+ if (!dev) {
171+ printk_once(KERN_NOTICE "tc mirred: target device is gone\n");
172+ goto out;
173+ }
174+
165175 if (!(dev->flags & IFF_UP)) {
166176 if (net_ratelimit())
167− pr_notice("tc mirred to Houston: device %s is gone!\n",
177+ pr_notice("tc mirred to Houston: device %s is down\n",
168178 dev->name);
169179 goto out;
170180 }
@@ -232,6 +242,28 @@ nla_put_failure: · 61 unmodified lines
232242 return -1;
233243 }
234244
245+static int mirred_device_event(struct notifier_block *unused,
246+ unsigned long event, void *ptr)
247+{
248+ struct net_device *dev = ptr;
249+ struct tcf_mirred *m;
250+
251+ if (event == NETDEV_UNREGISTER)
252+ list_for_each_entry(m, &mirred_list, tcfm_list) {
253+ if (m->tcfm_dev == dev) {
254+ dev_put(dev);
255+ m->tcfm_dev = NULL;
256+ }
257+ }
258+
259+ return NOTIFY_DONE;
260+}
261+
262+static struct notifier_block mirred_device_notifier = {
263+ .notifier_call = mirred_device_event,
264+};
265+
266+
235267 static struct tc_action_ops act_mirred_ops = {
236268 .kind = "mirred",
237269 .hinfo = &mirred_hash_info,
@@ -252,12 +284,17 @@ MODULE_LICENSE("GPL"); · 14 unmodified lines
252284
253285 static int __init mirred_init_module(void)
254286 {
287+ int err = register_netdevice_notifier(&mirred_device_notifier);
288+ if (err)
289+ return err;
290+
255291 pr_info("Mirror/redirect action on\n");
256292 return tcf_register_action(&act_mirred_ops);
257293 }
258294
259295 static void __exit mirred_cleanup_module(void)
260296 {
297+ unregister_netdevice_notifier(&mirred_device_notifier);
261298 tcf_unregister_action(&act_mirred_ops);
262299 }
263300