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