@samitouri / QOSamiQemu / commits / f5d2d8532f

tcg: Defer tb_flush when initial thread region alloc fails

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. Tested-by: Yogesh Vyas <yvyas1991@gmail.com> Reviewed-by: Yogesh Vyas <yvyas1991@gmail.com> Reported-by: Anushree Mathur <anushree.mathur@linux.ibm.com> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/2984 Signed-off-by: Richard Henderson <richard.henderson@linaro.org>

Richard Henderson committed Aug 12, 2026 at 17:11 UTC f5d2d8532f3c3cefb644fe9c1866a21b75f76faf
3 files changed +37 -11
tcg/region.c
+22 -2
@@ -396,11 +396,31 @@ static void tcg_region_initial_alloc__locked(TCGContext *s)
396 g_assert(ok);
397 }
398
399 -void tcg_region_initial_alloc(TCGContext *s)
399 +void tcg_region_thread_initial_alloc(TCGContext *s)
400 {
401 + bool ok;
402 +
403 qemu_mutex_lock(&region.lock);
402 - tcg_region_initial_alloc__locked(s);
404 + ok = tcg_region_alloc__locked(s);
405 qemu_mutex_unlock(&region.lock);
406 +
407 + /*
408 + * A vCPU hotplug may happen at any time. When the new thread is
409 + * started, the region pool may be exhausted. At this point in
410 + * the new thread call stack, we are not in a position to fix this.
411 + * Leave code_gen_ptr NULL, so that this thread's first call to
412 + * tcg_tb_alloc() returns NULL, so that the translator performs
413 + * a tb_flush() and retry.
414 + *
415 + * During the tb_flush(), tcg_region_reset_all() will assign a
416 + * new region to all contexts, including this one.
417 + */
418 + if (!ok) {
419 + s->code_gen_buffer = NULL;
420 + s->code_gen_ptr = NULL;
421 + s->code_gen_buffer_size = 0;
422 + s->code_gen_highwater = NULL;
423 + }
424 }
425
426 /* Call from a safe-work context */
tcg/tcg-internal.h
+1 -1
@@ -42,7 +42,7 @@ extern unsigned int tcg_max_ctxs;
42
43 void tcg_region_init(size_t tb_size, int splitwx, unsigned max_threads);
44 bool tcg_region_alloc(TCGContext *s);
45 -void tcg_region_initial_alloc(TCGContext *s);
45 +void tcg_region_thread_initial_alloc(TCGContext *s);
46 void tcg_region_prologue_set(TCGContext *s);
47
48 static inline void *tcg_call_func(TCGOp *op)
tcg/tcg.c
+14 -8
@@ -1279,7 +1279,7 @@ void tcg_register_thread(void)
1279 qatomic_set(&tcg_ctxs[n], s);
1280
1281 if (n > 0) {
1282 - tcg_region_initial_alloc(s);
1282 + tcg_region_thread_initial_alloc(s);
1283 }
1284
1285 tcg_ctx = s;
@@ -1830,18 +1830,24 @@ TranslationBlock *tcg_tb_alloc(TCGContext *s)
1830 TranslationBlock *tb;
1831 void *next;
1832
1833 - retry:
1834 - tb = (void *)ROUND_UP((uintptr_t)s->code_gen_ptr, align);
1835 - next = (void *)ROUND_UP((uintptr_t)(tb + 1), align);
1833 + while (1) {
1834 + tb = (void *)ROUND_UP((uintptr_t)s->code_gen_ptr, align);
1835
1837 - if (unlikely(next > s->code_gen_highwater)) {
1836 + /*
1837 + * Note that code_gen_ptr can be NULL after vCPU hotplug.
1838 + * See tcg_region_thread_initial_alloc.
1839 + */
1840 + if (tb) {
1841 + next = (void *)ROUND_UP((uintptr_t)(tb + 1), align);
1842 + if (next <= s->code_gen_highwater) {
1843 + qatomic_set(&s->code_gen_ptr, next);
1844 + return tb;
1845 + }
1846 + }
1847 if (!tcg_region_alloc(s)) {
1848 return NULL;
1849 }
1841 - goto retry;
1850 }
1843 - qatomic_set(&s->code_gen_ptr, next);
1844 - return tb;
1851 }
1852
1853 void tcg_prologue_init(void)