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" 11*c29a65edSSteve 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 1854857b08SSteve Sistare static bool vfio_cpr_supported(VFIOContainer *container, Error **errp) 1954857b08SSteve Sistare { 2054857b08SSteve Sistare if (!ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UPDATE_VADDR)) { 2154857b08SSteve Sistare error_setg(errp, "VFIO container does not support VFIO_UPDATE_VADDR"); 2254857b08SSteve Sistare return false; 2354857b08SSteve Sistare 2454857b08SSteve Sistare } else if (!ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UNMAP_ALL)) { 2554857b08SSteve Sistare error_setg(errp, "VFIO container does not support VFIO_UNMAP_ALL"); 2654857b08SSteve Sistare return false; 2754857b08SSteve Sistare 2854857b08SSteve Sistare } else { 2954857b08SSteve Sistare return true; 3054857b08SSteve Sistare } 3154857b08SSteve Sistare } 3254857b08SSteve Sistare 3354857b08SSteve Sistare static const VMStateDescription vfio_container_vmstate = { 3454857b08SSteve Sistare .name = "vfio-container", 3554857b08SSteve Sistare .version_id = 0, 3654857b08SSteve Sistare .minimum_version_id = 0, 3754857b08SSteve Sistare .needed = cpr_incoming_needed, 3854857b08SSteve Sistare .fields = (VMStateField[]) { 3954857b08SSteve Sistare VMSTATE_END_OF_LIST() 4054857b08SSteve Sistare } 4154857b08SSteve Sistare }; 4254857b08SSteve Sistare 4354857b08SSteve Sistare bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp) 4454857b08SSteve Sistare { 4554857b08SSteve Sistare VFIOContainerBase *bcontainer = &container->bcontainer; 4654857b08SSteve Sistare Error **cpr_blocker = &container->cpr.blocker; 4754857b08SSteve Sistare 4854857b08SSteve Sistare migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier, 4954857b08SSteve Sistare vfio_cpr_reboot_notifier, 5054857b08SSteve Sistare MIG_MODE_CPR_REBOOT); 5154857b08SSteve Sistare 5254857b08SSteve Sistare if (!vfio_cpr_supported(container, cpr_blocker)) { 5354857b08SSteve Sistare return migrate_add_blocker_modes(cpr_blocker, errp, 5454857b08SSteve Sistare MIG_MODE_CPR_TRANSFER, -1) == 0; 5554857b08SSteve Sistare } 5654857b08SSteve Sistare 5754857b08SSteve Sistare vmstate_register(NULL, -1, &vfio_container_vmstate, container); 5854857b08SSteve Sistare 5954857b08SSteve Sistare return true; 6054857b08SSteve Sistare } 6154857b08SSteve Sistare 6254857b08SSteve Sistare void vfio_legacy_cpr_unregister_container(VFIOContainer *container) 6354857b08SSteve Sistare { 6454857b08SSteve Sistare VFIOContainerBase *bcontainer = &container->bcontainer; 6554857b08SSteve Sistare 6654857b08SSteve Sistare migration_remove_notifier(&bcontainer->cpr_reboot_notifier); 6754857b08SSteve Sistare migrate_del_blocker(&container->cpr.blocker); 6854857b08SSteve Sistare vmstate_unregister(NULL, &vfio_container_vmstate, container); 6954857b08SSteve Sistare } 70*c29a65edSSteve Sistare 71*c29a65edSSteve Sistare int vfio_cpr_group_get_device_fd(int d, const char *name) 72*c29a65edSSteve Sistare { 73*c29a65edSSteve Sistare const int id = 0; 74*c29a65edSSteve Sistare int fd = cpr_find_fd(name, id); 75*c29a65edSSteve Sistare 76*c29a65edSSteve Sistare if (fd < 0) { 77*c29a65edSSteve Sistare fd = ioctl(d, VFIO_GROUP_GET_DEVICE_FD, name); 78*c29a65edSSteve Sistare if (fd >= 0) { 79*c29a65edSSteve Sistare cpr_save_fd(name, id, fd); 80*c29a65edSSteve Sistare } 81*c29a65edSSteve Sistare } 82*c29a65edSSteve Sistare return fd; 83*c29a65edSSteve Sistare } 84*c29a65edSSteve Sistare 85*c29a65edSSteve Sistare static bool same_device(int fd1, int fd2) 86*c29a65edSSteve Sistare { 87*c29a65edSSteve Sistare struct stat st1, st2; 88*c29a65edSSteve Sistare 89*c29a65edSSteve Sistare return !fstat(fd1, &st1) && !fstat(fd2, &st2) && st1.st_dev == st2.st_dev; 90*c29a65edSSteve Sistare } 91*c29a65edSSteve Sistare 92*c29a65edSSteve Sistare bool vfio_cpr_container_match(VFIOContainer *container, VFIOGroup *group, 93*c29a65edSSteve Sistare int fd) 94*c29a65edSSteve Sistare { 95*c29a65edSSteve Sistare if (container->fd == fd) { 96*c29a65edSSteve Sistare return true; 97*c29a65edSSteve Sistare } 98*c29a65edSSteve Sistare if (!same_device(container->fd, fd)) { 99*c29a65edSSteve Sistare return false; 100*c29a65edSSteve Sistare } 101*c29a65edSSteve Sistare /* 102*c29a65edSSteve Sistare * Same device, different fd. This occurs when the container fd is 103*c29a65edSSteve Sistare * cpr_save'd multiple times, once for each groupid, so SCM_RIGHTS 104*c29a65edSSteve Sistare * produces duplicates. De-dup it. 105*c29a65edSSteve Sistare */ 106*c29a65edSSteve Sistare cpr_delete_fd("vfio_container_for_group", group->groupid); 107*c29a65edSSteve Sistare close(fd); 108*c29a65edSSteve Sistare cpr_save_fd("vfio_container_for_group", group->groupid, container->fd); 109*c29a65edSSteve Sistare return true; 110*c29a65edSSteve Sistare } 111