[libvirt] [PATCH V2 0/3] Add support for QEMU file descriptor sets

The following patch series adds initial support for QEMU file descriptor sets implementing support for creating the proper command line. Some devices are using the sets now. Regards, Stefan

Add function to find the next available bit in the bitmap and set it. Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> --- src/libvirt_private.syms | 1 + src/util/virbitmap.c | 34 ++++++++++++++++++++++++++++++++++ src/util/virbitmap.h | 3 +++ 3 files changed, 38 insertions(+) Index: libvirt/src/util/virbitmap.c =================================================================== --- libvirt.orig/src/util/virbitmap.c +++ libvirt/src/util/virbitmap.c @@ -642,3 +642,37 @@ virBitmapCountBits(virBitmapPtr bitmap) return ret; } + + +/** + * virBitmapSetNextBit: + * @bitmap: the bitmap + * @pos: the position after which to search for a clear bit and set it + * + * search the first clear bit after position @pos in bitmap @bitmap + * and set it. @pos can be -1 to search for the first set bit. + * Position starts at 0. + * + * returns the position of the set bit, or -1 if no bit could be set + */ +size_t +virBitmapSetNextBit(virBitmapPtr bitmap, ssize_t pos) +{ + if (pos < 0) + pos = -1; + + pos++; + + if (pos >= bitmap->max_bit) + return -1; + + while (pos < bitmap->max_bit) { + if (!virBitmapIsSet(bitmap, pos)) { + ignore_value(virBitmapSetBit(bitmap, pos)); + return pos; + } + pos++; + } + + return -1; +} Index: libvirt/src/util/virbitmap.h =================================================================== --- libvirt.orig/src/util/virbitmap.h +++ libvirt/src/util/virbitmap.h @@ -106,4 +106,7 @@ ssize_t virBitmapNextSetBit(virBitmapPtr size_t virBitmapCountBits(virBitmapPtr bitmap) ATTRIBUTE_NONNULL(1); +size_t virBitmapSetNextBit(virBitmapPtr bitmap, ssize_t pos) + ATTRIBUTE_NONNULL(1); + #endif Index: libvirt/src/libvirt_private.syms =================================================================== --- libvirt.orig/src/libvirt_private.syms +++ libvirt/src/libvirt_private.syms @@ -22,6 +22,7 @@ virBitmapNextSetBit; virBitmapParse; virBitmapSetAll; virBitmapSetBit; +virBitmapSetNextBit; virBitmapSize; virBitmapString; virBitmapToData;

On 02/01/2013 02:57 PM, Stefan Berger wrote:
Add function to find the next available bit in the bitmap and set it.
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
--- src/libvirt_private.syms | 1 + src/util/virbitmap.c | 34 ++++++++++++++++++++++++++++++++++ src/util/virbitmap.h | 3 +++ 3 files changed, 38 insertions(+)
Index: libvirt/src/util/virbitmap.c =================================================================== --- libvirt.orig/src/util/virbitmap.c +++ libvirt/src/util/virbitmap.c @@ -642,3 +642,37 @@ virBitmapCountBits(virBitmapPtr bitmap)
return ret; }
@@ -106,4 +106,7 @@ ssize_t virBitmapNextSetBit(virBitmapPtr size_t virBitmapCountBits(virBitmapPtr bitmap) ATTRIBUTE_NONNULL(1);
+size_t virBitmapSetNextBit(virBitmapPtr bitmap, ssize_t pos) + ATTRIBUTE_NONNULL(1);
With this patch, we'd have: virBitmapNextSetBit() - find without modifying virBitmapSetNextBit() - find and modify that feels a bit confusing (without reading the description, which one do you want?). Also, your implementation is slow (testing one bit at a time is slower than using tricks like ffsl). I'd rather see a virBitmapNextClearBit() with same semantics of virBitmapNextSetBit at iterating through the bitmap using ffsl() optimizations, then have callers explicitly set the returned bit, instead of providing a find-and-set function. And our testsuite should be updated to cover this new function. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

We had an easy way to iterate set bits, but not for iterating cleared bits. * src/util/virbitmap.h (virBitmapNextClearBit): New prototype. * src/util/virbitmap.c (virBitmapNextClearBit): Implement it. * src/libvirt_private.syms (bitmap.h): Export it. * tests/virbitmaptest.c (test4): Test it. --- src/libvirt_private.syms | 1 + src/util/virbitmap.c | 57 ++++++++++++++++++++++++++++++++++++++++++++---- src/util/virbitmap.h | 5 ++++- tests/virbitmaptest.c | 46 +++++++++++++++++++++++++++++--------- 4 files changed, 94 insertions(+), 15 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c589236..3483831 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -18,6 +18,7 @@ virBitmapIsAllSet; virBitmapNew; virBitmapNewCopy; virBitmapNewData; +virBitmapNextClearBit; virBitmapNextSetBit; virBitmapParse; virBitmapSetAll; diff --git a/src/util/virbitmap.c b/src/util/virbitmap.c index 2cfe03a..21509ac 100644 --- a/src/util/virbitmap.c +++ b/src/util/virbitmap.c @@ -1,7 +1,7 @@ /* * virbitmap.h: Simple bitmap operations * - * Copyright (C) 2010-2012 Red Hat, Inc. + * Copyright (C) 2010-2013 Red Hat, Inc. * Copyright (C) 2010 Novell, Inc. * * This library is free software; you can redistribute it and/or @@ -595,13 +595,14 @@ bool virBitmapIsAllSet(virBitmapPtr bitmap) * @bitmap: the bitmap * @pos: the position after which to search for a set bit * - * search the first set bit after position @pos in bitmap @bitmap. + * Search for the first set bit after position @pos in bitmap @bitmap. * @pos can be -1 to search for the first set bit. Position starts * at 0. * - * returns the position of the found bit, or -1 if no bit found. + * Returns the position of the found bit, or -1 if no bit found. */ -ssize_t virBitmapNextSetBit(virBitmapPtr bitmap, ssize_t pos) +ssize_t +virBitmapNextSetBit(virBitmapPtr bitmap, ssize_t pos) { size_t nl; size_t nb; @@ -630,6 +631,54 @@ ssize_t virBitmapNextSetBit(virBitmapPtr bitmap, ssize_t pos) return ffsl(bits) - 1 + nl * VIR_BITMAP_BITS_PER_UNIT; } +/** + * virBitmapNextClearBit: + * @bitmap: the bitmap + * @pos: the position after which to search for a clear bit + * + * Search for the first clear bit after position @pos in bitmap @bitmap. + * @pos can be -1 to search for the first set bit. Position starts + * at 0. + * + * Returns the position of the found bit, or -1 if no bit found. + */ +ssize_t +virBitmapNextClearBit(virBitmapPtr bitmap, ssize_t pos) +{ + size_t nl; + size_t nb; + unsigned long bits; + + if (pos < 0) + pos = -1; + + pos++; + + if (pos >= bitmap->max_bit) + return -1; + + nl = pos / VIR_BITMAP_BITS_PER_UNIT; + nb = pos % VIR_BITMAP_BITS_PER_UNIT; + + bits = ~bitmap->map[nl] & ~((1UL << nb) - 1); + + while (bits == 0 && ++nl < bitmap->map_len) { + bits = ~bitmap->map[nl]; + } + + if (nl == bitmap->map_len - 1) { + /* Ensure tail bits are ignored. */ + int tail = bitmap->max_bit % VIR_BITMAP_BITS_PER_UNIT; + + if (tail) + bits &= -1UL >> (VIR_BITMAP_BITS_PER_UNIT - tail); + } + if (bits == 0) + return -1; + + return ffsl(bits) - 1 + nl * VIR_BITMAP_BITS_PER_UNIT; +} + /* Return the number of bits currently set in the map. */ size_t virBitmapCountBits(virBitmapPtr bitmap) diff --git a/src/util/virbitmap.h b/src/util/virbitmap.h index 0f68b79..044c7a6 100644 --- a/src/util/virbitmap.h +++ b/src/util/virbitmap.h @@ -1,7 +1,7 @@ /* * virbitmap.h: Simple bitmap operations * - * Copyright (C) 2012 Red Hat, Inc. + * Copyright (C) 2012-2013 Red Hat, Inc. * Copyright (C) 2010 Novell, Inc. * * This library is free software; you can redistribute it and/or @@ -103,6 +103,9 @@ bool virBitmapIsAllSet(virBitmapPtr bitmap) ssize_t virBitmapNextSetBit(virBitmapPtr bitmap, ssize_t pos) ATTRIBUTE_NONNULL(1); +ssize_t virBitmapNextClearBit(virBitmapPtr bitmap, ssize_t pos) + ATTRIBUTE_NONNULL(1); + size_t virBitmapCountBits(virBitmapPtr bitmap) ATTRIBUTE_NONNULL(1); diff --git a/tests/virbitmaptest.c b/tests/virbitmaptest.c index 66ffd1b..95d010a 100644 --- a/tests/virbitmaptest.c +++ b/tests/virbitmaptest.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2013 Red Hat, Inc. * Copyright (C) 2012 Fujitsu. * * This library is free software; you can redistribute it and/or @@ -160,7 +161,7 @@ error: return ret; } -/* test for virBitmapNextSetBit */ +/* test for virBitmapNextSetBit, virBitmapNextClearBit */ static int test4(const void *data ATTRIBUTE_UNUSED) { const char *bitsString = "0, 2-4, 6-10, 12, 14-18, 20, 22, 25"; @@ -169,17 +170,30 @@ static int test4(const void *data ATTRIBUTE_UNUSED) 0, 2, 3, 4, 6, 7, 8, 9, 10, 12, 14, 15, 16, 17, 18, 20, 22, 25 }; - int npos = 18; + int bitsPosInv[] = { + 1, 5, 11, 13, 19, 21, 23, 24, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39 + }; virBitmapPtr bitmap = NULL; int i, j; + if (ARRAY_CARDINALITY(bitsPos) + ARRAY_CARDINALITY(bitsPosInv) != size) + goto error; + /* 1. zero set */ bitmap = virBitmapNew(size); if (!bitmap) goto error; - if (virBitmapNextSetBit(bitmap, -1) >= 0) + if (virBitmapNextSetBit(bitmap, -1) != -1) + goto error; + + for (i = 0; i < size; i++) { + if (virBitmapNextClearBit(bitmap, i - 1) != i) + goto error; + } + if (virBitmapNextClearBit(bitmap, i) != -1) goto error; virBitmapFree(bitmap); @@ -195,27 +209,39 @@ static int test4(const void *data ATTRIBUTE_UNUSED) j = 0; i = -1; - while (j < npos) { + while (j < ARRAY_CARDINALITY(bitsPos)) { i = virBitmapNextSetBit(bitmap, i); if (i != bitsPos[j++]) goto error; } - if (virBitmapNextSetBit(bitmap, i) > 0) + if (virBitmapNextSetBit(bitmap, i) != -1) + goto error; + + j = 0; + i = -1; + + while (j < ARRAY_CARDINALITY(bitsPosInv)) { + i = virBitmapNextClearBit(bitmap, i); + if (i != bitsPosInv[j++]) + goto error; + } + + if (virBitmapNextClearBit(bitmap, i) != -1) goto error; /* 3. full set */ - i = -1; virBitmapSetAll(bitmap); - for (j = 0; j < size; j++) { - i = virBitmapNextSetBit(bitmap, i); - if (i != j) + for (i = 0; i < size; i++) { + if (virBitmapNextSetBit(bitmap, i - 1) != i) goto error; } + if (virBitmapNextSetBit(bitmap, i) != -1) + goto error; - if (virBitmapNextSetBit(bitmap, i) > 0) + if (virBitmapNextClearBit(bitmap, -1) != -1) goto error; virBitmapFree(bitmap); -- 1.8.1

On 02/01/2013 09:16 PM, Eric Blake wrote:
We had an easy way to iterate set bits, but not for iterating cleared bits.
* src/util/virbitmap.h (virBitmapNextClearBit): New prototype. * src/util/virbitmap.c (virBitmapNextClearBit): Implement it. * src/libvirt_private.syms (bitmap.h): Export it. * tests/virbitmaptest.c (test4): Test it. ---
I didn't test it yours so far... Here's at least the part of the test I had fabricated (your bitmap extension seems better): Index: libvirt/tests/virbitmaptest.c =================================================================== --- libvirt.orig/tests/virbitmaptest.c +++ libvirt/tests/virbitmaptest.c @@ -390,6 +390,74 @@ error: return -1; } +/* test for virBitmapNextClearBit */ +static int test8(const void *data ATTRIBUTE_UNUSED) +{ + const char *bitsString = "0, 2-4, 6-10, 12, 14-18, 20, 22, 25, 28-33, 35, " + "41-42, 45-46"; + int size = 48; + int noBitsPos[] = { + 1, 5, 11, 13, 19, 21, 23, 24, + 26, 27, 34, 36, 37, 38, 39, 40, + 43, 44, 47 + }; + int npos = 19; + virBitmapPtr bitmap = NULL; + int i, j; + + /* 1. zero set */ + + bitmap = virBitmapNew(size); + if (!bitmap) + goto error; + + if (virBitmapNextClearBit(bitmap, -1) != 0) + goto error; + + virBitmapFree(bitmap); + bitmap = NULL; + + /* 2. partial set */ + + if (virBitmapParse(bitsString, 0, &bitmap, size) < 0) + goto error; + if (!bitmap) + goto error; + + j = 0; + i = -1; + + while (j < npos) { + i = virBitmapNextClearBit(bitmap, i); + if (i != noBitsPos[j++]) + goto error; + } + + if (virBitmapNextSetBit(bitmap, i) > 0) + goto error; + + /* 3. full set */ + + i = -1; + virBitmapSetAll(bitmap); + + for (j = 0; j < size; j++) { + i = virBitmapNextSetBit(bitmap, i); + if (i != j) + goto error; + } + + if (virBitmapNextClearBit(bitmap, i) > 0) + goto error; + + virBitmapFree(bitmap); + return 0; + +error: + virBitmapFree(bitmap); + return -1; +} + static int mymain(void) { @@ -409,6 +477,8 @@ mymain(void) ret = -1; if (virtTestRun("test7", 1, test7, NULL) < 0) ret = -1; + if (virtTestRun("test8", 1, test8, NULL) < 0) + ret = -1; return ret; Maybe we can merge the two for a joint effort :-) -- leaving test 4 untouched. Patch 3 of the series has calls missing regarding clearing of the bitmap upon failure and VM shutdown. I added that after sending. Regards, Stefan

On 02/01/2013 09:41 PM, Stefan Berger wrote:
On 02/01/2013 09:16 PM, Eric Blake wrote:
We had an easy way to iterate set bits, but not for iterating cleared bits.
* src/util/virbitmap.h (virBitmapNextClearBit): New prototype. * src/util/virbitmap.c (virBitmapNextClearBit): Implement it. * src/libvirt_private.syms (bitmap.h): Export it. * tests/virbitmaptest.c (test4): Test it. ---
I didn't test it yours so far... Here's at least the part of the test I had fabricated (your bitmap extension seems better):
Index: libvirt/tests/virbitmaptest.c =================================================================== --- libvirt.orig/tests/virbitmaptest.c +++ libvirt/tests/virbitmaptest.c @@ -390,6 +390,74 @@ error: return -1; }
+/* test for virBitmapNextClearBit */ +static int test8(const void *data ATTRIBUTE_UNUSED) +{ + const char *bitsString = "0, 2-4, 6-10, 12, 14-18, 20, 22, 25, 28-33, 35, " + "41-42, 45-46"; + int size = 48; + int noBitsPos[] = { + 1, 5, 11, 13, 19, 21, 23, 24, + 26, 27, 34, 36, 37, 38, 39, 40, + 43, 44, 47 + }; + int npos = 19;
I definitely prefer the ARRAY_CARDINALITY() instead of npos in my version of the test. Beyond that, it looks like your test copied and pasted so that the set vs. clear tests were independent, while mine interleaved the two to traverse over the same set. Coverage wise, either one of our tests gets just as much coverage of the added code; at least, I didn't see anything added by your test8() that wasn't present in my revised test4().
Maybe we can merge the two for a joint effort :-) -- leaving test 4 untouched.
Anyone else have an opinion?
Patch 3 of the series has calls missing regarding clearing of the bitmap upon failure and VM shutdown. I added that after sending.
Regards, Stefan
-- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 02/04/2013 03:53 PM, Eric Blake wrote:
On 02/01/2013 09:41 PM, Stefan Berger wrote:
On 02/01/2013 09:16 PM, Eric Blake wrote: I definitely prefer the ARRAY_CARDINALITY() instead of npos in my version of the test. Beyond that, it looks like your test copied and pasted so that the set vs. clear tests were independent, while mine interleaved the two to traverse over the same set. Coverage wise, either one of our tests gets just as much coverage of the added code; at least, I didn't see anything added by your test8() that wasn't present in my revised test4(). Maybe we can merge the two for a joint effort :-) -- leaving test 4 untouched. Anyone else have an opinion?
ACK.

On 02/04/2013 04:31 PM, Stefan Berger wrote:
On 02/04/2013 03:53 PM, Eric Blake wrote:
On 02/01/2013 09:41 PM, Stefan Berger wrote:
On 02/01/2013 09:16 PM, Eric Blake wrote: I definitely prefer the ARRAY_CARDINALITY() instead of npos in my version of the test. Beyond that, it looks like your test copied and pasted so that the set vs. clear tests were independent, while mine interleaved the two to traverse over the same set. Coverage wise, either one of our tests gets just as much coverage of the added code; at least, I didn't see anything added by your test8() that wasn't present in my revised test4(). Maybe we can merge the two for a joint effort :-) -- leaving test 4 untouched. Anyone else have an opinion?
ACK.
I've gone ahead and pushed my version, then. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Add a file descriptor set to the QEMU private domain structure. Stefan Berger <stefanb@linux.vnet.ibm.com> --- src/qemu/qemu_domain.c | 9 ++++++++- src/qemu/qemu_domain.h | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) Index: libvirt/src/qemu/qemu_domain.c =================================================================== --- libvirt.orig/src/qemu/qemu_domain.c +++ libvirt/src/qemu/qemu_domain.c @@ -37,6 +37,7 @@ #include "domain_event.h" #include "virtime.h" #include "virstoragefile.h" +#include "virbitmap.h" #include <sys/time.h> #include <fcntl.h> @@ -215,13 +216,18 @@ static void *qemuDomainObjPrivateAlloc(v if (qemuDomainObjInitJob(priv) < 0) goto error; - if (!(priv->devs = virChrdevAlloc())) + if (!(priv->fdset = virBitmapNew(4096))) goto error; + if (!(priv->devs = virChrdevAlloc())) + goto error_free_bitmap; + priv->migMaxBandwidth = QEMU_DOMAIN_MIG_BANDWIDTH_MAX; return priv; +error_free_bitmap: + virBitmapFree(priv->fdset); error: VIR_FREE(priv); return NULL; @@ -252,6 +258,7 @@ static void qemuDomainObjPrivateFree(voi qemuAgentClose(priv->agent); } VIR_FREE(priv->cleanupCallbacks); + virBitmapFree(priv->fdset); VIR_FREE(priv); } Index: libvirt/src/qemu/qemu_domain.h =================================================================== --- libvirt.orig/src/qemu/qemu_domain.h +++ libvirt/src/qemu/qemu_domain.h @@ -32,6 +32,7 @@ # include "qemu_conf.h" # include "qemu_capabilities.h" # include "virchrdev.h" +# include "virbitmap.h" # define QEMU_EXPECTED_VIRT_TYPES \ ((1 << VIR_DOMAIN_VIRT_QEMU) | \ @@ -160,6 +161,8 @@ struct _qemuDomainObjPrivate { qemuDomainCleanupCallback *cleanupCallbacks; size_t ncleanupCallbacks; size_t ncleanupCallbacks_max; + + virBitmapPtr fdset; }; struct qemuDomainWatchdogEvent

On 02/01/2013 02:57 PM, Stefan Berger wrote:
Add a file descriptor set to the QEMU private domain structure.
Stefan Berger <stefanb@linux.vnet.ibm.com>
--- src/qemu/qemu_domain.c | 9 ++++++++- src/qemu/qemu_domain.h | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-)
I think I mentioned this in another thread, and know I mentioned in IRC, but just for archival purposes: This version is overkill; we don't need a bitmap, but can instead just track a per-vm counter that we increment each time we allocate a new fdset (which won't be often enough to worry about overflow). On libvirtd reload, we can recompute the maximum in-use fdset, and set the counter one greater. Qemu won't care if hotunplugging leaves lower-valued fdsets unused, nor if we reuse an fdset across a libvirtd restart that was previously abandoned prior to the libvirtd restart. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Add support for file descriptor sets by converting some of the command line parameters to use /dev/fdset/%d if -add-fd is found to be supported by QEMU. For those devices libvirt now open()s the device to obtain the file descriptor and 'transfers' the fd using virCommand. For the following fragments of domain XML <disk type='file' device='disk'> <driver name='qemu' type='raw'/> <source file='/var/lib/libvirt/images/fc14-x86_64.img'/> <target dev='hda' bus='ide'/> <address type='drive' controller='0' bus='0' target='0' unit='0'/> </disk> <serial type='dev'> <source path='/dev/ttyS0'/> <target port='0'/> </serial> <serial type='pipe'> <source path='/tmp/testpipe'/> <target port='1'/> </serial> libvirt now creates the following parts for the QEMU command line: old: -drive file=/var/lib/libvirt/images/fc14-x86_64.img,if=none,id=drive-ide0-0-0,format=raw new: -add-fd set=1,fd=23,opaque=RDONLY:/var/lib/libvirt/images/fc14-x86_64.img -add-fd set=1,fd=24,opaque=RDWR:/var/lib/libvirt/images/fc14-x86_64.img -drive file=/dev/fdset/1,if=none,id=drive-ide0-0-0,format=raw old: -chardev tty,id=charserial0,path=/dev/ttyS0 new: -add-fd set=1,fd=30,opaque=/dev/ttyS0 -chardev tty,id=charserial0,path=/dev/fdset/1 old: -chardev pipe,id=charserial1,path=/tmp/testpipe new: -add-fd set=2,fd=32,opaque=/tmp/testpipe -chardev pipe,id=charserial1,path=/dev/fdset/2 Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> --- v1->v2: - Addressed many of Eric's comment; many changes, though - virBitmap holds used file descriptor sets; it's attached to QEMU private domain structure - persisting and parsing the fdset in the virDomainDeviceInfo XML - rebuilding the fdset bitmap upon libvirt start and after parsing the virDomainDeviceInfo XML --- src/conf/capabilities.h | 5 src/conf/domain_conf.c | 20 +++ src/conf/domain_conf.h | 1 src/lxc/lxc_domain.c | 3 src/qemu/qemu_command.c | 239 ++++++++++++++++++++++++++++++++++++++--------- src/qemu/qemu_command.h | 15 ++ src/qemu/qemu_domain.c | 26 ++++- src/qemu/qemu_driver.c | 5 src/qemu/qemu_driver.h | 4 src/qemu/qemu_hotplug.c | 9 + src/qemu/qemu_process.c | 3 tests/qemuxml2argvtest.c | 4 tests/qemuxmlnstest.c | 9 + 13 files changed, 281 insertions(+), 62 deletions(-) Index: libvirt/src/qemu/qemu_command.c =================================================================== --- libvirt.orig/src/qemu/qemu_command.c +++ libvirt/src/qemu/qemu_command.c @@ -46,6 +46,7 @@ #include "base64.h" #include "device_conf.h" #include "virstoragefile.h" +#include "qemu_driver.h" #include <sys/stat.h> #include <fcntl.h> @@ -133,6 +134,70 @@ VIR_ENUM_IMPL(qemuDomainFSDriver, VIR_DO /** + * qemuCommandPrintFDSet: + * @fdset: the number of the file descriptor set to which @fd belongs + * @fd: fd that will be assigned to QEMU + * @open_flags: the flags used for opening the fd; of interest are only + * O_RDONLY, O_WRONLY, O_RDWR + * @name: optional name of the file + * + * Write the parameters for the QEMU -add-fd command line option + * for the given file descriptor and return the string. + * This function for example returns "set=10,fd=20,opaque=RDWR:/foo/bar". + */ +static char * +qemuCommandPrintFDSet(int fdset, int fd, int open_flags, const char *name) +{ + const char *mode = ""; + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (name) { + switch ((open_flags & O_ACCMODE)) { + case O_RDONLY: + mode = "RDONLY:"; + break; + case O_WRONLY: + mode = "WRONLY:"; + break; + case O_RDWR: + mode = "RDWR:"; + break; + } + } + + virBufferAsprintf(&buf, "set=%d,fd=%d%s%s", fdset, fd, + (name) ? ",opaque=" : "", + mode); + if (name) + virBufferEscape(&buf, ',', ",", "%s", name); + + if (virBufferError(&buf)) { + virReportOOMError(); + virBufferFreeAndReset(&buf); + return NULL; + } + + return virBufferContentAndReset(&buf); +} + +/** + * qemuCommandPrintDevSet: + * @buf: buffer to write the file descriptor set string + * @fdset: the file descriptor set + * + * Get the parameters for the QEMU path= parameter where a file + * descriptor is accessed via a file descriptor set, for example + * /dev/fdset/10. The file descriptor must previously have been + * 'transferred' in a virCommandTransfer() call. + */ +static void +qemuCommandPrintDevSet(virBufferPtr buf, int fdset) +{ + virBufferAsprintf(buf, "/dev/fdset/%d", fdset); +} + + +/** * qemuPhysIfaceConnect: * @def: the definition of the VM (needed by 802.1Qbh and audit) * @driver: pointer to the driver instance @@ -2223,11 +2288,72 @@ no_memory: goto cleanup; } +static void +qemuCreatePathForFilePath(virQEMUDriverPtr driver, virBufferPtr buf, + const char *prefix, const char *path, + const int *open_flags, + virCommandPtr cmd, virBitmapPtr fdset, + unsigned int *fdsetnum, + qemuCapsPtr caps) +{ + char *fdsetstr = NULL; + int i; + + virBufferAdd(buf, prefix, -1); + /* + * cmd == NULL hints at hotplugging; use old way of doing things + * fdset == NULL hints at a call path where we should not open files + * In this case we fall back to the old command line + * (at least for now) + */ + if (!cmd || !fdset || !qemuCapsGet(caps, QEMU_CAPS_ADD_FD)) { + virBufferEscape(buf, ',', ",", "%s", path); + } else { + ssize_t nextfdset; + + nextfdset = virBitmapSetNextBit(fdset, 0); + if (nextfdset < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("File descriptor set is full")); + goto error; + } + *fdsetnum = nextfdset; + + qemuCommandPrintDevSet(buf, *fdsetnum); + + i = 0; + while (open_flags[i] != -1) { + int fd = qemuOpenFile(driver, path, open_flags[i], NULL, NULL); + if (fd < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not open %s"), path); + goto error; + } + virCommandTransferFD(cmd, fd); + + fdsetstr = qemuCommandPrintFDSet(*fdsetnum, fd, open_flags[i], + path); + if (!fdsetstr) + goto error; + virCommandAddArgList(cmd, "-add-fd", fdsetstr, NULL); + VIR_FREE(fdsetstr); + + i++; + } + } + +error: + return; +} + char * qemuBuildDriveStr(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainDiskDefPtr disk, bool bootable, - qemuCapsPtr caps) + qemuCapsPtr caps, + virCommandPtr cmd, + virQEMUDriverPtr driver, + virBitmapPtr fdset) { virBuffer opt = VIR_BUFFER_INITIALIZER; const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus); @@ -2389,7 +2515,12 @@ qemuBuildDriveStr(virConnectPtr conn ATT "block type disk")); goto error; } - virBufferEscape(&opt, ',', ",", "file=%s,", disk->src); + qemuCreatePathForFilePath(driver, &opt, + "file=", disk->src, + (int[]){O_RDONLY, O_RDWR, -1}, + cmd, fdset, &disk->info.fdset, + caps); + virBufferAddChar(&opt, ','); } } if (qemuCapsGet(caps, QEMU_CAPS_DEVICE)) @@ -2886,7 +3017,10 @@ error: char *qemuBuildFSStr(virDomainFSDefPtr fs, - qemuCapsPtr caps ATTRIBUTE_UNUSED) + qemuCapsPtr caps ATTRIBUTE_UNUSED, + virCommandPtr cmd, + virQEMUDriverPtr qemu_driver, + virBitmapPtr fdset) { virBuffer opt = VIR_BUFFER_INITIALIZER; const char *driver = qemuDomainFSDriverTypeToString(fs->fsdriver); @@ -2935,7 +3069,9 @@ char *qemuBuildFSStr(virDomainFSDefPtr f } virBufferAsprintf(&opt, ",id=%s%s", QEMU_FSDEV_HOST_PREFIX, fs->info.alias); - virBufferAsprintf(&opt, ",path=%s", fs->src); + qemuCreatePathForFilePath(qemu_driver, &opt, + ",path=", fs->src, (int[]){O_RDWR, -1}, + cmd, fdset, &fs->info.fdset, caps); if (fs->readonly) { if (qemuCapsGet(caps, QEMU_CAPS_FSDEV_READONLY)) { @@ -3884,12 +4020,13 @@ qemuBuildUSBHostdevUsbDevStr(virDomainHo } - /* This function outputs a -chardev command line option which describes only the * host side of the character device */ static char * qemuBuildChrChardevStr(virDomainChrSourceDefPtr dev, const char *alias, - qemuCapsPtr caps) + qemuCapsPtr caps, virCommandPtr cmd, + virQEMUDriverPtr driver, + virBitmapPtr fdset, unsigned int *fdsetnum) { virBuffer buf = VIR_BUFFER_INITIALIZER; bool telnet; @@ -3908,19 +4045,29 @@ qemuBuildChrChardevStr(virDomainChrSourc break; case VIR_DOMAIN_CHR_TYPE_DEV: - virBufferAsprintf(&buf, "%s,id=char%s,path=%s", + virBufferAsprintf(&buf, "%s,id=char%s", STRPREFIX(alias, "parallel") ? "parport" : "tty", - alias, dev->data.file.path); + alias); + qemuCreatePathForFilePath(driver, &buf, + ",path=", dev->data.file.path, + (int[]){O_RDWR, -1}, cmd, fdset, + fdsetnum, caps); break; case VIR_DOMAIN_CHR_TYPE_FILE: - virBufferAsprintf(&buf, "file,id=char%s,path=%s", alias, - dev->data.file.path); + virBufferAsprintf(&buf, "file,id=char%s", alias); + qemuCreatePathForFilePath(driver, &buf, + ",path=", dev->data.file.path, + (int[]){O_RDWR, -1}, cmd, fdset, fdsetnum, + caps); break; case VIR_DOMAIN_CHR_TYPE_PIPE: - virBufferAsprintf(&buf, "pipe,id=char%s,path=%s", alias, - dev->data.file.path); + virBufferAsprintf(&buf, "pipe,id=char%s",alias); + qemuCreatePathForFilePath(driver, &buf, + ",path=", dev->data.file.path, + (int[]){O_RDWR, -1}, cmd, fdset, fdsetnum, + caps); break; case VIR_DOMAIN_CHR_TYPE_STDIO: @@ -5056,7 +5203,8 @@ qemuBuildCommandLine(virConnectPtr conn, const char *migrateFrom, int migrateFd, virDomainSnapshotObjPtr snapshot, - enum virNetDevVPortProfileOp vmop) + enum virNetDevVPortProfileOp vmop, + virBitmapPtr fdset) { int i, j; int disableKQEMU = 0; @@ -5350,11 +5498,11 @@ qemuBuildCommandLine(virConnectPtr conn, /* Use -chardev if it's available */ if (qemuCapsGet(caps, QEMU_CAPS_CHARDEV)) { - virCommandAddArg(cmd, "-chardev"); if (!(chrdev = qemuBuildChrChardevStr(monitor_chr, "monitor", - caps))) + caps, cmd, driver, fdset, + NULL))) goto error; - virCommandAddArg(cmd, chrdev); + virCommandAddArgList(cmd, "-chardev", chrdev, NULL); VIR_FREE(chrdev); virCommandAddArg(cmd, "-mon"); @@ -5797,8 +5945,6 @@ qemuBuildCommandLine(virConnectPtr conn, break; } - virCommandAddArg(cmd, "-drive"); - /* Unfortunately it is not possible to use -device for floppies, or Xen paravirt devices. Fortunately, those don't need @@ -5814,12 +5960,12 @@ qemuBuildCommandLine(virConnectPtr conn, } optstr = qemuBuildDriveStr(conn, disk, emitBootindex ? false : !!bootindex, - caps); + caps, cmd, driver, fdset); if (deviceFlagMasked) qemuCapsSet(caps, QEMU_CAPS_DEVICE); if (!optstr) goto error; - virCommandAddArg(cmd, optstr); + virCommandAddArgList(cmd, "-drive", optstr, NULL); VIR_FREE(optstr); if (!emitBootindex) @@ -5995,7 +6141,7 @@ qemuBuildCommandLine(virConnectPtr conn, virDomainFSDefPtr fs = def->fss[i]; virCommandAddArg(cmd, "-fsdev"); - if (!(optstr = qemuBuildFSStr(fs, caps))) + if (!(optstr = qemuBuildFSStr(fs, caps, cmd, driver, fdset))) goto error; virCommandAddArg(cmd, optstr); VIR_FREE(optstr); @@ -6275,14 +6421,14 @@ qemuBuildCommandLine(virConnectPtr conn, goto error; } - virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(&smartcard->data.passthru, smartcard->info.alias, - caps))) { + caps, cmd, driver, fdset, + &smartcard->info.fdset))) { virBufferFreeAndReset(&opt); goto error; } - virCommandAddArg(cmd, devstr); + virCommandAddArgList(cmd, "-chardev", devstr, NULL); VIR_FREE(devstr); virBufferAsprintf(&opt, "ccid-card-passthru,chardev=char%s", @@ -6313,12 +6459,13 @@ qemuBuildCommandLine(virConnectPtr conn, /* Use -chardev with -device if they are available */ if (qemuCapsGet(caps, QEMU_CAPS_CHARDEV) && qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { - virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(&serial->source, serial->info.alias, - caps))) + caps, cmd, driver, + fdset, + &serial->info.fdset))) goto error; - virCommandAddArg(cmd, devstr); + virCommandAddArgList(cmd, "-chardev", devstr, NULL); VIR_FREE(devstr); virCommandAddArg(cmd, "-device"); @@ -6350,12 +6497,13 @@ qemuBuildCommandLine(virConnectPtr conn, /* Use -chardev with -device if they are available */ if (qemuCapsGet(caps, QEMU_CAPS_CHARDEV) && qemuCapsGet(caps, QEMU_CAPS_DEVICE)) { - virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(¶llel->source, parallel->info.alias, - caps))) + caps, cmd, driver, + fdset, + ¶llel->info.fdset))) goto error; - virCommandAddArg(cmd, devstr); + virCommandAddArgList(cmd, "-chardev", devstr, NULL); VIR_FREE(devstr); virCommandAddArg(cmd, "-device"); @@ -6387,12 +6535,12 @@ qemuBuildCommandLine(virConnectPtr conn, goto error; } - virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(&channel->source, channel->info.alias, - caps))) + caps, cmd, driver, fdset, + &channel->info.fdset))) goto error; - virCommandAddArg(cmd, devstr); + virCommandAddArgList(cmd, "-chardev", devstr, NULL); VIR_FREE(devstr); addr = virSocketAddrFormat(channel->target.addr); @@ -6422,12 +6570,13 @@ qemuBuildCommandLine(virConnectPtr conn, * the newer -chardev interface. */ ; } else { - virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(&channel->source, channel->info.alias, - caps))) + caps, cmd, driver, + fdset, + &channel->info.fdset))) goto error; - virCommandAddArg(cmd, devstr); + virCommandAddArgList(cmd, "-chardev", devstr, NULL); VIR_FREE(devstr); } @@ -6460,12 +6609,12 @@ qemuBuildCommandLine(virConnectPtr conn, goto error; } - virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(&console->source, console->info.alias, - caps))) + caps, cmd, driver, fdset, + &console->info.fdset))) goto error; - virCommandAddArg(cmd, devstr); + virCommandAddArgList(cmd, "-chardev", devstr, NULL); VIR_FREE(devstr); virCommandAddArg(cmd, "-device"); @@ -6482,12 +6631,12 @@ qemuBuildCommandLine(virConnectPtr conn, goto error; } - virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(&console->source, console->info.alias, - caps))) + caps, cmd, driver, fdset, + &console->info.fdset))) goto error; - virCommandAddArg(cmd, devstr); + virCommandAddArgList(cmd, "-chardev", devstr, NULL); VIR_FREE(devstr); virCommandAddArg(cmd, "-device"); @@ -6824,14 +6973,14 @@ qemuBuildCommandLine(virConnectPtr conn, virDomainRedirdevDefPtr redirdev = def->redirdevs[i]; char *devstr; - virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(&redirdev->source.chr, redirdev->info.alias, - caps))) { + caps, cmd, driver, fdset, + &redirdev->info.fdset))) { goto error; } - virCommandAddArg(cmd, devstr); + virCommandAddArgList(cmd, "-chardev", devstr, NULL); VIR_FREE(devstr); if (!qemuCapsGet(caps, QEMU_CAPS_DEVICE)) Index: libvirt/src/qemu/qemu_command.h =================================================================== --- libvirt.orig/src/qemu/qemu_command.h +++ libvirt/src/qemu/qemu_command.h @@ -30,6 +30,7 @@ # include "qemu_conf.h" # include "qemu_domain.h" # include "qemu_capabilities.h" +# include "virbitmap.h" /* Config type for XML import/export conversions */ # define QEMU_CONFIG_FORMAT_ARGV "qemu-argv" @@ -58,7 +59,8 @@ virCommandPtr qemuBuildCommandLine(virCo const char *migrateFrom, int migrateFd, virDomainSnapshotObjPtr current_snapshot, - enum virNetDevVPortProfileOp vmop) + enum virNetDevVPortProfileOp vmop, + virBitmapPtr fdset) ATTRIBUTE_NONNULL(1); /* Generate string for arch-specific '-device' parameter */ @@ -95,9 +97,16 @@ char *qemuDeviceDriveHostAlias(virDomain char *qemuBuildDriveStr(virConnectPtr conn, virDomainDiskDefPtr disk, bool bootable, - qemuCapsPtr caps); + qemuCapsPtr caps, + virCommandPtr cmd, + virQEMUDriverPtr driver, + virBitmapPtr fdset); + char *qemuBuildFSStr(virDomainFSDefPtr fs, - qemuCapsPtr caps); + qemuCapsPtr caps, + virCommandPtr cmd, + virQEMUDriverPtr driver, + virBitmapPtr fdset); /* Current, best practice */ char * qemuBuildDriveDevStr(virDomainDefPtr def, Index: libvirt/src/qemu/qemu_driver.c =================================================================== --- libvirt.orig/src/qemu/qemu_driver.c +++ libvirt/src/qemu/qemu_driver.c @@ -2679,7 +2679,7 @@ qemuCompressProgramName(int compress) /* Internal function to properly create or open existing files, with * ownership affected by qemu driver setup. */ -static int +int qemuOpenFile(virQEMUDriverPtr driver, const char *path, int oflags, bool *needUnlink, bool *bypassSecurityDriver) { @@ -5437,7 +5437,8 @@ static char *qemuDomainXMLToNative(virCo if (!(cmd = qemuBuildCommandLine(conn, driver, def, &monConfig, monitor_json, caps, - NULL, -1, NULL, VIR_NETDEV_VPORT_PROFILE_OP_NO_OP))) + NULL, -1, NULL, VIR_NETDEV_VPORT_PROFILE_OP_NO_OP, + NULL))) goto cleanup; ret = virCommandToString(cmd); Index: libvirt/src/qemu/qemu_hotplug.c =================================================================== --- libvirt.orig/src/qemu/qemu_hotplug.c +++ libvirt/src/qemu/qemu_hotplug.c @@ -262,7 +262,8 @@ int qemuDomainAttachPciDiskDevice(virCon if (qemuAssignDeviceDiskAlias(vm->def, disk, priv->caps) < 0) goto error; - if (!(drivestr = qemuBuildDriveStr(conn, disk, false, priv->caps))) + if (!(drivestr = qemuBuildDriveStr(conn, disk, false, priv->caps, + NULL, driver, priv->fdset))) goto error; if (!(devstr = qemuBuildDriveDevStr(NULL, disk, 0, priv->caps))) @@ -503,7 +504,8 @@ int qemuDomainAttachSCSIDisk(virConnectP goto error; } - if (!(drivestr = qemuBuildDriveStr(conn, disk, false, priv->caps))) + if (!(drivestr = qemuBuildDriveStr(conn, disk, false, priv->caps, NULL, + driver, priv->fdset))) goto error; for (i = 0 ; i <= disk->info.addr.drive.controller ; i++) { @@ -622,7 +624,8 @@ int qemuDomainAttachUsbMassstorageDevice if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (qemuAssignDeviceDiskAlias(vm->def, disk, priv->caps) < 0) goto error; - if (!(drivestr = qemuBuildDriveStr(conn, disk, false, priv->caps))) + if (!(drivestr = qemuBuildDriveStr(conn, disk, false, priv->caps, + NULL, driver, priv->fdset))) goto error; if (!(devstr = qemuBuildDriveDevStr(NULL, disk, 0, priv->caps))) goto error; Index: libvirt/src/qemu/qemu_process.c =================================================================== --- libvirt.orig/src/qemu/qemu_process.c +++ libvirt/src/qemu/qemu_process.c @@ -3779,7 +3779,8 @@ int qemuProcessStart(virConnectPtr conn, VIR_DEBUG("Building emulator command line"); if (!(cmd = qemuBuildCommandLine(conn, driver, vm->def, priv->monConfig, priv->monJSON != 0, priv->caps, - migrateFrom, stdin_fd, snapshot, vmop))) + migrateFrom, stdin_fd, snapshot, vmop, + priv->fdset))) goto cleanup; /* now that we know it is about to start call the hook if present */ Index: libvirt/tests/qemuxml2argvtest.c =================================================================== --- libvirt.orig/tests/qemuxml2argvtest.c +++ libvirt/tests/qemuxml2argvtest.c @@ -18,6 +18,7 @@ # include "qemu/qemu_domain.h" # include "datatypes.h" # include "cpu/cpu_map.h" +# include "virbitmap.h" # include "testutilsqemu.h" @@ -151,7 +152,8 @@ static int testCompareXMLToArgvFiles(con if (!(cmd = qemuBuildCommandLine(conn, &driver, vmdef, &monitor_chr, (flags & FLAG_JSON), extraFlags, migrateFrom, migrateFd, NULL, - VIR_NETDEV_VPORT_PROFILE_OP_NO_OP))) { + VIR_NETDEV_VPORT_PROFILE_OP_NO_OP, + NULL))) { if (flags & FLAG_EXPECT_FAILURE) { ret = 0; virResetLastError(); Index: libvirt/tests/qemuxmlnstest.c =================================================================== --- libvirt.orig/tests/qemuxmlnstest.c +++ libvirt/tests/qemuxmlnstest.c @@ -17,6 +17,7 @@ # include "qemu/qemu_domain.h" # include "datatypes.h" # include "cpu/cpu_map.h" +# include "virbitmap.h" # include "testutilsqemu.h" @@ -41,10 +42,14 @@ static int testCompareXMLToArgvFiles(con char *log = NULL; char *emulator = NULL; virCommandPtr cmd = NULL; + virBitmapPtr fdset; if (!(conn = virGetConnect())) goto fail; + if (!(fdset = virBitmapNew(4096))) + goto fail; + len = virtTestLoadFile(cmdline, &expectargv); if (len < 0) goto fail; @@ -110,7 +115,8 @@ static int testCompareXMLToArgvFiles(con if (!(cmd = qemuBuildCommandLine(conn, &driver, vmdef, &monitor_chr, json, extraFlags, migrateFrom, migrateFd, NULL, - VIR_NETDEV_VPORT_PROFILE_OP_NO_OP))) + VIR_NETDEV_VPORT_PROFILE_OP_NO_OP, + fdset))) goto fail; if (!!virGetLastError() != expectError) { @@ -151,6 +157,7 @@ static int testCompareXMLToArgvFiles(con virCommandFree(cmd); virDomainDefFree(vmdef); virObjectUnref(conn); + virBitmapFree(fdset); return ret; } Index: libvirt/src/qemu/qemu_driver.h =================================================================== --- libvirt.orig/src/qemu/qemu_driver.h +++ libvirt/src/qemu/qemu_driver.h @@ -24,6 +24,10 @@ #ifndef __QEMU_DRIVER_H__ # define __QEMU_DRIVER_H__ +# include "qemu_conf.h" + int qemuRegister(void); +int qemuOpenFile(virQEMUDriverPtr driver, const char *path, int oflags, + bool *needUnlink, bool *bypassSecurityDriver); #endif /* __QEMU_DRIVER_H__ */ Index: libvirt/src/conf/domain_conf.h =================================================================== --- libvirt.orig/src/conf/domain_conf.h +++ libvirt/src/conf/domain_conf.h @@ -262,6 +262,7 @@ struct _virDomainDeviceInfo { * to consider the new fields */ char *alias; + unsigned int fdset; /* > 0; "-add-fd set=%u,fd=123", fdsetnum */ int type; union { virDevicePCIAddress pci; Index: libvirt/src/conf/domain_conf.c =================================================================== --- libvirt.orig/src/conf/domain_conf.c +++ libvirt/src/conf/domain_conf.c @@ -2219,7 +2219,10 @@ virDomainDeviceInfoFormat(virBufferPtr b if (info->alias && !(flags & VIR_DOMAIN_XML_INACTIVE)) { - virBufferAsprintf(buf, " <alias name='%s'/>\n", info->alias); + virBufferAsprintf(buf, " <alias name='%s'", info->alias); + if (info->fdset > 0) + virBufferAsprintf(buf, " fdset='%u'", info->fdset); + virBufferAddLit(buf, "/>\n"); } if (info->mastertype == VIR_DOMAIN_CONTROLLER_MASTER_USB) { @@ -2597,6 +2600,7 @@ virDomainDeviceInfoParseXML(xmlNodePtr n xmlNodePtr boot = NULL; xmlNodePtr rom = NULL; char *type = NULL; + char *fdset = NULL; int ret = -1; virDomainDeviceInfoClear(info); @@ -2627,9 +2631,18 @@ virDomainDeviceInfoParseXML(xmlNodePtr n cur = cur->next; } - if (alias) + if (alias) { info->alias = virXMLPropString(alias, "name"); + fdset = virXMLPropString(alias, "fdset"); + if (fdset && virStrToLong_ui(fdset, NULL, 10, &info->fdset) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("incorrect fdset '%s', expecting positive integer"), + fdset); + goto cleanup; + } + } + if (master) { info->mastertype = VIR_DOMAIN_CONTROLLER_MASTER_USB; if (virDomainDeviceUSBMasterParseXML(master, &info->master.usb) < 0) @@ -2716,6 +2729,7 @@ cleanup: if (ret == -1) VIR_FREE(info->alias); VIR_FREE(type); + VIR_FREE(fdset); return ret; } @@ -10563,7 +10577,7 @@ static virDomainObjPtr virDomainObjParse VIR_FREE(nodes); if (caps->privateDataXMLParse && - ((caps->privateDataXMLParse)(ctxt, obj->privateData)) < 0) + ((caps->privateDataXMLParse)(ctxt, obj->privateData, obj->def)) < 0) goto error; return obj; Index: libvirt/src/conf/capabilities.h =================================================================== --- libvirt.orig/src/conf/capabilities.h +++ libvirt/src/conf/capabilities.h @@ -149,6 +149,9 @@ struct _virDomainXMLNamespace { virDomainDefNamespaceHref href; }; +typedef struct _virDomainDef virDomainDef; +typedef virDomainDef *virDomainDefPtr; + typedef struct _virCaps virCaps; typedef virCaps* virCapsPtr; struct _virCaps { @@ -164,7 +167,7 @@ struct _virCaps { void *(*privateDataAllocFunc)(void); void (*privateDataFreeFunc)(void *); int (*privateDataXMLFormat)(virBufferPtr, void *); - int (*privateDataXMLParse)(xmlXPathContextPtr, void *); + int (*privateDataXMLParse)(xmlXPathContextPtr, void *, virDomainDefPtr); bool hasWideScsiBus; const char *defaultInitPath; Index: libvirt/src/lxc/lxc_domain.c =================================================================== --- libvirt.orig/src/lxc/lxc_domain.c +++ libvirt/src/lxc/lxc_domain.c @@ -57,7 +57,8 @@ static int virLXCDomainObjPrivateXMLForm return 0; } -static int virLXCDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data) +static int virLXCDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data, + virDomainDefPtr def ATTRIBUTE_UNUSED) { virLXCDomainObjPrivatePtr priv = data; unsigned long long thepid; Index: libvirt/src/qemu/qemu_domain.c =================================================================== --- libvirt.orig/src/qemu/qemu_domain.c +++ libvirt/src/qemu/qemu_domain.c @@ -336,7 +336,29 @@ static int qemuDomainObjPrivateXMLFormat return 0; } -static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data) +static int qemuDomainRebuildFDSet(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainDeviceDefPtr dev ATTRIBUTE_UNUSED, + virDomainDeviceInfoPtr info, + void *opaque) +{ + virBitmapPtr fdset = opaque; + + if (info->fdset > 0) { + VIR_DEBUG("Found fdset %u from domain '%s'", + info->fdset, def->name); + if (virBitmapSetBit(fdset, info->fdset)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("File descriptor set cannot set bit %u"), + info->fdset); + return -1; + } + } + + return 0; +} + +static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data, + virDomainDefPtr def) { qemuDomainObjPrivatePtr priv = data; char *monitorpath; @@ -478,6 +500,8 @@ static int qemuDomainObjPrivateXMLParse( priv->fakeReboot = virXPathBoolean("boolean(./fakereboot)", ctxt) == 1; + virDomainDeviceInfoIterate(def, qemuDomainRebuildFDSet, priv->fdset); + return 0; error:
participants (2)
-
Eric Blake
-
Stefan Berger