master
c 226 lines 5.86 KB
Raw
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 "qemu/error-report.h"
9 #include "qapi/error.h"
10 #include "hw/vfio/vfio-cpr.h"
11 #include "hw/vfio/vfio-device.h"
12 #include "migration/blocker.h"
13 #include "migration/cpr.h"
14 #include "migration/migration.h"
15 #include "migration/vmstate.h"
16 #include "system/iommufd.h"
17 #include "vfio-iommufd.h"
18 #include "trace.h"
19
20 typedef struct CprVFIODevice {
21 char *name;
22 unsigned int namelen;
23 uint32_t ioas_id;
24 int devid;
25 uint32_t hwpt_id;
26 QLIST_ENTRY(CprVFIODevice) next;
27 } CprVFIODevice;
28
29 static const VMStateDescription vmstate_cpr_vfio_device = {
30 .name = "cpr vfio device",
31 .version_id = 1,
32 .minimum_version_id = 1,
33 .fields = (VMStateField[]) {
34 VMSTATE_UINT32(namelen, CprVFIODevice),
35 VMSTATE_VBUFFER_ALLOC_UINT32(name, CprVFIODevice, 0, NULL, namelen),
36 VMSTATE_INT32(devid, CprVFIODevice),
37 VMSTATE_UINT32(ioas_id, CprVFIODevice),
38 VMSTATE_UINT32(hwpt_id, CprVFIODevice),
39 VMSTATE_END_OF_LIST()
40 }
41 };
42
43 const VMStateDescription vmstate_cpr_vfio_devices = {
44 .name = CPR_STATE "/vfio devices",
45 .version_id = 1,
46 .minimum_version_id = 1,
47 .fields = (const VMStateField[]){
48 VMSTATE_QLIST_V(vfio_devices, CprState, 1, vmstate_cpr_vfio_device,
49 CprVFIODevice, next),
50 VMSTATE_END_OF_LIST()
51 }
52 };
53
54 static void vfio_cpr_save_device(VFIODevice *vbasedev)
55 {
56 CprVFIODevice *elem = g_new0(CprVFIODevice, 1);
57
58 elem->name = g_strdup(vbasedev->name);
59 elem->namelen = strlen(vbasedev->name) + 1;
60 elem->ioas_id = vbasedev->cpr.ioas_id;
61 elem->devid = vbasedev->devid;
62 elem->hwpt_id = vbasedev->cpr.hwpt_id;
63 QLIST_INSERT_HEAD(&cpr_state.vfio_devices, elem, next);
64 }
65
66 static CprVFIODevice *find_device(const char *name)
67 {
68 CprVFIODeviceList *head = &cpr_state.vfio_devices;
69 CprVFIODevice *elem;
70
71 QLIST_FOREACH(elem, head, next) {
72 if (!strcmp(elem->name, name)) {
73 return elem;
74 }
75 }
76 return NULL;
77 }
78
79 static void vfio_cpr_delete_device(const char *name)
80 {
81 CprVFIODevice *elem = find_device(name);
82
83 if (elem) {
84 QLIST_REMOVE(elem, next);
85 g_free(elem->name);
86 g_free(elem);
87 }
88 }
89
90 static bool vfio_cpr_find_device(VFIODevice *vbasedev)
91 {
92 CprVFIODevice *elem = find_device(vbasedev->name);
93
94 if (elem) {
95 vbasedev->cpr.ioas_id = elem->ioas_id;
96 vbasedev->devid = elem->devid;
97 vbasedev->cpr.hwpt_id = elem->hwpt_id;
98 trace_vfio_cpr_find_device(elem->ioas_id, elem->devid, elem->hwpt_id);
99 return true;
100 }
101 return false;
102 }
103
104 static bool vfio_cpr_supported(IOMMUFDBackend *be, Error **errp)
105 {
106 if (!iommufd_change_process_capable(be)) {
107 if (errp) {
108 error_setg(errp, "vfio iommufd backend does not support "
109 "IOMMU_IOAS_CHANGE_PROCESS");
110 }
111 return false;
112 }
113 return true;
114 }
115
116 static int iommufd_cpr_pre_save(void *opaque)
117 {
118 IOMMUFDBackend *be = opaque;
119
120 /*
121 * The process has not changed yet, but proactively try the ioctl,
122 * and it will fail if any DMA mappings are not supported.
123 */
124 if (!iommufd_change_process_capable(be)) {
125 error_report("some memory regions do not support "
126 "IOMMU_IOAS_CHANGE_PROCESS");
127 return -1;
128 }
129 return 0;
130 }
131
132 static int iommufd_cpr_post_load(void *opaque, int version_id)
133 {
134 IOMMUFDBackend *be = opaque;
135 Error *local_err = NULL;
136
137 if (!iommufd_change_process(be, &local_err)) {
138 error_report_err(local_err);
139 return -1;
140 }
141 return 0;
142 }
143
144 static const VMStateDescription iommufd_cpr_vmstate = {
145 .name = "iommufd",
146 .version_id = 0,
147 .minimum_version_id = 0,
148 .pre_save = iommufd_cpr_pre_save,
149 .post_load = iommufd_cpr_post_load,
150 .needed = cpr_incoming_needed,
151 .fields = (VMStateField[]) {
152 VMSTATE_END_OF_LIST()
153 }
154 };
155
156 bool vfio_iommufd_cpr_register_iommufd(IOMMUFDBackend *be, Error **errp)
157 {
158 Error **cpr_blocker = &be->cpr_blocker;
159
160 if (!vfio_cpr_supported(be, cpr_blocker)) {
161 return migrate_add_blocker_modes(cpr_blocker,
162 BIT(MIG_MODE_CPR_TRANSFER) | BIT(MIG_MODE_CPR_EXEC),
163 errp) == 0;
164 }
165
166 vmstate_register(NULL, -1, &iommufd_cpr_vmstate, be);
167
168 return true;
169 }
170
171 void vfio_iommufd_cpr_unregister_iommufd(IOMMUFDBackend *be)
172 {
173 vmstate_unregister(NULL, &iommufd_cpr_vmstate, be);
174 migrate_del_blocker(&be->cpr_blocker);
175 }
176
177 bool vfio_iommufd_cpr_register_container(VFIOIOMMUFDContainer *container,
178 Error **errp)
179 {
180 VFIOContainer *bcontainer = VFIO_IOMMU(container);
181
182 migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier,
183 vfio_cpr_reboot_notifier,
184 MIG_MODE_CPR_REBOOT);
185
186 vfio_cpr_add_kvm_notifier();
187
188 return true;
189 }
190
191 void vfio_iommufd_cpr_unregister_container(VFIOIOMMUFDContainer *container)
192 {
193 VFIOContainer *bcontainer = VFIO_IOMMU(container);
194
195 migration_remove_notifier(&bcontainer->cpr_reboot_notifier);
196 }
197
198 void vfio_iommufd_cpr_register_device(VFIODevice *vbasedev)
199 {
200 if (!cpr_is_incoming()) {
201 /*
202 * Beware fd may have already been saved by vfio_device_set_fd,
203 * so call resave to avoid a duplicate entry.
204 */
205 cpr_resave_fd(vbasedev->name, 0, vbasedev->fd);
206 vfio_cpr_save_device(vbasedev);
207 }
208 }
209
210 void vfio_iommufd_cpr_unregister_device(VFIODevice *vbasedev)
211 {
212 cpr_delete_fd(vbasedev->name, 0);
213 vfio_cpr_delete_device(vbasedev->name);
214 }
215
216 void vfio_cpr_load_device(VFIODevice *vbasedev)
217 {
218 if (cpr_is_incoming()) {
219 bool ret = vfio_cpr_find_device(vbasedev);
220 g_assert(ret);
221
222 if (vbasedev->fd < 0) {
223 vbasedev->fd = cpr_find_fd(vbasedev->name, 0);
224 }
225 }
226 }