mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-02-09 05:01:28 +00:00
qemu: Extract code to setup memory backing objects
Extract the memory backend device code into a separate function so that it can be later easily refactored and reused. Few small changes for future reusability, namely: - new (currently unused) parameter for user specified page size - size of the memory is specified in kibibytes, divided up in the function - new (currently unused) parameter for user specifed source nodeset - option to enforce capability check
This commit is contained in:
parent
331b2583ec
commit
db3b1c4a1c
@ -4502,6 +4502,243 @@ qemuBuildControllerDevStr(virDomainDefPtr domainDef,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qemuBuildMemoryBackendStr:
|
||||||
|
* @size: size of the memory device in kibibytes
|
||||||
|
* @pagesize: size of the requested memory page in KiB, 0 for default
|
||||||
|
* @guestNode: NUMA node in the guest that the memory object will be attached to
|
||||||
|
* @hostNodes: map of host nodes to alloc the memory in, NULL for default
|
||||||
|
* @autoNodeset: fallback nodeset in case of automatic numa placement
|
||||||
|
* @def: domain definition object
|
||||||
|
* @qemuCaps: qemu capabilities object
|
||||||
|
* @cfg: qemu driver config object
|
||||||
|
* @aliasPrefix: prefix string of the alias (to allow for multiple frontents)
|
||||||
|
* @id: index of the device (to construct the alias)
|
||||||
|
* @backendStr: returns the object string
|
||||||
|
*
|
||||||
|
* Formats the configuration string for the memory device backend according
|
||||||
|
* to the configuration. @pagesize and @hostNodes can be used to override the
|
||||||
|
* default source configuration, both are optional.
|
||||||
|
*
|
||||||
|
* Returns 0 on success, 1 if only the implicit memory-device-ram with no
|
||||||
|
* other configuration was used (to detect legacy configurations). Returns
|
||||||
|
* -1 in case of an error.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
qemuBuildMemoryBackendStr(unsigned long long size,
|
||||||
|
unsigned long long pagesize,
|
||||||
|
int guestNode,
|
||||||
|
virBitmapPtr userNodeset,
|
||||||
|
virBitmapPtr autoNodeset,
|
||||||
|
virDomainDefPtr def,
|
||||||
|
virQEMUCapsPtr qemuCaps,
|
||||||
|
virQEMUDriverConfigPtr cfg,
|
||||||
|
const char *aliasPrefix,
|
||||||
|
size_t id,
|
||||||
|
char **backendStr,
|
||||||
|
bool force)
|
||||||
|
{
|
||||||
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
||||||
|
virDomainHugePagePtr master_hugepage = NULL;
|
||||||
|
virDomainHugePagePtr hugepage = NULL;
|
||||||
|
virDomainNumatuneMemMode mode;
|
||||||
|
const long system_page_size = sysconf(_SC_PAGESIZE) / 1024;
|
||||||
|
virMemAccess memAccess = def->cpu->cells[guestNode].memAccess;
|
||||||
|
size_t i;
|
||||||
|
char *mem_path = NULL;
|
||||||
|
char *nodemask = NULL;
|
||||||
|
char *tmpmask = NULL, *next = NULL;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
*backendStr = NULL;
|
||||||
|
|
||||||
|
mode = virDomainNumatuneGetMode(def->numatune, guestNode);
|
||||||
|
|
||||||
|
if (pagesize == 0 || pagesize != system_page_size) {
|
||||||
|
/* Find the huge page size we want to use */
|
||||||
|
for (i = 0; i < def->mem.nhugepages; i++) {
|
||||||
|
bool thisHugepage = false;
|
||||||
|
|
||||||
|
hugepage = &def->mem.hugepages[i];
|
||||||
|
|
||||||
|
if (!hugepage->nodemask) {
|
||||||
|
master_hugepage = hugepage;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (virBitmapGetBit(hugepage->nodemask, guestNode,
|
||||||
|
&thisHugepage) < 0) {
|
||||||
|
/* Ignore this error. It's not an error after all. Well,
|
||||||
|
* the nodemask for this <page/> can contain lower NUMA
|
||||||
|
* nodes than we are querying in here. */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thisHugepage) {
|
||||||
|
/* Hooray, we've found the page size */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == def->mem.nhugepages) {
|
||||||
|
/* We have not found specific huge page to be used with this
|
||||||
|
* NUMA node. Use the generic setting then (<page/> without any
|
||||||
|
* @nodemask) if possible. */
|
||||||
|
hugepage = master_hugepage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hugepage)
|
||||||
|
pagesize = hugepage->size;
|
||||||
|
|
||||||
|
if (hugepage && hugepage->size == system_page_size) {
|
||||||
|
/* However, if user specified to use "huge" page
|
||||||
|
* of regular system page size, it's as if they
|
||||||
|
* hasn't specified any huge pages at all. */
|
||||||
|
hugepage = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hugepage) {
|
||||||
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) {
|
||||||
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||||
|
_("this qemu doesn't support hugepage memory backing"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now lets see, if the huge page we want to use is even mounted
|
||||||
|
* and ready to use */
|
||||||
|
for (i = 0; i < cfg->nhugetlbfs; i++) {
|
||||||
|
if (cfg->hugetlbfs[i].size == hugepage->size)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == cfg->nhugetlbfs) {
|
||||||
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("Unable to find any usable hugetlbfs mount for %llu KiB"),
|
||||||
|
pagesize);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
VIR_FREE(mem_path);
|
||||||
|
if (!(mem_path = qemuGetHugepagePath(&cfg->hugetlbfs[i])))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
virBufferAsprintf(&buf, "memory-backend-file,prealloc=yes,mem-path=%s",
|
||||||
|
mem_path);
|
||||||
|
|
||||||
|
switch (memAccess) {
|
||||||
|
case VIR_MEM_ACCESS_SHARED:
|
||||||
|
virBufferAddLit(&buf, ",share=on");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIR_MEM_ACCESS_PRIVATE:
|
||||||
|
virBufferAddLit(&buf, ",share=off");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIR_MEM_ACCESS_DEFAULT:
|
||||||
|
case VIR_MEM_ACCESS_LAST:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (memAccess) {
|
||||||
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||||
|
_("Shared memory mapping is supported "
|
||||||
|
"only with hugepages"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
virBufferAddLit(&buf, "memory-backend-ram");
|
||||||
|
}
|
||||||
|
|
||||||
|
virBufferAsprintf(&buf, ",size=%lluM,id=%s%zu", size / 1024,
|
||||||
|
aliasPrefix, id);
|
||||||
|
|
||||||
|
if (userNodeset) {
|
||||||
|
if (!(nodemask = virBitmapFormat(userNodeset)))
|
||||||
|
goto cleanup;
|
||||||
|
} else {
|
||||||
|
if (virDomainNumatuneMaybeFormatNodeset(def->numatune, autoNodeset,
|
||||||
|
&nodemask, guestNode) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodemask) {
|
||||||
|
if (strchr(nodemask, ',') &&
|
||||||
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_NUMA)) {
|
||||||
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||||
|
_("disjoint NUMA node ranges are not supported "
|
||||||
|
"with this QEMU"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (tmpmask = nodemask; tmpmask; tmpmask = next) {
|
||||||
|
if ((next = strchr(tmpmask, ',')))
|
||||||
|
*(next++) = '\0';
|
||||||
|
virBufferAddLit(&buf, ",host-nodes=");
|
||||||
|
virBufferAdd(&buf, tmpmask, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
virBufferAsprintf(&buf, ",policy=%s", qemuNumaPolicyTypeToString(mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (virBufferCheckError(&buf) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
*backendStr = virBufferContentAndReset(&buf);
|
||||||
|
|
||||||
|
if (!hugepage) {
|
||||||
|
if ((nodemask || force) &&
|
||||||
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM)) {
|
||||||
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||||
|
_("this qemu doesn't support the "
|
||||||
|
"memory-backend-ram object"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* report back that using the new backend is not necessary to achieve
|
||||||
|
* the desired configuration */
|
||||||
|
if (!nodemask) {
|
||||||
|
ret = 1;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
virBufferFreeAndReset(&buf);
|
||||||
|
VIR_FREE(nodemask);
|
||||||
|
VIR_FREE(mem_path);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
qemuBuildMemoryCellBackendStr(virDomainDefPtr def,
|
||||||
|
virQEMUCapsPtr qemuCaps,
|
||||||
|
virQEMUDriverConfigPtr cfg,
|
||||||
|
size_t cell,
|
||||||
|
virBitmapPtr auto_nodeset,
|
||||||
|
char **backendStr)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = qemuBuildMemoryBackendStr(def->cpu->cells[cell].mem, 0, cell,
|
||||||
|
NULL, auto_nodeset,
|
||||||
|
def, qemuCaps, cfg,
|
||||||
|
"ram-node", cell,
|
||||||
|
backendStr, false);
|
||||||
|
|
||||||
|
if (ret == 1) {
|
||||||
|
VIR_FREE(*backendStr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
char *
|
char *
|
||||||
qemuBuildNicStr(virDomainNetDefPtr net,
|
qemuBuildNicStr(virDomainNetDefPtr net,
|
||||||
const char *prefix,
|
const char *prefix,
|
||||||
@ -6796,14 +7033,12 @@ qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg,
|
|||||||
virDomainDefPtr def,
|
virDomainDefPtr def,
|
||||||
virCommandPtr cmd,
|
virCommandPtr cmd,
|
||||||
virQEMUCapsPtr qemuCaps,
|
virQEMUCapsPtr qemuCaps,
|
||||||
virBitmapPtr nodeset)
|
virBitmapPtr auto_nodeset)
|
||||||
{
|
{
|
||||||
size_t i, j;
|
size_t i;
|
||||||
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
||||||
virDomainHugePagePtr master_hugepage = NULL;
|
|
||||||
char *cpumask = NULL, *tmpmask = NULL, *next = NULL;
|
char *cpumask = NULL, *tmpmask = NULL, *next = NULL;
|
||||||
char *nodemask = NULL;
|
char *backendStr = NULL;
|
||||||
char *mem_path = NULL;
|
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
const long system_page_size = sysconf(_SC_PAGESIZE) / 1024;
|
const long system_page_size = sysconf(_SC_PAGESIZE) / 1024;
|
||||||
|
|
||||||
@ -6825,7 +7060,7 @@ qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!virDomainNumatuneNodesetIsAvailable(def->numatune, nodeset))
|
if (!virDomainNumatuneNodesetIsAvailable(def->numatune, auto_nodeset))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
for (i = 0; i < def->mem.nhugepages; i++) {
|
for (i = 0; i < def->mem.nhugepages; i++) {
|
||||||
@ -6853,13 +7088,11 @@ qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < def->cpu->ncells; i++) {
|
for (i = 0; i < def->cpu->ncells; i++) {
|
||||||
virDomainHugePagePtr hugepage = NULL;
|
|
||||||
unsigned long long cellmem = VIR_DIV_UP(def->cpu->cells[i].mem, 1024);
|
unsigned long long cellmem = VIR_DIV_UP(def->cpu->cells[i].mem, 1024);
|
||||||
def->cpu->cells[i].mem = cellmem * 1024;
|
def->cpu->cells[i].mem = cellmem * 1024;
|
||||||
virMemAccess memAccess = def->cpu->cells[i].memAccess;
|
|
||||||
|
|
||||||
VIR_FREE(cpumask);
|
VIR_FREE(cpumask);
|
||||||
VIR_FREE(nodemask);
|
VIR_FREE(backendStr);
|
||||||
|
|
||||||
if (!(cpumask = virBitmapFormat(def->cpu->cells[i].cpumask)))
|
if (!(cpumask = virBitmapFormat(def->cpu->cells[i].cpumask)))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@ -6872,133 +7105,19 @@ qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM) ||
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM) ||
|
||||||
virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) {
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) {
|
||||||
virDomainNumatuneMemMode mode;
|
if (qemuBuildMemoryCellBackendStr(def, qemuCaps, cfg, i,
|
||||||
const char *policy = NULL;
|
auto_nodeset, &backendStr) < 0)
|
||||||
|
|
||||||
mode = virDomainNumatuneGetMode(def->numatune, i);
|
|
||||||
policy = qemuNumaPolicyTypeToString(mode);
|
|
||||||
|
|
||||||
/* Find the huge page size we want to use */
|
|
||||||
for (j = 0; j < def->mem.nhugepages; j++) {
|
|
||||||
bool thisHugepage = false;
|
|
||||||
|
|
||||||
hugepage = &def->mem.hugepages[j];
|
|
||||||
|
|
||||||
if (!hugepage->nodemask) {
|
|
||||||
master_hugepage = hugepage;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (virBitmapGetBit(hugepage->nodemask, i, &thisHugepage) < 0) {
|
|
||||||
/* Ignore this error. It's not an error after all. Well,
|
|
||||||
* the nodemask for this <page/> can contain lower NUMA
|
|
||||||
* nodes than we are querying in here. */
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thisHugepage) {
|
|
||||||
/* Hooray, we've found the page size */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j == def->mem.nhugepages) {
|
|
||||||
/* We have not found specific huge page to be used with this
|
|
||||||
* NUMA node. Use the generic setting then (<page/> without any
|
|
||||||
* @nodemask) if possible. */
|
|
||||||
hugepage = master_hugepage;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hugepage && hugepage->size == system_page_size) {
|
|
||||||
/* However, if user specified to use "huge" page
|
|
||||||
* of regular system page size, it's as if they
|
|
||||||
* hasn't specified any huge pages at all. */
|
|
||||||
hugepage = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hugepage) {
|
|
||||||
/* Now lets see, if the huge page we want to use is even mounted
|
|
||||||
* and ready to use */
|
|
||||||
|
|
||||||
for (j = 0; j < cfg->nhugetlbfs; j++) {
|
|
||||||
if (cfg->hugetlbfs[j].size == hugepage->size)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j == cfg->nhugetlbfs) {
|
|
||||||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
||||||
_("Unable to find any usable hugetlbfs mount for %llu KiB"),
|
|
||||||
hugepage->size);
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
VIR_FREE(mem_path);
|
|
||||||
if (!(mem_path = qemuGetHugepagePath(&cfg->hugetlbfs[j])))
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
virBufferAsprintf(&buf,
|
if (backendStr) {
|
||||||
"memory-backend-file,prealloc=yes,mem-path=%s",
|
|
||||||
mem_path);
|
|
||||||
|
|
||||||
switch (memAccess) {
|
|
||||||
case VIR_MEM_ACCESS_SHARED:
|
|
||||||
virBufferAddLit(&buf, ",share=on");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VIR_MEM_ACCESS_PRIVATE:
|
|
||||||
virBufferAddLit(&buf, ",share=off");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VIR_MEM_ACCESS_DEFAULT:
|
|
||||||
case VIR_MEM_ACCESS_LAST:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (memAccess) {
|
|
||||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
||||||
_("Shared memory mapping is supported "
|
|
||||||
"only with hugepages"));
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
virBufferAddLit(&buf, "memory-backend-ram");
|
|
||||||
}
|
|
||||||
|
|
||||||
virBufferAsprintf(&buf, ",size=%lluM,id=ram-node%zu", cellmem, i);
|
|
||||||
|
|
||||||
if (virDomainNumatuneMaybeFormatNodeset(def->numatune, nodeset,
|
|
||||||
&nodemask, i) < 0)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
if (nodemask) {
|
|
||||||
if (strchr(nodemask, ',') &&
|
|
||||||
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_NUMA)) {
|
|
||||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
||||||
_("disjoint NUMA node ranges are not supported "
|
|
||||||
"with this QEMU"));
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (tmpmask = nodemask; tmpmask; tmpmask = next) {
|
|
||||||
if ((next = strchr(tmpmask, ',')))
|
|
||||||
*(next++) = '\0';
|
|
||||||
virBufferAddLit(&buf, ",host-nodes=");
|
|
||||||
virBufferAdd(&buf, tmpmask, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
virBufferAsprintf(&buf, ",policy=%s", policy);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hugepage || nodemask) {
|
|
||||||
virCommandAddArg(cmd, "-object");
|
virCommandAddArg(cmd, "-object");
|
||||||
virCommandAddArgBuffer(cmd, &buf);
|
virCommandAddArg(cmd, backendStr);
|
||||||
} else {
|
|
||||||
virBufferFreeAndReset(&buf);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (memAccess) {
|
if (def->cpu->cells[i].memAccess) {
|
||||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||||
_("Shared memory mapping is not supported "
|
_("Shared memory mapping is not supported "
|
||||||
"with this QEMU"));
|
"with this QEMU"));
|
||||||
@ -7016,7 +7135,7 @@ qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg,
|
|||||||
virBufferAdd(&buf, tmpmask, -1);
|
virBufferAdd(&buf, tmpmask, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hugepage || nodemask)
|
if (backendStr)
|
||||||
virBufferAsprintf(&buf, ",memdev=ram-node%zu", i);
|
virBufferAsprintf(&buf, ",memdev=ram-node%zu", i);
|
||||||
else
|
else
|
||||||
virBufferAsprintf(&buf, ",mem=%llu", cellmem);
|
virBufferAsprintf(&buf, ",mem=%llu", cellmem);
|
||||||
@ -7027,8 +7146,7 @@ qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg,
|
|||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
VIR_FREE(cpumask);
|
VIR_FREE(cpumask);
|
||||||
VIR_FREE(nodemask);
|
VIR_FREE(backendStr);
|
||||||
VIR_FREE(mem_path);
|
|
||||||
virBufferFreeAndReset(&buf);
|
virBufferFreeAndReset(&buf);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user