drop the pool lock when allocating fs volumes

* 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
daniel
This commit is contained in:
Daniel Veillard 2009-04-17 19:12:37 +00:00
parent 10d1650843
commit 2cd9b2d8ee
6 changed files with 142 additions and 35 deletions

View File

@ -1,3 +1,9 @@
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> Fri Apr 17 18:05:52 CEST 2009 Daniel Veillard <veillard@redhat.com>
* configure.in include/libvirt/virterror.h src/Makefile.am * configure.in include/libvirt/virterror.h src/Makefile.am

View File

@ -46,6 +46,7 @@ virGetDomain;
virGetNetwork; virGetNetwork;
virGetStoragePool; virGetStoragePool;
virGetStorageVol; virGetStorageVol;
virUnrefStorageVol;
virGetNodeDevice; virGetNodeDevice;
virUnrefDomain; virUnrefDomain;
virUnrefConnect; virUnrefConnect;

View File

@ -34,6 +34,7 @@ typedef int (*virStorageBackendRefreshPool)(virConnectPtr conn, virStoragePoolOb
typedef int (*virStorageBackendStopPool)(virConnectPtr conn, virStoragePoolObjPtr pool); typedef int (*virStorageBackendStopPool)(virConnectPtr conn, virStoragePoolObjPtr pool);
typedef int (*virStorageBackendDeletePool)(virConnectPtr conn, virStoragePoolObjPtr pool, unsigned int flags); 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 (*virStorageBackendCreateVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol);
typedef int (*virStorageBackendRefreshVol)(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); typedef int (*virStorageBackendDeleteVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol, unsigned int flags);
@ -52,6 +53,7 @@ struct _virStorageBackend {
virStorageBackendStopPool stopPool; virStorageBackendStopPool stopPool;
virStorageBackendDeletePool deletePool; virStorageBackendDeletePool deletePool;
virStorageBackendBuildVol buildVol;
virStorageBackendCreateVol createVol; virStorageBackendCreateVol createVol;
virStorageBackendRefreshVol refreshVol; virStorageBackendRefreshVol refreshVol;
virStorageBackendDeleteVol deleteVol; virStorageBackendDeleteVol deleteVol;

View File

@ -982,16 +982,16 @@ virStorageBackendFileSystemDelete(virConnectPtr conn,
/** /**
* Allocate a new file as a volume. This is either done directly * Set up a volume definition to be added to a pool's volume list, but
* for raw/sparse files, or by calling qemu-img/qcow-create for * don't do any file creation or allocation. By separating the two processes,
* special kinds of files * 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 static int
virStorageBackendFileSystemVolCreate(virConnectPtr conn, virStorageBackendFileSystemVolCreate(virConnectPtr conn,
virStoragePoolObjPtr pool, virStoragePoolObjPtr pool,
virStorageVolDefPtr vol) virStorageVolDefPtr vol)
{ {
int fd;
if (VIR_ALLOC_N(vol->target.path, strlen(pool->def->target.path) + if (VIR_ALLOC_N(vol->target.path, strlen(pool->def->target.path) +
1 + strlen(vol->name) + 1) < 0) { 1 + strlen(vol->name) + 1) < 0) {
@ -1008,6 +1008,20 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
return -1; 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 (vol->target.format == VIR_STORAGE_VOL_FILE_RAW) {
if ((fd = open(vol->target.path, O_RDWR | O_CREAT | O_EXCL, if ((fd = open(vol->target.path, O_RDWR | O_CREAT | O_EXCL,
vol->target.perms.mode)) < 0) { vol->target.perms.mode)) < 0) {
@ -1017,6 +1031,16 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
return -1; 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 */ /* Pre-allocate any data if requested */
/* XXX slooooooooooooooooow on non-extents-based file systems */ /* XXX slooooooooooooooooow on non-extents-based file systems */
/* FIXME: Add in progress bars & bg thread if progress bar requested */ /* FIXME: Add in progress bars & bg thread if progress bar requested */
@ -1039,7 +1063,6 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
virReportSystemError(conn, r, virReportSystemError(conn, r,
_("cannot fill file '%s'"), _("cannot fill file '%s'"),
vol->target.path); vol->target.path);
unlink(vol->target.path);
close(fd); close(fd);
return -1; return -1;
} }
@ -1052,22 +1075,12 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
virReportSystemError(conn, r, virReportSystemError(conn, r,
_("cannot fill file '%s'"), _("cannot fill file '%s'"),
vol->target.path); vol->target.path);
unlink(vol->target.path);
close(fd); close(fd);
return -1; 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) { } else if (vol->target.format == VIR_STORAGE_VOL_FILE_DIR) {
if (mkdir(vol->target.path, vol->target.perms.mode) < 0) { if (mkdir(vol->target.path, vol->target.perms.mode) < 0) {
virReportSystemError(conn, errno, virReportSystemError(conn, errno,
@ -1127,7 +1140,6 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
snprintf(size, sizeof(size), "%llu", vol->capacity/1024); snprintf(size, sizeof(size), "%llu", vol->capacity/1024);
if (virRun(conn, imgargv, NULL) < 0) { if (virRun(conn, imgargv, NULL) < 0) {
unlink(vol->target.path);
return -1; return -1;
} }
@ -1135,7 +1147,6 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
virReportSystemError(conn, errno, virReportSystemError(conn, errno,
_("cannot read path '%s'"), _("cannot read path '%s'"),
vol->target.path); vol->target.path);
unlink(vol->target.path);
return -1; return -1;
} }
#elif HAVE_QCOW_CREATE #elif HAVE_QCOW_CREATE
@ -1168,7 +1179,6 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
imgargv[3] = NULL; imgargv[3] = NULL;
if (virRun(conn, imgargv, NULL) < 0) { if (virRun(conn, imgargv, NULL) < 0) {
unlink(vol->target.path);
return -1; return -1;
} }
@ -1176,7 +1186,6 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
virReportSystemError(conn, errno, virReportSystemError(conn, errno,
_("cannot read path '%s'"), _("cannot read path '%s'"),
vol->target.path); vol->target.path);
unlink(vol->target.path);
return -1; return -1;
} }
#else #else
@ -1193,7 +1202,6 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
virReportSystemError(conn, errno, virReportSystemError(conn, errno,
_("cannot set file owner '%s'"), _("cannot set file owner '%s'"),
vol->target.path); vol->target.path);
unlink(vol->target.path);
close(fd); close(fd);
return -1; return -1;
} }
@ -1202,7 +1210,6 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
virReportSystemError(conn, errno, virReportSystemError(conn, errno,
_("cannot set file mode '%s'"), _("cannot set file mode '%s'"),
vol->target.path); vol->target.path);
unlink(vol->target.path);
close(fd); close(fd);
return -1; return -1;
} }
@ -1211,7 +1218,6 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
if (virStorageBackendUpdateVolTargetInfoFD(conn, &vol->target, fd, if (virStorageBackendUpdateVolTargetInfoFD(conn, &vol->target, fd,
&vol->allocation, &vol->allocation,
NULL) < 0) { NULL) < 0) {
unlink(vol->target.path);
close(fd); close(fd);
return -1; return -1;
} }
@ -1220,7 +1226,6 @@ virStorageBackendFileSystemVolCreate(virConnectPtr conn,
virReportSystemError(conn, errno, virReportSystemError(conn, errno,
_("cannot close file '%s'"), _("cannot close file '%s'"),
vol->target.path); vol->target.path);
unlink(vol->target.path);
return -1; return -1;
} }
@ -1268,6 +1273,7 @@ virStorageBackend virStorageBackendDirectory = {
.buildPool = virStorageBackendFileSystemBuild, .buildPool = virStorageBackendFileSystemBuild,
.refreshPool = virStorageBackendFileSystemRefresh, .refreshPool = virStorageBackendFileSystemRefresh,
.deletePool = virStorageBackendFileSystemDelete, .deletePool = virStorageBackendFileSystemDelete,
.buildVol = virStorageBackendFileSystemVolBuild,
.createVol = virStorageBackendFileSystemVolCreate, .createVol = virStorageBackendFileSystemVolCreate,
.refreshVol = virStorageBackendFileSystemVolRefresh, .refreshVol = virStorageBackendFileSystemVolRefresh,
.deleteVol = virStorageBackendFileSystemVolDelete, .deleteVol = virStorageBackendFileSystemVolDelete,
@ -1282,6 +1288,7 @@ virStorageBackend virStorageBackendFileSystem = {
.refreshPool = virStorageBackendFileSystemRefresh, .refreshPool = virStorageBackendFileSystemRefresh,
.stopPool = virStorageBackendFileSystemStop, .stopPool = virStorageBackendFileSystemStop,
.deletePool = virStorageBackendFileSystemDelete, .deletePool = virStorageBackendFileSystemDelete,
.buildVol = virStorageBackendFileSystemVolBuild,
.createVol = virStorageBackendFileSystemVolCreate, .createVol = virStorageBackendFileSystemVolCreate,
.refreshVol = virStorageBackendFileSystemVolRefresh, .refreshVol = virStorageBackendFileSystemVolRefresh,
.deleteVol = virStorageBackendFileSystemVolDelete, .deleteVol = virStorageBackendFileSystemVolDelete,
@ -1295,6 +1302,7 @@ virStorageBackend virStorageBackendNetFileSystem = {
.refreshPool = virStorageBackendFileSystemRefresh, .refreshPool = virStorageBackendFileSystemRefresh,
.stopPool = virStorageBackendFileSystemStop, .stopPool = virStorageBackendFileSystemStop,
.deletePool = virStorageBackendFileSystemDelete, .deletePool = virStorageBackendFileSystemDelete,
.buildVol = virStorageBackendFileSystemVolBuild,
.createVol = virStorageBackendFileSystemVolCreate, .createVol = virStorageBackendFileSystemVolCreate,
.refreshVol = virStorageBackendFileSystemVolRefresh, .refreshVol = virStorageBackendFileSystemVolRefresh,
.deleteVol = virStorageBackendFileSystemVolDelete, .deleteVol = virStorageBackendFileSystemVolDelete,

View File

@ -84,6 +84,8 @@ struct _virStorageVolDef {
char *key; char *key;
int type; /* virStorageVolType enum */ int type; /* virStorageVolType enum */
unsigned int building;
unsigned long long allocation; unsigned long long allocation;
unsigned long long capacity; unsigned long long capacity;
@ -238,6 +240,7 @@ struct _virStoragePoolObj {
char *autostartLink; char *autostartLink;
int active; int active;
int autostart; int autostart;
unsigned int asyncjobs;
virStoragePoolDefPtr def; virStoragePoolDefPtr def;
virStoragePoolDefPtr newDef; virStoragePoolDefPtr newDef;

View File

@ -564,6 +564,13 @@ storagePoolUndefine(virStoragePoolPtr obj) {
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 (virStoragePoolObjDeleteDef(obj->conn, pool) < 0) if (virStoragePoolObjDeleteDef(obj->conn, pool) < 0)
goto cleanup; goto cleanup;
@ -696,6 +703,13 @@ storagePoolDestroy(virStoragePoolPtr obj) {
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 && if (backend->stopPool &&
backend->stopPool(obj->conn, pool) < 0) backend->stopPool(obj->conn, pool) < 0)
goto cleanup; goto cleanup;
@ -745,6 +759,13 @@ storagePoolDelete(virStoragePoolPtr obj,
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) { if (!backend->deletePool) {
virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT, virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT,
"%s", _("pool does not support volume delete")); "%s", _("pool does not support volume delete"));
@ -788,6 +809,13 @@ storagePoolRefresh(virStoragePoolPtr obj,
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;
}
virStoragePoolObjClearVols(pool); virStoragePoolObjClearVols(pool);
if (backend->refreshPool(obj->conn, pool) < 0) { if (backend->refreshPool(obj->conn, pool) < 0) {
if (backend->stopPool) if (backend->stopPool)
@ -1161,6 +1189,8 @@ cleanup:
return ret; return ret;
} }
static int storageVolumeDelete(virStorageVolPtr obj, unsigned int flags);
static virStorageVolPtr static virStorageVolPtr
storageVolumeCreateXML(virStoragePoolPtr obj, storageVolumeCreateXML(virStoragePoolPtr obj,
const char *xmldesc, const char *xmldesc,
@ -1168,8 +1198,8 @@ storageVolumeCreateXML(virStoragePoolPtr obj,
virStorageDriverStatePtr driver = obj->conn->storagePrivateData; virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
virStoragePoolObjPtr pool; virStoragePoolObjPtr pool;
virStorageBackendPtr backend; virStorageBackendPtr backend;
virStorageVolDefPtr vol = NULL; virStorageVolDefPtr voldef = NULL;
virStorageVolPtr ret = NULL; virStorageVolPtr ret = NULL, volobj = NULL;
storageDriverLock(driver); storageDriverLock(driver);
pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid); pool = virStoragePoolObjFindByUUID(&driver->pools, obj->uuid);
@ -1190,11 +1220,11 @@ storageVolumeCreateXML(virStoragePoolPtr obj,
if ((backend = virStorageBackendForType(pool->def->type)) == NULL) if ((backend = virStorageBackendForType(pool->def->type)) == NULL)
goto cleanup; goto cleanup;
vol = virStorageVolDefParse(obj->conn, pool->def, xmldesc, NULL); voldef = virStorageVolDefParse(obj->conn, pool->def, xmldesc, NULL);
if (vol == NULL) if (voldef == NULL)
goto cleanup; goto cleanup;
if (virStorageVolDefFindByName(pool, vol->name)) { if (virStorageVolDefFindByName(pool, voldef->name)) {
virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL, virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL,
"%s", _("storage vol already exists")); "%s", _("storage vol already exists"));
goto cleanup; goto cleanup;
@ -1208,22 +1238,72 @@ storageVolumeCreateXML(virStoragePoolPtr obj,
if (!backend->createVol) { if (!backend->createVol) {
virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT, 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; goto cleanup;
} }
/* XXX ought to drop pool lock while creating instance */ if (backend->createVol(obj->conn, pool, voldef) < 0) {
if (backend->createVol(obj->conn, pool, vol) < 0) {
goto cleanup; 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); if (0) {
vol = NULL; 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: cleanup:
virStorageVolDefFree(vol); if (volobj)
virUnrefStorageVol(volobj);
virStorageVolDefFree(voldef);
if (pool) if (pool)
virStoragePoolObjUnlock(pool); virStoragePoolObjUnlock(pool);
return ret; return ret;
@ -1266,6 +1346,13 @@ storageVolumeDelete(virStorageVolPtr obj,
goto cleanup; 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) { if (!backend->deleteVol) {
virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT, virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT,
"%s", _("storage pool does not support vol deletion")); "%s", _("storage pool does not support vol deletion"));