This implies breaking up some jobs into cycles during which
we check for job abortion request. The virStorageVolAbortJob
API will then just set request and wait until job is released.
If a job was, however, interrupted it should fail with
VIR_ERR_OPERATION_ABORTED error.
---
src/conf/storage_conf.h | 7 ++
src/libvirt_private.syms | 3 +
src/storage/storage_backend.c | 53 +++++++++-------
src/storage/storage_backend_fs.c | 8 ++-
src/storage/storage_driver.c | 128 ++++++++++++++++++++++++++++++++-----
src/storage/storage_driver.h | 7 ++
6 files changed, 162 insertions(+), 44 deletions(-)
diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h
index 481c806..ee25e34 100644
--- a/src/conf/storage_conf.h
+++ b/src/conf/storage_conf.h
@@ -28,6 +28,7 @@
# include "util.h"
# include "storage_encryption_conf.h"
# include "threads.h"
+# include "command.h"
# include <libxml/tree.h>
@@ -100,6 +101,12 @@ struct virStorageVolJobObj {
virCond cond;
enum virStorageVolJob active;
unsigned long long start;
+
+ bool abort_requested; /* abort was requested */
+ virCommandPtr cmd; /* if we are running external program,
+ store pointer here too, so we can
+ virCommandAbort it if necessary */
+ virStreamPtr stream; /* stream to abort */
};
typedef struct _virStorageVolDef virStorageVolDef;
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index fef9d5a..cec7a94 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1017,6 +1017,9 @@ virStorageVolDefParseFile;
virStorageVolDefParseNode;
virStorageVolDefParseString;
+# storage_driver.h
+virStorageVolJobSetCommand;
+virStorageVolJobSetStream;
# storage_encryption_conf.h
virStorageEncryptionFormat;
diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c
index caac2f8..2e82c03 100644
--- a/src/storage/storage_backend.c
+++ b/src/storage/storage_backend.c
@@ -53,6 +53,7 @@
#include "internal.h"
#include "secret_conf.h"
#include "uuid.h"
+#include "storage_driver.h"
#include "storage_file.h"
#include "storage_backend.h"
#include "logging.h"
@@ -106,8 +107,6 @@ static virStorageBackendPtr backends[] = {
NULL
};
-static int track_allocation_progress = 0;
-
enum {
TOOL_QEMU_IMG,
TOOL_KVM_IMG,
@@ -202,6 +201,15 @@ virStorageBackendCopyToFD(virStorageVolDefPtr vol,
goto cleanup;
}
+
+ if (vol->job.abort_requested ||
+ inputvol->job.abort_requested) {
+ virStorageReportError(VIR_ERR_OPERATION_ABORTED, "%s",
+ _("Job abort requested"));
+ ret = -1;
+ goto cleanup;
+ }
+
} while ((amtleft -= interval) > 0);
}
@@ -325,34 +333,31 @@ createRawFile(int fd, virStorageVolDefPtr vol,
}
if (remain) {
- if (track_allocation_progress) {
+ while (remain) {
+ /* Allocate in chunks of 512MiB: big-enough chunk
+ * size and takes approx. 9s on ext3. A progress
+ * update every 9s is a fair-enough trade-off
+ */
+ unsigned long long bytes = 512 * 1024 * 1024;
- while (remain) {
- /* Allocate in chunks of 512MiB: big-enough chunk
- * size and takes approx. 9s on ext3. A progress
- * update every 9s is a fair-enough trade-off
- */
- unsigned long long bytes = 512 * 1024 * 1024;
-
- if (bytes > remain)
- bytes = remain;
- if (safezero(fd, vol->allocation - remain, bytes) < 0) {
- ret = -errno;
- virReportSystemError(errno, _("cannot fill file
'%s'"),
- vol->target.path);
- goto cleanup;
- }
- remain -= bytes;
- }
- } else { /* No progress bars to be shown */
- if (safezero(fd, 0, remain) < 0) {
+ if (bytes > remain)
+ bytes = remain;
+ if (safezero(fd, vol->allocation - remain, bytes) < 0) {
ret = -errno;
virReportSystemError(errno, _("cannot fill file
'%s'"),
vol->target.path);
goto cleanup;
}
- }
+ remain -= bytes;
+ if (vol->job.abort_requested) {
+ virStorageReportError(VIR_ERR_OPERATION_ABORTED,
+ "%s",
+ _("Job abort requested"));
+ ret = -1;
+ goto cleanup;
+ }
+ }
}
if (fsync(fd) < 0) {
@@ -782,6 +787,7 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
goto cleanup;
cmd = virCommandNew(create_tool);
+ virStorageVolJobSetCommand(vol, cmd);
if (inputvol) {
virCommandAddArgList(cmd, "convert", "-f", inputType,
"-O", type,
@@ -898,6 +904,7 @@ virStorageBackendCreateQcowCreate(virConnectPtr conn
ATTRIBUTE_UNUSED,
}
cmd = virCommandNewArgList("qcow-create", size, vol->target.path,
NULL);
+ virStorageVolJobSetCommand(vol, cmd);
ret = virStorageBackendCreateExecCommand(pool, vol, cmd);
virCommandFree(cmd);
diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c
index 1af12e6..2d16be5 100644
--- a/src/storage/storage_backend_fs.c
+++ b/src/storage/storage_backend_fs.c
@@ -44,6 +44,7 @@
#include "virterror_internal.h"
#include "storage_backend_fs.h"
#include "storage_conf.h"
+#include "storage_driver.h"
#include "storage_file.h"
#include "command.h"
#include "memory.h"
@@ -1188,7 +1189,7 @@ virStorageBackendFileSystemVolRefresh(virConnectPtr conn,
}
static int
-virStorageBackendFilesystemResizeQemuImg(const char *path,
+virStorageBackendFilesystemResizeQemuImg(virStorageVolDefPtr vol,
unsigned long long capacity)
{
int ret = -1;
@@ -1207,7 +1208,8 @@ virStorageBackendFilesystemResizeQemuImg(const char *path,
}
cmd = virCommandNew(img_tool);
- virCommandAddArgList(cmd, "resize", path, NULL);
+ virStorageVolJobSetCommand(vol, cmd);
+ virCommandAddArgList(cmd, "resize", vol->target.path, NULL);
virCommandAddArgFormat(cmd, "%llu", capacity);
ret = virCommandRun(cmd, NULL);
@@ -1233,7 +1235,7 @@ virStorageBackendFileSystemVolResize(virConnectPtr conn
ATTRIBUTE_UNUSED,
if (vol->target.format == VIR_STORAGE_FILE_RAW)
return virStorageFileResize(vol->target.path, capacity);
else
- return virStorageBackendFilesystemResizeQemuImg(vol->target.path,
+ return virStorageBackendFilesystemResizeQemuImg(vol,
capacity);
}
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
index 7f3dfcd..7e11c74 100644
--- a/src/storage/storage_driver.c
+++ b/src/storage/storage_driver.c
@@ -72,6 +72,9 @@ storageResetJob(virStorageVolDefPtr vol)
job->active = VIR_STORAGE_VOL_JOB_NONE;
job->start = 0;
+ job->abort_requested = false;
+ job->cmd = NULL;
+ job->stream = NULL;
}
/* wait max. 30 seconds for job ackquire */
@@ -92,11 +95,17 @@ storageBeginJobInternal(virStorageDriverStatePtr driver
ATTRIBUTE_UNUSED,
then = now + STORAGE_JOB_WAIT_TIME;
- while (vol->job.active != VIR_STORAGE_VOL_JOB_NONE) {
+ while (vol->job.active != VIR_STORAGE_VOL_JOB_NONE &&
+ !vol->job.abort_requested) {
if (virCondWaitUntil(&vol->job.cond, &vol->lock, then) < 0)
goto error;
}
+ if (vol->job.abort_requested) {
+ VIR_DEBUG("JobAbort requested. Not starting a job");
+ return -1;
+ }
+
VIR_DEBUG("Starting job %d", job);
storageResetJob(vol);
vol->job.active = job;
@@ -170,6 +179,24 @@ storageEndJobWithPool(virStorageDriverStatePtr driver,
return storageEndJobInternal(driver, pool, true, vol);
}
+void virStorageVolJobSetCommand(virStorageVolDefPtr vol,
+ virCommandPtr cmd)
+{
+ if (!vol || !cmd)
+ return;
+
+ vol->job.cmd = cmd;
+}
+
+void virStorageVolJobSetStream(virStorageVolDefPtr vol,
+ virStreamPtr stream)
+{
+ if (!vol || !stream)
+ return;
+
+ vol->job.stream = stream;
+}
+
static void
storageDriverAutostart(virStorageDriverStatePtr driver) {
unsigned int i;
@@ -1456,35 +1483,19 @@ storageVolumeCreateXML(virStoragePoolPtr obj,
if (backend->buildVol) {
int buildret;
- virStorageVolDefPtr buildvoldef = NULL;
-
- if (VIR_ALLOC(buildvoldef) < 0) {
- virReportOOMError();
- voldef = NULL;
- goto cleanup;
- }
-
- /* Make a shallow copy of the 'defined' volume definition, since the
- * original allocation value will change as the user polls 'info',
- * but we only need the initial requested values
- */
- memcpy(buildvoldef, voldef, sizeof(*voldef));
/* Drop the pool lock during volume allocation */
if (storageBeginJobWithPool(driver, pool, voldef,
VIR_STORAGE_VOL_JOB_BUILD) < 0) {
- VIR_FREE(buildvoldef);
goto cleanup;
}
- buildret = backend->buildVol(obj->conn, pool, buildvoldef);
+ buildret = backend->buildVol(obj->conn, pool, voldef);
storageEndJobWithPool(driver, pool, voldef);
voldef = NULL;
- VIR_FREE(buildvoldef);
-
if (buildret < 0) {
virStoragePoolObjUnlock(pool);
storageVolumeDelete(volobj, 0);
@@ -1727,6 +1738,8 @@ storageVolumeDownload(virStorageVolPtr obj,
if (storageBeginJob(driver, vol, VIR_STORAGE_VOL_JOB_DOWNLOAD) < 0)
goto out;
+ virStorageVolJobSetStream(vol, stream);
+
if (virFDStreamOpenFile(stream,
vol->target.path,
offset, length,
@@ -1803,6 +1816,8 @@ storageVolumeUpload(virStorageVolPtr obj,
if (storageBeginJob(driver, vol, VIR_STORAGE_VOL_JOB_DOWNLOAD) < 0)
goto out;
+ virStorageVolJobSetStream(vol, stream);
+
/* Not using O_CREAT because the file is required to
* already exist at this point */
if (virFDStreamOpenFile(stream,
@@ -2007,6 +2022,13 @@ storageWipeExtent(virStorageVolDefPtr vol,
*bytes_wiped += written;
remaining -= written;
+
+ if (vol->job.abort_requested) {
+ virStorageReportError(VIR_ERR_OPERATION_ABORTED, "%s",
+ _("Job abort requested"));
+ ret = -1;
+ goto out;
+ }
}
if (fdatasync(fd) < 0) {
@@ -2088,6 +2110,7 @@ storageVolumeWipeInternal(virStorageVolDefPtr def,
algorithm);
}
cmd = virCommandNew(SCRUB);
+ virStorageVolJobSetCommand(def, cmd);
virCommandAddArgList(cmd, "-f", "-p", alg_char,
def->target.path, NULL);
@@ -2426,6 +2449,74 @@ cleanup:
return ret;
}
+static int
+storageVolAbortJob(virStorageVolPtr obj,
+ unsigned int flags)
+{
+ virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
+ virStoragePoolObjPtr pool;
+ virStorageVolDefPtr vol;
+ int ret = -1;
+
+ virCheckFlags(0, -1);
+
+ storageDriverLock(driver);
+ pool = virStoragePoolObjFindByName(&driver->pools, obj->pool);
+ storageDriverUnlock(driver);
+ if (!pool) {
+ virStorageReportError(VIR_ERR_NO_STORAGE_POOL,
+ "%s", _("no storage pool with matching
uuid"));
+ goto cleanup;
+ }
+
+ if (!virStoragePoolObjIsActive(pool)) {
+ virStorageReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("storage pool is not
active"));
+ goto cleanup;
+ }
+
+ vol = virStorageVolDefFindByName(pool, obj->name);
+
+ if (!vol) {
+ virStorageReportError(VIR_ERR_NO_STORAGE_VOL,
+ _("no storage vol with matching name
'%s'"),
+ obj->name);
+ goto cleanup;
+ }
+
+ if (vol->job.active == VIR_STORAGE_VOL_JOB_NONE) {
+ virStorageReportError(VIR_ERR_OPERATION_INVALID,
+ _("there is no job running on '%s'"),
+ obj->name);
+ goto cleanup;
+ }
+
+ vol->job.abort_requested = true;
+ virStoragePoolObjUnlock(pool);
+
+ /* If we use external program kill it */
+ virCommandAbort(vol->job.cmd);
+ if (vol->job.stream)
+ virStreamAbort(vol->job.stream);
+
+ while (vol->job.active) {
+ if (virCondWait(&vol->job.cond, &vol->lock) < 0) {
+ virReportSystemError(errno, "%s",
+ _("unable to wait on condition"));
+ goto cleanup;
+ }
+ }
+
+ virStoragePoolObjLock(pool);
+
+ ret = 0;
+
+cleanup:
+ if (pool)
+ virStoragePoolObjUnlock(pool);
+ return ret;
+}
+
static virStorageDriver storageDriver = {
.name = "storage",
.open = storageOpen, /* 0.4.0 */
@@ -2467,6 +2558,7 @@ static virStorageDriver storageDriver = {
.volGetXMLDesc = storageVolumeGetXMLDesc, /* 0.4.0 */
.volGetPath = storageVolumeGetPath, /* 0.4.0 */
.volResize = storageVolumeResize, /* 0.9.10 */
+ .volAbortJob = storageVolAbortJob, /* 0.9.11 */
.poolIsActive = storagePoolIsActive, /* 0.7.3 */
.poolIsPersistent = storagePoolIsPersistent, /* 0.7.3 */
diff --git a/src/storage/storage_driver.h b/src/storage/storage_driver.h
index b3e2554..2220c90 100644
--- a/src/storage/storage_driver.h
+++ b/src/storage/storage_driver.h
@@ -25,7 +25,14 @@
# define __VIR_STORAGE_DRIVER_H__
# include "storage_conf.h"
+# include "command.h"
+# include "fdstream.h"
int storageRegister(void);
+void virStorageVolJobSetCommand(virStorageVolDefPtr vol,
+ virCommandPtr cmd);
+void virStorageVolJobSetStream(virStorageVolDefPtr vol,
+ virStreamPtr stream);
+
#endif /* __VIR_STORAGE_DRIVER_H__ */
--
1.7.8.5