render / rpms / libvirt

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