computing-offload/generic_vdpa/qemu/vhost-implement-vhost_vdpa_device_suspend-resume.patch
jiangdongxu 79c4324644 add generic_vdpa basecode
Change-Id: I2d302dda68298877c65c99147f5bf22186a59aac
2024-09-19 17:19:46 +08:00

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