2012-12-12 04:01:36 +04:00
/* Test selecting other page sizes for mmap/shmget.
Before running this huge pages for each huge page size must have been
reserved .
For large pages beyond MAX_ORDER ( like 1 GB on x86 ) boot options must be used .
Also shmmax must be increased .
And you need to run as root to work around some weird permissions in shm .
And nothing using huge pages should run in parallel .
When the program aborts you may need to clean up the shm segments with
ipcrm - m by hand , like this
sudo ipcs | awk ' $ 1 = = " 0x00000000 " { print $ 2 } ' | xargs - n1 sudo ipcrm - m
( warning this will remove all if someone else uses them ) */
# define _GNU_SOURCE 1
# include <sys/mman.h>
# include <stdlib.h>
# include <stdio.h>
# include <sys/ipc.h>
# include <sys/shm.h>
# include <sys/stat.h>
# include <glob.h>
# include <assert.h>
# include <unistd.h>
# include <stdarg.h>
# include <string.h>
# define err(x) perror(x), exit(1)
# define MAP_HUGE_2MB (21 << MAP_HUGE_SHIFT)
# define MAP_HUGE_1GB (30 << MAP_HUGE_SHIFT)
# define MAP_HUGE_SHIFT 26
# define MAP_HUGE_MASK 0x3f
2016-03-31 14:11:05 +03:00
# if !defined(MAP_HUGETLB)
2012-12-12 04:01:36 +04:00
# define MAP_HUGETLB 0x40000
2016-03-31 14:11:05 +03:00
# endif
2012-12-12 04:01:36 +04:00
# define SHM_HUGETLB 04000 /* segment will use huge TLB pages */
# define SHM_HUGE_SHIFT 26
# define SHM_HUGE_MASK 0x3f
# define SHM_HUGE_2MB (21 << SHM_HUGE_SHIFT)
# define SHM_HUGE_1GB (30 << SHM_HUGE_SHIFT)
# define NUM_PAGESIZES 5
# define NUM_PAGES 4
# define Dprintf(fmt...) // printf(fmt)
unsigned long page_sizes [ NUM_PAGESIZES ] ;
int num_page_sizes ;
int ilog2 ( unsigned long v )
{
int l = 0 ;
while ( ( 1UL < < l ) < v )
l + + ;
return l ;
}
void find_pagesizes ( void )
{
glob_t g ;
int i ;
glob ( " /sys/kernel/mm/hugepages/hugepages-*kB " , 0 , NULL , & g ) ;
assert ( g . gl_pathc < = NUM_PAGESIZES ) ;
for ( i = 0 ; i < g . gl_pathc ; i + + ) {
sscanf ( g . gl_pathv [ i ] , " /sys/kernel/mm/hugepages/hugepages-%lukB " ,
& page_sizes [ i ] ) ;
page_sizes [ i ] < < = 10 ;
printf ( " Found %luMB \n " , page_sizes [ i ] > > 20 ) ;
}
num_page_sizes = g . gl_pathc ;
globfree ( & g ) ;
}
unsigned long default_huge_page_size ( void )
{
unsigned long hps = 0 ;
char * line = NULL ;
size_t linelen = 0 ;
FILE * f = fopen ( " /proc/meminfo " , " r " ) ;
if ( ! f )
return 0 ;
while ( getline ( & line , & linelen , f ) > 0 ) {
if ( sscanf ( line , " Hugepagesize: %lu kB " , & hps ) = = 1 ) {
hps < < = 10 ;
break ;
}
}
free ( line ) ;
return hps ;
}
void show ( unsigned long ps )
{
char buf [ 100 ] ;
if ( ps = = getpagesize ( ) )
return ;
printf ( " %luMB: " , ps > > 20 ) ;
fflush ( stdout ) ;
snprintf ( buf , sizeof buf ,
" cat /sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages " ,
ps > > 10 ) ;
system ( buf ) ;
}
unsigned long read_sysfs ( int warn , char * fmt , . . . )
{
char * line = NULL ;
size_t linelen = 0 ;
char buf [ 100 ] ;
FILE * f ;
va_list ap ;
unsigned long val = 0 ;
va_start ( ap , fmt ) ;
vsnprintf ( buf , sizeof buf , fmt , ap ) ;
va_end ( ap ) ;
f = fopen ( buf , " r " ) ;
if ( ! f ) {
if ( warn )
printf ( " missing %s \n " , buf ) ;
return 0 ;
}
if ( getline ( & line , & linelen , f ) > 0 ) {
sscanf ( line , " %lu " , & val ) ;
}
fclose ( f ) ;
free ( line ) ;
return val ;
}
unsigned long read_free ( unsigned long ps )
{
return read_sysfs ( ps ! = getpagesize ( ) ,
" /sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages " ,
ps > > 10 ) ;
}
void test_mmap ( unsigned long size , unsigned flags )
{
char * map ;
unsigned long before , after ;
int err ;
before = read_free ( size ) ;
map = mmap ( NULL , size * NUM_PAGES , PROT_READ | PROT_WRITE ,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | flags , 0 , 0 ) ;
if ( map = = ( char * ) - 1 ) err ( " mmap " ) ;
memset ( map , 0xff , size * NUM_PAGES ) ;
after = read_free ( size ) ;
Dprintf ( " before %lu after %lu diff %ld size %lu \n " ,
before , after , before - after , size ) ;
assert ( size = = getpagesize ( ) | | ( before - after ) = = NUM_PAGES ) ;
show ( size ) ;
err = munmap ( map , size ) ;
assert ( ! err ) ;
}
void test_shmget ( unsigned long size , unsigned flags )
{
int id ;
unsigned long before , after ;
int err ;
before = read_free ( size ) ;
id = shmget ( IPC_PRIVATE , size * NUM_PAGES , IPC_CREAT | 0600 | flags ) ;
if ( id < 0 ) err ( " shmget " ) ;
struct shm_info i ;
if ( shmctl ( id , SHM_INFO , ( void * ) & i ) < 0 ) err ( " shmctl " ) ;
Dprintf ( " alloc %lu res %lu \n " , i . shm_tot , i . shm_rss ) ;
Dprintf ( " id %d \n " , id ) ;
char * map = shmat ( id , NULL , 0600 ) ;
if ( map = = ( char * ) - 1 ) err ( " shmat " ) ;
shmctl ( id , IPC_RMID , NULL ) ;
memset ( map , 0xff , size * NUM_PAGES ) ;
after = read_free ( size ) ;
Dprintf ( " before %lu after %lu diff %ld size %lu \n " ,
before , after , before - after , size ) ;
assert ( size = = getpagesize ( ) | | ( before - after ) = = NUM_PAGES ) ;
show ( size ) ;
err = shmdt ( map ) ;
assert ( ! err ) ;
}
void sanity_checks ( void )
{
int i ;
unsigned long largest = getpagesize ( ) ;
for ( i = 0 ; i < num_page_sizes ; i + + ) {
if ( page_sizes [ i ] > largest )
largest = page_sizes [ i ] ;
if ( read_free ( page_sizes [ i ] ) < NUM_PAGES ) {
printf ( " Not enough huge pages for page size %lu MB, need %u \n " ,
page_sizes [ i ] > > 20 ,
NUM_PAGES ) ;
exit ( 0 ) ;
}
}
if ( read_sysfs ( 0 , " /proc/sys/kernel/shmmax " ) < NUM_PAGES * largest ) {
printf ( " Please do echo %lu > /proc/sys/kernel/shmmax " , largest * NUM_PAGES ) ;
exit ( 0 ) ;
}
# if defined(__x86_64__)
if ( largest ! = 1U < < 30 ) {
printf ( " No GB pages available on x86-64 \n "
" Please boot with hugepagesz=1G hugepages=%d \n " , NUM_PAGES ) ;
exit ( 0 ) ;
}
# endif
}
int main ( void )
{
int i ;
unsigned default_hps = default_huge_page_size ( ) ;
find_pagesizes ( ) ;
sanity_checks ( ) ;
for ( i = 0 ; i < num_page_sizes ; i + + ) {
unsigned long ps = page_sizes [ i ] ;
int arg = ilog2 ( ps ) < < MAP_HUGE_SHIFT ;
printf ( " Testing %luMB mmap with shift %x \n " , ps > > 20 , arg ) ;
test_mmap ( ps , MAP_HUGETLB | arg ) ;
}
printf ( " Testing default huge mmap \n " ) ;
test_mmap ( default_hps , SHM_HUGETLB ) ;
puts ( " Testing non-huge shmget " ) ;
test_shmget ( getpagesize ( ) , 0 ) ;
for ( i = 0 ; i < num_page_sizes ; i + + ) {
unsigned long ps = page_sizes [ i ] ;
int arg = ilog2 ( ps ) < < SHM_HUGE_SHIFT ;
printf ( " Testing %luMB shmget with shift %x \n " , ps > > 20 , arg ) ;
test_shmget ( ps , SHM_HUGETLB | arg ) ;
}
puts ( " default huge shmget " ) ;
test_shmget ( default_hps , SHM_HUGETLB ) ;
return 0 ;
}