From a21603f7ecfaa2fb53b2037f46ee3fb868d8c9cb Mon Sep 17 00:00:00 2001 From: fangyi Date: Mon, 4 Dec 2023 15:27:34 +0800 Subject: [PATCH] vhost: implement vhost_vdpa_device_suspend/resume Signed-off-by: jiangdongxu Signed-off-by: fangyi --- 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 . + */ + +#include +#include +#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