[Ocfs2-tools-devel] [PATCH 5/5] Add "clear sparse" feature for ocfs2-tools, take 1

Tao Ma tao.ma at oracle.com
Wed Oct 17 02:26:55 PDT 2007


With a new formatted volume, we can use "--fs-features=nosparse"
in tunefs.ocfs2 to clear the sparse flag and let it to be used by
old ocfs2 kernel. By this option, all the holes will be allocated
with spaces and all the unwritten extents will be removed with
their contents emptied with zero.

Signed-off-by: Tao Ma <tao.ma at oracle.com>
---
 tunefs.ocfs2/features.c    |   19 +++-
 tunefs.ocfs2/sparse_file.c |  293 +++++++++++++++++++++++++++++++++++++++++++-
 tunefs.ocfs2/tunefs.c      |    2 +
 tunefs.ocfs2/tunefs.h      |    3 +
 4 files changed, 314 insertions(+), 3 deletions(-)

diff --git a/tunefs.ocfs2/features.c b/tunefs.ocfs2/features.c
index 77fb89a..bee211c 100644
--- a/tunefs.ocfs2/features.c
+++ b/tunefs.ocfs2/features.c
@@ -42,9 +42,23 @@ errcode_t feature_check(ocfs2_filesys *fs)
 
 	if ((opts.set_feature.incompat &
 	     OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC) &&
-	    ocfs2_sparse_alloc(OCFS2_RAW_SB(fs->fs_super)))
+	    ocfs2_sparse_alloc(OCFS2_RAW_SB(fs->fs_super))) {
 		ret = 1;
+		goto bail;
+	}
 
+ 	if ((opts.clear_feature.incompat &
+ 	     OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC)) {
+ 		if (!ocfs2_sparse_alloc(OCFS2_RAW_SB(fs->fs_super))) {
+ 			ret = 1;
+ 			goto bail;
+ 		}
+
+ 		ret = clear_sparse_file_check(fs, opts.progname);
+ 		if (ret)
+ 			goto bail;
+ 	}
+bail:
 	return ret;
 }
 
@@ -55,5 +69,8 @@ errcode_t update_feature(ocfs2_filesys *fs)
 	if (opts.set_feature.incompat & OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC)
 		ret = set_sparse_file_flag(fs, opts.progname);
 
+	if (opts.clear_feature.incompat & OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC)
+		ret = clear_sparse_file_flag(fs, opts.progname);
+
 	return ret;
 }
diff --git a/tunefs.ocfs2/sparse_file.c b/tunefs.ocfs2/sparse_file.c
index f56ff93..fde9078 100644
--- a/tunefs.ocfs2/sparse_file.c
+++ b/tunefs.ocfs2/sparse_file.c
@@ -45,6 +45,32 @@ struct list_ctxt {
 	struct rb_root multi_link_files;
 };
 
+struct hole_list {
+	uint32_t hole_start;
+	uint32_t hole_len;
+	struct hole_list *next;
+};
+
+struct sparse_file {
+	uint64_t blkno;
+	uint32_t holes_num;
+	struct hole_list *holes;
+	struct unwritten_list *unwritten;
+	struct sparse_file *next;
+};
+
+struct unwritten_list {
+	struct ocfs2_extent_rec rec;
+	struct unwritten_list *next;
+};
+
+struct clear_hole_unwritten_ctxt {
+	uint32_t total_clusters;
+	uint32_t more_ebs;
+	struct sparse_file *files;
+};
+
+static struct clear_hole_unwritten_ctxt clear_ctxt;
 static errcode_t get_total_free_clusters(ocfs2_filesys *fs, uint32_t *clusters)
 {
 	errcode_t ret;
@@ -176,6 +202,8 @@ static errcode_t iterate_extent_list(ocfs2_filesys *fs,
 				     void (*func)(void *priv_data,
 				     		  uint32_t hole_start,
 						  uint32_t hole_end),
+				     void (*rec_func)(void *priv_data,
+						struct ocfs2_extent_rec *rec),
 				     void *priv_data)
 {
 	int i;
@@ -211,7 +239,7 @@ static errcode_t iterate_extent_list(ocfs2_filesys *fs,
 
 			ret = iterate_extent_list(fs, child_el, rec->e_cpos,
 					rec->e_cpos + rec->e_int_clusters,
-					func, priv_data);
+					func, rec_func, priv_data);
 			if (ret)
 				goto bail;
 		}
@@ -222,6 +250,8 @@ static errcode_t iterate_extent_list(ocfs2_filesys *fs,
 			if (!ocfs2_rec_clusters(el->l_tree_depth, rec))
 				continue;
 
+			if (rec_func)
+				rec_func(priv_data, rec);
 			/*
 			 * We have to consider the hole between the start of
 			 * the first extent rec and the beginning of the extent
@@ -277,7 +307,8 @@ static errcode_t tunefs_iterate_regular(ocfs2_filesys *fs,
 
 	end_clusters = (di->i_size + fs->fs_clustersize -1 ) /
 			fs->fs_clustersize;
-	ret = iterate_extent_list(fs, el, 0, end_clusters, ctxt->func, ctxt);
+	ret = iterate_extent_list(fs, el, 0, end_clusters,
+				  ctxt->func, NULL, ctxt);
 	if (ret)
 		goto bail;
 
@@ -640,3 +671,261 @@ errcode_t set_sparse_file_flag(ocfs2_filesys *fs, char *progname)
 bail:
 	return ret;
 }
+
+static void add_hole(void *priv_data, uint32_t hole_start, uint32_t hole_end)
+{
+	struct hole_list *hole = NULL;
+	struct clear_hole_unwritten_ctxt *ctxt =
+			(struct clear_hole_unwritten_ctxt *)priv_data;
+
+	ocfs2_malloc0(sizeof(struct hole_list), &hole);
+	assert(hole);
+
+	hole->hole_start = hole_start;
+	hole->hole_len = hole_end - hole_start;
+
+	hole->next = ctxt->files->holes;
+	ctxt->files->holes = hole;
+	ctxt->total_clusters += hole_end - hole_start;
+	ctxt->files->holes_num++;
+}
+
+static void add_unwritten(void *priv_data, struct ocfs2_extent_rec *rec)
+{
+	struct unwritten_list *unwritten = NULL;
+	struct clear_hole_unwritten_ctxt *ctxt =
+			(struct clear_hole_unwritten_ctxt *)priv_data;
+
+	if (!(rec->e_flags & OCFS2_EXT_UNWRITTEN))
+		return;
+
+	ocfs2_malloc0(sizeof(struct unwritten_list), &unwritten);
+	assert(unwritten);
+
+	memcpy(&unwritten->rec, rec, sizeof(struct ocfs2_extent_rec));
+
+	unwritten->next = ctxt->files->unwritten;
+	ctxt->files->unwritten = unwritten;
+}
+
+static errcode_t calc_hole_unwritten(ocfs2_filesys *fs,
+				     struct ocfs2_dinode *di)
+{
+	errcode_t ret = 0;
+	uint32_t end_clusters, clusters;
+	uint64_t blk_num;
+	struct ocfs2_extent_list *el = &di->id2.i_list;
+	struct sparse_file *file = NULL, *old_files = NULL;
+	uint32_t recs_per_eb = ocfs2_extent_recs_per_eb(fs->fs_blocksize);
+
+	assert(S_ISREG(di->i_mode));
+
+	end_clusters = (di->i_size + fs->fs_clustersize -1 ) /
+			fs->fs_clustersize;
+
+	ret = ocfs2_malloc0(sizeof(struct sparse_file), &file);
+	if (ret)
+		goto bail;
+
+	memset(file, 0, sizeof(struct sparse_file));
+	file->blkno = di->i_blkno;
+	old_files = clear_ctxt.files;
+	clear_ctxt.files = file;
+	ret = iterate_extent_list(fs, el, 0, end_clusters,
+				  add_hole, add_unwritten, &clear_ctxt);
+	if (ret) {
+		clear_ctxt.files = old_files;
+		goto bail;
+	}
+
+	/*
+	 * We have  "hole_num" holes, so more extent records are needed,
+	 * and more extent blocks may needed here.
+	 * In order to simplify the estimation process, we take it for
+	 * granted that one hole need one extent record, so that we can
+	 * calculate the extent block we need roughly.
+	 */
+	blk_num = (file->holes_num + recs_per_eb - 1) / recs_per_eb;
+	clusters = ocfs2_clusters_in_blocks(fs, blk_num);
+	clear_ctxt.more_ebs += clusters;
+
+	file->next = old_files;
+
+bail:
+	return ret;
+}
+
+errcode_t clear_sparse_file_check(ocfs2_filesys *fs, char *progname)
+{
+	errcode_t ret;
+	uint32_t free_clusters = 0;
+
+	ret = iterate_all_regular(fs, progname, calc_hole_unwritten);
+	if (ret)
+		goto bail;
+
+	ret = get_total_free_clusters(fs, &free_clusters);
+	if (ret)
+		goto bail;
+
+	printf("We have %u clusters free and need %u clusters for sparse files "
+		"and %u clusters for more extent blocks\n",
+		free_clusters, clear_ctxt.total_clusters, clear_ctxt.more_ebs);
+
+	if (free_clusters < clear_ctxt.total_clusters + clear_ctxt.more_ebs) {
+		com_err(progname, 0, "Don't have enough free space.");
+		ret = OCFS2_ET_NO_SPACE;
+	}
+bail:
+	return ret;
+}
+
+static errcode_t empty_blocks(ocfs2_filesys *fs,
+			      uint64_t start_blk,
+			      uint64_t num_blocks)
+{
+	errcode_t ret;
+	char *buf = NULL;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &buf);
+	if (ret)
+		goto bail;
+
+	memset(buf, 0, fs->fs_blocksize);
+
+	while (num_blocks) {
+		ret = io_write_block(fs->fs_io, start_blk, 1, buf);
+		if (ret)
+			goto bail;
+
+		num_blocks--;
+		start_blk++;
+	}
+
+bail:
+	if (buf)
+		ocfs2_free(&buf);
+
+	return ret;
+}
+
+errcode_t clear_sparse_file_flag(ocfs2_filesys *fs, char *progname)
+{
+	errcode_t ret = 0;
+	uint32_t hole_len, hole_start, n_clusters;
+	uint64_t p_start;
+	char *buf = NULL;
+	struct ocfs2_dinode *di = NULL;
+	struct hole_list *hole = NULL;
+	struct unwritten_list *unwritten = NULL;
+	struct ocfs2_extent_rec *rec = NULL;
+	struct sparse_file *file = clear_ctxt.files;
+	struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super);
+	uint16_t bpc = fs->fs_clustersize / fs->fs_blocksize;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &buf);
+	if (ret)
+		goto bail;
+
+	/* Iterate all the holes and fill them. */
+	while (file) {
+		hole = file->holes;
+		while (hole) {
+			hole_len = hole->hole_len;
+			hole_start = hole->hole_start;
+			while (hole_len) {
+				ret = ocfs2_new_clusters(fs, 1, hole_len,
+							 &p_start, &n_clusters);
+				if (n_clusters == 0)
+					ret = OCFS2_ET_NO_SPACE;
+				if (ret)
+					goto bail;
+
+				ret = empty_blocks(fs, p_start,
+						   n_clusters * bpc);
+				if (ret)
+					goto bail;
+
+				ret = ocfs2_insert_extent(fs, file->blkno,
+							  hole_start, p_start,
+							  n_clusters, 0);
+				if (ret)
+					goto bail;
+
+				hole_len -= n_clusters;
+				hole_start += n_clusters;
+			}
+
+			hole = hole->next;
+		}
+
+		file = file->next;
+	}
+
+	/*
+	 * Iterate all the unwritten extents, empty its content and
+	 * mark it written.
+	 */
+	file = clear_ctxt.files;
+	while (file) {
+		if (!file->unwritten)
+			goto next_file;
+
+		ret = ocfs2_read_inode(fs, file->blkno, buf);
+		if (ret)
+			goto bail;
+		di = (struct ocfs2_dinode *)buf;
+
+		unwritten = file->unwritten;
+		while (unwritten) {
+			rec = &unwritten->rec;
+
+			ret = empty_blocks(fs, rec->e_blkno,
+					   rec->e_leaf_clusters * bpc);
+			if (ret)
+				goto bail;
+
+			ret = ocfs2_mark_extent_written(fs, di, rec->e_cpos,
+							rec->e_leaf_clusters,
+							rec->e_blkno);
+			unwritten = unwritten->next;
+		}
+next_file:
+		file = file->next;
+	}
+
+	if(ocfs2_writes_unwritten_extents(super))
+		OCFS2_CLEAR_RO_COMPAT_FEATURE(super,
+					 OCFS2_FEATURE_RO_COMPAT_UNWRITTEN);
+
+	OCFS2_CLEAR_INCOMPAT_FEATURE(super,
+				     OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC);
+
+bail:
+	return ret;
+}
+
+void free_clear_ctxt(void)
+{
+	struct sparse_file *file = NULL;
+	struct hole_list *hole = NULL;
+	struct unwritten_list *unwritten = NULL;
+
+	while (clear_ctxt.files) {
+		while (clear_ctxt.files->holes) {
+			hole = clear_ctxt.files->holes;
+			clear_ctxt.files->holes = hole->next;
+			ocfs2_free(&hole);
+		}
+
+		while (clear_ctxt.files->unwritten) {
+			unwritten = clear_ctxt.files->unwritten;
+			clear_ctxt.files->unwritten = unwritten->next;
+			ocfs2_free(&unwritten);
+		}
+
+		file = clear_ctxt.files;
+		clear_ctxt.files = file->next;
+		ocfs2_free(&file);
+	}
+}
diff --git a/tunefs.ocfs2/tunefs.c b/tunefs.ocfs2/tunefs.c
index 035f69d..0a24ec0 100644
--- a/tunefs.ocfs2/tunefs.c
+++ b/tunefs.ocfs2/tunefs.c
@@ -1720,6 +1720,8 @@ close:
 		ocfs2_shutdown_dlm(fs);
 	block_signals(SIG_UNBLOCK);
 
+	free_clear_ctxt();
+
 	free_opts();
 
 	if (fs)
diff --git a/tunefs.ocfs2/tunefs.h b/tunefs.ocfs2/tunefs.h
index 4216b4b..23bab70 100644
--- a/tunefs.ocfs2/tunefs.h
+++ b/tunefs.ocfs2/tunefs.h
@@ -106,6 +106,9 @@ errcode_t remove_slot_check(ocfs2_filesys *fs);
 
 errcode_t list_sparse(ocfs2_filesys *fs);
 errcode_t set_sparse_file_flag(ocfs2_filesys *fs, char *progname);
+errcode_t clear_sparse_file_check(ocfs2_filesys *fs, char *progname);
+errcode_t clear_sparse_file_flag(ocfs2_filesys *fs, char *progname);
+void free_clear_ctxt(void);
 
 errcode_t feature_check(ocfs2_filesys *fs);
 errcode_t update_feature(ocfs2_filesys *fs);
-- 
1.5.3.2.g4f337



More information about the Ocfs2-tools-devel mailing list