Query block allocation extent from QEMU monitor

The virDomainGetBlockInfo API allows query physical block
extent and allocated block extent. These are normally the
same value unless storing a special format like qcow2
inside a block device. In this scenario we can query QEMU
to get the actual allocated extent.

Since last time:

 - Return fatal error in text monitor
 - Only invoke monitor command for block devices
 - Fix error handling JSON code

* src/qemu/qemu_driver.c: Fill in block aloction extent when VM
  is running
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
  src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
  src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
  API to query the highest block extent via info blockstats
This commit is contained in:
Daniel P. Berrange 2010-05-14 09:10:01 -04:00 committed by Eric Blake
parent dd1058fa8f
commit ebb0c19c48
7 changed files with 156 additions and 8 deletions

View File

@ -9453,6 +9453,7 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom,
int fd = -1;
off_t end;
virStorageFileMetadata meta;
virDomainDiskDefPtr disk = NULL;
struct stat sb;
int i;
@ -9479,19 +9480,17 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom,
for (i = 0 ; i < vm->def->ndisks ; i++) {
if (vm->def->disks[i]->src != NULL &&
STREQ (vm->def->disks[i]->src, path)) {
ret = 0;
disk = vm->def->disks[i];
break;
}
}
if (ret != 0) {
if (!disk) {
qemuReportError(VIR_ERR_INVALID_ARG,
_("invalid path %s not assigned to domain"), path);
goto cleanup;
}
ret = -1;
/* The path is correct, now try to open it and get its size. */
fd = open (path, O_RDONLY);
if (fd == -1) {
@ -9541,11 +9540,31 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom,
if (meta.capacity)
info->capacity = meta.capacity;
/* XXX allocation will need to be pulled from QEMU for
* the qcow inside LVM case */
/* Set default value .. */
info->allocation = info->physical;
ret = 0;
/* ..but if guest is running & not using raw
disk format and on a block device, then query
highest allocated extent from QEMU */
if (virDomainObjIsActive(vm) &&
disk->type == VIR_DOMAIN_DISK_TYPE_BLOCK &&
meta.format != VIR_STORAGE_FILE_RAW &&
S_ISBLK(sb.st_mode)) {
qemuDomainObjPrivatePtr priv = vm->privateData;
if (qemuDomainObjBeginJob(vm) < 0)
goto cleanup;
qemuDomainObjEnterMonitor(vm);
ret = qemuMonitorGetBlockExtent(priv->mon,
disk->info.alias,
&info->allocation);
qemuDomainObjExitMonitor(vm);
if (qemuDomainObjEndJob(vm) == 0)
vm = NULL;
} else {
ret = 0;
}
cleanup:
if (fd != -1)

View File

@ -1060,6 +1060,22 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon,
return ret;
}
int qemuMonitorGetBlockExtent(qemuMonitorPtr mon,
const char *devname,
unsigned long long *extent)
{
int ret;
DEBUG("mon=%p, fd=%d, devname=%p",
mon, mon->fd, devname);
if (mon->json)
ret = qemuMonitorJSONGetBlockExtent(mon, devname, extent);
else
ret = qemuMonitorTextGetBlockExtent(mon, devname, extent);
return ret;
}
int qemuMonitorSetVNCPassword(qemuMonitorPtr mon,
const char *password)

View File

@ -185,6 +185,10 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon,
long long *wr_bytes,
long long *errs);
int qemuMonitorGetBlockExtent(qemuMonitorPtr mon,
const char *devname,
unsigned long long *extent);
int qemuMonitorSetVNCPassword(qemuMonitorPtr mon,
const char *password);

View File

@ -1143,6 +1143,100 @@ cleanup:
}
int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
const char *devname,
unsigned long long *extent)
{
int ret = -1;
int i;
int found = 0;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-blockstats",
NULL);
virJSONValuePtr reply = NULL;
virJSONValuePtr devices;
*extent = 0;
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
devices = virJSONValueObjectGet(reply, "return");
if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats reply was missing device list"));
goto cleanup;
}
for (i = 0 ; i < virJSONValueArraySize(devices) ; i++) {
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
virJSONValuePtr stats;
virJSONValuePtr parent;
const char *thisdev;
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats device entry was not in expected format"));
goto cleanup;
}
if ((thisdev = virJSONValueObjectGetString(dev, "device")) == NULL) {
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats device entry was not in expected format"));
goto cleanup;
}
/* New QEMU has separate names for host & guest side of the disk
* and libvirt gives the host side a 'drive-' prefix. The passed
* in devname is the guest side though
*/
if (STRPREFIX(thisdev, QEMU_DRIVE_HOST_PREFIX))
thisdev += strlen(QEMU_DRIVE_HOST_PREFIX);
if (STRNEQ(thisdev, devname))
continue;
found = 1;
if ((parent = virJSONValueObjectGet(dev, "parent")) == NULL ||
parent->type != VIR_JSON_TYPE_OBJECT) {
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats parent entry was not in expected format"));
goto cleanup;
}
if ((stats = virJSONValueObjectGet(parent, "stats")) == NULL ||
stats->type != VIR_JSON_TYPE_OBJECT) {
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats stats entry was not in expected format"));
goto cleanup;
}
if (virJSONValueObjectGetNumberUlong(stats, "wr_highest_offset", extent) < 0) {
qemuReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot read %s statistic"),
"wr_highest_offset");
goto cleanup;
}
}
if (!found) {
qemuReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot find statistics for device '%s'"), devname);
goto cleanup;
}
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon,
const char *password)
{

View File

@ -56,6 +56,9 @@ int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
long long *wr_req,
long long *wr_bytes,
long long *errs);
int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
const char *devname,
unsigned long long *extent);
int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon,

View File

@ -712,6 +712,16 @@ int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon,
}
int qemuMonitorTextGetBlockExtent(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
const char *devname ATTRIBUTE_UNUSED,
unsigned long long *extent ATTRIBUTE_UNUSED)
{
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("unable to query block extent with this QEMU"));
return -1;
}
static int
qemuMonitorSendVNCPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
qemuMonitorMessagePtr msg,

View File

@ -55,7 +55,9 @@ int qemuMonitorTextGetBlockStatsInfo(qemuMonitorPtr mon,
long long *wr_req,
long long *wr_bytes,
long long *errs);
int qemuMonitorTextGetBlockExtent(qemuMonitorPtr mon,
const char *devname,
unsigned long long *extent);
int qemuMonitorTextSetVNCPassword(qemuMonitorPtr mon,
const char *password);