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'"