diff --git a/autogen.sh b/autogen.sh old mode 100644 new mode 100755 diff --git a/fsck/Makefile.am b/fsck/Makefile.am index 6c19e11..73df884 100644 --- a/fsck/Makefile.am +++ b/fsck/Makefile.am @@ -3,8 +3,9 @@ AM_CPPFLAGS = ${libuuid_CFLAGS} -I$(top_srcdir)/include AM_CFLAGS = -Wall sbin_PROGRAMS = fsck.f2fs -fsck_f2fs_SOURCES = main.c fsck.c dump.c mount.c f2fs.h fsck.h $(top_srcdir)/include/f2fs_fs.h +fsck_f2fs_SOURCES = main.c fsck.c dump.c mount.c defrag.c f2fs.h fsck.h $(top_srcdir)/include/f2fs_fs.h fsck_f2fs_LDADD = ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la install-data-hook: ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/dump.f2fs + ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/defrag.f2fs diff --git a/fsck/defrag.c b/fsck/defrag.c new file mode 100644 index 0000000..7ca7260 --- /dev/null +++ b/fsck/defrag.c @@ -0,0 +1,101 @@ +/** + * defrag.c + * + * Copyright (c) 2015 Jaegeuk Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include "fsck.h" + +static int migrate_block(struct f2fs_sb_info *sbi, u64 from, u64 to) +{ + void *raw = calloc(BLOCK_SZ, 1); + struct seg_entry *se; + struct f2fs_summary sum; + u64 offset; + int ret, type; + + ASSERT(raw != NULL); + + /* read from */ + ret = dev_read_block(raw, from); + ASSERT(ret >= 0); + + /* write to */ + ret = dev_write_block(raw, to); + ASSERT(ret >= 0); + + /* update sit bitmap & valid_blocks && se->type */ + se = get_seg_entry(sbi, GET_SEGNO(sbi, from)); + offset = OFFSET_IN_SEG(sbi, from); + type = se->type; + se->valid_blocks--; + f2fs_clear_bit(offset, (char *)se->cur_valid_map); + se->dirty = 1; + + se = get_seg_entry(sbi, GET_SEGNO(sbi, to)); + offset = OFFSET_IN_SEG(sbi, to); + se->type = type; + se->valid_blocks++; + f2fs_set_bit(offset, (char *)se->cur_valid_map); + se->dirty = 1; + + /* read/write SSA */ + get_sum_entry(sbi, from, &sum); + update_sum_entry(sbi, to, &sum); + + /* if data block, read node and update node block */ + if (IS_DATASEG(type)) + update_data_blkaddr(sbi, le32_to_cpu(sum.nid), + le16_to_cpu(sum.ofs_in_node), to); + else + update_nat_blkaddr(sbi, le32_to_cpu(sum.nid), to); + + DBG(0, "Migrate %s block %"PRIx64" -> %"PRIx64"\n", + IS_DATASEG(type) ? "data" : "node", + from, to); + free(raw); + return 0; +} + +int f2fs_defragment(struct f2fs_sb_info *sbi, u64 from, u64 len, u64 to, int left) +{ + struct seg_entry *se; + u64 idx, offset; + + /* flush NAT/SIT journal entries */ + flush_journal_entries(sbi); + + for (idx = from; idx < from + len; idx++) { + u64 target = to; + + se = get_seg_entry(sbi, GET_SEGNO(sbi, idx)); + offset = OFFSET_IN_SEG(sbi, idx); + + if (!f2fs_test_bit(offset, (const char *)se->cur_valid_map)) + continue; + + if (find_next_free_block(sbi, &target, left, se->type)) { + ASSERT_MSG("Not enough space to migrate blocks"); + break; + } + + if (migrate_block(sbi, idx, target)) { + ASSERT_MSG("Found inconsistency: please run FSCK"); + return -1; + } + } + + /* update curseg info; can update sit->types */ + move_curseg_info(sbi, to); + write_curseg_info(sbi); + + /* flush dirty sit entries */ + flush_sit_entries(sbi); + + write_checkpoint(sbi); + + return 0; +} diff --git a/fsck/f2fs.h b/fsck/f2fs.h index 03a0646..af5cc40 100644 --- a/fsck/f2fs.h +++ b/fsck/f2fs.h @@ -74,6 +74,7 @@ struct seg_entry { unsigned char type; /* segment type like CURSEG_XXX_TYPE */ unsigned char orig_type; /* segment type like CURSEG_XXX_TYPE */ unsigned long long mtime; /* modification time of the segment */ + int dirty; }; struct sec_entry { diff --git a/fsck/fsck.h b/fsck/fsck.h index 4876914..1464146 100644 --- a/fsck/fsck.h +++ b/fsck/fsck.h @@ -121,6 +121,8 @@ extern struct seg_entry *get_seg_entry(struct f2fs_sb_info *, unsigned int); extern struct f2fs_summary_block *get_sum_block(struct f2fs_sb_info *, unsigned int, int *); extern int get_sum_entry(struct f2fs_sb_info *, u32, struct f2fs_summary *); +extern void update_sum_entry(struct f2fs_sb_info *, block_t, + struct f2fs_summary *); extern void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *); extern void nullify_nat_entry(struct f2fs_sb_info *, u32); extern void rewrite_sit_area_bitmap(struct f2fs_sb_info *); @@ -132,9 +134,14 @@ extern void fsck_free(struct f2fs_sb_info *); extern int f2fs_do_mount(struct f2fs_sb_info *); extern void f2fs_do_umount(struct f2fs_sb_info *); +extern void flush_journal_entries(struct f2fs_sb_info *); +extern void flush_sit_entries(struct f2fs_sb_info *); extern void move_curseg_info(struct f2fs_sb_info *, u64); extern void write_curseg_info(struct f2fs_sb_info *); extern int find_next_free_block(struct f2fs_sb_info *, u64 *, int, int); +extern void write_checkpoint(struct f2fs_sb_info *); +extern void update_data_blkaddr(struct f2fs_sb_info *, nid_t, u16, block_t); +extern void update_nat_blkaddr(struct f2fs_sb_info *, nid_t, block_t); extern void print_raw_sb_info(struct f2fs_super_block *); @@ -153,4 +160,7 @@ extern void ssa_dump(struct f2fs_sb_info *, int, int); extern void dump_node(struct f2fs_sb_info *, nid_t); extern int dump_info_from_blkaddr(struct f2fs_sb_info *, u32); +/* defrag.c */ +int f2fs_defragment(struct f2fs_sb_info *, u64, u64, u64, int); + #endif /* _FSCK_H_ */ diff --git a/fsck/main.c b/fsck/main.c index 6b0d97e..d70b9ed 100644 --- a/fsck/main.c +++ b/fsck/main.c @@ -3,6 +3,8 @@ * * Copyright (c) 2013 Samsung Electronics Co., Ltd. * http://www.samsung.com/ + * Copyright (c) 2015 Jaegeuk Kim + * : implement defrag.f2fs * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -38,6 +40,18 @@ void dump_usage() exit(1); } +void defrag_usage() +{ + MSG(0, "\nUsage: defrag.f2fs [options] device\n"); + MSG(0, "[options]:\n"); + MSG(0, " -d debug level [default:0]\n"); + MSG(0, " -s start block address [default: main_blkaddr]\n"); + MSG(0, " -l length [default:512 (2MB)]\n"); + MSG(0, " -t target block address [default: main_blkaddr + 2MB]\n"); + MSG(0, " -i set direction as shrink [default: expand]\n"); + exit(1); +} + void f2fs_parse_options(int argc, char *argv[]) { int option = 0; @@ -128,6 +142,53 @@ void f2fs_parse_options(int argc, char *argv[]) } config.private = &dump_opt; + } else if (!strcmp("defrag.f2fs", prog)) { + const char *option_string = "d:s:l:t:i"; + + config.func = DEFRAG; + while ((option = getopt(argc, argv, option_string)) != EOF) { + int ret = 0; + + switch (option) { + case 'd': + config.dbg_lv = atoi(optarg); + MSG(0, "Info: Debug level = %d\n", + config.dbg_lv); + break; + case 's': + if (strncmp(optarg, "0x", 2)) + ret = sscanf(optarg, "%"PRIu64"", + &config.defrag_start); + else + ret = sscanf(optarg, "%"PRIx64"", + &config.defrag_start); + break; + case 'l': + if (strncmp(optarg, "0x", 2)) + ret = sscanf(optarg, "%"PRIu64"", + &config.defrag_len); + else + ret = sscanf(optarg, "%"PRIx64"", + &config.defrag_len); + break; + case 't': + if (strncmp(optarg, "0x", 2)) + ret = sscanf(optarg, "%"PRIu64"", + &config.defrag_target); + else + ret = sscanf(optarg, "%"PRIx64"", + &config.defrag_target); + break; + case 'i': + config.defrag_shrink = 1; + break; + default: + MSG(0, "\tError: Unknown option %c\n", option); + defrag_usage(); + break; + } + ASSERT(ret >= 0); + } } if ((optind + 1) != argc) { @@ -136,6 +197,8 @@ void f2fs_parse_options(int argc, char *argv[]) fsck_usage(); else if (config.func == DUMP) dump_usage(); + else if (config.func == DEFRAG) + defrag_usage(); } config.device_name = argv[optind]; } @@ -188,6 +251,55 @@ cleanup: fsck_free(sbi); } +static int do_defrag(struct f2fs_sb_info *sbi) +{ + struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); + + if (config.defrag_start > get_sb(block_count)) + goto out_range; + if (config.defrag_start < SM_I(sbi)->main_blkaddr) + config.defrag_start = SM_I(sbi)->main_blkaddr; + + if (config.defrag_len == 0) + config.defrag_len = sbi->blocks_per_seg; + + if (config.defrag_start + config.defrag_len > get_sb(block_count)) + config.defrag_len = get_sb(block_count) - config.defrag_start; + + if (config.defrag_target == 0) { + config.defrag_target = config.defrag_start - 1; + if (!config.defrag_shrink) + config.defrag_target += config.defrag_len + 1; + } + + if (config.defrag_target < SM_I(sbi)->main_blkaddr || + config.defrag_target > get_sb(block_count)) + goto out_range; + if (config.defrag_target >= config.defrag_start && + config.defrag_target < config.defrag_start + config.defrag_len) + goto out_range; + + if (config.defrag_start > config.defrag_target) + MSG(0, "Info: Move 0x%"PRIx64" <- [0x%"PRIx64"-0x%"PRIx64"]\n", + config.defrag_target, + config.defrag_start, + config.defrag_start + config.defrag_len - 1); + else + MSG(0, "Info: Move [0x%"PRIx64"-0x%"PRIx64"] -> 0x%"PRIx64"\n", + config.defrag_start, + config.defrag_start + config.defrag_len - 1, + config.defrag_target); + + return f2fs_defragment(sbi, config.defrag_start, config.defrag_len, + config.defrag_target, config.defrag_shrink); +out_range: + ASSERT_MSG("Out-of-range [0x%"PRIx64" ~ 0x%"PRIx64"] to 0x%"PRIx64"", + config.defrag_start, + config.defrag_start + config.defrag_len - 1, + config.defrag_target); + return -1; +} + int main(int argc, char **argv) { struct f2fs_sb_info *sbi; @@ -198,7 +310,7 @@ int main(int argc, char **argv) f2fs_parse_options(argc, argv); if (f2fs_dev_is_umounted(&config) < 0) { - if (!config.ro) { + if (!config.ro || config.func == DEFRAG) { MSG(0, "\tError: Not available on mounted device!\n"); return -1; } @@ -218,12 +330,8 @@ fsck_again: sbi = &gfsck.sbi; ret = f2fs_do_mount(sbi); - if (ret == 1) { - free(sbi->ckpt); - free(sbi->raw_super); - goto out; - } else if (ret < 0) - return -1; + if (ret != 0) + goto out_err; switch (config.func) { case FSCK: @@ -232,10 +340,14 @@ fsck_again: case DUMP: do_dump(sbi); break; + case DEFRAG: + if (do_defrag(sbi)) + goto out_err; + break; } f2fs_do_umount(sbi); -out: + if (config.func == FSCK && config.bug_on) { if (!config.ro && config.fix_on == 0 && config.auto_fix == 0) { char ans[255] = {0}; @@ -258,4 +370,11 @@ retry: printf("\nDone.\n"); return 0; + +out_err: + if (sbi->ckpt) + free(sbi->ckpt); + if (sbi->raw_super) + free(sbi->raw_super); + return -1; } diff --git a/fsck/mount.c b/fsck/mount.c index 970b159..e773471 100644 --- a/fsck/mount.c +++ b/fsck/mount.c @@ -718,6 +718,33 @@ static void read_normal_summaries(struct f2fs_sb_info *sbi, int type) free(sum_blk); } +void update_sum_entry(struct f2fs_sb_info *sbi, block_t blk_addr, + struct f2fs_summary *sum) +{ + struct f2fs_summary_block *sum_blk; + u32 segno, offset; + int type, ret; + struct seg_entry *se; + + segno = GET_SEGNO(sbi, blk_addr); + offset = OFFSET_IN_SEG(sbi, blk_addr); + + se = get_seg_entry(sbi, segno); + + sum_blk = get_sum_block(sbi, segno, &type); + memcpy(&sum_blk->entries[offset], sum, sizeof(*sum)); + sum_blk->footer.entry_type = IS_NODESEG(se->type) ? SUM_TYPE_NODE : + SUM_TYPE_DATA; + + if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA || + type == SEG_TYPE_MAX) { + u64 ssa_blk = GET_SUM_BLKADDR(sbi, segno); + ret = dev_write_block(sum_blk, ssa_blk); + ASSERT(ret >= 0); + free(sum_blk); + } +} + static void restore_curseg_summaries(struct f2fs_sb_info *sbi) { int type = CURSEG_HOT_DATA; @@ -965,6 +992,88 @@ static void get_nat_entry(struct f2fs_sb_info *sbi, nid_t nid, free(nat_block); } +void update_data_blkaddr(struct f2fs_sb_info *sbi, nid_t nid, + u16 ofs_in_node, block_t newaddr) +{ + struct f2fs_node *node_blk = NULL; + struct node_info ni; + block_t oldaddr, startaddr, endaddr; + int ret; + + node_blk = (struct f2fs_node *)calloc(BLOCK_SZ, 1); + ASSERT(node_blk != NULL); + + get_node_info(sbi, nid, &ni); + + /* read node_block */ + ret = dev_read_block(node_blk, ni.blk_addr); + ASSERT(ret >= 0); + + /* check its block address */ + if (node_blk->footer.nid == node_blk->footer.ino) { + oldaddr = le32_to_cpu(node_blk->i.i_addr[ofs_in_node]); + node_blk->i.i_addr[ofs_in_node] = cpu_to_le32(newaddr); + } else { + oldaddr = le32_to_cpu(node_blk->dn.addr[ofs_in_node]); + node_blk->dn.addr[ofs_in_node] = cpu_to_le32(newaddr); + } + + ret = dev_write_block(node_blk, ni.blk_addr); + ASSERT(ret >= 0); + + /* check extent cache entry */ + if (node_blk->footer.nid != node_blk->footer.ino) { + get_node_info(sbi, le32_to_cpu(node_blk->footer.ino), &ni); + + /* read inode block */ + ret = dev_read_block(node_blk, ni.blk_addr); + ASSERT(ret >= 0); + } + + startaddr = le32_to_cpu(node_blk->i.i_ext.blk_addr); + endaddr = startaddr + le32_to_cpu(node_blk->i.i_ext.len); + if (oldaddr >= startaddr && oldaddr < endaddr) { + node_blk->i.i_ext.len = 0; + + /* update inode block */ + ret = dev_write_block(node_blk, ni.blk_addr); + ASSERT(ret >= 0); + } + free(node_blk); +} + +void update_nat_blkaddr(struct f2fs_sb_info *sbi, nid_t nid, block_t newaddr) +{ + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct f2fs_nat_block *nat_block; + pgoff_t block_off; + pgoff_t block_addr; + int seg_off, entry_off; + int ret; + + nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1); + + block_off = nid / NAT_ENTRY_PER_BLOCK; + entry_off = nid % NAT_ENTRY_PER_BLOCK; + + seg_off = block_off >> sbi->log_blocks_per_seg; + block_addr = (pgoff_t)(nm_i->nat_blkaddr + + (seg_off << sbi->log_blocks_per_seg << 1) + + (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); + + if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) + block_addr += sbi->blocks_per_seg; + + ret = dev_read_block(nat_block, block_addr); + ASSERT(ret >= 0); + + nat_block->entries[entry_off].block_addr = cpu_to_le32(newaddr); + + ret = dev_write_block(nat_block, block_addr); + ASSERT(ret >= 0); + free(nat_block); +} + void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) { struct f2fs_nat_entry raw_nat; @@ -1133,6 +1242,123 @@ void rewrite_sit_area_bitmap(struct f2fs_sb_info *sbi) } } +static void flush_sit_journal_entries(struct f2fs_sb_info *sbi) +{ + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); + struct f2fs_summary_block *sum = curseg->sum_blk; + struct sit_info *sit_i = SIT_I(sbi); + unsigned int segno; + int i; + + for (i = 0; i < sits_in_cursum(sum); i++) { + struct f2fs_sit_block *sit_blk; + struct f2fs_sit_entry *sit; + struct seg_entry *se; + + segno = segno_in_journal(sum, i); + se = get_seg_entry(sbi, segno); + + sit_blk = get_current_sit_page(sbi, segno); + sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno)]; + + memcpy(sit->valid_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE); + sit->vblocks = cpu_to_le16((se->type << SIT_VBLOCKS_SHIFT) | + se->valid_blocks); + sit->mtime = cpu_to_le64(se->mtime); + + rewrite_current_sit_page(sbi, segno, sit_blk); + free(sit_blk); + } + sum->n_sits = 0; +} + +static void flush_nat_journal_entries(struct f2fs_sb_info *sbi) +{ + struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); + struct f2fs_summary_block *sum = curseg->sum_blk; + struct f2fs_nm_info *nm_i = NM_I(sbi); + struct f2fs_nat_block *nat_block; + pgoff_t block_off; + pgoff_t block_addr; + int seg_off, entry_off; + nid_t nid; + int ret; + int i = 0; + +next: + if (i >= nats_in_cursum(sum)) { + sum->n_nats = 0; + return; + } + + nid = le32_to_cpu(nid_in_journal(sum, i)); + nat_block = (struct f2fs_nat_block *)calloc(BLOCK_SZ, 1); + + block_off = nid / NAT_ENTRY_PER_BLOCK; + entry_off = nid % NAT_ENTRY_PER_BLOCK; + + seg_off = block_off >> sbi->log_blocks_per_seg; + block_addr = (pgoff_t)(nm_i->nat_blkaddr + + (seg_off << sbi->log_blocks_per_seg << 1) + + (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); + + if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) + block_addr += sbi->blocks_per_seg; + + ret = dev_read_block(nat_block, block_addr); + ASSERT(ret >= 0); + + memcpy(&nat_block->entries[entry_off], &nat_in_journal(sum, i), + sizeof(struct f2fs_nat_entry)); + + ret = dev_write_block(nat_block, block_addr); + ASSERT(ret >= 0); + free(nat_block); + i++; + goto next; +} + +void flush_journal_entries(struct f2fs_sb_info *sbi) +{ + flush_nat_journal_entries(sbi); + flush_sit_journal_entries(sbi); + write_checkpoint(sbi); +} + +void flush_sit_entries(struct f2fs_sb_info *sbi) +{ + struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); + struct sit_info *sit_i = SIT_I(sbi); + unsigned int segno = 0; + u32 free_segs = 0; + + /* update free segments */ + for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) { + struct f2fs_sit_block *sit_blk; + struct f2fs_sit_entry *sit; + struct seg_entry *se; + + se = get_seg_entry(sbi, segno); + + if (!se->dirty) + continue; + + sit_blk = get_current_sit_page(sbi, segno); + sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno)]; + memcpy(sit->valid_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE); + sit->vblocks = cpu_to_le16((se->type << SIT_VBLOCKS_SHIFT) | + se->valid_blocks); + rewrite_current_sit_page(sbi, segno, sit_blk); + free(sit_blk); + + if (se->valid_blocks == 0x0 && + !IS_CUR_SEGNO(sbi, segno, NO_CHECK_TYPE)) + free_segs++; + } + + set_cp(free_segment_count, free_segs); +} + int find_next_free_block(struct f2fs_sb_info *sbi, u64 *to, int left, int type) { struct seg_entry *se; @@ -1293,6 +1519,70 @@ void nullify_nat_entry(struct f2fs_sb_info *sbi, u32 nid) free(nat_block); } +void write_checkpoint(struct f2fs_sb_info *sbi) +{ + struct f2fs_checkpoint *cp = F2FS_CKPT(sbi); + struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi); + block_t orphan_blks = 0; + u32 free_segs = 0; + unsigned long long cp_blk_no; + u32 flags = CP_UMOUNT_FLAG; + unsigned int segno; + int i, ret; + u_int32_t crc = 0; + + if (is_set_ckpt_flags(cp, CP_ORPHAN_PRESENT_FLAG)) { + orphan_blks = __start_sum_addr(sbi) - 1; + flags |= CP_ORPHAN_PRESENT_FLAG; + } + + set_cp(ckpt_flags, flags); + + for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) { + struct seg_entry *se = get_seg_entry(sbi, segno); + + if (se->valid_blocks == 0x0 && + !IS_CUR_SEGNO(sbi, segno, NO_CHECK_TYPE)) + free_segs++; + } + set_cp(free_segment_count, free_segs); + set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_sb(cp_payload)); + + crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, cp, CHECKSUM_OFFSET); + *((__le32 *)((unsigned char *)cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc); + + cp_blk_no = get_sb(cp_blkaddr); + if (sbi->cur_cp == 2) + cp_blk_no += 1 << get_sb(log_blocks_per_seg); + + /* write the first cp */ + ret = dev_write_block(cp, cp_blk_no++); + ASSERT(ret >= 0); + + /* skip payload */ + cp_blk_no += get_sb(cp_payload); + /* skip orphan blocks */ + cp_blk_no += orphan_blks; + + /* update summary blocks having nullified journal entries */ + for (i = 0; i < NO_CHECK_TYPE; i++) { + struct curseg_info *curseg = CURSEG_I(sbi, i); + u64 ssa_blk; + + ret = dev_write_block(curseg->sum_blk, cp_blk_no++); + ASSERT(ret >= 0); + + /* update original SSA too */ + ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno); + ret = dev_write_block(curseg->sum_blk, ssa_blk); + ASSERT(ret >= 0); + } + + /* write the last cp */ + ret = dev_write_block(cp, cp_blk_no++); + ASSERT(ret >= 0); +} + void build_nat_area_bitmap(struct f2fs_sb_info *sbi) { struct f2fs_fsck *fsck = F2FS_FSCK(sbi); diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h index bbdeb51..29ff9fe 100644 --- a/include/f2fs_fs.h +++ b/include/f2fs_fs.h @@ -220,6 +220,7 @@ static inline uint64_t bswap_64(uint64_t val) enum f2fs_config_func { FSCK, DUMP, + DEFRAG, }; struct f2fs_configuration { @@ -252,6 +253,12 @@ struct f2fs_configuration { int auto_fix; int ro; __le32 feature; /* defined features */ + + /* defragmentation parameters */ + int defrag_shrink; + u_int64_t defrag_start; + u_int64_t defrag_len; + u_int64_t defrag_target; } __attribute__((packed)); #ifdef CONFIG_64BIT