[Ocfs2-tools-devel] [PATCH 07/11] Ocfs2-tools: Add implementation of operations to o2info.

Tristan Ye tristan.ye at oracle.com
Fri Jan 8 00:50:57 PST 2010


All of o2info's operations will be linked to a task list,
and executed one by one later, the real running code of
a new item for o2info is expected to be added there.

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

diff --git a/o2info/o2info_operations.c b/o2info/o2info_operations.c
new file mode 100644
index 0000000..008e68e
--- /dev/null
+++ b/o2info/o2info_operations.c
@@ -0,0 +1,1198 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * o2info_operations.c
+ *
+ * Operation helpers for o2info utility
+ *
+ * Copyright (C) 2009 Oracle.  All rights reserved.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#define _XOPEN_SOURCE 600
+#define _LARGEFILE64_SOURCE
+#define _GNU_SOURCE /* Because libc really doesn't want us using O_DIRECT? */
+
+#include <errno.h>
+#include <sys/raw.h>
+#include <inttypes.h>
+
+#include "ocfs2/ocfs2.h"
+#include "ocfs2/bitops.h"
+#include "ocfs2-kernel/ocfs2_ioctl.h"
+#include "ocfs2-kernel/kernel-list.h"
+#include "ocfs2-kernel/fiemap.h"
+#include "tools-internal/verbose.h"
+
+#include "o2info_utils.h"
+
+static inline void __o2info_fill_request_header(struct ocfs2_info_request *req,
+						size_t size, int code,
+						int flags)
+{
+	req->ir_magic = OCFS2_INFO_MAGIC;
+	req->ir_code = code;
+	req->ir_flags = flags;
+	req->ir_size = size;
+}
+
+#define o2info_fill_request(req, code, flags)		do {		\
+	memset(req, 0, sizeof(*req));					\
+	__o2info_fill_request_header((struct ocfs2_info_request *)req,	\
+				     sizeof(*req), code, flags);	\
+} while (0)
+
+struct o2info_volinfo {
+	uint32_t blocksize;
+	uint32_t clustersize;
+	uint16_t slotnum;
+	uint8_t label[OCFS2_INFO_MAX_VOL_LABEL_LEN];
+	union {
+		uint8_t uuid_str[OCFS2_INFO_VOL_UUIDSTR_LEN];
+		uint8_t uuid[OCFS2_INFO_VOL_UUID_LEN];
+	};
+};
+
+extern void print_usage(int rc);
+extern int no_coherency;
+
+static void o2info_fprintf(struct o2info_operation *op,
+			   FILE *stream, const char *fmt, ...)
+{
+	va_list ap;
+
+	fprintf(stream, "%s: ", op->to_name);
+	va_start(ap, fmt);
+	vfprintf(stream, fmt, ap);
+
+	return;
+}
+
+static int get_volinfo(struct o2info_operation *op, struct o2info_method *om,
+		       struct o2info_volinfo *vf)
+{
+	int rc = 0;
+	struct ocfs2_super_block *sb = NULL;
+
+	memset(vf, 0, sizeof(struct o2info_volinfo));
+
+	if (om->om_method & O2INFO_USE_LIBOCFS2) {
+
+		sb = OCFS2_RAW_SB(om->om_fs->fs_super);
+		vf->blocksize = om->om_fs->fs_blocksize;
+		vf->clustersize = om->om_fs->fs_clustersize;
+		vf->slotnum = sb->s_max_slots;
+		memcpy(vf->label, sb->s_label, OCFS2_INFO_MAX_VOL_LABEL_LEN);
+		memcpy(vf->uuid, sb->s_uuid, OCFS2_INFO_VOL_UUID_LEN);
+		goto out;
+	}
+
+	if (om->om_method & O2INFO_USE_IOCTL) {
+
+		struct ocfs2_info_blocksize brq;
+		struct ocfs2_info_clustersize crq;
+		struct ocfs2_info_slotnum srq;
+		struct ocfs2_info_label lrq;
+		struct ocfs2_info_uuid urq;
+
+		o2info_fill_request(&brq, OCFS2_INFO_BLOCKSIZE, no_coherency);
+		o2info_fill_request(&crq, OCFS2_INFO_CLUSTERSIZE, no_coherency);
+		o2info_fill_request(&srq, OCFS2_INFO_SLOTNUM, no_coherency);
+		o2info_fill_request(&lrq, OCFS2_INFO_LABEL, no_coherency);
+		o2info_fill_request(&urq, OCFS2_INFO_UUID, no_coherency);
+
+		uint64_t reqs[5] = {(unsigned long)&brq,
+				    (unsigned long)&crq,
+				    (unsigned long)&srq,
+				    (unsigned long)&lrq,
+				    (unsigned long)&urq};
+
+		struct ocfs2_info info = {
+			.info_requests = (uint64_t)reqs,
+			.info_count = 5,
+		};
+
+		rc = ioctl(om->om_fd, OCFS2_IOC_INFO, &info);
+		if (rc) {
+			rc = errno;
+			o2info_fprintf(op, stderr, "ioctl failed:%d %s\n",
+				       rc, strerror(rc));
+			goto out;
+		}
+
+		if (brq.ir_request.ir_flags & OCFS2_INFO_FL_FILLED)
+			vf->blocksize = brq.ir_blocksize;
+
+		if (crq.ir_request.ir_flags & OCFS2_INFO_FL_FILLED)
+			vf->clustersize = crq.ir_clustersize;
+
+		if (srq.ir_request.ir_flags & OCFS2_INFO_FL_FILLED)
+			vf->slotnum = srq.ir_slotnum;
+
+		if (lrq.ir_request.ir_flags & OCFS2_INFO_FL_FILLED)
+			memcpy(vf->label, lrq.ir_label,
+			       OCFS2_INFO_MAX_VOL_LABEL_LEN);
+
+		if (urq.ir_request.ir_flags & OCFS2_INFO_FL_FILLED)
+			memcpy(vf->uuid_str, urq.ir_uuid_str,
+			       OCFS2_INFO_VOL_UUIDSTR_LEN);
+
+		goto out;
+	}
+
+out:
+	return rc;
+}
+
+static int volinfo_run(struct o2info_operation *op,
+		       struct o2info_method *om,
+		       void *arg)
+{
+	int i, rc = 0;
+	static struct o2info_volinfo vf;
+
+	const char *items[] = {
+		"Block Size:",
+		"Cluster Size:",
+		"Node Slots:",
+		"Label:",
+		"UUID:"
+	};
+
+	rc = get_volinfo(op, om, &vf);
+	if (rc)
+		goto out;
+
+	fprintf(stdout, "  %s %u\n", items[0], vf.blocksize);
+	fprintf(stdout, "%s %u\n", items[1], vf.clustersize);
+	fprintf(stdout, "  %s %u\n", items[2], vf.slotnum);
+	fprintf(stdout, "       %s %.*s\n", items[3],
+		OCFS2_INFO_MAX_VOL_LABEL_LEN, vf.label);
+	fprintf(stdout, "        %s ", items[4]);
+	if (om->om_method & O2INFO_USE_IOCTL) {
+		for (i = 0; i < OCFS2_INFO_VOL_UUIDSTR_LEN; i++)
+			fprintf(stdout, "%c", vf.uuid_str[i]);
+	} else {
+		for (i = 0; i < OCFS2_INFO_VOL_UUID_LEN; i++)
+			fprintf(stdout, "%02X", vf.uuid[i]);
+	}
+
+	fprintf(stdout, "\n");
+out:
+	return rc;
+}
+
+DEFINE_O2INFO_OP(volinfo,
+		 volinfo_run,
+		 NULL);
+
+struct o2info_fs_features {
+	uint32_t compat;
+	uint32_t incompat;
+	uint32_t rocompat;
+};
+
+static int get_fs_features(struct o2info_operation *op,
+			   struct o2info_method *om,
+			   struct o2info_fs_features *ofs)
+{
+	int rc = 0;
+	struct ocfs2_super_block *sb = NULL;
+
+	memset(ofs, 0, sizeof(struct o2info_fs_features));
+
+	if (om->om_method & O2INFO_USE_LIBOCFS2) {
+		sb = OCFS2_RAW_SB(om->om_fs->fs_super);
+		ofs->compat = sb->s_feature_compat;
+		ofs->incompat = sb->s_feature_incompat;
+		ofs->rocompat = sb->s_feature_ro_compat;
+		goto out;
+	}
+
+	if (om->om_method & O2INFO_USE_IOCTL) {
+
+		struct ocfs2_info_fs_features frq;
+		o2info_fill_request(&frq, OCFS2_INFO_FS_FEATURES,
+				    no_coherency);
+
+		uint64_t reqs[1] = {(unsigned long)&frq};
+
+		struct ocfs2_info info = {
+			.info_requests = (uint64_t)reqs,
+			.info_count = 1,
+		};
+
+		rc = ioctl(om->om_fd, OCFS2_IOC_INFO, &info);
+		if (rc) {
+			rc = errno;
+			o2info_fprintf(op, stderr, "ioctl failed:%d %s\n",
+				       rc, strerror(rc));
+			goto out;
+		}
+
+		if (frq.ir_request.ir_flags & OCFS2_INFO_FL_FILLED) {
+			ofs->compat = frq.ir_compat_features;
+			ofs->incompat = frq.ir_incompat_features;
+			ofs->rocompat = frq.ir_ro_compat_features;
+		}
+	}
+out:
+	return rc;
+}
+
+static int fs_features_run(struct o2info_operation *op,
+			   struct o2info_method *om,
+			   void *arg)
+{
+	int rc = 0;
+	static struct o2info_fs_features ofs;
+
+	GString *compat = NULL;
+	GString *incompat = NULL;
+	GString *rocompat = NULL;
+
+	const char *items[] = {
+		"Feature Compat:",
+		"Feature Incompat:",
+		"Feature RO compat:"
+	};
+
+	compat = g_string_new(NULL);
+	incompat = g_string_new(NULL);
+	rocompat = g_string_new(NULL);
+
+	rc = get_fs_features(op, om, &ofs);
+
+	o2info_get_compat_flag(ofs.compat, compat);
+	o2info_get_incompat_flag(ofs.incompat, incompat);
+	o2info_get_rocompat_flag(ofs.rocompat, rocompat);
+
+	fprintf(stdout, "   %s %u %s\n", items[0], ofs.compat,
+		compat->str);
+	fprintf(stdout, " %s %u %s\n", items[1], ofs.incompat,
+		incompat->str);
+	fprintf(stdout, "%s %u %s\n", items[2], ofs.rocompat,
+		rocompat->str);
+
+	g_string_free(compat, 1);
+	g_string_free(incompat, 1);
+	g_string_free(rocompat, 1);
+
+	return rc;
+}
+
+DEFINE_O2INFO_OP(fs_features,
+		 fs_features_run,
+		 NULL);
+/*
+ * File statistics from fiemap.
+ */
+struct o2info_fiemap_stats {
+	uint32_t blocksize;
+	uint32_t clustersize;
+	uint32_t num_extents;
+	uint32_t num_extents_xattr;
+	uint32_t clusters;
+	float frag;
+	float score;
+	uint32_t shared;
+	uint32_t holes;
+	uint32_t unwrittens;
+	uint32_t xattr;
+};
+
+static int ul_log2(unsigned long arg)
+{
+	unsigned int i = 0;
+
+	arg >>= 1;
+	while (arg) {
+		i++;
+		arg >>= 1;
+	}
+
+	return i;
+}
+
+static int figure_extents(int fd, uint32_t *num, int flags)
+{
+	int ret;
+	static struct fiemap fiemap;
+
+	fiemap.fm_start = 0ULL;
+	fiemap.fm_length = FIEMAP_MAX_OFFSET;
+
+	if (flags & FIEMAP_FLAG_XATTR)
+		fiemap.fm_flags = FIEMAP_FLAG_XATTR;
+
+	fiemap.fm_extent_count = 0;
+	ret = ioctl(fd, FS_IOC_FIEMAP, &fiemap);
+	if (ret < 0) {
+		ret = errno;
+		tcom_err(ret, "fiemap get count error");
+		return -1;
+	}
+
+	*num = fiemap.fm_mapped_extents;
+
+	return 0;
+}
+
+static uint32_t clusters_in_bytes(uint32_t clustersize, uint32_t bytes)
+{
+	uint64_t ret = bytes + clustersize - 1;
+
+	if (ret < bytes)
+		ret = UINT64_MAX;
+
+	ret = ret >> ul_log2(clustersize);
+	if (ret > UINT32_MAX)
+		ret = UINT32_MAX;
+
+	return (uint32_t)ret;
+}
+
+static int do_fiemap(int fd, int flags, struct o2info_fiemap_stats *ofs,
+		     struct o2info_operation *op)
+{
+
+	char buf[4096];
+
+	int ret = 0, last = 0;
+	int cluster_shift = 0, blk_shift = 0;
+	int count = (sizeof(buf) - sizeof(struct fiemap)) /
+		     sizeof(struct fiemap_extent);
+
+	struct fiemap *fiemap = (struct fiemap *)buf;
+	struct fiemap_extent *fm_ext = &fiemap->fm_extents[0];
+	uint32_t num_extents = 0, extents_got = 0, i;
+
+	uint32_t prev_start = 0, prev_len = 0;
+	uint32_t start = 0, len = 0, phy_pos = 0;
+
+	if (ofs->clustersize)
+		cluster_shift = ul_log2(ofs->clustersize);
+
+	if (ofs->blocksize)
+		blk_shift = ul_log2(ofs->blocksize);
+
+	memset(fiemap, 0, sizeof(struct fiemap));
+
+	ret = figure_extents(fd, &num_extents, 0);
+	if (ret)
+		return -1;
+
+	do {
+		fiemap->fm_length = ~0ULL;
+		if (flags & FIEMAP_FLAG_XATTR)
+			fiemap->fm_flags = FIEMAP_FLAG_XATTR;
+		else
+			fiemap->fm_flags = flags;
+		fiemap->fm_extent_count = count;
+
+		ret = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap);
+		if (ret < 0) {
+			ret = errno;
+			if (errno == EBADR) {
+				o2info_fprintf(op, stderr, "fiemap failed with"
+					       " unsupported flags %x\n",
+					       fiemap->fm_flags);
+			} else
+				o2info_fprintf(op, stderr, "fiemap error "
+					       "%d: \"%s\"\n", ret,
+					       strerror(ret));
+			return -1;
+		}
+
+		if (!fiemap->fm_mapped_extents)
+			break;
+
+		for (i = 0; i < fiemap->fm_mapped_extents; i++) {
+
+			start = fm_ext[i].fe_logical >> cluster_shift;
+			len = fm_ext[i].fe_length >> cluster_shift;
+			phy_pos = fm_ext[i].fe_physical >> blk_shift;
+
+			if (fiemap->fm_flags & FIEMAP_FLAG_XATTR) {
+				ofs->xattr += len;
+			} else {
+				if (fm_ext[i].fe_flags &
+				    FIEMAP_EXTENT_UNWRITTEN)
+					ofs->unwrittens += len;
+
+				if (fm_ext[i].fe_flags & FIEMAP_EXTENT_SHARED)
+					ofs->shared += len;
+
+				if ((prev_start + prev_len) < start)
+					ofs->holes += start - prev_start -
+						      prev_len;
+				}
+
+			if (fm_ext[i].fe_flags & FIEMAP_EXTENT_LAST)
+				last = 1;
+
+			prev_start = start;
+			prev_len = len;
+
+			extents_got++;
+			ofs->clusters += len;
+		}
+
+		fiemap->fm_start = (fm_ext[i-1].fe_logical +
+				    fm_ext[i-1].fe_length);
+	} while (!last);
+
+	if (extents_got != num_extents) {
+		o2info_fprintf(op, stderr, "Got wrong extents number, expected:"
+			       "%lu, got:%lu\n", num_extents, extents_got);
+		return -1;
+	}
+
+	if (flags & FIEMAP_FLAG_XATTR)
+		ofs->num_extents_xattr = num_extents;
+	else
+		ofs->num_extents = num_extents;
+
+	return ret;
+}
+
+static int get_fiemap_stats(struct o2info_operation *op,
+			    struct o2info_method *om,
+			    struct o2info_fiemap_stats *ofs)
+{
+	int ret = 0, flags = 0;
+	static struct o2info_volinfo vf;
+
+	memset(ofs, 0, sizeof(struct o2info_fiemap_stats));
+
+	ret = get_volinfo(op, om, &vf);
+	if (ret)
+		return -1;
+
+	ofs->blocksize = vf.blocksize;
+	ofs->clustersize = vf.clustersize;
+
+	ret = do_fiemap(om->om_fd, flags, ofs, op);
+	if (ret)
+		return ret;
+
+	/*
+	ret = do_fiemap(om->om_fd, FIEMAP_FLAG_SYNC, ofs, op);
+	if (ret)
+		return ret;
+	*/
+
+	if (ofs->clusters > 1 && ofs->num_extents) {
+		float e = ofs->num_extents, c = ofs->clusters;
+		int clusters_per_mb = clusters_in_bytes(ofs->clustersize,
+							OCFS2_MAX_CLUSTERSIZE);
+		ofs->frag = 100 * (e / c);
+		ofs->score = ofs->frag * clusters_per_mb;
+	}
+
+	return ret;
+}
+
+static int usage_run(struct o2info_operation *op,
+		     struct o2info_method *om,
+		     void *arg)
+{
+	int rc = 0;
+	struct stat st;
+	static struct o2info_fiemap_stats ofs;
+
+	rc = lstat(om->om_path, &st);
+	if (rc < 0) {
+		rc = errno;
+		o2info_fprintf(op, stderr, "stat error %d: \"%s\"\n",
+			       rc, strerror(rc));
+		return -1;
+	}
+
+	rc = get_fiemap_stats(op, om, &ofs);
+	if (rc)
+		return rc;
+
+	fprintf(stdout, "Blocks: %-10u Shared: %-10u\tUnwritten: %-7u "
+		"Holes: %-6u\n", st.st_blocks, ofs.shared, ofs.unwrittens,
+		ofs.holes);
+
+	return rc;
+}
+
+DEFINE_O2INFO_OP(usage,
+		 usage_run,
+		 NULL);
+
+static int filestat_run(struct o2info_operation *op,
+			struct o2info_method *om,
+			void *arg)
+{
+	int rc = 0;
+	uint16_t perm;
+	struct stat st;
+
+	char *path = NULL;
+	char *filetype = NULL, *h_perm = NULL;
+	char *uname = NULL, *gname = NULL;
+	char *ah_time = NULL, *ch_time = NULL, *mh_time = NULL;
+
+	static struct o2info_fiemap_stats ofs;
+
+	rc = lstat(om->om_path, &st);
+	if (rc < 0) {
+		rc = errno;
+		o2info_fprintf(op, stderr, "stat error %d: \"%s\"\n",
+			       rc, strerror(rc));
+		goto out;
+	}
+
+	rc = o2info_get_human_path(st.st_mode, om->om_path, &path);
+	if (rc)
+		goto out;
+
+	rc = o2info_get_filetype(st, &filetype);
+	if (rc)
+		goto out;
+
+	rc = o2info_uid2name(st.st_uid, &uname);
+	if (rc)
+		goto out;
+
+	rc = o2info_gid2name(st.st_gid, &gname);
+	if (rc)
+		goto out;
+
+	rc = o2info_get_human_permission(st.st_mode, &perm, &h_perm);
+	if (rc)
+		goto out;
+
+	rc = get_fiemap_stats(op, om, &ofs);
+	if (rc)
+		goto out;
+
+	if (!ofs.blocksize)
+		ofs.blocksize = st.st_blksize;
+
+	fprintf(stdout, "  File: %s\n", path);
+	fprintf(stdout, "  Size: %-10d\tBlocks: %-10u IO Block: %-6u %s\n",
+		st.st_size, st.st_blocks, ofs.blocksize, filetype);
+	 if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
+		fprintf(stdout, "Device: %xh/%dd\tInode: %-10i  Links: %-5u"
+			" Device type: %u,%u\n", st.st_dev, st.st_dev,
+			st.st_ino, st.st_nlink,
+			st.st_dev >> 16UL, st.st_dev & 0x0000FFFF);
+	else
+		fprintf(stdout, "Device: %xh/%dd\tInode: %-10i  Links: %u\n",
+			st.st_dev, st.st_dev, st.st_ino, st.st_nlink);
+	fprintf(stdout, " Frag%: %-10.2f\tClusters: %-8u Extents: "
+		"%-6lu Score: %.0f\n", ofs.frag, ofs.clusters,
+		ofs.num_extents, ofs.score);
+	fprintf(stdout, "Shared: %-10u\tUnwritten: %-7u Holes: %-8u "
+		"Xattr: %u\n", ofs.shared, ofs.unwrittens,
+		ofs.holes, ofs.xattr);
+	fprintf(stdout, "Access: (%04o/%10s)  Uid: (%5u/%8s)   "
+		"Gid: (%5u/%8s)\n", perm, h_perm, st.st_uid,
+		uname, st.st_gid, gname);
+
+	rc = o2info_get_human_time(&ah_time, o2info_get_stat_atime(&st));
+	if (rc)
+		goto out;
+	rc = o2info_get_human_time(&mh_time, o2info_get_stat_mtime(&st));
+	if (rc)
+		goto out;
+	rc = o2info_get_human_time(&ch_time, o2info_get_stat_ctime(&st));
+	if (rc)
+		goto out;
+
+	fprintf(stdout, "Access: %s\n", ah_time);
+	fprintf(stdout, "Modify: %s\n", mh_time);
+	fprintf(stdout, "Change: %s\n", ch_time);
+
+out:
+	if (path)
+		ocfs2_free(&path);
+	if (filetype)
+		ocfs2_free(&filetype);
+	if (uname)
+		ocfs2_free(&uname);
+	if (gname)
+		ocfs2_free(&gname);
+	if (h_perm)
+		ocfs2_free(&h_perm);
+	if (ah_time)
+		ocfs2_free(&ah_time);
+	if (mh_time)
+		ocfs2_free(&mh_time);
+	if (ch_time)
+		ocfs2_free(&ch_time);
+
+	return rc;
+}
+
+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;
+		o2info_fprintf(op, stderr, "ioctl failed:%d %s\n",
+			       ret, 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");
+
+	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') {
+		o2info_fprintf(op, stderr, "bad chunk size '%s'\n",
+			       (char *)arg);
+		ret = -1;
+		print_usage(ret);
+	}
+
+	if (info.chunkbytes & (info.chunkbytes - 1)) {
+		o2info_fprintf(op, stderr, "chunksize should be a "
+			       "power of 2\n", (char *)arg);
+		ret = -1;
+		print_usage(ret);
+	}
+
+	info.chunkbytes *= 1024;
+
+	ret = get_volinfo(op, om, &vf);
+	if (ret)
+		return -1;
+
+	if (info.chunkbytes &&
+	    (info.chunkbytes < vf.clustersize)) {
+		o2info_fprintf(op, stderr, "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);
+
+struct o2info_local_freeinode {
+	unsigned long total;
+	unsigned long free;
+};
+
+struct o2info_freeinode_info {
+	int slotnum;
+	struct o2info_local_freeinode fi[OCFS2_MAX_SLOTS];
+};
+
+static int get_freeinode_libocfs2(struct o2info_operation *op,
+				 ocfs2_filesys *fs,
+				 struct o2info_freeinode_info *info)
+{
+	int ret = 0, i, j;
+	char *block = NULL;
+	uint64_t inode_alloc;
+
+	struct ocfs2_dinode *dinode_alloc = NULL;
+	struct ocfs2_chain_list *cl = NULL;
+	struct ocfs2_chain_rec *rec = NULL;
+	struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super);
+
+	info->slotnum = sb->s_max_slots;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &block);
+	if (ret) {
+		tcom_err(ret, "while allocating block buffer");
+		goto out;
+	}
+
+	dinode_alloc = (struct ocfs2_dinode *)block;
+
+	for (i = 0; i < info->slotnum; i++) {
+
+		info->fi[i].total = info->fi[i].free = 0;
+
+		ret = ocfs2_lookup_system_inode(fs, INODE_ALLOC_SYSTEM_INODE,
+						i, &inode_alloc);
+		if (ret) {
+			tcom_err(ret, "while looking up the global"
+				 " bitmap inode");
+			goto out;
+		}
+
+		ret = ocfs2_read_inode(fs, inode_alloc, (char *)dinode_alloc);
+		if (ret) {
+			tcom_err(ret, "reading global_bitmap inode "
+				 "%"PRIu64" for stats", inode_alloc);
+			goto out;
+		}
+
+		cl = &(dinode_alloc->id2.i_chain);
+
+		for (j = 0; j < cl->cl_next_free_rec; j++) {
+			rec = &(cl->cl_recs[j]);
+			info->fi[i].total += rec->c_total;
+			info->fi[i].free += rec->c_free;
+		}
+	}
+out:
+	if (block)
+		ocfs2_free(&block);
+
+	return ret;
+}
+
+static int get_freeinode_ioctl(struct o2info_operation *op,
+			       int fd,
+			       struct o2info_freeinode_info *info)
+{
+	int ret = 0;
+	struct ocfs2_info_freeinode firq;
+
+	o2info_fill_request(&firq, OCFS2_INFO_FREEINODE, no_coherency);
+
+	uint64_t reqs[1] = {(unsigned long)&firq};
+
+	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;
+		o2info_fprintf(op, stderr, "ioctl failed:%d %s\n",
+			       ret, strerror(ret));
+		ret = -1;
+	}
+
+	if (firq.ir_request.ir_flags & OCFS2_INFO_FL_FILLED) {
+		info->slotnum = firq.ir_slotnum;
+		memcpy(info->fi, firq.ir_fi_stat,
+		       sizeof(struct o2info_local_freeinode) * OCFS2_MAX_SLOTS);
+	}
+
+	return ret;
+}
+
+static int freeinode_run(struct o2info_operation *op,
+			struct o2info_method *om,
+			void *arg)
+{
+	int ret = 0, i;
+
+	struct o2info_freeinode_info info;
+	unsigned long total = 0, free = 0;
+
+	if (om->om_method & O2INFO_USE_IOCTL)
+		ret = get_freeinode_ioctl(op, om->om_fd, &info);
+	else
+		ret = get_freeinode_libocfs2(op, om->om_fs, &info);
+
+	if (ret)
+		return ret;
+
+	fprintf(stdout, "Slot\t\tSpace\t\tFree\n");
+	for (i = 0; i < info.slotnum ; i++) {
+		fprintf(stdout, "%3d\t%13lu\t%12lu\n", i, info.fi[i].total,
+			info.fi[i].free);
+		total += info.fi[i].total;
+		free += info.fi[i].free;
+	}
+
+	fprintf(stdout, "Total\t%13lu\t%12lu\n", total, free);
+
+	return ret;
+}
+
+DEFINE_O2INFO_OP(freeinode,
+		 freeinode_run,
+		 NULL);
-- 
1.5.5




More information about the Ocfs2-tools-devel mailing list