Re: [PATCH 5/5] contrib/plugins: add ips plugin example for cost modeling

2024-05-29 Thread Alex Bennée
Pierrick Bouvier  writes:

> On 5/29/24 05:13, Alex Bennée wrote:
>> Pierrick Bouvier  writes:
>> (Added Philip to CC)
>> 
>>> On 5/28/24 12:57, Alex Bennée wrote:
 Pierrick Bouvier  writes:

> On 5/28/24 12:14, Alex Bennée wrote:
>> Pierrick Bouvier  writes:
>>
>>> This plugin uses the new time control interface to make decisions
>>> about the state of time during the emulation. The algorithm is
>>> currently very simple. The user specifies an ips rate which applies
>>> per core.
>> 
>> 
>>> +static void vcpu_init(qemu_plugin_id_t id, unsigned int cpu_index)
>>> +{
>>> +vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
>>> +/* ensure start time is set first */
>>> +set_start_time();
>>> +/* start counter from absolute time reference */
>>> +vcpu->counter = num_insn_during(uptime_ns());
>>> +vcpu_set_state(vcpu, EXECUTING);
>>> +}
>>> +
>>> +static void vcpu_idle(qemu_plugin_id_t id, unsigned int cpu_index)
>>> +{
>>> +vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
>>> +vcpu_set_state(vcpu, IDLE);
>>> +}
>>> +
>>> +static void vcpu_resume(qemu_plugin_id_t id, unsigned int cpu_index)
>>> +{
>>> +vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
>>> +g_assert(vcpu->state == IDLE);
>> I'm triggering a weird race here:
>>  (gdb) b vcpu_init
>>  Breakpoint 1 at 0x77fa15f7: file 
>> /home/alex/lsrc/qemu.git/contrib/plugins/ips.c, line 127.
>>  (gdb) r
>>  The program being debugged has been started already.
>>  Start it from the beginning? (y or n) y
>>  Starting program:
>> /home/alex/lsrc/qemu.git/builds/arm.debug/qemu-system-aarch64
>> -machine type=virt,virtualization=on,pflash0=rom,pflash1=efivars
>> -cpu cortex-a57 -smp 32 -accel tcg -device
>> virtio-net-pci,netdev=unet -device virtio-scsi-pci -device
>> scsi-hd,drive=hd -netdev user,id=unet,hostfwd=tcp::-:22
>> -blockdev
>> driver=raw,node-name=hd,file.driver=host_device,file.filename=/dev/zen-ssd2/trixie-arm64,discard=unmap
>> -serial mon:stdio -blockdev
>> node-name=rom,driver=file,filename=/home/alex/lsrc/qemu.git/builds/arm.debug/pc-bios/edk2-aarch64-code.fd,read-only=true
>> -blockdev
>> node-name=efivars,driver=file,filename=/home/alex/images/qemu-arm64-efivars
>> -m 8192 -object memory-backend-memfd,id=mem,size=8G,share=on -kernel
>> /home/alex/lsrc/linux.git/builds/arm64/arch/arm64/boot/Image -append
>> root=/dev/sda2\ console=ttyAMA0 -plugin
>> contrib/plugins/libips.so,ips=10
>>  [Thread debugging using libthread_db enabled]
>>  Using host libthread_db library 
>> "/lib/x86_64-linux-gnu/libthread_db.so.1".
>>  [New Thread 0x7fffe72006c0 (LWP 360538)]
>>  [New Thread 0x7fffe68006c0 (LWP 360540)]
>>  [New Thread 0x7fffe5e006c0 (LWP 360541)]
>>  [New Thread 0x7fffe54006c0 (LWP 360542)]
>>  [New Thread 0x7fffe4a006c0 (LWP 360543)]
>>  [New Thread 0x7fffdfe006c0 (LWP 360544)]
>>  [New Thread 0x7fffdf4006c0 (LWP 360545)]
>>  [New Thread 0x7fffdea006c0 (LWP 360546)]
>>  [Switching to Thread 0x7fffdf4006c0 (LWP 360545)]
>>  Thread 8 "qemu-system-aar" hit Breakpoint 1, vcpu_init
>> (id=10457908569352202058, cpu_index=0) at
>> /home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
>>  127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, 
>> cpu_index);
>>  (gdb) c
>>  Continuing.
>>  [New Thread 0x7fffde0006c0 (LWP 360548)]
>>  [Switching to Thread 0x7fffdea006c0 (LWP 360546)]
>>  Thread 9 "qemu-system-aar" hit Breakpoint 1, vcpu_init
>> (id=10457908569352202058, cpu_index=1) at
>> /home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
>>  127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, 
>> cpu_index);
>>  (gdb)
>>  Continuing.
>>  [New Thread 0x7fffdd6006c0 (LWP 360549)]
>>  [Switching to Thread 0x7fffde0006c0 (LWP 360548)]
>>  Thread 10 "qemu-system-aar" hit Breakpoint 1, vcpu_init
>> (id=10457908569352202058, cpu_index=2) at
>> /home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
>>  127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, 
>> cpu_index);
>>  (gdb)
>>  Continuing.
>>  [New Thread 0x7fffdcc006c0 (LWP 360550)]
>>  [Switching to Thread 0x7fffdd6006c0 (LWP 360549)]
>>  Thread 11 "qemu-system-aar" hit Breakpoint 1, vcpu_init
>> (id=10457908569352202058, cpu_index=3) at
>> /home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
>>  127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, 
>> cpu_index);
>>  (gdb)
>>  Continuing.
>>  [New Thread 0x7fffd3e006c0 (LWP 

Re: [PATCH 5/5] contrib/plugins: add ips plugin example for cost modeling

2024-05-29 Thread Pierrick Bouvier

On 5/29/24 05:13, Alex Bennée wrote:

Pierrick Bouvier  writes:

(Added Philip to CC)


On 5/28/24 12:57, Alex Bennée wrote:

Pierrick Bouvier  writes:


On 5/28/24 12:14, Alex Bennée wrote:

Pierrick Bouvier  writes:


This plugin uses the new time control interface to make decisions
about the state of time during the emulation. The algorithm is
currently very simple. The user specifies an ips rate which applies
per core.





+static void vcpu_init(qemu_plugin_id_t id, unsigned int cpu_index)
+{
+vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
+/* ensure start time is set first */
+set_start_time();
+/* start counter from absolute time reference */
+vcpu->counter = num_insn_during(uptime_ns());
+vcpu_set_state(vcpu, EXECUTING);
+}
+
+static void vcpu_idle(qemu_plugin_id_t id, unsigned int cpu_index)
+{
+vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
+vcpu_set_state(vcpu, IDLE);
+}
+
+static void vcpu_resume(qemu_plugin_id_t id, unsigned int cpu_index)
+{
+vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
+g_assert(vcpu->state == IDLE);

I'm triggering a weird race here:
 (gdb) b vcpu_init
 Breakpoint 1 at 0x77fa15f7: file 
/home/alex/lsrc/qemu.git/contrib/plugins/ips.c, line 127.
 (gdb) r
 The program being debugged has been started already.
 Start it from the beginning? (y or n) y
 Starting program:
/home/alex/lsrc/qemu.git/builds/arm.debug/qemu-system-aarch64
-machine type=virt,virtualization=on,pflash0=rom,pflash1=efivars
-cpu cortex-a57 -smp 32 -accel tcg -device
virtio-net-pci,netdev=unet -device virtio-scsi-pci -device
scsi-hd,drive=hd -netdev user,id=unet,hostfwd=tcp::-:22
-blockdev
driver=raw,node-name=hd,file.driver=host_device,file.filename=/dev/zen-ssd2/trixie-arm64,discard=unmap
-serial mon:stdio -blockdev
node-name=rom,driver=file,filename=/home/alex/lsrc/qemu.git/builds/arm.debug/pc-bios/edk2-aarch64-code.fd,read-only=true
-blockdev
node-name=efivars,driver=file,filename=/home/alex/images/qemu-arm64-efivars
-m 8192 -object memory-backend-memfd,id=mem,size=8G,share=on -kernel
/home/alex/lsrc/linux.git/builds/arm64/arch/arm64/boot/Image -append
root=/dev/sda2\ console=ttyAMA0 -plugin
contrib/plugins/libips.so,ips=10
 [Thread debugging using libthread_db enabled]
 Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
 [New Thread 0x7fffe72006c0 (LWP 360538)]
 [New Thread 0x7fffe68006c0 (LWP 360540)]
 [New Thread 0x7fffe5e006c0 (LWP 360541)]
 [New Thread 0x7fffe54006c0 (LWP 360542)]
 [New Thread 0x7fffe4a006c0 (LWP 360543)]
 [New Thread 0x7fffdfe006c0 (LWP 360544)]
 [New Thread 0x7fffdf4006c0 (LWP 360545)]
 [New Thread 0x7fffdea006c0 (LWP 360546)]
 [Switching to Thread 0x7fffdf4006c0 (LWP 360545)]
 Thread 8 "qemu-system-aar" hit Breakpoint 1, vcpu_init
(id=10457908569352202058, cpu_index=0) at
/home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
 127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
 (gdb) c
 Continuing.
 [New Thread 0x7fffde0006c0 (LWP 360548)]
 [Switching to Thread 0x7fffdea006c0 (LWP 360546)]
 Thread 9 "qemu-system-aar" hit Breakpoint 1, vcpu_init
(id=10457908569352202058, cpu_index=1) at
/home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
 127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
 (gdb)
 Continuing.
 [New Thread 0x7fffdd6006c0 (LWP 360549)]
 [Switching to Thread 0x7fffde0006c0 (LWP 360548)]
 Thread 10 "qemu-system-aar" hit Breakpoint 1, vcpu_init
(id=10457908569352202058, cpu_index=2) at
/home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
 127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
 (gdb)
 Continuing.
 [New Thread 0x7fffdcc006c0 (LWP 360550)]
 [Switching to Thread 0x7fffdd6006c0 (LWP 360549)]
 Thread 11 "qemu-system-aar" hit Breakpoint 1, vcpu_init
(id=10457908569352202058, cpu_index=3) at
/home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
 127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
 (gdb)
 Continuing.
 [New Thread 0x7fffd3e006c0 (LWP 360551)]
 [Switching to Thread 0x7fffdcc006c0 (LWP 360550)]
 Thread 12 "qemu-system-aar" hit Breakpoint 1, vcpu_init
(id=10457908569352202058, cpu_index=4) at
/home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
 127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
 (gdb) n
 129 set_start_time();
 (gdb)
 131 vcpu->counter = num_insn_during(uptime_ns());
 (gdb)
 132 vcpu_set_state(vcpu, EXECUTING);
 (gdb)
 133 }
 (gdb) p vcpu->state
 $1 = EXECUTING
 (gdb) p >state
 $2 = (vCPUState *) 0x57c6b5d0
 (gdb) watch *(vCPUState *) 0x57c6b5d0
 Hardware watchpoint 2: *(vCPUState *) 0x57c6b5d0
 (gdb) c
 Continuing.
 

Re: [PATCH 5/5] contrib/plugins: add ips plugin example for cost modeling

2024-05-29 Thread Alex Bennée
Pierrick Bouvier  writes:

(Added Philip to CC)

> On 5/28/24 12:57, Alex Bennée wrote:
>> Pierrick Bouvier  writes:
>> 
>>> On 5/28/24 12:14, Alex Bennée wrote:
 Pierrick Bouvier  writes:

> This plugin uses the new time control interface to make decisions
> about the state of time during the emulation. The algorithm is
> currently very simple. The user specifies an ips rate which applies
> per core.



> +static void vcpu_init(qemu_plugin_id_t id, unsigned int cpu_index)
> +{
> +vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
> +/* ensure start time is set first */
> +set_start_time();
> +/* start counter from absolute time reference */
> +vcpu->counter = num_insn_during(uptime_ns());
> +vcpu_set_state(vcpu, EXECUTING);
> +}
> +
> +static void vcpu_idle(qemu_plugin_id_t id, unsigned int cpu_index)
> +{
> +vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
> +vcpu_set_state(vcpu, IDLE);
> +}
> +
> +static void vcpu_resume(qemu_plugin_id_t id, unsigned int cpu_index)
> +{
> +vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index);
> +g_assert(vcpu->state == IDLE);
 I'm triggering a weird race here:
 (gdb) b vcpu_init
 Breakpoint 1 at 0x77fa15f7: file 
 /home/alex/lsrc/qemu.git/contrib/plugins/ips.c, line 127.
 (gdb) r
 The program being debugged has been started already.
 Start it from the beginning? (y or n) y
 Starting program:
 /home/alex/lsrc/qemu.git/builds/arm.debug/qemu-system-aarch64
 -machine type=virt,virtualization=on,pflash0=rom,pflash1=efivars
 -cpu cortex-a57 -smp 32 -accel tcg -device
 virtio-net-pci,netdev=unet -device virtio-scsi-pci -device
 scsi-hd,drive=hd -netdev user,id=unet,hostfwd=tcp::-:22
 -blockdev
 driver=raw,node-name=hd,file.driver=host_device,file.filename=/dev/zen-ssd2/trixie-arm64,discard=unmap
 -serial mon:stdio -blockdev
 node-name=rom,driver=file,filename=/home/alex/lsrc/qemu.git/builds/arm.debug/pc-bios/edk2-aarch64-code.fd,read-only=true
 -blockdev
 node-name=efivars,driver=file,filename=/home/alex/images/qemu-arm64-efivars
 -m 8192 -object memory-backend-memfd,id=mem,size=8G,share=on -kernel
 /home/alex/lsrc/linux.git/builds/arm64/arch/arm64/boot/Image -append
 root=/dev/sda2\ console=ttyAMA0 -plugin
 contrib/plugins/libips.so,ips=10
 [Thread debugging using libthread_db enabled]
 Using host libthread_db library 
 "/lib/x86_64-linux-gnu/libthread_db.so.1".
 [New Thread 0x7fffe72006c0 (LWP 360538)]
 [New Thread 0x7fffe68006c0 (LWP 360540)]
 [New Thread 0x7fffe5e006c0 (LWP 360541)]
 [New Thread 0x7fffe54006c0 (LWP 360542)]
 [New Thread 0x7fffe4a006c0 (LWP 360543)]
 [New Thread 0x7fffdfe006c0 (LWP 360544)]
 [New Thread 0x7fffdf4006c0 (LWP 360545)]
 [New Thread 0x7fffdea006c0 (LWP 360546)]
 [Switching to Thread 0x7fffdf4006c0 (LWP 360545)]
 Thread 8 "qemu-system-aar" hit Breakpoint 1, vcpu_init
 (id=10457908569352202058, cpu_index=0) at
 /home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
 127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, 
 cpu_index);
 (gdb) c
 Continuing.
 [New Thread 0x7fffde0006c0 (LWP 360548)]
 [Switching to Thread 0x7fffdea006c0 (LWP 360546)]
 Thread 9 "qemu-system-aar" hit Breakpoint 1, vcpu_init
 (id=10457908569352202058, cpu_index=1) at
 /home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
 127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, 
 cpu_index);
 (gdb)
 Continuing.
 [New Thread 0x7fffdd6006c0 (LWP 360549)]
 [Switching to Thread 0x7fffde0006c0 (LWP 360548)]
 Thread 10 "qemu-system-aar" hit Breakpoint 1, vcpu_init
 (id=10457908569352202058, cpu_index=2) at
 /home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
 127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, 
 cpu_index);
 (gdb)
 Continuing.
 [New Thread 0x7fffdcc006c0 (LWP 360550)]
 [Switching to Thread 0x7fffdd6006c0 (LWP 360549)]
 Thread 11 "qemu-system-aar" hit Breakpoint 1, vcpu_init
 (id=10457908569352202058, cpu_index=3) at
 /home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
 127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, 
 cpu_index);
 (gdb)
 Continuing.
 [New Thread 0x7fffd3e006c0 (LWP 360551)]
 [Switching to Thread 0x7fffdcc006c0 (LWP 360550)]
 Thread 12 "qemu-system-aar" hit Breakpoint 1, vcpu_init
 (id=10457908569352202058, cpu_index=4) at
 /home/alex/lsrc/qemu.git/contrib/plugins/ips.c:127
 127 vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, 
 

Re: [PATCH 5/5] contrib/plugins: add ips plugin example for cost modeling

2024-05-28 Thread Pierrick Bouvier

On 5/28/24 12:57, Alex Bennée wrote:

Pierrick Bouvier  writes:


On 5/28/24 12:14, Alex Bennée wrote:

Pierrick Bouvier  writes:


This plugin uses the new time control interface to make decisions
about the state of time during the emulation. The algorithm is
currently very simple. The user specifies an ips rate which applies
per core. If the core runs ahead of its allocated execution time the
plugin sleeps for a bit to let real time catch up. Either way time is
updated for the emulation as a function of total executed instructions
with some adjustments for cores that idle.

Examples


Slow down execution of /bin/true:
$ num_insn=$(./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d plugin 
/bin/true |& grep total | sed -e 's/.*: //')
$ time ./build/qemu-x86_64 -plugin 
./build/contrib/plugins/libips.so,ips=$(($num_insn/4)) /bin/true
real 4.000s

Boot a Linux kernel simulating a 250MHz cpu:
$ /build/qemu-system-x86_64 -kernel /boot/vmlinuz-6.1.0-21-amd64 -append 
"console=ttyS0" -plugin 
./build/contrib/plugins/libips.so,ips=$((250*1000*1000)) -smp 1 -m 512
check time until kernel panic on serial0

Signed-off-by: Pierrick Bouvier 
---
   contrib/plugins/ips.c| 239 +++
   contrib/plugins/Makefile |   1 +
   2 files changed, 240 insertions(+)
   create mode 100644 contrib/plugins/ips.c

diff --git a/contrib/plugins/ips.c b/contrib/plugins/ips.c
new file mode 100644
index 000..cf3159df391
--- /dev/null
+++ b/contrib/plugins/ips.c
@@ -0,0 +1,239 @@
+/*
+ * ips rate limiting plugin.
+ *
+ * This plugin can be used to restrict the execution of a system to a
+ * particular number of Instructions Per Second (ips). This controls
+ * time as seen by the guest so while wall-clock time may be longer
+ * from the guests point of view time will pass at the normal rate.
+ *
+ * This uses the new plugin API which allows the plugin to control
+ * system time.
+ *
+ * Copyright (c) 2023 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include 
+#include 
+#include 
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
+
+/* how many times do we update time per sec */
+#define NUM_TIME_UPDATE_PER_SEC 10
+#define NSEC_IN_ONE_SEC (1000 * 1000 * 1000)
+
+static GMutex global_state_lock;
+
+static uint64_t insn_per_second = 1000 * 1000; /* ips per core, per second */
+static uint64_t insn_quantum; /* trap every N instructions */
+static bool precise_execution; /* count every instruction */
+static int64_t start_time_ns; /* time (ns since epoch) first vCPU started */
+static int64_t virtual_time_ns; /* last set virtual time */
+
+static const void *time_handle;
+
+typedef enum {
+UNKNOWN = 0,
+EXECUTING,
+IDLE,
+FINISHED
+} vCPUState;
+
+typedef struct {
+uint64_t counter;
+uint64_t track_insn;
+vCPUState state;
+/* timestamp when vCPU entered state */
+int64_t last_state_time;
+} vCPUTime;
+
+struct qemu_plugin_scoreboard *vcpus;
+
+/* return epoch time in ns */
+static int64_t now_ns(void)
+{
+return g_get_real_time() * 1000;
+}
+
+static uint64_t num_insn_during(int64_t elapsed_ns)
+{
+double num_secs = elapsed_ns / (double) NSEC_IN_ONE_SEC;
+return num_secs * (double) insn_per_second;
+}
+
+static int64_t time_for_insn(uint64_t num_insn)
+{
+double num_secs = (double) num_insn / (double) insn_per_second;
+return num_secs * (double) NSEC_IN_ONE_SEC;
+}
+
+static int64_t uptime_ns(void)
+{
+int64_t now = now_ns();
+g_assert(now >= start_time_ns);
+return now - start_time_ns;
+}
+
+static void vcpu_set_state(vCPUTime *vcpu, vCPUState new_state)
+{
+vcpu->last_state_time = now_ns();
+vcpu->state = new_state;
+}
+
+static void update_system_time(vCPUTime *vcpu)
+{
+/* flush remaining instructions */
+vcpu->counter += vcpu->track_insn;
+vcpu->track_insn = 0;
+
+int64_t uptime = uptime_ns();
+uint64_t expected_insn = num_insn_during(uptime);
+
+if (vcpu->counter >= expected_insn) {
+/* this vcpu ran faster than expected, so it has to sleep */
+uint64_t insn_advance = vcpu->counter - expected_insn;
+uint64_t time_advance_ns = time_for_insn(insn_advance);
+int64_t sleep_us = time_advance_ns / 1000;
+g_usleep(sleep_us);
+}
+
+/* based on number of instructions, what should be the new time? */
+int64_t new_virtual_time = time_for_insn(vcpu->counter);
+
+g_mutex_lock(_state_lock);
+
+/* Time only moves forward. Another vcpu might have updated it already. */
+if (new_virtual_time > virtual_time_ns) {
+qemu_plugin_update_ns(time_handle, new_virtual_time);
+virtual_time_ns = new_virtual_time;
+}
+
+g_mutex_unlock(_state_lock);
+}
+
+static void set_start_time()
+{
+g_mutex_lock(_state_lock);
+if (!start_time_ns) {
+start_time_ns = now_ns();
+}
+g_mutex_unlock(_state_lock);
+}
+
+static void vcpu_init(qemu_plugin_id_t id, 

Re: [PATCH 5/5] contrib/plugins: add ips plugin example for cost modeling

2024-05-28 Thread Alex Bennée
Pierrick Bouvier  writes:

> On 5/28/24 12:14, Alex Bennée wrote:
>> Pierrick Bouvier  writes:
>> 
>>> This plugin uses the new time control interface to make decisions
>>> about the state of time during the emulation. The algorithm is
>>> currently very simple. The user specifies an ips rate which applies
>>> per core. If the core runs ahead of its allocated execution time the
>>> plugin sleeps for a bit to let real time catch up. Either way time is
>>> updated for the emulation as a function of total executed instructions
>>> with some adjustments for cores that idle.
>>>
>>> Examples
>>> 
>>>
>>> Slow down execution of /bin/true:
>>> $ num_insn=$(./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d 
>>> plugin /bin/true |& grep total | sed -e 's/.*: //')
>>> $ time ./build/qemu-x86_64 -plugin 
>>> ./build/contrib/plugins/libips.so,ips=$(($num_insn/4)) /bin/true
>>> real 4.000s
>>>
>>> Boot a Linux kernel simulating a 250MHz cpu:
>>> $ /build/qemu-system-x86_64 -kernel /boot/vmlinuz-6.1.0-21-amd64 -append 
>>> "console=ttyS0" -plugin 
>>> ./build/contrib/plugins/libips.so,ips=$((250*1000*1000)) -smp 1 -m 512
>>> check time until kernel panic on serial0
>>>
>>> Signed-off-by: Pierrick Bouvier 
>>> ---
>>>   contrib/plugins/ips.c| 239 +++
>>>   contrib/plugins/Makefile |   1 +
>>>   2 files changed, 240 insertions(+)
>>>   create mode 100644 contrib/plugins/ips.c
>>>
>>> diff --git a/contrib/plugins/ips.c b/contrib/plugins/ips.c
>>> new file mode 100644
>>> index 000..cf3159df391
>>> --- /dev/null
>>> +++ b/contrib/plugins/ips.c
>>> @@ -0,0 +1,239 @@
>>> +/*
>>> + * ips rate limiting plugin.
>>> + *
>>> + * This plugin can be used to restrict the execution of a system to a
>>> + * particular number of Instructions Per Second (ips). This controls
>>> + * time as seen by the guest so while wall-clock time may be longer
>>> + * from the guests point of view time will pass at the normal rate.
>>> + *
>>> + * This uses the new plugin API which allows the plugin to control
>>> + * system time.
>>> + *
>>> + * Copyright (c) 2023 Linaro Ltd
>>> + *
>>> + * SPDX-License-Identifier: GPL-2.0-or-later
>>> + */
>>> +
>>> +#include 
>>> +#include 
>>> +#include 
>>> +
>>> +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
>>> +
>>> +/* how many times do we update time per sec */
>>> +#define NUM_TIME_UPDATE_PER_SEC 10
>>> +#define NSEC_IN_ONE_SEC (1000 * 1000 * 1000)
>>> +
>>> +static GMutex global_state_lock;
>>> +
>>> +static uint64_t insn_per_second = 1000 * 1000; /* ips per core, per second 
>>> */
>>> +static uint64_t insn_quantum; /* trap every N instructions */
>>> +static bool precise_execution; /* count every instruction */
>>> +static int64_t start_time_ns; /* time (ns since epoch) first vCPU started 
>>> */
>>> +static int64_t virtual_time_ns; /* last set virtual time */
>>> +
>>> +static const void *time_handle;
>>> +
>>> +typedef enum {
>>> +UNKNOWN = 0,
>>> +EXECUTING,
>>> +IDLE,
>>> +FINISHED
>>> +} vCPUState;
>>> +
>>> +typedef struct {
>>> +uint64_t counter;
>>> +uint64_t track_insn;
>>> +vCPUState state;
>>> +/* timestamp when vCPU entered state */
>>> +int64_t last_state_time;
>>> +} vCPUTime;
>>> +
>>> +struct qemu_plugin_scoreboard *vcpus;
>>> +
>>> +/* return epoch time in ns */
>>> +static int64_t now_ns(void)
>>> +{
>>> +return g_get_real_time() * 1000;
>>> +}
>>> +
>>> +static uint64_t num_insn_during(int64_t elapsed_ns)
>>> +{
>>> +double num_secs = elapsed_ns / (double) NSEC_IN_ONE_SEC;
>>> +return num_secs * (double) insn_per_second;
>>> +}
>>> +
>>> +static int64_t time_for_insn(uint64_t num_insn)
>>> +{
>>> +double num_secs = (double) num_insn / (double) insn_per_second;
>>> +return num_secs * (double) NSEC_IN_ONE_SEC;
>>> +}
>>> +
>>> +static int64_t uptime_ns(void)
>>> +{
>>> +int64_t now = now_ns();
>>> +g_assert(now >= start_time_ns);
>>> +return now - start_time_ns;
>>> +}
>>> +
>>> +static void vcpu_set_state(vCPUTime *vcpu, vCPUState new_state)
>>> +{
>>> +vcpu->last_state_time = now_ns();
>>> +vcpu->state = new_state;
>>> +}
>>> +
>>> +static void update_system_time(vCPUTime *vcpu)
>>> +{
>>> +/* flush remaining instructions */
>>> +vcpu->counter += vcpu->track_insn;
>>> +vcpu->track_insn = 0;
>>> +
>>> +int64_t uptime = uptime_ns();
>>> +uint64_t expected_insn = num_insn_during(uptime);
>>> +
>>> +if (vcpu->counter >= expected_insn) {
>>> +/* this vcpu ran faster than expected, so it has to sleep */
>>> +uint64_t insn_advance = vcpu->counter - expected_insn;
>>> +uint64_t time_advance_ns = time_for_insn(insn_advance);
>>> +int64_t sleep_us = time_advance_ns / 1000;
>>> +g_usleep(sleep_us);
>>> +}
>>> +
>>> +/* based on number of instructions, what should be the new time? */
>>> +int64_t new_virtual_time = time_for_insn(vcpu->counter);
>>> +
>>> +

Re: [PATCH 5/5] contrib/plugins: add ips plugin example for cost modeling

2024-05-28 Thread Pierrick Bouvier

On 5/28/24 12:14, Alex Bennée wrote:

Pierrick Bouvier  writes:


This plugin uses the new time control interface to make decisions
about the state of time during the emulation. The algorithm is
currently very simple. The user specifies an ips rate which applies
per core. If the core runs ahead of its allocated execution time the
plugin sleeps for a bit to let real time catch up. Either way time is
updated for the emulation as a function of total executed instructions
with some adjustments for cores that idle.

Examples


Slow down execution of /bin/true:
$ num_insn=$(./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d plugin 
/bin/true |& grep total | sed -e 's/.*: //')
$ time ./build/qemu-x86_64 -plugin 
./build/contrib/plugins/libips.so,ips=$(($num_insn/4)) /bin/true
real 4.000s

Boot a Linux kernel simulating a 250MHz cpu:
$ /build/qemu-system-x86_64 -kernel /boot/vmlinuz-6.1.0-21-amd64 -append 
"console=ttyS0" -plugin 
./build/contrib/plugins/libips.so,ips=$((250*1000*1000)) -smp 1 -m 512
check time until kernel panic on serial0

Signed-off-by: Pierrick Bouvier 
---
  contrib/plugins/ips.c| 239 +++
  contrib/plugins/Makefile |   1 +
  2 files changed, 240 insertions(+)
  create mode 100644 contrib/plugins/ips.c

diff --git a/contrib/plugins/ips.c b/contrib/plugins/ips.c
new file mode 100644
index 000..cf3159df391
--- /dev/null
+++ b/contrib/plugins/ips.c
@@ -0,0 +1,239 @@
+/*
+ * ips rate limiting plugin.
+ *
+ * This plugin can be used to restrict the execution of a system to a
+ * particular number of Instructions Per Second (ips). This controls
+ * time as seen by the guest so while wall-clock time may be longer
+ * from the guests point of view time will pass at the normal rate.
+ *
+ * This uses the new plugin API which allows the plugin to control
+ * system time.
+ *
+ * Copyright (c) 2023 Linaro Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include 
+#include 
+#include 
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
+
+/* how many times do we update time per sec */
+#define NUM_TIME_UPDATE_PER_SEC 10
+#define NSEC_IN_ONE_SEC (1000 * 1000 * 1000)
+
+static GMutex global_state_lock;
+
+static uint64_t insn_per_second = 1000 * 1000; /* ips per core, per second */
+static uint64_t insn_quantum; /* trap every N instructions */
+static bool precise_execution; /* count every instruction */
+static int64_t start_time_ns; /* time (ns since epoch) first vCPU started */
+static int64_t virtual_time_ns; /* last set virtual time */
+
+static const void *time_handle;
+
+typedef enum {
+UNKNOWN = 0,
+EXECUTING,
+IDLE,
+FINISHED
+} vCPUState;
+
+typedef struct {
+uint64_t counter;
+uint64_t track_insn;
+vCPUState state;
+/* timestamp when vCPU entered state */
+int64_t last_state_time;
+} vCPUTime;
+
+struct qemu_plugin_scoreboard *vcpus;
+
+/* return epoch time in ns */
+static int64_t now_ns(void)
+{
+return g_get_real_time() * 1000;
+}
+
+static uint64_t num_insn_during(int64_t elapsed_ns)
+{
+double num_secs = elapsed_ns / (double) NSEC_IN_ONE_SEC;
+return num_secs * (double) insn_per_second;
+}
+
+static int64_t time_for_insn(uint64_t num_insn)
+{
+double num_secs = (double) num_insn / (double) insn_per_second;
+return num_secs * (double) NSEC_IN_ONE_SEC;
+}
+
+static int64_t uptime_ns(void)
+{
+int64_t now = now_ns();
+g_assert(now >= start_time_ns);
+return now - start_time_ns;
+}
+
+static void vcpu_set_state(vCPUTime *vcpu, vCPUState new_state)
+{
+vcpu->last_state_time = now_ns();
+vcpu->state = new_state;
+}
+
+static void update_system_time(vCPUTime *vcpu)
+{
+/* flush remaining instructions */
+vcpu->counter += vcpu->track_insn;
+vcpu->track_insn = 0;
+
+int64_t uptime = uptime_ns();
+uint64_t expected_insn = num_insn_during(uptime);
+
+if (vcpu->counter >= expected_insn) {
+/* this vcpu ran faster than expected, so it has to sleep */
+uint64_t insn_advance = vcpu->counter - expected_insn;
+uint64_t time_advance_ns = time_for_insn(insn_advance);
+int64_t sleep_us = time_advance_ns / 1000;
+g_usleep(sleep_us);
+}
+
+/* based on number of instructions, what should be the new time? */
+int64_t new_virtual_time = time_for_insn(vcpu->counter);
+
+g_mutex_lock(_state_lock);
+
+/* Time only moves forward. Another vcpu might have updated it already. */
+if (new_virtual_time > virtual_time_ns) {
+qemu_plugin_update_ns(time_handle, new_virtual_time);
+virtual_time_ns = new_virtual_time;
+}
+
+g_mutex_unlock(_state_lock);
+}
+
+static void set_start_time()
+{
+g_mutex_lock(_state_lock);
+if (!start_time_ns) {
+start_time_ns = now_ns();
+}
+g_mutex_unlock(_state_lock);
+}
+
+static void vcpu_init(qemu_plugin_id_t id, unsigned int cpu_index)
+{
+vCPUTime *vcpu = 

Re: [PATCH 5/5] contrib/plugins: add ips plugin example for cost modeling

2024-05-28 Thread Alex Bennée
Pierrick Bouvier  writes:

> This plugin uses the new time control interface to make decisions
> about the state of time during the emulation. The algorithm is
> currently very simple. The user specifies an ips rate which applies
> per core. If the core runs ahead of its allocated execution time the
> plugin sleeps for a bit to let real time catch up. Either way time is
> updated for the emulation as a function of total executed instructions
> with some adjustments for cores that idle.
>
> Examples
> 
>
> Slow down execution of /bin/true:
> $ num_insn=$(./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d 
> plugin /bin/true |& grep total | sed -e 's/.*: //')
> $ time ./build/qemu-x86_64 -plugin 
> ./build/contrib/plugins/libips.so,ips=$(($num_insn/4)) /bin/true
> real 4.000s
>
> Boot a Linux kernel simulating a 250MHz cpu:
> $ /build/qemu-system-x86_64 -kernel /boot/vmlinuz-6.1.0-21-amd64 -append 
> "console=ttyS0" -plugin 
> ./build/contrib/plugins/libips.so,ips=$((250*1000*1000)) -smp 1 -m 512
> check time until kernel panic on serial0
>
> Signed-off-by: Pierrick Bouvier 
> ---
>  contrib/plugins/ips.c| 239 +++
>  contrib/plugins/Makefile |   1 +
>  2 files changed, 240 insertions(+)
>  create mode 100644 contrib/plugins/ips.c
>
> diff --git a/contrib/plugins/ips.c b/contrib/plugins/ips.c
> new file mode 100644
> index 000..cf3159df391
> --- /dev/null
> +++ b/contrib/plugins/ips.c
> @@ -0,0 +1,239 @@
> +/*
> + * ips rate limiting plugin.
> + *
> + * This plugin can be used to restrict the execution of a system to a
> + * particular number of Instructions Per Second (ips). This controls
> + * time as seen by the guest so while wall-clock time may be longer
> + * from the guests point of view time will pass at the normal rate.
> + *
> + * This uses the new plugin API which allows the plugin to control
> + * system time.
> + *
> + * Copyright (c) 2023 Linaro Ltd
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include 
> +#include 
> +#include 
> +
> +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
> +
> +/* how many times do we update time per sec */
> +#define NUM_TIME_UPDATE_PER_SEC 10
> +#define NSEC_IN_ONE_SEC (1000 * 1000 * 1000)
> +
> +static GMutex global_state_lock;
> +
> +static uint64_t insn_per_second = 1000 * 1000; /* ips per core, per second */
> +static uint64_t insn_quantum; /* trap every N instructions */
> +static bool precise_execution; /* count every instruction */
> +static int64_t start_time_ns; /* time (ns since epoch) first vCPU started */
> +static int64_t virtual_time_ns; /* last set virtual time */
> +
> +static const void *time_handle;
> +
> +typedef enum {
> +UNKNOWN = 0,
> +EXECUTING,
> +IDLE,
> +FINISHED
> +} vCPUState;
> +
> +typedef struct {
> +uint64_t counter;
> +uint64_t track_insn;
> +vCPUState state;
> +/* timestamp when vCPU entered state */
> +int64_t last_state_time;
> +} vCPUTime;
> +
> +struct qemu_plugin_scoreboard *vcpus;
> +
> +/* return epoch time in ns */
> +static int64_t now_ns(void)
> +{
> +return g_get_real_time() * 1000;
> +}
> +
> +static uint64_t num_insn_during(int64_t elapsed_ns)
> +{
> +double num_secs = elapsed_ns / (double) NSEC_IN_ONE_SEC;
> +return num_secs * (double) insn_per_second;
> +}
> +
> +static int64_t time_for_insn(uint64_t num_insn)
> +{
> +double num_secs = (double) num_insn / (double) insn_per_second;
> +return num_secs * (double) NSEC_IN_ONE_SEC;
> +}
> +
> +static int64_t uptime_ns(void)
> +{
> +int64_t now = now_ns();
> +g_assert(now >= start_time_ns);
> +return now - start_time_ns;
> +}
> +
> +static void vcpu_set_state(vCPUTime *vcpu, vCPUState new_state)
> +{
> +vcpu->last_state_time = now_ns();
> +vcpu->state = new_state;
> +}
> +
> +static void update_system_time(vCPUTime *vcpu)
> +{
> +/* flush remaining instructions */
> +vcpu->counter += vcpu->track_insn;
> +vcpu->track_insn = 0;
> +
> +int64_t uptime = uptime_ns();
> +uint64_t expected_insn = num_insn_during(uptime);
> +
> +if (vcpu->counter >= expected_insn) {
> +/* this vcpu ran faster than expected, so it has to sleep */
> +uint64_t insn_advance = vcpu->counter - expected_insn;
> +uint64_t time_advance_ns = time_for_insn(insn_advance);
> +int64_t sleep_us = time_advance_ns / 1000;
> +g_usleep(sleep_us);
> +}
> +
> +/* based on number of instructions, what should be the new time? */
> +int64_t new_virtual_time = time_for_insn(vcpu->counter);
> +
> +g_mutex_lock(_state_lock);
> +
> +/* Time only moves forward. Another vcpu might have updated it already. 
> */
> +if (new_virtual_time > virtual_time_ns) {
> +qemu_plugin_update_ns(time_handle, new_virtual_time);
> +virtual_time_ns = new_virtual_time;
> +}
> +
> +g_mutex_unlock(_state_lock);
> +}
> +
> +static void set_start_time()
> +{