[libvirt] [PATCH] qemu: Do not wait if the PCI device is not managed when reattaching
by Osier Yang
Waiting for qemu-kvm cleaning up the PCI bar(s) mapping with long time
while the device is not managed is just waste of time.
---
src/qemu/qemu_hostdev.c | 15 ++++++++-------
1 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c
index c65f6f5..1fb373e 100644
--- a/src/qemu/qemu_hostdev.c
+++ b/src/qemu/qemu_hostdev.c
@@ -322,19 +322,20 @@ void qemuReattachPciDevice(pciDevice *dev, struct qemud_driver *driver)
{
int retries = 100;
+ if (!pciDeviceGetManaged(dev))
+ return;
+
while (pciWaitForDeviceCleanup(dev, "kvm_assigned_device")
&& retries) {
usleep(100*1000);
retries--;
}
- if (pciDeviceGetManaged(dev)) {
- if (pciReAttachDevice(dev, driver->activePciHostdevs) < 0) {
- virErrorPtr err = virGetLastError();
- VIR_ERROR(_("Failed to re-attach PCI device: %s"),
- err ? err->message : _("unknown error"));
- virResetError(err);
- }
+ if (pciReAttachDevice(dev, driver->activePciHostdevs) < 0) {
+ virErrorPtr err = virGetLastError();
+ VIR_ERROR(_("Failed to re-attach PCI device: %s"),
+ err ? err->message : _("unknown error"));
+ virResetError(err);
}
}
--
1.7.6
13 years, 1 month
[libvirt] [PATCH] Add support for autodestroy of guests to the LXC and UML drivers
by Daniel P. Berrange
From: "Daniel P. Berrange" <berrange(a)redhat.com>
I wrote this months ago and thought I had already submitted/merged
it. Obviously not. I need this for the virt sandbox tools asap.
We recently added support for VIR_DOMAIN_START_AUTODESTROY and
an impl to the QEMU driver. It is very desirable to support in
other drivers, so this adds it to LXC and UML
* src/lxc/lxc_conf.h, src/lxc/lxc_driver.c,
src/uml/uml_conf.h, src/uml/uml_driver.c: Wire up autodestroy
functions
---
src/lxc/lxc_conf.h | 5 ++
src/lxc/lxc_driver.c | 140 +++++++++++++++++++++++++++++++++++++++++--
src/uml/uml_conf.h | 6 ++
src/uml/uml_driver.c | 164 +++++++++++++++++++++++++++++++++++++++++++-------
4 files changed, 289 insertions(+), 26 deletions(-)
diff --git a/src/lxc/lxc_conf.h b/src/lxc/lxc_conf.h
index 66aa469..b124330 100644
--- a/src/lxc/lxc_conf.h
+++ b/src/lxc/lxc_conf.h
@@ -56,6 +56,11 @@ struct __lxc_driver {
int have_netns;
virDomainEventStatePtr domainEventState;
+
+ /* Mapping of 'char *uuidstr' -> virConnectPtr
+ * of guests which will be automatically killed
+ * when the virConnectPtr is closed*/
+ virHashTablePtr autodestroy;
};
int lxcLoadDriverConfig(lxc_driver_t *driver);
diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c
index c475887..f08e8d1 100644
--- a/src/lxc/lxc_driver.c
+++ b/src/lxc/lxc_driver.c
@@ -110,6 +110,19 @@ static void lxcDomainEventFlush(int timer, void *opaque);
static void lxcDomainEventQueue(lxc_driver_t *driver,
virDomainEventPtr event);
+static int lxcVmTerminate(lxc_driver_t *driver,
+ virDomainObjPtr vm,
+ virDomainShutoffReason reason);
+static int lxcProcessAutoDestroyInit(lxc_driver_t *driver);
+static void lxcProcessAutoDestroyRun(lxc_driver_t *driver,
+ virConnectPtr conn);
+static void lxcProcessAutoDestroyShutdown(lxc_driver_t *driver);
+static int lxcProcessAutoDestroyAdd(lxc_driver_t *driver,
+ virDomainObjPtr vm,
+ virConnectPtr conn);
+static int lxcProcessAutoDestroyRemove(lxc_driver_t *driver,
+ virDomainObjPtr vm);
+
static virDrvOpenStatus lxcOpen(virConnectPtr conn,
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
@@ -165,6 +178,7 @@ static int lxcClose(virConnectPtr conn)
lxcDriverLock(driver);
virDomainEventCallbackListRemoveConn(conn,
driver->domainEventState->callbacks);
+ lxcProcessAutoDestroyRun(driver, conn);
lxcDriverUnlock(driver);
conn->privateData = NULL;
@@ -1001,6 +1015,104 @@ cleanup:
}
+static int lxcProcessAutoDestroyInit(lxc_driver_t *driver)
+{
+ if (!(driver->autodestroy = virHashCreate(5, NULL)))
+ return -1;
+
+ return 0;
+}
+
+struct lxcProcessAutoDestroyData {
+ lxc_driver_t *driver;
+ virConnectPtr conn;
+};
+
+static void lxcProcessAutoDestroyDom(void *payload,
+ const void *name,
+ void *opaque)
+{
+ struct lxcProcessAutoDestroyData *data = opaque;
+ virConnectPtr conn = payload;
+ const char *uuidstr = name;
+ unsigned char uuid[VIR_UUID_BUFLEN];
+ virDomainObjPtr dom;
+ virDomainEventPtr event = NULL;
+
+ VIR_DEBUG("conn=%p uuidstr=%s thisconn=%p", conn, uuidstr, data->conn);
+
+ if (data->conn != conn)
+ return;
+
+ if (virUUIDParse(uuidstr, uuid) < 0) {
+ VIR_WARN("Failed to parse %s", uuidstr);
+ return;
+ }
+
+ if (!(dom = virDomainFindByUUID(&data->driver->domains,
+ uuid))) {
+ VIR_DEBUG("No domain object to kill");
+ return;
+ }
+
+ VIR_DEBUG("Killing domain");
+ lxcVmTerminate(data->driver, dom, VIR_DOMAIN_SHUTOFF_DESTROYED);
+ virDomainAuditStop(dom, "destroyed");
+ event = virDomainEventNewFromObj(dom,
+ VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_DESTROYED);
+
+ if (dom && !dom->persistent)
+ virDomainRemoveInactive(&data->driver->domains, dom);
+
+ if (dom)
+ virDomainObjUnlock(dom);
+ if (event)
+ lxcDomainEventQueue(data->driver, event);
+ virHashRemoveEntry(data->driver->autodestroy, uuidstr);
+}
+
+/*
+ * Precondition: driver is locked
+ */
+static void lxcProcessAutoDestroyRun(lxc_driver_t *driver, virConnectPtr conn)
+{
+ struct lxcProcessAutoDestroyData data = {
+ driver, conn
+ };
+ VIR_DEBUG("conn=%p", conn);
+ virHashForEach(driver->autodestroy, lxcProcessAutoDestroyDom, &data);
+}
+
+static void lxcProcessAutoDestroyShutdown(lxc_driver_t *driver)
+{
+ virHashFree(driver->autodestroy);
+}
+
+static int lxcProcessAutoDestroyAdd(lxc_driver_t *driver,
+ virDomainObjPtr vm,
+ virConnectPtr conn)
+{
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(vm->def->uuid, uuidstr);
+ VIR_DEBUG("vm=%s uuid=%s conn=%p", vm->def->name, uuidstr, conn);
+ if (virHashAddEntry(driver->autodestroy, uuidstr, conn) < 0)
+ return -1;
+ return 0;
+}
+
+static int lxcProcessAutoDestroyRemove(lxc_driver_t *driver,
+ virDomainObjPtr vm)
+{
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(vm->def->uuid, uuidstr);
+ VIR_DEBUG("vm=%s uuid=%s", vm->def->name, uuidstr);
+ if (virHashRemoveEntry(driver->autodestroy, uuidstr) < 0)
+ return -1;
+ return 0;
+}
+
+
/**
* lxcVmCleanup:
* @driver: pointer to driver structure
@@ -1028,6 +1140,9 @@ static void lxcVmCleanup(lxc_driver_t *driver,
VIR_FREE(xml);
}
+ /* Stop autodestroy in case guest is restarted */
+ lxcProcessAutoDestroyRemove(driver, vm);
+
virEventRemoveHandle(priv->monitorWatch);
VIR_FORCE_CLOSE(priv->monitor);
@@ -1496,6 +1611,7 @@ cleanup:
* @conn: pointer to connection
* @driver: pointer to driver structure
* @vm: pointer to virtual machine structure
+ * @autoDestroy: mark the domain for auto destruction
* @reason: reason for switching vm to running state
*
* Starts a vm
@@ -1505,6 +1621,7 @@ cleanup:
static int lxcVmStart(virConnectPtr conn,
lxc_driver_t * driver,
virDomainObjPtr vm,
+ bool autoDestroy,
virDomainRunningReason reason)
{
int rc = -1, r;
@@ -1665,6 +1782,10 @@ static int lxcVmStart(virConnectPtr conn,
goto cleanup;
}
+ if (autoDestroy &&
+ lxcProcessAutoDestroyAdd(driver, vm, conn) < 0)
+ goto cleanup;
+
/*
* Again, need to save the live configuration, because the function
* requires vm->def->id != -1 to save tty info surely.
@@ -1719,7 +1840,7 @@ static int lxcDomainStartWithFlags(virDomainPtr dom, unsigned int flags)
virDomainEventPtr event = NULL;
int ret = -1;
- virCheckFlags(0, -1);
+ virCheckFlags(VIR_DOMAIN_START_AUTODESTROY, -1);
lxcDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -1743,7 +1864,9 @@ static int lxcDomainStartWithFlags(virDomainPtr dom, unsigned int flags)
goto cleanup;
}
- ret = lxcVmStart(dom->conn, driver, vm, VIR_DOMAIN_RUNNING_BOOTED);
+ ret = lxcVmStart(dom->conn, driver, vm,
+ (flags & VIR_DOMAIN_START_AUTODESTROY),
+ VIR_DOMAIN_RUNNING_BOOTED);
if (ret == 0) {
event = virDomainEventNewFromObj(vm,
@@ -1796,7 +1919,7 @@ lxcDomainCreateAndStart(virConnectPtr conn,
virDomainPtr dom = NULL;
virDomainEventPtr event = NULL;
- virCheckFlags(0, NULL);
+ virCheckFlags(VIR_DOMAIN_START_AUTODESTROY, NULL);
lxcDriverLock(driver);
if (!(def = virDomainDefParseString(driver->caps, xml,
@@ -1819,7 +1942,9 @@ lxcDomainCreateAndStart(virConnectPtr conn,
goto cleanup;
def = NULL;
- if (lxcVmStart(conn, driver, vm, VIR_DOMAIN_RUNNING_BOOTED) < 0) {
+ if (lxcVmStart(conn, driver, vm,
+ (flags & VIR_DOMAIN_START_AUTODESTROY),
+ VIR_DOMAIN_RUNNING_BOOTED) < 0) {
virDomainAuditStart(vm, "booted", false);
virDomainRemoveInactive(&driver->domains, vm);
vm = NULL;
@@ -2054,7 +2179,7 @@ lxcAutostartDomain(void *payload, const void *name ATTRIBUTE_UNUSED, void *opaqu
virDomainObjLock(vm);
if (vm->autostart &&
!virDomainObjIsActive(vm)) {
- int ret = lxcVmStart(data->conn, data->driver, vm,
+ int ret = lxcVmStart(data->conn, data->driver, vm, false,
VIR_DOMAIN_RUNNING_BOOTED);
virDomainAuditStart(vm, "booted", ret >= 0);
if (ret < 0) {
@@ -2205,6 +2330,9 @@ static int lxcStartup(int privileged)
lxc_driver->caps->privateDataAllocFunc = lxcDomainObjPrivateAlloc;
lxc_driver->caps->privateDataFreeFunc = lxcDomainObjPrivateFree;
+ if (lxcProcessAutoDestroyInit(lxc_driver) < 0)
+ goto cleanup;
+
/* Get all the running persistent or transient configs first */
if (virDomainLoadAllConfigs(lxc_driver->caps,
&lxc_driver->domains,
@@ -2285,6 +2413,8 @@ static int lxcShutdown(void)
virDomainObjListDeinit(&lxc_driver->domains);
virDomainEventStateFree(lxc_driver->domainEventState);
+ lxcProcessAutoDestroyShutdown(lxc_driver);
+
virCapabilitiesFree(lxc_driver->caps);
VIR_FREE(lxc_driver->configDir);
VIR_FREE(lxc_driver->autostartDir);
diff --git a/src/uml/uml_conf.h b/src/uml/uml_conf.h
index 5401a7e..657f877 100644
--- a/src/uml/uml_conf.h
+++ b/src/uml/uml_conf.h
@@ -33,6 +33,7 @@
# include "virterror_internal.h"
# include "threads.h"
# include "command.h"
+# include "hash.h"
# define umlDebug(fmt, ...) do {} while(0)
@@ -64,6 +65,11 @@ struct uml_driver {
/* Event handling */
virDomainEventStatePtr domainEventState;
+
+ /* Mapping of 'char *uuidstr' -> virConnectPtr
+ * of guests which will be automatically killed
+ * when the virConnectPtr is closed*/
+ virHashTablePtr autodestroy;
};
diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c
index 2b7219a..16ab73a 100644
--- a/src/uml/uml_driver.c
+++ b/src/uml/uml_driver.c
@@ -75,6 +75,16 @@ struct _umlDomainObjPrivate {
int monitorWatch;
};
+static int umlProcessAutoDestroyInit(struct uml_driver *driver);
+static void umlProcessAutoDestroyRun(struct uml_driver *driver,
+ virConnectPtr conn);
+static void umlProcessAutoDestroyShutdown(struct uml_driver *driver);
+static int umlProcessAutoDestroyAdd(struct uml_driver *driver,
+ virDomainObjPtr vm,
+ virConnectPtr conn);
+static int umlProcessAutoDestroyRemove(struct uml_driver *driver,
+ virDomainObjPtr vm);
+
static int umlShutdown(void);
@@ -119,10 +129,10 @@ static void umlDomainEventQueue(struct uml_driver *driver,
static int umlStartVMDaemon(virConnectPtr conn,
struct uml_driver *driver,
- virDomainObjPtr vm);
+ virDomainObjPtr vm,
+ bool autoDestroy);
-static void umlShutdownVMDaemon(virConnectPtr conn,
- struct uml_driver *driver,
+static void umlShutdownVMDaemon(struct uml_driver *driver,
virDomainObjPtr vm,
virDomainShutoffReason reason);
@@ -150,7 +160,7 @@ umlAutostartDomain(void *payload, const void *name ATTRIBUTE_UNUSED, void *opaqu
!virDomainObjIsActive(vm)) {
int ret;
virResetLastError();
- ret = umlStartVMDaemon(data->conn, data->driver, vm);
+ ret = umlStartVMDaemon(data->conn, data->driver, vm, false);
virDomainAuditStart(vm, "booted", ret >= 0);
if (ret < 0) {
virErrorPtr err = virGetLastError();
@@ -309,7 +319,7 @@ reread:
continue;
}
- umlShutdownVMDaemon(NULL, driver, dom, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
+ umlShutdownVMDaemon(driver, dom, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
virDomainAuditStop(dom, "shutdown");
event = virDomainEventNewFromObj(dom,
VIR_DOMAIN_EVENT_STOPPED,
@@ -337,7 +347,7 @@ reread:
if (umlOpenMonitor(driver, dom) < 0) {
VIR_WARN("Could not open monitor for new domain");
- umlShutdownVMDaemon(NULL, driver, dom,
+ umlShutdownVMDaemon(driver, dom,
VIR_DOMAIN_SHUTOFF_FAILED);
virDomainAuditStop(dom, "failed");
event = virDomainEventNewFromObj(dom,
@@ -350,7 +360,7 @@ reread:
}
} else if (umlIdentifyChrPTY(driver, dom) < 0) {
VIR_WARN("Could not identify character devices for new domain");
- umlShutdownVMDaemon(NULL, driver, dom,
+ umlShutdownVMDaemon(driver, dom,
VIR_DOMAIN_SHUTOFF_FAILED);
virDomainAuditStop(dom, "failed");
event = virDomainEventNewFromObj(dom,
@@ -480,6 +490,9 @@ umlStartup(int privileged)
umlInotifyEvent, uml_driver, NULL)) < 0)
goto error;
+ if (umlProcessAutoDestroyInit(uml_driver) < 0)
+ goto error;
+
if (virDomainLoadAllConfigs(uml_driver->caps,
¨_driver->domains,
uml_driver->configDir,
@@ -577,7 +590,7 @@ umlShutdownOneVM(void *payload, const void *name ATTRIBUTE_UNUSED, void *opaque)
virDomainObjLock(dom);
if (virDomainObjIsActive(dom)) {
- umlShutdownVMDaemon(NULL, driver, dom, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
+ umlShutdownVMDaemon(driver, dom, VIR_DOMAIN_SHUTOFF_SHUTDOWN);
virDomainAuditStop(dom, "shutdown");
}
virDomainObjUnlock(dom);
@@ -612,6 +625,8 @@ umlShutdown(void) {
VIR_FREE(uml_driver->autostartDir);
VIR_FREE(uml_driver->monitorDir);
+ umlProcessAutoDestroyShutdown(uml_driver);
+
if (uml_driver->brctl)
brShutdown(uml_driver->brctl);
@@ -623,6 +638,104 @@ umlShutdown(void) {
}
+static int umlProcessAutoDestroyInit(struct uml_driver *driver)
+{
+ if (!(driver->autodestroy = virHashCreate(5, NULL)))
+ return -1;
+
+ return 0;
+}
+
+struct umlProcessAutoDestroyData {
+ struct uml_driver *driver;
+ virConnectPtr conn;
+};
+
+static void umlProcessAutoDestroyDom(void *payload,
+ const void *name,
+ void *opaque)
+{
+ struct umlProcessAutoDestroyData *data = opaque;
+ virConnectPtr conn = payload;
+ const char *uuidstr = name;
+ unsigned char uuid[VIR_UUID_BUFLEN];
+ virDomainObjPtr dom;
+ virDomainEventPtr event = NULL;
+
+ VIR_DEBUG("conn=%p uuidstr=%s thisconn=%p", conn, uuidstr, data->conn);
+
+ if (data->conn != conn)
+ return;
+
+ if (virUUIDParse(uuidstr, uuid) < 0) {
+ VIR_WARN("Failed to parse %s", uuidstr);
+ return;
+ }
+
+ if (!(dom = virDomainFindByUUID(&data->driver->domains,
+ uuid))) {
+ VIR_DEBUG("No domain object to kill");
+ return;
+ }
+
+ VIR_DEBUG("Killing domain");
+ umlShutdownVMDaemon(data->driver, dom, VIR_DOMAIN_SHUTOFF_DESTROYED);
+ virDomainAuditStop(dom, "destroyed");
+ event = virDomainEventNewFromObj(dom,
+ VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_DESTROYED);
+
+ if (dom && !dom->persistent)
+ virDomainRemoveInactive(&data->driver->domains, dom);
+
+ if (dom)
+ virDomainObjUnlock(dom);
+ if (event)
+ umlDomainEventQueue(data->driver, event);
+ virHashRemoveEntry(data->driver->autodestroy, uuidstr);
+}
+
+/*
+ * Precondition: driver is locked
+ */
+static void umlProcessAutoDestroyRun(struct uml_driver *driver, virConnectPtr conn)
+{
+ struct umlProcessAutoDestroyData data = {
+ driver, conn
+ };
+ VIR_DEBUG("conn=%p", conn);
+ virHashForEach(driver->autodestroy, umlProcessAutoDestroyDom, &data);
+}
+
+static void umlProcessAutoDestroyShutdown(struct uml_driver *driver)
+{
+ virHashFree(driver->autodestroy);
+}
+
+static int umlProcessAutoDestroyAdd(struct uml_driver *driver,
+ virDomainObjPtr vm,
+ virConnectPtr conn)
+{
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(vm->def->uuid, uuidstr);
+ VIR_DEBUG("vm=%s uuid=%s conn=%p", vm->def->name, uuidstr, conn);
+ if (virHashAddEntry(driver->autodestroy, uuidstr, conn) < 0)
+ return -1;
+ return 0;
+}
+
+static int umlProcessAutoDestroyRemove(struct uml_driver *driver,
+ virDomainObjPtr vm)
+{
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(vm->def->uuid, uuidstr);
+ VIR_DEBUG("vm=%s uuid=%s", vm->def->name, uuidstr);
+ if (virHashRemoveEntry(driver->autodestroy, uuidstr) < 0)
+ return -1;
+ return 0;
+}
+
+
static int umlReadPidFile(struct uml_driver *driver,
virDomainObjPtr vm)
{
@@ -842,8 +955,7 @@ error:
}
-static int umlCleanupTapDevices(virConnectPtr conn ATTRIBUTE_UNUSED,
- virDomainObjPtr vm) {
+static int umlCleanupTapDevices(virDomainObjPtr vm) {
int i;
int err;
int ret = 0;
@@ -873,7 +985,8 @@ static int umlCleanupTapDevices(virConnectPtr conn ATTRIBUTE_UNUSED,
static int umlStartVMDaemon(virConnectPtr conn,
struct uml_driver *driver,
- virDomainObjPtr vm) {
+ virDomainObjPtr vm,
+ bool autoDestroy) {
int ret;
char *logfile;
int logfd = -1;
@@ -935,7 +1048,7 @@ static int umlStartVMDaemon(virConnectPtr conn,
if (!(cmd = umlBuildCommandLine(conn, driver, vm))) {
VIR_FORCE_CLOSE(logfd);
virDomainConfVMNWFilterTeardown(vm);
- umlCleanupTapDevices(conn, vm);
+ umlCleanupTapDevices(vm);
return -1;
}
@@ -953,13 +1066,17 @@ static int umlStartVMDaemon(virConnectPtr conn,
if (ret < 0)
goto cleanup;
+ if (autoDestroy &&
+ umlProcessAutoDestroyAdd(driver, vm, conn) < 0)
+ goto cleanup;
+
ret = virDomainObjSetDefTransient(driver->caps, vm, false);
cleanup:
virCommandFree(cmd);
if (ret < 0) {
virDomainConfVMNWFilterTeardown(vm);
- umlCleanupTapDevices(conn, vm);
+ umlCleanupTapDevices(vm);
}
/* NB we don't mark it running here - we do that async
@@ -972,8 +1089,7 @@ cleanup:
return ret;
}
-static void umlShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED,
- struct uml_driver *driver ATTRIBUTE_UNUSED,
+static void umlShutdownVMDaemon(struct uml_driver *driver,
virDomainObjPtr vm,
virDomainShutoffReason reason)
{
@@ -997,7 +1113,10 @@ static void umlShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainObjSetState(vm, VIR_DOMAIN_SHUTOFF, reason);
virDomainConfVMNWFilterTeardown(vm);
- umlCleanupTapDevices(conn, vm);
+ umlCleanupTapDevices(vm);
+
+ /* Stop autodestroy in case guest is restarted */
+ umlProcessAutoDestroyRemove(driver, vm);
if (vm->newDef) {
virDomainDefFree(vm->def);
@@ -1072,6 +1191,7 @@ static int umlClose(virConnectPtr conn) {
umlDriverLock(driver);
virDomainEventCallbackListRemoveConn(conn,
driver->domainEventState->callbacks);
+ umlProcessAutoDestroyRun(driver, conn);
umlDriverUnlock(driver);
conn->privateData = NULL;
@@ -1342,7 +1462,7 @@ static virDomainPtr umlDomainCreate(virConnectPtr conn, const char *xml,
virDomainPtr dom = NULL;
virDomainEventPtr event = NULL;
- virCheckFlags(0, NULL);
+ virCheckFlags(VIR_DOMAIN_START_AUTODESTROY, NULL);
umlDriverLock(driver);
if (!(def = virDomainDefParseString(driver->caps, xml,
@@ -1359,7 +1479,8 @@ static virDomainPtr umlDomainCreate(virConnectPtr conn, const char *xml,
goto cleanup;
def = NULL;
- if (umlStartVMDaemon(conn, driver, vm) < 0) {
+ if (umlStartVMDaemon(conn, driver, vm,
+ (flags & VIR_DOMAIN_START_AUTODESTROY)) < 0) {
virDomainAuditStart(vm, "booted", false);
virDomainRemoveInactive(&driver->domains,
vm);
@@ -1436,7 +1557,7 @@ umlDomainDestroyFlags(virDomainPtr dom,
goto cleanup;
}
- umlShutdownVMDaemon(dom->conn, driver, vm, VIR_DOMAIN_SHUTOFF_DESTROYED);
+ umlShutdownVMDaemon(driver, vm, VIR_DOMAIN_SHUTOFF_DESTROYED);
virDomainAuditStop(vm, "destroyed");
event = virDomainEventNewFromObj(vm,
VIR_DOMAIN_EVENT_STOPPED,
@@ -1717,7 +1838,7 @@ static int umlDomainStartWithFlags(virDomainPtr dom, unsigned int flags) {
virDomainEventPtr event = NULL;
int ret = -1;
- virCheckFlags(0, -1);
+ virCheckFlags(VIR_DOMAIN_START_AUTODESTROY, -1);
umlDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -1728,7 +1849,8 @@ static int umlDomainStartWithFlags(virDomainPtr dom, unsigned int flags) {
goto cleanup;
}
- ret = umlStartVMDaemon(dom->conn, driver, vm);
+ ret = umlStartVMDaemon(dom->conn, driver, vm,
+ (flags & VIR_DOMAIN_START_AUTODESTROY));
virDomainAuditStart(vm, "booted", ret >= 0);
if (ret == 0)
event = virDomainEventNewFromObj(vm,
--
1.7.6.4
13 years, 1 month
[libvirt] [PATCH 0/3] snapshot: another snapshot-list filter
by Eric Blake
Previously, we could filter 'virsh snapshot-list --roots' to show
just snapshots lacking parents, which is good for top-down algorithms.
But sometimes you want just snapshots lacking children, for
bottom-up algorithms - this implements that.
Once again, I can only compile-test esx. Also, I haven't yet spent
time with the vbox code to write up this patch, partly because the
previous round of factoring it into reusable recursive iteration
is still in churn in an edit-compile-test cycle between Matthias and me.
I'm still debating on whether to emulate --leaves when talking to
an 0.9.5 server. Probably not too hard...
Eric Blake (3):
snapshot: add API for filtering by leaves
snapshot: implement LIST_LEAVES flag in qemu
snapshot: implement LIST_LEAVES flag in esx
include/libvirt/libvirt.h.in | 2 ++
src/conf/domain_conf.c | 8 ++++++--
src/esx/esx_driver.c | 29 +++++++++++++++++++++--------
src/esx/esx_vi.c | 27 ++++++++++++++++-----------
src/esx/esx_vi.h | 4 ++--
src/libvirt.c | 16 ++++++++++++++--
src/qemu/qemu_driver.c | 12 ++++++++----
tools/virsh.c | 12 ++++++++++++
tools/virsh.pod | 7 ++++++-
9 files changed, 87 insertions(+), 30 deletions(-)
--
1.7.4.4
13 years, 1 month
[libvirt] [PATCH v2] qemu: Do not reattach PCI device used by other domain when shutdown
by Osier Yang
When failing on starting a domain, it tries to reattach all the PCI
devices defined in the domain conf, regardless of whether the devices
are still used by other domain. This will cause the devices to be deleted
from the list qemu_driver->activePciHostdevs, thus the devices will be
thought as usable even if it's not true. And following commands
nodedev-{reattach,reset} will be successful.
How to reproduce:
1) Define two domains with same PCI device defined in the confs.
2) # virsh start domain1
3) # virsh start domain2
4) # virsh nodedev-reattach $pci_device
You will see the device will be reattached to host successfully.
As pciDeviceReattach just check if the device is still used by
other domain via checking if the device is in list driver->activePciHostdevs,
however, the device is deleted from the list by step 2).
This patch is to prohibit the bug by:
1) Prohibit a domain starting or device attachment right at
preparation period (qemuPrepareHostdevPCIDevices) if the
device is in list driver->activePciHostdevs, which means
it's used by other domain.
2) Introduces a new field for struct _pciDevice, (const char *used_by),
it will be set as the domain name at preparation period,
(qemuPrepareHostdevPCIDevices). Thus we can prohibit deleting
the device from driver->activePciHostdevs if it's still used by
other domain when stopping the domain process.
* src/pci.h (define two internal functions, pciDeviceSetUsedBy and
pciDevceGetUsedBy)
* src/pci.c (new field "const char *used_by" for struct _pciDevice,
implementations for the two new functions)
* src/libvirt_private.syms (Add the two new internal functions)
* src/qemu_hostdev.h (Modify the definition of functions
qemuPrepareHostdevPCIDevices, and qemuDomainReAttachHostdevDevices)
* src/qemu_hostdev.c (Prohibit preparation and don't delete the
device from activePciHostdevs list if it's still used by other domain)
* src/qemu_hotplug.c (Update function usage, as the definitions are
changed)
v1 - v2:
* char * used_by => const char *used_by, and no strdup()
* Other nits pointed out by Eric.
---
src/libvirt_private.syms | 2 ++
src/qemu/qemu_hostdev.c | 43 ++++++++++++++++++++++++++++++++++++++-----
src/qemu/qemu_hostdev.h | 2 ++
src/qemu/qemu_hotplug.c | 4 ++--
src/util/pci.c | 15 +++++++++++++++
src/util/pci.h | 3 +++
6 files changed, 62 insertions(+), 7 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 4f96518..4d50d86 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -880,6 +880,7 @@ virNWFilterHashTableRemoveEntry;
pciDettachDevice;
pciDeviceFileIterate;
pciDeviceGetManaged;
+pciDeviceGetUsedBy;
pciDeviceIsAssignable;
pciDeviceIsVirtualFunction;
pciDeviceListAdd;
@@ -892,6 +893,7 @@ pciDeviceListSteal;
pciDeviceNetName;
pciDeviceReAttachInit;
pciDeviceSetManaged;
+pciDeviceSetUsedBy;
pciFreeDevice;
pciGetDevice;
pciGetPhysicalFunction;
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c
index 6f77717..f354ea8 100644
--- a/src/qemu/qemu_hostdev.c
+++ b/src/qemu/qemu_hostdev.c
@@ -101,6 +101,7 @@ cleanup:
int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
+ const char *name,
virDomainHostdevDefPtr *hostdevs,
int nhostdevs)
{
@@ -111,7 +112,7 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
if (!(pcidevs = qemuGetPciHostDeviceList(hostdevs, nhostdevs)))
return -1;
- /* We have to use 4 loops here. *All* devices must
+ /* We have to use 6 loops here. *All* devices must
* be detached before we reset any of them, because
* in some cases you have to reset the whole PCI,
* which impacts all devices on it. Also, all devices
@@ -125,9 +126,16 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
pciDevice *dev = pciDeviceListGet(pcidevs, i);
- if (!pciDeviceIsAssignable(dev, !driver->relaxedACS))
- goto reattachdevs;
+ /* The device is in use by other active domain if
+ * the dev is in list driver->activePciHostdevs.
+ */
+ if (!pciDeviceIsAssignable(dev, !driver->relaxedACS) ||
+ pciDeviceListFind(driver->activePciHostdevs, dev))
+ goto cleanup;
+ }
+ for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
+ pciDevice *dev = pciDeviceListGet(pcidevs, i);
if (pciDeviceGetManaged(dev) &&
pciDettachDevice(dev, driver->activePciHostdevs) < 0)
goto reattachdevs;
@@ -150,6 +158,18 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
}
}
+ /* Now set the used_by_domain of the device in driver->activePciHostdevs
+ * as domain name.
+ */
+ for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
+ pciDevice *dev, *activeDev;
+
+ dev = pciDeviceListGet(pcidevs, i);
+ activeDev = pciDeviceListFind(driver->activePciHostdevs, dev);
+
+ pciDeviceSetUsedBy(activeDev, name);
+ }
+
/* Now steal all the devices from pcidevs */
while (pciDeviceListCount(pcidevs) > 0) {
pciDevice *dev = pciDeviceListGet(pcidevs, 0);
@@ -183,7 +203,7 @@ static int
qemuPrepareHostPCIDevices(struct qemud_driver *driver,
virDomainDefPtr def)
{
- return qemuPrepareHostdevPCIDevices(driver, def->hostdevs, def->nhostdevs);
+ return qemuPrepareHostdevPCIDevices(driver, def->name, def->hostdevs, def->nhostdevs);
}
@@ -258,6 +278,7 @@ void qemuReattachPciDevice(pciDevice *dev, struct qemud_driver *driver)
void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver,
+ const char *name,
virDomainHostdevDefPtr *hostdevs,
int nhostdevs)
{
@@ -277,6 +298,18 @@ void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver,
for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
pciDevice *dev = pciDeviceListGet(pcidevs, i);
+ pciDevice *activeDev = NULL;
+ const char *used_by = NULL;
+
+ /* Never delete the dev from list driver->activePciHostdevs
+ * if it's used by other domain.
+ */
+ activeDev = pciDeviceListFind(driver->activePciHostdevs, dev);
+ if (activeDev &&
+ (used_by = pciDeviceGetUsedBy(activeDev)) &&
+ STRNEQ(name, used_by))
+ continue;
+
pciDeviceListDel(driver->activePciHostdevs, dev);
}
@@ -305,5 +338,5 @@ void qemuDomainReAttachHostDevices(struct qemud_driver *driver,
if (!def->nhostdevs)
return;
- qemuDomainReAttachHostdevDevices(driver, def->hostdevs, def->nhostdevs);
+ qemuDomainReAttachHostdevDevices(driver, def->name, def->hostdevs, def->nhostdevs);
}
diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h
index 1f3d1bc..07d7de2 100644
--- a/src/qemu/qemu_hostdev.h
+++ b/src/qemu/qemu_hostdev.h
@@ -30,12 +30,14 @@
int qemuUpdateActivePciHostdevs(struct qemud_driver *driver,
virDomainDefPtr def);
int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
+ const char *name,
virDomainHostdevDefPtr *hostdevs,
int nhostdevs);
int qemuPrepareHostDevices(struct qemud_driver *driver,
virDomainDefPtr def);
void qemuReattachPciDevice(pciDevice *dev, struct qemud_driver *driver);
void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver,
+ const char *name,
virDomainHostdevDefPtr *hostdevs,
int nhostdevs);
void qemuDomainReAttachHostDevices(struct qemud_driver *driver,
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 4c1705d..bfa524b 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -893,7 +893,7 @@ int qemuDomainAttachHostPciDevice(struct qemud_driver *driver,
return -1;
}
- if (qemuPrepareHostdevPCIDevices(driver, &hostdev, 1) < 0)
+ if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, &hostdev, 1) < 0)
return -1;
if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
@@ -959,7 +959,7 @@ error:
hostdev->info.addr.pci.slot) < 0)
VIR_WARN("Unable to release PCI address on host device");
- qemuDomainReAttachHostdevDevices(driver, &hostdev, 1);
+ qemuDomainReAttachHostdevDevices(driver, vm->def->name, &hostdev, 1);
VIR_FREE(devstr);
VIR_FREE(configfd_name);
diff --git a/src/util/pci.c b/src/util/pci.c
index 8d8e157..888784a 100644
--- a/src/util/pci.c
+++ b/src/util/pci.c
@@ -62,6 +62,7 @@ struct _pciDevice {
char name[PCI_ADDR_LEN]; /* domain:bus:slot.function */
char id[PCI_ID_LEN]; /* product vendor */
char *path;
+ const char *used_by; /* The domain which uses the device */
int fd;
unsigned initted;
@@ -1312,6 +1313,7 @@ pciGetDevice(unsigned domain,
dev->bus = bus;
dev->slot = slot;
dev->function = function;
+ dev->used_by = NULL;
if (snprintf(dev->name, sizeof(dev->name), "%.4x:%.2x:%.2x.%.1x",
dev->domain, dev->bus, dev->slot,
@@ -1374,6 +1376,7 @@ pciFreeDevice(pciDevice *dev)
VIR_DEBUG("%s %s: freeing", dev->id, dev->name);
pciCloseConfig(dev);
VIR_FREE(dev->path);
+ dev->used_by = NULL;
VIR_FREE(dev);
}
@@ -1387,6 +1390,18 @@ unsigned pciDeviceGetManaged(pciDevice *dev)
return dev->managed;
}
+void
+pciDeviceSetUsedBy(pciDevice *dev, const char *name)
+{
+ dev->used_by = name;
+}
+
+const char *
+pciDeviceGetUsedBy(pciDevice *dev)
+{
+ return dev->used_by;
+}
+
void pciDeviceReAttachInit(pciDevice *pci)
{
pci->unbind_from_stub = 1;
diff --git a/src/util/pci.h b/src/util/pci.h
index a1600fe..640c6af 100644
--- a/src/util/pci.h
+++ b/src/util/pci.h
@@ -47,6 +47,9 @@ int pciResetDevice (pciDevice *dev,
void pciDeviceSetManaged(pciDevice *dev,
unsigned managed);
unsigned pciDeviceGetManaged(pciDevice *dev);
+void pciDeviceSetUsedBy(pciDevice *dev,
+ const char *used_by);
+const char *pciDeviceGetUsedBy(pciDevice *dev);
void pciDeviceReAttachInit(pciDevice *dev);
pciDeviceList *pciDeviceListNew (void);
--
1.7.6
13 years, 1 month
[libvirt] [PATCH] Fix virt-sanlock-cleanup documentation
by Philipp Hahn
The referenced page does not exist, but locking.html has a section about
sanlock.
Signed-off-by: Philipp Hahn <hahn(a)univention.de>
---
tools/virt-sanlock-cleanup.in | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/tools/virt-sanlock-cleanup.in b/tools/virt-sanlock-cleanup.in
index 72cd5e0..e143e7d 100644
--- a/tools/virt-sanlock-cleanup.in
+++ b/tools/virt-sanlock-cleanup.in
@@ -64,8 +64,8 @@ active.
=head1 EXIT STATUS
-Upon successful processing of leases cleanup, the exit status
-will be 0 will be set. Upon fatal error a non-zero status will
+Upon successful processing of leases cleanup, an exit status
+of 0 will be set. Upon fatal error a non-zero status will
be set.
=head1 AUTHOR
@@ -91,6 +91,6 @@ PURPOSE
=head1 SEE ALSO
-C<virsh(1)>, online instructions C<http://libvirt.org/locksanlock.html>
+C<virsh(1)>, online instructions C<http://libvirt.org/locking.html>
=cut
--
1.7.1
13 years, 1 month
[libvirt] [PATCH] qemu: avoid dereferencing a NULL pointer
by ajia@redhat.com
From: Alex Jia <ajia(a)redhat.com>
* src/qemu/qemu_hostdev.c: function 'pciDeviceListFind' probably explicitly
returns null, however, the function 'pciDeviceSetUsedBy' directly uses it
without any judgement.
Signed-off-by: Alex Jia <ajia(a)redhat.com>
---
src/qemu/qemu_hostdev.c | 5 ++---
1 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c
index c65f6f5..4e148b0 100644
--- a/src/qemu/qemu_hostdev.c
+++ b/src/qemu/qemu_hostdev.c
@@ -227,9 +227,8 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
pciDevice *dev, *activeDev;
dev = pciDeviceListGet(pcidevs, i);
- activeDev = pciDeviceListFind(driver->activePciHostdevs, dev);
-
- pciDeviceSetUsedBy(activeDev, name);
+ if ((activeDev = pciDeviceListFind(driver->activePciHostdevs, dev)))
+ pciDeviceSetUsedBy(activeDev, name);
}
/* Loop 6: Now steal all the devices from pcidevs */
--
1.7.1
13 years, 1 month
[libvirt] [PATCH] Fix two comments related to error handling
by Philipp Hahn
Signed-off-by: Philipp Hahn <hahn(a)univention.de>
---
include/libvirt/virterror.h | 2 +-
python/libvirt-override.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index dfbe2bc..a8549b7 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -206,7 +206,7 @@ typedef enum {
VIR_ERR_INVALID_STORAGE_VOL = 47, /* invalid storage vol object */
VIR_WAR_NO_STORAGE = 48, /* failed to start storage */
VIR_ERR_NO_STORAGE_POOL = 49, /* storage pool not found */
- VIR_ERR_NO_STORAGE_VOL = 50, /* storage pool not found */
+ VIR_ERR_NO_STORAGE_VOL = 50, /* storage volume not found */
VIR_WAR_NO_NODE = 51, /* failed to start node driver */
VIR_ERR_INVALID_NODE_DEVICE = 52, /* invalid node device object */
VIR_ERR_NO_NODE_DEVICE = 53, /* node device not found */
diff --git a/python/libvirt-override.py b/python/libvirt-override.py
index 387fddf..8427eab 100644
--- a/python/libvirt-override.py
+++ b/python/libvirt-override.py
@@ -79,7 +79,7 @@ class libvirtError(Exception):
# register the libvirt global error handler
#
def registerErrorHandler(f, ctx):
- """Register a Python written function to for error reporting.
+ """Register a Python function for error reporting.
The function is called back as f(ctx, error), with error
being a list of information about the error being raised.
Returns 1 in case of success."""
--
1.7.1
13 years, 1 month
[libvirt] [PATCH V1 0/6] Make part of inner workings of nwfilters more flexible
by Stefan Berger
The following series of patches re-does some of the inner workings
of nwfilters with the goal to enable users to write filters that have other
than the system-known chains supported right now ('root','arp','rarp','ipv4'
and 'ipv6'). Ideally users should be able to provide a chain name in the
chains XML attribute and either be able to jump to it as an 'action' or
have the chain created automatically as it is the case right now for those
chains enumerated before.
I am introducing internal priorities for the chains mentioned above so that
their creation can be made more flexible -- currently their creation and
the order in which they are accessed is hardcoded. This largely does away
with the hardcoded stuff.
Further, filters will be automatically accessed from the (ebtables)
'root' chain using the prefix of the name of the chain. As an example, this
filter will be accessed from the root chain for 'arp' packets since its
name 'arpmac' has the prefix 'arp'.
<filter name='allow-arpmac' chain='arpmac'>
<uuid>94abeecc-c956-0ac8-1f49-a06ee8995688</uuid>
<rule action='accept' direction='out' priority='100'>
<arp opcode='Request_Reverse' arpsrcmacaddr='$MAC' arpdstmacaddr='$MAC'
arpsrcipaddr='0.0.0.0' arpdstipaddr='0.0.0.0'/>
</rule>
<rule action='accept' direction='inout' priority='500'/>
</filter>
In the future the chains may have priorities supported in the XML in order to
control the order in which they are accessed.
<filter name='allow-arpmac' chain='arpmac' prirotiy='650'>
[...]
The series does not enable users yet to provide names of chains but instead
pushes the problem of their later enablement into the XML parser and
prepares the rest of the code to handle it (as far as this can be seen).
I did the testing with the test cases in libvirt-tck and did not see
regressions.
Regards,
Stefan
13 years, 1 month
[libvirt] [BUG, RFC] Python generator: missing error_codes in generated libvirError exceptions
by Philipp Hahn
Hello,
I just encountered a situation, where a KVM instance was reported as "no
state" in virsh. This broke some code of mine, which was
using "lookupByUUID()" to lookup several VMs. The Python code did raise a
libvirt.libvirtError('virDomainLookupByUUID() failed',), where all
get_error_*() functions return None. This is because the generated libvirt.py
wrapper just raises libvirtError('%s() failed'), which makes deteting the
exact fault harder. My current code explicitly checks for get_error_code() ==
VIR_ERR_NO_DOMAIN.
On further investigation I also noticed that qemudDomainLookupByUUID() uses
qemuReportError() to reports the error condition, which doesn't associate
that error condition with the connection.
What's the most appropriate way to fix this:
1. Assume lookupBy*() only will ever report an invalid lookup key and catch
all libvirtErrors?
2. Parse the exception string?
3. Extend the generator to add additional information from annotations?
4. Replace the generated code with hand-extended code.
Any ideas? Thanks for your input.
Sincerely
Philipp
--
Philipp Hahn Open Source Software Engineer hahn(a)univention.de
Univention GmbH Linux for Your Business fon: +49 421 22 232- 0
Mary-Somerville-Str.1 D-28359 Bremen fax: +49 421 22 232-99
http://www.univention.de/
13 years, 1 month
[libvirt] [libvirt-glib] Turn GVirStream into a GIOStream
by Marc-André Lureau
Allows to read async from a stream with GVirInputStream.
This is modelled after GSocket.
---
libvirt-gobject/Makefile.am | 2 +
libvirt-gobject/libvirt-gobject-connection.c | 2 +-
libvirt-gobject/libvirt-gobject-input-stream.c | 239 ++++++++++++++++++++++++
libvirt-gobject/libvirt-gobject-input-stream.h | 68 +++++++
libvirt-gobject/libvirt-gobject-stream.c | 130 +++++++++++++-
libvirt-gobject/libvirt-gobject-stream.h | 10 +-
6 files changed, 443 insertions(+), 8 deletions(-)
create mode 100644 libvirt-gobject/libvirt-gobject-input-stream.c
create mode 100644 libvirt-gobject/libvirt-gobject-input-stream.h
diff --git a/libvirt-gobject/Makefile.am b/libvirt-gobject/Makefile.am
index 8147db2..7013675 100644
--- a/libvirt-gobject/Makefile.am
+++ b/libvirt-gobject/Makefile.am
@@ -40,6 +40,8 @@ libvirt_gobject_1_0_la_HEADERS = \
libvirt_gobject_1_0_la_SOURCES = \
$(libvirt_gobject_1_0_la_HEADERS) \
libvirt-gobject-enums.c \
+ libvirt-gobject-input-stream.c \
+ libvirt-gobject-input-stream.h \
$(GOBJECT_SOURCE_FILES)
libvirt_gobject_1_0_la_CFLAGS = \
-DDATADIR="\"$(datadir)\"" \
diff --git a/libvirt-gobject/libvirt-gobject-connection.c b/libvirt-gobject/libvirt-gobject-connection.c
index 5fc0a9e..95cd878 100644
--- a/libvirt-gobject/libvirt-gobject-connection.c
+++ b/libvirt-gobject/libvirt-gobject-connection.c
@@ -1151,7 +1151,7 @@ GVirStream *gvir_connection_get_stream(GVirConnection *self,
klass = GVIR_CONNECTION_GET_CLASS(self);
g_return_val_if_fail(klass->stream_new, NULL);
- virStreamPtr st = virStreamNew(self->priv->conn, flags);
+ virStreamPtr st = virStreamNew(self->priv->conn, flags | VIR_STREAM_NONBLOCK);
return klass->stream_new(self, st);
}
diff --git a/libvirt-gobject/libvirt-gobject-input-stream.c b/libvirt-gobject/libvirt-gobject-input-stream.c
new file mode 100644
index 0000000..a76d670
--- /dev/null
+++ b/libvirt-gobject/libvirt-gobject-input-stream.c
@@ -0,0 +1,239 @@
+/*
+ * libvirt-gobject-input-stream.h: libvirt gobject integration
+ *
+ * Copyright (C) 2011 Red Hat
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors: Daniel P. Berrange <berrange(a)redhat.com>
+ * Marc-André Lureau <marcandre.lureau(a)redhat.com>
+ */
+
+#include <config.h>
+
+#include <libvirt/virterror.h>
+#include <string.h>
+
+#include "libvirt-glib/libvirt-glib.h"
+#include "libvirt-gobject/libvirt-gobject.h"
+#include "libvirt-gobject-input-stream.h"
+
+extern gboolean debugFlag;
+
+#define DEBUG(fmt, ...) do { if (G_UNLIKELY(debugFlag)) g_debug(fmt, ## __VA_ARGS__); } while (0)
+
+#define gvir_input_stream_get_type _gvir_input_stream_get_type
+G_DEFINE_TYPE(GVirInputStream, gvir_input_stream, G_TYPE_INPUT_STREAM);
+
+enum
+{
+ PROP_0,
+ PROP_STREAM
+};
+
+struct _GVirInputStreamPrivate
+{
+ GVirStream *stream;
+
+ /* pending operation metadata */
+ GSimpleAsyncResult *result;
+ GCancellable *cancellable;
+ gpointer buffer;
+ gsize count;
+};
+
+static void gvir_input_stream_get_property(GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GVirInputStream *stream = GVIR_INPUT_STREAM(object);
+
+ switch (prop_id) {
+ case PROP_STREAM:
+ g_value_set_object(value, stream->priv->stream);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ }
+}
+
+static void gvir_input_stream_set_property(GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GVirInputStream *stream = GVIR_INPUT_STREAM(object);
+
+ switch (prop_id) {
+ case PROP_STREAM:
+ stream->priv->stream = g_value_dup_object(value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ }
+}
+
+static void gvir_input_stream_finalize(GObject *object)
+{
+ GVirInputStream *stream = GVIR_INPUT_STREAM(object);
+
+ if (stream->priv->stream)
+ g_object_unref(stream->priv->stream);
+
+ if (G_OBJECT_CLASS(gvir_input_stream_parent_class)->finalize)
+ (*G_OBJECT_CLASS(gvir_input_stream_parent_class)->finalize)(object);
+}
+
+static void
+gvir_input_stream_read_ready (G_GNUC_UNUSED virStreamPtr st,
+ int events, void *opaque)
+{
+ GVirInputStream *stream = GVIR_INPUT_STREAM(opaque);
+ GVirInputStreamPrivate *priv = stream->priv;
+ GSimpleAsyncResult *simple;
+ GError *error = NULL;
+ gssize result;
+
+ g_return_if_fail(events & VIR_STREAM_EVENT_READABLE);
+
+ result = gvir_stream_receive(priv->stream, priv->buffer, priv->count,
+ priv->cancellable, &error);
+
+ if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+ g_warn_if_reached();
+ return;
+ }
+
+ simple = stream->priv->result;
+ stream->priv->result = NULL;
+
+ if (result >= 0)
+ g_simple_async_result_set_op_res_gssize(simple, result);
+
+ if (error)
+ g_simple_async_result_take_error(simple, error);
+
+ if (priv->cancellable) {
+ g_object_unref(stream->priv->cancellable);
+ priv->cancellable = NULL;
+ }
+
+ g_simple_async_result_complete(simple);
+ g_object_unref(simple);
+
+ return;
+}
+
+static void gvir_input_stream_read_async(GInputStream *stream,
+ void *buffer,
+ gsize count,
+ G_GNUC_UNUSED int io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GVirInputStream *input_stream = GVIR_INPUT_STREAM(stream);
+ virStreamPtr handle;
+
+ g_return_if_fail(GVIR_IS_INPUT_STREAM(stream));
+ g_return_if_fail(input_stream->priv->result == NULL);
+
+ g_object_get(input_stream->priv->stream, "handle", &handle, NULL);
+
+ if (virStreamEventAddCallback(handle, VIR_STREAM_EVENT_READABLE,
+ gvir_input_stream_read_ready, stream, NULL) < 0) {
+ g_simple_async_report_error_in_idle(G_OBJECT(stream),
+ callback,
+ user_data,
+ G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ "Couldn't add event callback %s",
+ G_STRFUNC);
+ goto end;
+ }
+
+ input_stream->priv->result =
+ g_simple_async_result_new(G_OBJECT(stream), callback, user_data,
+ gvir_input_stream_read_async);
+ if (cancellable)
+ g_object_ref(cancellable);
+ input_stream->priv->cancellable = cancellable;
+ input_stream->priv->buffer = buffer;
+ input_stream->priv->count = count;
+
+end:
+ virStreamFree(handle);
+}
+
+
+static gssize gvir_input_stream_read_finish(GInputStream *stream,
+ GAsyncResult *result,
+ G_GNUC_UNUSED GError **error)
+{
+ GVirInputStream *input_stream = GVIR_INPUT_STREAM(stream);
+ GSimpleAsyncResult *simple;
+ virStreamPtr handle;
+ gssize count;
+
+ g_return_val_if_fail(GVIR_IS_INPUT_STREAM(stream), -1);
+ g_object_get(input_stream->priv->stream, "handle", &handle, NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT(result);
+
+ g_warn_if_fail(g_simple_async_result_get_source_tag(simple) == gvir_input_stream_read_async);
+
+ count = g_simple_async_result_get_op_res_gssize(simple);
+
+ virStreamEventRemoveCallback(handle);
+ virStreamFree(handle);
+
+ return count;
+}
+
+
+static void gvir_input_stream_class_init(GVirInputStreamClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+ GInputStreamClass *ginputstream_class = G_INPUT_STREAM_CLASS(klass);
+
+ g_type_class_add_private(klass, sizeof(GVirInputStreamPrivate));
+
+ gobject_class->finalize = gvir_input_stream_finalize;
+ gobject_class->get_property = gvir_input_stream_get_property;
+ gobject_class->set_property = gvir_input_stream_set_property;
+
+ ginputstream_class->read_fn = NULL;
+ ginputstream_class->read_async = gvir_input_stream_read_async;
+ ginputstream_class->read_finish = gvir_input_stream_read_finish;
+
+ g_object_class_install_property(gobject_class, PROP_STREAM,
+ g_param_spec_object("stream",
+ "stream",
+ "GVirStream",
+ GVIR_TYPE_STREAM, G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void gvir_input_stream_init(GVirInputStream *stream)
+{
+ stream->priv = G_TYPE_INSTANCE_GET_PRIVATE(stream, GVIR_TYPE_INPUT_STREAM, GVirInputStreamPrivate);
+}
+
+GVirInputStream* _gvir_input_stream_new(GVirStream *stream)
+{
+ return GVIR_INPUT_STREAM(g_object_new(GVIR_TYPE_INPUT_STREAM, "stream", stream, NULL));
+}
diff --git a/libvirt-gobject/libvirt-gobject-input-stream.h b/libvirt-gobject/libvirt-gobject-input-stream.h
new file mode 100644
index 0000000..e8002b9
--- /dev/null
+++ b/libvirt-gobject/libvirt-gobject-input-stream.h
@@ -0,0 +1,68 @@
+/*
+ * libvirt-gobject-input-stream.h: libvirt gobject integration
+ *
+ * Copyright (C) 2011 Red Hat
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors: Daniel P. Berrange <berrange(a)redhat.com>
+ * Marc-André Lureau <marcandre.lureau(a)redhat.com>
+ */
+
+#if !defined(__LIBVIRT_GOBJECT_H__) && !defined(LIBVIRT_GOBJECT_BUILD)
+#error "Only <libvirt-gobject/libvirt-gobject.h> can be included directly."
+#endif
+
+#ifndef __LIBVIRT_GOBJECT_INPUT_STREAM_H__
+#define __LIBVIRT_GOBJECT_INPUT_STREAM_H__
+
+#include <gio/gio.h>
+#include "libvirt-gobject-stream.h"
+
+G_BEGIN_DECLS
+
+#define GVIR_TYPE_INPUT_STREAM (_gvir_input_stream_get_type ())
+#define GVIR_INPUT_STREAM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
+ GVIR_TYPE_INPUT_STREAM, GVirInputStream))
+#define GVIR_INPUT_STREAM_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \
+ GVIR_TYPE_INPUT_STREAM, GVirInputStreamClass))
+#define GVIR_IS_INPUT_STREAM(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
+ GVIR_TYPE_INPUT_STREAM))
+#define GVIR_IS_INPUT_STREAM_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \
+ GVIR_TYPE_INPUT_STREAM))
+#define GVIR_INPUT_STREAM_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \
+ GVIR_TYPE_INPUT_STREAM, GVirInputStreamClass))
+
+typedef struct _GVirInputStreamPrivate GVirInputStreamPrivate;
+typedef struct _GVirInputStreamClass GVirInputStreamClass;
+typedef struct _GVirInputStream GVirInputStream;
+
+struct _GVirInputStreamClass
+{
+ GInputStreamClass parent_class;
+};
+
+struct _GVirInputStream
+{
+ GInputStream parent_instance;
+ GVirInputStreamPrivate *priv;
+};
+
+GType _gvir_input_stream_get_type (void) G_GNUC_CONST;
+GVirInputStream * _gvir_input_stream_new (GVirStream *stream);
+
+G_END_DECLS
+
+#endif /* __LIBVIRT_GOBJECT_INPUT_STREAM_H__ */
diff --git a/libvirt-gobject/libvirt-gobject-stream.c b/libvirt-gobject/libvirt-gobject-stream.c
index 519d733..88e3a40 100644
--- a/libvirt-gobject/libvirt-gobject-stream.c
+++ b/libvirt-gobject/libvirt-gobject-stream.c
@@ -30,6 +30,8 @@
#include "libvirt-glib/libvirt-glib.h"
#include "libvirt-gobject/libvirt-gobject.h"
+#include "libvirt-gobject/libvirt-gobject-input-stream.h"
+
extern gboolean debugFlag;
#define DEBUG(fmt, ...) do { if (G_UNLIKELY(debugFlag)) g_debug(fmt, ## __VA_ARGS__); } while (0)
@@ -39,10 +41,12 @@ extern gboolean debugFlag;
struct _GVirStreamPrivate
{
- virStreamPtr handle;
+ virStreamPtr handle;
+ GInputStream *input_stream;
+ gboolean in_dispose;
};
-G_DEFINE_TYPE(GVirStream, gvir_stream, G_TYPE_OBJECT);
+G_DEFINE_TYPE(GVirStream, gvir_stream, G_TYPE_IO_STREAM);
enum {
@@ -60,6 +64,71 @@ gvir_stream_error_quark(void)
return g_quark_from_static_string("vir-g-stream");
}
+
+static GInputStream* gvir_stream_get_input_stream(GIOStream *io_stream)
+{
+ GVirStream *self = GVIR_STREAM(io_stream);
+
+ if (self->priv->input_stream == NULL)
+ self->priv->input_stream = (GInputStream *)_gvir_input_stream_new(self);
+
+ return self->priv->input_stream;
+}
+
+
+static gboolean gvir_stream_close(GIOStream *io_stream,
+ GCancellable *cancellable, G_GNUC_UNUSED GError **error)
+{
+ GVirStream *self = GVIR_STREAM(io_stream);
+
+ if (self->priv->input_stream)
+ g_input_stream_close(self->priv->input_stream, cancellable, NULL);
+
+ if (self->priv->in_dispose)
+ return TRUE;
+
+ return TRUE; /* FIXME: really close the stream? */
+}
+
+
+static void gvir_stream_close_async(GIOStream *stream, G_GNUC_UNUSED int io_priority,
+ GCancellable *cancellable, GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *res;
+ GIOStreamClass *class;
+ GError *error;
+
+ class = G_IO_STREAM_GET_CLASS(stream);
+
+ /* close is not blocked, just do it! */
+ error = NULL;
+ if (class->close_fn &&
+ !class->close_fn(stream, cancellable, &error)) {
+ g_simple_async_report_take_gerror_in_idle(G_OBJECT (stream),
+ callback, user_data,
+ error);
+ return;
+ }
+
+ res = g_simple_async_result_new(G_OBJECT (stream),
+ callback,
+ user_data,
+ gvir_stream_close_async);
+ g_simple_async_result_complete_in_idle(res);
+ g_object_unref (res);
+}
+
+
+static gboolean
+gvir_stream_close_finish(G_GNUC_UNUSED GIOStream *stream,
+ G_GNUC_UNUSED GAsyncResult *result,
+ G_GNUC_UNUSED GError **error)
+{
+ return TRUE;
+}
+
+
static void gvir_stream_get_property(GObject *object,
guint prop_id,
GValue *value,
@@ -107,6 +176,9 @@ static void gvir_stream_finalize(GObject *object)
DEBUG("Finalize GVirStream=%p", self);
+ if (self->priv->input_stream)
+ g_object_unref(self->priv->input_stream);
+
if (priv->handle) {
if (virStreamFinish(priv->handle) < 0)
g_critical("cannot finish stream");
@@ -120,12 +192,18 @@ static void gvir_stream_finalize(GObject *object)
static void gvir_stream_class_init(GVirStreamClass *klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ GIOStreamClass *stream_class = G_IO_STREAM_CLASS(klass);
object_class->finalize = gvir_stream_finalize;
object_class->get_property = gvir_stream_get_property;
object_class->set_property = gvir_stream_set_property;
+ stream_class->get_input_stream = gvir_stream_get_input_stream;
+ stream_class->close_fn = gvir_stream_close;
+ stream_class->close_async = gvir_stream_close_async;
+ stream_class->close_finish = gvir_stream_close_finish;
+
g_object_class_install_property(object_class,
PROP_HANDLE,
g_param_spec_boxed("handle",
@@ -170,6 +248,50 @@ GType gvir_stream_handle_get_type(void)
return handle_type;
}
+/**
+ * gvir_stream_receive:
+ * @stream: the stream
+ * @buffer: a buffer to read data into (which should be at least @size
+ * bytes long).
+ * @size: the number of bytes you want to read from the stream
+ * @cancellable: (allow-none): a %GCancellable or %NULL
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Receive data (up to @size bytes) from a stream.
+ * On error -1 is returned and @error is set accordingly.
+ *
+ * gvir_stream_receive() can return any number of bytes, up to
+ * @size. If more than @size bytes have been received, the additional
+ * data will be returned in future calls to gvir_stream_receive().
+ *
+ * If there is no data available, a %G_IO_ERROR_WOULD_BLOCK error will be
+ * returned.
+ *
+ * Returns: Number of bytes read, or 0 if the end of stream reached,
+ * or -1 on error.
+ */
+gssize gvir_stream_receive(GVirStream *self, gchar *buffer, gsize size,
+ GCancellable *cancellable, GError **error)
+{
+ int got;
+
+ g_return_val_if_fail(GVIR_IS_STREAM(self), -1);
+ g_return_val_if_fail(buffer != NULL, -1);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return -1;
+
+ got = virStreamRecv(self->priv->handle, buffer, size);
+
+ if (got == -2) { /* blocking */
+ g_set_error(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK, NULL);
+ } else if (got < 0) {
+ g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+ "Got virStreamRecv error in %s", G_STRFUNC);
+ }
+
+ return got;
+}
struct stream_sink_helper {
GVirStream *self;
@@ -197,7 +319,7 @@ stream_sink(virStreamPtr st G_GNUC_UNUSED,
* requested data sink. This is simply a convenient alternative
* to virStreamRecv, for apps that do blocking-I/o.
*/
-gint
+gssize
gvir_stream_receive_all(GVirStream *self, GVirStreamSinkFunc func, gpointer user_data, GError **err)
{
struct stream_sink_helper helper = {
diff --git a/libvirt-gobject/libvirt-gobject-stream.h b/libvirt-gobject/libvirt-gobject-stream.h
index 5181e24..35526db 100644
--- a/libvirt-gobject/libvirt-gobject-stream.h
+++ b/libvirt-gobject/libvirt-gobject-stream.h
@@ -28,6 +28,9 @@
#ifndef __LIBVIRT_GOBJECT_STREAM_H__
#define __LIBVIRT_GOBJECT_STREAM_H__
+#include <glib-object.h>
+#include <gio/gio.h>
+
G_BEGIN_DECLS
#define GVIR_TYPE_STREAM (gvir_stream_get_type ())
@@ -45,7 +48,7 @@ typedef struct _GVirStreamClass GVirStreamClass;
struct _GVirStream
{
- GObject parent;
+ GIOStream parent_instance;
GVirStreamPrivate *priv;
@@ -54,7 +57,7 @@ struct _GVirStream
struct _GVirStreamClass
{
- GObjectClass parent_class;
+ GIOStreamClass parent_class;
gpointer padding[20];
};
@@ -76,7 +79,8 @@ typedef gint (* GVirStreamSinkFunc) (GVirStream *stream,
GType gvir_stream_get_type(void);
GType gvir_stream_handle_get_type(void);
-gint gvir_stream_receive_all(GVirStream *stream, GVirStreamSinkFunc func, gpointer user_data, GError **err);
+gssize gvir_stream_receive_all(GVirStream *stream, GVirStreamSinkFunc func, gpointer user_data, GError **err);
+gssize gvir_stream_receive(GVirStream *stream, gchar *buffer, gsize size, GCancellable *cancellable, GError **error);
G_END_DECLS
--
1.7.6.2
13 years, 1 month