xref: /qemu/hw/vfio/cpr-iommufd.c (revision 2a3f0a59bd6479f75fa5335f82b85b4f9cd7ed4e)
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