2012-10-09 03:30:39 +04:00
# include <linux/module.h>
2017-09-09 02:14:46 +03:00
# include <linux/moduleparam.h>
2012-10-09 03:31:33 +04:00
# include <linux/rbtree_augmented.h>
2012-10-09 03:30:39 +04:00
# include <linux/random.h>
2017-09-09 02:14:46 +03:00
# include <linux/slab.h>
2012-10-09 03:30:39 +04:00
# include <asm/timex.h>
2017-09-09 02:14:46 +03:00
# define __param(type, name, init, msg) \
static type name = init ; \
module_param ( name , type , 0444 ) ; \
MODULE_PARM_DESC ( name , msg ) ;
__param ( int , nnodes , 100 , " Number of nodes in the rb-tree " ) ;
2017-11-18 02:28:27 +03:00
__param ( int , perf_loops , 1000 , " Number of iterations modifying the rb-tree " ) ;
2017-09-09 02:14:46 +03:00
__param ( int , check_loops , 100 , " Number of iterations modifying and verifying the rb-tree " ) ;
2012-10-09 03:30:39 +04:00
struct test_node {
u32 key ;
2014-01-24 03:56:05 +04:00
struct rb_node rb ;
2012-10-09 03:31:15 +04:00
/* following fields used for testing augmented rbtree functionality */
u32 val ;
u32 augmented ;
2012-10-09 03:30:39 +04:00
} ;
2017-09-09 02:14:52 +03:00
static struct rb_root_cached root = RB_ROOT_CACHED ;
2017-09-09 02:14:46 +03:00
static struct test_node * nodes = NULL ;
2012-10-09 03:30:39 +04:00
static struct rnd_state rnd ;
2017-09-09 02:14:52 +03:00
static void insert ( struct test_node * node , struct rb_root_cached * root )
2012-10-09 03:30:39 +04:00
{
2017-09-09 02:14:52 +03:00
struct rb_node * * new = & root - > rb_root . rb_node , * parent = NULL ;
2012-10-09 03:31:15 +04:00
u32 key = node - > key ;
2012-10-09 03:30:39 +04:00
while ( * new ) {
parent = * new ;
2012-10-09 03:31:15 +04:00
if ( key < rb_entry ( parent , struct test_node , rb ) - > key )
2012-10-09 03:30:39 +04:00
new = & parent - > rb_left ;
else
new = & parent - > rb_right ;
}
rb_link_node ( & node - > rb , parent , new ) ;
2017-09-09 02:14:52 +03:00
rb_insert_color ( & node - > rb , & root - > rb_root ) ;
2012-10-09 03:30:39 +04:00
}
2017-09-09 02:14:52 +03:00
static void insert_cached ( struct test_node * node , struct rb_root_cached * root )
2012-10-09 03:30:39 +04:00
{
2017-09-09 02:14:52 +03:00
struct rb_node * * new = & root - > rb_root . rb_node , * parent = NULL ;
u32 key = node - > key ;
bool leftmost = true ;
while ( * new ) {
parent = * new ;
if ( key < rb_entry ( parent , struct test_node , rb ) - > key )
new = & parent - > rb_left ;
else {
new = & parent - > rb_right ;
leftmost = false ;
}
}
rb_link_node ( & node - > rb , parent , new ) ;
rb_insert_color_cached ( & node - > rb , root , leftmost ) ;
2012-10-09 03:30:39 +04:00
}
2017-09-09 02:14:52 +03:00
static inline void erase ( struct test_node * node , struct rb_root_cached * root )
{
rb_erase ( & node - > rb , & root - > rb_root ) ;
}
static inline void erase_cached ( struct test_node * node , struct rb_root_cached * root )
{
rb_erase_cached ( & node - > rb , root ) ;
}
2012-10-09 03:31:15 +04:00
static inline u32 augment_recompute ( struct test_node * node )
{
u32 max = node - > val , child_augmented ;
if ( node - > rb . rb_left ) {
child_augmented = rb_entry ( node - > rb . rb_left , struct test_node ,
rb ) - > augmented ;
if ( max < child_augmented )
max = child_augmented ;
}
if ( node - > rb . rb_right ) {
child_augmented = rb_entry ( node - > rb . rb_right , struct test_node ,
rb ) - > augmented ;
if ( max < child_augmented )
max = child_augmented ;
}
return max ;
}
2012-10-09 03:31:21 +04:00
RB_DECLARE_CALLBACKS ( static , augment_callbacks , struct test_node , rb ,
u32 , augmented , augment_recompute )
2012-10-09 03:31:17 +04:00
2017-09-09 02:14:52 +03:00
static void insert_augmented ( struct test_node * node ,
struct rb_root_cached * root )
2012-10-09 03:31:15 +04:00
{
2017-09-09 02:14:52 +03:00
struct rb_node * * new = & root - > rb_root . rb_node , * rb_parent = NULL ;
2012-10-09 03:31:15 +04:00
u32 key = node - > key ;
2012-10-09 03:31:17 +04:00
u32 val = node - > val ;
struct test_node * parent ;
2012-10-09 03:31:15 +04:00
while ( * new ) {
2012-10-09 03:31:17 +04:00
rb_parent = * new ;
parent = rb_entry ( rb_parent , struct test_node , rb ) ;
if ( parent - > augmented < val )
parent - > augmented = val ;
if ( key < parent - > key )
new = & parent - > rb . rb_left ;
2012-10-09 03:31:15 +04:00
else
2012-10-09 03:31:17 +04:00
new = & parent - > rb . rb_right ;
2012-10-09 03:31:15 +04:00
}
2012-10-09 03:31:17 +04:00
node - > augmented = val ;
rb_link_node ( & node - > rb , rb_parent , new ) ;
2017-09-09 02:14:52 +03:00
rb_insert_augmented ( & node - > rb , & root - > rb_root , & augment_callbacks ) ;
}
static void insert_augmented_cached ( struct test_node * node ,
struct rb_root_cached * root )
{
struct rb_node * * new = & root - > rb_root . rb_node , * rb_parent = NULL ;
u32 key = node - > key ;
u32 val = node - > val ;
struct test_node * parent ;
bool leftmost = true ;
while ( * new ) {
rb_parent = * new ;
parent = rb_entry ( rb_parent , struct test_node , rb ) ;
if ( parent - > augmented < val )
parent - > augmented = val ;
if ( key < parent - > key )
new = & parent - > rb . rb_left ;
else {
new = & parent - > rb . rb_right ;
leftmost = false ;
}
}
node - > augmented = val ;
rb_link_node ( & node - > rb , rb_parent , new ) ;
rb_insert_augmented_cached ( & node - > rb , root ,
leftmost , & augment_callbacks ) ;
}
static void erase_augmented ( struct test_node * node , struct rb_root_cached * root )
{
rb_erase_augmented ( & node - > rb , & root - > rb_root , & augment_callbacks ) ;
2012-10-09 03:31:15 +04:00
}
2017-09-09 02:14:52 +03:00
static void erase_augmented_cached ( struct test_node * node ,
struct rb_root_cached * root )
2012-10-09 03:31:15 +04:00
{
2017-09-09 02:14:52 +03:00
rb_erase_augmented_cached ( & node - > rb , root , & augment_callbacks ) ;
2012-10-09 03:31:15 +04:00
}
2012-10-09 03:30:39 +04:00
static void init ( void )
{
int i ;
2017-09-09 02:14:46 +03:00
for ( i = 0 ; i < nnodes ; i + + ) {
2012-12-18 04:04:23 +04:00
nodes [ i ] . key = prandom_u32_state ( & rnd ) ;
nodes [ i ] . val = prandom_u32_state ( & rnd ) ;
2012-10-09 03:31:15 +04:00
}
2012-10-09 03:30:39 +04:00
}
static bool is_red ( struct rb_node * rb )
{
return ! ( rb - > __rb_parent_color & 1 ) ;
}
static int black_path_count ( struct rb_node * rb )
{
int count ;
for ( count = 0 ; rb ; rb = rb_parent ( rb ) )
count + = ! is_red ( rb ) ;
return count ;
}
2014-01-24 03:56:06 +04:00
static void check_postorder_foreach ( int nr_nodes )
{
struct test_node * cur , * n ;
int count = 0 ;
2017-09-09 02:14:52 +03:00
rbtree_postorder_for_each_entry_safe ( cur , n , & root . rb_root , rb )
2014-01-24 03:56:06 +04:00
count + + ;
WARN_ON_ONCE ( count ! = nr_nodes ) ;
}
2013-09-12 01:25:17 +04:00
static void check_postorder ( int nr_nodes )
{
struct rb_node * rb ;
int count = 0 ;
2017-09-09 02:14:52 +03:00
for ( rb = rb_first_postorder ( & root . rb_root ) ; rb ; rb = rb_next_postorder ( rb ) )
2013-09-12 01:25:17 +04:00
count + + ;
WARN_ON_ONCE ( count ! = nr_nodes ) ;
}
2012-10-09 03:30:39 +04:00
static void check ( int nr_nodes )
{
struct rb_node * rb ;
2013-05-01 02:28:24 +04:00
int count = 0 , blacks = 0 ;
2012-10-09 03:30:39 +04:00
u32 prev_key = 0 ;
2017-09-09 02:14:52 +03:00
for ( rb = rb_first ( & root . rb_root ) ; rb ; rb = rb_next ( rb ) ) {
2012-10-09 03:30:39 +04:00
struct test_node * node = rb_entry ( rb , struct test_node , rb ) ;
WARN_ON_ONCE ( node - > key < prev_key ) ;
WARN_ON_ONCE ( is_red ( rb ) & &
( ! rb_parent ( rb ) | | is_red ( rb_parent ( rb ) ) ) ) ;
if ( ! count )
blacks = black_path_count ( rb ) ;
else
WARN_ON_ONCE ( ( ! rb - > rb_left | | ! rb - > rb_right ) & &
blacks ! = black_path_count ( rb ) ) ;
prev_key = node - > key ;
count + + ;
}
2013-05-01 02:28:24 +04:00
2012-10-09 03:30:39 +04:00
WARN_ON_ONCE ( count ! = nr_nodes ) ;
2017-09-09 02:14:52 +03:00
WARN_ON_ONCE ( count < ( 1 < < black_path_count ( rb_last ( & root . rb_root ) ) ) - 1 ) ;
2013-09-12 01:25:17 +04:00
check_postorder ( nr_nodes ) ;
2014-01-24 03:56:06 +04:00
check_postorder_foreach ( nr_nodes ) ;
2012-10-09 03:30:39 +04:00
}
2012-10-09 03:31:15 +04:00
static void check_augmented ( int nr_nodes )
{
struct rb_node * rb ;
check ( nr_nodes ) ;
2017-09-09 02:14:52 +03:00
for ( rb = rb_first ( & root . rb_root ) ; rb ; rb = rb_next ( rb ) ) {
2012-10-09 03:31:15 +04:00
struct test_node * node = rb_entry ( rb , struct test_node , rb ) ;
WARN_ON_ONCE ( node - > augmented ! = augment_recompute ( node ) ) ;
}
}
2013-05-01 02:28:25 +04:00
static int __init rbtree_test_init ( void )
2012-10-09 03:30:39 +04:00
{
int i , j ;
cycles_t time1 , time2 , time ;
2017-09-09 02:14:49 +03:00
struct rb_node * node ;
2012-10-09 03:30:39 +04:00
2017-09-09 02:14:46 +03:00
nodes = kmalloc ( nnodes * sizeof ( * nodes ) , GFP_KERNEL ) ;
if ( ! nodes )
return - ENOMEM ;
2012-10-09 03:30:39 +04:00
printk ( KERN_ALERT " rbtree testing " ) ;
2012-12-18 04:04:23 +04:00
prandom_seed_state ( & rnd , 3141592653589793238ULL ) ;
2012-10-09 03:30:39 +04:00
init ( ) ;
time1 = get_cycles ( ) ;
2017-09-09 02:14:46 +03:00
for ( i = 0 ; i < perf_loops ; i + + ) {
for ( j = 0 ; j < nnodes ; j + + )
2012-10-09 03:30:39 +04:00
insert ( nodes + j , & root ) ;
2017-09-09 02:14:46 +03:00
for ( j = 0 ; j < nnodes ; j + + )
2012-10-09 03:30:39 +04:00
erase ( nodes + j , & root ) ;
}
time2 = get_cycles ( ) ;
time = time2 - time1 ;
2017-09-09 02:14:46 +03:00
time = div_u64 ( time , perf_loops ) ;
2017-09-09 02:14:52 +03:00
printk ( " -> test 1 (latency of nnodes insert+delete): %llu cycles \n " ,
( unsigned long long ) time ) ;
time1 = get_cycles ( ) ;
for ( i = 0 ; i < perf_loops ; i + + ) {
for ( j = 0 ; j < nnodes ; j + + )
insert_cached ( nodes + j , & root ) ;
for ( j = 0 ; j < nnodes ; j + + )
erase_cached ( nodes + j , & root ) ;
}
time2 = get_cycles ( ) ;
time = time2 - time1 ;
time = div_u64 ( time , perf_loops ) ;
printk ( " -> test 2 (latency of nnodes cached insert+delete): %llu cycles \n " ,
( unsigned long long ) time ) ;
2012-10-09 03:30:39 +04:00
2017-09-09 02:14:49 +03:00
for ( i = 0 ; i < nnodes ; i + + )
insert ( nodes + i , & root ) ;
time1 = get_cycles ( ) ;
for ( i = 0 ; i < perf_loops ; i + + ) {
2017-09-09 02:14:52 +03:00
for ( node = rb_first ( & root . rb_root ) ; node ; node = rb_next ( node ) )
2017-09-09 02:14:49 +03:00
;
}
time2 = get_cycles ( ) ;
time = time2 - time1 ;
time = div_u64 ( time , perf_loops ) ;
2017-09-09 02:14:52 +03:00
printk ( " -> test 3 (latency of inorder traversal): %llu cycles \n " ,
( unsigned long long ) time ) ;
time1 = get_cycles ( ) ;
for ( i = 0 ; i < perf_loops ; i + + )
node = rb_first ( & root . rb_root ) ;
time2 = get_cycles ( ) ;
time = time2 - time1 ;
time = div_u64 ( time , perf_loops ) ;
printk ( " -> test 4 (latency to fetch first node) \n " ) ;
printk ( " non-cached: %llu cycles \n " , ( unsigned long long ) time ) ;
time1 = get_cycles ( ) ;
for ( i = 0 ; i < perf_loops ; i + + )
node = rb_first_cached ( & root ) ;
time2 = get_cycles ( ) ;
time = time2 - time1 ;
time = div_u64 ( time , perf_loops ) ;
printk ( " cached: %llu cycles \n " , ( unsigned long long ) time ) ;
2017-09-09 02:14:49 +03:00
for ( i = 0 ; i < nnodes ; i + + )
erase ( nodes + i , & root ) ;
/* run checks */
2017-09-09 02:14:46 +03:00
for ( i = 0 ; i < check_loops ; i + + ) {
2012-10-09 03:30:39 +04:00
init ( ) ;
2017-09-09 02:14:46 +03:00
for ( j = 0 ; j < nnodes ; j + + ) {
2012-10-09 03:30:39 +04:00
check ( j ) ;
insert ( nodes + j , & root ) ;
}
2017-09-09 02:14:46 +03:00
for ( j = 0 ; j < nnodes ; j + + ) {
check ( nnodes - j ) ;
2012-10-09 03:30:39 +04:00
erase ( nodes + j , & root ) ;
}
check ( 0 ) ;
}
2012-10-09 03:31:15 +04:00
printk ( KERN_ALERT " augmented rbtree testing " ) ;
init ( ) ;
time1 = get_cycles ( ) ;
2017-09-09 02:14:46 +03:00
for ( i = 0 ; i < perf_loops ; i + + ) {
for ( j = 0 ; j < nnodes ; j + + )
2012-10-09 03:31:15 +04:00
insert_augmented ( nodes + j , & root ) ;
2017-09-09 02:14:46 +03:00
for ( j = 0 ; j < nnodes ; j + + )
2012-10-09 03:31:15 +04:00
erase_augmented ( nodes + j , & root ) ;
}
time2 = get_cycles ( ) ;
time = time2 - time1 ;
2017-09-09 02:14:46 +03:00
time = div_u64 ( time , perf_loops ) ;
2017-09-09 02:14:49 +03:00
printk ( " -> test 1 (latency of nnodes insert+delete): %llu cycles \n " , ( unsigned long long ) time ) ;
2012-10-09 03:31:15 +04:00
2017-09-09 02:14:52 +03:00
time1 = get_cycles ( ) ;
for ( i = 0 ; i < perf_loops ; i + + ) {
for ( j = 0 ; j < nnodes ; j + + )
insert_augmented_cached ( nodes + j , & root ) ;
for ( j = 0 ; j < nnodes ; j + + )
erase_augmented_cached ( nodes + j , & root ) ;
}
time2 = get_cycles ( ) ;
time = time2 - time1 ;
time = div_u64 ( time , perf_loops ) ;
printk ( " -> test 2 (latency of nnodes cached insert+delete): %llu cycles \n " , ( unsigned long long ) time ) ;
2017-09-09 02:14:46 +03:00
for ( i = 0 ; i < check_loops ; i + + ) {
2012-10-09 03:31:15 +04:00
init ( ) ;
2017-09-09 02:14:46 +03:00
for ( j = 0 ; j < nnodes ; j + + ) {
2012-10-09 03:31:15 +04:00
check_augmented ( j ) ;
insert_augmented ( nodes + j , & root ) ;
}
2017-09-09 02:14:46 +03:00
for ( j = 0 ; j < nnodes ; j + + ) {
check_augmented ( nnodes - j ) ;
2012-10-09 03:31:15 +04:00
erase_augmented ( nodes + j , & root ) ;
}
check_augmented ( 0 ) ;
}
2017-09-09 02:14:46 +03:00
kfree ( nodes ) ;
2012-10-09 03:30:39 +04:00
return - EAGAIN ; /* Fail will directly unload the module */
}
2013-05-01 02:28:25 +04:00
static void __exit rbtree_test_exit ( void )
2012-10-09 03:30:39 +04:00
{
printk ( KERN_ALERT " test exit \n " ) ;
}
module_init ( rbtree_test_init )
module_exit ( rbtree_test_exit )
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Michel Lespinasse " ) ;
MODULE_DESCRIPTION ( " Red Black Tree test " ) ;