master
c 362 lines 9.98 KB
Raw
1 /*
2 * VFIO based AP matrix device assignment
3 *
4 * Copyright 2018 IBM Corp.
5 * Author(s): Tony Krowiak <akrowiak@linux.ibm.com>
6 * Halil Pasic <pasic@linux.ibm.com>
7 *
8 * This work is licensed under the terms of the GNU GPL, version 2 or (at
9 * your option) any later version. See the COPYING file in the top-level
10 * directory.
11 */
12
13 #include "qemu/osdep.h"
14 #include <linux/vfio.h>
15 #include <sys/ioctl.h>
16 #include "qapi/error.h"
17 #include "hw/vfio/vfio-device.h"
18 #include "system/iommufd.h"
19 #include "hw/s390x/ap-device.h"
20 #include "hw/s390x/css.h"
21 #include "qemu/error-report.h"
22 #include "qemu/event_notifier.h"
23 #include "qemu/lockable.h"
24 #include "qemu/main-loop.h"
25 #include "qemu/module.h"
26 #include "qemu/option.h"
27 #include "qemu/config-file.h"
28 #include "target/s390x/kvm/kvm_s390x.h"
29 #include "migration/vmstate.h"
30 #include "hw/core/qdev-properties.h"
31 #include "hw/s390x/ap-bridge.h"
32 #include "system/address-spaces.h"
33 #include "qom/object.h"
34
35 #define TYPE_VFIO_AP_DEVICE "vfio-ap"
36
37 struct VFIOAPDevice {
38 APDevice apdev;
39 VFIODevice vdev;
40 EventNotifier req_notifier;
41 EventNotifier cfg_notifier;
42 };
43
44 typedef struct APConfigChgEvent {
45 QTAILQ_ENTRY(APConfigChgEvent) next;
46 } APConfigChgEvent;
47
48 static QTAILQ_HEAD(, APConfigChgEvent) cfg_chg_events =
49 QTAILQ_HEAD_INITIALIZER(cfg_chg_events);
50
51 static QemuMutex cfg_chg_events_lock;
52
53 static void __attribute__((constructor)) vfio_ap_global_init(void)
54 {
55 qemu_mutex_init(&cfg_chg_events_lock);
56 }
57
58 OBJECT_DECLARE_SIMPLE_TYPE(VFIOAPDevice, VFIO_AP_DEVICE)
59
60 static void vfio_ap_compute_needs_reset(VFIODevice *vdev)
61 {
62 vdev->needs_reset = false;
63 }
64
65 /*
66 * We don't need vfio_hot_reset_multi and vfio_eoi operations for
67 * vfio-ap device now.
68 */
69 struct VFIODeviceOps vfio_ap_ops = {
70 .vfio_compute_needs_reset = vfio_ap_compute_needs_reset,
71 };
72
73 static void vfio_ap_req_notifier_handler(void *opaque)
74 {
75 VFIOAPDevice *vapdev = opaque;
76 Error *err = NULL;
77
78 if (!event_notifier_test_and_clear(&vapdev->req_notifier)) {
79 return;
80 }
81
82 qdev_unplug(DEVICE(vapdev), &err);
83
84 if (err) {
85 warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name);
86 }
87 }
88
89 static void vfio_ap_cfg_chg_notifier_handler(void *opaque)
90 {
91 APConfigChgEvent *cfg_chg_event;
92 VFIOAPDevice *vapdev = opaque;
93
94 if (!event_notifier_test_and_clear(&vapdev->cfg_notifier)) {
95 return;
96 }
97
98 cfg_chg_event = g_new0(APConfigChgEvent, 1);
99
100 WITH_QEMU_LOCK_GUARD(&cfg_chg_events_lock) {
101 QTAILQ_INSERT_TAIL(&cfg_chg_events, cfg_chg_event, next);
102 }
103
104 css_generate_css_crws(0);
105
106 }
107
108 int ap_chsc_sei_nt0_get_event(void *res)
109 {
110 ChscSeiNt0Res *nt0_res = (ChscSeiNt0Res *)res;
111 APConfigChgEvent *cfg_chg_event;
112
113 WITH_QEMU_LOCK_GUARD(&cfg_chg_events_lock) {
114 if (QTAILQ_EMPTY(&cfg_chg_events)) {
115 return EVENT_INFORMATION_NOT_STORED;
116 }
117
118 cfg_chg_event = QTAILQ_FIRST(&cfg_chg_events);
119 QTAILQ_REMOVE(&cfg_chg_events, cfg_chg_event, next);
120 }
121
122 memset(nt0_res, 0, sizeof(*nt0_res));
123 g_free(cfg_chg_event);
124 nt0_res->flags |= PENDING_EVENT_INFO_BITMASK;
125 nt0_res->length = sizeof(ChscSeiNt0Res);
126 nt0_res->code = NT0_RES_RESPONSE_CODE;
127 nt0_res->nt = NT0_RES_NT_DEFAULT;
128 nt0_res->rs = NT0_RES_RS_AP_CHANGE;
129 nt0_res->cc = NT0_RES_CC_AP_CHANGE;
130
131 return EVENT_INFORMATION_STORED;
132 }
133
134 bool ap_chsc_sei_nt0_have_event(void)
135 {
136 QEMU_LOCK_GUARD(&cfg_chg_events_lock);
137 return !QTAILQ_EMPTY(&cfg_chg_events);
138 }
139
140 static bool vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev,
141 unsigned int irq, Error **errp)
142 {
143 int fd;
144 int ret;
145 IOHandler *fd_read;
146 EventNotifier *notifier;
147 struct vfio_irq_info irq_info;
148 VFIODevice *vdev = &vapdev->vdev;
149
150 switch (irq) {
151 case VFIO_AP_REQ_IRQ_INDEX:
152 notifier = &vapdev->req_notifier;
153 fd_read = vfio_ap_req_notifier_handler;
154 break;
155 case VFIO_AP_CFG_CHG_IRQ_INDEX:
156 notifier = &vapdev->cfg_notifier;
157 fd_read = vfio_ap_cfg_chg_notifier_handler;
158 break;
159 default:
160 error_setg(errp, "vfio: Unsupported device irq(%d)", irq);
161 return false;
162 }
163
164 if (vdev->num_irqs < irq + 1) {
165 error_setg(errp, "vfio: IRQ %u not available (number of irqs %u)",
166 irq, vdev->num_irqs);
167 return false;
168 }
169
170 ret = vfio_device_get_irq_info(vdev, irq, &irq_info);
171
172 if (ret < 0) {
173 error_setg_errno(errp, -ret, "vfio: Error getting irq info");
174 return false;
175 }
176
177 if (irq_info.count < 1) {
178 error_setg(errp, "vfio: Error getting irq info, count=0");
179 return false;
180 }
181
182 if (event_notifier_init(notifier, 0) < 0) {
183 error_setg_errno(errp, errno,
184 "vfio: Unable to init event notifier for irq (%d)",
185 irq);
186 return false;
187 }
188
189 fd = event_notifier_get_fd(notifier);
190 qemu_set_fd_handler(fd, fd_read, NULL, vapdev);
191
192 if (!vfio_device_irq_set_signaling(vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd,
193 errp)) {
194 qemu_set_fd_handler(fd, NULL, NULL, vapdev);
195 event_notifier_cleanup(notifier);
196 return false;
197 }
198
199 return true;
200 }
201
202 static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev,
203 unsigned int irq)
204 {
205 Error *err = NULL;
206 EventNotifier *notifier;
207
208 switch (irq) {
209 case VFIO_AP_REQ_IRQ_INDEX:
210 notifier = &vapdev->req_notifier;
211 break;
212 case VFIO_AP_CFG_CHG_IRQ_INDEX:
213 notifier = &vapdev->cfg_notifier;
214 break;
215 default:
216 error_report("vfio: Unsupported device irq(%d)", irq);
217 return;
218 }
219
220 if (!vfio_device_irq_set_signaling(&vapdev->vdev, irq, 0,
221 VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) {
222 warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name);
223 }
224
225 qemu_set_fd_handler(event_notifier_get_fd(notifier),
226 NULL, NULL, vapdev);
227 event_notifier_cleanup(notifier);
228 }
229
230 static void vfio_ap_realize(DeviceState *dev, Error **errp)
231 {
232 ERRP_GUARD();
233 Error *err = NULL;
234 VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev);
235 VFIODevice *vbasedev = &vapdev->vdev;
236
237 if (!vfio_device_get_name(vbasedev, errp)) {
238 return;
239 }
240
241 if (!vfio_device_attach(vbasedev->name, vbasedev,
242 &address_space_memory, errp)) {
243 goto error;
244 }
245
246 if (!vfio_ap_register_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX, &err)) {
247 /*
248 * Report this error, but do not make it a failing condition.
249 * Lack of this IRQ in the host does not prevent normal operation.
250 */
251 warn_report_err(err);
252 }
253
254 if (!vfio_ap_register_irq_notifier(vapdev, VFIO_AP_CFG_CHG_IRQ_INDEX, &err))
255 {
256 /*
257 * Report this error, but do not make it a failing condition.
258 * Lack of this IRQ in the host does not prevent normal operation.
259 */
260 warn_report_err(err);
261 }
262
263 return;
264
265 error:
266 error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->name);
267 vfio_device_free_name(vbasedev);
268 }
269
270 static void vfio_ap_unrealize(DeviceState *dev)
271 {
272 VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev);
273
274 vfio_ap_unregister_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX);
275 vfio_ap_unregister_irq_notifier(vapdev, VFIO_AP_CFG_CHG_IRQ_INDEX);
276 vfio_device_detach(&vapdev->vdev);
277 vfio_device_free_name(&vapdev->vdev);
278 }
279
280 static const Property vfio_ap_properties[] = {
281 DEFINE_PROP_STRING("sysfsdev", VFIOAPDevice, vdev.sysfsdev),
282 DEFINE_PROP_LINK("iommufd", VFIOAPDevice, vdev.iommufd,
283 TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *),
284 };
285
286 static void vfio_ap_reset(DeviceState *dev)
287 {
288 int ret;
289 VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev);
290
291 ret = ioctl(vapdev->vdev.fd, VFIO_DEVICE_RESET);
292 if (ret) {
293 error_report("%s: failed to reset %s device: %s", __func__,
294 vapdev->vdev.name, strerror(errno));
295 }
296 }
297
298 static const VMStateDescription vfio_ap_vmstate = {
299 .name = "vfio-ap",
300 .unmigratable = 1,
301 };
302
303 static void vfio_ap_instance_init(Object *obj)
304 {
305 VFIOAPDevice *vapdev = VFIO_AP_DEVICE(obj);
306 VFIODevice *vbasedev = &vapdev->vdev;
307
308 /*
309 * vfio-ap devices operate in a way compatible with discarding of
310 * memory in RAM blocks, as no pages are pinned in the host.
311 * This needs to be set before vfio_get_device() for vfio common to
312 * handle ram_block_discard_disable().
313 */
314 vfio_device_init(vbasedev, VFIO_DEVICE_TYPE_AP, &vfio_ap_ops,
315 DEVICE(vapdev), true);
316
317 /* AP device is mdev type device */
318 vbasedev->mdev = true;
319 }
320
321 static void vfio_ap_set_fd(Object *obj, const char *str, Error **errp)
322 {
323 vfio_device_set_fd(&VFIO_AP_DEVICE(obj)->vdev, str, errp);
324 }
325
326 static void vfio_ap_class_init(ObjectClass *klass, const void *data)
327 {
328 DeviceClass *dc = DEVICE_CLASS(klass);
329
330 device_class_set_props(dc, vfio_ap_properties);
331 object_class_property_add_str(klass, "fd", NULL, vfio_ap_set_fd);
332 dc->vmsd = &vfio_ap_vmstate;
333 dc->desc = "VFIO-based AP device assignment";
334 set_bit(DEVICE_CATEGORY_MISC, dc->categories);
335 dc->realize = vfio_ap_realize;
336 dc->unrealize = vfio_ap_unrealize;
337 dc->hotpluggable = true;
338 device_class_set_legacy_reset(dc, vfio_ap_reset);
339 dc->bus_type = TYPE_AP_BUS;
340
341 object_class_property_set_description(klass, /* 3.1 */
342 "sysfsdev",
343 "Host sysfs path of assigned device");
344 object_class_property_set_description(klass, /* 9.0 */
345 "iommufd",
346 "Set host IOMMUFD backend device");
347 }
348
349 static const TypeInfo vfio_ap_info = {
350 .name = TYPE_VFIO_AP_DEVICE,
351 .parent = TYPE_AP_DEVICE,
352 .instance_size = sizeof(VFIOAPDevice),
353 .instance_init = vfio_ap_instance_init,
354 .class_init = vfio_ap_class_init,
355 };
356
357 static void vfio_ap_type_init(void)
358 {
359 type_register_static(&vfio_ap_info);
360 }
361
362 type_init(vfio_ap_type_init)