The qemu driver contains a subtle race in the logic to find next available vnc port. Currently it iterates through all available ports and returns the first for which bind(2) succeeds. However it is possible that a previously issued port has not yet been bound by qemu, resulting in the same port used for a subsequent domain. This patch addresses the race by using a simple bitmap to "reserve" the ports allocated by libvirt. V2: - Put port bitmap in struct qemud_driver - Initialize bitmap in qemudStartup --- src/qemu/qemu_conf.h | 3 +++ src/qemu/qemu_driver.c | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index a101e47..d7d4c57 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -39,6 +39,7 @@ # include "pci.h" # include "cpu_conf.h" # include "driver.h" +# include "bitmap.h" # define qemudDebug(fmt, ...) do {} while(0) @@ -153,6 +154,8 @@ struct qemud_driver { char *saveImageFormat; pciDeviceList *activePciHostdevs; + + virBitmapPtr reservedVNCPorts; }; typedef struct _qemuDomainPCIAddressSet qemuDomainPCIAddressSet; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 06cf660..01867db 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1479,6 +1479,11 @@ qemudStartup(int privileged) { virEventAddTimeout(-1, qemuDomainEventFlush, qemu_driver, NULL)) < 0) goto error; + /* Allocate bitmap for vnc port reservation */ + if ((qemu_driver->reservedVNCPorts = + virBitmapAlloc(QEMU_VNC_PORT_MAX - QEMU_VNC_PORT_MIN)) == NULL) + goto out_of_memory; + if (privileged) { if (virAsprintf(&qemu_driver->logDir, "%s/log/libvirt/qemu", LOCAL_STATE_DIR) == -1) @@ -1775,6 +1780,7 @@ qemudShutdown(void) { virCapabilitiesFree(qemu_driver->caps); virDomainObjListDeinit(&qemu_driver->domains); + virBitmapFree(qemu_driver->reservedVNCPorts); VIR_FREE(qemu_driver->securityDriverName); VIR_FREE(qemu_driver->logDir); @@ -2632,13 +2638,19 @@ qemuInitPCIAddresses(struct qemud_driver *driver, return ret; } -static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) { +static int qemudNextFreeVNCPort(struct qemud_driver *driver) { int i; for (i = QEMU_VNC_PORT_MIN; i < QEMU_VNC_PORT_MAX; i++) { int fd; int reuse = 1; struct sockaddr_in addr; + bool used = false; + + virBitmapGetBit(driver->reservedVNCPorts, i - QEMU_VNC_PORT_MIN, &used); + if (used) + continue; + addr.sin_family = AF_INET; addr.sin_port = htons(i); addr.sin_addr.s_addr = htonl(INADDR_ANY); @@ -2654,6 +2666,8 @@ static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) { if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) { /* Not in use, lets grab it */ close(fd); + /* Add port to bitmap of reserved ports */ + virBitmapSetBit(driver->reservedVNCPorts, i - QEMU_VNC_PORT_MIN); return i; } close(fd); @@ -3582,8 +3596,13 @@ cleanup: qemuRemoveCgroup(driver, vm, 1); if ((vm->def->ngraphics == 1) && vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && - vm->def->graphics[0]->data.vnc.autoport) + vm->def->graphics[0]->data.vnc.autoport) { + virBitmapClearBit(driver->reservedVNCPorts, + vm->def->graphics[0]->data.vnc.port - \ + QEMU_VNC_PORT_MIN); vm->def->graphics[0]->data.vnc.port = -1; + } + if (logfile != -1) close(logfile); vm->def->id = -1; @@ -3716,6 +3735,17 @@ retry: qemudRemoveDomainStatus(driver, vm); + /* Remove VNC port from port reservation bitmap, but only if it was + reserved by the driver (autoport=yes) + */ + if ((vm->def->ngraphics == 1) && + vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && + vm->def->graphics[0]->data.vnc.autoport) { + virBitmapClearBit(driver->reservedVNCPorts, + vm->def->graphics[0]->data.vnc.port - \ + QEMU_VNC_PORT_MIN); + } + vm->pid = -1; vm->def->id = -1; vm->state = VIR_DOMAIN_SHUTOFF; -- 1.6.0.2 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list