|
|
a41c76 |
From b155913f796b313ce969ae318beb66e3e35d13dc Mon Sep 17 00:00:00 2001
|
|
|
a41c76 |
Message-Id: <b155913f796b313ce969ae318beb66e3e35d13dc@dist-git>
|
|
|
a41c76 |
From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com>
|
|
|
a41c76 |
Date: Thu, 11 Jun 2020 18:21:26 +0100
|
|
|
a41c76 |
Subject: [PATCH] nodedev: fix race in API usage vs initial device enumeration
|
|
|
a41c76 |
MIME-Version: 1.0
|
|
|
a41c76 |
Content-Type: text/plain; charset=UTF-8
|
|
|
a41c76 |
Content-Transfer-Encoding: 8bit
|
|
|
a41c76 |
|
|
|
a41c76 |
During startup the udev node device driver impl uses a background thread
|
|
|
a41c76 |
to populate the list of devices to avoid blocking the daemon startup
|
|
|
a41c76 |
entirely. There is no synchronization to the public APIs, so it is
|
|
|
a41c76 |
possible for an application to start calling APIs before the device
|
|
|
a41c76 |
initialization is complete.
|
|
|
a41c76 |
|
|
|
a41c76 |
This was not a problem in the old approach where libvirtd was started
|
|
|
a41c76 |
on boot, as initialization would easily complete before any APIs were
|
|
|
a41c76 |
called.
|
|
|
a41c76 |
|
|
|
a41c76 |
With the use of socket activation, however, APIs are invoked from the
|
|
|
a41c76 |
very moment the daemon starts. This is easily seen by doing a
|
|
|
a41c76 |
|
|
|
a41c76 |
'virsh -c nodedev:///system list'
|
|
|
a41c76 |
|
|
|
a41c76 |
the first time it runs it will only show one or two devices. The second
|
|
|
a41c76 |
time it runs it will show all devices. The solution is to introduce a
|
|
|
a41c76 |
flag and condition variable for APIs to synchronize against before
|
|
|
a41c76 |
returning any data.
|
|
|
a41c76 |
|
|
|
a41c76 |
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
|
|
|
a41c76 |
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
|
|
|
a41c76 |
(cherry picked from commit 008abeb03c262149b756ad5a226ff6cbc5e37e2c)
|
|
|
a41c76 |
|
|
|
a41c76 |
https://bugzilla.redhat.com/show_bug.cgi?id=1846237
|
|
|
a41c76 |
https://bugzilla.redhat.com/show_bug.cgi?id=1845459
|
|
|
a41c76 |
Message-Id: <20200611172126.269081-2-berrange@redhat.com>
|
|
|
a41c76 |
Reviewed-by: Jiri Denemark <jdenemar@redhat.com>
|
|
|
a41c76 |
---
|
|
|
a41c76 |
src/conf/virnodedeviceobj.h | 2 ++
|
|
|
a41c76 |
src/node_device/node_device_driver.c | 44 ++++++++++++++++++++++++++++
|
|
|
a41c76 |
src/node_device/node_device_hal.c | 15 ++++++++++
|
|
|
a41c76 |
src/node_device/node_device_udev.c | 13 ++++++++
|
|
|
a41c76 |
4 files changed, 74 insertions(+)
|
|
|
a41c76 |
|
|
|
a41c76 |
diff --git a/src/conf/virnodedeviceobj.h b/src/conf/virnodedeviceobj.h
|
|
|
a41c76 |
index c4d3c55d73..c9df8dedab 100644
|
|
|
a41c76 |
--- a/src/conf/virnodedeviceobj.h
|
|
|
a41c76 |
+++ b/src/conf/virnodedeviceobj.h
|
|
|
a41c76 |
@@ -36,6 +36,8 @@ typedef struct _virNodeDeviceDriverState virNodeDeviceDriverState;
|
|
|
a41c76 |
typedef virNodeDeviceDriverState *virNodeDeviceDriverStatePtr;
|
|
|
a41c76 |
struct _virNodeDeviceDriverState {
|
|
|
a41c76 |
virMutex lock;
|
|
|
a41c76 |
+ virCond initCond;
|
|
|
a41c76 |
+ bool initialized;
|
|
|
a41c76 |
|
|
|
a41c76 |
/* pid file FD, ensures two copies of the driver can't use the same root */
|
|
|
a41c76 |
int lockFD;
|
|
|
a41c76 |
diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c
|
|
|
a41c76 |
index b630be4399..a894f7265f 100644
|
|
|
a41c76 |
--- a/src/node_device/node_device_driver.c
|
|
|
a41c76 |
+++ b/src/node_device/node_device_driver.c
|
|
|
a41c76 |
@@ -162,6 +162,22 @@ nodeDeviceUnlock(void)
|
|
|
a41c76 |
}
|
|
|
a41c76 |
|
|
|
a41c76 |
|
|
|
a41c76 |
+static int
|
|
|
a41c76 |
+nodeDeviceWaitInit(void)
|
|
|
a41c76 |
+{
|
|
|
a41c76 |
+ nodeDeviceLock();
|
|
|
a41c76 |
+ while (!driver->initialized) {
|
|
|
a41c76 |
+ if (virCondWait(&driver->initCond, &driver->lock) < 0) {
|
|
|
a41c76 |
+ virReportSystemError(errno, "%s",
|
|
|
a41c76 |
+ _("failed to wait on condition"));
|
|
|
a41c76 |
+ nodeDeviceUnlock();
|
|
|
a41c76 |
+ return -1;
|
|
|
a41c76 |
+ }
|
|
|
a41c76 |
+ }
|
|
|
a41c76 |
+ nodeDeviceUnlock();
|
|
|
a41c76 |
+ return 0;
|
|
|
a41c76 |
+}
|
|
|
a41c76 |
+
|
|
|
a41c76 |
int
|
|
|
a41c76 |
nodeNumOfDevices(virConnectPtr conn,
|
|
|
a41c76 |
const char *cap,
|
|
|
a41c76 |
@@ -172,6 +188,9 @@ nodeNumOfDevices(virConnectPtr conn,
|
|
|
a41c76 |
|
|
|
a41c76 |
virCheckFlags(0, -1);
|
|
|
a41c76 |
|
|
|
a41c76 |
+ if (nodeDeviceWaitInit() < 0)
|
|
|
a41c76 |
+ return -1;
|
|
|
a41c76 |
+
|
|
|
a41c76 |
return virNodeDeviceObjListNumOfDevices(driver->devs, conn, cap,
|
|
|
a41c76 |
virNodeNumOfDevicesCheckACL);
|
|
|
a41c76 |
}
|
|
|
a41c76 |
@@ -189,6 +208,9 @@ nodeListDevices(virConnectPtr conn,
|
|
|
a41c76 |
|
|
|
a41c76 |
virCheckFlags(0, -1);
|
|
|
a41c76 |
|
|
|
a41c76 |
+ if (nodeDeviceWaitInit() < 0)
|
|
|
a41c76 |
+ return -1;
|
|
|
a41c76 |
+
|
|
|
a41c76 |
return virNodeDeviceObjListGetNames(driver->devs, conn,
|
|
|
a41c76 |
virNodeListDevicesCheckACL,
|
|
|
a41c76 |
cap, names, maxnames);
|
|
|
a41c76 |
@@ -205,6 +227,9 @@ nodeConnectListAllNodeDevices(virConnectPtr conn,
|
|
|
a41c76 |
if (virConnectListAllNodeDevicesEnsureACL(conn) < 0)
|
|
|
a41c76 |
return -1;
|
|
|
a41c76 |
|
|
|
a41c76 |
+ if (nodeDeviceWaitInit() < 0)
|
|
|
a41c76 |
+ return -1;
|
|
|
a41c76 |
+
|
|
|
a41c76 |
return virNodeDeviceObjListExport(conn, driver->devs, devices,
|
|
|
a41c76 |
virConnectListAllNodeDevicesCheckACL,
|
|
|
a41c76 |
flags);
|
|
|
a41c76 |
@@ -234,6 +259,9 @@ nodeDeviceLookupByName(virConnectPtr conn,
|
|
|
a41c76 |
virNodeDeviceDefPtr def;
|
|
|
a41c76 |
virNodeDevicePtr device = NULL;
|
|
|
a41c76 |
|
|
|
a41c76 |
+ if (nodeDeviceWaitInit() < 0)
|
|
|
a41c76 |
+ return NULL;
|
|
|
a41c76 |
+
|
|
|
a41c76 |
if (!(obj = nodeDeviceObjFindByName(name)))
|
|
|
a41c76 |
return NULL;
|
|
|
a41c76 |
def = virNodeDeviceObjGetDef(obj);
|
|
|
a41c76 |
@@ -262,6 +290,9 @@ nodeDeviceLookupSCSIHostByWWN(virConnectPtr conn,
|
|
|
a41c76 |
|
|
|
a41c76 |
virCheckFlags(0, NULL);
|
|
|
a41c76 |
|
|
|
a41c76 |
+ if (nodeDeviceWaitInit() < 0)
|
|
|
a41c76 |
+ return NULL;
|
|
|
a41c76 |
+
|
|
|
a41c76 |
if (!(obj = virNodeDeviceObjListFindSCSIHostByWWNs(driver->devs,
|
|
|
a41c76 |
wwnn, wwpn)))
|
|
|
a41c76 |
return NULL;
|
|
|
a41c76 |
@@ -475,6 +506,10 @@ nodeDeviceCreateXML(virConnectPtr conn,
|
|
|
a41c76 |
const char *virt_type = NULL;
|
|
|
a41c76 |
|
|
|
a41c76 |
virCheckFlags(0, NULL);
|
|
|
a41c76 |
+
|
|
|
a41c76 |
+ if (nodeDeviceWaitInit() < 0)
|
|
|
a41c76 |
+ return NULL;
|
|
|
a41c76 |
+
|
|
|
a41c76 |
virt_type = virConnectGetType(conn);
|
|
|
a41c76 |
|
|
|
a41c76 |
if (!(def = virNodeDeviceDefParseString(xmlDesc, CREATE_DEVICE, virt_type)))
|
|
|
a41c76 |
@@ -519,6 +554,9 @@ nodeDeviceDestroy(virNodeDevicePtr device)
|
|
|
a41c76 |
char *wwnn = NULL, *wwpn = NULL;
|
|
|
a41c76 |
unsigned int parent_host;
|
|
|
a41c76 |
|
|
|
a41c76 |
+ if (nodeDeviceWaitInit() < 0)
|
|
|
a41c76 |
+ return -1;
|
|
|
a41c76 |
+
|
|
|
a41c76 |
if (!(obj = nodeDeviceObjFindByName(device->name)))
|
|
|
a41c76 |
return -1;
|
|
|
a41c76 |
def = virNodeDeviceObjGetDef(obj);
|
|
|
a41c76 |
@@ -574,6 +612,9 @@ nodeConnectNodeDeviceEventRegisterAny(virConnectPtr conn,
|
|
|
a41c76 |
if (virConnectNodeDeviceEventRegisterAnyEnsureACL(conn) < 0)
|
|
|
a41c76 |
return -1;
|
|
|
a41c76 |
|
|
|
a41c76 |
+ if (nodeDeviceWaitInit() < 0)
|
|
|
a41c76 |
+ return -1;
|
|
|
a41c76 |
+
|
|
|
a41c76 |
if (virNodeDeviceEventStateRegisterID(conn, driver->nodeDeviceEventState,
|
|
|
a41c76 |
device, eventID, callback,
|
|
|
a41c76 |
opaque, freecb, &callbackID) < 0)
|
|
|
a41c76 |
@@ -590,6 +631,9 @@ nodeConnectNodeDeviceEventDeregisterAny(virConnectPtr conn,
|
|
|
a41c76 |
if (virConnectNodeDeviceEventDeregisterAnyEnsureACL(conn) < 0)
|
|
|
a41c76 |
return -1;
|
|
|
a41c76 |
|
|
|
a41c76 |
+ if (nodeDeviceWaitInit() < 0)
|
|
|
a41c76 |
+ return -1;
|
|
|
a41c76 |
+
|
|
|
a41c76 |
if (virObjectEventStateDeregisterID(conn,
|
|
|
a41c76 |
driver->nodeDeviceEventState,
|
|
|
a41c76 |
callbackID, true) < 0)
|
|
|
a41c76 |
diff --git a/src/node_device/node_device_hal.c b/src/node_device/node_device_hal.c
|
|
|
a41c76 |
index 4cef7c2c12..0d48895b66 100644
|
|
|
a41c76 |
--- a/src/node_device/node_device_hal.c
|
|
|
a41c76 |
+++ b/src/node_device/node_device_hal.c
|
|
|
a41c76 |
@@ -603,6 +603,15 @@ nodeStateInitialize(bool privileged G_GNUC_UNUSED,
|
|
|
a41c76 |
VIR_FREE(driver);
|
|
|
a41c76 |
return VIR_DRV_STATE_INIT_ERROR;
|
|
|
a41c76 |
}
|
|
|
a41c76 |
+
|
|
|
a41c76 |
+ if (virCondInit(&driver->initCond) < 0) {
|
|
|
a41c76 |
+ virReportSystemError(errno, "%s",
|
|
|
a41c76 |
+ _("Unable to initialize condition variable"));
|
|
|
a41c76 |
+ virMutexDestroy(&driver->lock);
|
|
|
a41c76 |
+ VIR_FREE(driver);
|
|
|
a41c76 |
+ return VIR_DRV_STATE_INIT_ERROR;
|
|
|
a41c76 |
+ }
|
|
|
a41c76 |
+
|
|
|
a41c76 |
nodeDeviceLock();
|
|
|
a41c76 |
|
|
|
a41c76 |
if (privileged) {
|
|
|
a41c76 |
@@ -693,6 +702,11 @@ nodeStateInitialize(bool privileged G_GNUC_UNUSED,
|
|
|
a41c76 |
}
|
|
|
a41c76 |
VIR_FREE(udi);
|
|
|
a41c76 |
|
|
|
a41c76 |
+ nodeDeviceLock();
|
|
|
a41c76 |
+ driver->initialized = true;
|
|
|
a41c76 |
+ nodeDeviceUnlock();
|
|
|
a41c76 |
+ virCondBroadcast(&driver->initCond);
|
|
|
a41c76 |
+
|
|
|
a41c76 |
return VIR_DRV_STATE_INIT_COMPLETE;
|
|
|
a41c76 |
|
|
|
a41c76 |
failure:
|
|
|
a41c76 |
@@ -725,6 +739,7 @@ nodeStateCleanup(void)
|
|
|
a41c76 |
|
|
|
a41c76 |
VIR_FREE(driver->stateDir);
|
|
|
a41c76 |
nodeDeviceUnlock();
|
|
|
a41c76 |
+ virCondDestroy(&driver->initCond);
|
|
|
a41c76 |
virMutexDestroy(&driver->lock);
|
|
|
a41c76 |
VIR_FREE(driver);
|
|
|
a41c76 |
return 0;
|
|
|
a41c76 |
diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
|
|
|
a41c76 |
index 4b33dc25d8..ae3d081e66 100644
|
|
|
a41c76 |
--- a/src/node_device/node_device_udev.c
|
|
|
a41c76 |
+++ b/src/node_device/node_device_udev.c
|
|
|
a41c76 |
@@ -1476,6 +1476,7 @@ nodeStateCleanup(void)
|
|
|
a41c76 |
virPidFileRelease(driver->stateDir, "driver", driver->lockFD);
|
|
|
a41c76 |
|
|
|
a41c76 |
VIR_FREE(driver->stateDir);
|
|
|
a41c76 |
+ virCondDestroy(&driver->initCond);
|
|
|
a41c76 |
virMutexDestroy(&driver->lock);
|
|
|
a41c76 |
VIR_FREE(driver);
|
|
|
a41c76 |
|
|
|
a41c76 |
@@ -1743,6 +1744,11 @@ nodeStateInitializeEnumerate(void *opaque)
|
|
|
a41c76 |
if (udevEnumerateDevices(udev) != 0)
|
|
|
a41c76 |
goto error;
|
|
|
a41c76 |
|
|
|
a41c76 |
+ nodeDeviceLock();
|
|
|
a41c76 |
+ driver->initialized = true;
|
|
|
a41c76 |
+ nodeDeviceUnlock();
|
|
|
a41c76 |
+ virCondBroadcast(&driver->initCond);
|
|
|
a41c76 |
+
|
|
|
a41c76 |
return;
|
|
|
a41c76 |
|
|
|
a41c76 |
error:
|
|
|
a41c76 |
@@ -1798,6 +1804,13 @@ nodeStateInitialize(bool privileged,
|
|
|
a41c76 |
VIR_FREE(driver);
|
|
|
a41c76 |
return VIR_DRV_STATE_INIT_ERROR;
|
|
|
a41c76 |
}
|
|
|
a41c76 |
+ if (virCondInit(&driver->initCond) < 0) {
|
|
|
a41c76 |
+ virReportSystemError(errno, "%s",
|
|
|
a41c76 |
+ _("Unable to initialize condition variable"));
|
|
|
a41c76 |
+ virMutexDestroy(&driver->lock);
|
|
|
a41c76 |
+ VIR_FREE(driver);
|
|
|
a41c76 |
+ return VIR_DRV_STATE_INIT_ERROR;
|
|
|
a41c76 |
+ }
|
|
|
a41c76 |
|
|
|
a41c76 |
driver->privileged = privileged;
|
|
|
a41c76 |
|
|
|
a41c76 |
--
|
|
|
a41c76 |
2.27.0
|
|
|
a41c76 |
|