|
@@ -42,6 +42,7 @@
|
|
#include "qemu/cutils.h"
|
|
#include "qemu/cutils.h"
|
|
#include "qemu/units.h"
|
|
#include "qemu/units.h"
|
|
#include "qemu/thread-context.h"
|
|
#include "qemu/thread-context.h"
|
|
|
|
+#include "qemu/main-loop.h"
|
|
|
|
|
|
#ifdef CONFIG_LINUX
|
|
#ifdef CONFIG_LINUX
|
|
#include <sys/syscall.h>
|
|
#include <sys/syscall.h>
|
|
@@ -63,11 +64,15 @@
|
|
|
|
|
|
struct MemsetThread;
|
|
struct MemsetThread;
|
|
|
|
|
|
|
|
+static QLIST_HEAD(, MemsetContext) memset_contexts =
|
|
|
|
+ QLIST_HEAD_INITIALIZER(memset_contexts);
|
|
|
|
+
|
|
typedef struct MemsetContext {
|
|
typedef struct MemsetContext {
|
|
bool all_threads_created;
|
|
bool all_threads_created;
|
|
bool any_thread_failed;
|
|
bool any_thread_failed;
|
|
struct MemsetThread *threads;
|
|
struct MemsetThread *threads;
|
|
int num_threads;
|
|
int num_threads;
|
|
|
|
+ QLIST_ENTRY(MemsetContext) next;
|
|
} MemsetContext;
|
|
} MemsetContext;
|
|
|
|
|
|
struct MemsetThread {
|
|
struct MemsetThread {
|
|
@@ -412,19 +417,44 @@ static inline int get_memset_num_threads(size_t hpagesize, size_t numpages,
|
|
return ret;
|
|
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,
|
|
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)
|
|
bool use_madv_populate_write)
|
|
{
|
|
{
|
|
static gsize initialized = 0;
|
|
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;
|
|
size_t numpages_per_thread, leftover;
|
|
void *(*touch_fn)(void *);
|
|
void *(*touch_fn)(void *);
|
|
- int ret = 0, i = 0;
|
|
|
|
|
|
+ int ret, i = 0;
|
|
char *addr = area;
|
|
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)) {
|
|
if (g_once_init_enter(&initialized)) {
|
|
qemu_mutex_init(&page_mutex);
|
|
qemu_mutex_init(&page_mutex);
|
|
qemu_cond_init(&page_cond);
|
|
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) {
|
|
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,
|
|
if (qemu_madvise(area, hpagesize * numpages,
|
|
QEMU_MADV_POPULATE_WRITE)) {
|
|
QEMU_MADV_POPULATE_WRITE)) {
|
|
return -errno;
|
|
return -errno;
|
|
@@ -445,50 +478,86 @@ static int touch_all_pages(char *area, size_t hpagesize, size_t numpages,
|
|
touch_fn = do_touch_pages;
|
|
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) {
|
|
if (tc) {
|
|
- thread_context_create_thread(tc, &context.threads[i].pgthread,
|
|
|
|
|
|
+ thread_context_create_thread(tc, &context->threads[i].pgthread,
|
|
"touch_pages",
|
|
"touch_pages",
|
|
- touch_fn, &context.threads[i],
|
|
|
|
|
|
+ touch_fn, &context->threads[i],
|
|
QEMU_THREAD_JOINABLE);
|
|
QEMU_THREAD_JOINABLE);
|
|
} else {
|
|
} 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);
|
|
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) {
|
|
if (!use_madv_populate_write) {
|
|
- sigbus_memset_context = &context;
|
|
|
|
|
|
+ sigbus_memset_context = context;
|
|
}
|
|
}
|
|
|
|
|
|
qemu_mutex_lock(&page_mutex);
|
|
qemu_mutex_lock(&page_mutex);
|
|
- context.all_threads_created = true;
|
|
|
|
|
|
+ context->all_threads_created = true;
|
|
qemu_cond_broadcast(&page_cond);
|
|
qemu_cond_broadcast(&page_cond);
|
|
qemu_mutex_unlock(&page_mutex);
|
|
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) {
|
|
if (tmp) {
|
|
ret = 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)
|
|
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,
|
|
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;
|
|
static gsize initialized;
|
|
int ret;
|
|
int ret;
|
|
@@ -540,7 +609,7 @@ bool qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads,
|
|
}
|
|
}
|
|
|
|
|
|
/* touch pages simultaneously */
|
|
/* 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);
|
|
use_madv_populate_write);
|
|
if (ret) {
|
|
if (ret) {
|
|
error_setg_errno(errp, -ret,
|
|
error_setg_errno(errp, -ret,
|