Merge remote-tracking branch 'lsk/v3.10/topic/gator' into linux-linaro-lsk
[firefly-linux-kernel-4.4.55.git] / net / ipv4 / datagram.c
index 19e36376d2a083b5c8b307208ebd17ade086dcaa..5f3dc1df04bf28a8bc7b993cff545f8beff08192 100644 (file)
@@ -86,18 +86,26 @@ out:
 }
 EXPORT_SYMBOL(ip4_datagram_connect);
 
+/* Because UDP xmit path can manipulate sk_dst_cache without holding
+ * socket lock, we need to use sk_dst_set() here,
+ * even if we own the socket lock.
+ */
 void ip4_datagram_release_cb(struct sock *sk)
 {
        const struct inet_sock *inet = inet_sk(sk);
        const struct ip_options_rcu *inet_opt;
        __be32 daddr = inet->inet_daddr;
+       struct dst_entry *dst;
        struct flowi4 fl4;
        struct rtable *rt;
 
-       if (! __sk_dst_get(sk) || __sk_dst_check(sk, 0))
-               return;
-
        rcu_read_lock();
+
+       dst = __sk_dst_get(sk);
+       if (!dst || !dst->obsolete || dst->ops->check(dst, 0)) {
+               rcu_read_unlock();
+               return;
+       }
        inet_opt = rcu_dereference(inet->inet_opt);
        if (inet_opt && inet_opt->opt.srr)
                daddr = inet_opt->opt.faddr;
@@ -105,8 +113,10 @@ void ip4_datagram_release_cb(struct sock *sk)
                                   inet->inet_saddr, inet->inet_dport,
                                   inet->inet_sport, sk->sk_protocol,
                                   RT_CONN_FLAGS(sk), sk->sk_bound_dev_if);
-       if (!IS_ERR(rt))
-               __sk_dst_set(sk, &rt->dst);
+
+       dst = !IS_ERR(rt) ? &rt->dst : NULL;
+       sk_dst_set(sk, dst);
+
        rcu_read_unlock();
 }
 EXPORT_SYMBOL_GPL(ip4_datagram_release_cb);