[libvirt] [RFC PATCH 00/11] qemu: add support to hotplug memory device

Now qemu has already supported memory hotplug, so this patchset will make libvirt support hotplug memory device for qemu driver. First, add two parameters slots and maxmem for memory hotplug, and the xml format can be like this. <memory slot='10'>102400</memory> <maxMemory>1024000</maxMemory> Second, memory device's xml format can be like this. <dimm driver='pc-dimm' addr='0' node='0' slot='1'> <backend type='ram' size='131072'/> </dimm> Zhu Guihua (11): domain_conf: support slots and maxmem properties in memory xml domain_conf: introduce virDomainDimmGetFreeSlot domain_conf: add support for memory device configuration in XML domain_conf: introduce dimm def helpers domain_conf: introduce dimm device hotplug helpers qemu: implement dimm device hotplug on config level qemu_monitor: introduce qemuMonitorAddMemoryBackend qemu_command: introduce a func for memory device alias qemu: implement support for pc-dimm qemu: introduce qemuBuildDimmDeviceStr qemu: implement dimm device hotplug on live level src/conf/domain_conf.c | 306 ++++++++++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 54 ++++++++ src/libvirt_private.syms | 6 + src/qemu/qemu_capabilities.c | 3 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 110 +++++++++++++++- src/qemu/qemu_command.h | 7 + src/qemu/qemu_driver.c | 26 ++++ src/qemu/qemu_hotplug.c | 71 ++++++++++ src/qemu/qemu_hotplug.h | 5 + src/qemu/qemu_monitor.c | 54 ++++++++ src/qemu/qemu_monitor.h | 4 + 12 files changed, 644 insertions(+), 3 deletions(-) -- 1.9.3

Add properties slots and maxmem, so as to support memory hotplug. Signed-off-by: Zhu Guihua <zhugh.fnst@cn.fujitsu.com> --- src/conf/domain_conf.c | 22 ++++++++++++++++++++++ src/conf/domain_conf.h | 4 ++++ src/qemu/qemu_command.c | 28 ++++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 8792f5e..526f2da 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -12594,6 +12594,7 @@ virDomainDefParseXML(xmlDocPtr xml, long id = -1; virDomainDefPtr def; unsigned long count; + unsigned int slots; bool uuid_generated = false; virHashTablePtr bootHash = NULL; bool usb_none = false; @@ -12723,6 +12724,21 @@ virDomainDefParseXML(xmlDocPtr xml, &def->mem.cur_balloon, false, true) < 0) goto error; + n = virXPathUInt("string(./memory[1]/@slots)", ctxt, &slots); + if (n == -2) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("maximum slots for memory devices " + "must be an integer")); + goto error; + } else if (n == -1) { + def->mem.slots = -1; + } else { + def->mem.slots = slots; + if (virDomainParseMemory("./maxMemory[1]", NULL, ctxt, + &def->mem.maxmem, true, true) < 0) + goto error; + } + /* and info about it */ if ((tmp = virXPathString("string(./memory[1]/@dumpCore)", ctxt)) && (def->mem.dump_core = virTristateSwitchTypeFromString(tmp)) <= 0) { @@ -19319,12 +19335,18 @@ virDomainDefFormatInternal(virDomainDefPtr def, if (def->mem.dump_core) virBufferAsprintf(buf, " dumpCore='%s'", virTristateSwitchTypeToString(def->mem.dump_core)); + if (def->mem.slots && def->mem.maxmem) + virBufferAsprintf(buf, " slot='%u'", + def->mem.slots); virBufferAsprintf(buf, " unit='KiB'>%llu</memory>\n", def->mem.max_balloon); virBufferAsprintf(buf, "<currentMemory unit='KiB'>%llu</currentMemory>\n", def->mem.cur_balloon); + if (def->mem.slots && def->mem.maxmem) + virBufferAsprintf(buf, "<maxMemory unit='KiB'>%llu</maxMemory>\n", + def->mem.maxmem); /* add blkiotune only if there are any */ if (def->blkio.weight) { blkio = true; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 09ab194..731d14f 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2018,6 +2018,10 @@ struct _virDomainMemtune { unsigned long long cur_balloon; /* in kibibytes, capped at ulong thanks to virDomainGetInfo */ + unsigned int slots; /* the maximum slots for memory devices */ + unsigned long long maxmem; /* in kibibytes, the maximum memory + for the guest */ + virDomainHugePagePtr hugepages; size_t nhugepages; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index c041ee7..536abc3 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -6598,6 +6598,26 @@ qemuBuildMachineArgStr(virCommandPtr cmd, } static char * +qemuBuildMemArgStr(virDomainDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + def->mem.max_balloon = VIR_DIV_UP(def->mem.max_balloon, 1024) * 1024; + + virBufferAsprintf(&buf, "%lluM", def->mem.max_balloon / 1024); + if (def->mem.slots != -1) { + def->mem.maxmem = VIR_DIV_UP(def->mem.maxmem, 1024) * 1024; + virBufferAsprintf(&buf, ",slots=%u", def->mem.slots); + virBufferAsprintf(&buf, ",maxmem=%lluM", def->mem.maxmem / 1024); + } + + if (virBufferCheckError(&buf) < 0) + return NULL; + + return virBufferContentAndReset(&buf); +} + +static char * qemuBuildSmpArgStr(const virDomainDef *def, virQEMUCapsPtr qemuCaps) { @@ -7796,6 +7816,7 @@ qemuBuildCommandLine(virConnectPtr conn, char uuid[VIR_UUID_STRING_BUFLEN]; char *cpu; char *smp; + char *mem; int last_good_net = -1; bool hasHwVirt = false; virCommandPtr cmd = NULL; @@ -7929,8 +7950,11 @@ qemuBuildCommandLine(virConnectPtr conn, * XML to reflect our rounding. */ virCommandAddArg(cmd, "-m"); - def->mem.max_balloon = VIR_DIV_UP(def->mem.max_balloon, 1024) * 1024; - virCommandAddArgFormat(cmd, "%llu", def->mem.max_balloon / 1024); + if (!(mem = qemuBuildMemArgStr(def))) + goto error; + virCommandAddArg(cmd, mem); + VIR_FREE(mem); + if (def->mem.nhugepages && (!def->cpu || !def->cpu->ncells)) { const long system_page_size = sysconf(_SC_PAGESIZE) / 1024; char *mem_path = NULL; -- 1.9.3

Add a bitmap dimm_slot_map to store status of slots. If you want to use a slot, you can find a minimum value which has not been used in the bitmap. Signed-off-by: Zhu Guihua <zhugh.fnst@cn.fujitsu.com> --- src/conf/domain_conf.c | 12 ++++++++++++ src/conf/domain_conf.h | 3 +++ 2 files changed, 15 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 526f2da..d7f7a9e 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -12739,6 +12739,9 @@ virDomainDefParseXML(xmlDocPtr xml, goto error; } + if (!(def->mem.dimm_slot_map = virBitmapNew(def->mem.slots))) + goto error; + /* and info about it */ if ((tmp = virXPathString("string(./memory[1]/@dumpCore)", ctxt)) && (def->mem.dump_core = virTristateSwitchTypeFromString(tmp)) <= 0) { @@ -16205,6 +16208,15 @@ virDomainDefAddImplicitControllers(virDomainDefPtr def) return 0; } +int +virDomainDimmGetFreeSlot(virDomainDefPtr def) +{ + int i; + i = virBitmapNextClearBit(def->mem.dimm_slot_map, 0); + + return i; +} + /* Check if vcpupin with same vcpuid already exists. * Return 1 if exists, 0 if not. */ int diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 731d14f..d746272 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2021,6 +2021,7 @@ struct _virDomainMemtune { unsigned int slots; /* the maximum slots for memory devices */ unsigned long long maxmem; /* in kibibytes, the maximum memory for the guest */ + virBitmapPtr dimm_slot_map; virDomainHugePagePtr hugepages; size_t nhugepages; @@ -2526,6 +2527,8 @@ int virDomainDefCompatibleDevice(virDomainDefPtr def, virDomainDeviceDefPtr dev, virDomainDeviceAction action); +int virDomainDimmGetFreeSlot(virDomainDefPtr def); + int virDomainVcpuPinAdd(virDomainVcpuPinDefPtr **vcpupin_list, size_t *nvcpupin, unsigned char *cpumap, -- 1.9.3

This patch adds configuration support for the memory device. Signed-off-by: Zhu Guihua <zhugh.fnst@cn.fujitsu.com> --- src/conf/domain_conf.c | 16 +++++++++++++++- src/conf/domain_conf.h | 30 ++++++++++++++++++++++++++++++ src/qemu/qemu_driver.c | 6 ++++++ src/qemu/qemu_hotplug.c | 1 + 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index d7f7a9e..ea41cbd 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -236,7 +236,8 @@ VIR_ENUM_IMPL(virDomainDevice, VIR_DOMAIN_DEVICE_LAST, "rng", "shmem", "tpm", - "panic") + "panic", + "dimm") VIR_ENUM_IMPL(virDomainDeviceAddress, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST, "none", @@ -1981,6 +1982,7 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def) case VIR_DOMAIN_DEVICE_PANIC: virDomainPanicDefFree(def->data.panic); break; + case VIR_DOMAIN_DEVICE_DIMM: case VIR_DOMAIN_DEVICE_LAST: case VIR_DOMAIN_DEVICE_NONE: break; @@ -2687,6 +2689,8 @@ virDomainDeviceGetInfo(virDomainDeviceDefPtr device) return &device->data.tpm->info; case VIR_DOMAIN_DEVICE_PANIC: return &device->data.panic->info; + case VIR_DOMAIN_DEVICE_DIMM: + return &device->data.dimm->info; /* The following devices do not contain virDomainDeviceInfo */ case VIR_DOMAIN_DEVICE_LEASE: @@ -2917,6 +2921,12 @@ virDomainDeviceInfoIterateInternal(virDomainDefPtr def, if (cb(def, &device, &def->panic->info, opaque) < 0) return -1; } + device.type = VIR_DOMAIN_DEVICE_DIMM; + for (i = 0; i < def->ndimms; i++) { + device.data.dimm = def->dimms[i]; + if (cb(def, &device, &def->dimms[i]->info, opaque) < 0) + return -1; + } /* Coverity is not very happy with this - all dead_error_condition */ #if !STATIC_ANALYSIS @@ -2948,6 +2958,7 @@ virDomainDeviceInfoIterateInternal(virDomainDefPtr def, case VIR_DOMAIN_DEVICE_SHMEM: case VIR_DOMAIN_DEVICE_TPM: case VIR_DOMAIN_DEVICE_PANIC: + case VIR_DOMAIN_DEVICE_DIMM: case VIR_DOMAIN_DEVICE_LAST: case VIR_DOMAIN_DEVICE_RNG: break; @@ -11175,6 +11186,7 @@ virDomainDeviceDefParse(const char *xmlStr, if (!(dev->data.panic = virDomainPanicDefParseXML(node))) goto error; break; + case VIR_DOMAIN_DEVICE_DIMM: case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LAST: break; @@ -16035,6 +16047,7 @@ virDomainDefCheckABIStability(virDomainDefPtr src, case VIR_DOMAIN_DEVICE_TPM: case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_SHMEM: + case VIR_DOMAIN_DEVICE_DIMM: break; } #endif @@ -21482,6 +21495,7 @@ virDomainDeviceDefCopy(virDomainDeviceDefPtr src, case VIR_DOMAIN_DEVICE_PANIC: rc = virDomainPanicDefFormat(&buf, src->data.panic); break; + case VIR_DOMAIN_DEVICE_DIMM: case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_SMARTCARD: case VIR_DOMAIN_DEVICE_MEMBALLOON: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index d746272..c4ebbd4 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -132,6 +132,9 @@ typedef virDomainIdMapDef *virDomainIdMapDefPtr; typedef struct _virDomainPanicDef virDomainPanicDef; typedef virDomainPanicDef *virDomainPanicDefPtr; +typedef struct _virDomainDimmDef virDomainDimmDef; +typedef virDomainDimmDef *virDomainDimmDefPtr; + /* forward declarations virDomainChrSourceDef, required by * virDomainNetDef */ @@ -168,6 +171,7 @@ typedef enum { VIR_DOMAIN_DEVICE_SHMEM, VIR_DOMAIN_DEVICE_TPM, VIR_DOMAIN_DEVICE_PANIC, + VIR_DOMAIN_DEVICE_DIMM, VIR_DOMAIN_DEVICE_LAST } virDomainDeviceType; @@ -198,6 +202,7 @@ struct _virDomainDeviceDef { virDomainShmemDefPtr shmem; virDomainTPMDefPtr tpm; virDomainPanicDefPtr panic; + virDomainDimmDefPtr dimm; } data; }; @@ -1982,6 +1987,28 @@ struct _virDomainHugePage { unsigned long long size; /* hugepage size in KiB */ }; +typedef enum { + VIR_DOMAIN_MEMORY_BACKEND_RAM = 0, + VIR_DOMAIN_MEMORY_BACKEND_FILE, + + VIR_DOMAIN_MEMORY_BACKEND_LAST +} virDomainMemoryBackend; + +struct _virDomainDimmDef { + char *driver; + int addr; /* default value: 0, means that address is auto-allocated*/ + int node; + int slot; + + struct { + virDomainMemoryBackend type; + unsigned long long size; /* in kibibytes */ + char *mem_path; + } backend; + + virDomainDeviceInfo info; +}; + typedef struct _virDomainCputune virDomainCputune; typedef virDomainCputune *virDomainCputunePtr; @@ -2161,6 +2188,9 @@ struct _virDomainDef { size_t nshmems; virDomainShmemDefPtr *shmems; + size_t ndimms; + virDomainDimmDefPtr *dimms; + /* Only 1 */ virDomainWatchdogDefPtr watchdog; virDomainMemballoonDefPtr memballoon; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 5994558..d017a84 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6991,6 +6991,7 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm, dev->data.chr = NULL; break; + case VIR_DOMAIN_DEVICE_DIMM: case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_FS: case VIR_DOMAIN_DEVICE_INPUT: @@ -7066,6 +7067,7 @@ qemuDomainDetachDeviceLive(virDomainObjPtr vm, case VIR_DOMAIN_DEVICE_CHR: ret = qemuDomainDetachChrDevice(driver, vm, dev->data.chr); break; + case VIR_DOMAIN_DEVICE_DIMM: case VIR_DOMAIN_DEVICE_FS: case VIR_DOMAIN_DEVICE_INPUT: case VIR_DOMAIN_DEVICE_SOUND: @@ -7188,6 +7190,7 @@ qemuDomainUpdateDeviceLive(virConnectPtr conn, case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_TPM: case VIR_DOMAIN_DEVICE_PANIC: + case VIR_DOMAIN_DEVICE_DIMM: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("live update of device '%s' is not supported"), @@ -7309,6 +7312,7 @@ qemuDomainAttachDeviceConfig(virQEMUCapsPtr qemuCaps, dev->data.fs = NULL; break; + case VIR_DOMAIN_DEVICE_DIMM: case VIR_DOMAIN_DEVICE_INPUT: case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_VIDEO: @@ -7425,6 +7429,7 @@ qemuDomainDetachDeviceConfig(virDomainDefPtr vmdef, virDomainFSDefFree(fs); break; + case VIR_DOMAIN_DEVICE_DIMM: case VIR_DOMAIN_DEVICE_INPUT: case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_VIDEO: @@ -7543,6 +7548,7 @@ qemuDomainUpdateDeviceConfig(virQEMUCapsPtr qemuCaps, case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_TPM: case VIR_DOMAIN_DEVICE_PANIC: + case VIR_DOMAIN_DEVICE_DIMM: case VIR_DOMAIN_DEVICE_LAST: virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("persistent update of device '%s' is not supported"), diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 9ed96dc..f83cb1c 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -2885,6 +2885,7 @@ qemuDomainRemoveDevice(virQEMUDriverPtr driver, ret = qemuDomainRemoveChrDevice(driver, vm, dev->data.chr); break; + case VIR_DOMAIN_DEVICE_DIMM: case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_FS: -- 1.9.3

virDomainDimmDefFree - free memory allocated for dimm virDomainDimmDefParseXML - parse job type virDomainDimmDefFormat - output job type Signed-off-by: Zhu Guihua <zhugh.fnst@cn.fujitsu.com> --- src/conf/domain_conf.c | 196 +++++++++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 4 + src/libvirt_private.syms | 3 + 3 files changed, 203 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index ea41cbd..d4da728 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -751,6 +751,10 @@ VIR_ENUM_IMPL(virDomainRNGBackend, "random", "egd"); +VIR_ENUM_IMPL(virDomainMemoryBackend, VIR_DOMAIN_MEMORY_BACKEND_LAST, + "ram", + "file"); + VIR_ENUM_IMPL(virDomainTPMModel, VIR_DOMAIN_TPM_MODEL_LAST, "tpm-tis") @@ -1733,6 +1737,22 @@ void virDomainMemballoonDefFree(virDomainMemballoonDefPtr def) VIR_FREE(def); } +void virDomainDimmDefFree(virDomainDimmDefPtr def) +{ + if (!def) + return; + + if (def->driver) + VIR_FREE(def->driver); + if (def->backend.type == VIR_DOMAIN_MEMORY_BACKEND_FILE && + def->backend.mem_path) + VIR_FREE(def->backend.mem_path); + + virDomainDeviceInfoClear(&def->info); + + VIR_FREE(def); +} + void virDomainNVRAMDefFree(virDomainNVRAMDefPtr def) { if (!def) @@ -1983,6 +2003,8 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def) virDomainPanicDefFree(def->data.panic); break; case VIR_DOMAIN_DEVICE_DIMM: + virDomainDimmDefFree(def->data.dimm); + break; case VIR_DOMAIN_DEVICE_LAST: case VIR_DOMAIN_DEVICE_NONE: break; @@ -10203,6 +10225,132 @@ virDomainMemballoonDefParseXML(xmlNodePtr node, goto cleanup; } +virDomainDimmDefPtr +virDomainDimmDefNew(void) +{ + virDomainDimmDefPtr def = NULL; + + if (VIR_ALLOC(def) < 0) + return NULL; + + return def; +} + +/* Parse the XML definition for a dimm + * + * The XML looks like this: + * + * <dimm driver='pc-dimm' addr='0' node='0' slot='1'> + * <backend type='ram' size='128000'/> + * </dimm> + * + */ +static virDomainDimmDefPtr +virDomainDimmDefParseXML(xmlNodePtr node, + virDomainDefPtr def) +{ + virDomainDimmDefPtr dev; + xmlNodePtr cur; + char *driver = NULL; + char *addr = NULL; + char *nodeid = NULL; + char *slot = NULL; + char *type = NULL; + char *size = NULL; + char *mem_path = NULL; + + if (!(dev = virDomainDimmDefNew())) + return NULL; + + driver = virXMLPropString(node, "driver"); + if (driver == NULL) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing memory device driver")); + goto error; + } + + addr = virXMLPropString(node, "addr"); + nodeid = virXMLPropString(node, "node"); + slot = virXMLPropString(node, "slot"); + + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + type = virXMLPropString(cur, "type"); + if (type != NULL) { + if ((int)(dev->backend.type = virDomainMemoryBackendTypeFromString(type)) < 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown memory backend type '%s'"), type); + goto error; + } + } else { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing memory backend")); + goto error; + } + + size = virXMLPropString(cur, "size"); + if (size == NULL) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("missing memory backend's size")); + goto error; + } else { + dev->backend.size = atoi (size); + } + + if (dev->backend.type == VIR_DOMAIN_MEMORY_BACKEND_FILE) { + mem_path = virXMLPropString(cur, "mem_path"); + if (mem_path == NULL) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("mem_path property not set")); + goto error; + } else { + dev->backend.mem_path = mem_path; + } + } + } + + cur = cur->next; + } + + dev->driver = driver; + if (!addr) + dev->addr = 0; + else + dev->addr = atoi (addr); + if (!nodeid) + dev->node = 0; + else + dev->node = atoi (nodeid); + if (!slot) + dev->slot = virDomainDimmGetFreeSlot(def); + else + dev->slot = atoi (slot); + cleanup: + driver = NULL; + addr = NULL; + nodeid = NULL; + slot = NULL; + type = NULL; + size = NULL; + mem_path = NULL; + + VIR_FREE(driver); + VIR_FREE(addr); + VIR_FREE(nodeid); + VIR_FREE(slot); + VIR_FREE(type); + VIR_FREE(size); + VIR_FREE(mem_path); + + return dev; + + error: + virDomainDimmDefFree(dev); + dev = NULL; + goto cleanup; +} + static virDomainNVRAMDefPtr virDomainNVRAMDefParseXML(xmlNodePtr node, unsigned int flags) @@ -11187,6 +11335,9 @@ virDomainDeviceDefParse(const char *xmlStr, goto error; break; case VIR_DOMAIN_DEVICE_DIMM: + if (!(dev->data.dimm = virDomainDimmDefParseXML(node, (virDomainDefPtr)def))) + goto error; + break; case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LAST: break; @@ -18348,6 +18499,45 @@ virDomainMemballoonDefFormat(virBufferPtr buf, } static int +virDomainDimmDefFormat(virBufferPtr buf, + virDomainDimmDefPtr def, + unsigned int flags) +{ + char *addr = NULL; + char *node = NULL; + char *slot = NULL; + + ignore_value(virAsprintf(&addr, "%d", def->addr)); + ignore_value(virAsprintf(&node, "%d", def->node)); + ignore_value(virAsprintf(&slot, "%d", def->slot)); + + virBufferAsprintf(buf, "<dimm driver='%s'", def->driver); + + virBufferEscapeString(buf, " addr='%s'", addr); + virBufferEscapeString(buf, " node='%s'", node); + virBufferEscapeString(buf, " slot='%s'", slot); + virBufferAddLit(buf, ">\n"); + virBufferAdjustIndent(buf, 2); + + if (virDomainDeviceInfoFormat(buf, &def->info, flags) < 0) + return -1; + + virBufferAddLit(buf, "<backend"); + virBufferEscapeString(buf, " type='%s'", + virDomainMemoryBackendTypeToString(def->backend.type)); + virBufferAsprintf(buf, " size='%llu' unit='KiB'", def->backend.size); + if (def->backend.type == VIR_DOMAIN_MEMORY_BACKEND_FILE) { + virBufferEscapeString(buf, " mem_path='%s'", def->backend.mem_path); + } + virBufferAddLit(buf, "/>\n"); + + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</dimm>\n"); + + return 0; +} + +static int virDomainNVRAMDefFormat(virBufferPtr buf, virDomainNVRAMDefPtr def, unsigned int flags) @@ -20092,6 +20282,10 @@ virDomainDefFormatInternal(virDomainDefPtr def, if (virDomainShmemDefFormat(buf, def->shmems[n], flags) < 0) goto error; + for (n = 0; n < def->ndimms; n++) + if (virDomainDimmDefFormat(buf, def->dimms[n], flags) < 0) + goto error; + virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "</devices>\n"); @@ -21496,6 +21690,8 @@ virDomainDeviceDefCopy(virDomainDeviceDefPtr src, rc = virDomainPanicDefFormat(&buf, src->data.panic); break; case VIR_DOMAIN_DEVICE_DIMM: + rc = virDomainDimmDefFormat(&buf, src->data.dimm, flags); + break; case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_SMARTCARD: case VIR_DOMAIN_DEVICE_MEMBALLOON: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index c4ebbd4..4864dc3 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2382,6 +2382,8 @@ int virDomainChrSourceDefCopy(virDomainChrSourceDefPtr src, void virDomainSoundCodecDefFree(virDomainSoundCodecDefPtr def); void virDomainSoundDefFree(virDomainSoundDefPtr def); void virDomainMemballoonDefFree(virDomainMemballoonDefPtr def); +virDomainDimmDefPtr virDomainDimmDefNew(void); +void virDomainDimmDefFree(virDomainDimmDefPtr dimm); void virDomainNVRAMDefFree(virDomainNVRAMDefPtr def); void virDomainWatchdogDefFree(virDomainWatchdogDefPtr def); void virDomainVideoDefFree(virDomainVideoDefPtr def); @@ -2845,6 +2847,8 @@ VIR_ENUM_DECL(virDomainChrSpicevmc) VIR_ENUM_DECL(virDomainSoundCodec) VIR_ENUM_DECL(virDomainSoundModel) VIR_ENUM_DECL(virDomainMemballoonModel) +VIR_ENUM_DECL(virDomainMemoryBackend) +VIR_ENUM_DECL(virDomainDimm) VIR_ENUM_DECL(virDomainSmbiosMode) VIR_ENUM_DECL(virDomainWatchdogModel) VIR_ENUM_DECL(virDomainWatchdogAction) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index a2eec83..c5daf5b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -216,6 +216,7 @@ virDomainDeviceGetInfo; virDomainDeviceInfoCopy; virDomainDeviceInfoIterate; virDomainDeviceTypeToString; +virDomainDimmDefFree; virDomainDiskBusTypeToString; virDomainDiskCacheTypeFromString; virDomainDiskCacheTypeToString; @@ -323,6 +324,8 @@ virDomainLockFailureTypeFromString; virDomainLockFailureTypeToString; virDomainMemballoonModelTypeFromString; virDomainMemballoonModelTypeToString; +virDomainMemoryBackendTypeFromString; +virDomainMemoryBackendTypeToString; virDomainNetAppendIpAddress; virDomainNetDefFormat; virDomainNetDefFree; -- 1.9.3

virDomainDimmFind - to find a dimm within VM def virDomainDimmInsert - wrapper for inserting a new dimm into VM def virDomainDimmRemove - wrapper for removing dimm from VM def Signed-off-by: Zhu Guihua <zhugh.fnst@cn.fujitsu.com> --- src/conf/domain_conf.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 13 +++++++++++ src/libvirt_private.syms | 3 +++ 3 files changed, 76 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index d4da728..ff39b70 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -11790,6 +11790,66 @@ virDomainHasDiskMirror(virDomainObjPtr vm) return false; } +int +virDomainDimmInsert(virDomainDefPtr vmdef, + virDomainDimmDefPtr dimm) +{ + return VIR_APPEND_ELEMENT(vmdef->dimms, vmdef->ndimms, dimm); +} + +bool +virDomainDimmEquals(virDomainDimmDefPtr src, + virDomainDimmDefPtr tgt) +{ + bool ret = false; + + if (!src || !tgt) + return src == tgt; + + if (src->slot == tgt->slot) + ret = true; + + return ret; +} + +virDomainDimmDefPtr +virDomainDimmFind(virDomainDefPtr def, + virDomainDimmDefPtr target) +{ + virDomainDimmDefPtr dimm; + size_t i; + + for (i = 0; i < def->ndimms; i++) { + dimm = def->dimms[i]; + + if (virDomainDimmEquals(dimm, target)) + return dimm; + } + + return NULL; +} + +virDomainDimmDefPtr +virDomainDimmRemove(virDomainDefPtr vmdef, + virDomainDimmDefPtr dimm) +{ + virDomainDimmDefPtr ret; + size_t i; + + for (i = 0; i < vmdef->ndimms; i++) { + ret = vmdef->dimms[i]; + + if (virDomainDimmEquals(ret, dimm)) + break; + } + + if (i == vmdef->ndimms) + return NULL; + + VIR_DELETE_ELEMENT(vmdef->dimms, i, vmdef->ndimms); + return ret; +} + int virDomainNetInsert(virDomainDefPtr def, virDomainNetDefPtr net) { /* hostdev net devices must also exist in the hostdevs array */ diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 4864dc3..b46b28a 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2602,6 +2602,19 @@ int virDomainDiskSourceParse(xmlNodePtr node, bool virDomainHasDiskMirror(virDomainObjPtr vm); +int +virDomainDimmInsert(virDomainDefPtr vmdef, + virDomainDimmDefPtr dimm); +bool +virDomainDimmEquals(virDomainDimmDefPtr src, + virDomainDimmDefPtr tgt); +virDomainDimmDefPtr +virDomainDimmFind(virDomainDefPtr def, + virDomainDimmDefPtr dimm); +virDomainDimmDefPtr +virDomainDimmRemove(virDomainDefPtr def, + virDomainDimmDefPtr dimm); + int virDomainNetFindIdx(virDomainDefPtr def, virDomainNetDefPtr net); virDomainNetDefPtr virDomainNetFind(virDomainDefPtr def, const char *device); int virDomainNetInsert(virDomainDefPtr def, virDomainNetDefPtr net); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c5daf5b..092c1c6 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -217,6 +217,9 @@ virDomainDeviceInfoCopy; virDomainDeviceInfoIterate; virDomainDeviceTypeToString; virDomainDimmDefFree; +virDomainDimmFind; +virDomainDimmInsert; +virDomainDimmRemove; virDomainDiskBusTypeToString; virDomainDiskCacheTypeFromString; virDomainDiskCacheTypeToString; -- 1.9.3

The config level requires an insert or remove from domain definition structure. Signed-off-by: Zhu Guihua <zhugh.fnst@cn.fujitsu.com> --- src/qemu/qemu_driver.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index d017a84..a7a50e0 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7313,6 +7313,11 @@ qemuDomainAttachDeviceConfig(virQEMUCapsPtr qemuCaps, break; case VIR_DOMAIN_DEVICE_DIMM: + if (virDomainDimmInsert(vmdef, dev->data.dimm) < 0) + return -1; + dev->data.dimm = NULL; + break; + case VIR_DOMAIN_DEVICE_INPUT: case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_VIDEO: @@ -7349,6 +7354,7 @@ qemuDomainDetachDeviceConfig(virDomainDefPtr vmdef, virDomainControllerDefPtr cont, det_cont; virDomainChrDefPtr chr; virDomainFSDefPtr fs; + virDomainDimmDefPtr dimm; int idx; switch ((virDomainDeviceType) dev->type) { @@ -7430,6 +7436,14 @@ qemuDomainDetachDeviceConfig(virDomainDefPtr vmdef, break; case VIR_DOMAIN_DEVICE_DIMM: + if (!(dimm = virDomainDimmRemove(vmdef, dev->data.dimm))) + return -1; + + virDomainDimmDefFree(dimm); + virDomainDimmDefFree(dev->data.dimm); + dev->data.dimm = NULL; + break; + case VIR_DOMAIN_DEVICE_INPUT: case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_VIDEO: -- 1.9.3

The function being introduced is responsible for excuting 'object_add' command to hot add memory backend. Signed-off-by: Zhu Guihua <zhugh.fnst@cn.fujitsu.com> --- src/qemu/qemu_monitor.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 4 ++++ 2 files changed, 58 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 6882a50..1db8ad0 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4165,6 +4165,60 @@ int qemuMonitorGetTPMTypes(qemuMonitorPtr mon, return qemuMonitorJSONGetTPMTypes(mon, tpmtypes); } +int qemuMonitorAddMemoryBackend(qemuMonitorPtr mon, + const char *objID, + virDomainDimmDefPtr dimm) +{ + VIR_DEBUG("mon=%p objID=%s dimm=%p", + mon, objID, dimm); + + virJSONValuePtr props = NULL; + const char *type = NULL; + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + goto cleanup; + } + + if (!mon->json) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("JSON monitor is required")); + goto cleanup; + } + + if (!(props = virJSONValueNewObject())) + goto cleanup; + + switch (dimm->backend.type) { + case VIR_DOMAIN_MEMORY_BACKEND_RAM: + type = "memory-backend-ram"; + if ( virJSONValueObjectAppendNumberUlong(props, + "size", dimm->backend.size * 1024) < 0) + goto cleanup; + break; + + case VIR_DOMAIN_MEMORY_BACKEND_FILE: + type = "memory-backend-file"; + if (virJSONValueObjectAppendNumberUlong(props, "size", + dimm->backend.size * 1024) < 0) + goto cleanup; + if (virJSONValueObjectAppendString(props, "mem-path", + dimm->backend.mem_path) < 0) + goto cleanup; + break; + + default: + break; + } + + return qemuMonitorAddObject(mon, type, objID, props); + + cleanup: + virJSONValueFree(props); + return -1; +} + int qemuMonitorAttachCharDev(qemuMonitorPtr mon, const char *chrID, virDomainChrSourceDefPtr chr) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 133d42d..9a69611 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -863,6 +863,10 @@ int qemuMonitorGetTPMModels(qemuMonitorPtr mon, int qemuMonitorGetTPMTypes(qemuMonitorPtr mon, char ***tpmtypes); +int qemuMonitorAddMemoryBackend(qemuMonitorPtr mon, + const char *objID, + virDomainDimmDefPtr dimm); + int qemuMonitorAttachCharDev(qemuMonitorPtr mon, const char *chrID, virDomainChrSourceDefPtr chr); -- 1.9.3

This function used to set a alias name for memory device. Signed-off-by: Zhu Guihua <zhugh.fnst@cn.fujitsu.com> --- src/qemu/qemu_command.c | 32 ++++++++++++++++++++++++++++++++ src/qemu/qemu_command.h | 3 +++ 2 files changed, 35 insertions(+) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 536abc3..68fdab7 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -946,6 +946,34 @@ qemuAssignDeviceChrAlias(virDomainDefPtr def, } int +qemuAssignDeviceDimmAlias(virDomainDefPtr def, + virDomainDimmDefPtr dimm, + int idx) +{ + if (idx == -1) { + size_t i; + idx = 0; + + for (i = 0; i < def->ndimms; i++) { + int thisidx; + if ((thisidx = qemuDomainDeviceAliasIndex(&def->dimms[i]->info, "dimm")) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unable to determine device index from dimm device")); + return -1; + } + + if (thisidx >= idx) + idx = thisidx + 1; + } + } + + if (virAsprintf(&dimm->info.alias, "dimm%d", idx) < 0) + return -1; + + return 0; +} + +int qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps) { size_t i; @@ -1039,6 +1067,10 @@ qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps) if (virAsprintf(&def->rngs[i]->info.alias, "rng%zu", i) < 0) return -1; } + for (i = 0; i < def->ndimms; i++) { + if (qemuAssignDeviceDimmAlias(def, def->dimms[i], i) < 0) + return -1; + } if (def->tpm) { if (virAsprintf(&def->tpm->info.alias, "tpm%d", 0) < 0) return -1; diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index dcc7127..512d444 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -264,6 +264,9 @@ int qemuAssignDeviceRedirdevAlias(virDomainDefPtr def, virDomainRedirdevDefPtr r int qemuAssignDeviceChrAlias(virDomainDefPtr def, virDomainChrDefPtr chr, ssize_t idx); +int qemuAssignDeviceDimmAlias(virDomainDefPtr def, + virDomainDimmDefPtr dimm, + int idx); int qemuParseKeywords(const char *str, -- 1.9.3

This patch adds a new capability bit QEMU_CAPS_PC_DIMM. Signed-off-by: Zhu Guihua <zhugh.fnst@cn.fujitsu.com> --- src/qemu/qemu_capabilities.c | 3 +++ src/qemu/qemu_capabilities.h | 1 + 2 files changed, 4 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 13f3cd3..42e7e85 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -277,6 +277,7 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST, "vmware-svga.vgamem_mb", "qxl.vgamem_mb", "qxl-vga.vgamem_mb", + "pc-dimm", ); @@ -1524,6 +1525,7 @@ struct virQEMUCapsStringFlags virQEMUCapsObjectTypes[] = { { "usb-audio", QEMU_CAPS_OBJECT_USB_AUDIO }, { "iothread", QEMU_CAPS_OBJECT_IOTHREAD}, { "ivshmem", QEMU_CAPS_DEVICE_IVSHMEM }, + {"pc-dimm", QEMU_CAPS_DEVICE_PC_DIMM}, }; static struct virQEMUCapsStringFlags virQEMUCapsObjectPropsVirtioBlk[] = { @@ -3121,6 +3123,7 @@ virQEMUCapsInitQMPBasic(virQEMUCapsPtr qemuCaps) virQEMUCapsSet(qemuCaps, QEMU_CAPS_DUMP_GUEST_CORE); virQEMUCapsSet(qemuCaps, QEMU_CAPS_VNC_SHARE_POLICY); virQEMUCapsSet(qemuCaps, QEMU_CAPS_HOST_PCI_MULTIDOMAIN); + virQEMUCapsSet(qemuCaps, QEMU_CAPS_DEVICE_PC_DIMM); } /* Capabilities that are architecture depending diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 12e1688..bab0cca 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -223,6 +223,7 @@ typedef enum { QEMU_CAPS_VMWARE_SVGA_VGAMEM = 181, /* -device vmware-svga.vgamem_mb */ QEMU_CAPS_QXL_VGAMEM = 182, /* -device qxl.vgamem_mb */ QEMU_CAPS_QXL_VGA_VGAMEM = 183, /* -device qxl-vga.vgamem_mb */ + QEMU_CAPS_DEVICE_PC_DIMM = 184, /* -device pc-dimm */ QEMU_CAPS_LAST, /* this must always be the last item */ } virQEMUCapsFlags; -- 1.9.3

qemuBuildDimmDeviceStr being introduced is responsible for creating command line argument for '-device' for given dimm device. Signed-off-by: Zhu Guihua <zhugh.fnst@cn.fujitsu.com> --- src/qemu/qemu_command.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_command.h | 4 ++++ 2 files changed, 54 insertions(+) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 68fdab7..ff72f46 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -7735,6 +7735,49 @@ qemuBuildChrDeviceCommandLine(virCommandPtr cmd, return 0; } +int +qemuBuildDimmDeviceStr(char **deviceStr, + virDomainDimmDefPtr dev, + virQEMUCapsPtr qemuCaps) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PC_DIMM)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("%s not supported in this QEMU binary"), dev->driver); + goto error; + } + + virBufferAsprintf(&buf, "%s,id=%s,addr=%d,node=%d,slot=%d,memdev=obj%s", + dev->driver, dev->info.alias, dev->addr, + dev->node, dev->slot, dev->info.alias); + + if (virBufferCheckError(&buf) < 0) + goto error; + + *deviceStr = virBufferContentAndReset(&buf); + return 0; + + error: + virBufferFreeAndReset(&buf); + return -1; +} + +static int +qemuBuildDimmDeviceCommandLine(virCommandPtr cmd, + virDomainDimmDefPtr dev, + virQEMUCapsPtr qemuCaps) +{ + char *devstr = NULL; + + if (qemuBuildDimmDeviceStr(&devstr, dev, qemuCaps) < 0) + return -1; + + virCommandAddArgList(cmd, "-device", devstr, NULL); + VIR_FREE(devstr); + return 0; +} + static int qemuBuildDomainLoaderCommandLine(virCommandPtr cmd, virDomainDefPtr def, @@ -9887,6 +9930,13 @@ qemuBuildCommandLine(virConnectPtr conn, goto error; } + if (def->ndimms) { + for (i = 0; i < def->ndimms; i++) { + if (qemuBuildDimmDeviceCommandLine(cmd, def->dimms[i], qemuCaps) < 0) + goto error; + } + } + if (def->nvram) { if (ARCH_IS_PPC64(def->os.arch) && STRPREFIX(def->os.machine, "pseries")) { diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 512d444..c9d2eb7 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -148,6 +148,10 @@ char *qemuBuildMemballoonDevStr(virDomainDefPtr domainDef, virDomainMemballoonDefPtr dev, virQEMUCapsPtr qemuCaps); +int qemuBuildDimmDeviceStr(char **deviceStr, + virDomainDimmDefPtr dimm, + virQEMUCapsPtr qemuCaps); + char *qemuBuildUSBInputDevStr(virDomainDefPtr domainDef, virDomainInputDefPtr dev, virQEMUCapsPtr qemuCaps); -- 1.9.3

This patch implements live hotplug of a memory device. Signed-off-by: Zhu Guihua <zhugh.fnst@cn.fujitsu.com> --- src/qemu/qemu_driver.c | 6 +++++ src/qemu/qemu_hotplug.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hotplug.h | 5 ++++ 3 files changed, 81 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a7a50e0..76ff7b5 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6992,6 +6992,12 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm, break; case VIR_DOMAIN_DEVICE_DIMM: + ret = qemuDomainAttachDimmDevice(driver, vm, + dev->data.dimm); + if (!ret) + dev->data.dimm = NULL; + break; + case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_FS: case VIR_DOMAIN_DEVICE_INPUT: diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index f83cb1c..b7ca41e 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1776,6 +1776,76 @@ static virDomainNetDefPtr *qemuDomainFindNet(virDomainObjPtr vm, static int +qemuDomainDimmInsert(virDomainDefPtr vmdef, + virDomainDimmDefPtr dimm) +{ + if (virDomainDimmFind(vmdef, dimm)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("pc-dimm already exists")); + return -1; + } + + if (virDomainDimmInsert(vmdef, dimm) < 0) + return -1; + + return 0; +} + +int qemuDomainAttachDimmDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDimmDefPtr dimm) +{ + int ret = -1; + char *devstr = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + virDomainDefPtr vmdef = vm->def; + char *objAlias = NULL; + + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("qemu does not support -device")); + goto cleanup;; + } + + if (qemuAssignDeviceDimmAlias(vmdef, dimm, -1) < 0) + goto cleanup; + + if (virAsprintf(&objAlias, "obj%s", dimm->info.alias) < 0) + goto cleanup; + + if (qemuBuildDimmDeviceStr(&devstr, dimm, priv->qemuCaps) < 0) + goto cleanup; + + if (qemuDomainDimmInsert(vmdef, dimm) < 0) + goto cleanup; + + qemuDomainObjEnterMonitor(driver, vm); + + if (qemuMonitorAddMemoryBackend(priv->mon, objAlias, dimm) < 0) { + qemuDomainObjExitMonitor(driver, vm); + goto cleanup; + } + + if (devstr && qemuMonitorAddDevice(priv->mon, devstr) < 0) { + qemuDomainObjExitMonitor(driver, vm); + goto cleanup; + } + + qemuDomainObjExitMonitor(driver, vm); + virDomainAuditMemory(vm, vm->def->mem.cur_balloon, + vm->def->mem.cur_balloon + dimm->backend.size, + "update", true); + + ignore_value(virBitmapSetBit(vm->def->mem.dimm_slot_map, dimm->slot)); + ret = 0; + + cleanup: + VIR_FREE(devstr); + VIR_FREE(objAlias); + return ret; +} + +static int qemuDomainChangeNetBridge(virDomainObjPtr vm, virDomainNetDefPtr olddev, virDomainNetDefPtr newdev) diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 19ab9a0..1e9f7b3 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -107,6 +107,11 @@ virDomainChrDefPtr qemuDomainChrRemove(virDomainDefPtr vmdef, virDomainChrDefPtr chr); +int +qemuDomainAttachDimmDevice(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDimmDefPtr dimm); + int qemuDomainRemoveDevice(virQEMUDriverPtr driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev); -- 1.9.3

On Wed, Jan 21, 2015 at 16:20:16 +0800, Zhu Guihua wrote:
Now qemu has already supported memory hotplug, so this patchset will make libvirt support hotplug memory device for qemu driver.
As I'm already working on this I can see a few problems with your series.
First, add two parameters slots and maxmem for memory hotplug, and the xml format can be like this. <memory slot='10'>102400</memory> <maxMemory>1024000</maxMemory>
Second, memory device's xml format can be like this. <dimm driver='pc-dimm' addr='0' node='0' slot='1'>
Your code states that addr=0 auto-allocates the address, but I don't see any code that would retrieve it afterwards. Without that the migration can't work reliably.
<backend type='ram' size='131072'/>
Your code doesn't support specifying source nodes for the memory device. This wouldn't work with libvirt with numa pinnnig enabled. Aditionally the target numa node mode that is configured in the numatune section is not honored. The hugepage backend (memory-backend-file) part in your impl requires the user to specify the path for the memory device. Libvirt has a lot code that does this detection and it should be reused.
</dimm>
I'm pretty far in my effort so I think it's not worth for you to continue working on this feature. Peter

On Wed, 2015-01-21 at 10:50 +0100, Peter Krempa wrote:
On Wed, Jan 21, 2015 at 16:20:16 +0800, Zhu Guihua wrote:
Now qemu has already supported memory hotplug, so this patchset will make libvirt support hotplug memory device for qemu driver.
As I'm already working on this I can see a few problems with your series.
First, add two parameters slots and maxmem for memory hotplug, and the xml format can be like this. <memory slot='10'>102400</memory> <maxMemory>1024000</maxMemory>
Second, memory device's xml format can be like this. <dimm driver='pc-dimm' addr='0' node='0' slot='1'>
Your code states that addr=0 auto-allocates the address, but I don't see any code that would retrieve it afterwards. Without that the migration can't work reliably.
<backend type='ram' size='131072'/>
Your code doesn't support specifying source nodes for the memory device. This wouldn't work with libvirt with numa pinnnig enabled.
Aditionally the target numa node mode that is configured in the numatune section is not honored.
The hugepage backend (memory-backend-file) part in your impl requires the user to specify the path for the memory device. Libvirt has a lot code that does this detection and it should be reused.
</dimm>
I'm pretty far in my effort so I think it's not worth for you to continue working on this feature.
Thank you for pointing out the problems, and we hope the feature could be supported in libvirt as soon as possible. So, 1) could you tell us your schedule for this feature? 2) When can you send the patch series to the community? 3) If there are any else work we can do for this feature? Regards, Zhu
Peter

On Thu, Jan 22, 2015 at 16:24:12 +0800, Zhu Guihua wrote:
On Wed, 2015-01-21 at 10:50 +0100, Peter Krempa wrote:
On Wed, Jan 21, 2015 at 16:20:16 +0800, Zhu Guihua wrote:
Thank you for pointing out the problems, and we hope the feature could be supported in libvirt as soon as possible.
So, 1) could you tell us your schedule for this feature?
I hope the next release (1.2.13) will contain this feature. Libvirt's current release (1.2.12) is scheduled next week, so I'll have a complete dev cycle to add and fix all the bits.
2) When can you send the patch series to the community?
I'm expecting to be ready to send first version next week.
3) If there are any else work we can do for this feature?a
Not really, I'm almost ready. There are a few details that need tweaking. Peter
participants (2)
-
Peter Krempa
-
Zhu Guihua