Skip to content

Commit

Permalink
vlan: Add GRO support for non hardware accelerated vlan
Browse files Browse the repository at this point in the history
Currently packets with non-hardware-accelerated vlan cannot be handled
by GRO. This causes low performance for 802.1ad and stacked vlan, as their
vlan tags are currently not stripped by hardware.

This patch adds GRO support for non-hardware-accelerated vlan and
improves receive performance of them.

Test Environment:
 vlan device (.1Q) on vlan device (.1ad) on ixgbe (82599)

Result:

- Before

$ netperf -t TCP_STREAM -H 192.168.20.2 -l 60
Recv   Send    Send
Socket Socket  Message  Elapsed
Size   Size    Size     Time     Throughput
bytes  bytes   bytes    secs.    10^6bits/sec

 87380  16384  16384    60.00    5233.17

Rx side CPU usage:
  %usr      %sys      %irq     %soft     %idle
  0.27     58.03      0.00     41.70      0.00

- After

$ netperf -t TCP_STREAM -H 192.168.20.2 -l 60
Recv   Send    Send
Socket Socket  Message  Elapsed
Size   Size    Size     Time     Throughput
bytes  bytes   bytes    secs.    10^6bits/sec

 87380  16384  16384    60.00    7586.85

Rx side CPU usage:
  %usr      %sys      %irq     %soft     %idle
  0.50     25.83      0.00     59.53     14.14

[ Register VLAN offloads with priority 10 -DaveM ]

Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Toshiaki Makita authored and davem330 committed Jun 1, 2015
1 parent 661b689 commit 66e5133
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 0 deletions.
20 changes: 20 additions & 0 deletions include/linux/if_vlan.h
Original file line number Diff line number Diff line change
Expand Up @@ -628,4 +628,24 @@ static inline netdev_features_t vlan_features_check(const struct sk_buff *skb,
return features;
}

/**
* compare_vlan_header - Compare two vlan headers
* @h1: Pointer to vlan header
* @h2: Pointer to vlan header
*
* Compare two vlan headers, returns 0 if equal.
*
* Please note that alignment of h1 & h2 are only guaranteed to be 16 bits.
*/
static inline unsigned long compare_vlan_header(const struct vlan_hdr *h1,
const struct vlan_hdr *h2)
{
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
return *(u32 *)h1 ^ *(u32 *)h2;
#else
return ((__force u32)h1->h_vlan_TCI ^ (__force u32)h2->h_vlan_TCI) |
((__force u32)h1->h_vlan_encapsulated_proto ^
(__force u32)h2->h_vlan_encapsulated_proto);
#endif
}
#endif /* !(_LINUX_IF_VLAN_H_) */
96 changes: 96 additions & 0 deletions net/8021q/vlan.c
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,92 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg)
return err;
}

static struct sk_buff **vlan_gro_receive(struct sk_buff **head,
struct sk_buff *skb)
{
struct sk_buff *p, **pp = NULL;
struct vlan_hdr *vhdr;
unsigned int hlen, off_vlan;
const struct packet_offload *ptype;
__be16 type;
int flush = 1;

off_vlan = skb_gro_offset(skb);
hlen = off_vlan + sizeof(*vhdr);
vhdr = skb_gro_header_fast(skb, off_vlan);
if (skb_gro_header_hard(skb, hlen)) {
vhdr = skb_gro_header_slow(skb, hlen, off_vlan);
if (unlikely(!vhdr))
goto out;
}

type = vhdr->h_vlan_encapsulated_proto;

rcu_read_lock();
ptype = gro_find_receive_by_type(type);
if (!ptype)
goto out_unlock;

flush = 0;

for (p = *head; p; p = p->next) {
struct vlan_hdr *vhdr2;

if (!NAPI_GRO_CB(p)->same_flow)
continue;

vhdr2 = (struct vlan_hdr *)(p->data + off_vlan);
if (compare_vlan_header(vhdr, vhdr2))
NAPI_GRO_CB(p)->same_flow = 0;
}

skb_gro_pull(skb, sizeof(*vhdr));
skb_gro_postpull_rcsum(skb, vhdr, sizeof(*vhdr));
pp = ptype->callbacks.gro_receive(head, skb);

out_unlock:
rcu_read_unlock();
out:
NAPI_GRO_CB(skb)->flush |= flush;

return pp;
}

static int vlan_gro_complete(struct sk_buff *skb, int nhoff)
{
struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb->data + nhoff);
__be16 type = vhdr->h_vlan_encapsulated_proto;
struct packet_offload *ptype;
int err = -ENOENT;

rcu_read_lock();
ptype = gro_find_complete_by_type(type);
if (ptype)
err = ptype->callbacks.gro_complete(skb, nhoff + sizeof(*vhdr));

rcu_read_unlock();
return err;
}

static struct packet_offload vlan_packet_offloads[] __read_mostly = {
{
.type = cpu_to_be16(ETH_P_8021Q),
.priority = 10,
.callbacks = {
.gro_receive = vlan_gro_receive,
.gro_complete = vlan_gro_complete,
},
},
{
.type = cpu_to_be16(ETH_P_8021AD),
.priority = 10,
.callbacks = {
.gro_receive = vlan_gro_receive,
.gro_complete = vlan_gro_complete,
},
},
};

static int __net_init vlan_init_net(struct net *net)
{
struct vlan_net *vn = net_generic(net, vlan_net_id);
Expand Down Expand Up @@ -645,6 +731,7 @@ static struct pernet_operations vlan_net_ops = {
static int __init vlan_proto_init(void)
{
int err;
unsigned int i;

pr_info("%s v%s\n", vlan_fullname, vlan_version);

Expand All @@ -668,6 +755,9 @@ static int __init vlan_proto_init(void)
if (err < 0)
goto err5;

for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++)
dev_add_offload(&vlan_packet_offloads[i]);

vlan_ioctl_set(vlan_ioctl_handler);
return 0;

Expand All @@ -685,7 +775,13 @@ static int __init vlan_proto_init(void)

static void __exit vlan_cleanup_module(void)
{
unsigned int i;

vlan_ioctl_set(NULL);

for (i = 0; i < ARRAY_SIZE(vlan_packet_offloads); i++)
dev_remove_offload(&vlan_packet_offloads[i]);

vlan_netlink_fini();

unregister_netdevice_notifier(&vlan_notifier_block);
Expand Down

0 comments on commit 66e5133

Please sign in to comment.