From nobody Mon Feb 9 22:20:24 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1568379182; cv=none; d=zoho.com; s=zohoarc; b=GeFaKqlJG23Oda0AwchSYukCNHbf02D0J+nP2TLTppVRPRN6h7Win8DeVcAQTwM1RlW9FjZqMt/6dJ9mkf7LHdsc7DrETcogdIGa7jUlEZmAECgfENSnTxwvUDA7IyejSEkrDgt7eHAg2N8prmKJjVyklXuMwL6ciM6Qz/8l1rU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1568379182; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To:ARC-Authentication-Results; bh=nAEaftaR7uzGRyioJHcNROD6uFVpRK3ip47JdUO6E2Q=; b=AjBEiOzkGQ1cpucs2yws29JSfJfzN2alDxpOD8VBWdx6w9tgdWJgDLdIc2i+6SLphRE8jqOCj31ipnWy+3sdThjoQhqXCAVtrEEiiZDQFkW+h4pcEXWCYTJxIkO46XeOABR9UWD6yGG3Y4LdENeCuszCetLDY9Y/t6AdR4udJ78= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1568379182279431.79001797531885; Fri, 13 Sep 2019 05:53:02 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 8519B8980FE; Fri, 13 Sep 2019 12:53:00 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 4DE0360623; Fri, 13 Sep 2019 12:53:00 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id EC1CA149F8; Fri, 13 Sep 2019 12:52:59 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x8DCqw6x028329 for ; Fri, 13 Sep 2019 08:52:58 -0400 Received: by smtp.corp.redhat.com (Postfix) id 2861F60623; Fri, 13 Sep 2019 12:52:58 +0000 (UTC) Received: from localhost (ovpn-112-45.ams2.redhat.com [10.36.112.45]) by smtp.corp.redhat.com (Postfix) with ESMTP id E04F260606; Fri, 13 Sep 2019 12:52:47 +0000 (UTC) From: marcandre.lureau@redhat.com To: libvir-list@redhat.com Date: Fri, 13 Sep 2019 16:50:47 +0400 Message-Id: <20190913125057.29333-11-marcandre.lureau@redhat.com> In-Reply-To: <20190913125057.29333-1-marcandre.lureau@redhat.com> References: <20190913125057.29333-1-marcandre.lureau@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-loop: libvir-list@redhat.com Cc: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , jtomko@redhat.com Subject: [libvirt] [PATCH v4 10/20] qemu: add vhost-user helpers X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.6.2 (mx1.redhat.com [10.5.110.67]); Fri, 13 Sep 2019 12:53:01 +0000 (UTC) From: Marc-Andr=C3=A9 Lureau Add qemuVhostUserFetchConfigs() to discover vhost-user helpers. qemuVhostUserFillDomainGPU() will find the first matching GPU helper with the required capabilities and set the associated vhost_user_binary. Signed-off-by: Marc-Andr=C3=A9 Lureau Reviewed-by: Cole Robinson --- src/conf/domain_conf.c | 2 + src/conf/domain_conf.h | 1 + src/qemu/Makefile.inc.am | 2 + src/qemu/qemu_vhost_user.c | 422 ++++++++++++++++++ src/qemu/qemu_vhost_user.h | 48 ++ tests/Makefile.am | 9 + .../etc/qemu/vhost-user/40-gpu.json | 1 + .../etc/qemu/vhost-user/50-gpu.json | 0 .../qemu/vhost-user/test-vhost-user-gpu | 11 + .../usr/share/qemu/vhost-user/30-gpu.json | 1 + .../usr/share/qemu/vhost-user/50-gpu.json | 8 + .../usr/share/qemu/vhost-user/60-gpu.json | 1 + tests/qemuvhostusertest.c | 132 ++++++ 13 files changed, 638 insertions(+) create mode 100644 src/qemu/qemu_vhost_user.c create mode 100644 src/qemu/qemu_vhost_user.h create mode 120000 tests/qemuvhostuserdata/etc/qemu/vhost-user/40-gpu.json create mode 100644 tests/qemuvhostuserdata/etc/qemu/vhost-user/50-gpu.json create mode 100755 tests/qemuvhostuserdata/usr/libexec/qemu/vhost-user/tes= t-vhost-user-gpu create mode 120000 tests/qemuvhostuserdata/usr/share/qemu/vhost-user/30-gp= u.json create mode 100644 tests/qemuvhostuserdata/usr/share/qemu/vhost-user/50-gp= u.json create mode 120000 tests/qemuvhostuserdata/usr/share/qemu/vhost-user/60-gp= u.json create mode 100644 tests/qemuvhostusertest.c diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 15fb8dea17..9e1c8babf8 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2844,6 +2844,8 @@ virDomainVideoDefClear(virDomainVideoDefPtr def) VIR_FREE(def->accel->rendernode); VIR_FREE(def->accel); VIR_FREE(def->virtio); + if (def->driver) + VIR_FREE(def->driver->vhost_user_binary); VIR_FREE(def->driver); =20 memset(def, 0, sizeof(*def)); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 16dacd4b43..cec945de54 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1423,6 +1423,7 @@ struct _virDomainVideoAccelDef { =20 struct _virDomainVideoDriverDef { virDomainVideoVGAConf vgaconf; + char *vhost_user_binary; }; =20 struct _virDomainVideoDef { diff --git a/src/qemu/Makefile.inc.am b/src/qemu/Makefile.inc.am index 7351dc0486..449550e64b 100644 --- a/src/qemu/Makefile.inc.am +++ b/src/qemu/Makefile.inc.am @@ -64,6 +64,8 @@ QEMU_DRIVER_SOURCES =3D \ qemu/qemu_slirp.h \ qemu/qemu_tpm.c \ qemu/qemu_tpm.h \ + qemu/qemu_vhost_user.c \ + qemu/qemu_vhost_user.h \ $(NULL) =20 =20 diff --git a/src/qemu/qemu_vhost_user.c b/src/qemu/qemu_vhost_user.c new file mode 100644 index 0000000000..bdc6cfb104 --- /dev/null +++ b/src/qemu/qemu_vhost_user.c @@ -0,0 +1,422 @@ +/* + * qemu_vhost_user.c: QEMU vhost-user + * + * Copyright (C) 2019 Red Hat, Inc. + * + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#include + +#include "qemu_vhost_user.h" +#include "qemu_interop_config.h" +#include "virjson.h" +#include "virlog.h" +#include "virstring.h" +#include "viralloc.h" +#include "virenum.h" + +#define VIR_FROM_THIS VIR_FROM_QEMU + +VIR_LOG_INIT("qemu.qemu_vhost_user"); + +typedef enum { + QEMU_VHOST_USER_TYPE_NONE =3D 0, + QEMU_VHOST_USER_TYPE_9P, + QEMU_VHOST_USER_TYPE_BALLOON, + QEMU_VHOST_USER_TYPE_BLOCK, + QEMU_VHOST_USER_TYPE_CAIF, + QEMU_VHOST_USER_TYPE_CONSOLE, + QEMU_VHOST_USER_TYPE_CRYPTO, + QEMU_VHOST_USER_TYPE_GPU, + QEMU_VHOST_USER_TYPE_INPUT, + QEMU_VHOST_USER_TYPE_NET, + QEMU_VHOST_USER_TYPE_RNG, + QEMU_VHOST_USER_TYPE_RPMSG, + QEMU_VHOST_USER_TYPE_RPROC_SERIAL, + QEMU_VHOST_USER_TYPE_SCSI, + QEMU_VHOST_USER_TYPE_VSOCK, + QEMU_VHOST_USER_TYPE_FS, + + QEMU_VHOST_USER_TYPE_LAST +} qemuVhostUserType; + +VIR_ENUM_DECL(qemuVhostUserType); +VIR_ENUM_IMPL(qemuVhostUserType, + QEMU_VHOST_USER_TYPE_LAST, + "", + "9p", + "balloon", + "block", + "caif", + "console", + "crypto", + "gpu", + "input", + "net", + "rng", + "rpmsg", + "rproc-serial", + "scsi", + "vsock", + "fs", +); + +typedef enum { + QEMU_VHOST_USER_GPU_FEATURE_NONE =3D 0, + QEMU_VHOST_USER_GPU_FEATURE_VIRGL, + QEMU_VHOST_USER_GPU_FEATURE_RENDER_NODE, + + QEMU_VHOST_USER_GPU_FEATURE_LAST +} qemuVhostUserGPUFeature; + +VIR_ENUM_DECL(qemuVhostUserGPUFeature); +VIR_ENUM_IMPL(qemuVhostUserGPUFeature, + QEMU_VHOST_USER_GPU_FEATURE_LAST, + "", + "virgl", + "render-node", +); + +typedef struct _qemuVhostUserGPU qemuVhostUserGPU; +typedef qemuVhostUserGPU *qemuVhostUserGPUPtr; +struct _qemuVhostUserGPU { + size_t nfeatures; + qemuVhostUserGPUFeature *features; +}; + +struct _qemuVhostUser { + /* Description intentionally not parsed. */ + qemuVhostUserType type; + char *binary; + /* Tags intentionally not parsed. */ + + union { + qemuVhostUserGPU gpu; + } capabilities; +}; + + +static void +qemuVhostUserGPUFeatureFree(qemuVhostUserGPUFeature *features) +{ + VIR_FREE(features); +} + + +VIR_DEFINE_AUTOPTR_FUNC(qemuVhostUserGPUFeature, qemuVhostUserGPUFeatureFr= ee); + + +void +qemuVhostUserFree(qemuVhostUserPtr vu) +{ + if (!vu) + return; + + if (vu->type =3D=3D QEMU_VHOST_USER_TYPE_GPU) + VIR_FREE(vu->capabilities.gpu.features); + + VIR_FREE(vu->binary); + + VIR_FREE(vu); +} + + +/* 1MiB should be enough for everybody (TM) */ +#define DOCUMENT_SIZE (1024 * 1024) + + +static int +qemuVhostUserTypeParse(const char *path, + virJSONValuePtr doc, + qemuVhostUserPtr vu) +{ + const char *type =3D virJSONValueObjectGetString(doc, "type"); + int tmp; + + VIR_DEBUG("vhost-user description path '%s' type : %s", + path, type); + + if ((tmp =3D qemuVhostUserTypeTypeFromString(type)) <=3D 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown vhost-user type: '%s'"), + type); + return -1; + } + + vu->type =3D tmp; + + return 0; +} + + +static int +qemuVhostUserBinaryParse(const char *path, + virJSONValuePtr doc, + qemuVhostUserPtr vu) +{ + const char *binary =3D virJSONValueObjectGetString(doc, "binary"); + + VIR_DEBUG("vhost-user description path '%s' binary : %s", + path, binary); + + if (VIR_STRDUP(vu->binary, binary) < 0) + return -1; + + return 0; +} + + +qemuVhostUserPtr +qemuVhostUserParse(const char *path) +{ + VIR_AUTOFREE(char *) cont =3D NULL; + VIR_AUTOPTR(virJSONValue) doc =3D NULL; + VIR_AUTOPTR(qemuVhostUser) vu =3D NULL; + qemuVhostUserPtr ret =3D NULL; + + if (virFileReadAll(path, DOCUMENT_SIZE, &cont) < 0) + return NULL; + + if (!(doc =3D virJSONValueFromString(cont))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unable to parse json file '%s'"), + path); + return NULL; + } + + if (VIR_ALLOC(vu) < 0) + return NULL; + + if (qemuVhostUserTypeParse(path, doc, vu) < 0) + return NULL; + + if (qemuVhostUserBinaryParse(path, doc, vu) < 0) + return NULL; + + VIR_STEAL_PTR(ret, vu); + return ret; +} + + +char * +qemuVhostUserFormat(qemuVhostUserPtr vu) +{ + VIR_AUTOPTR(virJSONValue) doc =3D NULL; + + if (!vu) + return NULL; + + if (!(doc =3D virJSONValueNewObject())) + return NULL; + + if (virJSONValueObjectAppendString(doc, "type", + qemuVhostUserTypeTypeToString(vu->t= ype)) < 0) + return NULL; + + if (virJSONValueObjectAppendString(doc, "binary", vu->binary) < 0) + return NULL; + + return virJSONValueToString(doc, true); +} + + +int +qemuVhostUserFetchConfigs(char ***configs, + bool privileged) +{ + return qemuInteropFetchConfigs("vhost-user", configs, privileged); +} + + +static ssize_t +qemuVhostUserFetchParsedConfigs(bool privileged, + qemuVhostUserPtr **vhostuserRet, + char ***pathsRet) +{ + VIR_AUTOSTRINGLIST paths =3D NULL; + size_t npaths; + qemuVhostUserPtr *vus =3D NULL; + size_t i; + + if (qemuVhostUserFetchConfigs(&paths, privileged) < 0) + return -1; + + npaths =3D virStringListLength((const char **)paths); + + if (VIR_ALLOC_N(vus, npaths) < 0) + return -1; + + for (i =3D 0; i < npaths; i++) { + if (!(vus[i] =3D qemuVhostUserParse(paths[i]))) + goto error; + } + + VIR_STEAL_PTR(*vhostuserRet, vus); + if (pathsRet) + VIR_STEAL_PTR(*pathsRet, paths); + return npaths; + + error: + while (i > 0) + qemuVhostUserFree(vus[--i]); + VIR_FREE(vus); + return -1; +} + + +static int +qemuVhostUserGPUFillCapabilities(qemuVhostUserPtr vu, + virJSONValuePtr doc) +{ + qemuVhostUserGPUPtr gpu =3D &vu->capabilities.gpu; + virJSONValuePtr featuresJSON; + size_t nfeatures; + size_t i; + VIR_AUTOPTR(qemuVhostUserGPUFeature) features =3D NULL; + + if (!(featuresJSON =3D virJSONValueObjectGetArray(doc, "features"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to get features from '%s'"), + vu->binary); + return -1; + } + + nfeatures =3D virJSONValueArraySize(featuresJSON); + if (VIR_ALLOC_N(features, nfeatures) < 0) + return -1; + + for (i =3D 0; i < nfeatures; i++) { + virJSONValuePtr item =3D virJSONValueArrayGet(featuresJSON, i); + const char *tmpStr =3D virJSONValueGetString(item); + int tmp; + + if ((tmp =3D qemuVhostUserGPUFeatureTypeFromString(tmpStr)) <=3D 0= ) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown feature %s"), + tmpStr); + continue; + } + + features[i] =3D tmp; + } + + VIR_STEAL_PTR(gpu->features, features); + gpu->nfeatures =3D nfeatures; + + return 0; +} + + +static bool +qemuVhostUserGPUHasFeature(qemuVhostUserGPUPtr gpu, + qemuVhostUserGPUFeature feature) +{ + size_t i; + + for (i =3D 0; i < gpu->nfeatures; i++) { + if (gpu->features[i] =3D=3D feature) + return true; + } + + return false; +} + + +int +qemuVhostUserFillDomainGPU(virQEMUDriverPtr driver, + virDomainVideoDefPtr video) +{ + qemuVhostUserPtr *vus =3D NULL; + qemuVhostUserPtr vu =3D NULL; + ssize_t nvus =3D 0; + ssize_t i; + int ret =3D -1; + + if ((nvus =3D qemuVhostUserFetchParsedConfigs(driver->privileged, + &vus, NULL)) < 0) + goto end; + + for (i =3D 0; i < nvus; i++) { + VIR_AUTOPTR(virJSONValue) doc =3D NULL; + VIR_AUTOFREE(char *) output =3D NULL; + VIR_AUTOPTR(virCommand) cmd =3D NULL; + + vu =3D vus[i]; + if (vu->type !=3D QEMU_VHOST_USER_TYPE_GPU) + continue; + + cmd =3D virCommandNewArgList(vu->binary, "--print-capabilities", N= ULL); + virCommandSetOutputBuffer(cmd, &output); + if (virCommandRun(cmd, NULL) < 0) + continue; + + if (!(doc =3D virJSONValueFromString(output))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unable to parse json capabilities '%s'"), + vu->binary); + continue; + } + + if (qemuVhostUserGPUFillCapabilities(vu, doc) < 0) + continue; + + if (video->accel) { + if (video->accel->accel3d && + !qemuVhostUserGPUHasFeature(&vu->capabilities.gpu, + QEMU_VHOST_USER_GPU_FEATURE_VI= RGL)) + continue; + + if (video->accel->rendernode && + !qemuVhostUserGPUHasFeature(&vu->capabilities.gpu, + QEMU_VHOST_USER_GPU_FEATURE_RE= NDER_NODE)) + continue; + } + + if (!video->driver && VIR_ALLOC(video->driver) < 0) + goto end; + + VIR_FREE(video->driver->vhost_user_binary); + if (VIR_STRDUP(video->driver->vhost_user_binary, vu->binary) < 0) + goto end; + + break; + } + + if (i =3D=3D nvus) { + virReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Unable to find a satisfying vhost-user-gpu")); + goto end; + } + + if (!video->accel && VIR_ALLOC(video->accel) < 0) + goto end; + + if (!video->accel->rendernode && + qemuVhostUserGPUHasFeature(&vu->capabilities.gpu, + QEMU_VHOST_USER_GPU_FEATURE_RENDER_NODE= )) { + video->accel->rendernode =3D virHostGetDRMRenderNode(); + if (!video->accel->rendernode) + goto end; + } + + ret =3D 0; + + end: + for (i =3D 0; i < nvus; i++) + qemuVhostUserFree(vus[i]); + VIR_FREE(vus); + return ret; +} diff --git a/src/qemu/qemu_vhost_user.h b/src/qemu/qemu_vhost_user.h new file mode 100644 index 0000000000..76701dd1fa --- /dev/null +++ b/src/qemu/qemu_vhost_user.h @@ -0,0 +1,48 @@ +/* + * qemu_vhost_user.h: QEMU vhost-user + * + * Copyright (C) 2019 Red Hat, Inc. + * + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#pragma once + +#include "domain_conf.h" +#include "qemu_conf.h" +#include "virautoclean.h" +#include "virarch.h" + +typedef struct _qemuVhostUser qemuVhostUser; +typedef qemuVhostUser *qemuVhostUserPtr; + +void +qemuVhostUserFree(qemuVhostUserPtr fw); + +VIR_DEFINE_AUTOPTR_FUNC(qemuVhostUser, qemuVhostUserFree); + +qemuVhostUserPtr +qemuVhostUserParse(const char *path); + +char * +qemuVhostUserFormat(qemuVhostUserPtr fw); + +int +qemuVhostUserFetchConfigs(char ***configs, + bool privileged); + +int +qemuVhostUserFillDomainGPU(virQEMUDriverPtr driver, + virDomainVideoDefPtr video); diff --git a/tests/Makefile.am b/tests/Makefile.am index 8c343857a5..9093b35bbc 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -291,6 +291,7 @@ test_programs +=3D qemuxml2argvtest qemuxml2xmltest \ qemumigparamstest \ qemusecuritytest \ qemufirmwaretest \ + qemuvhostusertest \ $(NULL) test_helpers +=3D qemucapsprobe test_libraries +=3D libqemumonitortestutils.la \ @@ -693,6 +694,13 @@ qemufirmwaretest_SOURCES =3D \ $(NULL) qemufirmwaretest_LDADD =3D $(qemu_LDADDS) =20 +qemuvhostusertest_SOURCES =3D \ + qemuvhostusertest.c \ + testutils.h testutils.c \ + virfilewrapper.c virfilewrapper.h \ + $(NULL) +qemuvhostusertest_LDADD =3D $(qemu_LDADDS) + else ! WITH_QEMU EXTRA_DIST +=3D qemuxml2argvtest.c qemuxml2xmltest.c \ qemudomaincheckpointxml2xmltest.c qemudomainsnapshotxml2xmltest.c \ @@ -707,6 +715,7 @@ EXTRA_DIST +=3D qemuxml2argvtest.c qemuxml2xmltest.c \ qemusecuritytest.c qemusecuritytest.h \ qemusecuritymock.c \ qemufirmwaretest.c \ + qemuvhostusertest.c \ $(QEMUMONITORTESTUTILS_SOURCES) endif ! WITH_QEMU =20 diff --git a/tests/qemuvhostuserdata/etc/qemu/vhost-user/40-gpu.json b/test= s/qemuvhostuserdata/etc/qemu/vhost-user/40-gpu.json new file mode 120000 index 0000000000..aa93864aa7 --- /dev/null +++ b/tests/qemuvhostuserdata/etc/qemu/vhost-user/40-gpu.json @@ -0,0 +1 @@ +../../../usr/share/qemu/vhost-user/50-gpu.json \ No newline at end of file diff --git a/tests/qemuvhostuserdata/etc/qemu/vhost-user/50-gpu.json b/test= s/qemuvhostuserdata/etc/qemu/vhost-user/50-gpu.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qemuvhostuserdata/usr/libexec/qemu/vhost-user/test-vhost= -user-gpu b/tests/qemuvhostuserdata/usr/libexec/qemu/vhost-user/test-vhost-= user-gpu new file mode 100755 index 0000000000..a2c2ee0713 --- /dev/null +++ b/tests/qemuvhostuserdata/usr/libexec/qemu/vhost-user/test-vhost-user-g= pu @@ -0,0 +1,11 @@ +#!/bin/sh + +cat < + +#include + +#include "testutils.h" +#include "virfilewrapper.h" +#include "qemu/qemu_vhost_user.h" +#include "configmake.h" + +#define VIR_FROM_THIS VIR_FROM_QEMU + +/* A very basic test. Parse given JSON vhostuser description into + * an internal structure, format it back and compare with the + * contents of the file (minus some keys that are not parsed). + */ +static int +testParseFormatVU(const void *opaque) +{ + const char *filename =3D opaque; + VIR_AUTOFREE(char *) path =3D NULL; + VIR_AUTOPTR(qemuVhostUser) vu =3D NULL; + VIR_AUTOFREE(char *) buf =3D NULL; + VIR_AUTOPTR(virJSONValue) json =3D NULL; + VIR_AUTOFREE(char *) expected =3D NULL; + VIR_AUTOFREE(char *) actual =3D NULL; + + if (virAsprintf(&path, "%s/qemuvhostuserdata/%s", + abs_srcdir, filename) < 0) + return -1; + + if (!(vu =3D qemuVhostUserParse(path))) + return -1; + + if (virFileReadAll(path, + 1024 * 1024, /* 1MiB */ + &buf) < 0) + return -1; + + if (!(json =3D virJSONValueFromString(buf))) + return -1; + + /* Description and tags are not parsed. */ + if (virJSONValueObjectRemoveKey(json, "description", NULL) < 0 || + virJSONValueObjectRemoveKey(json, "tags", NULL) < 0) + return -1; + + if (!(expected =3D virJSONValueToString(json, true))) + return -1; + + if (!(actual =3D qemuVhostUserFormat(vu))) + return -1; + + return virTestCompareToString(expected, actual); +} + + +static int +testVUPrecedence(const void *opaque ATTRIBUTE_UNUSED) +{ + VIR_AUTOFREE(char *) fakehome =3D NULL; + VIR_AUTOSTRINGLIST vuList =3D NULL; + size_t nvuList; + size_t i; + const char *expected[] =3D { + PREFIX "/share/qemu/vhost-user/30-gpu.json", + SYSCONFDIR "/qemu/vhost-user/40-gpu.json", + PREFIX "/share/qemu/vhost-user/60-gpu.json", + }; + const size_t nexpected =3D ARRAY_CARDINALITY(expected); + + if (VIR_STRDUP(fakehome, abs_srcdir "/qemuvhostuserdata/home/user/.con= fig") < 0) + return -1; + + setenv("XDG_CONFIG_HOME", fakehome, 1); + + if (qemuVhostUserFetchConfigs(&vuList, false) < 0) + return -1; + + if (!vuList) { + fprintf(stderr, "Expected a non-NULL result, but got a NULL result= \n"); + return -1; + } + + nvuList =3D virStringListLength((const char **)vuList); + + for (i =3D 0; i < MAX(nvuList, nexpected); i++) { + const char *e =3D i < nexpected ? expected[i] : NULL; + const char *f =3D i < nvuList ? vuList[i] : NULL; + + if (STRNEQ_NULLABLE(e, f)) { + fprintf(stderr, + "Unexpected path (i=3D%zu). Expected %s got %s \n", + i, NULLSTR(e), NULLSTR(f)); + return -1; + } + } + + return 0; +} + + +static int +mymain(void) +{ + int ret =3D 0; + + virFileWrapperAddPrefix(SYSCONFDIR "/qemu/vhost-user", + abs_srcdir "/qemuvhostuserdata/etc/qemu/vhost-= user"); + virFileWrapperAddPrefix(PREFIX "/share/qemu/vhost-user", + abs_srcdir "/qemuvhostuserdata/usr/share/qemu/= vhost-user"); + virFileWrapperAddPrefix("/home/user/.config/qemu/vhost-user", + abs_srcdir "/qemuvhostuserdata/home/user/.conf= ig/qemu/vhost-user"); + +#define DO_PARSE_TEST(filename) \ + do { \ + if (virTestRun("QEMU vhost-user " filename, \ + testParseFormatVU, filename) < 0) \ + ret =3D -1; \ + } while (0) + + DO_PARSE_TEST("usr/share/qemu/vhost-user/50-gpu.json"); + + if (virTestRun("QEMU vhost-user precedence test", testVUPrecedence, NU= LL) < 0) + ret =3D -1; + + virFileWrapperClearPrefixes(); + + return ret =3D=3D 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + + +VIR_TEST_MAIN(mymain) --=20 2.23.0 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list