79c4324644
Change-Id: I2d302dda68298877c65c99147f5bf22186a59aac
454 lines
14 KiB
Diff
454 lines
14 KiB
Diff
From a21603f7ecfaa2fb53b2037f46ee3fb868d8c9cb Mon Sep 17 00:00:00 2001
|
|
From: fangyi <eric.fangyi@huawei.com>
|
|
Date: Mon, 4 Dec 2023 15:27:34 +0800
|
|
Subject: [PATCH] vhost: implement vhost_vdpa_device_suspend/resume
|
|
|
|
Signed-off-by: jiangdongxu <jiangdongxu1@huawei.com>
|
|
Signed-off-by: fangyi <eric.fangyi@huawei.com>
|
|
---
|
|
hw/virtio/meson.build | 2 +-
|
|
hw/virtio/vdpa-dev-mig.c | 186 +++++++++++++++++++++++++++++++
|
|
hw/virtio/vhost.c | 138 +++++++++++++++++++++++
|
|
include/hw/virtio/vdpa-dev-mig.h | 16 +++
|
|
include/hw/virtio/vdpa-dev.h | 1 +
|
|
include/hw/virtio/vhost.h | 4 +
|
|
migration/migration.c | 3 +-
|
|
migration/migration.h | 2 +
|
|
8 files changed, 349 insertions(+), 3 deletions(-)
|
|
create mode 100644 hw/virtio/vdpa-dev-mig.c
|
|
create mode 100644 include/hw/virtio/vdpa-dev-mig.h
|
|
|
|
diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build
|
|
index c2da69616f..94a030f329 100644
|
|
--- a/hw/virtio/meson.build
|
|
+++ b/hw/virtio/meson.build
|
|
@@ -29,7 +29,7 @@ virtio_ss.add(when: 'CONFIG_VHOST_USER_I2C', if_true: files('vhost-user-i2c.c'))
|
|
virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_I2C'], if_true: files('vhost-user-i2c-pci.c'))
|
|
virtio_ss.add(when: 'CONFIG_VHOST_USER_RNG', if_true: files('vhost-user-rng.c'))
|
|
virtio_ss.add(when: ['CONFIG_VHOST_USER_RNG', 'CONFIG_VIRTIO_PCI'], if_true: files('vhost-user-rng-pci.c'))
|
|
-virtio_ss.add(when: 'CONFIG_VHOST_VDPA_DEV', if_true: files('vdpa-dev.c'))
|
|
+virtio_ss.add(when: 'CONFIG_VHOST_VDPA_DEV', if_true: files('vdpa-dev.c', 'vdpa-dev-mig.c'))
|
|
|
|
virtio_pci_ss = ss.source_set()
|
|
virtio_pci_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-pci.c'))
|
|
diff --git a/hw/virtio/vdpa-dev-mig.c b/hw/virtio/vdpa-dev-mig.c
|
|
new file mode 100644
|
|
index 0000000000..64c9e245d1
|
|
--- /dev/null
|
|
+++ b/hw/virtio/vdpa-dev-mig.c
|
|
@@ -0,0 +1,186 @@
|
|
+/*
|
|
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+
|
|
+ * You should have received a copy of the GNU General Public License along
|
|
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
+ */
|
|
+
|
|
+#include <sys/ioctl.h>
|
|
+#include <linux/vhost.h>
|
|
+#include "qemu/osdep.h"
|
|
+#include "migration/misc.h"
|
|
+#include "hw/qdev-core.h"
|
|
+#include "hw/qdev-properties.h"
|
|
+#include "hw/virtio/vhost.h"
|
|
+#include "hw/virtio/vdpa-dev.h"
|
|
+#include "hw/virtio/virtio.h"
|
|
+#include "hw/virtio/virtio-bus.h"
|
|
+#include "hw/virtio/virtio-access.h"
|
|
+#include "migration/register.h"
|
|
+#include "migration/migration.h"
|
|
+#include "qemu-common.h"
|
|
+#include "sysemu/runstate.h"
|
|
+#include "qemu/error-report.h"
|
|
+#include "hw/virtio/vdpa-dev-mig.h"
|
|
+
|
|
+static int vhost_vdpa_call(struct vhost_dev *dev, unsigned long int request,
|
|
+ void *arg)
|
|
+{
|
|
+ struct vhost_vdpa *v = dev->opaque;
|
|
+ int fd = v->device_fd;
|
|
+
|
|
+ if (dev->vhost_ops->backend_type != VHOST_BACKEND_TYPE_VDPA) {
|
|
+ error_report("backend type isn't VDPA. Operation not permitted!\n");
|
|
+ return -EPERM;
|
|
+ }
|
|
+
|
|
+ return ioctl(fd, request, arg);
|
|
+}
|
|
+
|
|
+static int vhost_vdpa_device_suspend(VhostVdpaDevice *vdpa)
|
|
+{
|
|
+ VirtIODevice *vdev = VIRTIO_DEVICE(vdpa);
|
|
+ BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
|
|
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
|
+ int ret;
|
|
+
|
|
+ if (!vdpa->started) {
|
|
+ return -EFAULT;
|
|
+ }
|
|
+
|
|
+ if (!k->set_guest_notifiers) {
|
|
+ return -EFAULT;
|
|
+ }
|
|
+
|
|
+ vdpa->started = false;
|
|
+
|
|
+ ret = vhost_dev_suspend(&vdpa->dev, vdev, false);
|
|
+ if (ret) {
|
|
+ goto suspend_fail;
|
|
+ }
|
|
+
|
|
+ ret = k->set_guest_notifiers(qbus->parent, vdpa->dev.nvqs, false);
|
|
+ if (ret < 0) {
|
|
+ error_report("vhost guest notifier cleanup failed: %d\n", ret);
|
|
+ goto set_guest_notifiers_fail;
|
|
+ }
|
|
+
|
|
+ vhost_dev_disable_notifiers(&vdpa->dev, vdev);
|
|
+ return ret;
|
|
+
|
|
+set_guest_notifiers_fail:
|
|
+ ret = k->set_guest_notifiers(qbus->parent, vdpa->dev.nvqs, true);
|
|
+ if (ret) {
|
|
+ error_report("vhost guest notifier restore failed: %d\n", ret);
|
|
+ }
|
|
+
|
|
+suspend_fail:
|
|
+ vdpa->started = true;
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int vhost_vdpa_device_resume(VhostVdpaDevice *vdpa)
|
|
+{
|
|
+ VirtIODevice *vdev = VIRTIO_DEVICE(vdpa);
|
|
+ BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
|
|
+ VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
|
|
+ int i, ret;
|
|
+
|
|
+ if (!k->set_guest_notifiers) {
|
|
+ error_report("binding does not support guest notifiers\n");
|
|
+ return -ENOSYS;
|
|
+ }
|
|
+
|
|
+ ret = vhost_dev_enable_notifiers(&vdpa->dev, vdev);
|
|
+ if (ret < 0) {
|
|
+ error_report("Error enabling host notifiers: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = k->set_guest_notifiers(qbus->parent, vdpa->dev.nvqs, true);
|
|
+ if (ret < 0) {
|
|
+ error_report("Error binding guest notifier: %d\n", ret);
|
|
+ goto err_host_notifiers;
|
|
+ }
|
|
+
|
|
+ vdpa->dev.acked_features = vdev->guest_features;
|
|
+
|
|
+ ret = vhost_dev_resume(&vdpa->dev, vdev, false);
|
|
+ if (ret < 0) {
|
|
+ error_report("Error starting vhost: %d\n", ret);
|
|
+ goto err_guest_notifiers;
|
|
+ }
|
|
+ vdpa->started = true;
|
|
+
|
|
+ /*
|
|
+ * guest_notifier_mask/pending not used yet, so just unmask
|
|
+ * everything here. virtio-pci will do the right thing by
|
|
+ * enabling/disabling irqfd.
|
|
+ */
|
|
+ for (i = 0; i < vdpa->dev.nvqs; i++) {
|
|
+ vhost_virtqueue_mask(&vdpa->dev, vdev, i, false);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+
|
|
+err_guest_notifiers:
|
|
+ k->set_guest_notifiers(qbus->parent, vdpa->dev.nvqs, false);
|
|
+err_host_notifiers:
|
|
+ vhost_dev_disable_notifiers(&vdpa->dev, vdev);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void vdpa_dev_vmstate_change(void *opaque, bool running, RunState state)
|
|
+{
|
|
+ VhostVdpaDevice *vdpa = VHOST_VDPA_DEVICE(opaque);
|
|
+ struct vhost_dev *hdev = &vdpa->dev;
|
|
+ int ret;
|
|
+ MigrationState *ms = migrate_get_current();
|
|
+ MigrationIncomingState *mis = migration_incoming_get_current();
|
|
+
|
|
+ if (!running) {
|
|
+ if (ms->state == RUN_STATE_PAUSED) {
|
|
+ ret = vhost_vdpa_device_suspend(vdpa);
|
|
+ if (ret) {
|
|
+ error_report("suspend vdpa device failed: %d\n", ret);
|
|
+ if (ms->migration_thread_running) {
|
|
+ migrate_fd_cancel(ms);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ if (ms->state == RUN_STATE_RESTORE_VM) {
|
|
+ ret = vhost_vdpa_device_resume(vdpa);
|
|
+ if (ret) {
|
|
+ error_report("migration dest resume device failed, abort!\n");
|
|
+ exit(EXIT_FAILURE);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (mis->state == RUN_STATE_RESTORE_VM) {
|
|
+ vhost_vdpa_call(hdev, VHOST_VDPA_RESUME, NULL);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void vdpa_migration_register(VhostVdpaDevice *vdev)
|
|
+{
|
|
+ vdev->vmstate = qdev_add_vm_change_state_handler(DEVICE(vdev),
|
|
+ vdpa_dev_vmstate_change,
|
|
+ DEVICE(vdev));
|
|
+}
|
|
+
|
|
+void vdpa_migration_unregister(VhostVdpaDevice *vdev)
|
|
+{
|
|
+ qemu_del_vm_change_state_handler(vdev->vmstate);
|
|
+}
|
|
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
|
|
index d2b9278474..ed1506d3e0 100644
|
|
--- a/hw/virtio/vhost.c
|
|
+++ b/hw/virtio/vhost.c
|
|
@@ -2201,3 +2201,141 @@ bool used_memslots_is_exceeded(void)
|
|
{
|
|
return used_memslots_exceeded;
|
|
}
|
|
+
|
|
+int vhost_dev_resume(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings)
|
|
+{
|
|
+ int i, r;
|
|
+ EventNotifier *e = &hdev->vqs[VHOST_QUEUE_NUM_CONFIG_INR].masked_config_notifier;
|
|
+
|
|
+ /* should only be called after backend is connected */
|
|
+ if (!hdev->vhost_ops) {
|
|
+ error_report("Missing vhost_ops! Operation not permitted!\n");
|
|
+ return -EPERM;
|
|
+ }
|
|
+
|
|
+ vdev->vhost_started = true;
|
|
+ hdev->started = true;
|
|
+ hdev->vdev = vdev;
|
|
+
|
|
+ if (vhost_dev_has_iommu(hdev)) {
|
|
+ memory_listener_register(&hdev->iommu_listener, vdev->dma_as);
|
|
+ }
|
|
+
|
|
+ r = hdev->vhost_ops->vhost_set_mem_table(hdev, hdev->mem);
|
|
+ if (r < 0) {
|
|
+ VHOST_OPS_DEBUG(r, "vhost_set_mem_table failed");
|
|
+ goto fail_mem;
|
|
+ }
|
|
+ for (i = 0; i < hdev->nvqs; ++i) {
|
|
+ r = vhost_virtqueue_start(hdev,
|
|
+ vdev,
|
|
+ hdev->vqs + i,
|
|
+ hdev->vq_index + i);
|
|
+ if (r < 0) {
|
|
+ goto fail_vq;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ r = event_notifier_init(e, 0);
|
|
+ if (r < 0) {
|
|
+ return r;
|
|
+ }
|
|
+ event_notifier_test_and_clear(e);
|
|
+ if (!vdev->use_guest_notifier_mask) {
|
|
+ vhost_config_mask(hdev, vdev, true);
|
|
+ }
|
|
+ if (vrings) {
|
|
+ r = vhost_dev_set_vring_enable(hdev, true);
|
|
+ if (r) {
|
|
+ goto fail_vq;
|
|
+ }
|
|
+ }
|
|
+ if (hdev->vhost_ops->vhost_dev_resume) {
|
|
+ r = hdev->vhost_ops->vhost_dev_resume(hdev);
|
|
+ if (r) {
|
|
+ goto fail_start;
|
|
+ }
|
|
+ }
|
|
+ if (vhost_dev_has_iommu(hdev)) {
|
|
+ hdev->vhost_ops->vhost_set_iotlb_callback(hdev, true);
|
|
+
|
|
+ /*
|
|
+ * Update used ring information for IOTLB to work correctly,
|
|
+ * vhost-kernel code requires for this.
|
|
+ */
|
|
+ for (i = 0; i < hdev->nvqs; ++i) {
|
|
+ struct vhost_virtqueue *vq = hdev->vqs + i;
|
|
+ vhost_device_iotlb_miss(hdev, vq->used_phys, true);
|
|
+ }
|
|
+ }
|
|
+ vhost_start_config_intr(hdev);
|
|
+ return 0;
|
|
+fail_start:
|
|
+ if (vrings) {
|
|
+ vhost_dev_set_vring_enable(hdev, false);
|
|
+ }
|
|
+fail_vq:
|
|
+ while (--i >= 0) {
|
|
+ vhost_virtqueue_stop(hdev,
|
|
+ vdev,
|
|
+ hdev->vqs + i,
|
|
+ hdev->vq_index + i);
|
|
+ }
|
|
+
|
|
+fail_mem:
|
|
+ vdev->vhost_started = false;
|
|
+ hdev->started = false;
|
|
+ return r;
|
|
+}
|
|
+
|
|
+int vhost_dev_suspend(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings)
|
|
+{
|
|
+ int i;
|
|
+ int ret = 0;
|
|
+ EventNotifier *e = &hdev->vqs[VHOST_QUEUE_NUM_CONFIG_INR].masked_config_notifier;
|
|
+
|
|
+ /* should only be called after backend is connected */
|
|
+ if (!hdev->vhost_ops) {
|
|
+ error_report("Missing vhost_ops! Operation not permitted!\n");
|
|
+ return -EPERM;
|
|
+ }
|
|
+
|
|
+ event_notifier_test_and_clear(e);
|
|
+ event_notifier_test_and_clear(&vdev->config_notifier);
|
|
+
|
|
+ if (hdev->vhost_ops->vhost_dev_suspend) {
|
|
+ ret = hdev->vhost_ops->vhost_dev_suspend(hdev);
|
|
+ if (ret) {
|
|
+ goto fail_suspend;
|
|
+ }
|
|
+ }
|
|
+ if (vrings) {
|
|
+ ret = vhost_dev_set_vring_enable(hdev, false);
|
|
+ if (ret) {
|
|
+ goto fail_suspend;
|
|
+ }
|
|
+ }
|
|
+ for (i = 0; i < hdev->nvqs; ++i) {
|
|
+ vhost_virtqueue_stop(hdev,
|
|
+ vdev,
|
|
+ hdev->vqs + i,
|
|
+ hdev->vq_index + i);
|
|
+ }
|
|
+
|
|
+ if (vhost_dev_has_iommu(hdev)) {
|
|
+ hdev->vhost_ops->vhost_set_iotlb_callback(hdev, false);
|
|
+ memory_listener_unregister(&hdev->iommu_listener);
|
|
+ }
|
|
+ vhost_stop_config_intr(hdev);
|
|
+ vhost_log_put(hdev, true);
|
|
+ hdev->started = false;
|
|
+ vdev->vhost_started = false;
|
|
+ hdev->vdev = NULL;
|
|
+
|
|
+ return ret;
|
|
+
|
|
+fail_suspend:
|
|
+ event_notifier_test_and_clear(e);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
diff --git a/include/hw/virtio/vdpa-dev-mig.h b/include/hw/virtio/vdpa-dev-mig.h
|
|
new file mode 100644
|
|
index 0000000000..89665ca747
|
|
--- /dev/null
|
|
+++ b/include/hw/virtio/vdpa-dev-mig.h
|
|
@@ -0,0 +1,16 @@
|
|
+/*
|
|
+ * Vhost Vdpa Device Migration Header
|
|
+ *
|
|
+ * Copyright (c) Huawei Technologies Co., Ltd. 2023. All Rights Reserved.
|
|
+ */
|
|
+
|
|
+#ifndef _VHOST_VDPA_MIGRATION_H
|
|
+#define _VHOST_VDPA_MIGRATION_H
|
|
+
|
|
+#include "hw/virtio/vdpa-dev.h"
|
|
+
|
|
+void vdpa_migration_register(VhostVdpaDevice *vdev);
|
|
+
|
|
+void vdpa_migration_unregister(VhostVdpaDevice *vdev);
|
|
+
|
|
+#endif /* _VHOST_VDPA_MIGRATION_H */
|
|
diff --git a/include/hw/virtio/vdpa-dev.h b/include/hw/virtio/vdpa-dev.h
|
|
index 4dbf98195c..43cbcef81b 100644
|
|
--- a/include/hw/virtio/vdpa-dev.h
|
|
+++ b/include/hw/virtio/vdpa-dev.h
|
|
@@ -38,6 +38,7 @@ struct VhostVdpaDevice {
|
|
uint16_t queue_size;
|
|
bool started;
|
|
int (*post_init)(VhostVdpaDevice *v, Error **errp);
|
|
+ VMChangeStateEntry *vmstate;
|
|
};
|
|
|
|
#endif
|
|
diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h
|
|
index 0491fe1ed7..9441b4c50e 100644
|
|
--- a/include/hw/virtio/vhost.h
|
|
+++ b/include/hw/virtio/vhost.h
|
|
@@ -277,4 +277,8 @@ int vhost_dev_set_inflight(struct vhost_dev *dev,
|
|
int vhost_dev_get_inflight(struct vhost_dev *dev, uint16_t queue_size,
|
|
struct vhost_inflight *inflight);
|
|
bool used_memslots_is_exceeded(void);
|
|
+
|
|
+int vhost_dev_resume(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings);
|
|
+int vhost_dev_suspend(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings);
|
|
+
|
|
#endif
|
|
diff --git a/migration/migration.c b/migration/migration.c
|
|
index 2ec116f901..40e743b4e9 100644
|
|
--- a/migration/migration.c
|
|
+++ b/migration/migration.c
|
|
@@ -178,7 +178,6 @@ static bool migration_object_check(MigrationState *ms, Error **errp);
|
|
static int migration_maybe_pause(MigrationState *s,
|
|
int *current_active_state,
|
|
int new_state);
|
|
-static void migrate_fd_cancel(MigrationState *s);
|
|
|
|
static gint page_request_addr_cmp(gconstpointer ap, gconstpointer bp)
|
|
{
|
|
@@ -1914,7 +1913,7 @@ void migrate_fd_error(MigrationState *s, const Error *error)
|
|
migrate_set_error(s, error);
|
|
}
|
|
|
|
-static void migrate_fd_cancel(MigrationState *s)
|
|
+void migrate_fd_cancel(MigrationState *s)
|
|
{
|
|
int old_state ;
|
|
QEMUFile *f = migrate_get_current()->to_dst_file;
|
|
diff --git a/migration/migration.h b/migration/migration.h
|
|
index 4ed4f555da..a87fd54d10 100644
|
|
--- a/migration/migration.h
|
|
+++ b/migration/migration.h
|
|
@@ -393,4 +393,6 @@ void migration_cancel(const Error *error);
|
|
|
|
void populate_vfio_info(MigrationInfo *info);
|
|
|
|
+void migrate_fd_cancel(MigrationState *s);
|
|
+
|
|
#endif
|
|
--
|
|
2.27.0
|
|
|