diff options
author | Matthew Wilcox <mawilcox@microsoft.com> | 2016-12-14 18:09:07 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-12-14 19:04:10 -0500 |
commit | a90eb3a2a405cf7e96093ed531a285067dfdbc9d (patch) | |
tree | 5bcc4f8c9da0dabf082a6cf411ba9558ffe63d13 | |
parent | 2791653a6814d170fa893344618563a7b1da95c6 (diff) |
radix-tree: fix replacement for multiorder entries
When replacing an entry with NULL, we need to delete any sibling
entries. Also account deleting exceptional entries properly. Also fix
a bug with radix_tree_iter_replace() where we would fail to remove
entirely freed nodes. Also fix accounting bug when switching between
normal and exceptional entries with replace_slot. Also add testcases
for all these bugs.
Link: http://lkml.kernel.org/r/1480369871-5271-61-git-send-email-mawilcox@linuxonhyperv.com
Signed-off-by: Matthew Wilcox <mawilcox@microsoft.com>
Tested-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Konstantin Khlebnikov <koct9i@gmail.com>
Cc: Ross Zwisler <ross.zwisler@linux.intel.com>
Cc: Matthew Wilcox <mawilcox@microsoft.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | lib/radix-tree.c | 60 | ||||
-rw-r--r-- | tools/testing/radix-tree/multiorder.c | 87 |
2 files changed, 119 insertions, 28 deletions
diff --git a/lib/radix-tree.c b/lib/radix-tree.c index be1183e62590..d09c17dd60ae 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c | |||
@@ -977,6 +977,24 @@ void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index) | |||
977 | } | 977 | } |
978 | EXPORT_SYMBOL(radix_tree_lookup); | 978 | EXPORT_SYMBOL(radix_tree_lookup); |
979 | 979 | ||
980 | static inline int slot_count(struct radix_tree_node *node, | ||
981 | void **slot) | ||
982 | { | ||
983 | int n = 1; | ||
984 | #ifdef CONFIG_RADIX_TREE_MULTIORDER | ||
985 | void *ptr = node_to_entry(slot); | ||
986 | unsigned offset = get_slot_offset(node, slot); | ||
987 | int i; | ||
988 | |||
989 | for (i = 1; offset + i < RADIX_TREE_MAP_SIZE; i++) { | ||
990 | if (node->slots[offset + i] != ptr) | ||
991 | break; | ||
992 | n++; | ||
993 | } | ||
994 | #endif | ||
995 | return n; | ||
996 | } | ||
997 | |||
980 | static void replace_slot(struct radix_tree_root *root, | 998 | static void replace_slot(struct radix_tree_root *root, |
981 | struct radix_tree_node *node, | 999 | struct radix_tree_node *node, |
982 | void **slot, void *item, | 1000 | void **slot, void *item, |
@@ -995,12 +1013,35 @@ static void replace_slot(struct radix_tree_root *root, | |||
995 | 1013 | ||
996 | if (node) { | 1014 | if (node) { |
997 | node->count += count; | 1015 | node->count += count; |
998 | node->exceptional += exceptional; | 1016 | if (exceptional) { |
1017 | exceptional *= slot_count(node, slot); | ||
1018 | node->exceptional += exceptional; | ||
1019 | } | ||
999 | } | 1020 | } |
1000 | 1021 | ||
1001 | rcu_assign_pointer(*slot, item); | 1022 | rcu_assign_pointer(*slot, item); |
1002 | } | 1023 | } |
1003 | 1024 | ||
1025 | static inline void delete_sibling_entries(struct radix_tree_node *node, | ||
1026 | void **slot) | ||
1027 | { | ||
1028 | #ifdef CONFIG_RADIX_TREE_MULTIORDER | ||
1029 | bool exceptional = radix_tree_exceptional_entry(*slot); | ||
1030 | void *ptr = node_to_entry(slot); | ||
1031 | unsigned offset = get_slot_offset(node, slot); | ||
1032 | int i; | ||
1033 | |||
1034 | for (i = 1; offset + i < RADIX_TREE_MAP_SIZE; i++) { | ||
1035 | if (node->slots[offset + i] != ptr) | ||
1036 | break; | ||
1037 | node->slots[offset + i] = NULL; | ||
1038 | node->count--; | ||
1039 | if (exceptional) | ||
1040 | node->exceptional--; | ||
1041 | } | ||
1042 | #endif | ||
1043 | } | ||
1044 | |||
1004 | /** | 1045 | /** |
1005 | * __radix_tree_replace - replace item in a slot | 1046 | * __radix_tree_replace - replace item in a slot |
1006 | * @root: radix tree root | 1047 | * @root: radix tree root |
@@ -1018,6 +1059,8 @@ void __radix_tree_replace(struct radix_tree_root *root, | |||
1018 | void **slot, void *item, | 1059 | void **slot, void *item, |
1019 | radix_tree_update_node_t update_node, void *private) | 1060 | radix_tree_update_node_t update_node, void *private) |
1020 | { | 1061 | { |
1062 | if (!item) | ||
1063 | delete_sibling_entries(node, slot); | ||
1021 | /* | 1064 | /* |
1022 | * This function supports replacing exceptional entries and | 1065 | * This function supports replacing exceptional entries and |
1023 | * deleting entries, but that needs accounting against the | 1066 | * deleting entries, but that needs accounting against the |
@@ -1794,20 +1837,6 @@ void __radix_tree_delete_node(struct radix_tree_root *root, | |||
1794 | delete_node(root, node, NULL, NULL); | 1837 | delete_node(root, node, NULL, NULL); |
1795 | } | 1838 | } |
1796 | 1839 | ||
1797 | static inline void delete_sibling_entries(struct radix_tree_node *node, | ||
1798 | void *ptr, unsigned offset) | ||
1799 | { | ||
1800 | #ifdef CONFIG_RADIX_TREE_MULTIORDER | ||
1801 | int i; | ||
1802 | for (i = 1; offset + i < RADIX_TREE_MAP_SIZE; i++) { | ||
1803 | if (node->slots[offset + i] != ptr) | ||
1804 | break; | ||
1805 | node->slots[offset + i] = NULL; | ||
1806 | node->count--; | ||
1807 | } | ||
1808 | #endif | ||
1809 | } | ||
1810 | |||
1811 | /** | 1840 | /** |
1812 | * radix_tree_delete_item - delete an item from a radix tree | 1841 | * radix_tree_delete_item - delete an item from a radix tree |
1813 | * @root: radix tree root | 1842 | * @root: radix tree root |
@@ -1847,7 +1876,6 @@ void *radix_tree_delete_item(struct radix_tree_root *root, | |||
1847 | for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) | 1876 | for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) |
1848 | node_tag_clear(root, node, tag, offset); | 1877 | node_tag_clear(root, node, tag, offset); |
1849 | 1878 | ||
1850 | delete_sibling_entries(node, node_to_entry(slot), offset); | ||
1851 | __radix_tree_replace(root, node, slot, NULL, NULL, NULL); | 1879 | __radix_tree_replace(root, node, slot, NULL, NULL, NULL); |
1852 | 1880 | ||
1853 | return entry; | 1881 | return entry; |
diff --git a/tools/testing/radix-tree/multiorder.c b/tools/testing/radix-tree/multiorder.c index 5421f015f46c..9757b8928bd4 100644 --- a/tools/testing/radix-tree/multiorder.c +++ b/tools/testing/radix-tree/multiorder.c | |||
@@ -410,8 +410,6 @@ static void __multiorder_split(int old_order, int new_order) | |||
410 | RADIX_TREE(tree, GFP_ATOMIC); | 410 | RADIX_TREE(tree, GFP_ATOMIC); |
411 | void **slot; | 411 | void **slot; |
412 | struct radix_tree_iter iter; | 412 | struct radix_tree_iter iter; |
413 | struct radix_tree_node *node; | ||
414 | void *item; | ||
415 | unsigned alloc; | 413 | unsigned alloc; |
416 | 414 | ||
417 | radix_tree_preload(GFP_KERNEL); | 415 | radix_tree_preload(GFP_KERNEL); |
@@ -434,58 +432,122 @@ static void __multiorder_split(int old_order, int new_order) | |||
434 | radix_tree_preload_end(); | 432 | radix_tree_preload_end(); |
435 | 433 | ||
436 | item_kill_tree(&tree); | 434 | item_kill_tree(&tree); |
435 | } | ||
436 | |||
437 | static void __multiorder_split2(int old_order, int new_order) | ||
438 | { | ||
439 | RADIX_TREE(tree, GFP_KERNEL); | ||
440 | void **slot; | ||
441 | struct radix_tree_iter iter; | ||
442 | struct radix_tree_node *node; | ||
443 | void *item; | ||
437 | 444 | ||
438 | radix_tree_preload(GFP_KERNEL); | ||
439 | __radix_tree_insert(&tree, 0, old_order, (void *)0x12); | 445 | __radix_tree_insert(&tree, 0, old_order, (void *)0x12); |
440 | radix_tree_preload_end(); | ||
441 | 446 | ||
442 | item = __radix_tree_lookup(&tree, 0, &node, NULL); | 447 | item = __radix_tree_lookup(&tree, 0, &node, NULL); |
443 | assert(item == (void *)0x12); | 448 | assert(item == (void *)0x12); |
444 | assert(node->exceptional > 0); | 449 | assert(node->exceptional > 0); |
445 | 450 | ||
446 | radix_tree_split_preload(old_order, new_order, GFP_KERNEL); | ||
447 | radix_tree_split(&tree, 0, new_order); | 451 | radix_tree_split(&tree, 0, new_order); |
448 | radix_tree_for_each_slot(slot, &tree, &iter, 0) { | 452 | radix_tree_for_each_slot(slot, &tree, &iter, 0) { |
449 | radix_tree_iter_replace(&tree, &iter, slot, | 453 | radix_tree_iter_replace(&tree, &iter, slot, |
450 | item_create(iter.index, new_order)); | 454 | item_create(iter.index, new_order)); |
451 | } | 455 | } |
452 | radix_tree_preload_end(); | ||
453 | 456 | ||
454 | item = __radix_tree_lookup(&tree, 0, &node, NULL); | 457 | item = __radix_tree_lookup(&tree, 0, &node, NULL); |
455 | assert(item != (void *)0x12); | 458 | assert(item != (void *)0x12); |
456 | assert(node->exceptional == 0); | 459 | assert(node->exceptional == 0); |
457 | 460 | ||
458 | item_kill_tree(&tree); | 461 | item_kill_tree(&tree); |
462 | } | ||
463 | |||
464 | static void __multiorder_split3(int old_order, int new_order) | ||
465 | { | ||
466 | RADIX_TREE(tree, GFP_KERNEL); | ||
467 | void **slot; | ||
468 | struct radix_tree_iter iter; | ||
469 | struct radix_tree_node *node; | ||
470 | void *item; | ||
459 | 471 | ||
460 | radix_tree_preload(GFP_KERNEL); | ||
461 | __radix_tree_insert(&tree, 0, old_order, (void *)0x12); | 472 | __radix_tree_insert(&tree, 0, old_order, (void *)0x12); |
462 | radix_tree_preload_end(); | ||
463 | 473 | ||
464 | item = __radix_tree_lookup(&tree, 0, &node, NULL); | 474 | item = __radix_tree_lookup(&tree, 0, &node, NULL); |
465 | assert(item == (void *)0x12); | 475 | assert(item == (void *)0x12); |
466 | assert(node->exceptional > 0); | 476 | assert(node->exceptional > 0); |
467 | 477 | ||
468 | radix_tree_split_preload(old_order, new_order, GFP_KERNEL); | ||
469 | radix_tree_split(&tree, 0, new_order); | 478 | radix_tree_split(&tree, 0, new_order); |
470 | radix_tree_for_each_slot(slot, &tree, &iter, 0) { | 479 | radix_tree_for_each_slot(slot, &tree, &iter, 0) { |
471 | radix_tree_iter_replace(&tree, &iter, slot, (void *)0x16); | 480 | radix_tree_iter_replace(&tree, &iter, slot, (void *)0x16); |
472 | } | 481 | } |
473 | radix_tree_preload_end(); | ||
474 | 482 | ||
475 | item = __radix_tree_lookup(&tree, 0, &node, NULL); | 483 | item = __radix_tree_lookup(&tree, 0, &node, NULL); |
476 | assert(item == (void *)0x16); | 484 | assert(item == (void *)0x16); |
477 | assert(node->exceptional > 0); | 485 | assert(node->exceptional > 0); |
478 | 486 | ||
479 | item_kill_tree(&tree); | 487 | item_kill_tree(&tree); |
488 | |||
489 | __radix_tree_insert(&tree, 0, old_order, (void *)0x12); | ||
490 | |||
491 | item = __radix_tree_lookup(&tree, 0, &node, NULL); | ||
492 | assert(item == (void *)0x12); | ||
493 | assert(node->exceptional > 0); | ||
494 | |||
495 | radix_tree_split(&tree, 0, new_order); | ||
496 | radix_tree_for_each_slot(slot, &tree, &iter, 0) { | ||
497 | if (iter.index == (1 << new_order)) | ||
498 | radix_tree_iter_replace(&tree, &iter, slot, | ||
499 | (void *)0x16); | ||
500 | else | ||
501 | radix_tree_iter_replace(&tree, &iter, slot, NULL); | ||
502 | } | ||
503 | |||
504 | item = __radix_tree_lookup(&tree, 1 << new_order, &node, NULL); | ||
505 | assert(item == (void *)0x16); | ||
506 | assert(node->count == node->exceptional); | ||
507 | do { | ||
508 | node = node->parent; | ||
509 | if (!node) | ||
510 | break; | ||
511 | assert(node->count == 1); | ||
512 | assert(node->exceptional == 0); | ||
513 | } while (1); | ||
514 | |||
515 | item_kill_tree(&tree); | ||
480 | } | 516 | } |
481 | 517 | ||
482 | static void multiorder_split(void) | 518 | static void multiorder_split(void) |
483 | { | 519 | { |
484 | int i, j; | 520 | int i, j; |
485 | 521 | ||
486 | for (i = 9; i < 19; i++) | 522 | for (i = 3; i < 11; i++) |
487 | for (j = 0; j < i; j++) | 523 | for (j = 0; j < i; j++) { |
488 | __multiorder_split(i, j); | 524 | __multiorder_split(i, j); |
525 | __multiorder_split2(i, j); | ||
526 | __multiorder_split3(i, j); | ||
527 | } | ||
528 | } | ||
529 | |||
530 | static void multiorder_account(void) | ||
531 | { | ||
532 | RADIX_TREE(tree, GFP_KERNEL); | ||
533 | struct radix_tree_node *node; | ||
534 | void **slot; | ||
535 | |||
536 | item_insert_order(&tree, 0, 5); | ||
537 | |||
538 | __radix_tree_insert(&tree, 1 << 5, 5, (void *)0x12); | ||
539 | __radix_tree_lookup(&tree, 0, &node, NULL); | ||
540 | assert(node->count == node->exceptional * 2); | ||
541 | radix_tree_delete(&tree, 1 << 5); | ||
542 | assert(node->exceptional == 0); | ||
543 | |||
544 | __radix_tree_insert(&tree, 1 << 5, 5, (void *)0x12); | ||
545 | __radix_tree_lookup(&tree, 1 << 5, &node, &slot); | ||
546 | assert(node->count == node->exceptional * 2); | ||
547 | __radix_tree_replace(&tree, node, slot, NULL, NULL, NULL); | ||
548 | assert(node->exceptional == 0); | ||
549 | |||
550 | item_kill_tree(&tree); | ||
489 | } | 551 | } |
490 | 552 | ||
491 | void multiorder_checks(void) | 553 | void multiorder_checks(void) |
@@ -507,6 +569,7 @@ void multiorder_checks(void) | |||
507 | multiorder_tagged_iteration(); | 569 | multiorder_tagged_iteration(); |
508 | multiorder_join(); | 570 | multiorder_join(); |
509 | multiorder_split(); | 571 | multiorder_split(); |
572 | multiorder_account(); | ||
510 | 573 | ||
511 | radix_tree_cpu_dead(0); | 574 | radix_tree_cpu_dead(0); |
512 | } | 575 | } |