A vCPU hotplug may happen at any time. When the new thread is started, the region pool may be exhausted. Do not abort.
Rename tcg_region_thread_initial_alloc to differentiate it from tcg_region_initial_alloc__locked. The renamed function now uses tcg_region_alloc__locked and is prepared for failure. In tcg_tb_alloc, allow code_gen_ptr to be NULL. Treat that as any other region exhaustion. Reorg with while instead of goto. Reported-by: Anushree Mathur <[email protected]> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/2984 Signed-off-by: Richard Henderson <[email protected]> --- tcg/tcg-internal.h | 2 +- tcg/region.c | 24 ++++++++++++++++++++++-- tcg/tcg.c | 22 ++++++++++++++-------- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/tcg/tcg-internal.h b/tcg/tcg-internal.h index c0997ab224..e35440dc8c 100644 --- a/tcg/tcg-internal.h +++ b/tcg/tcg-internal.h @@ -42,7 +42,7 @@ extern unsigned int tcg_max_ctxs; void tcg_region_init(size_t tb_size, int splitwx, unsigned max_threads); bool tcg_region_alloc(TCGContext *s); -void tcg_region_initial_alloc(TCGContext *s); +void tcg_region_thread_initial_alloc(TCGContext *s); void tcg_region_prologue_set(TCGContext *s); static inline void *tcg_call_func(TCGOp *op) diff --git a/tcg/region.c b/tcg/region.c index 8ee8c39c43..8bd378b212 100644 --- a/tcg/region.c +++ b/tcg/region.c @@ -396,11 +396,31 @@ static void tcg_region_initial_alloc__locked(TCGContext *s) g_assert(ok); } -void tcg_region_initial_alloc(TCGContext *s) +void tcg_region_thread_initial_alloc(TCGContext *s) { + bool ok; + qemu_mutex_lock(®ion.lock); - tcg_region_initial_alloc__locked(s); + ok = tcg_region_alloc__locked(s); qemu_mutex_unlock(®ion.lock); + + /* + * A vCPU hotplug may happen at any time. When the new thread is + * started, the region pool may be exhausted. At this point in + * the new thread call stack, we are not in a position to fix this. + * Leave code_gen_ptr NULL, so that this thread's first call to + * tcg_tb_alloc() returns NULL, so that the translator performs + * a tb_flush() and retry. + * + * During the tb_flush(), tcg_region_reset_all() will assign a + * new region to all contexts, including this one. + */ + if (!ok) { + s->code_gen_buffer = NULL; + s->code_gen_ptr = NULL; + s->code_gen_buffer_size = 0; + s->code_gen_highwater = NULL; + } } /* Call from a safe-work context */ diff --git a/tcg/tcg.c b/tcg/tcg.c index af15c3d63e..db43589fa2 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1279,7 +1279,7 @@ void tcg_register_thread(void) qatomic_set(&tcg_ctxs[n], s); if (n > 0) { - tcg_region_initial_alloc(s); + tcg_region_thread_initial_alloc(s); } tcg_ctx = s; @@ -1830,18 +1830,24 @@ TranslationBlock *tcg_tb_alloc(TCGContext *s) TranslationBlock *tb; void *next; - retry: - tb = (void *)ROUND_UP((uintptr_t)s->code_gen_ptr, align); - next = (void *)ROUND_UP((uintptr_t)(tb + 1), align); + while (1) { + tb = (void *)ROUND_UP((uintptr_t)s->code_gen_ptr, align); - if (unlikely(next > s->code_gen_highwater)) { + /* + * Note that code_gen_ptr can be NULL after vCPU hotplug. + * See tcg_region_thread_initial_alloc. + */ + if (tb) { + next = (void *)ROUND_UP((uintptr_t)(tb + 1), align); + if (next <= s->code_gen_highwater) { + qatomic_set(&s->code_gen_ptr, next); + return tb; + } + } if (!tcg_region_alloc(s)) { return NULL; } - goto retry; } - qatomic_set(&s->code_gen_ptr, next); - return tb; } void tcg_prologue_init(void) -- 2.43.0
