linux-f2fs-devel.lists.sourceforge.net archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/6] libf2fs: enhance the bit operations
@ 2015-12-12  0:00 Jaegeuk Kim
  2015-12-12  0:00 ` [PATCH 2/6] fsck.f2fs: sanity_check for extent_cache entry Jaegeuk Kim
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Jaegeuk Kim @ 2015-12-12  0:00 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: Jaegeuk Kim

This patch modifies the existing bit operations.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fsck/fsck.c       |  12 +++----
 include/f2fs_fs.h |  23 ++++++++++--
 lib/libf2fs.c     | 104 ++++++++++++++++++++++++------------------------------
 3 files changed, 73 insertions(+), 66 deletions(-)

diff --git a/fsck/fsck.c b/fsck/fsck.c
index c71f225..c81dde9 100644
--- a/fsck/fsck.c
+++ b/fsck/fsck.c
@@ -820,7 +820,7 @@ static void convert_encrypted_name(unsigned char *name, int len,
 }
 
 static void print_dentry(__u32 depth, __u8 *name,
-		unsigned long *bitmap,
+		unsigned char *bitmap,
 		struct f2fs_dir_entry *dentry,
 		int max, int idx, int last_blk, int encrypted)
 {
@@ -837,7 +837,7 @@ static void print_dentry(__u32 depth, __u8 *name,
 	name_len = le16_to_cpu(dentry[idx].name_len);
 	next_idx = idx + (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN;
 
-	bit_offset = find_next_bit(bitmap, max, next_idx);
+	bit_offset = find_next_bit_le(bitmap, max, next_idx);
 	if (bit_offset >= max && last_blk)
 		last_de = 1;
 
@@ -889,7 +889,7 @@ static int f2fs_check_hash_code(struct f2fs_dir_entry *dentry,
 }
 
 static int __chk_dentries(struct f2fs_sb_info *sbi, struct child_info *child,
-			unsigned long *bitmap,
+			unsigned char *bitmap,
 			struct f2fs_dir_entry *dentry,
 			__u8 (*filenames)[F2FS_SLOT_LEN],
 			int max, int last_blk, int encrypted)
@@ -946,7 +946,7 @@ static int __chk_dentries(struct f2fs_sb_info *sbi, struct child_info *child,
 
 		ftype = dentry[i].file_type;
 		if ((ftype <= F2FS_FT_UNKNOWN || ftype > F2FS_FT_LAST_FILE_TYPE)) {
-			ASSERT_MSG("Bad dentry 0x%x with unexpected ftype 0x%x", i, ftype);
+			ASSERT_MSG("Bad dentry 0x%x with unexpected ftype 0x%x", ino, ftype);
 			if (config.fix_on) {
 				FIX_MSG("Clear bad dentry 0x%x with bad ftype 0x%x",
 					i, ftype);
@@ -1036,7 +1036,7 @@ int fsck_chk_inline_dentries(struct f2fs_sb_info *sbi,
 
 	fsck->dentry_depth++;
 	dentries = __chk_dentries(sbi, child,
-			(unsigned long *)de_blk->dentry_bitmap,
+			de_blk->dentry_bitmap,
 			de_blk->dentry, de_blk->filename,
 			NR_INLINE_DENTRY, 1,
 			file_is_encrypt(node_blk->i.i_advise));
@@ -1068,7 +1068,7 @@ int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
 
 	fsck->dentry_depth++;
 	dentries = __chk_dentries(sbi, child,
-			(unsigned long *)de_blk->dentry_bitmap,
+			de_blk->dentry_bitmap,
 			de_blk->dentry, de_blk->filename,
 			NR_DENTRY_IN_BLOCK, last_blk, encrypted);
 
diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
index a448d61..6fd4c80 100644
--- a/include/f2fs_fs.h
+++ b/include/f2fs_fs.h
@@ -320,6 +320,23 @@ struct f2fs_configuration {
 			})
 
 /*
+ * Copied from include/linux/kernel.h
+ */
+#define __round_mask(x, y)	((__typeof__(x))((y)-1))
+#define round_down(x, y)	((x) & ~__round_mask(x, y))
+#define min(x, y) ({				\
+	typeof(x) _min1 = (x);			\
+	typeof(y) _min2 = (y);			\
+	(void) (&_min1 == &_min2);		\
+	_min1 < _min2 ? _min1 : _min2; })
+
+#define max(x, y) ({				\
+	typeof(x) _max1 = (x);			\
+	typeof(y) _max2 = (y);			\
+	(void) (&_max1 == &_max2);		\
+	_max1 > _max2 ? _max1 : _max2; })
+
+/*
  * Copied from fs/f2fs/f2fs.h
  */
 #define	NR_CURSEG_DATA_TYPE	(3)
@@ -834,8 +851,10 @@ extern int test_bit(unsigned int nr, const void * addr);
 extern int f2fs_test_bit(unsigned int, const char *);
 extern int f2fs_set_bit(unsigned int, char *);
 extern int f2fs_clear_bit(unsigned int, char *);
-extern unsigned long find_next_bit(const unsigned long *,
-				unsigned long, unsigned long);
+extern unsigned long find_next_bit_le(const unsigned char *, unsigned long,
+		unsigned long);
+extern unsigned long find_next_zero_bit_le(const unsigned char *, unsigned long,
+		unsigned long);
 
 extern u_int32_t f2fs_cal_crc32(u_int32_t, void *, int);
 extern int f2fs_crc_valid(u_int32_t blk_crc, void *buf, int len);
diff --git a/lib/libf2fs.c b/lib/libf2fs.c
index 0ac9994..307ad56 100644
--- a/lib/libf2fs.c
+++ b/lib/libf2fs.c
@@ -75,10 +75,10 @@ int get_bits_in_byte(unsigned char n)
 	return bits_in_byte[n];
 }
 
-int set_bit(unsigned int nr,void * addr)
+int set_bit(unsigned int nr, void *addr)
 {
-	int             mask, retval;
-	unsigned char   *ADDR = (unsigned char *) addr;
+	int mask, retval;
+	unsigned char *ADDR = (unsigned char *)addr;
 
 	ADDR += nr >> 3;
 	mask = 1 << ((nr & 0x07));
@@ -87,10 +87,10 @@ int set_bit(unsigned int nr,void * addr)
 	return retval;
 }
 
-int clear_bit(unsigned int nr, void * addr)
+int clear_bit(unsigned int nr, void *addr)
 {
-	int             mask, retval;
-	unsigned char   *ADDR = (unsigned char *) addr;
+	int mask, retval;
+	unsigned char *ADDR = (unsigned char *)addr;
 
 	ADDR += nr >> 3;
 	mask = 1 << ((nr & 0x07));
@@ -99,7 +99,7 @@ int clear_bit(unsigned int nr, void * addr)
 	return retval;
 }
 
-int test_bit(unsigned int nr, const void * addr)
+int test_bit(unsigned int nr, const void *addr)
 {
 	const __u32 *p = (const __u32 *)addr;
 
@@ -142,24 +142,10 @@ int f2fs_clear_bit(unsigned int nr, char *addr)
 	return ret;
 }
 
-static inline unsigned long __ffs(unsigned long word)
+static inline unsigned long __ffs(unsigned char word)
 {
 	int num = 0;
 
-#if BITS_PER_LONG == 64
-	if ((word & 0xffffffff) == 0) {
-		num += 32;
-		word >>= 32;
-	}
-#endif
-	if ((word & 0xffff) == 0) {
-		num += 16;
-		word >>= 16;
-	}
-	if ((word & 0xff) == 0) {
-		num += 8;
-		word >>= 8;
-	}
 	if ((word & 0xf) == 0) {
 		num += 4;
 		word >>= 4;
@@ -173,43 +159,45 @@ static inline unsigned long __ffs(unsigned long word)
 	return num;
 }
 
-unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
-                unsigned long offset)
+/* Copied from linux/lib/find_bit.c */
+#define BITMAP_FIRST_BYTE_MASK(start) (0xff << ((start) & (BITS_PER_BYTE - 1)))
+
+static unsigned long _find_next_bit_le(const unsigned char *addr,
+		unsigned long nbits, unsigned long start, char invert)
+{
+	unsigned char tmp;
+
+	if (!nbits || start >= nbits)
+		return nbits;
+
+	tmp = addr[start / BITS_PER_BYTE] ^ invert;
+
+	/* Handle 1st word. */
+	tmp &= BITMAP_FIRST_BYTE_MASK(start);
+	start = round_down(start, BITS_PER_BYTE);
+
+	while (!tmp) {
+		start += BITS_PER_BYTE;
+		if (start >= nbits)
+			return nbits;
+
+		tmp = addr[start / BITS_PER_BYTE] ^ invert;
+	}
+
+	return min(start + __ffs(tmp), nbits);
+}
+
+unsigned long find_next_bit_le(const unsigned char *addr, unsigned long size,
+		unsigned long offset)
+{
+	return _find_next_bit_le(addr, size, offset, 0);
+}
+
+
+unsigned long find_next_zero_bit_le(const unsigned char *addr,
+		unsigned long size, unsigned long offset)
 {
-        const unsigned long *p = addr + BIT_WORD(offset);
-        unsigned long result = offset & ~(BITS_PER_LONG-1);
-        unsigned long tmp;
-
-        if (offset >= size)
-                return size;
-        size -= result;
-        offset %= BITS_PER_LONG;
-        if (offset) {
-                tmp = *(p++);
-                tmp &= (~0UL << offset);
-                if (size < BITS_PER_LONG)
-                        goto found_first;
-                if (tmp)
-                        goto found_middle;
-                size -= BITS_PER_LONG;
-                result += BITS_PER_LONG;
-        }
-        while (size & ~(BITS_PER_LONG-1)) {
-                if ((tmp = *(p++)))
-                        goto found_middle;
-                result += BITS_PER_LONG;
-                size -= BITS_PER_LONG;
-        }
-        if (!size)
-                return result;
-        tmp = *p;
-
-found_first:
-        tmp &= (~0UL >> (BITS_PER_LONG - size));
-        if (tmp == 0UL)		/* Are any bits set? */
-                return result + size;   /* Nope. */
-found_middle:
-        return result + __ffs(tmp);
+	return _find_next_bit_le(addr, size, offset, 0xff);
 }
 
 /*
-- 
2.5.4 (Apple Git-61)


------------------------------------------------------------------------------

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

* [PATCH 2/6] fsck.f2fs: sanity_check for extent_cache entry
  2015-12-12  0:00 [PATCH 1/6] libf2fs: enhance the bit operations Jaegeuk Kim
@ 2015-12-12  0:00 ` Jaegeuk Kim
  2015-12-12  0:00 ` [PATCH 3/6] mkfs.f2fs: export get_best_overprovision Jaegeuk Kim
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Jaegeuk Kim @ 2015-12-12  0:00 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: Jaegeuk Kim

This patch adds to check the stored extent_cache entry is consistent or not.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fsck/fsck.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++--------------
 fsck/fsck.h | 17 +++++++++---
 fsck/main.c |  2 +-
 3 files changed, 82 insertions(+), 24 deletions(-)

diff --git a/fsck/fsck.c b/fsck/fsck.c
index c81dde9..adc61d2 100644
--- a/fsck/fsck.c
+++ b/fsck/fsck.c
@@ -468,7 +468,8 @@ out:
 
 int fsck_chk_node_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
 		u32 nid, u8 *name, enum FILE_TYPE ftype, enum NODE_TYPE ntype,
-		u32 *blk_cnt, struct child_info *child)
+		u32 *blk_cnt, struct child_info *child,
+		struct extent_info *i_ext)
 {
 	struct node_info ni;
 	struct f2fs_node *node_blk = NULL;
@@ -487,19 +488,19 @@ int fsck_chk_node_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
 			f2fs_set_main_bitmap(sbi, ni.blk_addr,
 							CURSEG_WARM_NODE);
 			fsck_chk_dnode_blk(sbi, inode, nid, ftype, node_blk,
-					blk_cnt, child, &ni);
+					blk_cnt, child, &ni, i_ext);
 			break;
 		case TYPE_INDIRECT_NODE:
 			f2fs_set_main_bitmap(sbi, ni.blk_addr,
 							CURSEG_COLD_NODE);
 			fsck_chk_idnode_blk(sbi, inode, ftype, node_blk,
-					blk_cnt, child);
+					blk_cnt, child, i_ext);
 			break;
 		case TYPE_DOUBLE_INDIRECT_NODE:
 			f2fs_set_main_bitmap(sbi, ni.blk_addr,
 							CURSEG_COLD_NODE);
 			fsck_chk_didnode_blk(sbi, inode, ftype, node_blk,
-					blk_cnt, child);
+					blk_cnt, child, i_ext);
 			break;
 		default:
 			ASSERT(0);
@@ -512,6 +513,26 @@ err:
 	return -EINVAL;
 }
 
+static void update_i_extent(struct extent_info *i_ext, block_t blkaddr)
+{
+	block_t end_addr;
+
+	if (!i_ext)
+		return;
+
+	end_addr = i_ext->ext.blk_addr + i_ext->ext.len;
+
+	/* TODO: check its file offset later */
+	if (blkaddr >= i_ext->ext.blk_addr && blkaddr < end_addr) {
+		unsigned int offset = blkaddr - i_ext->ext.blk_addr;
+
+		if (f2fs_set_bit(offset, i_ext->map))
+			i_ext->fail = 1;
+		else
+			i_ext->len--;
+	}
+}
+
 /* start with valid nid and blkaddr */
 void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
 		enum FILE_TYPE ftype, struct f2fs_node *node_blk,
@@ -522,6 +543,7 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
 	enum NODE_TYPE ntype;
 	u32 i_links = le32_to_cpu(node_blk->i.i_links);
 	u64 i_blocks = le64_to_cpu(node_blk->i.i_blocks);
+	struct extent_info i_extent;
 	unsigned int idx = 0;
 	int need_fix = 0;
 	int ret;
@@ -619,16 +641,28 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
 		}
 	}
 
+	i_extent.ext = node_blk->i.i_ext;
+	i_extent.len = le32_to_cpu(i_extent.ext.len);
+	i_extent.fail = 0;
+	if (i_extent.len) {
+		/* max 126KB */
+		i_extent.map = calloc(i_extent.len, 1);
+		ASSERT(i_extent.map != NULL);
+	}
+
 	/* check data blocks in inode */
 	for (idx = 0; idx < ADDRS_PER_INODE(&node_blk->i); idx++) {
-		if (le32_to_cpu(node_blk->i.i_addr[idx]) != 0) {
+		block_t blkaddr = le32_to_cpu(node_blk->i.i_addr[idx]);
+
+		if (blkaddr != 0) {
 			ret = fsck_chk_data_blk(sbi,
-					le32_to_cpu(node_blk->i.i_addr[idx]),
+					blkaddr,
 					&child, (i_blocks == *blk_cnt),
 					ftype, nid, idx, ni->version,
 					file_is_encrypt(node_blk->i.i_advise));
 			if (!ret) {
 				*blk_cnt = *blk_cnt + 1;
+				update_i_extent(&i_extent, blkaddr);
 			} else if (config.fix_on) {
 				node_blk->i.i_addr[idx] = 0;
 				need_fix = 1;
@@ -639,6 +673,8 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
 
 	/* check node blocks in inode */
 	for (idx = 0; idx < 5; idx++) {
+		block_t blkaddr = le32_to_cpu(node_blk->i.i_nid[idx]);
+
 		if (idx == 0 || idx == 1)
 			ntype = TYPE_DIRECT_NODE;
 		else if (idx == 2 || idx == 3)
@@ -648,10 +684,11 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
 		else
 			ASSERT(0);
 
-		if (le32_to_cpu(node_blk->i.i_nid[idx]) != 0) {
+		if (blkaddr != 0) {
 			ret = fsck_chk_node_blk(sbi, &node_blk->i,
-					le32_to_cpu(node_blk->i.i_nid[idx]),
-					NULL, ftype, ntype, blk_cnt, &child);
+					blkaddr,
+					NULL, ftype, ntype, blk_cnt, &child,
+					&i_extent);
 			if (!ret) {
 				*blk_cnt = *blk_cnt + 1;
 			} else if (config.fix_on) {
@@ -661,6 +698,12 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
 			}
 		}
 	}
+	if (i_extent.len || i_extent.fail) {
+		ASSERT_MSG("ino: 0x%x has wrong ext: untouched=%d, overlap=%d",
+					nid, i_extent.len, i_extent.fail);
+		if (config.fix_on)
+			need_fix = 1;
+	}
 check:
 	if (i_blocks != *blk_cnt) {
 		ASSERT_MSG("ino: 0x%x has i_blocks: %08"PRIx64", "
@@ -729,21 +772,25 @@ skip_blkcnt_fix:
 
 int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
 		u32 nid, enum FILE_TYPE ftype, struct f2fs_node *node_blk,
-		u32 *blk_cnt, struct child_info *child, struct node_info *ni)
+		u32 *blk_cnt, struct child_info *child, struct node_info *ni,
+		struct extent_info *i_ext)
 {
 	int idx, ret;
 	int need_fix = 0;
 
 	for (idx = 0; idx < ADDRS_PER_BLOCK; idx++) {
-		if (le32_to_cpu(node_blk->dn.addr[idx]) == 0x0)
+		block_t blkaddr = le32_to_cpu(node_blk->dn.addr[idx]);
+
+		if (blkaddr == 0x0)
 			continue;
 		ret = fsck_chk_data_blk(sbi,
-			le32_to_cpu(node_blk->dn.addr[idx]),
-			child, le64_to_cpu(inode->i_blocks) == *blk_cnt, ftype,
+			blkaddr, child,
+			le64_to_cpu(inode->i_blocks) == *blk_cnt, ftype,
 			nid, idx, ni->version,
 			file_is_encrypt(inode->i_advise));
 		if (!ret) {
 			*blk_cnt = *blk_cnt + 1;
+			update_i_extent(i_ext, blkaddr);
 		} else if (config.fix_on) {
 			node_blk->dn.addr[idx] = 0;
 			need_fix = 1;
@@ -759,7 +806,7 @@ int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
 
 int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
 		enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt,
-		struct child_info *child)
+		struct child_info *child, struct extent_info *i_ext)
 {
 	int ret;
 	int i = 0;
@@ -769,7 +816,8 @@ int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
 			continue;
 		ret = fsck_chk_node_blk(sbi, inode,
 				le32_to_cpu(node_blk->in.nid[i]), NULL,
-				ftype, TYPE_DIRECT_NODE, blk_cnt, child);
+				ftype, TYPE_DIRECT_NODE, blk_cnt, child,
+				i_ext);
 		if (!ret)
 			*blk_cnt = *blk_cnt + 1;
 		else if (ret == -EINVAL)
@@ -780,7 +828,7 @@ int fsck_chk_idnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
 
 int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
 		enum FILE_TYPE ftype, struct f2fs_node *node_blk, u32 *blk_cnt,
-		struct child_info *child)
+		struct child_info *child, struct extent_info *i_ext)
 {
 	int i = 0;
 	int ret = 0;
@@ -790,7 +838,8 @@ int fsck_chk_didnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
 			continue;
 		ret = fsck_chk_node_blk(sbi, inode,
 				le32_to_cpu(node_blk->in.nid[i]), NULL,
-				ftype, TYPE_INDIRECT_NODE, blk_cnt, child);
+				ftype, TYPE_INDIRECT_NODE, blk_cnt, child,
+				i_ext);
 		if (!ret)
 			*blk_cnt = *blk_cnt + 1;
 		else if (ret == -EINVAL)
@@ -999,7 +1048,7 @@ static int __chk_dentries(struct f2fs_sb_info *sbi, struct child_info *child,
 		blk_cnt = 1;
 		ret = fsck_chk_node_blk(sbi,
 				NULL, le32_to_cpu(dentry[i].ino), name,
-				ftype, TYPE_INODE, &blk_cnt, NULL);
+				ftype, TYPE_INODE, &blk_cnt, NULL, NULL);
 
 		if (ret && config.fix_on) {
 			int j;
@@ -1165,7 +1214,7 @@ void fsck_chk_orphan_node(struct f2fs_sb_info *sbi)
 			blk_cnt = 1;
 			ret = fsck_chk_node_blk(sbi, NULL, ino, NULL,
 					F2FS_FT_ORPHAN, TYPE_INODE, &blk_cnt,
-					NULL);
+					NULL, NULL);
 			if (!ret)
 				new_blk->ino[new_entry_count++] =
 							orphan_blk->ino[j];
diff --git a/fsck/fsck.h b/fsck/fsck.h
index 37a25ba..20dfe6b 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -25,6 +25,13 @@ struct child_info {
 	u8 dots;
 };
 
+struct extent_info {
+	struct f2fs_extent ext;
+	char *map;
+	int len;
+	int fail;
+};
+
 struct f2fs_fsck {
 	struct f2fs_sb_info sbi;
 
@@ -88,16 +95,18 @@ enum seg_type {
 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 *,
-		struct child_info *);
+		struct child_info *, struct extent_info *);
 extern void fsck_chk_inode_blk(struct f2fs_sb_info *, u32, enum FILE_TYPE,
 		struct f2fs_node *, u32 *, struct node_info *);
 extern int fsck_chk_dnode_blk(struct f2fs_sb_info *, struct f2fs_inode *,
 		u32, enum FILE_TYPE, struct f2fs_node *, u32 *,
-		struct child_info *, struct node_info *);
+		struct child_info *, struct node_info *, struct extent_info *);
 extern int fsck_chk_idnode_blk(struct f2fs_sb_info *, struct f2fs_inode *,
-		enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *);
+		enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *,
+		struct extent_info *);
 extern int fsck_chk_didnode_blk(struct f2fs_sb_info *, struct f2fs_inode *,
-		enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *);
+		enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *,
+		struct extent_info *);
 extern int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32, struct child_info *,
 		int, enum FILE_TYPE, u32, u16, u8, int);
 extern int fsck_chk_dentry_blk(struct f2fs_sb_info *, u32, struct child_info *,
diff --git a/fsck/main.c b/fsck/main.c
index 3db4a44..6b0d97e 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -155,7 +155,7 @@ static void do_fsck(struct f2fs_sb_info *sbi)
 	/* Traverse all block recursively from root inode */
 	blk_cnt = 1;
 	fsck_chk_node_blk(sbi, NULL, sbi->root_ino_num, (u8 *)"/",
-			F2FS_FT_DIR, TYPE_INODE, &blk_cnt, NULL);
+			F2FS_FT_DIR, TYPE_INODE, &blk_cnt, NULL, NULL);
 	fsck_verify(sbi);
 	fsck_free(sbi);
 }
-- 
2.5.4 (Apple Git-61)


------------------------------------------------------------------------------

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

* [PATCH 3/6] mkfs.f2fs: export get_best_overprovision
  2015-12-12  0:00 [PATCH 1/6] libf2fs: enhance the bit operations Jaegeuk Kim
  2015-12-12  0:00 ` [PATCH 2/6] fsck.f2fs: sanity_check for extent_cache entry Jaegeuk Kim
@ 2015-12-12  0:00 ` Jaegeuk Kim
  2015-12-12  0:00 ` [PATCH 4/6] f2fs-tools: export print_raw_sb_info Jaegeuk Kim
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Jaegeuk Kim @ 2015-12-12  0:00 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: Jaegeuk Kim

This patch exports get_best_overprovision() function.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 include/f2fs_fs.h  | 28 ++++++++++++++++++++++++++++
 mkfs/f2fs_format.c | 30 +-----------------------------
 2 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
index 6fd4c80..bbdeb51 100644
--- a/include/f2fs_fs.h
+++ b/include/f2fs_fs.h
@@ -886,4 +886,32 @@ extern struct f2fs_configuration config;
 #define ZONE_ALIGN(blks)	ALIGN(blks, config.blks_per_seg * \
 					config.segs_per_zone)
 
+static inline double get_best_overprovision(struct f2fs_super_block *sb)
+{
+	double reserved, ovp, candidate, end, diff, space;
+	double max_ovp = 0, max_space = 0;
+
+	if (get_sb(segment_count_main) < 256) {
+		candidate = 10;
+		end = 95;
+		diff = 5;
+	} else {
+		candidate = 0.01;
+		end = 10;
+		diff = 0.01;
+	}
+
+	for (; candidate <= end; candidate += diff) {
+		reserved = (2 * (100 / candidate + 1) + 6) *
+						get_sb(segs_per_sec);
+		ovp = (get_sb(segment_count_main) - reserved) * candidate / 100;
+		space = get_sb(segment_count_main) - reserved - ovp;
+		if (max_space < space) {
+			max_space = space;
+			max_ovp = candidate;
+		}
+	}
+	return max_ovp;
+}
+
 #endif	/*__F2FS_FS_H */
diff --git a/mkfs/f2fs_format.c b/mkfs/f2fs_format.c
index b0c12f2..c4b17de 100644
--- a/mkfs/f2fs_format.c
+++ b/mkfs/f2fs_format.c
@@ -117,34 +117,6 @@ next:
 	free(config.extension_list);
 }
 
-static double get_best_overprovision(void)
-{
-	double reserved, ovp, candidate, end, diff, space;
-	double max_ovp = 0, max_space = 0;
-
-	if (get_sb(segment_count_main) < 256) {
-		candidate = 10;
-		end = 95;
-		diff = 5;
-	} else {
-		candidate = 0.01;
-		end = 10;
-		diff = 0.01;
-	}
-
-	for (; candidate <= end; candidate += diff) {
-		reserved = (2 * (100 / candidate + 1) + 6) *
-						get_sb(segs_per_sec);
-		ovp = (get_sb(segment_count_main) - reserved) * candidate / 100;
-		space = get_sb(segment_count_main) - reserved - ovp;
-		if (max_space < space) {
-			max_space = space;
-			max_ovp = candidate;
-		}
-	}
-	return max_ovp;
-}
-
 static int f2fs_prepare_super_block(void)
 {
 	u_int32_t blk_size_bytes;
@@ -302,7 +274,7 @@ static int f2fs_prepare_super_block(void)
 
 	/* Let's determine the best reserved and overprovisioned space */
 	if (config.overprovision == 0)
-		config.overprovision = get_best_overprovision();
+		config.overprovision = get_best_overprovision(sb);
 
 	config.reserved_segments =
 			(2 * (100 / config.overprovision + 1) + 6)
-- 
2.5.4 (Apple Git-61)


------------------------------------------------------------------------------

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

* [PATCH 4/6] f2fs-tools: export print_raw_sb_info
  2015-12-12  0:00 [PATCH 1/6] libf2fs: enhance the bit operations Jaegeuk Kim
  2015-12-12  0:00 ` [PATCH 2/6] fsck.f2fs: sanity_check for extent_cache entry Jaegeuk Kim
  2015-12-12  0:00 ` [PATCH 3/6] mkfs.f2fs: export get_best_overprovision Jaegeuk Kim
@ 2015-12-12  0:00 ` Jaegeuk Kim
  2015-12-12  0:00 ` [PATCH 5/6] fsck.f2fs: LFS alloc_type must have free segment after blkoff Jaegeuk Kim
  2015-12-12  0:00 ` [PATCH 6/6] defrag.f2fs: introduce defragmentation tool Jaegeuk Kim
  4 siblings, 0 replies; 6+ messages in thread
From: Jaegeuk Kim @ 2015-12-12  0:00 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: Jaegeuk Kim

This patch exports print_raw_sb_info().

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fsck/fsck.h  | 2 ++
 fsck/mount.c | 6 ++----
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/fsck/fsck.h b/fsck/fsck.h
index 20dfe6b..f6b8c53 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -132,6 +132,8 @@ extern void fsck_free(struct f2fs_sb_info *);
 extern int f2fs_do_mount(struct f2fs_sb_info *);
 extern void f2fs_do_umount(struct f2fs_sb_info *);
 
+extern void print_raw_sb_info(struct f2fs_super_block *);
+
 /* dump.c */
 struct dump_option {
 	nid_t nid;
diff --git a/fsck/mount.c b/fsck/mount.c
index 9431cd0..99e2439 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -99,10 +99,8 @@ void print_node_info(struct f2fs_node *node_block)
 	}
 }
 
-void print_raw_sb_info(struct f2fs_sb_info *sbi)
+void print_raw_sb_info(struct f2fs_super_block *sb)
 {
-	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
-
 	if (!config.dbg_lv)
 		return;
 
@@ -1292,7 +1290,7 @@ int f2fs_do_mount(struct f2fs_sb_info *sbi)
 			return -1;
 	}
 
-	print_raw_sb_info(sbi);
+	print_raw_sb_info(F2FS_RAW_SUPER(sbi));
 
 	init_sb_info(sbi);
 
-- 
2.5.4 (Apple Git-61)


------------------------------------------------------------------------------

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

* [PATCH 5/6] fsck.f2fs: LFS alloc_type must have free segment after blkoff
  2015-12-12  0:00 [PATCH 1/6] libf2fs: enhance the bit operations Jaegeuk Kim
                   ` (2 preceding siblings ...)
  2015-12-12  0:00 ` [PATCH 4/6] f2fs-tools: export print_raw_sb_info Jaegeuk Kim
@ 2015-12-12  0:00 ` Jaegeuk Kim
  2015-12-12  0:00 ` [PATCH 6/6] defrag.f2fs: introduce defragmentation tool Jaegeuk Kim
  4 siblings, 0 replies; 6+ messages in thread
From: Jaegeuk Kim @ 2015-12-12  0:00 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: Jaegeuk Kim

This patch checks alloc_type of current segment type.
If it is LFS, the last of segment should have no valid block.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 fsck/f2fs.h  | 16 ++++++++++
 fsck/fsck.c  | 15 +++++++++-
 fsck/fsck.h  |  4 +++
 fsck/mount.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 131 insertions(+), 1 deletion(-)

diff --git a/fsck/f2fs.h b/fsck/f2fs.h
index 100854e..03a0646 100644
--- a/fsck/f2fs.h
+++ b/fsck/f2fs.h
@@ -354,6 +354,22 @@ static inline bool IS_VALID_BLK_ADDR(struct f2fs_sb_info *sbi, u32 addr)
 	return 1;
 }
 
+static inline int IS_CUR_SEGNO(struct f2fs_sb_info *sbi, u32 segno, int type)
+{
+	int i;
+
+	for (i = 0; i < NO_CHECK_TYPE; i++) {
+		struct curseg_info *curseg = CURSEG_I(sbi, i);
+
+		if (type == i)
+			continue;
+
+		if (segno == curseg->segno)
+			return 1;
+	}
+	return 0;
+}
+
 static inline u64 BLKOFF_FROM_MAIN(struct f2fs_sb_info *sbi, u64 blk_addr)
 {
 	ASSERT(blk_addr >= SM_I(sbi)->main_blkaddr);
diff --git a/fsck/fsck.c b/fsck/fsck.c
index adc61d2..1b55c30 100644
--- a/fsck/fsck.c
+++ b/fsck/fsck.c
@@ -1368,13 +1368,24 @@ int check_curseg_offset(struct f2fs_sb_info *sbi)
 	for (i = 0; i < NO_CHECK_TYPE; i++) {
 		struct curseg_info *curseg = CURSEG_I(sbi, i);
 		struct seg_entry *se;
+		int j, nblocks;
 
 		se = get_seg_entry(sbi, curseg->segno);
 		if (f2fs_test_bit(curseg->next_blkoff,
-				(const char *)se->cur_valid_map) == 1) {
+					(const char *)se->cur_valid_map)) {
 			ASSERT_MSG("Next block offset is not free, type:%d", i);
 			return -EINVAL;
 		}
+		if (curseg->alloc_type == SSR)
+			return 0;
+
+		nblocks = sbi->blocks_per_seg;
+		for (j = curseg->next_blkoff + 1; j < nblocks; j++) {
+			if (f2fs_test_bit(j, (const char *)se->cur_valid_map)) {
+				ASSERT_MSG("LFS must have free section:%d", i);
+				return -EINVAL;
+			}
+		}
 	}
 	return 0;
 }
@@ -1528,6 +1539,8 @@ int fsck_verify(struct f2fs_sb_info *sbi)
 	if (force || (config.bug_on && config.fix_on && !config.ro)) {
 		fix_hard_links(sbi);
 		fix_nat_entries(sbi);
+		move_curseg_info(sbi, SM_I(sbi)->main_blkaddr);
+		write_curseg_info(sbi);
 		rewrite_sit_area_bitmap(sbi);
 		fix_checkpoint(sbi);
 	}
diff --git a/fsck/fsck.h b/fsck/fsck.h
index f6b8c53..4876914 100644
--- a/fsck/fsck.h
+++ b/fsck/fsck.h
@@ -132,6 +132,10 @@ extern void fsck_free(struct f2fs_sb_info *);
 extern int f2fs_do_mount(struct f2fs_sb_info *);
 extern void f2fs_do_umount(struct f2fs_sb_info *);
 
+extern void move_curseg_info(struct f2fs_sb_info *, u64);
+extern void write_curseg_info(struct f2fs_sb_info *);
+extern int find_next_free_block(struct f2fs_sb_info *, u64 *, int, int);
+
 extern void print_raw_sb_info(struct f2fs_super_block *);
 
 /* dump.c */
diff --git a/fsck/mount.c b/fsck/mount.c
index 99e2439..970b159 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -1133,6 +1133,103 @@ void rewrite_sit_area_bitmap(struct f2fs_sb_info *sbi)
 	}
 }
 
+int find_next_free_block(struct f2fs_sb_info *sbi, u64 *to, int left, int type)
+{
+	struct seg_entry *se;
+	u32 segno;
+	u64 offset;
+
+	while (*to >= SM_I(sbi)->main_blkaddr &&
+			*to < F2FS_RAW_SUPER(sbi)->block_count) {
+		segno = GET_SEGNO(sbi, *to);
+		offset = OFFSET_IN_SEG(sbi, *to);
+
+		se = get_seg_entry(sbi, segno);
+
+		if (se->valid_blocks == sbi->blocks_per_seg)
+			goto next;
+
+		if (se->valid_blocks == 0 && !(segno % sbi->segs_per_sec) &&
+					!IS_CUR_SEGNO(sbi, segno, type)) {
+			struct seg_entry *se2;
+			int i;
+
+			for (i = 0; i < sbi->segs_per_sec; i++) {
+				se2 = get_seg_entry(sbi, segno + i);
+				if (se2->valid_blocks)
+					break;
+			}
+			if (i == sbi->segs_per_sec)
+				return 0;
+		}
+
+		if (se->type == type &&
+			!f2fs_test_bit(offset, (const char *)se->cur_valid_map))
+			return 0;
+next:
+		*to = left ? *to - 1: *to + 1;
+	}
+	return -1;
+}
+
+void move_curseg_info(struct f2fs_sb_info *sbi, u64 from)
+{
+	int i, ret;
+
+	/* update summary blocks having nullified journal entries */
+	for (i = 0; i < NO_CHECK_TYPE; i++) {
+		struct curseg_info *curseg = CURSEG_I(sbi, i);
+		u32 old_segno;
+		u64 ssa_blk, to;
+
+		/* update original SSA too */
+		ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno);
+		ret = dev_write_block(curseg->sum_blk, ssa_blk);
+		ASSERT(ret >= 0);
+
+		to = from;
+		ret = find_next_free_block(sbi, &to, 0, i);
+		ASSERT(ret == 0);
+
+		old_segno = curseg->segno;
+		curseg->segno = GET_SEGNO(sbi, to);
+		curseg->next_blkoff = OFFSET_IN_SEG(sbi, to);
+		curseg->alloc_type = SSR;
+
+		/* update new segno */
+		ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno);
+		ret = dev_read_block(curseg->sum_blk, ssa_blk);
+		ASSERT(ret >= 0);
+
+		/* update se->types */
+		reset_curseg(sbi, i);
+
+		DBG(0, "Move curseg[%d] %x -> %x after %"PRIx64"\n",
+				i, old_segno, curseg->segno, from);
+	}
+}
+
+void write_curseg_info(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+	int i;
+
+	for (i = 0; i < NO_CHECK_TYPE; i++) {
+		cp->alloc_type[i] = CURSEG_I(sbi, i)->alloc_type;
+		if (i < CURSEG_HOT_NODE) {
+			set_cp(cur_data_segno[i], CURSEG_I(sbi, i)->segno);
+			set_cp(cur_data_blkoff[i],
+					CURSEG_I(sbi, i)->next_blkoff);
+		} else {
+			int n = i - CURSEG_HOT_NODE;
+
+			set_cp(cur_node_segno[n], CURSEG_I(sbi, i)->segno);
+			set_cp(cur_node_blkoff[n],
+					CURSEG_I(sbi, i)->next_blkoff);
+		}
+	}
+}
+
 int lookup_nat_in_journal(struct f2fs_sb_info *sbi, u32 nid,
 					struct f2fs_nat_entry *raw_nat)
 {
-- 
2.5.4 (Apple Git-61)


------------------------------------------------------------------------------

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

* [PATCH 6/6] defrag.f2fs: introduce defragmentation tool
  2015-12-12  0:00 [PATCH 1/6] libf2fs: enhance the bit operations Jaegeuk Kim
                   ` (3 preceding siblings ...)
  2015-12-12  0:00 ` [PATCH 5/6] fsck.f2fs: LFS alloc_type must have free segment after blkoff Jaegeuk Kim
@ 2015-12-12  0:00 ` Jaegeuk Kim
  4 siblings, 0 replies; 6+ messages in thread
From: Jaegeuk Kim @ 2015-12-12  0:00 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: Jaegeuk Kim

This tool tries to move the valid blocks ranging from blkaddr to blkaddr + len
to targeted blkaddr with a direction like expand or shrink.

The option includes:
 -d debug level [default:0]
 -s start block address [default: main_blkaddr]
 -l length [default:512 (2MB)]
 -t target block address [default: main_blkaddr + 2MB]
 -i set direction as shrink [default: expand]

For example,
 # defrag.f2fs -s 0x100 -l 0x10 -t 0x4000 /dev/sdb1

This will move data blocks between 0x100 and 0x110 to the right side of
0x4000 space.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
---
 autogen.sh        |   0
 fsck/Makefile.am  |   3 +-
 fsck/defrag.c     | 101 +++++++++++++++++++
 fsck/f2fs.h       |   1 +
 fsck/fsck.h       |  10 ++
 fsck/main.c       | 135 +++++++++++++++++++++++--
 fsck/mount.c      | 290 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/f2fs_fs.h |   7 ++
 8 files changed, 538 insertions(+), 9 deletions(-)
 mode change 100644 => 100755 autogen.sh
 create mode 100644 fsck/defrag.c

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


------------------------------------------------------------------------------

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

end of thread, other threads:[~2015-12-12  0:00 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-12-12  0:00 [PATCH 1/6] libf2fs: enhance the bit operations Jaegeuk Kim
2015-12-12  0:00 ` [PATCH 2/6] fsck.f2fs: sanity_check for extent_cache entry Jaegeuk Kim
2015-12-12  0:00 ` [PATCH 3/6] mkfs.f2fs: export get_best_overprovision Jaegeuk Kim
2015-12-12  0:00 ` [PATCH 4/6] f2fs-tools: export print_raw_sb_info Jaegeuk Kim
2015-12-12  0:00 ` [PATCH 5/6] fsck.f2fs: LFS alloc_type must have free segment after blkoff Jaegeuk Kim
2015-12-12  0:00 ` [PATCH 6/6] defrag.f2fs: introduce defragmentation tool Jaegeuk Kim

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).