[PATCH net-next V3] net: af_packet: Untangle packet_snd by adding vpacket_snd

From: Joe Perches
Date: Thu May 12 2011 - 19:25:41 EST


The current packet_snd handles both vlan and non-vlan frames
with initialization of a struct virtio_net_hdr that is unused
by non-vlan frames.

Create a new vpacket_snd that uses this virtio_net_hdr and
remove it and the vlan and gso logic from packet_snd.

Signed-off-by: Joe Perches <joe@xxxxxxxxxxx>
---
net/packet/af_packet.c | 250 ++++++++++++++++++++++++++++++++----------------
1 files changed, 169 insertions(+), 81 deletions(-)

diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 549527b..d4bcf51 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1113,42 +1113,38 @@ static inline struct sk_buff *packet_alloc_skb(struct sock *sk, size_t prepad,
return skb;
}

-static int packet_snd(struct socket *sock,
- struct msghdr *msg, size_t len)
+static int vpacket_snd(struct socket *sock, struct msghdr *msg, size_t len)
{
struct sock *sk = sock->sk;
- struct sockaddr_ll *saddr = (struct sockaddr_ll *)msg->msg_name;
struct sk_buff *skb;
struct net_device *dev;
__be16 proto;
unsigned char *addr;
int ifindex, err, reserve = 0;
- struct virtio_net_hdr vnet_hdr = { 0 };
- int offset = 0;
+ struct virtio_net_hdr vnet_hdr;
+ int offset;
int vnet_hdr_len;
struct packet_sock *po = pkt_sk(sk);
unsigned short gso_type = 0;
+ struct sockaddr_ll *saddr = (struct sockaddr_ll *)msg->msg_name;

- /*
- * Get and verify the address.
- */
-
- if (saddr == NULL) {
- ifindex = po->ifindex;
- proto = po->num;
- addr = NULL;
+ /* Get and verify the address */
+ if (!saddr) {
+ ifindex = po->ifindex;
+ proto = po->num;
+ addr = NULL;
} else {
err = -EINVAL;
if (msg->msg_namelen < sizeof(struct sockaddr_ll))
goto out;
- if (msg->msg_namelen < (saddr->sll_halen + offsetof(struct sockaddr_ll, sll_addr)))
+ if (msg->msg_namelen < (saddr->sll_halen +
+ offsetof(struct sockaddr_ll, sll_addr)))
goto out;
- ifindex = saddr->sll_ifindex;
- proto = saddr->sll_protocol;
- addr = saddr->sll_addr;
+ ifindex = saddr->sll_ifindex;
+ proto = saddr->sll_protocol;
+ addr = saddr->sll_addr;
}

-
dev = dev_get_by_index(sock_net(sk), ifindex);
err = -ENXIO;
if (dev == NULL)
@@ -1160,52 +1156,47 @@ static int packet_snd(struct socket *sock,
if (!(dev->flags & IFF_UP))
goto out_unlock;

- if (po->has_vnet_hdr) {
- vnet_hdr_len = sizeof(vnet_hdr);
+ vnet_hdr_len = sizeof(vnet_hdr);

- err = -EINVAL;
- if (len < vnet_hdr_len)
- goto out_unlock;
-
- len -= vnet_hdr_len;
+ err = -EINVAL;
+ if (len < vnet_hdr_len)
+ goto out_unlock;

- err = memcpy_fromiovec((void *)&vnet_hdr, msg->msg_iov,
- vnet_hdr_len);
- if (err < 0)
- goto out_unlock;
+ len -= vnet_hdr_len;

- if ((vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) &&
- (vnet_hdr.csum_start + vnet_hdr.csum_offset + 2 >
- vnet_hdr.hdr_len))
- vnet_hdr.hdr_len = vnet_hdr.csum_start +
- vnet_hdr.csum_offset + 2;
+ err = memcpy_fromiovec((void *)&vnet_hdr, msg->msg_iov, vnet_hdr_len);
+ if (err < 0)
+ goto out_unlock;

- err = -EINVAL;
- if (vnet_hdr.hdr_len > len)
- goto out_unlock;
+ if ((vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) &&
+ (vnet_hdr.csum_start + vnet_hdr.csum_offset + 2 > vnet_hdr.hdr_len))
+ vnet_hdr.hdr_len = (vnet_hdr.csum_start +
+ vnet_hdr.csum_offset + 2);

- if (vnet_hdr.gso_type != VIRTIO_NET_HDR_GSO_NONE) {
- switch (vnet_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
- case VIRTIO_NET_HDR_GSO_TCPV4:
- gso_type = SKB_GSO_TCPV4;
- break;
- case VIRTIO_NET_HDR_GSO_TCPV6:
- gso_type = SKB_GSO_TCPV6;
- break;
- case VIRTIO_NET_HDR_GSO_UDP:
- gso_type = SKB_GSO_UDP;
- break;
- default:
- goto out_unlock;
- }
+ err = -EINVAL;
+ if (vnet_hdr.hdr_len > len)
+ goto out_unlock;

- if (vnet_hdr.gso_type & VIRTIO_NET_HDR_GSO_ECN)
- gso_type |= SKB_GSO_TCP_ECN;
+ if (vnet_hdr.gso_type != VIRTIO_NET_HDR_GSO_NONE) {
+ switch (vnet_hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
+ case VIRTIO_NET_HDR_GSO_TCPV4:
+ gso_type = SKB_GSO_TCPV4;
+ break;
+ case VIRTIO_NET_HDR_GSO_TCPV6:
+ gso_type = SKB_GSO_TCPV6;
+ break;
+ case VIRTIO_NET_HDR_GSO_UDP:
+ gso_type = SKB_GSO_UDP;
+ break;
+ default:
+ goto out_unlock;
+ }

- if (vnet_hdr.gso_size == 0)
- goto out_unlock;
+ if (vnet_hdr.gso_type & VIRTIO_NET_HDR_GSO_ECN)
+ gso_type |= SKB_GSO_TCP_ECN;

- }
+ if (vnet_hdr.gso_size == 0)
+ goto out_unlock;
}

err = -EMSGSIZE;
@@ -1216,15 +1207,19 @@ static int packet_snd(struct socket *sock,
skb = packet_alloc_skb(sk, LL_ALLOCATED_SPACE(dev),
LL_RESERVED_SPACE(dev), len, vnet_hdr.hdr_len,
msg->msg_flags & MSG_DONTWAIT, &err);
- if (skb == NULL)
+ if (!skb)
goto out_unlock;

skb_set_network_header(skb, reserve);

err = -EINVAL;
- if (sock->type == SOCK_DGRAM &&
- (offset = dev_hard_header(skb, dev, ntohs(proto), addr, NULL, len)) < 0)
- goto out_free;
+ if (sock->type == SOCK_DGRAM) {
+ offset = dev_hard_header(skb, dev, ntohs(proto), addr,
+ NULL, len);
+ if (offset < 0)
+ goto out_free;
+ } else
+ offset = 0;

/* Returns -EFAULT on error */
err = skb_copy_datagram_from_iovec(skb, offset, msg->msg_iov, 0, len);
@@ -1253,33 +1248,123 @@ static int packet_snd(struct socket *sock,
skb->priority = sk->sk_priority;
skb->mark = sk->sk_mark;

- if (po->has_vnet_hdr) {
- if (vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
- if (!skb_partial_csum_set(skb, vnet_hdr.csum_start,
- vnet_hdr.csum_offset)) {
- err = -EINVAL;
- goto out_free;
- }
+ if (vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
+ if (!skb_partial_csum_set(skb, vnet_hdr.csum_start,
+ vnet_hdr.csum_offset)) {
+ err = -EINVAL;
+ goto out_free;
}
+ }
+
+ skb_shinfo(skb)->gso_size = vnet_hdr.gso_size;
+ skb_shinfo(skb)->gso_type = gso_type;

- skb_shinfo(skb)->gso_size = vnet_hdr.gso_size;
- skb_shinfo(skb)->gso_type = gso_type;
+ /* Header must be checked, and gso_segs computed */
+ skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
+ skb_shinfo(skb)->gso_segs = 0;

- /* Header must be checked, and gso_segs computed. */
- skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
- skb_shinfo(skb)->gso_segs = 0;
+ len += vnet_hdr_len;

- len += vnet_hdr_len;
+ /* Now send it */
+ err = dev_queue_xmit(skb);
+ if (err > 0) {
+ err = net_xmit_errno(err);
+ if (err)
+ goto out_unlock;
}

- /*
- * Now send it
- */
+ dev_put(dev);

- err = dev_queue_xmit(skb);
- if (err > 0 && (err = net_xmit_errno(err)) != 0)
+ return len;
+
+out_free:
+ kfree_skb(skb);
+out_unlock:
+ if (dev)
+ dev_put(dev);
+out:
+ return err;
+}
+
+static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
+{
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ struct net_device *dev;
+ __be16 proto;
+ unsigned char *addr;
+ int ifindex, err, reserve = 0;
+ int offset;
+ struct packet_sock *po = pkt_sk(sk);
+ struct sockaddr_ll *saddr = (struct sockaddr_ll *)msg->msg_name;
+
+ /* Get and verify the address */
+ if (!saddr) {
+ ifindex = po->ifindex;
+ proto = po->num;
+ addr = NULL;
+ } else {
+ err = -EINVAL;
+ if (msg->msg_namelen < sizeof(struct sockaddr_ll))
+ goto out;
+ if (msg->msg_namelen < (saddr->sll_halen +
+ offsetof(struct sockaddr_ll, sll_addr)))
+ goto out;
+ ifindex = saddr->sll_ifindex;
+ proto = saddr->sll_protocol;
+ addr = saddr->sll_addr;
+ }
+
+ dev = dev_get_by_index(sock_net(sk), ifindex);
+ err = -ENXIO;
+ if (dev == NULL)
+ goto out_unlock;
+ if (sock->type == SOCK_RAW)
+ reserve = dev->hard_header_len;
+
+ err = -ENETDOWN;
+ if (!(dev->flags & IFF_UP))
+ goto out_unlock;
+
+ err = -ENOBUFS;
+ skb = packet_alloc_skb(sk, LL_ALLOCATED_SPACE(dev),
+ LL_RESERVED_SPACE(dev), len, 0,
+ msg->msg_flags & MSG_DONTWAIT, &err);
+ if (!skb)
goto out_unlock;

+ skb_set_network_header(skb, reserve);
+
+ err = -EINVAL;
+ if (sock->type == SOCK_DGRAM) {
+ offset = dev_hard_header(skb, dev, ntohs(proto), addr,
+ NULL, len);
+ if (offset < 0)
+ goto out_free;
+ } else
+ offset = 0;
+
+ /* Returns -EFAULT on error */
+ err = skb_copy_datagram_from_iovec(skb, offset, msg->msg_iov, 0, len);
+ if (err)
+ goto out_free;
+ err = sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags);
+ if (err < 0)
+ goto out_free;
+
+ skb->protocol = proto;
+ skb->dev = dev;
+ skb->priority = sk->sk_priority;
+ skb->mark = sk->sk_mark;
+
+ /* Now send it */
+ err = dev_queue_xmit(skb);
+ if (err > 0) {
+ err = net_xmit_errno(err);
+ if (err)
+ goto out_unlock;
+ }
+
dev_put(dev);

return len;
@@ -1294,14 +1379,17 @@ out:
}

static int packet_sendmsg(struct kiocb *iocb, struct socket *sock,
- struct msghdr *msg, size_t len)
+ struct msghdr *msg, size_t len)
{
struct sock *sk = sock->sk;
struct packet_sock *po = pkt_sk(sk);
+
if (po->tx_ring.pg_vec)
return tpacket_snd(po, msg);
- else
- return packet_snd(sock, msg, len);
+ else if (po->has_vnet_hdr)
+ return vpacket_snd(sock, msg, len);
+
+ return packet_snd(sock, msg, len);
}

/*
--
1.7.5.rc3.dirty

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/