From nobody Sun May 5 10:05:35 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1556025762; cv=none; d=zoho.com; s=zohoarc; b=DCLSJcA34saK/zsdhdbvI036xgoVTvEjC6Dcb0Z4I9C1lTjUHvkIeoENey0b/U+B1g4xHRyKr4hEYwysqX/9ectgfHODrjuCV3KSYTbwogMUkl92EHE2jgNI4kFL1oPzwMCnfGadDT8BXnCqPmJm4aLtHwNCYR0jhjeGJCTylr8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556025762; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=R3VLPxV1RQ1itHFHrrc4FjlLxiwLRdpPpgkASz20ck0=; b=fS8VhfzbeTyRYNg9i6e3PRd2zh1rg5uqEHzkzrkwUER6tB/K8+5szpfLQCjn/2ZTOpq5IMjn5Uh6FJf4nQ3A8kWvPpAJonew/1FLs1e9+CRUnRQoohQ93FfSL9nZaZjIrBr0SXEMB52GQ5EVIk09OsC6+P4eqWpS19kxgSHJvqU= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556025762123683.2573783539781; Tue, 23 Apr 2019 06:22:42 -0700 (PDT) Received: from localhost ([127.0.0.1]:53806 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvNb-0007Yd-R9 for importer@patchew.org; Tue, 23 Apr 2019 09:22:35 -0400 Received: from eggs.gnu.org ([209.51.188.92]:60361) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvLW-0006Ix-ML for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:20:28 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hIvLU-0004jw-TX for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:20:26 -0400 Received: from mx1.redhat.com ([209.132.183.28]:58304) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hIvLU-0004iT-Ke for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:20:24 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id D54CA30ADBD5 for ; Tue, 23 Apr 2019 13:20:23 +0000 (UTC) Received: from localhost (ovpn-112-54.ams2.redhat.com [10.36.112.54]) by smtp.corp.redhat.com (Postfix) with ESMTP id 6E5376013D; Tue, 23 Apr 2019 13:20:16 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Tue, 23 Apr 2019 15:19:54 +0200 Message-Id: <20190423132004.13725-2-marcandre.lureau@redhat.com> In-Reply-To: <20190423132004.13725-1-marcandre.lureau@redhat.com> References: <20190423132004.13725-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.47]); Tue, 23 Apr 2019 13:20:23 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v6 01/11] Add vhost-user-backend X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Create a vhost-user-backend object that holds a connection to a vhost-user backend (or "slave" process) and can be referenced from virtio devices that support it. See later patches for input & gpu usage. Note: a previous iteration of this object made it user-creatable, and allowed managed sub-process spawning, but that has been dropped for now. Signed-off-by: Marc-Andr=C3=A9 Lureau --- include/sysemu/vhost-user-backend.h | 57 ++++++++ backends/vhost-user.c | 209 ++++++++++++++++++++++++++++ MAINTAINERS | 2 + backends/Makefile.objs | 2 + 4 files changed, 270 insertions(+) create mode 100644 include/sysemu/vhost-user-backend.h create mode 100644 backends/vhost-user.c diff --git a/include/sysemu/vhost-user-backend.h b/include/sysemu/vhost-use= r-backend.h new file mode 100644 index 0000000000..9abf8f06a1 --- /dev/null +++ b/include/sysemu/vhost-user-backend.h @@ -0,0 +1,57 @@ +/* + * QEMU vhost-user backend + * + * Copyright (C) 2018 Red Hat Inc + * + * Authors: + * Marc-Andr=C3=A9 Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ +#ifndef QEMU_VHOST_USER_BACKEND_H +#define QEMU_VHOST_USER_BACKEND_H + +#include "qom/object.h" +#include "exec/memory.h" +#include "qemu/option.h" +#include "qemu/bitmap.h" +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" +#include "chardev/char-fe.h" +#include "io/channel.h" + +#define TYPE_VHOST_USER_BACKEND "vhost-user-backend" +#define VHOST_USER_BACKEND(obj) \ + OBJECT_CHECK(VhostUserBackend, (obj), TYPE_VHOST_USER_BACKEND) +#define VHOST_USER_BACKEND_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VhostUserBackendClass, (obj), TYPE_VHOST_USER_BACKEND) +#define VHOST_USER_BACKEND_CLASS(klass) \ + OBJECT_CLASS_CHECK(VhostUserBackendClass, (klass), TYPE_VHOST_USER_BAC= KEND) + +typedef struct VhostUserBackend VhostUserBackend; +typedef struct VhostUserBackendClass VhostUserBackendClass; + +struct VhostUserBackendClass { + ObjectClass parent_class; +}; + +struct VhostUserBackend { + /* private */ + Object parent; + + char *chr_name; + CharBackend chr; + VhostUserState vhost_user; + struct vhost_dev dev; + VirtIODevice *vdev; + bool started; + bool completed; +}; + +int vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev, + unsigned nvqs, Error **errp); +void vhost_user_backend_start(VhostUserBackend *b); +void vhost_user_backend_stop(VhostUserBackend *b); + +#endif diff --git a/backends/vhost-user.c b/backends/vhost-user.c new file mode 100644 index 0000000000..2b055544a7 --- /dev/null +++ b/backends/vhost-user.c @@ -0,0 +1,209 @@ +/* + * QEMU vhost-user backend + * + * Copyright (C) 2018 Red Hat Inc + * + * Authors: + * Marc-Andr=C3=A9 Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + + +#include "qemu/osdep.h" +#include "hw/qdev.h" +#include "qapi/error.h" +#include "qapi/qmp/qerror.h" +#include "qemu/error-report.h" +#include "qom/object_interfaces.h" +#include "sysemu/vhost-user-backend.h" +#include "sysemu/kvm.h" +#include "io/channel-command.h" +#include "hw/virtio/virtio-bus.h" + +static bool +ioeventfd_enabled(void) +{ + return kvm_enabled() && kvm_eventfds_enabled(); +} + +int +vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev, + unsigned nvqs, Error **errp) +{ + int ret; + + assert(!b->vdev && vdev); + + if (!ioeventfd_enabled()) { + error_setg(errp, "vhost initialization failed: requires kvm"); + return -1; + } + + if (!vhost_user_init(&b->vhost_user, &b->chr, errp)) { + return -1; + } + + b->vdev =3D vdev; + b->dev.nvqs =3D nvqs; + b->dev.vqs =3D g_new(struct vhost_virtqueue, nvqs); + + ret =3D vhost_dev_init(&b->dev, &b->vhost_user, VHOST_BACKEND_TYPE_USE= R, 0); + if (ret < 0) { + error_setg_errno(errp, -ret, "vhost initialization failed"); + return -1; + } + + return 0; +} + +void +vhost_user_backend_start(VhostUserBackend *b) +{ + BusState *qbus =3D BUS(qdev_get_parent_bus(DEVICE(b->vdev))); + VirtioBusClass *k =3D VIRTIO_BUS_GET_CLASS(qbus); + int ret, i ; + + if (b->started) { + return; + } + + if (!k->set_guest_notifiers) { + error_report("binding does not support guest notifiers"); + return; + } + + ret =3D vhost_dev_enable_notifiers(&b->dev, b->vdev); + if (ret < 0) { + return; + } + + ret =3D k->set_guest_notifiers(qbus->parent, b->dev.nvqs, true); + if (ret < 0) { + error_report("Error binding guest notifier"); + goto err_host_notifiers; + } + + b->dev.acked_features =3D b->vdev->guest_features; + ret =3D vhost_dev_start(&b->dev, b->vdev); + if (ret < 0) { + error_report("Error start vhost dev"); + goto err_guest_notifiers; + } + + /* guest_notifier_mask/pending not used yet, so just unmask + * everything here. virtio-pci will do the right thing by + * enabling/disabling irqfd. + */ + for (i =3D 0; i < b->dev.nvqs; i++) { + vhost_virtqueue_mask(&b->dev, b->vdev, + b->dev.vq_index + i, false); + } + + b->started =3D true; + return; + +err_guest_notifiers: + k->set_guest_notifiers(qbus->parent, b->dev.nvqs, false); +err_host_notifiers: + vhost_dev_disable_notifiers(&b->dev, b->vdev); +} + +void +vhost_user_backend_stop(VhostUserBackend *b) +{ + BusState *qbus =3D BUS(qdev_get_parent_bus(DEVICE(b->vdev))); + VirtioBusClass *k =3D VIRTIO_BUS_GET_CLASS(qbus); + int ret =3D 0; + + if (!b->started) { + return; + } + + vhost_dev_stop(&b->dev, b->vdev); + + if (k->set_guest_notifiers) { + ret =3D k->set_guest_notifiers(qbus->parent, + b->dev.nvqs, false); + if (ret < 0) { + error_report("vhost guest notifier cleanup failed: %d", ret); + } + } + assert(ret >=3D 0); + + vhost_dev_disable_notifiers(&b->dev, b->vdev); + b->started =3D false; +} + +static void set_chardev(Object *obj, const char *value, Error **errp) +{ + VhostUserBackend *b =3D VHOST_USER_BACKEND(obj); + Chardev *chr; + + if (b->completed) { + error_setg(errp, QERR_PERMISSION_DENIED); + return; + } + + g_free(b->chr_name); + b->chr_name =3D g_strdup(value); + + chr =3D qemu_chr_find(b->chr_name); + if (chr =3D=3D NULL) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Chardev '%s' not found", b->chr_name); + return; + } + + if (!qemu_chr_fe_init(&b->chr, chr, errp)) { + return; + } + + b->completed =3D true; + /* could call vhost_dev_init() so early message can be exchanged */ +} + +static char *get_chardev(Object *obj, Error **errp) +{ + VhostUserBackend *b =3D VHOST_USER_BACKEND(obj); + Chardev *chr =3D qemu_chr_fe_get_driver(&b->chr); + + if (chr && chr->label) { + return g_strdup(chr->label); + } + + return NULL; +} + +static void vhost_user_backend_init(Object *obj) +{ + object_property_add_str(obj, "chardev", get_chardev, set_chardev, NULL= ); +} + +static void vhost_user_backend_finalize(Object *obj) +{ + VhostUserBackend *b =3D VHOST_USER_BACKEND(obj); + + g_free(b->dev.vqs); + g_free(b->chr_name); + + vhost_user_cleanup(&b->vhost_user); + qemu_chr_fe_deinit(&b->chr, true); +} + +static const TypeInfo vhost_user_backend_info =3D { + .name =3D TYPE_VHOST_USER_BACKEND, + .parent =3D TYPE_OBJECT, + .instance_size =3D sizeof(VhostUserBackend), + .instance_init =3D vhost_user_backend_init, + .instance_finalize =3D vhost_user_backend_finalize, + .class_size =3D sizeof(VhostUserBackendClass), +}; + +static void register_types(void) +{ + type_register_static(&vhost_user_backend_info); +} + +type_init(register_types); diff --git a/MAINTAINERS b/MAINTAINERS index 56139ac8ab..e0f3f9c4bd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1469,6 +1469,8 @@ F: hw/*/*vhost* F: docs/interop/vhost-user.json F: docs/interop/vhost-user.txt F: contrib/vhost-user-*/ +F: backends/vhost-user.c +F: include/sysemu/vhost-user-backend.h =20 virtio M: Michael S. Tsirkin diff --git a/backends/Makefile.objs b/backends/Makefile.objs index ff619d31b4..981e8e122f 100644 --- a/backends/Makefile.objs +++ b/backends/Makefile.objs @@ -14,4 +14,6 @@ common-obj-y +=3D cryptodev-vhost.o common-obj-$(CONFIG_VHOST_CRYPTO) +=3D cryptodev-vhost-user.o endif =20 +common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO)) +=3D vhost-u= ser.o + common-obj-$(CONFIG_LINUX) +=3D hostmem-memfd.o --=20 2.21.0.313.ge35b8cb8e2 From nobody Sun May 5 10:05:35 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1556026030; cv=none; d=zoho.com; s=zohoarc; b=BZkRaph1yX+/nowlL505YKlCnyY0it4aHb2acly0YNp2BRwLohrATyDuiOzM+3a8r4dZY/OdWLdFJTmkNSStCFrmimCw5i1/QUtHhN/2l9aMo0eF6zR+zD+Sq7Z4Qp8ALP4DjzbVDoxub7pi8Rlt37deC9760EcNaOUn9kAqwy4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556026030; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=CLOQJdPkkPUOD5giOIouj9ZUYq7yu9ABlG7i2v0jLlI=; b=CsNZX4r3qy+EsUG/Ne9mkXrDiLXCJuxM2b+ZaKXh/o9H5j4SLL3x5l0hDQuuoLZ87gnq/s5JYvyB5ZKL04BYR5vc5orb4em3pcMP9C7+GPpesft2v70Dhmrx8458+cAZONUg3N58ChmAmJC7JeD+KGN5YBn9r5w9PLg8G9nqz5s= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556026030208852.5377173649783; Tue, 23 Apr 2019 06:27:10 -0700 (PDT) Received: from localhost ([127.0.0.1]:53889 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvRw-00031U-3p for importer@patchew.org; Tue, 23 Apr 2019 09:27:04 -0400 Received: from eggs.gnu.org ([209.51.188.92]:60404) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvLh-0006QL-EP for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:20:39 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hIvLd-000548-MJ for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:20:35 -0400 Received: from mx1.redhat.com ([209.132.183.28]:48970) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hIvLd-00053E-E5 for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:20:33 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id AF754308792A for ; Tue, 23 Apr 2019 13:20:32 +0000 (UTC) Received: from localhost (ovpn-112-54.ams2.redhat.com [10.36.112.54]) by smtp.corp.redhat.com (Postfix) with ESMTP id 7948E17DED; Tue, 23 Apr 2019 13:20:25 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Tue, 23 Apr 2019 15:19:55 +0200 Message-Id: <20190423132004.13725-3-marcandre.lureau@redhat.com> In-Reply-To: <20190423132004.13725-1-marcandre.lureau@redhat.com> References: <20190423132004.13725-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.45]); Tue, 23 Apr 2019 13:20:32 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v6 02/11] Add vhost-user-input-pci X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add a new virtio-input device, which connects to a vhost-user backend. Instead of reading configuration directly from an input device / evdev (like virtio-input-host), it reads it over vhost-user protocol with {SET,GET}_CONFIG messages. The vhost-user-backend handles the queues & events setup. Signed-off-by: Marc-Andr=C3=A9 Lureau --- include/hw/virtio/virtio-input.h | 14 ++++ hw/input/vhost-user-input.c | 129 +++++++++++++++++++++++++++++++ hw/virtio/vhost-user-input-pci.c | 53 +++++++++++++ MAINTAINERS | 1 + hw/input/Kconfig | 5 ++ hw/input/Makefile.objs | 1 + hw/virtio/Makefile.objs | 1 + 7 files changed, 204 insertions(+) create mode 100644 hw/input/vhost-user-input.c create mode 100644 hw/virtio/vhost-user-input-pci.c diff --git a/include/hw/virtio/virtio-input.h b/include/hw/virtio/virtio-in= put.h index 054c38836f..4fca03e796 100644 --- a/include/hw/virtio/virtio-input.h +++ b/include/hw/virtio/virtio-input.h @@ -2,6 +2,7 @@ #define QEMU_VIRTIO_INPUT_H =20 #include "ui/input.h" +#include "sysemu/vhost-user-backend.h" =20 /* ----------------------------------------------------------------- */ /* virtio input protocol */ @@ -42,11 +43,18 @@ typedef struct virtio_input_event virtio_input_event; #define VIRTIO_INPUT_HOST_GET_PARENT_CLASS(obj) \ OBJECT_GET_PARENT_CLASS(obj, TYPE_VIRTIO_INPUT_HOST) =20 +#define TYPE_VHOST_USER_INPUT "vhost-user-input" +#define VHOST_USER_INPUT(obj) \ + OBJECT_CHECK(VHostUserInput, (obj), TYPE_VHOST_USER_INPUT) +#define VHOST_USER_INPUT_GET_PARENT_CLASS(obj) \ + OBJECT_GET_PARENT_CLASS(obj, TYPE_VHOST_USER_INPUT) + typedef struct VirtIOInput VirtIOInput; typedef struct VirtIOInputClass VirtIOInputClass; typedef struct VirtIOInputConfig VirtIOInputConfig; typedef struct VirtIOInputHID VirtIOInputHID; typedef struct VirtIOInputHost VirtIOInputHost; +typedef struct VHostUserInput VHostUserInput; =20 struct VirtIOInputConfig { virtio_input_config config; @@ -98,6 +106,12 @@ struct VirtIOInputHost { int fd; }; =20 +struct VHostUserInput { + VirtIOInput parent_obj; + + VhostUserBackend *vhost; +}; + void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event); void virtio_input_init_config(VirtIOInput *vinput, virtio_input_config *config); diff --git a/hw/input/vhost-user-input.c b/hw/input/vhost-user-input.c new file mode 100644 index 0000000000..6da497b1a8 --- /dev/null +++ b/hw/input/vhost-user-input.c @@ -0,0 +1,129 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "qemu-common.h" + +#include "hw/qdev.h" +#include "hw/virtio/virtio-input.h" + +static int vhost_input_config_change(struct vhost_dev *dev) +{ + error_report("vhost-user-input: unhandled backend config change"); + return -1; +} + +static const VhostDevConfigOps config_ops =3D { + .vhost_dev_config_notifier =3D vhost_input_config_change, +}; + +static void vhost_input_realize(DeviceState *dev, Error **errp) +{ + VHostUserInput *vhi =3D VHOST_USER_INPUT(dev); + VirtIOInput *vinput =3D VIRTIO_INPUT(dev); + VirtIODevice *vdev =3D VIRTIO_DEVICE(dev); + + vhost_dev_set_config_notifier(&vhi->vhost->dev, &config_ops); + vinput->cfg_size =3D sizeof_field(virtio_input_config, u); + if (vhost_user_backend_dev_init(vhi->vhost, vdev, 2, errp) =3D=3D -1) { + return; + } +} + +static void vhost_input_change_active(VirtIOInput *vinput) +{ + VHostUserInput *vhi =3D VHOST_USER_INPUT(vinput); + + if (vinput->active) { + vhost_user_backend_start(vhi->vhost); + } else { + vhost_user_backend_stop(vhi->vhost); + } +} + +static void vhost_input_get_config(VirtIODevice *vdev, uint8_t *config_dat= a) +{ + VirtIOInput *vinput =3D VIRTIO_INPUT(vdev); + VHostUserInput *vhi =3D VHOST_USER_INPUT(vdev); + int ret; + + memset(config_data, 0, vinput->cfg_size); + + ret =3D vhost_dev_get_config(&vhi->vhost->dev, config_data, vinput->cf= g_size); + if (ret) { + error_report("vhost-user-input: get device config space failed"); + return; + } +} + +static void vhost_input_set_config(VirtIODevice *vdev, + const uint8_t *config_data) +{ + VHostUserInput *vhi =3D VHOST_USER_INPUT(vdev); + int ret; + + ret =3D vhost_dev_set_config(&vhi->vhost->dev, config_data, + 0, sizeof(virtio_input_config), + VHOST_SET_CONFIG_TYPE_MASTER); + if (ret) { + error_report("vhost-user-input: set device config space failed"); + return; + } + + virtio_notify_config(vdev); +} + +static const VMStateDescription vmstate_vhost_input =3D { + .name =3D "vhost-user-input", + .unmigratable =3D 1, +}; + +static void vhost_input_class_init(ObjectClass *klass, void *data) +{ + VirtIOInputClass *vic =3D VIRTIO_INPUT_CLASS(klass); + VirtioDeviceClass *vdc =3D VIRTIO_DEVICE_CLASS(klass); + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->vmsd =3D &vmstate_vhost_input; + vdc->get_config =3D vhost_input_get_config; + vdc->set_config =3D vhost_input_set_config; + vic->realize =3D vhost_input_realize; + vic->change_active =3D vhost_input_change_active; +} + +static void vhost_input_init(Object *obj) +{ + VHostUserInput *vhi =3D VHOST_USER_INPUT(obj); + + vhi->vhost =3D VHOST_USER_BACKEND(object_new(TYPE_VHOST_USER_BACKEND)); + object_property_add_alias(obj, "chardev", + OBJECT(vhi->vhost), "chardev", &error_abort); +} + +static void vhost_input_finalize(Object *obj) +{ + VHostUserInput *vhi =3D VHOST_USER_INPUT(obj); + + object_unref(OBJECT(vhi->vhost)); +} + +static const TypeInfo vhost_input_info =3D { + .name =3D TYPE_VHOST_USER_INPUT, + .parent =3D TYPE_VIRTIO_INPUT, + .instance_size =3D sizeof(VHostUserInput), + .instance_init =3D vhost_input_init, + .instance_finalize =3D vhost_input_finalize, + .class_init =3D vhost_input_class_init, +}; + +static void vhost_input_register_types(void) +{ + type_register_static(&vhost_input_info); +} + +type_init(vhost_input_register_types) diff --git a/hw/virtio/vhost-user-input-pci.c b/hw/virtio/vhost-user-input-= pci.c new file mode 100644 index 0000000000..3d1b7a85fd --- /dev/null +++ b/hw/virtio/vhost-user-input-pci.c @@ -0,0 +1,53 @@ +/* + * This work is licensed under the terms of the GNU LGPL, version 2 or + * later. See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-input.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "virtio-pci.h" + +typedef struct VHostUserInputPCI VHostUserInputPCI; + +#define TYPE_VHOST_USER_INPUT_PCI "vhost-user-input-pci-base" + +#define VHOST_USER_INPUT_PCI(obj) \ + OBJECT_CHECK(VHostUserInputPCI, (obj), TYPE_VHOST_USER_INPUT_PCI) + +struct VHostUserInputPCI { + VirtIOPCIProxy parent_obj; + VHostUserInput vhi; +}; + +static void vhost_user_input_pci_instance_init(Object *obj) +{ + VHostUserInputPCI *dev =3D VHOST_USER_INPUT_PCI(obj); + + virtio_instance_init_common(obj, &dev->vhi, sizeof(dev->vhi), + TYPE_VHOST_USER_INPUT); + + object_property_add_alias(obj, "chardev", + OBJECT(&dev->vhi), "chardev", + &error_abort); +} + +static const VirtioPCIDeviceTypeInfo vhost_user_input_pci_info =3D { + .base_name =3D TYPE_VHOST_USER_INPUT_PCI, + .generic_name =3D "vhost-user-input-pci", + .transitional_name =3D "vhost-user-input-pci-transitional", + .non_transitional_name =3D "vhost-user-input-pci-non-transitional", + .parent =3D TYPE_VIRTIO_INPUT_PCI, + .instance_size =3D sizeof(VHostUserInputPCI), + .instance_init =3D vhost_user_input_pci_instance_init, +}; + +static void vhost_user_input_pci_register(void) +{ + virtio_pci_types_register(&vhost_user_input_pci_info); +} + +type_init(vhost_user_input_pci_register) diff --git a/MAINTAINERS b/MAINTAINERS index e0f3f9c4bd..b6e855ccf9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1512,6 +1512,7 @@ L: qemu-s390x@nongnu.org virtio-input M: Gerd Hoffmann S: Maintained +F: hw/input/vhost-user-input.c F: hw/input/virtio-input*.c F: include/hw/virtio/virtio-input.h =20 diff --git a/hw/input/Kconfig b/hw/input/Kconfig index e2e66f0858..50e55e3538 100644 --- a/hw/input/Kconfig +++ b/hw/input/Kconfig @@ -29,5 +29,10 @@ config VIRTIO_INPUT_HOST default y depends on VIRTIO && LINUX =20 +config VHOST_USER_INPUT + bool + default y + depends on VIRTIO_INPUT && VHOST_USER + config TSC210X bool diff --git a/hw/input/Makefile.objs b/hw/input/Makefile.objs index c8b00f71ec..9511e3102f 100644 --- a/hw/input/Makefile.objs +++ b/hw/input/Makefile.objs @@ -11,6 +11,7 @@ common-obj-$(CONFIG_VIRTIO_INPUT) +=3D virtio-input.o common-obj-$(CONFIG_VIRTIO_INPUT) +=3D virtio-input-hid.o ifeq ($(CONFIG_LINUX),y) common-obj-$(CONFIG_VIRTIO_INPUT) +=3D virtio-input-host.o +common-obj-$(CONFIG_VHOST_USER_INPUT) +=3D vhost-user-input.o endif =20 obj-$(CONFIG_MILKYMIST) +=3D milkymist-softusb.o diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs index f2ab667a21..5570ea8df8 100644 --- a/hw/virtio/Makefile.objs +++ b/hw/virtio/Makefile.objs @@ -17,6 +17,7 @@ obj-$(CONFIG_VHOST_VSOCK) +=3D vhost-vsock.o ifeq ($(CONFIG_VIRTIO_PCI),y) obj-$(CONFIG_VHOST_VSOCK) +=3D vhost-vsock-pci.o obj-$(CONFIG_VHOST_USER_BLK) +=3D vhost-user-blk-pci.o +obj-$(CONFIG_VHOST_USER_INPUT) +=3D vhost-user-input-pci.o obj-$(CONFIG_VHOST_USER_SCSI) +=3D vhost-user-scsi-pci.o obj-$(CONFIG_VHOST_SCSI) +=3D vhost-scsi-pci.o obj-$(CONFIG_VIRTIO_INPUT_HOST) +=3D virtio-input-host-pci.o --=20 2.21.0.313.ge35b8cb8e2 From nobody Sun May 5 10:05:35 2024 Delivered-To: importer@patchew.org Received-SPF: temperror (zoho.com: Error in retrieving data from DNS) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=temperror (zoho.com: Error in retrieving data from DNS) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1556025940; cv=none; d=zoho.com; s=zohoarc; b=N1Ko+VupNx+wWLKMmY15oedKUwtWFmQUypwhFWSIupZPgcwFcx2S8zsa4Nm3jXVTBtHU6ZoeMkGuBus10hBA6TgDutTBXQ7n0dXH02oa034s5xu+nhHhK+k3RcVLtFYYIcPlV4DNcu0axwB8XFKwnuLUrcIuq1GOZrYVEUQx4Oc= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556025940; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=laww6EXFodW5Ro2GtTrTfm5QrFvRD/zW1yyohirz0OY=; b=SCR86YLnG9fxPMRr+sQRBe90Oenl7u/QwYk6oMY1WkyCj5EcWdbf9K5D2vSJ/F2W1dXi8OV5Oggjw/W5zi4r24VA/fWkeBZgYigWfApiDuWRRUPz43Z0GwW6v4w/pFmfqVrXJaBfN0BHJIk9+NCawgOdMz6LQTIgR/UN7VSe3HY= ARC-Authentication-Results: i=1; mx.zoho.com; spf=temperror (zoho.com: Error in retrieving data from DNS) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556025940834817.1070001016317; Tue, 23 Apr 2019 06:25:40 -0700 (PDT) Received: from localhost ([127.0.0.1]:53831 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvQO-0001Yc-J4 for importer@patchew.org; Tue, 23 Apr 2019 09:25:28 -0400 Received: from eggs.gnu.org ([209.51.188.92]:60441) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvLr-0006b7-EQ for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:20:48 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hIvLq-0005Ex-Ko for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:20:47 -0400 Received: from mx1.redhat.com ([209.132.183.28]:53408) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hIvLp-00059U-74 for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:20:46 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 9EBB0307C941 for ; Tue, 23 Apr 2019 13:20:41 +0000 (UTC) Received: from localhost (ovpn-112-54.ams2.redhat.com [10.36.112.54]) by smtp.corp.redhat.com (Postfix) with ESMTP id 17D8A5C220; Tue, 23 Apr 2019 13:20:33 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Tue, 23 Apr 2019 15:19:56 +0200 Message-Id: <20190423132004.13725-4-marcandre.lureau@redhat.com> In-Reply-To: <20190423132004.13725-1-marcandre.lureau@redhat.com> References: <20190423132004.13725-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.46]); Tue, 23 Apr 2019 13:20:41 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v6 03/11] libvhost-user: add PROTOCOL_F_CONFIG if {set, get}_config X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add the config protocol feature bit if the set_config & get_config callbacks are implemented. Signed-off-by: Marc-Andr=C3=A9 Lureau --- contrib/libvhost-user/libvhost-user.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/= libvhost-user.c index e08d6c7b97..e738f6ce88 100644 --- a/contrib/libvhost-user/libvhost-user.c +++ b/contrib/libvhost-user/libvhost-user.c @@ -1157,6 +1157,10 @@ vu_get_protocol_features_exec(VuDev *dev, VhostUserM= sg *vmsg) features |=3D 1ULL << VHOST_USER_PROTOCOL_F_PAGEFAULT; } =20 + if (dev->iface->get_config && dev->iface->set_config) { + features |=3D 1ULL << VHOST_USER_PROTOCOL_F_CONFIG; + } + if (dev->iface->get_protocol_features) { features |=3D dev->iface->get_protocol_features(dev); } --=20 2.21.0.313.ge35b8cb8e2 From nobody Sun May 5 10:05:35 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1556025940; cv=none; d=zoho.com; s=zohoarc; b=jSvrOWTUSITKnYIBxwRa9nsWEIhJnoBhpjcsuSYJUGOxf6rzg0fFtiTzmrIjBKdockvsXColfZObMf8ry2+tXji3Ri1Y5HM03ZTIjrOuteuFafkD2jzXcuCpLEZddlft2ZUU15hbKyl0IF9rT3zK2KI+Gu8FR36vdsDI5Eiv+zs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556025940; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=uMzf4OXR5c4+fcWN62baHEin/xAzBLti1YPiLaZJw9U=; b=Ghb9wT5mNQ2CSR5Ydn1IoywMD8TW7K4h9IvuYY0pVUtWg5ToZELz1bof4bp+9jgDINmo/CSzkmXeT1Z0wWapQfP9tVv+7cruxeF+ks/L2SmJP+j4f/O83BazlxsnSaK0ZjT1klgZ4HM5tdHE0WxKU7P1gwIGwIK1s/idTLSUXso= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556025940433170.9341142945857; Tue, 23 Apr 2019 06:25:40 -0700 (PDT) Received: from localhost ([127.0.0.1]:53835 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvQX-0001fG-BE for importer@patchew.org; Tue, 23 Apr 2019 09:25:37 -0400 Received: from eggs.gnu.org ([209.51.188.92]:60519) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvM2-0006k1-Ds for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:00 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hIvM0-0005N6-F5 for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:20:58 -0400 Received: from mx1.redhat.com ([209.132.183.28]:56762) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hIvM0-0005MH-6Z for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:20:56 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 7D5D93199366 for ; Tue, 23 Apr 2019 13:20:55 +0000 (UTC) Received: from localhost (ovpn-112-54.ams2.redhat.com [10.36.112.54]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8E2FB5D71D; Tue, 23 Apr 2019 13:20:43 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Tue, 23 Apr 2019 15:19:57 +0200 Message-Id: <20190423132004.13725-5-marcandre.lureau@redhat.com> In-Reply-To: <20190423132004.13725-1-marcandre.lureau@redhat.com> References: <20190423132004.13725-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.41]); Tue, 23 Apr 2019 13:20:55 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v6 04/11] contrib: add vhost-user-input X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add a vhost-user input backend example, based on virtio-input-host device. It takes an evdev path as argument, and can be associated with a vhost-user-input device via a UNIX socket: $ vhost-user-input -p /dev/input/eventX -s /tmp/vui.sock $ qemu ... -chardev socket,id=3Dvuic,path=3D/tmp/vui.sock -device vhost-user-input-pci,chardev=3Dvuic This example is intentionally not included in $TOOLS, to not be built and installed by default. Signed-off-by: Marc-Andr=C3=A9 Lureau --- contrib/vhost-user-input/main.c | 427 +++++++++++++++++++++++++ MAINTAINERS | 1 + Makefile | 4 + Makefile.objs | 1 + contrib/vhost-user-input/Makefile.objs | 1 + 5 files changed, 434 insertions(+) create mode 100644 contrib/vhost-user-input/main.c create mode 100644 contrib/vhost-user-input/Makefile.objs diff --git a/contrib/vhost-user-input/main.c b/contrib/vhost-user-input/mai= n.c new file mode 100644 index 0000000000..425056cd69 --- /dev/null +++ b/contrib/vhost-user-input/main.c @@ -0,0 +1,427 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" + +#include +#include + +#include "qemu/iov.h" +#include "qemu/bswap.h" +#include "contrib/libvhost-user/libvhost-user.h" +#include "contrib/libvhost-user/libvhost-user-glib.h" +#include "standard-headers/linux/virtio_input.h" +#include "qapi/error.h" + +typedef struct virtio_input_event virtio_input_event; +typedef struct virtio_input_config virtio_input_config; + +typedef struct VuInput { + VugDev dev; + GSource *evsrc; + int evdevfd; + GArray *config; + virtio_input_config *sel_config; + struct { + virtio_input_event event; + VuVirtqElement *elem; + } *queue; + uint32_t qindex, qsize; +} VuInput; + +static void vi_input_send(VuInput *vi, struct virtio_input_event *event) +{ + VuDev *dev =3D &vi->dev.parent; + VuVirtq *vq =3D vu_get_queue(dev, 0); + VuVirtqElement *elem; + int i, len; + + /* queue up events ... */ + if (vi->qindex =3D=3D vi->qsize) { + vi->qsize++; + vi->queue =3D g_realloc_n(vi->queue, vi->qsize, sizeof(vi->queue[0= ])); + } + vi->queue[vi->qindex++].event =3D *event; + + /* ... until we see a report sync ... */ + if (event->type !=3D htole16(EV_SYN) || + event->code !=3D htole16(SYN_REPORT)) { + return; + } + + /* ... then check available space ... */ + for (i =3D 0; i < vi->qindex; i++) { + elem =3D vu_queue_pop(dev, vq, sizeof(VuVirtqElement)); + if (!elem) { + while (--i >=3D 0) { + vu_queue_unpop(dev, vq, vi->queue[i].elem, 0); + } + vi->qindex =3D 0; + g_warning("virtio-input queue full"); + return; + } + vi->queue[i].elem =3D elem; + } + + /* ... and finally pass them to the guest */ + for (i =3D 0; i < vi->qindex; i++) { + elem =3D vi->queue[i].elem; + len =3D iov_from_buf(elem->in_sg, elem->in_num, + 0, &vi->queue[i].event, sizeof(virtio_input_eve= nt)); + vu_queue_push(dev, vq, elem, len); + g_free(elem); + } + + vu_queue_notify(&vi->dev.parent, vq); + vi->qindex =3D 0; +} + +static void +vi_evdev_watch(VuDev *dev, int condition, void *data) +{ + VuInput *vi =3D data; + int fd =3D vi->evdevfd; + + g_debug("Got evdev condition %x", condition); + + struct virtio_input_event virtio; + struct input_event evdev; + int rc; + + for (;;) { + rc =3D read(fd, &evdev, sizeof(evdev)); + if (rc !=3D sizeof(evdev)) { + break; + } + + g_debug("input %d %d %d", evdev.type, evdev.code, evdev.value); + + virtio.type =3D htole16(evdev.type); + virtio.code =3D htole16(evdev.code); + virtio.value =3D htole32(evdev.value); + vi_input_send(vi, &virtio); + } +} + + +static void vi_handle_status(VuInput *vi, virtio_input_event *event) +{ + struct input_event evdev; + int rc; + + if (gettimeofday(&evdev.time, NULL)) { + perror("vi_handle_status: gettimeofday"); + return; + } + + evdev.type =3D le16toh(event->type); + evdev.code =3D le16toh(event->code); + evdev.value =3D le32toh(event->value); + + rc =3D write(vi->evdevfd, &evdev, sizeof(evdev)); + if (rc =3D=3D -1) { + perror("vi_host_handle_status: write"); + } +} + +static void vi_handle_sts(VuDev *dev, int qidx) +{ + VuInput *vi =3D container_of(dev, VuInput, dev.parent); + VuVirtq *vq =3D vu_get_queue(dev, qidx); + virtio_input_event event; + VuVirtqElement *elem; + int len; + + g_debug("%s", G_STRFUNC); + + for (;;) { + elem =3D vu_queue_pop(dev, vq, sizeof(VuVirtqElement)); + if (!elem) { + break; + } + + memset(&event, 0, sizeof(event)); + len =3D iov_to_buf(elem->out_sg, elem->out_num, + 0, &event, sizeof(event)); + vi_handle_status(vi, &event); + vu_queue_push(dev, vq, elem, len); + g_free(elem); + } + + vu_queue_notify(&vi->dev.parent, vq); +} + +static void +vi_panic(VuDev *dev, const char *msg) +{ + g_critical("%s\n", msg); + exit(EXIT_FAILURE); +} + +static void +vi_queue_set_started(VuDev *dev, int qidx, bool started) +{ + VuInput *vi =3D container_of(dev, VuInput, dev.parent); + VuVirtq *vq =3D vu_get_queue(dev, qidx); + + g_debug("queue started %d:%d", qidx, started); + + if (qidx =3D=3D 1) { + vu_set_queue_handler(dev, vq, started ? vi_handle_sts : NULL); + } + + started =3D vu_queue_started(dev, vu_get_queue(dev, 0)) && + vu_queue_started(dev, vu_get_queue(dev, 1)); + + if (started && !vi->evsrc) { + vi->evsrc =3D vug_source_new(&vi->dev, vi->evdevfd, + G_IO_IN, vi_evdev_watch, vi); + } + + if (!started && vi->evsrc) { + g_source_destroy(vi->evsrc); + vi->evsrc =3D NULL; + } +} + +static virtio_input_config * +vi_find_config(VuInput *vi, uint8_t select, uint8_t subsel) +{ + virtio_input_config *cfg; + int i; + + for (i =3D 0; i < vi->config->len; i++) { + cfg =3D &g_array_index(vi->config, virtio_input_config, i); + if (select =3D=3D cfg->select && subsel =3D=3D cfg->subsel) { + return cfg; + } + } + + return NULL; +} + +static int vi_get_config(VuDev *dev, uint8_t *config, uint32_t len) +{ + VuInput *vi =3D container_of(dev, VuInput, dev.parent); + + g_return_val_if_fail(len <=3D sizeof(*vi->sel_config), -1); + + if (vi->sel_config) { + memcpy(config, vi->sel_config, len); + } else { + memset(config, 0, len); + } + + return 0; +} + +static int vi_set_config(VuDev *dev, const uint8_t *data, + uint32_t offset, uint32_t size, + uint32_t flags) +{ + VuInput *vi =3D container_of(dev, VuInput, dev.parent); + virtio_input_config *config =3D (virtio_input_config *)data; + + vi->sel_config =3D vi_find_config(vi, config->select, config->subsel); + + return 0; +} + +static const VuDevIface vuiface =3D { + .queue_set_started =3D vi_queue_set_started, + .get_config =3D vi_get_config, + .set_config =3D vi_set_config, +}; + +static void +vi_bits_config(VuInput *vi, int type, int count) +{ + virtio_input_config bits; + int rc, i, size =3D 0; + + memset(&bits, 0, sizeof(bits)); + rc =3D ioctl(vi->evdevfd, EVIOCGBIT(type, count / 8), bits.u.bitmap); + if (rc < 0) { + return; + } + + for (i =3D 0; i < count / 8; i++) { + if (bits.u.bitmap[i]) { + size =3D i + 1; + } + } + if (size =3D=3D 0) { + return; + } + + bits.select =3D VIRTIO_INPUT_CFG_EV_BITS; + bits.subsel =3D type; + bits.size =3D size; + g_array_append_val(vi->config, bits); +} + +static int unix_sock_new(char *path) +{ + int sock; + struct sockaddr_un un; + size_t len; + + sock =3D socket(AF_UNIX, SOCK_STREAM, 0); + if (sock <=3D 0) { + perror("socket"); + return -1; + } + + un.sun_family =3D AF_UNIX; + snprintf(un.sun_path, sizeof(un.sun_path), "%s", path); + len =3D sizeof(un.sun_family) + strlen(un.sun_path); + + unlink(path); + if (bind(sock, (struct sockaddr *)&un, len) < 0) { + perror("bind"); + goto fail; + } + + if (listen(sock, 1) < 0) { + perror("listen"); + goto fail; + } + + return sock; + +fail: + close(sock); + + return -1; +} + +static char *opt_evdev; +static int opt_fdnum =3D -1; +static char *opt_socket_path; +static gboolean opt_nograb; +static gboolean opt_print_caps; + +static GOptionEntry entries[] =3D { + { "print-capabilities", 'c', 0, G_OPTION_ARG_NONE, &opt_print_caps, + "Print capabilities", NULL }, + { "no-grab", 'n', 0, G_OPTION_ARG_NONE, &opt_nograb, + "Don't grab device", NULL }, + { "fd", 'f', 0, G_OPTION_ARG_INT, &opt_fdnum, + "Use inherited fd socket", "FDNUM" }, + { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &opt_socket_path, + "Use UNIX socket path", "PATH" }, + { "evdev-path", 'p', 0, G_OPTION_ARG_FILENAME, &opt_evdev, + "evdev input device path", "PATH" }, + { NULL, } +}; + +int +main(int argc, char *argv[]) +{ + GMainLoop *loop =3D NULL; + VuInput vi =3D { 0, }; + int rc, ver, fd; + virtio_input_config id; + struct input_id ids; + GError *error =3D NULL; + GOptionContext *context; + + context =3D g_option_context_new(NULL); + g_option_context_add_main_entries(context, entries, NULL); + if (!g_option_context_parse(context, &argc, &argv, &error)) { + g_printerr("Option parsing failed: %s\n", error->message); + exit(EXIT_FAILURE); + } + if (opt_print_caps) { + g_print("{\n"); + g_print(" \"type\": \"input\",\n"); + g_print(" \"features\": [\n"); + g_print(" \"evdev-path\",\n"); + g_print(" \"no-grab\"\n"); + g_print(" ]\n"); + g_print("}\n"); + exit(EXIT_SUCCESS); + } + if (!opt_evdev) { + g_printerr("Please specify an evdev path\n"); + exit(EXIT_FAILURE); + } + if ((!!opt_socket_path + (opt_fdnum !=3D -1)) !=3D 1) { + g_printerr("Please specify either --fd or --socket-path\n"); + exit(EXIT_FAILURE); + } + + vi.evdevfd =3D open(opt_evdev, O_RDWR); + if (vi.evdevfd < 0) { + g_printerr("Failed to open evdev: %s\n", g_strerror(errno)); + exit(EXIT_FAILURE); + } + + rc =3D ioctl(vi.evdevfd, EVIOCGVERSION, &ver); + if (rc < 0) { + g_printerr("%s: is not an evdev device\n", argv[1]); + exit(EXIT_FAILURE); + } + + if (!opt_nograb) { + rc =3D ioctl(vi.evdevfd, EVIOCGRAB, 1); + if (rc < 0) { + g_printerr("Failed to grab device\n"); + exit(EXIT_FAILURE); + } + } + + vi.config =3D g_array_new(false, false, sizeof(virtio_input_config)); + memset(&id, 0, sizeof(id)); + ioctl(vi.evdevfd, EVIOCGNAME(sizeof(id.u.string) - 1), id.u.string); + id.select =3D VIRTIO_INPUT_CFG_ID_NAME; + id.size =3D strlen(id.u.string); + g_array_append_val(vi.config, id); + + if (ioctl(vi.evdevfd, EVIOCGID, &ids) =3D=3D 0) { + memset(&id, 0, sizeof(id)); + id.select =3D VIRTIO_INPUT_CFG_ID_DEVIDS; + id.size =3D sizeof(struct virtio_input_devids); + id.u.ids.bustype =3D cpu_to_le16(ids.bustype); + id.u.ids.vendor =3D cpu_to_le16(ids.vendor); + id.u.ids.product =3D cpu_to_le16(ids.product); + id.u.ids.version =3D cpu_to_le16(ids.version); + g_array_append_val(vi.config, id); + } + + vi_bits_config(&vi, EV_KEY, KEY_CNT); + vi_bits_config(&vi, EV_REL, REL_CNT); + vi_bits_config(&vi, EV_ABS, ABS_CNT); + vi_bits_config(&vi, EV_MSC, MSC_CNT); + vi_bits_config(&vi, EV_SW, SW_CNT); + g_debug("config length: %u", vi.config->len); + + if (opt_socket_path) { + int lsock =3D unix_sock_new(opt_socket_path); + fd =3D accept(lsock, NULL, NULL); + close(lsock); + } else { + fd =3D opt_fdnum; + } + if (fd =3D=3D -1) { + g_printerr("Invalid socket"); + exit(EXIT_FAILURE); + } + vug_init(&vi.dev, fd, vi_panic, &vuiface); + + loop =3D g_main_loop_new(NULL, FALSE); + g_main_loop_run(loop); + g_main_loop_unref(loop); + + vug_deinit(&vi.dev); + + if (vi.evsrc) { + g_source_unref(vi.evsrc); + } + g_array_free(vi.config, TRUE); + g_free(vi.queue); + return 0; +} diff --git a/MAINTAINERS b/MAINTAINERS index b6e855ccf9..7e727299de 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1515,6 +1515,7 @@ S: Maintained F: hw/input/vhost-user-input.c F: hw/input/virtio-input*.c F: include/hw/virtio/virtio-input.h +F: contrib/vhost-user-input/* =20 virtio-serial M: Amit Shah diff --git a/Makefile b/Makefile index 04a0d45050..bf46e603a5 100644 --- a/Makefile +++ b/Makefile @@ -402,6 +402,7 @@ dummy :=3D $(call unnest-vars,, \ libvhost-user-obj-y \ vhost-user-scsi-obj-y \ vhost-user-blk-obj-y \ + vhost-user-input-obj-y \ qga-vss-dll-obj-y \ block-obj-y \ block-obj-m \ @@ -610,6 +611,9 @@ rdmacm-mux$(EXESUF): LIBS +=3D "-libumad" rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS) $(call LINK, $^) =20 +vhost-user-input$(EXESUF): $(vhost-user-input-obj-y) libvhost-user.a libqe= muutil.a + $(call LINK, $^) + module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak $(call quiet-command,$(PYTHON) $< $@ \ $(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \ diff --git a/Makefile.objs b/Makefile.objs index cf065de5ed..27502b7df1 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -122,6 +122,7 @@ vhost-user-scsi.o-libs :=3D $(LIBISCSI_LIBS) vhost-user-scsi-obj-y =3D contrib/vhost-user-scsi/ vhost-user-blk-obj-y =3D contrib/vhost-user-blk/ rdmacm-mux-obj-y =3D contrib/rdmacm-mux/ +vhost-user-input-obj-y =3D contrib/vhost-user-input/ =20 ###################################################################### trace-events-subdirs =3D diff --git a/contrib/vhost-user-input/Makefile.objs b/contrib/vhost-user-in= put/Makefile.objs new file mode 100644 index 0000000000..b1fad90d51 --- /dev/null +++ b/contrib/vhost-user-input/Makefile.objs @@ -0,0 +1 @@ +vhost-user-input-obj-y =3D main.o --=20 2.21.0.313.ge35b8cb8e2 From nobody Sun May 5 10:05:35 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1556026240; cv=none; d=zoho.com; s=zohoarc; b=grQ99rf1b7BXK5WGAAUzmiP+Pj9Mtzi2J8w+eLXgsVMl8tG7SRUY2QDp8C9jM85+9qvEokYk0ZNDjST5oPM0KBdofsTcC/HTHB6SBlDXOL9hpf52mbT5IxC3Q8LDxulERW56A71S0KypUnqXCenBvLzARU9EzNiqsYvaV6atN5c= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556026240; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=9JZsi2FmVAwhc6LW8PKK6TIog3KdB3Nqo6MwCs1KYt4=; b=maoSRTAaAoFzw9WzUcj8oxY7O+9sAIhNOyku/p96LA0uKe4sWII6sOh0w13VTlOeXM6NCmm35tfs6oEU0U3qv3z4mjJq5lRIQ1kv3y9/FgV7QOgSbbvSp8ae0EpzjrH+/h5/GmrYbzYOOjwcdZWzlTj/4eUy8XjMj4edlUHs9zk= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556026240677708.8628579717243; Tue, 23 Apr 2019 06:30:40 -0700 (PDT) Received: from localhost ([127.0.0.1]:53951 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvVP-0006Wl-Jx for importer@patchew.org; Tue, 23 Apr 2019 09:30:39 -0400 Received: from eggs.gnu.org ([209.51.188.92]:60613) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvMR-000743-2m for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:24 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hIvMO-0005gQ-Ug for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:23 -0400 Received: from mx1.redhat.com ([209.132.183.28]:43834) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hIvMO-0005YH-LF for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:20 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 7BA782D7FB for ; Tue, 23 Apr 2019 13:21:09 +0000 (UTC) Received: from localhost (ovpn-112-54.ams2.redhat.com [10.36.112.54]) by smtp.corp.redhat.com (Postfix) with ESMTP id 4C4176013D; Tue, 23 Apr 2019 13:20:56 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Tue, 23 Apr 2019 15:19:58 +0200 Message-Id: <20190423132004.13725-6-marcandre.lureau@redhat.com> In-Reply-To: <20190423132004.13725-1-marcandre.lureau@redhat.com> References: <20190423132004.13725-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.30]); Tue, 23 Apr 2019 13:21:09 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v6 05/11] vhost-user: add vhost_user_gpu_set_socket() X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add a new vhost-user message to give a unix socket to a vhost-user backend for GPU display updates. Back when I started that work, I added a new GPU channel because the vhost-user protocol wasn't bidirectional. Since then, there is a vhost-user-slave channel for the slave to send requests to the master. We could extend it with GPU messages. However, the GPU protocol is quite orthogonal to vhost-user, thus I chose to have a new dedicated channel. See vhost-user-gpu.rst for the protocol details. Signed-off-by: Marc-Andr=C3=A9 Lureau --- contrib/libvhost-user/libvhost-user.h | 1 + include/hw/virtio/vhost-backend.h | 2 + contrib/libvhost-user/libvhost-user.c | 1 + hw/virtio/vhost-user.c | 11 ++ MAINTAINERS | 6 + docs/interop/index.rst | 2 +- docs/interop/vhost-user-gpu.rst | 238 ++++++++++++++++++++++++++ docs/interop/vhost-user.txt | 9 + 8 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 docs/interop/vhost-user-gpu.rst diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/= libvhost-user.h index 414ceb0a2f..eac5c120de 100644 --- a/contrib/libvhost-user/libvhost-user.h +++ b/contrib/libvhost-user/libvhost-user.h @@ -94,6 +94,7 @@ typedef enum VhostUserRequest { VHOST_USER_POSTCOPY_END =3D 30, VHOST_USER_GET_INFLIGHT_FD =3D 31, VHOST_USER_SET_INFLIGHT_FD =3D 32, + VHOST_USER_GPU_SET_SOCKET =3D 33, VHOST_USER_MAX } VhostUserRequest; =20 diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-ba= ckend.h index d6632a18e6..6f6670783f 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -170,4 +170,6 @@ int vhost_backend_invalidate_device_iotlb(struct vhost_= dev *dev, int vhost_backend_handle_iotlb_msg(struct vhost_dev *dev, struct vhost_iotlb_msg *imsg); =20 +int vhost_user_gpu_set_socket(struct vhost_dev *dev, int fd); + #endif /* VHOST_BACKEND_H */ diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/= libvhost-user.c index e738f6ce88..c9823a1354 100644 --- a/contrib/libvhost-user/libvhost-user.c +++ b/contrib/libvhost-user/libvhost-user.c @@ -130,6 +130,7 @@ vu_request_to_string(unsigned int req) REQ(VHOST_USER_POSTCOPY_END), REQ(VHOST_USER_GET_INFLIGHT_FD), REQ(VHOST_USER_SET_INFLIGHT_FD), + REQ(VHOST_USER_GPU_SET_SOCKET), REQ(VHOST_USER_MAX), }; #undef REQ diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 553319c7ac..4ca5b2551e 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -96,6 +96,7 @@ typedef enum VhostUserRequest { VHOST_USER_POSTCOPY_END =3D 30, VHOST_USER_GET_INFLIGHT_FD =3D 31, VHOST_USER_SET_INFLIGHT_FD =3D 32, + VHOST_USER_GPU_SET_SOCKET =3D 33, VHOST_USER_MAX } VhostUserRequest; =20 @@ -353,6 +354,16 @@ static int vhost_user_write(struct vhost_dev *dev, Vho= stUserMsg *msg, return 0; } =20 +int vhost_user_gpu_set_socket(struct vhost_dev *dev, int fd) +{ + VhostUserMsg msg =3D { + .hdr.request =3D VHOST_USER_GPU_SET_SOCKET, + .hdr.flags =3D VHOST_USER_VERSION, + }; + + return vhost_user_write(dev, &msg, &fd, 1); +} + static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base, struct vhost_log *log) { diff --git a/MAINTAINERS b/MAINTAINERS index 7e727299de..337eca10a3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1659,6 +1659,12 @@ F: hw/display/virtio-gpu* F: hw/display/virtio-vga.c F: include/hw/virtio/virtio-gpu.h =20 +vhost-user-gpu +M: Marc-Andr=C3=A9 Lureau +M: Gerd Hoffmann +S: Maintained +F: docs/interop/vhost-user-gpu.rst + Cirrus VGA M: Gerd Hoffmann S: Odd Fixes diff --git a/docs/interop/index.rst b/docs/interop/index.rst index 2df977dd52..28ead07466 100644 --- a/docs/interop/index.rst +++ b/docs/interop/index.rst @@ -15,4 +15,4 @@ Contents: bitmaps live-block-operations pr-helper - + vhost-user-gpu diff --git a/docs/interop/vhost-user-gpu.rst b/docs/interop/vhost-user-gpu.= rst new file mode 100644 index 0000000000..9cc1106b02 --- /dev/null +++ b/docs/interop/vhost-user-gpu.rst @@ -0,0 +1,238 @@ +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Vhost-user-gpu Protocol +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +:Licence: This work is licensed under the terms of the GNU GPL, + version 2 or later. See the COPYING file in the top-level + directory. + +.. contents:: Table of Contents + +Introduction +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The vhost-user-gpu protocol is aiming at sharing the rendering result +of a virtio-gpu, done from a vhost-user slave process to a vhost-user +master process (such as QEMU). It bears a resemblance to a display +server protocol, if you consider QEMU as the display server and the +slave as the client, but in a very limited way. Typically, it will +work by setting a scanout/display configuration, before sending flush +events for the display updates. It will also update the cursor shape +and position. + +The protocol is sent over a UNIX domain stream socket, since it uses +socket ancillary data to share opened file descriptors (DMABUF fds or +shared memory). The socket is usually obtained via +``VHOST_USER_GPU_SET_SOCKET``. + +Requests are sent by the *slave*, and the optional replies by the +*master*. + +Wire format +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Unless specified differently, numbers are in the machine native byte +order. + +A vhost-user-gpu request consists of 2 header fields and a payload. + ++---------+------+---------+ +| request | size | payload | ++---------+------+---------+ + +Header +------ + +:request: ``u32``, type of the request + +:size: ``u32``, size of the payload + +A reply consists only of a payload, whose content depends on the request. + +Payload types +------------- + +Depending on the request type, **payload** can be: + +VhostUserGpuCursorPos +^^^^^^^^^^^^^^^^^^^^^ + ++------------+---+---+ +| scanout-id | x | y | ++------------+---+---+ + +:scanout-id: ``u32``, the scanout where the cursor is located + +:x/y: ``u32``, the cursor postion + +VhostUserGpuCursorUpdate +^^^^^^^^^^^^^^^^^^^^^^^^ + ++-----+-------+-------+--------+ +| pos | hot_x | hot_y | cursor | ++-----+-------+-------+--------+ + +:pos: a ``VhostUserGpuCursorPos``, the cursor location + +:hot_x/hot_y: ``u32``, the cursor hot location + +:cursor: ``[u32; 64 * 64]``, 64x64 RGBA cursor data + +VhostUserGpuScanout +^^^^^^^^^^^^^^^^^^^ + ++------------+---+---+ +| scanout-id | w | h | ++------------+---+---+ + +:scanout-id: ``u32``, the scanout configuration to set + +:w/h: ``u32``, the scanout width/height size + +VhostUserGpuUpdate +^^^^^^^^^^^^^^^^^^ + ++------------+---+---+---+---+------+ +| scanout-id | x | y | w | h | data | ++------------+---+---+---+---+------+ + +:scanout-id: ``u32``, the scanout content to update + +:x/y/w/h: ``u32``, region of the update + +:data: RGBA data (the size is computed based on the region size, and + the request type) + +VhostUserGpuDMABUFScanout +^^^^^^^^^^^^^^^^^^^^^^^^^ + ++------------+---+---+---+---+-----+-----+--------+-------+--------+ +| scanout-id | x | y | w | h | fdw | fwh | stride | flags | fourcc | ++------------+---+---+---+---+-----+-----+--------+-------+--------+ + +:scanout-id: ``u32``, the scanout configuration to set + +:x/y: ``u32``, the location of the scanout within the DMABUF + +:w/h: ``u32``, the scanout width/height size + +:fdw/fdh/stride/flags: ``u32``, the DMABUF width/height/stride/flags + +:fourcc: ``i32``, the DMABUF fourcc + + +C structure +----------- + +In QEMU the vhost-user-gpu message is implemented with the following struc= t: + +.. code:: c + + typedef struct VhostUserGpuMsg { + uint32_t request; /* VhostUserGpuRequest */ + uint32_t size; /* the following payload size */ + union { + VhostUserGpuCursorPos cursor_pos; + VhostUserGpuCursorUpdate cursor_update; + VhostUserGpuScanout scanout; + VhostUserGpuUpdate update; + VhostUserGpuDMABUFScanout dmabuf_scanout; + uint64_t u64; + } payload; + } QEMU_PACKED VhostUserGpuMsg; + +Protocol features +----------------- + +None yet. + +As the protocol may need to evolve, new messages and communication +changes are negotiated thanks to preliminary +``VHOST_USER_GPU_GET_PROTOCOL_FEATURES`` and +``VHOST_USER_GPU_SET_PROTOCOL_FEATURES`` requests. + +Communication +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Message types +------------- + +``VHOST_USER_GPU_GET_PROTOCOL_FEATURES`` + :id: 1 + :request payload: N/A + :reply payload: ``u64`` + + Get the supported protocol features bitmask. + +``VHOST_USER_GPU_SET_PROTOCOL_FEATURES`` + :id: 2 + :request payload: ``u64`` + :reply payload: N/A + + Enable protocol features using a bitmask. + +``VHOST_USER_GPU_GET_DISPLAY_INFO`` + :id: 3 + :request payload: N/A + :reply payload: ``struct virtio_gpu_resp_display_info`` (from virtio spe= cification) + + Get the preferred display configuration. + +``VHOST_USER_GPU_CURSOR_POS`` + :id: 4 + :request payload: ``VhostUserGpuCursorPos`` + :reply payload: N/A + + Set/show the cursor position. + +``VHOST_USER_GPU_CURSOR_POS_HIDE`` + :id: 5 + :request payload: ``VhostUserGpuCursorPos`` + :reply payload: N/A + + Set/hide the cursor. + +``VHOST_USER_GPU_CURSOR_UPDATE`` + :id: 6 + :request payload: ``VhostUserGpuCursorUpdate`` + :reply payload: N/A + + Update the cursor shape and location. + +``VHOST_USER_GPU_SCANOUT`` + :id: 7 + :request payload: ``VhostUserGpuScanout`` + :reply payload: N/A + + Set the scanout resolution. To disable a scanout, the dimensions + width/height are set to 0. + +``VHOST_USER_GPU_UPDATE`` + :id: 8 + :request payload: ``VhostUserGpuUpdate`` + :reply payload: N/A + + Update the scanout content. The data payload contains the graphical bits. + The display should be flushed and presented. + +``VHOST_USER_GPU_DMABUF_SCANOUT`` + :id: 9 + :request payload: ``VhostUserGpuDMABUFScanout`` + :reply payload: N/A + + Set the scanout resolution/configuration, and share a DMABUF file + descriptor for the scanout content, which is passed as ancillary + data. To disable a scanout, the dimensions width/height are set + to 0, there is no file descriptor passed. + +``VHOST_USER_GPU_DMABUF_UPDATE`` + :id: 10 + :request payload: ``VhostUserGpuUpdate`` + :reply payload: ``u32`` + + The display should be flushed and presented according to updated + region from ``VhostUserGpuUpdate``. + + Note: there is no data payload, since the scanout is shared thanks + to DMABUF, that must have been set previously with + ``VHOST_USER_GPU_DMABUF_SCANOUT``. diff --git a/docs/interop/vhost-user.txt b/docs/interop/vhost-user.txt index 4dbd530cb9..1be2c74cc7 100644 --- a/docs/interop/vhost-user.txt +++ b/docs/interop/vhost-user.txt @@ -1051,6 +1051,15 @@ Master message types the shared inflight buffer back to slave so that slave could get inflight I/O after a crash or restart. =20 + * VHOST_USER_GPU_SET_SOCKET + Id: 33 + Master payload: N/A + Slave payload: N/A + + Sets the GPU protocol socket file descriptor, which is passed as + ancillary data. The GPU protocol is used to inform the master of + rendering state and updates. See vhost-user-gpu.rst for details. + Slave message types ------------------- =20 --=20 2.21.0.313.ge35b8cb8e2 From nobody Sun May 5 10:05:35 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1556026105; cv=none; d=zoho.com; s=zohoarc; b=i+7J0egSEBIURCHQ7R5S3Q8yErFOTEWNV5qcCSoYMIN4hUQyxK2StMiwPdIMhwoAJhTwDljYV6QiORij1eQoFFoLjoPJEc1G7fvK0q+yACD1hUGXaaiZb4XPxWe+73rEQWyigcs4TTpOCa4ZKEia/9IhjcL9/FqbKinxcGslvYk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556026105; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=YWcuhHmtR8W99iOYLujYJvWfqYIzOZCvtYxIdEjsTSs=; b=dV3tfX2pAXU6bfEncwVow/w+ufshvr7lWRJivoyLriAwIHst8ooxH623sFJDmU9BHLgsxqp4FBiZYvULFWZNHfK/mk9ojFW0ruBfvX9gioL9Dia4UrOwleZZen46HmoNmQ9oLSiUgotRLLCwcnQMM3JgoN1fJwuNktkr/xSeCvQ= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556026105588548.7074182084087; Tue, 23 Apr 2019 06:28:25 -0700 (PDT) Received: from localhost ([127.0.0.1]:53903 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvTB-0004Nl-GP for importer@patchew.org; Tue, 23 Apr 2019 09:28:21 -0400 Received: from eggs.gnu.org ([209.51.188.92]:60589) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvMQ-00073D-6N for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:23 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hIvMO-0005gK-Tl for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:22 -0400 Received: from mx1.redhat.com ([209.132.183.28]:57219) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hIvMO-0005Zg-KZ for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:20 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id AFC1D30789BE for ; Tue, 23 Apr 2019 13:21:11 +0000 (UTC) Received: from localhost (ovpn-112-54.ams2.redhat.com [10.36.112.54]) by smtp.corp.redhat.com (Postfix) with ESMTP id 397AA6013B; Tue, 23 Apr 2019 13:21:10 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Tue, 23 Apr 2019 15:19:59 +0200 Message-Id: <20190423132004.13725-7-marcandre.lureau@redhat.com> In-Reply-To: <20190423132004.13725-1-marcandre.lureau@redhat.com> References: <20190423132004.13725-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.49]); Tue, 23 Apr 2019 13:21:11 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v6 06/11] virtio: add virtio-gpu bswap helpers header X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" The helper functions are useful to build the vhost-user-gpu backend. Signed-off-by: Marc-Andr=C3=A9 Lureau --- include/hw/virtio/virtio-gpu-bswap.h | 61 ++++++++++++++++++++++++++++ hw/display/virtio-gpu.c | 43 +------------------- 2 files changed, 62 insertions(+), 42 deletions(-) create mode 100644 include/hw/virtio/virtio-gpu-bswap.h diff --git a/include/hw/virtio/virtio-gpu-bswap.h b/include/hw/virtio/virti= o-gpu-bswap.h new file mode 100644 index 0000000000..38d12160f6 --- /dev/null +++ b/include/hw/virtio/virtio-gpu-bswap.h @@ -0,0 +1,61 @@ +/* + * Virtio GPU Device + * + * Copyright Red Hat, Inc. 2013-2014 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_VIRTIO_GPU_BSWAP_H +#define HW_VIRTIO_GPU_BSWAP_H + +#include "qemu/bswap.h" + +static inline void +virtio_gpu_ctrl_hdr_bswap(struct virtio_gpu_ctrl_hdr *hdr) +{ + le32_to_cpus(&hdr->type); + le32_to_cpus(&hdr->flags); + le64_to_cpus(&hdr->fence_id); + le32_to_cpus(&hdr->ctx_id); + le32_to_cpus(&hdr->padding); +} + +static inline void +virtio_gpu_bswap_32(void *ptr, size_t size) +{ +#ifdef HOST_WORDS_BIGENDIAN + + size_t i; + struct virtio_gpu_ctrl_hdr *hdr =3D (struct virtio_gpu_ctrl_hdr *) ptr; + + virtio_gpu_ctrl_hdr_bswap(hdr); + + i =3D sizeof(struct virtio_gpu_ctrl_hdr); + while (i < size) { + le32_to_cpus((uint32_t *)(ptr + i)); + i =3D i + sizeof(uint32_t); + } + +#endif +} + +static inline void +virtio_gpu_t2d_bswap(struct virtio_gpu_transfer_to_host_2d *t2d) +{ + virtio_gpu_ctrl_hdr_bswap(&t2d->hdr); + le32_to_cpus(&t2d->r.x); + le32_to_cpus(&t2d->r.y); + le32_to_cpus(&t2d->r.width); + le32_to_cpus(&t2d->r.height); + le64_to_cpus(&t2d->offset); + le32_to_cpus(&t2d->resource_id); + le32_to_cpus(&t2d->padding); +} + +#endif diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 9e37e0ac96..c35025b6fb 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -20,6 +20,7 @@ #include "sysemu/dma.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-gpu.h" +#include "hw/virtio/virtio-gpu-bswap.h" #include "hw/virtio/virtio-bus.h" #include "hw/display/edid.h" #include "migration/blocker.h" @@ -34,48 +35,6 @@ virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource= _id); static void virtio_gpu_cleanup_mapping(VirtIOGPU *g, struct virtio_gpu_simple_resource *= res); =20 -static void -virtio_gpu_ctrl_hdr_bswap(struct virtio_gpu_ctrl_hdr *hdr) -{ - le32_to_cpus(&hdr->type); - le32_to_cpus(&hdr->flags); - le64_to_cpus(&hdr->fence_id); - le32_to_cpus(&hdr->ctx_id); - le32_to_cpus(&hdr->padding); -} - -static void virtio_gpu_bswap_32(void *ptr, - size_t size) -{ -#ifdef HOST_WORDS_BIGENDIAN - - size_t i; - struct virtio_gpu_ctrl_hdr *hdr =3D (struct virtio_gpu_ctrl_hdr *) ptr; - - virtio_gpu_ctrl_hdr_bswap(hdr); - - i =3D sizeof(struct virtio_gpu_ctrl_hdr); - while (i < size) { - le32_to_cpus((uint32_t *)(ptr + i)); - i =3D i + sizeof(uint32_t); - } - -#endif -} - -static void -virtio_gpu_t2d_bswap(struct virtio_gpu_transfer_to_host_2d *t2d) -{ - virtio_gpu_ctrl_hdr_bswap(&t2d->hdr); - le32_to_cpus(&t2d->r.x); - le32_to_cpus(&t2d->r.y); - le32_to_cpus(&t2d->r.width); - le32_to_cpus(&t2d->r.height); - le64_to_cpus(&t2d->offset); - le32_to_cpus(&t2d->resource_id); - le32_to_cpus(&t2d->padding); -} - #ifdef CONFIG_VIRGL #include #define VIRGL(_g, _virgl, _simple, ...) \ --=20 2.21.0.313.ge35b8cb8e2 From nobody Sun May 5 10:05:35 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1556026182; cv=none; d=zoho.com; s=zohoarc; b=Q7CzTS9VnaTrAYinrYoZED9vj2sJRdcL9hXVM3nlFr5Zg49YYT43rA6bEnCiDsA6iypmMIuoWF3TJFXw0A02pz7eLF5DmGGKDtp2Ut1P5yQwOZ1NSbP3tII6lo8NY8TnXSGms4tqOTuhWK7jdR5LibqtFzLtX6G6zEerphmirjU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556026182; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=+OpNBIT1sJhU9Ywp2PKb2kDwi2fkJYb9WBDLzHoypII=; b=Gc4Pty51330GQKLUKGmGpYmnrbGHn+B+DCkCMAY3QuTR2pC1/AY9IYrt9IxmeFu8iQWuycC1Txx5Tjt/gvnIwUHfuWPAQh3vwQpPV39ZPkbNqsvpo0SdJ0fzxQ/ymY+R4ouk+CWO+v3hL/hy69a8udod8Xgp4wFb1PVrl5WRX7g= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556026182002860.0405999812345; Tue, 23 Apr 2019 06:29:42 -0700 (PDT) Received: from localhost ([127.0.0.1]:53911 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvUR-0005VA-38 for importer@patchew.org; Tue, 23 Apr 2019 09:29:39 -0400 Received: from eggs.gnu.org ([209.51.188.92]:60595) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvMQ-00073R-E4 for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:23 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hIvMP-0005h8-E3 for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:22 -0400 Received: from mx1.redhat.com ([209.132.183.28]:21065) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hIvMP-0005fx-8S for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:21 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 88442309266B for ; Tue, 23 Apr 2019 13:21:20 +0000 (UTC) Received: from localhost (ovpn-112-54.ams2.redhat.com [10.36.112.54]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9005A5D71E; Tue, 23 Apr 2019 13:21:13 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Tue, 23 Apr 2019 15:20:00 +0200 Message-Id: <20190423132004.13725-8-marcandre.lureau@redhat.com> In-Reply-To: <20190423132004.13725-1-marcandre.lureau@redhat.com> References: <20190423132004.13725-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.43]); Tue, 23 Apr 2019 13:21:20 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v6 07/11] util: compile drm.o on Linux X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" OpenGL isn't required to use DRM rendernodes. The following patches uses it for 2d resources for ex. Signed-off-by: Marc-Andr=C3=A9 Lureau --- util/Makefile.objs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/Makefile.objs b/util/Makefile.objs index 835fcd69e2..119a3f9b25 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -52,6 +52,6 @@ util-obj-y +=3D systemd.o util-obj-y +=3D iova-tree.o util-obj-$(CONFIG_INOTIFY1) +=3D filemonitor-inotify.o util-obj-$(CONFIG_LINUX) +=3D vfio-helpers.o -util-obj-$(CONFIG_OPENGL) +=3D drm.o +util-obj-$(CONFIG_LINUX) +=3D drm.o =20 stub-obj-y +=3D filemonitor-stub.o --=20 2.21.0.313.ge35b8cb8e2 From nobody Sun May 5 10:05:35 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1556025963; cv=none; d=zoho.com; s=zohoarc; b=eACNWzESvHHF9HnAdlceBOcLNhVDJ8WbZFU7uUJkWTTe2eUEOGyawxBg0SzlCAlZ3qR3WqHP6TLWHUvcbJ91715T/Qjwn1cQcP6GpZ/CoYvxoBsSGQn9b5kwmGvTS+cCMxy9hoMHUW80IARSH2K2Xju7L5nxNf+4r6Cs6w2Hd+o= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556025963; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=6HLZGuKyZt5vBdSFwUB3g+VPmPoSzN1imSi//D+6Nh0=; b=JZnGyc05TIp4zVWx4UlXbGMaTl6+yUcMA0n+ogyUM/Re2wdA50CE0LATMwG0OzG5zzNsazVuREDpew5x6RYkmG3ubx54GfSnzlRwBal5XwY3xnJkwEapc7bzRrFYdql/AHS4kH+5/bQ3Ek+YryMegMAwdeKc06+I0wF84Q4pwE0= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556025963128129.21229356356469; Tue, 23 Apr 2019 06:26:03 -0700 (PDT) Received: from localhost ([127.0.0.1]:53874 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvQt-0001u8-M4 for importer@patchew.org; Tue, 23 Apr 2019 09:25:59 -0400 Received: from eggs.gnu.org ([209.51.188.92]:60725) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvMf-0007Ia-Te for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:43 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hIvMZ-0005ql-Vs for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:37 -0400 Received: from mx1.redhat.com ([209.132.183.28]:18731) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hIvMZ-0005q5-Ib for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:31 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id D736E330279 for ; Tue, 23 Apr 2019 13:21:30 +0000 (UTC) Received: from localhost (ovpn-112-54.ams2.redhat.com [10.36.112.54]) by smtp.corp.redhat.com (Postfix) with ESMTP id 364D35C3FA; Tue, 23 Apr 2019 13:21:22 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Tue, 23 Apr 2019 15:20:01 +0200 Message-Id: <20190423132004.13725-9-marcandre.lureau@redhat.com> In-Reply-To: <20190423132004.13725-1-marcandre.lureau@redhat.com> References: <20190423132004.13725-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.29]); Tue, 23 Apr 2019 13:21:30 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v6 08/11] contrib: add vhost-user-gpu X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add a vhost-user gpu backend, based on virtio-gpu/3d device. It is to be associated with a vhost-user-gpu device. Various TODO and nice to have items: - multi-head support - crash & resume handling - accelerated rendering/display that avoids the waiting round trips - more drm devices support for 2d surfaces - edid support Signed-off-by: Marc-Andr=C3=A9 Lureau --- contrib/vhost-user-gpu/drm.h | 72 ++ contrib/vhost-user-gpu/virgl.h | 25 + contrib/vhost-user-gpu/vugpu.h | 170 +++ contrib/vhost-user-gpu/drm.c | 341 ++++++ contrib/vhost-user-gpu/main.c | 1236 ++++++++++++++++++++ contrib/vhost-user-gpu/virgl.c | 579 +++++++++ MAINTAINERS | 2 + Makefile | 23 +- Makefile.objs | 1 + configure | 29 + contrib/vhost-user-gpu/50-qemu-gpu.json.in | 5 + contrib/vhost-user-gpu/Makefile.objs | 10 + rules.mak | 9 +- 13 files changed, 2500 insertions(+), 2 deletions(-) create mode 100644 contrib/vhost-user-gpu/drm.h create mode 100644 contrib/vhost-user-gpu/virgl.h create mode 100644 contrib/vhost-user-gpu/vugpu.h create mode 100644 contrib/vhost-user-gpu/drm.c create mode 100644 contrib/vhost-user-gpu/main.c create mode 100644 contrib/vhost-user-gpu/virgl.c create mode 100644 contrib/vhost-user-gpu/50-qemu-gpu.json.in create mode 100644 contrib/vhost-user-gpu/Makefile.objs diff --git a/contrib/vhost-user-gpu/drm.h b/contrib/vhost-user-gpu/drm.h new file mode 100644 index 0000000000..1c3e7b2c51 --- /dev/null +++ b/contrib/vhost-user-gpu/drm.h @@ -0,0 +1,72 @@ +/* + * Virtio vhost-user GPU Device + * + * DRM helpers + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ +#ifndef VHOST_USER_GPU_DRM_H +#define VHOST_USER_GPU_DRM_H + +#include "qemu/osdep.h" + +#ifdef CONFIG_LIBDRM_INTEL +#include +#include +#endif + +#ifdef CONFIG_MEMFD +#include +#include +#endif + +struct drm_buffer; + +struct drm_device { + bool inited; + int fd; + char *name; +#ifdef CONFIG_LIBDRM_INTEL + drm_intel_bufmgr *bufmgr; +#endif + + bool (*alloc_bo)(struct drm_buffer *buf); + void (*free_bo)(struct drm_buffer *buf); + bool (*export_bo_to_prime)(struct drm_buffer *buf, int *fd); + bool (*map_bo)(struct drm_buffer *buf); + void (*unmap_bo)(struct drm_buffer *buf); + void (*device_destroy)(struct drm_device *dev); +}; + +struct drm_buffer { + struct drm_device *dev; + +#ifdef CONFIG_LIBDRM_INTEL + drm_intel_bo *intel_bo; +#endif /* HAVE_LIBDRM_INTEL */ +#ifdef CONFIG_MEMFD + int memfd; +#endif + + uint32_t gem_handle; + int dmabuf_fd; + uint8_t *mmap; + + int width; + int height; + int bpp; + unsigned long stride; + int format; +}; + +bool drm_device_init(struct drm_device *dev, int fd); +void drm_device_destroy(struct drm_device *dev); + +bool drm_buffer_create(struct drm_buffer *buffer, struct drm_device *dev, + int width, int height); +bool drm_buffer_can_get_dmabuf_fd(struct drm_buffer *buffer); +bool drm_buffer_get_dmabuf_fd(struct drm_buffer *buffer, int *fd); +void drm_buffer_destroy(struct drm_buffer *buffer); + +#endif diff --git a/contrib/vhost-user-gpu/virgl.h b/contrib/vhost-user-gpu/virgl.h new file mode 100644 index 0000000000..f952bc9d4f --- /dev/null +++ b/contrib/vhost-user-gpu/virgl.h @@ -0,0 +1,25 @@ +/* + * Virtio vhost-user GPU Device + * + * Copyright Red Hat, Inc. 2013-2018 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * Marc-Andr=C3=A9 Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ +#ifndef VUGPU_VIRGL_H_ +#define VUGPU_VIRGL_H_ + +#include "vugpu.h" + +bool vg_virgl_init(VuGpu *g); +uint32_t vg_virgl_get_num_capsets(void); +void vg_virgl_process_cmd(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd); +void vg_virgl_update_cursor_data(VuGpu *g, uint32_t resource_id, + gpointer data); + +#endif diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h new file mode 100644 index 0000000000..fbee95e7b9 --- /dev/null +++ b/contrib/vhost-user-gpu/vugpu.h @@ -0,0 +1,170 @@ +/* + * Virtio vhost-user GPU Device + * + * Copyright Red Hat, Inc. 2013-2018 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * Marc-Andr=C3=A9 Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ +#ifndef VUGPU_H_ +#define VUGPU_H_ + +#include "qemu/osdep.h" + +#include "contrib/libvhost-user/libvhost-user-glib.h" +#include "standard-headers/linux/virtio_gpu.h" + +#include "qemu/queue.h" +#include "qemu/iov.h" +#include "qemu/bswap.h" +#include "drm.h" + +typedef enum VhostUserGpuRequest { + VHOST_USER_GPU_NONE =3D 0, + VHOST_USER_GPU_GET_PROTOCOL_FEATURES, + VHOST_USER_GPU_SET_PROTOCOL_FEATURES, + VHOST_USER_GPU_GET_DISPLAY_INFO, + VHOST_USER_GPU_CURSOR_POS, + VHOST_USER_GPU_CURSOR_POS_HIDE, + VHOST_USER_GPU_CURSOR_UPDATE, + VHOST_USER_GPU_SCANOUT, + VHOST_USER_GPU_UPDATE, + VHOST_USER_GPU_DMABUF_SCANOUT, + VHOST_USER_GPU_DMABUF_UPDATE, +} VhostUserGpuRequest; + +typedef struct VhostUserGpuDisplayInfoReply { + struct virtio_gpu_resp_display_info info; +} VhostUserGpuDisplayInfoReply; + +typedef struct VhostUserGpuCursorPos { + uint32_t scanout_id; + uint32_t x; + uint32_t y; +} QEMU_PACKED VhostUserGpuCursorPos; + +typedef struct VhostUserGpuCursorUpdate { + VhostUserGpuCursorPos pos; + uint32_t hot_x; + uint32_t hot_y; + uint32_t data[64 * 64]; +} QEMU_PACKED VhostUserGpuCursorUpdate; + +typedef struct VhostUserGpuScanout { + uint32_t scanout_id; + uint32_t width; + uint32_t height; +} QEMU_PACKED VhostUserGpuScanout; + +typedef struct VhostUserGpuUpdate { + uint32_t scanout_id; + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; + uint8_t data[]; +} QEMU_PACKED VhostUserGpuUpdate; + +typedef struct VhostUserGpuDMABUFScanout { + uint32_t scanout_id; + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; + uint32_t fd_width; + uint32_t fd_height; + uint32_t fd_stride; + uint32_t fd_flags; + int fd_drm_fourcc; +} QEMU_PACKED VhostUserGpuDMABUFScanout; + +typedef struct VhostUserGpuMsg { + uint32_t request; /* VhostUserGpuRequest */ + uint32_t size; /* the following payload size */ + union { + VhostUserGpuCursorPos cursor_pos; + VhostUserGpuCursorUpdate cursor_update; + VhostUserGpuScanout scanout; + VhostUserGpuUpdate update; + VhostUserGpuDMABUFScanout dmabuf_scanout; + uint64_t u64; + } payload; +} QEMU_PACKED VhostUserGpuMsg; + +static VhostUserGpuMsg m __attribute__ ((unused)); +#define VHOST_USER_GPU_HDR_SIZE (sizeof(m.request) + sizeof(m.size)) + +struct virtio_gpu_scanout { + uint32_t width, height; + int x, y; + int invalidate; + uint32_t resource_id; +}; + +typedef struct VuGpu { + VugDev dev; + struct virtio_gpu_config virtio_config; + struct drm_device drm_dev; + int sock_fd; + int drm_rnode_fd; + GSource *renderer_source; + guint wait_ok; + + bool virgl; + bool virgl_inited; + uint32_t inflight; + + struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS]; + QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist; + QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq; +} VuGpu; + +struct virtio_gpu_ctrl_command { + VuVirtqElement elem; + VuVirtq *vq; + struct virtio_gpu_ctrl_hdr cmd_hdr; + uint32_t error; + bool finished; + QTAILQ_ENTRY(virtio_gpu_ctrl_command) next; +}; + +#define VUGPU_FILL_CMD(out) do { \ + size_t s; \ + s =3D iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \ + &out, sizeof(out)); \ + if (s !=3D sizeof(out)) { \ + g_critical("%s: command size incorrect %zu vs %zu", \ + __func__, s, sizeof(out)); \ + return; \ + } \ + } while (0) + + +void vg_ctrl_response(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd, + struct virtio_gpu_ctrl_hdr *resp, + size_t resp_len); + +void vg_ctrl_response_nodata(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd, + enum virtio_gpu_ctrl_type type); + +int vg_create_mapping_iov(VuGpu *g, + struct virtio_gpu_resource_attach_backing *a= b, + struct virtio_gpu_ctrl_command *cmd, + struct iovec **iov); + +void vg_get_display_info(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd= ); + +void vg_wait_ok(VuGpu *g); + +void vg_send_msg(VuGpu *g, const VhostUserGpuMsg *msg, int fd); + +int vg_sock_fd_read(int sock, void *buf, ssize_t buflen); + +#endif diff --git a/contrib/vhost-user-gpu/drm.c b/contrib/vhost-user-gpu/drm.c new file mode 100644 index 0000000000..70177918a9 --- /dev/null +++ b/contrib/vhost-user-gpu/drm.c @@ -0,0 +1,341 @@ +/* + * Virtio vhost-user GPU Device + * + * DRM helpers + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#include /* not much to do with xf86 */ +#include + +#include "drm.h" + +static bool +mem_alloc_bo(struct drm_buffer *buf) +{ + buf->mmap =3D g_malloc(buf->width * buf->height * buf->bpp / 8); + buf->stride =3D buf->width * buf->bpp / 8; + return true; +} + +static void +mem_free_bo(struct drm_buffer *buf) +{ + g_free(buf->mmap); +} + +static bool +mem_map_bo(struct drm_buffer *buf) +{ + return buf->mmap !=3D NULL; +} + +static void +mem_unmap_bo(struct drm_buffer *buf) +{ +} + +static void +mem_device_destroy(struct drm_device *dev) +{ +} + +#ifdef CONFIG_MEMFD +struct udmabuf_create { + uint32_t memfd; + uint32_t flags; + uint64_t offset; + uint64_t size; +}; + +#define UDMABUF_CREATE _IOW('u', 0x42, struct udmabuf_create) + +static size_t +udmabuf_get_size(struct drm_buffer *buf) +{ + return ROUND_UP(buf->width * buf->height * buf->bpp / 8, getpagesize()= ); +} + +static bool +udmabuf_alloc_bo(struct drm_buffer *buf) +{ + int ret; + + buf->memfd =3D memfd_create("udmabuf-bo", MFD_ALLOW_SEALING); + if (buf->memfd < 0) { + return false; + } + + ret =3D ftruncate(buf->memfd, udmabuf_get_size(buf)); + if (ret < 0) { + close(buf->memfd); + return false; + } + + ret =3D fcntl(buf->memfd, F_ADD_SEALS, F_SEAL_SHRINK); + if (ret < 0) { + close(buf->memfd); + return false; + } + + buf->stride =3D buf->width * buf->bpp / 8; + + return true; +} + +static void +udmabuf_free_bo(struct drm_buffer *buf) +{ + close(buf->memfd); +} + +static bool +udmabuf_map_bo(struct drm_buffer *buf) +{ + buf->mmap =3D mmap(NULL, udmabuf_get_size(buf), + PROT_READ | PROT_WRITE, MAP_SHARED, buf->memfd, 0); + if (buf->mmap =3D=3D MAP_FAILED) { + return false; + } + + return true; +} + +static bool +udmabuf_export_bo_to_prime(struct drm_buffer *buf, int *fd) +{ + struct udmabuf_create create =3D { + .memfd =3D buf->memfd, + .offset =3D 0, + .size =3D udmabuf_get_size(buf), + }; + + *fd =3D ioctl(buf->dev->fd, UDMABUF_CREATE, &create); + + return *fd >=3D 0; +} + +static void +udmabuf_unmap_bo(struct drm_buffer *buf) +{ + munmap(buf->mmap, udmabuf_get_size(buf)); +} + +static void +udmabuf_device_destroy(struct drm_device *dev) +{ + close(dev->fd); +} +#endif + +#ifdef CONFIG_LIBDRM_INTEL +static bool +intel_alloc_bo(struct drm_buffer *buf) +{ + uint32_t tiling =3D I915_TILING_NONE; + + buf->intel_bo =3D drm_intel_bo_alloc_tiled(buf->dev->bufmgr, "vhost-us= er-gpu", + buf->width, buf->height, + (buf->bpp / 8), &tiling, + &buf->stride, 0); + + if (!buf->intel_bo) { + return false; + } + + if (tiling !=3D I915_TILING_NONE) { + drm_intel_bo_unreference(buf->intel_bo); + return false; + } + + return true; +} + +static void +intel_free_bo(struct drm_buffer *buf) +{ + drm_intel_bo_unreference(buf->intel_bo); +} + +static bool +intel_map_bo(struct drm_buffer *buf) +{ + if (drm_intel_gem_bo_map_gtt(buf->intel_bo) !=3D 0) { + return false; + } + + buf->mmap =3D buf->intel_bo->virtual; + + return true; +} + +static bool +intel_export_bo_to_prime(struct drm_buffer *buffer, int *fd) +{ + if (drm_intel_bo_gem_export_to_prime(buffer->intel_bo, fd) < 0) { + return false; + } + + return true; +} + +static void +intel_unmap_bo(struct drm_buffer *buf) +{ + drm_intel_gem_bo_unmap_gtt(buf->intel_bo); +} + +static void +intel_device_destroy(struct drm_device *dev) +{ + drm_intel_bufmgr_destroy(dev->bufmgr); +} + +#endif /* CONFIG_LIBDRM_INTEL */ + +void +drm_device_destroy(struct drm_device *dev) +{ + if (!dev->inited) { + return; + } + + dev->device_destroy(dev); + g_free(dev->name); +} + +bool +drm_device_init(struct drm_device *dev, int fd) +{ + drmVersionPtr version =3D drmGetVersion(fd); + + dev->fd =3D fd; + dev->name =3D g_strdup(version->name); + drmFreeVersion(version); + + if (0) { + /* nothing */ + } +#ifdef CONFIG_LIBDRM_INTEL + else if (g_str_equal(dev->name, "i915")) { + dev->bufmgr =3D drm_intel_bufmgr_gem_init(fd, 32); + if (!dev->bufmgr) { + return false; + } + dev->alloc_bo =3D intel_alloc_bo; + dev->free_bo =3D intel_free_bo; + dev->export_bo_to_prime =3D intel_export_bo_to_prime; + dev->map_bo =3D intel_map_bo; + dev->unmap_bo =3D intel_unmap_bo; + dev->device_destroy =3D intel_device_destroy; + } +#endif +#ifdef CONFIG_MEMFD + else if (g_file_test("/dev/udmabuf", G_FILE_TEST_EXISTS)) { + dev->fd =3D open("/dev/udmabuf", O_RDWR); + if (dev->fd < 0) { + return false; + } + g_debug("Using experimental udmabuf backend"); + dev->alloc_bo =3D udmabuf_alloc_bo; + dev->free_bo =3D udmabuf_free_bo; + dev->export_bo_to_prime =3D udmabuf_export_bo_to_prime; + dev->map_bo =3D udmabuf_map_bo; + dev->unmap_bo =3D udmabuf_unmap_bo; + dev->device_destroy =3D udmabuf_device_destroy; + } +#endif + else { + g_debug("Using mem fallback"); + dev->alloc_bo =3D mem_alloc_bo; + dev->free_bo =3D mem_free_bo; + dev->map_bo =3D mem_map_bo; + dev->unmap_bo =3D mem_unmap_bo; + dev->device_destroy =3D mem_device_destroy; + return false; + } + + dev->inited =3D true; + return true; +} + +static bool +drm_buffer_map(struct drm_buffer *buf) +{ + struct drm_device *dev =3D buf->dev; + + return dev->map_bo(buf); +} + +static void +drm_buffer_unmap(struct drm_buffer *buf) +{ + struct drm_device *dev =3D buf->dev; + + dev->unmap_bo(buf); +} + +bool +drm_buffer_can_get_dmabuf_fd(struct drm_buffer *buffer) +{ + if (!buffer->dev->export_bo_to_prime) { + return false; + } + + return true; +} + +bool +drm_buffer_get_dmabuf_fd(struct drm_buffer *buffer, int *fd) +{ + if (!drm_buffer_can_get_dmabuf_fd(buffer) || + !buffer->dev->export_bo_to_prime(buffer, fd)) { + g_warning("Failed to get dmabuf"); + return false; + } + + if (*fd < 0) { + g_warning("error: dmabuf_fd < 0"); + return false; + } + + return true; +} + +bool +drm_buffer_create(struct drm_buffer *buffer, struct drm_device *dev, + int width, int height) +{ + buffer->dev =3D dev; + buffer->width =3D width; + buffer->height =3D height; + buffer->stride =3D 0; /* modified during alloc */ + buffer->bpp =3D 32; + buffer->format =3D DRM_FORMAT_XRGB8888; + if (!dev->alloc_bo(buffer)) { + g_warning("alloc_bo failed"); + return false; + } + + if (!drm_buffer_map(buffer)) { + g_warning("map_bo failed"); + goto err; + } + + return true; + +err: + dev->free_bo(buffer); + return false; +} + +void +drm_buffer_destroy(struct drm_buffer *buffer) +{ + struct drm_device *drm_dev =3D buffer->dev; + + drm_buffer_unmap(buffer); + drm_dev->free_bo(buffer); +} diff --git a/contrib/vhost-user-gpu/main.c b/contrib/vhost-user-gpu/main.c new file mode 100644 index 0000000000..0b2fd81797 --- /dev/null +++ b/contrib/vhost-user-gpu/main.c @@ -0,0 +1,1236 @@ +/* + * Virtio vhost-user GPU Device + * + * Copyright Red Hat, Inc. 2013-2018 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * Marc-Andr=C3=A9 Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/drm.h" +#include "qapi/error.h" + +#include +#include + +#include "vugpu.h" +#include "hw/virtio/virtio-gpu-bswap.h" +#include "virgl.h" + +struct virtio_gpu_simple_resource { + uint32_t resource_id; + uint32_t width; + uint32_t height; + uint32_t format; + struct iovec *iov; + unsigned int iov_cnt; + uint32_t scanout_bitmask; + pixman_image_t *image; + struct drm_buffer drm_buffer; + QTAILQ_ENTRY(virtio_gpu_simple_resource) next; +}; + +static gboolean opt_print_caps; +static int opt_fdnum =3D -1; +static char *opt_socket_path; +static char *opt_render_node; +static gboolean opt_virgl; + +static void vg_handle_ctrl(VuDev *dev, int qidx); + +static const char * +vg_cmd_to_string(int cmd) +{ +#define CMD(cmd) [cmd] =3D #cmd + static const char *vg_cmd_str[] =3D { + CMD(VIRTIO_GPU_UNDEFINED), + + /* 2d commands */ + CMD(VIRTIO_GPU_CMD_GET_DISPLAY_INFO), + CMD(VIRTIO_GPU_CMD_RESOURCE_CREATE_2D), + CMD(VIRTIO_GPU_CMD_RESOURCE_UNREF), + CMD(VIRTIO_GPU_CMD_SET_SCANOUT), + CMD(VIRTIO_GPU_CMD_RESOURCE_FLUSH), + CMD(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D), + CMD(VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING), + CMD(VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING), + CMD(VIRTIO_GPU_CMD_GET_CAPSET_INFO), + CMD(VIRTIO_GPU_CMD_GET_CAPSET), + + /* 3d commands */ + CMD(VIRTIO_GPU_CMD_CTX_CREATE), + CMD(VIRTIO_GPU_CMD_CTX_DESTROY), + CMD(VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE), + CMD(VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE), + CMD(VIRTIO_GPU_CMD_RESOURCE_CREATE_3D), + CMD(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D), + CMD(VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D), + CMD(VIRTIO_GPU_CMD_SUBMIT_3D), + + /* cursor commands */ + CMD(VIRTIO_GPU_CMD_UPDATE_CURSOR), + CMD(VIRTIO_GPU_CMD_MOVE_CURSOR), + }; +#undef REQ + + if (cmd >=3D 0 && cmd < G_N_ELEMENTS(vg_cmd_str)) { + return vg_cmd_str[cmd]; + } else { + return "unknown"; + } +} + +int +vg_sock_fd_read(int sock, void *buf, ssize_t buflen) +{ + int ret; + + do { + ret =3D read(sock, buf, buflen); + } while (ret < 0 && (errno =3D=3D EINTR || errno =3D=3D EAGAIN)); + + g_warn_if_fail(ret =3D=3D buflen); + return ret; +} + +static void +vg_sock_fd_close(VuGpu *g) +{ + if (g->sock_fd >=3D 0) { + close(g->sock_fd); + g->sock_fd =3D -1; + } +} + +static gboolean +source_wait_cb(gint fd, GIOCondition condition, gpointer user_data) +{ + VuGpu *g =3D user_data; + uint32_t ok; + + if (vg_sock_fd_read(g->sock_fd, &ok, sizeof(ok)) < 0) { + vg_sock_fd_close(g); + } + + /* resume */ + g->wait_ok =3D 0; + vg_handle_ctrl(&g->dev.parent, 0); + + return G_SOURCE_REMOVE; +} + +void +vg_wait_ok(VuGpu *g) +{ + assert(g->wait_ok =3D=3D 0); + g->wait_ok =3D g_unix_fd_add(g->sock_fd, G_IO_IN | G_IO_HUP, + source_wait_cb, g); +} + +static int +vg_sock_fd_write(int sock, const void *buf, ssize_t buflen, int fd) +{ + ssize_t ret; + struct msghdr msg; + struct iovec iov; + union { + struct cmsghdr cmsghdr; + char control[CMSG_SPACE(sizeof(int))]; + } cmsgu; + struct cmsghdr *cmsg; + + iov.iov_base =3D (void *)buf; + iov.iov_len =3D buflen; + + msg.msg_name =3D NULL; + msg.msg_namelen =3D 0; + msg.msg_iov =3D &iov; + msg.msg_iovlen =3D 1; + + if (fd !=3D -1) { + msg.msg_control =3D cmsgu.control; + msg.msg_controllen =3D sizeof(cmsgu.control); + + cmsg =3D CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len =3D CMSG_LEN(sizeof(int)); + cmsg->cmsg_level =3D SOL_SOCKET; + cmsg->cmsg_type =3D SCM_RIGHTS; + + *((int *)CMSG_DATA(cmsg)) =3D fd; + } else { + msg.msg_control =3D NULL; + msg.msg_controllen =3D 0; + } + + do { + ret =3D sendmsg(sock, &msg, 0); + } while (ret =3D=3D -1 && (errno =3D=3D EINTR || errno =3D=3D EAGAIN)); + + g_warn_if_fail(ret =3D=3D buflen); + return ret; +} + +void +vg_send_msg(VuGpu *vg, const VhostUserGpuMsg *msg, int fd) +{ + if (vg_sock_fd_write(vg->sock_fd, msg, + VHOST_USER_GPU_HDR_SIZE + msg->size, fd) < 0) { + vg_sock_fd_close(vg); + } +} + +static struct virtio_gpu_simple_resource * +virtio_gpu_find_resource(VuGpu *g, uint32_t resource_id) +{ + struct virtio_gpu_simple_resource *res; + + QTAILQ_FOREACH(res, &g->reslist, next) { + if (res->resource_id =3D=3D resource_id) { + return res; + } + } + return NULL; +} + +void +vg_ctrl_response(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd, + struct virtio_gpu_ctrl_hdr *resp, + size_t resp_len) +{ + size_t s; + + if (cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE) { + resp->flags |=3D VIRTIO_GPU_FLAG_FENCE; + resp->fence_id =3D cmd->cmd_hdr.fence_id; + resp->ctx_id =3D cmd->cmd_hdr.ctx_id; + } + virtio_gpu_ctrl_hdr_bswap(resp); + s =3D iov_from_buf(cmd->elem.in_sg, cmd->elem.in_num, 0, resp, resp_le= n); + if (s !=3D resp_len) { + g_critical("%s: response size incorrect %zu vs %zu", + __func__, s, resp_len); + } + vu_queue_push(&g->dev.parent, cmd->vq, &cmd->elem, s); + vu_queue_notify(&g->dev.parent, cmd->vq); + cmd->finished =3D true; +} + +void +vg_ctrl_response_nodata(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd, + enum virtio_gpu_ctrl_type type) +{ + struct virtio_gpu_ctrl_hdr resp =3D { + .type =3D type, + }; + + vg_ctrl_response(g, cmd, &resp, sizeof(resp)); +} + +void +vg_get_display_info(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resp_display_info dpy_info =3D { {} }; + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_GET_DISPLAY_INFO, + .size =3D 0, + }; + + assert(vg->wait_ok =3D=3D 0); + + vg_send_msg(vg, &msg, -1); + if (vg_sock_fd_read(vg->sock_fd, &dpy_info, sizeof(dpy_info)) < 0) { + vg_sock_fd_close(vg); + return; + } + + vg_ctrl_response(vg, cmd, &dpy_info.hdr, sizeof(dpy_info)); +} + +static pixman_format_code_t +get_pixman_format(uint32_t virtio_gpu_format) +{ + switch (virtio_gpu_format) { +#ifdef HOST_WORDS_BIGENDIAN + case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: + return PIXMAN_b8g8r8x8; + case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: + return PIXMAN_b8g8r8a8; + case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: + return PIXMAN_x8r8g8b8; + case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: + return PIXMAN_a8r8g8b8; + case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: + return PIXMAN_r8g8b8x8; + case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: + return PIXMAN_r8g8b8a8; + case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: + return PIXMAN_x8b8g8r8; + case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: + return PIXMAN_a8b8g8r8; +#else + case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: + return PIXMAN_x8r8g8b8; + case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: + return PIXMAN_a8r8g8b8; + case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: + return PIXMAN_b8g8r8x8; + case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: + return PIXMAN_b8g8r8a8; + case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: + return PIXMAN_x8b8g8r8; + case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: + return PIXMAN_a8b8g8r8; + case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: + return PIXMAN_r8g8b8x8; + case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: + return PIXMAN_r8g8b8a8; +#endif + default: + return 0; + } +} + +static void +vg_resource_create_2d(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + pixman_format_code_t pformat; + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_resource_create_2d c2d; + + VUGPU_FILL_CMD(c2d); + virtio_gpu_bswap_32(&c2d, sizeof(c2d)); + + if (c2d.resource_id =3D=3D 0) { + g_critical("%s: resource id 0 is not allowed", __func__); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + res =3D virtio_gpu_find_resource(g, c2d.resource_id); + if (res) { + g_critical("%s: resource already exists %d", __func__, c2d.resourc= e_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + res =3D g_new0(struct virtio_gpu_simple_resource, 1); + res->width =3D c2d.width; + res->height =3D c2d.height; + res->format =3D c2d.format; + res->resource_id =3D c2d.resource_id; + + pformat =3D get_pixman_format(c2d.format); + if (!pformat) { + g_critical("%s: host couldn't handle guest format %d", + __func__, c2d.format); + g_free(res); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + drm_buffer_create(&res->drm_buffer, &g->drm_dev, c2d.width, c2d.height= ); + res->image =3D pixman_image_create_bits(pformat, + c2d.width, + c2d.height, + (uint32_t *)res->drm_buffer.mmap, + res->drm_buffer.stride); + if (!res->image) { + g_critical("%s: resource creation failed %d %d %d", + __func__, c2d.resource_id, c2d.width, c2d.height); + g_free(res); + cmd->error =3D VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + return; + } + + QTAILQ_INSERT_HEAD(&g->reslist, res, next); +} + +static void +vg_disable_scanout(VuGpu *g, int scanout_id) +{ + struct virtio_gpu_scanout *scanout =3D &g->scanout[scanout_id]; + struct virtio_gpu_simple_resource *res; + + if (scanout->resource_id =3D=3D 0) { + return; + } + + res =3D virtio_gpu_find_resource(g, scanout->resource_id); + if (res) { + res->scanout_bitmask &=3D ~(1 << scanout_id); + } + + scanout->width =3D 0; + scanout->height =3D 0; + + { + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_SCANOUT, + .size =3D sizeof(VhostUserGpuScanout), + .payload.scanout.scanout_id =3D scanout_id, + }; + vg_send_msg(g, &msg, -1); + } +} + +static void +vg_resource_destroy(VuGpu *g, + struct virtio_gpu_simple_resource *res) +{ + int i; + + if (res->scanout_bitmask) { + for (i =3D 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) { + if (res->scanout_bitmask & (1 << i)) { + vg_disable_scanout(g, i); + } + } + } + + drm_buffer_destroy(&res->drm_buffer); + pixman_image_unref(res->image); + QTAILQ_REMOVE(&g->reslist, res, next); + g_free(res); +} + +static void +vg_resource_unref(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_resource_unref unref; + + VUGPU_FILL_CMD(unref); + virtio_gpu_bswap_32(&unref, sizeof(unref)); + + res =3D virtio_gpu_find_resource(g, unref.resource_id); + if (!res) { + g_critical("%s: illegal resource specified %d", + __func__, unref.resource_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + vg_resource_destroy(g, res); +} + +int +vg_create_mapping_iov(VuGpu *g, + struct virtio_gpu_resource_attach_backing *ab, + struct virtio_gpu_ctrl_command *cmd, + struct iovec **iov) +{ + struct virtio_gpu_mem_entry *ents; + size_t esize, s; + int i; + + if (ab->nr_entries > 16384) { + g_critical("%s: nr_entries is too big (%d > 16384)", + __func__, ab->nr_entries); + return -1; + } + + esize =3D sizeof(*ents) * ab->nr_entries; + ents =3D g_malloc(esize); + s =3D iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, + sizeof(*ab), ents, esize); + if (s !=3D esize) { + g_critical("%s: command data size incorrect %zu vs %zu", + __func__, s, esize); + g_free(ents); + return -1; + } + + *iov =3D g_malloc0(sizeof(struct iovec) * ab->nr_entries); + for (i =3D 0; i < ab->nr_entries; i++) { + uint64_t len =3D ents[i].length; + (*iov)[i].iov_len =3D ents[i].length; + (*iov)[i].iov_base =3D vu_gpa_to_va(&g->dev.parent, &len, ents[i].= addr); + if (!(*iov)[i].iov_base || len !=3D ents[i].length) { + g_critical("%s: resource %d element %d", + __func__, ab->resource_id, i); + g_free(*iov); + g_free(ents); + *iov =3D NULL; + return -1; + } + } + g_free(ents); + return 0; +} + +static void +vg_resource_attach_backing(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_resource_attach_backing ab; + int ret; + + VUGPU_FILL_CMD(ab); + virtio_gpu_bswap_32(&ab, sizeof(ab)); + + res =3D virtio_gpu_find_resource(g, ab.resource_id); + if (!res) { + g_critical("%s: illegal resource specified %d", + __func__, ab.resource_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + ret =3D vg_create_mapping_iov(g, &ab, cmd, &res->iov); + if (ret !=3D 0) { + cmd->error =3D VIRTIO_GPU_RESP_ERR_UNSPEC; + return; + } + + res->iov_cnt =3D ab.nr_entries; +} + +static void +vg_resource_detach_backing(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_resource_detach_backing detach; + + VUGPU_FILL_CMD(detach); + virtio_gpu_bswap_32(&detach, sizeof(detach)); + + res =3D virtio_gpu_find_resource(g, detach.resource_id); + if (!res || !res->iov) { + g_critical("%s: illegal resource specified %d", + __func__, detach.resource_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + g_free(res->iov); + res->iov =3D NULL; + res->iov_cnt =3D 0; +} + +static void +vg_transfer_to_host_2d(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_simple_resource *res; + int h; + uint32_t src_offset, dst_offset, stride; + int bpp; + pixman_format_code_t format; + struct virtio_gpu_transfer_to_host_2d t2d; + + VUGPU_FILL_CMD(t2d); + virtio_gpu_t2d_bswap(&t2d); + + res =3D virtio_gpu_find_resource(g, t2d.resource_id); + if (!res || !res->iov) { + g_critical("%s: illegal resource specified %d", + __func__, t2d.resource_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + if (t2d.r.x > res->width || + t2d.r.y > res->height || + t2d.r.width > res->width || + t2d.r.height > res->height || + t2d.r.x + t2d.r.width > res->width || + t2d.r.y + t2d.r.height > res->height) { + g_critical("%s: transfer bounds outside resource" + " bounds for resource %d: %d %d %d %d vs %d %d", + __func__, t2d.resource_id, t2d.r.x, t2d.r.y, + t2d.r.width, t2d.r.height, res->width, res->height); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + + format =3D pixman_image_get_format(res->image); + bpp =3D (PIXMAN_FORMAT_BPP(format) + 7) / 8; + stride =3D pixman_image_get_stride(res->image); + + if (t2d.offset || t2d.r.x || t2d.r.y || + t2d.r.width !=3D pixman_image_get_width(res->image)) { + void *img_data =3D pixman_image_get_data(res->image); + for (h =3D 0; h < t2d.r.height; h++) { + src_offset =3D t2d.offset + stride * h; + dst_offset =3D (t2d.r.y + h) * stride + (t2d.r.x * bpp); + + iov_to_buf(res->iov, res->iov_cnt, src_offset, + img_data + + dst_offset, t2d.r.width * bpp); + } + } else { + iov_to_buf(res->iov, res->iov_cnt, 0, + pixman_image_get_data(res->image), + pixman_image_get_stride(res->image) + * pixman_image_get_height(res->image)); + } +} + +static void +vg_set_scanout(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_simple_resource *res, *ores; + struct virtio_gpu_scanout *scanout; + struct virtio_gpu_set_scanout ss; + int fd; + + VUGPU_FILL_CMD(ss); + virtio_gpu_bswap_32(&ss, sizeof(ss)); + + if (ss.scanout_id >=3D VIRTIO_GPU_MAX_SCANOUTS) { + g_critical("%s: illegal scanout id specified %d", + __func__, ss.scanout_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; + return; + } + + if (ss.resource_id =3D=3D 0) { + vg_disable_scanout(g, ss.scanout_id); + return; + } + + /* create a surface for this scanout */ + res =3D virtio_gpu_find_resource(g, ss.resource_id); + if (!res) { + g_critical("%s: illegal resource specified %d", + __func__, ss.resource_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + if (ss.r.x > res->width || + ss.r.y > res->height || + ss.r.width > res->width || + ss.r.height > res->height || + ss.r.x + ss.r.width > res->width || + ss.r.y + ss.r.height > res->height) { + g_critical("%s: illegal scanout %d bounds for" + " resource %d, (%d,%d)+%d,%d vs %d %d", + __func__, ss.scanout_id, ss.resource_id, ss.r.x, ss.r.y, + ss.r.width, ss.r.height, res->width, res->height); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + + scanout =3D &g->scanout[ss.scanout_id]; + + ores =3D virtio_gpu_find_resource(g, scanout->resource_id); + if (ores) { + ores->scanout_bitmask &=3D ~(1 << ss.scanout_id); + } + + res->scanout_bitmask |=3D (1 << ss.scanout_id); + scanout->resource_id =3D ss.resource_id; + scanout->x =3D ss.r.x; + scanout->y =3D ss.r.y; + scanout->width =3D ss.r.width; + scanout->height =3D ss.r.height; + + struct drm_buffer *buffer =3D &res->drm_buffer; + + if (drm_buffer_can_get_dmabuf_fd(buffer)) { + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_DMABUF_SCANOUT, + .size =3D sizeof(VhostUserGpuDMABUFScanout), + .payload.dmabuf_scanout =3D (VhostUserGpuDMABUFScanout) { + .scanout_id =3D ss.scanout_id, + .x =3D ss.r.x, + .y =3D ss.r.y, + .width =3D ss.r.width, + .height =3D ss.r.height, + .fd_width =3D buffer->width, + .fd_height =3D buffer->height, + .fd_stride =3D buffer->stride, + .fd_drm_fourcc =3D buffer->format + } + }; + + if (drm_buffer_get_dmabuf_fd(buffer, &fd)) { + vg_send_msg(g, &msg, fd); + close(fd); + } + } else { + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_SCANOUT, + .size =3D sizeof(VhostUserGpuScanout), + .payload.scanout =3D (VhostUserGpuScanout) { + .scanout_id =3D ss.scanout_id, + .width =3D scanout->width, + .height =3D scanout->height + } + }; + vg_send_msg(g, &msg, -1); + } +} + +static void +vg_resource_flush(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_simple_resource *res; + struct virtio_gpu_resource_flush rf; + pixman_region16_t flush_region; + int i; + + VUGPU_FILL_CMD(rf); + virtio_gpu_bswap_32(&rf, sizeof(rf)); + + res =3D virtio_gpu_find_resource(g, rf.resource_id); + if (!res) { + g_critical("%s: illegal resource specified %d\n", + __func__, rf.resource_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + if (rf.r.x > res->width || + rf.r.y > res->height || + rf.r.width > res->width || + rf.r.height > res->height || + rf.r.x + rf.r.width > res->width || + rf.r.y + rf.r.height > res->height) { + g_critical("%s: flush bounds outside resource" + " bounds for resource %d: %d %d %d %d vs %d %d\n", + __func__, rf.resource_id, rf.r.x, rf.r.y, + rf.r.width, rf.r.height, res->width, res->height); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + + pixman_region_init_rect(&flush_region, + rf.r.x, rf.r.y, rf.r.width, rf.r.height); + for (i =3D 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) { + struct virtio_gpu_scanout *scanout; + pixman_region16_t region, finalregion; + pixman_box16_t *extents; + + if (!(res->scanout_bitmask & (1 << i))) { + continue; + } + scanout =3D &g->scanout[i]; + + pixman_region_init(&finalregion); + pixman_region_init_rect(®ion, scanout->x, scanout->y, + scanout->width, scanout->height); + + pixman_region_intersect(&finalregion, &flush_region, ®ion); + + extents =3D pixman_region_extents(&finalregion); + size_t width =3D extents->x2 - extents->x1; + size_t height =3D extents->y2 - extents->y1; + + if (drm_buffer_can_get_dmabuf_fd(&res->drm_buffer)) { + VhostUserGpuMsg vmsg =3D { + .request =3D VHOST_USER_GPU_DMABUF_UPDATE, + .size =3D sizeof(VhostUserGpuUpdate), + .payload.update =3D (VhostUserGpuUpdate) { + .scanout_id =3D i, + .x =3D extents->x1, + .y =3D extents->y1, + .width =3D width, + .height =3D height, + } + }; + vg_send_msg(g, &vmsg, -1); + vg_wait_ok(g); + } else { + size_t bpp =3D + PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) / 8; + size_t size =3D width * height * bpp; + + void *p =3D g_malloc(VHOST_USER_GPU_HDR_SIZE + + sizeof(VhostUserGpuUpdate) + size); + VhostUserGpuMsg *msg =3D p; + msg->request =3D VHOST_USER_GPU_UPDATE; + msg->size =3D sizeof(VhostUserGpuUpdate) + size; + msg->payload.update =3D (VhostUserGpuUpdate) { + .scanout_id =3D i, + .x =3D extents->x1, + .y =3D extents->y1, + .width =3D width, + .height =3D height, + }; + pixman_image_t *i =3D + pixman_image_create_bits(pixman_image_get_format(res->imag= e), + msg->payload.update.width, + msg->payload.update.height, + p + offsetof(VhostUserGpuMsg, + payload.update.data), + width * bpp); + pixman_image_composite(PIXMAN_OP_SRC, + res->image, NULL, i, + extents->x1, extents->y1, + 0, 0, 0, 0, + width, height); + pixman_image_unref(i); + vg_send_msg(g, msg, -1); + g_free(msg); + } + pixman_region_fini(®ion); + pixman_region_fini(&finalregion); + } + pixman_region_fini(&flush_region); +} + +static void +vg_process_cmd(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) +{ + switch (cmd->cmd_hdr.type) { + case VIRTIO_GPU_CMD_GET_DISPLAY_INFO: + vg_get_display_info(vg, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: + vg_resource_create_2d(vg, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_UNREF: + vg_resource_unref(vg, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_FLUSH: + vg_resource_flush(vg, cmd); + break; + case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: + vg_transfer_to_host_2d(vg, cmd); + break; + case VIRTIO_GPU_CMD_SET_SCANOUT: + vg_set_scanout(vg, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: + vg_resource_attach_backing(vg, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: + vg_resource_detach_backing(vg, cmd); + break; + /* case VIRTIO_GPU_CMD_GET_EDID: */ + /* break */ + default: + g_warning("TODO handle ctrl %x\n", cmd->cmd_hdr.type); + cmd->error =3D VIRTIO_GPU_RESP_ERR_UNSPEC; + break; + } + if (!cmd->finished) { + vg_ctrl_response_nodata(vg, cmd, cmd->error ? cmd->error : + VIRTIO_GPU_RESP_OK_NODATA); + } +} + +static void +vg_handle_ctrl(VuDev *dev, int qidx) +{ + VuGpu *vg =3D container_of(dev, VuGpu, dev.parent); + VuVirtq *vq =3D vu_get_queue(dev, qidx); + struct virtio_gpu_ctrl_command *cmd =3D NULL; + size_t len; + + for (;;) { + if (vg->wait_ok !=3D 0) { + return; + } + + cmd =3D vu_queue_pop(dev, vq, sizeof(struct virtio_gpu_ctrl_comman= d)); + if (!cmd) { + break; + } + cmd->vq =3D vq; + cmd->error =3D 0; + cmd->finished =3D false; + + len =3D iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, + 0, &cmd->cmd_hdr, sizeof(cmd->cmd_hdr)); + if (len !=3D sizeof(cmd->cmd_hdr)) { + g_warning("%s: command size incorrect %zu vs %zu\n", + __func__, len, sizeof(cmd->cmd_hdr)); + } + + virtio_gpu_ctrl_hdr_bswap(&cmd->cmd_hdr); + g_debug("%d %s\n", cmd->cmd_hdr.type, + vg_cmd_to_string(cmd->cmd_hdr.type)); + + if (vg->virgl) { + vg_virgl_process_cmd(vg, cmd); + } else { + vg_process_cmd(vg, cmd); + } + + if (!cmd->finished) { + QTAILQ_INSERT_TAIL(&vg->fenceq, cmd, next); + vg->inflight++; + } else { + g_free(cmd); + } + } +} + +static void +update_cursor_data_simple(VuGpu *g, uint32_t resource_id, gpointer data) +{ + struct virtio_gpu_simple_resource *res; + + res =3D virtio_gpu_find_resource(g, resource_id); + g_return_if_fail(res !=3D NULL); + g_return_if_fail(pixman_image_get_width(res->image) =3D=3D 64); + g_return_if_fail(pixman_image_get_height(res->image) =3D=3D 64); + g_return_if_fail( + PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) =3D=3D 32); + + memcpy(data, pixman_image_get_data(res->image), 64 * 64 * sizeof(uint3= 2_t)); +} + +static void +vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor) +{ + bool move =3D cursor->hdr.type !=3D VIRTIO_GPU_CMD_MOVE_CURSOR; + + g_debug("%s move:%d\n", G_STRFUNC, move); + + if (move) { + VhostUserGpuMsg msg =3D { + .request =3D cursor->resource_id ? + VHOST_USER_GPU_CURSOR_POS : VHOST_USER_GPU_CURSOR_POS_HIDE, + .size =3D sizeof(VhostUserGpuCursorPos), + .payload.cursor_pos =3D { + .scanout_id =3D cursor->pos.scanout_id, + .x =3D cursor->pos.x, + .y =3D cursor->pos.y, + } + }; + vg_send_msg(g, &msg, -1); + } else { + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_CURSOR_UPDATE, + .size =3D sizeof(VhostUserGpuCursorUpdate), + .payload.cursor_update =3D { + .pos =3D { + .scanout_id =3D cursor->pos.scanout_id, + .x =3D cursor->pos.x, + .y =3D cursor->pos.y, + }, + .hot_x =3D cursor->hot_x, + .hot_y =3D cursor->hot_y, + } + }; + if (g->virgl) { + vg_virgl_update_cursor_data(g, cursor->resource_id, + msg.payload.cursor_update.data); + } else { + update_cursor_data_simple(g, cursor->resource_id, + msg.payload.cursor_update.data); + } + vg_send_msg(g, &msg, -1); + } +} + +static void +vg_handle_cursor(VuDev *dev, int qidx) +{ + VuGpu *g =3D container_of(dev, VuGpu, dev.parent); + VuVirtq *vq =3D vu_get_queue(dev, qidx); + VuVirtqElement *elem; + size_t len; + struct virtio_gpu_update_cursor cursor; + + for (;;) { + elem =3D vu_queue_pop(dev, vq, sizeof(VuVirtqElement)); + if (!elem) { + break; + } + g_debug("cursor out:%d in:%d\n", elem->out_num, elem->in_num); + + len =3D iov_to_buf(elem->out_sg, elem->out_num, + 0, &cursor, sizeof(cursor)); + if (len !=3D sizeof(cursor)) { + g_warning("%s: cursor size incorrect %zu vs %zu\n", + __func__, len, sizeof(cursor)); + } else { + virtio_gpu_bswap_32(&cursor, sizeof(cursor)); + vg_process_cursor_cmd(g, &cursor); + } + vu_queue_push(dev, vq, elem, 0); + vu_queue_notify(dev, vq); + g_free(elem); + } +} + +static void +vg_panic(VuDev *dev, const char *msg) +{ + g_critical("%s\n", msg); + exit(1); +} + +static void +vg_queue_set_started(VuDev *dev, int qidx, bool started) +{ + VuVirtq *vq =3D vu_get_queue(dev, qidx); + + g_debug("queue started %d:%d\n", qidx, started); + + switch (qidx) { + case 0: + vu_set_queue_handler(dev, vq, started ? vg_handle_ctrl : NULL); + break; + case 1: + vu_set_queue_handler(dev, vq, started ? vg_handle_cursor : NULL); + break; + default: + break; + } +} + +static void +set_gpu_protocol_features(VuGpu *g) +{ + uint64_t u64; + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_GET_PROTOCOL_FEATURES + }; + + assert(g->wait_ok =3D=3D 0); + vg_send_msg(g, &msg, -1); + if (vg_sock_fd_read(g->sock_fd, &u64, sizeof(u64)) < 0) { + vg_sock_fd_close(g); + return; + } + msg =3D (VhostUserGpuMsg) { + .request =3D VHOST_USER_GPU_SET_PROTOCOL_FEATURES, + .size =3D sizeof(uint64_t), + .payload.u64 =3D 0 + }; + vg_send_msg(g, &msg, -1); +} + +static int +vg_process_msg(VuDev *dev, VhostUserMsg *msg, int *do_reply) +{ + VuGpu *g =3D container_of(dev, VuGpu, dev.parent); + + switch (msg->request) { + case VHOST_USER_GPU_SET_SOCKET: { + g_return_val_if_fail(msg->fd_num =3D=3D 1, 1); + g_return_val_if_fail(g->sock_fd =3D=3D -1, 1); + g->sock_fd =3D msg->fds[0]; + set_gpu_protocol_features(g); + return 1; + } + default: + return 0; + } + + return 0; +} + +static uint64_t +vg_get_features(VuDev *dev) +{ + uint64_t features =3D 0; + + if (opt_virgl) { + features |=3D 1 << VIRTIO_GPU_F_VIRGL; + } + + return features; +} + +static void +vg_set_features(VuDev *dev, uint64_t features) +{ + VuGpu *g =3D container_of(dev, VuGpu, dev.parent); + bool virgl =3D features & (1 << VIRTIO_GPU_F_VIRGL); + + if (virgl && !g->virgl_inited) { + if (!vg_virgl_init(g)) { + vg_panic(dev, "Failed to initialize virgl"); + } + g->virgl_inited =3D true; + } + + g->virgl =3D virgl; +} + +static int +vg_get_config(VuDev *dev, uint8_t *config, uint32_t len) +{ + VuGpu *g =3D container_of(dev, VuGpu, dev.parent); + + g_return_val_if_fail(len <=3D sizeof(struct virtio_gpu_config), -1); + + if (opt_virgl) { + g->virtio_config.num_capsets =3D vg_virgl_get_num_capsets(); + } + + memcpy(config, &g->virtio_config, len); + + return 0; +} + +static int +vg_set_config(VuDev *dev, const uint8_t *data, + uint32_t offset, uint32_t size, + uint32_t flags) +{ + VuGpu *g =3D container_of(dev, VuGpu, dev.parent); + struct virtio_gpu_config *config =3D (struct virtio_gpu_config *)data; + + if (config->events_clear) { + g->virtio_config.events_read &=3D ~config->events_clear; + } + + return 0; +} + +static const VuDevIface vuiface =3D { + .set_features =3D vg_set_features, + .get_features =3D vg_get_features, + .queue_set_started =3D vg_queue_set_started, + .process_msg =3D vg_process_msg, + .get_config =3D vg_get_config, + .set_config =3D vg_set_config, +}; + +static void +vg_destroy(VuGpu *g) +{ + struct virtio_gpu_simple_resource *res, *tmp; + + vug_deinit(&g->dev); + + vg_sock_fd_close(g); + + QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) { + vg_resource_destroy(g, res); + } + + drm_device_destroy(&g->drm_dev); +} + +static int unix_sock_new(char *path) +{ + int sock; + struct sockaddr_un un; + size_t len; + + sock =3D socket(AF_UNIX, SOCK_STREAM, 0); + if (sock <=3D 0) { + perror("socket"); + return -1; + } + + un.sun_family =3D AF_UNIX; + snprintf(un.sun_path, sizeof(un.sun_path), "%s", path); + len =3D sizeof(un.sun_family) + strlen(un.sun_path); + + unlink(path); + if (bind(sock, (struct sockaddr *)&un, len) < 0) { + perror("bind"); + goto fail; + } + + if (listen(sock, 1) < 0) { + perror("listen"); + goto fail; + } + + return sock; + +fail: + close(sock); + + return -1; +} + +static GOptionEntry entries[] =3D { + { "print-capabilities", 'c', 0, G_OPTION_ARG_NONE, &opt_print_caps, + "Print capabilities", NULL }, + { "fd", 'f', 0, G_OPTION_ARG_INT, &opt_fdnum, + "Use inherited fd socket", "FDNUM" }, + { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &opt_socket_path, + "Use UNIX socket path", "PATH" }, + { "render-node", 'r', 0, G_OPTION_ARG_FILENAME, &opt_render_node, + "Specify DRM render node", "PATH" }, + { "virgl", 'v', 0, G_OPTION_ARG_NONE, &opt_virgl, + "Turn virgl rendering on", NULL }, + { NULL, } +}; + +int +main(int argc, char *argv[]) +{ + GOptionContext *context; + GError *error =3D NULL; + GMainLoop *loop =3D NULL; + int fd; + VuGpu g =3D { .sock_fd =3D -1, .drm_rnode_fd =3D -1 }; + + QTAILQ_INIT(&g.reslist); + QTAILQ_INIT(&g.fenceq); + + context =3D g_option_context_new("QEMU vhost-user-gpu"); + g_option_context_add_main_entries(context, entries, NULL); + if (!g_option_context_parse(context, &argc, &argv, &error)) { + g_printerr("Option parsing failed: %s\n", error->message); + exit(EXIT_FAILURE); + } + g_option_context_free(context); + + if (opt_print_caps) { + g_print("{\n"); + g_print(" \"type\": \"gpu\",\n"); + g_print(" \"features\": [\n"); + g_print(" \"render-node\",\n"); + g_print(" \"virgl\"\n"); + g_print(" ]\n"); + g_print("}\n"); + exit(EXIT_SUCCESS); + } + + g.drm_rnode_fd =3D qemu_drm_rendernode_open(opt_render_node); + if (opt_render_node && g.drm_rnode_fd =3D=3D -1) { + g_printerr("Failed to open DRM rendernode.\n"); + exit(EXIT_FAILURE); + } + + if (g.drm_rnode_fd >=3D 0) { + if (!drm_device_init(&g.drm_dev, g.drm_rnode_fd)) { + g_warning("Failed to init DRM device, using fallback path"); + } + } + + if ((!!opt_socket_path + (opt_fdnum !=3D -1)) !=3D 1) { + g_printerr("Please specify either --fd or --socket-path\n"); + exit(EXIT_FAILURE); + } + + if (opt_socket_path) { + int lsock =3D unix_sock_new(opt_socket_path); + fd =3D accept(lsock, NULL, NULL); + close(lsock); + } else { + fd =3D opt_fdnum; + } + if (fd =3D=3D -1) { + g_printerr("Invalid socket"); + exit(EXIT_FAILURE); + } + + vug_init(&g.dev, fd, vg_panic, &vuiface); + + loop =3D g_main_loop_new(NULL, FALSE); + g_main_loop_run(loop); + g_main_loop_unref(loop); + + vg_destroy(&g); + if (g.drm_rnode_fd >=3D 0) { + close(g.drm_rnode_fd); + } + + return 0; +} diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c new file mode 100644 index 0000000000..43413e29df --- /dev/null +++ b/contrib/vhost-user-gpu/virgl.c @@ -0,0 +1,579 @@ +/* + * Virtio vhost-user GPU Device + * + * Copyright Red Hat, Inc. 2013-2018 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * Marc-Andr=C3=A9 Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#include +#include "virgl.h" + +void +vg_virgl_update_cursor_data(VuGpu *g, uint32_t resource_id, + gpointer data) +{ + uint32_t width, height; + uint32_t *cursor; + + cursor =3D virgl_renderer_get_cursor_data(resource_id, &width, &height= ); + g_return_if_fail(cursor !=3D NULL); + g_return_if_fail(width =3D=3D 64); + g_return_if_fail(height =3D=3D 64); + + memcpy(data, cursor, 64 * 64 * sizeof(uint32_t)); + free(cursor); +} + +static void +virgl_cmd_context_create(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_ctx_create cc; + + VUGPU_FILL_CMD(cc); + + virgl_renderer_context_create(cc.hdr.ctx_id, cc.nlen, + cc.debug_name); +} + +static void +virgl_cmd_context_destroy(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_ctx_destroy cd; + + VUGPU_FILL_CMD(cd); + + virgl_renderer_context_destroy(cd.hdr.ctx_id); +} + +static void +virgl_cmd_create_resource_2d(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_create_2d c2d; + struct virgl_renderer_resource_create_args args; + + VUGPU_FILL_CMD(c2d); + + args.handle =3D c2d.resource_id; + args.target =3D 2; + args.format =3D c2d.format; + args.bind =3D (1 << 1); + args.width =3D c2d.width; + args.height =3D c2d.height; + args.depth =3D 1; + args.array_size =3D 1; + args.last_level =3D 0; + args.nr_samples =3D 0; + args.flags =3D VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP; + virgl_renderer_resource_create(&args, NULL, 0); +} + +static void +virgl_cmd_create_resource_3d(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_create_3d c3d; + struct virgl_renderer_resource_create_args args; + + VUGPU_FILL_CMD(c3d); + + args.handle =3D c3d.resource_id; + args.target =3D c3d.target; + args.format =3D c3d.format; + args.bind =3D c3d.bind; + args.width =3D c3d.width; + args.height =3D c3d.height; + args.depth =3D c3d.depth; + args.array_size =3D c3d.array_size; + args.last_level =3D c3d.last_level; + args.nr_samples =3D c3d.nr_samples; + args.flags =3D c3d.flags; + virgl_renderer_resource_create(&args, NULL, 0); +} + +static void +virgl_cmd_resource_unref(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_unref unref; + + VUGPU_FILL_CMD(unref); + + virgl_renderer_resource_unref(unref.resource_id); +} + +/* Not yet(?) defined in standard-headers, remove when possible */ +#ifndef VIRTIO_GPU_CAPSET_VIRGL2 +#define VIRTIO_GPU_CAPSET_VIRGL2 2 +#endif + +static void +virgl_cmd_get_capset_info(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_get_capset_info info; + struct virtio_gpu_resp_capset_info resp; + + VUGPU_FILL_CMD(info); + + if (info.capset_index =3D=3D 0) { + resp.capset_id =3D VIRTIO_GPU_CAPSET_VIRGL; + virgl_renderer_get_cap_set(resp.capset_id, + &resp.capset_max_version, + &resp.capset_max_size); + } else if (info.capset_index =3D=3D 1) { + resp.capset_id =3D VIRTIO_GPU_CAPSET_VIRGL2; + virgl_renderer_get_cap_set(resp.capset_id, + &resp.capset_max_version, + &resp.capset_max_size); + } else { + resp.capset_max_version =3D 0; + resp.capset_max_size =3D 0; + } + resp.hdr.type =3D VIRTIO_GPU_RESP_OK_CAPSET_INFO; + vg_ctrl_response(g, cmd, &resp.hdr, sizeof(resp)); +} + +uint32_t +vg_virgl_get_num_capsets(void) +{ + uint32_t capset2_max_ver, capset2_max_size; + virgl_renderer_get_cap_set(VIRTIO_GPU_CAPSET_VIRGL2, + &capset2_max_ver, + &capset2_max_size); + + return capset2_max_ver ? 2 : 1; +} + +static void +virgl_cmd_get_capset(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_get_capset gc; + struct virtio_gpu_resp_capset *resp; + uint32_t max_ver, max_size; + + VUGPU_FILL_CMD(gc); + + virgl_renderer_get_cap_set(gc.capset_id, &max_ver, + &max_size); + resp =3D g_malloc0(sizeof(*resp) + max_size); + + resp->hdr.type =3D VIRTIO_GPU_RESP_OK_CAPSET; + virgl_renderer_fill_caps(gc.capset_id, + gc.capset_version, + (void *)resp->capset_data); + vg_ctrl_response(g, cmd, &resp->hdr, sizeof(*resp) + max_size); + g_free(resp); +} + +static void +virgl_cmd_submit_3d(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_cmd_submit cs; + void *buf; + size_t s; + + VUGPU_FILL_CMD(cs); + + buf =3D g_malloc(cs.size); + s =3D iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, + sizeof(cs), buf, cs.size); + if (s !=3D cs.size) { + g_critical("%s: size mismatch (%zd/%d)", __func__, s, cs.size); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + goto out; + } + + virgl_renderer_submit_cmd(buf, cs.hdr.ctx_id, cs.size / 4); + +out: + g_free(buf); +} + +static void +virgl_cmd_transfer_to_host_2d(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_transfer_to_host_2d t2d; + struct virtio_gpu_box box; + + VUGPU_FILL_CMD(t2d); + + box.x =3D t2d.r.x; + box.y =3D t2d.r.y; + box.z =3D 0; + box.w =3D t2d.r.width; + box.h =3D t2d.r.height; + box.d =3D 1; + + virgl_renderer_transfer_write_iov(t2d.resource_id, + 0, + 0, + 0, + 0, + (struct virgl_box *)&box, + t2d.offset, NULL, 0); +} + +static void +virgl_cmd_transfer_to_host_3d(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_transfer_host_3d t3d; + + VUGPU_FILL_CMD(t3d); + + virgl_renderer_transfer_write_iov(t3d.resource_id, + t3d.hdr.ctx_id, + t3d.level, + t3d.stride, + t3d.layer_stride, + (struct virgl_box *)&t3d.box, + t3d.offset, NULL, 0); +} + +static void +virgl_cmd_transfer_from_host_3d(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_transfer_host_3d tf3d; + + VUGPU_FILL_CMD(tf3d); + + virgl_renderer_transfer_read_iov(tf3d.resource_id, + tf3d.hdr.ctx_id, + tf3d.level, + tf3d.stride, + tf3d.layer_stride, + (struct virgl_box *)&tf3d.box, + tf3d.offset, NULL, 0); +} + +static void +virgl_resource_attach_backing(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_attach_backing att_rb; + struct iovec *res_iovs; + int ret; + + VUGPU_FILL_CMD(att_rb); + + ret =3D vg_create_mapping_iov(g, &att_rb, cmd, &res_iovs); + if (ret !=3D 0) { + cmd->error =3D VIRTIO_GPU_RESP_ERR_UNSPEC; + return; + } + + virgl_renderer_resource_attach_iov(att_rb.resource_id, + res_iovs, att_rb.nr_entries); +} + +static void +virgl_resource_detach_backing(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_detach_backing detach_rb; + struct iovec *res_iovs =3D NULL; + int num_iovs =3D 0; + + VUGPU_FILL_CMD(detach_rb); + + virgl_renderer_resource_detach_iov(detach_rb.resource_id, + &res_iovs, + &num_iovs); + if (res_iovs =3D=3D NULL || num_iovs =3D=3D 0) { + return; + } + g_free(res_iovs); +} + +static void +virgl_cmd_set_scanout(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_set_scanout ss; + struct virgl_renderer_resource_info info; + int ret; + + VUGPU_FILL_CMD(ss); + + if (ss.scanout_id >=3D VIRTIO_GPU_MAX_SCANOUTS) { + g_critical("%s: illegal scanout id specified %d", + __func__, ss.scanout_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; + return; + } + + memset(&info, 0, sizeof(info)); + + if (ss.resource_id && ss.r.width && ss.r.height) { + ret =3D virgl_renderer_resource_get_info(ss.resource_id, &info); + if (ret =3D=3D -1) { + g_critical("%s: illegal resource specified %d\n", + __func__, ss.resource_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + int fd =3D -1; + if (virgl_renderer_get_fd_for_texture(info.tex_id, &fd) < 0) { + g_critical("%s: failed to get fd for texture\n", __func__); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + assert(fd >=3D 0); + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_DMABUF_SCANOUT, + .size =3D sizeof(VhostUserGpuDMABUFScanout), + .payload.dmabuf_scanout.scanout_id =3D ss.scanout_id, + .payload.dmabuf_scanout.x =3D ss.r.x, + .payload.dmabuf_scanout.y =3D ss.r.y, + .payload.dmabuf_scanout.width =3D ss.r.width, + .payload.dmabuf_scanout.height =3D ss.r.height, + .payload.dmabuf_scanout.fd_width =3D info.width, + .payload.dmabuf_scanout.fd_height =3D info.height, + .payload.dmabuf_scanout.fd_stride =3D info.stride, + .payload.dmabuf_scanout.fd_flags =3D info.flags, + .payload.dmabuf_scanout.fd_drm_fourcc =3D info.drm_fourcc + }; + vg_send_msg(g, &msg, fd); + close(fd); + } else { + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_DMABUF_SCANOUT, + .size =3D sizeof(VhostUserGpuDMABUFScanout), + .payload.dmabuf_scanout.scanout_id =3D ss.scanout_id, + }; + g_debug("disable scanout"); + vg_send_msg(g, &msg, -1); + } + g->scanout[ss.scanout_id].resource_id =3D ss.resource_id; +} + +static void +virgl_cmd_resource_flush(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_flush rf; + int i; + + VUGPU_FILL_CMD(rf); + + if (!rf.resource_id) { + g_debug("bad resource id for flush..?"); + return; + } + for (i =3D 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) { + if (g->scanout[i].resource_id !=3D rf.resource_id) { + continue; + } + VhostUserGpuMsg msg =3D { + .request =3D VHOST_USER_GPU_DMABUF_UPDATE, + .size =3D sizeof(VhostUserGpuUpdate), + .payload.update.scanout_id =3D i, + .payload.update.x =3D rf.r.x, + .payload.update.y =3D rf.r.y, + .payload.update.width =3D rf.r.width, + .payload.update.height =3D rf.r.height + }; + vg_send_msg(g, &msg, -1); + vg_wait_ok(g); + } +} + +static void +virgl_cmd_ctx_attach_resource(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_ctx_resource att_res; + + VUGPU_FILL_CMD(att_res); + + virgl_renderer_ctx_attach_resource(att_res.hdr.ctx_id, att_res.resourc= e_id); +} + +static void +virgl_cmd_ctx_detach_resource(VuGpu *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_ctx_resource det_res; + + VUGPU_FILL_CMD(det_res); + + virgl_renderer_ctx_detach_resource(det_res.hdr.ctx_id, det_res.resourc= e_id); +} + +void vg_virgl_process_cmd(VuGpu *g, struct virtio_gpu_ctrl_command *cmd) +{ + virgl_renderer_force_ctx_0(); + switch (cmd->cmd_hdr.type) { + case VIRTIO_GPU_CMD_CTX_CREATE: + virgl_cmd_context_create(g, cmd); + break; + case VIRTIO_GPU_CMD_CTX_DESTROY: + virgl_cmd_context_destroy(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: + virgl_cmd_create_resource_2d(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_CREATE_3D: + virgl_cmd_create_resource_3d(g, cmd); + break; + case VIRTIO_GPU_CMD_SUBMIT_3D: + virgl_cmd_submit_3d(g, cmd); + break; + case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: + virgl_cmd_transfer_to_host_2d(g, cmd); + break; + case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D: + virgl_cmd_transfer_to_host_3d(g, cmd); + break; + case VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D: + virgl_cmd_transfer_from_host_3d(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: + virgl_resource_attach_backing(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: + virgl_resource_detach_backing(g, cmd); + break; + case VIRTIO_GPU_CMD_SET_SCANOUT: + virgl_cmd_set_scanout(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_FLUSH: + virgl_cmd_resource_flush(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_UNREF: + virgl_cmd_resource_unref(g, cmd); + break; + case VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE: + /* TODO add security */ + virgl_cmd_ctx_attach_resource(g, cmd); + break; + case VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE: + /* TODO add security */ + virgl_cmd_ctx_detach_resource(g, cmd); + break; + case VIRTIO_GPU_CMD_GET_CAPSET_INFO: + virgl_cmd_get_capset_info(g, cmd); + break; + case VIRTIO_GPU_CMD_GET_CAPSET: + virgl_cmd_get_capset(g, cmd); + break; + case VIRTIO_GPU_CMD_GET_DISPLAY_INFO: + vg_get_display_info(g, cmd); + break; + default: + g_debug("TODO handle ctrl %x\n", cmd->cmd_hdr.type); + cmd->error =3D VIRTIO_GPU_RESP_ERR_UNSPEC; + break; + } + + if (cmd->finished) { + return; + } + + if (cmd->error) { + g_warning("%s: ctrl 0x%x, error 0x%x\n", __func__, + cmd->cmd_hdr.type, cmd->error); + vg_ctrl_response_nodata(g, cmd, cmd->error); + return; + } + + if (!(cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE)) { + vg_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA); + return; + } + + g_debug("Creating fence id:%" PRId64 " type:%d", + cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type); + virgl_renderer_create_fence(cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type); +} + +static void +virgl_write_fence(void *opaque, uint32_t fence) +{ + VuGpu *g =3D opaque; + struct virtio_gpu_ctrl_command *cmd, *tmp; + + QTAILQ_FOREACH_SAFE(cmd, &g->fenceq, next, tmp) { + /* + * the guest can end up emitting fences out of order + * so we should check all fenced cmds not just the first one. + */ + if (cmd->cmd_hdr.fence_id > fence) { + continue; + } + g_debug("FENCE %" PRIu64, cmd->cmd_hdr.fence_id); + vg_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA); + QTAILQ_REMOVE(&g->fenceq, cmd, next); + g_free(cmd); + g->inflight--; + } +} + +#if defined(VIRGL_RENDERER_CALLBACKS_VERSION) && \ + VIRGL_RENDERER_CALLBACKS_VERSION >=3D 2 +static int +virgl_get_drm_fd(void *opaque) +{ + VuGpu *g =3D opaque; + + return g->drm_rnode_fd; +} +#endif + +static struct virgl_renderer_callbacks virgl_cbs =3D { +#if defined(VIRGL_RENDERER_CALLBACKS_VERSION) && \ + VIRGL_RENDERER_CALLBACKS_VERSION >=3D 2 + .get_drm_fd =3D virgl_get_drm_fd, + .version =3D 2, +#else + .version =3D 1, +#endif + .write_fence =3D virgl_write_fence, +}; + +static void +vg_virgl_poll(VuDev *dev, int condition, void *data) +{ + virgl_renderer_poll(); +} + +bool +vg_virgl_init(VuGpu *g) +{ + int ret; + + if (g->drm_rnode_fd && virgl_cbs.version =3D=3D 1) { + g_warning("virgl will use the default rendernode"); + } + + ret =3D virgl_renderer_init(g, + VIRGL_RENDERER_USE_EGL | + VIRGL_RENDERER_THREAD_SYNC, + &virgl_cbs); + if (ret !=3D 0) { + return false; + } + + ret =3D virgl_renderer_get_poll_fd(); + if (ret !=3D -1) { + g->renderer_source =3D + vug_source_new(&g->dev, ret, G_IO_IN, vg_virgl_poll, g); + } + + return true; +} diff --git a/MAINTAINERS b/MAINTAINERS index 337eca10a3..476fe4c49c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1664,6 +1664,8 @@ M: Marc-Andr=C3=A9 Lureau M: Gerd Hoffmann S: Maintained F: docs/interop/vhost-user-gpu.rst +F: contrib/vhost-user-gpu +F: hw/display/vhost-user-* =20 Cirrus VGA M: Gerd Hoffmann diff --git a/Makefile b/Makefile index bf46e603a5..fea1028b96 100644 --- a/Makefile +++ b/Makefile @@ -308,8 +308,20 @@ $(call set-vpath, $(SRC_PATH)) =20 LIBS+=3D-lz $(LIBS_TOOLS) =20 +vhost-user-json-y =3D +HELPERS-y =3D + HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) =3D qemu-bridge-hel= per$(EXESUF) =20 +ifdef CONFIG_LINUX +ifdef CONFIG_VIRGL +ifdef CONFIG_LIBDRM +HELPERS-y +=3D vhost-user-gpu$(EXESUF) +vhost-user-json-y +=3D contrib/vhost-user-gpu/50-qemu-gpu.json +endif +endif +endif + ifdef BUILD_DOCS DOCS=3Dqemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 DOCS+=3Ddocs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/= interop/qemu-qmp-ref.7 @@ -403,6 +415,7 @@ dummy :=3D $(call unnest-vars,, \ vhost-user-scsi-obj-y \ vhost-user-blk-obj-y \ vhost-user-input-obj-y \ + vhost-user-gpu-obj-y \ qga-vss-dll-obj-y \ block-obj-y \ block-obj-m \ @@ -420,7 +433,7 @@ dummy :=3D $(call unnest-vars,, \ =20 include $(SRC_PATH)/tests/Makefile.include =20 -all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) $(HELPERS-y) recurse-= all modules +all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) $(HELPERS-y) recurse-= all modules $(vhost-user-json-y) =20 qemu-version.h: FORCE $(call quiet-command, \ @@ -613,6 +626,8 @@ rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADD= S) =20 vhost-user-input$(EXESUF): $(vhost-user-input-obj-y) libvhost-user.a libqe= muutil.a $(call LINK, $^) +vhost-user-gpu$(EXESUF): $(vhost-user-gpu-obj-y) $(libvhost-user-obj-y) li= bqemuutil.a libqemustub.a + $(call LINK, $^) =20 module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak $(call quiet-command,$(PYTHON) $< $@ \ @@ -805,6 +820,12 @@ endif ifneq ($(HELPERS-y),) $(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir)) endif +ifneq ($(vhost-user-json-y),) + $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/vhost-user/" + for x in $(vhost-user-json-y); do \ + $(INSTALL_DATA) $$x "$(DESTDIR)$(qemu_datadir)/vhost-user/"; \ + done +endif ifdef CONFIG_TRACE_SYSTEMTAP $(INSTALL_PROG) "scripts/qemu-trace-stap" $(DESTDIR)$(bindir) endif diff --git a/Makefile.objs b/Makefile.objs index 27502b7df1..89ed0b7ad4 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -123,6 +123,7 @@ vhost-user-scsi-obj-y =3D contrib/vhost-user-scsi/ vhost-user-blk-obj-y =3D contrib/vhost-user-blk/ rdmacm-mux-obj-y =3D contrib/rdmacm-mux/ vhost-user-input-obj-y =3D contrib/vhost-user-input/ +vhost-user-gpu-obj-y =3D contrib/vhost-user-gpu/ =20 ###################################################################### trace-events-subdirs =3D diff --git a/configure b/configure index 1c563a7027..d40b468f5e 100755 --- a/configure +++ b/configure @@ -4095,6 +4095,20 @@ libs_softmmu=3D"$libs_softmmu $fdt_libs" ########################################## # opengl probe (for sdl2, gtk, milkymist-tmu2) =20 +libdrm=3D"no" +if $pkg_config libdrm; then + libdrm_cflags=3D"$($pkg_config --cflags libdrm)" + libdrm_libs=3D"$($pkg_config --libs libdrm)" + libdrm=3D"yes" +fi + +libdrm_intel=3D"no" +if $pkg_config libdrm_intel; then + libdrm_intel_cflags=3D"$($pkg_config --cflags libdrm_intel)" + libdrm_intel_libs=3D"$($pkg_config --libs libdrm_intel)" + libdrm_intel=3D"yes" +fi + if test "$opengl" !=3D "no" ; then opengl_pkgs=3D"epoxy gbm" if $pkg_config $opengl_pkgs; then @@ -6939,6 +6953,18 @@ if test "$opengl" =3D "yes" ; then fi fi =20 +if test "$libdrm" =3D "yes" ; then + echo "CONFIG_LIBDRM=3Dy" >> $config_host_mak + echo "LIBDRM_LIBS=3D$libdrm_libs" >> $config_host_mak + echo "LIBDRM_CFLAGS=3D$libdrm_cflags" >> $config_host_mak +fi + +if test "$libdrm_intel" =3D "yes" ; then + echo "CONFIG_LIBDRM_INTEL=3Dy" >> $config_host_mak + echo "LIBDRM_INTEL_LIBS=3D$libdrm_intel_libs" >> $config_host_mak + echo "LIBDRM_INTEL_CFLAGS=3D$libdrm_intel_cflags" >> $config_host_mak +fi + if test "$malloc_trim" =3D "yes" ; then echo "CONFIG_MALLOC_TRIM=3Dy" >> $config_host_mak fi @@ -7832,6 +7858,9 @@ echo "QEMU_CFLAGS+=3D$cflags" >> $config_target_mak =20 done # for target in $targets =20 +echo "PIXMAN_CFLAGS=3D$pixman_cflags" >> $config_host_mak +echo "PIXMAN_LIBS=3D$pixman_libs" >> $config_host_mak + if test -n "$enabled_cross_compilers"; then echo echo "NOTE: cross-compilers enabled: $enabled_cross_compilers" diff --git a/contrib/vhost-user-gpu/50-qemu-gpu.json.in b/contrib/vhost-use= r-gpu/50-qemu-gpu.json.in new file mode 100644 index 0000000000..658b545864 --- /dev/null +++ b/contrib/vhost-user-gpu/50-qemu-gpu.json.in @@ -0,0 +1,5 @@ +{ + "description": "QEMU vhost-user-gpu", + "type": "gpu", + "binary": "@libexecdir@/vhost-user-gpu", +} diff --git a/contrib/vhost-user-gpu/Makefile.objs b/contrib/vhost-user-gpu/= Makefile.objs new file mode 100644 index 0000000000..454919d32d --- /dev/null +++ b/contrib/vhost-user-gpu/Makefile.objs @@ -0,0 +1,10 @@ +vhost-user-gpu-obj-y =3D main.o virgl.o drm.o + +main.o-cflags :=3D $(PIXMAN_CFLAGS) $(LIBDRM_CFLAGS) $(LIBDRM_INTEL_CFLAGS) +main.o-libs :=3D $(PIXMAN_LIBS) + +virgl.o-cflags :=3D $(VIRGL_CFLAGS) $(LIBDRM_CFLAGS) $(LIBDRM_INTEL_CFLAGS) +virgl.o-libs :=3D $(VIRGL_LIBS) + +drm.o-cflags :=3D $(LIBDRM_CFLAGS) $(LIBDRM_INTEL_CFLAGS) +drm.o-libs :=3D $(LIBDRM_LIBS) $(LIBDRM_INTEL_LIBS) diff --git a/rules.mak b/rules.mak index df45bcffb4..967295dd2b 100644 --- a/rules.mak +++ b/rules.mak @@ -144,7 +144,7 @@ cc-option =3D $(if $(shell $(CC) $1 $2 -S -o /dev/null = -xc /dev/null \ cc-c-option =3D $(if $(shell $(CC) $1 $2 -c -o /dev/null -xc /dev/null \ >/dev/null 2>&1 && echo OK), $2, $3) =20 -VPATH_SUFFIXES =3D %.c %.h %.S %.cc %.cpp %.m %.mak %.texi %.sh %.rc Kconf= ig% +VPATH_SUFFIXES =3D %.c %.h %.S %.cc %.cpp %.m %.mak %.texi %.sh %.rc Kconf= ig% %.json.in set-vpath =3D $(if $1,$(foreach PATTERN,$(VPATH_SUFFIXES),$(eval vpath $(P= ATTERN) $1))) =20 # install-prog list, dir @@ -392,3 +392,10 @@ TEXI2MAN =3D $(call quiet-command, \ $(call TEXI2MAN) %.8: $(call TEXI2MAN) + +GEN_SUBST =3D $(call quiet-command, \ + sed -e "s!@libexecdir@!$(libexecdir)!g" < $< > $@, \ + "GEN","$@") + +%.json: %.json.in + $(call GEN_SUBST) --=20 2.21.0.313.ge35b8cb8e2 From nobody Sun May 5 10:05:35 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1556026112; cv=none; d=zoho.com; s=zohoarc; b=EnvHaVEXWtsZUGgav6wYwN3dovuy/poVdoxsCbIFiMRlzMIsvE9WO/1p5lA+sDjccYlCcUtgSccsyfx8DlSdVgHPJrJD9gziUKTj/zbim01AGZ08IT1hJSCzwX/GUAGQ23fp/OGt/5zoHExhuvWuQ8W+mVlK/GB+wDMSVZhbaVI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556026112; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=hF2D6IaPphwVmIPP63RzAG4vuBjVoT12pTfIfFcdBCM=; b=AKs+toiFxC4VokxbzolKrbiNQKOvRBvQgw3Fbs98d93p3qlRasnIvsE3kHdIU+AjC/7JloyCidV2trc5u4vYqiifwn0L9TSHA5H6CPpo+OgiqSxFS9BUJB3wD9VDjoZQZ88QR0OuOSDP7IC0MhLXmZwAz6/E+MFM9fm4T3NRwNY= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556026112634829.8131358329598; Tue, 23 Apr 2019 06:28:32 -0700 (PDT) Received: from localhost ([127.0.0.1]:53898 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvTD-0004ML-8H for importer@patchew.org; Tue, 23 Apr 2019 09:28:23 -0400 Received: from eggs.gnu.org ([209.51.188.92]:60761) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvMq-0007UD-36 for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:52 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hIvMk-0005yV-Cn for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:48 -0400 Received: from mx1.redhat.com ([209.132.183.28]:46994) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hIvMi-0005vf-D1 for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:42 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 41D7730031B0 for ; Tue, 23 Apr 2019 13:21:38 +0000 (UTC) Received: from localhost (ovpn-112-54.ams2.redhat.com [10.36.112.54]) by smtp.corp.redhat.com (Postfix) with ESMTP id 5649E5D961; Tue, 23 Apr 2019 13:21:32 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Tue, 23 Apr 2019 15:20:02 +0200 Message-Id: <20190423132004.13725-10-marcandre.lureau@redhat.com> In-Reply-To: <20190423132004.13725-1-marcandre.lureau@redhat.com> References: <20190423132004.13725-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.40]); Tue, 23 Apr 2019 13:21:38 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v6 09/11] virtio-gpu: split virtio-gpu, introduce virtio-gpu-base X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add a base class that is common to virtio-gpu and vhost-user-gpu devices. The VirtIOGPUBase base class provides common functionalities necessary for both virtio-gpu and vhost-user-gpu: - common configuration (max-outputs, initial resolution, flags) - virtio device initialization, including queue setup - device pre-conditions checks (iommu) - migration blocker - virtio device callbacks - hooking up to qemu display subsystem - a few common helper functions to reset the device, retrieve display informations - a class callback to unblock the rendering (for GL updates) What is left to the virtio-gpu subdevice to take care of, in short, are all the virtio queues handling, command processing and migration. Signed-off-by: Marc-Andr=C3=A9 Lureau --- include/hw/virtio/virtio-gpu.h | 73 +++++-- hw/display/virtio-gpu-3d.c | 49 +++-- hw/display/virtio-gpu-base.c | 268 ++++++++++++++++++++++++ hw/display/virtio-gpu-pci.c | 2 +- hw/display/virtio-gpu.c | 372 +++++++++------------------------ hw/display/virtio-vga.c | 11 +- hw/display/Makefile.objs | 2 +- 7 files changed, 458 insertions(+), 319 deletions(-) create mode 100644 hw/display/virtio-gpu-base.c diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 60425c5d58..d0d8d7b246 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -22,6 +22,14 @@ =20 #include "standard-headers/linux/virtio_gpu.h" =20 +#define TYPE_VIRTIO_GPU_BASE "virtio-gpu-base" +#define VIRTIO_GPU_BASE(obj) = \ + OBJECT_CHECK(VirtIOGPUBase, (obj), TYPE_VIRTIO_GPU_BASE) +#define VIRTIO_GPU_BASE_GET_CLASS(obj) = \ + OBJECT_GET_CLASS(VirtIOGPUBaseClass, obj, TYPE_VIRTIO_GPU_BASE) +#define VIRTIO_GPU_BASE_CLASS(klass) = \ + OBJECT_CLASS_CHECK(VirtIOGPUBaseClass, klass, TYPE_VIRTIO_GPU_BASE) + #define TYPE_VIRTIO_GPU "virtio-gpu-device" #define VIRTIO_GPU(obj) \ OBJECT_CHECK(VirtIOGPU, (obj), TYPE_VIRTIO_GPU) @@ -58,7 +66,7 @@ struct virtio_gpu_requested_state { int x, y; }; =20 -enum virtio_gpu_conf_flags { +enum virtio_gpu_base_conf_flags { VIRTIO_GPU_FLAG_VIRGL_ENABLED =3D 1, VIRTIO_GPU_FLAG_STATS_ENABLED, VIRTIO_GPU_FLAG_EDID_ENABLED, @@ -71,8 +79,7 @@ enum virtio_gpu_conf_flags { #define virtio_gpu_edid_enabled(_cfg) \ (_cfg.flags & (1 << VIRTIO_GPU_FLAG_EDID_ENABLED)) =20 -struct virtio_gpu_conf { - uint64_t max_hostmem; +struct virtio_gpu_base_conf { uint32_t max_outputs; uint32_t flags; uint32_t xres; @@ -88,31 +95,55 @@ struct virtio_gpu_ctrl_command { QTAILQ_ENTRY(virtio_gpu_ctrl_command) next; }; =20 -typedef struct VirtIOGPU { +typedef struct VirtIOGPUBase { VirtIODevice parent_obj; =20 - QEMUBH *ctrl_bh; - QEMUBH *cursor_bh; + Error *migration_blocker; + + struct virtio_gpu_base_conf conf; + struct virtio_gpu_config virtio_config; + + bool use_virgl_renderer; + int renderer_blocked; + int enable; + + struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS]; + + int enabled_output_bitmask; + struct virtio_gpu_requested_state req_state[VIRTIO_GPU_MAX_SCANOUTS]; +} VirtIOGPUBase; + +typedef struct VirtIOGPUBaseClass { + VirtioDeviceClass parent; + + void (*gl_unblock)(VirtIOGPUBase *g); +} VirtIOGPUBaseClass; + +#define VIRTIO_GPU_BASE_PROPERTIES(_state, _conf) \ + DEFINE_PROP_UINT32("max_outputs", _state, _conf.max_outputs, 1), \ + DEFINE_PROP_BIT("edid", _state, _conf.flags, \ + VIRTIO_GPU_FLAG_EDID_ENABLED, false), \ + DEFINE_PROP_UINT32("xres", _state, _conf.xres, 1024), \ + DEFINE_PROP_UINT32("yres", _state, _conf.yres, 768) + +typedef struct VirtIOGPU { + VirtIOGPUBase parent_obj; + + uint64_t conf_max_hostmem; + VirtQueue *ctrl_vq; VirtQueue *cursor_vq; =20 - int enable; + QEMUBH *ctrl_bh; + QEMUBH *cursor_bh; =20 QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist; QTAILQ_HEAD(, virtio_gpu_ctrl_command) cmdq; QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq; =20 - struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS]; - struct virtio_gpu_requested_state req_state[VIRTIO_GPU_MAX_SCANOUTS]; - - struct virtio_gpu_conf conf; uint64_t hostmem; - int enabled_output_bitmask; - struct virtio_gpu_config virtio_config; =20 - bool use_virgl_renderer; bool renderer_inited; - int renderer_blocked; bool renderer_reset; QEMUTimer *fence_poll; QEMUTimer *print_stats; @@ -124,8 +155,6 @@ typedef struct VirtIOGPU { uint32_t req_3d; uint32_t bytes_3d; } stats; - - Error *migration_blocker; } VirtIOGPU; =20 extern const GraphicHwOps virtio_gpu_ops; @@ -148,6 +177,15 @@ extern const GraphicHwOps virtio_gpu_ops; } \ } while (0) =20 +/* virtio-gpu-base.c */ +bool virtio_gpu_base_device_realize(DeviceState *qdev, + VirtIOHandleOutput ctrl_cb, + VirtIOHandleOutput cursor_cb, + Error **errp); +void virtio_gpu_base_reset(VirtIOGPUBase *g); +void virtio_gpu_base_fill_display_info(VirtIOGPUBase *g, + struct virtio_gpu_resp_display_info *dpy_info); + /* virtio-gpu.c */ void virtio_gpu_ctrl_response(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd, @@ -175,4 +213,5 @@ void virtio_gpu_virgl_fence_poll(VirtIOGPU *g); void virtio_gpu_virgl_reset(VirtIOGPU *g); int virtio_gpu_virgl_init(VirtIOGPU *g); int virtio_gpu_virgl_get_num_capsets(VirtIOGPU *g); + #endif diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c index 2d302526ab..b918167aec 100644 --- a/hw/display/virtio-gpu-3d.c +++ b/hw/display/virtio-gpu-3d.c @@ -118,11 +118,11 @@ static void virgl_cmd_context_destroy(VirtIOGPU *g, static void virtio_gpu_rect_update(VirtIOGPU *g, int idx, int x, int y, int width, int height) { - if (!g->scanout[idx].con) { + if (!g->parent_obj.scanout[idx].con) { return; } =20 - dpy_gl_update(g->scanout[idx].con, x, y, width, height); + dpy_gl_update(g->parent_obj.scanout[idx].con, x, y, width, height); } =20 static void virgl_cmd_resource_flush(VirtIOGPU *g, @@ -135,8 +135,8 @@ static void virgl_cmd_resource_flush(VirtIOGPU *g, trace_virtio_gpu_cmd_res_flush(rf.resource_id, rf.r.width, rf.r.height, rf.r.x, rf.r.y= ); =20 - for (i =3D 0; i < g->conf.max_outputs; i++) { - if (g->scanout[i].resource_id !=3D rf.resource_id) { + for (i =3D 0; i < g->parent_obj.conf.max_outputs; i++) { + if (g->parent_obj.scanout[i].resource_id !=3D rf.resource_id) { continue; } virtio_gpu_rect_update(g, i, rf.r.x, rf.r.y, rf.r.width, rf.r.heig= ht); @@ -154,13 +154,13 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id, ss.r.width, ss.r.height, ss.r.x, ss.r= .y); =20 - if (ss.scanout_id >=3D g->conf.max_outputs) { + if (ss.scanout_id >=3D g->parent_obj.conf.max_outputs) { qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %= d", __func__, ss.scanout_id); cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; return; } - g->enable =3D 1; + g->parent_obj.enable =3D 1; =20 memset(&info, 0, sizeof(info)); =20 @@ -173,20 +173,22 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; return; } - qemu_console_resize(g->scanout[ss.scanout_id].con, + qemu_console_resize(g->parent_obj.scanout[ss.scanout_id].con, ss.r.width, ss.r.height); virgl_renderer_force_ctx_0(); - dpy_gl_scanout_texture(g->scanout[ss.scanout_id].con, info.tex_id, - info.flags & 1 /* FIXME: Y_0_TOP */, - info.width, info.height, - ss.r.x, ss.r.y, ss.r.width, ss.r.height); + dpy_gl_scanout_texture( + g->parent_obj.scanout[ss.scanout_id].con, info.tex_id, + info.flags & 1 /* FIXME: Y_0_TOP */, + info.width, info.height, + ss.r.x, ss.r.y, ss.r.width, ss.r.height); } else { if (ss.scanout_id !=3D 0) { - dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL); + dpy_gfx_replace_surface( + g->parent_obj.scanout[ss.scanout_id].con, NULL); } - dpy_gl_scanout_disable(g->scanout[ss.scanout_id].con); + dpy_gl_scanout_disable(g->parent_obj.scanout[ss.scanout_id].con); } - g->scanout[ss.scanout_id].resource_id =3D ss.resource_id; + g->parent_obj.scanout[ss.scanout_id].resource_id =3D ss.resource_id; } =20 static void virgl_cmd_submit_3d(VirtIOGPU *g, @@ -209,7 +211,7 @@ static void virgl_cmd_submit_3d(VirtIOGPU *g, goto out; } =20 - if (virtio_gpu_stats_enabled(g->conf)) { + if (virtio_gpu_stats_enabled(g->parent_obj.conf)) { g->stats.req_3d++; g->stats.bytes_3d +=3D cs.size; } @@ -507,7 +509,7 @@ static void virgl_write_fence(void *opaque, uint32_t fe= nce) QTAILQ_REMOVE(&g->fenceq, cmd, next); g_free(cmd); g->inflight--; - if (virtio_gpu_stats_enabled(g->conf)) { + if (virtio_gpu_stats_enabled(g->parent_obj.conf)) { fprintf(stderr, "inflight: %3d (-)\r", g->inflight); } } @@ -524,7 +526,7 @@ virgl_create_context(void *opaque, int scanout_idx, qparams.major_ver =3D params->major_ver; qparams.minor_ver =3D params->minor_ver; =20 - ctx =3D dpy_gl_ctx_create(g->scanout[scanout_idx].con, &qparams); + ctx =3D dpy_gl_ctx_create(g->parent_obj.scanout[scanout_idx].con, &qpa= rams); return (virgl_renderer_gl_context)ctx; } =20 @@ -533,7 +535,7 @@ static void virgl_destroy_context(void *opaque, virgl_r= enderer_gl_context ctx) VirtIOGPU *g =3D opaque; QEMUGLContext qctx =3D (QEMUGLContext)ctx; =20 - dpy_gl_ctx_destroy(g->scanout[0].con, qctx); + dpy_gl_ctx_destroy(g->parent_obj.scanout[0].con, qctx); } =20 static int virgl_make_context_current(void *opaque, int scanout_idx, @@ -542,7 +544,8 @@ static int virgl_make_context_current(void *opaque, int= scanout_idx, VirtIOGPU *g =3D opaque; QEMUGLContext qctx =3D (QEMUGLContext)ctx; =20 - return dpy_gl_ctx_make_current(g->scanout[scanout_idx].con, qctx); + return dpy_gl_ctx_make_current(g->parent_obj.scanout[scanout_idx].con, + qctx); } =20 static struct virgl_renderer_callbacks virtio_gpu_3d_cbs =3D { @@ -594,11 +597,11 @@ void virtio_gpu_virgl_reset(VirtIOGPU *g) int i; =20 /* virgl_renderer_reset() ??? */ - for (i =3D 0; i < g->conf.max_outputs; i++) { + for (i =3D 0; i < g->parent_obj.conf.max_outputs; i++) { if (i !=3D 0) { - dpy_gfx_replace_surface(g->scanout[i].con, NULL); + dpy_gfx_replace_surface(g->parent_obj.scanout[i].con, NULL); } - dpy_gl_scanout_disable(g->scanout[i].con); + dpy_gl_scanout_disable(g->parent_obj.scanout[i].con); } } =20 @@ -614,7 +617,7 @@ int virtio_gpu_virgl_init(VirtIOGPU *g) g->fence_poll =3D timer_new_ms(QEMU_CLOCK_VIRTUAL, virtio_gpu_fence_poll, g); =20 - if (virtio_gpu_stats_enabled(g->conf)) { + if (virtio_gpu_stats_enabled(g->parent_obj.conf)) { g->print_stats =3D timer_new_ms(QEMU_CLOCK_VIRTUAL, virtio_gpu_print_stats, g); timer_mod(g->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + = 1000); diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c new file mode 100644 index 0000000000..55e07995fe --- /dev/null +++ b/hw/display/virtio-gpu-base.c @@ -0,0 +1,268 @@ +/* + * Virtio GPU Device + * + * Copyright Red Hat, Inc. 2013-2014 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "hw/virtio/virtio-gpu.h" +#include "migration/blocker.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "trace.h" + +void +virtio_gpu_base_reset(VirtIOGPUBase *g) +{ + int i; + + g->enable =3D 0; + g->use_virgl_renderer =3D false; + + for (i =3D 0; i < g->conf.max_outputs; i++) { + g->scanout[i].resource_id =3D 0; + g->scanout[i].width =3D 0; + g->scanout[i].height =3D 0; + g->scanout[i].x =3D 0; + g->scanout[i].y =3D 0; + g->scanout[i].ds =3D NULL; + } +} + +void +virtio_gpu_base_fill_display_info(VirtIOGPUBase *g, + struct virtio_gpu_resp_display_info *dpy= _info) +{ + int i; + + for (i =3D 0; i < g->conf.max_outputs; i++) { + if (g->enabled_output_bitmask & (1 << i)) { + dpy_info->pmodes[i].enabled =3D 1; + dpy_info->pmodes[i].r.width =3D cpu_to_le32(g->req_state[i].wi= dth); + dpy_info->pmodes[i].r.height =3D cpu_to_le32(g->req_state[i].h= eight); + } + } +} + +static void virtio_gpu_invalidate_display(void *opaque) +{ +} + +static void virtio_gpu_update_display(void *opaque) +{ +} + +static void virtio_gpu_text_update(void *opaque, console_ch_t *chardata) +{ +} + +static void virtio_gpu_notify_event(VirtIOGPUBase *g, uint32_t event_type) +{ + g->virtio_config.events_read |=3D event_type; + virtio_notify_config(&g->parent_obj); +} + +static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) +{ + VirtIOGPUBase *g =3D opaque; + + if (idx >=3D g->conf.max_outputs) { + return -1; + } + + g->req_state[idx].x =3D info->xoff; + g->req_state[idx].y =3D info->yoff; + g->req_state[idx].width =3D info->width; + g->req_state[idx].height =3D info->height; + + if (info->width && info->height) { + g->enabled_output_bitmask |=3D (1 << idx); + } else { + g->enabled_output_bitmask &=3D ~(1 << idx); + } + + /* send event to guest */ + virtio_gpu_notify_event(g, VIRTIO_GPU_EVENT_DISPLAY); + return 0; +} + +static void +virtio_gpu_gl_block(void *opaque, bool block) +{ + VirtIOGPUBase *g =3D opaque; + VirtIOGPUBaseClass *vgc =3D VIRTIO_GPU_BASE_GET_CLASS(g); + + if (block) { + g->renderer_blocked++; + } else { + g->renderer_blocked--; + } + assert(g->renderer_blocked >=3D 0); + + if (g->renderer_blocked =3D=3D 0) { + vgc->gl_unblock(g); + } +} + +const GraphicHwOps virtio_gpu_ops =3D { + .invalidate =3D virtio_gpu_invalidate_display, + .gfx_update =3D virtio_gpu_update_display, + .text_update =3D virtio_gpu_text_update, + .ui_info =3D virtio_gpu_ui_info, + .gl_block =3D virtio_gpu_gl_block, +}; + +bool +virtio_gpu_base_device_realize(DeviceState *qdev, + VirtIOHandleOutput ctrl_cb, + VirtIOHandleOutput cursor_cb, + Error **errp) +{ + VirtIODevice *vdev =3D VIRTIO_DEVICE(qdev); + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(qdev); + Error *local_err =3D NULL; + int i; + + if (g->conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS) { + error_setg(errp, "invalid max_outputs > %d", VIRTIO_GPU_MAX_SCANOU= TS); + return false; + } + + g->use_virgl_renderer =3D false; + if (virtio_gpu_virgl_enabled(g->conf)) { + error_setg(&g->migration_blocker, "virgl is not yet migratable"); + migrate_add_blocker(g->migration_blocker, &local_err); + if (local_err) { + error_propagate(errp, local_err); + error_free(g->migration_blocker); + return false; + } + } + + g->virtio_config.num_scanouts =3D cpu_to_le32(g->conf.max_outputs); + virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU, + sizeof(struct virtio_gpu_config)); + + if (virtio_gpu_virgl_enabled(g->conf)) { + /* use larger control queue in 3d mode */ + virtio_add_queue(vdev, 256, ctrl_cb); + virtio_add_queue(vdev, 16, cursor_cb); + } else { + virtio_add_queue(vdev, 64, ctrl_cb); + virtio_add_queue(vdev, 16, cursor_cb); + } + + g->enabled_output_bitmask =3D 1; + + g->req_state[0].width =3D g->conf.xres; + g->req_state[0].height =3D g->conf.yres; + + for (i =3D 0; i < g->conf.max_outputs; i++) { + g->scanout[i].con =3D + graphic_console_init(DEVICE(g), i, &virtio_gpu_ops, g); + if (i > 0) { + dpy_gfx_replace_surface(g->scanout[i].con, NULL); + } + } + + return true; +} + +static uint64_t +virtio_gpu_base_get_features(VirtIODevice *vdev, uint64_t features, + Error **errp) +{ + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(vdev); + + if (virtio_gpu_virgl_enabled(g->conf)) { + features |=3D (1 << VIRTIO_GPU_F_VIRGL); + } + if (virtio_gpu_edid_enabled(g->conf)) { + features |=3D (1 << VIRTIO_GPU_F_EDID); + } + + return features; +} + +static void +virtio_gpu_base_set_features(VirtIODevice *vdev, uint64_t features) +{ + static const uint32_t virgl =3D (1 << VIRTIO_GPU_F_VIRGL); + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(vdev); + + g->use_virgl_renderer =3D ((features & virgl) =3D=3D virgl); + trace_virtio_gpu_features(g->use_virgl_renderer); +} + +static void +virtio_gpu_base_device_unrealize(DeviceState *qdev, Error **errp) +{ + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(qdev); + + if (g->migration_blocker) { + migrate_del_blocker(g->migration_blocker); + error_free(g->migration_blocker); + } +} + +static void +virtio_gpu_base_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + VirtioDeviceClass *vdc =3D VIRTIO_DEVICE_CLASS(klass); + + vdc->unrealize =3D virtio_gpu_base_device_unrealize; + vdc->get_features =3D virtio_gpu_base_get_features; + vdc->set_features =3D virtio_gpu_base_set_features; + + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); + dc->hotpluggable =3D false; +} + +static const TypeInfo virtio_gpu_base_info =3D { + .name =3D TYPE_VIRTIO_GPU_BASE, + .parent =3D TYPE_VIRTIO_DEVICE, + .instance_size =3D sizeof(VirtIOGPUBase), + .class_size =3D sizeof(VirtIOGPUBaseClass), + .class_init =3D virtio_gpu_base_class_init, + .abstract =3D true +}; + +static void +virtio_register_types(void) +{ + type_register_static(&virtio_gpu_base_info); +} + +type_init(virtio_register_types) + +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctrl_hdr) !=3D 2= 4); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_update_cursor) !=3D 5= 6); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_unref) !=3D 3= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_2d) !=3D 4= 0); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_set_scanout) !=3D 4= 8); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_flush) !=3D 4= 8); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_to_host_2d) !=3D 5= 6); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_mem_entry) !=3D 1= 6); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_attach_backing) !=3D 3= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_detach_backing) !=3D 3= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_display_info) !=3D 4= 08); + +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_host_3d) !=3D 7= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_3d) !=3D 7= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_create) !=3D 9= 6); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_destroy) !=3D 2= 4); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_resource) !=3D 3= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_cmd_submit) !=3D 3= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset_info) !=3D 3= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset_info) !=3D 4= 0); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset) !=3D 3= 2); +QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset) !=3D 2= 4); diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c index bdcd33c925..305ea5fff3 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -41,7 +41,7 @@ static Property virtio_gpu_pci_properties[] =3D { static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VirtIOGPUPCI *vgpu =3D VIRTIO_GPU_PCI(vpci_dev); - VirtIOGPU *g =3D &vgpu->vdev; + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vgpu->vdev); DeviceState *vdev =3D DEVICE(&vgpu->vdev); int i; Error *local_error =3D NULL; diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index c35025b6fb..b0a71537b9 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -23,9 +23,9 @@ #include "hw/virtio/virtio-gpu-bswap.h" #include "hw/virtio/virtio-bus.h" #include "hw/display/edid.h" -#include "migration/blocker.h" #include "qemu/log.h" #include "qapi/error.h" +#include "qemu/error-report.h" =20 #define VIRTIO_GPU_VM_VERSION 1 =20 @@ -39,7 +39,7 @@ static void virtio_gpu_cleanup_mapping(VirtIOGPU *g, #include #define VIRGL(_g, _virgl, _simple, ...) \ do { \ - if (_g->use_virgl_renderer) { \ + if (_g->parent_obj.use_virgl_renderer) { \ _virgl(__VA_ARGS__); \ } else { \ _simple(__VA_ARGS__); \ @@ -107,10 +107,10 @@ static void update_cursor(VirtIOGPU *g, struct virtio= _gpu_update_cursor *cursor) struct virtio_gpu_scanout *s; bool move =3D cursor->hdr.type =3D=3D VIRTIO_GPU_CMD_MOVE_CURSOR; =20 - if (cursor->pos.scanout_id >=3D g->conf.max_outputs) { + if (cursor->pos.scanout_id >=3D g->parent_obj.conf.max_outputs) { return; } - s =3D &g->scanout[cursor->pos.scanout_id]; + s =3D &g->parent_obj.scanout[cursor->pos.scanout_id]; =20 trace_virtio_gpu_update_cursor(cursor->pos.scanout_id, cursor->pos.x, @@ -141,53 +141,6 @@ static void update_cursor(VirtIOGPU *g, struct virtio_= gpu_update_cursor *cursor) cursor->resource_id ? 1 : 0); } =20 -static void virtio_gpu_get_config(VirtIODevice *vdev, uint8_t *config) -{ - VirtIOGPU *g =3D VIRTIO_GPU(vdev); - memcpy(config, &g->virtio_config, sizeof(g->virtio_config)); -} - -static void virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *confi= g) -{ - VirtIOGPU *g =3D VIRTIO_GPU(vdev); - struct virtio_gpu_config vgconfig; - - memcpy(&vgconfig, config, sizeof(g->virtio_config)); - - if (vgconfig.events_clear) { - g->virtio_config.events_read &=3D ~vgconfig.events_clear; - } -} - -static uint64_t virtio_gpu_get_features(VirtIODevice *vdev, uint64_t featu= res, - Error **errp) -{ - VirtIOGPU *g =3D VIRTIO_GPU(vdev); - - if (virtio_gpu_virgl_enabled(g->conf)) { - features |=3D (1 << VIRTIO_GPU_F_VIRGL); - } - if (virtio_gpu_edid_enabled(g->conf)) { - features |=3D (1 << VIRTIO_GPU_F_EDID); - } - return features; -} - -static void virtio_gpu_set_features(VirtIODevice *vdev, uint64_t features) -{ - static const uint32_t virgl =3D (1 << VIRTIO_GPU_F_VIRGL); - VirtIOGPU *g =3D VIRTIO_GPU(vdev); - - g->use_virgl_renderer =3D ((features & virgl) =3D=3D virgl); - trace_virtio_gpu_features(g->use_virgl_renderer); -} - -static void virtio_gpu_notify_event(VirtIOGPU *g, uint32_t event_type) -{ - g->virtio_config.events_read |=3D event_type; - virtio_notify_config(&g->parent_obj); -} - static struct virtio_gpu_simple_resource * virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id) { @@ -236,21 +189,6 @@ void virtio_gpu_ctrl_response_nodata(VirtIOGPU *g, virtio_gpu_ctrl_response(g, cmd, &resp, sizeof(resp)); } =20 -static void -virtio_gpu_fill_display_info(VirtIOGPU *g, - struct virtio_gpu_resp_display_info *dpy_info) -{ - int i; - - for (i =3D 0; i < g->conf.max_outputs; i++) { - if (g->enabled_output_bitmask & (1 << i)) { - dpy_info->pmodes[i].enabled =3D 1; - dpy_info->pmodes[i].r.width =3D cpu_to_le32(g->req_state[i].wi= dth); - dpy_info->pmodes[i].r.height =3D cpu_to_le32(g->req_state[i].h= eight); - } - } -} - void virtio_gpu_get_display_info(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { @@ -259,7 +197,7 @@ void virtio_gpu_get_display_info(VirtIOGPU *g, trace_virtio_gpu_cmd_get_display_info(); memset(&display_info, 0, sizeof(display_info)); display_info.hdr.type =3D VIRTIO_GPU_RESP_OK_DISPLAY_INFO; - virtio_gpu_fill_display_info(g, &display_info); + virtio_gpu_base_fill_display_info(VIRTIO_GPU_BASE(g), &display_info); virtio_gpu_ctrl_response(g, cmd, &display_info.hdr, sizeof(display_info)); } @@ -268,9 +206,10 @@ static void virtio_gpu_generate_edid(VirtIOGPU *g, int scanout, struct virtio_gpu_resp_edid *edid) { + VirtIOGPUBase *b =3D VIRTIO_GPU_BASE(g); qemu_edid_info info =3D { - .prefx =3D g->req_state[scanout].width, - .prefy =3D g->req_state[scanout].height, + .prefx =3D b->req_state[scanout].width, + .prefy =3D b->req_state[scanout].height, }; =20 edid->size =3D cpu_to_le32(sizeof(edid->edid)); @@ -282,11 +221,12 @@ void virtio_gpu_get_edid(VirtIOGPU *g, { struct virtio_gpu_resp_edid edid; struct virtio_gpu_cmd_get_edid get_edid; + VirtIOGPUBase *b =3D VIRTIO_GPU_BASE(g); =20 VIRTIO_GPU_FILL_CMD(get_edid); virtio_gpu_bswap_32(&get_edid, sizeof(get_edid)); =20 - if (get_edid.scanout >=3D g->conf.max_outputs) { + if (get_edid.scanout >=3D b->conf.max_outputs) { cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; return; } @@ -379,7 +319,7 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, } =20 res->hostmem =3D calc_image_hostmem(pformat, c2d.width, c2d.height); - if (res->hostmem + g->hostmem < g->conf.max_hostmem) { + if (res->hostmem + g->hostmem < g->conf_max_hostmem) { res->image =3D pixman_image_create_bits(pformat, c2d.width, c2d.height, @@ -401,7 +341,7 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, =20 static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id) { - struct virtio_gpu_scanout *scanout =3D &g->scanout[scanout_id]; + struct virtio_gpu_scanout *scanout =3D &g->parent_obj.scanout[scanout_= id]; struct virtio_gpu_simple_resource *res; DisplaySurface *ds =3D NULL; =20 @@ -433,7 +373,7 @@ static void virtio_gpu_resource_destroy(VirtIOGPU *g, int i; =20 if (res->scanout_bitmask) { - for (i =3D 0; i < g->conf.max_outputs; i++) { + for (i =3D 0; i < g->parent_obj.conf.max_outputs; i++) { if (res->scanout_bitmask & (1 << i)) { virtio_gpu_disable_scanout(g, i); } @@ -563,7 +503,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g, =20 pixman_region_init_rect(&flush_region, rf.r.x, rf.r.y, rf.r.width, rf.r.height); - for (i =3D 0; i < g->conf.max_outputs; i++) { + for (i =3D 0; i < g->parent_obj.conf.max_outputs; i++) { struct virtio_gpu_scanout *scanout; pixman_region16_t region, finalregion; pixman_box16_t *extents; @@ -571,7 +511,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g, if (!(res->scanout_bitmask & (1 << i))) { continue; } - scanout =3D &g->scanout[i]; + scanout =3D &g->parent_obj.scanout[i]; =20 pixman_region_init(&finalregion); pixman_region_init_rect(®ion, scanout->x, scanout->y, @@ -581,7 +521,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g, pixman_region_translate(&finalregion, -scanout->x, -scanout->y); extents =3D pixman_region_extents(&finalregion); /* work out the area we need to update for each console */ - dpy_gfx_update(g->scanout[i].con, + dpy_gfx_update(g->parent_obj.scanout[i].con, extents->x1, extents->y1, extents->x2 - extents->x1, extents->y2 - extents->y1); @@ -612,14 +552,14 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id, ss.r.width, ss.r.height, ss.r.x, ss.r= .y); =20 - if (ss.scanout_id >=3D g->conf.max_outputs) { + if (ss.scanout_id >=3D g->parent_obj.conf.max_outputs) { qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %= d", __func__, ss.scanout_id); cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; return; } =20 - g->enable =3D 1; + g->parent_obj.enable =3D 1; if (ss.resource_id =3D=3D 0) { virtio_gpu_disable_scanout(g, ss.scanout_id); return; @@ -648,7 +588,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, return; } =20 - scanout =3D &g->scanout[ss.scanout_id]; + scanout =3D &g->parent_obj.scanout[ss.scanout_id]; =20 format =3D pixman_image_get_format(res->image); bpp =3D DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8); @@ -671,7 +611,8 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, return; } pixman_image_unref(rect); - dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, scanout->ds= ); + dpy_gfx_replace_surface(g->parent_obj.scanout[ss.scanout_id].con, + scanout->ds); } =20 ores =3D virtio_gpu_find_resource(g, scanout->resource_id); @@ -889,7 +830,7 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g) while (!QTAILQ_EMPTY(&g->cmdq)) { cmd =3D QTAILQ_FIRST(&g->cmdq); =20 - if (g->renderer_blocked) { + if (g->parent_obj.renderer_blocked) { break; } =20 @@ -898,14 +839,14 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g) g, cmd); =20 QTAILQ_REMOVE(&g->cmdq, cmd, next); - if (virtio_gpu_stats_enabled(g->conf)) { + if (virtio_gpu_stats_enabled(g->parent_obj.conf)) { g->stats.requests++; } =20 if (!cmd->finished) { QTAILQ_INSERT_TAIL(&g->fenceq, cmd, next); g->inflight++; - if (virtio_gpu_stats_enabled(g->conf)) { + if (virtio_gpu_stats_enabled(g->parent_obj.conf)) { if (g->stats.max_inflight < g->inflight) { g->stats.max_inflight =3D g->inflight; } @@ -917,6 +858,19 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g) } } =20 +static void virtio_gpu_gl_unblock(VirtIOGPUBase *b) +{ + VirtIOGPU *g =3D VIRTIO_GPU(b); + +#ifdef CONFIG_VIRGL + if (g->renderer_reset) { + g->renderer_reset =3D false; + virtio_gpu_virgl_reset(g); + } +#endif + virtio_gpu_process_cmdq(g); +} + static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) { VirtIOGPU *g =3D VIRTIO_GPU(vdev); @@ -927,7 +881,7 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, = VirtQueue *vq) } =20 #ifdef CONFIG_VIRGL - if (!g->renderer_inited && g->use_virgl_renderer) { + if (!g->renderer_inited && g->parent_obj.use_virgl_renderer) { virtio_gpu_virgl_init(g); g->renderer_inited =3D true; } @@ -945,7 +899,7 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, = VirtQueue *vq) virtio_gpu_process_cmdq(g); =20 #ifdef CONFIG_VIRGL - if (g->use_virgl_renderer) { + if (g->parent_obj.use_virgl_renderer) { virtio_gpu_virgl_fence_poll(g); } #endif @@ -954,7 +908,7 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, = VirtQueue *vq) static void virtio_gpu_ctrl_bh(void *opaque) { VirtIOGPU *g =3D opaque; - virtio_gpu_handle_ctrl(&g->parent_obj, g->ctrl_vq); + virtio_gpu_handle_ctrl(&g->parent_obj.parent_obj, g->ctrl_vq); } =20 static void virtio_gpu_handle_cursor(VirtIODevice *vdev, VirtQueue *vq) @@ -992,75 +946,9 @@ static void virtio_gpu_handle_cursor(VirtIODevice *vde= v, VirtQueue *vq) static void virtio_gpu_cursor_bh(void *opaque) { VirtIOGPU *g =3D opaque; - virtio_gpu_handle_cursor(&g->parent_obj, g->cursor_vq); -} - -static void virtio_gpu_invalidate_display(void *opaque) -{ + virtio_gpu_handle_cursor(&g->parent_obj.parent_obj, g->cursor_vq); } =20 -static void virtio_gpu_update_display(void *opaque) -{ -} - -static void virtio_gpu_text_update(void *opaque, console_ch_t *chardata) -{ -} - -static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) -{ - VirtIOGPU *g =3D opaque; - - if (idx >=3D g->conf.max_outputs) { - return -1; - } - - g->req_state[idx].x =3D info->xoff; - g->req_state[idx].y =3D info->yoff; - g->req_state[idx].width =3D info->width; - g->req_state[idx].height =3D info->height; - - if (info->width && info->height) { - g->enabled_output_bitmask |=3D (1 << idx); - } else { - g->enabled_output_bitmask &=3D ~(1 << idx); - } - - /* send event to guest */ - virtio_gpu_notify_event(g, VIRTIO_GPU_EVENT_DISPLAY); - return 0; -} - -static void virtio_gpu_gl_block(void *opaque, bool block) -{ - VirtIOGPU *g =3D opaque; - - if (block) { - g->renderer_blocked++; - } else { - g->renderer_blocked--; - } - assert(g->renderer_blocked >=3D 0); - - if (g->renderer_blocked =3D=3D 0) { -#ifdef CONFIG_VIRGL - if (g->renderer_reset) { - g->renderer_reset =3D false; - virtio_gpu_virgl_reset(g); - } -#endif - virtio_gpu_process_cmdq(g); - } -} - -const GraphicHwOps virtio_gpu_ops =3D { - .invalidate =3D virtio_gpu_invalidate_display, - .gfx_update =3D virtio_gpu_update_display, - .text_update =3D virtio_gpu_text_update, - .ui_info =3D virtio_gpu_ui_info, - .gl_block =3D virtio_gpu_gl_block, -}; - static const VMStateDescription vmstate_virtio_gpu_scanout =3D { .name =3D "virtio-gpu-one-scanout", .version_id =3D 1, @@ -1083,10 +971,11 @@ static const VMStateDescription vmstate_virtio_gpu_s= canouts =3D { .name =3D "virtio-gpu-scanouts", .version_id =3D 1, .fields =3D (VMStateField[]) { - VMSTATE_INT32(enable, struct VirtIOGPU), - VMSTATE_UINT32_EQUAL(conf.max_outputs, struct VirtIOGPU, NULL), - VMSTATE_STRUCT_VARRAY_UINT32(scanout, struct VirtIOGPU, - conf.max_outputs, 1, + VMSTATE_INT32(parent_obj.enable, struct VirtIOGPU), + VMSTATE_UINT32_EQUAL(parent_obj.conf.max_outputs, + struct VirtIOGPU, NULL), + VMSTATE_STRUCT_VARRAY_UINT32(parent_obj.scanout, struct VirtIOGPU, + parent_obj.conf.max_outputs, 1, vmstate_virtio_gpu_scanout, struct virtio_gpu_scanout), VMSTATE_END_OF_LIST() @@ -1201,8 +1090,8 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque,= size_t size, =20 /* load & apply scanout state */ vmstate_load_state(f, &vmstate_virtio_gpu_scanouts, g, 1); - for (i =3D 0; i < g->conf.max_outputs; i++) { - scanout =3D &g->scanout[i]; + for (i =3D 0; i < g->parent_obj.conf.max_outputs; i++) { + scanout =3D &g->parent_obj.scanout[i]; if (!scanout->resource_id) { continue; } @@ -1231,84 +1120,35 @@ static void virtio_gpu_device_realize(DeviceState *= qdev, Error **errp) VirtIODevice *vdev =3D VIRTIO_DEVICE(qdev); VirtIOGPU *g =3D VIRTIO_GPU(qdev); bool have_virgl; - Error *local_err =3D NULL; - int i; =20 - if (g->conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS) { - error_setg(errp, "invalid max_outputs > %d", VIRTIO_GPU_MAX_SCANOU= TS); - return; - } - - g->use_virgl_renderer =3D false; #if !defined(CONFIG_VIRGL) || defined(HOST_WORDS_BIGENDIAN) have_virgl =3D false; #else have_virgl =3D display_opengl; #endif if (!have_virgl) { - g->conf.flags &=3D ~(1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED); - } - - if (virtio_gpu_virgl_enabled(g->conf)) { - error_setg(&g->migration_blocker, "virgl is not yet migratable"); - migrate_add_blocker(g->migration_blocker, &local_err); - if (local_err) { - error_propagate(errp, local_err); - error_free(g->migration_blocker); - return; - } + g->parent_obj.conf.flags &=3D ~(1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED= ); } =20 - g->virtio_config.num_scanouts =3D cpu_to_le32(g->conf.max_outputs); - virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU, - sizeof(struct virtio_gpu_config)); - - g->req_state[0].width =3D g->conf.xres; - g->req_state[0].height =3D g->conf.yres; - - if (virtio_gpu_virgl_enabled(g->conf)) { - /* use larger control queue in 3d mode */ - g->ctrl_vq =3D virtio_add_queue(vdev, 256, virtio_gpu_handle_ctr= l_cb); - g->cursor_vq =3D virtio_add_queue(vdev, 16, virtio_gpu_handle_curs= or_cb); - #if defined(CONFIG_VIRGL) - g->virtio_config.num_capsets =3D virtio_gpu_virgl_get_num_capsets(= g); -#else - g->virtio_config.num_capsets =3D 0; + VIRTIO_GPU_BASE(g)->virtio_config.num_capsets =3D + virtio_gpu_virgl_get_num_capsets(g); #endif - } else { - g->ctrl_vq =3D virtio_add_queue(vdev, 64, virtio_gpu_handle_ctrl= _cb); - g->cursor_vq =3D virtio_add_queue(vdev, 16, virtio_gpu_handle_curs= or_cb); + + if (!virtio_gpu_base_device_realize(qdev, + virtio_gpu_handle_ctrl_cb, + virtio_gpu_handle_cursor_cb, + errp)) { + return; } =20 + g->ctrl_vq =3D virtio_get_queue(vdev, 0); + g->cursor_vq =3D virtio_get_queue(vdev, 1); g->ctrl_bh =3D qemu_bh_new(virtio_gpu_ctrl_bh, g); g->cursor_bh =3D qemu_bh_new(virtio_gpu_cursor_bh, g); QTAILQ_INIT(&g->reslist); QTAILQ_INIT(&g->cmdq); QTAILQ_INIT(&g->fenceq); - - g->enabled_output_bitmask =3D 1; - - for (i =3D 0; i < g->conf.max_outputs; i++) { - g->scanout[i].con =3D - graphic_console_init(DEVICE(g), i, &virtio_gpu_ops, g); - if (i > 0) { - dpy_gfx_replace_surface(g->scanout[i].con, NULL); - } - } -} - -static void virtio_gpu_device_unrealize(DeviceState *qdev, Error **errp) -{ - VirtIOGPU *g =3D VIRTIO_GPU(qdev); - if (g->migration_blocker) { - migrate_del_blocker(g->migration_blocker); - error_free(g->migration_blocker); - } -} - -static void virtio_gpu_instance_init(Object *obj) -{ } =20 static void virtio_gpu_reset(VirtIODevice *vdev) @@ -1316,21 +1156,16 @@ static void virtio_gpu_reset(VirtIODevice *vdev) VirtIOGPU *g =3D VIRTIO_GPU(vdev); struct virtio_gpu_simple_resource *res, *tmp; struct virtio_gpu_ctrl_command *cmd; - int i; =20 - g->enable =3D 0; +#ifdef CONFIG_VIRGL + if (g->parent_obj.use_virgl_renderer) { + virtio_gpu_virgl_reset(g); + } +#endif =20 QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) { virtio_gpu_resource_destroy(g, res); } - for (i =3D 0; i < g->conf.max_outputs; i++) { - g->scanout[i].resource_id =3D 0; - g->scanout[i].width =3D 0; - g->scanout[i].height =3D 0; - g->scanout[i].x =3D 0; - g->scanout[i].y =3D 0; - g->scanout[i].ds =3D NULL; - } =20 while (!QTAILQ_EMPTY(&g->cmdq)) { cmd =3D QTAILQ_FIRST(&g->cmdq); @@ -1346,15 +1181,37 @@ static void virtio_gpu_reset(VirtIODevice *vdev) } =20 #ifdef CONFIG_VIRGL - if (g->use_virgl_renderer) { - if (g->renderer_blocked) { + if (g->parent_obj.use_virgl_renderer) { + if (g->parent_obj.renderer_blocked) { g->renderer_reset =3D true; } else { virtio_gpu_virgl_reset(g); } - g->use_virgl_renderer =3D 0; + g->parent_obj.use_virgl_renderer =3D false; } #endif + + virtio_gpu_base_reset(VIRTIO_GPU_BASE(vdev)); +} + +static void +virtio_gpu_get_config(VirtIODevice *vdev, uint8_t *config) +{ + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(vdev); + + memcpy(config, &g->virtio_config, sizeof(g->virtio_config)); +} + +static void +virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config) +{ + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(vdev); + const struct virtio_gpu_config *vgconfig =3D + (const struct virtio_gpu_config *)config; + + if (vgconfig->events_clear) { + g->virtio_config.events_read &=3D ~vgconfig->events_clear; + } } =20 /* @@ -1385,18 +1242,15 @@ static const VMStateDescription vmstate_virtio_gpu = =3D { }; =20 static Property virtio_gpu_properties[] =3D { - DEFINE_PROP_UINT32("max_outputs", VirtIOGPU, conf.max_outputs, 1), - DEFINE_PROP_SIZE("max_hostmem", VirtIOGPU, conf.max_hostmem, 256 * MiB= ), + VIRTIO_GPU_BASE_PROPERTIES(VirtIOGPU, parent_obj.conf), + DEFINE_PROP_SIZE("max_hostmem", VirtIOGPU, conf_max_hostmem, + 256 * MiB), #ifdef CONFIG_VIRGL - DEFINE_PROP_BIT("virgl", VirtIOGPU, conf.flags, + DEFINE_PROP_BIT("virgl", VirtIOGPU, parent_obj.conf.flags, VIRTIO_GPU_FLAG_VIRGL_ENABLED, true), - DEFINE_PROP_BIT("stats", VirtIOGPU, conf.flags, + DEFINE_PROP_BIT("stats", VirtIOGPU, parent_obj.conf.flags, VIRTIO_GPU_FLAG_STATS_ENABLED, false), #endif - DEFINE_PROP_BIT("edid", VirtIOGPU, conf.flags, - VIRTIO_GPU_FLAG_EDID_ENABLED, false), - DEFINE_PROP_UINT32("xres", VirtIOGPU, conf.xres, 1024), - DEFINE_PROP_UINT32("yres", VirtIOGPU, conf.yres, 768), DEFINE_PROP_END_OF_LIST(), }; =20 @@ -1404,27 +1258,22 @@ static void virtio_gpu_class_init(ObjectClass *klas= s, void *data) { DeviceClass *dc =3D DEVICE_CLASS(klass); VirtioDeviceClass *vdc =3D VIRTIO_DEVICE_CLASS(klass); + VirtIOGPUBaseClass *vgc =3D VIRTIO_GPU_BASE_CLASS(klass); =20 + vgc->gl_unblock =3D virtio_gpu_gl_unblock; vdc->realize =3D virtio_gpu_device_realize; - vdc->unrealize =3D virtio_gpu_device_unrealize; + vdc->reset =3D virtio_gpu_reset; vdc->get_config =3D virtio_gpu_get_config; vdc->set_config =3D virtio_gpu_set_config; - vdc->get_features =3D virtio_gpu_get_features; - vdc->set_features =3D virtio_gpu_set_features; - - vdc->reset =3D virtio_gpu_reset; =20 - set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->props =3D virtio_gpu_properties; dc->vmsd =3D &vmstate_virtio_gpu; - dc->hotpluggable =3D false; + dc->props =3D virtio_gpu_properties; } =20 static const TypeInfo virtio_gpu_info =3D { .name =3D TYPE_VIRTIO_GPU, - .parent =3D TYPE_VIRTIO_DEVICE, + .parent =3D TYPE_VIRTIO_GPU_BASE, .instance_size =3D sizeof(VirtIOGPU), - .instance_init =3D virtio_gpu_instance_init, .class_init =3D virtio_gpu_class_init, }; =20 @@ -1434,26 +1283,3 @@ static void virtio_register_types(void) } =20 type_init(virtio_register_types) - -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctrl_hdr) !=3D 2= 4); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_update_cursor) !=3D 5= 6); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_unref) !=3D 3= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_2d) !=3D 4= 0); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_set_scanout) !=3D 4= 8); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_flush) !=3D 4= 8); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_to_host_2d) !=3D 5= 6); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_mem_entry) !=3D 1= 6); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_attach_backing) !=3D 3= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_detach_backing) !=3D 3= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_display_info) !=3D 4= 08); - -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_host_3d) !=3D 7= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_3d) !=3D 7= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_create) !=3D 9= 6); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_destroy) !=3D 2= 4); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_resource) !=3D 3= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_cmd_submit) !=3D 3= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset_info) !=3D 3= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset_info) !=3D 4= 0); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset) !=3D 3= 2); -QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset) !=3D 2= 4); diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index a2b803b75f..33af54d44f 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -32,8 +32,9 @@ typedef struct VirtIOVGAClass { static void virtio_vga_invalidate_display(void *opaque) { VirtIOVGA *vvga =3D opaque; + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vvga->vdev); =20 - if (vvga->vdev.enable) { + if (g->enable) { virtio_gpu_ops.invalidate(&vvga->vdev); } else { vvga->vga.hw_ops->invalidate(&vvga->vga); @@ -43,8 +44,9 @@ static void virtio_vga_invalidate_display(void *opaque) static void virtio_vga_update_display(void *opaque) { VirtIOVGA *vvga =3D opaque; + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vvga->vdev); =20 - if (vvga->vdev.enable) { + if (g->enable) { virtio_gpu_ops.gfx_update(&vvga->vdev); } else { vvga->vga.hw_ops->gfx_update(&vvga->vga); @@ -54,8 +56,9 @@ static void virtio_vga_update_display(void *opaque) static void virtio_vga_text_update(void *opaque, console_ch_t *chardata) { VirtIOVGA *vvga =3D opaque; + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vvga->vdev); =20 - if (vvga->vdev.enable) { + if (g->enable) { if (virtio_gpu_ops.text_update) { virtio_gpu_ops.text_update(&vvga->vdev, chardata); } @@ -108,7 +111,7 @@ static const VMStateDescription vmstate_virtio_vga =3D { static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { VirtIOVGA *vvga =3D VIRTIO_VGA(vpci_dev); - VirtIOGPU *g =3D &vvga->vdev; + VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vvga->vdev); VGACommonState *vga =3D &vvga->vga; Error *err =3D NULL; uint32_t offset; diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index dbd453ab1b..f1c46b8e3e 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -42,7 +42,7 @@ obj-$(CONFIG_VGA) +=3D vga.o =20 common-obj-$(CONFIG_QXL) +=3D qxl.o qxl-logger.o qxl-render.o =20 -obj-$(CONFIG_VIRTIO_GPU) +=3D virtio-gpu.o virtio-gpu-3d.o +obj-$(CONFIG_VIRTIO_GPU) +=3D virtio-gpu-base.o virtio-gpu.o virtio-gpu-3d= .o obj-$(call land,$(CONFIG_VIRTIO_GPU),$(CONFIG_VIRTIO_PCI)) +=3D virtio-gpu= -pci.o obj-$(CONFIG_VIRTIO_VGA) +=3D virtio-vga.o virtio-gpu.o-cflags :=3D $(VIRGL_CFLAGS) --=20 2.21.0.313.ge35b8cb8e2 From nobody Sun May 5 10:05:35 2024 Delivered-To: importer@patchew.org Received-SPF: temperror (zoho.com: Error in retrieving data from DNS) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=temperror (zoho.com: Error in retrieving data from DNS) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1556026389; cv=none; d=zoho.com; s=zohoarc; b=euqYbBy3VoEZgyW8ECA3V/uaEWLb8a1H5G3WcGXZ1GSvq7BNLdVesFCzpmOpLIb4rGUkrPV+xjY0PFF+4C4/RGV9m0EficewcdekzRSOpQaOg+/zZsAmGbxGPnTtth3t6OC6BonaWR+BzX1kTJmVC5KgcYb0deqxtBP/hGMecng= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556026389; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=wOUJ2cDRQkfLFaEWkWmdCPcrRKyzCwtQzMy0NlqWXpQ=; b=FbelM0tracHu0TJpXc6Y21Hdt6sa5Ppsvhdgqx+GtyVSY2ZAwHlNjy035XgN/s5G5m5T0NbvLeHVevAnQvS+PKe4omq46KKam6SiOxbhEHBQzFzDOMsY0hZ5D/TUffYZF4aVMoSsdahiFCwnlpNrSt3Kx5R/lQYhMlRMwor61g0= ARC-Authentication-Results: i=1; mx.zoho.com; spf=temperror (zoho.com: Error in retrieving data from DNS) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556026389880832.6767146091627; Tue, 23 Apr 2019 06:33:09 -0700 (PDT) Received: from localhost ([127.0.0.1]:53981 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvXl-0008LU-Jj for importer@patchew.org; Tue, 23 Apr 2019 09:33:05 -0400 Received: from eggs.gnu.org ([209.51.188.92]:60913) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvNJ-0007rN-56 for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:22:18 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hIvMu-00067l-1O for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:53 -0400 Received: from mx1.redhat.com ([209.132.183.28]:53052) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hIvMs-00064c-1b for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:51 -0400 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 19DD0C04FFF6 for ; Tue, 23 Apr 2019 13:21:48 +0000 (UTC) Received: from localhost (ovpn-112-54.ams2.redhat.com [10.36.112.54]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1BC9F1001DC5; Tue, 23 Apr 2019 13:21:39 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Tue, 23 Apr 2019 15:20:03 +0200 Message-Id: <20190423132004.13725-11-marcandre.lureau@redhat.com> In-Reply-To: <20190423132004.13725-1-marcandre.lureau@redhat.com> References: <20190423132004.13725-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.31]); Tue, 23 Apr 2019 13:21:48 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v6 10/11] virtio-gpu: split virtio-gpu-pci & virtio-vga X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add base classes that are common to vhost-user-gpu-pci and vhost-user-vga. Signed-off-by: Marc-Andr=C3=A9 Lureau --- hw/display/virtio-vga.h | 32 +++++++++ hw/display/virtio-gpu-pci.c | 52 +++++++++----- hw/display/virtio-vga.c | 135 ++++++++++++++++++------------------ MAINTAINERS | 2 +- 4 files changed, 137 insertions(+), 84 deletions(-) create mode 100644 hw/display/virtio-vga.h diff --git a/hw/display/virtio-vga.h b/hw/display/virtio-vga.h new file mode 100644 index 0000000000..f03e1ba619 --- /dev/null +++ b/hw/display/virtio-vga.h @@ -0,0 +1,32 @@ +#ifndef VIRTIO_VGA_H_ +#define VIRTIO_VGA_H_ + +#include "hw/virtio/virtio-pci.h" +#include "vga_int.h" + +/* + * virtio-vga-base: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_VGA_BASE "virtio-vga-base" +#define VIRTIO_VGA_BASE(obj) \ + OBJECT_CHECK(VirtIOVGABase, (obj), TYPE_VIRTIO_VGA_BASE) +#define VIRTIO_VGA_BASE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VirtIOVGABaseClass, obj, TYPE_VIRTIO_VGA_BASE) +#define VIRTIO_VGA_BASE_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtIOVGABaseClass, klass, TYPE_VIRTIO_VGA_BASE) + +typedef struct VirtIOVGABase { + VirtIOPCIProxy parent_obj; + + VirtIOGPUBase *vgpu; + VGACommonState vga; + MemoryRegion vga_mrs[3]; +} VirtIOVGABase; + +typedef struct VirtIOVGABaseClass { + VirtioPCIClass parent_class; + + DeviceReset parent_reset; +} VirtIOVGABaseClass; + +#endif /* VIRTIO_VGA_H_ */ diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c index 305ea5fff3..3b88bf4d1f 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -19,30 +19,30 @@ #include "hw/virtio/virtio-pci.h" #include "hw/virtio/virtio-gpu.h" =20 -typedef struct VirtIOGPUPCI VirtIOGPUPCI; +typedef struct VirtIOGPUPCIBase VirtIOGPUPCIBase; =20 /* - * virtio-gpu-pci: This extends VirtioPCIProxy. + * virtio-gpu-pci-base: This extends VirtioPCIProxy. */ -#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci" -#define VIRTIO_GPU_PCI(obj) \ - OBJECT_CHECK(VirtIOGPUPCI, (obj), TYPE_VIRTIO_GPU_PCI) +#define TYPE_VIRTIO_GPU_PCI_BASE "virtio-gpu-pci-base" +#define VIRTIO_GPU_PCI_BASE(obj) \ + OBJECT_CHECK(VirtIOGPUPCIBase, (obj), TYPE_VIRTIO_GPU_PCI_BASE) =20 -struct VirtIOGPUPCI { +struct VirtIOGPUPCIBase { VirtIOPCIProxy parent_obj; - VirtIOGPU vdev; + VirtIOGPUBase *vgpu; }; =20 -static Property virtio_gpu_pci_properties[] =3D { +static Property virtio_gpu_pci_base_properties[] =3D { DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), DEFINE_PROP_END_OF_LIST(), }; =20 -static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +static void virtio_gpu_pci_base_realize(VirtIOPCIProxy *vpci_dev, Error **= errp) { - VirtIOGPUPCI *vgpu =3D VIRTIO_GPU_PCI(vpci_dev); - VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vgpu->vdev); - DeviceState *vdev =3D DEVICE(&vgpu->vdev); + VirtIOGPUPCIBase *vgpu =3D VIRTIO_GPU_PCI_BASE(vpci_dev); + VirtIOGPUBase *g =3D vgpu->vgpu; + DeviceState *vdev =3D DEVICE(g); int i; Error *local_error =3D NULL; =20 @@ -62,36 +62,56 @@ static void virtio_gpu_pci_realize(VirtIOPCIProxy *vpci= _dev, Error **errp) } } =20 -static void virtio_gpu_pci_class_init(ObjectClass *klass, void *data) +static void virtio_gpu_pci_base_class_init(ObjectClass *klass, void *data) { DeviceClass *dc =3D DEVICE_CLASS(klass); VirtioPCIClass *k =3D VIRTIO_PCI_CLASS(klass); PCIDeviceClass *pcidev_k =3D PCI_DEVICE_CLASS(klass); =20 set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->props =3D virtio_gpu_pci_properties; + dc->props =3D virtio_gpu_pci_base_properties; dc->hotpluggable =3D false; - k->realize =3D virtio_gpu_pci_realize; + k->realize =3D virtio_gpu_pci_base_realize; pcidev_k->class_id =3D PCI_CLASS_DISPLAY_OTHER; } =20 +static const TypeInfo virtio_gpu_pci_base_info =3D { + .name =3D TYPE_VIRTIO_GPU_PCI_BASE, + .parent =3D TYPE_VIRTIO_PCI, + .instance_size =3D sizeof(VirtIOGPUPCIBase), + .class_init =3D virtio_gpu_pci_base_class_init, + .abstract =3D true +}; + +#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci" +#define VIRTIO_GPU_PCI(obj) \ + OBJECT_CHECK(VirtIOGPUPCI, (obj), TYPE_VIRTIO_GPU_PCI) + +typedef struct VirtIOGPUPCI { + VirtIOGPUPCIBase parent_obj; + VirtIOGPU vdev; +} VirtIOGPUPCI; + static void virtio_gpu_initfn(Object *obj) { VirtIOGPUPCI *dev =3D VIRTIO_GPU_PCI(obj); =20 virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_GPU); + VIRTIO_GPU_PCI_BASE(obj)->vgpu =3D VIRTIO_GPU_BASE(&dev->vdev); } =20 static const VirtioPCIDeviceTypeInfo virtio_gpu_pci_info =3D { .generic_name =3D TYPE_VIRTIO_GPU_PCI, + .parent =3D TYPE_VIRTIO_GPU_PCI_BASE, .instance_size =3D sizeof(VirtIOGPUPCI), .instance_init =3D virtio_gpu_initfn, - .class_init =3D virtio_gpu_pci_class_init, }; =20 static void virtio_gpu_pci_register_types(void) { + type_register_static(&virtio_gpu_pci_base_info); virtio_pci_types_register(&virtio_gpu_pci_info); } + type_init(virtio_gpu_pci_register_types) diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index 33af54d44f..caa3e138b0 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -1,66 +1,42 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" -#include "vga_int.h" -#include "hw/virtio/virtio-pci.h" #include "hw/virtio/virtio-gpu.h" #include "qapi/error.h" +#include "virtio-vga.h" =20 -/* - * virtio-vga: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_VGA "virtio-vga" -#define VIRTIO_VGA(obj) \ - OBJECT_CHECK(VirtIOVGA, (obj), TYPE_VIRTIO_VGA) -#define VIRTIO_VGA_GET_CLASS(obj) \ - OBJECT_GET_CLASS(VirtIOVGAClass, obj, TYPE_VIRTIO_VGA) -#define VIRTIO_VGA_CLASS(klass) \ - OBJECT_CLASS_CHECK(VirtIOVGAClass, klass, TYPE_VIRTIO_VGA) - -typedef struct VirtIOVGA { - VirtIOPCIProxy parent_obj; - VirtIOGPU vdev; - VGACommonState vga; - MemoryRegion vga_mrs[3]; -} VirtIOVGA; - -typedef struct VirtIOVGAClass { - VirtioPCIClass parent_class; - DeviceReset parent_reset; -} VirtIOVGAClass; - -static void virtio_vga_invalidate_display(void *opaque) +static void virtio_vga_base_invalidate_display(void *opaque) { - VirtIOVGA *vvga =3D opaque; - VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vvga->vdev); + VirtIOVGABase *vvga =3D opaque; + VirtIOGPUBase *g =3D vvga->vgpu; =20 if (g->enable) { - virtio_gpu_ops.invalidate(&vvga->vdev); + virtio_gpu_ops.invalidate(g); } else { vvga->vga.hw_ops->invalidate(&vvga->vga); } } =20 -static void virtio_vga_update_display(void *opaque) +static void virtio_vga_base_update_display(void *opaque) { - VirtIOVGA *vvga =3D opaque; - VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vvga->vdev); + VirtIOVGABase *vvga =3D opaque; + VirtIOGPUBase *g =3D vvga->vgpu; =20 if (g->enable) { - virtio_gpu_ops.gfx_update(&vvga->vdev); + virtio_gpu_ops.gfx_update(g); } else { vvga->vga.hw_ops->gfx_update(&vvga->vga); } } =20 -static void virtio_vga_text_update(void *opaque, console_ch_t *chardata) +static void virtio_vga_base_text_update(void *opaque, console_ch_t *charda= ta) { - VirtIOVGA *vvga =3D opaque; - VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vvga->vdev); + VirtIOVGABase *vvga =3D opaque; + VirtIOGPUBase *g =3D vvga->vgpu; =20 if (g->enable) { if (virtio_gpu_ops.text_update) { - virtio_gpu_ops.text_update(&vvga->vdev, chardata); + virtio_gpu_ops.text_update(g, chardata); } } else { if (vvga->vga.hw_ops->text_update) { @@ -69,49 +45,52 @@ static void virtio_vga_text_update(void *opaque, consol= e_ch_t *chardata) } } =20 -static int virtio_vga_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) +static int virtio_vga_base_ui_info(void *opaque, uint32_t idx, QemuUIInfo = *info) { - VirtIOVGA *vvga =3D opaque; + VirtIOVGABase *vvga =3D opaque; + VirtIOGPUBase *g =3D vvga->vgpu; =20 if (virtio_gpu_ops.ui_info) { - return virtio_gpu_ops.ui_info(&vvga->vdev, idx, info); + return virtio_gpu_ops.ui_info(g, idx, info); } return -1; } =20 -static void virtio_vga_gl_block(void *opaque, bool block) +static void virtio_vga_base_gl_block(void *opaque, bool block) { - VirtIOVGA *vvga =3D opaque; + VirtIOVGABase *vvga =3D opaque; + VirtIOGPUBase *g =3D vvga->vgpu; =20 if (virtio_gpu_ops.gl_block) { - virtio_gpu_ops.gl_block(&vvga->vdev, block); + virtio_gpu_ops.gl_block(g, block); } } =20 -static const GraphicHwOps virtio_vga_ops =3D { - .invalidate =3D virtio_vga_invalidate_display, - .gfx_update =3D virtio_vga_update_display, - .text_update =3D virtio_vga_text_update, - .ui_info =3D virtio_vga_ui_info, - .gl_block =3D virtio_vga_gl_block, +static const GraphicHwOps virtio_vga_base_ops =3D { + .invalidate =3D virtio_vga_base_invalidate_display, + .gfx_update =3D virtio_vga_base_update_display, + .text_update =3D virtio_vga_base_text_update, + .ui_info =3D virtio_vga_base_ui_info, + .gl_block =3D virtio_vga_base_gl_block, }; =20 -static const VMStateDescription vmstate_virtio_vga =3D { +static const VMStateDescription vmstate_virtio_vga_base =3D { .name =3D "virtio-vga", .version_id =3D 2, .minimum_version_id =3D 2, .fields =3D (VMStateField[]) { /* no pci stuff here, saving the virtio device will handle that */ - VMSTATE_STRUCT(vga, VirtIOVGA, 0, vmstate_vga_common, VGACommonSta= te), + VMSTATE_STRUCT(vga, VirtIOVGABase, 0, + vmstate_vga_common, VGACommonState), VMSTATE_END_OF_LIST() } }; =20 /* VGA device wrapper around PCI device around virtio GPU */ -static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +static void virtio_vga_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp) { - VirtIOVGA *vvga =3D VIRTIO_VGA(vpci_dev); - VirtIOGPUBase *g =3D VIRTIO_GPU_BASE(&vvga->vdev); + VirtIOVGABase *vvga =3D VIRTIO_VGA_BASE(vpci_dev); + VirtIOGPUBase *g =3D vvga->vgpu; VGACommonState *vga =3D &vvga->vga; Error *err =3D NULL; uint32_t offset; @@ -169,7 +148,7 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev= , Error **errp) vvga->vga_mrs, true, false); =20 vga->con =3D g->scanout[0].con; - graphic_console_set_hwops(vga->con, &virtio_vga_ops, vvga); + graphic_console_set_hwops(vga->con, &virtio_vga_base_ops, vvga); =20 for (i =3D 0; i < g->conf.max_outputs; i++) { object_property_set_link(OBJECT(g->scanout[i].con), @@ -178,10 +157,10 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_d= ev, Error **errp) } } =20 -static void virtio_vga_reset(DeviceState *dev) +static void virtio_vga_base_reset(DeviceState *dev) { - VirtIOVGAClass *klass =3D VIRTIO_VGA_GET_CLASS(dev); - VirtIOVGA *vvga =3D VIRTIO_VGA(dev); + VirtIOVGABaseClass *klass =3D VIRTIO_VGA_BASE_GET_CLASS(dev); + VirtIOVGABase *vvga =3D VIRTIO_VGA_BASE(dev); =20 /* reset virtio-gpu */ klass->parent_reset(dev); @@ -191,48 +170,70 @@ static void virtio_vga_reset(DeviceState *dev) vga_dirty_log_start(&vvga->vga); } =20 -static Property virtio_vga_properties[] =3D { +static Property virtio_vga_base_properties[] =3D { DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), DEFINE_PROP_END_OF_LIST(), }; =20 -static void virtio_vga_class_init(ObjectClass *klass, void *data) +static void virtio_vga_base_class_init(ObjectClass *klass, void *data) { DeviceClass *dc =3D DEVICE_CLASS(klass); VirtioPCIClass *k =3D VIRTIO_PCI_CLASS(klass); - VirtIOVGAClass *v =3D VIRTIO_VGA_CLASS(klass); + VirtIOVGABaseClass *v =3D VIRTIO_VGA_BASE_CLASS(klass); PCIDeviceClass *pcidev_k =3D PCI_DEVICE_CLASS(klass); =20 set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->props =3D virtio_vga_properties; - dc->vmsd =3D &vmstate_virtio_vga; + dc->props =3D virtio_vga_base_properties; + dc->vmsd =3D &vmstate_virtio_vga_base; dc->hotpluggable =3D false; - device_class_set_parent_reset(dc, virtio_vga_reset, + device_class_set_parent_reset(dc, virtio_vga_base_reset, &v->parent_reset); =20 - k->realize =3D virtio_vga_realize; + k->realize =3D virtio_vga_base_realize; pcidev_k->romfile =3D "vgabios-virtio.bin"; pcidev_k->class_id =3D PCI_CLASS_DISPLAY_VGA; } =20 +static TypeInfo virtio_vga_base_info =3D { + .name =3D TYPE_VIRTIO_VGA_BASE, + .parent =3D TYPE_VIRTIO_PCI, + .instance_size =3D sizeof(struct VirtIOVGABase), + .class_size =3D sizeof(struct VirtIOVGABaseClass), + .class_init =3D virtio_vga_base_class_init, + .abstract =3D true, +}; + +#define TYPE_VIRTIO_VGA "virtio-vga" + +#define VIRTIO_VGA(obj) \ + OBJECT_CHECK(VirtIOVGA, (obj), TYPE_VIRTIO_VGA) + +typedef struct VirtIOVGA { + VirtIOVGABase parent_obj; + + VirtIOGPU vdev; +} VirtIOVGA; + static void virtio_vga_inst_initfn(Object *obj) { VirtIOVGA *dev =3D VIRTIO_VGA(obj); =20 virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_GPU); + VIRTIO_VGA_BASE(dev)->vgpu =3D VIRTIO_GPU_BASE(&dev->vdev); } =20 + static VirtioPCIDeviceTypeInfo virtio_vga_info =3D { .generic_name =3D TYPE_VIRTIO_VGA, + .parent =3D TYPE_VIRTIO_VGA_BASE, .instance_size =3D sizeof(struct VirtIOVGA), .instance_init =3D virtio_vga_inst_initfn, - .class_size =3D sizeof(struct VirtIOVGAClass), - .class_init =3D virtio_vga_class_init, }; =20 static void virtio_vga_register_types(void) { + type_register_static(&virtio_vga_base_info); virtio_pci_types_register(&virtio_vga_info); } =20 diff --git a/MAINTAINERS b/MAINTAINERS index 476fe4c49c..ef25553169 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1656,7 +1656,7 @@ virtio-gpu M: Gerd Hoffmann S: Maintained F: hw/display/virtio-gpu* -F: hw/display/virtio-vga.c +F: hw/display/virtio-vga.* F: include/hw/virtio/virtio-gpu.h =20 vhost-user-gpu --=20 2.21.0.313.ge35b8cb8e2 From nobody Sun May 5 10:05:35 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1556026195; cv=none; d=zoho.com; s=zohoarc; b=jThKaDNV3JSr8dUHZ4sH6JLBa1q95xzOUgHK8DUoba8kpvPMV0xiJQHBkxGJBKhsclSA9THcfc5p7pXf/Z6sw/BgHSqCkONEmHHKyn0Jr8NUR5nPsfHEFzyBKIZS0ZBG6B4wvZl/716wbNpUX451SM/egEYrqLCNZlzAVQLe4GU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556026195; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=f2kpcyfasjsCVGBgI5VpeY2/rXYEZRyyEPFcCAgJtVQ=; b=fCNqfruKzp0NFc+Y4WZtgthacuOF3Df8Nk9fmC9+3Kq0yQtd8rEb9YKGM3JlGJDI7Yq38Ma50OdYGuKC94QIii4xT4T4o4rg+oOSReoECa3ATT6pl7jcfCnkf7eEkRqSL7QVzBFp4y7X3cQ1KQXF8o3JQ9r7VfoELgqHQo5gRK0= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556026195457475.30913661908573; Tue, 23 Apr 2019 06:29:55 -0700 (PDT) Received: from localhost ([127.0.0.1]:53920 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvUg-0005k9-9c for importer@patchew.org; Tue, 23 Apr 2019 09:29:54 -0400 Received: from eggs.gnu.org ([209.51.188.92]:60839) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hIvN3-0007bj-8l for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:22:04 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hIvN0-0006Cm-EI for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:22:01 -0400 Received: from mx1.redhat.com ([209.132.183.28]:49830) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hIvN0-0006Bf-4t for qemu-devel@nongnu.org; Tue, 23 Apr 2019 09:21:58 -0400 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 6EEB4308792E for ; Tue, 23 Apr 2019 13:21:57 +0000 (UTC) Received: from localhost (ovpn-112-54.ams2.redhat.com [10.36.112.54]) by smtp.corp.redhat.com (Postfix) with ESMTP id E4ACC1A913; Tue, 23 Apr 2019 13:21:49 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Tue, 23 Apr 2019 15:20:04 +0200 Message-Id: <20190423132004.13725-12-marcandre.lureau@redhat.com> In-Reply-To: <20190423132004.13725-1-marcandre.lureau@redhat.com> References: <20190423132004.13725-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.45]); Tue, 23 Apr 2019 13:21:57 +0000 (UTC) Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PATCH v6 11/11] hw/display: add vhost-user-vga & gpu-pci X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , Paolo Bonzini , Gerd Hoffmann , "Michael S. Tsirkin" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" Add new virtio-gpu devices with a "vhost-user" property. The associated vhost-user backend is used to handle the virtio rings and provide rendering results thanks to the vhost-user-gpu protocol. Example usage: -object vhost-user-backend,id=3Dvug,cmd=3D"./vhost-user-gpu" -device vhost-user-vga,vhost-user=3Dvug Signed-off-by: Marc-Andr=C3=A9 Lureau --- hw/display/virtio-vga.h | 2 +- include/hw/virtio/virtio-gpu-pci.h | 40 ++ include/hw/virtio/virtio-gpu.h | 19 +- hw/display/vhost-user-gpu-pci.c | 51 +++ hw/display/vhost-user-gpu.c | 572 +++++++++++++++++++++++++++++ hw/display/vhost-user-vga.c | 52 +++ hw/display/virtio-gpu-pci.c | 17 +- vl.c | 1 + hw/display/Kconfig | 10 + hw/display/Makefile.objs | 3 + 10 files changed, 744 insertions(+), 23 deletions(-) create mode 100644 include/hw/virtio/virtio-gpu-pci.h create mode 100644 hw/display/vhost-user-gpu-pci.c create mode 100644 hw/display/vhost-user-gpu.c create mode 100644 hw/display/vhost-user-vga.c diff --git a/hw/display/virtio-vga.h b/hw/display/virtio-vga.h index f03e1ba619..c10bf390aa 100644 --- a/hw/display/virtio-vga.h +++ b/hw/display/virtio-vga.h @@ -1,7 +1,7 @@ #ifndef VIRTIO_VGA_H_ #define VIRTIO_VGA_H_ =20 -#include "hw/virtio/virtio-pci.h" +#include "hw/virtio/virtio-gpu-pci.h" #include "vga_int.h" =20 /* diff --git a/include/hw/virtio/virtio-gpu-pci.h b/include/hw/virtio/virtio-= gpu-pci.h new file mode 100644 index 0000000000..2f69b5a9cc --- /dev/null +++ b/include/hw/virtio/virtio-gpu-pci.h @@ -0,0 +1,40 @@ +/* + * Virtio GPU PCI Device + * + * Copyright Red Hat, Inc. 2013-2014 + * + * Authors: + * Dave Airlie + * Gerd Hoffmann + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_VIRTIO_GPU_PCI_H +#define HW_VIRTIO_GPU_PCI_H + +#include "hw/virtio/virtio-pci.h" +#include "hw/virtio/virtio-gpu.h" + +typedef struct VirtIOGPUPCIBase VirtIOGPUPCIBase; + +/* + * virtio-gpu-pci-base: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_GPU_PCI_BASE "virtio-gpu-pci-base" +#define VIRTIO_GPU_PCI_BASE(obj) \ + OBJECT_CHECK(VirtIOGPUPCIBase, (obj), TYPE_VIRTIO_GPU_PCI_BASE) + +struct VirtIOGPUPCIBase { + VirtIOPCIProxy parent_obj; + VirtIOGPUBase *vgpu; +}; + +/* to share between PCI and VGA */ +#define DEFINE_VIRTIO_GPU_PCI_PROPERTIES(_state) \ + DEFINE_PROP_BIT("ioeventfd", _state, flags, \ + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false), \ + DEFINE_PROP_UINT32("vectors", _state, nvectors, 3) + +#endif /* HW_VIRTIO_GPU_PCI_H */ diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index d0d8d7b246..8ecac1987a 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -19,6 +19,7 @@ #include "ui/console.h" #include "hw/virtio/virtio.h" #include "qemu/log.h" +#include "sysemu/vhost-user-backend.h" =20 #include "standard-headers/linux/virtio_gpu.h" =20 @@ -34,6 +35,8 @@ #define VIRTIO_GPU(obj) \ OBJECT_CHECK(VirtIOGPU, (obj), TYPE_VIRTIO_GPU) =20 +#define TYPE_VHOST_USER_GPU "vhost-user-gpu" + #define VIRTIO_ID_GPU 16 =20 struct virtio_gpu_simple_resource { @@ -157,13 +160,17 @@ typedef struct VirtIOGPU { } stats; } VirtIOGPU; =20 -extern const GraphicHwOps virtio_gpu_ops; +typedef struct VhostUserGPU { + VirtIOGPUBase parent_obj; =20 -/* to share between PCI and VGA */ -#define DEFINE_VIRTIO_GPU_PCI_PROPERTIES(_state) \ - DEFINE_PROP_BIT("ioeventfd", _state, flags, \ - VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false), \ - DEFINE_PROP_UINT32("vectors", _state, nvectors, 3) + VhostUserBackend *vhost; + int vhost_gpu_fd; /* closed by the chardev */ + CharBackend vhost_chr; + QemuDmaBuf dmabuf[VIRTIO_GPU_MAX_SCANOUTS]; + bool backend_blocked; +} VhostUserGPU; + +extern const GraphicHwOps virtio_gpu_ops; =20 #define VIRTIO_GPU_FILL_CMD(out) do { \ size_t s; \ diff --git a/hw/display/vhost-user-gpu-pci.c b/hw/display/vhost-user-gpu-pc= i.c new file mode 100644 index 0000000000..7d9b1f5a8c --- /dev/null +++ b/hw/display/vhost-user-gpu-pci.c @@ -0,0 +1,51 @@ +/* + * vhost-user GPU PCI device + * + * Copyright Red Hat, Inc. 2018 + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/virtio/virtio-gpu-pci.h" + +#define TYPE_VHOST_USER_GPU_PCI "vhost-user-gpu-pci" +#define VHOST_USER_GPU_PCI(obj) \ + OBJECT_CHECK(VhostUserGPUPCI, (obj), TYPE_VHOST_USER_GPU_PCI) + +typedef struct VhostUserGPUPCI { + VirtIOGPUPCIBase parent_obj; + + VhostUserGPU vdev; +} VhostUserGPUPCI; + +static void vhost_user_gpu_pci_initfn(Object *obj) +{ + VhostUserGPUPCI *dev =3D VHOST_USER_GPU_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_USER_GPU); + + VIRTIO_GPU_PCI_BASE(obj)->vgpu =3D VIRTIO_GPU_BASE(&dev->vdev); + + object_property_add_alias(obj, "chardev", + OBJECT(&dev->vdev), "chardev", + &error_abort); +} + +static const VirtioPCIDeviceTypeInfo vhost_user_gpu_pci_info =3D { + .generic_name =3D TYPE_VHOST_USER_GPU_PCI, + .parent =3D TYPE_VIRTIO_GPU_PCI_BASE, + .instance_size =3D sizeof(VhostUserGPUPCI), + .instance_init =3D vhost_user_gpu_pci_initfn, +}; + +static void vhost_user_gpu_pci_register_types(void) +{ + virtio_pci_types_register(&vhost_user_gpu_pci_info); +} + +type_init(vhost_user_gpu_pci_register_types) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c new file mode 100644 index 0000000000..8f21967061 --- /dev/null +++ b/hw/display/vhost-user-gpu.c @@ -0,0 +1,572 @@ +/* + * vhost-user GPU Device + * + * Copyright Red Hat, Inc. 2018 + * + * Authors: + * Marc-Andr=C3=A9 Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/virtio/virtio-gpu.h" +#include "chardev/char-fe.h" +#include "qapi/error.h" +#include "migration/blocker.h" + +#define VHOST_USER_GPU(obj) \ + OBJECT_CHECK(VhostUserGPU, (obj), TYPE_VHOST_USER_GPU) + +typedef enum VhostUserGpuRequest { + VHOST_USER_GPU_NONE =3D 0, + VHOST_USER_GPU_GET_PROTOCOL_FEATURES, + VHOST_USER_GPU_SET_PROTOCOL_FEATURES, + VHOST_USER_GPU_GET_DISPLAY_INFO, + VHOST_USER_GPU_CURSOR_POS, + VHOST_USER_GPU_CURSOR_POS_HIDE, + VHOST_USER_GPU_CURSOR_UPDATE, + VHOST_USER_GPU_SCANOUT, + VHOST_USER_GPU_UPDATE, + VHOST_USER_GPU_DMABUF_SCANOUT, + VHOST_USER_GPU_DMABUF_UPDATE, +} VhostUserGpuRequest; + +typedef struct VhostUserGpuDisplayInfoReply { + struct virtio_gpu_resp_display_info info; +} VhostUserGpuDisplayInfoReply; + +typedef struct VhostUserGpuCursorPos { + uint32_t scanout_id; + uint32_t x; + uint32_t y; +} QEMU_PACKED VhostUserGpuCursorPos; + +typedef struct VhostUserGpuCursorUpdate { + VhostUserGpuCursorPos pos; + uint32_t hot_x; + uint32_t hot_y; + uint32_t data[64 * 64]; +} QEMU_PACKED VhostUserGpuCursorUpdate; + +typedef struct VhostUserGpuScanout { + uint32_t scanout_id; + uint32_t width; + uint32_t height; +} QEMU_PACKED VhostUserGpuScanout; + +typedef struct VhostUserGpuUpdate { + uint32_t scanout_id; + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; + uint8_t data[]; +} QEMU_PACKED VhostUserGpuUpdate; + +typedef struct VhostUserGpuDMABUFScanout { + uint32_t scanout_id; + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; + uint32_t fd_width; + uint32_t fd_height; + uint32_t fd_stride; + uint32_t fd_flags; + int fd_drm_fourcc; +} QEMU_PACKED VhostUserGpuDMABUFScanout; + +typedef struct VhostUserGpuMsg { + uint32_t request; /* VhostUserGpuRequest */ + uint32_t size; /* the following payload size */ + union { + VhostUserGpuCursorPos cursor_pos; + VhostUserGpuCursorUpdate cursor_update; + VhostUserGpuScanout scanout; + VhostUserGpuUpdate update; + VhostUserGpuDMABUFScanout dmabuf_scanout; + uint64_t u64; + } payload; +} QEMU_PACKED VhostUserGpuMsg; + +static VhostUserGpuMsg m __attribute__ ((unused)); +#define VHOST_USER_GPU_HDR_SIZE (sizeof(m.request) + sizeof(m.size)) + +static void vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked); + +static void +vhost_user_gpu_handle_cursor(VhostUserGPU *g, VhostUserGpuMsg *msg) +{ + VhostUserGpuCursorPos *pos =3D &msg->payload.cursor_pos; + struct virtio_gpu_scanout *s; + + if (pos->scanout_id >=3D g->parent_obj.conf.max_outputs) { + return; + } + s =3D &g->parent_obj.scanout[pos->scanout_id]; + + if (msg->request =3D=3D VHOST_USER_GPU_CURSOR_UPDATE) { + VhostUserGpuCursorUpdate *up =3D &msg->payload.cursor_update; + if (!s->current_cursor) { + s->current_cursor =3D cursor_alloc(64, 64); + } + + s->current_cursor->hot_x =3D up->hot_x; + s->current_cursor->hot_y =3D up->hot_y; + + memcpy(s->current_cursor->data, up->data, + 64 * 64 * sizeof(uint32_t)); + + dpy_cursor_define(s->con, s->current_cursor); + } + + dpy_mouse_set(s->con, pos->x, pos->y, + msg->request !=3D VHOST_USER_GPU_CURSOR_POS_HIDE); +} + +static void +vhost_user_gpu_unblock(VhostUserGPU *g) +{ + uint32_t ok; + + qemu_chr_fe_write(&g->vhost_chr, (uint8_t *)&ok, sizeof(ok)); + +} +static void +vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) +{ + QemuConsole *con =3D NULL; + struct virtio_gpu_scanout *s; + + switch (msg->request) { + case VHOST_USER_GPU_GET_PROTOCOL_FEATURES: { + uint64_t u64 =3D 0; + qemu_chr_fe_write(&g->vhost_chr, (uint8_t *)&u64, sizeof(u64)); + break; + } + case VHOST_USER_GPU_SET_PROTOCOL_FEATURES: { + break; + } + case VHOST_USER_GPU_GET_DISPLAY_INFO: { + struct virtio_gpu_resp_display_info display_info =3D { {} }; + display_info.hdr.type =3D VIRTIO_GPU_RESP_OK_DISPLAY_INFO; + virtio_gpu_base_fill_display_info(VIRTIO_GPU_BASE(g), &display_inf= o); + qemu_chr_fe_write(&g->vhost_chr, + (uint8_t *)&display_info, sizeof(display_info)); + break; + } + case VHOST_USER_GPU_SCANOUT: { + VhostUserGpuScanout *m =3D &msg->payload.scanout; + + if (m->scanout_id >=3D g->parent_obj.conf.max_outputs) { + return; + } + + g->parent_obj.enable =3D 1; + s =3D &g->parent_obj.scanout[m->scanout_id]; + con =3D s->con; + + if (m->scanout_id =3D=3D 0 && m->width =3D=3D 0) { + s->ds =3D qemu_create_message_surface(640, 480, + "Guest disabled display."); + dpy_gfx_replace_surface(con, s->ds); + } else { + s->ds =3D qemu_create_displaysurface(m->width, m->height); + /* replace surface on next update */ + } + + break; + } + case VHOST_USER_GPU_DMABUF_SCANOUT: { + VhostUserGpuDMABUFScanout *m =3D &msg->payload.dmabuf_scanout; + int fd =3D qemu_chr_fe_get_msgfd(&g->vhost_chr); + QemuDmaBuf *dmabuf; + + if (m->scanout_id >=3D g->parent_obj.conf.max_outputs) { + error_report("invalid scanout: %d", m->scanout_id); + if (fd >=3D 0) { + close(fd); + } + break; + } + + g->parent_obj.enable =3D 1; + con =3D g->parent_obj.scanout[m->scanout_id].con; + dmabuf =3D &g->dmabuf[m->scanout_id]; + if (dmabuf->fd >=3D 0) { + close(dmabuf->fd); + dmabuf->fd =3D -1; + } + if (!console_has_gl_dmabuf(con)) { + /* it would be nice to report that error earlier */ + error_report("console doesn't support dmabuf!"); + break; + } + dpy_gl_release_dmabuf(con, dmabuf); + if (fd =3D=3D -1) { + dpy_gl_scanout_disable(con); + break; + } + *dmabuf =3D (QemuDmaBuf) { + .fd =3D fd, + .width =3D m->fd_width, + .height =3D m->fd_height, + .stride =3D m->fd_stride, + .fourcc =3D m->fd_drm_fourcc, + .y0_top =3D m->fd_flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP, + }; + dpy_gl_scanout_dmabuf(con, dmabuf); + break; + } + case VHOST_USER_GPU_DMABUF_UPDATE: { + VhostUserGpuUpdate *m =3D &msg->payload.update; + + if (m->scanout_id >=3D g->parent_obj.conf.max_outputs || + !g->parent_obj.scanout[m->scanout_id].con) { + error_report("invalid scanout update: %d", m->scanout_id); + vhost_user_gpu_unblock(g); + break; + } + + con =3D g->parent_obj.scanout[m->scanout_id].con; + if (!console_has_gl(con)) { + error_report("console doesn't support GL!"); + vhost_user_gpu_unblock(g); + break; + } + dpy_gl_update(con, m->x, m->y, m->width, m->height); + g->backend_blocked =3D true; + break; + } + case VHOST_USER_GPU_UPDATE: { + VhostUserGpuUpdate *m =3D &msg->payload.update; + + if (m->scanout_id >=3D g->parent_obj.conf.max_outputs) { + break; + } + s =3D &g->parent_obj.scanout[m->scanout_id]; + con =3D s->con; + pixman_image_t *image =3D + pixman_image_create_bits(PIXMAN_x8r8g8b8, + m->width, + m->height, + (uint32_t *)m->data, + m->width * 4); + + pixman_image_composite(PIXMAN_OP_SRC, + image, NULL, s->ds->image, + 0, 0, 0, 0, m->x, m->y, m->width, m->height= ); + + pixman_image_unref(image); + if (qemu_console_surface(con) !=3D s->ds) { + dpy_gfx_replace_surface(con, s->ds); + } else { + dpy_gfx_update(con, m->x, m->y, m->width, m->height); + } + break; + } + default: + g_warning("unhandled message %d %d", msg->request, msg->size); + } + + if (con && qemu_console_is_gl_blocked(con)) { + vhost_user_gpu_update_blocked(g, true); + } +} + +static void +vhost_user_gpu_chr_read(void *opaque) +{ + VhostUserGPU *g =3D opaque; + VhostUserGpuMsg *msg =3D NULL; + VhostUserGpuRequest request; + uint32_t size; + int r; + + r =3D qemu_chr_fe_read_all(&g->vhost_chr, + (uint8_t *)&request, sizeof(uint32_t)); + if (r !=3D sizeof(uint32_t)) { + error_report("failed to read msg header: %d, %d", r, errno); + goto end; + } + + r =3D qemu_chr_fe_read_all(&g->vhost_chr, + (uint8_t *)&size, sizeof(uint32_t)); + if (r !=3D sizeof(uint32_t)) { + error_report("failed to read msg size"); + goto end; + } + + msg =3D g_malloc(VHOST_USER_GPU_HDR_SIZE + size); + g_return_if_fail(msg !=3D NULL); + + r =3D qemu_chr_fe_read_all(&g->vhost_chr, + (uint8_t *)&msg->payload, size); + if (r !=3D size) { + error_report("failed to read msg payload %d !=3D %d", r, size); + goto end; + } + + msg->request =3D request; + msg->size =3D size; + + if (request =3D=3D VHOST_USER_GPU_CURSOR_UPDATE || + request =3D=3D VHOST_USER_GPU_CURSOR_POS || + request =3D=3D VHOST_USER_GPU_CURSOR_POS_HIDE) { + vhost_user_gpu_handle_cursor(g, msg); + } else { + vhost_user_gpu_handle_display(g, msg); + } + +end: + g_free(msg); +} + +static void +vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked) +{ + qemu_set_fd_handler(g->vhost_gpu_fd, + blocked ? NULL : vhost_user_gpu_chr_read, NULL, g); +} + +static void +vhost_user_gpu_gl_unblock(VirtIOGPUBase *b) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(b); + + if (g->backend_blocked) { + vhost_user_gpu_unblock(VHOST_USER_GPU(g)); + g->backend_blocked =3D false; + } + + vhost_user_gpu_update_blocked(VHOST_USER_GPU(g), false); +} + +static bool +vhost_user_gpu_do_set_socket(VhostUserGPU *g, Error **errp) +{ + Chardev *chr; + int sv[2]; + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) =3D=3D -1) { + error_setg_errno(errp, errno, "socketpair() failed"); + return false; + } + + chr =3D CHARDEV(object_new(TYPE_CHARDEV_SOCKET)); + if (!chr || qemu_chr_add_client(chr, sv[0]) =3D=3D -1) { + error_setg(errp, "Failed to make socket chardev"); + goto err; + } + if (!qemu_chr_fe_init(&g->vhost_chr, chr, errp)) { + goto err; + } + if (vhost_user_gpu_set_socket(&g->vhost->dev, sv[1]) < 0) { + error_setg(errp, "Failed to set vhost-user-gpu socket"); + qemu_chr_fe_deinit(&g->vhost_chr, false); + goto err; + } + + g->vhost_gpu_fd =3D sv[0]; + vhost_user_gpu_update_blocked(g, false); + close(sv[1]); + return true; + +err: + close(sv[0]); + close(sv[1]); + if (chr) { + object_unref(OBJECT(chr)); + } + return false; +} + +static void +vhost_user_gpu_get_config(VirtIODevice *vdev, uint8_t *config_data) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(vdev); + VirtIOGPUBase *b =3D VIRTIO_GPU_BASE(vdev); + struct virtio_gpu_config *vgconfig =3D + (struct virtio_gpu_config *)config_data; + int ret; + + memset(config_data, 0, sizeof(struct virtio_gpu_config)); + + ret =3D vhost_dev_get_config(&g->vhost->dev, + config_data, sizeof(struct virtio_gpu_confi= g)); + if (ret) { + error_report("vhost-user-gpu: get device config space failed"); + return; + } + + /* those fields are managed by qemu */ + vgconfig->num_scanouts =3D b->virtio_config.num_scanouts; + vgconfig->events_read =3D b->virtio_config.events_read; + vgconfig->events_clear =3D b->virtio_config.events_clear; +} + +static void +vhost_user_gpu_set_config(VirtIODevice *vdev, + const uint8_t *config_data) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(vdev); + VirtIOGPUBase *b =3D VIRTIO_GPU_BASE(vdev); + const struct virtio_gpu_config *vgconfig =3D + (const struct virtio_gpu_config *)config_data; + int ret; + + if (vgconfig->events_clear) { + b->virtio_config.events_read &=3D ~vgconfig->events_clear; + } + + ret =3D vhost_dev_set_config(&g->vhost->dev, config_data, + 0, sizeof(struct virtio_gpu_config), + VHOST_SET_CONFIG_TYPE_MASTER); + if (ret) { + error_report("vhost-user-input: set device config space failed"); + return; + } +} + +static void +vhost_user_gpu_set_status(VirtIODevice *vdev, uint8_t val) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(vdev); + Error *err =3D NULL; + + if (val & VIRTIO_CONFIG_S_DRIVER_OK && vdev->vm_running) { + if (!vhost_user_gpu_do_set_socket(g, &err)) { + error_report_err(err); + return; + } + vhost_user_backend_start(g->vhost); + } else { + /* unblock any wait and stop processing */ + if (g->vhost_gpu_fd !=3D -1) { + vhost_user_gpu_update_blocked(g, true); + qemu_chr_fe_deinit(&g->vhost_chr, true); + g->vhost_gpu_fd =3D -1; + } + vhost_user_backend_stop(g->vhost); + } +} + +static bool +vhost_user_gpu_guest_notifier_pending(VirtIODevice *vdev, int idx) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(vdev); + + return vhost_virtqueue_pending(&g->vhost->dev, idx); +} + +static void +vhost_user_gpu_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(vdev); + + vhost_virtqueue_mask(&g->vhost->dev, vdev, idx, mask); +} + +static void +vhost_user_gpu_instance_init(Object *obj) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(obj); + + g->vhost =3D VHOST_USER_BACKEND(object_new(TYPE_VHOST_USER_BACKEND)); + object_property_add_alias(obj, "chardev", + OBJECT(g->vhost), "chardev", &error_abort); +} + +static void +vhost_user_gpu_instance_finalize(Object *obj) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(obj); + + object_unref(OBJECT(g->vhost)); +} + +static void +vhost_user_gpu_reset(VirtIODevice *vdev) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(vdev); + + virtio_gpu_base_reset(VIRTIO_GPU_BASE(vdev)); + + vhost_user_backend_stop(g->vhost); +} + +static int +vhost_user_gpu_config_change(struct vhost_dev *dev) +{ + error_report("vhost-user-gpu: unhandled backend config change"); + return -1; +} + +static const VhostDevConfigOps config_ops =3D { + .vhost_dev_config_notifier =3D vhost_user_gpu_config_change, +}; + +static void +vhost_user_gpu_device_realize(DeviceState *qdev, Error **errp) +{ + VhostUserGPU *g =3D VHOST_USER_GPU(qdev); + VirtIODevice *vdev =3D VIRTIO_DEVICE(g); + + vhost_dev_set_config_notifier(&g->vhost->dev, &config_ops); + if (vhost_user_backend_dev_init(g->vhost, vdev, 2, errp) < 0) { + return; + } + + if (virtio_has_feature(g->vhost->dev.features, VIRTIO_GPU_F_VIRGL)) { + g->parent_obj.conf.flags |=3D 1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED; + } + + if (!virtio_gpu_base_device_realize(qdev, NULL, NULL, errp)) { + return; + } + + g->vhost_gpu_fd =3D -1; +} + +static Property vhost_user_gpu_properties[] =3D { + VIRTIO_GPU_BASE_PROPERTIES(VhostUserGPU, parent_obj.conf), + DEFINE_PROP_END_OF_LIST(), +}; + +static void +vhost_user_gpu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + VirtioDeviceClass *vdc =3D VIRTIO_DEVICE_CLASS(klass); + VirtIOGPUBaseClass *vgc =3D VIRTIO_GPU_BASE_CLASS(klass); + + vgc->gl_unblock =3D vhost_user_gpu_gl_unblock; + + vdc->realize =3D vhost_user_gpu_device_realize; + vdc->reset =3D vhost_user_gpu_reset; + vdc->set_status =3D vhost_user_gpu_set_status; + vdc->guest_notifier_mask =3D vhost_user_gpu_guest_notifier_mask; + vdc->guest_notifier_pending =3D vhost_user_gpu_guest_notifier_pending; + vdc->get_config =3D vhost_user_gpu_get_config; + vdc->set_config =3D vhost_user_gpu_set_config; + + dc->props =3D vhost_user_gpu_properties; +} + +static const TypeInfo vhost_user_gpu_info =3D { + .name =3D TYPE_VHOST_USER_GPU, + .parent =3D TYPE_VIRTIO_GPU_BASE, + .instance_size =3D sizeof(VhostUserGPU), + .instance_init =3D vhost_user_gpu_instance_init, + .instance_finalize =3D vhost_user_gpu_instance_finalize, + .class_init =3D vhost_user_gpu_class_init, +}; + +static void vhost_user_gpu_register_types(void) +{ + type_register_static(&vhost_user_gpu_info); +} + +type_init(vhost_user_gpu_register_types) diff --git a/hw/display/vhost-user-vga.c b/hw/display/vhost-user-vga.c new file mode 100644 index 0000000000..a7195276d9 --- /dev/null +++ b/hw/display/vhost-user-vga.c @@ -0,0 +1,52 @@ +/* + * vhost-user VGA device + * + * Copyright Red Hat, Inc. 2018 + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "virtio-vga.h" + +#define TYPE_VHOST_USER_VGA "vhost-user-vga" + +#define VHOST_USER_VGA(obj) \ + OBJECT_CHECK(VhostUserVGA, (obj), TYPE_VHOST_USER_VGA) + +typedef struct VhostUserVGA { + VirtIOVGABase parent_obj; + + VhostUserGPU vdev; +} VhostUserVGA; + +static void vhost_user_vga_inst_initfn(Object *obj) +{ + VhostUserVGA *dev =3D VHOST_USER_VGA(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_USER_GPU); + + VIRTIO_VGA_BASE(dev)->vgpu =3D VIRTIO_GPU_BASE(&dev->vdev); + + object_property_add_alias(obj, "chardev", + OBJECT(&dev->vdev), "chardev", + &error_abort); +} + +static const VirtioPCIDeviceTypeInfo vhost_user_vga_info =3D { + .generic_name =3D TYPE_VHOST_USER_VGA, + .parent =3D TYPE_VIRTIO_VGA_BASE, + .instance_size =3D sizeof(struct VhostUserVGA), + .instance_init =3D vhost_user_vga_inst_initfn, +}; + +static void vhost_user_vga_register_types(void) +{ + virtio_pci_types_register(&vhost_user_vga_info); +} + +type_init(vhost_user_vga_register_types) diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c index 3b88bf4d1f..ef8d6ecb49 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -16,22 +16,7 @@ #include "hw/pci/pci.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-bus.h" -#include "hw/virtio/virtio-pci.h" -#include "hw/virtio/virtio-gpu.h" - -typedef struct VirtIOGPUPCIBase VirtIOGPUPCIBase; - -/* - * virtio-gpu-pci-base: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_GPU_PCI_BASE "virtio-gpu-pci-base" -#define VIRTIO_GPU_PCI_BASE(obj) \ - OBJECT_CHECK(VirtIOGPUPCIBase, (obj), TYPE_VIRTIO_GPU_PCI_BASE) - -struct VirtIOGPUPCIBase { - VirtIOPCIProxy parent_obj; - VirtIOGPUBase *vgpu; -}; +#include "hw/virtio/virtio-gpu-pci.h" =20 static Property virtio_gpu_pci_base_properties[] =3D { DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), diff --git a/vl.c b/vl.c index c696ad2a13..b06458070f 100644 --- a/vl.c +++ b/vl.c @@ -238,6 +238,7 @@ static struct { { .driver =3D "qxl-vga", .flag =3D &default_vga }, { .driver =3D "virtio-vga", .flag =3D &default_vga }, { .driver =3D "ati-vga", .flag =3D &default_vga }, + { .driver =3D "vhost-user-vga", .flag =3D &default_vga }, }; =20 static QemuOptsList qemu_rtc_opts =3D { diff --git a/hw/display/Kconfig b/hw/display/Kconfig index 72be57a403..492ea35104 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -104,6 +104,16 @@ config VIRTIO_VGA depends on VIRTIO_PCI select VGA =20 +config VHOST_USER_GPU + bool + default y + depends on VIRTIO_GPU && VHOST_USER + +config VHOST_USER_VGA + bool + default y + depends on VIRTIO_VGA && VHOST_USER_GPU + config DPCD bool =20 diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index f1c46b8e3e..9c342eef41 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -43,8 +43,11 @@ obj-$(CONFIG_VGA) +=3D vga.o common-obj-$(CONFIG_QXL) +=3D qxl.o qxl-logger.o qxl-render.o =20 obj-$(CONFIG_VIRTIO_GPU) +=3D virtio-gpu-base.o virtio-gpu.o virtio-gpu-3d= .o +obj-$(CONFIG_VHOST_USER_GPU) +=3D vhost-user-gpu.o obj-$(call land,$(CONFIG_VIRTIO_GPU),$(CONFIG_VIRTIO_PCI)) +=3D virtio-gpu= -pci.o +obj-$(call land,$(CONFIG_VHOST_USER_GPU),$(CONFIG_VIRTIO_PCI)) +=3D vhost-= user-gpu-pci.o obj-$(CONFIG_VIRTIO_VGA) +=3D virtio-vga.o +obj-$(CONFIG_VHOST_USER_VGA) +=3D vhost-user-vga.o virtio-gpu.o-cflags :=3D $(VIRGL_CFLAGS) virtio-gpu.o-libs +=3D $(VIRGL_LIBS) virtio-gpu-3d.o-cflags :=3D $(VIRGL_CFLAGS) --=20 2.21.0.313.ge35b8cb8e2