From: Claudio Fontana Date: Thu, 4 Feb 2021 16:39:25 +0000 (+0100) Subject: accel: replace struct CpusAccel with AccelOpsClass X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=b86f59c7155;p=qemu.git accel: replace struct CpusAccel with AccelOpsClass This will allow us to centralize the registration of the cpus.c module accelerator operations (in accel/accel-softmmu.c), and trigger it automatically using object hierarchy lookup from the new accel_init_interfaces() initialization step, depending just on which accelerators are available in the code. Rename all tcg-cpus.c, kvm-cpus.c, etc to tcg-accel-ops.c, kvm-accel-ops.c, etc, matching the object type names. Signed-off-by: Claudio Fontana Message-Id: <20210204163931.7358-18-cfontana@suse.de> Signed-off-by: Richard Henderson --- diff --git a/MAINTAINERS b/MAINTAINERS index 9356db50c3..2e63561ad0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -439,7 +439,8 @@ M: Richard Henderson R: Paolo Bonzini S: Maintained F: include/qemu/accel.h -F: accel/accel.c +F: include/sysemu/accel-ops.h +F: accel/accel-*.c F: accel/Makefile.objs F: accel/stubs/Makefile.objs diff --git a/accel/accel-common.c b/accel/accel-common.c index ddec8cb5ae..6b59873419 100644 --- a/accel/accel-common.c +++ b/accel/accel-common.c @@ -26,6 +26,10 @@ #include "qemu/osdep.h" #include "qemu/accel.h" +#ifndef CONFIG_USER_ONLY +#include "accel-softmmu.h" +#endif /* !CONFIG_USER_ONLY */ + static const TypeInfo accel_type = { .name = TYPE_ACCEL, .parent = TYPE_OBJECT, @@ -42,6 +46,13 @@ AccelClass *accel_find(const char *opt_name) return ac; } +void accel_init_interfaces(AccelClass *ac) +{ +#ifndef CONFIG_USER_ONLY + accel_init_ops_interfaces(ac); +#endif /* !CONFIG_USER_ONLY */ +} + static void register_accel_types(void) { type_register_static(&accel_type); diff --git a/accel/accel-softmmu.c b/accel/accel-softmmu.c index f89da8f9d1..50fa5acaa4 100644 --- a/accel/accel-softmmu.c +++ b/accel/accel-softmmu.c @@ -26,9 +26,9 @@ #include "qemu/osdep.h" #include "qemu/accel.h" #include "hw/boards.h" -#include "sysemu/arch_init.h" -#include "sysemu/sysemu.h" -#include "qom/object.h" +#include "sysemu/cpus.h" + +#include "accel-softmmu.h" int accel_init_machine(AccelState *accel, MachineState *ms) { @@ -60,3 +60,41 @@ void accel_setup_post(MachineState *ms) acc->setup_post(ms, accel); } } + +/* initialize the arch-independent accel operation interfaces */ +void accel_init_ops_interfaces(AccelClass *ac) +{ + const char *ac_name; + char *ops_name; + AccelOpsClass *ops; + + ac_name = object_class_get_name(OBJECT_CLASS(ac)); + g_assert(ac_name != NULL); + + ops_name = g_strdup_printf("%s" ACCEL_OPS_SUFFIX, ac_name); + ops = ACCEL_OPS_CLASS(object_class_by_name(ops_name)); + g_free(ops_name); + + /* + * all accelerators need to define ops, providing at least a mandatory + * non-NULL create_vcpu_thread operation. + */ + g_assert(ops != NULL); + if (ops->ops_init) { + ops->ops_init(ops); + } + cpus_register_accel(ops); +} + +static const TypeInfo accel_ops_type_info = { + .name = TYPE_ACCEL_OPS, + .parent = TYPE_OBJECT, + .abstract = true, + .class_size = sizeof(AccelOpsClass), +}; + +static void accel_softmmu_register_types(void) +{ + type_register_static(&accel_ops_type_info); +} +type_init(accel_softmmu_register_types); diff --git a/accel/accel-softmmu.h b/accel/accel-softmmu.h new file mode 100644 index 0000000000..5e192f1882 --- /dev/null +++ b/accel/accel-softmmu.h @@ -0,0 +1,15 @@ +/* + * QEMU System Emulation accel internal functions + * + * Copyright 2021 SUSE LLC + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef ACCEL_SOFTMMU_H +#define ACCEL_SOFTMMU_H + +void accel_init_ops_interfaces(AccelClass *ac); + +#endif /* ACCEL_SOFTMMU_H */ diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c new file mode 100644 index 0000000000..7516c67a3f --- /dev/null +++ b/accel/kvm/kvm-accel-ops.c @@ -0,0 +1,100 @@ +/* + * QEMU KVM support + * + * Copyright IBM, Corp. 2008 + * Red Hat, Inc. 2008 + * + * Authors: + * Anthony Liguori + * Glauber Costa + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "sysemu/kvm_int.h" +#include "sysemu/runstate.h" +#include "sysemu/cpus.h" +#include "qemu/guest-random.h" +#include "qapi/error.h" + +#include "kvm-cpus.h" + +static void *kvm_vcpu_thread_fn(void *arg) +{ + CPUState *cpu = arg; + int r; + + rcu_register_thread(); + + qemu_mutex_lock_iothread(); + qemu_thread_get_self(cpu->thread); + cpu->thread_id = qemu_get_thread_id(); + cpu->can_do_io = 1; + current_cpu = cpu; + + r = kvm_init_vcpu(cpu, &error_fatal); + kvm_init_cpu_signals(cpu); + + /* signal CPU creation */ + cpu_thread_signal_created(cpu); + qemu_guest_random_seed_thread_part2(cpu->random_seed); + + do { + if (cpu_can_run(cpu)) { + r = kvm_cpu_exec(cpu); + if (r == EXCP_DEBUG) { + cpu_handle_guest_debug(cpu); + } + } + qemu_wait_io_event(cpu); + } while (!cpu->unplug || cpu_can_run(cpu)); + + kvm_destroy_vcpu(cpu); + cpu_thread_signal_destroyed(cpu); + qemu_mutex_unlock_iothread(); + rcu_unregister_thread(); + return NULL; +} + +static void kvm_start_vcpu_thread(CPUState *cpu) +{ + char thread_name[VCPU_THREAD_NAME_SIZE]; + + cpu->thread = g_malloc0(sizeof(QemuThread)); + cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + qemu_cond_init(cpu->halt_cond); + snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/KVM", + cpu->cpu_index); + qemu_thread_create(cpu->thread, thread_name, kvm_vcpu_thread_fn, + cpu, QEMU_THREAD_JOINABLE); +} + +static void kvm_accel_ops_class_init(ObjectClass *oc, void *data) +{ + AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); + + ops->create_vcpu_thread = kvm_start_vcpu_thread; + ops->synchronize_post_reset = kvm_cpu_synchronize_post_reset; + ops->synchronize_post_init = kvm_cpu_synchronize_post_init; + ops->synchronize_state = kvm_cpu_synchronize_state; + ops->synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm; +} + +static const TypeInfo kvm_accel_ops_type = { + .name = ACCEL_OPS_NAME("kvm"), + + .parent = TYPE_ACCEL_OPS, + .class_init = kvm_accel_ops_class_init, + .abstract = true, +}; + +static void kvm_accel_ops_register_types(void) +{ + type_register_static(&kvm_accel_ops_type); +} +type_init(kvm_accel_ops_register_types); diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 3feb17d965..5164d838b9 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2256,8 +2256,6 @@ static int kvm_init(MachineState *ms) ret = ram_block_discard_disable(true); assert(!ret); } - - cpus_register_accel(&kvm_cpus); return 0; err: diff --git a/accel/kvm/kvm-cpus.c b/accel/kvm/kvm-cpus.c deleted file mode 100644 index d809b1e74c..0000000000 --- a/accel/kvm/kvm-cpus.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * QEMU KVM support - * - * Copyright IBM, Corp. 2008 - * Red Hat, Inc. 2008 - * - * Authors: - * Anthony Liguori - * Glauber Costa - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qemu/main-loop.h" -#include "sysemu/kvm_int.h" -#include "sysemu/runstate.h" -#include "sysemu/cpus.h" -#include "qemu/guest-random.h" -#include "qapi/error.h" - -#include "kvm-cpus.h" - -static void *kvm_vcpu_thread_fn(void *arg) -{ - CPUState *cpu = arg; - int r; - - rcu_register_thread(); - - qemu_mutex_lock_iothread(); - qemu_thread_get_self(cpu->thread); - cpu->thread_id = qemu_get_thread_id(); - cpu->can_do_io = 1; - current_cpu = cpu; - - r = kvm_init_vcpu(cpu, &error_fatal); - kvm_init_cpu_signals(cpu); - - /* signal CPU creation */ - cpu_thread_signal_created(cpu); - qemu_guest_random_seed_thread_part2(cpu->random_seed); - - do { - if (cpu_can_run(cpu)) { - r = kvm_cpu_exec(cpu); - if (r == EXCP_DEBUG) { - cpu_handle_guest_debug(cpu); - } - } - qemu_wait_io_event(cpu); - } while (!cpu->unplug || cpu_can_run(cpu)); - - kvm_destroy_vcpu(cpu); - cpu_thread_signal_destroyed(cpu); - qemu_mutex_unlock_iothread(); - rcu_unregister_thread(); - return NULL; -} - -static void kvm_start_vcpu_thread(CPUState *cpu) -{ - char thread_name[VCPU_THREAD_NAME_SIZE]; - - cpu->thread = g_malloc0(sizeof(QemuThread)); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); - qemu_cond_init(cpu->halt_cond); - snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/KVM", - cpu->cpu_index); - qemu_thread_create(cpu->thread, thread_name, kvm_vcpu_thread_fn, - cpu, QEMU_THREAD_JOINABLE); -} - -const CpusAccel kvm_cpus = { - .create_vcpu_thread = kvm_start_vcpu_thread, - - .synchronize_post_reset = kvm_cpu_synchronize_post_reset, - .synchronize_post_init = kvm_cpu_synchronize_post_init, - .synchronize_state = kvm_cpu_synchronize_state, - .synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm, -}; diff --git a/accel/kvm/kvm-cpus.h b/accel/kvm/kvm-cpus.h index 3df732b816..bf0bd1bee4 100644 --- a/accel/kvm/kvm-cpus.h +++ b/accel/kvm/kvm-cpus.h @@ -12,8 +12,6 @@ #include "sysemu/cpus.h" -extern const CpusAccel kvm_cpus; - int kvm_init_vcpu(CPUState *cpu, Error **errp); int kvm_cpu_exec(CPUState *cpu); void kvm_destroy_vcpu(CPUState *cpu); diff --git a/accel/kvm/meson.build b/accel/kvm/meson.build index 7e9dafe24c..8d219bea50 100644 --- a/accel/kvm/meson.build +++ b/accel/kvm/meson.build @@ -1,7 +1,7 @@ kvm_ss = ss.source_set() kvm_ss.add(files( 'kvm-all.c', - 'kvm-cpus.c', + 'kvm-accel-ops.c', )) kvm_ss.add(when: 'CONFIG_SEV', if_false: files('sev-stub.c')) diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index b4e731cb2b..edb29f6fa4 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -25,14 +25,8 @@ #include "qemu/main-loop.h" #include "hw/core/cpu.h" -const CpusAccel qtest_cpus = { - .create_vcpu_thread = dummy_start_vcpu_thread, - .get_virtual_clock = qtest_get_virtual_clock, -}; - static int qtest_init_accel(MachineState *ms) { - cpus_register_accel(&qtest_cpus); return 0; } @@ -52,9 +46,26 @@ static const TypeInfo qtest_accel_type = { .class_init = qtest_accel_class_init, }; +static void qtest_accel_ops_class_init(ObjectClass *oc, void *data) +{ + AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); + + ops->create_vcpu_thread = dummy_start_vcpu_thread; + ops->get_virtual_clock = qtest_get_virtual_clock; +}; + +static const TypeInfo qtest_accel_ops_type = { + .name = ACCEL_OPS_NAME("qtest"), + + .parent = TYPE_ACCEL_OPS, + .class_init = qtest_accel_ops_class_init, + .abstract = true, +}; + static void qtest_type_init(void) { type_register_static(&qtest_accel_type); + type_register_static(&qtest_accel_ops_type); } type_init(qtest_type_init); diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 424d9bb1fc..1236ac7b91 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -15,8 +15,8 @@ specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss) specific_ss.add(when: ['CONFIG_SOFTMMU', 'CONFIG_TCG'], if_true: files( 'cputlb.c', - 'tcg-cpus.c', - 'tcg-cpus-mttcg.c', - 'tcg-cpus-icount.c', - 'tcg-cpus-rr.c' + 'tcg-accel-ops.c', + 'tcg-accel-ops-mttcg.c', + 'tcg-accel-ops-icount.c', + 'tcg-accel-ops-rr.c' )) diff --git a/accel/tcg/tcg-accel-ops-icount.c b/accel/tcg/tcg-accel-ops-icount.c new file mode 100644 index 0000000000..87762b469c --- /dev/null +++ b/accel/tcg/tcg-accel-ops-icount.c @@ -0,0 +1,138 @@ +/* + * QEMU TCG Single Threaded vCPUs implementation using instruction counting + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "sysemu/tcg.h" +#include "sysemu/replay.h" +#include "qemu/main-loop.h" +#include "qemu/guest-random.h" +#include "exec/exec-all.h" +#include "hw/boards.h" + +#include "tcg-accel-ops.h" +#include "tcg-accel-ops-icount.h" +#include "tcg-accel-ops-rr.h" + +static int64_t icount_get_limit(void) +{ + int64_t deadline; + + if (replay_mode != REPLAY_MODE_PLAY) { + /* + * Include all the timers, because they may need an attention. + * Too long CPU execution may create unnecessary delay in UI. + */ + deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, + QEMU_TIMER_ATTR_ALL); + /* Check realtime timers, because they help with input processing */ + deadline = qemu_soonest_timeout(deadline, + qemu_clock_deadline_ns_all(QEMU_CLOCK_REALTIME, + QEMU_TIMER_ATTR_ALL)); + + /* + * Maintain prior (possibly buggy) behaviour where if no deadline + * was set (as there is no QEMU_CLOCK_VIRTUAL timer) or it is more than + * INT32_MAX nanoseconds ahead, we still use INT32_MAX + * nanoseconds. + */ + if ((deadline < 0) || (deadline > INT32_MAX)) { + deadline = INT32_MAX; + } + + return icount_round(deadline); + } else { + return replay_get_instructions(); + } +} + +static void icount_notify_aio_contexts(void) +{ + /* Wake up other AioContexts. */ + qemu_clock_notify(QEMU_CLOCK_VIRTUAL); + qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); +} + +void icount_handle_deadline(void) +{ + assert(qemu_in_vcpu_thread()); + int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, + QEMU_TIMER_ATTR_ALL); + + if (deadline == 0) { + icount_notify_aio_contexts(); + } +} + +void icount_prepare_for_run(CPUState *cpu) +{ + int insns_left; + + /* + * These should always be cleared by icount_process_data after + * each vCPU execution. However u16.high can be raised + * asynchronously by cpu_exit/cpu_interrupt/tcg_handle_interrupt + */ + g_assert(cpu_neg(cpu)->icount_decr.u16.low == 0); + g_assert(cpu->icount_extra == 0); + + cpu->icount_budget = icount_get_limit(); + insns_left = MIN(0xffff, cpu->icount_budget); + cpu_neg(cpu)->icount_decr.u16.low = insns_left; + cpu->icount_extra = cpu->icount_budget - insns_left; + + replay_mutex_lock(); + + if (cpu->icount_budget == 0 && replay_has_checkpoint()) { + icount_notify_aio_contexts(); + } +} + +void icount_process_data(CPUState *cpu) +{ + /* Account for executed instructions */ + icount_update(cpu); + + /* Reset the counters */ + cpu_neg(cpu)->icount_decr.u16.low = 0; + cpu->icount_extra = 0; + cpu->icount_budget = 0; + + replay_account_executed_instructions(); + + replay_mutex_unlock(); +} + +void icount_handle_interrupt(CPUState *cpu, int mask) +{ + int old_mask = cpu->interrupt_request; + + tcg_handle_interrupt(cpu, mask); + if (qemu_cpu_is_self(cpu) && + !cpu->can_do_io + && (mask & ~old_mask) != 0) { + cpu_abort(cpu, "Raised interrupt while not in I/O function"); + } +} diff --git a/accel/tcg/tcg-accel-ops-icount.h b/accel/tcg/tcg-accel-ops-icount.h new file mode 100644 index 0000000000..d884aa2aaa --- /dev/null +++ b/accel/tcg/tcg-accel-ops-icount.h @@ -0,0 +1,19 @@ +/* + * QEMU TCG Single Threaded vCPUs implementation using instruction counting + * + * Copyright 2020 SUSE LLC + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TCG_CPUS_ICOUNT_H +#define TCG_CPUS_ICOUNT_H + +void icount_handle_deadline(void); +void icount_prepare_for_run(CPUState *cpu); +void icount_process_data(CPUState *cpu); + +void icount_handle_interrupt(CPUState *cpu, int mask); + +#endif /* TCG_CPUS_ICOUNT_H */ diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c new file mode 100644 index 0000000000..42973fb062 --- /dev/null +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -0,0 +1,134 @@ +/* + * QEMU TCG Multi Threaded vCPUs implementation + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "sysemu/tcg.h" +#include "sysemu/replay.h" +#include "qemu/main-loop.h" +#include "qemu/guest-random.h" +#include "exec/exec-all.h" +#include "hw/boards.h" + +#include "tcg-accel-ops.h" +#include "tcg-accel-ops-mttcg.h" + +/* + * In the multi-threaded case each vCPU has its own thread. The TLS + * variable current_cpu can be used deep in the code to find the + * current CPUState for a given thread. + */ + +static void *mttcg_cpu_thread_fn(void *arg) +{ + CPUState *cpu = arg; + + assert(tcg_enabled()); + g_assert(!icount_enabled()); + + rcu_register_thread(); + tcg_register_thread(); + + qemu_mutex_lock_iothread(); + qemu_thread_get_self(cpu->thread); + + cpu->thread_id = qemu_get_thread_id(); + cpu->can_do_io = 1; + current_cpu = cpu; + cpu_thread_signal_created(cpu); + qemu_guest_random_seed_thread_part2(cpu->random_seed); + + /* process any pending work */ + cpu->exit_request = 1; + + do { + if (cpu_can_run(cpu)) { + int r; + qemu_mutex_unlock_iothread(); + r = tcg_cpus_exec(cpu); + qemu_mutex_lock_iothread(); + switch (r) { + case EXCP_DEBUG: + cpu_handle_guest_debug(cpu); + break; + case EXCP_HALTED: + /* + * during start-up the vCPU is reset and the thread is + * kicked several times. If we don't ensure we go back + * to sleep in the halted state we won't cleanly + * start-up when the vCPU is enabled. + * + * cpu->halted should ensure we sleep in wait_io_event + */ + g_assert(cpu->halted); + break; + case EXCP_ATOMIC: + qemu_mutex_unlock_iothread(); + cpu_exec_step_atomic(cpu); + qemu_mutex_lock_iothread(); + default: + /* Ignore everything else? */ + break; + } + } + + qatomic_mb_set(&cpu->exit_request, 0); + qemu_wait_io_event(cpu); + } while (!cpu->unplug || cpu_can_run(cpu)); + + tcg_cpus_destroy(cpu); + qemu_mutex_unlock_iothread(); + rcu_unregister_thread(); + return NULL; +} + +void mttcg_kick_vcpu_thread(CPUState *cpu) +{ + cpu_exit(cpu); +} + +void mttcg_start_vcpu_thread(CPUState *cpu) +{ + char thread_name[VCPU_THREAD_NAME_SIZE]; + + g_assert(tcg_enabled()); + + parallel_cpus = (current_machine->smp.max_cpus > 1); + + cpu->thread = g_malloc0(sizeof(QemuThread)); + cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + qemu_cond_init(cpu->halt_cond); + + /* create a thread per vCPU with TCG (MTTCG) */ + snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG", + cpu->cpu_index); + + qemu_thread_create(cpu->thread, thread_name, mttcg_cpu_thread_fn, + cpu, QEMU_THREAD_JOINABLE); + +#ifdef _WIN32 + cpu->hThread = qemu_thread_get_handle(cpu->thread); +#endif +} diff --git a/accel/tcg/tcg-accel-ops-mttcg.h b/accel/tcg/tcg-accel-ops-mttcg.h new file mode 100644 index 0000000000..9fdc5a2ab5 --- /dev/null +++ b/accel/tcg/tcg-accel-ops-mttcg.h @@ -0,0 +1,19 @@ +/* + * QEMU TCG Multi Threaded vCPUs implementation + * + * Copyright 2021 SUSE LLC + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TCG_CPUS_MTTCG_H +#define TCG_CPUS_MTTCG_H + +/* kick MTTCG vCPU thread */ +void mttcg_kick_vcpu_thread(CPUState *cpu); + +/* start an mttcg vCPU thread */ +void mttcg_start_vcpu_thread(CPUState *cpu); + +#endif /* TCG_CPUS_MTTCG_H */ diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c new file mode 100644 index 0000000000..4a66055e0d --- /dev/null +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -0,0 +1,298 @@ +/* + * QEMU TCG Single Threaded vCPUs implementation + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "sysemu/tcg.h" +#include "sysemu/replay.h" +#include "qemu/main-loop.h" +#include "qemu/guest-random.h" +#include "exec/exec-all.h" +#include "hw/boards.h" + +#include "tcg-accel-ops.h" +#include "tcg-accel-ops-rr.h" +#include "tcg-accel-ops-icount.h" + +/* Kick all RR vCPUs */ +void rr_kick_vcpu_thread(CPUState *unused) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + cpu_exit(cpu); + }; +} + +/* + * TCG vCPU kick timer + * + * The kick timer is responsible for moving single threaded vCPU + * emulation on to the next vCPU. If more than one vCPU is running a + * timer event with force a cpu->exit so the next vCPU can get + * scheduled. + * + * The timer is removed if all vCPUs are idle and restarted again once + * idleness is complete. + */ + +static QEMUTimer *rr_kick_vcpu_timer; +static CPUState *rr_current_cpu; + +#define TCG_KICK_PERIOD (NANOSECONDS_PER_SECOND / 10) + +static inline int64_t rr_next_kick_time(void) +{ + return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + TCG_KICK_PERIOD; +} + +/* Kick the currently round-robin scheduled vCPU to next */ +static void rr_kick_next_cpu(void) +{ + CPUState *cpu; + do { + cpu = qatomic_mb_read(&rr_current_cpu); + if (cpu) { + cpu_exit(cpu); + } + } while (cpu != qatomic_mb_read(&rr_current_cpu)); +} + +static void rr_kick_thread(void *opaque) +{ + timer_mod(rr_kick_vcpu_timer, rr_next_kick_time()); + rr_kick_next_cpu(); +} + +static void rr_start_kick_timer(void) +{ + if (!rr_kick_vcpu_timer && CPU_NEXT(first_cpu)) { + rr_kick_vcpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + rr_kick_thread, NULL); + } + if (rr_kick_vcpu_timer && !timer_pending(rr_kick_vcpu_timer)) { + timer_mod(rr_kick_vcpu_timer, rr_next_kick_time()); + } +} + +static void rr_stop_kick_timer(void) +{ + if (rr_kick_vcpu_timer && timer_pending(rr_kick_vcpu_timer)) { + timer_del(rr_kick_vcpu_timer); + } +} + +static void rr_wait_io_event(void) +{ + CPUState *cpu; + + while (all_cpu_threads_idle()) { + rr_stop_kick_timer(); + qemu_cond_wait_iothread(first_cpu->halt_cond); + } + + rr_start_kick_timer(); + + CPU_FOREACH(cpu) { + qemu_wait_io_event_common(cpu); + } +} + +/* + * Destroy any remaining vCPUs which have been unplugged and have + * finished running + */ +static void rr_deal_with_unplugged_cpus(void) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + if (cpu->unplug && !cpu_can_run(cpu)) { + tcg_cpus_destroy(cpu); + break; + } + } +} + +/* + * In the single-threaded case each vCPU is simulated in turn. If + * there is more than a single vCPU we create a simple timer to kick + * the vCPU and ensure we don't get stuck in a tight loop in one vCPU. + * This is done explicitly rather than relying on side-effects + * elsewhere. + */ + +static void *rr_cpu_thread_fn(void *arg) +{ + CPUState *cpu = arg; + + assert(tcg_enabled()); + rcu_register_thread(); + tcg_register_thread(); + + qemu_mutex_lock_iothread(); + qemu_thread_get_self(cpu->thread); + + cpu->thread_id = qemu_get_thread_id(); + cpu->can_do_io = 1; + cpu_thread_signal_created(cpu); + qemu_guest_random_seed_thread_part2(cpu->random_seed); + + /* wait for initial kick-off after machine start */ + while (first_cpu->stopped) { + qemu_cond_wait_iothread(first_cpu->halt_cond); + + /* process any pending work */ + CPU_FOREACH(cpu) { + current_cpu = cpu; + qemu_wait_io_event_common(cpu); + } + } + + rr_start_kick_timer(); + + cpu = first_cpu; + + /* process any pending work */ + cpu->exit_request = 1; + + while (1) { + qemu_mutex_unlock_iothread(); + replay_mutex_lock(); + qemu_mutex_lock_iothread(); + + if (icount_enabled()) { + /* Account partial waits to QEMU_CLOCK_VIRTUAL. */ + icount_account_warp_timer(); + /* + * Run the timers here. This is much more efficient than + * waking up the I/O thread and waiting for completion. + */ + icount_handle_deadline(); + } + + replay_mutex_unlock(); + + if (!cpu) { + cpu = first_cpu; + } + + while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) { + + qatomic_mb_set(&rr_current_cpu, cpu); + current_cpu = cpu; + + qemu_clock_enable(QEMU_CLOCK_VIRTUAL, + (cpu->singlestep_enabled & SSTEP_NOTIMER) == 0); + + if (cpu_can_run(cpu)) { + int r; + + qemu_mutex_unlock_iothread(); + if (icount_enabled()) { + icount_prepare_for_run(cpu); + } + r = tcg_cpus_exec(cpu); + if (icount_enabled()) { + icount_process_data(cpu); + } + qemu_mutex_lock_iothread(); + + if (r == EXCP_DEBUG) { + cpu_handle_guest_debug(cpu); + break; + } else if (r == EXCP_ATOMIC) { + qemu_mutex_unlock_iothread(); + cpu_exec_step_atomic(cpu); + qemu_mutex_lock_iothread(); + break; + } + } else if (cpu->stop) { + if (cpu->unplug) { + cpu = CPU_NEXT(cpu); + } + break; + } + + cpu = CPU_NEXT(cpu); + } /* while (cpu && !cpu->exit_request).. */ + + /* Does not need qatomic_mb_set because a spurious wakeup is okay. */ + qatomic_set(&rr_current_cpu, NULL); + + if (cpu && cpu->exit_request) { + qatomic_mb_set(&cpu->exit_request, 0); + } + + if (icount_enabled() && all_cpu_threads_idle()) { + /* + * When all cpus are sleeping (e.g in WFI), to avoid a deadlock + * in the main_loop, wake it up in order to start the warp timer. + */ + qemu_notify_event(); + } + + rr_wait_io_event(); + rr_deal_with_unplugged_cpus(); + } + + rcu_unregister_thread(); + return NULL; +} + +void rr_start_vcpu_thread(CPUState *cpu) +{ + char thread_name[VCPU_THREAD_NAME_SIZE]; + static QemuCond *single_tcg_halt_cond; + static QemuThread *single_tcg_cpu_thread; + + g_assert(tcg_enabled()); + parallel_cpus = false; + + if (!single_tcg_cpu_thread) { + cpu->thread = g_malloc0(sizeof(QemuThread)); + cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + qemu_cond_init(cpu->halt_cond); + + /* share a single thread for all cpus with TCG */ + snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "ALL CPUs/TCG"); + qemu_thread_create(cpu->thread, thread_name, + rr_cpu_thread_fn, + cpu, QEMU_THREAD_JOINABLE); + + single_tcg_halt_cond = cpu->halt_cond; + single_tcg_cpu_thread = cpu->thread; +#ifdef _WIN32 + cpu->hThread = qemu_thread_get_handle(cpu->thread); +#endif + } else { + /* we share the thread */ + cpu->thread = single_tcg_cpu_thread; + cpu->halt_cond = single_tcg_halt_cond; + cpu->thread_id = first_cpu->thread_id; + cpu->can_do_io = 1; + cpu->created = true; + } +} diff --git a/accel/tcg/tcg-accel-ops-rr.h b/accel/tcg/tcg-accel-ops-rr.h new file mode 100644 index 0000000000..54f6ae6e86 --- /dev/null +++ b/accel/tcg/tcg-accel-ops-rr.h @@ -0,0 +1,21 @@ +/* + * QEMU TCG Single Threaded vCPUs implementation + * + * Copyright 2020 SUSE LLC + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TCG_CPUS_RR_H +#define TCG_CPUS_RR_H + +#define TCG_KICK_PERIOD (NANOSECONDS_PER_SECOND / 10) + +/* Kick all RR vCPUs. */ +void rr_kick_vcpu_thread(CPUState *unused); + +/* start the round robin vcpu thread */ +void rr_start_vcpu_thread(CPUState *cpu); + +#endif /* TCG_CPUS_RR_H */ diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c new file mode 100644 index 0000000000..6144d9df87 --- /dev/null +++ b/accel/tcg/tcg-accel-ops.c @@ -0,0 +1,125 @@ +/* + * QEMU TCG vCPU common functionality + * + * Functionality common to all TCG vCPU variants: mttcg, rr and icount. + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "sysemu/tcg.h" +#include "sysemu/replay.h" +#include "qemu/main-loop.h" +#include "qemu/guest-random.h" +#include "exec/exec-all.h" +#include "hw/boards.h" + +#include "tcg-accel-ops.h" +#include "tcg-accel-ops-mttcg.h" +#include "tcg-accel-ops-rr.h" +#include "tcg-accel-ops-icount.h" + +/* common functionality among all TCG variants */ + +void tcg_cpus_destroy(CPUState *cpu) +{ + cpu_thread_signal_destroyed(cpu); +} + +int tcg_cpus_exec(CPUState *cpu) +{ + int ret; +#ifdef CONFIG_PROFILER + int64_t ti; +#endif + assert(tcg_enabled()); +#ifdef CONFIG_PROFILER + ti = profile_getclock(); +#endif + cpu_exec_start(cpu); + ret = cpu_exec(cpu); + cpu_exec_end(cpu); +#ifdef CONFIG_PROFILER + qatomic_set(&tcg_ctx->prof.cpu_exec_time, + tcg_ctx->prof.cpu_exec_time + profile_getclock() - ti); +#endif + return ret; +} + +/* mask must never be zero, except for A20 change call */ +void tcg_handle_interrupt(CPUState *cpu, int mask) +{ + g_assert(qemu_mutex_iothread_locked()); + + cpu->interrupt_request |= mask; + + /* + * If called from iothread context, wake the target cpu in + * case its halted. + */ + if (!qemu_cpu_is_self(cpu)) { + qemu_cpu_kick(cpu); + } else { + qatomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1); + } +} + +static void tcg_accel_ops_init(AccelOpsClass *ops) +{ + if (qemu_tcg_mttcg_enabled()) { + ops->create_vcpu_thread = mttcg_start_vcpu_thread; + ops->kick_vcpu_thread = mttcg_kick_vcpu_thread; + ops->handle_interrupt = tcg_handle_interrupt; + } else if (icount_enabled()) { + ops->create_vcpu_thread = rr_start_vcpu_thread; + ops->kick_vcpu_thread = rr_kick_vcpu_thread; + ops->handle_interrupt = icount_handle_interrupt; + ops->get_virtual_clock = icount_get; + ops->get_elapsed_ticks = icount_get; + } else { + ops->create_vcpu_thread = rr_start_vcpu_thread; + ops->kick_vcpu_thread = rr_kick_vcpu_thread; + ops->handle_interrupt = tcg_handle_interrupt; + } +} + +static void tcg_accel_ops_class_init(ObjectClass *oc, void *data) +{ + AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); + + ops->ops_init = tcg_accel_ops_init; +} + +static const TypeInfo tcg_accel_ops_type = { + .name = ACCEL_OPS_NAME("tcg"), + + .parent = TYPE_ACCEL_OPS, + .class_init = tcg_accel_ops_class_init, + .abstract = true, +}; + +static void tcg_accel_ops_register_types(void) +{ + type_register_static(&tcg_accel_ops_type); +} +type_init(tcg_accel_ops_register_types); diff --git a/accel/tcg/tcg-accel-ops.h b/accel/tcg/tcg-accel-ops.h new file mode 100644 index 0000000000..48130006de --- /dev/null +++ b/accel/tcg/tcg-accel-ops.h @@ -0,0 +1,21 @@ +/* + * QEMU TCG vCPU common functionality + * + * Functionality common to all TCG vcpu variants: mttcg, rr and icount. + * + * Copyright 2020 SUSE LLC + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef TCG_CPUS_H +#define TCG_CPUS_H + +#include "sysemu/cpus.h" + +void tcg_cpus_destroy(CPUState *cpu); +int tcg_cpus_exec(CPUState *cpu); +void tcg_handle_interrupt(CPUState *cpu, int mask); + +#endif /* TCG_CPUS_H */ diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 642a7b94a7..e378c2db73 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -33,10 +33,6 @@ #include "qemu/accel.h" #include "qapi/qapi-builtin-visit.h" -#ifndef CONFIG_USER_ONLY -#include "tcg-cpus.h" -#endif /* CONFIG_USER_ONLY */ - struct TCGState { AccelState parent_obj; @@ -124,14 +120,6 @@ static int tcg_init(MachineState *ms) */ #ifndef CONFIG_USER_ONLY tcg_region_init(); - - if (mttcg_enabled) { - cpus_register_accel(&tcg_cpus_mttcg); - } else if (icount_enabled()) { - cpus_register_accel(&tcg_cpus_icount); - } else { - cpus_register_accel(&tcg_cpus_rr); - } #endif /* !CONFIG_USER_ONLY */ return 0; diff --git a/accel/tcg/tcg-cpus-icount.c b/accel/tcg/tcg-cpus-icount.c deleted file mode 100644 index 9f45432275..0000000000 --- a/accel/tcg/tcg-cpus-icount.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * QEMU TCG Single Threaded vCPUs implementation using instruction counting - * - * Copyright (c) 2003-2008 Fabrice Bellard - * Copyright (c) 2014 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "sysemu/tcg.h" -#include "sysemu/replay.h" -#include "qemu/main-loop.h" -#include "qemu/guest-random.h" -#include "exec/exec-all.h" -#include "hw/boards.h" - -#include "tcg-cpus.h" -#include "tcg-cpus-icount.h" -#include "tcg-cpus-rr.h" - -static int64_t icount_get_limit(void) -{ - int64_t deadline; - - if (replay_mode != REPLAY_MODE_PLAY) { - /* - * Include all the timers, because they may need an attention. - * Too long CPU execution may create unnecessary delay in UI. - */ - deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, - QEMU_TIMER_ATTR_ALL); - /* Check realtime timers, because they help with input processing */ - deadline = qemu_soonest_timeout(deadline, - qemu_clock_deadline_ns_all(QEMU_CLOCK_REALTIME, - QEMU_TIMER_ATTR_ALL)); - - /* - * Maintain prior (possibly buggy) behaviour where if no deadline - * was set (as there is no QEMU_CLOCK_VIRTUAL timer) or it is more than - * INT32_MAX nanoseconds ahead, we still use INT32_MAX - * nanoseconds. - */ - if ((deadline < 0) || (deadline > INT32_MAX)) { - deadline = INT32_MAX; - } - - return icount_round(deadline); - } else { - return replay_get_instructions(); - } -} - -static void icount_notify_aio_contexts(void) -{ - /* Wake up other AioContexts. */ - qemu_clock_notify(QEMU_CLOCK_VIRTUAL); - qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); -} - -void icount_handle_deadline(void) -{ - assert(qemu_in_vcpu_thread()); - int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, - QEMU_TIMER_ATTR_ALL); - - if (deadline == 0) { - icount_notify_aio_contexts(); - } -} - -void icount_prepare_for_run(CPUState *cpu) -{ - int insns_left; - - /* - * These should always be cleared by icount_process_data after - * each vCPU execution. However u16.high can be raised - * asynchronously by cpu_exit/cpu_interrupt/tcg_cpus_handle_interrupt - */ - g_assert(cpu_neg(cpu)->icount_decr.u16.low == 0); - g_assert(cpu->icount_extra == 0); - - cpu->icount_budget = icount_get_limit(); - insns_left = MIN(0xffff, cpu->icount_budget); - cpu_neg(cpu)->icount_decr.u16.low = insns_left; - cpu->icount_extra = cpu->icount_budget - insns_left; - - replay_mutex_lock(); - - if (cpu->icount_budget == 0 && replay_has_checkpoint()) { - icount_notify_aio_contexts(); - } -} - -void icount_process_data(CPUState *cpu) -{ - /* Account for executed instructions */ - icount_update(cpu); - - /* Reset the counters */ - cpu_neg(cpu)->icount_decr.u16.low = 0; - cpu->icount_extra = 0; - cpu->icount_budget = 0; - - replay_account_executed_instructions(); - - replay_mutex_unlock(); -} - -static void icount_handle_interrupt(CPUState *cpu, int mask) -{ - int old_mask = cpu->interrupt_request; - - tcg_cpus_handle_interrupt(cpu, mask); - if (qemu_cpu_is_self(cpu) && - !cpu->can_do_io - && (mask & ~old_mask) != 0) { - cpu_abort(cpu, "Raised interrupt while not in I/O function"); - } -} - -const CpusAccel tcg_cpus_icount = { - .create_vcpu_thread = rr_start_vcpu_thread, - .kick_vcpu_thread = rr_kick_vcpu_thread, - - .handle_interrupt = icount_handle_interrupt, - .get_virtual_clock = icount_get, - .get_elapsed_ticks = icount_get, -}; diff --git a/accel/tcg/tcg-cpus-icount.h b/accel/tcg/tcg-cpus-icount.h deleted file mode 100644 index b695939dfa..0000000000 --- a/accel/tcg/tcg-cpus-icount.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * QEMU TCG Single Threaded vCPUs implementation using instruction counting - * - * Copyright 2020 SUSE LLC - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef TCG_CPUS_ICOUNT_H -#define TCG_CPUS_ICOUNT_H - -void icount_handle_deadline(void); -void icount_prepare_for_run(CPUState *cpu); -void icount_process_data(CPUState *cpu); - -#endif /* TCG_CPUS_ICOUNT_H */ diff --git a/accel/tcg/tcg-cpus-mttcg.c b/accel/tcg/tcg-cpus-mttcg.c deleted file mode 100644 index 9c3767d260..0000000000 --- a/accel/tcg/tcg-cpus-mttcg.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * QEMU TCG Multi Threaded vCPUs implementation - * - * Copyright (c) 2003-2008 Fabrice Bellard - * Copyright (c) 2014 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "sysemu/tcg.h" -#include "sysemu/replay.h" -#include "qemu/main-loop.h" -#include "qemu/guest-random.h" -#include "exec/exec-all.h" -#include "hw/boards.h" - -#include "tcg-cpus.h" - -/* - * In the multi-threaded case each vCPU has its own thread. The TLS - * variable current_cpu can be used deep in the code to find the - * current CPUState for a given thread. - */ - -static void *mttcg_cpu_thread_fn(void *arg) -{ - CPUState *cpu = arg; - - assert(tcg_enabled()); - g_assert(!icount_enabled()); - - rcu_register_thread(); - tcg_register_thread(); - - qemu_mutex_lock_iothread(); - qemu_thread_get_self(cpu->thread); - - cpu->thread_id = qemu_get_thread_id(); - cpu->can_do_io = 1; - current_cpu = cpu; - cpu_thread_signal_created(cpu); - qemu_guest_random_seed_thread_part2(cpu->random_seed); - - /* process any pending work */ - cpu->exit_request = 1; - - do { - if (cpu_can_run(cpu)) { - int r; - qemu_mutex_unlock_iothread(); - r = tcg_cpus_exec(cpu); - qemu_mutex_lock_iothread(); - switch (r) { - case EXCP_DEBUG: - cpu_handle_guest_debug(cpu); - break; - case EXCP_HALTED: - /* - * during start-up the vCPU is reset and the thread is - * kicked several times. If we don't ensure we go back - * to sleep in the halted state we won't cleanly - * start-up when the vCPU is enabled. - * - * cpu->halted should ensure we sleep in wait_io_event - */ - g_assert(cpu->halted); - break; - case EXCP_ATOMIC: - qemu_mutex_unlock_iothread(); - cpu_exec_step_atomic(cpu); - qemu_mutex_lock_iothread(); - default: - /* Ignore everything else? */ - break; - } - } - - qatomic_mb_set(&cpu->exit_request, 0); - qemu_wait_io_event(cpu); - } while (!cpu->unplug || cpu_can_run(cpu)); - - tcg_cpus_destroy(cpu); - qemu_mutex_unlock_iothread(); - rcu_unregister_thread(); - return NULL; -} - -static void mttcg_kick_vcpu_thread(CPUState *cpu) -{ - cpu_exit(cpu); -} - -static void mttcg_start_vcpu_thread(CPUState *cpu) -{ - char thread_name[VCPU_THREAD_NAME_SIZE]; - - g_assert(tcg_enabled()); - - parallel_cpus = (current_machine->smp.max_cpus > 1); - - cpu->thread = g_malloc0(sizeof(QemuThread)); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); - qemu_cond_init(cpu->halt_cond); - - /* create a thread per vCPU with TCG (MTTCG) */ - snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG", - cpu->cpu_index); - - qemu_thread_create(cpu->thread, thread_name, mttcg_cpu_thread_fn, - cpu, QEMU_THREAD_JOINABLE); - -#ifdef _WIN32 - cpu->hThread = qemu_thread_get_handle(cpu->thread); -#endif -} - -const CpusAccel tcg_cpus_mttcg = { - .create_vcpu_thread = mttcg_start_vcpu_thread, - .kick_vcpu_thread = mttcg_kick_vcpu_thread, - - .handle_interrupt = tcg_cpus_handle_interrupt, -}; diff --git a/accel/tcg/tcg-cpus-rr.c b/accel/tcg/tcg-cpus-rr.c deleted file mode 100644 index 0181d2e4eb..0000000000 --- a/accel/tcg/tcg-cpus-rr.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * QEMU TCG Single Threaded vCPUs implementation - * - * Copyright (c) 2003-2008 Fabrice Bellard - * Copyright (c) 2014 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "sysemu/tcg.h" -#include "sysemu/replay.h" -#include "qemu/main-loop.h" -#include "qemu/guest-random.h" -#include "exec/exec-all.h" -#include "hw/boards.h" - -#include "tcg-cpus.h" -#include "tcg-cpus-rr.h" -#include "tcg-cpus-icount.h" - -/* Kick all RR vCPUs */ -void rr_kick_vcpu_thread(CPUState *unused) -{ - CPUState *cpu; - - CPU_FOREACH(cpu) { - cpu_exit(cpu); - }; -} - -/* - * TCG vCPU kick timer - * - * The kick timer is responsible for moving single threaded vCPU - * emulation on to the next vCPU. If more than one vCPU is running a - * timer event with force a cpu->exit so the next vCPU can get - * scheduled. - * - * The timer is removed if all vCPUs are idle and restarted again once - * idleness is complete. - */ - -static QEMUTimer *rr_kick_vcpu_timer; -static CPUState *rr_current_cpu; - -#define TCG_KICK_PERIOD (NANOSECONDS_PER_SECOND / 10) - -static inline int64_t rr_next_kick_time(void) -{ - return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + TCG_KICK_PERIOD; -} - -/* Kick the currently round-robin scheduled vCPU to next */ -static void rr_kick_next_cpu(void) -{ - CPUState *cpu; - do { - cpu = qatomic_mb_read(&rr_current_cpu); - if (cpu) { - cpu_exit(cpu); - } - } while (cpu != qatomic_mb_read(&rr_current_cpu)); -} - -static void rr_kick_thread(void *opaque) -{ - timer_mod(rr_kick_vcpu_timer, rr_next_kick_time()); - rr_kick_next_cpu(); -} - -static void rr_start_kick_timer(void) -{ - if (!rr_kick_vcpu_timer && CPU_NEXT(first_cpu)) { - rr_kick_vcpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - rr_kick_thread, NULL); - } - if (rr_kick_vcpu_timer && !timer_pending(rr_kick_vcpu_timer)) { - timer_mod(rr_kick_vcpu_timer, rr_next_kick_time()); - } -} - -static void rr_stop_kick_timer(void) -{ - if (rr_kick_vcpu_timer && timer_pending(rr_kick_vcpu_timer)) { - timer_del(rr_kick_vcpu_timer); - } -} - -static void rr_wait_io_event(void) -{ - CPUState *cpu; - - while (all_cpu_threads_idle()) { - rr_stop_kick_timer(); - qemu_cond_wait_iothread(first_cpu->halt_cond); - } - - rr_start_kick_timer(); - - CPU_FOREACH(cpu) { - qemu_wait_io_event_common(cpu); - } -} - -/* - * Destroy any remaining vCPUs which have been unplugged and have - * finished running - */ -static void rr_deal_with_unplugged_cpus(void) -{ - CPUState *cpu; - - CPU_FOREACH(cpu) { - if (cpu->unplug && !cpu_can_run(cpu)) { - tcg_cpus_destroy(cpu); - break; - } - } -} - -/* - * In the single-threaded case each vCPU is simulated in turn. If - * there is more than a single vCPU we create a simple timer to kick - * the vCPU and ensure we don't get stuck in a tight loop in one vCPU. - * This is done explicitly rather than relying on side-effects - * elsewhere. - */ - -static void *rr_cpu_thread_fn(void *arg) -{ - CPUState *cpu = arg; - - assert(tcg_enabled()); - rcu_register_thread(); - tcg_register_thread(); - - qemu_mutex_lock_iothread(); - qemu_thread_get_self(cpu->thread); - - cpu->thread_id = qemu_get_thread_id(); - cpu->can_do_io = 1; - cpu_thread_signal_created(cpu); - qemu_guest_random_seed_thread_part2(cpu->random_seed); - - /* wait for initial kick-off after machine start */ - while (first_cpu->stopped) { - qemu_cond_wait_iothread(first_cpu->halt_cond); - - /* process any pending work */ - CPU_FOREACH(cpu) { - current_cpu = cpu; - qemu_wait_io_event_common(cpu); - } - } - - rr_start_kick_timer(); - - cpu = first_cpu; - - /* process any pending work */ - cpu->exit_request = 1; - - while (1) { - qemu_mutex_unlock_iothread(); - replay_mutex_lock(); - qemu_mutex_lock_iothread(); - - if (icount_enabled()) { - /* Account partial waits to QEMU_CLOCK_VIRTUAL. */ - icount_account_warp_timer(); - /* - * Run the timers here. This is much more efficient than - * waking up the I/O thread and waiting for completion. - */ - icount_handle_deadline(); - } - - replay_mutex_unlock(); - - if (!cpu) { - cpu = first_cpu; - } - - while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) { - - qatomic_mb_set(&rr_current_cpu, cpu); - current_cpu = cpu; - - qemu_clock_enable(QEMU_CLOCK_VIRTUAL, - (cpu->singlestep_enabled & SSTEP_NOTIMER) == 0); - - if (cpu_can_run(cpu)) { - int r; - - qemu_mutex_unlock_iothread(); - if (icount_enabled()) { - icount_prepare_for_run(cpu); - } - r = tcg_cpus_exec(cpu); - if (icount_enabled()) { - icount_process_data(cpu); - } - qemu_mutex_lock_iothread(); - - if (r == EXCP_DEBUG) { - cpu_handle_guest_debug(cpu); - break; - } else if (r == EXCP_ATOMIC) { - qemu_mutex_unlock_iothread(); - cpu_exec_step_atomic(cpu); - qemu_mutex_lock_iothread(); - break; - } - } else if (cpu->stop) { - if (cpu->unplug) { - cpu = CPU_NEXT(cpu); - } - break; - } - - cpu = CPU_NEXT(cpu); - } /* while (cpu && !cpu->exit_request).. */ - - /* Does not need qatomic_mb_set because a spurious wakeup is okay. */ - qatomic_set(&rr_current_cpu, NULL); - - if (cpu && cpu->exit_request) { - qatomic_mb_set(&cpu->exit_request, 0); - } - - if (icount_enabled() && all_cpu_threads_idle()) { - /* - * When all cpus are sleeping (e.g in WFI), to avoid a deadlock - * in the main_loop, wake it up in order to start the warp timer. - */ - qemu_notify_event(); - } - - rr_wait_io_event(); - rr_deal_with_unplugged_cpus(); - } - - rcu_unregister_thread(); - return NULL; -} - -void rr_start_vcpu_thread(CPUState *cpu) -{ - char thread_name[VCPU_THREAD_NAME_SIZE]; - static QemuCond *single_tcg_halt_cond; - static QemuThread *single_tcg_cpu_thread; - - g_assert(tcg_enabled()); - parallel_cpus = false; - - if (!single_tcg_cpu_thread) { - cpu->thread = g_malloc0(sizeof(QemuThread)); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); - qemu_cond_init(cpu->halt_cond); - - /* share a single thread for all cpus with TCG */ - snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "ALL CPUs/TCG"); - qemu_thread_create(cpu->thread, thread_name, - rr_cpu_thread_fn, - cpu, QEMU_THREAD_JOINABLE); - - single_tcg_halt_cond = cpu->halt_cond; - single_tcg_cpu_thread = cpu->thread; -#ifdef _WIN32 - cpu->hThread = qemu_thread_get_handle(cpu->thread); -#endif - } else { - /* we share the thread */ - cpu->thread = single_tcg_cpu_thread; - cpu->halt_cond = single_tcg_halt_cond; - cpu->thread_id = first_cpu->thread_id; - cpu->can_do_io = 1; - cpu->created = true; - } -} - -const CpusAccel tcg_cpus_rr = { - .create_vcpu_thread = rr_start_vcpu_thread, - .kick_vcpu_thread = rr_kick_vcpu_thread, - - .handle_interrupt = tcg_cpus_handle_interrupt, -}; diff --git a/accel/tcg/tcg-cpus-rr.h b/accel/tcg/tcg-cpus-rr.h deleted file mode 100644 index 54f6ae6e86..0000000000 --- a/accel/tcg/tcg-cpus-rr.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * QEMU TCG Single Threaded vCPUs implementation - * - * Copyright 2020 SUSE LLC - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef TCG_CPUS_RR_H -#define TCG_CPUS_RR_H - -#define TCG_KICK_PERIOD (NANOSECONDS_PER_SECOND / 10) - -/* Kick all RR vCPUs. */ -void rr_kick_vcpu_thread(CPUState *unused); - -/* start the round robin vcpu thread */ -void rr_start_vcpu_thread(CPUState *cpu); - -#endif /* TCG_CPUS_RR_H */ diff --git a/accel/tcg/tcg-cpus.c b/accel/tcg/tcg-cpus.c deleted file mode 100644 index e335f9f155..0000000000 --- a/accel/tcg/tcg-cpus.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * QEMU TCG vCPU common functionality - * - * Functionality common to all TCG vCPU variants: mttcg, rr and icount. - * - * Copyright (c) 2003-2008 Fabrice Bellard - * Copyright (c) 2014 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "sysemu/tcg.h" -#include "sysemu/replay.h" -#include "qemu/main-loop.h" -#include "qemu/guest-random.h" -#include "exec/exec-all.h" -#include "hw/boards.h" - -#include "tcg-cpus.h" - -/* common functionality among all TCG variants */ - -void tcg_cpus_destroy(CPUState *cpu) -{ - cpu_thread_signal_destroyed(cpu); -} - -int tcg_cpus_exec(CPUState *cpu) -{ - int ret; -#ifdef CONFIG_PROFILER - int64_t ti; -#endif - assert(tcg_enabled()); -#ifdef CONFIG_PROFILER - ti = profile_getclock(); -#endif - cpu_exec_start(cpu); - ret = cpu_exec(cpu); - cpu_exec_end(cpu); -#ifdef CONFIG_PROFILER - qatomic_set(&tcg_ctx->prof.cpu_exec_time, - tcg_ctx->prof.cpu_exec_time + profile_getclock() - ti); -#endif - return ret; -} - -/* mask must never be zero, except for A20 change call */ -void tcg_cpus_handle_interrupt(CPUState *cpu, int mask) -{ - g_assert(qemu_mutex_iothread_locked()); - - cpu->interrupt_request |= mask; - - /* - * If called from iothread context, wake the target cpu in - * case its halted. - */ - if (!qemu_cpu_is_self(cpu)) { - qemu_cpu_kick(cpu); - } else { - qatomic_set(&cpu_neg(cpu)->icount_decr.u16.high, -1); - } -} diff --git a/accel/tcg/tcg-cpus.h b/accel/tcg/tcg-cpus.h deleted file mode 100644 index d6893a32f8..0000000000 --- a/accel/tcg/tcg-cpus.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * QEMU TCG vCPU common functionality - * - * Functionality common to all TCG vcpu variants: mttcg, rr and icount. - * - * Copyright 2020 SUSE LLC - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef TCG_CPUS_H -#define TCG_CPUS_H - -#include "sysemu/cpus.h" - -extern const CpusAccel tcg_cpus_mttcg; -extern const CpusAccel tcg_cpus_icount; -extern const CpusAccel tcg_cpus_rr; - -void tcg_cpus_destroy(CPUState *cpu); -int tcg_cpus_exec(CPUState *cpu); -void tcg_cpus_handle_interrupt(CPUState *cpu, int mask); - -#endif /* TCG_CPUS_H */ diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index 594aaf6b49..e9d2d6aaaa 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -154,10 +154,6 @@ static void xen_setup_post(MachineState *ms, AccelState *accel) } } -const CpusAccel xen_cpus = { - .create_vcpu_thread = dummy_start_vcpu_thread, -}; - static int xen_init(MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); @@ -185,9 +181,6 @@ static int xen_init(MachineState *ms) * opt out of system RAM being allocated by generic code */ mc->default_ram_id = NULL; - - cpus_register_accel(&xen_cpus); - return 0; } @@ -222,9 +215,24 @@ static const TypeInfo xen_accel_type = { .class_init = xen_accel_class_init, }; +static void xen_accel_ops_class_init(ObjectClass *oc, void *data) +{ + AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); + + ops->create_vcpu_thread = dummy_start_vcpu_thread; +} + +static const TypeInfo xen_accel_ops_type = { + .name = ACCEL_OPS_NAME("xen"), + + .parent = TYPE_ACCEL_OPS, + .class_init = xen_accel_ops_class_init, + .abstract = true, +}; + static void xen_type_init(void) { type_register_static(&xen_accel_type); + type_register_static(&xen_accel_ops_type); } - type_init(xen_type_init); diff --git a/bsd-user/main.c b/bsd-user/main.c index 6501164e05..7cc08024e3 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -909,13 +909,14 @@ int main(int argc, char **argv) #endif } + cpu_type = parse_cpu_option(cpu_model); /* init tcg before creating CPUs and to get qemu_host_page_size */ { AccelClass *ac = ACCEL_GET_CLASS(current_accel()); ac->init_machine(NULL); + accel_init_interfaces(ac); } - cpu_type = parse_cpu_option(cpu_model); cpu = cpu_create(cpu_type); env = cpu->env_ptr; #if defined(TARGET_SPARC) || defined(TARGET_PPC) diff --git a/include/qemu/accel.h b/include/qemu/accel.h index fac4a18703..b9d6d69eb8 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -69,6 +69,8 @@ typedef struct AccelClass { AccelClass *accel_find(const char *opt_name); AccelState *current_accel(void); +void accel_init_interfaces(AccelClass *ac); + #ifndef CONFIG_USER_ONLY int accel_init_machine(AccelState *accel, MachineState *ms); diff --git a/include/sysemu/accel-ops.h b/include/sysemu/accel-ops.h new file mode 100644 index 0000000000..032f6979d7 --- /dev/null +++ b/include/sysemu/accel-ops.h @@ -0,0 +1,45 @@ +/* + * Accelerator OPS, used for cpus.c module + * + * Copyright 2021 SUSE LLC + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef ACCEL_OPS_H +#define ACCEL_OPS_H + +#include "qom/object.h" + +#define ACCEL_OPS_SUFFIX "-ops" +#define TYPE_ACCEL_OPS "accel" ACCEL_OPS_SUFFIX +#define ACCEL_OPS_NAME(name) (name "-" TYPE_ACCEL_OPS) + +typedef struct AccelOpsClass AccelOpsClass; +DECLARE_CLASS_CHECKERS(AccelOpsClass, ACCEL_OPS, TYPE_ACCEL_OPS) + +/* cpus.c operations interface */ +struct AccelOpsClass { + /*< private >*/ + ObjectClass parent_class; + /*< public >*/ + + /* initialization function called when accel is chosen */ + void (*ops_init)(AccelOpsClass *ops); + + void (*create_vcpu_thread)(CPUState *cpu); /* MANDATORY NON-NULL */ + void (*kick_vcpu_thread)(CPUState *cpu); + + void (*synchronize_post_reset)(CPUState *cpu); + void (*synchronize_post_init)(CPUState *cpu); + void (*synchronize_state)(CPUState *cpu); + void (*synchronize_pre_loadvm)(CPUState *cpu); + + void (*handle_interrupt)(CPUState *cpu, int mask); + + int64_t (*get_virtual_clock)(void); + int64_t (*get_elapsed_ticks)(void); +}; + +#endif /* ACCEL_OPS_H */ diff --git a/include/sysemu/cpus.h b/include/sysemu/cpus.h index e8156728c6..2cd74392e0 100644 --- a/include/sysemu/cpus.h +++ b/include/sysemu/cpus.h @@ -2,30 +2,14 @@ #define QEMU_CPUS_H #include "qemu/timer.h" +#include "sysemu/accel-ops.h" -/* cpus.c */ +/* register accel-specific operations */ +void cpus_register_accel(const AccelOpsClass *i); -/* CPU execution threads */ +/* accel/dummy-cpus.c */ -typedef struct CpusAccel { - void (*create_vcpu_thread)(CPUState *cpu); /* MANDATORY */ - void (*kick_vcpu_thread)(CPUState *cpu); - - void (*synchronize_post_reset)(CPUState *cpu); - void (*synchronize_post_init)(CPUState *cpu); - void (*synchronize_state)(CPUState *cpu); - void (*synchronize_pre_loadvm)(CPUState *cpu); - - void (*handle_interrupt)(CPUState *cpu, int mask); - - int64_t (*get_virtual_clock)(void); - int64_t (*get_elapsed_ticks)(void); -} CpusAccel; - -/* register accel-specific cpus interface implementation */ -void cpus_register_accel(const CpusAccel *i); - -/* Create a dummy vcpu for CpusAccel->create_vcpu_thread */ +/* Create a dummy vcpu for AccelOpsClass->create_vcpu_thread */ void dummy_start_vcpu_thread(CPUState *); /* interface available for cpus accelerator threads */ diff --git a/linux-user/main.c b/linux-user/main.c index 7ed23d9a29..2e3c169878 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -706,6 +706,7 @@ int main(int argc, char **argv, char **envp) AccelClass *ac = ACCEL_GET_CLASS(current_accel()); ac->init_machine(NULL); + accel_init_interfaces(ac); } cpu = cpu_create(cpu_type); env = cpu->env_ptr; diff --git a/softmmu/cpus.c b/softmmu/cpus.c index 1dc20b9dc3..112eba9d54 100644 --- a/softmmu/cpus.c +++ b/softmmu/cpus.c @@ -128,7 +128,7 @@ void hw_error(const char *fmt, ...) /* * The chosen accelerator is supposed to register this. */ -static const CpusAccel *cpus_accel; +static const AccelOpsClass *cpus_accel; void cpu_synchronize_all_states(void) { @@ -594,11 +594,11 @@ void cpu_remove_sync(CPUState *cpu) qemu_mutex_lock_iothread(); } -void cpus_register_accel(const CpusAccel *ca) +void cpus_register_accel(const AccelOpsClass *ops) { - assert(ca != NULL); - assert(ca->create_vcpu_thread != NULL); /* mandatory */ - cpus_accel = ca; + assert(ops != NULL); + assert(ops->create_vcpu_thread != NULL); /* mandatory */ + cpus_accel = ops; } void qemu_init_vcpu(CPUState *cpu) @@ -618,7 +618,7 @@ void qemu_init_vcpu(CPUState *cpu) cpu_address_space_init(cpu, 0, "cpu-memory", cpu->memory); } - /* accelerators all implement the CpusAccel interface */ + /* accelerators all implement the AccelOpsClass */ g_assert(cpus_accel != NULL && cpus_accel->create_vcpu_thread != NULL); cpus_accel->create_vcpu_thread(cpu); diff --git a/softmmu/vl.c b/softmmu/vl.c index 6105c75bc7..2bf94ece9c 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -1726,7 +1726,8 @@ static bool object_create_early(const char *type, QemuOpts *opts) return false; } - /* Allocation of large amounts of memory may delay + /* + * Allocation of large amounts of memory may delay * chardev initialization for too long, and trigger timeouts * on software that waits for a monitor socket to be created * (e.g. libvirt). @@ -3497,7 +3498,7 @@ void qemu_init(int argc, char **argv, char **envp) * * Machine compat properties: object_set_machine_compat_props(). * Accelerator compat props: object_set_accelerator_compat_props(), - * called from configure_accelerator(). + * called from do_configure_accelerator(). */ machine_class = MACHINE_GET_CLASS(current_machine); @@ -3519,6 +3520,8 @@ void qemu_init(int argc, char **argv, char **envp) if (cpu_option) { current_machine->cpu_type = parse_cpu_option(cpu_option); } + /* NB: for machine none cpu_type could STILL be NULL here! */ + accel_init_interfaces(ACCEL_GET_CLASS(current_machine->accelerator)); qemu_resolve_machine_memdev(); parse_numa_opts(current_machine); diff --git a/target/i386/hax/hax-accel-ops.c b/target/i386/hax/hax-accel-ops.c new file mode 100644 index 0000000000..136630e9b2 --- /dev/null +++ b/target/i386/hax/hax-accel-ops.c @@ -0,0 +1,102 @@ +/* + * QEMU HAX support + * + * Copyright IBM, Corp. 2008 + * Red Hat, Inc. 2008 + * + * Authors: + * Anthony Liguori + * Glauber Costa + * + * Copyright (c) 2011 Intel Corporation + * Written by: + * Jiang Yunhong + * Xin Xiaohui + * Zhang Xiantao + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "sysemu/runstate.h" +#include "sysemu/cpus.h" +#include "qemu/guest-random.h" + +#include "hax-accel-ops.h" + +static void *hax_cpu_thread_fn(void *arg) +{ + CPUState *cpu = arg; + int r; + + rcu_register_thread(); + qemu_mutex_lock_iothread(); + qemu_thread_get_self(cpu->thread); + + cpu->thread_id = qemu_get_thread_id(); + current_cpu = cpu; + hax_init_vcpu(cpu); + cpu_thread_signal_created(cpu); + qemu_guest_random_seed_thread_part2(cpu->random_seed); + + do { + if (cpu_can_run(cpu)) { + r = hax_smp_cpu_exec(cpu); + if (r == EXCP_DEBUG) { + cpu_handle_guest_debug(cpu); + } + } + + qemu_wait_io_event(cpu); + } while (!cpu->unplug || cpu_can_run(cpu)); + rcu_unregister_thread(); + return NULL; +} + +static void hax_start_vcpu_thread(CPUState *cpu) +{ + char thread_name[VCPU_THREAD_NAME_SIZE]; + + cpu->thread = g_malloc0(sizeof(QemuThread)); + cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + qemu_cond_init(cpu->halt_cond); + + snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HAX", + cpu->cpu_index); + qemu_thread_create(cpu->thread, thread_name, hax_cpu_thread_fn, + cpu, QEMU_THREAD_JOINABLE); +#ifdef _WIN32 + cpu->hThread = qemu_thread_get_handle(cpu->thread); +#endif +} + +static void hax_accel_ops_class_init(ObjectClass *oc, void *data) +{ + AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); + + ops->create_vcpu_thread = hax_start_vcpu_thread; + ops->kick_vcpu_thread = hax_kick_vcpu_thread; + + ops->synchronize_post_reset = hax_cpu_synchronize_post_reset; + ops->synchronize_post_init = hax_cpu_synchronize_post_init; + ops->synchronize_state = hax_cpu_synchronize_state; + ops->synchronize_pre_loadvm = hax_cpu_synchronize_pre_loadvm; +} + +static const TypeInfo hax_accel_ops_type = { + .name = ACCEL_OPS_NAME("hax"), + + .parent = TYPE_ACCEL_OPS, + .class_init = hax_accel_ops_class_init, + .abstract = true, +}; + +static void hax_accel_ops_register_types(void) +{ + type_register_static(&hax_accel_ops_type); +} +type_init(hax_accel_ops_register_types); diff --git a/target/i386/hax/hax-accel-ops.h b/target/i386/hax/hax-accel-ops.h new file mode 100644 index 0000000000..c7698519cd --- /dev/null +++ b/target/i386/hax/hax-accel-ops.h @@ -0,0 +1,31 @@ +/* + * Accelerator CPUS Interface + * + * Copyright 2020 SUSE LLC + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef HAX_CPUS_H +#define HAX_CPUS_H + +#include "sysemu/cpus.h" + +#include "hax-interface.h" +#include "hax-i386.h" + +int hax_init_vcpu(CPUState *cpu); +int hax_smp_cpu_exec(CPUState *cpu); +int hax_populate_ram(uint64_t va, uint64_t size); + +void hax_cpu_synchronize_state(CPUState *cpu); +void hax_cpu_synchronize_post_reset(CPUState *cpu); +void hax_cpu_synchronize_post_init(CPUState *cpu); +void hax_cpu_synchronize_pre_loadvm(CPUState *cpu); + +int hax_vcpu_destroy(CPUState *cpu); +void hax_raise_event(CPUState *cpu); +void hax_reset_vcpu_state(void *opaque); + +#endif /* HAX_CPUS_H */ diff --git a/target/i386/hax/hax-all.c b/target/i386/hax/hax-all.c index d7f4bb44a7..bf65ed6fa9 100644 --- a/target/i386/hax/hax-all.c +++ b/target/i386/hax/hax-all.c @@ -33,7 +33,7 @@ #include "sysemu/runstate.h" #include "hw/boards.h" -#include "hax-cpus.h" +#include "hax-accel-ops.h" #define DEBUG_HAX 0 @@ -364,9 +364,6 @@ static int hax_accel_init(MachineState *ms) !ret ? "working" : "not working", !ret ? "fast virt" : "emulation"); } - if (ret == 0) { - cpus_register_accel(&hax_cpus); - } return ret; } diff --git a/target/i386/hax/hax-cpus.c b/target/i386/hax/hax-cpus.c deleted file mode 100644 index f72c85bd49..0000000000 --- a/target/i386/hax/hax-cpus.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * QEMU HAX support - * - * Copyright IBM, Corp. 2008 - * Red Hat, Inc. 2008 - * - * Authors: - * Anthony Liguori - * Glauber Costa - * - * Copyright (c) 2011 Intel Corporation - * Written by: - * Jiang Yunhong - * Xin Xiaohui - * Zhang Xiantao - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qemu/main-loop.h" -#include "sysemu/runstate.h" -#include "sysemu/cpus.h" -#include "qemu/guest-random.h" - -#include "hax-cpus.h" - -static void *hax_cpu_thread_fn(void *arg) -{ - CPUState *cpu = arg; - int r; - - rcu_register_thread(); - qemu_mutex_lock_iothread(); - qemu_thread_get_self(cpu->thread); - - cpu->thread_id = qemu_get_thread_id(); - current_cpu = cpu; - hax_init_vcpu(cpu); - cpu_thread_signal_created(cpu); - qemu_guest_random_seed_thread_part2(cpu->random_seed); - - do { - if (cpu_can_run(cpu)) { - r = hax_smp_cpu_exec(cpu); - if (r == EXCP_DEBUG) { - cpu_handle_guest_debug(cpu); - } - } - - qemu_wait_io_event(cpu); - } while (!cpu->unplug || cpu_can_run(cpu)); - rcu_unregister_thread(); - return NULL; -} - -static void hax_start_vcpu_thread(CPUState *cpu) -{ - char thread_name[VCPU_THREAD_NAME_SIZE]; - - cpu->thread = g_malloc0(sizeof(QemuThread)); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); - qemu_cond_init(cpu->halt_cond); - - snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HAX", - cpu->cpu_index); - qemu_thread_create(cpu->thread, thread_name, hax_cpu_thread_fn, - cpu, QEMU_THREAD_JOINABLE); -#ifdef _WIN32 - cpu->hThread = qemu_thread_get_handle(cpu->thread); -#endif -} - -const CpusAccel hax_cpus = { - .create_vcpu_thread = hax_start_vcpu_thread, - .kick_vcpu_thread = hax_kick_vcpu_thread, - - .synchronize_post_reset = hax_cpu_synchronize_post_reset, - .synchronize_post_init = hax_cpu_synchronize_post_init, - .synchronize_state = hax_cpu_synchronize_state, - .synchronize_pre_loadvm = hax_cpu_synchronize_pre_loadvm, -}; diff --git a/target/i386/hax/hax-cpus.h b/target/i386/hax/hax-cpus.h deleted file mode 100644 index ee8ab7a631..0000000000 --- a/target/i386/hax/hax-cpus.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Accelerator CPUS Interface - * - * Copyright 2020 SUSE LLC - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef HAX_CPUS_H -#define HAX_CPUS_H - -#include "sysemu/cpus.h" - -extern const CpusAccel hax_cpus; - -#include "hax-interface.h" -#include "hax-i386.h" - -int hax_init_vcpu(CPUState *cpu); -int hax_smp_cpu_exec(CPUState *cpu); -int hax_populate_ram(uint64_t va, uint64_t size); - -void hax_cpu_synchronize_state(CPUState *cpu); -void hax_cpu_synchronize_post_reset(CPUState *cpu); -void hax_cpu_synchronize_post_init(CPUState *cpu); -void hax_cpu_synchronize_pre_loadvm(CPUState *cpu); - -int hax_vcpu_destroy(CPUState *cpu); -void hax_raise_event(CPUState *cpu); -void hax_reset_vcpu_state(void *opaque); - -#endif /* HAX_CPUS_H */ diff --git a/target/i386/hax/hax-mem.c b/target/i386/hax/hax-mem.c index 71e637cf16..35495f5e82 100644 --- a/target/i386/hax/hax-mem.c +++ b/target/i386/hax/hax-mem.c @@ -13,7 +13,7 @@ #include "exec/address-spaces.h" #include "qemu/error-report.h" -#include "hax-cpus.h" +#include "hax-accel-ops.h" #include "qemu/queue.h" #define DEBUG_HAX_MEM 0 diff --git a/target/i386/hax/hax-posix.c b/target/i386/hax/hax-posix.c index 735a749d4b..ac1a51096e 100644 --- a/target/i386/hax/hax-posix.c +++ b/target/i386/hax/hax-posix.c @@ -15,7 +15,7 @@ #include #include "sysemu/cpus.h" -#include "hax-cpus.h" +#include "hax-accel-ops.h" hax_fd hax_mod_open(void) { diff --git a/target/i386/hax/hax-windows.c b/target/i386/hax/hax-windows.c index 6c82dfb54f..59afa213a6 100644 --- a/target/i386/hax/hax-windows.c +++ b/target/i386/hax/hax-windows.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "hax-cpus.h" +#include "hax-accel-ops.h" /* * return 0 when success, -1 when driver not loaded, diff --git a/target/i386/hax/hax-windows.h b/target/i386/hax/hax-windows.h index a5ce12d663..b1f5d4f32f 100644 --- a/target/i386/hax/hax-windows.h +++ b/target/i386/hax/hax-windows.h @@ -23,7 +23,7 @@ #include #include -#include "hax-cpus.h" +#include "hax-accel-ops.h" #define HAX_INVALID_FD INVALID_HANDLE_VALUE diff --git a/target/i386/hax/meson.build b/target/i386/hax/meson.build index 77ea431b30..d6c520fb6b 100644 --- a/target/i386/hax/meson.build +++ b/target/i386/hax/meson.build @@ -1,7 +1,7 @@ i386_softmmu_ss.add(when: 'CONFIG_HAX', if_true: files( 'hax-all.c', 'hax-mem.c', - 'hax-cpus.c', + 'hax-accel-ops.c', )) i386_softmmu_ss.add(when: ['CONFIG_HAX', 'CONFIG_POSIX'], if_true: files('hax-posix.c')) i386_softmmu_ss.add(when: ['CONFIG_HAX', 'CONFIG_WIN32'], if_true: files('hax-windows.c')) diff --git a/target/i386/hvf/hvf-accel-ops.c b/target/i386/hvf/hvf-accel-ops.c new file mode 100644 index 0000000000..cbaad238e0 --- /dev/null +++ b/target/i386/hvf/hvf-accel-ops.c @@ -0,0 +1,146 @@ +/* + * Copyright 2008 IBM Corporation + * 2008 Red Hat, Inc. + * Copyright 2011 Intel Corporation + * Copyright 2016 Veertu, Inc. + * Copyright 2017 The Android Open Source Project + * + * QEMU Hypervisor.framework support + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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 . + * + * This file contain code under public domain from the hvdos project: + * https://github.com/mist64/hvdos + * + * Parts Copyright (c) 2011 NetApp, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (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 "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "sysemu/hvf.h" +#include "sysemu/runstate.h" +#include "target/i386/cpu.h" +#include "qemu/guest-random.h" + +#include "hvf-accel-ops.h" + +/* + * The HVF-specific vCPU thread function. This one should only run when the host + * CPU supports the VMX "unrestricted guest" feature. + */ +static void *hvf_cpu_thread_fn(void *arg) +{ + CPUState *cpu = arg; + + int r; + + assert(hvf_enabled()); + + rcu_register_thread(); + + qemu_mutex_lock_iothread(); + qemu_thread_get_self(cpu->thread); + + cpu->thread_id = qemu_get_thread_id(); + cpu->can_do_io = 1; + current_cpu = cpu; + + hvf_init_vcpu(cpu); + + /* signal CPU creation */ + cpu_thread_signal_created(cpu); + qemu_guest_random_seed_thread_part2(cpu->random_seed); + + do { + if (cpu_can_run(cpu)) { + r = hvf_vcpu_exec(cpu); + if (r == EXCP_DEBUG) { + cpu_handle_guest_debug(cpu); + } + } + qemu_wait_io_event(cpu); + } while (!cpu->unplug || cpu_can_run(cpu)); + + hvf_vcpu_destroy(cpu); + cpu_thread_signal_destroyed(cpu); + qemu_mutex_unlock_iothread(); + rcu_unregister_thread(); + return NULL; +} + +static void hvf_start_vcpu_thread(CPUState *cpu) +{ + char thread_name[VCPU_THREAD_NAME_SIZE]; + + /* + * HVF currently does not support TCG, and only runs in + * unrestricted-guest mode. + */ + assert(hvf_enabled()); + + cpu->thread = g_malloc0(sizeof(QemuThread)); + cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + qemu_cond_init(cpu->halt_cond); + + snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HVF", + cpu->cpu_index); + qemu_thread_create(cpu->thread, thread_name, hvf_cpu_thread_fn, + cpu, QEMU_THREAD_JOINABLE); +} + +static void hvf_accel_ops_class_init(ObjectClass *oc, void *data) +{ + AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); + + ops->create_vcpu_thread = hvf_start_vcpu_thread; + + ops->synchronize_post_reset = hvf_cpu_synchronize_post_reset; + ops->synchronize_post_init = hvf_cpu_synchronize_post_init; + ops->synchronize_state = hvf_cpu_synchronize_state; + ops->synchronize_pre_loadvm = hvf_cpu_synchronize_pre_loadvm; +}; +static const TypeInfo hvf_accel_ops_type = { + .name = ACCEL_OPS_NAME("hvf"), + + .parent = TYPE_ACCEL_OPS, + .class_init = hvf_accel_ops_class_init, + .abstract = true, +}; +static void hvf_accel_ops_register_types(void) +{ + type_register_static(&hvf_accel_ops_type); +} +type_init(hvf_accel_ops_register_types); diff --git a/target/i386/hvf/hvf-accel-ops.h b/target/i386/hvf/hvf-accel-ops.h new file mode 100644 index 0000000000..8f992da168 --- /dev/null +++ b/target/i386/hvf/hvf-accel-ops.h @@ -0,0 +1,23 @@ +/* + * Accelerator CPUS Interface + * + * Copyright 2020 SUSE LLC + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef HVF_CPUS_H +#define HVF_CPUS_H + +#include "sysemu/cpus.h" + +int hvf_init_vcpu(CPUState *); +int hvf_vcpu_exec(CPUState *); +void hvf_cpu_synchronize_state(CPUState *); +void hvf_cpu_synchronize_post_reset(CPUState *); +void hvf_cpu_synchronize_post_init(CPUState *); +void hvf_cpu_synchronize_pre_loadvm(CPUState *); +void hvf_vcpu_destroy(CPUState *); + +#endif /* HVF_CPUS_H */ diff --git a/target/i386/hvf/hvf-cpus.c b/target/i386/hvf/hvf-cpus.c deleted file mode 100644 index 817b3d7452..0000000000 --- a/target/i386/hvf/hvf-cpus.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2008 IBM Corporation - * 2008 Red Hat, Inc. - * Copyright 2011 Intel Corporation - * Copyright 2016 Veertu, Inc. - * Copyright 2017 The Android Open Source Project - * - * QEMU Hypervisor.framework support - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * 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 . - * - * This file contain code under public domain from the hvdos project: - * https://github.com/mist64/hvdos - * - * Parts Copyright (c) 2011 NetApp, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (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 "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qemu/main-loop.h" -#include "sysemu/hvf.h" -#include "sysemu/runstate.h" -#include "target/i386/cpu.h" -#include "qemu/guest-random.h" - -#include "hvf-cpus.h" - -/* - * The HVF-specific vCPU thread function. This one should only run when the host - * CPU supports the VMX "unrestricted guest" feature. - */ -static void *hvf_cpu_thread_fn(void *arg) -{ - CPUState *cpu = arg; - - int r; - - assert(hvf_enabled()); - - rcu_register_thread(); - - qemu_mutex_lock_iothread(); - qemu_thread_get_self(cpu->thread); - - cpu->thread_id = qemu_get_thread_id(); - cpu->can_do_io = 1; - current_cpu = cpu; - - hvf_init_vcpu(cpu); - - /* signal CPU creation */ - cpu_thread_signal_created(cpu); - qemu_guest_random_seed_thread_part2(cpu->random_seed); - - do { - if (cpu_can_run(cpu)) { - r = hvf_vcpu_exec(cpu); - if (r == EXCP_DEBUG) { - cpu_handle_guest_debug(cpu); - } - } - qemu_wait_io_event(cpu); - } while (!cpu->unplug || cpu_can_run(cpu)); - - hvf_vcpu_destroy(cpu); - cpu_thread_signal_destroyed(cpu); - qemu_mutex_unlock_iothread(); - rcu_unregister_thread(); - return NULL; -} - -static void hvf_start_vcpu_thread(CPUState *cpu) -{ - char thread_name[VCPU_THREAD_NAME_SIZE]; - - /* - * HVF currently does not support TCG, and only runs in - * unrestricted-guest mode. - */ - assert(hvf_enabled()); - - cpu->thread = g_malloc0(sizeof(QemuThread)); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); - qemu_cond_init(cpu->halt_cond); - - snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HVF", - cpu->cpu_index); - qemu_thread_create(cpu->thread, thread_name, hvf_cpu_thread_fn, - cpu, QEMU_THREAD_JOINABLE); -} - -const CpusAccel hvf_cpus = { - .create_vcpu_thread = hvf_start_vcpu_thread, - - .synchronize_post_reset = hvf_cpu_synchronize_post_reset, - .synchronize_post_init = hvf_cpu_synchronize_post_init, - .synchronize_state = hvf_cpu_synchronize_state, - .synchronize_pre_loadvm = hvf_cpu_synchronize_pre_loadvm, -}; diff --git a/target/i386/hvf/hvf-cpus.h b/target/i386/hvf/hvf-cpus.h deleted file mode 100644 index ced31b82c0..0000000000 --- a/target/i386/hvf/hvf-cpus.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Accelerator CPUS Interface - * - * Copyright 2020 SUSE LLC - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef HVF_CPUS_H -#define HVF_CPUS_H - -#include "sysemu/cpus.h" - -extern const CpusAccel hvf_cpus; - -int hvf_init_vcpu(CPUState *); -int hvf_vcpu_exec(CPUState *); -void hvf_cpu_synchronize_state(CPUState *); -void hvf_cpu_synchronize_post_reset(CPUState *); -void hvf_cpu_synchronize_post_init(CPUState *); -void hvf_cpu_synchronize_pre_loadvm(CPUState *); -void hvf_vcpu_destroy(CPUState *); - -#endif /* HVF_CPUS_H */ diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index ffc9efa40f..5b90dcdf88 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -72,7 +72,7 @@ #include "qemu/accel.h" #include "target/i386/cpu.h" -#include "hvf-cpus.h" +#include "hvf-accel-ops.h" HVFState *hvf_state; @@ -887,7 +887,6 @@ static int hvf_accel_init(MachineState *ms) hvf_state = s; memory_listener_register(&hvf_memory_listener, &address_space_memory); - cpus_register_accel(&hvf_cpus); return 0; } diff --git a/target/i386/hvf/meson.build b/target/i386/hvf/meson.build index 409c9a3f14..e9eb5a5da8 100644 --- a/target/i386/hvf/meson.build +++ b/target/i386/hvf/meson.build @@ -1,6 +1,6 @@ i386_softmmu_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: files( 'hvf.c', - 'hvf-cpus.c', + 'hvf-accel-ops.c', 'x86.c', 'x86_cpuid.c', 'x86_decode.c', diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index bbec412b6c..0d7533742e 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -32,7 +32,7 @@ #include #include -#include "hvf-cpus.h" +#include "hvf-accel-ops.h" void hvf_set_segment(struct CPUState *cpu, struct vmx_segment *vmx_seg, SegmentCache *qseg, bool is_tr) diff --git a/target/i386/whpx/meson.build b/target/i386/whpx/meson.build index d8aa683999..95fc31eb81 100644 --- a/target/i386/whpx/meson.build +++ b/target/i386/whpx/meson.build @@ -1,5 +1,5 @@ i386_softmmu_ss.add(when: 'CONFIG_WHPX', if_true: files( 'whpx-all.c', 'whpx-apic.c', - 'whpx-cpus.c', + 'whpx-accel-ops.c', )) diff --git a/target/i386/whpx/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c new file mode 100644 index 0000000000..6bc47c5309 --- /dev/null +++ b/target/i386/whpx/whpx-accel-ops.c @@ -0,0 +1,111 @@ +/* + * QEMU Windows Hypervisor Platform accelerator (WHPX) + * + * Copyright Microsoft Corp. 2017 + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "sysemu/kvm_int.h" +#include "qemu/main-loop.h" +#include "sysemu/cpus.h" +#include "qemu/guest-random.h" + +#include "sysemu/whpx.h" +#include "whpx-internal.h" +#include "whpx-accel-ops.h" + +static void *whpx_cpu_thread_fn(void *arg) +{ + CPUState *cpu = arg; + int r; + + rcu_register_thread(); + + qemu_mutex_lock_iothread(); + qemu_thread_get_self(cpu->thread); + cpu->thread_id = qemu_get_thread_id(); + current_cpu = cpu; + + r = whpx_init_vcpu(cpu); + if (r < 0) { + fprintf(stderr, "whpx_init_vcpu failed: %s\n", strerror(-r)); + exit(1); + } + + /* signal CPU creation */ + cpu_thread_signal_created(cpu); + qemu_guest_random_seed_thread_part2(cpu->random_seed); + + do { + if (cpu_can_run(cpu)) { + r = whpx_vcpu_exec(cpu); + if (r == EXCP_DEBUG) { + cpu_handle_guest_debug(cpu); + } + } + while (cpu_thread_is_idle(cpu)) { + qemu_cond_wait_iothread(cpu->halt_cond); + } + qemu_wait_io_event_common(cpu); + } while (!cpu->unplug || cpu_can_run(cpu)); + + whpx_destroy_vcpu(cpu); + cpu_thread_signal_destroyed(cpu); + qemu_mutex_unlock_iothread(); + rcu_unregister_thread(); + return NULL; +} + +static void whpx_start_vcpu_thread(CPUState *cpu) +{ + char thread_name[VCPU_THREAD_NAME_SIZE]; + + cpu->thread = g_malloc0(sizeof(QemuThread)); + cpu->halt_cond = g_malloc0(sizeof(QemuCond)); + qemu_cond_init(cpu->halt_cond); + snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/WHPX", + cpu->cpu_index); + qemu_thread_create(cpu->thread, thread_name, whpx_cpu_thread_fn, + cpu, QEMU_THREAD_JOINABLE); +#ifdef _WIN32 + cpu->hThread = qemu_thread_get_handle(cpu->thread); +#endif +} + +static void whpx_kick_vcpu_thread(CPUState *cpu) +{ + if (!qemu_cpu_is_self(cpu)) { + whpx_vcpu_kick(cpu); + } +} + +static void whpx_accel_ops_class_init(ObjectClass *oc, void *data) +{ + AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); + + ops->create_vcpu_thread = whpx_start_vcpu_thread; + ops->kick_vcpu_thread = whpx_kick_vcpu_thread; + + ops->synchronize_post_reset = whpx_cpu_synchronize_post_reset; + ops->synchronize_post_init = whpx_cpu_synchronize_post_init; + ops->synchronize_state = whpx_cpu_synchronize_state; + ops->synchronize_pre_loadvm = whpx_cpu_synchronize_pre_loadvm; +} + +static const TypeInfo whpx_accel_ops_type = { + .name = ACCEL_OPS_NAME("whpx"), + + .parent = TYPE_ACCEL_OPS, + .class_init = whpx_accel_ops_class_init, + .abstract = true, +}; + +static void whpx_accel_ops_register_types(void) +{ + type_register_static(&whpx_accel_ops_type); +} +type_init(whpx_accel_ops_register_types); diff --git a/target/i386/whpx/whpx-accel-ops.h b/target/i386/whpx/whpx-accel-ops.h new file mode 100644 index 0000000000..2dee6d61ea --- /dev/null +++ b/target/i386/whpx/whpx-accel-ops.h @@ -0,0 +1,32 @@ +/* + * Accelerator CPUS Interface + * + * Copyright 2020 SUSE LLC + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef WHPX_CPUS_H +#define WHPX_CPUS_H + +#include "sysemu/cpus.h" + +int whpx_init_vcpu(CPUState *cpu); +int whpx_vcpu_exec(CPUState *cpu); +void whpx_destroy_vcpu(CPUState *cpu); +void whpx_vcpu_kick(CPUState *cpu); + +void whpx_cpu_synchronize_state(CPUState *cpu); +void whpx_cpu_synchronize_post_reset(CPUState *cpu); +void whpx_cpu_synchronize_post_init(CPUState *cpu); +void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu); + +/* state subset only touched by the VCPU itself during runtime */ +#define WHPX_SET_RUNTIME_STATE 1 +/* state subset modified during VCPU reset */ +#define WHPX_SET_RESET_STATE 2 +/* full state set, modified during initialization or on vmload */ +#define WHPX_SET_FULL_STATE 3 + +#endif /* WHPX_CPUS_H */ diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index aa5c876138..f0a35df3bb 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -28,8 +28,11 @@ #include "migration/blocker.h" #include -#include "whpx-cpus.h" #include "whpx-internal.h" +#include "whpx-accel-ops.h" + +#include +#include #define HYPERV_APIC_BUS_FREQUENCY (200000000ULL) @@ -1846,8 +1849,6 @@ static int whpx_accel_init(MachineState *ms) whpx_memory_init(); - cpus_register_accel(&whpx_cpus); - printf("Windows Hypervisor Platform accelerator is operational\n"); return 0; diff --git a/target/i386/whpx/whpx-cpus.c b/target/i386/whpx/whpx-cpus.c deleted file mode 100644 index f7e69881a3..0000000000 --- a/target/i386/whpx/whpx-cpus.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * QEMU Windows Hypervisor Platform accelerator (WHPX) - * - * Copyright Microsoft Corp. 2017 - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "sysemu/kvm_int.h" -#include "qemu/main-loop.h" -#include "sysemu/cpus.h" -#include "qemu/guest-random.h" - -#include "sysemu/whpx.h" -#include "whpx-internal.h" -#include "whpx-cpus.h" - -static void *whpx_cpu_thread_fn(void *arg) -{ - CPUState *cpu = arg; - int r; - - rcu_register_thread(); - - qemu_mutex_lock_iothread(); - qemu_thread_get_self(cpu->thread); - cpu->thread_id = qemu_get_thread_id(); - current_cpu = cpu; - - r = whpx_init_vcpu(cpu); - if (r < 0) { - fprintf(stderr, "whpx_init_vcpu failed: %s\n", strerror(-r)); - exit(1); - } - - /* signal CPU creation */ - cpu_thread_signal_created(cpu); - qemu_guest_random_seed_thread_part2(cpu->random_seed); - - do { - if (cpu_can_run(cpu)) { - r = whpx_vcpu_exec(cpu); - if (r == EXCP_DEBUG) { - cpu_handle_guest_debug(cpu); - } - } - while (cpu_thread_is_idle(cpu)) { - qemu_cond_wait_iothread(cpu->halt_cond); - } - qemu_wait_io_event_common(cpu); - } while (!cpu->unplug || cpu_can_run(cpu)); - - whpx_destroy_vcpu(cpu); - cpu_thread_signal_destroyed(cpu); - qemu_mutex_unlock_iothread(); - rcu_unregister_thread(); - return NULL; -} - -static void whpx_start_vcpu_thread(CPUState *cpu) -{ - char thread_name[VCPU_THREAD_NAME_SIZE]; - - cpu->thread = g_malloc0(sizeof(QemuThread)); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); - qemu_cond_init(cpu->halt_cond); - snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/WHPX", - cpu->cpu_index); - qemu_thread_create(cpu->thread, thread_name, whpx_cpu_thread_fn, - cpu, QEMU_THREAD_JOINABLE); -#ifdef _WIN32 - cpu->hThread = qemu_thread_get_handle(cpu->thread); -#endif -} - -static void whpx_kick_vcpu_thread(CPUState *cpu) -{ - if (!qemu_cpu_is_self(cpu)) { - whpx_vcpu_kick(cpu); - } -} - -const CpusAccel whpx_cpus = { - .create_vcpu_thread = whpx_start_vcpu_thread, - .kick_vcpu_thread = whpx_kick_vcpu_thread, - - .synchronize_post_reset = whpx_cpu_synchronize_post_reset, - .synchronize_post_init = whpx_cpu_synchronize_post_init, - .synchronize_state = whpx_cpu_synchronize_state, - .synchronize_pre_loadvm = whpx_cpu_synchronize_pre_loadvm, -}; diff --git a/target/i386/whpx/whpx-cpus.h b/target/i386/whpx/whpx-cpus.h deleted file mode 100644 index bdb367d1d0..0000000000 --- a/target/i386/whpx/whpx-cpus.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Accelerator CPUS Interface - * - * Copyright 2020 SUSE LLC - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef WHPX_CPUS_H -#define WHPX_CPUS_H - -#include "sysemu/cpus.h" - -extern const CpusAccel whpx_cpus; - -int whpx_init_vcpu(CPUState *cpu); -int whpx_vcpu_exec(CPUState *cpu); -void whpx_destroy_vcpu(CPUState *cpu); -void whpx_vcpu_kick(CPUState *cpu); - -void whpx_cpu_synchronize_state(CPUState *cpu); -void whpx_cpu_synchronize_post_reset(CPUState *cpu); -void whpx_cpu_synchronize_post_init(CPUState *cpu); -void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu); - -/* state subset only touched by the VCPU itself during runtime */ -#define WHPX_SET_RUNTIME_STATE 1 -/* state subset modified during VCPU reset */ -#define WHPX_SET_RESET_STATE 2 -/* full state set, modified during initialization or on vmload */ -#define WHPX_SET_FULL_STATE 3 - -#endif /* WHPX_CPUS_H */