]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - sys-utils/zramctl.c
misc: consolidate smartcols error messages
[thirdparty/util-linux.git] / sys-utils / zramctl.c
index a8d0bbdd701f2ce925e703e7bbb7dd254b277d36..4ae4742dd200d579df8b871299a98dbe75ad7f43 100644 (file)
@@ -34,6 +34,9 @@
 #include "sysfs.h"
 #include "optutils.h"
 #include "ismounted.h"
+#include "strv.h"
+#include "path.h"
+#include "pathnames.h"
 
 /*#define CONFIG_ZRAM_DEBUG*/
 
@@ -60,6 +63,9 @@ enum {
        COL_STREAMS,
        COL_ZEROPAGES,
        COL_MEMTOTAL,
+       COL_MEMLIMIT,
+       COL_MEMUSED,
+       COL_MIGRATED,
        COL_MOUNTPOINT
 };
 
@@ -72,15 +78,44 @@ static const struct colinfo infos[] = {
        [COL_STREAMS]   = { "STREAMS",      3, SCOLS_FL_RIGHT, N_("number of concurrent compress operations") },
        [COL_ZEROPAGES] = { "ZERO-PAGES",   3, SCOLS_FL_RIGHT, N_("empty pages with no allocated memory") },
        [COL_MEMTOTAL]  = { "TOTAL",        5, SCOLS_FL_RIGHT, N_("all memory including allocator fragmentation and metadata overhead") },
+       [COL_MEMLIMIT]  = { "MEM-LIMIT",    5, SCOLS_FL_RIGHT, N_("memory limit used to store compressed data") },
+       [COL_MEMUSED]   = { "MEM-USED",     5, SCOLS_FL_RIGHT, N_("memory zram have been consumed to store compressed data") },
+       [COL_MIGRATED]  = { "MIGRATED",     5, SCOLS_FL_RIGHT, N_("number of objects migrated by compaction") },
        [COL_MOUNTPOINT]= { "MOUNTPOINT",0.10, SCOLS_FL_TRUNC, N_("where the device is mounted") },
 };
 
 static int columns[ARRAY_SIZE(infos) * 2] = {-1};
 static int ncolumns;
 
+enum {
+       MM_ORIG_DATA_SIZE = 0,
+       MM_COMPR_DATA_SIZE,
+       MM_MEM_USED_TOTAL,
+       MM_MEM_LIMIT,
+       MM_MEM_USED_MAX,
+       MM_ZERO_PAGES,
+       MM_NUM_MIGRATED
+};
+
+static const char *mm_stat_names[] = {
+       [MM_ORIG_DATA_SIZE]  = "orig_data_size",
+       [MM_COMPR_DATA_SIZE] = "compr_data_size",
+       [MM_MEM_USED_TOTAL]  = "mem_used_total",
+       [MM_MEM_LIMIT]       = "mem_limit",
+       [MM_MEM_USED_MAX]    = "mem_used_max",
+       [MM_ZERO_PAGES]      = "zero_pages",
+       [MM_NUM_MIGRATED]    = "num_migrated"
+};
+
+
 struct zram {
        char    devname[32];
        struct sysfs_cxt sysfs;
+       char    **mm_stat;
+
+       unsigned int mm_stat_probed : 1,
+                    control_probed : 1,
+                    has_control : 1;   /* has /sys/class/zram-control/ */
 };
 
 #define ZRAM_EMPTY     { .devname = { '\0' }, .sysfs = UL_SYSFSCXT_EMPTY }
@@ -114,6 +149,15 @@ static int column_name_to_id(const char *name, size_t namesz)
        return -1;
 }
 
+static void zram_reset_stat(struct zram *z)
+{
+       if (z) {
+               strv_free(z->mm_stat);
+               z->mm_stat = NULL;
+               z->mm_stat_probed = 0;
+       }
+}
+
 static void zram_set_devname(struct zram *z, const char *devname, size_t n)
 {
        assert(z);
@@ -127,6 +171,18 @@ static void zram_set_devname(struct zram *z, const char *devname, size_t n)
 
        DBG(fprintf(stderr, "set devname: %s", z->devname));
        sysfs_deinit(&z->sysfs);
+       zram_reset_stat(z);
+}
+
+static int zram_get_devnum(struct zram *z)
+{
+       int n;
+
+       assert(z);
+
+       if (sscanf(z->devname, "/dev/zram%d", &n) == 1)
+               return n;
+       return -EINVAL;
 }
 
 static struct zram *new_zram(const char *devname)
@@ -145,6 +201,7 @@ static void free_zram(struct zram *z)
                return;
        DBG(fprintf(stderr, "free: %p", z));
        sysfs_deinit(&z->sysfs);
+       zram_reset_stat(z);
        free(z);
 }
 
@@ -158,6 +215,12 @@ static struct sysfs_cxt *zram_get_sysfs(struct zram *z)
                        return NULL;
                if (sysfs_init(&z->sysfs, devno, NULL))
                        return NULL;
+               if (*z->devname != '/') {
+                       /* canonicalize the device name according to /sys */
+                       char name[PATH_MAX];
+                       if (sysfs_get_devname(&z->sysfs, name, sizeof(name)))
+                               snprintf(z->devname, sizeof(z->devname), "/dev/%s", name);
+               }
        }
 
        return &z->sysfs;
@@ -212,6 +275,50 @@ static int zram_used(struct zram *z)
        return 0;
 }
 
+static int zram_has_control(struct zram *z)
+{
+       if (!z->control_probed) {
+               z->has_control = access(_PATH_SYS_CLASS "/zram-control/", F_OK) == 0 ? 1 : 0;
+               z->control_probed = 1;
+               DBG(fprintf(stderr, "zram-control: %s", z->has_control ? "yes" : "no"));
+       }
+
+       return z->has_control;
+}
+
+static int zram_control_add(struct zram *z)
+{
+       int n;
+
+       if (!zram_has_control(z))
+               return -ENOSYS;
+
+       n = path_read_s32(_PATH_SYS_CLASS "/zram-control/hot_add");
+       if (n < 0)
+               return n;
+
+       DBG(fprintf(stderr, "hot-add: %d", n));
+       zram_set_devname(z, NULL, n);
+       return 0;
+}
+
+static int zram_control_remove(struct zram *z)
+{
+       char str[sizeof stringify_value(INT_MAX)];
+       int n;
+
+       if (!zram_has_control(z))
+               return -ENOSYS;
+
+       n = zram_get_devnum(z);
+       if (n < 0)
+               return n;
+
+       DBG(fprintf(stderr, "hot-remove: %d", n));
+       snprintf(str, sizeof(str), "%d", n);
+       return path_write_str(str, _PATH_SYS_CLASS "/zram-control/hot_remove");
+}
+
 static struct zram *find_free_zram(void)
 {
        struct zram *z = new_zram(NULL);
@@ -221,7 +328,7 @@ static struct zram *find_free_zram(void)
        for (i = 0; isfree == 0; i++) {
                DBG(fprintf(stderr, "find free: checking zram%zu", i));
                zram_set_devname(z, NULL, i);
-               if (!zram_exist(z))
+               if (!zram_exist(z) && zram_control_add(z) != 0)
                        break;
                isfree = !zram_used(z);
        }
@@ -232,6 +339,55 @@ static struct zram *find_free_zram(void)
        return z;
 }
 
+static char *get_mm_stat(struct zram *z, size_t idx, int bytes)
+{
+       struct sysfs_cxt *sysfs;
+       const char *name;
+       uint64_t num;
+
+       assert(idx < ARRAY_SIZE(mm_stat_names));
+       assert(z);
+
+       sysfs = zram_get_sysfs(z);
+       if (!sysfs)
+               return NULL;
+
+       /* Linux >= 4.1 uses /sys/block/zram<id>/mm_stat */
+       if (!z->mm_stat && !z->mm_stat_probed) {
+               char *str;
+
+               str = sysfs_strdup(sysfs, "mm_stat");
+               if (str) {
+                       z->mm_stat = strv_split(str, " ");
+
+                       /* make sure kernel provides mm_stat as expected */
+                       if (strv_length(z->mm_stat) < ARRAY_SIZE(mm_stat_names)) {
+                               strv_free(z->mm_stat);
+                               z->mm_stat = NULL;
+                       }
+               }
+               z->mm_stat_probed = 1;
+               free(str);
+
+       }
+
+       if (z->mm_stat) {
+               if (bytes)
+                       return xstrdup(z->mm_stat[idx]);
+
+               num = strtou64_or_err(z->mm_stat[idx], _("Failed to parse mm_stat"));
+               return size_to_human_string(SIZE_SUFFIX_1LETTER, num);
+       }
+
+       /* Linux < 4.1 uses /sys/block/zram<id>/<attrname> */
+       name = mm_stat_names[idx];
+       if (bytes)
+               return sysfs_strdup(sysfs, name);
+       else if (sysfs_read_u64(sysfs, name, &num) == 0)
+               return size_to_human_string(SIZE_SUFFIX_1LETTER, num);
+       return NULL;
+}
+
 static void fill_table_row(struct libscols_table *tb, struct zram *z)
 {
        static struct libscols_line *ln;
@@ -250,7 +406,7 @@ static void fill_table_row(struct libscols_table *tb, struct zram *z)
 
        ln = scols_table_new_line(tb, NULL);
        if (!ln)
-               err(EXIT_FAILURE, _("failed to initialize output line"));
+               err(EXIT_FAILURE, _("failed to allocate output line"));
 
        for (i = 0; i < (size_t) ncolumns; i++) {
                char *str = NULL;
@@ -265,18 +421,6 @@ static void fill_table_row(struct libscols_table *tb, struct zram *z)
                        else if (sysfs_read_u64(sysfs, "disksize", &num) == 0)
                                str = size_to_human_string(SIZE_SUFFIX_1LETTER, num);
                        break;
-               case COL_ORIG_SIZE:
-                       if (inbytes)
-                               str = sysfs_strdup(sysfs, "orig_data_size");
-                       else if (sysfs_read_u64(sysfs, "orig_data_size", &num) == 0)
-                               str = size_to_human_string(SIZE_SUFFIX_1LETTER, num);
-                       break;
-               case COL_COMP_SIZE:
-                       if (inbytes)
-                               str = sysfs_strdup(sysfs, "compr_data_size");
-                       else if (sysfs_read_u64(sysfs, "compr_data_size", &num) == 0)
-                               str = size_to_human_string(SIZE_SUFFIX_1LETTER, num);
-                       break;
                case COL_ALGORITHM:
                {
                        char *alg = sysfs_strdup(sysfs, "comp_algorithm");
@@ -306,18 +450,29 @@ static void fill_table_row(struct libscols_table *tb, struct zram *z)
                        str = sysfs_strdup(sysfs, "max_comp_streams");
                        break;
                case COL_ZEROPAGES:
-                       str = sysfs_strdup(sysfs, "zero_pages");
+                       str = get_mm_stat(z, MM_ZERO_PAGES, 1);
+                       break;
+               case COL_ORIG_SIZE:
+                       str = get_mm_stat(z, MM_ORIG_DATA_SIZE, inbytes);
+                       break;
+               case COL_COMP_SIZE:
+                       str = get_mm_stat(z, MM_COMPR_DATA_SIZE, inbytes);
                        break;
                case COL_MEMTOTAL:
-                       if (inbytes)
-                               str = sysfs_strdup(sysfs, "mem_used_total");
-                       else if (sysfs_read_u64(sysfs, "mem_used_total", &num) == 0)
-                               str = size_to_human_string(SIZE_SUFFIX_1LETTER, num);
+                       str = get_mm_stat(z, MM_MEM_USED_TOTAL, inbytes);
+                       break;
+               case COL_MEMLIMIT:
+                       str = get_mm_stat(z, MM_MEM_LIMIT, inbytes);
+                       break;
+               case COL_MEMUSED:
+                       str = get_mm_stat(z, MM_MEM_USED_MAX, inbytes);
+                       break;
+               case COL_MIGRATED:
+                       str = get_mm_stat(z, MM_NUM_MIGRATED, inbytes);
                        break;
                }
-
-               if (str)
-                       scols_line_refer_data(ln, i, str);
+               if (str && scols_line_refer_data(ln, i, str))
+                       err(EXIT_FAILURE, _("failed to add output data"))
        }
 }
 
@@ -330,7 +485,7 @@ static void status(struct zram *z)
 
        tb = scols_new_table();
        if (!tb)
-               err(EXIT_FAILURE, _("failed to initialize output table"));
+               err(EXIT_FAILURE, _("failed to allocate output table"));
 
        scols_table_enable_raw(tb, raw);
        scols_table_enable_noheadings(tb, no_headings);
@@ -345,7 +500,7 @@ static void status(struct zram *z)
        if (z)
                fill_table_row(tb, z);          /* just one device specified */
        else {
-               size_t i;                       /* list all used devices */
+               /* list all used devices */
                z = new_zram(NULL);
 
                for (i = 0; ; i++) {
@@ -372,6 +527,9 @@ static void __attribute__ ((__noreturn__)) usage(FILE * out)
                        " %1$s [options] -f | <device> -s <size>\n"),
                        program_invocation_short_name);
 
+       fputs(USAGE_SEPARATOR, out);
+       fputs(_("Set up and control zram devices.\n"), out);
+
        fputs(USAGE_OPTIONS, out);
        fputs(_(" -a, --algorithm lzo|lz4   compression algorithm to use\n"), out);
        fputs(_(" -b, --bytes               print sizes in bytes rather than in human readable format\n"), out);
@@ -486,7 +644,7 @@ int main(int argc, char **argv)
                case 'h':
                        usage(stdout);
                default:
-                       usage(stderr);
+                       errtryhelp(EXIT_FAILURE);
                }
        }
 
@@ -499,11 +657,12 @@ int main(int argc, char **argv)
        if (act != A_RESET && optind + 1 < argc)
                errx(EXIT_FAILURE, _("only one <device> at a time is allowed"));
 
+       if ((act == A_STATUS || act == A_FINDONLY) && (algorithm || nstreams))
+               errx(EXIT_FAILURE, _("options --algorithm and --streams "
+                                    "must be combined with --size"));
+
        switch (act) {
        case A_STATUS:
-               if (algorithm || find || nstreams)
-                       errx(EXIT_FAILURE, _("options --algorithm, --find and "
-                                       "--streams are mutually exclusive"));
                if (!ncolumns) {                /* default columns */
                        columns[ncolumns++] = COL_NAME;
                        columns[ncolumns++] = COL_ALGORITHM;
@@ -517,7 +676,7 @@ int main(int argc, char **argv)
                if (optind < argc) {
                        zram = new_zram(argv[optind++]);
                        if (!zram_exist(zram))
-                               err(EXIT_FAILURE, zram->devname);
+                               err(EXIT_FAILURE, "%s", zram->devname);
                }
                status(zram);
                free_zram(zram);
@@ -532,6 +691,7 @@ int main(int argc, char **argv)
                                warn(_("%s: failed to reset"), zram->devname);
                                rc = 1;
                        }
+                       zram_control_remove(zram);
                        free_zram(zram);
                        optind++;
                }
@@ -553,7 +713,7 @@ int main(int argc, char **argv)
                else {
                        zram = new_zram(argv[optind]);
                        if (!zram_exist(zram))
-                               err(EXIT_FAILURE, zram->devname);
+                               err(EXIT_FAILURE, "%s", zram->devname);
                }
 
                if (zram_set_u64parm(zram, "reset", 1))