hw/ppc: Clean up local variable shadowing in _FDT helper routine
[qemu/kevin.git] / hw / display / vhost-user-gpu.c
blob1150521d9db744009d3c1c364882f88cd68cbb63
1 /*
2 * vhost-user GPU Device
4 * Copyright Red Hat, Inc. 2018
6 * Authors:
7 * Marc-André Lureau <marcandre.lureau@redhat.com>
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
13 #include "qemu/osdep.h"
14 #include "qemu/error-report.h"
15 #include "qemu/sockets.h"
16 #include "hw/qdev-properties.h"
17 #include "hw/virtio/virtio-gpu.h"
18 #include "chardev/char-fe.h"
19 #include "qapi/error.h"
20 #include "migration/blocker.h"
22 typedef enum VhostUserGpuRequest {
23 VHOST_USER_GPU_NONE = 0,
24 VHOST_USER_GPU_GET_PROTOCOL_FEATURES,
25 VHOST_USER_GPU_SET_PROTOCOL_FEATURES,
26 VHOST_USER_GPU_GET_DISPLAY_INFO,
27 VHOST_USER_GPU_CURSOR_POS,
28 VHOST_USER_GPU_CURSOR_POS_HIDE,
29 VHOST_USER_GPU_CURSOR_UPDATE,
30 VHOST_USER_GPU_SCANOUT,
31 VHOST_USER_GPU_UPDATE,
32 VHOST_USER_GPU_DMABUF_SCANOUT,
33 VHOST_USER_GPU_DMABUF_UPDATE,
34 VHOST_USER_GPU_GET_EDID,
35 VHOST_USER_GPU_DMABUF_SCANOUT2,
36 } VhostUserGpuRequest;
38 typedef struct VhostUserGpuDisplayInfoReply {
39 struct virtio_gpu_resp_display_info info;
40 } VhostUserGpuDisplayInfoReply;
42 typedef struct VhostUserGpuCursorPos {
43 uint32_t scanout_id;
44 uint32_t x;
45 uint32_t y;
46 } QEMU_PACKED VhostUserGpuCursorPos;
48 typedef struct VhostUserGpuCursorUpdate {
49 VhostUserGpuCursorPos pos;
50 uint32_t hot_x;
51 uint32_t hot_y;
52 uint32_t data[64 * 64];
53 } QEMU_PACKED VhostUserGpuCursorUpdate;
55 typedef struct VhostUserGpuScanout {
56 uint32_t scanout_id;
57 uint32_t width;
58 uint32_t height;
59 } QEMU_PACKED VhostUserGpuScanout;
61 typedef struct VhostUserGpuUpdate {
62 uint32_t scanout_id;
63 uint32_t x;
64 uint32_t y;
65 uint32_t width;
66 uint32_t height;
67 uint8_t data[];
68 } QEMU_PACKED VhostUserGpuUpdate;
70 typedef struct VhostUserGpuDMABUFScanout {
71 uint32_t scanout_id;
72 uint32_t x;
73 uint32_t y;
74 uint32_t width;
75 uint32_t height;
76 uint32_t fd_width;
77 uint32_t fd_height;
78 uint32_t fd_stride;
79 uint32_t fd_flags;
80 int fd_drm_fourcc;
81 } QEMU_PACKED VhostUserGpuDMABUFScanout;
83 typedef struct VhostUserGpuDMABUFScanout2 {
84 struct VhostUserGpuDMABUFScanout dmabuf_scanout;
85 uint64_t modifier;
86 } QEMU_PACKED VhostUserGpuDMABUFScanout2;
88 typedef struct VhostUserGpuEdidRequest {
89 uint32_t scanout_id;
90 } QEMU_PACKED VhostUserGpuEdidRequest;
92 typedef struct VhostUserGpuMsg {
93 uint32_t request; /* VhostUserGpuRequest */
94 uint32_t flags;
95 uint32_t size; /* the following payload size */
96 union {
97 VhostUserGpuCursorPos cursor_pos;
98 VhostUserGpuCursorUpdate cursor_update;
99 VhostUserGpuScanout scanout;
100 VhostUserGpuUpdate update;
101 VhostUserGpuDMABUFScanout dmabuf_scanout;
102 VhostUserGpuDMABUFScanout2 dmabuf_scanout2;
103 VhostUserGpuEdidRequest edid_req;
104 struct virtio_gpu_resp_edid resp_edid;
105 struct virtio_gpu_resp_display_info display_info;
106 uint64_t u64;
107 } payload;
108 } QEMU_PACKED VhostUserGpuMsg;
110 static VhostUserGpuMsg m __attribute__ ((unused));
111 #define VHOST_USER_GPU_HDR_SIZE \
112 (sizeof(m.request) + sizeof(m.size) + sizeof(m.flags))
114 #define VHOST_USER_GPU_MSG_FLAG_REPLY 0x4
116 #define VHOST_USER_GPU_PROTOCOL_F_EDID 0
117 #define VHOST_USER_GPU_PROTOCOL_F_DMABUF2 1
119 static void vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked);
121 static void
122 vhost_user_gpu_handle_cursor(VhostUserGPU *g, VhostUserGpuMsg *msg)
124 VhostUserGpuCursorPos *pos = &msg->payload.cursor_pos;
125 struct virtio_gpu_scanout *s;
127 if (pos->scanout_id >= g->parent_obj.conf.max_outputs) {
128 return;
130 s = &g->parent_obj.scanout[pos->scanout_id];
132 if (msg->request == VHOST_USER_GPU_CURSOR_UPDATE) {
133 VhostUserGpuCursorUpdate *up = &msg->payload.cursor_update;
134 if (!s->current_cursor) {
135 s->current_cursor = cursor_alloc(64, 64);
138 s->current_cursor->hot_x = up->hot_x;
139 s->current_cursor->hot_y = up->hot_y;
141 memcpy(s->current_cursor->data, up->data,
142 64 * 64 * sizeof(uint32_t));
144 dpy_cursor_define(s->con, s->current_cursor);
147 dpy_mouse_set(s->con, pos->x, pos->y,
148 msg->request != VHOST_USER_GPU_CURSOR_POS_HIDE);
151 static void
152 vhost_user_gpu_send_msg(VhostUserGPU *g, const VhostUserGpuMsg *msg)
154 qemu_chr_fe_write(&g->vhost_chr, (uint8_t *)msg,
155 VHOST_USER_GPU_HDR_SIZE + msg->size);
158 static void
159 vhost_user_gpu_unblock(VhostUserGPU *g)
161 VhostUserGpuMsg msg = {
162 .request = VHOST_USER_GPU_DMABUF_UPDATE,
163 .flags = VHOST_USER_GPU_MSG_FLAG_REPLY,
166 vhost_user_gpu_send_msg(g, &msg);
169 static void
170 vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg)
172 QemuConsole *con = NULL;
173 struct virtio_gpu_scanout *s;
175 switch (msg->request) {
176 case VHOST_USER_GPU_GET_PROTOCOL_FEATURES: {
177 VhostUserGpuMsg reply = {
178 .request = msg->request,
179 .flags = VHOST_USER_GPU_MSG_FLAG_REPLY,
180 .size = sizeof(uint64_t),
181 .payload = {
182 .u64 = (1 << VHOST_USER_GPU_PROTOCOL_F_EDID) |
183 (1 << VHOST_USER_GPU_PROTOCOL_F_DMABUF2)
187 vhost_user_gpu_send_msg(g, &reply);
188 break;
190 case VHOST_USER_GPU_SET_PROTOCOL_FEATURES: {
191 break;
193 case VHOST_USER_GPU_GET_DISPLAY_INFO: {
194 struct virtio_gpu_resp_display_info display_info = { {} };
195 VhostUserGpuMsg reply = {
196 .request = msg->request,
197 .flags = VHOST_USER_GPU_MSG_FLAG_REPLY,
198 .size = sizeof(struct virtio_gpu_resp_display_info),
201 display_info.hdr.type = VIRTIO_GPU_RESP_OK_DISPLAY_INFO;
202 virtio_gpu_base_fill_display_info(VIRTIO_GPU_BASE(g), &display_info);
203 memcpy(&reply.payload.display_info, &display_info,
204 sizeof(display_info));
205 vhost_user_gpu_send_msg(g, &reply);
206 break;
208 case VHOST_USER_GPU_GET_EDID: {
209 VhostUserGpuEdidRequest *m = &msg->payload.edid_req;
210 struct virtio_gpu_resp_edid resp = { {} };
211 VhostUserGpuMsg reply = {
212 .request = msg->request,
213 .flags = VHOST_USER_GPU_MSG_FLAG_REPLY,
214 .size = sizeof(reply.payload.resp_edid),
217 if (m->scanout_id >= g->parent_obj.conf.max_outputs) {
218 error_report("invalid scanout: %d", m->scanout_id);
219 break;
222 resp.hdr.type = VIRTIO_GPU_RESP_OK_EDID;
223 virtio_gpu_base_generate_edid(VIRTIO_GPU_BASE(g), m->scanout_id, &resp);
224 memcpy(&reply.payload.resp_edid, &resp, sizeof(resp));
225 vhost_user_gpu_send_msg(g, &reply);
226 break;
228 case VHOST_USER_GPU_SCANOUT: {
229 VhostUserGpuScanout *m = &msg->payload.scanout;
231 if (m->scanout_id >= g->parent_obj.conf.max_outputs) {
232 return;
235 g->parent_obj.enable = 1;
236 s = &g->parent_obj.scanout[m->scanout_id];
237 con = s->con;
239 if (m->width == 0) {
240 dpy_gfx_replace_surface(con, NULL);
241 } else {
242 s->ds = qemu_create_displaysurface(m->width, m->height);
243 /* replace surface on next update */
246 break;
248 case VHOST_USER_GPU_DMABUF_SCANOUT2:
249 case VHOST_USER_GPU_DMABUF_SCANOUT: {
250 VhostUserGpuDMABUFScanout *m = &msg->payload.dmabuf_scanout;
251 int fd = qemu_chr_fe_get_msgfd(&g->vhost_chr);
252 QemuDmaBuf *dmabuf;
254 if (m->scanout_id >= g->parent_obj.conf.max_outputs) {
255 error_report("invalid scanout: %d", m->scanout_id);
256 if (fd >= 0) {
257 close(fd);
259 break;
262 g->parent_obj.enable = 1;
263 con = g->parent_obj.scanout[m->scanout_id].con;
264 dmabuf = &g->dmabuf[m->scanout_id];
265 if (dmabuf->fd >= 0) {
266 close(dmabuf->fd);
267 dmabuf->fd = -1;
269 dpy_gl_release_dmabuf(con, dmabuf);
270 if (fd == -1) {
271 dpy_gl_scanout_disable(con);
272 break;
274 *dmabuf = (QemuDmaBuf) {
275 .fd = fd,
276 .width = m->fd_width,
277 .height = m->fd_height,
278 .stride = m->fd_stride,
279 .fourcc = m->fd_drm_fourcc,
280 .y0_top = m->fd_flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP,
282 if (msg->request == VHOST_USER_GPU_DMABUF_SCANOUT2) {
283 VhostUserGpuDMABUFScanout2 *m2 = &msg->payload.dmabuf_scanout2;
284 dmabuf->modifier = m2->modifier;
287 dpy_gl_scanout_dmabuf(con, dmabuf);
288 break;
290 case VHOST_USER_GPU_DMABUF_UPDATE: {
291 VhostUserGpuUpdate *m = &msg->payload.update;
293 if (m->scanout_id >= g->parent_obj.conf.max_outputs ||
294 !g->parent_obj.scanout[m->scanout_id].con) {
295 error_report("invalid scanout update: %d", m->scanout_id);
296 vhost_user_gpu_unblock(g);
297 break;
300 con = g->parent_obj.scanout[m->scanout_id].con;
301 if (!console_has_gl(con)) {
302 error_report("console doesn't support GL!");
303 vhost_user_gpu_unblock(g);
304 break;
306 g->backend_blocked = true;
307 dpy_gl_update(con, m->x, m->y, m->width, m->height);
308 break;
310 case VHOST_USER_GPU_UPDATE: {
311 VhostUserGpuUpdate *m = &msg->payload.update;
313 if (m->scanout_id >= g->parent_obj.conf.max_outputs) {
314 break;
316 s = &g->parent_obj.scanout[m->scanout_id];
317 con = s->con;
318 pixman_image_t *image =
319 pixman_image_create_bits(PIXMAN_x8r8g8b8,
320 m->width,
321 m->height,
322 (uint32_t *)m->data,
323 m->width * 4);
325 pixman_image_composite(PIXMAN_OP_SRC,
326 image, NULL, s->ds->image,
327 0, 0, 0, 0, m->x, m->y, m->width, m->height);
329 pixman_image_unref(image);
330 if (qemu_console_surface(con) != s->ds) {
331 dpy_gfx_replace_surface(con, s->ds);
332 } else {
333 dpy_gfx_update(con, m->x, m->y, m->width, m->height);
335 break;
337 default:
338 g_warning("unhandled message %d %d", msg->request, msg->size);
341 if (con && qemu_console_is_gl_blocked(con)) {
342 vhost_user_gpu_update_blocked(g, true);
346 static void
347 vhost_user_gpu_chr_read(void *opaque)
349 VhostUserGPU *g = opaque;
350 VhostUserGpuMsg *msg = NULL;
351 VhostUserGpuRequest request;
352 uint32_t size, flags;
353 int r;
355 r = qemu_chr_fe_read_all(&g->vhost_chr,
356 (uint8_t *)&request, sizeof(uint32_t));
357 if (r != sizeof(uint32_t)) {
358 error_report("failed to read msg header: %d, %d", r, errno);
359 goto end;
362 r = qemu_chr_fe_read_all(&g->vhost_chr,
363 (uint8_t *)&flags, sizeof(uint32_t));
364 if (r != sizeof(uint32_t)) {
365 error_report("failed to read msg flags");
366 goto end;
369 r = qemu_chr_fe_read_all(&g->vhost_chr,
370 (uint8_t *)&size, sizeof(uint32_t));
371 if (r != sizeof(uint32_t)) {
372 error_report("failed to read msg size");
373 goto end;
376 msg = g_malloc(VHOST_USER_GPU_HDR_SIZE + size);
378 r = qemu_chr_fe_read_all(&g->vhost_chr,
379 (uint8_t *)&msg->payload, size);
380 if (r != size) {
381 error_report("failed to read msg payload %d != %d", r, size);
382 goto end;
385 msg->request = request;
386 msg->flags = size;
387 msg->size = size;
389 if (request == VHOST_USER_GPU_CURSOR_UPDATE ||
390 request == VHOST_USER_GPU_CURSOR_POS ||
391 request == VHOST_USER_GPU_CURSOR_POS_HIDE) {
392 vhost_user_gpu_handle_cursor(g, msg);
393 } else {
394 vhost_user_gpu_handle_display(g, msg);
397 end:
398 g_free(msg);
401 static void
402 vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked)
404 qemu_set_fd_handler(g->vhost_gpu_fd,
405 blocked ? NULL : vhost_user_gpu_chr_read, NULL, g);
408 static void
409 vhost_user_gpu_gl_flushed(VirtIOGPUBase *b)
411 VhostUserGPU *g = VHOST_USER_GPU(b);
413 if (g->backend_blocked) {
414 vhost_user_gpu_unblock(g);
415 g->backend_blocked = false;
418 vhost_user_gpu_update_blocked(g, false);
421 static bool
422 vhost_user_gpu_do_set_socket(VhostUserGPU *g, Error **errp)
424 Chardev *chr;
425 int sv[2];
427 if (qemu_socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) {
428 error_setg_errno(errp, errno, "socketpair() failed");
429 return false;
432 chr = CHARDEV(object_new(TYPE_CHARDEV_SOCKET));
433 if (!chr || qemu_chr_add_client(chr, sv[0]) == -1) {
434 error_setg(errp, "Failed to make socket chardev");
435 goto err;
437 if (!qemu_chr_fe_init(&g->vhost_chr, chr, errp)) {
438 goto err;
440 if (vhost_user_gpu_set_socket(&g->vhost->dev, sv[1]) < 0) {
441 error_setg(errp, "Failed to set vhost-user-gpu socket");
442 qemu_chr_fe_deinit(&g->vhost_chr, false);
443 goto err;
446 g->vhost_gpu_fd = sv[0];
447 vhost_user_gpu_update_blocked(g, false);
448 close(sv[1]);
449 return true;
451 err:
452 close(sv[0]);
453 close(sv[1]);
454 if (chr) {
455 object_unref(OBJECT(chr));
457 return false;
460 static void
461 vhost_user_gpu_get_config(VirtIODevice *vdev, uint8_t *config_data)
463 VhostUserGPU *g = VHOST_USER_GPU(vdev);
464 VirtIOGPUBase *b = VIRTIO_GPU_BASE(vdev);
465 struct virtio_gpu_config *vgconfig =
466 (struct virtio_gpu_config *)config_data;
467 Error *local_err = NULL;
468 int ret;
470 memset(config_data, 0, sizeof(struct virtio_gpu_config));
472 ret = vhost_dev_get_config(&g->vhost->dev,
473 config_data, sizeof(struct virtio_gpu_config),
474 &local_err);
475 if (ret) {
476 error_report_err(local_err);
477 return;
480 /* those fields are managed by qemu */
481 vgconfig->num_scanouts = b->virtio_config.num_scanouts;
482 vgconfig->events_read = b->virtio_config.events_read;
483 vgconfig->events_clear = b->virtio_config.events_clear;
486 static void
487 vhost_user_gpu_set_config(VirtIODevice *vdev,
488 const uint8_t *config_data)
490 VhostUserGPU *g = VHOST_USER_GPU(vdev);
491 VirtIOGPUBase *b = VIRTIO_GPU_BASE(vdev);
492 const struct virtio_gpu_config *vgconfig =
493 (const struct virtio_gpu_config *)config_data;
494 int ret;
496 if (vgconfig->events_clear) {
497 b->virtio_config.events_read &= ~vgconfig->events_clear;
500 ret = vhost_dev_set_config(&g->vhost->dev, config_data,
501 0, sizeof(struct virtio_gpu_config),
502 VHOST_SET_CONFIG_TYPE_FRONTEND);
503 if (ret) {
504 error_report("vhost-user-gpu: set device config space failed");
505 return;
509 static void
510 vhost_user_gpu_set_status(VirtIODevice *vdev, uint8_t val)
512 VhostUserGPU *g = VHOST_USER_GPU(vdev);
513 Error *err = NULL;
515 if (val & VIRTIO_CONFIG_S_DRIVER_OK && vdev->vm_running) {
516 if (!vhost_user_gpu_do_set_socket(g, &err)) {
517 error_report_err(err);
518 return;
520 vhost_user_backend_start(g->vhost);
521 } else {
522 /* unblock any wait and stop processing */
523 if (g->vhost_gpu_fd != -1) {
524 vhost_user_gpu_update_blocked(g, true);
525 qemu_chr_fe_deinit(&g->vhost_chr, true);
526 g->vhost_gpu_fd = -1;
528 vhost_user_backend_stop(g->vhost);
532 static bool
533 vhost_user_gpu_guest_notifier_pending(VirtIODevice *vdev, int idx)
535 VhostUserGPU *g = VHOST_USER_GPU(vdev);
538 * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1
539 * as the macro of configure interrupt's IDX, If this driver does not
540 * support, the function will return
543 if (idx == VIRTIO_CONFIG_IRQ_IDX) {
544 return false;
546 return vhost_virtqueue_pending(&g->vhost->dev, idx);
549 static void
550 vhost_user_gpu_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask)
552 VhostUserGPU *g = VHOST_USER_GPU(vdev);
555 * Add the check for configure interrupt, Use VIRTIO_CONFIG_IRQ_IDX -1
556 * as the macro of configure interrupt's IDX, If this driver does not
557 * support, the function will return
560 if (idx == VIRTIO_CONFIG_IRQ_IDX) {
561 return;
563 vhost_virtqueue_mask(&g->vhost->dev, vdev, idx, mask);
566 static void
567 vhost_user_gpu_instance_init(Object *obj)
569 VhostUserGPU *g = VHOST_USER_GPU(obj);
571 g->vhost = VHOST_USER_BACKEND(object_new(TYPE_VHOST_USER_BACKEND));
572 object_property_add_alias(obj, "chardev",
573 OBJECT(g->vhost), "chardev");
576 static void
577 vhost_user_gpu_instance_finalize(Object *obj)
579 VhostUserGPU *g = VHOST_USER_GPU(obj);
581 object_unref(OBJECT(g->vhost));
584 static void
585 vhost_user_gpu_reset(VirtIODevice *vdev)
587 VhostUserGPU *g = VHOST_USER_GPU(vdev);
589 virtio_gpu_base_reset(VIRTIO_GPU_BASE(vdev));
591 vhost_user_backend_stop(g->vhost);
594 static int
595 vhost_user_gpu_config_change(struct vhost_dev *dev)
597 error_report("vhost-user-gpu: unhandled backend config change");
598 return -1;
601 static const VhostDevConfigOps config_ops = {
602 .vhost_dev_config_notifier = vhost_user_gpu_config_change,
605 static void
606 vhost_user_gpu_device_realize(DeviceState *qdev, Error **errp)
608 VhostUserGPU *g = VHOST_USER_GPU(qdev);
609 VirtIODevice *vdev = VIRTIO_DEVICE(g);
611 vhost_dev_set_config_notifier(&g->vhost->dev, &config_ops);
612 if (vhost_user_backend_dev_init(g->vhost, vdev, 2, errp) < 0) {
613 return;
616 /* existing backend may send DMABUF, so let's add that requirement */
617 g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_DMABUF_ENABLED;
618 if (virtio_has_feature(g->vhost->dev.features, VIRTIO_GPU_F_VIRGL)) {
619 g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED;
621 if (virtio_has_feature(g->vhost->dev.features, VIRTIO_GPU_F_EDID)) {
622 g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_EDID_ENABLED;
623 } else {
624 error_report("EDID requested but the backend doesn't support it.");
625 g->parent_obj.conf.flags &= ~(1 << VIRTIO_GPU_FLAG_EDID_ENABLED);
628 if (!virtio_gpu_base_device_realize(qdev, NULL, NULL, errp)) {
629 return;
632 g->vhost_gpu_fd = -1;
635 static struct vhost_dev *vhost_user_gpu_get_vhost(VirtIODevice *vdev)
637 VhostUserGPU *g = VHOST_USER_GPU(vdev);
638 return &g->vhost->dev;
641 static Property vhost_user_gpu_properties[] = {
642 VIRTIO_GPU_BASE_PROPERTIES(VhostUserGPU, parent_obj.conf),
643 DEFINE_PROP_END_OF_LIST(),
646 static void
647 vhost_user_gpu_class_init(ObjectClass *klass, void *data)
649 DeviceClass *dc = DEVICE_CLASS(klass);
650 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
651 VirtIOGPUBaseClass *vgc = VIRTIO_GPU_BASE_CLASS(klass);
653 vgc->gl_flushed = vhost_user_gpu_gl_flushed;
655 vdc->realize = vhost_user_gpu_device_realize;
656 vdc->reset = vhost_user_gpu_reset;
657 vdc->set_status = vhost_user_gpu_set_status;
658 vdc->guest_notifier_mask = vhost_user_gpu_guest_notifier_mask;
659 vdc->guest_notifier_pending = vhost_user_gpu_guest_notifier_pending;
660 vdc->get_config = vhost_user_gpu_get_config;
661 vdc->set_config = vhost_user_gpu_set_config;
662 vdc->get_vhost = vhost_user_gpu_get_vhost;
664 device_class_set_props(dc, vhost_user_gpu_properties);
667 static const TypeInfo vhost_user_gpu_info = {
668 .name = TYPE_VHOST_USER_GPU,
669 .parent = TYPE_VIRTIO_GPU_BASE,
670 .instance_size = sizeof(VhostUserGPU),
671 .instance_init = vhost_user_gpu_instance_init,
672 .instance_finalize = vhost_user_gpu_instance_finalize,
673 .class_init = vhost_user_gpu_class_init,
675 module_obj(TYPE_VHOST_USER_GPU);
676 module_kconfig(VHOST_USER_GPU);
678 static void vhost_user_gpu_register_types(void)
680 type_register_static(&vhost_user_gpu_info);
683 type_init(vhost_user_gpu_register_types)