aboutsummaryrefslogtreecommitdiffstats
path: root/net/netfilter
diff options
context:
space:
mode:
authorJulian Anastasov <ja@ssi.bg>2013-03-22 05:46:52 -0400
committerPablo Neira Ayuso <pablo@netfilter.org>2013-04-01 18:23:57 -0400
commit413c2d04e9494ca38629d8a7ffeff1e4398a9fe3 (patch)
tree84a0ce4e93f5855c1a9f3f010b4f364917581da1 /net/netfilter
parentba3a3ce14ea26d602b253ef13a56d540827cd51d (diff)
ipvs: convert dests to rcu
In previous commits the schedulers started to access svc->destinations with _rcu list traversal primitives because the IP_VS_WAIT_WHILE macro still plays the role of grace period. Now it is time to finish the updating part, i.e. adding and deleting of dests with _rcu suffix before removing the IP_VS_WAIT_WHILE in next commit. We use the same rule for conns as for the schedulers: dests can be searched in RCU read-side critical section where ip_vs_dest_hold can be called by ip_vs_bind_dest. Some things are not perfect, for example, calling functions like ip_vs_lookup_dest from updating code under RCU, just because we use some function both from reader and from updater. Signed-off-by: Julian Anastasov <ja@ssi.bg> Signed-off-by: Simon Horman <horms@verge.net.au>
Diffstat (limited to 'net/netfilter')
-rw-r--r--net/netfilter/ipvs/ip_vs_conn.c8
-rw-r--r--net/netfilter/ipvs/ip_vs_ctl.c32
-rw-r--r--net/netfilter/ipvs/ip_vs_sync.c11
3 files changed, 26 insertions, 25 deletions
diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c
index 1b29e4a2b26c..54de34077b63 100644
--- a/net/netfilter/ipvs/ip_vs_conn.c
+++ b/net/netfilter/ipvs/ip_vs_conn.c
@@ -611,10 +611,11 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
611 * Check if there is a destination for the connection, if so 611 * Check if there is a destination for the connection, if so
612 * bind the connection to the destination. 612 * bind the connection to the destination.
613 */ 613 */
614struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp) 614void ip_vs_try_bind_dest(struct ip_vs_conn *cp)
615{ 615{
616 struct ip_vs_dest *dest; 616 struct ip_vs_dest *dest;
617 617
618 rcu_read_lock();
618 dest = ip_vs_find_dest(ip_vs_conn_net(cp), cp->af, &cp->daddr, 619 dest = ip_vs_find_dest(ip_vs_conn_net(cp), cp->af, &cp->daddr,
619 cp->dport, &cp->vaddr, cp->vport, 620 cp->dport, &cp->vaddr, cp->vport,
620 cp->protocol, cp->fwmark, cp->flags); 621 cp->protocol, cp->fwmark, cp->flags);
@@ -624,7 +625,8 @@ struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp)
624 spin_lock(&cp->lock); 625 spin_lock(&cp->lock);
625 if (cp->dest) { 626 if (cp->dest) {
626 spin_unlock(&cp->lock); 627 spin_unlock(&cp->lock);
627 return dest; 628 rcu_read_unlock();
629 return;
628 } 630 }
629 631
630 /* Applications work depending on the forwarding method 632 /* Applications work depending on the forwarding method
@@ -648,7 +650,7 @@ struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp)
648 if (pd && atomic_read(&pd->appcnt)) 650 if (pd && atomic_read(&pd->appcnt))
649 ip_vs_bind_app(cp, pd->pp); 651 ip_vs_bind_app(cp, pd->pp);
650 } 652 }
651 return dest; 653 rcu_read_unlock();
652} 654}
653 655
654 656
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 2bfd807bc93f..0763cc6e092b 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -565,8 +565,8 @@ bool ip_vs_has_real_service(struct net *net, int af, __u16 protocol,
565 return false; 565 return false;
566} 566}
567 567
568/* 568/* Lookup destination by {addr,port} in the given service
569 * Lookup destination by {addr,port} in the given service 569 * Called under RCU lock.
570 */ 570 */
571static struct ip_vs_dest * 571static struct ip_vs_dest *
572ip_vs_lookup_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr, 572ip_vs_lookup_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr,
@@ -577,7 +577,7 @@ ip_vs_lookup_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr,
577 /* 577 /*
578 * Find the destination for the given service 578 * Find the destination for the given service
579 */ 579 */
580 list_for_each_entry(dest, &svc->destinations, n_list) { 580 list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
581 if ((dest->af == svc->af) 581 if ((dest->af == svc->af)
582 && ip_vs_addr_equal(svc->af, &dest->addr, daddr) 582 && ip_vs_addr_equal(svc->af, &dest->addr, daddr)
583 && (dest->port == dport)) { 583 && (dest->port == dport)) {
@@ -591,10 +591,11 @@ ip_vs_lookup_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr,
591 591
592/* 592/*
593 * Find destination by {daddr,dport,vaddr,protocol} 593 * Find destination by {daddr,dport,vaddr,protocol}
594 * Cretaed to be used in ip_vs_process_message() in 594 * Created to be used in ip_vs_process_message() in
595 * the backup synchronization daemon. It finds the 595 * the backup synchronization daemon. It finds the
596 * destination to be bound to the received connection 596 * destination to be bound to the received connection
597 * on the backup. 597 * on the backup.
598 * Called under RCU lock, no refcnt is returned.
598 */ 599 */
599struct ip_vs_dest *ip_vs_find_dest(struct net *net, int af, 600struct ip_vs_dest *ip_vs_find_dest(struct net *net, int af,
600 const union nf_inet_addr *daddr, 601 const union nf_inet_addr *daddr,
@@ -615,8 +616,6 @@ struct ip_vs_dest *ip_vs_find_dest(struct net *net, int af,
615 dest = ip_vs_lookup_dest(svc, daddr, port); 616 dest = ip_vs_lookup_dest(svc, daddr, port);
616 if (!dest) 617 if (!dest)
617 dest = ip_vs_lookup_dest(svc, daddr, port ^ dport); 618 dest = ip_vs_lookup_dest(svc, daddr, port ^ dport);
618 if (dest)
619 ip_vs_dest_hold(dest);
620 ip_vs_service_put(svc); 619 ip_vs_service_put(svc);
621 return dest; 620 return dest;
622} 621}
@@ -826,7 +825,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest,
826 IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0); 825 IP_VS_WAIT_WHILE(atomic_read(&svc->usecnt) > 0);
827 826
828 if (add) { 827 if (add) {
829 list_add(&dest->n_list, &svc->destinations); 828 list_add_rcu(&dest->n_list, &svc->destinations);
830 svc->num_dests++; 829 svc->num_dests++;
831 if (svc->scheduler->add_dest) 830 if (svc->scheduler->add_dest)
832 svc->scheduler->add_dest(svc, dest); 831 svc->scheduler->add_dest(svc, dest);
@@ -933,10 +932,10 @@ ip_vs_add_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
933 932
934 ip_vs_addr_copy(svc->af, &daddr, &udest->addr); 933 ip_vs_addr_copy(svc->af, &daddr, &udest->addr);
935 934
936 /* 935 /* We use function that requires RCU lock */
937 * Check if the dest already exists in the list 936 rcu_read_lock();
938 */
939 dest = ip_vs_lookup_dest(svc, &daddr, dport); 937 dest = ip_vs_lookup_dest(svc, &daddr, dport);
938 rcu_read_unlock();
940 939
941 if (dest != NULL) { 940 if (dest != NULL) {
942 IP_VS_DBG(1, "%s(): dest already exists\n", __func__); 941 IP_VS_DBG(1, "%s(): dest already exists\n", __func__);
@@ -997,10 +996,10 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
997 996
998 ip_vs_addr_copy(svc->af, &daddr, &udest->addr); 997 ip_vs_addr_copy(svc->af, &daddr, &udest->addr);
999 998
1000 /* 999 /* We use function that requires RCU lock */
1001 * Lookup the destination list 1000 rcu_read_lock();
1002 */
1003 dest = ip_vs_lookup_dest(svc, &daddr, dport); 1001 dest = ip_vs_lookup_dest(svc, &daddr, dport);
1002 rcu_read_unlock();
1004 1003
1005 if (dest == NULL) { 1004 if (dest == NULL) {
1006 IP_VS_DBG(1, "%s(): dest doesn't exist\n", __func__); 1005 IP_VS_DBG(1, "%s(): dest doesn't exist\n", __func__);
@@ -1069,7 +1068,7 @@ static void __ip_vs_unlink_dest(struct ip_vs_service *svc,
1069 /* 1068 /*
1070 * Remove it from the d-linked destination list. 1069 * Remove it from the d-linked destination list.
1071 */ 1070 */
1072 list_del(&dest->n_list); 1071 list_del_rcu(&dest->n_list);
1073 svc->num_dests--; 1072 svc->num_dests--;
1074 1073
1075 if (svcupd && svc->scheduler->del_dest) 1074 if (svcupd && svc->scheduler->del_dest)
@@ -1094,7 +1093,10 @@ ip_vs_del_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest)
1094 1093
1095 EnterFunction(2); 1094 EnterFunction(2);
1096 1095
1096 /* We use function that requires RCU lock */
1097 rcu_read_lock();
1097 dest = ip_vs_lookup_dest(svc, &udest->addr, dport); 1098 dest = ip_vs_lookup_dest(svc, &udest->addr, dport);
1099 rcu_read_unlock();
1098 1100
1099 if (dest == NULL) { 1101 if (dest == NULL) {
1100 IP_VS_DBG(1, "%s(): destination not found!\n", __func__); 1102 IP_VS_DBG(1, "%s(): destination not found!\n", __func__);
@@ -2104,7 +2106,7 @@ static int ip_vs_info_seq_show(struct seq_file *seq, void *v)
2104 else 2106 else
2105 seq_putc(seq, '\n'); 2107 seq_putc(seq, '\n');
2106 2108
2107 list_for_each_entry(dest, &svc->destinations, n_list) { 2109 list_for_each_entry_rcu(dest, &svc->destinations, n_list) {
2108#ifdef CONFIG_IP_VS_IPV6 2110#ifdef CONFIG_IP_VS_IPV6
2109 if (dest->af == AF_INET6) 2111 if (dest->af == AF_INET6)
2110 seq_printf(seq, 2112 seq_printf(seq,
diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
index 6cc3e52f1f35..97241749216d 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -858,23 +858,20 @@ static void ip_vs_proc_conn(struct net *net, struct ip_vs_conn_param *param,
858 flags |= cp->flags & ~IP_VS_CONN_F_BACKUP_UPD_MASK; 858 flags |= cp->flags & ~IP_VS_CONN_F_BACKUP_UPD_MASK;
859 cp->flags = flags; 859 cp->flags = flags;
860 spin_unlock(&cp->lock); 860 spin_unlock(&cp->lock);
861 if (!dest) { 861 if (!dest)
862 dest = ip_vs_try_bind_dest(cp); 862 ip_vs_try_bind_dest(cp);
863 if (dest)
864 ip_vs_dest_put(dest);
865 }
866 } else { 863 } else {
867 /* 864 /*
868 * Find the appropriate destination for the connection. 865 * Find the appropriate destination for the connection.
869 * If it is not found the connection will remain unbound 866 * If it is not found the connection will remain unbound
870 * but still handled. 867 * but still handled.
871 */ 868 */
869 rcu_read_lock();
872 dest = ip_vs_find_dest(net, type, daddr, dport, param->vaddr, 870 dest = ip_vs_find_dest(net, type, daddr, dport, param->vaddr,
873 param->vport, protocol, fwmark, flags); 871 param->vport, protocol, fwmark, flags);
874 872
875 cp = ip_vs_conn_new(param, daddr, dport, flags, dest, fwmark); 873 cp = ip_vs_conn_new(param, daddr, dport, flags, dest, fwmark);
876 if (dest) 874 rcu_read_unlock();
877 ip_vs_dest_put(dest);
878 if (!cp) { 875 if (!cp) {
879 if (param->pe_data) 876 if (param->pe_data)
880 kfree(param->pe_data); 877 kfree(param->pe_data);