[Ocfs2-tools-devel] [PATCH 14/19] Ocfs2-tools: Add running codes for '--freefrag' in operation.c

Tristan Ye tristan.ye at oracle.com
Tue Apr 13 19:50:14 PDT 2010


Task of '--freefrag' is going to get the fragmentation info from
global bitmap in terms of size-fixed chunks and size-variable
extents, with a vivid percentage histogram.

Signed-off-by: Tristan Ye <tristan.ye at oracle.com>
---
 o2info/operations.c |  431 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 431 insertions(+), 0 deletions(-)

diff --git a/o2info/operations.c b/o2info/operations.c
index 21504d6..25b877f 100644
--- a/o2info/operations.c
+++ b/o2info/operations.c
@@ -662,3 +662,434 @@ out:
 DEFINE_O2INFO_OP(filestat,
 		 filestat_run,
 		 NULL);
+
+#define DEFAULT_CHUNKSIZE (1024*1024)
+
+struct free_chunk_histogram {
+	uint32_t fc_chunks[OCFS2_INFO_MAX_HIST];
+	uint32_t fc_clusters[OCFS2_INFO_MAX_HIST];
+};
+
+/*
+ * All info we need to report free-space fragmentation.
+ */
+struct o2info_chunk_info {
+	unsigned long chunkbytes;
+	uint32_t clusters;
+	uint32_t free_clusters;
+	uint32_t total_chunks;
+	uint32_t free_chunks;
+	uint32_t free_chunks_real;
+	int clustersize_bits;
+	int blksize_bits;
+	int chunkbits;
+	uint32_t clusters_in_chunk;
+	uint32_t chunks_in_group;
+	uint32_t min, max, avg; /* chunksize in clusters */
+	struct free_chunk_histogram histogram;
+};
+
+static int init_freefrag_info(struct o2info_chunk_info *info,
+			      struct o2info_volinfo *vf)
+{
+	int ret = 0, i;
+
+	info->clustersize_bits = ul_log2((unsigned long)vf->clustersize);
+	info->blksize_bits = ul_log2((unsigned long)vf->blocksize);
+
+	if (info->chunkbytes) {
+		info->chunkbits = ul_log2(info->chunkbytes);
+		info->clusters_in_chunk = info->chunkbytes >>
+						info->clustersize_bits;
+	} else {
+		info->chunkbits = ul_log2(DEFAULT_CHUNKSIZE);
+		info->clusters_in_chunk = DEFAULT_CHUNKSIZE >>
+						info->clustersize_bits;
+	}
+
+	info->min = ~0U;
+	info->max = info->avg = 0;
+	info->free_chunks_real = 0;
+	info->free_chunks = 0;
+
+	for (i = 0; i < OCFS2_INFO_MAX_HIST; i++) {
+		info->histogram.fc_chunks[i] = 0;
+		info->histogram.fc_clusters[i] = 0;
+	}
+
+	return ret;
+}
+
+static void update_freefrag_stats(struct o2info_chunk_info *info,
+				  unsigned int chunksize)
+{
+	int index;
+
+	index = ul_log2(chunksize);
+	if (index >= OCFS2_INFO_MAX_HIST)
+		index = OCFS2_INFO_MAX_HIST - 1;
+
+	info->histogram.fc_chunks[index]++;
+	info->histogram.fc_clusters[index] += chunksize;
+
+	if (chunksize > info->max)
+		info->max = chunksize;
+
+	if (chunksize < info->min)
+		info->min = chunksize;
+
+	info->avg += chunksize;
+	info->free_chunks_real++;
+}
+
+static int scan_global_bitmap_chain(ocfs2_filesys *fs,
+				    struct ocfs2_chain_rec *rec,
+				    struct o2info_chunk_info *info)
+{
+	int ret = 0, used;
+	uint64_t blkno;
+
+	char *block = NULL;
+	struct ocfs2_group_desc *bg = NULL;
+
+	unsigned int max_bits, num_clusters;
+	unsigned int offset = 0, cluster, chunk;
+	unsigned int chunk_free, last_chunksize = 0;
+
+	if (!rec->c_free)
+		goto out;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &block);
+	if (ret) {
+		tcom_err(ret, "while allocating block buffer");
+		goto out;
+	}
+
+	do {
+		if (!bg)
+			blkno = rec->c_blkno;
+		else
+			blkno = bg->bg_next_group;
+
+		ret = ocfs2_read_blocks(fs, blkno, 1, block);
+		if (ret < 0) {
+			tcom_err(ret, "while reading group descriptor "
+				 "%"PRIu64" for stats", blkno);
+			goto out;
+		}
+
+		bg = (struct ocfs2_group_desc *)block;
+
+		if (!bg->bg_free_bits_count)
+			continue;
+
+		max_bits = bg->bg_bits;
+		offset = 0;
+
+		for (chunk = 0; chunk < info->chunks_in_group; chunk++) {
+
+			/* Last chunk may be not a entire one */
+			if ((offset + info->clusters_in_chunk) > max_bits)
+				num_clusters = max_bits - offset;
+			else
+				num_clusters = info->clusters_in_chunk;
+
+			chunk_free = 0;
+
+			for (cluster = 0; cluster < num_clusters; cluster++) {
+				used = ocfs2_test_bit(offset,
+						(unsigned long *)bg->bg_bitmap);
+				if (!used) {
+					last_chunksize++;
+					chunk_free++;
+				}
+
+				if (used && (last_chunksize)) {
+					update_freefrag_stats(info,
+							      last_chunksize);
+					last_chunksize = 0;
+				}
+
+				offset++;
+			}
+
+			if (chunk_free == info->clusters_in_chunk)
+				info->free_chunks++;
+		}
+
+		/* we need to update the info of last free chunk */
+		if (last_chunksize)
+			update_freefrag_stats(info, last_chunksize);
+
+	} while (bg->bg_next_group);
+
+out:
+	if (block)
+		ocfs2_free(&block);
+
+	return ret;
+}
+
+static int scan_global_bitmap(ocfs2_filesys *fs,
+			      struct ocfs2_chain_list *cl,
+			      struct o2info_chunk_info *info)
+{
+	int ret = 0, i;
+	struct ocfs2_chain_rec *rec = NULL;
+
+	info->chunks_in_group = (cl->cl_cpg / info->clusters_in_chunk) + 1;
+
+	for (i = 0; i < cl->cl_next_free_rec; i++) {
+		rec = &(cl->cl_recs[i]);
+		ret = scan_global_bitmap_chain(fs, rec, info);
+		if (ret)
+			return ret;
+	}
+
+	return ret;
+}
+
+static int get_freefrag_info_libocfs2(struct o2info_operation *op,
+				      ocfs2_filesys *fs,
+				      struct o2info_chunk_info *info)
+{
+	int ret = 0;
+	char *block = NULL;
+
+	uint64_t gb_inode;
+	struct ocfs2_dinode *gb_di = NULL;
+	struct ocfs2_chain_list *cl = NULL;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &block);
+	if (ret) {
+		tcom_err(ret, "while allocating block buffer");
+		goto out;
+	}
+
+	gb_di = (struct ocfs2_dinode *)block;
+
+	ret = ocfs2_lookup_system_inode(fs, GLOBAL_BITMAP_SYSTEM_INODE,
+					0, &gb_inode);
+	if (ret) {
+		tcom_err(ret, "while looking up the global bitmap inode");
+		goto out;
+	}
+
+	ret = ocfs2_read_inode(fs, gb_inode, (char *)gb_di);
+	if (ret) {
+		tcom_err(ret, "reading global_bitmap inode "
+			 "%"PRIu64" for stats", gb_inode);
+		goto out;
+	}
+
+	info->clusters = gb_di->id1.bitmap1.i_total;
+	info->free_clusters = gb_di->id1.bitmap1.i_total -
+				gb_di->id1.bitmap1.i_used;
+
+	info->total_chunks = (info->clusters + info->clusters_in_chunk) >>
+				(info->chunkbits - info->clustersize_bits);
+	cl = &(gb_di->id2.i_chain);
+
+	ret = scan_global_bitmap(fs, cl, info);
+	if (ret)
+		goto out;
+
+	if (info->free_chunks_real) {
+		info->min <<= (info->clustersize_bits - 10);
+		info->max <<= (info->clustersize_bits - 10);
+		info->avg /= info->free_chunks_real;
+		info->avg <<= (info->clustersize_bits - 10);
+	}
+
+out:
+	if (block)
+		ocfs2_free(&block);
+
+	return ret;
+}
+
+static int get_freefrag_info_ioctl(struct o2info_operation *op,
+				   int fd,
+				   struct o2info_chunk_info *info)
+{
+	int ret = 0;
+	struct ocfs2_info_freefrag frq;
+
+	o2info_fill_request(&frq, OCFS2_INFO_FREEFRAG, no_coherency);
+	frq.ir_chunksize = info->clusters_in_chunk;
+
+	uint64_t reqs[1] = {(unsigned long)&frq};
+
+	struct ocfs2_info o_info = {
+		.info_requests = (uint64_t)reqs,
+		.info_count = 1,
+	};
+
+	ret = ioctl(fd, OCFS2_IOC_INFO, &o_info);
+	if (ret) {
+		ret = errno;
+		o2i_error(op, "ioctl failed: %s\n", strerror(ret));
+		goto out;
+	}
+
+	if (frq.ir_request.ir_flags & OCFS2_INFO_FL_FILLED) {
+		info->clusters = frq.ir_ffg.ir_clusters;
+		info->free_clusters = frq.ir_ffg.ir_free_clusters;
+		info->free_chunks = frq.ir_ffg.ir_free_chunks;
+		info->free_chunks_real = frq.ir_ffg.ir_free_chunks_real;
+		if (info->free_chunks_real) {
+			info->min = frq.ir_ffg.ir_min <<
+					(info->clustersize_bits - 10);
+			info->max = frq.ir_ffg.ir_max <<
+					(info->clustersize_bits - 10);
+			info->avg = frq.ir_ffg.ir_avg <<
+					(info->clustersize_bits - 10);
+		} else
+			info->min = 0;
+
+		memcpy(&(info->histogram), &(frq.ir_ffg.ir_fc_hist),
+		       sizeof(struct free_chunk_histogram));
+	}
+
+	info->total_chunks = (info->clusters + info->clusters_in_chunk) >>
+				(info->chunkbits - info->clustersize_bits);
+out:
+	return ret;
+}
+
+static void report_freefrag_info(struct o2info_chunk_info *info)
+{
+	char *unitp = "KMGTPEZY";
+	char end_str[32];
+
+	int i, unit = 10; /* Begin from KB in terms of 10 bits */
+	unsigned int start = 0, end;
+
+	fprintf(stdout, "Blocksize: %u bytes\n", 1 << info->blksize_bits);
+	fprintf(stdout, "Clustersize: %u bytes\n", 1 << info->clustersize_bits);
+	fprintf(stdout, "Total clusters: %llu\nFree clusters: %u (%0.1f%%)\n",
+		info->clusters, info->free_clusters,
+		(double)info->free_clusters * 100 / info->clusters);
+
+	fprintf(stdout, "\nMin. free extent: %u KB \nMax. free extent: %u KB\n"
+		"Avg. free extent: %u KB\n", info->min, info->max, info->avg);
+
+	if (info->chunkbytes) {
+		fprintf(stdout, "\nChunksize: %lu bytes (%u clusters)\n",
+			info->chunkbytes, info->clusters_in_chunk);
+
+		fprintf(stdout, "Total chunks: %u\nFree chunks: %u (%0.1f%%)\n",
+			info->total_chunks, info->free_chunks,
+			(double)info->free_chunks * 100 / info->total_chunks);
+	}
+
+	fprintf(stdout, "\nHISTOGRAM OF FREE EXTENT SIZES:\n");
+	fprintf(stdout, "%s :  %12s  %12s  %7s\n", "Extent Size Range",
+		"Free extents", "Free Clusters", "Percent");
+
+	/*
+	 * We probably need to start with 'M' when clustersize = 1M.
+	 */
+	start = 1 << (info->clustersize_bits - unit);
+	if (start == (1 << 10)) {
+		unit += 10;
+		unitp++;
+	}
+
+	for (i = 0; i < OCFS2_INFO_MAX_HIST; i++) {
+
+		start = 1 << (i + info->clustersize_bits - unit);
+		end = start << 1;
+
+		if (info->histogram.fc_chunks[i] != 0) {
+			snprintf(end_str, 32,  "%5lu%c-", end, *unitp);
+			if (i == (OCFS2_INFO_MAX_HIST - 1))
+				strcpy(end_str, "max ");
+			fprintf(stdout, "%5u%c...%7s  :  "
+				"%12u  %12u  %6.2f%%\n",
+				start, *unitp, end_str,
+				info->histogram.fc_chunks[i],
+				info->histogram.fc_clusters[i],
+				(double)info->histogram.fc_clusters[i] * 100 /
+				info->free_clusters);
+		}
+
+		start = end;
+		if (start == (1 << 10)) {
+			unit += 10;
+			unitp++;
+			if (!(*unitp))
+				break;
+		}
+	}
+}
+
+static int get_freefrag_info(struct o2info_operation *op,
+			     struct o2info_method *om,
+			     struct o2info_chunk_info *info)
+{
+	int ret = 0;
+
+	if (om->om_method == O2INFO_USE_LIBOCFS2)
+		ret = get_freefrag_info_libocfs2(op, om->om_fs, info);
+	else
+		ret = get_freefrag_info_ioctl(op, om->om_fd, info);
+	if (ret)
+		return ret;
+
+	report_freefrag_info(info);
+
+	return ret;
+}
+
+static int freefrag_run(struct o2info_operation *op,
+			struct o2info_method *om,
+			void *arg)
+{
+	int ret = 0;
+	static struct o2info_chunk_info info;
+	static struct o2info_volinfo vf;
+	char *end;
+
+	info.chunkbytes = strtoull((char *)arg, &end, 0);
+	if (*end != '\0') {
+		o2i_error(op, "bad chunk size '%s'\n", (char *)arg);
+		ret = -1;
+		print_usage(ret);
+	}
+
+	if (info.chunkbytes & (info.chunkbytes - 1)) {
+		o2i_error(op, "chunksize needs to be power of 2\n");
+		ret = -1;
+		print_usage(ret);
+	}
+
+	info.chunkbytes *= 1024;
+
+	if (om->om_method == O2INFO_USE_IOCTL)
+		ret = get_volinfo_ioctl(op, om->om_fd, &vf);
+	else
+		ret = get_volinfo_libocfs2(op, om->om_fs, &vf);
+	if (ret)
+		return -1;
+
+	if (info.chunkbytes &&
+	    (info.chunkbytes < vf.clustersize)) {
+		o2i_error(op, "chunksize should be greater than or equal to "
+			  "filesystem cluster size\n");
+		ret = -1;
+		print_usage(ret);
+	}
+
+	ret = init_freefrag_info(&info, &vf);
+	if (ret)
+		return -1;
+
+	ret = get_freefrag_info(op, om, &info);
+
+	return ret;
+}
+
+DEFINE_O2INFO_OP(freefrag,
+		 freefrag_run,
+		 NULL);
-- 
1.5.5




More information about the Ocfs2-tools-devel mailing list