#include "sysfs.h"
#include "optutils.h"
#include "ismounted.h"
+#include "strv.h"
+#include "path.h"
+#include "pathnames.h"
/*#define CONFIG_ZRAM_DEBUG*/
COL_STREAMS,
COL_ZEROPAGES,
COL_MEMTOTAL,
+ COL_MEMLIMIT,
+ COL_MEMUSED,
+ COL_MIGRATED,
COL_MOUNTPOINT
};
[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 }
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);
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)
return;
DBG(fprintf(stderr, "free: %p", z));
sysfs_deinit(&z->sysfs);
+ zram_reset_stat(z);
free(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;
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);
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);
}
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;
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;
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");
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"))
}
}
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);
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++) {
" %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);
case 'h':
usage(stdout);
default:
- usage(stderr);
+ errtryhelp(EXIT_FAILURE);
}
}
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;
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);
warn(_("%s: failed to reset"), zram->devname);
rc = 1;
}
+ zram_control_remove(zram);
free_zram(zram);
optind++;
}
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))