106c6a658SSteve Sistare /* 206c6a658SSteve Sistare * Copyright (c) 2024-2025 Oracle and/or its affiliates. 306c6a658SSteve Sistare * 406c6a658SSteve Sistare * SPDX-License-Identifier: GPL-2.0-or-later 506c6a658SSteve Sistare */ 606c6a658SSteve Sistare 706c6a658SSteve Sistare #include "qemu/osdep.h" 806c6a658SSteve Sistare #include "qapi/error.h" 906c6a658SSteve Sistare #include "hw/vfio/vfio-cpr.h" 10f2f3e466SSteve Sistare #include "hw/vfio/vfio-device.h" 1106c6a658SSteve Sistare #include "migration/blocker.h" 1206c6a658SSteve Sistare #include "migration/cpr.h" 1306c6a658SSteve Sistare #include "migration/migration.h" 1406c6a658SSteve Sistare #include "migration/vmstate.h" 1506c6a658SSteve Sistare #include "system/iommufd.h" 1606c6a658SSteve Sistare #include "vfio-iommufd.h" 17f2f3e466SSteve Sistare #include "trace.h" 1806c6a658SSteve Sistare 19f2f3e466SSteve Sistare typedef struct CprVFIODevice { 20f2f3e466SSteve Sistare char *name; 21f2f3e466SSteve Sistare unsigned int namelen; 22f2f3e466SSteve Sistare uint32_t ioas_id; 23f2f3e466SSteve Sistare int devid; 24f2f3e466SSteve Sistare uint32_t hwpt_id; 25f2f3e466SSteve Sistare QLIST_ENTRY(CprVFIODevice) next; 26f2f3e466SSteve Sistare } CprVFIODevice; 27f2f3e466SSteve Sistare 28f2f3e466SSteve Sistare static const VMStateDescription vmstate_cpr_vfio_device = { 29f2f3e466SSteve Sistare .name = "cpr vfio device", 30f2f3e466SSteve Sistare .version_id = 1, 31f2f3e466SSteve Sistare .minimum_version_id = 1, 32f2f3e466SSteve Sistare .fields = (VMStateField[]) { 33f2f3e466SSteve Sistare VMSTATE_UINT32(namelen, CprVFIODevice), 34f2f3e466SSteve Sistare VMSTATE_VBUFFER_ALLOC_UINT32(name, CprVFIODevice, 0, NULL, namelen), 35f2f3e466SSteve Sistare VMSTATE_INT32(devid, CprVFIODevice), 36f2f3e466SSteve Sistare VMSTATE_UINT32(ioas_id, CprVFIODevice), 37f2f3e466SSteve Sistare VMSTATE_UINT32(hwpt_id, CprVFIODevice), 38f2f3e466SSteve Sistare VMSTATE_END_OF_LIST() 39f2f3e466SSteve Sistare } 40f2f3e466SSteve Sistare }; 41f2f3e466SSteve Sistare 42f2f3e466SSteve Sistare const VMStateDescription vmstate_cpr_vfio_devices = { 43f2f3e466SSteve Sistare .name = CPR_STATE "/vfio devices", 44f2f3e466SSteve Sistare .version_id = 1, 45f2f3e466SSteve Sistare .minimum_version_id = 1, 46f2f3e466SSteve Sistare .fields = (const VMStateField[]){ 47f2f3e466SSteve Sistare VMSTATE_QLIST_V(vfio_devices, CprState, 1, vmstate_cpr_vfio_device, 48f2f3e466SSteve Sistare CprVFIODevice, next), 49f2f3e466SSteve Sistare VMSTATE_END_OF_LIST() 50f2f3e466SSteve Sistare } 51f2f3e466SSteve Sistare }; 52f2f3e466SSteve Sistare 53f2f3e466SSteve Sistare static void vfio_cpr_save_device(VFIODevice *vbasedev) 54f2f3e466SSteve Sistare { 55f2f3e466SSteve Sistare CprVFIODevice *elem = g_new0(CprVFIODevice, 1); 56f2f3e466SSteve Sistare 57f2f3e466SSteve Sistare elem->name = g_strdup(vbasedev->name); 58f2f3e466SSteve Sistare elem->namelen = strlen(vbasedev->name) + 1; 59f2f3e466SSteve Sistare elem->ioas_id = vbasedev->cpr.ioas_id; 60f2f3e466SSteve Sistare elem->devid = vbasedev->devid; 61f2f3e466SSteve Sistare elem->hwpt_id = vbasedev->cpr.hwpt_id; 62f2f3e466SSteve Sistare QLIST_INSERT_HEAD(&cpr_state.vfio_devices, elem, next); 63f2f3e466SSteve Sistare } 64f2f3e466SSteve Sistare 65f2f3e466SSteve Sistare static CprVFIODevice *find_device(const char *name) 66f2f3e466SSteve Sistare { 67f2f3e466SSteve Sistare CprVFIODeviceList *head = &cpr_state.vfio_devices; 68f2f3e466SSteve Sistare CprVFIODevice *elem; 69f2f3e466SSteve Sistare 70f2f3e466SSteve Sistare QLIST_FOREACH(elem, head, next) { 71f2f3e466SSteve Sistare if (!strcmp(elem->name, name)) { 72f2f3e466SSteve Sistare return elem; 73f2f3e466SSteve Sistare } 74f2f3e466SSteve Sistare } 75f2f3e466SSteve Sistare return NULL; 76f2f3e466SSteve Sistare } 77f2f3e466SSteve Sistare 78f2f3e466SSteve Sistare static void vfio_cpr_delete_device(const char *name) 79f2f3e466SSteve Sistare { 80f2f3e466SSteve Sistare CprVFIODevice *elem = find_device(name); 81f2f3e466SSteve Sistare 82f2f3e466SSteve Sistare if (elem) { 83f2f3e466SSteve Sistare QLIST_REMOVE(elem, next); 84f2f3e466SSteve Sistare g_free(elem->name); 85f2f3e466SSteve Sistare g_free(elem); 86f2f3e466SSteve Sistare } 87f2f3e466SSteve Sistare } 88f2f3e466SSteve Sistare 89f2f3e466SSteve Sistare static bool vfio_cpr_find_device(VFIODevice *vbasedev) 90f2f3e466SSteve Sistare { 91f2f3e466SSteve Sistare CprVFIODevice *elem = find_device(vbasedev->name); 92f2f3e466SSteve Sistare 93f2f3e466SSteve Sistare if (elem) { 94f2f3e466SSteve Sistare vbasedev->cpr.ioas_id = elem->ioas_id; 95f2f3e466SSteve Sistare vbasedev->devid = elem->devid; 96f2f3e466SSteve Sistare vbasedev->cpr.hwpt_id = elem->hwpt_id; 97f2f3e466SSteve Sistare trace_vfio_cpr_find_device(elem->ioas_id, elem->devid, elem->hwpt_id); 98f2f3e466SSteve Sistare return true; 99f2f3e466SSteve Sistare } 100f2f3e466SSteve Sistare return false; 101f2f3e466SSteve Sistare } 102a6f2f9c4SSteve Sistare 10306c6a658SSteve Sistare static bool vfio_cpr_supported(IOMMUFDBackend *be, Error **errp) 10406c6a658SSteve Sistare { 10506c6a658SSteve Sistare if (!iommufd_change_process_capable(be)) { 10606c6a658SSteve Sistare if (errp) { 10706c6a658SSteve Sistare error_setg(errp, "vfio iommufd backend does not support " 10806c6a658SSteve Sistare "IOMMU_IOAS_CHANGE_PROCESS"); 10906c6a658SSteve Sistare } 11006c6a658SSteve Sistare return false; 11106c6a658SSteve Sistare } 11206c6a658SSteve Sistare return true; 11306c6a658SSteve Sistare } 11406c6a658SSteve Sistare 11506c6a658SSteve Sistare static const VMStateDescription iommufd_cpr_vmstate = { 11606c6a658SSteve Sistare .name = "iommufd", 11706c6a658SSteve Sistare .version_id = 0, 11806c6a658SSteve Sistare .minimum_version_id = 0, 11906c6a658SSteve Sistare .needed = cpr_incoming_needed, 12006c6a658SSteve Sistare .fields = (VMStateField[]) { 12106c6a658SSteve Sistare VMSTATE_END_OF_LIST() 12206c6a658SSteve Sistare } 12306c6a658SSteve Sistare }; 12406c6a658SSteve Sistare 12506c6a658SSteve Sistare bool vfio_iommufd_cpr_register_iommufd(IOMMUFDBackend *be, Error **errp) 12606c6a658SSteve Sistare { 12706c6a658SSteve Sistare Error **cpr_blocker = &be->cpr_blocker; 12806c6a658SSteve Sistare 12906c6a658SSteve Sistare if (!vfio_cpr_supported(be, cpr_blocker)) { 13006c6a658SSteve Sistare return migrate_add_blocker_modes(cpr_blocker, errp, 13106c6a658SSteve Sistare MIG_MODE_CPR_TRANSFER, -1) == 0; 13206c6a658SSteve Sistare } 13306c6a658SSteve Sistare 13406c6a658SSteve Sistare vmstate_register(NULL, -1, &iommufd_cpr_vmstate, be); 13506c6a658SSteve Sistare 13606c6a658SSteve Sistare return true; 13706c6a658SSteve Sistare } 13806c6a658SSteve Sistare 13906c6a658SSteve Sistare void vfio_iommufd_cpr_unregister_iommufd(IOMMUFDBackend *be) 14006c6a658SSteve Sistare { 14106c6a658SSteve Sistare vmstate_unregister(NULL, &iommufd_cpr_vmstate, be); 14206c6a658SSteve Sistare migrate_del_blocker(&be->cpr_blocker); 14306c6a658SSteve Sistare } 14406c6a658SSteve Sistare 14506c6a658SSteve Sistare bool vfio_iommufd_cpr_register_container(VFIOIOMMUFDContainer *container, 14606c6a658SSteve Sistare Error **errp) 14706c6a658SSteve Sistare { 14806c6a658SSteve Sistare VFIOContainerBase *bcontainer = &container->bcontainer; 14906c6a658SSteve Sistare 15006c6a658SSteve Sistare migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier, 15106c6a658SSteve Sistare vfio_cpr_reboot_notifier, 15206c6a658SSteve Sistare MIG_MODE_CPR_REBOOT); 15306c6a658SSteve Sistare 15406c6a658SSteve Sistare vfio_cpr_add_kvm_notifier(); 15506c6a658SSteve Sistare 15606c6a658SSteve Sistare return true; 15706c6a658SSteve Sistare } 15806c6a658SSteve Sistare 15906c6a658SSteve Sistare void vfio_iommufd_cpr_unregister_container(VFIOIOMMUFDContainer *container) 16006c6a658SSteve Sistare { 16106c6a658SSteve Sistare VFIOContainerBase *bcontainer = &container->bcontainer; 16206c6a658SSteve Sistare 16306c6a658SSteve Sistare migration_remove_notifier(&bcontainer->cpr_reboot_notifier); 16406c6a658SSteve Sistare } 16506c6a658SSteve Sistare 16606c6a658SSteve Sistare void vfio_iommufd_cpr_register_device(VFIODevice *vbasedev) 16706c6a658SSteve Sistare { 168f2f3e466SSteve Sistare if (!cpr_is_incoming()) { 169*2a3f0a59SSteve Sistare /* 170*2a3f0a59SSteve Sistare * Beware fd may have already been saved by vfio_device_set_fd, 171*2a3f0a59SSteve Sistare * so call resave to avoid a duplicate entry. 172*2a3f0a59SSteve Sistare */ 173*2a3f0a59SSteve Sistare cpr_resave_fd(vbasedev->name, 0, vbasedev->fd); 174f2f3e466SSteve Sistare vfio_cpr_save_device(vbasedev); 175f2f3e466SSteve Sistare } 17606c6a658SSteve Sistare } 17706c6a658SSteve Sistare 17806c6a658SSteve Sistare void vfio_iommufd_cpr_unregister_device(VFIODevice *vbasedev) 17906c6a658SSteve Sistare { 180*2a3f0a59SSteve Sistare cpr_delete_fd(vbasedev->name, 0); 181f2f3e466SSteve Sistare vfio_cpr_delete_device(vbasedev->name); 182f2f3e466SSteve Sistare } 183f2f3e466SSteve Sistare 184f2f3e466SSteve Sistare void vfio_cpr_load_device(VFIODevice *vbasedev) 185f2f3e466SSteve Sistare { 186f2f3e466SSteve Sistare if (cpr_is_incoming()) { 187f2f3e466SSteve Sistare bool ret = vfio_cpr_find_device(vbasedev); 188f2f3e466SSteve Sistare g_assert(ret); 189*2a3f0a59SSteve Sistare 190*2a3f0a59SSteve Sistare if (vbasedev->fd < 0) { 191*2a3f0a59SSteve Sistare vbasedev->fd = cpr_find_fd(vbasedev->name, 0); 192*2a3f0a59SSteve Sistare } 193f2f3e466SSteve Sistare } 19406c6a658SSteve Sistare } 195