+Fri Apr 17 21:10:28 CEST 2009 Daniel Veillard <veillard@redhat.com>
+
+ * src/libvirt_private.syms src/storage_backend.h
+ src/storage_backend_fs.c src/storage_conf.h src/storage_driver.c:
+ drop the pool lock when allocating fs volumes, patch by Cole Robinson
+
Fri Apr 17 18:05:52 CEST 2009 Daniel Veillard <veillard@redhat.com>
* configure.in include/libvirt/virterror.h src/Makefile.am
virGetNetwork;
virGetStoragePool;
virGetStorageVol;
+virUnrefStorageVol;
virGetNodeDevice;
virUnrefDomain;
virUnrefConnect;
typedef int (*virStorageBackendStopPool)(virConnectPtr conn, virStoragePoolObjPtr pool);
typedef int (*virStorageBackendDeletePool)(virConnectPtr conn, virStoragePoolObjPtr pool, unsigned int flags);
+typedef int (*virStorageBackendBuildVol)(virConnectPtr conn, virStorageVolDefPtr vol);
typedef int (*virStorageBackendCreateVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol);
typedef int (*virStorageBackendRefreshVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol);
typedef int (*virStorageBackendDeleteVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol, unsigned int flags);
virStorageBackendStopPool stopPool;
virStorageBackendDeletePool deletePool;
+ virStorageBackendBuildVol buildVol;
virStorageBackendCreateVol createVol;
virStorageBackendRefreshVol refreshVol;
virStorageBackendDeleteVol deleteVol;
/**
- * Allocate a new file as a volume. This is either done directly
- * for raw/sparse files, or by calling qemu-img/qcow-create for
- * special kinds of files
+ * Set up a volume definition to be added to a pool's volume list, but
+ * don't do any file creation or allocation. By separating the two processes,
+ * we allow allocation progress reporting (by polling the volume's 'info'
+ * function), and can drop the parent pool lock during the (slow) allocation.
*/
static int
virStorageBackendFileSystemVolCreate(virConnectPtr conn,
virStoragePoolObjPtr pool,
virStorageVolDefPtr vol)
{
- int fd;
if (VIR_ALLOC_N(vol->target.path, strlen(pool->def->target.path) +
1 + strlen(vol->name) + 1) < 0) {
return -1;
}
+ return 0;
+}
+
+/**
+ * Allocate a new file as a volume. This is either done directly
+ * for raw/sparse files, or by calling qemu-img/qcow-create for
+ * special kinds of files
+ */
+static int
+virStorageBackendFileSystemVolBuild(virConnectPtr conn,
+ virStorageVolDefPtr vol)
+{
+ int fd;
+
if (vol->target.format == VIR_STORAGE_VOL_FILE_RAW) {
if ((fd = open(vol->target.path, O_RDWR | O_CREAT | O_EXCL,
vol->target.perms.mode)) < 0) {
return -1;
}
+ /* Seek to the final size, so the capacity is available upfront
+ * for progress reporting */
+ if (ftruncate(fd, vol->capacity) < 0) {
+ virReportSystemError(conn, errno,
+ _("cannot extend file '%s'"),
+ vol->target.path);
+ close(fd);
+ return -1;
+ }
+
/* Pre-allocate any data if requested */
/* XXX slooooooooooooooooow on non-extents-based file systems */
/* FIXME: Add in progress bars & bg thread if progress bar requested */
virReportSystemError(conn, r,
_("cannot fill file '%s'"),
vol->target.path);
- unlink(vol->target.path);
close(fd);
return -1;
}
virReportSystemError(conn, r,
_("cannot fill file '%s'"),
vol->target.path);
- unlink(vol->target.path);
close(fd);
return -1;
}
}
}
- /* Now seek to final size, possibly making the file sparse */
- if (ftruncate(fd, vol->capacity) < 0) {
- virReportSystemError(conn, errno,
- _("cannot extend file '%s'"),
- vol->target.path);
- unlink(vol->target.path);
- close(fd);
- return -1;
- }
} else if (vol->target.format == VIR_STORAGE_VOL_FILE_DIR) {
if (mkdir(vol->target.path, vol->target.perms.mode) < 0) {
virReportSystemError(conn, errno,
snprintf(size, sizeof(size), "%llu", vol->capacity/1024);
if (virRun(conn, imgargv, NULL) < 0) {
- unlink(vol->target.path);
return -1;
}
virReportSystemError(conn, errno,
_("cannot read path '%s'"),
vol->target.path);
- unlink(vol->target.path);
return -1;
}
#elif HAVE_QCOW_CREATE
imgargv[3] = NULL;
if (virRun(conn, imgargv, NULL) < 0) {
- unlink(vol->target.path);
return -1;
}
virReportSystemError(conn, errno,
_("cannot read path '%s'"),
vol->target.path);
- unlink(vol->target.path);
return -1;
}
#else
virReportSystemError(conn, errno,
_("cannot set file owner '%s'"),
vol->target.path);
- unlink(vol->target.path);
close(fd);
return -1;
}
virReportSystemError(conn, errno,
_("cannot set file mode '%s'"),
vol->target.path);
- unlink(vol->target.path);
close(fd);
return -1;
}
if (virStorageBackendUpdateVolTargetInfoFD(conn, &vol->target, fd,
&vol->allocation,
NULL) < 0) {
- unlink(vol->target.path);
close(fd);
return -1;
}
virReportSystemError(conn, errno,
_("cannot close file '%s'"),
vol->target.path);
- unlink(vol->target.path);
return -1;
}
.buildPool = virStorageBackendFileSystemBuild,
.refreshPool = virStorageBackendFileSystemRefresh,
.deletePool = virStorageBackendFileSystemDelete,
+ .buildVol = virStorageBackendFileSystemVolBuild,
.createVol = virStorageBackendFileSystemVolCreate,
.refreshVol = virStorageBackendFileSystemVolRefresh,
.deleteVol = virStorageBackendFileSystemVolDelete,
.refreshPool = virStorageBackendFileSystemRefresh,
.stopPool = virStorageBackendFileSystemStop,
.deletePool = virStorageBackendFileSystemDelete,
+ .buildVol = virStorageBackendFileSystemVolBuild,
.createVol = virStorageBackendFileSystemVolCreate,
.refreshVol = virStorageBackendFileSystemVolRefresh,
.deleteVol = virStorageBackendFileSystemVolDelete,
.refreshPool = virStorageBackendFileSystemRefresh,
.stopPool = virStorageBackendFileSystemStop,
.deletePool = virStorageBackendFileSystemDelete,
+ .buildVol = virStorageBackendFileSystemVolBuild,
.createVol = virStorageBackendFileSystemVolCreate,
.refreshVol = virStorageBackendFileSystemVolRefresh,
.deleteVol = virStorageBackendFileSystemVolDelete,
char *key;
int type; /* virStorageVolType enum */
+ unsigned int building;
+
unsigned long long allocation;
unsigned long long capacity;
char *autostartLink;
int active;
int autostart;
+ unsigned int asyncjobs;
virStoragePoolDefPtr def;
virStoragePoolDefPtr newDef;
goto cleanup;
}
+ if (pool->asyncjobs > 0) {
+ virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR,
+ _("pool '%s' has asynchronous jobs running."),
+ pool->def->name);
+ goto cleanup;
+ }
+
if (virStoragePoolObjDeleteDef(obj->conn, pool) < 0)
goto cleanup;
goto cleanup;
}
+ if (pool->asyncjobs > 0) {
+ virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR,
+ _("pool '%s' has asynchronous jobs running."),
+ pool->def->name);
+ goto cleanup;
+ }
+
if (backend->stopPool &&
backend->stopPool(obj->conn, pool) < 0)
goto cleanup;
goto cleanup;
}
+ if (pool->asyncjobs > 0) {
+ virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR,
+ _("pool '%s' has asynchronous jobs running."),
+ pool->def->name);
+ goto cleanup;
+ }
+
if (!backend->deletePool) {
virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT,
"%s", _("pool does not support volume delete"));
goto cleanup;
}
+ if (pool->asyncjobs > 0) {
+ virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR,
+ _("pool '%s' has asynchronous jobs running."),
+ pool->def->name);
+ goto cleanup;
+ }
+
virStoragePoolObjClearVols(pool);
if (backend->refreshPool(obj->conn, pool) < 0) {
if (backend->stopPool)
return ret;
}
+static int storageVolumeDelete(virStorageVolPtr obj, unsigned int flags);
+
static virStorageVolPtr
storageVolumeCreateXML(virStoragePoolPtr obj,
const char *xmldesc,
virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
virStoragePoolObjPtr pool;
virStorageBackendPtr backend;
- virStorageVolDefPtr vol = NULL;
- virStorageVolPtr ret = NULL;
+ virStorageVolDefPtr voldef = NULL;
+ virStorageVolPtr ret = NULL, volobj = NULL;
storageDriverLock(driver);
pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid);
if ((backend = virStorageBackendForType(pool->def->type)) == NULL)
goto cleanup;
- vol = virStorageVolDefParse(obj->conn, pool->def, xmldesc, NULL);
- if (vol == NULL)
+ voldef = virStorageVolDefParse(obj->conn, pool->def, xmldesc, NULL);
+ if (voldef == NULL)
goto cleanup;
- if (virStorageVolDefFindByName(pool, vol->name)) {
+ if (virStorageVolDefFindByName(pool, voldef->name)) {
virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL,
"%s", _("storage vol already exists"));
goto cleanup;
if (!backend->createVol) {
virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT,
- "%s", _("storage pool does not support volume creation"));
+ "%s", _("storage pool does not support volume "
+ "creation"));
goto cleanup;
}
- /* XXX ought to drop pool lock while creating instance */
- if (backend->createVol(obj->conn, pool, vol) < 0) {
+ if (backend->createVol(obj->conn, pool, voldef) < 0) {
goto cleanup;
}
- pool->volumes.objs[pool->volumes.count++] = vol;
+ pool->volumes.objs[pool->volumes.count++] = voldef;
+ volobj = virGetStorageVol(obj->conn, pool->def->name, voldef->name,
+ voldef->key);
- ret = virGetStorageVol(obj->conn, pool->def->name, vol->name, vol->key);
- vol = NULL;
+ if (0) {
+ printf("after vol lookup.\n");
+ virReportOOMError(obj->conn);
+ goto cleanup;
+ }
+
+ if (volobj && backend->buildVol) {
+ int buildret;
+ virStorageVolDefPtr buildvoldef = NULL;
+
+ if (VIR_ALLOC(buildvoldef) < 0) {
+ virReportOOMError(obj->conn);
+ 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 */
+ pool->asyncjobs++;
+ voldef->building = 1;
+ virStoragePoolObjUnlock(pool);
+
+ buildret = backend->buildVol(obj->conn, buildvoldef);
+
+ virStoragePoolObjLock(pool);
+ voldef->building = 0;
+ pool->asyncjobs--;
+
+ voldef = NULL;
+ VIR_FREE(buildvoldef);
+
+ if (buildret < 0) {
+ virStoragePoolObjUnlock(pool);
+ storageVolumeDelete(volobj, 0);
+ pool = NULL;
+ goto cleanup;
+ }
+
+ }
+
+ ret = volobj;
+ volobj = NULL;
+ voldef = NULL;
cleanup:
- virStorageVolDefFree(vol);
+ if (volobj)
+ virUnrefStorageVol(volobj);
+ virStorageVolDefFree(voldef);
if (pool)
virStoragePoolObjUnlock(pool);
return ret;
goto cleanup;
}
+ if (vol->building) {
+ virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR,
+ _("volume '%s' is still being allocated."),
+ vol->name);
+ goto cleanup;
+ }
+
if (!backend->deleteVol) {
virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT,
"%s", _("storage pool does not support vol deletion"));