diff options
author | Gleb Natapov <gleb@redhat.com> | 2013-10-17 10:04:47 -0400 |
---|---|---|
committer | Gleb Natapov <gleb@redhat.com> | 2013-10-17 10:41:49 -0400 |
commit | 13acfd5715144518232d8e29acf7c76ef1b089d8 (patch) | |
tree | 093c4311a737210bbdd3d1919f8193cb5f017241 /net/netfilter | |
parent | d570142674890fe10b3d7d86aa105e3dfce1ddfa (diff) | |
parent | 34ec4de42be5006abdd8d0c08b306ffaa64d0d5d (diff) |
Powerpc KVM work is based on a commit after rc4.
Merging master into next to satisfy the dependencies.
Conflicts:
arch/arm/kvm/reset.c
Diffstat (limited to 'net/netfilter')
-rw-r--r-- | net/netfilter/ipvs/ip_vs_core.c | 12 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_ctl.c | 86 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_est.c | 4 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_lblc.c | 72 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_lblcr.c | 62 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_nq.c | 8 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_sed.c | 8 | ||||
-rw-r--r-- | net/netfilter/ipvs/ip_vs_wlc.c | 6 | ||||
-rw-r--r-- | net/netfilter/nf_synproxy_core.c | 12 |
9 files changed, 125 insertions, 145 deletions
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 4f69e83ff836..74fd00c27210 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c | |||
@@ -116,6 +116,7 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb) | |||
116 | 116 | ||
117 | if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { | 117 | if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { |
118 | struct ip_vs_cpu_stats *s; | 118 | struct ip_vs_cpu_stats *s; |
119 | struct ip_vs_service *svc; | ||
119 | 120 | ||
120 | s = this_cpu_ptr(dest->stats.cpustats); | 121 | s = this_cpu_ptr(dest->stats.cpustats); |
121 | s->ustats.inpkts++; | 122 | s->ustats.inpkts++; |
@@ -123,11 +124,14 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb) | |||
123 | s->ustats.inbytes += skb->len; | 124 | s->ustats.inbytes += skb->len; |
124 | u64_stats_update_end(&s->syncp); | 125 | u64_stats_update_end(&s->syncp); |
125 | 126 | ||
126 | s = this_cpu_ptr(dest->svc->stats.cpustats); | 127 | rcu_read_lock(); |
128 | svc = rcu_dereference(dest->svc); | ||
129 | s = this_cpu_ptr(svc->stats.cpustats); | ||
127 | s->ustats.inpkts++; | 130 | s->ustats.inpkts++; |
128 | u64_stats_update_begin(&s->syncp); | 131 | u64_stats_update_begin(&s->syncp); |
129 | s->ustats.inbytes += skb->len; | 132 | s->ustats.inbytes += skb->len; |
130 | u64_stats_update_end(&s->syncp); | 133 | u64_stats_update_end(&s->syncp); |
134 | rcu_read_unlock(); | ||
131 | 135 | ||
132 | s = this_cpu_ptr(ipvs->tot_stats.cpustats); | 136 | s = this_cpu_ptr(ipvs->tot_stats.cpustats); |
133 | s->ustats.inpkts++; | 137 | s->ustats.inpkts++; |
@@ -146,6 +150,7 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb) | |||
146 | 150 | ||
147 | if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { | 151 | if (dest && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { |
148 | struct ip_vs_cpu_stats *s; | 152 | struct ip_vs_cpu_stats *s; |
153 | struct ip_vs_service *svc; | ||
149 | 154 | ||
150 | s = this_cpu_ptr(dest->stats.cpustats); | 155 | s = this_cpu_ptr(dest->stats.cpustats); |
151 | s->ustats.outpkts++; | 156 | s->ustats.outpkts++; |
@@ -153,11 +158,14 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb) | |||
153 | s->ustats.outbytes += skb->len; | 158 | s->ustats.outbytes += skb->len; |
154 | u64_stats_update_end(&s->syncp); | 159 | u64_stats_update_end(&s->syncp); |
155 | 160 | ||
156 | s = this_cpu_ptr(dest->svc->stats.cpustats); | 161 | rcu_read_lock(); |
162 | svc = rcu_dereference(dest->svc); | ||
163 | s = this_cpu_ptr(svc->stats.cpustats); | ||
157 | s->ustats.outpkts++; | 164 | s->ustats.outpkts++; |
158 | u64_stats_update_begin(&s->syncp); | 165 | u64_stats_update_begin(&s->syncp); |
159 | s->ustats.outbytes += skb->len; | 166 | s->ustats.outbytes += skb->len; |
160 | u64_stats_update_end(&s->syncp); | 167 | u64_stats_update_end(&s->syncp); |
168 | rcu_read_unlock(); | ||
161 | 169 | ||
162 | s = this_cpu_ptr(ipvs->tot_stats.cpustats); | 170 | s = this_cpu_ptr(ipvs->tot_stats.cpustats); |
163 | s->ustats.outpkts++; | 171 | s->ustats.outpkts++; |
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index c8148e487386..a3df9bddc4f7 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c | |||
@@ -460,7 +460,7 @@ static inline void | |||
460 | __ip_vs_bind_svc(struct ip_vs_dest *dest, struct ip_vs_service *svc) | 460 | __ip_vs_bind_svc(struct ip_vs_dest *dest, struct ip_vs_service *svc) |
461 | { | 461 | { |
462 | atomic_inc(&svc->refcnt); | 462 | atomic_inc(&svc->refcnt); |
463 | dest->svc = svc; | 463 | rcu_assign_pointer(dest->svc, svc); |
464 | } | 464 | } |
465 | 465 | ||
466 | static void ip_vs_service_free(struct ip_vs_service *svc) | 466 | static void ip_vs_service_free(struct ip_vs_service *svc) |
@@ -470,18 +470,25 @@ static void ip_vs_service_free(struct ip_vs_service *svc) | |||
470 | kfree(svc); | 470 | kfree(svc); |
471 | } | 471 | } |
472 | 472 | ||
473 | static void | 473 | static void ip_vs_service_rcu_free(struct rcu_head *head) |
474 | __ip_vs_unbind_svc(struct ip_vs_dest *dest) | ||
475 | { | 474 | { |
476 | struct ip_vs_service *svc = dest->svc; | 475 | struct ip_vs_service *svc; |
476 | |||
477 | svc = container_of(head, struct ip_vs_service, rcu_head); | ||
478 | ip_vs_service_free(svc); | ||
479 | } | ||
477 | 480 | ||
478 | dest->svc = NULL; | 481 | static void __ip_vs_svc_put(struct ip_vs_service *svc, bool do_delay) |
482 | { | ||
479 | if (atomic_dec_and_test(&svc->refcnt)) { | 483 | if (atomic_dec_and_test(&svc->refcnt)) { |
480 | IP_VS_DBG_BUF(3, "Removing service %u/%s:%u\n", | 484 | IP_VS_DBG_BUF(3, "Removing service %u/%s:%u\n", |
481 | svc->fwmark, | 485 | svc->fwmark, |
482 | IP_VS_DBG_ADDR(svc->af, &svc->addr), | 486 | IP_VS_DBG_ADDR(svc->af, &svc->addr), |
483 | ntohs(svc->port)); | 487 | ntohs(svc->port)); |
484 | ip_vs_service_free(svc); | 488 | if (do_delay) |
489 | call_rcu(&svc->rcu_head, ip_vs_service_rcu_free); | ||
490 | else | ||
491 | ip_vs_service_free(svc); | ||
485 | } | 492 | } |
486 | } | 493 | } |
487 | 494 | ||
@@ -667,11 +674,6 @@ ip_vs_trash_get_dest(struct ip_vs_service *svc, const union nf_inet_addr *daddr, | |||
667 | IP_VS_DBG_ADDR(svc->af, &dest->addr), | 674 | IP_VS_DBG_ADDR(svc->af, &dest->addr), |
668 | ntohs(dest->port), | 675 | ntohs(dest->port), |
669 | atomic_read(&dest->refcnt)); | 676 | atomic_read(&dest->refcnt)); |
670 | /* We can not reuse dest while in grace period | ||
671 | * because conns still can use dest->svc | ||
672 | */ | ||
673 | if (test_bit(IP_VS_DEST_STATE_REMOVING, &dest->state)) | ||
674 | continue; | ||
675 | if (dest->af == svc->af && | 677 | if (dest->af == svc->af && |
676 | ip_vs_addr_equal(svc->af, &dest->addr, daddr) && | 678 | ip_vs_addr_equal(svc->af, &dest->addr, daddr) && |
677 | dest->port == dport && | 679 | dest->port == dport && |
@@ -697,8 +699,10 @@ out: | |||
697 | 699 | ||
698 | static void ip_vs_dest_free(struct ip_vs_dest *dest) | 700 | static void ip_vs_dest_free(struct ip_vs_dest *dest) |
699 | { | 701 | { |
702 | struct ip_vs_service *svc = rcu_dereference_protected(dest->svc, 1); | ||
703 | |||
700 | __ip_vs_dst_cache_reset(dest); | 704 | __ip_vs_dst_cache_reset(dest); |
701 | __ip_vs_unbind_svc(dest); | 705 | __ip_vs_svc_put(svc, false); |
702 | free_percpu(dest->stats.cpustats); | 706 | free_percpu(dest->stats.cpustats); |
703 | kfree(dest); | 707 | kfree(dest); |
704 | } | 708 | } |
@@ -771,6 +775,7 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, | |||
771 | struct ip_vs_dest_user_kern *udest, int add) | 775 | struct ip_vs_dest_user_kern *udest, int add) |
772 | { | 776 | { |
773 | struct netns_ipvs *ipvs = net_ipvs(svc->net); | 777 | struct netns_ipvs *ipvs = net_ipvs(svc->net); |
778 | struct ip_vs_service *old_svc; | ||
774 | struct ip_vs_scheduler *sched; | 779 | struct ip_vs_scheduler *sched; |
775 | int conn_flags; | 780 | int conn_flags; |
776 | 781 | ||
@@ -792,13 +797,14 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, | |||
792 | atomic_set(&dest->conn_flags, conn_flags); | 797 | atomic_set(&dest->conn_flags, conn_flags); |
793 | 798 | ||
794 | /* bind the service */ | 799 | /* bind the service */ |
795 | if (!dest->svc) { | 800 | old_svc = rcu_dereference_protected(dest->svc, 1); |
801 | if (!old_svc) { | ||
796 | __ip_vs_bind_svc(dest, svc); | 802 | __ip_vs_bind_svc(dest, svc); |
797 | } else { | 803 | } else { |
798 | if (dest->svc != svc) { | 804 | if (old_svc != svc) { |
799 | __ip_vs_unbind_svc(dest); | ||
800 | ip_vs_zero_stats(&dest->stats); | 805 | ip_vs_zero_stats(&dest->stats); |
801 | __ip_vs_bind_svc(dest, svc); | 806 | __ip_vs_bind_svc(dest, svc); |
807 | __ip_vs_svc_put(old_svc, true); | ||
802 | } | 808 | } |
803 | } | 809 | } |
804 | 810 | ||
@@ -998,16 +1004,6 @@ ip_vs_edit_dest(struct ip_vs_service *svc, struct ip_vs_dest_user_kern *udest) | |||
998 | return 0; | 1004 | return 0; |
999 | } | 1005 | } |
1000 | 1006 | ||
1001 | static void ip_vs_dest_wait_readers(struct rcu_head *head) | ||
1002 | { | ||
1003 | struct ip_vs_dest *dest = container_of(head, struct ip_vs_dest, | ||
1004 | rcu_head); | ||
1005 | |||
1006 | /* End of grace period after unlinking */ | ||
1007 | clear_bit(IP_VS_DEST_STATE_REMOVING, &dest->state); | ||
1008 | } | ||
1009 | |||
1010 | |||
1011 | /* | 1007 | /* |
1012 | * Delete a destination (must be already unlinked from the service) | 1008 | * Delete a destination (must be already unlinked from the service) |
1013 | */ | 1009 | */ |
@@ -1023,20 +1019,16 @@ static void __ip_vs_del_dest(struct net *net, struct ip_vs_dest *dest, | |||
1023 | */ | 1019 | */ |
1024 | ip_vs_rs_unhash(dest); | 1020 | ip_vs_rs_unhash(dest); |
1025 | 1021 | ||
1026 | if (!cleanup) { | ||
1027 | set_bit(IP_VS_DEST_STATE_REMOVING, &dest->state); | ||
1028 | call_rcu(&dest->rcu_head, ip_vs_dest_wait_readers); | ||
1029 | } | ||
1030 | |||
1031 | spin_lock_bh(&ipvs->dest_trash_lock); | 1022 | spin_lock_bh(&ipvs->dest_trash_lock); |
1032 | IP_VS_DBG_BUF(3, "Moving dest %s:%u into trash, dest->refcnt=%d\n", | 1023 | IP_VS_DBG_BUF(3, "Moving dest %s:%u into trash, dest->refcnt=%d\n", |
1033 | IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port), | 1024 | IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port), |
1034 | atomic_read(&dest->refcnt)); | 1025 | atomic_read(&dest->refcnt)); |
1035 | if (list_empty(&ipvs->dest_trash) && !cleanup) | 1026 | if (list_empty(&ipvs->dest_trash) && !cleanup) |
1036 | mod_timer(&ipvs->dest_trash_timer, | 1027 | mod_timer(&ipvs->dest_trash_timer, |
1037 | jiffies + IP_VS_DEST_TRASH_PERIOD); | 1028 | jiffies + (IP_VS_DEST_TRASH_PERIOD >> 1)); |
1038 | /* dest lives in trash without reference */ | 1029 | /* dest lives in trash without reference */ |
1039 | list_add(&dest->t_list, &ipvs->dest_trash); | 1030 | list_add(&dest->t_list, &ipvs->dest_trash); |
1031 | dest->idle_start = 0; | ||
1040 | spin_unlock_bh(&ipvs->dest_trash_lock); | 1032 | spin_unlock_bh(&ipvs->dest_trash_lock); |
1041 | ip_vs_dest_put(dest); | 1033 | ip_vs_dest_put(dest); |
1042 | } | 1034 | } |
@@ -1108,24 +1100,30 @@ static void ip_vs_dest_trash_expire(unsigned long data) | |||
1108 | struct net *net = (struct net *) data; | 1100 | struct net *net = (struct net *) data; |
1109 | struct netns_ipvs *ipvs = net_ipvs(net); | 1101 | struct netns_ipvs *ipvs = net_ipvs(net); |
1110 | struct ip_vs_dest *dest, *next; | 1102 | struct ip_vs_dest *dest, *next; |
1103 | unsigned long now = jiffies; | ||
1111 | 1104 | ||
1112 | spin_lock(&ipvs->dest_trash_lock); | 1105 | spin_lock(&ipvs->dest_trash_lock); |
1113 | list_for_each_entry_safe(dest, next, &ipvs->dest_trash, t_list) { | 1106 | list_for_each_entry_safe(dest, next, &ipvs->dest_trash, t_list) { |
1114 | /* Skip if dest is in grace period */ | ||
1115 | if (test_bit(IP_VS_DEST_STATE_REMOVING, &dest->state)) | ||
1116 | continue; | ||
1117 | if (atomic_read(&dest->refcnt) > 0) | 1107 | if (atomic_read(&dest->refcnt) > 0) |
1118 | continue; | 1108 | continue; |
1109 | if (dest->idle_start) { | ||
1110 | if (time_before(now, dest->idle_start + | ||
1111 | IP_VS_DEST_TRASH_PERIOD)) | ||
1112 | continue; | ||
1113 | } else { | ||
1114 | dest->idle_start = max(1UL, now); | ||
1115 | continue; | ||
1116 | } | ||
1119 | IP_VS_DBG_BUF(3, "Removing destination %u/%s:%u from trash\n", | 1117 | IP_VS_DBG_BUF(3, "Removing destination %u/%s:%u from trash\n", |
1120 | dest->vfwmark, | 1118 | dest->vfwmark, |
1121 | IP_VS_DBG_ADDR(dest->svc->af, &dest->addr), | 1119 | IP_VS_DBG_ADDR(dest->af, &dest->addr), |
1122 | ntohs(dest->port)); | 1120 | ntohs(dest->port)); |
1123 | list_del(&dest->t_list); | 1121 | list_del(&dest->t_list); |
1124 | ip_vs_dest_free(dest); | 1122 | ip_vs_dest_free(dest); |
1125 | } | 1123 | } |
1126 | if (!list_empty(&ipvs->dest_trash)) | 1124 | if (!list_empty(&ipvs->dest_trash)) |
1127 | mod_timer(&ipvs->dest_trash_timer, | 1125 | mod_timer(&ipvs->dest_trash_timer, |
1128 | jiffies + IP_VS_DEST_TRASH_PERIOD); | 1126 | jiffies + (IP_VS_DEST_TRASH_PERIOD >> 1)); |
1129 | spin_unlock(&ipvs->dest_trash_lock); | 1127 | spin_unlock(&ipvs->dest_trash_lock); |
1130 | } | 1128 | } |
1131 | 1129 | ||
@@ -1320,14 +1318,6 @@ out: | |||
1320 | return ret; | 1318 | return ret; |
1321 | } | 1319 | } |
1322 | 1320 | ||
1323 | static void ip_vs_service_rcu_free(struct rcu_head *head) | ||
1324 | { | ||
1325 | struct ip_vs_service *svc; | ||
1326 | |||
1327 | svc = container_of(head, struct ip_vs_service, rcu_head); | ||
1328 | ip_vs_service_free(svc); | ||
1329 | } | ||
1330 | |||
1331 | /* | 1321 | /* |
1332 | * Delete a service from the service list | 1322 | * Delete a service from the service list |
1333 | * - The service must be unlinked, unlocked and not referenced! | 1323 | * - The service must be unlinked, unlocked and not referenced! |
@@ -1376,13 +1366,7 @@ static void __ip_vs_del_service(struct ip_vs_service *svc, bool cleanup) | |||
1376 | /* | 1366 | /* |
1377 | * Free the service if nobody refers to it | 1367 | * Free the service if nobody refers to it |
1378 | */ | 1368 | */ |
1379 | if (atomic_dec_and_test(&svc->refcnt)) { | 1369 | __ip_vs_svc_put(svc, true); |
1380 | IP_VS_DBG_BUF(3, "Removing service %u/%s:%u\n", | ||
1381 | svc->fwmark, | ||
1382 | IP_VS_DBG_ADDR(svc->af, &svc->addr), | ||
1383 | ntohs(svc->port)); | ||
1384 | call_rcu(&svc->rcu_head, ip_vs_service_rcu_free); | ||
1385 | } | ||
1386 | 1370 | ||
1387 | /* decrease the module use count */ | 1371 | /* decrease the module use count */ |
1388 | ip_vs_use_count_dec(); | 1372 | ip_vs_use_count_dec(); |
diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index 6bee6d0c73a5..1425e9a924c4 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c | |||
@@ -59,12 +59,13 @@ static void ip_vs_read_cpu_stats(struct ip_vs_stats_user *sum, | |||
59 | struct ip_vs_cpu_stats __percpu *stats) | 59 | struct ip_vs_cpu_stats __percpu *stats) |
60 | { | 60 | { |
61 | int i; | 61 | int i; |
62 | bool add = false; | ||
62 | 63 | ||
63 | for_each_possible_cpu(i) { | 64 | for_each_possible_cpu(i) { |
64 | struct ip_vs_cpu_stats *s = per_cpu_ptr(stats, i); | 65 | struct ip_vs_cpu_stats *s = per_cpu_ptr(stats, i); |
65 | unsigned int start; | 66 | unsigned int start; |
66 | __u64 inbytes, outbytes; | 67 | __u64 inbytes, outbytes; |
67 | if (i) { | 68 | if (add) { |
68 | sum->conns += s->ustats.conns; | 69 | sum->conns += s->ustats.conns; |
69 | sum->inpkts += s->ustats.inpkts; | 70 | sum->inpkts += s->ustats.inpkts; |
70 | sum->outpkts += s->ustats.outpkts; | 71 | sum->outpkts += s->ustats.outpkts; |
@@ -76,6 +77,7 @@ static void ip_vs_read_cpu_stats(struct ip_vs_stats_user *sum, | |||
76 | sum->inbytes += inbytes; | 77 | sum->inbytes += inbytes; |
77 | sum->outbytes += outbytes; | 78 | sum->outbytes += outbytes; |
78 | } else { | 79 | } else { |
80 | add = true; | ||
79 | sum->conns = s->ustats.conns; | 81 | sum->conns = s->ustats.conns; |
80 | sum->inpkts = s->ustats.inpkts; | 82 | sum->inpkts = s->ustats.inpkts; |
81 | sum->outpkts = s->ustats.outpkts; | 83 | sum->outpkts = s->ustats.outpkts; |
diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 1383b0eadc0e..eff13c94498e 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c | |||
@@ -93,7 +93,7 @@ struct ip_vs_lblc_entry { | |||
93 | struct hlist_node list; | 93 | struct hlist_node list; |
94 | int af; /* address family */ | 94 | int af; /* address family */ |
95 | union nf_inet_addr addr; /* destination IP address */ | 95 | union nf_inet_addr addr; /* destination IP address */ |
96 | struct ip_vs_dest __rcu *dest; /* real server (cache) */ | 96 | struct ip_vs_dest *dest; /* real server (cache) */ |
97 | unsigned long lastuse; /* last used time */ | 97 | unsigned long lastuse; /* last used time */ |
98 | struct rcu_head rcu_head; | 98 | struct rcu_head rcu_head; |
99 | }; | 99 | }; |
@@ -130,20 +130,21 @@ static struct ctl_table vs_vars_table[] = { | |||
130 | }; | 130 | }; |
131 | #endif | 131 | #endif |
132 | 132 | ||
133 | static inline void ip_vs_lblc_free(struct ip_vs_lblc_entry *en) | 133 | static void ip_vs_lblc_rcu_free(struct rcu_head *head) |
134 | { | 134 | { |
135 | struct ip_vs_dest *dest; | 135 | struct ip_vs_lblc_entry *en = container_of(head, |
136 | struct ip_vs_lblc_entry, | ||
137 | rcu_head); | ||
136 | 138 | ||
137 | hlist_del_rcu(&en->list); | 139 | ip_vs_dest_put(en->dest); |
138 | /* | 140 | kfree(en); |
139 | * We don't kfree dest because it is referred either by its service | ||
140 | * or the trash dest list. | ||
141 | */ | ||
142 | dest = rcu_dereference_protected(en->dest, 1); | ||
143 | ip_vs_dest_put(dest); | ||
144 | kfree_rcu(en, rcu_head); | ||
145 | } | 141 | } |
146 | 142 | ||
143 | static inline void ip_vs_lblc_del(struct ip_vs_lblc_entry *en) | ||
144 | { | ||
145 | hlist_del_rcu(&en->list); | ||
146 | call_rcu(&en->rcu_head, ip_vs_lblc_rcu_free); | ||
147 | } | ||
147 | 148 | ||
148 | /* | 149 | /* |
149 | * Returns hash value for IPVS LBLC entry | 150 | * Returns hash value for IPVS LBLC entry |
@@ -203,30 +204,23 @@ ip_vs_lblc_new(struct ip_vs_lblc_table *tbl, const union nf_inet_addr *daddr, | |||
203 | struct ip_vs_lblc_entry *en; | 204 | struct ip_vs_lblc_entry *en; |
204 | 205 | ||
205 | en = ip_vs_lblc_get(dest->af, tbl, daddr); | 206 | en = ip_vs_lblc_get(dest->af, tbl, daddr); |
206 | if (!en) { | 207 | if (en) { |
207 | en = kmalloc(sizeof(*en), GFP_ATOMIC); | 208 | if (en->dest == dest) |
208 | if (!en) | 209 | return en; |
209 | return NULL; | 210 | ip_vs_lblc_del(en); |
210 | 211 | } | |
211 | en->af = dest->af; | 212 | en = kmalloc(sizeof(*en), GFP_ATOMIC); |
212 | ip_vs_addr_copy(dest->af, &en->addr, daddr); | 213 | if (!en) |
213 | en->lastuse = jiffies; | 214 | return NULL; |
214 | 215 | ||
215 | ip_vs_dest_hold(dest); | 216 | en->af = dest->af; |
216 | RCU_INIT_POINTER(en->dest, dest); | 217 | ip_vs_addr_copy(dest->af, &en->addr, daddr); |
218 | en->lastuse = jiffies; | ||
217 | 219 | ||
218 | ip_vs_lblc_hash(tbl, en); | 220 | ip_vs_dest_hold(dest); |
219 | } else { | 221 | en->dest = dest; |
220 | struct ip_vs_dest *old_dest; | ||
221 | 222 | ||
222 | old_dest = rcu_dereference_protected(en->dest, 1); | 223 | ip_vs_lblc_hash(tbl, en); |
223 | if (old_dest != dest) { | ||
224 | ip_vs_dest_put(old_dest); | ||
225 | ip_vs_dest_hold(dest); | ||
226 | /* No ordering constraints for refcnt */ | ||
227 | RCU_INIT_POINTER(en->dest, dest); | ||
228 | } | ||
229 | } | ||
230 | 224 | ||
231 | return en; | 225 | return en; |
232 | } | 226 | } |
@@ -246,7 +240,7 @@ static void ip_vs_lblc_flush(struct ip_vs_service *svc) | |||
246 | tbl->dead = 1; | 240 | tbl->dead = 1; |
247 | for (i=0; i<IP_VS_LBLC_TAB_SIZE; i++) { | 241 | for (i=0; i<IP_VS_LBLC_TAB_SIZE; i++) { |
248 | hlist_for_each_entry_safe(en, next, &tbl->bucket[i], list) { | 242 | hlist_for_each_entry_safe(en, next, &tbl->bucket[i], list) { |
249 | ip_vs_lblc_free(en); | 243 | ip_vs_lblc_del(en); |
250 | atomic_dec(&tbl->entries); | 244 | atomic_dec(&tbl->entries); |
251 | } | 245 | } |
252 | } | 246 | } |
@@ -281,7 +275,7 @@ static inline void ip_vs_lblc_full_check(struct ip_vs_service *svc) | |||
281 | sysctl_lblc_expiration(svc))) | 275 | sysctl_lblc_expiration(svc))) |
282 | continue; | 276 | continue; |
283 | 277 | ||
284 | ip_vs_lblc_free(en); | 278 | ip_vs_lblc_del(en); |
285 | atomic_dec(&tbl->entries); | 279 | atomic_dec(&tbl->entries); |
286 | } | 280 | } |
287 | spin_unlock(&svc->sched_lock); | 281 | spin_unlock(&svc->sched_lock); |
@@ -335,7 +329,7 @@ static void ip_vs_lblc_check_expire(unsigned long data) | |||
335 | if (time_before(now, en->lastuse + ENTRY_TIMEOUT)) | 329 | if (time_before(now, en->lastuse + ENTRY_TIMEOUT)) |
336 | continue; | 330 | continue; |
337 | 331 | ||
338 | ip_vs_lblc_free(en); | 332 | ip_vs_lblc_del(en); |
339 | atomic_dec(&tbl->entries); | 333 | atomic_dec(&tbl->entries); |
340 | goal--; | 334 | goal--; |
341 | } | 335 | } |
@@ -443,8 +437,8 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc) | |||
443 | continue; | 437 | continue; |
444 | 438 | ||
445 | doh = ip_vs_dest_conn_overhead(dest); | 439 | doh = ip_vs_dest_conn_overhead(dest); |
446 | if (loh * atomic_read(&dest->weight) > | 440 | if ((__s64)loh * atomic_read(&dest->weight) > |
447 | doh * atomic_read(&least->weight)) { | 441 | (__s64)doh * atomic_read(&least->weight)) { |
448 | least = dest; | 442 | least = dest; |
449 | loh = doh; | 443 | loh = doh; |
450 | } | 444 | } |
@@ -511,7 +505,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, | |||
511 | * free up entries from the trash at any time. | 505 | * free up entries from the trash at any time. |
512 | */ | 506 | */ |
513 | 507 | ||
514 | dest = rcu_dereference(en->dest); | 508 | dest = en->dest; |
515 | if ((dest->flags & IP_VS_DEST_F_AVAILABLE) && | 509 | if ((dest->flags & IP_VS_DEST_F_AVAILABLE) && |
516 | atomic_read(&dest->weight) > 0 && !is_overloaded(dest, svc)) | 510 | atomic_read(&dest->weight) > 0 && !is_overloaded(dest, svc)) |
517 | goto out; | 511 | goto out; |
@@ -631,7 +625,7 @@ static void __exit ip_vs_lblc_cleanup(void) | |||
631 | { | 625 | { |
632 | unregister_ip_vs_scheduler(&ip_vs_lblc_scheduler); | 626 | unregister_ip_vs_scheduler(&ip_vs_lblc_scheduler); |
633 | unregister_pernet_subsys(&ip_vs_lblc_ops); | 627 | unregister_pernet_subsys(&ip_vs_lblc_ops); |
634 | synchronize_rcu(); | 628 | rcu_barrier(); |
635 | } | 629 | } |
636 | 630 | ||
637 | 631 | ||
diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index 5199448697f6..0b8550089a2e 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c | |||
@@ -89,7 +89,7 @@ | |||
89 | */ | 89 | */ |
90 | struct ip_vs_dest_set_elem { | 90 | struct ip_vs_dest_set_elem { |
91 | struct list_head list; /* list link */ | 91 | struct list_head list; /* list link */ |
92 | struct ip_vs_dest __rcu *dest; /* destination server */ | 92 | struct ip_vs_dest *dest; /* destination server */ |
93 | struct rcu_head rcu_head; | 93 | struct rcu_head rcu_head; |
94 | }; | 94 | }; |
95 | 95 | ||
@@ -107,11 +107,7 @@ static void ip_vs_dest_set_insert(struct ip_vs_dest_set *set, | |||
107 | 107 | ||
108 | if (check) { | 108 | if (check) { |
109 | list_for_each_entry(e, &set->list, list) { | 109 | list_for_each_entry(e, &set->list, list) { |
110 | struct ip_vs_dest *d; | 110 | if (e->dest == dest) |
111 | |||
112 | d = rcu_dereference_protected(e->dest, 1); | ||
113 | if (d == dest) | ||
114 | /* already existed */ | ||
115 | return; | 111 | return; |
116 | } | 112 | } |
117 | } | 113 | } |
@@ -121,7 +117,7 @@ static void ip_vs_dest_set_insert(struct ip_vs_dest_set *set, | |||
121 | return; | 117 | return; |
122 | 118 | ||
123 | ip_vs_dest_hold(dest); | 119 | ip_vs_dest_hold(dest); |
124 | RCU_INIT_POINTER(e->dest, dest); | 120 | e->dest = dest; |
125 | 121 | ||
126 | list_add_rcu(&e->list, &set->list); | 122 | list_add_rcu(&e->list, &set->list); |
127 | atomic_inc(&set->size); | 123 | atomic_inc(&set->size); |
@@ -129,22 +125,27 @@ static void ip_vs_dest_set_insert(struct ip_vs_dest_set *set, | |||
129 | set->lastmod = jiffies; | 125 | set->lastmod = jiffies; |
130 | } | 126 | } |
131 | 127 | ||
128 | static void ip_vs_lblcr_elem_rcu_free(struct rcu_head *head) | ||
129 | { | ||
130 | struct ip_vs_dest_set_elem *e; | ||
131 | |||
132 | e = container_of(head, struct ip_vs_dest_set_elem, rcu_head); | ||
133 | ip_vs_dest_put(e->dest); | ||
134 | kfree(e); | ||
135 | } | ||
136 | |||
132 | static void | 137 | static void |
133 | ip_vs_dest_set_erase(struct ip_vs_dest_set *set, struct ip_vs_dest *dest) | 138 | ip_vs_dest_set_erase(struct ip_vs_dest_set *set, struct ip_vs_dest *dest) |
134 | { | 139 | { |
135 | struct ip_vs_dest_set_elem *e; | 140 | struct ip_vs_dest_set_elem *e; |
136 | 141 | ||
137 | list_for_each_entry(e, &set->list, list) { | 142 | list_for_each_entry(e, &set->list, list) { |
138 | struct ip_vs_dest *d; | 143 | if (e->dest == dest) { |
139 | |||
140 | d = rcu_dereference_protected(e->dest, 1); | ||
141 | if (d == dest) { | ||
142 | /* HIT */ | 144 | /* HIT */ |
143 | atomic_dec(&set->size); | 145 | atomic_dec(&set->size); |
144 | set->lastmod = jiffies; | 146 | set->lastmod = jiffies; |
145 | ip_vs_dest_put(dest); | ||
146 | list_del_rcu(&e->list); | 147 | list_del_rcu(&e->list); |
147 | kfree_rcu(e, rcu_head); | 148 | call_rcu(&e->rcu_head, ip_vs_lblcr_elem_rcu_free); |
148 | break; | 149 | break; |
149 | } | 150 | } |
150 | } | 151 | } |
@@ -155,16 +156,8 @@ static void ip_vs_dest_set_eraseall(struct ip_vs_dest_set *set) | |||
155 | struct ip_vs_dest_set_elem *e, *ep; | 156 | struct ip_vs_dest_set_elem *e, *ep; |
156 | 157 | ||
157 | list_for_each_entry_safe(e, ep, &set->list, list) { | 158 | list_for_each_entry_safe(e, ep, &set->list, list) { |
158 | struct ip_vs_dest *d; | ||
159 | |||
160 | d = rcu_dereference_protected(e->dest, 1); | ||
161 | /* | ||
162 | * We don't kfree dest because it is referred either | ||
163 | * by its service or by the trash dest list. | ||
164 | */ | ||
165 | ip_vs_dest_put(d); | ||
166 | list_del_rcu(&e->list); | 159 | list_del_rcu(&e->list); |
167 | kfree_rcu(e, rcu_head); | 160 | call_rcu(&e->rcu_head, ip_vs_lblcr_elem_rcu_free); |
168 | } | 161 | } |
169 | } | 162 | } |
170 | 163 | ||
@@ -175,12 +168,9 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) | |||
175 | struct ip_vs_dest *dest, *least; | 168 | struct ip_vs_dest *dest, *least; |
176 | int loh, doh; | 169 | int loh, doh; |
177 | 170 | ||
178 | if (set == NULL) | ||
179 | return NULL; | ||
180 | |||
181 | /* select the first destination server, whose weight > 0 */ | 171 | /* select the first destination server, whose weight > 0 */ |
182 | list_for_each_entry_rcu(e, &set->list, list) { | 172 | list_for_each_entry_rcu(e, &set->list, list) { |
183 | least = rcu_dereference(e->dest); | 173 | least = e->dest; |
184 | if (least->flags & IP_VS_DEST_F_OVERLOAD) | 174 | if (least->flags & IP_VS_DEST_F_OVERLOAD) |
185 | continue; | 175 | continue; |
186 | 176 | ||
@@ -195,13 +185,13 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) | |||
195 | /* find the destination with the weighted least load */ | 185 | /* find the destination with the weighted least load */ |
196 | nextstage: | 186 | nextstage: |
197 | list_for_each_entry_continue_rcu(e, &set->list, list) { | 187 | list_for_each_entry_continue_rcu(e, &set->list, list) { |
198 | dest = rcu_dereference(e->dest); | 188 | dest = e->dest; |
199 | if (dest->flags & IP_VS_DEST_F_OVERLOAD) | 189 | if (dest->flags & IP_VS_DEST_F_OVERLOAD) |
200 | continue; | 190 | continue; |
201 | 191 | ||
202 | doh = ip_vs_dest_conn_overhead(dest); | 192 | doh = ip_vs_dest_conn_overhead(dest); |
203 | if ((loh * atomic_read(&dest->weight) > | 193 | if (((__s64)loh * atomic_read(&dest->weight) > |
204 | doh * atomic_read(&least->weight)) | 194 | (__s64)doh * atomic_read(&least->weight)) |
205 | && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { | 195 | && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { |
206 | least = dest; | 196 | least = dest; |
207 | loh = doh; | 197 | loh = doh; |
@@ -232,7 +222,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set) | |||
232 | 222 | ||
233 | /* select the first destination server, whose weight > 0 */ | 223 | /* select the first destination server, whose weight > 0 */ |
234 | list_for_each_entry(e, &set->list, list) { | 224 | list_for_each_entry(e, &set->list, list) { |
235 | most = rcu_dereference_protected(e->dest, 1); | 225 | most = e->dest; |
236 | if (atomic_read(&most->weight) > 0) { | 226 | if (atomic_read(&most->weight) > 0) { |
237 | moh = ip_vs_dest_conn_overhead(most); | 227 | moh = ip_vs_dest_conn_overhead(most); |
238 | goto nextstage; | 228 | goto nextstage; |
@@ -243,11 +233,11 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set) | |||
243 | /* find the destination with the weighted most load */ | 233 | /* find the destination with the weighted most load */ |
244 | nextstage: | 234 | nextstage: |
245 | list_for_each_entry_continue(e, &set->list, list) { | 235 | list_for_each_entry_continue(e, &set->list, list) { |
246 | dest = rcu_dereference_protected(e->dest, 1); | 236 | dest = e->dest; |
247 | doh = ip_vs_dest_conn_overhead(dest); | 237 | doh = ip_vs_dest_conn_overhead(dest); |
248 | /* moh/mw < doh/dw ==> moh*dw < doh*mw, where mw,dw>0 */ | 238 | /* moh/mw < doh/dw ==> moh*dw < doh*mw, where mw,dw>0 */ |
249 | if ((moh * atomic_read(&dest->weight) < | 239 | if (((__s64)moh * atomic_read(&dest->weight) < |
250 | doh * atomic_read(&most->weight)) | 240 | (__s64)doh * atomic_read(&most->weight)) |
251 | && (atomic_read(&dest->weight) > 0)) { | 241 | && (atomic_read(&dest->weight) > 0)) { |
252 | most = dest; | 242 | most = dest; |
253 | moh = doh; | 243 | moh = doh; |
@@ -611,8 +601,8 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc) | |||
611 | continue; | 601 | continue; |
612 | 602 | ||
613 | doh = ip_vs_dest_conn_overhead(dest); | 603 | doh = ip_vs_dest_conn_overhead(dest); |
614 | if (loh * atomic_read(&dest->weight) > | 604 | if ((__s64)loh * atomic_read(&dest->weight) > |
615 | doh * atomic_read(&least->weight)) { | 605 | (__s64)doh * atomic_read(&least->weight)) { |
616 | least = dest; | 606 | least = dest; |
617 | loh = doh; | 607 | loh = doh; |
618 | } | 608 | } |
@@ -819,7 +809,7 @@ static void __exit ip_vs_lblcr_cleanup(void) | |||
819 | { | 809 | { |
820 | unregister_ip_vs_scheduler(&ip_vs_lblcr_scheduler); | 810 | unregister_ip_vs_scheduler(&ip_vs_lblcr_scheduler); |
821 | unregister_pernet_subsys(&ip_vs_lblcr_ops); | 811 | unregister_pernet_subsys(&ip_vs_lblcr_ops); |
822 | synchronize_rcu(); | 812 | rcu_barrier(); |
823 | } | 813 | } |
824 | 814 | ||
825 | 815 | ||
diff --git a/net/netfilter/ipvs/ip_vs_nq.c b/net/netfilter/ipvs/ip_vs_nq.c index d8d9860934fe..961a6de9bb29 100644 --- a/net/netfilter/ipvs/ip_vs_nq.c +++ b/net/netfilter/ipvs/ip_vs_nq.c | |||
@@ -40,7 +40,7 @@ | |||
40 | #include <net/ip_vs.h> | 40 | #include <net/ip_vs.h> |
41 | 41 | ||
42 | 42 | ||
43 | static inline unsigned int | 43 | static inline int |
44 | ip_vs_nq_dest_overhead(struct ip_vs_dest *dest) | 44 | ip_vs_nq_dest_overhead(struct ip_vs_dest *dest) |
45 | { | 45 | { |
46 | /* | 46 | /* |
@@ -59,7 +59,7 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, | |||
59 | struct ip_vs_iphdr *iph) | 59 | struct ip_vs_iphdr *iph) |
60 | { | 60 | { |
61 | struct ip_vs_dest *dest, *least = NULL; | 61 | struct ip_vs_dest *dest, *least = NULL; |
62 | unsigned int loh = 0, doh; | 62 | int loh = 0, doh; |
63 | 63 | ||
64 | IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); | 64 | IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); |
65 | 65 | ||
@@ -92,8 +92,8 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, | |||
92 | } | 92 | } |
93 | 93 | ||
94 | if (!least || | 94 | if (!least || |
95 | (loh * atomic_read(&dest->weight) > | 95 | ((__s64)loh * atomic_read(&dest->weight) > |
96 | doh * atomic_read(&least->weight))) { | 96 | (__s64)doh * atomic_read(&least->weight))) { |
97 | least = dest; | 97 | least = dest; |
98 | loh = doh; | 98 | loh = doh; |
99 | } | 99 | } |
diff --git a/net/netfilter/ipvs/ip_vs_sed.c b/net/netfilter/ipvs/ip_vs_sed.c index a5284cc3d882..e446b9fa7424 100644 --- a/net/netfilter/ipvs/ip_vs_sed.c +++ b/net/netfilter/ipvs/ip_vs_sed.c | |||
@@ -44,7 +44,7 @@ | |||
44 | #include <net/ip_vs.h> | 44 | #include <net/ip_vs.h> |
45 | 45 | ||
46 | 46 | ||
47 | static inline unsigned int | 47 | static inline int |
48 | ip_vs_sed_dest_overhead(struct ip_vs_dest *dest) | 48 | ip_vs_sed_dest_overhead(struct ip_vs_dest *dest) |
49 | { | 49 | { |
50 | /* | 50 | /* |
@@ -63,7 +63,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, | |||
63 | struct ip_vs_iphdr *iph) | 63 | struct ip_vs_iphdr *iph) |
64 | { | 64 | { |
65 | struct ip_vs_dest *dest, *least; | 65 | struct ip_vs_dest *dest, *least; |
66 | unsigned int loh, doh; | 66 | int loh, doh; |
67 | 67 | ||
68 | IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); | 68 | IP_VS_DBG(6, "%s(): Scheduling...\n", __func__); |
69 | 69 | ||
@@ -99,8 +99,8 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, | |||
99 | if (dest->flags & IP_VS_DEST_F_OVERLOAD) | 99 | if (dest->flags & IP_VS_DEST_F_OVERLOAD) |
100 | continue; | 100 | continue; |
101 | doh = ip_vs_sed_dest_overhead(dest); | 101 | doh = ip_vs_sed_dest_overhead(dest); |
102 | if (loh * atomic_read(&dest->weight) > | 102 | if ((__s64)loh * atomic_read(&dest->weight) > |
103 | doh * atomic_read(&least->weight)) { | 103 | (__s64)doh * atomic_read(&least->weight)) { |
104 | least = dest; | 104 | least = dest; |
105 | loh = doh; | 105 | loh = doh; |
106 | } | 106 | } |
diff --git a/net/netfilter/ipvs/ip_vs_wlc.c b/net/netfilter/ipvs/ip_vs_wlc.c index 6dc1fa128840..b5b4650d50a9 100644 --- a/net/netfilter/ipvs/ip_vs_wlc.c +++ b/net/netfilter/ipvs/ip_vs_wlc.c | |||
@@ -35,7 +35,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, | |||
35 | struct ip_vs_iphdr *iph) | 35 | struct ip_vs_iphdr *iph) |
36 | { | 36 | { |
37 | struct ip_vs_dest *dest, *least; | 37 | struct ip_vs_dest *dest, *least; |
38 | unsigned int loh, doh; | 38 | int loh, doh; |
39 | 39 | ||
40 | IP_VS_DBG(6, "ip_vs_wlc_schedule(): Scheduling...\n"); | 40 | IP_VS_DBG(6, "ip_vs_wlc_schedule(): Scheduling...\n"); |
41 | 41 | ||
@@ -71,8 +71,8 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb, | |||
71 | if (dest->flags & IP_VS_DEST_F_OVERLOAD) | 71 | if (dest->flags & IP_VS_DEST_F_OVERLOAD) |
72 | continue; | 72 | continue; |
73 | doh = ip_vs_dest_conn_overhead(dest); | 73 | doh = ip_vs_dest_conn_overhead(dest); |
74 | if (loh * atomic_read(&dest->weight) > | 74 | if ((__s64)loh * atomic_read(&dest->weight) > |
75 | doh * atomic_read(&least->weight)) { | 75 | (__s64)doh * atomic_read(&least->weight)) { |
76 | least = dest; | 76 | least = dest; |
77 | loh = doh; | 77 | loh = doh; |
78 | } | 78 | } |
diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index 6fd967c6278c..cdf4567ba9b3 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c | |||
@@ -24,7 +24,7 @@ | |||
24 | int synproxy_net_id; | 24 | int synproxy_net_id; |
25 | EXPORT_SYMBOL_GPL(synproxy_net_id); | 25 | EXPORT_SYMBOL_GPL(synproxy_net_id); |
26 | 26 | ||
27 | void | 27 | bool |
28 | synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, | 28 | synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, |
29 | const struct tcphdr *th, struct synproxy_options *opts) | 29 | const struct tcphdr *th, struct synproxy_options *opts) |
30 | { | 30 | { |
@@ -32,7 +32,8 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, | |||
32 | u8 buf[40], *ptr; | 32 | u8 buf[40], *ptr; |
33 | 33 | ||
34 | ptr = skb_header_pointer(skb, doff + sizeof(*th), length, buf); | 34 | ptr = skb_header_pointer(skb, doff + sizeof(*th), length, buf); |
35 | BUG_ON(ptr == NULL); | 35 | if (ptr == NULL) |
36 | return false; | ||
36 | 37 | ||
37 | opts->options = 0; | 38 | opts->options = 0; |
38 | while (length > 0) { | 39 | while (length > 0) { |
@@ -41,16 +42,16 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, | |||
41 | 42 | ||
42 | switch (opcode) { | 43 | switch (opcode) { |
43 | case TCPOPT_EOL: | 44 | case TCPOPT_EOL: |
44 | return; | 45 | return true; |
45 | case TCPOPT_NOP: | 46 | case TCPOPT_NOP: |
46 | length--; | 47 | length--; |
47 | continue; | 48 | continue; |
48 | default: | 49 | default: |
49 | opsize = *ptr++; | 50 | opsize = *ptr++; |
50 | if (opsize < 2) | 51 | if (opsize < 2) |
51 | return; | 52 | return true; |
52 | if (opsize > length) | 53 | if (opsize > length) |
53 | return; | 54 | return true; |
54 | 55 | ||
55 | switch (opcode) { | 56 | switch (opcode) { |
56 | case TCPOPT_MSS: | 57 | case TCPOPT_MSS: |
@@ -84,6 +85,7 @@ synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, | |||
84 | length -= opsize; | 85 | length -= opsize; |
85 | } | 86 | } |
86 | } | 87 | } |
88 | return true; | ||
87 | } | 89 | } |
88 | EXPORT_SYMBOL_GPL(synproxy_parse_options); | 90 | EXPORT_SYMBOL_GPL(synproxy_parse_options); |
89 | 91 | ||