From nobody Mon Feb 9 16:20:45 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.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 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1527870858175302.8439796183237; Fri, 1 Jun 2018 09:34:18 -0700 (PDT) Received: from localhost ([::1]:56601 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOn0L-0007Z0-3K for importer@patchew.org; Fri, 01 Jun 2018 12:34:17 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39149) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOmuE-00032s-UM for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:27:59 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fOmuE-0007m0-3R for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:27:58 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:52734 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fOmuD-0007lq-Un for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:27:58 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 863D9406F619 for ; Fri, 1 Jun 2018 16:27:57 +0000 (UTC) Received: from localhost (ovpn-112-61.ams2.redhat.com [10.36.112.61]) by smtp.corp.redhat.com (Postfix) with ESMTP id E2D311134CB4; Fri, 1 Jun 2018 16:27:51 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 1 Jun 2018 18:27:38 +0200 Message-Id: <20180601162749.27406-2-marcandre.lureau@redhat.com> In-Reply-To: <20180601162749.27406-1-marcandre.lureau@redhat.com> References: <20180601162749.27406-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.7]); Fri, 01 Jun 2018 16:27:57 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.7]); Fri, 01 Jun 2018 16:27:57 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'marcandre.lureau@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [RFC v2 01/12] chardev: avoid crash if no associated address 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 Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" A socket chardev may not have associated address (when adding client fd manually for example). But on disconnect, updating socket filename expects an address and may lead to this crash: Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault. 0x0000555555d8c70c in SocketAddress_to_str (prefix=3D0x555556043062 "disc= onnected:", addr=3D0x0, is_listen=3Dfalse, is_telnet=3Dfalse) at /home/elma= rco/src/qq/chardev/char-socket.c:388 388 switch (addr->type) { (gdb) bt #0 0x0000555555d8c70c in SocketAddress_to_str (prefix=3D0x555556043062 "= disconnected:", addr=3D0x0, is_listen=3Dfalse, is_telnet=3Dfalse) at /home/= elmarco/src/qq/chardev/char-socket.c:388 #1 0x0000555555d8c8aa in update_disconnected_filename (s=3D0x555556b1ed0= 0) at /home/elmarco/src/qq/chardev/char-socket.c:419 #2 0x0000555555d8c959 in tcp_chr_disconnect (chr=3D0x555556b1ed00) at /h= ome/elmarco/src/qq/chardev/char-socket.c:438 #3 0x0000555555d8cba1 in tcp_chr_hup (channel=3D0x555556b75690, cond=3DG= _IO_HUP, opaque=3D0x555556b1ed00) at /home/elmarco/src/qq/chardev/char-sock= et.c:482 #4 0x0000555555da596e in qio_channel_fd_source_dispatch (source=3D0x5555= 56bb68b0, callback=3D0x555555d8cb58 , user_data=3D0x555556b1ed= 00) at /home/elmarco/src/qq/io/channel-watch.c:84 Signed-off-by: Marc-Andr=C3=A9 Lureau --- chardev/char-socket.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 159e69c3b1..f1b7907798 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -416,8 +416,11 @@ static void update_disconnected_filename(SocketChardev= *s) Chardev *chr =3D CHARDEV(s); =20 g_free(chr->filename); - chr->filename =3D SocketAddress_to_str("disconnected:", s->addr, - s->is_listen, s->is_telnet); + chr->filename =3D NULL; + if (s->addr) { + chr->filename =3D SocketAddress_to_str("disconnected:", s->addr, + s->is_listen, s->is_telnet); + } } =20 /* NB may be called even if tcp_chr_connect has not been --=20 2.17.1.906.g10fd178552 From nobody Mon Feb 9 16:20:45 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.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 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1527871045528357.93620586128304; Fri, 1 Jun 2018 09:37:25 -0700 (PDT) Received: from localhost ([::1]:56620 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOn3I-0001cF-Nr for importer@patchew.org; Fri, 01 Jun 2018 12:37:20 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39163) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOmuG-000344-46 for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:03 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fOmuF-0007mq-Fd for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:00 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:46996 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fOmuF-0007mc-At for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:27:59 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id EA4AFC12AD for ; Fri, 1 Jun 2018 16:27:58 +0000 (UTC) Received: from localhost (ovpn-112-61.ams2.redhat.com [10.36.112.61]) by smtp.corp.redhat.com (Postfix) with ESMTP id 95B6B2166BB2; Fri, 1 Jun 2018 16:27:58 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 1 Jun 2018 18:27:39 +0200 Message-Id: <20180601162749.27406-3-marcandre.lureau@redhat.com> In-Reply-To: <20180601162749.27406-1-marcandre.lureau@redhat.com> References: <20180601162749.27406-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.1]); Fri, 01 Jun 2018 16:27:58 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.1]); Fri, 01 Jun 2018 16:27:58 +0000 (UTC) for IP:'10.11.54.6' DOMAIN:'int-mx06.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'marcandre.lureau@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [RFC v2 02/12] libvhost-user: exit by default on VHOST_USER_NONE 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 Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Since commit 2566378d6d13bf4d28c7770bdbda5f7682594bbe, libvhost-user no longer panics on disconnect (rc =3D=3D 0), and instead silently ignores an invalid VHOST_USER_NONE message. Without extra work from the API user, this will simply busy-loop on HUP events. The obvious thing to do is to exit(0) instead, while additional or different work can be done by overriding iface->process_msg(). Signed-off-by: Marc-Andr=C3=A9 Lureau --- contrib/libvhost-user/libvhost-user.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/= libvhost-user.c index 54e643d871..ebe30c7dab 100644 --- a/contrib/libvhost-user/libvhost-user.c +++ b/contrib/libvhost-user/libvhost-user.c @@ -1220,6 +1220,8 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg) case VHOST_USER_SET_CONFIG: return vu_set_config(dev, vmsg); case VHOST_USER_NONE: + /* if you need processing before exit, override iface->process_msg= */ + exit(0); break; case VHOST_USER_POSTCOPY_ADVISE: return vu_set_postcopy_advise(dev, vmsg); --=20 2.17.1.906.g10fd178552 From nobody Mon Feb 9 16:20:45 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.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 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (208.118.235.17 [208.118.235.17]) by mx.zohomail.com with SMTPS id 1527871225642457.0279758815202; Fri, 1 Jun 2018 09:40:25 -0700 (PDT) Received: from localhost ([::1]:56633 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOn65-0003xi-5b for importer@patchew.org; Fri, 01 Jun 2018 12:40:13 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39179) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOmuJ-00036r-AQ for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:04 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fOmuG-0007nb-So for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:03 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:60312 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fOmuG-0007nK-OP for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:00 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 5A74B401EF23 for ; Fri, 1 Jun 2018 16:28:00 +0000 (UTC) Received: from localhost (ovpn-112-61.ams2.redhat.com [10.36.112.61]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0B19B1134CB4; Fri, 1 Jun 2018 16:27:59 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 1 Jun 2018 18:27:40 +0200 Message-Id: <20180601162749.27406-4-marcandre.lureau@redhat.com> In-Reply-To: <20180601162749.27406-1-marcandre.lureau@redhat.com> References: <20180601162749.27406-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.5]); Fri, 01 Jun 2018 16:28:00 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.5]); Fri, 01 Jun 2018 16:28:00 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'marcandre.lureau@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [RFC v2 03/12] vhost-user: wrap some read/write with retry handling 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 Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Signed-off-by: Marc-Andr=C3=A9 Lureau Reviewed-by: Philippe Mathieu-Daud=C3=A9 --- hw/virtio/vhost-user.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index ca554d4ff1..cc9298792d 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -869,7 +869,10 @@ static void slave_read(void *opaque) iov.iov_base =3D &hdr; iov.iov_len =3D VHOST_USER_HDR_SIZE; =20 - size =3D recvmsg(u->slave_fd, &msgh, 0); + do { + size =3D recvmsg(u->slave_fd, &msgh, 0); + } while (size < 0 && (errno =3D=3D EINTR || errno =3D=3D EAGAIN)); + if (size !=3D VHOST_USER_HDR_SIZE) { error_report("Failed to read from slave."); goto err; @@ -898,7 +901,10 @@ static void slave_read(void *opaque) } =20 /* Read payload */ - size =3D read(u->slave_fd, &payload, hdr.size); + do { + size =3D read(u->slave_fd, &payload, hdr.size); + } while (size < 0 && (errno =3D=3D EINTR || errno =3D=3D EAGAIN)); + if (size !=3D hdr.size) { error_report("Failed to read payload from slave."); goto err; @@ -941,7 +947,10 @@ static void slave_read(void *opaque) iovec[1].iov_base =3D &payload; iovec[1].iov_len =3D hdr.size; =20 - size =3D writev(u->slave_fd, iovec, ARRAY_SIZE(iovec)); + do { + size =3D writev(u->slave_fd, iovec, ARRAY_SIZE(iovec)); + } while (size < 0 && (errno =3D=3D EINTR || errno =3D=3D EAGAIN)); + if (size !=3D VHOST_USER_HDR_SIZE + hdr.size) { error_report("Failed to send msg reply to slave."); goto err; --=20 2.17.1.906.g10fd178552 From nobody Mon Feb 9 16:20:45 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.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 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1527873038146993.4762168510584; Fri, 1 Jun 2018 10:10:38 -0700 (PDT) Received: from localhost ([::1]:56840 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOnZV-0002KT-Au for importer@patchew.org; Fri, 01 Jun 2018 13:10:37 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39203) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOmuP-0003CY-Cr for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:12 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fOmuN-0007pc-Jb for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:09 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:47000 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fOmuN-0007pU-EC for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:07 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 0BFD4C12AD for ; Fri, 1 Jun 2018 16:28:07 +0000 (UTC) Received: from localhost (ovpn-112-61.ams2.redhat.com [10.36.112.61]) by smtp.corp.redhat.com (Postfix) with ESMTP id 6BC3663F72; Fri, 1 Jun 2018 16:28:01 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 1 Jun 2018 18:27:41 +0200 Message-Id: <20180601162749.27406-5-marcandre.lureau@redhat.com> In-Reply-To: <20180601162749.27406-1-marcandre.lureau@redhat.com> References: <20180601162749.27406-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.1]); Fri, 01 Jun 2018 16:28:07 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.1]); Fri, 01 Jun 2018 16:28:07 +0000 (UTC) for IP:'10.11.54.5' DOMAIN:'int-mx05.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'marcandre.lureau@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [RFC v2 04/12] 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 Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Create a vhost-user-backend object that holds a connection to a vhost-user backend and can be referenced from virtio devices that support it. See later patches for input & gpu usage. A chardev can be specified to communicate with the vhost-user backend, ex: -chardev socket,id=3Dchar0,path=3D/tmp/foo.sock -object vhost-user-backend,id=3Dvuid,chardev=3Dchar0. Alternatively, an executable with its arguments may be given as 'cmd' property, ex: -object vhost-user-backend,id=3Dvui,cmd=3D"./vhost-user-input /dev/input..". The executable is then spawn and, by convention, the vhost-user socket is passed as fd=3D3. It may be considered a security breach to allow creating processes that may execute arbitrary executables, so this may be restricted to some known executables (via signature etc) or directory. Signed-off-by: Marc-Andr=C3=A9 Lureau --- include/sysemu/vhost-user-backend.h | 58 +++++ backends/vhost-user.c | 330 ++++++++++++++++++++++++++++ vl.c | 3 +- backends/Makefile.objs | 4 + 4 files changed, 394 insertions(+), 1 deletion(-) 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..890db176fd --- /dev/null +++ b/include/sysemu/vhost-user-backend.h @@ -0,0 +1,58 @@ +/* + * 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 "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 *cmd; + char *chr_name; + + CharBackend chr; + struct vhost_dev dev; + QIOChannel *child; + 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..ae40dee373 --- /dev/null +++ b/backends/vhost-user.c @@ -0,0 +1,330 @@ +/* + * 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; + } + + 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->chr, VHOST_BACKEND_TYPE_USER, 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 int +vhost_user_backend_spawn_cmd(VhostUserBackend *b, int vhostfd, Error **err= p) +{ + int devnull =3D open("/dev/null", O_RDWR); + pid_t pid; + + assert(!b->child); + + if (!b->cmd) { + error_setg_errno(errp, errno, "Missing cmd property"); + return -1; + } + if (devnull < 0) { + error_setg_errno(errp, errno, "Unable to open /dev/null"); + return -1; + } + + pid =3D qemu_fork(errp); + if (pid < 0) { + close(devnull); + return -1; + } + + if (pid =3D=3D 0) { /* child */ + int fd, maxfd =3D sysconf(_SC_OPEN_MAX); + + dup2(devnull, STDIN_FILENO); + dup2(devnull, STDOUT_FILENO); + dup2(vhostfd, 3); + + signal(SIGINT, SIG_IGN); + + for (fd =3D 4; fd < maxfd; fd++) { + close(fd); + } + + execlp("/bin/sh", "sh", "-c", b->cmd, NULL); + _exit(1); + } + + b->child =3D QIO_CHANNEL(qio_channel_command_new_pid(devnull, devnull,= pid)); + + return 0; +} + +static void +vhost_user_backend_complete(UserCreatable *uc, Error **errp) +{ + VhostUserBackend *b =3D VHOST_USER_BACKEND(uc); + Chardev *chr; + + if (!!b->chr_name + !!b->cmd !=3D 1) { + error_setg(errp, "You may specificy only one of 'chardev' or 'cmd'= ."); + return; + } + + if (b->chr_name) { + 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; + } + } else { + int sv[2]; + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) =3D=3D -1) { + error_setg_errno(errp, errno, "socketpair() failed"); + return; + } + + 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"); + object_unref(OBJECT(chr)); + return; + } + + if (!qemu_chr_fe_init(&b->chr, chr, errp)) { + return; + } + + vhost_user_backend_spawn_cmd(b, sv[1], errp); + + close(sv[1]); + } + + b->completed =3D true; + /* could vhost_dev_init() happen here, so early vhost-user message + * can be exchanged */ +} + +static char *get_cmd(Object *obj, Error **errp) +{ + VhostUserBackend *b =3D VHOST_USER_BACKEND(obj); + + return g_strdup(b->cmd); +} + +static void set_cmd(Object *obj, const char *str, Error **errp) +{ + VhostUserBackend *b =3D VHOST_USER_BACKEND(obj); + + if (b->child) { + error_setg(errp, "cannot change property value"); + return; + } + + g_free(b->cmd); + b->cmd =3D g_strdup(str); +} + +static void set_chardev(Object *obj, const char *value, Error **errp) +{ + VhostUserBackend *b =3D VHOST_USER_BACKEND(obj); + + if (b->completed) { + error_setg(errp, QERR_PERMISSION_DENIED); + } else { + g_free(b->chr_name); + b->chr_name =3D g_strdup(value); + } +} + +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, "cmd", get_cmd, set_cmd, NULL); + 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->cmd); + g_free(b->chr_name); + + qemu_chr_fe_deinit(&b->chr, true); + + if (b->child) { + object_unref(OBJECT(b->child)); + } +} + +static bool +vhost_user_backend_can_be_deleted(UserCreatable *uc) +{ + return true; +} + +static void +vhost_user_backend_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc =3D USER_CREATABLE_CLASS(oc); + + ucc->complete =3D vhost_user_backend_complete; + ucc->can_be_deleted =3D vhost_user_backend_can_be_deleted; +} + +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), + .class_init =3D vhost_user_backend_class_init, + .interfaces =3D (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static void register_types(void) +{ + type_register_static(&vhost_user_backend_info); +} + +type_init(register_types); diff --git a/vl.c b/vl.c index c4fe25560c..4ed094dd71 100644 --- a/vl.c +++ b/vl.c @@ -2833,7 +2833,8 @@ static bool object_create_initial(const char *type) } =20 #if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX) - if (g_str_equal(type, "cryptodev-vhost-user")) { + if (g_str_equal(type, "cryptodev-vhost-user") || + g_str_equal(type, "vhost-user-backend")) { return false; } #endif diff --git a/backends/Makefile.objs b/backends/Makefile.objs index ad7c0325ed..bac27de11b 100644 --- a/backends/Makefile.objs +++ b/backends/Makefile.objs @@ -16,3 +16,7 @@ common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINU= X)) +=3D \ endif =20 common-obj-$(CONFIG_LINUX) +=3D hostmem-memfd.o + +ifdef CONFIG_LINUX +common-obj-$(CONFIG_VIRTIO) +=3D vhost-user.o +endif --=20 2.17.1.906.g10fd178552 From nobody Mon Feb 9 16:20:45 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.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 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1527871826982511.16679217164017; Fri, 1 Jun 2018 09:50:26 -0700 (PDT) Received: from localhost ([::1]:56692 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOnFy-0003ly-8v for importer@patchew.org; Fri, 01 Jun 2018 12:50:26 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39220) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOmuS-0003Ev-6S for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:13 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fOmuO-0007q6-VT for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:12 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:60156 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fOmuO-0007pt-QO for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:08 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 68A11818BAEE for ; Fri, 1 Jun 2018 16:28:08 +0000 (UTC) Received: from localhost (ovpn-112-61.ams2.redhat.com [10.36.112.61]) by smtp.corp.redhat.com (Postfix) with ESMTP id 19D5320244E0; Fri, 1 Jun 2018 16:28:07 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 1 Jun 2018 18:27:42 +0200 Message-Id: <20180601162749.27406-6-marcandre.lureau@redhat.com> In-Reply-To: <20180601162749.27406-1-marcandre.lureau@redhat.com> References: <20180601162749.27406-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.4 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Fri, 01 Jun 2018 16:28:08 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Fri, 01 Jun 2018 16:28:08 +0000 (UTC) for IP:'10.11.54.4' DOMAIN:'int-mx04.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'marcandre.lureau@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [RFC v2 05/12] vhost-user: split vhost_user_read() 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 Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Split vhost_user_read(), so only header can be read with vhost_user_read_header(). Signed-off-by: Marc-Andr=C3=A9 Lureau --- hw/virtio/vhost-user.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index cc9298792d..a87db01e55 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -196,7 +196,7 @@ static bool ioeventfd_enabled(void) return kvm_enabled() && kvm_eventfds_enabled(); } =20 -static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) +static int vhost_user_read_header(struct vhost_dev *dev, VhostUserMsg *msg) { struct vhost_user *u =3D dev->opaque; CharBackend *chr =3D u->chr; @@ -207,7 +207,7 @@ static int vhost_user_read(struct vhost_dev *dev, Vhost= UserMsg *msg) if (r !=3D size) { error_report("Failed to read msg header. Read %d instead of %d." " Original request %d.", r, size, msg->hdr.request); - goto fail; + return -1; } =20 /* validate received flags */ @@ -215,7 +215,21 @@ static int vhost_user_read(struct vhost_dev *dev, Vhos= tUserMsg *msg) error_report("Failed to read msg header." " Flags 0x%x instead of 0x%x.", msg->hdr.flags, VHOST_USER_REPLY_MASK | VHOST_USER_VERSION); - goto fail; + return -1; + } + + return 0; +} + +static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) +{ + struct vhost_user *u =3D dev->opaque; + CharBackend *chr =3D u->chr; + uint8_t *p =3D (uint8_t *) msg; + int r, size; + + if (vhost_user_read_header(dev, msg) < 0) { + return -1; } =20 /* validate message size is sane */ @@ -223,7 +237,7 @@ static int vhost_user_read(struct vhost_dev *dev, Vhost= UserMsg *msg) error_report("Failed to read msg header." " Size %d exceeds the maximum %zu.", msg->hdr.size, VHOST_USER_PAYLOAD_SIZE); - goto fail; + return -1; } =20 if (msg->hdr.size) { @@ -233,14 +247,11 @@ static int vhost_user_read(struct vhost_dev *dev, Vho= stUserMsg *msg) if (r !=3D size) { error_report("Failed to read msg payload." " Read %d instead of %d.", r, msg->hdr.size); - goto fail; + return -1; } } =20 return 0; - -fail: - return -1; } =20 static int process_message_reply(struct vhost_dev *dev, --=20 2.17.1.906.g10fd178552 From nobody Mon Feb 9 16:20:45 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.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 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1527871384883249.09354920824967; Fri, 1 Jun 2018 09:43:04 -0700 (PDT) Received: from localhost ([::1]:56652 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOn8p-0006QK-Sl for importer@patchew.org; Fri, 01 Jun 2018 12:43:03 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39219) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOmuS-0003Eu-6Q for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:13 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fOmuQ-0007qw-Do for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:12 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:52740 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fOmuQ-0007qk-8U for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:10 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id D8A8A406F61B for ; Fri, 1 Jun 2018 16:28:09 +0000 (UTC) Received: from localhost (ovpn-112-61.ams2.redhat.com [10.36.112.61]) by smtp.corp.redhat.com (Postfix) with ESMTP id 787C91134CB4; Fri, 1 Jun 2018 16:28:09 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 1 Jun 2018 18:27:43 +0200 Message-Id: <20180601162749.27406-7-marcandre.lureau@redhat.com> In-Reply-To: <20180601162749.27406-1-marcandre.lureau@redhat.com> References: <20180601162749.27406-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.7]); Fri, 01 Jun 2018 16:28:09 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.7]); Fri, 01 Jun 2018 16:28:09 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'marcandre.lureau@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [RFC v2 06/12] vhost-user: add vhost_user_input_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 Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Ask vhost user input backend the list of virtio_input_config. Signed-off-by: Marc-Andr=C3=A9 Lureau --- include/hw/virtio/vhost-backend.h | 4 +++ hw/virtio/vhost-user.c | 59 +++++++++++++++++++++++++++++++ docs/interop/vhost-user.txt | 6 ++++ 3 files changed, 69 insertions(+) diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-ba= ckend.h index 5dac61f9ea..6cc2edacc5 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -12,6 +12,7 @@ #define VHOST_BACKEND_H =20 #include "exec/memory.h" +#include "standard-headers/linux/virtio_input.h" =20 typedef enum VhostBackendType { VHOST_BACKEND_TYPE_NONE =3D 0, @@ -156,4 +157,7 @@ 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_input_get_config(struct vhost_dev *dev, + struct virtio_input_config **config); + #endif /* VHOST_BACKEND_H */ diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index a87db01e55..19ed87d07c 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -84,6 +84,7 @@ typedef enum VhostUserRequest { VHOST_USER_POSTCOPY_ADVISE =3D 28, VHOST_USER_POSTCOPY_LISTEN =3D 29, VHOST_USER_POSTCOPY_END =3D 30, + VHOST_USER_INPUT_GET_CONFIG, VHOST_USER_MAX } VhostUserRequest; =20 @@ -324,6 +325,64 @@ static int vhost_user_write(struct vhost_dev *dev, Vho= stUserMsg *msg, return 0; } =20 +static void *vhost_user_read_size(struct vhost_dev *dev, uint32_t size) +{ + struct vhost_user *u =3D dev->opaque; + CharBackend *chr =3D u->chr; + int r; + uint8_t *p =3D g_malloc(size); + + r =3D qemu_chr_fe_read_all(chr, p, size); + if (r !=3D size) { + error_report("Failed to read msg payload." + " Read %d instead of %d.", r, size); + return NULL; + } + + return p; +} + +int vhost_user_input_get_config(struct vhost_dev *dev, + struct virtio_input_config **config) +{ + void *p =3D NULL; + VhostUserMsg msg =3D { + .hdr.request =3D VHOST_USER_INPUT_GET_CONFIG, + .hdr.flags =3D VHOST_USER_VERSION, + }; + + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { + goto err; + } + + if (vhost_user_read_header(dev, &msg) < 0) { + goto err; + } + + p =3D vhost_user_read_size(dev, msg.hdr.size); + if (!p) { + goto err; + } + + if (msg.hdr.request !=3D VHOST_USER_INPUT_GET_CONFIG) { + error_report("Received unexpected msg type. Expected %d received %= d", + VHOST_USER_INPUT_GET_CONFIG, msg.hdr.request); + goto err; + } + + if (msg.hdr.size % sizeof(struct virtio_input_config)) { + error_report("Invalid msg size"); + goto err; + } + + *config =3D p; + return msg.hdr.size / sizeof(struct virtio_input_config); + +err: + g_free(p); + return -1; +} + static int vhost_user_set_log_base(struct vhost_dev *dev, uint64_t base, struct vhost_log *log) { diff --git a/docs/interop/vhost-user.txt b/docs/interop/vhost-user.txt index 534caab18a..3df9927386 100644 --- a/docs/interop/vhost-user.txt +++ b/docs/interop/vhost-user.txt @@ -744,6 +744,12 @@ Master message types was previously sent. The value returned is an error indication; 0 is success. =20 + * VHOST_USER_INPUT_GET_CONFIG + + Slave payload: (struct virtio_input_config)* + + Ask vhost user input backend the list of virtio_input_config. + Slave message types ------------------- =20 --=20 2.17.1.906.g10fd178552 From nobody Mon Feb 9 16:20:45 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.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 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (208.118.235.17 [208.118.235.17]) by mx.zohomail.com with SMTPS id 1527872513236321.9783038277675; Fri, 1 Jun 2018 10:01:53 -0700 (PDT) Received: from localhost ([::1]:56776 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOnQv-0004TV-Bp for importer@patchew.org; Fri, 01 Jun 2018 13:01:45 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39234) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOmuS-0003Ff-Uv for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:14 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fOmuR-0007ra-Q3 for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:12 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:59632 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fOmuR-0007rL-KN for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:11 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 465CF8A3B7 for ; Fri, 1 Jun 2018 16:28:11 +0000 (UTC) Received: from localhost (ovpn-112-61.ams2.redhat.com [10.36.112.61]) by smtp.corp.redhat.com (Postfix) with ESMTP id EF0D220244E0; Fri, 1 Jun 2018 16:28:10 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 1 Jun 2018 18:27:44 +0200 Message-Id: <20180601162749.27406-8-marcandre.lureau@redhat.com> In-Reply-To: <20180601162749.27406-1-marcandre.lureau@redhat.com> References: <20180601162749.27406-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.4 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Fri, 01 Jun 2018 16:28:11 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Fri, 01 Jun 2018 16:28:11 +0000 (UTC) for IP:'10.11.54.4' DOMAIN:'int-mx04.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'marcandre.lureau@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [RFC v2 07/12] libvhost-user: export vug_source_new 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 Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Simplify the creation of FD sources for other users. Signed-off-by: Marc-Andr=C3=A9 Lureau --- contrib/libvhost-user/libvhost-user-glib.h | 3 +++ contrib/libvhost-user/libvhost-user-glib.c | 15 +++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/contrib/libvhost-user/libvhost-user-glib.h b/contrib/libvhost-= user/libvhost-user-glib.h index 6b2110b94c..d3200f3afc 100644 --- a/contrib/libvhost-user/libvhost-user-glib.h +++ b/contrib/libvhost-user/libvhost-user-glib.h @@ -29,4 +29,7 @@ void vug_init(VugDev *dev, int socket, vu_panic_cb panic, const VuDevIface *iface); void vug_deinit(VugDev *dev); =20 +GSource *vug_source_new(VugDev *dev, int fd, GIOCondition cond, + vu_watch_cb vu_cb, gpointer data); + #endif /* LIBVHOST_USER_GLIB_H */ diff --git a/contrib/libvhost-user/libvhost-user-glib.c b/contrib/libvhost-= user/libvhost-user-glib.c index 545f089587..0e2c467f28 100644 --- a/contrib/libvhost-user/libvhost-user-glib.c +++ b/contrib/libvhost-user/libvhost-user-glib.c @@ -69,8 +69,8 @@ static GSourceFuncs vug_src_funcs =3D { }; =20 static GSource * -vug_source_new(VuDev *dev, int fd, GIOCondition cond, - vu_watch_cb vu_cb, gpointer data) +_vug_source_new(VuDev *dev, int fd, GIOCondition cond, + vu_watch_cb vu_cb, gpointer data) { GSource *gsrc; VugSrc *src; @@ -95,6 +95,13 @@ vug_source_new(VuDev *dev, int fd, GIOCondition cond, return gsrc; } =20 +GSource * +vug_source_new(VugDev *dev, int fd, GIOCondition cond, + vu_watch_cb vu_cb, gpointer data) +{ + return _vug_source_new(&dev->parent, fd, cond, vu_cb, data); +} + static void set_watch(VuDev *vu_dev, int fd, int vu_evt, vu_watch_cb cb, void *pvt) { @@ -106,7 +113,7 @@ set_watch(VuDev *vu_dev, int fd, int vu_evt, vu_watch_c= b cb, void *pvt) g_assert(cb); =20 dev =3D container_of(vu_dev, VugDev, parent); - src =3D vug_source_new(vu_dev, fd, vu_evt, cb, pvt); + src =3D _vug_source_new(vu_dev, fd, vu_evt, cb, pvt); g_hash_table_replace(dev->fdmap, GINT_TO_POINTER(fd), src); } =20 @@ -141,7 +148,7 @@ vug_init(VugDev *dev, int socket, dev->fdmap =3D g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify) g_source_destroy); =20 - dev->src =3D vug_source_new(&dev->parent, socket, G_IO_IN, vug_watch, = NULL); + dev->src =3D _vug_source_new(&dev->parent, socket, G_IO_IN, vug_watch,= NULL); } =20 void --=20 2.17.1.906.g10fd178552 From nobody Mon Feb 9 16:20:45 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.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 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1527872678987913.9307053326064; Fri, 1 Jun 2018 10:04:38 -0700 (PDT) Received: from localhost ([::1]:56791 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOnTi-0006rN-0V for importer@patchew.org; Fri, 01 Jun 2018 13:04:38 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39255) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOmuV-0003Gz-3H for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:17 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fOmuT-0007sX-EP for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:15 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:50246 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fOmuT-0007sC-8z for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:13 -0400 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id DB65B40137F6 for ; Fri, 1 Jun 2018 16:28:12 +0000 (UTC) Received: from localhost (ovpn-112-61.ams2.redhat.com [10.36.112.61]) by smtp.corp.redhat.com (Postfix) with ESMTP id 578E72166BB2; Fri, 1 Jun 2018 16:28:12 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 1 Jun 2018 18:27:45 +0200 Message-Id: <20180601162749.27406-9-marcandre.lureau@redhat.com> In-Reply-To: <20180601162749.27406-1-marcandre.lureau@redhat.com> References: <20180601162749.27406-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Fri, 01 Jun 2018 16:28:12 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Fri, 01 Jun 2018 16:28:12 +0000 (UTC) for IP:'10.11.54.6' DOMAIN:'int-mx06.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'marcandre.lureau@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [RFC v2 08/12] 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 Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 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-backend object, ex: -object vhost-user-backend,id=3Dvuid,cmd=3D"vhost-user-input /dev/input/eve= nt0" Signed-off-by: Marc-Andr=C3=A9 Lureau --- contrib/libvhost-user/libvhost-user.h | 1 + contrib/vhost-user-input/main.c | 358 +++++++++++++++++++++++++ Makefile | 3 + Makefile.objs | 1 + configure | 1 + contrib/vhost-user-input/Makefile.objs | 1 + 6 files changed, 365 insertions(+) create mode 100644 contrib/vhost-user-input/main.c create mode 100644 contrib/vhost-user-input/Makefile.objs diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/= libvhost-user.h index b27075ea3b..d6badc8653 100644 --- a/contrib/libvhost-user/libvhost-user.h +++ b/contrib/libvhost-user/libvhost-user.h @@ -89,6 +89,7 @@ typedef enum VhostUserRequest { VHOST_USER_POSTCOPY_ADVISE =3D 28, VHOST_USER_POSTCOPY_LISTEN =3D 29, VHOST_USER_POSTCOPY_END =3D 30, + VHOST_USER_INPUT_GET_CONFIG, VHOST_USER_MAX } VhostUserRequest; =20 diff --git a/contrib/vhost-user-input/main.c b/contrib/vhost-user-input/mai= n.c new file mode 100644 index 0000000000..b2c2184446 --- /dev/null +++ b/contrib/vhost-user-input/main.c @@ -0,0 +1,358 @@ +#include +#include + +#include "qemu/osdep.h" +#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" + +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_event *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; + unsigned have, need; + int i, len; + + /* queue up events ... */ + if (vi->qindex =3D=3D vi->qsize) { + vi->qsize++; + vi->queue =3D realloc(vi->queue, vi->qsize * + sizeof(virtio_input_event)); + } + vi->queue[vi->qindex++] =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 ... */ + need =3D sizeof(virtio_input_event) * vi->qindex; + vu_queue_get_avail_bytes(dev, vq, &have, NULL, need, 0); + if (have < need) { + vi->qindex =3D 0; + g_warning("ENOSPC in vq, dropping events"); + return; + } + + /* ... and finally pass them to the guest */ + for (i =3D 0; i < vi->qindex; i++) { + elem =3D vu_queue_pop(dev, vq, sizeof(VuVirtqElement)); + if (!elem) { + /* should not happen, we've checked for space beforehand */ + g_warning("%s: Huh? No vq elem available ...\n", __func__); + return; + } + len =3D iov_from_buf(elem->in_sg, elem->in_num, + 0, vi->queue + i, sizeof(virtio_input_event)); + 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", __func__); + + 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(1); +} + +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 0) { + if (started && !vi->evsrc) { + vi->evsrc =3D vug_source_new(&vi->dev, vi->evdevfd, + G_IO_IN, vi_evdev_watch, vi); + } else if (!started) { + g_source_destroy(vi->evsrc); + vi->evsrc =3D NULL; + } + } else { + vu_set_queue_handler(dev, vq, started ? vi_handle_sts : NULL); + } +} + +static int +vi_process_msg(VuDev *dev, VhostUserMsg *vmsg, int *do_reply) +{ + VuInput *vi =3D container_of(dev, VuInput, dev.parent); + + switch (vmsg->request) { + case VHOST_USER_INPUT_GET_CONFIG: + vmsg->size =3D vi->config->len * sizeof(virtio_input_config); + vmsg->data =3D g_memdup(vi->config->data, vmsg->size); + *do_reply =3D true; + return 1; + default: + return 0; + } +} + +static const VuDevIface vuiface =3D { + .queue_set_started =3D vi_queue_set_started, + .process_msg =3D vi_process_msg, +}; + +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; +} + +char **fname; +char *socket_path; +bool nograb; + +static GOptionEntry entries[] =3D { + { "no-grab", 'n', 0, G_OPTION_ARG_NONE, &nograb, + "Don't grab device", "" }, + { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &socket_path, + "Use UNIX socket path", "" }, + { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &fname, + "EVDEV filename", "" }, + { 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("EVDEV - vhost-user-input sample"); + 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(1); + } + if (!fname || !fname[0] || fname[1]) { + g_printerr("Please specify a single EVDEV filename\n"); + exit(1); + } + + vi.evdevfd =3D open(fname[0], O_RDWR); + if (vi.evdevfd < 0) { + g_printerr("Failed to open evdev: %s\n", g_strerror(errno)); + exit(1); + } + + rc =3D ioctl(vi.evdevfd, EVIOCGVERSION, &ver); + if (rc < 0) { + g_printerr("%s: is not an evdev device\n", argv[1]); + exit(1); + } + + if (!nograb) { + rc =3D ioctl(vi.evdevfd, EVIOCGRAB, 1); + if (rc < 0) { + g_printerr("Failed to grab device\n"); + exit(1); + } + } + + 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 (socket_path) { + int lsock =3D unix_sock_new(socket_path); + fd =3D accept(lsock, NULL, NULL); + close(lsock); + } else { + fd =3D 3; + } + if (fd =3D=3D -1) { + g_printerr("Invalid socket"); + exit(1); + } + 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); + + return 0; +} diff --git a/Makefile b/Makefile index 6d588d1f71..d1222da359 100644 --- a/Makefile +++ b/Makefile @@ -420,6 +420,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 \ @@ -719,6 +720,8 @@ vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y) libv= host-user.a $(call LINK, $^) vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a $(call LINK, $^) +vhost-user-input$(EXESUF): $(vhost-user-input-obj-y) libvhost-user.a libqe= muutil.a + $(call LINK, $^) =20 module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak $(call quiet-command,$(PYTHON) $< $@ \ diff --git a/Makefile.objs b/Makefile.objs index c6c3554203..c20f8d35d7 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -193,6 +193,7 @@ vhost-user-scsi.o-cflags :=3D $(LIBISCSI_CFLAGS) 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/ +vhost-user-input-obj-y =3D contrib/vhost-user-input/ =20 ###################################################################### trace-events-subdirs =3D diff --git a/configure b/configure index a6a4616c3e..1d1caea9e7 100755 --- a/configure +++ b/configure @@ -5532,6 +5532,7 @@ if test "$want_tools" =3D "yes" ; then fi if [ "$ivshmem" =3D "yes" ]; then tools=3D"ivshmem-client\$(EXESUF) ivshmem-server\$(EXESUF) $tools" + tools=3D"vhost-user-input\$(EXESUF) $tools" fi fi if test "$softmmu" =3D yes ; then 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.17.1.906.g10fd178552 From nobody Mon Feb 9 16:20:45 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.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 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1527871556734550.871271628791; Fri, 1 Jun 2018 09:45:56 -0700 (PDT) Received: from localhost ([::1]:56671 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOnBb-0000Lx-SZ for importer@patchew.org; Fri, 01 Jun 2018 12:45:55 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39269) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOmuW-0003IL-J0 for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:17 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fOmuU-0007tF-UW for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:16 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:59636 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fOmuU-0007t3-Ou for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:14 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 5D7C18A3B7 for ; Fri, 1 Jun 2018 16:28:14 +0000 (UTC) Received: from localhost (ovpn-112-61.ams2.redhat.com [10.36.112.61]) by smtp.corp.redhat.com (Postfix) with ESMTP id ED8A563F72; Fri, 1 Jun 2018 16:28:13 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 1 Jun 2018 18:27:46 +0200 Message-Id: <20180601162749.27406-10-marcandre.lureau@redhat.com> In-Reply-To: <20180601162749.27406-1-marcandre.lureau@redhat.com> References: <20180601162749.27406-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Fri, 01 Jun 2018 16:28:14 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Fri, 01 Jun 2018 16:28:14 +0000 (UTC) for IP:'10.11.54.5' DOMAIN:'int-mx05.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'marcandre.lureau@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [RFC v2 09/12] Add vhost-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 Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Use a vhost-user input backend. Usage: -object vhost-user-backend,id=3Dvuid -device vhost-input-pci,vhost-user=3Dv= uid Signed-off-by: Marc-Andr=C3=A9 Lureau --- hw/virtio/virtio-pci.h | 10 +++ include/hw/virtio/virtio-input.h | 14 ++++ hw/input/vhost-user-input.c | 124 +++++++++++++++++++++++++++++++ hw/virtio/virtio-pci.c | 20 +++++ MAINTAINERS | 1 + hw/input/Makefile.objs | 1 + 6 files changed, 170 insertions(+) create mode 100644 hw/input/vhost-user-input.c diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h index 813082b0d7..c7e28e1b9c 100644 --- a/hw/virtio/virtio-pci.h +++ b/hw/virtio/virtio-pci.h @@ -54,6 +54,7 @@ typedef struct VirtIORngPCI VirtIORngPCI; typedef struct VirtIOInputPCI VirtIOInputPCI; typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI; typedef struct VirtIOInputHostPCI VirtIOInputHostPCI; +typedef struct VHostUserInputPCI VHostUserInputPCI; typedef struct VirtIOGPUPCI VirtIOGPUPCI; typedef struct VHostVSockPCI VHostVSockPCI; typedef struct VirtIOCryptoPCI VirtIOCryptoPCI; @@ -376,6 +377,15 @@ struct VirtIOInputHostPCI { =20 #endif =20 +#define TYPE_VHOST_USER_INPUT_PCI "vhost-user-input-pci" +#define VHOST_USER_INPUT_PCI(obj) \ + OBJECT_CHECK(VHostUserInputPCI, (obj), TYPE_VHOST_USER_INPUT_PCI) + +struct VHostUserInputPCI { + VirtIOPCIProxy parent_obj; + VHostUserInput vhi; +}; + /* * virtio-gpu-pci: This extends VirtioPCIProxy. */ 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..b293020d08 --- /dev/null +++ b/hw/input/vhost-user-input.c @@ -0,0 +1,124 @@ +/* + * 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 "qapi/error.h" +#include "qemu-common.h" +#include "qemu/sockets.h" + +#include "hw/qdev.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-input.h" + +#include +#include "standard-headers/linux/input.h" + +/* ----------------------------------------------------------------- */ + +static struct virtio_input_config vhost_input_config[] =3D { + { /* empty list */ }, +}; + +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); + virtio_input_config *config; + int i, ret; + + if (!vhi->vhost) { + error_setg(errp, "'vhost-user' property is required"); + return; + } + + if (vhost_user_backend_dev_init(vhi->vhost, vdev, 2, errp) =3D=3D -1) { + return; + } + + ret =3D vhost_user_input_get_config(&vhi->vhost->dev, &config); + if (ret < 0) { + error_setg(errp, "failed to get input config"); + return; + } + for (i =3D 0; i < ret; i++) { + virtio_input_add_config(vinput, &config[i]); + } + g_free(config); +} + +static void vhost_input_change_active(VirtIOInput *vinput) +{ + VHostUserInput *vhi =3D VHOST_USER_INPUT(vinput); + + if (!vhi->vhost) { + return; + } + + if (vinput->active) { + vhost_user_backend_start(vhi->vhost); + } else { + vhost_user_backend_stop(vhi->vhost); + } +} + +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); + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->vmsd =3D &vmstate_vhost_input; + vic->realize =3D vhost_input_realize; + vic->change_active =3D vhost_input_change_active; +} + +static void vhost_input_is_busy(const Object *obj, const char *name, + Object *val, Error **errp) +{ + VHostUserInput *vhi =3D VHOST_USER_INPUT(obj); + + if (vhi->vhost) { + error_setg(errp, "can't use already busy vhost-user"); + } else { + qdev_prop_allow_set_link_before_realize(obj, name, val, errp); + } +} + +static void vhost_input_init(Object *obj) +{ + VHostUserInput *vhi =3D VHOST_USER_INPUT(obj); + VirtIOInput *vinput =3D VIRTIO_INPUT(obj); + + virtio_input_init_config(vinput, vhost_input_config); + + object_property_add_link(obj, "vhost-user", TYPE_VHOST_USER_BACKEND, + (Object **)&vhi->vhost, + vhost_input_is_busy, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); +} + +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, + .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/virtio-pci.c b/hw/virtio/virtio-pci.c index 5eb0c323ca..ecec2e713c 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2646,6 +2646,25 @@ static const TypeInfo virtio_host_pci_info =3D { }; #endif =20 +static void vhost_input_pci_initfn(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, "vhost-user", + OBJECT(&dev->vhi), "vhost-user", + &error_abort); +} + +static const TypeInfo vhost_input_pci_info =3D { + .name =3D TYPE_VHOST_USER_INPUT_PCI, + .parent =3D TYPE_VIRTIO_INPUT_PCI, + .instance_size =3D sizeof(VHostUserInputPCI), + .instance_init =3D vhost_input_pci_initfn, +}; + /* virtio-pci-bus */ =20 static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size, @@ -2702,6 +2721,7 @@ static void virtio_pci_register_types(void) #ifdef CONFIG_LINUX type_register_static(&virtio_host_pci_info); #endif + type_register_static(&vhost_input_pci_info); type_register_static(&virtio_pci_bus_info); type_register_static(&virtio_pci_info); #ifdef CONFIG_VIRTFS diff --git a/MAINTAINERS b/MAINTAINERS index 41cd3736a9..67c0197570 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1202,6 +1202,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/Makefile.objs b/hw/input/Makefile.objs index 77e53e6883..f787df3aa5 100644 --- a/hw/input/Makefile.objs +++ b/hw/input/Makefile.objs @@ -12,6 +12,7 @@ common-obj-$(CONFIG_VIRTIO) +=3D virtio-input-hid.o ifeq ($(CONFIG_LINUX),y) common-obj-$(CONFIG_VIRTIO) +=3D virtio-input-host.o endif +common-obj-$(CONFIG_VIRTIO) +=3D vhost-user-input.o =20 obj-$(CONFIG_MILKYMIST) +=3D milkymist-softusb.o obj-$(CONFIG_PXA2XX) +=3D pxa2xx_keypad.o --=20 2.17.1.906.g10fd178552 From nobody Mon Feb 9 16:20:45 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.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 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 152787197302896.94677961537116; Fri, 1 Jun 2018 09:52:53 -0700 (PDT) Received: from localhost ([::1]:56706 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOnIK-0005gh-9R for importer@patchew.org; Fri, 01 Jun 2018 12:52:52 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39280) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOmuX-0003JE-4z for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:18 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fOmuW-0007tx-9f for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:17 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:60160 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fOmuW-0007tb-4s for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:16 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id BBA9C818BAEE for ; Fri, 1 Jun 2018 16:28:15 +0000 (UTC) Received: from localhost (ovpn-112-61.ams2.redhat.com [10.36.112.61]) by smtp.corp.redhat.com (Postfix) with ESMTP id 712E320244E0; Fri, 1 Jun 2018 16:28:15 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 1 Jun 2018 18:27:47 +0200 Message-Id: <20180601162749.27406-11-marcandre.lureau@redhat.com> In-Reply-To: <20180601162749.27406-1-marcandre.lureau@redhat.com> References: <20180601162749.27406-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.4 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Fri, 01 Jun 2018 16:28:15 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Fri, 01 Jun 2018 16:28:15 +0000 (UTC) for IP:'10.11.54.4' DOMAIN:'int-mx04.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'marcandre.lureau@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [RFC v2 10/12] 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 Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Add a new vhost-user message to give a unix socket for gpu updates to a vhost-user backend. 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 and may not need the same kind of stability guarantees, thus I recommend to have a new dedicated channel. FIXME: document the GPU protocol. Signed-off-by: Marc-Andr=C3=A9 Lureau --- contrib/libvhost-user/libvhost-user.h | 1 + include/hw/virtio/vhost-backend.h | 1 + hw/virtio/vhost-user.c | 11 +++++++++++ 3 files changed, 13 insertions(+) diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/= libvhost-user.h index d6badc8653..57a3a36100 100644 --- a/contrib/libvhost-user/libvhost-user.h +++ b/contrib/libvhost-user/libvhost-user.h @@ -90,6 +90,7 @@ typedef enum VhostUserRequest { VHOST_USER_POSTCOPY_LISTEN =3D 29, VHOST_USER_POSTCOPY_END =3D 30, VHOST_USER_INPUT_GET_CONFIG, + VHOST_USER_GPU_SET_SOCKET, VHOST_USER_MAX } VhostUserRequest; =20 diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-ba= ckend.h index 6cc2edacc5..a60d278f05 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -159,5 +159,6 @@ int vhost_backend_handle_iotlb_msg(struct vhost_dev *de= v, =20 int vhost_user_input_get_config(struct vhost_dev *dev, struct virtio_input_config **config); +int vhost_user_gpu_set_socket(struct vhost_dev *dev, int fd); =20 #endif /* VHOST_BACKEND_H */ diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 19ed87d07c..5cb76f436b 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -85,6 +85,7 @@ typedef enum VhostUserRequest { VHOST_USER_POSTCOPY_LISTEN =3D 29, VHOST_USER_POSTCOPY_END =3D 30, VHOST_USER_INPUT_GET_CONFIG, + VHOST_USER_GPU_SET_SOCKET, VHOST_USER_MAX } VhostUserRequest; =20 @@ -383,6 +384,16 @@ err: return -1; } =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) { --=20 2.17.1.906.g10fd178552 From nobody Mon Feb 9 16:20:45 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.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 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 15278721128401019.4796917651656; Fri, 1 Jun 2018 09:55:12 -0700 (PDT) Received: from localhost ([::1]:56717 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOnKY-0007hq-8q for importer@patchew.org; Fri, 01 Jun 2018 12:55:10 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39338) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOmug-0003TW-Go for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:28 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fOmud-0007wh-Az for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:26 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:59640 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fOmud-0007wS-50 for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:23 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id CD3DD8A3B7 for ; Fri, 1 Jun 2018 16:28:22 +0000 (UTC) Received: from localhost (ovpn-112-61.ams2.redhat.com [10.36.112.61]) by smtp.corp.redhat.com (Postfix) with ESMTP id CCC3C1134CB4; Fri, 1 Jun 2018 16:28:16 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 1 Jun 2018 18:27:48 +0200 Message-Id: <20180601162749.27406-12-marcandre.lureau@redhat.com> In-Reply-To: <20180601162749.27406-1-marcandre.lureau@redhat.com> References: <20180601162749.27406-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Fri, 01 Jun 2018 16:28:22 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.2]); Fri, 01 Jun 2018 16:28:22 +0000 (UTC) for IP:'10.11.54.3' DOMAIN:'int-mx03.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'marcandre.lureau@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [RFC v2 11/12] Add virtio-gpu 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 Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Add to virtio-gpu devices a "vhost-user" property. When set, the associated vhost-user backend is used to handle the virtio rings. For now, a socketpair is created for the backend to share the rendering results with qemu via a simple VHOST_GPU protocol. Example usage: -object vhost-user-backend,id=3Dvug,cmd=3D"./vhost-user-gpu" -device virtio-vga,virgl=3Dtrue,vhost-user=3Dvug Signed-off-by: Marc-Andr=C3=A9 Lureau --- include/hw/virtio/virtio-gpu.h | 9 + include/ui/console.h | 1 + hw/display/vhost-gpu.c | 290 +++++++++++++++++++++++++++++++++ hw/display/virtio-gpu-3d.c | 8 +- hw/display/virtio-gpu-pci.c | 5 + hw/display/virtio-gpu.c | 77 ++++++++- hw/display/virtio-vga.c | 5 + ui/spice-display.c | 3 +- hw/display/Makefile.objs | 2 +- 9 files changed, 393 insertions(+), 7 deletions(-) create mode 100644 hw/display/vhost-gpu.c diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 79bb3fb3dd..7cd514175a 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" #define TYPE_VIRTIO_GPU "virtio-gpu-device" @@ -88,6 +89,9 @@ struct virtio_gpu_ctrl_command { typedef struct VirtIOGPU { VirtIODevice parent_obj; =20 + VhostUserBackend *vhost; + CharBackend vhost_chr; + QEMUBH *ctrl_bh; QEMUBH *cursor_bh; VirtQueue *ctrl_vq; @@ -103,6 +107,7 @@ typedef struct VirtIOGPU { QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq; =20 struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS]; + QemuDmaBuf dmabuf[VIRTIO_GPU_MAX_SCANOUTS]; struct virtio_gpu_requested_state req_state[VIRTIO_GPU_MAX_SCANOUTS]; =20 struct virtio_gpu_conf conf; @@ -171,4 +176,8 @@ void virtio_gpu_virgl_reset(VirtIOGPU *g); void virtio_gpu_gl_block(void *opaque, bool block); int virtio_gpu_virgl_init(VirtIOGPU *g); int virtio_gpu_virgl_get_num_capsets(VirtIOGPU *g); + +/* vhost-gpu.c */ +int vhost_gpu_init(VirtIOGPU *g, Error **errp); + #endif diff --git a/include/ui/console.h b/include/ui/console.h index 981b519dde..fb969caf70 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -186,6 +186,7 @@ struct QemuDmaBuf { uint32_t stride; uint32_t fourcc; uint32_t texture; + bool y0_top; }; =20 typedef struct DisplayChangeListenerOps { diff --git a/hw/display/vhost-gpu.c b/hw/display/vhost-gpu.c new file mode 100644 index 0000000000..42d9143d3d --- /dev/null +++ b/hw/display/vhost-gpu.c @@ -0,0 +1,290 @@ +/* + * Virtio vhost GPU Device + * + * Copyright Red Hat, Inc. 2016 + * + * 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" + +typedef enum VhostGpuRequest { + VHOST_GPU_NONE =3D 0, + VHOST_GPU_CURSOR_POS, + VHOST_GPU_CURSOR_POS_HIDE, + VHOST_GPU_CURSOR_UPDATE, + VHOST_GPU_SCANOUT, + VHOST_GPU_UPDATE, + VHOST_GPU_GL_SCANOUT, + VHOST_GPU_GL_UPDATE, +} VhostGpuRequest; + +typedef struct VhostGpuCursorPos { + uint32_t scanout_id; + uint32_t x; + uint32_t y; +} QEMU_PACKED VhostGpuCursorPos; + +typedef struct VhostGpuCursorUpdate { + VhostGpuCursorPos pos; + uint32_t hot_x; + uint32_t hot_y; + uint32_t data[64 * 64]; +} QEMU_PACKED VhostGpuCursorUpdate; + +typedef struct VhostGpuScanout { + uint32_t scanout_id; + uint32_t width; + uint32_t height; +} QEMU_PACKED VhostGpuScanout; + +typedef struct VhostGpuGlScanout { + 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 VhostGpuGlScanout; + +typedef struct VhostGpuUpdate { + uint32_t scanout_id; + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; + uint8_t data[]; +} QEMU_PACKED VhostGpuUpdate; + +typedef struct VhostGpuMsg { + VhostGpuRequest request; + uint32_t size; /* the following payload size */ + union { + VhostGpuCursorPos cursor_pos; + VhostGpuCursorUpdate cursor_update; + VhostGpuScanout scanout; + VhostGpuUpdate update; + VhostGpuGlScanout gl_scanout; + } payload; +} QEMU_PACKED VhostGpuMsg; + +static VhostGpuMsg m __attribute__ ((unused)); +#define VHOST_GPU_HDR_SIZE (sizeof(m.request) + sizeof(m.size)) + +static void vhost_gpu_handle_cursor(VirtIOGPU *g, VhostGpuMsg *msg) +{ + VhostGpuCursorPos *pos =3D &msg->payload.cursor_pos; + struct virtio_gpu_scanout *s; + + if (pos->scanout_id >=3D g->conf.max_outputs) { + return; + } + s =3D &g->scanout[pos->scanout_id]; + + if (msg->request =3D=3D VHOST_GPU_CURSOR_UPDATE) { + VhostGpuCursorUpdate *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_GPU_CURSOR_POS_HIDE); +} + +static void vhost_gpu_handle_display(VirtIOGPU *g, VhostGpuMsg *msg) +{ + struct virtio_gpu_scanout *s; + + switch (msg->request) { + case VHOST_GPU_SCANOUT: { + VhostGpuScanout *m =3D &msg->payload.scanout; + + if (m->scanout_id >=3D g->conf.max_outputs) { + return; + } + s =3D &g->scanout[m->scanout_id]; + + s->ds =3D qemu_create_displaysurface(m->width, m->height); + if (!s->ds) { + return; + } + + dpy_gfx_replace_surface(s->con, s->ds); + break; + } + case VHOST_GPU_GL_SCANOUT: { + VhostGpuGlScanout *m =3D &msg->payload.gl_scanout; + int fd =3D qemu_chr_fe_get_msgfd(&g->vhost_chr); + QemuDmaBuf *dmabuf; + + if (m->scanout_id >=3D g->conf.max_outputs) { + if (fd >=3D 0) { + close(fd); + } + break; + } + + dmabuf =3D &g->dmabuf[m->scanout_id]; + if (g->enable && dmabuf->fd >=3D 0) { + close(dmabuf->fd); + dmabuf->fd =3D -1; + } + if (fd =3D=3D -1) { + dpy_gl_release_dmabuf(g->scanout[m->scanout_id].con, dmabuf); + 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, + }; + g->enable =3D 1; + dpy_gl_scanout_dmabuf(g->scanout[m->scanout_id].con, dmabuf); + break; + } + case VHOST_GPU_GL_UPDATE: { + VhostGpuUpdate *m =3D &msg->payload.update; + QemuConsole *con =3D g->scanout[m->scanout_id].con; + + if (m->scanout_id >=3D g->conf.max_outputs || + !g->scanout[m->scanout_id].con) { + break; + } + + graphic_hw_gl_block(con, true); + dpy_gl_update(con, m->x, m->y, m->width, m->height); + graphic_hw_gl_block(con, false); + break; + } + case VHOST_GPU_UPDATE: { + VhostGpuUpdate *m =3D &msg->payload.update; + + if (m->scanout_id >=3D g->conf.max_outputs) { + break; + } + s =3D &g->scanout[m->scanout_id]; + + 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); + dpy_gfx_update(s->con, m->x, m->y, m->width, m->height); + break; + } + default: + g_warning("unhandled message %d %d", msg->request, msg->size); + } +} + +static void vhost_gpu_chr_read(void *opaque) +{ + VirtIOGPU *g =3D opaque; + VhostGpuMsg *msg =3D NULL; + VhostGpuRequest 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_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_GPU_CURSOR_UPDATE || + request =3D=3D VHOST_GPU_CURSOR_POS || + request =3D=3D VHOST_GPU_CURSOR_POS_HIDE) { + vhost_gpu_handle_cursor(g, msg); + } else { + vhost_gpu_handle_display(g, msg); + } + +end: + g_free(msg); +} + +int vhost_gpu_init(VirtIOGPU *g, Error **errp) +{ + VirtIODevice *vdev =3D VIRTIO_DEVICE(g); + Chardev *chr; + int sv[2]; + + if (vhost_user_backend_dev_init(g->vhost, vdev, 2, errp) < 0) { + return -1; + } + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) =3D=3D -1) { + error_setg_errno(errp, errno, "socketpair() failed"); + return -1; + } + + 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"); + object_unref(OBJECT(chr)); + return -1; + } + if (!qemu_chr_fe_init(&g->vhost_chr, chr, errp)) { + object_unref(OBJECT(chr)); + return -1; + } + + qemu_set_fd_handler(sv[0], vhost_gpu_chr_read, NULL, g); + + vhost_user_gpu_set_socket(&g->vhost->dev, sv[1]); + + close(sv[1]); + + return 0; +} diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c index 3558f38fe8..90984ea1bb 100644 --- a/hw/display/virtio-gpu-3d.c +++ b/hw/display/virtio-gpu-3d.c @@ -614,9 +614,13 @@ void virtio_gpu_gl_block(void *opaque, bool block) g->renderer_blocked--; } assert(g->renderer_blocked >=3D 0); - if (g->renderer_blocked =3D=3D 0) { - virtio_gpu_process_cmdq(g); + if (g->vhost && g->enable) { + uint32_t ok; /*unblock renderer */ + qemu_chr_fe_write(&g->vhost_chr, (uint8_t *)&ok, sizeof(ok)); + } else { + virtio_gpu_process_cmdq(g); + } } } =20 diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c index cece4aa495..470bf0608c 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -18,6 +18,7 @@ #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-pci.h" #include "hw/virtio/virtio-gpu.h" +#include "qapi/error.h" =20 static Property virtio_gpu_pci_properties[] =3D { DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), @@ -67,6 +68,10 @@ static void virtio_gpu_initfn(Object *obj) =20 virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_GPU); + + object_property_add_alias(obj, "vhost-user", + OBJECT(&dev->vdev), "vhost-user", + &error_abort); } =20 static const TypeInfo virtio_gpu_pci_info =3D { diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 2dd3c3481a..0dfa08a173 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -22,6 +22,7 @@ #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 @@ -1184,11 +1185,16 @@ static void virtio_gpu_device_realize(DeviceState *= qdev, Error **errp) } =20 g->use_virgl_renderer =3D false; + if (g->vhost) { + /* default to backend virgl=3Don */ + have_virgl =3D true; + } else { #if !defined(CONFIG_VIRGL) || defined(HOST_WORDS_BIGENDIAN) - have_virgl =3D false; + have_virgl =3D false; #else - have_virgl =3D display_opengl; + have_virgl =3D display_opengl; #endif + } if (!have_virgl) { g->conf.flags &=3D ~(1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED); } @@ -1226,6 +1232,10 @@ static void virtio_gpu_device_realize(DeviceState *q= dev, Error **errp) g->cursor_vq =3D virtio_add_queue(vdev, 16, virtio_gpu_handle_curs= or_cb); } =20 + if (g->vhost && vhost_gpu_init(g, errp) < 0) { + return; + } + 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); @@ -1253,8 +1263,27 @@ static void virtio_gpu_device_unrealize(DeviceState = *qdev, Error **errp) } } =20 +static void virtio_gpu_host_user_is_busy(const Object *obj, const char *na= me, + Object *val, Error **errp) +{ + VirtIOGPU *g =3D VIRTIO_GPU(obj); + + if (g->vhost) { + error_setg(errp, "can't use already busy vhost-user"); + } else { + qdev_prop_allow_set_link_before_realize(obj, name, val, errp); + } +} + static void virtio_gpu_instance_init(Object *obj) { + VirtIOGPU *g =3D VIRTIO_GPU(obj); + + object_property_add_link(obj, "vhost-user", TYPE_VHOST_USER_BACKEND, + (Object **)&g->vhost, + virtio_gpu_host_user_is_busy, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); } =20 static void virtio_gpu_reset(VirtIODevice *vdev) @@ -1264,7 +1293,9 @@ static void virtio_gpu_reset(VirtIODevice *vdev) int i; =20 g->enable =3D 0; - + if (g->vhost) { + vhost_user_backend_stop(g->vhost); + } QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) { virtio_gpu_resource_destroy(g, res); } @@ -1312,6 +1343,43 @@ static const VMStateDescription vmstate_virtio_gpu = =3D { }, }; =20 +static void virtio_gpu_set_status(VirtIODevice *vdev, uint8_t val) +{ + VirtIOGPU *g =3D VIRTIO_GPU(vdev); + + if (g->vhost) { + if (val & VIRTIO_CONFIG_S_DRIVER_OK && vdev->vm_running) { + vhost_user_backend_start(g->vhost); + } else { + /* TODO: forcefully gl unblock ? */ + vhost_user_backend_stop(g->vhost); + } + } +} + +static bool virtio_gpu_guest_notifier_pending(VirtIODevice *vdev, int idx) +{ + VirtIOGPU *g =3D VIRTIO_GPU(vdev); + + if (!g->vhost) { + return false; + } + + return vhost_virtqueue_pending(&g->vhost->dev, idx); +} + +static void virtio_gpu_guest_notifier_mask(VirtIODevice *vdev, int idx, + bool mask) +{ + VirtIOGPU *g =3D VIRTIO_GPU(vdev); + + if (!g->vhost) { + return; + } + + vhost_virtqueue_mask(&g->vhost->dev, vdev, idx, mask); +} + 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, @@ -1338,6 +1406,9 @@ static void virtio_gpu_class_init(ObjectClass *klass,= void *data) 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->set_status =3D virtio_gpu_set_status; + vdc->guest_notifier_mask =3D virtio_gpu_guest_notifier_mask; + vdc->guest_notifier_pending =3D virtio_gpu_guest_notifier_pending; =20 vdc->reset =3D virtio_gpu_reset; =20 diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index baa74ba82c..b16edd9e6d 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -201,6 +201,11 @@ static void virtio_vga_inst_initfn(Object *obj) =20 virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_GPU); + + /* could eventually be included in qdev_alias_all_properties? */ + object_property_add_alias(obj, "vhost-user", + OBJECT(&dev->vdev), "vhost-user", + &error_abort); } =20 static TypeInfo virtio_vga_info =3D { diff --git a/ui/spice-display.c b/ui/spice-display.c index fe734821dd..81f08a85bc 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -1048,7 +1048,8 @@ static void qemu_spice_gl_update(DisplayChangeListene= r *dcl, /* note: spice server will close the fd, so hand over a dup */ spice_qxl_gl_scanout(&ssd->qxl, dup(dmabuf->fd), dmabuf->width, dmabuf->height, - dmabuf->stride, dmabuf->fourcc, false); + dmabuf->stride, dmabuf->fourcc, + dmabuf->y0_top); } qemu_spice_gl_monitor_config(ssd, 0, 0, dmabuf->width, dmabuf->hei= ght); ssd->guest_dmabuf_refresh =3D false; diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index d907b381ae..1c6f3c6bc1 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -36,7 +36,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) +=3D virtio-gpu.o virtio-gpu-3d.o +obj-$(CONFIG_VIRTIO) +=3D virtio-gpu.o virtio-gpu-3d.o vhost-gpu.o obj-$(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.17.1.906.g10fd178552 From nobody Mon Feb 9 16:20:45 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.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 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1527872257506849.0136307245173; Fri, 1 Jun 2018 09:57:37 -0700 (PDT) Received: from localhost ([::1]:56739 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOnMs-0001Bu-Jm for importer@patchew.org; Fri, 01 Jun 2018 12:57:34 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39368) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fOmup-0003bR-HX for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:40 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fOmuk-00080i-I5 for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:35 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:60166 helo=mx1.redhat.com) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fOmuk-00080R-9d for qemu-devel@nongnu.org; Fri, 01 Jun 2018 12:28:30 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id E2C9F818BAEE for ; Fri, 1 Jun 2018 16:28:29 +0000 (UTC) Received: from localhost (ovpn-112-61.ams2.redhat.com [10.36.112.61]) by smtp.corp.redhat.com (Postfix) with ESMTP id E07C46B400; Fri, 1 Jun 2018 16:28:23 +0000 (UTC) From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= To: qemu-devel@nongnu.org Date: Fri, 1 Jun 2018 18:27:49 +0200 Message-Id: <20180601162749.27406-13-marcandre.lureau@redhat.com> In-Reply-To: <20180601162749.27406-1-marcandre.lureau@redhat.com> References: <20180601162749.27406-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Fri, 01 Jun 2018 16:28:29 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.8]); Fri, 01 Jun 2018 16:28:29 +0000 (UTC) for IP:'10.11.54.5' DOMAIN:'int-mx05.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'marcandre.lureau@redhat.com' RCPT:'' Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 66.187.233.73 Subject: [Qemu-devel] [RFC v2 12/12] 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 Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" Add a vhost-user gpu backend example, based on virtio-gpu/3d device. It is to be associated with a vhost-user-backend object, ex: -object vhost-user-backend,id=3Dvug,cmd=3D"vhost-user-gpu" TODO: - add/check multi-head support - add/check resize support - add missing options, such as virgl=3Doff,rendernode config, initial resolution etc.. - stop/crash & resume handling - accelerated rendering/display to avoid the many round trips Signed-off-by: Marc-Andr=C3=A9 Lureau --- contrib/vhost-user-gpu/virgl.h | 24 + contrib/vhost-user-gpu/vugpu.h | 152 +++++ contrib/vhost-user-gpu/main.c | 953 +++++++++++++++++++++++++++ contrib/vhost-user-gpu/virgl.c | 537 +++++++++++++++ Makefile | 3 + Makefile.objs | 1 + configure | 4 + contrib/vhost-user-gpu/Makefile.objs | 7 + 8 files changed, 1681 insertions(+) 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/main.c create mode 100644 contrib/vhost-user-gpu/virgl.c create mode 100644 contrib/vhost-user-gpu/Makefile.objs diff --git a/contrib/vhost-user-gpu/virgl.h b/contrib/vhost-user-gpu/virgl.h new file mode 100644 index 0000000000..c86625b963 --- /dev/null +++ b/contrib/vhost-user-gpu/virgl.h @@ -0,0 +1,24 @@ +/* + * 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" + +int vg_virgl_init(VuGpu *g); +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..4515703401 --- /dev/null +++ b/contrib/vhost-user-gpu/vugpu.h @@ -0,0 +1,152 @@ +/* + * 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 "contrib/libvhost-user/libvhost-user-glib.h" +#include "standard-headers/linux/virtio_gpu.h" + +#include "qemu/osdep.h" +#include "qemu/queue.h" +#include "qemu/iov.h" +#include "qemu/bswap.h" + +typedef enum VhostGpuRequest { + VHOST_GPU_NONE =3D 0, + VHOST_GPU_CURSOR_POS, + VHOST_GPU_CURSOR_POS_HIDE, + VHOST_GPU_CURSOR_UPDATE, + VHOST_GPU_SCANOUT, + VHOST_GPU_UPDATE, + VHOST_GPU_GL_SCANOUT, + VHOST_GPU_GL_UPDATE, +} VhostGpuRequest; + +typedef struct VhostGpuCursorPos { + uint32_t scanout_id; + uint32_t x; + uint32_t y; +} QEMU_PACKED VhostGpuCursorPos; + +typedef struct VhostGpuCursorUpdate { + VhostGpuCursorPos pos; + uint32_t hot_x; + uint32_t hot_y; + uint32_t data[64 * 64]; +} QEMU_PACKED VhostGpuCursorUpdate; + +typedef struct VhostGpuScanout { + uint32_t scanout_id; + uint32_t width; + uint32_t height; +} QEMU_PACKED VhostGpuScanout; + +typedef struct VhostGpuUpdate { + uint32_t scanout_id; + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; + uint8_t data[]; +} QEMU_PACKED VhostGpuUpdate; + +typedef struct VhostGpuGlScanout { + 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 VhostGpuGlScanout; + +typedef struct VhostGpuMsg { + VhostGpuRequest request; + uint32_t size; /* the following payload size */ + union { + VhostGpuCursorPos cursor_pos; + VhostGpuCursorUpdate cursor_update; + VhostGpuScanout scanout; + VhostGpuUpdate update; + VhostGpuGlScanout gl_scanout; + } payload; +} QEMU_PACKED VhostGpuMsg; + +static VhostGpuMsg m __attribute__ ((unused)); +#define VHOST_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; + int sock_fd; + GSource *renderer_source; + + 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= ); + +ssize_t vg_sock_fd_write(int sock, void *buf, ssize_t buflen, int fd); + +#endif diff --git a/contrib/vhost-user-gpu/main.c b/contrib/vhost-user-gpu/main.c new file mode 100644 index 0000000000..e14d0d2e8a --- /dev/null +++ b/contrib/vhost-user-gpu/main.c @@ -0,0 +1,953 @@ +/* + * 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 + +#include "vugpu.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; + QTAILQ_ENTRY(virtio_gpu_simple_resource) next; +}; + +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 < G_N_ELEMENTS(vg_cmd_str)) { + return vg_cmd_str[cmd]; + } else { + return "unknown"; + } +} + +ssize_t +vg_sock_fd_write(int sock, void *buf, ssize_t buflen, int fd) +{ + ssize_t size; + struct msghdr msg; + struct iovec iov; + union { + struct cmsghdr cmsghdr; + char control[CMSG_SPACE(sizeof(int))]; + } cmsgu; + struct cmsghdr *cmsg; + + iov.iov_base =3D 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; + } + + size =3D sendmsg(sock, &msg, 0); + + return size; +} + +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; + } + 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 { 0 ,}; + int i; + + dpy_info.hdr.type =3D VIRTIO_GPU_RESP_OK_DISPLAY_INFO; + for (i =3D 0; i < 1 /* 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 1024; + dpy_info.pmodes[i].r.height =3D 768; + /* } */ + } + + 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); + + 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; + } + res->image =3D pixman_image_create_bits(pformat, + c2d.width, + c2d.height, + NULL, 0); + 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_resource_destroy(VuGpu *g, + struct virtio_gpu_simple_resource *res) +{ + 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); + + 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); + + 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); + + 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); + + 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, + (uint8_t *)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; + struct virtio_gpu_scanout *scanout; + struct virtio_gpu_set_scanout ss; + + 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; + } + + if (ss.resource_id =3D=3D 0) { + scanout =3D &g->scanout[ss.scanout_id]; + if (scanout->resource_id) { + res =3D virtio_gpu_find_resource(g, scanout->resource_id); + if (res) { + res->scanout_bitmask &=3D ~(1 << ss.scanout_id); + } + } + if (ss.scanout_id =3D=3D 0) { + g_critical("%s: illegal scanout id specified %d", + __func__, ss.scanout_id); + cmd->error =3D VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; + return; + } + /* dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL); */ + scanout->width =3D 0; + scanout->height =3D 0; + 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]; + + 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; + + VhostGpuMsg msg =3D { + .request =3D VHOST_GPU_SCANOUT, + .size =3D sizeof(VhostGpuScanout), + .payload.scanout.scanout_id =3D ss.scanout_id, + .payload.scanout.width =3D scanout->width, + .payload.scanout.height =3D scanout->height + }; + vg_sock_fd_write(g->sock_fd, &msg, VHOST_GPU_HDR_SIZE + msg.size, -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); + + 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; + size_t bpp =3D PIXMAN_FORMAT_BPP(pixman_image_get_format(res->imag= e)) / 8; + size_t size =3D width * height * bpp; + + VhostGpuMsg *msg =3D g_malloc(VHOST_GPU_HDR_SIZE + + sizeof(VhostGpuUpdate) + size); + msg->request =3D VHOST_GPU_UPDATE; + msg->size =3D sizeof(VhostGpuUpdate) + size; + msg->payload.update.scanout_id =3D i; + msg->payload.update.x =3D extents->x1; + msg->payload.update.y =3D extents->y1; + msg->payload.update.width =3D width; + msg->payload.update.height =3D height; + pixman_image_t *i =3D + pixman_image_create_bits(pixman_image_get_format(res->image), + msg->payload.update.width, + msg->payload.update.height, + (uint32_t *)msg->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_sock_fd_write(g->sock_fd, msg, VHOST_GPU_HDR_SIZE + msg->size, = -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; + default: + g_debug("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; + + g_debug("%s\n", __func__); + + for (;;) { + 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)); + } + + 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_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)); + } + bool move =3D cursor.hdr.type !=3D VIRTIO_GPU_CMD_MOVE_CURSOR; + g_debug("%s move:%d\n", __func__, move); + + if (move) { + VhostGpuMsg msg =3D { + .request =3D cursor.resource_id ? + VHOST_GPU_CURSOR_POS : VHOST_GPU_CURSOR_POS_HIDE, + .size =3D sizeof(VhostGpuCursorPos), + .payload.cursor_pos =3D { + .scanout_id =3D cursor.pos.scanout_id, + .x =3D cursor.pos.x, + .y =3D cursor.pos.y, + } + }; + vg_sock_fd_write(g->sock_fd, &msg, + VHOST_GPU_HDR_SIZE + msg.size, -1); + } else { + VhostGpuMsg msg =3D { + .request =3D VHOST_GPU_CURSOR_UPDATE, + .size =3D sizeof(VhostGpuCursorUpdate), + .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_sock_fd_write(g->sock_fd, &msg, + VHOST_GPU_HDR_SIZE + msg.size, -1); + } + + 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 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]; + return 1; + default: + return 0; + } + + return 0; +} + +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) { + vg_virgl_init(g); + g->virgl_inited =3D true; + } + + g->virgl =3D virgl; +} + +static const VuDevIface vuiface =3D { + .set_features =3D vg_set_features, + .queue_set_started =3D vg_queue_set_started, + .process_msg =3D vg_process_msg, +}; + +static void +vg_reset(VuGpu *g) +{ + struct virtio_gpu_simple_resource *res, *tmp; + + vug_deinit(&g->dev); + + if (g->sock_fd !=3D -1) { + close(g->sock_fd); + g->sock_fd =3D -1; + } + + QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) { + vg_resource_destroy(g, res); + } +} + +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; +} + +char *socket_path; + +static GOptionEntry entries[] =3D { + { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &socket_path, + "Use UNIX socket path", "" }, + { 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 }; + + QTAILQ_INIT(&g.reslist); + QTAILQ_INIT(&g.fenceq); + + context =3D g_option_context_new("vhost-user-gpu sample"); + 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(1); + } + g_option_context_free(context); + + if (socket_path) { + int lsock =3D unix_sock_new(socket_path); + fd =3D accept(lsock, NULL, NULL); + close(lsock); + } else { + fd =3D 3; + } + if (fd =3D=3D -1) { + g_printerr("Invalid socket"); + exit(1); + } + 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_reset(&g); + + return 0; +} diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c new file mode 100644 index 0000000000..38fd5c0198 --- /dev/null +++ b/contrib/vhost-user-gpu/virgl.c @@ -0,0 +1,537 @@ +/* + * 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); +} + +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; + + g_debug("%s\n", __func__); + 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 { + 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)); +} + +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; + } + + VhostGpuMsg msg =3D { + .request =3D VHOST_GPU_GL_SCANOUT, + .size =3D sizeof(VhostGpuGlScanout), + .payload.gl_scanout.scanout_id =3D ss.scanout_id, + .payload.gl_scanout.x =3D ss.r.x, + .payload.gl_scanout.y =3D ss.r.y, + .payload.gl_scanout.width =3D ss.r.width, + .payload.gl_scanout.height =3D ss.r.height, + .payload.gl_scanout.fd_width =3D info.width, + .payload.gl_scanout.fd_height =3D info.height, + .payload.gl_scanout.fd_stride =3D info.stride, + .payload.gl_scanout.fd_flags =3D info.flags, + .payload.gl_scanout.fd_drm_fourcc =3D info.drm_fourcc + }; + vg_sock_fd_write(g->sock_fd, &msg, VHOST_GPU_HDR_SIZE + msg.size, = fd); + close(fd); + } else { + VhostGpuMsg msg =3D { + .request =3D VHOST_GPU_GL_SCANOUT, + .size =3D sizeof(VhostGpuGlScanout), + .payload.gl_scanout.scanout_id =3D ss.scanout_id, + }; + vg_sock_fd_write(g->sock_fd, &msg, VHOST_GPU_HDR_SIZE + msg.size, = -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, ret; + uint32_t ok; + + VUGPU_FILL_CMD(rf); + + for (i =3D 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) { + if (g->scanout[i].resource_id !=3D rf.resource_id) { + continue; + } + VhostGpuMsg msg =3D { + .request =3D VHOST_GPU_GL_UPDATE, + .size =3D sizeof(VhostGpuUpdate), + .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 + }; + ret =3D vg_sock_fd_write(g->sock_fd, &msg, + VHOST_GPU_HDR_SIZE + msg.size, -1); + g_return_if_fail(ret =3D=3D VHOST_GPU_HDR_SIZE + msg.size); + ret =3D read(g->sock_fd, &ok, sizeof(ok)); + g_return_if_fail(ret =3D=3D sizeof(ok)); + } +} + +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--; + } +} + +static struct virgl_renderer_callbacks virgl_cbs =3D { + .version =3D 1, + .write_fence =3D virgl_write_fence, +}; + +static void +vg_virgl_poll(VuDev *dev, int condition, void *data) +{ + virgl_renderer_poll(); +} + +int +vg_virgl_init(VuGpu *g) +{ + int ret; + + ret =3D virgl_renderer_init(g, + VIRGL_RENDERER_USE_EGL | + VIRGL_RENDERER_THREAD_SYNC, + &virgl_cbs); + if (ret !=3D 0) { + return ret; + } + + 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 ret; +} diff --git a/Makefile b/Makefile index d1222da359..fb2359f877 100644 --- a/Makefile +++ b/Makefile @@ -421,6 +421,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 \ @@ -722,6 +723,8 @@ vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvho= st-user.a $(call LINK, $^) 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) $< $@ \ diff --git a/Makefile.objs b/Makefile.objs index c20f8d35d7..417c115f26 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -194,6 +194,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/ 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 1d1caea9e7..675a4ba626 100755 --- a/configure +++ b/configure @@ -5533,6 +5533,7 @@ if test "$want_tools" =3D "yes" ; then if [ "$ivshmem" =3D "yes" ]; then tools=3D"ivshmem-client\$(EXESUF) ivshmem-server\$(EXESUF) $tools" tools=3D"vhost-user-input\$(EXESUF) $tools" + tools=3D"vhost-user-gpu\$(EXESUF) $tools" fi fi if test "$softmmu" =3D yes ; then @@ -7141,6 +7142,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 [ "$fdt" =3D "git" ]; then echo "config-host.h: subdir-dtc" >> $config_host_mak fi diff --git a/contrib/vhost-user-gpu/Makefile.objs b/contrib/vhost-user-gpu/= Makefile.objs new file mode 100644 index 0000000000..da7c4b8ba9 --- /dev/null +++ b/contrib/vhost-user-gpu/Makefile.objs @@ -0,0 +1,7 @@ +vhost-user-gpu-obj-y =3D main.o virgl.o + +main.o-cflags :=3D $(PIXMAN_CFLAGS) +main.o-libs :=3D $(PIXMAN_LIBS) + +virgl.o-cflags :=3D $(VIRGL_CFLAGS) +virgl.o-libs :=3D $(VIRGL_LIBS) --=20 2.17.1.906.g10fd178552