diff mbox

[v2,net-next] vlan: Add GRO support for non hardware accelerated vlan

Message ID 1433163307-9299-1-git-send-email-makita.toshiaki@lab.ntt.co.jp
State Accepted, archived
Delegated to: David Miller
Headers show

Commit Message

Toshiaki Makita June 1, 2015, 12:55 p.m. UTC
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

Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
---
v2:
- Add compare_vlan_header() as per Eric Dumazet.

 include/linux/if_vlan.h | 20 +++++++++++
 net/8021q/vlan.c        | 94 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 114 insertions(+)

Comments

Eric Dumazet June 1, 2015, 2:12 p.m. UTC | #1
On Mon, 2015-06-01 at 21:55 +0900, Toshiaki Makita wrote:

> @@ -668,6 +753,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;

My concern about this is :

This might slow down GRO stack for other traffic, if dev_add_offload()
for vlan offloads is called after 
dev_add_offload(&ip_packet_offload) /
dev_add_offload(&ipv6_packet_offload)


This is because list_add_rcu is used and this inserts in front of the
offload_base list.

void dev_add_offload(struct packet_offload *po)
{
        struct list_head *head = &offload_base;

        spin_lock(&offload_lock);
        list_add_rcu(&po->list, head);
        spin_unlock(&offload_lock);
}

Can we ensure offload_base contains a sensible order of expected types ?


--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Toshiaki Makita June 1, 2015, 4:03 p.m. UTC | #2
On 15/06/01 (月) 23:12, Eric Dumazet wrote:
> On Mon, 2015-06-01 at 21:55 +0900, Toshiaki Makita wrote:
>
>> @@ -668,6 +753,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;
>
> My concern about this is :
>
> This might slow down GRO stack for other traffic, if dev_add_offload()
> for vlan offloads is called after
> dev_add_offload(&ip_packet_offload) /
> dev_add_offload(&ipv6_packet_offload)

I didn't have that concern because there are already other similar 
offloads (eth, mpls_uc, mpls_mc). But indeed, they and this could slow 
down GRO stack.

>
>
> This is because list_add_rcu is used and this inserts in front of the
> offload_base list.
>
> void dev_add_offload(struct packet_offload *po)
> {
>          struct list_head *head = &offload_base;
>
>          spin_lock(&offload_lock);
>          list_add_rcu(&po->list, head);
>          spin_unlock(&offload_lock);
> }
>
> Can we ensure offload_base contains a sensible order of expected types ?

Add priority to packet_offload like nf_hook_ops?
Or have dev_add_offload() prioritize IP > IPV6 > others?

Toshiaki Makita
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Eric Dumazet June 1, 2015, 4:28 p.m. UTC | #3
On Tue, 2015-06-02 at 01:03 +0900, Toshiaki Makita wrote:

> I didn't have that concern because there are already other similar 
> offloads (eth, mpls_uc, mpls_mc). But indeed, they and this could slow 
> down GRO stack.

Right, but these mpls offloads are not installed on my kernels ;)

And I already checked that eth was installed before IPv4/IPv6,
although it might be pure luck.



> Add priority to packet_offload like nf_hook_ops?
> Or have dev_add_offload() prioritize IP > IPV6 > others?


You also could use a CONFIG_NET_VLAN_GRO module, so that only
users stuck with non accelerated vlan can load.

But yes, we might take care of this problem at some point.



--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Miller June 1, 2015, 11:51 p.m. UTC | #4
From: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
Date: Mon,  1 Jun 2015 21:55:06 +0900

> 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:
 ...
> Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>

So I applied this, but gave the new offloads a priority of 10.

Thanks.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Toshiaki Makita June 2, 2015, 1:24 a.m. UTC | #5
On 2015/06/02 8:51, David Miller wrote:
> From: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
> Date: Mon,  1 Jun 2015 21:55:06 +0900
> 
>> 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:
>  ...
>> Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
> 
> So I applied this, but gave the new offloads a priority of 10.

Thank you for handling the priority.

Thanks,
Toshiaki Makita

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h
index a40d298..67ce5bd 100644
--- a/include/linux/if_vlan.h
+++ b/include/linux/if_vlan.h
@@ -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_) */
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index 59555f0..9c4f884 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -618,6 +618,90 @@  out:
 	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),
+		.callbacks = {
+			.gro_receive = vlan_gro_receive,
+			.gro_complete = vlan_gro_complete,
+		},
+	},
+	{
+		.type = cpu_to_be16(ETH_P_8021AD),
+		.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);
@@ -645,6 +729,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);
 
@@ -668,6 +753,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;
 
@@ -685,7 +773,13 @@  err0:
 
 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);