From nobody Wed May 14 01:26:40 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1526564457570175.74335695411207; Thu, 17 May 2018 06:40:57 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.24]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id C4E9030B27C2; Thu, 17 May 2018 13:40:55 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 8A5B6300172B; Thu, 17 May 2018 13:40:55 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 32AC44CA95; Thu, 17 May 2018 13:40:55 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id w4HDeq6H007134 for ; Thu, 17 May 2018 09:40:52 -0400 Received: by smtp.corp.redhat.com (Postfix) id 1B1B210B2B52; Thu, 17 May 2018 13:40:52 +0000 (UTC) Received: from t460.redhat.com (unknown [10.33.36.69]) by smtp.corp.redhat.com (Postfix) with ESMTP id 291E710B2B4E; Thu, 17 May 2018 13:40:50 +0000 (UTC) From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= To: libvir-list@redhat.com Date: Thu, 17 May 2018 14:40:43 +0100 Message-Id: <20180517134045.21610-3-berrange@redhat.com> In-Reply-To: <20180517134045.21610-1-berrange@redhat.com> References: <20180517134045.21610-1-berrange@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-loop: libvir-list@redhat.com Subject: [libvirt] [PATCH v3 2/4] qemu: support passing pre-opened UNIX socket listen FD X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.84 on 10.5.11.24 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.48]); Thu, 17 May 2018 13:40:56 +0000 (UTC) X-ZohoMail: RSF_0 Z_629925259 SPT_0 There is a race condition when spawning QEMU where libvirt has spawned QEMU but the monitor socket is not yet open. Libvirt has to repeatedly try to connect() to QEMU's monitor until eventually it succeeds, or times out. We use kill() to check if QEMU is still alive so we avoid waiting a long time if QEMU exited, but having a timeout at all is still unpleasant. With QEMU 2.12 we can pass in a pre-opened FD for UNIX domain or TCP sockets. If libvirt has called bind() and listen() on this FD, then we have a guarantee that libvirt can immediately call connect() and succeed without any race. Although we only really care about this for the monitor socket and agent socket, this patch does FD passing for all UNIX socket based character devices since there appears to be no downside to it. We don't do FD passing for TCP sockets, however, because it is only possible to pass a single FD, while some hostnames may require listening on multiple FDs to cover IPv4 and IPv6 concurrently. Signed-off-by: Daniel P. Berrang=C3=A9 Reviewed-by: John Ferlan --- src/qemu/qemu_command.c | 64 ++++++++++++++++++- src/qemu/qemu_command.h | 4 ++ .../disk-drive-write-cache.x86_64-latest.args | 3 +- ...irtio-scsi-reservations.x86_64-latest.args | 3 +- tests/qemuxml2argvmock.c | 16 +++++ 5 files changed, 84 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index c4237339bf..6834480e1f 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -4913,6 +4913,56 @@ qemuBuildChrChardevReconnectStr(virBufferPtr buf, } =20 =20 +int +qemuOpenChrChardevUNIXSocket(const virDomainChrSourceDef *dev) +{ + struct sockaddr_un addr; + socklen_t addrlen =3D sizeof(addr); + int fd; + + if ((fd =3D socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + virReportSystemError(errno, "%s", + _("Unable to create UNIX socket")); + goto error; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family =3D AF_UNIX; + if (virStrcpyStatic(addr.sun_path, dev->data.nix.path) =3D=3D NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("UNIX socket path '%s' too long"), + dev->data.nix.path); + goto error; + } + + if (unlink(dev->data.nix.path) < 0 && errno !=3D ENOENT) { + virReportSystemError(errno, + _("Unable to unlink %s"), + dev->data.nix.path); + goto error; + } + + if (bind(fd, (struct sockaddr *)&addr, addrlen) < 0) { + virReportSystemError(errno, + _("Unable to bind to UNIX socket path '%s'"), + dev->data.nix.path); + goto error; + } + + if (listen(fd, 1) < 0) { + virReportSystemError(errno, + _("Unable to listen to UNIX socket path '%s'"= ), + dev->data.nix.path); + goto error; + } + + return fd; + + error: + VIR_FORCE_CLOSE(fd); + return -1; +} + /* This function outputs a -chardev command line option which describes on= ly the * host side of the character device */ static char * @@ -5042,8 +5092,18 @@ qemuBuildChrChardevStr(virLogManagerPtr logManager, break; =20 case VIR_DOMAIN_CHR_TYPE_UNIX: - virBufferAsprintf(&buf, "socket,id=3D%s,path=3D", charAlias); - virQEMUBuildBufferEscapeComma(&buf, dev->data.nix.path); + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_CHARDEV_FD_PASS)) { + int fd =3D qemuOpenChrChardevUNIXSocket(dev); + if (fd < 0) + goto cleanup; + + virBufferAsprintf(&buf, "socket,id=3D%s,fd=3D%d", charAlias, f= d); + + virCommandPassFD(cmd, fd, VIR_COMMAND_PASS_FD_CLOSE_PARENT); + } else { + virBufferAsprintf(&buf, "socket,id=3D%s,path=3D", charAlias); + virQEMUBuildBufferEscapeComma(&buf, dev->data.nix.path); + } if (dev->data.nix.listen) virBufferAdd(&buf, nowait ? ",server,nowait" : ",server", -1); =20 diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 28bc33558b..f722b1be72 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -70,6 +70,10 @@ int qemuBuildTLSx509BackendProps(const char *tlspath, virQEMUCapsPtr qemuCaps, virJSONValuePtr *propsret); =20 +/* Open a UNIX socket for chardev FD passing */ +int +qemuOpenChrChardevUNIXSocket(const virDomainChrSourceDef *dev); + /* Generate '-device' string for chardev device */ int qemuBuildChrDeviceStr(char **deviceStr, diff --git a/tests/qemuxml2argvdata/disk-drive-write-cache.x86_64-latest.ar= gs b/tests/qemuxml2argvdata/disk-drive-write-cache.x86_64-latest.args index a63c5b7477..9e5b611351 100644 --- a/tests/qemuxml2argvdata/disk-drive-write-cache.x86_64-latest.args +++ b/tests/qemuxml2argvdata/disk-drive-write-cache.x86_64-latest.args @@ -17,8 +17,7 @@ file=3D/tmp/lib/domain--1-QEMUGuest1/master-key.aes \ -display none \ -no-user-config \ -nodefaults \ --chardev socket,id=3Dcharmonitor,path=3D/tmp/lib/domain--1-QEMUGuest1/moni= tor.sock,\ -server,nowait \ +-chardev socket,id=3Dcharmonitor,fd=3D1729,server,nowait \ -mon chardev=3Dcharmonitor,id=3Dmonitor,mode=3Dcontrol \ -rtc base=3Dutc \ -no-shutdown \ diff --git a/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.x86_64-la= test.args b/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.x86_64-lat= est.args index 768bc22f9f..ad88d3319a 100644 --- a/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.x86_64-latest.ar= gs +++ b/tests/qemuxml2argvdata/disk-virtio-scsi-reservations.x86_64-latest.ar= gs @@ -21,8 +21,7 @@ path=3D/path/to/qemu-pr-helper.sock \ -display none \ -no-user-config \ -nodefaults \ --chardev socket,id=3Dcharmonitor,path=3D/tmp/lib/domain--1-QEMUGuest1/moni= tor.sock,\ -server,nowait \ +-chardev socket,id=3Dcharmonitor,fd=3D1729,server,nowait \ -mon chardev=3Dcharmonitor,id=3Dmonitor,mode=3Dcontrol \ -rtc base=3Dutc \ -no-shutdown \ diff --git a/tests/qemuxml2argvmock.c b/tests/qemuxml2argvmock.c index 6d78063f00..b1edbdd0e6 100644 --- a/tests/qemuxml2argvmock.c +++ b/tests/qemuxml2argvmock.c @@ -37,6 +37,7 @@ #include "virtpm.h" #include "virutil.h" #include "qemu/qemu_interface.h" +#include "qemu/qemu_command.h" #include #include =20 @@ -227,3 +228,18 @@ qemuInterfaceOpenVhostNet(virDomainDefPtr def ATTRIBUT= E_UNUSED, vhostfd[i] =3D STDERR_FILENO + 42 + i; return 0; } + + +int +qemuOpenChrChardevUNIXSocket(const virDomainChrSourceDef *dev ATTRIBUTE_UN= USED) + +{ + /* We need to return an FD number for a UNIX listener socket, + * which will be given to QEMU via a CLI arg. We need a fixed + * number to get stable tests. This is obviously not a real + * FD number, so when virCommand closes the FD in the parent + * it will get EINVAL, but that's (hopefully) not going to + * be a problem.... + */ + return 1729; +} --=20 2.17.0 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list