test_rhashtable: don't allocate huge static array
Subsystems (4)
v2 cover test_rhashtable: don't allocate huge static array
Add a test case for the rhlist interface.
While at it, cleanup current rhashtable test a bit and add a check for max_size support.
No changes since v1, except in last patch.
kbuild robot complained about large onstack allocation caused by struct rhltable when lockdep is enabled.
PATCH v2 1/4 test_rhashtable: don't allocate huge static array
--- lib/test_rhashtable.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-)
lib/test_rhashtable.c +16 -11
@@ -72,8 +72,6 @@ struct thread_data {struct test_obj *objs; }; -static struct test_obj array[MAX_ENTRIES ]; -static struct rhashtable_params test_rht_params = { .head_offset = offsetof(struct test_obj , node), .key_offset = offsetof(struct test_obj , value),
@@ -85,7 +83,7 @@ static struct rhashtable_params test_rht_params = {static struct semaphore prestart_sem;static struct semaphore startup_sem =__SEMAPHORE_INITIALIZER (startup_sem,0 ); -static int insert_retry (struct rhashtable *ht,struct rhash_head *obj, +static int insert_retry (struct rhashtable *ht,struct test_obj *obj,const struct rhashtable_params params) {int err, retries =-1 , enomem_retries =0 ;
@@ -93,7 +91,7 @@ static int insert_retry(struct rhashtable *ht, struct rhash_head *obj, do { retries++; cond_resched(); - err = rhashtable_insert_fast(ht, obj, params); + err = rhashtable_insert_fast(ht, &obj->node, params); if (err == -ENOMEM && enomem_retry) { enomem_retries++; err = -EBUSY;
@@ -107,7 +105,7 @@ static int insert_retry(struct rhashtable *ht, struct rhash_head *obj,return err ? : retries; } -static int __init test_rht_lookup (struct rhashtable *ht) +static int __init test_rht_lookup (struct rhashtable *ht,struct test_obj *array) {unsigned int i;
@@ -186,7 +184,7 @@ static void test_bucket_stats(struct rhashtable *ht)pr_warn ("Test failed: Total count mismatch ^^^" ); } -static s64 __init test_rhashtable (struct rhashtable *ht) +static s64 __init test_rhashtable (struct rhashtable *ht,struct test_obj *array) {struct test_obj *obj;int err;
@@ -203,7 +201,7 @@ static s64 __init test_rhashtable(struct rhashtable *ht)struct test_obj *obj = &array[i]; obj->value.id = i *2 ; - err =insert_retry (ht, &obj->node, test_rht_params); + err =insert_retry (ht, obj, test_rht_params);if (err >0 ) insert_retries += err;else if (err)
@@ -216,7 +214,7 @@ static s64 __init test_rhashtable(struct rhashtable *ht)test_bucket_stats (ht);rcu_read_lock (); -test_rht_lookup (ht); +test_rht_lookup (ht, array);rcu_read_unlock ();test_bucket_stats (ht);
@@ -286,7 +284,7 @@ static int threadfunc(void *data)for (i =0 ; i < entries; i++) { tdata->objs[i].value.id = i; tdata->objs[i].value.tid = tdata->id; - err =insert_retry (&ht, &tdata->objs[i].node, test_rht_params); + err =insert_retry (&ht, &tdata->objs[i], test_rht_params);if (err >0 ) { insert_retries += err; }else if (err) {
@@ -349,6 +347,10 @@ static int __init test_rht_init(void) test_rht_params.max_size = max_size ? :roundup_pow_of_two (entries); test_rht_params.nelem_hint = size; + objs =vzalloc ((test_rht_params.max_size +1 ) *sizeof (struct test_obj )); +if (!objs) +return -ENOMEM ; +pr_info ("Running rhashtable test nelem=%d, max_size=%d, shrinking=%d\n" , size, max_size, shrinking);
@@ -356,7 +358,8 @@ static int __init test_rht_init(void)s64 time;pr_info ("Test %02d:\n" , i); -memset (&array,0 ,sizeof (array)); +memset (objs,0 , test_rht_params.max_size *sizeof (struct test_obj )); + err =rhashtable_init (&ht, &test_rht_params);if (err <0 ) { pr_warn("Test failed: Unable to initialize hashtable: %d\n",
@@ -364,9 +367,10 @@ static int __init test_rht_init(void)continue ; } - time =test_rhashtable (&ht); + time =test_rhashtable (&ht, objs);rhashtable_destroy (&ht);if (time <0 ) { +vfree (objs);pr_warn ("Test failed: return code %lld\n" , time);return -EINVAL ; }
@@ -374,6 +378,7 @@ static int __init test_rht_init(void) total_time += time; } +vfree (objs);do_div (total_time, runs);pr_info ("Average test time: %llu\n" , total_time);
Signature · 1 line
2.13.5
PATCH v2 2/4 test_rhashtable: don't use global entries variable
In reply to Florian Westphalpass the entries to test as an argument instead.
Followup patch will add an rhlist test case; rhlist delete opererations are slow so we need to use a smaller number to test it.
--- lib/test_rhashtable.c | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-)
lib/test_rhashtable.c +23 -14
@@ -28,9 +28,9 @@#define MAX_ENTRIES 1000000#define TEST_INSERT_FAIL INT_MAX -static int entries =50000 ; -module_param (entries, int,0 ); -MODULE_PARM_DESC (entries,"Number of entries to add (default: 50000)" ); +static int parm_entries =50000 ; +module_param (parm_entries, int,0 ); +MODULE_PARM_DESC (parm_entries,"Number of entries to add (default: 50000)" );static int runs =4 ;module_param (runs, int,0 );
@@ -67,6 +67,7 @@ struct test_obj { };struct thread_data { +unsigned int entries;int id;struct task_struct *task;struct test_obj *objs;
@@ -105,11 +106,12 @@ static int insert_retry(struct rhashtable *ht, struct test_obj *obj,return err ? : retries; } -static int __init test_rht_lookup (struct rhashtable *ht,struct test_obj *array) +static int __init test_rht_lookup (struct rhashtable *ht,struct test_obj *array, +unsigned int entries) {unsigned int i; -for (i =0 ; i < entries *2 ; i++) { +for (i =0 ; i < entries; i++) {struct test_obj *obj;bool expected = !(i %2 ); struct test_obj_val key = {
@@ -142,7 +144,7 @@ static int __init test_rht_lookup(struct rhashtable *ht, struct test_obj *array)return 0 ; } -static void test_bucket_stats (struct rhashtable *ht) +static void test_bucket_stats (struct rhashtable *ht,unsigned int entries) {unsigned int err, total =0 , chain_len =0 ;struct rhashtable_iter hti;
@@ -184,7 +186,8 @@ static void test_bucket_stats(struct rhashtable *ht)pr_warn ("Test failed: Total count mismatch ^^^" ); } -static s64 __init test_rhashtable (struct rhashtable *ht,struct test_obj *array) +static s64 __init test_rhashtable (struct rhashtable *ht,struct test_obj *array, +unsigned int entries) {struct test_obj *obj;int err;
@@ -212,12 +215,12 @@ static s64 __init test_rhashtable(struct rhashtable *ht, struct test_obj *array)pr_info (" %u insertions retried due to memory pressure\n" , insert_retries); -test_bucket_stats (ht); +test_bucket_stats (ht, entries);rcu_read_lock (); -test_rht_lookup (ht, array); +test_rht_lookup (ht, array, entries);rcu_read_unlock (); -test_bucket_stats (ht); +test_bucket_stats (ht, entries);pr_info (" Deleting %d keys\n" , entries);for (i =0 ; i < entries; i++) {
@@ -245,6 +248,7 @@ static struct rhashtable ht;static int thread_lookup_test (struct thread_data *tdata) { +unsigned int entries = tdata->entries;int i, err =0 ;for (i =0 ; i < entries; i++) {
@@ -281,7 +285,7 @@ static int threadfunc(void *data)if (down_interruptible (&startup_sem))pr_err (" thread[%d]: down_interruptible failed\n" , tdata->id); -for (i =0 ; i < entries; i++) { +for (i =0 ; i < tdata->entries; i++) { tdata->objs[i].value.id = i; tdata->objs[i].value.tid = tdata->id; err =insert_retry (&ht, &tdata->objs[i], test_rht_params);
@@ -305,7 +309,7 @@ static int threadfunc(void *data) }for (step =10 ; step >0 ; step--) { -for (i =0 ; i < entries; i += step) { +for (i =0 ; i < tdata->entries; i += step) {if (tdata->objs[i].value.id ==TEST_INSERT_FAIL )continue ; err = rhashtable_remove_fast(&ht, &tdata->objs[i].node,
@@ -336,12 +340,16 @@ static int threadfunc(void *data)static int __init test_rht_init (void ) { +unsigned int entries;int i, err, started_threads =0 , failed_threads =0 ;u64 total_time =0 ;struct thread_data *tdata;struct test_obj *objs; - entries =min (entries,MAX_ENTRIES ); +if (parm_entries <0 ) + parm_entries =1 ; + + entries =min (parm_entries,MAX_ENTRIES ); test_rht_params.automatic_shrinking = shrinking; test_rht_params.max_size = max_size ? :roundup_pow_of_two (entries);
@@ -367,7 +375,7 @@ static int __init test_rht_init(void)continue ; } - time =test_rhashtable (&ht, objs); + time =test_rhashtable (&ht, objs, entries);rhashtable_destroy (&ht);if (time <0 ) {vfree (objs);
@@ -409,6 +417,7 @@ static int __init test_rht_init(void) }for (i =0 ; i < tcount; i++) { tdata[i].id = i; + tdata[i].entries = entries; tdata[i].objs = objs + i * entries; tdata[i].task =kthread_run (threadfunc, &tdata[i],"rhashtable_thrad[%d]" , i);
Signature · 1 line
2.13.5
PATCH v2 3/4 test_rhashtable: add a check for max_size
In reply to Florian Westphaladd a test that tries to insert more than max_size elements.
--- lib/test_rhashtable.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+)
lib/test_rhashtable.c +41 -0
@@ -246,6 +246,43 @@ static s64 __init test_rhashtable(struct rhashtable *ht, struct test_obj *array,static struct rhashtable ht; +static int __init test_rhashtable_max (struct test_obj *array, +unsigned int entries) +{ +unsigned int i, insert_retries =0 ; +int err; + + test_rht_params.max_size =roundup_pow_of_two (entries /8 ); + err =rhashtable_init (&ht, &test_rht_params); +if (err) +return err; + +for (i =0 ; i < ht.max_elems; i++) { +struct test_obj *obj = &array[i]; + + obj->value.id = i *2 ; + err =insert_retry (&ht, obj, test_rht_params); +if (err >0 ) + insert_retries += err; +else if (err) +return err; + } + + err =insert_retry (&ht, &array[ht.max_elems], test_rht_params); +if (err == -E2BIG ) { + err =0 ; + }else { +pr_info ("insert element %u should have failed with %d, got %d\n" , + ht.max_elems, -E2BIG , err); +if (err ==0 ) + err =-1 ; + } + +rhashtable_destroy (&ht); + +return err; +} +static int thread_lookup_test (struct thread_data *tdata) {unsigned int entries = tdata->entries;
@@ -386,7 +423,11 @@ static int __init test_rht_init(void) total_time += time; } +pr_info ("test if its possible to exceed max_size %d: %s\n" , + test_rht_params.max_size,test_rhashtable_max (objs, entries) ==0 ? +"no, ok" :"YES, failed" );vfree (objs); +do_div (total_time, runs);pr_info ("Average test time: %llu\n" , total_time);
Signature · 1 line
2.13.5
PATCH v2 4/4 test_rhashtable: add test case for rhl_table interface
In reply to Florian Westphalalso test rhltable. rhltable remove operations are slow as deletions require a list walk, thus test with 1/16th of the given entry count number to get a run duration similar to rhashtable one.
---
change since v1:
place struct rhltable in initdata section to avoid large onstack allocation warnings when lockdep is enabled.
lib/test_rhashtable.c | 196 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 194 insertions(+), 2 deletions(-)
lib/test_rhashtable.c +194 -2
@@ -23,6 +23,7 @@#include <linux/semaphore.h> #include <linux/slab.h> #include <linux/sched.h> +#include <linux/random.h> #include <linux/vmalloc.h> #define MAX_ENTRIES 1000000
@@ -66,6 +67,11 @@ struct test_obj {struct rhash_head node; }; +struct test_obj_rhl { +struct test_obj_val value; +struct rhlist_head list_node; +}; +struct thread_data {unsigned int entries;int id;
@@ -245,6 +251,186 @@ static s64 __init test_rhashtable(struct rhashtable *ht, struct test_obj *array, }static struct rhashtable ht; +static struct rhltable rhlt__initdata ; + +static int __init test_rhltable (unsigned int entries) +{ +struct test_obj_rhl *rhl_test_objects; +unsigned long *obj_in_table; +unsigned int i, j, k; +int ret, err; + +if (entries ==0 ) + entries =1 ; + + rhl_test_objects =vzalloc (sizeof (*rhl_test_objects) * entries); +if (!rhl_test_objects) +return -ENOMEM ; + + ret = -ENOMEM ; + obj_in_table =vzalloc (BITS_TO_LONGS (entries) *sizeof (unsigned long )); +if (!obj_in_table) + goto out_free; + +/* nulls_base not supported in rhlist interface */ + test_rht_params.nulls_base =0 ; + err =rhltable_init (&rhlt, &test_rht_params); +if (WARN_ON (err)) + goto out_free; + + k =prandom_u32 (); + ret =0 ; +for (i =0 ; i < entries; i++) { + rhl_test_objects[i].value.id = k; + err =rhltable_insert (&rhlt, &rhl_test_objects[i].list_node, + test_rht_params); +if (WARN (err,"error %d on element %d\n" , err, i)) +break ; +if (err ==0 ) +set_bit (i, obj_in_table); + } + +if (err) + ret = err; + +pr_info ("test %d add/delete pairs into rhlist\n" , entries); +for (i =0 ; i < entries; i++) { +struct rhlist_head *h, *pos; +struct test_obj_rhl *obj; +struct test_obj_val key = { + .id = k, + }; +bool found; + +rcu_read_lock (); + h =rhltable_lookup (&rhlt, &key, test_rht_params); +if (WARN (!h,"key not found during iteration %d of %d" , i, entries)) { +rcu_read_unlock (); +break ; + } + +if (i) { + j = i -1 ; +rhl_for_each_entry_rcu (obj, pos, h, list_node) { +if (WARN (pos == &rhl_test_objects[j].list_node,"old element found, should be gone" )) +break ; + } + } + +cond_resched_rcu (); + + found = false; + +rhl_for_each_entry_rcu (obj, pos, h, list_node) { +if (pos == &rhl_test_objects[i].list_node) { + found = true; +break ; + } + } + +rcu_read_unlock (); + +if (WARN (!found,"element %d not found" , i)) +break ; + + err =rhltable_remove (&rhlt, &rhl_test_objects[i].list_node, test_rht_params); +WARN (err,"rhltable_remove: err %d for iteration %d\n" , err, i); +if (err ==0 ) +clear_bit (i, obj_in_table); + } + +if (ret ==0 && err) + ret = err; + +for (i =0 ; i < entries; i++) { +WARN (test_bit (i, obj_in_table),"elem %d allegedly still present" , i); + + err =rhltable_insert (&rhlt, &rhl_test_objects[i].list_node, + test_rht_params); +if (WARN (err,"error %d on element %d\n" , err, i)) +break ; +if (err ==0 ) +set_bit (i, obj_in_table); + } + +pr_info ("test %d random rhlist add/delete operations\n" , entries); +for (j =0 ; j < entries; j++) { +u32 i =prandom_u32_max (entries); +u32 prand =prandom_u32 (); + +cond_resched (); + +if (prand ==0 ) + prand =prandom_u32 (); + +if (prand &1 ) { + prand >>=1 ; +continue ; + } + + err =rhltable_remove (&rhlt, &rhl_test_objects[i].list_node, test_rht_params); +if (test_bit (i, obj_in_table)) { +clear_bit (i, obj_in_table); +if (WARN (err,"cannot remove element at slot %d" , i)) +continue ; + }else { +if (WARN (err != -ENOENT ,"removed non-existant element %d, error %d not %d" , + i, err, -ENOENT )) +continue ; + } + +if (prand &1 ) { + prand >>=1 ; +continue ; + } + + err =rhltable_insert (&rhlt, &rhl_test_objects[i].list_node, test_rht_params); +if (err ==0 ) { +if (WARN (test_and_set_bit (i, obj_in_table),"succeeded to insert same object %d" , i)) +continue ; + }else { +if (WARN (!test_bit (i, obj_in_table),"failed to insert object %d" , i)) +continue ; + } + +if (prand &1 ) { + prand >>=1 ; +continue ; + } + + i =prandom_u32_max (entries); +if (test_bit (i, obj_in_table)) { + err =rhltable_remove (&rhlt, &rhl_test_objects[i].list_node, test_rht_params); +WARN (err,"cannot remove element at slot %d" , i); +if (err ==0 ) +clear_bit (i, obj_in_table); + }else { + err =rhltable_insert (&rhlt, &rhl_test_objects[i].list_node, test_rht_params); +WARN (err,"failed to insert object %d" , i); +if (err ==0 ) +set_bit (i, obj_in_table); + } + } + +for (i =0 ; i < entries; i++) { +cond_resched (); + err =rhltable_remove (&rhlt, &rhl_test_objects[i].list_node, test_rht_params); +if (test_bit (i, obj_in_table)) { +if (WARN (err,"cannot remove element at slot %d" , i)) +continue ; + }else { +if (WARN (err != -ENOENT ,"removed non-existant element, error %d not %d" , + err, -ENOENT )) +continue ; + } + } + +rhltable_destroy (&rhlt); +out_free: +vfree (rhl_test_objects); +vfree (obj_in_table); +return ret; +}static int __init test_rhashtable_max (struct test_obj *array,unsigned int entries)
@@ -480,11 +666,17 @@ static int __init test_rht_init(void) failed_threads++; } } -pr_info ("Started %d threads, %d failed\n" , - started_threads, failed_threads);rhashtable_destroy (&ht);vfree (tdata);vfree (objs); + +/* +* rhltable_remove is very expensive, default values can cause test +* to run for 2 minutes or more, use a smaller number instead. +*/ + err =test_rhltable (entries /16 ); +pr_info ("Started %d threads, %d failed, rhltable test returns %d\n" , + started_threads, failed_threads, err);return 0 ; }
Signature · 1 line
2.13.5
David Miller
maintainerFrom: Florian Westphal <[email protected]>
Date: Wed, 20 Sep 2017 01:12:10 +0200
Quoted 7 lines
Add a test case for the rhlist interface.
While at it, cleanup current rhashtable test a bit and add a check formax_sizesupport.No changes since v1, except in last patch.
kbuild robot complained about large onstack allocation caused by struct rhltable when lockdep is enabled.
Looks good, series applied, thanks Florian.