Merge branches 'pm-devfreq', 'pm-qos', 'pm-tools' and 'pm-docs'
[linux-2.6-block.git] / drivers / net / amt.c
index 2d007c5c546362a917fd0083c889c839b43c6f5f..e019526e1df67527803363da684448d7f55ddae8 100644 (file)
@@ -967,8 +967,11 @@ static void amt_event_send_request(struct amt_dev *amt)
                goto out;
        }
 
-       if (!amt->req_cnt)
+       if (!amt->req_cnt) {
+               WRITE_ONCE(amt->ready4, false);
+               WRITE_ONCE(amt->ready6, false);
                get_random_bytes(&amt->nonce, sizeof(__be32));
+       }
 
        amt_send_request(amt, false);
        amt_send_request(amt, true);
@@ -2279,6 +2282,9 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb)
        struct ethhdr *eth;
        struct iphdr *iph;
 
+       if (READ_ONCE(amt->status) != AMT_STATUS_SENT_UPDATE)
+               return true;
+
        hdr_size = sizeof(*amtmd) + sizeof(struct udphdr);
        if (!pskb_may_pull(skb, hdr_size))
                return true;
@@ -2353,6 +2359,9 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
        if (amtmq->reserved || amtmq->version)
                return true;
 
+       if (amtmq->nonce != amt->nonce)
+               return true;
+
        hdr_size -= sizeof(*eth);
        if (iptunnel_pull_header(skb, hdr_size, htons(ETH_P_TEB), false))
                return true;
@@ -2367,6 +2376,9 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
 
        iph = ip_hdr(skb);
        if (iph->version == 4) {
+               if (READ_ONCE(amt->ready4))
+                       return true;
+
                if (!pskb_may_pull(skb, sizeof(*iph) + AMT_IPHDR_OPTS +
                                   sizeof(*ihv3)))
                        return true;
@@ -2389,6 +2401,9 @@ static bool amt_membership_query_handler(struct amt_dev *amt,
                struct mld2_query *mld2q;
                struct ipv6hdr *ip6h;
 
+               if (READ_ONCE(amt->ready6))
+                       return true;
+
                if (!pskb_may_pull(skb, sizeof(*ip6h) + AMT_IP6HDR_OPTS +
                                   sizeof(*mld2q)))
                        return true;
@@ -2664,7 +2679,9 @@ static bool amt_request_handler(struct amt_dev *amt, struct sk_buff *skb)
                if (tunnel->ip4 == iph->saddr)
                        goto send;
 
+       spin_lock_bh(&amt->lock);
        if (amt->nr_tunnels >= amt->max_tunnels) {
+               spin_unlock_bh(&amt->lock);
                icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
                return true;
        }
@@ -2672,8 +2689,10 @@ static bool amt_request_handler(struct amt_dev *amt, struct sk_buff *skb)
        tunnel = kzalloc(sizeof(*tunnel) +
                         (sizeof(struct hlist_head) * amt->hash_buckets),
                         GFP_ATOMIC);
-       if (!tunnel)
+       if (!tunnel) {
+               spin_unlock_bh(&amt->lock);
                return true;
+       }
 
        tunnel->source_port = udph->source;
        tunnel->ip4 = iph->saddr;
@@ -2686,10 +2705,9 @@ static bool amt_request_handler(struct amt_dev *amt, struct sk_buff *skb)
 
        INIT_DELAYED_WORK(&tunnel->gc_wq, amt_tunnel_expire);
 
-       spin_lock_bh(&amt->lock);
        list_add_tail_rcu(&tunnel->list, &amt->tunnel_list);
        tunnel->key = amt->key;
-       amt_update_relay_status(tunnel, AMT_STATUS_RECEIVED_REQUEST, true);
+       __amt_update_relay_status(tunnel, AMT_STATUS_RECEIVED_REQUEST, true);
        amt->nr_tunnels++;
        mod_delayed_work(amt_wq, &tunnel->gc_wq,
                         msecs_to_jiffies(amt_gmi(amt)));