1 /* 2 * Copyright (c) 2024-2025 Oracle and/or its affiliates. 3 * 4 * SPDX-License-Identifier: GPL-2.0-or-later 5 */ 6 7 #include "qemu/osdep.h" 8 #include "qapi/error.h" 9 #include "hw/vfio/vfio-cpr.h" 10 #include "hw/vfio/vfio-device.h" 11 #include "migration/blocker.h" 12 #include "migration/cpr.h" 13 #include "migration/migration.h" 14 #include "migration/vmstate.h" 15 #include "system/iommufd.h" 16 #include "vfio-iommufd.h" 17 #include "trace.h" 18 19 typedef struct CprVFIODevice { 20 char *name; 21 unsigned int namelen; 22 uint32_t ioas_id; 23 int devid; 24 uint32_t hwpt_id; 25 QLIST_ENTRY(CprVFIODevice) next; 26 } CprVFIODevice; 27 28 static const VMStateDescription vmstate_cpr_vfio_device = { 29 .name = "cpr vfio device", 30 .version_id = 1, 31 .minimum_version_id = 1, 32 .fields = (VMStateField[]) { 33 VMSTATE_UINT32(namelen, CprVFIODevice), 34 VMSTATE_VBUFFER_ALLOC_UINT32(name, CprVFIODevice, 0, NULL, namelen), 35 VMSTATE_INT32(devid, CprVFIODevice), 36 VMSTATE_UINT32(ioas_id, CprVFIODevice), 37 VMSTATE_UINT32(hwpt_id, CprVFIODevice), 38 VMSTATE_END_OF_LIST() 39 } 40 }; 41 42 const VMStateDescription vmstate_cpr_vfio_devices = { 43 .name = CPR_STATE "/vfio devices", 44 .version_id = 1, 45 .minimum_version_id = 1, 46 .fields = (const VMStateField[]){ 47 VMSTATE_QLIST_V(vfio_devices, CprState, 1, vmstate_cpr_vfio_device, 48 CprVFIODevice, next), 49 VMSTATE_END_OF_LIST() 50 } 51 }; 52 53 static void vfio_cpr_save_device(VFIODevice *vbasedev) 54 { 55 CprVFIODevice *elem = g_new0(CprVFIODevice, 1); 56 57 elem->name = g_strdup(vbasedev->name); 58 elem->namelen = strlen(vbasedev->name) + 1; 59 elem->ioas_id = vbasedev->cpr.ioas_id; 60 elem->devid = vbasedev->devid; 61 elem->hwpt_id = vbasedev->cpr.hwpt_id; 62 QLIST_INSERT_HEAD(&cpr_state.vfio_devices, elem, next); 63 } 64 65 static CprVFIODevice *find_device(const char *name) 66 { 67 CprVFIODeviceList *head = &cpr_state.vfio_devices; 68 CprVFIODevice *elem; 69 70 QLIST_FOREACH(elem, head, next) { 71 if (!strcmp(elem->name, name)) { 72 return elem; 73 } 74 } 75 return NULL; 76 } 77 78 static void vfio_cpr_delete_device(const char *name) 79 { 80 CprVFIODevice *elem = find_device(name); 81 82 if (elem) { 83 QLIST_REMOVE(elem, next); 84 g_free(elem->name); 85 g_free(elem); 86 } 87 } 88 89 static bool vfio_cpr_find_device(VFIODevice *vbasedev) 90 { 91 CprVFIODevice *elem = find_device(vbasedev->name); 92 93 if (elem) { 94 vbasedev->cpr.ioas_id = elem->ioas_id; 95 vbasedev->devid = elem->devid; 96 vbasedev->cpr.hwpt_id = elem->hwpt_id; 97 trace_vfio_cpr_find_device(elem->ioas_id, elem->devid, elem->hwpt_id); 98 return true; 99 } 100 return false; 101 } 102 103 static bool vfio_cpr_supported(IOMMUFDBackend *be, Error **errp) 104 { 105 if (!iommufd_change_process_capable(be)) { 106 if (errp) { 107 error_setg(errp, "vfio iommufd backend does not support " 108 "IOMMU_IOAS_CHANGE_PROCESS"); 109 } 110 return false; 111 } 112 return true; 113 } 114 115 static const VMStateDescription iommufd_cpr_vmstate = { 116 .name = "iommufd", 117 .version_id = 0, 118 .minimum_version_id = 0, 119 .needed = cpr_incoming_needed, 120 .fields = (VMStateField[]) { 121 VMSTATE_END_OF_LIST() 122 } 123 }; 124 125 bool vfio_iommufd_cpr_register_iommufd(IOMMUFDBackend *be, Error **errp) 126 { 127 Error **cpr_blocker = &be->cpr_blocker; 128 129 if (!vfio_cpr_supported(be, cpr_blocker)) { 130 return migrate_add_blocker_modes(cpr_blocker, errp, 131 MIG_MODE_CPR_TRANSFER, -1) == 0; 132 } 133 134 vmstate_register(NULL, -1, &iommufd_cpr_vmstate, be); 135 136 return true; 137 } 138 139 void vfio_iommufd_cpr_unregister_iommufd(IOMMUFDBackend *be) 140 { 141 vmstate_unregister(NULL, &iommufd_cpr_vmstate, be); 142 migrate_del_blocker(&be->cpr_blocker); 143 } 144 145 bool vfio_iommufd_cpr_register_container(VFIOIOMMUFDContainer *container, 146 Error **errp) 147 { 148 VFIOContainerBase *bcontainer = &container->bcontainer; 149 150 migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier, 151 vfio_cpr_reboot_notifier, 152 MIG_MODE_CPR_REBOOT); 153 154 vfio_cpr_add_kvm_notifier(); 155 156 return true; 157 } 158 159 void vfio_iommufd_cpr_unregister_container(VFIOIOMMUFDContainer *container) 160 { 161 VFIOContainerBase *bcontainer = &container->bcontainer; 162 163 migration_remove_notifier(&bcontainer->cpr_reboot_notifier); 164 } 165 166 void vfio_iommufd_cpr_register_device(VFIODevice *vbasedev) 167 { 168 if (!cpr_is_incoming()) { 169 /* 170 * Beware fd may have already been saved by vfio_device_set_fd, 171 * so call resave to avoid a duplicate entry. 172 */ 173 cpr_resave_fd(vbasedev->name, 0, vbasedev->fd); 174 vfio_cpr_save_device(vbasedev); 175 } 176 } 177 178 void vfio_iommufd_cpr_unregister_device(VFIODevice *vbasedev) 179 { 180 cpr_delete_fd(vbasedev->name, 0); 181 vfio_cpr_delete_device(vbasedev->name); 182 } 183 184 void vfio_cpr_load_device(VFIODevice *vbasedev) 185 { 186 if (cpr_is_incoming()) { 187 bool ret = vfio_cpr_find_device(vbasedev); 188 g_assert(ret); 189 190 if (vbasedev->fd < 0) { 191 vbasedev->fd = cpr_find_fd(vbasedev->name, 0); 192 } 193 } 194 } 195