mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-25 15:15:25 +00:00
qemu: refactor blockinfo data gathering
Create a helper function that can be reused for gathering block info from virDomainListGetStats. * src/qemu/qemu_driver.c (qemuDomainGetBlockInfo): Split guts... (qemuStorageLimitsRefresh): ...into new helper function. Signed-off-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
parent
0282ca45a0
commit
8de6544e98
@ -11003,6 +11003,149 @@ qemuDomainMemoryPeek(virDomainPtr dom,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Refresh the capacity and allocation limits of a given storage
|
||||||
|
* source. Assumes that the caller has already obtained a domain
|
||||||
|
* job. */
|
||||||
|
static int
|
||||||
|
qemuStorageLimitsRefresh(virQEMUDriverPtr driver,
|
||||||
|
virQEMUDriverConfigPtr cfg,
|
||||||
|
virDomainObjPtr vm,
|
||||||
|
virStorageSourcePtr src)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
int fd = -1;
|
||||||
|
off_t end;
|
||||||
|
virStorageSourcePtr meta = NULL;
|
||||||
|
struct stat sb;
|
||||||
|
int format;
|
||||||
|
char *buf = NULL;
|
||||||
|
ssize_t len;
|
||||||
|
|
||||||
|
/* FIXME: For an offline domain, we always want to check current
|
||||||
|
* on-disk statistics (as users have been known to change offline
|
||||||
|
* images behind our backs). For a running domain, however, it
|
||||||
|
* would be nice to avoid opening a file (particularly since
|
||||||
|
* reading a file while qemu is writing it risks the reader seeing
|
||||||
|
* bogus data), or even avoid a stat, if the information
|
||||||
|
* remembered from the previous run is still viable.
|
||||||
|
*
|
||||||
|
* For read-only disks, nothing should be changing unless the user
|
||||||
|
* has requested a block-commit action. For read-write disks, we
|
||||||
|
* know some special cases: capacity should not change without a
|
||||||
|
* block-resize (where capacity is the only stat that requires
|
||||||
|
* reading a file, and even then, only for non-raw files); and
|
||||||
|
* physical size of a raw image or of a block device should
|
||||||
|
* likewise not be changing without block-resize. On the other
|
||||||
|
* hand, allocation of a raw file can change (if the file is
|
||||||
|
* sparse, but the amount of sparseness changes due to writes or
|
||||||
|
* punching holes), and physical size of a non-raw file can
|
||||||
|
* change.
|
||||||
|
*/
|
||||||
|
if (virStorageSourceIsLocalStorage(src)) {
|
||||||
|
if ((fd = qemuOpenFile(driver, vm, src->path, O_RDONLY,
|
||||||
|
NULL, NULL)) == -1)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (fstat(fd, &sb) < 0) {
|
||||||
|
virReportSystemError(errno,
|
||||||
|
_("cannot stat file '%s'"), src->path);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((len = virFileReadHeaderFD(fd, VIR_STORAGE_MAX_HEADER, &buf)) < 0) {
|
||||||
|
virReportSystemError(errno, _("cannot read header '%s'"),
|
||||||
|
src->path);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (virStorageFileInitAs(src, cfg->user, cfg->group) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if ((len = virStorageFileReadHeader(src, VIR_STORAGE_MAX_HEADER,
|
||||||
|
&buf)) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (virStorageFileStat(src, &sb) < 0) {
|
||||||
|
virReportSystemError(errno, _("failed to stat remote file '%s'"),
|
||||||
|
NULLSTR(src->path));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get info for normal formats */
|
||||||
|
if (S_ISREG(sb.st_mode) || fd == -1) {
|
||||||
|
#ifndef WIN32
|
||||||
|
src->allocation = (unsigned long long)sb.st_blocks *
|
||||||
|
(unsigned long long)DEV_BSIZE;
|
||||||
|
#else
|
||||||
|
src->allocation = sb.st_size;
|
||||||
|
#endif
|
||||||
|
/* Allocation tracks when the file is sparse, physical is the
|
||||||
|
* last offset of the file. */
|
||||||
|
src->physical = sb.st_size;
|
||||||
|
} else {
|
||||||
|
/* NB. Because we configure with AC_SYS_LARGEFILE, off_t
|
||||||
|
* should be 64 bits on all platforms. For block devices, we
|
||||||
|
* have to seek (safe even if someone else is writing) to
|
||||||
|
* determine physical size, and assume that allocation is the
|
||||||
|
* same as physical (but can refine that assumption later if
|
||||||
|
* qemu is still running).
|
||||||
|
*/
|
||||||
|
end = lseek(fd, 0, SEEK_END);
|
||||||
|
if (end == (off_t)-1) {
|
||||||
|
virReportSystemError(errno,
|
||||||
|
_("failed to seek to end of %s"), src->path);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
src->physical = end;
|
||||||
|
src->allocation = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Raw files: capacity is physical size. For all other files: if
|
||||||
|
* the metadata has a capacity, use that, otherwise fall back to
|
||||||
|
* physical size. */
|
||||||
|
if (!(format = src->format)) {
|
||||||
|
if (!cfg->allowDiskFormatProbing) {
|
||||||
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("no disk format for %s and probing is disabled"),
|
||||||
|
src->path);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((format = virStorageFileProbeFormatFromBuf(src->path,
|
||||||
|
buf, len)) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (!(meta = virStorageFileGetMetadataFromBuf(src->path, buf, len,
|
||||||
|
format, NULL)))
|
||||||
|
goto cleanup;
|
||||||
|
if (format == VIR_STORAGE_FILE_RAW)
|
||||||
|
src->capacity = src->physical;
|
||||||
|
else if ((meta = virStorageFileGetMetadataFromBuf(src->path, buf,
|
||||||
|
len, format, NULL)))
|
||||||
|
src->capacity = meta->capacity ? meta->capacity : src->physical;
|
||||||
|
else
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
/* If guest is not using raw disk format and is on a host block
|
||||||
|
* device, then leave the value unspecified, so caller knows to
|
||||||
|
* query the highest allocated extent from QEMU
|
||||||
|
*/
|
||||||
|
if (virStorageSourceGetActualType(src) == VIR_STORAGE_TYPE_BLOCK &&
|
||||||
|
format != VIR_STORAGE_FILE_RAW &&
|
||||||
|
S_ISBLK(sb.st_mode))
|
||||||
|
src->allocation = 0;
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
cleanup:
|
||||||
|
VIR_FREE(buf);
|
||||||
|
virStorageSourceFree(meta);
|
||||||
|
VIR_FORCE_CLOSE(fd);
|
||||||
|
virStorageFileDeinit(src);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
qemuDomainGetBlockInfo(virDomainPtr dom,
|
qemuDomainGetBlockInfo(virDomainPtr dom,
|
||||||
const char *path,
|
const char *path,
|
||||||
@ -11012,18 +11155,11 @@ qemuDomainGetBlockInfo(virDomainPtr dom,
|
|||||||
virQEMUDriverPtr driver = dom->conn->privateData;
|
virQEMUDriverPtr driver = dom->conn->privateData;
|
||||||
virDomainObjPtr vm;
|
virDomainObjPtr vm;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
int fd = -1;
|
|
||||||
off_t end;
|
|
||||||
virStorageSourcePtr meta = NULL;
|
|
||||||
virDomainDiskDefPtr disk = NULL;
|
virDomainDiskDefPtr disk = NULL;
|
||||||
virStorageSourcePtr src;
|
virStorageSourcePtr src;
|
||||||
struct stat sb;
|
|
||||||
int idx;
|
int idx;
|
||||||
int format;
|
|
||||||
bool activeFail = false;
|
bool activeFail = false;
|
||||||
virQEMUDriverConfigPtr cfg = NULL;
|
virQEMUDriverConfigPtr cfg = NULL;
|
||||||
char *buf = NULL;
|
|
||||||
ssize_t len;
|
|
||||||
|
|
||||||
virCheckFlags(0, -1);
|
virCheckFlags(0, -1);
|
||||||
|
|
||||||
@ -11064,118 +11200,10 @@ qemuDomainGetBlockInfo(virDomainPtr dom,
|
|||||||
goto endjob;
|
goto endjob;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: For an offline domain, we always want to check current
|
if ((ret = qemuStorageLimitsRefresh(driver, cfg, vm, src)) < 0)
|
||||||
* on-disk statistics (as users have been known to change offline
|
|
||||||
* images behind our backs). For a running domain, however, it
|
|
||||||
* would be nice to avoid opening a file (particularly since
|
|
||||||
* reading a file while qemu is writing it risks the reader seeing
|
|
||||||
* bogus data), or even avoid a stat, if the information
|
|
||||||
* remembered from the previous run is still viable.
|
|
||||||
*
|
|
||||||
* For read-only disks, nothing should be changing unless the user
|
|
||||||
* has requested a block-commit action. For read-write disks, we
|
|
||||||
* know some special cases: capacity should not change without a
|
|
||||||
* block-resize (where capacity is the only stat that requires
|
|
||||||
* reading a file, and even then, only for non-raw files); and
|
|
||||||
* physical size of a raw image or of a block device should
|
|
||||||
* likewise not be changing without block-resize. On the other
|
|
||||||
* hand, allocation of a raw file can change (if the file is
|
|
||||||
* sparse, but the amount of sparseness changes due to writes or
|
|
||||||
* punching holes), and physical size of a non-raw file can
|
|
||||||
* change.
|
|
||||||
*/
|
|
||||||
if (virStorageSourceIsLocalStorage(src)) {
|
|
||||||
if ((fd = qemuOpenFile(driver, vm, src->path, O_RDONLY,
|
|
||||||
NULL, NULL)) == -1)
|
|
||||||
goto endjob;
|
|
||||||
|
|
||||||
if (fstat(fd, &sb) < 0) {
|
|
||||||
virReportSystemError(errno,
|
|
||||||
_("cannot stat file '%s'"), src->path);
|
|
||||||
goto endjob;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((len = virFileReadHeaderFD(fd, VIR_STORAGE_MAX_HEADER, &buf)) < 0) {
|
|
||||||
virReportSystemError(errno, _("cannot read header '%s'"),
|
|
||||||
src->path);
|
|
||||||
goto endjob;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (virStorageFileInitAs(src, cfg->user, cfg->group) < 0)
|
|
||||||
goto endjob;
|
|
||||||
|
|
||||||
if ((len = virStorageFileReadHeader(src, VIR_STORAGE_MAX_HEADER,
|
|
||||||
&buf)) < 0)
|
|
||||||
goto endjob;
|
|
||||||
|
|
||||||
if (virStorageFileStat(src, &sb) < 0) {
|
|
||||||
virReportSystemError(errno, _("failed to stat remote file '%s'"),
|
|
||||||
NULLSTR(src->path));
|
|
||||||
goto endjob;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get info for normal formats */
|
|
||||||
if (S_ISREG(sb.st_mode) || fd == -1) {
|
|
||||||
#ifndef WIN32
|
|
||||||
src->allocation = (unsigned long long)sb.st_blocks *
|
|
||||||
(unsigned long long)DEV_BSIZE;
|
|
||||||
#else
|
|
||||||
src->allocation = sb.st_size;
|
|
||||||
#endif
|
|
||||||
/* Allocation tracks when the file is sparse, physical is the
|
|
||||||
* last offset of the file. */
|
|
||||||
src->physical = sb.st_size;
|
|
||||||
} else {
|
|
||||||
/* NB. Because we configure with AC_SYS_LARGEFILE, off_t
|
|
||||||
* should be 64 bits on all platforms. For block devices, we
|
|
||||||
* have to seek (safe even if someone else is writing) to
|
|
||||||
* determine physical size, and assume that allocation is the
|
|
||||||
* same as physical (but can refine that assumption later if
|
|
||||||
* qemu is still running).
|
|
||||||
*/
|
|
||||||
end = lseek(fd, 0, SEEK_END);
|
|
||||||
if (end == (off_t)-1) {
|
|
||||||
virReportSystemError(errno,
|
|
||||||
_("failed to seek to end of %s"), path);
|
|
||||||
goto endjob;
|
|
||||||
}
|
|
||||||
src->physical = end;
|
|
||||||
src->allocation = end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Raw files: capacity is physical size. For all other files: if
|
|
||||||
* the metadata has a capacity, use that, otherwise fall back to
|
|
||||||
* physical size. */
|
|
||||||
if (!(format = src->format)) {
|
|
||||||
if (!cfg->allowDiskFormatProbing) {
|
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
||||||
_("no disk format for %s and probing is disabled"),
|
|
||||||
path);
|
|
||||||
goto endjob;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((format = virStorageFileProbeFormatFromBuf(src->path,
|
|
||||||
buf, len)) < 0)
|
|
||||||
goto endjob;
|
|
||||||
}
|
|
||||||
if (!(meta = virStorageFileGetMetadataFromBuf(src->path, buf, len,
|
|
||||||
format, NULL)))
|
|
||||||
goto endjob;
|
|
||||||
if (format == VIR_STORAGE_FILE_RAW)
|
|
||||||
src->capacity = src->physical;
|
|
||||||
else if ((meta = virStorageFileGetMetadataFromBuf(src->path, buf,
|
|
||||||
len, format, NULL)))
|
|
||||||
src->capacity = meta->capacity ? meta->capacity : src->physical;
|
|
||||||
else
|
|
||||||
goto endjob;
|
goto endjob;
|
||||||
|
|
||||||
/* If guest is not using raw disk format and on a block device,
|
if (!src->allocation) {
|
||||||
* then query highest allocated extent from QEMU
|
|
||||||
*/
|
|
||||||
if (virStorageSourceGetActualType(src) == VIR_STORAGE_TYPE_BLOCK &&
|
|
||||||
format != VIR_STORAGE_FILE_RAW &&
|
|
||||||
S_ISBLK(sb.st_mode)) {
|
|
||||||
qemuDomainObjPrivatePtr priv = vm->privateData;
|
qemuDomainObjPrivatePtr priv = vm->privateData;
|
||||||
|
|
||||||
/* If the guest is not running, then success/failure return
|
/* If the guest is not running, then success/failure return
|
||||||
@ -11183,7 +11211,6 @@ qemuDomainGetBlockInfo(virDomainPtr dom,
|
|||||||
*/
|
*/
|
||||||
if (!virDomainObjIsActive(vm)) {
|
if (!virDomainObjIsActive(vm)) {
|
||||||
activeFail = true;
|
activeFail = true;
|
||||||
ret = 0;
|
|
||||||
goto endjob;
|
goto endjob;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11192,9 +11219,6 @@ qemuDomainGetBlockInfo(virDomainPtr dom,
|
|||||||
disk->info.alias,
|
disk->info.alias,
|
||||||
&src->allocation);
|
&src->allocation);
|
||||||
qemuDomainObjExitMonitor(driver, vm);
|
qemuDomainObjExitMonitor(driver, vm);
|
||||||
|
|
||||||
} else {
|
|
||||||
ret = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
@ -11207,12 +11231,6 @@ qemuDomainGetBlockInfo(virDomainPtr dom,
|
|||||||
if (!qemuDomainObjEndJob(driver, vm))
|
if (!qemuDomainObjEndJob(driver, vm))
|
||||||
vm = NULL;
|
vm = NULL;
|
||||||
cleanup:
|
cleanup:
|
||||||
VIR_FREE(buf);
|
|
||||||
virStorageSourceFree(meta);
|
|
||||||
VIR_FORCE_CLOSE(fd);
|
|
||||||
if (disk)
|
|
||||||
virStorageFileDeinit(disk->src);
|
|
||||||
|
|
||||||
/* If we failed to get data from a domain because it's inactive and
|
/* If we failed to get data from a domain because it's inactive and
|
||||||
* it's not a persistent domain, then force failure.
|
* it's not a persistent domain, then force failure.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user