]>
Commit | Line | Data |
---|---|---|
2fe2942c TK |
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 | ||
b7d89466 | 13 | #include "qemu/osdep.h" |
2fe2942c TK |
14 | #include <linux/vfio.h> |
15 | #include <sys/ioctl.h> | |
2fe2942c | 16 | #include "qapi/error.h" |
2fe2942c TK |
17 | #include "hw/vfio/vfio.h" |
18 | #include "hw/vfio/vfio-common.h" | |
19 | #include "hw/s390x/ap-device.h" | |
20 | #include "qemu/error-report.h" | |
1360b2ad TK |
21 | #include "qemu/event_notifier.h" |
22 | #include "qemu/main-loop.h" | |
0b8fa32f | 23 | #include "qemu/module.h" |
2fe2942c TK |
24 | #include "qemu/option.h" |
25 | #include "qemu/config-file.h" | |
67043607 | 26 | #include "kvm/kvm_s390x.h" |
d6454270 | 27 | #include "migration/vmstate.h" |
a27bd6c7 | 28 | #include "hw/qdev-properties.h" |
2fe2942c TK |
29 | #include "hw/s390x/ap-bridge.h" |
30 | #include "exec/address-spaces.h" | |
db1015e9 | 31 | #include "qom/object.h" |
2fe2942c | 32 | |
8b3a1ee5 | 33 | #define TYPE_VFIO_AP_DEVICE "vfio-ap" |
2fe2942c | 34 | |
db1015e9 | 35 | struct VFIOAPDevice { |
2fe2942c TK |
36 | APDevice apdev; |
37 | VFIODevice vdev; | |
1360b2ad | 38 | EventNotifier req_notifier; |
db1015e9 | 39 | }; |
2fe2942c | 40 | |
8063396b | 41 | OBJECT_DECLARE_SIMPLE_TYPE(VFIOAPDevice, VFIO_AP_DEVICE) |
2fe2942c TK |
42 | |
43 | static void vfio_ap_compute_needs_reset(VFIODevice *vdev) | |
44 | { | |
45 | vdev->needs_reset = false; | |
46 | } | |
47 | ||
48 | /* | |
49 | * We don't need vfio_hot_reset_multi and vfio_eoi operations for | |
50 | * vfio-ap device now. | |
51 | */ | |
52 | struct VFIODeviceOps vfio_ap_ops = { | |
53 | .vfio_compute_needs_reset = vfio_ap_compute_needs_reset, | |
54 | }; | |
55 | ||
1360b2ad TK |
56 | static void vfio_ap_req_notifier_handler(void *opaque) |
57 | { | |
58 | VFIOAPDevice *vapdev = opaque; | |
59 | Error *err = NULL; | |
60 | ||
61 | if (!event_notifier_test_and_clear(&vapdev->req_notifier)) { | |
62 | return; | |
63 | } | |
64 | ||
65 | qdev_unplug(DEVICE(vapdev), &err); | |
66 | ||
67 | if (err) { | |
68 | warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name); | |
69 | } | |
70 | } | |
71 | ||
72 | static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, | |
73 | unsigned int irq, Error **errp) | |
74 | { | |
75 | int fd; | |
76 | size_t argsz; | |
77 | IOHandler *fd_read; | |
78 | EventNotifier *notifier; | |
79 | struct vfio_irq_info *irq_info; | |
80 | VFIODevice *vdev = &vapdev->vdev; | |
81 | ||
82 | switch (irq) { | |
83 | case VFIO_AP_REQ_IRQ_INDEX: | |
84 | notifier = &vapdev->req_notifier; | |
85 | fd_read = vfio_ap_req_notifier_handler; | |
86 | break; | |
87 | default: | |
88 | error_setg(errp, "vfio: Unsupported device irq(%d)", irq); | |
89 | return; | |
90 | } | |
91 | ||
92 | if (vdev->num_irqs < irq + 1) { | |
93 | error_setg(errp, "vfio: IRQ %u not available (number of irqs %u)", | |
94 | irq, vdev->num_irqs); | |
95 | return; | |
96 | } | |
97 | ||
98 | argsz = sizeof(*irq_info); | |
99 | irq_info = g_malloc0(argsz); | |
100 | irq_info->index = irq; | |
101 | irq_info->argsz = argsz; | |
102 | ||
103 | if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, | |
104 | irq_info) < 0 || irq_info->count < 1) { | |
105 | error_setg_errno(errp, errno, "vfio: Error getting irq info"); | |
106 | goto out_free_info; | |
107 | } | |
108 | ||
109 | if (event_notifier_init(notifier, 0)) { | |
110 | error_setg_errno(errp, errno, | |
111 | "vfio: Unable to init event notifier for irq (%d)", | |
112 | irq); | |
113 | goto out_free_info; | |
114 | } | |
115 | ||
116 | fd = event_notifier_get_fd(notifier); | |
117 | qemu_set_fd_handler(fd, fd_read, NULL, vapdev); | |
118 | ||
119 | if (vfio_set_irq_signaling(vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd, | |
120 | errp)) { | |
121 | qemu_set_fd_handler(fd, NULL, NULL, vapdev); | |
122 | event_notifier_cleanup(notifier); | |
123 | } | |
124 | ||
125 | out_free_info: | |
126 | g_free(irq_info); | |
127 | ||
128 | } | |
129 | ||
130 | static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev, | |
131 | unsigned int irq) | |
132 | { | |
133 | Error *err = NULL; | |
134 | EventNotifier *notifier; | |
135 | ||
136 | switch (irq) { | |
137 | case VFIO_AP_REQ_IRQ_INDEX: | |
138 | notifier = &vapdev->req_notifier; | |
139 | break; | |
140 | default: | |
141 | error_report("vfio: Unsupported device irq(%d)", irq); | |
142 | return; | |
143 | } | |
144 | ||
145 | if (vfio_set_irq_signaling(&vapdev->vdev, irq, 0, | |
146 | VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { | |
147 | warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name); | |
148 | } | |
149 | ||
150 | qemu_set_fd_handler(event_notifier_get_fd(notifier), | |
151 | NULL, NULL, vapdev); | |
152 | event_notifier_cleanup(notifier); | |
153 | } | |
154 | ||
2fe2942c TK |
155 | static void vfio_ap_realize(DeviceState *dev, Error **errp) |
156 | { | |
157 | int ret; | |
1360b2ad | 158 | Error *err = NULL; |
2fe2942c TK |
159 | APDevice *apdev = AP_DEVICE(dev); |
160 | VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); | |
c95d128e | 161 | VFIODevice *vbasedev = &vapdev->vdev; |
2fe2942c | 162 | |
c95d128e EA |
163 | vbasedev->name = g_path_get_basename(vbasedev->sysfsdev); |
164 | vbasedev->ops = &vfio_ap_ops; | |
165 | vbasedev->type = VFIO_DEVICE_TYPE_AP; | |
166 | vbasedev->dev = dev; | |
2fe2942c | 167 | |
1883e8fc | 168 | /* |
aff92b82 DH |
169 | * vfio-ap devices operate in a way compatible with discarding of |
170 | * memory in RAM blocks, as no pages are pinned in the host. | |
1883e8fc | 171 | * This needs to be set before vfio_get_device() for vfio common to |
aff92b82 | 172 | * handle ram_block_discard_disable(). |
1883e8fc | 173 | */ |
aff92b82 | 174 | vapdev->vdev.ram_block_discard_allowed = true; |
1883e8fc | 175 | |
c95d128e EA |
176 | ret = vfio_attach_device(vbasedev->name, vbasedev, |
177 | &address_space_memory, errp); | |
2fe2942c | 178 | if (ret) { |
c95d128e | 179 | goto error; |
2fe2942c TK |
180 | } |
181 | ||
1360b2ad TK |
182 | vfio_ap_register_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX, &err); |
183 | if (err) { | |
184 | /* | |
185 | * Report this error, but do not make it a failing condition. | |
186 | * Lack of this IRQ in the host does not prevent normal operation. | |
187 | */ | |
188 | error_report_err(err); | |
189 | } | |
190 | ||
2fe2942c TK |
191 | return; |
192 | ||
c95d128e EA |
193 | error: |
194 | error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->name); | |
195 | g_free(vbasedev->name); | |
2fe2942c TK |
196 | } |
197 | ||
b69c3c21 | 198 | static void vfio_ap_unrealize(DeviceState *dev) |
2fe2942c TK |
199 | { |
200 | APDevice *apdev = AP_DEVICE(dev); | |
201 | VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); | |
2fe2942c | 202 | |
1360b2ad | 203 | vfio_ap_unregister_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX); |
c95d128e EA |
204 | vfio_detach_device(&vapdev->vdev); |
205 | g_free(vapdev->vdev.name); | |
2fe2942c TK |
206 | } |
207 | ||
208 | static Property vfio_ap_properties[] = { | |
209 | DEFINE_PROP_STRING("sysfsdev", VFIOAPDevice, vdev.sysfsdev), | |
210 | DEFINE_PROP_END_OF_LIST(), | |
211 | }; | |
212 | ||
213 | static void vfio_ap_reset(DeviceState *dev) | |
214 | { | |
215 | int ret; | |
216 | APDevice *apdev = AP_DEVICE(dev); | |
217 | VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); | |
218 | ||
219 | ret = ioctl(vapdev->vdev.fd, VFIO_DEVICE_RESET); | |
220 | if (ret) { | |
221 | error_report("%s: failed to reset %s device: %s", __func__, | |
ab17b1fc | 222 | vapdev->vdev.name, strerror(errno)); |
2fe2942c TK |
223 | } |
224 | } | |
225 | ||
226 | static const VMStateDescription vfio_ap_vmstate = { | |
da56e330 | 227 | .name = "vfio-ap", |
2fe2942c TK |
228 | .unmigratable = 1, |
229 | }; | |
230 | ||
231 | static void vfio_ap_class_init(ObjectClass *klass, void *data) | |
232 | { | |
233 | DeviceClass *dc = DEVICE_CLASS(klass); | |
234 | ||
4f67d30b | 235 | device_class_set_props(dc, vfio_ap_properties); |
2fe2942c TK |
236 | dc->vmsd = &vfio_ap_vmstate; |
237 | dc->desc = "VFIO-based AP device assignment"; | |
238 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); | |
239 | dc->realize = vfio_ap_realize; | |
240 | dc->unrealize = vfio_ap_unrealize; | |
374b78e3 | 241 | dc->hotpluggable = true; |
2fe2942c TK |
242 | dc->reset = vfio_ap_reset; |
243 | dc->bus_type = TYPE_AP_BUS; | |
244 | } | |
245 | ||
246 | static const TypeInfo vfio_ap_info = { | |
8b3a1ee5 | 247 | .name = TYPE_VFIO_AP_DEVICE, |
fab2afff | 248 | .parent = TYPE_AP_DEVICE, |
2fe2942c TK |
249 | .instance_size = sizeof(VFIOAPDevice), |
250 | .class_init = vfio_ap_class_init, | |
251 | }; | |
252 | ||
253 | static void vfio_ap_type_init(void) | |
254 | { | |
255 | type_register_static(&vfio_ap_info); | |
256 | } | |
257 | ||
258 | type_init(vfio_ap_type_init) |