Commit 9e91c144 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'idr-4.11' of git://git.infradead.org/users/willy/linux-dax

Pull idr fix (and new tests) from Matthew Wilcox:
 "One urgent patch in here; freeing the correct IDA bitmap.

  Everything else is changes to the test suite"

* 'idr-4.11' of git://git.infradead.org/users/willy/linux-dax:
  radix tree test suite: Specify -m32 in LDFLAGS too
  ida: Free correct IDA bitmap
  radix tree test suite: Depend on Makefile and quieten grep
  radix tree test suite: Fix build with --as-needed
  radix tree test suite: Build 32 bit binaries
  radix tree test suite: Add performance test for radix_tree_join()
  radix tree test suite: Add performance test for radix_tree_split()
  radix tree test suite: Add performance benchmarks
  radix tree test suite: Add test for radix_tree_clear_tags()
  radix tree test suite: Add tests for ida_simple_get() and ida_simple_remove()
  radix tree test suite: Add test for idr_get_next()
parents f7d6a728 f0f3f2d0
...@@ -2129,7 +2129,7 @@ int ida_pre_get(struct ida *ida, gfp_t gfp) ...@@ -2129,7 +2129,7 @@ int ida_pre_get(struct ida *ida, gfp_t gfp)
struct ida_bitmap *bitmap = kmalloc(sizeof(*bitmap), gfp); struct ida_bitmap *bitmap = kmalloc(sizeof(*bitmap), gfp);
if (!bitmap) if (!bitmap)
return 0; return 0;
bitmap = this_cpu_cmpxchg(ida_bitmap, NULL, bitmap); if (this_cpu_cmpxchg(ida_bitmap, NULL, bitmap))
kfree(bitmap); kfree(bitmap);
} }
......
CFLAGS += -I. -I../../include -g -O2 -Wall -D_LGPL_SOURCE -fsanitize=address CFLAGS += -I. -I../../include -g -O2 -Wall -D_LGPL_SOURCE -fsanitize=address
LDFLAGS += -lpthread -lurcu LDFLAGS += -fsanitize=address
LDLIBS+= -lpthread -lurcu
TARGETS = main idr-test multiorder TARGETS = main idr-test multiorder
CORE_OFILES := radix-tree.o idr.o linux.o test.o find_bit.o CORE_OFILES := radix-tree.o idr.o linux.o test.o find_bit.o
OFILES = main.o $(CORE_OFILES) regression1.o regression2.o regression3.o \ OFILES = main.o $(CORE_OFILES) regression1.o regression2.o regression3.o \
...@@ -10,23 +11,25 @@ ifndef SHIFT ...@@ -10,23 +11,25 @@ ifndef SHIFT
SHIFT=3 SHIFT=3
endif endif
ifeq ($(BUILD), 32)
CFLAGS += -m32
LDFLAGS += -m32
endif
targets: mapshift $(TARGETS) targets: mapshift $(TARGETS)
main: $(OFILES) main: $(OFILES)
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o main
idr-test: idr-test.o $(CORE_OFILES) idr-test: idr-test.o $(CORE_OFILES)
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o idr-test
multiorder: multiorder.o $(CORE_OFILES) multiorder: multiorder.o $(CORE_OFILES)
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o multiorder
clean: clean:
$(RM) $(TARGETS) *.o radix-tree.c idr.c generated/map-shift.h $(RM) $(TARGETS) *.o radix-tree.c idr.c generated/map-shift.h
vpath %.c ../../lib vpath %.c ../../lib
$(OFILES): *.h */*.h generated/map-shift.h \ $(OFILES): Makefile *.h */*.h generated/map-shift.h \
../../include/linux/*.h \ ../../include/linux/*.h \
../../include/asm/*.h \ ../../include/asm/*.h \
../../../include/linux/radix-tree.h \ ../../../include/linux/radix-tree.h \
...@@ -41,7 +44,7 @@ idr.c: ../../../lib/idr.c ...@@ -41,7 +44,7 @@ idr.c: ../../../lib/idr.c
.PHONY: mapshift .PHONY: mapshift
mapshift: mapshift:
@if ! grep -qw $(SHIFT) generated/map-shift.h; then \ @if ! grep -qws $(SHIFT) generated/map-shift.h; then \
echo "#define RADIX_TREE_MAP_SHIFT $(SHIFT)" > \ echo "#define RADIX_TREE_MAP_SHIFT $(SHIFT)" > \
generated/map-shift.h; \ generated/map-shift.h; \
fi fi
...@@ -17,6 +17,9 @@ ...@@ -17,6 +17,9 @@
#include <time.h> #include <time.h>
#include "test.h" #include "test.h"
#define for_each_index(i, base, order) \
for (i = base; i < base + (1 << order); i++)
#define NSEC_PER_SEC 1000000000L #define NSEC_PER_SEC 1000000000L
static long long benchmark_iter(struct radix_tree_root *root, bool tagged) static long long benchmark_iter(struct radix_tree_root *root, bool tagged)
...@@ -57,27 +60,176 @@ static long long benchmark_iter(struct radix_tree_root *root, bool tagged) ...@@ -57,27 +60,176 @@ static long long benchmark_iter(struct radix_tree_root *root, bool tagged)
return nsec; return nsec;
} }
static void benchmark_insert(struct radix_tree_root *root,
unsigned long size, unsigned long step, int order)
{
struct timespec start, finish;
unsigned long index;
long long nsec;
clock_gettime(CLOCK_MONOTONIC, &start);
for (index = 0 ; index < size ; index += step)
item_insert_order(root, index, order);
clock_gettime(CLOCK_MONOTONIC, &finish);
nsec = (finish.tv_sec - start.tv_sec) * NSEC_PER_SEC +
(finish.tv_nsec - start.tv_nsec);
printv(2, "Size: %8ld, step: %8ld, order: %d, insertion: %15lld ns\n",
size, step, order, nsec);
}
static void benchmark_tagging(struct radix_tree_root *root,
unsigned long size, unsigned long step, int order)
{
struct timespec start, finish;
unsigned long index;
long long nsec;
clock_gettime(CLOCK_MONOTONIC, &start);
for (index = 0 ; index < size ; index += step)
radix_tree_tag_set(root, index, 0);
clock_gettime(CLOCK_MONOTONIC, &finish);
nsec = (finish.tv_sec - start.tv_sec) * NSEC_PER_SEC +
(finish.tv_nsec - start.tv_nsec);
printv(2, "Size: %8ld, step: %8ld, order: %d, tagging: %17lld ns\n",
size, step, order, nsec);
}
static void benchmark_delete(struct radix_tree_root *root,
unsigned long size, unsigned long step, int order)
{
struct timespec start, finish;
unsigned long index, i;
long long nsec;
clock_gettime(CLOCK_MONOTONIC, &start);
for (index = 0 ; index < size ; index += step)
for_each_index(i, index, order)
item_delete(root, i);
clock_gettime(CLOCK_MONOTONIC, &finish);
nsec = (finish.tv_sec - start.tv_sec) * NSEC_PER_SEC +
(finish.tv_nsec - start.tv_nsec);
printv(2, "Size: %8ld, step: %8ld, order: %d, deletion: %16lld ns\n",
size, step, order, nsec);
}
static void benchmark_size(unsigned long size, unsigned long step, int order) static void benchmark_size(unsigned long size, unsigned long step, int order)
{ {
RADIX_TREE(tree, GFP_KERNEL); RADIX_TREE(tree, GFP_KERNEL);
long long normal, tagged; long long normal, tagged;
unsigned long index;
for (index = 0 ; index < size ; index += step) { benchmark_insert(&tree, size, step, order);
item_insert_order(&tree, index, order); benchmark_tagging(&tree, size, step, order);
radix_tree_tag_set(&tree, index, 0);
}
tagged = benchmark_iter(&tree, true); tagged = benchmark_iter(&tree, true);
normal = benchmark_iter(&tree, false); normal = benchmark_iter(&tree, false);
printv(2, "Size %ld, step %6ld, order %d tagged %10lld ns, normal %10lld ns\n", printv(2, "Size: %8ld, step: %8ld, order: %d, tagged iteration: %8lld ns\n",
size, step, order, tagged, normal); size, step, order, tagged);
printv(2, "Size: %8ld, step: %8ld, order: %d, normal iteration: %8lld ns\n",
size, step, order, normal);
benchmark_delete(&tree, size, step, order);
item_kill_tree(&tree); item_kill_tree(&tree);
rcu_barrier(); rcu_barrier();
} }
static long long __benchmark_split(unsigned long index,
int old_order, int new_order)
{
struct timespec start, finish;
long long nsec;
RADIX_TREE(tree, GFP_ATOMIC);
item_insert_order(&tree, index, old_order);
clock_gettime(CLOCK_MONOTONIC, &start);
radix_tree_split(&tree, index, new_order);
clock_gettime(CLOCK_MONOTONIC, &finish);
nsec = (finish.tv_sec - start.tv_sec) * NSEC_PER_SEC +
(finish.tv_nsec - start.tv_nsec);
item_kill_tree(&tree);
return nsec;
}
static void benchmark_split(unsigned long size, unsigned long step)
{
int i, j, idx;
long long nsec = 0;
for (idx = 0; idx < size; idx += step) {
for (i = 3; i < 11; i++) {
for (j = 0; j < i; j++) {
nsec += __benchmark_split(idx, i, j);
}
}
}
printv(2, "Size %8ld, step %8ld, split time %10lld ns\n",
size, step, nsec);
}
static long long __benchmark_join(unsigned long index,
unsigned order1, unsigned order2)
{
unsigned long loc;
struct timespec start, finish;
long long nsec;
void *item, *item2 = item_create(index + 1, order1);
RADIX_TREE(tree, GFP_KERNEL);
item_insert_order(&tree, index, order2);
item = radix_tree_lookup(&tree, index);
clock_gettime(CLOCK_MONOTONIC, &start);
radix_tree_join(&tree, index + 1, order1, item2);
clock_gettime(CLOCK_MONOTONIC, &finish);
nsec = (finish.tv_sec - start.tv_sec) * NSEC_PER_SEC +
(finish.tv_nsec - start.tv_nsec);
loc = find_item(&tree, item);
if (loc == -1)
free(item);
item_kill_tree(&tree);
return nsec;
}
static void benchmark_join(unsigned long step)
{
int i, j, idx;
long long nsec = 0;
for (idx = 0; idx < 1 << 10; idx += step) {
for (i = 1; i < 15; i++) {
for (j = 0; j < i; j++) {
nsec += __benchmark_join(idx, i, j);
}
}
}
printv(2, "Size %8d, step %8ld, join time %10lld ns\n",
1 << 10, step, nsec);
}
void benchmark(void) void benchmark(void)
{ {
unsigned long size[] = {1 << 10, 1 << 20, 0}; unsigned long size[] = {1 << 10, 1 << 20, 0};
...@@ -95,4 +247,11 @@ void benchmark(void) ...@@ -95,4 +247,11 @@ void benchmark(void)
for (c = 0; size[c]; c++) for (c = 0; size[c]; c++)
for (s = 0; step[s]; s++) for (s = 0; step[s]; s++)
benchmark_size(size[c], step[s] << 9, 9); benchmark_size(size[c], step[s] << 9, 9);
for (c = 0; size[c]; c++)
for (s = 0; step[s]; s++)
benchmark_split(size[c], step[s]);
for (s = 0; step[s]; s++)
benchmark_join(step[s]);
} }
...@@ -153,6 +153,30 @@ void idr_nowait_test(void) ...@@ -153,6 +153,30 @@ void idr_nowait_test(void)
idr_destroy(&idr); idr_destroy(&idr);
} }
void idr_get_next_test(void)
{
unsigned long i;
int nextid;
DEFINE_IDR(idr);
int indices[] = {4, 7, 9, 15, 65, 128, 1000, 99999, 0};
for(i = 0; indices[i]; i++) {
struct item *item = item_create(indices[i], 0);
assert(idr_alloc(&idr, item, indices[i], indices[i+1],
GFP_KERNEL) == indices[i]);
}
for(i = 0, nextid = 0; indices[i]; i++) {
idr_get_next(&idr, &nextid);
assert(nextid == indices[i]);
nextid++;
}
idr_for_each(&idr, item_idr_free, &idr);
idr_destroy(&idr);
}
void idr_checks(void) void idr_checks(void)
{ {
unsigned long i; unsigned long i;
...@@ -202,6 +226,7 @@ void idr_checks(void) ...@@ -202,6 +226,7 @@ void idr_checks(void)
idr_alloc_test(); idr_alloc_test();
idr_null_test(); idr_null_test();
idr_nowait_test(); idr_nowait_test();
idr_get_next_test();
} }
/* /*
...@@ -338,7 +363,7 @@ void ida_check_random(void) ...@@ -338,7 +363,7 @@ void ida_check_random(void)
{ {
DEFINE_IDA(ida); DEFINE_IDA(ida);
DECLARE_BITMAP(bitmap, 2048); DECLARE_BITMAP(bitmap, 2048);
int id; int id, err;
unsigned int i; unsigned int i;
time_t s = time(NULL); time_t s = time(NULL);
...@@ -352,8 +377,11 @@ void ida_check_random(void) ...@@ -352,8 +377,11 @@ void ida_check_random(void)
ida_remove(&ida, bit); ida_remove(&ida, bit);
} else { } else {
__set_bit(bit, bitmap); __set_bit(bit, bitmap);
do {
ida_pre_get(&ida, GFP_KERNEL); ida_pre_get(&ida, GFP_KERNEL);
assert(!ida_get_new_above(&ida, bit, &id)); err = ida_get_new_above(&ida, bit, &id);
} while (err == -ENOMEM);
assert(!err);
assert(id == bit); assert(id == bit);
} }
} }
...@@ -362,6 +390,24 @@ void ida_check_random(void) ...@@ -362,6 +390,24 @@ void ida_check_random(void)
goto repeat; goto repeat;
} }
void ida_simple_get_remove_test(void)
{
DEFINE_IDA(ida);
unsigned long i;
for (i = 0; i < 10000; i++) {
assert(ida_simple_get(&ida, 0, 20000, GFP_KERNEL) == i);
}
assert(ida_simple_get(&ida, 5, 30, GFP_KERNEL) < 0);
for (i = 0; i < 10000; i++) {
ida_simple_remove(&ida, i);
}
assert(ida_is_empty(&ida));
ida_destroy(&ida);
}
void ida_checks(void) void ida_checks(void)
{ {
DEFINE_IDA(ida); DEFINE_IDA(ida);
...@@ -428,15 +474,41 @@ void ida_checks(void) ...@@ -428,15 +474,41 @@ void ida_checks(void)
ida_check_max(); ida_check_max();
ida_check_conv(); ida_check_conv();
ida_check_random(); ida_check_random();
ida_simple_get_remove_test();
radix_tree_cpu_dead(1); radix_tree_cpu_dead(1);
} }
static void *ida_random_fn(void *arg)
{
rcu_register_thread();
ida_check_random();
rcu_unregister_thread();
return NULL;
}
void ida_thread_tests(void)
{
pthread_t threads[10];
int i;
for (i = 0; i < ARRAY_SIZE(threads); i++)
if (pthread_create(&threads[i], NULL, ida_random_fn, NULL)) {
perror("creating ida thread");
exit(1);
}
while (i--)
pthread_join(threads[i], NULL);
}
int __weak main(void) int __weak main(void)
{ {
radix_tree_init(); radix_tree_init();
idr_checks(); idr_checks();
ida_checks(); ida_checks();
ida_thread_tests();
radix_tree_cpu_dead(1);
rcu_barrier(); rcu_barrier();
if (nr_allocated) if (nr_allocated)
printf("nr_allocated = %d\n", nr_allocated); printf("nr_allocated = %d\n", nr_allocated);
......
...@@ -368,6 +368,7 @@ int main(int argc, char **argv) ...@@ -368,6 +368,7 @@ int main(int argc, char **argv)
iteration_test(0, 10 + 90 * long_run); iteration_test(0, 10 + 90 * long_run);
iteration_test(7, 10 + 90 * long_run); iteration_test(7, 10 + 90 * long_run);
single_thread_tests(long_run); single_thread_tests(long_run);
ida_thread_tests();
/* Free any remaining preallocated nodes */ /* Free any remaining preallocated nodes */
radix_tree_cpu_dead(0); radix_tree_cpu_dead(0);
......
...@@ -330,6 +330,34 @@ static void single_check(void) ...@@ -330,6 +330,34 @@ static void single_check(void)
item_kill_tree(&tree); item_kill_tree(&tree);
} }
void radix_tree_clear_tags_test(void)
{
unsigned long index;
struct radix_tree_node *node;
struct radix_tree_iter iter;
void **slot;
RADIX_TREE(tree, GFP_KERNEL);
item_insert(&tree, 0);
item_tag_set(&tree, 0, 0);
__radix_tree_lookup(&tree, 0, &node, &slot);
radix_tree_clear_tags(&tree, node, slot);
assert(item_tag_get(&tree, 0, 0) == 0);
for (index = 0; index < 1000; index++) {
item_insert(&tree, index);
item_tag_set(&tree, index, 0);
}
radix_tree_for_each_slot(slot, &tree, &iter, 0) {
radix_tree_clear_tags(&tree, iter.node, slot);
assert(item_tag_get(&tree, iter.index, 0) == 0);
}
item_kill_tree(&tree);
}
void tag_check(void) void tag_check(void)
{ {
single_check(); single_check();
...@@ -347,4 +375,5 @@ void tag_check(void) ...@@ -347,4 +375,5 @@ void tag_check(void)
thrash_tags(); thrash_tags();
rcu_barrier(); rcu_barrier();
printv(2, "after thrash_tags: %d allocated\n", nr_allocated); printv(2, "after thrash_tags: %d allocated\n", nr_allocated);
radix_tree_clear_tags_test();
} }
...@@ -36,6 +36,7 @@ void iteration_test(unsigned order, unsigned duration); ...@@ -36,6 +36,7 @@ void iteration_test(unsigned order, unsigned duration);
void benchmark(void); void benchmark(void);
void idr_checks(void); void idr_checks(void);
void ida_checks(void); void ida_checks(void);
void ida_thread_tests(void);
struct item * struct item *
item_tag_set(struct radix_tree_root *root, unsigned long index, int tag); item_tag_set(struct radix_tree_root *root, unsigned long index, int tag);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment