This is the mail archive of the cluster-cvs@sourceware.org mailing list for the cluster.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

cluster: master - Make gfs2_freedi delete indirect blocks with height>= 2


Gitweb:        http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=ad5b1f74e7298184f917c642d883f3569a07426e
Commit:        ad5b1f74e7298184f917c642d883f3569a07426e
Parent:        aa98945a53b3cf3b9511e5d3b25374ac3687cb5c
Author:        Bob Peterson <rpeterso@redhat.com>
AuthorDate:    Tue Dec 16 10:57:07 2008 -0600
Committer:     Bob Peterson <rpeterso@redhat.com>
CommitterDate: Tue Dec 16 10:57:07 2008 -0600

Make gfs2_freedi delete indirect blocks with height >= 2

bz 474707 - GFS2: gfs2_convert not freeing blocks when removing file with height >=2

Before this patch, the libgfs2 function gfs2_freedi would only
free indirect blocks from an inode at height==1.  If an inode
grew to a height of 2 or more, some blocks would not be freed
when they should have been.  This patch allows it to free all
indirect blocks at all heights.  In the case of the bugzilla,
the users would run gfs2_convert on a gfs file system and
afterward, gfs2_fsck would find a number of blocks that should
have been freed, but were not.
---
 gfs2/libgfs2/fs_ops.c  |   59 +++++++++++++++++++++++++++++++++++-------------
 gfs2/libgfs2/libgfs2.h |    1 +
 2 files changed, 44 insertions(+), 16 deletions(-)

diff --git a/gfs2/libgfs2/fs_ops.c b/gfs2/libgfs2/fs_ops.c
index 3d614e2..0402e85 100644
--- a/gfs2/libgfs2/fs_ops.c
+++ b/gfs2/libgfs2/fs_ops.c
@@ -1567,32 +1567,59 @@ void gfs2_free_block(struct gfs2_sbd *sdp, uint64_t block)
  * gfs2_freedi - unlink a disk inode by block number.
  * Note: currently only works for regular files.
  */
-int gfs2_freedi(struct gfs2_sbd *sdp, uint64_t block)
+int gfs2_freedi(struct gfs2_sbd *sdp, uint64_t diblock)
 {
 	struct gfs2_inode *ip;
-	struct gfs2_buffer_head *bh;
-	int x;
-	uint64_t p;
-	unsigned char *buf;
+	struct gfs2_buffer_head *bh, *nbh;
+	int h, head_size;
+	uint64_t *ptr, block;
 	struct rgrp_list *rgd;
-	
-	bh = bread(&sdp->buf_list, block);
+	uint32_t height;
+	osi_list_t metalist[GFS2_MAX_META_HEIGHT];
+	osi_list_t *cur_list, *next_list, *tmp;
+
+	for (h = 0; h < GFS2_MAX_META_HEIGHT; h++)
+		osi_list_init(&metalist[h]);
+
+	bh = bread(&sdp->buf_list, diblock);
 	ip = inode_get(sdp, bh);
-	if (ip->i_di.di_height > 0) {
-		buf = (unsigned char *)bh->b_data;
-		/* Free up all indirect blocks */
-		for (x = sizeof(struct gfs2_dinode); x < sdp->bsize;
-			 x += sizeof(uint64_t)) {
-			p = be64_to_cpu(*(uint64_t *)(buf + x));
-			if (p)
-				gfs2_free_block(sdp, p);
+	height = ip->i_di.di_height;
+	osi_list_add(&bh->b_altlist, &metalist[0]);
+
+	for (h = 0; h < height; h++){
+		cur_list = &metalist[h];
+		next_list = &metalist[h + 1];
+		head_size = (h > 0 ? sizeof(struct gfs2_meta_header) :
+			     sizeof(struct gfs2_dinode));
+
+		for (tmp = cur_list->next; tmp != cur_list; tmp = tmp->next){
+			bh = osi_list_entry(tmp, struct gfs2_buffer_head,
+					    b_altlist);
+
+			for (ptr = (uint64_t *)(bh->b_data + head_size);
+			     (char *)ptr < (bh->b_data + sdp->bsize); ptr++) {
+				if (!*ptr)
+					continue;
+
+				block = be64_to_cpu(*ptr);
+				gfs2_free_block(sdp, block);
+				if (h == height - 1) /* if not metadata */
+					continue; /* don't queue it up */
+				/* Read the next metadata block in the chain.
+				   First see if it's on the nvbuf_list. */
+				nbh = bfind(&sdp->nvbuf_list, block);
+				if (!nbh)
+					nbh = bread(&sdp->buf_list, block);
+				osi_list_add(&nbh->b_altlist, next_list);
+				brelse(nbh, not_updated);
+			}
 		}
 	}
 	/* Set the bitmap type for inode to free space: */
 	gfs2_set_bitmap(sdp, ip->i_di.di_num.no_addr, GFS2_BLKST_FREE);
 	inode_put(ip, updated);
 	/* Now we have to adjust the rg freespace count and inode count: */
-	rgd = gfs2_blk2rgrpd(sdp, block);
+	rgd = gfs2_blk2rgrpd(sdp, diblock);
 	/* The rg itself is in memory as rgd->rg, but there's most likely a  */
 	/* buffer in memory for the rg on disk because we used it to fix the */
 	/* bitmaps, some of which are on the same block on disk.             */
diff --git a/gfs2/libgfs2/libgfs2.h b/gfs2/libgfs2/libgfs2.h
index 82f74c7..7fe530c 100644
--- a/gfs2/libgfs2/libgfs2.h
+++ b/gfs2/libgfs2/libgfs2.h
@@ -376,6 +376,7 @@ void *gfs2_block_list_destroy(struct gfs2_sbd *sdp,
 
 /* buf.c */
 void init_buf_list(struct gfs2_sbd *sdp, struct buf_list *bl, uint32_t limit);
+struct gfs2_buffer_head *bfind(struct buf_list *bl, uint64_t num);
 struct gfs2_buffer_head *bget_generic(struct buf_list *bl, uint64_t num,
 				      int find_existing, int read_disk);
 struct gfs2_buffer_head *bget(struct buf_list *bl, uint64_t num);


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]