ipvs: add ip_vs_dest_hold and ip_vs_dest_put
authorJulian Anastasov <ja@ssi.bg>
Fri, 22 Mar 2013 09:46:38 +0000 (11:46 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 1 Apr 2013 22:23:48 +0000 (00:23 +0200)
ip_vs_dest_hold will be used under RCU lock
while ip_vs_dest_put can be called even after dest
is removed from service, as it happens for conns and
some schedulers.

Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Simon Horman <horms@verge.net.au>
include/net/ip_vs.h
net/netfilter/ipvs/ip_vs_conn.c
net/netfilter/ipvs/ip_vs_ctl.c
net/netfilter/ipvs/ip_vs_sync.c

index d91385c1b3a27960d20994f7a5c7e2853cdf66e9..7d3027f2c06e59219bd2486a6fdfd887e6ba1da0 100644 (file)
@@ -1427,6 +1427,16 @@ ip_vs_find_dest(struct net *net, int af, const union nf_inet_addr *daddr,
                __u16 protocol, __u32 fwmark, __u32 flags);
 extern struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp);
 
+static inline void ip_vs_dest_hold(struct ip_vs_dest *dest)
+{
+       atomic_inc(&dest->refcnt);
+}
+
+static inline void ip_vs_dest_put(struct ip_vs_dest *dest)
+{
+       smp_mb__before_atomic_dec();
+       atomic_dec(&dest->refcnt);
+}
 
 /*
  *      IPVS sync daemon data and function prototypes
index e3e2b4d3b6d86b2a4b0bc11a5151616eb30926ae..1b29e4a2b26c676af2a60b217698f1116c7fa4f4 100644 (file)
@@ -554,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)
@@ -700,12 +700,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,
index d64f800a34269b250e6d62118785a20928b9cba4..a4f6388804709885fa8dc74d87c197685a3f3b6e 100644 (file)
@@ -616,7 +616,7 @@ struct ip_vs_dest *ip_vs_find_dest(struct net  *net, int af,
        if (!dest)
                dest = ip_vs_lookup_dest(svc, daddr, port ^ dport);
        if (dest)
-               atomic_inc(&dest->refcnt);
+               ip_vs_dest_hold(dest);
        ip_vs_service_put(svc);
        return dest;
 }
@@ -1056,7 +1056,7 @@ static void __ip_vs_del_dest(struct net *net, struct ip_vs_dest *dest)
                              ntohs(dest->port),
                              atomic_read(&dest->refcnt));
                list_add(&dest->n_list, &ipvs->dest_trash);
-               atomic_inc(&dest->refcnt);
+               ip_vs_dest_hold(dest);
        }
 }
 
index 44fd10c539ac42dfaa967974051bdfacb17099fb..6cc3e52f1f3541ddf69b28b5e5bc37ad5b4850e3 100644 (file)
@@ -861,7 +861,7 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param,
                if (!dest) {
                        dest = ip_vs_try_bind_dest(cp);
                        if (dest)
-                               atomic_dec(&dest->refcnt);
+                               ip_vs_dest_put(dest);
                }
        } else {
                /*
@@ -874,7 +874,7 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param,
 
                cp = ip_vs_conn_new(param, daddr, dport, flags, dest, fwmark);
                if (dest)
-                       atomic_dec(&dest->refcnt);
+                       ip_vs_dest_put(dest);
                if (!cp) {
                        if (param->pe_data)
                                kfree(param->pe_data);