From b86f59c71552591a17dd21ba8f09654bfa19a31e Mon Sep 17 00:00:00 2001 From: Claudio Fontana Date: Thu, 4 Feb 2021 17:39:25 +0100 Subject: 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 --- accel/tcg/meson.build | 8 +- accel/tcg/tcg-accel-ops-icount.c | 138 ++++++++++++++++++ accel/tcg/tcg-accel-ops-icount.h | 19 +++ accel/tcg/tcg-accel-ops-mttcg.c | 134 +++++++++++++++++ accel/tcg/tcg-accel-ops-mttcg.h | 19 +++ accel/tcg/tcg-accel-ops-rr.c | 298 ++++++++++++++++++++++++++++++++++++++ accel/tcg/tcg-accel-ops-rr.h | 21 +++ accel/tcg/tcg-accel-ops.c | 125 ++++++++++++++++ accel/tcg/tcg-accel-ops.h | 21 +++ accel/tcg/tcg-all.c | 12 -- accel/tcg/tcg-cpus-icount.c | 147 ------------------- accel/tcg/tcg-cpus-icount.h | 17 --- accel/tcg/tcg-cpus-mttcg.c | 140 ------------------ accel/tcg/tcg-cpus-rr.c | 305 --------------------------------------- accel/tcg/tcg-cpus-rr.h | 21 --- accel/tcg/tcg-cpus.c | 82 ----------- accel/tcg/tcg-cpus.h | 25 ---- 17 files changed, 779 insertions(+), 753 deletions(-) create mode 100644 accel/tcg/tcg-accel-ops-icount.c create mode 100644 accel/tcg/tcg-accel-ops-icount.h create mode 100644 accel/tcg/tcg-accel-ops-mttcg.c create mode 100644 accel/tcg/tcg-accel-ops-mttcg.h create mode 100644 accel/tcg/tcg-accel-ops-rr.c create mode 100644 accel/tcg/tcg-accel-ops-rr.h create mode 100644 accel/tcg/tcg-accel-ops.c create mode 100644 accel/tcg/tcg-accel-ops.h delete mode 100644 accel/tcg/tcg-cpus-icount.c delete mode 100644 accel/tcg/tcg-cpus-icount.h delete mode 100644 accel/tcg/tcg-cpus-mttcg.c delete mode 100644 accel/tcg/tcg-cpus-rr.c delete mode 100644 accel/tcg/tcg-cpus-rr.h delete mode 100644 accel/tcg/tcg-cpus.c delete mode 100644 accel/tcg/tcg-cpus.h (limited to 'accel/tcg') diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 424d9bb..1236ac7 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 0000000..87762b4 --- /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 0000000..d884aa2 --- /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 0000000..42973fb --- /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 0000000..9fdc5a2 --- /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 0000000..4a66055 --- /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 0000000..54f6ae6 --- /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 0000000..6144d9d --- /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 0000000..4813000 --- /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 642a7b9..e378c2d 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 9f45432..0000000 --- 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 b695939..0000000 --- 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 9c3767d..0000000 --- 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 0181d2e..0000000 --- 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 54f6ae6..0000000 --- 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 e335f9f..0000000 --- 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 d6893a3..0000000 --- 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 */ -- cgit v1.1