[RFC] Add basic driver for the Cloud-Hypervisor
by William Douglas
This patch adds support for the following initial VM actions using the
Cloud-Hypervsior API:
* vm.create
* vm.delete
* vm.boot
* vm.shutdown
* vm.reboot
* vm.pause
* vm.resume
To use the Cloud-Hypervisor driver, the v0.9.0 (the as of now current)
release of Cloud-Hypervisor is required to be installed.
Signed-off-by: William Douglas <william.douglas(a)intel.com>
---
include/libvirt/virterror.h | 1 +
libvirt.spec.in | 32 ++
meson.build | 5 +
meson_options.txt | 1 +
po/POTFILES.in | 5 +
src/ch/ch_conf.c | 239 +++++++++
src/ch/ch_conf.h | 87 ++++
src/ch/ch_domain.c | 219 +++++++++
src/ch/ch_domain.h | 68 +++
src/ch/ch_driver.c | 937 ++++++++++++++++++++++++++++++++++++
src/ch/ch_driver.h | 24 +
src/ch/ch_monitor.c | 796 ++++++++++++++++++++++++++++++
src/ch/ch_monitor.h | 62 +++
src/ch/ch_process.c | 125 +++++
src/ch/ch_process.h | 30 ++
src/ch/meson.build | 44 ++
src/conf/domain_conf.c | 1 +
src/conf/domain_conf.h | 1 +
src/meson.build | 1 +
src/qemu/qemu_command.c | 1 +
src/remote/remote_daemon.c | 4 +
src/util/virerror.c | 1 +
tools/virsh.c | 3 +
23 files changed, 2687 insertions(+)
create mode 100644 src/ch/ch_conf.c
create mode 100644 src/ch/ch_conf.h
create mode 100644 src/ch/ch_domain.c
create mode 100644 src/ch/ch_domain.h
create mode 100644 src/ch/ch_driver.c
create mode 100644 src/ch/ch_driver.h
create mode 100644 src/ch/ch_monitor.c
create mode 100644 src/ch/ch_monitor.h
create mode 100644 src/ch/ch_process.c
create mode 100644 src/ch/ch_process.h
create mode 100644 src/ch/meson.build
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index 0f1c32283d..21faa8f128 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -136,6 +136,7 @@ typedef enum {
VIR_FROM_TPM = 70, /* Error from TPM */
VIR_FROM_BPF = 71, /* Error from BPF code */
+ VIR_FROM_CH = 72, /* Error from Cloud-Hypervisor driver */
# ifdef VIR_ENUM_SENTINELS
VIR_ERR_DOMAIN_LAST
diff --git a/libvirt.spec.in b/libvirt.spec.in
index bb74443484..66edb1fa76 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -14,6 +14,7 @@
# The hypervisor drivers that run in libvirtd
%define with_qemu 0%{!?_without_qemu:1}
+%define with_ch 0%{!?_without_ch:1}
%define with_lxc 0%{!?_without_lxc:1}
%define with_libxl 0%{!?_without_libxl:1}
%define with_vbox 0%{!?_without_vbox:1}
@@ -232,6 +233,9 @@ Requires: libvirt-daemon-driver-lxc = %{version}-%{release}
%if %{with_qemu}
Requires: libvirt-daemon-driver-qemu = %{version}-%{release}
%endif
+%if %{with_ch}
+Requires: libvirt-daemon-driver-ch = %{version}-%{release}
+%endif
# We had UML driver, but we've removed it.
Obsoletes: libvirt-daemon-driver-uml <= 5.0.0
Obsoletes: libvirt-daemon-uml <= 5.0.0
@@ -744,6 +748,20 @@ QEMU
%endif
+%if %{with_ch}
+%package daemon-driver-ch
+Summary: Cloud-Hypervisor driver plugin for the libvirtd daemon
+Requires: libvirt-daemon = %{version}-%{release}
+Requires: libvirt-libs = %{version}-%{release}
+Requires: /usr/bin/cloud-hypervisor
+
+%description daemon-driver-ch
+The Cloud-Hypervisor driver plugin for the libvirtd daemon,
+providing an implementation of the hypervisor driver APIs
+using Cloud-Hypervisor
+%endif
+
+
%if %{with_lxc}
%package daemon-driver-lxc
Summary: LXC driver plugin for the libvirtd daemon
@@ -1001,6 +1019,12 @@ exit 1
%define arg_qemu -Ddriver_qemu=disabled
%endif
+%if %{with_ch}
+ %define arg_ch -Ddriver_ch=enabled
+%else
+ %define arg_ch -Ddriver_ch=disabled
+%endif
+
%if %{with_openvz}
%define arg_openvz -Ddriver_openvz=enabled
%else
@@ -1132,6 +1156,7 @@ export SOURCE_DATE_EPOCH=$(stat --printf='%Y' %{_specdir}/%{name}.spec)
%meson \
-Drunstatedir=%{_rundir} \
%{?arg_qemu} \
+ %{?arg_ch} \
%{?arg_openvz} \
%{?arg_lxc} \
%{?arg_vbox} \
@@ -1739,6 +1764,13 @@ exit 0
%{_mandir}/man1/virt-qemu-run.1*
%endif
+%if %{with_ch}
+%files daemon-driver-ch
+%dir %attr(0700, root, root) %{_localstatedir}/log/libvirt/ch/
+%ghost %dir %{_rundir}/libvirt/ch/
+%{_libdir}/%{name}/connection-driver/libvirt_driver_ch.so
+%endif
+
%if %{with_lxc}
%files daemon-driver-lxc
%config(noreplace) %{_sysconfdir}/sysconfig/virtlxcd
diff --git a/meson.build b/meson.build
index dabd4196e6..a6759cb051 100644
--- a/meson.build
+++ b/meson.build
@@ -1722,6 +1722,10 @@ elif get_option('driver_lxc').enabled()
error('linux and remote_driver are required for LXC')
endif
+if not get_option('driver_ch').disabled() and host_machine.system() == 'linux' and conf.has('WITH_LIBVIRTD')
+ conf.set('WITH_CH', 1)
+endif
+
# there's no use compiling the network driver without the libvirt
# daemon, nor compiling it for macOS, where it breaks the compile
if not get_option('driver_network').disabled() and conf.has('WITH_LIBVIRTD') and host_machine.system() != 'darwin'
@@ -2369,6 +2373,7 @@ driver_summary = {
'VBox': conf.has('WITH_VBOX'),
'libxl': conf.has('WITH_LIBXL'),
'LXC': conf.has('WITH_LXC'),
+ 'Cloud-Hypervisor': conf.has('WITH_CH'),
'ESX': conf.has('WITH_ESX'),
'Hyper-V': conf.has('WITH_HYPERV'),
'vz': conf.has('WITH_VZ'),
diff --git a/meson_options.txt b/meson_options.txt
index 79554c3186..bfb3e79e1a 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -55,6 +55,7 @@ option('driver_interface', type: 'feature', value: 'auto', description: 'host in
option('driver_libvirtd', type: 'feature', value: 'auto', description: 'libvirtd driver')
option('driver_libxl', type: 'feature', value: 'auto', description: 'libxenlight driver')
option('driver_lxc', type: 'feature', value: 'auto', description: 'Linux Container driver')
+option('driver_ch', type: 'feature', value: 'auto', description: 'Cloud-Hypervisor driver')
option('driver_network', type: 'feature', value: 'auto', description: 'virtual network driver')
option('driver_openvz', type: 'feature', value: 'auto', description: 'OpenVZ driver')
option('driver_qemu', type: 'feature', value: 'auto', description: 'QEMU/KVM driver')
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3d6c20c55f..b12a1b1e56 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -18,6 +18,11 @@
@SRCDIR(a)src/bhyve/bhyve_monitor.c
@SRCDIR(a)src/bhyve/bhyve_parse_command.c
@SRCDIR(a)src/bhyve/bhyve_process.c
+@SRCDIR(a)src/ch/ch_conf.c
+@SRCDIR(a)src/ch/ch_domain.c
+@SRCDIR(a)src/ch/ch_driver.c
+@SRCDIR(a)src/ch/ch_monitor.c
+@SRCDIR(a)src/ch/ch_process.c
@SRCDIR(a)src/conf/backup_conf.c
@SRCDIR(a)src/conf/capabilities.c
@SRCDIR(a)src/conf/checkpoint_conf.c
diff --git a/src/ch/ch_conf.c b/src/ch/ch_conf.c
new file mode 100644
index 0000000000..8769b0f7e2
--- /dev/null
+++ b/src/ch/ch_conf.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright Intel Corp. 2020
+ *
+ * ch_driver.h: header file for Cloud-Hypervisor driver functions
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "configmake.h"
+#include "viralloc.h"
+#include "vircommand.h"
+#include "virlog.h"
+#include "virobject.h"
+#include "virstring.h"
+#include "virutil.h"
+
+#include "ch_conf.h"
+#include "ch_domain.h"
+
+#define VIR_FROM_THIS VIR_FROM_CH
+
+VIR_LOG_INIT("ch.ch_conf");
+
+static virClassPtr virCHDriverConfigClass;
+static void virCHDriverConfigDispose(void *obj);
+
+static int virCHConfigOnceInit(void)
+{
+ if (!VIR_CLASS_NEW(virCHDriverConfig, virClassForObject()))
+ return -1;
+
+ return 0;
+}
+
+VIR_ONCE_GLOBAL_INIT(virCHConfig);
+
+
+/* Functions */
+virCapsPtr virCHDriverCapsInit(void)
+{
+ virCapsPtr caps;
+ virCapsGuestPtr guest;
+
+ if ((caps = virCapabilitiesNew(virArchFromHost(),
+ false, false)) == NULL)
+ goto cleanup;
+
+ if (!(caps->host.numa = virCapabilitiesHostNUMANewHost()))
+ goto cleanup;
+
+ if (virCapabilitiesInitCaches(caps) < 0)
+ goto cleanup;
+
+ if ((guest = virCapabilitiesAddGuest(caps,
+ VIR_DOMAIN_OSTYPE_HVM,
+ caps->host.arch,
+ NULL,
+ NULL,
+ 0,
+ NULL)) == NULL)
+ goto cleanup;
+
+ if (virCapabilitiesAddGuestDomain(guest,
+ VIR_DOMAIN_VIRT_CH,
+ NULL,
+ NULL,
+ 0,
+ NULL) == NULL)
+ goto cleanup;
+
+ return caps;
+
+ cleanup:
+ virObjectUnref(caps);
+ return NULL;
+}
+
+/**
+ * virCHDriverGetCapabilities:
+ *
+ * Get a reference to the virCapsPtr instance for the
+ * driver. If @refresh is true, the capabilities will be
+ * rebuilt first
+ *
+ * The caller must release the reference with virObjetUnref
+ *
+ * Returns: a reference to a virCapsPtr instance or NULL
+ */
+virCapsPtr virCHDriverGetCapabilities(virCHDriverPtr driver,
+ bool refresh)
+{
+ virCapsPtr ret;
+ if (refresh) {
+ virCapsPtr caps = NULL;
+ if ((caps = virCHDriverCapsInit()) == NULL)
+ return NULL;
+
+ chDriverLock(driver);
+ virObjectUnref(driver->caps);
+ driver->caps = caps;
+ } else {
+ chDriverLock(driver);
+ }
+
+ ret = virObjectRef(driver->caps);
+ chDriverUnlock(driver);
+ return ret;
+}
+
+virDomainXMLOptionPtr
+chDomainXMLConfInit(virCHDriverPtr driver)
+{
+ virCHDriverDomainDefParserConfig.priv = driver;
+ return virDomainXMLOptionNew(&virCHDriverDomainDefParserConfig,
+ &virCHDriverPrivateDataCallbacks,
+ NULL, NULL, NULL);
+}
+
+virCHDriverConfigPtr
+virCHDriverConfigNew(void)
+{
+ virCHDriverConfigPtr cfg;
+
+ if (virCHConfigInitialize() < 0)
+ return NULL;
+
+ if (!(cfg = virObjectNew(virCHDriverConfigClass)))
+ return NULL;
+
+ cfg->stateDir = g_strdup(CH_STATE_DIR);
+ cfg->logDir = g_strdup(CH_LOG_DIR);
+
+ return cfg;
+}
+
+virCHDriverConfigPtr virCHDriverGetConfig(virCHDriverPtr driver)
+{
+ virCHDriverConfigPtr cfg;
+ chDriverLock(driver);
+ cfg = virObjectRef(driver->config);
+ chDriverUnlock(driver);
+ return cfg;
+}
+
+static void
+virCHDriverConfigDispose(void *obj)
+{
+ virCHDriverConfigPtr cfg = obj;
+
+ VIR_FREE(cfg->stateDir);
+ VIR_FREE(cfg->logDir);
+}
+
+static int
+chExtractVersionInfo(int *retversion)
+{
+ int ret = -1;
+ unsigned long version;
+ char *help = NULL;
+ char *tmp;
+ virCommandPtr cmd = virCommandNewArgList(CH_CMD, "--version", NULL);
+
+ if (retversion)
+ *retversion = 0;
+
+ virCommandAddEnvString(cmd, "LC_ALL=C");
+ virCommandSetOutputBuffer(cmd, &help);
+
+ if (virCommandRun(cmd, NULL) < 0)
+ goto cleanup;
+
+ tmp = help;
+
+ /* expected format: cloud-hypervisor v<major>.<minor>.<micro> */
+ if ((tmp = STRSKIP(tmp, "cloud-hypervisor v")) == NULL)
+ goto cleanup;
+
+ if (virParseVersionString(tmp, &version, false) < 0)
+ goto cleanup;
+
+ // v0.9.0 is the minimum supported version
+ if ((unsigned int)(version / 1000000) < 1) {
+ if (((unsigned int)((unsigned long)(version % 1000000)) / 1000) < 9) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Cloud-Hypervisor version is too old (v0.9.0 is the minimum supported version)"));
+ goto cleanup;
+ }
+ }
+
+
+ if (retversion)
+ *retversion = version;
+
+ ret = 0;
+
+ cleanup:
+ virCommandFree(cmd);
+ VIR_FREE(help);
+
+ return ret;
+}
+
+int chExtractVersion(virCHDriverPtr driver)
+{
+ if (driver->version > 0)
+ return 0;
+
+ if (chExtractVersionInfo(&driver->version) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Could not extract Cloud-Hypervisor version"));
+ return -1;
+ }
+
+ return 0;
+}
+
+int chStrToInt(const char *str)
+{
+ int val;
+
+ if (virStrToLong_i(str, NULL, 10, &val) < 0)
+ return 0;
+
+ return val;
+}
diff --git a/src/ch/ch_conf.h b/src/ch/ch_conf.h
new file mode 100644
index 0000000000..04334130f7
--- /dev/null
+++ b/src/ch/ch_conf.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright Intel Corp. 2020
+ *
+ * ch_driver.h: header file for Cloud-Hypervisor driver functions
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "virdomainobjlist.h"
+#include "virthread.h"
+
+#define CH_DRIVER_NAME "CH"
+#define CH_CMD "cloud-hypervisor"
+
+#define CH_STATE_DIR RUNSTATEDIR "/libvirt/ch"
+#define CH_LOG_DIR LOCALSTATEDIR "/log/libvirt/ch"
+
+typedef struct _virCHDriver virCHDriver;
+typedef virCHDriver *virCHDriverPtr;
+
+typedef struct _virCHDriverConfig virCHDriverConfig;
+typedef virCHDriverConfig *virCHDriverConfigPtr;
+
+struct _virCHDriverConfig {
+ virObject parent;
+
+ char *stateDir;
+ char *logDir;
+};
+
+struct _virCHDriver
+{
+ virMutex lock;
+
+ /* Require lock to get a reference on the object,
+ * lockless access thereafter */
+ virCapsPtr caps;
+
+ /* Immutable pointer, Immutable object */
+ virDomainXMLOptionPtr xmlopt;
+
+ /* Immutable pointer, self-locking APIs */
+ virDomainObjListPtr domains;
+
+ /* Cloud-Hypervisor version */
+ int version;
+
+ /* Require lock to get reference on 'config',
+ * then lockless thereafter */
+ virCHDriverConfigPtr config;
+
+ /* pid file FD, ensures two copies of the driver can't use the same root */
+ int lockFD;
+};
+
+virCapsPtr virCHDriverCapsInit(void);
+virCapsPtr virCHDriverGetCapabilities(virCHDriverPtr driver,
+ bool refresh);
+virDomainXMLOptionPtr chDomainXMLConfInit(virCHDriverPtr driver);
+virCHDriverConfigPtr virCHDriverConfigNew(void);
+virCHDriverConfigPtr virCHDriverGetConfig(virCHDriverPtr driver);
+int chExtractVersion(virCHDriverPtr driver);
+int chStrToInt(const char *str);
+
+static inline void chDriverLock(virCHDriverPtr driver)
+{
+ virMutexLock(&driver->lock);
+}
+
+static inline void chDriverUnlock(virCHDriverPtr driver)
+{
+ virMutexUnlock(&driver->lock);
+}
diff --git a/src/ch/ch_domain.c b/src/ch/ch_domain.c
new file mode 100644
index 0000000000..a46641d50d
--- /dev/null
+++ b/src/ch/ch_domain.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright Intel Corp. 2020
+ *
+ * ch_driver.h: header file for Cloud-Hypervisor driver functions
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "ch_domain.h"
+#include "viralloc.h"
+#include "virlog.h"
+#include "virtime.h"
+
+#define VIR_FROM_THIS VIR_FROM_CH
+
+VIR_ENUM_IMPL(virCHDomainJob,
+ CH_JOB_LAST,
+ "none",
+ "query",
+ "destroy",
+ "modify",
+);
+
+VIR_LOG_INIT("ch.ch_domain");
+
+static int
+virCHDomainObjInitJob(virCHDomainObjPrivatePtr priv)
+{
+ memset(&priv->job, 0, sizeof(priv->job));
+
+ if (virCondInit(&priv->job.cond) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void
+virCHDomainObjResetJob(virCHDomainObjPrivatePtr priv)
+{
+ struct virCHDomainJobObj *job = &priv->job;
+
+ job->active = CH_JOB_NONE;
+ job->owner = 0;
+}
+
+static void
+virCHDomainObjFreeJob(virCHDomainObjPrivatePtr priv)
+{
+ ignore_value(virCondDestroy(&priv->job.cond));
+}
+
+/*
+ * obj must be locked before calling, virCHDriverPtr must NOT be locked
+ *
+ * This must be called by anything that will change the VM state
+ * in any way
+ *
+ * Upon successful return, the object will have its ref count increased.
+ * Successful calls must be followed by EndJob eventually.
+ */
+int
+virCHDomainObjBeginJob(virDomainObjPtr obj, enum virCHDomainJob job)
+{
+ virCHDomainObjPrivatePtr priv = obj->privateData;
+ unsigned long long now;
+ unsigned long long then;
+
+ if (virTimeMillisNow(&now) < 0)
+ return -1;
+ then = now + CH_JOB_WAIT_TIME;
+
+ while (priv->job.active) {
+ VIR_DEBUG("Wait normal job condition for starting job: %s",
+ virCHDomainJobTypeToString(job));
+ if (virCondWaitUntil(&priv->job.cond, &obj->parent.lock, then) < 0)
+ goto error;
+ }
+
+ virCHDomainObjResetJob(priv);
+
+ VIR_DEBUG("Starting job: %s", virCHDomainJobTypeToString(job));
+ priv->job.active = job;
+ priv->job.owner = virThreadSelfID();
+
+ return 0;
+
+ error:
+ VIR_WARN("Cannot start job (%s) for domain %s;"
+ " current job is (%s) owned by (%d)",
+ virCHDomainJobTypeToString(job),
+ obj->def->name,
+ virCHDomainJobTypeToString(priv->job.active),
+ priv->job.owner);
+
+ if (errno == ETIMEDOUT)
+ virReportError(VIR_ERR_OPERATION_TIMEOUT,
+ "%s", _("cannot acquire state change lock"));
+ else
+ virReportSystemError(errno,
+ "%s", _("cannot acquire job mutex"));
+ return -1;
+}
+
+/*
+ * obj must be locked and have a reference before calling
+ *
+ * To be called after completing the work associated with the
+ * earlier virCHDomainBeginJob() call
+ */
+void
+virCHDomainObjEndJob(virDomainObjPtr obj)
+{
+ virCHDomainObjPrivatePtr priv = obj->privateData;
+ enum virCHDomainJob job = priv->job.active;
+
+ VIR_DEBUG("Stopping job: %s",
+ virCHDomainJobTypeToString(job));
+
+ virCHDomainObjResetJob(priv);
+ virCondSignal(&priv->job.cond);
+}
+
+static void *
+virCHDomainObjPrivateAlloc(void *opaque G_GNUC_UNUSED)
+{
+ virCHDomainObjPrivatePtr priv;
+
+ if (VIR_ALLOC(priv) < 0)
+ return NULL;
+
+ if (virCHDomainObjInitJob(priv) < 0) {
+ VIR_FREE(priv);
+ return NULL;
+ }
+
+ return priv;
+}
+
+static void
+virCHDomainObjPrivateFree(void *data)
+{
+ virCHDomainObjPrivatePtr priv = data;
+
+ virCHDomainObjFreeJob(priv);
+ VIR_FREE(priv);
+}
+
+static int
+virCHDomainObjPrivateXMLFormat(virBufferPtr buf,
+ virDomainObjPtr vm)
+{
+ virCHDomainObjPrivatePtr priv = vm->privateData;
+ virBufferAsprintf(buf, "<init pid='%lld'/>\n",
+ (long long)priv->initpid);
+
+ return 0;
+}
+
+static int
+virCHDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt,
+ virDomainObjPtr vm,
+ virDomainDefParserConfigPtr config G_GNUC_UNUSED)
+{
+ virCHDomainObjPrivatePtr priv = vm->privateData;
+ long long thepid;
+
+ if (virXPathLongLong("string(./init[1]/@pid)", ctxt, &thepid) < 0) {
+ VIR_WARN("Failed to load init pid from state %s",
+ virGetLastErrorMessage());
+ priv->initpid = 0;
+ } else {
+ priv->initpid = thepid;
+ }
+
+ return 0;
+}
+
+virDomainXMLPrivateDataCallbacks virCHDriverPrivateDataCallbacks = {
+ .alloc = virCHDomainObjPrivateAlloc,
+ .free = virCHDomainObjPrivateFree,
+ .format = virCHDomainObjPrivateXMLFormat,
+ .parse = virCHDomainObjPrivateXMLParse,
+};
+
+static int
+virCHDomainDefPostParse(virDomainDefPtr def,
+ unsigned int parseFlags G_GNUC_UNUSED,
+ void *opaque,
+ void *parseOpaque G_GNUC_UNUSED)
+{
+ virCHDriverPtr driver = opaque;
+ g_autoptr(virCaps) caps = virCHDriverGetCapabilities(driver, false);
+ if (!caps)
+ return -1;
+ if (!virCapabilitiesDomainSupported(caps, def->os.type,
+ def->os.arch,
+ def->virtType))
+ return -1;
+
+ return 0;
+}
+
+virDomainDefParserConfig virCHDriverDomainDefParserConfig = {
+ .domainPostParseCallback = virCHDomainDefPostParse,
+};
diff --git a/src/ch/ch_domain.h b/src/ch/ch_domain.h
new file mode 100644
index 0000000000..144f147173
--- /dev/null
+++ b/src/ch/ch_domain.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright Intel Corp. 2020
+ *
+ * ch_driver.h: header file for Cloud-Hypervisor driver functions
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "ch_conf.h"
+#include "ch_monitor.h"
+
+/* Give up waiting for mutex after 30 seconds */
+#define CH_JOB_WAIT_TIME (1000ull * 30)
+
+/* Only 1 job is allowed at any time
+ * A job includes *all* ch.so api, even those just querying
+ * information, not merely actions */
+
+enum virCHDomainJob {
+ CH_JOB_NONE = 0, /* Always set to 0 for easy if (jobActive) conditions */
+ CH_JOB_QUERY, /* Doesn't change any state */
+ CH_JOB_DESTROY, /* Destroys the domain (cannot be masked out) */
+ CH_JOB_MODIFY, /* May change state */
+ CH_JOB_LAST
+};
+VIR_ENUM_DECL(virCHDomainJob);
+
+
+struct virCHDomainJobObj {
+ virCond cond; /* Use to coordinate jobs */
+ enum virCHDomainJob active; /* Currently running job */
+ int owner; /* Thread which set current job */
+};
+
+
+typedef struct _virCHDomainObjPrivate virCHDomainObjPrivate;
+typedef virCHDomainObjPrivate *virCHDomainObjPrivatePtr;
+struct _virCHDomainObjPrivate {
+ pid_t initpid;
+
+ struct virCHDomainJobObj job;
+
+ virCHMonitorPtr monitor;
+};
+
+extern virDomainXMLPrivateDataCallbacks virCHDriverPrivateDataCallbacks;
+extern virDomainDefParserConfig virCHDriverDomainDefParserConfig;
+
+int
+virCHDomainObjBeginJob(virDomainObjPtr obj, enum virCHDomainJob job)
+ G_GNUC_WARN_UNUSED_RESULT;
+
+void
+virCHDomainObjEndJob(virDomainObjPtr obj);
diff --git a/src/ch/ch_driver.c b/src/ch/ch_driver.c
new file mode 100644
index 0000000000..e5b027f71f
--- /dev/null
+++ b/src/ch/ch_driver.c
@@ -0,0 +1,937 @@
+/*
+ * Copyright Intel Corp. 2020
+ *
+ * ch_driver.h: header file for Cloud-Hypervisor driver functions
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "ch_conf.h"
+#include "ch_domain.h"
+#include "ch_driver.h"
+#include "ch_monitor.h"
+#include "ch_process.h"
+#include "datatypes.h"
+#include "driver.h"
+#include "viraccessapicheck.h"
+#include "viralloc.h"
+#include "virbuffer.h"
+#include "vircommand.h"
+#include "virerror.h"
+#include "virfile.h"
+#include "virlog.h"
+#include "virnetdevtap.h"
+#include "virobject.h"
+#include "virstring.h"
+#include "virtypedparam.h"
+#include "viruri.h"
+#include "virutil.h"
+#include "viruuid.h"
+
+#define VIR_FROM_THIS VIR_FROM_CH
+
+VIR_LOG_INIT("ch.ch_driver");
+
+static int chStateInitialize(bool privileged,
+ const char *root,
+ virStateInhibitCallback callback,
+ void *opaque);
+static int chStateCleanup(void);
+virCHDriverPtr ch_driver = NULL;
+
+static virDomainObjPtr
+chDomObjFromDomain(virDomainPtr domain)
+{
+ virDomainObjPtr vm;
+ virCHDriverPtr driver = domain->conn->privateData;
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+ vm = virDomainObjListFindByUUID(driver->domains, domain->uuid);
+ if (!vm) {
+ virUUIDFormat(domain->uuid, uuidstr);
+ virReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s' (%s)"),
+ uuidstr, domain->name);
+ return NULL;
+ }
+
+ return vm;
+}
+
+/* Functions */
+static int
+chConnectURIProbe(char **uri)
+{
+ if (ch_driver == NULL)
+ return 0;
+
+ *uri = g_strdup("ch:///system");
+ return 1;
+}
+
+static virDrvOpenStatus chConnectOpen(virConnectPtr conn,
+ virConnectAuthPtr auth G_GNUC_UNUSED,
+ virConfPtr conf G_GNUC_UNUSED,
+ unsigned int flags)
+{
+ virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
+
+ /* URI was good, but driver isn't active */
+ if (ch_driver == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Cloud-Hypervisor state driver is not active"));
+ return VIR_DRV_OPEN_ERROR;
+ }
+
+ if (virConnectOpenEnsureACL(conn) < 0)
+ return VIR_DRV_OPEN_ERROR;
+
+ conn->privateData = ch_driver;
+
+ return VIR_DRV_OPEN_SUCCESS;
+}
+
+static int chConnectClose(virConnectPtr conn)
+{
+ conn->privateData = NULL;
+ return 0;
+}
+
+static const char *chConnectGetType(virConnectPtr conn)
+{
+ if (virConnectGetTypeEnsureACL(conn) < 0)
+ return NULL;
+
+ return "CH";
+}
+
+static int chConnectGetVersion(virConnectPtr conn,
+ unsigned long *version)
+{
+ virCHDriverPtr driver = conn->privateData;
+
+ if (virConnectGetVersionEnsureACL(conn) < 0)
+ return -1;
+
+ chDriverLock(driver);
+ *version = driver->version;
+ chDriverUnlock(driver);
+ return 0;
+}
+
+static char *chConnectGetHostname(virConnectPtr conn)
+{
+ if (virConnectGetHostnameEnsureACL(conn) < 0)
+ return NULL;
+
+ return virGetHostname();
+}
+
+static int chConnectNumOfDomains(virConnectPtr conn)
+{
+ virCHDriverPtr driver = conn->privateData;
+
+ if (virConnectNumOfDomainsEnsureACL(conn) < 0)
+ return -1;
+
+ return virDomainObjListNumOfDomains(driver->domains, true,
+ virConnectNumOfDomainsCheckACL, conn);
+}
+
+static int chConnectListDomains(virConnectPtr conn, int *ids, int nids)
+{
+ virCHDriverPtr driver = conn->privateData;
+
+ if (virConnectListDomainsEnsureACL(conn) < 0)
+ return -1;
+
+ return virDomainObjListGetActiveIDs(driver->domains, ids, nids,
+ virConnectListDomainsCheckACL, conn);
+}
+
+static int
+chConnectListAllDomains(virConnectPtr conn,
+ virDomainPtr **domains,
+ unsigned int flags)
+{
+ virCHDriverPtr driver = conn->privateData;
+
+ virCheckFlags(VIR_CONNECT_LIST_DOMAINS_FILTERS_ALL, -1);
+
+ if (virConnectListAllDomainsEnsureACL(conn) < 0)
+ return -1;
+
+ return virDomainObjListExport(driver->domains, conn, domains,
+ virConnectListAllDomainsCheckACL, flags);
+}
+
+static int chNodeGetInfo(virConnectPtr conn,
+ virNodeInfoPtr nodeinfo)
+{
+ if (virNodeGetInfoEnsureACL(conn) < 0)
+ return -1;
+
+ return virCapabilitiesGetNodeInfo(nodeinfo);
+}
+
+static char *chConnectGetCapabilities(virConnectPtr conn)
+{
+ virCHDriverPtr driver = conn->privateData;
+ virCapsPtr caps;
+ char *xml;
+
+ if (virConnectGetCapabilitiesEnsureACL(conn) < 0)
+ return NULL;
+
+ if (!(caps = virCHDriverGetCapabilities(driver, true)))
+ return NULL;
+
+ xml = virCapabilitiesFormatXML(caps);
+
+ virObjectUnref(caps);
+ return xml;
+}
+
+/**
+ * chDomainCreateXML:
+ * @conn: pointer to connection
+ * @xml: XML definition of domain
+ * @flags: bitwise-OR of supported virDomainCreateFlags
+ *
+ * Creates a domain based on xml and starts it
+ *
+ * Returns a new domain object or NULL in case of failure.
+ */
+static virDomainPtr
+chDomainCreateXML(virConnectPtr conn,
+ const char *xml,
+ unsigned int flags)
+{
+ virCHDriverPtr driver = conn->privateData;
+ virDomainDefPtr vmdef = NULL;
+ virDomainObjPtr vm = NULL;
+ virDomainPtr dom = NULL;
+ unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
+
+ virCheckFlags(VIR_DOMAIN_START_VALIDATE, NULL);
+
+ if (flags & VIR_DOMAIN_START_VALIDATE)
+ parse_flags |= VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA;
+
+
+ if ((vmdef = virDomainDefParseString(xml, driver->xmlopt,
+ NULL, parse_flags)) == NULL)
+ goto cleanup;
+
+ if (virDomainCreateXMLEnsureACL(conn, vmdef) < 0)
+ goto cleanup;
+
+ if (!(vm = virDomainObjListAdd(driver->domains,
+ vmdef,
+ driver->xmlopt,
+ VIR_DOMAIN_OBJ_LIST_ADD_LIVE |
+ VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE,
+ NULL)))
+ goto cleanup;
+
+ vmdef = NULL;
+ vm->persistent = 1;
+
+ if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
+ goto cleanup;
+
+ if (virCHProcessStart(driver, vm, VIR_DOMAIN_RUNNING_BOOTED) < 0)
+ goto cleanup;
+
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
+
+ virCHDomainObjEndJob(vm);
+
+ cleanup:
+ virDomainDefFree(vmdef);
+ virDomainObjEndAPI(&vm);
+ chDriverUnlock(driver);
+ return dom;
+}
+
+static int
+chDomainCreateWithFlags(virDomainPtr dom, unsigned int flags)
+{
+ virCHDriverPtr driver = dom->conn->privateData;
+ virDomainObjPtr vm;
+ int ret = -1;
+
+ virCheckFlags(0, -1);
+
+ if (!(vm = chDomObjFromDomain(dom)))
+ goto cleanup;
+
+ if (virDomainCreateWithFlagsEnsureACL(dom->conn, vm->def) < 0)
+ goto cleanup;
+
+ if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
+ goto cleanup;
+
+ ret = virCHProcessStart(driver, vm, VIR_DOMAIN_RUNNING_BOOTED);
+
+ virCHDomainObjEndJob(vm);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return ret;
+}
+
+static int
+chDomainCreate(virDomainPtr dom)
+{
+ return chDomainCreateWithFlags(dom, 0);
+}
+
+static virDomainPtr
+chDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
+{
+ virCHDriverPtr driver = conn->privateData;
+ virDomainDefPtr vmdef = NULL;
+ virDomainObjPtr vm = NULL;
+ virDomainPtr dom = NULL;
+ unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
+
+ virCheckFlags(VIR_DOMAIN_DEFINE_VALIDATE, NULL);
+
+ if (flags & VIR_DOMAIN_START_VALIDATE)
+ parse_flags |= VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA;
+
+ if ((vmdef = virDomainDefParseString(xml, driver->xmlopt,
+ NULL, parse_flags)) == NULL)
+ goto cleanup;
+
+ if (virXMLCheckIllegalChars("name", vmdef->name, "\n") < 0)
+ goto cleanup;
+
+ if (virDomainDefineXMLFlagsEnsureACL(conn, vmdef) < 0)
+ goto cleanup;
+
+ if (!(vm = virDomainObjListAdd(driver->domains, vmdef,
+ driver->xmlopt,
+ 0, NULL)))
+ goto cleanup;
+
+ vmdef = NULL;
+ vm->persistent = 1;
+
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
+
+ cleanup:
+ virDomainDefFree(vmdef);
+ virDomainObjEndAPI(&vm);
+ return dom;
+}
+
+static virDomainPtr
+chDomainDefineXML(virConnectPtr conn, const char *xml)
+{
+ return chDomainDefineXMLFlags(conn, xml, 0);
+}
+
+static int
+chDomainUndefineFlags(virDomainPtr dom,
+ unsigned int flags)
+{
+ virCHDriverPtr driver = dom->conn->privateData;
+ virDomainObjPtr vm;
+ int ret = -1;
+
+ virCheckFlags(0, -1);
+
+ if (!(vm = chDomObjFromDomain(dom)))
+ goto cleanup;
+
+ if (virDomainUndefineFlagsEnsureACL(dom->conn, vm->def) < 0)
+ goto cleanup;
+
+ if (!vm->persistent) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("Cannot undefine transient domain"));
+ goto cleanup;
+ }
+
+ if (virDomainObjIsActive(vm)) {
+ vm->persistent = 0;
+ } else {
+ virDomainObjListRemove(driver->domains, vm);
+ }
+
+ ret = 0;
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return ret;
+}
+
+static int
+chDomainUndefine(virDomainPtr dom)
+{
+ return chDomainUndefineFlags(dom, 0);
+}
+
+static int chDomainIsActive(virDomainPtr dom)
+{
+ virCHDriverPtr driver = dom->conn->privateData;
+ virDomainObjPtr vm;
+ int ret = -1;
+
+ chDriverLock(driver);
+ if (!(vm = chDomObjFromDomain(dom)))
+ goto cleanup;
+
+ if (virDomainIsActiveEnsureACL(dom->conn, vm->def) < 0)
+ goto cleanup;
+
+ ret = virDomainObjIsActive(vm);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ chDriverUnlock(driver);
+ return ret;
+}
+
+static int
+chDomainShutdownFlags(virDomainPtr dom,
+ unsigned int flags)
+{
+ virCHDomainObjPrivatePtr priv;
+ virDomainObjPtr vm;
+ virDomainState state;
+ int ret = -1;
+
+ virCheckFlags(VIR_DOMAIN_SHUTDOWN_INITCTL |
+ VIR_DOMAIN_SHUTDOWN_SIGNAL, -1);
+
+ if (!(vm = chDomObjFromDomain(dom)))
+ goto cleanup;
+
+ priv = vm->privateData;
+
+ if (virDomainShutdownFlagsEnsureACL(dom->conn, vm->def, flags) < 0)
+ goto cleanup;
+
+ if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
+ goto cleanup;
+
+ if (virDomainObjCheckActive(vm) < 0)
+ goto endjob;
+
+ state = virDomainObjGetState(vm, NULL);
+ if (state != VIR_DOMAIN_RUNNING && state != VIR_DOMAIN_PAUSED) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("only can shutdown running/paused domain"));
+ goto endjob;
+ } else {
+ if (virCHMonitorShutdownVM(priv->monitor) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to shutdown guest VM"));
+ goto endjob;
+ }
+ }
+
+ virDomainObjSetState(vm, VIR_DOMAIN_SHUTDOWN, VIR_DOMAIN_SHUTDOWN_USER);
+
+ ret = 0;
+
+ endjob:
+ virCHDomainObjEndJob(vm);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return ret;
+}
+
+static int
+chDomainShutdown(virDomainPtr dom)
+{
+ return chDomainShutdownFlags(dom, 0);
+}
+
+
+static int
+chDomainReboot(virDomainPtr dom, unsigned int flags)
+{
+ virCHDomainObjPrivatePtr priv;
+ virDomainObjPtr vm;
+ virDomainState state;
+ int ret = -1;
+
+ virCheckFlags(VIR_DOMAIN_REBOOT_INITCTL |
+ VIR_DOMAIN_REBOOT_SIGNAL, -1);
+
+ if (!(vm = chDomObjFromDomain(dom)))
+ goto cleanup;
+
+ priv = vm->privateData;
+
+ if (virDomainRebootEnsureACL(dom->conn, vm->def, flags) < 0)
+ goto cleanup;
+
+ if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
+ goto cleanup;
+
+ if (virDomainObjCheckActive(vm) < 0)
+ goto endjob;
+
+ state = virDomainObjGetState(vm, NULL);
+ if (state != VIR_DOMAIN_RUNNING && state != VIR_DOMAIN_PAUSED) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("only can reboot running/paused domain"));
+ goto endjob;
+ } else {
+ if (virCHMonitorRebootVM(priv->monitor) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to reboot domain"));
+ goto endjob;
+ }
+ }
+
+ if (state == VIR_DOMAIN_RUNNING)
+ virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_BOOTED);
+ else
+ virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UNPAUSED);
+
+ ret = 0;
+
+ endjob:
+ virCHDomainObjEndJob(vm);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return ret;
+}
+
+static int
+chDomainSuspend(virDomainPtr dom)
+{
+ virCHDomainObjPrivatePtr priv;
+ virDomainObjPtr vm;
+ int ret = -1;
+
+ if (!(vm = chDomObjFromDomain(dom)))
+ goto cleanup;
+
+ priv = vm->privateData;
+
+ if (virDomainSuspendEnsureACL(dom->conn, vm->def) < 0)
+ goto cleanup;
+
+ if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
+ goto cleanup;
+
+ if (virDomainObjCheckActive(vm) < 0)
+ goto endjob;
+
+ if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_RUNNING) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("only can suspend running domain"));
+ goto endjob;
+ } else {
+ if (virCHMonitorSuspendVM(priv->monitor) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to suspend domain"));
+ goto endjob;
+ }
+ }
+
+ virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_USER);
+
+ ret = 0;
+
+ endjob:
+ virCHDomainObjEndJob(vm);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return ret;
+}
+
+static int
+chDomainResume(virDomainPtr dom)
+{
+ virCHDomainObjPrivatePtr priv;
+ virDomainObjPtr vm;
+ int ret = -1;
+
+ if (!(vm = chDomObjFromDomain(dom)))
+ goto cleanup;
+
+ priv = vm->privateData;
+
+ if (virDomainResumeEnsureACL(dom->conn, vm->def) < 0)
+ goto cleanup;
+
+ if (virCHDomainObjBeginJob(vm, CH_JOB_MODIFY) < 0)
+ goto cleanup;
+
+ if (virDomainObjCheckActive(vm) < 0)
+ goto endjob;
+
+ if (virDomainObjGetState(vm, NULL) != VIR_DOMAIN_PAUSED) {
+ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+ _("only can resume paused domain"));
+ goto endjob;
+ } else {
+ if (virCHMonitorResumeVM(priv->monitor) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to resume domain"));
+ goto endjob;
+ }
+ }
+
+ virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_UNPAUSED);
+
+ ret = 0;
+
+ endjob:
+ virCHDomainObjEndJob(vm);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return ret;
+}
+
+/**
+ * chDomainDestroyFlags:
+ * @dom: pointer to domain to destroy
+ * @flags: extra flags; not used yet.
+ *
+ * Sends SIGKILL to Cloud-Hypervisor process to terminate it
+ *
+ * Returns 0 on success or -1 in case of error
+ */
+static int
+chDomainDestroyFlags(virDomainPtr dom, unsigned int flags)
+{
+ virCHDriverPtr driver = dom->conn->privateData;
+ virDomainObjPtr vm;
+ int ret = -1;
+
+ virCheckFlags(0, -1);
+
+ if (!(vm = chDomObjFromDomain(dom)))
+ goto cleanup;
+
+ if (virDomainDestroyFlagsEnsureACL(dom->conn, vm->def) < 0)
+ goto cleanup;
+
+ if (virCHDomainObjBeginJob(vm, CH_JOB_DESTROY) < 0)
+ goto cleanup;
+
+ if (virDomainObjCheckActive(vm) < 0)
+ goto endjob;
+
+ ret = virCHProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_DESTROYED);
+
+ endjob:
+ virCHDomainObjEndJob(vm);
+ if (!vm->persistent)
+ virDomainObjListRemove(driver->domains, vm);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return ret;
+}
+
+static int
+chDomainDestroy(virDomainPtr dom)
+{
+ return chDomainDestroyFlags(dom, 0);
+}
+
+static virDomainPtr chDomainLookupByID(virConnectPtr conn,
+ int id)
+{
+ virCHDriverPtr driver = conn->privateData;
+ virDomainObjPtr vm;
+ virDomainPtr dom = NULL;
+
+ chDriverLock(driver);
+ vm = virDomainObjListFindByID(driver->domains, id);
+ chDriverUnlock(driver);
+
+ if (!vm) {
+ virReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching id '%d'"), id);
+ goto cleanup;
+ }
+
+ if (virDomainLookupByIDEnsureACL(conn, vm->def) < 0)
+ goto cleanup;
+
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return dom;
+}
+
+static virDomainPtr chDomainLookupByName(virConnectPtr conn,
+ const char *name)
+{
+ virCHDriverPtr driver = conn->privateData;
+ virDomainObjPtr vm;
+ virDomainPtr dom = NULL;
+
+ chDriverLock(driver);
+ vm = virDomainObjListFindByName(driver->domains, name);
+ chDriverUnlock(driver);
+
+ if (!vm) {
+ virReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching name '%s'"), name);
+ goto cleanup;
+ }
+
+ if (virDomainLookupByNameEnsureACL(conn, vm->def) < 0)
+ goto cleanup;
+
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return dom;
+}
+
+static virDomainPtr chDomainLookupByUUID(virConnectPtr conn,
+ const unsigned char *uuid)
+{
+ virCHDriverPtr driver = conn->privateData;
+ virDomainObjPtr vm;
+ virDomainPtr dom = NULL;
+
+ chDriverLock(driver);
+ vm = virDomainObjListFindByUUID(driver->domains, uuid);
+ chDriverUnlock(driver);
+
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(uuid, uuidstr);
+ virReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"), uuidstr);
+ goto cleanup;
+ }
+
+ if (virDomainLookupByUUIDEnsureACL(conn, vm->def) < 0)
+ goto cleanup;
+
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return dom;
+}
+
+static int
+chDomainGetState(virDomainPtr dom,
+ int *state,
+ int *reason,
+ unsigned int flags)
+{
+ virDomainObjPtr vm;
+ int ret = -1;
+
+ virCheckFlags(0, -1);
+
+ if (!(vm = chDomObjFromDomain(dom)))
+ goto cleanup;
+
+ if (virDomainGetStateEnsureACL(dom->conn, vm->def) < 0)
+ goto cleanup;
+
+ *state = virDomainObjGetState(vm, reason);
+ ret = 0;
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return ret;
+}
+
+static char *chDomainGetXMLDesc(virDomainPtr dom,
+ unsigned int flags)
+{
+ virCHDriverPtr driver = dom->conn->privateData;
+ virDomainObjPtr vm;
+ char *ret = NULL;
+
+ virCheckFlags(VIR_DOMAIN_XML_COMMON_FLAGS, NULL);
+
+ if (!(vm = chDomObjFromDomain(dom)))
+ goto cleanup;
+
+ if (virDomainGetXMLDescEnsureACL(dom->conn, vm->def, flags) < 0)
+ goto cleanup;
+
+ ret = virDomainDefFormat(vm->def, driver->xmlopt,
+ virDomainDefFormatConvertXMLFlags(flags));
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return ret;
+}
+
+static int chDomainGetInfo(virDomainPtr dom,
+ virDomainInfoPtr info)
+{
+ virDomainObjPtr vm;
+ int ret = -1;
+
+ if (!(vm = chDomObjFromDomain(dom)))
+ goto cleanup;
+
+ if (virDomainGetInfoEnsureACL(dom->conn, vm->def) < 0)
+ goto cleanup;
+
+ info->state = virDomainObjGetState(vm, NULL);
+
+ info->cpuTime = 0;
+
+ info->maxMem = virDomainDefGetMemoryTotal(vm->def);
+ info->memory = vm->def->mem.cur_balloon;
+ info->nrVirtCpu = virDomainDefGetVcpus(vm->def);
+
+ ret = 0;
+
+ cleanup:
+ virDomainObjEndAPI(&vm);
+ return ret;
+}
+
+static int chStateCleanup(void)
+{
+ if (ch_driver == NULL)
+ return -1;
+
+ virObjectUnref(ch_driver->domains);
+ virObjectUnref(ch_driver->xmlopt);
+ virObjectUnref(ch_driver->caps);
+ virObjectUnref(ch_driver->config);
+ virMutexDestroy(&ch_driver->lock);
+ VIR_FREE(ch_driver);
+
+ return 0;
+}
+
+static int chStateInitialize(bool privileged,
+ const char *root,
+ virStateInhibitCallback callback G_GNUC_UNUSED,
+ void *opaque G_GNUC_UNUSED)
+{
+ if (root != NULL) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("Driver does not support embedded mode"));
+ return -1;
+ }
+
+ /* Check that the user is root, silently disable if not */
+ if (!privileged) {
+ VIR_INFO("Not running privileged, disabling driver");
+ return VIR_DRV_STATE_INIT_SKIPPED;
+ }
+
+ if (VIR_ALLOC(ch_driver) < 0)
+ return VIR_DRV_STATE_INIT_ERROR;
+
+ if (virMutexInit(&ch_driver->lock) < 0) {
+ VIR_FREE(ch_driver);
+ return VIR_DRV_STATE_INIT_ERROR;
+ }
+
+ if (!(ch_driver->domains = virDomainObjListNew()))
+ goto cleanup;
+
+ if (!(ch_driver->caps = virCHDriverCapsInit()))
+ goto cleanup;
+
+ if (!(ch_driver->xmlopt = chDomainXMLConfInit(ch_driver)))
+ goto cleanup;
+
+ if (!(ch_driver->config = virCHDriverConfigNew()))
+ goto cleanup;
+
+ if (chExtractVersion(ch_driver) < 0)
+ goto cleanup;
+
+ return VIR_DRV_STATE_INIT_COMPLETE;
+
+ cleanup:
+ chStateCleanup();
+ return VIR_DRV_STATE_INIT_ERROR;
+}
+
+/* Function Tables */
+static virHypervisorDriver chHypervisorDriver = {
+ .name = "CH",
+ .connectURIProbe = chConnectURIProbe,
+ .connectOpen = chConnectOpen, /* 6.7.0 */
+ .connectClose = chConnectClose, /* 6.7.0 */
+ .connectGetType = chConnectGetType, /* 6.7.0 */
+ .connectGetVersion = chConnectGetVersion, /* 6.7.0 */
+ .connectGetHostname = chConnectGetHostname, /* 6.7.0 */
+ .connectNumOfDomains = chConnectNumOfDomains, /* 6.7.0 */
+ .connectListAllDomains = chConnectListAllDomains, /* 6.7.0 */
+ .connectListDomains = chConnectListDomains, /* 6.7.0 */
+ .connectGetCapabilities = chConnectGetCapabilities, /* 6.7.0 */
+ .domainCreateXML = chDomainCreateXML, /* 6.7.0 */
+ .domainCreate = chDomainCreate, /* 6.7.0 */
+ .domainCreateWithFlags = chDomainCreateWithFlags, /* 6.7.0 */
+ .domainShutdown = chDomainShutdown, /* 6.7.0 */
+ .domainShutdownFlags = chDomainShutdownFlags, /* 6.7.0 */
+ .domainReboot = chDomainReboot, /* 6.7.0 */
+ .domainSuspend = chDomainSuspend, /* 6.7.0 */
+ .domainResume = chDomainResume, /* 6.7.0 */
+ .domainDestroy = chDomainDestroy, /* 6.7.0 */
+ .domainDestroyFlags = chDomainDestroyFlags, /* 6.7.0 */
+ .domainDefineXML = chDomainDefineXML, /* 6.7.0 */
+ .domainDefineXMLFlags = chDomainDefineXMLFlags, /* 6.7.0 */
+ .domainUndefine = chDomainUndefine, /* 6.7.0 */
+ .domainUndefineFlags = chDomainUndefineFlags, /* 6.7.0 */
+ .domainLookupByID = chDomainLookupByID, /* 6.7.0 */
+ .domainLookupByUUID = chDomainLookupByUUID, /* 6.7.0 */
+ .domainLookupByName = chDomainLookupByName, /* 6.7.0 */
+ .domainGetState = chDomainGetState, /* 6.7.0 */
+ .domainGetXMLDesc = chDomainGetXMLDesc, /* 6.7.0 */
+ .domainGetInfo = chDomainGetInfo, /* 6.7.0 */
+ .domainIsActive = chDomainIsActive, /* 6.7.0 */
+ .nodeGetInfo = chNodeGetInfo, /* 6.7.0 */
+};
+
+static virConnectDriver chConnectDriver = {
+ .localOnly = true,
+ .uriSchemes = (const char *[]){"CH", "Ch", "ch", "Cloud-Hypervisor", NULL},
+ .hypervisorDriver = &chHypervisorDriver,
+};
+
+static virStateDriver chStateDriver = {
+ .name = "CH",
+ .stateInitialize = chStateInitialize,
+ .stateCleanup = chStateCleanup,
+};
+
+int chRegister(void)
+{
+ if (virRegisterConnectDriver(&chConnectDriver, false) < 0)
+ return -1;
+ if (virRegisterStateDriver(&chStateDriver) < 0)
+ return -1;
+ return 0;
+}
diff --git a/src/ch/ch_driver.h b/src/ch/ch_driver.h
new file mode 100644
index 0000000000..0516c91c24
--- /dev/null
+++ b/src/ch/ch_driver.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright Intel Corp. 2020
+ *
+ * ch_driver.h: header file for Cloud-Hypervisor driver functions
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+/* Function declarations */
+int chRegister(void);
diff --git a/src/ch/ch_monitor.c b/src/ch/ch_monitor.c
new file mode 100644
index 0000000000..ccef70f719
--- /dev/null
+++ b/src/ch/ch_monitor.c
@@ -0,0 +1,796 @@
+/*
+ * Copyright Intel Corp. 2020
+ *
+ * ch_driver.h: header file for Cloud-Hypervisor driver functions
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <curl/curl.h>
+
+#include "ch_conf.h"
+#include "ch_monitor.h"
+#include "viralloc.h"
+#include "vircommand.h"
+#include "virerror.h"
+#include "virfile.h"
+#include "virjson.h"
+#include "virlog.h"
+#include "virtime.h"
+
+#define VIR_FROM_THIS VIR_FROM_CH
+
+VIR_LOG_INIT("ch.ch_monitor");
+
+static virClassPtr virCHMonitorClass;
+static void virCHMonitorDispose(void *obj);
+
+static int virCHMonitorOnceInit(void)
+{
+ if (!VIR_CLASS_NEW(virCHMonitor, virClassForObjectLockable()))
+ return -1;
+
+ return 0;
+}
+
+VIR_ONCE_GLOBAL_INIT(virCHMonitor);
+
+int virCHMonitorShutdownVMM(virCHMonitorPtr mon);
+int virCHMonitorPutNoContent(virCHMonitorPtr mon, const char *endpoint);
+int virCHMonitorGet(virCHMonitorPtr mon, const char *endpoint);
+int virCHMonitorPingVMM(virCHMonitorPtr mon);
+
+static int
+virCHMonitorBuildCPUJson(virJSONValuePtr content, virDomainDefPtr vmdef)
+{
+ virJSONValuePtr cpus;
+ unsigned int maxvcpus = 0;
+ unsigned int nvcpus = 0;
+ virDomainVcpuDefPtr vcpu;
+ size_t i;
+
+ /* count maximum allowed number vcpus and enabled vcpus when boot.*/
+ maxvcpus = virDomainDefGetVcpusMax(vmdef);
+ for (i = 0; i < maxvcpus; i++) {
+ vcpu = virDomainDefGetVcpu(vmdef, i);
+ if (vcpu->online)
+ nvcpus++;
+ }
+
+ if (maxvcpus != 0 || nvcpus != 0) {
+ cpus = virJSONValueNewObject();
+ if (virJSONValueObjectAppendNumberInt(cpus, "boot_vcpus", nvcpus) < 0)
+ goto cleanup;
+ if (virJSONValueObjectAppendNumberInt(cpus, "max_vcpus", vmdef->maxvcpus) < 0)
+ goto cleanup;
+ if (virJSONValueObjectAppend(content, "cpus", cpus) < 0)
+ goto cleanup;
+ }
+
+ return 0;
+
+ cleanup:
+ virJSONValueFree(cpus);
+ return -1;
+}
+
+static int
+virCHMonitorBuildKernelJson(virJSONValuePtr content, virDomainDefPtr vmdef)
+{
+ virJSONValuePtr kernel;
+
+ if (vmdef->os.kernel == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Kernel image path in this domain is not defined"));
+ return -1;
+ } else {
+ kernel = virJSONValueNewObject();
+ if (virJSONValueObjectAppendString(kernel, "path", vmdef->os.kernel) < 0)
+ goto cleanup;
+ if (virJSONValueObjectAppend(content, "kernel", kernel) < 0)
+ goto cleanup;
+ }
+
+ return 0;
+
+ cleanup:
+ virJSONValueFree(kernel);
+ return -1;
+}
+
+static int
+virCHMonitorBuildCmdlineJson(virJSONValuePtr content, virDomainDefPtr vmdef)
+{
+ virJSONValuePtr cmdline;
+
+ cmdline = virJSONValueNewObject();
+ if (vmdef->os.cmdline) {
+ if (virJSONValueObjectAppendString(cmdline, "args", vmdef->os.cmdline) < 0)
+ goto cleanup;
+ if (virJSONValueObjectAppend(content, "cmdline", cmdline) < 0)
+ goto cleanup;
+ }
+
+ return 0;
+
+ cleanup:
+ virJSONValueFree(cmdline);
+ return -1;
+}
+
+static int
+virCHMonitorBuildMemoryJson(virJSONValuePtr content, virDomainDefPtr vmdef)
+{
+ virJSONValuePtr memory;
+ unsigned long long total_memory = virDomainDefGetMemoryInitial(vmdef) * 1024;
+
+ if (total_memory != 0) {
+ memory = virJSONValueNewObject();
+ if (virJSONValueObjectAppendNumberUlong(memory, "size", total_memory) < 0)
+ goto cleanup;
+ if (virJSONValueObjectAppend(content, "memory", memory) < 0)
+ goto cleanup;
+ }
+
+ return 0;
+
+ cleanup:
+ virJSONValueFree(memory);
+ return -1;
+}
+
+static int
+virCHMonitorBuildInitramfsJson(virJSONValuePtr content, virDomainDefPtr vmdef)
+{
+ virJSONValuePtr initramfs;
+
+ if (vmdef->os.initrd != NULL) {
+ initramfs = virJSONValueNewObject();
+ if (virJSONValueObjectAppendString(initramfs, "path", vmdef->os.initrd) < 0)
+ goto cleanup;
+ if (virJSONValueObjectAppend(content, "initramfs", initramfs) < 0)
+ goto cleanup;
+ }
+
+ return 0;
+
+ cleanup:
+ virJSONValueFree(initramfs);
+ return -1;
+}
+
+static int
+virCHMonitorBuildDiskJson(virJSONValuePtr disks, virDomainDiskDefPtr diskdef)
+{
+ virJSONValuePtr disk;
+
+ if (diskdef->src != NULL && diskdef->src->path != NULL) {
+ disk = virJSONValueNewObject();
+ if (virJSONValueObjectAppendString(disk, "path", diskdef->src->path) < 0)
+ goto cleanup;
+ if (diskdef->src->readonly) {
+ if (virJSONValueObjectAppendBoolean(disk, "readonly", true) < 0)
+ goto cleanup;
+ }
+ if (virJSONValueArrayAppend(disks, disk) < 0)
+ goto cleanup;
+ }
+
+ return 0;
+
+ cleanup:
+ virJSONValueFree(disk);
+ return -1;
+}
+
+static int
+virCHMonitorBuildDisksJson(virJSONValuePtr content, virDomainDefPtr vmdef)
+{
+ virJSONValuePtr disks;
+ size_t i;
+
+ if (vmdef->ndisks > 0) {
+ disks = virJSONValueNewArray();
+
+ for (i = 0; i < vmdef->ndisks; i++) {
+ if (virCHMonitorBuildDiskJson(disks, vmdef->disks[i]) < 0)
+ goto cleanup;
+ }
+ if (virJSONValueObjectAppend(content, "disks", disks) < 0)
+ goto cleanup;
+ }
+
+ return 0;
+
+ cleanup:
+ virJSONValueFree(disks);
+ return -1;
+}
+
+static int
+virCHMonitorBuildNetJson(virJSONValuePtr nets, virDomainNetDefPtr netdef)
+{
+ virDomainNetType netType = virDomainNetGetActualType(netdef);
+ char macaddr[VIR_MAC_STRING_BUFLEN];
+ virJSONValuePtr net;
+
+ // check net type at first
+ net = virJSONValueNewObject();
+
+ switch (netType) {
+ case VIR_DOMAIN_NET_TYPE_ETHERNET:
+ if (netdef->guestIP.nips == 1) {
+ const virNetDevIPAddr *ip = netdef->guestIP.ips[0];
+ g_autofree char *addr = NULL;
+ virSocketAddr netmask;
+ g_autofree char *netmaskStr = NULL;
+ if (!(addr = virSocketAddrFormat(&ip->address)))
+ goto cleanup;
+ if (virJSONValueObjectAppendString(net, "ip", addr) < 0)
+ goto cleanup;
+
+ if (virSocketAddrPrefixToNetmask(ip->prefix, &netmask, AF_INET) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Failed to translate net prefix %d to netmask"),
+ ip->prefix);
+ goto cleanup;
+ }
+ if (!(netmaskStr = virSocketAddrFormat(&netmask)))
+ goto cleanup;
+ if (virJSONValueObjectAppendString(net, "mask", netmaskStr) < 0)
+ goto cleanup;
+ }
+ break;
+ case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
+ if ((virDomainChrType)netdef->data.vhostuser->type != VIR_DOMAIN_CHR_TYPE_UNIX) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("vhost_user type support UNIX socket in this CH"));
+ goto cleanup;
+ } else {
+ if (virJSONValueObjectAppendString(net, "vhost_socket", netdef->data.vhostuser->data.nix.path) < 0)
+ goto cleanup;
+ if (virJSONValueObjectAppendBoolean(net, "vhost_user", true) < 0)
+ goto cleanup;
+ }
+ break;
+ case VIR_DOMAIN_NET_TYPE_BRIDGE:
+ case VIR_DOMAIN_NET_TYPE_NETWORK:
+ case VIR_DOMAIN_NET_TYPE_DIRECT:
+ case VIR_DOMAIN_NET_TYPE_USER:
+ case VIR_DOMAIN_NET_TYPE_SERVER:
+ case VIR_DOMAIN_NET_TYPE_CLIENT:
+ case VIR_DOMAIN_NET_TYPE_MCAST:
+ case VIR_DOMAIN_NET_TYPE_INTERNAL:
+ case VIR_DOMAIN_NET_TYPE_HOSTDEV:
+ case VIR_DOMAIN_NET_TYPE_UDP:
+ case VIR_DOMAIN_NET_TYPE_LAST:
+ default:
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Only ethernet and vhost_user type network types are "
+ "supported in this CH"));
+ goto cleanup;
+ }
+
+ if (netdef->ifname != NULL) {
+ if (virJSONValueObjectAppendString(net, "tap", netdef->ifname) < 0)
+ goto cleanup;
+ }
+ if (virJSONValueObjectAppendString(net, "mac", virMacAddrFormat(&netdef->mac, macaddr)) < 0)
+ goto cleanup;
+
+
+ if (netdef->virtio != NULL) {
+ if (netdef->virtio->iommu == VIR_TRISTATE_SWITCH_ON) {
+ if (virJSONValueObjectAppendBoolean(net, "iommu", true) < 0)
+ goto cleanup;
+ }
+ }
+ if (netdef->driver.virtio.queues) {
+ if (virJSONValueObjectAppendNumberInt(net, "num_queues", netdef->driver.virtio.queues) < 0)
+ goto cleanup;
+ }
+
+ if (netdef->driver.virtio.rx_queue_size || netdef->driver.virtio.tx_queue_size) {
+ if (netdef->driver.virtio.rx_queue_size != netdef->driver.virtio.tx_queue_size) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("virtio rx_queue_size option %d is not same with tx_queue_size %d"),
+ netdef->driver.virtio.rx_queue_size,
+ netdef->driver.virtio.tx_queue_size);
+ goto cleanup;
+ }
+ if (virJSONValueObjectAppendNumberInt(net, "queue_size", netdef->driver.virtio.rx_queue_size) < 0)
+ goto cleanup;
+ }
+
+ if (virJSONValueArrayAppend(nets, net) < 0)
+ goto cleanup;
+
+ return 0;
+
+ cleanup:
+ virJSONValueFree(net);
+ return -1;
+}
+
+static int
+virCHMonitorBuildNetsJson(virJSONValuePtr content, virDomainDefPtr vmdef)
+{
+ virJSONValuePtr nets;
+ size_t i;
+
+ if (vmdef->nnets > 0) {
+ nets = virJSONValueNewArray();
+
+ for (i = 0; i < vmdef->nnets; i++) {
+ if (virCHMonitorBuildNetJson(nets, vmdef->nets[i]) < 0)
+ goto cleanup;
+ }
+ if (virJSONValueObjectAppend(content, "net", nets) < 0)
+ goto cleanup;
+ }
+
+ return 0;
+
+ cleanup:
+ virJSONValueFree(nets);
+ return -1;
+}
+
+static int
+virCHMonitorBuildVMJson(virDomainDefPtr vmdef, char **jsonstr)
+{
+ virJSONValuePtr content = virJSONValueNewObject();
+ int ret = -1;
+
+ if (vmdef == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("VM is not defined"));
+ goto cleanup;
+ }
+
+ if (virCHMonitorBuildCPUJson(content, vmdef) < 0)
+ goto cleanup;
+
+ if (virCHMonitorBuildMemoryJson(content, vmdef) < 0)
+ goto cleanup;
+
+ if (virCHMonitorBuildKernelJson(content, vmdef) < 0)
+ goto cleanup;
+
+ if (virCHMonitorBuildCmdlineJson(content, vmdef) < 0)
+ goto cleanup;
+
+ if (virCHMonitorBuildInitramfsJson(content, vmdef) < 0)
+ goto cleanup;
+
+ if (virCHMonitorBuildDisksJson(content, vmdef) < 0)
+ goto cleanup;
+
+ if (virCHMonitorBuildNetsJson(content, vmdef) < 0)
+ goto cleanup;
+
+ if (!(*jsonstr = virJSONValueToString(content, false)))
+ goto cleanup;
+
+ ret = 0;
+
+ cleanup:
+ virJSONValueFree(content);
+ return ret;
+}
+
+/* generate command to launch Cloud-Hypervisor socket
+ return -1 - error
+ 0 - OK
+ Caller has to free the cmd
+*/
+static virCommandPtr
+chMonitorBuildSocketCmd(virDomainObjPtr vm, const char *socket_path)
+{
+ virCommandPtr cmd;
+
+ if (vm->def == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("VM is not defined"));
+ return NULL;
+ }
+
+ if (vm->def->emulator != NULL)
+ cmd = virCommandNew(vm->def->emulator);
+ else
+ cmd = virCommandNew(CH_CMD);
+
+ virCommandAddArgList(cmd, "--api-socket", socket_path, NULL);
+
+ return cmd;
+}
+
+virCHMonitorPtr
+virCHMonitorNew(virDomainObjPtr vm, const char *socketdir)
+{
+ virCHMonitorPtr ret = NULL;
+ virCHMonitorPtr mon = NULL;
+ virCommandPtr cmd = NULL;
+ int pings = 0;
+
+ if (virCHMonitorInitialize() < 0)
+ return NULL;
+
+ if (!(mon = virObjectLockableNew(virCHMonitorClass)))
+ return NULL;
+
+ mon->socketpath = g_strdup_printf("%s/%s-socket", socketdir, vm->def->name);
+
+ /* prepare to launch Cloud-Hypervisor socket */
+ if (!(cmd = chMonitorBuildSocketCmd(vm, mon->socketpath)))
+ goto cleanup;
+
+ if (virFileMakePath(socketdir) < 0) {
+ virReportSystemError(errno,
+ _("Cannot create socket directory '%s'"),
+ socketdir);
+ goto cleanup;
+ }
+
+ /* launch Cloud-Hypervisor socket */
+ if (virCommandRunAsync(cmd, &mon->pid) < 0)
+ goto cleanup;
+
+ /* get a curl handle */
+ mon->handle = curl_easy_init();
+
+ /* try to ping VMM socket 5 times to make sure it is ready */
+ while (pings < 5) {
+ if (virCHMonitorPingVMM(mon) == 0)
+ break;
+ if (pings == 5)
+ goto cleanup;
+
+ g_usleep(100 * 1000);
+ }
+
+ /* now has its own reference */
+ virObjectRef(mon);
+ mon->vm = virObjectRef(vm);
+
+ ret = mon;
+
+ cleanup:
+ virCommandFree(cmd);
+ return ret;
+}
+
+static void virCHMonitorDispose(void *opaque)
+{
+ virCHMonitorPtr mon = opaque;
+
+ VIR_DEBUG("mon=%p", mon);
+ virObjectUnref(mon->vm);
+}
+
+void virCHMonitorClose(virCHMonitorPtr mon)
+{
+ if (!mon)
+ return;
+
+ if (mon->pid > 0) {
+ /* try cleaning up the Cloud-Hypervisor process */
+ virProcessAbort(mon->pid);
+ mon->pid = 0;
+ }
+
+ if (mon->handle)
+ curl_easy_cleanup(mon->handle);
+
+ if (mon->socketpath) {
+ if (virFileRemove(mon->socketpath, -1, -1) < 0) {
+ VIR_WARN("Unable to remove CH socket file '%s'",
+ mon->socketpath);
+ }
+ VIR_FREE(mon->socketpath);
+ }
+
+ virObjectUnref(mon);
+ if (mon->vm)
+ virObjectUnref(mon->vm);
+}
+
+
+struct data {
+ char trace_ascii; /* 1 or 0 */
+};
+
+static void dump(const char *text,
+ FILE *stream,
+ unsigned char *ptr,
+ size_t size,
+ char nohex)
+{
+ size_t i;
+ size_t c;
+
+ unsigned int width = 0x10;
+
+ if (nohex)
+ /* without the hex output, we can fit more on screen */
+ width = 0x40;
+
+ fprintf(stream, "%s, %10.10lu bytes (0x%8.8lx)\n", text, (unsigned long)size,
+ (unsigned long)size);
+
+ for (i = 0; i < size; i += width) {
+
+ fprintf(stream, "%4.4lx: ", (unsigned long)i);
+
+ if (!nohex) {
+ /* hex not disabled, show it */
+ for (c = 0; c < width; c++) {
+ if (i + c < size)
+ fprintf(stream, "%02x ", ptr[i + c]);
+ else
+ fputs(" ", stream);
+ }
+ }
+
+ for (c = 0; (c < width) && (i + c < size); c++) {
+ /* check for 0D0A; if found, skip past and start a new line of output */
+ if (nohex && (i + c + 1 < size) && ptr[i + c] == 0x0D &&
+ ptr[i + c + 1] == 0x0A) {
+ i += (c + 2 - width);
+ break;
+ }
+ fprintf(stream, "%c",
+ (ptr[i + c] >= 0x20) && (ptr[i + c] < 0x80) ? ptr[i + c] : '.');
+ /* check again for 0D0A, to avoid an extra \n if it's at width */
+ if (nohex && (i + c + 2 < size) && ptr[i + c + 1] == 0x0D &&
+ ptr[i + c + 2] == 0x0A) {
+ i += (c + 3 - width);
+ break;
+ }
+ }
+ fputc('\n', stream); /* newline */
+ }
+ fflush(stream);
+}
+
+static int my_trace(CURL *handle,
+ curl_infotype type,
+ char *data,
+ size_t size,
+ void *userp)
+{
+ struct data *config = (struct data *)userp;
+ const char *text = "";
+ (void)handle; /* prevent compiler warning */
+
+ switch (type) {
+ case CURLINFO_TEXT:
+ fprintf(stderr, "== Info: %s", data);
+ /* FALLTHROUGH */
+ case CURLINFO_END: /* in case a new one is introduced to shock us */
+ break;
+ case CURLINFO_HEADER_OUT:
+ text = "=> Send header";
+ break;
+ case CURLINFO_DATA_OUT:
+ text = "=> Send data";
+ break;
+ case CURLINFO_SSL_DATA_OUT:
+ text = "=> Send SSL data";
+ break;
+ case CURLINFO_HEADER_IN:
+ text = "<= Recv header";
+ break;
+ case CURLINFO_DATA_IN:
+ text = "<= Recv data";
+ break;
+ case CURLINFO_SSL_DATA_IN:
+ text = "<= Recv SSL data";
+ break;
+ }
+
+ dump(text, stderr, (unsigned char *)data, size, config->trace_ascii);
+ return 0;
+}
+
+static int
+virCHMonitorCurlPerform(CURL *handle)
+{
+ CURLcode errorCode;
+ long responseCode = 0;
+
+ struct data config;
+
+ config.trace_ascii = 1; /* enable ascii tracing */
+
+ curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, my_trace);
+ curl_easy_setopt(handle, CURLOPT_DEBUGDATA, &config);
+
+ /* the DEBUGFUNCTION has no effect until we enable VERBOSE */
+ curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);
+
+ errorCode = curl_easy_perform(handle);
+
+ if (errorCode != CURLE_OK) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("curl_easy_perform() returned an error: %s (%d)"),
+ curl_easy_strerror(errorCode), errorCode);
+ return -1;
+ }
+
+ errorCode = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE,
+ &responseCode);
+
+ if (errorCode != CURLE_OK) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("curl_easy_getinfo(CURLINFO_RESPONSE_CODE) returned an "
+ "error: %s (%d)"), curl_easy_strerror(errorCode),
+ errorCode);
+ return -1;
+ }
+
+ if (responseCode < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("curl_easy_getinfo(CURLINFO_RESPONSE_CODE) returned a "
+ "negative response code"));
+ return -1;
+ }
+
+ return responseCode;
+}
+
+int
+virCHMonitorPutNoContent(virCHMonitorPtr mon, const char *endpoint)
+{
+ char *url;
+ int responseCode = 0;
+ int ret = -1;
+
+ url = g_strdup_printf("%s/%s", URL_ROOT, endpoint);
+
+ virObjectLock(mon);
+
+ /* reset all options of a libcurl session handle at first */
+ curl_easy_reset(mon->handle);
+
+ curl_easy_setopt(mon->handle, CURLOPT_UNIX_SOCKET_PATH, mon->socketpath);
+ curl_easy_setopt(mon->handle, CURLOPT_URL, url);
+ curl_easy_setopt(mon->handle, CURLOPT_PUT, true);
+ curl_easy_setopt(mon->handle, CURLOPT_HTTPHEADER, NULL);
+
+ responseCode = virCHMonitorCurlPerform(mon->handle);
+
+ virObjectUnlock(mon);
+
+ if (responseCode == 200 || responseCode == 204)
+ ret = 0;
+
+ VIR_FREE(url);
+ return ret;
+}
+
+int
+virCHMonitorGet(virCHMonitorPtr mon, const char *endpoint)
+{
+ char *url;
+ int responseCode = 0;
+ int ret = -1;
+
+ url = g_strdup_printf("%s/%s", URL_ROOT, endpoint);
+
+ virObjectLock(mon);
+
+ /* reset all options of a libcurl session handle at first */
+ curl_easy_reset(mon->handle);
+
+ curl_easy_setopt(mon->handle, CURLOPT_UNIX_SOCKET_PATH, mon->socketpath);
+ curl_easy_setopt(mon->handle, CURLOPT_URL, url);
+
+ responseCode = virCHMonitorCurlPerform(mon->handle);
+
+ virObjectUnlock(mon);
+
+ if (responseCode == 200 || responseCode == 204)
+ ret = 0;
+
+ VIR_FREE(url);
+ return ret;
+}
+
+int
+virCHMonitorPingVMM(virCHMonitorPtr mon)
+{
+ return virCHMonitorGet(mon, URL_VMM_PING);
+}
+
+int
+virCHMonitorShutdownVMM(virCHMonitorPtr mon)
+{
+ return virCHMonitorPutNoContent(mon, URL_VMM_SHUTDOWN);
+}
+
+int
+virCHMonitorCreateVM(virCHMonitorPtr mon)
+{
+ g_autofree char *url = NULL;
+ int responseCode = 0;
+ int ret = -1;
+ g_autofree char *payload = NULL;
+ struct curl_slist *headers = NULL;
+
+ url = g_strdup_printf("%s/%s", URL_ROOT, URL_VM_CREATE);
+ headers = curl_slist_append(headers, "Accept: application/json");
+ headers = curl_slist_append(headers, "Content-Type: application/json");
+ headers = curl_slist_append(headers, "Expect:");
+
+ if (virCHMonitorBuildVMJson(mon->vm->def, &payload) != 0)
+ return -1;
+
+ virObjectLock(mon);
+
+ /* reset all options of a libcurl session handle at first */
+ curl_easy_reset(mon->handle);
+
+ curl_easy_setopt(mon->handle, CURLOPT_UNIX_SOCKET_PATH, mon->socketpath);
+ curl_easy_setopt(mon->handle, CURLOPT_URL, url);
+ curl_easy_setopt(mon->handle, CURLOPT_CUSTOMREQUEST, "PUT");
+ curl_easy_setopt(mon->handle, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(mon->handle, CURLOPT_POSTFIELDS, payload);
+
+ responseCode = virCHMonitorCurlPerform(mon->handle);
+
+ virObjectUnlock(mon);
+
+ if (responseCode == 200 || responseCode == 204)
+ ret = 0;
+
+ curl_slist_free_all(headers);
+ VIR_FREE(url);
+ VIR_FREE(payload);
+ return ret;
+}
+
+int
+virCHMonitorBootVM(virCHMonitorPtr mon)
+{
+ return virCHMonitorPutNoContent(mon, URL_VM_BOOT);
+}
+
+int
+virCHMonitorShutdownVM(virCHMonitorPtr mon)
+{
+ return virCHMonitorPutNoContent(mon, URL_VM_SHUTDOWN);
+}
+
+int
+virCHMonitorRebootVM(virCHMonitorPtr mon)
+{
+ return virCHMonitorPutNoContent(mon, URL_VM_REBOOT);
+}
+
+int
+virCHMonitorSuspendVM(virCHMonitorPtr mon)
+{
+ return virCHMonitorPutNoContent(mon, URL_VM_Suspend);
+}
+
+int
+virCHMonitorResumeVM(virCHMonitorPtr mon)
+{
+ return virCHMonitorPutNoContent(mon, URL_VM_RESUME);
+}
diff --git a/src/ch/ch_monitor.h b/src/ch/ch_monitor.h
new file mode 100644
index 0000000000..bd6046a50b
--- /dev/null
+++ b/src/ch/ch_monitor.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright Intel Corp. 2020
+ *
+ * ch_driver.h: header file for Cloud-Hypervisor driver functions
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <curl/curl.h>
+
+#include "virobject.h"
+#include "domain_conf.h"
+
+#define URL_ROOT "http://localhost/api/v1"
+#define URL_VMM_SHUTDOWN "vmm.shutdown"
+#define URL_VMM_PING "vmm.ping"
+#define URL_VM_CREATE "vm.create"
+#define URL_VM_DELETE "vm.delete"
+#define URL_VM_BOOT "vm.boot"
+#define URL_VM_SHUTDOWN "vm.shutdown"
+#define URL_VM_REBOOT "vm.reboot"
+#define URL_VM_Suspend "vm.pause"
+#define URL_VM_RESUME "vm.resume"
+
+typedef struct _virCHMonitor virCHMonitor;
+typedef virCHMonitor *virCHMonitorPtr;
+
+struct _virCHMonitor {
+ virObjectLockable parent;
+
+ CURL *handle;
+
+ char *socketpath;
+
+ pid_t pid;
+
+ virDomainObjPtr vm;
+};
+
+virCHMonitorPtr virCHMonitorNew(virDomainObjPtr vm, const char *socketdir);
+void virCHMonitorClose(virCHMonitorPtr mon);
+
+int virCHMonitorCreateVM(virCHMonitorPtr mon);
+int virCHMonitorBootVM(virCHMonitorPtr mon);
+int virCHMonitorShutdownVM(virCHMonitorPtr mon);
+int virCHMonitorRebootVM(virCHMonitorPtr mon);
+int virCHMonitorSuspendVM(virCHMonitorPtr mon);
+int virCHMonitorResumeVM(virCHMonitorPtr mon);
diff --git a/src/ch/ch_process.c b/src/ch/ch_process.c
new file mode 100644
index 0000000000..15f4801549
--- /dev/null
+++ b/src/ch/ch_process.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright Intel Corp. 2020
+ *
+ * ch_driver.h: header file for Cloud-Hypervisor driver functions
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "ch_domain.h"
+#include "ch_monitor.h"
+#include "ch_process.h"
+#include "viralloc.h"
+#include "virerror.h"
+#include "virlog.h"
+
+#define VIR_FROM_THIS VIR_FROM_CH
+
+VIR_LOG_INIT("ch.ch_process");
+
+#define START_SOCKET_POSTFIX ": starting up socket\n"
+#define START_VM_POSTFIX ": starting up vm\n"
+
+
+
+static virCHMonitorPtr virCHProcessConnectMonitor(virCHDriverPtr driver,
+ virDomainObjPtr vm)
+{
+ virCHMonitorPtr monitor = NULL;
+ virCHDriverConfigPtr cfg = virCHDriverGetConfig(driver);
+
+ monitor = virCHMonitorNew(vm, cfg->stateDir);
+
+ virObjectUnref(cfg);
+ return monitor;
+}
+
+/**
+ * virCHProcessStart:
+ * @driver: pointer to driver structure
+ * @vm: pointer to virtual machine structure
+ * @reason: reason for switching vm to running state
+ *
+ * Starts Cloud-Hypervisor listen on a local socket
+ *
+ * Returns 0 on success or -1 in case of error
+ */
+int virCHProcessStart(virCHDriverPtr driver,
+ virDomainObjPtr vm,
+ virDomainRunningReason reason)
+{
+ int ret = -1;
+ virCHDomainObjPrivatePtr priv = vm->privateData;
+
+ if (!priv->monitor) {
+ /* And we can get the first monitor connection now too */
+ if (!(priv->monitor = virCHProcessConnectMonitor(driver, vm))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to create connection to CH socket"));
+ goto cleanup;
+ }
+
+ if (virCHMonitorCreateVM(priv->monitor) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to create guest VM"));
+ goto cleanup;
+ }
+ }
+
+ if (virCHMonitorBootVM(priv->monitor) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to boot guest VM"));
+ goto cleanup;
+ }
+
+ vm->pid = priv->monitor->pid;
+ vm->def->id = vm->pid;
+ virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, reason);
+
+ return 0;
+
+ cleanup:
+ if (ret)
+ virCHProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_FAILED);
+
+ return ret;
+}
+
+int virCHProcessStop(virCHDriverPtr driver G_GNUC_UNUSED,
+ virDomainObjPtr vm,
+ virDomainShutoffReason reason)
+{
+ virCHDomainObjPrivatePtr priv = vm->privateData;
+
+ VIR_DEBUG("Stopping VM name=%s pid=%d reason=%d",
+ vm->def->name, (int)vm->pid, (int)reason);
+
+ if (priv->monitor) {
+ virCHMonitorClose(priv->monitor);
+ priv->monitor = NULL;
+ }
+
+ vm->pid = -1;
+ vm->def->id = -1;
+
+ virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason);
+
+ return 0;
+}
diff --git a/src/ch/ch_process.h b/src/ch/ch_process.h
new file mode 100644
index 0000000000..22f3c39618
--- /dev/null
+++ b/src/ch/ch_process.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright Intel Corp. 2020
+ *
+ * ch_driver.h: header file for Cloud-Hypervisor driver functions
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "internal.h"
+
+int virCHProcessStart(virCHDriverPtr driver,
+ virDomainObjPtr vm,
+ virDomainRunningReason reason);
+int virCHProcessStop(virCHDriverPtr driver,
+ virDomainObjPtr vm,
+ virDomainShutoffReason reason);
diff --git a/src/ch/meson.build b/src/ch/meson.build
new file mode 100644
index 0000000000..e41691bc05
--- /dev/null
+++ b/src/ch/meson.build
@@ -0,0 +1,44 @@
+ch_driver_sources = [
+ 'ch_conf.c',
+ 'ch_conf.h',
+ 'ch_domain.c',
+ 'ch_domain.h',
+ 'ch_driver.c',
+ 'ch_driver.h',
+ 'ch_monitor.c',
+ 'ch_monitor.h',
+ 'ch_process.c',
+ 'ch_process.h',
+]
+
+driver_source_files += files(ch_driver_sources)
+
+stateful_driver_source_files += files(ch_driver_sources)
+
+if conf.has('WITH_CH')
+ ch_driver_impl = static_library(
+ 'virt_driver_ch_impl',
+ [
+ ch_driver_sources,
+ ],
+ dependencies: [
+ access_dep,
+ curl_dep,
+ log_dep,
+ src_dep,
+ ],
+ include_directories: [
+ conf_inc_dir,
+ ],
+ )
+
+ virt_modules += {
+ 'name': 'virt_driver_ch',
+ 'link_whole': [
+ ch_driver_impl,
+ ],
+ 'link_args': [
+ libvirt_no_undefined,
+ ],
+ }
+endif
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 5d3ae8bb28..11b183ad2c 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -130,6 +130,7 @@ VIR_ENUM_IMPL(virDomainVirt,
"parallels",
"bhyve",
"vz",
+ "ch",
);
VIR_ENUM_IMPL(virDomainOS,
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 8a0f26f5c0..4dba588728 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -139,6 +139,7 @@ typedef enum {
VIR_DOMAIN_VIRT_PARALLELS,
VIR_DOMAIN_VIRT_BHYVE,
VIR_DOMAIN_VIRT_VZ,
+ VIR_DOMAIN_VIRT_CH,
VIR_DOMAIN_VIRT_LAST
} virDomainVirtType;
diff --git a/src/meson.build b/src/meson.build
index 5d8deaf548..90fbee39e5 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -272,6 +272,7 @@ subdir('esx')
subdir('hyperv')
subdir('libxl')
subdir('lxc')
+subdir('ch')
subdir('openvz')
subdir('qemu')
subdir('test')
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 6798febf8d..819b381e21 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -6820,6 +6820,7 @@ qemuBuildMachineCommandLine(virCommandPtr cmd,
case VIR_DOMAIN_VIRT_PARALLELS:
case VIR_DOMAIN_VIRT_BHYVE:
case VIR_DOMAIN_VIRT_VZ:
+ case VIR_DOMAIN_VIRT_CH:
case VIR_DOMAIN_VIRT_NONE:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("the QEMU binary does not support %s"),
diff --git a/src/remote/remote_daemon.c b/src/remote/remote_daemon.c
index 1aa9bfc0d2..f8df8de095 100644
--- a/src/remote/remote_daemon.c
+++ b/src/remote/remote_daemon.c
@@ -169,6 +169,10 @@ static int daemonInitialize(void)
if (virDriverLoadModule("qemu", "qemuRegister", false) < 0)
return -1;
# endif
+# ifdef WITH_CH
+ if (virDriverLoadModule("ch", "chRegister", false) < 0)
+ return -1;
+# endif
# ifdef WITH_LXC
if (virDriverLoadModule("lxc", "lxcRegister", false) < 0)
return -1;
diff --git a/src/util/virerror.c b/src/util/virerror.c
index 507a29f50f..9446c908bf 100644
--- a/src/util/virerror.c
+++ b/src/util/virerror.c
@@ -144,6 +144,7 @@ VIR_ENUM_IMPL(virErrorDomain,
"TPM", /* 70 */
"BPF",
+ "Cloud-Hypervisor Driver",
);
diff --git a/tools/virsh.c b/tools/virsh.c
index 06ff5e8336..9555ea3374 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -510,6 +510,9 @@ virshShowVersion(vshControl *ctl G_GNUC_UNUSED)
#ifdef WITH_OPENVZ
vshPrint(ctl, " OpenVZ");
#endif
+#ifdef WITH_CH
+ vshPrint(ctl, " Cloud-Hypervisor");
+#endif
#ifdef WITH_VZ
vshPrint(ctl, " Virtuozzo");
#endif
--
2.26.2
4 years, 2 months
[PATCH] Modify virCPUarmCompare to perform compare actions
by Zhenyu Zheng
Modify virCPUarmCompare in cpu_arm.c to perform
actual compare actions. Compare host cpu vendor
and model info with guest cpu as initial implementation,
as most ARM clouds uses host-passthrogh mode.
Signed-off-by: Zhenyu Zheng <zheng.zhenyu(a)outlook.com>
---
src/cpu/cpu_arm.c | 188 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 184 insertions(+), 4 deletions(-)
diff --git a/src/cpu/cpu_arm.c b/src/cpu/cpu_arm.c
index 374a4d6f6c..98c5e551f5 100644
--- a/src/cpu/cpu_arm.c
+++ b/src/cpu/cpu_arm.c
@@ -118,6 +118,36 @@ virCPUarmMapNew(void)
return map;
}
+static int
+virCPUarmDataCopy(virCPUarmData *dst, const virCPUarmData *src)
+{
+ if (VIR_ALLOC(dst->features) < 0)
+ return -1;
+
+ dst->pvr = src->pvr;
+ dst->vendor_id = src->vendor_id;
+ dst->features = src->features;
+
+ return 0;
+}
+
+static virCPUDataPtr
+virCPUarmMakeCPUData(virArch arch,
+ virCPUarmData *data)
+{
+ virCPUDataPtr cpuData;
+
+ if (VIR_ALLOC(cpuData) < 0)
+ return NULL;
+
+ cpuData->arch = arch;
+
+ if (virCPUarmDataCopy(&cpuData->data.arm, data) < 0)
+ VIR_FREE(cpuData);
+
+ return cpuData;
+}
+
static void
virCPUarmDataClear(virCPUarmData *data)
{
@@ -376,6 +406,42 @@ virCPUarmModelParse(xmlXPathContextPtr ctxt,
return 0;
}
+static virCPUarmModelPtr
+virCPUarmModelCopy(virCPUarmModelPtr model)
+{
+ virCPUarmModelPtr copy;
+
+ copy = g_new0(virCPUarmModel, 1);
+ copy->name = g_strdup(model->name);
+ virCPUarmDataCopy(©->data, &model->data);
+ copy->vendor = model->vendor;
+
+ return g_steal_pointer(©);
+}
+
+static virCPUarmModelPtr
+virCPUarmModelFromCPU(const virCPUDef *cpu,
+ virCPUarmMapPtr map)
+{
+ g_autoptr(virCPUarmModel) model = NULL;
+
+ if (!cpu->model) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("no CPU model specified"));
+ return NULL;
+ }
+
+ if (!(model = virCPUarmModelFind(map, cpu->model))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unknown CPU model %s"), cpu->model);
+ return NULL;
+ }
+
+ model = virCPUarmModelCopy(model);
+
+ return g_steal_pointer(&model);
+}
+
static virCPUarmMapPtr
virCPUarmLoadMap(void)
{
@@ -463,11 +529,125 @@ virCPUarmBaseline(virCPUDefPtr *cpus,
}
static virCPUCompareResult
-virCPUarmCompare(virCPUDefPtr host G_GNUC_UNUSED,
- virCPUDefPtr cpu G_GNUC_UNUSED,
- bool failMessages G_GNUC_UNUSED)
+armCompute(virCPUDefPtr host,
+ virCPUDefPtr cpu,
+ virCPUDataPtr *guestData,
+ char **message)
{
- return VIR_CPU_COMPARE_IDENTICAL;
+ virCPUarmMapPtr map = NULL;
+ virCPUarmModelPtr host_model = NULL;
+ virCPUarmModelPtr guest_model = NULL;
+ virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR;
+ virArch arch;
+ size_t i;
+
+ if (cpu->arch != VIR_ARCH_NONE) {
+ bool found = false;
+
+ for (i = 0; i < G_N_ELEMENTS(archs); i++) {
+ if (archs[i] == cpu->arch) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ VIR_DEBUG("CPU arch %s does not match host arch",
+ virArchToString(cpu->arch));
+ if (message)
+ *message = g_strdup_printf(_("CPU arch %s does not match host arch"),
+ virArchToString(cpu->arch));
+
+ ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+ goto cleanup;
+ }
+ arch = cpu->arch;
+ } else {
+ arch = host->arch;
+ }
+
+ if (cpu->vendor &&
+ (!host->vendor || STRNEQ(cpu->vendor, host->vendor))) {
+ VIR_DEBUG("host CPU vendor does not match required CPU vendor %s",
+ cpu->vendor);
+ if (message) {
+ *message = g_strdup_printf(_("host CPU vendor does not match required "
+ "CPU vendor %s"),
+ cpu->vendor);
+ }
+
+ ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+ goto cleanup;
+ }
+
+ if (!(map = virCPUarmLoadMap()))
+ goto cleanup;
+
+ /* Host CPU information */
+ if (!(host_model = virCPUarmModelFromCPU(host, map)))
+ goto cleanup;
+
+ /* Guest CPU information */
+ if (!(guest_model = virCPUarmModelFromCPU(cpu, map)))
+ goto cleanup;
+
+ if (STRNEQ(guest_model->name, host_model->name)) {
+ VIR_DEBUG("host CPU model does not match required CPU model %s",
+ guest_model->name);
+ if (message) {
+ *message = g_strdup_printf(_("host CPU model does not match required "
+ "CPU model %s"),
+ guest_model->name);
+ }
+
+ ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+ goto cleanup;
+ }
+
+ if (guestData)
+ if (!(*guestData = virCPUarmMakeCPUData(arch, &guest_model->data)))
+ goto cleanup;
+
+ ret = VIR_CPU_COMPARE_IDENTICAL;
+
+ cleanup:
+ virCPUarmModelFree(host_model);
+ virCPUarmModelFree(guest_model);
+ return ret;
+}
+
+static virCPUCompareResult
+virCPUarmCompare(virCPUDefPtr host,
+ virCPUDefPtr cpu,
+ bool failIncompatible)
+{
+ virCPUCompareResult ret;
+ char *message = NULL;
+
+ if (!host || !host->model) {
+ if (failIncompatible) {
+ virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s",
+ _("unknown host CPU"));
+ } else {
+ VIR_WARN("unknown host CPU");
+ ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+ }
+ return -1;
+ }
+
+ ret = armCompute(host, cpu, NULL, &message);
+
+ if (failIncompatible && ret == VIR_CPU_COMPARE_INCOMPATIBLE) {
+ ret = VIR_CPU_COMPARE_ERROR;
+ if (message) {
+ virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s", message);
+ } else {
+ virReportError(VIR_ERR_CPU_INCOMPATIBLE, NULL);
+ }
+ }
+ VIR_FREE(message);
+
+ return ret;
}
static int
--
2.20.1
4 years, 2 months
[PATCH v5 0/2] bhyve: support 'isa' controller for LPC
by Roman Bogorodskiy
Changes from v4:
- Document 'isa' controller type in docs/formatdomain.html.in.
While here, add 'xenbus' which was also missing.
Roman Bogorodskiy (2):
conf: add 'isa' controller type
bhyve: support 'isa' controller for LPC
docs/formatdomain.html.in | 6 ++--
docs/schemas/domaincommon.rng | 6 ++++
src/bhyve/bhyve_command.c | 27 +++++++-------
src/bhyve/bhyve_device.c | 23 +++++++++---
src/bhyve/bhyve_domain.c | 25 +++++++++++--
src/bhyve/bhyve_domain.h | 2 --
src/conf/domain_conf.c | 9 +++++
src/conf/domain_conf.h | 8 +++++
src/qemu/qemu_command.c | 1 +
src/qemu/qemu_domain.c | 1 +
src/qemu/qemu_domain_address.c | 1 +
src/qemu/qemu_validate.c | 1 +
src/vbox/vbox_common.c | 1 +
...ml2argv-addr-isa-controller-on-slot-1.args | 10 ++++++
...2argv-addr-isa-controller-on-slot-1.ldargs | 3 ++
...xml2argv-addr-isa-controller-on-slot-1.xml | 26 ++++++++++++++
...l2argv-addr-isa-controller-on-slot-31.args | 10 ++++++
...argv-addr-isa-controller-on-slot-31.ldargs | 3 ++
...ml2argv-addr-isa-controller-on-slot-31.xml | 26 ++++++++++++++
...argv-addr-non-isa-controller-on-slot-1.xml | 23 ++++++++++++
.../bhyvexml2argv-console.args | 2 +-
.../bhyvexml2argv-isa-controller.args | 10 ++++++
.../bhyvexml2argv-isa-controller.ldargs | 3 ++
.../bhyvexml2argv-isa-controller.xml | 24 +++++++++++++
...bhyvexml2argv-isa-multiple-controllers.xml | 25 +++++++++++++
.../bhyvexml2argv-serial-grub-nocons.args | 2 +-
.../bhyvexml2argv-serial-grub.args | 2 +-
.../bhyvexml2argv-serial.args | 2 +-
.../bhyvexml2argvdata/bhyvexml2argv-uefi.args | 4 +--
.../bhyvexml2argv-vnc-autoport.args | 4 +--
.../bhyvexml2argv-vnc-vgaconf-io.args | 4 +--
.../bhyvexml2argv-vnc-vgaconf-off.args | 4 +--
.../bhyvexml2argv-vnc-vgaconf-on.args | 4 +--
.../bhyvexml2argvdata/bhyvexml2argv-vnc.args | 4 +--
tests/bhyvexml2argvtest.c | 5 +++
...l2xmlout-addr-isa-controller-on-slot-1.xml | 36 +++++++++++++++++++
...2xmlout-addr-isa-controller-on-slot-31.xml | 36 +++++++++++++++++++
.../bhyvexml2xmlout-console.xml | 3 ++
.../bhyvexml2xmlout-isa-controller.xml | 36 +++++++++++++++++++
.../bhyvexml2xmlout-serial-grub-nocons.xml | 3 ++
.../bhyvexml2xmlout-serial-grub.xml | 3 ++
.../bhyvexml2xmlout-serial.xml | 3 ++
.../bhyvexml2xmlout-vnc-autoport.xml | 3 ++
.../bhyvexml2xmlout-vnc-vgaconf-io.xml | 3 ++
.../bhyvexml2xmlout-vnc-vgaconf-off.xml | 3 ++
.../bhyvexml2xmlout-vnc-vgaconf-on.xml | 3 ++
.../bhyvexml2xmlout-vnc.xml | 3 ++
tests/bhyvexml2xmltest.c | 3 ++
48 files changed, 409 insertions(+), 40 deletions(-)
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-addr-isa-controller-on-slot-1.args
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-addr-isa-controller-on-slot-1.ldargs
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-addr-isa-controller-on-slot-1.xml
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-addr-isa-controller-on-slot-31.args
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-addr-isa-controller-on-slot-31.ldargs
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-addr-isa-controller-on-slot-31.xml
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-addr-non-isa-controller-on-slot-1.xml
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-isa-controller.args
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-isa-controller.ldargs
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-isa-controller.xml
create mode 100644 tests/bhyvexml2argvdata/bhyvexml2argv-isa-multiple-controllers.xml
create mode 100644 tests/bhyvexml2xmloutdata/bhyvexml2xmlout-addr-isa-controller-on-slot-1.xml
create mode 100644 tests/bhyvexml2xmloutdata/bhyvexml2xmlout-addr-isa-controller-on-slot-31.xml
create mode 100644 tests/bhyvexml2xmloutdata/bhyvexml2xmlout-isa-controller.xml
--
2.27.0
4 years, 2 months
[PATCH] docs/system: clarify deprecation scheduled
by Stefan Hajnoczi
The sentence explaining the deprecation schedule is ambiguous. Make it
clear that a feature deprecated in the Nth release is guaranteed to
remain available in the N+1th release. Removal can occur in the N+2nd
release or later.
As an example of this in action, see commit
25956af3fe5dd0385ad8017bc768a6afe41e2a74 ("block: Finish deprecation of
'qemu-img convert -n -o'"). The feature was deprecated in QEMU 4.2.0. It
was present in the 5.0.0 release and removed in the 5.1.0 release.
Signed-off-by: Stefan Hajnoczi <stefanha(a)redhat.com>
---
docs/system/deprecated.rst | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/system/deprecated.rst b/docs/system/deprecated.rst
index 851dbdeb8a..fecfb2f1c1 100644
--- a/docs/system/deprecated.rst
+++ b/docs/system/deprecated.rst
@@ -4,9 +4,9 @@ Deprecated features
In general features are intended to be supported indefinitely once
introduced into QEMU. In the event that a feature needs to be removed,
it will be listed in this section. The feature will remain functional
-for 2 releases prior to actual removal. Deprecated features may also
-generate warnings on the console when QEMU starts up, or if activated
-via a monitor command, however, this is not a mandatory requirement.
+for 1 more release after deprecation. Deprecated features may also generate
+warnings on the console when QEMU starts up, or if activated via a monitor
+command, however, this is not a mandatory requirement.
Prior to the 2.10.0 release there was no official policy on how
long features would be deprecated prior to their removal, nor
--
2.26.2
4 years, 2 months
device compatibility interface for live migration with assigned devices
by Yan Zhao
hi folks,
we are defining a device migration compatibility interface that helps upper
layer stack like openstack/ovirt/libvirt to check if two devices are
live migration compatible.
The "devices" here could be MDEVs, physical devices, or hybrid of the two.
e.g. we could use it to check whether
- a src MDEV can migrate to a target MDEV,
- a src VF in SRIOV can migrate to a target VF in SRIOV,
- a src MDEV can migration to a target VF in SRIOV.
(e.g. SIOV/SRIOV backward compatibility case)
The upper layer stack could use this interface as the last step to check
if one device is able to migrate to another device before triggering a real
live migration procedure.
we are not sure if this interface is of value or help to you. please don't
hesitate to drop your valuable comments.
(1) interface definition
The interface is defined in below way:
__ userspace
/\ \
/ \write
/ read \
________/__________ ___\|/_____________
| migration_version | | migration_version |-->check migration
--------------------- --------------------- compatibility
device A device B
a device attribute named migration_version is defined under each device's
sysfs node. e.g. (/sys/bus/pci/devices/0000\:00\:02.0/$mdev_UUID/migration_version).
userspace tools read the migration_version as a string from the source device,
and write it to the migration_version sysfs attribute in the target device.
The userspace should treat ANY of below conditions as two devices not compatible:
- any one of the two devices does not have a migration_version attribute
- error when reading from migration_version attribute of one device
- error when writing migration_version string of one device to
migration_version attribute of the other device
The string read from migration_version attribute is defined by device vendor
driver and is completely opaque to the userspace.
for a Intel vGPU, string format can be defined like
"parent device PCI ID" + "version of gvt driver" + "mdev type" + "aggregator count".
for an NVMe VF connecting to a remote storage. it could be
"PCI ID" + "driver version" + "configured remote storage URL"
for a QAT VF, it may be
"PCI ID" + "driver version" + "supported encryption set".
(to avoid namespace confliction from each vendor, we may prefix a driver name to
each migration_version string. e.g. i915-v1-8086-591d-i915-GVTg_V5_8-1)
(2) backgrounds
The reason we hope the migration_version string is opaque to the userspace
is that it is hard to generalize standard comparing fields and comparing
methods for different devices from different vendors.
Though userspace now could still do a simple string compare to check if
two devices are compatible, and result should also be right, it's still
too limited as it excludes the possible candidate whose migration_version
string fails to be equal.
e.g. an MDEV with mdev_type_1, aggregator count 3 is probably compatible
with another MDEV with mdev_type_3, aggregator count 1, even their
migration_version strings are not equal.
(assumed mdev_type_3 is of 3 times equal resources of mdev_type_1).
besides that, driver version + configured resources are all elements demanding
to take into account.
So, we hope leaving the freedom to vendor driver and let it make the final decision
in a simple reading from source side and writing for test in the target side way.
we then think the device compatibility issues for live migration with assigned
devices can be divided into two steps:
a. management tools filter out possible migration target devices.
Tags could be created according to info from product specification.
we think openstack/ovirt may have vendor proprietary components to create
those customized tags for each product from each vendor.
e.g.
for Intel vGPU, with a vGPU(a MDEV device) in source side, the tags to
search target vGPU are like:
a tag for compatible parent PCI IDs,
a tag for a range of gvt driver versions,
a tag for a range of mdev type + aggregator count
for NVMe VF, the tags to search target VF may be like:
a tag for compatible PCI IDs,
a tag for a range of driver versions,
a tag for URL of configured remote storage.
b. with the output from step a, openstack/ovirt/libvirt could use our proposed
device migration compatibility interface to make sure the two devices are
indeed live migration compatible before launching the real live migration
process to start stream copying, src device stopping and target device
resuming.
It is supposed that this step would not bring any performance penalty as
-in kernel it's just a simple string decoding and comparing
-in openstack/ovirt, it could be done by extending current function
check_can_live_migrate_destination, along side claiming target resources.[1]
[1] https://specs.openstack.org/openstack/nova-specs/specs/stein/approved/lib...
Thanks
Yan
4 years, 2 months
[PATCH 0/5] Support CSS and DASD devices in node_device driver
by Bjoern Walk
This series enables support for channel subsystem (CSS) devices,
Direct Access Storage Devices (DASDs) in the node_device driver and
adds support to create vfio-ccw mdev devices in the node_device driver.
Boris Fiuczynski (5):
node_device: refactor udevProcessCCW
node_device: detect CSS devices
virsh: nodedev: ability to filter CSS capabilities
node_device: detect DASD devices
node_device: mdev vfio-ccw support
docs/formatnode.html.in | 12 +++
docs/manpages/virsh.rst | 2 +-
docs/schemas/nodedev.rng | 16 ++++
include/libvirt/libvirt-nodedev.h | 1 +
src/conf/domain_addr.c | 2 +-
src/conf/domain_addr.h | 3 +
src/conf/node_device_conf.c | 5 ++
src/conf/node_device_conf.h | 4 +-
src/conf/virnodedeviceobj.c | 4 +-
src/libvirt-nodedev.c | 1 +
src/libvirt_private.syms | 1 +
src/node_device/node_device_driver.c | 49 +++++++++---
src/node_device/node_device_udev.c | 77 ++++++++++++++++---
.../ccw_0_0_10000-invalid.xml | 4 +-
tests/nodedevschemadata/ccw_0_0_ffff.xml | 4 +-
tests/nodedevschemadata/css_0_0_ffff.xml | 10 +++
tests/nodedevxml2xmltest.c | 1 +
tools/virsh-nodedev.c | 3 +
18 files changed, 172 insertions(+), 27 deletions(-)
create mode 100644 tests/nodedevschemadata/css_0_0_ffff.xml
--
2.24.1
4 years, 2 months
[PATCH v2 0/7] qemu: implementation of transient disk option
by Masayoshi Mizuma
This patchset tries to implement transient option for qcow2 and raw
format disk. This uses the snapshot cleanup codes:
https://www.redhat.com/archives/libvir-list/2020-August/msg00299.html
It gets user available to set <transient/> to the domain xml file like as:
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='/var/lib/libvirt/images/guest.qcow2'/>
<target dev='vda' bus='virtio'/>
<transient/>
</disk>
Any changes which the Guest does to the disk is dropped when the Guest
is shutdowned.
There are some limitations for transient disk option so far:
- Supported disk format is qcow2 and raw
- blockdev capability is required for qemu
- Following features are blocked with transient disk option
- blockjobs
- Migration
- Disk hotplug
Masayoshi Mizuma (7):
qemuSnapshotDiskPrepareOne: Get available even if snapdisk is NULL
qemu: Introduce functions to handle transient disk
qemu: Add transient disk handler to start and stop the guest
qemu: Transient option gets avaiable for qcow2 and raw format disk
qemu: Block blockjobs when transient disk option is enabled
qemu: Block migration when transient disk option is enabled
qemu: Block disk hotplug when transient disk option is enabled
src/qemu/qemu_domain.c | 7 ++
src/qemu/qemu_hotplug.c | 6 ++
src/qemu/qemu_migration.c | 22 ++++++
src/qemu/qemu_process.c | 8 +++
src/qemu/qemu_snapshot.c | 139 +++++++++++++++++++++++++++++++++++++-
src/qemu/qemu_snapshot.h | 8 +++
src/qemu/qemu_validate.c | 9 ++-
src/util/virstoragefile.h | 2 +
8 files changed, 196 insertions(+), 5 deletions(-)
--
2.27.0
4 years, 2 months
[PATCH 0/5] remove NVDIMM auto-alignment for pSeries guests
by Daniel Henrique Barboza
Hi,
In [1] Andrea proposed that we should *not* auto-align down
the NVDIMM size for pSeries guests. Instead we should error
out and provide users with a suggested aligned value. This
patch implements it.
[1] https://www.redhat.com/archives/libvir-list/2020-July/msg01471.html
Daniel Henrique Barboza (5):
qemu_domain.c: make qemuDomainGetMemorySizeAlignment() public
qemu_validate.c: add pSeries NVDIMM size alignment validation
qemu_domain.c: do not auto-align ppc64 NVDIMMs
qemu_domain.c: change qemuDomainMemoryDeviceAlignSize() return type
Revert "formatdomain.html.in: mention pSeries NVDIMM 'align down'
mechanic"
docs/formatdomain.html.in | 6 +-
src/qemu/qemu_domain.c | 57 ++-----------------
src/qemu/qemu_domain.h | 7 ++-
src/qemu/qemu_hotplug.c | 6 +-
src/qemu/qemu_validate.c | 43 ++++++++++++--
...ory-hotplug-nvdimm-ppc64.ppc64-latest.args | 2 +-
.../memory-hotplug-nvdimm-ppc64.xml | 2 +-
.../memory-hotplug-nvdimm-ppc64.xml | 2 +-
8 files changed, 55 insertions(+), 70 deletions(-)
--
2.26.2
4 years, 2 months
[PATCH] network: allow accept_ra == 0 when enabling ipv6 forwarding
by Ian Wienand
The checks modified here were added with
00d28a78b5d1f6eaf79f06ac59e31c568af9da37 to avoid losing routes on
hosts.
However, tools such as systemd-networking and NetworkManager manage
RA's in userspace and thus IPv6 may be up and working on an interface
even with accept_ra == 0.
This modifies the check to only error if an interface's accept_ra is
already set to "1"; as noted inline this seems to when it is likely
that enabling forwarding may change the RA acceptance behaviour of the
interface.
I have noticed this because I am using the IPv6 NAT features enabled
with 927acaedec7effbe67a154d8bfa0e67f7d08e6c7. I am using this on my
laptop which switches between wired and wireless connections; both of
which are configured in an unremarkable way by Fedora's NetworkManager
and get configured for IPv6 via SLAAC and whatever NetworkManager
magic it does. With this I can define and start a libvirt network
with <nat ipv6="yes"> and <ip family='ipv6'
address='fc00:abcd:ef12:34::' prefix='64'> and it seems to "just work"
for guests.
Signed-off-by: Ian Wienand <iwienand(a)redhat.com>
---
src/util/virnetdevip.c | 41 +++++++++++++++++++++++++++--------------
1 file changed, 27 insertions(+), 14 deletions(-)
diff --git a/src/util/virnetdevip.c b/src/util/virnetdevip.c
index 409f062c5c..de27cacfc9 100644
--- a/src/util/virnetdevip.c
+++ b/src/util/virnetdevip.c
@@ -496,7 +496,7 @@ virNetDevIPGetAcceptRA(const char *ifname)
}
struct virNetDevIPCheckIPv6ForwardingData {
- bool hasRARoutes;
+ bool hasKernelRARoutes;
/* Devices with conflicting accept_ra */
char **devices;
@@ -552,15 +552,26 @@ virNetDevIPCheckIPv6ForwardingCallback(struct nlmsghdr *resp,
if (!ifname)
return -1;
- accept_ra = virNetDevIPGetAcceptRA(ifname);
-
VIR_DEBUG("Checking route for device %s (%d), accept_ra: %d",
ifname, ifindex, accept_ra);
- if (accept_ra != 2 && virNetDevIPCheckIPv6ForwardingAddIF(data, &ifname) < 0)
+ accept_ra = virNetDevIPGetAcceptRA(ifname);
+ /* 0 = do no accept RA
+ * 1 = accept if forwarding disabled
+ * 2 = ovveride and accept RA when forwarding enabled
+ *
+ * When RA is managed by userspace (systemd-networkd or
+ * NetworkManager) accept_ra is unset and we don't need to
+ * worry about it. If it is 1, enabling forwarding might
+ * change the behaviour so the user needs to be warned.
+ */
+ if (accept_ra == 0)
+ return 0;
+
+ if (accept_ra == 1 && virNetDevIPCheckIPv6ForwardingAddIF(data, &ifname) < 0)
return -1;
- data->hasRARoutes = true;
+ data->hasKernelRARoutes = true;
return 0;
}
@@ -590,11 +601,13 @@ virNetDevIPCheckIPv6ForwardingCallback(struct nlmsghdr *resp,
VIR_DEBUG("Checking multipath route nexthop device %s (%d), accept_ra: %d",
ifname, nh->rtnh_ifindex, accept_ra);
- if (accept_ra != 2 && virNetDevIPCheckIPv6ForwardingAddIF(data, &ifname) < 0)
- return -1;
+ if (accept_ra == 1) {
+ if (virNetDevIPCheckIPv6ForwardingAddIF(data, &ifname) < 0)
+ return -1;
+ data->hasKernelRARoutes = true;
+ }
VIR_FREE(ifname);
- data->hasRARoutes = true;
len -= NLMSG_ALIGN(nh->rtnh_len);
VIR_WARNINGS_NO_CAST_ALIGN
@@ -613,7 +626,7 @@ virNetDevIPCheckIPv6Forwarding(void)
struct rtgenmsg genmsg;
size_t i;
struct virNetDevIPCheckIPv6ForwardingData data = {
- .hasRARoutes = false,
+ .hasKernelRARoutes = false,
.devices = NULL,
.ndevices = 0
};
@@ -644,11 +657,11 @@ virNetDevIPCheckIPv6Forwarding(void)
goto cleanup;
}
- valid = !data.hasRARoutes || data.ndevices == 0;
+ valid = !data.hasKernelRARoutes || data.ndevices == 0;
/* Check the global accept_ra if at least one isn't set on a
per-device basis */
- if (!valid && data.hasRARoutes) {
+ if (!valid && data.hasKernelRARoutes) {
int accept_ra = virNetDevIPGetAcceptRA(NULL);
valid = accept_ra == 2;
VIR_DEBUG("Checked global accept_ra: %d", accept_ra);
@@ -663,9 +676,9 @@ virNetDevIPCheckIPv6Forwarding(void)
}
virReportError(VIR_ERR_INTERNAL_ERROR,
- _("Check the host setup: enabling IPv6 forwarding with "
- "RA routes without accept_ra set to 2 is likely to cause "
- "routes loss. Interfaces to look at: %s"),
+ _("Check the host setup: interface has accept_ra set to 1 "
+ "and enabling forwarding without accept_ra set to 2 is "
+ "likely to cause routes loss. Interfaces to look at: %s"),
virBufferCurrentContent(&buf));
}
--
2.26.2
4 years, 2 months
[libvirt] [PATCH-for-4.2] hw/mips: Deprecate the r4k machine
by Philippe Mathieu-Daudé
The r4k machine was introduced in 2005 (6af0bf9c7) and its last
logical change was in 2005 (9542611a6). After we can count 164
maintenance commits (QEMU API changes) with the exception of
1 fix in 2015 (memory leak, commit 3ad9fd5a).
This machine was introduced as a proof of concept to run a MIPS
CPU. 2 years later, the Malta machine was add (commit 5856de80)
modeling a real platform.
Note also this machine has no specification except 5 lines in
the header of this file:
* emulates a simple machine with ISA-like bus.
* ISA IO space mapped to the 0x14000000 (PHYS) and
* ISA memory at the 0x10000000 (PHYS, 16Mb in size).
* All peripherial devices are attached to this "bus" with
* the standard PC ISA addresses.
It is time to deprecate this obsolete machine. Users are
recommended to use the Malta board, which hardware is well
documented.
Signed-off-by: Philippe Mathieu-Daudé <philmd(a)redhat.com>
---
qemu-deprecated.texi | 5 +++++
hw/mips/mips_r4k.c | 1 +
MAINTAINERS | 2 +-
3 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/qemu-deprecated.texi b/qemu-deprecated.texi
index 4b4b7425ac..05265b43c8 100644
--- a/qemu-deprecated.texi
+++ b/qemu-deprecated.texi
@@ -266,6 +266,11 @@ The 'scsi-disk' device is deprecated. Users should use 'scsi-hd' or
@section System emulator machines
+@subsection mips r4k platform (since 4.2)
+
+This machine type is very old and unmaintained. Users should use the 'malta'
+machine type instead.
+
@subsection pc-0.12, pc-0.13, pc-0.14 and pc-0.15 (since 4.0)
These machine types are very old and likely can not be used for live migration
diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c
index 70024235ae..0b79ad26cb 100644
--- a/hw/mips/mips_r4k.c
+++ b/hw/mips/mips_r4k.c
@@ -294,6 +294,7 @@ void mips_r4k_init(MachineState *machine)
static void mips_machine_init(MachineClass *mc)
{
+ mc->deprecation_reason = "use malta machine type instead";
mc->desc = "mips r4k platform";
mc->init = mips_r4k_init;
mc->block_default_type = IF_IDE;
diff --git a/MAINTAINERS b/MAINTAINERS
index 5e5e3e52d6..3b3a88e264 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -972,7 +972,7 @@ F: hw/net/mipsnet.c
R4000
M: Aurelien Jarno <aurelien(a)aurel32.net>
R: Aleksandar Rikalo <aleksandar.rikalo(a)rt-rk.com>
-S: Maintained
+S: Obsolete
F: hw/mips/mips_r4k.c
Fulong 2E
--
2.21.0
4 years, 2 months