https://bugzilla.redhat.com/show_bug.cgi?id=1072653
Upon successful upload of a volume, the target volume and storage pool
were not updated to reflect any changes as a result of the upload. Make
use of the existing stream close callback mechanism to force a backend
pool refresh to occur in a separate thread once the stream closes. The
separate thread should avoid potential deadlocks if the refresh needed
to wait on some event from the event loop which is used to perform
the stream callback.
Signed-off-by: John Ferlan <jferlan(a)redhat.com>
---
Update since v1 - as discussed in original posting, use virThreadCreate to
generate a thread to handle the refresh callback:
http://www.redhat.com/archives/libvir-list/2014-July/msg01395.html
Avoid the Free callback function and have the thread do the memory free
as long as thread creation was successful. Thus a couple of changes to
function names were appropriate.
Understood that this can wait for 1.2.8 - figured to get it ready to go
NOTE: Still used pool refresh, since I found that without this patch
applied a pool-dumpxml displayed the same allocation/available before
and after the upload, so that data is not refreshed for each call as
was my concern over using refreshVol vs. refreshPool
# virsh pool-dumpxml default
...
<allocation unit='bytes'>33852141568</allocation>
<available unit='bytes'>110510419968</available>
...
# virsh vol-upload test-qcow2-4g.img test.img default
# virsh pool-dumpxml default
...
<allocation unit='bytes'>33852141568</allocation>
<available unit='bytes'>110510419968</available>
...
#
src/libvirt.c | 8 ++++
src/libvirt_private.syms | 1 +
src/storage/storage_driver.c | 103 +++++++++++++++++++++++++++++++++++++++++++
tools/virsh.pod | 3 ++
4 files changed, 115 insertions(+)
diff --git a/src/libvirt.c b/src/libvirt.c
index 143d319..992e4f2 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -13960,6 +13960,14 @@ virStorageVolDownload(virStorageVolPtr vol,
* detect any errors. The results will be unpredictable if
* another active stream is writing to the storage volume.
*
+ * When the data stream is closed whether the upload is successful
+ * or not the target storage pool will be refreshed to reflect pool
+ * and volume changes as a result of the upload. Depending on
+ * the target volume storage backend and the source stream type
+ * for a successful upload, the target volume may take on the
+ * characteristics from the source stream such as format type,
+ * capacity, and allocation.
+ *
* Returns 0, or -1 upon error.
*/
int
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index e792a44..08111d4 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -825,6 +825,7 @@ virFDStreamCreateFile;
virFDStreamOpen;
virFDStreamOpenFile;
virFDStreamOpenPTY;
+virFDStreamSetInternalCloseCb;
# libvirt_internal.h
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
index efbe5ff..b023281 100644
--- a/src/storage/storage_driver.c
+++ b/src/storage/storage_driver.c
@@ -59,6 +59,12 @@ static virStorageDriverStatePtr driverState;
static int storageStateCleanup(void);
+typedef struct _virStorageVolStreamInfo virStorageVolStreamInfo;
+typedef virStorageVolStreamInfo *virStorageVolStreamInfoPtr;
+struct _virStorageVolStreamInfo {
+ char *pool_name;
+};
+
static void storageDriverLock(virStorageDriverStatePtr driver)
{
virMutexLock(&driver->lock);
@@ -1956,6 +1962,78 @@ storageVolDownload(virStorageVolPtr obj,
}
+/**
+ * Frees opaque data.
+ *
+ * @opaque Data to be freed.
+ */
+static void
+virStorageVolPoolRefreshDataFree(void *opaque)
+{
+ virStorageVolStreamInfoPtr cbdata = opaque;
+
+ VIR_FREE(cbdata->pool_name);
+ VIR_FREE(cbdata);
+}
+
+/**
+ * Thread to handle the pool refresh
+ *
+ * @st Pointer to stream being closed.
+ * @opaque Domain's device information structure.
+ */
+static void
+virStorageVolPoolRefreshThread(void *opaque)
+{
+
+ virStorageVolStreamInfoPtr cbdata = opaque;
+ virStoragePoolObjPtr pool = NULL;
+ virStorageBackendPtr backend;
+
+ storageDriverLock(driverState);
+ if (!(pool = virStoragePoolObjFindByName(&driverState->pools,
+ cbdata->pool_name)))
+ goto cleanup;
+
+ if (!(backend = virStorageBackendForType(pool->def->type)))
+ goto cleanup;
+
+ virStoragePoolObjClearVols(pool);
+ if (backend->refreshPool(NULL, pool) < 0)
+ VIR_DEBUG("Failed to refresh storage pool");
+
+ cleanup:
+ if (pool)
+ virStoragePoolObjUnlock(pool);
+ storageDriverUnlock(driverState);
+ virStorageVolPoolRefreshDataFree(cbdata);
+}
+
+/**
+ * Callback being called if a FDstream is closed. Will spin off a thread
+ * to perform a pool refresh.
+ *
+ * @st Pointer to stream being closed.
+ * @opaque Buffer to hold the pool name to be rereshed
+ */
+static void
+virStorageVolFDStreamCloseCb(virStreamPtr st ATTRIBUTE_UNUSED,
+ void *opaque)
+{
+ virThread thread;
+
+ if (virThreadCreate(&thread, false, virStorageVolPoolRefreshThread,
+ opaque) < 0) {
+ /* Not much else can be done */
+ VIR_ERROR(_("Failed to create thread to handle pool refresh"));
+ goto error;
+ }
+ return; /* Thread will free opaque data */
+
+ error:
+ virStorageVolPoolRefreshDataFree(opaque);
+}
+
static int
storageVolUpload(virStorageVolPtr obj,
virStreamPtr stream,
@@ -1966,6 +2044,7 @@ storageVolUpload(virStorageVolPtr obj,
virStorageBackendPtr backend;
virStoragePoolObjPtr pool = NULL;
virStorageVolDefPtr vol = NULL;
+ virStorageVolStreamInfoPtr cbdata = NULL;
int ret = -1;
virCheckFlags(0, -1);
@@ -1996,11 +2075,35 @@ storageVolUpload(virStorageVolPtr obj,
goto cleanup;
}
+ /* If we have a refreshPool, use the callback routine in order to
+ * refresh the pool after the volume upload stream closes. This way
+ * we make sure the volume and pool data are refreshed without user
+ * interaction and we can just lookup the backend in the callback
+ * routine in order to call the refresh API.
+ */
+ if (backend->refreshPool) {
+ if (VIR_ALLOC(cbdata) < 0 ||
+ VIR_STRDUP(cbdata->pool_name, pool->def->name) < 0)
+ goto cleanup;
+ }
+
ret = backend->uploadVol(obj->conn, pool, vol, stream,
offset, length, flags);
+ /* Add cleanup callback - call after uploadVol since the stream
+ * is then fully set up
+ */
+ if (cbdata) {
+ virFDStreamSetInternalCloseCb(stream,
+ virStorageVolFDStreamCloseCb,
+ cbdata, NULL);
+ cbdata = NULL;
+ }
+
cleanup:
virStoragePoolObjUnlock(pool);
+ if (cbdata)
+ virStorageVolPoolRefreshDataFree(cbdata);
return ret;
}
diff --git a/tools/virsh.pod b/tools/virsh.pod
index 849ae31..e377df9 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -2981,6 +2981,9 @@ of the amount of data to be uploaded. A negative value is
interpreted
as an unsigned long long value to essentially include everything from
the offset to the end of the volume.
An error will occur if the I<local-file> is greater than the specified length.
+See the description for the libvirt virStorageVolUpload API for details
+regarding possible target volume and pool changes as a result of the
+pool refresh when the upload is attempted.
=item B<vol-download> [I<--pool> I<pool-or-uuid>] [I<--offset>
I<bytes>]
[I<--length> I<bytes>] I<vol-name-or-key-or-path> I<local-file>
--
1.9.3