Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 347290
b: refs/heads/master
c: 4008e97
h: refs/heads/master
v: v3
  • Loading branch information
Jason Wang authored and David S. Miller committed Dec 14, 2012
1 parent 614c139 commit a117716
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 24 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 8fa45a70badf6ce2c57421c17e86e8967ce0d478
refs/heads/master: 4008e97f866db66511f065ae9052e0733a3a8429
86 changes: 63 additions & 23 deletions trunk/drivers/net/tun.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ struct tun_file {
/* only used for fasnyc */
unsigned int flags;
u16 queue_index;
struct list_head next;
struct tun_struct *detached;
};

struct tun_flow_entry {
Expand Down Expand Up @@ -182,6 +184,8 @@ struct tun_struct {
struct hlist_head flows[TUN_NUM_FLOW_ENTRIES];
struct timer_list flow_gc_timer;
unsigned long ageing_time;
unsigned int numdisabled;
struct list_head disabled;
};

static inline u32 tun_hashfn(u32 rxhash)
Expand Down Expand Up @@ -385,6 +389,23 @@ static void tun_set_real_num_queues(struct tun_struct *tun)
netif_set_real_num_rx_queues(tun->dev, tun->numqueues);
}

static void tun_disable_queue(struct tun_struct *tun, struct tun_file *tfile)
{
tfile->detached = tun;
list_add_tail(&tfile->next, &tun->disabled);
++tun->numdisabled;
}

struct tun_struct *tun_enable_queue(struct tun_file *tfile)
{
struct tun_struct *tun = tfile->detached;

tfile->detached = NULL;
list_del_init(&tfile->next);
--tun->numdisabled;
return tun;
}

static void __tun_detach(struct tun_file *tfile, bool clean)
{
struct tun_file *ntfile;
Expand All @@ -406,20 +427,25 @@ static void __tun_detach(struct tun_file *tfile, bool clean)
ntfile->queue_index = index;

--tun->numqueues;
sock_put(&tfile->sk);
if (clean)
sock_put(&tfile->sk);
else
tun_disable_queue(tun, tfile);

synchronize_net();
tun_flow_delete_by_queue(tun, tun->numqueues + 1);
/* Drop read queue */
skb_queue_purge(&tfile->sk.sk_receive_queue);
tun_set_real_num_queues(tun);

if (tun->numqueues == 0 && !(tun->flags & TUN_PERSIST))
if (dev->reg_state == NETREG_REGISTERED)
unregister_netdevice(dev);
}
} else if (tfile->detached && clean)
tun = tun_enable_queue(tfile);

if (clean) {
if (tun && tun->numqueues == 0 && tun->numdisabled == 0 &&
!(tun->flags & TUN_PERSIST))
if (tun->dev->reg_state == NETREG_REGISTERED)
unregister_netdevice(tun->dev);

BUG_ON(!test_bit(SOCK_EXTERNALLY_ALLOCATED,
&tfile->socket.flags));
sk_release_kernel(&tfile->sk);
Expand All @@ -436,7 +462,7 @@ static void tun_detach(struct tun_file *tfile, bool clean)
static void tun_detach_all(struct net_device *dev)
{
struct tun_struct *tun = netdev_priv(dev);
struct tun_file *tfile;
struct tun_file *tfile, *tmp;
int i, n = tun->numqueues;

for (i = 0; i < n; i++) {
Expand All @@ -457,6 +483,12 @@ static void tun_detach_all(struct net_device *dev)
skb_queue_purge(&tfile->sk.sk_receive_queue);
sock_put(&tfile->sk);
}
list_for_each_entry_safe(tfile, tmp, &tun->disabled, next) {
tun_enable_queue(tfile);
skb_queue_purge(&tfile->sk.sk_receive_queue);
sock_put(&tfile->sk);
}
BUG_ON(tun->numdisabled != 0);
}

static int tun_attach(struct tun_struct *tun, struct file *file)
Expand All @@ -473,7 +505,8 @@ static int tun_attach(struct tun_struct *tun, struct file *file)
goto out;

err = -E2BIG;
if (tun->numqueues == MAX_TAP_QUEUES)
if (!tfile->detached &&
tun->numqueues + tun->numdisabled == MAX_TAP_QUEUES)
goto out;

err = 0;
Expand All @@ -487,9 +520,13 @@ static int tun_attach(struct tun_struct *tun, struct file *file)
tfile->queue_index = tun->numqueues;
rcu_assign_pointer(tfile->tun, tun);
rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile);
sock_hold(&tfile->sk);
tun->numqueues++;

if (tfile->detached)
tun_enable_queue(tfile);
else
sock_hold(&tfile->sk);

tun_set_real_num_queues(tun);

/* device is allowed to go away first, so no need to hold extra
Expand Down Expand Up @@ -1349,6 +1386,7 @@ static void tun_free_netdev(struct net_device *dev)
{
struct tun_struct *tun = netdev_priv(dev);

BUG_ON(!(list_empty(&tun->disabled)));
tun_flow_uninit(tun);
free_netdev(dev);
}
Expand Down Expand Up @@ -1543,6 +1581,10 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
err = tun_attach(tun, file);
if (err < 0)
return err;

if (tun->flags & TUN_TAP_MQ &&
(tun->numqueues + tun->numdisabled > 1))
return err;
}
else {
char *name;
Expand Down Expand Up @@ -1601,6 +1643,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
TUN_USER_FEATURES;
dev->features = dev->hw_features;

INIT_LIST_HEAD(&tun->disabled);
err = tun_attach(tun, file);
if (err < 0)
goto err_free_dev;
Expand Down Expand Up @@ -1755,32 +1798,28 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr)
{
struct tun_file *tfile = file->private_data;
struct tun_struct *tun;
struct net_device *dev;
int ret = 0;

rtnl_lock();

if (ifr->ifr_flags & IFF_ATTACH_QUEUE) {
dev = __dev_get_by_name(tfile->net, ifr->ifr_name);
if (!dev) {
ret = -EINVAL;
goto unlock;
}

tun = netdev_priv(dev);
if (dev->netdev_ops != &tap_netdev_ops &&
dev->netdev_ops != &tun_netdev_ops)
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_detach(tfile, false);
else
} else if (ifr->ifr_flags & IFF_DETACH_QUEUE) {
tun = rcu_dereference_protected(tfile->tun,
lockdep_rtnl_is_held());
if (!tun || !(tun->flags & TUN_TAP_MQ))
ret = -EINVAL;
else
__tun_detach(tfile, false);
} else
ret = -EINVAL;

unlock:
rtnl_unlock();
return ret;
}
Expand Down Expand Up @@ -2092,6 +2131,7 @@ static int tun_chr_open(struct inode *inode, struct file * file)

file->private_data = tfile;
set_bit(SOCK_EXTERNALLY_ALLOCATED, &tfile->socket.flags);
INIT_LIST_HEAD(&tfile->next);

return 0;
}
Expand Down

0 comments on commit a117716

Please sign in to comment.