From: Willem de Bruijn <will...@google.com>

Extension headers in ipv6 are pulled without calling a callback
function. An inet6_offload signals this feature with flag
INET6_PROTO_GSO_EXTHDR.

Add net_has_flag helper to hide implementation details and in
prepartion for configurable gro.

Convert NEXTHDR_HOP from a special case branch to a standard
extension header offload.

Signed-off-by: Willem de Bruijn <will...@google.com>
---
 include/linux/netdevice.h  |  9 +++++++++
 net/ipv6/exthdrs_offload.c | 17 ++++++++++++++---
 net/ipv6/ip6_offload.c     | 36 +++++++++++++-----------------------
 3 files changed, 36 insertions(+), 26 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 0be594f8d1ce..1c97a048506f 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3567,6 +3567,15 @@ static inline u8 net_offload_from_type(u16 type)
        return type & 0xFF;
 }
 
+static inline bool net_offload_has_flag(const struct net_offload __rcu **offs,
+                                       u16 type, u16 flag)
+{
+       const struct net_offload *off;
+
+       off = offs ? rcu_dereference(offs[net_offload_from_type(type)]) : NULL;
+       return off && off->flags & flag;
+}
+
 static inline const struct net_offload *
 net_gro_receive(const struct net_offload __rcu **offs, u16 type)
 {
diff --git a/net/ipv6/exthdrs_offload.c b/net/ipv6/exthdrs_offload.c
index f5e2ba1c18bf..2230331c6012 100644
--- a/net/ipv6/exthdrs_offload.c
+++ b/net/ipv6/exthdrs_offload.c
@@ -12,11 +12,15 @@
 #include <net/protocol.h>
 #include "ip6_offload.h"
 
-static const struct net_offload rthdr_offload = {
+static struct net_offload hophdr_offload = {
        .flags          =       INET6_PROTO_GSO_EXTHDR,
 };
 
-static const struct net_offload dstopt_offload = {
+static struct net_offload rthdr_offload = {
+       .flags          =       INET6_PROTO_GSO_EXTHDR,
+};
+
+static struct net_offload dstopt_offload = {
        .flags          =       INET6_PROTO_GSO_EXTHDR,
 };
 
@@ -24,10 +28,14 @@ int __init ipv6_exthdrs_offload_init(void)
 {
        int ret;
 
-       ret = inet6_add_offload(&rthdr_offload, IPPROTO_ROUTING);
+       ret = inet6_add_offload(&hophdr_offload, IPPROTO_HOPOPTS);
        if (ret)
                goto out;
 
+       ret = inet6_add_offload(&rthdr_offload, IPPROTO_ROUTING);
+       if (ret)
+               goto out_hop;
+
        ret = inet6_add_offload(&dstopt_offload, IPPROTO_DSTOPTS);
        if (ret)
                goto out_rt;
@@ -37,5 +45,8 @@ int __init ipv6_exthdrs_offload_init(void)
 
 out_rt:
        inet6_del_offload(&rthdr_offload, IPPROTO_ROUTING);
+
+out_hop:
+       inet6_del_offload(&rthdr_offload, IPPROTO_HOPOPTS);
        goto out;
 }
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index 9d301bef0e23..4854509a2c5d 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -22,21 +22,13 @@
 
 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 (!net_offload_has_flag(inet6_offloads, proto,
+                                         INET6_PROTO_GSO_EXTHDR))
+                       break;
 
                if (unlikely(!pskb_may_pull(skb, 8)))
                        break;
@@ -141,26 +133,24 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff 
*skb,
 /* Return the total length of all the extension hdrs, following the same
  * logic in ipv6_gso_pull_exthdrs() when parsing ext-hdrs.
  */
-static int ipv6_exthdrs_len(struct ipv6hdr *iph,
-                           const struct net_offload **opps)
+static int ipv6_exthdrs_len(struct ipv6hdr *iph, u8 *pproto)
 {
        struct ipv6_opt_hdr *opth = (void *)iph;
        int len = 0, proto, optlen = sizeof(*iph);
 
        proto = iph->nexthdr;
        for (;;) {
-               if (proto != NEXTHDR_HOP) {
-                       *opps = rcu_dereference(inet6_offloads[proto]);
-                       if (unlikely(!(*opps)))
-                               break;
-                       if (!((*opps)->flags & INET6_PROTO_GSO_EXTHDR))
-                               break;
-               }
+               if (!net_offload_has_flag(inet6_offloads, proto,
+                                         INET6_PROTO_GSO_EXTHDR))
+                       break;
+
                opth = (void *)opth + optlen;
                optlen = ipv6_optlen(opth);
                len += optlen;
                proto = opth->nexthdr;
        }
+
+       *pproto = proto;
        return len;
 }
 
@@ -296,8 +286,8 @@ static struct sk_buff *ip4ip6_gro_receive(struct list_head 
*head,
 
 static int ipv6_gro_complete(struct sk_buff *skb, int nhoff)
 {
-       const struct net_offload *ops;
        struct ipv6hdr *iph = (struct ipv6hdr *)(skb->data + nhoff);
+       u8 proto;
 
        if (skb->encapsulation) {
                skb_set_inner_protocol(skb, cpu_to_be16(ETH_P_IPV6));
@@ -306,8 +296,8 @@ static int ipv6_gro_complete(struct sk_buff *skb, int nhoff)
 
        iph->payload_len = htons(skb->len - nhoff - sizeof(*iph));
 
-       nhoff += sizeof(*iph) + ipv6_exthdrs_len(iph, &ops);
-       return net_gro_complete(inet6_offloads, ops->type, skb, nhoff);
+       nhoff += sizeof(*iph) + ipv6_exthdrs_len(iph, &proto);
+       return net_gro_complete(inet6_offloads, proto, skb, nhoff);
 }
 
 static int sit_gro_complete(struct sk_buff *skb, int nhoff)
-- 
2.19.0.397.gdd90340f6a-goog

Reply via email to