2 * zramctl - control compressed block devices in RAM
4 * Copyright (c) 2014 Timofey Titovets <Nefelim4ag@gmail.com>
5 * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it would be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <sys/types.h>
29 #include <libsmartcols.h>
33 #include "closestream.h"
38 #include "ismounted.h"
41 #include "pathnames.h"
43 /*#define CONFIG_ZRAM_DEBUG*/
45 #ifdef CONFIG_ZRAM_DEBUG
46 # define DBG(x) do { fputs("zram: ", stderr); x; fputc('\n', stderr); } while(0)
51 /* status output columns */
74 static const struct colinfo infos
[] = {
75 [COL_NAME
] = { "NAME", 0.25, 0, N_("zram device name") },
76 [COL_DISKSIZE
] = { "DISKSIZE", 5, SCOLS_FL_RIGHT
, N_("limit on the uncompressed amount of data") },
77 [COL_ORIG_SIZE
] = { "DATA", 5, SCOLS_FL_RIGHT
, N_("uncompressed size of stored data") },
78 [COL_COMP_SIZE
] = { "COMPR", 5, SCOLS_FL_RIGHT
, N_("compressed size of stored data") },
79 [COL_ALGORITHM
] = { "ALGORITHM", 3, 0, N_("the selected compression algorithm") },
80 [COL_STREAMS
] = { "STREAMS", 3, SCOLS_FL_RIGHT
, N_("number of concurrent compress operations") },
81 [COL_ZEROPAGES
] = { "ZERO-PAGES", 3, SCOLS_FL_RIGHT
, N_("empty pages with no allocated memory") },
82 [COL_MEMTOTAL
] = { "TOTAL", 5, SCOLS_FL_RIGHT
, N_("all memory including allocator fragmentation and metadata overhead") },
83 [COL_MEMLIMIT
] = { "MEM-LIMIT", 5, SCOLS_FL_RIGHT
, N_("memory limit used to store compressed data") },
84 [COL_MEMUSED
] = { "MEM-USED", 5, SCOLS_FL_RIGHT
, N_("memory zram have been consumed to store compressed data") },
85 [COL_MIGRATED
] = { "MIGRATED", 5, SCOLS_FL_RIGHT
, N_("number of objects migrated by compaction") },
86 [COL_MOUNTPOINT
]= { "MOUNTPOINT",0.10, SCOLS_FL_TRUNC
, N_("where the device is mounted") },
89 static int columns
[ARRAY_SIZE(infos
) * 2] = {-1};
93 MM_ORIG_DATA_SIZE
= 0,
102 static const char *mm_stat_names
[] = {
103 [MM_ORIG_DATA_SIZE
] = "orig_data_size",
104 [MM_COMPR_DATA_SIZE
] = "compr_data_size",
105 [MM_MEM_USED_TOTAL
] = "mem_used_total",
106 [MM_MEM_LIMIT
] = "mem_limit",
107 [MM_MEM_USED_MAX
] = "mem_used_max",
108 [MM_ZERO_PAGES
] = "zero_pages",
109 [MM_NUM_MIGRATED
] = "num_migrated"
114 struct path_cxt
*sysfs
; /* device specific sysfs directory */
117 unsigned int mm_stat_probed
: 1,
119 has_control
: 1; /* has /sys/class/zram-control/ */
122 static unsigned int raw
, no_headings
, inbytes
;
123 static struct path_cxt
*__control
;
125 static int get_column_id(int num
)
127 assert(num
< ncolumns
);
128 assert(columns
[num
] < (int) ARRAY_SIZE(infos
));
132 static const struct colinfo
*get_column_info(int num
)
134 return &infos
[ get_column_id(num
) ];
137 static int column_name_to_id(const char *name
, size_t namesz
)
141 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
142 const char *cn
= infos
[i
].name
;
144 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
147 warnx(_("unknown column: %s"), name
);
151 static void zram_reset_stat(struct zram
*z
)
154 strv_free(z
->mm_stat
);
156 z
->mm_stat_probed
= 0;
160 static void zram_set_devname(struct zram
*z
, const char *devname
, size_t n
)
165 snprintf(z
->devname
, sizeof(z
->devname
), "/dev/zram%zu", n
);
167 xstrncpy(z
->devname
, devname
, sizeof(z
->devname
));
169 DBG(fprintf(stderr
, "set devname: %s", z
->devname
));
170 ul_unref_path(z
->sysfs
);
175 static int zram_get_devnum(struct zram
*z
)
181 if (sscanf(z
->devname
, "/dev/zram%d", &n
) == 1)
186 static struct zram
*new_zram(const char *devname
)
188 struct zram
*z
= xcalloc(1, sizeof(struct zram
));
190 DBG(fprintf(stderr
, "new: %p", z
));
192 zram_set_devname(z
, devname
, 0);
196 static void free_zram(struct zram
*z
)
200 DBG(fprintf(stderr
, "free: %p", z
));
201 ul_unref_path(z
->sysfs
);
206 static struct path_cxt
*zram_get_sysfs(struct zram
*z
)
211 dev_t devno
= sysfs_devname_to_devno(z
->devname
);
214 z
->sysfs
= ul_new_sysfs_path(devno
, NULL
, NULL
);
217 if (*z
->devname
!= '/')
218 /* canonicalize the device name according to /sys */
219 sysfs_blkdev_get_path(z
->sysfs
, z
->devname
, sizeof(z
->devname
));
225 static inline int zram_exist(struct zram
*z
)
230 if (zram_get_sysfs(z
) == NULL
) {
235 DBG(fprintf(stderr
, "%s exists", z
->devname
));
239 static int zram_set_u64parm(struct zram
*z
, const char *attr
, uint64_t num
)
241 struct path_cxt
*sysfs
= zram_get_sysfs(z
);
244 DBG(fprintf(stderr
, "%s writing %ju to %s", z
->devname
, num
, attr
));
245 return ul_path_write_u64(sysfs
, num
, attr
);
248 static int zram_set_strparm(struct zram
*z
, const char *attr
, const char *str
)
250 struct path_cxt
*sysfs
= zram_get_sysfs(z
);
253 DBG(fprintf(stderr
, "%s writing %s to %s", z
->devname
, str
, attr
));
254 return ul_path_write_string(sysfs
, str
, attr
);
258 static int zram_used(struct zram
*z
)
261 struct path_cxt
*sysfs
= zram_get_sysfs(z
);
264 ul_path_read_u64(sysfs
, &size
, "disksize") == 0 &&
267 DBG(fprintf(stderr
, "%s used", z
->devname
));
270 DBG(fprintf(stderr
, "%s unused", z
->devname
));
274 static int zram_has_control(struct zram
*z
)
276 if (!z
->control_probed
) {
277 z
->has_control
= access(_PATH_SYS_CLASS
"/zram-control/", F_OK
) == 0 ? 1 : 0;
278 z
->control_probed
= 1;
279 DBG(fprintf(stderr
, "zram-control: %s", z
->has_control
? "yes" : "no"));
282 return z
->has_control
;
285 static struct path_cxt
*zram_get_control(void)
288 __control
= ul_new_path(_PATH_SYS_CLASS
"/zram-control");
292 static int zram_control_add(struct zram
*z
)
295 struct path_cxt
*ctl
;
297 if (!zram_has_control(z
) || !(ctl
= zram_get_control()))
300 if (ul_path_read_s32(ctl
, &n
, "hot_add") != 0 || n
< 0)
303 DBG(fprintf(stderr
, "hot-add: %d", n
));
304 zram_set_devname(z
, NULL
, n
);
308 static int zram_control_remove(struct zram
*z
)
310 struct path_cxt
*ctl
;
313 if (!zram_has_control(z
) || !(ctl
= zram_get_control()))
316 n
= zram_get_devnum(z
);
320 DBG(fprintf(stderr
, "hot-remove: %d", n
));
321 return ul_path_write_u64(ctl
, n
, "hot_remove");
324 static struct zram
*find_free_zram(void)
326 struct zram
*z
= new_zram(NULL
);
330 for (i
= 0; isfree
== 0; i
++) {
331 DBG(fprintf(stderr
, "find free: checking zram%zu", i
));
332 zram_set_devname(z
, NULL
, i
);
333 if (!zram_exist(z
) && zram_control_add(z
) != 0)
335 isfree
= !zram_used(z
);
344 static char *get_mm_stat(struct zram
*z
, size_t idx
, int bytes
)
346 struct path_cxt
*sysfs
;
351 assert(idx
< ARRAY_SIZE(mm_stat_names
));
354 sysfs
= zram_get_sysfs(z
);
358 /* Linux >= 4.1 uses /sys/block/zram<id>/mm_stat */
359 if (!z
->mm_stat
&& !z
->mm_stat_probed
) {
360 if (ul_path_read_string(sysfs
, &str
, "mm_stat") > 0 && str
) {
361 z
->mm_stat
= strv_split(str
, " ");
363 /* make sure kernel provides mm_stat as expected */
364 if (strv_length(z
->mm_stat
) < ARRAY_SIZE(mm_stat_names
)) {
365 strv_free(z
->mm_stat
);
369 z
->mm_stat_probed
= 1;
376 return xstrdup(z
->mm_stat
[idx
]);
378 num
= strtou64_or_err(z
->mm_stat
[idx
], _("Failed to parse mm_stat"));
379 return size_to_human_string(SIZE_SUFFIX_1LETTER
, num
);
382 /* Linux < 4.1 uses /sys/block/zram<id>/<attrname> */
383 name
= mm_stat_names
[idx
];
385 ul_path_read_string(sysfs
, &str
, name
);
390 if (ul_path_read_u64(sysfs
, &num
, name
) == 0)
391 return size_to_human_string(SIZE_SUFFIX_1LETTER
, num
);
396 static void fill_table_row(struct libscols_table
*tb
, struct zram
*z
)
398 static struct libscols_line
*ln
;
399 struct path_cxt
*sysfs
;
406 DBG(fprintf(stderr
, "%s: filling status table", z
->devname
));
408 sysfs
= zram_get_sysfs(z
);
412 ln
= scols_table_new_line(tb
, NULL
);
414 err(EXIT_FAILURE
, _("failed to allocate output line"));
416 for (i
= 0; i
< (size_t) ncolumns
; i
++) {
419 switch (get_column_id(i
)) {
421 str
= xstrdup(z
->devname
);
425 ul_path_read_string(sysfs
, &str
, "disksize");
427 else if (ul_path_read_u64(sysfs
, &num
, "disksize") == 0)
428 str
= size_to_human_string(SIZE_SUFFIX_1LETTER
, num
);
434 ul_path_read_string(sysfs
, &alg
, "comp_algorithm");
436 char* lbr
= strrchr(alg
, '[');
437 char* rbr
= strrchr(alg
, ']');
439 if (lbr
!= NULL
&& rbr
!= NULL
&& rbr
- lbr
> 1)
440 str
= xstrndup(lbr
+ 1, rbr
- lbr
- 1);
447 char path
[PATH_MAX
] = { '\0' };
450 check_mount_point(z
->devname
, &fl
, path
, sizeof(path
));
456 ul_path_read_string(sysfs
, &str
, "max_comp_streams");
459 str
= get_mm_stat(z
, MM_ZERO_PAGES
, 1);
462 str
= get_mm_stat(z
, MM_ORIG_DATA_SIZE
, inbytes
);
465 str
= get_mm_stat(z
, MM_COMPR_DATA_SIZE
, inbytes
);
468 str
= get_mm_stat(z
, MM_MEM_USED_TOTAL
, inbytes
);
471 str
= get_mm_stat(z
, MM_MEM_LIMIT
, inbytes
);
474 str
= get_mm_stat(z
, MM_MEM_USED_MAX
, inbytes
);
477 str
= get_mm_stat(z
, MM_NUM_MIGRATED
, inbytes
);
480 if (str
&& scols_line_refer_data(ln
, i
, str
))
481 err(EXIT_FAILURE
, _("failed to add output data"));
485 static void status(struct zram
*z
)
487 struct libscols_table
*tb
;
494 tb
= scols_new_table();
496 err(EXIT_FAILURE
, _("failed to allocate output table"));
498 scols_table_enable_raw(tb
, raw
);
499 scols_table_enable_noheadings(tb
, no_headings
);
501 for (i
= 0; i
< (size_t) ncolumns
; i
++) {
502 const struct colinfo
*col
= get_column_info(i
);
504 if (!scols_table_new_column(tb
, col
->name
, col
->whint
, col
->flags
))
505 err(EXIT_FAILURE
, _("failed to initialize output column"));
509 /* just one device specified */
510 fill_table_row(tb
, z
);
514 /* list all used devices */
516 if (!(dir
= opendir(_PATH_DEV
)))
517 err(EXIT_FAILURE
, _("cannot open %s"), _PATH_DEV
);
519 while ((d
= readdir(dir
))) {
521 if (sscanf(d
->d_name
, "zram%d", &n
) != 1)
523 zram_set_devname(z
, NULL
, n
);
524 if (zram_exist(z
) && zram_used(z
))
525 fill_table_row(tb
, z
);
531 scols_print_table(tb
);
532 scols_unref_table(tb
);
535 static void __attribute__((__noreturn__
)) usage(void)
540 fputs(USAGE_HEADER
, out
);
541 fprintf(out
, _( " %1$s [options] <device>\n"
542 " %1$s -r <device> [...]\n"
543 " %1$s [options] -f | <device> -s <size>\n"),
544 program_invocation_short_name
);
546 fputs(USAGE_SEPARATOR
, out
);
547 fputs(_("Set up and control zram devices.\n"), out
);
549 fputs(USAGE_OPTIONS
, out
);
550 fputs(_(" -a, --algorithm <alg> compression algorithm to use\n"), out
);
551 fputs(_(" -b, --bytes print sizes in bytes rather than in human readable format\n"), out
);
552 fputs(_(" -f, --find find a free device\n"), out
);
553 fputs(_(" -n, --noheadings don't print headings\n"), out
);
554 fputs(_(" -o, --output <list> columns to use for status output\n"), out
);
555 fputs(_(" --output-all output all columns\n"), out
);
556 fputs(_(" --raw use raw status output format\n"), out
);
557 fputs(_(" -r, --reset reset all specified devices\n"), out
);
558 fputs(_(" -s, --size <size> device size\n"), out
);
559 fputs(_(" -t, --streams <number> number of compression streams\n"), out
);
561 fputs(USAGE_SEPARATOR
, out
);
562 printf(USAGE_HELP_OPTIONS(27));
564 fputs(USAGE_ARGUMENTS
, out
);
565 printf(USAGE_ARG_SIZE(_("<size>")));
567 fputs(_(" <alg> specify algorithm, supported are:\n"), out
);
568 fputs(_(" lzo, lz4, lz4hc, deflate, 842 and zstd\n"), out
);
569 fputs(_(" (List may be inaccurate, consult man page.)\n"), out
);
571 fputs(USAGE_COLUMNS
, out
);
572 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++)
573 fprintf(out
, " %11s %s\n", infos
[i
].name
, _(infos
[i
].help
));
575 printf(USAGE_MAN_TAIL("zramctl(8)"));
588 int main(int argc
, char **argv
)
590 uintmax_t size
= 0, nstreams
= 0;
591 char *algorithm
= NULL
;
592 int rc
= 0, c
, find
= 0, act
= A_NONE
;
593 struct zram
*zram
= NULL
;
596 OPT_RAW
= CHAR_MAX
+ 1,
600 static const struct option longopts
[] = {
601 { "algorithm", required_argument
, NULL
, 'a' },
602 { "bytes", no_argument
, NULL
, 'b' },
603 { "find", no_argument
, NULL
, 'f' },
604 { "help", no_argument
, NULL
, 'h' },
605 { "output", required_argument
, NULL
, 'o' },
606 { "output-all",no_argument
, NULL
, OPT_LIST_TYPES
},
607 { "noheadings",no_argument
, NULL
, 'n' },
608 { "reset", no_argument
, NULL
, 'r' },
609 { "raw", no_argument
, NULL
, OPT_RAW
},
610 { "size", required_argument
, NULL
, 's' },
611 { "streams", required_argument
, NULL
, 't' },
612 { "version", no_argument
, NULL
, 'V' },
616 static const ul_excl_t excl
[] = {
621 int excl_st
[ARRAY_SIZE(excl
)] = UL_EXCL_STATUS_INIT
;
623 setlocale(LC_ALL
, "");
624 bindtextdomain(PACKAGE
, LOCALEDIR
);
626 close_stdout_atexit();
628 while ((c
= getopt_long(argc
, argv
, "a:bfho:nrs:t:V", longopts
, NULL
)) != -1) {
630 err_exclusive_options(c
, longopts
, excl
, excl_st
);
643 ncolumns
= string_to_idarray(optarg
,
644 columns
, ARRAY_SIZE(columns
),
650 for (ncolumns
= 0; (size_t)ncolumns
< ARRAY_SIZE(infos
); ncolumns
++)
651 columns
[ncolumns
] = ncolumns
;
654 size
= strtosize_or_err(optarg
, _("failed to parse size"));
658 nstreams
= strtou64_or_err(optarg
, _("failed to parse streams"));
671 print_version(EXIT_SUCCESS
);
675 errtryhelp(EXIT_FAILURE
);
679 if (find
&& optind
< argc
)
680 errx(EXIT_FAILURE
, _("option --find is mutually exclusive "
683 act
= find
? A_FINDONLY
: A_STATUS
;
685 if (act
!= A_RESET
&& optind
+ 1 < argc
)
686 errx(EXIT_FAILURE
, _("only one <device> at a time is allowed"));
688 if ((act
== A_STATUS
|| act
== A_FINDONLY
) && (algorithm
|| nstreams
))
689 errx(EXIT_FAILURE
, _("options --algorithm and --streams "
690 "must be combined with --size"));
692 ul_path_init_debug();
693 ul_sysfs_init_debug();
697 if (!ncolumns
) { /* default columns */
698 columns
[ncolumns
++] = COL_NAME
;
699 columns
[ncolumns
++] = COL_ALGORITHM
;
700 columns
[ncolumns
++] = COL_DISKSIZE
;
701 columns
[ncolumns
++] = COL_ORIG_SIZE
;
702 columns
[ncolumns
++] = COL_COMP_SIZE
;
703 columns
[ncolumns
++] = COL_MEMTOTAL
;
704 columns
[ncolumns
++] = COL_STREAMS
;
705 columns
[ncolumns
++] = COL_MOUNTPOINT
;
708 zram
= new_zram(argv
[optind
++]);
709 if (!zram_exist(zram
))
710 err(EXIT_FAILURE
, "%s", zram
->devname
);
717 errx(EXIT_FAILURE
, _("no device specified"));
718 while (optind
< argc
) {
719 zram
= new_zram(argv
[optind
]);
720 if (!zram_exist(zram
)
721 || zram_set_u64parm(zram
, "reset", 1)) {
722 warn(_("%s: failed to reset"), zram
->devname
);
725 zram_control_remove(zram
);
731 zram
= find_free_zram();
733 errx(EXIT_FAILURE
, _("no free zram device found"));
734 printf("%s\n", zram
->devname
);
739 zram
= find_free_zram();
741 errx(EXIT_FAILURE
, _("no free zram device found"));
742 } else if (optind
== argc
)
743 errx(EXIT_FAILURE
, _("no device specified"));
745 zram
= new_zram(argv
[optind
]);
746 if (!zram_exist(zram
))
747 err(EXIT_FAILURE
, "%s", zram
->devname
);
750 if (zram_set_u64parm(zram
, "reset", 1))
751 err(EXIT_FAILURE
, _("%s: failed to reset"), zram
->devname
);
754 zram_set_u64parm(zram
, "max_comp_streams", nstreams
))
755 err(EXIT_FAILURE
, _("%s: failed to set number of streams"), zram
->devname
);
758 zram_set_strparm(zram
, "comp_algorithm", algorithm
))
759 err(EXIT_FAILURE
, _("%s: failed to set algorithm"), zram
->devname
);
761 if (zram_set_u64parm(zram
, "disksize", size
))
762 err(EXIT_FAILURE
, _("%s: failed to set disksize (%ju bytes)"),
763 zram
->devname
, size
);
765 printf("%s\n", zram
->devname
);
770 ul_unref_path(__control
);
771 return rc
? EXIT_FAILURE
: EXIT_SUCCESS
;