Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[firefly-linux-kernel-4.4.55.git] / net / netfilter / ipvs / ip_vs_conn.c
index b0cd2be01d753f5ff594d4695ce00f4da71612cf..de6475894a39e10b04c7892fe53960a92b8ac1b4 100644 (file)
@@ -86,14 +86,14 @@ struct ip_vs_aligned_lock
 static struct ip_vs_aligned_lock
 __ip_vs_conntbl_lock_array[CT_LOCKARRAY_SIZE] __cacheline_aligned;
 
-static inline void ct_write_lock(unsigned int key)
+static inline void ct_write_lock_bh(unsigned int key)
 {
-       spin_lock(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l);
+       spin_lock_bh(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l);
 }
 
-static inline void ct_write_unlock(unsigned int key)
+static inline void ct_write_unlock_bh(unsigned int key)
 {
-       spin_unlock(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l);
+       spin_unlock_bh(&__ip_vs_conntbl_lock_array[key&CT_LOCKARRAY_MASK].l);
 }
 
 
@@ -167,7 +167,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)
        /* Hash by protocol, client address and port */
        hash = ip_vs_conn_hashkey_conn(cp);
 
-       ct_write_lock(hash);
+       ct_write_lock_bh(hash);
        spin_lock(&cp->lock);
 
        if (!(cp->flags & IP_VS_CONN_F_HASHED)) {
@@ -182,7 +182,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp)
        }
 
        spin_unlock(&cp->lock);
-       ct_write_unlock(hash);
+       ct_write_unlock_bh(hash);
 
        return ret;
 }
@@ -200,7 +200,7 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp)
        /* unhash it and decrease its reference counter */
        hash = ip_vs_conn_hashkey_conn(cp);
 
-       ct_write_lock(hash);
+       ct_write_lock_bh(hash);
        spin_lock(&cp->lock);
 
        if (cp->flags & IP_VS_CONN_F_HASHED) {
@@ -212,7 +212,7 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp)
                ret = 0;
 
        spin_unlock(&cp->lock);
-       ct_write_unlock(hash);
+       ct_write_unlock_bh(hash);
 
        return ret;
 }
@@ -227,7 +227,7 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)
 
        hash = ip_vs_conn_hashkey_conn(cp);
 
-       ct_write_lock(hash);
+       ct_write_lock_bh(hash);
        spin_lock(&cp->lock);
 
        if (cp->flags & IP_VS_CONN_F_HASHED) {
@@ -242,7 +242,7 @@ static inline bool ip_vs_conn_unlink(struct ip_vs_conn *cp)
                ret = atomic_read(&cp->refcnt) ? false : true;
 
        spin_unlock(&cp->lock);
-       ct_write_unlock(hash);
+       ct_write_unlock_bh(hash);
 
        return ret;
 }
@@ -265,8 +265,8 @@ __ip_vs_conn_in_get(const struct ip_vs_conn_param *p)
        rcu_read_lock();
 
        hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[hash], c_list) {
-               if (cp->af == p->af &&
-                   p->cport == cp->cport && p->vport == cp->vport &&
+               if (p->cport == cp->cport && p->vport == cp->vport &&
+                   cp->af == p->af &&
                    ip_vs_addr_equal(p->af, p->caddr, &cp->caddr) &&
                    ip_vs_addr_equal(p->af, p->vaddr, &cp->vaddr) &&
                    ((!p->cport) ^ (!(cp->flags & IP_VS_CONN_F_NO_CPORT))) &&
@@ -350,9 +350,9 @@ struct ip_vs_conn *ip_vs_ct_in_get(const struct ip_vs_conn_param *p)
        rcu_read_lock();
 
        hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[hash], c_list) {
-               if (!ip_vs_conn_net_eq(cp, p->net))
-                       continue;
-               if (p->pe_data && p->pe->ct_match) {
+               if (unlikely(p->pe_data && p->pe->ct_match)) {
+                       if (!ip_vs_conn_net_eq(cp, p->net))
+                               continue;
                        if (p->pe == cp->pe && p->pe->ct_match(p, cp)) {
                                if (__ip_vs_conn_get(cp))
                                        goto out;
@@ -366,9 +366,10 @@ struct ip_vs_conn *ip_vs_ct_in_get(const struct ip_vs_conn_param *p)
                     * p->vaddr is a fwmark */
                    ip_vs_addr_equal(p->protocol == IPPROTO_IP ? AF_UNSPEC :
                                     p->af, p->vaddr, &cp->vaddr) &&
-                   p->cport == cp->cport && p->vport == cp->vport &&
+                   p->vport == cp->vport && p->cport == cp->cport &&
                    cp->flags & IP_VS_CONN_F_TEMPLATE &&
-                   p->protocol == cp->protocol) {
+                   p->protocol == cp->protocol &&
+                   ip_vs_conn_net_eq(cp, p->net)) {
                        if (__ip_vs_conn_get(cp))
                                goto out;
                }
@@ -404,8 +405,8 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p)
        rcu_read_lock();
 
        hlist_for_each_entry_rcu(cp, &ip_vs_conn_tab[hash], c_list) {
-               if (cp->af == p->af &&
-                   p->vport == cp->cport && p->cport == cp->dport &&
+               if (p->vport == cp->cport && p->cport == cp->dport &&
+                   cp->af == p->af &&
                    ip_vs_addr_equal(p->af, p->vaddr, &cp->caddr) &&
                    ip_vs_addr_equal(p->af, p->caddr, &cp->daddr) &&
                    p->protocol == cp->protocol &&
@@ -461,13 +462,13 @@ void ip_vs_conn_put(struct ip_vs_conn *cp)
 void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport)
 {
        if (ip_vs_conn_unhash(cp)) {
-               spin_lock(&cp->lock);
+               spin_lock_bh(&cp->lock);
                if (cp->flags & IP_VS_CONN_F_NO_CPORT) {
                        atomic_dec(&ip_vs_conn_no_cport_cnt);
                        cp->flags &= ~IP_VS_CONN_F_NO_CPORT;
                        cp->cport = cport;
                }
-               spin_unlock(&cp->lock);
+               spin_unlock_bh(&cp->lock);
 
                /* hash on new dport */
                ip_vs_conn_hash(cp);
@@ -553,7 +554,7 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
                return;
 
        /* Increase the refcnt counter of the dest */
-       atomic_inc(&dest->refcnt);
+       ip_vs_dest_hold(dest);
 
        conn_flags = atomic_read(&dest->conn_flags);
        if (cp->protocol != IPPROTO_UDP)
@@ -610,20 +611,22 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
  * Check if there is a destination for the connection, if so
  * bind the connection to the destination.
  */
-struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp)
+void ip_vs_try_bind_dest(struct ip_vs_conn *cp)
 {
        struct ip_vs_dest *dest;
 
+       rcu_read_lock();
        dest = ip_vs_find_dest(ip_vs_conn_net(cp), cp->af, &cp->daddr,
                               cp->dport, &cp->vaddr, cp->vport,
                               cp->protocol, cp->fwmark, cp->flags);
        if (dest) {
                struct ip_vs_proto_data *pd;
 
-               spin_lock(&cp->lock);
+               spin_lock_bh(&cp->lock);
                if (cp->dest) {
-                       spin_unlock(&cp->lock);
-                       return dest;
+                       spin_unlock_bh(&cp->lock);
+                       rcu_read_unlock();
+                       return;
                }
 
                /* Applications work depending on the forwarding method
@@ -632,7 +635,7 @@ struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp)
                        ip_vs_unbind_app(cp);
 
                ip_vs_bind_dest(cp, dest);
-               spin_unlock(&cp->lock);
+               spin_unlock_bh(&cp->lock);
 
                /* Update its packet transmitter */
                cp->packet_xmit = NULL;
@@ -647,7 +650,7 @@ struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp)
                if (pd && atomic_read(&pd->appcnt))
                        ip_vs_bind_app(cp, pd->pp);
        }
-       return dest;
+       rcu_read_unlock();
 }
 
 
@@ -699,12 +702,7 @@ static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp)
                        dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
        }
 
-       /*
-        * Simply decrease the refcnt of the dest, because the
-        * dest will be either in service's destination list
-        * or in the trash.
-        */
-       atomic_dec(&dest->refcnt);
+       ip_vs_dest_put(dest);
 }
 
 static int expire_quiescent_template(struct netns_ipvs *ipvs,
@@ -860,7 +858,7 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p,
        struct ip_vs_proto_data *pd = ip_vs_proto_data_get(p->net,
                                                           p->protocol);
 
-       cp = kmem_cache_zalloc(ip_vs_conn_cachep, GFP_ATOMIC);
+       cp = kmem_cache_alloc(ip_vs_conn_cachep, GFP_ATOMIC);
        if (cp == NULL) {
                IP_VS_ERR_RL("%s(): no memory\n", __func__);
                return NULL;
@@ -871,13 +869,13 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p,
        ip_vs_conn_net_set(cp, p->net);
        cp->af             = p->af;
        cp->protocol       = p->protocol;
-       ip_vs_addr_copy(p->af, &cp->caddr, p->caddr);
+       ip_vs_addr_set(p->af, &cp->caddr, p->caddr);
        cp->cport          = p->cport;
-       ip_vs_addr_copy(p->af, &cp->vaddr, p->vaddr);
+       ip_vs_addr_set(p->af, &cp->vaddr, p->vaddr);
        cp->vport          = p->vport;
        /* proto should only be IPPROTO_IP if d_addr is a fwmark */
-       ip_vs_addr_copy(p->protocol == IPPROTO_IP ? AF_UNSPEC : p->af,
-                       &cp->daddr, daddr);
+       ip_vs_addr_set(p->protocol == IPPROTO_IP ? AF_UNSPEC : p->af,
+                      &cp->daddr, daddr);
        cp->dport          = dport;
        cp->flags          = flags;
        cp->fwmark         = fwmark;
@@ -886,6 +884,10 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p,
                cp->pe = p->pe;
                cp->pe_data = p->pe_data;
                cp->pe_data_len = p->pe_data_len;
+       } else {
+               cp->pe = NULL;
+               cp->pe_data = NULL;
+               cp->pe_data_len = 0;
        }
        spin_lock_init(&cp->lock);
 
@@ -896,18 +898,28 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p,
         */
        atomic_set(&cp->refcnt, 1);
 
+       cp->control = NULL;
        atomic_set(&cp->n_control, 0);
        atomic_set(&cp->in_pkts, 0);
 
+       cp->packet_xmit = NULL;
+       cp->app = NULL;
+       cp->app_data = NULL;
+       /* reset struct ip_vs_seq */
+       cp->in_seq.delta = 0;
+       cp->out_seq.delta = 0;
+
        atomic_inc(&ipvs->conn_count);
        if (flags & IP_VS_CONN_F_NO_CPORT)
                atomic_inc(&ip_vs_conn_no_cport_cnt);
 
        /* Bind the connection with a destination server */
+       cp->dest = NULL;
        ip_vs_bind_dest(cp, dest);
 
        /* Set its state and timeout */
        cp->state = 0;
+       cp->old_state = 0;
        cp->timeout = 3*HZ;
        cp->sync_endtime = jiffies & ~3UL;