summaryrefslogtreecommitdiffstats
path: root/util
diff options
context:
space:
mode:
Diffstat (limited to 'util')
-rw-r--r--util/Makefile.objs1
-rw-r--r--util/aio-posix.c2
-rw-r--r--util/atomic64.c83
-rw-r--r--util/cacheinfo.c11
-rw-r--r--util/cutils.c5
-rw-r--r--util/error.c13
-rw-r--r--util/hbitmap.c14
-rw-r--r--util/memfd.c35
-rw-r--r--util/oslib-posix.c73
-rw-r--r--util/oslib-win32.c27
-rw-r--r--util/qemu-error.c5
-rw-r--r--util/qemu-option.c71
-rw-r--r--util/qemu-timer.c63
-rw-r--r--util/qsp.c49
14 files changed, 349 insertions, 103 deletions
diff --git a/util/Makefile.objs b/util/Makefile.objs
index 0e88899011..0820923c18 100644
--- a/util/Makefile.objs
+++ b/util/Makefile.objs
@@ -3,6 +3,7 @@ util-obj-y += bufferiszero.o
util-obj-y += lockcnt.o
util-obj-y += aiocb.o async.o aio-wait.o thread-pool.o qemu-timer.o
util-obj-y += main-loop.o iohandler.o
+util-obj-$(call lnot,$(CONFIG_ATOMIC64)) += atomic64.o
util-obj-$(CONFIG_POSIX) += aio-posix.o
util-obj-$(CONFIG_POSIX) += compatfd.o
util-obj-$(CONFIG_POSIX) += event_notifier-posix.o
diff --git a/util/aio-posix.c b/util/aio-posix.c
index 621b3025d8..51c41ed3c9 100644
--- a/util/aio-posix.c
+++ b/util/aio-posix.c
@@ -40,7 +40,7 @@ struct AioHandler
#ifdef CONFIG_EPOLL_CREATE1
-/* The fd number threashold to switch to epoll */
+/* The fd number threshold to switch to epoll */
#define EPOLL_ENABLE_THRESHOLD 64
static void aio_epoll_disable(AioContext *ctx)
diff --git a/util/atomic64.c b/util/atomic64.c
new file mode 100644
index 0000000000..b198a6c9c8
--- /dev/null
+++ b/util/atomic64.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
+ *
+ * License: GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "qemu/atomic.h"
+#include "qemu/thread.h"
+
+#ifdef CONFIG_ATOMIC64
+#error This file must only be compiled if !CONFIG_ATOMIC64
+#endif
+
+/*
+ * When !CONFIG_ATOMIC64, we serialize both reads and writes with spinlocks.
+ * We use an array of spinlocks, with padding computed at run-time based on
+ * the host's dcache line size.
+ * We point to the array with a void * to simplify the padding's computation.
+ * Each spinlock is located every lock_size bytes.
+ */
+static void *lock_array;
+static size_t lock_size;
+
+/*
+ * Systems without CONFIG_ATOMIC64 are unlikely to have many cores, so we use a
+ * small array of locks.
+ */
+#define NR_LOCKS 16
+
+static QemuSpin *addr_to_lock(const void *addr)
+{
+ uintptr_t a = (uintptr_t)addr;
+ uintptr_t idx;
+
+ idx = a >> qemu_dcache_linesize_log;
+ idx ^= (idx >> 8) ^ (idx >> 16);
+ idx &= NR_LOCKS - 1;
+ return lock_array + idx * lock_size;
+}
+
+#define GEN_READ(name, type) \
+ type name(const type *ptr) \
+ { \
+ QemuSpin *lock = addr_to_lock(ptr); \
+ type ret; \
+ \
+ qemu_spin_lock(lock); \
+ ret = *ptr; \
+ qemu_spin_unlock(lock); \
+ return ret; \
+ }
+
+GEN_READ(atomic_read_i64, int64_t)
+GEN_READ(atomic_read_u64, uint64_t)
+#undef GEN_READ
+
+#define GEN_SET(name, type) \
+ void name(type *ptr, type val) \
+ { \
+ QemuSpin *lock = addr_to_lock(ptr); \
+ \
+ qemu_spin_lock(lock); \
+ *ptr = val; \
+ qemu_spin_unlock(lock); \
+ }
+
+GEN_SET(atomic_set_i64, int64_t)
+GEN_SET(atomic_set_u64, uint64_t)
+#undef GEN_SET
+
+void atomic64_init(void)
+{
+ int i;
+
+ lock_size = ROUND_UP(sizeof(QemuSpin), qemu_dcache_linesize);
+ lock_array = qemu_memalign(qemu_dcache_linesize, lock_size * NR_LOCKS);
+ for (i = 0; i < NR_LOCKS; i++) {
+ QemuSpin *lock = lock_array + i * lock_size;
+
+ qemu_spin_init(lock);
+ }
+}
diff --git a/util/cacheinfo.c b/util/cacheinfo.c
index db5172d07c..3cd080b83d 100644
--- a/util/cacheinfo.c
+++ b/util/cacheinfo.c
@@ -7,9 +7,13 @@
*/
#include "qemu/osdep.h"
+#include "qemu/host-utils.h"
+#include "qemu/atomic.h"
int qemu_icache_linesize = 0;
+int qemu_icache_linesize_log;
int qemu_dcache_linesize = 0;
+int qemu_dcache_linesize_log;
/*
* Operating system specific detection mechanisms.
@@ -172,6 +176,13 @@ static void __attribute__((constructor)) init_cache_info(void)
arch_cache_info(&isize, &dsize);
fallback_cache_info(&isize, &dsize);
+ assert((isize & (isize - 1)) == 0);
+ assert((dsize & (dsize - 1)) == 0);
+
qemu_icache_linesize = isize;
+ qemu_icache_linesize_log = ctz32(isize);
qemu_dcache_linesize = dsize;
+ qemu_dcache_linesize_log = ctz32(dsize);
+
+ atomic64_init();
}
diff --git a/util/cutils.c b/util/cutils.c
index 9205e09031..698bd315bd 100644
--- a/util/cutils.c
+++ b/util/cutils.c
@@ -769,3 +769,8 @@ char *size_to_str(uint64_t val)
return g_strdup_printf("%0.3g %sB", (double)val / div, suffixes[i]);
}
+
+int qemu_pstrcmp0(const char **str1, const char **str2)
+{
+ return g_strcmp0(*str1, *str2);
+}
diff --git a/util/error.c b/util/error.c
index 3efdd69162..b5ccbd8eac 100644
--- a/util/error.c
+++ b/util/error.c
@@ -292,3 +292,16 @@ void error_propagate(Error **dst_errp, Error *local_err)
error_free(local_err);
}
}
+
+void error_propagate_prepend(Error **dst_errp, Error *err,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ if (dst_errp && !*dst_errp) {
+ va_start(ap, fmt);
+ error_vprepend(&err, fmt, ap);
+ va_end(ap);
+ } /* else error is being ignored, don't bother with prepending */
+ error_propagate(dst_errp, err);
+}
diff --git a/util/hbitmap.c b/util/hbitmap.c
index bcd304041a..8d402c59d9 100644
--- a/util/hbitmap.c
+++ b/util/hbitmap.c
@@ -723,6 +723,10 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size)
}
}
+bool hbitmap_can_merge(const HBitmap *a, const HBitmap *b)
+{
+ return (a->size == b->size) && (a->granularity == b->granularity);
+}
/**
* Given HBitmaps A and B, let A := A (BITOR) B.
@@ -731,14 +735,15 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size)
* @return true if the merge was successful,
* false if it was not attempted.
*/
-bool hbitmap_merge(HBitmap *a, const HBitmap *b)
+bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result)
{
int i;
uint64_t j;
- if ((a->size != b->size) || (a->granularity != b->granularity)) {
+ if (!hbitmap_can_merge(a, b) || !hbitmap_can_merge(a, result)) {
return false;
}
+ assert(hbitmap_can_merge(b, result));
if (hbitmap_count(b) == 0) {
return true;
@@ -750,10 +755,13 @@ bool hbitmap_merge(HBitmap *a, const HBitmap *b)
*/
for (i = HBITMAP_LEVELS - 1; i >= 0; i--) {
for (j = 0; j < a->sizes[i]; j++) {
- a->levels[i][j] |= b->levels[i][j];
+ result->levels[i][j] = a->levels[i][j] | b->levels[i][j];
}
}
+ /* Recompute the dirty count */
+ result->count = hb_count_between(result, 0, result->size - 1);
+
return true;
}
diff --git a/util/memfd.c b/util/memfd.c
index 6287946b61..8debd0d037 100644
--- a/util/memfd.c
+++ b/util/memfd.c
@@ -45,22 +45,6 @@ static int memfd_create(const char *name, unsigned int flags)
}
#endif
-#ifndef MFD_CLOEXEC
-#define MFD_CLOEXEC 0x0001U
-#endif
-
-#ifndef MFD_ALLOW_SEALING
-#define MFD_ALLOW_SEALING 0x0002U
-#endif
-
-#ifndef MFD_HUGETLB
-#define MFD_HUGETLB 0x0004U
-#endif
-
-#ifndef MFD_HUGE_SHIFT
-#define MFD_HUGE_SHIFT 26
-#endif
-
int qemu_memfd_create(const char *name, size_t size, bool hugetlb,
uint64_t hugetlbsize, unsigned int seals, Error **errp)
{
@@ -201,23 +185,16 @@ bool qemu_memfd_alloc_check(void)
*
* Check if host supports memfd.
*/
-bool qemu_memfd_check(void)
+bool qemu_memfd_check(unsigned int flags)
{
#ifdef CONFIG_LINUX
- static int memfd_check = MEMFD_TODO;
+ int mfd = memfd_create("test", flags);
- if (memfd_check == MEMFD_TODO) {
- int mfd = memfd_create("test", 0);
- if (mfd >= 0) {
- memfd_check = MEMFD_OK;
- close(mfd);
- } else {
- memfd_check = MEMFD_KO;
- }
+ if (mfd >= 0) {
+ close(mfd);
+ return true;
}
+#endif
- return memfd_check == MEMFD_OK;
-#else
return false;
-#endif
}
diff --git a/util/oslib-posix.c b/util/oslib-posix.c
index 13b6f8d776..fbd0dc8c57 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -88,6 +88,79 @@ int qemu_daemon(int nochdir, int noclose)
return daemon(nochdir, noclose);
}
+bool qemu_write_pidfile(const char *path, Error **errp)
+{
+ int fd;
+ char pidstr[32];
+
+ while (1) {
+ struct stat a, b;
+ struct flock lock = {
+ .l_type = F_WRLCK,
+ .l_whence = SEEK_SET,
+ .l_len = 0,
+ };
+
+ fd = qemu_open(path, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ error_setg_errno(errp, errno, "Cannot open pid file");
+ return false;
+ }
+
+ if (fstat(fd, &b) < 0) {
+ error_setg_errno(errp, errno, "Cannot stat file");
+ goto fail_close;
+ }
+
+ if (fcntl(fd, F_SETLK, &lock)) {
+ error_setg_errno(errp, errno, "Cannot lock pid file");
+ goto fail_close;
+ }
+
+ /*
+ * Now make sure the path we locked is the same one that now
+ * exists on the filesystem.
+ */
+ if (stat(path, &a) < 0) {
+ /*
+ * PID file disappeared, someone else must be racing with
+ * us, so try again.
+ */
+ close(fd);
+ continue;
+ }
+
+ if (a.st_ino == b.st_ino) {
+ break;
+ }
+
+ /*
+ * PID file was recreated, someone else must be racing with
+ * us, so try again.
+ */
+ close(fd);
+ }
+
+ if (ftruncate(fd, 0) < 0) {
+ error_setg_errno(errp, errno, "Failed to truncate pid file");
+ goto fail_unlink;
+ }
+
+ snprintf(pidstr, sizeof(pidstr), FMT_pid "\n", getpid());
+ if (write(fd, pidstr, strlen(pidstr)) != strlen(pidstr)) {
+ error_setg(errp, "Failed to write pid file");
+ goto fail_unlink;
+ }
+
+ return true;
+
+fail_unlink:
+ unlink(path);
+fail_close:
+ close(fd);
+ return false;
+}
+
void *qemu_oom_check(void *ptr)
{
if (ptr == NULL) {
diff --git a/util/oslib-win32.c b/util/oslib-win32.c
index 25dd1595ad..b4c17f5dfa 100644
--- a/util/oslib-win32.c
+++ b/util/oslib-win32.c
@@ -776,3 +776,30 @@ ssize_t qemu_recvfrom_wrap(int sockfd, void *buf, size_t len, int flags,
}
return ret;
}
+
+bool qemu_write_pidfile(const char *filename, Error **errp)
+{
+ char buffer[128];
+ int len;
+ HANDLE file;
+ OVERLAPPED overlap;
+ BOOL ret;
+ memset(&overlap, 0, sizeof(overlap));
+
+ file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (file == INVALID_HANDLE_VALUE) {
+ error_setg(errp, "Failed to create PID file");
+ return false;
+ }
+ len = snprintf(buffer, sizeof(buffer), FMT_pid "\n", (pid_t)getpid());
+ ret = WriteFile(file, (LPCVOID)buffer, (DWORD)len,
+ NULL, &overlap);
+ CloseHandle(file);
+ if (ret == 0) {
+ error_setg(errp, "Failed to write PID file");
+ return false;
+ }
+ return true;
+}
diff --git a/util/qemu-error.c b/util/qemu-error.c
index 4ab428f7e4..fcbe8a1f74 100644
--- a/util/qemu-error.c
+++ b/util/qemu-error.c
@@ -194,7 +194,6 @@ bool enable_timestamp_msg;
* Format arguments like vsprintf(). The resulting message should be
* a single phrase, with no newline or trailing punctuation.
* Prepend the current location and append a newline.
- * It's wrong to call this in a QMP monitor. Use error_setg() there.
*/
static void vreport(report_type type, const char *fmt, va_list ap)
{
@@ -242,7 +241,6 @@ void error_vreport(const char *fmt, va_list ap)
* Format arguments like vsprintf(). The resulting message should be
* a single phrase, with no newline or trailing punctuation.
* Prepend the current location and append a newline.
- * It's wrong to call this in a QMP monitor. Use error_setg() there.
*/
void warn_vreport(const char *fmt, va_list ap)
{
@@ -255,7 +253,6 @@ void warn_vreport(const char *fmt, va_list ap)
* Format arguments like vsprintf(). The resulting message should be
* a single phrase, with no newline or trailing punctuation.
* Prepend the current location and append a newline.
- * It's wrong to call this in a QMP monitor. Use error_setg() there.
*/
void info_vreport(const char *fmt, va_list ap)
{
@@ -283,7 +280,6 @@ void error_report(const char *fmt, ...)
* Format arguments like sprintf(). The resulting message should be a
* single phrase, with no newline or trailing punctuation.
* Prepend the current location and append a newline.
- * It's wrong to call this in a QMP monitor. Use error_setg() there.
*/
void warn_report(const char *fmt, ...)
{
@@ -300,7 +296,6 @@ void warn_report(const char *fmt, ...)
* Format arguments like sprintf(). The resulting message should be a
* single phrase, with no newline or trailing punctuation.
* Prepend the current location and append a newline.
- * It's wrong to call this in a QMP monitor. Use error_setg() there.
*/
void info_report(const char *fmt, ...)
{
diff --git a/util/qemu-option.c b/util/qemu-option.c
index 01886efe90..9a5f263294 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -208,17 +208,51 @@ out:
return result;
}
+static const char *opt_type_to_string(enum QemuOptType type)
+{
+ switch (type) {
+ case QEMU_OPT_STRING:
+ return "str";
+ case QEMU_OPT_BOOL:
+ return "bool (on/off)";
+ case QEMU_OPT_NUMBER:
+ return "num";
+ case QEMU_OPT_SIZE:
+ return "size";
+ }
+
+ g_assert_not_reached();
+}
+
void qemu_opts_print_help(QemuOptsList *list)
{
QemuOptDesc *desc;
+ int i;
+ GPtrArray *array = g_ptr_array_new();
assert(list);
desc = list->desc;
while (desc && desc->name) {
- printf("%-16s %s\n", desc->name,
- desc->help ? desc->help : "No description available");
+ GString *str = g_string_new(NULL);
+ if (list->name) {
+ g_string_append_printf(str, "%s.", list->name);
+ }
+ g_string_append_printf(str, "%s=%s", desc->name,
+ opt_type_to_string(desc->type));
+ if (desc->help) {
+ g_string_append_printf(str, " - %s", desc->help);
+ }
+ g_ptr_array_add(array, g_string_free(str, false));
desc++;
}
+
+ g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0);
+ for (i = 0; i < array->len; i++) {
+ printf("%s\n", (char *)array->pdata[i]);
+ }
+ g_ptr_array_set_free_func(array, g_free);
+ g_ptr_array_free(array, true);
+
}
/* ------------------------------------------------------------------ */
@@ -486,7 +520,7 @@ int qemu_opt_unset(QemuOpts *opts, const char *name)
}
static void opt_set(QemuOpts *opts, const char *name, char *value,
- bool prepend, Error **errp)
+ bool prepend, bool *invalidp, Error **errp)
{
QemuOpt *opt;
const QemuOptDesc *desc;
@@ -496,6 +530,9 @@ static void opt_set(QemuOpts *opts, const char *name, char *value,
if (!desc && !opts_accepts_any(opts)) {
g_free(value);
error_setg(errp, QERR_INVALID_PARAMETER, name);
+ if (invalidp) {
+ *invalidp = true;
+ }
return;
}
@@ -519,7 +556,7 @@ static void opt_set(QemuOpts *opts, const char *name, char *value,
void qemu_opt_set(QemuOpts *opts, const char *name, const char *value,
Error **errp)
{
- opt_set(opts, name, g_strdup(value), false, errp);
+ opt_set(opts, name, g_strdup(value), false, NULL, errp);
}
void qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val,
@@ -750,7 +787,8 @@ void qemu_opts_print(QemuOpts *opts, const char *separator)
}
static void opts_do_parse(QemuOpts *opts, const char *params,
- const char *firstname, bool prepend, Error **errp)
+ const char *firstname, bool prepend,
+ bool *invalidp, Error **errp)
{
char *option = NULL;
char *value = NULL;
@@ -785,7 +823,7 @@ static void opts_do_parse(QemuOpts *opts, const char *params,
}
if (strcmp(option, "id") != 0) {
/* store and parse */
- opt_set(opts, option, value, prepend, &local_err);
+ opt_set(opts, option, value, prepend, invalidp, &local_err);
value = NULL;
if (local_err) {
error_propagate(errp, local_err);
@@ -814,11 +852,12 @@ static void opts_do_parse(QemuOpts *opts, const char *params,
void qemu_opts_do_parse(QemuOpts *opts, const char *params,
const char *firstname, Error **errp)
{
- opts_do_parse(opts, params, firstname, false, errp);
+ opts_do_parse(opts, params, firstname, false, NULL, errp);
}
static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
- bool permit_abbrev, bool defaults, Error **errp)
+ bool permit_abbrev, bool defaults,
+ bool *invalidp, Error **errp)
{
const char *firstname;
char *id = NULL;
@@ -850,7 +889,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
return NULL;
}
- opts_do_parse(opts, params, firstname, defaults, &local_err);
+ opts_do_parse(opts, params, firstname, defaults, invalidp, &local_err);
if (local_err) {
error_propagate(errp, local_err);
qemu_opts_del(opts);
@@ -870,7 +909,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params,
bool permit_abbrev, Error **errp)
{
- return opts_parse(list, params, permit_abbrev, false, errp);
+ return opts_parse(list, params, permit_abbrev, false, NULL, errp);
}
/**
@@ -886,10 +925,16 @@ QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
{
Error *err = NULL;
QemuOpts *opts;
+ bool invalidp = false;
- opts = opts_parse(list, params, permit_abbrev, false, &err);
+ opts = opts_parse(list, params, permit_abbrev, false, &invalidp, &err);
if (err) {
- error_report_err(err);
+ if (invalidp && has_help_option(params)) {
+ qemu_opts_print_help(list);
+ error_free(err);
+ } else {
+ error_report_err(err);
+ }
}
return opts;
}
@@ -899,7 +944,7 @@ void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
{
QemuOpts *opts;
- opts = opts_parse(list, params, permit_abbrev, true, NULL);
+ opts = opts_parse(list, params, permit_abbrev, true, NULL, NULL);
assert(opts);
}
diff --git a/util/qemu-timer.c b/util/qemu-timer.c
index 86bfe84037..1cc1b2f2c3 100644
--- a/util/qemu-timer.c
+++ b/util/qemu-timer.c
@@ -339,14 +339,19 @@ int qemu_poll_ns(GPollFD *fds, guint nfds, int64_t timeout)
}
-void timer_init_tl(QEMUTimer *ts,
- QEMUTimerList *timer_list, int scale,
- QEMUTimerCB *cb, void *opaque)
+void timer_init_full(QEMUTimer *ts,
+ QEMUTimerListGroup *timer_list_group, QEMUClockType type,
+ int scale, int attributes,
+ QEMUTimerCB *cb, void *opaque)
{
- ts->timer_list = timer_list;
+ if (!timer_list_group) {
+ timer_list_group = &main_loop_tlg;
+ }
+ ts->timer_list = timer_list_group->tl[type];
ts->cb = cb;
ts->opaque = opaque;
ts->scale = scale;
+ ts->attributes = attributes;
ts->expire_time = -1;
}
@@ -484,6 +489,7 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
bool progress = false;
QEMUTimerCB *cb;
void *opaque;
+ bool need_replay_checkpoint = false;
if (!atomic_read(&timer_list->active_timers)) {
return false;
@@ -499,8 +505,15 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
break;
default:
case QEMU_CLOCK_VIRTUAL:
- if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) {
- goto out;
+ if (replay_mode != REPLAY_MODE_NONE) {
+ /* Checkpoint for virtual clock is redundant in cases where
+ * it's being triggered with only non-EXTERNAL timers, because
+ * these timers don't change guest state directly.
+ * Since it has conditional dependence on specific timers, it is
+ * subject to race conditions and requires special handling.
+ * See below.
+ */
+ need_replay_checkpoint = true;
}
break;
case QEMU_CLOCK_HOST:
@@ -515,14 +528,39 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
break;
}
+ /*
+ * Extract expired timers from active timers list and and process them.
+ *
+ * In rr mode we need "filtered" checkpointing for virtual clock. The
+ * checkpoint must be recorded/replayed before processing any non-EXTERNAL timer,
+ * and that must only be done once since the clock value stays the same. Because
+ * non-EXTERNAL timers may appear in the timers list while it being processed,
+ * the checkpoint can be issued at a time until no timers are left and we are
+ * done".
+ */
current_time = qemu_clock_get_ns(timer_list->clock->type);
- for(;;) {
- qemu_mutex_lock(&timer_list->active_timers_lock);
- ts = timer_list->active_timers;
+ qemu_mutex_lock(&timer_list->active_timers_lock);
+ while ((ts = timer_list->active_timers)) {
if (!timer_expired_ns(ts, current_time)) {
- qemu_mutex_unlock(&timer_list->active_timers_lock);
+ /* No expired timers left. The checkpoint can be skipped
+ * if no timers fired or they were all external.
+ */
break;
}
+ if (need_replay_checkpoint
+ && !(ts->attributes & QEMU_TIMER_ATTR_EXTERNAL)) {
+ /* once we got here, checkpoint clock only once */
+ need_replay_checkpoint = false;
+ qemu_mutex_unlock(&timer_list->active_timers_lock);
+ if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) {
+ goto out;
+ }
+ qemu_mutex_lock(&timer_list->active_timers_lock);
+ /* The lock was released; start over again in case the list was
+ * modified.
+ */
+ continue;
+ }
/* remove timer from the list before calling the callback */
timer_list->active_timers = ts->next;
@@ -530,12 +568,15 @@ bool timerlist_run_timers(QEMUTimerList *timer_list)
ts->expire_time = -1;
cb = ts->cb;
opaque = ts->opaque;
- qemu_mutex_unlock(&timer_list->active_timers_lock);
/* run the callback (the timer list can be modified) */
+ qemu_mutex_unlock(&timer_list->active_timers_lock);
cb(opaque);
+ qemu_mutex_lock(&timer_list->active_timers_lock);
+
progress = true;
}
+ qemu_mutex_unlock(&timer_list->active_timers_lock);
out:
qemu_event_set(&timer_list->timers_done_ev);
diff --git a/util/qsp.c b/util/qsp.c
index 2de3a97594..a848b09c6d 100644
--- a/util/qsp.c
+++ b/util/qsp.c
@@ -84,13 +84,6 @@ struct QSPEntry {
uint64_t n_acqs;
uint64_t ns;
unsigned int n_objs; /* count of coalesced objs; only used for reporting */
-#ifndef CONFIG_ATOMIC64
- /*
- * If we cannot update the counts atomically, then use a seqlock.
- * We don't need an associated lock because the updates are thread-local.
- */
- QemuSeqLock sequence;
-#endif
};
typedef struct QSPEntry QSPEntry;
@@ -345,46 +338,15 @@ static QSPEntry *qsp_entry_get(const void *obj, const char *file, int line,
}
/*
- * @from is in the global hash table; read it atomically if the host
- * supports it, otherwise use the seqlock.
- */
-static void qsp_entry_aggregate(QSPEntry *to, const QSPEntry *from)
-{
-#ifdef CONFIG_ATOMIC64
- to->ns += atomic_read__nocheck(&from->ns);
- to->n_acqs += atomic_read__nocheck(&from->n_acqs);
-#else
- unsigned int version;
- uint64_t ns, n_acqs;
-
- do {
- version = seqlock_read_begin(&from->sequence);
- ns = atomic_read__nocheck(&from->ns);
- n_acqs = atomic_read__nocheck(&from->n_acqs);
- } while (seqlock_read_retry(&from->sequence, version));
-
- to->ns += ns;
- to->n_acqs += n_acqs;
-#endif
-}
-
-/*
* @e is in the global hash table; it is only written to by the current thread,
* so we write to it atomically (as in "write once") to prevent torn reads.
- * If the host doesn't support u64 atomics, use the seqlock.
*/
static inline void do_qsp_entry_record(QSPEntry *e, int64_t delta, bool acq)
{
-#ifndef CONFIG_ATOMIC64
- seqlock_write_begin(&e->sequence);
-#endif
- atomic_set__nocheck(&e->ns, e->ns + delta);
+ atomic_set_u64(&e->ns, e->ns + delta);
if (acq) {
- atomic_set__nocheck(&e->n_acqs, e->n_acqs + 1);
+ atomic_set_u64(&e->n_acqs, e->n_acqs + 1);
}
-#ifndef CONFIG_ATOMIC64
- seqlock_write_end(&e->sequence);
-#endif
}
static inline void qsp_entry_record(QSPEntry *e, int64_t delta)
@@ -550,7 +512,12 @@ static void qsp_aggregate(void *p, uint32_t h, void *up)
hash = qsp_entry_no_thread_hash(e);
agg = qsp_entry_find(ht, e, hash);
- qsp_entry_aggregate(agg, e);
+ /*
+ * The entry is in the global hash table; read from it atomically (as in
+ * "read once").
+ */
+ agg->ns += atomic_read_u64(&e->ns);
+ agg->n_acqs += atomic_read_u64(&e->n_acqs);
}
static void qsp_iter_diff(void *p, uint32_t hash, void *htp)