]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/ipv6/ip6_output.c
include/linux: enclose idr.h in #ifndef
[mv-sheeva.git] / net / ipv6 / ip6_output.c
index 01ef94f7c7f1ce5dfd899703d287be308376a83c..614296a920c6ab189b58607726eea55bee4d0426 100644 (file)
@@ -147,7 +147,8 @@ static int ip6_output2(struct sk_buff *skb)
 
 int ip6_output(struct sk_buff *skb)
 {
-       if (skb->len > dst_mtu(skb->dst) || dst_allfrag(skb->dst))
+       if ((skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->ufo_size) ||
+                               dst_allfrag(skb->dst))
                return ip6_fragment(skb, ip6_output2);
        else
                return ip6_output2(skb);
@@ -166,7 +167,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
        struct ipv6hdr *hdr;
        u8  proto = fl->proto;
        int seg_len = skb->len;
-       int hlimit;
+       int hlimit, tclass;
        u32 mtu;
 
        if (opt) {
@@ -202,7 +203,6 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
         *      Fill in the IPv6 header
         */
 
-       *(u32*)hdr = htonl(0x60000000) | fl->fl6_flowlabel;
        hlimit = -1;
        if (np)
                hlimit = np->hop_limit;
@@ -211,6 +211,14 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
        if (hlimit < 0)
                hlimit = ipv6_get_hoplimit(dst->dev);
 
+       tclass = -1;
+       if (np)
+               tclass = np->tclass;
+       if (tclass < 0)
+               tclass = 0;
+
+       *(u32 *)hdr = htonl(0x60000000 | (tclass << 20)) | fl->fl6_flowlabel;
+
        hdr->payload_len = htons(seg_len);
        hdr->nexthdr = proto;
        hdr->hop_limit = hlimit;
@@ -659,7 +667,7 @@ slow_path:
                 */
                fh->nexthdr = nexthdr;
                fh->reserved = 0;
-               if (frag_id) {
+               if (!frag_id) {
                        ipv6_select_ident(skb, fh);
                        frag_id = fh->identification;
                } else
@@ -761,11 +769,71 @@ out_err_release:
        *dst = NULL;
        return err;
 }
+inline int ip6_ufo_append_data(struct sock *sk,
+                       int getfrag(void *from, char *to, int offset, int len,
+                       int odd, struct sk_buff *skb),
+                       void *from, int length, int hh_len, int fragheaderlen,
+                       int transhdrlen, int mtu,unsigned int flags)
+
+{
+       struct sk_buff *skb;
+       int err;
+
+       /* There is support for UDP large send offload by network
+        * device, so create one single skb packet containing complete
+        * udp datagram
+        */
+       if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) {
+               skb = sock_alloc_send_skb(sk,
+                       hh_len + fragheaderlen + transhdrlen + 20,
+                       (flags & MSG_DONTWAIT), &err);
+               if (skb == NULL)
+                       return -ENOMEM;
+
+               /* reserve space for Hardware header */
+               skb_reserve(skb, hh_len);
+
+               /* create space for UDP/IP header */
+               skb_put(skb,fragheaderlen + transhdrlen);
+
+               /* initialize network header pointer */
+               skb->nh.raw = skb->data;
+
+               /* initialize protocol header pointer */
+               skb->h.raw = skb->data + fragheaderlen;
+
+               skb->ip_summed = CHECKSUM_HW;
+               skb->csum = 0;
+               sk->sk_sndmsg_off = 0;
+       }
+
+       err = skb_append_datato_frags(sk,skb, getfrag, from,
+                                     (length - transhdrlen));
+       if (!err) {
+               struct frag_hdr fhdr;
+
+               /* specify the length of each IP datagram fragment*/
+               skb_shinfo(skb)->ufo_size = (mtu - fragheaderlen) - 
+                                               sizeof(struct frag_hdr);
+               ipv6_select_ident(skb, &fhdr);
+               skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
+               __skb_queue_tail(&sk->sk_write_queue, skb);
+
+               return 0;
+       }
+       /* There is not enough support do UPD LSO,
+        * so follow normal path
+        */
+       kfree_skb(skb);
+
+       return err;
+}
 
-int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb),
-                   void *from, int length, int transhdrlen,
-                   int hlimit, struct ipv6_txoptions *opt, struct flowi *fl, struct rt6_info *rt,
-                   unsigned int flags)
+int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
+       int offset, int len, int odd, struct sk_buff *skb),
+       void *from, int length, int transhdrlen,
+       int hlimit, int tclass, struct ipv6_txoptions *opt, struct flowi *fl,
+       struct rt6_info *rt, unsigned int flags)
 {
        struct inet_sock *inet = inet_sk(sk);
        struct ipv6_pinfo *np = inet6_sk(sk);
@@ -803,6 +871,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse
                np->cork.rt = rt;
                inet->cork.fl = *fl;
                np->cork.hop_limit = hlimit;
+               np->cork.tclass = tclass;
                inet->cork.fragsize = mtu = dst_mtu(rt->u.dst.path);
                if (dst_allfrag(rt->u.dst.path))
                        inet->cork.flags |= IPCORK_ALLFRAG;
@@ -851,6 +920,15 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse
         */
 
        inet->cork.length += length;
+       if (((length > mtu) && (sk->sk_protocol == IPPROTO_UDP)) &&
+           (rt->u.dst.dev->features & NETIF_F_UFO)) {
+
+               if(ip6_ufo_append_data(sk, getfrag, from, length, hh_len,
+                               fragheaderlen, transhdrlen, mtu, flags))
+                       goto error;
+
+               return 0;
+       }
 
        if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL)
                goto alloc_new_skb;
@@ -1084,7 +1162,8 @@ int ip6_push_pending_frames(struct sock *sk)
 
        skb->nh.ipv6h = hdr = (struct ipv6hdr*) skb_push(skb, sizeof(struct ipv6hdr));
        
-       *(u32*)hdr = fl->fl6_flowlabel | htonl(0x60000000);
+       *(u32*)hdr = fl->fl6_flowlabel |
+                    htonl(0x60000000 | ((int)np->cork.tclass << 20));
 
        if (skb->len <= sizeof(struct ipv6hdr) + IPV6_MAXPLEN)
                hdr->payload_len = htons(skb->len - sizeof(struct ipv6hdr));