@samitouri / QOSamiQemu / commits / 9d378cab53

hw/nvme: add basic live migration support

It has some limitations: - only one NVMe namespace is supported - SMART counters are not preserved - CMB is not supported - PMR is not supported - SPDM is not supported - SR-IOV is not supported Acked-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@futurfusion.io> Reviewed-by: Klaus Jensen <k.jensen@samsung.com> Reviewed-by: Fabiano Rosas <farosas@suse.de> Signed-off-by: Klaus Jensen <k.jensen@samsung.com>

Alexander Mikhalitsyn committed Jun 11, 2026 at 20:08 UTC 9d378cab537d70d9e02edc11ff6ded7266c1078e
4 files changed +934 -9
hw/nvme/ctrl.c
+751 -9
@@ -210,6 +210,7 @@
210 #include "hw/pci/pcie_sriov.h"
211 #include "system/spdm-socket.h"
212 #include "migration/blocker.h"
213 +#include "migration/qemu-file-types.h"
214 #include "migration/vmstate.h"
215
216 #include "nvme.h"
@@ -1533,6 +1534,18 @@ static void nvme_post_cqes(void *opaque)
1534 break;
1535 }
1536
1537 + /*
1538 + * Here we take the following fields from NvmeRequest structure
1539 + * and write cqe to the guest RAM based on them:
1540 + * - req->sq
1541 + * - req->status
1542 + * - req->cqe
1543 + *
1544 + * If you change this code and more fields from NvmeRequest are
1545 + * used, please make sure that you have handled this in:
1546 + * nvme_vmstate_request and nvme_ctrl_pre_save().
1547 + */
1548 +
1549 sq = req->sq;
1550 req->cqe.status = cpu_to_le16((req->status << 1) | cq->phase);
1551 req->cqe.sq_head = cpu_to_le16(sq->head);
@@ -4918,6 +4931,25 @@ static void nvme_init_sq(NvmeSQueue *sq, NvmeCtrl *n, uint64_t dma_addr,
4931 __nvme_init_sq(sq);
4932 }
4933
4934 +static void nvme_restore_sq(NvmeSQueue *sq_from)
4935 +{
4936 + NvmeCtrl *n = sq_from->ctrl;
4937 + NvmeSQueue *sq = sq_from;
4938 +
4939 + if (sq_from->sqid == 0) {
4940 + sq = &n->admin_sq;
4941 + sq->ctrl = n;
4942 + sq->dma_addr = sq_from->dma_addr;
4943 + sq->sqid = sq_from->sqid;
4944 + sq->size = sq_from->size;
4945 + sq->cqid = sq_from->cqid;
4946 + sq->head = sq_from->head;
4947 + sq->tail = sq_from->tail;
4948 + }
4949 +
4950 + __nvme_init_sq(sq);
4951 +}
4952 +
4953 static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeRequest *req)
4954 {
4955 NvmeSQueue *sq;
@@ -5620,6 +5652,39 @@ static void nvme_init_cq(NvmeCQueue *cq, NvmeCtrl *n, uint64_t dma_addr,
5652 __nvme_init_cq(cq);
5653 }
5654
5655 +static void move_cq_req_list(NvmeCQueue *cq_to, NvmeCQueue *cq_from)
5656 +{
5657 + NvmeRequest *req, *next;
5658 +
5659 + QTAILQ_FOREACH_SAFE(req, &cq_from->req_list, entry, next) {
5660 + QTAILQ_REMOVE(&cq_from->req_list, req, entry);
5661 + QTAILQ_INSERT_TAIL(&cq_to->req_list, req, entry);
5662 + }
5663 +}
5664 +
5665 +static void nvme_restore_cq(NvmeCQueue *cq_from)
5666 +{
5667 + NvmeCtrl *n = cq_from->ctrl;
5668 + NvmeCQueue *cq = cq_from;
5669 +
5670 + if (cq_from->cqid == 0) {
5671 + cq = &n->admin_cq;
5672 + cq->ctrl = n;
5673 + cq->cqid = cq_from->cqid;
5674 + cq->size = cq_from->size;
5675 + cq->dma_addr = cq_from->dma_addr;
5676 + cq->phase = cq_from->phase;
5677 + cq->irq_enabled = cq_from->irq_enabled;
5678 + cq->vector = cq_from->vector;
5679 + cq->head = cq_from->head;
5680 + cq->tail = cq_from->tail;
5681 + QTAILQ_INIT(&cq->req_list);
5682 + move_cq_req_list(cq, cq_from);
5683 + }
5684 +
5685 + __nvme_init_cq(cq);
5686 +}
5687 +
5688 static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeRequest *req)
5689 {
5690 NvmeCQueue *cq;
@@ -7320,7 +7385,7 @@ static uint16_t nvme_dbbuf_config(NvmeCtrl *n, const NvmeRequest *req)
7385 n->dbbuf_eis = eis_addr;
7386 n->dbbuf_enabled = true;
7387
7323 - for (i = 0; i < n->params.max_ioqpairs + 1; i++) {
7388 + for (i = 0; i < n->num_queues; i++) {
7389 NvmeSQueue *sq = n->sq[i];
7390 NvmeCQueue *cq = n->cq[i];
7391
@@ -7764,7 +7829,7 @@ static int nvme_atomic_write_check(NvmeCtrl *n, NvmeCmd *cmd,
7829 /*
7830 * Walk the queues to see if there are any atomic conflicts.
7831 */
7767 - for (i = 1; i < n->params.max_ioqpairs + 1; i++) {
7832 + for (i = 1; i < n->num_queues; i++) {
7833 NvmeSQueue *sq;
7834 NvmeRequest *req;
7835 NvmeRwCmd *req_rw;
@@ -7834,6 +7899,12 @@ static void nvme_process_sq(void *opaque)
7899 NvmeCmd cmd;
7900 NvmeRequest *req;
7901
7902 + /*
7903 + * We don't want to have a race with nvme_ctrl_pre_save().
7904 + * What implicitly protects us from this is BQL.
7905 + */
7906 + assert(bql_locked());
7907 +
7908 if (n->dbbuf_enabled) {
7909 nvme_update_sq_tail(sq);
7910 }
@@ -7951,12 +8022,12 @@ static void nvme_ctrl_reset(NvmeCtrl *n, NvmeResetType rst)
8022 nvme_ns_drain(ns);
8023 }
8024
7954 - for (i = 0; i < n->params.max_ioqpairs + 1; i++) {
8025 + for (i = 0; i < n->num_queues; i++) {
8026 if (n->sq[i] != NULL) {
8027 nvme_free_sq(n->sq[i], n);
8028 }
8029 }
7959 - for (i = 0; i < n->params.max_ioqpairs + 1; i++) {
8030 + for (i = 0; i < n->num_queues; i++) {
8031 if (n->cq[i] != NULL) {
8032 nvme_free_cq(n->cq[i], n);
8033 }
@@ -8626,6 +8697,8 @@ static bool nvme_check_params(NvmeCtrl *n, Error **errp)
8697 params->max_ioqpairs = params->num_queues - 1;
8698 }
8699
8700 + n->num_queues = params->max_ioqpairs + 1;
8701 +
8702 if (n->namespace.blkconf.blk && n->subsys) {
8703 error_setg(errp, "subsystem support is unavailable with legacy "
8704 "namespace ('drive' property)");
@@ -8799,8 +8872,8 @@ static void nvme_init_state(NvmeCtrl *n)
8872 n->conf_msix_qsize = n->params.msix_qsize;
8873 }
8874
8802 - n->sq = g_new0(NvmeSQueue *, n->params.max_ioqpairs + 1);
8803 - n->cq = g_new0(NvmeCQueue *, n->params.max_ioqpairs + 1);
8875 + n->sq = g_new0(NvmeSQueue *, n->num_queues);
8876 + n->cq = g_new0(NvmeCQueue *, n->num_queues);
8877 n->temperature = NVME_TEMPERATURE;
8878 n->features.temp_thresh_hi = NVME_TEMPERATURE_WARNING;
8879 n->starttime_ms = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
@@ -9035,7 +9108,7 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp)
9108 }
9109
9110 if (n->params.msix_exclusive_bar && !pci_is_vf(pci_dev)) {
9038 - bar_size = nvme_mbar_size(n->params.max_ioqpairs + 1, 0, NULL, NULL);
9111 + bar_size = nvme_mbar_size(n->num_queues, 0, NULL, NULL);
9112 memory_region_init_io(&n->iomem, OBJECT(n), &nvme_mmio_ops, n, "nvme",
9113 bar_size);
9114 pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY |
@@ -9047,7 +9120,7 @@ static bool nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp)
9120 /* add one to max_ioqpairs to account for the admin queue pair */
9121 if (!pci_is_vf(pci_dev)) {
9122 nr_vectors = n->params.msix_qsize;
9050 - bar_size = nvme_mbar_size(n->params.max_ioqpairs + 1,
9123 + bar_size = nvme_mbar_size(n->num_queues,
9124 nr_vectors, &msix_table_offset,
9125 &msix_pba_offset);
9126 } else {
@@ -9780,9 +9853,678 @@ static uint32_t nvme_pci_read_config(PCIDevice *dev, uint32_t address, int len)
9853 return pci_default_read_config(dev, address, len);
9854 }
9855
9856 +static const VMStateDescription nvme_vmstate_cqe = {
9857 + .name = "nvme-cqe",
9858 + .version_id = 1,
9859 + .minimum_version_id = 1,
9860 + .fields = (const VMStateField[]) {
9861 + VMSTATE_UINT32(result, NvmeCqe),
9862 + VMSTATE_UINT32(dw1, NvmeCqe),
9863 + VMSTATE_UINT16(sq_head, NvmeCqe),
9864 + VMSTATE_UINT16(sq_id, NvmeCqe),
9865 + VMSTATE_UINT16(cid, NvmeCqe),
9866 + VMSTATE_UINT16(status, NvmeCqe),
9867 + VMSTATE_END_OF_LIST()
9868 + }
9869 +};
9870 +
9871 +static const VMStateDescription nvme_vmstate_cmd_dptr_sgl = {
9872 + .name = "nvme-request-cmd-dptr-sgl",
9873 + .version_id = 1,
9874 + .minimum_version_id = 1,
9875 + .fields = (const VMStateField[]) {
9876 + VMSTATE_UINT64(addr, NvmeSglDescriptor),
9877 + VMSTATE_UINT32(len, NvmeSglDescriptor),
9878 + VMSTATE_UINT8_ARRAY(rsvd, NvmeSglDescriptor, 3),
9879 + VMSTATE_UINT8(type, NvmeSglDescriptor),
9880 + VMSTATE_END_OF_LIST()
9881 + }
9882 +};
9883 +
9884 +static const VMStateDescription nvme_vmstate_cmd_dptr = {
9885 + .name = "nvme-request-cmd-dptr",
9886 + .version_id = 1,
9887 + .minimum_version_id = 1,
9888 + .fields = (const VMStateField[]) {
9889 + VMSTATE_UINT64(prp1, NvmeCmdDptr),
9890 + VMSTATE_UINT64(prp2, NvmeCmdDptr),
9891 + VMSTATE_STRUCT(sgl, NvmeCmdDptr, 0,
9892 + nvme_vmstate_cmd_dptr_sgl, NvmeSglDescriptor),
9893 + VMSTATE_END_OF_LIST()
9894 + }
9895 +};
9896 +
9897 +static const VMStateDescription nvme_vmstate_cmd = {
9898 + .name = "nvme-request-cmd",
9899 + .version_id = 1,
9900 + .minimum_version_id = 1,
9901 + .fields = (const VMStateField[]) {
9902 + VMSTATE_UINT8(opcode, NvmeCmd),
9903 + VMSTATE_UINT8(flags, NvmeCmd),
9904 + VMSTATE_UINT16(cid, NvmeCmd),
9905 + VMSTATE_UINT32(nsid, NvmeCmd),
9906 + VMSTATE_UINT64(res1, NvmeCmd),
9907 + VMSTATE_UINT64(mptr, NvmeCmd),
9908 + VMSTATE_STRUCT(dptr, NvmeCmd, 0, nvme_vmstate_cmd_dptr, NvmeCmdDptr),
9909 + VMSTATE_UINT32(cdw10, NvmeCmd),
9910 + VMSTATE_UINT32(cdw11, NvmeCmd),
9911 + VMSTATE_UINT32(cdw12, NvmeCmd),
9912 + VMSTATE_UINT32(cdw13, NvmeCmd),
9913 + VMSTATE_UINT32(cdw14, NvmeCmd),
9914 + VMSTATE_UINT32(cdw15, NvmeCmd),
9915 + VMSTATE_END_OF_LIST()
9916 + }
9917 +};
9918 +
9919 +static bool nvme_req_pre_load(void *opaque, Error **errp)
9920 +{
9921 + memset(opaque, 0x0, sizeof(NvmeRequest));
9922 + return true;
9923 +}
9924 +
9925 +static const VMStateDescription nvme_vmstate_request = {
9926 + .name = "nvme-request",
9927 + .version_id = 1,
9928 + .minimum_version_id = 1,
9929 + .pre_load_errp = nvme_req_pre_load,
9930 + .fields = (const VMStateField[]) {
9931 + VMSTATE_UINT16(status, NvmeRequest),
9932 + VMSTATE_STRUCT(cqe, NvmeRequest, 0, nvme_vmstate_cqe, NvmeCqe),
9933 + VMSTATE_STRUCT(cmd, NvmeRequest, 0, nvme_vmstate_cmd, NvmeCmd),
9934 + VMSTATE_END_OF_LIST()
9935 + }
9936 +};
9937 +
9938 +static const VMStateDescription nvme_vmstate_bar = {
9939 + .name = "nvme-bar",
9940 + .minimum_version_id = 1,
9941 + .version_id = 1,
9942 + .fields = (const VMStateField[]) {
9943 + VMSTATE_UINT64(cap, NvmeBar),
9944 + VMSTATE_UINT32(vs, NvmeBar),
9945 + VMSTATE_UINT32(intms, NvmeBar),
9946 + VMSTATE_UINT32(intmc, NvmeBar),
9947 + VMSTATE_UINT32(cc, NvmeBar),
9948 + VMSTATE_UINT8_ARRAY(rsvd24, NvmeBar, 4),
9949 + VMSTATE_UINT32(csts, NvmeBar),
9950 + VMSTATE_UINT32(nssr, NvmeBar),
9951 + VMSTATE_UINT32(aqa, NvmeBar),
9952 + VMSTATE_UINT64(asq, NvmeBar),
9953 + VMSTATE_UINT64(acq, NvmeBar),
9954 + VMSTATE_UINT32(cmbloc, NvmeBar),
9955 + VMSTATE_UINT32(cmbsz, NvmeBar),
9956 + VMSTATE_UINT32(bpinfo, NvmeBar),
9957 + VMSTATE_UINT32(bprsel, NvmeBar),
9958 + VMSTATE_UINT64(bpmbl, NvmeBar),
9959 + VMSTATE_UINT64(cmbmsc, NvmeBar),
9960 + VMSTATE_UINT32(cmbsts, NvmeBar),
9961 + VMSTATE_UINT8_ARRAY(rsvd92, NvmeBar, 3492),
9962 + VMSTATE_UINT32(pmrcap, NvmeBar),
9963 + VMSTATE_UINT32(pmrctl, NvmeBar),
9964 + VMSTATE_UINT32(pmrsts, NvmeBar),
9965 + VMSTATE_UINT32(pmrebs, NvmeBar),
9966 + VMSTATE_UINT32(pmrswtp, NvmeBar),
9967 + VMSTATE_UINT32(pmrmscl, NvmeBar),
9968 + VMSTATE_UINT32(pmrmscu, NvmeBar),
9969 + VMSTATE_UINT8_ARRAY(css, NvmeBar, 484),
9970 + VMSTATE_END_OF_LIST()
9971 + },
9972 +};
9973 +
9974 +static bool nvme_cqueue_pre_load(void *opaque, Error **errp)
9975 +{
9976 + NvmeCQueue *cq = opaque;
9977 +
9978 + QTAILQ_INIT(&cq->req_list);
9979 + return true;
9980 +}
9981 +
9982 +static const VMStateDescription nvme_vmstate_cqueue = {
9983 + .name = "nvme-cq",
9984 + .version_id = 1,
9985 + .minimum_version_id = 1,
9986 + .pre_load_errp = nvme_cqueue_pre_load,
9987 + .fields = (const VMStateField[]) {
9988 + VMSTATE_UINT8(phase, NvmeCQueue),
9989 + VMSTATE_UINT16(cqid, NvmeCQueue),
9990 + VMSTATE_UINT16(irq_enabled, NvmeCQueue),
9991 + VMSTATE_UINT32(head, NvmeCQueue),
9992 + VMSTATE_UINT32(tail, NvmeCQueue),
9993 + VMSTATE_UINT32(vector, NvmeCQueue),
9994 + VMSTATE_UINT32(size, NvmeCQueue),
9995 + VMSTATE_UINT64(dma_addr, NvmeCQueue),
9996 +
9997 + VMSTATE_QTAILQ_V(req_list, NvmeCQueue, 1, nvme_vmstate_request,
9998 + NvmeRequest, entry),
9999 +
10000 + /* db_addr, ei_addr, etc will be recalculated */
10001 + VMSTATE_END_OF_LIST()
10002 + }
10003 +};
10004 +
10005 +static const VMStateDescription nvme_vmstate_squeue = {
10006 + .name = "nvme-sq",
10007 + .version_id = 1,
10008 + .minimum_version_id = 1,
10009 + .fields = (const VMStateField[]) {
10010 + VMSTATE_UINT16(sqid, NvmeSQueue),
10011 + VMSTATE_UINT16(cqid, NvmeSQueue),
10012 + VMSTATE_UINT32(head, NvmeSQueue),
10013 + VMSTATE_UINT32(tail, NvmeSQueue),
10014 + VMSTATE_UINT32(size, NvmeSQueue),
10015 + VMSTATE_UINT64(dma_addr, NvmeSQueue),
10016 + /* db_addr, ei_addr, etc will be recalculated */
10017 + VMSTATE_END_OF_LIST()
10018 + }
10019 +};
10020 +
10021 +static const VMStateDescription nvme_vmstate_async_event_result = {
10022 + .name = "nvme-async-event-result",
10023 + .version_id = 1,
10024 + .minimum_version_id = 1,
10025 + .fields = (const VMStateField[]) {
10026 + VMSTATE_UINT8(event_type, NvmeAerResult),
10027 + VMSTATE_UINT8(event_info, NvmeAerResult),
10028 + VMSTATE_UINT8(log_page, NvmeAerResult),
10029 + VMSTATE_UINT8(resv, NvmeAerResult),
10030 + VMSTATE_END_OF_LIST()
10031 + }
10032 +};
10033 +
10034 +static const VMStateDescription nvme_vmstate_async_event = {
10035 + .name = "nvme-async-event",
10036 + .version_id = 1,
10037 + .minimum_version_id = 1,
10038 + .fields = (const VMStateField[]) {
10039 + VMSTATE_STRUCT(result, NvmeAsyncEvent, 0,
10040 + nvme_vmstate_async_event_result, NvmeAerResult),
10041 + VMSTATE_END_OF_LIST()
10042 + }
10043 +};
10044 +
10045 +static const VMStateDescription nvme_vmstate_hbs = {
10046 + .name = "nvme-hbs",
10047 + .version_id = 1,
10048 + .minimum_version_id = 1,
10049 + .fields = (const VMStateField[]) {
10050 + VMSTATE_UINT8(acre, NvmeHostBehaviorSupport),
10051 + VMSTATE_UINT8(etdas, NvmeHostBehaviorSupport),
10052 + VMSTATE_UINT8(lbafee, NvmeHostBehaviorSupport),
10053 + VMSTATE_UINT8(rsvd3, NvmeHostBehaviorSupport),
10054 + VMSTATE_UINT16(cdfe, NvmeHostBehaviorSupport),
10055 + VMSTATE_UINT8_ARRAY(rsvd6, NvmeHostBehaviorSupport, 506),
10056 + VMSTATE_END_OF_LIST()
10057 + }
10058 +};
10059 +
10060 +const VMStateDescription nvme_vmstate_atomic = {
10061 + .name = "nvme-atomic",
10062 + .version_id = 1,
10063 + .minimum_version_id = 1,
10064 + .fields = (const VMStateField[]) {
10065 + VMSTATE_UINT32(atomic_max_write_size, NvmeAtomic),
10066 + VMSTATE_UINT64(atomic_boundary, NvmeAtomic),
10067 + VMSTATE_UINT64(atomic_nabo, NvmeAtomic),
10068 + VMSTATE_BOOL(atomic_writes, NvmeAtomic),
10069 + VMSTATE_END_OF_LIST()
10070 + }
10071 +};
10072 +
10073 +static bool pre_save_validate_aer_req(NvmeRequest *req, Error **errp)
10074 +{
10075 + /*
10076 + * Can't use assert() here, because we don't want
10077 + * to just crash QEMU when user requests a migration.
10078 + */
10079 + if (!(req->cmd.opcode == NVME_ADM_CMD_ASYNC_EV_REQ)) {
10080 + error_setg(errp, "req->cmd.opcode (%u) != NVME_ADM_CMD_ASYNC_EV_REQ",
10081 + req->cmd.opcode);
10082 + return false;
10083 + }
10084 +
10085 + if (!(req->ns == NULL)) {
10086 + error_setg(errp, "req->ns != NULL");
10087 + return false;
10088 + }
10089 +
10090 + if (!(req->sq == &req->sq->ctrl->admin_sq)) {
10091 + error_setg(errp, "req->sq != &req->sq->ctrl->admin_sq");
10092 + return false;
10093 + }
10094 +
10095 + if (!(req->aiocb == NULL)) {
10096 + error_setg(errp, "req->aiocb != NULL");
10097 + return false;
10098 + }
10099 +
10100 + if (!(req->opaque == NULL)) {
10101 + error_setg(errp, "req->opaque != NULL");
10102 + return false;
10103 + }
10104 +
10105 + if (!(req->atomic_write == false)) {
10106 + error_setg(errp, "req->atomic_write != false");
10107 + return false;
10108 + }
10109 +
10110 + if (req->sg.flags & NVME_SG_ALLOC) {
10111 + error_setg(errp, "unexpected NVME_SG_ALLOC flag in req->sg.flags");
10112 + return false;
10113 + }
10114 +
10115 + return true;
10116 +}
10117 +
10118 +static bool pre_save_validate_cq_req(NvmeRequest *req, Error **errp)
10119 +{
10120 + if (!(req->ns == NULL)) {
10121 + error_setg(errp, "req->ns != NULL");
10122 + return false;
10123 + }
10124 +
10125 + if (!(req->aiocb == NULL)) {
10126 + error_setg(errp, "req->aiocb != NULL");
10127 + return false;
10128 + }
10129 +
10130 + if (!(req->opaque == NULL)) {
10131 + error_setg(errp, "req->opaque != NULL");
10132 + return false;
10133 + }
10134 +
10135 + if (!(req->atomic_write == false)) {
10136 + error_setg(errp, "req->atomic_write != false");
10137 + return false;
10138 + }
10139 +
10140 + if (req->sg.flags & NVME_SG_ALLOC) {
10141 + error_setg(errp, "unexpected NVME_SG_ALLOC flag in req->sg.flags");
10142 + return false;
10143 + }
10144 +
10145 + return true;
10146 +}
10147 +
10148 +static bool nvme_ctrl_pre_save(void *opaque, Error **errp)
10149 +{
10150 + NvmeCtrl *n = opaque;
10151 + int i;
10152 +
10153 + trace_pci_nvme_pre_save_enter(n);
10154 +
10155 + /*
10156 + * We don't want to have a race with nvme_process_sq().
10157 + * What implicitly protects us from this is BQL.
10158 + */
10159 + assert(bql_locked());
10160 +
10161 + /* cancel all SQ processing BHs */
10162 + for (i = 0; i < n->num_queues; i++) {
10163 + NvmeSQueue *sq = n->sq[i];
10164 +
10165 + if (!sq) {
10166 + continue;
10167 + }
10168 +
10169 + qemu_bh_cancel(sq->bh);
10170 + }
10171 +
10172 + /* drain all IO */
10173 + for (i = 1; i <= NVME_MAX_NAMESPACES; i++) {
10174 + NvmeNamespace *ns;
10175 +
10176 + ns = nvme_ns(n, i);
10177 + if (!ns) {
10178 + continue;
10179 + }
10180 +
10181 + trace_pci_nvme_pre_save_ns_drain(n, i);
10182 + nvme_ns_drain(ns);
10183 + }
10184 +
10185 + /*
10186 + * Now, we should take care of AERs.
10187 + *
10188 + * 1. Save all queued events (n->aer_queue).
10189 + * This is done automatically, see nvme_vmstate VMStateDescription.
10190 + * Here we only need to print them for debugging purpose.
10191 + * 2. Go over outstanding AER requests (n->aer_reqs) and check they are
10192 + * all have expected opcode (NVME_ADM_CMD_ASYNC_EV_REQ) and other fields.
10193 + *
10194 + * We must be really careful here, because in case of further
10195 + * QEMU NVMe changes, we may break migration without noticing it, or worse,
10196 + * introduce silent data corruption during migration.
10197 + */
10198 + if (n->aer_queued) {
10199 + NvmeAsyncEvent *event;
10200 +
10201 + QTAILQ_FOREACH(event, &n->aer_queue, entry) {
10202 + trace_pci_nvme_pre_save_aer(event->result.event_type,
10203 + event->result.event_info,
10204 + event->result.log_page);
10205 + }
10206 + }
10207 +
10208 + for (i = 0; i < n->outstanding_aers; i++) {
10209 + NvmeRequest *req = n->aer_reqs[i];
10210 +
10211 + if (!pre_save_validate_aer_req(req, errp)) {
10212 + return false;
10213 + }
10214 + }
10215 +
10216 + /*
10217 + * Make sure that all in-flight IO requests
10218 + * (except NVME_ADM_CMD_ASYNC_EV_REQ) are processed.
10219 + */
10220 + for (i = 0; i < n->num_queues; i++) {
10221 + NvmeRequest *req;
10222 + NvmeSQueue *sq = n->sq[i];
10223 +
10224 + if (!sq) {
10225 + continue;
10226 + }
10227 +
10228 + trace_pci_nvme_pre_save_sq_out_req_check(n, i,
10229 + sq->head, sq->tail, sq->size);
10230 +
10231 + QTAILQ_FOREACH(req, &sq->out_req_list, entry) {
10232 + assert(req->cmd.opcode == NVME_ADM_CMD_ASYNC_EV_REQ);
10233 + }
10234 + }
10235 +
10236 + /* wait when all IO requests completions are written to guest memory */
10237 + for (i = 0; i < n->num_queues; i++) {
10238 + NvmeCQueue *cq = n->cq[i];
10239 +
10240 + if (!cq) {
10241 + continue;
10242 + }
10243 +
10244 + qemu_bh_cancel(cq->bh);
10245 + /* this should empty cq->req_list unless CQ is full */
10246 + nvme_post_cqes(cq);
10247 +
10248 + trace_pci_nvme_pre_save_cq_req_check(n, i,
10249 + cq->head, cq->tail, cq->size);
10250 +
10251 + if (!QTAILQ_EMPTY(&cq->req_list)) {
10252 + NvmeRequest *req;
10253 +
10254 + assert(nvme_cq_full(cq));
10255 +
10256 + QTAILQ_FOREACH(req, &cq->req_list, entry) {
10257 + trace_pci_nvme_pre_save_cq_unposted_cqe(
10258 + n, i, nvme_cid(req),
10259 + nvme_nsid(req->ns),
10260 + le32_to_cpu(req->cqe.result),
10261 + le32_to_cpu(req->cqe.dw1),
10262 + req->status, req->cmd.opcode);
10263 + if (!pre_save_validate_cq_req(req, errp)) {
10264 + return false;
10265 + }
10266 + }
10267 + }
10268 + }
10269 +
10270 + for (uint32_t nsid = 0; nsid <= NVME_MAX_NAMESPACES; nsid++) {
10271 + NvmeNamespace *ns = n->namespaces[nsid];
10272 +
10273 + if (!ns) {
10274 + continue;
10275 + }
10276 +
10277 + if (ns != &n->namespace) {
10278 + error_setg(errp,
10279 + "only one NVMe namespace is supported for migration");
10280 + return false;
10281 + }
10282 + }
10283 +
10284 + return true;
10285 +}
10286 +
10287 +static bool nvme_ctrl_post_load(void *opaque, int version_id, Error **errp)
10288 +{
10289 + NvmeCtrl *n = opaque;
10290 + int i;
10291 +
10292 + trace_pci_nvme_post_load_enter(n);
10293 +
10294 + /* restore CQs first */
10295 + for (i = 0; i < n->num_queues; i++) {
10296 + NvmeCQueue *cq = n->cq[i];
10297 +
10298 + if (!cq) {
10299 + continue;
10300 + }
10301 +
10302 + if (cq->cqid != i) {
10303 + error_setg(errp, "inconsistent migration stream (cq->cqid != i)");
10304 + return false;
10305 + }
10306 +
10307 + cq->ctrl = n;
10308 + nvme_restore_cq(cq);
10309 + trace_pci_nvme_post_load_restore_cq(n, i, cq->head, cq->tail, cq->size);
10310 +
10311 + if (i == 0) {
10312 + /*
10313 + * Admin CQ lives in n->admin_cq, we don't need
10314 + * memory allocated for it in get_ptrs_array_entry() anymore.
10315 + *
10316 + * nvme_restore_cq() also takes care of:
10317 + * n->cq[0] = &n->admin_cq;
10318 + * so n->cq[0] remains valid.
10319 + */
10320 + g_free(cq);
10321 + }
10322 + }
10323 +
10324 + for (i = 0; i < n->num_queues; i++) {
10325 + NvmeSQueue *sq = n->sq[i];
10326 +
10327 + if (!sq) {
10328 + continue;
10329 + }
10330 +
10331 + if (sq->sqid != i) {
10332 + error_setg(errp, "inconsistent migration stream (sq->sqid != i)");
10333 + return false;
10334 + }
10335 +
10336 + if (!n->cq[sq->cqid]) {
10337 + error_setg(errp,
10338 + "inconsistent migration stream (n->cq[sq->cqid] is NULL)");
10339 + return false;
10340 + }
10341 +
10342 + sq->ctrl = n;
10343 + nvme_restore_sq(sq);
10344 + trace_pci_nvme_post_load_restore_sq(n, i, sq->head, sq->tail, sq->size);
10345 +
10346 + if (i == 0) {
10347 + /* same as for CQ */
10348 + g_free(sq);
10349 + }
10350 + }
10351 +
10352 + /* restore cq->req_list-s */
10353 + for (i = 0; i < n->num_queues; i++) {
10354 + NvmeRequest *req_from, *next;
10355 + typeof_field(NvmeCQueue, req_list) req_list;
10356 + NvmeCQueue *cq = n->cq[i];
10357 +
10358 + if (!cq || QTAILQ_EMPTY(&cq->req_list)) {
10359 + continue;
10360 + }
10361 +
10362 + /*
10363 + * We use nvme_vmstate_request VMStateDescription to save/restore
10364 + * NvmeRequest structures, but tricky thing here is that
10365 + * memory for each cq->req_list item is allocated separately
10366 + * during restore. It doesn't work for us. We need to take
10367 + * an existing NvmeRequest structure from SQ's req_list pool
10368 + * and fill it with data from the newly allocated one (req_from).
10369 + * Then, we can safely release allocated memory for it.
10370 + */
10371 +
10372 + /* make a copy of cq->req_list (QTAILQ head) and clean cq->req_list */
10373 + QTAILQ_INIT(&req_list);
10374 + QTAILQ_FOREACH_SAFE(req_from, &cq->req_list, entry, next) {
10375 + QTAILQ_REMOVE(&cq->req_list, req_from, entry);
10376 + QTAILQ_INSERT_TAIL(&req_list, req_from, entry);
10377 + }
10378 + QTAILQ_INIT(&cq->req_list);
10379 +
10380 + QTAILQ_FOREACH_SAFE(req_from, &req_list, entry, next) {
10381 + uint16_t sqid = le16_to_cpu(req_from->cqe.sq_id);
10382 + NvmeRequest *req;
10383 + NvmeSQueue *sq;
10384 +
10385 + assert(!nvme_check_sqid(n, sqid));
10386 + sq = n->sq[sqid];
10387 +
10388 + req = QTAILQ_FIRST(&sq->req_list);
10389 + QTAILQ_REMOVE(&sq->req_list, req, entry);
10390 + QTAILQ_INSERT_TAIL(&cq->req_list, req, entry);
10391 + nvme_req_clear(req);
10392 +
10393 + /* copy data from the source NvmeRequest */
10394 + req->status = req_from->status;
10395 + memcpy(&req->cqe, &req_from->cqe, sizeof(NvmeCqe));
10396 + memcpy(&req->cmd, &req_from->cmd, sizeof(NvmeCmd));
10397 +
10398 + QTAILQ_REMOVE(&req_list, req_from, entry);
10399 + g_free(req_from);
10400 + }
10401 +
10402 + qemu_bh_schedule(cq->bh);
10403 + }
10404 +
10405 + if (n->aer_queued) {
10406 + NvmeAsyncEvent *event;
10407 +
10408 + QTAILQ_FOREACH(event, &n->aer_queue, entry) {
10409 + trace_pci_nvme_post_load_aer(event->result.event_type,
10410 + event->result.event_info,
10411 + event->result.log_page);
10412 + }
10413 + }
10414 +
10415 + for (i = 0; i < n->outstanding_aers; i++) {
10416 + NvmeSQueue *sq = &n->admin_sq;
10417 + NvmeRequest *req_from = n->aer_reqs[i];
10418 + NvmeRequest *req;
10419 +
10420 + /* Idea here is the same as for "restore cq->req_list-s" step */
10421 +
10422 + /* take an NvmeRequest struct from SQ */
10423 + req = QTAILQ_FIRST(&sq->req_list);
10424 + QTAILQ_REMOVE(&sq->req_list, req, entry);
10425 + QTAILQ_INSERT_TAIL(&sq->out_req_list, req, entry);
10426 + nvme_req_clear(req);
10427 +
10428 + /* copy data from the source NvmeRequest */
10429 + req->status = req_from->status;
10430 + memcpy(&req->cqe, &req_from->cqe, sizeof(NvmeCqe));
10431 + memcpy(&req->cmd, &req_from->cmd, sizeof(NvmeCmd));
10432 +
10433 + n->aer_reqs[i] = req;
10434 + g_free(req_from);
10435 + }
10436 +
10437 + /*
10438 + * We need to attach namespaces (currently, only one namespace is
10439 + * supported for migration).
10440 + * This logic comes from nvme_start_ctrl().
10441 + */
10442 + for (i = 1; i <= NVME_MAX_NAMESPACES; i++) {
10443 + NvmeNamespace *ns = nvme_subsys_ns(n->subsys, i);
10444 +
10445 + if (!ns || (!ns->params.shared && ns->ctrl != n)) {
10446 + continue;
10447 + }
10448 +
10449 + if (nvme_csi_supported(n, ns->csi) && !ns->params.detached) {
10450 + if (!ns->attached || ns->params.shared) {
10451 + nvme_attach_ns(n, ns);
10452 + }
10453 + }
10454 + }
10455 +
10456 + /* schedule SQ processing */
10457 + for (i = 0; i < n->num_queues; i++) {
10458 + NvmeSQueue *sq = n->sq[i];
10459 +
10460 + if (!sq) {
10461 + continue;
10462 + }
10463 +
10464 + qemu_bh_schedule(sq->bh);
10465 + }
10466 +
10467 + return true;
10468 +}
10469 +
10470 static const VMStateDescription nvme_vmstate = {
10471 .name = "nvme",
9785 - .unmigratable = 1,
10472 + .minimum_version_id = 1,
10473 + .version_id = 1,
10474 + .pre_save_errp = nvme_ctrl_pre_save,
10475 + .post_load_errp = nvme_ctrl_post_load,
10476 + .fields = (const VMStateField[]) {
10477 + VMSTATE_PCI_DEVICE(parent_obj, NvmeCtrl),
10478 + VMSTATE_MSIX(parent_obj, NvmeCtrl),
10479 + VMSTATE_STRUCT(bar, NvmeCtrl, 0, nvme_vmstate_bar, NvmeBar),
10480 +
10481 + VMSTATE_BOOL(qs_created, NvmeCtrl),
10482 + VMSTATE_UINT32(page_size, NvmeCtrl),
10483 + VMSTATE_UINT16(page_bits, NvmeCtrl),
10484 + VMSTATE_UINT16(max_prp_ents, NvmeCtrl),
10485 + VMSTATE_UINT32(max_q_ents, NvmeCtrl),
10486 + VMSTATE_UINT8(outstanding_aers, NvmeCtrl),
10487 + VMSTATE_UINT32(irq_status, NvmeCtrl),
10488 + VMSTATE_INT32(cq_pending, NvmeCtrl),
10489 +
10490 + VMSTATE_UINT64(host_timestamp, NvmeCtrl),
10491 + VMSTATE_UINT64(timestamp_set_qemu_clock_ms, NvmeCtrl),
10492 + VMSTATE_UINT64(starttime_ms, NvmeCtrl),
10493 + VMSTATE_UINT16(temperature, NvmeCtrl),
10494 + VMSTATE_UINT8(smart_critical_warning, NvmeCtrl),
10495 +
10496 + VMSTATE_UINT32(conf_msix_qsize, NvmeCtrl),
10497 + VMSTATE_UINT32(conf_ioqpairs, NvmeCtrl),
10498 + VMSTATE_UINT64(dbbuf_dbs, NvmeCtrl),
10499 + VMSTATE_UINT64(dbbuf_eis, NvmeCtrl),
10500 + VMSTATE_BOOL(dbbuf_enabled, NvmeCtrl),
10501 +
10502 + VMSTATE_UINT8(aer_mask, NvmeCtrl),
10503 + VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT8_ALLOC(
10504 + aer_reqs, NvmeCtrl, outstanding_aers, 0,
10505 + nvme_vmstate_request, NvmeRequest),
10506 + VMSTATE_QTAILQ_V(aer_queue, NvmeCtrl, 1, nvme_vmstate_async_event,
10507 + NvmeAsyncEvent, entry),
10508 + VMSTATE_INT32(aer_queued, NvmeCtrl),
10509 +
10510 + VMSTATE_STRUCT(namespace, NvmeCtrl, 0, nvme_vmstate_ns, NvmeNamespace),
10511 +
10512 + VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT32_ALLOC(
10513 + sq, NvmeCtrl, num_queues, 0, nvme_vmstate_squeue, NvmeSQueue),
10514 + VMSTATE_VARRAY_OF_POINTER_TO_STRUCT_UINT32_ALLOC(
10515 + cq, NvmeCtrl, num_queues, 0, nvme_vmstate_cqueue, NvmeCQueue),
10516 +
10517 + VMSTATE_UINT16(features.temp_thresh_hi, NvmeCtrl),
10518 + VMSTATE_UINT16(features.temp_thresh_low, NvmeCtrl),
10519 + VMSTATE_UINT32(features.async_config, NvmeCtrl),
10520 + VMSTATE_STRUCT(features.hbs, NvmeCtrl, 0,
10521 + nvme_vmstate_hbs, NvmeHostBehaviorSupport),
10522 +
10523 + VMSTATE_UINT32(dn, NvmeCtrl),
10524 + VMSTATE_STRUCT(atomic, NvmeCtrl, 0, nvme_vmstate_atomic, NvmeAtomic),
10525 +
10526 + VMSTATE_END_OF_LIST()
10527 + },
10528 };
10529
10530 static void nvme_class_init(ObjectClass *oc, const void *data)
hw/nvme/ns.c
+164
@@ -20,6 +20,7 @@
20 #include "qemu/bitops.h"
21 #include "system/system.h"
22 #include "system/block-backend.h"
23 +#include "migration/vmstate.h"
24
25 #include "nvme.h"
26 #include "trace.h"
@@ -886,6 +887,168 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp)
887 }
888 }
889
890 +static const VMStateDescription nvme_vmstate_lbaf = {
891 + .name = "nvme_lbaf",
892 + .version_id = 1,
893 + .minimum_version_id = 1,
894 + .fields = (const VMStateField[]) {
895 + VMSTATE_UINT16(ms, NvmeLBAF),
896 + VMSTATE_UINT8(ds, NvmeLBAF),
897 + VMSTATE_UINT8(rp, NvmeLBAF),
898 + VMSTATE_END_OF_LIST()
899 + }
900 +};
901 +
902 +static const VMStateDescription nvme_vmstate_id_ns = {
903 + .name = "nvme_id_ns",
904 + .version_id = 1,
905 + .minimum_version_id = 1,
906 + .fields = (const VMStateField[]) {
907 + VMSTATE_UINT64(nsze, NvmeIdNs),
908 + VMSTATE_UINT64(ncap, NvmeIdNs),
909 + VMSTATE_UINT64(nuse, NvmeIdNs),
910 + VMSTATE_UINT8(nsfeat, NvmeIdNs),
911 + VMSTATE_UINT8(nlbaf, NvmeIdNs),
912 + VMSTATE_UINT8(flbas, NvmeIdNs),
913 + VMSTATE_UINT8(mc, NvmeIdNs),
914 + VMSTATE_UINT8(dpc, NvmeIdNs),
915 + VMSTATE_UINT8(dps, NvmeIdNs),
916 + VMSTATE_UINT8(nmic, NvmeIdNs),
917 + VMSTATE_UINT8(rescap, NvmeIdNs),
918 + VMSTATE_UINT8(fpi, NvmeIdNs),
919 + VMSTATE_UINT8(dlfeat, NvmeIdNs),
920 + VMSTATE_UINT16(nawun, NvmeIdNs),
921 + VMSTATE_UINT16(nawupf, NvmeIdNs),
922 + VMSTATE_UINT16(nacwu, NvmeIdNs),
923 + VMSTATE_UINT16(nabsn, NvmeIdNs),
924 + VMSTATE_UINT16(nabo, NvmeIdNs),
925 + VMSTATE_UINT16(nabspf, NvmeIdNs),
926 + VMSTATE_UINT16(noiob, NvmeIdNs),
927 + VMSTATE_UINT8_ARRAY(nvmcap, NvmeIdNs, 16),
928 + VMSTATE_UINT16(npwg, NvmeIdNs),
929 + VMSTATE_UINT16(npwa, NvmeIdNs),
930 + VMSTATE_UINT16(npdg, NvmeIdNs),
931 + VMSTATE_UINT16(npda, NvmeIdNs),
932 + VMSTATE_UINT16(nows, NvmeIdNs),
933 + VMSTATE_UINT16(mssrl, NvmeIdNs),
934 + VMSTATE_UINT32(mcl, NvmeIdNs),
935 + VMSTATE_UINT8(msrc, NvmeIdNs),
936 + VMSTATE_UINT8_ARRAY(rsvd81, NvmeIdNs, 18),
937 + VMSTATE_UINT8(nsattr, NvmeIdNs),
938 + VMSTATE_UINT16(nvmsetid, NvmeIdNs),
939 + VMSTATE_UINT16(endgid, NvmeIdNs),
940 + VMSTATE_UINT8_ARRAY(nguid, NvmeIdNs, 16),
941 + VMSTATE_UINT64(eui64, NvmeIdNs),
942 + VMSTATE_STRUCT_ARRAY(lbaf, NvmeIdNs, NVME_MAX_NLBAF, 1,
943 + nvme_vmstate_lbaf, NvmeLBAF),
944 + VMSTATE_UINT8_ARRAY(vs, NvmeIdNs, 3712),
945 +
946 + VMSTATE_END_OF_LIST()
947 + }
948 +};
949 +
950 +static const VMStateDescription nvme_vmstate_id_ns_nvm = {
951 + .name = "nvme_id_ns_nvm",
952 + .version_id = 1,
953 + .minimum_version_id = 1,
954 + .fields = (const VMStateField[]) {
955 + VMSTATE_UINT64(lbstm, NvmeIdNsNvm),
956 + VMSTATE_UINT8(pic, NvmeIdNsNvm),
957 + VMSTATE_UINT8_ARRAY(rsvd9, NvmeIdNsNvm, 3),
958 + VMSTATE_UINT32_ARRAY(elbaf, NvmeIdNsNvm, NVME_MAX_NLBAF),
959 + VMSTATE_UINT32(npdgl, NvmeIdNsNvm),
960 + VMSTATE_UINT32(nprg, NvmeIdNsNvm),
961 + VMSTATE_UINT32(npra, NvmeIdNsNvm),
962 + VMSTATE_UINT32(nors, NvmeIdNsNvm),
963 + VMSTATE_UINT32(npdal, NvmeIdNsNvm),
964 + VMSTATE_UINT8_ARRAY(rsvd288, NvmeIdNsNvm, 3808),
965 + VMSTATE_END_OF_LIST()
966 + }
967 +};
968 +
969 +static const VMStateDescription nvme_vmstate_id_ns_ind = {
970 + .name = "nvme_id_ns_ind",
971 + .version_id = 1,
972 + .minimum_version_id = 1,
973 + .fields = (const VMStateField[]) {
974 + VMSTATE_UINT8(nsfeat, NvmeIdNsInd),
975 + VMSTATE_UINT8(nmic, NvmeIdNsInd),
976 + VMSTATE_UINT8(rescap, NvmeIdNsInd),
977 + VMSTATE_UINT8(fpi, NvmeIdNsInd),
978 + VMSTATE_UINT32(anagrpid, NvmeIdNsInd),
979 + VMSTATE_UINT8(nsattr, NvmeIdNsInd),
980 + VMSTATE_UINT8(rsvd9, NvmeIdNsInd),
981 + VMSTATE_UINT16(nvmsetid, NvmeIdNsInd),
982 + VMSTATE_UINT16(endgrpid, NvmeIdNsInd),
983 + VMSTATE_UINT8(nstat, NvmeIdNsInd),
984 + VMSTATE_UINT8_ARRAY(rsvd15, NvmeIdNsInd, 4081),
985 + VMSTATE_END_OF_LIST()
986 + }
987 +};
988 +
989 +typedef struct TmpNvmeNamespace {
990 + NvmeNamespace *parent;
991 + bool enable_write_cache;
992 +} TmpNvmeNamespace;
993 +
994 +static bool nvme_ns_tmp_pre_save(void *opaque, Error **errp)
995 +{
996 + struct TmpNvmeNamespace *tns = opaque;
997 +
998 + tns->enable_write_cache = blk_enable_write_cache(tns->parent->blkconf.blk);
999 +
1000 + return true;
1001 +}
1002 +
1003 +static bool nvme_ns_tmp_post_load(void *opaque, int version_id, Error **errp)
1004 +{
1005 + struct TmpNvmeNamespace *tns = opaque;
1006 +
1007 + blk_set_enable_write_cache(tns->parent->blkconf.blk,
1008 + tns->enable_write_cache);
1009 +
1010 + return true;
1011 +}
1012 +
1013 +static const VMStateDescription nvme_vmstate_ns_tmp = {
1014 + .name = "nvme_ns_tmp",
1015 + .pre_save_errp = nvme_ns_tmp_pre_save,
1016 + .post_load_errp = nvme_ns_tmp_post_load,
1017 + .fields = (const VMStateField[]) {
1018 + VMSTATE_BOOL(enable_write_cache, TmpNvmeNamespace),
1019 + VMSTATE_END_OF_LIST()
1020 + }
1021 +};
1022 +
1023 +const VMStateDescription nvme_vmstate_ns = {
1024 + .name = "nvme_ns",
1025 + .version_id = 1,
1026 + .minimum_version_id = 1,
1027 + .fields = (const VMStateField[]) {
1028 + VMSTATE_WITH_TMP(NvmeNamespace, TmpNvmeNamespace, nvme_vmstate_ns_tmp),
1029 +
1030 + VMSTATE_STRUCT(id_ns, NvmeNamespace, 0, nvme_vmstate_id_ns, NvmeIdNs),
1031 + VMSTATE_STRUCT(id_ns_nvm, NvmeNamespace, 0,
1032 + nvme_vmstate_id_ns_nvm, NvmeIdNsNvm),
1033 + VMSTATE_STRUCT(id_ns_ind, NvmeNamespace, 0,
1034 + nvme_vmstate_id_ns_ind, NvmeIdNsInd),
1035 + VMSTATE_STRUCT(lbaf, NvmeNamespace, 0, nvme_vmstate_lbaf, NvmeLBAF),
1036 + VMSTATE_UINT32(nlbaf, NvmeNamespace),
1037 + VMSTATE_UINT8(csi, NvmeNamespace),
1038 + VMSTATE_UINT16(status, NvmeNamespace),
1039 + VMSTATE_UINT8(pif, NvmeNamespace),
1040 +
1041 + VMSTATE_UINT16(zns.zrwas, NvmeNamespace),
1042 + VMSTATE_UINT16(zns.zrwafg, NvmeNamespace),
1043 + VMSTATE_UINT32(zns.numzrwa, NvmeNamespace),
1044 +
1045 + VMSTATE_UINT32(features.err_rec, NvmeNamespace),
1046 + VMSTATE_STRUCT(atomic, NvmeNamespace, 0,
1047 + nvme_vmstate_atomic, NvmeAtomic),
1048 + VMSTATE_END_OF_LIST()
1049 + }
1050 +};
1051 +
1052 static const Property nvme_ns_props[] = {
1053 DEFINE_BLOCK_PROPERTIES(NvmeNamespace, blkconf),
1054 DEFINE_PROP_BOOL("detached", NvmeNamespace, params.detached, false),
@@ -937,6 +1100,7 @@ static void nvme_ns_class_init(ObjectClass *oc, const void *data)
1100 dc->bus_type = TYPE_NVME_BUS;
1101 dc->realize = nvme_ns_realize;
1102 dc->unrealize = nvme_ns_unrealize;
1103 + dc->vmsd = &nvme_vmstate_ns;
1104 device_class_set_props(dc, nvme_ns_props);
1105 dc->desc = "Virtual NVMe namespace";
1106 }
hw/nvme/nvme.h
+9
@@ -451,6 +451,11 @@ typedef struct NvmeRequest {
451 NvmeSg sg;
452 bool atomic_write;
453 QTAILQ_ENTRY(NvmeRequest)entry;
454 + /*
455 + * If you add a new field here, please make sure to update
456 + * nvme_vmstate_request, pre_save_validate_aer_req() and
457 + * pre_save_validate_cq_req().
458 + */
459 } NvmeRequest;
460
461 typedef struct NvmeBounceContext {
@@ -647,6 +652,7 @@ typedef struct NvmeCtrl {
652
653 NvmeNamespace namespace;
654 NvmeNamespace *namespaces[NVME_MAX_NAMESPACES + 1];
655 + uint32_t num_queues;
656 NvmeSQueue **sq;
657 NvmeCQueue **cq;
658 NvmeSQueue admin_sq;
@@ -758,4 +764,7 @@ void nvme_atomic_configure_max_write_size(bool dn, uint16_t awun,
764 void nvme_ns_atomic_configure_boundary(bool dn, uint16_t nabsn,
765 uint16_t nabspf, NvmeAtomic *atomic);
766
767 +extern const VMStateDescription nvme_vmstate_atomic;
768 +extern const VMStateDescription nvme_vmstate_ns;
769 +
770 #endif /* HW_NVME_NVME_H */
hw/nvme/trace-events
+10
@@ -7,6 +7,16 @@ pci_nvme_dbbuf_config(uint64_t dbs_addr, uint64_t eis_addr) "dbs_addr=0x%"PRIx64
7 pci_nvme_map_addr(uint64_t addr, uint64_t len) "addr 0x%"PRIx64" len %"PRIu64""
8 pci_nvme_map_addr_cmb(uint64_t addr, uint64_t len) "addr 0x%"PRIx64" len %"PRIu64""
9 pci_nvme_map_prp(uint64_t trans_len, uint32_t len, uint64_t prp1, uint64_t prp2, int num_prps) "trans_len %"PRIu64" len %"PRIu32" prp1 0x%"PRIx64" prp2 0x%"PRIx64" num_prps %d"
10 +pci_nvme_pre_save_enter(void *n) "n=%p"
11 +pci_nvme_pre_save_ns_drain(void *n, int i) "n=%p i=%d"
12 +pci_nvme_pre_save_sq_out_req_check(void *n, int i, uint32_t head, uint32_t tail, uint32_t size) "n=%p i=%d head=0x%"PRIx32" tail=0x%"PRIx32" size=0x%"PRIx32""
13 +pci_nvme_pre_save_cq_req_check(void *n, int i, uint32_t head, uint32_t tail, uint32_t size) "n=%p i=%d head=0x%"PRIx32" tail=0x%"PRIx32" size=0x%"PRIx32""
14 +pci_nvme_pre_save_cq_unposted_cqe(void *n, int i, uint16_t cid, uint32_t nsid, uint32_t dw0, uint32_t dw1, uint16_t status, uint8_t opc) "n=%p i=%d cid %"PRIu16" nsid %"PRIu32" dw0 0x%"PRIx32" dw1 0x%"PRIx32" status 0x%"PRIx16" opc 0x%"PRIx8""
15 +pci_nvme_pre_save_aer(uint8_t typ, uint8_t info, uint8_t log_page) "type 0x%"PRIx8" info 0x%"PRIx8" lid 0x%"PRIx8""
16 +pci_nvme_post_load_enter(void *n) "n=%p"
17 +pci_nvme_post_load_restore_cq(void *n, int i, uint32_t head, uint32_t tail, uint32_t size) "n=%p i=%d head=0x%"PRIx32" tail=0x%"PRIx32" size=0x%"PRIx32""
18 +pci_nvme_post_load_restore_sq(void *n, int i, uint32_t head, uint32_t tail, uint32_t size) "n=%p i=%d head=0x%"PRIx32" tail=0x%"PRIx32" size=0x%"PRIx32""
19 +pci_nvme_post_load_aer(uint8_t typ, uint8_t info, uint8_t log_page) "type 0x%"PRIx8" info 0x%"PRIx8" lid 0x%"PRIx8""
20 pci_nvme_map_sgl(uint8_t typ, uint64_t len) "type 0x%"PRIx8" len %"PRIu64""
21 pci_nvme_io_cmd(uint16_t cid, uint32_t nsid, uint16_t sqid, uint8_t opcode, const char *opname) "cid %"PRIu16" nsid 0x%"PRIx32" sqid %"PRIu16" opc 0x%"PRIx8" opname '%s'"
22 pci_nvme_admin_cmd(uint16_t cid, uint16_t sqid, uint8_t opcode, const char *opname) "cid %"PRIu16" sqid %"PRIu16" opc 0x%"PRIx8" opname '%s'"