libvirt/src/libxl/libxl_domain.c

1150 lines
34 KiB
C
Raw Normal View History

/*
* libxl_domain.c: libxl domain object private state
*
libxl: use driver-wide ctx in fd and timer event handling Long ago I incorrectly associated libxl fd and timer registrations with per-domain libxl_ctx objects. When creating a libxlDomainObjPrivate, a libxl_ctx is allocated, and libxl_osevent_register_hooks is called passing a pointer to the libxlDomainObjPrivate. When an fd or timer registration occurred, the registration callback received the libxlDomainObjPrivate, containing the per-domain libxl_ctx. This libxl_ctx was then used when informing libxl about fd events or timer expirations. The problem with this approach is that fd and timer registrations do not share the same lifespan as libxlDomainObjPrivate, and hence the per-domain libxl_ctx ojects. The result is races between per-domain libxl_ctx's being destoryed and events firing on associated fds/timers, typically manifesting as an assert in libxl libxl_internal.h:2788: libxl__ctx_unlock: Assertion `!r' failed There is no need to associate libxlDomainObjPrivate objects with libxl's desire to use libvirt's event loop. Instead, the driver-wide libxl_ctx can be used for the fd and timer registrations. This patch moves the fd and timer handling code away from the domain-specific code in libxl_domain.c into libxl_driver.c. While at it, function names were changed a bit to better describe their purpose. The unnecessary locking was also removed since the code simply provides a wrapper over the event loop interface. Indeed the locks may have been causing some deadlocks when repeatedly creating/destroying muliple domains. There have also been rumors about such deadlocks during parallel OpenStack Tempest runs. Signed-off-by: Jim Fehlig <jfehlig@suse.com>
2015-02-02 15:12:58 -06:00
* Copyright (C) 2011-2015 SUSE LINUX Products GmbH, Nuernberg, Germany.
*
* 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/>.
*
* Authors:
* Jim Fehlig <jfehlig@suse.com>
*/
#include <config.h>
#include <fcntl.h>
#include "libxl_domain.h"
#include "viralloc.h"
#include "viratomic.h"
#include "virfile.h"
#include "virerror.h"
#include "virlog.h"
#include "virstring.h"
#include "virtime.h"
#define VIR_FROM_THIS VIR_FROM_LIBXL
VIR_LOG_INIT("libxl.libxl_domain");
VIR_ENUM_IMPL(libxlDomainJob, LIBXL_JOB_LAST,
"none",
"query",
"destroy",
"modify",
);
static virClassPtr libxlDomainObjPrivateClass;
static void
libxlDomainObjPrivateDispose(void *obj);
static int
libxlDomainObjPrivateOnceInit(void)
{
if (!(libxlDomainObjPrivateClass = virClassNew(virClassForObjectLockable(),
"libxlDomainObjPrivate",
sizeof(libxlDomainObjPrivate),
libxlDomainObjPrivateDispose)))
return -1;
return 0;
}
VIR_ONCE_GLOBAL_INIT(libxlDomainObjPrivate)
static int
libxlDomainObjInitJob(libxlDomainObjPrivatePtr priv)
{
memset(&priv->job, 0, sizeof(priv->job));
if (virCondInit(&priv->job.cond) < 0)
return -1;
return 0;
}
static void
libxlDomainObjResetJob(libxlDomainObjPrivatePtr priv)
{
struct libxlDomainJobObj *job = &priv->job;
job->active = LIBXL_JOB_NONE;
job->owner = 0;
}
static void
libxlDomainObjFreeJob(libxlDomainObjPrivatePtr priv)
{
ignore_value(virCondDestroy(&priv->job.cond));
}
/* Give up waiting for mutex after 30 seconds */
#define LIBXL_JOB_WAIT_TIME (1000ull * 30)
/*
* obj must be locked before calling, libxlDriverPrivatePtr 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
libxlDomainObjBeginJob(libxlDriverPrivatePtr driver ATTRIBUTE_UNUSED,
virDomainObjPtr obj,
enum libxlDomainJob job)
{
libxlDomainObjPrivatePtr priv = obj->privateData;
unsigned long long now;
unsigned long long then;
if (virTimeMillisNow(&now) < 0)
return -1;
then = now + LIBXL_JOB_WAIT_TIME;
virObjectRef(obj);
while (priv->job.active) {
VIR_DEBUG("Wait normal job condition for starting job: %s",
libxlDomainJobTypeToString(job));
if (virCondWaitUntil(&priv->job.cond, &obj->parent.lock, then) < 0)
goto error;
}
libxlDomainObjResetJob(priv);
VIR_DEBUG("Starting job: %s", libxlDomainJobTypeToString(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)",
libxlDomainJobTypeToString(job),
obj->def->name,
libxlDomainJobTypeToString(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"));
virObjectUnref(obj);
return -1;
}
/*
* obj must be locked before calling
*
* To be called after completing the work associated with the
* earlier libxlDomainBeginJob() call
*
* Returns true if the remaining reference count on obj is
* non-zero, false if the reference count has dropped to zero
* and obj is disposed.
*/
bool
libxlDomainObjEndJob(libxlDriverPrivatePtr driver ATTRIBUTE_UNUSED,
virDomainObjPtr obj)
{
libxlDomainObjPrivatePtr priv = obj->privateData;
enum libxlDomainJob job = priv->job.active;
VIR_DEBUG("Stopping job: %s",
libxlDomainJobTypeToString(job));
libxlDomainObjResetJob(priv);
virCondSignal(&priv->job.cond);
return virObjectUnref(obj);
}
static void *
libxlDomainObjPrivateAlloc(void)
{
libxlDomainObjPrivatePtr priv;
if (libxlDomainObjPrivateInitialize() < 0)
return NULL;
if (!(priv = virObjectLockableNew(libxlDomainObjPrivateClass)))
return NULL;
if (!(priv->devs = virChrdevAlloc())) {
virObjectUnref(priv);
return NULL;
}
if (libxlDomainObjInitJob(priv) < 0) {
virChrdevFree(priv->devs);
virObjectUnref(priv);
return NULL;
}
return priv;
}
static void
libxlDomainObjPrivateDispose(void *obj)
{
libxlDomainObjPrivatePtr priv = obj;
libxlDomainObjFreeJob(priv);
virChrdevFree(priv->devs);
libxl_ctx_free(priv->ctx);
if (priv->logger_file)
VIR_FORCE_FCLOSE(priv->logger_file);
xtl_logger_destroy(priv->logger);
}
static void
libxlDomainObjPrivateFree(void *data)
{
libxlDomainObjPrivatePtr priv = data;
virObjectUnref(priv);
}
virDomainXMLPrivateDataCallbacks libxlDomainXMLPrivateDataCallbacks = {
.alloc = libxlDomainObjPrivateAlloc,
.free = libxlDomainObjPrivateFree,
};
static int
libxlDomainDeviceDefPostParse(virDomainDeviceDefPtr dev,
maint: avoid 'const fooPtr' in domain_conf 'const fooPtr' is the same as 'foo * const' (the pointer won't change, but it's contents can). But in general, if an interface is trying to be const-correct, it should be using 'const foo *' (the pointer is to data that can't be changed). Fix up offenders in src/conf/domain_conf, and their fallout. Several things to note: virObjectLock() requires a non-const argument; if this were C++, we could treat the locking field as 'mutable' and allow locking an otherwise 'const' object, but that is a more invasive change, so I instead dropped attempts to be const-correct on domain lookup. virXMLPropString and friends require a non-const xmlNodePtr - this is because libxml2 is not a const-correct library. We could make the src/util/virxml wrappers cast away const, but I figured it was easier to not try to mark xmlNodePtr as const. Finally, virDomainDeviceDefCopy was a rather hard conversion - it calls virDomainDeviceDefPostParse, which in turn in the xen driver was actually modifying the domain outside of the current device being visited. We should not be adding a device on the first per-device callback, but waiting until after all per-device callbacks are complete. * src/conf/domain_conf.h (virDomainObjListFindByID) (virDomainObjListFindByUUID, virDomainObjListFindByName) (virDomainObjAssignDef, virDomainObjListAdd): Drop attempt at const. (virDomainDeviceDefCopy): Use intended type. (virDomainDeviceDefParse, virDomainDeviceDefPostParseCallback) (virDomainVideoDefaultType, virDomainVideoDefaultRAM) (virDomainChrGetDomainPtrs): Make const-correct. * src/conf/domain_conf.c (virDomainObjListFindByID) (virDomainObjListFindByUUID, virDomainObjListFindByName) (virDomainDeviceDefCopy, virDomainObjListAdd) (virDomainObjAssignDef, virDomainHostdevSubsysUsbDefParseXML) (virDomainHostdevSubsysPciOrigStatesDefParseXML) (virDomainHostdevSubsysPciDefParseXML) (virDomainHostdevSubsysScsiDefParseXML) (virDomainControllerModelTypeFromString) (virDomainTPMDefParseXML, virDomainTimerDefParseXML) (virDomainSoundCodecDefParseXML, virDomainSoundDefParseXML) (virDomainWatchdogDefParseXML, virDomainRNGDefParseXML) (virDomainMemballoonDefParseXML, virDomainNVRAMDefParseXML) (virSysinfoParseXML, virDomainVideoAccelDefParseXML) (virDomainVideoDefParseXML, virDomainHostdevDefParseXML) (virDomainRedirdevDefParseXML) (virDomainRedirFilterUsbDevDefParseXML) (virDomainRedirFilterDefParseXML, virDomainIdMapEntrySort) (virDomainIdmapDefParseXML, virDomainVcpuPinDefParseXML) (virDiskNameToBusDeviceIndex, virDomainDeviceDefCopy) (virDomainVideoDefaultType, virDomainHostdevAssignAddress) (virDomainDeviceDefPostParseInternal, virDomainDeviceDefPostParse) (virDomainChrGetDomainPtrs, virDomainControllerSCSINextUnit) (virDomainSCSIDriveAddressIsUsed) (virDomainDriveAddressIsUsedByDisk) (virDomainDriveAddressIsUsedByHostdev): Fix fallout. * src/openvz/openvz_driver.c (openvzDomainDeviceDefPostParse): Likewise. * src/libxl/libxl_domain.c (libxlDomainDeviceDefPostParse): Likewise. * src/qemu/qemu_domain.c (qemuDomainDeviceDefPostParse) (qemuDomainDefaultNetModel): Likewise. * src/lxc/lxc_domain.c (virLXCDomainDeviceDefPostParse): Likewise. * src/uml/uml_driver.c (umlDomainDeviceDefPostParse): Likewise. * src/xen/xen_driver.c (xenDomainDeviceDefPostParse): Split... (xenDomainDefPostParse): ...since per-device callback is not the time to be adding a device. Signed-off-by: Eric Blake <eblake@redhat.com>
2013-10-08 10:08:25 -05:00
const virDomainDef *def,
virCapsPtr caps ATTRIBUTE_UNUSED,
void *opaque ATTRIBUTE_UNUSED)
{
if (dev->type == VIR_DOMAIN_DEVICE_CHR &&
dev->data.chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE &&
dev->data.chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_NONE &&
STRNEQ(def->os.type, "hvm"))
dev->data.chr->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_XEN;
if (dev->type == VIR_DOMAIN_DEVICE_NET &&
(dev->data.net->type == VIR_DOMAIN_NET_TYPE_BRIDGE ||
dev->data.net->type == VIR_DOMAIN_NET_TYPE_ETHERNET ||
dev->data.net->type == VIR_DOMAIN_NET_TYPE_NETWORK)) {
if (dev->data.net->nips > 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("multiple IP addresses not supported on device type %s"),
virDomainNetTypeToString(dev->data.net->type));
return -1;
}
}
if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV ||
(dev->type == VIR_DOMAIN_DEVICE_NET &&
dev->data.net->type == VIR_DOMAIN_NET_TYPE_HOSTDEV)) {
virDomainHostdevDefPtr hostdev;
virDomainHostdevSubsysPCIPtr pcisrc;
if (dev->type == VIR_DOMAIN_DEVICE_NET)
hostdev = &(dev->data.net)->data.hostdev.def;
else
hostdev = dev->data.hostdev;
pcisrc = &hostdev->source.subsys.u.pci;
/* forbid capabilities mode hostdev in this kind of hypervisor */
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("hostdev mode 'capabilities' is not "
"supported in %s"),
virDomainVirtTypeToString(def->virtType));
return -1;
}
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI &&
pcisrc->backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT)
pcisrc->backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN;
}
if (dev->type == VIR_DOMAIN_DEVICE_VIDEO && STREQ(def->os.type, "hvm")) {
int dm_type = libxlDomainGetEmulatorType(def);
switch (dev->data.video->type) {
case VIR_DOMAIN_VIDEO_TYPE_VGA:
case VIR_DOMAIN_VIDEO_TYPE_XEN:
if (dev->data.video->vram == 0) {
if (dm_type == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN)
dev->data.video->vram = 16 * 1024;
else
dev->data.video->vram = 8 * 1024;
}
break;
case VIR_DOMAIN_VIDEO_TYPE_CIRRUS:
if (dev->data.video->vram == 0) {
if (dm_type == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN)
dev->data.video->vram = 8 * 1024;
else
dev->data.video->vram = 4 * 1024;
}
break;
}
}
if (virDomainDeviceDefCheckUnsupportedMemoryDevice(dev) < 0)
return -1;
return 0;
}
static int
libxlDomainDefPostParse(virDomainDefPtr def,
virCapsPtr caps ATTRIBUTE_UNUSED,
void *opaque ATTRIBUTE_UNUSED)
{
/* Xen PV domains always have a PV console, so add one to the domain config
* via post-parse callback if not explicitly specified in the XML. */
if (STRNEQ(def->os.type, "hvm") && def->nconsoles == 0) {
virDomainChrDefPtr chrdef;
if (!(chrdef = virDomainChrDefNew()))
return -1;
chrdef->source.type = VIR_DOMAIN_CHR_TYPE_PTY;
chrdef->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE;
chrdef->target.port = 0;
chrdef->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_XEN;
if (VIR_ALLOC_N(def->consoles, 1) < 0) {
virDomainChrDefFree(chrdef);
return -1;
}
def->nconsoles = 1;
def->consoles[0] = chrdef;
}
/* memory hotplug tunables are not supported by this driver */
if (virDomainDefCheckUnsupportedMemoryHotplug(def) < 0)
return -1;
return 0;
}
virDomainDefParserConfig libxlDomainDefParserConfig = {
.macPrefix = { 0x00, 0x16, 0x3e },
.devicesPostParseCallback = libxlDomainDeviceDefPostParse,
.domainPostParseCallback = libxlDomainDefPostParse,
};
struct libxlShutdownThreadInfo
{
virDomainObjPtr vm;
libxl_event *event;
};
static void
libxlDomainShutdownThread(void *opaque)
{
struct libxlShutdownThreadInfo *shutdown_info = opaque;
virDomainObjPtr vm = shutdown_info->vm;
libxlDomainObjPrivatePtr priv = vm->privateData;
libxl_event *ev = shutdown_info->event;
libxlDriverPrivatePtr driver = priv->driver;
libxl_ctx *ctx = priv->ctx;
virObjectEventPtr dom_event = NULL;
libxl_shutdown_reason xl_reason = ev->u.domain_shutdown.shutdown_reason;
virDomainShutoffReason reason = VIR_DOMAIN_SHUTOFF_SHUTDOWN;
virObjectLock(vm);
if (xl_reason == LIBXL_SHUTDOWN_REASON_POWEROFF) {
dom_event = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
switch ((virDomainLifecycleAction) vm->def->onPoweroff) {
case VIR_DOMAIN_LIFECYCLE_DESTROY:
reason = VIR_DOMAIN_SHUTOFF_SHUTDOWN;
goto destroy;
case VIR_DOMAIN_LIFECYCLE_RESTART:
case VIR_DOMAIN_LIFECYCLE_RESTART_RENAME:
goto restart;
case VIR_DOMAIN_LIFECYCLE_PRESERVE:
case VIR_DOMAIN_LIFECYCLE_LAST:
goto cleanup;
}
} else if (xl_reason == LIBXL_SHUTDOWN_REASON_CRASH) {
dom_event = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_CRASHED);
switch ((virDomainLifecycleCrashAction) vm->def->onCrash) {
case VIR_DOMAIN_LIFECYCLE_CRASH_DESTROY:
reason = VIR_DOMAIN_SHUTOFF_CRASHED;
goto destroy;
case VIR_DOMAIN_LIFECYCLE_CRASH_RESTART:
case VIR_DOMAIN_LIFECYCLE_CRASH_RESTART_RENAME:
goto restart;
case VIR_DOMAIN_LIFECYCLE_CRASH_PRESERVE:
case VIR_DOMAIN_LIFECYCLE_CRASH_LAST:
goto cleanup;
case VIR_DOMAIN_LIFECYCLE_CRASH_COREDUMP_DESTROY:
libxlDomainAutoCoreDump(driver, vm);
goto destroy;
case VIR_DOMAIN_LIFECYCLE_CRASH_COREDUMP_RESTART:
libxlDomainAutoCoreDump(driver, vm);
goto restart;
}
} else if (xl_reason == LIBXL_SHUTDOWN_REASON_REBOOT) {
dom_event = virDomainEventLifecycleNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN);
switch ((virDomainLifecycleAction) vm->def->onReboot) {
case VIR_DOMAIN_LIFECYCLE_DESTROY:
reason = VIR_DOMAIN_SHUTOFF_SHUTDOWN;
goto destroy;
case VIR_DOMAIN_LIFECYCLE_RESTART:
case VIR_DOMAIN_LIFECYCLE_RESTART_RENAME:
goto restart;
case VIR_DOMAIN_LIFECYCLE_PRESERVE:
case VIR_DOMAIN_LIFECYCLE_LAST:
goto cleanup;
}
} else {
VIR_INFO("Unhandled shutdown_reason %d", xl_reason);
goto cleanup;
}
destroy:
if (dom_event) {
libxlDomainEventQueue(driver, dom_event);
dom_event = NULL;
}
libxl_domain_destroy(ctx, vm->def->id, NULL);
if (libxlDomainCleanupJob(driver, vm, reason)) {
if (!vm->persistent) {
virDomainObjListRemove(driver->domains, vm);
vm = NULL;
}
}
goto cleanup;
restart:
if (dom_event) {
libxlDomainEventQueue(driver, dom_event);
dom_event = NULL;
}
libxl_domain_destroy(ctx, vm->def->id, NULL);
libxlDomainCleanupJob(driver, vm, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
if (libxlDomainStart(driver, vm, false, -1) < 0) {
virErrorPtr err = virGetLastError();
VIR_ERROR(_("Failed to restart VM '%s': %s"),
vm->def->name, err ? err->message : _("unknown error"));
}
cleanup:
if (vm)
virObjectUnlock(vm);
if (dom_event)
libxlDomainEventQueue(driver, dom_event);
libxl_event_free(ctx, ev);
VIR_FREE(shutdown_info);
}
/*
* Handle previously registered domain event notification from libxenlight.
*/
void
libxlDomainEventHandler(void *data, VIR_LIBXL_EVENT_CONST libxl_event *event)
{
virDomainObjPtr vm = data;
libxlDomainObjPrivatePtr priv = vm->privateData;
libxl_shutdown_reason xl_reason = event->u.domain_shutdown.shutdown_reason;
struct libxlShutdownThreadInfo *shutdown_info;
virThread thread;
if (event->type != LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN) {
VIR_INFO("Unhandled event type %d", event->type);
goto error;
}
/*
* Similar to the xl implementation, ignore SUSPEND. Any actions needed
* after calling libxl_domain_suspend() are handled by it's callers.
*/
if (xl_reason == LIBXL_SHUTDOWN_REASON_SUSPEND)
goto error;
/*
* Start a thread to handle shutdown. We don't want to be tying up
* libxl's event machinery by doing a potentially lengthy shutdown.
*/
if (VIR_ALLOC(shutdown_info) < 0)
goto error;
shutdown_info->vm = data;
shutdown_info->event = (libxl_event *)event;
if (virThreadCreate(&thread, false, libxlDomainShutdownThread,
shutdown_info) < 0) {
/*
* Not much we can do on error here except log it.
*/
VIR_ERROR(_("Failed to create thread to handle domain shutdown"));
goto error;
}
/*
* libxl_event freed in shutdown thread
*/
return;
error:
/* Cast away any const */
libxl_event_free(priv->ctx, (libxl_event *)event);
}
int
libxlDomainObjPrivateInitCtx(virDomainObjPtr vm)
{
libxlDomainObjPrivatePtr priv = vm->privateData;
char *log_file;
int ret = -1;
if (priv->ctx)
return 0;
if (virAsprintf(&log_file, "%s/%s.log", LIBXL_LOG_DIR, vm->def->name) < 0)
return -1;
if ((priv->logger_file = fopen(log_file, "a")) == NULL) {
virReportSystemError(errno,
_("failed to open logfile %s"),
log_file);
goto cleanup;
}
priv->logger =
(xentoollog_logger *)xtl_createlogger_stdiostream(priv->logger_file,
XTL_DEBUG, 0);
if (!priv->logger) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot create libxenlight logger for domain %s"),
vm->def->name);
goto cleanup;
}
if (libxl_ctx_alloc(&priv->ctx, LIBXL_VERSION, 0, priv->logger)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Failed libxl context initialization"));
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(log_file);
return ret;
}
void
libxlDomainEventQueue(libxlDriverPrivatePtr driver, virObjectEventPtr event)
{
virObjectEventStateQueue(driver->domainEventState, event);
}
char *
libxlDomainManagedSavePath(libxlDriverPrivatePtr driver, virDomainObjPtr vm)
{
char *ret;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
ignore_value(virAsprintf(&ret, "%s/%s.save", cfg->saveDir, vm->def->name));
virObjectUnref(cfg);
return ret;
}
/*
* Open a saved image file and initialize domain definition from the header.
*
* Returns the opened fd on success, -1 on failure.
*/
int
libxlDomainSaveImageOpen(libxlDriverPrivatePtr driver,
libxlDriverConfigPtr cfg,
const char *from,
virDomainDefPtr *ret_def,
libxlSavefileHeaderPtr ret_hdr)
{
int fd;
virDomainDefPtr def = NULL;
libxlSavefileHeader hdr;
char *xml = NULL;
if ((fd = virFileOpenAs(from, O_RDONLY, 0, -1, -1, 0)) < 0) {
virReportSystemError(-fd,
_("Failed to open domain image file '%s'"), from);
goto error;
}
if (saferead(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
virReportError(VIR_ERR_OPERATION_FAILED,
"%s", _("failed to read libxl header"));
goto error;
}
if (memcmp(hdr.magic, LIBXL_SAVE_MAGIC, sizeof(hdr.magic))) {
virReportError(VIR_ERR_INVALID_ARG, "%s", _("image magic is incorrect"));
goto error;
}
if (hdr.version > LIBXL_SAVE_VERSION) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("image version is not supported (%d > %d)"),
hdr.version, LIBXL_SAVE_VERSION);
goto error;
}
if (hdr.xmlLen <= 0) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("invalid XML length: %d"), hdr.xmlLen);
goto error;
}
if (VIR_ALLOC_N(xml, hdr.xmlLen) < 0)
goto error;
if (saferead(fd, xml, hdr.xmlLen) != hdr.xmlLen) {
virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("failed to read XML"));
goto error;
}
if (!(def = virDomainDefParseString(xml, cfg->caps, driver->xmlopt,
1 << VIR_DOMAIN_VIRT_XEN,
VIR_DOMAIN_DEF_PARSE_INACTIVE)))
goto error;
VIR_FREE(xml);
*ret_def = def;
*ret_hdr = hdr;
return fd;
error:
VIR_FREE(xml);
virDomainDefFree(def);
VIR_FORCE_CLOSE(fd);
return -1;
}
/*
* Cleanup function for domain that has reached shutoff state.
*
* virDomainObjPtr must be locked on invocation
*/
void
libxlDomainCleanup(libxlDriverPrivatePtr driver,
virDomainObjPtr vm,
virDomainShutoffReason reason)
{
libxlDomainObjPrivatePtr priv = vm->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
int vnc_port;
char *file;
size_t i;
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
virHostdevReAttachDomainDevices(hostdev_mgr, LIBXL_DRIVER_NAME,
vm->def, VIR_HOSTDEV_SP_PCI, NULL);
vm->def->id = -1;
if (priv->deathW) {
libxl_evdisable_domain_death(priv->ctx, priv->deathW);
priv->deathW = NULL;
}
if (vm->persistent)
virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason);
if (virAtomicIntDecAndTest(&driver->nactive) && driver->inhibitCallback)
driver->inhibitCallback(false, driver->inhibitOpaque);
if ((vm->def->ngraphics == 1) &&
vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
vm->def->graphics[0]->data.vnc.autoport) {
vnc_port = vm->def->graphics[0]->data.vnc.port;
if (vnc_port >= LIBXL_VNC_PORT_MIN) {
if (virPortAllocatorRelease(driver->reservedVNCPorts,
vnc_port) < 0)
VIR_DEBUG("Could not mark port %d as unused", vnc_port);
}
}
/* Remove any cputune settings */
if (vm->def->cputune.nvcpupin) {
for (i = 0; i < vm->def->cputune.nvcpupin; ++i) {
virBitmapFree(vm->def->cputune.vcpupin[i]->cpumask);
VIR_FREE(vm->def->cputune.vcpupin[i]);
}
VIR_FREE(vm->def->cputune.vcpupin);
vm->def->cputune.nvcpupin = 0;
}
if (virAsprintf(&file, "%s/%s.xml", cfg->stateDir, vm->def->name) > 0) {
if (unlink(file) < 0 && errno != ENOENT && errno != ENOTDIR)
VIR_DEBUG("Failed to remove domain XML for %s", vm->def->name);
VIR_FREE(file);
}
if (vm->newDef) {
virDomainDefFree(vm->def);
vm->def = vm->newDef;
vm->def->id = -1;
vm->newDef = NULL;
}
virObjectUnref(cfg);
}
/*
* Cleanup function for domain that has reached shutoff state.
* Executed in the context of a job.
*
* virDomainObjPtr should be locked on invocation
* Returns true if references remain on virDomainObjPtr, false otherwise.
*/
bool
libxlDomainCleanupJob(libxlDriverPrivatePtr driver,
virDomainObjPtr vm,
virDomainShutoffReason reason)
{
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_DESTROY) < 0)
return true;
libxlDomainCleanup(driver, vm, reason);
return libxlDomainObjEndJob(driver, vm);
}
/*
* Register for domain events emitted by libxl.
*/
int
libxlDomainEventsRegister(libxlDriverPrivatePtr driver, virDomainObjPtr vm)
{
libxlDomainObjPrivatePtr priv = vm->privateData;
priv->driver = driver;
/* Always enable domain death events */
if (libxl_evenable_domain_death(priv->ctx, vm->def->id, 0, &priv->deathW))
goto error;
return 0;
error:
if (priv->deathW) {
libxl_evdisable_domain_death(priv->ctx, priv->deathW);
priv->deathW = NULL;
}
return -1;
}
/*
* Core dump domain to default dump path.
*
* virDomainObjPtr must be locked on invocation
*/
int
libxlDomainAutoCoreDump(libxlDriverPrivatePtr driver,
virDomainObjPtr vm)
{
libxlDomainObjPrivatePtr priv = vm->privateData;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
time_t curtime = time(NULL);
char timestr[100];
struct tm time_info;
char *dumpfile = NULL;
int ret = -1;
localtime_r(&curtime, &time_info);
strftime(timestr, sizeof(timestr), "%Y-%m-%d-%H:%M:%S", &time_info);
if (virAsprintf(&dumpfile, "%s/%s-%s",
cfg->autoDumpDir,
vm->def->name,
timestr) < 0)
goto cleanup;
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0)
goto cleanup;
/* Unlock virDomainObj while dumping core */
virObjectUnlock(vm);
libxl_domain_core_dump(priv->ctx, vm->def->id, dumpfile, NULL);
virObjectLock(vm);
ignore_value(libxlDomainObjEndJob(driver, vm));
ret = 0;
cleanup:
VIR_FREE(dumpfile);
virObjectUnref(cfg);
return ret;
}
int
libxlDomainSetVcpuAffinities(libxlDriverPrivatePtr driver, virDomainObjPtr vm)
{
libxlDomainObjPrivatePtr priv = vm->privateData;
virDomainDefPtr def = vm->def;
libxl_bitmap map;
virBitmapPtr cpumask = NULL;
uint8_t *cpumap = NULL;
virNodeInfo nodeinfo;
size_t cpumaplen;
int vcpu;
size_t i;
int ret = -1;
if (libxlDriverNodeGetInfo(driver, &nodeinfo) < 0)
goto cleanup;
cpumaplen = VIR_CPU_MAPLEN(VIR_NODEINFO_MAXCPUS(nodeinfo));
for (vcpu = 0; vcpu < def->cputune.nvcpupin; ++vcpu) {
if (vcpu != def->cputune.vcpupin[vcpu]->id)
continue;
if (VIR_ALLOC_N(cpumap, cpumaplen) < 0)
goto cleanup;
cpumask = def->cputune.vcpupin[vcpu]->cpumask;
for (i = 0; i < virBitmapSize(cpumask); ++i) {
if (virBitmapIsBitSet(cpumask, i))
VIR_USE_CPU(cpumap, i);
}
map.size = cpumaplen;
map.map = cpumap;
if (libxl_set_vcpuaffinity(priv->ctx, def->id, vcpu, &map) != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to pin vcpu '%d' with libxenlight"), vcpu);
goto cleanup;
}
VIR_FREE(cpumap);
}
ret = 0;
cleanup:
VIR_FREE(cpumap);
return ret;
}
int
libxlDomainFreeMem(libxlDomainObjPrivatePtr priv, libxl_domain_config *d_config)
{
uint32_t needed_mem;
uint32_t free_mem;
size_t i;
int ret = -1;
int tries = 3;
int wait_secs = 10;
if ((ret = libxl_domain_need_memory(priv->ctx, &d_config->b_info,
&needed_mem)) >= 0) {
for (i = 0; i < tries; ++i) {
if ((ret = libxl_get_free_memory(priv->ctx, &free_mem)) < 0)
break;
if (free_mem >= needed_mem) {
ret = 0;
break;
}
if ((ret = libxl_set_memory_target(priv->ctx, 0,
free_mem - needed_mem,
/* relative */ 1, 0)) < 0)
break;
ret = libxl_wait_for_free_memory(priv->ctx, 0, needed_mem,
wait_secs);
if (ret == 0 || ret != ERROR_NOMEM)
break;
if ((ret = libxl_wait_for_memory_target(priv->ctx, 0, 1)) < 0)
break;
}
}
return ret;
}
static void
libxlConsoleCallback(libxl_ctx *ctx, libxl_event *ev, void *for_callback)
{
virDomainObjPtr vm = for_callback;
size_t i;
virObjectLock(vm);
for (i = 0; i < vm->def->nconsoles; i++) {
virDomainChrDefPtr chr = vm->def->consoles[i];
if (chr && chr->source.type == VIR_DOMAIN_CHR_TYPE_PTY) {
libxl_console_type console_type;
char *console = NULL;
int ret;
console_type =
(chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL ?
LIBXL_CONSOLE_TYPE_SERIAL : LIBXL_CONSOLE_TYPE_PV);
ret = libxl_console_get_tty(ctx, ev->domid,
chr->target.port, console_type,
&console);
if (!ret) {
VIR_FREE(chr->source.data.file.path);
if (console && console[0] != '\0') {
ignore_value(VIR_STRDUP(chr->source.data.file.path,
console));
}
}
VIR_FREE(console);
}
}
virObjectUnlock(vm);
libxl_event_free(ctx, ev);
}
/*
* Start a domain through libxenlight.
*
* virDomainObjPtr must be locked on invocation
*/
int
libxlDomainStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm,
bool start_paused, int restore_fd)
{
libxl_domain_config d_config;
virDomainDefPtr def = NULL;
virObjectEventPtr event = NULL;
libxlSavefileHeader hdr;
int ret = -1;
uint32_t domid = 0;
char *dom_xml = NULL;
char *managed_save_path = NULL;
int managed_save_fd = -1;
libxlDomainObjPrivatePtr priv = vm->privateData;
libxlDriverConfigPtr cfg;
#ifdef LIBXL_HAVE_DOMAIN_CREATE_RESTORE_PARAMS
libxl_domain_restore_params params;
#endif
virHostdevManagerPtr hostdev_mgr = driver->hostdevMgr;
libxl_asyncprogress_how aop_console_how;
libxl_domain_config_init(&d_config);
if (libxlDomainObjPrivateInitCtx(vm) < 0)
return ret;
if (libxlDomainObjBeginJob(driver, vm, LIBXL_JOB_MODIFY) < 0)
return ret;
cfg = libxlDriverConfigGet(driver);
/* If there is a managed saved state restore it instead of starting
* from scratch. The old state is removed once the restoring succeeded. */
if (restore_fd < 0) {
managed_save_path = libxlDomainManagedSavePath(driver, vm);
if (managed_save_path == NULL)
goto endjob;
if (virFileExists(managed_save_path)) {
managed_save_fd = libxlDomainSaveImageOpen(driver, cfg,
managed_save_path,
&def, &hdr);
if (managed_save_fd < 0)
goto endjob;
restore_fd = managed_save_fd;
if (STRNEQ(vm->def->name, def->name) ||
memcmp(vm->def->uuid, def->uuid, VIR_UUID_BUFLEN)) {
char vm_uuidstr[VIR_UUID_STRING_BUFLEN];
char def_uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(vm->def->uuid, vm_uuidstr);
virUUIDFormat(def->uuid, def_uuidstr);
virReportError(VIR_ERR_OPERATION_FAILED,
_("cannot restore domain '%s' uuid %s from a file"
" which belongs to domain '%s' uuid %s"),
vm->def->name, vm_uuidstr, def->name, def_uuidstr);
goto endjob;
}
virDomainObjAssignDef(vm, def, true, NULL);
def = NULL;
if (unlink(managed_save_path) < 0)
VIR_WARN("Failed to remove the managed state %s",
managed_save_path);
vm->hasManagedSave = false;
}
VIR_FREE(managed_save_path);
}
if (libxlBuildDomainConfig(driver->reservedVNCPorts, vm->def,
priv->ctx, &d_config) < 0)
goto endjob;
if (cfg->autoballoon && libxlDomainFreeMem(priv, &d_config) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxenlight failed to get free memory for domain '%s'"),
d_config.c_info.name);
goto endjob;
}
if (virHostdevPrepareDomainDevices(hostdev_mgr, LIBXL_DRIVER_NAME,
vm->def, VIR_HOSTDEV_SP_PCI) < 0)
goto endjob;
/* Unlock virDomainObj while creating the domain */
virObjectUnlock(vm);
aop_console_how.for_callback = vm;
aop_console_how.callback = libxlConsoleCallback;
if (restore_fd < 0) {
ret = libxl_domain_create_new(priv->ctx, &d_config,
&domid, NULL, &aop_console_how);
} else {
#ifdef LIBXL_HAVE_DOMAIN_CREATE_RESTORE_PARAMS
params.checkpointed_stream = 0;
ret = libxl_domain_create_restore(priv->ctx, &d_config, &domid,
restore_fd, &params, NULL,
&aop_console_how);
#else
ret = libxl_domain_create_restore(priv->ctx, &d_config, &domid,
restore_fd, NULL, &aop_console_how);
#endif
}
virObjectLock(vm);
if (ret) {
if (restore_fd < 0)
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxenlight failed to create new domain '%s'"),
d_config.c_info.name);
else
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxenlight failed to restore domain '%s'"),
d_config.c_info.name);
goto endjob;
}
/*
* The domain has been successfully created with libxl, so it should
* be cleaned up if there are any subsequent failures.
*/
vm->def->id = domid;
if (libxlDomainEventsRegister(driver, vm) < 0)
goto cleanup_dom;
if ((dom_xml = virDomainDefFormat(vm->def, 0)) == NULL)
goto cleanup_dom;
if (libxl_userdata_store(priv->ctx, domid, "libvirt-xml",
(uint8_t *)dom_xml, strlen(dom_xml) + 1)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("libxenlight failed to store userdata"));
goto cleanup_dom;
}
if (libxlDomainSetVcpuAffinities(driver, vm) < 0)
goto cleanup_dom;
if (!start_paused) {
libxl_domain_unpause(priv->ctx, domid);
virDomainObjSetState(vm, VIR_DOMAIN_RUNNING, VIR_DOMAIN_RUNNING_BOOTED);
} else {
virDomainObjSetState(vm, VIR_DOMAIN_PAUSED, VIR_DOMAIN_PAUSED_USER);
}
if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0)
goto cleanup_dom;
if (virAtomicIntInc(&driver->nactive) == 1 && driver->inhibitCallback)
driver->inhibitCallback(true, driver->inhibitOpaque);
event = virDomainEventLifecycleNewFromObj(vm, VIR_DOMAIN_EVENT_STARTED,
restore_fd < 0 ?
VIR_DOMAIN_EVENT_STARTED_BOOTED :
VIR_DOMAIN_EVENT_STARTED_RESTORED);
if (event)
libxlDomainEventQueue(driver, event);
ret = 0;
goto endjob;
cleanup_dom:
libxl_domain_destroy(priv->ctx, domid, NULL);
vm->def->id = -1;
virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, VIR_DOMAIN_SHUTOFF_FAILED);
endjob:
if (!libxlDomainObjEndJob(driver, vm))
vm = NULL;
libxl_domain_config_dispose(&d_config);
VIR_FREE(dom_xml);
VIR_FREE(managed_save_path);
virDomainDefFree(def);
VIR_FORCE_CLOSE(managed_save_fd);
virObjectUnref(cfg);
return ret;
}
bool
libxlDomainDefCheckABIStability(libxlDriverPrivatePtr driver,
virDomainDefPtr src,
virDomainDefPtr dst)
{
virDomainDefPtr migratableDefSrc = NULL;
virDomainDefPtr migratableDefDst = NULL;
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
bool ret = false;
if (!(migratableDefSrc = virDomainDefCopy(src, cfg->caps, driver->xmlopt, true)) ||
!(migratableDefDst = virDomainDefCopy(dst, cfg->caps, driver->xmlopt, true)))
goto cleanup;
ret = virDomainDefCheckABIStability(migratableDefSrc, migratableDefDst);
cleanup:
virDomainDefFree(migratableDefSrc);
virDomainDefFree(migratableDefDst);
virObjectUnref(cfg);
return ret;
}