diff options
author | Julian Anastasov <ja@ssi.bg> | 2013-03-22 05:46:52 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2013-04-01 18:23:57 -0400 |
commit | 413c2d04e9494ca38629d8a7ffeff1e4398a9fe3 (patch) | |
tree | 84a0ce4e93f5855c1a9f3f010b4f364917581da1 /net/netfilter | |
parent | ba3a3ce14ea26d602b253ef13a56d540827cd51d (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.c | 8 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_ctl.c | 32 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_sync.c | 11 |
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 | */ |
614 | struct ip_vs_dest *ip_vs_try_bind_dest(struct ip_vs_conn *cp) | 614 | void 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 | */ |
571 | static struct ip_vs_dest * | 571 | static struct ip_vs_dest * |
572 | ip_vs_lookup_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr, | 572 | ip_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 | */ |
599 | struct ip_vs_dest *ip_vs_find_dest(struct net *net, int af, | 600 | struct 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); |