linux-f2fs-devel.lists.sourceforge.net archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/3] resize.f2fs: support to expand partition size
@ 2016-04-26  0:28 Jaegeuk Kim
  2016-04-26  0:28 ` [PATCH 2/3] sload.f2fs: support loading files into partition directly Jaegeuk Kim
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Jaegeuk Kim @ 2016-04-26  0:28 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: Jaegeuk Kim

Now user can expand existing partition with resize.f2fs.
Currently, it doesn't support shrink an image.

For example,
 # resize.f2fs -t [# of sectors] [image]

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fsck/Makefile.am  |   4 +-
 fsck/f2fs.h       |   4 +-
 fsck/fsck.h       |   2 +
 fsck/main.c       |  64 ++++++
 fsck/resize.c     | 578 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/f2fs_fs.h |  17 ++
 man/Makefile.am   |   2 +-
 man/defrag.f2fs.8 |   3 +-
 man/dump.f2fs.8   |   3 +-
 man/fsck.f2fs.8   |   3 +-
 man/mkfs.f2fs.8   |   3 +-
 man/resize.f2fs.8 |  49 +++++
 12 files changed, 724 insertions(+), 8 deletions(-)
 create mode 100644 fsck/resize.c
 create mode 100644 man/resize.f2fs.8

diff --git a/fsck/Makefile.am b/fsck/Makefile.am
index 73df884..3586625 100644
--- a/fsck/Makefile.am
+++ b/fsck/Makefile.am
@@ -3,9 +3,11 @@
 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 defrag.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	\
+		resize.c
 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
+	ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/resize.f2fs
diff --git a/fsck/f2fs.h b/fsck/f2fs.h
index a618b47..4b3c666 100644
--- a/fsck/f2fs.h
+++ b/fsck/f2fs.h
@@ -323,9 +323,9 @@ static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type)
 #define segno_in_journal(jnl, i)        (jnl->sit_j.entries[i].segno)
 
 #define SIT_ENTRY_OFFSET(sit_i, segno)                                  \
-	(segno % sit_i->sents_per_block)
+	((segno) % sit_i->sents_per_block)
 #define SIT_BLOCK_OFFSET(sit_i, segno)                                  \
-	(segno / SIT_ENTRY_PER_BLOCK)
+	((segno) / SIT_ENTRY_PER_BLOCK)
 #define TOTAL_SEGS(sbi) (SM_I(sbi)->main_segments)
 
 static inline bool IS_VALID_NID(struct f2fs_sb_info *sbi, u32 nid)
diff --git a/fsck/fsck.h b/fsck/fsck.h
index 5fc214e..db11b41 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -179,4 +179,6 @@ extern int dump_info_from_blkaddr(struct f2fs_sb_info *, u32);
 /* defrag.c */
 int f2fs_defragment(struct f2fs_sb_info *, u64, u64, u64, int);
 
+/* resize.c */
+int f2fs_resize(struct f2fs_sb_info *);
 #endif /* _FSCK_H_ */
diff --git a/fsck/main.c b/fsck/main.c
index 6058c4d..885e2cf 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -52,6 +52,15 @@ void defrag_usage()
 	exit(1);
 }
 
+void resize_usage()
+{
+	MSG(0, "\nUsage: resize.f2fs [options] device\n");
+	MSG(0, "[options]:\n");
+	MSG(0, "  -d debug level [default:0]\n");
+	MSG(0, "  -t target sectors [default: device size]\n");
+	exit(1);
+}
+
 void f2fs_parse_options(int argc, char *argv[])
 {
 	int option = 0;
@@ -203,6 +212,34 @@ void f2fs_parse_options(int argc, char *argv[])
 			}
 			ASSERT(ret >= 0);
 		}
+	} else if (!strcmp("resize.f2fs", prog)) {
+		const char *option_string = "d:t:";
+
+		config.func = RESIZE;
+		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 't':
+				if (strncmp(optarg, "0x", 2))
+					ret = sscanf(optarg, "%"PRIu64"",
+							&config.target_sectors);
+				else
+					ret = sscanf(optarg, "%"PRIx64"",
+							&config.target_sectors);
+				break;
+			default:
+				MSG(0, "\tError: Unknown option %c\n", option);
+				resize_usage();
+				break;
+			}
+			ASSERT(ret >= 0);
+		}
 	}
 
 	if ((optind + 1) != argc) {
@@ -213,6 +250,8 @@ void f2fs_parse_options(int argc, char *argv[])
 			dump_usage();
 		else if (config.func == DEFRAG)
 			defrag_usage();
+		else if (config.func == RESIZE)
+			resize_usage();
 	}
 	config.device_name = argv[optind];
 }
@@ -345,6 +384,27 @@ out_range:
 	return -1;
 }
 
+static int do_resize(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+
+	if (!config.target_sectors)
+		config.target_sectors = config.total_sectors;
+
+	if (config.target_sectors > config.total_sectors) {
+		ASSERT_MSG("Out-of-range Target=0x%"PRIx64" / 0x%"PRIx64"",
+				config.target_sectors, config.total_sectors);
+		return -1;
+	}
+
+	if (config.target_sectors ==
+			(get_sb(block_count) << get_sb(log_sectors_per_block))) {
+		ASSERT_MSG("Nothing to resize; it's same");
+		return -1;
+	}
+	return f2fs_resize(sbi);
+}
+
 int main(int argc, char **argv)
 {
 	struct f2fs_sb_info *sbi;
@@ -395,6 +455,10 @@ fsck_again:
 		if (ret)
 			goto out_err;
 		break;
+	case RESIZE:
+		if (do_resize(sbi))
+			goto out_err;
+		break;
 	}
 
 	f2fs_do_umount(sbi);
diff --git a/fsck/resize.c b/fsck/resize.c
new file mode 100644
index 0000000..0803024
--- /dev/null
+++ b/fsck/resize.c
@@ -0,0 +1,578 @@
+/**
+ * resize.c
+ *
+ * Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * 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 get_new_sb(struct f2fs_sb_info *sbi, struct f2fs_super_block *sb)
+{
+	u_int32_t zone_size_bytes, zone_align_start_offset;
+	u_int32_t blocks_for_sit, blocks_for_nat, blocks_for_ssa;
+	u_int32_t sit_segments, diff, total_meta_segments;
+	u_int32_t total_valid_blks_available;
+	u_int32_t sit_bitmap_size, max_sit_bitmap_size;
+	u_int32_t max_nat_bitmap_size, max_nat_segments;
+	u_int32_t segment_size_bytes = 1 << (get_sb(log_blocksize) +
+					get_sb(log_blocks_per_seg));
+	u_int32_t blks_per_seg = 1 << get_sb(log_blocks_per_seg);
+	u_int32_t segs_per_zone = get_sb(segs_per_sec) * get_sb(secs_per_zone);
+
+	set_sb(block_count, config.target_sectors >>
+				get_sb(log_sectors_per_block));
+
+	zone_size_bytes = segment_size_bytes * segs_per_zone;
+	zone_align_start_offset =
+		(config.start_sector * config.sector_size +
+		2 * F2FS_BLKSIZE + zone_size_bytes - 1) /
+		zone_size_bytes * zone_size_bytes -
+		config.start_sector * config.sector_size;
+
+	set_sb(segment_count, (config.target_sectors * config.sector_size -
+				zone_align_start_offset) / segment_size_bytes /
+				config.segs_per_sec * config.segs_per_sec);
+
+	blocks_for_sit = ALIGN(get_sb(segment_count), SIT_ENTRY_PER_BLOCK);
+	sit_segments = SEG_ALIGN(blocks_for_sit);
+	set_sb(segment_count_sit, sit_segments * 2);
+	set_sb(nat_blkaddr, get_sb(sit_blkaddr) +
+				get_sb(segment_count_sit) * blks_per_seg);
+
+	total_valid_blks_available = (get_sb(segment_count) -
+			(get_sb(segment_count_ckpt) +
+			get_sb(segment_count_sit))) * blks_per_seg;
+	blocks_for_nat = ALIGN(total_valid_blks_available, NAT_ENTRY_PER_BLOCK);
+	set_sb(segment_count_nat, SEG_ALIGN(blocks_for_nat));
+
+	sit_bitmap_size = ((get_sb(segment_count_sit) / 2) <<
+				get_sb(log_blocks_per_seg)) / 8;
+	if (sit_bitmap_size > MAX_SIT_BITMAP_SIZE)
+		max_sit_bitmap_size = MAX_SIT_BITMAP_SIZE;
+	else
+		max_sit_bitmap_size = sit_bitmap_size;
+
+	/*
+	 * It should be reserved minimum 1 segment for nat.
+	 * When sit is too large, we should expand cp area. It requires more pages for cp.
+	 */
+	if (max_sit_bitmap_size >
+			(CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 65)) {
+		max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1;
+		set_sb(cp_payload, F2FS_BLK_ALIGN(max_sit_bitmap_size));
+	} else {
+		max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1
+			- max_sit_bitmap_size;
+		set_sb(cp_payload, 0);
+	}
+
+	max_nat_segments = (max_nat_bitmap_size * 8) >>
+					get_sb(log_blocks_per_seg);
+
+	if (get_sb(segment_count_nat) > max_nat_segments)
+		set_sb(segment_count_nat, max_nat_segments);
+
+	set_sb(segment_count_nat, get_sb(segment_count_nat) * 2);
+
+	set_sb(ssa_blkaddr, get_sb(nat_blkaddr) +
+				get_sb(segment_count_nat) * blks_per_seg);
+
+	total_valid_blks_available = (get_sb(segment_count) -
+			(get_sb(segment_count_ckpt) +
+			get_sb(segment_count_sit) +
+			get_sb(segment_count_nat))) * blks_per_seg;
+
+	blocks_for_ssa = total_valid_blks_available / blks_per_seg + 1;
+
+	set_sb(segment_count_ssa, SEG_ALIGN(blocks_for_ssa));
+
+	total_meta_segments = get_sb(segment_count_ckpt) +
+		get_sb(segment_count_sit) +
+		get_sb(segment_count_nat) +
+		get_sb(segment_count_ssa);
+
+	diff = total_meta_segments % segs_per_zone;
+	if (diff)
+		set_sb(segment_count_ssa, get_sb(segment_count_ssa) +
+			(segs_per_zone - diff));
+
+	set_sb(main_blkaddr, get_sb(ssa_blkaddr) + get_sb(segment_count_ssa) *
+			 blks_per_seg);
+
+	set_sb(segment_count_main, get_sb(segment_count) -
+			(get_sb(segment_count_ckpt) +
+			 get_sb(segment_count_sit) +
+			 get_sb(segment_count_nat) +
+			 get_sb(segment_count_ssa)));
+
+	set_sb(section_count, get_sb(segment_count_main) /
+						get_sb(segs_per_sec));
+
+	set_sb(segment_count_main, get_sb(section_count) *
+						get_sb(segs_per_sec));
+
+	/* Let's determine the best reserved and overprovisioned space */
+	config.new_overprovision = get_best_overprovision(sb);
+	config.new_reserved_segments =
+		(2 * (100 / config.new_overprovision + 1) + 6) *
+						get_sb(segs_per_sec);
+
+	if ((get_sb(segment_count_main) - 2) < config.new_reserved_segments ||
+		get_sb(segment_count_main) * blks_per_seg >
+						get_sb(block_count)) {
+		MSG(0, "\tError: Device size is not sufficient for F2FS volume,\
+			more segment needed =%u",
+			config.new_reserved_segments -
+			(get_sb(segment_count_main) - 2));
+		return -1;
+	}
+	return 0;
+}
+
+static void migrate_main(struct f2fs_sb_info *sbi,
+		struct f2fs_super_block *new_sb, unsigned int offset)
+{
+	void *raw = calloc(BLOCK_SZ, 1);
+	struct seg_entry *se;
+	block_t from, to;
+	int i, j, ret;
+	struct f2fs_summary sum;
+
+	ASSERT(raw != NULL);
+
+	for (i = TOTAL_SEGS(sbi); i >= 0; i--) {
+		se = get_seg_entry(sbi, i);
+		if (!se->valid_blocks)
+			continue;
+
+		for (j = sbi->blocks_per_seg - 1; j >= 0; j--) {
+			if (!f2fs_test_bit(j, (const char *)se->cur_valid_map))
+				continue;
+
+			from = START_BLOCK(sbi, i) + j;
+			ret = dev_read_block(raw, from);
+			ASSERT(ret >= 0);
+
+			to = from + offset;
+			ret = dev_write_block(raw, to);
+			ASSERT(ret >= 0);
+
+			get_sum_entry(sbi, from, &sum);
+
+			if (IS_DATASEG(se->type))
+				update_data_blkaddr(sbi, le32_to_cpu(sum.nid),
+					le16_to_cpu(sum.ofs_in_node), to);
+			else
+				update_nat_blkaddr(sbi, 0,
+						le32_to_cpu(sum.nid), to);
+		}
+	}
+	free(raw);
+	DBG(0, "Info: Done to migrate data and node blocks\n");
+}
+
+static void move_ssa(struct f2fs_sb_info *sbi, unsigned int segno,
+					block_t new_sum_blk_addr)
+{
+	struct f2fs_summary_block *sum_blk;
+	int type;
+
+	sum_blk = get_sum_block(sbi, segno, &type);
+	if (type < SEG_TYPE_MAX) {
+		int ret;
+
+		ret = dev_write_block(sum_blk, new_sum_blk_addr);
+		ASSERT(ret >= 0);
+		DBG(1, "Write summary block: (%d) segno=%x/%x --> (%d) %x\n",
+				type, segno, GET_SUM_BLKADDR(sbi, segno),
+				IS_SUM_NODE_SEG(sum_blk->footer),
+				new_sum_blk_addr);
+	}
+	if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
+			type == SEG_TYPE_MAX) {
+		free(sum_blk);
+	}
+	DBG(1, "Info: Done to migrate SSA blocks\n");
+}
+
+static void migrate_ssa(struct f2fs_sb_info *sbi,
+		struct f2fs_super_block *new_sb, unsigned int offset)
+{
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	block_t old_sum_blkaddr = get_sb(ssa_blkaddr);
+	block_t new_sum_blkaddr = get_newsb(ssa_blkaddr);
+	int segno;
+
+	if (new_sum_blkaddr < old_sum_blkaddr + offset) {
+		for (segno = offset; segno < TOTAL_SEGS(sbi); segno++)
+			move_ssa(sbi, segno, new_sum_blkaddr + segno - offset);
+	} else {
+		for (segno = TOTAL_SEGS(sbi) - 1; segno >= offset; segno--)
+			move_ssa(sbi, segno, new_sum_blkaddr + segno - offset);
+	}
+
+	DBG(0, "Info: Done to migrate SSA blocks\n");
+}
+
+static int shrink_nats(struct f2fs_sb_info *sbi,
+				struct f2fs_super_block *new_sb)
+{
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	struct f2fs_nm_info *nm_i = NM_I(sbi);
+	block_t old_nat_blkaddr = get_sb(nat_blkaddr);
+	unsigned int nat_blocks;
+	void *nat_block, *zero_block;
+	int nid, ret, new_max_nid;
+	pgoff_t block_off;
+	pgoff_t block_addr;
+	int seg_off;
+
+	nat_block = malloc(BLOCK_SZ);
+	ASSERT(nat_block);
+	zero_block = calloc(BLOCK_SZ, 1);
+	ASSERT(zero_block);
+
+	nat_blocks = get_newsb(segment_count_nat) >> 1;
+	nat_blocks = nat_blocks << get_sb(log_blocks_per_seg);
+	new_max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks;
+
+	for (nid = nm_i->max_nid - 1; nid > new_max_nid; nid -= NAT_ENTRY_PER_BLOCK) {
+		block_off = nid / NAT_ENTRY_PER_BLOCK;
+		seg_off = block_off >> sbi->log_blocks_per_seg;
+		block_addr = (pgoff_t)(old_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);
+
+		if (memcmp(zero_block, nat_block, BLOCK_SZ)) {
+			ret = -1;
+			goto not_avail;
+		}
+	}
+	ret = 0;
+	nm_i->max_nid = new_max_nid;
+not_avail:
+	free(nat_block);
+	free(zero_block);
+	return ret;
+}
+
+static void migrate_nat(struct f2fs_sb_info *sbi,
+			struct f2fs_super_block *new_sb)
+{
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	struct f2fs_nm_info *nm_i = NM_I(sbi);
+	block_t old_nat_blkaddr = get_sb(nat_blkaddr);
+	block_t new_nat_blkaddr = get_newsb(nat_blkaddr);
+	unsigned int nat_blocks;
+	void *nat_block;
+	int nid, ret, new_max_nid;
+	pgoff_t block_off;
+	pgoff_t block_addr;
+	int seg_off;
+
+	nat_block = malloc(BLOCK_SZ);
+	ASSERT(nat_block);
+
+	for (nid = nm_i->max_nid - 1; nid >= 0; nid -= NAT_ENTRY_PER_BLOCK) {
+		block_off = nid / NAT_ENTRY_PER_BLOCK;
+		seg_off = block_off >> sbi->log_blocks_per_seg;
+		block_addr = (pgoff_t)(old_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);
+
+		block_addr = (pgoff_t)(new_nat_blkaddr +
+				(seg_off << sbi->log_blocks_per_seg << 1) +
+				(block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
+
+		/* new bitmap should be zeros */
+		ret = dev_write_block(nat_block, block_addr);
+		ASSERT(ret >= 0);
+	}
+	/* zero out newly assigned nids */
+	memset(nat_block, 0, BLOCK_SZ);
+	nat_blocks = get_newsb(segment_count_nat) >> 1;
+	nat_blocks = nat_blocks << get_sb(log_blocks_per_seg);
+	new_max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks;
+
+	DBG(1, "Write NAT block: %x->%x, max_nid=%x->%x\n",
+			old_nat_blkaddr, new_nat_blkaddr,
+			get_sb(segment_count_nat),
+			get_newsb(segment_count_nat));
+
+	for (nid = nm_i->max_nid; nid < new_max_nid;
+				nid += NAT_ENTRY_PER_BLOCK) {
+		block_off = nid / NAT_ENTRY_PER_BLOCK;
+		seg_off = block_off >> sbi->log_blocks_per_seg;
+		block_addr = (pgoff_t)(new_nat_blkaddr +
+				(seg_off << sbi->log_blocks_per_seg << 1) +
+				(block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
+		ret = dev_write_block(nat_block, block_addr);
+		ASSERT(ret >= 0);
+		DBG(1, "Write NAT: %lx\n", block_addr);
+	}
+	DBG(0, "Info: Done to migrate NAT blocks\n");
+}
+
+static void migrate_sit(struct f2fs_sb_info *sbi,
+		struct f2fs_super_block *new_sb, unsigned int offset)
+{
+	struct sit_info *sit_i = SIT_I(sbi);
+	unsigned int ofs = 0, pre_ofs = 0;
+	unsigned int segno, index;
+	struct f2fs_sit_block *sit_blk = calloc(BLOCK_SZ, 1);
+	block_t sit_blks = get_newsb(segment_count_sit) <<
+						(sbi->log_blocks_per_seg - 1);
+	struct seg_entry *se;
+	block_t blk_addr = 0;
+	int ret;
+
+	ASSERT(sit_blk);
+
+	/* initialize with zeros */
+	for (index = 0; index < sit_blks; index++) {
+		ret = dev_write_block(sit_blk, get_newsb(sit_blkaddr) + index);
+		ASSERT(ret >= 0);
+		DBG(1, "Write zero sit: %x\n", get_newsb(sit_blkaddr) + index);
+	}
+
+	for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) {
+		struct f2fs_sit_entry *sit;
+
+		se = get_seg_entry(sbi, segno);
+		if (segno < offset) {
+			ASSERT(se->valid_blocks == 0);
+			continue;
+		}
+
+		ofs = SIT_BLOCK_OFFSET(sit_i, segno - offset);
+
+		if (ofs != pre_ofs) {
+			blk_addr = get_newsb(sit_blkaddr) + pre_ofs;
+			ret = dev_write_block(sit_blk, blk_addr);
+			ASSERT(ret >= 0);
+			DBG(1, "Write valid sit: %x\n", blk_addr);
+
+			pre_ofs = ofs;
+			memset(sit_blk, 0, BLOCK_SZ);
+		}
+
+		sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno - offset)];
+		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);
+	}
+	blk_addr = get_newsb(sit_blkaddr) + ofs;
+	ret = dev_write_block(sit_blk, blk_addr);
+	DBG(1, "Write valid sit: %x\n", blk_addr);
+	ASSERT(ret >= 0);
+
+	free(sit_blk);
+	DBG(0, "Info: Done to migrate SIT blocks\n");
+}
+
+static void rebuild_checkpoint(struct f2fs_sb_info *sbi,
+			struct f2fs_super_block *new_sb, unsigned int offset)
+{
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+	struct f2fs_checkpoint *new_cp;
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	unsigned int free_segment_count, new_segment_count;
+	block_t new_cp_blks = 1 + get_newsb(cp_payload);
+	block_t orphan_blks = 0;
+	block_t new_cp_blk_no, old_cp_blk_no;
+	u_int32_t crc = 0;
+	void *buf;
+	int i, ret;
+
+	new_cp = calloc(new_cp_blks * BLOCK_SZ, 1);
+	ASSERT(new_cp);
+
+	buf = malloc(BLOCK_SZ);
+	ASSERT(buf);
+
+	/* ovp / free segments */
+	set_cp(overprov_segment_count, config.new_overprovision);
+	set_cp(rsvd_segment_count, config.new_reserved_segments);
+	free_segment_count = get_cp(free_segment_count);
+	new_segment_count = get_newsb(segment_count_main) -
+					get_sb(segment_count_main);
+
+	set_cp(free_segment_count, free_segment_count + new_segment_count);
+	set_cp(user_block_count, ((get_sb(segment_count_main) -
+			get_cp(overprov_segment_count)) * config.blks_per_seg));
+
+	if (is_set_ckpt_flags(cp, CP_ORPHAN_PRESENT_FLAG))
+		orphan_blks = __start_sum_addr(sbi) - 1;
+
+	set_cp(cp_pack_start_sum, 1 + get_newsb(cp_payload));
+	set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_newsb(cp_payload));
+
+	/* cur->segno - offset */
+	for (i = 0; i < NO_CHECK_TYPE; i++) {
+		if (i < CURSEG_HOT_NODE) {
+			set_cp(cur_data_segno[i],
+					CURSEG_I(sbi, i)->segno - offset);
+		} else {
+			int n = i - CURSEG_HOT_NODE;
+
+			set_cp(cur_node_segno[n],
+					CURSEG_I(sbi, i)->segno - offset);
+		}
+	}
+
+	/* sit / nat ver bitmap bytesize */
+	set_cp(sit_ver_bitmap_bytesize,
+			((get_newsb(segment_count_sit) / 2) <<
+			get_newsb(log_blocks_per_seg)) / 8);
+	set_cp(nat_ver_bitmap_bytesize,
+			((get_newsb(segment_count_nat) / 2) <<
+			get_newsb(log_blocks_per_seg)) / 8);
+
+	memcpy(new_cp, cp, (unsigned char *)cp->sit_nat_version_bitmap -
+						(unsigned char *)cp);
+
+	crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, new_cp, CHECKSUM_OFFSET);
+	*((__le32 *)((unsigned char *)new_cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc);
+
+	/* Write a new checkpoint in the other set */
+	new_cp_blk_no = old_cp_blk_no = get_sb(cp_blkaddr);
+	if (sbi->cur_cp == 2)
+		old_cp_blk_no += 1 << get_sb(log_blocks_per_seg);
+	else
+		new_cp_blk_no += 1 << get_sb(log_blocks_per_seg);
+
+	/* write first cp */
+	ret = dev_write_block(new_cp, new_cp_blk_no++);
+	ASSERT(ret >= 0);
+
+	memset(buf, 0, BLOCK_SZ);
+	for (i = 0; i < get_newsb(cp_payload); i++) {
+		ret = dev_write_block(buf, new_cp_blk_no++);
+		ASSERT(ret >= 0);
+	}
+
+	for (i = 0; i < orphan_blks; i++) {
+		block_t orphan_blk_no = old_cp_blk_no + 1 + get_sb(cp_payload);
+
+		ret = dev_read_block(buf, orphan_blk_no++);
+		ASSERT(ret >= 0);
+
+		ret = dev_write_block(buf, new_cp_blk_no++);
+		ASSERT(ret >= 0);
+	}
+
+	/* update summary blocks having nullified journal entries */
+	for (i = 0; i < NO_CHECK_TYPE; i++) {
+		struct curseg_info *curseg = CURSEG_I(sbi, i);
+
+		ret = dev_write_block(curseg->sum_blk, new_cp_blk_no++);
+		ASSERT(ret >= 0);
+	}
+
+	/* write the last cp */
+	ret = dev_write_block(new_cp, new_cp_blk_no++);
+	ASSERT(ret >= 0);
+
+	/* disable old checkpoint */
+	memset(buf, 0, BLOCK_SZ);
+	ret = dev_write_block(buf, old_cp_blk_no);
+	ASSERT(ret >= 0);
+
+	free(buf);
+	free(new_cp);
+	DBG(0, "Info: Done to rebuild checkpoint blocks\n");
+}
+
+static void rebuild_superblock(struct f2fs_sb_info *sbi,
+				struct f2fs_super_block *new_sb)
+{
+	int index, ret;
+	u_int8_t *buf;
+
+	buf = calloc(BLOCK_SZ, 1);
+
+	memcpy(buf + F2FS_SUPER_OFFSET, new_sb, sizeof(*new_sb));
+	for (index = 0; index < 2; index++) {
+		ret = dev_write_block(buf, index);
+		ASSERT(ret >= 0);
+	}
+	free(buf);
+	DBG(0, "Info: Done to rebuild superblock\n");
+}
+
+int f2fs_resize(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+	struct f2fs_super_block new_sb_raw;
+	struct f2fs_super_block *new_sb = &new_sb_raw;
+	block_t end_blkaddr, old_main_blkaddr, new_main_blkaddr;
+	unsigned int offset, offset_seg;
+	int err = -1;
+
+	/* flush NAT/SIT journal entries */
+	flush_journal_entries(sbi);
+
+	memcpy(new_sb, F2FS_RAW_SUPER(sbi), sizeof(*new_sb));
+	if (get_new_sb(sbi, new_sb))
+		return -1;
+
+	/* check nat availability */
+	if (get_sb(segment_count_nat) > get_newsb(segment_count_nat)) {
+		err = shrink_nats(sbi, new_sb);
+		if (err) {
+			MSG(0, "\tError: Failed to shrink NATs\n");
+			return err;
+		}
+	}
+
+	config.dbg_lv = 1;
+	print_raw_sb_info(sb);
+	print_raw_sb_info(new_sb);
+	config.dbg_lv = 0;
+
+	old_main_blkaddr = get_sb(main_blkaddr);
+	new_main_blkaddr = get_newsb(main_blkaddr);
+	offset = new_main_blkaddr - old_main_blkaddr;
+	end_blkaddr = (get_sb(segment_count) << get_sb(log_blocks_per_seg)) +
+						get_sb(main_blkaddr);
+
+	if (old_main_blkaddr > new_main_blkaddr) {
+		MSG(0, "\tError: Support resize to expand only\n");
+		return -1;
+	}
+
+	err = -EAGAIN;
+	offset_seg = offset >> get_sb(log_blocks_per_seg);
+
+	if (new_main_blkaddr < end_blkaddr) {
+		err = f2fs_defragment(sbi, old_main_blkaddr, offset,
+						new_main_blkaddr, 0);
+		if (err)
+			MSG(0, "Skip defragement\n");
+	}
+	/* move whole data region */
+	if (err)
+		migrate_main(sbi, new_sb, offset);
+
+	migrate_ssa(sbi, new_sb, offset_seg);
+	migrate_nat(sbi, new_sb);
+	migrate_sit(sbi, new_sb, offset_seg);
+	rebuild_checkpoint(sbi, new_sb, offset_seg);
+	rebuild_superblock(sbi, new_sb);
+	return 0;
+}
diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
index 330cbe5..99798d0 100644
--- a/include/f2fs_fs.h
+++ b/include/f2fs_fs.h
@@ -221,18 +221,22 @@ enum f2fs_config_func {
 	FSCK,
 	DUMP,
 	DEFRAG,
+	RESIZE,
 };
 
 struct f2fs_configuration {
 	u_int32_t sector_size;
 	u_int32_t reserved_segments;
+	u_int32_t new_reserved_segments;
 	double overprovision;
+	double new_overprovision;
 	u_int32_t cur_seg[6];
 	u_int32_t segs_per_sec;
 	u_int32_t secs_per_zone;
 	u_int32_t segs_per_zone;
 	u_int32_t start_sector;
 	u_int64_t total_sectors;
+	u_int64_t target_sectors;
 	u_int32_t sectors_per_blk;
 	u_int32_t blks_per_seg;
 	__u8 init_version[VERSION_LEN + 1];
@@ -277,6 +281,9 @@ struct f2fs_configuration {
 #define get_sb_le64(member)			le64_to_cpu(sb->member)
 #define get_sb_le32(member)			le32_to_cpu(sb->member)
 #define get_sb_le16(member)			le16_to_cpu(sb->member)
+#define get_newsb_le64(member)			le64_to_cpu(new_sb->member)
+#define get_newsb_le32(member)			le32_to_cpu(new_sb->member)
+#define get_newsb_le16(member)			le16_to_cpu(new_sb->member)
 
 #define set_sb(member, val)	\
 			do {						\
@@ -298,6 +305,16 @@ struct f2fs_configuration {
 				} 					\
 				t; \
 			})
+#define get_newsb(member)		\
+			({						\
+				typeof(new_sb->member) t;		\
+				switch (sizeof(t)) {			\
+				case 8: t = get_newsb_le64(member); break; \
+				case 4: t = get_newsb_le32(member); break; \
+				case 2: t = get_newsb_le16(member); break; \
+				} 					\
+				t; \
+			})
 
 #define set_cp_le64(member, val)		(cp->member = cpu_to_le64(val))
 #define set_cp_le32(member, val)		(cp->member = cpu_to_le32(val))
diff --git a/man/Makefile.am b/man/Makefile.am
index 256f3c3..6c04de7 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -1,3 +1,3 @@
 ## Makefile.am
 
-dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8
+dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8 resize.f2fs.8
diff --git a/man/defrag.f2fs.8 b/man/defrag.f2fs.8
index e18e69f..8c709a7 100644
--- a/man/defrag.f2fs.8
+++ b/man/defrag.f2fs.8
@@ -71,4 +71,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
 .SH SEE ALSO
 .BR mkfs.f2fs(8),
 .BR dump.f2fs(8),
-.BR fsck.f2fs(8).
+.BR fsck.f2fs(8),
+.BR resize.f2fs(8).
diff --git a/man/dump.f2fs.8 b/man/dump.f2fs.8
index ca5f511..dc1d806 100644
--- a/man/dump.f2fs.8
+++ b/man/dump.f2fs.8
@@ -65,4 +65,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
 .SH SEE ALSO
 .BR mkfs.f2fs(8),
 .BR fsck.f2fs(8),
-.BR defrag.f2fs(8).
+.BR defrag.f2fs(8),
+.BR resize.f2fs(8).
diff --git a/man/fsck.f2fs.8 b/man/fsck.f2fs.8
index 0ffe6e1..22457c5 100644
--- a/man/fsck.f2fs.8
+++ b/man/fsck.f2fs.8
@@ -64,4 +64,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
 .SH SEE ALSO
 .BR mkfs.f2fs(8),
 .BR dump.f2fs(8),
-.BR defrag.f2fs(8).
+.BR defrag.f2fs(8),
+.BR resize.f2fs(8).
diff --git a/man/mkfs.f2fs.8 b/man/mkfs.f2fs.8
index cdbde96..b767c0c 100644
--- a/man/mkfs.f2fs.8
+++ b/man/mkfs.f2fs.8
@@ -94,4 +94,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
 .BR mkfs (8),
 .BR fsck.f2fs(8),
 .BR dump.f2fs(8),
-.BR defrag.f2fs(8).
+.BR defrag.f2fs(8),
+.BR resize.f2fs(8).
diff --git a/man/resize.f2fs.8 b/man/resize.f2fs.8
new file mode 100644
index 0000000..1920810
--- /dev/null
+++ b/man/resize.f2fs.8
@@ -0,0 +1,49 @@
+.\" Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
+.\"
+.TH RESIZE.F2FS 8
+.SH NAME
+resize.f2fs \- resize filesystem size
+.SH SYNOPSIS
+.B resize.f2fs
+[
+.B \-t
+.I target sectors
+]
+[
+.B \-d
+.I debugging-level
+]
+.I device
+.SH DESCRIPTION
+.B resize.f2fs
+is used to resize an f2fs file system (usually in a disk partition).
+\fIdevice\fP is the special file corresponding to the device (e.g.
+\fI/dev/sdXX\fP).
+
+Current version only supports expanding the prebuilt filesystem.
+
+.PP
+The exit code returned by
+.B resize.f2fs
+is 0 on success and -1 on failure.
+.SH OPTIONS
+.TP
+.BI \-t " target sectors"
+Specify the size in sectors.
+.TP
+.BI \-d " debug-level"
+Specify the level of debugging options.
+The default number is 0, which shows basic debugging messages.
+.TP
+.SH AUTHOR
+This version of
+.B resize.f2fs
+has been written by Jaegeuk Kim <jaegeuk@kernel.org>.
+.SH AVAILABILITY
+.B resize.f2fs
+is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
+.SH SEE ALSO
+.BR mkfs.f2fs(8),
+.BR fsck.f2fs(8),
+.BR dump.f2fs(8),
+.BR defrag.f2fs(8).
-- 
2.6.3


------------------------------------------------------------------------------
Find and fix application performance issues faster with Applications Manager
Applications Manager provides deep performance insights into multiple tiers of
your business applications. It resolves application problems quickly and
reduces your MTTR. Get your free trial!
https://ad.doubleclick.net/ddm/clk/302982198;130105516;z

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/3] sload.f2fs: support loading files into partition directly
  2016-04-26  0:28 [PATCH 1/3] resize.f2fs: support to expand partition size Jaegeuk Kim
@ 2016-04-26  0:28 ` Jaegeuk Kim
  2016-04-26  0:28 ` [PATCH 3/3] script: add simple test script Jaegeuk Kim
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 7+ messages in thread
From: Jaegeuk Kim @ 2016-04-26  0:28 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: Jaegeuk Kim

This patch implements loading files into the existing partition.
For example,
 # sload.f2fs -f ./ /dev/sdb1

Then, all the directories and files will be loaded into /dev/sdb1.
By default, newly files should have inline_data and inline_xattr, if possible.

Signed-off-by: Hou Pengyang <houpengyang@huawei.com>
Signed-off-by: Liu Shuoran <liushuoran@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 README            |   1 +
 configure.ac      |   1 +
 fsck/Makefile.am  |   6 +-
 fsck/defrag.c     |   2 +-
 fsck/dir.c        | 584 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 fsck/f2fs.h       |  77 ++++++-
 fsck/fsck.h       |  26 ++-
 fsck/main.c       |  56 ++++++
 fsck/mount.c      |  90 ++++++++-
 fsck/node.c       | 250 +++++++++++++++++++++++
 fsck/node.h       | 101 ++++++++++
 fsck/segment.c    | 220 ++++++++++++++++++++
 fsck/sload.c      | 247 +++++++++++++++++++++++
 fsck/xattr.c      | 241 ++++++++++++++++++++++
 fsck/xattr.h      |  66 ++++++
 include/f2fs_fs.h |  18 +-
 man/Makefile.am   |   2 +-
 man/defrag.f2fs.8 |   3 +-
 man/dump.f2fs.8   |   3 +-
 man/fsck.f2fs.8   |   3 +-
 man/mkfs.f2fs.8   |   3 +-
 man/resize.f2fs.8 |   3 +-
 man/sload.f2fs.8  |  56 ++++++
 23 files changed, 2042 insertions(+), 17 deletions(-)
 create mode 100644 fsck/dir.c
 create mode 100644 fsck/node.c
 create mode 100644 fsck/node.h
 create mode 100644 fsck/segment.c
 create mode 100644 fsck/sload.c
 create mode 100644 fsck/xattr.c
 create mode 100644 fsck/xattr.h
 create mode 100644 man/sload.f2fs.8

diff --git a/README b/README
index 222cbc3..77d9b49 100644
--- a/README
+++ b/README
@@ -12,6 +12,7 @@ Your should install the following packages.
  - pkg-config
  - autoconf
  - libtool
+ - libselinux1-dev
 
 Initial compilation
 -------------------
diff --git a/configure.ac b/configure.ac
index 1f6ff5a..42df775 100644
--- a/configure.ac
+++ b/configure.ac
@@ -54,6 +54,7 @@ AC_PATH_PROG([LDCONFIG], [ldconfig],
 
 # Checks for libraries.
 PKG_CHECK_MODULES([libuuid], [uuid])
+PKG_CHECK_MODULES([libselinux], [libselinux])
 
 # Checks for header files.
 AC_CHECK_HEADERS([linux/fs.h fcntl.h mntent.h stdlib.h string.h \
diff --git a/fsck/Makefile.am b/fsck/Makefile.am
index 3586625..7abcd00 100644
--- a/fsck/Makefile.am
+++ b/fsck/Makefile.am
@@ -4,10 +4,12 @@ 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 defrag.c f2fs.h fsck.h $(top_srcdir)/include/f2fs_fs.h	\
-		resize.c
-fsck_f2fs_LDADD = ${libuuid_LIBS} $(top_builddir)/lib/libf2fs.la
+		resize.c										\
+		node.c segment.c dir.c sload.c xattr.c
+fsck_f2fs_LDADD = ${libselinux_LIBS} ${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
 	ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/resize.f2fs
+	ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/sload.f2fs
diff --git a/fsck/defrag.c b/fsck/defrag.c
index 7abc0bf..079e7a7 100644
--- a/fsck/defrag.c
+++ b/fsck/defrag.c
@@ -51,7 +51,7 @@ static int migrate_block(struct f2fs_sb_info *sbi, u64 from, u64 to)
 		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);
+		update_nat_blkaddr(sbi, 0, le32_to_cpu(sum.nid), to);
 
 	DBG(0, "Migrate %s block %"PRIx64" -> %"PRIx64"\n",
 					IS_DATASEG(type) ? "data" : "node",
diff --git a/fsck/dir.c b/fsck/dir.c
new file mode 100644
index 0000000..18b79bd
--- /dev/null
+++ b/fsck/dir.c
@@ -0,0 +1,584 @@
+/**
+ * dir.c
+ *
+ * Many parts of codes are copied from Linux kernel/fs/f2fs.
+ *
+ * Copyright (C) 2015 Huawei Ltd.
+ * Witten by:
+ *   Hou Pengyang <houpengyang@huawei.com>
+ *   Liu Shuoran <liushuoran@huawei.com>
+ *   Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * 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"
+#include "node.h"
+
+static unsigned int dir_buckets(unsigned int level)
+{
+	if (level < MAX_DIR_HASH_DEPTH / 2)
+		return 1 << level;
+	else
+		return MAX_DIR_BUCKETS;
+}
+
+static unsigned int bucket_blocks(unsigned int level)
+{
+	if (level < MAX_DIR_HASH_DEPTH / 2)
+		return 2;
+	else
+		return 4;
+}
+
+static unsigned long dir_block_index(unsigned int level,
+		int dir_level, unsigned int idx)
+{
+	unsigned long i;
+	unsigned long bidx = 0;
+
+	for (i = 0; i < level; i++)
+		bidx += dir_buckets(i + dir_level) * bucket_blocks(i);
+	bidx += idx * bucket_blocks(level);
+	return bidx;
+}
+
+static int room_for_filename(const u8 *bitmap, int slots, int max_slots)
+{
+	int bit_start = 0;
+	int zero_start, zero_end;
+next:
+	zero_start = find_next_zero_bit_le(bitmap, max_slots, bit_start);
+	if (zero_start >= max_slots)
+		return max_slots;
+
+	zero_end = find_next_bit_le(bitmap, max_slots, zero_start + 1);
+
+	if (zero_end - zero_start >= slots)
+		return zero_start;
+	bit_start = zero_end;
+	goto next;
+
+}
+
+static void make_dentry_ptr(struct f2fs_dentry_ptr *d, void *src, int type)
+{
+	if (type == 1) {
+		struct f2fs_dentry_block *t = (struct f2fs_dentry_block *)src;
+		d->max = NR_DENTRY_IN_BLOCK;
+		d->bitmap = t->dentry_bitmap;
+		d->dentry = t->dentry;
+		d->filename = t->filename;
+	} else {
+		struct f2fs_inline_dentry *t = (struct f2fs_inline_dentry *)src;
+		d->max = NR_INLINE_DENTRY;
+		d->bitmap = t->dentry_bitmap;
+		d->dentry = t->dentry;
+		d->filename = t->filename;
+	}
+}
+
+static struct f2fs_dir_entry *find_target_dentry(const u8 *name,
+		unsigned int len, f2fs_hash_t namehash, int *max_slots,
+		struct f2fs_dentry_ptr *d)
+{
+	struct f2fs_dir_entry *de;
+	unsigned long bit_pos = 0;
+	int max_len = 0;
+
+	if (max_slots)
+		*max_slots = 0;
+	while (bit_pos < d->max) {
+		if (!test_bit_le(bit_pos, d->bitmap)) {
+			bit_pos++;
+			max_len++;
+			continue;
+		}
+
+		de = &d->dentry[bit_pos];
+		if (le16_to_cpu(de->name_len) == len &&
+			de->hash_code == namehash &&
+			!memcmp(d->filename[bit_pos], name, len)) {
+			goto found;
+		}
+
+		if (max_slots && max_len > *max_slots)
+			*max_slots = max_len;
+		max_len = 0;
+		bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
+	}
+	de = NULL;
+found:
+	if (max_slots && max_len > *max_slots)
+		*max_slots = max_len;
+	return de;
+}
+
+static struct f2fs_dir_entry *find_in_block(void *block,
+		const u8 *name, int len, f2fs_hash_t namehash,
+		int *max_slots)
+{
+	struct f2fs_dentry_ptr d;
+
+	make_dentry_ptr(&d, block, 1);
+	return find_target_dentry(name, len, namehash, max_slots, &d);
+}
+
+static int find_in_level(struct f2fs_sb_info *sbi,struct f2fs_node *dir,
+		unsigned int level, struct dentry *de)
+{
+	unsigned int nbucket, nblock;
+	unsigned int bidx, end_block;
+	struct f2fs_dir_entry *dentry = NULL;
+	struct dnode_of_data dn = {0};
+	void *dentry_blk;
+	int max_slots = 214;
+	nid_t ino = dir->footer.ino;
+	f2fs_hash_t namehash;
+	int ret = 0;
+
+	namehash = f2fs_dentry_hash(de->name, de->len);
+
+	nbucket = dir_buckets(level);
+	nblock = bucket_blocks(level);
+
+	bidx = dir_block_index(level, 0, le32_to_cpu(namehash) % nbucket);
+	end_block = bidx + nblock;
+
+	dentry_blk = calloc(BLOCK_SZ, 1);
+	ASSERT(dentry_blk);
+
+	for (; bidx < end_block; bidx++) {
+
+		/* Firstly, we should know direct node of target data blk */
+		if (dn.node_blk && dn.node_blk != dn.inode_blk)
+			free(dn.node_blk);
+
+		set_new_dnode(&dn, dir, NULL, ino);
+		get_dnode_of_data(sbi, &dn, bidx, LOOKUP_NODE);
+		if (dn.data_blkaddr == NULL_ADDR)
+			continue;
+
+		ret = dev_read_block(dentry_blk, dn.data_blkaddr);
+		ASSERT(ret >= 0);
+
+		dentry = find_in_block(dentry_blk, de->name, de->len,
+						namehash, &max_slots);
+		if (dentry) {
+			ret = 1;
+			de->ino = le32_to_cpu(dentry->ino);
+			break;
+		}
+	}
+
+	if (dn.node_blk && dn.node_blk != dn.inode_blk)
+		free(dn.node_blk);
+	free(dentry_blk);
+
+	return ret;
+}
+
+static int f2fs_find_entry(struct f2fs_sb_info *sbi,
+				struct f2fs_node *dir, struct dentry *de)
+{
+	unsigned int max_depth;
+	unsigned int level;
+
+	max_depth = dir->i.i_current_depth;
+	for (level = 0; level < max_depth; level ++) {
+		if (find_in_level(sbi, dir, level, de))
+			return 1;
+	}
+	return 0;
+}
+
+static void f2fs_update_dentry(nid_t ino, umode_t mode,
+		struct f2fs_dentry_ptr *d,
+		const unsigned char *name, int len, f2fs_hash_t name_hash,
+		unsigned int bit_pos)
+{
+	struct f2fs_dir_entry *de;
+	int slots = GET_DENTRY_SLOTS(len);
+	int i;
+
+	de = &d->dentry[bit_pos];
+	de->name_len = len;
+	de->hash_code = name_hash;
+	memcpy(d->filename[bit_pos], name, len);
+	d->filename[bit_pos][len] = 0;
+	de->ino = cpu_to_le32(ino);
+	set_de_type(de, mode);
+	for (i = 0; i < slots; i++)
+		test_and_set_bit_le(bit_pos + i, d->bitmap);
+}
+
+/*
+ * f2fs_add_link - Add a new file(dir) to parent dir.
+ */
+static int f2fs_add_link(struct f2fs_sb_info *sbi, struct f2fs_node *parent,
+			struct f2fs_node *child, block_t p_blkaddr)
+{
+	int level = 0, current_depth, bit_pos;
+	int nbucket, nblock, bidx, block;
+	const unsigned char *name = child->i.i_name;
+	int name_len = le32_to_cpu(child->i.i_namelen);
+	int slots = GET_DENTRY_SLOTS(name_len);
+	f2fs_hash_t dentry_hash = f2fs_dentry_hash(name, name_len);
+	struct f2fs_dentry_block *dentry_blk;
+	struct f2fs_dentry_ptr d;
+	struct dnode_of_data dn = {0};
+	nid_t pino = le32_to_cpu(parent->footer.ino);
+	nid_t ino = le32_to_cpu(child->footer.ino);
+	umode_t mode = le16_to_cpu(child->i.i_mode);
+	int ret;
+
+	if (parent == NULL || child == NULL)
+		return -EINVAL;
+
+	if (!pino) {
+		ERR_MSG("Wrong parent ino:%d \n", pino);
+		return -EINVAL;
+	}
+
+	dentry_blk = calloc(BLOCK_SZ, 1);
+	ASSERT(dentry_blk);
+
+	current_depth = le32_to_cpu(parent->i.i_current_depth);
+start:
+	if (current_depth == MAX_DIR_HASH_DEPTH) {
+		free(dentry_blk);
+		ERR_MSG("\tError: MAX_DIR_HASH\n");
+		return -ENOSPC;
+	}
+
+	/* Need a new dentry block */
+	if (level == current_depth)
+		++current_depth;
+
+	nbucket = dir_buckets(level);
+	nblock = bucket_blocks(level);
+	bidx = dir_block_index(level, 0, le32_to_cpu(dentry_hash) % nbucket);
+
+	for (block = bidx; block <= (bidx + nblock - 1); block++) {
+
+		/* Firstly, we should know the direct node of target data blk */
+		if (dn.node_blk && dn.node_blk != dn.inode_blk)
+			free(dn.node_blk);
+
+		set_new_dnode(&dn, parent, NULL, pino);
+		get_dnode_of_data(sbi, &dn, block, ALLOC_NODE);
+
+		if (dn.data_blkaddr == NULL_ADDR) {
+			new_data_block(sbi, dentry_blk, &dn, CURSEG_HOT_DATA);
+		} else {
+			ret = dev_read_block(dentry_blk, dn.data_blkaddr);
+			ASSERT(ret >= 0);
+		}
+		bit_pos = room_for_filename(dentry_blk->dentry_bitmap,
+				slots, NR_DENTRY_IN_BLOCK);
+
+		if (bit_pos < NR_DENTRY_IN_BLOCK)
+			goto add_dentry;
+	}
+	level ++;
+	goto start;
+
+add_dentry:
+	make_dentry_ptr(&d, (void *)dentry_blk, 1);
+	f2fs_update_dentry(ino, mode, &d, name, name_len, dentry_hash, bit_pos);
+
+	ret = dev_write_block(dentry_blk, dn.data_blkaddr);
+	ASSERT(ret >= 0);
+
+	/*
+	 * Parent inode needs updating, because its inode info may be changed.
+	 * such as i_current_depth and i_blocks.
+	 */
+	if (parent->i.i_current_depth != cpu_to_le32(current_depth)) {
+		parent->i.i_current_depth = cpu_to_le32(current_depth);
+		dn.idirty = 1;
+	}
+
+	/* Update parent's i_links info*/
+	if (S_ISDIR(mode)) {
+		u32 links = le32_to_cpu(parent->i.i_links);
+		parent->i.i_links = cpu_to_le32(links + 1);
+		dn.idirty = 1;
+	}
+
+	if ((block + 1) * F2FS_BLKSIZE > le64_to_cpu(parent->i.i_size)) {
+		parent->i.i_size = cpu_to_le64((block + 1) * F2FS_BLKSIZE);
+		dn.idirty = 1;
+	}
+
+	if (dn.ndirty) {
+		ret = dev_write_block(dn.node_blk, dn.node_blkaddr);
+		ASSERT(ret >= 0);
+	}
+
+	if (dn.idirty) {
+		ASSERT(parent == dn.inode_blk);
+		ret = dev_write_block(dn.inode_blk, p_blkaddr);
+		ASSERT(ret >= 0);
+	}
+
+	if (dn.node_blk != dn.inode_blk)
+		free(dn.node_blk);
+	free(dentry_blk);
+	return 0;
+}
+
+static void make_empty_dir(struct f2fs_sb_info *sbi, struct f2fs_node *inode)
+{
+	struct f2fs_dentry_block *dent_blk;
+	nid_t ino = le32_to_cpu(inode->footer.ino);
+	nid_t pino = le32_to_cpu(inode->i.i_pino);
+	struct f2fs_summary sum;
+	struct node_info ni;
+	block_t blkaddr;
+	int ret;
+
+	get_node_info(sbi, ino, &ni);
+
+	dent_blk = calloc(BLOCK_SZ, 1);
+	ASSERT(dent_blk);
+
+	dent_blk->dentry[0].hash_code = 0;
+	dent_blk->dentry[0].ino = cpu_to_le32(ino);
+	dent_blk->dentry[0].name_len = cpu_to_le16(1);
+	dent_blk->dentry[0].file_type = F2FS_FT_DIR;
+	memcpy(dent_blk->filename[0], ".", 1);
+
+	dent_blk->dentry[1].hash_code = 0;
+	dent_blk->dentry[1].ino = cpu_to_le32(pino);
+	dent_blk->dentry[1].name_len = cpu_to_le16(2);
+	dent_blk->dentry[1].file_type = F2FS_FT_DIR;
+	memcpy(dent_blk->filename[1], "..", 2);
+
+	test_and_set_bit_le(0, dent_blk->dentry_bitmap);
+	test_and_set_bit_le(1, dent_blk->dentry_bitmap);
+
+	set_summary(&sum, ino, 0, ni.version);
+	reserve_new_block(sbi, &blkaddr, &sum, CURSEG_HOT_DATA);
+
+	ret = dev_write_block(dent_blk, blkaddr);
+	ASSERT(ret >= 0);
+
+	inode->i.i_addr[0] = cpu_to_le32(blkaddr);
+	free(dent_blk);
+}
+
+static void page_symlink(struct f2fs_sb_info *sbi, struct f2fs_node *inode,
+					const char *symname, int symlen)
+{
+	nid_t ino = le32_to_cpu(inode->footer.ino);
+	struct f2fs_summary sum;
+	struct node_info ni;
+	char *data_blk;
+	block_t blkaddr;
+	int ret;
+
+	get_node_info(sbi, ino, &ni);
+
+	/* store into inline_data */
+	if (symlen + 1 <= MAX_INLINE_DATA) {
+		inode->i.i_inline |= F2FS_INLINE_DATA;
+		inode->i.i_inline |= F2FS_DATA_EXIST;
+		memcpy(&inode->i.i_addr[1], symname, symlen);
+		return;
+	}
+
+	data_blk = calloc(BLOCK_SZ, 1);
+	ASSERT(data_blk);
+
+	memcpy(data_blk, symname, symlen);
+
+	set_summary(&sum, ino, 0, ni.version);
+	reserve_new_block(sbi, &blkaddr, &sum, CURSEG_WARM_DATA);
+
+	ret = dev_write_block(data_blk, blkaddr);
+	ASSERT(ret >= 0);
+
+	inode->i.i_addr[0] = cpu_to_le32(blkaddr);
+	free(data_blk);
+}
+
+static void init_inode_block(struct f2fs_sb_info *sbi,
+		struct f2fs_node *node_blk, struct dentry *de)
+{
+	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+	mode_t mode = de->mode;
+	int links = 1;
+	unsigned int size;
+	int blocks = 1;
+
+	if (de->file_type == F2FS_FT_DIR) {
+		mode |= S_IFDIR;
+		size = 4096;
+		links++;
+		blocks++;
+	} else if (de->file_type == F2FS_FT_REG_FILE) {
+		mode |= S_IFREG;
+		size = 0;
+	} else if (de->file_type == F2FS_FT_SYMLINK) {
+		ASSERT(de->link);
+		mode |= S_IFLNK;
+		size = strlen(de->link);
+		if (size + 1 > MAX_INLINE_DATA)
+			blocks++;
+	} else {
+		ASSERT(0);
+	}
+
+	node_blk->i.i_mode = cpu_to_le16(mode);
+	node_blk->i.i_advise = 0;
+	node_blk->i.i_uid = cpu_to_le32(de->uid);
+	node_blk->i.i_gid = cpu_to_le32(de->gid);
+	node_blk->i.i_links = cpu_to_le32(links);
+	node_blk->i.i_size = cpu_to_le32(size);
+	node_blk->i.i_blocks = cpu_to_le32(blocks);
+	node_blk->i.i_atime = cpu_to_le64(de->mtime);
+	node_blk->i.i_ctime = cpu_to_le64(de->mtime);
+	node_blk->i.i_mtime = cpu_to_le64(de->mtime);
+	node_blk->i.i_atime_nsec = 0;
+	node_blk->i.i_ctime_nsec = 0;
+	node_blk->i.i_mtime_nsec = 0;
+	node_blk->i.i_generation = 0;
+	node_blk->i.i_current_depth = cpu_to_le32(1);
+	node_blk->i.i_xattr_nid = 0;
+	node_blk->i.i_flags = 0;
+	node_blk->i.i_inline = F2FS_INLINE_XATTR;
+	node_blk->i.i_pino = cpu_to_le32(de->pino);
+	node_blk->i.i_namelen = cpu_to_le32(de->len);
+	memcpy(node_blk->i.i_name, de->name, de->len);
+	node_blk->i.i_name[de->len] = 0;
+
+	node_blk->footer.ino = cpu_to_le32(de->ino);
+	node_blk->footer.nid = cpu_to_le32(de->ino);
+	node_blk->footer.flag = 0;
+	node_blk->footer.cp_ver = ckpt->checkpoint_ver;
+
+	if (S_ISDIR(mode))
+		make_empty_dir(sbi, node_blk);
+	else if (S_ISLNK(mode))
+		page_symlink(sbi, node_blk, de->link, size);
+}
+
+int f2fs_create(struct f2fs_sb_info *sbi, struct dentry *de)
+{
+	struct f2fs_node *parent, *child;
+	struct node_info ni;
+	struct f2fs_summary sum;
+	block_t blkaddr;
+	int ret;
+
+	/* Find if there is a */
+	get_node_info(sbi, de->pino, &ni);
+	if (ni.blk_addr == NULL_ADDR) {
+		MSG(0, "No parent directory pino=%x\n", de->pino);
+		return -1;
+	}
+
+	parent = calloc(BLOCK_SZ, 1);
+	ASSERT(parent);
+
+	ret = dev_read_block(parent, ni.blk_addr);
+	ASSERT(ret >= 0);
+
+	ret = f2fs_find_entry(sbi, parent, de);
+	if (ret) {
+		MSG(0, "Skip the existing \"%s\" pino=%x ERR=%d\n",
+					de->name, de->pino, ret);
+		if (de->file_type == F2FS_FT_REG_FILE)
+			de->ino = 0;
+		goto free_parent_dir;
+	}
+
+	child = calloc(BLOCK_SZ, 1);
+	ASSERT(child);
+
+	f2fs_alloc_nid(sbi, &de->ino, 1);
+
+	init_inode_block(sbi, child, de);
+
+	ret = f2fs_add_link(sbi, parent, child, ni.blk_addr);
+	if (ret) {
+		MSG(0, "Skip the existing \"%s\" pino=%x ERR=%d\n",
+					de->name, de->pino, ret);
+		goto free_child_dir;
+	}
+
+	/* write child */
+	set_summary(&sum, de->ino, 0, ni.version);
+	reserve_new_block(sbi, &blkaddr, &sum, CURSEG_HOT_NODE);
+
+	/* update nat info */
+	update_nat_blkaddr(sbi, de->ino, de->ino, blkaddr);
+
+	ret = dev_write_block(child, blkaddr);
+	ASSERT(ret >= 0);
+
+	update_free_segments(sbi);
+	MSG(1, "Info: Create \"%s\" type=%x, ino=%x / %x into \"%s\"\n",
+			de->full_path, de->file_type,
+			de->ino, de->pino, de->path);
+free_child_dir:
+	free(child);
+free_parent_dir:
+	free(parent);
+	return 0;
+}
+
+int f2fs_mkdir(struct f2fs_sb_info *sbi, struct dentry *de)
+{
+	return f2fs_create(sbi, de);
+}
+
+int f2fs_symlink(struct f2fs_sb_info *sbi, struct dentry *de)
+{
+	return f2fs_create(sbi, de);
+}
+
+int f2fs_find_path(struct f2fs_sb_info *sbi, char *path, nid_t *ino)
+{
+	struct f2fs_node *parent;
+	struct node_info ni;
+	struct dentry de;
+	int err = 0;
+	int ret;
+	char *p;
+
+	if (path[0] != '/')
+		return -ENOENT;
+
+	*ino = F2FS_ROOT_INO(sbi);
+	parent = calloc(BLOCK_SZ, 1);
+	ASSERT(parent);
+
+	p = strtok(path, "/");
+	while (p) {
+		de.name = (const u8 *)p;
+		de.len = strlen(p);
+
+		get_node_info(sbi, *ino, &ni);
+		if (ni.blk_addr == NULL_ADDR) {
+			err = -ENOENT;
+			goto err;
+		}
+		ret = dev_read_block(parent, ni.blk_addr);
+		ASSERT(ret >= 0);
+
+		ret = f2fs_find_entry(sbi, parent, &de);
+		if (!ret) {
+			err = -ENOENT;
+			goto err;
+		}
+
+		*ino = de.ino;
+		p = strtok(NULL, "/");
+	}
+err:
+	free(parent);
+	return err;
+}
diff --git a/fsck/f2fs.h b/fsck/f2fs.h
index 4b3c666..39ff161 100644
--- a/fsck/f2fs.h
+++ b/fsck/f2fs.h
@@ -60,6 +60,7 @@ struct f2fs_nm_info {
 
 	char *nat_bitmap;
 	int bitmap_size;
+	char *nid_bitmap;
 };
 
 struct seg_entry {
@@ -124,6 +125,44 @@ struct f2fs_sm_info {
 	unsigned int ovp_segments;
 };
 
+struct f2fs_dentry_ptr {
+	struct inode *inode;
+	u8 *bitmap;
+	struct f2fs_dir_entry *dentry;
+	__u8 (*filename)[F2FS_SLOT_LEN];
+	int max;
+};
+
+struct dentry {
+	char *path;
+	char *full_path;
+	const u8 *name;
+	int len;
+	char *link;
+	unsigned long size;
+	u8 file_type;
+	u16 mode;
+	u16 uid;
+	u16 gid;
+	u32 *inode;
+	u32 mtime;
+	char *secon;
+	uint64_t capabilities;
+	nid_t ino;
+	nid_t pino;
+};
+
+/* different from dnode_of_data in kernel */
+struct dnode_of_data {
+	struct f2fs_node *inode_blk;	/* inode page */
+	struct f2fs_node *node_blk;	/* cached direct node page */
+	nid_t nid;
+	unsigned int ofs_in_node;
+	block_t data_blkaddr;
+	block_t node_blkaddr;
+	int idirty, ndirty;
+};
+
 struct f2fs_sb_info {
 	struct f2fs_fsck *fsck;
 
@@ -160,6 +199,7 @@ struct f2fs_sb_info {
 	u32 s_next_generation;                  /* for NFS support */
 
 	unsigned int cur_victim_sec;            /* current victim section num */
+	u32 free_segments;
 };
 
 static inline struct f2fs_super_block *F2FS_RAW_SUPER(struct f2fs_sb_info *sbi)
@@ -313,7 +353,6 @@ static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type)
 		- (base + 1) + type;
 }
 
-
 #define nats_in_cursum(jnl)             (le16_to_cpu(jnl->n_nats))
 #define sits_in_cursum(jnl)             (le16_to_cpu(jnl->n_sits))
 
@@ -397,6 +436,42 @@ static inline void node_info_from_raw_nat(struct node_info *ni,
 	ni->version = raw_nat->version;
 }
 
+static inline void set_summary(struct f2fs_summary *sum, nid_t nid,
+			unsigned int ofs_in_node, unsigned char version)
+{
+	sum->nid = cpu_to_le32(nid);
+	sum->ofs_in_node = cpu_to_le16(ofs_in_node);
+	sum->version = version;
+}
+
+#define S_SHIFT 12
+static unsigned char f2fs_type_by_mode[S_IFMT >> S_SHIFT] = {
+	[S_IFREG >> S_SHIFT]    = F2FS_FT_REG_FILE,
+	[S_IFDIR >> S_SHIFT]    = F2FS_FT_DIR,
+	[S_IFCHR >> S_SHIFT]    = F2FS_FT_CHRDEV,
+	[S_IFBLK >> S_SHIFT]    = F2FS_FT_BLKDEV,
+	[S_IFIFO >> S_SHIFT]    = F2FS_FT_FIFO,
+	[S_IFSOCK >> S_SHIFT]   = F2FS_FT_SOCK,
+	[S_IFLNK >> S_SHIFT]    = F2FS_FT_SYMLINK,
+};
+
+static inline void set_de_type(struct f2fs_dir_entry *de, umode_t mode)
+{
+	de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT];
+}
+
+static inline void *inline_xattr_addr(struct f2fs_inode *inode)
+{
+	return (void *)&(inode->i_addr[DEF_ADDRS_PER_INODE_INLINE_XATTR]);
+}
+
+static inline int inline_xattr_size(struct f2fs_inode *inode)
+{
+	if (inode->i_inline & F2FS_INLINE_XATTR)
+		return F2FS_INLINE_XATTR_ADDRS << 2;
+	return 0;
+}
+
 extern int lookup_nat_in_journal(struct f2fs_sb_info *sbi, u32 nid, struct f2fs_nat_entry *ne);
 #define IS_SUM_NODE_SEG(footer)		(footer.entry_type == SUM_TYPE_NODE)
 #define IS_SUM_DATA_SEG(footer)		(footer.entry_type == SUM_TYPE_DATA)
diff --git a/fsck/fsck.h b/fsck/fsck.h
index db11b41..b0481d1 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -108,6 +108,8 @@ enum seg_type {
 	SEG_TYPE_MAX,
 };
 
+struct selabel_handle;
+
 extern void fsck_chk_orphan_node(struct f2fs_sb_info *);
 extern int fsck_chk_node_blk(struct f2fs_sb_info *, struct f2fs_inode *, u32,
 		u8 *, enum FILE_TYPE, enum NODE_TYPE, u32 *,
@@ -129,6 +131,7 @@ int fsck_chk_inline_dentries(struct f2fs_sb_info *, struct f2fs_node *,
 		struct child_info *);
 int fsck_chk_meta(struct f2fs_sb_info *sbi);
 
+extern void update_free_segments(struct f2fs_sb_info *);
 void print_cp_state(u32);
 extern void print_node_info(struct f2fs_node *);
 extern void print_inode_info(struct f2fs_inode *, int);
@@ -157,7 +160,7 @@ 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 update_nat_blkaddr(struct f2fs_sb_info *, nid_t, nid_t, block_t);
 
 extern void print_raw_sb_info(struct f2fs_super_block *);
 
@@ -181,4 +184,25 @@ int f2fs_defragment(struct f2fs_sb_info *, u64, u64, u64, int);
 
 /* resize.c */
 int f2fs_resize(struct f2fs_sb_info *);
+
+/* sload.c */
+int f2fs_sload(struct f2fs_sb_info *, const char *, const char *,
+		const char *, struct selabel_handle *);
+void reserve_new_block(struct f2fs_sb_info *, block_t *,
+					struct f2fs_summary *, int);
+void new_data_block(struct f2fs_sb_info *, void *,
+					struct dnode_of_data *, int);
+int f2fs_build_file(struct f2fs_sb_info *, struct dentry *);
+void f2fs_alloc_nid(struct f2fs_sb_info *, nid_t *, int);
+void set_data_blkaddr(struct dnode_of_data *);
+block_t new_node_block(struct f2fs_sb_info *,
+					struct dnode_of_data *, unsigned int);
+void get_dnode_of_data(struct f2fs_sb_info *, struct dnode_of_data *,
+					pgoff_t, int);
+int f2fs_create(struct f2fs_sb_info *, struct dentry *);
+int f2fs_mkdir(struct f2fs_sb_info *, struct dentry *);
+int f2fs_symlink(struct f2fs_sb_info *, struct dentry *);
+int inode_set_selinux(struct f2fs_sb_info *, u32, const char *);
+int f2fs_find_path(struct f2fs_sb_info *, char *, nid_t *);
+
 #endif /* _FSCK_H_ */
diff --git a/fsck/main.c b/fsck/main.c
index 885e2cf..cf670ed 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -5,6 +5,11 @@
  *             http://www.samsung.com/
  * Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
  *  : implement defrag.f2fs
+ * Copyright (C) 2015 Huawei Ltd.
+ *   Hou Pengyang <houpengyang@huawei.com>
+ *   Liu Shuoran <liushuoran@huawei.com>
+ *   Jaegeuk Kim <jaegeuk@kernel.org>
+ *  : add sload.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
@@ -61,6 +66,16 @@ void resize_usage()
 	exit(1);
 }
 
+void sload_usage()
+{
+	MSG(0, "\nUsage: sload.f2fs [options] device\n");
+	MSG(0, "[options]:\n");
+	MSG(0, "  -f source directory [path of the source directory]\n");
+	MSG(0, "  -t mount point [prefix of target fs path, default:/]\n");
+	MSG(0, "  -d debug level [default:0]\n");
+	exit(1);
+}
+
 void f2fs_parse_options(int argc, char *argv[])
 {
 	int option = 0;
@@ -240,6 +255,29 @@ void f2fs_parse_options(int argc, char *argv[])
 			}
 			ASSERT(ret >= 0);
 		}
+	} else if (!strcmp("sload.f2fs", prog)) {
+		const char *option_string = "d:f:t:";
+
+		config.func = SLOAD;
+		while ((option = getopt(argc, argv, option_string)) != EOF) {
+			switch (option) {
+			case 'd':
+				config.dbg_lv = atoi(optarg);
+				MSG(0, "Info: Debug level = %d\n",
+						config.dbg_lv);
+				break;
+			case 'f':
+				config.from_dir = (char *)optarg;
+				break;
+			case 't':
+				config.mount_point = (char *)optarg;
+				break;
+			default:
+				MSG(0, "\tError: Unknown option %c\n", option);
+				sload_usage();
+				break;
+			}
+		}
 	}
 
 	if ((optind + 1) != argc) {
@@ -252,6 +290,8 @@ void f2fs_parse_options(int argc, char *argv[])
 			defrag_usage();
 		else if (config.func == RESIZE)
 			resize_usage();
+		else if (config.func == SLOAD)
+			sload_usage();
 	}
 	config.device_name = argv[optind];
 }
@@ -405,6 +445,19 @@ static int do_resize(struct f2fs_sb_info *sbi)
 	return f2fs_resize(sbi);
 }
 
+static int do_sload(struct f2fs_sb_info *sbi)
+{
+	if (!config.from_dir) {
+		MSG(0, "\tError: Need source directory\n");
+		sload_usage();
+		return -1;
+	}
+	if (!config.mount_point)
+		config.mount_point = "/";
+
+	return f2fs_sload(sbi, config.from_dir, config.mount_point, NULL, NULL);
+}
+
 int main(int argc, char **argv)
 {
 	struct f2fs_sb_info *sbi;
@@ -459,6 +512,9 @@ fsck_again:
 		if (do_resize(sbi))
 			goto out_err;
 		break;
+	case SLOAD:
+		do_sload(sbi);
+		break;
 	}
 
 	f2fs_do_umount(sbi);
diff --git a/fsck/mount.c b/fsck/mount.c
index 67c681e..4bde179 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -25,6 +25,16 @@ static u32 get_free_segments(struct f2fs_sb_info *sbi)
 	return free_segs;
 }
 
+void update_free_segments(struct f2fs_sb_info *sbi)
+{
+	char *progress = "-*|*-";
+	static int i = 0;
+
+	MSG(0, "\r [ %c ] Free segments: 0x%x", progress[i % 5], get_free_segments(sbi));
+	fflush(stdout);
+	i++;
+}
+
 void print_inode_info(struct f2fs_inode *inode, int name)
 {
 	unsigned int i = 0;
@@ -624,6 +634,74 @@ int sanity_check_ckpt(struct f2fs_sb_info *sbi)
 	return 0;
 }
 
+static pgoff_t current_nat_addr(struct f2fs_sb_info *sbi, nid_t start)
+{
+	struct f2fs_nm_info *nm_i = NM_I(sbi);
+	pgoff_t block_off;
+	pgoff_t block_addr;
+	int seg_off;
+
+	block_off = NAT_BLOCK_OFFSET(start);
+	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;
+
+	return block_addr;
+}
+
+static int f2fs_init_nid_bitmap(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_nm_info *nm_i = NM_I(sbi);
+	int nid_bitmap_size = (nm_i->max_nid + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
+	struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
+	struct f2fs_summary_block *sum = curseg->sum_blk;
+	struct f2fs_journal *journal = &sum->journal;
+	struct f2fs_nat_block nat_block;
+	block_t start_blk;
+	nid_t nid;
+	int i;
+
+	if (!(config.func == SLOAD))
+		return 0;
+
+	nm_i->nid_bitmap = (char *)calloc(nid_bitmap_size, 1);
+	if (!nm_i->nid_bitmap)
+		return -ENOMEM;
+
+	/* arbitrarily set 0 bit */
+	f2fs_set_bit(0, nm_i->nid_bitmap);
+
+	memset((void *)&nat_block, 0, sizeof(struct f2fs_nat_block));
+
+	for (nid = 0; nid < nm_i->max_nid; nid++) {
+		if (!(nid % NAT_ENTRY_PER_BLOCK)) {
+			int ret;
+
+			start_blk = current_nat_addr(sbi, nid);
+			ret = dev_read_block((void *)&nat_block, start_blk);
+			ASSERT(ret >= 0);
+		}
+
+		if (nat_block.entries[nid % NAT_ENTRY_PER_BLOCK].block_addr)
+			f2fs_set_bit(nid, nm_i->nid_bitmap);
+	}
+
+	for (i = 0; i < nats_in_cursum(journal); i++) {
+		block_t addr;
+
+		addr = le32_to_cpu(nat_in_journal(journal, i).block_addr);
+		nid = le32_to_cpu(nid_in_journal(journal, i));
+		if (addr != NULL_ADDR)
+			f2fs_set_bit(nid, nm_i->nid_bitmap);
+	}
+	return 0;
+}
+
 int init_node_manager(struct f2fs_sb_info *sbi)
 {
 	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
@@ -654,7 +732,7 @@ int init_node_manager(struct f2fs_sb_info *sbi)
 
 	/* copy version bitmap */
 	memcpy(nm_i->nat_bitmap, version_bitmap, nm_i->bitmap_size);
-	return 0;
+	return f2fs_init_nid_bitmap(sbi);
 }
 
 int build_node_manager(struct f2fs_sb_info *sbi)
@@ -1171,7 +1249,8 @@ void update_data_blkaddr(struct f2fs_sb_info *sbi, nid_t nid,
 	free(node_blk);
 }
 
-void update_nat_blkaddr(struct f2fs_sb_info *sbi, nid_t nid, block_t newaddr)
+void update_nat_blkaddr(struct f2fs_sb_info *sbi, nid_t ino,
+					nid_t nid, block_t newaddr)
 {
 	struct f2fs_nm_info *nm_i = NM_I(sbi);
 	struct f2fs_nat_block *nat_block;
@@ -1196,6 +1275,8 @@ void update_nat_blkaddr(struct f2fs_sb_info *sbi, nid_t nid, block_t newaddr)
 	ret = dev_read_block(nat_block, block_addr);
 	ASSERT(ret >= 0);
 
+	if (ino)
+		nat_block->entries[entry_off].ino = cpu_to_le32(ino);
 	nat_block->entries[entry_off].block_addr = cpu_to_le32(newaddr);
 
 	ret = dev_write_block(nat_block, block_addr);
@@ -1567,7 +1648,7 @@ void move_curseg_info(struct f2fs_sb_info *sbi, u64 from)
 		/* update se->types */
 		reset_curseg(sbi, i);
 
-		DBG(0, "Move curseg[%d] %x -> %x after %"PRIx64"\n",
+		DBG(1, "Move curseg[%d] %x -> %x after %"PRIx64"\n",
 				i, old_segno, curseg->segno, from);
 	}
 }
@@ -1682,6 +1763,7 @@ void write_checkpoint(struct f2fs_sb_info *sbi)
 	set_cp(ckpt_flags, flags);
 
 	set_cp(free_segment_count, get_free_segments(sbi));
+	set_cp(valid_block_count, sbi->total_valid_block_count);
 	set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_sb(cp_payload));
 
 	crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, cp, CHECKSUM_OFFSET);
@@ -1915,6 +1997,8 @@ void f2fs_do_umount(struct f2fs_sb_info *sbi)
 	unsigned int i;
 
 	/* free nm_info */
+	if (config.func == SLOAD)
+		free(nm_i->nid_bitmap);
 	free(nm_i->nat_bitmap);
 	free(sbi->nm_info);
 
diff --git a/fsck/node.c b/fsck/node.c
new file mode 100644
index 0000000..c2f83b8
--- /dev/null
+++ b/fsck/node.c
@@ -0,0 +1,250 @@
+/**
+ * node.c
+ *
+ * Many parts of codes are copied from Linux kernel/fs/f2fs.
+ *
+ * Copyright (C) 2015 Huawei Ltd.
+ * Witten by:
+ *   Hou Pengyang <houpengyang@huawei.com>
+ *   Liu Shuoran <liushuoran@huawei.com>
+ *   Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * 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"
+#include "node.h"
+
+void f2fs_alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid, int inode)
+{
+	struct f2fs_nm_info *nm_i = NM_I(sbi);
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+	nid_t i, inode_cnt, node_cnt;
+
+	for (i = 0; i < nm_i->max_nid; i++)
+		if(f2fs_test_bit(i, nm_i->nid_bitmap) == 0)
+			break;
+
+	ASSERT(i < nm_i->max_nid);
+	f2fs_set_bit(i, nm_i->nid_bitmap);
+	*nid = i;
+
+	inode_cnt = get_cp(valid_inode_count);
+	node_cnt = get_cp(valid_node_count);
+	if (inode)
+		set_cp(valid_inode_count, inode_cnt + 1);
+	set_cp(valid_node_count, node_cnt + 1);
+}
+
+void set_data_blkaddr(struct dnode_of_data *dn)
+{
+	__le32 *addr_array;
+	struct f2fs_node *node_blk = dn->node_blk;
+	unsigned int ofs_in_node = dn->ofs_in_node;
+
+	addr_array = blkaddr_in_node(node_blk);
+	addr_array[ofs_in_node] = cpu_to_le32(dn->data_blkaddr);
+	if (dn->node_blk != dn->inode_blk)
+		dn->ndirty = 1;
+	else
+		dn->idirty = 1;
+}
+
+/*
+ * In this function, we get a new node blk, and write back
+ * node_blk would be sloadd in RAM, linked by dn->node_blk
+ */
+block_t new_node_block(struct f2fs_sb_info *sbi,
+				struct dnode_of_data *dn, unsigned int ofs)
+{
+	struct f2fs_node *f2fs_inode;
+	struct f2fs_node *node_blk;
+	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
+	struct f2fs_summary sum;
+	struct node_info ni;
+	block_t blkaddr;
+	int type;
+
+	f2fs_inode = dn->inode_blk;
+
+	node_blk = calloc(BLOCK_SZ, 1);
+	ASSERT(node_blk);
+
+	node_blk->footer.nid = cpu_to_le32(dn->nid);
+	node_blk->footer.ino = f2fs_inode->footer.ino;
+	node_blk->footer.flag = cpu_to_le32(ofs << OFFSET_BIT_SHIFT);
+	node_blk->footer.cp_ver = ckpt->checkpoint_ver;
+
+	type = CURSEG_COLD_NODE;
+	if (IS_DNODE(node_blk)) {
+		if (S_ISDIR(f2fs_inode->i.i_mode))
+			type = CURSEG_HOT_NODE;
+		else
+			type = CURSEG_WARM_NODE;
+	}
+
+	get_node_info(sbi, dn->nid, &ni);
+	set_summary(&sum, dn->nid, 0, ni.version);
+	reserve_new_block(sbi, &blkaddr, &sum, type);
+
+	/* update nat info */
+	update_nat_blkaddr(sbi, le32_to_cpu(f2fs_inode->footer.ino),
+						dn->nid, blkaddr);
+
+	dn->node_blk = node_blk;
+	inc_inode_blocks(dn);
+	return blkaddr;
+}
+
+/*
+ * get_node_path - Get the index path of pgoff_t block
+ * @offset: offset in the current index node block.
+ * @noffset: NO. of the index block within a file.
+ * return: depth of the index path.
+ *
+ * By default, it sets inline_xattr and inline_data
+ */
+static int get_node_path(unsigned long block,
+				int offset[4], unsigned int noffset[4])
+{
+	const long direct_index = DEF_ADDRS_PER_INODE_INLINE_XATTR;
+	const long direct_blks = ADDRS_PER_BLOCK;
+	const long dptrs_per_blk = NIDS_PER_BLOCK;
+	const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK;
+	const long dindirect_blks = indirect_blks * NIDS_PER_BLOCK;
+	int n = 0;
+	int level = 0;
+
+	noffset[0] = 0;
+	if (block < direct_index) {
+		offset[n] = block;
+		goto got;
+	}
+
+	block -= direct_index;
+	if (block < direct_blks) {
+		offset[n++] = NODE_DIR1_BLOCK;
+		noffset[n]= 1;
+		offset[n] = block;
+		level = 1;
+		goto got;
+	}
+	block -= direct_blks;
+	if (block < direct_blks) {
+		offset[n++] = NODE_DIR2_BLOCK;
+		noffset[n] = 2;
+		offset[n] = block;
+		level = 1;
+		goto got;
+	}
+	block -= direct_blks;
+	if (block < indirect_blks) {
+		offset[n++] = NODE_IND1_BLOCK;
+		noffset[n] = 3;
+		offset[n++] = block / direct_blks;
+		noffset[n] = 4 + offset[n - 1];
+		offset[n] = block % direct_blks;
+		level = 2;
+		goto got;
+	}
+	block -= indirect_blks;
+	if (block < indirect_blks) {
+		offset[n++] = NODE_IND2_BLOCK;
+		noffset[n] = 4 + dptrs_per_blk;
+		offset[n++] = block / direct_blks;
+		noffset[n] = 5 + dptrs_per_blk + offset[n - 1];
+		offset[n] = block % direct_blks;
+		level = 2;
+		goto got;
+	}
+	block -= indirect_blks;
+	if (block < dindirect_blks) {
+		offset[n++] = NODE_DIND_BLOCK;
+		noffset[n] = 5 + (dptrs_per_blk * 2);
+		offset[n++] = block / indirect_blks;
+		noffset[n] = 6 + (dptrs_per_blk * 2) +
+			offset[n - 1] * (dptrs_per_blk + 1);
+		offset[n++] = (block / direct_blks) % dptrs_per_blk;
+		noffset[n] = 7 + (dptrs_per_blk * 2) +
+			offset[n - 2] * (dptrs_per_blk + 1) +
+			offset[n - 1];
+		offset[n] = block % direct_blks;
+		level = 3;
+		goto got;
+	} else {
+		ASSERT(0);
+	}
+got:
+	return level;
+}
+
+void get_dnode_of_data(struct f2fs_sb_info *sbi, struct dnode_of_data *dn,
+						pgoff_t index, int mode)
+{
+	int offset[4];
+	unsigned int noffset[4];
+	struct f2fs_node *parent = NULL;
+	nid_t nids[4];
+	block_t nblk[4];
+	struct node_info ni;
+	int level, i;
+	int ret;
+
+	level = get_node_path(index, offset, noffset);
+
+	nids[0] = dn->nid;
+	parent = dn->inode_blk;
+	if (level != 0)
+		nids[1] = get_nid(parent, offset[0], 1);
+	else
+		dn->node_blk = dn->inode_blk;
+
+	get_node_info(sbi, nids[0], &ni);
+	nblk[0] = ni.blk_addr;
+
+	for (i = 1; i <= level; i++) {
+		if (!nids[i] && mode == ALLOC_NODE) {
+			f2fs_alloc_nid(sbi, &nids[i], 0);
+
+			dn->nid = nids[i];
+
+			/* Function new_node_blk get a new f2fs_node blk and update*/
+			/* We should make sure that dn->node_blk == NULL*/
+			nblk[i] = new_node_block(sbi, dn, noffset[i]);
+			ASSERT(nblk[i]);
+
+			set_nid(parent, offset[i - 1], nids[i], i == 1);
+		} else {
+			/* If Sparse file no read API, */
+			struct node_info ni;
+
+			get_node_info(sbi, nids[i], &ni);
+			dn->node_blk = calloc(BLOCK_SZ, 1);
+			ASSERT(dn->node_blk);
+
+			ret = dev_read_block(dn->node_blk, ni.blk_addr);
+			ASSERT(ret >= 0);
+
+			nblk[i] = ni.blk_addr;
+		}
+
+		if (mode == ALLOC_NODE){
+			/* Parent node may have changed */
+			ret = dev_write_block(parent, nblk[i - 1]);
+			ASSERT(ret >= 0);
+		}
+		if (i != 1)
+			free(parent);
+
+		if (i < level) {
+			parent = dn->node_blk;
+			nids[i + 1] = get_nid(parent, offset[i], 0);
+		}
+	}
+
+	dn->nid = nids[level];
+	dn->ofs_in_node = offset[level];
+	dn->data_blkaddr = datablock_addr(dn->node_blk, dn->ofs_in_node);
+	dn->node_blkaddr = nblk[level];
+}
diff --git a/fsck/node.h b/fsck/node.h
new file mode 100644
index 0000000..721e5b7
--- /dev/null
+++ b/fsck/node.h
@@ -0,0 +1,101 @@
+/**
+ * node.h
+ *
+ * Many parts of codes are copied from Linux kernel/fs/f2fs.
+ *
+ * Copyright (C) 2015 Huawei Ltd.
+ * Witten by:
+ *   Hou Pengyang <houpengyang@huawei.com>
+ *   Liu Shuoran <liushuoran@huawei.com>
+ *   Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * 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.
+ */
+#ifndef _NODE_H_
+#define _NODE_H_
+
+#include "fsck.h"
+
+#define ADDRS_PER_PAGE(page) \
+	(IS_INODE(page) ? ADDRS_PER_INODE(&page->i) : ADDRS_PER_BLOCK)
+
+static inline int IS_INODE(struct f2fs_node *node)
+{
+	return ((node)->footer.nid == (node)->footer.ino);
+}
+
+static inline __le32 *blkaddr_in_node(struct f2fs_node *node)
+{
+	return IS_INODE(node) ? node->i.i_addr : node->dn.addr;
+}
+
+static inline block_t datablock_addr(struct f2fs_node *node_page,
+					unsigned int offset)
+{
+	__le32 *addr_array;
+
+	ASSERT(node_page);
+	addr_array = blkaddr_in_node(node_page);
+	return le32_to_cpu(addr_array[offset]);
+}
+
+static inline void set_nid(struct f2fs_node * rn, int off, nid_t nid, int i)
+{
+	if (i)
+		rn->i.i_nid[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid);
+	else
+		rn->in.nid[off] = cpu_to_le32(nid);
+}
+
+static inline nid_t get_nid(struct f2fs_node * rn, int off, int i)
+{
+	if (i)
+		return le32_to_cpu(rn->i.i_nid[off - NODE_DIR1_BLOCK]);
+	else
+		return le32_to_cpu(rn->in.nid[off]);
+}
+
+enum {
+	ALLOC_NODE,	/* allocate a new node page if needed */
+	LOOKUP_NODE,	/* lookup up a node without readahead */
+	LOOKUP_NODE_RA,
+};
+
+static inline void set_new_dnode(struct dnode_of_data *dn,
+		struct f2fs_node *iblk, struct f2fs_node *nblk, nid_t nid)
+{
+	memset(dn, 0, sizeof(*dn));
+	dn->inode_blk = iblk;
+	dn->node_blk = nblk;
+	dn->nid = nid;
+	dn->idirty = 0;
+	dn->ndirty = 0;
+}
+
+static inline void inc_inode_blocks(struct dnode_of_data *dn)
+{
+	u64 blocks = le64_to_cpu(dn->inode_blk->i.i_blocks);
+
+	dn->inode_blk->i.i_blocks = cpu_to_le64(blocks + 1);
+	dn->idirty = 1;
+}
+
+static inline int IS_DNODE(struct f2fs_node *node_page)
+{
+	unsigned int ofs = ofs_of_node(node_page);
+
+	if (ofs == 3 || ofs == 4 + NIDS_PER_BLOCK ||
+			ofs == 5 + 2 * NIDS_PER_BLOCK)
+		return 0;
+
+	if (ofs >= 6 + 2 * NIDS_PER_BLOCK) {
+		ofs -= 6 + 2 * NIDS_PER_BLOCK;
+		if (!((long int)ofs % (NIDS_PER_BLOCK + 1)))
+			return 0;
+	}
+	return 1;
+}
+
+#endif
diff --git a/fsck/segment.c b/fsck/segment.c
new file mode 100644
index 0000000..9ce8bf5
--- /dev/null
+++ b/fsck/segment.c
@@ -0,0 +1,220 @@
+/**
+ * segment.c
+ *
+ * Many parts of codes are copied from Linux kernel/fs/f2fs.
+ *
+ * Copyright (C) 2015 Huawei Ltd.
+ * Witten by:
+ *   Hou Pengyang <houpengyang@huawei.com>
+ *   Liu Shuoran <liushuoran@huawei.com>
+ *   Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * 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"
+#include "node.h"
+
+void reserve_new_block(struct f2fs_sb_info *sbi, block_t *to,
+			struct f2fs_summary *sum, int type)
+{
+	struct seg_entry *se;
+	u64 blkaddr;
+	u64 offset;
+
+	blkaddr = SM_I(sbi)->main_blkaddr;
+
+	if (find_next_free_block(sbi, &blkaddr, 0, type)) {
+		ERR_MSG("Not enough space to allocate blocks");
+		ASSERT(0);
+	}
+
+	se = get_seg_entry(sbi, GET_SEGNO(sbi, blkaddr));
+	offset = OFFSET_IN_SEG(sbi, blkaddr);
+	se->type = type;
+	se->valid_blocks++;
+	f2fs_set_bit(offset, (char *)se->cur_valid_map);
+	sbi->total_valid_block_count++;
+	se->dirty = 1;
+
+	/* read/write SSA */
+	*to = (block_t)blkaddr;
+	update_sum_entry(sbi, *to, sum);
+}
+
+void new_data_block(struct f2fs_sb_info *sbi, void *block,
+				struct dnode_of_data *dn, int type)
+{
+	struct f2fs_summary sum;
+	struct node_info ni;
+
+	ASSERT(dn->node_blk);
+	memset(block, 0, BLOCK_SZ);
+
+	get_node_info(sbi, dn->nid, &ni);
+	set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
+	reserve_new_block(sbi, &dn->data_blkaddr, &sum, type);
+
+	inc_inode_blocks(dn);
+	set_data_blkaddr(dn);
+}
+
+static void f2fs_write_block(struct f2fs_sb_info *sbi, nid_t ino, void *buffer,
+					u64 count, pgoff_t offset)
+{
+	u64 start = F2FS_BYTES_TO_BLK(offset);
+	u64 len = F2FS_BYTES_TO_BLK(count);
+	u64 end_offset;
+	u64 off_in_block, len_in_block, len_already;
+	struct dnode_of_data dn = {0};
+	void *data_blk;
+	struct node_info ni;
+	struct f2fs_node *inode;
+	int ret = -1;
+
+	get_node_info(sbi, ino, &ni);
+	inode = calloc(BLOCK_SZ, 1);
+	ASSERT(inode);
+
+	ret = dev_read_block(inode, ni.blk_addr);
+	ASSERT(ret >= 0);
+
+	if (S_ISDIR(inode->i.i_mode) || S_ISLNK(inode->i.i_mode))
+		ASSERT(0);
+
+	off_in_block = offset & ((1 << F2FS_BLKSIZE_BITS) - 1);
+	len_in_block = (1 << F2FS_BLKSIZE_BITS) - off_in_block;
+	len_already = 0;
+
+	/*
+	 * When calculate how many blocks this 'count' stride accross,
+	 * We should take offset in a block in account.
+	 */
+	len = F2FS_BYTES_TO_BLK(count + off_in_block
+			+ ((1 << F2FS_BLKSIZE_BITS) - 1));
+
+	data_blk = calloc(BLOCK_SZ, 1);
+	ASSERT(data_blk);
+
+	set_new_dnode(&dn, inode, NULL, ino);
+
+	while (len) {
+		if (dn.node_blk != dn.inode_blk)
+			free(dn.node_blk);
+
+		set_new_dnode(&dn, inode, NULL, ino);
+		get_dnode_of_data(sbi, &dn, start, ALLOC_NODE);
+
+		end_offset = ADDRS_PER_PAGE(dn.node_blk);
+
+		while (dn.ofs_in_node < end_offset && len) {
+			block_t blkaddr;
+
+			blkaddr = datablock_addr(dn.node_blk, dn.ofs_in_node);
+
+			/* A new page from WARM_DATA */
+			if (blkaddr == NULL_ADDR)
+				new_data_block(sbi, data_blk, &dn,
+							CURSEG_WARM_DATA);
+
+			/* Copy data from buffer to file */
+			ret = dev_read_block(data_blk, dn.data_blkaddr);
+			ASSERT(ret >= 0);
+
+			memcpy(data_blk + off_in_block, buffer, len_in_block);
+
+			ret = dev_write_block(data_blk, dn.data_blkaddr);
+			ASSERT(ret >= 0);
+
+			off_in_block = 0;
+			len_already += len_in_block;
+			if ((count - len_already) > (1 << F2FS_BLKSIZE_BITS))
+				len_in_block = 1 << F2FS_BLKSIZE_BITS;
+			else
+				len_in_block = count - len_already;
+			len--;
+			start++;
+			dn.ofs_in_node++;
+		}
+		/* Update the direct node */
+		if (dn.ndirty) {
+			ret = dev_write_block(dn.node_blk, dn.node_blkaddr);
+			ASSERT(ret >= 0);
+		}
+	}
+
+	/* Update the inode info */
+	if (le64_to_cpu(inode->i.i_size) < offset + count) {
+		inode->i.i_size = cpu_to_le64(offset + count);
+		dn.idirty = 1;
+	}
+
+	if (dn.idirty) {
+		ASSERT(inode == dn.inode_blk);
+		ret = dev_write_block(inode, ni.blk_addr);
+		ASSERT(ret >= 0);
+	}
+
+	if (dn.node_blk && dn.node_blk != dn.inode_blk)
+		free(dn.node_blk);
+	free(data_blk);
+	free(inode);
+}
+
+int f2fs_build_file(struct f2fs_sb_info *sbi, struct dentry *de)
+{
+	int fd, n;
+	pgoff_t off = 0;
+	char buffer[BLOCK_SZ];
+
+	if (de->ino == 0)
+		return -1;
+
+	fd = open(de->full_path, O_RDONLY);
+	if (fd < 0) {
+		MSG(0, "Skip: Fail to open %s\n", de->full_path);
+		return -1;
+	}
+
+	/* inline_data support */
+	if (de->size <= MAX_INLINE_DATA) {
+		struct node_info ni;
+		struct f2fs_node *node_blk;
+		int ret;
+
+		get_node_info(sbi, de->ino, &ni);
+
+		node_blk = calloc(BLOCK_SZ, 1);
+		ASSERT(node_blk);
+
+		ret = dev_read_block(node_blk, ni.blk_addr);
+		ASSERT(ret >= 0);
+
+		node_blk->i.i_inline |= F2FS_INLINE_DATA;
+		node_blk->i.i_inline |= F2FS_DATA_EXIST;
+		n = read(fd, buffer, BLOCK_SZ);
+		ASSERT(n == de->size);
+		memcpy(&node_blk->i.i_addr[1], buffer, de->size);
+
+		node_blk->i.i_size = cpu_to_le64(de->size);
+
+		ret = dev_write_block(node_blk, ni.blk_addr);
+		ASSERT(ret >= 0);
+		free(node_blk);
+	} else {
+		while ((n = read(fd, buffer, BLOCK_SZ)) > 0) {
+			f2fs_write_block(sbi, de->ino, buffer, n, off);
+			off += n;
+		}
+	}
+
+	close(fd);
+	if (n < 0)
+		return -1;
+
+	update_free_segments(sbi);
+
+	MSG(1, "Info: built a file %s, size=%lu\n", de->full_path, de->size);
+	return 0;
+}
diff --git a/fsck/sload.c b/fsck/sload.c
new file mode 100644
index 0000000..424e642
--- /dev/null
+++ b/fsck/sload.c
@@ -0,0 +1,247 @@
+/**
+ * sload.c
+ *
+ * Copyright (C) 2015 Huawei Ltd.
+ * Witten by:
+ *   Hou Pengyang <houpengyang@huawei.com>
+ *   Liu Shuoran <liushuoran@huawei.com>
+ *   Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * 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.
+ */
+#define _GNU_SOURCE
+#include "fsck.h"
+#include <libgen.h>
+#include <dirent.h>
+#include <mntent.h>
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+
+#ifdef WITH_ANDROID
+#include <selinux/label.h>
+#include <private/android_filesystem_config.h>
+
+static void handle_selabel(struct dentry *de, int dir, char *target_out)
+{
+	uint64_t capabilities;
+	unsigned int mode = 0;
+	unsigned int uid = 0;
+	unsigned int gid = 0;
+
+	fs_config(de[i].path, dir, target_out, &uid,
+			&gid, &mode, &capabilities);
+	de.mode = mode;
+	de.uid = uid;
+	de.gid = gid;
+	de.capabilities = capabilities;
+}
+#else
+#define handle_selabel(...)
+#endif
+
+static int filter_dot(const struct dirent *d)
+{
+	return (strcmp(d->d_name, "..") && strcmp(d->d_name, "."));
+}
+
+static void f2fs_make_directory(struct f2fs_sb_info *sbi,
+				int entries, struct dentry *de)
+{
+	int i = 0;
+
+	for (i = 0; i < entries; i++) {
+		if (de[i].file_type == F2FS_FT_DIR)
+			f2fs_mkdir(sbi, de + i);
+		else if (de[i].file_type == F2FS_FT_REG_FILE)
+			f2fs_create(sbi, de + i);
+		else if (de[i].file_type == F2FS_FT_SYMLINK)
+			f2fs_symlink(sbi, de + i);
+	}
+}
+
+static int build_directory(struct f2fs_sb_info *sbi, const char *full_path,
+			const char *dir_path, const char *target_out_dir,
+			nid_t dir_ino, struct selabel_handle *sehnd)
+{
+	int entries = 0;
+	struct dentry *dentries;
+	struct dirent **namelist = NULL;
+	struct stat stat;
+	int i, ret = 0;
+
+	entries = scandir(full_path, &namelist, filter_dot, (void *)alphasort);
+	if (entries < 0) {
+		ERR_MSG("No entries in %s\n", full_path);
+		return -ENOENT;
+	}
+
+	dentries = calloc(entries, sizeof(struct dentry));
+	if (dentries == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < entries; i++) {
+		dentries[i].name = (unsigned char *)strdup(namelist[i]->d_name);
+		if (dentries[i].name == NULL) {
+			ERR_MSG("Skip: ENOMEM\n");
+			continue;
+		}
+		dentries[i].len = strlen((char *)dentries[i].name);
+
+		ret = asprintf(&dentries[i].path, "%s/%s",
+					dir_path, namelist[i]->d_name);
+		ASSERT(ret > 0);
+		ret = asprintf(&dentries[i].full_path, "%s/%s",
+					full_path, namelist[i]->d_name);
+		ASSERT(ret > 0);
+		free(namelist[i]);
+
+		ret = lstat(dentries[i].full_path, &stat);
+		if (ret < 0) {
+			ERR_MSG("Skip: lstat failure\n");
+			continue;
+		}
+		dentries[i].size = stat.st_size;
+		dentries[i].mode = stat.st_mode &
+			(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO);
+		dentries[i].mtime = stat.st_mtime;
+
+		handle_selabel(dentries + i, S_ISDIR(stat.st_mode),
+							target_out_dir);
+
+		if (sehnd && selabel_lookup(sehnd, &dentries[i].secon,
+					dentries[i].path, stat.st_mode) < 0)
+			ERR_MSG("Cannot lookup security context for %s\n",
+						dentries[i].path);
+
+		dentries[i].pino = dir_ino;
+
+		if (S_ISREG(stat.st_mode)) {
+			dentries[i].file_type = F2FS_FT_REG_FILE;
+		} else if (S_ISDIR(stat.st_mode)) {
+			dentries[i].file_type = F2FS_FT_DIR;
+		} else if (S_ISCHR(stat.st_mode)) {
+			dentries[i].file_type = F2FS_FT_CHRDEV;
+		} else if (S_ISBLK(stat.st_mode)) {
+			dentries[i].file_type = F2FS_FT_BLKDEV;
+		} else if (S_ISFIFO(stat.st_mode)) {
+			dentries[i].file_type = F2FS_FT_FIFO;
+		} else if (S_ISSOCK(stat.st_mode)) {
+			dentries[i].file_type = F2FS_FT_SOCK;
+		} else if (S_ISLNK(stat.st_mode)) {
+			dentries[i].file_type = F2FS_FT_SYMLINK;
+			dentries[i].link = calloc(F2FS_BLKSIZE, 1);
+			ASSERT(dentries[i].link);
+			ret = readlink(dentries[i].full_path,
+					dentries[i].link, F2FS_BLKSIZE - 1);
+			ASSERT(ret >= 0);
+		} else {
+			MSG(1, "unknown file type on %s", dentries[i].path);
+			i--;
+			entries--;
+		}
+	}
+
+	free(namelist);
+
+	f2fs_make_directory(sbi, entries, dentries);
+
+	for (i = 0; i < entries; i++) {
+		if (dentries[i].file_type == F2FS_FT_REG_FILE) {
+			f2fs_build_file(sbi, dentries + i);
+		} else if (dentries[i].file_type == F2FS_FT_DIR) {
+			char *subdir_full_path = NULL;
+			char *subdir_dir_path;
+
+			ret = asprintf(&subdir_full_path, "%s/",
+							dentries[i].full_path);
+			ASSERT(ret > 0);
+			ret = asprintf(&subdir_dir_path, "%s/",
+							dentries[i].path);
+			ASSERT(ret > 0);
+
+			build_directory(sbi, subdir_full_path, subdir_dir_path,
+					target_out_dir, dentries[i].ino, sehnd);
+			free(subdir_full_path);
+			free(subdir_dir_path);
+		} else if (dentries[i].file_type == F2FS_FT_SYMLINK) {
+			/*
+			 * It is already done in f2fs_make_directory
+			 * f2fs_make_symlink(sbi, dir_ino, &dentries[i]);
+			 */
+		} else {
+			MSG(1, "Error unknown file type\n");
+		}
+
+		if (dentries[i].secon) {
+			inode_set_selinux(sbi, dentries[i].ino, dentries[i].secon);
+			MSG(1, "File = %s \n----->SELinux context = %s\n",
+					dentries[i].path, dentries[i].secon);
+			MSG(1, "----->mode = 0x%x, uid = 0x%x, gid = 0x%x, "
+					"capabilities = 0x%lx \n",
+					dentries[i].mode, dentries[i].uid,
+					dentries[i].gid, dentries[i].capabilities);
+		}
+
+		free(dentries[i].path);
+		free(dentries[i].full_path);
+		free((void *)dentries[i].name);
+		free(dentries[i].secon);
+	}
+
+	free(dentries);
+	return 0;
+}
+
+int f2fs_sload(struct f2fs_sb_info *sbi, const char *from_dir,
+				const char *mount_point,
+				const char *target_out_dir,
+				struct selabel_handle *sehnd)
+{
+	int ret = 0;
+	nid_t mnt_ino = F2FS_ROOT_INO(sbi);
+
+	/* flush NAT/SIT journal entries */
+	flush_journal_entries(sbi);
+
+	ret = f2fs_find_path(sbi, (char *)mount_point, &mnt_ino);
+	if (ret) {
+		ERR_MSG("Failed to get mount point %s\n", mount_point);
+		return ret;
+	}
+
+	ret = build_directory(sbi, from_dir, mount_point, target_out_dir,
+						mnt_ino, sehnd);
+	if (ret) {
+		ERR_MSG("Failed to build due to %d\n", ret);
+		return ret;
+	}
+
+	if (sehnd) {
+		char *secontext = NULL;
+
+		/* set root inode selinux context */
+		if (selabel_lookup(sehnd, &secontext, mount_point, S_IFDIR) < 0)
+			ERR_MSG("cannot lookup security context for %s\n",
+								mount_point);
+		if (secontext) {
+			MSG(1, "Labeling %s as %s, root_ino = %d\n",
+					mount_point, secontext, F2FS_ROOT_INO(sbi));
+			/* xattr_add for root inode */
+			inode_set_selinux(sbi, F2FS_ROOT_INO(sbi), secontext);
+		}
+		free(secontext);
+	}
+
+	/* update curseg info; can update sit->types */
+	move_curseg_info(sbi, SM_I(sbi)->main_blkaddr);
+	zero_journal_entries(sbi);
+	write_curseg_info(sbi);
+
+	/* flush dirty sit entries */
+	flush_sit_entries(sbi);
+
+	write_checkpoint(sbi);
+	return 0;
+}
diff --git a/fsck/xattr.c b/fsck/xattr.c
new file mode 100644
index 0000000..3f5c7d3
--- /dev/null
+++ b/fsck/xattr.c
@@ -0,0 +1,241 @@
+/**
+ * xattr.c
+ *
+ * Many parts of codes are copied from Linux kernel/fs/f2fs.
+ *
+ * Copyright (C) 2015 Huawei Ltd.
+ * Witten by:
+ *   Hou Pengyang <houpengyang@huawei.com>
+ *   Liu Shuoran <liushuoran@huawei.com>
+ *   Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * 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"
+#include "node.h"
+#include "xattr.h"
+
+#define XATTR_CREATE 0x1
+#define XATTR_REPLACE 0x2
+
+static void *read_all_xattrs(struct f2fs_sb_info *sbi, struct f2fs_node *inode)
+{
+	struct f2fs_xattr_header *header;
+	void *txattr_addr;
+	u64 inline_size = inline_xattr_size(&inode->i);
+
+	txattr_addr = calloc(inline_size + BLOCK_SZ, 1);
+	ASSERT(txattr_addr);
+
+	if (inline_size)
+		memcpy(txattr_addr, inline_xattr_addr(&inode->i), inline_size);
+
+	/* Read from xattr node block. */
+	if (inode->i.i_xattr_nid) {
+		struct node_info ni;
+		int ret;
+
+		get_node_info(sbi, le32_to_cpu(inode->i.i_xattr_nid), &ni);
+		ret = dev_read_block(txattr_addr + inline_size, ni.blk_addr);
+		ASSERT(ret >= 0);
+	}
+
+	header = XATTR_HDR(txattr_addr);
+
+	/* Never been allocated xattrs */
+	if (le32_to_cpu(header->h_magic) != F2FS_XATTR_MAGIC) {
+		header->h_magic = cpu_to_le32(F2FS_XATTR_MAGIC);
+		header->h_refcount = cpu_to_le32(1);
+	}
+	return txattr_addr;
+}
+
+static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index,
+		size_t len, const char *name)
+{
+	struct f2fs_xattr_entry *entry;
+	list_for_each_xattr(entry, base_addr) {
+		if (entry->e_name_index != index)
+			continue;
+		if (entry->e_name_len != len)
+			continue;
+		if (!memcmp(entry->e_name, name, len))
+			break;
+	}
+	return entry;
+}
+
+static void write_all_xattrs(struct f2fs_sb_info *sbi,
+		struct f2fs_node *inode, __u32 hsize, void *txattr_addr)
+{
+	void *xattr_addr;
+	struct dnode_of_data dn;
+	struct node_info ni;
+	struct f2fs_node *xattr_node;
+	nid_t new_nid = 0;
+	block_t blkaddr;
+	nid_t xnid = le32_to_cpu(inode->i.i_xattr_nid);
+	u64 inline_size = inline_xattr_size(&inode->i);
+	int ret;
+
+	ASSERT(inode->i.i_inline & F2FS_INLINE_XATTR);
+	memcpy(inline_xattr_addr(&inode->i), txattr_addr, inline_size);
+
+	if (hsize <= inline_size)
+		return;
+
+	if (!xnid) {
+		f2fs_alloc_nid(sbi, &new_nid, 0);
+
+		set_new_dnode(&dn, inode, NULL, new_nid);
+		/* NAT entry would be updated by new_node_page. */
+		blkaddr = new_node_block(sbi, &dn, XATTR_NODE_OFFSET);
+		ASSERT(dn.node_blk);
+		xattr_node = dn.node_blk;
+		inode->i.i_xattr_nid = cpu_to_le32(new_nid);
+	} else {
+		set_new_dnode(&dn, inode, NULL, xnid);
+		get_node_info(sbi, xnid, &ni);
+		blkaddr = ni.blk_addr;
+		xattr_node = calloc(BLOCK_SZ, 1);
+		ASSERT(xattr_node);
+		ret = dev_read_block(xattr_node, ni.blk_addr);
+		ASSERT(ret >= 0);
+	}
+
+	/* write to xattr node block */
+	xattr_addr = (void *)xattr_node;
+	memcpy(xattr_addr, txattr_addr + inline_size,
+			PAGE_SIZE - sizeof(struct node_footer));
+
+	ret = dev_write_block(xattr_node, blkaddr);
+	ASSERT(ret >= 0);
+}
+
+int f2fs_setxattr(struct f2fs_sb_info *sbi, nid_t ino, int index, const char *name,
+		const void *value, size_t size, int flags)
+{
+	struct f2fs_node *inode;
+	void *base_addr;
+	struct f2fs_xattr_entry *here, *last;
+	struct node_info ni;
+	int error = 0;
+	int len;
+	int found, newsize;
+	__u32 new_hsize;
+	int ret;
+
+	if (name == NULL)
+		return -EINVAL;
+
+	if (value == NULL)
+		return -EINVAL;
+
+	len = strlen(name);
+
+	if (len > F2FS_NAME_LEN || size > MAX_VALUE_LEN)
+		return -ERANGE;
+
+	if (ino < 3)
+		return -EINVAL;
+
+	/* Now We just support selinux */
+	ASSERT(index == F2FS_XATTR_INDEX_SECURITY);
+
+	get_node_info(sbi, ino, &ni);
+	inode = calloc(BLOCK_SZ, 1);
+	ASSERT(inode);
+	ret = dev_read_block(inode, ni.blk_addr);
+	ASSERT(ret >= 0);
+
+	base_addr = read_all_xattrs(sbi, inode);
+	ASSERT(base_addr);
+
+	here = __find_xattr(base_addr, index, len, name);
+
+	found = IS_XATTR_LAST_ENTRY(here) ? 0 : 1;
+
+	if ((flags & XATTR_REPLACE) && !found) {
+		error = -ENODATA;
+		goto exit;
+	} else if ((flags & XATTR_CREATE) && found) {
+		error = -EEXIST;
+		goto exit;
+	}
+
+	last = here;
+	while (!IS_XATTR_LAST_ENTRY(last))
+		last = XATTR_NEXT_ENTRY(last);
+
+	newsize = XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + len + size);
+
+	/* 1. Check space */
+	if (value) {
+		int free;
+		/*
+		 * If value is NULL, it is remove operation.
+		 * In case of update operation, we calculate free.
+		 */
+		free = MIN_OFFSET - ((char *)last - (char *)base_addr);
+		if (found)
+			free = free + ENTRY_SIZE(here);
+		if (free < newsize) {
+			error = -ENOSPC;
+			goto exit;
+		}
+	}
+
+	/* 2. Remove old entry */
+	if (found) {
+		/*
+		 * If entry if sound, remove old entry.
+		 * If not found, remove operation is not needed
+		 */
+		struct f2fs_xattr_entry *next = XATTR_NEXT_ENTRY(here);
+		int oldsize = ENTRY_SIZE(here);
+
+		memmove(here, next, (char *)last - (char *)next);
+		last = (struct f2fs_xattr_entry *)((char *)last - oldsize);
+		memset(last, 0, oldsize);
+
+	}
+
+	new_hsize = (char *)last - (char *)base_addr;
+
+	/* 3. Write new entry */
+	if (value) {
+		char *pval;
+		/*
+		 * Before we come here, old entry is removed.
+		 * We just write new entry.
+		 */
+		memset(last, 0, newsize);
+		last->e_name_index = index;
+		last->e_name_len = len;
+		memcpy(last->e_name, name, len);
+		pval = last->e_name + len;
+		memcpy(pval, value, size);
+		last->e_value_size = cpu_to_le16(size);
+		new_hsize += newsize;
+	}
+
+	write_all_xattrs(sbi, inode, new_hsize, base_addr);
+
+	/* inode need update */
+	ret = dev_write_block(inode, ni.blk_addr);
+	ASSERT(ret >= 0);
+exit:
+	free(base_addr);
+	return error;
+}
+
+int inode_set_selinux(struct f2fs_sb_info *sbi, u32 ino, const char *secon)
+{
+	if (!secon)
+		return 0;
+
+	return f2fs_setxattr(sbi, ino, F2FS_XATTR_INDEX_SECURITY,
+			XATTR_SELINUX_SUFFIX, secon, strlen(secon), 1);
+}
diff --git a/fsck/xattr.h b/fsck/xattr.h
new file mode 100644
index 0000000..3ca9133
--- /dev/null
+++ b/fsck/xattr.h
@@ -0,0 +1,66 @@
+/**
+ * xattr.h
+ *
+ * Many parts of codes are copied from Linux kernel/fs/f2fs.
+ *
+ * Copyright (C) 2015 Huawei Ltd.
+ * Witten by:
+ *   Hou Pengyang <houpengyang@huawei.com>
+ *   Liu Shuoran <liushuoran@huawei.com>
+ *   Jaegeuk Kim <jaegeuk@kernel.org>
+ *
+ * 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.
+ */
+#ifndef _XATTR_H_
+#define _XATTR_H_
+
+#include <f2fs_fs.h>
+#include "f2fs.h"
+
+struct f2fs_xattr_header {
+	__le32 h_magic;		/* magic number for identification */
+	__le32 h_refcount;	/* reference count */
+	__u32 h_sloadd[4];	/* zero right now */
+};
+
+struct f2fs_xattr_entry {
+	__u8 e_name_index;
+	__u8 e_name_len;
+	__le16 e_value_size;	/* size of attribute value */
+	char e_name[0];		/* attribute name */
+};
+
+#define XATTR_ROUND	(3)
+
+#define XATTR_SELINUX_SUFFIX "selinux"
+#define F2FS_XATTR_INDEX_SECURITY	6
+#define IS_XATTR_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
+
+#define XATTR_HDR(ptr)		((struct f2fs_xattr_header *)(ptr))
+#define XATTR_ENTRY(ptr) 	((struct f2fs_xattr_entry *)(ptr))
+#define F2FS_XATTR_MAGIC	0xF2F52011
+
+#define XATTR_NEXT_ENTRY(entry) ((struct f2fs_xattr_entry *) ((char *)(entry) +\
+					ENTRY_SIZE(entry)))
+#define XATTR_FIRST_ENTRY(ptr)	(XATTR_ENTRY(XATTR_HDR(ptr) + 1))
+
+#define XATTR_ALIGN(size)	((size + XATTR_ROUND) & ~XATTR_ROUND)
+
+#define ENTRY_SIZE(entry) (XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + \
+			entry->e_name_len + le16_to_cpu(entry->e_value_size)))
+
+#define list_for_each_xattr(entry, addr) \
+	for (entry = XATTR_FIRST_ENTRY(addr); \
+			!IS_XATTR_LAST_ENTRY(entry); \
+			entry = XATTR_NEXT_ENTRY(entry))
+
+#define MIN_OFFSET	XATTR_ALIGN(PAGE_SIZE -			\
+		sizeof(struct node_footer) - sizeof(__u32))
+
+#define MAX_VALUE_LEN	(MIN_OFFSET -				\
+		sizeof(struct f2fs_xattr_header) -		\
+		sizeof(struct f2fs_xattr_entry))
+
+#endif
diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
index 99798d0..1cc08fd 100644
--- a/include/f2fs_fs.h
+++ b/include/f2fs_fs.h
@@ -28,6 +28,7 @@ typedef u32		block_t;
 typedef u32		nid_t;
 typedef u8		bool;
 typedef unsigned long	pgoff_t;
+typedef unsigned short	umode_t;
 
 #if HAVE_BYTESWAP_H
 #include <byteswap.h>
@@ -208,6 +209,9 @@ static inline uint64_t bswap_64(uint64_t val)
 #define F2FS_SUPER_MAGIC	0xF2F52010	/* F2FS Magic Number */
 #define CHECKSUM_OFFSET		4092
 
+#define F2FS_BYTES_TO_BLK(bytes)    ((bytes) >> F2FS_BLKSIZE_BITS)
+#define F2FS_BLKSIZE_BITS 12
+
 /* for mkfs */
 #define	F2FS_NUMBER_OF_CHECKPOINT_PACK	2
 #define	DEFAULT_SECTOR_SIZE		512
@@ -222,6 +226,7 @@ enum f2fs_config_func {
 	DUMP,
 	DEFRAG,
 	RESIZE,
+	SLOAD,
 };
 
 struct f2fs_configuration {
@@ -264,6 +269,10 @@ struct f2fs_configuration {
 	u_int64_t defrag_start;
 	u_int64_t defrag_len;
 	u_int64_t defrag_target;
+
+	/* sload parameters */
+	char *from_dir;
+	char *mount_point;
 } __attribute__((packed));
 
 #ifdef CONFIG_64BIT
@@ -529,7 +538,9 @@ struct f2fs_extent {
 #define F2FS_NAME_LEN		255
 #define F2FS_INLINE_XATTR_ADDRS	50	/* 200 bytes for inline xattrs */
 #define DEF_ADDRS_PER_INODE	923	/* Address Pointers in an Inode */
-#define ADDRS_PER_INODE(fi)	addrs_per_inode(fi)
+#define ADDRS_PER_INODE(i)	addrs_per_inode(i)
+#define DEF_ADDRS_PER_INODE_INLINE_XATTR				\
+		(DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS)
 #define ADDRS_PER_BLOCK         1018	/* Address Pointers in a Direct Block */
 #define NIDS_PER_BLOCK          1018	/* Node IDs in an Indirect Block */
 
@@ -545,8 +556,8 @@ struct f2fs_extent {
 #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
 #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
 
-#define MAX_INLINE_DATA		(sizeof(__le32) * (DEF_ADDRS_PER_INODE - \
-						F2FS_INLINE_XATTR_ADDRS - 1))
+#define MAX_INLINE_DATA (sizeof(__le32) *				\
+			(DEF_ADDRS_PER_INODE_INLINE_XATTR - 1))
 
 #define INLINE_DATA_OFFSET	(PAGE_CACHE_SIZE - sizeof(struct node_footer) \
 				- sizeof(__le32)*(DEF_ADDRS_PER_INODE + 5 - 1))
@@ -634,6 +645,7 @@ struct f2fs_node {
  * For NAT entries
  */
 #define NAT_ENTRY_PER_BLOCK (PAGE_CACHE_SIZE / sizeof(struct f2fs_nat_entry))
+#define NAT_BLOCK_OFFSET(start_nid) (start_nid / NAT_ENTRY_PER_BLOCK)
 
 struct f2fs_nat_entry {
 	__u8 version;		/* latest version of cached nat entry */
diff --git a/man/Makefile.am b/man/Makefile.am
index 6c04de7..7856586 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -1,3 +1,3 @@
 ## Makefile.am
 
-dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8 resize.f2fs.8
+dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8 resize.f2fs.8 sload.f2fs.8
diff --git a/man/defrag.f2fs.8 b/man/defrag.f2fs.8
index 8c709a7..b08399b 100644
--- a/man/defrag.f2fs.8
+++ b/man/defrag.f2fs.8
@@ -72,4 +72,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
 .BR mkfs.f2fs(8),
 .BR dump.f2fs(8),
 .BR fsck.f2fs(8),
-.BR resize.f2fs(8).
+.BR resize.f2fs(8),
+.BR sload.f2fs(8).
diff --git a/man/dump.f2fs.8 b/man/dump.f2fs.8
index dc1d806..35616e5 100644
--- a/man/dump.f2fs.8
+++ b/man/dump.f2fs.8
@@ -66,4 +66,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
 .BR mkfs.f2fs(8),
 .BR fsck.f2fs(8),
 .BR defrag.f2fs(8),
-.BR resize.f2fs(8).
+.BR resize.f2fs(8),
+.BR sload.f2fs(8).
diff --git a/man/fsck.f2fs.8 b/man/fsck.f2fs.8
index 22457c5..af1076c 100644
--- a/man/fsck.f2fs.8
+++ b/man/fsck.f2fs.8
@@ -65,4 +65,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
 .BR mkfs.f2fs(8),
 .BR dump.f2fs(8),
 .BR defrag.f2fs(8),
-.BR resize.f2fs(8).
+.BR resize.f2fs(8),
+.BR sload.f2fs(8).
diff --git a/man/mkfs.f2fs.8 b/man/mkfs.f2fs.8
index b767c0c..0e54fe8 100644
--- a/man/mkfs.f2fs.8
+++ b/man/mkfs.f2fs.8
@@ -95,4 +95,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
 .BR fsck.f2fs(8),
 .BR dump.f2fs(8),
 .BR defrag.f2fs(8),
-.BR resize.f2fs(8).
+.BR resize.f2fs(8),
+.BR sload.f2fs(8).
diff --git a/man/resize.f2fs.8 b/man/resize.f2fs.8
index 1920810..463eca5 100644
--- a/man/resize.f2fs.8
+++ b/man/resize.f2fs.8
@@ -46,4 +46,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
 .BR mkfs.f2fs(8),
 .BR fsck.f2fs(8),
 .BR dump.f2fs(8),
-.BR defrag.f2fs(8).
+.BR defrag.f2fs(8),
+.BR sload.f2fs(8).
diff --git a/man/sload.f2fs.8 b/man/sload.f2fs.8
new file mode 100644
index 0000000..d07330c
--- /dev/null
+++ b/man/sload.f2fs.8
@@ -0,0 +1,56 @@
+.\" Copyright (C) 2015 Huawei Ltd.
+.\"
+.TH SLOAD.F2FS 8
+.SH NAME
+sload.f2fs \- load directories and files into the device directly
+.SH SYNOPSIS
+.B sload.f2fs
+[
+.B \-f
+.I source directory path
+]
+[
+.B \-t
+.I mount point
+]
+[
+.B \-d
+.I debugging-level
+]
+.I device
+.SH DESCRIPTION
+.B sload.f2fs
+is used to load directories and files into a disk partition.
+\fIdevice\fP is the special file corresponding to the device (e.g.
+\fI/dev/sdXX\fP).
+
+.PP
+The exit code returned by
+.B sload.f2fs
+is 0 on success and -1 on failure.
+.SH OPTIONS
+.TP
+.BI \-f " source directory path"
+Specify the source directory path to be loaded.
+.TP
+.BI \-t " mount point path"
+Specify the mount point path in the partition to load.
+.TP
+.BI \-d " debug-level"
+Specify the level of debugging options.
+The default number is 0, which shows basic debugging messages.
+.TP
+.SH AUTHOR
+This version of
+.B sload.f2fs
+has been written by Hou Pengyang <houpengyang@huawei.com>,
+Liu Shuoran <liushuoran@huawei.com>, Jaegeuk Kim <jaegeuk@kernel.org>
+.SH AVAILABILITY
+.B sload.f2fs
+is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
+.SH SEE ALSO
+.BR mkfs.f2fs(8),
+.BR fsck.f2fs(8),
+.BR dump.f2fs(8),
+.BR defrag.f2fs(8),
+.BR resize.f2fs(8).
-- 
2.6.3


------------------------------------------------------------------------------
Find and fix application performance issues faster with Applications Manager
Applications Manager provides deep performance insights into multiple tiers of
your business applications. It resolves application problems quickly and
reduces your MTTR. Get your free trial!
https://ad.doubleclick.net/ddm/clk/302982198;130105516;z

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/3] script: add simple test script
  2016-04-26  0:28 [PATCH 1/3] resize.f2fs: support to expand partition size Jaegeuk Kim
  2016-04-26  0:28 ` [PATCH 2/3] sload.f2fs: support loading files into partition directly Jaegeuk Kim
@ 2016-04-26  0:28 ` Jaegeuk Kim
  2016-04-26  1:57 ` [PATCH 1/3] resize.f2fs: support to expand partition size He YunLei
  2016-04-26  2:17 ` He YunLei
  3 siblings, 0 replies; 7+ messages in thread
From: Jaegeuk Kim @ 2016-04-26  0:28 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: Jaegeuk Kim

This patch adds a script to test sload.f2fs, defrag.f2fs, and resize.f2fs.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 scripts/verify.sh | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 137 insertions(+)
 create mode 100755 scripts/verify.sh

diff --git a/scripts/verify.sh b/scripts/verify.sh
new file mode 100755
index 0000000..8a22a2d
--- /dev/null
+++ b/scripts/verify.sh
@@ -0,0 +1,137 @@
+#!/bin/bash
+
+IMG=../test.img
+TMP=/tmp/res
+XFSTESTS=~/xfstests
+TESTS="4 5 8 11 16 25 32 55 64"
+
+TARGET=./testdir
+MNT=/mnt/resize
+
+mkdir $TARGET 2>/dev/null
+mkdir $MNT 2>/dev/null
+
+umount $TARGET
+umount $MNT
+
+_check_out()
+{
+	if [ $1 -ne 0 ]; then
+		grep ASSERT $TMP
+		echo FAIL RETURN $1
+		exit
+	fi
+}
+
+_get_sec()
+{
+	echo $(($1*1024*1024*1024/512))
+}
+
+_mkfs()
+{
+	echo "========== Initialize $1 GB ============"
+	mkfs.f2fs $IMG `_get_sec $1` | grep sectors
+}
+
+_mount()
+{
+	echo "========== mount to $1 ================="
+	mount -t f2fs -o loop,discard,inline_data,inline_xattr $IMG $1 2>&1
+	_check_out $?
+}
+
+_fsck()
+{
+	echo "========== fsck.f2fs ==================="
+	fsck.f2fs $IMG -t 2>&1 >$TMP
+	_check_out $?
+	grep FSCK $TMP
+}
+
+_fsstress()
+{
+	echo "========== fsstress $1 ================="
+	$XFSTESTS/ltp/fsstress -x "echo 3 > /proc/sys/vm/drop_caches && sleep 1" -X 1 -r -f fsync=8 -f sync=0 -f write=8 -f dwrite=2 -f truncate=6 -f allocsp=0 -f bulkstat=0 -f bulkstat1=0 -f freesp=0 -f zero=1 -f collapse=1 -f insert=1 -f resvsp=0 -f unresvsp=0 -S t -p 10 -n $2 -d $1 >/dev/null
+}
+
+_resize()
+{
+	echo "========== resize.f2fs $1 GB ==========="
+	resize.f2fs -t `_get_sec $1` $IMG 2>&1 >$TMP
+	_check_out $?
+	_fsck
+}
+
+_resize_tests()
+{
+	for i in $TESTS
+	do
+		if [ $i -ge $1 ]; then
+			_resize $i
+		fi
+	done
+}
+
+_sload()
+{
+	echo "========== sload $1 ===================="
+	sload.f2fs -f $1 $IMG 2>&1
+	_check_out $?
+}
+
+from_mount()
+{
+	echo ""
+	echo " ****  $1 GB to $2 GB with $3 *** "
+	_mkfs $1
+	_mount $3
+	_fsstress $3 10000
+	umount $3
+	_fsck
+	_resize_tests $2
+}
+
+from_sload()
+{
+	echo ""
+	echo " ****  $1 GB to $2 GB with $3 *** "
+
+	_mkfs $1
+	_sload $3
+	_fsck
+
+	_mount $MNT
+	_fsstress $MNT 10000
+	umount $MNT
+	_fsck
+
+	_resize_tests $2
+
+	_mount $MNT
+	_fsstress $MNT 10000
+	umount $MNT
+	_fsck
+}
+
+test_all()
+{
+	for i in $TESTS
+	do
+		for j in $TESTS
+		do
+			if [ $i -lt $j ]; then
+				$1 $i $j $2
+			fi
+		done
+	done
+}
+
+test_all from_sload ~/grub
+
+rm -rf $TARGET/*
+_fsstress $TARGET 5000
+test_all from_sload $TARGET
+rm -rf $TARGET 2>/dev/null
+
+test_all from_mount $MNT
-- 
2.6.3


------------------------------------------------------------------------------
Find and fix application performance issues faster with Applications Manager
Applications Manager provides deep performance insights into multiple tiers of
your business applications. It resolves application problems quickly and
reduces your MTTR. Get your free trial!
https://ad.doubleclick.net/ddm/clk/302982198;130105516;z

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/3] resize.f2fs: support to expand partition size
  2016-04-26  0:28 [PATCH 1/3] resize.f2fs: support to expand partition size Jaegeuk Kim
  2016-04-26  0:28 ` [PATCH 2/3] sload.f2fs: support loading files into partition directly Jaegeuk Kim
  2016-04-26  0:28 ` [PATCH 3/3] script: add simple test script Jaegeuk Kim
@ 2016-04-26  1:57 ` He YunLei
  2016-04-26  2:17   ` Jaegeuk Kim
  2016-04-26  2:17 ` He YunLei
  3 siblings, 1 reply; 7+ messages in thread
From: He YunLei @ 2016-04-26  1:57 UTC (permalink / raw)
  To: Jaegeuk Kim, linux-f2fs-devel

hi, kim

On 2016/4/26 8:28, Jaegeuk Kim wrote:
> Now user can expand existing partition with resize.f2fs.
> Currently, it doesn't support shrink an image.
>
> For example,
>   # resize.f2fs -t [# of sectors] [image]
>
> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> ---
>   fsck/Makefile.am  |   4 +-
>   fsck/f2fs.h       |   4 +-
>   fsck/fsck.h       |   2 +
>   fsck/main.c       |  64 ++++++
>   fsck/resize.c     | 578 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   include/f2fs_fs.h |  17 ++
>   man/Makefile.am   |   2 +-
>   man/defrag.f2fs.8 |   3 +-
>   man/dump.f2fs.8   |   3 +-
>   man/fsck.f2fs.8   |   3 +-
>   man/mkfs.f2fs.8   |   3 +-
>   man/resize.f2fs.8 |  49 +++++
>   12 files changed, 724 insertions(+), 8 deletions(-)
>   create mode 100644 fsck/resize.c
>   create mode 100644 man/resize.f2fs.8
>
> diff --git a/fsck/Makefile.am b/fsck/Makefile.am
> index 73df884..3586625 100644
> --- a/fsck/Makefile.am
> +++ b/fsck/Makefile.am
> @@ -3,9 +3,11 @@
>   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 defrag.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	\
> +		resize.c
>   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
> +	ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/resize.f2fs
> diff --git a/fsck/f2fs.h b/fsck/f2fs.h
> index a618b47..4b3c666 100644
> --- a/fsck/f2fs.h
> +++ b/fsck/f2fs.h
> @@ -323,9 +323,9 @@ static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type)
>   #define segno_in_journal(jnl, i)        (jnl->sit_j.entries[i].segno)
>
>   #define SIT_ENTRY_OFFSET(sit_i, segno)                                  \
> -	(segno % sit_i->sents_per_block)
> +	((segno) % sit_i->sents_per_block)
>   #define SIT_BLOCK_OFFSET(sit_i, segno)                                  \
> -	(segno / SIT_ENTRY_PER_BLOCK)
> +	((segno) / SIT_ENTRY_PER_BLOCK)
>   #define TOTAL_SEGS(sbi) (SM_I(sbi)->main_segments)
>
>   static inline bool IS_VALID_NID(struct f2fs_sb_info *sbi, u32 nid)
> diff --git a/fsck/fsck.h b/fsck/fsck.h
> index 5fc214e..db11b41 100644
> --- a/fsck/fsck.h
> +++ b/fsck/fsck.h
> @@ -179,4 +179,6 @@ extern int dump_info_from_blkaddr(struct f2fs_sb_info *, u32);
>   /* defrag.c */
>   int f2fs_defragment(struct f2fs_sb_info *, u64, u64, u64, int);
>
> +/* resize.c */
> +int f2fs_resize(struct f2fs_sb_info *);
>   #endif /* _FSCK_H_ */
> diff --git a/fsck/main.c b/fsck/main.c
> index 6058c4d..885e2cf 100644
> --- a/fsck/main.c
> +++ b/fsck/main.c
> @@ -52,6 +52,15 @@ void defrag_usage()
>   	exit(1);
>   }
>
> +void resize_usage()
> +{
> +	MSG(0, "\nUsage: resize.f2fs [options] device\n");
> +	MSG(0, "[options]:\n");
> +	MSG(0, "  -d debug level [default:0]\n");
> +	MSG(0, "  -t target sectors [default: device size]\n");
> +	exit(1);
> +}
> +
>   void f2fs_parse_options(int argc, char *argv[])
>   {
>   	int option = 0;
> @@ -203,6 +212,34 @@ void f2fs_parse_options(int argc, char *argv[])
>   			}
>   			ASSERT(ret >= 0);
>   		}
> +	} else if (!strcmp("resize.f2fs", prog)) {
> +		const char *option_string = "d:t:";
> +
> +		config.func = RESIZE;
> +		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 't':
> +				if (strncmp(optarg, "0x", 2))
> +					ret = sscanf(optarg, "%"PRIu64"",
> +							&config.target_sectors);
> +				else
> +					ret = sscanf(optarg, "%"PRIx64"",
> +							&config.target_sectors);
> +				break;
> +			default:
> +				MSG(0, "\tError: Unknown option %c\n", option);
> +				resize_usage();
> +				break;
> +			}
> +			ASSERT(ret >= 0);
> +		}
>   	}
>
>   	if ((optind + 1) != argc) {
> @@ -213,6 +250,8 @@ void f2fs_parse_options(int argc, char *argv[])
>   			dump_usage();
>   		else if (config.func == DEFRAG)
>   			defrag_usage();
> +		else if (config.func == RESIZE)
> +			resize_usage();
>   	}
>   	config.device_name = argv[optind];
>   }
> @@ -345,6 +384,27 @@ out_range:
>   	return -1;
>   }
>
> +static int do_resize(struct f2fs_sb_info *sbi)
> +{
> +	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
> +
> +	if (!config.target_sectors)
> +		config.target_sectors = config.total_sectors;
> +
> +	if (config.target_sectors > config.total_sectors) {
> +		ASSERT_MSG("Out-of-range Target=0x%"PRIx64" / 0x%"PRIx64"",
> +				config.target_sectors, config.total_sectors);
> +		return -1;
> +	}
> +
> +	if (config.target_sectors ==
> +			(get_sb(block_count) << get_sb(log_sectors_per_block))) {
> +		ASSERT_MSG("Nothing to resize; it's same");
> +		return -1;
> +	}
> +	return f2fs_resize(sbi);
> +}
> +
>   int main(int argc, char **argv)
>   {
>   	struct f2fs_sb_info *sbi;
> @@ -395,6 +455,10 @@ fsck_again:
>   		if (ret)
>   			goto out_err;
>   		break;
> +	case RESIZE:
> +		if (do_resize(sbi))
> +			goto out_err;
> +		break;
>   	}
>
>   	f2fs_do_umount(sbi);
> diff --git a/fsck/resize.c b/fsck/resize.c
> new file mode 100644
> index 0000000..0803024
> --- /dev/null
> +++ b/fsck/resize.c
> @@ -0,0 +1,578 @@
> +/**
> + * resize.c
> + *
> + * Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
> + *
> + * 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 get_new_sb(struct f2fs_sb_info *sbi, struct f2fs_super_block *sb)
> +{
> +	u_int32_t zone_size_bytes, zone_align_start_offset;
> +	u_int32_t blocks_for_sit, blocks_for_nat, blocks_for_ssa;
> +	u_int32_t sit_segments, diff, total_meta_segments;
> +	u_int32_t total_valid_blks_available;
> +	u_int32_t sit_bitmap_size, max_sit_bitmap_size;
> +	u_int32_t max_nat_bitmap_size, max_nat_segments;
> +	u_int32_t segment_size_bytes = 1 << (get_sb(log_blocksize) +
> +					get_sb(log_blocks_per_seg));
> +	u_int32_t blks_per_seg = 1 << get_sb(log_blocks_per_seg);
> +	u_int32_t segs_per_zone = get_sb(segs_per_sec) * get_sb(secs_per_zone);
> +
> +	set_sb(block_count, config.target_sectors >>
> +				get_sb(log_sectors_per_block));
> +
> +	zone_size_bytes = segment_size_bytes * segs_per_zone;
> +	zone_align_start_offset =
> +		(config.start_sector * config.sector_size +
> +		2 * F2FS_BLKSIZE + zone_size_bytes - 1) /
> +		zone_size_bytes * zone_size_bytes -
> +		config.start_sector * config.sector_size;
> +
> +	set_sb(segment_count, (config.target_sectors * config.sector_size -
> +				zone_align_start_offset) / segment_size_bytes /
> +				config.segs_per_sec * config.segs_per_sec);
> +
> +	blocks_for_sit = ALIGN(get_sb(segment_count), SIT_ENTRY_PER_BLOCK);
> +	sit_segments = SEG_ALIGN(blocks_for_sit);
> +	set_sb(segment_count_sit, sit_segments * 2);
> +	set_sb(nat_blkaddr, get_sb(sit_blkaddr) +
> +				get_sb(segment_count_sit) * blks_per_seg);
> +
> +	total_valid_blks_available = (get_sb(segment_count) -
> +			(get_sb(segment_count_ckpt) +
> +			get_sb(segment_count_sit))) * blks_per_seg;
> +	blocks_for_nat = ALIGN(total_valid_blks_available, NAT_ENTRY_PER_BLOCK);
> +	set_sb(segment_count_nat, SEG_ALIGN(blocks_for_nat));
> +
> +	sit_bitmap_size = ((get_sb(segment_count_sit) / 2) <<
> +				get_sb(log_blocks_per_seg)) / 8;
> +	if (sit_bitmap_size > MAX_SIT_BITMAP_SIZE)
> +		max_sit_bitmap_size = MAX_SIT_BITMAP_SIZE;
> +	else
> +		max_sit_bitmap_size = sit_bitmap_size;
> +
> +	/*
> +	 * It should be reserved minimum 1 segment for nat.
> +	 * When sit is too large, we should expand cp area. It requires more pages for cp.
> +	 */
> +	if (max_sit_bitmap_size >
> +			(CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 65)) {
> +		max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1;
> +		set_sb(cp_payload, F2FS_BLK_ALIGN(max_sit_bitmap_size));
> +	} else {
> +		max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1
> +			- max_sit_bitmap_size;
> +		set_sb(cp_payload, 0);
> +	}
> +
> +	max_nat_segments = (max_nat_bitmap_size * 8) >>
> +					get_sb(log_blocks_per_seg);
> +
> +	if (get_sb(segment_count_nat) > max_nat_segments)
> +		set_sb(segment_count_nat, max_nat_segments);
> +
> +	set_sb(segment_count_nat, get_sb(segment_count_nat) * 2);
> +
> +	set_sb(ssa_blkaddr, get_sb(nat_blkaddr) +
> +				get_sb(segment_count_nat) * blks_per_seg);
> +
> +	total_valid_blks_available = (get_sb(segment_count) -
> +			(get_sb(segment_count_ckpt) +
> +			get_sb(segment_count_sit) +
> +			get_sb(segment_count_nat))) * blks_per_seg;
> +
> +	blocks_for_ssa = total_valid_blks_available / blks_per_seg + 1;
> +
> +	set_sb(segment_count_ssa, SEG_ALIGN(blocks_for_ssa));
> +
> +	total_meta_segments = get_sb(segment_count_ckpt) +
> +		get_sb(segment_count_sit) +
> +		get_sb(segment_count_nat) +
> +		get_sb(segment_count_ssa);
> +
> +	diff = total_meta_segments % segs_per_zone;
> +	if (diff)
> +		set_sb(segment_count_ssa, get_sb(segment_count_ssa) +
> +			(segs_per_zone - diff));
> +
> +	set_sb(main_blkaddr, get_sb(ssa_blkaddr) + get_sb(segment_count_ssa) *
> +			 blks_per_seg);
> +
> +	set_sb(segment_count_main, get_sb(segment_count) -
> +			(get_sb(segment_count_ckpt) +
> +			 get_sb(segment_count_sit) +
> +			 get_sb(segment_count_nat) +
> +			 get_sb(segment_count_ssa)));
> +
> +	set_sb(section_count, get_sb(segment_count_main) /
> +						get_sb(segs_per_sec));
> +
> +	set_sb(segment_count_main, get_sb(section_count) *
> +						get_sb(segs_per_sec));
> +
> +	/* Let's determine the best reserved and overprovisioned space */
> +	config.new_overprovision = get_best_overprovision(sb);
> +	config.new_reserved_segments =
> +		(2 * (100 / config.new_overprovision + 1) + 6) *
> +						get_sb(segs_per_sec);
> +
> +	if ((get_sb(segment_count_main) - 2) < config.new_reserved_segments ||
> +		get_sb(segment_count_main) * blks_per_seg >
> +						get_sb(block_count)) {
> +		MSG(0, "\tError: Device size is not sufficient for F2FS volume,\
> +			more segment needed =%u",
> +			config.new_reserved_segments -
> +			(get_sb(segment_count_main) - 2));
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static void migrate_main(struct f2fs_sb_info *sbi,
> +		struct f2fs_super_block *new_sb, unsigned int offset)
> +{
> +	void *raw = calloc(BLOCK_SZ, 1);
> +	struct seg_entry *se;
> +	block_t from, to;
> +	int i, j, ret;
> +	struct f2fs_summary sum;
> +
> +	ASSERT(raw != NULL);
> +
> +	for (i = TOTAL_SEGS(sbi); i >= 0; i--) {
> +		se = get_seg_entry(sbi, i);
> +		if (!se->valid_blocks)
> +			continue;
> +
> +		for (j = sbi->blocks_per_seg - 1; j >= 0; j--) {
> +			if (!f2fs_test_bit(j, (const char *)se->cur_valid_map))
> +				continue;
> +
> +			from = START_BLOCK(sbi, i) + j;
> +			ret = dev_read_block(raw, from);
> +			ASSERT(ret >= 0);
> +
> +			to = from + offset;
> +			ret = dev_write_block(raw, to);
> +			ASSERT(ret >= 0);
> +
> +			get_sum_entry(sbi, from, &sum);
> +
> +			if (IS_DATASEG(se->type))
> +				update_data_blkaddr(sbi, le32_to_cpu(sum.nid),
> +					le16_to_cpu(sum.ofs_in_node), to);
> +			else
> +				update_nat_blkaddr(sbi, 0,
> +						le32_to_cpu(sum.nid), to);
> +		}
> +	}
> +	free(raw);
> +	DBG(0, "Info: Done to migrate data and node blocks\n");
> +}
> +
> +static void move_ssa(struct f2fs_sb_info *sbi, unsigned int segno,
> +					block_t new_sum_blk_addr)
> +{
> +	struct f2fs_summary_block *sum_blk;
> +	int type;
> +
> +	sum_blk = get_sum_block(sbi, segno, &type);
> +	if (type < SEG_TYPE_MAX) {
> +		int ret;
> +
> +		ret = dev_write_block(sum_blk, new_sum_blk_addr);
> +		ASSERT(ret >= 0);
> +		DBG(1, "Write summary block: (%d) segno=%x/%x --> (%d) %x\n",
> +				type, segno, GET_SUM_BLKADDR(sbi, segno),
> +				IS_SUM_NODE_SEG(sum_blk->footer),
> +				new_sum_blk_addr);
> +	}
> +	if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
> +			type == SEG_TYPE_MAX) {
> +		free(sum_blk);
> +	}
> +	DBG(1, "Info: Done to migrate SSA blocks\n");
> +}
> +
> +static void migrate_ssa(struct f2fs_sb_info *sbi,
> +		struct f2fs_super_block *new_sb, unsigned int offset)
> +{
> +	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
> +	block_t old_sum_blkaddr = get_sb(ssa_blkaddr);
> +	block_t new_sum_blkaddr = get_newsb(ssa_blkaddr);
> +	int segno;
> +
> +	if (new_sum_blkaddr < old_sum_blkaddr + offset) {
> +		for (segno = offset; segno < TOTAL_SEGS(sbi); segno++)
> +			move_ssa(sbi, segno, new_sum_blkaddr + segno - offset);
> +	} else {
> +		for (segno = TOTAL_SEGS(sbi) - 1; segno >= offset; segno--)
> +			move_ssa(sbi, segno, new_sum_blkaddr + segno - offset);
> +	}
> +
> +	DBG(0, "Info: Done to migrate SSA blocks\n");
> +}
> +
> +static int shrink_nats(struct f2fs_sb_info *sbi,
> +				struct f2fs_super_block *new_sb)
> +{
> +	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
> +	struct f2fs_nm_info *nm_i = NM_I(sbi);
> +	block_t old_nat_blkaddr = get_sb(nat_blkaddr);
> +	unsigned int nat_blocks;
> +	void *nat_block, *zero_block;
> +	int nid, ret, new_max_nid;
> +	pgoff_t block_off;
> +	pgoff_t block_addr;
> +	int seg_off;
> +
> +	nat_block = malloc(BLOCK_SZ);
> +	ASSERT(nat_block);
> +	zero_block = calloc(BLOCK_SZ, 1);
> +	ASSERT(zero_block);
> +
> +	nat_blocks = get_newsb(segment_count_nat) >> 1;
> +	nat_blocks = nat_blocks << get_sb(log_blocks_per_seg);
> +	new_max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks;
> +
> +	for (nid = nm_i->max_nid - 1; nid > new_max_nid; nid -= NAT_ENTRY_PER_BLOCK) {
> +		block_off = nid / NAT_ENTRY_PER_BLOCK;
> +		seg_off = block_off >> sbi->log_blocks_per_seg;
> +		block_addr = (pgoff_t)(old_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);
> +
> +		if (memcmp(zero_block, nat_block, BLOCK_SZ)) {
> +			ret = -1;
> +			goto not_avail;
> +		}
> +	}
> +	ret = 0;
> +	nm_i->max_nid = new_max_nid;
> +not_avail:
> +	free(nat_block);
> +	free(zero_block);
> +	return ret;
> +}
> +
> +static void migrate_nat(struct f2fs_sb_info *sbi,
> +			struct f2fs_super_block *new_sb)
> +{
> +	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
> +	struct f2fs_nm_info *nm_i = NM_I(sbi);
> +	block_t old_nat_blkaddr = get_sb(nat_blkaddr);
> +	block_t new_nat_blkaddr = get_newsb(nat_blkaddr);
> +	unsigned int nat_blocks;
> +	void *nat_block;
> +	int nid, ret, new_max_nid;
> +	pgoff_t block_off;
> +	pgoff_t block_addr;
> +	int seg_off;
> +
> +	nat_block = malloc(BLOCK_SZ);
> +	ASSERT(nat_block);
> +
> +	for (nid = nm_i->max_nid - 1; nid >= 0; nid -= NAT_ENTRY_PER_BLOCK) {
> +		block_off = nid / NAT_ENTRY_PER_BLOCK;
> +		seg_off = block_off >> sbi->log_blocks_per_seg;
> +		block_addr = (pgoff_t)(old_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);
> +
> +		block_addr = (pgoff_t)(new_nat_blkaddr +
> +				(seg_off << sbi->log_blocks_per_seg << 1) +
> +				(block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
> +
> +		/* new bitmap should be zeros */
> +		ret = dev_write_block(nat_block, block_addr);
> +		ASSERT(ret >= 0);
> +	}
> +	/* zero out newly assigned nids */
> +	memset(nat_block, 0, BLOCK_SZ);
> +	nat_blocks = get_newsb(segment_count_nat) >> 1;
> +	nat_blocks = nat_blocks << get_sb(log_blocks_per_seg);
> +	new_max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks;
> +
> +	DBG(1, "Write NAT block: %x->%x, max_nid=%x->%x\n",
> +			old_nat_blkaddr, new_nat_blkaddr,
> +			get_sb(segment_count_nat),
> +			get_newsb(segment_count_nat));
> +
> +	for (nid = nm_i->max_nid; nid < new_max_nid;
> +				nid += NAT_ENTRY_PER_BLOCK) {
> +		block_off = nid / NAT_ENTRY_PER_BLOCK;
> +		seg_off = block_off >> sbi->log_blocks_per_seg;
> +		block_addr = (pgoff_t)(new_nat_blkaddr +
> +				(seg_off << sbi->log_blocks_per_seg << 1) +
> +				(block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
> +		ret = dev_write_block(nat_block, block_addr);
> +		ASSERT(ret >= 0);
> +		DBG(1, "Write NAT: %lx\n", block_addr);
> +	}
> +	DBG(0, "Info: Done to migrate NAT blocks\n");
> +}
> +
> +static void migrate_sit(struct f2fs_sb_info *sbi,
> +		struct f2fs_super_block *new_sb, unsigned int offset)
> +{
> +	struct sit_info *sit_i = SIT_I(sbi);
> +	unsigned int ofs = 0, pre_ofs = 0;
> +	unsigned int segno, index;
> +	struct f2fs_sit_block *sit_blk = calloc(BLOCK_SZ, 1);
> +	block_t sit_blks = get_newsb(segment_count_sit) <<
> +						(sbi->log_blocks_per_seg - 1);
> +	struct seg_entry *se;
> +	block_t blk_addr = 0;
> +	int ret;
> +
> +	ASSERT(sit_blk);
> +
> +	/* initialize with zeros */
> +	for (index = 0; index < sit_blks; index++) {
> +		ret = dev_write_block(sit_blk, get_newsb(sit_blkaddr) + index);
> +		ASSERT(ret >= 0);
> +		DBG(1, "Write zero sit: %x\n", get_newsb(sit_blkaddr) + index);
> +	}
> +
> +	for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) {
> +		struct f2fs_sit_entry *sit;
> +
> +		se = get_seg_entry(sbi, segno);
> +		if (segno < offset) {
> +			ASSERT(se->valid_blocks == 0);
> +			continue;
> +		}
> +
> +		ofs = SIT_BLOCK_OFFSET(sit_i, segno - offset);
> +
> +		if (ofs != pre_ofs) {
> +			blk_addr = get_newsb(sit_blkaddr) + pre_ofs;
> +			ret = dev_write_block(sit_blk, blk_addr);
> +			ASSERT(ret >= 0);
> +			DBG(1, "Write valid sit: %x\n", blk_addr);
> +
> +			pre_ofs = ofs;
> +			memset(sit_blk, 0, BLOCK_SZ);
> +		}
> +
> +		sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno - offset)];
> +		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);
> +	}
> +	blk_addr = get_newsb(sit_blkaddr) + ofs;
> +	ret = dev_write_block(sit_blk, blk_addr);
> +	DBG(1, "Write valid sit: %x\n", blk_addr);
> +	ASSERT(ret >= 0);
> +
> +	free(sit_blk);
> +	DBG(0, "Info: Done to migrate SIT blocks\n");
> +}
> +
> +static void rebuild_checkpoint(struct f2fs_sb_info *sbi,
> +			struct f2fs_super_block *new_sb, unsigned int offset)
> +{
> +	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
> +	struct f2fs_checkpoint *new_cp;
> +	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
> +	unsigned int free_segment_count, new_segment_count;
> +	block_t new_cp_blks = 1 + get_newsb(cp_payload);
> +	block_t orphan_blks = 0;
> +	block_t new_cp_blk_no, old_cp_blk_no;
> +	u_int32_t crc = 0;
> +	void *buf;
> +	int i, ret;
> +
> +	new_cp = calloc(new_cp_blks * BLOCK_SZ, 1);
> +	ASSERT(new_cp);
> +
> +	buf = malloc(BLOCK_SZ);
> +	ASSERT(buf);
> +
> +	/* ovp / free segments */
> +	set_cp(overprov_segment_count, config.new_overprovision);
> +	set_cp(rsvd_segment_count, config.new_reserved_segments);

maybe something wrong here:
	set_cp(rsvd_segment_count, config.new_reserved_segments);
	set_cp(overprov_segment_count, (get_newsb(segment_count_main) -
			get_cp(rsvd_segment_count)) *
			config.new_overprovision / 100);
	set_cp(overprov_segment_count, get_cp(overprov_segment_count) +
						get_cp(rsvd_segment_count));

Thanks.

> +	free_segment_count = get_cp(free_segment_count);
> +	new_segment_count = get_newsb(segment_count_main) -
> +					get_sb(segment_count_main);
> +
> +	set_cp(free_segment_count, free_segment_count + new_segment_count);
> +	set_cp(user_block_count, ((get_sb(segment_count_main) -
> +			get_cp(overprov_segment_count)) * config.blks_per_seg));
> +
> +	if (is_set_ckpt_flags(cp, CP_ORPHAN_PRESENT_FLAG))
> +		orphan_blks = __start_sum_addr(sbi) - 1;
> +
> +	set_cp(cp_pack_start_sum, 1 + get_newsb(cp_payload));
> +	set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_newsb(cp_payload));
> +
> +	/* cur->segno - offset */
> +	for (i = 0; i < NO_CHECK_TYPE; i++) {
> +		if (i < CURSEG_HOT_NODE) {
> +			set_cp(cur_data_segno[i],
> +					CURSEG_I(sbi, i)->segno - offset);
> +		} else {
> +			int n = i - CURSEG_HOT_NODE;
> +
> +			set_cp(cur_node_segno[n],
> +					CURSEG_I(sbi, i)->segno - offset);
> +		}
> +	}
> +
> +	/* sit / nat ver bitmap bytesize */
> +	set_cp(sit_ver_bitmap_bytesize,
> +			((get_newsb(segment_count_sit) / 2) <<
> +			get_newsb(log_blocks_per_seg)) / 8);
> +	set_cp(nat_ver_bitmap_bytesize,
> +			((get_newsb(segment_count_nat) / 2) <<
> +			get_newsb(log_blocks_per_seg)) / 8);
> +
> +	memcpy(new_cp, cp, (unsigned char *)cp->sit_nat_version_bitmap -
> +						(unsigned char *)cp);
> +
> +	crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, new_cp, CHECKSUM_OFFSET);
> +	*((__le32 *)((unsigned char *)new_cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc);
> +
> +	/* Write a new checkpoint in the other set */
> +	new_cp_blk_no = old_cp_blk_no = get_sb(cp_blkaddr);
> +	if (sbi->cur_cp == 2)
> +		old_cp_blk_no += 1 << get_sb(log_blocks_per_seg);
> +	else
> +		new_cp_blk_no += 1 << get_sb(log_blocks_per_seg);
> +
> +	/* write first cp */
> +	ret = dev_write_block(new_cp, new_cp_blk_no++);
> +	ASSERT(ret >= 0);
> +
> +	memset(buf, 0, BLOCK_SZ);
> +	for (i = 0; i < get_newsb(cp_payload); i++) {
> +		ret = dev_write_block(buf, new_cp_blk_no++);
> +		ASSERT(ret >= 0);
> +	}
> +
> +	for (i = 0; i < orphan_blks; i++) {
> +		block_t orphan_blk_no = old_cp_blk_no + 1 + get_sb(cp_payload);
> +
> +		ret = dev_read_block(buf, orphan_blk_no++);
> +		ASSERT(ret >= 0);
> +
> +		ret = dev_write_block(buf, new_cp_blk_no++);
> +		ASSERT(ret >= 0);
> +	}
> +
> +	/* update summary blocks having nullified journal entries */
> +	for (i = 0; i < NO_CHECK_TYPE; i++) {
> +		struct curseg_info *curseg = CURSEG_I(sbi, i);
> +
> +		ret = dev_write_block(curseg->sum_blk, new_cp_blk_no++);
> +		ASSERT(ret >= 0);
> +	}
> +
> +	/* write the last cp */
> +	ret = dev_write_block(new_cp, new_cp_blk_no++);
> +	ASSERT(ret >= 0);
> +
> +	/* disable old checkpoint */
> +	memset(buf, 0, BLOCK_SZ);
> +	ret = dev_write_block(buf, old_cp_blk_no);
> +	ASSERT(ret >= 0);
> +
> +	free(buf);
> +	free(new_cp);
> +	DBG(0, "Info: Done to rebuild checkpoint blocks\n");
> +}
> +
> +static void rebuild_superblock(struct f2fs_sb_info *sbi,
> +				struct f2fs_super_block *new_sb)
> +{
> +	int index, ret;
> +	u_int8_t *buf;
> +
> +	buf = calloc(BLOCK_SZ, 1);
> +
> +	memcpy(buf + F2FS_SUPER_OFFSET, new_sb, sizeof(*new_sb));
> +	for (index = 0; index < 2; index++) {
> +		ret = dev_write_block(buf, index);
> +		ASSERT(ret >= 0);
> +	}
> +	free(buf);
> +	DBG(0, "Info: Done to rebuild superblock\n");
> +}
> +
> +int f2fs_resize(struct f2fs_sb_info *sbi)
> +{
> +	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
> +	struct f2fs_super_block new_sb_raw;
> +	struct f2fs_super_block *new_sb = &new_sb_raw;
> +	block_t end_blkaddr, old_main_blkaddr, new_main_blkaddr;
> +	unsigned int offset, offset_seg;
> +	int err = -1;
> +
> +	/* flush NAT/SIT journal entries */
> +	flush_journal_entries(sbi);
> +
> +	memcpy(new_sb, F2FS_RAW_SUPER(sbi), sizeof(*new_sb));
> +	if (get_new_sb(sbi, new_sb))
> +		return -1;
> +
> +	/* check nat availability */
> +	if (get_sb(segment_count_nat) > get_newsb(segment_count_nat)) {
> +		err = shrink_nats(sbi, new_sb);
> +		if (err) {
> +			MSG(0, "\tError: Failed to shrink NATs\n");
> +			return err;
> +		}
> +	}
> +
> +	config.dbg_lv = 1;
> +	print_raw_sb_info(sb);
> +	print_raw_sb_info(new_sb);
> +	config.dbg_lv = 0;
> +
> +	old_main_blkaddr = get_sb(main_blkaddr);
> +	new_main_blkaddr = get_newsb(main_blkaddr);
> +	offset = new_main_blkaddr - old_main_blkaddr;
> +	end_blkaddr = (get_sb(segment_count) << get_sb(log_blocks_per_seg)) +
> +						get_sb(main_blkaddr);
> +
> +	if (old_main_blkaddr > new_main_blkaddr) {
> +		MSG(0, "\tError: Support resize to expand only\n");
> +		return -1;
> +	}
> +
> +	err = -EAGAIN;
> +	offset_seg = offset >> get_sb(log_blocks_per_seg);
> +
> +	if (new_main_blkaddr < end_blkaddr) {
> +		err = f2fs_defragment(sbi, old_main_blkaddr, offset,
> +						new_main_blkaddr, 0);
> +		if (err)
> +			MSG(0, "Skip defragement\n");
> +	}
> +	/* move whole data region */
> +	if (err)
> +		migrate_main(sbi, new_sb, offset);
> +
> +	migrate_ssa(sbi, new_sb, offset_seg);
> +	migrate_nat(sbi, new_sb);
> +	migrate_sit(sbi, new_sb, offset_seg);
> +	rebuild_checkpoint(sbi, new_sb, offset_seg);
> +	rebuild_superblock(sbi, new_sb);
> +	return 0;
> +}
> diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
> index 330cbe5..99798d0 100644
> --- a/include/f2fs_fs.h
> +++ b/include/f2fs_fs.h
> @@ -221,18 +221,22 @@ enum f2fs_config_func {
>   	FSCK,
>   	DUMP,
>   	DEFRAG,
> +	RESIZE,
>   };
>
>   struct f2fs_configuration {
>   	u_int32_t sector_size;
>   	u_int32_t reserved_segments;
> +	u_int32_t new_reserved_segments;
>   	double overprovision;
> +	double new_overprovision;
>   	u_int32_t cur_seg[6];
>   	u_int32_t segs_per_sec;
>   	u_int32_t secs_per_zone;
>   	u_int32_t segs_per_zone;
>   	u_int32_t start_sector;
>   	u_int64_t total_sectors;
> +	u_int64_t target_sectors;
>   	u_int32_t sectors_per_blk;
>   	u_int32_t blks_per_seg;
>   	__u8 init_version[VERSION_LEN + 1];
> @@ -277,6 +281,9 @@ struct f2fs_configuration {
>   #define get_sb_le64(member)			le64_to_cpu(sb->member)
>   #define get_sb_le32(member)			le32_to_cpu(sb->member)
>   #define get_sb_le16(member)			le16_to_cpu(sb->member)
> +#define get_newsb_le64(member)			le64_to_cpu(new_sb->member)
> +#define get_newsb_le32(member)			le32_to_cpu(new_sb->member)
> +#define get_newsb_le16(member)			le16_to_cpu(new_sb->member)
>
>   #define set_sb(member, val)	\
>   			do {						\
> @@ -298,6 +305,16 @@ struct f2fs_configuration {
>   				} 					\
>   				t; \
>   			})
> +#define get_newsb(member)		\
> +			({						\
> +				typeof(new_sb->member) t;		\
> +				switch (sizeof(t)) {			\
> +				case 8: t = get_newsb_le64(member); break; \
> +				case 4: t = get_newsb_le32(member); break; \
> +				case 2: t = get_newsb_le16(member); break; \
> +				} 					\
> +				t; \
> +			})
>
>   #define set_cp_le64(member, val)		(cp->member = cpu_to_le64(val))
>   #define set_cp_le32(member, val)		(cp->member = cpu_to_le32(val))
> diff --git a/man/Makefile.am b/man/Makefile.am
> index 256f3c3..6c04de7 100644
> --- a/man/Makefile.am
> +++ b/man/Makefile.am
> @@ -1,3 +1,3 @@
>   ## Makefile.am
>
> -dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8
> +dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8 resize.f2fs.8
> diff --git a/man/defrag.f2fs.8 b/man/defrag.f2fs.8
> index e18e69f..8c709a7 100644
> --- a/man/defrag.f2fs.8
> +++ b/man/defrag.f2fs.8
> @@ -71,4 +71,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
>   .SH SEE ALSO
>   .BR mkfs.f2fs(8),
>   .BR dump.f2fs(8),
> -.BR fsck.f2fs(8).
> +.BR fsck.f2fs(8),
> +.BR resize.f2fs(8).
> diff --git a/man/dump.f2fs.8 b/man/dump.f2fs.8
> index ca5f511..dc1d806 100644
> --- a/man/dump.f2fs.8
> +++ b/man/dump.f2fs.8
> @@ -65,4 +65,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
>   .SH SEE ALSO
>   .BR mkfs.f2fs(8),
>   .BR fsck.f2fs(8),
> -.BR defrag.f2fs(8).
> +.BR defrag.f2fs(8),
> +.BR resize.f2fs(8).
> diff --git a/man/fsck.f2fs.8 b/man/fsck.f2fs.8
> index 0ffe6e1..22457c5 100644
> --- a/man/fsck.f2fs.8
> +++ b/man/fsck.f2fs.8
> @@ -64,4 +64,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
>   .SH SEE ALSO
>   .BR mkfs.f2fs(8),
>   .BR dump.f2fs(8),
> -.BR defrag.f2fs(8).
> +.BR defrag.f2fs(8),
> +.BR resize.f2fs(8).
> diff --git a/man/mkfs.f2fs.8 b/man/mkfs.f2fs.8
> index cdbde96..b767c0c 100644
> --- a/man/mkfs.f2fs.8
> +++ b/man/mkfs.f2fs.8
> @@ -94,4 +94,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
>   .BR mkfs (8),
>   .BR fsck.f2fs(8),
>   .BR dump.f2fs(8),
> -.BR defrag.f2fs(8).
> +.BR defrag.f2fs(8),
> +.BR resize.f2fs(8).
> diff --git a/man/resize.f2fs.8 b/man/resize.f2fs.8
> new file mode 100644
> index 0000000..1920810
> --- /dev/null
> +++ b/man/resize.f2fs.8
> @@ -0,0 +1,49 @@
> +.\" Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
> +.\"
> +.TH RESIZE.F2FS 8
> +.SH NAME
> +resize.f2fs \- resize filesystem size
> +.SH SYNOPSIS
> +.B resize.f2fs
> +[
> +.B \-t
> +.I target sectors
> +]
> +[
> +.B \-d
> +.I debugging-level
> +]
> +.I device
> +.SH DESCRIPTION
> +.B resize.f2fs
> +is used to resize an f2fs file system (usually in a disk partition).
> +\fIdevice\fP is the special file corresponding to the device (e.g.
> +\fI/dev/sdXX\fP).
> +
> +Current version only supports expanding the prebuilt filesystem.
> +
> +.PP
> +The exit code returned by
> +.B resize.f2fs
> +is 0 on success and -1 on failure.
> +.SH OPTIONS
> +.TP
> +.BI \-t " target sectors"
> +Specify the size in sectors.
> +.TP
> +.BI \-d " debug-level"
> +Specify the level of debugging options.
> +The default number is 0, which shows basic debugging messages.
> +.TP
> +.SH AUTHOR
> +This version of
> +.B resize.f2fs
> +has been written by Jaegeuk Kim <jaegeuk@kernel.org>.
> +.SH AVAILABILITY
> +.B resize.f2fs
> +is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
> +.SH SEE ALSO
> +.BR mkfs.f2fs(8),
> +.BR fsck.f2fs(8),
> +.BR dump.f2fs(8),
> +.BR defrag.f2fs(8).
>


------------------------------------------------------------------------------
Find and fix application performance issues faster with Applications Manager
Applications Manager provides deep performance insights into multiple tiers of
your business applications. It resolves application problems quickly and
reduces your MTTR. Get your free trial!
https://ad.doubleclick.net/ddm/clk/302982198;130105516;z

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/3] resize.f2fs: support to expand partition size
  2016-04-26  1:57 ` [PATCH 1/3] resize.f2fs: support to expand partition size He YunLei
@ 2016-04-26  2:17   ` Jaegeuk Kim
  2016-04-26  2:28     ` He YunLei
  0 siblings, 1 reply; 7+ messages in thread
From: Jaegeuk Kim @ 2016-04-26  2:17 UTC (permalink / raw)
  To: He YunLei; +Cc: linux-f2fs-devel

Hello,

...

> >+	/* ovp / free segments */
> >+	set_cp(overprov_segment_count, config.new_overprovision);
> >+	set_cp(rsvd_segment_count, config.new_reserved_segments);
> 
> maybe something wrong here:
> 	set_cp(rsvd_segment_count, config.new_reserved_segments);
> 	set_cp(overprov_segment_count, (get_newsb(segment_count_main) -
> 			get_cp(rsvd_segment_count)) *
> 			config.new_overprovision / 100);
> 	set_cp(overprov_segment_count, get_cp(overprov_segment_count) +
> 						get_cp(rsvd_segment_count));
> 

So, could you illustrate the point?
The new_overprovision and new_reserved_segments were calculated from the target
partition size.

Am I missing something?

Thanks,

> >+	free_segment_count = get_cp(free_segment_count);
> >+	new_segment_count = get_newsb(segment_count_main) -
> >+					get_sb(segment_count_main);
> >+
> >+	set_cp(free_segment_count, free_segment_count + new_segment_count);
> >+	set_cp(user_block_count, ((get_sb(segment_count_main) -
> >+			get_cp(overprov_segment_count)) * config.blks_per_seg));
> >+
> >+	if (is_set_ckpt_flags(cp, CP_ORPHAN_PRESENT_FLAG))
> >+		orphan_blks = __start_sum_addr(sbi) - 1;
> >+
> >+	set_cp(cp_pack_start_sum, 1 + get_newsb(cp_payload));
> >+	set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_newsb(cp_payload));
> >+
> >+	/* cur->segno - offset */
> >+	for (i = 0; i < NO_CHECK_TYPE; i++) {
> >+		if (i < CURSEG_HOT_NODE) {
> >+			set_cp(cur_data_segno[i],
> >+					CURSEG_I(sbi, i)->segno - offset);
> >+		} else {
> >+			int n = i - CURSEG_HOT_NODE;
> >+
> >+			set_cp(cur_node_segno[n],
> >+					CURSEG_I(sbi, i)->segno - offset);
> >+		}
> >+	}
> >+
> >+	/* sit / nat ver bitmap bytesize */
> >+	set_cp(sit_ver_bitmap_bytesize,
> >+			((get_newsb(segment_count_sit) / 2) <<
> >+			get_newsb(log_blocks_per_seg)) / 8);
> >+	set_cp(nat_ver_bitmap_bytesize,
> >+			((get_newsb(segment_count_nat) / 2) <<
> >+			get_newsb(log_blocks_per_seg)) / 8);
> >+
> >+	memcpy(new_cp, cp, (unsigned char *)cp->sit_nat_version_bitmap -
> >+						(unsigned char *)cp);
> >+
> >+	crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, new_cp, CHECKSUM_OFFSET);
> >+	*((__le32 *)((unsigned char *)new_cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc);
> >+
> >+	/* Write a new checkpoint in the other set */
> >+	new_cp_blk_no = old_cp_blk_no = get_sb(cp_blkaddr);
> >+	if (sbi->cur_cp == 2)
> >+		old_cp_blk_no += 1 << get_sb(log_blocks_per_seg);
> >+	else
> >+		new_cp_blk_no += 1 << get_sb(log_blocks_per_seg);
> >+
> >+	/* write first cp */
> >+	ret = dev_write_block(new_cp, new_cp_blk_no++);
> >+	ASSERT(ret >= 0);
> >+
> >+	memset(buf, 0, BLOCK_SZ);
> >+	for (i = 0; i < get_newsb(cp_payload); i++) {
> >+		ret = dev_write_block(buf, new_cp_blk_no++);
> >+		ASSERT(ret >= 0);
> >+	}
> >+
> >+	for (i = 0; i < orphan_blks; i++) {
> >+		block_t orphan_blk_no = old_cp_blk_no + 1 + get_sb(cp_payload);
> >+
> >+		ret = dev_read_block(buf, orphan_blk_no++);
> >+		ASSERT(ret >= 0);
> >+
> >+		ret = dev_write_block(buf, new_cp_blk_no++);
> >+		ASSERT(ret >= 0);
> >+	}
> >+
> >+	/* update summary blocks having nullified journal entries */
> >+	for (i = 0; i < NO_CHECK_TYPE; i++) {
> >+		struct curseg_info *curseg = CURSEG_I(sbi, i);
> >+
> >+		ret = dev_write_block(curseg->sum_blk, new_cp_blk_no++);
> >+		ASSERT(ret >= 0);
> >+	}
> >+
> >+	/* write the last cp */
> >+	ret = dev_write_block(new_cp, new_cp_blk_no++);
> >+	ASSERT(ret >= 0);
> >+
> >+	/* disable old checkpoint */
> >+	memset(buf, 0, BLOCK_SZ);
> >+	ret = dev_write_block(buf, old_cp_blk_no);
> >+	ASSERT(ret >= 0);
> >+
> >+	free(buf);
> >+	free(new_cp);
> >+	DBG(0, "Info: Done to rebuild checkpoint blocks\n");
> >+}
> >+
> >+static void rebuild_superblock(struct f2fs_sb_info *sbi,
> >+				struct f2fs_super_block *new_sb)
> >+{
> >+	int index, ret;
> >+	u_int8_t *buf;
> >+
> >+	buf = calloc(BLOCK_SZ, 1);
> >+
> >+	memcpy(buf + F2FS_SUPER_OFFSET, new_sb, sizeof(*new_sb));
> >+	for (index = 0; index < 2; index++) {
> >+		ret = dev_write_block(buf, index);
> >+		ASSERT(ret >= 0);
> >+	}
> >+	free(buf);
> >+	DBG(0, "Info: Done to rebuild superblock\n");
> >+}
> >+
> >+int f2fs_resize(struct f2fs_sb_info *sbi)
> >+{
> >+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
> >+	struct f2fs_super_block new_sb_raw;
> >+	struct f2fs_super_block *new_sb = &new_sb_raw;
> >+	block_t end_blkaddr, old_main_blkaddr, new_main_blkaddr;
> >+	unsigned int offset, offset_seg;
> >+	int err = -1;
> >+
> >+	/* flush NAT/SIT journal entries */
> >+	flush_journal_entries(sbi);
> >+
> >+	memcpy(new_sb, F2FS_RAW_SUPER(sbi), sizeof(*new_sb));
> >+	if (get_new_sb(sbi, new_sb))
> >+		return -1;
> >+
> >+	/* check nat availability */
> >+	if (get_sb(segment_count_nat) > get_newsb(segment_count_nat)) {
> >+		err = shrink_nats(sbi, new_sb);
> >+		if (err) {
> >+			MSG(0, "\tError: Failed to shrink NATs\n");
> >+			return err;
> >+		}
> >+	}
> >+
> >+	config.dbg_lv = 1;
> >+	print_raw_sb_info(sb);
> >+	print_raw_sb_info(new_sb);
> >+	config.dbg_lv = 0;
> >+
> >+	old_main_blkaddr = get_sb(main_blkaddr);
> >+	new_main_blkaddr = get_newsb(main_blkaddr);
> >+	offset = new_main_blkaddr - old_main_blkaddr;
> >+	end_blkaddr = (get_sb(segment_count) << get_sb(log_blocks_per_seg)) +
> >+						get_sb(main_blkaddr);
> >+
> >+	if (old_main_blkaddr > new_main_blkaddr) {
> >+		MSG(0, "\tError: Support resize to expand only\n");
> >+		return -1;
> >+	}
> >+
> >+	err = -EAGAIN;
> >+	offset_seg = offset >> get_sb(log_blocks_per_seg);
> >+
> >+	if (new_main_blkaddr < end_blkaddr) {
> >+		err = f2fs_defragment(sbi, old_main_blkaddr, offset,
> >+						new_main_blkaddr, 0);
> >+		if (err)
> >+			MSG(0, "Skip defragement\n");
> >+	}
> >+	/* move whole data region */
> >+	if (err)
> >+		migrate_main(sbi, new_sb, offset);
> >+
> >+	migrate_ssa(sbi, new_sb, offset_seg);
> >+	migrate_nat(sbi, new_sb);
> >+	migrate_sit(sbi, new_sb, offset_seg);
> >+	rebuild_checkpoint(sbi, new_sb, offset_seg);
> >+	rebuild_superblock(sbi, new_sb);
> >+	return 0;
> >+}
> >diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
> >index 330cbe5..99798d0 100644
> >--- a/include/f2fs_fs.h
> >+++ b/include/f2fs_fs.h
> >@@ -221,18 +221,22 @@ enum f2fs_config_func {
> >  	FSCK,
> >  	DUMP,
> >  	DEFRAG,
> >+	RESIZE,
> >  };
> >
> >  struct f2fs_configuration {
> >  	u_int32_t sector_size;
> >  	u_int32_t reserved_segments;
> >+	u_int32_t new_reserved_segments;
> >  	double overprovision;
> >+	double new_overprovision;
> >  	u_int32_t cur_seg[6];
> >  	u_int32_t segs_per_sec;
> >  	u_int32_t secs_per_zone;
> >  	u_int32_t segs_per_zone;
> >  	u_int32_t start_sector;
> >  	u_int64_t total_sectors;
> >+	u_int64_t target_sectors;
> >  	u_int32_t sectors_per_blk;
> >  	u_int32_t blks_per_seg;
> >  	__u8 init_version[VERSION_LEN + 1];
> >@@ -277,6 +281,9 @@ struct f2fs_configuration {
> >  #define get_sb_le64(member)			le64_to_cpu(sb->member)
> >  #define get_sb_le32(member)			le32_to_cpu(sb->member)
> >  #define get_sb_le16(member)			le16_to_cpu(sb->member)
> >+#define get_newsb_le64(member)			le64_to_cpu(new_sb->member)
> >+#define get_newsb_le32(member)			le32_to_cpu(new_sb->member)
> >+#define get_newsb_le16(member)			le16_to_cpu(new_sb->member)
> >
> >  #define set_sb(member, val)	\
> >  			do {						\
> >@@ -298,6 +305,16 @@ struct f2fs_configuration {
> >  				} 					\
> >  				t; \
> >  			})
> >+#define get_newsb(member)		\
> >+			({						\
> >+				typeof(new_sb->member) t;		\
> >+				switch (sizeof(t)) {			\
> >+				case 8: t = get_newsb_le64(member); break; \
> >+				case 4: t = get_newsb_le32(member); break; \
> >+				case 2: t = get_newsb_le16(member); break; \
> >+				} 					\
> >+				t; \
> >+			})
> >
> >  #define set_cp_le64(member, val)		(cp->member = cpu_to_le64(val))
> >  #define set_cp_le32(member, val)		(cp->member = cpu_to_le32(val))
> >diff --git a/man/Makefile.am b/man/Makefile.am
> >index 256f3c3..6c04de7 100644
> >--- a/man/Makefile.am
> >+++ b/man/Makefile.am
> >@@ -1,3 +1,3 @@
> >  ## Makefile.am
> >
> >-dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8
> >+dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8 resize.f2fs.8
> >diff --git a/man/defrag.f2fs.8 b/man/defrag.f2fs.8
> >index e18e69f..8c709a7 100644
> >--- a/man/defrag.f2fs.8
> >+++ b/man/defrag.f2fs.8
> >@@ -71,4 +71,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
> >  .SH SEE ALSO
> >  .BR mkfs.f2fs(8),
> >  .BR dump.f2fs(8),
> >-.BR fsck.f2fs(8).
> >+.BR fsck.f2fs(8),
> >+.BR resize.f2fs(8).
> >diff --git a/man/dump.f2fs.8 b/man/dump.f2fs.8
> >index ca5f511..dc1d806 100644
> >--- a/man/dump.f2fs.8
> >+++ b/man/dump.f2fs.8
> >@@ -65,4 +65,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
> >  .SH SEE ALSO
> >  .BR mkfs.f2fs(8),
> >  .BR fsck.f2fs(8),
> >-.BR defrag.f2fs(8).
> >+.BR defrag.f2fs(8),
> >+.BR resize.f2fs(8).
> >diff --git a/man/fsck.f2fs.8 b/man/fsck.f2fs.8
> >index 0ffe6e1..22457c5 100644
> >--- a/man/fsck.f2fs.8
> >+++ b/man/fsck.f2fs.8
> >@@ -64,4 +64,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
> >  .SH SEE ALSO
> >  .BR mkfs.f2fs(8),
> >  .BR dump.f2fs(8),
> >-.BR defrag.f2fs(8).
> >+.BR defrag.f2fs(8),
> >+.BR resize.f2fs(8).
> >diff --git a/man/mkfs.f2fs.8 b/man/mkfs.f2fs.8
> >index cdbde96..b767c0c 100644
> >--- a/man/mkfs.f2fs.8
> >+++ b/man/mkfs.f2fs.8
> >@@ -94,4 +94,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
> >  .BR mkfs (8),
> >  .BR fsck.f2fs(8),
> >  .BR dump.f2fs(8),
> >-.BR defrag.f2fs(8).
> >+.BR defrag.f2fs(8),
> >+.BR resize.f2fs(8).
> >diff --git a/man/resize.f2fs.8 b/man/resize.f2fs.8
> >new file mode 100644
> >index 0000000..1920810
> >--- /dev/null
> >+++ b/man/resize.f2fs.8
> >@@ -0,0 +1,49 @@
> >+.\" Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
> >+.\"
> >+.TH RESIZE.F2FS 8
> >+.SH NAME
> >+resize.f2fs \- resize filesystem size
> >+.SH SYNOPSIS
> >+.B resize.f2fs
> >+[
> >+.B \-t
> >+.I target sectors
> >+]
> >+[
> >+.B \-d
> >+.I debugging-level
> >+]
> >+.I device
> >+.SH DESCRIPTION
> >+.B resize.f2fs
> >+is used to resize an f2fs file system (usually in a disk partition).
> >+\fIdevice\fP is the special file corresponding to the device (e.g.
> >+\fI/dev/sdXX\fP).
> >+
> >+Current version only supports expanding the prebuilt filesystem.
> >+
> >+.PP
> >+The exit code returned by
> >+.B resize.f2fs
> >+is 0 on success and -1 on failure.
> >+.SH OPTIONS
> >+.TP
> >+.BI \-t " target sectors"
> >+Specify the size in sectors.
> >+.TP
> >+.BI \-d " debug-level"
> >+Specify the level of debugging options.
> >+The default number is 0, which shows basic debugging messages.
> >+.TP
> >+.SH AUTHOR
> >+This version of
> >+.B resize.f2fs
> >+has been written by Jaegeuk Kim <jaegeuk@kernel.org>.
> >+.SH AVAILABILITY
> >+.B resize.f2fs
> >+is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
> >+.SH SEE ALSO
> >+.BR mkfs.f2fs(8),
> >+.BR fsck.f2fs(8),
> >+.BR dump.f2fs(8),
> >+.BR defrag.f2fs(8).
> >

------------------------------------------------------------------------------
Find and fix application performance issues faster with Applications Manager
Applications Manager provides deep performance insights into multiple tiers of
your business applications. It resolves application problems quickly and
reduces your MTTR. Get your free trial!
https://ad.doubleclick.net/ddm/clk/302982198;130105516;z

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/3] resize.f2fs: support to expand partition size
  2016-04-26  0:28 [PATCH 1/3] resize.f2fs: support to expand partition size Jaegeuk Kim
                   ` (2 preceding siblings ...)
  2016-04-26  1:57 ` [PATCH 1/3] resize.f2fs: support to expand partition size He YunLei
@ 2016-04-26  2:17 ` He YunLei
  3 siblings, 0 replies; 7+ messages in thread
From: He YunLei @ 2016-04-26  2:17 UTC (permalink / raw)
  To: Jaegeuk Kim, linux-f2fs-devel

hi, kim

On 2016/4/26 8:28, Jaegeuk Kim wrote:
> Now user can expand existing partition with resize.f2fs.
> Currently, it doesn't support shrink an image.
>
> For example,
>   # resize.f2fs -t [# of sectors] [image]
>
> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
> ---
>   fsck/Makefile.am  |   4 +-
>   fsck/f2fs.h       |   4 +-
>   fsck/fsck.h       |   2 +
>   fsck/main.c       |  64 ++++++
>   fsck/resize.c     | 578 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   include/f2fs_fs.h |  17 ++
>   man/Makefile.am   |   2 +-
>   man/defrag.f2fs.8 |   3 +-
>   man/dump.f2fs.8   |   3 +-
>   man/fsck.f2fs.8   |   3 +-
>   man/mkfs.f2fs.8   |   3 +-
>   man/resize.f2fs.8 |  49 +++++
>   12 files changed, 724 insertions(+), 8 deletions(-)
>   create mode 100644 fsck/resize.c
>   create mode 100644 man/resize.f2fs.8
>
> diff --git a/fsck/Makefile.am b/fsck/Makefile.am
> index 73df884..3586625 100644
> --- a/fsck/Makefile.am
> +++ b/fsck/Makefile.am
> @@ -3,9 +3,11 @@
>   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 defrag.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	\
> +		resize.c
>   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
> +	ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/resize.f2fs
> diff --git a/fsck/f2fs.h b/fsck/f2fs.h
> index a618b47..4b3c666 100644
> --- a/fsck/f2fs.h
> +++ b/fsck/f2fs.h
> @@ -323,9 +323,9 @@ static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type)
>   #define segno_in_journal(jnl, i)        (jnl->sit_j.entries[i].segno)
>
>   #define SIT_ENTRY_OFFSET(sit_i, segno)                                  \
> -	(segno % sit_i->sents_per_block)
> +	((segno) % sit_i->sents_per_block)
>   #define SIT_BLOCK_OFFSET(sit_i, segno)                                  \
> -	(segno / SIT_ENTRY_PER_BLOCK)
> +	((segno) / SIT_ENTRY_PER_BLOCK)
>   #define TOTAL_SEGS(sbi) (SM_I(sbi)->main_segments)
>
>   static inline bool IS_VALID_NID(struct f2fs_sb_info *sbi, u32 nid)
> diff --git a/fsck/fsck.h b/fsck/fsck.h
> index 5fc214e..db11b41 100644
> --- a/fsck/fsck.h
> +++ b/fsck/fsck.h
> @@ -179,4 +179,6 @@ extern int dump_info_from_blkaddr(struct f2fs_sb_info *, u32);
>   /* defrag.c */
>   int f2fs_defragment(struct f2fs_sb_info *, u64, u64, u64, int);
>
> +/* resize.c */
> +int f2fs_resize(struct f2fs_sb_info *);
>   #endif /* _FSCK_H_ */
> diff --git a/fsck/main.c b/fsck/main.c
> index 6058c4d..885e2cf 100644
> --- a/fsck/main.c
> +++ b/fsck/main.c
> @@ -52,6 +52,15 @@ void defrag_usage()
>   	exit(1);
>   }
>
> +void resize_usage()
> +{
> +	MSG(0, "\nUsage: resize.f2fs [options] device\n");
> +	MSG(0, "[options]:\n");
> +	MSG(0, "  -d debug level [default:0]\n");
> +	MSG(0, "  -t target sectors [default: device size]\n");
> +	exit(1);
> +}
> +
>   void f2fs_parse_options(int argc, char *argv[])
>   {
>   	int option = 0;
> @@ -203,6 +212,34 @@ void f2fs_parse_options(int argc, char *argv[])
>   			}
>   			ASSERT(ret >= 0);
>   		}
> +	} else if (!strcmp("resize.f2fs", prog)) {
> +		const char *option_string = "d:t:";
> +
> +		config.func = RESIZE;
> +		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 't':
> +				if (strncmp(optarg, "0x", 2))
> +					ret = sscanf(optarg, "%"PRIu64"",
> +							&config.target_sectors);
> +				else
> +					ret = sscanf(optarg, "%"PRIx64"",
> +							&config.target_sectors);
> +				break;
> +			default:
> +				MSG(0, "\tError: Unknown option %c\n", option);
> +				resize_usage();
> +				break;
> +			}
> +			ASSERT(ret >= 0);
> +		}
>   	}
>
>   	if ((optind + 1) != argc) {
> @@ -213,6 +250,8 @@ void f2fs_parse_options(int argc, char *argv[])
>   			dump_usage();
>   		else if (config.func == DEFRAG)
>   			defrag_usage();
> +		else if (config.func == RESIZE)
> +			resize_usage();
>   	}
>   	config.device_name = argv[optind];
>   }
> @@ -345,6 +384,27 @@ out_range:
>   	return -1;
>   }
>
> +static int do_resize(struct f2fs_sb_info *sbi)
> +{
> +	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
> +
> +	if (!config.target_sectors)
> +		config.target_sectors = config.total_sectors;
> +
> +	if (config.target_sectors > config.total_sectors) {
> +		ASSERT_MSG("Out-of-range Target=0x%"PRIx64" / 0x%"PRIx64"",
> +				config.target_sectors, config.total_sectors);
> +		return -1;
> +	}
> +
> +	if (config.target_sectors ==
> +			(get_sb(block_count) << get_sb(log_sectors_per_block))) {
> +		ASSERT_MSG("Nothing to resize; it's same");
> +		return -1;
> +	}
> +	return f2fs_resize(sbi);
> +}
> +
>   int main(int argc, char **argv)
>   {
>   	struct f2fs_sb_info *sbi;
> @@ -395,6 +455,10 @@ fsck_again:
>   		if (ret)
>   			goto out_err;
>   		break;
> +	case RESIZE:
> +		if (do_resize(sbi))
> +			goto out_err;
> +		break;
>   	}
>
>   	f2fs_do_umount(sbi);
> diff --git a/fsck/resize.c b/fsck/resize.c
> new file mode 100644
> index 0000000..0803024
> --- /dev/null
> +++ b/fsck/resize.c
> @@ -0,0 +1,578 @@
> +/**
> + * resize.c
> + *
> + * Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
> + *
> + * 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 get_new_sb(struct f2fs_sb_info *sbi, struct f2fs_super_block *sb)
> +{
> +	u_int32_t zone_size_bytes, zone_align_start_offset;
> +	u_int32_t blocks_for_sit, blocks_for_nat, blocks_for_ssa;
> +	u_int32_t sit_segments, diff, total_meta_segments;
> +	u_int32_t total_valid_blks_available;
> +	u_int32_t sit_bitmap_size, max_sit_bitmap_size;
> +	u_int32_t max_nat_bitmap_size, max_nat_segments;
> +	u_int32_t segment_size_bytes = 1 << (get_sb(log_blocksize) +
> +					get_sb(log_blocks_per_seg));
> +	u_int32_t blks_per_seg = 1 << get_sb(log_blocks_per_seg);
> +	u_int32_t segs_per_zone = get_sb(segs_per_sec) * get_sb(secs_per_zone);
> +
> +	set_sb(block_count, config.target_sectors >>
> +				get_sb(log_sectors_per_block));
> +
> +	zone_size_bytes = segment_size_bytes * segs_per_zone;
> +	zone_align_start_offset =
> +		(config.start_sector * config.sector_size +
> +		2 * F2FS_BLKSIZE + zone_size_bytes - 1) /
> +		zone_size_bytes * zone_size_bytes -
> +		config.start_sector * config.sector_size;
> +
> +	set_sb(segment_count, (config.target_sectors * config.sector_size -
> +				zone_align_start_offset) / segment_size_bytes /
> +				config.segs_per_sec * config.segs_per_sec);
> +
> +	blocks_for_sit = ALIGN(get_sb(segment_count), SIT_ENTRY_PER_BLOCK);
> +	sit_segments = SEG_ALIGN(blocks_for_sit);
> +	set_sb(segment_count_sit, sit_segments * 2);
> +	set_sb(nat_blkaddr, get_sb(sit_blkaddr) +
> +				get_sb(segment_count_sit) * blks_per_seg);
> +
> +	total_valid_blks_available = (get_sb(segment_count) -
> +			(get_sb(segment_count_ckpt) +
> +			get_sb(segment_count_sit))) * blks_per_seg;
> +	blocks_for_nat = ALIGN(total_valid_blks_available, NAT_ENTRY_PER_BLOCK);
> +	set_sb(segment_count_nat, SEG_ALIGN(blocks_for_nat));
> +
> +	sit_bitmap_size = ((get_sb(segment_count_sit) / 2) <<
> +				get_sb(log_blocks_per_seg)) / 8;
> +	if (sit_bitmap_size > MAX_SIT_BITMAP_SIZE)
> +		max_sit_bitmap_size = MAX_SIT_BITMAP_SIZE;
> +	else
> +		max_sit_bitmap_size = sit_bitmap_size;
> +
> +	/*
> +	 * It should be reserved minimum 1 segment for nat.
> +	 * When sit is too large, we should expand cp area. It requires more pages for cp.
> +	 */
> +	if (max_sit_bitmap_size >
> +			(CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 65)) {
> +		max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1;
> +		set_sb(cp_payload, F2FS_BLK_ALIGN(max_sit_bitmap_size));
> +	} else {
> +		max_nat_bitmap_size = CHECKSUM_OFFSET - sizeof(struct f2fs_checkpoint) + 1
> +			- max_sit_bitmap_size;
> +		set_sb(cp_payload, 0);
> +	}
> +
> +	max_nat_segments = (max_nat_bitmap_size * 8) >>
> +					get_sb(log_blocks_per_seg);
> +
> +	if (get_sb(segment_count_nat) > max_nat_segments)
> +		set_sb(segment_count_nat, max_nat_segments);
> +
> +	set_sb(segment_count_nat, get_sb(segment_count_nat) * 2);
> +
> +	set_sb(ssa_blkaddr, get_sb(nat_blkaddr) +
> +				get_sb(segment_count_nat) * blks_per_seg);
> +
> +	total_valid_blks_available = (get_sb(segment_count) -
> +			(get_sb(segment_count_ckpt) +
> +			get_sb(segment_count_sit) +
> +			get_sb(segment_count_nat))) * blks_per_seg;
> +
> +	blocks_for_ssa = total_valid_blks_available / blks_per_seg + 1;
> +
> +	set_sb(segment_count_ssa, SEG_ALIGN(blocks_for_ssa));
> +
> +	total_meta_segments = get_sb(segment_count_ckpt) +
> +		get_sb(segment_count_sit) +
> +		get_sb(segment_count_nat) +
> +		get_sb(segment_count_ssa);
> +
> +	diff = total_meta_segments % segs_per_zone;
> +	if (diff)
> +		set_sb(segment_count_ssa, get_sb(segment_count_ssa) +
> +			(segs_per_zone - diff));
> +
> +	set_sb(main_blkaddr, get_sb(ssa_blkaddr) + get_sb(segment_count_ssa) *
> +			 blks_per_seg);
> +
> +	set_sb(segment_count_main, get_sb(segment_count) -
> +			(get_sb(segment_count_ckpt) +
> +			 get_sb(segment_count_sit) +
> +			 get_sb(segment_count_nat) +
> +			 get_sb(segment_count_ssa)));
> +
> +	set_sb(section_count, get_sb(segment_count_main) /
> +						get_sb(segs_per_sec));
> +
> +	set_sb(segment_count_main, get_sb(section_count) *
> +						get_sb(segs_per_sec));
> +
> +	/* Let's determine the best reserved and overprovisioned space */
> +	config.new_overprovision = get_best_overprovision(sb);
> +	config.new_reserved_segments =
> +		(2 * (100 / config.new_overprovision + 1) + 6) *
> +						get_sb(segs_per_sec);
> +
> +	if ((get_sb(segment_count_main) - 2) < config.new_reserved_segments ||
> +		get_sb(segment_count_main) * blks_per_seg >
> +						get_sb(block_count)) {
> +		MSG(0, "\tError: Device size is not sufficient for F2FS volume,\
> +			more segment needed =%u",
> +			config.new_reserved_segments -
> +			(get_sb(segment_count_main) - 2));
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static void migrate_main(struct f2fs_sb_info *sbi,
> +		struct f2fs_super_block *new_sb, unsigned int offset)
> +{
> +	void *raw = calloc(BLOCK_SZ, 1);
> +	struct seg_entry *se;
> +	block_t from, to;
> +	int i, j, ret;
> +	struct f2fs_summary sum;
> +
> +	ASSERT(raw != NULL);
> +
> +	for (i = TOTAL_SEGS(sbi); i >= 0; i--) {
> +		se = get_seg_entry(sbi, i);
> +		if (!se->valid_blocks)
> +			continue;
> +
> +		for (j = sbi->blocks_per_seg - 1; j >= 0; j--) {
> +			if (!f2fs_test_bit(j, (const char *)se->cur_valid_map))
> +				continue;
> +
> +			from = START_BLOCK(sbi, i) + j;
> +			ret = dev_read_block(raw, from);
> +			ASSERT(ret >= 0);
> +
> +			to = from + offset;
> +			ret = dev_write_block(raw, to);
> +			ASSERT(ret >= 0);
> +
> +			get_sum_entry(sbi, from, &sum);
> +
> +			if (IS_DATASEG(se->type))
> +				update_data_blkaddr(sbi, le32_to_cpu(sum.nid),
> +					le16_to_cpu(sum.ofs_in_node), to);
> +			else
> +				update_nat_blkaddr(sbi, 0,
> +						le32_to_cpu(sum.nid), to);
> +		}
> +	}
> +	free(raw);
> +	DBG(0, "Info: Done to migrate data and node blocks\n");
> +}
> +
> +static void move_ssa(struct f2fs_sb_info *sbi, unsigned int segno,
> +					block_t new_sum_blk_addr)
> +{
> +	struct f2fs_summary_block *sum_blk;
> +	int type;
> +
> +	sum_blk = get_sum_block(sbi, segno, &type);
> +	if (type < SEG_TYPE_MAX) {
> +		int ret;
> +
> +		ret = dev_write_block(sum_blk, new_sum_blk_addr);
> +		ASSERT(ret >= 0);
> +		DBG(1, "Write summary block: (%d) segno=%x/%x --> (%d) %x\n",
> +				type, segno, GET_SUM_BLKADDR(sbi, segno),
> +				IS_SUM_NODE_SEG(sum_blk->footer),
> +				new_sum_blk_addr);
> +	}
> +	if (type == SEG_TYPE_NODE || type == SEG_TYPE_DATA ||
> +			type == SEG_TYPE_MAX) {
> +		free(sum_blk);
> +	}
> +	DBG(1, "Info: Done to migrate SSA blocks\n");
> +}
> +
> +static void migrate_ssa(struct f2fs_sb_info *sbi,
> +		struct f2fs_super_block *new_sb, unsigned int offset)
> +{
> +	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
> +	block_t old_sum_blkaddr = get_sb(ssa_blkaddr);
> +	block_t new_sum_blkaddr = get_newsb(ssa_blkaddr);
> +	int segno;
> +
> +	if (new_sum_blkaddr < old_sum_blkaddr + offset) {
> +		for (segno = offset; segno < TOTAL_SEGS(sbi); segno++)
> +			move_ssa(sbi, segno, new_sum_blkaddr + segno - offset);
> +	} else {
> +		for (segno = TOTAL_SEGS(sbi) - 1; segno >= offset; segno--)
> +			move_ssa(sbi, segno, new_sum_blkaddr + segno - offset);
> +	}
> +
> +	DBG(0, "Info: Done to migrate SSA blocks\n");
> +}
> +
> +static int shrink_nats(struct f2fs_sb_info *sbi,
> +				struct f2fs_super_block *new_sb)
> +{
> +	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
> +	struct f2fs_nm_info *nm_i = NM_I(sbi);
> +	block_t old_nat_blkaddr = get_sb(nat_blkaddr);
> +	unsigned int nat_blocks;
> +	void *nat_block, *zero_block;
> +	int nid, ret, new_max_nid;
> +	pgoff_t block_off;
> +	pgoff_t block_addr;
> +	int seg_off;
> +
> +	nat_block = malloc(BLOCK_SZ);
> +	ASSERT(nat_block);
> +	zero_block = calloc(BLOCK_SZ, 1);
> +	ASSERT(zero_block);
> +
> +	nat_blocks = get_newsb(segment_count_nat) >> 1;
> +	nat_blocks = nat_blocks << get_sb(log_blocks_per_seg);
> +	new_max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks;
> +
> +	for (nid = nm_i->max_nid - 1; nid > new_max_nid; nid -= NAT_ENTRY_PER_BLOCK) {
> +		block_off = nid / NAT_ENTRY_PER_BLOCK;
> +		seg_off = block_off >> sbi->log_blocks_per_seg;
> +		block_addr = (pgoff_t)(old_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);
> +
> +		if (memcmp(zero_block, nat_block, BLOCK_SZ)) {
> +			ret = -1;
> +			goto not_avail;
> +		}
> +	}
> +	ret = 0;
> +	nm_i->max_nid = new_max_nid;
> +not_avail:
> +	free(nat_block);
> +	free(zero_block);
> +	return ret;
> +}
> +
> +static void migrate_nat(struct f2fs_sb_info *sbi,
> +			struct f2fs_super_block *new_sb)
> +{
> +	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
> +	struct f2fs_nm_info *nm_i = NM_I(sbi);
> +	block_t old_nat_blkaddr = get_sb(nat_blkaddr);
> +	block_t new_nat_blkaddr = get_newsb(nat_blkaddr);
> +	unsigned int nat_blocks;
> +	void *nat_block;
> +	int nid, ret, new_max_nid;
> +	pgoff_t block_off;
> +	pgoff_t block_addr;
> +	int seg_off;
> +
> +	nat_block = malloc(BLOCK_SZ);
> +	ASSERT(nat_block);
> +
> +	for (nid = nm_i->max_nid - 1; nid >= 0; nid -= NAT_ENTRY_PER_BLOCK) {
> +		block_off = nid / NAT_ENTRY_PER_BLOCK;
> +		seg_off = block_off >> sbi->log_blocks_per_seg;
> +		block_addr = (pgoff_t)(old_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);
> +
> +		block_addr = (pgoff_t)(new_nat_blkaddr +
> +				(seg_off << sbi->log_blocks_per_seg << 1) +
> +				(block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
> +
> +		/* new bitmap should be zeros */
> +		ret = dev_write_block(nat_block, block_addr);
> +		ASSERT(ret >= 0);
> +	}
> +	/* zero out newly assigned nids */
> +	memset(nat_block, 0, BLOCK_SZ);
> +	nat_blocks = get_newsb(segment_count_nat) >> 1;
> +	nat_blocks = nat_blocks << get_sb(log_blocks_per_seg);
> +	new_max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks;
> +
> +	DBG(1, "Write NAT block: %x->%x, max_nid=%x->%x\n",
> +			old_nat_blkaddr, new_nat_blkaddr,
> +			get_sb(segment_count_nat),
> +			get_newsb(segment_count_nat));
> +
> +	for (nid = nm_i->max_nid; nid < new_max_nid;
> +				nid += NAT_ENTRY_PER_BLOCK) {
> +		block_off = nid / NAT_ENTRY_PER_BLOCK;
> +		seg_off = block_off >> sbi->log_blocks_per_seg;
> +		block_addr = (pgoff_t)(new_nat_blkaddr +
> +				(seg_off << sbi->log_blocks_per_seg << 1) +
> +				(block_off & ((1 << sbi->log_blocks_per_seg) - 1)));
> +		ret = dev_write_block(nat_block, block_addr);
> +		ASSERT(ret >= 0);
> +		DBG(1, "Write NAT: %lx\n", block_addr);
> +	}
> +	DBG(0, "Info: Done to migrate NAT blocks\n");
> +}
> +
> +static void migrate_sit(struct f2fs_sb_info *sbi,
> +		struct f2fs_super_block *new_sb, unsigned int offset)
> +{
> +	struct sit_info *sit_i = SIT_I(sbi);
> +	unsigned int ofs = 0, pre_ofs = 0;
> +	unsigned int segno, index;
> +	struct f2fs_sit_block *sit_blk = calloc(BLOCK_SZ, 1);
> +	block_t sit_blks = get_newsb(segment_count_sit) <<
> +						(sbi->log_blocks_per_seg - 1);
> +	struct seg_entry *se;
> +	block_t blk_addr = 0;
> +	int ret;
> +
> +	ASSERT(sit_blk);
> +
> +	/* initialize with zeros */
> +	for (index = 0; index < sit_blks; index++) {
> +		ret = dev_write_block(sit_blk, get_newsb(sit_blkaddr) + index);
> +		ASSERT(ret >= 0);
> +		DBG(1, "Write zero sit: %x\n", get_newsb(sit_blkaddr) + index);
> +	}
> +
> +	for (segno = 0; segno < TOTAL_SEGS(sbi); segno++) {
> +		struct f2fs_sit_entry *sit;
> +
> +		se = get_seg_entry(sbi, segno);
> +		if (segno < offset) {
> +			ASSERT(se->valid_blocks == 0);
> +			continue;
> +		}
> +
> +		ofs = SIT_BLOCK_OFFSET(sit_i, segno - offset);
> +
> +		if (ofs != pre_ofs) {
> +			blk_addr = get_newsb(sit_blkaddr) + pre_ofs;
> +			ret = dev_write_block(sit_blk, blk_addr);
> +			ASSERT(ret >= 0);
> +			DBG(1, "Write valid sit: %x\n", blk_addr);
> +
> +			pre_ofs = ofs;
> +			memset(sit_blk, 0, BLOCK_SZ);
> +		}
> +
> +		sit = &sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, segno - offset)];
> +		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);
> +	}
> +	blk_addr = get_newsb(sit_blkaddr) + ofs;
> +	ret = dev_write_block(sit_blk, blk_addr);
> +	DBG(1, "Write valid sit: %x\n", blk_addr);
> +	ASSERT(ret >= 0);
> +
> +	free(sit_blk);
> +	DBG(0, "Info: Done to migrate SIT blocks\n");
> +}
> +
> +static void rebuild_checkpoint(struct f2fs_sb_info *sbi,
> +			struct f2fs_super_block *new_sb, unsigned int offset)
> +{
> +	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
> +	struct f2fs_checkpoint *new_cp;
> +	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
> +	unsigned int free_segment_count, new_segment_count;
> +	block_t new_cp_blks = 1 + get_newsb(cp_payload);
> +	block_t orphan_blks = 0;
> +	block_t new_cp_blk_no, old_cp_blk_no;
> +	u_int32_t crc = 0;
> +	void *buf;
> +	int i, ret;
> +
> +	new_cp = calloc(new_cp_blks * BLOCK_SZ, 1);
> +	ASSERT(new_cp);
> +
> +	buf = malloc(BLOCK_SZ);
> +	ASSERT(buf);
> +
> +	/* ovp / free segments */
> +	set_cp(overprov_segment_count, config.new_overprovision);
> +	set_cp(rsvd_segment_count, config.new_reserved_segments);
> +	free_segment_count = get_cp(free_segment_count);
> +	new_segment_count = get_newsb(segment_count_main) -
> +					get_sb(segment_count_main);
> +
> +	set_cp(free_segment_count, free_segment_count + new_segment_count);
> +	set_cp(user_block_count, ((get_sb(segment_count_main) -

there maybe we should use new_sb:
		
	set_cp(user_block_count, ((get_newsb(segment_count_main) -

Thanks.
	
> +			get_cp(overprov_segment_count)) * config.blks_per_seg));
> +
> +	if (is_set_ckpt_flags(cp, CP_ORPHAN_PRESENT_FLAG))
> +		orphan_blks = __start_sum_addr(sbi) - 1;
> +
> +	set_cp(cp_pack_start_sum, 1 + get_newsb(cp_payload));
> +	set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_newsb(cp_payload));
> +
> +	/* cur->segno - offset */
> +	for (i = 0; i < NO_CHECK_TYPE; i++) {
> +		if (i < CURSEG_HOT_NODE) {
> +			set_cp(cur_data_segno[i],
> +					CURSEG_I(sbi, i)->segno - offset);
> +		} else {
> +			int n = i - CURSEG_HOT_NODE;
> +
> +			set_cp(cur_node_segno[n],
> +					CURSEG_I(sbi, i)->segno - offset);
> +		}
> +	}
> +
> +	/* sit / nat ver bitmap bytesize */
> +	set_cp(sit_ver_bitmap_bytesize,
> +			((get_newsb(segment_count_sit) / 2) <<
> +			get_newsb(log_blocks_per_seg)) / 8);
> +	set_cp(nat_ver_bitmap_bytesize,
> +			((get_newsb(segment_count_nat) / 2) <<
> +			get_newsb(log_blocks_per_seg)) / 8);
> +
> +	memcpy(new_cp, cp, (unsigned char *)cp->sit_nat_version_bitmap -
> +						(unsigned char *)cp);
> +
> +	crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, new_cp, CHECKSUM_OFFSET);
> +	*((__le32 *)((unsigned char *)new_cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc);
> +
> +	/* Write a new checkpoint in the other set */
> +	new_cp_blk_no = old_cp_blk_no = get_sb(cp_blkaddr);
> +	if (sbi->cur_cp == 2)
> +		old_cp_blk_no += 1 << get_sb(log_blocks_per_seg);
> +	else
> +		new_cp_blk_no += 1 << get_sb(log_blocks_per_seg);
> +
> +	/* write first cp */
> +	ret = dev_write_block(new_cp, new_cp_blk_no++);
> +	ASSERT(ret >= 0);
> +
> +	memset(buf, 0, BLOCK_SZ);
> +	for (i = 0; i < get_newsb(cp_payload); i++) {
> +		ret = dev_write_block(buf, new_cp_blk_no++);
> +		ASSERT(ret >= 0);
> +	}
> +
> +	for (i = 0; i < orphan_blks; i++) {
> +		block_t orphan_blk_no = old_cp_blk_no + 1 + get_sb(cp_payload);
> +
> +		ret = dev_read_block(buf, orphan_blk_no++);
> +		ASSERT(ret >= 0);
> +
> +		ret = dev_write_block(buf, new_cp_blk_no++);
> +		ASSERT(ret >= 0);
> +	}
> +
> +	/* update summary blocks having nullified journal entries */
> +	for (i = 0; i < NO_CHECK_TYPE; i++) {
> +		struct curseg_info *curseg = CURSEG_I(sbi, i);
> +
> +		ret = dev_write_block(curseg->sum_blk, new_cp_blk_no++);
> +		ASSERT(ret >= 0);
> +	}
> +
> +	/* write the last cp */
> +	ret = dev_write_block(new_cp, new_cp_blk_no++);
> +	ASSERT(ret >= 0);
> +
> +	/* disable old checkpoint */
> +	memset(buf, 0, BLOCK_SZ);
> +	ret = dev_write_block(buf, old_cp_blk_no);
> +	ASSERT(ret >= 0);
> +
> +	free(buf);
> +	free(new_cp);
> +	DBG(0, "Info: Done to rebuild checkpoint blocks\n");
> +}
> +
> +static void rebuild_superblock(struct f2fs_sb_info *sbi,
> +				struct f2fs_super_block *new_sb)
> +{
> +	int index, ret;
> +	u_int8_t *buf;
> +
> +	buf = calloc(BLOCK_SZ, 1);
> +
> +	memcpy(buf + F2FS_SUPER_OFFSET, new_sb, sizeof(*new_sb));
> +	for (index = 0; index < 2; index++) {
> +		ret = dev_write_block(buf, index);
> +		ASSERT(ret >= 0);
> +	}
> +	free(buf);
> +	DBG(0, "Info: Done to rebuild superblock\n");
> +}
> +
> +int f2fs_resize(struct f2fs_sb_info *sbi)
> +{
> +	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
> +	struct f2fs_super_block new_sb_raw;
> +	struct f2fs_super_block *new_sb = &new_sb_raw;
> +	block_t end_blkaddr, old_main_blkaddr, new_main_blkaddr;
> +	unsigned int offset, offset_seg;
> +	int err = -1;
> +
> +	/* flush NAT/SIT journal entries */
> +	flush_journal_entries(sbi);
> +
> +	memcpy(new_sb, F2FS_RAW_SUPER(sbi), sizeof(*new_sb));
> +	if (get_new_sb(sbi, new_sb))
> +		return -1;
> +
> +	/* check nat availability */
> +	if (get_sb(segment_count_nat) > get_newsb(segment_count_nat)) {
> +		err = shrink_nats(sbi, new_sb);
> +		if (err) {
> +			MSG(0, "\tError: Failed to shrink NATs\n");
> +			return err;
> +		}
> +	}
> +
> +	config.dbg_lv = 1;
> +	print_raw_sb_info(sb);
> +	print_raw_sb_info(new_sb);
> +	config.dbg_lv = 0;
> +
> +	old_main_blkaddr = get_sb(main_blkaddr);
> +	new_main_blkaddr = get_newsb(main_blkaddr);
> +	offset = new_main_blkaddr - old_main_blkaddr;
> +	end_blkaddr = (get_sb(segment_count) << get_sb(log_blocks_per_seg)) +
> +						get_sb(main_blkaddr);
> +
> +	if (old_main_blkaddr > new_main_blkaddr) {
> +		MSG(0, "\tError: Support resize to expand only\n");
> +		return -1;
> +	}
> +
> +	err = -EAGAIN;
> +	offset_seg = offset >> get_sb(log_blocks_per_seg);
> +
> +	if (new_main_blkaddr < end_blkaddr) {
> +		err = f2fs_defragment(sbi, old_main_blkaddr, offset,
> +						new_main_blkaddr, 0);
> +		if (err)
> +			MSG(0, "Skip defragement\n");
> +	}
> +	/* move whole data region */
> +	if (err)
> +		migrate_main(sbi, new_sb, offset);
> +
> +	migrate_ssa(sbi, new_sb, offset_seg);
> +	migrate_nat(sbi, new_sb);
> +	migrate_sit(sbi, new_sb, offset_seg);
> +	rebuild_checkpoint(sbi, new_sb, offset_seg);
> +	rebuild_superblock(sbi, new_sb);
> +	return 0;
> +}
> diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
> index 330cbe5..99798d0 100644
> --- a/include/f2fs_fs.h
> +++ b/include/f2fs_fs.h
> @@ -221,18 +221,22 @@ enum f2fs_config_func {
>   	FSCK,
>   	DUMP,
>   	DEFRAG,
> +	RESIZE,
>   };
>
>   struct f2fs_configuration {
>   	u_int32_t sector_size;
>   	u_int32_t reserved_segments;
> +	u_int32_t new_reserved_segments;
>   	double overprovision;
> +	double new_overprovision;
>   	u_int32_t cur_seg[6];
>   	u_int32_t segs_per_sec;
>   	u_int32_t secs_per_zone;
>   	u_int32_t segs_per_zone;
>   	u_int32_t start_sector;
>   	u_int64_t total_sectors;
> +	u_int64_t target_sectors;
>   	u_int32_t sectors_per_blk;
>   	u_int32_t blks_per_seg;
>   	__u8 init_version[VERSION_LEN + 1];
> @@ -277,6 +281,9 @@ struct f2fs_configuration {
>   #define get_sb_le64(member)			le64_to_cpu(sb->member)
>   #define get_sb_le32(member)			le32_to_cpu(sb->member)
>   #define get_sb_le16(member)			le16_to_cpu(sb->member)
> +#define get_newsb_le64(member)			le64_to_cpu(new_sb->member)
> +#define get_newsb_le32(member)			le32_to_cpu(new_sb->member)
> +#define get_newsb_le16(member)			le16_to_cpu(new_sb->member)
>
>   #define set_sb(member, val)	\
>   			do {						\
> @@ -298,6 +305,16 @@ struct f2fs_configuration {
>   				} 					\
>   				t; \
>   			})
> +#define get_newsb(member)		\
> +			({						\
> +				typeof(new_sb->member) t;		\
> +				switch (sizeof(t)) {			\
> +				case 8: t = get_newsb_le64(member); break; \
> +				case 4: t = get_newsb_le32(member); break; \
> +				case 2: t = get_newsb_le16(member); break; \
> +				} 					\
> +				t; \
> +			})
>
>   #define set_cp_le64(member, val)		(cp->member = cpu_to_le64(val))
>   #define set_cp_le32(member, val)		(cp->member = cpu_to_le32(val))
> diff --git a/man/Makefile.am b/man/Makefile.am
> index 256f3c3..6c04de7 100644
> --- a/man/Makefile.am
> +++ b/man/Makefile.am
> @@ -1,3 +1,3 @@
>   ## Makefile.am
>
> -dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8
> +dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8 resize.f2fs.8
> diff --git a/man/defrag.f2fs.8 b/man/defrag.f2fs.8
> index e18e69f..8c709a7 100644
> --- a/man/defrag.f2fs.8
> +++ b/man/defrag.f2fs.8
> @@ -71,4 +71,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
>   .SH SEE ALSO
>   .BR mkfs.f2fs(8),
>   .BR dump.f2fs(8),
> -.BR fsck.f2fs(8).
> +.BR fsck.f2fs(8),
> +.BR resize.f2fs(8).
> diff --git a/man/dump.f2fs.8 b/man/dump.f2fs.8
> index ca5f511..dc1d806 100644
> --- a/man/dump.f2fs.8
> +++ b/man/dump.f2fs.8
> @@ -65,4 +65,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
>   .SH SEE ALSO
>   .BR mkfs.f2fs(8),
>   .BR fsck.f2fs(8),
> -.BR defrag.f2fs(8).
> +.BR defrag.f2fs(8),
> +.BR resize.f2fs(8).
> diff --git a/man/fsck.f2fs.8 b/man/fsck.f2fs.8
> index 0ffe6e1..22457c5 100644
> --- a/man/fsck.f2fs.8
> +++ b/man/fsck.f2fs.8
> @@ -64,4 +64,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
>   .SH SEE ALSO
>   .BR mkfs.f2fs(8),
>   .BR dump.f2fs(8),
> -.BR defrag.f2fs(8).
> +.BR defrag.f2fs(8),
> +.BR resize.f2fs(8).
> diff --git a/man/mkfs.f2fs.8 b/man/mkfs.f2fs.8
> index cdbde96..b767c0c 100644
> --- a/man/mkfs.f2fs.8
> +++ b/man/mkfs.f2fs.8
> @@ -94,4 +94,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
>   .BR mkfs (8),
>   .BR fsck.f2fs(8),
>   .BR dump.f2fs(8),
> -.BR defrag.f2fs(8).
> +.BR defrag.f2fs(8),
> +.BR resize.f2fs(8).
> diff --git a/man/resize.f2fs.8 b/man/resize.f2fs.8
> new file mode 100644
> index 0000000..1920810
> --- /dev/null
> +++ b/man/resize.f2fs.8
> @@ -0,0 +1,49 @@
> +.\" Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
> +.\"
> +.TH RESIZE.F2FS 8
> +.SH NAME
> +resize.f2fs \- resize filesystem size
> +.SH SYNOPSIS
> +.B resize.f2fs
> +[
> +.B \-t
> +.I target sectors
> +]
> +[
> +.B \-d
> +.I debugging-level
> +]
> +.I device
> +.SH DESCRIPTION
> +.B resize.f2fs
> +is used to resize an f2fs file system (usually in a disk partition).
> +\fIdevice\fP is the special file corresponding to the device (e.g.
> +\fI/dev/sdXX\fP).
> +
> +Current version only supports expanding the prebuilt filesystem.
> +
> +.PP
> +The exit code returned by
> +.B resize.f2fs
> +is 0 on success and -1 on failure.
> +.SH OPTIONS
> +.TP
> +.BI \-t " target sectors"
> +Specify the size in sectors.
> +.TP
> +.BI \-d " debug-level"
> +Specify the level of debugging options.
> +The default number is 0, which shows basic debugging messages.
> +.TP
> +.SH AUTHOR
> +This version of
> +.B resize.f2fs
> +has been written by Jaegeuk Kim <jaegeuk@kernel.org>.
> +.SH AVAILABILITY
> +.B resize.f2fs
> +is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
> +.SH SEE ALSO
> +.BR mkfs.f2fs(8),
> +.BR fsck.f2fs(8),
> +.BR dump.f2fs(8),
> +.BR defrag.f2fs(8).
>


------------------------------------------------------------------------------
Find and fix application performance issues faster with Applications Manager
Applications Manager provides deep performance insights into multiple tiers of
your business applications. It resolves application problems quickly and
reduces your MTTR. Get your free trial!
https://ad.doubleclick.net/ddm/clk/302982198;130105516;z

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/3] resize.f2fs: support to expand partition size
  2016-04-26  2:17   ` Jaegeuk Kim
@ 2016-04-26  2:28     ` He YunLei
  0 siblings, 0 replies; 7+ messages in thread
From: He YunLei @ 2016-04-26  2:28 UTC (permalink / raw)
  To: Jaegeuk Kim; +Cc: linux-f2fs-devel

On 2016/4/26 10:17, Jaegeuk Kim wrote:
> Hello,
>
> ...
>
>>> +	/* ovp / free segments */
>>> +	set_cp(overprov_segment_count, config.new_overprovision);
>>> +	set_cp(rsvd_segment_count, config.new_reserved_segments);
>>
>> maybe something wrong here:
>> 	set_cp(rsvd_segment_count, config.new_reserved_segments);
>> 	set_cp(overprov_segment_count, (get_newsb(segment_count_main) -
>> 			get_cp(rsvd_segment_count)) *
>> 			config.new_overprovision / 100);
>> 	set_cp(overprov_segment_count, get_cp(overprov_segment_count) +
>> 						get_cp(rsvd_segment_count));
>>
>
> So, could you illustrate the point?
> The new_overprovision and new_reserved_segments were calculated from the target
> partition size.
>
> Am I missing something?

hi, Kim
	
	I test the resize and find something wrong with overprov_segment_count,
and I find its calculation is different with mkfs tool. So I copy the method of
calculation from mkfs tool.

Thanks,
>
> Thanks,
>
>>> +	free_segment_count = get_cp(free_segment_count);
>>> +	new_segment_count = get_newsb(segment_count_main) -
>>> +					get_sb(segment_count_main);
>>> +
>>> +	set_cp(free_segment_count, free_segment_count + new_segment_count);
>>> +	set_cp(user_block_count, ((get_sb(segment_count_main) -
>>> +			get_cp(overprov_segment_count)) * config.blks_per_seg));
>>> +
>>> +	if (is_set_ckpt_flags(cp, CP_ORPHAN_PRESENT_FLAG))
>>> +		orphan_blks = __start_sum_addr(sbi) - 1;
>>> +
>>> +	set_cp(cp_pack_start_sum, 1 + get_newsb(cp_payload));
>>> +	set_cp(cp_pack_total_block_count, 8 + orphan_blks + get_newsb(cp_payload));
>>> +
>>> +	/* cur->segno - offset */
>>> +	for (i = 0; i < NO_CHECK_TYPE; i++) {
>>> +		if (i < CURSEG_HOT_NODE) {
>>> +			set_cp(cur_data_segno[i],
>>> +					CURSEG_I(sbi, i)->segno - offset);
>>> +		} else {
>>> +			int n = i - CURSEG_HOT_NODE;
>>> +
>>> +			set_cp(cur_node_segno[n],
>>> +					CURSEG_I(sbi, i)->segno - offset);
>>> +		}
>>> +	}
>>> +
>>> +	/* sit / nat ver bitmap bytesize */
>>> +	set_cp(sit_ver_bitmap_bytesize,
>>> +			((get_newsb(segment_count_sit) / 2) <<
>>> +			get_newsb(log_blocks_per_seg)) / 8);
>>> +	set_cp(nat_ver_bitmap_bytesize,
>>> +			((get_newsb(segment_count_nat) / 2) <<
>>> +			get_newsb(log_blocks_per_seg)) / 8);
>>> +
>>> +	memcpy(new_cp, cp, (unsigned char *)cp->sit_nat_version_bitmap -
>>> +						(unsigned char *)cp);
>>> +
>>> +	crc = f2fs_cal_crc32(F2FS_SUPER_MAGIC, new_cp, CHECKSUM_OFFSET);
>>> +	*((__le32 *)((unsigned char *)new_cp + CHECKSUM_OFFSET)) = cpu_to_le32(crc);
>>> +
>>> +	/* Write a new checkpoint in the other set */
>>> +	new_cp_blk_no = old_cp_blk_no = get_sb(cp_blkaddr);
>>> +	if (sbi->cur_cp == 2)
>>> +		old_cp_blk_no += 1 << get_sb(log_blocks_per_seg);
>>> +	else
>>> +		new_cp_blk_no += 1 << get_sb(log_blocks_per_seg);
>>> +
>>> +	/* write first cp */
>>> +	ret = dev_write_block(new_cp, new_cp_blk_no++);
>>> +	ASSERT(ret >= 0);
>>> +
>>> +	memset(buf, 0, BLOCK_SZ);
>>> +	for (i = 0; i < get_newsb(cp_payload); i++) {
>>> +		ret = dev_write_block(buf, new_cp_blk_no++);
>>> +		ASSERT(ret >= 0);
>>> +	}
>>> +
>>> +	for (i = 0; i < orphan_blks; i++) {
>>> +		block_t orphan_blk_no = old_cp_blk_no + 1 + get_sb(cp_payload);
>>> +
>>> +		ret = dev_read_block(buf, orphan_blk_no++);
>>> +		ASSERT(ret >= 0);
>>> +
>>> +		ret = dev_write_block(buf, new_cp_blk_no++);
>>> +		ASSERT(ret >= 0);
>>> +	}
>>> +
>>> +	/* update summary blocks having nullified journal entries */
>>> +	for (i = 0; i < NO_CHECK_TYPE; i++) {
>>> +		struct curseg_info *curseg = CURSEG_I(sbi, i);
>>> +
>>> +		ret = dev_write_block(curseg->sum_blk, new_cp_blk_no++);
>>> +		ASSERT(ret >= 0);
>>> +	}
>>> +
>>> +	/* write the last cp */
>>> +	ret = dev_write_block(new_cp, new_cp_blk_no++);
>>> +	ASSERT(ret >= 0);
>>> +
>>> +	/* disable old checkpoint */
>>> +	memset(buf, 0, BLOCK_SZ);
>>> +	ret = dev_write_block(buf, old_cp_blk_no);
>>> +	ASSERT(ret >= 0);
>>> +
>>> +	free(buf);
>>> +	free(new_cp);
>>> +	DBG(0, "Info: Done to rebuild checkpoint blocks\n");
>>> +}
>>> +
>>> +static void rebuild_superblock(struct f2fs_sb_info *sbi,
>>> +				struct f2fs_super_block *new_sb)
>>> +{
>>> +	int index, ret;
>>> +	u_int8_t *buf;
>>> +
>>> +	buf = calloc(BLOCK_SZ, 1);
>>> +
>>> +	memcpy(buf + F2FS_SUPER_OFFSET, new_sb, sizeof(*new_sb));
>>> +	for (index = 0; index < 2; index++) {
>>> +		ret = dev_write_block(buf, index);
>>> +		ASSERT(ret >= 0);
>>> +	}
>>> +	free(buf);
>>> +	DBG(0, "Info: Done to rebuild superblock\n");
>>> +}
>>> +
>>> +int f2fs_resize(struct f2fs_sb_info *sbi)
>>> +{
>>> +	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
>>> +	struct f2fs_super_block new_sb_raw;
>>> +	struct f2fs_super_block *new_sb = &new_sb_raw;
>>> +	block_t end_blkaddr, old_main_blkaddr, new_main_blkaddr;
>>> +	unsigned int offset, offset_seg;
>>> +	int err = -1;
>>> +
>>> +	/* flush NAT/SIT journal entries */
>>> +	flush_journal_entries(sbi);
>>> +
>>> +	memcpy(new_sb, F2FS_RAW_SUPER(sbi), sizeof(*new_sb));
>>> +	if (get_new_sb(sbi, new_sb))
>>> +		return -1;
>>> +
>>> +	/* check nat availability */
>>> +	if (get_sb(segment_count_nat) > get_newsb(segment_count_nat)) {
>>> +		err = shrink_nats(sbi, new_sb);
>>> +		if (err) {
>>> +			MSG(0, "\tError: Failed to shrink NATs\n");
>>> +			return err;
>>> +		}
>>> +	}
>>> +
>>> +	config.dbg_lv = 1;
>>> +	print_raw_sb_info(sb);
>>> +	print_raw_sb_info(new_sb);
>>> +	config.dbg_lv = 0;
>>> +
>>> +	old_main_blkaddr = get_sb(main_blkaddr);
>>> +	new_main_blkaddr = get_newsb(main_blkaddr);
>>> +	offset = new_main_blkaddr - old_main_blkaddr;
>>> +	end_blkaddr = (get_sb(segment_count) << get_sb(log_blocks_per_seg)) +
>>> +						get_sb(main_blkaddr);
>>> +
>>> +	if (old_main_blkaddr > new_main_blkaddr) {
>>> +		MSG(0, "\tError: Support resize to expand only\n");
>>> +		return -1;
>>> +	}
>>> +
>>> +	err = -EAGAIN;
>>> +	offset_seg = offset >> get_sb(log_blocks_per_seg);
>>> +
>>> +	if (new_main_blkaddr < end_blkaddr) {
>>> +		err = f2fs_defragment(sbi, old_main_blkaddr, offset,
>>> +						new_main_blkaddr, 0);
>>> +		if (err)
>>> +			MSG(0, "Skip defragement\n");
>>> +	}
>>> +	/* move whole data region */
>>> +	if (err)
>>> +		migrate_main(sbi, new_sb, offset);
>>> +
>>> +	migrate_ssa(sbi, new_sb, offset_seg);
>>> +	migrate_nat(sbi, new_sb);
>>> +	migrate_sit(sbi, new_sb, offset_seg);
>>> +	rebuild_checkpoint(sbi, new_sb, offset_seg);
>>> +	rebuild_superblock(sbi, new_sb);
>>> +	return 0;
>>> +}
>>> diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
>>> index 330cbe5..99798d0 100644
>>> --- a/include/f2fs_fs.h
>>> +++ b/include/f2fs_fs.h
>>> @@ -221,18 +221,22 @@ enum f2fs_config_func {
>>>   	FSCK,
>>>   	DUMP,
>>>   	DEFRAG,
>>> +	RESIZE,
>>>   };
>>>
>>>   struct f2fs_configuration {
>>>   	u_int32_t sector_size;
>>>   	u_int32_t reserved_segments;
>>> +	u_int32_t new_reserved_segments;
>>>   	double overprovision;
>>> +	double new_overprovision;
>>>   	u_int32_t cur_seg[6];
>>>   	u_int32_t segs_per_sec;
>>>   	u_int32_t secs_per_zone;
>>>   	u_int32_t segs_per_zone;
>>>   	u_int32_t start_sector;
>>>   	u_int64_t total_sectors;
>>> +	u_int64_t target_sectors;
>>>   	u_int32_t sectors_per_blk;
>>>   	u_int32_t blks_per_seg;
>>>   	__u8 init_version[VERSION_LEN + 1];
>>> @@ -277,6 +281,9 @@ struct f2fs_configuration {
>>>   #define get_sb_le64(member)			le64_to_cpu(sb->member)
>>>   #define get_sb_le32(member)			le32_to_cpu(sb->member)
>>>   #define get_sb_le16(member)			le16_to_cpu(sb->member)
>>> +#define get_newsb_le64(member)			le64_to_cpu(new_sb->member)
>>> +#define get_newsb_le32(member)			le32_to_cpu(new_sb->member)
>>> +#define get_newsb_le16(member)			le16_to_cpu(new_sb->member)
>>>
>>>   #define set_sb(member, val)	\
>>>   			do {						\
>>> @@ -298,6 +305,16 @@ struct f2fs_configuration {
>>>   				} 					\
>>>   				t; \
>>>   			})
>>> +#define get_newsb(member)		\
>>> +			({						\
>>> +				typeof(new_sb->member) t;		\
>>> +				switch (sizeof(t)) {			\
>>> +				case 8: t = get_newsb_le64(member); break; \
>>> +				case 4: t = get_newsb_le32(member); break; \
>>> +				case 2: t = get_newsb_le16(member); break; \
>>> +				} 					\
>>> +				t; \
>>> +			})
>>>
>>>   #define set_cp_le64(member, val)		(cp->member = cpu_to_le64(val))
>>>   #define set_cp_le32(member, val)		(cp->member = cpu_to_le32(val))
>>> diff --git a/man/Makefile.am b/man/Makefile.am
>>> index 256f3c3..6c04de7 100644
>>> --- a/man/Makefile.am
>>> +++ b/man/Makefile.am
>>> @@ -1,3 +1,3 @@
>>>   ## Makefile.am
>>>
>>> -dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8
>>> +dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8 resize.f2fs.8
>>> diff --git a/man/defrag.f2fs.8 b/man/defrag.f2fs.8
>>> index e18e69f..8c709a7 100644
>>> --- a/man/defrag.f2fs.8
>>> +++ b/man/defrag.f2fs.8
>>> @@ -71,4 +71,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
>>>   .SH SEE ALSO
>>>   .BR mkfs.f2fs(8),
>>>   .BR dump.f2fs(8),
>>> -.BR fsck.f2fs(8).
>>> +.BR fsck.f2fs(8),
>>> +.BR resize.f2fs(8).
>>> diff --git a/man/dump.f2fs.8 b/man/dump.f2fs.8
>>> index ca5f511..dc1d806 100644
>>> --- a/man/dump.f2fs.8
>>> +++ b/man/dump.f2fs.8
>>> @@ -65,4 +65,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
>>>   .SH SEE ALSO
>>>   .BR mkfs.f2fs(8),
>>>   .BR fsck.f2fs(8),
>>> -.BR defrag.f2fs(8).
>>> +.BR defrag.f2fs(8),
>>> +.BR resize.f2fs(8).
>>> diff --git a/man/fsck.f2fs.8 b/man/fsck.f2fs.8
>>> index 0ffe6e1..22457c5 100644
>>> --- a/man/fsck.f2fs.8
>>> +++ b/man/fsck.f2fs.8
>>> @@ -64,4 +64,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
>>>   .SH SEE ALSO
>>>   .BR mkfs.f2fs(8),
>>>   .BR dump.f2fs(8),
>>> -.BR defrag.f2fs(8).
>>> +.BR defrag.f2fs(8),
>>> +.BR resize.f2fs(8).
>>> diff --git a/man/mkfs.f2fs.8 b/man/mkfs.f2fs.8
>>> index cdbde96..b767c0c 100644
>>> --- a/man/mkfs.f2fs.8
>>> +++ b/man/mkfs.f2fs.8
>>> @@ -94,4 +94,5 @@ is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-too
>>>   .BR mkfs (8),
>>>   .BR fsck.f2fs(8),
>>>   .BR dump.f2fs(8),
>>> -.BR defrag.f2fs(8).
>>> +.BR defrag.f2fs(8),
>>> +.BR resize.f2fs(8).
>>> diff --git a/man/resize.f2fs.8 b/man/resize.f2fs.8
>>> new file mode 100644
>>> index 0000000..1920810
>>> --- /dev/null
>>> +++ b/man/resize.f2fs.8
>>> @@ -0,0 +1,49 @@
>>> +.\" Copyright (c) 2015 Jaegeuk Kim <jaegeuk@kernel.org>
>>> +.\"
>>> +.TH RESIZE.F2FS 8
>>> +.SH NAME
>>> +resize.f2fs \- resize filesystem size
>>> +.SH SYNOPSIS
>>> +.B resize.f2fs
>>> +[
>>> +.B \-t
>>> +.I target sectors
>>> +]
>>> +[
>>> +.B \-d
>>> +.I debugging-level
>>> +]
>>> +.I device
>>> +.SH DESCRIPTION
>>> +.B resize.f2fs
>>> +is used to resize an f2fs file system (usually in a disk partition).
>>> +\fIdevice\fP is the special file corresponding to the device (e.g.
>>> +\fI/dev/sdXX\fP).
>>> +
>>> +Current version only supports expanding the prebuilt filesystem.
>>> +
>>> +.PP
>>> +The exit code returned by
>>> +.B resize.f2fs
>>> +is 0 on success and -1 on failure.
>>> +.SH OPTIONS
>>> +.TP
>>> +.BI \-t " target sectors"
>>> +Specify the size in sectors.
>>> +.TP
>>> +.BI \-d " debug-level"
>>> +Specify the level of debugging options.
>>> +The default number is 0, which shows basic debugging messages.
>>> +.TP
>>> +.SH AUTHOR
>>> +This version of
>>> +.B resize.f2fs
>>> +has been written by Jaegeuk Kim <jaegeuk@kernel.org>.
>>> +.SH AVAILABILITY
>>> +.B resize.f2fs
>>> +is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
>>> +.SH SEE ALSO
>>> +.BR mkfs.f2fs(8),
>>> +.BR fsck.f2fs(8),
>>> +.BR dump.f2fs(8),
>>> +.BR defrag.f2fs(8).
>>>
>
> .
>


------------------------------------------------------------------------------
Find and fix application performance issues faster with Applications Manager
Applications Manager provides deep performance insights into multiple tiers of
your business applications. It resolves application problems quickly and
reduces your MTTR. Get your free trial!
https://ad.doubleclick.net/ddm/clk/302982198;130105516;z

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2016-04-26  2:39 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-26  0:28 [PATCH 1/3] resize.f2fs: support to expand partition size Jaegeuk Kim
2016-04-26  0:28 ` [PATCH 2/3] sload.f2fs: support loading files into partition directly Jaegeuk Kim
2016-04-26  0:28 ` [PATCH 3/3] script: add simple test script Jaegeuk Kim
2016-04-26  1:57 ` [PATCH 1/3] resize.f2fs: support to expand partition size He YunLei
2016-04-26  2:17   ` Jaegeuk Kim
2016-04-26  2:28     ` He YunLei
2016-04-26  2:17 ` He YunLei

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).