* [PATCH 0/2] hw/nvme: add monitor commands for inspecting state
@ 2026-05-10 10:57 Mateusz Nowicki
2026-05-10 10:57 ` [PATCH 1/2] hw/nvme: add 'info nvme' HMP command Mateusz Nowicki
2026-05-10 10:57 ` [PATCH 2/2] hw/nvme: add 'info nvme-queues' " Mateusz Nowicki
0 siblings, 2 replies; 10+ messages in thread
From: Mateusz Nowicki @ 2026-05-10 10:57 UTC (permalink / raw)
To: qemu-devel; +Cc: qemu-block, Mateusz Nowicki
Add two HMP commands for inspecting emulated NVMe controllers from
the QEMU monitor without attaching gdb to the QEMU process:
- 'info nvme' - per-controller summary (PCI, identify
fields, CC/CSTS/AQA, queue counts)
- 'info nvme-queues' - per-queue listing of admin and I/O SQ/CQ
(size, head/tail, PRP1, doorbell offset,
phase tag)
Useful for verifying queue setup, doorbell rings, AERs held in the
admin SQ and similar driver/controller interaction details from a
running QEMU monitor.
Mateusz Nowicki (2):
hw/nvme: add 'info nvme' HMP command
hw/nvme: add 'info nvme-queues' HMP command
hmp-commands-info.hx | 28 +++++++++
hw/nvme/meson.build | 2 +-
hw/nvme/monitor.c | 139 +++++++++++++++++++++++++++++++++++++++++++
qapi/machine.json | 34 +++++++++++
4 files changed, 202 insertions(+), 1 deletion(-)
create mode 100644 hw/nvme/monitor.c
--
2.53.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH 1/2] hw/nvme: add 'info nvme' HMP command 2026-05-10 10:57 [PATCH 0/2] hw/nvme: add monitor commands for inspecting state Mateusz Nowicki @ 2026-05-10 10:57 ` Mateusz Nowicki 2026-05-12 0:03 ` Dr. David Alan Gilbert 2026-05-18 12:52 ` Klaus Jensen 2026-05-10 10:57 ` [PATCH 2/2] hw/nvme: add 'info nvme-queues' " Mateusz Nowicki 1 sibling, 2 replies; 10+ messages in thread From: Mateusz Nowicki @ 2026-05-10 10:57 UTC (permalink / raw) To: qemu-devel Cc: qemu-block, Mateusz Nowicki, Dr. David Alan Gilbert, Keith Busch, Klaus Jensen, Jesper Devantier, Eric Blake, Markus Armbruster, Philippe Mathieu-Daudé, Zhao Liu Add an 'info nvme' HMP command for inspecting emulated NVMe controllers from the QEMU monitor. For each NVMe controller in the QOM tree the command prints: - PCI BDF, vendor/device ID and BAR0 base address; - Identify Controller fields visible to the host driver: SN, MN, FR and CNTLID; - the CC, CSTS and AQA registers (raw 32-bit values); - the number of admin and active I/O submission/completion queues. Useful when debugging the Linux NVMe driver against the QEMU emulation without attaching gdb to the QEMU process. Example: (qemu) info nvme /machine/peripheral-anon/device[0] PCI: BDF 00:04.0 VID=8086 DID=5845 BAR0=0x00000000feb50000 ID: SN=NVME0001 MN=QEMU NVMe Ctrl FR=... CNTLID=0x0000 CC: 0x00460001 CSTS: 0x00000001 AQA: 0x001f001f Queues: 1 admin + 4 IO SQ / 4 IO CQ Signed-off-by: Mateusz Nowicki <mateusz.nowicki@posteo.net> --- hmp-commands-info.hx | 13 ++++++++ hw/nvme/meson.build | 2 +- hw/nvme/monitor.c | 76 ++++++++++++++++++++++++++++++++++++++++++++ qapi/machine.json | 17 ++++++++++ 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 hw/nvme/monitor.c diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 82134eb6c2..b984691c3c 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -340,6 +340,19 @@ SRST Show guest USB devices. ERST + { + .name = "nvme", + .args_type = "", + .params = "", + .help = "show emulated NVMe controllers", + .cmd_info_hrt = qmp_x_query_nvme, + }, + +SRST + ``info nvme`` + Show emulated NVMe controllers. +ERST + { .name = "usbhost", .args_type = "", diff --git a/hw/nvme/meson.build b/hw/nvme/meson.build index 7d5caa53c2..3017f058f9 100644 --- a/hw/nvme/meson.build +++ b/hw/nvme/meson.build @@ -1 +1 @@ -system_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('ctrl.c', 'dif.c', 'ns.c', 'subsys.c', 'nguid.c')) \ No newline at end of file +system_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('ctrl.c', 'dif.c', 'ns.c', 'subsys.c', 'nguid.c', 'monitor.c')) \ No newline at end of file diff --git a/hw/nvme/monitor.c b/hw/nvme/monitor.c new file mode 100644 index 0000000000..95a6754437 --- /dev/null +++ b/hw/nvme/monitor.c @@ -0,0 +1,76 @@ +/* + * QEMU NVMe Controller monitor commands + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" +#include "qapi/type-helpers.h" +#include "hw/pci/pci.h" + +#include "nvme.h" + +static int collect_one(Object *obj, void *opaque) +{ + GString *buf = opaque; + NvmeCtrl *n; + PCIDevice *pci; + pcibus_t bar0; + unsigned io_sq = 0, io_cq = 0; + + if (!object_dynamic_cast(obj, TYPE_NVME)) { + return 0; + } + n = NVME(obj); + pci = PCI_DEVICE(n); + bar0 = pci_get_bar_addr(pci, 0); + + for (unsigned i = 1; i <= n->params.max_ioqpairs; i++) { + if (n->sq && n->sq[i]) { + io_sq++; + } + if (n->cq && n->cq[i]) { + io_cq++; + } + } + + g_string_append_printf(buf, "%s\n", object_get_canonical_path(obj)); + g_string_append_printf(buf, + " PCI: BDF %02x:%02x.%x VID=%04x DID=%04x ", + pci_dev_bus_num(pci), PCI_SLOT(pci->devfn), PCI_FUNC(pci->devfn), + pci_get_word(pci->config + PCI_VENDOR_ID), + pci_get_word(pci->config + PCI_DEVICE_ID)); + if (bar0 == PCI_BAR_UNMAPPED) { + g_string_append(buf, "BAR0=unmapped\n"); + } else { + g_string_append_printf(buf, "BAR0=0x%016" PRIx64 "\n", + (uint64_t)bar0); + } + g_string_append_printf(buf, + " ID: SN=%.20s MN=%.40s FR=%.8s CNTLID=0x%04x\n", + n->id_ctrl.sn, n->id_ctrl.mn, n->id_ctrl.fr, n->cntlid); + g_string_append_printf(buf, " CC: 0x%08x\n", + ldl_le_p(&n->bar.cc)); + g_string_append_printf(buf, " CSTS: 0x%08x\n", + ldl_le_p(&n->bar.csts)); + g_string_append_printf(buf, " AQA: 0x%08x\n", + ldl_le_p(&n->bar.aqa)); + g_string_append_printf(buf, + " Queues: 1 admin + %u IO SQ / %u IO CQ\n", io_sq, io_cq); + return 0; +} + +HumanReadableText *qmp_x_query_nvme(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + + object_child_foreach_recursive(object_get_root(), collect_one, buf); + + if (buf->len == 0) { + g_string_append(buf, "no NVMe controllers\n"); + } + + return human_readable_text_from_str(buf); +} diff --git a/qapi/machine.json b/qapi/machine.json index 685e4e29b8..d4a589e768 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1772,6 +1772,23 @@ 'returns': 'HumanReadableText', 'features': [ 'unstable' ] } +## +# @x-query-nvme: +# +# Query state of emulated NVMe controllers. +# +# Features: +# +# @unstable: This command is meant for debugging. +# +# Returns: NVMe controller state as human-readable text +# +# Since: 11.1 +## +{ 'command': 'x-query-nvme', + 'returns': 'HumanReadableText', + 'features': [ 'unstable' ] } + ## # @SmbiosEntryPointType: # -- 2.53.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] hw/nvme: add 'info nvme' HMP command 2026-05-10 10:57 ` [PATCH 1/2] hw/nvme: add 'info nvme' HMP command Mateusz Nowicki @ 2026-05-12 0:03 ` Dr. David Alan Gilbert 2026-05-13 11:51 ` Markus Armbruster 2026-05-18 12:52 ` Klaus Jensen 1 sibling, 1 reply; 10+ messages in thread From: Dr. David Alan Gilbert @ 2026-05-12 0:03 UTC (permalink / raw) To: Mateusz Nowicki, Markus Armbruster Cc: qemu-devel, qemu-block, Keith Busch, Klaus Jensen, Jesper Devantier, Eric Blake, Philippe Mathieu-Daudé, Zhao Liu * Mateusz Nowicki (mateusz.nowicki@posteo.net) wrote: > Add an 'info nvme' HMP command for inspecting emulated NVMe > controllers from the QEMU monitor. > > For each NVMe controller in the QOM tree the command prints: > > - PCI BDF, vendor/device ID and BAR0 base address; > - Identify Controller fields visible to the host driver: SN, MN, FR > and CNTLID; > - the CC, CSTS and AQA registers (raw 32-bit values); > - the number of admin and active I/O submission/completion queues. > > Useful when debugging the Linux NVMe driver against the QEMU emulation > without attaching gdb to the QEMU process. > > Example: > > (qemu) info nvme > /machine/peripheral-anon/device[0] > PCI: BDF 00:04.0 VID=8086 DID=5845 BAR0=0x00000000feb50000 > ID: SN=NVME0001 MN=QEMU NVMe Ctrl FR=... CNTLID=0x0000 > CC: 0x00460001 > CSTS: 0x00000001 > AQA: 0x001f001f > Queues: 1 admin + 4 IO SQ / 4 IO CQ I won't claim to know what those NVMe magic numbers mean, but I'll trust you they're useful. > Signed-off-by: Mateusz Nowicki <mateusz.nowicki@posteo.net> > --- > hmp-commands-info.hx | 13 ++++++++ > hw/nvme/meson.build | 2 +- > hw/nvme/monitor.c | 76 ++++++++++++++++++++++++++++++++++++++++++++ > qapi/machine.json | 17 ++++++++++ So that lot looks OK from an HMP point of view, I wonder if qapi/machine.json is the right place for it. Markus? Acked-by: Dr. David Alan Gilbert <dave@treblig.org> > 4 files changed, 107 insertions(+), 1 deletion(-) > create mode 100644 hw/nvme/monitor.c > > diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx > index 82134eb6c2..b984691c3c 100644 > --- a/hmp-commands-info.hx > +++ b/hmp-commands-info.hx > @@ -340,6 +340,19 @@ SRST > Show guest USB devices. > ERST > > + { > + .name = "nvme", > + .args_type = "", > + .params = "", > + .help = "show emulated NVMe controllers", > + .cmd_info_hrt = qmp_x_query_nvme, > + }, > + > +SRST > + ``info nvme`` > + Show emulated NVMe controllers. > +ERST > + > { > .name = "usbhost", > .args_type = "", > diff --git a/hw/nvme/meson.build b/hw/nvme/meson.build > index 7d5caa53c2..3017f058f9 100644 > --- a/hw/nvme/meson.build > +++ b/hw/nvme/meson.build > @@ -1 +1 @@ > -system_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('ctrl.c', 'dif.c', 'ns.c', 'subsys.c', 'nguid.c')) > \ No newline at end of file > +system_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('ctrl.c', 'dif.c', 'ns.c', 'subsys.c', 'nguid.c', 'monitor.c')) > \ No newline at end of file > diff --git a/hw/nvme/monitor.c b/hw/nvme/monitor.c > new file mode 100644 > index 0000000000..95a6754437 > --- /dev/null > +++ b/hw/nvme/monitor.c > @@ -0,0 +1,76 @@ > +/* > + * QEMU NVMe Controller monitor commands > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + */ > + > +#include "qemu/osdep.h" > +#include "qapi/error.h" > +#include "qapi/qapi-commands-machine.h" > +#include "qapi/type-helpers.h" > +#include "hw/pci/pci.h" > + > +#include "nvme.h" > + > +static int collect_one(Object *obj, void *opaque) > +{ > + GString *buf = opaque; > + NvmeCtrl *n; > + PCIDevice *pci; > + pcibus_t bar0; > + unsigned io_sq = 0, io_cq = 0; > + > + if (!object_dynamic_cast(obj, TYPE_NVME)) { > + return 0; > + } > + n = NVME(obj); > + pci = PCI_DEVICE(n); > + bar0 = pci_get_bar_addr(pci, 0); > + > + for (unsigned i = 1; i <= n->params.max_ioqpairs; i++) { > + if (n->sq && n->sq[i]) { > + io_sq++; > + } > + if (n->cq && n->cq[i]) { > + io_cq++; > + } > + } > + > + g_string_append_printf(buf, "%s\n", object_get_canonical_path(obj)); > + g_string_append_printf(buf, > + " PCI: BDF %02x:%02x.%x VID=%04x DID=%04x ", > + pci_dev_bus_num(pci), PCI_SLOT(pci->devfn), PCI_FUNC(pci->devfn), > + pci_get_word(pci->config + PCI_VENDOR_ID), > + pci_get_word(pci->config + PCI_DEVICE_ID)); > + if (bar0 == PCI_BAR_UNMAPPED) { > + g_string_append(buf, "BAR0=unmapped\n"); > + } else { > + g_string_append_printf(buf, "BAR0=0x%016" PRIx64 "\n", > + (uint64_t)bar0); > + } > + g_string_append_printf(buf, > + " ID: SN=%.20s MN=%.40s FR=%.8s CNTLID=0x%04x\n", > + n->id_ctrl.sn, n->id_ctrl.mn, n->id_ctrl.fr, n->cntlid); > + g_string_append_printf(buf, " CC: 0x%08x\n", > + ldl_le_p(&n->bar.cc)); > + g_string_append_printf(buf, " CSTS: 0x%08x\n", > + ldl_le_p(&n->bar.csts)); > + g_string_append_printf(buf, " AQA: 0x%08x\n", > + ldl_le_p(&n->bar.aqa)); > + g_string_append_printf(buf, > + " Queues: 1 admin + %u IO SQ / %u IO CQ\n", io_sq, io_cq); > + return 0; > +} > + > +HumanReadableText *qmp_x_query_nvme(Error **errp) > +{ > + g_autoptr(GString) buf = g_string_new(""); > + > + object_child_foreach_recursive(object_get_root(), collect_one, buf); > + > + if (buf->len == 0) { > + g_string_append(buf, "no NVMe controllers\n"); > + } > + > + return human_readable_text_from_str(buf); > +} > diff --git a/qapi/machine.json b/qapi/machine.json > index 685e4e29b8..d4a589e768 100644 > --- a/qapi/machine.json > +++ b/qapi/machine.json > @@ -1772,6 +1772,23 @@ > 'returns': 'HumanReadableText', > 'features': [ 'unstable' ] } > > +## > +# @x-query-nvme: > +# > +# Query state of emulated NVMe controllers. > +# > +# Features: > +# > +# @unstable: This command is meant for debugging. > +# > +# Returns: NVMe controller state as human-readable text > +# > +# Since: 11.1 > +## > +{ 'command': 'x-query-nvme', > + 'returns': 'HumanReadableText', > + 'features': [ 'unstable' ] } > + > ## > # @SmbiosEntryPointType: > # > -- > 2.53.0 > -- -----Open up your eyes, open up your mind, open up your code ------- / Dr. David Alan Gilbert | Running GNU/Linux | Happy \ \ dave @ treblig.org | | In Hex / \ _________________________|_____ http://www.treblig.org |_______/ ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] hw/nvme: add 'info nvme' HMP command 2026-05-12 0:03 ` Dr. David Alan Gilbert @ 2026-05-13 11:51 ` Markus Armbruster 2026-05-13 18:15 ` Mateusz Nowicki 0 siblings, 1 reply; 10+ messages in thread From: Markus Armbruster @ 2026-05-13 11:51 UTC (permalink / raw) To: Dr. David Alan Gilbert Cc: Mateusz Nowicki, qemu-devel, qemu-block, Keith Busch, Klaus Jensen, Jesper Devantier, Eric Blake, Philippe Mathieu-Daudé, Zhao Liu "Dr. David Alan Gilbert" <dave@treblig.org> writes: > * Mateusz Nowicki (mateusz.nowicki@posteo.net) wrote: >> Add an 'info nvme' HMP command for inspecting emulated NVMe >> controllers from the QEMU monitor. >> >> For each NVMe controller in the QOM tree the command prints: >> >> - PCI BDF, vendor/device ID and BAR0 base address; >> - Identify Controller fields visible to the host driver: SN, MN, FR >> and CNTLID; >> - the CC, CSTS and AQA registers (raw 32-bit values); >> - the number of admin and active I/O submission/completion queues. >> >> Useful when debugging the Linux NVMe driver against the QEMU emulation >> without attaching gdb to the QEMU process. >> >> Example: >> >> (qemu) info nvme >> /machine/peripheral-anon/device[0] >> PCI: BDF 00:04.0 VID=8086 DID=5845 BAR0=0x00000000feb50000 >> ID: SN=NVME0001 MN=QEMU NVMe Ctrl FR=... CNTLID=0x0000 >> CC: 0x00460001 >> CSTS: 0x00000001 >> AQA: 0x001f001f >> Queues: 1 admin + 4 IO SQ / 4 IO CQ > > I won't claim to know what those NVMe magic numbers mean, but I'll > trust you they're useful. > >> Signed-off-by: Mateusz Nowicki <mateusz.nowicki@posteo.net> >> --- >> hmp-commands-info.hx | 13 ++++++++ >> hw/nvme/meson.build | 2 +- >> hw/nvme/monitor.c | 76 ++++++++++++++++++++++++++++++++++++++++++++ >> qapi/machine.json | 17 ++++++++++ > > So that lot looks OK from an HMP point of view, I wonder if > qapi/machine.json is the right place for it. Markus? qapi/machine.json has become a bit of a dumping ground, I'm afraid. Precedence for putting queries for the machine's device frontends of certain kinds there: x-query-irq, x-query-interrupt-controllers, and x-query-usb. All the other NVMe QAPI stuff is in qapi/block-core.json, but that's the block backend, not frontend. Not a good home for x-query-nvme. Let's take the patch as is. One nitpick below. > Acked-by: Dr. David Alan Gilbert <dave@treblig.org> > >> 4 files changed, 107 insertions(+), 1 deletion(-) >> create mode 100644 hw/nvme/monitor.c >> >> diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx >> index 82134eb6c2..b984691c3c 100644 >> --- a/hmp-commands-info.hx >> +++ b/hmp-commands-info.hx >> @@ -340,6 +340,19 @@ SRST >> Show guest USB devices. >> ERST >> >> + { >> + .name = "nvme", >> + .args_type = "", >> + .params = "", >> + .help = "show emulated NVMe controllers", >> + .cmd_info_hrt = qmp_x_query_nvme, >> + }, >> + >> +SRST >> + ``info nvme`` >> + Show emulated NVMe controllers. >> +ERST >> + >> { >> .name = "usbhost", >> .args_type = "", >> diff --git a/hw/nvme/meson.build b/hw/nvme/meson.build >> index 7d5caa53c2..3017f058f9 100644 >> --- a/hw/nvme/meson.build >> +++ b/hw/nvme/meson.build >> @@ -1 +1 @@ >> -system_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('ctrl.c', 'dif.c', 'ns.c', 'subsys.c', 'nguid.c')) >> \ No newline at end of file >> +system_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('ctrl.c', 'dif.c', 'ns.c', 'subsys.c', 'nguid.c', 'monitor.c')) >> \ No newline at end of file Please use the opportunity to add the newline. [...] QAPI schema Acked-by: Markus Armbruster <armbru@redhat.com> ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] hw/nvme: add 'info nvme' HMP command 2026-05-13 11:51 ` Markus Armbruster @ 2026-05-13 18:15 ` Mateusz Nowicki 0 siblings, 0 replies; 10+ messages in thread From: Mateusz Nowicki @ 2026-05-13 18:15 UTC (permalink / raw) To: Markus Armbruster; +Cc: Dr . David Alan Gilbert, qemu-devel, Mateusz Nowicki Thanks for the reviews. v2 sent, with the meson.build trailing newline fixed and your Acked-by tags picked up: https://lore.kernel.org/qemu-devel/cover.1778694320.git.mateusz.nowicki@posteo.net/ Mateusz ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] hw/nvme: add 'info nvme' HMP command 2026-05-10 10:57 ` [PATCH 1/2] hw/nvme: add 'info nvme' HMP command Mateusz Nowicki 2026-05-12 0:03 ` Dr. David Alan Gilbert @ 2026-05-18 12:52 ` Klaus Jensen 2026-05-18 15:01 ` mateusz.nowicki 1 sibling, 1 reply; 10+ messages in thread From: Klaus Jensen @ 2026-05-18 12:52 UTC (permalink / raw) To: Mateusz Nowicki Cc: qemu-devel, qemu-block, Dr. David Alan Gilbert, Keith Busch, Jesper Devantier, Eric Blake, Markus Armbruster, Philippe Mathieu-Daudé, Zhao Liu [-- Attachment #1: Type: text/plain, Size: 5927 bytes --] On May 10 10:57, Mateusz Nowicki wrote: > Add an 'info nvme' HMP command for inspecting emulated NVMe > controllers from the QEMU monitor. > > For each NVMe controller in the QOM tree the command prints: > > - PCI BDF, vendor/device ID and BAR0 base address; > - Identify Controller fields visible to the host driver: SN, MN, FR > and CNTLID; > - the CC, CSTS and AQA registers (raw 32-bit values); > - the number of admin and active I/O submission/completion queues. > > Useful when debugging the Linux NVMe driver against the QEMU emulation > without attaching gdb to the QEMU process. > > Example: > > (qemu) info nvme > /machine/peripheral-anon/device[0] > PCI: BDF 00:04.0 VID=8086 DID=5845 BAR0=0x00000000feb50000 > ID: SN=NVME0001 MN=QEMU NVMe Ctrl FR=... CNTLID=0x0000 > CC: 0x00460001 > CSTS: 0x00000001 > AQA: 0x001f001f > Queues: 1 admin + 4 IO SQ / 4 IO CQ > > Signed-off-by: Mateusz Nowicki <mateusz.nowicki@posteo.net> > --- > hmp-commands-info.hx | 13 ++++++++ > hw/nvme/meson.build | 2 +- > hw/nvme/monitor.c | 76 ++++++++++++++++++++++++++++++++++++++++++++ > qapi/machine.json | 17 ++++++++++ > 4 files changed, 107 insertions(+), 1 deletion(-) > create mode 100644 hw/nvme/monitor.c > > diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx > index 82134eb6c2..b984691c3c 100644 > --- a/hmp-commands-info.hx > +++ b/hmp-commands-info.hx > @@ -340,6 +340,19 @@ SRST > Show guest USB devices. > ERST > > + { > + .name = "nvme", > + .args_type = "", > + .params = "", > + .help = "show emulated NVMe controllers", > + .cmd_info_hrt = qmp_x_query_nvme, > + }, > + > +SRST > + ``info nvme`` > + Show emulated NVMe controllers. > +ERST > + > { > .name = "usbhost", > .args_type = "", > diff --git a/hw/nvme/meson.build b/hw/nvme/meson.build > index 7d5caa53c2..3017f058f9 100644 > --- a/hw/nvme/meson.build > +++ b/hw/nvme/meson.build > @@ -1 +1 @@ > -system_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('ctrl.c', 'dif.c', 'ns.c', 'subsys.c', 'nguid.c')) > \ No newline at end of file > +system_ss.add(when: 'CONFIG_NVME_PCI', if_true: files('ctrl.c', 'dif.c', 'ns.c', 'subsys.c', 'nguid.c', 'monitor.c')) > \ No newline at end of file > diff --git a/hw/nvme/monitor.c b/hw/nvme/monitor.c > new file mode 100644 > index 0000000000..95a6754437 > --- /dev/null > +++ b/hw/nvme/monitor.c > @@ -0,0 +1,76 @@ > +/* > + * QEMU NVMe Controller monitor commands > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + */ > + > +#include "qemu/osdep.h" > +#include "qapi/error.h" > +#include "qapi/qapi-commands-machine.h" > +#include "qapi/type-helpers.h" > +#include "hw/pci/pci.h" > + > +#include "nvme.h" > + > +static int collect_one(Object *obj, void *opaque) > +{ > + GString *buf = opaque; > + NvmeCtrl *n; > + PCIDevice *pci; > + pcibus_t bar0; > + unsigned io_sq = 0, io_cq = 0; > + > + if (!object_dynamic_cast(obj, TYPE_NVME)) { > + return 0; > + } > + n = NVME(obj); > + pci = PCI_DEVICE(n); > + bar0 = pci_get_bar_addr(pci, 0); > + > + for (unsigned i = 1; i <= n->params.max_ioqpairs; i++) { I think this needs to run over n->conf_ioqpairs to also work for SR-IOV VFs? Same goes for patch 2 I think. > + if (n->sq && n->sq[i]) { > + io_sq++; > + } > + if (n->cq && n->cq[i]) { > + io_cq++; > + } > + } > + > + g_string_append_printf(buf, "%s\n", object_get_canonical_path(obj)); > + g_string_append_printf(buf, > + " PCI: BDF %02x:%02x.%x VID=%04x DID=%04x ", > + pci_dev_bus_num(pci), PCI_SLOT(pci->devfn), PCI_FUNC(pci->devfn), > + pci_get_word(pci->config + PCI_VENDOR_ID), > + pci_get_word(pci->config + PCI_DEVICE_ID)); > + if (bar0 == PCI_BAR_UNMAPPED) { > + g_string_append(buf, "BAR0=unmapped\n"); > + } else { > + g_string_append_printf(buf, "BAR0=0x%016" PRIx64 "\n", > + (uint64_t)bar0); > + } > + g_string_append_printf(buf, > + " ID: SN=%.20s MN=%.40s FR=%.8s CNTLID=0x%04x\n", > + n->id_ctrl.sn, n->id_ctrl.mn, n->id_ctrl.fr, n->cntlid); > + g_string_append_printf(buf, " CC: 0x%08x\n", > + ldl_le_p(&n->bar.cc)); > + g_string_append_printf(buf, " CSTS: 0x%08x\n", > + ldl_le_p(&n->bar.csts)); > + g_string_append_printf(buf, " AQA: 0x%08x\n", > + ldl_le_p(&n->bar.aqa)); > + g_string_append_printf(buf, > + " Queues: 1 admin + %u IO SQ / %u IO CQ\n", io_sq, io_cq); > + return 0; > +} > + > +HumanReadableText *qmp_x_query_nvme(Error **errp) > +{ > + g_autoptr(GString) buf = g_string_new(""); > + > + object_child_foreach_recursive(object_get_root(), collect_one, buf); > + > + if (buf->len == 0) { > + g_string_append(buf, "no NVMe controllers\n"); > + } > + > + return human_readable_text_from_str(buf); > +} > diff --git a/qapi/machine.json b/qapi/machine.json > index 685e4e29b8..d4a589e768 100644 > --- a/qapi/machine.json > +++ b/qapi/machine.json > @@ -1772,6 +1772,23 @@ > 'returns': 'HumanReadableText', > 'features': [ 'unstable' ] } > > +## > +# @x-query-nvme: > +# > +# Query state of emulated NVMe controllers. > +# > +# Features: > +# > +# @unstable: This command is meant for debugging. > +# > +# Returns: NVMe controller state as human-readable text > +# > +# Since: 11.1 > +## > +{ 'command': 'x-query-nvme', > + 'returns': 'HumanReadableText', > + 'features': [ 'unstable' ] } > + > ## > # @SmbiosEntryPointType: > # > -- > 2.53.0 > > [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 488 bytes --] ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/2] hw/nvme: add 'info nvme' HMP command 2026-05-18 12:52 ` Klaus Jensen @ 2026-05-18 15:01 ` mateusz.nowicki 0 siblings, 0 replies; 10+ messages in thread From: mateusz.nowicki @ 2026-05-18 15:01 UTC (permalink / raw) To: Klaus Jensen Cc: qemu-devel, qemu-block, Dr. David Alan Gilbert, Keith Busch, Jesper Devantier, Eric Blake, Markus Armbruster, Philippe Mathieu-Daudé, Zhao Liu > I think this needs to run over n->conf_ioqpairs to also work for SR-IOV > VFs? Same goes for patch 2 I think. > Good catch, agreed. Sending v3 with the fix applied to both patches. Thanks, Mateusz ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 2/2] hw/nvme: add 'info nvme-queues' HMP command 2026-05-10 10:57 [PATCH 0/2] hw/nvme: add monitor commands for inspecting state Mateusz Nowicki 2026-05-10 10:57 ` [PATCH 1/2] hw/nvme: add 'info nvme' HMP command Mateusz Nowicki @ 2026-05-10 10:57 ` Mateusz Nowicki 2026-05-12 0:08 ` Dr. David Alan Gilbert 1 sibling, 1 reply; 10+ messages in thread From: Mateusz Nowicki @ 2026-05-10 10:57 UTC (permalink / raw) To: qemu-devel Cc: qemu-block, Mateusz Nowicki, Dr. David Alan Gilbert, Keith Busch, Klaus Jensen, Jesper Devantier, Philippe Mathieu-Daudé, Zhao Liu, Eric Blake, Markus Armbruster Add an 'info nvme-queues' HMP command that lists, for every emulated NVMe controller, the admin SQ/CQ and every active I/O SQ/CQ. For each queue the command prints: - the ring size, head and tail indices; - the associated CQ id (for SQ) or interrupt vector (for CQ); - the queue's PRP1 (DMA base address of the ring); - the BAR0-relative doorbell offset (SQyTDBL / CQyHDBL); - the current CQ phase tag. The doorbell offsets are computed from CAP.DSTRD so they remain correct if the emulation ever exposes a non-zero stride. Useful when debugging the Linux NVMe driver against the QEMU emulation - queue setup, doorbell rings, AERs held in the admin SQ etc. - without attaching gdb to the QEMU process. Example, with the long PRP1 ring address and BAR0-relative doorbell offset replaced by '...'; phase tag column omitted for brevity: (qemu) info nvme-queues /machine/peripheral-anon/device[0] SQ 0 size=32 head=9 tail=9 cqid=0 prp1=... SQTDBL=... CQ 0 size=32 head=8 tail=8 iv=0 prp1=... CQHDBL=... SQ 1 size=1024 head=2 tail=2 cqid=1 prp1=... SQTDBL=... CQ 1 size=1024 head=2 tail=2 iv=1 prp1=... CQHDBL=... Signed-off-by: Mateusz Nowicki <mateusz.nowicki@posteo.net> --- hmp-commands-info.hx | 15 +++++++++++ hw/nvme/monitor.c | 63 ++++++++++++++++++++++++++++++++++++++++++++ qapi/machine.json | 17 ++++++++++++ 3 files changed, 95 insertions(+) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index b984691c3c..874c1e1970 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -353,6 +353,21 @@ SRST Show emulated NVMe controllers. ERST + { + .name = "nvme-queues", + .args_type = "", + .params = "", + .help = "show active NVMe queues and their doorbells", + .cmd_info_hrt = qmp_x_query_nvme_queues, + }, + +SRST + ``info nvme-queues`` + Show all active NVMe submission and completion queues, including + head/tail, DMA address of the ring and BAR0-relative doorbell + offset. +ERST + { .name = "usbhost", .args_type = "", diff --git a/hw/nvme/monitor.c b/hw/nvme/monitor.c index 95a6754437..c8161943c0 100644 --- a/hw/nvme/monitor.c +++ b/hw/nvme/monitor.c @@ -74,3 +74,66 @@ HumanReadableText *qmp_x_query_nvme(Error **errp) return human_readable_text_from_str(buf); } + +static void append_sq(GString *buf, NvmeSQueue *sq, unsigned stride) +{ + hwaddr db_off = 0x1000 + 2 * sq->sqid * stride; + + g_string_append_printf(buf, + " SQ %u size=%-5u head=%-5u tail=%-5u cqid=%-3u " + "prp1=0x%016" PRIx64 " SQTDBL=BAR0+0x%03" HWADDR_PRIx "\n", + sq->sqid, sq->size, sq->head, sq->tail, sq->cqid, + sq->dma_addr, db_off); +} + +static void append_cq(GString *buf, NvmeCQueue *cq, unsigned stride) +{ + hwaddr db_off = 0x1000 + (2 * cq->cqid + 1) * stride; + + g_string_append_printf(buf, + " CQ %u size=%-5u head=%-5u tail=%-5u iv=%-5u " + "prp1=0x%016" PRIx64 " CQHDBL=BAR0+0x%03" HWADDR_PRIx + " phaseTag=%u\n", + cq->cqid, cq->size, cq->head, cq->tail, cq->vector, + cq->dma_addr, db_off, cq->phase); +} + +static int collect_queues(Object *obj, void *opaque) +{ + GString *buf = opaque; + NvmeCtrl *n; + unsigned stride; + + if (!object_dynamic_cast(obj, TYPE_NVME)) { + return 0; + } + n = NVME(obj); + stride = 4u << NVME_CAP_DSTRD(ldq_le_p(&n->bar.cap)); + + g_string_append_printf(buf, "%s\n", object_get_canonical_path(obj)); + append_sq(buf, &n->admin_sq, stride); + append_cq(buf, &n->admin_cq, stride); + + for (unsigned i = 1; i <= n->params.max_ioqpairs; i++) { + if (n->sq && n->sq[i]) { + append_sq(buf, n->sq[i], stride); + } + if (n->cq && n->cq[i]) { + append_cq(buf, n->cq[i], stride); + } + } + return 0; +} + +HumanReadableText *qmp_x_query_nvme_queues(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + + object_child_foreach_recursive(object_get_root(), collect_queues, buf); + + if (buf->len == 0) { + g_string_append(buf, "no NVMe controllers\n"); + } + + return human_readable_text_from_str(buf); +} diff --git a/qapi/machine.json b/qapi/machine.json index d4a589e768..c21b128e0a 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1789,6 +1789,23 @@ 'returns': 'HumanReadableText', 'features': [ 'unstable' ] } +## +# @x-query-nvme-queues: +# +# Query state of all active SQ/CQ for emulated NVMe controllers. +# +# Features: +# +# @unstable: This command is meant for debugging. +# +# Returns: per-queue state as human-readable text +# +# Since: 11.1 +## +{ 'command': 'x-query-nvme-queues', + 'returns': 'HumanReadableText', + 'features': [ 'unstable' ] } + ## # @SmbiosEntryPointType: # -- 2.53.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] hw/nvme: add 'info nvme-queues' HMP command 2026-05-10 10:57 ` [PATCH 2/2] hw/nvme: add 'info nvme-queues' " Mateusz Nowicki @ 2026-05-12 0:08 ` Dr. David Alan Gilbert 2026-05-13 11:52 ` Markus Armbruster 0 siblings, 1 reply; 10+ messages in thread From: Dr. David Alan Gilbert @ 2026-05-12 0:08 UTC (permalink / raw) To: Mateusz Nowicki, Markus Armbruster Cc: qemu-devel, qemu-block, Keith Busch, Klaus Jensen, Jesper Devantier, Philippe Mathieu-Daudé, Zhao Liu, Eric Blake * Mateusz Nowicki (mateusz.nowicki@posteo.net) wrote: > Add an 'info nvme-queues' HMP command that lists, for every emulated > NVMe controller, the admin SQ/CQ and every active I/O SQ/CQ. > > For each queue the command prints: > > - the ring size, head and tail indices; > - the associated CQ id (for SQ) or interrupt vector (for CQ); > - the queue's PRP1 (DMA base address of the ring); > - the BAR0-relative doorbell offset (SQyTDBL / CQyHDBL); > - the current CQ phase tag. > > The doorbell offsets are computed from CAP.DSTRD so they remain > correct if the emulation ever exposes a non-zero stride. > > Useful when debugging the Linux NVMe driver against the QEMU > emulation - queue setup, doorbell rings, AERs held in the admin SQ > etc. - without attaching gdb to the QEMU process. > > Example, with the long PRP1 ring address and BAR0-relative doorbell > offset replaced by '...'; phase tag column omitted for brevity: > > (qemu) info nvme-queues > /machine/peripheral-anon/device[0] > SQ 0 size=32 head=9 tail=9 cqid=0 prp1=... SQTDBL=... > CQ 0 size=32 head=8 tail=8 iv=0 prp1=... CQHDBL=... > SQ 1 size=1024 head=2 tail=2 cqid=1 prp1=... SQTDBL=... > CQ 1 size=1024 head=2 tail=2 iv=1 prp1=... CQHDBL=... > > Signed-off-by: Mateusz Nowicki <mateusz.nowicki@posteo.net> as the previous patch; looks OK from HMP but check where the json should live. Acked-by: Dr. David Alan Gilbert <dave@treblig.org> > --- > hmp-commands-info.hx | 15 +++++++++++ > hw/nvme/monitor.c | 63 ++++++++++++++++++++++++++++++++++++++++++++ > qapi/machine.json | 17 ++++++++++++ > 3 files changed, 95 insertions(+) > > diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx > index b984691c3c..874c1e1970 100644 > --- a/hmp-commands-info.hx > +++ b/hmp-commands-info.hx > @@ -353,6 +353,21 @@ SRST > Show emulated NVMe controllers. > ERST > > + { > + .name = "nvme-queues", > + .args_type = "", > + .params = "", > + .help = "show active NVMe queues and their doorbells", > + .cmd_info_hrt = qmp_x_query_nvme_queues, > + }, > + > +SRST > + ``info nvme-queues`` > + Show all active NVMe submission and completion queues, including > + head/tail, DMA address of the ring and BAR0-relative doorbell > + offset. > +ERST > + > { > .name = "usbhost", > .args_type = "", > diff --git a/hw/nvme/monitor.c b/hw/nvme/monitor.c > index 95a6754437..c8161943c0 100644 > --- a/hw/nvme/monitor.c > +++ b/hw/nvme/monitor.c > @@ -74,3 +74,66 @@ HumanReadableText *qmp_x_query_nvme(Error **errp) > > return human_readable_text_from_str(buf); > } > + > +static void append_sq(GString *buf, NvmeSQueue *sq, unsigned stride) > +{ > + hwaddr db_off = 0x1000 + 2 * sq->sqid * stride; > + > + g_string_append_printf(buf, > + " SQ %u size=%-5u head=%-5u tail=%-5u cqid=%-3u " > + "prp1=0x%016" PRIx64 " SQTDBL=BAR0+0x%03" HWADDR_PRIx "\n", > + sq->sqid, sq->size, sq->head, sq->tail, sq->cqid, > + sq->dma_addr, db_off); > +} > + > +static void append_cq(GString *buf, NvmeCQueue *cq, unsigned stride) > +{ > + hwaddr db_off = 0x1000 + (2 * cq->cqid + 1) * stride; > + > + g_string_append_printf(buf, > + " CQ %u size=%-5u head=%-5u tail=%-5u iv=%-5u " > + "prp1=0x%016" PRIx64 " CQHDBL=BAR0+0x%03" HWADDR_PRIx > + " phaseTag=%u\n", > + cq->cqid, cq->size, cq->head, cq->tail, cq->vector, > + cq->dma_addr, db_off, cq->phase); > +} > + > +static int collect_queues(Object *obj, void *opaque) > +{ > + GString *buf = opaque; > + NvmeCtrl *n; > + unsigned stride; > + > + if (!object_dynamic_cast(obj, TYPE_NVME)) { > + return 0; > + } > + n = NVME(obj); > + stride = 4u << NVME_CAP_DSTRD(ldq_le_p(&n->bar.cap)); > + > + g_string_append_printf(buf, "%s\n", object_get_canonical_path(obj)); > + append_sq(buf, &n->admin_sq, stride); > + append_cq(buf, &n->admin_cq, stride); > + > + for (unsigned i = 1; i <= n->params.max_ioqpairs; i++) { > + if (n->sq && n->sq[i]) { > + append_sq(buf, n->sq[i], stride); > + } > + if (n->cq && n->cq[i]) { > + append_cq(buf, n->cq[i], stride); > + } > + } > + return 0; > +} > + > +HumanReadableText *qmp_x_query_nvme_queues(Error **errp) > +{ > + g_autoptr(GString) buf = g_string_new(""); > + > + object_child_foreach_recursive(object_get_root(), collect_queues, buf); > + > + if (buf->len == 0) { > + g_string_append(buf, "no NVMe controllers\n"); > + } > + > + return human_readable_text_from_str(buf); > +} > diff --git a/qapi/machine.json b/qapi/machine.json > index d4a589e768..c21b128e0a 100644 > --- a/qapi/machine.json > +++ b/qapi/machine.json > @@ -1789,6 +1789,23 @@ > 'returns': 'HumanReadableText', > 'features': [ 'unstable' ] } > > +## > +# @x-query-nvme-queues: > +# > +# Query state of all active SQ/CQ for emulated NVMe controllers. > +# > +# Features: > +# > +# @unstable: This command is meant for debugging. > +# > +# Returns: per-queue state as human-readable text > +# > +# Since: 11.1 > +## > +{ 'command': 'x-query-nvme-queues', > + 'returns': 'HumanReadableText', > + 'features': [ 'unstable' ] } > + > ## > # @SmbiosEntryPointType: > # > -- > 2.53.0 > -- -----Open up your eyes, open up your mind, open up your code ------- / Dr. David Alan Gilbert | Running GNU/Linux | Happy \ \ dave @ treblig.org | | In Hex / \ _________________________|_____ http://www.treblig.org |_______/ ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/2] hw/nvme: add 'info nvme-queues' HMP command 2026-05-12 0:08 ` Dr. David Alan Gilbert @ 2026-05-13 11:52 ` Markus Armbruster 0 siblings, 0 replies; 10+ messages in thread From: Markus Armbruster @ 2026-05-13 11:52 UTC (permalink / raw) To: Dr. David Alan Gilbert Cc: Mateusz Nowicki, qemu-devel, qemu-block, Keith Busch, Klaus Jensen, Jesper Devantier, Philippe Mathieu-Daudé, Zhao Liu, Eric Blake "Dr. David Alan Gilbert" <dave@treblig.org> writes: > * Mateusz Nowicki (mateusz.nowicki@posteo.net) wrote: >> Add an 'info nvme-queues' HMP command that lists, for every emulated >> NVMe controller, the admin SQ/CQ and every active I/O SQ/CQ. >> >> For each queue the command prints: >> >> - the ring size, head and tail indices; >> - the associated CQ id (for SQ) or interrupt vector (for CQ); >> - the queue's PRP1 (DMA base address of the ring); >> - the BAR0-relative doorbell offset (SQyTDBL / CQyHDBL); >> - the current CQ phase tag. >> >> The doorbell offsets are computed from CAP.DSTRD so they remain >> correct if the emulation ever exposes a non-zero stride. >> >> Useful when debugging the Linux NVMe driver against the QEMU >> emulation - queue setup, doorbell rings, AERs held in the admin SQ >> etc. - without attaching gdb to the QEMU process. >> >> Example, with the long PRP1 ring address and BAR0-relative doorbell >> offset replaced by '...'; phase tag column omitted for brevity: >> >> (qemu) info nvme-queues >> /machine/peripheral-anon/device[0] >> SQ 0 size=32 head=9 tail=9 cqid=0 prp1=... SQTDBL=... >> CQ 0 size=32 head=8 tail=8 iv=0 prp1=... CQHDBL=... >> SQ 1 size=1024 head=2 tail=2 cqid=1 prp1=... SQTDBL=... >> CQ 1 size=1024 head=2 tail=2 iv=1 prp1=... CQHDBL=... >> >> Signed-off-by: Mateusz Nowicki <mateusz.nowicki@posteo.net> > > as the previous patch; looks OK from HMP but check where the json should live. > > Acked-by: Dr. David Alan Gilbert <dave@treblig.org> Same answer. QAPI schema Acked-by: Markus Armbruster <armbru@redhat.com> ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-05-18 15:02 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-10 10:57 [PATCH 0/2] hw/nvme: add monitor commands for inspecting state Mateusz Nowicki 2026-05-10 10:57 ` [PATCH 1/2] hw/nvme: add 'info nvme' HMP command Mateusz Nowicki 2026-05-12 0:03 ` Dr. David Alan Gilbert 2026-05-13 11:51 ` Markus Armbruster 2026-05-13 18:15 ` Mateusz Nowicki 2026-05-18 12:52 ` Klaus Jensen 2026-05-18 15:01 ` mateusz.nowicki 2026-05-10 10:57 ` [PATCH 2/2] hw/nvme: add 'info nvme-queues' " Mateusz Nowicki 2026-05-12 0:08 ` Dr. David Alan Gilbert 2026-05-13 11:52 ` Markus Armbruster
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.