// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2010 Red Hat, Inc. */ /*\ * [Description] * * zram: generic RAM based compressed R/W block devices * http://lkml.org/lkml/2010/8/9/227 * * This case check whether data read from zram device is consistent with * thoese are written. */ #include #include #include #include #include #include #include #include #include "tst_safe_stdio.h" #include "tst_test.h" #define ZRAM_CONTROL_PATH "/sys/class/zram-control" #define HOT_ADD_PATH ZRAM_CONTROL_PATH"/hot_add" #define HOT_REMOVE_PATH ZRAM_CONTROL_PATH"/hot_remove" #define SIZE (512 * 1024 * 1024L) static char zram_block_path[100], zram_dev_path[100]; static int modprobe, dev_num, hot_add_flag; static const char *const cmd_rmmod[] = {"rmmod", "zram", NULL}; static void set_disksize(void) { char disksize_path[200]; tst_res(TINFO, "create a zram device with %ld bytes in size", SIZE); sprintf(disksize_path, "%s/disksize", zram_block_path); SAFE_FILE_PRINTF(disksize_path, "%ld", SIZE); } static void write_device(void) { int fd; char *s; tst_res(TINFO, "map this zram device into memory"); fd = SAFE_OPEN(zram_dev_path, O_RDWR); s = SAFE_MMAP(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); tst_res(TINFO, "write all the memory"); memset(s, 'a', SIZE - 1); s[SIZE - 1] = '\0'; SAFE_MUNMAP(s, SIZE); SAFE_CLOSE(fd); } static void verify_device(void) { int fd; long i = 0, fail = 0; char *s; tst_res(TINFO, "verify contents from device"); fd = SAFE_OPEN(zram_dev_path, O_RDONLY); s = SAFE_MMAP(NULL, SIZE, PROT_READ, MAP_PRIVATE, fd, 0); while (s[i] && i < SIZE - 1) { if (s[i] != 'a') fail++; i++; } if (i != SIZE - 1) { tst_res(TFAIL, "expect size: %ld, actual size: %ld.", SIZE - 1, i); } else if (s[i] != '\0') { tst_res(TFAIL, "zram device seems not null terminated"); } else if (fail) { tst_res(TFAIL, "%ld failed bytes found", fail); } else { tst_res(TPASS, "data read from zram device is consistent with those are written"); } SAFE_MUNMAP(s, SIZE); SAFE_CLOSE(fd); } static void reset_zram(void) { char reset_path[200]; tst_res(TINFO, "Reset zram"); sprintf(reset_path, "%s/reset", zram_block_path); SAFE_FILE_PRINTF(reset_path, "1"); } static void print(char *string) { char filename[BUFSIZ], value[BUFSIZ]; tst_res(TINFO, "%s", zram_block_path); sprintf(filename, "%s/%s", zram_block_path, string); SAFE_FILE_SCANF(filename, "%s", value); tst_res(TINFO, "%s is %s", filename, value); } static void print_stat(char *nread, char *nwrite) { char nread_val[BUFSIZ], nwrite_val[BUFSIZ]; char zram_stat_path[100]; sprintf(zram_stat_path, "/sys/block/zram%d/stat", dev_num); SAFE_FILE_SCANF(zram_stat_path, "%s %*s %*s %*s %s", nread_val, nwrite_val); tst_res(TINFO, "%s from %s is %s", nread, zram_stat_path, nread_val); tst_res(TINFO, "%s from %s is %s", nwrite, zram_stat_path, nwrite_val); } static void print_mm_stat(char *orig, char *compr, char *mem, char *zero) { char orig_val[BUFSIZ], compr_val[BUFSIZ]; char mem_val[BUFSIZ], zero_val[BUFSIZ]; char zram_mm_stat_path[100]; sprintf(zram_mm_stat_path, "/sys/block/zram%d/mm_stat", dev_num); SAFE_FILE_SCANF(zram_mm_stat_path, "%s %s %s %*s %*s %s", orig_val, compr_val, mem_val, zero_val); tst_res(TINFO, "%s from %s is %s", orig, zram_mm_stat_path, orig_val); tst_res(TINFO, "%s from %s is %s", compr, zram_mm_stat_path, compr_val); tst_res(TINFO, "%s from %s is %s", mem, zram_mm_stat_path, mem_val); tst_res(TINFO, "%s from %s is %s", zero, zram_mm_stat_path, zero_val); } static void dump_info(void) { char zram_obsolete_file_path[100]; sprintf(zram_obsolete_file_path, "/sys/block/zram%d/num_reads", dev_num); print("initstate"); print("disksize"); if (!access(zram_obsolete_file_path, F_OK)) { print("orig_data_size"); print("compr_data_size"); print("mem_used_total"); print("zero_pages"); print("num_reads"); print("num_writes"); } else { print_mm_stat("orig_data_size", "compr_data_size", "mem_used_total", "zero/same_pages"); print_stat("num_reads", "num_writes"); } } static void run(void) { set_disksize(); write_device(); dump_info(); verify_device(); reset_zram(); dump_info(); } static void setup(void) { const char *const cmd_modprobe[] = {"modprobe", "zram", NULL}; const char *const cmd_zramctl[] = {"zramctl", "-f", NULL}; const char *zramctl_log_path = "zramctl.log"; FILE *file; char line[PATH_MAX]; int fd; /* zram module was built in or loaded on new kernel */ if (!access(ZRAM_CONTROL_PATH, F_OK)) { tst_res(TINFO, "zram module already loaded, kernel supports zram-control interface"); SAFE_FILE_SCANF(HOT_ADD_PATH, "%d", &dev_num); hot_add_flag = 1; goto fill_path; } /* zram module was built in or being used on old kernel */ SAFE_CMD(cmd_modprobe, NULL, NULL); file = SAFE_FOPEN("/proc/modules", "r"); while (fgets(line, sizeof(line), file)) { if (strstr(line, "zram")) { modprobe = 1; break; } } SAFE_FCLOSE(file); if (access(ZRAM_CONTROL_PATH, F_OK)) { if (modprobe) { tst_res(TINFO, "rmmod zram before test on old kernel without zram-control interface"); if (!tst_cmd(cmd_rmmod, NULL, NULL, TST_CMD_PASS_RETVAL)) { SAFE_CMD(cmd_modprobe, NULL, NULL); goto fill_path; } } else { tst_res(TINFO, "zram module is built in old kernel without zram-control interface"); } modprobe = 0; tst_res(TINFO, "use zramctl -f to find free zram device"); fd = SAFE_OPEN(zramctl_log_path, O_CREAT | O_RDWR, 0644); SAFE_CLOSE(fd); if (tst_cmd(cmd_zramctl, zramctl_log_path, NULL, TST_CMD_PASS_RETVAL)) tst_brk(TCONF | TERRNO, "zramctl -f failed"); else SAFE_FILE_SCANF(zramctl_log_path, "/dev/zram%d", &dev_num); } fill_path: sprintf(zram_block_path, "/sys/block/zram%d", dev_num); sprintf(zram_dev_path, "/dev/zram%d", dev_num); } static void cleanup(void) { if (hot_add_flag) SAFE_FILE_PRINTF(HOT_REMOVE_PATH, "%d", dev_num); if (modprobe) SAFE_CMD(cmd_rmmod, NULL, NULL); } static struct tst_test test = { .test_all = run, .setup = setup, .cleanup = cleanup, .needs_root = 1, .needs_tmpdir = 1, .needs_drivers = (const char *const []) { "zram", NULL }, .needs_cmds = (const char *[]) { "modprobe", "rmmod", NULL } };