|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | #include <stdlib.h> | 
|  | #include <assert.h> | 
|  | #include <stdio.h> | 
|  | #include <linux/types.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/bitops.h> | 
|  |  | 
|  | #include "test.h" | 
|  |  | 
|  | struct item * | 
|  | item_tag_set(struct radix_tree_root *root, unsigned long index, int tag) | 
|  | { | 
|  | return radix_tree_tag_set(root, index, tag); | 
|  | } | 
|  |  | 
|  | struct item * | 
|  | item_tag_clear(struct radix_tree_root *root, unsigned long index, int tag) | 
|  | { | 
|  | return radix_tree_tag_clear(root, index, tag); | 
|  | } | 
|  |  | 
|  | int item_tag_get(struct radix_tree_root *root, unsigned long index, int tag) | 
|  | { | 
|  | return radix_tree_tag_get(root, index, tag); | 
|  | } | 
|  |  | 
|  | int __item_insert(struct radix_tree_root *root, struct item *item) | 
|  | { | 
|  | return __radix_tree_insert(root, item->index, item->order, item); | 
|  | } | 
|  |  | 
|  | struct item *item_create(unsigned long index, unsigned int order) | 
|  | { | 
|  | struct item *ret = malloc(sizeof(*ret)); | 
|  |  | 
|  | ret->index = index; | 
|  | ret->order = order; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int item_insert_order(struct radix_tree_root *root, unsigned long index, | 
|  | unsigned order) | 
|  | { | 
|  | struct item *item = item_create(index, order); | 
|  | int err = __item_insert(root, item); | 
|  | if (err) | 
|  | free(item); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | int item_insert(struct radix_tree_root *root, unsigned long index) | 
|  | { | 
|  | return item_insert_order(root, index, 0); | 
|  | } | 
|  |  | 
|  | void item_sanity(struct item *item, unsigned long index) | 
|  | { | 
|  | unsigned long mask; | 
|  | assert(!radix_tree_is_internal_node(item)); | 
|  | assert(item->order < BITS_PER_LONG); | 
|  | mask = (1UL << item->order) - 1; | 
|  | assert((item->index | mask) == (index | mask)); | 
|  | } | 
|  |  | 
|  | int item_delete(struct radix_tree_root *root, unsigned long index) | 
|  | { | 
|  | struct item *item = radix_tree_delete(root, index); | 
|  |  | 
|  | if (item) { | 
|  | item_sanity(item, index); | 
|  | free(item); | 
|  | return 1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void item_free_rcu(struct rcu_head *head) | 
|  | { | 
|  | struct item *item = container_of(head, struct item, rcu_head); | 
|  |  | 
|  | free(item); | 
|  | } | 
|  |  | 
|  | int item_delete_rcu(struct radix_tree_root *root, unsigned long index) | 
|  | { | 
|  | struct item *item = radix_tree_delete(root, index); | 
|  |  | 
|  | if (item) { | 
|  | item_sanity(item, index); | 
|  | call_rcu(&item->rcu_head, item_free_rcu); | 
|  | return 1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void item_check_present(struct radix_tree_root *root, unsigned long index) | 
|  | { | 
|  | struct item *item; | 
|  |  | 
|  | item = radix_tree_lookup(root, index); | 
|  | assert(item != NULL); | 
|  | item_sanity(item, index); | 
|  | } | 
|  |  | 
|  | struct item *item_lookup(struct radix_tree_root *root, unsigned long index) | 
|  | { | 
|  | return radix_tree_lookup(root, index); | 
|  | } | 
|  |  | 
|  | void item_check_absent(struct radix_tree_root *root, unsigned long index) | 
|  | { | 
|  | struct item *item; | 
|  |  | 
|  | item = radix_tree_lookup(root, index); | 
|  | assert(item == NULL); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Scan only the passed (start, start+nr] for present items | 
|  | */ | 
|  | void item_gang_check_present(struct radix_tree_root *root, | 
|  | unsigned long start, unsigned long nr, | 
|  | int chunk, int hop) | 
|  | { | 
|  | struct item *items[chunk]; | 
|  | unsigned long into; | 
|  |  | 
|  | for (into = 0; into < nr; ) { | 
|  | int nfound; | 
|  | int nr_to_find = chunk; | 
|  | int i; | 
|  |  | 
|  | if (nr_to_find > (nr - into)) | 
|  | nr_to_find = nr - into; | 
|  |  | 
|  | nfound = radix_tree_gang_lookup(root, (void **)items, | 
|  | start + into, nr_to_find); | 
|  | assert(nfound == nr_to_find); | 
|  | for (i = 0; i < nfound; i++) | 
|  | assert(items[i]->index == start + into + i); | 
|  | into += hop; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Scan the entire tree, only expecting present items (start, start+nr] | 
|  | */ | 
|  | void item_full_scan(struct radix_tree_root *root, unsigned long start, | 
|  | unsigned long nr, int chunk) | 
|  | { | 
|  | struct item *items[chunk]; | 
|  | unsigned long into = 0; | 
|  | unsigned long this_index = start; | 
|  | int nfound; | 
|  | int i; | 
|  |  | 
|  | //	printf("%s(0x%08lx, 0x%08lx, %d)\n", __FUNCTION__, start, nr, chunk); | 
|  |  | 
|  | while ((nfound = radix_tree_gang_lookup(root, (void **)items, into, | 
|  | chunk))) { | 
|  | //		printf("At 0x%08lx, nfound=%d\n", into, nfound); | 
|  | for (i = 0; i < nfound; i++) { | 
|  | assert(items[i]->index == this_index); | 
|  | this_index++; | 
|  | } | 
|  | //		printf("Found 0x%08lx->0x%08lx\n", | 
|  | //			items[0]->index, items[nfound-1]->index); | 
|  | into = this_index; | 
|  | } | 
|  | if (chunk) | 
|  | assert(this_index == start + nr); | 
|  | nfound = radix_tree_gang_lookup(root, (void **)items, | 
|  | this_index, chunk); | 
|  | assert(nfound == 0); | 
|  | } | 
|  |  | 
|  | /* Use the same pattern as tag_pages_for_writeback() in mm/page-writeback.c */ | 
|  | int tag_tagged_items(struct radix_tree_root *root, pthread_mutex_t *lock, | 
|  | unsigned long start, unsigned long end, unsigned batch, | 
|  | unsigned iftag, unsigned thentag) | 
|  | { | 
|  | unsigned long tagged = 0; | 
|  | struct radix_tree_iter iter; | 
|  | void **slot; | 
|  |  | 
|  | if (batch == 0) | 
|  | batch = 1; | 
|  |  | 
|  | if (lock) | 
|  | pthread_mutex_lock(lock); | 
|  | radix_tree_for_each_tagged(slot, root, &iter, start, iftag) { | 
|  | if (iter.index > end) | 
|  | break; | 
|  | radix_tree_iter_tag_set(root, &iter, thentag); | 
|  | tagged++; | 
|  | if ((tagged % batch) != 0) | 
|  | continue; | 
|  | slot = radix_tree_iter_resume(slot, &iter); | 
|  | if (lock) { | 
|  | pthread_mutex_unlock(lock); | 
|  | rcu_barrier(); | 
|  | pthread_mutex_lock(lock); | 
|  | } | 
|  | } | 
|  | if (lock) | 
|  | pthread_mutex_unlock(lock); | 
|  |  | 
|  | return tagged; | 
|  | } | 
|  |  | 
|  | /* Use the same pattern as find_swap_entry() in mm/shmem.c */ | 
|  | unsigned long find_item(struct radix_tree_root *root, void *item) | 
|  | { | 
|  | struct radix_tree_iter iter; | 
|  | void **slot; | 
|  | unsigned long found = -1; | 
|  | unsigned long checked = 0; | 
|  |  | 
|  | radix_tree_for_each_slot(slot, root, &iter, 0) { | 
|  | if (*slot == item) { | 
|  | found = iter.index; | 
|  | break; | 
|  | } | 
|  | checked++; | 
|  | if ((checked % 4) != 0) | 
|  | continue; | 
|  | slot = radix_tree_iter_resume(slot, &iter); | 
|  | } | 
|  |  | 
|  | return found; | 
|  | } | 
|  |  | 
|  | static int verify_node(struct radix_tree_node *slot, unsigned int tag, | 
|  | int tagged) | 
|  | { | 
|  | int anyset = 0; | 
|  | int i; | 
|  | int j; | 
|  |  | 
|  | slot = entry_to_node(slot); | 
|  |  | 
|  | /* Verify consistency at this level */ | 
|  | for (i = 0; i < RADIX_TREE_TAG_LONGS; i++) { | 
|  | if (slot->tags[tag][i]) { | 
|  | anyset = 1; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (tagged != anyset) { | 
|  | printf("tag: %u, shift %u, tagged: %d, anyset: %d\n", | 
|  | tag, slot->shift, tagged, anyset); | 
|  | for (j = 0; j < RADIX_TREE_MAX_TAGS; j++) { | 
|  | printf("tag %d: ", j); | 
|  | for (i = 0; i < RADIX_TREE_TAG_LONGS; i++) | 
|  | printf("%016lx ", slot->tags[j][i]); | 
|  | printf("\n"); | 
|  | } | 
|  | return 1; | 
|  | } | 
|  | assert(tagged == anyset); | 
|  |  | 
|  | /* Go for next level */ | 
|  | if (slot->shift > 0) { | 
|  | for (i = 0; i < RADIX_TREE_MAP_SIZE; i++) | 
|  | if (slot->slots[i]) | 
|  | if (verify_node(slot->slots[i], tag, | 
|  | !!test_bit(i, slot->tags[tag]))) { | 
|  | printf("Failure at off %d\n", i); | 
|  | for (j = 0; j < RADIX_TREE_MAX_TAGS; j++) { | 
|  | printf("tag %d: ", j); | 
|  | for (i = 0; i < RADIX_TREE_TAG_LONGS; i++) | 
|  | printf("%016lx ", slot->tags[j][i]); | 
|  | printf("\n"); | 
|  | } | 
|  | return 1; | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void verify_tag_consistency(struct radix_tree_root *root, unsigned int tag) | 
|  | { | 
|  | struct radix_tree_node *node = root->rnode; | 
|  | if (!radix_tree_is_internal_node(node)) | 
|  | return; | 
|  | verify_node(node, tag, !!root_tag_get(root, tag)); | 
|  | } | 
|  |  | 
|  | void item_kill_tree(struct radix_tree_root *root) | 
|  | { | 
|  | struct radix_tree_iter iter; | 
|  | void **slot; | 
|  | struct item *items[32]; | 
|  | int nfound; | 
|  |  | 
|  | radix_tree_for_each_slot(slot, root, &iter, 0) { | 
|  | if (radix_tree_exceptional_entry(*slot)) | 
|  | radix_tree_delete(root, iter.index); | 
|  | } | 
|  |  | 
|  | while ((nfound = radix_tree_gang_lookup(root, (void **)items, 0, 32))) { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < nfound; i++) { | 
|  | void *ret; | 
|  |  | 
|  | ret = radix_tree_delete(root, items[i]->index); | 
|  | assert(ret == items[i]); | 
|  | free(items[i]); | 
|  | } | 
|  | } | 
|  | assert(radix_tree_gang_lookup(root, (void **)items, 0, 32) == 0); | 
|  | assert(root->rnode == NULL); | 
|  | } | 
|  |  | 
|  | void tree_verify_min_height(struct radix_tree_root *root, int maxindex) | 
|  | { | 
|  | unsigned shift; | 
|  | struct radix_tree_node *node = root->rnode; | 
|  | if (!radix_tree_is_internal_node(node)) { | 
|  | assert(maxindex == 0); | 
|  | return; | 
|  | } | 
|  |  | 
|  | node = entry_to_node(node); | 
|  | assert(maxindex <= node_maxindex(node)); | 
|  |  | 
|  | shift = node->shift; | 
|  | if (shift > 0) | 
|  | assert(maxindex > shift_maxindex(shift - RADIX_TREE_MAP_SHIFT)); | 
|  | else | 
|  | assert(maxindex > 0); | 
|  | } |