From 0624d8406b8e8e64f7a1c6dcea7af648d99ef08d Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Fri, 1 Aug 2014 12:09:55 +0200 Subject: zrmactl: add new command to control /dev/zramN devices Co-Author: Timofey Titovets Signed-off-by: Karel Zak --- sys-utils/zramctl.c | 571 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 571 insertions(+) create mode 100644 sys-utils/zramctl.c (limited to 'sys-utils/zramctl.c') diff --git a/sys-utils/zramctl.c b/sys-utils/zramctl.c new file mode 100644 index 000000000..221589867 --- /dev/null +++ b/sys-utils/zramctl.c @@ -0,0 +1,571 @@ +/* + * zramctl - purpose of it + * + * Copyright (c) 2014 Timofey Titovets + * Copyright (C) 2014 Karel Zak + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include + +#include + +#include "c.h" +#include "nls.h" +#include "closestream.h" +#include "strutils.h" +#include "xalloc.h" +#include "sysfs.h" +#include "optutils.h" +#include "ismounted.h" + +/*#define CONFIG_ZRAM_DEBUG*/ + +#ifdef CONFIG_ZRAM_DEBUG +# define DBG(x) do { fputs("zram: ", stderr); x; fputc('\n', stderr); } while(0) +#else +# define DBG(x) +#endif + +/* status output columns */ +struct colinfo { + const char *name; + double whint; + int flags; + const char *help; +}; + +enum { + COL_NAME = 0, + COL_DISKSIZE, + COL_ORIG_SIZE, + COL_COMP_SIZE, + COL_ALGORITHM, + COL_STREAMS, + COL_ZEROPAGES, + COL_MEMTOTAL, + COL_MOUNTPOINT +}; + +static const struct colinfo infos[] = { + [COL_NAME] = { "NAME", 0.25, 0, N_("zram device name") }, + [COL_DISKSIZE] = { "DISKSIZE", 5, SCOLS_FL_RIGHT, N_("limit on the uncompressed worth of data") }, + [COL_ORIG_SIZE] = { "DATA", 5, SCOLS_FL_RIGHT, N_("uncompressed size of stored data") }, + [COL_COMP_SIZE] = { "COMPR", 5, SCOLS_FL_RIGHT, N_("compressed size of stored data") }, + [COL_ALGORITHM] = { "ALGORITHM", 3, 0, N_("selected compression algorithms") }, + [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_MOUNTPOINT]= { "MOUNTPOINT",0.10, SCOLS_FL_TRUNC, N_("where the device is mounted") }, + +}; + +static int columns[ARRAY_SIZE(infos) * 2] = {-1}; +static int ncolumns; + +struct zram { + char devname[32]; + struct sysfs_cxt sysfs; +}; + +#define ZRAM_EMPTY { .devname = { '\0' }, .sysfs = UL_SYSFSCXT_EMPTY } + +static unsigned int raw, no_headings, inbytes; + + +static int get_column_id(int num) +{ + assert(num < ncolumns); + assert(columns[num] < (int) ARRAY_SIZE(infos)); + return columns[num]; +} + +static const struct colinfo *get_column_info(int num) +{ + return &infos[ get_column_id(num) ]; +} + +static int column_name_to_id(const char *name, size_t namesz) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(infos); i++) { + const char *cn = infos[i].name; + + if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) + return i; + } + warnx(_("unknown column: %s"), name); + return -1; +} + +static void zram_set_devname(struct zram *z, const char *devname, size_t n) +{ + assert(z); + + if (!devname) + snprintf(z->devname, sizeof(z->devname), "/dev/zram%zu", n); + else { + strncpy(z->devname, devname, sizeof(z->devname)); + z->devname[sizeof(z->devname) - 1] = '\0'; + } + + DBG(fprintf(stderr, "set devname: %s", z->devname)); + sysfs_deinit(&z->sysfs); +} + +static struct zram *new_zram(const char *devname) +{ + struct zram *z = xcalloc(1, sizeof(struct zram)); + + DBG(fprintf(stderr, "new: %p", z)); + if (devname) + zram_set_devname(z, devname, 0); + return z; +} + +static void free_zram(struct zram *z) +{ + if (!z) + return; + DBG(fprintf(stderr, "free: %p", z)); + sysfs_deinit(&z->sysfs); + free(z); +} + +static struct sysfs_cxt *zram_get_sysfs(struct zram *z) +{ + assert(z); + + if (!z->sysfs.devno) { + dev_t devno = sysfs_devname_to_devno(z->devname, NULL); + if (!devno) + return NULL; + if (sysfs_init(&z->sysfs, devno, NULL)) + return NULL; + } + + return &z->sysfs; +} + +static inline int zram_exist(struct zram *z) +{ + assert(z); + + if (zram_get_sysfs(z) == NULL) + return 0; + + DBG(fprintf(stderr, "%s exists", z->devname)); + return 1; +} + +static int zram_set_u64parm(struct zram *z, const char *attr, uint64_t num) +{ + struct sysfs_cxt *sysfs = zram_get_sysfs(z); + if (!sysfs) + return -EINVAL; + DBG(fprintf(stderr, "%s writing %ju to %s", z->devname, num, attr)); + return sysfs_write_u64(sysfs, attr, num); +} + +static int zram_set_strparm(struct zram *z, const char *attr, const char *str) +{ + struct sysfs_cxt *sysfs = zram_get_sysfs(z); + if (!sysfs) + return -EINVAL; + DBG(fprintf(stderr, "%s writing %s to %s", z->devname, str, attr)); + return sysfs_write_string(sysfs, attr, str); +} + + +static int zram_used(struct zram *z) +{ + uint64_t size; + struct sysfs_cxt *sysfs = zram_get_sysfs(z); + + if (sysfs && + sysfs_read_u64(sysfs, "disksize", &size) == 0 && + size > 0) { + + DBG(fprintf(stderr, "%s used", z->devname)); + return 1; + } + DBG(fprintf(stderr, "%s unused", z->devname)); + return 0; +} + +static struct zram *find_free_zram(void) +{ + struct zram *z = new_zram(NULL); + size_t i; + int isfree = 0; + + 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)) + break; + isfree = !zram_used(z); + } + if (!isfree) { + free_zram(z); + z = NULL; + } + return z; +} + +static void fill_table_row(struct libscols_table *tb, struct zram *z) +{ + static struct libscols_line *ln; + struct sysfs_cxt *sysfs; + size_t i; + uint64_t num; + + assert(tb); + assert(z); + + DBG(fprintf(stderr, "%s: filling status table", z->devname)); + + sysfs = zram_get_sysfs(z); + if (!sysfs) + return; + + ln = scols_table_new_line(tb, NULL); + if (!ln) + err(EXIT_FAILURE, _("failed to initialize output line")); + + for (i = 0; i < (size_t) ncolumns; i++) { + char *str = NULL; + + switch (get_column_id(i)) { + case COL_NAME: + str = xstrdup(z->devname); + break; + case COL_DISKSIZE: + if (inbytes) + str = sysfs_strdup(sysfs, "disksize"); + 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"); + if (!alg) + break; + if (strstr(alg, "[lzo]") == NULL) { + if (strstr(alg, "[lz4]") == NULL) + ; + else + str = xstrdup("lz4"); + } else + str = xstrdup("lzo"); + free(alg); + break; + } + case COL_MOUNTPOINT: + { + char path[PATH_MAX] = { '\0' }; + int fl; + + check_mount_point(z->devname, &fl, path, sizeof(path)); + if (*path) + str = xstrdup(path); + break; + } + case COL_STREAMS: + str = sysfs_strdup(sysfs, "max_comp_streams"); + break; + case COL_ZEROPAGES: + str = sysfs_strdup(sysfs, "zero_pages"); + 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); + break; + } + + if (str) + scols_line_refer_data(ln, i, str); + } +} + +static void status(struct zram *z) +{ + struct libscols_table *tb; + size_t i; + + scols_init_debug(0); + + tb = scols_new_table(); + if (!tb) + err(EXIT_FAILURE, _("failed to initialize output table")); + + scols_table_enable_raw(tb, raw); + scols_table_enable_noheadings(tb, no_headings); + + for (i = 0; i < (size_t) ncolumns; i++) { + const struct colinfo *col = get_column_info(i); + + if (!scols_table_new_column(tb, col->name, col->whint, col->flags)) + err(EXIT_FAILURE, _("failed to initialize output column")); + } + + if (z) + fill_table_row(tb, z); /* just one device specified */ + else { + size_t i; /* list all used devices */ + z = new_zram(NULL); + + for (i = 0; ; i++) { + zram_set_devname(z, NULL, i); + if (!zram_exist(z)) + break; + if (zram_used(z)) + fill_table_row(tb, z); + } + free_zram(z); + } + + scols_print_table(tb); + scols_unref_table(tb); +} + +static void __attribute__ ((__noreturn__)) usage(FILE * out) +{ + size_t i; + + fputs(USAGE_HEADER, out); + fprintf(out, _( " %1$s [options] \n" + " %1$s -r [...]\n" + " %1$s [options] -f | -s \n"), + program_invocation_short_name); + + fputs(USAGE_OPTIONS, out); + fputs(_(" -a, --algorithm compression algorithm\n"), out); + fputs(_(" -b, --bytes print sizes in bytes rather than in human readable format\n"), out); + fputs(_(" -f, --find find free device\n"), out); + fputs(_(" -n, --noheadings don't print headings\n"), out); + fputs(_(" -o, --output columns to use for status output\n"), out); + fputs(_(" -r, --reset reset all specified devices\n"), out); + fputs(_(" -s, --size device size\n"), out); + fputs(_(" --raw use raw status output format\n"), out); + fputs(_(" -t, --streams number of compressoin streams\n\n"), out); + + fputs(USAGE_SEPARATOR, out); + fputs(USAGE_HELP, out); + fputs(USAGE_VERSION, out); + + fputs(_("\nAvailable columns (for --output):\n"), out); + for (i = 0; i < ARRAY_SIZE(infos); i++) + fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help)); + + fprintf(out, USAGE_MAN_TAIL("zramctl(8)")); + exit(out == stderr ? 1 : EXIT_SUCCESS); +} + +/* actions */ +enum { + A_NONE = 0, + A_STATUS, + A_CREATE, + A_FINDONLY, + A_RESET +}; + +int main(int argc, char **argv) +{ + uintmax_t size = 0, nstreams = 0; + char *algorithm = NULL; + int rc = 0, c, find = 0, act = A_NONE; + struct zram *zram = NULL; + + enum { OPT_RAW = CHAR_MAX + 1 }; + + static const struct option longopts[] = { + { "algorithm", required_argument, NULL, 'a' }, + { "find", no_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { "bytes", no_argument, NULL, 'b' }, + { "output", required_argument, NULL, 'o' }, + { "noheadings",no_argument, NULL, 'n' }, + { "reset", no_argument, NULL, 'r' }, + { "raw", no_argument, NULL, OPT_RAW }, + { "size", required_argument, NULL, 's' }, + { "streams", required_argument, NULL, 't' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + static const ul_excl_t excl[] = { + { 'f', 'o', 'r' }, + { 'o', 'r', 's' }, + { 0 } + }; + int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + + while ((c = getopt_long(argc, argv, "a:bfho:nrs:t:V", longopts, NULL)) != -1) { + + err_exclusive_options(c, longopts, excl, excl_st); + + switch (c) { + case 'a': + if (strcmp(optarg,"lzo") && strcmp(optarg,"lz4")) + errx(EXIT_FAILURE, _("unsupported algorithm: %s"), + optarg); + algorithm = optarg; + break; + case 'b': + inbytes = 1; + break; + case 'f': + find = 1; + break; + case 'o': + ncolumns = string_to_idarray(optarg, + columns, ARRAY_SIZE(columns), + column_name_to_id); + if (ncolumns < 0) + return EXIT_FAILURE; + break; + case 's': + size = strtosize_or_err(optarg, _("failed to parse size")); + act = A_CREATE; + break; + case 't': + nstreams = strtou64_or_err(optarg, _("failed to parse streams")); + break; + case 'r': + act = A_RESET; + break; + case OPT_RAW: + raw = 1; + break; + case 'n': + no_headings = 1; + break; + case 'V': + printf(UTIL_LINUX_VERSION); + return EXIT_SUCCESS; + case 'h': + usage(stdout); + default: + usage(stderr); + } + } + + if (find && optind < argc) + errx(EXIT_FAILURE, _("option --find is mutually exclusive " + "with .")); + if (act == A_NONE) + act = find ? A_FINDONLY : A_STATUS; + + if (act != A_RESET && optind + 1 < argc) + usage(stderr); /* only --reset holds more devices */ + + 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; + columns[ncolumns++] = COL_DISKSIZE; + columns[ncolumns++] = COL_ORIG_SIZE; + columns[ncolumns++] = COL_COMP_SIZE; + columns[ncolumns++] = COL_MEMTOTAL; + columns[ncolumns++] = COL_STREAMS; + columns[ncolumns++] = COL_MOUNTPOINT; + } + if (optind < argc) + zram = new_zram(argv[optind++]); + status(zram); + free_zram(zram); + break; + case A_RESET: + if (optind == argc) + errx(EXIT_FAILURE, _("no device specified")); + while (optind < argc) { + zram = new_zram(argv[optind]); + if (zram_set_u64parm(zram, "reset", 1)) { + warn(_("%s: failed to reset"), zram->devname); + rc = 1; + } + free_zram(zram); + optind++; + } + break; + case A_FINDONLY: + zram = find_free_zram(); + if (!zram) + errx(EXIT_FAILURE, _("no found free zram device")); + printf("%s\n", zram->devname); + free_zram(zram); + break; + case A_CREATE: + if (find) { + zram = find_free_zram(); + if (!zram) + errx(EXIT_FAILURE, _("no found free zram device")); + } else if (optind == argc) + errx(EXIT_FAILURE, _("no device specified")); + else + zram = new_zram(argv[optind]); + + if (zram_set_u64parm(zram, "reset", 1)) + err(EXIT_FAILURE, _("%s: failed to reset"), zram->devname); + + if (nstreams && + zram_set_u64parm(zram, "max_comp_streams", nstreams)) + err(EXIT_FAILURE, _("%s: failed to set number of streams"), zram->devname); + + if (algorithm && + zram_set_strparm(zram, "comp_algorithm", algorithm)) + err(EXIT_FAILURE, _("%s: failed to set algorithm"), zram->devname); + + if (zram_set_u64parm(zram, "disksize", size)) + err(EXIT_FAILURE, _("%s: failed to set disksize (%ju bytes)"), + zram->devname, size); + if (find) + printf("%s\n", zram->devname); + free_zram(zram); + break; + } + + return rc ? EXIT_FAILURE : EXIT_SUCCESS; +} -- cgit v1.2.3-55-g7522