storage: Refresh storage pool after upload

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.
This commit is contained in:
John Ferlan 2014-07-28 08:39:09 -04:00
parent e3e1e52ace
commit 4a85bf3e2f
4 changed files with 115 additions and 0 deletions

View File

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

View File

@ -825,6 +825,7 @@ virFDStreamCreateFile;
virFDStreamOpen;
virFDStreamOpenFile;
virFDStreamOpenPTY;
virFDStreamSetInternalCloseCb;
# libvirt_internal.h

View File

@ -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;
}

View File

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