2018-11-14 11:22:28 +03:00
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/random.h>
# include <linux/objagg.h>
struct tokey {
unsigned int id ;
} ;
# define NUM_KEYS 32
static int key_id_index ( unsigned int key_id )
{
if ( key_id > = NUM_KEYS ) {
WARN_ON ( 1 ) ;
return 0 ;
}
return key_id ;
}
# define BUF_LEN 128
struct world {
unsigned int root_count ;
unsigned int delta_count ;
char next_root_buf [ BUF_LEN ] ;
struct objagg_obj * objagg_objs [ NUM_KEYS ] ;
unsigned int key_refs [ NUM_KEYS ] ;
} ;
struct root {
struct tokey key ;
char buf [ BUF_LEN ] ;
} ;
struct delta {
unsigned int key_id_diff ;
} ;
static struct objagg_obj * world_obj_get ( struct world * world ,
struct objagg * objagg ,
unsigned int key_id )
{
struct objagg_obj * objagg_obj ;
struct tokey key ;
int err ;
key . id = key_id ;
objagg_obj = objagg_obj_get ( objagg , & key ) ;
if ( IS_ERR ( objagg_obj ) ) {
pr_err ( " Key %u: Failed to get object. \n " , key_id ) ;
return objagg_obj ;
}
if ( ! world - > key_refs [ key_id_index ( key_id ) ] ) {
world - > objagg_objs [ key_id_index ( key_id ) ] = objagg_obj ;
} else if ( world - > objagg_objs [ key_id_index ( key_id ) ] ! = objagg_obj ) {
pr_err ( " Key %u: God another object for the same key. \n " ,
key_id ) ;
err = - EINVAL ;
goto err_key_id_check ;
}
world - > key_refs [ key_id_index ( key_id ) ] + + ;
return objagg_obj ;
err_key_id_check :
objagg_obj_put ( objagg , objagg_obj ) ;
return ERR_PTR ( err ) ;
}
static void world_obj_put ( struct world * world , struct objagg * objagg ,
unsigned int key_id )
{
struct objagg_obj * objagg_obj ;
if ( ! world - > key_refs [ key_id_index ( key_id ) ] )
return ;
objagg_obj = world - > objagg_objs [ key_id_index ( key_id ) ] ;
objagg_obj_put ( objagg , objagg_obj ) ;
world - > key_refs [ key_id_index ( key_id ) ] - - ;
}
# define MAX_KEY_ID_DIFF 5
2019-02-07 14:22:46 +03:00
static bool delta_check ( void * priv , const void * parent_obj , const void * obj )
{
const struct tokey * parent_key = parent_obj ;
const struct tokey * key = obj ;
int diff = key - > id - parent_key - > id ;
return diff > = 0 & & diff < = MAX_KEY_ID_DIFF ;
}
2018-11-14 11:22:28 +03:00
static void * delta_create ( void * priv , void * parent_obj , void * obj )
{
struct tokey * parent_key = parent_obj ;
struct world * world = priv ;
struct tokey * key = obj ;
int diff = key - > id - parent_key - > id ;
struct delta * delta ;
2019-02-07 14:22:46 +03:00
if ( ! delta_check ( priv , parent_obj , obj ) )
2018-11-14 11:22:28 +03:00
return ERR_PTR ( - EINVAL ) ;
delta = kzalloc ( sizeof ( * delta ) , GFP_KERNEL ) ;
if ( ! delta )
return ERR_PTR ( - ENOMEM ) ;
delta - > key_id_diff = diff ;
world - > delta_count + + ;
return delta ;
}
static void delta_destroy ( void * priv , void * delta_priv )
{
struct delta * delta = delta_priv ;
struct world * world = priv ;
world - > delta_count - - ;
kfree ( delta ) ;
}
2019-02-07 14:22:46 +03:00
static void * root_create ( void * priv , void * obj , unsigned int id )
2018-11-14 11:22:28 +03:00
{
struct world * world = priv ;
struct tokey * key = obj ;
struct root * root ;
root = kzalloc ( sizeof ( * root ) , GFP_KERNEL ) ;
if ( ! root )
return ERR_PTR ( - ENOMEM ) ;
memcpy ( & root - > key , key , sizeof ( root - > key ) ) ;
memcpy ( root - > buf , world - > next_root_buf , sizeof ( root - > buf ) ) ;
world - > root_count + + ;
return root ;
}
static void root_destroy ( void * priv , void * root_priv )
{
struct root * root = root_priv ;
struct world * world = priv ;
world - > root_count - - ;
kfree ( root ) ;
}
static int test_nodelta_obj_get ( struct world * world , struct objagg * objagg ,
unsigned int key_id , bool should_create_root )
{
unsigned int orig_root_count = world - > root_count ;
struct objagg_obj * objagg_obj ;
const struct root * root ;
int err ;
if ( should_create_root )
2022-10-05 18:49:46 +03:00
get_random_bytes ( world - > next_root_buf ,
2018-11-14 11:22:28 +03:00
sizeof ( world - > next_root_buf ) ) ;
objagg_obj = world_obj_get ( world , objagg , key_id ) ;
if ( IS_ERR ( objagg_obj ) ) {
pr_err ( " Key %u: Failed to get object. \n " , key_id ) ;
return PTR_ERR ( objagg_obj ) ;
}
if ( should_create_root ) {
if ( world - > root_count ! = orig_root_count + 1 ) {
pr_err ( " Key %u: Root was not created \n " , key_id ) ;
err = - EINVAL ;
goto err_check_root_count ;
}
} else {
if ( world - > root_count ! = orig_root_count ) {
pr_err ( " Key %u: Root was incorrectly created \n " ,
key_id ) ;
err = - EINVAL ;
goto err_check_root_count ;
}
}
root = objagg_obj_root_priv ( objagg_obj ) ;
if ( root - > key . id ! = key_id ) {
pr_err ( " Key %u: Root has unexpected key id \n " , key_id ) ;
err = - EINVAL ;
goto err_check_key_id ;
}
if ( should_create_root & &
memcmp ( world - > next_root_buf , root - > buf , sizeof ( root - > buf ) ) ) {
pr_err ( " Key %u: Buffer does not match the expected content \n " ,
key_id ) ;
err = - EINVAL ;
goto err_check_buf ;
}
return 0 ;
err_check_buf :
err_check_key_id :
err_check_root_count :
objagg_obj_put ( objagg , objagg_obj ) ;
return err ;
}
static int test_nodelta_obj_put ( struct world * world , struct objagg * objagg ,
unsigned int key_id , bool should_destroy_root )
{
unsigned int orig_root_count = world - > root_count ;
world_obj_put ( world , objagg , key_id ) ;
if ( should_destroy_root ) {
if ( world - > root_count ! = orig_root_count - 1 ) {
pr_err ( " Key %u: Root was not destroyed \n " , key_id ) ;
return - EINVAL ;
}
} else {
if ( world - > root_count ! = orig_root_count ) {
pr_err ( " Key %u: Root was incorrectly destroyed \n " ,
key_id ) ;
return - EINVAL ;
}
}
return 0 ;
}
static int check_stats_zero ( struct objagg * objagg )
{
const struct objagg_stats * stats ;
int err = 0 ;
stats = objagg_stats_get ( objagg ) ;
if ( IS_ERR ( stats ) )
return PTR_ERR ( stats ) ;
if ( stats - > stats_info_count ! = 0 ) {
pr_err ( " Stats: Object count is not zero while it should be \n " ) ;
err = - EINVAL ;
}
objagg_stats_put ( stats ) ;
return err ;
}
static int check_stats_nodelta ( struct objagg * objagg )
{
const struct objagg_stats * stats ;
int i ;
int err ;
stats = objagg_stats_get ( objagg ) ;
if ( IS_ERR ( stats ) )
return PTR_ERR ( stats ) ;
if ( stats - > stats_info_count ! = NUM_KEYS ) {
pr_err ( " Stats: Unexpected object count (%u expected, %u returned) \n " ,
NUM_KEYS , stats - > stats_info_count ) ;
err = - EINVAL ;
goto stats_put ;
}
for ( i = 0 ; i < stats - > stats_info_count ; i + + ) {
if ( stats - > stats_info [ i ] . stats . user_count ! = 2 ) {
pr_err ( " Stats: incorrect user count \n " ) ;
err = - EINVAL ;
goto stats_put ;
}
if ( stats - > stats_info [ i ] . stats . delta_user_count ! = 2 ) {
pr_err ( " Stats: incorrect delta user count \n " ) ;
err = - EINVAL ;
goto stats_put ;
}
}
err = 0 ;
stats_put :
objagg_stats_put ( stats ) ;
return err ;
}
2019-02-07 14:22:46 +03:00
static bool delta_check_dummy ( void * priv , const void * parent_obj ,
const void * obj )
{
return false ;
}
2018-11-14 11:22:28 +03:00
static void * delta_create_dummy ( void * priv , void * parent_obj , void * obj )
{
return ERR_PTR ( - EOPNOTSUPP ) ;
}
static void delta_destroy_dummy ( void * priv , void * delta_priv )
{
}
static const struct objagg_ops nodelta_ops = {
. obj_size = sizeof ( struct tokey ) ,
2019-02-07 14:22:46 +03:00
. delta_check = delta_check_dummy ,
2018-11-14 11:22:28 +03:00
. delta_create = delta_create_dummy ,
. delta_destroy = delta_destroy_dummy ,
. root_create = root_create ,
. root_destroy = root_destroy ,
} ;
static int test_nodelta ( void )
{
struct world world = { } ;
struct objagg * objagg ;
int i ;
int err ;
2019-02-07 14:22:46 +03:00
objagg = objagg_create ( & nodelta_ops , NULL , & world ) ;
2018-11-14 11:22:28 +03:00
if ( IS_ERR ( objagg ) )
return PTR_ERR ( objagg ) ;
err = check_stats_zero ( objagg ) ;
if ( err )
goto err_stats_first_zero ;
/* First round of gets, the root objects should be created */
for ( i = 0 ; i < NUM_KEYS ; i + + ) {
err = test_nodelta_obj_get ( & world , objagg , i , true ) ;
if ( err )
goto err_obj_first_get ;
}
/* Do the second round of gets, all roots are already created,
* make sure that no new root is created
*/
for ( i = 0 ; i < NUM_KEYS ; i + + ) {
err = test_nodelta_obj_get ( & world , objagg , i , false ) ;
if ( err )
goto err_obj_second_get ;
}
err = check_stats_nodelta ( objagg ) ;
if ( err )
goto err_stats_nodelta ;
for ( i = NUM_KEYS - 1 ; i > = 0 ; i - - ) {
err = test_nodelta_obj_put ( & world , objagg , i , false ) ;
if ( err )
goto err_obj_first_put ;
}
for ( i = NUM_KEYS - 1 ; i > = 0 ; i - - ) {
err = test_nodelta_obj_put ( & world , objagg , i , true ) ;
if ( err )
goto err_obj_second_put ;
}
err = check_stats_zero ( objagg ) ;
if ( err )
goto err_stats_second_zero ;
objagg_destroy ( objagg ) ;
return 0 ;
err_stats_nodelta :
err_obj_first_put :
err_obj_second_get :
for ( i - - ; i > = 0 ; i - - )
world_obj_put ( & world , objagg , i ) ;
i = NUM_KEYS ;
err_obj_first_get :
err_obj_second_put :
for ( i - - ; i > = 0 ; i - - )
world_obj_put ( & world , objagg , i ) ;
err_stats_first_zero :
err_stats_second_zero :
objagg_destroy ( objagg ) ;
return err ;
}
static const struct objagg_ops delta_ops = {
. obj_size = sizeof ( struct tokey ) ,
2019-02-07 14:22:46 +03:00
. delta_check = delta_check ,
2018-11-14 11:22:28 +03:00
. delta_create = delta_create ,
. delta_destroy = delta_destroy ,
. root_create = root_create ,
. root_destroy = root_destroy ,
} ;
enum action {
ACTION_GET ,
ACTION_PUT ,
} ;
enum expect_delta {
EXPECT_DELTA_SAME ,
EXPECT_DELTA_INC ,
EXPECT_DELTA_DEC ,
} ;
enum expect_root {
EXPECT_ROOT_SAME ,
EXPECT_ROOT_INC ,
EXPECT_ROOT_DEC ,
} ;
struct expect_stats_info {
struct objagg_obj_stats stats ;
bool is_root ;
unsigned int key_id ;
} ;
struct expect_stats {
unsigned int info_count ;
struct expect_stats_info info [ NUM_KEYS ] ;
} ;
struct action_item {
unsigned int key_id ;
enum action action ;
enum expect_delta expect_delta ;
enum expect_root expect_root ;
struct expect_stats expect_stats ;
} ;
# define EXPECT_STATS(count, ...) \
{ \
. info_count = count , \
. info = { __VA_ARGS__ } \
}
# define ROOT(key_id, user_count, delta_user_count) \
{ { user_count , delta_user_count } , true , key_id }
# define DELTA(key_id, user_count) \
{ { user_count , user_count } , false , key_id }
static const struct action_item action_items [ ] = {
{
1 , ACTION_GET , EXPECT_DELTA_SAME , EXPECT_ROOT_INC ,
EXPECT_STATS ( 1 , ROOT ( 1 , 1 , 1 ) ) ,
} , /* r: 1 d: */
{
7 , ACTION_GET , EXPECT_DELTA_SAME , EXPECT_ROOT_INC ,
EXPECT_STATS ( 2 , ROOT ( 1 , 1 , 1 ) , ROOT ( 7 , 1 , 1 ) ) ,
} , /* r: 1, 7 d: */
{
3 , ACTION_GET , EXPECT_DELTA_INC , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 3 , ROOT ( 1 , 1 , 2 ) , ROOT ( 7 , 1 , 1 ) ,
DELTA ( 3 , 1 ) ) ,
} , /* r: 1, 7 d: 3^1 */
{
5 , ACTION_GET , EXPECT_DELTA_INC , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 4 , ROOT ( 1 , 1 , 3 ) , ROOT ( 7 , 1 , 1 ) ,
DELTA ( 3 , 1 ) , DELTA ( 5 , 1 ) ) ,
} , /* r: 1, 7 d: 3^1, 5^1 */
{
3 , ACTION_GET , EXPECT_DELTA_SAME , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 4 , ROOT ( 1 , 1 , 4 ) , ROOT ( 7 , 1 , 1 ) ,
DELTA ( 3 , 2 ) , DELTA ( 5 , 1 ) ) ,
} , /* r: 1, 7 d: 3^1, 3^1, 5^1 */
{
1 , ACTION_GET , EXPECT_DELTA_SAME , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 4 , ROOT ( 1 , 2 , 5 ) , ROOT ( 7 , 1 , 1 ) ,
DELTA ( 3 , 2 ) , DELTA ( 5 , 1 ) ) ,
} , /* r: 1, 1, 7 d: 3^1, 3^1, 5^1 */
{
30 , ACTION_GET , EXPECT_DELTA_SAME , EXPECT_ROOT_INC ,
EXPECT_STATS ( 5 , ROOT ( 1 , 2 , 5 ) , ROOT ( 7 , 1 , 1 ) , ROOT ( 30 , 1 , 1 ) ,
DELTA ( 3 , 2 ) , DELTA ( 5 , 1 ) ) ,
} , /* r: 1, 1, 7, 30 d: 3^1, 3^1, 5^1 */
{
8 , ACTION_GET , EXPECT_DELTA_INC , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 6 , ROOT ( 1 , 2 , 5 ) , ROOT ( 7 , 1 , 2 ) , ROOT ( 30 , 1 , 1 ) ,
DELTA ( 3 , 2 ) , DELTA ( 5 , 1 ) , DELTA ( 8 , 1 ) ) ,
} , /* r: 1, 1, 7, 30 d: 3^1, 3^1, 5^1, 8^7 */
{
8 , ACTION_GET , EXPECT_DELTA_SAME , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 6 , ROOT ( 1 , 2 , 5 ) , ROOT ( 7 , 1 , 3 ) , ROOT ( 30 , 1 , 1 ) ,
DELTA ( 3 , 2 ) , DELTA ( 8 , 2 ) , DELTA ( 5 , 1 ) ) ,
} , /* r: 1, 1, 7, 30 d: 3^1, 3^1, 5^1, 8^7, 8^7 */
{
3 , ACTION_PUT , EXPECT_DELTA_SAME , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 6 , ROOT ( 1 , 2 , 4 ) , ROOT ( 7 , 1 , 3 ) , ROOT ( 30 , 1 , 1 ) ,
DELTA ( 8 , 2 ) , DELTA ( 3 , 1 ) , DELTA ( 5 , 1 ) ) ,
} , /* r: 1, 1, 7, 30 d: 3^1, 5^1, 8^7, 8^7 */
{
3 , ACTION_PUT , EXPECT_DELTA_DEC , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 5 , ROOT ( 1 , 2 , 3 ) , ROOT ( 7 , 1 , 3 ) , ROOT ( 30 , 1 , 1 ) ,
DELTA ( 8 , 2 ) , DELTA ( 5 , 1 ) ) ,
} , /* r: 1, 1, 7, 30 d: 5^1, 8^7, 8^7 */
{
1 , ACTION_PUT , EXPECT_DELTA_SAME , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 5 , ROOT ( 7 , 1 , 3 ) , ROOT ( 1 , 1 , 2 ) , ROOT ( 30 , 1 , 1 ) ,
DELTA ( 8 , 2 ) , DELTA ( 5 , 1 ) ) ,
} , /* r: 1, 7, 30 d: 5^1, 8^7, 8^7 */
{
1 , ACTION_PUT , EXPECT_DELTA_SAME , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 5 , ROOT ( 7 , 1 , 3 ) , ROOT ( 30 , 1 , 1 ) , ROOT ( 1 , 0 , 1 ) ,
DELTA ( 8 , 2 ) , DELTA ( 5 , 1 ) ) ,
} , /* r: 7, 30 d: 5^1, 8^7, 8^7 */
{
5 , ACTION_PUT , EXPECT_DELTA_DEC , EXPECT_ROOT_DEC ,
EXPECT_STATS ( 3 , ROOT ( 7 , 1 , 3 ) , ROOT ( 30 , 1 , 1 ) ,
DELTA ( 8 , 2 ) ) ,
} , /* r: 7, 30 d: 8^7, 8^7 */
{
5 , ACTION_GET , EXPECT_DELTA_SAME , EXPECT_ROOT_INC ,
EXPECT_STATS ( 4 , ROOT ( 7 , 1 , 3 ) , ROOT ( 30 , 1 , 1 ) , ROOT ( 5 , 1 , 1 ) ,
DELTA ( 8 , 2 ) ) ,
} , /* r: 7, 30, 5 d: 8^7, 8^7 */
{
6 , ACTION_GET , EXPECT_DELTA_INC , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 5 , ROOT ( 7 , 1 , 3 ) , ROOT ( 5 , 1 , 2 ) , ROOT ( 30 , 1 , 1 ) ,
DELTA ( 8 , 2 ) , DELTA ( 6 , 1 ) ) ,
} , /* r: 7, 30, 5 d: 8^7, 8^7, 6^5 */
{
8 , ACTION_GET , EXPECT_DELTA_SAME , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 5 , ROOT ( 7 , 1 , 4 ) , ROOT ( 5 , 1 , 2 ) , ROOT ( 30 , 1 , 1 ) ,
DELTA ( 8 , 3 ) , DELTA ( 6 , 1 ) ) ,
} , /* r: 7, 30, 5 d: 8^7, 8^7, 8^7, 6^5 */
{
8 , ACTION_PUT , EXPECT_DELTA_SAME , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 5 , ROOT ( 7 , 1 , 3 ) , ROOT ( 5 , 1 , 2 ) , ROOT ( 30 , 1 , 1 ) ,
DELTA ( 8 , 2 ) , DELTA ( 6 , 1 ) ) ,
} , /* r: 7, 30, 5 d: 8^7, 8^7, 6^5 */
{
8 , ACTION_PUT , EXPECT_DELTA_SAME , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 5 , ROOT ( 7 , 1 , 2 ) , ROOT ( 5 , 1 , 2 ) , ROOT ( 30 , 1 , 1 ) ,
DELTA ( 8 , 1 ) , DELTA ( 6 , 1 ) ) ,
} , /* r: 7, 30, 5 d: 8^7, 6^5 */
{
8 , ACTION_PUT , EXPECT_DELTA_DEC , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 4 , ROOT ( 5 , 1 , 2 ) , ROOT ( 7 , 1 , 1 ) , ROOT ( 30 , 1 , 1 ) ,
DELTA ( 6 , 1 ) ) ,
} , /* r: 7, 30, 5 d: 6^5 */
{
8 , ACTION_GET , EXPECT_DELTA_INC , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 5 , ROOT ( 5 , 1 , 3 ) , ROOT ( 7 , 1 , 1 ) , ROOT ( 30 , 1 , 1 ) ,
DELTA ( 6 , 1 ) , DELTA ( 8 , 1 ) ) ,
} , /* r: 7, 30, 5 d: 6^5, 8^5 */
{
7 , ACTION_PUT , EXPECT_DELTA_SAME , EXPECT_ROOT_DEC ,
EXPECT_STATS ( 4 , ROOT ( 5 , 1 , 3 ) , ROOT ( 30 , 1 , 1 ) ,
DELTA ( 6 , 1 ) , DELTA ( 8 , 1 ) ) ,
} , /* r: 30, 5 d: 6^5, 8^5 */
{
30 , ACTION_PUT , EXPECT_DELTA_SAME , EXPECT_ROOT_DEC ,
EXPECT_STATS ( 3 , ROOT ( 5 , 1 , 3 ) ,
DELTA ( 6 , 1 ) , DELTA ( 8 , 1 ) ) ,
} , /* r: 5 d: 6^5, 8^5 */
{
5 , ACTION_PUT , EXPECT_DELTA_SAME , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 3 , ROOT ( 5 , 0 , 2 ) ,
DELTA ( 6 , 1 ) , DELTA ( 8 , 1 ) ) ,
} , /* r: d: 6^5, 8^5 */
{
6 , ACTION_PUT , EXPECT_DELTA_DEC , EXPECT_ROOT_SAME ,
EXPECT_STATS ( 2 , ROOT ( 5 , 0 , 1 ) ,
DELTA ( 8 , 1 ) ) ,
} , /* r: d: 6^5 */
{
8 , ACTION_PUT , EXPECT_DELTA_DEC , EXPECT_ROOT_DEC ,
EXPECT_STATS ( 0 , ) ,
} , /* r: d: */
} ;
static int check_expect ( struct world * world ,
const struct action_item * action_item ,
unsigned int orig_delta_count ,
unsigned int orig_root_count )
{
unsigned int key_id = action_item - > key_id ;
switch ( action_item - > expect_delta ) {
case EXPECT_DELTA_SAME :
if ( orig_delta_count ! = world - > delta_count ) {
pr_err ( " Key %u: Delta count changed while expected to remain the same. \n " ,
key_id ) ;
return - EINVAL ;
}
break ;
case EXPECT_DELTA_INC :
if ( WARN_ON ( action_item - > action = = ACTION_PUT ) )
return - EINVAL ;
if ( orig_delta_count + 1 ! = world - > delta_count ) {
pr_err ( " Key %u: Delta count was not incremented. \n " ,
key_id ) ;
return - EINVAL ;
}
break ;
case EXPECT_DELTA_DEC :
if ( WARN_ON ( action_item - > action = = ACTION_GET ) )
return - EINVAL ;
if ( orig_delta_count - 1 ! = world - > delta_count ) {
pr_err ( " Key %u: Delta count was not decremented. \n " ,
key_id ) ;
return - EINVAL ;
}
break ;
}
switch ( action_item - > expect_root ) {
case EXPECT_ROOT_SAME :
if ( orig_root_count ! = world - > root_count ) {
pr_err ( " Key %u: Root count changed while expected to remain the same. \n " ,
key_id ) ;
return - EINVAL ;
}
break ;
case EXPECT_ROOT_INC :
if ( WARN_ON ( action_item - > action = = ACTION_PUT ) )
return - EINVAL ;
if ( orig_root_count + 1 ! = world - > root_count ) {
pr_err ( " Key %u: Root count was not incremented. \n " ,
key_id ) ;
return - EINVAL ;
}
break ;
case EXPECT_ROOT_DEC :
if ( WARN_ON ( action_item - > action = = ACTION_GET ) )
return - EINVAL ;
if ( orig_root_count - 1 ! = world - > root_count ) {
pr_err ( " Key %u: Root count was not decremented. \n " ,
key_id ) ;
return - EINVAL ;
}
}
return 0 ;
}
static unsigned int obj_to_key_id ( struct objagg_obj * objagg_obj )
{
const struct tokey * root_key ;
const struct delta * delta ;
unsigned int key_id ;
root_key = objagg_obj_root_priv ( objagg_obj ) ;
key_id = root_key - > id ;
delta = objagg_obj_delta_priv ( objagg_obj ) ;
if ( delta )
key_id + = delta - > key_id_diff ;
return key_id ;
}
static int
check_expect_stats_nums ( const struct objagg_obj_stats_info * stats_info ,
const struct expect_stats_info * expect_stats_info ,
const char * * errmsg )
{
if ( stats_info - > is_root ! = expect_stats_info - > is_root ) {
if ( errmsg )
* errmsg = " Incorrect root/delta indication " ;
return - EINVAL ;
}
if ( stats_info - > stats . user_count ! =
expect_stats_info - > stats . user_count ) {
if ( errmsg )
* errmsg = " Incorrect user count " ;
return - EINVAL ;
}
if ( stats_info - > stats . delta_user_count ! =
expect_stats_info - > stats . delta_user_count ) {
if ( errmsg )
* errmsg = " Incorrect delta user count " ;
return - EINVAL ;
}
return 0 ;
}
static int
check_expect_stats_key_id ( const struct objagg_obj_stats_info * stats_info ,
const struct expect_stats_info * expect_stats_info ,
const char * * errmsg )
{
if ( obj_to_key_id ( stats_info - > objagg_obj ) ! =
expect_stats_info - > key_id ) {
if ( errmsg )
* errmsg = " incorrect key id " ;
return - EINVAL ;
}
return 0 ;
}
static int check_expect_stats_neigh ( const struct objagg_stats * stats ,
const struct expect_stats * expect_stats ,
int pos )
{
int i ;
int err ;
for ( i = pos - 1 ; i > = 0 ; i - - ) {
err = check_expect_stats_nums ( & stats - > stats_info [ i ] ,
& expect_stats - > info [ pos ] , NULL ) ;
if ( err )
break ;
err = check_expect_stats_key_id ( & stats - > stats_info [ i ] ,
& expect_stats - > info [ pos ] , NULL ) ;
if ( ! err )
return 0 ;
}
for ( i = pos + 1 ; i < stats - > stats_info_count ; i + + ) {
err = check_expect_stats_nums ( & stats - > stats_info [ i ] ,
& expect_stats - > info [ pos ] , NULL ) ;
if ( err )
break ;
err = check_expect_stats_key_id ( & stats - > stats_info [ i ] ,
& expect_stats - > info [ pos ] , NULL ) ;
if ( ! err )
return 0 ;
}
return - EINVAL ;
}
static int __check_expect_stats ( const struct objagg_stats * stats ,
const struct expect_stats * expect_stats ,
const char * * errmsg )
{
int i ;
int err ;
if ( stats - > stats_info_count ! = expect_stats - > info_count ) {
* errmsg = " Unexpected object count " ;
return - EINVAL ;
}
for ( i = 0 ; i < stats - > stats_info_count ; i + + ) {
err = check_expect_stats_nums ( & stats - > stats_info [ i ] ,
& expect_stats - > info [ i ] , errmsg ) ;
if ( err )
return err ;
err = check_expect_stats_key_id ( & stats - > stats_info [ i ] ,
& expect_stats - > info [ i ] , errmsg ) ;
if ( err ) {
/* It is possible that one of the neighbor stats with
* same numbers have the correct key id , so check it
*/
err = check_expect_stats_neigh ( stats , expect_stats , i ) ;
if ( err )
return err ;
}
}
return 0 ;
}
static int check_expect_stats ( struct objagg * objagg ,
const struct expect_stats * expect_stats ,
const char * * errmsg )
{
const struct objagg_stats * stats ;
int err ;
stats = objagg_stats_get ( objagg ) ;
2019-02-13 11:59:31 +03:00
if ( IS_ERR ( stats ) ) {
* errmsg = " objagg_stats_get() failed. " ;
2018-11-14 11:22:28 +03:00
return PTR_ERR ( stats ) ;
2019-02-13 11:59:31 +03:00
}
2018-11-14 11:22:28 +03:00
err = __check_expect_stats ( stats , expect_stats , errmsg ) ;
objagg_stats_put ( stats ) ;
return err ;
}
static int test_delta_action_item ( struct world * world ,
struct objagg * objagg ,
const struct action_item * action_item ,
bool inverse )
{
unsigned int orig_delta_count = world - > delta_count ;
unsigned int orig_root_count = world - > root_count ;
unsigned int key_id = action_item - > key_id ;
enum action action = action_item - > action ;
struct objagg_obj * objagg_obj ;
const char * errmsg ;
int err ;
if ( inverse )
action = action = = ACTION_GET ? ACTION_PUT : ACTION_GET ;
switch ( action ) {
case ACTION_GET :
objagg_obj = world_obj_get ( world , objagg , key_id ) ;
if ( IS_ERR ( objagg_obj ) )
return PTR_ERR ( objagg_obj ) ;
break ;
case ACTION_PUT :
world_obj_put ( world , objagg , key_id ) ;
break ;
}
if ( inverse )
return 0 ;
err = check_expect ( world , action_item ,
orig_delta_count , orig_root_count ) ;
if ( err )
goto errout ;
err = check_expect_stats ( objagg , & action_item - > expect_stats , & errmsg ) ;
if ( err ) {
pr_err ( " Key %u: Stats: %s \n " , action_item - > key_id , errmsg ) ;
goto errout ;
}
return 0 ;
errout :
/* This can only happen when action is not inversed.
* So in case of an error , cleanup by doing inverse action .
*/
test_delta_action_item ( world , objagg , action_item , true ) ;
return err ;
}
static int test_delta ( void )
{
struct world world = { } ;
struct objagg * objagg ;
int i ;
int err ;
2019-02-07 14:22:46 +03:00
objagg = objagg_create ( & delta_ops , NULL , & world ) ;
2018-11-14 11:22:28 +03:00
if ( IS_ERR ( objagg ) )
return PTR_ERR ( objagg ) ;
for ( i = 0 ; i < ARRAY_SIZE ( action_items ) ; i + + ) {
err = test_delta_action_item ( & world , objagg ,
& action_items [ i ] , false ) ;
if ( err )
goto err_do_action_item ;
}
objagg_destroy ( objagg ) ;
return 0 ;
err_do_action_item :
for ( i - - ; i > = 0 ; i - - )
test_delta_action_item ( & world , objagg , & action_items [ i ] , true ) ;
objagg_destroy ( objagg ) ;
return err ;
}
2019-02-07 14:22:46 +03:00
struct hints_case {
const unsigned int * key_ids ;
size_t key_ids_count ;
struct expect_stats expect_stats ;
struct expect_stats expect_stats_hints ;
} ;
static const unsigned int hints_case_key_ids [ ] = {
1 , 7 , 3 , 5 , 3 , 1 , 30 , 8 , 8 , 5 , 6 , 8 ,
} ;
static const struct hints_case hints_case = {
. key_ids = hints_case_key_ids ,
. key_ids_count = ARRAY_SIZE ( hints_case_key_ids ) ,
. expect_stats =
EXPECT_STATS ( 7 , ROOT ( 1 , 2 , 7 ) , ROOT ( 7 , 1 , 4 ) , ROOT ( 30 , 1 , 1 ) ,
DELTA ( 8 , 3 ) , DELTA ( 3 , 2 ) ,
DELTA ( 5 , 2 ) , DELTA ( 6 , 1 ) ) ,
. expect_stats_hints =
EXPECT_STATS ( 7 , ROOT ( 3 , 2 , 9 ) , ROOT ( 1 , 2 , 2 ) , ROOT ( 30 , 1 , 1 ) ,
DELTA ( 8 , 3 ) , DELTA ( 5 , 2 ) ,
DELTA ( 6 , 1 ) , DELTA ( 7 , 1 ) ) ,
} ;
static void __pr_debug_stats ( const struct objagg_stats * stats )
{
int i ;
for ( i = 0 ; i < stats - > stats_info_count ; i + + )
pr_debug ( " Stat index %d key %u: u %d, d %d, %s \n " , i ,
obj_to_key_id ( stats - > stats_info [ i ] . objagg_obj ) ,
stats - > stats_info [ i ] . stats . user_count ,
stats - > stats_info [ i ] . stats . delta_user_count ,
stats - > stats_info [ i ] . is_root ? " root " : " noroot " ) ;
}
static void pr_debug_stats ( struct objagg * objagg )
{
const struct objagg_stats * stats ;
stats = objagg_stats_get ( objagg ) ;
if ( IS_ERR ( stats ) )
return ;
__pr_debug_stats ( stats ) ;
objagg_stats_put ( stats ) ;
}
static void pr_debug_hints_stats ( struct objagg_hints * objagg_hints )
{
const struct objagg_stats * stats ;
stats = objagg_hints_stats_get ( objagg_hints ) ;
if ( IS_ERR ( stats ) )
return ;
__pr_debug_stats ( stats ) ;
objagg_stats_put ( stats ) ;
}
static int check_expect_hints_stats ( struct objagg_hints * objagg_hints ,
const struct expect_stats * expect_stats ,
const char * * errmsg )
{
const struct objagg_stats * stats ;
int err ;
stats = objagg_hints_stats_get ( objagg_hints ) ;
if ( IS_ERR ( stats ) )
return PTR_ERR ( stats ) ;
err = __check_expect_stats ( stats , expect_stats , errmsg ) ;
objagg_stats_put ( stats ) ;
return err ;
}
static int test_hints_case ( const struct hints_case * hints_case )
{
struct objagg_obj * objagg_obj ;
struct objagg_hints * hints ;
struct world world2 = { } ;
struct world world = { } ;
struct objagg * objagg2 ;
struct objagg * objagg ;
const char * errmsg ;
int i ;
int err ;
objagg = objagg_create ( & delta_ops , NULL , & world ) ;
if ( IS_ERR ( objagg ) )
return PTR_ERR ( objagg ) ;
for ( i = 0 ; i < hints_case - > key_ids_count ; i + + ) {
objagg_obj = world_obj_get ( & world , objagg ,
hints_case - > key_ids [ i ] ) ;
if ( IS_ERR ( objagg_obj ) ) {
err = PTR_ERR ( objagg_obj ) ;
goto err_world_obj_get ;
}
}
pr_debug_stats ( objagg ) ;
err = check_expect_stats ( objagg , & hints_case - > expect_stats , & errmsg ) ;
if ( err ) {
pr_err ( " Stats: %s \n " , errmsg ) ;
goto err_check_expect_stats ;
}
hints = objagg_hints_get ( objagg , OBJAGG_OPT_ALGO_SIMPLE_GREEDY ) ;
if ( IS_ERR ( hints ) ) {
err = PTR_ERR ( hints ) ;
goto err_hints_get ;
}
pr_debug_hints_stats ( hints ) ;
err = check_expect_hints_stats ( hints , & hints_case - > expect_stats_hints ,
& errmsg ) ;
if ( err ) {
pr_err ( " Hints stats: %s \n " , errmsg ) ;
goto err_check_expect_hints_stats ;
}
objagg2 = objagg_create ( & delta_ops , hints , & world2 ) ;
2019-02-13 11:58:20 +03:00
if ( IS_ERR ( objagg2 ) )
return PTR_ERR ( objagg2 ) ;
2019-02-07 14:22:46 +03:00
for ( i = 0 ; i < hints_case - > key_ids_count ; i + + ) {
objagg_obj = world_obj_get ( & world2 , objagg2 ,
hints_case - > key_ids [ i ] ) ;
if ( IS_ERR ( objagg_obj ) ) {
err = PTR_ERR ( objagg_obj ) ;
goto err_world2_obj_get ;
}
}
pr_debug_stats ( objagg2 ) ;
err = check_expect_stats ( objagg2 , & hints_case - > expect_stats_hints ,
& errmsg ) ;
if ( err ) {
pr_err ( " Stats2: %s \n " , errmsg ) ;
goto err_check_expect_stats2 ;
}
err = 0 ;
err_check_expect_stats2 :
err_world2_obj_get :
for ( i - - ; i > = 0 ; i - - )
world_obj_put ( & world2 , objagg , hints_case - > key_ids [ i ] ) ;
i = hints_case - > key_ids_count ;
2020-06-12 23:01:54 +03:00
objagg_destroy ( objagg2 ) ;
2019-02-07 14:22:46 +03:00
err_check_expect_hints_stats :
2020-06-12 23:01:54 +03:00
objagg_hints_put ( hints ) ;
2019-02-07 14:22:46 +03:00
err_hints_get :
err_check_expect_stats :
err_world_obj_get :
for ( i - - ; i > = 0 ; i - - )
world_obj_put ( & world , objagg , hints_case - > key_ids [ i ] ) ;
objagg_destroy ( objagg ) ;
return err ;
}
static int test_hints ( void )
{
return test_hints_case ( & hints_case ) ;
}
2018-11-14 11:22:28 +03:00
static int __init test_objagg_init ( void )
{
int err ;
err = test_nodelta ( ) ;
if ( err )
return err ;
2019-02-07 14:22:46 +03:00
err = test_delta ( ) ;
if ( err )
return err ;
return test_hints ( ) ;
2018-11-14 11:22:28 +03:00
}
static void __exit test_objagg_exit ( void )
{
}
module_init ( test_objagg_init ) ;
module_exit ( test_objagg_exit ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
MODULE_AUTHOR ( " Jiri Pirko <jiri@mellanox.com> " ) ;
MODULE_DESCRIPTION ( " Test module for objagg " ) ;