diff options
author | Ross Zwisler <ross.zwisler@linux.intel.com> | 2016-05-20 20:02:26 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-20 20:58:30 -0400 |
commit | 21ef533931f73a8e963a6107aa5ec51b192f28be (patch) | |
tree | bc5066db87ca565cbc35e3535e88b1e5ec1beb41 | |
parent | 7b60e9ad59a31dd98c2f7ef841e2882c2b0e0f3b (diff) |
radix-tree: add support for multi-order iterating
This enables the macros radix_tree_for_each_slot() and friends to be
used with multi-order entries.
The way that this works is that we treat all entries in a given slots[]
array as a single chunk. If the index given to radix_tree_next_chunk()
happens to point us to a sibling entry, we will back up iter->index so
that it points to the canonical entry, and that will be the place where
we start our iteration.
As we're processing a chunk in radix_tree_next_slot(), we process
canonical entries, skip over sibling entries, and restart the chunk
lookup if we find a non-sibling indirect pointer. This drops back to
the radix_tree_next_chunk() code, which will re-walk the tree and look
for another chunk.
This allows us to properly handle multi-order entries mixed with other
entries that are at various heights in the radix tree.
Signed-off-by: Ross Zwisler <ross.zwisler@linux.intel.com>
Signed-off-by: Matthew Wilcox <willy@linux.intel.com>
Cc: Konstantin Khlebnikov <koct9i@gmail.com>
Cc: Kirill Shutemov <kirill.shutemov@linux.intel.com>
Cc: Jan Kara <jack@suse.com>
Cc: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | include/linux/radix-tree.h | 69 | ||||
-rw-r--r-- | lib/radix-tree.c | 66 | ||||
-rw-r--r-- | tools/testing/radix-tree/generated/autoconf.h | 3 | ||||
-rw-r--r-- | tools/testing/radix-tree/linux/kernel.h | 5 |
4 files changed, 102 insertions, 41 deletions
diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index e1512a607709..8558d52e1c7b 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h | |||
@@ -330,8 +330,9 @@ static inline void radix_tree_preload_end(void) | |||
330 | * struct radix_tree_iter - radix tree iterator state | 330 | * struct radix_tree_iter - radix tree iterator state |
331 | * | 331 | * |
332 | * @index: index of current slot | 332 | * @index: index of current slot |
333 | * @next_index: next-to-last index for this chunk | 333 | * @next_index: one beyond the last index for this chunk |
334 | * @tags: bit-mask for tag-iterating | 334 | * @tags: bit-mask for tag-iterating |
335 | * @shift: shift for the node that holds our slots | ||
335 | * | 336 | * |
336 | * This radix tree iterator works in terms of "chunks" of slots. A chunk is a | 337 | * This radix tree iterator works in terms of "chunks" of slots. A chunk is a |
337 | * subinterval of slots contained within one radix tree leaf node. It is | 338 | * subinterval of slots contained within one radix tree leaf node. It is |
@@ -344,8 +345,20 @@ struct radix_tree_iter { | |||
344 | unsigned long index; | 345 | unsigned long index; |
345 | unsigned long next_index; | 346 | unsigned long next_index; |
346 | unsigned long tags; | 347 | unsigned long tags; |
348 | #ifdef CONFIG_RADIX_TREE_MULTIORDER | ||
349 | unsigned int shift; | ||
350 | #endif | ||
347 | }; | 351 | }; |
348 | 352 | ||
353 | static inline unsigned int iter_shift(struct radix_tree_iter *iter) | ||
354 | { | ||
355 | #ifdef CONFIG_RADIX_TREE_MULTIORDER | ||
356 | return iter->shift; | ||
357 | #else | ||
358 | return 0; | ||
359 | #endif | ||
360 | } | ||
361 | |||
349 | #define RADIX_TREE_ITER_TAG_MASK 0x00FF /* tag index in lower byte */ | 362 | #define RADIX_TREE_ITER_TAG_MASK 0x00FF /* tag index in lower byte */ |
350 | #define RADIX_TREE_ITER_TAGGED 0x0100 /* lookup tagged slots */ | 363 | #define RADIX_TREE_ITER_TAGGED 0x0100 /* lookup tagged slots */ |
351 | #define RADIX_TREE_ITER_CONTIG 0x0200 /* stop at first hole */ | 364 | #define RADIX_TREE_ITER_CONTIG 0x0200 /* stop at first hole */ |
@@ -405,6 +418,12 @@ void **radix_tree_iter_retry(struct radix_tree_iter *iter) | |||
405 | return NULL; | 418 | return NULL; |
406 | } | 419 | } |
407 | 420 | ||
421 | static inline unsigned long | ||
422 | __radix_tree_iter_add(struct radix_tree_iter *iter, unsigned long slots) | ||
423 | { | ||
424 | return iter->index + (slots << iter_shift(iter)); | ||
425 | } | ||
426 | |||
408 | /** | 427 | /** |
409 | * radix_tree_iter_next - resume iterating when the chunk may be invalid | 428 | * radix_tree_iter_next - resume iterating when the chunk may be invalid |
410 | * @iter: iterator state | 429 | * @iter: iterator state |
@@ -416,7 +435,7 @@ void **radix_tree_iter_retry(struct radix_tree_iter *iter) | |||
416 | static inline __must_check | 435 | static inline __must_check |
417 | void **radix_tree_iter_next(struct radix_tree_iter *iter) | 436 | void **radix_tree_iter_next(struct radix_tree_iter *iter) |
418 | { | 437 | { |
419 | iter->next_index = iter->index + 1; | 438 | iter->next_index = __radix_tree_iter_add(iter, 1); |
420 | iter->tags = 0; | 439 | iter->tags = 0; |
421 | return NULL; | 440 | return NULL; |
422 | } | 441 | } |
@@ -430,7 +449,12 @@ void **radix_tree_iter_next(struct radix_tree_iter *iter) | |||
430 | static __always_inline long | 449 | static __always_inline long |
431 | radix_tree_chunk_size(struct radix_tree_iter *iter) | 450 | radix_tree_chunk_size(struct radix_tree_iter *iter) |
432 | { | 451 | { |
433 | return iter->next_index - iter->index; | 452 | return (iter->next_index - iter->index) >> iter_shift(iter); |
453 | } | ||
454 | |||
455 | static inline void *indirect_to_ptr(void *ptr) | ||
456 | { | ||
457 | return (void *)((unsigned long)ptr & ~RADIX_TREE_INDIRECT_PTR); | ||
434 | } | 458 | } |
435 | 459 | ||
436 | /** | 460 | /** |
@@ -448,24 +472,51 @@ static __always_inline void ** | |||
448 | radix_tree_next_slot(void **slot, struct radix_tree_iter *iter, unsigned flags) | 472 | radix_tree_next_slot(void **slot, struct radix_tree_iter *iter, unsigned flags) |
449 | { | 473 | { |
450 | if (flags & RADIX_TREE_ITER_TAGGED) { | 474 | if (flags & RADIX_TREE_ITER_TAGGED) { |
475 | void *canon = slot; | ||
476 | |||
451 | iter->tags >>= 1; | 477 | iter->tags >>= 1; |
478 | if (unlikely(!iter->tags)) | ||
479 | return NULL; | ||
480 | while (IS_ENABLED(CONFIG_RADIX_TREE_MULTIORDER) && | ||
481 | radix_tree_is_indirect_ptr(slot[1])) { | ||
482 | if (indirect_to_ptr(slot[1]) == canon) { | ||
483 | iter->tags >>= 1; | ||
484 | iter->index = __radix_tree_iter_add(iter, 1); | ||
485 | slot++; | ||
486 | continue; | ||
487 | } | ||
488 | iter->next_index = __radix_tree_iter_add(iter, 1); | ||
489 | return NULL; | ||
490 | } | ||
452 | if (likely(iter->tags & 1ul)) { | 491 | if (likely(iter->tags & 1ul)) { |
453 | iter->index++; | 492 | iter->index = __radix_tree_iter_add(iter, 1); |
454 | return slot + 1; | 493 | return slot + 1; |
455 | } | 494 | } |
456 | if (!(flags & RADIX_TREE_ITER_CONTIG) && likely(iter->tags)) { | 495 | if (!(flags & RADIX_TREE_ITER_CONTIG)) { |
457 | unsigned offset = __ffs(iter->tags); | 496 | unsigned offset = __ffs(iter->tags); |
458 | 497 | ||
459 | iter->tags >>= offset; | 498 | iter->tags >>= offset; |
460 | iter->index += offset + 1; | 499 | iter->index = __radix_tree_iter_add(iter, offset + 1); |
461 | return slot + offset + 1; | 500 | return slot + offset + 1; |
462 | } | 501 | } |
463 | } else { | 502 | } else { |
464 | long size = radix_tree_chunk_size(iter); | 503 | long count = radix_tree_chunk_size(iter); |
504 | void *canon = slot; | ||
465 | 505 | ||
466 | while (--size > 0) { | 506 | while (--count > 0) { |
467 | slot++; | 507 | slot++; |
468 | iter->index++; | 508 | iter->index = __radix_tree_iter_add(iter, 1); |
509 | |||
510 | if (IS_ENABLED(CONFIG_RADIX_TREE_MULTIORDER) && | ||
511 | radix_tree_is_indirect_ptr(*slot)) { | ||
512 | if (indirect_to_ptr(*slot) == canon) | ||
513 | continue; | ||
514 | else { | ||
515 | iter->next_index = iter->index; | ||
516 | break; | ||
517 | } | ||
518 | } | ||
519 | |||
469 | if (likely(*slot)) | 520 | if (likely(*slot)) |
470 | return slot; | 521 | return slot; |
471 | if (flags & RADIX_TREE_ITER_CONTIG) { | 522 | if (flags & RADIX_TREE_ITER_CONTIG) { |
diff --git a/lib/radix-tree.c b/lib/radix-tree.c index ff460423ff4b..a4da86e40def 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c | |||
@@ -75,11 +75,6 @@ static inline void *ptr_to_indirect(void *ptr) | |||
75 | return (void *)((unsigned long)ptr | RADIX_TREE_INDIRECT_PTR); | 75 | return (void *)((unsigned long)ptr | RADIX_TREE_INDIRECT_PTR); |
76 | } | 76 | } |
77 | 77 | ||
78 | static inline void *indirect_to_ptr(void *ptr) | ||
79 | { | ||
80 | return (void *)((unsigned long)ptr & ~RADIX_TREE_INDIRECT_PTR); | ||
81 | } | ||
82 | |||
83 | #define RADIX_TREE_RETRY ptr_to_indirect(NULL) | 78 | #define RADIX_TREE_RETRY ptr_to_indirect(NULL) |
84 | 79 | ||
85 | #ifdef CONFIG_RADIX_TREE_MULTIORDER | 80 | #ifdef CONFIG_RADIX_TREE_MULTIORDER |
@@ -885,6 +880,14 @@ int radix_tree_tag_get(struct radix_tree_root *root, | |||
885 | } | 880 | } |
886 | EXPORT_SYMBOL(radix_tree_tag_get); | 881 | EXPORT_SYMBOL(radix_tree_tag_get); |
887 | 882 | ||
883 | static inline void __set_iter_shift(struct radix_tree_iter *iter, | ||
884 | unsigned int shift) | ||
885 | { | ||
886 | #ifdef CONFIG_RADIX_TREE_MULTIORDER | ||
887 | iter->shift = shift; | ||
888 | #endif | ||
889 | } | ||
890 | |||
888 | /** | 891 | /** |
889 | * radix_tree_next_chunk - find next chunk of slots for iteration | 892 | * radix_tree_next_chunk - find next chunk of slots for iteration |
890 | * | 893 | * |
@@ -898,7 +901,7 @@ void **radix_tree_next_chunk(struct radix_tree_root *root, | |||
898 | { | 901 | { |
899 | unsigned shift, tag = flags & RADIX_TREE_ITER_TAG_MASK; | 902 | unsigned shift, tag = flags & RADIX_TREE_ITER_TAG_MASK; |
900 | struct radix_tree_node *rnode, *node; | 903 | struct radix_tree_node *rnode, *node; |
901 | unsigned long index, offset, height; | 904 | unsigned long index, offset, maxindex; |
902 | 905 | ||
903 | if ((flags & RADIX_TREE_ITER_TAGGED) && !root_tag_get(root, tag)) | 906 | if ((flags & RADIX_TREE_ITER_TAGGED) && !root_tag_get(root, tag)) |
904 | return NULL; | 907 | return NULL; |
@@ -916,33 +919,39 @@ void **radix_tree_next_chunk(struct radix_tree_root *root, | |||
916 | if (!index && iter->index) | 919 | if (!index && iter->index) |
917 | return NULL; | 920 | return NULL; |
918 | 921 | ||
919 | rnode = rcu_dereference_raw(root->rnode); | 922 | restart: |
923 | shift = radix_tree_load_root(root, &rnode, &maxindex); | ||
924 | if (index > maxindex) | ||
925 | return NULL; | ||
926 | |||
920 | if (radix_tree_is_indirect_ptr(rnode)) { | 927 | if (radix_tree_is_indirect_ptr(rnode)) { |
921 | rnode = indirect_to_ptr(rnode); | 928 | rnode = indirect_to_ptr(rnode); |
922 | } else if (rnode && !index) { | 929 | } else if (rnode) { |
923 | /* Single-slot tree */ | 930 | /* Single-slot tree */ |
924 | iter->index = 0; | 931 | iter->index = index; |
925 | iter->next_index = 1; | 932 | iter->next_index = maxindex + 1; |
926 | iter->tags = 1; | 933 | iter->tags = 1; |
934 | __set_iter_shift(iter, shift); | ||
927 | return (void **)&root->rnode; | 935 | return (void **)&root->rnode; |
928 | } else | 936 | } else |
929 | return NULL; | 937 | return NULL; |
930 | 938 | ||
931 | restart: | 939 | shift -= RADIX_TREE_MAP_SHIFT; |
932 | height = rnode->path & RADIX_TREE_HEIGHT_MASK; | ||
933 | shift = (height - 1) * RADIX_TREE_MAP_SHIFT; | ||
934 | offset = index >> shift; | 940 | offset = index >> shift; |
935 | 941 | ||
936 | /* Index outside of the tree */ | ||
937 | if (offset >= RADIX_TREE_MAP_SIZE) | ||
938 | return NULL; | ||
939 | |||
940 | node = rnode; | 942 | node = rnode; |
941 | while (1) { | 943 | while (1) { |
942 | struct radix_tree_node *slot; | 944 | struct radix_tree_node *slot; |
945 | unsigned new_off = radix_tree_descend(node, &slot, offset); | ||
946 | |||
947 | if (new_off < offset) { | ||
948 | offset = new_off; | ||
949 | index &= ~((RADIX_TREE_MAP_SIZE << shift) - 1); | ||
950 | index |= offset << shift; | ||
951 | } | ||
952 | |||
943 | if ((flags & RADIX_TREE_ITER_TAGGED) ? | 953 | if ((flags & RADIX_TREE_ITER_TAGGED) ? |
944 | !test_bit(offset, node->tags[tag]) : | 954 | !tag_get(node, tag, offset) : !slot) { |
945 | !node->slots[offset]) { | ||
946 | /* Hole detected */ | 955 | /* Hole detected */ |
947 | if (flags & RADIX_TREE_ITER_CONTIG) | 956 | if (flags & RADIX_TREE_ITER_CONTIG) |
948 | return NULL; | 957 | return NULL; |
@@ -954,7 +963,10 @@ restart: | |||
954 | offset + 1); | 963 | offset + 1); |
955 | else | 964 | else |
956 | while (++offset < RADIX_TREE_MAP_SIZE) { | 965 | while (++offset < RADIX_TREE_MAP_SIZE) { |
957 | if (node->slots[offset]) | 966 | void *slot = node->slots[offset]; |
967 | if (is_sibling_entry(node, slot)) | ||
968 | continue; | ||
969 | if (slot) | ||
958 | break; | 970 | break; |
959 | } | 971 | } |
960 | index &= ~((RADIX_TREE_MAP_SIZE << shift) - 1); | 972 | index &= ~((RADIX_TREE_MAP_SIZE << shift) - 1); |
@@ -964,25 +976,23 @@ restart: | |||
964 | return NULL; | 976 | return NULL; |
965 | if (offset == RADIX_TREE_MAP_SIZE) | 977 | if (offset == RADIX_TREE_MAP_SIZE) |
966 | goto restart; | 978 | goto restart; |
979 | slot = rcu_dereference_raw(node->slots[offset]); | ||
967 | } | 980 | } |
968 | 981 | ||
969 | /* This is leaf-node */ | 982 | if ((slot == NULL) || (slot == RADIX_TREE_RETRY)) |
970 | if (!shift) | ||
971 | break; | ||
972 | |||
973 | slot = rcu_dereference_raw(node->slots[offset]); | ||
974 | if (slot == NULL) | ||
975 | goto restart; | 983 | goto restart; |
976 | if (!radix_tree_is_indirect_ptr(slot)) | 984 | if (!radix_tree_is_indirect_ptr(slot)) |
977 | break; | 985 | break; |
986 | |||
978 | node = indirect_to_ptr(slot); | 987 | node = indirect_to_ptr(slot); |
979 | shift -= RADIX_TREE_MAP_SHIFT; | 988 | shift -= RADIX_TREE_MAP_SHIFT; |
980 | offset = (index >> shift) & RADIX_TREE_MAP_MASK; | 989 | offset = (index >> shift) & RADIX_TREE_MAP_MASK; |
981 | } | 990 | } |
982 | 991 | ||
983 | /* Update the iterator state */ | 992 | /* Update the iterator state */ |
984 | iter->index = index; | 993 | iter->index = index & ~((1 << shift) - 1); |
985 | iter->next_index = (index | RADIX_TREE_MAP_MASK) + 1; | 994 | iter->next_index = (index | ((RADIX_TREE_MAP_SIZE << shift) - 1)) + 1; |
995 | __set_iter_shift(iter, shift); | ||
986 | 996 | ||
987 | /* Construct iter->tags bit-mask from node->tags[tag] array */ | 997 | /* Construct iter->tags bit-mask from node->tags[tag] array */ |
988 | if (flags & RADIX_TREE_ITER_TAGGED) { | 998 | if (flags & RADIX_TREE_ITER_TAGGED) { |
diff --git a/tools/testing/radix-tree/generated/autoconf.h b/tools/testing/radix-tree/generated/autoconf.h new file mode 100644 index 000000000000..ad18cf5a2a3a --- /dev/null +++ b/tools/testing/radix-tree/generated/autoconf.h | |||
@@ -0,0 +1,3 @@ | |||
1 | #define CONFIG_RADIX_TREE_MULTIORDER 1 | ||
2 | #define CONFIG_SHMEM 1 | ||
3 | #define CONFIG_SWAP 1 | ||
diff --git a/tools/testing/radix-tree/linux/kernel.h b/tools/testing/radix-tree/linux/kernel.h index 8ea0ed450810..be98a47b4e1b 100644 --- a/tools/testing/radix-tree/linux/kernel.h +++ b/tools/testing/radix-tree/linux/kernel.h | |||
@@ -8,10 +8,7 @@ | |||
8 | #include <limits.h> | 8 | #include <limits.h> |
9 | 9 | ||
10 | #include "../../include/linux/compiler.h" | 10 | #include "../../include/linux/compiler.h" |
11 | 11 | #include "../../../include/linux/kconfig.h" | |
12 | #define CONFIG_RADIX_TREE_MULTIORDER | ||
13 | #define CONFIG_SHMEM | ||
14 | #define CONFIG_SWAP | ||
15 | 12 | ||
16 | #define RADIX_TREE_MAP_SHIFT 3 | 13 | #define RADIX_TREE_MAP_SHIFT 3 |
17 | 14 | ||