diff options
Diffstat (limited to 'drivers/net/tun.c')
| -rw-r--r-- | drivers/net/tun.c | 68 | 
1 files changed, 25 insertions, 43 deletions
| diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 504f7f1cad94..af372d0957fe 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -180,7 +180,6 @@ struct tun_struct {  	int debug;  #endif  	spinlock_t lock; -	struct kmem_cache *flow_cache;  	struct hlist_head flows[TUN_NUM_FLOW_ENTRIES];  	struct timer_list flow_gc_timer;  	unsigned long ageing_time; @@ -209,8 +208,8 @@ static struct tun_flow_entry *tun_flow_create(struct tun_struct *tun,  					      struct hlist_head *head,  					      u32 rxhash, u16 queue_index)  { -	struct tun_flow_entry *e = kmem_cache_alloc(tun->flow_cache, -						    GFP_ATOMIC); +	struct tun_flow_entry *e = kmalloc(sizeof(*e), GFP_ATOMIC); +  	if (e) {  		tun_debug(KERN_INFO, tun, "create flow: hash %u index %u\n",  			  rxhash, queue_index); @@ -223,19 +222,12 @@ static struct tun_flow_entry *tun_flow_create(struct tun_struct *tun,  	return e;  } -static void tun_flow_free(struct rcu_head *head) -{ -	struct tun_flow_entry *e -		= container_of(head, struct tun_flow_entry, rcu); -	kmem_cache_free(e->tun->flow_cache, e); -} -  static void tun_flow_delete(struct tun_struct *tun, struct tun_flow_entry *e)  {  	tun_debug(KERN_INFO, tun, "delete flow: hash %u index %u\n",  		  e->rxhash, e->queue_index);  	hlist_del_rcu(&e->hash_link); -	call_rcu(&e->rcu, tun_flow_free); +	kfree_rcu(e, rcu);  }  static void tun_flow_flush(struct tun_struct *tun) @@ -412,8 +404,8 @@ static void __tun_detach(struct tun_file *tfile, bool clean)  	struct tun_struct *tun;  	struct net_device *dev; -	tun = rcu_dereference_protected(tfile->tun, -					lockdep_rtnl_is_held()); +	tun = rtnl_dereference(tfile->tun); +  	if (tun) {  		u16 index = tfile->queue_index;  		BUG_ON(index >= tun->numqueues); @@ -422,8 +414,7 @@ static void __tun_detach(struct tun_file *tfile, bool clean)  		rcu_assign_pointer(tun->tfiles[index],  				   tun->tfiles[tun->numqueues - 1]);  		rcu_assign_pointer(tfile->tun, NULL); -		ntfile = rcu_dereference_protected(tun->tfiles[index], -						   lockdep_rtnl_is_held()); +		ntfile = rtnl_dereference(tun->tfiles[index]);  		ntfile->queue_index = index;  		--tun->numqueues; @@ -437,8 +428,10 @@ static void __tun_detach(struct tun_file *tfile, bool clean)  		/* Drop read queue */  		skb_queue_purge(&tfile->sk.sk_receive_queue);  		tun_set_real_num_queues(tun); -	} else if (tfile->detached && clean) +	} else if (tfile->detached && clean) {  		tun = tun_enable_queue(tfile); +		sock_put(&tfile->sk); +	}  	if (clean) {  		if (tun && tun->numqueues == 0 && tun->numdisabled == 0 && @@ -466,8 +459,7 @@ static void tun_detach_all(struct net_device *dev)  	int i, n = tun->numqueues;  	for (i = 0; i < n; i++) { -		tfile = rcu_dereference_protected(tun->tfiles[i], -						  lockdep_rtnl_is_held()); +		tfile = rtnl_dereference(tun->tfiles[i]);  		BUG_ON(!tfile);  		wake_up_all(&tfile->wq.wait);  		rcu_assign_pointer(tfile->tun, NULL); @@ -477,8 +469,7 @@ static void tun_detach_all(struct net_device *dev)  	synchronize_net();  	for (i = 0; i < n; i++) { -		tfile = rcu_dereference_protected(tun->tfiles[i], -						  lockdep_rtnl_is_held()); +		tfile = rtnl_dereference(tun->tfiles[i]);  		/* Drop read queue */  		skb_queue_purge(&tfile->sk.sk_receive_queue);  		sock_put(&tfile->sk); @@ -489,6 +480,9 @@ static void tun_detach_all(struct net_device *dev)  		sock_put(&tfile->sk);  	}  	BUG_ON(tun->numdisabled != 0); + +	if (tun->flags & TUN_PERSIST) +		module_put(THIS_MODULE);  }  static int tun_attach(struct tun_struct *tun, struct file *file) @@ -497,7 +491,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file)  	int err;  	err = -EINVAL; -	if (rcu_dereference_protected(tfile->tun, lockdep_rtnl_is_held())) +	if (rtnl_dereference(tfile->tun))  		goto out;  	err = -EBUSY; @@ -833,12 +827,6 @@ static int tun_flow_init(struct tun_struct *tun)  {  	int i; -	tun->flow_cache = kmem_cache_create("tun_flow_cache", -					    sizeof(struct tun_flow_entry), 0, 0, -					    NULL); -	if (!tun->flow_cache) -		return -ENOMEM; -  	for (i = 0; i < TUN_NUM_FLOW_ENTRIES; i++)  		INIT_HLIST_HEAD(&tun->flows[i]); @@ -854,10 +842,6 @@ static void tun_flow_uninit(struct tun_struct *tun)  {  	del_timer_sync(&tun->flow_gc_timer);  	tun_flow_flush(tun); - -	/* Wait for completion of call_rcu()'s */ -	rcu_barrier(); -	kmem_cache_destroy(tun->flow_cache);  }  /* Initialize net device. */ @@ -1562,6 +1546,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)  	struct net_device *dev;  	int err; +	if (tfile->detached) +		return -EINVAL; +  	dev = __dev_get_by_name(net, ifr->ifr_name);  	if (dev) {  		if (ifr->ifr_flags & IFF_TUN_EXCL) @@ -1756,8 +1743,7 @@ static void tun_detach_filter(struct tun_struct *tun, int n)  	struct tun_file *tfile;  	for (i = 0; i < n; i++) { -		tfile = rcu_dereference_protected(tun->tfiles[i], -						  lockdep_rtnl_is_held()); +		tfile = rtnl_dereference(tun->tfiles[i]);  		sk_detach_filter(tfile->socket.sk);  	} @@ -1770,8 +1756,7 @@ static int tun_attach_filter(struct tun_struct *tun)  	struct tun_file *tfile;  	for (i = 0; i < tun->numqueues; i++) { -		tfile = rcu_dereference_protected(tun->tfiles[i], -						  lockdep_rtnl_is_held()); +		tfile = rtnl_dereference(tun->tfiles[i]);  		ret = sk_attach_filter(&tun->fprog, tfile->socket.sk);  		if (ret) {  			tun_detach_filter(tun, i); @@ -1789,8 +1774,7 @@ static void tun_set_sndbuf(struct tun_struct *tun)  	int i;  	for (i = 0; i < tun->numqueues; i++) { -		tfile = rcu_dereference_protected(tun->tfiles[i], -						lockdep_rtnl_is_held()); +		tfile = rtnl_dereference(tun->tfiles[i]);  		tfile->socket.sk->sk_sndbuf = tun->sndbuf;  	}  } @@ -1807,13 +1791,10 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr)  		tun = tfile->detached;  		if (!tun)  			ret = -EINVAL; -		else if (tun_not_capable(tun)) -			ret = -EPERM;  		else  			ret = tun_attach(tun, file);  	} else if (ifr->ifr_flags & IFF_DETACH_QUEUE) { -		tun = rcu_dereference_protected(tfile->tun, -						lockdep_rtnl_is_held()); +		tun = rtnl_dereference(tfile->tun);  		if (!tun || !(tun->flags & TUN_TAP_MQ))  			ret = -EINVAL;  		else @@ -1898,10 +1879,11 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,  		/* Disable/Enable persist mode. Keep an extra reference to the  		 * module to prevent the module being unprobed.  		 */ -		if (arg) { +		if (arg && !(tun->flags & TUN_PERSIST)) {  			tun->flags |= TUN_PERSIST;  			__module_get(THIS_MODULE); -		} else { +		} +		if (!arg && (tun->flags & TUN_PERSIST)) {  			tun->flags &= ~TUN_PERSIST;  			module_put(THIS_MODULE);  		} |