@@ -648,6 +648,37 @@ STEXI
Hot-add dimm.
ETEXI
+ {
+ .name = "mem_increase",
+ .args_type = "pfx:s,num:s",
+ .params = "pfx num",
+ .help = "hot-plug num dimms of memory pool pfx",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_dimm_add_range,
+ },
+
+STEXI
+@item mem_increase @var{config}
+@findex mem_increase
+
+Hotplug dimms.
+ETEXI
+
+ {
+ .name = "mem_decrease",
+ .args_type = "pfx:s,num:s",
+ .params = "pfx num",
+ .help = "hot-unplug num dimms of memory pool pfx",
+ .user_print = monitor_user_noop,
+ .mhandler.cmd_new = do_dimm_del_range,
+ },
+
+STEXI
+@item mem_decrease @var{config}
+@findex mem_decrease
+
+Hot-unplug dimms.
+ETEXI
{
.name = "device_del",
@@ -166,6 +166,110 @@ int dimm_do(Monitor *mon, const QDict *qdict, bool add)
return 0;
}
+/* Find for dimm_do_range operation
+ DIMM_MIN_UNPOPULATED: used for finding next DIMM to hotplug
+ DIMM_MAX_POPULATED: used for finding next DIMM for hot-unplug
+ */
+
+DimmState *dimm_find_next(char *pfx, uint32_t mode)
+{
+ DeviceState *dev;
+ DimmState *slot, *ret;
+ const char *type;
+ uint32_t idx;
+
+ Error *err = NULL;
+ BusChild *kid;
+ BusState *bus = sysbus_get_default();
+ ret = NULL;
+
+ if (mode == DIMM_MIN_UNPOPULATED)
+ idx = MAX_DIMMS;
+ else if (mode == DIMM_MAX_POPULATED)
+ idx = 0;
+ else
+ return false;
+
+ QTAILQ_FOREACH(kid, &bus->children, sibling) {
+ dev = kid->child;
+ type = object_property_get_str(OBJECT(dev), "type", &err);
+ if (err) {
+ error_free(err);
+ fprintf(stderr, "error getting device type\n");
+ exit(1);
+ }
+
+ if (!strcmp(type, "dimm")) {
+ slot = DIMM(dev);
+ if (strstr(dev->id, pfx) && strcmp(dev->id, pfx)) {
+ if (mode == DIMM_MIN_UNPOPULATED &&
+ (slot->populated == false) &&
+ (slot->pending == false) &&
+ (idx > slot->idx)) {
+ idx = slot->idx;
+ ret = slot;
+ }
+ else if (mode == DIMM_MAX_POPULATED &&
+ (slot->populated == true) &&
+ (slot->pending == false) &&
+ (idx <= slot->idx)) {
+ idx = slot->idx;
+ ret = slot;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+int dimm_do_range(Monitor *mon, const QDict *qdict, bool add)
+{
+ DimmState *slot = NULL;
+ uint32_t mode;
+ uint32_t idx;
+ int num, ndimms;
+
+ char *pfx = (char*) qdict_get_try_str(qdict, "pfx");
+ if (!pfx) {
+ fprintf(stderr, "ERROR %s invalid pfx\n",__FUNCTION__);
+ return 1;
+ }
+
+ char *value = (char*) qdict_get_try_str(qdict, "num");
+ if (!value) {
+ fprintf(stderr, "ERROR %s invalid pfx\n",__FUNCTION__);
+ return 1;
+ }
+ num = atoi(value);
+
+ if (add)
+ mode = DIMM_MIN_UNPOPULATED;
+ else
+ mode = DIMM_MAX_POPULATED;
+
+ ndimms = 0;
+ while (ndimms < num) {
+ slot = dimm_find_next(pfx, mode);
+ if (slot == NULL) {
+ fprintf(stderr, "%s no further slot found for pool %s\n",
+ __FUNCTION__, pfx);
+ fprintf(stderr, "%s operated on %d / %d requested dimms\n",
+ __FUNCTION__, ndimms, num);
+ return 1;
+ }
+
+ if (add) {
+ dimm_activate(slot);
+ }
+ else {
+ dimm_deactivate(slot);
+ }
+ ndimms++;
+ idx++;
+ }
+
+ return 0;
+}
DimmState *dimm_find_from_idx(uint32_t idx)
{
DimmState *slot;
@@ -11,6 +11,11 @@
#define DIMM_BITMAP_BYTES (MAX_DIMMS + 7) / 8
#define DEFAULT_DIMMSIZE 1024*1024*1024
+enum {
+ DIMM_MIN_UNPOPULATED= 0,
+ DIMM_MAX_POPULATED = 1
+};
+
typedef enum {
DIMM_REMOVE_SUCCESS = 0,
DIMM_REMOVE_FAIL = 1,
@@ -61,5 +66,7 @@ void dimm_deactivate(DimmState *slot);
void dimm_scan_populated(void);
void dimm_notify(uint32_t idx, uint32_t event);
int dimm_set_populated(DimmState *s);
+DimmState *dimm_find_next(char *pfx, uint32_t mode);
+int dimm_do_range(Monitor *mon, const QDict *qdict, bool add);
#endif
@@ -4838,3 +4838,13 @@ int do_dimm_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
return dimm_do(mon, qdict, false);
}
+
+int do_dimm_add_range(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ return dimm_do_range(mon, qdict, true);
+}
+
+int do_dimm_del_range(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ return dimm_do_range(mon, qdict, false);
+}
@@ -88,5 +88,7 @@ int qmp_qom_get(Monitor *mon, const QDict *qdict, QObject **ret);
int do_dimm_add(Monitor *mon, const QDict *qdict, QObject **ret_data);
int do_dimm_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
+int do_dimm_add_range(Monitor *mon, const QDict *qdict, QObject **ret_data);
+int do_dimm_del_range(Monitor *mon, const QDict *qdict, QObject **ret_data);
#endif /* !MONITOR_H */
@@ -2306,3 +2306,43 @@ Example:
}
EQMP
+
+ {
+ .name = "mem-increase",
+ .args_type = "pfx:s,num:s",
+ .mhandler.cmd_new = do_dimm_add_range,
+ },
+SQMP
+mem-increase
+-------------
+
+Hotplug memory DIMMs from memory pool
+
+Will hotplug num memory DIMMs from pool with name pfx.
+
+Example:
+
+-> { "execute": "mem-increase", "arguments": { "pfx" : "pool", "num": "10" } }
+<- { "return": {} }
+
+EQMP
+
+ {
+ .name = "mem-decrease",
+ .args_type = "pfx:s,num:s",
+ .mhandler.cmd_new = do_dimm_del_range,
+ },
+SQMP
+mem-decrease
+-------------
+
+Hot-unplug memory DIMMs from memory pool
+
+Will hot-unplug num memory DIMMs from pool with name pfx.
+
+Example:
+
+-> { "execute": "mem-decrease", "arguments": { "pfx" : "pool", "num": "10" } }
+<- { "return": {} }
+
+EQMP
This implements batch monitor operations for hot-add and hot-remove. These are probably better suited for a higher-level management layer, but are useful for testing. Let me know if there is interest for such commands upstream. syntax: mem_increase poolid num will hotplug num dimms from pool poolid. This starts from lowest unpopulated physical memory (dimm) and trying to cover any existing physical holes. syntax: mem_decrease poolid num will hot-unplug num dimms from pool poolid, This starts from highest populated physical memory (dimm). Respective qmp commands are "mem-increase", "mem-decrease". Signed-off-by: Vasilis Liaskovitis <vasilis.liaskovitis@profitbricks.com> --- hmp-commands.hx | 31 ++++++++++++++++ hw/dimm.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/dimm.h | 7 ++++ monitor.c | 10 +++++ monitor.h | 2 + qmp-commands.hx | 40 +++++++++++++++++++++ 6 files changed, 194 insertions(+), 0 deletions(-)