]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/zramctl.c
Merge branch 'table_title' of https://github.com/ignatenkobrain/util-linux
[thirdparty/util-linux.git] / sys-utils / zramctl.c
1 /*
2 * zramctl - control compressed block devices in RAM
3 *
4 * Copyright (c) 2014 Timofey Titovets <Nefelim4ag@gmail.com>
5 * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
6 *
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.
10 *
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.
15 *
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.
19 */
20
21 #include <getopt.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdarg.h>
25 #include <assert.h>
26
27 #include <libsmartcols.h>
28
29 #include "c.h"
30 #include "nls.h"
31 #include "closestream.h"
32 #include "strutils.h"
33 #include "xalloc.h"
34 #include "sysfs.h"
35 #include "optutils.h"
36 #include "ismounted.h"
37 #include "strv.h"
38 #include "path.h"
39
40 /*#define CONFIG_ZRAM_DEBUG*/
41
42 #ifdef CONFIG_ZRAM_DEBUG
43 # define DBG(x) do { fputs("zram: ", stderr); x; fputc('\n', stderr); } while(0)
44 #else
45 # define DBG(x)
46 #endif
47
48 /* status output columns */
49 struct colinfo {
50 const char *name;
51 double whint;
52 int flags;
53 const char *help;
54 };
55
56 enum {
57 COL_NAME = 0,
58 COL_DISKSIZE,
59 COL_ORIG_SIZE,
60 COL_COMP_SIZE,
61 COL_ALGORITHM,
62 COL_STREAMS,
63 COL_ZEROPAGES,
64 COL_MEMTOTAL,
65 COL_MEMLIMIT,
66 COL_MEMUSED,
67 COL_MIGRATED,
68 COL_MOUNTPOINT
69 };
70
71 static const struct colinfo infos[] = {
72 [COL_NAME] = { "NAME", 0.25, 0, N_("zram device name") },
73 [COL_DISKSIZE] = { "DISKSIZE", 5, SCOLS_FL_RIGHT, N_("limit on the uncompressed amount of data") },
74 [COL_ORIG_SIZE] = { "DATA", 5, SCOLS_FL_RIGHT, N_("uncompressed size of stored data") },
75 [COL_COMP_SIZE] = { "COMPR", 5, SCOLS_FL_RIGHT, N_("compressed size of stored data") },
76 [COL_ALGORITHM] = { "ALGORITHM", 3, 0, N_("the selected compression algorithm") },
77 [COL_STREAMS] = { "STREAMS", 3, SCOLS_FL_RIGHT, N_("number of concurrent compress operations") },
78 [COL_ZEROPAGES] = { "ZERO-PAGES", 3, SCOLS_FL_RIGHT, N_("empty pages with no allocated memory") },
79 [COL_MEMTOTAL] = { "TOTAL", 5, SCOLS_FL_RIGHT, N_("all memory including allocator fragmentation and metadata overhead") },
80 [COL_MEMLIMIT] = { "MEM-LIMIT", 5, SCOLS_FL_RIGHT, N_("memory limit used to store compressed data") },
81 [COL_MEMUSED] = { "MEM-USED", 5, SCOLS_FL_RIGHT, N_("memory zram have been consumed to store compressed data") },
82 [COL_MIGRATED] = { "MIGRATED", 5, SCOLS_FL_RIGHT, N_("number of objects migrated by compaction") },
83 [COL_MOUNTPOINT]= { "MOUNTPOINT",0.10, SCOLS_FL_TRUNC, N_("where the device is mounted") },
84 };
85
86 static int columns[ARRAY_SIZE(infos) * 2] = {-1};
87 static int ncolumns;
88
89 enum {
90 MM_ORIG_DATA_SIZE = 0,
91 MM_COMPR_DATA_SIZE,
92 MM_MEM_USED_TOTAL,
93 MM_MEM_LIMIT,
94 MM_MEM_USED_MAX,
95 MM_ZERO_PAGES,
96 MM_NUM_MIGRATED
97 };
98
99 static const char *mm_stat_names[] = {
100 [MM_ORIG_DATA_SIZE] = "orig_data_size",
101 [MM_COMPR_DATA_SIZE] = "compr_data_size",
102 [MM_MEM_USED_TOTAL] = "mem_used_total",
103 [MM_MEM_LIMIT] = "mem_limit",
104 [MM_MEM_USED_MAX] = "mem_used_max",
105 [MM_ZERO_PAGES] = "zero_pages",
106 [MM_NUM_MIGRATED] = "num_migrated"
107 };
108
109
110 struct zram {
111 char devname[32];
112 struct sysfs_cxt sysfs;
113 char **mm_stat;
114
115 unsigned int mm_stat_probed : 1;
116 };
117
118 #define ZRAM_EMPTY { .devname = { '\0' }, .sysfs = UL_SYSFSCXT_EMPTY }
119
120 static unsigned int raw, no_headings, inbytes;
121
122
123 static int get_column_id(int num)
124 {
125 assert(num < ncolumns);
126 assert(columns[num] < (int) ARRAY_SIZE(infos));
127 return columns[num];
128 }
129
130 static const struct colinfo *get_column_info(int num)
131 {
132 return &infos[ get_column_id(num) ];
133 }
134
135 static int column_name_to_id(const char *name, size_t namesz)
136 {
137 size_t i;
138
139 for (i = 0; i < ARRAY_SIZE(infos); i++) {
140 const char *cn = infos[i].name;
141
142 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
143 return i;
144 }
145 warnx(_("unknown column: %s"), name);
146 return -1;
147 }
148
149 static void zram_reset_stat(struct zram *z)
150 {
151 if (z) {
152 strv_free(z->mm_stat);
153 z->mm_stat = NULL;
154 z->mm_stat_probed = 0;
155 }
156 }
157
158 static void zram_set_devname(struct zram *z, const char *devname, size_t n)
159 {
160 assert(z);
161
162 if (!devname)
163 snprintf(z->devname, sizeof(z->devname), "/dev/zram%zu", n);
164 else {
165 strncpy(z->devname, devname, sizeof(z->devname));
166 z->devname[sizeof(z->devname) - 1] = '\0';
167 }
168
169 DBG(fprintf(stderr, "set devname: %s", z->devname));
170 sysfs_deinit(&z->sysfs);
171 zram_reset_stat(z);
172 }
173
174 static struct zram *new_zram(const char *devname)
175 {
176 struct zram *z = xcalloc(1, sizeof(struct zram));
177
178 DBG(fprintf(stderr, "new: %p", z));
179 if (devname)
180 zram_set_devname(z, devname, 0);
181 return z;
182 }
183
184 static void free_zram(struct zram *z)
185 {
186 if (!z)
187 return;
188 DBG(fprintf(stderr, "free: %p", z));
189 sysfs_deinit(&z->sysfs);
190 zram_reset_stat(z);
191 free(z);
192 }
193
194 static struct sysfs_cxt *zram_get_sysfs(struct zram *z)
195 {
196 assert(z);
197
198 if (!z->sysfs.devno) {
199 dev_t devno = sysfs_devname_to_devno(z->devname, NULL);
200 if (!devno)
201 return NULL;
202 if (sysfs_init(&z->sysfs, devno, NULL))
203 return NULL;
204 if (*z->devname != '/') {
205 /* cannonicalize the device name according to /sys */
206 char name[PATH_MAX];
207 if (sysfs_get_devname(&z->sysfs, name, sizeof(name)))
208 snprintf(z->devname, sizeof(z->devname), "/dev/%s", name);
209 }
210 }
211
212 return &z->sysfs;
213 }
214
215 static inline int zram_exist(struct zram *z)
216 {
217 assert(z);
218
219 errno = 0;
220 if (zram_get_sysfs(z) == NULL) {
221 errno = ENODEV;
222 return 0;
223 }
224
225 DBG(fprintf(stderr, "%s exists", z->devname));
226 return 1;
227 }
228
229 static int zram_set_u64parm(struct zram *z, const char *attr, uint64_t num)
230 {
231 struct sysfs_cxt *sysfs = zram_get_sysfs(z);
232 if (!sysfs)
233 return -EINVAL;
234 DBG(fprintf(stderr, "%s writing %ju to %s", z->devname, num, attr));
235 return sysfs_write_u64(sysfs, attr, num);
236 }
237
238 static int zram_set_strparm(struct zram *z, const char *attr, const char *str)
239 {
240 struct sysfs_cxt *sysfs = zram_get_sysfs(z);
241 if (!sysfs)
242 return -EINVAL;
243 DBG(fprintf(stderr, "%s writing %s to %s", z->devname, str, attr));
244 return sysfs_write_string(sysfs, attr, str);
245 }
246
247
248 static int zram_used(struct zram *z)
249 {
250 uint64_t size;
251 struct sysfs_cxt *sysfs = zram_get_sysfs(z);
252
253 if (sysfs &&
254 sysfs_read_u64(sysfs, "disksize", &size) == 0 &&
255 size > 0) {
256
257 DBG(fprintf(stderr, "%s used", z->devname));
258 return 1;
259 }
260 DBG(fprintf(stderr, "%s unused", z->devname));
261 return 0;
262 }
263
264 static struct zram *find_free_zram(void)
265 {
266 struct zram *z = new_zram(NULL);
267 size_t i;
268 int isfree = 0;
269
270 for (i = 0; isfree == 0; i++) {
271 DBG(fprintf(stderr, "find free: checking zram%zu", i));
272 zram_set_devname(z, NULL, i);
273 if (!zram_exist(z))
274 break;
275 isfree = !zram_used(z);
276 }
277 if (!isfree) {
278 free_zram(z);
279 z = NULL;
280 }
281 return z;
282 }
283
284
285 static char *get_mm_stat(struct zram *z, size_t idx, int bytes)
286 {
287 struct sysfs_cxt *sysfs;
288 const char *name;
289 uint64_t num;
290
291 assert(idx < ARRAY_SIZE(mm_stat_names));
292 assert(z);
293
294 sysfs = zram_get_sysfs(z);
295 if (!sysfs)
296 return NULL;
297
298 /* Linux >= 4.1 uses /sys/block/zram<id>/mm_stat */
299 if (!z->mm_stat && !z->mm_stat_probed) {
300 char *str;
301
302 str = sysfs_strdup(sysfs, "mm_stat");
303 if (str) {
304 z->mm_stat = strv_split(str, " ");
305 if (strv_length(z->mm_stat) < ARRAY_SIZE(mm_stat_names))
306 errx(EXIT_FAILURE, _("Failed to parse mm_stat"));
307 }
308 z->mm_stat_probed = 1;
309 free(str);
310
311 }
312
313 if (z->mm_stat) {
314 if (bytes)
315 return xstrdup(z->mm_stat[idx]);
316
317 num = strtou64_or_err(z->mm_stat[idx], _("Failed to parse mm_stat"));
318 return size_to_human_string(SIZE_SUFFIX_1LETTER, num);
319 }
320
321 /* Linux < 4.1 uses /sys/block/zram<id>/<attrname> */
322 name = mm_stat_names[idx];
323 if (bytes)
324 return sysfs_strdup(sysfs, name);
325 else if (sysfs_read_u64(sysfs, name, &num) == 0)
326 return size_to_human_string(SIZE_SUFFIX_1LETTER, num);
327 return NULL;
328 }
329
330 static void fill_table_row(struct libscols_table *tb, struct zram *z)
331 {
332 static struct libscols_line *ln;
333 struct sysfs_cxt *sysfs;
334 size_t i;
335 uint64_t num;
336
337 assert(tb);
338 assert(z);
339
340 DBG(fprintf(stderr, "%s: filling status table", z->devname));
341
342 sysfs = zram_get_sysfs(z);
343 if (!sysfs)
344 return;
345
346 ln = scols_table_new_line(tb, NULL);
347 if (!ln)
348 err(EXIT_FAILURE, _("failed to initialize output line"));
349
350 for (i = 0; i < (size_t) ncolumns; i++) {
351 char *str = NULL;
352
353 switch (get_column_id(i)) {
354 case COL_NAME:
355 str = xstrdup(z->devname);
356 break;
357 case COL_DISKSIZE:
358 if (inbytes)
359 str = sysfs_strdup(sysfs, "disksize");
360 else if (sysfs_read_u64(sysfs, "disksize", &num) == 0)
361 str = size_to_human_string(SIZE_SUFFIX_1LETTER, num);
362 break;
363 case COL_ALGORITHM:
364 {
365 char *alg = sysfs_strdup(sysfs, "comp_algorithm");
366 if (!alg)
367 break;
368 if (strstr(alg, "[lzo]") == NULL) {
369 if (strstr(alg, "[lz4]") == NULL)
370 ;
371 else
372 str = xstrdup("lz4");
373 } else
374 str = xstrdup("lzo");
375 free(alg);
376 break;
377 }
378 case COL_MOUNTPOINT:
379 {
380 char path[PATH_MAX] = { '\0' };
381 int fl;
382
383 check_mount_point(z->devname, &fl, path, sizeof(path));
384 if (*path)
385 str = xstrdup(path);
386 break;
387 }
388 case COL_STREAMS:
389 str = sysfs_strdup(sysfs, "max_comp_streams");
390 break;
391 case COL_ZEROPAGES:
392 str = get_mm_stat(z, MM_ZERO_PAGES, 1);
393 break;
394 case COL_ORIG_SIZE:
395 str = get_mm_stat(z, MM_ORIG_DATA_SIZE, inbytes);
396 break;
397 case COL_COMP_SIZE:
398 str = get_mm_stat(z, MM_COMPR_DATA_SIZE, inbytes);
399 break;
400 case COL_MEMTOTAL:
401 str = get_mm_stat(z, MM_MEM_USED_TOTAL, inbytes);
402 break;
403 case COL_MEMLIMIT:
404 str = get_mm_stat(z, MM_MEM_LIMIT, inbytes);
405 break;
406 case COL_MEMUSED:
407 str = get_mm_stat(z, MM_MEM_USED_MAX, inbytes);
408 break;
409 case COL_MIGRATED:
410 str = get_mm_stat(z, MM_NUM_MIGRATED, inbytes);
411 break;
412 }
413 if (str)
414 scols_line_refer_data(ln, i, str);
415 }
416 }
417
418 static void status(struct zram *z)
419 {
420 struct libscols_table *tb;
421 size_t i;
422
423 scols_init_debug(0);
424
425 tb = scols_new_table();
426 if (!tb)
427 err(EXIT_FAILURE, _("failed to initialize output table"));
428
429 scols_table_enable_raw(tb, raw);
430 scols_table_enable_noheadings(tb, no_headings);
431
432 for (i = 0; i < (size_t) ncolumns; i++) {
433 const struct colinfo *col = get_column_info(i);
434
435 if (!scols_table_new_column(tb, col->name, col->whint, col->flags))
436 err(EXIT_FAILURE, _("failed to initialize output column"));
437 }
438
439 if (z)
440 fill_table_row(tb, z); /* just one device specified */
441 else {
442 /* list all used devices */
443 z = new_zram(NULL);
444
445 for (i = 0; ; i++) {
446 zram_set_devname(z, NULL, i);
447 if (!zram_exist(z))
448 break;
449 if (zram_used(z))
450 fill_table_row(tb, z);
451 }
452 free_zram(z);
453 }
454
455 scols_print_table(tb);
456 scols_unref_table(tb);
457 }
458
459 static void __attribute__ ((__noreturn__)) usage(FILE * out)
460 {
461 size_t i;
462
463 fputs(USAGE_HEADER, out);
464 fprintf(out, _( " %1$s [options] <device>\n"
465 " %1$s -r <device> [...]\n"
466 " %1$s [options] -f | <device> -s <size>\n"),
467 program_invocation_short_name);
468
469 fputs(USAGE_SEPARATOR, out);
470 fputs(_("Set up and control zram devices.\n"), out);
471
472 fputs(USAGE_OPTIONS, out);
473 fputs(_(" -a, --algorithm lzo|lz4 compression algorithm to use\n"), out);
474 fputs(_(" -b, --bytes print sizes in bytes rather than in human readable format\n"), out);
475 fputs(_(" -f, --find find a free device\n"), out);
476 fputs(_(" -n, --noheadings don't print headings\n"), out);
477 fputs(_(" -o, --output <list> columns to use for status output\n"), out);
478 fputs(_(" --raw use raw status output format\n"), out);
479 fputs(_(" -r, --reset reset all specified devices\n"), out);
480 fputs(_(" -s, --size <size> device size\n"), out);
481 fputs(_(" -t, --streams <number> number of compression streams\n"), out);
482
483 fputs(USAGE_SEPARATOR, out);
484 fputs(USAGE_HELP, out);
485 fputs(USAGE_VERSION, out);
486
487 fputs(_("\nAvailable columns (for --output):\n"), out);
488 for (i = 0; i < ARRAY_SIZE(infos); i++)
489 fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help));
490
491 fprintf(out, USAGE_MAN_TAIL("zramctl(8)"));
492 exit(out == stderr ? 1 : EXIT_SUCCESS);
493 }
494
495 /* actions */
496 enum {
497 A_NONE = 0,
498 A_STATUS,
499 A_CREATE,
500 A_FINDONLY,
501 A_RESET
502 };
503
504 int main(int argc, char **argv)
505 {
506 uintmax_t size = 0, nstreams = 0;
507 char *algorithm = NULL;
508 int rc = 0, c, find = 0, act = A_NONE;
509 struct zram *zram = NULL;
510
511 enum { OPT_RAW = CHAR_MAX + 1 };
512
513 static const struct option longopts[] = {
514 { "algorithm", required_argument, NULL, 'a' },
515 { "bytes", no_argument, NULL, 'b' },
516 { "find", no_argument, NULL, 'f' },
517 { "help", no_argument, NULL, 'h' },
518 { "output", required_argument, NULL, 'o' },
519 { "noheadings",no_argument, NULL, 'n' },
520 { "reset", no_argument, NULL, 'r' },
521 { "raw", no_argument, NULL, OPT_RAW },
522 { "size", required_argument, NULL, 's' },
523 { "streams", required_argument, NULL, 't' },
524 { "version", no_argument, NULL, 'V' },
525 { NULL, 0, NULL, 0 }
526 };
527
528 static const ul_excl_t excl[] = {
529 { 'f', 'o', 'r' },
530 { 'o', 'r', 's' },
531 { 0 }
532 };
533 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
534
535 setlocale(LC_ALL, "");
536 bindtextdomain(PACKAGE, LOCALEDIR);
537 textdomain(PACKAGE);
538 atexit(close_stdout);
539
540 while ((c = getopt_long(argc, argv, "a:bfho:nrs:t:V", longopts, NULL)) != -1) {
541
542 err_exclusive_options(c, longopts, excl, excl_st);
543
544 switch (c) {
545 case 'a':
546 if (strcmp(optarg,"lzo") && strcmp(optarg,"lz4"))
547 errx(EXIT_FAILURE, _("unsupported algorithm: %s"),
548 optarg);
549 algorithm = optarg;
550 break;
551 case 'b':
552 inbytes = 1;
553 break;
554 case 'f':
555 find = 1;
556 break;
557 case 'o':
558 ncolumns = string_to_idarray(optarg,
559 columns, ARRAY_SIZE(columns),
560 column_name_to_id);
561 if (ncolumns < 0)
562 return EXIT_FAILURE;
563 break;
564 case 's':
565 size = strtosize_or_err(optarg, _("failed to parse size"));
566 act = A_CREATE;
567 break;
568 case 't':
569 nstreams = strtou64_or_err(optarg, _("failed to parse streams"));
570 break;
571 case 'r':
572 act = A_RESET;
573 break;
574 case OPT_RAW:
575 raw = 1;
576 break;
577 case 'n':
578 no_headings = 1;
579 break;
580 case 'V':
581 printf(UTIL_LINUX_VERSION);
582 return EXIT_SUCCESS;
583 case 'h':
584 usage(stdout);
585 default:
586 usage(stderr);
587 }
588 }
589
590 if (find && optind < argc)
591 errx(EXIT_FAILURE, _("option --find is mutually exclusive "
592 "with <device>"));
593 if (act == A_NONE)
594 act = find ? A_FINDONLY : A_STATUS;
595
596 if (act != A_RESET && optind + 1 < argc)
597 errx(EXIT_FAILURE, _("only one <device> at a time is allowed"));
598
599 if ((act == A_STATUS || act == A_FINDONLY) && (algorithm || nstreams))
600 errx(EXIT_FAILURE, _("options --algorithm and --streams "
601 "must be combined with --size"));
602
603 switch (act) {
604 case A_STATUS:
605 if (!ncolumns) { /* default columns */
606 columns[ncolumns++] = COL_NAME;
607 columns[ncolumns++] = COL_ALGORITHM;
608 columns[ncolumns++] = COL_DISKSIZE;
609 columns[ncolumns++] = COL_ORIG_SIZE;
610 columns[ncolumns++] = COL_COMP_SIZE;
611 columns[ncolumns++] = COL_MEMTOTAL;
612 columns[ncolumns++] = COL_STREAMS;
613 columns[ncolumns++] = COL_MOUNTPOINT;
614 }
615 if (optind < argc) {
616 zram = new_zram(argv[optind++]);
617 if (!zram_exist(zram))
618 err(EXIT_FAILURE, "%s", zram->devname);
619 }
620 status(zram);
621 free_zram(zram);
622 break;
623 case A_RESET:
624 if (optind == argc)
625 errx(EXIT_FAILURE, _("no device specified"));
626 while (optind < argc) {
627 zram = new_zram(argv[optind]);
628 if (!zram_exist(zram)
629 || zram_set_u64parm(zram, "reset", 1)) {
630 warn(_("%s: failed to reset"), zram->devname);
631 rc = 1;
632 }
633 free_zram(zram);
634 optind++;
635 }
636 break;
637 case A_FINDONLY:
638 zram = find_free_zram();
639 if (!zram)
640 errx(EXIT_FAILURE, _("no free zram device found"));
641 printf("%s\n", zram->devname);
642 free_zram(zram);
643 break;
644 case A_CREATE:
645 if (find) {
646 zram = find_free_zram();
647 if (!zram)
648 errx(EXIT_FAILURE, _("no free zram device found"));
649 } else if (optind == argc)
650 errx(EXIT_FAILURE, _("no device specified"));
651 else {
652 zram = new_zram(argv[optind]);
653 if (!zram_exist(zram))
654 err(EXIT_FAILURE, "%s", zram->devname);
655 }
656
657 if (zram_set_u64parm(zram, "reset", 1))
658 err(EXIT_FAILURE, _("%s: failed to reset"), zram->devname);
659
660 if (nstreams &&
661 zram_set_u64parm(zram, "max_comp_streams", nstreams))
662 err(EXIT_FAILURE, _("%s: failed to set number of streams"), zram->devname);
663
664 if (algorithm &&
665 zram_set_strparm(zram, "comp_algorithm", algorithm))
666 err(EXIT_FAILURE, _("%s: failed to set algorithm"), zram->devname);
667
668 if (zram_set_u64parm(zram, "disksize", size))
669 err(EXIT_FAILURE, _("%s: failed to set disksize (%ju bytes)"),
670 zram->devname, size);
671 if (find)
672 printf("%s\n", zram->devname);
673 free_zram(zram);
674 break;
675 }
676
677 return rc ? EXIT_FAILURE : EXIT_SUCCESS;
678 }