aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block/throttle-groups.c9
-rw-r--r--fpu/softfloat.c33
-rw-r--r--hmp.c2
-rw-r--r--include/block/throttle-groups.h5
-rw-r--r--include/fpu/softfloat-macros.h2
-rw-r--r--include/migration/register.h2
-rw-r--r--migration/channel.c11
-rw-r--r--migration/migration.c51
-rw-r--r--migration/migration.h12
-rw-r--r--migration/postcopy-ram.c1
-rw-r--r--migration/ram.c34
-rw-r--r--migration/ram.h4
-rw-r--r--migration/rdma.c1
-rw-r--r--migration/savevm.c5
-rw-r--r--qapi/migration.json5
-rw-r--r--qapi/ui.json10
-rwxr-xr-xscripts/archive-source.sh2
-rw-r--r--tests/Makefile.include137
-rw-r--r--tests/fp/Makefile3
m---------tests/fp/berkeley-testfloat-30
-rw-r--r--tests/fp/fp-bench.c15
-rw-r--r--tests/fp/fp-test.c4
-rw-r--r--tests/fp/platform.h1
-rwxr-xr-xtests/qemu-iotests/23847
-rw-r--r--tests/qemu-iotests/238.out6
-rw-r--r--tests/qemu-iotests/group1
-rw-r--r--tests/test-vmstate.c50
-rw-r--r--ui/input-linux.c66
-rw-r--r--util/main-loop.c8
-rw-r--r--util/qemu-coroutine-sleep.c27
30 files changed, 472 insertions, 82 deletions
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
index 5d8213a443..a5a2037924 100644
--- a/block/throttle-groups.c
+++ b/block/throttle-groups.c
@@ -415,6 +415,9 @@ static void coroutine_fn throttle_group_restart_queue_entry(void *opaque)
}
g_free(data);
+
+ atomic_dec(&tgm->restart_pending);
+ aio_wait_kick();
}
static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write)
@@ -430,6 +433,8 @@ static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write
* be no timer pending on this tgm at this point */
assert(!timer_pending(tgm->throttle_timers.timers[is_write]));
+ atomic_inc(&tgm->restart_pending);
+
co = qemu_coroutine_create(throttle_group_restart_queue_entry, rd);
aio_co_enter(tgm->aio_context, co);
}
@@ -538,6 +543,7 @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm,
tgm->throttle_state = ts;
tgm->aio_context = ctx;
+ atomic_set(&tgm->restart_pending, 0);
qemu_mutex_lock(&tg->lock);
/* If the ThrottleGroup is new set this ThrottleGroupMember as the token */
@@ -584,6 +590,9 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm)
return;
}
+ /* Wait for throttle_group_restart_queue_entry() coroutines to finish */
+ AIO_WAIT_WHILE(tgm->aio_context, atomic_read(&tgm->restart_pending) > 0);
+
qemu_mutex_lock(&tg->lock);
for (i = 0; i < 2; i++) {
assert(tgm->pending_reqs[i] == 0);
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 59eac97d10..9132d7a0b0 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -1542,6 +1542,8 @@ soft_f64_muladd(float64 a, float64 b, float64 c, int flags,
return float64_round_pack_canonical(pr, status);
}
+static bool force_soft_fma;
+
float32 QEMU_FLATTEN
float32_muladd(float32 xa, float32 xb, float32 xc, int flags, float_status *s)
{
@@ -1562,6 +1564,11 @@ float32_muladd(float32 xa, float32 xb, float32 xc, int flags, float_status *s)
if (unlikely(!f32_is_zon3(ua, ub, uc))) {
goto soft;
}
+
+ if (unlikely(force_soft_fma)) {
+ goto soft;
+ }
+
/*
* When (a || b) == 0, there's no need to check for under/over flow,
* since we know the addend is (normal || 0) and the product is 0.
@@ -1623,6 +1630,11 @@ float64_muladd(float64 xa, float64 xb, float64 xc, int flags, float_status *s)
if (unlikely(!f64_is_zon3(ua, ub, uc))) {
goto soft;
}
+
+ if (unlikely(force_soft_fma)) {
+ goto soft;
+ }
+
/*
* When (a || b) == 0, there's no need to check for under/over flow,
* since we know the addend is (normal || 0) and the product is 0.
@@ -7974,3 +7986,24 @@ float128 float128_scalbn(float128 a, int n, float_status *status)
, status);
}
+
+static void __attribute__((constructor)) softfloat_init(void)
+{
+ union_float64 ua, ub, uc, ur;
+
+ if (QEMU_NO_HARDFLOAT) {
+ return;
+ }
+ /*
+ * Test that the host's FMA is not obviously broken. For example,
+ * glibc < 2.23 can perform an incorrect FMA on certain hosts; see
+ * https://sourceware.org/bugzilla/show_bug.cgi?id=13304
+ */
+ ua.s = 0x0020000000000001ULL;
+ ub.s = 0x3ca0000000000000ULL;
+ uc.s = 0x0020000000000000ULL;
+ ur.h = fma(ua.h, ub.h, uc.h);
+ if (ur.s != 0x0020000000000001ULL) {
+ force_soft_fma = true;
+ }
+}
diff --git a/hmp.c b/hmp.c
index 8da5fd8760..b2a2b1f84e 100644
--- a/hmp.c
+++ b/hmp.c
@@ -236,6 +236,8 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict)
info->ram->page_size >> 10);
monitor_printf(mon, "multifd bytes: %" PRIu64 " kbytes\n",
info->ram->multifd_bytes >> 10);
+ monitor_printf(mon, "pages-per-second: %" PRIu64 "\n",
+ info->ram->pages_per_second);
if (info->ram->dirty_pages_rate) {
monitor_printf(mon, "dirty pages rate: %" PRIu64 " pages\n",
diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
index e2fd0513c4..712a8e64b4 100644
--- a/include/block/throttle-groups.h
+++ b/include/block/throttle-groups.h
@@ -43,6 +43,11 @@ typedef struct ThrottleGroupMember {
*/
unsigned int io_limits_disabled;
+ /* Number of pending throttle_group_restart_queue_entry() coroutines.
+ * Accessed with atomic operations.
+ */
+ unsigned int restart_pending;
+
/* The following fields are protected by the ThrottleGroup lock.
* See the ThrottleGroup documentation for details.
* throttle_state tells us if I/O limits are configured. */
diff --git a/include/fpu/softfloat-macros.h b/include/fpu/softfloat-macros.h
index b1d772e6d4..bd5b6418e3 100644
--- a/include/fpu/softfloat-macros.h
+++ b/include/fpu/softfloat-macros.h
@@ -641,7 +641,7 @@ static inline uint64_t udiv_qrnnd(uint64_t *r, uint64_t n1,
uint64_t q;
asm("divq %4" : "=a"(q), "=d"(*r) : "0"(n0), "1"(n1), "rm"(d));
return q;
-#elif defined(__s390x__)
+#elif defined(__s390x__) && !defined(__clang__)
/* Need to use a TImode type to get an even register pair for DLGR. */
unsigned __int128 n = (unsigned __int128)n1 << 64 | n0;
asm("dlgr %0, %1" : "+r"(n) : "r"(d));
diff --git a/include/migration/register.h b/include/migration/register.h
index d287f4c317..3d0b9833c6 100644
--- a/include/migration/register.h
+++ b/include/migration/register.h
@@ -72,7 +72,7 @@ int register_savevm_live(DeviceState *dev,
const char *idstr,
int instance_id,
int version_id,
- SaveVMHandlers *ops,
+ const SaveVMHandlers *ops,
void *opaque);
void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque);
diff --git a/migration/channel.c b/migration/channel.c
index 33e0e9b82f..20e4c8e2dc 100644
--- a/migration/channel.c
+++ b/migration/channel.c
@@ -30,6 +30,7 @@
void migration_channel_process_incoming(QIOChannel *ioc)
{
MigrationState *s = migrate_get_current();
+ Error *local_err = NULL;
trace_migration_set_incoming_channel(
ioc, object_get_typename(OBJECT(ioc)));
@@ -38,13 +39,13 @@ void migration_channel_process_incoming(QIOChannel *ioc)
*s->parameters.tls_creds &&
!object_dynamic_cast(OBJECT(ioc),
TYPE_QIO_CHANNEL_TLS)) {
- Error *local_err = NULL;
migration_tls_channel_process_incoming(s, ioc, &local_err);
- if (local_err) {
- error_report_err(local_err);
- }
} else {
- migration_ioc_process_incoming(ioc);
+ migration_ioc_process_incoming(ioc, &local_err);
+ }
+
+ if (local_err) {
+ error_report_err(local_err);
}
}
diff --git a/migration/migration.c b/migration/migration.c
index ffc4d9e556..37e06b76dc 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -438,15 +438,13 @@ static void process_incoming_migration_co(void *opaque)
/* Make sure all file formats flush their mutable metadata */
bdrv_invalidate_cache_all(&local_err);
if (local_err) {
- migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
- MIGRATION_STATUS_FAILED);
error_report_err(local_err);
- exit(EXIT_FAILURE);
+ goto fail;
}
if (colo_init_ram_cache() < 0) {
error_report("Init ram cache failed");
- exit(EXIT_FAILURE);
+ goto fail;
}
qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming",
@@ -461,20 +459,22 @@ static void process_incoming_migration_co(void *opaque)
}
if (ret < 0) {
- Error *local_err = NULL;
-
- migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
- MIGRATION_STATUS_FAILED);
error_report("load of migration failed: %s", strerror(-ret));
- qemu_fclose(mis->from_src_file);
- if (multifd_load_cleanup(&local_err) != 0) {
- error_report_err(local_err);
- }
- exit(EXIT_FAILURE);
+ goto fail;
}
mis->bh = qemu_bh_new(process_incoming_migration_bh, mis);
qemu_bh_schedule(mis->bh);
mis->migration_incoming_co = NULL;
+ return;
+fail:
+ local_err = NULL;
+ migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE,
+ MIGRATION_STATUS_FAILED);
+ qemu_fclose(mis->from_src_file);
+ if (multifd_load_cleanup(&local_err) != 0) {
+ error_report_err(local_err);
+ }
+ exit(EXIT_FAILURE);
}
static void migration_incoming_setup(QEMUFile *f)
@@ -541,7 +541,7 @@ void migration_fd_process_incoming(QEMUFile *f)
migration_incoming_process();
}
-void migration_ioc_process_incoming(QIOChannel *ioc)
+void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp)
{
MigrationIncomingState *mis = migration_incoming_get_current();
bool start_migration;
@@ -563,9 +563,14 @@ void migration_ioc_process_incoming(QIOChannel *ioc)
*/
start_migration = !migrate_use_multifd();
} else {
+ Error *local_err = NULL;
/* Multiple connections */
assert(migrate_use_multifd());
- start_migration = multifd_recv_new_channel(ioc);
+ start_migration = multifd_recv_new_channel(ioc, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
}
if (start_migration) {
@@ -777,6 +782,7 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s)
info->ram->postcopy_requests = ram_counters.postcopy_requests;
info->ram->page_size = qemu_target_page_size();
info->ram->multifd_bytes = ram_counters.multifd_bytes;
+ info->ram->pages_per_second = s->pages_per_second;
if (migrate_use_xbzrle()) {
info->has_xbzrle_cache = true;
@@ -1381,7 +1387,6 @@ static void migrate_fd_cleanup(void *opaque)
qemu_savevm_state_cleanup();
if (s->to_dst_file) {
- Error *local_err = NULL;
QEMUFile *tmp;
trace_migrate_fd_cleanup();
@@ -1392,9 +1397,7 @@ static void migrate_fd_cleanup(void *opaque)
}
qemu_mutex_lock_iothread();
- if (multifd_save_cleanup(&local_err) != 0) {
- error_report_err(local_err);
- }
+ multifd_save_cleanup();
qemu_mutex_lock(&s->qemu_file_lock);
tmp = s->to_dst_file;
s->to_dst_file = NULL;
@@ -1563,6 +1566,7 @@ void migrate_init(MigrationState *s)
s->rp_state.from_dst_file = NULL;
s->rp_state.error = false;
s->mbps = 0.0;
+ s->pages_per_second = 0.0;
s->downtime = 0;
s->expected_downtime = 0;
s->setup_time = 0;
@@ -2881,7 +2885,7 @@ static void migration_calculate_complete(MigrationState *s)
static void migration_update_counters(MigrationState *s,
int64_t current_time)
{
- uint64_t transferred, time_spent;
+ uint64_t transferred, transferred_pages, time_spent;
uint64_t current_bytes; /* bytes transferred since the beginning */
double bandwidth;
@@ -2898,6 +2902,11 @@ static void migration_update_counters(MigrationState *s,
s->mbps = (((double) transferred * 8.0) /
((double) time_spent / 1000.0)) / 1000.0 / 1000.0;
+ transferred_pages = ram_get_total_transferred_pages() -
+ s->iteration_initial_pages;
+ s->pages_per_second = (double) transferred_pages /
+ (((double) time_spent / 1000.0));
+
/*
* if we haven't sent anything, we don't want to
* recalculate. 10000 is a small enough number for our purposes
@@ -2910,6 +2919,7 @@ static void migration_update_counters(MigrationState *s,
s->iteration_start_time = current_time;
s->iteration_initial_bytes = current_bytes;
+ s->iteration_initial_pages = ram_get_total_transferred_pages();
trace_migrate_transferred(transferred, time_spent,
bandwidth, s->threshold_size);
@@ -3314,6 +3324,7 @@ static void migration_instance_init(Object *obj)
ms->state = MIGRATION_STATUS_NONE;
ms->mbps = -1;
+ ms->pages_per_second = -1;
qemu_sem_init(&ms->pause_sem, 0);
qemu_mutex_init(&ms->error_mutex);
diff --git a/migration/migration.h b/migration/migration.h
index e413d4d8b6..dcd05d9f87 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -126,7 +126,13 @@ struct MigrationState
*/
QemuSemaphore rate_limit_sem;
- /* bytes already send at the beggining of current interation */
+ /* pages already send at the beginning of current iteration */
+ uint64_t iteration_initial_pages;
+
+ /* pages transferred per second */
+ double pages_per_second;
+
+ /* bytes already send at the beginning of current iteration */
uint64_t iteration_initial_bytes;
/* time at the start of current iteration */
int64_t iteration_start_time;
@@ -229,7 +235,7 @@ struct MigrationState
void migrate_set_state(int *state, int old_state, int new_state);
void migration_fd_process_incoming(QEMUFile *f);
-void migration_ioc_process_incoming(QIOChannel *ioc);
+void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp);
void migration_incoming_process(void);
bool migration_has_all_channels(void);
@@ -271,6 +277,8 @@ bool migrate_use_block_incremental(void);
int migrate_max_cpu_throttle(void);
bool migrate_use_return_path(void);
+uint64_t ram_get_total_transferred_pages(void);
+
bool migrate_use_compression(void);
int migrate_compress_level(void);
int migrate_compress_threads(void);
diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c
index e5c02a32c5..fa09dba534 100644
--- a/migration/postcopy-ram.c
+++ b/migration/postcopy-ram.c
@@ -1117,6 +1117,7 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis)
/* Mark so that we get notified of accesses to unwritten areas */
if (qemu_ram_foreach_migratable_block(ram_block_enable_notify, mis)) {
+ error_report("ram_block_enable_notify failed");
return -1;
}
diff --git a/migration/ram.c b/migration/ram.c
index 1849979fed..59191c1ed2 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -917,13 +917,12 @@ static void multifd_send_terminate_threads(Error *err)
}
}
-int multifd_save_cleanup(Error **errp)
+void multifd_save_cleanup(void)
{
int i;
- int ret = 0;
if (!migrate_use_multifd()) {
- return 0;
+ return;
}
multifd_send_terminate_threads(NULL);
for (i = 0; i < migrate_multifd_channels(); i++) {
@@ -953,7 +952,6 @@ int multifd_save_cleanup(Error **errp)
multifd_send_state->pages = NULL;
g_free(multifd_send_state);
multifd_send_state = NULL;
- return ret;
}
static void multifd_send_sync_main(void)
@@ -1071,9 +1069,8 @@ static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque)
Error *local_err = NULL;
if (qio_task_propagate_error(task, &local_err)) {
- if (multifd_save_cleanup(&local_err) != 0) {
- migrate_set_error(migrate_get_current(), local_err);
- }
+ migrate_set_error(migrate_get_current(), local_err);
+ multifd_save_cleanup();
} else {
p->c = QIO_CHANNEL(sioc);
qio_channel_set_delay(p->c, false);
@@ -1322,8 +1319,13 @@ bool multifd_recv_all_channels_created(void)
return thread_count == atomic_read(&multifd_recv_state->count);
}
-/* Return true if multifd is ready for the migration, otherwise false */
-bool multifd_recv_new_channel(QIOChannel *ioc)
+/*
+ * Try to receive all multifd channels to get ready for the migration.
+ * - Return true and do not set @errp when correctly receving all channels;
+ * - Return false and do not set @errp when correctly receiving the current one;
+ * - Return false and set @errp when failing to receive the current channel.
+ */
+bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp)
{
MultiFDRecvParams *p;
Error *local_err = NULL;
@@ -1332,6 +1334,10 @@ bool multifd_recv_new_channel(QIOChannel *ioc)
id = multifd_recv_initial_packet(ioc, &local_err);
if (id < 0) {
multifd_recv_terminate_threads(local_err);
+ error_propagate_prepend(errp, local_err,
+ "failed to receive packet"
+ " via multifd channel %d: ",
+ atomic_read(&multifd_recv_state->count));
return false;
}
@@ -1340,6 +1346,7 @@ bool multifd_recv_new_channel(QIOChannel *ioc)
error_setg(&local_err, "multifd: received id '%d' already setup'",
id);
multifd_recv_terminate_threads(local_err);
+ error_propagate(errp, local_err);
return false;
}
p->c = ioc;
@@ -1351,7 +1358,8 @@ bool multifd_recv_new_channel(QIOChannel *ioc)
qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p,
QEMU_THREAD_JOINABLE);
atomic_inc(&multifd_recv_state->count);
- return multifd_recv_state->count == migrate_multifd_channels();
+ return atomic_read(&multifd_recv_state->count) ==
+ migrate_multifd_channels();
}
/**
@@ -1593,6 +1601,12 @@ uint64_t ram_pagesize_summary(void)
return summary;
}
+uint64_t ram_get_total_transferred_pages(void)
+{
+ return ram_counters.normal + ram_counters.duplicate +
+ compression_counters.pages + xbzrle_counters.pages;
+}
+
static void migration_update_rates(RAMState *rs, int64_t end_time)
{
uint64_t page_count = rs->target_page_count - rs->target_page_count_prev;
diff --git a/migration/ram.h b/migration/ram.h
index 83ff1bc11a..936177b3e9 100644
--- a/migration/ram.h
+++ b/migration/ram.h
@@ -43,11 +43,11 @@ uint64_t ram_bytes_remaining(void);
uint64_t ram_bytes_total(void);
int multifd_save_setup(void);
-int multifd_save_cleanup(Error **errp);
+void multifd_save_cleanup(void);
int multifd_load_setup(void);
int multifd_load_cleanup(Error **errp);
bool multifd_recv_all_channels_created(void);
-bool multifd_recv_new_channel(QIOChannel *ioc);
+bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp);
uint64_t ram_pagesize_summary(void);
int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len);
diff --git a/migration/rdma.c b/migration/rdma.c
index 9b2e7e10aa..54a3c11540 100644
--- a/migration/rdma.c
+++ b/migration/rdma.c
@@ -2321,6 +2321,7 @@ static void qemu_rdma_cleanup(RDMAContext *rdma)
rdma->connected = false;
}
+ qemu_set_fd_handler(rdma->channel->fd, NULL, NULL, NULL);
g_free(rdma->dest_blocks);
rdma->dest_blocks = NULL;
diff --git a/migration/savevm.c b/migration/savevm.c
index 9e45fb4f3f..322660438d 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -303,7 +303,7 @@ typedef struct SaveStateEntry {
int section_id;
/* section id read from the stream */
int load_section_id;
- SaveVMHandlers *ops;
+ const SaveVMHandlers *ops;
const VMStateDescription *vmsd;
void *opaque;
CompatEntry *compat;
@@ -614,7 +614,7 @@ int register_savevm_live(DeviceState *dev,
const char *idstr,
int instance_id,
int version_id,
- SaveVMHandlers *ops,
+ const SaveVMHandlers *ops,
void *opaque)
{
SaveStateEntry *se;
@@ -1729,6 +1729,7 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis)
*/
if (migrate_postcopy_ram()) {
if (postcopy_ram_enable_notify(mis)) {
+ postcopy_ram_incoming_cleanup(mis);
return -1;
}
}
diff --git a/qapi/migration.json b/qapi/migration.json
index 31b589ec26..7a795ecc16 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -41,6 +41,9 @@
#
# @multifd-bytes: The number of bytes sent through multifd (since 3.0)
#
+# @pages-per-second: the number of memory pages transferred per second
+# (Since 4.0)
+#
# Since: 0.14.0
##
{ 'struct': 'MigrationStats',
@@ -49,7 +52,7 @@
'normal-bytes': 'int', 'dirty-pages-rate' : 'int',
'mbps' : 'number', 'dirty-sync-count' : 'int',
'postcopy-requests' : 'int', 'page-size' : 'int',
- 'multifd-bytes' : 'uint64' } }
+ 'multifd-bytes' : 'uint64', 'pages-per-second' : 'uint64' } }
##
# @XBZRLECacheStats:
diff --git a/qapi/ui.json b/qapi/ui.json
index 5ad13248d5..7d9c4bddaf 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -1016,6 +1016,16 @@
'*head' : 'int',
'events' : [ 'InputEvent' ] } }
+##
+# @GrabToggleKeys:
+#
+# Keys to toggle input-linux between host and guest.
+#
+# Since: 4.0
+#
+##
+{ 'enum': 'GrabToggleKeys',
+ 'data': [ 'ctrl-ctrl', 'alt-alt', 'meta-meta', 'scrolllock', 'ctrl-scrolllock' ] }
##
# @DisplayGTK:
diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh
index 62bd22578b..6eed2a29bd 100755
--- a/scripts/archive-source.sh
+++ b/scripts/archive-source.sh
@@ -26,7 +26,7 @@ vroot_dir="${tar_file}.vroot"
# independent of what the developer currently has initialized
# in their checkout, because the build environment is completely
# different to the host OS.
-submodules="dtc ui/keycodemapdb"
+submodules="dtc ui/keycodemapdb tests/fp/berkeley-softfloat-3 tests/fp/berkeley-testfloat-3"
trap "status=$?; rm -rf \"$list_file\" \"$vroot_dir\"; exit \$status" 0 1 2 3 15
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 4eea38ae99..f5e6eb5152 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -12,6 +12,7 @@ check-help:
@echo " $(MAKE) check-qapi-schema Run QAPI schema tests"
@echo " $(MAKE) check-block Run block tests"
@echo " $(MAKE) check-tcg Run TCG tests"
+ @echo " $(MAKE) check-softfloat Run FPU emulation tests"
@echo " $(MAKE) check-acceptance Run all acceptance (functional) tests"
@echo
@echo " $(MAKE) check-report.html Generates an HTML test report"
@@ -880,6 +881,138 @@ check-report-unit.tap: $(check-unit-y)
check-report.tap: $(patsubst %,check-report-qtest-%.tap, $(QTEST_TARGETS)) check-report-unit.tap
$(call quiet-command,./scripts/tap-merge.py $^ > $@,"GEN","$@")
+# FPU Emulation tests (aka softfloat)
+#
+# As we still have some places that need fixing the rules are a little
+# more complex than they need to be and have to override some of the
+# generic Makefile expansions. Once we are cleanly passing all
+# the tests we can simplify the make syntax.
+
+FP_TEST_BIN=$(BUILD_DIR)/tests/fp/fp-test
+
+# the build dir is created by configure
+.PHONY: $(FP_TEST_BIN)
+$(FP_TEST_BIN):
+ $(call quiet-command, \
+ $(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" $(notdir $@), \
+ "BUILD", "$(notdir $@)")
+
+# The full test suite can take a bit of time, default to a quick run
+ifeq ($(SPEED), quick)
+FP_TL=-l 1
+else
+FP_TL=-l 2 -r all
+endif
+
+# $1 = tests, $2 = description
+test-softfloat = $(call quiet-command, \
+ cd $(BUILD_DIR)/tests/fp && \
+ ./fp-test -s $(FP_TL) $1 > $2.out 2>&1 || \
+ (cat $2.out && exit 1;), \
+ "FLOAT TEST", $2)
+
+# Conversion Routines:
+# FIXME: i32_to_extF80 (broken), i64_to_extF80 (broken)
+# ui32_to_f128 (not implemented), f128_to_ui32 (not implemented)
+# extF80_roundToInt (broken)
+#
+check-softfloat-conv: $(FP_TEST_BIN)
+ $(call test-softfloat, \
+ i32_to_f16 i64_to_f16 \
+ i32_to_f32 i64_to_f32 \
+ i32_to_f64 i64_to_f64 \
+ i32_to_f128 i64_to_f128, int-to-float)
+ $(call test-softfloat, \
+ ui32_to_f16 ui64_to_f16 \
+ ui32_to_f32 ui64_to_f32 \
+ ui32_to_f64 ui64_to_f64 \
+ ui64_to_f128, uint-to-float)
+ $(call test-softfloat, \
+ f16_to_i32 f16_to_i32_r_minMag \
+ f32_to_i32 f32_to_i32_r_minMag \
+ f64_to_i32 f64_to_i32_r_minMag \
+ extF80_to_i32 extF80_to_i32_r_minMag \
+ f128_to_i32 f128_to_i32_r_minMag \
+ f16_to_i64 f16_to_i64_r_minMag \
+ f32_to_i64 f32_to_i64_r_minMag \
+ f64_to_i64 f64_to_i64_r_minMag \
+ extF80_to_i64 extF80_to_i64_r_minMag \
+ f128_to_i64 f128_to_i64_r_minMag, \
+ float-to-int)
+ $(call test-softfloat, \
+ f16_to_ui32 f16_to_ui32_r_minMag \
+ f32_to_ui32 f32_to_ui32_r_minMag \
+ f64_to_ui32 f64_to_ui32_r_minMag \
+ f16_to_ui64 f16_to_ui64_r_minMag \
+ f32_to_ui64 f32_to_ui64_r_minMag \
+ f64_to_ui64 f64_to_ui64_r_minMag, \
+ float-to-uint)
+ $(call test-softfloat, \
+ f16_roundToInt f32_roundToInt \
+ f64_roundToInt f128_roundToInt, \
+ round-to-integer)
+
+# Generic rule for all float operations
+#
+# Some patterns are overidden due to broken or missing tests.
+# Hopefully these can be removed over time.
+
+check-softfloat-%: $(FP_TEST_BIN)
+ $(call test-softfloat, f16_$* f32_$* f64_$* extF80_$* f128_$*, $*)
+
+# Float Compare routines
+SF_COMPARE_OPS=eq eq_signaling le le_quiet lt_quiet
+SF_COMPARE_RULES=$(patsubst %,check-softfloat-%, $(SF_COMPARE_OPS))
+
+# FIXME: extF80_le_quiet (broken)
+check-softfloat-le_quiet: $(FP_TEST_BIN)
+ $(call test-softfloat, \
+ f16_le_quiet f32_le_quiet f64_le_quiet \
+ f128_le_quiet, \
+ le_quiet)
+
+# FIXME: extF80_lt_quiet (broken)
+check-softfloat-lt_quiet: $(FP_TEST_BIN)
+ $(call test-softfloat, \
+ f16_lt_quiet f32_lt_quiet f64_lt_quiet \
+ f128_lt_quiet, \
+ lt_quiet)
+
+.PHONY: check-softfloat-compare
+check-softfloat-compare: $(SF_COMPARE_RULES)
+
+# Math Operations
+
+# FIXME: extF80_mulAdd (missing)
+check-softfloat-mulAdd: $(FP_TEST_BIN)
+ $(call test-softfloat, \
+ f16_mulAdd f32_mulAdd f64_mulAdd f128_mulAdd, \
+ mulAdd)
+
+# FIXME: extF80_rem (broken)
+check-softfloat-rem: $(FP_TEST_BIN)
+ $(call test-softfloat, \
+ f16_rem f32_rem f64_rem f128_rem, \
+ rem)
+
+SF_MATH_OPS=add sub mul mulAdd div rem sqrt
+SF_MATH_RULES=$(patsubst %,check-softfloat-%, $(SF_MATH_OPS))
+
+.PHONY: check-softfloat-ops
+check-softfloat-ops: $(SF_MATH_RULES)
+
+# Finally a generic rule to test all of softfoat. If TCG isnt't
+# enabled we define a null operation which skips the tests.
+
+.PHONY: check-softfloat
+ifeq ($(CONFIG_TCG),y)
+check-softfloat: check-softfloat-conv check-softfloat-compare check-softfloat-ops
+else
+check-softfloat:
+ $(call quiet-command, /bin/true, "FLOAT TEST", \
+ "SKIPPED for non-TCG builds")
+endif
+
# Per guest TCG tests
LINUX_USER_TARGETS=$(filter %-linux-user,$(TARGET_DIRS))
@@ -912,7 +1045,7 @@ clean-tcg-tests-%:
build-tcg: $(BUILD_TCG_TARGET_RULES)
.PHONY: check-tcg
-check-tcg: $(RUN_TCG_TARGET_RULES)
+check-tcg: check-softfloat $(RUN_TCG_TARGET_RULES)
.PHONY: clean-tcg
clean-tcg: $(CLEAN_TCG_TARGET_RULES)
@@ -993,7 +1126,7 @@ check-acceptance: check-venv $(TESTS_RESULTS_DIR)
check-qapi-schema: $(patsubst %,check-%, $(check-qapi-schema-y)) check-tests/qapi-schema/doc-good.texi
check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS))
check-block: $(patsubst %,check-%, $(check-block-y))
-check: check-qapi-schema check-unit check-qtest check-decodetree
+check: check-qapi-schema check-unit check-softfloat check-qtest check-decodetree
check-clean:
rm -rf $(check-unit-y) tests/*.o $(QEMU_IOTESTS_HELPERS-y)
rm -rf $(sort $(foreach target,$(SYSEMU_TARGET_LIST), $(check-qtest-$(target)-y)) $(check-qtest-generic-y))
diff --git a/tests/fp/Makefile b/tests/fp/Makefile
index 5019dcdca0..5a35e7c210 100644
--- a/tests/fp/Makefile
+++ b/tests/fp/Makefile
@@ -65,8 +65,7 @@ QEMU_CFLAGS += $(TF_OPTS)
TF_CFLAGS :=
TF_CFLAGS += -Wno-strict-prototypes
TF_CFLAGS += -Wno-unknown-pragmas
-TF_CFLAGS += -Wno-discarded-qualifiers
-TF_CFLAGS += -Wno-maybe-uninitialized
+TF_CFLAGS += -Wno-uninitialized
TF_CFLAGS += -Wno-missing-prototypes
TF_CFLAGS += -Wno-return-type
TF_CFLAGS += -Wno-unused-function
diff --git a/tests/fp/berkeley-testfloat-3 b/tests/fp/berkeley-testfloat-3
-Subproject ca9fa2ba05625ba929958f163b01747e07dd39c
+Subproject 5a59dcec19327396a011a17fd924aed4fec416b
diff --git a/tests/fp/fp-bench.c b/tests/fp/fp-bench.c
index f5bc5edebf..4ba5e1d2d4 100644
--- a/tests/fp/fp-bench.c
+++ b/tests/fp/fp-bench.c
@@ -143,15 +143,20 @@ static void update_random_ops(int n_ops, enum precision prec)
for (i = 0; i < n_ops; i++) {
uint64_t r = random_ops[i];
- if (prec == PREC_SINGLE || PREC_FLOAT32) {
+ switch (prec) {
+ case PREC_SINGLE:
+ case PREC_FLOAT32:
do {
r = xorshift64star(r);
} while (!float32_is_normal(r));
- } else if (prec == PREC_DOUBLE || PREC_FLOAT64) {
+ break;
+ case PREC_DOUBLE:
+ case PREC_FLOAT64:
do {
r = xorshift64star(r);
} while (!float64_is_normal(r));
- } else {
+ break;
+ default:
g_assert_not_reached();
}
random_ops[i] = r;
@@ -171,8 +176,6 @@ static void fill_random(union fp *ops, int n_ops, enum precision prec,
if (no_neg && float32_is_neg(ops[i].f32)) {
ops[i].f32 = float32_chs(ops[i].f32);
}
- /* raise the exponent to limit the frequency of denormal results */
- ops[i].f32 |= 0x40000000;
break;
case PREC_DOUBLE:
case PREC_FLOAT64:
@@ -180,8 +183,6 @@ static void fill_random(union fp *ops, int n_ops, enum precision prec,
if (no_neg && float64_is_neg(ops[i].f64)) {
ops[i].f64 = float64_chs(ops[i].f64);
}
- /* raise the exponent to limit the frequency of denormal results */
- ops[i].f64 |= LIT64(0x4000000000000000);
break;
default:
g_assert_not_reached();
diff --git a/tests/fp/fp-test.c b/tests/fp/fp-test.c
index fca576309c..2a35ef601d 100644
--- a/tests/fp/fp-test.c
+++ b/tests/fp/fp-test.c
@@ -789,7 +789,7 @@ static int set_init_flags(const char *flags)
return 0;
}
-static uint8_t slow_clear_flags(void)
+static uint_fast8_t slow_clear_flags(void)
{
uint8_t prev = slowfloat_exceptionFlags;
@@ -797,7 +797,7 @@ static uint8_t slow_clear_flags(void)
return prev;
}
-static uint8_t qemu_clear_flags(void)
+static uint_fast8_t qemu_clear_flags(void)
{
uint8_t prev = qemu_flags_to_sf(qsf.float_exception_flags);
diff --git a/tests/fp/platform.h b/tests/fp/platform.h
index f8c423dde3..c20ba70baa 100644
--- a/tests/fp/platform.h
+++ b/tests/fp/platform.h
@@ -29,6 +29,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "config-host.h"
#ifndef HOST_WORDS_BIGENDIAN
#define LITTLEENDIAN 1
diff --git a/tests/qemu-iotests/238 b/tests/qemu-iotests/238
new file mode 100755
index 0000000000..f81ee1112f
--- /dev/null
+++ b/tests/qemu-iotests/238
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+#
+# Regression test for throttle group member unregister segfault with iothread
+#
+# Copyright (c) 2019 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import sys
+import os
+import iotests
+from iotests import log
+
+sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'scripts'))
+
+from qemu import QEMUMachine
+
+if iotests.qemu_default_machine == 's390-ccw-virtio':
+ virtio_scsi_device = 'virtio-scsi-ccw'
+else:
+ virtio_scsi_device = 'virtio-scsi-pci'
+
+vm = QEMUMachine(iotests.qemu_prog)
+vm.add_args('-machine', 'accel=kvm')
+vm.launch()
+
+log(vm.qmp('blockdev-add', node_name='hd0', driver='null-co'))
+log(vm.qmp('object-add', qom_type='iothread', id='iothread0'))
+log(vm.qmp('device_add', id='scsi0', driver=virtio_scsi_device, iothread='iothread0'))
+log(vm.qmp('device_add', id='scsi-hd0', driver='scsi-hd', drive='hd0'))
+log(vm.qmp('block_set_io_throttle', id='scsi-hd0', bps=0, bps_rd=0, bps_wr=0,
+ iops=1000, iops_rd=0, iops_wr=0, conv_keys=False))
+log(vm.qmp('device_del', id='scsi-hd0'))
+
+vm.shutdown()
diff --git a/tests/qemu-iotests/238.out b/tests/qemu-iotests/238.out
new file mode 100644
index 0000000000..4de840ba8c
--- /dev/null
+++ b/tests/qemu-iotests/238.out
@@ -0,0 +1,6 @@
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index f6b245917a..0f1c3f9cdf 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -234,3 +234,4 @@
234 auto quick migration
235 auto quick
236 auto quick
+238 auto quick
diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c
index 0ab29a8216..fc8ce62471 100644
--- a/tests/test-vmstate.c
+++ b/tests/test-vmstate.c
@@ -284,6 +284,55 @@ static void test_simple_primitive(void)
FIELD_EQUAL(i64_2);
}
+typedef struct TestSimpleArray {
+ uint16_t u16_1[3];
+} TestSimpleArray;
+
+/* Object instantiation, we are going to use it in more than one test */
+
+TestSimpleArray obj_simple_arr = {
+ .u16_1 = { 0x42, 0x43, 0x44 },
+};
+
+/* Description of the values. If you add a primitive type
+ you are expected to add a test here */
+
+static const VMStateDescription vmstate_simple_arr = {
+ .name = "simple/array",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT16_ARRAY(u16_1, TestSimpleArray, 3),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+uint8_t wire_simple_arr[] = {
+ /* u16_1 */ 0x00, 0x42,
+ /* u16_1 */ 0x00, 0x43,
+ /* u16_1 */ 0x00, 0x44,
+ QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
+};
+
+static void obj_simple_arr_copy(void *target, void *source)
+{
+ memcpy(target, source, sizeof(TestSimpleArray));
+}
+
+static void test_simple_array(void)
+{
+ TestSimpleArray obj, obj_clone;
+
+ memset(&obj, 0, sizeof(obj));
+ save_vmstate(&vmstate_simple_arr, &obj_simple_arr);
+
+ compare_vmstate(wire_simple_arr, sizeof(wire_simple_arr));
+
+ SUCCESS(load_vmstate(&vmstate_simple_arr, &obj, &obj_clone,
+ obj_simple_arr_copy, 1, wire_simple_arr,
+ sizeof(wire_simple_arr)));
+}
+
typedef struct TestStruct {
uint32_t a, b, c, e;
uint64_t d, f;
@@ -863,6 +912,7 @@ int main(int argc, char **argv)
g_test_init(&argc, &argv, NULL);
g_test_add_func("/vmstate/simple/primitive", test_simple_primitive);
+ g_test_add_func("/vmstate/simple/array", test_simple_array);
g_test_add_func("/vmstate/versioned/load/v1", test_load_v1);
g_test_add_func("/vmstate/versioned/load/v2", test_load_v2);
g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip);
diff --git a/ui/input-linux.c b/ui/input-linux.c
index 9720333b2c..ba550dd274 100644
--- a/ui/input-linux.c
+++ b/ui/input-linux.c
@@ -12,6 +12,8 @@
#include "sysemu/sysemu.h"
#include "ui/input.h"
#include "qom/object_interfaces.h"
+#include "sysemu/iothread.h"
+#include "block/aio.h"
#include <sys/ioctl.h>
#include "standard-headers/linux/input.h"
@@ -63,6 +65,8 @@ struct InputLinux {
struct input_event event;
int read_offset;
+ enum GrabToggleKeys grab_toggle;
+
QTAILQ_ENTRY(InputLinux) next;
};
@@ -98,6 +102,44 @@ static void input_linux_toggle_grab(InputLinux *il)
}
}
+static bool input_linux_check_toggle(InputLinux *il)
+{
+ switch (il->grab_toggle) {
+ case GRAB_TOGGLE_KEYS_CTRL_CTRL:
+ return il->keydown[KEY_LEFTCTRL] &&
+ il->keydown[KEY_RIGHTCTRL];
+
+ case GRAB_TOGGLE_KEYS_ALT_ALT:
+ return il->keydown[KEY_LEFTALT] &&
+ il->keydown[KEY_RIGHTALT];
+
+ case GRAB_TOGGLE_KEYS_META_META:
+ return il->keydown[KEY_LEFTMETA] &&
+ il->keydown[KEY_RIGHTMETA];
+
+ case GRAB_TOGGLE_KEYS_SCROLLLOCK:
+ return il->keydown[KEY_SCROLLLOCK];
+
+ case GRAB_TOGGLE_KEYS_CTRL_SCROLLLOCK:
+ return (il->keydown[KEY_LEFTCTRL] ||
+ il->keydown[KEY_RIGHTCTRL]) &&
+ il->keydown[KEY_SCROLLLOCK];
+
+ case GRAB_TOGGLE_KEYS__MAX:
+ /* avoid gcc error */
+ break;
+ }
+ return false;
+}
+
+static bool input_linux_should_skip(InputLinux *il,
+ struct input_event *event)
+{
+ return (il->grab_toggle == GRAB_TOGGLE_KEYS_SCROLLLOCK ||
+ il->grab_toggle == GRAB_TOGGLE_KEYS_CTRL_SCROLLLOCK) &&
+ event->code == KEY_SCROLLLOCK;
+}
+
static void input_linux_handle_keyboard(InputLinux *il,
struct input_event *event)
{
@@ -128,14 +170,13 @@ static void input_linux_handle_keyboard(InputLinux *il,
}
/* send event to guest when grab is active */
- if (il->grab_active) {
+ if (il->grab_active && !input_linux_should_skip(il, event)) {
int qcode = qemu_input_linux_to_qcode(event->code);
qemu_input_event_send_key_qcode(NULL, qcode, event->value);
}
/* hotkey -> record switch request ... */
- if (il->keydown[KEY_LEFTCTRL] &&
- il->keydown[KEY_RIGHTCTRL]) {
+ if (input_linux_check_toggle(il)) {
il->grab_request = true;
}
@@ -410,6 +451,21 @@ static void input_linux_set_repeat(Object *obj, bool value,
il->repeat = value;
}
+static int input_linux_get_grab_toggle(Object *obj, Error **errp)
+{
+ InputLinux *il = INPUT_LINUX(obj);
+
+ return il->grab_toggle;
+}
+
+static void input_linux_set_grab_toggle(Object *obj, int value,
+ Error **errp)
+{
+ InputLinux *il = INPUT_LINUX(obj);
+
+ il->grab_toggle = value;
+}
+
static void input_linux_instance_init(Object *obj)
{
object_property_add_str(obj, "evdev",
@@ -421,6 +477,10 @@ static void input_linux_instance_init(Object *obj)
object_property_add_bool(obj, "repeat",
input_linux_get_repeat,
input_linux_set_repeat, NULL);
+ object_property_add_enum(obj, "grab-toggle", "GrabToggleKeys",
+ &GrabToggleKeys_lookup,
+ input_linux_get_grab_toggle,
+ input_linux_set_grab_toggle, NULL);
}
static void input_linux_class_init(ObjectClass *oc, void *data)
diff --git a/util/main-loop.c b/util/main-loop.c
index affe0403c5..443cb4cfe8 100644
--- a/util/main-loop.c
+++ b/util/main-loop.c
@@ -71,7 +71,7 @@ static void sigfd_handler(void *opaque)
}
}
-static int qemu_signal_init(void)
+static int qemu_signal_init(Error **errp)
{
int sigfd;
sigset_t set;
@@ -96,7 +96,7 @@ static int qemu_signal_init(void)
sigdelset(&set, SIG_IPI);
sigfd = qemu_signalfd(&set);
if (sigfd == -1) {
- fprintf(stderr, "failed to create signalfd\n");
+ error_setg_errno(errp, errno, "failed to create signalfd");
return -errno;
}
@@ -109,7 +109,7 @@ static int qemu_signal_init(void)
#else /* _WIN32 */
-static int qemu_signal_init(void)
+static int qemu_signal_init(Error **errp)
{
return 0;
}
@@ -148,7 +148,7 @@ int qemu_init_main_loop(Error **errp)
init_clocks(qemu_timer_notify_cb);
- ret = qemu_signal_init();
+ ret = qemu_signal_init(errp);
if (ret) {
return ret;
}
diff --git a/util/qemu-coroutine-sleep.c b/util/qemu-coroutine-sleep.c
index afb678fbe5..4bfdd30cbf 100644
--- a/util/qemu-coroutine-sleep.c
+++ b/util/qemu-coroutine-sleep.c
@@ -17,38 +17,31 @@
#include "qemu/timer.h"
#include "block/aio.h"
-typedef struct CoSleepCB {
- QEMUTimer *ts;
- Coroutine *co;
-} CoSleepCB;
-
static void co_sleep_cb(void *opaque)
{
- CoSleepCB *sleep_cb = opaque;
+ Coroutine *co = opaque;
/* Write of schedule protected by barrier write in aio_co_schedule */
- atomic_set(&sleep_cb->co->scheduled, NULL);
- aio_co_wake(sleep_cb->co);
+ atomic_set(&co->scheduled, NULL);
+ aio_co_wake(co);
}
void coroutine_fn qemu_co_sleep_ns(QEMUClockType type, int64_t ns)
{
AioContext *ctx = qemu_get_current_aio_context();
- CoSleepCB sleep_cb = {
- .co = qemu_coroutine_self(),
- };
+ QEMUTimer *ts;
+ Coroutine *co = qemu_coroutine_self();
- const char *scheduled = atomic_cmpxchg(&sleep_cb.co->scheduled, NULL,
- __func__);
+ const char *scheduled = atomic_cmpxchg(&co->scheduled, NULL, __func__);
if (scheduled) {
fprintf(stderr,
"%s: Co-routine was already scheduled in '%s'\n",
__func__, scheduled);
abort();
}
- sleep_cb.ts = aio_timer_new(ctx, type, SCALE_NS, co_sleep_cb, &sleep_cb);
- timer_mod(sleep_cb.ts, qemu_clock_get_ns(type) + ns);
+ ts = aio_timer_new(ctx, type, SCALE_NS, co_sleep_cb, co);
+ timer_mod(ts, qemu_clock_get_ns(type) + ns);
qemu_coroutine_yield();
- timer_del(sleep_cb.ts);
- timer_free(sleep_cb.ts);
+ timer_del(ts);
+ timer_free(ts);
}