diff options
Diffstat (limited to 'drivers/net/tap.c')
-rw-r--r-- | drivers/net/tap.c | 43 |
1 files changed, 30 insertions, 13 deletions
diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 4d4173d25dd0..3570c7576993 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -106,7 +106,7 @@ struct major_info { struct rcu_head rcu; dev_t major; struct idr minor_idr; - struct mutex minor_lock; + spinlock_t minor_lock; const char *device_name; struct list_head next; }; @@ -416,15 +416,15 @@ int tap_get_minor(dev_t major, struct tap_dev *tap) goto unlock; } - mutex_lock(&tap_major->minor_lock); - retval = idr_alloc(&tap_major->minor_idr, tap, 1, TAP_NUM_DEVS, GFP_KERNEL); + spin_lock(&tap_major->minor_lock); + retval = idr_alloc(&tap_major->minor_idr, tap, 1, TAP_NUM_DEVS, GFP_ATOMIC); if (retval >= 0) { tap->minor = retval; } else if (retval == -ENOSPC) { netdev_err(tap->dev, "Too many tap devices\n"); retval = -EINVAL; } - mutex_unlock(&tap_major->minor_lock); + spin_unlock(&tap_major->minor_lock); unlock: rcu_read_unlock(); @@ -442,12 +442,12 @@ void tap_free_minor(dev_t major, struct tap_dev *tap) goto unlock; } - mutex_lock(&tap_major->minor_lock); + spin_lock(&tap_major->minor_lock); if (tap->minor) { idr_remove(&tap_major->minor_idr, tap->minor); tap->minor = 0; } - mutex_unlock(&tap_major->minor_lock); + spin_unlock(&tap_major->minor_lock); unlock: rcu_read_unlock(); @@ -467,13 +467,13 @@ static struct tap_dev *dev_get_by_tap_file(int major, int minor) goto unlock; } - mutex_lock(&tap_major->minor_lock); + spin_lock(&tap_major->minor_lock); tap = idr_find(&tap_major->minor_idr, minor); if (tap) { dev = tap->dev; dev_hold(dev); } - mutex_unlock(&tap_major->minor_lock); + spin_unlock(&tap_major->minor_lock); unlock: rcu_read_unlock(); @@ -824,15 +824,17 @@ done: static ssize_t tap_do_read(struct tap_queue *q, struct iov_iter *to, - int noblock) + int noblock, struct sk_buff *skb) { DEFINE_WAIT(wait); - struct sk_buff *skb; ssize_t ret = 0; if (!iov_iter_count(to)) return 0; + if (skb) + goto put; + while (1) { if (!noblock) prepare_to_wait(sk_sleep(&q->sk), &wait, @@ -856,6 +858,7 @@ static ssize_t tap_do_read(struct tap_queue *q, if (!noblock) finish_wait(sk_sleep(&q->sk), &wait); +put: if (skb) { ret = tap_put_user(q, skb, to); if (unlikely(ret < 0)) @@ -872,7 +875,7 @@ static ssize_t tap_read_iter(struct kiocb *iocb, struct iov_iter *to) struct tap_queue *q = file->private_data; ssize_t len = iov_iter_count(to), ret; - ret = tap_do_read(q, to, file->f_flags & O_NONBLOCK); + ret = tap_do_read(q, to, file->f_flags & O_NONBLOCK, NULL); ret = min_t(ssize_t, ret, len); if (ret > 0) iocb->ki_pos = ret; @@ -1155,7 +1158,8 @@ static int tap_recvmsg(struct socket *sock, struct msghdr *m, int ret; if (flags & ~(MSG_DONTWAIT|MSG_TRUNC)) return -EINVAL; - ret = tap_do_read(q, &m->msg_iter, flags & MSG_DONTWAIT); + ret = tap_do_read(q, &m->msg_iter, flags & MSG_DONTWAIT, + m->msg_control); if (ret > total_len) { m->msg_flags |= MSG_TRUNC; ret = flags & MSG_TRUNC ? ret : total_len; @@ -1193,6 +1197,19 @@ struct socket *tap_get_socket(struct file *file) } EXPORT_SYMBOL_GPL(tap_get_socket); +struct skb_array *tap_get_skb_array(struct file *file) +{ + struct tap_queue *q; + + if (file->f_op != &tap_fops) + return ERR_PTR(-EINVAL); + q = file->private_data; + if (!q) + return ERR_PTR(-EBADFD); + return &q->skb_array; +} +EXPORT_SYMBOL_GPL(tap_get_skb_array); + int tap_queue_resize(struct tap_dev *tap) { struct net_device *dev = tap->dev; @@ -1227,7 +1244,7 @@ static int tap_list_add(dev_t major, const char *device_name) tap_major->major = MAJOR(major); idr_init(&tap_major->minor_idr); - mutex_init(&tap_major->minor_lock); + spin_lock_init(&tap_major->minor_lock); tap_major->device_name = device_name; |