]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
zrmactl: add new command to control /dev/zramN devices
authorKarel Zak <kzak@redhat.com>
Fri, 1 Aug 2014 10:09:55 +0000 (12:09 +0200)
committerKarel Zak <kzak@redhat.com>
Fri, 1 Aug 2014 10:09:55 +0000 (12:09 +0200)
Co-Author: Timofey Titovets <nefelim4ag@gmail.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
.gitignore
configure.ac
sys-utils/Makemodule.am
sys-utils/zramctl.8 [new file with mode: 0644]
sys-utils/zramctl.c [new file with mode: 0644]

index 93e0bc8d62407128742856b5c0193d2359827952..6ef0c4da984b75da331f3ed1215a95d60f44e93f 100644 (file)
@@ -177,3 +177,4 @@ update.log
 /whereis
 /wipefs
 /write
+/zramctl
index c6c74da029e071bb091125dbeeb991ce270475ce..948710e19be4bd0067b6f899a5a6c64509734a41 100644 (file)
@@ -861,6 +861,14 @@ UL_REQUIRES_LINUX([losetup])
 UL_REQUIRES_BUILD([losetup], [libsmartcols])
 AM_CONDITIONAL([BUILD_LOSETUP], [test "x$build_losetup" = xyes])
 
+AC_ARG_ENABLE([zramctl],
+  AS_HELP_STRING([--disable-zramctl], [do not build zramctl]),
+  [], [UL_DEFAULT_ENABLE([zramctl], [check])]
+)
+UL_BUILD_INIT([zramctl])
+UL_REQUIRES_LINUX([zramctl])
+UL_REQUIRES_BUILD([zramctl], [libsmartcols])
+AM_CONDITIONAL([BUILD_ZRAMCTL], [test "x$build_zramctl" = xyes])
 
 AC_ARG_ENABLE([fsck],
   AS_HELP_STRING([--disable-fsck], [do not build fsck]),
index ca47df80b6232780a8617e3777774b9dd5b031c2..058bb62d498cbfb91b3fd5585cc58a41df937b92 100644 (file)
@@ -190,6 +190,15 @@ endif
 endif # BUILD_LOSETUP
 
 
+if BUILD_ZRAMCTL
+sbin_PROGRAMS += zramctl
+dist_man_MANS += sys-utils/zramctl.8
+zramctl_SOURCES = sys-utils/zramctl.c
+zramctl_LDADD = $(LDADD) libcommon.la libsmartcols.la
+zramctl_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir)
+endif
+
+
 if BUILD_PRLIMIT
 usrbin_exec_PROGRAMS += prlimit
 dist_man_MANS += sys-utils/prlimit.1
diff --git a/sys-utils/zramctl.8 b/sys-utils/zramctl.8
new file mode 100644 (file)
index 0000000..34bb71f
--- /dev/null
@@ -0,0 +1,106 @@
+.TH ZRAMCTL 8 "July 2014" "util-linux" "System Administration"
+.SH NAME
+zramctl \- set up and control zram devices
+.SH SYNOPSIS
+.ad l
+Get info:
+.sp
+.in +5
+.BR zramctl " [options]"
+.sp
+.in -5
+Reset zram:
+.sp
+.in +5
+.B "zramctl \-r"
+.IR zramdev ...
+.sp
+.in -5
+Print name of first unused zram device:
+.sp
+.in +5
+.B "zramctl \-f"
+.sp
+.in -5
+Setup zram device:
+.sp
+.in +5
+.B zramctl
+.RB [ \-f " | "\fIzramdev\fP ]
+.RB [ \-s
+.IR size ]
+.RB \ [ \-t
+.IR number ]
+.in +8
+.RB [ \-a
+.IR algorithm ]
+.sp
+.in -13
+.ad b
+.SH DESCRIPTION
+.B zramctl
+is used to quickly set up zram device parameters, to reset zram devices, and to
+query the status of used zram devices.  If no option is given, all zram devices
+are shown.
+
+.SH OPTIONS
+.IP "\fB\-a, \-\-algorithm \fI{lzo|lz4}\fP"
+Set compression algorithm used for compress data in zram device.
+.IP "\fB\-f, \-\-find\fP"
+Find the first unused zram device. If a \fB--size\fR argument is present, then
+initialize the device.
+.IP "\fB\-h, \-\-help\fP"
+print help
+.IP "\fB\-h, \-\-noheadings\fP"
+Do not print a header line in status output.
+.IP "\fB\-o, \-\-output \fIlist\fP"
+Define the status output columns to use.  If no output arrangement is specified,
+then a default set is used.
+Use \fB\-\-help\fP to get a list of all supported columns.
+.IP "\fB\-r, \-\-reset\fP
+Reset options specified zram device(s). Zram device setting can be changed only
+after reset.
+.IP "\fB\-\-raw\fP"
+Use the raw format for status output.
+.IP "\fB\-s, \-\-size\fP \fIsize\fP
+Force zram driver to reread size of the file associated with the specified zram device
+
+The \fIsize\fR argument may be followed by the multiplicative suffixes KiB (=1024),
+MiB (=1024*1024), and so on for GiB, TiB, PiB, EiB, ZiB and YiB (the "iB"
+is optional, e.g., "K" has the same meaning as "KiB") or the suffixes
+KB (=1000), MB (=1000*1000), and so on for GB, TB, PB, EB, ZB and YB.
+.IP "\fB\-t, \-\-streams \fInumber\fP"
+Set number of maximum compress streams what used for device. The default is one stream.
+.IP "\fB\-V, \-\-version\fP"
+Display version information and exit.
+
+.SH RETURN VALUE
+.B zramctl
+returns 0 on success, nonzero on failure.
+
+.SH FILES
+.TP
+.I /dev/zram[0..N]
+zram block devices
+
+.SH EXAMPLE
+The following commands can be used for setup the zram device with gigabyte size
+ and using as swap device.
+.nf
+.IP
+# zramctl --find --size 1024M
+/dev/zram0
+# mkswap /dev/zram0
+# swapon /dev/zram0
+ ...
+# swapoff /dev/zram0
+# zramctl --reset /dev/zram0
+.fi
+.SH AUTHORS
+.nf
+Timofey Titovets <nefelim4ag@gmail.com>
+Karel Zak <kzak@redhat.com>
+.fi
+.SH AVAILABILITY
+The zramctl command is part of the util-linux package and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/sys-utils/zramctl.c b/sys-utils/zramctl.c
new file mode 100644 (file)
index 0000000..2215898
--- /dev/null
@@ -0,0 +1,571 @@
+/*
+ * zramctl - purpose of it
+ *
+ * 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"
+
+/*#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] <device>\n"
+                       " %1$s -r <device> [...]\n" 
+                       " %1$s [options] -f | <device> -s <size>\n"),
+                       program_invocation_short_name);
+
+       fputs(USAGE_OPTIONS, out);
+       fputs(_(" -a, --algorithm <lzo|lz4> 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 <list>       columns to use for status output\n"), out);
+       fputs(_(" -r, --reset               reset all specified devices\n"), out);
+       fputs(_(" -s, --size <size>         device size\n"), out);
+       fputs(_("     --raw                 use raw status output format\n"), out);
+       fputs(_(" -t, --streams <number>    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 <device>."));
+       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;
+}