]> Pileus Git - ~andy/linux/blobdiff - net/ipv6/ip6_output.c
ipv6: Fix possible ipv6 seqlock deadlock
[~andy/linux] / net / ipv6 / ip6_output.c
index a54c45ce4a48f0d3a65f6c54ac77bb73a6a41280..ad138b187fb229599f4e2294a495f316ac7679e6 100644 (file)
@@ -105,7 +105,7 @@ static int ip6_finish_output2(struct sk_buff *skb)
        }
 
        rcu_read_lock_bh();
-       nexthop = rt6_nexthop((struct rt6_info *)dst, &ipv6_hdr(skb)->daddr);
+       nexthop = rt6_nexthop((struct rt6_info *)dst);
        neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop);
        if (unlikely(!neigh))
                neigh = __neigh_create(&nd_tbl, nexthop, dst->dev, false);
@@ -874,7 +874,7 @@ static int ip6_dst_lookup_tail(struct sock *sk,
         */
        rt = (struct rt6_info *) *dst;
        rcu_read_lock_bh();
-       n = __ipv6_neigh_lookup_noref(rt->dst.dev, rt6_nexthop(rt, &fl6->daddr));
+       n = __ipv6_neigh_lookup_noref(rt->dst.dev, rt6_nexthop(rt));
        err = n && !(n->nud_state & NUD_VALID) ? -EINVAL : 0;
        rcu_read_unlock_bh();
 
@@ -909,7 +909,7 @@ static int ip6_dst_lookup_tail(struct sock *sk,
 
 out_err_release:
        if (err == -ENETUNREACH)
-               IP6_INC_STATS_BH(net, NULL, IPSTATS_MIB_OUTNOROUTES);
+               IP6_INC_STATS(net, NULL, IPSTATS_MIB_OUTNOROUTES);
        dst_release(*dst);
        *dst = NULL;
        return err;
@@ -1008,6 +1008,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
 
 {
        struct sk_buff *skb;
+       struct frag_hdr fhdr;
        int err;
 
        /* There is support for UDP large send offload by network
@@ -1015,8 +1016,6 @@ static inline int ip6_ufo_append_data(struct sock *sk,
         * udp datagram
         */
        if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) {
-               struct frag_hdr fhdr;
-
                skb = sock_alloc_send_skb(sk,
                        hh_len + fragheaderlen + transhdrlen + 20,
                        (flags & MSG_DONTWAIT), &err);
@@ -1036,20 +1035,24 @@ static inline int ip6_ufo_append_data(struct sock *sk,
                skb->transport_header = skb->network_header + fragheaderlen;
 
                skb->protocol = htons(ETH_P_IPV6);
-               skb->ip_summed = CHECKSUM_PARTIAL;
                skb->csum = 0;
 
-               /* Specify the length of each IPv6 datagram fragment.
-                * It has to be a multiple of 8.
-                */
-               skb_shinfo(skb)->gso_size = (mtu - fragheaderlen -
-                                            sizeof(struct frag_hdr)) & ~7;
-               skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
-               ipv6_select_ident(&fhdr, rt);
-               skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
                __skb_queue_tail(&sk->sk_write_queue, skb);
+       } else if (skb_is_gso(skb)) {
+               goto append;
        }
 
+       skb->ip_summed = CHECKSUM_PARTIAL;
+       /* Specify the length of each IPv6 datagram fragment.
+        * It has to be a multiple of 8.
+        */
+       skb_shinfo(skb)->gso_size = (mtu - fragheaderlen -
+                                    sizeof(struct frag_hdr)) & ~7;
+       skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
+       ipv6_select_ident(&fhdr, rt);
+       skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
+
+append:
        return skb_append_datato_frags(sk, skb, getfrag, from,
                                       (length - transhdrlen));
 }