Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec...
[linux-2.6-block.git] / net / ipv4 / ip_output.c
index a2dfb5a9ba7698d193239f696d1f4d3dcc9732c4..95adb171f852d7c6e6ec964edd47c0bf74a6fbd2 100644 (file)
@@ -878,11 +878,14 @@ static int __ip_append_data(struct sock *sk,
        struct rtable *rt = (struct rtable *)cork->dst;
        unsigned int wmem_alloc_delta = 0;
        u32 tskey = 0;
+       bool paged;
 
        skb = skb_peek_tail(queue);
 
        exthdrlen = !skb ? rt->dst.header_len : 0;
-       mtu = cork->fragsize;
+       mtu = cork->gso_size ? IP_MAX_MTU : cork->fragsize;
+       paged = !!cork->gso_size;
+
        if (cork->tx_flags & SKBTX_ANY_SW_TSTAMP &&
            sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)
                tskey = sk->sk_tskey++;
@@ -906,7 +909,7 @@ static int __ip_append_data(struct sock *sk,
        if (transhdrlen &&
            length + fragheaderlen <= mtu &&
            rt->dst.dev->features & (NETIF_F_HW_CSUM | NETIF_F_IP_CSUM) &&
-           !(flags & MSG_MORE) &&
+           (!(flags & MSG_MORE) || cork->gso_size) &&
            (!exthdrlen || (rt->dst.dev->features & NETIF_F_HW_ESP_TX_CSUM)))
                csummode = CHECKSUM_PARTIAL;
 
@@ -933,6 +936,7 @@ static int __ip_append_data(struct sock *sk,
                        unsigned int fraglen;
                        unsigned int fraggap;
                        unsigned int alloclen;
+                       unsigned int pagedlen = 0;
                        struct sk_buff *skb_prev;
 alloc_new_skb:
                        skb_prev = skb;
@@ -953,8 +957,12 @@ alloc_new_skb:
                        if ((flags & MSG_MORE) &&
                            !(rt->dst.dev->features&NETIF_F_SG))
                                alloclen = mtu;
-                       else
+                       else if (!paged)
                                alloclen = fraglen;
+                       else {
+                               alloclen = min_t(int, fraglen, MAX_HEADER);
+                               pagedlen = fraglen - alloclen;
+                       }
 
                        alloclen += exthdrlen;
 
@@ -998,7 +1006,7 @@ alloc_new_skb:
                        /*
                         *      Find where to start putting bytes.
                         */
-                       data = skb_put(skb, fraglen + exthdrlen);
+                       data = skb_put(skb, fraglen + exthdrlen - pagedlen);
                        skb_set_network_header(skb, exthdrlen);
                        skb->transport_header = (skb->network_header +
                                                 fragheaderlen);
@@ -1014,7 +1022,7 @@ alloc_new_skb:
                                pskb_trim_unique(skb_prev, maxfraglen);
                        }
 
-                       copy = datalen - transhdrlen - fraggap;
+                       copy = datalen - transhdrlen - fraggap - pagedlen;
                        if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) {
                                err = -EFAULT;
                                kfree_skb(skb);
@@ -1022,7 +1030,7 @@ alloc_new_skb:
                        }
 
                        offset += copy;
-                       length -= datalen - fraggap;
+                       length -= copy + transhdrlen;
                        transhdrlen = 0;
                        exthdrlen = 0;
                        csummode = CHECKSUM_NONE;
@@ -1109,6 +1117,10 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork,
        struct ip_options_rcu *opt;
        struct rtable *rt;
 
+       rt = *rtp;
+       if (unlikely(!rt))
+               return -EFAULT;
+
        /*
         * setup for corking.
         */
@@ -1124,15 +1136,15 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork,
                cork->flags |= IPCORK_OPT;
                cork->addr = ipc->addr;
        }
-       rt = *rtp;
-       if (unlikely(!rt))
-               return -EFAULT;
+
        /*
         * We steal reference to this route, caller should not release it
         */
        *rtp = NULL;
        cork->fragsize = ip_sk_use_pmtu(sk) ?
                         dst_mtu(&rt->dst) : rt->dst.dev->mtu;
+
+       cork->gso_size = sk->sk_type == SOCK_DGRAM ? ipc->gso_size : 0;
        cork->dst = &rt->dst;
        cork->length = 0;
        cork->ttl = ipc->ttl;
@@ -1212,7 +1224,7 @@ ssize_t   ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page,
                return -EOPNOTSUPP;
 
        hh_len = LL_RESERVED_SPACE(rt->dst.dev);
-       mtu = cork->fragsize;
+       mtu = cork->gso_size ? IP_MAX_MTU : cork->fragsize;
 
        fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0);
        maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen;
@@ -1468,9 +1480,8 @@ struct sk_buff *ip_make_skb(struct sock *sk,
                                        int len, int odd, struct sk_buff *skb),
                            void *from, int length, int transhdrlen,
                            struct ipcm_cookie *ipc, struct rtable **rtp,
-                           unsigned int flags)
+                           struct inet_cork *cork, unsigned int flags)
 {
-       struct inet_cork cork;
        struct sk_buff_head queue;
        int err;
 
@@ -1479,22 +1490,22 @@ struct sk_buff *ip_make_skb(struct sock *sk,
 
        __skb_queue_head_init(&queue);
 
-       cork.flags = 0;
-       cork.addr = 0;
-       cork.opt = NULL;
-       err = ip_setup_cork(sk, &cork, ipc, rtp);
+       cork->flags = 0;
+       cork->addr = 0;
+       cork->opt = NULL;
+       err = ip_setup_cork(sk, cork, ipc, rtp);
        if (err)
                return ERR_PTR(err);
 
-       err = __ip_append_data(sk, fl4, &queue, &cork,
+       err = __ip_append_data(sk, fl4, &queue, cork,
                               &current->task_frag, getfrag,
                               from, length, transhdrlen, flags);
        if (err) {
-               __ip_flush_pending_frames(sk, &queue, &cork);
+               __ip_flush_pending_frames(sk, &queue, cork);
                return ERR_PTR(err);
        }
 
-       return __ip_make_skb(sk, fl4, &queue, &cork);
+       return __ip_make_skb(sk, fl4, &queue, cork);
 }
 
 /*