aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/fib_trie.c
diff options
context:
space:
mode:
authorAlexander Duyck <alexander.h.duyck@redhat.com>2015-01-22 18:51:14 -0500
committerDavid S. Miller <davem@davemloft.net>2015-01-25 17:47:15 -0500
commit69fa57b1e42c171599d53486839c3d58f7ed8eec (patch)
tree84113712d81670dcd2d685d9a6541a3ada9b2858 /net/ipv4/fib_trie.c
parentb3832117b4b61374fac08692f1b1a620088342dd (diff)
fib_trie: Fix RCU bug and merge similar bits of inflate/halve
This patch addresses two issues. The first issue is the fact that I believe I had the RCU freeing sequence slightly out of order. As a result we could get into an issue if a caller went into a child of a child of the new node, then backtraced into the to be freed parent, and then attempted to access a child of a child that may have been consumed in a resize of one of the new nodes children. To resolve this I have moved the resize after we have freed the oldtnode. The only side effect of this is that we will now be calling resize on more nodes in the case of inflate due to the fact that we don't have a good way to test to see if a full_tnode on the new node was there before or after the allocation. This should have minimal impact however since the node should already be correctly size so it is just the cost of calling should_inflate that we will be taking on the node which is only a couple of cycles. The second issue is the fact that inflate and halve were essentially doing the same thing after the new node was added to the trie replacing the old one. As such it wasn't really necessary to keep the code in both functions so I have split it out into two other functions, called replace and update_children. Signed-off-by: Alexander Duyck <alexander.h.duyck@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/fib_trie.c')
-rw-r--r--net/ipv4/fib_trie.c157
1 files changed, 73 insertions, 84 deletions
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index dea2f80e08c3..7e9031739f06 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -396,8 +396,30 @@ static void put_child(struct tnode *tn, unsigned long i, struct tnode *n)
396 rcu_assign_pointer(tn->child[i], n); 396 rcu_assign_pointer(tn->child[i], n);
397} 397}
398 398
399static void put_child_root(struct tnode *tp, struct trie *t, 399static void update_children(struct tnode *tn)
400 t_key key, struct tnode *n) 400{
401 unsigned long i;
402
403 /* update all of the child parent pointers */
404 for (i = tnode_child_length(tn); i;) {
405 struct tnode *inode = tnode_get_child(tn, --i);
406
407 if (!inode)
408 continue;
409
410 /* Either update the children of a tnode that
411 * already belongs to us or update the child
412 * to point to ourselves.
413 */
414 if (node_parent(inode) == tn)
415 update_children(inode);
416 else
417 node_set_parent(inode, tn);
418 }
419}
420
421static inline void put_child_root(struct tnode *tp, struct trie *t,
422 t_key key, struct tnode *n)
401{ 423{
402 if (tp) 424 if (tp)
403 put_child(tp, get_index(key, tp), n); 425 put_child(tp, get_index(key, tp), n);
@@ -434,10 +456,35 @@ static void tnode_free(struct tnode *tn)
434 } 456 }
435} 457}
436 458
459static void replace(struct trie *t, struct tnode *oldtnode, struct tnode *tn)
460{
461 struct tnode *tp = node_parent(oldtnode);
462 unsigned long i;
463
464 /* setup the parent pointer out of and back into this node */
465 NODE_INIT_PARENT(tn, tp);
466 put_child_root(tp, t, tn->key, tn);
467
468 /* update all of the child parent pointers */
469 update_children(tn);
470
471 /* all pointers should be clean so we are done */
472 tnode_free(oldtnode);
473
474 /* resize children now that oldtnode is freed */
475 for (i = tnode_child_length(tn); i;) {
476 struct tnode *inode = tnode_get_child(tn, --i);
477
478 /* resize child node */
479 if (tnode_full(tn, inode))
480 resize(t, inode);
481 }
482}
483
437static int inflate(struct trie *t, struct tnode *oldtnode) 484static int inflate(struct trie *t, struct tnode *oldtnode)
438{ 485{
439 struct tnode *inode, *node0, *node1, *tn, *tp; 486 struct tnode *tn;
440 unsigned long i, j, k; 487 unsigned long i;
441 t_key m; 488 t_key m;
442 489
443 pr_debug("In inflate\n"); 490 pr_debug("In inflate\n");
@@ -446,13 +493,18 @@ static int inflate(struct trie *t, struct tnode *oldtnode)
446 if (!tn) 493 if (!tn)
447 return -ENOMEM; 494 return -ENOMEM;
448 495
496 /* prepare oldtnode to be freed */
497 tnode_free_init(oldtnode);
498
449 /* Assemble all of the pointers in our cluster, in this case that 499 /* Assemble all of the pointers in our cluster, in this case that
450 * represents all of the pointers out of our allocated nodes that 500 * represents all of the pointers out of our allocated nodes that
451 * point to existing tnodes and the links between our allocated 501 * point to existing tnodes and the links between our allocated
452 * nodes. 502 * nodes.
453 */ 503 */
454 for (i = tnode_child_length(oldtnode), m = 1u << tn->pos; i;) { 504 for (i = tnode_child_length(oldtnode), m = 1u << tn->pos; i;) {
455 inode = tnode_get_child(oldtnode, --i); 505 struct tnode *inode = tnode_get_child(oldtnode, --i);
506 struct tnode *node0, *node1;
507 unsigned long j, k;
456 508
457 /* An empty child */ 509 /* An empty child */
458 if (inode == NULL) 510 if (inode == NULL)
@@ -464,6 +516,9 @@ static int inflate(struct trie *t, struct tnode *oldtnode)
464 continue; 516 continue;
465 } 517 }
466 518
519 /* drop the node in the old tnode free list */
520 tnode_free_append(oldtnode, inode);
521
467 /* An internal node with two children */ 522 /* An internal node with two children */
468 if (inode->bits == 1) { 523 if (inode->bits == 1) {
469 put_child(tn, 2 * i + 1, tnode_get_child(inode, 1)); 524 put_child(tn, 2 * i + 1, tnode_get_child(inode, 1));
@@ -488,9 +543,9 @@ static int inflate(struct trie *t, struct tnode *oldtnode)
488 node1 = tnode_new(inode->key | m, inode->pos, inode->bits - 1); 543 node1 = tnode_new(inode->key | m, inode->pos, inode->bits - 1);
489 if (!node1) 544 if (!node1)
490 goto nomem; 545 goto nomem;
491 tnode_free_append(tn, node1); 546 node0 = tnode_new(inode->key, inode->pos, inode->bits - 1);
492 547
493 node0 = tnode_new(inode->key & ~m, inode->pos, inode->bits - 1); 548 tnode_free_append(tn, node1);
494 if (!node0) 549 if (!node0)
495 goto nomem; 550 goto nomem;
496 tnode_free_append(tn, node0); 551 tnode_free_append(tn, node0);
@@ -512,53 +567,9 @@ static int inflate(struct trie *t, struct tnode *oldtnode)
512 put_child(tn, 2 * i, node0); 567 put_child(tn, 2 * i, node0);
513 } 568 }
514 569
515 /* setup the parent pointer into and out of this node */ 570 /* setup the parent pointers into and out of this node */
516 tp = node_parent(oldtnode); 571 replace(t, oldtnode, tn);
517 NODE_INIT_PARENT(tn, tp);
518 put_child_root(tp, t, tn->key, tn);
519
520 /* prepare oldtnode to be freed */
521 tnode_free_init(oldtnode);
522
523 /* update all child nodes parent pointers to route to us */
524 for (i = tnode_child_length(oldtnode); i;) {
525 inode = tnode_get_child(oldtnode, --i);
526
527 /* A leaf or an internal node with skipped bits */
528 if (!tnode_full(oldtnode, inode)) {
529 node_set_parent(inode, tn);
530 continue;
531 }
532
533 /* drop the node in the old tnode free list */
534 tnode_free_append(oldtnode, inode);
535
536 /* fetch new nodes */
537 node1 = tnode_get_child(tn, 2 * i + 1);
538 node0 = tnode_get_child(tn, 2 * i);
539 572
540 /* bits == 1 then node0 and node1 represent inode's children */
541 if (inode->bits == 1) {
542 node_set_parent(node1, tn);
543 node_set_parent(node0, tn);
544 continue;
545 }
546
547 /* update parent pointers in child node's children */
548 for (k = tnode_child_length(inode), j = k / 2; j;) {
549 node_set_parent(tnode_get_child(inode, --k), node1);
550 node_set_parent(tnode_get_child(inode, --j), node0);
551 node_set_parent(tnode_get_child(inode, --k), node1);
552 node_set_parent(tnode_get_child(inode, --j), node0);
553 }
554
555 /* resize child nodes */
556 resize(t, node1);
557 resize(t, node0);
558 }
559
560 /* we completed without error, prepare to free old node */
561 tnode_free(oldtnode);
562 return 0; 573 return 0;
563nomem: 574nomem:
564 /* all pointers should be clean so we are done */ 575 /* all pointers should be clean so we are done */
@@ -568,7 +579,7 @@ nomem:
568 579
569static int halve(struct trie *t, struct tnode *oldtnode) 580static int halve(struct trie *t, struct tnode *oldtnode)
570{ 581{
571 struct tnode *tn, *tp, *inode, *node0, *node1; 582 struct tnode *tn;
572 unsigned long i; 583 unsigned long i;
573 584
574 pr_debug("In halve\n"); 585 pr_debug("In halve\n");
@@ -577,14 +588,18 @@ static int halve(struct trie *t, struct tnode *oldtnode)
577 if (!tn) 588 if (!tn)
578 return -ENOMEM; 589 return -ENOMEM;
579 590
591 /* prepare oldtnode to be freed */
592 tnode_free_init(oldtnode);
593
580 /* Assemble all of the pointers in our cluster, in this case that 594 /* Assemble all of the pointers in our cluster, in this case that
581 * represents all of the pointers out of our allocated nodes that 595 * represents all of the pointers out of our allocated nodes that
582 * point to existing tnodes and the links between our allocated 596 * point to existing tnodes and the links between our allocated
583 * nodes. 597 * nodes.
584 */ 598 */
585 for (i = tnode_child_length(oldtnode); i;) { 599 for (i = tnode_child_length(oldtnode); i;) {
586 node1 = tnode_get_child(oldtnode, --i); 600 struct tnode *node1 = tnode_get_child(oldtnode, --i);
587 node0 = tnode_get_child(oldtnode, --i); 601 struct tnode *node0 = tnode_get_child(oldtnode, --i);
602 struct tnode *inode;
588 603
589 /* At least one of the children is empty */ 604 /* At least one of the children is empty */
590 if (!node1 || !node0) { 605 if (!node1 || !node0) {
@@ -609,34 +624,8 @@ static int halve(struct trie *t, struct tnode *oldtnode)
609 put_child(tn, i / 2, inode); 624 put_child(tn, i / 2, inode);
610 } 625 }
611 626
612 /* setup the parent pointer out of and back into this node */ 627 /* setup the parent pointers into and out of this node */
613 tp = node_parent(oldtnode); 628 replace(t, oldtnode, tn);
614 NODE_INIT_PARENT(tn, tp);
615 put_child_root(tp, t, tn->key, tn);
616
617 /* prepare oldtnode to be freed */
618 tnode_free_init(oldtnode);
619
620 /* update all of the child parent pointers */
621 for (i = tnode_child_length(tn); i;) {
622 inode = tnode_get_child(tn, --i);
623
624 /* only new tnodes will be considered "full" nodes */
625 if (!tnode_full(tn, inode)) {
626 node_set_parent(inode, tn);
627 continue;
628 }
629
630 /* Two nonempty children */
631 node_set_parent(tnode_get_child(inode, 1), inode);
632 node_set_parent(tnode_get_child(inode, 0), inode);
633
634 /* resize child node */
635 resize(t, inode);
636 }
637
638 /* all pointers should be clean so we are done */
639 tnode_free(oldtnode);
640 629
641 return 0; 630 return 0;
642} 631}