Skip to content

Commit

Permalink
rhashtable: Make selftest modular
Browse files Browse the repository at this point in the history
Allow the selftest on the resizable hash table to be built modular, just
like all other tests that do not depend on DEBUG_KERNEL.

Signed-off-by: Geert Uytterhoeven <[email protected]>
Acked-by: Thomas Graf <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
geertu authored and davem330 committed Jan 31, 2015
1 parent 207895f commit 9d6dbe1
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 206 deletions.
2 changes: 1 addition & 1 deletion lib/Kconfig.debug
Original file line number Diff line number Diff line change
Expand Up @@ -1586,7 +1586,7 @@ config TEST_KSTRTOX
tristate "Test kstrto*() family of functions at runtime"

config TEST_RHASHTABLE
bool "Perform selftest on resizable hash table"
tristate "Perform selftest on resizable hash table"
default n
help
Enable this option to test the rhashtable functions at boot.
Expand Down
1 change: 1 addition & 0 deletions lib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ obj-$(CONFIG_TEST_LKM) += test_module.o
obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o
obj-$(CONFIG_TEST_BPF) += test_bpf.o
obj-$(CONFIG_TEST_FIRMWARE) += test_firmware.o
obj-$(CONFIG_TEST_RHASHTABLE) += test_rhashtable.o

ifeq ($(CONFIG_DEBUG_KOBJECT),y)
CFLAGS_kobject.o += -DDEBUG
Expand Down
205 changes: 0 additions & 205 deletions lib/rhashtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -935,208 +935,3 @@ void rhashtable_destroy(struct rhashtable *ht)
mutex_unlock(&ht->mutex);
}
EXPORT_SYMBOL_GPL(rhashtable_destroy);

/**************************************************************************
* Self Test
**************************************************************************/

#ifdef CONFIG_TEST_RHASHTABLE

#define TEST_HT_SIZE 8
#define TEST_ENTRIES 2048
#define TEST_PTR ((void *) 0xdeadbeef)
#define TEST_NEXPANDS 4

struct test_obj {
void *ptr;
int value;
struct rhash_head node;
};

static int __init test_rht_lookup(struct rhashtable *ht)
{
unsigned int i;

for (i = 0; i < TEST_ENTRIES * 2; i++) {
struct test_obj *obj;
bool expected = !(i % 2);
u32 key = i;

obj = rhashtable_lookup(ht, &key);

if (expected && !obj) {
pr_warn("Test failed: Could not find key %u\n", key);
return -ENOENT;
} else if (!expected && obj) {
pr_warn("Test failed: Unexpected entry found for key %u\n",
key);
return -EEXIST;
} else if (expected && obj) {
if (obj->ptr != TEST_PTR || obj->value != i) {
pr_warn("Test failed: Lookup value mismatch %p!=%p, %u!=%u\n",
obj->ptr, TEST_PTR, obj->value, i);
return -EINVAL;
}
}
}

return 0;
}

static void test_bucket_stats(struct rhashtable *ht, bool quiet)
{
unsigned int cnt, rcu_cnt, i, total = 0;
struct rhash_head *pos;
struct test_obj *obj;
struct bucket_table *tbl;

tbl = rht_dereference_rcu(ht->tbl, ht);
for (i = 0; i < tbl->size; i++) {
rcu_cnt = cnt = 0;

if (!quiet)
pr_info(" [%#4x/%zu]", i, tbl->size);

rht_for_each_entry_rcu(obj, pos, tbl, i, node) {
cnt++;
total++;
if (!quiet)
pr_cont(" [%p],", obj);
}

rht_for_each_entry_rcu(obj, pos, tbl, i, node)
rcu_cnt++;

if (rcu_cnt != cnt)
pr_warn("Test failed: Chain count mismach %d != %d",
cnt, rcu_cnt);

if (!quiet)
pr_cont("\n [%#x] first element: %p, chain length: %u\n",
i, tbl->buckets[i], cnt);
}

pr_info(" Traversal complete: counted=%u, nelems=%u, entries=%d\n",
total, atomic_read(&ht->nelems), TEST_ENTRIES);

if (total != atomic_read(&ht->nelems) || total != TEST_ENTRIES)
pr_warn("Test failed: Total count mismatch ^^^");
}

static int __init test_rhashtable(struct rhashtable *ht)
{
struct bucket_table *tbl;
struct test_obj *obj;
struct rhash_head *pos, *next;
int err;
unsigned int i;

/*
* Insertion Test:
* Insert TEST_ENTRIES into table with all keys even numbers
*/
pr_info(" Adding %d keys\n", TEST_ENTRIES);
for (i = 0; i < TEST_ENTRIES; i++) {
struct test_obj *obj;

obj = kzalloc(sizeof(*obj), GFP_KERNEL);
if (!obj) {
err = -ENOMEM;
goto error;
}

obj->ptr = TEST_PTR;
obj->value = i * 2;

rhashtable_insert(ht, &obj->node);
}

rcu_read_lock();
test_bucket_stats(ht, true);
test_rht_lookup(ht);
rcu_read_unlock();

for (i = 0; i < TEST_NEXPANDS; i++) {
pr_info(" Table expansion iteration %u...\n", i);
mutex_lock(&ht->mutex);
rhashtable_expand(ht);
mutex_unlock(&ht->mutex);

rcu_read_lock();
pr_info(" Verifying lookups...\n");
test_rht_lookup(ht);
rcu_read_unlock();
}

for (i = 0; i < TEST_NEXPANDS; i++) {
pr_info(" Table shrinkage iteration %u...\n", i);
mutex_lock(&ht->mutex);
rhashtable_shrink(ht);
mutex_unlock(&ht->mutex);

rcu_read_lock();
pr_info(" Verifying lookups...\n");
test_rht_lookup(ht);
rcu_read_unlock();
}

rcu_read_lock();
test_bucket_stats(ht, true);
rcu_read_unlock();

pr_info(" Deleting %d keys\n", TEST_ENTRIES);
for (i = 0; i < TEST_ENTRIES; i++) {
u32 key = i * 2;

obj = rhashtable_lookup(ht, &key);
BUG_ON(!obj);

rhashtable_remove(ht, &obj->node);
kfree(obj);
}

return 0;

error:
tbl = rht_dereference_rcu(ht->tbl, ht);
for (i = 0; i < tbl->size; i++)
rht_for_each_entry_safe(obj, pos, next, tbl, i, node)
kfree(obj);

return err;
}

static int __init test_rht_init(void)
{
struct rhashtable ht;
struct rhashtable_params params = {
.nelem_hint = TEST_HT_SIZE,
.head_offset = offsetof(struct test_obj, node),
.key_offset = offsetof(struct test_obj, value),
.key_len = sizeof(int),
.hashfn = jhash,
.nulls_base = (3U << RHT_BASE_SHIFT),
.grow_decision = rht_grow_above_75,
.shrink_decision = rht_shrink_below_30,
};
int err;

pr_info("Running resizable hashtable tests...\n");

err = rhashtable_init(&ht, &params);
if (err < 0) {
pr_warn("Test failed: Unable to initialize hashtable: %d\n",
err);
return err;
}

err = test_rhashtable(&ht);

rhashtable_destroy(&ht);

return err;
}

subsys_initcall(test_rht_init);

#endif /* CONFIG_TEST_RHASHTABLE */
Loading

0 comments on commit 9d6dbe1

Please sign in to comment.