[Ocfs2-tools-devel] [PATCH] fsck.ocfs2: detect and break chain loop

Xue jiufei xuejiufei at huawei.com
Fri Dec 21 23:09:53 PST 2012


  We have encontered a global_bitmap chain loop. There is a previous
discussion on this issue:  
https://oss.oracle.com/pipermail/ocfs2-tools-devel/2010-March/002785.html.
  Based on previous discussion, we make another patch to detect and break
chain loop in fsck.ocfs2. It has been tested and run correctly.

Signed-off-by: xuejiufei <xuejiufei at huawei.com>
---
 fsck.ocfs2/pass0.c |  171 ++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 154 insertions(+), 17 deletions(-)

diff --git a/fsck.ocfs2/pass0.c b/fsck.ocfs2/pass0.c
index 0f6f14c..e6b21cf 100644
--- a/fsck.ocfs2/pass0.c
+++ b/fsck.ocfs2/pass0.c
@@ -665,6 +665,92 @@ out:
 	return ret;
 }
 
+static errcode_t get_group_desc_next(o2fsck_state *ost, uint64_t blkno,
+		char *buf, uint64_t *next)
+{
+	struct ocfs2_group_desc *gd;
+	errcode_t ret = 0;
+
+	ret = ocfs2_read_group_desc(ost->ost_fs, blkno, buf);
+	if (ret)
+		return ret;
+
+	gd = (struct ocfs2_group_desc *)buf;
+	*next = gd->bg_next_group;
+
+	return ret;
+}
+
+static errcode_t  break_chain_loop(o2fsck_state *ost, struct ocfs2_chain_rec *chain,
+		char *buf1, int *looped)
+{
+	char *buf;
+	uint64_t blkno = chain->c_blkno;
+	uint64_t slow_blkno, fast_blkno, prev_blkno, next = 0; 
+	errcode_t ret = 0;
+
+	ret = ocfs2_malloc_block(ost->ost_fs->fs_io, &buf);
+	if (ret)
+		goto out;
+
+	slow_blkno = blkno;
+	fast_blkno = blkno;
+	prev_blkno = blkno;
+
+	while (1) {
+		if (fast_blkno && ((ret = get_group_desc_next(ost, fast_blkno, buf,
+						&next)) == 0) && next) {
+			fast_blkno = next;
+			ret = get_group_desc_next(ost, fast_blkno, buf, &next); 
+			if (ret)
+				goto out;
+			fast_blkno = next;
+		}
+		else 
+			break;	
+
+		prev_blkno = slow_blkno;
+		ret = get_group_desc_next(ost, slow_blkno, buf, &next);
+		if (ret)
+			goto out;
+		slow_blkno = next;
+
+		if (fast_blkno == slow_blkno) {
+			*looped = 1;
+			break;
+		}
+	}
+
+	if (*looped) {
+		fast_blkno = blkno;
+
+		while (fast_blkno != slow_blkno) {
+			prev_blkno = slow_blkno;
+			ret = get_group_desc_next(ost, slow_blkno, buf, &next);
+			if (ret)
+				goto out;
+			slow_blkno = next;
+
+			ret = get_group_desc_next(ost, fast_blkno, buf, &next);
+			if (ret)
+				goto out;
+			fast_blkno = next;
+		}
+
+		ret = ocfs2_read_group_desc(ost->ost_fs, prev_blkno, buf);
+		if (ret)
+			goto out;
+
+		memcpy(buf1, buf, ost->ost_fs->fs_blocksize);
+	}
+
+out:	
+	if (buf)
+		ocfs2_free(&buf);
+
+	return ret;
+}
+
 /* this takes a slightly ridiculous number of arguments :/ */
 static errcode_t check_chain(o2fsck_state *ost,
 			     struct ocfs2_dinode *di,
@@ -675,19 +761,40 @@ static errcode_t check_chain(o2fsck_state *ost,
 			     char *pre_cache_buf,
 			     int *chain_changed,
 			     ocfs2_bitmap *allowed,
-			     ocfs2_bitmap *forbidden)
+			     ocfs2_bitmap *forbidden,
+				 unsigned int max_chain_len)
 {
 	struct ocfs2_group_desc *bg1 = (struct ocfs2_group_desc *)buf1;
 	struct ocfs2_group_desc *bg2 = (struct ocfs2_group_desc *)buf2;
 	uint64_t blkno;
 	errcode_t ret = 0;
-	int depth = 0, clear_ref = 0;
+	int depth, clear_ref, looped;
 	int blocks_per_group = ocfs2_clusters_to_blocks(ost->ost_fs,
 							cs->cs_cpg);
+	ocfs2_bitmap *tmp = NULL;
 
 	verbosef("free %u total %u blkno %"PRIu64"\n", chain->c_free,
 		 chain->c_total, (uint64_t)chain->c_blkno);
 
+	ret = ocfs2_block_bitmap_new(ost->ost_fs, "temporal group descriptors", &tmp);
+	if (ret) {
+		com_err(whoami, ret, "while allocating temporal descs "
+				"bitmap");
+		goto out;
+	}
+
+re_check:
+	depth = 0;
+	clear_ref = 0;
+	looped = 0;
+	cs->cs_free_bits = 0;
+	cs->cs_total_bits = 0;
+
+	for (blkno = ost->ost_fs->fs_first_cg_blkno;
+			!ocfs2_bitmap_find_next_set(tmp, blkno, &blkno);
+			blkno++)
+		o2fsck_bitmap_clear(tmp, blkno, NULL);
+
 	while(1) {
 		/* fetch the next reference */
 		if (depth == 0)
@@ -700,18 +807,9 @@ static errcode_t check_chain(o2fsck_state *ost,
 			 * to the next group, implying that we've just
 			 * decided that bg1 is valid. */
 			blkno = bg1->bg_blkno;
-			if (allowed) {
-				int was_set;
-				ocfs2_bitmap_test(allowed, blkno, &was_set);
-				if (was_set) {
-					o2fsck_bitmap_clear(allowed, blkno,
-							    &was_set);
-					mark_group_used(ost, cs, bg1->bg_blkno,
-							allowed != NULL, bg1);
-				} else if (forbidden)
-					o2fsck_bitmap_set(forbidden, blkno,
-							  &was_set);
-			} else
+			if (allowed || forbidden)
+				o2fsck_bitmap_set(tmp, blkno, NULL);
+			else
 				mark_group_used(ost, cs, bg1->bg_blkno,
 						allowed != NULL, bg1);
 			blkno = bg1->bg_next_group;
@@ -721,6 +819,17 @@ static errcode_t check_chain(o2fsck_state *ost,
 		if (blkno == 0)
 			break;
 
+		/* there exists a loop */
+		else if ((depth + 1) > max_chain_len) {
+			ret = break_chain_loop(ost, chain, buf1, &looped);
+			if (ret == 0 && looped) {
+				clear_ref = 1;
+				break;
+			}
+			else if (ret)	
+				break;	
+		}
+
 		/* is it even feasible? */
 		if (ocfs2_block_out_of_range(ost->ost_fs, blkno)) {
 			if (prompt(ost, PY, PR_CHAIN_LINK_RANGE,
@@ -826,6 +935,13 @@ static errcode_t check_chain(o2fsck_state *ost,
 				ost->ost_saw_error = 1;
 			}
 		}
+		/* 
+		 * If there exists a loop, cs_total_bits and cs_free_bits may
+		 * be incorrect, we should recheck the chain
+		 */
+		if (ret == 0 && looped) {
+			goto re_check;
+		}
 	}
 
 	if (cs->cs_total_bits != chain->c_total ||
@@ -845,7 +961,24 @@ static errcode_t check_chain(o2fsck_state *ost,
 		}
 	}
 
+	if (allowed) {
+		int was_set;
+		for (blkno = ost->ost_fs->fs_first_cg_blkno;
+				!ocfs2_bitmap_find_next_set(tmp, blkno, &blkno);
+				blkno++) {
+			ocfs2_bitmap_test(allowed, blkno, &was_set);
+			if (was_set) {
+				o2fsck_bitmap_clear(allowed, blkno,
+						&was_set);
+				mark_group_used(ost, cs, blkno, allowed !=NULL, NULL);
+			} else if (forbidden)
+				o2fsck_bitmap_set(forbidden, blkno, &was_set);
+		}
+	}
+
 out:
+	if (tmp)
+		ocfs2_bitmap_free(tmp);
 	return ret;
 }
 
@@ -866,6 +999,7 @@ static errcode_t verify_chain_alloc(o2fsck_state *ost,
 	int changed = 0, trust_next_free = 1;
 	errcode_t ret = 0;
 	uint64_t chain_bytes;
+	unsigned int num_gds, max_chain_len;
 
 	if (memcmp(di->i_signature, OCFS2_INODE_SIGNATURE,
 		   strlen(OCFS2_INODE_SIGNATURE))) {
@@ -895,9 +1029,12 @@ static errcode_t verify_chain_alloc(o2fsck_state *ost,
 	/* XXX should we check suballoc_node? */
 
 	cl = &di->id2.i_chain;
+	num_gds = (di->i_clusters + cl->cl_cpg) / cl->cl_cpg;
+	max_chain_len = (num_gds + cl->cl_count) / cl->cl_count;
 
-	verbosef("cl cpg %u bpc %u count %u next %u\n", 
-		 cl->cl_cpg, cl->cl_bpc, cl->cl_count, cl->cl_next_free_rec);
+	verbosef("cl cpg %u bpc %u count %u next %u, gds %u, max chain length %u\n", 
+		 cl->cl_cpg, cl->cl_bpc, cl->cl_count, cl->cl_next_free_rec,
+		 num_gds, max_chain_len);
 
 	max_count = ocfs2_chain_recs_per_inode(ost->ost_fs->fs_blocksize);
 
@@ -966,7 +1103,7 @@ static errcode_t verify_chain_alloc(o2fsck_state *ost,
 			.cs_cpg = cl->cl_cpg,
 		};
 		ret = check_chain(ost, di, &cs, cr, buf1, buf2, pre_cache_buf,
-				  &changed, allowed, forbidden);
+				  &changed, allowed, forbidden, max_chain_len);
 		/* XXX what?  not checking ret? */
 
 		if (cr->c_blkno != 0) {
-- 
1.7.8.6



More information about the Ocfs2-tools-devel mailing list