aboutsummaryrefslogtreecommitdiffstats
path: root/net/netfilter
diff options
context:
space:
mode:
authorJulian Anastasov <ja@ssi.bg>2013-03-22 05:46:46 -0400
committerPablo Neira Ayuso <pablo@netfilter.org>2013-04-01 18:23:53 -0400
commit1acb7f6761626f4834ea5ce16d8a4dd2a5dd1949 (patch)
tree0eb9257cb29c7892a4a7f8c98da8366f86c2e79f /net/netfilter
parent9be52aba7a7fdaaad82d88b2e66b0d215877a1fd (diff)
ipvs: convert sh scheduler to rcu
Use the 3 new methods to reassign dests. 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_sh.c81
1 files changed, 45 insertions, 36 deletions
diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c
index e33126994628..55e76d8db711 100644
--- a/net/netfilter/ipvs/ip_vs_sh.c
+++ b/net/netfilter/ipvs/ip_vs_sh.c
@@ -53,7 +53,7 @@
53 * IPVS SH bucket 53 * IPVS SH bucket
54 */ 54 */
55struct ip_vs_sh_bucket { 55struct ip_vs_sh_bucket {
56 struct ip_vs_dest *dest; /* real server (cache) */ 56 struct ip_vs_dest __rcu *dest; /* real server (cache) */
57}; 57};
58 58
59/* 59/*
@@ -66,6 +66,10 @@ struct ip_vs_sh_bucket {
66#define IP_VS_SH_TAB_SIZE (1 << IP_VS_SH_TAB_BITS) 66#define IP_VS_SH_TAB_SIZE (1 << IP_VS_SH_TAB_BITS)
67#define IP_VS_SH_TAB_MASK (IP_VS_SH_TAB_SIZE - 1) 67#define IP_VS_SH_TAB_MASK (IP_VS_SH_TAB_SIZE - 1)
68 68
69struct ip_vs_sh_state {
70 struct ip_vs_sh_bucket buckets[IP_VS_SH_TAB_SIZE];
71 struct rcu_head rcu_head;
72};
69 73
70/* 74/*
71 * Returns hash value for IPVS SH entry 75 * Returns hash value for IPVS SH entry
@@ -87,10 +91,9 @@ static inline unsigned int ip_vs_sh_hashkey(int af, const union nf_inet_addr *ad
87 * Get ip_vs_dest associated with supplied parameters. 91 * Get ip_vs_dest associated with supplied parameters.
88 */ 92 */
89static inline struct ip_vs_dest * 93static inline struct ip_vs_dest *
90ip_vs_sh_get(int af, struct ip_vs_sh_bucket *tbl, 94ip_vs_sh_get(int af, struct ip_vs_sh_state *s, const union nf_inet_addr *addr)
91 const union nf_inet_addr *addr)
92{ 95{
93 return (tbl[ip_vs_sh_hashkey(af, addr)]).dest; 96 return rcu_dereference(s->buckets[ip_vs_sh_hashkey(af, addr)].dest);
94} 97}
95 98
96 99
@@ -98,27 +101,32 @@ ip_vs_sh_get(int af, struct ip_vs_sh_bucket *tbl,
98 * Assign all the hash buckets of the specified table with the service. 101 * Assign all the hash buckets of the specified table with the service.
99 */ 102 */
100static int 103static int
101ip_vs_sh_assign(struct ip_vs_sh_bucket *tbl, struct ip_vs_service *svc) 104ip_vs_sh_reassign(struct ip_vs_sh_state *s, struct ip_vs_service *svc)
102{ 105{
103 int i; 106 int i;
104 struct ip_vs_sh_bucket *b; 107 struct ip_vs_sh_bucket *b;
105 struct list_head *p; 108 struct list_head *p;
106 struct ip_vs_dest *dest; 109 struct ip_vs_dest *dest;
107 int d_count; 110 int d_count;
111 bool empty;
108 112
109 b = tbl; 113 b = &s->buckets[0];
110 p = &svc->destinations; 114 p = &svc->destinations;
115 empty = list_empty(p);
111 d_count = 0; 116 d_count = 0;
112 for (i=0; i<IP_VS_SH_TAB_SIZE; i++) { 117 for (i=0; i<IP_VS_SH_TAB_SIZE; i++) {
113 if (list_empty(p)) { 118 dest = rcu_dereference_protected(b->dest, 1);
114 b->dest = NULL; 119 if (dest)
115 } else { 120 ip_vs_dest_put(dest);
121 if (empty)
122 RCU_INIT_POINTER(b->dest, NULL);
123 else {
116 if (p == &svc->destinations) 124 if (p == &svc->destinations)
117 p = p->next; 125 p = p->next;
118 126
119 dest = list_entry(p, struct ip_vs_dest, n_list); 127 dest = list_entry(p, struct ip_vs_dest, n_list);
120 atomic_inc(&dest->refcnt); 128 ip_vs_dest_hold(dest);
121 b->dest = dest; 129 RCU_INIT_POINTER(b->dest, dest);
122 130
123 IP_VS_DBG_BUF(6, "assigned i: %d dest: %s weight: %d\n", 131 IP_VS_DBG_BUF(6, "assigned i: %d dest: %s weight: %d\n",
124 i, IP_VS_DBG_ADDR(svc->af, &dest->addr), 132 i, IP_VS_DBG_ADDR(svc->af, &dest->addr),
@@ -140,16 +148,18 @@ ip_vs_sh_assign(struct ip_vs_sh_bucket *tbl, struct ip_vs_service *svc)
140/* 148/*
141 * Flush all the hash buckets of the specified table. 149 * Flush all the hash buckets of the specified table.
142 */ 150 */
143static void ip_vs_sh_flush(struct ip_vs_sh_bucket *tbl) 151static void ip_vs_sh_flush(struct ip_vs_sh_state *s)
144{ 152{
145 int i; 153 int i;
146 struct ip_vs_sh_bucket *b; 154 struct ip_vs_sh_bucket *b;
155 struct ip_vs_dest *dest;
147 156
148 b = tbl; 157 b = &s->buckets[0];
149 for (i=0; i<IP_VS_SH_TAB_SIZE; i++) { 158 for (i=0; i<IP_VS_SH_TAB_SIZE; i++) {
150 if (b->dest) { 159 dest = rcu_dereference_protected(b->dest, 1);
151 atomic_dec(&b->dest->refcnt); 160 if (dest) {
152 b->dest = NULL; 161 ip_vs_dest_put(dest);
162 RCU_INIT_POINTER(b->dest, NULL);
153 } 163 }
154 b++; 164 b++;
155 } 165 }
@@ -158,21 +168,20 @@ static void ip_vs_sh_flush(struct ip_vs_sh_bucket *tbl)
158 168
159static int ip_vs_sh_init_svc(struct ip_vs_service *svc) 169static int ip_vs_sh_init_svc(struct ip_vs_service *svc)
160{ 170{
161 struct ip_vs_sh_bucket *tbl; 171 struct ip_vs_sh_state *s;
162 172
163 /* allocate the SH table for this service */ 173 /* allocate the SH table for this service */
164 tbl = kmalloc(sizeof(struct ip_vs_sh_bucket)*IP_VS_SH_TAB_SIZE, 174 s = kzalloc(sizeof(struct ip_vs_sh_state), GFP_KERNEL);
165 GFP_KERNEL); 175 if (s == NULL)
166 if (tbl == NULL)
167 return -ENOMEM; 176 return -ENOMEM;
168 177
169 svc->sched_data = tbl; 178 svc->sched_data = s;
170 IP_VS_DBG(6, "SH hash table (memory=%Zdbytes) allocated for " 179 IP_VS_DBG(6, "SH hash table (memory=%Zdbytes) allocated for "
171 "current service\n", 180 "current service\n",
172 sizeof(struct ip_vs_sh_bucket)*IP_VS_SH_TAB_SIZE); 181 sizeof(struct ip_vs_sh_bucket)*IP_VS_SH_TAB_SIZE);
173 182
174 /* assign the hash buckets with the updated service */ 183 /* assign the hash buckets with current dests */
175 ip_vs_sh_assign(tbl, svc); 184 ip_vs_sh_reassign(s, svc);
176 185
177 return 0; 186 return 0;
178} 187}
@@ -180,13 +189,13 @@ static int ip_vs_sh_init_svc(struct ip_vs_service *svc)
180 189
181static int ip_vs_sh_done_svc(struct ip_vs_service *svc) 190static int ip_vs_sh_done_svc(struct ip_vs_service *svc)
182{ 191{
183 struct ip_vs_sh_bucket *tbl = svc->sched_data; 192 struct ip_vs_sh_state *s = svc->sched_data;
184 193
185 /* got to clean up hash buckets here */ 194 /* got to clean up hash buckets here */
186 ip_vs_sh_flush(tbl); 195 ip_vs_sh_flush(s);
187 196
188 /* release the table itself */ 197 /* release the table itself */
189 kfree(svc->sched_data); 198 kfree_rcu(s, rcu_head);
190 IP_VS_DBG(6, "SH hash table (memory=%Zdbytes) released\n", 199 IP_VS_DBG(6, "SH hash table (memory=%Zdbytes) released\n",
191 sizeof(struct ip_vs_sh_bucket)*IP_VS_SH_TAB_SIZE); 200 sizeof(struct ip_vs_sh_bucket)*IP_VS_SH_TAB_SIZE);
192 201
@@ -194,15 +203,13 @@ static int ip_vs_sh_done_svc(struct ip_vs_service *svc)
194} 203}
195 204
196 205
197static int ip_vs_sh_update_svc(struct ip_vs_service *svc) 206static int ip_vs_sh_dest_changed(struct ip_vs_service *svc,
207 struct ip_vs_dest *dest)
198{ 208{
199 struct ip_vs_sh_bucket *tbl = svc->sched_data; 209 struct ip_vs_sh_state *s = svc->sched_data;
200
201 /* got to clean up hash buckets here */
202 ip_vs_sh_flush(tbl);
203 210
204 /* assign the hash buckets with the updated service */ 211 /* assign the hash buckets with the updated service */
205 ip_vs_sh_assign(tbl, svc); 212 ip_vs_sh_reassign(s, svc);
206 213
207 return 0; 214 return 0;
208} 215}
@@ -225,15 +232,15 @@ static struct ip_vs_dest *
225ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) 232ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
226{ 233{
227 struct ip_vs_dest *dest; 234 struct ip_vs_dest *dest;
228 struct ip_vs_sh_bucket *tbl; 235 struct ip_vs_sh_state *s;
229 struct ip_vs_iphdr iph; 236 struct ip_vs_iphdr iph;
230 237
231 ip_vs_fill_iph_addr_only(svc->af, skb, &iph); 238 ip_vs_fill_iph_addr_only(svc->af, skb, &iph);
232 239
233 IP_VS_DBG(6, "ip_vs_sh_schedule(): Scheduling...\n"); 240 IP_VS_DBG(6, "ip_vs_sh_schedule(): Scheduling...\n");
234 241
235 tbl = (struct ip_vs_sh_bucket *)svc->sched_data; 242 s = (struct ip_vs_sh_state *) svc->sched_data;
236 dest = ip_vs_sh_get(svc->af, tbl, &iph.saddr); 243 dest = ip_vs_sh_get(svc->af, s, &iph.saddr);
237 if (!dest 244 if (!dest
238 || !(dest->flags & IP_VS_DEST_F_AVAILABLE) 245 || !(dest->flags & IP_VS_DEST_F_AVAILABLE)
239 || atomic_read(&dest->weight) <= 0 246 || atomic_read(&dest->weight) <= 0
@@ -262,7 +269,9 @@ static struct ip_vs_scheduler ip_vs_sh_scheduler =
262 .n_list = LIST_HEAD_INIT(ip_vs_sh_scheduler.n_list), 269 .n_list = LIST_HEAD_INIT(ip_vs_sh_scheduler.n_list),
263 .init_service = ip_vs_sh_init_svc, 270 .init_service = ip_vs_sh_init_svc,
264 .done_service = ip_vs_sh_done_svc, 271 .done_service = ip_vs_sh_done_svc,
265 .update_service = ip_vs_sh_update_svc, 272 .add_dest = ip_vs_sh_dest_changed,
273 .del_dest = ip_vs_sh_dest_changed,
274 .upd_dest = ip_vs_sh_dest_changed,
266 .schedule = ip_vs_sh_schedule, 275 .schedule = ip_vs_sh_schedule,
267}; 276};
268 277