|
|
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 |
|