]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/sched/act_mirred.c
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[net-next-2.6.git] / net / sched / act_mirred.c
index a16b0175f890686025c5b6684ef13cc878acd6f3..11f195af2da0732aaf362380928e298f7f35a199 100644 (file)
@@ -33,6 +33,7 @@
 static struct tcf_common *tcf_mirred_ht[MIRRED_TAB_MASK + 1];
 static u32 mirred_idx_gen;
 static DEFINE_RWLOCK(mirred_lock);
+static LIST_HEAD(mirred_list);
 
 static struct tcf_hashinfo mirred_hash_info = {
        .htab   =       tcf_mirred_ht,
@@ -47,7 +48,9 @@ static inline int tcf_mirred_release(struct tcf_mirred *m, int bind)
                        m->tcf_bindcnt--;
                m->tcf_refcnt--;
                if(!m->tcf_bindcnt && m->tcf_refcnt <= 0) {
-                       dev_put(m->tcfm_dev);
+                       list_del(&m->tcfm_list);
+                       if (m->tcfm_dev)
+                               dev_put(m->tcfm_dev);
                        tcf_hash_destroy(&m->common, &mirred_hash_info);
                        return 1;
                }
@@ -134,8 +137,10 @@ static int tcf_mirred_init(struct nlattr *nla, struct nlattr *est,
                m->tcfm_ok_push = ok_push;
        }
        spin_unlock_bh(&m->tcf_lock);
-       if (ret == ACT_P_CREATED)
+       if (ret == ACT_P_CREATED) {
+               list_add(&m->tcfm_list, &mirred_list);
                tcf_hash_insert(pc, &mirred_hash_info);
+       }
 
        return ret;
 }
@@ -164,9 +169,14 @@ static int tcf_mirred(struct sk_buff *skb, struct tc_action *a,
        m->tcf_bstats.packets++;
 
        dev = m->tcfm_dev;
+       if (!dev) {
+               printk_once(KERN_NOTICE "tc mirred: target device is gone\n");
+               goto out;
+       }
+
        if (!(dev->flags & IFF_UP)) {
                if (net_ratelimit())
-                       pr_notice("tc mirred to Houston: device %s is gone!\n",
+                       pr_notice("tc mirred to Houston: device %s is down\n",
                                  dev->name);
                goto out;
        }
@@ -230,6 +240,28 @@ nla_put_failure:
        return -1;
 }
 
+static int mirred_device_event(struct notifier_block *unused,
+                              unsigned long event, void *ptr)
+{
+       struct net_device *dev = ptr;
+       struct tcf_mirred *m;
+
+       if (event == NETDEV_UNREGISTER)
+               list_for_each_entry(m, &mirred_list, tcfm_list) {
+                       if (m->tcfm_dev == dev) {
+                               dev_put(dev);
+                               m->tcfm_dev = NULL;
+                       }
+               }
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block mirred_device_notifier = {
+       .notifier_call = mirred_device_event,
+};
+
+
 static struct tc_action_ops act_mirred_ops = {
        .kind           =       "mirred",
        .hinfo          =       &mirred_hash_info,
@@ -250,12 +282,17 @@ MODULE_LICENSE("GPL");
 
 static int __init mirred_init_module(void)
 {
+       int err = register_netdevice_notifier(&mirred_device_notifier);
+       if (err)
+               return err;
+
        pr_info("Mirror/redirect action on\n");
        return tcf_register_action(&act_mirred_ops);
 }
 
 static void __exit mirred_cleanup_module(void)
 {
+       unregister_netdevice_notifier(&mirred_device_notifier);
        tcf_unregister_action(&act_mirred_ops);
 }