Block layer patches

- Managing inactive nodes (enables QSD migration with shared storage)
 - Fix swapped values for BLOCK_IO_ERROR 'device' and 'qom-path'
 - vpc: Read images exported from Azure correctly
 - scripts/qemu-gdb: Support coroutine dumps in coredumps
 - Minor cleanups
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEE3D3rFZqa+V09dFb+fwmycsiPL9YFAmek34IRHGt3b2xmQHJl
 ZGhhdC5jb20ACgkQfwmycsiPL9bDpxAAnTvwmdazAXG0g9GzqvrEB/+6rStjAsqE
 9MTWV4WxyN41d0RXxN8CYKb8CXSiTRyw6r3CSGNYEI2eShe9e934PriSkZm41HyX
 n9Yh5YxqGZqitzvPtx62Ii/1KG+PcjQbfHuK1p4+rlKa0yQ2eGlio1JIIrZrCkBZ
 ikZcQUrhIyD0XV8hTQ2+Ysa+ZN6itjnlTQIG3gS3m8f8WR7kyUXD8YFMQFJFyjVx
 NrAIpLnc/ln9+5PZR9tje8U7XEn2KCgI5pgGaQnrd0h0G1H4ig8ogzYYnKTLhjU/
 AmQpS8np8Tyg6S1UZTiekEq0VuAhThEQc5b3sGbmHWH/R2ABMStyf18oCBAkPzZ7
 s6h+3XzTKKY2Q5Q3ZG/ANkUJjTNBhdj1fcaARvbSWsqsuk5CWX/I3jzvgihFtCSs
 eGu+b/bLeW6P7hu4qPHBcgLHuB1Fc7Rd2t4BoIGM1wcO2CeC9DzUKOiIMZOEJIh0
 GGqCkEWDHgckDTakD4/vSqm0UDKt6FSlQC9ga/ILBY3IB5HpHoArY58selymy28i
 X7MgAvbjdsmNuUuXDZZOiObcFt3j8jlmwPJpPyzXPQIiPX1RXeBPRhVAEeZCKn6Z
 tfHr72SJdMeVOGXVTvOrJ2iW+4g03rPdmkDFCUhpOwo62RODq7ahvCIXsNf3nEFR
 rSB3T1M/8EM=
 =iQLP
 -----END PGP SIGNATURE-----

Merge tag 'for-upstream' of https://repo.or.cz/qemu/kevin into staging

Block layer patches

- Managing inactive nodes (enables QSD migration with shared storage)
- Fix swapped values for BLOCK_IO_ERROR 'device' and 'qom-path'
- vpc: Read images exported from Azure correctly
- scripts/qemu-gdb: Support coroutine dumps in coredumps
- Minor cleanups

# -----BEGIN PGP SIGNATURE-----
#
# iQJFBAABCAAvFiEE3D3rFZqa+V09dFb+fwmycsiPL9YFAmek34IRHGt3b2xmQHJl
# ZGhhdC5jb20ACgkQfwmycsiPL9bDpxAAnTvwmdazAXG0g9GzqvrEB/+6rStjAsqE
# 9MTWV4WxyN41d0RXxN8CYKb8CXSiTRyw6r3CSGNYEI2eShe9e934PriSkZm41HyX
# n9Yh5YxqGZqitzvPtx62Ii/1KG+PcjQbfHuK1p4+rlKa0yQ2eGlio1JIIrZrCkBZ
# ikZcQUrhIyD0XV8hTQ2+Ysa+ZN6itjnlTQIG3gS3m8f8WR7kyUXD8YFMQFJFyjVx
# NrAIpLnc/ln9+5PZR9tje8U7XEn2KCgI5pgGaQnrd0h0G1H4ig8ogzYYnKTLhjU/
# AmQpS8np8Tyg6S1UZTiekEq0VuAhThEQc5b3sGbmHWH/R2ABMStyf18oCBAkPzZ7
# s6h+3XzTKKY2Q5Q3ZG/ANkUJjTNBhdj1fcaARvbSWsqsuk5CWX/I3jzvgihFtCSs
# eGu+b/bLeW6P7hu4qPHBcgLHuB1Fc7Rd2t4BoIGM1wcO2CeC9DzUKOiIMZOEJIh0
# GGqCkEWDHgckDTakD4/vSqm0UDKt6FSlQC9ga/ILBY3IB5HpHoArY58selymy28i
# X7MgAvbjdsmNuUuXDZZOiObcFt3j8jlmwPJpPyzXPQIiPX1RXeBPRhVAEeZCKn6Z
# tfHr72SJdMeVOGXVTvOrJ2iW+4g03rPdmkDFCUhpOwo62RODq7ahvCIXsNf3nEFR
# rSB3T1M/8EM=
# =iQLP
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 06 Feb 2025 11:12:50 EST
# gpg:                using RSA key DC3DEB159A9AF95D3D7456FE7F09B272C88F2FD6
# gpg:                issuer "kwolf@redhat.com"
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full]
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74  56FE 7F09 B272 C88F 2FD6

* tag 'for-upstream' of https://repo.or.cz/qemu/kevin: (25 commits)
  block: remove unused BLOCK_OP_TYPE_DATAPLANE
  iotests: Add (NBD-based) tests for inactive nodes
  iotests: Add qsd-migrate case
  iotests: Add filter_qtest()
  nbd/server: Support inactive nodes
  block/export: Add option to allow export of inactive nodes
  block: Drain nodes before inactivating them
  block/export: Don't ignore image activation error in blk_exp_add()
  block: Support inactive nodes in blk_insert_bs()
  block: Add blockdev-set-active QMP command
  block: Add option to create inactive nodes
  block: Fix crash on block_resize on inactive node
  block: Don't attach inactive child to active node
  migration/block-active: Remove global active flag
  block: Inactivate external snapshot overlays when necessary
  block: Allow inactivating already inactive nodes
  block: Add 'active' field to BlockDeviceInfo
  block-backend: Fix argument order when calling 'qapi_event_send_block_io_error()'
  scripts/qemu-gdb: Support coroutine dumps in coredumps
  scripts/qemu-gdb: Simplify fs_base fetching for coroutines
  ...

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2025-02-10 13:25:36 -05:00
commit f2ec48fefd
35 changed files with 1133 additions and 166 deletions

View file

@ -45,3 +45,5 @@ coroutine.CoroutineBt()
# Default to silently passing through SIGUSR1, because QEMU sends it
# to itself a lot.
gdb.execute('handle SIGUSR1 pass noprint nostop')
# Always print full stack for python errors, easier to debug and report issues
gdb.execute('set python print-stack full')

View file

@ -13,28 +13,9 @@ import gdb
VOID_PTR = gdb.lookup_type('void').pointer()
def get_fs_base():
'''Fetch %fs base value using arch_prctl(ARCH_GET_FS). This is
pthread_self().'''
# %rsp - 120 is scratch space according to the SystemV ABI
old = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)')
gdb.execute('call (int)arch_prctl(0x1003, $rsp - 120)', False, True)
fs_base = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)')
gdb.execute('set *(uint64_t*)($rsp - 120) = %s' % old, False, True)
return fs_base
def pthread_self():
'''Fetch pthread_self() from the glibc start_thread function.'''
f = gdb.newest_frame()
while f.name() != 'start_thread':
f = f.older()
if f is None:
return get_fs_base()
try:
return f.read_var("arg")
except ValueError:
return get_fs_base()
'''Fetch the base address of TLS.'''
return gdb.parse_and_eval("$fs_base")
def get_glibc_pointer_guard():
'''Fetch glibc pointer guard value'''
@ -65,9 +46,60 @@ def get_jmpbuf_regs(jmpbuf):
'r15': jmpbuf[JB_R15],
'rip': glibc_ptr_demangle(jmpbuf[JB_PC], pointer_guard) }
def bt_jmpbuf(jmpbuf):
'''Backtrace a jmpbuf'''
regs = get_jmpbuf_regs(jmpbuf)
def symbol_lookup(addr):
# Example: "__clone3 + 44 in section .text of /lib64/libc.so.6"
result = gdb.execute(f"info symbol {hex(addr)}", to_string=True).strip()
try:
if "+" in result:
(func, result) = result.split(" + ")
(offset, result) = result.split(" in ")
else:
offset = "0"
(func, result) = result.split(" in ")
func_str = f"{func}<+{offset}> ()"
except:
return f"??? ({result})"
# Example: Line 321 of "../util/coroutine-ucontext.c" starts at address
# 0x55cf3894d993 <qemu_coroutine_switch+99> and ends at 0x55cf3894d9ab
# <qemu_coroutine_switch+123>.
result = gdb.execute(f"info line *{hex(addr)}", to_string=True).strip()
if not result.startswith("Line "):
return func_str
result = result[5:]
try:
result = result.split(" starts ")[0]
(line, path) = result.split(" of ")
path = path.replace("\"", "")
except:
return func_str
return f"{func_str} at {path}:{line}"
def dump_backtrace(regs):
'''
Backtrace dump with raw registers, mimic GDB command 'bt'.
'''
# Here only rbp and rip that matter..
rbp = regs['rbp']
rip = regs['rip']
i = 0
while rbp:
# For all return addresses on stack, we want to look up symbol/line
# on the CALL command, because the return address is the next
# instruction instead of the CALL. Here -1 would work for any
# sized CALL instruction.
print(f"#{i} {hex(rip)} in {symbol_lookup(rip if i == 0 else rip-1)}")
rip = gdb.parse_and_eval(f"*(uint64_t *)(uint64_t)({hex(rbp)} + 8)")
rbp = gdb.parse_and_eval(f"*(uint64_t *)(uint64_t)({hex(rbp)})")
i += 1
def dump_backtrace_live(regs):
'''
Backtrace dump with gdb's 'bt' command, only usable in a live session.
'''
old = dict()
# remember current stack frame and select the topmost
@ -88,6 +120,17 @@ def bt_jmpbuf(jmpbuf):
selected_frame.select()
def bt_jmpbuf(jmpbuf):
'''Backtrace a jmpbuf'''
regs = get_jmpbuf_regs(jmpbuf)
try:
# This reuses gdb's "bt" command, which can be slightly prettier
# but only works with live sessions.
dump_backtrace_live(regs)
except:
# If above doesn't work, fallback to poor man's unwind
dump_backtrace(regs)
def co_cast(co):
return co.cast(gdb.lookup_type('CoroutineUContext').pointer())
@ -120,10 +163,15 @@ class CoroutineBt(gdb.Command):
gdb.execute("bt")
if gdb.parse_and_eval("qemu_in_coroutine()") == False:
return
try:
# This only works with a live session
co_ptr = gdb.parse_and_eval("qemu_coroutine_self()")
except:
# Fallback to use hard-coded ucontext vars if it's coredump
co_ptr = gdb.parse_and_eval("co_tls_current")
co_ptr = gdb.parse_and_eval("qemu_coroutine_self()")
if co_ptr == False:
return
while True:
co = co_cast(co_ptr)