2016-12-22 08:36:09 +00:00
/*
* Test cases for the drm_mm range manager
*/
# define pr_fmt(fmt) "drm_mm: " fmt
# include <linux/module.h>
# include <linux/prime_numbers.h>
# include <linux/slab.h>
# include <linux/random.h>
# include <linux/vmalloc.h>
# include <drm/drm_mm.h>
# include "../lib/drm_random.h"
# define TESTS "drm_mm_selftests.h"
# include "drm_selftest.h"
static unsigned int random_seed ;
static unsigned int max_iterations = 8192 ;
static unsigned int max_prime = 128 ;
2016-12-22 08:36:13 +00:00
enum {
BEST ,
2017-02-02 21:04:38 +00:00
BOTTOMUP ,
TOPDOWN ,
EVICT ,
2016-12-22 08:36:13 +00:00
} ;
static const struct insert_mode {
const char * name ;
2017-02-02 21:04:38 +00:00
enum drm_mm_insert_mode mode ;
2016-12-22 08:36:13 +00:00
} insert_modes [ ] = {
2017-02-02 21:04:38 +00:00
[ BEST ] = { " best " , DRM_MM_INSERT_BEST } ,
[ BOTTOMUP ] = { " bottom-up " , DRM_MM_INSERT_LOW } ,
[ TOPDOWN ] = { " top-down " , DRM_MM_INSERT_HIGH } ,
[ EVICT ] = { " evict " , DRM_MM_INSERT_EVICT } ,
2016-12-22 08:36:13 +00:00
{ }
2016-12-22 08:36:17 +00:00
} , evict_modes [ ] = {
2017-02-02 21:04:38 +00:00
{ " bottom-up " , DRM_MM_INSERT_LOW } ,
{ " top-down " , DRM_MM_INSERT_HIGH } ,
2016-12-22 08:36:17 +00:00
{ }
2016-12-22 08:36:13 +00:00
} ;
2016-12-22 08:36:09 +00:00
static int igt_sanitycheck ( void * ignored )
{
pr_info ( " %s - ok! \n " , __func__ ) ;
return 0 ;
}
2016-12-22 08:36:10 +00:00
static bool assert_no_holes ( const struct drm_mm * mm )
{
struct drm_mm_node * hole ;
u64 hole_start , hole_end ;
unsigned long count ;
count = 0 ;
drm_mm_for_each_hole ( hole , mm , hole_start , hole_end )
count + + ;
if ( count ) {
pr_err ( " Expected to find no holes (after reserve), found %lu instead \n " , count ) ;
return false ;
}
drm_mm_for_each_node ( hole , mm ) {
2016-12-22 08:36:37 +00:00
if ( drm_mm_hole_follows ( hole ) ) {
2016-12-22 08:36:10 +00:00
pr_err ( " Hole follows node, expected none! \n " ) ;
return false ;
}
}
return true ;
}
static bool assert_one_hole ( const struct drm_mm * mm , u64 start , u64 end )
{
struct drm_mm_node * hole ;
u64 hole_start , hole_end ;
unsigned long count ;
bool ok = true ;
if ( end < = start )
return true ;
count = 0 ;
drm_mm_for_each_hole ( hole , mm , hole_start , hole_end ) {
if ( start ! = hole_start | | end ! = hole_end ) {
if ( ok )
pr_err ( " empty mm has incorrect hole, found (%llx, %llx), expect (%llx, %llx) \n " ,
hole_start , hole_end ,
start , end ) ;
ok = false ;
}
count + + ;
}
if ( count ! = 1 ) {
pr_err ( " Expected to find one hole, found %lu instead \n " , count ) ;
ok = false ;
}
return ok ;
}
2016-12-22 08:36:12 +00:00
static bool assert_continuous ( const struct drm_mm * mm , u64 size )
{
struct drm_mm_node * node , * check , * found ;
unsigned long n ;
u64 addr ;
if ( ! assert_no_holes ( mm ) )
return false ;
n = 0 ;
addr = 0 ;
drm_mm_for_each_node ( node , mm ) {
if ( node - > start ! = addr ) {
pr_err ( " node[%ld] list out of order, expected %llx found %llx \n " ,
n , addr , node - > start ) ;
return false ;
}
if ( node - > size ! = size ) {
pr_err ( " node[%ld].size incorrect, expected %llx, found %llx \n " ,
n , size , node - > size ) ;
return false ;
}
2016-12-22 08:36:37 +00:00
if ( drm_mm_hole_follows ( node ) ) {
2016-12-22 08:36:12 +00:00
pr_err ( " node[%ld] is followed by a hole! \n " , n ) ;
return false ;
}
found = NULL ;
drm_mm_for_each_node_in_range ( check , mm , addr , addr + size ) {
if ( node ! = check ) {
pr_err ( " lookup return wrong node, expected start %llx, found %llx \n " ,
node - > start , check - > start ) ;
return false ;
}
found = check ;
}
if ( ! found ) {
pr_err ( " lookup failed for node %llx + %llx \n " ,
addr , size ) ;
return false ;
}
addr + = size ;
n + + ;
}
return true ;
}
2016-12-22 08:36:13 +00:00
static u64 misalignment ( struct drm_mm_node * node , u64 alignment )
{
u64 rem ;
if ( ! alignment )
return 0 ;
div64_u64_rem ( node - > start , alignment , & rem ) ;
return rem ;
}
static bool assert_node ( struct drm_mm_node * node , struct drm_mm * mm ,
u64 size , u64 alignment , unsigned long color )
{
bool ok = true ;
if ( ! drm_mm_node_allocated ( node ) | | node - > mm ! = mm ) {
pr_err ( " node not allocated \n " ) ;
ok = false ;
}
if ( node - > size ! = size ) {
pr_err ( " node has wrong size, found %llu, expected %llu \n " ,
node - > size , size ) ;
ok = false ;
}
if ( misalignment ( node , alignment ) ) {
2017-02-23 00:07:17 +00:00
pr_err ( " node is misaligned, start %llx rem %llu, expected alignment %llu \n " ,
2016-12-22 08:36:13 +00:00
node - > start , misalignment ( node , alignment ) , alignment ) ;
ok = false ;
}
if ( node - > color ! = color ) {
pr_err ( " node has wrong color, found %lu, expected %lu \n " ,
node - > color , color ) ;
ok = false ;
}
return ok ;
}
2016-12-29 12:09:24 +01:00
# define show_mm(mm) do { \
struct drm_printer __p = drm_debug_printer ( __func__ ) ; \
drm_mm_print ( ( mm ) , & __p ) ; } while ( 0 )
2016-12-22 08:36:10 +00:00
static int igt_init ( void * ignored )
{
const unsigned int size = 4096 ;
struct drm_mm mm ;
struct drm_mm_node tmp ;
int ret = - EINVAL ;
/* Start with some simple checks on initialising the struct drm_mm */
memset ( & mm , 0 , sizeof ( mm ) ) ;
if ( drm_mm_initialized ( & mm ) ) {
pr_err ( " zeroed mm claims to be initialized \n " ) ;
return ret ;
}
memset ( & mm , 0xff , sizeof ( mm ) ) ;
drm_mm_init ( & mm , 0 , size ) ;
if ( ! drm_mm_initialized ( & mm ) ) {
pr_err ( " mm claims not to be initialized \n " ) ;
goto out ;
}
if ( ! drm_mm_clean ( & mm ) ) {
pr_err ( " mm not empty on creation \n " ) ;
goto out ;
}
/* After creation, it should all be one massive hole */
if ( ! assert_one_hole ( & mm , 0 , size ) ) {
ret = - EINVAL ;
goto out ;
}
memset ( & tmp , 0 , sizeof ( tmp ) ) ;
tmp . start = 0 ;
tmp . size = size ;
ret = drm_mm_reserve_node ( & mm , & tmp ) ;
if ( ret ) {
pr_err ( " failed to reserve whole drm_mm \n " ) ;
goto out ;
}
/* After filling the range entirely, there should be no holes */
if ( ! assert_no_holes ( & mm ) ) {
ret = - EINVAL ;
goto out ;
}
/* And then after emptying it again, the massive hole should be back */
drm_mm_remove_node ( & tmp ) ;
if ( ! assert_one_hole ( & mm , 0 , size ) ) {
ret = - EINVAL ;
goto out ;
}
out :
if ( ret )
2016-12-29 12:09:24 +01:00
show_mm ( & mm ) ;
2016-12-22 08:36:10 +00:00
drm_mm_takedown ( & mm ) ;
return ret ;
}
2016-12-22 08:36:11 +00:00
static int igt_debug ( void * ignored )
{
struct drm_mm mm ;
struct drm_mm_node nodes [ 2 ] ;
int ret ;
/* Create a small drm_mm with a couple of nodes and a few holes, and
* check that the debug iterator doesn ' t explode over a trivial drm_mm .
*/
drm_mm_init ( & mm , 0 , 4096 ) ;
memset ( nodes , 0 , sizeof ( nodes ) ) ;
nodes [ 0 ] . start = 512 ;
nodes [ 0 ] . size = 1024 ;
ret = drm_mm_reserve_node ( & mm , & nodes [ 0 ] ) ;
if ( ret ) {
pr_err ( " failed to reserve node[0] {start=%lld, size=%lld) \n " ,
nodes [ 0 ] . start , nodes [ 0 ] . size ) ;
return ret ;
}
nodes [ 1 ] . size = 1024 ;
nodes [ 1 ] . start = 4096 - 512 - nodes [ 1 ] . size ;
ret = drm_mm_reserve_node ( & mm , & nodes [ 1 ] ) ;
if ( ret ) {
pr_err ( " failed to reserve node[1] {start=%lld, size=%lld) \n " ,
nodes [ 1 ] . start , nodes [ 1 ] . size ) ;
return ret ;
}
2016-12-29 12:09:24 +01:00
show_mm ( & mm ) ;
2016-12-22 08:36:11 +00:00
return 0 ;
}
2016-12-22 08:36:12 +00:00
static struct drm_mm_node * set_node ( struct drm_mm_node * node ,
u64 start , u64 size )
{
node - > start = start ;
node - > size = size ;
return node ;
}
static bool expect_reserve_fail ( struct drm_mm * mm , struct drm_mm_node * node )
{
int err ;
err = drm_mm_reserve_node ( mm , node ) ;
if ( likely ( err = = - ENOSPC ) )
return true ;
if ( ! err ) {
pr_err ( " impossible reserve succeeded, node %llu + %llu \n " ,
node - > start , node - > size ) ;
drm_mm_remove_node ( node ) ;
} else {
pr_err ( " impossible reserve failed with wrong error %d [expected %d], node %llu + %llu \n " ,
err , - ENOSPC , node - > start , node - > size ) ;
}
return false ;
}
static bool check_reserve_boundaries ( struct drm_mm * mm ,
unsigned int count ,
u64 size )
{
const struct boundary {
u64 start , size ;
const char * name ;
} boundaries [ ] = {
# define B(st, sz) { (st), (sz), "{ " #st ", " #sz "}" }
B ( 0 , 0 ) ,
B ( - size , 0 ) ,
B ( size , 0 ) ,
B ( size * count , 0 ) ,
B ( - size , size ) ,
B ( - size , - size ) ,
B ( - size , 2 * size ) ,
B ( 0 , - size ) ,
B ( size , - size ) ,
B ( count * size , size ) ,
B ( count * size , - size ) ,
B ( count * size , count * size ) ,
B ( count * size , - count * size ) ,
B ( count * size , - ( count + 1 ) * size ) ,
B ( ( count + 1 ) * size , size ) ,
B ( ( count + 1 ) * size , - size ) ,
B ( ( count + 1 ) * size , - 2 * size ) ,
# undef B
} ;
struct drm_mm_node tmp = { } ;
int n ;
for ( n = 0 ; n < ARRAY_SIZE ( boundaries ) ; n + + ) {
if ( ! expect_reserve_fail ( mm ,
set_node ( & tmp ,
boundaries [ n ] . start ,
boundaries [ n ] . size ) ) ) {
pr_err ( " boundary[%d:%s] failed, count=%u, size=%lld \n " ,
n , boundaries [ n ] . name , count , size ) ;
return false ;
}
}
return true ;
}
static int __igt_reserve ( unsigned int count , u64 size )
{
DRM_RND_STATE ( prng , random_seed ) ;
struct drm_mm mm ;
struct drm_mm_node tmp , * nodes , * node , * next ;
unsigned int * order , n , m , o = 0 ;
int ret , err ;
/* For exercising drm_mm_reserve_node(), we want to check that
* reservations outside of the drm_mm range are rejected , and to
* overlapping and otherwise already occupied ranges . Afterwards ,
* the tree and nodes should be intact .
*/
DRM_MM_BUG_ON ( ! count ) ;
DRM_MM_BUG_ON ( ! size ) ;
ret = - ENOMEM ;
order = drm_random_order ( count , & prng ) ;
if ( ! order )
goto err ;
nodes = vzalloc ( sizeof ( * nodes ) * count ) ;
if ( ! nodes )
goto err_order ;
ret = - EINVAL ;
drm_mm_init ( & mm , 0 , count * size ) ;
if ( ! check_reserve_boundaries ( & mm , count , size ) )
goto out ;
for ( n = 0 ; n < count ; n + + ) {
nodes [ n ] . start = order [ n ] * size ;
nodes [ n ] . size = size ;
err = drm_mm_reserve_node ( & mm , & nodes [ n ] ) ;
if ( err ) {
pr_err ( " reserve failed, step %d, start %llu \n " ,
n , nodes [ n ] . start ) ;
ret = err ;
goto out ;
}
if ( ! drm_mm_node_allocated ( & nodes [ n ] ) ) {
pr_err ( " reserved node not allocated! step %d, start %llu \n " ,
n , nodes [ n ] . start ) ;
goto out ;
}
if ( ! expect_reserve_fail ( & mm , & nodes [ n ] ) )
goto out ;
}
/* After random insertion the nodes should be in order */
if ( ! assert_continuous ( & mm , size ) )
goto out ;
/* Repeated use should then fail */
drm_random_reorder ( order , count , & prng ) ;
for ( n = 0 ; n < count ; n + + ) {
if ( ! expect_reserve_fail ( & mm ,
set_node ( & tmp , order [ n ] * size , 1 ) ) )
goto out ;
/* Remove and reinsert should work */
drm_mm_remove_node ( & nodes [ order [ n ] ] ) ;
err = drm_mm_reserve_node ( & mm , & nodes [ order [ n ] ] ) ;
if ( err ) {
pr_err ( " reserve failed, step %d, start %llu \n " ,
n , nodes [ n ] . start ) ;
ret = err ;
goto out ;
}
}
if ( ! assert_continuous ( & mm , size ) )
goto out ;
/* Overlapping use should then fail */
for ( n = 0 ; n < count ; n + + ) {
if ( ! expect_reserve_fail ( & mm , set_node ( & tmp , 0 , size * count ) ) )
goto out ;
}
for ( n = 0 ; n < count ; n + + ) {
if ( ! expect_reserve_fail ( & mm ,
set_node ( & tmp ,
size * n ,
size * ( count - n ) ) ) )
goto out ;
}
/* Remove several, reinsert, check full */
for_each_prime_number ( n , min ( max_prime , count ) ) {
for ( m = 0 ; m < n ; m + + ) {
node = & nodes [ order [ ( o + m ) % count ] ] ;
drm_mm_remove_node ( node ) ;
}
for ( m = 0 ; m < n ; m + + ) {
node = & nodes [ order [ ( o + m ) % count ] ] ;
err = drm_mm_reserve_node ( & mm , node ) ;
if ( err ) {
pr_err ( " reserve failed, step %d/%d, start %llu \n " ,
m , n , node - > start ) ;
ret = err ;
goto out ;
}
}
o + = n ;
if ( ! assert_continuous ( & mm , size ) )
goto out ;
}
ret = 0 ;
out :
drm_mm_for_each_node_safe ( node , next , & mm )
drm_mm_remove_node ( node ) ;
drm_mm_takedown ( & mm ) ;
vfree ( nodes ) ;
err_order :
kfree ( order ) ;
err :
return ret ;
}
static int igt_reserve ( void * ignored )
{
const unsigned int count = min_t ( unsigned int , BIT ( 10 ) , max_iterations ) ;
int n , ret ;
for_each_prime_number_from ( n , 1 , 54 ) {
u64 size = BIT_ULL ( n ) ;
ret = __igt_reserve ( count , size - 1 ) ;
if ( ret )
return ret ;
ret = __igt_reserve ( count , size ) ;
if ( ret )
return ret ;
ret = __igt_reserve ( count , size + 1 ) ;
if ( ret )
return ret ;
2017-03-29 10:10:53 +01:00
cond_resched ( ) ;
2016-12-22 08:36:12 +00:00
}
return 0 ;
}
2016-12-22 08:36:13 +00:00
static bool expect_insert ( struct drm_mm * mm , struct drm_mm_node * node ,
u64 size , u64 alignment , unsigned long color ,
const struct insert_mode * mode )
{
int err ;
err = drm_mm_insert_node_generic ( mm , node ,
size , alignment , color ,
2017-02-02 21:04:38 +00:00
mode - > mode ) ;
2016-12-22 08:36:13 +00:00
if ( err ) {
pr_err ( " insert (size=%llu, alignment=%llu, color=%lu, mode=%s) failed with err=%d \n " ,
size , alignment , color , mode - > name , err ) ;
return false ;
}
if ( ! assert_node ( node , mm , size , alignment , color ) ) {
drm_mm_remove_node ( node ) ;
return false ;
}
return true ;
}
static bool expect_insert_fail ( struct drm_mm * mm , u64 size )
{
struct drm_mm_node tmp = { } ;
int err ;
2017-02-02 21:04:38 +00:00
err = drm_mm_insert_node ( mm , & tmp , size ) ;
2016-12-22 08:36:13 +00:00
if ( likely ( err = = - ENOSPC ) )
return true ;
if ( ! err ) {
pr_err ( " impossible insert succeeded, node %llu + %llu \n " ,
tmp . start , tmp . size ) ;
drm_mm_remove_node ( & tmp ) ;
} else {
pr_err ( " impossible insert failed with wrong error %d [expected %d], size %llu \n " ,
err , - ENOSPC , size ) ;
}
return false ;
}
2016-12-22 08:36:14 +00:00
static int __igt_insert ( unsigned int count , u64 size , bool replace )
2016-12-22 08:36:13 +00:00
{
DRM_RND_STATE ( prng , random_seed ) ;
const struct insert_mode * mode ;
struct drm_mm mm ;
struct drm_mm_node * nodes , * node , * next ;
unsigned int * order , n , m , o = 0 ;
int ret ;
/* Fill a range with lots of nodes, check it doesn't fail too early */
DRM_MM_BUG_ON ( ! count ) ;
DRM_MM_BUG_ON ( ! size ) ;
ret = - ENOMEM ;
2016-12-22 08:36:14 +00:00
nodes = vmalloc ( count * sizeof ( * nodes ) ) ;
2016-12-22 08:36:13 +00:00
if ( ! nodes )
goto err ;
order = drm_random_order ( count , & prng ) ;
if ( ! order )
goto err_nodes ;
ret = - EINVAL ;
drm_mm_init ( & mm , 0 , count * size ) ;
for ( mode = insert_modes ; mode - > name ; mode + + ) {
for ( n = 0 ; n < count ; n + + ) {
2016-12-22 08:36:14 +00:00
struct drm_mm_node tmp ;
node = replace ? & tmp : & nodes [ n ] ;
memset ( node , 0 , sizeof ( * node ) ) ;
if ( ! expect_insert ( & mm , node , size , 0 , n , mode ) ) {
2016-12-22 08:36:13 +00:00
pr_err ( " %s insert failed, size %llu step %d \n " ,
mode - > name , size , n ) ;
goto out ;
}
2016-12-22 08:36:14 +00:00
if ( replace ) {
drm_mm_replace_node ( & tmp , & nodes [ n ] ) ;
if ( drm_mm_node_allocated ( & tmp ) ) {
pr_err ( " replaced old-node still allocated! step %d \n " ,
n ) ;
goto out ;
}
if ( ! assert_node ( & nodes [ n ] , & mm , size , 0 , n ) ) {
pr_err ( " replaced node did not inherit parameters, size %llu step %d \n " ,
size , n ) ;
goto out ;
}
if ( tmp . start ! = nodes [ n ] . start ) {
pr_err ( " replaced node mismatch location expected [%llx + %llx], found [%llx + %llx] \n " ,
tmp . start , size ,
nodes [ n ] . start , nodes [ n ] . size ) ;
goto out ;
}
}
2016-12-22 08:36:13 +00:00
}
/* After random insertion the nodes should be in order */
if ( ! assert_continuous ( & mm , size ) )
goto out ;
/* Repeated use should then fail */
if ( ! expect_insert_fail ( & mm , size ) )
goto out ;
/* Remove one and reinsert, as the only hole it should refill itself */
for ( n = 0 ; n < count ; n + + ) {
u64 addr = nodes [ n ] . start ;
drm_mm_remove_node ( & nodes [ n ] ) ;
if ( ! expect_insert ( & mm , & nodes [ n ] , size , 0 , n , mode ) ) {
pr_err ( " %s reinsert failed, size %llu step %d \n " ,
mode - > name , size , n ) ;
goto out ;
}
if ( nodes [ n ] . start ! = addr ) {
pr_err ( " %s reinsert node moved, step %d, expected %llx, found %llx \n " ,
mode - > name , n , addr , nodes [ n ] . start ) ;
goto out ;
}
if ( ! assert_continuous ( & mm , size ) )
goto out ;
}
/* Remove several, reinsert, check full */
for_each_prime_number ( n , min ( max_prime , count ) ) {
for ( m = 0 ; m < n ; m + + ) {
node = & nodes [ order [ ( o + m ) % count ] ] ;
drm_mm_remove_node ( node ) ;
}
for ( m = 0 ; m < n ; m + + ) {
node = & nodes [ order [ ( o + m ) % count ] ] ;
if ( ! expect_insert ( & mm , node , size , 0 , n , mode ) ) {
pr_err ( " %s multiple reinsert failed, size %llu step %d \n " ,
mode - > name , size , n ) ;
goto out ;
}
}
o + = n ;
if ( ! assert_continuous ( & mm , size ) )
goto out ;
if ( ! expect_insert_fail ( & mm , size ) )
goto out ;
}
drm_mm_for_each_node_safe ( node , next , & mm )
drm_mm_remove_node ( node ) ;
DRM_MM_BUG_ON ( ! drm_mm_clean ( & mm ) ) ;
2017-11-07 10:41:31 +00:00
cond_resched ( ) ;
2016-12-22 08:36:13 +00:00
}
ret = 0 ;
out :
drm_mm_for_each_node_safe ( node , next , & mm )
drm_mm_remove_node ( node ) ;
drm_mm_takedown ( & mm ) ;
kfree ( order ) ;
err_nodes :
vfree ( nodes ) ;
err :
return ret ;
}
static int igt_insert ( void * ignored )
{
const unsigned int count = min_t ( unsigned int , BIT ( 10 ) , max_iterations ) ;
unsigned int n ;
int ret ;
for_each_prime_number_from ( n , 1 , 54 ) {
u64 size = BIT_ULL ( n ) ;
2016-12-22 08:36:14 +00:00
ret = __igt_insert ( count , size - 1 , false ) ;
2016-12-22 08:36:13 +00:00
if ( ret )
return ret ;
2016-12-22 08:36:14 +00:00
ret = __igt_insert ( count , size , false ) ;
2016-12-22 08:36:13 +00:00
if ( ret )
return ret ;
2016-12-22 08:36:14 +00:00
ret = __igt_insert ( count , size + 1 , false ) ;
2017-03-29 10:10:53 +01:00
if ( ret )
return ret ;
cond_resched ( ) ;
2016-12-22 08:36:14 +00:00
}
return 0 ;
}
static int igt_replace ( void * ignored )
{
const unsigned int count = min_t ( unsigned int , BIT ( 10 ) , max_iterations ) ;
unsigned int n ;
int ret ;
/* Reuse igt_insert to exercise replacement by inserting a dummy node,
* then replacing it with the intended node . We want to check that
* the tree is intact and all the information we need is carried
* across to the target node .
*/
for_each_prime_number_from ( n , 1 , 54 ) {
u64 size = BIT_ULL ( n ) ;
ret = __igt_insert ( count , size - 1 , true ) ;
2016-12-22 08:36:13 +00:00
if ( ret )
return ret ;
2016-12-22 08:36:14 +00:00
ret = __igt_insert ( count , size , true ) ;
if ( ret )
return ret ;
ret = __igt_insert ( count , size + 1 , true ) ;
2017-03-29 10:10:53 +01:00
if ( ret )
return ret ;
cond_resched ( ) ;
2016-12-22 08:36:13 +00:00
}
return 0 ;
}
2016-12-22 08:36:15 +00:00
static bool expect_insert_in_range ( struct drm_mm * mm , struct drm_mm_node * node ,
u64 size , u64 alignment , unsigned long color ,
u64 range_start , u64 range_end ,
const struct insert_mode * mode )
{
int err ;
2017-02-02 21:04:38 +00:00
err = drm_mm_insert_node_in_range ( mm , node ,
size , alignment , color ,
range_start , range_end ,
mode - > mode ) ;
2016-12-22 08:36:15 +00:00
if ( err ) {
pr_err ( " insert (size=%llu, alignment=%llu, color=%lu, mode=%s) nto range [%llx, %llx] failed with err=%d \n " ,
size , alignment , color , mode - > name ,
range_start , range_end , err ) ;
return false ;
}
if ( ! assert_node ( node , mm , size , alignment , color ) ) {
drm_mm_remove_node ( node ) ;
return false ;
}
return true ;
}
static bool expect_insert_in_range_fail ( struct drm_mm * mm ,
u64 size ,
u64 range_start ,
u64 range_end )
{
struct drm_mm_node tmp = { } ;
int err ;
2017-02-02 21:04:38 +00:00
err = drm_mm_insert_node_in_range ( mm , & tmp ,
size , 0 , 0 ,
range_start , range_end ,
0 ) ;
2016-12-22 08:36:15 +00:00
if ( likely ( err = = - ENOSPC ) )
return true ;
if ( ! err ) {
pr_err ( " impossible insert succeeded, node %llx + %llu, range [%llx, %llx] \n " ,
tmp . start , tmp . size , range_start , range_end ) ;
drm_mm_remove_node ( & tmp ) ;
} else {
pr_err ( " impossible insert failed with wrong error %d [expected %d], size %llu, range [%llx, %llx] \n " ,
err , - ENOSPC , size , range_start , range_end ) ;
}
return false ;
}
static bool assert_contiguous_in_range ( struct drm_mm * mm ,
u64 size ,
u64 start ,
u64 end )
{
struct drm_mm_node * node ;
unsigned int n ;
if ( ! expect_insert_in_range_fail ( mm , size , start , end ) )
return false ;
n = div64_u64 ( start + size - 1 , size ) ;
drm_mm_for_each_node ( node , mm ) {
if ( node - > start < start | | node - > start + node - > size > end ) {
pr_err ( " node %d out of range, address [%llx + %llu], range [%llx, %llx] \n " ,
n , node - > start , node - > start + node - > size , start , end ) ;
return false ;
}
if ( node - > start ! = n * size ) {
pr_err ( " node %d out of order, expected start %llx, found %llx \n " ,
n , n * size , node - > start ) ;
return false ;
}
if ( node - > size ! = size ) {
pr_err ( " node %d has wrong size, expected size %llx, found %llx \n " ,
n , size , node - > size ) ;
return false ;
}
2016-12-22 08:36:37 +00:00
if ( drm_mm_hole_follows ( node ) & &
drm_mm_hole_node_end ( node ) < end ) {
2016-12-22 08:36:15 +00:00
pr_err ( " node %d is followed by a hole! \n " , n ) ;
return false ;
}
n + + ;
}
2017-02-04 11:19:13 +00:00
if ( start > 0 ) {
node = __drm_mm_interval_first ( mm , 0 , start - 1 ) ;
if ( node - > allocated ) {
2016-12-22 08:36:15 +00:00
pr_err ( " node before start: node=%llx+%llu, start=%llx \n " ,
node - > start , node - > size , start ) ;
return false ;
}
}
2017-02-04 11:19:13 +00:00
if ( end < U64_MAX ) {
node = __drm_mm_interval_first ( mm , end , U64_MAX ) ;
if ( node - > allocated ) {
2016-12-22 08:36:15 +00:00
pr_err ( " node after end: node=%llx+%llu, end=%llx \n " ,
node - > start , node - > size , end ) ;
return false ;
}
}
return true ;
}
static int __igt_insert_range ( unsigned int count , u64 size , u64 start , u64 end )
{
const struct insert_mode * mode ;
struct drm_mm mm ;
struct drm_mm_node * nodes , * node , * next ;
unsigned int n , start_n , end_n ;
int ret ;
DRM_MM_BUG_ON ( ! count ) ;
DRM_MM_BUG_ON ( ! size ) ;
DRM_MM_BUG_ON ( end < = start ) ;
/* Very similar to __igt_insert(), but now instead of populating the
* full range of the drm_mm , we try to fill a small portion of it .
*/
ret = - ENOMEM ;
nodes = vzalloc ( count * sizeof ( * nodes ) ) ;
if ( ! nodes )
goto err ;
ret = - EINVAL ;
drm_mm_init ( & mm , 0 , count * size ) ;
start_n = div64_u64 ( start + size - 1 , size ) ;
end_n = div64_u64 ( end - size , size ) ;
for ( mode = insert_modes ; mode - > name ; mode + + ) {
for ( n = start_n ; n < = end_n ; n + + ) {
if ( ! expect_insert_in_range ( & mm , & nodes [ n ] ,
size , size , n ,
start , end , mode ) ) {
pr_err ( " %s insert failed, size %llu, step %d [%d, %d], range [%llx, %llx] \n " ,
mode - > name , size , n ,
start_n , end_n ,
start , end ) ;
goto out ;
}
}
if ( ! assert_contiguous_in_range ( & mm , size , start , end ) ) {
pr_err ( " %s: range [%llx, %llx] not full after initialisation, size=%llu \n " ,
mode - > name , start , end , size ) ;
goto out ;
}
/* Remove one and reinsert, it should refill itself */
for ( n = start_n ; n < = end_n ; n + + ) {
u64 addr = nodes [ n ] . start ;
drm_mm_remove_node ( & nodes [ n ] ) ;
if ( ! expect_insert_in_range ( & mm , & nodes [ n ] ,
size , size , n ,
start , end , mode ) ) {
pr_err ( " %s reinsert failed, step %d \n " , mode - > name , n ) ;
goto out ;
}
if ( nodes [ n ] . start ! = addr ) {
pr_err ( " %s reinsert node moved, step %d, expected %llx, found %llx \n " ,
mode - > name , n , addr , nodes [ n ] . start ) ;
goto out ;
}
}
if ( ! assert_contiguous_in_range ( & mm , size , start , end ) ) {
pr_err ( " %s: range [%llx, %llx] not full after reinsertion, size=%llu \n " ,
mode - > name , start , end , size ) ;
goto out ;
}
drm_mm_for_each_node_safe ( node , next , & mm )
drm_mm_remove_node ( node ) ;
DRM_MM_BUG_ON ( ! drm_mm_clean ( & mm ) ) ;
2017-11-07 10:41:31 +00:00
cond_resched ( ) ;
2016-12-22 08:36:15 +00:00
}
ret = 0 ;
out :
drm_mm_for_each_node_safe ( node , next , & mm )
drm_mm_remove_node ( node ) ;
drm_mm_takedown ( & mm ) ;
vfree ( nodes ) ;
err :
return ret ;
}
static int insert_outside_range ( void )
{
struct drm_mm mm ;
const unsigned int start = 1024 ;
const unsigned int end = 2048 ;
const unsigned int size = end - start ;
drm_mm_init ( & mm , start , size ) ;
if ( ! expect_insert_in_range_fail ( & mm , 1 , 0 , start ) )
return - EINVAL ;
if ( ! expect_insert_in_range_fail ( & mm , size ,
start - size / 2 , start + ( size + 1 ) / 2 ) )
return - EINVAL ;
if ( ! expect_insert_in_range_fail ( & mm , size ,
end - ( size + 1 ) / 2 , end + size / 2 ) )
return - EINVAL ;
if ( ! expect_insert_in_range_fail ( & mm , 1 , end , end + size ) )
return - EINVAL ;
drm_mm_takedown ( & mm ) ;
return 0 ;
}
static int igt_insert_range ( void * ignored )
{
const unsigned int count = min_t ( unsigned int , BIT ( 13 ) , max_iterations ) ;
unsigned int n ;
int ret ;
/* Check that requests outside the bounds of drm_mm are rejected. */
ret = insert_outside_range ( ) ;
if ( ret )
return ret ;
for_each_prime_number_from ( n , 1 , 50 ) {
const u64 size = BIT_ULL ( n ) ;
const u64 max = count * size ;
ret = __igt_insert_range ( count , size , 0 , max ) ;
if ( ret )
return ret ;
ret = __igt_insert_range ( count , size , 1 , max ) ;
if ( ret )
return ret ;
ret = __igt_insert_range ( count , size , 0 , max - 1 ) ;
if ( ret )
return ret ;
ret = __igt_insert_range ( count , size , 0 , max / 2 ) ;
if ( ret )
return ret ;
ret = __igt_insert_range ( count , size , max / 2 , max ) ;
if ( ret )
return ret ;
ret = __igt_insert_range ( count , size , max / 4 + 1 , 3 * max / 4 - 1 ) ;
if ( ret )
return ret ;
2017-03-29 10:10:53 +01:00
cond_resched ( ) ;
2016-12-22 08:36:15 +00:00
}
return 0 ;
}
2016-12-22 08:36:16 +00:00
static int igt_align ( void * ignored )
{
const struct insert_mode * mode ;
const unsigned int max_count = min ( 8192u , max_prime ) ;
struct drm_mm mm ;
struct drm_mm_node * nodes , * node , * next ;
unsigned int prime ;
int ret = - EINVAL ;
/* For each of the possible insertion modes, we pick a few
* arbitrary alignments and check that the inserted node
* meets our requirements .
*/
nodes = vzalloc ( max_count * sizeof ( * nodes ) ) ;
if ( ! nodes )
goto err ;
drm_mm_init ( & mm , 1 , U64_MAX - 2 ) ;
for ( mode = insert_modes ; mode - > name ; mode + + ) {
unsigned int i = 0 ;
for_each_prime_number_from ( prime , 1 , max_count ) {
u64 size = next_prime_number ( prime ) ;
if ( ! expect_insert ( & mm , & nodes [ i ] ,
size , prime , i ,
mode ) ) {
pr_err ( " %s insert failed with alignment=%d " ,
mode - > name , prime ) ;
goto out ;
}
i + + ;
}
drm_mm_for_each_node_safe ( node , next , & mm )
drm_mm_remove_node ( node ) ;
DRM_MM_BUG_ON ( ! drm_mm_clean ( & mm ) ) ;
2017-11-07 10:41:31 +00:00
2017-03-29 10:10:53 +01:00
cond_resched ( ) ;
2016-12-22 08:36:16 +00:00
}
ret = 0 ;
out :
drm_mm_for_each_node_safe ( node , next , & mm )
drm_mm_remove_node ( node ) ;
drm_mm_takedown ( & mm ) ;
vfree ( nodes ) ;
err :
return ret ;
}
static int igt_align_pot ( int max )
{
struct drm_mm mm ;
struct drm_mm_node * node , * next ;
int bit ;
int ret = - EINVAL ;
/* Check that we can align to the full u64 address space */
drm_mm_init ( & mm , 1 , U64_MAX - 2 ) ;
for ( bit = max - 1 ; bit ; bit - - ) {
u64 align , size ;
node = kzalloc ( sizeof ( * node ) , GFP_KERNEL ) ;
if ( ! node ) {
ret = - ENOMEM ;
goto out ;
}
align = BIT_ULL ( bit ) ;
size = BIT_ULL ( bit - 1 ) + 1 ;
if ( ! expect_insert ( & mm , node ,
size , align , bit ,
& insert_modes [ 0 ] ) ) {
pr_err ( " insert failed with alignment=%llx [%d] " ,
align , bit ) ;
goto out ;
}
2017-03-29 10:10:53 +01:00
cond_resched ( ) ;
2016-12-22 08:36:16 +00:00
}
ret = 0 ;
out :
drm_mm_for_each_node_safe ( node , next , & mm ) {
drm_mm_remove_node ( node ) ;
kfree ( node ) ;
}
drm_mm_takedown ( & mm ) ;
return ret ;
}
static int igt_align32 ( void * ignored )
{
return igt_align_pot ( 32 ) ;
}
static int igt_align64 ( void * ignored )
{
return igt_align_pot ( 64 ) ;
}
2016-12-22 08:36:29 +00:00
static void show_scan ( const struct drm_mm_scan * scan )
2016-12-22 08:36:17 +00:00
{
2016-12-22 08:36:24 +00:00
pr_info ( " scan: hit [%llx, %llx], size=%lld, align=%lld, color=%ld \n " ,
2016-12-22 08:36:29 +00:00
scan - > hit_start , scan - > hit_end ,
scan - > size , scan - > alignment , scan - > color ) ;
2016-12-22 08:36:17 +00:00
}
static void show_holes ( const struct drm_mm * mm , int count )
{
u64 hole_start , hole_end ;
struct drm_mm_node * hole ;
drm_mm_for_each_hole ( hole , mm , hole_start , hole_end ) {
struct drm_mm_node * next = list_next_entry ( hole , node_list ) ;
const char * node1 = NULL , * node2 = NULL ;
if ( hole - > allocated )
node1 = kasprintf ( GFP_KERNEL ,
" [%llx + %lld, color=%ld], " ,
hole - > start , hole - > size , hole - > color ) ;
if ( next - > allocated )
node2 = kasprintf ( GFP_KERNEL ,
" , [%llx + %lld, color=%ld] " ,
next - > start , next - > size , next - > color ) ;
pr_info ( " %sHole [%llx - %llx, size %lld]%s \n " ,
node1 ,
hole_start , hole_end , hole_end - hole_start ,
node2 ) ;
kfree ( node2 ) ;
kfree ( node1 ) ;
if ( ! - - count )
break ;
}
}
struct evict_node {
struct drm_mm_node node ;
struct list_head link ;
} ;
2016-12-22 08:36:29 +00:00
static bool evict_nodes ( struct drm_mm_scan * scan ,
2016-12-22 08:36:17 +00:00
struct evict_node * nodes ,
unsigned int * order ,
unsigned int count ,
2016-12-22 08:36:36 +00:00
bool use_color ,
2016-12-22 08:36:17 +00:00
struct list_head * evict_list )
{
struct evict_node * e , * en ;
unsigned int i ;
for ( i = 0 ; i < count ; i + + ) {
e = & nodes [ order ? order [ i ] : i ] ;
list_add ( & e - > link , evict_list ) ;
2016-12-22 08:36:29 +00:00
if ( drm_mm_scan_add_block ( scan , & e - > node ) )
2016-12-22 08:36:17 +00:00
break ;
}
list_for_each_entry_safe ( e , en , evict_list , link ) {
2016-12-22 08:36:29 +00:00
if ( ! drm_mm_scan_remove_block ( scan , & e - > node ) )
2016-12-22 08:36:17 +00:00
list_del ( & e - > link ) ;
}
if ( list_empty ( evict_list ) ) {
2016-12-22 08:36:24 +00:00
pr_err ( " Failed to find eviction: size=%lld [avail=%d], align=%lld (color=%lu) \n " ,
2016-12-22 08:36:29 +00:00
scan - > size , count , scan - > alignment , scan - > color ) ;
2016-12-22 08:36:17 +00:00
return false ;
}
list_for_each_entry ( e , evict_list , link )
drm_mm_remove_node ( & e - > node ) ;
2016-12-22 08:36:36 +00:00
if ( use_color ) {
struct drm_mm_node * node ;
while ( ( node = drm_mm_scan_color_evict ( scan ) ) ) {
e = container_of ( node , typeof ( * e ) , node ) ;
drm_mm_remove_node ( & e - > node ) ;
list_add ( & e - > link , evict_list ) ;
}
} else {
if ( drm_mm_scan_color_evict ( scan ) ) {
pr_err ( " drm_mm_scan_color_evict unexpectedly reported overlapping nodes! \n " ) ;
return false ;
}
}
2016-12-22 08:36:17 +00:00
return true ;
}
static bool evict_nothing ( struct drm_mm * mm ,
unsigned int total_size ,
struct evict_node * nodes )
{
2016-12-22 08:36:29 +00:00
struct drm_mm_scan scan ;
2016-12-22 08:36:17 +00:00
LIST_HEAD ( evict_list ) ;
struct evict_node * e ;
struct drm_mm_node * node ;
unsigned int n ;
2016-12-22 08:36:33 +00:00
drm_mm_scan_init ( & scan , mm , 1 , 0 , 0 , 0 ) ;
2016-12-22 08:36:17 +00:00
for ( n = 0 ; n < total_size ; n + + ) {
e = & nodes [ n ] ;
list_add ( & e - > link , & evict_list ) ;
2016-12-22 08:36:29 +00:00
drm_mm_scan_add_block ( & scan , & e - > node ) ;
2016-12-22 08:36:17 +00:00
}
list_for_each_entry ( e , & evict_list , link )
2016-12-22 08:36:29 +00:00
drm_mm_scan_remove_block ( & scan , & e - > node ) ;
2016-12-22 08:36:17 +00:00
for ( n = 0 ; n < total_size ; n + + ) {
e = & nodes [ n ] ;
if ( ! drm_mm_node_allocated ( & e - > node ) ) {
pr_err ( " node[%d] no longer allocated! \n " , n ) ;
return false ;
}
e - > link . next = NULL ;
}
drm_mm_for_each_node ( node , mm ) {
e = container_of ( node , typeof ( * e ) , node ) ;
e - > link . next = & e - > link ;
}
for ( n = 0 ; n < total_size ; n + + ) {
e = & nodes [ n ] ;
if ( ! e - > link . next ) {
pr_err ( " node[%d] no longer connected! \n " , n ) ;
return false ;
}
}
return assert_continuous ( mm , nodes [ 0 ] . node . size ) ;
}
static bool evict_everything ( struct drm_mm * mm ,
unsigned int total_size ,
struct evict_node * nodes )
{
2016-12-22 08:36:29 +00:00
struct drm_mm_scan scan ;
2016-12-22 08:36:17 +00:00
LIST_HEAD ( evict_list ) ;
struct evict_node * e ;
unsigned int n ;
int err ;
2016-12-22 08:36:33 +00:00
drm_mm_scan_init ( & scan , mm , total_size , 0 , 0 , 0 ) ;
2016-12-22 08:36:17 +00:00
for ( n = 0 ; n < total_size ; n + + ) {
e = & nodes [ n ] ;
list_add ( & e - > link , & evict_list ) ;
2016-12-22 08:36:29 +00:00
if ( drm_mm_scan_add_block ( & scan , & e - > node ) )
break ;
2016-12-22 08:36:17 +00:00
}
2017-01-10 14:40:31 +00:00
err = 0 ;
2016-12-22 08:36:17 +00:00
list_for_each_entry ( e , & evict_list , link ) {
2016-12-22 08:36:29 +00:00
if ( ! drm_mm_scan_remove_block ( & scan , & e - > node ) ) {
2017-01-10 14:40:31 +00:00
if ( ! err ) {
pr_err ( " Node %lld not marked for eviction! \n " ,
e - > node . start ) ;
err = - EINVAL ;
}
2016-12-22 08:36:17 +00:00
}
}
2017-01-10 14:40:31 +00:00
if ( err )
return false ;
2016-12-22 08:36:17 +00:00
list_for_each_entry ( e , & evict_list , link )
drm_mm_remove_node ( & e - > node ) ;
if ( ! assert_one_hole ( mm , 0 , total_size ) )
return false ;
list_for_each_entry ( e , & evict_list , link ) {
err = drm_mm_reserve_node ( mm , & e - > node ) ;
if ( err ) {
pr_err ( " Failed to reinsert node after eviction: start=%llx \n " ,
e - > node . start ) ;
return false ;
}
}
return assert_continuous ( mm , nodes [ 0 ] . node . size ) ;
}
static int evict_something ( struct drm_mm * mm ,
2016-12-22 08:36:18 +00:00
u64 range_start , u64 range_end ,
2016-12-22 08:36:17 +00:00
struct evict_node * nodes ,
unsigned int * order ,
unsigned int count ,
unsigned int size ,
unsigned int alignment ,
const struct insert_mode * mode )
{
2016-12-22 08:36:29 +00:00
struct drm_mm_scan scan ;
2016-12-22 08:36:17 +00:00
LIST_HEAD ( evict_list ) ;
struct evict_node * e ;
struct drm_mm_node tmp ;
int err ;
2016-12-22 08:36:29 +00:00
drm_mm_scan_init_with_range ( & scan , mm ,
2016-12-22 08:36:18 +00:00
size , alignment , 0 ,
2016-12-22 08:36:33 +00:00
range_start , range_end ,
2017-02-02 21:04:38 +00:00
mode - > mode ) ;
2016-12-22 08:36:29 +00:00
if ( ! evict_nodes ( & scan ,
2016-12-22 08:36:36 +00:00
nodes , order , count , false ,
2016-12-22 08:36:17 +00:00
& evict_list ) )
return - EINVAL ;
memset ( & tmp , 0 , sizeof ( tmp ) ) ;
err = drm_mm_insert_node_generic ( mm , & tmp , size , alignment , 0 ,
2017-02-02 21:04:38 +00:00
DRM_MM_INSERT_EVICT ) ;
2016-12-22 08:36:17 +00:00
if ( err ) {
pr_err ( " Failed to insert into eviction hole: size=%d, align=%d \n " ,
size , alignment ) ;
2016-12-22 08:36:29 +00:00
show_scan ( & scan ) ;
2016-12-22 08:36:17 +00:00
show_holes ( mm , 3 ) ;
return err ;
}
2016-12-22 08:36:18 +00:00
if ( tmp . start < range_start | | tmp . start + tmp . size > range_end ) {
pr_err ( " Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu] \n " ,
tmp . start , tmp . size , range_start , range_end ) ;
err = - EINVAL ;
}
2016-12-22 08:36:37 +00:00
if ( ! assert_node ( & tmp , mm , size , alignment , 0 ) | |
drm_mm_hole_follows ( & tmp ) ) {
2016-12-22 08:36:17 +00:00
pr_err ( " Inserted did not fill the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx, hole-follows?=%d \n " ,
tmp . size , size ,
alignment , misalignment ( & tmp , alignment ) ,
2016-12-22 08:36:37 +00:00
tmp . start , drm_mm_hole_follows ( & tmp ) ) ;
2016-12-22 08:36:17 +00:00
err = - EINVAL ;
}
drm_mm_remove_node ( & tmp ) ;
if ( err )
return err ;
list_for_each_entry ( e , & evict_list , link ) {
err = drm_mm_reserve_node ( mm , & e - > node ) ;
if ( err ) {
pr_err ( " Failed to reinsert node after eviction: start=%llx \n " ,
e - > node . start ) ;
return err ;
}
}
if ( ! assert_continuous ( mm , nodes [ 0 ] . node . size ) ) {
pr_err ( " range is no longer continuous \n " ) ;
return - EINVAL ;
}
return 0 ;
}
static int igt_evict ( void * ignored )
{
DRM_RND_STATE ( prng , random_seed ) ;
const unsigned int size = 8192 ;
const struct insert_mode * mode ;
struct drm_mm mm ;
struct evict_node * nodes ;
struct drm_mm_node * node , * next ;
unsigned int * order , n ;
int ret , err ;
/* Here we populate a full drm_mm and then try and insert a new node
* by evicting other nodes in a random order . The drm_mm_scan should
* pick the first matching hole it finds from the random list . We
* repeat that for different allocation strategies , alignments and
* sizes to try and stress the hole finder .
*/
ret = - ENOMEM ;
nodes = vzalloc ( size * sizeof ( * nodes ) ) ;
if ( ! nodes )
goto err ;
order = drm_random_order ( size , & prng ) ;
if ( ! order )
goto err_nodes ;
ret = - EINVAL ;
drm_mm_init ( & mm , 0 , size ) ;
for ( n = 0 ; n < size ; n + + ) {
2017-02-02 21:04:38 +00:00
err = drm_mm_insert_node ( & mm , & nodes [ n ] . node , 1 ) ;
2016-12-22 08:36:17 +00:00
if ( err ) {
pr_err ( " insert failed, step %d \n " , n ) ;
ret = err ;
goto out ;
}
}
/* First check that using the scanner doesn't break the mm */
if ( ! evict_nothing ( & mm , size , nodes ) ) {
pr_err ( " evict_nothing() failed \n " ) ;
goto out ;
}
if ( ! evict_everything ( & mm , size , nodes ) ) {
pr_err ( " evict_everything() failed \n " ) ;
goto out ;
}
for ( mode = evict_modes ; mode - > name ; mode + + ) {
for ( n = 1 ; n < = size ; n < < = 1 ) {
drm_random_reorder ( order , size , & prng ) ;
2016-12-22 08:36:18 +00:00
err = evict_something ( & mm , 0 , U64_MAX ,
2016-12-22 08:36:17 +00:00
nodes , order , size ,
n , 1 ,
mode ) ;
if ( err ) {
pr_err ( " %s evict_something(size=%u) failed \n " ,
mode - > name , n ) ;
ret = err ;
goto out ;
}
}
for ( n = 1 ; n < size ; n < < = 1 ) {
drm_random_reorder ( order , size , & prng ) ;
2016-12-22 08:36:18 +00:00
err = evict_something ( & mm , 0 , U64_MAX ,
2016-12-22 08:36:17 +00:00
nodes , order , size ,
size / 2 , n ,
mode ) ;
if ( err ) {
pr_err ( " %s evict_something(size=%u, alignment=%u) failed \n " ,
mode - > name , size / 2 , n ) ;
ret = err ;
goto out ;
}
}
for_each_prime_number_from ( n , 1 , min ( size , max_prime ) ) {
unsigned int nsize = ( size - n + 1 ) / 2 ;
DRM_MM_BUG_ON ( ! nsize ) ;
drm_random_reorder ( order , size , & prng ) ;
2016-12-22 08:36:18 +00:00
err = evict_something ( & mm , 0 , U64_MAX ,
2016-12-22 08:36:17 +00:00
nodes , order , size ,
nsize , n ,
mode ) ;
if ( err ) {
pr_err ( " %s evict_something(size=%u, alignment=%u) failed \n " ,
mode - > name , nsize , n ) ;
ret = err ;
goto out ;
}
}
2017-03-29 10:10:53 +01:00
cond_resched ( ) ;
2016-12-22 08:36:17 +00:00
}
ret = 0 ;
out :
drm_mm_for_each_node_safe ( node , next , & mm )
drm_mm_remove_node ( node ) ;
drm_mm_takedown ( & mm ) ;
kfree ( order ) ;
err_nodes :
vfree ( nodes ) ;
err :
return ret ;
}
2016-12-22 08:36:18 +00:00
static int igt_evict_range ( void * ignored )
{
DRM_RND_STATE ( prng , random_seed ) ;
const unsigned int size = 8192 ;
const unsigned int range_size = size / 2 ;
const unsigned int range_start = size / 4 ;
const unsigned int range_end = range_start + range_size ;
const struct insert_mode * mode ;
struct drm_mm mm ;
struct evict_node * nodes ;
struct drm_mm_node * node , * next ;
unsigned int * order , n ;
int ret , err ;
/* Like igt_evict() but now we are limiting the search to a
* small portion of the full drm_mm .
*/
ret = - ENOMEM ;
nodes = vzalloc ( size * sizeof ( * nodes ) ) ;
if ( ! nodes )
goto err ;
order = drm_random_order ( size , & prng ) ;
if ( ! order )
goto err_nodes ;
ret = - EINVAL ;
drm_mm_init ( & mm , 0 , size ) ;
for ( n = 0 ; n < size ; n + + ) {
2017-02-02 21:04:38 +00:00
err = drm_mm_insert_node ( & mm , & nodes [ n ] . node , 1 ) ;
2016-12-22 08:36:18 +00:00
if ( err ) {
pr_err ( " insert failed, step %d \n " , n ) ;
ret = err ;
goto out ;
}
}
for ( mode = evict_modes ; mode - > name ; mode + + ) {
for ( n = 1 ; n < = range_size ; n < < = 1 ) {
drm_random_reorder ( order , size , & prng ) ;
err = evict_something ( & mm , range_start , range_end ,
nodes , order , size ,
n , 1 ,
mode ) ;
if ( err ) {
pr_err ( " %s evict_something(size=%u) failed with range [%u, %u] \n " ,
mode - > name , n , range_start , range_end ) ;
goto out ;
}
}
for ( n = 1 ; n < = range_size ; n < < = 1 ) {
drm_random_reorder ( order , size , & prng ) ;
err = evict_something ( & mm , range_start , range_end ,
nodes , order , size ,
range_size / 2 , n ,
mode ) ;
if ( err ) {
pr_err ( " %s evict_something(size=%u, alignment=%u) failed with range [%u, %u] \n " ,
mode - > name , range_size / 2 , n , range_start , range_end ) ;
goto out ;
}
}
for_each_prime_number_from ( n , 1 , min ( range_size , max_prime ) ) {
unsigned int nsize = ( range_size - n + 1 ) / 2 ;
DRM_MM_BUG_ON ( ! nsize ) ;
drm_random_reorder ( order , size , & prng ) ;
err = evict_something ( & mm , range_start , range_end ,
nodes , order , size ,
nsize , n ,
mode ) ;
if ( err ) {
pr_err ( " %s evict_something(size=%u, alignment=%u) failed with range [%u, %u] \n " ,
mode - > name , nsize , n , range_start , range_end ) ;
goto out ;
}
}
2017-03-29 10:10:53 +01:00
cond_resched ( ) ;
2016-12-22 08:36:18 +00:00
}
ret = 0 ;
out :
drm_mm_for_each_node_safe ( node , next , & mm )
drm_mm_remove_node ( node ) ;
drm_mm_takedown ( & mm ) ;
kfree ( order ) ;
err_nodes :
vfree ( nodes ) ;
err :
return ret ;
}
2016-12-22 08:36:19 +00:00
static unsigned int node_index ( const struct drm_mm_node * node )
{
return div64_u64 ( node - > start , node - > size ) ;
}
static int igt_topdown ( void * ignored )
{
const struct insert_mode * topdown = & insert_modes [ TOPDOWN ] ;
DRM_RND_STATE ( prng , random_seed ) ;
const unsigned int count = 8192 ;
unsigned int size ;
unsigned long * bitmap = NULL ;
struct drm_mm mm ;
struct drm_mm_node * nodes , * node , * next ;
unsigned int * order , n , m , o = 0 ;
int ret ;
/* When allocating top-down, we expect to be returned a node
* from a suitable hole at the top of the drm_mm . We check that
* the returned node does match the highest available slot .
*/
ret = - ENOMEM ;
nodes = vzalloc ( count * sizeof ( * nodes ) ) ;
if ( ! nodes )
goto err ;
bitmap = kzalloc ( count / BITS_PER_LONG * sizeof ( unsigned long ) ,
2017-09-13 16:28:29 -07:00
GFP_KERNEL ) ;
2016-12-22 08:36:19 +00:00
if ( ! bitmap )
goto err_nodes ;
order = drm_random_order ( count , & prng ) ;
if ( ! order )
goto err_bitmap ;
ret = - EINVAL ;
for ( size = 1 ; size < = 64 ; size < < = 1 ) {
drm_mm_init ( & mm , 0 , size * count ) ;
for ( n = 0 ; n < count ; n + + ) {
if ( ! expect_insert ( & mm , & nodes [ n ] ,
size , 0 , n ,
topdown ) ) {
pr_err ( " insert failed, size %u step %d \n " , size , n ) ;
goto out ;
}
2016-12-22 08:36:37 +00:00
if ( drm_mm_hole_follows ( & nodes [ n ] ) ) {
2016-12-22 08:36:19 +00:00
pr_err ( " hole after topdown insert %d, start=%llx \n , size=%u " ,
n , nodes [ n ] . start , size ) ;
goto out ;
}
if ( ! assert_one_hole ( & mm , 0 , size * ( count - n - 1 ) ) )
goto out ;
}
if ( ! assert_continuous ( & mm , size ) )
goto out ;
drm_random_reorder ( order , count , & prng ) ;
for_each_prime_number_from ( n , 1 , min ( count , max_prime ) ) {
for ( m = 0 ; m < n ; m + + ) {
node = & nodes [ order [ ( o + m ) % count ] ] ;
drm_mm_remove_node ( node ) ;
__set_bit ( node_index ( node ) , bitmap ) ;
}
for ( m = 0 ; m < n ; m + + ) {
unsigned int last ;
node = & nodes [ order [ ( o + m ) % count ] ] ;
if ( ! expect_insert ( & mm , node ,
size , 0 , 0 ,
topdown ) ) {
pr_err ( " insert failed, step %d/%d \n " , m , n ) ;
goto out ;
}
2016-12-22 08:36:37 +00:00
if ( drm_mm_hole_follows ( node ) ) {
2016-12-22 08:36:19 +00:00
pr_err ( " hole after topdown insert %d/%d, start=%llx \n " ,
m , n , node - > start ) ;
goto out ;
}
last = find_last_bit ( bitmap , count ) ;
if ( node_index ( node ) ! = last ) {
pr_err ( " node %d/%d, size %d, not inserted into upmost hole, expected %d, found %d \n " ,
m , n , size , last , node_index ( node ) ) ;
goto out ;
}
__clear_bit ( last , bitmap ) ;
}
DRM_MM_BUG_ON ( find_first_bit ( bitmap , count ) ! = count ) ;
o + = n ;
}
drm_mm_for_each_node_safe ( node , next , & mm )
drm_mm_remove_node ( node ) ;
DRM_MM_BUG_ON ( ! drm_mm_clean ( & mm ) ) ;
2017-03-29 10:10:53 +01:00
cond_resched ( ) ;
2016-12-22 08:36:19 +00:00
}
ret = 0 ;
out :
drm_mm_for_each_node_safe ( node , next , & mm )
drm_mm_remove_node ( node ) ;
drm_mm_takedown ( & mm ) ;
kfree ( order ) ;
err_bitmap :
kfree ( bitmap ) ;
err_nodes :
vfree ( nodes ) ;
err :
return ret ;
}
2017-02-02 11:44:34 +00:00
static int igt_bottomup ( void * ignored )
{
const struct insert_mode * bottomup = & insert_modes [ BOTTOMUP ] ;
DRM_RND_STATE ( prng , random_seed ) ;
const unsigned int count = 8192 ;
unsigned int size ;
unsigned long * bitmap ;
struct drm_mm mm ;
struct drm_mm_node * nodes , * node , * next ;
unsigned int * order , n , m , o = 0 ;
int ret ;
/* Like igt_topdown, but instead of searching for the last hole,
* we search for the first .
*/
ret = - ENOMEM ;
nodes = vzalloc ( count * sizeof ( * nodes ) ) ;
if ( ! nodes )
goto err ;
bitmap = kzalloc ( count / BITS_PER_LONG * sizeof ( unsigned long ) ,
2017-09-13 16:28:29 -07:00
GFP_KERNEL ) ;
2017-02-02 11:44:34 +00:00
if ( ! bitmap )
goto err_nodes ;
order = drm_random_order ( count , & prng ) ;
if ( ! order )
goto err_bitmap ;
ret = - EINVAL ;
for ( size = 1 ; size < = 64 ; size < < = 1 ) {
drm_mm_init ( & mm , 0 , size * count ) ;
for ( n = 0 ; n < count ; n + + ) {
if ( ! expect_insert ( & mm , & nodes [ n ] ,
size , 0 , n ,
bottomup ) ) {
pr_err ( " bottomup insert failed, size %u step %d \n " , size , n ) ;
goto out ;
}
if ( ! assert_one_hole ( & mm , size * ( n + 1 ) , size * count ) )
goto out ;
}
if ( ! assert_continuous ( & mm , size ) )
goto out ;
drm_random_reorder ( order , count , & prng ) ;
for_each_prime_number_from ( n , 1 , min ( count , max_prime ) ) {
for ( m = 0 ; m < n ; m + + ) {
node = & nodes [ order [ ( o + m ) % count ] ] ;
drm_mm_remove_node ( node ) ;
__set_bit ( node_index ( node ) , bitmap ) ;
}
for ( m = 0 ; m < n ; m + + ) {
unsigned int first ;
node = & nodes [ order [ ( o + m ) % count ] ] ;
if ( ! expect_insert ( & mm , node ,
size , 0 , 0 ,
bottomup ) ) {
pr_err ( " insert failed, step %d/%d \n " , m , n ) ;
goto out ;
}
first = find_first_bit ( bitmap , count ) ;
if ( node_index ( node ) ! = first ) {
pr_err ( " node %d/%d not inserted into bottom hole, expected %d, found %d \n " ,
m , n , first , node_index ( node ) ) ;
goto out ;
}
__clear_bit ( first , bitmap ) ;
}
DRM_MM_BUG_ON ( find_first_bit ( bitmap , count ) ! = count ) ;
o + = n ;
}
drm_mm_for_each_node_safe ( node , next , & mm )
drm_mm_remove_node ( node ) ;
DRM_MM_BUG_ON ( ! drm_mm_clean ( & mm ) ) ;
2017-03-29 10:10:53 +01:00
cond_resched ( ) ;
2017-02-02 11:44:34 +00:00
}
ret = 0 ;
out :
drm_mm_for_each_node_safe ( node , next , & mm )
drm_mm_remove_node ( node ) ;
drm_mm_takedown ( & mm ) ;
kfree ( order ) ;
err_bitmap :
kfree ( bitmap ) ;
err_nodes :
vfree ( nodes ) ;
err :
return ret ;
}
2016-12-22 08:36:20 +00:00
static void separate_adjacent_colors ( const struct drm_mm_node * node ,
unsigned long color ,
u64 * start ,
u64 * end )
{
if ( node - > allocated & & node - > color ! = color )
+ + * start ;
node = list_next_entry ( node , node_list ) ;
if ( node - > allocated & & node - > color ! = color )
- - * end ;
}
static bool colors_abutt ( const struct drm_mm_node * node )
{
2016-12-22 08:36:37 +00:00
if ( ! drm_mm_hole_follows ( node ) & &
2016-12-22 08:36:20 +00:00
list_next_entry ( node , node_list ) - > allocated ) {
pr_err ( " colors abutt; %ld [%llx + %llx] is next to %ld [%llx + %llx]! \n " ,
node - > color , node - > start , node - > size ,
list_next_entry ( node , node_list ) - > color ,
list_next_entry ( node , node_list ) - > start ,
list_next_entry ( node , node_list ) - > size ) ;
return true ;
}
return false ;
}
static int igt_color ( void * ignored )
{
const unsigned int count = min ( 4096u , max_iterations ) ;
const struct insert_mode * mode ;
struct drm_mm mm ;
struct drm_mm_node * node , * nn ;
unsigned int n ;
int ret = - EINVAL , err ;
/* Color adjustment complicates everything. First we just check
* that when we insert a node we apply any color_adjustment callback .
* The callback we use should ensure that there is a gap between
* any two nodes , and so after each insertion we check that those
* holes are inserted and that they are preserved .
*/
drm_mm_init ( & mm , 0 , U64_MAX ) ;
for ( n = 1 ; n < = count ; n + + ) {
node = kzalloc ( sizeof ( * node ) , GFP_KERNEL ) ;
if ( ! node ) {
ret = - ENOMEM ;
goto out ;
}
if ( ! expect_insert ( & mm , node ,
n , 0 , n ,
& insert_modes [ 0 ] ) ) {
pr_err ( " insert failed, step %d \n " , n ) ;
kfree ( node ) ;
goto out ;
}
}
drm_mm_for_each_node_safe ( node , nn , & mm ) {
if ( node - > color ! = node - > size ) {
pr_err ( " invalid color stored: expected %lld, found %ld \n " ,
node - > size , node - > color ) ;
goto out ;
}
drm_mm_remove_node ( node ) ;
kfree ( node ) ;
}
/* Now, let's start experimenting with applying a color callback */
mm . color_adjust = separate_adjacent_colors ;
for ( mode = insert_modes ; mode - > name ; mode + + ) {
u64 last ;
node = kzalloc ( sizeof ( * node ) , GFP_KERNEL ) ;
if ( ! node ) {
ret = - ENOMEM ;
goto out ;
}
node - > size = 1 + 2 * count ;
node - > color = node - > size ;
err = drm_mm_reserve_node ( & mm , node ) ;
if ( err ) {
pr_err ( " initial reserve failed! \n " ) ;
ret = err ;
goto out ;
}
last = node - > start + node - > size ;
for ( n = 1 ; n < = count ; n + + ) {
int rem ;
node = kzalloc ( sizeof ( * node ) , GFP_KERNEL ) ;
if ( ! node ) {
ret = - ENOMEM ;
goto out ;
}
node - > start = last ;
node - > size = n + count ;
node - > color = node - > size ;
err = drm_mm_reserve_node ( & mm , node ) ;
if ( err ! = - ENOSPC ) {
pr_err ( " reserve %d did not report color overlap! err=%d \n " ,
n , err ) ;
goto out ;
}
node - > start + = n + 1 ;
rem = misalignment ( node , n + count ) ;
node - > start + = n + count - rem ;
err = drm_mm_reserve_node ( & mm , node ) ;
if ( err ) {
pr_err ( " reserve %d failed, err=%d \n " , n , err ) ;
ret = err ;
goto out ;
}
last = node - > start + node - > size ;
}
for ( n = 1 ; n < = count ; n + + ) {
node = kzalloc ( sizeof ( * node ) , GFP_KERNEL ) ;
if ( ! node ) {
ret = - ENOMEM ;
goto out ;
}
if ( ! expect_insert ( & mm , node ,
n , n , n ,
mode ) ) {
pr_err ( " %s insert failed, step %d \n " ,
mode - > name , n ) ;
kfree ( node ) ;
goto out ;
}
}
drm_mm_for_each_node_safe ( node , nn , & mm ) {
u64 rem ;
if ( node - > color ! = node - > size ) {
pr_err ( " %s invalid color stored: expected %lld, found %ld \n " ,
mode - > name , node - > size , node - > color ) ;
goto out ;
}
if ( colors_abutt ( node ) )
goto out ;
div64_u64_rem ( node - > start , node - > size , & rem ) ;
if ( rem ) {
pr_err ( " %s colored node misaligned, start=%llx expected alignment=%lld [rem=%lld] \n " ,
mode - > name , node - > start , node - > size , rem ) ;
goto out ;
}
drm_mm_remove_node ( node ) ;
kfree ( node ) ;
}
2017-03-29 10:10:53 +01:00
cond_resched ( ) ;
2016-12-22 08:36:20 +00:00
}
ret = 0 ;
out :
drm_mm_for_each_node_safe ( node , nn , & mm ) {
drm_mm_remove_node ( node ) ;
kfree ( node ) ;
}
drm_mm_takedown ( & mm ) ;
return ret ;
}
2016-12-22 08:36:21 +00:00
static int evict_color ( struct drm_mm * mm ,
2016-12-22 08:36:22 +00:00
u64 range_start , u64 range_end ,
2016-12-22 08:36:21 +00:00
struct evict_node * nodes ,
unsigned int * order ,
unsigned int count ,
unsigned int size ,
unsigned int alignment ,
unsigned long color ,
const struct insert_mode * mode )
{
2016-12-22 08:36:29 +00:00
struct drm_mm_scan scan ;
2016-12-22 08:36:21 +00:00
LIST_HEAD ( evict_list ) ;
struct evict_node * e ;
struct drm_mm_node tmp ;
int err ;
2016-12-22 08:36:29 +00:00
drm_mm_scan_init_with_range ( & scan , mm ,
2016-12-22 08:36:22 +00:00
size , alignment , color ,
2016-12-22 08:36:33 +00:00
range_start , range_end ,
2017-02-02 21:04:38 +00:00
mode - > mode ) ;
2016-12-22 08:36:29 +00:00
if ( ! evict_nodes ( & scan ,
2016-12-22 08:36:36 +00:00
nodes , order , count , true ,
2016-12-22 08:36:21 +00:00
& evict_list ) )
return - EINVAL ;
memset ( & tmp , 0 , sizeof ( tmp ) ) ;
err = drm_mm_insert_node_generic ( mm , & tmp , size , alignment , color ,
2017-02-02 21:04:38 +00:00
DRM_MM_INSERT_EVICT ) ;
2016-12-22 08:36:21 +00:00
if ( err ) {
pr_err ( " Failed to insert into eviction hole: size=%d, align=%d, color=%lu, err=%d \n " ,
size , alignment , color , err ) ;
2016-12-22 08:36:29 +00:00
show_scan ( & scan ) ;
2016-12-22 08:36:21 +00:00
show_holes ( mm , 3 ) ;
return err ;
}
2016-12-22 08:36:22 +00:00
if ( tmp . start < range_start | | tmp . start + tmp . size > range_end ) {
pr_err ( " Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu] \n " ,
tmp . start , tmp . size , range_start , range_end ) ;
err = - EINVAL ;
}
2016-12-22 08:36:21 +00:00
if ( colors_abutt ( & tmp ) )
err = - EINVAL ;
if ( ! assert_node ( & tmp , mm , size , alignment , color ) ) {
pr_err ( " Inserted did not fit the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx \n " ,
tmp . size , size ,
alignment , misalignment ( & tmp , alignment ) , tmp . start ) ;
err = - EINVAL ;
}
drm_mm_remove_node ( & tmp ) ;
if ( err )
return err ;
list_for_each_entry ( e , & evict_list , link ) {
err = drm_mm_reserve_node ( mm , & e - > node ) ;
if ( err ) {
pr_err ( " Failed to reinsert node after eviction: start=%llx \n " ,
e - > node . start ) ;
return err ;
}
}
2017-03-29 10:10:53 +01:00
cond_resched ( ) ;
2016-12-22 08:36:21 +00:00
return 0 ;
}
static int igt_color_evict ( void * ignored )
{
DRM_RND_STATE ( prng , random_seed ) ;
const unsigned int total_size = min ( 8192u , max_iterations ) ;
const struct insert_mode * mode ;
unsigned long color = 0 ;
struct drm_mm mm ;
struct evict_node * nodes ;
struct drm_mm_node * node , * next ;
unsigned int * order , n ;
int ret , err ;
/* Check that the drm_mm_scan also honours color adjustment when
* choosing its victims to create a hole . Our color_adjust does not
* allow two nodes to be placed together without an intervening hole
* enlarging the set of victims that must be evicted .
*/
ret = - ENOMEM ;
nodes = vzalloc ( total_size * sizeof ( * nodes ) ) ;
if ( ! nodes )
goto err ;
order = drm_random_order ( total_size , & prng ) ;
if ( ! order )
goto err_nodes ;
ret = - EINVAL ;
drm_mm_init ( & mm , 0 , 2 * total_size - 1 ) ;
mm . color_adjust = separate_adjacent_colors ;
for ( n = 0 ; n < total_size ; n + + ) {
if ( ! expect_insert ( & mm , & nodes [ n ] . node ,
1 , 0 , color + + ,
& insert_modes [ 0 ] ) ) {
pr_err ( " insert failed, step %d \n " , n ) ;
goto out ;
}
}
for ( mode = evict_modes ; mode - > name ; mode + + ) {
for ( n = 1 ; n < = total_size ; n < < = 1 ) {
drm_random_reorder ( order , total_size , & prng ) ;
2016-12-22 08:36:22 +00:00
err = evict_color ( & mm , 0 , U64_MAX ,
2016-12-22 08:36:21 +00:00
nodes , order , total_size ,
n , 1 , color + + ,
mode ) ;
if ( err ) {
pr_err ( " %s evict_color(size=%u) failed \n " ,
mode - > name , n ) ;
goto out ;
}
}
for ( n = 1 ; n < total_size ; n < < = 1 ) {
drm_random_reorder ( order , total_size , & prng ) ;
2016-12-22 08:36:22 +00:00
err = evict_color ( & mm , 0 , U64_MAX ,
2016-12-22 08:36:21 +00:00
nodes , order , total_size ,
total_size / 2 , n , color + + ,
mode ) ;
if ( err ) {
pr_err ( " %s evict_color(size=%u, alignment=%u) failed \n " ,
mode - > name , total_size / 2 , n ) ;
goto out ;
}
}
for_each_prime_number_from ( n , 1 , min ( total_size , max_prime ) ) {
unsigned int nsize = ( total_size - n + 1 ) / 2 ;
DRM_MM_BUG_ON ( ! nsize ) ;
drm_random_reorder ( order , total_size , & prng ) ;
2016-12-22 08:36:22 +00:00
err = evict_color ( & mm , 0 , U64_MAX ,
2016-12-22 08:36:21 +00:00
nodes , order , total_size ,
nsize , n , color + + ,
mode ) ;
if ( err ) {
pr_err ( " %s evict_color(size=%u, alignment=%u) failed \n " ,
mode - > name , nsize , n ) ;
goto out ;
}
}
2017-03-29 10:10:53 +01:00
cond_resched ( ) ;
2016-12-22 08:36:21 +00:00
}
ret = 0 ;
out :
if ( ret )
2016-12-29 12:09:24 +01:00
show_mm ( & mm ) ;
2016-12-22 08:36:21 +00:00
drm_mm_for_each_node_safe ( node , next , & mm )
drm_mm_remove_node ( node ) ;
drm_mm_takedown ( & mm ) ;
kfree ( order ) ;
err_nodes :
vfree ( nodes ) ;
err :
return ret ;
}
2016-12-22 08:36:22 +00:00
static int igt_color_evict_range ( void * ignored )
{
DRM_RND_STATE ( prng , random_seed ) ;
const unsigned int total_size = 8192 ;
const unsigned int range_size = total_size / 2 ;
const unsigned int range_start = total_size / 4 ;
const unsigned int range_end = range_start + range_size ;
const struct insert_mode * mode ;
unsigned long color = 0 ;
struct drm_mm mm ;
struct evict_node * nodes ;
struct drm_mm_node * node , * next ;
unsigned int * order , n ;
int ret , err ;
/* Like igt_color_evict(), but limited to small portion of the full
* drm_mm range .
*/
ret = - ENOMEM ;
nodes = vzalloc ( total_size * sizeof ( * nodes ) ) ;
if ( ! nodes )
goto err ;
order = drm_random_order ( total_size , & prng ) ;
if ( ! order )
goto err_nodes ;
ret = - EINVAL ;
drm_mm_init ( & mm , 0 , 2 * total_size - 1 ) ;
mm . color_adjust = separate_adjacent_colors ;
for ( n = 0 ; n < total_size ; n + + ) {
if ( ! expect_insert ( & mm , & nodes [ n ] . node ,
1 , 0 , color + + ,
& insert_modes [ 0 ] ) ) {
pr_err ( " insert failed, step %d \n " , n ) ;
goto out ;
}
}
for ( mode = evict_modes ; mode - > name ; mode + + ) {
for ( n = 1 ; n < = range_size ; n < < = 1 ) {
drm_random_reorder ( order , range_size , & prng ) ;
err = evict_color ( & mm , range_start , range_end ,
nodes , order , total_size ,
n , 1 , color + + ,
mode ) ;
if ( err ) {
pr_err ( " %s evict_color(size=%u) failed for range [%x, %x] \n " ,
mode - > name , n , range_start , range_end ) ;
goto out ;
}
}
for ( n = 1 ; n < range_size ; n < < = 1 ) {
drm_random_reorder ( order , total_size , & prng ) ;
err = evict_color ( & mm , range_start , range_end ,
nodes , order , total_size ,
range_size / 2 , n , color + + ,
mode ) ;
if ( err ) {
pr_err ( " %s evict_color(size=%u, alignment=%u) failed for range [%x, %x] \n " ,
mode - > name , total_size / 2 , n , range_start , range_end ) ;
goto out ;
}
}
for_each_prime_number_from ( n , 1 , min ( range_size , max_prime ) ) {
unsigned int nsize = ( range_size - n + 1 ) / 2 ;
DRM_MM_BUG_ON ( ! nsize ) ;
drm_random_reorder ( order , total_size , & prng ) ;
err = evict_color ( & mm , range_start , range_end ,
nodes , order , total_size ,
nsize , n , color + + ,
mode ) ;
if ( err ) {
pr_err ( " %s evict_color(size=%u, alignment=%u) failed for range [%x, %x] \n " ,
mode - > name , nsize , n , range_start , range_end ) ;
goto out ;
}
}
2017-03-29 10:10:53 +01:00
cond_resched ( ) ;
2016-12-22 08:36:22 +00:00
}
ret = 0 ;
out :
if ( ret )
2016-12-29 12:09:24 +01:00
show_mm ( & mm ) ;
2016-12-22 08:36:22 +00:00
drm_mm_for_each_node_safe ( node , next , & mm )
drm_mm_remove_node ( node ) ;
drm_mm_takedown ( & mm ) ;
kfree ( order ) ;
err_nodes :
vfree ( nodes ) ;
err :
return ret ;
}
2016-12-22 08:36:09 +00:00
# include "drm_selftest.c"
static int __init test_drm_mm_init ( void )
{
int err ;
while ( ! random_seed )
random_seed = get_random_int ( ) ;
pr_info ( " Testing DRM range manger (struct drm_mm), with random_seed=0x%x max_iterations=%u max_prime=%u \n " ,
random_seed , max_iterations , max_prime ) ;
err = run_selftests ( selftests , ARRAY_SIZE ( selftests ) , NULL ) ;
return err > 0 ? 0 : err ;
}
static void __exit test_drm_mm_exit ( void )
{
}
module_init ( test_drm_mm_init ) ;
module_exit ( test_drm_mm_exit ) ;
module_param ( random_seed , uint , 0400 ) ;
module_param ( max_iterations , uint , 0400 ) ;
module_param ( max_prime , uint , 0400 ) ;
MODULE_AUTHOR ( " Intel Corporation " ) ;
MODULE_LICENSE ( " GPL " ) ;