render / rpms / libvirt

Forked from rpms/libvirt 9 months ago
Clone
9119d9
From d2f4b2476f4e1fa8a65d9c7e74a5b909cc38e848 Mon Sep 17 00:00:00 2001
9119d9
Message-Id: <d2f4b2476f4e1fa8a65d9c7e74a5b909cc38e848@dist-git>
9119d9
From: John Ferlan <jferlan@redhat.com>
9119d9
Date: Thu, 20 Nov 2014 10:42:53 -0500
9119d9
Subject: [PATCH] storage: Add thread to refresh for createVport
9119d9
9119d9
https://bugzilla.redhat.com/show_bug.cgi?id=1152382
9119d9
9119d9
When libvirt create's the vport (VPORT_CREATE) for the vHBA, there isn't
9119d9
enough "time" between the creation and the running of the following
9119d9
backend->refreshPool after a backend->startPool in order to find the LU's.
9119d9
Population of LU's happens asynchronously when udevEventHandleCallback
9119d9
discovers the "new" vHBA port.  Creation of the infrastructure by udev
9119d9
is an iterative process creating and discovering actual storage devices and
9119d9
adjusting the environment.
9119d9
9119d9
Because of the time it takes to discover and set things up, the backend
9119d9
refreshPool call done after the startPool call will generally fail to
9119d9
find any devices. This leaves the newly started pool appear empty when
9119d9
querying via 'vol-list' after startup. The "workaround" has always been
9119d9
to run pool-refresh after startup (or any time thereafter) in order to
9119d9
find the LU's. Depending on how quickly run after startup, this too may
9119d9
not find any LUs in the pool. Eventually though given enough time and
9119d9
retries it will find something if LU's exist for the vHBA.
9119d9
9119d9
This patch adds a thread to be executed after the VPORT_CREATE which will
9119d9
attempt to find the LU's without requiring the external run of refresh-pool.
9119d9
It does this by waiting for 5 seconds and searching for the LU's. If any
9119d9
are found, then the thread completes; otherwise, it will retry once more
9119d9
in another 5 seconds.  If none are found in that second pass, the thread
9119d9
gives up.
9119d9
9119d9
Things learned while investigating this... No need to try and fill the
9119d9
pool too quickly or too many times. Over the course of creation, the udev
9119d9
code may 'add', 'change', and 'delete' the same device. So if the refresh
9119d9
code runs and finds something, it may display it only to have a subsequent
9119d9
refresh appear to "lose" the device. The udev processing doesn't seem to
9119d9
have a way to indicate that it's all done with the creation processing of a
9119d9
newly found vHBA. Only the Lone Ranger has silver bullets to fix everything.
9119d9
9119d9
(cherry picked from commit 512b8747d82c682d002f21249a934927da193e78)
9119d9
Signed-off-by: John Ferlan <jferlan@redhat.com>
9119d9
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
9119d9
---
9119d9
 src/storage/storage_backend_scsi.c | 131 +++++++++++++++++++++++++++++++++----
9119d9
 1 file changed, 120 insertions(+), 11 deletions(-)
9119d9
9119d9
diff --git a/src/storage/storage_backend_scsi.c b/src/storage/storage_backend_scsi.c
9119d9
index 367216a..7bc67e4 100644
9119d9
--- a/src/storage/storage_backend_scsi.c
9119d9
+++ b/src/storage/storage_backend_scsi.c
9119d9
@@ -41,6 +41,13 @@
9119d9
 
9119d9
 VIR_LOG_INIT("storage.storage_backend_scsi");
9119d9
 
9119d9
+typedef struct _virStoragePoolFCRefreshInfo virStoragePoolFCRefreshInfo;
9119d9
+typedef virStoragePoolFCRefreshInfo *virStoragePoolFCRefreshInfoPtr;
9119d9
+struct _virStoragePoolFCRefreshInfo {
9119d9
+    char *name;
9119d9
+    virStoragePoolObjPtr pool;
9119d9
+};
9119d9
+
9119d9
 /* Function to check if the type file in the given sysfs_path is a
9119d9
  * Direct-Access device (i.e. type 0).  Return -1 on failure, type of
9119d9
  * the device otherwise.
9119d9
@@ -420,9 +427,10 @@ processLU(virStoragePoolObjPtr pool,
9119d9
 }
9119d9
 
9119d9
 
9119d9
-int
9119d9
-virStorageBackendSCSIFindLUs(virStoragePoolObjPtr pool,
9119d9
-                             uint32_t scanhost)
9119d9
+static int
9119d9
+virStorageBackendSCSIFindLUsInternal(virStoragePoolObjPtr pool,
9119d9
+                                     uint32_t scanhost,
9119d9
+                                     bool *found)
9119d9
 {
9119d9
     int retval = 0;
9119d9
     uint32_t bus, target, lun;
9119d9
@@ -430,7 +438,6 @@ virStorageBackendSCSIFindLUs(virStoragePoolObjPtr pool,
9119d9
     DIR *devicedir = NULL;
9119d9
     struct dirent *lun_dirent = NULL;
9119d9
     char devicepattern[64];
9119d9
-    bool found = false;
9119d9
 
9119d9
     VIR_DEBUG("Discovering LUs on host %u", scanhost);
9119d9
 
9119d9
@@ -446,6 +453,7 @@ virStorageBackendSCSIFindLUs(virStoragePoolObjPtr pool,
9119d9
 
9119d9
     snprintf(devicepattern, sizeof(devicepattern), "%u:%%u:%%u:%%u\n", scanhost);
9119d9
 
9119d9
+    *found = false;
9119d9
     while ((retval = virDirRead(devicedir, &lun_dirent, device_path)) > 0) {
9119d9
         if (sscanf(lun_dirent->d_name, devicepattern,
9119d9
                    &bus, &target, &lun) != 3) {
9119d9
@@ -455,10 +463,10 @@ virStorageBackendSCSIFindLUs(virStoragePoolObjPtr pool,
9119d9
         VIR_DEBUG("Found possible LU '%s'", lun_dirent->d_name);
9119d9
 
9119d9
         if (processLU(pool, scanhost, bus, target, lun) == 0)
9119d9
-            found = true;
9119d9
+            *found = true;
9119d9
     }
9119d9
 
9119d9
-    if (!found)
9119d9
+    if (!*found)
9119d9
         VIR_DEBUG("No LU found for pool %s", pool->def->name);
9119d9
 
9119d9
     closedir(devicedir);
9119d9
@@ -466,6 +474,14 @@ virStorageBackendSCSIFindLUs(virStoragePoolObjPtr pool,
9119d9
     return retval;
9119d9
 }
9119d9
 
9119d9
+int
9119d9
+virStorageBackendSCSIFindLUs(virStoragePoolObjPtr pool,
9119d9
+                             uint32_t scanhost)
9119d9
+{
9119d9
+    bool found;  /* This path doesn't care whether found or not */
9119d9
+    return virStorageBackendSCSIFindLUsInternal(pool, scanhost, &found);
9119d9
+}
9119d9
+
9119d9
 static int
9119d9
 virStorageBackendSCSITriggerRescan(uint32_t host)
9119d9
 {
9119d9
@@ -511,6 +527,74 @@ virStorageBackendSCSITriggerRescan(uint32_t host)
9119d9
     return retval;
9119d9
 }
9119d9
 
9119d9
+/**
9119d9
+ * Frees opaque data
9119d9
+ *
9119d9
+ * @opaque Data to be freed
9119d9
+ */
9119d9
+static void
9119d9
+virStoragePoolFCRefreshDataFree(void *opaque)
9119d9
+{
9119d9
+    virStoragePoolFCRefreshInfoPtr cbdata = opaque;
9119d9
+
9119d9
+    VIR_FREE(cbdata->name);
9119d9
+    VIR_FREE(cbdata);
9119d9
+}
9119d9
+
9119d9
+/**
9119d9
+ * Thread to handle the pool refresh after a VPORT_CREATE is done. In this
9119d9
+ * case the 'udevEventHandleCallback' will be executed asynchronously as a
9119d9
+ * result of the node device driver callback routine to handle when udev
9119d9
+ * notices some sort of device change (such as adding a new device). It takes
9119d9
+ * some amount of time (usually a few seconds) for udev to go through the
9119d9
+ * process of setting up the new device.  Unfortunately, there is nothing
9119d9
+ * that says "when" it's done. The immediate virStorageBackendSCSIRefreshPool
9119d9
+ * done after virStorageBackendSCSIStartPool (and createVport) occurs too
9119d9
+ * quickly to find any devices.
9119d9
+ *
9119d9
+ * So this thread is designed to wait a few seconds (5), then make the query
9119d9
+ * to find the LUs for the pool.  If none yet exist, we'll try once more
9119d9
+ * to find the LUs before giving up.
9119d9
+ *
9119d9
+ * Attempting to find devices prior to allowing udev to settle down may result
9119d9
+ * in finding devices that then get deleted.
9119d9
+ *
9119d9
+ * @opaque Pool's Refresh Info containing name and pool object pointer
9119d9
+ */
9119d9
+static void
9119d9
+virStoragePoolFCRefreshThread(void *opaque)
9119d9
+{
9119d9
+    virStoragePoolFCRefreshInfoPtr cbdata = opaque;
9119d9
+    const char *name = cbdata->name;
9119d9
+    virStoragePoolObjPtr pool = cbdata->pool;
9119d9
+    unsigned int host;
9119d9
+    bool found = false;
9119d9
+    int tries = 2;
9119d9
+
9119d9
+    do {
9119d9
+        sleep(5); /* Give it time */
9119d9
+
9119d9
+        /* Lock the pool, if active, we can get the host number, successfully
9119d9
+         * rescan, and find LUN's, then we are happy
9119d9
+         */
9119d9
+        VIR_DEBUG("Attempt FC Refresh for pool='%s' name='%s' tries='%d'",
9119d9
+                  pool->def->name, name, tries);
9119d9
+        virStoragePoolObjLock(pool);
9119d9
+        if (virStoragePoolObjIsActive(pool) &&
9119d9
+            virGetSCSIHostNumber(name, &host) == 0 &&
9119d9
+            virStorageBackendSCSITriggerRescan(host) == 0) {
9119d9
+            virStoragePoolObjClearVols(pool);
9119d9
+            virStorageBackendSCSIFindLUsInternal(pool, host, &found);
9119d9
+        }
9119d9
+        virStoragePoolObjUnlock(pool);
9119d9
+    } while (!found && --tries);
9119d9
+
9119d9
+    if (!found)
9119d9
+        VIR_DEBUG("FC Refresh Thread failed to find LU's");
9119d9
+
9119d9
+    virStoragePoolFCRefreshDataFree(cbdata);
9119d9
+}
9119d9
+
9119d9
 static char *
9119d9
 getAdapterName(virStoragePoolSourceAdapter adapter)
9119d9
 {
9119d9
@@ -646,13 +730,15 @@ checkVhbaSCSIHostParent(virConnectPtr conn,
9119d9
 
9119d9
 static int
9119d9
 createVport(virConnectPtr conn,
9119d9
-            const char *configFile,
9119d9
-            virStoragePoolDefPtr def)
9119d9
+            virStoragePoolObjPtr pool)
9119d9
 {
9119d9
-    virStoragePoolSourceAdapterPtr adapter = &def->source.adapter;
9119d9
+    const char *configFile = pool->configFile;
9119d9
+    virStoragePoolSourceAdapterPtr adapter = &pool->def->source.adapter;
9119d9
     unsigned int parent_host;
9119d9
     char *name = NULL;
9119d9
     char *parent_hoststr = NULL;
9119d9
+    virStoragePoolFCRefreshInfoPtr cbdata = NULL;
9119d9
+    virThread thread;
9119d9
 
9119d9
     if (adapter->type != VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST)
9119d9
         return 0;
9119d9
@@ -726,7 +812,7 @@ createVport(virConnectPtr conn,
9119d9
     if (adapter->data.fchost.managed != VIR_TRISTATE_BOOL_YES) {
9119d9
         adapter->data.fchost.managed = VIR_TRISTATE_BOOL_YES;
9119d9
         if (configFile) {
9119d9
-            if (virStoragePoolSaveConfig(configFile, def) < 0)
9119d9
+            if (virStoragePoolSaveConfig(configFile, pool->def) < 0)
9119d9
                 return -1;
9119d9
         }
9119d9
     }
9119d9
@@ -736,6 +822,29 @@ createVport(virConnectPtr conn,
9119d9
         return -1;
9119d9
 
9119d9
     virFileWaitForDevices();
9119d9
+
9119d9
+    /* Creating our own VPORT didn't leave enough time to find any LUN's,
9119d9
+     * so, let's create a thread whose job it is to call the FindLU's with
9119d9
+     * retry logic set to true. If the thread isn't created, then no big
9119d9
+     * deal since it's still possible to refresh the pool later.
9119d9
+     */
9119d9
+    if ((name = virGetFCHostNameByWWN(NULL, adapter->data.fchost.wwnn,
9119d9
+                                      adapter->data.fchost.wwpn))) {
9119d9
+        if (VIR_ALLOC(cbdata) == 0) {
9119d9
+            cbdata->pool = pool;
9119d9
+            cbdata->name = name;
9119d9
+            name = NULL;
9119d9
+
9119d9
+            if (virThreadCreate(&thread, false, virStoragePoolFCRefreshThread,
9119d9
+                                cbdata) < 0) {
9119d9
+                /* Oh well - at least someone can still refresh afterwards */
9119d9
+                VIR_DEBUG("Failed to create FC Pool Refresh Thread");
9119d9
+                virStoragePoolFCRefreshDataFree(cbdata);
9119d9
+            }
9119d9
+        }
9119d9
+        VIR_FREE(name);
9119d9
+    }
9119d9
+
9119d9
     return 0;
9119d9
 }
9119d9
 
9119d9
@@ -872,7 +981,7 @@ static int
9119d9
 virStorageBackendSCSIStartPool(virConnectPtr conn,
9119d9
                                virStoragePoolObjPtr pool)
9119d9
 {
9119d9
-    return createVport(conn, pool->configFile, pool->def);
9119d9
+    return createVport(conn, pool);
9119d9
 }
9119d9
 
9119d9
 static int
9119d9
-- 
9119d9
2.1.3
9119d9