diff options
Diffstat (limited to 'net/sched/act_mirred.c')
| -rw-r--r-- | net/sched/act_mirred.c | 43 | 
1 files changed, 40 insertions, 3 deletions
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index c0b6863e3b8..1980b71c283 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -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;  } @@ -162,9 +167,14 @@ static int tcf_mirred(struct sk_buff *skb, struct tc_action *a,  	m->tcf_tm.lastuse = jiffies;  	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;  	} @@ -232,6 +242,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, @@ -252,12 +284,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);  }  |