]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - sys-utils/chmem.c
libmount: add support for MS_REMOUNT on --all
[thirdparty/util-linux.git] / sys-utils / chmem.c
index ad394ac4c4f21198977e3dee1c59823769195d17..861f6cfd22906de43eb08d560bcaadf0bec2c72e 100644 (file)
 #include <assert.h>
 #include <dirent.h>
 
-#include <c.h>
-#include <nls.h>
-#include <path.h>
-#include <strutils.h>
-#include <strv.h>
-#include <optutils.h>
-#include <closestream.h>
-#include <xalloc.h>
+#include "c.h"
+#include "nls.h"
+#include "path.h"
+#include "strutils.h"
+#include "strv.h"
+#include "optutils.h"
+#include "closestream.h"
+#include "xalloc.h"
 
 /* partial success, otherwise we return regular EXIT_{SUCCESS,FAILURE} */
 #define CHMEM_EXIT_SOMEOK              64
 
 #define _PATH_SYS_MEMORY               "/sys/devices/system/memory"
-#define _PATH_SYS_MEMORY_BLOCK_SIZE    _PATH_SYS_MEMORY "/block_size_bytes"
 
 struct chmem_desc {
+       struct path_cxt *sysmem;        /* _PATH_SYS_MEMORY handler */
        struct dirent   **dirs;
        int             ndirs;
        uint64_t        block_size;
@@ -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;
@@ -64,29 +97,56 @@ static void idxtostr(struct chmem_desc *desc, uint64_t idx, char *buf, size_t bu
        start = idx * desc->block_size;
        end = start + desc->block_size - 1;
        snprintf(buf, bufsz,
-                _("Memory Block %"SCNu64" (0x%016"PRIx64"-0x%016"PRIx64")"),
+                _("Memory Block %"PRIu64" (0x%016"PRIx64"-0x%016"PRIx64")"),
                 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 (ul_path_readf_buffer(desc->sysmem, line, sizeof(line), "%s/state", name) > 0
+                   && strncmp(onoff, line, 6) == 0)
                        continue;
+
+               if (desc->have_zones) {
+                       ul_path_readf_buffer(desc->sysmem, line, sizeof(line), "%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) {
+               rc = ul_path_writef_string(desc->sysmem, onoff, "%s/state", name);
+               if (rc != 0 && desc->verbose) {
                        if (enable)
                                fprintf(stdout, _("%s enable failed\n"), str);
                        else
@@ -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"));
@@ -132,8 +200,8 @@ static int chmem_range(struct chmem_desc *desc, int enable)
                if (index > desc->end)
                        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 (ul_path_readf_buffer(desc->sysmem, line, sizeof(line), "%s/state", name) > 0
+                   && strncmp(onoff, line, 6) == 0) {
                        if (desc->verbose && enable)
                                fprintf(stdout, _("%s already enabled\n"), str);
                        else if (desc->verbose && !enable)
@@ -141,8 +209,30 @@ static int chmem_range(struct chmem_desc *desc, int enable)
                        todo--;
                        continue;
                }
-               rc = path_write_str(onoff, _PATH_SYS_MEMORY"/%s/state", name);
-               if (rc == -1) {
+
+               if (desc->have_zones) {
+                       ul_path_readf_buffer(desc->sysmem, line, sizeof(line), "%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 = ul_path_writef_string(desc->sysmem, onoff, "%s/state", name);
+               if (rc != 0) {
                        if (enable)
                                warn(_("%s enable failed"), str);
                        else
@@ -168,12 +258,12 @@ static int filter(const struct dirent *de)
 
 static void read_info(struct chmem_desc *desc)
 {
-       char line[BUFSIZ];
+       char line[128];
 
        desc->ndirs = scandir(_PATH_SYS_MEMORY, &desc->dirs, filter, versionsort);
        if (desc->ndirs <= 0)
                err(EXIT_FAILURE, _("Failed to read %s"), _PATH_SYS_MEMORY);
-       path_read_str(line, sizeof(line), _PATH_SYS_MEMORY_BLOCK_SIZE);
+       ul_path_read_buffer(desc->sysmem, line, sizeof(line), "block_size_bytes");
        desc->block_size = strtoumax(line, NULL, 16);
 }
 
@@ -234,8 +324,11 @@ static void parse_parameter(struct chmem_desc *desc, char *param)
                errx(EXIT_FAILURE, _("Invalid range: %s"), param);
 }
 
-static void __attribute__((__noreturn__)) chmem_usage(FILE *out)
+static void __attribute__((__noreturn__)) usage(void)
 {
+       FILE *out = stdout;
+       size_t i;
+
        fputs(USAGE_HEADER, out);
        fprintf(out, _(" %s [options] [SIZE|RANGE|BLOCKRANGE]\n"), program_invocation_short_name);
 
@@ -243,23 +336,27 @@ static void __attribute__((__noreturn__)) chmem_usage(FILE *out)
        fputs(_("Set a particular size or range of memory online or offline.\n"), out);
 
        fputs(USAGE_OPTIONS, out);
-       fputs(_(" -e, --enable   enable memory\n"), out);
-       fputs(_(" -d, --disable  disable memory\n"), out);
-       fputs(_(" -b, --blocks   use memory blocks\n"), out);
-       fputs(_(" -v, --verbose  verbose output\n"), out);
-       fputs(USAGE_SEPARATOR, out);
-       fputs(USAGE_HELP, out);
-       fputs(USAGE_VERSION, out);
+       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 <name>  select memory zone (see below)\n"), out);
+       fputs(_(" -v, --verbose      verbose output\n"), out);
+       printf(USAGE_HELP_OPTIONS(20));
 
-       fprintf(out, USAGE_MAN_TAIL("chmem(8)"));
+       fputs(_("\nSupported zones:\n"), out);
+       for (i = 0; i < ARRAY_SIZE(zone_names); i++)
+               fprintf(out, " %s\n", zone_names[i]);
 
-       exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+       printf(USAGE_MAN_TAIL("chmem(8)"));
+
+       exit(EXIT_SUCCESS);
 }
 
 int main(int argc, char **argv)
 {
-       struct chmem_desc _desc = { }, *desc = &_desc;
-       int cmd = CMD_NONE;
+       struct chmem_desc _desc = { 0 }, *desc = &_desc;
+       int cmd = CMD_NONE, zone_id = -1;
+       char *zone = NULL;
        int c, rc;
 
        static const struct option longopts[] = {
@@ -269,6 +366,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}
        };
 
@@ -283,9 +381,14 @@ int main(int argc, char **argv)
        textdomain(PACKAGE);
        atexit(close_stdout);
 
+       ul_path_init_debug();
+       desc->sysmem = ul_new_path(_PATH_SYS_MEMORY);
+       if (!desc->sysmem)
+               err(EXIT_FAILURE, _("failed to initialize %s handler"), _PATH_SYS_MEMORY);
+
        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);
 
@@ -300,7 +403,7 @@ int main(int argc, char **argv)
                        desc->use_blocks = 1;
                        break;
                case 'h':
-                       chmem_usage(stdout);
+                       usage();
                        break;
                case 'v':
                        desc->verbose = 1;
@@ -308,20 +411,42 @@ 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);
                }
        }
 
-       if ((argc == 1) || (argc != optind + 1) || (cmd == CMD_NONE))
-               chmem_usage(stderr);
+       if ((argc == 1) || (argc != optind + 1) || (cmd == CMD_NONE)) {
+               warnx(_("bad usage"));
+               errtryhelp(EXIT_FAILURE);
+       }
 
        parse_parameter(desc, argv[optind]);
 
+
+       /* The valid_zones sysfs attribute was introduced with kernel 3.18 */
+       if (ul_path_access(desc->sysmem, F_OK, "memory0/valid_zones") == 0)
+               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);
+
+       ul_unref_path(desc->sysmem);
 
        return rc == 0 ? EXIT_SUCCESS :
                rc < 0 ? EXIT_FAILURE : CHMEM_EXIT_SOMEOK;