xref: /openbmc/qemu/hw/vfio/cpr-legacy.c (revision 1faadd96306b428f2e8e35f71761c333a6777f55)
154857b08SSteve Sistare /*
254857b08SSteve Sistare  * Copyright (c) 2021-2025 Oracle and/or its affiliates.
354857b08SSteve Sistare  *
454857b08SSteve Sistare  * SPDX-License-Identifier: GPL-2.0-or-later
554857b08SSteve Sistare  */
654857b08SSteve Sistare 
754857b08SSteve Sistare #include <sys/ioctl.h>
854857b08SSteve Sistare #include <linux/vfio.h>
954857b08SSteve Sistare #include "qemu/osdep.h"
1054857b08SSteve Sistare #include "hw/vfio/vfio-container.h"
11c29a65edSSteve Sistare #include "hw/vfio/vfio-device.h"
1254857b08SSteve Sistare #include "migration/blocker.h"
1354857b08SSteve Sistare #include "migration/cpr.h"
1454857b08SSteve Sistare #include "migration/migration.h"
1554857b08SSteve Sistare #include "migration/vmstate.h"
1654857b08SSteve Sistare #include "qapi/error.h"
1754857b08SSteve Sistare 
18*1faadd96SSteve Sistare static bool vfio_dma_unmap_vaddr_all(VFIOContainer *container, Error **errp)
19*1faadd96SSteve Sistare {
20*1faadd96SSteve Sistare     struct vfio_iommu_type1_dma_unmap unmap = {
21*1faadd96SSteve Sistare         .argsz = sizeof(unmap),
22*1faadd96SSteve Sistare         .flags = VFIO_DMA_UNMAP_FLAG_VADDR | VFIO_DMA_UNMAP_FLAG_ALL,
23*1faadd96SSteve Sistare         .iova = 0,
24*1faadd96SSteve Sistare         .size = 0,
25*1faadd96SSteve Sistare     };
26*1faadd96SSteve Sistare     if (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) {
27*1faadd96SSteve Sistare         error_setg_errno(errp, errno, "vfio_dma_unmap_vaddr_all");
28*1faadd96SSteve Sistare         return false;
29*1faadd96SSteve Sistare     }
30*1faadd96SSteve Sistare     return true;
31*1faadd96SSteve Sistare }
32*1faadd96SSteve Sistare 
33*1faadd96SSteve Sistare 
3454857b08SSteve Sistare static bool vfio_cpr_supported(VFIOContainer *container, Error **errp)
3554857b08SSteve Sistare {
3654857b08SSteve Sistare     if (!ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UPDATE_VADDR)) {
3754857b08SSteve Sistare         error_setg(errp, "VFIO container does not support VFIO_UPDATE_VADDR");
3854857b08SSteve Sistare         return false;
3954857b08SSteve Sistare 
4054857b08SSteve Sistare     } else if (!ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UNMAP_ALL)) {
4154857b08SSteve Sistare         error_setg(errp, "VFIO container does not support VFIO_UNMAP_ALL");
4254857b08SSteve Sistare         return false;
4354857b08SSteve Sistare 
4454857b08SSteve Sistare     } else {
4554857b08SSteve Sistare         return true;
4654857b08SSteve Sistare     }
4754857b08SSteve Sistare }
4854857b08SSteve Sistare 
49*1faadd96SSteve Sistare static int vfio_container_pre_save(void *opaque)
50*1faadd96SSteve Sistare {
51*1faadd96SSteve Sistare     VFIOContainer *container = opaque;
52*1faadd96SSteve Sistare     Error *local_err = NULL;
53*1faadd96SSteve Sistare 
54*1faadd96SSteve Sistare     if (!vfio_dma_unmap_vaddr_all(container, &local_err)) {
55*1faadd96SSteve Sistare         error_report_err(local_err);
56*1faadd96SSteve Sistare         return -1;
57*1faadd96SSteve Sistare     }
58*1faadd96SSteve Sistare     return 0;
59*1faadd96SSteve Sistare }
60*1faadd96SSteve Sistare 
6154857b08SSteve Sistare static const VMStateDescription vfio_container_vmstate = {
6254857b08SSteve Sistare     .name = "vfio-container",
6354857b08SSteve Sistare     .version_id = 0,
6454857b08SSteve Sistare     .minimum_version_id = 0,
65*1faadd96SSteve Sistare     .pre_save = vfio_container_pre_save,
6654857b08SSteve Sistare     .needed = cpr_incoming_needed,
6754857b08SSteve Sistare     .fields = (VMStateField[]) {
6854857b08SSteve Sistare         VMSTATE_END_OF_LIST()
6954857b08SSteve Sistare     }
7054857b08SSteve Sistare };
7154857b08SSteve Sistare 
7254857b08SSteve Sistare bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp)
7354857b08SSteve Sistare {
7454857b08SSteve Sistare     VFIOContainerBase *bcontainer = &container->bcontainer;
7554857b08SSteve Sistare     Error **cpr_blocker = &container->cpr.blocker;
7654857b08SSteve Sistare 
7754857b08SSteve Sistare     migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier,
7854857b08SSteve Sistare                                 vfio_cpr_reboot_notifier,
7954857b08SSteve Sistare                                 MIG_MODE_CPR_REBOOT);
8054857b08SSteve Sistare 
8154857b08SSteve Sistare     if (!vfio_cpr_supported(container, cpr_blocker)) {
8254857b08SSteve Sistare         return migrate_add_blocker_modes(cpr_blocker, errp,
8354857b08SSteve Sistare                                          MIG_MODE_CPR_TRANSFER, -1) == 0;
8454857b08SSteve Sistare     }
8554857b08SSteve Sistare 
8654857b08SSteve Sistare     vmstate_register(NULL, -1, &vfio_container_vmstate, container);
8754857b08SSteve Sistare 
8854857b08SSteve Sistare     return true;
8954857b08SSteve Sistare }
9054857b08SSteve Sistare 
9154857b08SSteve Sistare void vfio_legacy_cpr_unregister_container(VFIOContainer *container)
9254857b08SSteve Sistare {
9354857b08SSteve Sistare     VFIOContainerBase *bcontainer = &container->bcontainer;
9454857b08SSteve Sistare 
9554857b08SSteve Sistare     migration_remove_notifier(&bcontainer->cpr_reboot_notifier);
9654857b08SSteve Sistare     migrate_del_blocker(&container->cpr.blocker);
9754857b08SSteve Sistare     vmstate_unregister(NULL, &vfio_container_vmstate, container);
9854857b08SSteve Sistare }
99c29a65edSSteve Sistare 
100c29a65edSSteve Sistare int vfio_cpr_group_get_device_fd(int d, const char *name)
101c29a65edSSteve Sistare {
102c29a65edSSteve Sistare     const int id = 0;
103c29a65edSSteve Sistare     int fd = cpr_find_fd(name, id);
104c29a65edSSteve Sistare 
105c29a65edSSteve Sistare     if (fd < 0) {
106c29a65edSSteve Sistare         fd = ioctl(d, VFIO_GROUP_GET_DEVICE_FD, name);
107c29a65edSSteve Sistare         if (fd >= 0) {
108c29a65edSSteve Sistare             cpr_save_fd(name, id, fd);
109c29a65edSSteve Sistare         }
110c29a65edSSteve Sistare     }
111c29a65edSSteve Sistare     return fd;
112c29a65edSSteve Sistare }
113c29a65edSSteve Sistare 
114c29a65edSSteve Sistare static bool same_device(int fd1, int fd2)
115c29a65edSSteve Sistare {
116c29a65edSSteve Sistare     struct stat st1, st2;
117c29a65edSSteve Sistare 
118c29a65edSSteve Sistare     return !fstat(fd1, &st1) && !fstat(fd2, &st2) && st1.st_dev == st2.st_dev;
119c29a65edSSteve Sistare }
120c29a65edSSteve Sistare 
121c29a65edSSteve Sistare bool vfio_cpr_container_match(VFIOContainer *container, VFIOGroup *group,
122c29a65edSSteve Sistare                               int fd)
123c29a65edSSteve Sistare {
124c29a65edSSteve Sistare     if (container->fd == fd) {
125c29a65edSSteve Sistare         return true;
126c29a65edSSteve Sistare     }
127c29a65edSSteve Sistare     if (!same_device(container->fd, fd)) {
128c29a65edSSteve Sistare         return false;
129c29a65edSSteve Sistare     }
130c29a65edSSteve Sistare     /*
131c29a65edSSteve Sistare      * Same device, different fd.  This occurs when the container fd is
132c29a65edSSteve Sistare      * cpr_save'd multiple times, once for each groupid, so SCM_RIGHTS
133c29a65edSSteve Sistare      * produces duplicates.  De-dup it.
134c29a65edSSteve Sistare      */
135c29a65edSSteve Sistare     cpr_delete_fd("vfio_container_for_group", group->groupid);
136c29a65edSSteve Sistare     close(fd);
137c29a65edSSteve Sistare     cpr_save_fd("vfio_container_for_group", group->groupid, container->fd);
138c29a65edSSteve Sistare     return true;
139c29a65edSSteve Sistare }
140