diff options
Diffstat (limited to 'drivers/net/macvtap.c')
-rw-r--r-- | drivers/net/macvtap.c | 77 |
1 files changed, 38 insertions, 39 deletions
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 2a89da080317..ff111a89e17f 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -11,7 +11,6 @@ #include <linux/sched.h> #include <linux/types.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/wait.h> #include <linux/cdev.h> #include <linux/idr.h> @@ -70,6 +69,11 @@ static const struct proto_ops macvtap_socket_ops; #define RX_OFFLOADS (NETIF_F_GRO | NETIF_F_LRO) #define TAP_FEATURES (NETIF_F_GSO | NETIF_F_SG) +static struct macvlan_dev *macvtap_get_vlan_rcu(const struct net_device *dev) +{ + return rcu_dereference(dev->rx_handler_data); +} + /* * RCU usage: * The macvtap_queue and the macvlan_dev are loosely coupled, the @@ -219,7 +223,7 @@ static struct macvtap_queue *macvtap_get_queue(struct net_device *dev, goto out; /* Check if we can use flow to select a queue */ - rxq = skb_get_rxhash(skb); + rxq = skb_get_hash(skb); if (rxq) { tap = rcu_dereference(vlan->taps[rxq % numvtaps]); goto out; @@ -271,24 +275,27 @@ static void macvtap_del_queues(struct net_device *dev) sock_put(&qlist[j]->sk); } -/* - * Forward happens for data that gets sent from one macvlan - * endpoint to another one in bridge mode. We just take - * the skb and put it into the receive queue. - */ -static int macvtap_forward(struct net_device *dev, struct sk_buff *skb) +static rx_handler_result_t macvtap_handle_frame(struct sk_buff **pskb) { - struct macvlan_dev *vlan = netdev_priv(dev); - struct macvtap_queue *q = macvtap_get_queue(dev, skb); + struct sk_buff *skb = *pskb; + struct net_device *dev = skb->dev; + struct macvlan_dev *vlan; + struct macvtap_queue *q; netdev_features_t features = TAP_FEATURES; + vlan = macvtap_get_vlan_rcu(dev); + if (!vlan) + return RX_HANDLER_PASS; + + q = macvtap_get_queue(dev, skb); if (!q) - goto drop; + return RX_HANDLER_PASS; if (skb_queue_len(&q->sk.sk_receive_queue) >= dev->tx_queue_len) goto drop; - skb->dev = dev; + skb_push(skb, ETH_HLEN); + /* Apply the forward feature mask so that we perform segmentation * according to users wishes. This only works if VNET_HDR is * enabled. @@ -320,22 +327,13 @@ static int macvtap_forward(struct net_device *dev, struct sk_buff *skb) wake_up: wake_up_interruptible_poll(sk_sleep(&q->sk), POLLIN | POLLRDNORM | POLLRDBAND); - return NET_RX_SUCCESS; + return RX_HANDLER_CONSUMED; drop: + /* Count errors/drops only here, thus don't care about args. */ + macvlan_count_rx(vlan, 0, 0, 0); kfree_skb(skb); - return NET_RX_DROP; -} - -/* - * Receive is for data from the external interface (lowerdev), - * in case of macvtap, we can treat that the same way as - * forward, which macvlan cannot. - */ -static int macvtap_receive(struct sk_buff *skb) -{ - skb_push(skb, ETH_HLEN); - return macvtap_forward(skb->dev, skb); + return RX_HANDLER_CONSUMED; } static int macvtap_get_minor(struct macvlan_dev *vlan) @@ -385,6 +383,8 @@ static int macvtap_newlink(struct net *src_net, struct nlattr *data[]) { struct macvlan_dev *vlan = netdev_priv(dev); + int err; + INIT_LIST_HEAD(&vlan->queue_list); /* Since macvlan supports all offloads by default, make @@ -392,16 +392,20 @@ static int macvtap_newlink(struct net *src_net, */ vlan->tap_features = TUN_OFFLOADS; + err = netdev_rx_handler_register(dev, macvtap_handle_frame, vlan); + if (err) + return err; + /* Don't put anything that may fail after macvlan_common_newlink * because we can't undo what it does. */ - return macvlan_common_newlink(src_net, dev, tb, data, - macvtap_receive, macvtap_forward); + return macvlan_common_newlink(src_net, dev, tb, data); } static void macvtap_dellink(struct net_device *dev, struct list_head *head) { + netdev_rx_handler_unregister(dev); macvtap_del_queues(dev); macvlan_dellink(dev, head); } @@ -588,7 +592,7 @@ static int macvtap_skb_from_vnet_hdr(struct sk_buff *skb, return 0; } -static int macvtap_skb_to_vnet_hdr(const struct sk_buff *skb, +static void macvtap_skb_to_vnet_hdr(const struct sk_buff *skb, struct virtio_net_hdr *vnet_hdr) { memset(vnet_hdr, 0, sizeof(*vnet_hdr)); @@ -619,8 +623,6 @@ static int macvtap_skb_to_vnet_hdr(const struct sk_buff *skb, } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) { vnet_hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID; } /* else everything is zero */ - - return 0; } /* Get packet from user space buffer */ @@ -727,9 +729,8 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; } if (vlan) { - local_bh_disable(); - macvlan_start_xmit(skb, vlan->dev); - local_bh_enable(); + skb->dev = vlan->dev; + dev_queue_xmit(skb); } else { kfree_skb(skb); } @@ -778,9 +779,7 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q, if ((len -= vnet_hdr_len) < 0) return -EINVAL; - ret = macvtap_skb_to_vnet_hdr(skb, &vnet_hdr); - if (ret) - return ret; + macvtap_skb_to_vnet_hdr(skb, &vnet_hdr); if (memcpy_toiovecend(iv, (void *)&vnet_hdr, 0, sizeof(vnet_hdr))) return -EFAULT; @@ -824,7 +823,7 @@ done: return ret ? ret : total; } -static ssize_t macvtap_do_read(struct macvtap_queue *q, struct kiocb *iocb, +static ssize_t macvtap_do_read(struct macvtap_queue *q, const struct iovec *iv, unsigned long len, int noblock) { @@ -875,7 +874,7 @@ static ssize_t macvtap_aio_read(struct kiocb *iocb, const struct iovec *iv, goto out; } - ret = macvtap_do_read(q, iocb, iv, len, file->f_flags & O_NONBLOCK); + ret = macvtap_do_read(q, iv, len, file->f_flags & O_NONBLOCK); ret = min_t(ssize_t, ret, len); if (ret > 0) iocb->ki_pos = ret; @@ -1109,7 +1108,7 @@ static int macvtap_recvmsg(struct kiocb *iocb, struct socket *sock, int ret; if (flags & ~(MSG_DONTWAIT|MSG_TRUNC)) return -EINVAL; - ret = macvtap_do_read(q, iocb, m->msg_iov, total_len, + ret = macvtap_do_read(q, m->msg_iov, total_len, flags & MSG_DONTWAIT); if (ret > total_len) { m->msg_flags |= MSG_TRUNC; |