Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[linux-2.6-block.git] / net / ipv6 / ip6_output.c
index 4591ca4bdbe8831f8183c4df0d4237d023b5bb68..5f9fa0302b5a97af835b6f9ff10f53f1b6e1c9be 100644 (file)
@@ -378,6 +378,13 @@ static inline int ip6_forward_finish(struct net *net, struct sock *sk,
        __IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS);
        __IP6_ADD_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTOCTETS, skb->len);
 
+#ifdef CONFIG_NET_SWITCHDEV
+       if (skb->offload_l3_fwd_mark) {
+               consume_skb(skb);
+               return 0;
+       }
+#endif
+
        skb->tstamp = 0;
        return dst_output(net, sk, skb);
 }
@@ -575,6 +582,7 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
        to->tc_index = from->tc_index;
 #endif
        nf_copy(to, from);
+       skb_ext_copy(to, from);
        skb_copy_secmark(to, from);
 }
 
@@ -1246,6 +1254,7 @@ static int __ip6_append_data(struct sock *sk,
 {
        struct sk_buff *skb, *skb_prev = NULL;
        unsigned int maxfraglen, fragheaderlen, mtu, orig_mtu, pmtu;
+       struct ubuf_info *uarg = NULL;
        int exthdrlen = 0;
        int dst_exthdrlen = 0;
        int hh_len;
@@ -1258,7 +1267,7 @@ static int __ip6_append_data(struct sock *sk,
        int csummode = CHECKSUM_NONE;
        unsigned int maxnonfragsize, headersize;
        unsigned int wmem_alloc_delta = 0;
-       bool paged;
+       bool paged, extra_uref;
 
        skb = skb_peek_tail(queue);
        if (!skb) {
@@ -1323,6 +1332,20 @@ emsgsize:
            rt->dst.dev->features & (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM))
                csummode = CHECKSUM_PARTIAL;
 
+       if (flags & MSG_ZEROCOPY && length && sock_flag(sk, SOCK_ZEROCOPY)) {
+               uarg = sock_zerocopy_realloc(sk, length, skb_zcopy(skb));
+               if (!uarg)
+                       return -ENOBUFS;
+               extra_uref = true;
+               if (rt->dst.dev->features & NETIF_F_SG &&
+                   csummode == CHECKSUM_PARTIAL) {
+                       paged = true;
+               } else {
+                       uarg->zerocopy = 0;
+                       skb_zcopy_set(skb, uarg, &extra_uref);
+               }
+       }
+
        /*
         * Let's try using as much space as possible.
         * Use MTU if total length of the message fits into the MTU.
@@ -1441,12 +1464,6 @@ alloc_new_skb:
                        skb_reserve(skb, hh_len + sizeof(struct frag_hdr) +
                                    dst_exthdrlen);
 
-                       /* Only the initial fragment is time stamped */
-                       skb_shinfo(skb)->tx_flags = cork->tx_flags;
-                       cork->tx_flags = 0;
-                       skb_shinfo(skb)->tskey = tskey;
-                       tskey = 0;
-
                        /*
                         *      Find where to start putting bytes
                         */
@@ -1478,6 +1495,13 @@ alloc_new_skb:
                        exthdrlen = 0;
                        dst_exthdrlen = 0;
 
+                       /* Only the initial fragment is time stamped */
+                       skb_shinfo(skb)->tx_flags = cork->tx_flags;
+                       cork->tx_flags = 0;
+                       skb_shinfo(skb)->tskey = tskey;
+                       tskey = 0;
+                       skb_zcopy_set(skb, uarg, &extra_uref);
+
                        if ((flags & MSG_CONFIRM) && !skb_prev)
                                skb_set_dst_pending_confirm(skb, 1);
 
@@ -1507,7 +1531,7 @@ alloc_new_skb:
                                err = -EFAULT;
                                goto error;
                        }
-               } else {
+               } else if (!uarg || !uarg->zerocopy) {
                        int i = skb_shinfo(skb)->nr_frags;
 
                        err = -ENOMEM;
@@ -1537,6 +1561,10 @@ alloc_new_skb:
                        skb->data_len += copy;
                        skb->truesize += copy;
                        wmem_alloc_delta += copy;
+               } else {
+                       err = skb_zerocopy_iter_dgram(skb, from, copy);
+                       if (err < 0)
+                               goto error;
                }
                offset += copy;
                length -= copy;
@@ -1549,6 +1577,8 @@ alloc_new_skb:
 error_efault:
        err = -EFAULT;
 error:
+       if (uarg)
+               sock_zerocopy_put_abort(uarg, extra_uref);
        cork->length -= length;
        IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
        refcount_add(wmem_alloc_delta, &sk->sk_wmem_alloc);