diff options
-rw-r--r-- | block/throttle-groups.c | 9 | ||||
-rw-r--r-- | fpu/softfloat.c | 33 | ||||
-rw-r--r-- | hmp.c | 2 | ||||
-rw-r--r-- | include/block/throttle-groups.h | 5 | ||||
-rw-r--r-- | include/fpu/softfloat-macros.h | 2 | ||||
-rw-r--r-- | include/migration/register.h | 2 | ||||
-rw-r--r-- | migration/channel.c | 11 | ||||
-rw-r--r-- | migration/migration.c | 51 | ||||
-rw-r--r-- | migration/migration.h | 12 | ||||
-rw-r--r-- | migration/postcopy-ram.c | 1 | ||||
-rw-r--r-- | migration/ram.c | 34 | ||||
-rw-r--r-- | migration/ram.h | 4 | ||||
-rw-r--r-- | migration/rdma.c | 1 | ||||
-rw-r--r-- | migration/savevm.c | 5 | ||||
-rw-r--r-- | qapi/migration.json | 5 | ||||
-rw-r--r-- | qapi/ui.json | 10 | ||||
-rwxr-xr-x | scripts/archive-source.sh | 2 | ||||
-rw-r--r-- | tests/Makefile.include | 137 | ||||
-rw-r--r-- | tests/fp/Makefile | 3 | ||||
m--------- | tests/fp/berkeley-testfloat-3 | 0 | ||||
-rw-r--r-- | tests/fp/fp-bench.c | 15 | ||||
-rw-r--r-- | tests/fp/fp-test.c | 4 | ||||
-rw-r--r-- | tests/fp/platform.h | 1 | ||||
-rwxr-xr-x | tests/qemu-iotests/238 | 47 | ||||
-rw-r--r-- | tests/qemu-iotests/238.out | 6 | ||||
-rw-r--r-- | tests/qemu-iotests/group | 1 | ||||
-rw-r--r-- | tests/test-vmstate.c | 50 | ||||
-rw-r--r-- | ui/input-linux.c | 66 | ||||
-rw-r--r-- | util/main-loop.c | 8 | ||||
-rw-r--r-- | util/qemu-coroutine-sleep.c | 27 |
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; + } +} @@ -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); } |