forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
287 lines
6.3 KiB
287 lines
6.3 KiB
// 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); |
|
} |
|
|
|
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(struct radix_tree_root *root, unsigned long index) |
|
{ |
|
struct item *item = item_create(index, 0); |
|
int err = radix_tree_insert(root, item->index, item); |
|
if (err) |
|
free(item); |
|
return err; |
|
} |
|
|
|
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)); |
|
} |
|
|
|
void item_free(struct item *item, unsigned long index) |
|
{ |
|
item_sanity(item, index); |
|
free(item); |
|
} |
|
|
|
int item_delete(struct radix_tree_root *root, unsigned long index) |
|
{ |
|
struct item *item = radix_tree_delete(root, index); |
|
|
|
if (!item) |
|
return 0; |
|
|
|
item_free(item, index); |
|
return 1; |
|
} |
|
|
|
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 xarray *xa, unsigned long index) |
|
{ |
|
struct item *item = xa_erase(xa, 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 xarray *xa, unsigned long start, unsigned long end, |
|
unsigned batch, xa_mark_t iftag, xa_mark_t thentag) |
|
{ |
|
XA_STATE(xas, xa, start); |
|
unsigned int tagged = 0; |
|
struct item *item; |
|
|
|
if (batch == 0) |
|
batch = 1; |
|
|
|
xas_lock_irq(&xas); |
|
xas_for_each_marked(&xas, item, end, iftag) { |
|
xas_set_mark(&xas, thentag); |
|
if (++tagged % batch) |
|
continue; |
|
|
|
xas_pause(&xas); |
|
xas_unlock_irq(&xas); |
|
rcu_barrier(); |
|
xas_lock_irq(&xas); |
|
} |
|
xas_unlock_irq(&xas); |
|
|
|
return tagged; |
|
} |
|
|
|
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->xa_head; |
|
if (!radix_tree_is_internal_node(node)) |
|
return; |
|
verify_node(node, tag, !!root_tag_get(root, tag)); |
|
} |
|
|
|
void item_kill_tree(struct xarray *xa) |
|
{ |
|
XA_STATE(xas, xa, 0); |
|
void *entry; |
|
|
|
xas_for_each(&xas, entry, ULONG_MAX) { |
|
if (!xa_is_value(entry)) { |
|
item_free(entry, xas.xa_index); |
|
} |
|
xas_store(&xas, NULL); |
|
} |
|
|
|
assert(xa_empty(xa)); |
|
} |
|
|
|
void tree_verify_min_height(struct radix_tree_root *root, int maxindex) |
|
{ |
|
unsigned shift; |
|
struct radix_tree_node *node = root->xa_head; |
|
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); |
|
}
|
|
|