aboutsummaryrefslogtreecommitdiffstats
path: root/net/sched/cls_route.c
diff options
context:
space:
mode:
authorJohn Fastabend <john.fastabend@gmail.com>2014-09-12 23:07:50 -0400
committerDavid S. Miller <davem@davemloft.net>2014-09-13 12:30:26 -0400
commit1109c00547fc66df45b9ff923544be4c1e1bec13 (patch)
tree86594c639179f0773dbdcab526cea42ed1a0bef0 /net/sched/cls_route.c
parente35a8ee5993ba81fd6c092f6827458c60406255b (diff)
net: sched: RCU cls_route
RCUify the route classifier. For now however spinlock's are used to protect fastmap cache. The issue here is the fastmap may be read by one CPU while the cache is being updated by another. An array of pointers could be one possible solution. 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/cls_route.c')
-rw-r--r--net/sched/cls_route.c226
1 files changed, 132 insertions, 94 deletions
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
index dd9fc2523c76..ba96deacf27c 100644
--- a/net/sched/cls_route.c
+++ b/net/sched/cls_route.c
@@ -29,25 +29,26 @@
29 * are mutually exclusive. 29 * are mutually exclusive.
30 * 3. "to TAG from ANY" has higher priority, than "to ANY from XXX" 30 * 3. "to TAG from ANY" has higher priority, than "to ANY from XXX"
31 */ 31 */
32
33struct route4_fastmap { 32struct route4_fastmap {
34 struct route4_filter *filter; 33 struct route4_filter *filter;
35 u32 id; 34 u32 id;
36 int iif; 35 int iif;
37}; 36};
38 37
39struct route4_head { 38struct route4_head {
40 struct route4_fastmap fastmap[16]; 39 struct route4_fastmap fastmap[16];
41 struct route4_bucket *table[256 + 1]; 40 struct route4_bucket __rcu *table[256 + 1];
41 struct rcu_head rcu;
42}; 42};
43 43
44struct route4_bucket { 44struct route4_bucket {
45 /* 16 FROM buckets + 16 IIF buckets + 1 wildcard bucket */ 45 /* 16 FROM buckets + 16 IIF buckets + 1 wildcard bucket */
46 struct route4_filter *ht[16 + 16 + 1]; 46 struct route4_filter __rcu *ht[16 + 16 + 1];
47 struct rcu_head rcu;
47}; 48};
48 49
49struct route4_filter { 50struct route4_filter {
50 struct route4_filter *next; 51 struct route4_filter __rcu *next;
51 u32 id; 52 u32 id;
52 int iif; 53 int iif;
53 54
@@ -55,6 +56,8 @@ struct route4_filter {
55 struct tcf_exts exts; 56 struct tcf_exts exts;
56 u32 handle; 57 u32 handle;
57 struct route4_bucket *bkt; 58 struct route4_bucket *bkt;
59 struct tcf_proto *tp;
60 struct rcu_head rcu;
58}; 61};
59 62
60#define ROUTE4_FAILURE ((struct route4_filter *)(-1L)) 63#define ROUTE4_FAILURE ((struct route4_filter *)(-1L))
@@ -64,14 +67,13 @@ static inline int route4_fastmap_hash(u32 id, int iif)
64 return id & 0xF; 67 return id & 0xF;
65} 68}
66 69
70static DEFINE_SPINLOCK(fastmap_lock);
67static void 71static void
68route4_reset_fastmap(struct Qdisc *q, struct route4_head *head, u32 id) 72route4_reset_fastmap(struct route4_head *head)
69{ 73{
70 spinlock_t *root_lock = qdisc_root_sleeping_lock(q); 74 spin_lock_bh(&fastmap_lock);
71
72 spin_lock_bh(root_lock);
73 memset(head->fastmap, 0, sizeof(head->fastmap)); 75 memset(head->fastmap, 0, sizeof(head->fastmap));
74 spin_unlock_bh(root_lock); 76 spin_unlock_bh(&fastmap_lock);
75} 77}
76 78
77static void 79static void
@@ -80,9 +82,12 @@ route4_set_fastmap(struct route4_head *head, u32 id, int iif,
80{ 82{
81 int h = route4_fastmap_hash(id, iif); 83 int h = route4_fastmap_hash(id, iif);
82 84
85 /* fastmap updates must look atomic to aling id, iff, filter */
86 spin_lock_bh(&fastmap_lock);
83 head->fastmap[h].id = id; 87 head->fastmap[h].id = id;
84 head->fastmap[h].iif = iif; 88 head->fastmap[h].iif = iif;
85 head->fastmap[h].filter = f; 89 head->fastmap[h].filter = f;
90 spin_unlock_bh(&fastmap_lock);
86} 91}
87 92
88static inline int route4_hash_to(u32 id) 93static inline int route4_hash_to(u32 id)
@@ -123,7 +128,7 @@ static inline int route4_hash_wild(void)
123static int route4_classify(struct sk_buff *skb, const struct tcf_proto *tp, 128static int route4_classify(struct sk_buff *skb, const struct tcf_proto *tp,
124 struct tcf_result *res) 129 struct tcf_result *res)
125{ 130{
126 struct route4_head *head = tp->root; 131 struct route4_head *head = rcu_dereference_bh(tp->root);
127 struct dst_entry *dst; 132 struct dst_entry *dst;
128 struct route4_bucket *b; 133 struct route4_bucket *b;
129 struct route4_filter *f; 134 struct route4_filter *f;
@@ -141,32 +146,43 @@ static int route4_classify(struct sk_buff *skb, const struct tcf_proto *tp,
141 iif = inet_iif(skb); 146 iif = inet_iif(skb);
142 147
143 h = route4_fastmap_hash(id, iif); 148 h = route4_fastmap_hash(id, iif);
149
150 spin_lock(&fastmap_lock);
144 if (id == head->fastmap[h].id && 151 if (id == head->fastmap[h].id &&
145 iif == head->fastmap[h].iif && 152 iif == head->fastmap[h].iif &&
146 (f = head->fastmap[h].filter) != NULL) { 153 (f = head->fastmap[h].filter) != NULL) {
147 if (f == ROUTE4_FAILURE) 154 if (f == ROUTE4_FAILURE) {
155 spin_unlock(&fastmap_lock);
148 goto failure; 156 goto failure;
157 }
149 158
150 *res = f->res; 159 *res = f->res;
160 spin_unlock(&fastmap_lock);
151 return 0; 161 return 0;
152 } 162 }
163 spin_unlock(&fastmap_lock);
153 164
154 h = route4_hash_to(id); 165 h = route4_hash_to(id);
155 166
156restart: 167restart:
157 b = head->table[h]; 168 b = rcu_dereference_bh(head->table[h]);
158 if (b) { 169 if (b) {
159 for (f = b->ht[route4_hash_from(id)]; f; f = f->next) 170 for (f = rcu_dereference_bh(b->ht[route4_hash_from(id)]);
171 f;
172 f = rcu_dereference_bh(f->next))
160 if (f->id == id) 173 if (f->id == id)
161 ROUTE4_APPLY_RESULT(); 174 ROUTE4_APPLY_RESULT();
162 175
163 for (f = b->ht[route4_hash_iif(iif)]; f; f = f->next) 176 for (f = rcu_dereference_bh(b->ht[route4_hash_iif(iif)]);
177 f;
178 f = rcu_dereference_bh(f->next))
164 if (f->iif == iif) 179 if (f->iif == iif)
165 ROUTE4_APPLY_RESULT(); 180 ROUTE4_APPLY_RESULT();
166 181
167 for (f = b->ht[route4_hash_wild()]; f; f = f->next) 182 for (f = rcu_dereference_bh(b->ht[route4_hash_wild()]);
183 f;
184 f = rcu_dereference_bh(f->next))
168 ROUTE4_APPLY_RESULT(); 185 ROUTE4_APPLY_RESULT();
169
170 } 186 }
171 if (h < 256) { 187 if (h < 256) {
172 h = 256; 188 h = 256;
@@ -213,7 +229,7 @@ static inline u32 from_hash(u32 id)
213 229
214static unsigned long route4_get(struct tcf_proto *tp, u32 handle) 230static unsigned long route4_get(struct tcf_proto *tp, u32 handle)
215{ 231{
216 struct route4_head *head = tp->root; 232 struct route4_head *head = rtnl_dereference(tp->root);
217 struct route4_bucket *b; 233 struct route4_bucket *b;
218 struct route4_filter *f; 234 struct route4_filter *f;
219 unsigned int h1, h2; 235 unsigned int h1, h2;
@@ -229,9 +245,11 @@ static unsigned long route4_get(struct tcf_proto *tp, u32 handle)
229 if (h2 > 32) 245 if (h2 > 32)
230 return 0; 246 return 0;
231 247
232 b = head->table[h1]; 248 b = rtnl_dereference(head->table[h1]);
233 if (b) { 249 if (b) {
234 for (f = b->ht[h2]; f; f = f->next) 250 for (f = rtnl_dereference(b->ht[h2]);
251 f;
252 f = rtnl_dereference(f->next))
235 if (f->handle == handle) 253 if (f->handle == handle)
236 return (unsigned long)f; 254 return (unsigned long)f;
237 } 255 }
@@ -248,8 +266,11 @@ static int route4_init(struct tcf_proto *tp)
248} 266}
249 267
250static void 268static void
251route4_delete_filter(struct tcf_proto *tp, struct route4_filter *f) 269route4_delete_filter(struct rcu_head *head)
252{ 270{
271 struct route4_filter *f = container_of(head, struct route4_filter, rcu);
272 struct tcf_proto *tp = f->tp;
273
253 tcf_unbind_filter(tp, &f->res); 274 tcf_unbind_filter(tp, &f->res);
254 tcf_exts_destroy(tp, &f->exts); 275 tcf_exts_destroy(tp, &f->exts);
255 kfree(f); 276 kfree(f);
@@ -257,7 +278,7 @@ route4_delete_filter(struct tcf_proto *tp, struct route4_filter *f)
257 278
258static void route4_destroy(struct tcf_proto *tp) 279static void route4_destroy(struct tcf_proto *tp)
259{ 280{
260 struct route4_head *head = tp->root; 281 struct route4_head *head = rtnl_dereference(tp->root);
261 int h1, h2; 282 int h1, h2;
262 283
263 if (head == NULL) 284 if (head == NULL)
@@ -266,28 +287,35 @@ static void route4_destroy(struct tcf_proto *tp)
266 for (h1 = 0; h1 <= 256; h1++) { 287 for (h1 = 0; h1 <= 256; h1++) {
267 struct route4_bucket *b; 288 struct route4_bucket *b;
268 289
269 b = head->table[h1]; 290 b = rtnl_dereference(head->table[h1]);
270 if (b) { 291 if (b) {
271 for (h2 = 0; h2 <= 32; h2++) { 292 for (h2 = 0; h2 <= 32; h2++) {
272 struct route4_filter *f; 293 struct route4_filter *f;
273 294
274 while ((f = b->ht[h2]) != NULL) { 295 while ((f = rtnl_dereference(b->ht[h2])) != NULL) {
275 b->ht[h2] = f->next; 296 struct route4_filter *next;
276 route4_delete_filter(tp, f); 297
298 next = rtnl_dereference(f->next);
299 RCU_INIT_POINTER(b->ht[h2], next);
300 call_rcu(&f->rcu, route4_delete_filter);
277 } 301 }
278 } 302 }
279 kfree(b); 303 RCU_INIT_POINTER(head->table[h1], NULL);
304 kfree_rcu(b, rcu);
280 } 305 }
281 } 306 }
282 kfree(head); 307 RCU_INIT_POINTER(tp->root, NULL);
308 kfree_rcu(head, rcu);
283} 309}
284 310
285static int route4_delete(struct tcf_proto *tp, unsigned long arg) 311static int route4_delete(struct tcf_proto *tp, unsigned long arg)
286{ 312{
287 struct route4_head *head = tp->root; 313 struct route4_head *head = rtnl_dereference(tp->root);
288 struct route4_filter **fp, *f = (struct route4_filter *)arg; 314 struct route4_filter *f = (struct route4_filter *)arg;
289 unsigned int h = 0; 315 struct route4_filter __rcu **fp;
316 struct route4_filter *nf;
290 struct route4_bucket *b; 317 struct route4_bucket *b;
318 unsigned int h = 0;
291 int i; 319 int i;
292 320
293 if (!head || !f) 321 if (!head || !f)
@@ -296,27 +324,35 @@ static int route4_delete(struct tcf_proto *tp, unsigned long arg)
296 h = f->handle; 324 h = f->handle;
297 b = f->bkt; 325 b = f->bkt;
298 326
299 for (fp = &b->ht[from_hash(h >> 16)]; *fp; fp = &(*fp)->next) { 327 fp = &b->ht[from_hash(h >> 16)];
300 if (*fp == f) { 328 for (nf = rtnl_dereference(*fp); nf;
301 tcf_tree_lock(tp); 329 fp = &nf->next, nf = rtnl_dereference(*fp)) {
302 *fp = f->next; 330 if (nf == f) {
303 tcf_tree_unlock(tp); 331 /* unlink it */
332 RCU_INIT_POINTER(*fp, rtnl_dereference(f->next));
304 333
305 route4_reset_fastmap(tp->q, head, f->id); 334 /* Remove any fastmap lookups that might ref filter
306 route4_delete_filter(tp, f); 335 * notice we unlink'd the filter so we can't get it
336 * back in the fastmap.
337 */
338 route4_reset_fastmap(head);
307 339
308 /* Strip tree */ 340 /* Delete it */
341 call_rcu(&f->rcu, route4_delete_filter);
309 342
310 for (i = 0; i <= 32; i++) 343 /* Strip RTNL protected tree */
311 if (b->ht[i]) 344 for (i = 0; i <= 32; i++) {
345 struct route4_filter *rt;
346
347 rt = rtnl_dereference(b->ht[i]);
348 if (rt)
312 return 0; 349 return 0;
350 }
313 351
314 /* OK, session has no flows */ 352 /* OK, session has no flows */
315 tcf_tree_lock(tp); 353 RCU_INIT_POINTER(head->table[to_hash(h)], NULL);
316 head->table[to_hash(h)] = NULL; 354 kfree_rcu(b, rcu);
317 tcf_tree_unlock(tp);
318 355
319 kfree(b);
320 return 0; 356 return 0;
321 } 357 }
322 } 358 }
@@ -380,26 +416,25 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,
380 } 416 }
381 417
382 h1 = to_hash(nhandle); 418 h1 = to_hash(nhandle);
383 b = head->table[h1]; 419 b = rtnl_dereference(head->table[h1]);
384 if (!b) { 420 if (!b) {
385 err = -ENOBUFS; 421 err = -ENOBUFS;
386 b = kzalloc(sizeof(struct route4_bucket), GFP_KERNEL); 422 b = kzalloc(sizeof(struct route4_bucket), GFP_KERNEL);
387 if (b == NULL) 423 if (b == NULL)
388 goto errout; 424 goto errout;
389 425
390 tcf_tree_lock(tp); 426 rcu_assign_pointer(head->table[h1], b);
391 head->table[h1] = b;
392 tcf_tree_unlock(tp);
393 } else { 427 } else {
394 unsigned int h2 = from_hash(nhandle >> 16); 428 unsigned int h2 = from_hash(nhandle >> 16);
395 429
396 err = -EEXIST; 430 err = -EEXIST;
397 for (fp = b->ht[h2]; fp; fp = fp->next) 431 for (fp = rtnl_dereference(b->ht[h2]);
432 fp;
433 fp = rtnl_dereference(fp->next))
398 if (fp->handle == f->handle) 434 if (fp->handle == f->handle)
399 goto errout; 435 goto errout;
400 } 436 }
401 437
402 tcf_tree_lock(tp);
403 if (tb[TCA_ROUTE4_TO]) 438 if (tb[TCA_ROUTE4_TO])
404 f->id = to; 439 f->id = to;
405 440
@@ -410,7 +445,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,
410 445
411 f->handle = nhandle; 446 f->handle = nhandle;
412 f->bkt = b; 447 f->bkt = b;
413 tcf_tree_unlock(tp); 448 f->tp = tp;
414 449
415 if (tb[TCA_ROUTE4_CLASSID]) { 450 if (tb[TCA_ROUTE4_CLASSID]) {
416 f->res.classid = nla_get_u32(tb[TCA_ROUTE4_CLASSID]); 451 f->res.classid = nla_get_u32(tb[TCA_ROUTE4_CLASSID]);
@@ -431,14 +466,15 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
431 struct nlattr **tca, 466 struct nlattr **tca,
432 unsigned long *arg, bool ovr) 467 unsigned long *arg, bool ovr)
433{ 468{
434 struct route4_head *head = tp->root; 469 struct route4_head *head = rtnl_dereference(tp->root);
435 struct route4_filter *f, *f1, **fp; 470 struct route4_filter __rcu **fp;
471 struct route4_filter *fold, *f1, *pfp, *f = NULL;
436 struct route4_bucket *b; 472 struct route4_bucket *b;
437 struct nlattr *opt = tca[TCA_OPTIONS]; 473 struct nlattr *opt = tca[TCA_OPTIONS];
438 struct nlattr *tb[TCA_ROUTE4_MAX + 1]; 474 struct nlattr *tb[TCA_ROUTE4_MAX + 1];
439 unsigned int h, th; 475 unsigned int h, th;
440 u32 old_handle = 0;
441 int err; 476 int err;
477 bool new = true;
442 478
443 if (opt == NULL) 479 if (opt == NULL)
444 return handle ? -EINVAL : 0; 480 return handle ? -EINVAL : 0;
@@ -447,70 +483,70 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
447 if (err < 0) 483 if (err < 0)
448 return err; 484 return err;
449 485
450 f = (struct route4_filter *)*arg; 486 fold = (struct route4_filter *)*arg;
451 if (f) { 487 if (fold && handle && fold->handle != handle)
452 if (f->handle != handle && handle)
453 return -EINVAL; 488 return -EINVAL;
454 489
455 if (f->bkt)
456 old_handle = f->handle;
457
458 err = route4_set_parms(net, tp, base, f, handle, head, tb,
459 tca[TCA_RATE], 0, ovr);
460 if (err < 0)
461 return err;
462
463 goto reinsert;
464 }
465
466 err = -ENOBUFS; 490 err = -ENOBUFS;
467 if (head == NULL) { 491 if (head == NULL) {
468 head = kzalloc(sizeof(struct route4_head), GFP_KERNEL); 492 head = kzalloc(sizeof(struct route4_head), GFP_KERNEL);
469 if (head == NULL) 493 if (head == NULL)
470 goto errout; 494 goto errout;
471 495 rcu_assign_pointer(tp->root, head);
472 tcf_tree_lock(tp);
473 tp->root = head;
474 tcf_tree_unlock(tp);
475 } 496 }
476 497
477 f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL); 498 f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL);
478 if (f == NULL) 499 if (!f)
479 goto errout; 500 goto errout;
480 501
481 tcf_exts_init(&f->exts, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE); 502 tcf_exts_init(&f->exts, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE);
503 if (fold) {
504 f->id = fold->id;
505 f->iif = fold->iif;
506 f->res = fold->res;
507 f->handle = fold->handle;
508
509 f->tp = fold->tp;
510 f->bkt = fold->bkt;
511 new = false;
512 }
513
482 err = route4_set_parms(net, tp, base, f, handle, head, tb, 514 err = route4_set_parms(net, tp, base, f, handle, head, tb,
483 tca[TCA_RATE], 1, ovr); 515 tca[TCA_RATE], new, ovr);
484 if (err < 0) 516 if (err < 0)
485 goto errout; 517 goto errout;
486 518
487reinsert:
488 h = from_hash(f->handle >> 16); 519 h = from_hash(f->handle >> 16);
489 for (fp = &f->bkt->ht[h]; (f1 = *fp) != NULL; fp = &f1->next) 520 fp = &f->bkt->ht[h];
521 for (pfp = rtnl_dereference(*fp);
522 (f1 = rtnl_dereference(*fp)) != NULL;
523 fp = &f1->next)
490 if (f->handle < f1->handle) 524 if (f->handle < f1->handle)
491 break; 525 break;
492 526
493 f->next = f1; 527 rcu_assign_pointer(f->next, f1);
494 tcf_tree_lock(tp); 528 rcu_assign_pointer(*fp, f);
495 *fp = f;
496 529
497 if (old_handle && f->handle != old_handle) { 530 if (fold && fold->handle && f->handle != fold->handle) {
498 th = to_hash(old_handle); 531 th = to_hash(fold->handle);
499 h = from_hash(old_handle >> 16); 532 h = from_hash(fold->handle >> 16);
500 b = head->table[th]; 533 b = rtnl_dereference(head->table[th]);
501 if (b) { 534 if (b) {
502 for (fp = &b->ht[h]; *fp; fp = &(*fp)->next) { 535 fp = &b->ht[h];
503 if (*fp == f) { 536 for (pfp = rtnl_dereference(*fp); pfp;
537 fp = &pfp->next, pfp = rtnl_dereference(*fp)) {
538 if (pfp == f) {
504 *fp = f->next; 539 *fp = f->next;
505 break; 540 break;
506 } 541 }
507 } 542 }
508 } 543 }
509 } 544 }
510 tcf_tree_unlock(tp);
511 545
512 route4_reset_fastmap(tp->q, head, f->id); 546 route4_reset_fastmap(head);
513 *arg = (unsigned long)f; 547 *arg = (unsigned long)f;
548 if (fold)
549 call_rcu(&fold->rcu, route4_delete_filter);
514 return 0; 550 return 0;
515 551
516errout: 552errout:
@@ -520,7 +556,7 @@ errout:
520 556
521static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg) 557static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg)
522{ 558{
523 struct route4_head *head = tp->root; 559 struct route4_head *head = rtnl_dereference(tp->root);
524 unsigned int h, h1; 560 unsigned int h, h1;
525 561
526 if (head == NULL) 562 if (head == NULL)
@@ -530,13 +566,15 @@ static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg)
530 return; 566 return;
531 567
532 for (h = 0; h <= 256; h++) { 568 for (h = 0; h <= 256; h++) {
533 struct route4_bucket *b = head->table[h]; 569 struct route4_bucket *b = rtnl_dereference(head->table[h]);
534 570
535 if (b) { 571 if (b) {
536 for (h1 = 0; h1 <= 32; h1++) { 572 for (h1 = 0; h1 <= 32; h1++) {
537 struct route4_filter *f; 573 struct route4_filter *f;
538 574
539 for (f = b->ht[h1]; f; f = f->next) { 575 for (f = rtnl_dereference(b->ht[h1]);
576 f;
577 f = rtnl_dereference(f->next)) {
540 if (arg->count < arg->skip) { 578 if (arg->count < arg->skip) {
541 arg->count++; 579 arg->count++;
542 continue; 580 continue;