From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610749805; cv=none; d=zohomail.com; s=zohoarc; b=EMrbSFxfI1k5qJrLh8FQnkzJe3bmPWvTdP/rq1hUvZrum3VcgMA+DKbADg3IGA+W0Ym7dnLvjPzZnDux6vG5z56Jt5NSDqHUp75W/Pt89WUlAxN/1JXzv1esdvyS3J0tD4/LPh2DqUIrhr2Vbxo+75FaH4KMjwhp2+u4VzJiQqU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610749805; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=cNCH3w2sIyT5mVUAezcQ1sg9EHxnR6roCdjkRoKS7tQ=; b=avFco+Qhs3VggDuSqcBDTRYc7WxbMmBpHJ0zfO9RSQS4h1QdJGMmMakXSu+bsSnAJikUNbmpnlp6eQZkfHKDZONLJlsvbm+b5slX0XCMQFXXLFwxXOeFeSMN9lCY+Kgl95qazIgCAiuwy8fieYOMFdMafk25hZYOoLjLtFIF99s= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610749805517382.03951277588385; Fri, 15 Jan 2021 14:30:05 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68655.122924 (Exim 4.92) (envelope-from ) id 1l0XbA-0005xg-NO; Fri, 15 Jan 2021 22:29:40 +0000 Received: by outflank-mailman (output) from mailman id 68655.122924; Fri, 15 Jan 2021 22:29:40 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XbA-0005xZ-Jn; Fri, 15 Jan 2021 22:29:40 +0000 Received: by outflank-mailman (input) for mailman id 68655; Fri, 15 Jan 2021 22:29:39 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xb9-0005xU-Ad for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:29:39 +0000 Received: from esa1.hc3370-68.iphmx.com (unknown [216.71.145.142]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id 8ce1b3db-d953-436e-bf8f-8b1765b1fc98; Fri, 15 Jan 2021 22:29:37 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 8ce1b3db-d953-436e-bf8f-8b1765b1fc98 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610749777; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=MRMXAAKCznyCgSXYTb+3ZlgjJBpHTfYOk0OXr6qFO7Y=; b=E2TTAMqWwbPocunA5yNxuEgTpf8fLBsqvXl/i79qNC/KDVkfoKFzyzX8 J4An7SlAOl9S+p+UvF/XCmsTAa6WLDthWzLKomXu6K6n32Zf1fjSr6wKZ J8JbFMdMCYZLIm4RfGIMSa/c8Y90Q9Dc8WRlr0JxVL1m4Tb6HxyBIhaiz 0=; Authentication-Results: esa1.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: 1lKwyqH+GPZE8/aqrvXeJpMTjLDHobceXdarvyMZcQUZSpFc7J+fDsqqO3qKJHvyeXoDI6GUEK bIISUC2e4qjKyFqKO3CNWCdV7xRoAJApaN4GkYYEIGu+lTf1wm3uVCfGt2hnnsB8LuNwGYXBU/ g22/3UyZE79YprhgO8gGjTM5CmICHfnVei3y+B4j6PORGagVg2Xf3ZqhBFyg2pYgYW7rqv3Crg nRs8qVNRVfnTkSA1eyhZ6Z7JvhyAJO26BsvIXziY+44+lMF9oayFAfsxaphzsZFqhuajIWtj9Y Oro= X-SBRS: 5.1 X-MesageID: 35590778 X-Ironport-Server: esa1.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35590778" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Doug Goldstein" Subject: [PATCH v2 1/2] automation/: add Ubuntu:focal container Date: Fri, 15 Jan 2021 22:28:40 +0000 Message-ID: <63bf3ced2500eb9be579674ff2f0016ac9d73e70.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Doug Goldstein Acked-by: Christian Lindig --- Changed since v1: * dropped python-dev and markdown --- automation/build/ubuntu/focal.dockerfile | 48 ++++++++++++++++++++++++ automation/scripts/containerize | 1 + 2 files changed, 49 insertions(+) create mode 100644 automation/build/ubuntu/focal.dockerfile diff --git a/automation/build/ubuntu/focal.dockerfile b/automation/build/ub= untu/focal.dockerfile new file mode 100644 index 0000000000..c1c1f8d58f --- /dev/null +++ b/automation/build/ubuntu/focal.dockerfile @@ -0,0 +1,48 @@ +FROM ubuntu:20.04 +LABEL maintainer.name=3D"The Xen Project " \ + maintainer.email=3D"xen-devel@lists.xenproject.org" + +ENV DEBIAN_FRONTEND=3Dnoninteractive +ENV USER root + +RUN mkdir /build +WORKDIR /build + +# build depends +RUN apt-get update && \ + apt-get --quiet --yes install \ + build-essential \ + zlib1g-dev \ + libncurses5-dev \ + libssl-dev \ + python3-dev \ + xorg-dev \ + uuid-dev \ + libyajl-dev \ + libaio-dev \ + libglib2.0-dev \ + clang \ + libpixman-1-dev \ + pkg-config \ + flex \ + bison \ + gettext \ + acpica-tools \ + bin86 \ + bcc \ + liblzma-dev \ + libc6-dev-i386 \ + libnl-3-dev \ + ocaml-nox \ + libfindlib-ocaml-dev \ + libsystemd-dev \ + transfig \ + pandoc \ + checkpolicy \ + wget \ + git \ + nasm \ + && \ + apt-get autoremove -y && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists* /tmp/* /var/tmp/* diff --git a/automation/scripts/containerize b/automation/scripts/container= ize index c8c3c20fa2..da45baed4e 100755 --- a/automation/scripts/containerize +++ b/automation/scripts/containerize @@ -28,6 +28,7 @@ case "_${CONTAINER}" in _centos7) CONTAINER=3D"${BASE}/centos:7" ;; _centos72) CONTAINER=3D"${BASE}/centos:7.2" ;; _fedora) CONTAINER=3D"${BASE}/fedora:29";; + _focal) CONTAINER=3D"${BASE}/ubuntu:focal" ;; _jessie) CONTAINER=3D"${BASE}/debian:jessie" ;; _stretch|_) CONTAINER=3D"${BASE}/debian:stretch" ;; _unstable|_) CONTAINER=3D"${BASE}/debian:unstable" ;; --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610749826; cv=none; d=zohomail.com; s=zohoarc; b=Dl9McbnCdM1zoR1RsrIKkqAooe4lm3mbWhTfK4h+nHKfWb9nPPNa/+HfbIpC0X3yEZnNApqHmT7URAnVffHZlwq4WlCZ2mReECt5VDsPdriPGprVh0Jhkyg85WT1CNGgSsfv2/2DV2FK1pp0JXvaJ0qx2jvK025kHKci/nxgT4Q= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610749826; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=KWgjrTEuiayZF8/BgvVMzkhWJRnIaXA0O75FslQATXA=; b=aR1gvsVib9ykftQy0kWW5sX2SkgeJpfxDGaWj8KLA2svHDq09Dz/zhnHezyVj35u57TZcFxNfxVVKZPiptNz93D0XfqN3JkG5LNqUojEUyIdohbsz2sodc7uNIlEdljFstQ2cBK+vOGOclKuZnVi4FzAAL4G22exvuZOGmwXmmQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610749826333482.3759595091981; Fri, 15 Jan 2021 14:30:26 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68669.123008 (Exim 4.92) (envelope-from ) id 1l0Xbe-00078G-Cp; Fri, 15 Jan 2021 22:30:10 +0000 Received: by outflank-mailman (output) from mailman id 68669.123008; Fri, 15 Jan 2021 22:30:10 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xbe-000788-8I; Fri, 15 Jan 2021 22:30:10 +0000 Received: by outflank-mailman (input) for mailman id 68669; Fri, 15 Jan 2021 22:30:09 +0000 Received: from all-amaz-eas1.inumbo.com ([34.197.232.57] helo=us1-amaz-eas2.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xbd-00062b-8R for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:30:09 +0000 Received: from esa6.hc3370-68.iphmx.com (unknown [216.71.155.175]) by us1-amaz-eas2.inumbo.com (Halon) with ESMTPS id a99ab02d-4f0e-4962-a36f-76d7ea59a394; Fri, 15 Jan 2021 22:29:50 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: a99ab02d-4f0e-4962-a36f-76d7ea59a394 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610749790; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=/5XsBUrTdTecpeweYTHsOPIvx533UyFebG9xiAWZm4o=; b=S523PR3ikHZ7+pu6Yhh7EbWptRIrCQya3MIk5ARLG737ltHYZ9jEK0g3 P3WdNHMyUQbgsm6di+0r4LzsYgeOx9Ytz2Da6wO0vbJfFhoF+yzVNiwyQ iValc2/YSi1lBPspFEAQLJNc75mEvsUgUv1J8Ki1fTRtG4PkzrYfmJL0k 4=; Authentication-Results: esa6.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: +uE/EqKNKp6zxkGhJ1iFDx0/H3s4L/hLIdbYsNOxuclIOzTmKkH7g1lAlqt/OS6Mm1z9ukDUWp L51Dq689FH+nGPOeW1r8RyMmBDDHgG+kWd1nIC4LOwWRn6gZKev1gp8uFWCMSTqNFf6SJnRfv8 LuztWlWKqlIpZVGyvEW68dX/VaobgPthen1Eri0UbZ83FP8Wg1a0FH5TIsjfC8ruN+AA+U4+zm 5M1Efb4fjrrQlRGtFfhN96YAlyZFTLGZFJhXnOs0D4KrPM8KalfTARsQpJZTMtOJFyA4wi8hoM WWs= X-SBRS: 5.1 X-MesageID: 35434452 X-Ironport-Server: esa6.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35434452" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: Juergen Gross , Andrew Cooper , George Dunlap , "Ian Jackson" , Jan Beulich , Julien Grall , Stefano Stabellini , Wei Liu , Paul Durrant , Julien Grall Subject: [PATCH v2 1/8] tools/xenstore: add live update command to xenstore-control Date: Fri, 15 Jan 2021 22:28:43 +0000 Message-ID: <89d027f191af20c39dbf9d37dfcd602c2669511d.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) Content-Type: text/plain; charset="utf-8" From: Juergen Gross Add the "live-update" command to xenstore-control enabling updating xenstored to a new version in a running Xen system. With -c it is possible to pass a different command line to the new instance of xenstored. This will replace the command line used for the invocation of the just running xenstored instance. The running xenstored (or xenstore-stubdom) needs to support live updating, of course. For now just add a small dummy handler to C xenstore denying any live update action. Signed-off-by: Juergen Gross Reviewed-by: Paul Durrant Reviewed-by: Julien Grall Acked-by: Christian Lindig --- docs/misc/xenstore.txt | 21 ++ tools/xenstore/Makefile | 3 +- tools/xenstore/xenstore_control.c | 332 ++++++++++++++++++++++++++++-- 3 files changed, 339 insertions(+), 17 deletions(-) diff --git a/docs/misc/xenstore.txt b/docs/misc/xenstore.txt index 2081f20f55..1480742330 100644 --- a/docs/misc/xenstore.txt +++ b/docs/misc/xenstore.txt @@ -317,6 +317,27 @@ CONTROL |[|] Current commands are: check checks xenstored innards + live-update||+ + perform a live-update of the Xenstore daemon, only to + be used via xenstore-control command. + are implementation specific and are used for + different steps of the live-update processing. Currently + supported are: + -f specify new daemon binary + -b specify size of new stubdom binary + -d transfer chunk of new + stubdom binary + -c specify new command line to use + -s [-t ] [-F] start live update process (-t specifies + timeout in seconds to wait for active transactions + to finish, default is 60 seconds; -F will force + live update to happen even with running transactions + after timeout elapsed) + -a abort live update handling + All sub-options will return "OK" in case of success or an + error string in case of failure. -s can return "BUSY" in case + of an active transaction, a retry of -s can be done in that + case. log|on turn xenstore logging on log|off diff --git a/tools/xenstore/Makefile b/tools/xenstore/Makefile index 9a0f0d012d..ab89e22d3a 100644 --- a/tools/xenstore/Makefile +++ b/tools/xenstore/Makefile @@ -11,6 +11,7 @@ CFLAGS +=3D -include $(XEN_ROOT)/tools/config.h CFLAGS +=3D -I./include CFLAGS +=3D $(CFLAGS_libxenevtchn) CFLAGS +=3D $(CFLAGS_libxenctrl) +CFLAGS +=3D $(CFLAGS_libxenguest) CFLAGS +=3D $(CFLAGS_libxentoolcore) CFLAGS +=3D -DXEN_LIB_STORED=3D"\"$(XEN_LIB_STORED)\"" CFLAGS +=3D -DXEN_RUN_STORED=3D"\"$(XEN_RUN_STORED)\"" @@ -81,7 +82,7 @@ xenstore: xenstore_client.o $(CC) $< $(LDFLAGS) $(LDLIBS_libxenstore) $(LDLIBS_libxentoolcore) $(SOCK= ET_LIBS) -o $@ $(APPEND_LDFLAGS) =20 xenstore-control: xenstore_control.o - $(CC) $< $(LDFLAGS) $(LDLIBS_libxenstore) $(LDLIBS_libxentoolcore) $(SOCK= ET_LIBS) -o $@ $(APPEND_LDFLAGS) + $(CC) $< $(LDFLAGS) $(LDLIBS_libxenstore) $(LDLIBS_libxenctrl) $(LDLIBS_l= ibxenguest) $(LDLIBS_libxentoolcore) $(SOCKET_LIBS) -o $@ $(APPEND_LDFLAGS) =20 xs_tdb_dump: xs_tdb_dump.o utils.o tdb.o talloc.o $(CC) $^ $(LDFLAGS) -o $@ $(APPEND_LDFLAGS) diff --git a/tools/xenstore/xenstore_control.c b/tools/xenstore/xenstore_co= ntrol.c index afa04495a7..5ca015a07d 100644 --- a/tools/xenstore/xenstore_control.c +++ b/tools/xenstore/xenstore_control.c @@ -1,9 +1,311 @@ +#define _GNU_SOURCE +#include #include #include #include +#include +#include +#include =20 #include "xenstore.h" =20 +/* Add a string plus terminating 0 byte to buf, returning new len. */ +static int add_to_buf(char **buf, const char *val, int len) +{ + int vallen =3D strlen(val) + 1; + + if (len < 0) + return -1; + + *buf =3D realloc(*buf, len + vallen); + if (!*buf) + return -1; + + strcpy(*buf + len, val); + + return len + vallen; +} + +static int live_update_start(struct xs_handle *xsh, bool force, unsigned i= nt to) +{ + int len =3D 0; + char *buf =3D NULL, *ret; + time_t time_start; + + if (asprintf(&ret, "%u", to) < 0) + return 1; + len =3D add_to_buf(&buf, "-s", len); + len =3D add_to_buf(&buf, "-t", len); + len =3D add_to_buf(&buf, ret, len); + free(ret); + if (force) + len =3D add_to_buf(&buf, "-F", len); + if (len < 0) + return 1; + + for (time_start =3D time(NULL); time(NULL) - time_start < to;) { + ret =3D xs_control_command(xsh, "live-update", buf, len); + if (!ret) + goto err; + if (strcmp(ret, "BUSY")) + break; + } + + if (strcmp(ret, "OK")) + goto err; + + free(buf); + free(ret); + + return 0; + + err: + fprintf(stderr, "Starting live update failed:\n%s\n", + ret ? : strerror(errno)); + free(buf); + free(ret); + + return 3; +} + +static int live_update_cmdline(struct xs_handle *xsh, const char *cmdline) +{ + int len =3D 0, rc =3D 0; + char *buf =3D NULL, *ret; + + len =3D add_to_buf(&buf, "-c", len); + len =3D add_to_buf(&buf, cmdline, len); + if (len < 0) + return 1; + + ret =3D xs_control_command(xsh, "live-update", buf, len); + free(buf); + if (!ret || strcmp(ret, "OK")) { + fprintf(stderr, "Setting update binary failed:\n%s\n", + ret ? : strerror(errno)); + rc =3D 3; + } + free(ret); + + return rc; +} + +static int send_kernel_blob(struct xs_handle *xsh, const char *binary) +{ + int rc =3D 0, len =3D 0; + xc_interface *xch; + struct xc_dom_image *dom; + char *ret, *buf =3D NULL; + size_t off, sz; +#define BLOB_CHUNK_SZ 2048 + + xch =3D xc_interface_open(NULL, NULL, 0); + if (!xch) { + fprintf(stderr, "xc_interface_open() failed\n"); + return 1; + } + + dom =3D xc_dom_allocate(xch, NULL, NULL); + if (!dom) { + rc =3D 1; + goto out_close; + } + + rc =3D xc_dom_kernel_file(dom, binary); + if (rc) { + rc =3D 1; + goto out_rel; + } + + if (asprintf(&ret, "%zu", dom->kernel_size) < 0) { + rc =3D 1; + goto out_rel; + } + len =3D add_to_buf(&buf, "-b", len); + len =3D add_to_buf(&buf, ret, len); + free(ret); + if (len < 0) { + rc =3D 1; + goto out_rel; + } + ret =3D xs_control_command(xsh, "live-update", buf, len); + free(buf); + if (!ret || strcmp(ret, "OK")) { + fprintf(stderr, "Starting live update failed:\n%s\n", + ret ? : strerror(errno)); + rc =3D 3; + } + free(ret); + if (rc) + goto out_rel; + + /* buf capable to hold "-d" <1..2048> BLOB_CHUNK_SZ and a terminating = 0. */ + buf =3D malloc(3 + 5 + BLOB_CHUNK_SZ + 1); + if (!buf) { + rc =3D 1; + goto out_rel; + } + + strcpy(buf, "-d"); + sz =3D BLOB_CHUNK_SZ; + for (off =3D 0; off < dom->kernel_size; off +=3D BLOB_CHUNK_SZ) { + if (dom->kernel_size - off < BLOB_CHUNK_SZ) + sz =3D dom->kernel_size - off; + sprintf(buf + 3, "%zu", sz); + len =3D 3 + strlen(buf + 3) + 1; + memcpy(buf + len, dom->kernel_blob + off, sz); + buf[len + sz] =3D 0; + len +=3D sz + 1; + ret =3D xs_control_command(xsh, "live-update", buf, len); + if (!ret || strcmp(ret, "OK")) { + fprintf(stderr, "Transfer of new binary failed:\n%s\n", + ret ? : strerror(errno)); + rc =3D 3; + free(ret); + break; + } + free(ret); + } + + free(buf); + + out_rel: + xc_dom_release(dom); + + out_close: + xc_interface_close(xch); + + return rc; +} + +/* + * Live update of Xenstore stubdom + * + * Sequence of actions: + * 1. transfer new stubdom binary + * a) specify size + * b) transfer unpacked binary in chunks + * 2. transfer new cmdline (optional) + * 3. start update (includes flags) + */ +static int live_update_stubdom(struct xs_handle *xsh, const char *binary, + const char *cmdline, bool force, unsigned i= nt to) +{ + int rc; + + rc =3D send_kernel_blob(xsh, binary); + if (rc) + goto abort; + + if (cmdline) { + rc =3D live_update_cmdline(xsh, cmdline); + if (rc) + goto abort; + } + + rc =3D live_update_start(xsh, force, to); + if (rc) + goto abort; + + return 0; + + abort: + xs_control_command(xsh, "live-update", "-a", 3); + return rc; +} + +/* + * Live update of Xenstore daemon + * + * Sequence of actions: + * 1. transfer new binary filename + * 2. transfer new cmdline (optional) + * 3. start update (includes flags) + */ +static int live_update_daemon(struct xs_handle *xsh, const char *binary, + const char *cmdline, bool force, unsigned in= t to) +{ + int len =3D 0, rc; + char *buf =3D NULL, *ret; + + len =3D add_to_buf(&buf, "-f", len); + len =3D add_to_buf(&buf, binary, len); + if (len < 0) + return 1; + ret =3D xs_control_command(xsh, "live-update", buf, len); + free(buf); + if (!ret || strcmp(ret, "OK")) { + fprintf(stderr, "Setting update binary failed:\n%s\n", + ret ? : strerror(errno)); + free(ret); + return 3; + } + free(ret); + + if (cmdline) { + rc =3D live_update_cmdline(xsh, cmdline); + if (rc) + goto abort; + } + + rc =3D live_update_start(xsh, force, to); + if (rc) + goto abort; + + return 0; + + abort: + xs_control_command(xsh, "live-update", "-a", 3); + return rc; +} + +static int live_update(struct xs_handle *xsh, int argc, char **argv) +{ + int rc =3D 0; + unsigned int i, to =3D 60; + char *binary =3D NULL, *cmdline =3D NULL, *val; + bool force =3D false; + + for (i =3D 0; i < argc; i++) { + if (!strcmp(argv[i], "-c")) { + i++; + if (i =3D=3D argc) { + fprintf(stderr, "Missing command line value\n"); + rc =3D 2; + goto out; + } + cmdline =3D argv[i]; + } else if (!strcmp(argv[i], "-t")) { + i++; + if (i =3D=3D argc) { + fprintf(stderr, "Missing timeout value\n"); + rc =3D 2; + goto out; + } + to =3D atoi(argv[i]); + } else if (!strcmp(argv[i], "-F")) + force =3D true; + else + binary =3D argv[i]; + } + + if (!binary) { + fprintf(stderr, "Missing binary specification\n"); + rc =3D 2; + goto out; + } + + val =3D xs_read(xsh, XBT_NULL, "/tool/xenstored/domid", &i); + if (val) + rc =3D live_update_stubdom(xsh, binary, cmdline, force, to); + else + rc =3D live_update_daemon(xsh, binary, cmdline, force, to); + + free(val); + + out: + return rc; +} =20 int main(int argc, char **argv) { @@ -20,22 +322,6 @@ int main(int argc, char **argv) goto out; } =20 - for (p =3D 2; p < argc; p++) - len +=3D strlen(argv[p]) + 1; - if (len) { - par =3D malloc(len); - if (!par) { - fprintf(stderr, "Allocation error.\n"); - rc =3D 1; - goto out; - } - len =3D 0; - for (p =3D 2; p < argc; p++) { - memcpy(par + len, argv[p], strlen(argv[p]) + 1); - len +=3D strlen(argv[p]) + 1; - } - } - xsh =3D xs_open(0); if (xsh =3D=3D NULL) { fprintf(stderr, "Failed to contact Xenstored.\n"); @@ -43,6 +329,19 @@ int main(int argc, char **argv) goto out; } =20 + if (!strcmp(argv[1], "live-update")) { + rc =3D live_update(xsh, argc - 2, argv + 2); + goto out_close; + } + + for (p =3D 2; p < argc; p++) + len =3D add_to_buf(&par, argv[p], len); + if (len < 0) { + fprintf(stderr, "Allocation error.\n"); + rc =3D 1; + goto out_close; + } + ret =3D xs_control_command(xsh, argv[1], par, len); if (!ret) { rc =3D 3; @@ -59,6 +358,7 @@ int main(int argc, char **argv) } else if (strlen(ret) > 0) printf("%s\n", ret); =20 + out_close: xs_close(xsh); =20 out: --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751169; cv=none; d=zohomail.com; s=zohoarc; b=Mb0yTXYnxC6k4sCjF0u0gtSOs2zaoI2o+LufeK14BZowNP4Y66a5gmpXLeEpF2KKqKmRNiztaVwQtr+oOJpPn55H1jA1uNjrX7blmeX5JjmVK4rblbW4L0ng3qfBlCXjUSudfY4LyDVW8L3gCPdG4WD5sEXX3X3BOd1ighGTKiw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751169; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=P1kWT4LejG00vRtq8/nO3Dyp2zUg7zp6Cj7l4OWA8BQ=; b=mcgb4iVMpD/EjD9ol93P+PFQR65TU1TlgB9h56Ki/nKoNgtRg7LJceJ4rXov+m5IHXu2rsg88DxjKi4a8JVK/IgqpNtHABe+JX1v1/LTRseigiJYX4HzqNyrFx3dpQ6ny8r75xw9XctU0a7fLlzaywcLt86CfVJgdVM7//EFKn8= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610751169357736.2691311517355; Fri, 15 Jan 2021 14:52:49 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68724.123104 (Exim 4.92) (envelope-from ) id 1l0XxK-0001cM-Cq; Fri, 15 Jan 2021 22:52:34 +0000 Received: by outflank-mailman (output) from mailman id 68724.123104; Fri, 15 Jan 2021 22:52:34 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XxK-0001cA-9Z; Fri, 15 Jan 2021 22:52:34 +0000 Received: by outflank-mailman (input) for mailman id 68724; Fri, 15 Jan 2021 22:52:33 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XxI-0001Wj-Uw for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:52:32 +0000 Received: from esa2.hc3370-68.iphmx.com (unknown [216.71.145.153]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id 1366477a-b333-4dbf-9fcb-4f25fa92549d; Fri, 15 Jan 2021 22:52:27 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 1366477a-b333-4dbf-9fcb-4f25fa92549d DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751147; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=+OLxkWnJxNPmZrvNFRL0MUtN+MtApJbP2A5WxD+DnyM=; b=a+t0ffsA7VIZqI/z4eElpcbh9GUtyggG7NnL0k7xm597ZqSw+YL367pn s7LjtWfTDhjhBHz+6TCW/oNLpfwSAN0TW6NK4O1PNdpzredbDdKRavrai CQ+6kV8K7/fk2sTto3MDnI0Gs0aYAky+sct1cSAbrdMlPbAVbIDFCQ3bU 4=; Authentication-Results: esa2.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: V53lsOBJVkj3wb96l9yOL6SkECcF2upuHaHmTNxngYM3/flR16vvt099LsvoxFo/BD+Q+PFI7a TadbGOW8pJCyvIsyg0yvPsBZc8mA7RMmDKQBI366IIvLma9VvtzuB7tbZeqkxwwF550zwg7W1f 5O+iufqIUeysATXV7nVDbW2MIjrcyNEy2kef8/cgiQNasTj43U7Pu/n/yMMWONDPMJ7gEpVxDU 0wIg5zVr0nHClzSxJqSWOBuDyBwioKHtJP107nxlUD6TbQKDDPRhjlHm64nxl8cmwyE24fU/T7 2Ak= X-SBRS: 5.1 X-MesageID: 35263547 X-Ironport-Server: esa2.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35263547" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu Subject: [PATCH v2 1/2] tools/ocaml/xenstored: trim txhistory on xenbus reconnect Date: Fri, 15 Jan 2021 22:28:52 +0000 Message-ID: <08cd2d0b9af30f544ab63476b8f7d02d2f9c3fd8.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) There is a global history, containing transactions from the past 0.05s, whi= ch get trimmed whenever any transaction commits or aborts. Destroying a domain will cause xenopsd to perform some transactions deletin= g the tree, so that is fine. But I think that a domain can abuse the xenbus reconnect facility to cause = a large history to be recorded - provided that noone does any transactions on the system inbetwee= n, which may be difficult to achieve given squeezed's constant pinging. The theoretical situation is like this: - a domain starts a transaction, creates as large a tree as it can, commits= it. Then repeatedly: - start a transaction, do nothing with it, start a transaction, delete = part of the large tree, write some new unique data there, don't commit - cause a xenbus reconnect (I think this can be done by writing somethi= ng to the ring). This causes all transactions/watches for the connection to= be cleared, but NOT the history, there were no commits, so nobody trimmed = the history, i.e. it the history can contain transactions from more than ju= st 0.05s - loop back and start more transactions, you can keep this up indefinit= ely without hitting quotas Now there is a periodic History.trim running every 0.05s, so I don't think = you can do much damage with it. But lets be safe an trim the transaction history anyway on reconnect. Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Christian Lindig --- Changed since V1: * post publicly now that the XSA is out (not a security issue) --- tools/ocaml/xenstored/connection.ml | 2 +- tools/ocaml/xenstored/history.ml | 4 ++++ tools/ocaml/xenstored/process.ml | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/co= nnection.ml index eb23c3af7a..1cf24beafd 100644 --- a/tools/ocaml/xenstored/connection.ml +++ b/tools/ocaml/xenstored/connection.ml @@ -47,7 +47,7 @@ let mark_as_bad con =3D =20 let initial_next_tid =3D 1 =20 -let reconnect con =3D +let do_reconnect con =3D Xenbus.Xb.reconnect con.xb; (* dom is the same *) Hashtbl.clear con.transactions; diff --git a/tools/ocaml/xenstored/history.ml b/tools/ocaml/xenstored/histo= ry.ml index f39565bff5..3899353da8 100644 --- a/tools/ocaml/xenstored/history.ml +++ b/tools/ocaml/xenstored/history.ml @@ -53,6 +53,10 @@ let end_transaction txn con tid commit =3D trim ~txn (); success =20 +let reconnect con =3D + trim (); + Connection.do_reconnect con + let push (x: history_record) =3D let dom =3D x.con.Connection.dom in match dom with diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/proce= ss.ml index dd50456ad5..da8e9cdb26 100644 --- a/tools/ocaml/xenstored/process.ml +++ b/tools/ocaml/xenstored/process.ml @@ -705,7 +705,7 @@ let do_input store cons doms con =3D Connection.do_input con with Xenbus.Xb.Reconnect -> info "%s requests a reconnect" (Connection.get_domstr con); - Connection.reconnect con; + History.reconnect con; info "%s reconnection complete" (Connection.get_domstr con); false | Failure exp -> @@ -744,7 +744,7 @@ let do_output _store _cons _doms con =3D ignore (Connection.do_output con) with Xenbus.Xb.Reconnect -> info "%s requests a reconnect" (Connection.get_domstr con); - Connection.reconnect con; + History.reconnect con; info "%s reconnection complete" (Connection.get_domstr con) ) =20 --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751557; cv=none; d=zohomail.com; s=zohoarc; b=Q1/pznvR++E6LP6ZvWoC67E/NKEx+zYRLNIgk+fq52A1VXPvjcilVR1IX7HPch878kJtEUaPkwoSPcYKryFMlKLblFmDUmJRyzJmQEfKe9en1j98OcDaKjJ0ceZ6yXbo1QWWv//HR4HGZVloxFJnROTS9vkMXJWtkTAI5llgyMs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751557; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=Dwh4ZKWjqYqrwpf/HjaI+clnXBlk92MFckb44/KGpbA=; b=NlCstxYVLxnedUnYCeIGyb5UfHSqhSjy4RhVNnCMfERGRYZdfORkJObi/0sNxFXXqUOVfuuGz27+AQ1UB2egsE7ZDK+plfiSjXJVrYrbgsRXjeddrJQqQWVR7opCqV1MwmFCaflVAXVvNQq1lnWKGf4oezHwFMSk3DFN1u00nxw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610751557123585.0600143667192; Fri, 15 Jan 2021 14:59:17 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68776.123253 (Exim 4.92) (envelope-from ) id 1l0Y3V-0003Da-3l; Fri, 15 Jan 2021 22:58:57 +0000 Received: by outflank-mailman (output) from mailman id 68776.123253; Fri, 15 Jan 2021 22:58:57 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Y3U-0003DG-RV; Fri, 15 Jan 2021 22:58:56 +0000 Received: by outflank-mailman (input) for mailman id 68776; Fri, 15 Jan 2021 22:58:55 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XyR-0001Wj-1U for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:53:43 +0000 Received: from esa3.hc3370-68.iphmx.com (unknown [216.71.145.155]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id ced2fe17-0b7e-4cba-b8c8-82a98235f323; Fri, 15 Jan 2021 22:52:41 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: ced2fe17-0b7e-4cba-b8c8-82a98235f323 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751161; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=4ctOgSWO09hqKqGkR6ZkgXXqWAiaRVGebfeBTPPCY0I=; b=EdTEXkyNh0I1UbsJqBGs52L8iO3xrO6xPJmkkQQVLnixFje/GBxMzSq1 ud463C9/yzAZE2aqy1iIQ7cVZHjiUENBx1wqPzx05SdePHpNaV+8P01tO hRsIvwbjPJEDjgsuHefVCQZfIjKse4QvYlXIuj0ErUK2CP4sBsTAzDyw6 c=; Authentication-Results: esa3.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: Pn3IpfsYIeiLaqfg+u1KQDW6u72YGkslMb3EXP8c2D4Z5kRJ6tutZQgp2nt78k7Ztn5/m/sBrR tLQvnTPuQkNtKQbZqWb+yX6d8eRuxZFFzJzbmAqMHAIIZT2RwVOKdRjhrPhlJKJYbjvb9uQeVy DbNNxwaxQ0T40b4wX2pPFnucXRfDDcdFZCqqLS2uanX8QM4xTnU43hNIAVTlsSmlQYzHHThKpb eej3rHt2ECdbMpap1B/EVqO+oRWKddqjMtUUsz3wwjoDtiu4T3RH8m8nUUi0hK1/YFRfkPgOYg bPQ= X-SBRS: 5.1 X-MesageID: 35217500 X-Ironport-Server: esa3.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35217500" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu Subject: [PATCH v4 1/4] tools/ocaml/xenstored: replace hand rolled GC with weak GC references Date: Fri, 15 Jan 2021 22:28:55 +0000 Message-ID: X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) The code here is attempting to reduce memory usage by sharing common substrings in the tree: it replaces strings with ints, and keeps a string->int map that gets manually garbage collected using a hand-rolled mark and sweep algorithm. This is unnecessary: OCaml already has a mark-and-sweep Garbage Collector runtime, and sharing of common strings in tree nodes can be achieved through Weak references: if the string hasn't been seen yet it gets added to the Weak reference table, and if it has we use the entry from the table instead, thus storing a string only once. When the string is no longer referenced OCaml's GC will drop it from the weak table: there is no need to manually do a mark-and-sweep, or to tell OCaml when to drop it. Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Christian Lindig --- Changed since V3: * repost after XSA to avoid conflicts --- tools/ocaml/xenstored/connection.ml | 3 -- tools/ocaml/xenstored/history.ml | 14 ------ tools/ocaml/xenstored/store.ml | 11 ++--- tools/ocaml/xenstored/symbol.ml | 68 ++++++----------------------- tools/ocaml/xenstored/symbol.mli | 21 ++------- tools/ocaml/xenstored/xenstored.ml | 16 +------ 6 files changed, 24 insertions(+), 109 deletions(-) diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/co= nnection.ml index 1cf24beafd..51041dde8e 100644 --- a/tools/ocaml/xenstored/connection.ml +++ b/tools/ocaml/xenstored/connection.ml @@ -334,9 +334,6 @@ let has_more_work con =3D =20 let incr_ops con =3D con.stat_nb_ops <- con.stat_nb_ops + 1 =20 -let mark_symbols con =3D - Hashtbl.iter (fun _ t -> Store.mark_symbols (Transaction.get_store t)) co= n.transactions - let stats con =3D Hashtbl.length con.watches, con.stat_nb_ops =20 diff --git a/tools/ocaml/xenstored/history.ml b/tools/ocaml/xenstored/histo= ry.ml index 3899353da8..ba5c9cb571 100644 --- a/tools/ocaml/xenstored/history.ml +++ b/tools/ocaml/xenstored/history.ml @@ -22,20 +22,6 @@ type history_record =3D { =20 let history : history_record list ref =3D ref [] =20 -(* Called from periodic_ops to ensure we don't discard symbols that are st= ill needed. *) -(* There is scope for optimisation here, since in consecutive commits one = commit's `after` - * is the same thing as the next commit's `before`, but not all commits in= history are - * consecutive. *) -let mark_symbols () =3D - (* There are gaps where dom0's commits are missing. Otherwise we could as= sume that - * each element's `before` is the same thing as the next element's `after` - * since the next element is the previous commit *) - List.iter (fun hist_rec -> - Store.mark_symbols hist_rec.before; - Store.mark_symbols hist_rec.after; - ) - !history - (* Keep only enough commit-history to protect the running transactions tha= t we are still tracking *) (* There is scope for optimisation here, replacing List.filter with someth= ing more efficient, * probably on a different list-like structure. *) diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml index a3be2e6bbe..9c226e4ef7 100644 --- a/tools/ocaml/xenstored/store.ml +++ b/tools/ocaml/xenstored/store.ml @@ -46,18 +46,18 @@ let add_child node child =3D =20 let exists node childname =3D let childname =3D Symbol.of_string childname in - List.exists (fun n -> n.name =3D childname) node.children + List.exists (fun n -> Symbol.equal n.name childname) node.children =20 let find node childname =3D let childname =3D Symbol.of_string childname in - List.find (fun n -> n.name =3D childname) node.children + List.find (fun n -> Symbol.equal n.name childname) node.children =20 let replace_child node child nchild =3D (* this is the on-steroid version of the filter one-replace one *) let rec replace_one_in_list l =3D match l with | [] -> [] - | h :: tl when h.name =3D child.name -> nchild :: tl + | h :: tl when Symbol.equal h.name child.name -> nchild :: tl | h :: tl -> h :: replace_one_in_list tl in { node with children =3D (replace_one_in_list node.children) } @@ -67,7 +67,7 @@ let del_childname node childname =3D let rec delete_one_in_list l =3D match l with | [] -> raise Not_found - | h :: tl when h.name =3D sym -> tl + | h :: tl when Symbol.equal h.name sym -> tl | h :: tl -> h :: delete_one_in_list tl in { node with children =3D (delete_one_in_list node.children) } @@ -489,9 +489,6 @@ let copy store =3D { quota =3D Quota.copy store.quota; } =20 -let mark_symbols store =3D - Node.recurse (fun node -> Symbol.mark_as_used node.Node.name) store.root - let incr_transaction_coalesce store =3D store.stat_transaction_coalesce <- store.stat_transaction_coalesce + 1 let incr_transaction_abort store =3D diff --git a/tools/ocaml/xenstored/symbol.ml b/tools/ocaml/xenstored/symbol= .ml index 4420c6a4d7..2b41d120f6 100644 --- a/tools/ocaml/xenstored/symbol.ml +++ b/tools/ocaml/xenstored/symbol.ml @@ -14,63 +14,23 @@ * GNU Lesser General Public License for more details. *) =20 -type t =3D int +module WeakTable =3D Weak.Make(struct + type t =3D string + let equal (x:string) (y:string) =3D (x =3D y) + let hash =3D Hashtbl.hash +end) =20 -type 'a record =3D { data: 'a; mutable garbage: bool } -let int_string_tbl : (int,string record) Hashtbl.t =3D Hashtbl.create 1024 -let string_int_tbl : (string,int) Hashtbl.t =3D Hashtbl.create 1024 +type t =3D string =20 -let created_counter =3D ref 0 -let used_counter =3D ref 0 +let tbl =3D WeakTable.create 1024 =20 -let count =3D ref 0 -let rec fresh () =3D - if Hashtbl.mem int_string_tbl !count - then begin - incr count; - fresh () - end else - !count +let of_string s =3D WeakTable.merge tbl s +let to_string s =3D s =20 -let new_record v =3D { data=3Dv; garbage=3Dfalse } - -let of_string name =3D - if Hashtbl.mem string_int_tbl name - then begin - incr used_counter; - Hashtbl.find string_int_tbl name - end else begin - let i =3D fresh () in - incr created_counter; - Hashtbl.add string_int_tbl name i; - Hashtbl.add int_string_tbl i (new_record name); - i - end - -let to_string i =3D - (Hashtbl.find int_string_tbl i).data - -let mark_all_as_unused () =3D - Hashtbl.iter (fun _ v -> v.garbage <- true) int_string_tbl - -let mark_as_used symb =3D - let record1 =3D Hashtbl.find int_string_tbl symb in - record1.garbage <- false - -let garbage () =3D - let records =3D Hashtbl.fold (fun symb record accu -> - if record.garbage then (symb, record.data) :: accu else accu - ) int_string_tbl [] in - let remove (int,string) =3D - Hashtbl.remove int_string_tbl int; - Hashtbl.remove string_int_tbl string - in - created_counter :=3D 0; - used_counter :=3D 0; - List.iter remove records +let equal a b =3D + (* compare using physical equality, both members have to be part of the = above weak table *) + a =3D=3D b =20 let stats () =3D - Hashtbl.length string_int_tbl - -let created () =3D !created_counter -let used () =3D !used_counter + let len, entries, _, _, _, _ =3D WeakTable.stats tbl in + len, entries diff --git a/tools/ocaml/xenstored/symbol.mli b/tools/ocaml/xenstored/symbo= l.mli index c3c9f6e2f8..586ab57507 100644 --- a/tools/ocaml/xenstored/symbol.mli +++ b/tools/ocaml/xenstored/symbol.mli @@ -29,24 +29,11 @@ val of_string : string -> t val to_string : t -> string (** Convert a symbol into a string. *) =20 -(** {6 Garbage Collection} *) - -(** Symbols need to be regulary garbage collected. The following steps sho= uld be followed: -- mark all the knowns symbols as unused (with [mark_all_as_unused]); -- mark all the symbols really usefull as used (with [mark_as_used]); a= nd -- finally, call [garbage] *) - -val mark_all_as_unused : unit -> unit -val mark_as_used : t -> unit -val garbage : unit -> unit +val equal: t -> t -> bool +(** Compare two symbols for equality *) =20 (** {6 Statistics } *) =20 -val stats : unit -> int -(** Get the number of used symbols. *) +val stats : unit -> int * int +(** Get the table size and number of entries. *) =20 -val created : unit -> int -(** Returns the number of symbols created since the last GC. *) - -val used : unit -> int -(** Returns the number of existing symbols used since the last GC *) diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xen= stored.ml index 5893af2caa..885b397d71 100644 --- a/tools/ocaml/xenstored/xenstored.ml +++ b/tools/ocaml/xenstored/xenstored.ml @@ -431,18 +431,6 @@ let _ =3D =20 let periodic_ops now =3D debug "periodic_ops starting"; - (* we garbage collect the string->int dictionary after a sizeable amount= of operations, - * there's no need to be really fast even if we got loose - * objects since names are often reuse. - *) - if Symbol.created () > 1000 || Symbol.used () > 20000 - then begin - Symbol.mark_all_as_unused (); - Store.mark_symbols store; - Connections.iter cons Connection.mark_symbols; - History.mark_symbols (); - Symbol.garbage () - end; =20 (* scan all the xs rings as a safenet for ill-behaved clients *) if !ring_scan_interval >=3D 0 && now > (!last_scan_time +. float !ring_s= can_interval) then @@ -460,11 +448,11 @@ let _ =3D let (lanon, lanon_ops, lanon_watchs, ldom, ldom_ops, ldom_watchs) =3D Connections.stats cons in let store_nodes, store_abort, store_coalesce =3D Store.stats store in - let symtbl_len =3D Symbol.stats () in + let symtbl_len, symtbl_entries =3D Symbol.stats () in =20 info "store stat: nodes(%d) t-abort(%d) t-coalesce(%d)" store_nodes store_abort store_coalesce; - info "sytbl stat: %d" symtbl_len; + info "sytbl stat: length(%d) entries(%d)" symtbl_len symtbl_entries; info " con stat: anonymous(%d, %d o, %d w) domains(%d, %d o, %d w)" lanon lanon_ops lanon_watchs ldom ldom_ops ldom_watchs; info " mem stat: minor(%.0f) promoted(%.0f) major(%.0f) heap(%d w, %d = c) live(%d w, %d b) free(%d w, %d b)" --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751559; cv=none; d=zohomail.com; s=zohoarc; b=XWrrG9eHChAcUeMvfVPzdEUyNLfbb0uogtQmL/zXAcAuckK8p0IdGaiTCL6acpYSAKFacR352a6aqVoFmO7qDnDIp3gTWQYn3kNhbadxr+cGqwSD1i+LOlkDdmLX0pjBbzTMYCCnHhNBwUPWBuef9lu/70528GGWb7w6brj7yww= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751559; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=dQjYONcVfraJbN6crg1V0YhQ3jdA5BCm8GOIYnJrPCo=; b=Kewvp71z0JezfE2FnRtfWsSQsujhyePIaKENv5Dp9axxMHOoCMFmWU8HjWgZyx8taQ+A8y49aeY9sNU5b7RkyNjM9PDnkqZRPzUp2wFUeHakCpl8/Qbbl2DE5i3qG5WkuYPq+NdPYuuAnCCTCHmAhvFxhAIHqXEO3YEFD+lwP7o= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610751559687207.0230544421097; Fri, 15 Jan 2021 14:59:19 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68775.123244 (Exim 4.92) (envelope-from ) id 1l0Y3U-0003CV-OG; Fri, 15 Jan 2021 22:58:56 +0000 Received: by outflank-mailman (output) from mailman id 68775.123244; Fri, 15 Jan 2021 22:58:56 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Y3U-0003CB-FO; Fri, 15 Jan 2021 22:58:56 +0000 Received: by outflank-mailman (input) for mailman id 68775; Fri, 15 Jan 2021 22:58:55 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XyM-0001Wj-1N for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:53:38 +0000 Received: from esa4.hc3370-68.iphmx.com (unknown [216.71.155.144]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id 2b941d5d-5a37-4e75-91b0-a24787a7b669; Fri, 15 Jan 2021 22:52:40 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 2b941d5d-5a37-4e75-91b0-a24787a7b669 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751160; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=z+ioT6wmOyjk9o8JoO7XIs7aXHObUF73mKxWy5PYTjA=; b=IRYlWZzS61/ifoPWwExZMUb/QPquUH/r6aVL2NO4E3iF+nJLjTqRLHw0 0gxjRJEqTDhK80ftCyVWWXI712Xc6/7qnXlOdBIKV/AmDL/SU5k06YTgZ p7WJqyz5gvQMPPosAJdOTeMvkr9HHGGLs6QOBLSK6no16/ZV2qluh46Yf 4=; Authentication-Results: esa4.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: 3Zax/+P+JvMivlVNzkYK/wwIwFeJfn+UrK7Yc5WIMfMQCPW2nNaCUYMFd7wCopdRFBMU5yjBat 8MwtxgNHVpUygeKQ5doC9IV9o+F45WMAkY7vY/sriRV8A8zD1EVUTYLs2VgcqMGU+gFeZYRffM I1kG/uoHMhql03SqBqsYcaPpjS6Bdt0QqsZ/0joLqSx6GBU1NISXgQo3kNfwtBkEEjyN2Wiy8f wqKAlYsjljMF14zJXXum0gvre7XH5UwfnUryf3clb63AuAdEOsU0MRdm3/vwYkAguWopiGakQs u2o= X-SBRS: 5.1 X-MesageID: 36512262 X-Ironport-Server: esa4.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="36512262" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu Subject: [PATCH v1 1/5] tools/ocaml: add unit test skeleton with Dune build system Date: Fri, 15 Jan 2021 22:29:00 +0000 Message-ID: <58c08503e1f2c26aaba7297bf65caec25929564c.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) Based on initial work by Christian Lindig Doing oxenstored development, especially fuzzing/unit tests requires an incremental and fast build system. Dune is the preferred upstream build system for OCaml, and has been in use by the XAPI project for years. Is is incremental and also generates editor integration files (.merlin). Usage: ./xs-reconfigure.sh cd tools/ocaml make clean make check There are some other convenience targets as well: make dune-clean make dune-syntax-check make dune-build-oxenstored There are some files that are generated by Make, these are created by a 'dune-pre' target, they are too closely tied to make and cannot yet be generated by Dune itself. The various Makefile targets are used as entrypoints into Dune that set the needed env vars (for C include files and libraries) and ensure that the generated files are available. The unit tests do not require Xen to be available, so add mock eventchn and xenctrl libraries for the unit test to use, and copy the non-system specific modules from xenstored/ to xenstored/test/. Xenstored had to be split into Xenstored and Xenstored_main, so that we can use the functions defined in Xenstored without actually starting up the daemon in a unit test. Similarly argument parsing had to be delayed until after daemon startup. Also had to disable setrlimit when running as non-root in poll.ml. Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Christian Lindig --- tools/ocaml/.gitignore | 2 + tools/ocaml/Makefile | 33 +++++++++++++ tools/ocaml/dune-project | 5 ++ tools/ocaml/libs/eventchn/dune | 8 ++++ tools/ocaml/libs/mmap/dune | 8 ++++ tools/ocaml/libs/xb/dune | 7 +++ tools/ocaml/libs/xc/dune | 9 ++++ tools/ocaml/libs/xs/dune | 4 ++ tools/ocaml/xen.opam | 0 tools/ocaml/xenstore.opam | 0 tools/ocaml/xenstored.opam | 18 +++++++ tools/ocaml/xenstored/Makefile | 3 +- tools/ocaml/xenstored/dune | 19 ++++++++ tools/ocaml/xenstored/parse_arg.ml | 2 +- tools/ocaml/xenstored/poll.ml | 3 +- tools/ocaml/xenstored/test/dune | 11 +++++ tools/ocaml/xenstored/test/xenctrl.ml | 48 +++++++++++++++++++ tools/ocaml/xenstored/test/xeneventchn.ml | 50 ++++++++++++++++++++ tools/ocaml/xenstored/test/xenstored_test.ml | 2 + tools/ocaml/xenstored/xenstored.ml | 4 +- 20 files changed, 231 insertions(+), 5 deletions(-) create mode 100644 tools/ocaml/.gitignore create mode 100644 tools/ocaml/dune-project create mode 100644 tools/ocaml/libs/eventchn/dune create mode 100644 tools/ocaml/libs/mmap/dune create mode 100644 tools/ocaml/libs/xb/dune create mode 100644 tools/ocaml/libs/xc/dune create mode 100644 tools/ocaml/libs/xs/dune create mode 100644 tools/ocaml/xen.opam create mode 100644 tools/ocaml/xenstore.opam create mode 100644 tools/ocaml/xenstored.opam create mode 100644 tools/ocaml/xenstored/dune create mode 100644 tools/ocaml/xenstored/test/dune create mode 100644 tools/ocaml/xenstored/test/xenctrl.ml create mode 100644 tools/ocaml/xenstored/test/xeneventchn.ml create mode 100644 tools/ocaml/xenstored/test/xenstored_test.ml diff --git a/tools/ocaml/.gitignore b/tools/ocaml/.gitignore new file mode 100644 index 0000000000..655e32b07c --- /dev/null +++ b/tools/ocaml/.gitignore @@ -0,0 +1,2 @@ +_build +.merlin diff --git a/tools/ocaml/Makefile b/tools/ocaml/Makefile index a7c04b6546..53dd0a0f0d 100644 --- a/tools/ocaml/Makefile +++ b/tools/ocaml/Makefile @@ -34,3 +34,36 @@ build-tools-oxenstored: $(MAKE) -s -C libs/xb $(MAKE) -s -C libs/xc $(MAKE) -C xenstored + +LIBRARY_PATH=3D$(XEN_libxenctrl):$(XEN_libxenguest):$(XEN_libxentoollog):$= (XEN_libxencall):$(XEN_libxenevtchn):$(XEN_libxenforeignmemory):$(XEN_libxe= ngnttab):$(XEN_libxendevicemodel):$(XEN_libxentoolcore) +C_INCLUDE_PATH=3D$(XEN_libxenctrl)/include:$(XEN_libxengnttab)/include:$(X= EN_libxenevtchn)/include:$(XEN_libxentoollog)/include:$(XEN_INCLUDE) + +# Files generated by the Makefile +# These cannot be generated from dune, because dune cannot refer to files +# in the parent directory (so it couldn't copy/use Config.mk) +.PHONY: dune-pre +dune-pre: + $(MAKE) -s -C ../../ build-tools-public-headers + $(MAKE) -s -C libs/xs paths.ml + $(MAKE) -s -C libs/xc xenctrl_abi_check.h + $(MAKE) -s -C xenstored paths.ml _paths.h + +.PHONY: check +check: dune-pre + # --force isn't necessary here if the test is deterministic + OCAMLRUNPARAM=3Db C_INCLUDE_PATH=3D$(C_INCLUDE_PATH) dune runtest --profi= le=3Drelease --no-buffer --force + +# Convenience targets for development + +.PHONY: dune-clean +dune-clean: + $(MAKE) clean + dune clean + +.PHONY: dune-syntax-check +dune-syntax-check: dune-pre + LIBRARY_PATH=3D$(LIBRARY_PATH) C_INCLUDE_PATH=3D$(C_INCLUDE_PATH) dune bu= ild --profile=3Drelease @check + +.PHONY: build-oxenstored-dune +dune-build-oxenstored: dune-pre + LD_LIBRARY_PATH=3D$(LIBRARY_PATH) LIBRARY_PATH=3D$(LIBRARY_PATH) C_INCLUD= E_PATH=3D$(C_INCLUDE_PATH) dune build --profile=3Drelease @all diff --git a/tools/ocaml/dune-project b/tools/ocaml/dune-project new file mode 100644 index 0000000000..b41cfae68b --- /dev/null +++ b/tools/ocaml/dune-project @@ -0,0 +1,5 @@ +(lang dune 2.0) + +(name xen) + +(formatting disabled) diff --git a/tools/ocaml/libs/eventchn/dune b/tools/ocaml/libs/eventchn/dune new file mode 100644 index 0000000000..e08bc76fdf --- /dev/null +++ b/tools/ocaml/libs/eventchn/dune @@ -0,0 +1,8 @@ +(library + (foreign_stubs + (language c) + (names xeneventchn_stubs)) + (name xeneventchn) + (public_name xen.eventchn) + (libraries unix) + (c_library_flags -lxenevtchn)) diff --git a/tools/ocaml/libs/mmap/dune b/tools/ocaml/libs/mmap/dune new file mode 100644 index 0000000000..a47de44e47 --- /dev/null +++ b/tools/ocaml/libs/mmap/dune @@ -0,0 +1,8 @@ +(library + (foreign_stubs + (language c) + (names xenmmap_stubs)) + (name xenmmap) + (public_name xen.mmap) + (libraries unix) + (install_c_headers mmap_stubs)) diff --git a/tools/ocaml/libs/xb/dune b/tools/ocaml/libs/xb/dune new file mode 100644 index 0000000000..feb30adc01 --- /dev/null +++ b/tools/ocaml/libs/xb/dune @@ -0,0 +1,7 @@ +(library + (foreign_stubs + (language c) + (names xenbus_stubs xs_ring_stubs)) + (name xenbus) + (public_name xen.bus) + (libraries unix xenmmap)) diff --git a/tools/ocaml/libs/xc/dune b/tools/ocaml/libs/xc/dune new file mode 100644 index 0000000000..fb75ee8ff7 --- /dev/null +++ b/tools/ocaml/libs/xc/dune @@ -0,0 +1,9 @@ +(library + (foreign_stubs + (language c) + (names xenctrl_stubs)) + (name xenctrl) + (public_name xen.ctrl) + (libraries unix xenmmap) + (c_library_flags -lxenctrl -lxenguest -lxencall -lxenforeignmemory + -lxengnttab)) diff --git a/tools/ocaml/libs/xs/dune b/tools/ocaml/libs/xs/dune new file mode 100644 index 0000000000..c79ea75775 --- /dev/null +++ b/tools/ocaml/libs/xs/dune @@ -0,0 +1,4 @@ +(library + (name xenstore) + (public_name xen.store) + (libraries unix xenbus)) diff --git a/tools/ocaml/xen.opam b/tools/ocaml/xen.opam new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/ocaml/xenstore.opam b/tools/ocaml/xenstore.opam new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/ocaml/xenstored.opam b/tools/ocaml/xenstored.opam new file mode 100644 index 0000000000..31775a3033 --- /dev/null +++ b/tools/ocaml/xenstored.opam @@ -0,0 +1,18 @@ +opam-version: "2.0" +synopsis: "In-memory key-value store for the Xen hypervisor" +maintainer: "lindig@gmail.com" +authors: "lindig@gmail.com" +license: "LGPL" +homepage: "https://github.com/lindig/xen-ocaml-tools" +bug-reports: "https://github.com/lindig/xen-ocaml-tools/issues" +depends: [ + "ocaml" + "dune" {build} + "base-unix" + "crowbar" {with-test} + "qcheck-core" {with-test} + "qcstm" {with-test} +] +build: ["dune" "build" "-p" name "-j" jobs] +depexts: ["m4" "libxen-dev" "libsystemd-dev"] {os-distribution =3D "debian= "} +dev-repo: "git+https://github.com/lindig/xen-ocaml-tools.git" diff --git a/tools/ocaml/xenstored/Makefile b/tools/ocaml/xenstored/Makefile index 89ec3ec76a..9d2da206d8 100644 --- a/tools/ocaml/xenstored/Makefile +++ b/tools/ocaml/xenstored/Makefile @@ -56,7 +56,8 @@ OBJS =3D paths \ history \ parse_arg \ process \ - xenstored + xenstored \ + xenstored_main =20 INTF =3D symbol.cmi trie.cmi syslog.cmi systemd.cmi poll.cmi =20 diff --git a/tools/ocaml/xenstored/dune b/tools/ocaml/xenstored/dune new file mode 100644 index 0000000000..e59eb22638 --- /dev/null +++ b/tools/ocaml/xenstored/dune @@ -0,0 +1,19 @@ +(executable + (modes byte exe) + (name xenstored) + (modules (:standard \ syslog systemd)) + (public_name xenstored) + (package xenstored) + (flags (:standard -w -52)) + (libraries unix xen.bus xen.mmap xen.ctrl xen.eventchn xenstubs)) + +(library + (foreign_stubs + (language c) + (names syslog_stubs systemd_stubs select_stubs) + (flags (-DHAVE_SYSTEMD))) + (modules syslog systemd) + (name xenstubs) + (wrapped false) + (libraries unix) + (c_library_flags -lsystemd)) diff --git a/tools/ocaml/xenstored/parse_arg.ml b/tools/ocaml/xenstored/par= se_arg.ml index 7c0478e76a..965cb9ebeb 100644 --- a/tools/ocaml/xenstored/parse_arg.ml +++ b/tools/ocaml/xenstored/parse_arg.ml @@ -28,7 +28,7 @@ type config =3D disable_socket: bool; } =20 -let do_argv =3D +let do_argv () =3D let pidfile =3D ref "" and tracefile =3D ref "" (* old xenstored compatib= ility *) and domain_init =3D ref true and activate_access_log =3D ref true diff --git a/tools/ocaml/xenstored/poll.ml b/tools/ocaml/xenstored/poll.ml index 26f8620dfc..92e0717ed2 100644 --- a/tools/ocaml/xenstored/poll.ml +++ b/tools/ocaml/xenstored/poll.ml @@ -64,4 +64,5 @@ let poll_select in_fds out_fds exc_fds timeout =3D a r =20 let () =3D - set_fd_limit (get_sys_fs_nr_open ()) + if Unix.geteuid () =3D 0 then + set_fd_limit (get_sys_fs_nr_open ()) diff --git a/tools/ocaml/xenstored/test/dune b/tools/ocaml/xenstored/test/d= une new file mode 100644 index 0000000000..2a3eb2b7df --- /dev/null +++ b/tools/ocaml/xenstored/test/dune @@ -0,0 +1,11 @@ +(copy_files# ../*.ml{,i}) + +(test + (modes native) + (ocamlopt_flags -afl-instrument) + (name xenstored_test) + (modules (:standard \ syslog systemd)) + (package xenstored) + (flags (:standard -w -52)) + ;;(action (run %{test} -v --seed 364172147)) + (libraries unix xen.bus xen.mmap xenstubs crowbar xen.store fmt fmt.tty)) diff --git a/tools/ocaml/xenstored/test/xenctrl.ml b/tools/ocaml/xenstored/= test/xenctrl.ml new file mode 100644 index 0000000000..37d6da0a47 --- /dev/null +++ b/tools/ocaml/xenstored/test/xenctrl.ml @@ -0,0 +1,48 @@ +(* + * Copyright (C) 2006-2007 XenSource Ltd. + * Copyright (C) 2008 Citrix Ltd. + * Author Vincent Hanquez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + *) + +(** *) +type domid =3D int + +(* ** xenctrl.h ** *) + + +type domaininfo =3D +{ + domid : domid; + dying : bool; + shutdown : bool; + shutdown_code : int; +} + +exception Error of string + +type handle =3D unit + +let interface_open () =3D () +let interface_close () =3D () + +let domain_getinfo () domid =3D { + domid =3D domid; + dying =3D false; + shutdown =3D false; + shutdown_code =3D 0; +} + +let devzero =3D Unix.openfile "/dev/zero" [] 0 +let nullmap () =3D Xenmmap.mmap devzero Xenmmap.RDWR Xenmmap.PRIVATE 4096= 0 + +let map_foreign_range _ _ _ _ =3D nullmap () diff --git a/tools/ocaml/xenstored/test/xeneventchn.ml b/tools/ocaml/xensto= red/test/xeneventchn.ml new file mode 100644 index 0000000000..6612722dc2 --- /dev/null +++ b/tools/ocaml/xenstored/test/xeneventchn.ml @@ -0,0 +1,50 @@ +(* + * Copyright (C) 2006-2007 XenSource Ltd. + * Copyright (C) 2008 Citrix Ltd. + * Author Vincent Hanquez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + *) + +type handle =3D Unix.file_descr * int ref + +let devnull =3D Unix.openfile "/dev/null" [] 0 +let init () =3D devnull, ref 0 +let fd (h, _) =3D h + +type t =3D int + +type virq_t =3D + | Timer (* #define VIRQ_TIMER 0 *) + | Debug (* #define VIRQ_DEBUG 1 *) + | Console (* #define VIRQ_CONSOLE 2 *) + | Dom_exc (* #define VIRQ_DOM_EXC 3 *) + | Tbuf (* #define VIRQ_TBUF 4 *) + | Reserved_5 (* Do not use this value as it's not defined *) + | Debugger (* #define VIRQ_DEBUGGER 6 *) + | Xenoprof (* #define VIRQ_XENOPROF 7 *) + | Con_ring (* #define VIRQ_CON_RING 8 *) + | Pcpu_state (* #define VIRQ_PCPU_STATE 9 *) + | Mem_event (* #define VIRQ_MEM_EVENT 10 *) + | Xc_reserved (* #define VIRQ_XC_RESERVED 11 *) + | Enomem (* #define VIRQ_ENOMEM 12 *) + | Xenpmu (* #define VIRQ_XENPMU 13 *) + +let notify _h _ =3D () +let bind_interdomain (_h, port) domid remote_port =3D incr port; !port +let bind_virq (_h, port) _ =3D incr port; !port +let bind_dom_exc_virq handle =3D bind_virq handle Dom_exc +let unbind _ _ =3D () +let pending (_h, port) =3D !port +let unmask _ _ =3D () + +let to_int x =3D x +let of_int x =3D x diff --git a/tools/ocaml/xenstored/test/xenstored_test.ml b/tools/ocaml/xen= stored/test/xenstored_test.ml new file mode 100644 index 0000000000..e86b68e867 --- /dev/null +++ b/tools/ocaml/xenstored/test/xenstored_test.ml @@ -0,0 +1,2 @@ +open Xenstored +let () =3D () diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xen= stored.ml index 885b397d71..e25b407303 100644 --- a/tools/ocaml/xenstored/xenstored.ml +++ b/tools/ocaml/xenstored/xenstored.ml @@ -265,8 +265,8 @@ let to_file store cons fds file =3D (fun () -> close_out channel) end =20 -let _ =3D - let cf =3D do_argv in +let main () =3D + let cf =3D do_argv () in let pidfile =3D if Sys.file_exists (config_filename cf) then parse_config (config_filename cf) --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751180; cv=none; d=zohomail.com; s=zohoarc; b=eB7BYf0Mb1Sj4NxWvJSkt6n76YA6S/6xKFggl6NUljM6JceYJNwypsEpivoAZoNKVGmYVKga0IhRzTZBgYP3Aiqx8V83ryS9OaGw6wraS9r3XqDvOS7BZoYWbzHVczfzP+l4J8W6dpDGxlV2VMfm9tpg/DfzsZPwbX1anCwb3CY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751180; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=MQtVdCDd4wDzZUbjUejhu5mpnyUPY2K4RBc3AtyZzuY=; b=H0LfMpu3pr9OjkfaOwLDUcMjBANDINlEiBRqRudiqlWTcY7ZV5oBJ9WjNYN/sLMTZLDw6Fn3s4Fbb1lJIrEd6A/sbYdSdUKAJqSWbmJwvX7df0joOhvzn/XYJJ7gXnZ+u1v92/f95WZaaqoNA+cI6kciEOi0tu0GhXxN6ZEJCdw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 161075118051131.036801942363695; Fri, 15 Jan 2021 14:53:00 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68727.123140 (Exim 4.92) (envelope-from ) id 1l0XxU-0001oD-LO; Fri, 15 Jan 2021 22:52:44 +0000 Received: by outflank-mailman (output) from mailman id 68727.123140; Fri, 15 Jan 2021 22:52:44 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XxU-0001o4-Ex; Fri, 15 Jan 2021 22:52:44 +0000 Received: by outflank-mailman (input) for mailman id 68727; Fri, 15 Jan 2021 22:52:43 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XxS-0001Wj-VC for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:52:42 +0000 Received: from esa6.hc3370-68.iphmx.com (unknown [216.71.155.175]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id ce39791c-cde2-44e2-bddc-f6b8853784ee; Fri, 15 Jan 2021 22:52:31 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: ce39791c-cde2-44e2-bddc-f6b8853784ee DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751150; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=yOHeqFGiMe4hZS6YwMQMyCOdiJ0wM3c0HqxPstrpIhc=; b=ChfqWcI3+UBe+xsqzxLXcI3S9YDZ2SUz6+sd+Um9bpqjjBZUr+xiR2h3 B5yrZgiTB8O/acoXVw5HvEoVnwFgwbupeQ/stb89hixecPc5vsaxVa7HQ LPVZA+2y3qh+PKs5fc8fsEisL0iTqCoCchhmHU4nC6oYEpl9hvItrne7Q Y=; Authentication-Results: esa6.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: 38anpzmgEYm8RC8Iis3iaGr6eVYc2bd8LxVkaIVgGeoD/pNFjIQR4yS69KRxzhioTeAC5+4gS/ Hj5zYoImDN9l6+1npPaVAYxtu04FVAcHGByMckRUTwSNzpdyrxz6t80SAe30pofSha9SFCcyKE 0jtzlsBrDbgeQ0nNPxJPXL+/3qWXp2T78oxyk4rNY95oTIY+Oi56b1fAEEpr7kEAh6U1zyZLUu ubLKDYmSOENgXHcdi/E3AoUasW9oest8gKslDov1b9nDZwZ0L4V5ALvVPnn2zRpVyNqUuIZ9d0 +pU= X-SBRS: 5.1 X-MesageID: 35435267 X-Ironport-Server: esa6.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35435267" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu Subject: [PATCH v1 1/4] tools/ocaml/libs/xb: do not crash after xenbus is unmapped Date: Fri, 15 Jan 2021 22:29:06 +0000 Message-ID: <0af9839d3a8f27421b4c8f1220f9a2165d815ac2.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) Xenmmap.unmap sets the address to MAP_FAILED in xenmmap_stubs.c. If due to a bug there were still references to the Xenbus and we attempt to use it then we crash. Raise an exception instead of crashing. (My initial version of fuzz testing had such a bug) Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Christian Lindig --- tools/ocaml/libs/xb/xs_ring_stubs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/ocaml/libs/xb/xs_ring_stubs.c b/tools/ocaml/libs/xb/xs_r= ing_stubs.c index 7537a23949..7a91fdee75 100644 --- a/tools/ocaml/libs/xb/xs_ring_stubs.c +++ b/tools/ocaml/libs/xb/xs_ring_stubs.c @@ -32,6 +32,7 @@ #include #include =20 +#include #include "mmap_stubs.h" =20 #define GET_C_STRUCT(a) ((struct mmap_interface *) a) @@ -166,6 +167,8 @@ CAMLprim value ml_interface_set_server_features(value i= nterface, value v) { CAMLparam2(interface, v); struct xenstore_domain_interface *intf =3D GET_C_STRUCT(interface)->addr; + if (intf =3D=3D (void*)MAP_FAILED) + caml_failwith("Interface closed"); =20 intf->server_features =3D Int_val(v); =20 --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610749816; cv=none; d=zohomail.com; s=zohoarc; b=gzt4Qjf5UWgwKQdZksom9qUiUYRE57bcToH3jbPvkyxP7q7fN3XEqSOwsmYsaH3b+nBPpdyQ00pWCfy+PZA0DL04I0O+ohKK0smGW1N7K3hEAZqgP3wM8SX2h0iHybd6Dp/guWnYIH1XKgYnsi70jGehu+ktF5VjgBcz/sYu/KY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610749816; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=yttEhKbPpkx5zXzKuGhxXeXAFaq8ad+G4VtLAPW8gbQ=; b=Aj5LoDg+AdQcknf8dvy0so5bAI6ES2VlrCOst4yOQnl1PV2DLsWLjlw6Ah1XYIzIXU9OIRaJGIWLSHC2fZeivDHZFTrwCwrNqGkCv4reLq9n4xtGyZXB7zcVYV77mi1Aw9Ul0nzqdMXl8awNvFZBZPFcLaVW98x85Aew5mStQuE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610749816380277.32279323211696; Fri, 15 Jan 2021 14:30:16 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68663.122980 (Exim 4.92) (envelope-from ) id 1l0XbU-0006DO-GW; Fri, 15 Jan 2021 22:30:00 +0000 Received: by outflank-mailman (output) from mailman id 68663.122980; Fri, 15 Jan 2021 22:30:00 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XbU-0006D7-2c; Fri, 15 Jan 2021 22:30:00 +0000 Received: by outflank-mailman (input) for mailman id 68663; Fri, 15 Jan 2021 22:29:59 +0000 Received: from all-amaz-eas1.inumbo.com ([34.197.232.57] helo=us1-amaz-eas2.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XbT-00062b-8E for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:29:59 +0000 Received: from esa6.hc3370-68.iphmx.com (unknown [216.71.155.175]) by us1-amaz-eas2.inumbo.com (Halon) with ESMTPS id d2e3b80d-86da-4705-9edf-f9c045ca9bf7; Fri, 15 Jan 2021 22:29:49 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: d2e3b80d-86da-4705-9edf-f9c045ca9bf7 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610749789; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=lWsFTTbEVFGdoUm4Dgm9YQNeIr4pWQkbL4RlcMnain4=; b=aWmEMoSXP/SabntwWj72+ReS/+rpbPyvcUEcyfpo2i4lPxppW7T29ZcK 6oGbtCUqMZt+kKNgK67XQY3e+AMtVeEHNpPjKEZpgKx4cDbQLddWZN797 ZwlNgS2+grx9pow5cQJeaLA6+WCYsxSmPAQyE4QJmScM7SC//ix4MRw2E 4=; Authentication-Results: esa6.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: LqKnSJ01Vz6NbkjT5eTnpYW+v22/ib/w2FzMRwcnWFDPte6Sd4HtlfQTyfBwsBVBIQZ2qh6kQk K+U55Ebwd+zjAnMAvMtKufyGG8tRxQxG+n+7fzAe7QLe1KQNiKL8lTNKvnbUNm3sa+dTdnYcxm rqNxYp/2M3RSN2qY9gjVu4rqSjQCTiU/1xK4d43qADXtD3rupDpkjOIPOGSbm0d7OsDQRKxrEt HMqy15LOrIzhFMQB/YmFw068xVMF3mzDj50jggF/XSSTuMzI8YlwDunw6N89eHV08kNmYfY4yv U4k= X-SBRS: 5.1 X-MesageID: 35434450 X-Ironport-Server: esa6.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35434450" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , Ian Jackson , Wei Liu , Juergen Gross , Pau Ruiz Safont , Christian Lindig Subject: [PATCH v2 2/8] Add workaround for xenstore-control flood issues Date: Fri, 15 Jan 2021 22:28:44 +0000 Message-ID: X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) There are alternative fixes for this, e.g. do the entire live update inside oxenstored and reply OK from the next oxenstored or an error. This requires some asynchronous handling there. Once that code is available we can revert this one. Signed-off-by: Edwin T=C3=B6r=C3=B6k Reviewed-by: Pau Ruiz Safont Reviewed-by: Christian Lindig Acked-by: Christian Lindig --- Changed since V1: * post publicly now that the XSA is out --- tools/xenstore/xenstore_control.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tools/xenstore/xenstore_control.c b/tools/xenstore/xenstore_co= ntrol.c index 5ca015a07d..611e8b4fdd 100644 --- a/tools/xenstore/xenstore_control.c +++ b/tools/xenstore/xenstore_control.c @@ -42,6 +42,10 @@ static int live_update_start(struct xs_handle *xsh, bool= force, unsigned int to) len =3D add_to_buf(&buf, "-F", len); if (len < 0) return 1; + /* +1 for rounding issues + * +1 to give oxenstored a chance to timeout and report back first + */ + to +=3D 2; =20 for (time_start =3D time(NULL); time(NULL) - time_start < to;) { ret =3D xs_control_command(xsh, "live-update", buf, len); @@ -49,6 +53,15 @@ static int live_update_start(struct xs_handle *xsh, bool= force, unsigned int to) goto err; if (strcmp(ret, "BUSY")) break; + /* TODO: use task ID for commands, avoid busy loop polling +here + * oxenstored checks BUSY condition internally on every main +loop iteration anyway. + * Avoid flooding xenstored with live-update requests. + * The flooding can also cause the evtchn to overflow in +xenstored which makes + * xenstored enter an infinite loop */ + sleep(1); } =20 if (strcmp(ret, "OK")) --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751176; cv=none; d=zohomail.com; s=zohoarc; b=k9xK+tUaSIyn18nhmoao+AMoAJITD+Vu8xPYLQcTIZfF5AbMrvixY2QThllUFJXyopvRE9/KDgFS6Hc2mznP4pNwoV2BU+fNkTfV2btMDpBJQXPlpuyTns/CV6ndfSJ8vpTKjc+FcyvsrfgOBxyz+KgbelSeT31iya172NfBuFU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751176; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=c1WY9xBrY1DP0m7nsjVbVf3Q5ReSwcMmj4cjI+h3sWE=; b=jYmUdYS7GtSPCqK13TQ5ayth/JTVdQ8D4trYUpdiIjabAaCoq83yHwM8Vot5oCdvraNZIJzRClpEw3qoYh8kD9EmhtPT0bF9P9dXITpd3+m22mWK8ch3D1lZjVHKuJHn94K1K6hOJoAAuYaNr1EMekVfI8ojnHihRLz+fWKn+5Q= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610751176901319.1456120680525; Fri, 15 Jan 2021 14:52:56 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68725.123116 (Exim 4.92) (envelope-from ) id 1l0XxP-0001hJ-N1; Fri, 15 Jan 2021 22:52:39 +0000 Received: by outflank-mailman (output) from mailman id 68725.123116; Fri, 15 Jan 2021 22:52:39 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XxP-0001hA-Iw; Fri, 15 Jan 2021 22:52:39 +0000 Received: by outflank-mailman (input) for mailman id 68725; Fri, 15 Jan 2021 22:52:38 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XxN-0001Wj-V1 for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:52:37 +0000 Received: from esa6.hc3370-68.iphmx.com (unknown [216.71.155.175]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id a6544484-10a0-432f-b861-6e2a97ede685; Fri, 15 Jan 2021 22:52:29 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: a6544484-10a0-432f-b861-6e2a97ede685 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751149; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=LEALEW4zoPaTZqfqQdWRUFyayQ0TM4apFWLytoqaiiQ=; b=RHhOW17CTasd4AxHPoZn4+EF/D9WhnsstGfQNrXmgUWRKfQQruaYZ+Ze Xpm6/HbsuTd9OkIAc/GTeJqnDTJltzRXgZ/IzfS40eIbsO2en0ux/wPmE k2pcGdGiIN/nj80Cf8ClN8z8QDdqi285nQVvbDDS1RFnDOTeLTE6FoqKJ E=; Authentication-Results: esa6.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: nuv/IjZ3QDe7YpQlZvlNTTfhPqiZFhsfRvK0ROHZMb4miW+oxsMRixtanobo8lepoOLlAgQLL7 KvqaCe1UbO0TnrpztD4EiJ8xvjWJXy3K8zlAaHeDw7TSvcoKeTWNEcJ3x9yb1899WKYDeubW67 Hk270lIKABGtUXuB0qUOaq5fTr7wdV6w8+Xrt3V1KRT/PKdNPDESvxK+PM6Dh1t/KZ/lqt5fQE ETJLChXa5rJ8jsfL28JyCkuuAmC0rmdCjbSg8T0828usJwLhBBjXnfoN2ILBUIO2aHOHqyCqcW qjg= X-SBRS: 5.1 X-MesageID: 35435266 X-Ironport-Server: esa6.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35435266" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu Subject: [PATCH v1 2/5] tools/ocaml/xenstored: implement the live migration binary format Date: Fri, 15 Jan 2021 22:29:01 +0000 Message-ID: <25bad0197987e91e35a19763f6c058362605e2af.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) This is implemented by C xenstored as live update dump format. oxenstored already has its own (text-based) dump format, but for compatibility implement one compatible with C xenstored. This will also be useful in the future for non-cooperative guest live migra= tion. docs/designs/xenstore-migration.md documents the format For now this always dumps integers in big endian order, because even old versions of OCaml have support for that. The binary format supports both little and big endian orders, so this should be compatible. To dump in little endian or native endian order we would require OCaml 4.08+. Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Christian Lindig --- tools/ocaml/xenstored/disk.ml | 318 ++++++++++++++++++++++++++++++++++ 1 file changed, 318 insertions(+) diff --git a/tools/ocaml/xenstored/disk.ml b/tools/ocaml/xenstored/disk.ml index 4739967b61..595fdab54a 100644 --- a/tools/ocaml/xenstored/disk.ml +++ b/tools/ocaml/xenstored/disk.ml @@ -155,3 +155,321 @@ let write store =3D Unix.rename tfile xs_daemon_database with exc -> error "caught exn %s" (Printexc.to_string exc) + + module BinaryOut =3D struct + let version =3D 0x1 + let endian =3D 1 + let padding =3D String.make 7 '\x00' + + let write_header ch =3D + (* for testing endian order *) + output_binary_int ch 0x78656e73; + output_binary_int ch 0x746f7265; + output_binary_int ch version; + output_binary_int ch endian; + ch + + let w8 =3D output_char + let w16 ch i =3D + assert (i >=3D 0 && i lsr 16 =3D 0); + output_byte ch (i lsr 8); + output_byte ch i + + let w32 ch v =3D + assert (v >=3D 0 && v <=3D 0xFFFF_FFFF); + output_binary_int ch v + + let pos =3D pos_out + let wpad ch =3D + let padto =3D 8 in + let padby =3D (padto - pos ch mod padto) mod padto in + if padby > 0 then + output_substring ch padding 0 padby + + let wstring =3D output_string + end + + module BinaryIn =3D struct + type t =3D in_channel + + let read_header t =3D + let h =3D Bytes.make 8 '\x00' in + really_input t h 0 (Bytes.length h); + let ver =3D input_binary_int t in + let endian =3D input_binary_int t in + if Bytes.to_string h <> "xenstore" then + failwith "Header doesn't begin with 'xenstore'"; + if ver <> BinaryOut.version then + failwith "Incompatible version"; + if endian <> BinaryOut.endian then + failwith "Incompatible endianness" + + let r8 =3D input_char + + let r16 t =3D=20 + let r0 =3D input_byte t in + let r1 =3D input_byte t in + (r0 lsl 8) lor r1 + + let r32 t =3D + (* read unsigned 32-bit int *) + let r =3D input_binary_int t land 0xFFFF_FFFF in + assert (r >=3D 0); + r + + let rstring =3D really_input_string + + let rpad t =3D + let padto =3D 8 in + let padby =3D (padto - pos_in t mod padto) mod padto in + if padby > 0 then + ignore (really_input_string t padby) + end + +module FD : sig + type t =3D Unix.file_descr + val of_int: int -> t + val to_int : t -> int +end =3D struct + type t =3D Unix.file_descr + (* This is like Obj.magic but just for these types, + and relies on Unix.file_descr =3D int *) + external to_int : t -> int =3D "%identity" + external of_int : int -> t =3D "%identity" +end + +module LiveRecord =3D struct + (* See docs/designs/xenstore-migration.md for binary format *) + module Type : sig + type t =3D private int + val end_ : t + val global_data : t + val connection_data : t + val watch_data : t + val transaction_data : t + val node_data: t + end =3D struct + type t =3D int + let end_ =3D 0x0 + let global_data =3D 0x01 + let connection_data =3D 0x02 + let watch_data =3D 0x03 + let transaction_data =3D 0x04 + let node_data =3D 0x05 + end + + module I =3D BinaryIn + module O =3D BinaryOut + + let write_expect msg expected actual =3D + if expected <> actual then + let m =3D Printf.sprintf "expected %d <> %d: %s" expected actual msg in + invalid_arg m + + let write_record t (typ: Type.t) len f =3D + assert (O.pos t mod 8 =3D 0); + O.w32 t (typ :> int); + O.w32 t len; + let p0 =3D O.pos t in + f t; + let p1 =3D O.pos t in + write_expect "position and length" len (p1-p0); + O.wpad t + + let write_end t =3D + write_record t Type.end_ 0 ignore + + let read_expect t msg expected actual =3D + if expected <> actual then + let pos =3D pos_in t in + let m =3D Printf.sprintf "expected %d <> %d at ~%d: %s" expected actual= pos msg in + invalid_arg m + + let read_end t ~len f =3D + read_expect t "end" 0 len; + f () + + let write_global_data t ~rw_sock =3D + write_record t Type.global_data 8 @@ fun b -> + O.w32 b (FD.to_int rw_sock); + O.w32 b (-1) + + let read_global_data t ~len f =3D + read_expect t "global_data" 8 len; + let rw_sock =3D FD.of_int (I.r32 t) in + let _ =3D FD.of_int (I.r32 t) in + f ~rw_sock + + let conn_shared_ring =3D 0x0 + let conn_socket =3D 0x1 + let domid_invalid =3D 0x7FF4 + + (* oxenstored doesn't support readonly sockets yet *) + let flags_connection_readonly =3D 0x1l + + type dom =3D { id: int; target: int; remote_port: int } + type conn =3D Socket of Unix.file_descr | Domain of dom + + let write_connection_data t ~conid ~conn xb_pktin xb_partialout xb_pktout= =3D + let in_data_len =3D Buffer.length xb_pktin in + let out_resp_len =3D String.length xb_partialout in + let out_data_len =3D Buffer.length xb_pktout in + let data_len =3D in_data_len + out_data_len in + + write_record t Type.connection_data (32 + data_len) @@ fun b -> + assert (conid > 0); + O.w32 b conid; + O.w32 b (match conn with + | Socket _ -> conn_socket + | Domain _ -> conn_shared_ring + ); + let flags =3D 0x0 in + O.w32 b flags; + + (match conn with + | Socket fd -> + O.w32 b (FD.to_int fd); + O.w32 b 0 (* pad *) + | Domain dom -> + O.w16 b dom.id; + O.w16 b dom.target; + O.w32 b dom.remote_port + ); + + O.w32 b in_data_len; + O.w32 b out_resp_len; + O.w32 b out_data_len; + Buffer.output_buffer b xb_pktin; + O.wstring b xb_partialout; + Buffer.output_buffer b xb_pktout + + let read_connection_data t ~len f =3D + let conid =3D I.r32 t in + assert (conid > 0); + let kind =3D I.r32 t in + let flags =3D I.r32 t in + read_expect t "flags" 0 flags; + let conn =3D (match kind with + | x when x =3D conn_socket -> + let fd =3D FD.of_int (I.r32 t) in + I.r32 t |> ignore; + Socket fd + | x when x =3D conn_shared_ring -> + let id =3D I.r16 t in + let target =3D I.r16 t in + let remote_port =3D I.r32 t in + Domain {id; target; remote_port } + | x -> + invalid_arg (Printf.sprintf "Unknown connection kind %x" x) + ) in + let in_data_len =3D I.r32 t in + let out_resp_len =3D I.r32 t in + let out_data_len =3D I.r32 t in + let in_data =3D really_input_string t in_data_len in + let out_data =3D really_input_string t out_data_len in + f ~conid ~conn ~in_data ~out_data ~out_resp_len + + + let write_watch_data t ~conid ~wpath ~token =3D + let wpath_len =3D String.length wpath in + let token_len =3D String.length token in + + write_record t Type.watch_data (12+wpath_len+token_len) @@ fun b -> + O.w32 b conid; + O.w32 b (String.length wpath); + O.w32 b (String.length token); + O.wstring b wpath; + O.wstring b token + + let read_watch_data t ~len f =3D + let conid =3D I.r32 t in + let wpathlen =3D I.r32 t in + let tokenlen =3D I.r32 t in + let wpath =3D I.rstring t wpathlen in + let token =3D I.rstring t tokenlen in + f ~conid ~wpath ~token + + let write_transaction_data t ~conid ~txid =3D + write_record t Type.transaction_data 8 @@ fun b -> + O.w32 b conid; + O.w32 b txid + + let read_transaction_data t ~len f =3D + read_expect t "transaction" 8 len; + let conid =3D I.r32 t in + let txid =3D I.r32 t in + f ~conid ~txid + + type access =3D R | W | RW | Del + + let write_node_data t ~txidaccess ~path ~value ~perms =3D + let path_len =3D String.length path in + let value_len =3D String.length value in + let perms =3D Perms.Node.acls perms in + let len =3D 24 + (List.length perms)*4 + path_len + value_len in + + write_record t Type.node_data len @@ fun b -> + O.w32 b (match txidaccess with None -> 0 | Some (conid, _, _) -> conid); + O.w32 b (match txidaccess with None -> 0 | Some (_, txid, _) -> txid); + O.w32 b path_len; + O.w32 b value_len; + O.w32 b (match txidaccess with + | None -> 0x0 + | Some (_, _, Del) -> 0x0 + | Some (_, _, R) -> 0x1 + | Some (_, _, W) -> 0x2 + | Some (_, _, RW) -> 0x3 + ); + O.w32 b (List.length perms); + List.iter (fun (domid, permty) -> + O.w8 b (Perms.char_of_permty permty); + O.w8 b '\x00'; + O.w16 b domid; + ) perms; + O.wstring b path; + O.wstring b value + + let read_node_data t ~len f =3D + let conid =3D I.r32 t in + let txid =3D I.r32 t in + let path_len =3D I.r32 t in + let value_len =3D I.r32 t in + let txaccess =3D match conid, I.r32 t with + | 0, _ -> None + | _, 0 -> Some (conid, txid, Del) + | _, 1 -> Some (conid, txid, R) + | _, 2 -> Some (conid, txid, W) + | _, 3 -> Some (conid, txid, RW) + | _ -> invalid_arg "invalid access flag" + in + let a =3D Array.init (I.r32 t) (fun _ -> + let perm =3D Perms.permty_of_char (I.r8 t) in + I.r8 t |> ignore; + let domid =3D I.r16 t in + domid, perm + ) in + let perms =3D match Array.to_list a with + | [] -> invalid_arg "Permission list cannot be empty"; + | (owner, other) :: acls -> + Perms.Node.create owner other acls + in + let path =3D I.rstring t path_len in + let value =3D I.rstring t value_len in + f ~txaccess ~perms ~path ~value + + let read_record t ~on_end ~on_global_data ~on_connection_data ~on_watch_d= ata ~on_transaction_data ~on_node_data =3D + I.rpad t; (* if we fail to process a record (e.g. callback raises, ensur= e we resume at right place *) + let typ =3D I.r32 t in + let len =3D I.r32 t in + let p0 =3D pos_in t in + (match typ with + | x when x =3D (Type.end_ :> int) -> read_end t ~len on_end + | x when x =3D (Type.global_data :> int) -> read_global_data t ~len on_g= lobal_data + | x when x =3D (Type.connection_data :> int) -> read_connection_data t ~= len on_connection_data + | x when x =3D (Type.watch_data :> int) -> read_watch_data t ~len on_wat= ch_data + | x when x =3D (Type.transaction_data :> int) -> read_transaction_data t= ~len on_transaction_data + | x when x =3D (Type.node_data :> int) -> read_node_data t ~len on_node_= data + | x -> failwith (Printf.sprintf "Unknown record type: %x" x)); + let p1 =3D pos_in t in + read_expect t "record length" len (p1-p0) +end --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610749806; cv=none; d=zohomail.com; s=zohoarc; b=fzkyWRrKDgVhNlyGq2J1Uwa8rHavEVOlF+QR+QsELJjbxvcHCFw+UHH+2zpm8E1+4Twi3yfPtYBX2r4zq9XC1gf3yjwCYy9+hCLRa79L7YNKMLV9BSU309bLXx3ELnts0GALrAIOF7ctIRoUpp2NqGsfLDyIjq+5+xWNLjJ68nU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610749806; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=tbFN4KtKhTBX8OFEUsRBcs0CitQ+Sbij/Hn6iLrJBLw=; b=lLMWiw9iud1lmNlgJNm+pqYtNMLvgAOMKvF61EYj92EwDpzjtemkW28pdU1Rdl14Pso5BuLsFMrZ49vadYvMuIb9oZQYjs36yIKYiLukUvw2a0ZkCsm4QVIFNM8DH2INyoDg7hKz+x9U7UPWS1mQEhVhh1xc1BrbKx1aepqSSdg= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610749806004945.2421906813719; Fri, 15 Jan 2021 14:30:06 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68659.122948 (Exim 4.92) (envelope-from ) id 1l0XbL-00063E-7e; Fri, 15 Jan 2021 22:29:51 +0000 Received: by outflank-mailman (output) from mailman id 68659.122948; Fri, 15 Jan 2021 22:29:51 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XbL-000636-4b; Fri, 15 Jan 2021 22:29:51 +0000 Received: by outflank-mailman (input) for mailman id 68659; Fri, 15 Jan 2021 22:29:49 +0000 Received: from all-amaz-eas1.inumbo.com ([34.197.232.57] helo=us1-amaz-eas2.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XbJ-00062b-RB for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:29:49 +0000 Received: from esa3.hc3370-68.iphmx.com (unknown [216.71.145.155]) by us1-amaz-eas2.inumbo.com (Halon) with ESMTPS id fcbc4171-777d-4f1f-899b-05b569a5d2ce; Fri, 15 Jan 2021 22:29:48 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: fcbc4171-777d-4f1f-899b-05b569a5d2ce DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610749788; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=7FtxhcFGXeixISaf1MY+GwKw/DMXVUnwabrQqvgoO5Q=; b=JWykYM3BmBX5IBiuz56DqPvpLrKLT+EzNXwo46XlMeX0j5gRjs7hGWD5 7u0YWVK+TILlIx0w8/VQ/u/p5ej8cZe0Bys65kIIB11D+JEWypWmjTOCE eT6tuFi3gusno1Re0h/Rn7FJtFzfPiyeP1lEcCKSIsB2AKqf2FJL+w6Sk g=; Authentication-Results: esa3.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: 6PkBYPMIMklNQNl7Ds1iT2gy7F+DBMRbPnrr4njv5lnHg56aoXgovURdpyVmdROMbTHKoVUHzb RKzFV9/teGPcCKMwRaUig7/jO8HBGhleNCc77DTfpZ13oex8t2Aot2YH0f9xut16Ft4qG5l8V0 3KBXcHKUkVryQoF2LjYIpuMObqa9hl0wBuH34RNh/xv/utNKLIIwlUoWhgQavVIWlfkP3/Fz1D j0UwlTIe1F8dhytXFt6Vq87PJbqrQeEcXO4yxWJN3FWT1jeXSHjO7kCOlMMKltH/hVnap4doXZ xT0= X-SBRS: 5.1 X-MesageID: 35216600 X-Ironport-Server: esa3.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35216600" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Andrew Cooper" , George Dunlap , Ian Jackson , Jan Beulich , "Julien Grall" , Stefano Stabellini , Wei Liu , Christian Lindig , David Scott Subject: [PATCH v2 2/2] Makefile: add build-tools-oxenstored Date: Fri, 15 Jan 2021 22:28:41 +0000 Message-ID: <5805ac238f841a18e86e5a396ce7446f4914cf04.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) As a convenience so that oxenstored patches can be compile-tested using upstream's build-system before submitting upstream. Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Christian Lindig --- Changed since V1: * repost after XSA to avoid conflicts --- Makefile | 6 ++++++ tools/ocaml/Makefile | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/Makefile b/Makefile index 9ad2602f63..96d32cfd50 100644 --- a/Makefile +++ b/Makefile @@ -62,6 +62,12 @@ build-xen: build-tools: build-tools-public-headers $(MAKE) -C tools build =20 +.PHONY: build-tools-oxenstored +build-tools-oxenstored: build-tools-public-headers + $(MAKE) -s -C tools/ocaml clean + $(MAKE) -s -C tools/libs + $(MAKE) -C tools/ocaml build-tools-oxenstored + .PHONY: build-stubdom build-stubdom: mini-os-dir build-tools-public-headers $(MAKE) -C stubdom build diff --git a/tools/ocaml/Makefile b/tools/ocaml/Makefile index 66f2d6b131..a7c04b6546 100644 --- a/tools/ocaml/Makefile +++ b/tools/ocaml/Makefile @@ -26,3 +26,11 @@ clean: subdirs-clean =20 .PHONY: distclean distclean: subdirs-distclean + +.PHONY: build-tools-oxenstored +build-tools-oxenstored: + $(MAKE) -s -C libs/eventchn + $(MAKE) -s -C libs/mmap + $(MAKE) -s -C libs/xb + $(MAKE) -s -C libs/xc + $(MAKE) -C xenstored --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751556; cv=none; d=zohomail.com; s=zohoarc; b=F7jw9SzNayaYPPGYbNObbg2iYBfYQ4SZ7RW4tEOQFJiCN68XuuRvnAGiheulFhqBYbXbNDIrGqwyjt5NUHTwLzUtS/Yc30U+pOjDEWb37z3lfcwDExx5+K2PeQR7czdTwhRv0lDT4Jzk92QduD89LgTsduVc/Wu8t9s469LMDk0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751556; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=J0bUyhWwWkrSeOzEcBj5+InHuiWagSI45sYQYpKlKOw=; b=PXJrKNU8g64OR+g66KtV7UVtudwMtdAhIyBYbnabEOliPvLV9StFoI96kjWijyZAPESQKxJB0VjbvQdzzTzdQ/9oGLm9wruTAmxIrVQrRFBFA3iguTIrWveI30h21Jm62TU0flOIWivZmYgzq12enVUWw9VWulFIqiwUOfSOK4U= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610751556620760.8314231596653; Fri, 15 Jan 2021 14:59:16 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68772.123236 (Exim 4.92) (envelope-from ) id 1l0Y3U-0003Bk-Aa; Fri, 15 Jan 2021 22:58:56 +0000 Received: by outflank-mailman (output) from mailman id 68772.123236; Fri, 15 Jan 2021 22:58:56 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Y3U-0003BQ-6i; Fri, 15 Jan 2021 22:58:56 +0000 Received: by outflank-mailman (input) for mailman id 68772; Fri, 15 Jan 2021 22:58:54 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xyv-0001Wj-2v for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:54:13 +0000 Received: from esa6.hc3370-68.iphmx.com (unknown [216.71.155.175]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id 629a213d-4dfe-4247-bf5f-c08231a24f78; Fri, 15 Jan 2021 22:52:49 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 629a213d-4dfe-4247-bf5f-c08231a24f78 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751169; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=1fmXH+SV34jkCGcRhwIYTVVlbOpuedtyRttAjPwZ3tU=; b=CcVuX5iQNs+fVN63dfSRrLsvA6q/seyVFIkFDcNfMshZNM9Zj/oeWIaT yWVvjYZeLE7utfh4e4joa2CAmoq0335CYfWqitAN2fOMefSsD6hLl2lPo jqTK5rmY0QcMwhqgQBBVBlwJfYe7uMgHvvgVoucDopKQIloAdzVuW8SSl 8=; Authentication-Results: esa6.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: T6uE9a3+Hbivy7ic5p4Ho0uLZWHlEkl/q9h05/gdILwahYLCt++JDR8X6X08cYBMinvb6OivdF k+wPABfO6WAvOhXxAmB0ZcqtXv1dihG/xvPO1wfuV5Z9rJq3o6r9tnaDbC8UJiU/CriNC5PeKS 3wNLaVbm7iqU/z1uyFMTPCOOLjbEvxsTftN5AUQ8HxSYUf77VIfzRwM0dpye0XtkGw8joJBB03 Cnye1B64S1R6F0ywp2GmreXqXGUvj0miEoHp1xoz6N3Sc1RuluJbMVIwv7NWjAgA2klCqqDRGb 5Tk= X-SBRS: 5.1 X-MesageID: 35435287 X-Ironport-Server: esa6.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35435287" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu Subject: [PATCH v2 2/2] tools/ocaml/libs/xc: backward compatible domid control at domain creation time Date: Fri, 15 Jan 2021 22:28:53 +0000 Message-ID: <610924e3a2a426cfe1d7a614c095c15a095c3475.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) One can specify the domid to use when creating the domain, but this was har= dcoded to 0. Keep the existing `domain_create` function, and make domid an optional argu= ment. When not specified default to 0. A new version of xenopsd can choose to start using this, while old versions= of xenopsd will keep building and using the old API. (The ABI will change, but that changes every time a function is introduced/= removed or modified) Controlling the domid can be useful during testing or migration. Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Christian Lindig --- Changed since V1: * introduced an optional ?domid for better backwards compatibility * use CAMLparam3 because we have an additional parameter --- tools/ocaml/libs/xc/xenctrl.ml | 5 ++++- tools/ocaml/libs/xc/xenctrl.mli | 4 ++-- tools/ocaml/libs/xc/xenctrl_stubs.c | 6 +++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/tools/ocaml/libs/xc/xenctrl.ml b/tools/ocaml/libs/xc/xenctrl.ml index e878699b0a..e0a47c4769 100644 --- a/tools/ocaml/libs/xc/xenctrl.ml +++ b/tools/ocaml/libs/xc/xenctrl.ml @@ -179,9 +179,12 @@ let with_intf f =3D handle :=3D Some h; f h =20 -external domain_create: handle -> domctl_create_config -> domid +external domain_create_stub: handle -> domid -> domctl_create_config -> do= mid =3D "stub_xc_domain_create" =20 +let domain_create handle ?(domid=3D0) config =3D + domain_create_stub handle domid config + external domain_sethandle: handle -> domid -> string -> unit =3D "stub_xc_domain_sethandle" =20 diff --git a/tools/ocaml/libs/xc/xenctrl.mli b/tools/ocaml/libs/xc/xenctrl.= mli index e64907df8e..84311fa33d 100644 --- a/tools/ocaml/libs/xc/xenctrl.mli +++ b/tools/ocaml/libs/xc/xenctrl.mli @@ -143,8 +143,8 @@ val get_handle: unit -> handle option * would invalidate the handle that with_intf passes to its argument. *) val close_handle: unit -> unit =20 -external domain_create : handle -> domctl_create_config -> domid - =3D "stub_xc_domain_create" +val domain_create: handle -> ?domid:int -> domctl_create_config -> domid + external domain_sethandle : handle -> domid -> string -> unit =3D "stub_xc= _domain_sethandle" external domain_max_vcpus : handle -> domid -> int -> unit =3D "stub_xc_domain_max_vcpus" diff --git a/tools/ocaml/libs/xc/xenctrl_stubs.c b/tools/ocaml/libs/xc/xenc= trl_stubs.c index 94aba38a42..9a8dbe5579 100644 --- a/tools/ocaml/libs/xc/xenctrl_stubs.c +++ b/tools/ocaml/libs/xc/xenctrl_stubs.c @@ -175,9 +175,9 @@ static unsigned int ocaml_list_to_c_bitmap(value l) return val; } =20 -CAMLprim value stub_xc_domain_create(value xch, value config) +CAMLprim value stub_xc_domain_create(value xch, value wanted_domid, value = config) { - CAMLparam2(xch, config); + CAMLparam3(xch, wanted_domid, config); CAMLlocal2(l, arch_domconfig); =20 /* Mnemonics for the named fields inside domctl_create_config */ @@ -191,7 +191,7 @@ CAMLprim value stub_xc_domain_create(value xch, value c= onfig) #define VAL_MAX_MAPTRACK_FRAMES Field(config, 7) #define VAL_ARCH Field(config, 8) =20 - uint32_t domid =3D 0; + uint32_t domid =3D Int_val(wanted_domid); int result; struct xen_domctl_createdomain cfg =3D { .ssidref =3D Int32_val(VAL_SSIDREF), --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751196; cv=none; d=zohomail.com; s=zohoarc; b=dfh+YW/UebJmE0GuU2wfRCkR0QPKutA25JpWg6NP+bQ8B0MQknrKhFINoBIQJH5/qw5K3CauzV1UxFpeG1VrCQqnGepo9KU3/EjYz1zO31Gg2ZeqUJgAstsAU+MdmB8dr5PsJqmo89FCS1H/PYooor0bR+OaIjA85fD3my/tEyU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751196; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=92fROIy7t9eLqwFF2gn4qMJTmSor8NCBRTb6i4T1onY=; b=ddq5bdTEP3mLL/bu56WJtR5XnrNUzbOA8/VOyEnA/EU/Sz0Dv5MD0ISMHzEK/ApuPHl/+WSsELiKYKr+Q20j1W53A4DfzYQb2jQPDwqwQhOAPotURQHthcrPLA0ta53OFqMYtyzD9cZz6wSOQcrBrcOQIm7sbyY+YdcLtL0YMEM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610751196117179.88386914527746; Fri, 15 Jan 2021 14:53:16 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68736.123188 (Exim 4.92) (envelope-from ) id 1l0Xxi-00029m-5b; Fri, 15 Jan 2021 22:52:58 +0000 Received: by outflank-mailman (output) from mailman id 68736.123188; Fri, 15 Jan 2021 22:52:58 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xxh-00029d-Uw; Fri, 15 Jan 2021 22:52:57 +0000 Received: by outflank-mailman (input) for mailman id 68736; Fri, 15 Jan 2021 22:52:56 +0000 Received: from all-amaz-eas1.inumbo.com ([34.197.232.57] helo=us1-amaz-eas2.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xxg-0001jt-JL for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:52:56 +0000 Received: from esa5.hc3370-68.iphmx.com (unknown [216.71.155.168]) by us1-amaz-eas2.inumbo.com (Halon) with ESMTPS id 88666d25-87f4-428c-bada-d362b87ac924; Fri, 15 Jan 2021 22:52:45 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 88666d25-87f4-428c-bada-d362b87ac924 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751165; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=jgfv4P04PbdaWw919nXNWbo+zvG8+hgWuQW3OvzjTJ0=; b=P/2qFlHlGYQCWTa1lmc98Q0hLvSPQzK1JMI2kGRlKhqjDyTUw5DorGvC cA2KHLE3ew+ql8JxCQv9q69/f4RWBYs/5dCAYLb7IfbXYZClKRg/QDs1E C5XWI9lHrdWfQHqE3nj3MhU1R7+a5nAlHcDLSyBZPAs5CNgys205d9c/J M=; Authentication-Results: esa5.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: VKqtPcqsjhHQvdTIHL3aQ47xaq8O3xmEdlojko1lowvTT5oaY4xvpyMOvFCTDzf2/HVSqt+8MA Djcx8v8fVoUpvaDGx82w5yRVsyS/40OAJ6N6ahRMqNRyeHXJeCdhePsVxIxmxYBy+ZPlp+OlTK 1FMmSeBZOnpK1Ubjd1B0M4GYNjaxDzQCDLSbvJu+s8sBxzdq2ubJ9QlNdzAFLG0IE/ylvQRHhs 7x/FXZqoWXVEbw34aH2CVHmZCC8IVY9trvq8V4VouhHaQT9e7k7a4NbGaf23zpteFdXX2LjTlc BSE= X-SBRS: 5.1 X-MesageID: 35206337 X-Ironport-Server: esa5.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35206337" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Andrew Cooper" , George Dunlap , Ian Jackson , Jan Beulich , "Julien Grall" , Stefano Stabellini , Wei Liu , Christian Lindig , David Scott Subject: [PATCH v1 2/4] tools/ocaml/xenstored: fix quota calculation for mkdir EEXIST Date: Fri, 15 Jan 2021 22:29:07 +0000 Message-ID: <6293bbc59bf4904e975dcc0e31608f858d34d84e.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) We increment the domain's quota on mkdir even when the node already exists. This results in a quota inconsistency after live update, where reconstructing the tree from scratch results in a different quota. Not a security issue because the domain uses up quota faster, so it will only get a Quota error sooner than it should. Discovered by the structured fuzzing test: ``` live-update-agree: FAIL When given the input: [{ "domid" =3D 0; "cmd" =3D { "tid" =3D 0; "rid" =3D 0; "op" =3D MKDIR; "data" =3D "/" } }; { "domid" =3D 0; "cmd" =3D { "tid" =3D 0; "rid" =3D 0; "op" =3D DEBUG; "data" =3D "live-update\000-s" = } }] the test failed: store agrement: diff --git 1/tmp/expected5b4372.txt 2/tmp/actual1c18b5.= txt index ac39964836..af318026ec 100644 Acked-by: Christian Lindig --- 1/tmp/expected5b4372.txt +++ 2/tmp/actual1c18b5.txt @@ -1,9 +1,9 @@ { "stat_transaction_coalesce" =3D 0; "stat_transaction_abort" =3D 0; "store" =3D /{n0} /tool{n0} /local{n0} ; "quota" =3D { "maxent" =3D 8192; "maxsize" =3D 2048; "cur" =3D (hashtbl (0, +3+))-2-)) } } Fatal error: exception Crowbar.TestFailure ``` This shows that the quota was 2 instead of 3 after a live update. Signed-off-by: Edwin T=C3=B6r=C3=B6k --- tools/ocaml/xenstored/store.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml index a9c079a417..1a9f71fa62 100644 --- a/tools/ocaml/xenstored/store.ml +++ b/tools/ocaml/xenstored/store.ml @@ -420,6 +420,7 @@ let mkdir store perm path =3D (* It's upt to the mkdir logic to decide what to do with existing path *) if not (existing || (Perms.Connection.is_dom0 perm)) then Quota.check sto= re.quota owner 0; store.root <- path_mkdir store perm path; + if not existing then Quota.add_entry store.quota owner =20 let rm store perm path =3D --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751209; cv=none; d=zohomail.com; s=zohoarc; b=Ug6922rC866x7Jt59gccAXaqXp3TsE42z/DHwSkkLNRnykA7/i/vGwseGf1c++8VzgdeI4n/1s/CBeBXGyiMFWq3FSs0kFWWdrobYLUT3fMEOMrQEnXVPfXt5OpWL7/dyTwICOFL76Ql+OrG7AqsaIuRDXi2K6lQ2T9V2PbC5ko= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751209; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=ZReZjs6mRPHrFDjnp2pET3QfRVg0n+POVqKFGwFaX9g=; b=BDPPOyImipbqNJuPthqKBwYXKo5aeYf6zKRQhgFx0UJ0D7b/M//t9MiwLyy3+DNwNzjc9wISD96K2945OIfIly2+bx+VxwDYPMlh7RE318k9PmgYNy26nBE0sGWhE2EjBO+0LA/DDCWfd510sHQo7a2HeNe+JbCEgJfmbJlVAUc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610751209643671.778845856533; Fri, 15 Jan 2021 14:53:29 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68748.123224 (Exim 4.92) (envelope-from ) id 1l0Xxy-0002WJ-M7; Fri, 15 Jan 2021 22:53:14 +0000 Received: by outflank-mailman (output) from mailman id 68748.123224; Fri, 15 Jan 2021 22:53:14 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xxy-0002W9-Gq; Fri, 15 Jan 2021 22:53:14 +0000 Received: by outflank-mailman (input) for mailman id 68748; Fri, 15 Jan 2021 22:53:13 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xxx-0001Wj-04 for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:53:13 +0000 Received: from esa6.hc3370-68.iphmx.com (unknown [216.71.155.175]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id 3f5fe6cd-fc20-48c5-bbed-42ec3f69d494; Fri, 15 Jan 2021 22:52:35 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 3f5fe6cd-fc20-48c5-bbed-42ec3f69d494 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751154; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=X7XyD2V6jMFkw/QtmgQYzQRGshY0fkhqbVCJVUmwv1A=; b=QcxUH91eX51ccsFO6ttyQ4QWcuP1q6imal2VhHnuzrVS2GIDine7IJYs utvgQS8DDbDO9RU4qh0IUNKz/lq3eQzSOyB1MEiENyLTA+z+uttcmGviC YJ+uJ7WycUuOb+bVq/g3B4Qike6UgQW45btGMUJhNB1LACAQiMKu5CP3Z c=; Authentication-Results: esa6.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: amcrpVM51Xv+RMjduqtrDS7MY7GC3tzNnjjQlLbMq9NBB+U8ZEzJlWvUdV0GFczUC6uhPSfocp gLKU4kTmfYL8q7IqXireAozZ99Q6m6Hf1kanzVWQWEvh97GqDCRZ1acdQb9jlb7q6wuUVKJKP5 GpjiC0Ry//tbpKFHTJFpD3YB7wGYWrLl+jJdaqiEEuRglyaIifl0cPpUtB659nFj6q9OZFrei4 f8Ci8wweyx6pUz4RmDyOVPyZMP29SenWvGF1wXO6r4LqeRWuesDFtYghRsnPts4RrIYO2nlGrR Fns= X-SBRS: 5.1 X-MesageID: 35435269 X-Ironport-Server: esa6.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35435269" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu Subject: [PATCH v4 2/4] tools/ocaml/xenstored: backport find_opt/update from 4.06 Date: Fri, 15 Jan 2021 22:28:56 +0000 Message-ID: X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) We are currently on OCaml 4.02 as minimum version. To make the followup optimizations compile backport these functions from OCaml 4.06. This implementation is less efficient than the one in the 4.06 standard library which has access to the internals of the Map. Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Christian Lindig --- Changed since V3: * repost after XSA to avoid conflicts --- tools/ocaml/xenstored/stdext.ml | 19 +++++++++++++++++++ tools/ocaml/xenstored/trie.ml | 2 ++ 2 files changed, 21 insertions(+) diff --git a/tools/ocaml/xenstored/stdext.ml b/tools/ocaml/xenstored/stdext= .ml index e1567c4dfa..0640602449 100644 --- a/tools/ocaml/xenstored/stdext.ml +++ b/tools/ocaml/xenstored/stdext.ml @@ -50,6 +50,25 @@ module Filename =3D struct cmd :: args |> List.map quote |> String.concat " " end =20 +module Map =3D struct +module Make(Ord: Map.OrderedType) =3D struct + +include Map.Make(Ord) + +let find_opt k t =3D try Some (find k t) with Not_found -> None + +let update k f t =3D + let r =3D find_opt k t in + let r' =3D f r in + match r, r' with + | None, None -> t + | Some _, None -> remove k t + | Some r, Some r' when r =3D=3D r' -> t + | _, Some r' -> add k r' t + +end +end + module String =3D struct include String =20 let of_char c =3D String.make 1 c diff --git a/tools/ocaml/xenstored/trie.ml b/tools/ocaml/xenstored/trie.ml index dc42535092..f513f4e608 100644 --- a/tools/ocaml/xenstored/trie.ml +++ b/tools/ocaml/xenstored/trie.ml @@ -13,6 +13,8 @@ * GNU Lesser General Public License for more details. *) =20 +open Stdext + module Node =3D struct type ('a,'b) t =3D { --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751187; cv=none; d=zohomail.com; s=zohoarc; b=KO7RrkF/5pmVlx0+Z6KbAf5LuMHJHtTTfT4GhUgzNK8LfGDv994uTIdrzGVQT2a5vJqTFqVbm9OFAaPh7WUdPsdjpLCSwef+5M8MwITzybiw1dpSpP9OgL6/MqqqDcqBF4FnMYFJvasTUC0c1bJHSJ56636DEeBfR16iwfDFJNM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751187; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=hzp5mcM2Yzyowj56/KaWXL560FodGmHwyirH8sf6Y24=; b=eVDPSVc3KFV3rd3wqHqQFHAMJVFI0Slngp/R7l2gSafOh2EH8Blk8mPmJd3z/Aj20mAz+ht8J/N0aH2ij2o45uTpNO8plDtk38WqeqS3TehEEiyrzz/7zy8LeBjFV7eCMhSXEyKbrGTG5TvTvIOlZ2o6Zqh2MIykfrPMYMTNbso= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610751187013596.781223361326; Fri, 15 Jan 2021 14:53:07 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68733.123163 (Exim 4.92) (envelope-from ) id 1l0Xxc-0001zp-8y; Fri, 15 Jan 2021 22:52:52 +0000 Received: by outflank-mailman (output) from mailman id 68733.123163; Fri, 15 Jan 2021 22:52:52 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xxc-0001ze-4u; Fri, 15 Jan 2021 22:52:52 +0000 Received: by outflank-mailman (input) for mailman id 68733; Fri, 15 Jan 2021 22:52:51 +0000 Received: from all-amaz-eas1.inumbo.com ([34.197.232.57] helo=us1-amaz-eas2.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xxb-0001jt-JO for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:52:51 +0000 Received: from esa4.hc3370-68.iphmx.com (unknown [216.71.155.144]) by us1-amaz-eas2.inumbo.com (Halon) with ESMTPS id 4d891335-8e82-4711-b386-0ce786eecc66; Fri, 15 Jan 2021 22:52:43 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 4d891335-8e82-4711-b386-0ce786eecc66 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751163; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=PHd8ibAmz3y4d18ZBR6evC+akkeyLiBTovT6aCSEtlo=; b=aI+l2DuIzvIgz0JThekgEnOFfKK8WYs8zPbA3gMNEhPK9bPafN85zvax Fk+G5ilt4WI7LDHqz6amtDQhlH/n9a5J+unxMxgxV25M+V5f0GvCUVuHq XCqgB35wVUDT6yejl9K0RNjyghwx6DVPw7SHrcaa+y7B/As5F0K5iAEqo 8=; Authentication-Results: esa4.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: MoOL9HgsoRlZpHvVYMwVg76P/8QHGE+y3iOBZGBMrY+r4560E3TbKuWHW9LEaFqBNuY6ooGuXA +VzD4cDRrWYhZV19DGyaRnbfeR5RyzKMin87dONsdRaEVvxEw7J70DDm+2QxK7xczOj6Dh0Vqi o5nZtjwdAz6Xi9gRHsTqz9CmgF9S36d7vxiIqFiAg4emNrMkI+Bhtd6js4M5rV6VypuXCw3L8V WBD623jxKxNa/M+A9Y4M3L4XqjM1aXWA2pbWI1YROUtVdJ3AiknRjTggvbwomTBHTWiQ+fKQsY 3/A= X-SBRS: 5.1 X-MesageID: 36512267 X-Ironport-Server: esa4.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="36512267" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu Subject: [PATCH v1 3/5] tools/ocaml/xenstored: add binary dump format support Date: Fri, 15 Jan 2021 22:29:02 +0000 Message-ID: <4142c4a8c0859cace45473780a2ac63823e6af36.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Christian Lindig --- tools/ocaml/xenstored/connection.ml | 63 +++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/co= nnection.ml index 51041dde8e..1f9fe9e3b2 100644 --- a/tools/ocaml/xenstored/connection.ml +++ b/tools/ocaml/xenstored/connection.ml @@ -17,6 +17,7 @@ exception End_of_file =20 open Stdext +module LR =3D Disk.LiveRecord =20 let xenstore_payload_max =3D 4096 (* xen/include/public/io/xs_wire.h *) =20 @@ -77,6 +78,10 @@ let number_of_transactions con =3D =20 let get_domain con =3D con.dom =20 +let get_id con =3D match con.dom with +| None -> 2*LR.domid_invalid + con.anonid +| Some dom -> 1 + Domain.get_id dom + let anon_id_next =3D ref 1 =20 let get_domstr con =3D @@ -279,6 +284,9 @@ let end_transaction con tid commit =3D let get_transaction con tid =3D Hashtbl.find con.transactions tid =20 +let iter_transactions con f =3D + Hashtbl.iter f con.transactions + let do_input con =3D Xenbus.Xb.input con.xb let has_input con =3D Xenbus.Xb.has_in_packet con.xb let has_partial_input con =3D match con.xb.Xenbus.Xb.partial_in with @@ -337,22 +345,45 @@ let incr_ops con =3D con.stat_nb_ops <- con.stat_nb_o= ps + 1 let stats con =3D Hashtbl.length con.watches, con.stat_nb_ops =20 -let dump con chan =3D - let id =3D match con.dom with - | Some dom -> - let domid =3D Domain.get_id dom in - (* dump domain *) - Domain.dump dom chan; - domid - | None -> - let fd =3D con |> get_fd |> Utils.FD.to_int in - Printf.fprintf chan "socket,%d\n" fd; - -fd - in - (* dump watches *) - List.iter (fun (path, token) -> - Printf.fprintf chan "watch,%d,%s,%s\n" id (Utils.hexify path) (Utils.hex= ify token) - ) (list_watches con) +let serialize_pkt_in buf xb =3D + let open Xenbus.Xb in + Queue.iter (fun p -> Buffer.add_string buf (Packet.to_string p)) xb.pkt_i= n; + match xb.partial_in with + | NoHdr (to_read, hdrb) -> + (* see Xb.input *) + let used =3D Xenbus.Partial.header_size () - to_read in + Buffer.add_subbytes buf hdrb 0 used + | HaveHdr p -> + p |> Packet.of_partialpkt |> Packet.to_string |> Buffer.add_string buf + +let serialize_pkt_out buf xb =3D + let open Xenbus.Xb in + Buffer.add_string buf xb.partial_out; + Queue.iter (fun p -> Buffer.add_string buf (Packet.to_string p)) xb.pkt_o= ut + +let dump con store chan =3D + let conid =3D get_id con in + let conn =3D match con.dom with + | None -> LR.Socket (get_fd con) + | Some dom -> LR.Domain { + id =3D Domain.get_id dom; + target =3D LR.domid_invalid; (* TODO: we do not store this info *) + remote_port =3D Domain.get_remote_port dom + } in + let pkt_in =3D Buffer.create 4096 in + let pkt_out =3D Buffer.create 4096 in + serialize_pkt_in pkt_in con.xb; + serialize_pkt_out pkt_out con.xb; + LR.write_connection_data chan ~conid ~conn pkt_in con.xb.partial_out pkt= _out; + + con |> list_watches + |> List.rev (* preserve order in dump/reload *) + |> List.iter (fun (wpath, token) -> + LR.write_watch_data chan ~conid ~wpath ~token + ); + let conpath =3D get_path con in + iter_transactions con (fun _ txn -> + Transaction.dump store conpath ~conid txn chan) =20 let debug con =3D let domid =3D get_domstr con in --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751197; cv=none; d=zohomail.com; s=zohoarc; b=cPLR3uw7KIGaCvrD8v4lKkqk3Yu223kA8xMx+K8foMIOE2N2HEnRYJroJ+KHxHk4CgIxAOb+CmlkrPMoDq/ULORl+fqoObKuSM0eNTl0PntegVnldLKFxjZVhsHcK9HuCuJeI60XNbilJubCgDgy5tVghNTgDaTJ4gBVLcnGtCE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751197; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=e3Kn0UoOJpfOZzhpepOls5tSL0sYeZliMRp+FRY95Ng=; b=iOZDjJtuKyGa5S735LSUeaQXY3pseTRpyQ8pBpTdRR+WkB+FJVC6ZWh6Ihqsu2jOSRCqsg6oYtgb2eHGglmbzY0Hk3FY5OtiM+sHsXMx9zq7Dg7cna0fpx5NPrpGewfVrXkQ074CDxLOubUlgFHkjEfnXaSj6IMpA73OsE7xvro= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610751197847865.8254909624782; Fri, 15 Jan 2021 14:53:17 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68734.123176 (Exim 4.92) (envelope-from ) id 1l0Xxe-00024M-MF; Fri, 15 Jan 2021 22:52:54 +0000 Received: by outflank-mailman (output) from mailman id 68734.123176; Fri, 15 Jan 2021 22:52:54 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xxe-00024B-Hu; Fri, 15 Jan 2021 22:52:54 +0000 Received: by outflank-mailman (input) for mailman id 68734; Fri, 15 Jan 2021 22:52:53 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xxc-0001Wj-VU for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:52:52 +0000 Received: from esa1.hc3370-68.iphmx.com (unknown [216.71.145.142]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id 3292c6ea-e169-483b-a6ed-ac21b2d470a0; Fri, 15 Jan 2021 22:52:32 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 3292c6ea-e169-483b-a6ed-ac21b2d470a0 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751152; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=ZLD/rdH++y685dPBFUYhilMOR3v1G6op5GSP1Bi+bUQ=; b=OR1Frk0ybUU8Vu5fsT96CFzE4CAwQofmTXch2JvWLyAPet0Yndd0Ahw5 Z4KyiF9fEGzr4YCH+1sP9fkFBbq0dI/Nx03zRXLtBfcvqy7MZstwX84w/ HTqg0e57ETl/0Ww5MZlPKc/PKYS2ktCQWgMrxwcNDcCzkW/OzYhhCWrGp 0=; Authentication-Results: esa1.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: dDuZDu7tcut9zupgjwfW01a6YpUEC3otCm/2Zm69JWBoi2OMLrP8pQja1SSsIXjKZ81+OoThSt 5ncmLmhhvem4ZBG7agWEXCX86T5zWILAWRX3qf/OxTOquUx7vb7MHyVEmAWR21oYk04rHRWoFw qCf+2pksUisd2/hV+Bnkr3IncvSPAa9AO2ovpTYH5liKznm8lRaWJY8h7do9oTpq8LKwQusewO n+wRLhyi7xsnAZ2zgGu11RewGaxPK9yfBbAvvgLW+S5bdoKU6QIryNYG/qBLpKpyKgwH6MZHNs na4= X-SBRS: 5.1 X-MesageID: 35591680 X-Ironport-Server: esa1.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35591680" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu Subject: [PATCH v4 3/4] tools/ocaml/xenstored: use more efficient node trees Date: Fri, 15 Jan 2021 22:28:57 +0000 Message-ID: X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) This changes the output of xenstore-ls to be sorted. Previously the keys were listed in the order in which they were inserted in. docs/misc/xenstore.txt doesn't specify in what order keys are listed. Map.update is used to retain semantics with replace_child: only an existing child is replaced, if it wasn't part of the original map we don't add it. Similarly exception behaviour is retained for del_childname and related functions. Entries are stored in reverse sort order, so that upon Map.fold the constructed list is sorted in ascending order and there is no need for a List.rev. This changes the semantics and is not suitable as is for a backport. It reveals bugs in buggy clients that depend on xenstore entry order, however those clients should be fixed. (We found one such bug in our internal testsuite where the first xenstore entry from a subtree was always dropped, and changing the listing order changed what key got dropped making the test fail) Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Christian Lindig --- Changed since V3: * repost after XSA to avoid conflicts --- tools/ocaml/xenstored/store.ml | 48 +++++++++++++++----------------- tools/ocaml/xenstored/symbol.ml | 4 +++ tools/ocaml/xenstored/symbol.mli | 3 ++ 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml index 9c226e4ef7..5f155f45eb 100644 --- a/tools/ocaml/xenstored/store.ml +++ b/tools/ocaml/xenstored/store.ml @@ -16,17 +16,19 @@ *) open Stdext =20 +module SymbolMap =3D Map.Make(Symbol) + module Node =3D struct =20 type t =3D { name: Symbol.t; perms: Perms.Node.t; value: string; - children: t list; + children: t SymbolMap.t; } =20 let create _name _perms _value =3D - { name =3D Symbol.of_string _name; perms =3D _perms; value =3D _value; ch= ildren =3D []; } + { name =3D Symbol.of_string _name; perms =3D _perms; value =3D _value; ch= ildren =3D SymbolMap.empty; } =20 let get_owner node =3D Perms.Node.get_owner node.perms let get_children node =3D node.children @@ -42,38 +44,34 @@ let set_value node nvalue =3D let set_perms node nperms =3D { node with perms =3D nperms } =20 let add_child node child =3D - { node with children =3D child :: node.children } + let children =3D SymbolMap.add child.name child node.children in + { node with children } =20 let exists node childname =3D let childname =3D Symbol.of_string childname in - List.exists (fun n -> Symbol.equal n.name childname) node.children + SymbolMap.mem childname node.children =20 let find node childname =3D let childname =3D Symbol.of_string childname in - List.find (fun n -> Symbol.equal n.name childname) node.children + SymbolMap.find childname node.children =20 let replace_child node child nchild =3D - (* this is the on-steroid version of the filter one-replace one *) - let rec replace_one_in_list l =3D - match l with - | [] -> [] - | h :: tl when Symbol.equal h.name child.name -> nchild :: tl - | h :: tl -> h :: replace_one_in_list tl - in - { node with children =3D (replace_one_in_list node.children) } + { node with + children =3D SymbolMap.update child.name + (function None -> None | Some _ -> Some nchild) + node.children + } =20 let del_childname node childname =3D let sym =3D Symbol.of_string childname in - let rec delete_one_in_list l =3D - match l with - | [] -> raise Not_found - | h :: tl when Symbol.equal h.name sym -> tl - | h :: tl -> h :: delete_one_in_list tl - in - { node with children =3D (delete_one_in_list node.children) } + { node with children =3D + SymbolMap.update sym + (function None -> raise Not_found | Some _ -> None) + node.children + } =20 let del_all_children node =3D - { node with children =3D [] } + { node with children =3D SymbolMap.empty } =20 (* check if the current node can be accessed by the current connection wit= h rperm permissions *) let check_perm node connection request =3D @@ -87,12 +85,12 @@ let check_owner node connection =3D raise Define.Permission_denied; end =20 -let rec recurse fct node =3D fct node; List.iter (recurse fct) node.childr= en +let rec recurse fct node =3D fct node; SymbolMap.iter (fun _ -> recurse fc= t) node.children =20 (** [recurse_map f tree] applies [f] on each node in the tree recursively = *) let recurse_map f =3D let rec walk node =3D - f { node with children =3D List.rev_map walk node.children |> List.rev } + f { node with children =3D SymbolMap.map walk node.children } in walk =20 @@ -336,7 +334,7 @@ let ls store perm path =3D Node.check_perm cnode perm Perms.READ; cnode.Node.children in Path.apply store.root path do_ls in - List.rev (List.map (fun n -> Symbol.to_string n.Node.name) children) + SymbolMap.fold (fun k _ accu -> Symbol.to_string k :: accu) children [] =20 let getperms store perm path =3D if path =3D [] then ( @@ -366,7 +364,7 @@ let traversal root_node f =3D let rec _traversal path node =3D f path node; let node_path =3D Path.of_path_and_name path (Symbol.to_string node.Node= .name) in - List.iter (_traversal node_path) (List.rev node.Node.children) + SymbolMap.iter (fun _ -> _traversal node_path) node.Node.children in _traversal [] root_node =20 diff --git a/tools/ocaml/xenstored/symbol.ml b/tools/ocaml/xenstored/symbol= .ml index 2b41d120f6..301639f16f 100644 --- a/tools/ocaml/xenstored/symbol.ml +++ b/tools/ocaml/xenstored/symbol.ml @@ -31,6 +31,10 @@ let equal a b =3D (* compare using physical equality, both members have to be part of the = above weak table *) a =3D=3D b =20 +let compare a b =3D + if equal a b then 0 + else -(String.compare a b) + let stats () =3D let len, entries, _, _, _, _ =3D WeakTable.stats tbl in len, entries diff --git a/tools/ocaml/xenstored/symbol.mli b/tools/ocaml/xenstored/symbo= l.mli index 586ab57507..dd0f014796 100644 --- a/tools/ocaml/xenstored/symbol.mli +++ b/tools/ocaml/xenstored/symbol.mli @@ -32,6 +32,9 @@ val to_string : t -> string val equal: t -> t -> bool (** Compare two symbols for equality *) =20 +val compare: t -> t -> int +(** Compare two symbols *) + (** {6 Statistics } *) =20 val stats : unit -> int * int --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610749810; cv=none; d=zohomail.com; s=zohoarc; b=CXjQrCTtbvQoemz4K+nUeEpGiyTLabJ81c9unYlas/EyQAnnsTyCrlm5WVj3N/HWX88fAlzXZa4UtGyEKXrawzgvFQebYyEA/FOTyYRO3/cGdlIQfcVUTeGeeVdFMb+ZpQ0hc1ApotMpQ21RMv4CpG283sCqSbig5wBY2tLLQFE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610749810; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=05786td4076rH3PpzjM8vH6gDAm/8jshPRUka7c0ZMg=; b=mNZHyRFJeld1k4aOcnWuSk+Qwp0Uwwa1E90meqmP3NnQD6P4alhxXCr5Hy8dAX6aY36PJ7k2kKvsX+UAs05exwP2CJDhqrjxBCk/pwGuNUKCUKkZxZFg8jKtX4TVfSAWvn+xvdsRJrfKI8pT2c2GvxfBPUKhPtqag7AWR8pJsMM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 161074981005896.28342534661249; Fri, 15 Jan 2021 14:30:10 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68661.122959 (Exim 4.92) (envelope-from ) id 1l0XbP-00067m-IM; Fri, 15 Jan 2021 22:29:55 +0000 Received: by outflank-mailman (output) from mailman id 68661.122959; Fri, 15 Jan 2021 22:29:55 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XbP-00067W-Eg; Fri, 15 Jan 2021 22:29:55 +0000 Received: by outflank-mailman (input) for mailman id 68661; Fri, 15 Jan 2021 22:29:54 +0000 Received: from all-amaz-eas1.inumbo.com ([34.197.232.57] helo=us1-amaz-eas2.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XbO-00062b-7z for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:29:54 +0000 Received: from esa2.hc3370-68.iphmx.com (unknown [216.71.145.153]) by us1-amaz-eas2.inumbo.com (Halon) with ESMTPS id baa3299e-2f51-4b81-8b79-0bcd3ff41003; Fri, 15 Jan 2021 22:29:49 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: baa3299e-2f51-4b81-8b79-0bcd3ff41003 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610749789; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=NHqTfTuH/ZE50yfbzAZKh31IXsGU89p4AdJLcN9tjb0=; b=bBB0TI/EpCW2h9OIlINjoJxd7NsDmRVd2+p/uwKR/QnOW/soMLFoVMtu A19hBsvL8j0RaZDxNK6FcU6/ZBzkF1eqxxAs6On9YpynnoA2L+sfNs4mc sN2a5zGNFc4bBEG6BkUh/51fM7c7s3RgeAprnVdc4mUkv+slQnrtKF10C M=; Authentication-Results: esa2.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: Wo0p2h268fOq2mvPfOhvZ6ygDMuZtg6OFRmvbqMszVyh23LaMimvh9i2eRntArErmyZWK2orkQ 7jkLcwQGs89rczUZgR/Zu1TXzRFUcwkTVXElzRFvS5hxbAQS1CFTOE/fYmI6R97udzMKst4jtf Py2UODmWIaTJ1JsQ5zBtn3jEiKOSUywK5sH1p70NF4fKolFKLKFNmUogdqJhui/KqH3ZjNYIRo n232mpYy/ZubG6MwqauNBrIbTABJ6tnm6soONt12PmZ8jXRf8aB06DmHXl4KjlmKvkJsqPusPM jpU= X-SBRS: 5.1 X-MesageID: 35262766 X-Ironport-Server: esa2.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35262766" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Andrew Cooper" , George Dunlap , Ian Jackson , Jan Beulich , "Julien Grall" , Stefano Stabellini , Wei Liu Subject: [PATCH v2 3/8] docs/designs/xenstore-migration.md: clarify that deletes are recursive Date: Fri, 15 Jan 2021 22:28:45 +0000 Message-ID: <9f3823a494bd512348812355fbfecf6be447aca0.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Christian Lindig --- Changed since V1: * post publicly now that the XSA is out --- docs/designs/xenstore-migration.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/designs/xenstore-migration.md b/docs/designs/xenstore-mig= ration.md index 2ce2c836f5..f44bc0c61d 100644 --- a/docs/designs/xenstore-migration.md +++ b/docs/designs/xenstore-migration.md @@ -365,7 +365,8 @@ record previously present). | | 0x0001: read | | | 0x0002: written | | | | -| | The value will be zero for a deleted node | +| | The value will be zero for a recursively | +| | deleted node | | | | | `perm-count` | The number (N) of node permission specifiers | | | (which will be 0 for a node deleted in a | --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751185; cv=none; d=zohomail.com; s=zohoarc; b=F5bjtk2QhCVioqnOW1vC/qWxbG77HO+Qg0CvH0C38aXuy0Q6jq5/5g+INnGr2fbXanO5VELicAiVeo9g762a1mj/6Z9FZ6cVuMvVzysdW2iYxAPdfIF9A2GYwdiUpnoZtjJZKNmug2tY80xyZUsLoyDFhwxbS4HXni7X04Ps29Q= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751185; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=jz6RsZXgsbmjXRMnaCdV8Bqcv1SJLJcO5v9mmXb2Wa0=; b=iAXnDvxK2W8HkE5Np2742sBW+GvZmsCUsQBelciOKdB92WGvGld0DIJ2fZ7rCM9jVoVoqNINisuTzGOhdmhYqwYxTr0GLwl6+uc0Pq08cbddFUJZydQLC41ZZsQTlARjCgLWgUzuGHN+elvL2KiPz6zTncfjcPQ9wqZ9vRqrf/A= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610751185464608.03708881058; Fri, 15 Jan 2021 14:53:05 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68729.123152 (Exim 4.92) (envelope-from ) id 1l0XxX-0001tJ-VK; Fri, 15 Jan 2021 22:52:47 +0000 Received: by outflank-mailman (output) from mailman id 68729.123152; Fri, 15 Jan 2021 22:52:47 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XxX-0001t9-QF; Fri, 15 Jan 2021 22:52:47 +0000 Received: by outflank-mailman (input) for mailman id 68729; Fri, 15 Jan 2021 22:52:46 +0000 Received: from all-amaz-eas1.inumbo.com ([34.197.232.57] helo=us1-amaz-eas2.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XxW-0001jt-JH for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:52:46 +0000 Received: from esa2.hc3370-68.iphmx.com (unknown [216.71.145.153]) by us1-amaz-eas2.inumbo.com (Halon) with ESMTPS id cbd5bad1-0521-4255-9081-413b880163cd; Fri, 15 Jan 2021 22:52:42 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: cbd5bad1-0521-4255-9081-413b880163cd DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751162; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=m+mU/BwTks6nFEMCGPDEd+poI05u50+/Dta57xu6Hio=; b=SC4hW2RcpZDzQqkzPPKAm28RhWaIbKiZ0MFXq7bV6FmaK6dtBmcUDwq7 w7ZYFs5neA3Ds1JX0ViPQyHdQ0EQA9HgbDBnhqVf5SPhz20sybMfjIJOZ 6fBv18UnFHhkqNG4zBaKG8qh1YcS2CqwGkebxTIt4EEd6wl1Y7JvM3Jp5 Q=; Authentication-Results: esa2.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: +/LaM1m+rcM5Pq74ZGY60/XWuFJNnjkfXHsM05xZlzUgtwMAilYZZNon8dyE9gIa6mse3zQ1m4 OgskTagfrfHONx+iCP+6tBsPPbCvTEo7E2Qo1PNSJXX7DdpK7K2wJ5JLDvqnZrk9ncvSOqE5hV kU4HUUVCwYUGXEeW1ckW+zzD2rXMv6eOgk3eMxFDnsHoNhrU35JBjrctwRWiH6wQ7moClydpx1 KSNkEg0iuiQU0jtwde1QYUZ8GqJWGvVyi3GnqXadR9SUrLjUHDIcZTzA1tiv57YvPM85beVs0H OTo= X-SBRS: 5.1 X-MesageID: 35263553 X-Ironport-Server: esa2.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35263553" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu Subject: [PATCH v1 3/4] tools/ocaml/xenstored: reject invalid watch paths early Date: Fri, 15 Jan 2021 22:29:08 +0000 Message-ID: <2fc657a7d7af881f2c0ee437ddaec668452419fe.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) Watches on invalid paths were accepted, but they would never trigger. The client also got no notification that its watch is bad and would never trigger. Found again by the structured fuzzer, due to an error on live update reload: the invalid watch paths would get rejected during live update and the list of watches would be different pre/post live update. This was found by an older version of the fuzzer: ``` Test live-update failed (507 shrink steps): [NEW; (0, None, WATCH ([""; ""], "")); (0, None, CONTROL live-update ())] ``` The testcase is watch on `//`, which is an invalid path. Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Christian Lindig --- tools/ocaml/xenstored/connection.ml | 5 ++--- tools/ocaml/xenstored/connections.ml | 4 +++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/co= nnection.ml index 1f9fe9e3b2..c7f22e5ee9 100644 --- a/tools/ocaml/xenstored/connection.ml +++ b/tools/ocaml/xenstored/connection.ml @@ -163,18 +163,17 @@ let get_children_watches con path =3D let is_dom0 con =3D Perms.Connection.is_dom0 (get_perm con) =20 -let add_watch con path token =3D +let add_watch con (path, apath) token =3D if !Quota.activate && !Define.maxwatch > 0 && not (is_dom0 con) && con.nb_watches > !Define.maxwatch then raise Quota.Limit_reached; - let apath =3D get_watch_path con path in let l =3D get_watches con apath in if List.exists (fun w -> w.token =3D token) l then raise Define.Already_exist; let watch =3D watch_create ~con ~token ~path in Hashtbl.replace con.watches apath (watch :: l); con.nb_watches <- con.nb_watches + 1; - apath, watch + watch =20 let del_watch con path token =3D let apath =3D get_watch_path con path in diff --git a/tools/ocaml/xenstored/connections.ml b/tools/ocaml/xenstored/c= onnections.ml index 8a66eeec3a..3c7429fe7f 100644 --- a/tools/ocaml/xenstored/connections.ml +++ b/tools/ocaml/xenstored/connections.ml @@ -114,8 +114,10 @@ let key_of_path path =3D "" :: Store.Path.to_string_list path =20 let add_watch cons con path token =3D - let apath, watch =3D Connection.add_watch con path token in + let apath =3D Connection.get_watch_path con path in + (* fail on invalid paths early by calling key_of_str before adding watch = *) let key =3D key_of_str apath in + let watch =3D Connection.add_watch con (path, apath) token in let watches =3D if Trie.mem cons.watches key then Trie.find cons.watches key --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610749833; cv=none; d=zohomail.com; s=zohoarc; b=chpHDffMvNSP/CkXaFUKingQFV3EYYvhpWZsafifuhW4Jrvff7bdntxE28RXsD2wY+Kzz6uZ8CjmP9tPPAfbG6iwxYwBJ2bYvuPgw5yxq7LLqDpXfwGWOPSdPDAmxDLkuku3Tr4mpRC8VkKdnBafUXJ0XftZv3CgW4IP9DNX2ts= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610749833; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=Cnork4FLNvDo6GAHugl8GROrEny4NjFMlqoQq0h3vtc=; b=ZsQv3cHqZujGamRh3Wpg8CqlpSzjldY+QzghnWWL+fcRg+PDZCNIbkuxw15QMp2xeSXzhhZuItWmblF1D/hcBNYnqr8nsJAsKboS3cosxBwPvdR3kweF2ImAlhb68XMMpuPjfMCRsgwer3KPf2rY2zdf5C+cL/kUMuP3SL7q6nk= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610749833946243.28290339952264; Fri, 15 Jan 2021 14:30:33 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68672.123020 (Exim 4.92) (envelope-from ) id 1l0Xbj-0007Ef-NN; Fri, 15 Jan 2021 22:30:15 +0000 Received: by outflank-mailman (output) from mailman id 68672.123020; Fri, 15 Jan 2021 22:30:15 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xbj-0007EV-Jq; Fri, 15 Jan 2021 22:30:15 +0000 Received: by outflank-mailman (input) for mailman id 68672; Fri, 15 Jan 2021 22:30:14 +0000 Received: from all-amaz-eas1.inumbo.com ([34.197.232.57] helo=us1-amaz-eas2.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xbi-00062b-8V for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:30:14 +0000 Received: from esa3.hc3370-68.iphmx.com (unknown [216.71.145.155]) by us1-amaz-eas2.inumbo.com (Halon) with ESMTPS id 1c25cd1b-d022-4c55-b81e-1987ddc0eda6; Fri, 15 Jan 2021 22:29:51 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 1c25cd1b-d022-4c55-b81e-1987ddc0eda6 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610749791; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=tcW6aJFjdLYT7/rMoBR/lbzSOmMTuN1isJgOl/8Ingw=; b=akKWxwMIyITjsxNgoj6lcnUZVcd/siduO90sC/CSM2/QCLoXCdAhcEN0 ERwqdwpTRhMFj0gJQEMVhDjvV0eLeWKtYXO8O+FYa3QeC6+6mbMjey2L0 X38yUJbYWqYSjzFWOf1XHstjkfdBU89EWX6JvrRduSSzSZfh8QGQLDVbt A=; Authentication-Results: esa3.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: QSb8iFPrZC3nCgPcKdjmYn5SsNfAh0MxCe+2gLLmR50bxDKGKv0z2ojfYb16wVO5I7tys1TMcv l9nOggz0RQr4H77k/lBFmtA2yvZkNOBj7/iWFsfsX+p6P8DrYq3u6zzqWoAVXNg8jgUrQU3wrV +8SsQRMlgnT5gsxYtlrYjBY5id7DBotUbLqNt9Mu0SIW4NTa4CJoHHrI7aOgFqBwGBuPSmmyj2 UWGL2dimOLlBdaYa0UKkQiAva54vIff2xsl2/JzZ+v742yeK1U7ho6FNHEtg99gDSj4ZhXdWDk 85U= X-SBRS: 5.1 X-MesageID: 35216601 X-Ironport-Server: esa3.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35216601" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu , Juergen Gross , Pau Ruiz Safont Subject: [PATCH v2 4/8] tools/ocaml/xenstored: only quit on SIGTERM when a reload is possible Date: Fri, 15 Jan 2021 22:28:46 +0000 Message-ID: <023574503750d06132e3ca260848c364ff439001.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) Currently when oxenstored receives SIGTERM it dumps its state and quits. It is possible to then restart it if --restart is given, however that is not always safe: * domains could have active transactions, and after a restart they would either reuse transaction IDs of already open transactions, or get an error back that the transaction doesn't exist * there could be pending data to send to a VM still in oxenstored's queue which would be lost * there could be pending input to be processed from a VM in oxenstored's queue which would be lost Prevent shutting down oxenstored via SIGTERM in the above situations. Also ignore domains marked as bad because oxenstored would never talk to them again. Signed-off-by: Edwin T=C3=B6r=C3=B6k Reviewed-by: Pau Ruiz Safont Reviewed-by: Christian Lindig Acked-by: Christian Lindig --- Changed since V1: * post publicly now that the XSA is out --- tools/ocaml/xenstored/connection.ml | 35 ++++++++++++++++++++++++++++ tools/ocaml/xenstored/connections.ml | 8 +++++++ tools/ocaml/xenstored/xenstored.ml | 13 +++++++++-- tools/xenstore/xenstored_core.c | 7 +++++- 4 files changed, 60 insertions(+), 3 deletions(-) diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/co= nnection.ml index fa0d3c4d92..bd02060cd0 100644 --- a/tools/ocaml/xenstored/connection.ml +++ b/tools/ocaml/xenstored/connection.ml @@ -290,6 +290,41 @@ let has_new_output con =3D Xenbus.Xb.has_new_output co= n.xb let peek_output con =3D Xenbus.Xb.peek_output con.xb let do_output con =3D Xenbus.Xb.output con.xb =20 +let is_bad con =3D match con.dom with None -> false | Some dom -> Domain.i= s_bad_domain dom + +(* oxenstored currently only dumps limited information about its state. + A live update is only possible if any of the state that is not dumped w= ould be empty. + Compared to https://xenbits.xen.org/docs/unstable/designs/xenstore-migr= ation.html: + * GLOBAL_DATA: not strictly needed, systemd is giving the socket FDs = to us + * CONNECTION_DATA: PARTIAL + * for domains: PARTIAL, see Connection.dump -> Domain.dump, only if= data and tdomid is empty + * for sockets (Dom0 toolstack): NO + * WATCH_DATA: OK, see Connection.dump + * TRANSACTION_DATA: NO + * NODE_DATA: OK (except for transactions), see Store.dump_fct and DB.= to_channel + + Also xenstored will never talk to a Domain once it is marked as bad, + so treat it as idle for live-update. + + Restrictions below can be relaxed once xenstored learns to dump more + of its live state in a safe way *) +let has_extra_connection_data con =3D + let has_in =3D has_input con in + let has_out =3D has_output con in + let has_socket =3D con.dom =3D None in + let has_nondefault_perms =3D make_perm con.dom <> con.perm in + has_in || has_out + || has_socket (* dom0 sockets not dumped yet *) + || has_nondefault_perms (* set_target not dumped yet *) + +let has_transaction_data con =3D + let n =3D number_of_transactions con in + dbg "%s: number of transactions =3D %d" (get_domstr con) n; + n > 0 + +let prevents_live_update con =3D not (is_bad con) + && (has_extra_connection_data con || has_transaction_data con) + let has_more_work con =3D has_more_input con || not (has_old_output con) && has_new_output con =20 diff --git a/tools/ocaml/xenstored/connections.ml b/tools/ocaml/xenstored/c= onnections.ml index 6ee3552ec2..82988f7e8d 100644 --- a/tools/ocaml/xenstored/connections.ml +++ b/tools/ocaml/xenstored/connections.ml @@ -194,3 +194,11 @@ let debug cons =3D let anonymous =3D Hashtbl.fold (fun _ con accu -> Connection.debug con ::= accu) cons.anonymous [] in let domains =3D Hashtbl.fold (fun _ con accu -> Connection.debug con :: a= ccu) cons.domains [] in String.concat "" (domains @ anonymous) + +let filter ~f cons =3D + let fold _ v acc =3D if f v then v :: acc else acc in + [] + |> Hashtbl.fold fold cons.anonymous + |> Hashtbl.fold fold cons.domains + +let prevents_quit cons =3D filter ~f:Connection.prevents_live_update cons diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xen= stored.ml index 39d6d767e4..6b5381962b 100644 --- a/tools/ocaml/xenstored/xenstored.ml +++ b/tools/ocaml/xenstored/xenstored.ml @@ -20,6 +20,7 @@ open Parse_arg open Stdext =20 let error fmt =3D Logging.error "xenstored" fmt +let warn fmt =3D Logging.warn "xenstored" fmt let debug fmt =3D Logging.debug "xenstored" fmt let info fmt =3D Logging.info "xenstored" fmt =20 @@ -312,7 +313,9 @@ let _ =3D ); =20 Sys.set_signal Sys.sighup (Sys.Signal_handle sighup_handler); - Sys.set_signal Sys.sigterm (Sys.Signal_handle (fun _ -> quit :=3D true)); + Sys.set_signal Sys.sigterm (Sys.Signal_handle (fun _ -> + info "Received SIGTERM"; + quit :=3D true)); Sys.set_signal Sys.sigusr1 (Sys.Signal_handle (fun _ -> sigusr1_handler s= tore)); Sys.set_signal Sys.sigpipe Sys.Signal_ignore; =20 @@ -424,6 +427,12 @@ let _ =3D ); let elapsed =3D Unix.gettimeofday () -. now in debug "periodic_ops took %F seconds." elapsed; + if !quit then + (match Connections.prevents_quit cons with + | [] -> () + | domains -> + List.iter (fun con -> warn "%s prevents live update" (Connection.get= _domstr con)) domains + ); delay_next_frequent_ops_by elapsed in =20 @@ -475,7 +484,7 @@ let _ =3D in =20 Systemd.sd_notify_ready (); - while not !quit + while not (!quit && Connections.prevents_quit cons =3D []) do try main_loop () diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_cor= e.c index 50986f8b29..b9495365c4 100644 --- a/tools/xenstore/xenstored_core.c +++ b/tools/xenstore/xenstored_core.c @@ -1970,6 +1970,7 @@ static struct option options[] =3D { { "internal-db", 0, NULL, 'I' }, { "verbose", 0, NULL, 'V' }, { "watch-nb", 1, NULL, 'W' }, + { "live-update", 0, NULL, 'U' }, { NULL, 0, NULL, 0 } }; =20 extern void dump_conn(struct connection *conn);=20 @@ -1984,11 +1985,12 @@ int main(int argc, char *argv[]) bool dofork =3D true; bool outputpid =3D false; bool no_domain_init =3D false; + bool live_update =3D false; const char *pidfile =3D NULL; int timeout; =20 =20 - while ((opt =3D getopt_long(argc, argv, "DE:F:HNPS:t:A:M:T:RVW:", options, + while ((opt =3D getopt_long(argc, argv, "DE:F:HNPS:t:A:M:T:RVW:U", option= s, NULL)) !=3D -1) { switch (opt) { case 'D': @@ -2046,6 +2048,9 @@ int main(int argc, char *argv[]) case 'p': priv_domid =3D strtol(optarg, NULL, 10); break; + case 'U': + live_update =3D true; + break; } } if (optind !=3D argc) --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751203; cv=none; d=zohomail.com; s=zohoarc; b=RKa2acuDZ2RZWE25v9XmXID68euhTGhn6cUpnS0BUBGdB2CZIjTRzNZEYXEgMzAa+Jfiul4e/vzYl8NNoKhLmJvWg+jAnL5NQ4ZMyWzCtDgaufQPRYKNmiXu/Ri/LkPq7moFisDKVX9WK/QTQn7esoi3UMLCJXpAMnpZb8V9Cr4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751203; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=t9BYr/GOUJ4zhxBVjQ9kj50YpG+EWP4gkDeD7W5LLp4=; b=nXMmyYRuoiYHbJXOuypH2MrN5xqodVOXMZNleoP8IR24RLV1AAV/c8cv5Egy7UrfSVH3EcALtVBghQLCFMx2AWMFFi4ZWJUdaFm1DchEVsE3ZG/N5X3jHDTw1bNTcsMqBNMvnuhnJcPeUYKDEkc4Fv7em7mtsV4VK5zYRdXdiqg= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610751203306939.242674691854; Fri, 15 Jan 2021 14:53:23 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68741.123200 (Exim 4.92) (envelope-from ) id 1l0Xxo-0002J1-Nz; Fri, 15 Jan 2021 22:53:04 +0000 Received: by outflank-mailman (output) from mailman id 68741.123200; Fri, 15 Jan 2021 22:53:04 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xxo-0002Io-JN; Fri, 15 Jan 2021 22:53:04 +0000 Received: by outflank-mailman (input) for mailman id 68741; Fri, 15 Jan 2021 22:53:03 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xxm-0001Wj-W2 for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:53:03 +0000 Received: from esa2.hc3370-68.iphmx.com (unknown [216.71.145.153]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id 880bf2fc-3093-4f9a-a717-2ccf85eaf5af; Fri, 15 Jan 2021 22:52:34 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 880bf2fc-3093-4f9a-a717-2ccf85eaf5af DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751154; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=f2+2EkVgRSKwnJg04mCaQfo6Y0AeER0uEpuI0RzaCok=; b=iL948vxEipj/eqMhk7+3/rU+EnlL26wqVZVlWPMI6+iDvA7fpblkCYSh bRDFJlGHvZ8E13vwTGLFO5vvOMJq7FzR4YTOXki9tg7pAEyDr7EDAiGAy KfTpvwN1TzqHu6tuHvLbYy/m69B5BiAtt9gOtRi1pBuWr9dLs25lYhAwC c=; Authentication-Results: esa2.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: v+h0zdLX7VPiugjB/jYhRxC8UwuubOWqPqzGEsksAgLzCC/Rn75Wan3af3zyCWny0Fdu7dCw4f 4vlEDqsqF4qdUCSqtBwrh0GfvWwNo6QO66WhL2q9klC4o0DjYF1OJfaETC2r89EgSrMxQobTnq 9IlY3KJm534NjMuAGG6D9ZnUe9W/Q92gsckucHjNWNJRKCrz0oHKC38FseKa9BY1H4uVRLfPQo 7AZ5EJ8bjx1uY3UEJS785XbVAbgYvpnBhQcUT3cbeAVLrFp3sEESG+1Lkb/gE0ycNOEceeLsSr GyU= X-SBRS: 5.1 X-MesageID: 35263548 X-Ironport-Server: esa2.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35263548" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu Subject: [PATCH v1 4/4] tools/ocaml/xenstored: mkdir conflicts were sometimes missed Date: Fri, 15 Jan 2021 22:29:09 +0000 Message-ID: X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) Due to how set_write_lowpath was used here it didn't detect create/delete conflicts. When we create an entry we must mark our parent as modified (this is what creating a new node via write does). Otherwise we can have 2 transactions one creating, and another deleting a node both succeeding depending on timing. Or one transaction reading an entry, concluding it doesn't exist, do some other work based on that information and successfully commit even if another transaction creates the node via mkdir meanwhile. Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Christian Lindig --- tools/ocaml/xenstored/transaction.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ocaml/xenstored/transaction.ml b/tools/ocaml/xenstored/t= ransaction.ml index 4ee77b6e14..0466b04ae3 100644 --- a/tools/ocaml/xenstored/transaction.ml +++ b/tools/ocaml/xenstored/transaction.ml @@ -172,7 +172,7 @@ let write t perm path value =3D =20 let mkdir ?(with_watch=3Dtrue) t perm path =3D Store.mkdir t.store perm path; - set_write_lowpath t path; + set_write_lowpath t (Store.Path.get_parent path); if with_watch then add_wop t Xenbus.Xb.Op.Mkdir path =20 --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751562; cv=none; d=zohomail.com; s=zohoarc; b=YtrbpGqnG2/sBuh10oR0TWWd43VIKMNrRl1zXtcLeyCrCZHhDRFyTsKs2xN8tRrTD2prk6QDXOqo9eLY2rCFbKMci9xQw8PfDb3HshAu4YjyIlUEcryAE/WmT5Cqi8ASlAkSWUjhxHa0YtAf0PgUb+Y8dVb6FWrAUhj6Ld1O7fA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751562; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=Va2gppK8iDNZLhTkypcxxu8EbgkfPK2+EVGBGhHeV3s=; b=WQhqsqScujsJ9bwSZz6q1rtwC0EcJdm6dZ741PmqNasfPwDAokMdT1YXMlGnP9VOy0jQ0FA6jVwa0hnjLG7oUrxqT7BOpAouWvCzFiPZHL/nT3bWTD/AvkKVepu6RuCdIiHxtGaVtpeJJ0YqI3sq5jYpW3Mu990kQXMlibVHOoI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610751562808906.8893694678876; Fri, 15 Jan 2021 14:59:22 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68790.123300 (Exim 4.92) (envelope-from ) id 1l0Y3c-0003Ue-Sg; Fri, 15 Jan 2021 22:59:04 +0000 Received: by outflank-mailman (output) from mailman id 68790.123300; Fri, 15 Jan 2021 22:59:04 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Y3c-0003U4-6s; Fri, 15 Jan 2021 22:59:04 +0000 Received: by outflank-mailman (input) for mailman id 68790; Fri, 15 Jan 2021 22:59:02 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XyH-0001Wj-15 for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:53:33 +0000 Received: from esa3.hc3370-68.iphmx.com (unknown [216.71.145.155]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id 26887bda-f300-44c6-b71d-4f5718b9a07b; Fri, 15 Jan 2021 22:52:37 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 26887bda-f300-44c6-b71d-4f5718b9a07b DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751157; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=Mh4+eKEr9LhDfKwKuYHJiWolDcWugUyUrf0m4DtFF4M=; b=dUdVDx1Jvg1MGm8O96LNDjYjkOeAhPtlqisB+ygg91emNkoLRhFtLEg0 JB/a8/Lx6BB9XI5DYgkY2/zm1bT/7o7UQJbRPdTm20gdymwck4iThNWP1 kfCYXsroyeMIfMdgtHKeeeyfD23kljq8dv7f5CbI3PSr5q+LbE3OmPUuv I=; Authentication-Results: esa3.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: OQrlBuPf51cLcdff5hNyHoPZHagzmyyWFcZmbAOfqoa1NLXnXB76PV9XrB271TBirbhWHLsiGx E9W9Mx4CLtVXJpUxeoyUyQy0aA0NwTNjSdrrShM7AHsYpYJaypv1tXyfprLi6zyAnZ5ueT345Y mNpdPBpeRKmI8ivPcE4366fj1uYr9jcQNSSBJtJRM9OV1z3HNE/c77vjr2oEchC+ZpOEZzSA9I cdZOTyyEh6vsCb69XaQsot/RfhgdST1L6JMboQ6Z8DsU1tMuFZJeAvPu5fm+kKk+BH0tYI8XKJ IIk= X-SBRS: 5.1 X-MesageID: 35217499 X-Ironport-Server: esa3.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35217499" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu Subject: [PATCH v4 4/4] tools/ocaml/xenstored: use more efficient tries Date: Fri, 15 Jan 2021 22:28:58 +0000 Message-ID: X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) No functional change, just an optimization. Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Christian Lindig --- Changed since V3: * repost after XSA to avoid conflicts --- tools/ocaml/xenstored/connections.ml | 2 +- tools/ocaml/xenstored/symbol.ml | 6 +-- tools/ocaml/xenstored/trie.ml | 59 ++++++++++++---------------- tools/ocaml/xenstored/trie.mli | 26 ++++++------ 4 files changed, 43 insertions(+), 50 deletions(-) diff --git a/tools/ocaml/xenstored/connections.ml b/tools/ocaml/xenstored/c= onnections.ml index 82988f7e8d..8a66eeec3a 100644 --- a/tools/ocaml/xenstored/connections.ml +++ b/tools/ocaml/xenstored/connections.ml @@ -21,7 +21,7 @@ type t =3D { anonymous: (Unix.file_descr, Connection.t) Hashtbl.t; domains: (int, Connection.t) Hashtbl.t; ports: (Xeneventchn.t, Connection.t) Hashtbl.t; - mutable watches: (string, Connection.watch list) Trie.t; + mutable watches: Connection.watch list Trie.t; } =20 let create () =3D { diff --git a/tools/ocaml/xenstored/symbol.ml b/tools/ocaml/xenstored/symbol= .ml index 301639f16f..72a84ebf80 100644 --- a/tools/ocaml/xenstored/symbol.ml +++ b/tools/ocaml/xenstored/symbol.ml @@ -31,9 +31,9 @@ let equal a b =3D (* compare using physical equality, both members have to be part of the = above weak table *) a =3D=3D b =20 -let compare a b =3D - if equal a b then 0 - else -(String.compare a b) +(* the sort order is reversed here, so that Map.fold constructs a list + in ascending order *) +let compare a b =3D String.compare b a =20 let stats () =3D let len, entries, _, _, _, _ =3D WeakTable.stats tbl in diff --git a/tools/ocaml/xenstored/trie.ml b/tools/ocaml/xenstored/trie.ml index f513f4e608..ad2aed5123 100644 --- a/tools/ocaml/xenstored/trie.ml +++ b/tools/ocaml/xenstored/trie.ml @@ -15,24 +15,26 @@ =20 open Stdext =20 +module StringMap =3D Map.Make(String) + module Node =3D struct - type ('a,'b) t =3D { - key: 'a; - value: 'b option; - children: ('a,'b) t list; + type 'a t =3D { + key: string; + value: 'a option; + children: 'a t StringMap.t; } =20 let _create key value =3D { key =3D key; value =3D Some value; - children =3D []; + children =3D StringMap.empty; } =20 let empty key =3D { key =3D key; value =3D None; - children =3D [] + children =3D StringMap.empty; } =20 let _get_key node =3D node.key @@ -49,41 +51,31 @@ struct { node with children =3D children } =20 let _add_child node child =3D - { node with children =3D child :: node.children } + { node with children =3D StringMap.add child.key child node.children } end =20 -type ('a,'b) t =3D ('a,'b) Node.t list +type 'a t =3D 'a Node.t StringMap.t =20 let mem_node nodes key =3D - List.exists (fun n -> n.Node.key =3D key) nodes + StringMap.mem key nodes =20 let find_node nodes key =3D - List.find (fun n -> n.Node.key =3D key) nodes + StringMap.find key nodes =20 let replace_node nodes key node =3D - let rec aux =3D function - | [] -> [] - | h :: tl when h.Node.key =3D key -> node :: tl - | h :: tl -> h :: aux tl - in - aux nodes + StringMap.update key (function None -> None | Some _ -> Some node) nodes =20 let remove_node nodes key =3D - let rec aux =3D function - | [] -> raise Not_found - | h :: tl when h.Node.key =3D key -> tl - | h :: tl -> h :: aux tl - in - aux nodes + StringMap.update key (function None -> raise Not_found | Some _ -> None) = nodes =20 -let create () =3D [] +let create () =3D StringMap.empty =20 let rec iter f tree =3D - let aux node =3D - f node.Node.key node.Node.value; + let aux key node =3D + f key node.Node.value; iter f node.Node.children in - List.iter aux tree + StringMap.iter aux tree =20 let rec map f tree =3D let aux node =3D @@ -94,13 +86,14 @@ let rec map f tree =3D in { node with Node.value =3D value; Node.children =3D map f node.Node.chil= dren } in - List.filter (fun n -> n.Node.value <> None || n.Node.children <> []) (Lis= t.map aux tree) + tree |> StringMap.map aux + |> StringMap.filter (fun _ n -> n.Node.value <> None || not (StringMap.is= _empty n.Node.children) ) =20 let rec fold f tree acc =3D - let aux accu node =3D - fold f node.Node.children (f node.Node.key node.Node.value accu) + let aux key node accu =3D + fold f node.Node.children (f key node.Node.value accu) in - List.fold_left aux acc tree + StringMap.fold aux tree acc =20 (* return a sub-trie *) let rec sub_node tree =3D function @@ -117,7 +110,7 @@ let rec sub_node tree =3D function =20 let sub tree path =3D try (sub_node tree path).Node.children - with Not_found -> [] + with Not_found -> StringMap.empty =20 let find tree path =3D Node.get_value (sub_node tree path) @@ -161,7 +154,7 @@ and set tree path value =3D replace_node tree h (set_node node t value) end else begin let node =3D Node.empty h in - set_node node t value :: tree + StringMap.add node.Node.key (set_node node t value) tree end =20 let rec unset tree =3D function @@ -176,7 +169,7 @@ let rec unset tree =3D function then Node.set_children (Node.empty h) children else Node.set_children node children in - if children =3D [] && new_node.Node.value =3D None + if StringMap.is_empty children && new_node.Node.value =3D None then remove_node tree h else replace_node tree h new_node end else diff --git a/tools/ocaml/xenstored/trie.mli b/tools/ocaml/xenstored/trie.mli index 5dc53c1cb1..27785154f5 100644 --- a/tools/ocaml/xenstored/trie.mli +++ b/tools/ocaml/xenstored/trie.mli @@ -15,46 +15,46 @@ =20 (** Basic Implementation of polymorphic tries (ie. prefix trees) *) =20 -type ('a, 'b) t -(** The type of tries. ['a list] is the type of keys, ['b] the type of val= ues. +type 'a t +(** The type of tries. ['a] the type of values. Internally, a trie is represented as a labeled tree, where node contains = values - of type ['a * 'b option]. *) + of type [string * 'a option]. *) =20 -val create : unit -> ('a,'b) t +val create : unit -> 'a t (** Creates an empty trie. *) =20 -val mem : ('a,'b) t -> 'a list -> bool +val mem : 'a t -> string list -> bool (** [mem t k] returns true if a value is associated with the key [k] in th= e trie [t]. Otherwise, it returns false. *) =20 -val find : ('a, 'b) t -> 'a list -> 'b +val find : 'a t -> string list -> 'a (** [find t k] returns the value associated with the key [k] in the trie [= t]. Returns [Not_found] if no values are associated with [k] in [t]. *) =20 -val set : ('a, 'b) t -> 'a list -> 'b -> ('a, 'b) t +val set : 'a t -> string list -> 'a -> 'a t (** [set t k v] associates the value [v] with the key [k] in the trie [t].= *) =20 -val unset : ('a, 'b) t -> 'a list -> ('a, 'b) t +val unset : 'a t -> string list -> 'a t (** [unset k v] removes the association of value [v] with the key [k] in t= he trie [t]. Moreover, it automatically clean the trie, ie. it removes recursively every nodes of [t] containing no values and having no chil. *) =20 -val iter : ('a -> 'b option -> unit) -> ('a, 'b) t -> unit +val iter : (string -> 'a option -> unit) -> 'a t -> unit (** [iter f t] applies the function [f] to every node of the trie [t]. As nodes of the trie [t] do not necessary contains a value, the second ar= gument of [f] is an option type. *) =20 -val iter_path : ('a -> 'b option -> unit) -> ('a, 'b) t -> 'a list -> unit +val iter_path : (string -> 'a option -> unit) -> 'a t -> string list -> un= it (** [iter_path f t p] iterates [f] over nodes associated with the path [p]= in the trie [t]. If [p] is not a valid path of [t], it iterates on the longest valid prefi= x of [p]. *) =20 -val fold : ('a -> 'b option -> 'c -> 'c) -> ('a, 'b) t -> 'c -> 'c +val fold : (string -> 'a option -> 'c -> 'c) -> 'a t -> 'c -> 'c (** [fold f t x] fold [f] over every nodes of [t], with [x] as initial val= ue. *) =20 -val map : ('b -> 'c option) -> ('a,'b) t -> ('a,'c) t +val map : ('a -> 'b option) -> 'a t -> 'b t (** [map f t] maps [f] over every values stored in [t]. The return value o= f [f] is of type 'c option as one may wants to remove value associated to a key. This function is no= t tail-recursive. *) =20 -val sub : ('a, 'b) t -> 'a list -> ('a,'b) t +val sub : 'a t -> string list -> 'a t (** [sub t p] returns the sub-trie associated with the path [p] in the tri= e [t]. If [p] is not a valid path of [t], it returns an empty trie. *) --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751557; cv=none; d=zohomail.com; s=zohoarc; b=BrS82FS/5Vhr5z4/GU/ga3sWuCSQX+gx0FDaIWMam3zDP3HadsoxH35R4JAyKMq7m8usUrsQQCJlaBXktJEn+OgA4fGvrp1wl2HmebIVtXTAqjEdT+HPS+iLfQXHOjDH7jnFklvWKYUYyoZEnK3ERa+ueG5OArqbBEB+wZIxOCI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751557; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=s53TuUp49O6/92b75lrIaWnc+nbKN2IDudZ14Nto+J0=; b=S+P8fv9F153WWIq928MJdh/0uA4MhZ1SYvUcpNcmtckkop/Xgpk1y73NVRPENO0nk0KhClhErjykWzDXB54ZT8i0vcNEit9uWjBPVn5vD7ghr9a91Va2vVkHEaCKkRyw7LE6XD5iMzPyl9goFPr+fcDRkF2ZNklWaIemEGOLogs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610751557815148.05927397379662; Fri, 15 Jan 2021 14:59:17 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68781.123272 (Exim 4.92) (envelope-from ) id 1l0Y3W-0003GP-IS; Fri, 15 Jan 2021 22:58:58 +0000 Received: by outflank-mailman (output) from mailman id 68781.123272; Fri, 15 Jan 2021 22:58:58 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Y3W-0003GF-Ee; Fri, 15 Jan 2021 22:58:58 +0000 Received: by outflank-mailman (input) for mailman id 68781; Fri, 15 Jan 2021 22:58:56 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xyl-0001Wj-2P for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:54:03 +0000 Received: from esa5.hc3370-68.iphmx.com (unknown [216.71.155.168]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id b9bc6017-c10f-4e6e-875b-1fa39ef27fcc; Fri, 15 Jan 2021 22:52:47 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: b9bc6017-c10f-4e6e-875b-1fa39ef27fcc DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751167; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=GR5nos0MMBWS+VeXIkwq9oyfeOPL+We9fK2nz1YJlUY=; b=E1pwfgQneTWJ+dYwf4isRtck0pSJjSnmqB6r4BSQFe9FnrSn3u7gZfMY P19NqWzYYbL3iUVgAPmSQ8j5+Fx++ZXjiu8kF5HmI2qelIcFkeWnfwuCe JiEWVfiCUbbLumUmALO8OSmLD5ibXNjTe26NdVekGYYn8JftoQ1bH3J1x s=; Authentication-Results: esa5.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: 1GHynBy4xQn5N9BIsfVxB8CceneT7m1uaMP8C4WcFPZx1yzEsRQ93kSHlmxCs9KXoTlhboMjFo cn4yFcUDpuNuqMK793/76itPtSa0DEPrhP14zyOYMNPakpieXvYyHA/bgcCm8VhtCg8aZoxOGR G6mlFjRO7E+h9e4+7MtaV67K8S2Jf7JpDaPgwMNwg/Zfgv9pxy3i8DdH37ZRTF/mLhqC1QHWxb tQkb3qtByc5/NbtmmWve4afMq2TmEJ3S1yhn1aU37AeZbGb8SLF5zzbim8UlmgrXiLsl7sJtou e0A= X-SBRS: 5.1 X-MesageID: 35206340 X-Ironport-Server: esa5.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35206340" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu Subject: [PATCH v1 4/5] tools/ocaml/xenstored: add support for binary format Date: Fri, 15 Jan 2021 22:29:03 +0000 Message-ID: <52d3a36208ed7a31355b289acb323255ed365ef8.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Christian Lindig --- tools/ocaml/xenstored/perms.ml | 2 + tools/ocaml/xenstored/xenstored.ml | 201 ++++++++++++++++++++++++----- 2 files changed, 173 insertions(+), 30 deletions(-) diff --git a/tools/ocaml/xenstored/perms.ml b/tools/ocaml/xenstored/perms.ml index e8a16221f8..61c1c60083 100644 --- a/tools/ocaml/xenstored/perms.ml +++ b/tools/ocaml/xenstored/perms.ml @@ -69,6 +69,8 @@ let remove_domid ~domid perm =3D =20 let default0 =3D create 0 NONE [] =20 +let acls t =3D (t.owner, t.other) :: t.acl + let perm_of_string s =3D let ty =3D permty_of_char s.[0] and id =3D int_of_string (String.sub s 1 (String.length s - 1)) in diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xen= stored.ml index e25b407303..9338190804 100644 --- a/tools/ocaml/xenstored/xenstored.ml +++ b/tools/ocaml/xenstored/xenstored.ml @@ -141,7 +141,8 @@ exception Bad_format of string =20 let dump_format_header =3D "$xenstored-dump-format" =20 -let from_channel_f chan global_f socket_f domain_f watch_f store_f =3D +(* for backwards compatibility with already released live-update *) +let from_channel_f_compat chan global_f socket_f domain_f watch_f store_f = =3D let unhexify s =3D Utils.unhexify s in let getpath s =3D let u =3D Utils.unhexify s in @@ -186,7 +187,7 @@ let from_channel_f chan global_f socket_f domain_f watc= h_f store_f =3D done; info "Completed loading xenstore dump" =20 -let from_channel store cons doms chan =3D +let from_channel_compat ~live store cons doms chan =3D (* don't let the permission get on our way, full perm ! *) let op =3D Store.get_ops store Perms.Connection.full_rights in let rwro =3D ref (None) in @@ -226,43 +227,183 @@ let from_channel store cons doms chan =3D op.Store.write path value; op.Store.setperms path perms in - from_channel_f chan global_f socket_f domain_f watch_f store_f; + from_channel_f_compat chan global_f socket_f domain_f watch_f store_f; !rwro =20 -let from_file store cons doms file =3D - info "Loading xenstore dump from %s" file; - let channel =3D open_in file in - finally (fun () -> from_channel store doms cons channel) +module LR =3D Disk.LiveRecord + +let from_channel_f_bin chan on_global_data on_connection_data on_watch_dat= a on_transaction_data on_node_data =3D + Disk.BinaryIn.read_header chan; + let quit =3D ref false in + let on_end () =3D quit :=3D true in + let errors =3D ref 0 in + while not !quit + do + try + LR.read_record chan ~on_end ~on_global_data ~on_connection_data ~on_wat= ch_data ~on_transaction_data ~on_node_data + with exn -> + let bt =3D Printexc.get_backtrace () in + incr errors; + Logging.warn "xenstored" "restoring: ignoring faulty record (exception:= %s): %s" (Printexc.to_string exn) bt + done; + info "Completed loading xenstore dump"; + !errors + + +let from_channel_bin ~live store cons doms chan =3D + (* don't let the permission get on our way, full perm ! *) + let maintx =3D Transaction.make ~internal:true Transaction.none store in + let fullperm =3D Perms.Connection.full_rights in + let fds =3D ref None in + let allcons =3D Hashtbl.create 1021 in + let contxid_to_op =3D Hashtbl.create 1021 in + let global_f ~rw_sock =3D + (* file descriptors are only valid on a live-reload, a cold restart won'= t have them *) + if live then + fds :=3D Some rw_sock + in + let domain_f ~conid ~conn ~in_data ~out_data ~out_resp_len =3D + let con =3D match conn with + | LR.Domain { LR.id =3D 0; _ } -> + (* Dom0 is precreated *) + Connections.find_domain cons 0 + | LR.Domain d -> + debug "Recreating domain %d, port %d" d.id d.remote_port;=20 + (* FIXME: gnttab *) + Domains.create doms d.id 0n d.remote_port + |> Connections.add_domain cons; + Connections.find_domain cons d.id + | LR.Socket fd -> + debug "Recreating open socket"; + (* TODO: rw/ro flag *) + Connections.add_anonymous cons fd; + Connections.find cons fd + in + Hashtbl.add allcons conid con + in + let watch_f ~conid ~wpath ~token =3D + let con =3D Hashtbl.find allcons conid in + ignore (Connections.add_watch cons con wpath token); + () + in + let transaction_f ~conid ~txid =3D + let con =3D Hashtbl.find allcons conid in + con.Connection.next_tid <- txid; + let id =3D Connection.start_transaction con store in + assert (id =3D txid); + let txn =3D Connection.get_transaction con txid in + Hashtbl.add contxid_to_op (conid, txid) txn + in + let store_f ~txaccess ~perms ~path ~value =3D + let txn, op =3D match txaccess with + | None -> maintx, LR.W + | Some (conid, txid, op) -> + let (txn, _) as r =3D Hashtbl.find contxid_to_op (conid, txid), op in + (* make sure this doesn't commit, even as RO *) + Transaction.mark_failed txn; + r + in + let get_con id =3D + if id < 0 then Connections.find cons (Utils.FD.of_int (-id)) + else Connections.find_domain cons id + in + let watch_f id path token =3D + ignore (Connections.add_watch cons (get_con id) path token) + in + let path =3D Store.Path.of_string path in + try match op with + | LR.R -> + Logging.debug "xenstored" "TR %s %S" (Store.Path.to_string path) value; + (* these are values read by the tx, potentially + no write access here. Make the tree match. *) + Transaction.write txn fullperm path value;=20 + Transaction.setperms txn fullperm path perms; + | LR.W | LR.RW -> + Logging.debug "xenstored" "TW %d %s %S" (Transaction.get_id txn) (Stor= e.Path.to_string path) value; + (* We started with empty tree, create parents. + All the implicit mkdirs from the original tx should be explicit alr= eady for quota purposes. + *) + Process.create_implicit_path txn fullperm path; + Transaction.write txn fullperm path value;=20 + Transaction.setperms txn fullperm path perms; + Logging.debug "xenstored" "TWdone %s %S" (Store.Path.to_string path) v= alue; + | LR.Del -> + Logging.debug "xenstored" "TDel %s " (Store.Path.to_string path); + Transaction.rm txn fullperm path + with Not_found|Define.Doesnt_exist|Define.Lookup_Doesnt_exist _ -> () + in + (* make sure we got a quota entry for Dom0, so that setperms on / doesn't= cause quota to be off-by-one *) + Transaction.mkdir maintx fullperm (Store.Path.of_string "/local"); + let errors =3D from_channel_f_bin chan global_f domain_f watch_f transact= ion_f store_f in + (* do not fire any watches, but this makes a tx RO *) +(* Transaction.clear_wops maintx; *) + let errors =3D if not @@ Transaction.commit ~con:"live-update" maintx the= n begin + Logging.warn "xenstored" "live-update: failed to commit main transaction= "; + errors + 1 + end else errors + in + !fds, errors + +let from_channel =3D from_channel_bin (* TODO: detect and accept text form= at *) + +let from_file ~live store cons doms file =3D + let channel =3D open_in_bin file in + finally (fun () -> from_channel_bin ~live store doms cons channel) (fun () -> close_in channel) =20 -let to_channel store cons rw chan =3D - let hexify s =3D Utils.hexify s in +let to_channel rw_sock store cons chan =3D + let t =3D Disk.BinaryOut.write_header chan in =20 - fprintf chan "%s\n" dump_format_header; - let fdopt =3D function None -> -1 | Some fd -> - (* systemd and utils.ml sets it close on exec *) - Unix.clear_close_on_exec fd; - Utils.FD.to_int fd in - fprintf chan "global,%d\n" (fdopt rw); - - (* dump connections related to domains: domid, mfn, eventchn port/ socket= s, and watches *) - Connections.iter cons (fun con -> Connection.dump con chan); + (match rw_sock with + | Some rw_sock -> + LR.write_global_data t ~rw_sock + | _ -> ()); =20 (* dump the store *) Store.dump_fct store (fun path node -> - let name, perms, value =3D Store.Node.unpack node in - let fullpath =3D Store.Path.to_string (Store.Path.of_path_and_name path = name) in - let permstr =3D Perms.Node.to_string perms in - fprintf chan "store,%s,%s,%s\n" (hexify fullpath) (hexify permstr) (hexi= fy value) + Transaction.write_node t None path node ); + + (* dump connections related to domains and sockets; domid, mfn, eventchn = port, watches *) + Connections.iter cons (fun con -> Connection.dump con store t); + + LR.write_end t; flush chan; () =20 +let validate_f ch =3D + let conids =3D Hashtbl.create 1021 in + let txids =3D Hashtbl.create 1021 in + let global_f ~rw_sock =3D () in + let domain_f ~conid ~conn ~in_data ~out_data ~out_resp_len =3D + Hashtbl.add conids conid () + in + let watch_f ~conid ~wpath ~token =3D + Hashtbl.find conids conid + in + let transaction_f ~conid ~txid =3D + Hashtbl.find conids conid; + Hashtbl.add txids (conid, txid) () + in=20 + let store_f ~txaccess ~perms ~path ~value =3D + match txaccess with + | None -> () + | Some (conid, txid, _) -> + Hashtbl.find conids conid; + Hashtbl.find txids (conid, txid) + in + let errors =3D from_channel_f_bin ch global_f domain_f watch_f transactio= n_f store_f in + if errors > 0 then + failwith (Printf.sprintf "Failed to re-read dump: %d errors" errors) =20 -let to_file store cons fds file =3D - let channel =3D open_out_gen [ Open_wronly; Open_creat; Open_trunc; ] 0o6= 00 file in - finally (fun () -> to_channel store cons fds channel) - (fun () -> close_out channel) +let to_file fds store cons file =3D + let channel =3D open_out_gen [ Open_wronly; Open_creat; Open_trunc; Open_= binary ] 0o600 file in + finally (fun () -> to_channel fds store cons channel) + (fun () -> close_out channel); + let channel =3D open_in_bin file in + finally (fun () -> validate_f channel) + (fun () -> close_in channel) +=09 end =20 let main () =3D @@ -329,8 +470,8 @@ let main () =3D =20 let rw_sock =3D if cf.restart && Sys.file_exists Disk.xs_daemon_database then ( - let rwro =3D DB.from_file store domains cons Disk.xs_daemon_database in - info "Live reload: database loaded"; + let rwro, errors =3D DB.from_file ~live:cf.live_reload store domains con= s Disk.xs_daemon_database in + info "Live reload: database loaded (%d errors)" errors; Event.bind_dom_exc_virq eventchn; Process.LiveUpdate.completed (); rwro @@ -367,7 +508,7 @@ let main () =3D Sys.set_signal Sys.sigpipe Sys.Signal_ignore; =20 if cf.activate_access_log then begin - let post_rotate () =3D DB.to_file store cons (None) Disk.xs_daemon_datab= ase in + let post_rotate () =3D DB.to_file None store cons Disk.xs_daemon_databas= e in Logging.init_access_log post_rotate end; =20 @@ -528,7 +669,7 @@ let main () =3D live_update :=3D Process.LiveUpdate.should_run cons; if !live_update || !quit then begin (* don't initiate live update if saving state fails *) - DB.to_file store cons (rw_sock) Disk.xs_daemon_database; + DB.to_file rw_sock store cons Disk.xs_daemon_database; quit :=3D true; end with exc -> --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751212; cv=none; d=zohomail.com; s=zohoarc; b=km5LNAWAflcwI0Fqke6nWnXE+NGJ4KOT7mUoH9Yh8d3tPgkUmxFbySk9lIo3QbQO5BF5QpLCKHxurRoL2fVX2Bgo1yhg2EIRZ5qaauM6sFl3qB6hWjaPQEGoyb94YGPVgWM/L5pHkwziaCd/Bo/30vRog06BikfcclqrKQ7bMYQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751212; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=gCTMmoq43b7jHohZqVXQKUrxQ7zfOelDuMDks4jMiOg=; b=TBEC61PqHCk6jK01a6gwIvxcvBQe1Dw0Nr0yUD4SQAAP7BdAgUmq4iEBh0y/GDiuyhpOVJzk9/JjvWW4pMhQM/M8exfqvYoRKWxkUJYbTlWnrUtPVDRQllm9OswbXUmq1TXZYruwsdv9ytdIIpo2uZMLUrzFXmcwbelYLVwYEPQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610751212580566.9387181119552; Fri, 15 Jan 2021 14:53:32 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68745.123212 (Exim 4.92) (envelope-from ) id 1l0Xxu-0002QD-50; Fri, 15 Jan 2021 22:53:10 +0000 Received: by outflank-mailman (output) from mailman id 68745.123212; Fri, 15 Jan 2021 22:53:10 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xxt-0002Px-VK; Fri, 15 Jan 2021 22:53:09 +0000 Received: by outflank-mailman (input) for mailman id 68745; Fri, 15 Jan 2021 22:53:08 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xxs-0001Wj-04 for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:53:08 +0000 Received: from esa5.hc3370-68.iphmx.com (unknown [216.71.155.168]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id 286c0f93-e74e-417a-ad0a-335336b3ff4a; Fri, 15 Jan 2021 22:52:33 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 286c0f93-e74e-417a-ad0a-335336b3ff4a DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751153; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=JZpONuCdnJu3tE3gPyrhyDAhjfabiLrGIX1BDd9oppA=; b=UB6MHJb1JixY/+97HyrVhqaNC8DOrrg9UpdDBkNQaQJFrxh4H3VvLGbW SBeIKK3HanoCM6+yWYEv7joWp2g8X+8k3fUONt/WDfw0dHqH2SNo9u1xA DdXnr2Em5m8Ooxpx4ncSQhD1HX60xuSAiw1cfiawHgbyaaUf9ENsID3t6 M=; Authentication-Results: esa5.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: xz3k9UiXTDhg/j73TVol8MDg7qTrvDHAt10tu1y9x/htMRaJgA+qryrSt8eznL+YbxLzAdsDeN 6YeLdPwRKyFkTtCtU6A9KZaal9cPSA8d6qiZhjoE879/iCm/HrXmesLeHB+/xlY3NYcg5f3FWt bM2dZSV4dvC23b/IlPqkbvG4fXtWaogJLpHpN5xuc6C3132mmWywLWYvgqlsaUnv9EIU4fROqn 8tXLTB3RTwR2DZ2wicd2csbbgVIpZVGdfYXILXTrtjU+xMTD/N68AxgXBLaP7G+fNAKR6BXgFQ 4lY= X-SBRS: 5.1 X-MesageID: 35206328 X-Ironport-Server: esa5.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35206328" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu Subject: [PATCH v1 5/5] Add structured fuzzing unit test Date: Fri, 15 Jan 2021 22:29:04 +0000 Message-ID: X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) Based on ideas from qcstm, implemented using Crowbar. Quickcheck-style property tests that uses AFL for quickly exploring various values that trigger bugs in the code. This is structured/guided fuzzing: we read an arbitrary random number, and use it to generate some valid looking xenstore trees and commands. There are 2 instances of xenstored: one that runs the live update command, and one that ignores it. Live-update should be a no-op wrt to xenstored state: this is our quicheck property. When any mismatch is identified it prints the input (tree+xenstore commands), and a diff of the output: the internal xenstore tree state + quotas. afl-cmin can be used to further minimize the testcase. Crowbar (AFL persistent mode Quickcheck integration) is used due to speed: this very easily gets us a multi-core parallelizable test. Currently the Transaction tests fail, which is why live updates with active transactions are rejected. TODO: split out the non-working transaction code, and drop some obsolete code. There is also some incomplete code here that attempts to find functional bugs in xenstored by interpeting xenstore commands in a simpler way and comparing states. This will build the fuzzer and run it single core for sanity test: make container-fuzz-sanity-test This will run it multicore (requires all dependencies installed on the host, including ocaml-bun, the multi-core AFL runner): make dune-oxenstored-fuzz 'make check' will also run the fuzzer but with input supplied by OCaml's random number generator, and for a very small number of iterations (few thousand). This doesn't require any external tools (no AFL, bun). On failure it prints a base64 encoding of the fuzzer state that can be used to reproduce the failure instantly, which is very useful for debugging: one can iterate on the failed fuzzer state until it is fixed, and then run the fuzzer again to find next failure. The unit tests here require OCaml 4.06, but the rest of the codebase doesn't (yet). See https://lore.kernel.org/xen-devel/cbb2742191e9c1303fdfd95feef4d829ecf33= a0d.camel@citrix.com/ for previous discussion of OCaml version. Signed-off-by: Edwin T=C3=B6r=C3=B6k Acked-by: Christian Lindig --- tools/ocaml/Makefile | 19 + tools/ocaml/xenstored/process.ml | 12 +- tools/ocaml/xenstored/store.ml | 1 + tools/ocaml/xenstored/test/generator.ml | 189 +++++ tools/ocaml/xenstored/test/model.ml | 253 ++++++ tools/ocaml/xenstored/test/old/arbitrary.ml | 261 +++++++ tools/ocaml/xenstored/test/old/gen_paths.ml | 66 ++ .../xenstored/test/old/xenstored_test.ml | 527 +++++++++++++ tools/ocaml/xenstored/test/pathtree.ml | 40 + tools/ocaml/xenstored/test/testable.ml | 364 +++++++++ tools/ocaml/xenstored/test/types.ml | 427 ++++++++++ tools/ocaml/xenstored/test/xenstored_test.ml | 149 +++- tools/ocaml/xenstored/test/xs_protocol.ml | 733 ++++++++++++++++++ tools/ocaml/xenstored/transaction.ml | 119 ++- 14 files changed, 3151 insertions(+), 9 deletions(-) create mode 100644 tools/ocaml/xenstored/test/generator.ml create mode 100644 tools/ocaml/xenstored/test/model.ml create mode 100644 tools/ocaml/xenstored/test/old/arbitrary.ml create mode 100644 tools/ocaml/xenstored/test/old/gen_paths.ml create mode 100644 tools/ocaml/xenstored/test/old/xenstored_test.ml create mode 100644 tools/ocaml/xenstored/test/pathtree.ml create mode 100644 tools/ocaml/xenstored/test/testable.ml create mode 100644 tools/ocaml/xenstored/test/types.ml create mode 100644 tools/ocaml/xenstored/test/xs_protocol.ml diff --git a/tools/ocaml/Makefile b/tools/ocaml/Makefile index 53dd0a0f0d..de375820a3 100644 --- a/tools/ocaml/Makefile +++ b/tools/ocaml/Makefile @@ -67,3 +67,22 @@ dune-syntax-check: dune-pre .PHONY: build-oxenstored-dune dune-build-oxenstored: dune-pre LD_LIBRARY_PATH=3D$(LIBRARY_PATH) LIBRARY_PATH=3D$(LIBRARY_PATH) C_INCLUD= E_PATH=3D$(C_INCLUDE_PATH) dune build --profile=3Drelease @all + +.PHONY: oxenstored-fuzz1 oxenstored-fuzz +dune-oxenstored-fuzz: dune-pre + # --force is needed, otherwise it would cache a successful run + sh -c '. /etc/profile && C_INCLUDE_PATH=3D$(C_INCLUDE_PATH) dune build --= profile=3Drelease --no-buffer --force @fuzz' + +dune-oxenstored-fuzz1: dune-pre + sh -c '. /etc/profile && C_INCLUDE_PATH=3D$(C_INCLUDE_PATH) dune build --= profile=3Drelease --no-buffer --force @fuzz1' + +.PHONY: container-fuzz +container-fuzz-sanity-test: + dune clean + podman build -t oxenstored-fuzz . + # if UID is 0 then we get EPERM on setrlimit from inside the container + # use containerize script which ensures that uid is not 0 + # (podman/docker run would get us a uid of 0) + # Only do a sanity test with 1 core, actually doing fuzzing inside a cont= ainer is a bad idea + # due to FUSE overlayfs overhead + CONTAINER=3Doxenstored-fuzz CONTAINER_NO_PULL=3D1 DOCKER_CMD=3Dpodman ../= ../automation/scripts/containerize make -C tools/ocaml dune-oxenstored-fuzz1 diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/proce= ss.ml index da8e9cdb26..ac3afb495f 100644 --- a/tools/ocaml/xenstored/process.ml +++ b/tools/ocaml/xenstored/process.ml @@ -172,7 +172,7 @@ let parse_live_update args =3D after timeout elapsed" )*) ] (fun x -> raise (Arg.Bad x)) "live-update -s" ; - debug "Live update process queued" ; + (* debug "Live update process queued" ; *) {!state with deadline =3D Unix.gettimeofday () +. float !timeout ; force=3D !force; pending=3D true}) | _ -> @@ -452,6 +452,8 @@ let transaction_replay c t doms cons =3D (fun () -> try Logging.start_transaction ~con ~tid; + if t.must_fail then + raise Transaction_again; List.iter (perform_exn ~wlog:true replay_t) (Transaction.get_operation= s t); (* May throw EAGAIN *) =20 Logging.end_transaction ~con ~tid; @@ -553,7 +555,7 @@ let do_introduce con t domains cons data =3D | _ -> raise Invalid_Cmd_Args; in let dom =3D - if Domains.exist domains domid then + if Domains.exist domains domid then begin let edom =3D Domains.find domains domid in if (Domain.get_mfn edom) =3D mfn && (Connections.find_domain cons domid= ) !=3D con then begin (* Use XS_INTRODUCE for recreating the xenbus event-channel. *) @@ -561,12 +563,16 @@ let do_introduce con t domains cons data =3D Domain.bind_interdomain edom; end; edom + end else try let ndom =3D Domains.create domains domid mfn port in Connections.add_domain cons ndom; Connections.fire_spec_watches (Transaction.get_root t) cons Store.Path.= introduce_domain; ndom - with _ -> raise Invalid_Cmd_Args + with e -> + let bt =3D Printexc.get_backtrace () in + Logging.debug "process" "do_introduce: %s (%s)" (Printexc.to_string e)= bt; + raise Invalid_Cmd_Args in if (Domain.get_remote_port dom) <> port || (Domain.get_mfn dom) <> mfn th= en raise Domain_not_match diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml index 5f155f45eb..a9c079a417 100644 --- a/tools/ocaml/xenstored/store.ml +++ b/tools/ocaml/xenstored/store.ml @@ -133,6 +133,7 @@ let of_path_and_name path name =3D | _ -> path @ [name] =20 let create path connection_path =3D + Logging.debug "store" "Path.create %S %S" path connection_path; of_string (Utils.path_validate path connection_path) =20 let to_string t =3D diff --git a/tools/ocaml/xenstored/test/generator.ml b/tools/ocaml/xenstore= d/test/generator.ml new file mode 100644 index 0000000000..6f7dc374f8 --- /dev/null +++ b/tools/ocaml/xenstored/test/generator.ml @@ -0,0 +1,189 @@ +module type S =3D sig + type cmd + + type state + + val init_state : state + + val next_state : cmd -> state -> state + + val precond : cmd -> state -> bool +end + +module IntSet =3D Set.Make (Int) +module IntMap =3D Map.Make (Int) + +module Pickable (K : sig + include Map.OrderedType + + include Hashtbl.HashedType with type t :=3D t +end) =3D +struct + (* allow picking a random value from a changing map keys. + Store a random value (hash of key) as first element of key, + then use find_first to pick an item related to the random element if = any. + This should be more efficient than converting to a list and using Lis= t.nth to pick + *) + module Key =3D struct + type t =3D int * K.t + + let of_key k =3D (K.hash k, k) + + let compare (h, k) (h', k') =3D + match Int.compare h h' with 0 -> K.compare k k' | r -> r + end + + module M =3D Map.Make (Key) + + type 'a t =3D 'a M.t + + let empty =3D M.empty + + let singleton k v =3D M.singleton (Key.of_key k) v + + let add k v m =3D M.add (Key.of_key k) v m + + let find_opt k m =3D M.find_opt (Key.of_key k) m + + let mem k m =3D M.mem (Key.of_key k) m + + let remove k m =3D M.remove (Key.of_key k) m + + let merge f m m' =3D M.merge f m m' + + let is_empty =3D M.is_empty + + let update k f m =3D M.update (Key.of_key k) f m + + let choose rnd m =3D + (* function needs to be monotonic, so the hash has to be part of the k= ey *) + let gte (keyhash, _) =3D Int.compare keyhash rnd >=3D 0 in + match M.find_first_opt gte m with + | Some ((_, k), _) -> + k + | None -> + snd @@ fst @@ M.min_binding m +end + +module PickablePath =3D Pickable (struct + type t =3D string + + let hash =3D Hashtbl.hash + + let compare =3D String.compare + + let equal =3D String.equal +end) + +module PickableInt =3D Pickable (struct + include Int + + let hash =3D Hashtbl.hash +end) + +module PathObserver =3D struct + type state =3D + { seen: unit PickablePath.t + ; dom_txs: unit PickableInt.t PickableInt.t + ; next_tid: int } + + let choose_path t rnd =3D PickablePath.choose rnd t.seen + + let choose_domid t rnd =3D PickableInt.choose rnd t.dom_txs + + let choose_txid_opt t domid rnd =3D + match PickableInt.find_opt domid t.dom_txs with + | None -> + 0 + | Some txs -> + if PickableInt.is_empty txs then 0 else PickableInt.choose rnd txs + + let new_domid domid =3D PickableInt.singleton domid PickableInt.empty + + let both _ _ _ =3D Some () + + let merge_txs _ s s' =3D + let s =3D Option.value ~default:PickableInt.empty s in + let s' =3D Option.value ~default:PickableInt.empty s' in + Some (PickableInt.merge both s s') + + let init_state =3D + {seen=3D PickablePath.singleton "/" (); dom_txs=3D new_domid 0; next_t= id=3D 1} + + let with_path path t =3D {t with seen=3D PickablePath.add path () t.seen} + + let split0 str =3D + match Process.split (Some 2) '\000' str with + | [x; y] -> + (x, y) + | _ -> + invalid_arg str + + let next_state (domid, cmd) t =3D + let open Xenbus.Xb in + match cmd with + | {Xenbus.Packet.ty=3D Transaction_start; _} -> + let update =3D function + | None -> + None + | Some txs -> + Some (PickableInt.add t.next_tid () txs) + in + { t with + dom_txs=3D PickableInt.update domid update t.dom_txs + ; next_tid=3D t.next_tid + 1 } + | { Xenbus.Packet.ty=3D + Op.( + ( Rm + | Read + | Directory + | Getperms + | Setperms + | Unwatch + | Reset_watches + | Getdomainpath + | Isintroduced + | Set_target + | Debug )) + ; _ } -> + t + | {Xenbus.Packet.ty=3D Op.(Watchevent | Error | Resume | Invalid); _} = -> + assert false + | {Xenbus.Packet.ty=3D Op.Transaction_end; tid; _} -> + let update =3D function + | None -> + None + | Some txs -> + Some (PickableInt.remove tid txs) + in + {t with dom_txs=3D PickableInt.update domid update t.dom_txs} + | {Xenbus.Packet.ty=3D Op.(Write | Mkdir | Watch); data} -> + let path, _ =3D split0 data in + with_path path t + | {Xenbus.Packet.ty=3D Introduce; data} -> + let domidstr, _ =3D split0 data in + let domid' =3D int_of_string domidstr in + if domid =3D 0 then + { t with + dom_txs=3D PickableInt.merge merge_txs t.dom_txs (new_domid do= mid') } + else t + | {Xenbus.Packet.ty=3D Release; data} -> + let domidstr, _ =3D split0 data in + let domid =3D int_of_string domidstr in + {t with dom_txs=3D PickableInt.remove domid t.dom_txs} + + let precond (domid, cmd) t =3D + ( match PickableInt.find_opt domid t.dom_txs with + | None -> + false + | Some txs -> + let tid =3D cmd.Xenbus.Packet.tid in + tid =3D 0 || PickableInt.mem tid txs ) + && Testable.Command.precond cmd t + + let pp =3D + let open Fmt in + Dump.record + [ Dump.field "domid" fst Fmt.int + ; Dump.field "cmd" snd Testable.Command.pp_dump ] +end diff --git a/tools/ocaml/xenstored/test/model.ml b/tools/ocaml/xenstored/te= st/model.ml new file mode 100644 index 0000000000..4b5ae462fb --- /dev/null +++ b/tools/ocaml/xenstored/test/model.ml @@ -0,0 +1,253 @@ +open Xs_protocol + +(* Conventions: +Aim for correctness, use simplest data structure that unambigously represe= nts state. + +E.g.: +* a list when duplicates are allowed, order matters and the empty list is = a valid value +* a set when elements appearing multiple time have the same semantic meani= ng as them appearing once, +and the order is unspecified or sorted +* a map when a single key is mapped to a single value, and order is unspec= ified or sorted + +When we must retain the original order for queries, but semantically it do= esn't matter +then store both a canonical representation and the original order. + +*) + +let rec string_for_all_from s f pos =3D + pos =3D String.length s || (f s.[pos] && (string_for_all_from s f @@ (po= s + 1))) + +type error =3D [`Msg of string] + +module Path : sig + (** a valid xenstore path *) + type t + + val root : t + + val of_string : string -> t option + (** [of_string path] parses [path]. + @return [None] if the path is syntactically not valid *) + + val to_string : t -> string + (** [to_string path] converts path to string. *) + + (** [is_child parent child] returns true if [child] is a child of [paren= t]. + A path is considered to be a child of itself *) + val is_child : t -> t -> bool +end =3D struct + type t =3D string list + + let is_valid_char =3D function + | '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' | '-' | '/' | '_' | '@' -> + true + | _ -> + false + + let root =3D [""] + + let nonempty s =3D String.length s > 0 + + let of_string s =3D + let n =3D String.length s in + if + n > 0 (* empty path is not permitted *) + && n < 1024 + (* paths cannot exceed 1024 chars, FIXME: relative vs absolute *) + && string_for_all_from s is_valid_char 0 + then + match String.split_on_char '/' s with + | [] -> + assert false + | [""; ""] -> + Some root + | _ :: tl as path -> + if List.for_all nonempty tl then Some path else None + else None + + let to_string =3D String.concat "/" + + let rec is_child p c =3D + match (p, c) with + | [], [] -> + true (* a path is a child of itself *) + | [], _ -> + true + | phd :: ptl, chd :: ctl when phd =3D chd -> + is_child ptl ctl + | _ -> + false +end + +module PathMap =3D Map.Make (String) +module DomidSet =3D Set.Make (Int) +module DomidMap =3D Map.Make (Int) + +let preserve_order =3D ref true + +module CanonicalACL =3D struct + module RW =3D struct + type t =3D {read: bool; write: bool} + + let of_perm =3D function + | ACL.NONE -> + {read=3D false; write=3D false} + | ACL.WRITE -> + {read=3D false; write=3D true} + | ACL.READ -> + {read=3D true; write=3D false} + | ACL.RDWR -> + {read=3D true; write=3D true} + + let to_perm =3D function + | {read=3D false; write=3D false} -> + ACL.NONE + | {read=3D false; write=3D true} -> + ACL.WRITE + | {read=3D true; write=3D false} -> + ACL.READ + | {read=3D true; write=3D true} -> + ACL.RDWR + + let full =3D {read=3D true; write=3D true} + end + + module RWMap =3D struct + type t =3D {fallback: RW.t; map: RW.t DomidMap.t} + + let lookup t domid =3D + (* other=3DRDWR can be overriden by explicitly revoking + permissions for a domain, so a read=3Dfalse,write=3Dfalse + in the DomidMap is not necessarily redundant + *) + DomidMap.find_opt domid t.map |> Option.value ~default:t.fallback + + let create fallback owner =3D + (* owner always has full permissions, and cannot be overriden *) + {fallback; map=3D DomidMap.singleton owner RW.full} + + let override t (domid, perm) =3D + let rw =3D RW.of_perm perm in + (* first entry wins, see perms.ml, also entries that are same as the= fallback are + not necessarily redundant: (b1,b2,r2) means that domid 2 has rdwr, + but if we remove the seemingly redundant `b2` entry then the over= ride would make it + just read which would be wrong. *) + if DomidMap.mem domid t.map then t + else {t with map=3D DomidMap.add domid rw t.map} + end + + type t =3D {original: ACL.t; owner: ACL.domid; acl: RWMap.t} + + let can_read t domid =3D (RWMap.lookup t.acl domid).read + + let can_write t domid =3D (RWMap.lookup t.acl domid).write + + let owner t =3D t.owner + + let of_acl original =3D + let fallback =3D RW.of_perm original.ACL.other in + let owner =3D original.ACL.owner in + let acl =3D + let init =3D RWMap.create fallback owner in + List.fold_left RWMap.override init original.ACL.acl + in + {original; owner; acl} + + let to_acl t =3D + if !preserve_order then t.original + else + ACL. + { owner=3D t.owner + ; other=3D RW.to_perm t.acl.fallback + ; acl=3D t.acl.map |> DomidMap.map RW.to_perm |> DomidMap.bindings= } +end + +module Store =3D struct + type node =3D {value: string; children: string list; acl: CanonicalACL.t} + + type t =3D {paths: node PathMap.t} + + let create () =3D {paths=3D PathMap.empty} + + let parent x =3D failwith "TODO" + + let add t key value =3D + let paths =3D PathMap.add key value t.paths in + {paths} + + let remove t key =3D + let paths =3D PathMap.remove key t.paths in + {paths} +end + +type t =3D Store.t + +let create () =3D Store.create () + +let reply_enoent =3D Response.Error "ENOENT" + +let reply_eexist =3D Response.Error "EEXIST" + +let with_node_read t path f =3D + ( t + , match PathMap.find_opt path t.paths with + | None -> + reply_enoent + | Some n -> + f n ) + +(* TODO: perm check *) +let perform_path t domid path =3D function + | Request.Read -> + with_node_read t path @@ fun n -> Response.Read n.value + | Request.Directory -> + with_node_read t path @@ fun n -> Response.Directory n.children + | Request.Directory_part _ -> + (t, Response.Error "ENOTSUP") + | Request.Getperms -> + with_node_read t path @@ fun n -> Response.Getperms n.acl + | Request.Write value -> ( + (* TODO: implicit mkdir *) + match PathMap.find_opt path t.paths with + | Some _ -> + (t, reply_eexist) + | None -> + let acl =3D ACL.{owner=3D domid; other=3D NONE; acl=3D []} in + let n =3D {value; children=3D []; acl} in + ({t with paths=3D PathMap.add path n t.paths}, Response.Write) ) + | Request.Setperms acl -> ( + match PathMap.find_opt path t.paths with + | Some _ -> + (t, reply_enoent) + | None -> + let update_node =3D function + | None -> + None + | Some n -> + Some {n with acl} + in + ( {t with paths=3D PathMap.update path update_node t.paths} + , Response.Setperms ) ) + | Request.Mkdir -> ( + (* TODO: implicit mkdir *) + match PathMap.find_opt path t.paths with + | Some _ -> + (t, reply_eexist) + | None -> + let acl =3D ACL.{owner=3D domid; other=3D NONE; acl=3D []} in + let n =3D {value=3D ""; children=3D []; acl} in + ({t with paths=3D PathMap.add path n t.paths}, Response.Mkdir) ) + | Request.Rm -> ( + match PathMap.find_opt path t.paths with + | None -> + (t, reply_enoent) + | Some _ -> + ({t with paths=3D PathMap.remove path t.paths}, Response.Rm) ) + +let perform t domid =3D function + | Request.PathOp (path, op) -> + perform_path t domid path op + | Request.Getdomainpath domid -> + (t, Response.Getdomainpath (Printf.sprintf "/local/domain/%d" domid)) + | _ -> + failwith "TODO" diff --git a/tools/ocaml/xenstored/test/old/arbitrary.ml b/tools/ocaml/xens= tored/test/old/arbitrary.ml new file mode 100644 index 0000000000..6b0bf9864a --- /dev/null +++ b/tools/ocaml/xenstored/test/old/arbitrary.ml @@ -0,0 +1,261 @@ +open QCheck + +(* See https://github.com/gasche/random-generator/blob/51351c16b587a1c4216= d158e847dcfa6db15f009/random_generator.mli#L275-L325 + for background on fueled generators for recursive data structures. + The difference here is that we build an N-ary tree, not a binary tree a= s in the example. + So we need to spread the fuel among elements of a list of random size. +*) + +(** [spread fuel] creates an array of a random size, and spreads fuel amon= g array elements. + Each array slot uses up at least 1 fuel itself. + For example the full list of possible arrays with [4] fuel is: + {[ [[|3|]; [|0; 2|]; [|1; 1|]; [|2; 0|]; [|0; 0; 0; 0|]] ]} +*) +let spread =3D function + | 0 -> + Gen.return [||] + | n when n < 0 -> + invalid_arg "negative fuel" + | n -> + Gen.( + 1 -- n + >>=3D fun per_element -> + (* We got n fuel to divide up, such that most elements have [per_e= lement] fuel. + Round up the number of elements *) + let m =3D (n + per_element - 1) / per_element in + (* each element uses up at least one fuel, this has to be subtract= ed before propagation *) + let a =3D Array.make m (per_element - 1) in + (* handle remainder *) + a.(0) <- n - (per_element * (m - 1)) - 1 ; + assert (Array.fold_left ( + ) m a =3D n) ; + (* ensure that remainder is in a random position *) + Gen.shuffle_a a >|=3D fun () -> a) + +(** [spread_l fuel sized_element] spreads [fuel] among list elements, + where each list element is created using [sized_element]. + [sized_element] needs to create an element of exactly the requested si= ze + (which may be a recursive element, that calls [spread_l] in turn). + Each list element consumes 1 fuel implicitly and sized_element is call= ed with decreased fuel. + *) +let spread_l fuel (sized_elem : 'a Gen.sized) =3D + Gen.( + spread fuel + >>=3D fun a -> + a |> Array.map sized_elem |> Gen.flatten_a |> Gen.map Array.to_list) + +module Tree =3D struct + (* For better shrinking put the (recursive) list first *) + type 'a t =3D Nodes of ('a t * 'a) list + + (** [empty] the empty tree (of size 1) *) + let empty =3D Nodes [] + + (** [nodes subtree] tree constructor *) + let nodes children =3D Nodes children + + (** [tree elem_gen] generates a random tree, with elements generated by = [elem_gen] *) + let tree elem =3D + Gen.sized @@ Gen.fix + @@ fun self fuel -> + (* self is the generator for a subtree *) + let node fuel =3D Gen.(pair (self fuel) elem) in + (* using spread_l ensures that fuel decreases by at least 1, thus ensu= ring termination *) + Gen.map nodes @@ spread_l fuel node + + (** [zero _] is a default implementation for [small] *) + let zero _ =3D 0 + + (** [small elem_size tree] returns the count of nodes in the tree and th= e sum of element sizes + as determined by [elem_size] *) + let rec small ?(elem_size =3D zero) (Nodes tree) =3D + List.fold_left + (fun acc (subtree, elem) -> + acc + elem_size elem + small ~elem_size subtree) + 1 tree + + (** [shrink ?elem tree] returns a list of potentially smaller trees base= d on [tree]. + *) + let shrink ?(elem =3D Shrink.nil) =3D + (* Shrinking needs to generate smaller trees (as determined by [small]= ), + QCheck will keep iterating until it finds a smaller tree that still= reproduces the bug. + It will then invoke the shrinker again on the smaller tree to attem= pt to shrink it further. + Once the tree shape cannot be shrunk further individual node elemen= ts will be shrunk. + *) + let rec tree (Nodes t) =3D + (* first try to shrink the subtree to a leaf, + and if that doesn't work then recursively shrink the subtree + *) + Iter.append (Iter.return empty) + @@ Iter.map nodes + @@ Shrink.list ~shrink:(Shrink.pair tree elem) t + in + tree + + (** [make arb] creates a tree generator with elements generated by [arb]. + The tree has a shrinker and size defined. + *) + let make arb =3D + let gen =3D tree @@ gen arb in + QCheck.make + ~small:(small ?elem_size:arb.small) + ~shrink:(shrink ?elem:arb.shrink) gen + + (** [paths_of_tree ~join tree] return all paths through the tree, + with path elements joined using [join] *) + let paths_of_tree ~join t =3D + let rec paths_of_subtree (paths, path) (Nodes nodes) =3D + ListLabels.fold_left nodes ~init:paths ~f:(fun paths (tree, elem) -> + let path =3D elem :: path in + paths_of_subtree (join (List.rev path) :: paths, path) tree) + in + paths_of_subtree ([], []) t + + let paths join arb =3D + make arb + (* we need to retain the tree, so that the shrinking is done on the tr= ee, + and not on the paths *) + |> map_keep_input (paths_of_tree ~join) +end + +module Case =3D struct + type ('a, 'b) t =3D + { case_tag: string + ; orig: 'a QCheck.arbitrary + ; map: 'a -> 'b + ; shrink: 'a -> 'b Iter.t + ; print: 'a Print.t + ; small: 'a -> int } + + (** [make arb f] defines a new variant case with constructor arguments + generated by [arb] and constructor [f]. *) + let make case_tag orig map =3D + let shrink a =3D + match orig.QCheck.shrink with + | None -> + Iter.empty + | Some s -> + Iter.map map @@ s a + in + let small a =3D match orig.QCheck.small with None -> 0 | Some s -> s a= in + let print a =3D match orig.QCheck.print with None -> "_" | Some p -> p= a in + {case_tag; orig; map; shrink; small; print} + + type 'a call =3D + { tag: string + ; shrink_lazy: 'a Iter.t Lazy.t + ; small_lazy: int Lazy.t + ; print: string Lazy.t } + + (** [call tag case args] used by the implementation of [rev] to build a = shrinker/small of appropriate type *) + let call t a =3D + { tag=3D t.case_tag + ; shrink_lazy=3D lazy (t.shrink a) + ; small_lazy=3D lazy (t.small a) + ; print=3D lazy (t.print a) } + + (** [to_sum case] converts all variant cases to the same type so they ca= n be put into a list *) + let to_sum t =3D Gen.map t.map @@ QCheck.gen t.orig +end + +(** [sum ~print ~rev cases] defines an arbitrary for a sum type consisting= of [cases] + variant case generators. [print] converts the sum type to a string. + [rev] matches on the sum type and should invoke [Case.call = ]. + + E.g. + {| + type t =3D A of int | B of float + + let case_a =3D Case.make "A" int (fun i -> A i) + + let case_b =3D Case.make "B" float (fun f -> B f) + + let rev t =3D + match t with A i -> Case.call case_a i | B g -> Case.call case_b g + + let x =3D + sum + ~print:(fun _ -> failwith "TODO") + [Case.to_sum case_a; Case.to_sum case_b] + |} + *) +let sum ~rev lst =3D + let shrink b =3D Lazy.force (rev b).Case.shrink_lazy in + let small b =3D Lazy.force (rev b).Case.small_lazy in + let collect b =3D (rev b).Case.tag in + let print b =3D let r =3D rev b in r.Case.tag ^ " " ^ Lazy.force r.print= in + QCheck.make ~shrink ~small ~collect ~print (Gen.oneof lst) + +(* +let mk_packet op to_string arb =3D + Case.make arb (fun x -> Xenbus.Packet.create 0 0 op (to_string x)) + +let read_packet =3D + mk_packet Xenbus.Xb.Op.Read Store.Path.to_string (list path_element) + +let write_packet =3D + mk_packet Xenbus.Xb.Op.Write + (fun (x, y) -> Store.Path.to_string x ^ "\x00" ^ y) + (pair (list path_element) binary) + +let packet =3D + sum ~print:Xenbus.Packet.to_string + [Case.to_sum read_packet; Case.to_sum write_packet] +*) + +(** [binary] is a generator of strings containing \x00 characters. *) +let binary =3D + (* increase frequency of '\x00' to 10%, otherwise it'd be ~1/256 *) + string_gen (Gen.frequency [(10, Gen.return '\x00'); (90, Gen.char)]) + |> set_print String.escaped + +(** [path_chars] valid path characters according to Xenstore protocol. *) +let path_chars =3D + List.init 256 Char.chr + |> List.filter Store.Path.char_is_valid + |> Array.of_list |> Gen.oneofa + +(** [path_element] a valid path element *) +let path_element =3D + string_gen_of_size Gen.small_int path_chars + +type tree =3D string Tree.t + +let paths =3D Tree.paths Store.Path.to_string path_element + +let with_validate p =3D + map_same_type + @@ fun v -> + (* reject it in a way known to QCheck: precondition failed, + instead of testcase failed *) + assume @@ p v ; + v + +(** [non_nul string_arb] rejects strings generated by [string_arb] that co= ntain '\x00'. *) +let non_nul =3D with_validate @@ fun s -> not (String.contains s '\x00') + +(** [plus arb] generates a list of 1 or more elements generated by [arb] *) +let plus arb =3D list_of_size Gen.(map succ small_int) arb + +(** [star arb] generates a list of 0 or more elements generated by [arb] *) +let star arb =3D list_of_size Gen.small_int arb + +let reserved =3D + string_of_size Gen.(frequency [(90, Gen.return 0); (10, Gen.small_int)]) + +(** According to xenstore protocol this could go up to 65535, but an actua= l domid + shouldn't go above this value *) +let domid_first_reserved =3D 0x7FF0 + +(** [new_domid] generates DomU domids *) +let new_domid =3D 1 -- domid_first_reserved + +let permty =3D + let open Perms in + oneofl [READ; WRITE; RDWR; NONE] + +let perms domid =3D + map + (fun (domid, other, acls) -> Perms.Node.create domid other acls) + ~rev:(fun n -> + (Perms.Node.get_owner n, Perms.Node.get_other n, Perms.Node.get_acl = n)) + @@ triple domid permty (small_list (pair domid permty)) diff --git a/tools/ocaml/xenstored/test/old/gen_paths.ml b/tools/ocaml/xens= tored/test/old/gen_paths.ml new file mode 100644 index 0000000000..b50c5b7cad --- /dev/null +++ b/tools/ocaml/xenstored/test/old/gen_paths.ml @@ -0,0 +1,66 @@ +open QCheck +open Store + +type tree =3D Leaf | Nodes of (string * tree) list + +let nodes children =3D Nodes children +let gen_tree =3D QCheck.Gen.(sized @@ fix + (fun self n -> + let children =3D frequency [1, pure 0; 2, int_bound n] >>=3D fun m -> + match m with + | 0 -> pure [] + | _ -> list_repeat m (pair string (self (n/m))) + in + frequency + [ 1, pure Leaf + ; 2, map nodes children + ] + )) + +let rec paths_of_tree (acc, path) =3D function +| Leaf -> acc +| Nodes l -> + List.fold_left (fun acc (k, children) -> + let path =3D k :: path in + paths_of_tree (Store.Path.to_string (List.rev path) :: acc, path) chil= dren + ) acc l + +let gen_paths_choices =3D + Gen.map (fun tree -> + tree |> paths_of_tree ([], []) |> Array.of_list + ) gen_tree + +(*let arb_name =3D Gen.small_string + +let arb_permty =3D let open Perms in oneofl [ READ; WRITE; RDWR; NONE ] + +let arb_domid =3D oneofl [ 0; 1; 0x7FEF] + +let arb_perms =3D + map (fun (domid, other, acls) -> Perms.Node.create domid other acls) + ~rev:(fun n -> Perms.Node.get_owner n, Perms.Node.get_other n, Perms.No= de.get_acl n) + @@ triple arb_domid arb_permty (small_list (pair arb_domid arb_permty))= *) + +let arb_name =3D Gen.small_string +let arb_value =3D Gen.small_string + +let node_of name value children =3D + List.fold_left (fun c acc -> Node.add_child acc c) + (Node.create name Perms.Node.default0 value ) children + +let g =3D QCheck.Gen.(sized @@ fix + (fun self n -> + frequency [1, pure 0; 2, int_bound n] >>=3D fun m -> + let children =3D match m with + | 0 -> pure [] + | _ -> list_repeat m (self (n/m)) + in + map3 node_of arb_name arb_value children + )) + +let paths_of_tree t =3D + let paths =3D ref [] in + Store.traversal t (fun path node -> + paths :=3D (Store.Path.of_path_and_name path (Node.get_name node) |> S= tore.Path.to_string) :: !paths + ); + !paths diff --git a/tools/ocaml/xenstored/test/old/xenstored_test.ml b/tools/ocaml= /xenstored/test/old/xenstored_test.ml new file mode 100644 index 0000000000..84cfc45d4f --- /dev/null +++ b/tools/ocaml/xenstored/test/old/xenstored_test.ml @@ -0,0 +1,527 @@ +open Stdext +open QCheck +open Arbitrary + +let () =3D + (* Logging.access_log_nb_files :=3D 1 ; + Logging.access_log_transaction_ops :=3D true ; + Logging.access_log_special_ops :=3D true ; + Logging.access_log_destination :=3D File "/tmp/log" ; + Logging.init_access_log ignore ; + Logging.set_xenstored_log_destination "/dev/stderr"; + Logging.init_xenstored_log (); *) + Domains.xenstored_port :=3D "xenstored-port" ; + let f =3D open_out !Domains.xenstored_port in + Printf.fprintf f "%d" 1 ; + close_out f ; + Domains.xenstored_kva :=3D "/dev/zero" + +module Command =3D struct + type value =3D string + + let value =3D binary + + type token =3D string + + type txid =3D int + + type domid =3D Xenctrl.domid + + type t =3D + | Read of Store.Path.t + | Write of Store.Path.t * value + | Mkdir of Store.Path.t + | Rm of Store.Path.t + | Directory of Store.Path.t + (* | Directory_part not implemented *) + | Get_perms of Store.Path.t + | Set_perms of Store.Path.t * Perms.Node.t + | Watch of Store.Path.t * token + | Unwatch of Store.Path.t * token + | Reset_watches + | Transaction_start + | Transaction_end of bool + | Introduce of domid * nativeint * int + | Release of int + | Get_domain_path of domid + | Is_domain_introduced of domid + | Set_target of domid * domid + | LiveUpdate + + type state =3D + { store: Store.t + ; doms: Domains.domains + ; cons: Connections.t + ; domids: int array } + + let path =3D list path_element + + let token =3D printable_string + + let domid state =3D oneofa ~print:Print.int state.domids + + let cmd state =3D + let domid =3D domid state in + let cmd_read =3D Case.make "READ" path (fun path -> Read path) in + let cmd_write =3D + Case.make "WRITE" (pair path value) (fun (path, value) -> + Write (path, value)) + in + let cmd_mkdir =3D Case.make "MKDIR" path (fun path -> Mkdir path) in + let cmd_rm =3D Case.make "RM" path (fun path -> Rm path) in + let cmd_directory =3D + Case.make "DIRECTORY" path (fun path -> Directory path) + in + let cmd_get_perms =3D + Case.make "GET_PERMS" path (fun path -> Get_perms path) + in + let cmd_set_perms =3D + Case.make "SET_PERMS" + (pair path (perms domid)) + (fun (path, perms) -> Set_perms (path, perms)) + in + let cmd_watch =3D + Case.make "WATCH" (pair path token) (fun (path, token) -> + Watch (path, token)) + in + let cmd_unwatch =3D + Case.make "UNWATCH" (pair path token) (fun (path, token) -> + Unwatch (path, token)) + in + let cmd_reset_watches =3D + Case.make "RESET_WATCHES" unit (fun () -> Reset_watches) + in + let cmd_tx_start =3D + Case.make "TRANSACTION_START" unit (fun () -> Transaction_start) + in + let cmd_tx_end =3D + Case.make "TRANSACTION_END" bool (fun commit -> Transaction_end comm= it) + in + let cmd_introduce =3D + Case.make "INTRODUCE" (triple domid int int) (fun (domid, gfn, port)= -> + Introduce (domid, Nativeint.of_int gfn, port)) + in + let cmd_release =3D Case.make "RELEASE" domid (fun domid -> Release do= mid) in + let cmd_get_domain_path =3D + Case.make "GET_DOMAIN_PATH" domid (fun domid -> Get_domain_path domi= d) + in + let cmd_is_domain_introduced =3D + Case.make "IS_DOMAIN_INTRODUCED" domid (fun domid -> + Is_domain_introduced domid) + in + let cmd_set_target =3D + Case.make "SET_TARGET" (pair domid domid) (fun (domid, tdomid) -> + Set_target (domid, tdomid)) + in + let cmd_live_update =3D + Case.make "CONTROL live-update" unit (fun () -> LiveUpdate) + in + let rev =3D function + | Read a -> + Case.call cmd_read a + | Write (p, v) -> + Case.call cmd_write (p, v) + | Mkdir a -> + Case.call cmd_mkdir a + | Rm a -> + Case.call cmd_rm a + | Directory a -> + Case.call cmd_directory a + | Get_perms a -> + Case.call cmd_get_perms a + | Set_perms (p, v) -> + Case.call cmd_set_perms (p, v) + | Watch (p, t) -> + Case.call cmd_watch (p, t) + | Unwatch (p, t) -> + Case.call cmd_unwatch (p, t) + | Reset_watches -> + Case.call cmd_reset_watches () + | Transaction_start -> + Case.call cmd_tx_start () + | Transaction_end a -> + Case.call cmd_tx_end a + | Introduce (d, g, p) -> + Case.call cmd_introduce (d, Nativeint.to_int g, p) + | Release a -> + Case.call cmd_release a + | Get_domain_path a -> + Case.call cmd_get_domain_path a + | Is_domain_introduced a -> + Case.call cmd_is_domain_introduced a + | Set_target (d, t) -> + Case.call cmd_set_target (d, t) + | LiveUpdate -> + Case.call cmd_live_update () + in + let open Case in + sum ~rev + [ to_sum cmd_read + ; to_sum cmd_write + ; to_sum cmd_mkdir + ; to_sum cmd_rm + ; to_sum cmd_directory + ; to_sum cmd_get_perms + ; to_sum cmd_set_perms + ; to_sum cmd_watch + ; to_sum cmd_unwatch + ; to_sum cmd_reset_watches + ; to_sum cmd_tx_start + ; to_sum cmd_tx_end + ; to_sum cmd_introduce + ; to_sum cmd_release + ; to_sum cmd_get_domain_path + ; to_sum cmd_is_domain_introduced + ; to_sum cmd_set_target + ; to_sum cmd_live_update ] + + let run tid =3D + let open Xenstore.Queueop in + function + | Read p -> + read tid Store.Path.(to_string p) + | Write (p, v) -> + write tid Store.Path.(to_string p) v + | Mkdir p -> + mkdir tid Store.Path.(to_string p) + | Rm p -> + rm tid Store.Path.(to_string p) + | Directory p -> + directory tid Store.Path.(to_string p) + | Get_perms p -> + getperms tid Store.Path.(to_string p) + | Set_perms (p, v) -> + setperms tid Store.Path.(to_string p) Perms.Node.(to_string v) + | Watch (p, t) -> + watch Store.Path.(to_string p) t + | Unwatch (p, t) -> + unwatch Store.Path.(to_string p) t + | Reset_watches -> + let open Xenbus in + fun con -> Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Reset_watches = "") + | Transaction_start -> + transaction_start + | Transaction_end c -> + transaction_end tid c + | Release d -> + release d + | Get_domain_path d -> + getdomainpath d + | Is_domain_introduced d -> + let open Xenbus in + fun con -> + Xb.queue con + (Xb.Packet.create 0 0 Xb.Op.Isintroduced (string_of_int d)) + | Set_target (d, t) -> + let open Xenbus in + fun con -> + Xb.queue con + (Xb.Packet.create 0 0 Xb.Op.Isintroduced + (String.concat "\x00" [string_of_int d; string_of_int t])) + | LiveUpdate -> + debug ["live-update"; "-s"] + | Introduce (d, g, p) -> + introduce d g p +end + +module Spec =3D struct + type cmd =3D New | Cmd of Command.domid * int option * Command.t + + type state =3D + { xb: Xenbus.Xb.t + ; cnt: int + ; cmdstate: Command.state ref option + ; failure: (exn * string) option } + + type sut =3D state ref + + let doms =3D Domains.init (Event.init ()) ignore + + let dom0 =3D Domains.create0 doms + + let new_state () =3D + let cons =3D Connections.create () in + Connections.add_domain cons dom0 ; + let store =3D Store.create () in + let con =3D Perms.Connection.create 0 in + Store.mkdir store con ["tool"] ; + {Command.store; doms; cons; domids=3D [|0|]} + + let print =3D function + | New -> + "NEW" + | Cmd (d, t, c) -> + let s =3D new_state () in + let cmd =3D Command.cmd s in + (Option.get (triple (Command.domid s) (option int) cmd).print) (d,= t, c) + + let shrink =3D function + | New -> + Iter.empty + | Cmd (d, t, c) -> + let s =3D new_state () in + let cmd =3D Command.cmd s in + Iter.map (fun (d, t, c) -> Cmd (d, t, c)) + @@ (Option.get (triple (Command.domid s) (option int) cmd).shrink) + (d, t, c) + + let arb_cmd state =3D + ( match state.cmdstate with + | None -> + always New + | Some s -> + let cmd =3D Command.cmd !s in + QCheck.map + (fun (d, t, c) -> Cmd (d, t, c)) + ~rev:(fun (Cmd (d, t, c)) -> (d, t, c)) + @@ triple (Command.domid !s) (option int) cmd ) + |> set_print print |> set_shrink shrink + + (* |> set_collect (fun (_, _, c) -> (Option.get cmd.QCheck.collect) c= )*) + + let init_state =3D + {cnt=3D 0; xb=3D Xenbus.Xb.open_fd Unix.stdout; cmdstate=3D None; fail= ure=3D None} + + let precond cmd s =3D + match (cmd, s.cmdstate) with + | New, None -> + true + | New, _ -> + false + | Cmd _, None -> + false + | Cmd (_, _, Command.Release 0), _ -> + false + | _ -> + true + + let next_state cmd state =3D + { ( try + assume (precond cmd state) ; + match cmd with + | New -> + {state with cmdstate=3D Some (ref @@ new_state ())} + | Cmd (domid, tid, cmd) -> + let tid =3D match tid with None -> 0 | Some id -> 1 + id in + Command.run tid cmd state.xb ; + let s =3D !(Option.get state.cmdstate) in + let con =3D Connections.find_domain s.Command.cons domid in + Queue.clear con.xb.pkt_out ; + let run_packet packet =3D + let tid, rid, ty, data =3D Xenbus.Xb.Packet.unpack packet = in + let req =3D {Packet.tid; Packet.rid; Packet.ty; Packet.dat= a} in + Process.process_packet ~store:s.Command.store + ~cons:s.Command.cons ~doms:s.Command.doms ~con ~req ; + Process.write_access_log ~ty ~tid + ~con:(Connection.get_domstr con) + ~data ; + let packet =3D Connection.peek_output con in + let tid, _rid, ty, data =3D Xenbus.Xb.Packet.unpack packet= in + Process.write_answer_log ~ty ~tid + ~con:(Connection.get_domstr con) + ~data + in + Queue.iter run_packet state.xb.pkt_out ; + Queue.clear state.xb.pkt_out ; + state + with e -> + let bt =3D Printexc.get_backtrace () in + {state with failure=3D Some (e, bt)} ) + with + cnt=3D state.cnt + 1 } + + let init_sut () =3D ref init_state + + let cleanup _ =3D () + + module P =3D struct + type t =3D string list + + let compare =3D compare + end + + module PathMap =3D Map.Make (P) + + module DomidMap =3D Map.Make (struct + type t =3D Xenctrl.domid + + let compare =3D compare + end) + + module IntMap =3D Map.Make (struct + type t =3D int + + let compare =3D compare + end) + + module FDMap =3D Map.Make (struct + type t =3D Unix.file_descr + + let compare =3D compare + end) + + let map_of_store s =3D + let m =3D ref PathMap.empty in + Store.dump_fct s (fun path node -> m :=3D PathMap.add path node !m) ; + !m + + let node_equiv n n' =3D + Perms.equiv (Store.Node.get_perms n) (Store.Node.get_perms n') + && Store.Node.get_name n =3D Store.Node.get_name n' + && Store.Node.get_value n =3D Store.Node.get_value n' + + let store_root_equiv s s' =3D + if not (PathMap.equal node_equiv (map_of_store s) (map_of_store s')) t= hen + let b =3D Store.dump_store_buf s.root in + let b' =3D Store.dump_store_buf s'.root in + Test.fail_reportf "Store trees are not equivalent:\n %s\n <>\n %s" + (Buffer.contents b) (Buffer.contents b') + else true + + let map_of_domid_table tbl =3D Hashtbl.fold DomidMap.add tbl DomidMap.em= pty + + let map_of_quota q =3D map_of_domid_table q.Quota.cur + + let store_quota_equiv root root' q q' =3D + let _ =3D + DomidMap.merge + (fun domid q q' -> + let q =3D Option.value ~default:(-1) q in + let q' =3D Option.value ~default:(-1) q' in + if q <> q' then + let b =3D Store.dump_store_buf root in + let b' =3D Store.dump_store_buf root' in + Test.fail_reportf "quota mismatch on %d: %d <> %d\n%s\n%s\n" d= omid q + q' (Buffer.contents b) (Buffer.contents b') + else Some q) + (map_of_quota q) (map_of_quota q') + in + true + + let store_equiv s s' =3D + store_root_equiv s s' + && store_quota_equiv s.root s'.root (Store.get_quota s) (Store.get_quo= ta s') + + let map_of_domains d =3D map_of_domid_table d.Domains.table + + let domain_equiv d d' =3D + Domain.get_id d =3D Domain.get_id d' + && Domain.get_remote_port d =3D Domain.get_remote_port d' + + let domains_equiv d d' =3D + DomidMap.equal domain_equiv (map_of_domains d) (map_of_domains d') + + let map_of_fd_table tbl =3D Hashtbl.fold FDMap.add tbl FDMap.empty + + let map_of_int_table tbl =3D Hashtbl.fold IntMap.add tbl IntMap.empty + + let list_of_queue q =3D Queue.fold (fun acc e -> e :: acc) [] q + + let connection_equiv c c' =3D + let l =3D list_of_queue c.Connection.xb.pkt_out in + let l' =3D list_of_queue c'.Connection.xb.pkt_out in + if List.length l <> List.length l' || List.exists2 ( <> ) l l' then ( + let print_packets l =3D + l + |> List.rev_map (fun p -> + let tid, rid, ty, data =3D Xenbus.Packet.unpack p in + let tystr =3D Xenbus.Xb.Op.to_string ty in + Printf.sprintf "tid=3D%d, rid=3D%d, ty=3D%s, data=3D%s" tid= rid tystr + (String.escaped data)) + |> String.concat "\n" + in + let r =3D print_packets l in + let r' =3D print_packets l' in + Test.fail_reportf "Replies not equal:\n%s\n <>\n %s" r r' ) + else + let n =3D Connection.number_of_transactions c in + let n' =3D Connection.number_of_transactions c' in + if n <> n' then Test.fail_reportf "TX count mismatch: %d <> %d" n n' + else true + + let connections_equiv c c' =3D + FDMap.equal connection_equiv + (map_of_fd_table c.Connections.anonymous) + (map_of_fd_table c'.Connections.anonymous) + && IntMap.equal connection_equiv + (map_of_int_table c.Connections.domains) + (map_of_int_table c'.Connections.domains) + + let dump_load s =3D + let tmp =3D Filename.temp_file "xenstored" "qcheck.dump" in + finally + (fun () -> + let fds =3D {Xenstored.DB.rw_sock=3D None; ro_sock=3D None} in + Xenstored.DB.to_file fds !s.Command.store !s.Command.cons tmp ; + s :=3D new_state () ; + let _fds', errors =3D + Xenstored.DB.from_file ~live:true !s.Command.store !s.Command.do= ms + !s.Command.cons tmp + in + if errors > 0 then + Test.fail_reportf "Errors during live update: %d" errors) + (fun () -> Sys.remove tmp) + + let run_cmd cmd state sut =3D + ( match state.failure with + | None -> + true + | Some (e, bt) -> + Test.fail_reportf "Exception %s, backtrace: %s" (Printexc.to_strin= g e) + bt ) + && + match cmd with + | New -> + sut :=3D next_state cmd !sut ; + true + | Cmd (0, _, Command.LiveUpdate) -> + let s =3D !sut.cmdstate in + let store1 =3D Store.copy !(Option.get s).store in + let doms1 =3D !(Option.get s).doms in + dump_load (Option.get s) ; + (* reply is expected not to be equivalent, since after live update= we got an empty reply queue, + so don't compare connections + *) + store_equiv store1 !(Option.get s).store + && domains_equiv doms1 !(Option.get s).doms + | Cmd(_, _, cmd') -> ( + (* TODO: also got same reply, and check for equivalence on the act= ual Live Update *) + sut :=3D next_state cmd !sut ; + let ids =3D Hashtbl.create 47 in + Connections.iter !(Option.get state.cmdstate).cons (fun con -> + Hashtbl.add ids (Connection.get_id con) con.next_tid) ; + let state =3D next_state cmd state in + match (!sut.failure, state.cmdstate, !sut.cmdstate) with + | None, Some s, Some s' -> + let r =3D cmd' =3D Command.Transaction_start (* txid can chang= e *) ||=20 + connections_equiv !s.cons !s'.cons in + Connections.iter !(Option.get state.cmdstate).cons (fun con -> + let tid =3D Hashtbl.find ids (Connection.get_id con) in + if con.next_tid <> tid then ( + let (_ : bool) =3D Connection.end_transaction con tid No= ne in + () ; + con.next_tid <- tid )) ; + r + | None, None, None -> + true + | None, None, _ -> + Test.fail_report "state uninit" + | None, _, None -> + Test.fail_report "sut uninit" + | Some (e, bt), _, _ -> + Test.fail_reportf "Exception %s, backtrace: %s" + (Printexc.to_string e) bt ) +end + +module States =3D QCSTM.Make (Spec) + +(* && watches_equiv c c' *) + +let test =3D States.agree_test ~count:100 ~name:"live-update" + +let test =3D + Test.make ~name:"live-update" ~count:100 + (States.arb_cmds Spec.init_state) + States.agree_prop + +let () =3D QCheck_base_runner.run_tests_main [test] diff --git a/tools/ocaml/xenstored/test/pathtree.ml b/tools/ocaml/xenstored= /test/pathtree.ml new file mode 100644 index 0000000000..50cbb0302d --- /dev/null +++ b/tools/ocaml/xenstored/test/pathtree.ml @@ -0,0 +1,40 @@ +module M =3D Map.Make(String) +type 'a t =3D { data: 'a; children: 'a t M.t } + +type 'a tree =3D 'a t +let of_data data =3D { data; children =3D M.empty } + +let update key f t =3D { t with children =3D M.update key f t.children } +let set t data =3D { t with data } + +module Cursor =3D struct + type 'a t =3D { tree: 'a tree; up: ('a t * M.key) option } + + let of_tree tree =3D { tree; up =3D None } + + let create parent key tree =3D { tree; up =3D Some (parent, key) } + + let down cur k =3D + M.find_opt k cur.tree.children |> Option.map @@ create cur k + + let down_implicit_create ~implicit cur k =3D + match down cur k with + | Some r -> r + | None -> cur.tree.data |> implicit |> of_data |> create cur k + + let rec to_tree t =3D match t.up with + | None -> t.tree + | Some (parent, key) -> + to_tree { parent with tree =3D update key (fun _ -> Some t.tree) p= arent.tree } + + let set cur data =3D { cur with tree =3D set cur.tree data } + let get cur =3D cur.tree.data + + let rm_child cur key =3D { cur with tree =3D update key (fun _ -> None) = cur.tree} + + (* TODO: down with implicit create *) +end + + + +let rec map f t =3D { data =3D f t.data; children =3D M.map (map f) t.chil= dren } diff --git a/tools/ocaml/xenstored/test/testable.ml b/tools/ocaml/xenstored= /test/testable.ml new file mode 100644 index 0000000000..ec50b10391 --- /dev/null +++ b/tools/ocaml/xenstored/test/testable.ml @@ -0,0 +1,364 @@ +let is_output_devnull =3D Unix.stat "/dev/null" =3D Unix.fstat Unix.stdout + +let () =3D + if not is_output_devnull then ( + Printexc.record_backtrace true ; + Fmt_tty.setup_std_outputs () ; + try + let cols =3D + let ch =3D Unix.open_process_in "tput cols" in + Stdext.finally + (fun () -> input_line ch |> int_of_string) + (fun () -> Unix.close_process_in ch) + in + Format.set_margin cols + with _ -> () ) + +let devnull () =3D Unix.openfile "/dev/null" [] 0 + +let xb =3D Xenbus.Xb.open_fd (devnull ()) + +module Command =3D struct + type path =3D Store.Path.t + + type value =3D string + + type token =3D string + + type domid =3D int + + type t =3D Xenbus.Packet.t + + open Xenstore.Queueop + + let cmd f =3D + Queue.clear xb.pkt_out ; + let () =3D f xb in + let p =3D Xenbus.Xb.peek_output xb in + Queue.clear xb.pkt_out ; p + + let pathcmd f pathgen tid state =3D cmd @@ f tid @@ pathgen state + + let cmd_read gen tid state =3D pathcmd read gen tid state + + let cmd_write pathgen v tid state =3D cmd @@ write tid (pathgen state) v + + let cmd_mkdir g t s =3D pathcmd mkdir g t s + + let cmd_rm g t s =3D pathcmd rm g t s + + let cmd_directory g t s =3D pathcmd directory g t s + + let cmd_getperms g t s =3D pathcmd getperms g t s + + let cmd_setperms pathgen vgen tid state =3D + cmd @@ setperms tid (pathgen state) (Perms.Node.to_string @@ vgen stat= e) + + let cmd_watch pathgen token _ state =3D cmd @@ watch (pathgen state) tok= en + + let cmd_unwatch pathgen token _ state =3D cmd @@ unwatch (pathgen state)= token + + let cmd_reset_watches tid _state =3D + let open Xenbus in + cmd + @@ fun con -> + Xenbus.Xb.queue con + (Xenbus.Xb.Packet.create 0 0 Xenbus.Xb.Op.Reset_watches "") + + let cmd_transaction_start _ _ =3D cmd @@ transaction_start + + let cmd_transaction_end commit tid _ =3D cmd @@ transaction_end tid comm= it + + let domcmd f idgen _ state =3D cmd @@ f @@ idgen state + + let cmd_release idgen state =3D domcmd release idgen state + + let cmd_getdomainpath i s =3D domcmd getdomainpath i s + + let cmd_isintroduced i t s =3D + domcmd + (fun d con -> + let open Xenbus in + Xenbus.Xb.queue con + (Xenbus.Xb.Packet.create 0 0 Xenbus.Xb.Op.Isintroduced + (string_of_int d))) + i t s + + let cmd_set_target idgen1 idgen2 _ state =3D + let d =3D idgen1 state in + let t =3D idgen2 state in + cmd + @@ fun con -> + Xenbus.Xb.queue con + (Xenbus.Xb.Packet.create 0 0 Xenbus.Xb.Op.Isintroduced + (String.concat "\x00" [string_of_int d; string_of_int t])) + + let cmd_liveupdate _ _ =3D cmd @@ debug ["live-update"; "-s"] + + let cmd_introduce id port _ state =3D cmd @@ introduce id 0n port + + let pp_dump =3D Types.pp_dump_packet + + let precond cmd _state =3D + match cmd with + | {Xenbus.Packet.ty=3D Xenbus.Xb.Op.Release; data=3D "0\000"} -> + false + (* can't release Dom0 in the tests, or we crash due to shared dom0= backend *) + | {ty=3D Xenbus.Xb.Op.Rm; data=3D ""} -> + (* this is expected to cause inconsistencies on pre-created paths = like /local *) + false + | _ -> + true +end + +let with_logger ~on_exn f =3D + if is_output_devnull then f () + else + let old =3D (!Logging.xenstored_logger, !Logging.access_logger) in + let logs =3D ref [] in + let write ?(level =3D Logging.Debug) s =3D + let msg =3D Printf.sprintf "%s %s" (Logging.string_of_level level) s= in + logs :=3D msg :: !logs + in + let logger =3D + Some {Logging.stop=3D ignore; restart=3D ignore; rotate=3D ignore; w= rite} + in + Logging.xenstored_logger :=3D logger ; + Logging.access_logger :=3D logger ; + Stdext.finally + (fun () -> + try f () + with e -> + let bt =3D Printexc.get_raw_backtrace () in + on_exn e bt (List.rev !logs)) + (fun () -> + Logging.xenstored_logger :=3D fst old ; + Logging.access_logger :=3D snd old) + +type t =3D + { store: Store.t + ; cons: Connections.t + ; doms: Domains.domains + ; mutable anon: Unix.file_descr option + ; live_update: bool + ; txidtbl: (int, int) Hashtbl.t } + +let () =3D + Logging.xenstored_log_level :=3D Logging.Debug ; + Logging.access_log_special_ops :=3D true ; + Logging.access_log_transaction_ops :=3D true ; + let name, f =3D Filename.open_temp_file "xenstored" "port" in + Domains.xenstored_port :=3D name ; + Stdext.finally (fun () -> Printf.fprintf f "%d" 1) (fun () -> close_out = f) ; + Domains.xenstored_kva :=3D "/dev/zero" ; + (* entries from a typical oxenstored.conf *) + Transaction.do_coalesce :=3D true ; + Perms.activate :=3D true ; + Quota.activate :=3D true ; + Quota.maxent :=3D 8192 ; + Quota.maxsize :=3D 2048 ; + Define.maxwatch :=3D 512 ; + Define.maxtransaction :=3D 10 ; + Define.maxrequests :=3D 1024 + +(* we MUST NOT release dom0, or we crash, + this is shared between multiple tests, because + it keeps an FD open, and we want to avoid EMFILE +*) + +let create ?(live_update =3D false) () =3D + let store =3D Store.create () in + let cons =3D Connections.create () in + let doms =3D Domains.init (Event.init ()) ignore in + let dom0 =3D Domains.create0 doms in + let txidtbl =3D Hashtbl.create 47 in + Connections.add_domain cons dom0 ; + {store; cons; doms; anon=3D None; live_update; txidtbl} + +let cleanup t =3D Connections.iter t.cons Connection.close + +let init t =3D + let local =3D Store.Path.of_string "/local" in + let con =3D Perms.Connection.create 0 in + Store.mkdir t.store con local ; + Store.mkdir t.store con (Store.Path.of_string "/tool") ; + let fd =3D devnull () in + t.anon <- Some fd ; + Connections.add_anonymous t.cons fd + +let dump_load s =3D + let tmp =3D Filename.temp_file "xenstored" "qcheck.dump" in + Stdext.finally + (fun () -> + Xenstored.DB.to_file None s.store s.cons tmp ; + let s' =3D create () in + (* preserve FD *) + s'.anon <- s.anon ; + s.anon <- None ; + let _fds', errors =3D + Xenstored.DB.from_file ~live:true s'.store s'.doms s'.cons tmp + in + if errors > 0 then + failwith (Printf.sprintf "Errors during live update: %d" errors) ; + s') + (fun () -> Sys.remove tmp) + +let is_live_update =3D function + | {Xenbus.Packet.ty=3D Xenbus.Xb.Op.Debug; data=3D "live-update\000-s\00= 0"} -> + true + | _ -> + false + +let is_tx_start p =3D p.Xenbus.Packet.ty =3D Xenbus.Xb.Op.Transaction_start + +let with_tmpfile prefix write f =3D + let name, ch =3D Filename.open_temp_file prefix ".txt" in + Stdext.finally + (fun () -> + Stdext.finally (fun () -> write ch) (fun () -> close_out ch) ; + f name) + (fun () -> Sys.remove name) + +let with_pp_to_file prefix pp x f =3D + let write ch =3D + let ppf =3D Format.formatter_of_out_channel ch in + Format.pp_set_margin ppf @@ Format.get_margin () ; + pp ppf x ; + Fmt.flush ppf () + in + with_tmpfile prefix write f + +let run_cmd_get_output ?(ok_codes =3D [0]) cmd =3D + let cmd =3D Array.of_list cmd in + let ch =3D Unix.open_process_args_in cmd.(0) cmd in + Stdext.finally + (fun () -> + let lines =3D ref [] in + try + while true do + lines :=3D input_line ch :: !lines + done ; + assert false + with End_of_file -> List.rev !lines |> String.concat "\n") + (fun () -> + match Unix.close_process_in ch with + | Unix.WEXITED code when List.mem code ok_codes -> + () + | status -> + Crowbar.failf "%a %a" (Fmt.array Fmt.string) cmd + Types.pp_process_status status) + +let call_diff x y =3D + let ok_codes =3D [0; 1] in + run_cmd_get_output ~ok_codes + [ "/usr/bin/git" + ; "diff" + ; "-U10000" (* we want to see the entire state, where possible *) + ; "--no-index" + ; ( "--word-diff=3D" + ^ if Fmt.style_renderer Fmt.stdout =3D `Ansi_tty then "color" else "= plain" + ) + ; "--color-moved=3Ddimmed-zebra" + ; x + ; y ] + +let check_eq_exn prefix ~pp ~eq x y =3D + if not @@ eq x y then + if is_output_devnull then failwith "different" + else + with_pp_to_file "expected" pp x + @@ fun xfile -> + with_pp_to_file "actual" pp y + @@ fun yfile -> + failwith + @@ Printf.sprintf "%s agrement: %s" prefix (call_diff xfile yfile) + +let run next_tid t (domid, cmd) =3D + let con =3D + match domid with + | 0 -> + Connections.find !t.cons (Option.get !t.anon) + | id -> + Connections.find_domain !t.cons domid + in + (* clear out any watch events, TODO: don't *) + Connections.iter !t.cons (fun con -> Queue.clear con.xb.pkt_out) ; + (* TODO: use the global live update state that processing the command se= ts, but remember to reset it *) + if is_live_update cmd then + if !t.live_update then ( + let t0 =3D !t in + let t' =3D dump_load t0 in + Connections.iter t0.cons (fun con -> + Connection.iter_transactions con + @@ fun _ tx -> + if tx.Transaction.operations <> [] then + Transaction.mark_failed tx) ; + check_eq_exn "store" ~pp:Types.pp_dump_store ~eq:Types.equal_store + t0.store t'.store ; + check_eq_exn "connections" ~pp:Types.pp_dump_connections + ~eq:Types.equal_connections t0.cons t'.cons ; + check_eq_exn "domains" ~pp:Types.pp_dump_domains ~eq:Types.equal_dom= ains + t0.doms t'.doms ; + (* avoid double close on anonymous conn *) + Connections.iter_domains t0.cons Connection.close ; + t :=3D {t' with txidtbl=3D !t.txidtbl} ) + else begin + Logging.debug "testable" "BEFORE TXMARK"; + Connections.iter !t.cons (fun con -> + Connection.iter_transactions con + @@ fun txid tx -> + Logging.debug "testable" "marking to fail %d" txid;=20 + if tx.Transaction.operations <> [] then + Transaction.mark_failed tx)=20 + end; + let run_packet packet =3D + let tid, rid, ty, data =3D Xenbus.Xb.Packet.unpack packet in + Logging.debug "testable" "tid: %d" tid ; + let tid =3D if tid <> 0 then Hashtbl.find !t.txidtbl tid else tid in + let req : Packet.request =3D + {Packet.tid; Packet.rid; Packet.ty; Packet.data} + in + Process.process_packet ~store:!t.store ~cons:!t.cons ~doms:!t.doms ~co= n ~req ; + Process.write_access_log ~ty ~tid ~con:(Connection.get_domstr con) ~da= ta ; + let packet =3D Connection.peek_output con in + if ty =3D Xenbus.Xb.Op.Transaction_start then ( + Logging.debug "testable" "Adding mapping for tid %d" next_tid ; + Hashtbl.add !t.txidtbl next_tid (con.Connection.next_tid - 1) ) ; + let tid, _rid, ty, data =3D Xenbus.Xb.Packet.unpack packet in + Process.write_answer_log ~ty ~tid ~con:(Connection.get_domstr con) ~da= ta + in + (* TODO: also a Nodes command with multiple packets *) + run_packet cmd ; (* TODO: act on and clear watches? *) + con + +let run2 next_tid t t' (domid, cmd) =3D + let con =3D run next_tid t (domid, cmd) in + let con' =3D run next_tid t' (domid, cmd) in + (* TODO: ignore txid mismatches on transactions *) + if not @@ is_tx_start cmd then + check_eq_exn "reply packets" ~pp:Types.pp_dump_xb ~eq:Types.equal_xb_p= kts + con.xb con'.xb ; + Queue.clear con'.xb.pkt_out ; + Queue.clear con.xb.pkt_out + +module type S =3D sig + type cmd + + type state + + type sut + + val init_state : state + + val next_state : cmd -> state -> state + + val init_sut : unit -> sut + + val cleanup : sut -> unit + + val run_cmd : cmd -> state -> sut -> bool + + val precond : cmd -> state -> bool + + val pp : cmd Fmt.t +end diff --git a/tools/ocaml/xenstored/test/types.ml b/tools/ocaml/xenstored/te= st/types.ml new file mode 100644 index 0000000000..a85168cbcf --- /dev/null +++ b/tools/ocaml/xenstored/test/types.ml @@ -0,0 +1,427 @@ +(* + * Copyright (C) Citrix Systems Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + *) + +let domid_first_reserved =3D 0x7FF0 + +type 'a eq =3D 'a -> 'a -> bool + +let hashtable_equal (eq : 'a eq) h h' =3D + Hashtbl.length h =3D Hashtbl.length h' + && Hashtbl.fold + (fun k v acc -> + acc + && match Hashtbl.find_opt h' k with Some x -> eq v x | None -> fa= lse) + h true + +let list_equal (eq : 'a eq) l l' =3D + try List.for_all2 eq l l' with Invalid_argument _ -> false + +let queue_equal eq q q' =3D + Queue.length q =3D Queue.length q' + && + let list_of_queue q =3D Queue.fold (fun acc e -> e :: acc) [] q in + list_equal eq (list_of_queue q) (list_of_queue q') + +let pp_process_status ppf =3D function + | Unix.WEXITED code -> + Fmt.pf ppf "exited with code %d" code + | Unix.WSIGNALED osig -> + Fmt.pf ppf "killed by signal %a" Fmt.Dump.signal osig + | Unix.WSTOPPED osig -> + Fmt.pf ppf "stopped by signal %a" Fmt.Dump.signal osig + +let pp_dump_ref dump =3D + Fmt.using ( ! ) Fmt.(dump |> Fmt.braces |> prefix (const string "ref")) + +let pp_file_descr =3D Fmt.using Disk.FD.to_int Fmt.int + +module Quota =3D struct + open Quota + + let pp_dump =3D + let open Fmt in + Dump.record + [ Dump.field "maxent" (fun q -> q.maxent) int + ; Dump.field "maxsize" (fun q -> q.maxsize) int + ; Dump.field "cur" (fun q -> q.cur) @@ Dump.hashtbl int int ] + + let equal q q' =3D + q.maxent =3D q'.maxent && q.maxsize =3D q'.maxsize + && hashtable_equal Int.equal q.cur q'.cur +end +let pp_dump_quota =3D Quota.pp_dump +let equal_quota =3D Quota.equal + +module Store =3D struct + open Store + + module Node =3D struct + open Node + + let pp_dump ppf t =3D + let buf =3D dump_store_buf t in + Fmt.lines ppf (Buffer.contents buf) + + let rec equal n n' =3D + Symbol.equal n.name n'.name + && Perms.equiv n.perms n'.perms + && String.equal n.value n'.value + && SymbolMap.equal equal n.children n'.children + end + + module Path =3D struct + open Path + + let pp_dump =3D Fmt.using to_string Fmt.string + + let equal p p' =3D list_equal String.equal p p' + + let hash (p : t) =3D Hashtbl.hash p + + let compare (p : t) (p' : t) =3D compare p p' + end + + let pp_dump =3D + let open Fmt in + (* only print relevant fields, expected to stay same during live-updat= e. *) + Dump.record + [ Dump.field "stat_transaction_coalesce" + (fun t -> t.stat_transaction_coalesce) + int + ; Dump.field "stat_transaction_abort" + (fun t -> t.stat_transaction_coalesce) + int + ; Dump.field "store" (fun t -> t.root) Node.pp_dump + ; Dump.field "quota" (fun t -> t.quota) Quota.pp_dump ] + + let equal s s' =3D + (* ignore stats *) + Node.equal s.root s'.root && Quota.equal s.quota s'.quota +end + +let pp_dump_store =3D Store.pp_dump +let equal_store =3D Store.equal + +module Xb =3D struct + open Xenbus.Xb + + module Op =3D struct + open Xenbus.Op + + let pp_dump =3D Fmt.of_to_string to_string + + let equal (op : t) (op' : t) =3D op =3D op' + end + + module Packet =3D struct + open Xenbus.Packet + + let pp_dump =3D + let open Fmt in + Dump.record + [ Dump.field "tid" get_tid int + ; Dump.field "rid" get_rid int + ; Dump.field "ty" get_ty Op.pp_dump + ; Dump.field "data" get_data Dump.string ] + + let equal (p : t) (p' : t) =3D + (* ignore TXID, it can be different after a live-update *) + p.rid =3D p'.rid && p.ty =3D p'.ty && p.data =3D p'.data + end + + module Partial =3D struct + open Xenbus.Partial + + let pp_dump =3D + let open Fmt in + Dump.record + [ Dump.field "tid" (fun p -> p.tid) int + ; Dump.field "rid" (fun p -> p.rid) int + ; Dump.field "ty" (fun p -> p.ty) Op.pp_dump + ; Dump.field "len" (fun p -> p.len) int + ; Dump.field "buf" (fun p -> p.buf) Fmt.buffer ] + + let equal p p' =3D + p.tid =3D p'.tid && p.rid =3D p'.rid && p.ty =3D p'.ty + && Buffer.contents p.buf =3D Buffer.contents p'.buf + end + + let pp_dump_partial_buf ppf =3D function + | HaveHdr pkt -> + Fmt.pf ppf "HaveHdr %a" Partial.pp_dump pkt + | NoHdr (i, b) -> + Fmt.pf ppf "NoHdr(%d, %S)" i (Bytes.to_string b) + + let equal_partial_buf buf buf' =3D + match (buf, buf') with + | HaveHdr pkt, HaveHdr pkt' -> + Partial.equal pkt pkt' + | NoHdr (i, b), NoHdr (i', b') -> + i =3D i' && b =3D b' + | HaveHdr _, NoHdr _ | NoHdr _, HaveHdr _ -> + false + + let pp_backend ppf =3D function + | Fd {fd} -> + Fmt.prefix (Fmt.const Fmt.string "Fd ") pp_file_descr ppf fd + | Xenmmap _ -> + Fmt.const Fmt.string "Xenmmap _" ppf () + + let equal_backend b b' =3D + match (b, b') with + | Fd fd, Fd fd' -> + fd =3D fd' + | Xenmmap _, Xenmmap _ -> + true (* can't extract the FD to compare *) + | Fd _, Xenmmap _ | Xenmmap _, Fd _ -> + false + + let pp_dump =3D + let open Fmt in + Dump.record + [ Dump.field "backend" (fun x -> x.backend) pp_backend + ; Dump.field "pkt_in" (fun x -> x.pkt_in) @@ Dump.queue Packet.pp_du= mp + ; Dump.field "pkt_out" (fun x -> x.pkt_out) @@ Dump.queue Packet.pp_= dump + ; Dump.field "partial_in" (fun x -> x.partial_in) pp_dump_partial_buf + ; Dump.field "partial_out" (fun x -> x.partial_out) Dump.string ] + + let equal_pkts xb xb' =3D + let queue_eq =3D queue_equal Packet.equal in + queue_eq xb.pkt_in xb'.pkt_in + && queue_eq xb.pkt_out xb'.pkt_out + && xb.partial_in =3D xb'.partial_in + && xb.partial_out =3D xb'.partial_out + + let equal xb xb' =3D equal_backend xb.backend xb'.backend && equal_pkts = xb xb' +end + +let pp_dump_packet =3D Xb.Packet.pp_dump +let pp_dump_xb =3D Xb.pp_dump +let equal_xb =3D Xb.equal +let equal_xb_pkts =3D Xb.equal_pkts + +module Packet =3D struct + open Packet + + let pp_dump_request =3D + let open Fmt in + Dump.record + [ Dump.field "tid" (fun t -> t.tid) int + ; Dump.field "rid" (fun t -> t.rid) int + ; Dump.field "ty" (fun t -> t.ty) Xb.Op.pp_dump + ; Dump.field "data" (fun t -> t.data) Dump.string ] + + let equal_req r r' =3D + r.tid =3D r'.tid && r.rid =3D r'.rid && r.ty =3D r'.ty && r.data =3D r= '.data + + let pp_dump_response ppf =3D function + | Reply str -> + Fmt.pf ppf "Reply %S" str + | Error str -> + Fmt.pf ppf "Error %S" str + | Ack _ -> + Fmt.string ppf "Ack" + + let equal_response =3D response_equal +end + +module Transaction =3D struct + open Transaction + + let pp_dump_ty ppf =3D function + | Transaction.No -> + Fmt.string ppf "No" + | Full (id, orig, canonical) -> + Fmt.pf ppf "Full @[(%d, %a, %a)@]" id Store.pp_dump orig Store.pp_= dump + canonical + + let equal_ty t t' =3D + match (t, t') with + | Transaction.No, Transaction.No -> + true + | Transaction.Full _, Transaction.Full _ -> + (* We expect the trees not to be identical, so we ignore any diffe= rences here. + The reply comparison tests will find any mismatches in observab= le transaction state + *) + true + | Transaction.No, Transaction.Full _ | Transaction.Full _, Transaction= .No -> + false + + let equal_pathop (op, path) (op', path') =3D + op =3D op' && Store.Path.equal path path' + + let pp_dump_op =3D Fmt.pair Packet.pp_dump_request Packet.pp_dump_respon= se + + let equal_op (req, reply) (req', reply') =3D + Packet.equal_req req req' && Packet.equal_response reply reply' + + let pp_dump =3D + let open Fmt in + let open Transaction in + Dump.record + [ Dump.field "ty" (fun t -> t.ty) pp_dump_ty + ; Dump.field "start_count" (fun t -> t.start_count) int64 + ; Dump.field "store" (fun t -> t.store) Store.pp_dump + ; Dump.field "quota" (fun t -> t.quota) Quota.pp_dump + ; Dump.field "must_fail" (fun t -> t.must_fail) Fmt.bool + ; Dump.field "paths" (fun t -> t.paths) + @@ Dump.list (pair Xb.Op.pp_dump Store.Path.pp_dump) + ; Dump.field "operations" (fun t -> t.operations) + @@ list (pair Packet.pp_dump_request Packet.pp_dump_response) + ; Dump.field "read_lowpath" (fun t -> t.read_lowpath) + @@ option Store.Path.pp_dump + ; Dump.field "write_lowpath" (fun t -> t.write_lowpath) + @@ option Store.Path.pp_dump ] + + let equal t t' =3D + equal_ty t.ty t'.ty + (* ignored: quota at start of transaction, not relevant + && Quota.equal t.quota t'.quota *) + (*&& list_equal equal_pathop t.paths t'.paths *) + (*&& list_equal equal_op t.operations t'.operations*) + && t.must_fail =3D t'.must_fail + (* ignore lowpath, impossible to recreate from limited migration info = *) + (*&& Option.equal Store.Path.equal t.read_lowpath t'.read_lowpath + && Option.equal Store.Path.equal t.write_lowpath t'.write_lowpath *) +end + +module Connection =3D struct + open Connection + + let pp_dump_watch =3D + let open Fmt in + Dump.record + [ Dump.field "token" (fun w -> w.token) Dump.string + ; Dump.field "path" (fun w -> w.path) Dump.string + ; Dump.field "base" (fun w -> w.base) Dump.string + ; Dump.field "is_relative" (fun w -> w.is_relative) Fmt.bool ] + + let pp_dump =3D + let open Fmt in + Dump.record + [ Dump.field "xb" (fun c -> c.xb) Xb.pp_dump + ; Dump.field "transactions" (fun c -> c.transactions) + @@ Dump.hashtbl int Transaction.pp_dump + ; Dump.field "next_tid" (fun t -> t.next_tid) int + ; Dump.field "nb_watches" (fun c -> c.nb_watches) int + ; Dump.field "anonid" (fun c -> c.anonid) int + ; Dump.field "watches" (fun c -> c.watches) + @@ Dump.hashtbl Dump.string (Dump.list pp_dump_watch) + ; Dump.field "perm" (fun c -> c.perm) + @@ Fmt.using Perms.Connection.to_string Fmt.string ] + + let equal c c' =3D + let watch_equal w w' =3D + (* avoid recursion, these must be physically equal *) + w.con =3D=3D c && w'.con =3D=3D c' && w.token =3D w'.token && w.path= =3D w'.path + && w.base =3D w'.base + && w.is_relative =3D w'.is_relative + in + Xb.equal c.xb c'.xb + && hashtable_equal Transaction.equal c.transactions c'.transactions + (* next_tid ignored, not preserved *) + && hashtable_equal (list_equal watch_equal) c.watches c'.watches + && c.nb_watches =3D c'.nb_watches + (* anonid ignored, not preserved *) + (* && c.anonid =3D c'.anonid *) && c.perm =3D c'.perm + + let equal_watch w w' =3D + equal w.con w'.con && w.token =3D w'.token && w.path =3D w'.path + && w.base =3D w'.base + && w.is_relative =3D w'.is_relative +end + +module Trie =3D struct + open Trie + + let pp_dump dump_elt =3D + Fmt.Dump.iter_bindings Trie.iter (Fmt.any "trie") Fmt.string + Fmt.(option dump_elt) + + let plus1 _ _ acc =3D acc + 1 + + let length t =3D fold plus1 t 0 + + (* Trie.iter doesn't give full path so we can't compare the paths/values= exactly. + They will be compared as part of the individual connections + *) + let equal _eq t t' =3D length t =3D length t' +end + +module Connections =3D struct + open Connections + + let pp_dump =3D + let open Fmt in + Dump.record + [ Dump.field "anonymous" (fun t -> t.anonymous) + @@ Dump.hashtbl Fmt.(any "") Connection.pp_dump + ; Dump.field "domains" (fun t -> t.domains) + @@ Dump.hashtbl Fmt.int Connection.pp_dump + ; Dump.field "ports" (fun t -> t.ports) + @@ Dump.hashtbl + (Fmt.using Xeneventchn.to_int Fmt.int) + Connection.pp_dump + ; Dump.field "watches" (fun t -> t.watches) + @@ Trie.pp_dump (Dump.list Connection.pp_dump_watch) ] + + let equal c c' =3D + hashtable_equal Connection.equal c.anonymous c'.anonymous + && hashtable_equal Connection.equal c.domains c'.domains + (* TODO: local port changes for now *) + (*&& hashtable_equal Connection.equal c.ports c'.ports *) + && Trie.equal (list_equal Connection.equal_watch) c.watches c'.watches +end + +let pp_dump_connections =3D Connections.pp_dump +let equal_connections =3D Connections.equal + +module Domain =3D struct + open Domain + + let pp_dump =3D + let open Fmt in + Dump.record + [ Dump.field "id" Domain.get_id int + ; Dump.field "remote_port" Domain.get_remote_port int + ; Dump.field "bad_client" Domain.is_bad_domain bool + ; Dump.field "io_credit" Domain.get_io_credit int + ; Dump.field "conflict_credit" (fun t -> t.conflict_credit) float + ; Dump.field "caused_conflicts" (fun t -> t.caused_conflicts) int64 ] + + (* ignore stats fields *) + let equal t t' =3D t.id =3D t'.id && t.remote_port =3D t'.remote_port +end + +module Domains =3D struct + open Domains + + let pp_dump =3D + let open Fmt in + Dump.record + [ Dump.field "table" (fun t -> t.table) + @@ Dump.hashtbl Fmt.int Domain.pp_dump + ; Dump.field "doms_conflict_paused" (fun t -> t.doms_conflict_paused) + @@ Dump.queue (pp_dump_ref @@ Dump.option Domain.pp_dump) + ; Dump.field "doms_with_conflict_penalty" (fun t -> + t.doms_with_conflict_penalty) + @@ Dump.queue (pp_dump_ref @@ Dump.option Domain.pp_dump) + ; Dump.field "n_paused" (fun t -> t.n_paused) int + ; Dump.field "n_penalised" (fun t -> t.n_penalised) int ] + + (* ignore statistic fields *) + let equal t t' =3D hashtable_equal Domain.equal t.table t'.table +end +let pp_dump_domains =3D Domains.pp_dump +let equal_domains =3D Domains.equal \ No newline at end of file diff --git a/tools/ocaml/xenstored/test/xenstored_test.ml b/tools/ocaml/xen= stored/test/xenstored_test.ml index e86b68e867..acf3209087 100644 --- a/tools/ocaml/xenstored/test/xenstored_test.ml +++ b/tools/ocaml/xenstored/test/xenstored_test.ml @@ -1,2 +1,147 @@ -open Xenstored -let () =3D () +open Testable +open Generator +module Cb =3D Crowbar + +let random_path =3D Cb.list Cb.bytes + +let value =3D Cb.bytes + +let token =3D Cb.bytes + +let permty =3D + [Perms.READ; Perms.WRITE; Perms.RDWR; Perms.NONE] + |> List.map Cb.const |> Cb.choose + +let new_domid =3D Cb.range ~min:1 Types.domid_first_reserved + +let port =3D Cb.range 0xFFFF_FFFF (*uint32_t*) + +let arb_cmd =3D + let open Command in + let path =3D + Cb.choose + [ Cb.map [Cb.int] (fun rnd state -> PathObserver.choose_path state r= nd) + ; Cb.map [random_path] (fun x _ -> Store.Path.to_string x) ] + in + let domid =3D + Cb.map [Cb.int] (fun rnd state -> PathObserver.choose_domid state rnd) + in + let perms =3D + Cb.map [domid; permty; Cb.pair domid permty |> Cb.list] + @@ fun idgen owner other state -> + let other =3D List.map (fun (idgen, ty) -> (idgen state, ty)) other in + Perms.Node.create (idgen state) owner other + in + let guard' ~f gen state =3D + let v =3D gen state in + Cb.guard (f v) ; + v + in + let cmd =3D + let open Testable.Command in + Cb.choose + [ Cb.map [path] cmd_read + ; Cb.map [path; value] cmd_write + ; Cb.map [path] cmd_mkdir + ; Cb.map [path] (fun p -> cmd_rm @@ guard' ~f:(fun p -> p <> "/") p) + ; Cb.map [path] cmd_directory + ; Cb.map [path] cmd_getperms + ; Cb.map [path; perms] cmd_setperms + ; Cb.map [path; token] cmd_watch + ; Cb.map [path; token] cmd_unwatch + ; Cb.const cmd_reset_watches + ; Cb.const cmd_transaction_start + ; Cb.map [Cb.bool] cmd_transaction_end + ; Cb.map [new_domid; port] cmd_introduce + ; Cb.map [domid] (fun idgen -> + cmd_release @@ guard' ~f:(fun id -> id <> 0) idgen) + ; Cb.map [domid] cmd_getdomainpath + ; Cb.map [domid] cmd_isintroduced + ; Cb.map [domid; domid] cmd_set_target + ; Cb.const cmd_liveupdate ] + in + Cb.map [domid; Cb.int; cmd] (fun this rnd cmd state -> + let this =3D this state in + let txid =3D PathObserver.choose_txid_opt state this rnd in + let cmd =3D cmd txid state in + (this, cmd)) + +(* based on QCSTM *) +module Make (Spec : sig + include Testable.S + + val arb_cmd : (state -> cmd) Crowbar.gen +end) =3D +struct + let arb_cmds =3D + Crowbar.with_printer (Fmt.Dump.list Spec.pp) + @@ Crowbar.map [Crowbar.list1 Spec.arb_cmd] (fun cmdgens -> + let cmds, _ =3D + List.fold_left + (fun (cmds, s) f -> + let cmd =3D f s in + Crowbar.check (Spec.precond cmd s) ; + (cmd :: cmds, Spec.next_state cmd s)) + ([], Spec.init_state) cmdgens + in + List.rev cmds) + + let interp_agree sut cs =3D + List.fold_left + (fun s cmd -> + Crowbar.check + ( try Spec.run_cmd cmd s sut + with Failure msg -> Crowbar.failf "%a" Fmt.lines msg ) ; + Spec.next_state cmd s) + Spec.init_state cs + + let agree_prop cs =3D + let on_exn e bt logs =3D + List.iter prerr_endline logs ; + Printexc.raise_with_backtrace e bt + in + Testable.with_logger ~on_exn (fun () -> + let sut =3D Spec.init_sut () in + Stdext.finally (fun () ->=20 + let (_ : Spec.state) =3D interp_agree sut cs in ()) + (fun () ->=20 + Spec.cleanup sut)) + + let agree_test ~name =3D Crowbar.add_test ~name [arb_cmds] agree_prop +end + +module LU =3D Make (struct + include PathObserver + + type cmd =3D int * Testable.Command.t + + type sut =3D Testable.t ref * Testable.t ref + + let arb_cmd =3D arb_cmd + + let init_sut () =3D + let sut1 =3D Testable.create () in + Testable.init sut1 ; + let sut2 =3D Testable.create ~live_update:true () in + Testable.init sut2 ; + let sut1 =3D ref sut1 in + let sut2 =3D ref sut2 in + (sut1, sut2) + + let cleanup (sut1, sut2) =3D + Testable.cleanup !sut1 ; Testable.cleanup !sut2 + + let run_cmd cmd state (sut1, sut2) =3D + Testable.run2 state.next_tid sut1 sut2 cmd ; + true +end) + +let () =3D + (* Crowbar runs at_exit, and after bisect's coverage dumper, + registering an at_exit here would run *before* Crowbar starts, + hence the nested at_exit which puts the bisect dumper in the proper p= lace + to dump coverage *after* crowbar is finished. + *) + (* at_exit (fun () -> at_exit Bisect.Runtime.write_coverage_data);*) + print_endline ""; + LU.agree_test ~name:"live-update-agree"; diff --git a/tools/ocaml/xenstored/test/xs_protocol.ml b/tools/ocaml/xensto= red/test/xs_protocol.ml new file mode 100644 index 0000000000..b5da2aff34 --- /dev/null +++ b/tools/ocaml/xenstored/test/xs_protocol.ml @@ -0,0 +1,733 @@ +(* + * Copyright (C) Citrix Systems Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + *) + +let ( |> ) f g =3D g f +let ( ++ ) f g x =3D f (g x) + +module Op =3D struct + type t =3D + | Debug | Directory | Read | Getperms + | Watch | Unwatch | Transaction_start + | Transaction_end | Introduce | Release + | Getdomainpath | Write | Mkdir | Rm + | Setperms | Watchevent | Error | Isintroduced + | Resume | Set_target + | Reset_watches | Directory_part + + let to_int32 =3D function + | Debug -> 0l + | Directory -> 1l + | Read -> 2l + | Getperms -> 3l + | Watch -> 4l + | Unwatch -> 5l + | Transaction_start -> 6l + | Transaction_end -> 7l + | Introduce -> 8l + | Release -> 9l + | Getdomainpath -> 10l + | Write -> 11l + | Mkdir -> 12l + | Rm -> 13l + | Setperms -> 14l + | Watchevent -> 15l + | Error -> 16l + | Isintroduced -> 17l + | Resume -> 18l + | Set_target -> 19l + | Reset_watches -> 21l (* 20 is reserved *) + | Directory_part -> 22l + + (* The index of the value in the array is the integer representation used + by the wire protocol. Every element of t exists exactly once in the a= rray. *) + let on_the_wire =3D + let a =3D Array.make 23 None in + ListLabels.iter + ~f:(fun v -> a.(v |> to_int32 |> Int32.to_int) <- Some v) + [ Debug; Directory; Read; Getperms; Watch; Unwatch; Transaction_start + ; Transaction_end; Introduce; Release; Getdomainpath; Write; Mkdir; = Rm + ; Setperms; Watchevent; Error; Isintroduced; Resume; Set_target + ; Reset_watches; Directory_part ] ; + a + + let of_int32 i =3D + let i =3D Int32.to_int i in + if i >=3D 0 && i < Array.length on_the_wire then on_the_wire.(i) else = None + + let to_string =3D function + | Debug -> "debug" + | Directory -> "directory" + | Read -> "read" + | Getperms -> "getperms" + | Watch -> "watch" + | Unwatch -> "unwatch" + | Transaction_start -> "transaction_start" + | Transaction_end -> "transaction_end" + | Introduce -> "introduce" + | Release -> "release" + | Getdomainpath -> "getdomainpath" + | Write -> "write" + | Mkdir -> "mkdir" + | Rm -> "rm" + | Setperms -> "setperms" + | Watchevent -> "watchevent" + | Error -> "error" + | Isintroduced -> "isintroduced" + | Resume -> "resume" + | Set_target -> "set_target" + | Reset_watches -> "reset_watches" + | Directory_part -> "directory_part" +end + +let split_string ?limit:(limit=3Dmax_int) c s =3D + let len =3D String.length s in + let next_c from =3D + try + Some (String.index_from s from c) + with + | Not_found -> None + in + let decr n =3D max 0 (n-1) in + let rec loop n from acc =3D + match decr n, next_c from with + | 0, _ + | _, None -> + (* No further instances of c, or we've reached limit *) + String.sub s from (len - from) :: acc + | n', Some idx -> + let a =3D String.sub s from (idx - from) in + (loop[@tailcall]) n' (idx + 1) (a :: acc) + in loop limit 0 [] |> List.rev + + +module ACL =3D struct + type perm =3D + | NONE + | READ + | WRITE + | RDWR + + let char_of_perm =3D function + | READ -> 'r' + | WRITE -> 'w' + | RDWR -> 'b' + | NONE -> 'n' + + let perm_of_char =3D function + | 'r' -> Some READ + | 'w' -> Some WRITE + | 'b' -> Some RDWR + | 'n' -> Some NONE + | _ -> None + + type domid =3D int + + type t =3D { + owner: domid; (** domain which "owns", has full access *) + other: perm; (** default permissions for all others... *) + acl: (domid * perm) list; (** ... unless overridden in the ACL *) + } + + let to_string perms =3D + let string_of_perm (id, perm) =3D Printf.sprintf "%c%u" (char_of_perm = perm) id in + String.concat "\000" (List.map string_of_perm ((perms.owner,perms.othe= r) :: perms.acl)) + + let of_string s =3D + (* A perm is stored as 'domid' *) + let perm_of_char_exn x =3D match (perm_of_char x) with Some y -> y | N= one -> raise Not_found in + try + let perm_of_string s =3D + if String.length s < 2 + then invalid_arg (Printf.sprintf "Permission string too short: '%s= '" s); + int_of_string (String.sub s 1 (String.length s - 1)), perm_of_char= _exn s.[0] in + let l =3D List.map perm_of_string (split_string '\000' s) in + match l with + | (owner, other) :: l -> Some { owner =3D owner; other =3D other; ac= l =3D l } + | [] -> Some { owner =3D 0; other =3D NONE; acl =3D [] } + with _ -> + None +end + +type t =3D { + tid: int32; + rid: int32; + ty: Op.t; + len: int; + data: Buffer.t; +} + +let sizeof_header =3D 16 +let get_header_ty v =3D Cstruct.LE.get_uint32 v 0 +let set_header_ty v x =3D Cstruct.LE.set_uint32 v 0 x +let get_header_rid v =3D Cstruct.LE.get_uint32 v 4 +let set_header_rid v x =3D Cstruct.LE.set_uint32 v 4 x +let get_header_tid v =3D Cstruct.LE.get_uint32 v 8 +let set_header_tid v x =3D Cstruct.LE.set_uint32 v 8 x +let get_header_len v =3D Cstruct.LE.get_uint32 v 12 +let set_header_len v x =3D Cstruct.LE.set_uint32 v 12 x + +let to_bytes pkt =3D + let header =3D Cstruct.create sizeof_header in + let len =3D Int32.of_int (Buffer.length pkt.data) in + let ty =3D Op.to_int32 pkt.ty in + set_header_ty header ty; + set_header_rid header pkt.rid; + set_header_tid header pkt.tid; + set_header_len header len; + let result =3D Buffer.create 64 in + Buffer.add_bytes result (Cstruct.to_bytes header); + Buffer.add_buffer result pkt.data; + Buffer.to_bytes result + +let get_tid pkt =3D pkt.tid +let get_ty pkt =3D pkt.ty +let get_data pkt =3D + if pkt.len > 0 && Buffer.nth pkt.data (pkt.len - 1) =3D '\000' then + Buffer.sub pkt.data 0 (pkt.len - 1) + else + Buffer.contents pkt.data +let get_rid pkt =3D pkt.rid + +module Parser =3D struct + (** Incrementally parse packets *) + + let header_size =3D 16 + + let xenstore_payload_max =3D 4096 (* xen/include/public/io/xs_wire.h *) + + let allow_oversize_packets =3D ref true + + type state =3D + | Unknown_operation of int32 + | Parser_failed of string + | Need_more_data of int + | Packet of t + + type parse =3D + | ReadingHeader of int * bytes + | ReadingBody of t + | Finished of state + + let start () =3D ReadingHeader (0, Bytes.make header_size '\000') + + let state =3D function + | ReadingHeader(got_already, _) -> Need_more_data (header_size - got_a= lready) + | ReadingBody pkt -> Need_more_data (pkt.len - (Buffer.length pkt.data= )) + | Finished r -> r + + let parse_header str =3D + let header =3D Cstruct.create sizeof_header in + Cstruct.blit_from_string str 0 header 0 sizeof_header; + let ty =3D get_header_ty header in + let rid =3D get_header_rid header in + let tid =3D get_header_tid header in + let len =3D get_header_len header in + + let len =3D Int32.to_int len in + (* A packet which is bigger than xenstore_payload_max is illegal. + This will leave the guest connection is a bad state and will + be hard to recover from without restarting the connection + (ie rebooting the guest) *) + let len =3D if !allow_oversize_packets then len else max 0 (min xensto= re_payload_max len) in + + begin match Op.of_int32 ty with + | Some ty -> + let t =3D { + tid =3D tid; + rid =3D rid; + ty =3D ty; + len =3D len; + data =3D Buffer.create len; + } in + if len =3D 0 + then Finished (Packet t) + else ReadingBody t + | None -> Finished (Unknown_operation ty) + end + + let input state (bytes : string) =3D + match state with + | ReadingHeader(got_already, (str : bytes)) -> + Bytes.blit_string bytes 0 str got_already (String.length bytes); + let got_already =3D got_already + (String.length bytes) in + if got_already < header_size + then ReadingHeader(got_already, str) + else parse_header (Bytes.to_string str) + | ReadingBody x -> + Buffer.add_string x.data bytes; + let needed =3D x.len - (Buffer.length x.data) in + if needed > 0 + then ReadingBody x + else Finished (Packet x) + | Finished f -> Finished f +end + +(* Should we switch to an explicit stream abstraction here? *) +module type IO =3D sig + type 'a t + val return: 'a -> 'a t + val ( >>=3D ): 'a t -> ('a -> 'b t) -> 'b t + + type channel + val read: channel -> bytes -> int -> int -> int t + val write: channel -> bytes -> int -> int -> unit t +end + +exception Unknown_xenstore_operation of int32 +exception Response_parser_failed of string +exception EOF + +type ('a, 'b) result =3D + | Ok of 'a + | Exception of 'b + +module PacketStream =3D functor(IO: IO) -> struct + let ( >>=3D ) =3D IO.( >>=3D ) + let return =3D IO.return + + type stream =3D { + channel: IO.channel; + mutable incoming_pkt: Parser.parse; (* incrementally parses the next p= acket *) + } + + let make t =3D { + channel =3D t; + incoming_pkt =3D Parser.start (); + } + + (* [recv client] returns a single Packet, or fails *) + let rec recv t =3D + let open Parser in match Parser.state t.incoming_pkt with + | Packet pkt -> + t.incoming_pkt <- start (); + return (Ok pkt) + | Need_more_data x -> + let buf =3D Bytes.make x '\000' in + IO.read t.channel buf 0 x + >>=3D (function + | 0 -> return (Exception EOF) + | n -> + let fragment =3D Bytes.sub_string buf 0 n in + t.incoming_pkt <- input t.incoming_pkt fragment; + recv t) + | Unknown_operation x -> return (Exception (Unknown_xenstore_operation= x)) + | Parser_failed x -> return (Exception (Response_parser_failed x)) + + (* [send client pkt] sends [pkt] and returns (), or fails *) + let send t request =3D + let req =3D to_bytes request in + IO.write t.channel req 0 (Bytes.length req) +end + +module Token =3D struct + type t =3D string + + (** [to_user_string x] returns the user-supplied part of the watch token= *) + let to_user_string x =3D Scanf.sscanf x "%d:%s" (fun _ x -> x) + + let to_debug_string x =3D x + + let of_string x =3D x + let to_string x =3D x +end + +let data_concat ls =3D (String.concat "\000" ls) ^ "\000" + +let create tid rid ty data =3D + let len =3D String.length data in + let b =3D Buffer.create len in + Buffer.add_string b data; + { + tid =3D tid; + rid =3D rid; + ty =3D ty; + len =3D len; + data =3D b; + } + +module Response =3D struct + + type payload =3D + | Read of string + | Directory of string list + | Getperms of ACL.t + | Getdomainpath of string + | Transaction_start of int32 + | Write + | Mkdir + | Rm + | Setperms + | Watch + | Unwatch + | Transaction_end + | Debug of string list + | Introduce + | Resume + | Release + | Set_target + | Reset_watches + | Directory_part of int * string list + | Isintroduced of bool + | Error of string + | Watchevent of string * string + + let prettyprint_payload =3D + let open Printf in function + | Read x -> sprintf "Read %s" x + | Directory xs -> sprintf "Directory [ %s ]" (String.concat "; " xs) + | Getperms acl -> sprintf "Getperms %s" (ACL.to_string acl) + | Getdomainpath p -> sprintf "Getdomainpath %s" p + | Transaction_start x -> sprintf "Transaction_start %ld" x + | Write -> "Write" + | Mkdir -> "Mkdir" + | Rm -> "Rm" + | Setperms -> "Setperms" + | Watch -> "Watch" + | Unwatch -> "Unwatch" + | Transaction_end -> "Transaction_end" + | Debug xs -> sprintf "Debug [ %s ]" (String.concat "; " xs) + | Introduce -> "Introduce" + | Resume -> "Resume" + | Release -> "Release" + | Set_target -> "Set_target" + | Reset_watches -> "Reset_watches" + | Directory_part (gencnt, xs) -> + sprintf "Directory_part #%d [ %s ]" gencnt (String.concat "; " x= s) + | Isintroduced x -> sprintf "Isintroduced %b" x + | Error x -> sprintf "Error %s" x + | Watchevent (x, y) -> sprintf "Watchevent %s %s" x y + + let ty_of_payload =3D function + | Read _ -> Op.Read + | Directory _ -> Op.Directory + | Getperms _ -> Op.Getperms + | Getdomainpath _ -> Op.Getdomainpath + | Transaction_start _ -> Op.Transaction_start + | Debug _ -> Op.Debug + | Isintroduced _ -> Op.Isintroduced + | Watchevent (_, _) -> Op.Watchevent + | Error _ -> Op.Error + | Write -> Op.Write + | Mkdir -> Op.Mkdir + | Rm -> Op.Rm + | Setperms -> Op.Setperms + | Watch -> Op.Watch + | Unwatch -> Op.Unwatch + | Transaction_end -> Op.Transaction_end + | Introduce -> Op.Introduce + | Resume -> Op.Resume + | Release -> Op.Release + | Set_target -> Op.Set_target + | Reset_watches -> Op.Reset_watches + | Directory_part _ -> Op.Directory_part + + let ok =3D "OK\000" + + let data_of_payload =3D function + | Read x -> x + | Directory ls -> if ls =3D [] then "" else data_concat ls + | Getperms perms -> data_concat [ ACL.to_string perms ] + | Getdomainpath x -> data_concat [ x ] + | Transaction_start tid -> data_concat [ Int32.to_string tid ] + | Debug items -> data_concat items + | Isintroduced b -> data_concat [ if b then "T" else "F" ] + | Watchevent (path, token) -> data_concat [ path; token ] + | Error x -> data_concat [ x ] + | _ -> ok + + let print x tid rid =3D + create tid rid (ty_of_payload x) (data_of_payload x) +end + +module Request =3D struct + + type path_op =3D + | Read + | Directory + | Directory_part of int + | Getperms + | Write of string + | Mkdir + | Rm + | Setperms of ACL.t + + type payload =3D + | PathOp of string * path_op + | Getdomainpath of int + | Transaction_start + | Watch of string * string + | Unwatch of string * string + | Transaction_end of bool + | Debug of string list + | Introduce of int * Nativeint.t * int + | Resume of int + | Release of int + | Set_target of int * int + | Reset_watches + | Isintroduced of int + | Error of string + | Watchevent of string + + open Printf + + let prettyprint_pathop x =3D function + | Read -> sprintf "Read %s" x + | Directory -> sprintf "Directory %s" x + | Directory_part off -> sprintf "Directory %s @%d" x off + | Getperms -> sprintf "Getperms %s" x + | Write v -> sprintf "Write %s %s" x v + | Mkdir -> sprintf "Mkdir %s" x + | Rm -> sprintf "Rm %s" x + | Setperms acl -> sprintf "Setperms %s %s" x (ACL.to_string acl) + + let prettyprint_payload =3D function + | PathOp (path, op) -> prettyprint_pathop path op + | Getdomainpath x -> sprintf "Getdomainpath %d" x + | Transaction_start -> "Transaction_start" + | Watch (x, y) -> sprintf "Watch %s %s" x y + | Unwatch (x, y) -> sprintf "Unwatch %s %s" x y + | Transaction_end x -> sprintf "Transaction_end %b" x + | Debug xs -> sprintf "Debug [ %s ]" (String.concat "; " xs) + | Introduce (x, n, y) -> sprintf "Introduce %d %nu %d" x n y + | Resume x -> sprintf "Resume %d" x + | Release x -> sprintf "Release %d" x + | Set_target (x, y) -> sprintf "Set_target %d %d" x y + | Reset_watches -> "Reset_watches" + | Isintroduced x -> sprintf "Isintroduced %d" x + | Error x -> sprintf "Error %s" x + | Watchevent x -> sprintf "Watchevent %s" x + + exception Parse_failure + + let strings data =3D split_string '\000' data + + let one_string data =3D + let args =3D split_string ~limit:2 '\000' data in + match args with + | x :: [] -> x + | _ -> + raise Parse_failure + + let two_strings data =3D + let args =3D split_string ~limit:2 '\000' data in + match args with + | a :: b :: [] -> a, b + | a :: [] -> a, "" (* terminating NULL removed by get_data *) + | _ -> + raise Parse_failure + + let acl x =3D match ACL.of_string x with + | Some x -> x + | None -> + raise Parse_failure + + let domid s =3D + let v =3D ref 0 in + let is_digit c =3D c >=3D '0' && c <=3D '9' in + let len =3D String.length s in + let i =3D ref 0 in + while !i < len && not (is_digit s.[!i]) do incr i done; + while !i < len && is_digit s.[!i] + do + let x =3D (Char.code s.[!i]) - (Char.code '0') in + v :=3D !v * 10 + x; + incr i + done; + !v + + let bool =3D function + | "F" -> false + | "T" -> true + | _ -> + raise Parse_failure + + let parse_exn request =3D + let data =3D get_data request in + match get_ty request with + | Op.Read -> PathOp (data |> one_string, Read) + | Op.Directory -> PathOp (data |> one_string, Directory) + | Op.Getperms -> PathOp (data |> one_string, Getperms) + | Op.Getdomainpath -> Getdomainpath (data |> one_string |> domid) + | Op.Transaction_start -> Transaction_start + | Op.Write -> + let path, value =3D two_strings data in + PathOp (path, Write value) + | Op.Mkdir -> PathOp (data |> one_string, Mkdir) + | Op.Rm -> PathOp (data |> one_string, Rm) + | Op.Setperms -> + let path, perms =3D two_strings data in + let perms =3D acl perms in + PathOp(path, Setperms perms) + | Op.Watch -> + let path, token =3D two_strings data in + Watch(path, token) + | Op.Unwatch -> + let path, token =3D two_strings data in + Unwatch(path, token) + | Op.Transaction_end -> Transaction_end(data |> one_string |> bool) + | Op.Debug -> Debug (strings data) + | Op.Introduce -> + begin match strings data with + | d :: mfn :: port :: _ -> + let d =3D domid d in + let mfn =3D Nativeint.of_string mfn in + let port =3D int_of_string port in + Introduce (d, mfn, port) + | _ -> + raise Parse_failure + end + | Op.Resume -> Resume (data |> one_string |> domid) + | Op.Release -> Release (data |> one_string |> domid) + | Op.Set_target -> + let mine, yours =3D two_strings data in + let mine =3D domid mine and yours =3D domid yours in + Set_target(mine, yours) + | Op.Reset_watches -> Reset_watches + | Op.Directory_part -> + let path, offstr =3D two_strings data in + PathOp (path, Directory_part (int_of_string offstr)) + | Op.Isintroduced -> Isintroduced (data |> one_string |> domid) + | Op.Error -> Error(data |> one_string) + | Op.Watchevent -> Watchevent(data |> one_string) + + let parse request =3D + try + Some (parse_exn request) + with _ -> None + + let prettyprint request =3D + Printf.sprintf "tid =3D %ld; rid =3D %ld; payload =3D %s" + (get_tid request) (get_rid request) + (match parse request with + | None -> "None" + | Some x -> "Some " ^ (prettyprint_payload x)) + + let ty_of_payload =3D function + | PathOp(_, Directory) -> Op.Directory + | PathOp(_, Read) -> Op.Read + | PathOp(_, Getperms) -> Op.Getperms + | Debug _ -> Op.Debug + | Watch (_, _) -> Op.Watch + | Unwatch (_, _) -> Op.Unwatch + | Transaction_start -> Op.Transaction_start + | Transaction_end _ -> Op.Transaction_end + | Introduce(_, _, _) -> Op.Introduce + | Release _ -> Op.Release + | Resume _ -> Op.Resume + | Getdomainpath _ -> Op.Getdomainpath + | PathOp(_, Write _) -> Op.Write + | PathOp(_, Mkdir) -> Op.Mkdir + | PathOp(_, Rm) -> Op.Rm + | PathOp(_, Setperms _) -> Op.Setperms + | Set_target (_, _) -> Op.Set_target + | Reset_watches -> Op.Reset_watches + | PathOp(_, Directory_part _) -> Op.Directory_part + | Isintroduced _ -> Op.Isintroduced + | Error _ -> Op.Error + | Watchevent _ -> Op.Watchevent + + let transactional_of_payload =3D function + | PathOp(_, _) + | Transaction_end _ -> true + | _ -> false + + let data_of_payload =3D function + | PathOp(path, Write value) -> + path ^ "\000" ^ value (* no NULL at the end *) + | PathOp(path, Setperms perms) -> + data_concat [ path; ACL.to_string perms ] + | PathOp(path, _) -> data_concat [ path ] + | Debug commands -> data_concat commands + | Watch (path, token) + | Unwatch (path, token) -> data_concat [ path; token ] + | Transaction_start -> data_concat [] + | Transaction_end commit -> data_concat [ if commit then "T" else "F" ] + | Introduce(domid, mfn, port) -> + data_concat [ + Printf.sprintf "%u" domid; + Printf.sprintf "%nu" mfn; + string_of_int port; + ] + | Release domid + | Resume domid + | Getdomainpath domid + | Isintroduced domid -> + data_concat [ Printf.sprintf "%u" domid; ] + | Reset_watches -> data_concat [] + | Set_target (mine, yours) -> + data_concat [ Printf.sprintf "%u" mine; Printf.sprintf "%u" yours; ] + | Error _ -> + failwith "Unimplemented: data_of_payload (Error)" + | Watchevent _ -> + failwith "Unimplemented: data_of_payload (Watchevent)" + + let print x tid rid =3D + create + (if transactional_of_payload x then tid else 0l) + rid + (ty_of_payload x) + (data_of_payload x) +end + +module Unmarshal =3D struct + let some x =3D Some x + let int_of_string_opt x =3D try Some(int_of_string x) with _ -> None + let int32_of_string_opt x =3D try Some(Int32.of_string x) with _ -> None + let unit_of_string_opt x =3D if x =3D "" then Some () else None + let ok x =3D if x =3D "OK" then Some () else None + + let string =3D some ++ get_data + let list =3D some ++ split_string '\000' ++ get_data + let acl =3D ACL.of_string ++ get_data + let int =3D int_of_string_opt ++ get_data + let int32 =3D int32_of_string_opt ++ get_data + let unit =3D unit_of_string_opt ++ get_data + let ok =3D ok ++ get_data +end + +exception Enoent of string +exception Eagain +exception Eexist +exception Invalid +exception Error of string + +let response hint sent received f =3D match get_ty sent, get_ty received w= ith + | _, Op.Error -> + begin match get_data received with + | "ENOENT" -> raise (Enoent hint) + | "EAGAIN" -> raise Eagain + | "EINVAL" -> raise Invalid + | "EEXIST" -> raise Eexist + | s -> raise (Error s) + end + | x, y when x =3D y -> + begin match f received with + | None -> raise (Error (Printf.sprintf "failed to parse response (hi= nt:%s) (payload:%s)" hint (get_data received))) + | Some z -> z + end + | x, y -> + raise (Error (Printf.sprintf "unexpected packet: expected %s; got %s" = (Op.to_string x) (Op.to_string y))) + +type address =3D + | Unix of string + | Domain of int + +let string_of_address =3D function + | Unix x -> x + | Domain x -> string_of_int x + +let domain_of_address =3D function + | Unix _ -> 0 + | Domain x -> x + diff --git a/tools/ocaml/xenstored/transaction.ml b/tools/ocaml/xenstored/t= ransaction.ml index 25bc8c3b4a..4ee77b6e14 100644 --- a/tools/ocaml/xenstored/transaction.ml +++ b/tools/ocaml/xenstored/transaction.ml @@ -82,6 +82,7 @@ type t =3D { start_count: int64; store: Store.t; (* This is the store that we change in write operations. = *) quota: Quota.t; + mutable must_fail: bool; oldroot: Store.Node.t; mutable paths: (Xenbus.Xb.Op.operation * Store.Path.t) list; mutable operations: (Packet.request * Packet.response) list; @@ -89,7 +90,7 @@ type t =3D { mutable write_lowpath: Store.Path.t option; } let get_id t =3D match t.ty with No -> none | Full (id, _, _) -> id - +let mark_failed t =3D t.must_fail <- true let counter =3D ref 0L let failed_commits =3D ref 0L let failed_commits_no_culprit =3D ref 0L @@ -117,6 +118,8 @@ let trim_short_running_transactions txn =3D keep !short_running_txns =20 +let invalid_op =3D Xenbus.Xb.Op.Invalid, [] + let make ?(internal=3Dfalse) id store =3D let ty =3D if id =3D none then No else Full(id, Store.copy store, store) = in let txn =3D { @@ -129,6 +132,7 @@ let make ?(internal=3Dfalse) id store =3D operations =3D []; read_lowpath =3D None; write_lowpath =3D None; + must_fail =3D false; } in if id <> none && not internal then ( let now =3D Unix.gettimeofday () in @@ -139,10 +143,11 @@ let make ?(internal=3Dfalse) id store =3D let get_store t =3D t.store let get_paths t =3D t.paths =20 +let is_read_only t =3D t.paths =3D [] && not t.must_fail let get_root t =3D Store.get_root t.store =20 -let is_read_only t =3D t.paths =3D [] let add_wop t ty path =3D t.paths <- (ty, path) :: t.paths +let clear_wops t =3D t.paths <- [] let add_operation ~perm t request response =3D if !Define.maxrequests >=3D 0 && not (Perms.Connection.is_dom0 perm) @@ -151,7 +156,9 @@ let add_operation ~perm t request response =3D t.operations <- (request, response) :: t.operations let get_operations t =3D List.rev t.operations let set_read_lowpath t path =3D t.read_lowpath <- get_lowest path t.read_l= owpath -let set_write_lowpath t path =3D t.write_lowpath <- get_lowest path t.writ= e_lowpath +let set_write_lowpath t path =3D + Logging.debug "transaction" "set_writelowpath (%d) %s" (get_id t) (Stor= e.Path.to_string path); + t.write_lowpath <- get_lowest path t.write_lowpath =20 let path_exists t path =3D Store.path_exists t.store path =20 @@ -200,7 +207,7 @@ let commit ~con t =3D let has_commited =3D match t.ty with | No -> true - | Full (_id, oldstore, cstore) -> (* "cstore" meaning current canon= ical store *) + | Full (id, oldstore, cstore) -> (* "cstore" meaning current canoni= cal store *) let commit_partial oldroot cstore store =3D (* get the lowest path of the query and verify that it hasn't been modified by others transactions. *) @@ -240,11 +247,16 @@ let commit ~con t =3D (* we try a partial commit if possible *) commit_partial oldroot cstore store in + if t.must_fail then begin + Logging.info "transaction" "Transaction %d was marked to fail (by live-= update)" id; + false + end else if !test_eagain && Random.int 3 =3D 0 then false else try_commit (Store.get_root oldstore) cstore t.store in + Logging.info "transaction" "has_commited: %b" has_commited; if has_commited && has_write_ops then Disk.write t.store; if not has_commited @@ -252,3 +264,102 @@ let commit ~con t =3D else if not !has_coalesced then Logging.commit ~tid:(get_id t) ~con; has_commited + +module LR =3D Disk.LiveRecord + +(* here instead of Store.ml to avoid dependency cycle *) +let write_node ch txidaccess path node =3D + let value =3D Store.Node.get_value node in + let perms =3D Store.Node.get_perms node in + let path =3D Store.Path.of_path_and_name path (Symbol.to_string node.Stor= e.Node.name) |> Store.Path.to_string in + LR.write_node_data ch ~txidaccess ~path ~value ~perms + +let split limit c s =3D + let limit =3D match limit with None -> 8 | Some x -> x in + String.split ~limit c s +=09 +exception Invalid_Cmd_Args +let split_one_path data conpath =3D + let args =3D split (Some 2) '\000' data in + match args with + | path :: "" :: [] -> Store.Path.create path conpath + | _ -> raise Invalid_Cmd_Args +=09 +let dump base conpath ~conid txn ch =3D + (* TODO: implicit paths need to be converted to explicit *) + let txid =3D get_id txn in + LR.write_transaction_data ch ~conid ~txid; + let store =3D get_store txn in + let write_node_mkdir path =3D + let perms, value =3D match Store.get_node store path with + | None -> Perms.Node.default0, "" (* need to dump mkdir anyway even if l= ater deleted due to implicit path creation *) + | Some node -> Store.Node.get_perms node, Store.Node.get_value node (* n= ot always "", e.g. on EEXIST *) in + LR.write_node_data ch ~txidaccess:(Some (conid, txid, LR.W)) ~path:(Stor= e.Path.to_string path) ~value ~perms +in + maybe (fun path -> + (* if there were any reads make sure the tree matches, remove all conten= ts and write out subtree *) + match Store.get_node store path with + | None -> (* we've only read nodes that we ended up deleting, nothing to= do *) () + | Some node -> + write_node ch (Some (conid, txid, LR.Del)) (Store.Path.get_parent path)= node; + let path =3D Store.Path.get_parent path in + Store.traversal node @@ fun path' node -> + write_node ch (Some (conid,txid, LR.R)) (List.append path path') node + ) txn.read_lowpath; + (* we could do something similar for write_lowpath, but that would become=20 + complicated to handle correctly wrt to permissions and quotas if there= are nodes + owned by other domains in the subtree. + *) + let ops =3D get_operations txn in + if ops <> [] then + (* mark that we had some operation, these could be failures, etc. + we want to fail the transaction after a live-update, + unless it is completely a no-op + *) + let perms =3D Store.getperms store Perms.Connection.full_rights [] in + let value =3D Store.get_root store |> Store.Node.get_value in + LR.write_node_data ch ~txidaccess:(Some (conid, txid, LR.R)) ~path:"/" = ~value ~perms; + ListLabels.iter (fun (req, reply) -> + Logging.debug "transaction" "dumpop %s" (Xenbus.Xb.Op.to_string req.Pack= et.ty);=20 + let data =3D req.Packet.data in + let open Xenbus.Xb.Op in + match reply with + | Packet.Error _ -> () + | _ -> + try match req.Packet.ty with +| Debug +| Watch +| Unwatch +| Transaction_start +| Transaction_end +| Introduce +| Release +| Watchevent +| Getdomainpath +| Error +| Isintroduced +| Resume +| Set_target +| Reset_watches +| Invalid +| Directory +| (Read|Getperms) -> () +| (Write|Setperms) -> + (match (split (Some 2) '\000' data) with + | path :: _ :: _ -> + let path =3D Store.Path.create path conpath in + if req.Packet.ty =3D Write then + write_node_mkdir (Store.Path.get_parent path);(* implicit mkdir *) + (match Store.get_node store path with + | None -> () + | Some node -> + write_node ch (Some (conid, txid, LR.W)) (Store.Path.get_parent path) nod= e) + | _ -> raise Invalid_Cmd_Args) +| Mkdir -> + let path =3D split_one_path data conpath in + write_node_mkdir path; +| Rm -> + let path =3D split_one_path data conpath |> Store.Path.to_string in + LR.write_node_data ch ~txidaccess:(Some (conid, txid, LR.Del)) ~path ~val= ue:"" ~perms:Perms.Node.default0 + with Invalid_Cmd_Args|Define.Invalid_path|Not_found-> () + ) ops --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610749814; cv=none; d=zohomail.com; s=zohoarc; b=Cd8rez8QZc1t4tIikG58jSiqAYPnMC5rcWDwzUFE481eJf5+Z9kqnoXnyfNyWqNozz66shLo4dvu7Kt7mq3pKY/w7jQapP9gWs7IbbooMV+KB1gSFrLldxoGNVEkXGlpuWFn67SGDlZI8cOR7H5AbTSHbd1cvSr5KZDHJYiaPwE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610749814; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=quQ3C93RhqIhYCH2CL7KwNY12r0jRZKozQkm69pc+bU=; b=HjZlnZZZwWMWYS7u/AY0XD+eDJSfOdNIglyR3zoAgdPxzG9zPHLesw6ZjL2T0ZNHrXZZHivv39iSl2UnDJWbJkaiznkrz2qw6L7AkyHgOZGDlfzLD7OXow/hlNb3RRyW+OtFZ/sRA/fX6SxedGK7vLV0VnnLjzSayAlx4hSGJYQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 161074981494219.62235261390674; Fri, 15 Jan 2021 14:30:14 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68662.122972 (Exim 4.92) (envelope-from ) id 1l0XbT-0006CZ-Sf; Fri, 15 Jan 2021 22:29:59 +0000 Received: by outflank-mailman (output) from mailman id 68662.122972; Fri, 15 Jan 2021 22:29:59 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XbT-0006CP-PL; Fri, 15 Jan 2021 22:29:59 +0000 Received: by outflank-mailman (input) for mailman id 68662; Fri, 15 Jan 2021 22:29:59 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XbT-00061Y-5d for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:29:59 +0000 Received: from esa4.hc3370-68.iphmx.com (unknown [216.71.155.144]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id d3aabfd2-998e-4de8-9ee5-2441aabbaaf3; Fri, 15 Jan 2021 22:29:50 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: d3aabfd2-998e-4de8-9ee5-2441aabbaaf3 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610749790; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=6dNoOpxPMfbkvAFQKGVZg+g4DArUnz6wcJHSzYnlKes=; b=gms080TbWYlCG2O3cgCyEhdn2zxvN8BlmqiDjZVJd79c+b5VKVGEZYLN Iw0gCpkFNQWL8xbmgIBO2AxvWYmwXHDjuhkCHExbN9+CWUEI2LV/c1L1i uOoGfNs/0p12R+cfpz07XZ3PdezQ7212siISe55V4zGcqcD08aftMy9cm g=; Authentication-Results: esa4.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: 8JIRRQ/vTnM8hZE8q0nckRqCFDUqGtRyZFzsPEFzkfGMNg4c5LceWHyUP9Jxq7zq2wErry/YY6 p8azCp7mDxtfmHPk5pnN0E8LDinNV9HZm7OYn6Nn1wfrJiWO8n4HQMpkv3v6JsML2mAC/G/nkF R7HdRGjbgRQV46p7co/hjVDRB/mieTIbLw2y/Q7LIRfsOmMwVCRzSS8cl9P8ZheLCpbdcU1orK Vp8BlpFnAS9hMgrqkDIaa+gTHCb07n3Ot13bPjyGuR4SfgwmnyX8ZWFg2/XvL5M8zqeHQIJOdE his= X-SBRS: 5.1 X-MesageID: 36511519 X-Ironport-Server: esa4.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="36511519" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu , Pau Ruiz Safont Subject: [PATCH v2 5/8] tools/ocaml/xenstored: Automatically resume when possible Date: Fri, 15 Jan 2021 22:28:47 +0000 Message-ID: X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) When a `db` file exists use it to resume oxenstored. It will contains a xenstore tree, domain reconnection info, and watches. It is currently missing data about all active socket connections, so a toolstack should ideally be stopped and restarted too. Tell systemd about oxenstored's PID and allow it to restart on success. This should make updating oxenstored as easy as: `systemctl stop -s SIGTERM xenstored` on a suitable xenstored version. Signed-off-by: Edwin T=C3=B6r=C3=B6k Reviewed-by: Pau Ruiz Safont Reviewed-by: Christian Lindig Acked-by: Christian Lindig --- Changed since V1: * post publicly now that the XSA is out --- tools/ocaml/xenstored/xenstored.ml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xen= stored.ml index 6b5381962b..500d96753b 100644 --- a/tools/ocaml/xenstored/xenstored.ml +++ b/tools/ocaml/xenstored/xenstored.ml @@ -292,9 +292,8 @@ let _ =3D List.iter (fun path -> Store.write store Perms.Connection.full_rights path "") Store.Path.speci= als; =20 - let filename =3D Paths.xen_run_stored ^ "/db" in - if cf.restart && Sys.file_exists filename then ( - DB.from_file store domains cons filename; + if cf.restart && Sys.file_exists Disk.xs_daemon_database then ( + DB.from_file store domains cons Disk.xs_daemon_database; Event.bind_dom_exc_virq eventchn ) else ( if !Disk.enable then ( @@ -320,7 +319,7 @@ let _ =3D Sys.set_signal Sys.sigpipe Sys.Signal_ignore; =20 if cf.activate_access_log then begin - let post_rotate () =3D DB.to_file store cons (Paths.xen_run_stored ^ "/d= b") in + let post_rotate () =3D DB.to_file store cons Disk.xs_daemon_database in Logging.init_access_log post_rotate end; =20 @@ -494,5 +493,8 @@ let _ =3D raise exc done; info "stopping xenstored"; - DB.to_file store cons (Paths.xen_run_stored ^ "/db"); + DB.to_file store cons Disk.xs_daemon_database; + (* unlink pidfile so that launch-xenstore works again *) + Unixext.unlink_safe pidfile; + (match cf.pidfile with Some pidfile -> Unixext.unlink_safe pidfile | Non= e -> ()); () --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610749842; cv=none; d=zohomail.com; s=zohoarc; b=QpW4USL+5BqDcAFNHR5vBgyQPBclfrCKd48oU0BmQXVEZHDnP/qI3MyUAnNIS0hFcD/UknARXBgRh6/rCziuCkrEkL8O2kkxcOhgrivpU9rANbg3QqHbPfGSH7ZmnJSgGjuQpI2pMNeQQJ1JCk7bNZfVjcgsckoxGwa1SO7Ydpo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610749842; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=G7ZS4UNr6N35h+zSn8uGfaLi+HBIZgNfkcMWYSpatgg=; b=LI8VBHrdCBfSInx4pyCdHaLFBX4WItIZ6a0StWfQuW1RRk77SPV72E0xtkDb4qDRPXIJ+bVtbVS4XO60JeRI/8XWWfsBO7VWWo4oMwcD20CQC+nzib+cdfoB71398576lI31GKGiwC9ICuEqyRxOi5UnpAlhh0KFgAE1InZ9yzY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610749842405679.7428971046984; Fri, 15 Jan 2021 14:30:42 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68679.123032 (Exim 4.92) (envelope-from ) id 1l0Xbt-0007OG-8N; Fri, 15 Jan 2021 22:30:25 +0000 Received: by outflank-mailman (output) from mailman id 68679.123032; Fri, 15 Jan 2021 22:30:25 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xbt-0007O8-4P; Fri, 15 Jan 2021 22:30:25 +0000 Received: by outflank-mailman (input) for mailman id 68679; Fri, 15 Jan 2021 22:30:24 +0000 Received: from all-amaz-eas1.inumbo.com ([34.197.232.57] helo=us1-amaz-eas2.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xbs-00062b-8r for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:30:24 +0000 Received: from esa6.hc3370-68.iphmx.com (unknown [216.71.155.175]) by us1-amaz-eas2.inumbo.com (Halon) with ESMTPS id ef7b5529-d612-4839-bc0f-62c474490687; Fri, 15 Jan 2021 22:29:59 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: ef7b5529-d612-4839-bc0f-62c474490687 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610749799; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=ChX2rsVn5ssD/dNJKc5/y7eY+C7iouZky4Y2USpFMDg=; b=H6sUoNR2i7+FwKh1s7RElUORLN1i5UTL7SXUSmU8OMyW7NLHrAUo9pI9 9N9vYG6l9WEgmhNLKHK51QOMHMWU5L3GMPWDlUtfGfDs2rqQ/GW7wIy5U KESqckmy5KrKdmgSf7s0yoKt2FKuTYg4aUVPVFkvhQjmLqhm7Zd6Ju2f3 Q=; Authentication-Results: esa6.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: mHx+RKLkQwzT3oeD7dsMnyR42Pl540jrfqcz7L7sfXLrey9EYa01zjmVv4vetuOp0N8lNlWXUG UczC7I/oZuEu7xb9VYVO9QS9+nyEy89N9W1YJGoC1vp/IDoQwxAN3g+u4eYnFv5FQW+8GE4Bw2 iEZ7VTKFerwgBtIkj4y2c/gyoAnhia0FmkOdnZHkk/iOO/FVAqVf9U5Ca+CJOett8qdCSdiz3V D0b5qENmEU3sBb+AZGYy3neH6v5o9rtq4VmTZRNnco8te6VujdIjgUTu/oNUCMHpnPZzoDURY/ lDw= X-SBRS: 5.1 X-MesageID: 35434458 X-Ironport-Server: esa6.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35434458" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu , Pau Ruiz Safont Subject: [PATCH v2 6/8] tools/ocaml/xenstored: add cooperative live-update command Date: Fri, 15 Jan 2021 22:28:48 +0000 Message-ID: <0ed42a4cb25f53620c31594de9949f150c4833cc.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) See docs/misc/xenstore.txt for documentation on live-update command. Validate that the binary exists and that the cmdline is valid, to prevent typos from taking down xenstore (if live-update fails there is no way back due to the use of execve). Live update only proceeds if there are no active transactions, and no unprocess input or unflushed output. It is not yet possible to force the live-update. Signed-off-by: Edwin T=C3=B6r=C3=B6k Reviewed-by: Pau Ruiz Safont Reviewed-by: Christian Lindig Acked-by: Christian Lindig --- Changed since V1: * post publicly now that the XSA is out --- tools/ocaml/xenstored/process.ml | 112 +++++++++++++++++++++++++++++++ tools/ocaml/xenstored/stdext.ml | 6 ++ 2 files changed, 118 insertions(+) diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/proce= ss.ml index 437d2dcf9e..c3c5dc58c0 100644 --- a/tools/ocaml/xenstored/process.ml +++ b/tools/ocaml/xenstored/process.ml @@ -15,6 +15,7 @@ *) =20 let error fmt =3D Logging.error "process" fmt +let warn fmt =3D Logging.warn "process" fmt let info fmt =3D Logging.info "process" fmt let debug fmt =3D Logging.debug "process" fmt =20 @@ -84,11 +85,122 @@ let create_implicit_path t perm path =3D List.iter (fun s -> Transaction.mkdir ~with_watch:false t perm s) ret ) =20 +module LiveUpdate =3D struct +type t =3D + { binary: string + ; cmdline: string list + ; deadline: float + ; force: bool + ; pending: bool } + +let state =3D + ref + { binary=3D Sys.executable_name + ; cmdline=3D [] + ; deadline=3D 0. + ; force=3D false + ; pending=3D false } + +let debug =3D Printf.eprintf + +let args_of_t t =3D (t.binary, "--restart" :: t.cmdline) + +let string_of_t t =3D + let executable, rest =3D args_of_t t in + Filename.quote_command executable rest + +let launch_exn t =3D + let executable, rest =3D args_of_t t in + let args =3D Array.of_list (executable :: rest) in + Unix.execv args.(0) args + +let validate_exn t =3D + (* --help must be last to check validity of earlier arguments *) + let t =3D {t with cmdline=3D t.cmdline @ ["--help"]} in + let cmd =3D string_of_t t in + debug "Executing %s" cmd ; + match Unix.fork () with + | 0 -> + ( try launch_exn t with _ -> exit 2 ) + | pid -> ( + match Unix.waitpid [] pid with + | _, Unix.WEXITED 0 -> + debug "Live update validated cmdline %s" cmd; + t + | _, Unix.WEXITED n -> + invalid_arg (Printf.sprintf "Command %s exited with code %d" cmd n) + | _, Unix.WSIGNALED n -> + invalid_arg + (Printf.sprintf "Command %s killed by ocaml signal number %d" cmd n) + | _, Unix.WSTOPPED n -> + invalid_arg + (Printf.sprintf "Command %s stopped by ocaml signal number %d" cmd n) + ) + +let parse_live_update args =3D + try + (state :=3D + match args with + | ["-f"; file] -> + validate_exn {!state with binary=3D file} + | ["-a"] -> + debug "Live update aborted" ; + {!state with pending=3D false} + | "-c" :: cmdline -> + validate_exn {!state with cmdline} + | "-s" :: _ -> + let timeout =3D ref 60 in + let force =3D ref false in + Arg.parse_argv ~current:(ref 1) (Array.of_list args) + [ ( "-t" + , Arg.Set_int timeout + , "timeout in seconds to wait for active transactions to finish" + ) + (*; ( "-F" + , Arg.Set force + , "force live update to happen even with running transactions \ + after timeout elapsed" )*) ] + (fun x -> raise (Arg.Bad x)) + "live-update -s" ; + debug "Live update process queued" ; + {!state with deadline =3D Unix.gettimeofday () +. float !timeout + ; force=3D !force; pending=3D true} + | _ -> + invalid_arg ("Unknown arguments: " ^ String.concat " " args)) ; + None + with + | Arg.Bad s | Arg.Help s | Invalid_argument s -> + Some s + | Unix.Unix_error (e, fn, args) -> + Some (Printf.sprintf "%s(%s): %s" fn args (Unix.error_message e)) + + let should_run cons =3D + let t =3D !state in + if t.pending then begin + match Connections.prevents_quit cons with + | [] -> true + | _ when Unix.gettimeofday () < t.deadline -> false + | l -> + info "Live update timeout reached: %d active connections" (List.lengt= h l); + List.iter (fun con -> warn "%s prevents live update" (Connection.get_= domstr con)) l; + if t.force then begin + warn "Live update forced, some domain connections may break!"; + true + end else begin + warn "Live update aborted, try migrating or shutting down the domain= s/toolstack"; + state :=3D { t with pending =3D false }; + false + end + end else false +end + (* packets *) let do_debug con t _domains cons data =3D if not (Connection.is_dom0 con) && not !allow_debug then None else try match split None '\000' data with + | "live-update" :: params -> + LiveUpdate.parse_live_update params | "print" :: msg :: _ -> Logging.xb_op ~tid:0 ~ty:Xenbus.Xb.Op.Debug ~con:"=3D=3D=3D=3D=3D=3D=3D>= " msg; None diff --git a/tools/ocaml/xenstored/stdext.ml b/tools/ocaml/xenstored/stdext= .ml index 4f2f3a2c8c..e1567c4dfa 100644 --- a/tools/ocaml/xenstored/stdext.ml +++ b/tools/ocaml/xenstored/stdext.ml @@ -44,6 +44,12 @@ let default d v =3D let maybe f v =3D match v with None -> () | Some x -> f x =20 +module Filename =3D struct + include Filename + let quote_command cmd args =3D + cmd :: args |> List.map quote |> String.concat " " +end + module String =3D struct include String =20 let of_char c =3D String.make 1 c --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751184; cv=none; d=zohomail.com; s=zohoarc; b=mAkd9sjZ9YpvOWXawuZT1/uaGTyCjixTsZUxzTwUvzG8unIqhfv5KICQbdXS3wnsMJiMZp7y4PdZL9qdbgh7kGQ4E6h9lQjmcEi/gHqGiNa9sWBHr4g1b7717FgxRl6FuLXcUE3Cmnl/tD+l1/AT2UC1Aji7n34XcImSy+mgZTM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751184; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=kGC+LS7mqQElM1qzjfag9ffWIxu8Nw3qHAqzb/YQ0pU=; b=Sk4Y3FkEUrBhgIK5O6uZPq6MNndoq1PdMz74PBonYxmQqRtKxuGj4/61JWu4O2aWeNrJO0YoHtlU2XsNh4UP+agUPpyFsB4pwUghqkekxIU4aNh8NmT/WQBfPDI/twNUidhm4F2NYhmbx9RSdmdLWmrboNxCrioJz1yyPuLl/MY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610751184579105.68472719131432; Fri, 15 Jan 2021 14:53:04 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68726.123128 (Exim 4.92) (envelope-from ) id 1l0XxT-0001lA-3M; Fri, 15 Jan 2021 22:52:43 +0000 Received: by outflank-mailman (output) from mailman id 68726.123128; Fri, 15 Jan 2021 22:52:43 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XxS-0001kz-Uo; Fri, 15 Jan 2021 22:52:42 +0000 Received: by outflank-mailman (input) for mailman id 68726; Fri, 15 Jan 2021 22:52:41 +0000 Received: from all-amaz-eas1.inumbo.com ([34.197.232.57] helo=us1-amaz-eas2.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0XxR-0001jt-Kb for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:52:41 +0000 Received: from esa1.hc3370-68.iphmx.com (unknown [216.71.145.142]) by us1-amaz-eas2.inumbo.com (Halon) with ESMTPS id 112507ef-6449-4931-bcc5-a9048cea50c8; Fri, 15 Jan 2021 22:52:39 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 112507ef-6449-4931-bcc5-a9048cea50c8 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751159; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=augisCiDAmdzwz8nZWWTgE5LegS6N8R0zEPLsBzdOTA=; b=QU82e0qSt4lI3Ao8lVEuiiPLnqwWAyZVE+kGvSHmjVhtUi8iOZTOEUD8 FmZE8afXaiKiOnVQMhvoZFAg/bwM9TMWTTyAJCi4aG8mTm6XYjD9xF+6i k7kYbpx9Pg6G6IZpZxJhSIpNhbdjMsELUy1rhqABmpDGna9wonCnS81jo g=; Authentication-Results: esa1.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: 9jlr/S+Fg0jA0mFFaWVtVhO82mWeFmb6c+xVOZz+81qBhs4FkeJTYRq6spJHCsiWM1cK55DYHL WRSoJgQMfx11TpkCHZSPiFdSzsmotWCTXbVaavv60by8Rvav/4guc9rGgJmseVkUomN9iGQKSS blw9ALxKH5V2Uh4jKQ14AVEfhkzg/6+J9ivkDpBYUs23WTwSWRPW54IvvjTacDdTMXdKh4ET+8 YVN/ZYJKYdyb+4DqlIFZpkhnV14y8xW2CRItXbVGE8wJGBXue775uCwBSgNmOktwnMvdzakZ49 6LA= X-SBRS: 5.1 X-MesageID: 35591688 X-Ironport-Server: esa1.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35591688" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , "Christian Lindig" , David Scott , "Ian Jackson" , Wei Liu , Pau Ruiz Safont Subject: [PATCH v2 7/8] tools/ocaml/xenstored: start live update process Date: Fri, 15 Jan 2021 22:28:49 +0000 Message-ID: <4164cb728313c3b9fc38cf5e9ecb790ac93a9600.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) Signed-off-by: Edwin T=C3=B6r=C3=B6k Reviewed-by: Pau Ruiz Safont Reviewed-by: Christian Lindig Acked-by: Christian Lindig --- Changed since V1: * post publicly now that the XSA is out --- tools/ocaml/xenstored/logging.ml | 3 +++ tools/ocaml/xenstored/process.ml | 8 +++++--- tools/ocaml/xenstored/xenstored.ml | 29 ++++++++++++++++++++++------- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/tools/ocaml/xenstored/logging.ml b/tools/ocaml/xenstored/loggi= ng.ml index 1ede131329..39c3036155 100644 --- a/tools/ocaml/xenstored/logging.ml +++ b/tools/ocaml/xenstored/logging.ml @@ -327,6 +327,9 @@ let end_transaction ~tid ~con =3D if !access_log_transaction_ops && tid <> 0 then access_logging ~tid ~con (XbOp Xenbus.Xb.Op.Transaction_end) ~level:= Debug =20 +let live_update () =3D + xb_op ~tid:0 ~con:"" ~ty:Xenbus.Xb.Op.Debug "Live update begin" + let xb_answer ~tid ~con ~ty data =3D let print, level =3D match ty with | Xenbus.Xb.Op.Error when String.startswith "ENOENT" data -> !access_log= _read_ops , Warn diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/proce= ss.ml index c3c5dc58c0..3174d8ede5 100644 --- a/tools/ocaml/xenstored/process.ml +++ b/tools/ocaml/xenstored/process.ml @@ -112,6 +112,7 @@ let string_of_t t =3D let launch_exn t =3D let executable, rest =3D args_of_t t in let args =3D Array.of_list (executable :: rest) in + info "Launching %s, args: %s" executable (String.concat " " rest); Unix.execv args.(0) args =20 let validate_exn t =3D @@ -151,7 +152,7 @@ let parse_live_update args =3D | "-s" :: _ -> let timeout =3D ref 60 in let force =3D ref false in - Arg.parse_argv ~current:(ref 1) (Array.of_list args) + Arg.parse_argv ~current:(ref 0) (Array.of_list args) [ ( "-t" , Arg.Set_int timeout , "timeout in seconds to wait for active transactions to finish" @@ -166,7 +167,7 @@ let parse_live_update args =3D {!state with deadline =3D Unix.gettimeofday () +. float !timeout ; force=3D !force; pending=3D true} | _ -> - invalid_arg ("Unknown arguments: " ^ String.concat " " args)) ; + invalid_arg ("Unknown arguments: " ^ String.concat "," args)) ; None with | Arg.Bad s | Arg.Help s | Invalid_argument s -> @@ -200,7 +201,8 @@ let do_debug con t _domains cons data =3D then None else try match split None '\000' data with | "live-update" :: params -> - LiveUpdate.parse_live_update params + let dropped_trailing_nul =3D params |> List.rev |> List.tl |> List.rev in + LiveUpdate.parse_live_update dropped_trailing_nul | "print" :: msg :: _ -> Logging.xb_op ~tid:0 ~ty:Xenbus.Xb.Op.Debug ~con:"=3D=3D=3D=3D=3D=3D=3D>= " msg; None diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xen= stored.ml index 500d96753b..22413271fb 100644 --- a/tools/ocaml/xenstored/xenstored.ml +++ b/tools/ocaml/xenstored/xenstored.ml @@ -311,6 +311,11 @@ let _ =3D ); ); =20 + (* required for xenstore-control to detect availability of live-update *) + Store.mkdir store Perms.Connection.full_rights (Store.Path.of_string "/to= ol"); + Store.write store Perms.Connection.full_rights + (Store.Path.of_string "/tool/xenstored") Sys.executable_name; + Sys.set_signal Sys.sighup (Sys.Signal_handle sighup_handler); Sys.set_signal Sys.sigterm (Sys.Signal_handle (fun _ -> info "Received SIGTERM"; @@ -483,18 +488,28 @@ let _ =3D in =20 Systemd.sd_notify_ready (); + let live_update =3D ref false in while not (!quit && Connections.prevents_quit cons =3D []) do try - main_loop () + main_loop (); + live_update :=3D Process.LiveUpdate.should_run cons; + if !live_update || !quit then begin + (* don't initiate live update if saving state fails *) + DB.to_file store cons Disk.xs_daemon_database; + quit :=3D true; + end with exc -> - error "caught exception %s" (Printexc.to_string exc); + let bt =3D Printexc.get_backtrace () in + error "caught exception %s: %s" (Printexc.to_string exc) bt; if cf.reraise_top_level then raise exc done; info "stopping xenstored"; - DB.to_file store cons Disk.xs_daemon_database; - (* unlink pidfile so that launch-xenstore works again *) - Unixext.unlink_safe pidfile; - (match cf.pidfile with Some pidfile -> Unixext.unlink_safe pidfile | Non= e -> ()); - () + (* unlink pidfile so that launch-xenstore works again *) + Unixext.unlink_safe pidfile; + (match cf.pidfile with Some pidfile -> Unixext.unlink_safe pidfile | None= -> ()); + if !live_update then begin + Logging.live_update (); + Process.LiveUpdate.launch_exn !Process.LiveUpdate.state + end --=20 2.29.2 From nobody Fri May 3 16:36:00 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass(p=reject dis=none) header.from=citrix.com ARC-Seal: i=1; a=rsa-sha256; t=1610751563; cv=none; d=zohomail.com; s=zohoarc; b=hbo5Gy5pNcOgWVIX3qy+jU0c+UieLGpSTnVh4BpO3GFEN5wAdHrb4C3O7J41aCpcc16hV/2+KCdmZiDFSkmmc60wA4v9zsWvz9KA5snQCYdk0Z/NIZMgZUeFEBJX8762XHD5qLNYtoU6J+Z384BrpusfqF8V3sKAoPVS1PlZtYU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610751563; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=SvS7wl3JakT5JRL7Xu4UAYzPRWhiHZrW7P/U0sbY6c0=; b=PkT+OoVg6msmRegiuvIxQQJD/D7lu53SHRw1gqdHag800t6YhBeOEVz2Jn5uUE3n25lgJXjnPehvt6wDitR1km2Ok+D6Eeu43RM1qM+f/0aaBrVgN186YGfiRiNLkRCbUUfDdtEgqm7ttcBw3QMhR4NSh6OrArXBcEFFLizVadA= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=pass header.from= (p=reject dis=none) header.from= Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1610751563070576.644837879558; Fri, 15 Jan 2021 14:59:23 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.68784.123284 (Exim 4.92) (envelope-from ) id 1l0Y3a-0003QK-UA; Fri, 15 Jan 2021 22:59:02 +0000 Received: by outflank-mailman (output) from mailman id 68784.123284; Fri, 15 Jan 2021 22:59:02 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Y3a-0003Q2-PU; Fri, 15 Jan 2021 22:59:02 +0000 Received: by outflank-mailman (input) for mailman id 68784; Fri, 15 Jan 2021 22:59:01 +0000 Received: from us1-rack-iad1.inumbo.com ([172.99.69.81]) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1l0Xyb-0001Wj-20 for xen-devel@lists.xenproject.org; Fri, 15 Jan 2021 22:53:53 +0000 Received: from esa5.hc3370-68.iphmx.com (unknown [216.71.155.168]) by us1-rack-iad1.inumbo.com (Halon) with ESMTPS id f837a99d-1af1-4c1c-824d-9255f0634186; Fri, 15 Jan 2021 22:52:44 +0000 (UTC) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: f837a99d-1af1-4c1c-824d-9255f0634186 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=citrix.com; s=securemail; t=1610751164; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=Ebh7/z6kphJEF6Rxb6nhmacurC8cMZYuwIblENECTxE=; b=HX1tPbTGN6I3xHzCspGXnEklPWoBBEfLzpMdFWA0fPPc/aizW3hs8KjK 8RDTLwNHHOwj0e8kFWipA2O1RI6d2dIQpeKfvEJm7YJqAOTs9dZWPj+sI 7zJYeQXXMZVK5aVWdV33JWQonM50avau3n8XUBtM2sPuiG5mmEXQP6sSg M=; Authentication-Results: esa5.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none IronPort-SDR: ibHTxdOY10XEerFTAmHhhTKCDnAU/J5U4ykkAT7G1ZY/lwi+3HCbrMkFW9GkFslX2WHb7qNtsP IfErVuBdk3msIjTdzmpgrUltAr64mWnFQDvbvgF/L6C7K1egWRQE7iDwW3RTtkcHcYeOD5LR6X cRl6RRMH5EIM0ZFdpv2X0PrKDSw214iyu1/AOMX0lDPRAEmeJk1KiwVBhBmymCBfSJQvNIuOna c26aNuz/oxLAKRYRwKBuuZJ353DuVKpyiusj1oPh3UZFOdAr5bIuvTspxEB2A7BKHueJjPTEaZ g2s= X-SBRS: 5.1 X-MesageID: 35206335 X-Ironport-Server: esa5.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.79,350,1602561600"; d="scan'208";a="35206335" From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= To: CC: Edvin Torok , Christian Lindig , David Scott , Ian Jackson , Wei Liu , =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= , Pau Ruiz Safont Subject: [PATCH v2 8/8] tools/ocaml/xenstored: Implement live update for socket connections Date: Fri, 15 Jan 2021 22:28:50 +0000 Message-ID: <07cb0ca9b2d48c74bc499cd92d61ac9313bbbbf0.1610748224.git.edvin.torok@citrix.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @citrix.com) From: Edvin Torok Signed-off-by: Edwin T=C3=B6r=C3=B6k Reviewed-by: Pau Ruiz Safont Reviewed-by: Christian Lindig Acked-by: Christian Lindig --- Changed since V1 * post publicly now that the XSA is out --- tools/ocaml/xenstored/connection.ml | 25 +++++--- tools/ocaml/xenstored/parse_arg.ml | 4 ++ tools/ocaml/xenstored/process.ml | 51 ++++++++++++----- tools/ocaml/xenstored/store.ml | 2 +- tools/ocaml/xenstored/utils.ml | 12 ++++ tools/ocaml/xenstored/xenstored.ml | 88 +++++++++++++++++++++-------- 6 files changed, 138 insertions(+), 44 deletions(-) diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/co= nnection.ml index bd02060cd0..eb23c3af7a 100644 --- a/tools/ocaml/xenstored/connection.ml +++ b/tools/ocaml/xenstored/connection.ml @@ -281,6 +281,9 @@ let get_transaction con tid =3D =20 let do_input con =3D Xenbus.Xb.input con.xb let has_input con =3D Xenbus.Xb.has_in_packet con.xb +let has_partial_input con =3D match con.xb.Xenbus.Xb.partial_in with + | HaveHdr _ -> true + | NoHdr (n, _) -> n < Xenbus.Partial.header_size () let pop_in con =3D Xenbus.Xb.get_in_packet con.xb let has_more_input con =3D Xenbus.Xb.has_more_input con.xb =20 @@ -309,12 +312,13 @@ let is_bad con =3D match con.dom with None -> false |= Some dom -> Domain.is_bad_do Restrictions below can be relaxed once xenstored learns to dump more of its live state in a safe way *) let has_extra_connection_data con =3D - let has_in =3D has_input con in + let has_in =3D has_input con || has_partial_input con in let has_out =3D has_output con in let has_socket =3D con.dom =3D None in let has_nondefault_perms =3D make_perm con.dom <> con.perm in has_in || has_out - || has_socket (* dom0 sockets not dumped yet *) + (* TODO: what about SIGTERM, should use systemd to store FDS + || has_socket (* dom0 sockets not * dumped yet *) *) || has_nondefault_perms (* set_target not dumped yet *) =20 let has_transaction_data con =3D @@ -337,16 +341,21 @@ let stats con =3D Hashtbl.length con.watches, con.stat_nb_ops =20 let dump con chan =3D - match con.dom with + let id =3D match con.dom with | Some dom -> let domid =3D Domain.get_id dom in (* dump domain *) Domain.dump dom chan; - (* dump watches *) - List.iter (fun (path, token) -> - Printf.fprintf chan "watch,%d,%s,%s\n" domid (Utils.hexify path) (Utils= .hexify token) - ) (list_watches con); - | None -> () + domid + | None -> + let fd =3D con |> get_fd |> Utils.FD.to_int in + Printf.fprintf chan "socket,%d\n" fd; + -fd + in + (* dump watches *) + List.iter (fun (path, token) -> + Printf.fprintf chan "watch,%d,%s,%s\n" id (Utils.hexify path) (Utils.hex= ify token) + ) (list_watches con) =20 let debug con =3D let domid =3D get_domstr con in diff --git a/tools/ocaml/xenstored/parse_arg.ml b/tools/ocaml/xenstored/par= se_arg.ml index 2c4b5a8528..7c0478e76a 100644 --- a/tools/ocaml/xenstored/parse_arg.ml +++ b/tools/ocaml/xenstored/parse_arg.ml @@ -24,6 +24,7 @@ type config =3D pidfile: string option; (* old xenstored compatibility *) tracefile: string option; (* old xenstored compatibility *) restart: bool; + live_reload: bool; disable_socket: bool; } =20 @@ -35,6 +36,7 @@ let do_argv =3D and reraise_top_level =3D ref false and config_file =3D ref "" and restart =3D ref false + and live_reload =3D ref false and disable_socket =3D ref false in =20 @@ -52,6 +54,7 @@ let do_argv =3D ("--pid-file", Arg.Set_string pidfile, ""); (* for compatibility *) ("-T", Arg.Set_string tracefile, ""); (* for compatibility *) ("--restart", Arg.Set restart, "Read database on starting"); + ("--live", Arg.Set live_reload, "Read live dump on startup"); ("--disable-socket", Arg.Unit (fun () -> disable_socket :=3D true), "D= isable socket"); ] in let usage_msg =3D "usage : xenstored [--config-file ] [--no-dom= ain-init] [--help] [--no-fork] [--reraise-top-level] [--restart] [--disable= -socket]" in @@ -65,5 +68,6 @@ let do_argv =3D pidfile =3D if !pidfile <> "" then Some !pidfile else None; tracefile =3D if !tracefile <> "" then Some !tracefile else None; restart =3D !restart; + live_reload =3D !live_reload; disable_socket =3D !disable_socket; } diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/proce= ss.ml index 3174d8ede5..dd50456ad5 100644 --- a/tools/ocaml/xenstored/process.ml +++ b/tools/ocaml/xenstored/process.ml @@ -91,19 +91,24 @@ type t =3D ; cmdline: string list ; deadline: float ; force: bool + ; result: string list ; pending: bool } =20 let state =3D ref { binary=3D Sys.executable_name - ; cmdline=3D [] + ; cmdline=3D (Sys.argv |> Array.to_list |> List.tl) ; deadline=3D 0. ; force=3D false + ; result =3D [] ; pending=3D false } =20 let debug =3D Printf.eprintf =20 -let args_of_t t =3D (t.binary, "--restart" :: t.cmdline) +let forced_args =3D ["--live"; "--restart"] +let args_of_t t =3D + let filtered =3D List.filter (fun x -> not @@ List.mem x forced_args) t.c= mdline in + (t.binary, forced_args @ filtered) =20 let string_of_t t =3D let executable, rest =3D args_of_t t in @@ -117,12 +122,12 @@ let launch_exn t =3D =20 let validate_exn t =3D (* --help must be last to check validity of earlier arguments *) - let t =3D {t with cmdline=3D t.cmdline @ ["--help"]} in - let cmd =3D string_of_t t in + let t' =3D {t with cmdline=3D t.cmdline @ ["--help"]} in + let cmd =3D string_of_t t' in debug "Executing %s" cmd ; match Unix.fork () with | 0 -> - ( try launch_exn t with _ -> exit 2 ) + ( try launch_exn t' with _ -> exit 2 ) | pid -> ( match Unix.waitpid [] pid with | _, Unix.WEXITED 0 -> @@ -146,10 +151,14 @@ let parse_live_update args =3D validate_exn {!state with binary=3D file} | ["-a"] -> debug "Live update aborted" ; - {!state with pending=3D false} + {!state with pending=3D false; result =3D []} | "-c" :: cmdline -> - validate_exn {!state with cmdline} + validate_exn {!state with cmdline =3D !state.cmdline @ cmdline} | "-s" :: _ -> + (match !state.pending, !state.result with + | true, _ -> !state (* no change to state, avoid resetting timeout *) + | false, _ :: _ -> !state (* we got a pending result to deliver *) + | false, [] -> let timeout =3D ref 60 in let force =3D ref false in Arg.parse_argv ~current:(ref 0) (Array.of_list args) @@ -165,10 +174,16 @@ let parse_live_update args =3D "live-update -s" ; debug "Live update process queued" ; {!state with deadline =3D Unix.gettimeofday () +. float !timeout - ; force=3D !force; pending=3D true} + ; force=3D !force; pending=3D true}) | _ -> invalid_arg ("Unknown arguments: " ^ String.concat "," args)) ; - None + match !state.pending, !state.result with + | true, _ -> Some "BUSY" + | false, (_ :: _ as result) -> + (* xenstore-control has read the result, clear it *) + state :=3D { !state with result =3D [] }; + Some (String.concat "\n" result) + | false, [] -> None with | Arg.Bad s | Arg.Help s | Invalid_argument s -> Some s @@ -182,17 +197,27 @@ let parse_live_update args =3D | [] -> true | _ when Unix.gettimeofday () < t.deadline -> false | l -> - info "Live update timeout reached: %d active connections" (List.lengt= h l); - List.iter (fun con -> warn "%s prevents live update" (Connection.get_= domstr con)) l; + warn "timeout reached: have to wait, migrate or shutdown %d domains:"= (List.length l); + let msgs =3D List.rev_map (fun con -> Printf.sprintf "%s: %d tx, in: = %b, out: %b, perm: %s" + (Connection.get_domstr con) + (Connection.number_of_transactions con) + (Connection.has_input con) + (Connection.has_output con) + (Connection.get_perm con |> Perms.Connection.to_string) + ) l in + List.iter (warn "Live-update: %s") msgs; if t.force then begin warn "Live update forced, some domain connections may break!"; true end else begin - warn "Live update aborted, try migrating or shutting down the domain= s/toolstack"; - state :=3D { t with pending =3D false }; + warn "Live update aborted (see above for domains preventing it)"; + state :=3D { t with pending =3D false; result =3D msgs}; false end end else false + + let completed () =3D + state :=3D { !state with result =3D ["OK"] } end =20 (* packets *) diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml index e20767372f..a3be2e6bbe 100644 --- a/tools/ocaml/xenstored/store.ml +++ b/tools/ocaml/xenstored/store.ml @@ -366,7 +366,7 @@ let traversal root_node f =3D let rec _traversal path node =3D f path node; let node_path =3D Path.of_path_and_name path (Symbol.to_string node.Node= .name) in - List.iter (_traversal node_path) node.Node.children + List.iter (_traversal node_path) (List.rev node.Node.children) in _traversal [] root_node =20 diff --git a/tools/ocaml/xenstored/utils.ml b/tools/ocaml/xenstored/utils.ml index eb79bf0146..6c1603c276 100644 --- a/tools/ocaml/xenstored/utils.ml +++ b/tools/ocaml/xenstored/utils.ml @@ -115,3 +115,15 @@ let path_validate path connection_path =3D if len > !Define.path_max then raise Define.Invalid_path; =20 abs_path + +module FD : sig + type t =3D Unix.file_descr + val of_int: int -> t + val to_int : t -> int +end =3D struct + type t =3D Unix.file_descr + (* This is like Obj.magic but just for these types, + and relies on Unix.file_descr =3D int *) + external to_int : t -> int =3D "%identity" + external of_int : int -> t =3D "%identity" +end diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xen= stored.ml index 22413271fb..5893af2caa 100644 --- a/tools/ocaml/xenstored/xenstored.ml +++ b/tools/ocaml/xenstored/xenstored.ml @@ -141,9 +141,12 @@ exception Bad_format of string =20 let dump_format_header =3D "$xenstored-dump-format" =20 -let from_channel_f chan domain_f watch_f store_f =3D +let from_channel_f chan global_f socket_f domain_f watch_f store_f =3D let unhexify s =3D Utils.unhexify s in - let getpath s =3D Store.Path.of_string (Utils.unhexify s) in + let getpath s =3D + let u =3D Utils.unhexify s in + debug "Path: %s" u; + Store.Path.of_string u in let header =3D input_line chan in if header <> dump_format_header then raise (Bad_format "header"); @@ -155,6 +158,12 @@ let from_channel_f chan domain_f watch_f store_f =3D let l =3D String.split ',' line in try match l with + | "global" :: rw :: _ -> + (* there might be more parameters here, + e.g. a RO socket from a previous version: ignore it *) + global_f ~rw + | "socket" :: fd :: [] -> + socket_f ~fd:(int_of_string fd) | "dom" :: domid :: mfn :: port :: []-> domain_f (int_of_string domid) (Nativeint.of_string mfn) @@ -175,12 +184,28 @@ let from_channel_f chan domain_f watch_f store_f =3D with End_of_file -> quit :=3D true done; - () + info "Completed loading xenstore dump" =20 let from_channel store cons doms chan =3D (* don't let the permission get on our way, full perm ! *) let op =3D Store.get_ops store Perms.Connection.full_rights in - + let rwro =3D ref (None) in + let global_f ~rw =3D + let get_listen_sock sockfd =3D + let fd =3D sockfd |> int_of_string |> Utils.FD.of_int in + Unix.listen fd 1; + Some fd + in + rwro :=3D get_listen_sock rw + in + let socket_f ~fd =3D + let ufd =3D Utils.FD.of_int fd in + let is_valid =3D try (Unix.fstat ufd).Unix.st_kind =3D Unix.S_SOCK with = _ -> false in + if is_valid then + Connections.add_anonymous cons ufd + else + warn "Ignoring invalid socket FD %d" fd + in let domain_f domid mfn port =3D let ndom =3D if domid > 0 then @@ -190,28 +215,38 @@ let from_channel store cons doms chan =3D in Connections.add_domain cons ndom; in - let watch_f domid path token =3D - let con =3D Connections.find_domain cons domid in - ignore (Connections.add_watch cons con path token) + let get_con id =3D + if id < 0 then Connections.find cons (Utils.FD.of_int (-id)) + else Connections.find_domain cons id + in + let watch_f id path token =3D + ignore (Connections.add_watch cons (get_con id) path token) in let store_f path perms value =3D op.Store.write path value; op.Store.setperms path perms in - from_channel_f chan domain_f watch_f store_f + from_channel_f chan global_f socket_f domain_f watch_f store_f; + !rwro =20 let from_file store cons doms file =3D + info "Loading xenstore dump from %s" file; let channel =3D open_in file in finally (fun () -> from_channel store doms cons channel) (fun () -> close_in channel) =20 -let to_channel store cons chan =3D +let to_channel store cons rw chan =3D let hexify s =3D Utils.hexify s in =20 fprintf chan "%s\n" dump_format_header; + let fdopt =3D function None -> -1 | Some fd -> + (* systemd and utils.ml sets it close on exec *) + Unix.clear_close_on_exec fd; + Utils.FD.to_int fd in + fprintf chan "global,%d\n" (fdopt rw); =20 - (* dump connections related to domains; domid, mfn, eventchn port, watche= s *) - Connections.iter_domains cons (fun con -> Connection.dump con chan); + (* dump connections related to domains: domid, mfn, eventchn port/ socket= s, and watches *) + Connections.iter cons (fun con -> Connection.dump con chan); =20 (* dump the store *) Store.dump_fct store (fun path node -> @@ -224,9 +259,9 @@ let to_channel store cons chan =3D () =20 =20 -let to_file store cons file =3D +let to_file store cons fds file =3D let channel =3D open_out_gen [ Open_wronly; Open_creat; Open_trunc; ] 0o6= 00 file in - finally (fun () -> to_channel store cons channel) + finally (fun () -> to_channel store cons fds channel) (fun () -> close_out channel) end =20 @@ -246,13 +281,13 @@ let _ =3D ); =20 let rw_sock =3D - if cf.disable_socket then + if cf.disable_socket || cf.live_reload then None else Some (Unix.handle_unix_error Utils.create_unix_socket Define.xs_daemon_= socket) in =20 - if cf.daemonize then + if cf.daemonize && not cf.live_reload then Unixext.daemonize () else printf "Xen Storage Daemon, version %d.%d\n%!" @@ -292,10 +327,15 @@ let _ =3D List.iter (fun path -> Store.write store Perms.Connection.full_rights path "") Store.Path.speci= als; =20 + let rw_sock =3D if cf.restart && Sys.file_exists Disk.xs_daemon_database then ( - DB.from_file store domains cons Disk.xs_daemon_database; - Event.bind_dom_exc_virq eventchn - ) else ( + let rwro =3D DB.from_file store domains cons Disk.xs_daemon_database in + info "Live reload: database loaded"; + Event.bind_dom_exc_virq eventchn; + Process.LiveUpdate.completed (); + rwro + ) else ( + info "No live reload: regular startup"; if !Disk.enable then ( info "reading store from disk"; Disk.read store @@ -309,10 +349,13 @@ let _ =3D Connections.add_domain cons (Domains.create0 domains); Event.bind_dom_exc_virq eventchn ); - ); + rw_sock + ) in =20 (* required for xenstore-control to detect availability of live-update *) - Store.mkdir store Perms.Connection.full_rights (Store.Path.of_string "/to= ol"); + let toolpath =3D Store.Path.of_string "/tool" in + if not (Store.path_exists store toolpath) then + Store.mkdir store Perms.Connection.full_rights toolpath; Store.write store Perms.Connection.full_rights (Store.Path.of_string "/tool/xenstored") Sys.executable_name; =20 @@ -324,7 +367,7 @@ let _ =3D Sys.set_signal Sys.sigpipe Sys.Signal_ignore; =20 if cf.activate_access_log then begin - let post_rotate () =3D DB.to_file store cons Disk.xs_daemon_database in + let post_rotate () =3D DB.to_file store cons (None) Disk.xs_daemon_datab= ase in Logging.init_access_log post_rotate end; =20 @@ -367,6 +410,7 @@ let _ =3D let ring_scan_checker dom =3D (* no need to scan domains already marked as for processing *) if not (Domain.get_io_credit dom > 0) then + debug "Looking up domid %d" (Domain.get_id dom); let con =3D Connections.find_domain cons (Domain.get_id dom) in if not (Connection.has_more_work con) then ( Process.do_output store cons domains con; @@ -496,7 +540,7 @@ let _ =3D live_update :=3D Process.LiveUpdate.should_run cons; if !live_update || !quit then begin (* don't initiate live update if saving state fails *) - DB.to_file store cons Disk.xs_daemon_database; + DB.to_file store cons (rw_sock) Disk.xs_daemon_database; quit :=3D true; end with exc -> --=20 2.29.2