diff mbox series

[4/4] icount: preserve cflags when custom tb is about to execute

Message ID 163542170287.2127597.18369415404458239885.stgit@pasha-ThinkPad-X280
State New
Headers show
Series Some watchpoint-related patches | expand

Commit Message

Pavel Dovgalyuk Oct. 28, 2021, 11:48 a.m. UTC
When debugging with the watchpoints, qemu may need to create
TB with single instruction. This is achieved by setting cpu->cflags_next_tb.
But when this block is about to execute, it may be interrupted by another
thread. In this case cflags will be lost and next executed TB will not
be the special one.
This patch checks TB exit reason and restores cflags_next_tb to allow
finding the interrupted block.

Signed-off-by: Pavel Dovgalyuk <Pavel.Dovgalyuk@ispras.ru>
---
 accel/tcg/cpu-exec.c |   10 ++++++++++
 1 file changed, 10 insertions(+)

Comments

Richard Henderson Oct. 28, 2021, 7:26 p.m. UTC | #1
On 10/28/21 4:48 AM, Pavel Dovgalyuk wrote:
> +        if (cpu->cflags_next_tb == -1
> +            && (!use_icount || !(tb->cflags & CF_USE_ICOUNT)
> +                || cpu_neg(cpu)->icount_decr.u16.low >= tb->icount)) {
> +            /*
> +             * icount is disabled or there are enough instructions
> +             * in the budget, do not retranslate this block with
> +             * different parameters.
> +             */
> +            cpu->cflags_next_tb = tb->cflags;
> +        }

I can't see that this will work.

We've been asked to exit to the main loop; probably for an interrupt.  The next thing 
that's likely to happen is that cpu_cc->do_interrupt will adjust cpu state to continue in 
the guest interrupt handler.  The cflags_next_tb flag that you're setting up is not 
relevant to that context.

This seems related to Phil's reported problem

   https://gitlab.com/qemu-project/qemu/-/issues/245

in which an interrupt arrives before we finish processing the watchpoint.

I *think* we need to make cflags_next_tb != -1 be treated like any other interrupt disable 
bit, and delay delivery of the interrupt.  Which also means that we should not check for 
exit_tb at the beginning of any TB we generate for the watchpoint step.

I simply haven't taken the time to investigate this properly.


r~
Pavel Dovgalyuk Nov. 3, 2021, 8:44 a.m. UTC | #2
On 28.10.2021 22:26, Richard Henderson wrote:
> On 10/28/21 4:48 AM, Pavel Dovgalyuk wrote:
>> +        if (cpu->cflags_next_tb == -1
>> +            && (!use_icount || !(tb->cflags & CF_USE_ICOUNT)
>> +                || cpu_neg(cpu)->icount_decr.u16.low >= tb->icount)) {
>> +            /*
>> +             * icount is disabled or there are enough instructions
>> +             * in the budget, do not retranslate this block with
>> +             * different parameters.
>> +             */
>> +            cpu->cflags_next_tb = tb->cflags;
>> +        }
> 
> I can't see that this will work.

The issue was the following:
- icount is enabled
- watchpoint hit
- next block should contain a single instruction to stop the execution 
after memory access
- tb with one instruction is translated, cflags_next_tb is reset
- main loop breaks the execution for some reason
- after resuming the execution, we are trying to find new tb without any 
filter in cflags
- tb with multiple instructions is executed (instead of previously 
generated)

> 
> We've been asked to exit to the main loop; probably for an interrupt.  
> The next thing that's likely to happen is that cpu_cc->do_interrupt will 
> adjust cpu state to continue in the guest interrupt handler.  The 
> cflags_next_tb flag that you're setting up is not relevant to that context.
> 
> This seems related to Phil's reported problem
> 
>    https://gitlab.com/qemu-project/qemu/-/issues/245
> 
> in which an interrupt arrives before we finish processing the watchpoint.

Right, this is similar.

> 
> I *think* we need to make cflags_next_tb != -1 be treated like any other 
> interrupt disable bit, and delay delivery of the interrupt.  Which also 
> means that we should not check for exit_tb at the beginning of any TB we 
> generate for the watchpoint step.
> 
> I simply haven't taken the time to investigate this properly.
> 
> 
> r~
Pavel Dovgalyuk Nov. 11, 2021, 9:56 a.m. UTC | #3
On 28.10.2021 22:26, Richard Henderson wrote:
> On 10/28/21 4:48 AM, Pavel Dovgalyuk wrote:
>> +        if (cpu->cflags_next_tb == -1
>> +            && (!use_icount || !(tb->cflags & CF_USE_ICOUNT)
>> +                || cpu_neg(cpu)->icount_decr.u16.low >= tb->icount)) {
>> +            /*
>> +             * icount is disabled or there are enough instructions
>> +             * in the budget, do not retranslate this block with
>> +             * different parameters.
>> +             */
>> +            cpu->cflags_next_tb = tb->cflags;
>> +        }
> 
> I can't see that this will work.
> 
> We've been asked to exit to the main loop; probably for an interrupt.  
> The next thing that's likely to happen is that cpu_cc->do_interrupt will 
> adjust cpu state to continue in the guest interrupt handler.  The 
> cflags_next_tb flag that you're setting up is not relevant to that context.
> 
> This seems related to Phil's reported problem
> 
>    https://gitlab.com/qemu-project/qemu/-/issues/245
> 
> in which an interrupt arrives before we finish processing the watchpoint.
> 
> I *think* we need to make cflags_next_tb != -1 be treated like any other 
> interrupt disable bit, and delay delivery of the interrupt.  Which also 
> means that we should not check for exit_tb at the beginning of any TB we 
> generate for the watchpoint step.
> 
> I simply haven't taken the time to investigate this properly.

Please check the new series, it probably solves the problem described in 
that issue.

Pavel Dovgalyuk
diff mbox series

Patch

diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index c9764c1325..af1c6e6ba3 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -842,6 +842,16 @@  static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb,
          * cpu_handle_interrupt.  cpu_handle_interrupt will also
          * clear cpu->icount_decr.u16.high.
          */
+        if (cpu->cflags_next_tb == -1
+            && (!use_icount || !(tb->cflags & CF_USE_ICOUNT)
+                || cpu_neg(cpu)->icount_decr.u16.low >= tb->icount)) {
+            /*
+             * icount is disabled or there are enough instructions
+             * in the budget, do not retranslate this block with
+             * different parameters.
+             */
+            cpu->cflags_next_tb = tb->cflags;
+        }
         return;
     }