diff options
author | John Fastabend <john.fastabend@gmail.com> | 2014-09-12 23:09:16 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-09-13 12:30:26 -0400 |
commit | 1ce87720d456e471de0fbd814dc5d1fe10fc1c44 (patch) | |
tree | f4c7e4870211b78e1626f57deecfafff5622907a /net/sched | |
parent | 459d5f626da75573e985a7197b0919c3b143146c (diff) |
net: sched: make cls_u32 lockless
Make cls_u32 classifier safe to run without holding lock. This patch
converts statistics that are kept in read section u32_classify into
per cpu counters.
This patch was tested with a tight u32 filter add/delete loop while
generating traffic with pktgen. By running pktgen on vlan devices
created on top of a physical device we can hit the qdisc layer
correctly. For ingress qdisc's a loopback cable was used.
for i in {1..100}; do
q=`echo $i%8|bc`;
echo -n "u32 tos: iteration $i on queue $q";
tc filter add dev p3p2 parent $p prio $i u32 match ip tos 0x10 0xff \
action skbedit queue_mapping $q;
sleep 1;
tc filter del dev p3p2 prio $i;
echo -n "u32 tos hash table: iteration $i on queue $q";
tc filter add dev p3p2 parent $p protocol ip prio $i handle 628: u32 divisor 1
tc filter add dev p3p2 parent $p protocol ip prio $i u32 \
match ip protocol 17 0xff link 628: offset at 0 mask 0xf00 shift 6 plus 0
tc filter add dev p3p2 parent $p protocol ip prio $i u32 \
ht 628:0 match ip tos 0x10 0xff action skbedit queue_mapping $q
sleep 2;
tc filter del dev p3p2 prio $i
sleep 1;
done
Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
Acked-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sched')
-rw-r--r-- | net/sched/cls_u32.c | 183 |
1 files changed, 110 insertions, 73 deletions
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index f3227d73a7ae..5ed5ac4361b1 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/kernel.h> | 36 | #include <linux/kernel.h> |
37 | #include <linux/string.h> | 37 | #include <linux/string.h> |
38 | #include <linux/errno.h> | 38 | #include <linux/errno.h> |
39 | #include <linux/percpu.h> | ||
39 | #include <linux/rtnetlink.h> | 40 | #include <linux/rtnetlink.h> |
40 | #include <linux/skbuff.h> | 41 | #include <linux/skbuff.h> |
41 | #include <linux/bitmap.h> | 42 | #include <linux/bitmap.h> |
@@ -44,16 +45,16 @@ | |||
44 | #include <net/pkt_cls.h> | 45 | #include <net/pkt_cls.h> |
45 | 46 | ||
46 | struct tc_u_knode { | 47 | struct tc_u_knode { |
47 | struct tc_u_knode *next; | 48 | struct tc_u_knode __rcu *next; |
48 | u32 handle; | 49 | u32 handle; |
49 | struct tc_u_hnode *ht_up; | 50 | struct tc_u_hnode __rcu *ht_up; |
50 | struct tcf_exts exts; | 51 | struct tcf_exts exts; |
51 | #ifdef CONFIG_NET_CLS_IND | 52 | #ifdef CONFIG_NET_CLS_IND |
52 | int ifindex; | 53 | int ifindex; |
53 | #endif | 54 | #endif |
54 | u8 fshift; | 55 | u8 fshift; |
55 | struct tcf_result res; | 56 | struct tcf_result res; |
56 | struct tc_u_hnode *ht_down; | 57 | struct tc_u_hnode __rcu *ht_down; |
57 | #ifdef CONFIG_CLS_U32_PERF | 58 | #ifdef CONFIG_CLS_U32_PERF |
58 | struct tc_u32_pcnt __percpu *pf; | 59 | struct tc_u32_pcnt __percpu *pf; |
59 | #endif | 60 | #endif |
@@ -62,24 +63,28 @@ struct tc_u_knode { | |||
62 | u32 mask; | 63 | u32 mask; |
63 | u32 __percpu *pcpu_success; | 64 | u32 __percpu *pcpu_success; |
64 | #endif | 65 | #endif |
66 | struct tcf_proto *tp; | ||
65 | struct tc_u32_sel sel; | 67 | struct tc_u32_sel sel; |
68 | struct rcu_head rcu; | ||
66 | }; | 69 | }; |
67 | 70 | ||
68 | struct tc_u_hnode { | 71 | struct tc_u_hnode { |
69 | struct tc_u_hnode *next; | 72 | struct tc_u_hnode __rcu *next; |
70 | u32 handle; | 73 | u32 handle; |
71 | u32 prio; | 74 | u32 prio; |
72 | struct tc_u_common *tp_c; | 75 | struct tc_u_common *tp_c; |
73 | int refcnt; | 76 | int refcnt; |
74 | unsigned int divisor; | 77 | unsigned int divisor; |
75 | struct tc_u_knode *ht[1]; | 78 | struct tc_u_knode __rcu *ht[1]; |
79 | struct rcu_head rcu; | ||
76 | }; | 80 | }; |
77 | 81 | ||
78 | struct tc_u_common { | 82 | struct tc_u_common { |
79 | struct tc_u_hnode *hlist; | 83 | struct tc_u_hnode __rcu *hlist; |
80 | struct Qdisc *q; | 84 | struct Qdisc *q; |
81 | int refcnt; | 85 | int refcnt; |
82 | u32 hgenerator; | 86 | u32 hgenerator; |
87 | struct rcu_head rcu; | ||
83 | }; | 88 | }; |
84 | 89 | ||
85 | static inline unsigned int u32_hash_fold(__be32 key, | 90 | static inline unsigned int u32_hash_fold(__be32 key, |
@@ -98,7 +103,7 @@ static int u32_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct | |||
98 | unsigned int off; | 103 | unsigned int off; |
99 | } stack[TC_U32_MAXDEPTH]; | 104 | } stack[TC_U32_MAXDEPTH]; |
100 | 105 | ||
101 | struct tc_u_hnode *ht = tp->root; | 106 | struct tc_u_hnode *ht = rcu_dereference_bh(tp->root); |
102 | unsigned int off = skb_network_offset(skb); | 107 | unsigned int off = skb_network_offset(skb); |
103 | struct tc_u_knode *n; | 108 | struct tc_u_knode *n; |
104 | int sdepth = 0; | 109 | int sdepth = 0; |
@@ -110,7 +115,7 @@ static int u32_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct | |||
110 | int i, r; | 115 | int i, r; |
111 | 116 | ||
112 | next_ht: | 117 | next_ht: |
113 | n = ht->ht[sel]; | 118 | n = rcu_dereference_bh(ht->ht[sel]); |
114 | 119 | ||
115 | next_knode: | 120 | next_knode: |
116 | if (n) { | 121 | if (n) { |
@@ -123,7 +128,7 @@ next_knode: | |||
123 | 128 | ||
124 | #ifdef CONFIG_CLS_U32_MARK | 129 | #ifdef CONFIG_CLS_U32_MARK |
125 | if ((skb->mark & n->mask) != n->val) { | 130 | if ((skb->mark & n->mask) != n->val) { |
126 | n = n->next; | 131 | n = rcu_dereference_bh(n->next); |
127 | goto next_knode; | 132 | goto next_knode; |
128 | } else { | 133 | } else { |
129 | __this_cpu_inc(*n->pcpu_success); | 134 | __this_cpu_inc(*n->pcpu_success); |
@@ -141,7 +146,7 @@ next_knode: | |||
141 | if (!data) | 146 | if (!data) |
142 | goto out; | 147 | goto out; |
143 | if ((*data ^ key->val) & key->mask) { | 148 | if ((*data ^ key->val) & key->mask) { |
144 | n = n->next; | 149 | n = rcu_dereference_bh(n->next); |
145 | goto next_knode; | 150 | goto next_knode; |
146 | } | 151 | } |
147 | #ifdef CONFIG_CLS_U32_PERF | 152 | #ifdef CONFIG_CLS_U32_PERF |
@@ -149,14 +154,16 @@ next_knode: | |||
149 | j++; | 154 | j++; |
150 | #endif | 155 | #endif |
151 | } | 156 | } |
152 | if (n->ht_down == NULL) { | 157 | |
158 | ht = rcu_dereference_bh(n->ht_down); | ||
159 | if (!ht) { | ||
153 | check_terminal: | 160 | check_terminal: |
154 | if (n->sel.flags & TC_U32_TERMINAL) { | 161 | if (n->sel.flags & TC_U32_TERMINAL) { |
155 | 162 | ||
156 | *res = n->res; | 163 | *res = n->res; |
157 | #ifdef CONFIG_NET_CLS_IND | 164 | #ifdef CONFIG_NET_CLS_IND |
158 | if (!tcf_match_indev(skb, n->ifindex)) { | 165 | if (!tcf_match_indev(skb, n->ifindex)) { |
159 | n = n->next; | 166 | n = rcu_dereference_bh(n->next); |
160 | goto next_knode; | 167 | goto next_knode; |
161 | } | 168 | } |
162 | #endif | 169 | #endif |
@@ -165,13 +172,13 @@ check_terminal: | |||
165 | #endif | 172 | #endif |
166 | r = tcf_exts_exec(skb, &n->exts, res); | 173 | r = tcf_exts_exec(skb, &n->exts, res); |
167 | if (r < 0) { | 174 | if (r < 0) { |
168 | n = n->next; | 175 | n = rcu_dereference_bh(n->next); |
169 | goto next_knode; | 176 | goto next_knode; |
170 | } | 177 | } |
171 | 178 | ||
172 | return r; | 179 | return r; |
173 | } | 180 | } |
174 | n = n->next; | 181 | n = rcu_dereference_bh(n->next); |
175 | goto next_knode; | 182 | goto next_knode; |
176 | } | 183 | } |
177 | 184 | ||
@@ -182,7 +189,7 @@ check_terminal: | |||
182 | stack[sdepth].off = off; | 189 | stack[sdepth].off = off; |
183 | sdepth++; | 190 | sdepth++; |
184 | 191 | ||
185 | ht = n->ht_down; | 192 | ht = rcu_dereference_bh(n->ht_down); |
186 | sel = 0; | 193 | sel = 0; |
187 | if (ht->divisor) { | 194 | if (ht->divisor) { |
188 | __be32 *data, hdata; | 195 | __be32 *data, hdata; |
@@ -224,7 +231,7 @@ check_terminal: | |||
224 | /* POP */ | 231 | /* POP */ |
225 | if (sdepth--) { | 232 | if (sdepth--) { |
226 | n = stack[sdepth].knode; | 233 | n = stack[sdepth].knode; |
227 | ht = n->ht_up; | 234 | ht = rcu_dereference_bh(n->ht_up); |
228 | off = stack[sdepth].off; | 235 | off = stack[sdepth].off; |
229 | goto check_terminal; | 236 | goto check_terminal; |
230 | } | 237 | } |
@@ -241,7 +248,9 @@ u32_lookup_ht(struct tc_u_common *tp_c, u32 handle) | |||
241 | { | 248 | { |
242 | struct tc_u_hnode *ht; | 249 | struct tc_u_hnode *ht; |
243 | 250 | ||
244 | for (ht = tp_c->hlist; ht; ht = ht->next) | 251 | for (ht = rtnl_dereference(tp_c->hlist); |
252 | ht; | ||
253 | ht = rtnl_dereference(ht->next)) | ||
245 | if (ht->handle == handle) | 254 | if (ht->handle == handle) |
246 | break; | 255 | break; |
247 | 256 | ||
@@ -258,7 +267,9 @@ u32_lookup_key(struct tc_u_hnode *ht, u32 handle) | |||
258 | if (sel > ht->divisor) | 267 | if (sel > ht->divisor) |
259 | goto out; | 268 | goto out; |
260 | 269 | ||
261 | for (n = ht->ht[sel]; n; n = n->next) | 270 | for (n = rtnl_dereference(ht->ht[sel]); |
271 | n; | ||
272 | n = rtnl_dereference(n->next)) | ||
262 | if (n->handle == handle) | 273 | if (n->handle == handle) |
263 | break; | 274 | break; |
264 | out: | 275 | out: |
@@ -272,7 +283,7 @@ static unsigned long u32_get(struct tcf_proto *tp, u32 handle) | |||
272 | struct tc_u_common *tp_c = tp->data; | 283 | struct tc_u_common *tp_c = tp->data; |
273 | 284 | ||
274 | if (TC_U32_HTID(handle) == TC_U32_ROOT) | 285 | if (TC_U32_HTID(handle) == TC_U32_ROOT) |
275 | ht = tp->root; | 286 | ht = rtnl_dereference(tp->root); |
276 | else | 287 | else |
277 | ht = u32_lookup_ht(tp_c, TC_U32_HTID(handle)); | 288 | ht = u32_lookup_ht(tp_c, TC_U32_HTID(handle)); |
278 | 289 | ||
@@ -293,6 +304,9 @@ static u32 gen_new_htid(struct tc_u_common *tp_c) | |||
293 | { | 304 | { |
294 | int i = 0x800; | 305 | int i = 0x800; |
295 | 306 | ||
307 | /* hgenerator only used inside rtnl lock it is safe to increment | ||
308 | * without read _copy_ update semantics | ||
309 | */ | ||
296 | do { | 310 | do { |
297 | if (++tp_c->hgenerator == 0x7FF) | 311 | if (++tp_c->hgenerator == 0x7FF) |
298 | tp_c->hgenerator = 1; | 312 | tp_c->hgenerator = 1; |
@@ -328,11 +342,11 @@ static int u32_init(struct tcf_proto *tp) | |||
328 | } | 342 | } |
329 | 343 | ||
330 | tp_c->refcnt++; | 344 | tp_c->refcnt++; |
331 | root_ht->next = tp_c->hlist; | 345 | RCU_INIT_POINTER(root_ht->next, tp_c->hlist); |
332 | tp_c->hlist = root_ht; | 346 | rcu_assign_pointer(tp_c->hlist, root_ht); |
333 | root_ht->tp_c = tp_c; | 347 | root_ht->tp_c = tp_c; |
334 | 348 | ||
335 | tp->root = root_ht; | 349 | rcu_assign_pointer(tp->root, root_ht); |
336 | tp->data = tp_c; | 350 | tp->data = tp_c; |
337 | return 0; | 351 | return 0; |
338 | } | 352 | } |
@@ -350,19 +364,27 @@ static int u32_destroy_key(struct tcf_proto *tp, struct tc_u_knode *n) | |||
350 | return 0; | 364 | return 0; |
351 | } | 365 | } |
352 | 366 | ||
367 | static void u32_delete_key_rcu(struct rcu_head *rcu) | ||
368 | { | ||
369 | struct tc_u_knode *key = container_of(rcu, struct tc_u_knode, rcu); | ||
370 | |||
371 | u32_destroy_key(key->tp, key); | ||
372 | } | ||
373 | |||
353 | static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key) | 374 | static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key) |
354 | { | 375 | { |
355 | struct tc_u_knode **kp; | 376 | struct tc_u_knode __rcu **kp; |
377 | struct tc_u_knode *pkp; | ||
356 | struct tc_u_hnode *ht = key->ht_up; | 378 | struct tc_u_hnode *ht = key->ht_up; |
357 | 379 | ||
358 | if (ht) { | 380 | if (ht) { |
359 | for (kp = &ht->ht[TC_U32_HASH(key->handle)]; *kp; kp = &(*kp)->next) { | 381 | kp = &ht->ht[TC_U32_HASH(key->handle)]; |
360 | if (*kp == key) { | 382 | for (pkp = rtnl_dereference(*kp); pkp; |
361 | tcf_tree_lock(tp); | 383 | kp = &pkp->next, pkp = rtnl_dereference(*kp)) { |
362 | *kp = key->next; | 384 | if (pkp == key) { |
363 | tcf_tree_unlock(tp); | 385 | RCU_INIT_POINTER(*kp, key->next); |
364 | 386 | ||
365 | u32_destroy_key(tp, key); | 387 | call_rcu(&key->rcu, u32_delete_key_rcu); |
366 | return 0; | 388 | return 0; |
367 | } | 389 | } |
368 | } | 390 | } |
@@ -371,16 +393,16 @@ static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key) | |||
371 | return 0; | 393 | return 0; |
372 | } | 394 | } |
373 | 395 | ||
374 | static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht) | 396 | static void u32_clear_hnode(struct tc_u_hnode *ht) |
375 | { | 397 | { |
376 | struct tc_u_knode *n; | 398 | struct tc_u_knode *n; |
377 | unsigned int h; | 399 | unsigned int h; |
378 | 400 | ||
379 | for (h = 0; h <= ht->divisor; h++) { | 401 | for (h = 0; h <= ht->divisor; h++) { |
380 | while ((n = ht->ht[h]) != NULL) { | 402 | while ((n = rtnl_dereference(ht->ht[h])) != NULL) { |
381 | ht->ht[h] = n->next; | 403 | RCU_INIT_POINTER(ht->ht[h], |
382 | 404 | rtnl_dereference(n->next)); | |
383 | u32_destroy_key(tp, n); | 405 | call_rcu(&n->rcu, u32_delete_key_rcu); |
384 | } | 406 | } |
385 | } | 407 | } |
386 | } | 408 | } |
@@ -388,28 +410,31 @@ static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht) | |||
388 | static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht) | 410 | static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht) |
389 | { | 411 | { |
390 | struct tc_u_common *tp_c = tp->data; | 412 | struct tc_u_common *tp_c = tp->data; |
391 | struct tc_u_hnode **hn; | 413 | struct tc_u_hnode __rcu **hn; |
414 | struct tc_u_hnode *phn; | ||
392 | 415 | ||
393 | WARN_ON(ht->refcnt); | 416 | WARN_ON(ht->refcnt); |
394 | 417 | ||
395 | u32_clear_hnode(tp, ht); | 418 | u32_clear_hnode(ht); |
396 | 419 | ||
397 | for (hn = &tp_c->hlist; *hn; hn = &(*hn)->next) { | 420 | hn = &tp_c->hlist; |
398 | if (*hn == ht) { | 421 | for (phn = rtnl_dereference(*hn); |
399 | *hn = ht->next; | 422 | phn; |
400 | kfree(ht); | 423 | hn = &phn->next, phn = rtnl_dereference(*hn)) { |
424 | if (phn == ht) { | ||
425 | RCU_INIT_POINTER(*hn, ht->next); | ||
426 | kfree_rcu(ht, rcu); | ||
401 | return 0; | 427 | return 0; |
402 | } | 428 | } |
403 | } | 429 | } |
404 | 430 | ||
405 | WARN_ON(1); | ||
406 | return -ENOENT; | 431 | return -ENOENT; |
407 | } | 432 | } |
408 | 433 | ||
409 | static void u32_destroy(struct tcf_proto *tp) | 434 | static void u32_destroy(struct tcf_proto *tp) |
410 | { | 435 | { |
411 | struct tc_u_common *tp_c = tp->data; | 436 | struct tc_u_common *tp_c = tp->data; |
412 | struct tc_u_hnode *root_ht = tp->root; | 437 | struct tc_u_hnode *root_ht = rtnl_dereference(tp->root); |
413 | 438 | ||
414 | WARN_ON(root_ht == NULL); | 439 | WARN_ON(root_ht == NULL); |
415 | 440 | ||
@@ -421,17 +446,16 @@ static void u32_destroy(struct tcf_proto *tp) | |||
421 | 446 | ||
422 | tp->q->u32_node = NULL; | 447 | tp->q->u32_node = NULL; |
423 | 448 | ||
424 | for (ht = tp_c->hlist; ht; ht = ht->next) { | 449 | for (ht = rtnl_dereference(tp_c->hlist); |
450 | ht; | ||
451 | ht = rtnl_dereference(ht->next)) { | ||
425 | ht->refcnt--; | 452 | ht->refcnt--; |
426 | u32_clear_hnode(tp, ht); | 453 | u32_clear_hnode(ht); |
427 | } | 454 | } |
428 | 455 | ||
429 | while ((ht = tp_c->hlist) != NULL) { | 456 | while ((ht = rtnl_dereference(tp_c->hlist)) != NULL) { |
430 | tp_c->hlist = ht->next; | 457 | RCU_INIT_POINTER(tp_c->hlist, ht->next); |
431 | 458 | kfree_rcu(ht, rcu); | |
432 | WARN_ON(ht->refcnt != 0); | ||
433 | |||
434 | kfree(ht); | ||
435 | } | 459 | } |
436 | 460 | ||
437 | kfree(tp_c); | 461 | kfree(tp_c); |
@@ -443,6 +467,7 @@ static void u32_destroy(struct tcf_proto *tp) | |||
443 | static int u32_delete(struct tcf_proto *tp, unsigned long arg) | 467 | static int u32_delete(struct tcf_proto *tp, unsigned long arg) |
444 | { | 468 | { |
445 | struct tc_u_hnode *ht = (struct tc_u_hnode *)arg; | 469 | struct tc_u_hnode *ht = (struct tc_u_hnode *)arg; |
470 | struct tc_u_hnode *root_ht = rtnl_dereference(tp->root); | ||
446 | 471 | ||
447 | if (ht == NULL) | 472 | if (ht == NULL) |
448 | return 0; | 473 | return 0; |
@@ -450,7 +475,7 @@ static int u32_delete(struct tcf_proto *tp, unsigned long arg) | |||
450 | if (TC_U32_KEY(ht->handle)) | 475 | if (TC_U32_KEY(ht->handle)) |
451 | return u32_delete_key(tp, (struct tc_u_knode *)ht); | 476 | return u32_delete_key(tp, (struct tc_u_knode *)ht); |
452 | 477 | ||
453 | if (tp->root == ht) | 478 | if (root_ht == ht) |
454 | return -EINVAL; | 479 | return -EINVAL; |
455 | 480 | ||
456 | if (ht->refcnt == 1) { | 481 | if (ht->refcnt == 1) { |
@@ -473,7 +498,9 @@ static u32 gen_new_kid(struct tc_u_hnode *ht, u32 handle) | |||
473 | if (!bitmap) | 498 | if (!bitmap) |
474 | return handle | 0xFFF; | 499 | return handle | 0xFFF; |
475 | 500 | ||
476 | for (n = ht->ht[TC_U32_HASH(handle)]; n; n = n->next) | 501 | for (n = rtnl_dereference(ht->ht[TC_U32_HASH(handle)]); |
502 | n; | ||
503 | n = rtnl_dereference(n->next)) | ||
477 | set_bit(TC_U32_NODE(n->handle), bitmap); | 504 | set_bit(TC_U32_NODE(n->handle), bitmap); |
478 | 505 | ||
479 | i = find_next_zero_bit(bitmap, NR_U32_NODE, 0x800); | 506 | i = find_next_zero_bit(bitmap, NR_U32_NODE, 0x800); |
@@ -523,10 +550,8 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp, | |||
523 | ht_down->refcnt++; | 550 | ht_down->refcnt++; |
524 | } | 551 | } |
525 | 552 | ||
526 | tcf_tree_lock(tp); | 553 | ht_old = rtnl_dereference(n->ht_down); |
527 | ht_old = n->ht_down; | 554 | rcu_assign_pointer(n->ht_down, ht_down); |
528 | n->ht_down = ht_down; | ||
529 | tcf_tree_unlock(tp); | ||
530 | 555 | ||
531 | if (ht_old) | 556 | if (ht_old) |
532 | ht_old->refcnt--; | 557 | ht_old->refcnt--; |
@@ -606,8 +631,8 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, | |||
606 | ht->divisor = divisor; | 631 | ht->divisor = divisor; |
607 | ht->handle = handle; | 632 | ht->handle = handle; |
608 | ht->prio = tp->prio; | 633 | ht->prio = tp->prio; |
609 | ht->next = tp_c->hlist; | 634 | RCU_INIT_POINTER(ht->next, tp_c->hlist); |
610 | tp_c->hlist = ht; | 635 | rcu_assign_pointer(tp_c->hlist, ht); |
611 | *arg = (unsigned long)ht; | 636 | *arg = (unsigned long)ht; |
612 | return 0; | 637 | return 0; |
613 | } | 638 | } |
@@ -615,7 +640,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, | |||
615 | if (tb[TCA_U32_HASH]) { | 640 | if (tb[TCA_U32_HASH]) { |
616 | htid = nla_get_u32(tb[TCA_U32_HASH]); | 641 | htid = nla_get_u32(tb[TCA_U32_HASH]); |
617 | if (TC_U32_HTID(htid) == TC_U32_ROOT) { | 642 | if (TC_U32_HTID(htid) == TC_U32_ROOT) { |
618 | ht = tp->root; | 643 | ht = rtnl_dereference(tp->root); |
619 | htid = ht->handle; | 644 | htid = ht->handle; |
620 | } else { | 645 | } else { |
621 | ht = u32_lookup_ht(tp->data, TC_U32_HTID(htid)); | 646 | ht = u32_lookup_ht(tp->data, TC_U32_HTID(htid)); |
@@ -623,7 +648,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, | |||
623 | return -EINVAL; | 648 | return -EINVAL; |
624 | } | 649 | } |
625 | } else { | 650 | } else { |
626 | ht = tp->root; | 651 | ht = rtnl_dereference(tp->root); |
627 | htid = ht->handle; | 652 | htid = ht->handle; |
628 | } | 653 | } |
629 | 654 | ||
@@ -660,6 +685,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, | |||
660 | n->handle = handle; | 685 | n->handle = handle; |
661 | n->fshift = s->hmask ? ffs(ntohl(s->hmask)) - 1 : 0; | 686 | n->fshift = s->hmask ? ffs(ntohl(s->hmask)) - 1 : 0; |
662 | tcf_exts_init(&n->exts, TCA_U32_ACT, TCA_U32_POLICE); | 687 | tcf_exts_init(&n->exts, TCA_U32_ACT, TCA_U32_POLICE); |
688 | n->tp = tp; | ||
663 | 689 | ||
664 | #ifdef CONFIG_CLS_U32_MARK | 690 | #ifdef CONFIG_CLS_U32_MARK |
665 | n->pcpu_success = alloc_percpu(u32); | 691 | n->pcpu_success = alloc_percpu(u32); |
@@ -675,21 +701,23 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, | |||
675 | 701 | ||
676 | err = u32_set_parms(net, tp, base, ht, n, tb, tca[TCA_RATE], ovr); | 702 | err = u32_set_parms(net, tp, base, ht, n, tb, tca[TCA_RATE], ovr); |
677 | if (err == 0) { | 703 | if (err == 0) { |
678 | struct tc_u_knode **ins; | 704 | struct tc_u_knode __rcu **ins; |
679 | for (ins = &ht->ht[TC_U32_HASH(handle)]; *ins; ins = &(*ins)->next) | 705 | struct tc_u_knode *pins; |
680 | if (TC_U32_NODE(handle) < TC_U32_NODE((*ins)->handle)) | 706 | |
707 | ins = &ht->ht[TC_U32_HASH(handle)]; | ||
708 | for (pins = rtnl_dereference(*ins); pins; | ||
709 | ins = &pins->next, pins = rtnl_dereference(*ins)) | ||
710 | if (TC_U32_NODE(handle) < TC_U32_NODE(pins->handle)) | ||
681 | break; | 711 | break; |
682 | 712 | ||
683 | n->next = *ins; | 713 | RCU_INIT_POINTER(n->next, pins); |
684 | tcf_tree_lock(tp); | 714 | rcu_assign_pointer(*ins, n); |
685 | *ins = n; | ||
686 | tcf_tree_unlock(tp); | ||
687 | 715 | ||
688 | *arg = (unsigned long)n; | 716 | *arg = (unsigned long)n; |
689 | return 0; | 717 | return 0; |
690 | } | 718 | } |
691 | #ifdef CONFIG_CLS_U32_PERF | 719 | #ifdef CONFIG_CLS_U32_PERF |
692 | kfree(n->pf); | 720 | free_percpu(n->pf); |
693 | #endif | 721 | #endif |
694 | kfree(n); | 722 | kfree(n); |
695 | return err; | 723 | return err; |
@@ -705,7 +733,9 @@ static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg) | |||
705 | if (arg->stop) | 733 | if (arg->stop) |
706 | return; | 734 | return; |
707 | 735 | ||
708 | for (ht = tp_c->hlist; ht; ht = ht->next) { | 736 | for (ht = rtnl_dereference(tp_c->hlist); |
737 | ht; | ||
738 | ht = rtnl_dereference(ht->next)) { | ||
709 | if (ht->prio != tp->prio) | 739 | if (ht->prio != tp->prio) |
710 | continue; | 740 | continue; |
711 | if (arg->count >= arg->skip) { | 741 | if (arg->count >= arg->skip) { |
@@ -716,7 +746,9 @@ static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg) | |||
716 | } | 746 | } |
717 | arg->count++; | 747 | arg->count++; |
718 | for (h = 0; h <= ht->divisor; h++) { | 748 | for (h = 0; h <= ht->divisor; h++) { |
719 | for (n = ht->ht[h]; n; n = n->next) { | 749 | for (n = rtnl_dereference(ht->ht[h]); |
750 | n; | ||
751 | n = rtnl_dereference(n->next)) { | ||
720 | if (arg->count < arg->skip) { | 752 | if (arg->count < arg->skip) { |
721 | arg->count++; | 753 | arg->count++; |
722 | continue; | 754 | continue; |
@@ -735,6 +767,7 @@ static int u32_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, | |||
735 | struct sk_buff *skb, struct tcmsg *t) | 767 | struct sk_buff *skb, struct tcmsg *t) |
736 | { | 768 | { |
737 | struct tc_u_knode *n = (struct tc_u_knode *)fh; | 769 | struct tc_u_knode *n = (struct tc_u_knode *)fh; |
770 | struct tc_u_hnode *ht_up, *ht_down; | ||
738 | struct nlattr *nest; | 771 | struct nlattr *nest; |
739 | 772 | ||
740 | if (n == NULL) | 773 | if (n == NULL) |
@@ -762,7 +795,9 @@ static int u32_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, | |||
762 | sizeof(n->sel) + n->sel.nkeys*sizeof(struct tc_u32_key), | 795 | sizeof(n->sel) + n->sel.nkeys*sizeof(struct tc_u32_key), |
763 | &n->sel)) | 796 | &n->sel)) |
764 | goto nla_put_failure; | 797 | goto nla_put_failure; |
765 | if (n->ht_up) { | 798 | |
799 | ht_up = rtnl_dereference(n->ht_up); | ||
800 | if (ht_up) { | ||
766 | u32 htid = n->handle & 0xFFFFF000; | 801 | u32 htid = n->handle & 0xFFFFF000; |
767 | if (nla_put_u32(skb, TCA_U32_HASH, htid)) | 802 | if (nla_put_u32(skb, TCA_U32_HASH, htid)) |
768 | goto nla_put_failure; | 803 | goto nla_put_failure; |
@@ -770,8 +805,10 @@ static int u32_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, | |||
770 | if (n->res.classid && | 805 | if (n->res.classid && |
771 | nla_put_u32(skb, TCA_U32_CLASSID, n->res.classid)) | 806 | nla_put_u32(skb, TCA_U32_CLASSID, n->res.classid)) |
772 | goto nla_put_failure; | 807 | goto nla_put_failure; |
773 | if (n->ht_down && | 808 | |
774 | nla_put_u32(skb, TCA_U32_LINK, n->ht_down->handle)) | 809 | ht_down = rtnl_dereference(n->ht_down); |
810 | if (ht_down && | ||
811 | nla_put_u32(skb, TCA_U32_LINK, ht_down->handle)) | ||
775 | goto nla_put_failure; | 812 | goto nla_put_failure; |
776 | 813 | ||
777 | #ifdef CONFIG_CLS_U32_MARK | 814 | #ifdef CONFIG_CLS_U32_MARK |