[libvirt] [PATCH 2/2] netcf driver: use a single netcf handle for all connections

Laine Stump laine at laine.org
Wed Aug 28 17:39:09 UTC 2013


This resolves: https://bugzilla.redhat.com/show_bug.cgi?id=983026

The netcf interface driver previously had no state driver associated
with it - as a connection was opened, it would create a new netcf
instance just for that connection, and close it when it was
finished. the problem with this is that each connection to libvirt
used up a netlink socket, and there is a per process maximum of ~1000
netlink sockets.

The solution is to create a state driver to go along with the netcf
driver. The state driver will open a netcf instance, then all
connections will share that same netcf instance, thus only a single
netlink socket will be used no matter how many connections are mde to
libvirtd.

This was rather simple to do - most of the functionality from
netcfInterfaceOpen() was moved to netcfStateInitialize (which
initializes a single global driverState), and netcfInterfaceOpen now
just puts a pointer to driverState into the privateData. A similar
change was mad eto netcfStateCleanup() vs netcfInterfaceClose(). Since
all the functions already have locking around them, no other change
was necessary (they now use the single global lock rather than a lock
specific to their connection).
---
 src/interface/interface_backend_netcf.c | 157 ++++++++++++++++++++++++--------
 1 file changed, 117 insertions(+), 40 deletions(-)

diff --git a/src/interface/interface_backend_netcf.c b/src/interface/interface_backend_netcf.c
index f47669e..1b5c850 100644
--- a/src/interface/interface_backend_netcf.c
+++ b/src/interface/interface_backend_netcf.c
@@ -43,8 +43,10 @@ typedef struct
 {
     virMutex lock;
     struct netcf *netcf;
+    size_t nConnections;
 } virNetcfDriverState, *virNetcfDriverStatePtr;
 
+virNetcfDriverStatePtr driverState = NULL;
 
 static void interfaceDriverLock(virNetcfDriverStatePtr driver)
 {
@@ -56,6 +58,98 @@ static void interfaceDriverUnlock(virNetcfDriverStatePtr driver)
     virMutexUnlock(&driver->lock);
 }
 
+static int
+netcfStateInitialize(bool privileged ATTRIBUTE_UNUSED,
+                     virStateInhibitCallback callback ATTRIBUTE_UNUSED,
+                     void *opaque ATTRIBUTE_UNUSED)
+{
+    if (VIR_ALLOC(driverState) < 0)
+        goto alloc_error;
+
+    /* initialize non-0 stuff in driverState */
+    if (virMutexInit(&driverState->lock) < 0)
+    {
+        /* what error to report? */
+        goto mutex_error;
+    }
+
+    /* open netcf */
+    if (ncf_init(&driverState->netcf, NULL) != 0)
+    {
+        /* what error to report? */
+        goto netcf_error;
+    }
+    return 0;
+
+netcf_error:
+    if (driverState->netcf)
+    {
+        ncf_close(driverState->netcf);
+    }
+    virMutexDestroy(&driverState->lock);
+mutex_error:
+    VIR_FREE(driverState);
+alloc_error:
+    return -1;
+}
+
+static int
+netcfStateCleanup(void)
+{
+    if (!driverState) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Attempt to close netcf state driver already closed"));
+        return -1;
+    }
+    if (driverState->nConnections) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Attempt to close netcf state driver with %zu open connections"),
+                       driverState->nConnections);
+        return -1;
+    }
+
+    /* assume that we are single-thread here, so lock isn't needed */
+
+    /* close netcf instance */
+    ncf_close(driverState->netcf);
+    /* destroy lock */
+    virMutexDestroy(&driverState->lock);
+    /* free driver state */
+    VIR_FREE(driverState);
+    return 0;
+}
+
+static int
+netcfStateReload(void)
+{
+    int ret = -1;
+
+    if (!driverState)
+        return 0;
+
+    interfaceDriverLock(driverState);
+    ncf_close(driverState->netcf);
+    if (ncf_init(&driverState->netcf, NULL) != 0)
+    {
+        /* this isn't a good situation, because we can't shut down the
+         * driver as there may still be connections to it. If we set
+         * the netcf handle to NULL, any subsequent calls to netcf
+         * will just fail rather than causing a crash. Not ideal, but
+         * livable (since this should never happen).
+         */
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("failed to re-init netcf"));
+        driverState->netcf = NULL;
+        goto cleanup;
+    }
+
+    ret = 0;
+cleanup:
+    interfaceDriverUnlock(driverState);
+
+    return ret;
+}
+
 /*
  * Get a minimal virInterfaceDef containing enough metadata
  * for access control checks to be performed. Currently
@@ -148,59 +242,34 @@ static struct netcf_if *interfaceDriverGetNetcfIF(struct netcf *ncf, virInterfac
     return iface;
 }
 
-static virDrvOpenStatus netcfInterfaceOpen(virConnectPtr conn,
-                                           virConnectAuthPtr auth ATTRIBUTE_UNUSED,
-                                           unsigned int flags)
+static virDrvOpenStatus
+netcfInterfaceOpen(virConnectPtr conn,
+                   virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+                   unsigned int flags)
 {
-    virNetcfDriverStatePtr driverState;
-
     virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);
 
-    if (VIR_ALLOC(driverState) < 0)
-        goto alloc_error;
-
-    /* initialize non-0 stuff in driverState */
-    if (virMutexInit(&driverState->lock) < 0)
-    {
-        /* what error to report? */
-        goto mutex_error;
-    }
-
-    /* open netcf */
-    if (ncf_init(&driverState->netcf, NULL) != 0)
-    {
-        /* what error to report? */
-        goto netcf_error;
-    }
+    if (!driverState)
+        return VIR_DRV_OPEN_ERROR;
 
     conn->interfacePrivateData = driverState;
+    interfaceDriverLock(driverState);
+    driverState->nConnections++;
+    interfaceDriverUnlock(driverState);
     return VIR_DRV_OPEN_SUCCESS;
-
-netcf_error:
-    if (driverState->netcf)
-    {
-        ncf_close(driverState->netcf);
-    }
-    virMutexDestroy(&driverState->lock);
-mutex_error:
-    VIR_FREE(driverState);
-alloc_error:
-    return VIR_DRV_OPEN_ERROR;
 }
 
-static int netcfInterfaceClose(virConnectPtr conn)
+static int
+netcfInterfaceClose(virConnectPtr conn)
 {
 
     if (conn->interfacePrivateData != NULL)
     {
         virNetcfDriverStatePtr driver = conn->interfacePrivateData;
 
-        /* close netcf instance */
-        ncf_close(driver->netcf);
-        /* destroy lock */
-        virMutexDestroy(&driver->lock);
-        /* free driver state */
-        VIR_FREE(driver);
+        interfaceDriverLock(driver);
+        driverState->nConnections--;
+        interfaceDriverUnlock(driver);
     }
     conn->interfacePrivateData = NULL;
     return 0;
@@ -1070,7 +1139,7 @@ static int netcfInterfaceChangeRollback(virConnectPtr conn, unsigned int flags)
 #endif /* HAVE_NETCF_TRANSACTIONS */
 
 static virInterfaceDriver interfaceDriver = {
-    "netcf",
+    .name = INTERFACE_DRIVER_NAME,
     .interfaceOpen = netcfInterfaceOpen, /* 0.7.0 */
     .interfaceClose = netcfInterfaceClose, /* 0.7.0 */
     .connectNumOfInterfaces = netcfConnectNumOfInterfaces, /* 0.7.0 */
@@ -1093,11 +1162,19 @@ static virInterfaceDriver interfaceDriver = {
 #endif /* HAVE_NETCF_TRANSACTIONS */
 };
 
+static virStateDriver interfaceStateDriver = {
+    .name = INTERFACE_DRIVER_NAME,
+    .stateInitialize = netcfStateInitialize,
+    .stateCleanup = netcfStateCleanup,
+    .stateReload = netcfStateReload,
+};
+
 int netcfIfaceRegister(void) {
     if (virRegisterInterfaceDriver(&interfaceDriver) < 0) {
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                        _("failed to register netcf interface driver"));
         return -1;
     }
+    virRegisterStateDriver(&interfaceStateDriver);
     return 0;
 }
-- 
1.7.11.7




More information about the libvir-list mailing list