mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 21:55:25 +00:00
qemu: memdev: Add infrastructure to load memory device information
When using 'dimm' memory devices with qemu, some of the information like the slot number and base address need to be reloaded from qemu after process start so that it reflects the actual state. The state then allows to use memory devices across migrations.
This commit is contained in:
parent
3e4230d270
commit
5cdfaa31c4
@ -2815,6 +2815,55 @@ qemuDomainUpdateDeviceList(virQEMUDriverPtr driver,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
qemuDomainUpdateMemoryDeviceInfo(virQEMUDriverPtr driver,
|
||||
virDomainObjPtr vm,
|
||||
int asyncJob)
|
||||
{
|
||||
qemuDomainObjPrivatePtr priv = vm->privateData;
|
||||
virHashTablePtr meminfo = NULL;
|
||||
int rc;
|
||||
size_t i;
|
||||
|
||||
if (vm->def->nmems == 0)
|
||||
return 0;
|
||||
|
||||
if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0)
|
||||
return -1;
|
||||
|
||||
rc = qemuMonitorGetMemoryDeviceInfo(priv->mon, &meminfo);
|
||||
|
||||
if (qemuDomainObjExitMonitor(driver, vm) < 0)
|
||||
return -1;
|
||||
|
||||
/* if qemu doesn't support the info request, just carry on */
|
||||
if (rc == -2)
|
||||
return 0;
|
||||
|
||||
if (rc < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < vm->def->nmems; i++) {
|
||||
virDomainMemoryDefPtr mem = vm->def->mems[i];
|
||||
qemuMonitorMemoryDeviceInfoPtr dimm;
|
||||
|
||||
if (!mem->info.alias)
|
||||
continue;
|
||||
|
||||
if (!(dimm = virHashLookup(meminfo, mem->info.alias)))
|
||||
continue;
|
||||
|
||||
mem->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM;
|
||||
mem->info.addr.dimm.slot = dimm->slot;
|
||||
mem->info.addr.dimm.base = dimm->address;
|
||||
}
|
||||
|
||||
virHashFree(meminfo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
qemuDomainDefCheckABIStability(virQEMUDriverPtr driver,
|
||||
virDomainDefPtr src,
|
||||
|
@ -393,6 +393,10 @@ extern virDomainDefParserConfig virQEMUDriverDomainDefParserConfig;
|
||||
int qemuDomainUpdateDeviceList(virQEMUDriverPtr driver,
|
||||
virDomainObjPtr vm, int asyncJob);
|
||||
|
||||
int qemuDomainUpdateMemoryDeviceInfo(virQEMUDriverPtr driver,
|
||||
virDomainObjPtr vm,
|
||||
int asyncJob);
|
||||
|
||||
bool qemuDomainDefCheckABIStability(virQEMUDriverPtr driver,
|
||||
virDomainDefPtr src,
|
||||
virDomainDefPtr dst);
|
||||
|
@ -4363,3 +4363,45 @@ void qemuMonitorIOThreadsInfoFree(qemuMonitorIOThreadsInfoPtr iothread)
|
||||
VIR_FREE(iothread->name);
|
||||
VIR_FREE(iothread);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* qemuMonitorGetMemoryDeviceInfo:
|
||||
* @mon: pointer to the monitor
|
||||
* @info: Location to return the hash of qemuMonitorMemoryDeviceInfo
|
||||
*
|
||||
* Retrieve state and addresses of frontend memory devices present in
|
||||
* the guest.
|
||||
*
|
||||
* Returns 0 on success and fills @info with a newly allocated struct; if the
|
||||
* data can't be retrieved due to lack of support in qemu, returns -2. On
|
||||
* other errors returns -1.
|
||||
*/
|
||||
int
|
||||
qemuMonitorGetMemoryDeviceInfo(qemuMonitorPtr mon,
|
||||
virHashTablePtr *info)
|
||||
{
|
||||
VIR_DEBUG("mon=%p info=%p", mon, info);
|
||||
int ret;
|
||||
|
||||
*info = NULL;
|
||||
|
||||
if (!mon) {
|
||||
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
||||
_("monitor must not be NULL"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!mon->json)
|
||||
return -2;
|
||||
|
||||
if (!(*info = virHashCreate(10, virHashValueFree)))
|
||||
return -1;
|
||||
|
||||
if ((ret = qemuMonitorJSONGetMemoryDeviceInfo(mon, *info)) < 0) {
|
||||
virHashFree(*info);
|
||||
*info = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -884,6 +884,20 @@ int qemuMonitorGetIOThreads(qemuMonitorPtr mon,
|
||||
|
||||
void qemuMonitorIOThreadsInfoFree(qemuMonitorIOThreadsInfoPtr iothread);
|
||||
|
||||
typedef struct _qemuMonitorMemoryDeviceInfo qemuMonitorMemoryDeviceInfo;
|
||||
typedef qemuMonitorMemoryDeviceInfo *qemuMonitorMemoryDeviceInfoPtr;
|
||||
|
||||
struct _qemuMonitorMemoryDeviceInfo {
|
||||
unsigned long long address;
|
||||
unsigned int slot;
|
||||
bool hotplugged;
|
||||
bool hotpluggable;
|
||||
};
|
||||
|
||||
int qemuMonitorGetMemoryDeviceInfo(qemuMonitorPtr mon,
|
||||
virHashTablePtr *info)
|
||||
ATTRIBUTE_NONNULL(2);
|
||||
|
||||
/**
|
||||
* When running two dd process and using <> redirection, we need a
|
||||
* shell that will not truncate files. These two strings serve that
|
||||
|
@ -6493,3 +6493,125 @@ qemuMonitorJSONGetIOThreads(qemuMonitorPtr mon,
|
||||
virJSONValueFree(reply);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
qemuMonitorJSONGetMemoryDeviceInfo(qemuMonitorPtr mon,
|
||||
virHashTablePtr info)
|
||||
{
|
||||
int ret = -1;
|
||||
virJSONValuePtr cmd;
|
||||
virJSONValuePtr reply = NULL;
|
||||
virJSONValuePtr data = NULL;
|
||||
qemuMonitorMemoryDeviceInfoPtr meminfo = NULL;
|
||||
ssize_t n;
|
||||
size_t i;
|
||||
|
||||
if (!(cmd = qemuMonitorJSONMakeCommand("query-memory-devices", NULL)))
|
||||
return -1;
|
||||
|
||||
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
||||
|
||||
if (ret == 0) {
|
||||
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
|
||||
ret = -2;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = qemuMonitorJSONCheckError(cmd, reply);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
goto cleanup;
|
||||
|
||||
ret = -1;
|
||||
|
||||
if (!(data = virJSONValueObjectGet(reply, "return"))) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("query-memory-devices reply was missing return data"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((n = virJSONValueArraySize(data)) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("query-memory-devices reply data was not an array"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
virJSONValuePtr elem = virJSONValueArrayGet(data, i);
|
||||
const char *type;
|
||||
|
||||
if (!(type = virJSONValueObjectGetString(elem, "type"))) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("query-memory-devices reply data doesn't contain "
|
||||
"enum type discriminator"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* dimm memory devices */
|
||||
if (STREQ(type, "dimm")) {
|
||||
virJSONValuePtr dimminfo;
|
||||
const char *devalias;
|
||||
|
||||
if (!(dimminfo = virJSONValueObjectGet(elem, "data"))) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("query-memory-devices reply data doesn't "
|
||||
"contain enum data"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!(devalias = virJSONValueObjectGetString(dimminfo, "id"))) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("dimm memory info data is missing 'id'"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (VIR_ALLOC(meminfo) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (virJSONValueObjectGetNumberUlong(dimminfo, "addr",
|
||||
&meminfo->address) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("malformed/missing addr in dimm memory info"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (virJSONValueObjectGetNumberUint(dimminfo, "slot",
|
||||
&meminfo->slot) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("malformed/missing slot in dimm memory info"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (virJSONValueObjectGetBoolean(dimminfo, "hotplugged",
|
||||
&meminfo->hotplugged) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("malformed/missing hotplugged in dimm memory info"));
|
||||
goto cleanup;
|
||||
|
||||
}
|
||||
|
||||
if (virJSONValueObjectGetBoolean(dimminfo, "hotpluggable",
|
||||
&meminfo->hotpluggable) < 0) {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("malformed/missing hotpluggable in dimm memory info"));
|
||||
goto cleanup;
|
||||
|
||||
}
|
||||
|
||||
if (virHashAddEntry(info, devalias, meminfo) < 0)
|
||||
goto cleanup;
|
||||
|
||||
meminfo = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
VIR_FREE(meminfo);
|
||||
virJSONValueFree(cmd);
|
||||
virJSONValueFree(reply);
|
||||
return ret;
|
||||
}
|
||||
|
@ -469,4 +469,9 @@ int qemuMonitorJSONRTCResetReinjection(qemuMonitorPtr mon);
|
||||
int qemuMonitorJSONGetIOThreads(qemuMonitorPtr mon,
|
||||
qemuMonitorIOThreadsInfoPtr **iothreads)
|
||||
ATTRIBUTE_NONNULL(2);
|
||||
|
||||
int qemuMonitorJSONGetMemoryDeviceInfo(qemuMonitorPtr mon,
|
||||
virHashTablePtr info)
|
||||
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
|
||||
|
||||
#endif /* QEMU_MONITOR_JSON_H */
|
||||
|
@ -4916,6 +4916,10 @@ int qemuProcessStart(virConnectPtr conn,
|
||||
if (qemuDomainUpdateDeviceList(driver, vm, asyncJob) < 0)
|
||||
goto cleanup;
|
||||
|
||||
VIR_DEBUG("Updating info of memory devices");
|
||||
if (qemuDomainUpdateMemoryDeviceInfo(driver, vm, asyncJob) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* Technically, qemuProcessStart can be called from inside
|
||||
* QEMU_ASYNC_JOB_MIGRATION_IN, but we are okay treating this like
|
||||
* a sync job since no other job can call into the domain until
|
||||
|
Loading…
Reference in New Issue
Block a user