All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel
@ 2012-11-14  1:24 Vlad Yasevich
  2012-11-14  1:24 ` [RFC PATCH 01/13] net: Add generic packet offload infrastructure Vlad Yasevich
                   ` (13 more replies)
  0 siblings, 14 replies; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14  1:24 UTC (permalink / raw)
  To: netdev; +Cc: eric.dumazet, davem

This patch series is a revision suggested by Eric to solve the problem where
a host without IPv6 support drops GSO frames from the guest.

The problem is that GSO/GRO support is per protocol, and when said protocol
is not loaded or is disabled, packets attempting to go through GSO/GRO code paths
are dropped.  This causes retransmissions and a two orders of magnitude drop in
performance.

Prior attempt to solve the problem simply enabled enough GSO/GRO functionality
for IPv6 protocol when IPv6 was diabled.  This did not solve the problem when
the protocol was not build in or was blacklisted.
To solve the problem, it was suggested that we separate GSO/GRO callback
registration from packet processing registrations.  That way
GSO/GRO callbacks can be built into the kernel and always be there.
This patch series attempts to do just that.
* Patches 1 and 2 split the GSO/GRO handlers from packet_type and convert
  to the new structure.
* Patches 3, 4 and 5 do the same thing for net_protocol structure.
* The rest of the patches try to incrementally move the IPv6 GSO/GRO
  code out of the module and into the static kernel build.  Some IPv6
  helper functions also had to move as well.

I am currently testing the patches, but if folks could look this over
and send me any comments, I'd appreciate it.

Thanks
-vlad

Vlad Yasevich (13):
  net:  Add generic packet offload infrastructure.
  net:  Switch to using the new packet offload infrustructure
  net: Add net protocol offload registration infrustructure
  ipv6: Add new offload registration infrastructure.
  ipv4: Switch to using the new offload infrastructure.
  ipv6: Switch to using new offload infrastructure.
  ipv6: Separate ipv6 offload support
  ipv6: Separate tcp offload functionality
  ipv6: Separate out UDP offload functionality
  ipv6: Move exthdr offload support into separate file
  ipv6: Update ipv6 static library with newly needed functions
  ipv4: Pull GSO registration out of inet_init()
  ipv6: Pull IPv6 GSO registration out of the module

 include/linux/netdevice.h  |   15 ++-
 include/net/ip6_checksum.h |   33 +++++
 include/net/protocol.h     |   35 +++---
 net/core/dev.c             |   93 ++++++++++++++-
 net/ipv4/af_inet.c         |   53 ++++++---
 net/ipv4/protocol.c        |   22 ++++-
 net/ipv6/Makefile          |    7 +-
 net/ipv6/af_inet6.c        |  240 -------------------------------------
 net/ipv6/exthdrs.c         |    8 +-
 net/ipv6/exthdrs_offload.c |   41 +++++++
 net/ipv6/ip6_offload.c     |  280 ++++++++++++++++++++++++++++++++++++++++++++
 net/ipv6/ip6_offload.h     |   18 +++
 net/ipv6/ip6_output.c      |   65 ----------
 net/ipv6/output_core.c     |   76 ++++++++++++
 net/ipv6/protocol.c        |   21 ++++
 net/ipv6/tcp_ipv6.c        |  107 +-----------------
 net/ipv6/tcpv6_offload.c   |   93 +++++++++++++++
 net/ipv6/udp.c             |   94 ---------------
 net/ipv6/udp_offload.c     |  117 ++++++++++++++++++
 19 files changed, 867 insertions(+), 551 deletions(-)
 create mode 100644 net/ipv6/exthdrs_offload.c
 create mode 100644 net/ipv6/ip6_offload.c
 create mode 100644 net/ipv6/ip6_offload.h
 create mode 100644 net/ipv6/output_core.c
 create mode 100644 net/ipv6/tcpv6_offload.c
 create mode 100644 net/ipv6/udp_offload.c

-- 
1.7.7.6

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [RFC PATCH 01/13] net:  Add generic packet offload infrastructure.
  2012-11-14  1:24 [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Vlad Yasevich
@ 2012-11-14  1:24 ` Vlad Yasevich
  2012-11-14  2:24   ` Eric Dumazet
  2012-11-14  1:24 ` [RFC PATCH 02/13] core: Switch to using the new packet offload infrustructure Vlad Yasevich
                   ` (12 subsequent siblings)
  13 siblings, 1 reply; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14  1:24 UTC (permalink / raw)
  To: netdev; +Cc: eric.dumazet, davem

Create a new data structure to contain the GRO/GSO callbacks and add
a new registration mechanism.

Singed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 include/linux/netdevice.h |   15 ++++++++
 net/core/dev.c            |   80 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 95 insertions(+), 0 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index f8eda02..d15af51 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1511,6 +1511,18 @@ struct packet_type {
 	struct list_head	list;
 };
 
+struct packet_offload {
+	__be16			type;	/* This is really htons(ether_type). */
+	struct net_device	*dev;	/* NULL is wildcarded here	     */
+	struct sk_buff		*(*gso_segment)(struct sk_buff *skb,
+						netdev_features_t features);
+	int			(*gso_send_check)(struct sk_buff *skb);
+	struct sk_buff		**(*gro_receive)(struct sk_buff **head,
+					       struct sk_buff *skb);
+	int			(*gro_complete)(struct sk_buff *skb);
+	struct list_head	list;
+};
+
 #include <linux/notifier.h>
 
 /* netdevice notifier chain. Please remember to update the rtnetlink
@@ -1605,6 +1617,9 @@ extern struct net_device *__dev_getfirstbyhwtype(struct net *net, unsigned short
 extern void		dev_add_pack(struct packet_type *pt);
 extern void		dev_remove_pack(struct packet_type *pt);
 extern void		__dev_remove_pack(struct packet_type *pt);
+extern void		dev_add_offload(struct packet_offload *po);
+extern void		dev_remove_offload(struct packet_offload *po);
+extern void		__dev_remove_offload(struct packet_offload *po);
 
 extern struct net_device	*dev_get_by_flags_rcu(struct net *net, unsigned short flags,
 						      unsigned short mask);
diff --git a/net/core/dev.c b/net/core/dev.c
index 09cb3f6..10967e5 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -176,8 +176,10 @@
 #define PTYPE_HASH_MASK	(PTYPE_HASH_SIZE - 1)
 
 static DEFINE_SPINLOCK(ptype_lock);
+static DEFINE_SPINLOCK(offload_lock);
 static struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
 static struct list_head ptype_all __read_mostly;	/* Taps */
+static struct list_head offload_base __read_mostly;
 
 /*
  * The @dev_base_head list is protected by @dev_base_lock and the rtnl
@@ -470,6 +472,82 @@ void dev_remove_pack(struct packet_type *pt)
 }
 EXPORT_SYMBOL(dev_remove_pack);
 
+
+/**
+ *	dev_add_offload - register offload handlers
+ *	@po: protocol offload declaration
+ *
+ *	Add protocol offload handlers to the networking stack. The passed
+ *	&proto_offload is linked into kernel lists and may not be freed until
+ *	it has been removed from the kernel lists.
+ *
+ *	This call does not sleep therefore it can not
+ *	guarantee all CPU's that are in middle of receiving packets
+ *	will see the new offload handlers (until the next received packet).
+ */
+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);
+}
+EXPORT_SYMBOL(dev_add_offload);
+
+/**
+ *	__dev_remove_offload	 - remove offload handler
+ *	@po: packet offload declaration
+ *
+ *	Remove a protocol offload handler that was previously added to the
+ *	kernel offload handlers by dev_add_offload(). The passed &offload_type
+ *	is removed from the kernel lists and can be freed or reused once this
+ *	function returns.
+ *
+ *      The packet type might still be in use by receivers
+ *	and must not be freed until after all the CPU's have gone
+ *	through a quiescent state.
+ */
+void __dev_remove_offload(struct packet_offload *po)
+{
+	struct list_head *head = &offload_base;
+	struct packet_offload *po1;
+
+	spin_lock(&ptype_lock);
+
+	list_for_each_entry(po1, head, list) {
+		if (po == po1) {
+			list_del_rcu(&po->list);
+			goto out;
+		}
+	}
+
+	pr_warn("dev_remove_offload: %p not found\n", po);
+out:
+	spin_unlock(&ptype_lock);
+}
+EXPORT_SYMBOL(__dev_remove_offload);
+
+/**
+ *	dev_remove_offload	 - remove packet offload handler
+ *	@po: packet offload declaration
+ *
+ *	Remove a packet offload handler that was previously added to the kernel
+ *	offload handlers by dev_add_offload(). The passed &offload_type is
+ *	removed from the kernel lists and can be freed or reused once this
+ *	function returns.
+ *
+ *	This call sleeps to guarantee that no CPU is looking at the packet
+ *	type after return.
+ */
+void dev_remove_offload(struct packet_offload *po)
+{
+	__dev_remove_offload(po);
+
+	synchronize_net();
+}
+EXPORT_SYMBOL(dev_remove_offload);
+
 /******************************************************************************
 
 		      Device Boot-time Settings Routines
@@ -6662,6 +6740,8 @@ static int __init net_dev_init(void)
 	for (i = 0; i < PTYPE_HASH_SIZE; i++)
 		INIT_LIST_HEAD(&ptype_base[i]);
 
+	INIT_LIST_HEAD(&offload_base);
+
 	if (register_pernet_subsys(&netdev_net_ops))
 		goto out;
 
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [RFC PATCH 02/13] core:  Switch to using the new packet offload infrustructure
  2012-11-14  1:24 [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Vlad Yasevich
  2012-11-14  1:24 ` [RFC PATCH 01/13] net: Add generic packet offload infrastructure Vlad Yasevich
@ 2012-11-14  1:24 ` Vlad Yasevich
  2012-11-14  1:24 ` [RFC PATCH 03/13] net: Add net protocol offload registration infrustructure Vlad Yasevich
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14  1:24 UTC (permalink / raw)
  To: netdev; +Cc: eric.dumazet, davem

Convert to using the new GSO/GRO registration mechanism and new
packet offload structure.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 include/linux/netdevice.h |    6 ------
 net/core/dev.c            |   13 ++++++-------
 net/ipv4/af_inet.c        |    5 +++++
 net/ipv6/af_inet6.c       |    6 ++++++
 4 files changed, 17 insertions(+), 13 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index d15af51..d683508 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1499,12 +1499,6 @@ struct packet_type {
 					 struct net_device *,
 					 struct packet_type *,
 					 struct net_device *);
-	struct sk_buff		*(*gso_segment)(struct sk_buff *skb,
-						netdev_features_t features);
-	int			(*gso_send_check)(struct sk_buff *skb);
-	struct sk_buff		**(*gro_receive)(struct sk_buff **head,
-					       struct sk_buff *skb);
-	int			(*gro_complete)(struct sk_buff *skb);
 	bool			(*id_match)(struct packet_type *ptype,
 					    struct sock *sk);
 	void			*af_packet_priv;
diff --git a/net/core/dev.c b/net/core/dev.c
index 10967e5..64d4302 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2072,7 +2072,7 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb,
 	netdev_features_t features)
 {
 	struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT);
-	struct packet_type *ptype;
+	struct packet_offload *ptype;
 	__be16 type = skb->protocol;
 	int vlan_depth = ETH_HLEN;
 	int err;
@@ -2101,8 +2101,7 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb,
 	}
 
 	rcu_read_lock();
-	list_for_each_entry_rcu(ptype,
-			&ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
+	list_for_each_entry_rcu(ptype, &offload_base, list) {
 		if (ptype->type == type && !ptype->dev && ptype->gso_segment) {
 			if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
 				err = ptype->gso_send_check(skb);
@@ -3522,9 +3521,9 @@ static void flush_backlog(void *arg)
 
 static int napi_gro_complete(struct sk_buff *skb)
 {
-	struct packet_type *ptype;
+	struct packet_offload *ptype;
 	__be16 type = skb->protocol;
-	struct list_head *head = &ptype_base[ntohs(type) & PTYPE_HASH_MASK];
+	struct list_head *head = &offload_base;
 	int err = -ENOENT;
 
 	if (NAPI_GRO_CB(skb)->count == 1) {
@@ -3584,9 +3583,9 @@ EXPORT_SYMBOL(napi_gro_flush);
 enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
 {
 	struct sk_buff **pp = NULL;
-	struct packet_type *ptype;
+	struct packet_offload *ptype;
 	__be16 type = skb->protocol;
-	struct list_head *head = &ptype_base[ntohs(type) & PTYPE_HASH_MASK];
+	struct list_head *head = &offload_base;
 	int same_flow;
 	int mac_len;
 	enum gro_result ret;
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 766c596..4c99c5f 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1662,6 +1662,10 @@ static int ipv4_proc_init(void);
 static struct packet_type ip_packet_type __read_mostly = {
 	.type = cpu_to_be16(ETH_P_IP),
 	.func = ip_rcv,
+};
+
+static struct packet_offload ip_packet_offload __read_mostly = {
+	.type = cpu_to_be16(ETH_P_IP),
 	.gso_send_check = inet_gso_send_check,
 	.gso_segment = inet_gso_segment,
 	.gro_receive = inet_gro_receive,
@@ -1781,6 +1785,7 @@ static int __init inet_init(void)
 
 	ipfrag_init();
 
+	dev_add_offload(&ip_packet_offload);
 	dev_add_pack(&ip_packet_type);
 
 	rc = 0;
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index a974247..6e24517 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -938,6 +938,10 @@ out_unlock:
 static struct packet_type ipv6_packet_type __read_mostly = {
 	.type = cpu_to_be16(ETH_P_IPV6),
 	.func = ipv6_rcv,
+};
+
+static struct packet_offload ipv6_packet_offload __read_mostly = {
+	.type = cpu_to_be16(ETH_P_IPV6),
 	.gso_send_check = ipv6_gso_send_check,
 	.gso_segment = ipv6_gso_segment,
 	.gro_receive = ipv6_gro_receive,
@@ -946,6 +950,7 @@ static struct packet_type ipv6_packet_type __read_mostly = {
 
 static int __init ipv6_packet_init(void)
 {
+	dev_add_offload(&ipv6_packet_offload);
 	dev_add_pack(&ipv6_packet_type);
 	return 0;
 }
@@ -953,6 +958,7 @@ static int __init ipv6_packet_init(void)
 static void ipv6_packet_cleanup(void)
 {
 	dev_remove_pack(&ipv6_packet_type);
+	dev_remove_offload(&ipv6_packet_offload);
 }
 
 static int __net_init ipv6_init_mibs(struct net *net)
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [RFC PATCH 03/13] net: Add net protocol offload registration infrustructure
  2012-11-14  1:24 [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Vlad Yasevich
  2012-11-14  1:24 ` [RFC PATCH 01/13] net: Add generic packet offload infrastructure Vlad Yasevich
  2012-11-14  1:24 ` [RFC PATCH 02/13] core: Switch to using the new packet offload infrustructure Vlad Yasevich
@ 2012-11-14  1:24 ` Vlad Yasevich
  2012-11-14  8:22   ` Nicolas Dichtel
  2012-11-14 23:14   ` Francois Romieu
  2012-11-14  1:24 ` [RFC PATCH 04/13] ipv6: Add new offload registration infrastructure Vlad Yasevich
                   ` (10 subsequent siblings)
  13 siblings, 2 replies; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14  1:24 UTC (permalink / raw)
  To: netdev; +Cc: eric.dumazet, davem

Create a new data structure for IPv4 protocols that holds GRO/GSO
callbacks and a new array to track the protocols that register GRO/GSO.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 include/net/protocol.h |   12 ++++++++++++
 net/ipv4/af_inet.c     |   12 ++++++++++++
 net/ipv4/protocol.c    |   22 +++++++++++++++++++++-
 3 files changed, 45 insertions(+), 1 deletions(-)

diff --git a/include/net/protocol.h b/include/net/protocol.h
index 929528c..d8ecb17 100644
--- a/include/net/protocol.h
+++ b/include/net/protocol.h
@@ -77,6 +77,15 @@ struct inet6_protocol {
 #define INET6_PROTO_GSO_EXTHDR	0x4
 #endif
 
+struct net_offload {
+	int			(*gso_send_check)(struct sk_buff *skb);
+	struct sk_buff	       *(*gso_segment)(struct sk_buff *skb,
+					       netdev_features_t features);
+	struct sk_buff	      **(*gro_receive)(struct sk_buff **head,
+					       struct sk_buff *skb);
+	int			(*gro_complete)(struct sk_buff *skb);
+};
+
 /* This is used to register socket interfaces for IP protocols.  */
 struct inet_protosw {
 	struct list_head list;
@@ -96,6 +105,7 @@ struct inet_protosw {
 #define INET_PROTOSW_ICSK      0x04  /* Is this an inet_connection_sock? */
 
 extern const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS];
+extern const struct net_offload __rcu *inet_offloads[MAX_INET_PROTOS];
 
 #if IS_ENABLED(CONFIG_IPV6)
 extern const struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS];
@@ -103,6 +113,8 @@ extern const struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS];
 
 extern int	inet_add_protocol(const struct net_protocol *prot, unsigned char num);
 extern int	inet_del_protocol(const struct net_protocol *prot, unsigned char num);
+extern int	inet_add_offload(const struct net_offload *prot, unsigned char num);
+extern int	inet_del_offload(const struct net_offload *prot, unsigned char num);
 extern void	inet_register_protosw(struct inet_protosw *p);
 extern void	inet_unregister_protosw(struct inet_protosw *p);
 
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 4c99c5f..3918d86 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1566,6 +1566,13 @@ static const struct net_protocol tcp_protocol = {
 	.netns_ok	=	1,
 };
 
+static const struct net_offload tcp_offload = {
+	.gso_send_check	=	tcp_v4_gso_send_check,
+	.gso_segment	=	tcp_tso_segment,
+	.gro_receive	=	tcp4_gro_receive,
+	.gro_complete	=	tcp4_gro_complete,
+};
+
 static const struct net_protocol udp_protocol = {
 	.handler =	udp_rcv,
 	.err_handler =	udp_err,
@@ -1575,6 +1582,11 @@ static const struct net_protocol udp_protocol = {
 	.netns_ok =	1,
 };
 
+static const struct net_offload udp_offload = {
+	.gso_send_check = udp4_ufo_send_check,
+	.gso_segment = udp4_ufo_fragment,
+};
+
 static const struct net_protocol icmp_protocol = {
 	.handler =	icmp_rcv,
 	.err_handler =	ping_err,
diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c
index 8918eff..1278db8 100644
--- a/net/ipv4/protocol.c
+++ b/net/ipv4/protocol.c
@@ -29,6 +29,7 @@
 #include <net/protocol.h>
 
 const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly;
+const struct net_offload __rcu *inet_offloads[MAX_INET_PROTOS] __read_mostly;
 
 /*
  *	Add a protocol handler to the hash tables
@@ -41,6 +42,13 @@ int inet_add_protocol(const struct net_protocol *prot, unsigned char protocol)
 }
 EXPORT_SYMBOL(inet_add_protocol);
 
+int inet_add_offload(const struct net_offload *prot, unsigned char protocol)
+{
+	return !cmpxchg((const struct net_offload **)&inet_offloads[protocol],
+			NULL, prot) ? 0 : -1;
+}
+EXPORT_SYMBOL(inet_add_offload);
+
 /*
  *	Remove a protocol from the hash tables.
  */
@@ -56,4 +64,16 @@ int inet_del_protocol(const struct net_protocol *prot, unsigned char protocol)
 
 	return ret;
 }
-EXPORT_SYMBOL(inet_del_protocol);
+
+int inet_del_offload(const struct net_offload *prot, unsigned char protocol)
+{
+	int ret;
+
+	ret = (cmpxchg((const struct net_offload **)&inet_offloads[protocol],
+		       prot, NULL) == prot) ? 0 : -1;
+
+	synchronize_net();
+
+	return ret;
+}
+EXPORT_SYMBOL(inet_del_offload);
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [RFC PATCH 04/13] ipv6: Add new offload registration infrastructure.
  2012-11-14  1:24 [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Vlad Yasevich
                   ` (2 preceding siblings ...)
  2012-11-14  1:24 ` [RFC PATCH 03/13] net: Add net protocol offload registration infrustructure Vlad Yasevich
@ 2012-11-14  1:24 ` Vlad Yasevich
  2012-11-14  1:24 ` [RFC PATCH 05/13] ipv4: Switch to using the new offload infrastructure Vlad Yasevich
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14  1:24 UTC (permalink / raw)
  To: netdev; +Cc: eric.dumazet, davem

Create a new data structure for IPv6 protocols that holds GRO/GSO
callbacks and a new array to track the protocols that register GRO/GSO.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 include/net/protocol.h |    4 ++++
 net/ipv6/exthdrs.c     |    8 ++++++++
 net/ipv6/protocol.c    |   21 +++++++++++++++++++++
 net/ipv6/tcp_ipv6.c    |    7 +++++++
 net/ipv6/udp.c         |    5 +++++
 5 files changed, 45 insertions(+), 0 deletions(-)

diff --git a/include/net/protocol.h b/include/net/protocol.h
index d8ecb17..637e1bb 100644
--- a/include/net/protocol.h
+++ b/include/net/protocol.h
@@ -84,6 +84,7 @@ struct net_offload {
 	struct sk_buff	      **(*gro_receive)(struct sk_buff **head,
 					       struct sk_buff *skb);
 	int			(*gro_complete)(struct sk_buff *skb);
+	unsigned int		flags;	/* Flags used by IPv6 for now */
 };
 
 /* This is used to register socket interfaces for IP protocols.  */
@@ -109,6 +110,7 @@ extern const struct net_offload __rcu *inet_offloads[MAX_INET_PROTOS];
 
 #if IS_ENABLED(CONFIG_IPV6)
 extern const struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS];
+extern const struct net_offload __rcu *inet6_offloads[MAX_INET_PROTOS];
 #endif
 
 extern int	inet_add_protocol(const struct net_protocol *prot, unsigned char num);
@@ -121,6 +123,8 @@ extern void	inet_unregister_protosw(struct inet_protosw *p);
 #if IS_ENABLED(CONFIG_IPV6)
 extern int	inet6_add_protocol(const struct inet6_protocol *prot, unsigned char num);
 extern int	inet6_del_protocol(const struct inet6_protocol *prot, unsigned char num);
+extern int	inet6_add_offload(const struct net_offload *prot, unsigned char num);
+extern int	inet6_del_offload(const struct net_offload *prot, unsigned char num);
 extern int	inet6_register_protosw(struct inet_protosw *p);
 extern void	inet6_unregister_protosw(struct inet_protosw *p);
 #endif
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index fa3d9c3..8c01574 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -531,11 +531,19 @@ static const struct inet6_protocol rthdr_protocol = {
 	.flags		=	INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
 };
 
+static const struct net_offload rthdr_offload = {
+	.flags		=	INET6_PROTO_GSO_EXTHDR,
+};
+
 static const struct inet6_protocol destopt_protocol = {
 	.handler	=	ipv6_destopt_rcv,
 	.flags		=	INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
 };
 
+static const struct net_offload dstopt_offload = {
+	.flags		=	INET6_PROTO_GSO_EXTHDR,
+};
+
 static const struct inet6_protocol nodata_protocol = {
 	.handler	=	dst_discard,
 	.flags		=	INET6_PROTO_NOPOLICY,
diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c
index 053082d..f7c53a7 100644
--- a/net/ipv6/protocol.c
+++ b/net/ipv6/protocol.c
@@ -26,6 +26,7 @@
 #include <net/protocol.h>
 
 const struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS] __read_mostly;
+const struct net_offload __rcu *inet6_offloads[MAX_INET_PROTOS] __read_mostly;
 
 int inet6_add_protocol(const struct inet6_protocol *prot, unsigned char protocol)
 {
@@ -34,6 +35,13 @@ int inet6_add_protocol(const struct inet6_protocol *prot, unsigned char protocol
 }
 EXPORT_SYMBOL(inet6_add_protocol);
 
+int inet6_add_offload(const struct net_offload *prot, unsigned char protocol)
+{
+	return !cmpxchg((const struct net_offload **)&inet6_offloads[protocol],
+			NULL, prot) ? 0 : -1;
+}
+EXPORT_SYMBOL(inet6_add_offload);
+
 /*
  *	Remove a protocol from the hash tables.
  */
@@ -50,3 +58,16 @@ int inet6_del_protocol(const struct inet6_protocol *prot, unsigned char protocol
 	return ret;
 }
 EXPORT_SYMBOL(inet6_del_protocol);
+
+int inet6_del_offload(const struct net_offload *prot, unsigned char protocol)
+{
+	int ret;
+
+	ret = (cmpxchg((const struct net_offload **)&inet6_offloads[protocol],
+		       prot, NULL) == prot) ? 0 : -1;
+
+	synchronize_net();
+
+	return ret;
+}
+EXPORT_SYMBOL(inet6_del_offload);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 26175bf..8ce2c30 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -2070,6 +2070,13 @@ static const struct inet6_protocol tcpv6_protocol = {
 	.flags		=	INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
 };
 
+static const struct net_offload tcpv6_offload = {
+	.gso_send_check	=	tcp_v6_gso_send_check,
+	.gso_segment	=	tcp_tso_segment,
+	.gro_receive	=	tcp6_gro_receive,
+	.gro_complete	=	tcp6_gro_complete,
+};
+
 static struct inet_protosw tcpv6_protosw = {
 	.type		=	SOCK_STREAM,
 	.protocol	=	IPPROTO_TCP,
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index fc99972..3ad44e1 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1443,6 +1443,11 @@ static const struct inet6_protocol udpv6_protocol = {
 	.flags		=	INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
 };
 
+static const struct net_offload udpv6_offload = {
+	.gso_send_check =	udp6_ufo_send_check,
+	.gso_segment	=	udp6_ufo_fragment,
+};
+
 /* ------------------------------------------------------------------------ */
 #ifdef CONFIG_PROC_FS
 
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [RFC PATCH 05/13] ipv4: Switch to using the new offload infrastructure.
  2012-11-14  1:24 [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Vlad Yasevich
                   ` (3 preceding siblings ...)
  2012-11-14  1:24 ` [RFC PATCH 04/13] ipv6: Add new offload registration infrastructure Vlad Yasevich
@ 2012-11-14  1:24 ` Vlad Yasevich
  2012-11-14  1:24 ` [RFC PATCH 06/13] ipv6: Switch to using " Vlad Yasevich
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14  1:24 UTC (permalink / raw)
  To: netdev; +Cc: eric.dumazet, davem

Switch IPv4 code base to using the new GRO/GSO calls and data.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 include/net/protocol.h |    6 ------
 net/ipv4/af_inet.c     |   30 ++++++++++++++++--------------
 2 files changed, 16 insertions(+), 20 deletions(-)

diff --git a/include/net/protocol.h b/include/net/protocol.h
index 637e1bb..3bb7051 100644
--- a/include/net/protocol.h
+++ b/include/net/protocol.h
@@ -40,12 +40,6 @@ struct net_protocol {
 	void			(*early_demux)(struct sk_buff *skb);
 	int			(*handler)(struct sk_buff *skb);
 	void			(*err_handler)(struct sk_buff *skb, u32 info);
-	int			(*gso_send_check)(struct sk_buff *skb);
-	struct sk_buff	       *(*gso_segment)(struct sk_buff *skb,
-					       netdev_features_t features);
-	struct sk_buff	      **(*gro_receive)(struct sk_buff **head,
-					       struct sk_buff *skb);
-	int			(*gro_complete)(struct sk_buff *skb);
 	unsigned int		no_policy:1,
 				netns_ok:1;
 };
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 3918d86..66f63ce 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1251,7 +1251,7 @@ EXPORT_SYMBOL(inet_sk_rebuild_header);
 
 static int inet_gso_send_check(struct sk_buff *skb)
 {
-	const struct net_protocol *ops;
+	const struct net_offload *ops;
 	const struct iphdr *iph;
 	int proto;
 	int ihl;
@@ -1275,7 +1275,7 @@ static int inet_gso_send_check(struct sk_buff *skb)
 	err = -EPROTONOSUPPORT;
 
 	rcu_read_lock();
-	ops = rcu_dereference(inet_protos[proto]);
+	ops = rcu_dereference(inet_offloads[proto]);
 	if (likely(ops && ops->gso_send_check))
 		err = ops->gso_send_check(skb);
 	rcu_read_unlock();
@@ -1288,7 +1288,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
 	netdev_features_t features)
 {
 	struct sk_buff *segs = ERR_PTR(-EINVAL);
-	const struct net_protocol *ops;
+	const struct net_offload *ops;
 	struct iphdr *iph;
 	int proto;
 	int ihl;
@@ -1325,7 +1325,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
 	segs = ERR_PTR(-EPROTONOSUPPORT);
 
 	rcu_read_lock();
-	ops = rcu_dereference(inet_protos[proto]);
+	ops = rcu_dereference(inet_offloads[proto]);
 	if (likely(ops && ops->gso_segment))
 		segs = ops->gso_segment(skb, features);
 	rcu_read_unlock();
@@ -1356,7 +1356,7 @@ out:
 static struct sk_buff **inet_gro_receive(struct sk_buff **head,
 					 struct sk_buff *skb)
 {
-	const struct net_protocol *ops;
+	const struct net_offload *ops;
 	struct sk_buff **pp = NULL;
 	struct sk_buff *p;
 	const struct iphdr *iph;
@@ -1378,7 +1378,7 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
 	proto = iph->protocol;
 
 	rcu_read_lock();
-	ops = rcu_dereference(inet_protos[proto]);
+	ops = rcu_dereference(inet_offloads[proto]);
 	if (!ops || !ops->gro_receive)
 		goto out_unlock;
 
@@ -1435,7 +1435,7 @@ static int inet_gro_complete(struct sk_buff *skb)
 {
 	__be16 newlen = htons(skb->len - skb_network_offset(skb));
 	struct iphdr *iph = ip_hdr(skb);
-	const struct net_protocol *ops;
+	const struct net_offload *ops;
 	int proto = iph->protocol;
 	int err = -ENOSYS;
 
@@ -1443,7 +1443,7 @@ static int inet_gro_complete(struct sk_buff *skb)
 	iph->tot_len = newlen;
 
 	rcu_read_lock();
-	ops = rcu_dereference(inet_protos[proto]);
+	ops = rcu_dereference(inet_offloads[proto]);
 	if (WARN_ON(!ops || !ops->gro_complete))
 		goto out_unlock;
 
@@ -1558,10 +1558,6 @@ static const struct net_protocol tcp_protocol = {
 	.early_demux	=	tcp_v4_early_demux,
 	.handler	=	tcp_v4_rcv,
 	.err_handler	=	tcp_v4_err,
-	.gso_send_check	=	tcp_v4_gso_send_check,
-	.gso_segment	=	tcp_tso_segment,
-	.gro_receive	=	tcp4_gro_receive,
-	.gro_complete	=	tcp4_gro_complete,
 	.no_policy	=	1,
 	.netns_ok	=	1,
 };
@@ -1576,8 +1572,6 @@ static const struct net_offload tcp_offload = {
 static const struct net_protocol udp_protocol = {
 	.handler =	udp_rcv,
 	.err_handler =	udp_err,
-	.gso_send_check = udp4_ufo_send_check,
-	.gso_segment = udp4_ufo_fragment,
 	.no_policy =	1,
 	.netns_ok =	1,
 };
@@ -1726,6 +1720,14 @@ static int __init inet_init(void)
 	tcp_prot.sysctl_mem = init_net.ipv4.sysctl_tcp_mem;
 
 	/*
+	 * Add offloads
+	 */
+	if (inet_add_offload(&udp_offload, IPPROTO_UDP) < 0)
+		pr_crit("%s: Cannot add UDP protocol offload\n", __func__);
+	if (inet_add_offload(&tcp_offload, IPPROTO_TCP) < 0)
+		pr_crit("%s: Cannot add TCP protocol offlaod\n", __func__);
+
+	/*
 	 *	Add all the base protocols.
 	 */
 
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [RFC PATCH 06/13] ipv6: Switch to using new offload infrastructure.
  2012-11-14  1:24 [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Vlad Yasevich
                   ` (4 preceding siblings ...)
  2012-11-14  1:24 ` [RFC PATCH 05/13] ipv4: Switch to using the new offload infrastructure Vlad Yasevich
@ 2012-11-14  1:24 ` Vlad Yasevich
  2012-11-14  1:24 ` [RFC PATCH 07/13] ipv6: Separate ipv6 offload support Vlad Yasevich
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14  1:24 UTC (permalink / raw)
  To: netdev; +Cc: eric.dumazet, davem

Switch IPv6 protocol to using the new GRO/GSO calls and data.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 include/net/protocol.h |    8 --------
 net/ipv6/af_inet6.c    |   22 +++++++++++-----------
 net/ipv6/exthdrs.c     |   38 +++++++++++++++++++++++++++++++++++---
 net/ipv6/tcp_ipv6.c    |   17 ++++++++++-------
 net/ipv6/udp.c         |   11 ++++++++---
 5 files changed, 64 insertions(+), 32 deletions(-)

diff --git a/include/net/protocol.h b/include/net/protocol.h
index 3bb7051..7019c16 100644
--- a/include/net/protocol.h
+++ b/include/net/protocol.h
@@ -54,14 +54,6 @@ struct inet6_protocol {
 			       struct inet6_skb_parm *opt,
 			       u8 type, u8 code, int offset,
 			       __be32 info);
-
-	int	(*gso_send_check)(struct sk_buff *skb);
-	struct sk_buff *(*gso_segment)(struct sk_buff *skb,
-				       netdev_features_t features);
-	struct sk_buff **(*gro_receive)(struct sk_buff **head,
-					struct sk_buff *skb);
-	int	(*gro_complete)(struct sk_buff *skb);
-
 	unsigned int	flags;	/* INET6_PROTO_xxx */
 };
 
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 6e24517..eb63dac 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -701,14 +701,14 @@ EXPORT_SYMBOL_GPL(ipv6_opt_accepted);
 
 static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto)
 {
-	const struct inet6_protocol *ops = NULL;
+	const struct net_offload *ops = NULL;
 
 	for (;;) {
 		struct ipv6_opt_hdr *opth;
 		int len;
 
 		if (proto != NEXTHDR_HOP) {
-			ops = rcu_dereference(inet6_protos[proto]);
+			ops = rcu_dereference(inet6_offloads[proto]);
 
 			if (unlikely(!ops))
 				break;
@@ -736,7 +736,7 @@ static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto)
 static int ipv6_gso_send_check(struct sk_buff *skb)
 {
 	const struct ipv6hdr *ipv6h;
-	const struct inet6_protocol *ops;
+	const struct net_offload *ops;
 	int err = -EINVAL;
 
 	if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
@@ -747,7 +747,7 @@ static int ipv6_gso_send_check(struct sk_buff *skb)
 	err = -EPROTONOSUPPORT;
 
 	rcu_read_lock();
-	ops = rcu_dereference(inet6_protos[
+	ops = rcu_dereference(inet6_offloads[
 		ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]);
 
 	if (likely(ops && ops->gso_send_check)) {
@@ -765,7 +765,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
 {
 	struct sk_buff *segs = ERR_PTR(-EINVAL);
 	struct ipv6hdr *ipv6h;
-	const struct inet6_protocol *ops;
+	const struct net_offload *ops;
 	int proto;
 	struct frag_hdr *fptr;
 	unsigned int unfrag_ip6hlen;
@@ -792,7 +792,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
 
 	proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
 	rcu_read_lock();
-	ops = rcu_dereference(inet6_protos[proto]);
+	ops = rcu_dereference(inet6_offloads[proto]);
 	if (likely(ops && ops->gso_segment)) {
 		skb_reset_transport_header(skb);
 		segs = ops->gso_segment(skb, features);
@@ -825,7 +825,7 @@ out:
 static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
 					 struct sk_buff *skb)
 {
-	const struct inet6_protocol *ops;
+	const struct net_offload *ops;
 	struct sk_buff **pp = NULL;
 	struct sk_buff *p;
 	struct ipv6hdr *iph;
@@ -852,7 +852,7 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
 
 	rcu_read_lock();
 	proto = iph->nexthdr;
-	ops = rcu_dereference(inet6_protos[proto]);
+	ops = rcu_dereference(inet6_offloads[proto]);
 	if (!ops || !ops->gro_receive) {
 		__pskb_pull(skb, skb_gro_offset(skb));
 		proto = ipv6_gso_pull_exthdrs(skb, proto);
@@ -860,7 +860,7 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
 		skb_reset_transport_header(skb);
 		__skb_push(skb, skb_gro_offset(skb));
 
-		ops = rcu_dereference(inet6_protos[proto]);
+		ops = rcu_dereference(inet6_offloads[proto]);
 		if (!ops || !ops->gro_receive)
 			goto out_unlock;
 
@@ -915,7 +915,7 @@ out:
 
 static int ipv6_gro_complete(struct sk_buff *skb)
 {
-	const struct inet6_protocol *ops;
+	const struct net_offload *ops;
 	struct ipv6hdr *iph = ipv6_hdr(skb);
 	int err = -ENOSYS;
 
@@ -923,7 +923,7 @@ static int ipv6_gro_complete(struct sk_buff *skb)
 				 sizeof(*iph));
 
 	rcu_read_lock();
-	ops = rcu_dereference(inet6_protos[NAPI_GRO_CB(skb)->proto]);
+	ops = rcu_dereference(inet6_offloads[NAPI_GRO_CB(skb)->proto]);
 	if (WARN_ON(!ops || !ops->gro_complete))
 		goto out_unlock;
 
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 8c01574..d5a807d 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -549,14 +549,44 @@ static const struct inet6_protocol nodata_protocol = {
 	.flags		=	INET6_PROTO_NOPOLICY,
 };
 
+static int ipv6_exthdrs_offload_init(void)
+{
+	int ret;
+
+	ret = inet6_add_offload(&rthdr_offload, IPPROTO_ROUTING);
+	if (!ret)
+		goto out;
+
+	ret = inet6_add_offload(&dstopt_offload, IPPROTO_DSTOPTS);
+	if (!ret)
+		goto out_rt;
+
+out:
+	return ret;
+
+out_rt:
+	inet_del_offload(&rthdr_offload, IPPROTO_ROUTING);
+	goto out;
+}
+
+static void ipv6_exthdrs_offload_exit(void)
+{
+	inet_del_offload(&rthdr_offload, IPPROTO_ROUTING);
+	inet_del_offload(&rthdr_offload, IPPROTO_DSTOPTS);
+}
+
 int __init ipv6_exthdrs_init(void)
 {
 	int ret;
 
-	ret = inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING);
+	ret = ipv6_exthdrs_offload_init();
 	if (ret)
 		goto out;
 
+	ret = inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING);
+	if (ret)
+		goto out_offload;
+
 	ret = inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
 	if (ret)
 		goto out_rthdr;
@@ -567,10 +597,12 @@ int __init ipv6_exthdrs_init(void)
 
 out:
 	return ret;
-out_rthdr:
-	inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
 out_destopt:
 	inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
+out_rthdr:
+	inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
+out_offload:
+	ipv6_exthdrs_offload_exit();
 	goto out;
 };
 
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 8ce2c30..ac59c84 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -2063,10 +2063,6 @@ static const struct inet6_protocol tcpv6_protocol = {
 	.early_demux	=	tcp_v6_early_demux,
 	.handler	=	tcp_v6_rcv,
 	.err_handler	=	tcp_v6_err,
-	.gso_send_check	=	tcp_v6_gso_send_check,
-	.gso_segment	=	tcp_tso_segment,
-	.gro_receive	=	tcp6_gro_receive,
-	.gro_complete	=	tcp6_gro_complete,
 	.flags		=	INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
 };
 
@@ -2113,10 +2109,14 @@ int __init tcpv6_init(void)
 {
 	int ret;
 
-	ret = inet6_add_protocol(&tcpv6_protocol, IPPROTO_TCP);
+	ret = inet6_add_offload(&tcpv6_offload, IPPROTO_TCP);
 	if (ret)
 		goto out;
 
+	ret = inet6_add_protocol(&tcpv6_protocol, IPPROTO_TCP);
+	if (ret)
+		goto out_offload;
+
 	/* register inet6 protocol */
 	ret = inet6_register_protosw(&tcpv6_protosw);
 	if (ret)
@@ -2128,10 +2128,12 @@ int __init tcpv6_init(void)
 out:
 	return ret;
 
-out_tcpv6_protocol:
-	inet6_del_protocol(&tcpv6_protocol, IPPROTO_TCP);
 out_tcpv6_protosw:
 	inet6_unregister_protosw(&tcpv6_protosw);
+out_tcpv6_protocol:
+	inet6_del_protocol(&tcpv6_protocol, IPPROTO_TCP);
+out_offload:
+	inet6_del_offload(&tcpv6_offload, IPPROTO_TCP);
 	goto out;
 }
 
@@ -2140,4 +2142,5 @@ void tcpv6_exit(void)
 	unregister_pernet_subsys(&tcpv6_net_ops);
 	inet6_unregister_protosw(&tcpv6_protosw);
 	inet6_del_protocol(&tcpv6_protocol, IPPROTO_TCP);
+	inet6_del_offload(&tcpv6_offload, IPPROTO_TCP);
 }
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 3ad44e1..e4cc1f4 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1438,8 +1438,6 @@ out:
 static const struct inet6_protocol udpv6_protocol = {
 	.handler	=	udpv6_rcv,
 	.err_handler	=	udpv6_err,
-	.gso_send_check =	udp6_ufo_send_check,
-	.gso_segment	=	udp6_ufo_fragment,
 	.flags		=	INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
 };
 
@@ -1570,10 +1568,14 @@ int __init udpv6_init(void)
 {
 	int ret;
 
-	ret = inet6_add_protocol(&udpv6_protocol, IPPROTO_UDP);
+	ret = inet6_add_offload(&udpv6_offload, IPPROTO_UDP);
 	if (ret)
 		goto out;
 
+	ret = inet6_add_protocol(&udpv6_protocol, IPPROTO_UDP);
+	if (ret)
+		goto out_offload;
+
 	ret = inet6_register_protosw(&udpv6_protosw);
 	if (ret)
 		goto out_udpv6_protocol;
@@ -1582,6 +1584,8 @@ out:
 
 out_udpv6_protocol:
 	inet6_del_protocol(&udpv6_protocol, IPPROTO_UDP);
+out_offload:
+	inet6_del_offload(&udpv6_offload, IPPROTO_UDP);
 	goto out;
 }
 
@@ -1589,4 +1593,5 @@ void udpv6_exit(void)
 {
 	inet6_unregister_protosw(&udpv6_protosw);
 	inet6_del_protocol(&udpv6_protocol, IPPROTO_UDP);
+	inet6_del_offload(&udpv6_offload, IPPROTO_UDP);
 }
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [RFC PATCH 07/13] ipv6: Separate ipv6 offload support
  2012-11-14  1:24 [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Vlad Yasevich
                   ` (5 preceding siblings ...)
  2012-11-14  1:24 ` [RFC PATCH 06/13] ipv6: Switch to using " Vlad Yasevich
@ 2012-11-14  1:24 ` Vlad Yasevich
  2012-11-14  1:24 ` [RFC PATCH 08/13] ipv6: Separate tcp offload functionality Vlad Yasevich
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14  1:24 UTC (permalink / raw)
  To: netdev; +Cc: eric.dumazet, davem

Separate IPv6 offload functionality into its own file
in preparation for the move out of the module

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/ipv6/Makefile      |    3 +
 net/ipv6/af_inet6.c    |  249 +-------------------------------------------
 net/ipv6/ip6_offload.c |  273 ++++++++++++++++++++++++++++++++++++++++++++++++
 net/ipv6/ip6_offload.h |   17 +++
 4 files changed, 296 insertions(+), 246 deletions(-)
 create mode 100644 net/ipv6/ip6_offload.c
 create mode 100644 net/ipv6/ip6_offload.h

diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index b6d3f79..45bd9cd 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -10,6 +10,8 @@ ipv6-objs :=	af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
 		raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
 		exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o
 
+ipv6-offload :=	ip6_offload.o
+
 ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o
 ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o
 
@@ -21,6 +23,7 @@ ipv6-$(CONFIG_PROC_FS) += proc.o
 ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o
 
 ipv6-objs += $(ipv6-y)
+ipv6-objs += $(ipv6-offload)
 
 obj-$(CONFIG_INET6_AH) += ah6.o
 obj-$(CONFIG_INET6_ESP) += esp6.o
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index eb63dac..c84d5ba 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -62,6 +62,7 @@
 
 #include <asm/uaccess.h>
 #include <linux/mroute6.h>
+#include "ip6_offload.h"
 
 MODULE_AUTHOR("Cast of dozens");
 MODULE_DESCRIPTION("IPv6 protocol stack for Linux");
@@ -699,266 +700,22 @@ bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb)
 }
 EXPORT_SYMBOL_GPL(ipv6_opt_accepted);
 
-static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto)
-{
-	const struct net_offload *ops = NULL;
-
-	for (;;) {
-		struct ipv6_opt_hdr *opth;
-		int len;
-
-		if (proto != NEXTHDR_HOP) {
-			ops = rcu_dereference(inet6_offloads[proto]);
-
-			if (unlikely(!ops))
-				break;
-
-			if (!(ops->flags & INET6_PROTO_GSO_EXTHDR))
-				break;
-		}
-
-		if (unlikely(!pskb_may_pull(skb, 8)))
-			break;
-
-		opth = (void *)skb->data;
-		len = ipv6_optlen(opth);
-
-		if (unlikely(!pskb_may_pull(skb, len)))
-			break;
-
-		proto = opth->nexthdr;
-		__skb_pull(skb, len);
-	}
-
-	return proto;
-}
-
-static int ipv6_gso_send_check(struct sk_buff *skb)
-{
-	const struct ipv6hdr *ipv6h;
-	const struct net_offload *ops;
-	int err = -EINVAL;
-
-	if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
-		goto out;
-
-	ipv6h = ipv6_hdr(skb);
-	__skb_pull(skb, sizeof(*ipv6h));
-	err = -EPROTONOSUPPORT;
-
-	rcu_read_lock();
-	ops = rcu_dereference(inet6_offloads[
-		ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]);
-
-	if (likely(ops && ops->gso_send_check)) {
-		skb_reset_transport_header(skb);
-		err = ops->gso_send_check(skb);
-	}
-	rcu_read_unlock();
-
-out:
-	return err;
-}
-
-static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
-	netdev_features_t features)
-{
-	struct sk_buff *segs = ERR_PTR(-EINVAL);
-	struct ipv6hdr *ipv6h;
-	const struct net_offload *ops;
-	int proto;
-	struct frag_hdr *fptr;
-	unsigned int unfrag_ip6hlen;
-	u8 *prevhdr;
-	int offset = 0;
-
-	if (!(features & NETIF_F_V6_CSUM))
-		features &= ~NETIF_F_SG;
-
-	if (unlikely(skb_shinfo(skb)->gso_type &
-		     ~(SKB_GSO_UDP |
-		       SKB_GSO_DODGY |
-		       SKB_GSO_TCP_ECN |
-		       SKB_GSO_TCPV6 |
-		       0)))
-		goto out;
-
-	if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
-		goto out;
-
-	ipv6h = ipv6_hdr(skb);
-	__skb_pull(skb, sizeof(*ipv6h));
-	segs = ERR_PTR(-EPROTONOSUPPORT);
-
-	proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
-	rcu_read_lock();
-	ops = rcu_dereference(inet6_offloads[proto]);
-	if (likely(ops && ops->gso_segment)) {
-		skb_reset_transport_header(skb);
-		segs = ops->gso_segment(skb, features);
-	}
-	rcu_read_unlock();
-
-	if (IS_ERR(segs))
-		goto out;
-
-	for (skb = segs; skb; skb = skb->next) {
-		ipv6h = ipv6_hdr(skb);
-		ipv6h->payload_len = htons(skb->len - skb->mac_len -
-					   sizeof(*ipv6h));
-		if (proto == IPPROTO_UDP) {
-			unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
-			fptr = (struct frag_hdr *)(skb_network_header(skb) +
-				unfrag_ip6hlen);
-			fptr->frag_off = htons(offset);
-			if (skb->next != NULL)
-				fptr->frag_off |= htons(IP6_MF);
-			offset += (ntohs(ipv6h->payload_len) -
-				   sizeof(struct frag_hdr));
-		}
-	}
-
-out:
-	return segs;
-}
-
-static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
-					 struct sk_buff *skb)
-{
-	const struct net_offload *ops;
-	struct sk_buff **pp = NULL;
-	struct sk_buff *p;
-	struct ipv6hdr *iph;
-	unsigned int nlen;
-	unsigned int hlen;
-	unsigned int off;
-	int flush = 1;
-	int proto;
-	__wsum csum;
-
-	off = skb_gro_offset(skb);
-	hlen = off + sizeof(*iph);
-	iph = skb_gro_header_fast(skb, off);
-	if (skb_gro_header_hard(skb, hlen)) {
-		iph = skb_gro_header_slow(skb, hlen, off);
-		if (unlikely(!iph))
-			goto out;
-	}
-
-	skb_gro_pull(skb, sizeof(*iph));
-	skb_set_transport_header(skb, skb_gro_offset(skb));
-
-	flush += ntohs(iph->payload_len) != skb_gro_len(skb);
-
-	rcu_read_lock();
-	proto = iph->nexthdr;
-	ops = rcu_dereference(inet6_offloads[proto]);
-	if (!ops || !ops->gro_receive) {
-		__pskb_pull(skb, skb_gro_offset(skb));
-		proto = ipv6_gso_pull_exthdrs(skb, proto);
-		skb_gro_pull(skb, -skb_transport_offset(skb));
-		skb_reset_transport_header(skb);
-		__skb_push(skb, skb_gro_offset(skb));
-
-		ops = rcu_dereference(inet6_offloads[proto]);
-		if (!ops || !ops->gro_receive)
-			goto out_unlock;
-
-		iph = ipv6_hdr(skb);
-	}
-
-	NAPI_GRO_CB(skb)->proto = proto;
-
-	flush--;
-	nlen = skb_network_header_len(skb);
-
-	for (p = *head; p; p = p->next) {
-		const struct ipv6hdr *iph2;
-		__be32 first_word; /* <Version:4><Traffic_Class:8><Flow_Label:20> */
-
-		if (!NAPI_GRO_CB(p)->same_flow)
-			continue;
-
-		iph2 = ipv6_hdr(p);
-		first_word = *(__be32 *)iph ^ *(__be32 *)iph2 ;
-
-		/* All fields must match except length and Traffic Class. */
-		if (nlen != skb_network_header_len(p) ||
-		    (first_word & htonl(0xF00FFFFF)) ||
-		    memcmp(&iph->nexthdr, &iph2->nexthdr,
-			   nlen - offsetof(struct ipv6hdr, nexthdr))) {
-			NAPI_GRO_CB(p)->same_flow = 0;
-			continue;
-		}
-		/* flush if Traffic Class fields are different */
-		NAPI_GRO_CB(p)->flush |= !!(first_word & htonl(0x0FF00000));
-		NAPI_GRO_CB(p)->flush |= flush;
-	}
-
-	NAPI_GRO_CB(skb)->flush |= flush;
-
-	csum = skb->csum;
-	skb_postpull_rcsum(skb, iph, skb_network_header_len(skb));
-
-	pp = ops->gro_receive(head, skb);
-
-	skb->csum = csum;
-
-out_unlock:
-	rcu_read_unlock();
-
-out:
-	NAPI_GRO_CB(skb)->flush |= flush;
-
-	return pp;
-}
-
-static int ipv6_gro_complete(struct sk_buff *skb)
-{
-	const struct net_offload *ops;
-	struct ipv6hdr *iph = ipv6_hdr(skb);
-	int err = -ENOSYS;
-
-	iph->payload_len = htons(skb->len - skb_network_offset(skb) -
-				 sizeof(*iph));
-
-	rcu_read_lock();
-	ops = rcu_dereference(inet6_offloads[NAPI_GRO_CB(skb)->proto]);
-	if (WARN_ON(!ops || !ops->gro_complete))
-		goto out_unlock;
-
-	err = ops->gro_complete(skb);
-
-out_unlock:
-	rcu_read_unlock();
-
-	return err;
-}
-
 static struct packet_type ipv6_packet_type __read_mostly = {
 	.type = cpu_to_be16(ETH_P_IPV6),
 	.func = ipv6_rcv,
 };
 
-static struct packet_offload ipv6_packet_offload __read_mostly = {
-	.type = cpu_to_be16(ETH_P_IPV6),
-	.gso_send_check = ipv6_gso_send_check,
-	.gso_segment = ipv6_gso_segment,
-	.gro_receive = ipv6_gro_receive,
-	.gro_complete = ipv6_gro_complete,
-};
-
 static int __init ipv6_packet_init(void)
 {
-	dev_add_offload(&ipv6_packet_offload);
+	ipv6_offload_init();
 	dev_add_pack(&ipv6_packet_type);
 	return 0;
 }
 
 static void ipv6_packet_cleanup(void)
 {
+	ipv6_offload_cleanup();
 	dev_remove_pack(&ipv6_packet_type);
-	dev_remove_offload(&ipv6_packet_offload);
 }
 
 static int __net_init ipv6_init_mibs(struct net *net)
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
new file mode 100644
index 0000000..01cf983
--- /dev/null
+++ b/net/ipv6/ip6_offload.c
@@ -0,0 +1,273 @@
+/*
+ *	IPV6 GSO/GRO offload support
+ *	Linux INET6 implementation
+ *
+ *	This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/socket.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+
+#include <net/protocol.h>
+#include <net/ipv6.h>
+
+#include "ip6_offload.h"
+
+static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto)
+{
+	const struct net_offload *ops = NULL;
+
+	for (;;) {
+		struct ipv6_opt_hdr *opth;
+		int len;
+
+		if (proto != NEXTHDR_HOP) {
+			ops = rcu_dereference(inet6_offloads[proto]);
+
+			if (unlikely(!ops))
+				break;
+
+			if (!(ops->flags & INET6_PROTO_GSO_EXTHDR))
+				break;
+		}
+
+		if (unlikely(!pskb_may_pull(skb, 8)))
+			break;
+
+		opth = (void *)skb->data;
+		len = ipv6_optlen(opth);
+
+		if (unlikely(!pskb_may_pull(skb, len)))
+			break;
+
+		proto = opth->nexthdr;
+		__skb_pull(skb, len);
+	}
+
+	return proto;
+}
+
+static int ipv6_gso_send_check(struct sk_buff *skb)
+{
+	const struct ipv6hdr *ipv6h;
+	const struct net_offload *ops;
+	int err = -EINVAL;
+
+	if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
+		goto out;
+
+	ipv6h = ipv6_hdr(skb);
+	__skb_pull(skb, sizeof(*ipv6h));
+	err = -EPROTONOSUPPORT;
+
+	rcu_read_lock();
+	ops = rcu_dereference(inet6_offloads[
+		ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]);
+
+	if (likely(ops && ops->gso_send_check)) {
+		skb_reset_transport_header(skb);
+		err = ops->gso_send_check(skb);
+	}
+	rcu_read_unlock();
+
+out:
+	return err;
+}
+
+static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
+	netdev_features_t features)
+{
+	struct sk_buff *segs = ERR_PTR(-EINVAL);
+	struct ipv6hdr *ipv6h;
+	const struct net_offload *ops;
+	int proto;
+	struct frag_hdr *fptr;
+	unsigned int unfrag_ip6hlen;
+	u8 *prevhdr;
+	int offset = 0;
+
+	if (!(features & NETIF_F_V6_CSUM))
+		features &= ~NETIF_F_SG;
+
+	if (unlikely(skb_shinfo(skb)->gso_type &
+		     ~(SKB_GSO_UDP |
+		       SKB_GSO_DODGY |
+		       SKB_GSO_TCP_ECN |
+		       SKB_GSO_TCPV6 |
+		       0)))
+		goto out;
+
+	if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
+		goto out;
+
+	ipv6h = ipv6_hdr(skb);
+	__skb_pull(skb, sizeof(*ipv6h));
+	segs = ERR_PTR(-EPROTONOSUPPORT);
+
+	proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
+	rcu_read_lock();
+	ops = rcu_dereference(inet6_offloads[proto]);
+	if (likely(ops && ops->gso_segment)) {
+		skb_reset_transport_header(skb);
+		segs = ops->gso_segment(skb, features);
+	}
+	rcu_read_unlock();
+
+	if (IS_ERR(segs))
+		goto out;
+
+	for (skb = segs; skb; skb = skb->next) {
+		ipv6h = ipv6_hdr(skb);
+		ipv6h->payload_len = htons(skb->len - skb->mac_len -
+					   sizeof(*ipv6h));
+		if (proto == IPPROTO_UDP) {
+			unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
+			fptr = (struct frag_hdr *)(skb_network_header(skb) +
+				unfrag_ip6hlen);
+			fptr->frag_off = htons(offset);
+			if (skb->next != NULL)
+				fptr->frag_off |= htons(IP6_MF);
+			offset += (ntohs(ipv6h->payload_len) -
+				   sizeof(struct frag_hdr));
+		}
+	}
+
+out:
+	return segs;
+}
+
+static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
+					 struct sk_buff *skb)
+{
+	const struct net_offload *ops;
+	struct sk_buff **pp = NULL;
+	struct sk_buff *p;
+	struct ipv6hdr *iph;
+	unsigned int nlen;
+	unsigned int hlen;
+	unsigned int off;
+	int flush = 1;
+	int proto;
+	__wsum csum;
+
+	off = skb_gro_offset(skb);
+	hlen = off + sizeof(*iph);
+	iph = skb_gro_header_fast(skb, off);
+	if (skb_gro_header_hard(skb, hlen)) {
+		iph = skb_gro_header_slow(skb, hlen, off);
+		if (unlikely(!iph))
+			goto out;
+	}
+
+	skb_gro_pull(skb, sizeof(*iph));
+	skb_set_transport_header(skb, skb_gro_offset(skb));
+
+	flush += ntohs(iph->payload_len) != skb_gro_len(skb);
+
+	rcu_read_lock();
+	proto = iph->nexthdr;
+	ops = rcu_dereference(inet6_offloads[proto]);
+	if (!ops || !ops->gro_receive) {
+		__pskb_pull(skb, skb_gro_offset(skb));
+		proto = ipv6_gso_pull_exthdrs(skb, proto);
+		skb_gro_pull(skb, -skb_transport_offset(skb));
+		skb_reset_transport_header(skb);
+		__skb_push(skb, skb_gro_offset(skb));
+
+		ops = rcu_dereference(inet6_offloads[proto]);
+		if (!ops || !ops->gro_receive)
+			goto out_unlock;
+
+		iph = ipv6_hdr(skb);
+	}
+
+	NAPI_GRO_CB(skb)->proto = proto;
+
+	flush--;
+	nlen = skb_network_header_len(skb);
+
+	for (p = *head; p; p = p->next) {
+		const struct ipv6hdr *iph2;
+		__be32 first_word; /* <Version:4><Traffic_Class:8><Flow_Label:20> */
+
+		if (!NAPI_GRO_CB(p)->same_flow)
+			continue;
+
+		iph2 = ipv6_hdr(p);
+		first_word = *(__be32 *)iph ^ *(__be32 *)iph2 ;
+
+		/* All fields must match except length and Traffic Class. */
+		if (nlen != skb_network_header_len(p) ||
+		    (first_word & htonl(0xF00FFFFF)) ||
+		    memcmp(&iph->nexthdr, &iph2->nexthdr,
+			   nlen - offsetof(struct ipv6hdr, nexthdr))) {
+			NAPI_GRO_CB(p)->same_flow = 0;
+			continue;
+		}
+		/* flush if Traffic Class fields are different */
+		NAPI_GRO_CB(p)->flush |= !!(first_word & htonl(0x0FF00000));
+		NAPI_GRO_CB(p)->flush |= flush;
+	}
+
+	NAPI_GRO_CB(skb)->flush |= flush;
+
+	csum = skb->csum;
+	skb_postpull_rcsum(skb, iph, skb_network_header_len(skb));
+
+	pp = ops->gro_receive(head, skb);
+
+	skb->csum = csum;
+
+out_unlock:
+	rcu_read_unlock();
+
+out:
+	NAPI_GRO_CB(skb)->flush |= flush;
+
+	return pp;
+}
+
+static int ipv6_gro_complete(struct sk_buff *skb)
+{
+	const struct net_offload *ops;
+	struct ipv6hdr *iph = ipv6_hdr(skb);
+	int err = -ENOSYS;
+
+	iph->payload_len = htons(skb->len - skb_network_offset(skb) -
+				 sizeof(*iph));
+
+	rcu_read_lock();
+	ops = rcu_dereference(inet6_offloads[NAPI_GRO_CB(skb)->proto]);
+	if (WARN_ON(!ops || !ops->gro_complete))
+		goto out_unlock;
+
+	err = ops->gro_complete(skb);
+
+out_unlock:
+	rcu_read_unlock();
+
+	return err;
+}
+
+static struct packet_offload ipv6_packet_offload __read_mostly = {
+	.type = cpu_to_be16(ETH_P_IPV6),
+	.gso_send_check = ipv6_gso_send_check,
+	.gso_segment = ipv6_gso_segment,
+	.gro_receive = ipv6_gro_receive,
+	.gro_complete = ipv6_gro_complete,
+};
+
+void __init ipv6_offload_init(void)
+{
+	dev_add_offload(&ipv6_packet_offload);
+}
+
+void ipv6_offload_cleanup(void)
+{
+	dev_remove_offload(&ipv6_packet_offload);
+}
diff --git a/net/ipv6/ip6_offload.h b/net/ipv6/ip6_offload.h
new file mode 100644
index 0000000..c09614e
--- /dev/null
+++ b/net/ipv6/ip6_offload.h
@@ -0,0 +1,17 @@
+/*
+ *	IPV6 GSO/GRO offload support
+ *	Linux INET6 implementation
+ *
+ *	This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+
+#ifndef __ip6_offload_h
+#define __ip6_offload_h
+
+extern void ipv6_offload_init(void);
+extern void ipv6_offload_cleanup(void);
+
+#endif
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [RFC PATCH 08/13] ipv6: Separate tcp offload functionality
  2012-11-14  1:24 [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Vlad Yasevich
                   ` (6 preceding siblings ...)
  2012-11-14  1:24 ` [RFC PATCH 07/13] ipv6: Separate ipv6 offload support Vlad Yasevich
@ 2012-11-14  1:24 ` Vlad Yasevich
  2012-11-14  1:24 ` [RFC PATCH 09/13] ipv6: Separate out UDP " Vlad Yasevich
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14  1:24 UTC (permalink / raw)
  To: netdev; +Cc: eric.dumazet, davem

Pull TCPv6 offload functionality into its won file in preparation
for moving it out of the module.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 include/net/ip6_checksum.h |   33 +++++++++++++
 net/ipv6/Makefile          |    2 +-
 net/ipv6/ip6_offload.h     |    3 +
 net/ipv6/tcp_ipv6.c        |  113 ++------------------------------------------
 net/ipv6/tcpv6_offload.c   |   98 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 139 insertions(+), 110 deletions(-)
 create mode 100644 net/ipv6/tcpv6_offload.c

diff --git a/include/net/ip6_checksum.h b/include/net/ip6_checksum.h
index bc1b0fd..2f95621 100644
--- a/include/net/ip6_checksum.h
+++ b/include/net/ip6_checksum.h
@@ -91,4 +91,37 @@ static __inline__ __sum16 csum_ipv6_magic(const struct in6_addr *saddr,
 }
 
 #endif
+
+static __inline__ __sum16 tcp_v6_check(int len,
+				   const struct in6_addr *saddr,
+				   const struct in6_addr *daddr,
+				   __wsum base)
+{
+	return csum_ipv6_magic(saddr, daddr, len, IPPROTO_TCP, base);
+}
+
+static inline void __tcp_v6_send_check(struct sk_buff *skb,
+				       const struct in6_addr *saddr,
+				       const struct in6_addr *daddr)
+{
+	struct tcphdr *th = tcp_hdr(skb);
+
+	if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		th->check = ~tcp_v6_check(skb->len, saddr, daddr, 0);
+		skb->csum_start = skb_transport_header(skb) - skb->head;
+		skb->csum_offset = offsetof(struct tcphdr, check);
+	} else {
+		th->check = tcp_v6_check(skb->len, saddr, daddr,
+					 csum_partial(th, th->doff << 2,
+						      skb->csum));
+	}
+}
+
+static inline void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb)
+{
+	struct ipv6_pinfo *np = inet6_sk(sk);
+
+	__tcp_v6_send_check(skb, &np->saddr, &np->daddr);
+}
+
 #endif
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 45bd9cd..f47ad9f 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -10,7 +10,7 @@ ipv6-objs :=	af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
 		raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
 		exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o
 
-ipv6-offload :=	ip6_offload.o
+ipv6-offload :=	ip6_offload.o tcpv6_offload.o
 
 ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o
 ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o
diff --git a/net/ipv6/ip6_offload.h b/net/ipv6/ip6_offload.h
index c09614e..1891946 100644
--- a/net/ipv6/ip6_offload.h
+++ b/net/ipv6/ip6_offload.h
@@ -11,6 +11,9 @@
 #ifndef __ip6_offload_h
 #define __ip6_offload_h
 
+int tcpv6_offload_init(void);
+void tcpv6_offload_cleanup(void);
+
 extern void ipv6_offload_init(void);
 extern void ipv6_offload_cleanup(void);
 
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index ac59c84..58fabc5 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -71,15 +71,13 @@
 
 #include <linux/crypto.h>
 #include <linux/scatterlist.h>
+#include "ip6_offload.h"
 
 static void	tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb);
 static void	tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
 				      struct request_sock *req);
 
 static int	tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);
-static void	__tcp_v6_send_check(struct sk_buff *skb,
-				    const struct in6_addr *saddr,
-				    const struct in6_addr *daddr);
 
 static const struct inet_connection_sock_af_ops ipv6_mapped;
 static const struct inet_connection_sock_af_ops ipv6_specific;
@@ -119,14 +117,6 @@ static void tcp_v6_hash(struct sock *sk)
 	}
 }
 
-static __inline__ __sum16 tcp_v6_check(int len,
-				   const struct in6_addr *saddr,
-				   const struct in6_addr *daddr,
-				   __wsum base)
-{
-	return csum_ipv6_magic(saddr, daddr, len, IPPROTO_TCP, base);
-}
-
 static __u32 tcp_v6_init_sequence(const struct sk_buff *skb)
 {
 	return secure_tcpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32,
@@ -719,94 +709,6 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
 };
 #endif
 
-static void __tcp_v6_send_check(struct sk_buff *skb,
-				const struct in6_addr *saddr, const struct in6_addr *daddr)
-{
-	struct tcphdr *th = tcp_hdr(skb);
-
-	if (skb->ip_summed == CHECKSUM_PARTIAL) {
-		th->check = ~tcp_v6_check(skb->len, saddr, daddr, 0);
-		skb->csum_start = skb_transport_header(skb) - skb->head;
-		skb->csum_offset = offsetof(struct tcphdr, check);
-	} else {
-		th->check = tcp_v6_check(skb->len, saddr, daddr,
-					 csum_partial(th, th->doff << 2,
-						      skb->csum));
-	}
-}
-
-static void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb)
-{
-	struct ipv6_pinfo *np = inet6_sk(sk);
-
-	__tcp_v6_send_check(skb, &np->saddr, &np->daddr);
-}
-
-static int tcp_v6_gso_send_check(struct sk_buff *skb)
-{
-	const struct ipv6hdr *ipv6h;
-	struct tcphdr *th;
-
-	if (!pskb_may_pull(skb, sizeof(*th)))
-		return -EINVAL;
-
-	ipv6h = ipv6_hdr(skb);
-	th = tcp_hdr(skb);
-
-	th->check = 0;
-	skb->ip_summed = CHECKSUM_PARTIAL;
-	__tcp_v6_send_check(skb, &ipv6h->saddr, &ipv6h->daddr);
-	return 0;
-}
-
-static struct sk_buff **tcp6_gro_receive(struct sk_buff **head,
-					 struct sk_buff *skb)
-{
-	const struct ipv6hdr *iph = skb_gro_network_header(skb);
-	__wsum wsum;
-	__sum16 sum;
-
-	switch (skb->ip_summed) {
-	case CHECKSUM_COMPLETE:
-		if (!tcp_v6_check(skb_gro_len(skb), &iph->saddr, &iph->daddr,
-				  skb->csum)) {
-			skb->ip_summed = CHECKSUM_UNNECESSARY;
-			break;
-		}
-flush:
-		NAPI_GRO_CB(skb)->flush = 1;
-		return NULL;
-
-	case CHECKSUM_NONE:
-		wsum = ~csum_unfold(csum_ipv6_magic(&iph->saddr, &iph->daddr,
-						    skb_gro_len(skb),
-						    IPPROTO_TCP, 0));
-		sum = csum_fold(skb_checksum(skb,
-					     skb_gro_offset(skb),
-					     skb_gro_len(skb),
-					     wsum));
-		if (sum)
-			goto flush;
-
-		skb->ip_summed = CHECKSUM_UNNECESSARY;
-		break;
-	}
-
-	return tcp_gro_receive(head, skb);
-}
-
-static int tcp6_gro_complete(struct sk_buff *skb)
-{
-	const struct ipv6hdr *iph = ipv6_hdr(skb);
-	struct tcphdr *th = tcp_hdr(skb);
-
-	th->check = ~tcp_v6_check(skb->len - skb_transport_offset(skb),
-				  &iph->saddr, &iph->daddr, 0);
-	skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
-
-	return tcp_gro_complete(skb);
-}
-
 static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
 				 u32 ts, struct tcp_md5sig_key *key, int rst, u8 tclass)
 {
@@ -2066,13 +1968,6 @@ static const struct inet6_protocol tcpv6_protocol = {
 	.flags		=	INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
 };
 
-static const struct net_offload tcpv6_offload = {
-	.gso_send_check	=	tcp_v6_gso_send_check,
-	.gso_segment	=	tcp_tso_segment,
-	.gro_receive	=	tcp6_gro_receive,
-	.gro_complete	=	tcp6_gro_complete,
-};
-
 static struct inet_protosw tcpv6_protosw = {
 	.type		=	SOCK_STREAM,
 	.protocol	=	IPPROTO_TCP,
@@ -2109,7 +2004,7 @@ int __init tcpv6_init(void)
 {
 	int ret;
 
-	ret = inet6_add_offload(&tcpv6_offload, IPPROTO_TCP);
+	ret = tcpv6_offload_init();
 	if (ret)
 		goto out;
 
@@ -2133,7 +2028,7 @@ out_tcpv6_protosw:
 out_tcpv6_protocol:
 	inet6_del_protocol(&tcpv6_protocol, IPPROTO_TCP);
 out_offload:
-	inet6_del_offload(&tcpv6_offload, IPPROTO_TCP);
+	tcpv6_offload_cleanup();
 	goto out;
 }
 
@@ -2142,5 +2037,5 @@ void tcpv6_exit(void)
 	unregister_pernet_subsys(&tcpv6_net_ops);
 	inet6_unregister_protosw(&tcpv6_protosw);
 	inet6_del_protocol(&tcpv6_protocol, IPPROTO_TCP);
-	inet6_del_offload(&tcpv6_offload, IPPROTO_TCP);
+	tcpv6_offload_cleanup();
 }
diff --git a/net/ipv6/tcpv6_offload.c b/net/ipv6/tcpv6_offload.c
new file mode 100644
index 0000000..edeafed
--- /dev/null
+++ b/net/ipv6/tcpv6_offload.c
@@ -0,0 +1,98 @@
+/*
+ *	IPV6 GSO/GRO offload support
+ *	Linux INET6 implementation
+ *
+ *	This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ *
+ *      TCPv6 GSO/GRO support
+ */
+#include <linux/skbuff.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+#include <net/ip6_checksum.h>
+#include "ip6_offload.h"
+
+static int tcp_v6_gso_send_check(struct sk_buff *skb)
+{
+	const struct ipv6hdr *ipv6h;
+	struct tcphdr *th;
+
+	if (!pskb_may_pull(skb, sizeof(*th)))
+		return -EINVAL;
+
+	ipv6h = ipv6_hdr(skb);
+	th = tcp_hdr(skb);
+
+	th->check = 0;
+	skb->ip_summed = CHECKSUM_PARTIAL;
+	__tcp_v6_send_check(skb, &ipv6h->saddr, &ipv6h->daddr);
+	return 0;
+}
+
+static struct sk_buff **tcp6_gro_receive(struct sk_buff **head,
+					 struct sk_buff *skb)
+{
+	const struct ipv6hdr *iph = skb_gro_network_header(skb);
+	__wsum wsum;
+	__sum16 sum;
+
+	switch (skb->ip_summed) {
+	case CHECKSUM_COMPLETE:
+		if (!tcp_v6_check(skb_gro_len(skb), &iph->saddr, &iph->daddr,
+				  skb->csum)) {
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+			break;
+		}
+flush:
+		NAPI_GRO_CB(skb)->flush = 1;
+		return NULL;
+
+	case CHECKSUM_NONE:
+		wsum = ~csum_unfold(csum_ipv6_magic(&iph->saddr, &iph->daddr,
+						    skb_gro_len(skb),
+						    IPPROTO_TCP, 0));
+		sum = csum_fold(skb_checksum(skb,
+					     skb_gro_offset(skb),
+					     skb_gro_len(skb),
+					     wsum));
+		if (sum)
+			goto flush;
+
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+		break;
+	}
+
+	return tcp_gro_receive(head, skb);
+}
+
+static int tcp6_gro_complete(struct sk_buff *skb)
+{
+	const struct ipv6hdr *iph = ipv6_hdr(skb);
+	struct tcphdr *th = tcp_hdr(skb);
+
+	th->check = ~tcp_v6_check(skb->len - skb_transport_offset(skb),
+				  &iph->saddr, &iph->daddr, 0);
+	skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
+
+	return tcp_gro_complete(skb);
+}
+
+static const struct net_offload tcpv6_offload = {
+	.gso_send_check	=	tcp_v6_gso_send_check,
+	.gso_segment	=	tcp_tso_segment,
+	.gro_receive	=	tcp6_gro_receive,
+	.gro_complete	=	tcp6_gro_complete,
+};
+
+int __init tcpv6_offload_init(void)
+{
+	return inet6_add_offload(&tcpv6_offload, IPPROTO_TCP);
+}
+
+void tcpv6_offload_cleanup(void)
+{
+	inet6_del_offload(&tcpv6_offload, IPPROTO_TCP);
+}
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [RFC PATCH 09/13] ipv6: Separate out UDP offload functionality
  2012-11-14  1:24 [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Vlad Yasevich
                   ` (7 preceding siblings ...)
  2012-11-14  1:24 ` [RFC PATCH 08/13] ipv6: Separate tcp offload functionality Vlad Yasevich
@ 2012-11-14  1:24 ` Vlad Yasevich
  2012-11-14  1:24 ` [RFC PATCH 10/13] ipv6: Move exthdr offload support into separate file Vlad Yasevich
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14  1:24 UTC (permalink / raw)
  To: netdev; +Cc: eric.dumazet, davem

Pull UDP GSO code into a separate file in preparation for moving
the code out of the module.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/ipv6/Makefile      |    2 +-
 net/ipv6/ip6_offload.h |    3 +
 net/ipv6/udp.c         |  104 ++---------------------------------------
 net/ipv6/udp_offload.c |  122 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 130 insertions(+), 101 deletions(-)
 create mode 100644 net/ipv6/udp_offload.c

diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index f47ad9f..04b5c96 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -10,7 +10,7 @@ ipv6-objs :=	af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
 		raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
 		exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o
 
-ipv6-offload :=	ip6_offload.o tcpv6_offload.o
+ipv6-offload :=	ip6_offload.o tcpv6_offload.o udp_offload.o
 
 ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o
 ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o
diff --git a/net/ipv6/ip6_offload.h b/net/ipv6/ip6_offload.h
index 1891946..dff7936 100644
--- a/net/ipv6/ip6_offload.h
+++ b/net/ipv6/ip6_offload.h
@@ -11,6 +11,9 @@
 #ifndef __ip6_offload_h
 #define __ip6_offload_h
 
+int udp_offload_init(void);
+void udp_offload_cleanup(void);
+
 int tcpv6_offload_init(void);
 void tcpv6_offload_cleanup(void);
 
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index e4cc1f4..013fef7 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -50,6 +50,7 @@
 #include <linux/seq_file.h>
 #include <trace/events/skb.h>
 #include "udp_impl.h"
+#include "ip6_offload.h"
 
 int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
 {
@@ -1343,109 +1344,12 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname,
 }
 #endif
 
-static int udp6_ufo_send_check(struct sk_buff *skb)
-{
-	const struct ipv6hdr *ipv6h;
-	struct udphdr *uh;
-
-	if (!pskb_may_pull(skb, sizeof(*uh)))
-		return -EINVAL;
-
-	ipv6h = ipv6_hdr(skb);
-	uh = udp_hdr(skb);
-
-	uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
-				     IPPROTO_UDP, 0);
-	skb->csum_start = skb_transport_header(skb) - skb->head;
-	skb->csum_offset = offsetof(struct udphdr, check);
-	skb->ip_summed = CHECKSUM_PARTIAL;
-	return 0;
-}
-
-static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
-	netdev_features_t features)
-{
-	struct sk_buff *segs = ERR_PTR(-EINVAL);
-	unsigned int mss;
-	unsigned int unfrag_ip6hlen, unfrag_len;
-	struct frag_hdr *fptr;
-	u8 *mac_start, *prevhdr;
-	u8 nexthdr;
-	u8 frag_hdr_sz = sizeof(struct frag_hdr);
-	int offset;
-	__wsum csum;
-
-	mss = skb_shinfo(skb)->gso_size;
-	if (unlikely(skb->len <= mss))
-		goto out;
-
-	if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
-		/* Packet is from an untrusted source, reset gso_segs. */
-		int type = skb_shinfo(skb)->gso_type;
-
-		if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) ||
-			     !(type & (SKB_GSO_UDP))))
-			goto out;
-
-		skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);
-
-		segs = NULL;
-		goto out;
-	}
-
-	/* Do software UFO. Complete and fill in the UDP checksum as HW cannot
-	 * do checksum of UDP packets sent as multiple IP fragments.
-	 */
-	offset = skb_checksum_start_offset(skb);
-	csum = skb_checksum(skb, offset, skb->len - offset, 0);
-	offset += skb->csum_offset;
-	*(__sum16 *)(skb->data + offset) = csum_fold(csum);
-	skb->ip_summed = CHECKSUM_NONE;
-
-	/* Check if there is enough headroom to insert fragment header. */
-	if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) &&
-	    pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC))
-		goto out;
-
-	/* Find the unfragmentable header and shift it left by frag_hdr_sz
-	 * bytes to insert fragment header.
-	 */
-	unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
-	nexthdr = *prevhdr;
-	*prevhdr = NEXTHDR_FRAGMENT;
-	unfrag_len = skb_network_header(skb) - skb_mac_header(skb) +
-		     unfrag_ip6hlen;
-	mac_start = skb_mac_header(skb);
-	memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len);
-
-	skb->mac_header -= frag_hdr_sz;
-	skb->network_header -= frag_hdr_sz;
-
-	fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
-	fptr->nexthdr = nexthdr;
-	fptr->reserved = 0;
-	ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
-
-	/* Fragment the skb. ipv6 header and the remaining fields of the
-	 * fragment header are updated in ipv6_gso_segment()
-	 */
-	segs = skb_segment(skb, features);
-
-out:
-	return segs;
-}
-
 static const struct inet6_protocol udpv6_protocol = {
 	.handler	=	udpv6_rcv,
 	.err_handler	=	udpv6_err,
 	.flags		=	INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
 };
 
-static const struct net_offload udpv6_offload = {
-	.gso_send_check =	udp6_ufo_send_check,
-	.gso_segment	=	udp6_ufo_fragment,
-};
-
 /* ------------------------------------------------------------------------ */
 #ifdef CONFIG_PROC_FS
 
@@ -1568,7 +1472,7 @@ int __init udpv6_init(void)
 {
 	int ret;
 
-	ret = inet6_add_offload(&udpv6_offload, IPPROTO_UDP);
+	ret = udp_offload_init();
 	if (ret)
 		goto out;
 
@@ -1585,7 +1489,7 @@ out:
 out_udpv6_protocol:
 	inet6_del_protocol(&udpv6_protocol, IPPROTO_UDP);
 out_offload:
-	inet6_del_offload(&udpv6_offload, IPPROTO_UDP);
+	udp_offload_cleanup();
 	goto out;
 }
 
@@ -1593,5 +1497,5 @@ void udpv6_exit(void)
 {
 	inet6_unregister_protosw(&udpv6_protosw);
 	inet6_del_protocol(&udpv6_protocol, IPPROTO_UDP);
-	inet6_del_offload(&udpv6_offload, IPPROTO_UDP);
+	udp_offload_cleanup();
 }
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
new file mode 100644
index 0000000..f964d2b
--- /dev/null
+++ b/net/ipv6/udp_offload.c
@@ -0,0 +1,122 @@
+/*
+ *	IPV6 GSO/GRO offload support
+ *	Linux INET6 implementation
+ *
+ *	This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ *
+ *      UDPv6 GSO support
+ */
+#include <linux/skbuff.h>
+#include <net/protocol.h>
+#include <net/ipv6.h>
+#include <net/udp.h>
+#include "ip6_offload.h"
+
+static int udp6_ufo_send_check(struct sk_buff *skb)
+{
+	const struct ipv6hdr *ipv6h;
+	struct udphdr *uh;
+
+	if (!pskb_may_pull(skb, sizeof(*uh)))
+		return -EINVAL;
+
+	ipv6h = ipv6_hdr(skb);
+	uh = udp_hdr(skb);
+
+	uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
+				     IPPROTO_UDP, 0);
+	skb->csum_start = skb_transport_header(skb) - skb->head;
+	skb->csum_offset = offsetof(struct udphdr, check);
+	skb->ip_summed = CHECKSUM_PARTIAL;
+	return 0;
+}
+
+static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
+	netdev_features_t features)
+{
+	struct sk_buff *segs = ERR_PTR(-EINVAL);
+	unsigned int mss;
+	unsigned int unfrag_ip6hlen, unfrag_len;
+	struct frag_hdr *fptr;
+	u8 *mac_start, *prevhdr;
+	u8 nexthdr;
+	u8 frag_hdr_sz = sizeof(struct frag_hdr);
+	int offset;
+	__wsum csum;
+
+	mss = skb_shinfo(skb)->gso_size;
+	if (unlikely(skb->len <= mss))
+		goto out;
+
+	if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
+		/* Packet is from an untrusted source, reset gso_segs. */
+		int type = skb_shinfo(skb)->gso_type;
+
+		if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) ||
+			     !(type & (SKB_GSO_UDP))))
+			goto out;
+
+		skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);
+
+		segs = NULL;
+		goto out;
+	}
+
+	/* Do software UFO. Complete and fill in the UDP checksum as HW cannot
+	 * do checksum of UDP packets sent as multiple IP fragments.
+	 */
+	offset = skb_checksum_start_offset(skb);
+	csum = skb_checksum(skb, offset, skb->len - offset, 0);
+	offset += skb->csum_offset;
+	*(__sum16 *)(skb->data + offset) = csum_fold(csum);
+	skb->ip_summed = CHECKSUM_NONE;
+
+	/* Check if there is enough headroom to insert fragment header. */
+	if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) &&
+	    pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC))
+		goto out;
+
+	/* Find the unfragmentable header and shift it left by frag_hdr_sz
+	 * bytes to insert fragment header.
+	 */
+	unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
+	nexthdr = *prevhdr;
+	*prevhdr = NEXTHDR_FRAGMENT;
+	unfrag_len = skb_network_header(skb) - skb_mac_header(skb) +
+		     unfrag_ip6hlen;
+	mac_start = skb_mac_header(skb);
+	memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len);
+
+	skb->mac_header -= frag_hdr_sz;
+	skb->network_header -= frag_hdr_sz;
+
+	fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
+	fptr->nexthdr = nexthdr;
+	fptr->reserved = 0;
+	ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
+
+	/* Fragment the skb. ipv6 header and the remaining fields of the
+	 * fragment header are updated in ipv6_gso_segment()
+	 */
+	segs = skb_segment(skb, features);
+
+out:
+	return segs;
+}
+static const struct net_offload udpv6_offload = {
+	.gso_send_check =	udp6_ufo_send_check,
+	.gso_segment	=	udp6_ufo_fragment,
+};
+
+int __init udp_offload_init(void)
+{
+	return inet6_add_offload(&udpv6_offload, IPPROTO_UDP);
+}
+
+void udp_offload_cleanup(void)
+{
+	inet6_del_offload(&udpv6_offload, IPPROTO_UDP);
+}
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [RFC PATCH 10/13] ipv6: Move exthdr offload support into separate file
  2012-11-14  1:24 [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Vlad Yasevich
                   ` (8 preceding siblings ...)
  2012-11-14  1:24 ` [RFC PATCH 09/13] ipv6: Separate out UDP " Vlad Yasevich
@ 2012-11-14  1:24 ` Vlad Yasevich
  2012-11-14  1:24 ` [RFC PATCH 11/13] ipv6: Update ipv6 static library with newly needed functions Vlad Yasevich
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14  1:24 UTC (permalink / raw)
  To: netdev; +Cc: eric.dumazet, davem

Move the exthdr offload functionality into a separeate
file in preparate for moving it out of the module

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/ipv6/Makefile          |    2 +-
 net/ipv6/exthdrs.c         |   40 +++---------------------------------
 net/ipv6/exthdrs_offload.c |   48 ++++++++++++++++++++++++++++++++++++++++++++
 net/ipv6/ip6_offload.h     |    3 ++
 4 files changed, 56 insertions(+), 37 deletions(-)
 create mode 100644 net/ipv6/exthdrs_offload.c

diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 04b5c96..7f25077 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -10,7 +10,7 @@ ipv6-objs :=	af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
 		raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
 		exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o
 
-ipv6-offload :=	ip6_offload.o tcpv6_offload.o udp_offload.o
+ipv6-offload :=	ip6_offload.o tcpv6_offload.o udp_offload.o exthdrs_offload.o
 
 ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o
 ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index d5a807d..dc0faab 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -48,6 +48,7 @@
 #endif
 
 #include <asm/uaccess.h>
+#include "ip6_offload.h"
 
 int ipv6_find_tlv(struct sk_buff *skb, int offset, int type)
 {
@@ -528,20 +529,12 @@ unknown_rh:
 
 static const struct inet6_protocol rthdr_protocol = {
 	.handler	=	ipv6_rthdr_rcv,
-	.flags		=	INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
-};
-
-static const struct net_offload rthdr_offload = {
-	.flags		=	INET6_PROTO_GSO_EXTHDR,
+	.flags		=	INET6_PROTO_NOPOLICY,
 };
 
 static const struct inet6_protocol destopt_protocol = {
 	.handler	=	ipv6_destopt_rcv,
-	.flags		=	INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
-};
-
-static const struct net_offload dstopt_offload = {
-	.flags		=	INET6_PROTO_GSO_EXTHDR,
+	.flags		=	INET6_PROTO_NOPOLICY,
 };
 
 static const struct inet6_protocol nodata_protocol = {
@@ -549,32 +542,6 @@ static const struct inet6_protocol nodata_protocol = {
 	.flags		=	INET6_PROTO_NOPOLICY,
 };
 
-static int ipv6_exthdrs_offload_init(void)
-{
-	int ret;
-
-	ret = inet6_add_offload(&rthdr_offload, IPPROTO_ROUTING);
-	if (!ret)
-		goto out;
-
-	ret = inet6_add_offload(&dstopt_offload, IPPROTO_DSTOPTS);
-	if (!ret)
-		goto out_rt;
-
-out:
-	return ret;
-
-out_rt:
-	inet_del_offload(&rthdr_offload, IPPROTO_ROUTING);
-	goto out;
-}
-
-static void ipv6_exthdrs_offload_exit(void)
-{
-	inet_del_offload(&rthdr_offload, IPPROTO_ROUTING);
-	inet_del_offload(&rthdr_offload, IPPROTO_DSTOPTS);
-}
-
 int __init ipv6_exthdrs_init(void)
 {
 	int ret;
@@ -608,6 +575,7 @@ out_offload:
 
 void ipv6_exthdrs_exit(void)
 {
+	ipv6_exthdrs_offload_exit();
 	inet6_del_protocol(&nodata_protocol, IPPROTO_NONE);
 	inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
 	inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
diff --git a/net/ipv6/exthdrs_offload.c b/net/ipv6/exthdrs_offload.c
new file mode 100644
index 0000000..913b9fc
--- /dev/null
+++ b/net/ipv6/exthdrs_offload.c
@@ -0,0 +1,48 @@
+/*
+ *	IPV6 GSO/GRO offload support
+ *	Linux INET6 implementation
+ *
+ *	This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ *
+ *      IPV6 Extension Header GSO/GRO support
+ */
+#include <net/protocol.h>
+#include "ip6_offload.h"
+
+static const struct net_offload rthdr_offload = {
+	.flags		=	INET6_PROTO_GSO_EXTHDR,
+};
+
+static const struct net_offload dstopt_offload = {
+	.flags		=	INET6_PROTO_GSO_EXTHDR,
+};
+
+int __init ipv6_exthdrs_offload_init(void)
+{
+	int ret;
+
+	ret = inet6_add_offload(&rthdr_offload, IPPROTO_ROUTING);
+	if (!ret)
+		goto out;
+
+	ret = inet6_add_offload(&dstopt_offload, IPPROTO_DSTOPTS);
+	if (!ret)
+		goto out_rt;
+
+out:
+	return ret;
+
+out_rt:
+	inet_del_offload(&rthdr_offload, IPPROTO_ROUTING);
+	goto out;
+}
+
+void ipv6_exthdrs_offload_exit(void)
+{
+	inet_del_offload(&rthdr_offload, IPPROTO_ROUTING);
+	inet_del_offload(&rthdr_offload, IPPROTO_DSTOPTS);
+}
+
diff --git a/net/ipv6/ip6_offload.h b/net/ipv6/ip6_offload.h
index dff7936..4e88ddb 100644
--- a/net/ipv6/ip6_offload.h
+++ b/net/ipv6/ip6_offload.h
@@ -11,6 +11,9 @@
 #ifndef __ip6_offload_h
 #define __ip6_offload_h
 
+int ipv6_exthdrs_offload_init(void);
+void ipv6_exthdrs_offload_exit(void);
+
 int udp_offload_init(void);
 void udp_offload_cleanup(void);
 
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [RFC PATCH 11/13] ipv6: Update ipv6 static library with newly needed functions
  2012-11-14  1:24 [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Vlad Yasevich
                   ` (9 preceding siblings ...)
  2012-11-14  1:24 ` [RFC PATCH 10/13] ipv6: Move exthdr offload support into separate file Vlad Yasevich
@ 2012-11-14  1:24 ` Vlad Yasevich
  2012-11-14  1:24 ` [RFC PATCH 12/13] ipv4: Pull GSO registration out of inet_init() Vlad Yasevich
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14  1:24 UTC (permalink / raw)
  To: netdev; +Cc: eric.dumazet, davem

UDP offload needs some additional functions to be in the static kernel
for it work correclty.  Move those functions into the core.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/ipv6/Makefile      |    2 +-
 net/ipv6/ip6_output.c  |   65 -----------------------------------------
 net/ipv6/output_core.c |   76 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 77 insertions(+), 66 deletions(-)
 create mode 100644 net/ipv6/output_core.c

diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 7f25077..cdca302 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -41,6 +41,6 @@ obj-$(CONFIG_IPV6_SIT) += sit.o
 obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o
 obj-$(CONFIG_IPV6_GRE) += ip6_gre.o
 
-obj-y += addrconf_core.o exthdrs_core.o
+obj-y += addrconf_core.o exthdrs_core.o output_core.o
 
 obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index aece3e7..45e6558 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -545,71 +545,6 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
 	skb_copy_secmark(to, from);
 }
 
-int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
-{
-	u16 offset = sizeof(struct ipv6hdr);
-	struct ipv6_opt_hdr *exthdr =
-				(struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1);
-	unsigned int packet_len = skb->tail - skb->network_header;
-	int found_rhdr = 0;
-	*nexthdr = &ipv6_hdr(skb)->nexthdr;
-
-	while (offset + 1 <= packet_len) {
-
-		switch (**nexthdr) {
-
-		case NEXTHDR_HOP:
-			break;
-		case NEXTHDR_ROUTING:
-			found_rhdr = 1;
-			break;
-		case NEXTHDR_DEST:
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
-			if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0)
-				break;
-#endif
-			if (found_rhdr)
-				return offset;
-			break;
-		default :
-			return offset;
-		}
-
-		offset += ipv6_optlen(exthdr);
-		*nexthdr = &exthdr->nexthdr;
-		exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) +
-						 offset);
-	}
-
-	return offset;
-}
-
-void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
-{
-	static atomic_t ipv6_fragmentation_id;
-	int old, new;
-
-	if (rt && !(rt->dst.flags & DST_NOPEER)) {
-		struct inet_peer *peer;
-		struct net *net;
-
-		net = dev_net(rt->dst.dev);
-		peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
-		if (peer) {
-			fhdr->identification = htonl(inet_getid(peer, 0));
-			inet_putpeer(peer);
-			return;
-		}
-	}
-	do {
-		old = atomic_read(&ipv6_fragmentation_id);
-		new = old + 1;
-		if (!new)
-			new = 1;
-	} while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old);
-	fhdr->identification = htonl(new);
-}
-
 int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
 {
 	struct sk_buff *frag;
diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c
new file mode 100644
index 0000000..c2e73e6
--- /dev/null
+++ b/net/ipv6/output_core.c
@@ -0,0 +1,76 @@
+/*
+ * IPv6 library code, needed by static components when full IPv6 support is
+ * not configured or static.  These functions are needed by GSO/GRO implementation.
+ */
+#include <linux/export.h>
+#include <net/ipv6.h>
+#include <net/ip6_fib.h>
+
+void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
+{
+	static atomic_t ipv6_fragmentation_id;
+	int old, new;
+
+#if IS_ENABLED(CONFIG_IPV6)
+	if (rt && !(rt->dst.flags & DST_NOPEER)) {
+		struct inet_peer *peer;
+		struct net *net;
+
+		net = dev_net(rt->dst.dev);
+		peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
+		if (peer) {
+			fhdr->identification = htonl(inet_getid(peer, 0));
+			inet_putpeer(peer);
+			return;
+		}
+	}
+#endif
+	do {
+		old = atomic_read(&ipv6_fragmentation_id);
+		new = old + 1;
+		if (!new)
+			new = 1;
+	} while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old);
+	fhdr->identification = htonl(new);
+}
+EXPORT_SYMBOL(ipv6_select_ident);
+
+int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
+{
+	u16 offset = sizeof(struct ipv6hdr);
+	struct ipv6_opt_hdr *exthdr =
+				(struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1);
+	unsigned int packet_len = skb->tail - skb->network_header;
+	int found_rhdr = 0;
+	*nexthdr = &ipv6_hdr(skb)->nexthdr;
+
+	while (offset + 1 <= packet_len) {
+
+		switch (**nexthdr) {
+
+		case NEXTHDR_HOP:
+			break;
+		case NEXTHDR_ROUTING:
+			found_rhdr = 1;
+			break;
+		case NEXTHDR_DEST:
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
+			if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0)
+				break;
+#endif
+			if (found_rhdr)
+				return offset;
+			break;
+		default :
+			return offset;
+		}
+
+		offset += ipv6_optlen(exthdr);
+		*nexthdr = &exthdr->nexthdr;
+		exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) +
+						 offset);
+	}
+
+	return offset;
+}
+EXPORT_SYMBOL(ip6_find_1stfragopt);
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [RFC PATCH 12/13] ipv4: Pull GSO registration out of inet_init()
  2012-11-14  1:24 [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Vlad Yasevich
                   ` (10 preceding siblings ...)
  2012-11-14  1:24 ` [RFC PATCH 11/13] ipv6: Update ipv6 static library with newly needed functions Vlad Yasevich
@ 2012-11-14  1:24 ` Vlad Yasevich
  2012-11-14  1:24 ` [RFC PATCH 13/13] ipv6: Pull IPv6 GSO registration out of the module Vlad Yasevich
  2012-11-14  2:25 ` [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Eric Dumazet
  13 siblings, 0 replies; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14  1:24 UTC (permalink / raw)
  To: netdev; +Cc: eric.dumazet, davem

Since GSO/GRO support is now separated, make IPv4 GSO a
stand-alone init call and not part of inet_init().

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 net/ipv4/af_inet.c |   34 ++++++++++++++++++++--------------
 1 files changed, 20 insertions(+), 14 deletions(-)

diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 66f63ce..fd34b8c 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1665,11 +1665,6 @@ static int ipv4_proc_init(void);
  *	IP protocol layer initialiser
  */
 
-static struct packet_type ip_packet_type __read_mostly = {
-	.type = cpu_to_be16(ETH_P_IP),
-	.func = ip_rcv,
-};
-
 static struct packet_offload ip_packet_offload __read_mostly = {
 	.type = cpu_to_be16(ETH_P_IP),
 	.gso_send_check = inet_gso_send_check,
@@ -1678,6 +1673,26 @@ static struct packet_offload ip_packet_offload __read_mostly = {
 	.gro_complete = inet_gro_complete,
 };
 
+static void __init ipv4_offload_init(void)
+{
+	/*
+	 * Add offloads
+	 */
+	if (inet_add_offload(&udp_offload, IPPROTO_UDP) < 0)
+		pr_crit("%s: Cannot add UDP protocol offload\n", __func__);
+	if (inet_add_offload(&tcp_offload, IPPROTO_TCP) < 0)
+		pr_crit("%s: Cannot add TCP protocol offlaod\n", __func__);
+
+	dev_add_offload(&ip_packet_offload);
+}
+
+fs_initcall(ipv4_offload_init);
+
+static struct packet_type ip_packet_type __read_mostly = {
+	.type = cpu_to_be16(ETH_P_IP),
+	.func = ip_rcv,
+};
+
 static int __init inet_init(void)
 {
 	struct sk_buff *dummy_skb;
@@ -1720,14 +1735,6 @@ static int __init inet_init(void)
 	tcp_prot.sysctl_mem = init_net.ipv4.sysctl_tcp_mem;
 
 	/*
-	 * Add offloads
-	 */
-	if (inet_add_offload(&udp_offload, IPPROTO_UDP) < 0)
-		pr_crit("%s: Cannot add UDP protocol offload\n", __func__);
-	if (inet_add_offload(&tcp_offload, IPPROTO_TCP) < 0)
-		pr_crit("%s: Cannot add TCP protocol offlaod\n", __func__);
-
-	/*
 	 *	Add all the base protocols.
 	 */
 
@@ -1799,7 +1806,6 @@ static int __init inet_init(void)
 
 	ipfrag_init();
 
-	dev_add_offload(&ip_packet_offload);
 	dev_add_pack(&ip_packet_type);
 
 	rc = 0;
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* [RFC PATCH 13/13] ipv6: Pull IPv6 GSO registration out of the module
  2012-11-14  1:24 [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Vlad Yasevich
                   ` (11 preceding siblings ...)
  2012-11-14  1:24 ` [RFC PATCH 12/13] ipv4: Pull GSO registration out of inet_init() Vlad Yasevich
@ 2012-11-14  1:24 ` Vlad Yasevich
  2012-11-16 22:04   ` Ben Hutchings
  2012-11-14  2:25 ` [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Eric Dumazet
  13 siblings, 1 reply; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14  1:24 UTC (permalink / raw)
  To: netdev; +Cc: eric.dumazet, davem

Sing GSO support is now separate, pull it out of the module
and make it its own init call.
Remove the cleanup functions as they are no longer called.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
---
 include/net/protocol.h     |   11 ++++++-----
 net/ipv6/Makefile          |    6 +++---
 net/ipv6/af_inet6.c        |    3 ---
 net/ipv6/exthdrs.c         |   10 +---------
 net/ipv6/exthdrs_offload.c |    7 -------
 net/ipv6/ip6_offload.c     |   17 ++++++++++++-----
 net/ipv6/ip6_offload.h     |    8 --------
 net/ipv6/tcp_ipv6.c        |   10 +---------
 net/ipv6/tcpv6_offload.c   |    5 -----
 net/ipv6/udp.c             |   10 +---------
 net/ipv6/udp_offload.c     |    5 -----
 11 files changed, 24 insertions(+), 68 deletions(-)

diff --git a/include/net/protocol.h b/include/net/protocol.h
index 7019c16..2c90794 100644
--- a/include/net/protocol.h
+++ b/include/net/protocol.h
@@ -25,6 +25,7 @@
 #define _PROTOCOL_H
 
 #include <linux/in6.h>
+#include <linux/skbuff.h>
 #if IS_ENABLED(CONFIG_IPV6)
 #include <linux/ipv6.h>
 #endif
@@ -59,8 +60,6 @@ struct inet6_protocol {
 
 #define INET6_PROTO_NOPOLICY	0x1
 #define INET6_PROTO_FINAL	0x2
-/* This should be set for any extension header which is compatible with GSO. */
-#define INET6_PROTO_GSO_EXTHDR	0x4
 #endif
 
 struct net_offload {
@@ -72,6 +71,8 @@ struct net_offload {
 	int			(*gro_complete)(struct sk_buff *skb);
 	unsigned int		flags;	/* Flags used by IPv6 for now */
 };
+/* This should be set for any extension header which is compatible with GSO. */
+#define INET6_PROTO_GSO_EXTHDR	0x1
 
 /* This is used to register socket interfaces for IP protocols.  */
 struct inet_protosw {
@@ -93,10 +94,10 @@ struct inet_protosw {
 
 extern const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS];
 extern const struct net_offload __rcu *inet_offloads[MAX_INET_PROTOS];
+extern const struct net_offload __rcu *inet6_offloads[MAX_INET_PROTOS];
 
 #if IS_ENABLED(CONFIG_IPV6)
 extern const struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS];
-extern const struct net_offload __rcu *inet6_offloads[MAX_INET_PROTOS];
 #endif
 
 extern int	inet_add_protocol(const struct net_protocol *prot, unsigned char num);
@@ -109,10 +110,10 @@ extern void	inet_unregister_protosw(struct inet_protosw *p);
 #if IS_ENABLED(CONFIG_IPV6)
 extern int	inet6_add_protocol(const struct inet6_protocol *prot, unsigned char num);
 extern int	inet6_del_protocol(const struct inet6_protocol *prot, unsigned char num);
-extern int	inet6_add_offload(const struct net_offload *prot, unsigned char num);
-extern int	inet6_del_offload(const struct net_offload *prot, unsigned char num);
 extern int	inet6_register_protosw(struct inet_protosw *p);
 extern void	inet6_unregister_protosw(struct inet_protosw *p);
 #endif
+extern int	inet6_add_offload(const struct net_offload *prot, unsigned char num);
+extern int	inet6_del_offload(const struct net_offload *prot, unsigned char num);
 
 #endif	/* _PROTOCOL_H */
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index cdca302..04a475d 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_IPV6) += ipv6.o
 ipv6-objs :=	af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
 		addrlabel.o \
 		route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \
-		raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
+		raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
 		exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o
 
 ipv6-offload :=	ip6_offload.o tcpv6_offload.o udp_offload.o exthdrs_offload.o
@@ -23,7 +23,6 @@ ipv6-$(CONFIG_PROC_FS) += proc.o
 ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o
 
 ipv6-objs += $(ipv6-y)
-ipv6-objs += $(ipv6-offload)
 
 obj-$(CONFIG_INET6_AH) += ah6.o
 obj-$(CONFIG_INET6_ESP) += esp6.o
@@ -41,6 +40,7 @@ obj-$(CONFIG_IPV6_SIT) += sit.o
 obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o
 obj-$(CONFIG_IPV6_GRE) += ip6_gre.o
 
-obj-y += addrconf_core.o exthdrs_core.o output_core.o
+obj-y += addrconf_core.o exthdrs_core.o output_core.o protocol.o
+obj-y += $(ipv6-offload)
 
 obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index c84d5ba..7bafc51 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -62,7 +62,6 @@
 
 #include <asm/uaccess.h>
 #include <linux/mroute6.h>
-#include "ip6_offload.h"
 
 MODULE_AUTHOR("Cast of dozens");
 MODULE_DESCRIPTION("IPv6 protocol stack for Linux");
@@ -707,14 +706,12 @@ static struct packet_type ipv6_packet_type __read_mostly = {
 
 static int __init ipv6_packet_init(void)
 {
-	ipv6_offload_init();
 	dev_add_pack(&ipv6_packet_type);
 	return 0;
 }
 
 static void ipv6_packet_cleanup(void)
 {
-	ipv6_offload_cleanup();
 	dev_remove_pack(&ipv6_packet_type);
 }
 
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index dc0faab..53f14df 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -48,7 +48,6 @@
 #endif
 
 #include <asm/uaccess.h>
-#include "ip6_offload.h"
 
 int ipv6_find_tlv(struct sk_buff *skb, int offset, int type)
 {
@@ -546,13 +545,9 @@ int __init ipv6_exthdrs_init(void)
 {
 	int ret;
 
-	ret = ipv6_exthdrs_offload_init();
-	if (ret)
-		goto out;
-
 	ret = inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING);
 	if (ret)
-		goto out_offload;
+		goto out;
 
 	ret = inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
 	if (ret)
@@ -568,14 +563,11 @@ out_destopt:
 	inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
 out_rthdr:
 	inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
-out_offload:
-	ipv6_exthdrs_offload_exit();
 	goto out;
 };
 
 void ipv6_exthdrs_exit(void)
 {
-	ipv6_exthdrs_offload_exit();
 	inet6_del_protocol(&nodata_protocol, IPPROTO_NONE);
 	inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
 	inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
diff --git a/net/ipv6/exthdrs_offload.c b/net/ipv6/exthdrs_offload.c
index 913b9fc..cf77f3a 100644
--- a/net/ipv6/exthdrs_offload.c
+++ b/net/ipv6/exthdrs_offload.c
@@ -39,10 +39,3 @@ out_rt:
 	inet_del_offload(&rthdr_offload, IPPROTO_ROUTING);
 	goto out;
 }
-
-void ipv6_exthdrs_offload_exit(void)
-{
-	inet_del_offload(&rthdr_offload, IPPROTO_ROUTING);
-	inet_del_offload(&rthdr_offload, IPPROTO_DSTOPTS);
-}
-
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index 01cf983..63d79d9 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -12,6 +12,7 @@
 #include <linux/socket.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
+#include <linux/printk.h>
 
 #include <net/protocol.h>
 #include <net/ipv6.h>
@@ -262,12 +263,18 @@ static struct packet_offload ipv6_packet_offload __read_mostly = {
 	.gro_complete = ipv6_gro_complete,
 };
 
-void __init ipv6_offload_init(void)
+static int __init ipv6_offload_init(void)
 {
+
+	if (tcpv6_offload_init() < 0)
+		pr_crit("%s: Cannot add TCP protocol offload\n", __func__);
+	if (udp_offload_init() < 0)
+		pr_crit("%s: Cannot add UDP protocol offload\n", __func__);
+	if (ipv6_exthdrs_offload_init() < 0)
+		pr_crit("%s: Cannot add EXTHDRS protocol offload\n", __func__);
+
 	dev_add_offload(&ipv6_packet_offload);
+	return 0;
 }
 
-void ipv6_offload_cleanup(void)
-{
-	dev_remove_offload(&ipv6_packet_offload);
-}
+fs_initcall(ipv6_offload_init);
diff --git a/net/ipv6/ip6_offload.h b/net/ipv6/ip6_offload.h
index 4e88ddb..2e155c6 100644
--- a/net/ipv6/ip6_offload.h
+++ b/net/ipv6/ip6_offload.h
@@ -12,15 +12,7 @@
 #define __ip6_offload_h
 
 int ipv6_exthdrs_offload_init(void);
-void ipv6_exthdrs_offload_exit(void);
-
 int udp_offload_init(void);
-void udp_offload_cleanup(void);
-
 int tcpv6_offload_init(void);
-void tcpv6_offload_cleanup(void);
-
-extern void ipv6_offload_init(void);
-extern void ipv6_offload_cleanup(void);
 
 #endif
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 58fabc5..c5d2d61 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -71,7 +71,6 @@
 
 #include <linux/crypto.h>
 #include <linux/scatterlist.h>
-#include "ip6_offload.h"
 
 static void	tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb);
 static void	tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
@@ -2004,13 +2003,9 @@ int __init tcpv6_init(void)
 {
 	int ret;
 
-	ret = tcpv6_offload_init();
-	if (ret)
-		goto out;
-
 	ret = inet6_add_protocol(&tcpv6_protocol, IPPROTO_TCP);
 	if (ret)
-		goto out_offload;
+		goto out;
 
 	/* register inet6 protocol */
 	ret = inet6_register_protosw(&tcpv6_protosw);
@@ -2027,8 +2022,6 @@ out_tcpv6_protosw:
 	inet6_unregister_protosw(&tcpv6_protosw);
 out_tcpv6_protocol:
 	inet6_del_protocol(&tcpv6_protocol, IPPROTO_TCP);
-out_offload:
-	tcpv6_offload_cleanup();
 	goto out;
 }
 
@@ -2037,5 +2030,4 @@ void tcpv6_exit(void)
 	unregister_pernet_subsys(&tcpv6_net_ops);
 	inet6_unregister_protosw(&tcpv6_protosw);
 	inet6_del_protocol(&tcpv6_protocol, IPPROTO_TCP);
-	tcpv6_offload_cleanup();
 }
diff --git a/net/ipv6/tcpv6_offload.c b/net/ipv6/tcpv6_offload.c
index edeafed..3a27fe6 100644
--- a/net/ipv6/tcpv6_offload.c
+++ b/net/ipv6/tcpv6_offload.c
@@ -91,8 +91,3 @@ int __init tcpv6_offload_init(void)
 {
 	return inet6_add_offload(&tcpv6_offload, IPPROTO_TCP);
 }
-
-void tcpv6_offload_cleanup(void)
-{
-	inet6_del_offload(&tcpv6_offload, IPPROTO_TCP);
-}
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 013fef7..dfaa29b 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -50,7 +50,6 @@
 #include <linux/seq_file.h>
 #include <trace/events/skb.h>
 #include "udp_impl.h"
-#include "ip6_offload.h"
 
 int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
 {
@@ -1472,13 +1471,9 @@ int __init udpv6_init(void)
 {
 	int ret;
 
-	ret = udp_offload_init();
-	if (ret)
-		goto out;
-
 	ret = inet6_add_protocol(&udpv6_protocol, IPPROTO_UDP);
 	if (ret)
-		goto out_offload;
+		goto out;
 
 	ret = inet6_register_protosw(&udpv6_protosw);
 	if (ret)
@@ -1488,8 +1483,6 @@ out:
 
 out_udpv6_protocol:
 	inet6_del_protocol(&udpv6_protocol, IPPROTO_UDP);
-out_offload:
-	udp_offload_cleanup();
 	goto out;
 }
 
@@ -1497,5 +1490,4 @@ void udpv6_exit(void)
 {
 	inet6_unregister_protosw(&udpv6_protosw);
 	inet6_del_protocol(&udpv6_protocol, IPPROTO_UDP);
-	udp_offload_cleanup();
 }
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index f964d2b..979e4ab 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -115,8 +115,3 @@ int __init udp_offload_init(void)
 {
 	return inet6_add_offload(&udpv6_offload, IPPROTO_UDP);
 }
-
-void udp_offload_cleanup(void)
-{
-	inet6_del_offload(&udpv6_offload, IPPROTO_UDP);
-}
-- 
1.7.7.6

^ permalink raw reply related	[flat|nested] 23+ messages in thread

* Re: [RFC PATCH 01/13] net:  Add generic packet offload infrastructure.
  2012-11-14  1:24 ` [RFC PATCH 01/13] net: Add generic packet offload infrastructure Vlad Yasevich
@ 2012-11-14  2:24   ` Eric Dumazet
  2012-11-14 13:03     ` Vlad Yasevich
  0 siblings, 1 reply; 23+ messages in thread
From: Eric Dumazet @ 2012-11-14  2:24 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: netdev, davem

On Tue, 2012-11-13 at 20:24 -0500, Vlad Yasevich wrote:
> Create a new data structure to contain the GRO/GSO callbacks and add
> a new registration mechanism.
> 
> Singed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> ---
>  include/linux/netdevice.h |   15 ++++++++
>  net/core/dev.c            |   80 +++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 95 insertions(+), 0 deletions(-)
> 
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index f8eda02..d15af51 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -1511,6 +1511,18 @@ struct packet_type {
>  	struct list_head	list;
>  };
>  
> +struct packet_offload {
> +	__be16			type;	/* This is really htons(ether_type). */
> +	struct net_device	*dev;	/* NULL is wildcarded here	     */

Shouldnt this dev pointer be removed at some point in the patch serie ?

> +	struct sk_buff		*(*gso_segment)(struct sk_buff *skb,
> +						netdev_features_t features);
> +	int			(*gso_send_check)(struct sk_buff *skb);
> +	struct sk_buff		**(*gro_receive)(struct sk_buff **head,
> +					       struct sk_buff *skb);
> +	int			(*gro_complete)(struct sk_buff *skb);
> +	struct list_head	list;
> +};
> +
>  #include <linux/notifier.h>
>  

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel
  2012-11-14  1:24 [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Vlad Yasevich
                   ` (12 preceding siblings ...)
  2012-11-14  1:24 ` [RFC PATCH 13/13] ipv6: Pull IPv6 GSO registration out of the module Vlad Yasevich
@ 2012-11-14  2:25 ` Eric Dumazet
  2012-11-14 13:10   ` Vlad Yasevich
  13 siblings, 1 reply; 23+ messages in thread
From: Eric Dumazet @ 2012-11-14  2:25 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: netdev, davem

On Tue, 2012-11-13 at 20:24 -0500, Vlad Yasevich wrote:
> This patch series is a revision suggested by Eric to solve the problem where
> a host without IPv6 support drops GSO frames from the guest.
> 
> The problem is that GSO/GRO support is per protocol, and when said protocol
> is not loaded or is disabled, packets attempting to go through GSO/GRO code paths
> are dropped.  This causes retransmissions and a two orders of magnitude drop in
> performance.
> 
> Prior attempt to solve the problem simply enabled enough GSO/GRO functionality
> for IPv6 protocol when IPv6 was diabled.  This did not solve the problem when
> the protocol was not build in or was blacklisted.
> To solve the problem, it was suggested that we separate GSO/GRO callback
> registration from packet processing registrations.  That way
> GSO/GRO callbacks can be built into the kernel and always be there.
> This patch series attempts to do just that.
> * Patches 1 and 2 split the GSO/GRO handlers from packet_type and convert
>   to the new structure.
> * Patches 3, 4 and 5 do the same thing for net_protocol structure.
> * The rest of the patches try to incrementally move the IPv6 GSO/GRO
>   code out of the module and into the static kernel build.  Some IPv6
>   helper functions also had to move as well.
> 
> I am currently testing the patches, but if folks could look this over
> and send me any comments, I'd appreciate it.

Seems very nice !

I am just wondering if GSO/GRO is fully enabled at every step ?

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC PATCH 03/13] net: Add net protocol offload registration infrustructure
  2012-11-14  1:24 ` [RFC PATCH 03/13] net: Add net protocol offload registration infrustructure Vlad Yasevich
@ 2012-11-14  8:22   ` Nicolas Dichtel
  2012-11-14 13:08     ` Vlad Yasevich
  2012-11-14 23:14   ` Francois Romieu
  1 sibling, 1 reply; 23+ messages in thread
From: Nicolas Dichtel @ 2012-11-14  8:22 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: netdev, eric.dumazet, davem

Le 14/11/2012 02:24, Vlad Yasevich a écrit :
> Create a new data structure for IPv4 protocols that holds GRO/GSO
> callbacks and a new array to track the protocols that register GRO/GSO.
>
> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
> ---
>   include/net/protocol.h |   12 ++++++++++++
>   net/ipv4/af_inet.c     |   12 ++++++++++++
>   net/ipv4/protocol.c    |   22 +++++++++++++++++++++-
>   3 files changed, 45 insertions(+), 1 deletions(-)
>
> diff --git a/include/net/protocol.h b/include/net/protocol.h
> index 929528c..d8ecb17 100644
> --- a/include/net/protocol.h
> +++ b/include/net/protocol.h
> @@ -77,6 +77,15 @@ struct inet6_protocol {
>   #define INET6_PROTO_GSO_EXTHDR	0x4
>   #endif
>
> +struct net_offload {
> +	int			(*gso_send_check)(struct sk_buff *skb);
> +	struct sk_buff	       *(*gso_segment)(struct sk_buff *skb,
> +					       netdev_features_t features);
> +	struct sk_buff	      **(*gro_receive)(struct sk_buff **head,
> +					       struct sk_buff *skb);
> +	int			(*gro_complete)(struct sk_buff *skb);
> +};
> +
>   /* This is used to register socket interfaces for IP protocols.  */
>   struct inet_protosw {
>   	struct list_head list;
> @@ -96,6 +105,7 @@ struct inet_protosw {
>   #define INET_PROTOSW_ICSK      0x04  /* Is this an inet_connection_sock? */
>
>   extern const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS];
> +extern const struct net_offload __rcu *inet_offloads[MAX_INET_PROTOS];
>
>   #if IS_ENABLED(CONFIG_IPV6)
>   extern const struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS];
> @@ -103,6 +113,8 @@ extern const struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS];
>
>   extern int	inet_add_protocol(const struct net_protocol *prot, unsigned char num);
>   extern int	inet_del_protocol(const struct net_protocol *prot, unsigned char num);
> +extern int	inet_add_offload(const struct net_offload *prot, unsigned char num);
> +extern int	inet_del_offload(const struct net_offload *prot, unsigned char num);
>   extern void	inet_register_protosw(struct inet_protosw *p);
>   extern void	inet_unregister_protosw(struct inet_protosw *p);
>
> diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
> index 4c99c5f..3918d86 100644
> --- a/net/ipv4/af_inet.c
> +++ b/net/ipv4/af_inet.c
> @@ -1566,6 +1566,13 @@ static const struct net_protocol tcp_protocol = {
>   	.netns_ok	=	1,
>   };
>
> +static const struct net_offload tcp_offload = {
> +	.gso_send_check	=	tcp_v4_gso_send_check,
> +	.gso_segment	=	tcp_tso_segment,
> +	.gro_receive	=	tcp4_gro_receive,
> +	.gro_complete	=	tcp4_gro_complete,
> +};
> +
>   static const struct net_protocol udp_protocol = {
>   	.handler =	udp_rcv,
>   	.err_handler =	udp_err,
> @@ -1575,6 +1582,11 @@ static const struct net_protocol udp_protocol = {
>   	.netns_ok =	1,
>   };
>
> +static const struct net_offload udp_offload = {
> +	.gso_send_check = udp4_ufo_send_check,
> +	.gso_segment = udp4_ufo_fragment,
> +};
> +
>   static const struct net_protocol icmp_protocol = {
>   	.handler =	icmp_rcv,
>   	.err_handler =	ping_err,
> diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c
> index 8918eff..1278db8 100644
> --- a/net/ipv4/protocol.c
> +++ b/net/ipv4/protocol.c
> @@ -29,6 +29,7 @@
>   #include <net/protocol.h>
>
>   const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly;
> +const struct net_offload __rcu *inet_offloads[MAX_INET_PROTOS] __read_mostly;
>
>   /*
>    *	Add a protocol handler to the hash tables
> @@ -41,6 +42,13 @@ int inet_add_protocol(const struct net_protocol *prot, unsigned char protocol)
>   }
>   EXPORT_SYMBOL(inet_add_protocol);
>
> +int inet_add_offload(const struct net_offload *prot, unsigned char protocol)
> +{
> +	return !cmpxchg((const struct net_offload **)&inet_offloads[protocol],
> +			NULL, prot) ? 0 : -1;
> +}
> +EXPORT_SYMBOL(inet_add_offload);
> +
>   /*
>    *	Remove a protocol from the hash tables.
>    */
> @@ -56,4 +64,16 @@ int inet_del_protocol(const struct net_protocol *prot, unsigned char protocol)
>
>   	return ret;
>   }
> -EXPORT_SYMBOL(inet_del_protocol);
This line should probably not be removed ;-)

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC PATCH 01/13] net:  Add generic packet offload infrastructure.
  2012-11-14  2:24   ` Eric Dumazet
@ 2012-11-14 13:03     ` Vlad Yasevich
  0 siblings, 0 replies; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14 13:03 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: netdev, davem

On 11/13/2012 09:24 PM, Eric Dumazet wrote:
> On Tue, 2012-11-13 at 20:24 -0500, Vlad Yasevich wrote:
>> Create a new data structure to contain the GRO/GSO callbacks and add
>> a new registration mechanism.
>>
>> Singed-off-by: Vlad Yasevich <vyasevic@redhat.com>
>> ---
>>   include/linux/netdevice.h |   15 ++++++++
>>   net/core/dev.c            |   80 +++++++++++++++++++++++++++++++++++++++++++++
>>   2 files changed, 95 insertions(+), 0 deletions(-)
>>
>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>> index f8eda02..d15af51 100644
>> --- a/include/linux/netdevice.h
>> +++ b/include/linux/netdevice.h
>> @@ -1511,6 +1511,18 @@ struct packet_type {
>>   	struct list_head	list;
>>   };
>>
>> +struct packet_offload {
>> +	__be16			type;	/* This is really htons(ether_type). */
>> +	struct net_device	*dev;	/* NULL is wildcarded here	     */
>
> Shouldnt this dev pointer be removed at some point in the patch serie ?

yes.  i was thinking about this as well.  I actually shouldn't have been 
carried into this struct to begin with since its not really being used 
by the offload calls.

-vlad

>
>> +	struct sk_buff		*(*gso_segment)(struct sk_buff *skb,
>> +						netdev_features_t features);
>> +	int			(*gso_send_check)(struct sk_buff *skb);
>> +	struct sk_buff		**(*gro_receive)(struct sk_buff **head,
>> +					       struct sk_buff *skb);
>> +	int			(*gro_complete)(struct sk_buff *skb);
>> +	struct list_head	list;
>> +};
>> +
>>   #include <linux/notifier.h>
>>
>

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC PATCH 03/13] net: Add net protocol offload registration infrustructure
  2012-11-14  8:22   ` Nicolas Dichtel
@ 2012-11-14 13:08     ` Vlad Yasevich
  0 siblings, 0 replies; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14 13:08 UTC (permalink / raw)
  To: nicolas.dichtel; +Cc: netdev, eric.dumazet, davem

On 11/14/2012 03:22 AM, Nicolas Dichtel wrote:
> Le 14/11/2012 02:24, Vlad Yasevich a écrit :
>> diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c
>> index 8918eff..1278db8 100644
>> --- a/net/ipv4/protocol.c
>> +++ b/net/ipv4/protocol.c
>> @@ -29,6 +29,7 @@
>>   #include <net/protocol.h>
>>
>>   const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS]
>> __read_mostly;
>> +const struct net_offload __rcu *inet_offloads[MAX_INET_PROTOS]
>> __read_mostly;
>>
>>   /*
>>    *    Add a protocol handler to the hash tables
>> @@ -41,6 +42,13 @@ int inet_add_protocol(const struct net_protocol
>> *prot, unsigned char protocol)
>>   }
>>   EXPORT_SYMBOL(inet_add_protocol);
>>
>> +int inet_add_offload(const struct net_offload *prot, unsigned char
>> protocol)
>> +{
>> +    return !cmpxchg((const struct net_offload
>> **)&inet_offloads[protocol],
>> +            NULL, prot) ? 0 : -1;
>> +}
>> +EXPORT_SYMBOL(inet_add_offload);
>> +
>>   /*
>>    *    Remove a protocol from the hash tables.
>>    */
>> @@ -56,4 +64,16 @@ int inet_del_protocol(const struct net_protocol
>> *prot, unsigned char protocol)
>>
>>       return ret;
>>   }
>> -EXPORT_SYMBOL(inet_del_protocol);
> This line should probably not be removed ;-)

Yep,  good catch... thanks...

-vald

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel
  2012-11-14  2:25 ` [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Eric Dumazet
@ 2012-11-14 13:10   ` Vlad Yasevich
  0 siblings, 0 replies; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-14 13:10 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: netdev, davem

On 11/13/2012 09:25 PM, Eric Dumazet wrote:
> On Tue, 2012-11-13 at 20:24 -0500, Vlad Yasevich wrote:
>> This patch series is a revision suggested by Eric to solve the problem where
>> a host without IPv6 support drops GSO frames from the guest.
>>
>> The problem is that GSO/GRO support is per protocol, and when said protocol
>> is not loaded or is disabled, packets attempting to go through GSO/GRO code paths
>> are dropped.  This causes retransmissions and a two orders of magnitude drop in
>> performance.
>>
>> Prior attempt to solve the problem simply enabled enough GSO/GRO functionality
>> for IPv6 protocol when IPv6 was diabled.  This did not solve the problem when
>> the protocol was not build in or was blacklisted.
>> To solve the problem, it was suggested that we separate GSO/GRO callback
>> registration from packet processing registrations.  That way
>> GSO/GRO callbacks can be built into the kernel and always be there.
>> This patch series attempts to do just that.
>> * Patches 1 and 2 split the GSO/GRO handlers from packet_type and convert
>>    to the new structure.
>> * Patches 3, 4 and 5 do the same thing for net_protocol structure.
>> * The rest of the patches try to incrementally move the IPv6 GSO/GRO
>>    code out of the module and into the static kernel build.  Some IPv6
>>    helper functions also had to move as well.
>>
>> I am currently testing the patches, but if folks could look this over
>> and send me any comments, I'd appreciate it.
>
> Seems very nice !
>
> I am just wondering if GSO/GRO is fully enabled at every step ?
>

I think so.  I ran basic tests along most of the steps and it seemed to 
be enabled.  That's why this is a 13 patch series :)  Tried to do it 
incrementally without impacting functionality.

-vlad

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC PATCH 03/13] net: Add net protocol offload registration infrustructure
  2012-11-14  1:24 ` [RFC PATCH 03/13] net: Add net protocol offload registration infrustructure Vlad Yasevich
  2012-11-14  8:22   ` Nicolas Dichtel
@ 2012-11-14 23:14   ` Francois Romieu
  2012-11-15  2:16     ` Vlad Yasevich
  1 sibling, 1 reply; 23+ messages in thread
From: Francois Romieu @ 2012-11-14 23:14 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: netdev, eric.dumazet, davem

Vlad Yasevich <vyasevic@redhat.com> :
[...]
> diff --git a/include/net/protocol.h b/include/net/protocol.h
> index 929528c..d8ecb17 100644
> --- a/include/net/protocol.h
> +++ b/include/net/protocol.h
> @@ -77,6 +77,15 @@ struct inet6_protocol {
>  #define INET6_PROTO_GSO_EXTHDR	0x4
>  #endif
>  
> +struct net_offload {
> +	int			(*gso_send_check)(struct sk_buff *skb);
> +	struct sk_buff	       *(*gso_segment)(struct sk_buff *skb,
> +					       netdev_features_t features);
> +	struct sk_buff	      **(*gro_receive)(struct sk_buff **head,
> +					       struct sk_buff *skb);
> +	int			(*gro_complete)(struct sk_buff *skb);
> +};

Would it be worth adding a #14 where packet_offload and net_offload
share a common offload struct instead of duplicating (currently) identical
members ?

-- 
Ueimor

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC PATCH 03/13] net: Add net protocol offload registration infrustructure
  2012-11-14 23:14   ` Francois Romieu
@ 2012-11-15  2:16     ` Vlad Yasevich
  0 siblings, 0 replies; 23+ messages in thread
From: Vlad Yasevich @ 2012-11-15  2:16 UTC (permalink / raw)
  To: Francois Romieu; +Cc: netdev, eric.dumazet, davem

On 11/14/2012 06:14 PM, Francois Romieu wrote:
> Vlad Yasevich <vyasevic@redhat.com> :
> [...]
>> diff --git a/include/net/protocol.h b/include/net/protocol.h
>> index 929528c..d8ecb17 100644
>> --- a/include/net/protocol.h
>> +++ b/include/net/protocol.h
>> @@ -77,6 +77,15 @@ struct inet6_protocol {
>>   #define INET6_PROTO_GSO_EXTHDR	0x4
>>   #endif
>>
>> +struct net_offload {
>> +	int			(*gso_send_check)(struct sk_buff *skb);
>> +	struct sk_buff	       *(*gso_segment)(struct sk_buff *skb,
>> +					       netdev_features_t features);
>> +	struct sk_buff	      **(*gro_receive)(struct sk_buff **head,
>> +					       struct sk_buff *skb);
>> +	int			(*gro_complete)(struct sk_buff *skb);
>> +};
>
> Would it be worth adding a #14 where packet_offload and net_offload
> share a common offload struct instead of duplicating (currently) identical
> members ?
>

I'll look...  There are some very minor diffs between the structs, so 
one could at least be based on another.

What bugs me more actually is the duplication between IPv4 and IPv6 
lists.  One idea I had before was to have packet_offload be a container
for the array of net_offloads.  We would then have a single set of 
functions to register tcp/udp callbacks for both ipv4 and ipv6.  I might
resurrect that patch and send it for comments as well.

-vlad

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [RFC PATCH 13/13] ipv6: Pull IPv6 GSO registration out of the module
  2012-11-14  1:24 ` [RFC PATCH 13/13] ipv6: Pull IPv6 GSO registration out of the module Vlad Yasevich
@ 2012-11-16 22:04   ` Ben Hutchings
  0 siblings, 0 replies; 23+ messages in thread
From: Ben Hutchings @ 2012-11-16 22:04 UTC (permalink / raw)
  To: Vlad Yasevich; +Cc: netdev, eric.dumazet, davem

On Tue, 2012-11-13 at 20:24 -0500, Vlad Yasevich wrote:
> Sing GSO support is now separate, pull it out of the module

Sing?  Can we dance, too?

> and make it its own init call.
> Remove the cleanup functions as they are no longer called.
[...]
> --- a/include/net/protocol.h
> +++ b/include/net/protocol.h
> @@ -25,6 +25,7 @@
>  #define _PROTOCOL_H
>  
>  #include <linux/in6.h>
> +#include <linux/skbuff.h>
>  #if IS_ENABLED(CONFIG_IPV6)
>  #include <linux/ipv6.h>
>  #endif
> @@ -59,8 +60,6 @@ struct inet6_protocol {
>  
>  #define INET6_PROTO_NOPOLICY	0x1
>  #define INET6_PROTO_FINAL	0x2
> -/* This should be set for any extension header which is compatible with GSO. */
> -#define INET6_PROTO_GSO_EXTHDR	0x4
>  #endif
>  
>  struct net_offload {
> @@ -72,6 +71,8 @@ struct net_offload {
>  	int			(*gro_complete)(struct sk_buff *skb);
>  	unsigned int		flags;	/* Flags used by IPv6 for now */
>  };
> +/* This should be set for any extension header which is compatible with GSO. */
> +#define INET6_PROTO_GSO_EXTHDR	0x1
[...]

Should be renamed to INET6_OFFLOAD_GSO_EXTHDR, matching the new
structure name.

Ben.

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2012-11-16 22:04 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-11-14  1:24 [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Vlad Yasevich
2012-11-14  1:24 ` [RFC PATCH 01/13] net: Add generic packet offload infrastructure Vlad Yasevich
2012-11-14  2:24   ` Eric Dumazet
2012-11-14 13:03     ` Vlad Yasevich
2012-11-14  1:24 ` [RFC PATCH 02/13] core: Switch to using the new packet offload infrustructure Vlad Yasevich
2012-11-14  1:24 ` [RFC PATCH 03/13] net: Add net protocol offload registration infrustructure Vlad Yasevich
2012-11-14  8:22   ` Nicolas Dichtel
2012-11-14 13:08     ` Vlad Yasevich
2012-11-14 23:14   ` Francois Romieu
2012-11-15  2:16     ` Vlad Yasevich
2012-11-14  1:24 ` [RFC PATCH 04/13] ipv6: Add new offload registration infrastructure Vlad Yasevich
2012-11-14  1:24 ` [RFC PATCH 05/13] ipv4: Switch to using the new offload infrastructure Vlad Yasevich
2012-11-14  1:24 ` [RFC PATCH 06/13] ipv6: Switch to using " Vlad Yasevich
2012-11-14  1:24 ` [RFC PATCH 07/13] ipv6: Separate ipv6 offload support Vlad Yasevich
2012-11-14  1:24 ` [RFC PATCH 08/13] ipv6: Separate tcp offload functionality Vlad Yasevich
2012-11-14  1:24 ` [RFC PATCH 09/13] ipv6: Separate out UDP " Vlad Yasevich
2012-11-14  1:24 ` [RFC PATCH 10/13] ipv6: Move exthdr offload support into separate file Vlad Yasevich
2012-11-14  1:24 ` [RFC PATCH 11/13] ipv6: Update ipv6 static library with newly needed functions Vlad Yasevich
2012-11-14  1:24 ` [RFC PATCH 12/13] ipv4: Pull GSO registration out of inet_init() Vlad Yasevich
2012-11-14  1:24 ` [RFC PATCH 13/13] ipv6: Pull IPv6 GSO registration out of the module Vlad Yasevich
2012-11-16 22:04   ` Ben Hutchings
2012-11-14  2:25 ` [RFC PATCH 00/13] Always build GSO/GRO functionality into the kernel Eric Dumazet
2012-11-14 13:10   ` Vlad Yasevich

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.