Drop global lock during TCG code execution
RFC PATCH 1/1 Drop global lock during TCG code execution
This finally allows TCG to benefit from the iothread introduction: Drop the global mutex while running pure TCG CPU code. Reacquire the lock when entering MMIO or PIO emulation, or when leaving the TCG loop.
We have to revert a few optimization for the current TCG threading model, namely kicking the TCG thread in qemu_mutex_lock_iothread and not kicking it in qemu_cpu_kick. We also need to disable RAM block reordering until we have a more efficient locking mechanism at hand.
I'm pretty sure some cases are still broken, definitely SMP (we no longer perform round-robin scheduling "by chance"). Still, a Linux x86 UP guest and my Musicpal ARM model boot fine here. These numbers demonstrate where we gain something:
20338 jan 20 0 331m 75m 6904 R 99 0.9 0:50.95 qemu-system-arm 20337 jan 20 0 331m 75m 6904 S 20 0.9 0:26.50 qemu-system-arm
The guest CPU was fully loaded, but the iothread could still run mostly independent on a second core. Without the patch we don't get beyond
32206 jan 20 0 330m 73m 7036 R 82 0.9 1:06.00 qemu-system-arm 32204 jan 20 0 330m 73m 7036 S 21 0.9 0:17.03 qemu-system-arm
We don't benefit significantly, though, when the guest is not fully loading a host CPU.
Note that this patch depends on
http://thread.gmane.org/gmane.comp.emulators.qemu/118657
--- cpus.c | 18 ++++++------------ exec.c | 33 +++++++++++++++++++++++++++++++++ qemu-common.h | 11 ++++++++--- softmmu_template.h | 8 ++++++++ target-i386/op_helper.c | 27 ++++++++++++++++++++++++--- 5 files changed, 79 insertions(+), 18 deletions(-)
cpus.c +6 -12
@@ -721,7 +721,7 @@ void qemu_cpu_kick(void *_env)CPUState *env = _env;qemu_cond_broadcast (env->halt_cond); -if (kvm_enabled () && !env->thread_kicked) { +if (!env->thread_kicked) {qemu_cpu_kick_thread (env); env->thread_kicked = true; }
@@ -750,17 +750,7 @@ int qemu_cpu_is_self(void *_env)void qemu_mutex_lock_iothread (void ) { -if (kvm_enabled ()) { -qemu_mutex_lock (&qemu_global_mutex); - }else { - iothread_requesting_mutex = true; -if (qemu_mutex_trylock (&qemu_global_mutex)) { -qemu_cpu_kick_thread (first_cpu); -qemu_mutex_lock (&qemu_global_mutex); - } - iothread_requesting_mutex = false; -qemu_cond_broadcast (&qemu_io_proceeded_cond); - } +qemu_mutex_lock (&qemu_global_mutex); }void qemu_mutex_unlock_iothread (void )
@@ -912,7 +902,11 @@ static int tcg_cpu_exec(CPUState *env) env->icount_decr.u16.low = decr; env->icount_extra = count; } +qemu_mutex_unlock (&qemu_global_mutex); + ret =cpu_exec (env); + +qemu_mutex_lock (&qemu_global_mutex);#ifdef CONFIG_PROFILER qemu_time +=profile_getclock () - ti;#endif
exec.c +33 -0
@@ -1118,6 +1118,7 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,#endif #ifdef TARGET_HAS_PRECISE_SMC if (current_tb_modified) { +qemu_mutex_unlock_iothread ();/* we generate a block containing just the instruction modifying the memory. It will ensure that it cannot modify itself */
@@ -1205,6 +1206,7 @@ static void tb_invalidate_phys_page(tb_page_addr_t addr, p->first_tb =NULL ;#ifdef TARGET_HAS_PRECISE_SMC if (current_tb_modified) { +qemu_mutex_unlock_iothread ();/* we generate a block containing just the instruction modifying the memory. It will ensure that it cannot modify itself */
@@ -2061,9 +2063,11 @@ void tlb_flush_page(CPUState *env, target_ulong addr)can be detected */ static void tlb_protect_code (ram_addr_t ram_addr) { +qemu_mutex_lock_iothread ();cpu_physical_memory_reset_dirty (ram_addr, ram_addr +TARGET_PAGE_SIZE ,CODE_DIRTY_FLAG ); +qemu_mutex_unlock_iothread (); }/* update the TLB so that writes in physical page 'phys_addr' are no longer
@@ -3122,11 +3126,13 @@ void *qemu_get_ram_ptr(ram_addr_t addr) QLIST_FOREACH(block, &ram_list.blocks, next) {if (addr - block->offset < block->length) { +#if 0 /* requires RCU - and like also a smarter balancing algorithm */ /* Move this entry to to start of the list. */ if (block !=QLIST_FIRST (&ram_list.blocks)) {QLIST_REMOVE (block, next);QLIST_INSERT_HEAD (&ram_list.blocks, block, next); } +#endif if (xen_enabled ()) {/* We need to check if the requested address is in the RAM * because we don't want to map the entire memory in QEMU.
@@ -3417,6 +3423,7 @@ static void check_watchpoint(int offset, int len_mask, int flags) (vaddr & wp->len_mask) == wp->vaddr) && (wp->flags & flags)) { wp->flags |=BP_WATCHPOINT_HIT ;if (!env->watchpoint_hit) { +qemu_mutex_unlock_iothread (); env->watchpoint_hit = wp; tb =tb_find_pc (env->mem_io_pc);if (!tb) {
@@ -4194,6 +4201,7 @@ static inline uint32_t ldl_phys_internal(target_phys_addr_t addr, io_index = (pd >>IO_MEM_SHIFT ) & (IO_MEM_NB_ENTRIES -1 );if (p) addr = (addr & ~TARGET_PAGE_MASK ) + p->region_offset; +qemu_mutex_lock_iothread (); val = io_mem_read[io_index][2 ](io_mem_opaque[io_index], addr);#if defined(TARGET_WORDS_BIGENDIAN )if (endian ==DEVICE_LITTLE_ENDIAN ) {
@@ -4204,6 +4212,7 @@ static inline uint32_t ldl_phys_internal(target_phys_addr_t addr, val =bswap32 (val); }#endif +qemu_mutex_unlock_iothread (); }else {/* RAM case */ ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) +
@@ -4264,6 +4273,7 @@ static inline uint64_t ldq_phys_internal(target_phys_addr_t addr,/* XXX This is broken when device endian != cpu endian. Fix and add "endian" variable check */ +qemu_mutex_lock_iothread ();#ifdef TARGET_WORDS_BIGENDIAN val = (uint64_t )io_mem_read[io_index][2 ](io_mem_opaque[io_index], addr) <<32 ; val |= io_mem_read[io_index][2 ](io_mem_opaque[io_index], addr +4 );
@@ -4271,6 +4281,7 @@ static inline uint64_t ldq_phys_internal(target_phys_addr_t addr, val = io_mem_read[io_index][2 ](io_mem_opaque[io_index], addr); val |= (uint64_t )io_mem_read[io_index][2 ](io_mem_opaque[io_index], addr +4 ) <<32 ;#endif +qemu_mutex_unlock_iothread (); }else {/* RAM case */ ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) +
@@ -4336,6 +4347,7 @@ static inline uint32_t lduw_phys_internal(target_phys_addr_t addr, io_index = (pd >>IO_MEM_SHIFT ) & (IO_MEM_NB_ENTRIES -1 );if (p) addr = (addr & ~TARGET_PAGE_MASK ) + p->region_offset; +qemu_mutex_lock_iothread (); val = io_mem_read[io_index][1 ](io_mem_opaque[io_index], addr);#if defined(TARGET_WORDS_BIGENDIAN )if (endian ==DEVICE_LITTLE_ENDIAN ) {
@@ -4346,6 +4358,7 @@ static inline uint32_t lduw_phys_internal(target_phys_addr_t addr, val =bswap16 (val); }#endif +qemu_mutex_unlock_iothread (); }else {/* RAM case */ ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) +
@@ -4401,7 +4414,9 @@ void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val) io_index = (pd >>IO_MEM_SHIFT ) & (IO_MEM_NB_ENTRIES -1 );if (p) addr = (addr & ~TARGET_PAGE_MASK ) + p->region_offset; +qemu_mutex_lock_iothread (); io_mem_write[io_index][2 ](io_mem_opaque[io_index], addr, val); +qemu_mutex_unlock_iothread (); }else {unsigned long addr1 = (pd &TARGET_PAGE_MASK ) + (addr & ~TARGET_PAGE_MASK ); ptr =qemu_get_ram_ptr (addr1);
@@ -4409,11 +4424,13 @@ void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val)if (unlikely (in_migration)) {if (!cpu_physical_memory_is_dirty (addr1)) { +qemu_mutex_lock_iothread ();/* invalidate code */ tb_invalidate_phys_page_range (addr1, addr1 +4 ,0 );/* set dirty bit */ cpu_physical_memory_set_dirty_flags ( addr1, (0xff & ~CODE_DIRTY_FLAG )); +qemu_mutex_unlock_iothread (); } } }
@@ -4437,6 +4454,7 @@ void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val) io_index = (pd >>IO_MEM_SHIFT ) & (IO_MEM_NB_ENTRIES -1 );if (p) addr = (addr & ~TARGET_PAGE_MASK ) + p->region_offset; +qemu_mutex_lock_iothread ();#ifdef TARGET_WORDS_BIGENDIAN io_mem_write[io_index][2 ](io_mem_opaque[io_index], addr, val >>32 ); io_mem_write[io_index][2 ](io_mem_opaque[io_index], addr +4 , val);
@@ -4444,6 +4462,7 @@ void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val) io_mem_write[io_index][2 ](io_mem_opaque[io_index], addr, val); io_mem_write[io_index][2 ](io_mem_opaque[io_index], addr +4 , val >>32 );#endif +qemu_mutex_unlock_iothread (); }else { ptr =qemu_get_ram_ptr (pd &TARGET_PAGE_MASK ) + (addr & ~TARGET_PAGE_MASK );
@@ -4480,7 +4499,9 @@ static inline void stl_phys_internal(target_phys_addr_t addr, uint32_t val, val = bswap32(val); }#endif +qemu_mutex_lock_iothread (); io_mem_write[io_index][2 ](io_mem_opaque[io_index], addr, val); +qemu_mutex_unlock_iothread (); }else {unsigned long addr1; addr1 = (pd &TARGET_PAGE_MASK ) + (addr & ~TARGET_PAGE_MASK );
@@ -4498,11 +4519,13 @@ static inline void stl_phys_internal(target_phys_addr_t addr, uint32_t val,break ; }if (!cpu_physical_memory_is_dirty (addr1)) { +qemu_mutex_lock_iothread ();/* invalidate code */ tb_invalidate_phys_page_range (addr1, addr1 +4 ,0 );/* set dirty bit */ cpu_physical_memory_set_dirty_flags (addr1, (0xff & ~CODE_DIRTY_FLAG )); +qemu_mutex_unlock_iothread (); } } }
@@ -4558,7 +4581,9 @@ static inline void stw_phys_internal(target_phys_addr_t addr, uint32_t val, val = bswap16(val); }#endif +qemu_mutex_lock_iothread (); io_mem_write[io_index][1 ](io_mem_opaque[io_index], addr, val); +qemu_mutex_unlock_iothread (); }else {unsigned long addr1; addr1 = (pd &TARGET_PAGE_MASK ) + (addr & ~TARGET_PAGE_MASK );
@@ -4576,11 +4601,13 @@ static inline void stw_phys_internal(target_phys_addr_t addr, uint32_t val,break ; }if (!cpu_physical_memory_is_dirty (addr1)) { +qemu_mutex_lock_iothread ();/* invalidate code */ tb_invalidate_phys_page_range (addr1, addr1 +2 ,0 );/* set dirty bit */ cpu_physical_memory_set_dirty_flags (addr1, (0xff & ~CODE_DIRTY_FLAG )); +qemu_mutex_unlock_iothread (); } } }
@@ -4604,19 +4631,25 @@ void stw_be_phys(target_phys_addr_t addr, uint32_t val)void stq_phys (target_phys_addr_t addr,uint64_t val) { val =tswap64 (val); +qemu_mutex_lock_iothread ();cpu_physical_memory_write (addr, &val,8 ); +qemu_mutex_unlock_iothread (); }void stq_le_phys (target_phys_addr_t addr,uint64_t val) { val =cpu_to_le64 (val); +qemu_mutex_lock_iothread ();cpu_physical_memory_write (addr, &val,8 ); +qemu_mutex_unlock_iothread (); }void stq_be_phys (target_phys_addr_t addr,uint64_t val) { val =cpu_to_be64 (val); +qemu_mutex_lock_iothread ();cpu_physical_memory_write (addr, &val,8 ); +qemu_mutex_unlock_iothread (); }/* virtual memory access for debug (includes writing to ROM) */
qemu-common.h +8 -3
@@ -96,6 +96,14 @@ static inline char *realpath(const char *path, char *resolved_path) }#endif +#ifndef CONFIG_USER_ONLY +void qemu_mutex_lock_iothread (void ); +void qemu_mutex_unlock_iothread (void ); +#else +static inline void qemu_mutex_lock_iothread (void ) { } +static inline void qemu_mutex_unlock_iothread (void ) { } +#endif +/* FIXME: Remove NEED_CPU_H. */ #ifndef NEED_CPU_H
@@ -183,9 +191,6 @@ const char *path(const char *pathname);void *qemu_oom_check (void *ptr); -void qemu_mutex_lock_iothread (void ); -void qemu_mutex_unlock_iothread (void ); -int qemu_open (const char *name,int flags, ...); ssize_t qemu_write_full(int fd, const void *buf, size_t count) QEMU_WARN_UNUSED_RESULT;
softmmu_template.h +8 -0
@@ -65,6 +65,8 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(target_phys_addr_t physaddr,cpu_io_recompile (env, retaddr); } +qemu_mutex_lock_iothread (); + env->mem_io_vaddr = addr;#if SHIFT <=2 res = io_mem_read[index][SHIFT ](io_mem_opaque[index], physaddr);
@@ -77,6 +79,8 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(target_phys_addr_t physaddr, res |= (uint64_t )io_mem_read[index][2 ](io_mem_opaque[index], physaddr +4 ) <<32 ;#endif #endif /* SHIFT > 2 */ + +qemu_mutex_unlock_iothread ();return res; }
@@ -207,6 +211,8 @@ static inline void glue(io_write, SUFFIX)(target_phys_addr_t physaddr,cpu_io_recompile (env, retaddr); } +qemu_mutex_lock_iothread (); + env->mem_io_vaddr = addr; env->mem_io_pc = (unsigned long )retaddr;#if SHIFT <=2
@@ -220,6 +226,8 @@ static inline void glue(io_write, SUFFIX)(target_phys_addr_t physaddr, io_mem_write[index][2 ](io_mem_opaque[index], physaddr +4 , val >>32 );#endif #endif /* SHIFT > 2 */ + +qemu_mutex_unlock_iothread (); } void REGPARM glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr,
target-i386/op_helper.c +24 -3
@@ -662,32 +662,53 @@ void helper_check_iol(uint32_t t0)void helper_outb (uint32_t port,uint32_t data) { +qemu_mutex_lock_iothread ();cpu_outb (port, data &0xff ); +qemu_mutex_unlock_iothread (); }target_ulong helper_inb (uint32_t port) { -return cpu_inb (port); +target_ulong ret; + +qemu_mutex_lock_iothread (); + ret =cpu_inb (port); +qemu_mutex_unlock_iothread (); +return ret; }void helper_outw (uint32_t port,uint32_t data) { +qemu_mutex_lock_iothread ();cpu_outw (port, data &0xffff ); +qemu_mutex_unlock_iothread (); }target_ulong helper_inw (uint32_t port) { -return cpu_inw (port); +target_ulong ret; + +qemu_mutex_lock_iothread (); + ret =cpu_inw (port); +qemu_mutex_unlock_iothread (); +return ret; }void helper_outl (uint32_t port,uint32_t data) { +qemu_mutex_lock_iothread ();cpu_outl (port, data); +qemu_mutex_unlock_iothread (); }target_ulong helper_inl (uint32_t port) { -return cpu_inl (port); +target_ulong ret; + +qemu_mutex_lock_iothread (); + ret =cpu_inl (port); +qemu_mutex_unlock_iothread (); +return ret; }static inline unsigned int get_sp_mask (unsigned int e2)
Signature · 1 line
1.7.3.4