Am 08.05.23 um 12:40 schrieb Kevin Wolf: > Am 05.05.2023 um 11:35 hat Fiona Ebner geschrieben: >> Hi, >> I noticed that the bdrv_graph_co_rd_lock() and bdrv_graph_co_rd_unlock() >> functions use qemu_in_main_thread() as a conditional to return early. >> What high-level requirements ensure that qemu_in_main_thread() will >> evaluate to the same value during locking and unlocking? > > I think it's actually wrong. > > There are some conditions under which we don't actually need to do > anything for locking: Writing the graph is only possible from the main > context while holding the BQL. So if a reader is running in the main > contextunder the BQL and knows that it won't be interrupted until the > next writer runs, we don't actually need to do anything. > > This is the case if the reader code neither has a nested event loop > (this is forbidden anyway while you hold the lock) nor is a coroutine > (because a writer could run when the coroutine has yielded).
Sorry, I don't understand the first part. If bdrv_writev_vmstate() is called, then, because it is a generated coroutine wrapper, AIO_WAIT_WHILE()/aio_poll() is used. And that is the case regardless of whether the lock is held or not, i.e. there is a nested event loop even if the lock is held? > These conditions are exactly what bdrv_graph_rdlock_main_loop() asserts. > They are not fulfilled in bdrv_graph_co_rdlock, which always runs in a > coroutine. > Thank you for the explanation! >> In the output below, the boolean value after the backtraces of >> bdrv_graph_co_rd(un)lock is the value of qemu_in_main_thread(). >> >> AFAICT, what happened below is: >> 1. QMP function is executed in the main thread and drops the BQL. >> 2. bdrv_co_writev_vmstate_entry is called, increasing the reader count, >> because qemu_in_main_thread() is false. >> 3. A vCPU thread issued a write, not increasing the reader count, >> because qemu_in_main_thread() is true. >> 4. The write is finished in the main thread, decreasing the reader >> count, because qemu_in_main_thread() is false. > > This one is weird. Why don't we hold the BQL there? > > Ah, I actually have an idea: Maybe it is executed by the nested event > loop in bdrv_writev_vmstate(). This nested event loop runs now without > the BQL, too, and it can call any sort of callback in QEMU that really > expects to be called with the BQL. Scary! > > If this is what happens, you actually have your proof that not holding > the BQL can cause problems even if there is no other thread active that > could interfere. > > Can you try to confirm this in gdb? In case you haven't debugged > coroutines much yet: If you have this state in gdb for a running > instance, you can repeat 'fin' until you reach the next coroutine > switch, and then you should be able to get the stack trace to see who > entered the coroutine. > Thank you for the guidance! Hope I did it correctly, but AFAICS, you are spot-on :) > Run till exit from #0 blk_co_pwrite_entry (opaque=0x7ffff1242eb0) at > block/block-gen.c:1624 > coroutine_trampoline (i0=1543776144, i1=32767) at > ../util/coroutine-ucontext.c:178 > 178 qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); > (gdb) s > [New Thread 0x7fff537f7700 (LWP 128641)] > [Thread 0x7fff537f7700 (LWP 128641) exited] > qemu_coroutine_switch (from_=0x7fff5c042790, to_=0x7ffff3901148, > action=COROUTINE_TERMINATE) at ../util/coroutine-ucontext.c:298 > 298 { > (gdb) n > 299 CoroutineUContext *from = DO_UPCAST(CoroutineUContext, base, from_); > (gdb) > 300 CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, to_); > (gdb) > 302 void *fake_stack_save = NULL; > (gdb) > [New Thread 0x7fff537f7700 (LWP 128660)] > 304 set_current(to_); > (gdb) > [Thread 0x7fff537f7700 (LWP 128660) exited] > 306 ret = sigsetjmp(from->env, 0); > (gdb) n > [New Thread 0x7fff537f7700 (LWP 128762)] > [Thread 0x7fff537f7700 (LWP 128762) exited] > 307 if (ret == 0) { > (gdb) > 308 start_switch_fiber_asan(action, &fake_stack_save, to->stack, > (gdb) > 310 start_switch_fiber_tsan(&fake_stack_save, > (gdb) > [New Thread 0x7fff537f7700 (LWP 128868)] > 312 siglongjmp(to->env, action); > (gdb) s > [Thread 0x7fff537f7700 (LWP 128868) exited] > longjmp_compat (env=0x7ffff39011b0, val=2) at > ../sysdeps/x86/nptl/pt-longjmp.c:62 > 62 ../sysdeps/x86/nptl/pt-longjmp.c: No such file or directory. > (gdb) > __libc_siglongjmp (env=0x7fff5c0c07e8, val=3) at ../setjmp/longjmp.c:30 > 30 ../setjmp/longjmp.c: No such file or directory. > (gdb) > _longjmp_unwind (env=0x7fff5c0c07e8, val=3) at ../sysdeps/nptl/jmp-unwind.c:30 > 30 ../sysdeps/nptl/jmp-unwind.c: No such file or directory. > (gdb) bt > #0 _longjmp_unwind (env=0x7fff5c0c07e8, val=3) at > ../sysdeps/nptl/jmp-unwind.c:30 > #1 0x00007ffff6154961 in __libc_siglongjmp (env=0x7fff5c0c07e8, val=3) at > ../setjmp/longjmp.c:30 > #2 0x00007ffff712cc59 in longjmp_compat (env=<optimized out>, val=<optimized > out>) at ../sysdeps/x86/nptl/pt-longjmp.c:62 > #3 0x000055555605409b in qemu_coroutine_switch (from_=0x7ffff3901148, > to_=0x7fff5c0c0780, action=COROUTINE_ENTER) at > ../util/coroutine-ucontext.c:312 > #4 0x0000555556052214 in qemu_aio_coroutine_enter (ctx=0x555556b40f10, > co=0x7fff5c0c0780) at ../util/qemu-coroutine.c:161 > #5 0x0000555556050ae3 in aio_co_enter (ctx=0x555556b40f10, > co=0x7fff5c0c0780) at ../util/async.c:680 > #6 0x0000555556050a26 in aio_co_wake (co=0x7fff5c0c0780) at > ../util/async.c:664 > #7 0x0000555556054ea9 in thread_pool_co_cb (opaque=0x7fff516dd880, ret=0) at > ../util/thread-pool.c:284 > #8 0x0000555556054acb in thread_pool_completion_bh (opaque=0x555556b39c00) > at ../util/thread-pool.c:199 > #9 0x000055555604fa34 in aio_bh_call (bh=0x555556dcca30) at > ../util/async.c:155 > #10 0x000055555604fb3f in aio_bh_poll (ctx=0x555556b40f10) at > ../util/async.c:184 > #11 0x0000555556033ce2 in aio_poll (ctx=0x555556b40f10, blocking=true) at > ../util/aio-posix.c:721 > #12 0x0000555555e8d055 in bdrv_poll_co (s=0x7fffffffd610) at > /home/febner/repos/qemu/block/block-gen.h:43 > #13 0x0000555555e8e606 in bdrv_writev_vmstate (bs=0x555557a6aed0, > qiov=0x7fffffffd690, pos=0) at block/block-gen.c:809 > #14 0x0000555555ed75e3 in bdrv_save_vmstate (bs=0x555557a6aed0, > buf=0x555556dc5000 "\020t\317VUU", pos=0, size=1000) at ../block/io.c:2786 > #15 0x0000555555b77528 in qmp_unlock_write_lock (errp=0x7fffffffd750) at > ../migration/savevm.c:3411 > #16 0x0000555555fde0b8 in qmp_marshal_unlock_write_lock (args=0x7fffe8007e60, > ret=0x7ffff2e94d98, errp=0x7ffff2e94d90) at > qapi/qapi-commands-migration.c:1468 > #17 0x00005555560263b1 in do_qmp_dispatch_bh (opaque=0x7ffff2e94e30) at > ../qapi/qmp-dispatch.c:128 > #18 0x000055555604fa34 in aio_bh_call (bh=0x7fffe8005e90) at > ../util/async.c:155 > #19 0x000055555604fb3f in aio_bh_poll (ctx=0x555556b40f10) at > ../util/async.c:184 > #20 0x0000555556033399 in aio_dispatch (ctx=0x555556b40f10) at > ../util/aio-posix.c:421 > #21 0x000055555604ff7a in aio_ctx_dispatch (source=0x555556b40f10, > callback=0x0, user_data=0x0) at ../util/async.c:326 > #22 0x00007ffff7544e6b in g_main_context_dispatch () from > /lib/x86_64-linux-gnu/libglib-2.0.so.0 > #23 0x00005555560516c0 in glib_pollfds_poll () at ../util/main-loop.c:290 > #24 0x000055555605173a in os_host_main_loop_wait (timeout=0) at > ../util/main-loop.c:313 > #25 0x000055555605183f in main_loop_wait (nonblocking=0) at > ../util/main-loop.c:592 > #26 0x0000555555b2c188 in qemu_main_loop () at ../softmmu/runstate.c:731 > #27 0x000055555586ea92 in qemu_default_main () at ../softmmu/main.c:37 > #28 0x000055555586eac8 in main (argc=60, argv=0x7fffffffdb28) at > ../softmmu/main.c:48 Best Regards, Fiona