| /* |
| * zramctl - control compressed block devices in RAM |
| * |
| * Copyright (c) 2014 Timofey Titovets <Nefelim4ag@gmail.com> |
| * Copyright (C) 2014 Karel Zak <kzak@redhat.com> |
| * |
| * 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 <getopt.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdarg.h> |
| #include <assert.h> |
| |
| #include <libsmartcols.h> |
| |
| #include "c.h" |
| #include "nls.h" |
| #include "closestream.h" |
| #include "strutils.h" |
| #include "xalloc.h" |
| #include "sysfs.h" |
| #include "optutils.h" |
| #include "ismounted.h" |
| #include "strv.h" |
| #include "path.h" |
| #include "pathnames.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_MEMLIMIT, |
| COL_MEMUSED, |
| COL_MIGRATED, |
| 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 amount 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_("the selected compression algorithm") }, |
| [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 } |
| |
| 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_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); |
| |
| 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); |
| 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) |
| { |
| 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); |
| zram_reset_stat(z); |
| 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; |
| 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; |
| } |
| |
| static inline int zram_exist(struct zram *z) |
| { |
| assert(z); |
| |
| errno = 0; |
| if (zram_get_sysfs(z) == NULL) { |
| errno = ENODEV; |
| 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 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); |
| 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) && zram_control_add(z) != 0) |
| break; |
| isfree = !zram_used(z); |
| } |
| if (!isfree) { |
| free_zram(z); |
| z = NULL; |
| } |
| 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; |
| 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 allocate 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_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 = 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: |
| 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)) |
| err(EXIT_FAILURE, _("failed to add output data")); |
| } |
| } |
| |
| 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 allocate 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 { |
| /* 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] <device>\n" |
| " %1$s -r <device> [...]\n" |
| " %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); |
| fputs(_(" -f, --find find a free device\n"), out); |
| fputs(_(" -n, --noheadings don't print headings\n"), out); |
| fputs(_(" -o, --output <list> columns to use for status output\n"), out); |
| fputs(_(" --raw use raw status output format\n"), out); |
| fputs(_(" -r, --reset reset all specified devices\n"), out); |
| fputs(_(" -s, --size <size> device size\n"), out); |
| fputs(_(" -t, --streams <number> number of compression streams\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' }, |
| { "bytes", no_argument, NULL, 'b' }, |
| { "find", no_argument, NULL, 'f' }, |
| { "help", no_argument, NULL, 'h' }, |
| { "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: |
| errtryhelp(EXIT_FAILURE); |
| } |
| } |
| |
| if (find && optind < argc) |
| errx(EXIT_FAILURE, _("option --find is mutually exclusive " |
| "with <device>")); |
| if (act == A_NONE) |
| act = find ? A_FINDONLY : A_STATUS; |
| |
| 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 (!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++]); |
| if (!zram_exist(zram)) |
| err(EXIT_FAILURE, "%s", zram->devname); |
| } |
| 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_exist(zram) |
| || zram_set_u64parm(zram, "reset", 1)) { |
| warn(_("%s: failed to reset"), zram->devname); |
| rc = 1; |
| } |
| zram_control_remove(zram); |
| free_zram(zram); |
| optind++; |
| } |
| break; |
| case A_FINDONLY: |
| zram = find_free_zram(); |
| if (!zram) |
| errx(EXIT_FAILURE, _("no free zram device found")); |
| printf("%s\n", zram->devname); |
| free_zram(zram); |
| break; |
| case A_CREATE: |
| if (find) { |
| zram = find_free_zram(); |
| if (!zram) |
| errx(EXIT_FAILURE, _("no free zram device found")); |
| } else if (optind == argc) |
| errx(EXIT_FAILURE, _("no device specified")); |
| else { |
| zram = new_zram(argv[optind]); |
| if (!zram_exist(zram)) |
| err(EXIT_FAILURE, "%s", zram->devname); |
| } |
| |
| 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; |
| } |