xref: /qemu/hw/vfio/cpr-iommufd.c (revision f2f3e4667e4d6026f39ab17f355f79b2f8431e19)
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"
10*f2f3e466SSteve 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"
17*f2f3e466SSteve Sistare #include "trace.h"
1806c6a658SSteve Sistare 
19*f2f3e466SSteve Sistare typedef struct CprVFIODevice {
20*f2f3e466SSteve Sistare     char *name;
21*f2f3e466SSteve Sistare     unsigned int namelen;
22*f2f3e466SSteve Sistare     uint32_t ioas_id;
23*f2f3e466SSteve Sistare     int devid;
24*f2f3e466SSteve Sistare     uint32_t hwpt_id;
25*f2f3e466SSteve Sistare     QLIST_ENTRY(CprVFIODevice) next;
26*f2f3e466SSteve Sistare } CprVFIODevice;
27*f2f3e466SSteve Sistare 
28*f2f3e466SSteve Sistare static const VMStateDescription vmstate_cpr_vfio_device = {
29*f2f3e466SSteve Sistare     .name = "cpr vfio device",
30*f2f3e466SSteve Sistare     .version_id = 1,
31*f2f3e466SSteve Sistare     .minimum_version_id = 1,
32*f2f3e466SSteve Sistare     .fields = (VMStateField[]) {
33*f2f3e466SSteve Sistare         VMSTATE_UINT32(namelen, CprVFIODevice),
34*f2f3e466SSteve Sistare         VMSTATE_VBUFFER_ALLOC_UINT32(name, CprVFIODevice, 0, NULL, namelen),
35*f2f3e466SSteve Sistare         VMSTATE_INT32(devid, CprVFIODevice),
36*f2f3e466SSteve Sistare         VMSTATE_UINT32(ioas_id, CprVFIODevice),
37*f2f3e466SSteve Sistare         VMSTATE_UINT32(hwpt_id, CprVFIODevice),
38*f2f3e466SSteve Sistare         VMSTATE_END_OF_LIST()
39*f2f3e466SSteve Sistare     }
40*f2f3e466SSteve Sistare };
41*f2f3e466SSteve Sistare 
42*f2f3e466SSteve Sistare const VMStateDescription vmstate_cpr_vfio_devices = {
43*f2f3e466SSteve Sistare     .name = CPR_STATE "/vfio devices",
44*f2f3e466SSteve Sistare     .version_id = 1,
45*f2f3e466SSteve Sistare     .minimum_version_id = 1,
46*f2f3e466SSteve Sistare     .fields = (const VMStateField[]){
47*f2f3e466SSteve Sistare         VMSTATE_QLIST_V(vfio_devices, CprState, 1, vmstate_cpr_vfio_device,
48*f2f3e466SSteve Sistare                         CprVFIODevice, next),
49*f2f3e466SSteve Sistare         VMSTATE_END_OF_LIST()
50*f2f3e466SSteve Sistare     }
51*f2f3e466SSteve Sistare };
52*f2f3e466SSteve Sistare 
53*f2f3e466SSteve Sistare static void vfio_cpr_save_device(VFIODevice *vbasedev)
54*f2f3e466SSteve Sistare {
55*f2f3e466SSteve Sistare     CprVFIODevice *elem = g_new0(CprVFIODevice, 1);
56*f2f3e466SSteve Sistare 
57*f2f3e466SSteve Sistare     elem->name = g_strdup(vbasedev->name);
58*f2f3e466SSteve Sistare     elem->namelen = strlen(vbasedev->name) + 1;
59*f2f3e466SSteve Sistare     elem->ioas_id = vbasedev->cpr.ioas_id;
60*f2f3e466SSteve Sistare     elem->devid = vbasedev->devid;
61*f2f3e466SSteve Sistare     elem->hwpt_id = vbasedev->cpr.hwpt_id;
62*f2f3e466SSteve Sistare     QLIST_INSERT_HEAD(&cpr_state.vfio_devices, elem, next);
63*f2f3e466SSteve Sistare }
64*f2f3e466SSteve Sistare 
65*f2f3e466SSteve Sistare static CprVFIODevice *find_device(const char *name)
66*f2f3e466SSteve Sistare {
67*f2f3e466SSteve Sistare     CprVFIODeviceList *head = &cpr_state.vfio_devices;
68*f2f3e466SSteve Sistare     CprVFIODevice *elem;
69*f2f3e466SSteve Sistare 
70*f2f3e466SSteve Sistare     QLIST_FOREACH(elem, head, next) {
71*f2f3e466SSteve Sistare         if (!strcmp(elem->name, name)) {
72*f2f3e466SSteve Sistare             return elem;
73*f2f3e466SSteve Sistare         }
74*f2f3e466SSteve Sistare     }
75*f2f3e466SSteve Sistare     return NULL;
76*f2f3e466SSteve Sistare }
77*f2f3e466SSteve Sistare 
78*f2f3e466SSteve Sistare static void vfio_cpr_delete_device(const char *name)
79*f2f3e466SSteve Sistare {
80*f2f3e466SSteve Sistare     CprVFIODevice *elem = find_device(name);
81*f2f3e466SSteve Sistare 
82*f2f3e466SSteve Sistare     if (elem) {
83*f2f3e466SSteve Sistare         QLIST_REMOVE(elem, next);
84*f2f3e466SSteve Sistare         g_free(elem->name);
85*f2f3e466SSteve Sistare         g_free(elem);
86*f2f3e466SSteve Sistare     }
87*f2f3e466SSteve Sistare }
88*f2f3e466SSteve Sistare 
89*f2f3e466SSteve Sistare static bool vfio_cpr_find_device(VFIODevice *vbasedev)
90*f2f3e466SSteve Sistare {
91*f2f3e466SSteve Sistare     CprVFIODevice *elem = find_device(vbasedev->name);
92*f2f3e466SSteve Sistare 
93*f2f3e466SSteve Sistare     if (elem) {
94*f2f3e466SSteve Sistare         vbasedev->cpr.ioas_id = elem->ioas_id;
95*f2f3e466SSteve Sistare         vbasedev->devid = elem->devid;
96*f2f3e466SSteve Sistare         vbasedev->cpr.hwpt_id = elem->hwpt_id;
97*f2f3e466SSteve Sistare         trace_vfio_cpr_find_device(elem->ioas_id, elem->devid, elem->hwpt_id);
98*f2f3e466SSteve Sistare         return true;
99*f2f3e466SSteve Sistare     }
100*f2f3e466SSteve Sistare     return false;
101*f2f3e466SSteve 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 {
168*f2f3e466SSteve Sistare     if (!cpr_is_incoming()) {
169*f2f3e466SSteve Sistare         vfio_cpr_save_device(vbasedev);
170*f2f3e466SSteve Sistare     }
17106c6a658SSteve Sistare }
17206c6a658SSteve Sistare 
17306c6a658SSteve Sistare void vfio_iommufd_cpr_unregister_device(VFIODevice *vbasedev)
17406c6a658SSteve Sistare {
175*f2f3e466SSteve Sistare     vfio_cpr_delete_device(vbasedev->name);
176*f2f3e466SSteve Sistare }
177*f2f3e466SSteve Sistare 
178*f2f3e466SSteve Sistare void vfio_cpr_load_device(VFIODevice *vbasedev)
179*f2f3e466SSteve Sistare {
180*f2f3e466SSteve Sistare     if (cpr_is_incoming()) {
181*f2f3e466SSteve Sistare         bool ret = vfio_cpr_find_device(vbasedev);
182*f2f3e466SSteve Sistare         g_assert(ret);
183*f2f3e466SSteve Sistare     }
18406c6a658SSteve Sistare }
185