summaryrefslogtreecommitdiffstats
path: root/sys-utils/chmem.c
diff options
context:
space:
mode:
authorGerald Schaefer2017-09-27 19:44:44 +0200
committerKarel Zak2017-10-20 12:37:04 +0200
commit60a7e9e94e49215b1e6ac6e33b69c3bd0a426b5e (patch)
tree58c8429d9e8a24c4a1b92d72af1589f4c8fcc478 /sys-utils/chmem.c
parentlibmount: use eacess() rather than open() to check mtab/utab (diff)
downloadkernel-qcow2-util-linux-60a7e9e94e49215b1e6ac6e33b69c3bd0a426b5e.tar.gz
kernel-qcow2-util-linux-60a7e9e94e49215b1e6ac6e33b69c3bd0a426b5e.tar.xz
kernel-qcow2-util-linux-60a7e9e94e49215b1e6ac6e33b69c3bd0a426b5e.zip
lsmem/chmem: add memory zone awareness
With this patch, valid memory zones can be shown with lsmem, and chmem can set memory online/offline in a specific memory zone, if allowed by the kernel. The valid memory zones are read from the "valid_zones" sysfs attribute, and setting memory online to a specific zone is done by echoing "online_kernel" or "online_movable" to the "state" sysfs attribute, in addition to the previous "online". This patch also changes the default behavior of chmem, when setting memory online without specifying a memory zone. If valid, memory will be set online to the zone Movable. This zone is preferable for memory hotplug, as it makes memory offline much more likely to succeed. Signed-off-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Diffstat (limited to 'sys-utils/chmem.c')
-rw-r--r--sys-utils/chmem.c136
1 files changed, 128 insertions, 8 deletions
diff --git a/sys-utils/chmem.c b/sys-utils/chmem.c
index d9bc95cc1..2f0680de8 100644
--- a/sys-utils/chmem.c
+++ b/sys-utils/chmem.c
@@ -49,6 +49,7 @@ struct chmem_desc {
unsigned int use_blocks : 1;
unsigned int is_size : 1;
unsigned int verbose : 1;
+ unsigned int have_zones : 1;
};
enum {
@@ -57,6 +58,38 @@ enum {
CMD_NONE
};
+enum zone_id {
+ ZONE_DMA = 0,
+ ZONE_DMA32,
+ ZONE_NORMAL,
+ ZONE_HIGHMEM,
+ ZONE_MOVABLE,
+ ZONE_DEVICE,
+};
+
+static char *zone_names[] = {
+ [ZONE_DMA] = "DMA",
+ [ZONE_DMA32] = "DMA32",
+ [ZONE_NORMAL] = "Normal",
+ [ZONE_HIGHMEM] = "Highmem",
+ [ZONE_MOVABLE] = "Movable",
+ [ZONE_DEVICE] = "Device",
+};
+
+/*
+ * name must be null-terminated
+ */
+static int zone_name_to_id(const char *name)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(zone_names); i++) {
+ if (!strcasecmp(name, zone_names[i]))
+ return i;
+ }
+ return -1;
+}
+
static void idxtostr(struct chmem_desc *desc, uint64_t idx, char *buf, size_t bufsz)
{
uint64_t start, end;
@@ -68,22 +101,49 @@ static void idxtostr(struct chmem_desc *desc, uint64_t idx, char *buf, size_t bu
idx, start, end);
}
-static int chmem_size(struct chmem_desc *desc, int enable)
+static int chmem_size(struct chmem_desc *desc, int enable, int zone_id)
{
char *name, *onoff, line[BUFSIZ], str[BUFSIZ];
uint64_t size, index;
+ const char *zn;
int i, rc;
size = desc->size;
onoff = enable ? "online" : "offline";
i = enable ? 0 : desc->ndirs - 1;
+ if (enable && zone_id >= 0) {
+ if (zone_id == ZONE_MOVABLE)
+ onoff = "online_movable";
+ else
+ onoff = "online_kernel";
+ }
+
for (; i >= 0 && i < desc->ndirs && size; i += enable ? 1 : -1) {
name = desc->dirs[i]->d_name;
index = strtou64_or_err(name + 6, _("Failed to parse index"));
path_read_str(line, sizeof(line), _PATH_SYS_MEMORY "/%s/state", name);
- if (strcmp(onoff, line) == 0)
+ if (strncmp(onoff, line, 6) == 0)
continue;
+
+ if (desc->have_zones) {
+ path_read_str(line, sizeof(line),
+ _PATH_SYS_MEMORY "/%s/valid_zones", name);
+ if (zone_id >= 0) {
+ zn = zone_names[zone_id];
+ if (enable && !strcasestr(line, zn))
+ continue;
+ if (!enable && strncasecmp(line, zn, strlen(zn)))
+ continue;
+ } else if (enable) {
+ /* By default, use zone Movable for online, if valid */
+ if (strcasestr(line, zone_names[ZONE_MOVABLE]))
+ onoff = "online_movable";
+ else
+ onoff = "online";
+ }
+ }
+
idxtostr(desc, index, str, sizeof(str));
rc = path_write_str(onoff, _PATH_SYS_MEMORY"/%s/state", name);
if (rc == -1 && desc->verbose) {
@@ -115,15 +175,23 @@ static int chmem_size(struct chmem_desc *desc, int enable)
return size == 0 ? 0 : size == desc->size ? -1 : 1;
}
-static int chmem_range(struct chmem_desc *desc, int enable)
+static int chmem_range(struct chmem_desc *desc, int enable, int zone_id)
{
char *name, *onoff, line[BUFSIZ], str[BUFSIZ];
uint64_t index, todo;
+ const char *zn;
int i, rc;
todo = desc->end - desc->start + 1;
onoff = enable ? "online" : "offline";
+ if (enable && zone_id >= 0) {
+ if (zone_id == ZONE_MOVABLE)
+ onoff = "online_movable";
+ else
+ onoff = "online_kernel";
+ }
+
for (i = 0; i < desc->ndirs; i++) {
name = desc->dirs[i]->d_name;
index = strtou64_or_err(name + 6, _("Failed to parse index"));
@@ -133,7 +201,7 @@ static int chmem_range(struct chmem_desc *desc, int enable)
break;
idxtostr(desc, index, str, sizeof(str));
path_read_str(line, sizeof(line), _PATH_SYS_MEMORY "/%s/state", name);
- if (strcmp(onoff, line) == 0) {
+ if (strncmp(onoff, line, 6) == 0) {
if (desc->verbose && enable)
fprintf(stdout, _("%s already enabled\n"), str);
else if (desc->verbose && !enable)
@@ -141,6 +209,29 @@ static int chmem_range(struct chmem_desc *desc, int enable)
todo--;
continue;
}
+
+ if (desc->have_zones) {
+ path_read_str(line, sizeof(line),
+ _PATH_SYS_MEMORY "/%s/valid_zones", name);
+ if (zone_id >= 0) {
+ zn = zone_names[zone_id];
+ if (enable && !strcasestr(line, zn)) {
+ warnx(_("%s enable failed: Zone mismatch"), str);
+ continue;
+ }
+ if (!enable && strncasecmp(line, zn, strlen(zn))) {
+ warnx(_("%s disable failed: Zone mismatch"), str);
+ continue;
+ }
+ } else if (enable) {
+ /* By default, use zone Movable for online, if valid */
+ if (strcasestr(line, zone_names[ZONE_MOVABLE]))
+ onoff = "online_movable";
+ else
+ onoff = "online";
+ }
+ }
+
rc = path_write_str(onoff, _PATH_SYS_MEMORY"/%s/state", name);
if (rc == -1) {
if (enable)
@@ -237,6 +328,8 @@ static void parse_parameter(struct chmem_desc *desc, char *param)
static void __attribute__((__noreturn__)) usage(void)
{
FILE *out = stdout;
+ unsigned int i;
+
fputs(USAGE_HEADER, out);
fprintf(out, _(" %s [options] [SIZE|RANGE|BLOCKRANGE]\n"), program_invocation_short_name);
@@ -247,6 +340,14 @@ static void __attribute__((__noreturn__)) usage(void)
fputs(_(" -e, --enable enable memory\n"), out);
fputs(_(" -d, --disable disable memory\n"), out);
fputs(_(" -b, --blocks use memory blocks\n"), out);
+ fputs(_(" -z, --zone select memory zone ("), out);
+ for (i = 0; i < ARRAY_SIZE(zone_names); i++) {
+ fputs(zone_names[i], out);
+ if (i < ARRAY_SIZE(zone_names) - 1)
+ fputc('|', out);
+ }
+ fputs(")\n", out);
+
fputs(_(" -v, --verbose verbose output\n"), out);
fputs(USAGE_SEPARATOR, out);
printf(USAGE_HELP_OPTIONS(16));
@@ -259,7 +360,8 @@ static void __attribute__((__noreturn__)) usage(void)
int main(int argc, char **argv)
{
struct chmem_desc _desc = { }, *desc = &_desc;
- int cmd = CMD_NONE;
+ int cmd = CMD_NONE, zone_id = -1;
+ char *zone = NULL;
int c, rc;
static const struct option longopts[] = {
@@ -269,6 +371,7 @@ int main(int argc, char **argv)
{"help", no_argument, NULL, 'h'},
{"verbose", no_argument, NULL, 'v'},
{"version", no_argument, NULL, 'V'},
+ {"zone", required_argument, NULL, 'z'},
{NULL, 0, NULL, 0}
};
@@ -285,7 +388,7 @@ int main(int argc, char **argv)
read_info(desc);
- while ((c = getopt_long(argc, argv, "bdehvV", longopts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "bdehvVz:", longopts, NULL)) != -1) {
err_exclusive_options(c, longopts, excl, excl_st);
@@ -308,6 +411,9 @@ int main(int argc, char **argv)
case 'V':
printf(UTIL_LINUX_VERSION);
return EXIT_SUCCESS;
+ case 'z':
+ zone = xstrdup(optarg);
+ break;
default:
errtryhelp(EXIT_FAILURE);
}
@@ -320,10 +426,24 @@ int main(int argc, char **argv)
parse_parameter(desc, argv[optind]);
+ /* The valid_zones sysfs attribute was introduced with kernel 3.18 */
+ if (path_exist(_PATH_SYS_MEMORY "/memory0/valid_zones"))
+ desc->have_zones = 1;
+ else if (zone)
+ warnx(_("zone ignored, no valid_zones sysfs attribute present"));
+
+ if (zone && desc->have_zones) {
+ zone_id = zone_name_to_id(zone);
+ if (zone_id == -1) {
+ warnx(_("unknown memory zone: %s"), zone);
+ errtryhelp(EXIT_FAILURE);
+ }
+ }
+
if (desc->is_size)
- rc = chmem_size(desc, cmd == CMD_MEMORY_ENABLE ? 1 : 0);
+ rc = chmem_size(desc, cmd == CMD_MEMORY_ENABLE ? 1 : 0, zone_id);
else
- rc = chmem_range(desc, cmd == CMD_MEMORY_ENABLE ? 1 : 0);
+ rc = chmem_range(desc, cmd == CMD_MEMORY_ENABLE ? 1 : 0, zone_id);
return rc == 0 ? EXIT_SUCCESS :
rc < 0 ? EXIT_FAILURE : CHMEM_EXIT_SOMEOK;