/* * f2fs_io.c - f2fs ioctl utility * * Author: Jaegeuk Kim * * Copied portion of the code from ../f2fscrypt.c */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #ifndef _LARGEFILE_SOURCE #define _LARGEFILE_SOURCE #endif #ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE #endif #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "f2fs_io.h" struct cmd_desc { const char *cmd_name; void (*cmd_func)(int, char **, const struct cmd_desc *); const char *cmd_desc; const char *cmd_help; int cmd_flags; }; #define shutdown_desc "shutdown filesystem" #define shutdown_help \ "f2fs_io shutdown [level] [dir]\n\n" \ "Freeze and stop all IOs given mount point\n" \ "level can be\n" \ " 0 : going down with full sync\n" \ " 1 : going down with checkpoint only\n" \ " 2 : going down with no sync\n" \ " 3 : going down with metadata flush\n" \ " 4 : going down with fsck mark\n" static void do_shutdown(int argc, char **argv, const struct cmd_desc *cmd) { u32 flag; int ret, fd; if (argc != 3) { fputs("Excess arguments\n\n", stderr); fputs(cmd->cmd_help, stderr); exit(1); } flag = atoi(argv[1]); if (flag >= F2FS_GOING_DOWN_MAX) { fputs("Wrong level\n\n", stderr); fputs(cmd->cmd_help, stderr); exit(1); } fd = open(argv[2], O_RDONLY); if (fd == -1) { fputs("Open failed\n\n", stderr); fputs(cmd->cmd_help, stderr); exit(1); } ret = ioctl(fd, F2FS_IOC_SHUTDOWN, &flag); if (ret < 0) { perror("F2FS_IOC_SHUTDOWN"); exit(1); } printf("Shutdown %s with level=%d\n", argv[2], flag); exit(0); } #define pinfile_desc "pin file control" #define pinfile_help \ "f2fs_io pinfile [get|set] [file]\n\n" \ "get/set pinning given the file\n" \ static void do_pinfile(int argc, char **argv, const struct cmd_desc *cmd) { u32 pin; int ret, fd; if (argc != 3) { fputs("Excess arguments\n\n", stderr); fputs(cmd->cmd_help, stderr); exit(1); } fd = open(argv[2], O_RDWR); if (fd == -1) { fputs("Open failed\n\n", stderr); fputs(cmd->cmd_help, stderr); exit(1); } ret = -1; if (!strcmp(argv[1], "set")) { pin = 1; ret = ioctl(fd, F2FS_IOC_SET_PIN_FILE, &pin); if (ret != 0) { perror("set_pin_file failed"); exit(1); } printf("set_pin_file: %u blocks moved in %s\n", ret, argv[2]); } else if (!strcmp(argv[1], "get")) { unsigned int flags; ret = ioctl(fd, F2FS_IOC_GET_PIN_FILE, &pin); if (ret < 0) { perror("pin_file failed"); exit(1); } ret = ioctl(fd, F2FS_IOC_GETFLAGS, &flags); if (ret < 0) { perror("get flags failed"); exit(1); } printf("get_pin_file: %s with %u blocks moved in %s\n", (flags & F2FS_NOCOW_FL) ? "pinned" : "un-pinned", pin, argv[2]); } exit(0); } #define write_desc "write data into file" #define write_help \ "f2fs_io write [chunk_size in 4kb] [offset in chunk_size] [count] [pattern] [IO] [file_path]\n\n" \ "Write given patten data in file_path\n" \ "pattern can be\n" \ " zero : zeros\n" \ " inc_num : incrementing numbers\n" \ " rand : random numbers\n" \ "IO can be\n" \ " buffered : buffered IO\n" \ " dio : direct IO\n" \ static void do_write(int argc, char **argv, const struct cmd_desc *cmd) { u64 buf_size = 0, inc_num = 0, ret = 0, written = 0; u64 offset; char *buf = NULL; unsigned bs, count, i; int flags = 0; int fd; srand(time(0)); if (argc != 7) { fputs("Excess arguments\n\n", stderr); fputs(cmd->cmd_help, stderr); exit(1); } bs = atoi(argv[1]); if (bs > 1024) { fputs("Too big chunk size - limit: 4MB\n\n", stderr); exit(1); } buf_size = bs * 4096; offset = atoi(argv[2]) * buf_size; buf = aligned_alloc(4096, buf_size); if (!buf) { fputs("Memory alloc failed\n\n", stderr); exit(1); } count = atoi(argv[3]); if (!strcmp(argv[4], "zero")) { memset(buf, 0, buf_size); } else if (strcmp(argv[4], "inc_num") && strcmp(argv[4], "rand")) { fputs("Wrong pattern type\n\n", stderr); exit(1); } if (!strcmp(argv[5], "buffered")) { flags |= O_DIRECT; } else if (strcmp(argv[5], "dio")) { fputs("Wrong IO type\n\n", stderr); exit(1); } fd = open(argv[6], O_CREAT | O_WRONLY | flags, 0755); if (fd == -1) { fputs("Open failed\n\n", stderr); exit(1); } for (i = 0; i < count; i++) { if (!strcmp(argv[4], "inc_num")) *(int *)buf = inc_num++; else if (!strcmp(argv[4], "rand")) *(int *)buf = rand(); /* write data */ ret = pwrite(fd, buf, buf_size, offset + buf_size * i); if (ret != buf_size) break; written += ret; } printf("Written %"PRIu64" bytes with pattern=%s\n", written, argv[4]); exit(0); } #define read_desc "read data from file" #define read_help \ "f2fs_io read [chunk_size in 4kb] [offset in chunk_size] [count] [IO] [print_nbytes] [file_path]\n\n" \ "Read data in file_path and print nbytes\n" \ "IO can be\n" \ " buffered : buffered IO\n" \ " dio : direct IO\n" \ static void do_read(int argc, char **argv, const struct cmd_desc *cmd) { u64 buf_size = 0, ret = 0, read_cnt = 0; u64 offset; char *buf = NULL; char *print_buf = NULL; unsigned bs, count, i, print_bytes; int flags = 0; int fd; if (argc != 7) { fputs("Excess arguments\n\n", stderr); fputs(cmd->cmd_help, stderr); exit(1); } bs = atoi(argv[1]); if (bs > 1024) { fputs("Too big chunk size - limit: 4MB\n\n", stderr); exit(1); } buf_size = bs * 4096; offset = atoi(argv[2]) * buf_size; buf = aligned_alloc(4096, buf_size); if (!buf) { fputs("Memory alloc failed\n\n", stderr); exit(1); } count = atoi(argv[3]); if (!strcmp(argv[4], "buffered")) { flags |= O_DIRECT; } else if (strcmp(argv[4], "dio")) { fputs("Wrong IO type\n\n", stderr); exit(1); } print_bytes = atoi(argv[5]); if (print_bytes > buf_size) { fputs("Print_nbytes should be less then chunk_size in kb\n\n", stderr); exit(1); } print_buf = malloc(print_bytes); if (!print_buf) { fputs("Memory alloc failed\n\n", stderr); exit(1); } fd = open(argv[6], O_RDONLY | flags); if (fd == -1) { fputs("Open failed\n\n", stderr); exit(1); } for (i = 0; i < count; i++) { ret = pread(fd, buf, buf_size, offset + buf_size * i); if (ret != buf_size) break; read_cnt += ret; if (i == 0) memcpy(print_buf, buf, print_bytes); } printf("Read %"PRIu64" bytes and print %u bytes:\n", read_cnt, print_bytes); printf("%08"PRIx64" : ", offset); for (i = 1; i <= print_bytes; i++) { printf("%02x", print_buf[i - 1]); if (i % 16 == 0) printf("\n%08"PRIx64" : ", offset + 16 * i); else if (i % 2 == 0) printf(" "); } printf("\n"); exit(0); } struct file_ext { __u32 f_pos; __u32 start_blk; __u32 end_blk; __u32 blk_count; }; #ifndef FIBMAP #define FIBMAP _IO(0x00, 1) /* bmap access */ #endif #define fiemap_desc "get block address in file" #define fiemap_help \ "f2fs_io fiemap [offset in 4kb] [count] [file_path]\n\n"\ static void do_fiemap(int argc, char **argv, const struct cmd_desc *cmd) { u64 offset; u32 blknum; unsigned count, i; int fd; if (argc != 4) { fputs("Excess arguments\n\n", stderr); fputs(cmd->cmd_help, stderr); exit(1); } offset = atoi(argv[1]); count = atoi(argv[2]); fd = open(argv[3], O_RDONLY | O_LARGEFILE); if (fd == -1) { fputs("Open failed\n\n", stderr); exit(1); } printf("Fiemap: offset = %08"PRIx64" len = %d\n", offset, count); for (i = 0; i < count; i++) { blknum = offset + i; if (ioctl(fd, FIBMAP, &blknum) < 0) { fputs("FIBMAP failed\n\n", stderr); exit(1); } printf("%u ", blknum); } printf("\n"); exit(0); } #define gc_urgent_desc "start/end/run gc_urgent for given time period" #define gc_urgent_help \ "f2fs_io gc_urgent $dev [start/end/run] [time in sec]\n\n"\ " - f2fs_io gc_urgent sda21 start\n" \ " - f2fs_io gc_urgent sda21 end\n" \ " - f2fs_io gc_urgent sda21 run 10\n" \ static void do_gc_urgent(int argc, char **argv, const struct cmd_desc *cmd) { char command[255]; if (argc == 3 && !strcmp(argv[2], "start")) { printf("gc_urgent: start on %s\n", argv[1]); sprintf(command, "echo %d > %s/%s/gc_urgent", 1, "/sys/fs/f2fs/", argv[1]); if (system(command)) exit(1); } else if (argc == 3 && !strcmp(argv[2], "end")) { printf("gc_urgent: end on %s\n", argv[1]); sprintf(command, "echo %d > %s/%s/gc_urgent", 0, "/sys/fs/f2fs/", argv[1]); if (system(command)) exit(1); } else if (argc == 4 && !strcmp(argv[2], "run")) { printf("gc_urgent: start on %s for %d secs\n", argv[1], atoi(argv[3])); sprintf(command, "echo %d > %s/%s/gc_urgent", 1, "/sys/fs/f2fs/", argv[1]); if (system(command)) exit(1); sleep(atoi(argv[3])); printf("gc_urgent: end on %s for %d secs\n", argv[1], atoi(argv[3])); sprintf(command, "echo %d > %s/%s/gc_urgent", 0, "/sys/fs/f2fs/", argv[1]); if (system(command)) exit(1); } else { fputs("Excess arguments\n\n", stderr); fputs(cmd->cmd_help, stderr); exit(1); } } #define CMD_HIDDEN 0x0001 #define CMD(name) { #name, do_##name, name##_desc, name##_help, 0 } #define _CMD(name) { #name, do_##name, NULL, NULL, CMD_HIDDEN } static void do_help(int argc, char **argv, const struct cmd_desc *cmd); const struct cmd_desc cmd_list[] = { _CMD(help), CMD(shutdown), CMD(pinfile), CMD(write), CMD(read), CMD(fiemap), CMD(gc_urgent), { NULL, NULL, NULL, NULL, 0 } }; static void do_help(int argc, char **argv, const struct cmd_desc *UNUSED(cmd)) { const struct cmd_desc *p; if (argc > 1) { for (p = cmd_list; p->cmd_name; p++) { if (p->cmd_flags & CMD_HIDDEN) continue; if (strcmp(p->cmd_name, argv[1]) == 0) { putc('\n', stdout); fputs("USAGE:\n ", stdout); fputs(p->cmd_help, stdout); exit(0); } } printf("Unknown command: %s\n\n", argv[1]); } fputs("Available commands:\n", stdout); for (p = cmd_list; p->cmd_name; p++) { if (p->cmd_flags & CMD_HIDDEN) continue; printf(" %-20s %s\n", p->cmd_name, p->cmd_desc); } printf("\nTo get more information on a command, " "type 'f2fs_io help cmd'\n"); exit(0); } static void die_signal_handler(int UNUSED(signum), siginfo_t *UNUSED(siginfo), void *UNUSED(context)) { exit(-1); } static void sigcatcher_setup(void) { struct sigaction sa; memset(&sa, 0, sizeof(struct sigaction)); sa.sa_sigaction = die_signal_handler; sa.sa_flags = SA_SIGINFO; sigaction(SIGHUP, &sa, 0); sigaction(SIGINT, &sa, 0); sigaction(SIGQUIT, &sa, 0); sigaction(SIGFPE, &sa, 0); sigaction(SIGILL, &sa, 0); sigaction(SIGBUS, &sa, 0); sigaction(SIGSEGV, &sa, 0); sigaction(SIGABRT, &sa, 0); sigaction(SIGPIPE, &sa, 0); sigaction(SIGALRM, &sa, 0); sigaction(SIGTERM, &sa, 0); sigaction(SIGUSR1, &sa, 0); sigaction(SIGUSR2, &sa, 0); sigaction(SIGPOLL, &sa, 0); sigaction(SIGPROF, &sa, 0); sigaction(SIGSYS, &sa, 0); sigaction(SIGTRAP, &sa, 0); sigaction(SIGVTALRM, &sa, 0); sigaction(SIGXCPU, &sa, 0); sigaction(SIGXFSZ, &sa, 0); } int main(int argc, char **argv) { const struct cmd_desc *cmd; if (argc < 2) do_help(argc, argv, cmd_list); sigcatcher_setup(); for (cmd = cmd_list; cmd->cmd_name; cmd++) { if (strcmp(cmd->cmd_name, argv[1]) == 0) { cmd->cmd_func(argc - 1, argv + 1, cmd); exit(0); } } printf("Unknown command: %s\n\n", argv[1]); do_help(1, argv, cmd_list); return 0; }