mirror of https://gitlab.com/qemu-project/qemu
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
226 lines
5.8 KiB
C
226 lines
5.8 KiB
C
/*
|
|
* Copyright (c) 2024-2025 Oracle and/or its affiliates.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/error-report.h"
|
|
#include "qapi/error.h"
|
|
#include "hw/vfio/vfio-cpr.h"
|
|
#include "hw/vfio/vfio-device.h"
|
|
#include "migration/blocker.h"
|
|
#include "migration/cpr.h"
|
|
#include "migration/migration.h"
|
|
#include "migration/vmstate.h"
|
|
#include "system/iommufd.h"
|
|
#include "vfio-iommufd.h"
|
|
#include "trace.h"
|
|
|
|
typedef struct CprVFIODevice {
|
|
char *name;
|
|
unsigned int namelen;
|
|
uint32_t ioas_id;
|
|
int devid;
|
|
uint32_t hwpt_id;
|
|
QLIST_ENTRY(CprVFIODevice) next;
|
|
} CprVFIODevice;
|
|
|
|
static const VMStateDescription vmstate_cpr_vfio_device = {
|
|
.name = "cpr vfio device",
|
|
.version_id = 1,
|
|
.minimum_version_id = 1,
|
|
.fields = (VMStateField[]) {
|
|
VMSTATE_UINT32(namelen, CprVFIODevice),
|
|
VMSTATE_VBUFFER_ALLOC_UINT32(name, CprVFIODevice, 0, NULL, namelen),
|
|
VMSTATE_INT32(devid, CprVFIODevice),
|
|
VMSTATE_UINT32(ioas_id, CprVFIODevice),
|
|
VMSTATE_UINT32(hwpt_id, CprVFIODevice),
|
|
VMSTATE_END_OF_LIST()
|
|
}
|
|
};
|
|
|
|
const VMStateDescription vmstate_cpr_vfio_devices = {
|
|
.name = CPR_STATE "/vfio devices",
|
|
.version_id = 1,
|
|
.minimum_version_id = 1,
|
|
.fields = (const VMStateField[]){
|
|
VMSTATE_QLIST_V(vfio_devices, CprState, 1, vmstate_cpr_vfio_device,
|
|
CprVFIODevice, next),
|
|
VMSTATE_END_OF_LIST()
|
|
}
|
|
};
|
|
|
|
static void vfio_cpr_save_device(VFIODevice *vbasedev)
|
|
{
|
|
CprVFIODevice *elem = g_new0(CprVFIODevice, 1);
|
|
|
|
elem->name = g_strdup(vbasedev->name);
|
|
elem->namelen = strlen(vbasedev->name) + 1;
|
|
elem->ioas_id = vbasedev->cpr.ioas_id;
|
|
elem->devid = vbasedev->devid;
|
|
elem->hwpt_id = vbasedev->cpr.hwpt_id;
|
|
QLIST_INSERT_HEAD(&cpr_state.vfio_devices, elem, next);
|
|
}
|
|
|
|
static CprVFIODevice *find_device(const char *name)
|
|
{
|
|
CprVFIODeviceList *head = &cpr_state.vfio_devices;
|
|
CprVFIODevice *elem;
|
|
|
|
QLIST_FOREACH(elem, head, next) {
|
|
if (!strcmp(elem->name, name)) {
|
|
return elem;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void vfio_cpr_delete_device(const char *name)
|
|
{
|
|
CprVFIODevice *elem = find_device(name);
|
|
|
|
if (elem) {
|
|
QLIST_REMOVE(elem, next);
|
|
g_free(elem->name);
|
|
g_free(elem);
|
|
}
|
|
}
|
|
|
|
static bool vfio_cpr_find_device(VFIODevice *vbasedev)
|
|
{
|
|
CprVFIODevice *elem = find_device(vbasedev->name);
|
|
|
|
if (elem) {
|
|
vbasedev->cpr.ioas_id = elem->ioas_id;
|
|
vbasedev->devid = elem->devid;
|
|
vbasedev->cpr.hwpt_id = elem->hwpt_id;
|
|
trace_vfio_cpr_find_device(elem->ioas_id, elem->devid, elem->hwpt_id);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool vfio_cpr_supported(IOMMUFDBackend *be, Error **errp)
|
|
{
|
|
if (!iommufd_change_process_capable(be)) {
|
|
if (errp) {
|
|
error_setg(errp, "vfio iommufd backend does not support "
|
|
"IOMMU_IOAS_CHANGE_PROCESS");
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static int iommufd_cpr_pre_save(void *opaque)
|
|
{
|
|
IOMMUFDBackend *be = opaque;
|
|
|
|
/*
|
|
* The process has not changed yet, but proactively try the ioctl,
|
|
* and it will fail if any DMA mappings are not supported.
|
|
*/
|
|
if (!iommufd_change_process_capable(be)) {
|
|
error_report("some memory regions do not support "
|
|
"IOMMU_IOAS_CHANGE_PROCESS");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int iommufd_cpr_post_load(void *opaque, int version_id)
|
|
{
|
|
IOMMUFDBackend *be = opaque;
|
|
Error *local_err = NULL;
|
|
|
|
if (!iommufd_change_process(be, &local_err)) {
|
|
error_report_err(local_err);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const VMStateDescription iommufd_cpr_vmstate = {
|
|
.name = "iommufd",
|
|
.version_id = 0,
|
|
.minimum_version_id = 0,
|
|
.pre_save = iommufd_cpr_pre_save,
|
|
.post_load = iommufd_cpr_post_load,
|
|
.needed = cpr_incoming_needed,
|
|
.fields = (VMStateField[]) {
|
|
VMSTATE_END_OF_LIST()
|
|
}
|
|
};
|
|
|
|
bool vfio_iommufd_cpr_register_iommufd(IOMMUFDBackend *be, Error **errp)
|
|
{
|
|
Error **cpr_blocker = &be->cpr_blocker;
|
|
|
|
if (!vfio_cpr_supported(be, cpr_blocker)) {
|
|
return migrate_add_blocker_modes(cpr_blocker, errp,
|
|
MIG_MODE_CPR_TRANSFER, -1) == 0;
|
|
}
|
|
|
|
vmstate_register(NULL, -1, &iommufd_cpr_vmstate, be);
|
|
|
|
return true;
|
|
}
|
|
|
|
void vfio_iommufd_cpr_unregister_iommufd(IOMMUFDBackend *be)
|
|
{
|
|
vmstate_unregister(NULL, &iommufd_cpr_vmstate, be);
|
|
migrate_del_blocker(&be->cpr_blocker);
|
|
}
|
|
|
|
bool vfio_iommufd_cpr_register_container(VFIOIOMMUFDContainer *container,
|
|
Error **errp)
|
|
{
|
|
VFIOContainerBase *bcontainer = &container->bcontainer;
|
|
|
|
migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier,
|
|
vfio_cpr_reboot_notifier,
|
|
MIG_MODE_CPR_REBOOT);
|
|
|
|
vfio_cpr_add_kvm_notifier();
|
|
|
|
return true;
|
|
}
|
|
|
|
void vfio_iommufd_cpr_unregister_container(VFIOIOMMUFDContainer *container)
|
|
{
|
|
VFIOContainerBase *bcontainer = &container->bcontainer;
|
|
|
|
migration_remove_notifier(&bcontainer->cpr_reboot_notifier);
|
|
}
|
|
|
|
void vfio_iommufd_cpr_register_device(VFIODevice *vbasedev)
|
|
{
|
|
if (!cpr_is_incoming()) {
|
|
/*
|
|
* Beware fd may have already been saved by vfio_device_set_fd,
|
|
* so call resave to avoid a duplicate entry.
|
|
*/
|
|
cpr_resave_fd(vbasedev->name, 0, vbasedev->fd);
|
|
vfio_cpr_save_device(vbasedev);
|
|
}
|
|
}
|
|
|
|
void vfio_iommufd_cpr_unregister_device(VFIODevice *vbasedev)
|
|
{
|
|
cpr_delete_fd(vbasedev->name, 0);
|
|
vfio_cpr_delete_device(vbasedev->name);
|
|
}
|
|
|
|
void vfio_cpr_load_device(VFIODevice *vbasedev)
|
|
{
|
|
if (cpr_is_incoming()) {
|
|
bool ret = vfio_cpr_find_device(vbasedev);
|
|
g_assert(ret);
|
|
|
|
if (vbasedev->fd < 0) {
|
|
vbasedev->fd = cpr_find_fd(vbasedev->name, 0);
|
|
}
|
|
}
|
|
}
|