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(®ion.lock);
402
- tcg_region_initial_alloc__locked(s);
404
+ ok = tcg_region_alloc__locked(s);
405
qemu_mutex_unlock(®ion.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)