2020-04-22 22:50:27 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* HMM stands for Heterogeneous Memory Management , it is a helper layer inside
* the linux kernel to help device drivers mirror a process address space in
* the device . This allows the device to use the same address space which
* makes communication and data exchange a lot easier .
*
* This framework ' s sole purpose is to exercise various code paths inside
* the kernel to make sure that HMM performs as expected and to flush out any
* bugs .
*/
# include "../kselftest_harness.h"
# include <errno.h>
# include <fcntl.h>
# include <stdio.h>
# include <stdlib.h>
# include <stdint.h>
# include <unistd.h>
# include <strings.h>
# include <time.h>
# include <pthread.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/mman.h>
# include <sys/ioctl.h>
selftests/vm: hmm-tests: remove the libhugetlbfs dependency
HMM selftests are incredibly useful, but they are only effective if people
actually build and run them. All the other tests in selftests/vm can be
built with very standard, always-available libraries: libpthread, librt.
The hmm-tests.c program, on the other hand, requires something that is
(much) less readily available: libhugetlbfs. And so the build will
typically fail for many developers.
A simple attempt to install libhugetlbfs will also run into complications
on some common distros these days: Fedora and Arch Linux (yes, Arch AUR
has it, but that's fragile, as always with AUR). The library is not
maintained actively enough at the moment, for distros to deal with it. I
had to build it from source, for Fedora, and that didn't go too smoothly
either.
It turns out that, out of 21 tests in hmm-tests.c, only 2 actually require
functionality from libhugetlbfs. Therefore, if libhugetlbfs is missing,
simply ifdef those two tests out and allow the developer to at least have
the other 19 tests, if they don't want to pause to work through the above
issues. Also issue a warning, so that it's clear that there is an
imperfection in the build.
In order to do that, a tiny shell script (check_config.sh) runs a quick
compile (not link, that's too prone to false failures with library paths),
and basically, if the compiler doesn't find hugetlbfs.h in its standard
locations, then the script concludes that libhugetlbfs is not available.
The output is in two files, one for inclusion in hmm-test.c
(local_config.h), and one for inclusion in the Makefile (local_config.mk).
Link: https://lkml.kernel.org/r/20201026064021.3545418-9-jhubbard@nvidia.com
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
Cc: Ralph Campbell <rcampbell@nvidia.com>
Cc: Jérôme Glisse <jglisse@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2020-12-15 06:05:28 +03:00
# include "./local_config.h"
# ifdef LOCAL_CONFIG_HAVE_LIBHUGETLBFS
# include <hugetlbfs.h>
# endif
2020-04-22 22:50:27 +03:00
/*
* This is a private UAPI to the kernel test module so it isn ' t exported
* in the usual include / uapi / . . . directory .
*/
# include "../../../../lib/test_hmm_uapi.h"
struct hmm_buffer {
void * ptr ;
void * mirror ;
unsigned long size ;
int fd ;
uint64_t cpages ;
uint64_t faults ;
} ;
# define TWOMEG (1 << 21)
# define HMM_BUFFER_SIZE (1024 << 12)
# define HMM_PATH_MAX 64
2020-10-18 02:14:47 +03:00
# define NTIMES 10
2020-04-22 22:50:27 +03:00
# define ALIGN(x, a) (((x) + (a - 1)) & (~((a) - 1)))
FIXTURE ( hmm )
{
int fd ;
unsigned int page_size ;
unsigned int page_shift ;
} ;
FIXTURE ( hmm2 )
{
int fd0 ;
int fd1 ;
unsigned int page_size ;
unsigned int page_shift ;
} ;
static int hmm_open ( int unit )
{
char pathname [ HMM_PATH_MAX ] ;
int fd ;
snprintf ( pathname , sizeof ( pathname ) , " /dev/hmm_dmirror%d " , unit ) ;
fd = open ( pathname , O_RDWR , 0 ) ;
if ( fd < 0 )
fprintf ( stderr , " could not open hmm dmirror driver (%s) \n " ,
pathname ) ;
return fd ;
}
FIXTURE_SETUP ( hmm )
{
self - > page_size = sysconf ( _SC_PAGE_SIZE ) ;
self - > page_shift = ffs ( self - > page_size ) - 1 ;
self - > fd = hmm_open ( 0 ) ;
ASSERT_GE ( self - > fd , 0 ) ;
}
FIXTURE_SETUP ( hmm2 )
{
self - > page_size = sysconf ( _SC_PAGE_SIZE ) ;
self - > page_shift = ffs ( self - > page_size ) - 1 ;
self - > fd0 = hmm_open ( 0 ) ;
ASSERT_GE ( self - > fd0 , 0 ) ;
self - > fd1 = hmm_open ( 1 ) ;
ASSERT_GE ( self - > fd1 , 0 ) ;
}
FIXTURE_TEARDOWN ( hmm )
{
int ret = close ( self - > fd ) ;
ASSERT_EQ ( ret , 0 ) ;
self - > fd = - 1 ;
}
FIXTURE_TEARDOWN ( hmm2 )
{
int ret = close ( self - > fd0 ) ;
ASSERT_EQ ( ret , 0 ) ;
self - > fd0 = - 1 ;
ret = close ( self - > fd1 ) ;
ASSERT_EQ ( ret , 0 ) ;
self - > fd1 = - 1 ;
}
static int hmm_dmirror_cmd ( int fd ,
unsigned long request ,
struct hmm_buffer * buffer ,
unsigned long npages )
{
struct hmm_dmirror_cmd cmd ;
int ret ;
/* Simulate a device reading system memory. */
cmd . addr = ( __u64 ) buffer - > ptr ;
cmd . ptr = ( __u64 ) buffer - > mirror ;
cmd . npages = npages ;
for ( ; ; ) {
ret = ioctl ( fd , request , & cmd ) ;
if ( ret = = 0 )
break ;
if ( errno = = EINTR )
continue ;
return - errno ;
}
buffer - > cpages = cmd . cpages ;
buffer - > faults = cmd . faults ;
return 0 ;
}
static void hmm_buffer_free ( struct hmm_buffer * buffer )
{
if ( buffer = = NULL )
return ;
if ( buffer - > ptr )
munmap ( buffer - > ptr , buffer - > size ) ;
free ( buffer - > mirror ) ;
free ( buffer ) ;
}
/*
* Create a temporary file that will be deleted on close .
*/
static int hmm_create_file ( unsigned long size )
{
char path [ HMM_PATH_MAX ] ;
int fd ;
strcpy ( path , " /tmp " ) ;
fd = open ( path , O_TMPFILE | O_EXCL | O_RDWR , 0600 ) ;
if ( fd > = 0 ) {
int r ;
do {
r = ftruncate ( fd , size ) ;
} while ( r = = - 1 & & errno = = EINTR ) ;
if ( ! r )
return fd ;
close ( fd ) ;
}
return - 1 ;
}
/*
* Return a random unsigned number .
*/
static unsigned int hmm_random ( void )
{
static int fd = - 1 ;
unsigned int r ;
if ( fd < 0 ) {
fd = open ( " /dev/urandom " , O_RDONLY ) ;
if ( fd < 0 ) {
fprintf ( stderr , " %s:%d failed to open /dev/urandom \n " ,
__FILE__ , __LINE__ ) ;
return ~ 0U ;
}
}
read ( fd , & r , sizeof ( r ) ) ;
return r ;
}
static void hmm_nanosleep ( unsigned int n )
{
struct timespec t ;
t . tv_sec = 0 ;
t . tv_nsec = n ;
nanosleep ( & t , NULL ) ;
}
/*
* Simple NULL test of device open / close .
*/
TEST_F ( hmm , open_close )
{
}
/*
* Read private anonymous memory .
*/
TEST_F ( hmm , anon_read )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
int * ptr ;
int ret ;
int val ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/*
* Initialize buffer in system memory but leave the first two pages
* zero ( pte_none and pfn_zero ) .
*/
i = 2 * self - > page_size / sizeof ( * ptr ) ;
for ( ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Set buffer permission to read-only. */
ret = mprotect ( buffer - > ptr , size , PROT_READ ) ;
ASSERT_EQ ( ret , 0 ) ;
/* Populate the CPU page table with a special zero page. */
val = * ( int * ) ( buffer - > ptr + self - > page_size ) ;
ASSERT_EQ ( val , 0 ) ;
/* Simulate a device reading system memory. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_READ , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
ASSERT_EQ ( buffer - > faults , 1 ) ;
/* Check what the device read. */
ptr = buffer - > mirror ;
for ( i = 0 ; i < 2 * self - > page_size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , 0 ) ;
for ( ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
hmm_buffer_free ( buffer ) ;
}
/*
* Read private anonymous memory which has been protected with
* mprotect ( ) PROT_NONE .
*/
TEST_F ( hmm , anon_read_prot )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
int * ptr ;
int ret ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Initialize buffer in system memory. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Initialize mirror buffer so we can verify it isn't written. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = - i ;
/* Protect buffer from reading. */
ret = mprotect ( buffer - > ptr , size , PROT_NONE ) ;
ASSERT_EQ ( ret , 0 ) ;
/* Simulate a device reading system memory. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_READ , buffer , npages ) ;
ASSERT_EQ ( ret , - EFAULT ) ;
/* Allow CPU to read the buffer so we can check it. */
ret = mprotect ( buffer - > ptr , size , PROT_READ ) ;
ASSERT_EQ ( ret , 0 ) ;
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
/* Check what the device read. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , - i ) ;
hmm_buffer_free ( buffer ) ;
}
/*
* Write private anonymous memory .
*/
TEST_F ( hmm , anon_write )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
int * ptr ;
int ret ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Initialize data that the device will write to buffer->ptr. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Simulate a device writing system memory. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_WRITE , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
ASSERT_EQ ( buffer - > faults , 1 ) ;
/* Check what the device wrote. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
hmm_buffer_free ( buffer ) ;
}
/*
* Write private anonymous memory which has been protected with
* mprotect ( ) PROT_READ .
*/
TEST_F ( hmm , anon_write_prot )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
int * ptr ;
int ret ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Simulate a device reading a zero page of memory. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_READ , buffer , 1 ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , 1 ) ;
ASSERT_EQ ( buffer - > faults , 1 ) ;
/* Initialize data that the device will write to buffer->ptr. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Simulate a device writing system memory. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_WRITE , buffer , npages ) ;
ASSERT_EQ ( ret , - EPERM ) ;
/* Check what the device wrote. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , 0 ) ;
/* Now allow writing and see that the zero page is replaced. */
ret = mprotect ( buffer - > ptr , size , PROT_WRITE | PROT_READ ) ;
ASSERT_EQ ( ret , 0 ) ;
/* Simulate a device writing system memory. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_WRITE , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
ASSERT_EQ ( buffer - > faults , 1 ) ;
/* Check what the device wrote. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
hmm_buffer_free ( buffer ) ;
}
/*
* Check that a device writing an anonymous private mapping
* will copy - on - write if a child process inherits the mapping .
*/
TEST_F ( hmm , anon_write_child )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
int * ptr ;
pid_t pid ;
int child_fd ;
int ret ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Initialize buffer->ptr so we can tell if it is written. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Initialize data that the device will write to buffer->ptr. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = - i ;
pid = fork ( ) ;
if ( pid = = - 1 )
ASSERT_EQ ( pid , 0 ) ;
if ( pid ! = 0 ) {
waitpid ( pid , & ret , 0 ) ;
ASSERT_EQ ( WIFEXITED ( ret ) , 1 ) ;
/* Check that the parent's buffer did not change. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
return ;
}
/* Check that we see the parent's values. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , - i ) ;
/* The child process needs its own mirror to its own mm. */
child_fd = hmm_open ( 0 ) ;
ASSERT_GE ( child_fd , 0 ) ;
/* Simulate a device writing system memory. */
ret = hmm_dmirror_cmd ( child_fd , HMM_DMIRROR_WRITE , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
ASSERT_EQ ( buffer - > faults , 1 ) ;
/* Check what the device wrote. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , - i ) ;
close ( child_fd ) ;
exit ( 0 ) ;
}
/*
* Check that a device writing an anonymous shared mapping
* will not copy - on - write if a child process inherits the mapping .
*/
TEST_F ( hmm , anon_write_child_shared )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
int * ptr ;
pid_t pid ;
int child_fd ;
int ret ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_SHARED | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Initialize buffer->ptr so we can tell if it is written. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Initialize data that the device will write to buffer->ptr. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = - i ;
pid = fork ( ) ;
if ( pid = = - 1 )
ASSERT_EQ ( pid , 0 ) ;
if ( pid ! = 0 ) {
waitpid ( pid , & ret , 0 ) ;
ASSERT_EQ ( WIFEXITED ( ret ) , 1 ) ;
/* Check that the parent's buffer did change. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , - i ) ;
return ;
}
/* Check that we see the parent's values. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , - i ) ;
/* The child process needs its own mirror to its own mm. */
child_fd = hmm_open ( 0 ) ;
ASSERT_GE ( child_fd , 0 ) ;
/* Simulate a device writing system memory. */
ret = hmm_dmirror_cmd ( child_fd , HMM_DMIRROR_WRITE , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
ASSERT_EQ ( buffer - > faults , 1 ) ;
/* Check what the device wrote. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , - i ) ;
close ( child_fd ) ;
exit ( 0 ) ;
}
/*
* Write private anonymous huge page .
*/
TEST_F ( hmm , anon_write_huge )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
void * old_ptr ;
void * map ;
int * ptr ;
int ret ;
size = 2 * TWOMEG ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
size = TWOMEG ;
npages = size > > self - > page_shift ;
map = ( void * ) ALIGN ( ( uintptr_t ) buffer - > ptr , size ) ;
ret = madvise ( map , size , MADV_HUGEPAGE ) ;
ASSERT_EQ ( ret , 0 ) ;
old_ptr = buffer - > ptr ;
buffer - > ptr = map ;
/* Initialize data that the device will write to buffer->ptr. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Simulate a device writing system memory. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_WRITE , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
ASSERT_EQ ( buffer - > faults , 1 ) ;
/* Check what the device wrote. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
buffer - > ptr = old_ptr ;
hmm_buffer_free ( buffer ) ;
}
selftests/vm: hmm-tests: remove the libhugetlbfs dependency
HMM selftests are incredibly useful, but they are only effective if people
actually build and run them. All the other tests in selftests/vm can be
built with very standard, always-available libraries: libpthread, librt.
The hmm-tests.c program, on the other hand, requires something that is
(much) less readily available: libhugetlbfs. And so the build will
typically fail for many developers.
A simple attempt to install libhugetlbfs will also run into complications
on some common distros these days: Fedora and Arch Linux (yes, Arch AUR
has it, but that's fragile, as always with AUR). The library is not
maintained actively enough at the moment, for distros to deal with it. I
had to build it from source, for Fedora, and that didn't go too smoothly
either.
It turns out that, out of 21 tests in hmm-tests.c, only 2 actually require
functionality from libhugetlbfs. Therefore, if libhugetlbfs is missing,
simply ifdef those two tests out and allow the developer to at least have
the other 19 tests, if they don't want to pause to work through the above
issues. Also issue a warning, so that it's clear that there is an
imperfection in the build.
In order to do that, a tiny shell script (check_config.sh) runs a quick
compile (not link, that's too prone to false failures with library paths),
and basically, if the compiler doesn't find hugetlbfs.h in its standard
locations, then the script concludes that libhugetlbfs is not available.
The output is in two files, one for inclusion in hmm-test.c
(local_config.h), and one for inclusion in the Makefile (local_config.mk).
Link: https://lkml.kernel.org/r/20201026064021.3545418-9-jhubbard@nvidia.com
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
Cc: Ralph Campbell <rcampbell@nvidia.com>
Cc: Jérôme Glisse <jglisse@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2020-12-15 06:05:28 +03:00
# ifdef LOCAL_CONFIG_HAVE_LIBHUGETLBFS
2020-04-22 22:50:27 +03:00
/*
* Write huge TLBFS page .
*/
TEST_F ( hmm , anon_write_hugetlbfs )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
int * ptr ;
int ret ;
long pagesizes [ 4 ] ;
int n , idx ;
/* Skip test if we can't allocate a hugetlbfs page. */
n = gethugepagesizes ( pagesizes , 4 ) ;
if ( n < = 0 )
2020-10-14 02:54:29 +03:00
SKIP ( return , " Huge page size could not be determined " ) ;
2020-04-22 22:50:27 +03:00
for ( idx = 0 ; - - n > 0 ; ) {
if ( pagesizes [ n ] < pagesizes [ idx ] )
idx = n ;
}
size = ALIGN ( TWOMEG , pagesizes [ idx ] ) ;
npages = size > > self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > ptr = get_hugepage_region ( size , GHR_STRICT ) ;
if ( buffer - > ptr = = NULL ) {
free ( buffer ) ;
2020-10-14 02:54:29 +03:00
SKIP ( return , " Huge page could not be allocated " ) ;
2020-04-22 22:50:27 +03:00
}
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
/* Initialize data that the device will write to buffer->ptr. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Simulate a device writing system memory. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_WRITE , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
ASSERT_EQ ( buffer - > faults , 1 ) ;
/* Check what the device wrote. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
free_hugepage_region ( buffer - > ptr ) ;
buffer - > ptr = NULL ;
hmm_buffer_free ( buffer ) ;
}
selftests/vm: hmm-tests: remove the libhugetlbfs dependency
HMM selftests are incredibly useful, but they are only effective if people
actually build and run them. All the other tests in selftests/vm can be
built with very standard, always-available libraries: libpthread, librt.
The hmm-tests.c program, on the other hand, requires something that is
(much) less readily available: libhugetlbfs. And so the build will
typically fail for many developers.
A simple attempt to install libhugetlbfs will also run into complications
on some common distros these days: Fedora and Arch Linux (yes, Arch AUR
has it, but that's fragile, as always with AUR). The library is not
maintained actively enough at the moment, for distros to deal with it. I
had to build it from source, for Fedora, and that didn't go too smoothly
either.
It turns out that, out of 21 tests in hmm-tests.c, only 2 actually require
functionality from libhugetlbfs. Therefore, if libhugetlbfs is missing,
simply ifdef those two tests out and allow the developer to at least have
the other 19 tests, if they don't want to pause to work through the above
issues. Also issue a warning, so that it's clear that there is an
imperfection in the build.
In order to do that, a tiny shell script (check_config.sh) runs a quick
compile (not link, that's too prone to false failures with library paths),
and basically, if the compiler doesn't find hugetlbfs.h in its standard
locations, then the script concludes that libhugetlbfs is not available.
The output is in two files, one for inclusion in hmm-test.c
(local_config.h), and one for inclusion in the Makefile (local_config.mk).
Link: https://lkml.kernel.org/r/20201026064021.3545418-9-jhubbard@nvidia.com
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
Cc: Ralph Campbell <rcampbell@nvidia.com>
Cc: Jérôme Glisse <jglisse@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2020-12-15 06:05:28 +03:00
# endif /* LOCAL_CONFIG_HAVE_LIBHUGETLBFS */
2020-04-22 22:50:27 +03:00
/*
* Read mmap ' ed file memory .
*/
TEST_F ( hmm , file_read )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
int * ptr ;
int ret ;
int fd ;
ssize_t len ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
fd = hmm_create_file ( size ) ;
ASSERT_GE ( fd , 0 ) ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = fd ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
/* Write initial contents of the file. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
len = pwrite ( fd , buffer - > mirror , size , 0 ) ;
ASSERT_EQ ( len , size ) ;
memset ( buffer - > mirror , 0 , size ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ ,
MAP_SHARED ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Simulate a device reading system memory. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_READ , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
ASSERT_EQ ( buffer - > faults , 1 ) ;
/* Check what the device read. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
hmm_buffer_free ( buffer ) ;
}
/*
* Write mmap ' ed file memory .
*/
TEST_F ( hmm , file_write )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
int * ptr ;
int ret ;
int fd ;
ssize_t len ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
fd = hmm_create_file ( size ) ;
ASSERT_GE ( fd , 0 ) ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = fd ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_SHARED ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Initialize data that the device will write to buffer->ptr. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Simulate a device writing system memory. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_WRITE , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
ASSERT_EQ ( buffer - > faults , 1 ) ;
/* Check what the device wrote. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
/* Check that the device also wrote the file. */
len = pread ( fd , buffer - > mirror , size , 0 ) ;
ASSERT_EQ ( len , size ) ;
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
hmm_buffer_free ( buffer ) ;
}
/*
* Migrate anonymous memory to device private memory .
*/
TEST_F ( hmm , migrate )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
int * ptr ;
int ret ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Initialize buffer in system memory. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Migrate memory to device. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_MIGRATE , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
/* Check what the device read. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
hmm_buffer_free ( buffer ) ;
}
/*
2020-07-24 01:30:03 +03:00
* Migrate anonymous memory to device private memory and fault some of it back
* to system memory , then try migrating the resulting mix of system and device
* private memory to the device .
2020-04-22 22:50:27 +03:00
*/
TEST_F ( hmm , migrate_fault )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
int * ptr ;
int ret ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Initialize buffer in system memory. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Migrate memory to device. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_MIGRATE , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
/* Check what the device read. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
2020-07-24 01:30:03 +03:00
/* Fault half the pages back to system memory and check them. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / ( 2 * sizeof ( * ptr ) ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
/* Migrate memory to the device again. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_MIGRATE , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
/* Check what the device read. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
2020-04-22 22:50:27 +03:00
ASSERT_EQ ( ptr [ i ] , i ) ;
hmm_buffer_free ( buffer ) ;
}
2020-08-12 04:31:45 +03:00
/*
* Migrate anonymous shared memory to device private memory .
*/
TEST_F ( hmm , migrate_shared )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
int ret ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_SHARED | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Migrate memory to device. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_MIGRATE , buffer , npages ) ;
ASSERT_EQ ( ret , - ENOENT ) ;
hmm_buffer_free ( buffer ) ;
}
2020-04-22 22:50:27 +03:00
/*
* Try to migrate various memory types to device private memory .
*/
TEST_F ( hmm2 , migrate_mixed )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
int * ptr ;
unsigned char * p ;
int ret ;
int val ;
npages = 6 ;
size = npages < < self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
/* Reserve a range of addresses. */
buffer - > ptr = mmap ( NULL , size ,
PROT_NONE ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
p = buffer - > ptr ;
/* Migrating a protected area should be an error. */
ret = hmm_dmirror_cmd ( self - > fd1 , HMM_DMIRROR_MIGRATE , buffer , npages ) ;
ASSERT_EQ ( ret , - EINVAL ) ;
/* Punch a hole after the first page address. */
ret = munmap ( buffer - > ptr + self - > page_size , self - > page_size ) ;
ASSERT_EQ ( ret , 0 ) ;
/* We expect an error if the vma doesn't cover the range. */
ret = hmm_dmirror_cmd ( self - > fd1 , HMM_DMIRROR_MIGRATE , buffer , 3 ) ;
ASSERT_EQ ( ret , - EINVAL ) ;
/* Page 2 will be a read-only zero page. */
ret = mprotect ( buffer - > ptr + 2 * self - > page_size , self - > page_size ,
PROT_READ ) ;
ASSERT_EQ ( ret , 0 ) ;
ptr = ( int * ) ( buffer - > ptr + 2 * self - > page_size ) ;
val = * ptr + 3 ;
ASSERT_EQ ( val , 3 ) ;
/* Page 3 will be read-only. */
ret = mprotect ( buffer - > ptr + 3 * self - > page_size , self - > page_size ,
PROT_READ | PROT_WRITE ) ;
ASSERT_EQ ( ret , 0 ) ;
ptr = ( int * ) ( buffer - > ptr + 3 * self - > page_size ) ;
* ptr = val ;
ret = mprotect ( buffer - > ptr + 3 * self - > page_size , self - > page_size ,
PROT_READ ) ;
ASSERT_EQ ( ret , 0 ) ;
/* Page 4-5 will be read-write. */
ret = mprotect ( buffer - > ptr + 4 * self - > page_size , 2 * self - > page_size ,
PROT_READ | PROT_WRITE ) ;
ASSERT_EQ ( ret , 0 ) ;
ptr = ( int * ) ( buffer - > ptr + 4 * self - > page_size ) ;
* ptr = val ;
ptr = ( int * ) ( buffer - > ptr + 5 * self - > page_size ) ;
* ptr = val ;
/* Now try to migrate pages 2-5 to device 1. */
buffer - > ptr = p + 2 * self - > page_size ;
ret = hmm_dmirror_cmd ( self - > fd1 , HMM_DMIRROR_MIGRATE , buffer , 4 ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , 4 ) ;
/* Page 5 won't be migrated to device 0 because it's on device 1. */
buffer - > ptr = p + 5 * self - > page_size ;
ret = hmm_dmirror_cmd ( self - > fd0 , HMM_DMIRROR_MIGRATE , buffer , 1 ) ;
ASSERT_EQ ( ret , - ENOENT ) ;
buffer - > ptr = p ;
buffer - > ptr = p ;
hmm_buffer_free ( buffer ) ;
}
/*
* Migrate anonymous memory to device private memory and fault it back to system
* memory multiple times .
*/
TEST_F ( hmm , migrate_multiple )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
unsigned long c ;
int * ptr ;
int ret ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
for ( c = 0 ; c < NTIMES ; c + + ) {
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Initialize buffer in system memory. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Migrate memory to device. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_MIGRATE , buffer ,
npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
/* Check what the device read. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
/* Fault pages back to system memory and check them. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
hmm_buffer_free ( buffer ) ;
}
}
/*
* Read anonymous memory multiple times .
*/
TEST_F ( hmm , anon_read_multiple )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
unsigned long c ;
int * ptr ;
int ret ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
for ( c = 0 ; c < NTIMES ; c + + ) {
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Initialize buffer in system memory. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i + c ;
/* Simulate a device reading system memory. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_READ , buffer ,
npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
ASSERT_EQ ( buffer - > faults , 1 ) ;
/* Check what the device read. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i + c ) ;
hmm_buffer_free ( buffer ) ;
}
}
void * unmap_buffer ( void * p )
{
struct hmm_buffer * buffer = p ;
/* Delay for a bit and then unmap buffer while it is being read. */
hmm_nanosleep ( hmm_random ( ) % 32000 ) ;
munmap ( buffer - > ptr + buffer - > size / 2 , buffer - > size / 2 ) ;
buffer - > ptr = NULL ;
return NULL ;
}
/*
* Try reading anonymous memory while it is being unmapped .
*/
TEST_F ( hmm , anon_teardown )
{
unsigned long npages ;
unsigned long size ;
unsigned long c ;
void * ret ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
for ( c = 0 ; c < NTIMES ; + + c ) {
pthread_t thread ;
struct hmm_buffer * buffer ;
unsigned long i ;
int * ptr ;
int rc ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Initialize buffer in system memory. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i + c ;
rc = pthread_create ( & thread , NULL , unmap_buffer , buffer ) ;
ASSERT_EQ ( rc , 0 ) ;
/* Simulate a device reading system memory. */
rc = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_READ , buffer ,
npages ) ;
if ( rc = = 0 ) {
ASSERT_EQ ( buffer - > cpages , npages ) ;
ASSERT_EQ ( buffer - > faults , 1 ) ;
/* Check what the device read. */
for ( i = 0 , ptr = buffer - > mirror ;
i < size / sizeof ( * ptr ) ;
+ + i )
ASSERT_EQ ( ptr [ i ] , i + c ) ;
}
pthread_join ( thread , & ret ) ;
hmm_buffer_free ( buffer ) ;
}
}
2022-01-15 01:09:31 +03:00
/*
* Test memory snapshot without faulting in pages accessed by the device .
*/
TEST_F ( hmm , mixedmap )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned char * m ;
int ret ;
npages = 1 ;
size = npages < < self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( npages ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
/* Reserve a range of addresses. */
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_PRIVATE ,
self - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Simulate a device snapshotting CPU pagetables. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_SNAPSHOT , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
/* Check what the device saw. */
m = buffer - > mirror ;
ASSERT_EQ ( m [ 0 ] , HMM_DMIRROR_PROT_READ ) ;
hmm_buffer_free ( buffer ) ;
}
2020-04-22 22:50:27 +03:00
/*
* Test memory snapshot without faulting in pages accessed by the device .
*/
TEST_F ( hmm2 , snapshot )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
int * ptr ;
unsigned char * p ;
unsigned char * m ;
int ret ;
int val ;
npages = 7 ;
size = npages < < self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( npages ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
/* Reserve a range of addresses. */
buffer - > ptr = mmap ( NULL , size ,
PROT_NONE ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
p = buffer - > ptr ;
/* Punch a hole after the first page address. */
ret = munmap ( buffer - > ptr + self - > page_size , self - > page_size ) ;
ASSERT_EQ ( ret , 0 ) ;
/* Page 2 will be read-only zero page. */
ret = mprotect ( buffer - > ptr + 2 * self - > page_size , self - > page_size ,
PROT_READ ) ;
ASSERT_EQ ( ret , 0 ) ;
ptr = ( int * ) ( buffer - > ptr + 2 * self - > page_size ) ;
val = * ptr + 3 ;
ASSERT_EQ ( val , 3 ) ;
/* Page 3 will be read-only. */
ret = mprotect ( buffer - > ptr + 3 * self - > page_size , self - > page_size ,
PROT_READ | PROT_WRITE ) ;
ASSERT_EQ ( ret , 0 ) ;
ptr = ( int * ) ( buffer - > ptr + 3 * self - > page_size ) ;
* ptr = val ;
ret = mprotect ( buffer - > ptr + 3 * self - > page_size , self - > page_size ,
PROT_READ ) ;
ASSERT_EQ ( ret , 0 ) ;
/* Page 4-6 will be read-write. */
ret = mprotect ( buffer - > ptr + 4 * self - > page_size , 3 * self - > page_size ,
PROT_READ | PROT_WRITE ) ;
ASSERT_EQ ( ret , 0 ) ;
ptr = ( int * ) ( buffer - > ptr + 4 * self - > page_size ) ;
* ptr = val ;
/* Page 5 will be migrated to device 0. */
buffer - > ptr = p + 5 * self - > page_size ;
ret = hmm_dmirror_cmd ( self - > fd0 , HMM_DMIRROR_MIGRATE , buffer , 1 ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , 1 ) ;
/* Page 6 will be migrated to device 1. */
buffer - > ptr = p + 6 * self - > page_size ;
ret = hmm_dmirror_cmd ( self - > fd1 , HMM_DMIRROR_MIGRATE , buffer , 1 ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , 1 ) ;
/* Simulate a device snapshotting CPU pagetables. */
buffer - > ptr = p ;
ret = hmm_dmirror_cmd ( self - > fd0 , HMM_DMIRROR_SNAPSHOT , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
/* Check what the device saw. */
m = buffer - > mirror ;
ASSERT_EQ ( m [ 0 ] , HMM_DMIRROR_PROT_ERROR ) ;
ASSERT_EQ ( m [ 1 ] , HMM_DMIRROR_PROT_ERROR ) ;
ASSERT_EQ ( m [ 2 ] , HMM_DMIRROR_PROT_ZERO | HMM_DMIRROR_PROT_READ ) ;
ASSERT_EQ ( m [ 3 ] , HMM_DMIRROR_PROT_READ ) ;
ASSERT_EQ ( m [ 4 ] , HMM_DMIRROR_PROT_WRITE ) ;
ASSERT_EQ ( m [ 5 ] , HMM_DMIRROR_PROT_DEV_PRIVATE_LOCAL |
HMM_DMIRROR_PROT_WRITE ) ;
ASSERT_EQ ( m [ 6 ] , HMM_DMIRROR_PROT_NONE ) ;
hmm_buffer_free ( buffer ) ;
}
selftests/vm: hmm-tests: remove the libhugetlbfs dependency
HMM selftests are incredibly useful, but they are only effective if people
actually build and run them. All the other tests in selftests/vm can be
built with very standard, always-available libraries: libpthread, librt.
The hmm-tests.c program, on the other hand, requires something that is
(much) less readily available: libhugetlbfs. And so the build will
typically fail for many developers.
A simple attempt to install libhugetlbfs will also run into complications
on some common distros these days: Fedora and Arch Linux (yes, Arch AUR
has it, but that's fragile, as always with AUR). The library is not
maintained actively enough at the moment, for distros to deal with it. I
had to build it from source, for Fedora, and that didn't go too smoothly
either.
It turns out that, out of 21 tests in hmm-tests.c, only 2 actually require
functionality from libhugetlbfs. Therefore, if libhugetlbfs is missing,
simply ifdef those two tests out and allow the developer to at least have
the other 19 tests, if they don't want to pause to work through the above
issues. Also issue a warning, so that it's clear that there is an
imperfection in the build.
In order to do that, a tiny shell script (check_config.sh) runs a quick
compile (not link, that's too prone to false failures with library paths),
and basically, if the compiler doesn't find hugetlbfs.h in its standard
locations, then the script concludes that libhugetlbfs is not available.
The output is in two files, one for inclusion in hmm-test.c
(local_config.h), and one for inclusion in the Makefile (local_config.mk).
Link: https://lkml.kernel.org/r/20201026064021.3545418-9-jhubbard@nvidia.com
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
Cc: Ralph Campbell <rcampbell@nvidia.com>
Cc: Jérôme Glisse <jglisse@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2020-12-15 06:05:28 +03:00
# ifdef LOCAL_CONFIG_HAVE_LIBHUGETLBFS
2020-07-02 01:53:52 +03:00
/*
* Test the hmm_range_fault ( ) HMM_PFN_PMD flag for large pages that
* should be mapped by a large page table entry .
*/
TEST_F ( hmm , compound )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
int * ptr ;
unsigned char * m ;
int ret ;
long pagesizes [ 4 ] ;
int n , idx ;
unsigned long i ;
/* Skip test if we can't allocate a hugetlbfs page. */
n = gethugepagesizes ( pagesizes , 4 ) ;
if ( n < = 0 )
return ;
for ( idx = 0 ; - - n > 0 ; ) {
if ( pagesizes [ n ] < pagesizes [ idx ] )
idx = n ;
}
size = ALIGN ( TWOMEG , pagesizes [ idx ] ) ;
npages = size > > self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > ptr = get_hugepage_region ( size , GHR_STRICT ) ;
if ( buffer - > ptr = = NULL ) {
free ( buffer ) ;
return ;
}
buffer - > size = size ;
buffer - > mirror = malloc ( npages ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
/* Initialize the pages the device will snapshot in buffer->ptr. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Simulate a device snapshotting CPU pagetables. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_SNAPSHOT , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
/* Check what the device saw. */
m = buffer - > mirror ;
for ( i = 0 ; i < npages ; + + i )
ASSERT_EQ ( m [ i ] , HMM_DMIRROR_PROT_WRITE |
HMM_DMIRROR_PROT_PMD ) ;
/* Make the region read-only. */
ret = mprotect ( buffer - > ptr , size , PROT_READ ) ;
ASSERT_EQ ( ret , 0 ) ;
/* Simulate a device snapshotting CPU pagetables. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_SNAPSHOT , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
/* Check what the device saw. */
m = buffer - > mirror ;
for ( i = 0 ; i < npages ; + + i )
ASSERT_EQ ( m [ i ] , HMM_DMIRROR_PROT_READ |
HMM_DMIRROR_PROT_PMD ) ;
free_hugepage_region ( buffer - > ptr ) ;
buffer - > ptr = NULL ;
hmm_buffer_free ( buffer ) ;
}
selftests/vm: hmm-tests: remove the libhugetlbfs dependency
HMM selftests are incredibly useful, but they are only effective if people
actually build and run them. All the other tests in selftests/vm can be
built with very standard, always-available libraries: libpthread, librt.
The hmm-tests.c program, on the other hand, requires something that is
(much) less readily available: libhugetlbfs. And so the build will
typically fail for many developers.
A simple attempt to install libhugetlbfs will also run into complications
on some common distros these days: Fedora and Arch Linux (yes, Arch AUR
has it, but that's fragile, as always with AUR). The library is not
maintained actively enough at the moment, for distros to deal with it. I
had to build it from source, for Fedora, and that didn't go too smoothly
either.
It turns out that, out of 21 tests in hmm-tests.c, only 2 actually require
functionality from libhugetlbfs. Therefore, if libhugetlbfs is missing,
simply ifdef those two tests out and allow the developer to at least have
the other 19 tests, if they don't want to pause to work through the above
issues. Also issue a warning, so that it's clear that there is an
imperfection in the build.
In order to do that, a tiny shell script (check_config.sh) runs a quick
compile (not link, that's too prone to false failures with library paths),
and basically, if the compiler doesn't find hugetlbfs.h in its standard
locations, then the script concludes that libhugetlbfs is not available.
The output is in two files, one for inclusion in hmm-test.c
(local_config.h), and one for inclusion in the Makefile (local_config.mk).
Link: https://lkml.kernel.org/r/20201026064021.3545418-9-jhubbard@nvidia.com
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
Cc: Ralph Campbell <rcampbell@nvidia.com>
Cc: Jérôme Glisse <jglisse@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2020-12-15 06:05:28 +03:00
# endif /* LOCAL_CONFIG_HAVE_LIBHUGETLBFS */
2020-07-02 01:53:52 +03:00
2020-04-22 22:50:27 +03:00
/*
* Test two devices reading the same memory ( double mapped ) .
*/
TEST_F ( hmm2 , double_map )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
int * ptr ;
int ret ;
npages = 6 ;
size = npages < < self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( npages ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
/* Reserve a range of addresses. */
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Initialize buffer in system memory. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Make region read-only. */
ret = mprotect ( buffer - > ptr , size , PROT_READ ) ;
ASSERT_EQ ( ret , 0 ) ;
/* Simulate device 0 reading system memory. */
ret = hmm_dmirror_cmd ( self - > fd0 , HMM_DMIRROR_READ , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
ASSERT_EQ ( buffer - > faults , 1 ) ;
/* Check what the device read. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
/* Simulate device 1 reading system memory. */
ret = hmm_dmirror_cmd ( self - > fd1 , HMM_DMIRROR_READ , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
ASSERT_EQ ( buffer - > faults , 1 ) ;
/* Check what the device read. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
/* Punch a hole after the first page address. */
ret = munmap ( buffer - > ptr + self - > page_size , self - > page_size ) ;
ASSERT_EQ ( ret , 0 ) ;
hmm_buffer_free ( buffer ) ;
}
2021-07-01 04:54:28 +03:00
/*
* Basic check of exclusive faulting .
*/
TEST_F ( hmm , exclusive )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
int * ptr ;
int ret ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Initialize buffer in system memory. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Map memory exclusively for device access. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_EXCLUSIVE , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
/* Check what the device read. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
/* Fault pages back to system memory and check them. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] + + , i ) ;
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i + 1 ) ;
/* Check atomic access revoked */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_CHECK_EXCLUSIVE , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
hmm_buffer_free ( buffer ) ;
}
TEST_F ( hmm , exclusive_mprotect )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
int * ptr ;
int ret ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Initialize buffer in system memory. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Map memory exclusively for device access. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_EXCLUSIVE , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
/* Check what the device read. */
for ( i = 0 , ptr = buffer - > mirror ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i ) ;
ret = mprotect ( buffer - > ptr , size , PROT_READ ) ;
ASSERT_EQ ( ret , 0 ) ;
/* Simulate a device writing system memory. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_WRITE , buffer , npages ) ;
ASSERT_EQ ( ret , - EPERM ) ;
hmm_buffer_free ( buffer ) ;
}
/*
* Check copy - on - write works .
*/
TEST_F ( hmm , exclusive_cow )
{
struct hmm_buffer * buffer ;
unsigned long npages ;
unsigned long size ;
unsigned long i ;
int * ptr ;
int ret ;
npages = ALIGN ( HMM_BUFFER_SIZE , self - > page_size ) > > self - > page_shift ;
ASSERT_NE ( npages , 0 ) ;
size = npages < < self - > page_shift ;
buffer = malloc ( sizeof ( * buffer ) ) ;
ASSERT_NE ( buffer , NULL ) ;
buffer - > fd = - 1 ;
buffer - > size = size ;
buffer - > mirror = malloc ( size ) ;
ASSERT_NE ( buffer - > mirror , NULL ) ;
buffer - > ptr = mmap ( NULL , size ,
PROT_READ | PROT_WRITE ,
MAP_PRIVATE | MAP_ANONYMOUS ,
buffer - > fd , 0 ) ;
ASSERT_NE ( buffer - > ptr , MAP_FAILED ) ;
/* Initialize buffer in system memory. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ptr [ i ] = i ;
/* Map memory exclusively for device access. */
ret = hmm_dmirror_cmd ( self - > fd , HMM_DMIRROR_EXCLUSIVE , buffer , npages ) ;
ASSERT_EQ ( ret , 0 ) ;
ASSERT_EQ ( buffer - > cpages , npages ) ;
fork ( ) ;
/* Fault pages back to system memory and check them. */
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] + + , i ) ;
for ( i = 0 , ptr = buffer - > ptr ; i < size / sizeof ( * ptr ) ; + + i )
ASSERT_EQ ( ptr [ i ] , i + 1 ) ;
hmm_buffer_free ( buffer ) ;
}
2020-04-22 22:50:27 +03:00
TEST_HARNESS_MAIN