The next patch dispatches a goto_jc by probing the TB jump cache from
generated code. That probe cannot check everything helper_lookup_tb_ptr()
checks, and the one that matters is breakpoints: check_for_breakpoints()
raises EXCP_DEBUG on an exact pc match and selects CF_BP_PAGE cflags for the
rest of the page, and inserting a breakpoint deliberately invalidates no TB.
The probe does compare the destination's cflags against the cflags of the
block doing the dispatching, and only takes the destination when they are
equal. So a cflag is all that is needed. Add CF_NO_GOTO_JC, set it in
CPUState::tcg_cflags while cpu->breakpoints is non-empty, and blocks
translated from then on both decline to dispatch inline themselves -- the
next patch makes them emit the plain helper call -- and are unreachable from
blocks that do, because their cflags no longer match.
The two ends of the flag are cpu_breakpoint_insert() and
cpu_breakpoint_remove_by_ref(), which are the only places the list changes.
Both already run either on the CPU's own thread or with it stopped, or reach
another CPU exactly as cpu_single_step() does, which is where the previous
patch put the same kind of update.
That leaves blocks translated before the breakpoint was inserted, which are
still live and still chain to each other. They do so on the old cflags, so
inline dispatch among them keeps working until the vCPU reaches its main
loop, which then looks up with the new cflags and translates afresh. In
system mode gdb inserts breakpoints with the vCPUs stopped, so there is no
window at all. In user mode the window is the one goto_tb chaining already
has: a chained direct jump consults nothing either, and is not broken by
inserting a breakpoint.
Nothing reads CF_NO_GOTO_JC yet; the next patch does.
v5: New patch, replacing "accel/tcg: give the TB jump cache a second base
pointer for generated code", which forced the same fallback by pointing
generated code at a zero-filled jump cache when a breakpoint was
inserted, and needed a cross-thread poison and an un-poison race to do
it. Richard Henderson suggested a cflag instead, and pointed out that
the previous patch had already shown how to update tcg_cflags from
gdbstub. The base pointer comes back later in the series, for pending
exits, which a cflag cannot express.
Signed-off-by: Matt Turner <[email protected]>
---
accel/tcg/cpu-exec-common.c | 13 ++++++++++++-
cpu-common.c | 7 +++++++
include/exec/translation-block.h | 1 +
3 files changed, 20 insertions(+), 1 deletion(-)
diff --git ./accel/tcg/cpu-exec-common.c ./accel/tcg/cpu-exec-common.c
index 9f3517f36b..a3148bbf8f 100644
--- ./accel/tcg/cpu-exec-common.c
+++ ./accel/tcg/cpu-exec-common.c
@@ -41,7 +41,7 @@ void tcg_cflags_set(CPUState *cpu, uint32_t flags)
* they are derived from gdb single-step, one-insn-per-tb and -d nochain.
*/
#define CF_DERIVED (CF_COUNT_MASK | CF_NO_GOTO_TB | CF_NO_GOTO_PTR | \
- CF_SINGLE_STEP)
+ CF_SINGLE_STEP | CF_NO_GOTO_JC)
void tcg_update_cflags(CPUState *cpu)
{
@@ -62,6 +62,17 @@ void tcg_update_cflags(CPUState *cpu)
cflags |= CF_NO_GOTO_TB;
}
+ /*
+ * A block that dispatches through the jump cache inline does not consult
+ * cpu->breakpoints, and inserting a breakpoint deliberately invalidates
+ * nothing. Give blocks translated while one is set a distinct cflags, so
+ * that they neither dispatch inline themselves nor are reached by a block
+ * that does, and check_for_breakpoints() gets to run on every dispatch.
+ */
+ if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
+ cflags |= CF_NO_GOTO_JC;
+ }
+
cpu->tcg_cflags = cflags;
}
diff --git ./cpu-common.c ./cpu-common.c
index adb76b3a78..3178601987 100644
--- ./cpu-common.c
+++ ./cpu-common.c
@@ -22,6 +22,7 @@
#include "exec/cpu-common.h"
#include "hw/core/cpu.h"
#include "qemu/lockable.h"
+#include "system/tcg.h"
#include "trace/trace-root.h"
QemuMutex qemu_cpu_list_lock;
@@ -429,6 +430,9 @@ int cpu_breakpoint_insert(CPUState *cpu, vaddr pc, int
flags,
*breakpoint = bp;
}
+ /* The first breakpoint takes the CPU off the inline dispatch path. */
+ tcg_update_cflags(cpu);
+
trace_breakpoint_insert(cpu->cpu_index, pc, flags);
return 0;
}
@@ -456,6 +460,9 @@ void cpu_breakpoint_remove_by_ref(CPUState *cpu,
CPUBreakpoint *bp)
{
QTAILQ_REMOVE(&cpu->breakpoints, bp, entry);
+ /* The last breakpoint puts the CPU back on it. */
+ tcg_update_cflags(cpu);
+
trace_breakpoint_remove(cpu->cpu_index, bp->pc, bp->flags);
g_free(bp);
}
diff --git ./include/exec/translation-block.h ./include/exec/translation-block.h
index 40cc699031..8c4778c681 100644
--- ./include/exec/translation-block.h
+++ ./include/exec/translation-block.h
@@ -84,6 +84,7 @@ struct TranslationBlock {
#define CF_NOIRQ 0x00010000 /* Generate an uninterruptible TB */
#define CF_PCREL 0x00020000 /* Opcodes in TB are PC-relative */
#define CF_BP_PAGE 0x00040000 /* Breakpoint present in code page */
+#define CF_NO_GOTO_JC 0x00080000 /* Do not dispatch via the inline probe */
#define CF_CLUSTER_MASK 0xff000000 /* Top 8 bits are cluster ID */
#define CF_CLUSTER_SHIFT 24
--
2.54.0