]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - net/ipv4/udp_offload.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[karo-tx-linux.git] / net / ipv4 / udp_offload.c
index e330c0e56b118f47e125c1459ffb98e9c8998d77..6b7459c92bb2a89c9372b9dafa4621031b624123 100644 (file)
 #include <net/udp.h>
 #include <net/protocol.h>
 
-static DEFINE_SPINLOCK(udp_offload_lock);
-static struct udp_offload_priv __rcu *udp_offload_base __read_mostly;
-
-#define udp_deref_protected(X) rcu_dereference_protected(X, lockdep_is_held(&udp_offload_lock))
-
-struct udp_offload_priv {
-       struct udp_offload      *offload;
-       possible_net_t  net;
-       struct rcu_head         rcu;
-       struct udp_offload_priv __rcu *next;
-};
-
 static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
        netdev_features_t features,
        struct sk_buff *(*gso_inner_segment)(struct sk_buff *skb,
@@ -51,8 +39,11 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
         * 16 bit length field due to the header being added outside of an
         * IP or IPv6 frame that was already limited to 64K - 1.
         */
-       partial = csum_sub(csum_unfold(uh->check),
-                          (__force __wsum)htonl(skb->len));
+       if (skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL)
+               partial = (__force __wsum)uh->len;
+       else
+               partial = (__force __wsum)htonl(skb->len);
+       partial = csum_sub(csum_unfold(uh->check), partial);
 
        /* setup inner skb. */
        skb->encapsulation = 0;
@@ -101,7 +92,7 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
        udp_offset = outer_hlen - tnl_hlen;
        skb = segs;
        do {
-               __be16 len;
+               unsigned int len;
 
                if (remcsum)
                        skb->ip_summed = CHECKSUM_NONE;
@@ -119,14 +110,26 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
                skb_reset_mac_header(skb);
                skb_set_network_header(skb, mac_len);
                skb_set_transport_header(skb, udp_offset);
-               len = htons(skb->len - udp_offset);
+               len = skb->len - udp_offset;
                uh = udp_hdr(skb);
-               uh->len = len;
+
+               /* If we are only performing partial GSO the inner header
+                * will be using a length value equal to only one MSS sized
+                * segment instead of the entire frame.
+                */
+               if (skb_is_gso(skb)) {
+                       uh->len = htons(skb_shinfo(skb)->gso_size +
+                                       SKB_GSO_CB(skb)->data_offset +
+                                       skb->head - (unsigned char *)uh);
+               } else {
+                       uh->len = htons(len);
+               }
 
                if (!need_csum)
                        continue;
 
-               uh->check = ~csum_fold(csum_add(partial, (__force __wsum)len));
+               uh->check = ~csum_fold(csum_add(partial,
+                                      (__force __wsum)htonl(len)));
 
                if (skb->encapsulation || !offload_csum) {
                        uh->check = gso_make_checksum(skb, ~uh->check);
@@ -179,6 +182,7 @@ out_unlock:
 
        return segs;
 }
+EXPORT_SYMBOL(skb_udp_tunnel_segment);
 
 static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
                                         netdev_features_t features)
@@ -253,64 +257,14 @@ out:
        return segs;
 }
 
-int udp_add_offload(struct net *net, struct udp_offload *uo)
-{
-       struct udp_offload_priv *new_offload = kzalloc(sizeof(*new_offload), GFP_ATOMIC);
-
-       if (!new_offload)
-               return -ENOMEM;
-
-       write_pnet(&new_offload->net, net);
-       new_offload->offload = uo;
-
-       spin_lock(&udp_offload_lock);
-       new_offload->next = udp_offload_base;
-       rcu_assign_pointer(udp_offload_base, new_offload);
-       spin_unlock(&udp_offload_lock);
-
-       return 0;
-}
-EXPORT_SYMBOL(udp_add_offload);
-
-static void udp_offload_free_routine(struct rcu_head *head)
-{
-       struct udp_offload_priv *ou_priv = container_of(head, struct udp_offload_priv, rcu);
-       kfree(ou_priv);
-}
-
-void udp_del_offload(struct udp_offload *uo)
-{
-       struct udp_offload_priv __rcu **head = &udp_offload_base;
-       struct udp_offload_priv *uo_priv;
-
-       spin_lock(&udp_offload_lock);
-
-       uo_priv = udp_deref_protected(*head);
-       for (; uo_priv != NULL;
-            uo_priv = udp_deref_protected(*head)) {
-               if (uo_priv->offload == uo) {
-                       rcu_assign_pointer(*head,
-                                          udp_deref_protected(uo_priv->next));
-                       goto unlock;
-               }
-               head = &uo_priv->next;
-       }
-       pr_warn("udp_del_offload: didn't find offload for port %d\n", ntohs(uo->port));
-unlock:
-       spin_unlock(&udp_offload_lock);
-       if (uo_priv)
-               call_rcu(&uo_priv->rcu, udp_offload_free_routine);
-}
-EXPORT_SYMBOL(udp_del_offload);
-
 struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
-                                struct udphdr *uh)
+                                struct udphdr *uh, udp_lookup_t lookup)
 {
-       struct udp_offload_priv *uo_priv;
        struct sk_buff *p, **pp = NULL;
        struct udphdr *uh2;
        unsigned int off = skb_gro_offset(skb);
        int flush = 1;
+       struct sock *sk;
 
        if (NAPI_GRO_CB(skb)->encap_mark ||
            (skb->ip_summed != CHECKSUM_PARTIAL &&
@@ -322,13 +276,10 @@ struct sk_buff **udp_gro_receive(struct sk_buff **head, struct sk_buff *skb,
        NAPI_GRO_CB(skb)->encap_mark = 1;
 
        rcu_read_lock();
-       uo_priv = rcu_dereference(udp_offload_base);
-       for (; uo_priv != NULL; uo_priv = rcu_dereference(uo_priv->next)) {
-               if (net_eq(read_pnet(&uo_priv->net), dev_net(skb->dev)) &&
-                   uo_priv->offload->port == uh->dest &&
-                   uo_priv->offload->callbacks.gro_receive)
-                       goto unflush;
-       }
+       sk = (*lookup)(skb, uh->source, uh->dest);
+
+       if (sk && udp_sk(sk)->gro_receive)
+               goto unflush;
        goto out_unlock;
 
 unflush:
@@ -352,9 +303,7 @@ unflush:
 
        skb_gro_pull(skb, sizeof(struct udphdr)); /* pull encapsulating udp header */
        skb_gro_postpull_rcsum(skb, uh, sizeof(struct udphdr));
-       NAPI_GRO_CB(skb)->proto = uo_priv->offload->ipproto;
-       pp = uo_priv->offload->callbacks.gro_receive(head, skb,
-                                                    uo_priv->offload);
+       pp = udp_sk(sk)->gro_receive(sk, head, skb);
 
 out_unlock:
        rcu_read_unlock();
@@ -362,6 +311,7 @@ out:
        NAPI_GRO_CB(skb)->flush |= flush;
        return pp;
 }
+EXPORT_SYMBOL(udp_gro_receive);
 
 static struct sk_buff **udp4_gro_receive(struct sk_buff **head,
                                         struct sk_buff *skb)
@@ -383,19 +333,20 @@ static struct sk_buff **udp4_gro_receive(struct sk_buff **head,
                                             inet_gro_compute_pseudo);
 skip:
        NAPI_GRO_CB(skb)->is_ipv6 = 0;
-       return udp_gro_receive(head, skb, uh);
+       return udp_gro_receive(head, skb, uh, udp4_lib_lookup_skb);
 
 flush:
        NAPI_GRO_CB(skb)->flush = 1;
        return NULL;
 }
 
-int udp_gro_complete(struct sk_buff *skb, int nhoff)
+int udp_gro_complete(struct sk_buff *skb, int nhoff,
+                    udp_lookup_t lookup)
 {
-       struct udp_offload_priv *uo_priv;
        __be16 newlen = htons(skb->len - nhoff);
        struct udphdr *uh = (struct udphdr *)(skb->data + nhoff);
        int err = -ENOSYS;
+       struct sock *sk;
 
        uh->len = newlen;
 
@@ -405,22 +356,10 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff)
        skb->encapsulation = 1;
 
        rcu_read_lock();
-
-       uo_priv = rcu_dereference(udp_offload_base);
-       for (; uo_priv != NULL; uo_priv = rcu_dereference(uo_priv->next)) {
-               if (net_eq(read_pnet(&uo_priv->net), dev_net(skb->dev)) &&
-                   uo_priv->offload->port == uh->dest &&
-                   uo_priv->offload->callbacks.gro_complete)
-                       break;
-       }
-
-       if (uo_priv) {
-               NAPI_GRO_CB(skb)->proto = uo_priv->offload->ipproto;
-               err = uo_priv->offload->callbacks.gro_complete(skb,
-                               nhoff + sizeof(struct udphdr),
-                               uo_priv->offload);
-       }
-
+       sk = (*lookup)(skb, uh->source, uh->dest);
+       if (sk && udp_sk(sk)->gro_complete)
+               err = udp_sk(sk)->gro_complete(sk, skb,
+                               nhoff + sizeof(struct udphdr));
        rcu_read_unlock();
 
        if (skb->remcsum_offload)
@@ -428,6 +367,7 @@ int udp_gro_complete(struct sk_buff *skb, int nhoff)
 
        return err;
 }
+EXPORT_SYMBOL(udp_gro_complete);
 
 static int udp4_gro_complete(struct sk_buff *skb, int nhoff)
 {
@@ -442,7 +382,7 @@ static int udp4_gro_complete(struct sk_buff *skb, int nhoff)
                skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL;
        }
 
-       return udp_gro_complete(skb, nhoff);
+       return udp_gro_complete(skb, nhoff, udp4_lib_lookup_skb);
 }
 
 static const struct net_offload udpv4_offload = {