@ -42,6 +42,7 @@
# include "qemu/cutils.h"
# include "qemu/units.h"
# include "qemu/thread-context.h"
# include "qemu/main-loop.h"
# ifdef CONFIG_LINUX
# include <sys/syscall.h>
@ -63,11 +64,15 @@
struct MemsetThread ;
static QLIST_HEAD ( , MemsetContext ) memset_contexts =
QLIST_HEAD_INITIALIZER ( memset_contexts ) ;
typedef struct MemsetContext {
bool all_threads_created ;
bool any_thread_failed ;
struct MemsetThread * threads ;
int num_threads ;
QLIST_ENTRY ( MemsetContext ) next ;
} MemsetContext ;
struct MemsetThread {
@ -412,19 +417,44 @@ static inline int get_memset_num_threads(size_t hpagesize, size_t numpages,
return ret ;
}
static int wait_and_free_mem_prealloc_context ( MemsetContext * context )
{
int i , ret = 0 , tmp ;
for ( i = 0 ; i < context - > num_threads ; i + + ) {
tmp = ( uintptr_t ) qemu_thread_join ( & context - > threads [ i ] . pgthread ) ;
if ( tmp ) {
ret = tmp ;
}
}
g_free ( context - > threads ) ;
g_free ( context ) ;
return ret ;
}
static int touch_all_pages ( char * area , size_t hpagesize , size_t numpages ,
int max_threads , ThreadContext * tc ,
int max_threads , ThreadContext * tc , bool async ,
bool use_madv_populate_write )
{
static gsize initialized = 0 ;
MemsetContext context = {
. num_threads = get_memset_num_threads ( hpagesize , numpages , max_threads ) ,
} ;
MemsetContext * context = g_malloc0 ( sizeof ( MemsetContext ) ) ;
size_t numpages_per_thread , leftover ;
void * ( * touch_fn ) ( void * ) ;
int ret = 0 , i = 0 ;
int ret , i = 0 ;
char * addr = area ;
/*
* Asynchronous preallocation is only allowed when using MADV_POPULATE_WRITE
* and prealloc context for thread placement .
*/
if ( ! use_madv_populate_write | | ! tc ) {
async = false ;
}
context - > num_threads =
get_memset_num_threads ( hpagesize , numpages , max_threads ) ;
if ( g_once_init_enter ( & initialized ) ) {
qemu_mutex_init ( & page_mutex ) ;
qemu_cond_init ( & page_cond ) ;
@ -432,8 +462,11 @@ static int touch_all_pages(char *area, size_t hpagesize, size_t numpages,
}
if ( use_madv_populate_write ) {
/* Avoid creating a single thread for MADV_POPULATE_WRITE */
if ( context . num_threads = = 1 ) {
/*
* Avoid creating a single thread for MADV_POPULATE_WRITE when
* preallocating synchronously .
*/
if ( context - > num_threads = = 1 & & ! async ) {
if ( qemu_madvise ( area , hpagesize * numpages ,
QEMU_MADV_POPULATE_WRITE ) ) {
return - errno ;
@ -445,50 +478,86 @@ static int touch_all_pages(char *area, size_t hpagesize, size_t numpages,
touch_fn = do_touch_pages ;
}
context . threads = g_new0 ( MemsetThread , context . num_threads ) ;
numpages_per_thread = numpages / context . num_threads ;
leftover = numpages % context . num_threads ;
for ( i = 0 ; i < context . num_threads ; i + + ) {
context . threads [ i ] . addr = addr ;
context . threads [ i ] . numpages = numpages_per_thread + ( i < leftover ) ;
context . threads [ i ] . hpagesize = hpagesize ;
context . threads [ i ] . context = & context ;
context - > threads = g_new0 ( MemsetThread , context - > num_threads ) ;
numpages_per_thread = numpages / context - > num_threads ;
leftover = numpages % context - > num_threads ;
for ( i = 0 ; i < context - > num_threads ; i + + ) {
context - > threads [ i ] . addr = addr ;
context - > threads [ i ] . numpages = numpages_per_thread + ( i < leftover ) ;
context - > threads [ i ] . hpagesize = hpagesize ;
context - > threads [ i ] . context = context ;
if ( tc ) {
thread_context_create_thread ( tc , & context . threads [ i ] . pgthread ,
thread_context_create_thread ( tc , & context - > threads [ i ] . pgthread ,
" touch_pages " ,
touch_fn , & context . threads [ i ] ,
touch_fn , & context - > threads [ i ] ,
QEMU_THREAD_JOINABLE ) ;
} else {
qemu_thread_create ( & context . threads [ i ] . pgthread , " touch_pages " ,
touch_fn , & context . threads [ i ] ,
qemu_thread_create ( & context - > threads [ i ] . pgthread , " touch_pages " ,
touch_fn , & context - > threads [ i ] ,
QEMU_THREAD_JOINABLE ) ;
}
addr + = context . threads [ i ] . numpages * hpagesize ;
addr + = context - > threads [ i ] . numpages * hpagesize ;
}
if ( async ) {
/*
* async requests currently require the BQL . Add it to the list and kick
* preallocation off during qemu_finish_async_prealloc_mem ( ) .
*/
assert ( bql_locked ( ) ) ;
QLIST_INSERT_HEAD ( & memset_contexts , context , next ) ;
return 0 ;
}
if ( ! use_madv_populate_write ) {
sigbus_memset_context = & context ;
sigbus_memset_context = context ;
}
qemu_mutex_lock ( & page_mutex ) ;
context . all_threads_created = true ;
context - > all_threads_created = true ;
qemu_cond_broadcast ( & page_cond ) ;
qemu_mutex_unlock ( & page_mutex ) ;
for ( i = 0 ; i < context . num_threads ; i + + ) {
int tmp = ( uintptr_t ) qemu_thread_join ( & context . threads [ i ] . pgthread ) ;
ret = wait_and_free_mem_prealloc_context ( context ) ;
if ( ! use_madv_populate_write ) {
sigbus_memset_context = NULL ;
}
return ret ;
}
bool qemu_finish_async_prealloc_mem ( Error * * errp )
{
int ret = 0 , tmp ;
MemsetContext * context , * next_context ;
/* Waiting for preallocation requires the BQL. */
assert ( bql_locked ( ) ) ;
if ( QLIST_EMPTY ( & memset_contexts ) ) {
return true ;
}
qemu_mutex_lock ( & page_mutex ) ;
QLIST_FOREACH ( context , & memset_contexts , next ) {
context - > all_threads_created = true ;
}
qemu_cond_broadcast ( & page_cond ) ;
qemu_mutex_unlock ( & page_mutex ) ;
QLIST_FOREACH_SAFE ( context , & memset_contexts , next , next_context ) {
QLIST_REMOVE ( context , next ) ;
tmp = wait_and_free_mem_prealloc_context ( context ) ;
if ( tmp ) {
ret = tmp ;
}
}
if ( ! use_madv_populate_write ) {
sigbus_memset_context = NULL ;
if ( ret ) {
error_setg_errno ( errp , - ret ,
" qemu_prealloc_mem: preallocating memory failed " ) ;
return false ;
}
g_free ( context . threads ) ;
return ret ;
return true ;
}
static bool madv_populate_write_possible ( char * area , size_t pagesize )
@ -498,7 +567,7 @@ static bool madv_populate_write_possible(char *area, size_t pagesize)
}
bool qemu_prealloc_mem ( int fd , char * area , size_t sz , int max_threads ,
ThreadContext * tc , Error * * errp )
ThreadContext * tc , bool async , Error * * errp )
{
static gsize initialized ;
int ret ;
@ -540,7 +609,7 @@ bool qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads,
}
/* touch pages simultaneously */
ret = touch_all_pages ( area , hpagesize , numpages , max_threads , tc ,
ret = touch_all_pages ( area , hpagesize , numpages , max_threads , tc , async ,
use_madv_populate_write ) ;
if ( ret ) {
error_setg_errno ( errp , - ret ,