[Ocfs2-tools-devel] [PATCH 4/5] O2info: Add new running codes for '--space-usage'.

Tristan Ye tristan.ye at oracle.com
Sat Jan 29 22:31:27 PST 2011


This patch tries to teach o2info to dump file's usage information on disk in
terms of calculating its number of physical block, holes, reseverd/shared clusters
with the help of new 'FS_IOC_FIEMAP' ioctl.

Signed-off-by: Tristan Ye <tristan.ye at oracle.com>
---
 include/ocfs2-kernel/fiemap.h |   68 +++++++++++++++++
 o2info/libo2info.c            |  163 +++++++++++++++++++++++++++++++++++++++++
 o2info/libo2info.h            |   15 ++++
 o2info/o2info.1.in            |    6 +-
 o2info/o2info.c               |   15 ++++
 o2info/operations.c           |   48 ++++++++++++
 6 files changed, 314 insertions(+), 1 deletions(-)
 create mode 100644 include/ocfs2-kernel/fiemap.h

diff --git a/include/ocfs2-kernel/fiemap.h b/include/ocfs2-kernel/fiemap.h
new file mode 100644
index 0000000..d830747
--- /dev/null
+++ b/include/ocfs2-kernel/fiemap.h
@@ -0,0 +1,68 @@
+/*
+ * FS_IOC_FIEMAP ioctl infrastructure.
+ *
+ * Some portions copyright (C) 2007 Cluster File Systems, Inc
+ *
+ * Authors: Mark Fasheh <mfasheh at suse.com>
+ *          Kalpak Shah <kalpak.shah at sun.com>
+ *          Andreas Dilger <adilger at sun.com>
+ */
+
+#ifndef _LINUX_FIEMAP_H
+#define _LINUX_FIEMAP_H
+
+#include <linux/types.h>
+
+struct fiemap_extent {
+	__u64 fe_logical;  /* logical offset in bytes for the start of
+			    * the extent from the beginning of the file */
+	__u64 fe_physical; /* physical offset in bytes for the start
+			    * of the extent from the beginning of the disk */
+	__u64 fe_length;   /* length in bytes for this extent */
+	__u64 fe_reserved64[2];
+	__u32 fe_flags;    /* FIEMAP_EXTENT_* flags for this extent */
+	__u32 fe_reserved[3];
+};
+
+struct fiemap {
+	__u64 fm_start;		/* logical offset (inclusive) at
+				 * which to start mapping (in) */
+	__u64 fm_length;	/* logical length of mapping which
+				 * userspace wants (in) */
+	__u32 fm_flags;		/* FIEMAP_FLAG_* flags for request (in/out) */
+	__u32 fm_mapped_extents;/* number of extents that were mapped (out) */
+	__u32 fm_extent_count;  /* size of fm_extents array (in) */
+	__u32 fm_reserved;
+	struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */
+};
+
+#define FIEMAP_MAX_OFFSET	(~0ULL)
+
+#define FIEMAP_FLAG_SYNC	0x00000001 /* sync file data before map */
+#define FIEMAP_FLAG_XATTR	0x00000002 /* map extended attribute tree */
+
+#define FIEMAP_FLAGS_COMPAT	(FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR)
+
+#define FIEMAP_EXTENT_LAST		0x00000001 /* Last extent in file. */
+#define FIEMAP_EXTENT_UNKNOWN		0x00000002 /* Data location unknown. */
+#define FIEMAP_EXTENT_DELALLOC		0x00000004 /* Location still pending.
+						    * Sets EXTENT_UNKNOWN. */
+#define FIEMAP_EXTENT_ENCODED		0x00000008 /* Data can not be read
+						    * while fs is unmounted */
+#define FIEMAP_EXTENT_DATA_ENCRYPTED	0x00000080 /* Data is encrypted by fs.
+						    * Sets EXTENT_NO_BYPASS. */
+#define FIEMAP_EXTENT_NOT_ALIGNED	0x00000100 /* Extent offsets may not be
+						    * block aligned. */
+#define FIEMAP_EXTENT_DATA_INLINE	0x00000200 /* Data mixed with metadata.
+						    * Sets EXTENT_NOT_ALIGNED.*/
+#define FIEMAP_EXTENT_DATA_TAIL		0x00000400 /* Multiple files in block.
+						    * Sets EXTENT_NOT_ALIGNED.*/
+#define FIEMAP_EXTENT_UNWRITTEN		0x00000800 /* Space allocated, but
+						    * no data (i.e. zero). */
+#define FIEMAP_EXTENT_MERGED		0x00001000 /* File does not natively
+						    * support extents. Result
+						    * merged for efficiency. */
+#define FIEMAP_EXTENT_SHARED		0x00002000 /* Space shared with other
+						    * files. */
+
+#endif /* _LINUX_FIEMAP_H */
diff --git a/o2info/libo2info.c b/o2info/libo2info.c
index d716151..7bd8567 100644
--- a/o2info/libo2info.c
+++ b/o2info/libo2info.c
@@ -21,9 +21,14 @@
 #define _LARGEFILE64_SOURCE
 #define _GNU_SOURCE
 
+#include <errno.h>
 #include <inttypes.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
 
 #include "ocfs2/ocfs2.h"
+#include "ocfs2/bitops.h"
+#include "ocfs2-kernel/fiemap.h"
 #include "tools-internal/verbose.h"
 #include "libo2info.h"
 
@@ -356,3 +361,161 @@ out:
 
 	return ret;
 }
+
+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 *ofp)
+{
+	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 (ofp->clustersize)
+		cluster_shift = ul_log2(ofp->clustersize);
+
+	if (ofp->blocksize)
+		blk_shift = ul_log2(ofp->blocksize);
+
+	memset(fiemap, 0, sizeof(*fiemap));
+
+	ret = figure_extents(fd, &num_extents, 0);
+	if (ret)
+		return -1;
+
+	if (flags & FIEMAP_FLAG_XATTR)
+		fiemap->fm_flags = FIEMAP_FLAG_XATTR;
+	else
+		fiemap->fm_flags = flags;
+
+	do {
+		fiemap->fm_length = ~0ULL;
+		fiemap->fm_extent_count = count;
+
+		ret = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap);
+		if (ret < 0) {
+			ret = errno;
+			if (errno == EBADR) {
+				fprintf(stderr, "fiemap failed with unsupported"
+					" flags %x\n", fiemap->fm_flags);
+			} else
+				fprintf(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) {
+				ofp->xattr += len;
+			} else {
+				if (fm_ext[i].fe_flags &
+				    FIEMAP_EXTENT_UNWRITTEN)
+					ofp->unwrittens += len;
+
+				if (fm_ext[i].fe_flags & FIEMAP_EXTENT_SHARED)
+					ofp->shared += len;
+
+				if ((prev_start + prev_len) < start)
+					ofp->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++;
+			ofp->clusters += len;
+		}
+
+		fiemap->fm_start = (fm_ext[i-1].fe_logical +
+				    fm_ext[i-1].fe_length);
+	} while (!last);
+
+	if (extents_got != num_extents) {
+		fprintf(stderr, "Got wrong extents number, expected:%lu, "
+			"got:%lu\n", num_extents, extents_got);
+		return -1;
+	}
+
+	if (flags & FIEMAP_FLAG_XATTR)
+		ofp->num_extents_xattr = num_extents;
+	else
+		ofp->num_extents = num_extents;
+
+	return ret;
+}
+
+int o2info_get_fiemap(int fd, int flags, struct o2info_fiemap *ofp)
+{
+	int ret = 0;
+
+	ret = do_fiemap(fd, flags, ofp);
+	if (ret)
+		return ret;
+
+	if ((ofp->clusters > 1) && ofp->num_extents) {
+		float e = ofp->num_extents, c = ofp->clusters;
+		int clusters_per_mb = clusters_in_bytes(ofp->clustersize,
+							OCFS2_MAX_CLUSTERSIZE);
+		ofp->frag = 100 * (e / c);
+		ofp->score = ofp->frag * clusters_per_mb;
+	}
+
+	return ret;
+}
diff --git a/o2info/libo2info.h b/o2info/libo2info.h
index e8a8616..465b50d 100644
--- a/o2info/libo2info.h
+++ b/o2info/libo2info.h
@@ -73,10 +73,25 @@ struct o2info_freefrag {
 	struct free_chunk_histogram histogram;
 };
 
+struct o2info_fiemap {
+	uint32_t blocksize;
+	uint32_t clustersize;
+	uint32_t num_extents;
+	uint32_t num_extents_xattr;
+	uint32_t clusters;
+	uint32_t shared;
+	uint32_t holes;
+	uint32_t unwrittens;
+	uint32_t xattr;
+	float frag; /* extents / clusters ratio */
+	float score;
+};
+
 int o2info_get_fs_features(ocfs2_filesys *fs, struct o2info_fs_features *ofs);
 int o2info_get_volinfo(ocfs2_filesys *fs, struct o2info_volinfo *vf);
 int o2info_get_mkfs(ocfs2_filesys *fs, struct o2info_mkfs *oms);
 int o2info_get_freeinode(ocfs2_filesys *fs, struct o2info_freeinode *ofi);
 int o2info_get_freefrag(ocfs2_filesys *fs, struct o2info_freefrag *off);
+int o2info_get_fiemap(int fd, int flags, struct o2info_fiemap *ofp);
 
 #endif
diff --git a/o2info/o2info.1.in b/o2info/o2info.1.in
index 66e2483..3b6ee5c 100644
--- a/o2info/o2info.1.in
+++ b/o2info/o2info.1.in
@@ -2,7 +2,7 @@
 .SH "NAME"
 o2info \- Dump \fIOCFS2\fR file system information on disk.
 .SH "SYNOPSIS"
-\fBo2info\fR [\fB\-C|\-\-cluster\-coherent\fR] [\fB\-\-fs\-features\fR] [\fB\-\-volinfo\fR] [\fB\-\-mkfs\fR] [\fB\-\-freeinode\fR] [\fB\-\-freefrag\fR \fIchunksize\fR] <\fBdevice or file\fR>
+\fBo2info\fR [\fB\-C|\-\-cluster\-coherent\fR] [\fB\-\-fs\-features\fR] [\fB\-\-volinfo\fR] [\fB\-\-mkfs\fR] [\fB\-\-freeinode\fR] [\fB\-\-freefrag\fR \fIchunksize\fR] [\fB\-\-space\-usage\fR] <\fBdevice or file\fR>
 
 .SH "DESCRIPTION"
 .PP
@@ -35,6 +35,10 @@ Display free inode space for all nodes in \fIOCFS2\fR cluster.
 Report free space fragmentation on \fIOCFS2\fR filesystems, it prints how many chunks of chunksize are available as contiguous and aligned free space by scanning the global allocation bitmap. In the meantime, the percentage of contiguous free clusters of size and of alignment chunksize is reported, what's more, it also displays the minimum/maximum/average free chunk size in filesystem, along with a histogram of all free chunks. \fBNOTE\fR, chunksize specified must be a power of two and be equal to or larger than filesystem's clustersize.
 
 .TP
+\fB\-\-space\-usage\fR
+Display file's usage information on disk in terms of how many physical blocks it consumed, how many reserved space and holes in it.
+
+.TP
 \fB\-V, \-\-version\fR
 Show version and exit.
 
diff --git a/o2info/o2info.c b/o2info/o2info.c
index be9bc9b..c19e954 100644
--- a/o2info/o2info.c
+++ b/o2info/o2info.c
@@ -39,6 +39,7 @@ extern struct o2info_operation volinfo_op;
 extern struct o2info_operation mkfs_op;
 extern struct o2info_operation freeinode_op;
 extern struct o2info_operation freefrag_op;
+extern struct o2info_operation space_usage_op;
 
 static LIST_HEAD(o2info_op_task_list);
 static int o2info_op_task_count;
@@ -169,6 +170,19 @@ static struct o2info_option freefrag_option = {
 	.opt_private	= NULL,
 };
 
+static struct o2info_option space_usage_option = {
+	.opt_option	= {
+		.name		= "space-usage",
+		.val		= CHAR_MAX,
+		.has_arg	= 0,
+		.flag		= NULL,
+	},
+	.opt_help	= "   --space-usage",
+	.opt_handler	= NULL,
+	.opt_op		= &space_usage_op,
+	.opt_private	= NULL,
+};
+
 static struct o2info_option *options[] = {
 	&help_option,
 	&version_option,
@@ -178,6 +192,7 @@ static struct o2info_option *options[] = {
 	&mkfs_option,
 	&freeinode_option,
 	&freefrag_option,
+	&space_usage_option,
 	NULL,
 };
 
diff --git a/o2info/operations.c b/o2info/operations.c
index a5bd692..ee08667 100644
--- a/o2info/operations.c
+++ b/o2info/operations.c
@@ -837,3 +837,51 @@ static int freefrag_run(struct o2info_operation *op,
 DEFINE_O2INFO_OP(freefrag,
 		 freefrag_run,
 		 NULL);
+
+static int space_usage_run(struct o2info_operation *op,
+			   struct o2info_method *om,
+			   void *arg)
+{
+	int ret = 0, flags = 0;
+	struct stat st;
+	struct o2info_volinfo ovf;
+	struct o2info_fiemap ofp;
+
+	if (om->om_method == O2INFO_USE_LIBOCFS2) {
+		o2i_error(op, "specify a none-device file to stat\n");
+		ret = -1;
+		goto out;
+	}
+
+	ret = lstat(om->om_path, &st);
+	if (ret < 0) {
+		ret = errno;
+		o2i_error(op, "lstat error: %s\n", strerror(ret));
+		ret = -1;
+		goto out;
+	}
+
+	memset(&ofp, 0, sizeof(ofp));
+
+	ret = get_volinfo_ioctl(op, om->om_fd, &ovf);
+	if (ret)
+		return -1;
+
+	ofp.blocksize = ovf.blocksize;
+	ofp.clustersize = ovf.clustersize;
+
+	ret = o2info_get_fiemap(om->om_fd, flags, &ofp);
+	if (ret)
+		goto out;
+
+	fprintf(stdout, "Blocks: %-10u Shared: %-10u\tUnwritten: %-7u "
+		"Holes: %-6u\n", st.st_blocks, ofp.shared, ofp.unwrittens,
+		ofp.holes);
+
+out:
+	return ret;
+}
+
+DEFINE_O2INFO_OP(space_usage,
+		 space_usage_run,
+		 NULL);
-- 
1.5.5




More information about the Ocfs2-tools-devel mailing list