All of lore.kernel.org
 help / color / mirror / Atom feed
* [Ocfs2-devel] [PATCH 1/2] Ocfs2: Move ocfs2 ioctl definitions from ocfs2_fs.h to newly added ocfs2_ioctl.h
@ 2010-04-14  2:56 Tristan Ye
  2010-04-14  2:56 ` [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6 Tristan Ye
  0 siblings, 1 reply; 17+ messages in thread
From: Tristan Ye @ 2010-04-14  2:56 UTC (permalink / raw)
  To: ocfs2-devel

Currently we were adding ioctl cmds/structures for ocfs2 into ocfs2_fs.h
which was used for define ocfs2 on-disk layout. That sounds a little bit
confusing, and it may be quickly polluted espcially when growing the
ocfs2_info_request ioctls afterwards(it will grow i bet).

As a result, such OCFS2 IOCs do need to be placed somewhere other than
ocfs2_fs.h, a separated ocfs2_ioctl.h will be added to store such ioctl
structures and definitions which could also be used from userspace to
invoke ioctls call.

[This patch has already been in joel's fixes branch]

Signed-off-by: Tristan Ye <tristan.ye@oracle.com>
---
 fs/ocfs2/ioctl.h       |    6 ++--
 fs/ocfs2/ocfs2.h       |    1 +
 fs/ocfs2/ocfs2_fs.h    |   57 ----------------------------------
 fs/ocfs2/ocfs2_ioctl.h |   79 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 83 insertions(+), 60 deletions(-)
 create mode 100644 fs/ocfs2/ocfs2_ioctl.h

diff --git a/fs/ocfs2/ioctl.h b/fs/ocfs2/ioctl.h
index cf9a5ee..0cd5323 100644
--- a/fs/ocfs2/ioctl.h
+++ b/fs/ocfs2/ioctl.h
@@ -7,10 +7,10 @@
  *
  */
 
-#ifndef OCFS2_IOCTL_H
-#define OCFS2_IOCTL_H
+#ifndef OCFS2_IOCTL_PROTO_H
+#define OCFS2_IOCTL_PROTO_H
 
 long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
 long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg);
 
-#endif /* OCFS2_IOCTL_H */
+#endif /* OCFS2_IOCTL_PROTO_H */
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h
index 740f448..eb8e14e 100644
--- a/fs/ocfs2/ocfs2.h
+++ b/fs/ocfs2/ocfs2.h
@@ -42,6 +42,7 @@
 
 #include "ocfs2_fs.h"
 #include "ocfs2_lockid.h"
+#include "ocfs2_ioctl.h"
 
 /* For struct ocfs2_blockcheck_stats */
 #include "blockcheck.h"
diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h
index 7638a38..bb37218 100644
--- a/fs/ocfs2/ocfs2_fs.h
+++ b/fs/ocfs2/ocfs2_fs.h
@@ -254,63 +254,6 @@
 						 * refcount tree */
 
 /*
- * ioctl commands
- */
-#define OCFS2_IOC_GETFLAGS	_IOR('f', 1, long)
-#define OCFS2_IOC_SETFLAGS	_IOW('f', 2, long)
-#define OCFS2_IOC32_GETFLAGS	_IOR('f', 1, int)
-#define OCFS2_IOC32_SETFLAGS	_IOW('f', 2, int)
-
-/*
- * Space reservation / allocation / free ioctls and argument structure
- * are designed to be compatible with XFS.
- *
- * ALLOCSP* and FREESP* are not and will never be supported, but are
- * included here for completeness.
- */
-struct ocfs2_space_resv {
-	__s16		l_type;
-	__s16		l_whence;
-	__s64		l_start;
-	__s64		l_len;		/* len == 0 means until end of file */
-	__s32		l_sysid;
-	__u32		l_pid;
-	__s32		l_pad[4];	/* reserve area			    */
-};
-
-#define OCFS2_IOC_ALLOCSP		_IOW ('X', 10, struct ocfs2_space_resv)
-#define OCFS2_IOC_FREESP		_IOW ('X', 11, struct ocfs2_space_resv)
-#define OCFS2_IOC_RESVSP		_IOW ('X', 40, struct ocfs2_space_resv)
-#define OCFS2_IOC_UNRESVSP	_IOW ('X', 41, struct ocfs2_space_resv)
-#define OCFS2_IOC_ALLOCSP64	_IOW ('X', 36, struct ocfs2_space_resv)
-#define OCFS2_IOC_FREESP64	_IOW ('X', 37, struct ocfs2_space_resv)
-#define OCFS2_IOC_RESVSP64	_IOW ('X', 42, struct ocfs2_space_resv)
-#define OCFS2_IOC_UNRESVSP64	_IOW ('X', 43, struct ocfs2_space_resv)
-
-/* Used to pass group descriptor data when online resize is done */
-struct ocfs2_new_group_input {
-	__u64 group;		/* Group descriptor's blkno. */
-	__u32 clusters;		/* Total number of clusters in this group */
-	__u32 frees;		/* Total free clusters in this group */
-	__u16 chain;		/* Chain for this group */
-	__u16 reserved1;
-	__u32 reserved2;
-};
-
-#define OCFS2_IOC_GROUP_EXTEND	_IOW('o', 1, int)
-#define OCFS2_IOC_GROUP_ADD	_IOW('o', 2,struct ocfs2_new_group_input)
-#define OCFS2_IOC_GROUP_ADD64	_IOW('o', 3,struct ocfs2_new_group_input)
-
-/* Used to pass 2 file names to reflink. */
-struct reflink_arguments {
-	__u64 old_path;
-	__u64 new_path;
-	__u64 preserve;
-};
-#define OCFS2_IOC_REFLINK	_IOW('o', 4, struct reflink_arguments)
-
-
-/*
  * Journal Flags (ocfs2_dinode.id1.journal1.i_flags)
  */
 #define OCFS2_JOURNAL_DIRTY_FL	(0x00000001)	/* Journal needs recovery */
diff --git a/fs/ocfs2/ocfs2_ioctl.h b/fs/ocfs2/ocfs2_ioctl.h
new file mode 100644
index 0000000..2d3420a
--- /dev/null
+++ b/fs/ocfs2/ocfs2_ioctl.h
@@ -0,0 +1,79 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * ocfs2_ioctl.h
+ *
+ * Defines OCFS2 ioctls.
+ *
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef OCFS2_IOCTL_H
+#define OCFS2_IOCTL_H
+
+/*
+ * ioctl commands
+ */
+#define OCFS2_IOC_GETFLAGS	_IOR('f', 1, long)
+#define OCFS2_IOC_SETFLAGS	_IOW('f', 2, long)
+#define OCFS2_IOC32_GETFLAGS	_IOR('f', 1, int)
+#define OCFS2_IOC32_SETFLAGS	_IOW('f', 2, int)
+
+/*
+ * Space reservation / allocation / free ioctls and argument structure
+ * are designed to be compatible with XFS.
+ *
+ * ALLOCSP* and FREESP* are not and will never be supported, but are
+ * included here for completeness.
+ */
+struct ocfs2_space_resv {
+	__s16		l_type;
+	__s16		l_whence;
+	__s64		l_start;
+	__s64		l_len;		/* len == 0 means until end of file */
+	__s32		l_sysid;
+	__u32		l_pid;
+	__s32		l_pad[4];	/* reserve area			    */
+};
+
+#define OCFS2_IOC_ALLOCSP		_IOW ('X', 10, struct ocfs2_space_resv)
+#define OCFS2_IOC_FREESP		_IOW ('X', 11, struct ocfs2_space_resv)
+#define OCFS2_IOC_RESVSP		_IOW ('X', 40, struct ocfs2_space_resv)
+#define OCFS2_IOC_UNRESVSP	_IOW ('X', 41, struct ocfs2_space_resv)
+#define OCFS2_IOC_ALLOCSP64	_IOW ('X', 36, struct ocfs2_space_resv)
+#define OCFS2_IOC_FREESP64	_IOW ('X', 37, struct ocfs2_space_resv)
+#define OCFS2_IOC_RESVSP64	_IOW ('X', 42, struct ocfs2_space_resv)
+#define OCFS2_IOC_UNRESVSP64	_IOW ('X', 43, struct ocfs2_space_resv)
+
+/* Used to pass group descriptor data when online resize is done */
+struct ocfs2_new_group_input {
+	__u64 group;		/* Group descriptor's blkno. */
+	__u32 clusters;		/* Total number of clusters in this group */
+	__u32 frees;		/* Total free clusters in this group */
+	__u16 chain;		/* Chain for this group */
+	__u16 reserved1;
+	__u32 reserved2;
+};
+
+#define OCFS2_IOC_GROUP_EXTEND	_IOW('o', 1, int)
+#define OCFS2_IOC_GROUP_ADD	_IOW('o', 2,struct ocfs2_new_group_input)
+#define OCFS2_IOC_GROUP_ADD64	_IOW('o', 3,struct ocfs2_new_group_input)
+
+/* Used to pass 2 file names to reflink. */
+struct reflink_arguments {
+	__u64 old_path;
+	__u64 new_path;
+	__u64 preserve;
+};
+#define OCFS2_IOC_REFLINK	_IOW('o', 4, struct reflink_arguments)
+
+#endif /* OCFS2_IOCTL_H */
-- 
1.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6.
  2010-04-14  2:56 [Ocfs2-devel] [PATCH 1/2] Ocfs2: Move ocfs2 ioctl definitions from ocfs2_fs.h to newly added ocfs2_ioctl.h Tristan Ye
@ 2010-04-14  2:56 ` Tristan Ye
  2010-04-16 21:50   ` Sunil Mushran
  0 siblings, 1 reply; 17+ messages in thread
From: Tristan Ye @ 2010-04-14  2:56 UTC (permalink / raw)
  To: ocfs2-devel

The reason why we need this ioctl is to offer the none-privileged
end-user a possibility to get filesys info gathering.

We use OCFS2_IOC_INFO to manipulate the new ioctl, userspace passes a
structure to kernel containing an array of request pointers and request
count, such as,

* From userspace:

struct ocfs2_info_blocksize brq = {
        .ir_request = {
                .ir_magic = OCFS2_INFO_MAGIC,
                .ir_code = OCFS2_INFO_BLOCKSIZE,
                ...
        }
        ...
}

struct ocfs2_info_clustersize crq = {
        ...
}

uint64_t reqs[2] = {(unsigned long)&brq,
                    (unsigned long)&crq};

struct ocfs2_info info = {
        .ir_requests = reqs,
        .ir_count = 2,
}

ret = ioctl(fd, OCFS2_IOC_INFO, &info);

* In kernel:

Get the request pointers from *info*, then handle each request one bye one.

Idea here is to make the spearated request small enough to guarantee
a better backward&forward compatibility since a small piece of request
would be less likely to be broken if filesys on raw disk get changed.

Currently, following 8 ioctls get implemented per the requirement from
userspace tool o2info, and I believe it will grow over time:-)

        OCFS2_INFO_CLUSTERSIZE
        OCFS2_INFO_BLOCKSIZE
        OCFS2_INFO_SLOTNUM
        OCFS2_INFO_LABEL
        OCFS2_INFO_UUID
        OCFS2_INFO_FS_FEATURES
        OCFS2_INFO_FREEFRAG
        OCFS2_INFO_FREEINODE

This ioctl is only specific to OCFS2.

Signed-off-by: Tristan Ye <tristan.ye@oracle.com>
Signed-off-by: Joel Becker <joel.becker@oracle.com>
---
 fs/ocfs2/ioctl.c       |  677 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ocfs2/ocfs2_ioctl.h |  117 +++++++++
 2 files changed, 794 insertions(+), 0 deletions(-)

diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 7d9d9c1..58fb935 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -23,8 +23,13 @@
 #include "ioctl.h"
 #include "resize.h"
 #include "refcounttree.h"
+#include "sysfile.h"
+#include "buffer_head_io.h"
+#include "suballoc.h"
+
 
 #include <linux/ext2_fs.h>
+#include <linux/compat.h>
 
 static int ocfs2_get_inode_attr(struct inode *inode, unsigned *flags)
 {
@@ -109,6 +114,664 @@ bail:
 	return status;
 }
 
+int ocfs2_info_handle_blocksize(struct inode *inode,
+				struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_blocksize req_bs;
+
+	if (copy_from_user(&req_bs, user_req,
+			   sizeof(struct ocfs2_info_blocksize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_bs.ir_blocksize = inode->i_sb->s_blocksize;
+	req_bs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_blocksize __user *)user_req,
+			 &req_bs,
+			 sizeof(struct ocfs2_info_blocksize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_clustersize(struct inode *inode,
+				  struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_clustersize req_cs;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_cs, user_req,
+			   sizeof(struct ocfs2_info_clustersize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_cs.ir_clustersize = osb->s_clustersize;
+	req_cs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_clustersize __user *)user_req,
+			 &req_cs,
+			 sizeof(struct ocfs2_info_clustersize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_slotnum(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_slotnum req_sn;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_sn, user_req,
+			   sizeof(struct ocfs2_info_slotnum))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_sn.ir_slotnum = osb->max_slots;
+	req_sn.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_slotnum __user *)user_req,
+			 &req_sn,
+			 sizeof(struct ocfs2_info_slotnum))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_label(struct inode *inode,
+			    struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_label req_lb;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_lb, user_req,
+			   sizeof(struct ocfs2_info_label))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	memcpy(req_lb.ir_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN);
+	req_lb.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_label __user *)user_req,
+			 &req_lb,
+			 sizeof(struct ocfs2_info_label))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_uuid(struct inode *inode,
+			   struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_uuid req_uuid;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_uuid, user_req,
+			   sizeof(struct ocfs2_info_uuid))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	memcpy(req_uuid.ir_uuid_str, osb->uuid_str, OCFS2_INFO_VOL_UUIDSTR_LEN);
+	req_uuid.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_uuid __user *)user_req,
+			 &req_uuid,
+			 sizeof(struct ocfs2_info_uuid))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_fs_features(struct inode *inode,
+				  struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_fs_features req_fs;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_fs, user_req,
+			   sizeof(struct ocfs2_info_fs_features))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_fs.ir_compat_features = osb->s_feature_compat;
+	req_fs.ir_incompat_features = osb->s_feature_incompat;
+	req_fs.ir_ro_compat_features = osb->s_feature_ro_compat;
+	req_fs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_fs_features __user *)user_req,
+			 &req_fs,
+			 sizeof(struct ocfs2_info_fs_features))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg,
+			   unsigned int chunksize)
+{
+	int index;
+
+	index = __ilog2_u32(chunksize);
+	if (index >= OCFS2_INFO_MAX_HIST)
+		index = OCFS2_INFO_MAX_HIST - 1;
+
+	ffg->ir_ffg.ir_fc_hist.ir_fc_chunks[index]++;
+	ffg->ir_ffg.ir_fc_hist.ir_fc_clusters[index] += chunksize;
+
+	if (chunksize > ffg->ir_ffg.ir_max)
+		ffg->ir_ffg.ir_max = chunksize;
+
+	if (chunksize < ffg->ir_ffg.ir_min)
+		ffg->ir_ffg.ir_min = chunksize;
+
+	ffg->ir_ffg.ir_avg += chunksize;
+	ffg->ir_ffg.ir_free_chunks_real++;
+}
+
+int ocfs2_info_scan_chain(struct inode *gb_inode,
+			  struct ocfs2_dinode *gb_dinode,
+			  struct ocfs2_info_freefrag *ffg,
+			  struct ocfs2_chain_rec *rec,
+			  unsigned int chunks_in_group)
+{
+	int status = 0, used;
+	u64 blkno;
+
+	struct buffer_head *bh = 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 (!le32_to_cpu(rec->c_free))
+		goto bail;
+
+	do {
+		if (!bg)
+			blkno = le64_to_cpu(rec->c_blkno);
+		else
+			blkno = le64_to_cpu(bg->bg_next_group);
+
+		if (bh) {
+			brelse(bh);
+			bh = NULL;
+		}
+
+		status = ocfs2_read_group_descriptor(gb_inode, gb_dinode,
+						     blkno, &bh);
+		if (status < 0) {
+			mlog(ML_ERROR, "Can't read the group descriptor # "
+			     "%llu from device.", (unsigned long long)blkno);
+			status = -EIO;
+			goto bail;
+		}
+
+		bg = (struct ocfs2_group_desc *)bh->b_data;
+
+		if (!le16_to_cpu(bg->bg_free_bits_count))
+			continue;
+
+		max_bits = le16_to_cpu(bg->bg_bits);
+		offset = 0;
+
+		for (chunk = 0; chunk < chunks_in_group; chunk++) {
+
+			/* Last chunk may be not a entire one */
+			if ((offset + ffg->ir_chunksize) > max_bits)
+				num_clusters = max_bits - offset;
+			else
+				num_clusters = ffg->ir_chunksize;
+
+			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)) {
+					ocfs2_info_update_ffg(ffg,
+							      last_chunksize);
+					last_chunksize = 0;
+				}
+
+				offset++;
+			}
+
+			if (chunk_free == ffg->ir_chunksize)
+				ffg->ir_ffg.ir_free_chunks++;
+		}
+
+		/* we need to update the info of last free chunk */
+		if (last_chunksize)
+			ocfs2_info_update_ffg(ffg, last_chunksize);
+
+	} while (le64_to_cpu(bg->bg_next_group));
+
+bail:
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_scan_bitmap(struct inode *gb_inode,
+			   struct ocfs2_dinode *gb_dinode,
+			   struct ocfs2_info_freefrag *ffg,
+			   struct ocfs2_chain_list *cl)
+{
+	int status = 0, i;
+	unsigned int chunks_in_group;
+	struct ocfs2_chain_rec *rec = NULL;
+
+	chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->ir_chunksize + 1;
+
+	for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) {
+
+		rec = &(cl->cl_recs[i]);
+		status = ocfs2_info_scan_chain(gb_inode, gb_dinode,
+					       ffg, rec, chunks_in_group);
+		if (status)
+			goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_freefrag(struct inode *inode,
+			       struct ocfs2_info_request __user *user_req)
+{
+	int status = 0, unlock = 0;
+
+	struct ocfs2_info_freefrag req_ffg;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct buffer_head *bh = NULL;
+	struct inode *gb_inode = NULL;
+	struct ocfs2_dinode *gb_dinode = NULL;
+	struct ocfs2_chain_list *cl = NULL;
+
+	if (copy_from_user(&req_ffg, user_req,
+			   sizeof(struct ocfs2_info_freefrag))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	/*
+	 * chunksize from userspace should be power of 2,
+	 */
+	if ((req_ffg.ir_chunksize & (req_ffg.ir_chunksize - 1)) ||
+	    (!req_ffg.ir_chunksize)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	memset(&req_ffg.ir_ffg, 0, sizeof(struct ocfs2_info_freefrag_stats));
+	req_ffg.ir_ffg.ir_min = ~0U;
+
+	gb_inode = ocfs2_get_system_file_inode(osb,
+					       GLOBAL_BITMAP_SYSTEM_INODE,
+					       OCFS2_INVALID_SLOT);
+	if (!gb_inode) {
+		mlog(ML_ERROR, "failed to get bitmap inode\n");
+		status = -EIO;
+		goto bail;
+	}
+
+	mutex_lock(&gb_inode->i_mutex);
+
+	if (!(req_ffg.ir_request.ir_flags & OCFS2_INFO_FL_NON_COHERENT)) {
+		status = ocfs2_inode_lock(gb_inode, &bh, 0);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail_mutex_unlock;
+		}
+		unlock = 1;
+
+	} else {
+		status = ocfs2_read_inode_block(gb_inode, &bh);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail;
+		}
+	}
+
+	gb_dinode = (struct ocfs2_dinode *)bh->b_data;
+
+	req_ffg.ir_ffg.ir_clusters =
+			le32_to_cpu(gb_dinode->id1.bitmap1.i_total);
+	req_ffg.ir_ffg.ir_free_clusters = req_ffg.ir_ffg.ir_clusters -
+				le32_to_cpu(gb_dinode->id1.bitmap1.i_used);
+
+	cl = &(gb_dinode->id2.i_chain);
+
+	/* Chunksize from userspace should be less than clusters in a group */
+	if (req_ffg.ir_chunksize > le16_to_cpu(cl->cl_cpg)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	status = ocfs2_info_scan_bitmap(gb_inode, gb_dinode, &req_ffg, cl);
+	if (status)
+		goto bail;
+
+	if (req_ffg.ir_ffg.ir_free_chunks_real)
+		req_ffg.ir_ffg.ir_avg = (req_ffg.ir_ffg.ir_avg /
+					req_ffg.ir_ffg.ir_free_chunks_real);
+
+	req_ffg.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_freefrag __user *)user_req,
+			 &req_ffg,
+			 sizeof(struct ocfs2_info_freefrag))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	if (unlock)
+		ocfs2_inode_unlock(gb_inode, 0);
+
+bail_mutex_unlock:
+	if (gb_inode)
+		mutex_unlock(&gb_inode->i_mutex);
+
+	iput(gb_inode);
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_scan_inode_alloc(struct inode *inode_alloc,
+				struct ocfs2_info_freeinode *fi,
+				__u32 slotnum,
+				int flags)
+{
+	int status = 0, unlock = 0;
+
+	struct buffer_head *bh = NULL;
+	struct ocfs2_dinode *dinode_alloc = NULL;
+
+	mutex_lock(&inode_alloc->i_mutex);
+
+	if (!(flags & OCFS2_INFO_FL_NON_COHERENT)) {
+		status = ocfs2_inode_lock(inode_alloc, &bh, 0);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail_mutex_unlock;
+		}
+		unlock = 1;
+
+	} else {
+
+		status = ocfs2_read_inode_block(inode_alloc, &bh);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail;
+		}
+	}
+
+	dinode_alloc = (struct ocfs2_dinode *)bh->b_data;
+
+	fi->ir_fi_stat[slotnum].ir_total =
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total);
+	fi->ir_fi_stat[slotnum].ir_free =
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) -
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_used);
+bail:
+	if (unlock)
+		ocfs2_inode_unlock(inode_alloc, 0);
+
+bail_mutex_unlock:
+	mutex_unlock(&inode_alloc->i_mutex);
+
+	iput(inode_alloc);
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_freeinode(struct inode *inode,
+				struct ocfs2_info_request __user *user_req)
+{
+	int status = 0, i;
+
+	struct ocfs2_info_freeinode req_fi;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct inode *inode_alloc = NULL;
+
+
+	if (copy_from_user(&req_fi, user_req,
+			   sizeof(struct ocfs2_info_freeinode))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_fi.ir_slotnum = osb->max_slots;
+
+	for (i = 0; i < req_fi.ir_slotnum; i++) {
+		inode_alloc =
+			ocfs2_get_system_file_inode(osb,
+						    INODE_ALLOC_SYSTEM_INODE,
+						    i);
+		if (!inode_alloc) {
+			mlog(ML_ERROR, "unable to get alloc inode in slot %u\n",
+			     (u32)i);
+			status = -EIO;
+			goto bail;
+		}
+
+		status = ocfs2_info_scan_inode_alloc(inode_alloc, &req_fi, i,
+						req_fi.ir_request.ir_flags);
+		if (status < 0)
+			goto bail;
+	}
+
+	req_fi.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_freeinode __user *)user_req,
+			 &req_fi,
+			 sizeof(struct ocfs2_info_freeinode))) {
+		status = -EFAULT;
+	}
+
+bail:
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_unknown(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_request req;
+
+	if (copy_from_user(&req, user_req, sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req.ir_flags &= ~OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user(user_req, &req,
+			 sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_request(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_request req;
+
+	if (copy_from_user(&req, user_req, sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	if (req.ir_magic != OCFS2_INFO_MAGIC) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	switch (req.ir_code) {
+	case OCFS2_INFO_BLOCKSIZE:
+		if (req.ir_size != sizeof(struct ocfs2_info_blocksize)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_blocksize(inode, user_req);
+		break;
+	case OCFS2_INFO_CLUSTERSIZE:
+		if (req.ir_size != sizeof(struct ocfs2_info_clustersize)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_clustersize(inode, user_req);
+		break;
+	case OCFS2_INFO_SLOTNUM:
+		if (req.ir_size != sizeof(struct ocfs2_info_slotnum)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_slotnum(inode, user_req);
+		break;
+	case OCFS2_INFO_LABEL:
+		if (req.ir_size != sizeof(struct ocfs2_info_label)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_label(inode, user_req);
+		break;
+	case OCFS2_INFO_UUID:
+		if (req.ir_size != sizeof(struct ocfs2_info_uuid)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_uuid(inode, user_req);
+		break;
+	case OCFS2_INFO_FS_FEATURES:
+		if (req.ir_size != sizeof(struct ocfs2_info_fs_features)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_fs_features(inode, user_req);
+		break;
+	case OCFS2_INFO_FREEFRAG:
+		if (req.ir_size != sizeof(struct ocfs2_info_freefrag)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_freefrag(inode, user_req);
+		break;
+	case OCFS2_INFO_FREEINODE:
+		if (req.ir_size != sizeof(struct ocfs2_info_freeinode)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_freeinode(inode, user_req);
+		break;
+	default:
+		status = ocfs2_info_handle_unknown(inode, user_req);
+		break;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info,
+		      int compat_flag)
+{
+	int i, status = 0;
+	u64 req_addr;
+	struct ocfs2_info_request __user *reqp;
+
+	if ((info->info_count > OCFS2_INFO_MAX_REQUEST) ||
+	    (!info->info_requests)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	for (i = 0; i < info->info_count; i++) {
+		status = -EFAULT;
+		if (compat_flag) {
+			if (get_user(req_addr,
+			     (u64 __user *)compat_ptr(info->info_requests) + i))
+				goto bail;
+		} else {
+			if (get_user(req_addr,
+				     (u64 __user *)(info->info_requests) + i))
+				goto bail;
+		}
+
+		reqp = (struct ocfs2_info_request *)req_addr;
+		if (!reqp) {
+			status = -EINVAL;
+			goto bail;
+		}
+
+		status = ocfs2_info_handle_request(inode, reqp);
+		if (status)
+			goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
 long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	struct inode *inode = filp->f_path.dentry->d_inode;
@@ -120,6 +783,7 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 	struct reflink_arguments args;
 	const char *old_path, *new_path;
 	bool preserve;
+	struct ocfs2_info info;
 
 	switch (cmd) {
 	case OCFS2_IOC_GETFLAGS:
@@ -174,6 +838,12 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		preserve = (args.preserve != 0);
 
 		return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve);
+	case OCFS2_IOC_INFO:
+		if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
+				   sizeof(struct ocfs2_info)))
+			return -EFAULT;
+
+		return ocfs2_info_handle(inode, &info, 0);
 	default:
 		return -ENOTTY;
 	}
@@ -185,6 +855,7 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 	bool preserve;
 	struct reflink_arguments args;
 	struct inode *inode = file->f_path.dentry->d_inode;
+	struct ocfs2_info info;
 
 	switch (cmd) {
 	case OCFS2_IOC32_GETFLAGS:
@@ -209,6 +880,12 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 
 		return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path),
 					   compat_ptr(args.new_path), preserve);
+	case OCFS2_IOC_INFO:
+		if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
+				   sizeof(struct ocfs2_info)))
+			return -EFAULT;
+
+		return ocfs2_info_handle(inode, &info, 1);
 	default:
 		return -ENOIOCTLCMD;
 	}
diff --git a/fs/ocfs2/ocfs2_ioctl.h b/fs/ocfs2/ocfs2_ioctl.h
index 2d3420a..9cfe5be 100644
--- a/fs/ocfs2/ocfs2_ioctl.h
+++ b/fs/ocfs2/ocfs2_ioctl.h
@@ -76,4 +76,121 @@ struct reflink_arguments {
 };
 #define OCFS2_IOC_REFLINK	_IOW('o', 4, struct reflink_arguments)
 
+/* Following definitions dedicated for ocfs2_info_request ioctls. */
+
+#define OCFS2_INFO_VOL_UUID_LEN		(16)
+#define OCFS2_INFO_MAX_VOL_LABEL_LEN	(64)
+#define OCFS2_INFO_VOL_UUIDSTR_LEN	(OCFS2_INFO_VOL_UUID_LEN * 2 + 1)
+#define OCFS2_INFO_MAX_SLOTS		(255)
+#define OCFS2_INFO_MAX_HIST		(32)
+
+#define OCFS2_INFO_MAX_REQUEST		(50)
+
+/* Magic number of all requests */
+#define OCFS2_INFO_MAGIC		(0x4F32494E)
+
+/*
+ * Always try to separate info request into small pieces to
+ * guarantee the backward&forward compatibility.
+ */
+
+struct ocfs2_info {
+	__u64 info_requests;	/* Array of __u64 pointers to requests */
+	__u32 info_count;	/* Number of requests in info_requests */
+};
+
+struct ocfs2_info_request {
+/*00*/	__u32 ir_magic;	/* Magic number */
+	__u32 ir_code;	/* Info request code */
+	__u32 ir_size;	/* Size of request */
+	__u32 ir_flags;	/* Request flags */
+/*10*/	/* Request specific fields */
+};
+
+struct ocfs2_info_clustersize {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_clustersize;
+};
+
+struct ocfs2_info_blocksize {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_blocksize;
+};
+
+struct ocfs2_info_slotnum {
+	struct ocfs2_info_request ir_request;
+	__u16 ir_slotnum;
+};
+
+struct ocfs2_info_label {
+	struct ocfs2_info_request ir_request;
+	__u8	ir_label[OCFS2_INFO_MAX_VOL_LABEL_LEN];
+};
+
+struct ocfs2_info_uuid {
+	struct ocfs2_info_request ir_request;
+	__u8	ir_uuid_str[OCFS2_INFO_VOL_UUIDSTR_LEN];
+};
+
+struct ocfs2_info_fs_features {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_compat_features;
+	__u32 ir_incompat_features;
+	__u32 ir_ro_compat_features;
+};
+
+struct ocfs2_info_freefrag {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_chunksize; /* chunksize in clusters(in) */
+	struct ocfs2_info_freefrag_stats { /* (out) */
+		__u32 ir_clusters;
+		__u32 ir_free_clusters;
+		__u32 ir_free_chunks;
+		__u32 ir_free_chunks_real;
+		__u32 ir_min; /* Minimum free chunksize in clusters */
+		__u32 ir_max;
+		__u32 ir_avg;
+		struct ocfs2_info_free_chunk_list {
+			__u32 ir_fc_chunks[OCFS2_INFO_MAX_HIST];
+			__u32 ir_fc_clusters[OCFS2_INFO_MAX_HIST];
+		} ir_fc_hist;
+	} ir_ffg;
+};
+
+struct ocfs2_info_freeinode {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_slotnum; /* out */
+	struct ocfs2_info_local_fi {
+		__u64 ir_total;
+		__u64 ir_free;
+	} ir_fi_stat[OCFS2_INFO_MAX_SLOTS];
+};
+
+/* Codes for ocfs2_info_request */
+enum ocfs2_info_type {
+	OCFS2_INFO_CLUSTERSIZE = 1,
+	OCFS2_INFO_BLOCKSIZE,
+	OCFS2_INFO_SLOTNUM,
+	OCFS2_INFO_LABEL,
+	OCFS2_INFO_UUID,
+	OCFS2_INFO_FS_FEATURES,
+	OCFS2_INFO_FREEFRAG,
+	OCFS2_INFO_FREEINODE,
+	NUM_OCFS2_INFO_TYPE
+};
+
+/* Flags for struct ocfs2_info_request */
+/* Filled by the caller */
+#define OCFS2_INFO_FL_NON_COHERENT	(0x00000001)	/* Cluster coherency not
+							   required. This is a hint.
+							   It is up to ocfs2 whether
+							   the request can be fulfilled
+							   without locking. */
+/* Filled by ocfs2 */
+#define OCFS2_INFO_FL_FILLED		(0x80000000)	/* Filesystem understood
+							   this request and
+							   filled in the answer */
+
+#define OCFS2_IOC_INFO		_IOR('o', 5, struct ocfs2_info)
+
 #endif /* OCFS2_IOCTL_H */
-- 
1.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6.
  2010-04-14  2:56 ` [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6 Tristan Ye
@ 2010-04-16 21:50   ` Sunil Mushran
  2010-04-16 22:26     ` Sunil Mushran
  2010-04-19  1:48     ` tristan
  0 siblings, 2 replies; 17+ messages in thread
From: Sunil Mushran @ 2010-04-16 21:50 UTC (permalink / raw)
  To: ocfs2-devel

I like the overall flow. I just have some comments on style.

Also, would prefer if you drop freeinode and freefrag for the
first checkin. That not only needs a closer look.

For the next drop, just have this patch. The first one is in mainline.

Thanks

Tristan Ye wrote:
> The reason why we need this ioctl is to offer the none-privileged
> end-user a possibility to get filesys info gathering.
>
> We use OCFS2_IOC_INFO to manipulate the new ioctl, userspace passes a
> structure to kernel containing an array of request pointers and request
> count, such as,
>
> * From userspace:
>
> struct ocfs2_info_blocksize brq = {
>         .ir_request = {
>                 .ir_magic = OCFS2_INFO_MAGIC,
>                 .ir_code = OCFS2_INFO_BLOCKSIZE,
>                 ...
>         }
>         ...
> }
>
> struct ocfs2_info_clustersize crq = {
>         ...
> }
>
> uint64_t reqs[2] = {(unsigned long)&brq,
>                     (unsigned long)&crq};
>
> struct ocfs2_info info = {
>         .ir_requests = reqs,
>         .ir_count = 2,
> }
>
> ret = ioctl(fd, OCFS2_IOC_INFO, &info);
>
> * In kernel:
>
> Get the request pointers from *info*, then handle each request one bye one.
>
> Idea here is to make the spearated request small enough to guarantee
> a better backward&forward compatibility since a small piece of request
> would be less likely to be broken if filesys on raw disk get changed.
>
> Currently, following 8 ioctls get implemented per the requirement from
> userspace tool o2info, and I believe it will grow over time:-)
>
>         OCFS2_INFO_CLUSTERSIZE
>         OCFS2_INFO_BLOCKSIZE
>         OCFS2_INFO_SLOTNUM
>         OCFS2_INFO_LABEL
>         OCFS2_INFO_UUID
>         OCFS2_INFO_FS_FEATURES
>         OCFS2_INFO_FREEFRAG
>         OCFS2_INFO_FREEINODE
>
> This ioctl is only specific to OCFS2.
>
> Signed-off-by: Tristan Ye <tristan.ye@oracle.com>
> Signed-off-by: Joel Becker <joel.becker@oracle.com>
> ---
>  fs/ocfs2/ioctl.c       |  677 ++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/ocfs2/ocfs2_ioctl.h |  117 +++++++++
>  2 files changed, 794 insertions(+), 0 deletions(-)
>
> diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
> index 7d9d9c1..58fb935 100644
> --- a/fs/ocfs2/ioctl.c
> +++ b/fs/ocfs2/ioctl.c
> @@ -23,8 +23,13 @@
>  #include "ioctl.h"
>  #include "resize.h"
>  #include "refcounttree.h"
> +#include "sysfile.h"
> +#include "buffer_head_io.h"
> +#include "suballoc.h"
> +
>  
>  #include <linux/ext2_fs.h>
> +#include <linux/compat.h>
>  
>  static int ocfs2_get_inode_attr(struct inode *inode, unsigned *flags)
>  {
> @@ -109,6 +114,664 @@ bail:
>  	return status;
>  }
>  
> +int ocfs2_info_handle_blocksize(struct inode *inode,
> +				struct ocfs2_info_request __user *user_req)
> +{
> +	int status = 0;
> +	struct ocfs2_info_blocksize req_bs;
>   

req_bs is a mouthful. How about "oib"? For the next function it could
be "oic"? We do this everywhere. eg, osb, di, et, el, etc.

> +
> +	if (copy_from_user(&req_bs, user_req,
> +			   sizeof(struct ocfs2_info_blocksize))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +	req_bs.ir_blocksize = inode->i_sb->s_blocksize;
> +	req_bs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
> +
> +	if (copy_to_user((struct ocfs2_info_blocksize __user *)user_req,
> +			 &req_bs,
> +			 sizeof(struct ocfs2_info_blocksize))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +bail:
> +	mlog_exit(status);
>   

Don't have a exit without an entry.  But I think we can do
without the entry/exit mlogs in these ioctls.

> +	return status;
> +}
> +
> +int ocfs2_info_handle_clustersize(struct inode *inode,
> +				  struct ocfs2_info_request __user *user_req)
> +{
> +	int status = 0;
>   

Or, you could set it to -EFAULT here and set it to 0 just before bail.

> +	struct ocfs2_info_clustersize req_cs;
> +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
> +
> +	if (copy_from_user(&req_cs, user_req,
> +			   sizeof(struct ocfs2_info_clustersize))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +	req_cs.ir_clustersize = osb->s_clustersize;
> +	req_cs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
> +
> +	if (copy_to_user((struct ocfs2_info_clustersize __user *)user_req,
> +			 &req_cs,
> +			 sizeof(struct ocfs2_info_clustersize))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +bail:
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +int ocfs2_info_handle_slotnum(struct inode *inode,
> +			      struct ocfs2_info_request __user *user_req)
> +{
> +	int status = 0;
> +	struct ocfs2_info_slotnum req_sn;
> +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
> +
> +	if (copy_from_user(&req_sn, user_req,
> +			   sizeof(struct ocfs2_info_slotnum))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +	req_sn.ir_slotnum = osb->max_slots;
> +	req_sn.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
> +
> +	if (copy_to_user((struct ocfs2_info_slotnum __user *)user_req,
> +			 &req_sn,
> +			 sizeof(struct ocfs2_info_slotnum))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +bail:
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +int ocfs2_info_handle_label(struct inode *inode,
> +			    struct ocfs2_info_request __user *user_req)
> +{
> +	int status = 0;
> +	struct ocfs2_info_label req_lb;
> +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
> +
> +	if (copy_from_user(&req_lb, user_req,
> +			   sizeof(struct ocfs2_info_label))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +	memcpy(req_lb.ir_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN);
> +	req_lb.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
> +
> +	if (copy_to_user((struct ocfs2_info_label __user *)user_req,
> +			 &req_lb,
> +			 sizeof(struct ocfs2_info_label))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +bail:
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +int ocfs2_info_handle_uuid(struct inode *inode,
> +			   struct ocfs2_info_request __user *user_req)
> +{
> +	int status = 0;
> +	struct ocfs2_info_uuid req_uuid;
> +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
> +
> +	if (copy_from_user(&req_uuid, user_req,
> +			   sizeof(struct ocfs2_info_uuid))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +	memcpy(req_uuid.ir_uuid_str, osb->uuid_str, OCFS2_INFO_VOL_UUIDSTR_LEN);
> +	req_uuid.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
> +
> +	if (copy_to_user((struct ocfs2_info_uuid __user *)user_req,
> +			 &req_uuid,
> +			 sizeof(struct ocfs2_info_uuid))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +bail:
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +int ocfs2_info_handle_fs_features(struct inode *inode,
> +				  struct ocfs2_info_request __user *user_req)
> +{
> +	int status = 0;
> +	struct ocfs2_info_fs_features req_fs;
> +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
> +
> +	if (copy_from_user(&req_fs, user_req,
> +			   sizeof(struct ocfs2_info_fs_features))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +	req_fs.ir_compat_features = osb->s_feature_compat;
> +	req_fs.ir_incompat_features = osb->s_feature_incompat;
> +	req_fs.ir_ro_compat_features = osb->s_feature_ro_compat;
> +	req_fs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
> +
> +	if (copy_to_user((struct ocfs2_info_fs_features __user *)user_req,
> +			 &req_fs,
> +			 sizeof(struct ocfs2_info_fs_features))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +bail:
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg,
> +			   unsigned int chunksize)
> +{
> +	int index;
> +
> +	index = __ilog2_u32(chunksize);
> +	if (index >= OCFS2_INFO_MAX_HIST)
> +		index = OCFS2_INFO_MAX_HIST - 1;
> +
> +	ffg->ir_ffg.ir_fc_hist.ir_fc_chunks[index]++;
> +	ffg->ir_ffg.ir_fc_hist.ir_fc_clusters[index] += chunksize;
> +
> +	if (chunksize > ffg->ir_ffg.ir_max)
> +		ffg->ir_ffg.ir_max = chunksize;
> +
> +	if (chunksize < ffg->ir_ffg.ir_min)
> +		ffg->ir_ffg.ir_min = chunksize;
> +
> +	ffg->ir_ffg.ir_avg += chunksize;
> +	ffg->ir_ffg.ir_free_chunks_real++;
> +}
> +
> +int ocfs2_info_scan_chain(struct inode *gb_inode,
> +			  struct ocfs2_dinode *gb_dinode,
> +			  struct ocfs2_info_freefrag *ffg,
> +			  struct ocfs2_chain_rec *rec,
> +			  unsigned int chunks_in_group)
> +{
> +	int status = 0, used;
> +	u64 blkno;
> +
> +	struct buffer_head *bh = 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 (!le32_to_cpu(rec->c_free))
> +		goto bail;
> +
> +	do {
> +		if (!bg)
> +			blkno = le64_to_cpu(rec->c_blkno);
> +		else
> +			blkno = le64_to_cpu(bg->bg_next_group);
> +
> +		if (bh) {
> +			brelse(bh);
> +			bh = NULL;
> +		}
> +
> +		status = ocfs2_read_group_descriptor(gb_inode, gb_dinode,
> +						     blkno, &bh);
> +		if (status < 0) {
> +			mlog(ML_ERROR, "Can't read the group descriptor # "
> +			     "%llu from device.", (unsigned long long)blkno);
> +			status = -EIO;
> +			goto bail;
> +		}
> +
> +		bg = (struct ocfs2_group_desc *)bh->b_data;
> +
> +		if (!le16_to_cpu(bg->bg_free_bits_count))
> +			continue;
> +
> +		max_bits = le16_to_cpu(bg->bg_bits);
> +		offset = 0;
> +
> +		for (chunk = 0; chunk < chunks_in_group; chunk++) {
> +
> +			/* Last chunk may be not a entire one */
> +			if ((offset + ffg->ir_chunksize) > max_bits)
> +				num_clusters = max_bits - offset;
> +			else
> +				num_clusters = ffg->ir_chunksize;
> +
> +			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)) {
> +					ocfs2_info_update_ffg(ffg,
> +							      last_chunksize);
> +					last_chunksize = 0;
> +				}
> +
> +				offset++;
> +			}
> +
> +			if (chunk_free == ffg->ir_chunksize)
> +				ffg->ir_ffg.ir_free_chunks++;
> +		}
> +
> +		/* we need to update the info of last free chunk */
> +		if (last_chunksize)
> +			ocfs2_info_update_ffg(ffg, last_chunksize);
> +
> +	} while (le64_to_cpu(bg->bg_next_group));
> +
> +bail:
> +	brelse(bh);
> +
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +int ocfs2_info_scan_bitmap(struct inode *gb_inode,
> +			   struct ocfs2_dinode *gb_dinode,
> +			   struct ocfs2_info_freefrag *ffg,
> +			   struct ocfs2_chain_list *cl)
> +{
> +	int status = 0, i;
> +	unsigned int chunks_in_group;
> +	struct ocfs2_chain_rec *rec = NULL;
> +
> +	chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->ir_chunksize + 1;
> +
> +	for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) {
> +
> +		rec = &(cl->cl_recs[i]);
> +		status = ocfs2_info_scan_chain(gb_inode, gb_dinode,
> +					       ffg, rec, chunks_in_group);
> +		if (status)
> +			goto bail;
> +	}
> +
> +bail:
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +int ocfs2_info_handle_freefrag(struct inode *inode,
> +			       struct ocfs2_info_request __user *user_req)
> +{
> +	int status = 0, unlock = 0;
> +
> +	struct ocfs2_info_freefrag req_ffg;
> +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
> +	struct buffer_head *bh = NULL;
> +	struct inode *gb_inode = NULL;
> +	struct ocfs2_dinode *gb_dinode = NULL;
> +	struct ocfs2_chain_list *cl = NULL;
> +
> +	if (copy_from_user(&req_ffg, user_req,
> +			   sizeof(struct ocfs2_info_freefrag))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +	/*
> +	 * chunksize from userspace should be power of 2,
> +	 */
> +	if ((req_ffg.ir_chunksize & (req_ffg.ir_chunksize - 1)) ||
> +	    (!req_ffg.ir_chunksize)) {
> +		status = -EINVAL;
> +		goto bail;
> +	}
> +
> +	memset(&req_ffg.ir_ffg, 0, sizeof(struct ocfs2_info_freefrag_stats));
> +	req_ffg.ir_ffg.ir_min = ~0U;
> +
> +	gb_inode = ocfs2_get_system_file_inode(osb,
> +					       GLOBAL_BITMAP_SYSTEM_INODE,
> +					       OCFS2_INVALID_SLOT);
> +	if (!gb_inode) {
> +		mlog(ML_ERROR, "failed to get bitmap inode\n");
> +		status = -EIO;
> +		goto bail;
> +	}
> +
> +	mutex_lock(&gb_inode->i_mutex);
> +
> +	if (!(req_ffg.ir_request.ir_flags & OCFS2_INFO_FL_NON_COHERENT)) {
> +		status = ocfs2_inode_lock(gb_inode, &bh, 0);
> +		if (status < 0) {
> +			mlog_errno(status);
> +			goto bail_mutex_unlock;
> +		}
> +		unlock = 1;
> +
> +	} else {
> +		status = ocfs2_read_inode_block(gb_inode, &bh);
> +		if (status < 0) {
> +			mlog_errno(status);
> +			goto bail;
> +		}
> +	}
> +
> +	gb_dinode = (struct ocfs2_dinode *)bh->b_data;
> +
> +	req_ffg.ir_ffg.ir_clusters =
> +			le32_to_cpu(gb_dinode->id1.bitmap1.i_total);
> +	req_ffg.ir_ffg.ir_free_clusters = req_ffg.ir_ffg.ir_clusters -
> +				le32_to_cpu(gb_dinode->id1.bitmap1.i_used);
> +
> +	cl = &(gb_dinode->id2.i_chain);
> +
> +	/* Chunksize from userspace should be less than clusters in a group */
> +	if (req_ffg.ir_chunksize > le16_to_cpu(cl->cl_cpg)) {
> +		status = -EINVAL;
> +		goto bail;
> +	}
> +
> +	status = ocfs2_info_scan_bitmap(gb_inode, gb_dinode, &req_ffg, cl);
> +	if (status)
> +		goto bail;
> +
> +	if (req_ffg.ir_ffg.ir_free_chunks_real)
> +		req_ffg.ir_ffg.ir_avg = (req_ffg.ir_ffg.ir_avg /
> +					req_ffg.ir_ffg.ir_free_chunks_real);
> +
> +	req_ffg.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
> +
> +	if (copy_to_user((struct ocfs2_info_freefrag __user *)user_req,
> +			 &req_ffg,
> +			 sizeof(struct ocfs2_info_freefrag))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +bail:
> +	if (unlock)
> +		ocfs2_inode_unlock(gb_inode, 0);
> +
> +bail_mutex_unlock:
> +	if (gb_inode)
> +		mutex_unlock(&gb_inode->i_mutex);
> +
> +	iput(gb_inode);
> +	brelse(bh);
> +
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +int ocfs2_info_scan_inode_alloc(struct inode *inode_alloc,
> +				struct ocfs2_info_freeinode *fi,
> +				__u32 slotnum,
> +				int flags)
> +{
> +	int status = 0, unlock = 0;
> +
> +	struct buffer_head *bh = NULL;
> +	struct ocfs2_dinode *dinode_alloc = NULL;
> +
> +	mutex_lock(&inode_alloc->i_mutex);
> +
> +	if (!(flags & OCFS2_INFO_FL_NON_COHERENT)) {
> +		status = ocfs2_inode_lock(inode_alloc, &bh, 0);
> +		if (status < 0) {
> +			mlog_errno(status);
> +			goto bail_mutex_unlock;
> +		}
> +		unlock = 1;
> +
> +	} else {
> +
> +		status = ocfs2_read_inode_block(inode_alloc, &bh);
> +		if (status < 0) {
> +			mlog_errno(status);
> +			goto bail;
> +		}
> +	}
> +
> +	dinode_alloc = (struct ocfs2_dinode *)bh->b_data;
> +
> +	fi->ir_fi_stat[slotnum].ir_total =
> +		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total);
> +	fi->ir_fi_stat[slotnum].ir_free =
> +		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) -
> +		le32_to_cpu(dinode_alloc->id1.bitmap1.i_used);
> +bail:
> +	if (unlock)
> +		ocfs2_inode_unlock(inode_alloc, 0);
> +
> +bail_mutex_unlock:
> +	mutex_unlock(&inode_alloc->i_mutex);
> +
> +	iput(inode_alloc);
> +	brelse(bh);
> +
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +int ocfs2_info_handle_freeinode(struct inode *inode,
> +				struct ocfs2_info_request __user *user_req)
> +{
> +	int status = 0, i;
> +
> +	struct ocfs2_info_freeinode req_fi;
> +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
> +	struct inode *inode_alloc = NULL;
> +
> +
> +	if (copy_from_user(&req_fi, user_req,
> +			   sizeof(struct ocfs2_info_freeinode))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +	req_fi.ir_slotnum = osb->max_slots;
> +
> +	for (i = 0; i < req_fi.ir_slotnum; i++) {
> +		inode_alloc =
> +			ocfs2_get_system_file_inode(osb,
> +						    INODE_ALLOC_SYSTEM_INODE,
> +						    i);
> +		if (!inode_alloc) {
> +			mlog(ML_ERROR, "unable to get alloc inode in slot %u\n",
> +			     (u32)i);
> +			status = -EIO;
> +			goto bail;
> +		}
> +
> +		status = ocfs2_info_scan_inode_alloc(inode_alloc, &req_fi, i,
> +						req_fi.ir_request.ir_flags);
> +		if (status < 0)
> +			goto bail;
> +	}
> +
> +	req_fi.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
> +
> +	if (copy_to_user((struct ocfs2_info_freeinode __user *)user_req,
> +			 &req_fi,
> +			 sizeof(struct ocfs2_info_freeinode))) {
> +		status = -EFAULT;
> +	}
> +
> +bail:
> +
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +int ocfs2_info_handle_unknown(struct inode *inode,
> +			      struct ocfs2_info_request __user *user_req)
> +{
> +	int status = 0;
> +	struct ocfs2_info_request req;
> +
> +	if (copy_from_user(&req, user_req, sizeof(struct ocfs2_info_request))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +	req.ir_flags &= ~OCFS2_INFO_FL_FILLED;
> +
> +	if (copy_to_user(user_req, &req,
> +			 sizeof(struct ocfs2_info_request))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +bail:
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +int ocfs2_info_handle_request(struct inode *inode,
> +			      struct ocfs2_info_request __user *user_req)
> +{
> +	int status = 0;
> +	struct ocfs2_info_request req;
> +
> +	if (copy_from_user(&req, user_req, sizeof(struct ocfs2_info_request))) {
> +		status = -EFAULT;
> +		goto bail;
> +	}
> +
> +	if (req.ir_magic != OCFS2_INFO_MAGIC) {
> +		status = -EINVAL;
> +		goto bail;
> +	}
> +
> +	switch (req.ir_code) {
> +	case OCFS2_INFO_BLOCKSIZE:
> +		if (req.ir_size != sizeof(struct ocfs2_info_blocksize)) {
> +			status = -EINVAL;
> +			break;
> +		}
> +		status = ocfs2_info_handle_blocksize(inode, user_req);
> +		break;
> +	case OCFS2_INFO_CLUSTERSIZE:
> +		if (req.ir_size != sizeof(struct ocfs2_info_clustersize)) {
> +			status = -EINVAL;
> +			break;
> +		}
> +		status = ocfs2_info_handle_clustersize(inode, user_req);
> +		break;
> +	case OCFS2_INFO_SLOTNUM:
> +		if (req.ir_size != sizeof(struct ocfs2_info_slotnum)) {
> +			status = -EINVAL;
> +			break;
> +		}
> +		status = ocfs2_info_handle_slotnum(inode, user_req);
> +		break;
> +	case OCFS2_INFO_LABEL:
> +		if (req.ir_size != sizeof(struct ocfs2_info_label)) {
> +			status = -EINVAL;
> +			break;
> +		}
> +		status = ocfs2_info_handle_label(inode, user_req);
> +		break;
> +	case OCFS2_INFO_UUID:
> +		if (req.ir_size != sizeof(struct ocfs2_info_uuid)) {
> +			status = -EINVAL;
> +			break;
> +		}
> +		status = ocfs2_info_handle_uuid(inode, user_req);
> +		break;
> +	case OCFS2_INFO_FS_FEATURES:
> +		if (req.ir_size != sizeof(struct ocfs2_info_fs_features)) {
> +			status = -EINVAL;
> +			break;
> +		}
> +		status = ocfs2_info_handle_fs_features(inode, user_req);
> +		break;
> +	case OCFS2_INFO_FREEFRAG:
> +		if (req.ir_size != sizeof(struct ocfs2_info_freefrag)) {
> +			status = -EINVAL;
> +			break;
> +		}
> +		status = ocfs2_info_handle_freefrag(inode, user_req);
> +		break;
> +	case OCFS2_INFO_FREEINODE:
> +		if (req.ir_size != sizeof(struct ocfs2_info_freeinode)) {
> +			status = -EINVAL;
> +			break;
> +		}
> +		status = ocfs2_info_handle_freeinode(inode, user_req);
> +		break;
> +	default:
> +		status = ocfs2_info_handle_unknown(inode, user_req);
> +		break;
> +	}
>   

For the first round, can we remove the freefrag and freeinode.
That needs a closer look. I would like the scalars entered first.


> +
> +bail:
> +	mlog_exit(status);
> +	return status;
> +}
> +
> +int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info,
> +		      int compat_flag)
> +{
> +	int i, status = 0;
> +	u64 req_addr;
> +	struct ocfs2_info_request __user *reqp;
> +
> +	if ((info->info_count > OCFS2_INFO_MAX_REQUEST) ||
> +	    (!info->info_requests)) {
> +		status = -EINVAL;
> +		goto bail;
> +	}
> +
> +	for (i = 0; i < info->info_count; i++) {
> +		status = -EFAULT;
> +		if (compat_flag) {
> +			if (get_user(req_addr,
> +			     (u64 __user *)compat_ptr(info->info_requests) + i))
> +				goto bail;
> +		} else {
> +			if (get_user(req_addr,
> +				     (u64 __user *)(info->info_requests) + i))
> +				goto bail;
> +		}
> +
> +		reqp = (struct ocfs2_info_request *)req_addr;
> +		if (!reqp) {
> +			status = -EINVAL;
> +			goto bail;
> +		}
> +
> +		status = ocfs2_info_handle_request(inode, reqp);
> +		if (status)
> +			goto bail;
> +	}
> +
> +bail:
> +	mlog_exit(status);
> +	return status;
> +}
> +
>  long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  {
>  	struct inode *inode = filp->f_path.dentry->d_inode;
> @@ -120,6 +783,7 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  	struct reflink_arguments args;
>  	const char *old_path, *new_path;
>  	bool preserve;
> +	struct ocfs2_info info;
>  
>  	switch (cmd) {
>  	case OCFS2_IOC_GETFLAGS:
> @@ -174,6 +838,12 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  		preserve = (args.preserve != 0);
>  
>  		return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve);
> +	case OCFS2_IOC_INFO:
> +		if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
> +				   sizeof(struct ocfs2_info)))
> +			return -EFAULT;
> +
> +		return ocfs2_info_handle(inode, &info, 0);
>  	default:
>  		return -ENOTTY;
>  	}
> @@ -185,6 +855,7 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
>  	bool preserve;
>  	struct reflink_arguments args;
>  	struct inode *inode = file->f_path.dentry->d_inode;
> +	struct ocfs2_info info;
>  
>  	switch (cmd) {
>  	case OCFS2_IOC32_GETFLAGS:
> @@ -209,6 +880,12 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
>  
>  		return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path),
>  					   compat_ptr(args.new_path), preserve);
> +	case OCFS2_IOC_INFO:
> +		if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
> +				   sizeof(struct ocfs2_info)))
> +			return -EFAULT;
> +
> +		return ocfs2_info_handle(inode, &info, 1);
>  	default:
>  		return -ENOIOCTLCMD;
>  	}
> diff --git a/fs/ocfs2/ocfs2_ioctl.h b/fs/ocfs2/ocfs2_ioctl.h
> index 2d3420a..9cfe5be 100644
> --- a/fs/ocfs2/ocfs2_ioctl.h
> +++ b/fs/ocfs2/ocfs2_ioctl.h
> @@ -76,4 +76,121 @@ struct reflink_arguments {
>  };
>  #define OCFS2_IOC_REFLINK	_IOW('o', 4, struct reflink_arguments)
>  
> +/* Following definitions dedicated for ocfs2_info_request ioctls. */
> +
> +#define OCFS2_INFO_VOL_UUID_LEN		(16)
> +#define OCFS2_INFO_MAX_VOL_LABEL_LEN	(64)
>   

Why not use the existing #defines in ocfs2_fs.h

> +#define OCFS2_INFO_VOL_UUIDSTR_LEN	(OCFS2_INFO_VOL_UUID_LEN * 2 + 1)
>   
> +#define OCFS2_INFO_MAX_SLOTS		(255)
> +#define OCFS2_INFO_MAX_HIST		(32)
> +
> +#define OCFS2_INFO_MAX_REQUEST		(50)
> +
> +/* Magic number of all requests */
> +#define OCFS2_INFO_MAGIC		(0x4F32494E)
> +
> +/*
> + * Always try to separate info request into small pieces to
> + * guarantee the backward&forward compatibility.
> + */
> +
> +struct ocfs2_info {
> +	__u64 info_requests;	/* Array of __u64 pointers to requests */
> +	__u32 info_count;	/* Number of requests in info_requests */
>   
oi_requests, oi_count

> +};
> +
> +struct ocfs2_info_request {
> +/*00*/	__u32 ir_magic;	/* Magic number */
> +	__u32 ir_code;	/* Info request code */
> +	__u32 ir_size;	/* Size of request */
> +	__u32 ir_flags;	/* Request flags */
> +/*10*/	/* Request specific fields */
> +};
> +
> +struct ocfs2_info_clustersize {
> +	struct ocfs2_info_request ir_request;
> +	__u32 ir_clustersize;
>   
ic_req, ic_clustersize

> +};
> +
> +struct ocfs2_info_blocksize {
> +	struct ocfs2_info_request ir_request;
> +	__u32 ir_blocksize;
>   

ib_req, ib_blocksize

> +};
> +
> +struct ocfs2_info_slotnum {
> +	struct ocfs2_info_request ir_request;
> +	__u16 ir_slotnum;
>   

ocfs2_info_maxslots
is_req, is_max_slots

(slotnum could be later used to query the slot use by that node)

> +};
> +
> +struct ocfs2_info_label {
> +	struct ocfs2_info_request ir_request;
> +	__u8	ir_label[OCFS2_INFO_MAX_VOL_LABEL_LEN];
> +};
>   

il_req, il_label

You get the drift.

> +
> +struct ocfs2_info_uuid {
> +	struct ocfs2_info_request ir_request;
> +	__u8	ir_uuid_str[OCFS2_INFO_VOL_UUIDSTR_LEN];
> +};
> +
> +struct ocfs2_info_fs_features {
> +	struct ocfs2_info_request ir_request;
> +	__u32 ir_compat_features;
> +	__u32 ir_incompat_features;
> +	__u32 ir_ro_compat_features;
> +};
> +
> +struct ocfs2_info_freefrag {
> +	struct ocfs2_info_request ir_request;
> +	__u32 ir_chunksize; /* chunksize in clusters(in) */
> +	struct ocfs2_info_freefrag_stats { /* (out) */
> +		__u32 ir_clusters;
> +		__u32 ir_free_clusters;
> +		__u32 ir_free_chunks;
> +		__u32 ir_free_chunks_real;
> +		__u32 ir_min; /* Minimum free chunksize in clusters */
> +		__u32 ir_max;
> +		__u32 ir_avg;
> +		struct ocfs2_info_free_chunk_list {
> +			__u32 ir_fc_chunks[OCFS2_INFO_MAX_HIST];
> +			__u32 ir_fc_clusters[OCFS2_INFO_MAX_HIST];
> +		} ir_fc_hist;
> +	} ir_ffg;
> +};
> +
> +struct ocfs2_info_freeinode {
> +	struct ocfs2_info_request ir_request;
> +	__u32 ir_slotnum; /* out */
> +	struct ocfs2_info_local_fi {
> +		__u64 ir_total;
> +		__u64 ir_free;
> +	} ir_fi_stat[OCFS2_INFO_MAX_SLOTS];
> +};
> +
> +/* Codes for ocfs2_info_request */
> +enum ocfs2_info_type {
> +	OCFS2_INFO_CLUSTERSIZE = 1,
> +	OCFS2_INFO_BLOCKSIZE,
> +	OCFS2_INFO_SLOTNUM,
> +	OCFS2_INFO_LABEL,
> +	OCFS2_INFO_UUID,
> +	OCFS2_INFO_FS_FEATURES,
> +	OCFS2_INFO_FREEFRAG,
> +	OCFS2_INFO_FREEINODE,
> +	NUM_OCFS2_INFO_TYPE
>   

OCFS2_INFO_NUM_TYPES

> +};
> +
> +/* Flags for struct ocfs2_info_request */
> +/* Filled by the caller */
> +#define OCFS2_INFO_FL_NON_COHERENT	(0x00000001)	/* Cluster coherency not
> +							   required. This is a hint.
> +							   It is up to ocfs2 whether
> +							   the request can be fulfilled
> +							   without locking. */
> +/* Filled by ocfs2 */
> +#define OCFS2_INFO_FL_FILLED		(0x80000000)	/* Filesystem understood
> +							   this request and
> +							   filled in the answer */
> +
> +#define OCFS2_IOC_INFO		_IOR('o', 5, struct ocfs2_info)
> +
>  #endif /* OCFS2_IOCTL_H */
>   

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6.
  2010-04-16 21:50   ` Sunil Mushran
@ 2010-04-16 22:26     ` Sunil Mushran
  2010-04-17  0:11       ` Joel Becker
  2010-04-19  2:22       ` tristan
  2010-04-19  1:48     ` tristan
  1 sibling, 2 replies; 17+ messages in thread
From: Sunil Mushran @ 2010-04-16 22:26 UTC (permalink / raw)
  To: ocfs2-devel

So just to expand on the style issue. My intention here is not to be a 
p-i-t-a.
Just want the code to be readable.

By making those few changes, the boiler plate code of from_user and to_user
will look like this.

        if (copy_from_user(&oic, req, sizeof(struct 
ocfs2_info_clustersize)))
                goto bail;

        if (copy_to_user((struct ocfs2_info_clustersize __user *)req, &oic,
                          sizeof(struct ocfs2_info_clustersize)))
                goto bail;


Or, you could take it a step further and make some macros. Keep these
macros in close proximity to these functions.

#define _ocfs2_from_user(a, b)  \      
                copy_from_user(&(a), (a), typeof(a))

#define _ocfs2_to_user(a, b)    \      
                copy_to_user((typeof(a) __user *)b, &(a), sizeof(a))

        if (_ocfs2_from_user(oic, req))
                goto bail;

        if (_ocfs2_to_user(oic, req))
                goto bail;

Sunil


Sunil Mushran wrote:
> I like the overall flow. I just have some comments on style.
>
> Also, would prefer if you drop freeinode and freefrag for the
> first checkin. That not only needs a closer look.
>
> For the next drop, just have this patch. The first one is in mainline.
>
> Thanks
>
> Tristan Ye wrote:
>   
>> The reason why we need this ioctl is to offer the none-privileged
>> end-user a possibility to get filesys info gathering.
>>
>> We use OCFS2_IOC_INFO to manipulate the new ioctl, userspace passes a
>> structure to kernel containing an array of request pointers and request
>> count, such as,
>>
>> * From userspace:
>>
>> struct ocfs2_info_blocksize brq = {
>>         .ir_request = {
>>                 .ir_magic = OCFS2_INFO_MAGIC,
>>                 .ir_code = OCFS2_INFO_BLOCKSIZE,
>>                 ...
>>         }
>>         ...
>> }
>>
>> struct ocfs2_info_clustersize crq = {
>>         ...
>> }
>>
>> uint64_t reqs[2] = {(unsigned long)&brq,
>>                     (unsigned long)&crq};
>>
>> struct ocfs2_info info = {
>>         .ir_requests = reqs,
>>         .ir_count = 2,
>> }
>>
>> ret = ioctl(fd, OCFS2_IOC_INFO, &info);
>>
>> * In kernel:
>>
>> Get the request pointers from *info*, then handle each request one bye one.
>>
>> Idea here is to make the spearated request small enough to guarantee
>> a better backward&forward compatibility since a small piece of request
>> would be less likely to be broken if filesys on raw disk get changed.
>>
>> Currently, following 8 ioctls get implemented per the requirement from
>> userspace tool o2info, and I believe it will grow over time:-)
>>
>>         OCFS2_INFO_CLUSTERSIZE
>>         OCFS2_INFO_BLOCKSIZE
>>         OCFS2_INFO_SLOTNUM
>>         OCFS2_INFO_LABEL
>>         OCFS2_INFO_UUID
>>         OCFS2_INFO_FS_FEATURES
>>         OCFS2_INFO_FREEFRAG
>>         OCFS2_INFO_FREEINODE
>>
>> This ioctl is only specific to OCFS2.
>>
>> Signed-off-by: Tristan Ye <tristan.ye@oracle.com>
>> Signed-off-by: Joel Becker <joel.becker@oracle.com>
>> ---
>>  fs/ocfs2/ioctl.c       |  677 ++++++++++++++++++++++++++++++++++++++++++++++++
>>  fs/ocfs2/ocfs2_ioctl.h |  117 +++++++++
>>  2 files changed, 794 insertions(+), 0 deletions(-)
>>
>> diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
>> index 7d9d9c1..58fb935 100644
>> --- a/fs/ocfs2/ioctl.c
>> +++ b/fs/ocfs2/ioctl.c
>> @@ -23,8 +23,13 @@
>>  #include "ioctl.h"
>>  #include "resize.h"
>>  #include "refcounttree.h"
>> +#include "sysfile.h"
>> +#include "buffer_head_io.h"
>> +#include "suballoc.h"
>> +
>>  
>>  #include <linux/ext2_fs.h>
>> +#include <linux/compat.h>
>>  
>>  static int ocfs2_get_inode_attr(struct inode *inode, unsigned *flags)
>>  {
>> @@ -109,6 +114,664 @@ bail:
>>  	return status;
>>  }
>>  
>> +int ocfs2_info_handle_blocksize(struct inode *inode,
>> +				struct ocfs2_info_request __user *user_req)
>> +{
>> +	int status = 0;
>> +	struct ocfs2_info_blocksize req_bs;
>>   
>>     
>
> req_bs is a mouthful. How about "oib"? For the next function it could
> be "oic"? We do this everywhere. eg, osb, di, et, el, etc.
>
>   
>> +
>> +	if (copy_from_user(&req_bs, user_req,
>> +			   sizeof(struct ocfs2_info_blocksize))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +	req_bs.ir_blocksize = inode->i_sb->s_blocksize;
>> +	req_bs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>> +
>> +	if (copy_to_user((struct ocfs2_info_blocksize __user *)user_req,
>> +			 &req_bs,
>> +			 sizeof(struct ocfs2_info_blocksize))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +bail:
>> +	mlog_exit(status);
>>   
>>     
>
> Don't have a exit without an entry.  But I think we can do
> without the entry/exit mlogs in these ioctls.
>
>   
>> +	return status;
>> +}
>> +
>> +int ocfs2_info_handle_clustersize(struct inode *inode,
>> +				  struct ocfs2_info_request __user *user_req)
>> +{
>> +	int status = 0;
>>   
>>     
>
> Or, you could set it to -EFAULT here and set it to 0 just before bail.
>
>   
>> +	struct ocfs2_info_clustersize req_cs;
>> +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>> +
>> +	if (copy_from_user(&req_cs, user_req,
>> +			   sizeof(struct ocfs2_info_clustersize))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +	req_cs.ir_clustersize = osb->s_clustersize;
>> +	req_cs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>> +
>> +	if (copy_to_user((struct ocfs2_info_clustersize __user *)user_req,
>> +			 &req_cs,
>> +			 sizeof(struct ocfs2_info_clustersize))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +bail:
>> +	mlog_exit(status);
>> +	return status;
>> +}
>> +
>> +int ocfs2_info_handle_slotnum(struct inode *inode,
>> +			      struct ocfs2_info_request __user *user_req)
>> +{
>> +	int status = 0;
>> +	struct ocfs2_info_slotnum req_sn;
>> +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>> +
>> +	if (copy_from_user(&req_sn, user_req,
>> +			   sizeof(struct ocfs2_info_slotnum))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +	req_sn.ir_slotnum = osb->max_slots;
>> +	req_sn.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>> +
>> +	if (copy_to_user((struct ocfs2_info_slotnum __user *)user_req,
>> +			 &req_sn,
>> +			 sizeof(struct ocfs2_info_slotnum))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +bail:
>> +	mlog_exit(status);
>> +	return status;
>> +}
>> +
>> +int ocfs2_info_handle_label(struct inode *inode,
>> +			    struct ocfs2_info_request __user *user_req)
>> +{
>> +	int status = 0;
>> +	struct ocfs2_info_label req_lb;
>> +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>> +
>> +	if (copy_from_user(&req_lb, user_req,
>> +			   sizeof(struct ocfs2_info_label))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +	memcpy(req_lb.ir_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN);
>> +	req_lb.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>> +
>> +	if (copy_to_user((struct ocfs2_info_label __user *)user_req,
>> +			 &req_lb,
>> +			 sizeof(struct ocfs2_info_label))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +bail:
>> +	mlog_exit(status);
>> +	return status;
>> +}
>> +
>> +int ocfs2_info_handle_uuid(struct inode *inode,
>> +			   struct ocfs2_info_request __user *user_req)
>> +{
>> +	int status = 0;
>> +	struct ocfs2_info_uuid req_uuid;
>> +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>> +
>> +	if (copy_from_user(&req_uuid, user_req,
>> +			   sizeof(struct ocfs2_info_uuid))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +	memcpy(req_uuid.ir_uuid_str, osb->uuid_str, OCFS2_INFO_VOL_UUIDSTR_LEN);
>> +	req_uuid.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>> +
>> +	if (copy_to_user((struct ocfs2_info_uuid __user *)user_req,
>> +			 &req_uuid,
>> +			 sizeof(struct ocfs2_info_uuid))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +bail:
>> +	mlog_exit(status);
>> +	return status;
>> +}
>> +
>> +int ocfs2_info_handle_fs_features(struct inode *inode,
>> +				  struct ocfs2_info_request __user *user_req)
>> +{
>> +	int status = 0;
>> +	struct ocfs2_info_fs_features req_fs;
>> +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>> +
>> +	if (copy_from_user(&req_fs, user_req,
>> +			   sizeof(struct ocfs2_info_fs_features))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +	req_fs.ir_compat_features = osb->s_feature_compat;
>> +	req_fs.ir_incompat_features = osb->s_feature_incompat;
>> +	req_fs.ir_ro_compat_features = osb->s_feature_ro_compat;
>> +	req_fs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>> +
>> +	if (copy_to_user((struct ocfs2_info_fs_features __user *)user_req,
>> +			 &req_fs,
>> +			 sizeof(struct ocfs2_info_fs_features))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +bail:
>> +	mlog_exit(status);
>> +	return status;
>> +}
>> +
>> +void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg,
>> +			   unsigned int chunksize)
>> +{
>> +	int index;
>> +
>> +	index = __ilog2_u32(chunksize);
>> +	if (index >= OCFS2_INFO_MAX_HIST)
>> +		index = OCFS2_INFO_MAX_HIST - 1;
>> +
>> +	ffg->ir_ffg.ir_fc_hist.ir_fc_chunks[index]++;
>> +	ffg->ir_ffg.ir_fc_hist.ir_fc_clusters[index] += chunksize;
>> +
>> +	if (chunksize > ffg->ir_ffg.ir_max)
>> +		ffg->ir_ffg.ir_max = chunksize;
>> +
>> +	if (chunksize < ffg->ir_ffg.ir_min)
>> +		ffg->ir_ffg.ir_min = chunksize;
>> +
>> +	ffg->ir_ffg.ir_avg += chunksize;
>> +	ffg->ir_ffg.ir_free_chunks_real++;
>> +}
>> +
>> +int ocfs2_info_scan_chain(struct inode *gb_inode,
>> +			  struct ocfs2_dinode *gb_dinode,
>> +			  struct ocfs2_info_freefrag *ffg,
>> +			  struct ocfs2_chain_rec *rec,
>> +			  unsigned int chunks_in_group)
>> +{
>> +	int status = 0, used;
>> +	u64 blkno;
>> +
>> +	struct buffer_head *bh = 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 (!le32_to_cpu(rec->c_free))
>> +		goto bail;
>> +
>> +	do {
>> +		if (!bg)
>> +			blkno = le64_to_cpu(rec->c_blkno);
>> +		else
>> +			blkno = le64_to_cpu(bg->bg_next_group);
>> +
>> +		if (bh) {
>> +			brelse(bh);
>> +			bh = NULL;
>> +		}
>> +
>> +		status = ocfs2_read_group_descriptor(gb_inode, gb_dinode,
>> +						     blkno, &bh);
>> +		if (status < 0) {
>> +			mlog(ML_ERROR, "Can't read the group descriptor # "
>> +			     "%llu from device.", (unsigned long long)blkno);
>> +			status = -EIO;
>> +			goto bail;
>> +		}
>> +
>> +		bg = (struct ocfs2_group_desc *)bh->b_data;
>> +
>> +		if (!le16_to_cpu(bg->bg_free_bits_count))
>> +			continue;
>> +
>> +		max_bits = le16_to_cpu(bg->bg_bits);
>> +		offset = 0;
>> +
>> +		for (chunk = 0; chunk < chunks_in_group; chunk++) {
>> +
>> +			/* Last chunk may be not a entire one */
>> +			if ((offset + ffg->ir_chunksize) > max_bits)
>> +				num_clusters = max_bits - offset;
>> +			else
>> +				num_clusters = ffg->ir_chunksize;
>> +
>> +			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)) {
>> +					ocfs2_info_update_ffg(ffg,
>> +							      last_chunksize);
>> +					last_chunksize = 0;
>> +				}
>> +
>> +				offset++;
>> +			}
>> +
>> +			if (chunk_free == ffg->ir_chunksize)
>> +				ffg->ir_ffg.ir_free_chunks++;
>> +		}
>> +
>> +		/* we need to update the info of last free chunk */
>> +		if (last_chunksize)
>> +			ocfs2_info_update_ffg(ffg, last_chunksize);
>> +
>> +	} while (le64_to_cpu(bg->bg_next_group));
>> +
>> +bail:
>> +	brelse(bh);
>> +
>> +	mlog_exit(status);
>> +	return status;
>> +}
>> +
>> +int ocfs2_info_scan_bitmap(struct inode *gb_inode,
>> +			   struct ocfs2_dinode *gb_dinode,
>> +			   struct ocfs2_info_freefrag *ffg,
>> +			   struct ocfs2_chain_list *cl)
>> +{
>> +	int status = 0, i;
>> +	unsigned int chunks_in_group;
>> +	struct ocfs2_chain_rec *rec = NULL;
>> +
>> +	chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->ir_chunksize + 1;
>> +
>> +	for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) {
>> +
>> +		rec = &(cl->cl_recs[i]);
>> +		status = ocfs2_info_scan_chain(gb_inode, gb_dinode,
>> +					       ffg, rec, chunks_in_group);
>> +		if (status)
>> +			goto bail;
>> +	}
>> +
>> +bail:
>> +	mlog_exit(status);
>> +	return status;
>> +}
>> +
>> +int ocfs2_info_handle_freefrag(struct inode *inode,
>> +			       struct ocfs2_info_request __user *user_req)
>> +{
>> +	int status = 0, unlock = 0;
>> +
>> +	struct ocfs2_info_freefrag req_ffg;
>> +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>> +	struct buffer_head *bh = NULL;
>> +	struct inode *gb_inode = NULL;
>> +	struct ocfs2_dinode *gb_dinode = NULL;
>> +	struct ocfs2_chain_list *cl = NULL;
>> +
>> +	if (copy_from_user(&req_ffg, user_req,
>> +			   sizeof(struct ocfs2_info_freefrag))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +	/*
>> +	 * chunksize from userspace should be power of 2,
>> +	 */
>> +	if ((req_ffg.ir_chunksize & (req_ffg.ir_chunksize - 1)) ||
>> +	    (!req_ffg.ir_chunksize)) {
>> +		status = -EINVAL;
>> +		goto bail;
>> +	}
>> +
>> +	memset(&req_ffg.ir_ffg, 0, sizeof(struct ocfs2_info_freefrag_stats));
>> +	req_ffg.ir_ffg.ir_min = ~0U;
>> +
>> +	gb_inode = ocfs2_get_system_file_inode(osb,
>> +					       GLOBAL_BITMAP_SYSTEM_INODE,
>> +					       OCFS2_INVALID_SLOT);
>> +	if (!gb_inode) {
>> +		mlog(ML_ERROR, "failed to get bitmap inode\n");
>> +		status = -EIO;
>> +		goto bail;
>> +	}
>> +
>> +	mutex_lock(&gb_inode->i_mutex);
>> +
>> +	if (!(req_ffg.ir_request.ir_flags & OCFS2_INFO_FL_NON_COHERENT)) {
>> +		status = ocfs2_inode_lock(gb_inode, &bh, 0);
>> +		if (status < 0) {
>> +			mlog_errno(status);
>> +			goto bail_mutex_unlock;
>> +		}
>> +		unlock = 1;
>> +
>> +	} else {
>> +		status = ocfs2_read_inode_block(gb_inode, &bh);
>> +		if (status < 0) {
>> +			mlog_errno(status);
>> +			goto bail;
>> +		}
>> +	}
>> +
>> +	gb_dinode = (struct ocfs2_dinode *)bh->b_data;
>> +
>> +	req_ffg.ir_ffg.ir_clusters =
>> +			le32_to_cpu(gb_dinode->id1.bitmap1.i_total);
>> +	req_ffg.ir_ffg.ir_free_clusters = req_ffg.ir_ffg.ir_clusters -
>> +				le32_to_cpu(gb_dinode->id1.bitmap1.i_used);
>> +
>> +	cl = &(gb_dinode->id2.i_chain);
>> +
>> +	/* Chunksize from userspace should be less than clusters in a group */
>> +	if (req_ffg.ir_chunksize > le16_to_cpu(cl->cl_cpg)) {
>> +		status = -EINVAL;
>> +		goto bail;
>> +	}
>> +
>> +	status = ocfs2_info_scan_bitmap(gb_inode, gb_dinode, &req_ffg, cl);
>> +	if (status)
>> +		goto bail;
>> +
>> +	if (req_ffg.ir_ffg.ir_free_chunks_real)
>> +		req_ffg.ir_ffg.ir_avg = (req_ffg.ir_ffg.ir_avg /
>> +					req_ffg.ir_ffg.ir_free_chunks_real);
>> +
>> +	req_ffg.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>> +
>> +	if (copy_to_user((struct ocfs2_info_freefrag __user *)user_req,
>> +			 &req_ffg,
>> +			 sizeof(struct ocfs2_info_freefrag))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +bail:
>> +	if (unlock)
>> +		ocfs2_inode_unlock(gb_inode, 0);
>> +
>> +bail_mutex_unlock:
>> +	if (gb_inode)
>> +		mutex_unlock(&gb_inode->i_mutex);
>> +
>> +	iput(gb_inode);
>> +	brelse(bh);
>> +
>> +	mlog_exit(status);
>> +	return status;
>> +}
>> +
>> +int ocfs2_info_scan_inode_alloc(struct inode *inode_alloc,
>> +				struct ocfs2_info_freeinode *fi,
>> +				__u32 slotnum,
>> +				int flags)
>> +{
>> +	int status = 0, unlock = 0;
>> +
>> +	struct buffer_head *bh = NULL;
>> +	struct ocfs2_dinode *dinode_alloc = NULL;
>> +
>> +	mutex_lock(&inode_alloc->i_mutex);
>> +
>> +	if (!(flags & OCFS2_INFO_FL_NON_COHERENT)) {
>> +		status = ocfs2_inode_lock(inode_alloc, &bh, 0);
>> +		if (status < 0) {
>> +			mlog_errno(status);
>> +			goto bail_mutex_unlock;
>> +		}
>> +		unlock = 1;
>> +
>> +	} else {
>> +
>> +		status = ocfs2_read_inode_block(inode_alloc, &bh);
>> +		if (status < 0) {
>> +			mlog_errno(status);
>> +			goto bail;
>> +		}
>> +	}
>> +
>> +	dinode_alloc = (struct ocfs2_dinode *)bh->b_data;
>> +
>> +	fi->ir_fi_stat[slotnum].ir_total =
>> +		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total);
>> +	fi->ir_fi_stat[slotnum].ir_free =
>> +		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) -
>> +		le32_to_cpu(dinode_alloc->id1.bitmap1.i_used);
>> +bail:
>> +	if (unlock)
>> +		ocfs2_inode_unlock(inode_alloc, 0);
>> +
>> +bail_mutex_unlock:
>> +	mutex_unlock(&inode_alloc->i_mutex);
>> +
>> +	iput(inode_alloc);
>> +	brelse(bh);
>> +
>> +	mlog_exit(status);
>> +	return status;
>> +}
>> +
>> +int ocfs2_info_handle_freeinode(struct inode *inode,
>> +				struct ocfs2_info_request __user *user_req)
>> +{
>> +	int status = 0, i;
>> +
>> +	struct ocfs2_info_freeinode req_fi;
>> +	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>> +	struct inode *inode_alloc = NULL;
>> +
>> +
>> +	if (copy_from_user(&req_fi, user_req,
>> +			   sizeof(struct ocfs2_info_freeinode))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +	req_fi.ir_slotnum = osb->max_slots;
>> +
>> +	for (i = 0; i < req_fi.ir_slotnum; i++) {
>> +		inode_alloc =
>> +			ocfs2_get_system_file_inode(osb,
>> +						    INODE_ALLOC_SYSTEM_INODE,
>> +						    i);
>> +		if (!inode_alloc) {
>> +			mlog(ML_ERROR, "unable to get alloc inode in slot %u\n",
>> +			     (u32)i);
>> +			status = -EIO;
>> +			goto bail;
>> +		}
>> +
>> +		status = ocfs2_info_scan_inode_alloc(inode_alloc, &req_fi, i,
>> +						req_fi.ir_request.ir_flags);
>> +		if (status < 0)
>> +			goto bail;
>> +	}
>> +
>> +	req_fi.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>> +
>> +	if (copy_to_user((struct ocfs2_info_freeinode __user *)user_req,
>> +			 &req_fi,
>> +			 sizeof(struct ocfs2_info_freeinode))) {
>> +		status = -EFAULT;
>> +	}
>> +
>> +bail:
>> +
>> +	mlog_exit(status);
>> +	return status;
>> +}
>> +
>> +int ocfs2_info_handle_unknown(struct inode *inode,
>> +			      struct ocfs2_info_request __user *user_req)
>> +{
>> +	int status = 0;
>> +	struct ocfs2_info_request req;
>> +
>> +	if (copy_from_user(&req, user_req, sizeof(struct ocfs2_info_request))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +	req.ir_flags &= ~OCFS2_INFO_FL_FILLED;
>> +
>> +	if (copy_to_user(user_req, &req,
>> +			 sizeof(struct ocfs2_info_request))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +bail:
>> +	mlog_exit(status);
>> +	return status;
>> +}
>> +
>> +int ocfs2_info_handle_request(struct inode *inode,
>> +			      struct ocfs2_info_request __user *user_req)
>> +{
>> +	int status = 0;
>> +	struct ocfs2_info_request req;
>> +
>> +	if (copy_from_user(&req, user_req, sizeof(struct ocfs2_info_request))) {
>> +		status = -EFAULT;
>> +		goto bail;
>> +	}
>> +
>> +	if (req.ir_magic != OCFS2_INFO_MAGIC) {
>> +		status = -EINVAL;
>> +		goto bail;
>> +	}
>> +
>> +	switch (req.ir_code) {
>> +	case OCFS2_INFO_BLOCKSIZE:
>> +		if (req.ir_size != sizeof(struct ocfs2_info_blocksize)) {
>> +			status = -EINVAL;
>> +			break;
>> +		}
>> +		status = ocfs2_info_handle_blocksize(inode, user_req);
>> +		break;
>> +	case OCFS2_INFO_CLUSTERSIZE:
>> +		if (req.ir_size != sizeof(struct ocfs2_info_clustersize)) {
>> +			status = -EINVAL;
>> +			break;
>> +		}
>> +		status = ocfs2_info_handle_clustersize(inode, user_req);
>> +		break;
>> +	case OCFS2_INFO_SLOTNUM:
>> +		if (req.ir_size != sizeof(struct ocfs2_info_slotnum)) {
>> +			status = -EINVAL;
>> +			break;
>> +		}
>> +		status = ocfs2_info_handle_slotnum(inode, user_req);
>> +		break;
>> +	case OCFS2_INFO_LABEL:
>> +		if (req.ir_size != sizeof(struct ocfs2_info_label)) {
>> +			status = -EINVAL;
>> +			break;
>> +		}
>> +		status = ocfs2_info_handle_label(inode, user_req);
>> +		break;
>> +	case OCFS2_INFO_UUID:
>> +		if (req.ir_size != sizeof(struct ocfs2_info_uuid)) {
>> +			status = -EINVAL;
>> +			break;
>> +		}
>> +		status = ocfs2_info_handle_uuid(inode, user_req);
>> +		break;
>> +	case OCFS2_INFO_FS_FEATURES:
>> +		if (req.ir_size != sizeof(struct ocfs2_info_fs_features)) {
>> +			status = -EINVAL;
>> +			break;
>> +		}
>> +		status = ocfs2_info_handle_fs_features(inode, user_req);
>> +		break;
>> +	case OCFS2_INFO_FREEFRAG:
>> +		if (req.ir_size != sizeof(struct ocfs2_info_freefrag)) {
>> +			status = -EINVAL;
>> +			break;
>> +		}
>> +		status = ocfs2_info_handle_freefrag(inode, user_req);
>> +		break;
>> +	case OCFS2_INFO_FREEINODE:
>> +		if (req.ir_size != sizeof(struct ocfs2_info_freeinode)) {
>> +			status = -EINVAL;
>> +			break;
>> +		}
>> +		status = ocfs2_info_handle_freeinode(inode, user_req);
>> +		break;
>> +	default:
>> +		status = ocfs2_info_handle_unknown(inode, user_req);
>> +		break;
>> +	}
>>   
>>     
>
> For the first round, can we remove the freefrag and freeinode.
> That needs a closer look. I would like the scalars entered first.
>
>
>   
>> +
>> +bail:
>> +	mlog_exit(status);
>> +	return status;
>> +}
>> +
>> +int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info,
>> +		      int compat_flag)
>> +{
>> +	int i, status = 0;
>> +	u64 req_addr;
>> +	struct ocfs2_info_request __user *reqp;
>> +
>> +	if ((info->info_count > OCFS2_INFO_MAX_REQUEST) ||
>> +	    (!info->info_requests)) {
>> +		status = -EINVAL;
>> +		goto bail;
>> +	}
>> +
>> +	for (i = 0; i < info->info_count; i++) {
>> +		status = -EFAULT;
>> +		if (compat_flag) {
>> +			if (get_user(req_addr,
>> +			     (u64 __user *)compat_ptr(info->info_requests) + i))
>> +				goto bail;
>> +		} else {
>> +			if (get_user(req_addr,
>> +				     (u64 __user *)(info->info_requests) + i))
>> +				goto bail;
>> +		}
>> +
>> +		reqp = (struct ocfs2_info_request *)req_addr;
>> +		if (!reqp) {
>> +			status = -EINVAL;
>> +			goto bail;
>> +		}
>> +
>> +		status = ocfs2_info_handle_request(inode, reqp);
>> +		if (status)
>> +			goto bail;
>> +	}
>> +
>> +bail:
>> +	mlog_exit(status);
>> +	return status;
>> +}
>> +
>>  long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>>  {
>>  	struct inode *inode = filp->f_path.dentry->d_inode;
>> @@ -120,6 +783,7 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>>  	struct reflink_arguments args;
>>  	const char *old_path, *new_path;
>>  	bool preserve;
>> +	struct ocfs2_info info;
>>  
>>  	switch (cmd) {
>>  	case OCFS2_IOC_GETFLAGS:
>> @@ -174,6 +838,12 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>>  		preserve = (args.preserve != 0);
>>  
>>  		return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve);
>> +	case OCFS2_IOC_INFO:
>> +		if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
>> +				   sizeof(struct ocfs2_info)))
>> +			return -EFAULT;
>> +
>> +		return ocfs2_info_handle(inode, &info, 0);
>>  	default:
>>  		return -ENOTTY;
>>  	}
>> @@ -185,6 +855,7 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
>>  	bool preserve;
>>  	struct reflink_arguments args;
>>  	struct inode *inode = file->f_path.dentry->d_inode;
>> +	struct ocfs2_info info;
>>  
>>  	switch (cmd) {
>>  	case OCFS2_IOC32_GETFLAGS:
>> @@ -209,6 +880,12 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
>>  
>>  		return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path),
>>  					   compat_ptr(args.new_path), preserve);
>> +	case OCFS2_IOC_INFO:
>> +		if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
>> +				   sizeof(struct ocfs2_info)))
>> +			return -EFAULT;
>> +
>> +		return ocfs2_info_handle(inode, &info, 1);
>>  	default:
>>  		return -ENOIOCTLCMD;
>>  	}
>> diff --git a/fs/ocfs2/ocfs2_ioctl.h b/fs/ocfs2/ocfs2_ioctl.h
>> index 2d3420a..9cfe5be 100644
>> --- a/fs/ocfs2/ocfs2_ioctl.h
>> +++ b/fs/ocfs2/ocfs2_ioctl.h
>> @@ -76,4 +76,121 @@ struct reflink_arguments {
>>  };
>>  #define OCFS2_IOC_REFLINK	_IOW('o', 4, struct reflink_arguments)
>>  
>> +/* Following definitions dedicated for ocfs2_info_request ioctls. */
>> +
>> +#define OCFS2_INFO_VOL_UUID_LEN		(16)
>> +#define OCFS2_INFO_MAX_VOL_LABEL_LEN	(64)
>>   
>>     
>
> Why not use the existing #defines in ocfs2_fs.h
>
>   
>> +#define OCFS2_INFO_VOL_UUIDSTR_LEN	(OCFS2_INFO_VOL_UUID_LEN * 2 + 1)
>>   
>> +#define OCFS2_INFO_MAX_SLOTS		(255)
>> +#define OCFS2_INFO_MAX_HIST		(32)
>> +
>> +#define OCFS2_INFO_MAX_REQUEST		(50)
>> +
>> +/* Magic number of all requests */
>> +#define OCFS2_INFO_MAGIC		(0x4F32494E)
>> +
>> +/*
>> + * Always try to separate info request into small pieces to
>> + * guarantee the backward&forward compatibility.
>> + */
>> +
>> +struct ocfs2_info {
>> +	__u64 info_requests;	/* Array of __u64 pointers to requests */
>> +	__u32 info_count;	/* Number of requests in info_requests */
>>   
>>     
> oi_requests, oi_count
>
>   
>> +};
>> +
>> +struct ocfs2_info_request {
>> +/*00*/	__u32 ir_magic;	/* Magic number */
>> +	__u32 ir_code;	/* Info request code */
>> +	__u32 ir_size;	/* Size of request */
>> +	__u32 ir_flags;	/* Request flags */
>> +/*10*/	/* Request specific fields */
>> +};
>> +
>> +struct ocfs2_info_clustersize {
>> +	struct ocfs2_info_request ir_request;
>> +	__u32 ir_clustersize;
>>   
>>     
> ic_req, ic_clustersize
>
>   
>> +};
>> +
>> +struct ocfs2_info_blocksize {
>> +	struct ocfs2_info_request ir_request;
>> +	__u32 ir_blocksize;
>>   
>>     
>
> ib_req, ib_blocksize
>
>   
>> +};
>> +
>> +struct ocfs2_info_slotnum {
>> +	struct ocfs2_info_request ir_request;
>> +	__u16 ir_slotnum;
>>   
>>     
>
> ocfs2_info_maxslots
> is_req, is_max_slots
>
> (slotnum could be later used to query the slot use by that node)
>
>   
>> +};
>> +
>> +struct ocfs2_info_label {
>> +	struct ocfs2_info_request ir_request;
>> +	__u8	ir_label[OCFS2_INFO_MAX_VOL_LABEL_LEN];
>> +};
>>   
>>     
>
> il_req, il_label
>
> You get the drift.
>
>   
>> +
>> +struct ocfs2_info_uuid {
>> +	struct ocfs2_info_request ir_request;
>> +	__u8	ir_uuid_str[OCFS2_INFO_VOL_UUIDSTR_LEN];
>> +};
>> +
>> +struct ocfs2_info_fs_features {
>> +	struct ocfs2_info_request ir_request;
>> +	__u32 ir_compat_features;
>> +	__u32 ir_incompat_features;
>> +	__u32 ir_ro_compat_features;
>> +};
>> +
>> +struct ocfs2_info_freefrag {
>> +	struct ocfs2_info_request ir_request;
>> +	__u32 ir_chunksize; /* chunksize in clusters(in) */
>> +	struct ocfs2_info_freefrag_stats { /* (out) */
>> +		__u32 ir_clusters;
>> +		__u32 ir_free_clusters;
>> +		__u32 ir_free_chunks;
>> +		__u32 ir_free_chunks_real;
>> +		__u32 ir_min; /* Minimum free chunksize in clusters */
>> +		__u32 ir_max;
>> +		__u32 ir_avg;
>> +		struct ocfs2_info_free_chunk_list {
>> +			__u32 ir_fc_chunks[OCFS2_INFO_MAX_HIST];
>> +			__u32 ir_fc_clusters[OCFS2_INFO_MAX_HIST];
>> +		} ir_fc_hist;
>> +	} ir_ffg;
>> +};
>> +
>> +struct ocfs2_info_freeinode {
>> +	struct ocfs2_info_request ir_request;
>> +	__u32 ir_slotnum; /* out */
>> +	struct ocfs2_info_local_fi {
>> +		__u64 ir_total;
>> +		__u64 ir_free;
>> +	} ir_fi_stat[OCFS2_INFO_MAX_SLOTS];
>> +};
>> +
>> +/* Codes for ocfs2_info_request */
>> +enum ocfs2_info_type {
>> +	OCFS2_INFO_CLUSTERSIZE = 1,
>> +	OCFS2_INFO_BLOCKSIZE,
>> +	OCFS2_INFO_SLOTNUM,
>> +	OCFS2_INFO_LABEL,
>> +	OCFS2_INFO_UUID,
>> +	OCFS2_INFO_FS_FEATURES,
>> +	OCFS2_INFO_FREEFRAG,
>> +	OCFS2_INFO_FREEINODE,
>> +	NUM_OCFS2_INFO_TYPE
>>   
>>     
>
> OCFS2_INFO_NUM_TYPES
>
>   
>> +};
>> +
>> +/* Flags for struct ocfs2_info_request */
>> +/* Filled by the caller */
>> +#define OCFS2_INFO_FL_NON_COHERENT	(0x00000001)	/* Cluster coherency not
>> +							   required. This is a hint.
>> +							   It is up to ocfs2 whether
>> +							   the request can be fulfilled
>> +							   without locking. */
>> +/* Filled by ocfs2 */
>> +#define OCFS2_INFO_FL_FILLED		(0x80000000)	/* Filesystem understood
>> +							   this request and
>> +							   filled in the answer */
>> +
>> +#define OCFS2_IOC_INFO		_IOR('o', 5, struct ocfs2_info)
>> +
>>  #endif /* OCFS2_IOCTL_H */
>>   
>>     
>
>
> _______________________________________________
> Ocfs2-devel mailing list
> Ocfs2-devel at oss.oracle.com
> http://oss.oracle.com/mailman/listinfo/ocfs2-devel
>   

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6.
  2010-04-16 22:26     ` Sunil Mushran
@ 2010-04-17  0:11       ` Joel Becker
  2010-04-17  1:03         ` Sunil Mushran
  2010-04-19  2:22       ` tristan
  1 sibling, 1 reply; 17+ messages in thread
From: Joel Becker @ 2010-04-17  0:11 UTC (permalink / raw)
  To: ocfs2-devel

On Fri, Apr 16, 2010 at 03:26:14PM -0700, Sunil Mushran wrote:
> Or, you could take it a step further and make some macros. Keep these
> macros in close proximity to these functions.
> 
> #define _ocfs2_from_user(a, b)  \      
>                 copy_from_user(&(a), (a), typeof(a))
> 
> #define _ocfs2_to_user(a, b)    \      
>                 copy_to_user((typeof(a) __user *)b, &(a), sizeof(a))
> 
>         if (_ocfs2_from_user(oic, req))
>                 goto bail;
> 
>         if (_ocfs2_to_user(oic, req))
>                 goto bail;

	Give them a more specific name; no one should thing they are
generic c-t-u functions.  ocfs2-info-to-user?  bonus if they handle the
info types automagically.

Joel
-- 

"Born under a bad sign.
 I been down since I began to crawl.
 If it wasn't for bad luck,
 I wouldn't have no luck at all."

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6.
  2010-04-17  0:11       ` Joel Becker
@ 2010-04-17  1:03         ` Sunil Mushran
  0 siblings, 0 replies; 17+ messages in thread
From: Sunil Mushran @ 2010-04-17  1:03 UTC (permalink / raw)
  To: ocfs2-devel

Agreed. o2info_from_user() would be one suggestion.

On Apr 16, 2010, at 5:11 PM, Joel Becker <Joel.Becker@oracle.com> wrote:

> On Fri, Apr 16, 2010 at 03:26:14PM -0700, Sunil Mushran wrote:
>> Or, you could take it a step further and make some macros. Keep these
>> macros in close proximity to these functions.
>>
>> #define _ocfs2_from_user(a, b)  \
>>                copy_from_user(&(a), (a), typeof(a))
>>
>> #define _ocfs2_to_user(a, b)    \
>>                copy_to_user((typeof(a) __user *)b, &(a), sizeof(a))
>>
>>        if (_ocfs2_from_user(oic, req))
>>                goto bail;
>>
>>        if (_ocfs2_to_user(oic, req))
>>                goto bail;
>
>    Give them a more specific name; no one should thing they are
> generic c-t-u functions.  ocfs2-info-to-user?  bonus if they handle  
> the
> info types automagically.
>
> Joel
> -- 
>
> "Born under a bad sign.
> I been down since I began to crawl.
> If it wasn't for bad luck,
> I wouldn't have no luck at all."
>
> Joel Becker
> Principal Software Developer
> Oracle
> E-mail: joel.becker at oracle.com
> Phone: (650) 506-8127

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6.
  2010-04-16 21:50   ` Sunil Mushran
  2010-04-16 22:26     ` Sunil Mushran
@ 2010-04-19  1:48     ` tristan
  1 sibling, 0 replies; 17+ messages in thread
From: tristan @ 2010-04-19  1:48 UTC (permalink / raw)
  To: ocfs2-devel

Sunil Mushran wrote:
> I like the overall flow. I just have some comments on style.
>
> Also, would prefer if you drop freeinode and freefrag for the
> first checkin. That not only needs a closer look.

It will be fine to me.

>
> For the next drop, just have this patch. The first one is in mainline.

Yes, the first patch of moving ioctl definitions from ocfs2_fs.h to 
ocfs2_ioctl.h has been in mainline now, the reason why I post here is 
for readability.


>
> Thanks
>
> Tristan Ye wrote:
>> The reason why we need this ioctl is to offer the none-privileged
>> end-user a possibility to get filesys info gathering.
>>
>> We use OCFS2_IOC_INFO to manipulate the new ioctl, userspace passes a
>> structure to kernel containing an array of request pointers and request
>> count, such as,
>>
>> * From userspace:
>>
>> struct ocfs2_info_blocksize brq = {
>>         .ir_request = {
>>                 .ir_magic = OCFS2_INFO_MAGIC,
>>                 .ir_code = OCFS2_INFO_BLOCKSIZE,
>>                 ...
>>         }
>>         ...
>> }
>>
>> struct ocfs2_info_clustersize crq = {
>>         ...
>> }
>>
>> uint64_t reqs[2] = {(unsigned long)&brq,
>>                     (unsigned long)&crq};
>>
>> struct ocfs2_info info = {
>>         .ir_requests = reqs,
>>         .ir_count = 2,
>> }
>>
>> ret = ioctl(fd, OCFS2_IOC_INFO, &info);
>>
>> * In kernel:
>>
>> Get the request pointers from *info*, then handle each request one 
>> bye one.
>>
>> Idea here is to make the spearated request small enough to guarantee
>> a better backward&forward compatibility since a small piece of request
>> would be less likely to be broken if filesys on raw disk get changed.
>>
>> Currently, following 8 ioctls get implemented per the requirement from
>> userspace tool o2info, and I believe it will grow over time:-)
>>
>>         OCFS2_INFO_CLUSTERSIZE
>>         OCFS2_INFO_BLOCKSIZE
>>         OCFS2_INFO_SLOTNUM
>>         OCFS2_INFO_LABEL
>>         OCFS2_INFO_UUID
>>         OCFS2_INFO_FS_FEATURES
>>         OCFS2_INFO_FREEFRAG
>>         OCFS2_INFO_FREEINODE
>>
>> This ioctl is only specific to OCFS2.
>>
>> Signed-off-by: Tristan Ye <tristan.ye@oracle.com>
>> Signed-off-by: Joel Becker <joel.becker@oracle.com>
>> ---
>>  fs/ocfs2/ioctl.c       |  677 
>> ++++++++++++++++++++++++++++++++++++++++++++++++
>>  fs/ocfs2/ocfs2_ioctl.h |  117 +++++++++
>>  2 files changed, 794 insertions(+), 0 deletions(-)
>>
>> diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
>> index 7d9d9c1..58fb935 100644
>> --- a/fs/ocfs2/ioctl.c
>> +++ b/fs/ocfs2/ioctl.c
>> @@ -23,8 +23,13 @@
>>  #include "ioctl.h"
>>  #include "resize.h"
>>  #include "refcounttree.h"
>> +#include "sysfile.h"
>> +#include "buffer_head_io.h"
>> +#include "suballoc.h"
>> +
>>  
>>  #include <linux/ext2_fs.h>
>> +#include <linux/compat.h>
>>  
>>  static int ocfs2_get_inode_attr(struct inode *inode, unsigned *flags)
>>  {
>> @@ -109,6 +114,664 @@ bail:
>>      return status;
>>  }
>>  
>> +int ocfs2_info_handle_blocksize(struct inode *inode,
>> +                struct ocfs2_info_request __user *user_req)
>> +{
>> +    int status = 0;
>> +    struct ocfs2_info_blocksize req_bs;
>>   
>
> req_bs is a mouthful. How about "oib"? For the next function it could
> be "oic"? We do this everywhere. eg, osb, di, et, el, etc.
>
>> +
>> +    if (copy_from_user(&req_bs, user_req,
>> +               sizeof(struct ocfs2_info_blocksize))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +    req_bs.ir_blocksize = inode->i_sb->s_blocksize;
>> +    req_bs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>> +
>> +    if (copy_to_user((struct ocfs2_info_blocksize __user *)user_req,
>> +             &req_bs,
>> +             sizeof(struct ocfs2_info_blocksize))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +bail:
>> +    mlog_exit(status);
>>   
>
> Don't have a exit without an entry.  But I think we can do
> without the entry/exit mlogs in these ioctls.
>
>> +    return status;
>> +}
>> +
>> +int ocfs2_info_handle_clustersize(struct inode *inode,
>> +                  struct ocfs2_info_request __user *user_req)
>> +{
>> +    int status = 0;
>>   
>
> Or, you could set it to -EFAULT here and set it to 0 just before bail.
>
>> +    struct ocfs2_info_clustersize req_cs;
>> +    struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>> +
>> +    if (copy_from_user(&req_cs, user_req,
>> +               sizeof(struct ocfs2_info_clustersize))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +    req_cs.ir_clustersize = osb->s_clustersize;
>> +    req_cs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>> +
>> +    if (copy_to_user((struct ocfs2_info_clustersize __user *)user_req,
>> +             &req_cs,
>> +             sizeof(struct ocfs2_info_clustersize))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +bail:
>> +    mlog_exit(status);
>> +    return status;
>> +}
>> +
>> +int ocfs2_info_handle_slotnum(struct inode *inode,
>> +                  struct ocfs2_info_request __user *user_req)
>> +{
>> +    int status = 0;
>> +    struct ocfs2_info_slotnum req_sn;
>> +    struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>> +
>> +    if (copy_from_user(&req_sn, user_req,
>> +               sizeof(struct ocfs2_info_slotnum))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +    req_sn.ir_slotnum = osb->max_slots;
>> +    req_sn.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>> +
>> +    if (copy_to_user((struct ocfs2_info_slotnum __user *)user_req,
>> +             &req_sn,
>> +             sizeof(struct ocfs2_info_slotnum))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +bail:
>> +    mlog_exit(status);
>> +    return status;
>> +}
>> +
>> +int ocfs2_info_handle_label(struct inode *inode,
>> +                struct ocfs2_info_request __user *user_req)
>> +{
>> +    int status = 0;
>> +    struct ocfs2_info_label req_lb;
>> +    struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>> +
>> +    if (copy_from_user(&req_lb, user_req,
>> +               sizeof(struct ocfs2_info_label))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +    memcpy(req_lb.ir_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN);
>> +    req_lb.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>> +
>> +    if (copy_to_user((struct ocfs2_info_label __user *)user_req,
>> +             &req_lb,
>> +             sizeof(struct ocfs2_info_label))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +bail:
>> +    mlog_exit(status);
>> +    return status;
>> +}
>> +
>> +int ocfs2_info_handle_uuid(struct inode *inode,
>> +               struct ocfs2_info_request __user *user_req)
>> +{
>> +    int status = 0;
>> +    struct ocfs2_info_uuid req_uuid;
>> +    struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>> +
>> +    if (copy_from_user(&req_uuid, user_req,
>> +               sizeof(struct ocfs2_info_uuid))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +    memcpy(req_uuid.ir_uuid_str, osb->uuid_str, 
>> OCFS2_INFO_VOL_UUIDSTR_LEN);
>> +    req_uuid.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>> +
>> +    if (copy_to_user((struct ocfs2_info_uuid __user *)user_req,
>> +             &req_uuid,
>> +             sizeof(struct ocfs2_info_uuid))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +bail:
>> +    mlog_exit(status);
>> +    return status;
>> +}
>> +
>> +int ocfs2_info_handle_fs_features(struct inode *inode,
>> +                  struct ocfs2_info_request __user *user_req)
>> +{
>> +    int status = 0;
>> +    struct ocfs2_info_fs_features req_fs;
>> +    struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>> +
>> +    if (copy_from_user(&req_fs, user_req,
>> +               sizeof(struct ocfs2_info_fs_features))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +    req_fs.ir_compat_features = osb->s_feature_compat;
>> +    req_fs.ir_incompat_features = osb->s_feature_incompat;
>> +    req_fs.ir_ro_compat_features = osb->s_feature_ro_compat;
>> +    req_fs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>> +
>> +    if (copy_to_user((struct ocfs2_info_fs_features __user *)user_req,
>> +             &req_fs,
>> +             sizeof(struct ocfs2_info_fs_features))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +bail:
>> +    mlog_exit(status);
>> +    return status;
>> +}
>> +
>> +void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg,
>> +               unsigned int chunksize)
>> +{
>> +    int index;
>> +
>> +    index = __ilog2_u32(chunksize);
>> +    if (index >= OCFS2_INFO_MAX_HIST)
>> +        index = OCFS2_INFO_MAX_HIST - 1;
>> +
>> +    ffg->ir_ffg.ir_fc_hist.ir_fc_chunks[index]++;
>> +    ffg->ir_ffg.ir_fc_hist.ir_fc_clusters[index] += chunksize;
>> +
>> +    if (chunksize > ffg->ir_ffg.ir_max)
>> +        ffg->ir_ffg.ir_max = chunksize;
>> +
>> +    if (chunksize < ffg->ir_ffg.ir_min)
>> +        ffg->ir_ffg.ir_min = chunksize;
>> +
>> +    ffg->ir_ffg.ir_avg += chunksize;
>> +    ffg->ir_ffg.ir_free_chunks_real++;
>> +}
>> +
>> +int ocfs2_info_scan_chain(struct inode *gb_inode,
>> +              struct ocfs2_dinode *gb_dinode,
>> +              struct ocfs2_info_freefrag *ffg,
>> +              struct ocfs2_chain_rec *rec,
>> +              unsigned int chunks_in_group)
>> +{
>> +    int status = 0, used;
>> +    u64 blkno;
>> +
>> +    struct buffer_head *bh = 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 (!le32_to_cpu(rec->c_free))
>> +        goto bail;
>> +
>> +    do {
>> +        if (!bg)
>> +            blkno = le64_to_cpu(rec->c_blkno);
>> +        else
>> +            blkno = le64_to_cpu(bg->bg_next_group);
>> +
>> +        if (bh) {
>> +            brelse(bh);
>> +            bh = NULL;
>> +        }
>> +
>> +        status = ocfs2_read_group_descriptor(gb_inode, gb_dinode,
>> +                             blkno, &bh);
>> +        if (status < 0) {
>> +            mlog(ML_ERROR, "Can't read the group descriptor # "
>> +                 "%llu from device.", (unsigned long long)blkno);
>> +            status = -EIO;
>> +            goto bail;
>> +        }
>> +
>> +        bg = (struct ocfs2_group_desc *)bh->b_data;
>> +
>> +        if (!le16_to_cpu(bg->bg_free_bits_count))
>> +            continue;
>> +
>> +        max_bits = le16_to_cpu(bg->bg_bits);
>> +        offset = 0;
>> +
>> +        for (chunk = 0; chunk < chunks_in_group; chunk++) {
>> +
>> +            /* Last chunk may be not a entire one */
>> +            if ((offset + ffg->ir_chunksize) > max_bits)
>> +                num_clusters = max_bits - offset;
>> +            else
>> +                num_clusters = ffg->ir_chunksize;
>> +
>> +            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)) {
>> +                    ocfs2_info_update_ffg(ffg,
>> +                                  last_chunksize);
>> +                    last_chunksize = 0;
>> +                }
>> +
>> +                offset++;
>> +            }
>> +
>> +            if (chunk_free == ffg->ir_chunksize)
>> +                ffg->ir_ffg.ir_free_chunks++;
>> +        }
>> +
>> +        /* we need to update the info of last free chunk */
>> +        if (last_chunksize)
>> +            ocfs2_info_update_ffg(ffg, last_chunksize);
>> +
>> +    } while (le64_to_cpu(bg->bg_next_group));
>> +
>> +bail:
>> +    brelse(bh);
>> +
>> +    mlog_exit(status);
>> +    return status;
>> +}
>> +
>> +int ocfs2_info_scan_bitmap(struct inode *gb_inode,
>> +               struct ocfs2_dinode *gb_dinode,
>> +               struct ocfs2_info_freefrag *ffg,
>> +               struct ocfs2_chain_list *cl)
>> +{
>> +    int status = 0, i;
>> +    unsigned int chunks_in_group;
>> +    struct ocfs2_chain_rec *rec = NULL;
>> +
>> +    chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->ir_chunksize + 1;
>> +
>> +    for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) {
>> +
>> +        rec = &(cl->cl_recs[i]);
>> +        status = ocfs2_info_scan_chain(gb_inode, gb_dinode,
>> +                           ffg, rec, chunks_in_group);
>> +        if (status)
>> +            goto bail;
>> +    }
>> +
>> +bail:
>> +    mlog_exit(status);
>> +    return status;
>> +}
>> +
>> +int ocfs2_info_handle_freefrag(struct inode *inode,
>> +                   struct ocfs2_info_request __user *user_req)
>> +{
>> +    int status = 0, unlock = 0;
>> +
>> +    struct ocfs2_info_freefrag req_ffg;
>> +    struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>> +    struct buffer_head *bh = NULL;
>> +    struct inode *gb_inode = NULL;
>> +    struct ocfs2_dinode *gb_dinode = NULL;
>> +    struct ocfs2_chain_list *cl = NULL;
>> +
>> +    if (copy_from_user(&req_ffg, user_req,
>> +               sizeof(struct ocfs2_info_freefrag))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +    /*
>> +     * chunksize from userspace should be power of 2,
>> +     */
>> +    if ((req_ffg.ir_chunksize & (req_ffg.ir_chunksize - 1)) ||
>> +        (!req_ffg.ir_chunksize)) {
>> +        status = -EINVAL;
>> +        goto bail;
>> +    }
>> +
>> +    memset(&req_ffg.ir_ffg, 0, sizeof(struct 
>> ocfs2_info_freefrag_stats));
>> +    req_ffg.ir_ffg.ir_min = ~0U;
>> +
>> +    gb_inode = ocfs2_get_system_file_inode(osb,
>> +                           GLOBAL_BITMAP_SYSTEM_INODE,
>> +                           OCFS2_INVALID_SLOT);
>> +    if (!gb_inode) {
>> +        mlog(ML_ERROR, "failed to get bitmap inode\n");
>> +        status = -EIO;
>> +        goto bail;
>> +    }
>> +
>> +    mutex_lock(&gb_inode->i_mutex);
>> +
>> +    if (!(req_ffg.ir_request.ir_flags & OCFS2_INFO_FL_NON_COHERENT)) {
>> +        status = ocfs2_inode_lock(gb_inode, &bh, 0);
>> +        if (status < 0) {
>> +            mlog_errno(status);
>> +            goto bail_mutex_unlock;
>> +        }
>> +        unlock = 1;
>> +
>> +    } else {
>> +        status = ocfs2_read_inode_block(gb_inode, &bh);
>> +        if (status < 0) {
>> +            mlog_errno(status);
>> +            goto bail;
>> +        }
>> +    }
>> +
>> +    gb_dinode = (struct ocfs2_dinode *)bh->b_data;
>> +
>> +    req_ffg.ir_ffg.ir_clusters =
>> +            le32_to_cpu(gb_dinode->id1.bitmap1.i_total);
>> +    req_ffg.ir_ffg.ir_free_clusters = req_ffg.ir_ffg.ir_clusters -
>> +                le32_to_cpu(gb_dinode->id1.bitmap1.i_used);
>> +
>> +    cl = &(gb_dinode->id2.i_chain);
>> +
>> +    /* Chunksize from userspace should be less than clusters in a 
>> group */
>> +    if (req_ffg.ir_chunksize > le16_to_cpu(cl->cl_cpg)) {
>> +        status = -EINVAL;
>> +        goto bail;
>> +    }
>> +
>> +    status = ocfs2_info_scan_bitmap(gb_inode, gb_dinode, &req_ffg, cl);
>> +    if (status)
>> +        goto bail;
>> +
>> +    if (req_ffg.ir_ffg.ir_free_chunks_real)
>> +        req_ffg.ir_ffg.ir_avg = (req_ffg.ir_ffg.ir_avg /
>> +                    req_ffg.ir_ffg.ir_free_chunks_real);
>> +
>> +    req_ffg.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>> +
>> +    if (copy_to_user((struct ocfs2_info_freefrag __user *)user_req,
>> +             &req_ffg,
>> +             sizeof(struct ocfs2_info_freefrag))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +bail:
>> +    if (unlock)
>> +        ocfs2_inode_unlock(gb_inode, 0);
>> +
>> +bail_mutex_unlock:
>> +    if (gb_inode)
>> +        mutex_unlock(&gb_inode->i_mutex);
>> +
>> +    iput(gb_inode);
>> +    brelse(bh);
>> +
>> +    mlog_exit(status);
>> +    return status;
>> +}
>> +
>> +int ocfs2_info_scan_inode_alloc(struct inode *inode_alloc,
>> +                struct ocfs2_info_freeinode *fi,
>> +                __u32 slotnum,
>> +                int flags)
>> +{
>> +    int status = 0, unlock = 0;
>> +
>> +    struct buffer_head *bh = NULL;
>> +    struct ocfs2_dinode *dinode_alloc = NULL;
>> +
>> +    mutex_lock(&inode_alloc->i_mutex);
>> +
>> +    if (!(flags & OCFS2_INFO_FL_NON_COHERENT)) {
>> +        status = ocfs2_inode_lock(inode_alloc, &bh, 0);
>> +        if (status < 0) {
>> +            mlog_errno(status);
>> +            goto bail_mutex_unlock;
>> +        }
>> +        unlock = 1;
>> +
>> +    } else {
>> +
>> +        status = ocfs2_read_inode_block(inode_alloc, &bh);
>> +        if (status < 0) {
>> +            mlog_errno(status);
>> +            goto bail;
>> +        }
>> +    }
>> +
>> +    dinode_alloc = (struct ocfs2_dinode *)bh->b_data;
>> +
>> +    fi->ir_fi_stat[slotnum].ir_total =
>> +        le32_to_cpu(dinode_alloc->id1.bitmap1.i_total);
>> +    fi->ir_fi_stat[slotnum].ir_free =
>> +        le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) -
>> +        le32_to_cpu(dinode_alloc->id1.bitmap1.i_used);
>> +bail:
>> +    if (unlock)
>> +        ocfs2_inode_unlock(inode_alloc, 0);
>> +
>> +bail_mutex_unlock:
>> +    mutex_unlock(&inode_alloc->i_mutex);
>> +
>> +    iput(inode_alloc);
>> +    brelse(bh);
>> +
>> +    mlog_exit(status);
>> +    return status;
>> +}
>> +
>> +int ocfs2_info_handle_freeinode(struct inode *inode,
>> +                struct ocfs2_info_request __user *user_req)
>> +{
>> +    int status = 0, i;
>> +
>> +    struct ocfs2_info_freeinode req_fi;
>> +    struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>> +    struct inode *inode_alloc = NULL;
>> +
>> +
>> +    if (copy_from_user(&req_fi, user_req,
>> +               sizeof(struct ocfs2_info_freeinode))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +    req_fi.ir_slotnum = osb->max_slots;
>> +
>> +    for (i = 0; i < req_fi.ir_slotnum; i++) {
>> +        inode_alloc =
>> +            ocfs2_get_system_file_inode(osb,
>> +                            INODE_ALLOC_SYSTEM_INODE,
>> +                            i);
>> +        if (!inode_alloc) {
>> +            mlog(ML_ERROR, "unable to get alloc inode in slot %u\n",
>> +                 (u32)i);
>> +            status = -EIO;
>> +            goto bail;
>> +        }
>> +
>> +        status = ocfs2_info_scan_inode_alloc(inode_alloc, &req_fi, i,
>> +                        req_fi.ir_request.ir_flags);
>> +        if (status < 0)
>> +            goto bail;
>> +    }
>> +
>> +    req_fi.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>> +
>> +    if (copy_to_user((struct ocfs2_info_freeinode __user *)user_req,
>> +             &req_fi,
>> +             sizeof(struct ocfs2_info_freeinode))) {
>> +        status = -EFAULT;
>> +    }
>> +
>> +bail:
>> +
>> +    mlog_exit(status);
>> +    return status;
>> +}
>> +
>> +int ocfs2_info_handle_unknown(struct inode *inode,
>> +                  struct ocfs2_info_request __user *user_req)
>> +{
>> +    int status = 0;
>> +    struct ocfs2_info_request req;
>> +
>> +    if (copy_from_user(&req, user_req, sizeof(struct 
>> ocfs2_info_request))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +    req.ir_flags &= ~OCFS2_INFO_FL_FILLED;
>> +
>> +    if (copy_to_user(user_req, &req,
>> +             sizeof(struct ocfs2_info_request))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +bail:
>> +    mlog_exit(status);
>> +    return status;
>> +}
>> +
>> +int ocfs2_info_handle_request(struct inode *inode,
>> +                  struct ocfs2_info_request __user *user_req)
>> +{
>> +    int status = 0;
>> +    struct ocfs2_info_request req;
>> +
>> +    if (copy_from_user(&req, user_req, sizeof(struct 
>> ocfs2_info_request))) {
>> +        status = -EFAULT;
>> +        goto bail;
>> +    }
>> +
>> +    if (req.ir_magic != OCFS2_INFO_MAGIC) {
>> +        status = -EINVAL;
>> +        goto bail;
>> +    }
>> +
>> +    switch (req.ir_code) {
>> +    case OCFS2_INFO_BLOCKSIZE:
>> +        if (req.ir_size != sizeof(struct ocfs2_info_blocksize)) {
>> +            status = -EINVAL;
>> +            break;
>> +        }
>> +        status = ocfs2_info_handle_blocksize(inode, user_req);
>> +        break;
>> +    case OCFS2_INFO_CLUSTERSIZE:
>> +        if (req.ir_size != sizeof(struct ocfs2_info_clustersize)) {
>> +            status = -EINVAL;
>> +            break;
>> +        }
>> +        status = ocfs2_info_handle_clustersize(inode, user_req);
>> +        break;
>> +    case OCFS2_INFO_SLOTNUM:
>> +        if (req.ir_size != sizeof(struct ocfs2_info_slotnum)) {
>> +            status = -EINVAL;
>> +            break;
>> +        }
>> +        status = ocfs2_info_handle_slotnum(inode, user_req);
>> +        break;
>> +    case OCFS2_INFO_LABEL:
>> +        if (req.ir_size != sizeof(struct ocfs2_info_label)) {
>> +            status = -EINVAL;
>> +            break;
>> +        }
>> +        status = ocfs2_info_handle_label(inode, user_req);
>> +        break;
>> +    case OCFS2_INFO_UUID:
>> +        if (req.ir_size != sizeof(struct ocfs2_info_uuid)) {
>> +            status = -EINVAL;
>> +            break;
>> +        }
>> +        status = ocfs2_info_handle_uuid(inode, user_req);
>> +        break;
>> +    case OCFS2_INFO_FS_FEATURES:
>> +        if (req.ir_size != sizeof(struct ocfs2_info_fs_features)) {
>> +            status = -EINVAL;
>> +            break;
>> +        }
>> +        status = ocfs2_info_handle_fs_features(inode, user_req);
>> +        break;
>> +    case OCFS2_INFO_FREEFRAG:
>> +        if (req.ir_size != sizeof(struct ocfs2_info_freefrag)) {
>> +            status = -EINVAL;
>> +            break;
>> +        }
>> +        status = ocfs2_info_handle_freefrag(inode, user_req);
>> +        break;
>> +    case OCFS2_INFO_FREEINODE:
>> +        if (req.ir_size != sizeof(struct ocfs2_info_freeinode)) {
>> +            status = -EINVAL;
>> +            break;
>> +        }
>> +        status = ocfs2_info_handle_freeinode(inode, user_req);
>> +        break;
>> +    default:
>> +        status = ocfs2_info_handle_unknown(inode, user_req);
>> +        break;
>> +    }
>>   
>
> For the first round, can we remove the freefrag and freeinode.
> That needs a closer look. I would like the scalars entered first.
>
>
>> +
>> +bail:
>> +    mlog_exit(status);
>> +    return status;
>> +}
>> +
>> +int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info,
>> +              int compat_flag)
>> +{
>> +    int i, status = 0;
>> +    u64 req_addr;
>> +    struct ocfs2_info_request __user *reqp;
>> +
>> +    if ((info->info_count > OCFS2_INFO_MAX_REQUEST) ||
>> +        (!info->info_requests)) {
>> +        status = -EINVAL;
>> +        goto bail;
>> +    }
>> +
>> +    for (i = 0; i < info->info_count; i++) {
>> +        status = -EFAULT;
>> +        if (compat_flag) {
>> +            if (get_user(req_addr,
>> +                 (u64 __user *)compat_ptr(info->info_requests) + i))
>> +                goto bail;
>> +        } else {
>> +            if (get_user(req_addr,
>> +                     (u64 __user *)(info->info_requests) + i))
>> +                goto bail;
>> +        }
>> +
>> +        reqp = (struct ocfs2_info_request *)req_addr;
>> +        if (!reqp) {
>> +            status = -EINVAL;
>> +            goto bail;
>> +        }
>> +
>> +        status = ocfs2_info_handle_request(inode, reqp);
>> +        if (status)
>> +            goto bail;
>> +    }
>> +
>> +bail:
>> +    mlog_exit(status);
>> +    return status;
>> +}
>> +
>>  long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long 
>> arg)
>>  {
>>      struct inode *inode = filp->f_path.dentry->d_inode;
>> @@ -120,6 +783,7 @@ long ocfs2_ioctl(struct file *filp, unsigned int 
>> cmd, unsigned long arg)
>>      struct reflink_arguments args;
>>      const char *old_path, *new_path;
>>      bool preserve;
>> +    struct ocfs2_info info;
>>  
>>      switch (cmd) {
>>      case OCFS2_IOC_GETFLAGS:
>> @@ -174,6 +838,12 @@ long ocfs2_ioctl(struct file *filp, unsigned int 
>> cmd, unsigned long arg)
>>          preserve = (args.preserve != 0);
>>  
>>          return ocfs2_reflink_ioctl(inode, old_path, new_path, 
>> preserve);
>> +    case OCFS2_IOC_INFO:
>> +        if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
>> +                   sizeof(struct ocfs2_info)))
>> +            return -EFAULT;
>> +
>> +        return ocfs2_info_handle(inode, &info, 0);
>>      default:
>>          return -ENOTTY;
>>      }
>> @@ -185,6 +855,7 @@ long ocfs2_compat_ioctl(struct file *file, 
>> unsigned cmd, unsigned long arg)
>>      bool preserve;
>>      struct reflink_arguments args;
>>      struct inode *inode = file->f_path.dentry->d_inode;
>> +    struct ocfs2_info info;
>>  
>>      switch (cmd) {
>>      case OCFS2_IOC32_GETFLAGS:
>> @@ -209,6 +880,12 @@ long ocfs2_compat_ioctl(struct file *file, 
>> unsigned cmd, unsigned long arg)
>>  
>>          return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path),
>>                         compat_ptr(args.new_path), preserve);
>> +    case OCFS2_IOC_INFO:
>> +        if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
>> +                   sizeof(struct ocfs2_info)))
>> +            return -EFAULT;
>> +
>> +        return ocfs2_info_handle(inode, &info, 1);
>>      default:
>>          return -ENOIOCTLCMD;
>>      }
>> diff --git a/fs/ocfs2/ocfs2_ioctl.h b/fs/ocfs2/ocfs2_ioctl.h
>> index 2d3420a..9cfe5be 100644
>> --- a/fs/ocfs2/ocfs2_ioctl.h
>> +++ b/fs/ocfs2/ocfs2_ioctl.h
>> @@ -76,4 +76,121 @@ struct reflink_arguments {
>>  };
>>  #define OCFS2_IOC_REFLINK    _IOW('o', 4, struct reflink_arguments)
>>  
>> +/* Following definitions dedicated for ocfs2_info_request ioctls. */
>> +
>> +#define OCFS2_INFO_VOL_UUID_LEN        (16)
>> +#define OCFS2_INFO_MAX_VOL_LABEL_LEN    (64)
>>   
>
> Why not use the existing #defines in ocfs2_fs.h
>
>> +#define OCFS2_INFO_VOL_UUIDSTR_LEN    (OCFS2_INFO_VOL_UUID_LEN * 2 + 1)
>>   +#define OCFS2_INFO_MAX_SLOTS        (255)
>> +#define OCFS2_INFO_MAX_HIST        (32)
>> +
>> +#define OCFS2_INFO_MAX_REQUEST        (50)
>> +
>> +/* Magic number of all requests */
>> +#define OCFS2_INFO_MAGIC        (0x4F32494E)
>> +
>> +/*
>> + * Always try to separate info request into small pieces to
>> + * guarantee the backward&forward compatibility.
>> + */
>> +
>> +struct ocfs2_info {
>> +    __u64 info_requests;    /* Array of __u64 pointers to requests */
>> +    __u32 info_count;    /* Number of requests in info_requests */
>>   
> oi_requests, oi_count
>
>> +};
>> +
>> +struct ocfs2_info_request {
>> +/*00*/    __u32 ir_magic;    /* Magic number */
>> +    __u32 ir_code;    /* Info request code */
>> +    __u32 ir_size;    /* Size of request */
>> +    __u32 ir_flags;    /* Request flags */
>> +/*10*/    /* Request specific fields */
>> +};
>> +
>> +struct ocfs2_info_clustersize {
>> +    struct ocfs2_info_request ir_request;
>> +    __u32 ir_clustersize;
>>   
> ic_req, ic_clustersize
>
>> +};
>> +
>> +struct ocfs2_info_blocksize {
>> +    struct ocfs2_info_request ir_request;
>> +    __u32 ir_blocksize;
>>   
>
> ib_req, ib_blocksize
>
>> +};
>> +
>> +struct ocfs2_info_slotnum {
>> +    struct ocfs2_info_request ir_request;
>> +    __u16 ir_slotnum;
>>   
>
> ocfs2_info_maxslots
> is_req, is_max_slots
>
> (slotnum could be later used to query the slot use by that node)
>
>> +};
>> +
>> +struct ocfs2_info_label {
>> +    struct ocfs2_info_request ir_request;
>> +    __u8    ir_label[OCFS2_INFO_MAX_VOL_LABEL_LEN];
>> +};
>>   
>
> il_req, il_label
>
> You get the drift.
>
>> +
>> +struct ocfs2_info_uuid {
>> +    struct ocfs2_info_request ir_request;
>> +    __u8    ir_uuid_str[OCFS2_INFO_VOL_UUIDSTR_LEN];
>> +};
>> +
>> +struct ocfs2_info_fs_features {
>> +    struct ocfs2_info_request ir_request;
>> +    __u32 ir_compat_features;
>> +    __u32 ir_incompat_features;
>> +    __u32 ir_ro_compat_features;
>> +};
>> +
>> +struct ocfs2_info_freefrag {
>> +    struct ocfs2_info_request ir_request;
>> +    __u32 ir_chunksize; /* chunksize in clusters(in) */
>> +    struct ocfs2_info_freefrag_stats { /* (out) */
>> +        __u32 ir_clusters;
>> +        __u32 ir_free_clusters;
>> +        __u32 ir_free_chunks;
>> +        __u32 ir_free_chunks_real;
>> +        __u32 ir_min; /* Minimum free chunksize in clusters */
>> +        __u32 ir_max;
>> +        __u32 ir_avg;
>> +        struct ocfs2_info_free_chunk_list {
>> +            __u32 ir_fc_chunks[OCFS2_INFO_MAX_HIST];
>> +            __u32 ir_fc_clusters[OCFS2_INFO_MAX_HIST];
>> +        } ir_fc_hist;
>> +    } ir_ffg;
>> +};
>> +
>> +struct ocfs2_info_freeinode {
>> +    struct ocfs2_info_request ir_request;
>> +    __u32 ir_slotnum; /* out */
>> +    struct ocfs2_info_local_fi {
>> +        __u64 ir_total;
>> +        __u64 ir_free;
>> +    } ir_fi_stat[OCFS2_INFO_MAX_SLOTS];
>> +};
>> +
>> +/* Codes for ocfs2_info_request */
>> +enum ocfs2_info_type {
>> +    OCFS2_INFO_CLUSTERSIZE = 1,
>> +    OCFS2_INFO_BLOCKSIZE,
>> +    OCFS2_INFO_SLOTNUM,
>> +    OCFS2_INFO_LABEL,
>> +    OCFS2_INFO_UUID,
>> +    OCFS2_INFO_FS_FEATURES,
>> +    OCFS2_INFO_FREEFRAG,
>> +    OCFS2_INFO_FREEINODE,
>> +    NUM_OCFS2_INFO_TYPE
>>   
>
> OCFS2_INFO_NUM_TYPES
>
>> +};
>> +
>> +/* Flags for struct ocfs2_info_request */
>> +/* Filled by the caller */
>> +#define OCFS2_INFO_FL_NON_COHERENT    (0x00000001)    /* Cluster 
>> coherency not
>> +                               required. This is a hint.
>> +                               It is up to ocfs2 whether
>> +                               the request can be fulfilled
>> +                               without locking. */
>> +/* Filled by ocfs2 */
>> +#define OCFS2_INFO_FL_FILLED        (0x80000000)    /* Filesystem 
>> understood
>> +                               this request and
>> +                               filled in the answer */
>> +
>> +#define OCFS2_IOC_INFO        _IOR('o', 5, struct ocfs2_info)
>> +
>>  #endif /* OCFS2_IOCTL_H */
>>   
>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6.
  2010-04-16 22:26     ` Sunil Mushran
  2010-04-17  0:11       ` Joel Becker
@ 2010-04-19  2:22       ` tristan
  1 sibling, 0 replies; 17+ messages in thread
From: tristan @ 2010-04-19  2:22 UTC (permalink / raw)
  To: ocfs2-devel

Sunil Mushran wrote:
> So just to expand on the style issue. My intention here is not to be a 
> p-i-t-a.
> Just want the code to be readable.
>
> By making those few changes, the boiler plate code of from_user and 
> to_user
> will look like this.
>
>        if (copy_from_user(&oic, req, sizeof(struct 
> ocfs2_info_clustersize)))
>                goto bail;
>
>        if (copy_to_user((struct ocfs2_info_clustersize __user *)req, 
> &oic,
>                          sizeof(struct ocfs2_info_clustersize)))
>                goto bail;

Yes, sunil, you're right, 'oic' makes more sense.

>
>
> Or, you could take it a step further and make some macros. Keep these
> macros in close proximity to these functions.
>
> #define _ocfs2_from_user(a, b)  \                     
> copy_from_user(&(a), (a), typeof(a))
>
> #define _ocfs2_to_user(a, b)    \                     
> copy_to_user((typeof(a) __user *)b, &(a), sizeof(a))
>
>        if (_ocfs2_from_user(oic, req))
>                goto bail;
>
>        if (_ocfs2_to_user(oic, req))
>                goto bail;

Thanks a lot for giving so many comments and conventions on naming 
issues, they're all helpful:)


>
> Sunil
>
>
> Sunil Mushran wrote:
>> I like the overall flow. I just have some comments on style.
>>
>> Also, would prefer if you drop freeinode and freefrag for the
>> first checkin. That not only needs a closer look.
>>
>> For the next drop, just have this patch. The first one is in mainline.
>>
>> Thanks
>>
>> Tristan Ye wrote:
>>  
>>> The reason why we need this ioctl is to offer the none-privileged
>>> end-user a possibility to get filesys info gathering.
>>>
>>> We use OCFS2_IOC_INFO to manipulate the new ioctl, userspace passes a
>>> structure to kernel containing an array of request pointers and request
>>> count, such as,
>>>
>>> * From userspace:
>>>
>>> struct ocfs2_info_blocksize brq = {
>>>         .ir_request = {
>>>                 .ir_magic = OCFS2_INFO_MAGIC,
>>>                 .ir_code = OCFS2_INFO_BLOCKSIZE,
>>>                 ...
>>>         }
>>>         ...
>>> }
>>>
>>> struct ocfs2_info_clustersize crq = {
>>>         ...
>>> }
>>>
>>> uint64_t reqs[2] = {(unsigned long)&brq,
>>>                     (unsigned long)&crq};
>>>
>>> struct ocfs2_info info = {
>>>         .ir_requests = reqs,
>>>         .ir_count = 2,
>>> }
>>>
>>> ret = ioctl(fd, OCFS2_IOC_INFO, &info);
>>>
>>> * In kernel:
>>>
>>> Get the request pointers from *info*, then handle each request one 
>>> bye one.
>>>
>>> Idea here is to make the spearated request small enough to guarantee
>>> a better backward&forward compatibility since a small piece of request
>>> would be less likely to be broken if filesys on raw disk get changed.
>>>
>>> Currently, following 8 ioctls get implemented per the requirement from
>>> userspace tool o2info, and I believe it will grow over time:-)
>>>
>>>         OCFS2_INFO_CLUSTERSIZE
>>>         OCFS2_INFO_BLOCKSIZE
>>>         OCFS2_INFO_SLOTNUM
>>>         OCFS2_INFO_LABEL
>>>         OCFS2_INFO_UUID
>>>         OCFS2_INFO_FS_FEATURES
>>>         OCFS2_INFO_FREEFRAG
>>>         OCFS2_INFO_FREEINODE
>>>
>>> This ioctl is only specific to OCFS2.
>>>
>>> Signed-off-by: Tristan Ye <tristan.ye@oracle.com>
>>> Signed-off-by: Joel Becker <joel.becker@oracle.com>
>>> ---
>>>  fs/ocfs2/ioctl.c       |  677 
>>> ++++++++++++++++++++++++++++++++++++++++++++++++
>>>  fs/ocfs2/ocfs2_ioctl.h |  117 +++++++++
>>>  2 files changed, 794 insertions(+), 0 deletions(-)
>>>
>>> diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
>>> index 7d9d9c1..58fb935 100644
>>> --- a/fs/ocfs2/ioctl.c
>>> +++ b/fs/ocfs2/ioctl.c
>>> @@ -23,8 +23,13 @@
>>>  #include "ioctl.h"
>>>  #include "resize.h"
>>>  #include "refcounttree.h"
>>> +#include "sysfile.h"
>>> +#include "buffer_head_io.h"
>>> +#include "suballoc.h"
>>> +
>>>  
>>>  #include <linux/ext2_fs.h>
>>> +#include <linux/compat.h>
>>>  
>>>  static int ocfs2_get_inode_attr(struct inode *inode, unsigned *flags)
>>>  {
>>> @@ -109,6 +114,664 @@ bail:
>>>      return status;
>>>  }
>>>  
>>> +int ocfs2_info_handle_blocksize(struct inode *inode,
>>> +                struct ocfs2_info_request __user *user_req)
>>> +{
>>> +    int status = 0;
>>> +    struct ocfs2_info_blocksize req_bs;
>>>       
>>
>> req_bs is a mouthful. How about "oib"? For the next function it could
>> be "oic"? We do this everywhere. eg, osb, di, et, el, etc.

Agree, makes more sense than stuffs like 'req_bs', now I realised how 
obscure it looks when being compared to 'oic'...

>>
>>  
>>> +
>>> +    if (copy_from_user(&req_bs, user_req,
>>> +               sizeof(struct ocfs2_info_blocksize))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +    req_bs.ir_blocksize = inode->i_sb->s_blocksize;
>>> +    req_bs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>>> +
>>> +    if (copy_to_user((struct ocfs2_info_blocksize __user *)user_req,
>>> +             &req_bs,
>>> +             sizeof(struct ocfs2_info_blocksize))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +bail:
>>> +    mlog_exit(status);
>>>       
>>
>> Don't have a exit without an entry.  But I think we can do
>> without the entry/exit mlogs in these ioctls.
>>
>>  
>>> +    return status;
>>> +}
>>> +
>>> +int ocfs2_info_handle_clustersize(struct inode *inode,
>>> +                  struct ocfs2_info_request __user *user_req)
>>> +{
>>> +    int status = 0;
>>>       
>>
>> Or, you could set it to -EFAULT here and set it to 0 just before bail.

All right.

>>
>>  
>>> +    struct ocfs2_info_clustersize req_cs;
>>> +    struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>>> +
>>> +    if (copy_from_user(&req_cs, user_req,
>>> +               sizeof(struct ocfs2_info_clustersize))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +    req_cs.ir_clustersize = osb->s_clustersize;
>>> +    req_cs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>>> +
>>> +    if (copy_to_user((struct ocfs2_info_clustersize __user *)user_req,
>>> +             &req_cs,
>>> +             sizeof(struct ocfs2_info_clustersize))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +bail:
>>> +    mlog_exit(status);
>>> +    return status;
>>> +}
>>> +
>>> +int ocfs2_info_handle_slotnum(struct inode *inode,
>>> +                  struct ocfs2_info_request __user *user_req)
>>> +{
>>> +    int status = 0;
>>> +    struct ocfs2_info_slotnum req_sn;
>>> +    struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>>> +
>>> +    if (copy_from_user(&req_sn, user_req,
>>> +               sizeof(struct ocfs2_info_slotnum))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +    req_sn.ir_slotnum = osb->max_slots;
>>> +    req_sn.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>>> +
>>> +    if (copy_to_user((struct ocfs2_info_slotnum __user *)user_req,
>>> +             &req_sn,
>>> +             sizeof(struct ocfs2_info_slotnum))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +bail:
>>> +    mlog_exit(status);
>>> +    return status;
>>> +}
>>> +
>>> +int ocfs2_info_handle_label(struct inode *inode,
>>> +                struct ocfs2_info_request __user *user_req)
>>> +{
>>> +    int status = 0;
>>> +    struct ocfs2_info_label req_lb;
>>> +    struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>>> +
>>> +    if (copy_from_user(&req_lb, user_req,
>>> +               sizeof(struct ocfs2_info_label))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +    memcpy(req_lb.ir_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN);
>>> +    req_lb.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>>> +
>>> +    if (copy_to_user((struct ocfs2_info_label __user *)user_req,
>>> +             &req_lb,
>>> +             sizeof(struct ocfs2_info_label))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +bail:
>>> +    mlog_exit(status);
>>> +    return status;
>>> +}
>>> +
>>> +int ocfs2_info_handle_uuid(struct inode *inode,
>>> +               struct ocfs2_info_request __user *user_req)
>>> +{
>>> +    int status = 0;
>>> +    struct ocfs2_info_uuid req_uuid;
>>> +    struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>>> +
>>> +    if (copy_from_user(&req_uuid, user_req,
>>> +               sizeof(struct ocfs2_info_uuid))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +    memcpy(req_uuid.ir_uuid_str, osb->uuid_str, 
>>> OCFS2_INFO_VOL_UUIDSTR_LEN);
>>> +    req_uuid.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>>> +
>>> +    if (copy_to_user((struct ocfs2_info_uuid __user *)user_req,
>>> +             &req_uuid,
>>> +             sizeof(struct ocfs2_info_uuid))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +bail:
>>> +    mlog_exit(status);
>>> +    return status;
>>> +}
>>> +
>>> +int ocfs2_info_handle_fs_features(struct inode *inode,
>>> +                  struct ocfs2_info_request __user *user_req)
>>> +{
>>> +    int status = 0;
>>> +    struct ocfs2_info_fs_features req_fs;
>>> +    struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>>> +
>>> +    if (copy_from_user(&req_fs, user_req,
>>> +               sizeof(struct ocfs2_info_fs_features))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +    req_fs.ir_compat_features = osb->s_feature_compat;
>>> +    req_fs.ir_incompat_features = osb->s_feature_incompat;
>>> +    req_fs.ir_ro_compat_features = osb->s_feature_ro_compat;
>>> +    req_fs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>>> +
>>> +    if (copy_to_user((struct ocfs2_info_fs_features __user *)user_req,
>>> +             &req_fs,
>>> +             sizeof(struct ocfs2_info_fs_features))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +bail:
>>> +    mlog_exit(status);
>>> +    return status;
>>> +}
>>> +
>>> +void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg,
>>> +               unsigned int chunksize)
>>> +{
>>> +    int index;
>>> +
>>> +    index = __ilog2_u32(chunksize);
>>> +    if (index >= OCFS2_INFO_MAX_HIST)
>>> +        index = OCFS2_INFO_MAX_HIST - 1;
>>> +
>>> +    ffg->ir_ffg.ir_fc_hist.ir_fc_chunks[index]++;
>>> +    ffg->ir_ffg.ir_fc_hist.ir_fc_clusters[index] += chunksize;
>>> +
>>> +    if (chunksize > ffg->ir_ffg.ir_max)
>>> +        ffg->ir_ffg.ir_max = chunksize;
>>> +
>>> +    if (chunksize < ffg->ir_ffg.ir_min)
>>> +        ffg->ir_ffg.ir_min = chunksize;
>>> +
>>> +    ffg->ir_ffg.ir_avg += chunksize;
>>> +    ffg->ir_ffg.ir_free_chunks_real++;
>>> +}
>>> +
>>> +int ocfs2_info_scan_chain(struct inode *gb_inode,
>>> +              struct ocfs2_dinode *gb_dinode,
>>> +              struct ocfs2_info_freefrag *ffg,
>>> +              struct ocfs2_chain_rec *rec,
>>> +              unsigned int chunks_in_group)
>>> +{
>>> +    int status = 0, used;
>>> +    u64 blkno;
>>> +
>>> +    struct buffer_head *bh = 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 (!le32_to_cpu(rec->c_free))
>>> +        goto bail;
>>> +
>>> +    do {
>>> +        if (!bg)
>>> +            blkno = le64_to_cpu(rec->c_blkno);
>>> +        else
>>> +            blkno = le64_to_cpu(bg->bg_next_group);
>>> +
>>> +        if (bh) {
>>> +            brelse(bh);
>>> +            bh = NULL;
>>> +        }
>>> +
>>> +        status = ocfs2_read_group_descriptor(gb_inode, gb_dinode,
>>> +                             blkno, &bh);
>>> +        if (status < 0) {
>>> +            mlog(ML_ERROR, "Can't read the group descriptor # "
>>> +                 "%llu from device.", (unsigned long long)blkno);
>>> +            status = -EIO;
>>> +            goto bail;
>>> +        }
>>> +
>>> +        bg = (struct ocfs2_group_desc *)bh->b_data;
>>> +
>>> +        if (!le16_to_cpu(bg->bg_free_bits_count))
>>> +            continue;
>>> +
>>> +        max_bits = le16_to_cpu(bg->bg_bits);
>>> +        offset = 0;
>>> +
>>> +        for (chunk = 0; chunk < chunks_in_group; chunk++) {
>>> +
>>> +            /* Last chunk may be not a entire one */
>>> +            if ((offset + ffg->ir_chunksize) > max_bits)
>>> +                num_clusters = max_bits - offset;
>>> +            else
>>> +                num_clusters = ffg->ir_chunksize;
>>> +
>>> +            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)) {
>>> +                    ocfs2_info_update_ffg(ffg,
>>> +                                  last_chunksize);
>>> +                    last_chunksize = 0;
>>> +                }
>>> +
>>> +                offset++;
>>> +            }
>>> +
>>> +            if (chunk_free == ffg->ir_chunksize)
>>> +                ffg->ir_ffg.ir_free_chunks++;
>>> +        }
>>> +
>>> +        /* we need to update the info of last free chunk */
>>> +        if (last_chunksize)
>>> +            ocfs2_info_update_ffg(ffg, last_chunksize);
>>> +
>>> +    } while (le64_to_cpu(bg->bg_next_group));
>>> +
>>> +bail:
>>> +    brelse(bh);
>>> +
>>> +    mlog_exit(status);
>>> +    return status;
>>> +}
>>> +
>>> +int ocfs2_info_scan_bitmap(struct inode *gb_inode,
>>> +               struct ocfs2_dinode *gb_dinode,
>>> +               struct ocfs2_info_freefrag *ffg,
>>> +               struct ocfs2_chain_list *cl)
>>> +{
>>> +    int status = 0, i;
>>> +    unsigned int chunks_in_group;
>>> +    struct ocfs2_chain_rec *rec = NULL;
>>> +
>>> +    chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->ir_chunksize + 1;
>>> +
>>> +    for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) {
>>> +
>>> +        rec = &(cl->cl_recs[i]);
>>> +        status = ocfs2_info_scan_chain(gb_inode, gb_dinode,
>>> +                           ffg, rec, chunks_in_group);
>>> +        if (status)
>>> +            goto bail;
>>> +    }
>>> +
>>> +bail:
>>> +    mlog_exit(status);
>>> +    return status;
>>> +}
>>> +
>>> +int ocfs2_info_handle_freefrag(struct inode *inode,
>>> +                   struct ocfs2_info_request __user *user_req)
>>> +{
>>> +    int status = 0, unlock = 0;
>>> +
>>> +    struct ocfs2_info_freefrag req_ffg;
>>> +    struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>>> +    struct buffer_head *bh = NULL;
>>> +    struct inode *gb_inode = NULL;
>>> +    struct ocfs2_dinode *gb_dinode = NULL;
>>> +    struct ocfs2_chain_list *cl = NULL;
>>> +
>>> +    if (copy_from_user(&req_ffg, user_req,
>>> +               sizeof(struct ocfs2_info_freefrag))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +    /*
>>> +     * chunksize from userspace should be power of 2,
>>> +     */
>>> +    if ((req_ffg.ir_chunksize & (req_ffg.ir_chunksize - 1)) ||
>>> +        (!req_ffg.ir_chunksize)) {
>>> +        status = -EINVAL;
>>> +        goto bail;
>>> +    }
>>> +
>>> +    memset(&req_ffg.ir_ffg, 0, sizeof(struct 
>>> ocfs2_info_freefrag_stats));
>>> +    req_ffg.ir_ffg.ir_min = ~0U;
>>> +
>>> +    gb_inode = ocfs2_get_system_file_inode(osb,
>>> +                           GLOBAL_BITMAP_SYSTEM_INODE,
>>> +                           OCFS2_INVALID_SLOT);
>>> +    if (!gb_inode) {
>>> +        mlog(ML_ERROR, "failed to get bitmap inode\n");
>>> +        status = -EIO;
>>> +        goto bail;
>>> +    }
>>> +
>>> +    mutex_lock(&gb_inode->i_mutex);
>>> +
>>> +    if (!(req_ffg.ir_request.ir_flags & OCFS2_INFO_FL_NON_COHERENT)) {
>>> +        status = ocfs2_inode_lock(gb_inode, &bh, 0);
>>> +        if (status < 0) {
>>> +            mlog_errno(status);
>>> +            goto bail_mutex_unlock;
>>> +        }
>>> +        unlock = 1;
>>> +
>>> +    } else {
>>> +        status = ocfs2_read_inode_block(gb_inode, &bh);
>>> +        if (status < 0) {
>>> +            mlog_errno(status);
>>> +            goto bail;
>>> +        }
>>> +    }
>>> +
>>> +    gb_dinode = (struct ocfs2_dinode *)bh->b_data;
>>> +
>>> +    req_ffg.ir_ffg.ir_clusters =
>>> +            le32_to_cpu(gb_dinode->id1.bitmap1.i_total);
>>> +    req_ffg.ir_ffg.ir_free_clusters = req_ffg.ir_ffg.ir_clusters -
>>> +                le32_to_cpu(gb_dinode->id1.bitmap1.i_used);
>>> +
>>> +    cl = &(gb_dinode->id2.i_chain);
>>> +
>>> +    /* Chunksize from userspace should be less than clusters in a 
>>> group */
>>> +    if (req_ffg.ir_chunksize > le16_to_cpu(cl->cl_cpg)) {
>>> +        status = -EINVAL;
>>> +        goto bail;
>>> +    }
>>> +
>>> +    status = ocfs2_info_scan_bitmap(gb_inode, gb_dinode, &req_ffg, 
>>> cl);
>>> +    if (status)
>>> +        goto bail;
>>> +
>>> +    if (req_ffg.ir_ffg.ir_free_chunks_real)
>>> +        req_ffg.ir_ffg.ir_avg = (req_ffg.ir_ffg.ir_avg /
>>> +                    req_ffg.ir_ffg.ir_free_chunks_real);
>>> +
>>> +    req_ffg.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>>> +
>>> +    if (copy_to_user((struct ocfs2_info_freefrag __user *)user_req,
>>> +             &req_ffg,
>>> +             sizeof(struct ocfs2_info_freefrag))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +bail:
>>> +    if (unlock)
>>> +        ocfs2_inode_unlock(gb_inode, 0);
>>> +
>>> +bail_mutex_unlock:
>>> +    if (gb_inode)
>>> +        mutex_unlock(&gb_inode->i_mutex);
>>> +
>>> +    iput(gb_inode);
>>> +    brelse(bh);
>>> +
>>> +    mlog_exit(status);
>>> +    return status;
>>> +}
>>> +
>>> +int ocfs2_info_scan_inode_alloc(struct inode *inode_alloc,
>>> +                struct ocfs2_info_freeinode *fi,
>>> +                __u32 slotnum,
>>> +                int flags)
>>> +{
>>> +    int status = 0, unlock = 0;
>>> +
>>> +    struct buffer_head *bh = NULL;
>>> +    struct ocfs2_dinode *dinode_alloc = NULL;
>>> +
>>> +    mutex_lock(&inode_alloc->i_mutex);
>>> +
>>> +    if (!(flags & OCFS2_INFO_FL_NON_COHERENT)) {
>>> +        status = ocfs2_inode_lock(inode_alloc, &bh, 0);
>>> +        if (status < 0) {
>>> +            mlog_errno(status);
>>> +            goto bail_mutex_unlock;
>>> +        }
>>> +        unlock = 1;
>>> +
>>> +    } else {
>>> +
>>> +        status = ocfs2_read_inode_block(inode_alloc, &bh);
>>> +        if (status < 0) {
>>> +            mlog_errno(status);
>>> +            goto bail;
>>> +        }
>>> +    }
>>> +
>>> +    dinode_alloc = (struct ocfs2_dinode *)bh->b_data;
>>> +
>>> +    fi->ir_fi_stat[slotnum].ir_total =
>>> +        le32_to_cpu(dinode_alloc->id1.bitmap1.i_total);
>>> +    fi->ir_fi_stat[slotnum].ir_free =
>>> +        le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) -
>>> +        le32_to_cpu(dinode_alloc->id1.bitmap1.i_used);
>>> +bail:
>>> +    if (unlock)
>>> +        ocfs2_inode_unlock(inode_alloc, 0);
>>> +
>>> +bail_mutex_unlock:
>>> +    mutex_unlock(&inode_alloc->i_mutex);
>>> +
>>> +    iput(inode_alloc);
>>> +    brelse(bh);
>>> +
>>> +    mlog_exit(status);
>>> +    return status;
>>> +}
>>> +
>>> +int ocfs2_info_handle_freeinode(struct inode *inode,
>>> +                struct ocfs2_info_request __user *user_req)
>>> +{
>>> +    int status = 0, i;
>>> +
>>> +    struct ocfs2_info_freeinode req_fi;
>>> +    struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
>>> +    struct inode *inode_alloc = NULL;
>>> +
>>> +
>>> +    if (copy_from_user(&req_fi, user_req,
>>> +               sizeof(struct ocfs2_info_freeinode))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +    req_fi.ir_slotnum = osb->max_slots;
>>> +
>>> +    for (i = 0; i < req_fi.ir_slotnum; i++) {
>>> +        inode_alloc =
>>> +            ocfs2_get_system_file_inode(osb,
>>> +                            INODE_ALLOC_SYSTEM_INODE,
>>> +                            i);
>>> +        if (!inode_alloc) {
>>> +            mlog(ML_ERROR, "unable to get alloc inode in slot %u\n",
>>> +                 (u32)i);
>>> +            status = -EIO;
>>> +            goto bail;
>>> +        }
>>> +
>>> +        status = ocfs2_info_scan_inode_alloc(inode_alloc, &req_fi, i,
>>> +                        req_fi.ir_request.ir_flags);
>>> +        if (status < 0)
>>> +            goto bail;
>>> +    }
>>> +
>>> +    req_fi.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
>>> +
>>> +    if (copy_to_user((struct ocfs2_info_freeinode __user *)user_req,
>>> +             &req_fi,
>>> +             sizeof(struct ocfs2_info_freeinode))) {
>>> +        status = -EFAULT;
>>> +    }
>>> +
>>> +bail:
>>> +
>>> +    mlog_exit(status);
>>> +    return status;
>>> +}
>>> +
>>> +int ocfs2_info_handle_unknown(struct inode *inode,
>>> +                  struct ocfs2_info_request __user *user_req)
>>> +{
>>> +    int status = 0;
>>> +    struct ocfs2_info_request req;
>>> +
>>> +    if (copy_from_user(&req, user_req, sizeof(struct 
>>> ocfs2_info_request))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +    req.ir_flags &= ~OCFS2_INFO_FL_FILLED;
>>> +
>>> +    if (copy_to_user(user_req, &req,
>>> +             sizeof(struct ocfs2_info_request))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +bail:
>>> +    mlog_exit(status);
>>> +    return status;
>>> +}
>>> +
>>> +int ocfs2_info_handle_request(struct inode *inode,
>>> +                  struct ocfs2_info_request __user *user_req)
>>> +{
>>> +    int status = 0;
>>> +    struct ocfs2_info_request req;
>>> +
>>> +    if (copy_from_user(&req, user_req, sizeof(struct 
>>> ocfs2_info_request))) {
>>> +        status = -EFAULT;
>>> +        goto bail;
>>> +    }
>>> +
>>> +    if (req.ir_magic != OCFS2_INFO_MAGIC) {
>>> +        status = -EINVAL;
>>> +        goto bail;
>>> +    }
>>> +
>>> +    switch (req.ir_code) {
>>> +    case OCFS2_INFO_BLOCKSIZE:
>>> +        if (req.ir_size != sizeof(struct ocfs2_info_blocksize)) {
>>> +            status = -EINVAL;
>>> +            break;
>>> +        }
>>> +        status = ocfs2_info_handle_blocksize(inode, user_req);
>>> +        break;
>>> +    case OCFS2_INFO_CLUSTERSIZE:
>>> +        if (req.ir_size != sizeof(struct ocfs2_info_clustersize)) {
>>> +            status = -EINVAL;
>>> +            break;
>>> +        }
>>> +        status = ocfs2_info_handle_clustersize(inode, user_req);
>>> +        break;
>>> +    case OCFS2_INFO_SLOTNUM:
>>> +        if (req.ir_size != sizeof(struct ocfs2_info_slotnum)) {
>>> +            status = -EINVAL;
>>> +            break;
>>> +        }
>>> +        status = ocfs2_info_handle_slotnum(inode, user_req);
>>> +        break;
>>> +    case OCFS2_INFO_LABEL:
>>> +        if (req.ir_size != sizeof(struct ocfs2_info_label)) {
>>> +            status = -EINVAL;
>>> +            break;
>>> +        }
>>> +        status = ocfs2_info_handle_label(inode, user_req);
>>> +        break;
>>> +    case OCFS2_INFO_UUID:
>>> +        if (req.ir_size != sizeof(struct ocfs2_info_uuid)) {
>>> +            status = -EINVAL;
>>> +            break;
>>> +        }
>>> +        status = ocfs2_info_handle_uuid(inode, user_req);
>>> +        break;
>>> +    case OCFS2_INFO_FS_FEATURES:
>>> +        if (req.ir_size != sizeof(struct ocfs2_info_fs_features)) {
>>> +            status = -EINVAL;
>>> +            break;
>>> +        }
>>> +        status = ocfs2_info_handle_fs_features(inode, user_req);
>>> +        break;
>>> +    case OCFS2_INFO_FREEFRAG:
>>> +        if (req.ir_size != sizeof(struct ocfs2_info_freefrag)) {
>>> +            status = -EINVAL;
>>> +            break;
>>> +        }
>>> +        status = ocfs2_info_handle_freefrag(inode, user_req);
>>> +        break;
>>> +    case OCFS2_INFO_FREEINODE:
>>> +        if (req.ir_size != sizeof(struct ocfs2_info_freeinode)) {
>>> +            status = -EINVAL;
>>> +            break;
>>> +        }
>>> +        status = ocfs2_info_handle_freeinode(inode, user_req);
>>> +        break;
>>> +    default:
>>> +        status = ocfs2_info_handle_unknown(inode, user_req);
>>> +        break;
>>> +    }
>>>       
>>
>> For the first round, can we remove the freefrag and freeinode.
>> That needs a closer look. I would like the scalars entered first.

Fine to me.

>>
>>
>>  
>>> +
>>> +bail:
>>> +    mlog_exit(status);
>>> +    return status;
>>> +}
>>> +
>>> +int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info,
>>> +              int compat_flag)
>>> +{
>>> +    int i, status = 0;
>>> +    u64 req_addr;
>>> +    struct ocfs2_info_request __user *reqp;
>>> +
>>> +    if ((info->info_count > OCFS2_INFO_MAX_REQUEST) ||
>>> +        (!info->info_requests)) {
>>> +        status = -EINVAL;
>>> +        goto bail;
>>> +    }
>>> +
>>> +    for (i = 0; i < info->info_count; i++) {
>>> +        status = -EFAULT;
>>> +        if (compat_flag) {
>>> +            if (get_user(req_addr,
>>> +                 (u64 __user *)compat_ptr(info->info_requests) + i))
>>> +                goto bail;
>>> +        } else {
>>> +            if (get_user(req_addr,
>>> +                     (u64 __user *)(info->info_requests) + i))
>>> +                goto bail;
>>> +        }
>>> +
>>> +        reqp = (struct ocfs2_info_request *)req_addr;
>>> +        if (!reqp) {
>>> +            status = -EINVAL;
>>> +            goto bail;
>>> +        }
>>> +
>>> +        status = ocfs2_info_handle_request(inode, reqp);
>>> +        if (status)
>>> +            goto bail;
>>> +    }
>>> +
>>> +bail:
>>> +    mlog_exit(status);
>>> +    return status;
>>> +}
>>> +
>>>  long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long 
>>> arg)
>>>  {
>>>      struct inode *inode = filp->f_path.dentry->d_inode;
>>> @@ -120,6 +783,7 @@ long ocfs2_ioctl(struct file *filp, unsigned int 
>>> cmd, unsigned long arg)
>>>      struct reflink_arguments args;
>>>      const char *old_path, *new_path;
>>>      bool preserve;
>>> +    struct ocfs2_info info;
>>>  
>>>      switch (cmd) {
>>>      case OCFS2_IOC_GETFLAGS:
>>> @@ -174,6 +838,12 @@ long ocfs2_ioctl(struct file *filp, unsigned 
>>> int cmd, unsigned long arg)
>>>          preserve = (args.preserve != 0);
>>>  
>>>          return ocfs2_reflink_ioctl(inode, old_path, new_path, 
>>> preserve);
>>> +    case OCFS2_IOC_INFO:
>>> +        if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
>>> +                   sizeof(struct ocfs2_info)))
>>> +            return -EFAULT;
>>> +
>>> +        return ocfs2_info_handle(inode, &info, 0);
>>>      default:
>>>          return -ENOTTY;
>>>      }
>>> @@ -185,6 +855,7 @@ long ocfs2_compat_ioctl(struct file *file, 
>>> unsigned cmd, unsigned long arg)
>>>      bool preserve;
>>>      struct reflink_arguments args;
>>>      struct inode *inode = file->f_path.dentry->d_inode;
>>> +    struct ocfs2_info info;
>>>  
>>>      switch (cmd) {
>>>      case OCFS2_IOC32_GETFLAGS:
>>> @@ -209,6 +880,12 @@ long ocfs2_compat_ioctl(struct file *file, 
>>> unsigned cmd, unsigned long arg)
>>>  
>>>          return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path),
>>>                         compat_ptr(args.new_path), preserve);
>>> +    case OCFS2_IOC_INFO:
>>> +        if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
>>> +                   sizeof(struct ocfs2_info)))
>>> +            return -EFAULT;
>>> +
>>> +        return ocfs2_info_handle(inode, &info, 1);
>>>      default:
>>>          return -ENOIOCTLCMD;
>>>      }
>>> diff --git a/fs/ocfs2/ocfs2_ioctl.h b/fs/ocfs2/ocfs2_ioctl.h
>>> index 2d3420a..9cfe5be 100644
>>> --- a/fs/ocfs2/ocfs2_ioctl.h
>>> +++ b/fs/ocfs2/ocfs2_ioctl.h
>>> @@ -76,4 +76,121 @@ struct reflink_arguments {
>>>  };
>>>  #define OCFS2_IOC_REFLINK    _IOW('o', 4, struct reflink_arguments)
>>>  
>>> +/* Following definitions dedicated for ocfs2_info_request ioctls. */
>>> +
>>> +#define OCFS2_INFO_VOL_UUID_LEN        (16)
>>> +#define OCFS2_INFO_MAX_VOL_LABEL_LEN    (64)
>>>       
>>
>> Why not use the existing #defines in ocfs2_fs.h


Good catch, thanks for pointing this out, originally I define this in 
ocfs2_fs.h, where 'OCFS2_VOL_UUID_LEN' and 'OCFS2_MAX_VOL_LABEL_LEN' 
were too far away from being referred, now we're free to use them:)


>>
>>  
>>> +#define OCFS2_INFO_VOL_UUIDSTR_LEN    (OCFS2_INFO_VOL_UUID_LEN * 2 
>>> + 1)
>>>   +#define OCFS2_INFO_MAX_SLOTS        (255)
>>> +#define OCFS2_INFO_MAX_HIST        (32)
>>> +
>>> +#define OCFS2_INFO_MAX_REQUEST        (50)
>>> +
>>> +/* Magic number of all requests */
>>> +#define OCFS2_INFO_MAGIC        (0x4F32494E)
>>> +
>>> +/*
>>> + * Always try to separate info request into small pieces to
>>> + * guarantee the backward&forward compatibility.
>>> + */
>>> +
>>> +struct ocfs2_info {
>>> +    __u64 info_requests;    /* Array of __u64 pointers to requests */
>>> +    __u32 info_count;    /* Number of requests in info_requests */
>>>       
>> oi_requests, oi_count

Makes sense.

>>
>>  
>>> +};
>>> +
>>> +struct ocfs2_info_request {
>>> +/*00*/    __u32 ir_magic;    /* Magic number */
>>> +    __u32 ir_code;    /* Info request code */
>>> +    __u32 ir_size;    /* Size of request */
>>> +    __u32 ir_flags;    /* Request flags */
>>> +/*10*/    /* Request specific fields */
>>> +};
>>> +
>>> +struct ocfs2_info_clustersize {
>>> +    struct ocfs2_info_request ir_request;
>>> +    __u32 ir_clustersize;
>>>       
>> ic_req, ic_clustersize

Just puzzled, why not 'oic_req' and 'oic_clustersize'?

>>
>>  
>>> +};
>>> +
>>> +struct ocfs2_info_blocksize {
>>> +    struct ocfs2_info_request ir_request;
>>> +    __u32 ir_blocksize;
>>>       
>>
>> ib_req, ib_blocksize


also, why not 'oib_request' and 'oib_blocksize'?
>>
>>  
>>> +};
>>> +
>>> +struct ocfs2_info_slotnum {
>>> +    struct ocfs2_info_request ir_request;
>>> +    __u16 ir_slotnum;
>>>       
>>
>> ocfs2_info_maxslots
>> is_req, is_max_slots
>>
>> (slotnum could be later used to query the slot use by that node)

Thanks for making things more clear!

>>
>>  
>>> +};
>>> +
>>> +struct ocfs2_info_label {
>>> +    struct ocfs2_info_request ir_request;
>>> +    __u8    ir_label[OCFS2_INFO_MAX_VOL_LABEL_LEN];
>>> +};
>>>       
>>
>> il_req, il_label
>>
>> You get the drift.
>>
>>  
>>> +
>>> +struct ocfs2_info_uuid {
>>> +    struct ocfs2_info_request ir_request;
>>> +    __u8    ir_uuid_str[OCFS2_INFO_VOL_UUIDSTR_LEN];
>>> +};
>>> +
>>> +struct ocfs2_info_fs_features {
>>> +    struct ocfs2_info_request ir_request;
>>> +    __u32 ir_compat_features;
>>> +    __u32 ir_incompat_features;
>>> +    __u32 ir_ro_compat_features;
>>> +};
>>> +
>>> +struct ocfs2_info_freefrag {
>>> +    struct ocfs2_info_request ir_request;
>>> +    __u32 ir_chunksize; /* chunksize in clusters(in) */
>>> +    struct ocfs2_info_freefrag_stats { /* (out) */
>>> +        __u32 ir_clusters;
>>> +        __u32 ir_free_clusters;
>>> +        __u32 ir_free_chunks;
>>> +        __u32 ir_free_chunks_real;
>>> +        __u32 ir_min; /* Minimum free chunksize in clusters */
>>> +        __u32 ir_max;
>>> +        __u32 ir_avg;
>>> +        struct ocfs2_info_free_chunk_list {
>>> +            __u32 ir_fc_chunks[OCFS2_INFO_MAX_HIST];
>>> +            __u32 ir_fc_clusters[OCFS2_INFO_MAX_HIST];
>>> +        } ir_fc_hist;
>>> +    } ir_ffg;
>>> +};
>>> +
>>> +struct ocfs2_info_freeinode {
>>> +    struct ocfs2_info_request ir_request;
>>> +    __u32 ir_slotnum; /* out */
>>> +    struct ocfs2_info_local_fi {
>>> +        __u64 ir_total;
>>> +        __u64 ir_free;
>>> +    } ir_fi_stat[OCFS2_INFO_MAX_SLOTS];
>>> +};
>>> +
>>> +/* Codes for ocfs2_info_request */
>>> +enum ocfs2_info_type {
>>> +    OCFS2_INFO_CLUSTERSIZE = 1,
>>> +    OCFS2_INFO_BLOCKSIZE,
>>> +    OCFS2_INFO_SLOTNUM,
>>> +    OCFS2_INFO_LABEL,
>>> +    OCFS2_INFO_UUID,
>>> +    OCFS2_INFO_FS_FEATURES,
>>> +    OCFS2_INFO_FREEFRAG,
>>> +    OCFS2_INFO_FREEINODE,
>>> +    NUM_OCFS2_INFO_TYPE
>>>       
>>
>> OCFS2_INFO_NUM_TYPES

All right.

>>
>>  
>>> +};
>>> +
>>> +/* Flags for struct ocfs2_info_request */
>>> +/* Filled by the caller */
>>> +#define OCFS2_INFO_FL_NON_COHERENT    (0x00000001)    /* Cluster 
>>> coherency not
>>> +                               required. This is a hint.
>>> +                               It is up to ocfs2 whether
>>> +                               the request can be fulfilled
>>> +                               without locking. */
>>> +/* Filled by ocfs2 */
>>> +#define OCFS2_INFO_FL_FILLED        (0x80000000)    /* Filesystem 
>>> understood
>>> +                               this request and
>>> +                               filled in the answer */
>>> +
>>> +#define OCFS2_IOC_INFO        _IOR('o', 5, struct ocfs2_info)
>>> +
>>>  #endif /* OCFS2_IOCTL_H */
>>>       
>>
>>
>> _______________________________________________
>> Ocfs2-devel mailing list
>> Ocfs2-devel at oss.oracle.com
>> http://oss.oracle.com/mailman/listinfo/ocfs2-devel
>>   
>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6.
  2010-03-02  5:59 [Ocfs2-devel] [PATCH 1/2] Ocfs2: Move ocfs2 ioctl definitions from ocfs2_fs.h to newly added ocfs2_ioctl.h Tristan Ye
@ 2010-03-02  5:59 ` Tristan Ye
  0 siblings, 0 replies; 17+ messages in thread
From: Tristan Ye @ 2010-03-02  5:59 UTC (permalink / raw)
  To: ocfs2-devel

The reason why we need this ioctl is to offer the none-privileged
end-user a possibility to get filesys info gathering.

We use OCFS2_IOC_INFO to manipulate the new ioctl, userspace passes a
structure to kernel containing an array of request pointers and request
count, such as,

* From userspace:

struct ocfs2_info_blocksize brq = {
        .ir_request = {
                .ir_magic = OCFS2_INFO_MAGIC,
                .ir_code = OCFS2_INFO_BLOCKSIZE,
                ...
        }
        ...
}

struct ocfs2_info_clustersize crq = {
        ...
}

uint64_t reqs[2] = {(unsigned long)&brq,
                    (unsigned long)&crq};

struct ocfs2_info info = {
        .ir_requests = reqs,
        .ir_count = 2,
}

ret = ioctl(fd, OCFS2_IOC_INFO, &info);

* In kernel:

Get the request pointers from *info*, then handle each request one bye one.

Idea here is to make the spearated request small enough to guarantee
a better backward&forward compatibility since a small piece of request
would be less likely to be broken if filesys on raw disk get changed.

Currently, following 8 ioctls get implemented per the requirement from
userspace tool o2info, and I believe it will grow over time:-)

        OCFS2_INFO_CLUSTERSIZE
        OCFS2_INFO_BLOCKSIZE
        OCFS2_INFO_SLOTNUM
        OCFS2_INFO_LABEL
        OCFS2_INFO_UUID
        OCFS2_INFO_FS_FEATURES
        OCFS2_INFO_FREEFRAG
        OCFS2_INFO_FREEINODE

This ioctl is only specific to OCFS2.

Signed-off-by: Tristan Ye <tristan.ye@oracle.com>
---
 fs/ocfs2/ioctl.c       |  677 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ocfs2/ocfs2_ioctl.h |  117 +++++++++
 2 files changed, 794 insertions(+), 0 deletions(-)

diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 7d9d9c1..58fb935 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -23,8 +23,13 @@
 #include "ioctl.h"
 #include "resize.h"
 #include "refcounttree.h"
+#include "sysfile.h"
+#include "buffer_head_io.h"
+#include "suballoc.h"
+
 
 #include <linux/ext2_fs.h>
+#include <linux/compat.h>
 
 static int ocfs2_get_inode_attr(struct inode *inode, unsigned *flags)
 {
@@ -109,6 +114,664 @@ bail:
 	return status;
 }
 
+int ocfs2_info_handle_blocksize(struct inode *inode,
+				struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_blocksize req_bs;
+
+	if (copy_from_user(&req_bs, user_req,
+			   sizeof(struct ocfs2_info_blocksize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_bs.ir_blocksize = inode->i_sb->s_blocksize;
+	req_bs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_blocksize __user *)user_req,
+			 &req_bs,
+			 sizeof(struct ocfs2_info_blocksize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_clustersize(struct inode *inode,
+				  struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_clustersize req_cs;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_cs, user_req,
+			   sizeof(struct ocfs2_info_clustersize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_cs.ir_clustersize = osb->s_clustersize;
+	req_cs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_clustersize __user *)user_req,
+			 &req_cs,
+			 sizeof(struct ocfs2_info_clustersize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_slotnum(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_slotnum req_sn;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_sn, user_req,
+			   sizeof(struct ocfs2_info_slotnum))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_sn.ir_slotnum = osb->max_slots;
+	req_sn.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_slotnum __user *)user_req,
+			 &req_sn,
+			 sizeof(struct ocfs2_info_slotnum))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_label(struct inode *inode,
+			    struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_label req_lb;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_lb, user_req,
+			   sizeof(struct ocfs2_info_label))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	memcpy(req_lb.ir_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN);
+	req_lb.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_label __user *)user_req,
+			 &req_lb,
+			 sizeof(struct ocfs2_info_label))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_uuid(struct inode *inode,
+			   struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_uuid req_uuid;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_uuid, user_req,
+			   sizeof(struct ocfs2_info_uuid))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	memcpy(req_uuid.ir_uuid_str, osb->uuid_str, OCFS2_INFO_VOL_UUIDSTR_LEN);
+	req_uuid.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_uuid __user *)user_req,
+			 &req_uuid,
+			 sizeof(struct ocfs2_info_uuid))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_fs_features(struct inode *inode,
+				  struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_fs_features req_fs;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_fs, user_req,
+			   sizeof(struct ocfs2_info_fs_features))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_fs.ir_compat_features = osb->s_feature_compat;
+	req_fs.ir_incompat_features = osb->s_feature_incompat;
+	req_fs.ir_ro_compat_features = osb->s_feature_ro_compat;
+	req_fs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_fs_features __user *)user_req,
+			 &req_fs,
+			 sizeof(struct ocfs2_info_fs_features))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg,
+			   unsigned int chunksize)
+{
+	int index;
+
+	index = __ilog2_u32(chunksize);
+	if (index >= OCFS2_INFO_MAX_HIST)
+		index = OCFS2_INFO_MAX_HIST - 1;
+
+	ffg->ir_ffg.ir_fc_hist.ir_fc_chunks[index]++;
+	ffg->ir_ffg.ir_fc_hist.ir_fc_clusters[index] += chunksize;
+
+	if (chunksize > ffg->ir_ffg.ir_max)
+		ffg->ir_ffg.ir_max = chunksize;
+
+	if (chunksize < ffg->ir_ffg.ir_min)
+		ffg->ir_ffg.ir_min = chunksize;
+
+	ffg->ir_ffg.ir_avg += chunksize;
+	ffg->ir_ffg.ir_free_chunks_real++;
+}
+
+int ocfs2_info_scan_chain(struct inode *gb_inode,
+			  struct ocfs2_dinode *gb_dinode,
+			  struct ocfs2_info_freefrag *ffg,
+			  struct ocfs2_chain_rec *rec,
+			  unsigned int chunks_in_group)
+{
+	int status = 0, used;
+	u64 blkno;
+
+	struct buffer_head *bh = 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 (!le32_to_cpu(rec->c_free))
+		goto bail;
+
+	do {
+		if (!bg)
+			blkno = le64_to_cpu(rec->c_blkno);
+		else
+			blkno = le64_to_cpu(bg->bg_next_group);
+
+		if (bh) {
+			brelse(bh);
+			bh = NULL;
+		}
+
+		status = ocfs2_read_group_descriptor(gb_inode, gb_dinode,
+						     blkno, &bh);
+		if (status < 0) {
+			mlog(ML_ERROR, "Can't read the group descriptor # "
+			     "%llu from device.", (unsigned long long)blkno);
+			status = -EIO;
+			goto bail;
+		}
+
+		bg = (struct ocfs2_group_desc *)bh->b_data;
+
+		if (!le16_to_cpu(bg->bg_free_bits_count))
+			continue;
+
+		max_bits = le16_to_cpu(bg->bg_bits);
+		offset = 0;
+
+		for (chunk = 0; chunk < chunks_in_group; chunk++) {
+
+			/* Last chunk may be not a entire one */
+			if ((offset + ffg->ir_chunksize) > max_bits)
+				num_clusters = max_bits - offset;
+			else
+				num_clusters = ffg->ir_chunksize;
+
+			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)) {
+					ocfs2_info_update_ffg(ffg,
+							      last_chunksize);
+					last_chunksize = 0;
+				}
+
+				offset++;
+			}
+
+			if (chunk_free == ffg->ir_chunksize)
+				ffg->ir_ffg.ir_free_chunks++;
+		}
+
+		/* we need to update the info of last free chunk */
+		if (last_chunksize)
+			ocfs2_info_update_ffg(ffg, last_chunksize);
+
+	} while (le64_to_cpu(bg->bg_next_group));
+
+bail:
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_scan_bitmap(struct inode *gb_inode,
+			   struct ocfs2_dinode *gb_dinode,
+			   struct ocfs2_info_freefrag *ffg,
+			   struct ocfs2_chain_list *cl)
+{
+	int status = 0, i;
+	unsigned int chunks_in_group;
+	struct ocfs2_chain_rec *rec = NULL;
+
+	chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->ir_chunksize + 1;
+
+	for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) {
+
+		rec = &(cl->cl_recs[i]);
+		status = ocfs2_info_scan_chain(gb_inode, gb_dinode,
+					       ffg, rec, chunks_in_group);
+		if (status)
+			goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_freefrag(struct inode *inode,
+			       struct ocfs2_info_request __user *user_req)
+{
+	int status = 0, unlock = 0;
+
+	struct ocfs2_info_freefrag req_ffg;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct buffer_head *bh = NULL;
+	struct inode *gb_inode = NULL;
+	struct ocfs2_dinode *gb_dinode = NULL;
+	struct ocfs2_chain_list *cl = NULL;
+
+	if (copy_from_user(&req_ffg, user_req,
+			   sizeof(struct ocfs2_info_freefrag))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	/*
+	 * chunksize from userspace should be power of 2,
+	 */
+	if ((req_ffg.ir_chunksize & (req_ffg.ir_chunksize - 1)) ||
+	    (!req_ffg.ir_chunksize)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	memset(&req_ffg.ir_ffg, 0, sizeof(struct ocfs2_info_freefrag_stats));
+	req_ffg.ir_ffg.ir_min = ~0U;
+
+	gb_inode = ocfs2_get_system_file_inode(osb,
+					       GLOBAL_BITMAP_SYSTEM_INODE,
+					       OCFS2_INVALID_SLOT);
+	if (!gb_inode) {
+		mlog(ML_ERROR, "failed to get bitmap inode\n");
+		status = -EIO;
+		goto bail;
+	}
+
+	mutex_lock(&gb_inode->i_mutex);
+
+	if (!(req_ffg.ir_request.ir_flags & OCFS2_INFO_FL_NON_COHERENT)) {
+		status = ocfs2_inode_lock(gb_inode, &bh, 0);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail_mutex_unlock;
+		}
+		unlock = 1;
+
+	} else {
+		status = ocfs2_read_inode_block(gb_inode, &bh);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail;
+		}
+	}
+
+	gb_dinode = (struct ocfs2_dinode *)bh->b_data;
+
+	req_ffg.ir_ffg.ir_clusters =
+			le32_to_cpu(gb_dinode->id1.bitmap1.i_total);
+	req_ffg.ir_ffg.ir_free_clusters = req_ffg.ir_ffg.ir_clusters -
+				le32_to_cpu(gb_dinode->id1.bitmap1.i_used);
+
+	cl = &(gb_dinode->id2.i_chain);
+
+	/* Chunksize from userspace should be less than clusters in a group */
+	if (req_ffg.ir_chunksize > le16_to_cpu(cl->cl_cpg)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	status = ocfs2_info_scan_bitmap(gb_inode, gb_dinode, &req_ffg, cl);
+	if (status)
+		goto bail;
+
+	if (req_ffg.ir_ffg.ir_free_chunks_real)
+		req_ffg.ir_ffg.ir_avg = (req_ffg.ir_ffg.ir_avg /
+					req_ffg.ir_ffg.ir_free_chunks_real);
+
+	req_ffg.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_freefrag __user *)user_req,
+			 &req_ffg,
+			 sizeof(struct ocfs2_info_freefrag))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	if (unlock)
+		ocfs2_inode_unlock(gb_inode, 0);
+
+bail_mutex_unlock:
+	if (gb_inode)
+		mutex_unlock(&gb_inode->i_mutex);
+
+	iput(gb_inode);
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_scan_inode_alloc(struct inode *inode_alloc,
+				struct ocfs2_info_freeinode *fi,
+				__u32 slotnum,
+				int flags)
+{
+	int status = 0, unlock = 0;
+
+	struct buffer_head *bh = NULL;
+	struct ocfs2_dinode *dinode_alloc = NULL;
+
+	mutex_lock(&inode_alloc->i_mutex);
+
+	if (!(flags & OCFS2_INFO_FL_NON_COHERENT)) {
+		status = ocfs2_inode_lock(inode_alloc, &bh, 0);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail_mutex_unlock;
+		}
+		unlock = 1;
+
+	} else {
+
+		status = ocfs2_read_inode_block(inode_alloc, &bh);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail;
+		}
+	}
+
+	dinode_alloc = (struct ocfs2_dinode *)bh->b_data;
+
+	fi->ir_fi_stat[slotnum].ir_total =
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total);
+	fi->ir_fi_stat[slotnum].ir_free =
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) -
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_used);
+bail:
+	if (unlock)
+		ocfs2_inode_unlock(inode_alloc, 0);
+
+bail_mutex_unlock:
+	mutex_unlock(&inode_alloc->i_mutex);
+
+	iput(inode_alloc);
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_freeinode(struct inode *inode,
+				struct ocfs2_info_request __user *user_req)
+{
+	int status = 0, i;
+
+	struct ocfs2_info_freeinode req_fi;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct inode *inode_alloc = NULL;
+
+
+	if (copy_from_user(&req_fi, user_req,
+			   sizeof(struct ocfs2_info_freeinode))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_fi.ir_slotnum = osb->max_slots;
+
+	for (i = 0; i < req_fi.ir_slotnum; i++) {
+		inode_alloc =
+			ocfs2_get_system_file_inode(osb,
+						    INODE_ALLOC_SYSTEM_INODE,
+						    i);
+		if (!inode_alloc) {
+			mlog(ML_ERROR, "unable to get alloc inode in slot %u\n",
+			     (u32)i);
+			status = -EIO;
+			goto bail;
+		}
+
+		status = ocfs2_info_scan_inode_alloc(inode_alloc, &req_fi, i,
+						req_fi.ir_request.ir_flags);
+		if (status < 0)
+			goto bail;
+	}
+
+	req_fi.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_freeinode __user *)user_req,
+			 &req_fi,
+			 sizeof(struct ocfs2_info_freeinode))) {
+		status = -EFAULT;
+	}
+
+bail:
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_unknown(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_request req;
+
+	if (copy_from_user(&req, user_req, sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req.ir_flags &= ~OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user(user_req, &req,
+			 sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_request(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_request req;
+
+	if (copy_from_user(&req, user_req, sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	if (req.ir_magic != OCFS2_INFO_MAGIC) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	switch (req.ir_code) {
+	case OCFS2_INFO_BLOCKSIZE:
+		if (req.ir_size != sizeof(struct ocfs2_info_blocksize)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_blocksize(inode, user_req);
+		break;
+	case OCFS2_INFO_CLUSTERSIZE:
+		if (req.ir_size != sizeof(struct ocfs2_info_clustersize)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_clustersize(inode, user_req);
+		break;
+	case OCFS2_INFO_SLOTNUM:
+		if (req.ir_size != sizeof(struct ocfs2_info_slotnum)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_slotnum(inode, user_req);
+		break;
+	case OCFS2_INFO_LABEL:
+		if (req.ir_size != sizeof(struct ocfs2_info_label)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_label(inode, user_req);
+		break;
+	case OCFS2_INFO_UUID:
+		if (req.ir_size != sizeof(struct ocfs2_info_uuid)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_uuid(inode, user_req);
+		break;
+	case OCFS2_INFO_FS_FEATURES:
+		if (req.ir_size != sizeof(struct ocfs2_info_fs_features)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_fs_features(inode, user_req);
+		break;
+	case OCFS2_INFO_FREEFRAG:
+		if (req.ir_size != sizeof(struct ocfs2_info_freefrag)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_freefrag(inode, user_req);
+		break;
+	case OCFS2_INFO_FREEINODE:
+		if (req.ir_size != sizeof(struct ocfs2_info_freeinode)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_freeinode(inode, user_req);
+		break;
+	default:
+		status = ocfs2_info_handle_unknown(inode, user_req);
+		break;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info,
+		      int compat_flag)
+{
+	int i, status = 0;
+	u64 req_addr;
+	struct ocfs2_info_request __user *reqp;
+
+	if ((info->info_count > OCFS2_INFO_MAX_REQUEST) ||
+	    (!info->info_requests)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	for (i = 0; i < info->info_count; i++) {
+		status = -EFAULT;
+		if (compat_flag) {
+			if (get_user(req_addr,
+			     (u64 __user *)compat_ptr(info->info_requests) + i))
+				goto bail;
+		} else {
+			if (get_user(req_addr,
+				     (u64 __user *)(info->info_requests) + i))
+				goto bail;
+		}
+
+		reqp = (struct ocfs2_info_request *)req_addr;
+		if (!reqp) {
+			status = -EINVAL;
+			goto bail;
+		}
+
+		status = ocfs2_info_handle_request(inode, reqp);
+		if (status)
+			goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
 long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	struct inode *inode = filp->f_path.dentry->d_inode;
@@ -120,6 +783,7 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 	struct reflink_arguments args;
 	const char *old_path, *new_path;
 	bool preserve;
+	struct ocfs2_info info;
 
 	switch (cmd) {
 	case OCFS2_IOC_GETFLAGS:
@@ -174,6 +838,12 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		preserve = (args.preserve != 0);
 
 		return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve);
+	case OCFS2_IOC_INFO:
+		if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
+				   sizeof(struct ocfs2_info)))
+			return -EFAULT;
+
+		return ocfs2_info_handle(inode, &info, 0);
 	default:
 		return -ENOTTY;
 	}
@@ -185,6 +855,7 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 	bool preserve;
 	struct reflink_arguments args;
 	struct inode *inode = file->f_path.dentry->d_inode;
+	struct ocfs2_info info;
 
 	switch (cmd) {
 	case OCFS2_IOC32_GETFLAGS:
@@ -209,6 +880,12 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 
 		return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path),
 					   compat_ptr(args.new_path), preserve);
+	case OCFS2_IOC_INFO:
+		if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
+				   sizeof(struct ocfs2_info)))
+			return -EFAULT;
+
+		return ocfs2_info_handle(inode, &info, 1);
 	default:
 		return -ENOIOCTLCMD;
 	}
diff --git a/fs/ocfs2/ocfs2_ioctl.h b/fs/ocfs2/ocfs2_ioctl.h
index 2d3420a..9cfe5be 100644
--- a/fs/ocfs2/ocfs2_ioctl.h
+++ b/fs/ocfs2/ocfs2_ioctl.h
@@ -76,4 +76,121 @@ struct reflink_arguments {
 };
 #define OCFS2_IOC_REFLINK	_IOW('o', 4, struct reflink_arguments)
 
+/* Following definitions dedicated for ocfs2_info_request ioctls. */
+
+#define OCFS2_INFO_VOL_UUID_LEN		(16)
+#define OCFS2_INFO_MAX_VOL_LABEL_LEN	(64)
+#define OCFS2_INFO_VOL_UUIDSTR_LEN	(OCFS2_INFO_VOL_UUID_LEN * 2 + 1)
+#define OCFS2_INFO_MAX_SLOTS		(255)
+#define OCFS2_INFO_MAX_HIST		(32)
+
+#define OCFS2_INFO_MAX_REQUEST		(50)
+
+/* Magic number of all requests */
+#define OCFS2_INFO_MAGIC		(0x4F32494E)
+
+/*
+ * Always try to separate info request into small pieces to
+ * guarantee the backward&forward compatibility.
+ */
+
+struct ocfs2_info {
+	__u64 info_requests;	/* Array of __u64 pointers to requests */
+	__u32 info_count;	/* Number of requests in info_requests */
+};
+
+struct ocfs2_info_request {
+/*00*/	__u32 ir_magic;	/* Magic number */
+	__u32 ir_code;	/* Info request code */
+	__u32 ir_size;	/* Size of request */
+	__u32 ir_flags;	/* Request flags */
+/*10*/	/* Request specific fields */
+};
+
+struct ocfs2_info_clustersize {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_clustersize;
+};
+
+struct ocfs2_info_blocksize {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_blocksize;
+};
+
+struct ocfs2_info_slotnum {
+	struct ocfs2_info_request ir_request;
+	__u16 ir_slotnum;
+};
+
+struct ocfs2_info_label {
+	struct ocfs2_info_request ir_request;
+	__u8	ir_label[OCFS2_INFO_MAX_VOL_LABEL_LEN];
+};
+
+struct ocfs2_info_uuid {
+	struct ocfs2_info_request ir_request;
+	__u8	ir_uuid_str[OCFS2_INFO_VOL_UUIDSTR_LEN];
+};
+
+struct ocfs2_info_fs_features {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_compat_features;
+	__u32 ir_incompat_features;
+	__u32 ir_ro_compat_features;
+};
+
+struct ocfs2_info_freefrag {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_chunksize; /* chunksize in clusters(in) */
+	struct ocfs2_info_freefrag_stats { /* (out) */
+		__u32 ir_clusters;
+		__u32 ir_free_clusters;
+		__u32 ir_free_chunks;
+		__u32 ir_free_chunks_real;
+		__u32 ir_min; /* Minimum free chunksize in clusters */
+		__u32 ir_max;
+		__u32 ir_avg;
+		struct ocfs2_info_free_chunk_list {
+			__u32 ir_fc_chunks[OCFS2_INFO_MAX_HIST];
+			__u32 ir_fc_clusters[OCFS2_INFO_MAX_HIST];
+		} ir_fc_hist;
+	} ir_ffg;
+};
+
+struct ocfs2_info_freeinode {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_slotnum; /* out */
+	struct ocfs2_info_local_fi {
+		__u64 ir_total;
+		__u64 ir_free;
+	} ir_fi_stat[OCFS2_INFO_MAX_SLOTS];
+};
+
+/* Codes for ocfs2_info_request */
+enum ocfs2_info_type {
+	OCFS2_INFO_CLUSTERSIZE = 1,
+	OCFS2_INFO_BLOCKSIZE,
+	OCFS2_INFO_SLOTNUM,
+	OCFS2_INFO_LABEL,
+	OCFS2_INFO_UUID,
+	OCFS2_INFO_FS_FEATURES,
+	OCFS2_INFO_FREEFRAG,
+	OCFS2_INFO_FREEINODE,
+	NUM_OCFS2_INFO_TYPE
+};
+
+/* Flags for struct ocfs2_info_request */
+/* Filled by the caller */
+#define OCFS2_INFO_FL_NON_COHERENT	(0x00000001)	/* Cluster coherency not
+							   required. This is a hint.
+							   It is up to ocfs2 whether
+							   the request can be fulfilled
+							   without locking. */
+/* Filled by ocfs2 */
+#define OCFS2_INFO_FL_FILLED		(0x80000000)	/* Filesystem understood
+							   this request and
+							   filled in the answer */
+
+#define OCFS2_IOC_INFO		_IOR('o', 5, struct ocfs2_info)
+
 #endif /* OCFS2_IOCTL_H */
-- 
1.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6.
  2010-02-24  7:51 [Ocfs2-devel] [PATCH 1/2] Ocfs2: Move ocfs2 ioctl definitions from ocfs2_fs.h to new added ocfs2_ioctl.h Tiger Yang
@ 2010-02-24  7:51 ` Tiger Yang
  0 siblings, 0 replies; 17+ messages in thread
From: Tiger Yang @ 2010-02-24  7:51 UTC (permalink / raw)
  To: ocfs2-devel

Now the ocfs2_info patches has been generated atop of joel's fixes branch.

Major changes from v5 to v6:

1. Fix a ptr passing bug on PPC machine, that is, 32bits binary running
   on 64bits kernel needs to use compat_ptr() to reterive the right ptr
   address from userspace if we use u64 as a pointer.

2. Use enum instead of flag bits for o2info type.

====================================================================
The reason why we need this ioctl is to offer the none-privileged
end-user a possibility to get filesys info gathering.

We use OCFS2_IOC_INFO to manipulate the new ioctl, userspace passes a
structure to kernel containing an array of request pointers and request
count, such as,

* From userspace:

struct ocfs2_info_blocksize brq = {
        .ir_request = {
                .ir_magic = OCFS2_INFO_MAGIC,
                .ir_code = OCFS2_INFO_BLOCKSIZE,
                ...
        }
        ...
}

struct ocfs2_info_clustersize crq = {
        ...
}

uint64_t reqs[2] = {(unsigned long)&brq,
                    (unsigned long)&crq};

struct ocfs2_info info = {
        .ir_requests = reqs,
        .ir_count = 2,
}

ret = ioctl(fd, OCFS2_IOC_INFO, &info);

* In kernel:

Get the request pointers from *info*, then handle each request one bye one.

Idea here is to make the spearated request small enough to guarantee
a better backward&forward compatibility since a small piece of request
would be less likely to be broken if filesys on raw disk get changed.

Currently, following 8 ioctls get implemented per the requirement from
userspace tool o2info, and I believe it will grow over time:-)

        OCFS2_INFO_CLUSTERSIZE
        OCFS2_INFO_BLOCKSIZE
        OCFS2_INFO_SLOTNUM
        OCFS2_INFO_LABEL
        OCFS2_INFO_UUID
        OCFS2_INFO_FS_FEATURES
        OCFS2_INFO_FREEFRAG
        OCFS2_INFO_FREEINODE

This ioctl is only specific to OCFS2.

Signed-off-by: Tiger Yang <tiger.yang@oracle.com>
---
 Makefile               |    2 +-
 fs/ocfs2/ioctl.c       |  677 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ocfs2/ocfs2_ioctl.h |  117 +++++++++
 3 files changed, 795 insertions(+), 1 deletions(-)

diff --git a/Makefile b/Makefile
index 1231094..e5d6b02 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 6
 SUBLEVEL = 33
-EXTRAVERSION = -rc3
+EXTRAVERSION = -o2info
 NAME = Man-Eating Seals of Antiquity
 
 # *DOCUMENTATION*
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index d88e5f0..1f5316b 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -24,8 +24,13 @@
 #include "ocfs2_ioctl.h"
 #include "resize.h"
 #include "refcounttree.h"
+#include "sysfile.h"
+#include "buffer_head_io.h"
+#include "suballoc.h"
+
 
 #include <linux/ext2_fs.h>
+#include <linux/compat.h>
 
 static int ocfs2_get_inode_attr(struct inode *inode, unsigned *flags)
 {
@@ -110,6 +115,664 @@ bail:
 	return status;
 }
 
+int ocfs2_info_handle_blocksize(struct inode *inode,
+				struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_blocksize req_bs;
+
+	if (copy_from_user(&req_bs, user_req,
+			   sizeof(struct ocfs2_info_blocksize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_bs.ir_blocksize = inode->i_sb->s_blocksize;
+	req_bs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_blocksize __user *)user_req,
+			 &req_bs,
+			 sizeof(struct ocfs2_info_blocksize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_clustersize(struct inode *inode,
+				  struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_clustersize req_cs;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_cs, user_req,
+			   sizeof(struct ocfs2_info_clustersize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_cs.ir_clustersize = osb->s_clustersize;
+	req_cs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_clustersize __user *)user_req,
+			 &req_cs,
+			 sizeof(struct ocfs2_info_clustersize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_slotnum(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_slotnum req_sn;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_sn, user_req,
+			   sizeof(struct ocfs2_info_slotnum))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_sn.ir_slotnum = osb->max_slots;
+	req_sn.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_slotnum __user *)user_req,
+			 &req_sn,
+			 sizeof(struct ocfs2_info_slotnum))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_label(struct inode *inode,
+			    struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_label req_lb;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_lb, user_req,
+			   sizeof(struct ocfs2_info_label))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	memcpy(req_lb.ir_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN);
+	req_lb.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_label __user *)user_req,
+			 &req_lb,
+			 sizeof(struct ocfs2_info_label))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_uuid(struct inode *inode,
+			   struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_uuid req_uuid;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_uuid, user_req,
+			   sizeof(struct ocfs2_info_uuid))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	memcpy(req_uuid.ir_uuid_str, osb->uuid_str, OCFS2_INFO_VOL_UUIDSTR_LEN);
+	req_uuid.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_uuid __user *)user_req,
+			 &req_uuid,
+			 sizeof(struct ocfs2_info_uuid))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_fs_features(struct inode *inode,
+				  struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_fs_features req_fs;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_fs, user_req,
+			   sizeof(struct ocfs2_info_fs_features))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_fs.ir_compat_features = osb->s_feature_compat;
+	req_fs.ir_incompat_features = osb->s_feature_incompat;
+	req_fs.ir_ro_compat_features = osb->s_feature_ro_compat;
+	req_fs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_fs_features __user *)user_req,
+			 &req_fs,
+			 sizeof(struct ocfs2_info_fs_features))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg,
+			   unsigned int chunksize)
+{
+	int index;
+
+	index = __ilog2_u32(chunksize);
+	if (index >= OCFS2_INFO_MAX_HIST)
+		index = OCFS2_INFO_MAX_HIST - 1;
+
+	ffg->ir_ffg.ir_fc_hist.ir_fc_chunks[index]++;
+	ffg->ir_ffg.ir_fc_hist.ir_fc_clusters[index] += chunksize;
+
+	if (chunksize > ffg->ir_ffg.ir_max)
+		ffg->ir_ffg.ir_max = chunksize;
+
+	if (chunksize < ffg->ir_ffg.ir_min)
+		ffg->ir_ffg.ir_min = chunksize;
+
+	ffg->ir_ffg.ir_avg += chunksize;
+	ffg->ir_ffg.ir_free_chunks_real++;
+}
+
+int ocfs2_info_scan_chain(struct inode *gb_inode,
+			  struct ocfs2_dinode *gb_dinode,
+			  struct ocfs2_info_freefrag *ffg,
+			  struct ocfs2_chain_rec *rec,
+			  unsigned int chunks_in_group)
+{
+	int status = 0, used;
+	u64 blkno;
+
+	struct buffer_head *bh = 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 (!le32_to_cpu(rec->c_free))
+		goto bail;
+
+	do {
+		if (!bg)
+			blkno = le64_to_cpu(rec->c_blkno);
+		else
+			blkno = le64_to_cpu(bg->bg_next_group);
+
+		if (bh) {
+			brelse(bh);
+			bh = NULL;
+		}
+
+		status = ocfs2_read_group_descriptor(gb_inode, gb_dinode,
+						     blkno, &bh);
+		if (status < 0) {
+			mlog(ML_ERROR, "Can't read the group descriptor # "
+			     "%llu from device.", (unsigned long long)blkno);
+			status = -EIO;
+			goto bail;
+		}
+
+		bg = (struct ocfs2_group_desc *)bh->b_data;
+
+		if (!le16_to_cpu(bg->bg_free_bits_count))
+			continue;
+
+		max_bits = le16_to_cpu(bg->bg_bits);
+		offset = 0;
+
+		for (chunk = 0; chunk < chunks_in_group; chunk++) {
+
+			/* Last chunk may be not a entire one */
+			if ((offset + ffg->ir_chunksize) > max_bits)
+				num_clusters = max_bits - offset;
+			else
+				num_clusters = ffg->ir_chunksize;
+
+			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)) {
+					ocfs2_info_update_ffg(ffg,
+							      last_chunksize);
+					last_chunksize = 0;
+				}
+
+				offset++;
+			}
+
+			if (chunk_free == ffg->ir_chunksize)
+				ffg->ir_ffg.ir_free_chunks++;
+		}
+
+		/* we need to update the info of last free chunk */
+		if (last_chunksize)
+			ocfs2_info_update_ffg(ffg, last_chunksize);
+
+	} while (le64_to_cpu(bg->bg_next_group));
+
+bail:
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_scan_bitmap(struct inode *gb_inode,
+			   struct ocfs2_dinode *gb_dinode,
+			   struct ocfs2_info_freefrag *ffg,
+			   struct ocfs2_chain_list *cl)
+{
+	int status = 0, i;
+	unsigned int chunks_in_group;
+	struct ocfs2_chain_rec *rec = NULL;
+
+	chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->ir_chunksize + 1;
+
+	for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) {
+
+		rec = &(cl->cl_recs[i]);
+		status = ocfs2_info_scan_chain(gb_inode, gb_dinode,
+					       ffg, rec, chunks_in_group);
+		if (status)
+			goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_freefrag(struct inode *inode,
+			       struct ocfs2_info_request __user *user_req)
+{
+	int status = 0, unlock = 0;
+
+	struct ocfs2_info_freefrag req_ffg;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct buffer_head *bh = NULL;
+	struct inode *gb_inode = NULL;
+	struct ocfs2_dinode *gb_dinode = NULL;
+	struct ocfs2_chain_list *cl = NULL;
+
+	if (copy_from_user(&req_ffg, user_req,
+			   sizeof(struct ocfs2_info_freefrag))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	/*
+	 * chunksize from userspace should be power of 2,
+	 */
+	if ((req_ffg.ir_chunksize & (req_ffg.ir_chunksize - 1)) ||
+	    (!req_ffg.ir_chunksize)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	memset(&req_ffg.ir_ffg, 0, sizeof(struct ocfs2_info_freefrag_stats));
+	req_ffg.ir_ffg.ir_min = ~0U;
+
+	gb_inode = ocfs2_get_system_file_inode(osb,
+					       GLOBAL_BITMAP_SYSTEM_INODE,
+					       OCFS2_INVALID_SLOT);
+	if (!gb_inode) {
+		mlog(ML_ERROR, "failed to get bitmap inode\n");
+		status = -EIO;
+		goto bail;
+	}
+
+	mutex_lock(&gb_inode->i_mutex);
+
+	if (!(req_ffg.ir_request.ir_flags & OCFS2_INFO_FL_NON_COHERENT)) {
+		status = ocfs2_inode_lock(gb_inode, &bh, 0);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail_mutex_unlock;
+		}
+		unlock = 1;
+
+	} else {
+		status = ocfs2_read_inode_block(gb_inode, &bh);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail;
+		}
+	}
+
+	gb_dinode = (struct ocfs2_dinode *)bh->b_data;
+
+	req_ffg.ir_ffg.ir_clusters =
+			le32_to_cpu(gb_dinode->id1.bitmap1.i_total);
+	req_ffg.ir_ffg.ir_free_clusters = req_ffg.ir_ffg.ir_clusters -
+				le32_to_cpu(gb_dinode->id1.bitmap1.i_used);
+
+	cl = &(gb_dinode->id2.i_chain);
+
+	/* Chunksize from userspace should be less than clusters in a group */
+	if (req_ffg.ir_chunksize > le16_to_cpu(cl->cl_cpg)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	status = ocfs2_info_scan_bitmap(gb_inode, gb_dinode, &req_ffg, cl);
+	if (status)
+		goto bail;
+
+	if (req_ffg.ir_ffg.ir_free_chunks_real)
+		req_ffg.ir_ffg.ir_avg = (req_ffg.ir_ffg.ir_avg /
+					req_ffg.ir_ffg.ir_free_chunks_real);
+
+	req_ffg.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_freefrag __user *)user_req,
+			 &req_ffg,
+			 sizeof(struct ocfs2_info_freefrag))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	if (unlock)
+		ocfs2_inode_unlock(gb_inode, 0);
+
+bail_mutex_unlock:
+	if (gb_inode)
+		mutex_unlock(&gb_inode->i_mutex);
+
+	iput(gb_inode);
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_scan_inode_alloc(struct inode *inode_alloc,
+				struct ocfs2_info_freeinode *fi,
+				__u32 slotnum,
+				int flags)
+{
+	int status = 0, unlock = 0;
+
+	struct buffer_head *bh = NULL;
+	struct ocfs2_dinode *dinode_alloc = NULL;
+
+	mutex_lock(&inode_alloc->i_mutex);
+
+	if (!(flags & OCFS2_INFO_FL_NON_COHERENT)) {
+		status = ocfs2_inode_lock(inode_alloc, &bh, 0);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail_mutex_unlock;
+		}
+		unlock = 1;
+
+	} else {
+
+		status = ocfs2_read_inode_block(inode_alloc, &bh);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail;
+		}
+	}
+
+	dinode_alloc = (struct ocfs2_dinode *)bh->b_data;
+
+	fi->ir_fi_stat[slotnum].ir_total =
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total);
+	fi->ir_fi_stat[slotnum].ir_free =
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) -
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_used);
+bail:
+	if (unlock)
+		ocfs2_inode_unlock(inode_alloc, 0);
+
+bail_mutex_unlock:
+	mutex_unlock(&inode_alloc->i_mutex);
+
+	iput(inode_alloc);
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_freeinode(struct inode *inode,
+				struct ocfs2_info_request __user *user_req)
+{
+	int status = 0, i;
+
+	struct ocfs2_info_freeinode req_fi;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct inode *inode_alloc = NULL;
+
+
+	if (copy_from_user(&req_fi, user_req,
+			   sizeof(struct ocfs2_info_freeinode))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_fi.ir_slotnum = osb->max_slots;
+
+	for (i = 0; i < req_fi.ir_slotnum; i++) {
+		inode_alloc =
+			ocfs2_get_system_file_inode(osb,
+						    INODE_ALLOC_SYSTEM_INODE,
+						    i);
+		if (!inode_alloc) {
+			mlog(ML_ERROR, "unable to get alloc inode in slot %u\n",
+			     (u32)i);
+			status = -EIO;
+			goto bail;
+		}
+
+		status = ocfs2_info_scan_inode_alloc(inode_alloc, &req_fi, i,
+						req_fi.ir_request.ir_flags);
+		if (status < 0)
+			goto bail;
+	}
+
+	req_fi.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_freeinode __user *)user_req,
+			 &req_fi,
+			 sizeof(struct ocfs2_info_freeinode))) {
+		status = -EFAULT;
+	}
+
+bail:
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_unknown(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_request req;
+
+	if (copy_from_user(&req, user_req, sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req.ir_flags &= ~OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user(user_req, &req,
+			 sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_request(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_request req;
+
+	if (copy_from_user(&req, user_req, sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	if (req.ir_magic != OCFS2_INFO_MAGIC) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	switch (req.ir_code) {
+	case OCFS2_INFO_BLOCKSIZE:
+		if (req.ir_size != sizeof(struct ocfs2_info_blocksize)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_blocksize(inode, user_req);
+		break;
+	case OCFS2_INFO_CLUSTERSIZE:
+		if (req.ir_size != sizeof(struct ocfs2_info_clustersize)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_clustersize(inode, user_req);
+		break;
+	case OCFS2_INFO_SLOTNUM:
+		if (req.ir_size != sizeof(struct ocfs2_info_slotnum)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_slotnum(inode, user_req);
+		break;
+	case OCFS2_INFO_LABEL:
+		if (req.ir_size != sizeof(struct ocfs2_info_label)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_label(inode, user_req);
+		break;
+	case OCFS2_INFO_UUID:
+		if (req.ir_size != sizeof(struct ocfs2_info_uuid)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_uuid(inode, user_req);
+		break;
+	case OCFS2_INFO_FS_FEATURES:
+		if (req.ir_size != sizeof(struct ocfs2_info_fs_features)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_fs_features(inode, user_req);
+		break;
+	case OCFS2_INFO_FREEFRAG:
+		if (req.ir_size != sizeof(struct ocfs2_info_freefrag)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_freefrag(inode, user_req);
+		break;
+	case OCFS2_INFO_FREEINODE:
+		if (req.ir_size != sizeof(struct ocfs2_info_freeinode)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_freeinode(inode, user_req);
+		break;
+	default:
+		status = ocfs2_info_handle_unknown(inode, user_req);
+		break;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info,
+		      int compat_flag)
+{
+	int i, status = 0;
+	u64 req_addr;
+	struct ocfs2_info_request __user *reqp;
+
+	if ((info->info_count > OCFS2_INFO_MAX_REQUEST) ||
+	    (!info->info_requests)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	for (i = 0; i < info->info_count; i++) {
+		status = -EFAULT;
+		if (compat_flag) {
+			if (get_user(req_addr,
+			     (u64 __user *)compat_ptr(info->info_requests) + i))
+				goto bail;
+		} else {
+			if (get_user(req_addr,
+				     (u64 __user *)(info->info_requests) + i))
+				goto bail;
+		}
+
+		reqp = (struct ocfs2_info_request *)req_addr;
+		if (!reqp) {
+			status = -EINVAL;
+			goto bail;
+		}
+
+		status = ocfs2_info_handle_request(inode, reqp);
+		if (status)
+			goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
 long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	struct inode *inode = filp->f_path.dentry->d_inode;
@@ -121,6 +784,7 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 	struct reflink_arguments args;
 	const char *old_path, *new_path;
 	bool preserve;
+	struct ocfs2_info info;
 
 	switch (cmd) {
 	case OCFS2_IOC_GETFLAGS:
@@ -175,6 +839,12 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		preserve = (args.preserve != 0);
 
 		return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve);
+	case OCFS2_IOC_INFO:
+		if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
+				   sizeof(struct ocfs2_info)))
+			return -EFAULT;
+
+		return ocfs2_info_handle(inode, &info, 0);
 	default:
 		return -ENOTTY;
 	}
@@ -186,6 +856,7 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 	bool preserve;
 	struct reflink_arguments args;
 	struct inode *inode = file->f_path.dentry->d_inode;
+	struct ocfs2_info info;
 
 	switch (cmd) {
 	case OCFS2_IOC32_GETFLAGS:
@@ -210,6 +881,12 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 
 		return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path),
 					   compat_ptr(args.new_path), preserve);
+	case OCFS2_IOC_INFO:
+		if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
+				   sizeof(struct ocfs2_info)))
+			return -EFAULT;
+
+		return ocfs2_info_handle(inode, &info, 1);
 	default:
 		return -ENOIOCTLCMD;
 	}
diff --git a/fs/ocfs2/ocfs2_ioctl.h b/fs/ocfs2/ocfs2_ioctl.h
index 2d3420a..9cfe5be 100644
--- a/fs/ocfs2/ocfs2_ioctl.h
+++ b/fs/ocfs2/ocfs2_ioctl.h
@@ -76,4 +76,121 @@ struct reflink_arguments {
 };
 #define OCFS2_IOC_REFLINK	_IOW('o', 4, struct reflink_arguments)
 
+/* Following definitions dedicated for ocfs2_info_request ioctls. */
+
+#define OCFS2_INFO_VOL_UUID_LEN		(16)
+#define OCFS2_INFO_MAX_VOL_LABEL_LEN	(64)
+#define OCFS2_INFO_VOL_UUIDSTR_LEN	(OCFS2_INFO_VOL_UUID_LEN * 2 + 1)
+#define OCFS2_INFO_MAX_SLOTS		(255)
+#define OCFS2_INFO_MAX_HIST		(32)
+
+#define OCFS2_INFO_MAX_REQUEST		(50)
+
+/* Magic number of all requests */
+#define OCFS2_INFO_MAGIC		(0x4F32494E)
+
+/*
+ * Always try to separate info request into small pieces to
+ * guarantee the backward&forward compatibility.
+ */
+
+struct ocfs2_info {
+	__u64 info_requests;	/* Array of __u64 pointers to requests */
+	__u32 info_count;	/* Number of requests in info_requests */
+};
+
+struct ocfs2_info_request {
+/*00*/	__u32 ir_magic;	/* Magic number */
+	__u32 ir_code;	/* Info request code */
+	__u32 ir_size;	/* Size of request */
+	__u32 ir_flags;	/* Request flags */
+/*10*/	/* Request specific fields */
+};
+
+struct ocfs2_info_clustersize {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_clustersize;
+};
+
+struct ocfs2_info_blocksize {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_blocksize;
+};
+
+struct ocfs2_info_slotnum {
+	struct ocfs2_info_request ir_request;
+	__u16 ir_slotnum;
+};
+
+struct ocfs2_info_label {
+	struct ocfs2_info_request ir_request;
+	__u8	ir_label[OCFS2_INFO_MAX_VOL_LABEL_LEN];
+};
+
+struct ocfs2_info_uuid {
+	struct ocfs2_info_request ir_request;
+	__u8	ir_uuid_str[OCFS2_INFO_VOL_UUIDSTR_LEN];
+};
+
+struct ocfs2_info_fs_features {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_compat_features;
+	__u32 ir_incompat_features;
+	__u32 ir_ro_compat_features;
+};
+
+struct ocfs2_info_freefrag {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_chunksize; /* chunksize in clusters(in) */
+	struct ocfs2_info_freefrag_stats { /* (out) */
+		__u32 ir_clusters;
+		__u32 ir_free_clusters;
+		__u32 ir_free_chunks;
+		__u32 ir_free_chunks_real;
+		__u32 ir_min; /* Minimum free chunksize in clusters */
+		__u32 ir_max;
+		__u32 ir_avg;
+		struct ocfs2_info_free_chunk_list {
+			__u32 ir_fc_chunks[OCFS2_INFO_MAX_HIST];
+			__u32 ir_fc_clusters[OCFS2_INFO_MAX_HIST];
+		} ir_fc_hist;
+	} ir_ffg;
+};
+
+struct ocfs2_info_freeinode {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_slotnum; /* out */
+	struct ocfs2_info_local_fi {
+		__u64 ir_total;
+		__u64 ir_free;
+	} ir_fi_stat[OCFS2_INFO_MAX_SLOTS];
+};
+
+/* Codes for ocfs2_info_request */
+enum ocfs2_info_type {
+	OCFS2_INFO_CLUSTERSIZE = 1,
+	OCFS2_INFO_BLOCKSIZE,
+	OCFS2_INFO_SLOTNUM,
+	OCFS2_INFO_LABEL,
+	OCFS2_INFO_UUID,
+	OCFS2_INFO_FS_FEATURES,
+	OCFS2_INFO_FREEFRAG,
+	OCFS2_INFO_FREEINODE,
+	NUM_OCFS2_INFO_TYPE
+};
+
+/* Flags for struct ocfs2_info_request */
+/* Filled by the caller */
+#define OCFS2_INFO_FL_NON_COHERENT	(0x00000001)	/* Cluster coherency not
+							   required. This is a hint.
+							   It is up to ocfs2 whether
+							   the request can be fulfilled
+							   without locking. */
+/* Filled by ocfs2 */
+#define OCFS2_INFO_FL_FILLED		(0x80000000)	/* Filesystem understood
+							   this request and
+							   filled in the answer */
+
+#define OCFS2_IOC_INFO		_IOR('o', 5, struct ocfs2_info)
+
 #endif /* OCFS2_IOCTL_H */
-- 
1.5.4.4

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6.
  2010-02-08  9:21 [Ocfs2-devel] [PATCH 1/2] Ocfs2: Move ocfs2 ioctl definitions from ocfs2_fs.h to new added ocfs2_ioctl.h Tristan Ye
@ 2010-02-08  9:21 ` Tristan Ye
  0 siblings, 0 replies; 17+ messages in thread
From: Tristan Ye @ 2010-02-08  9:21 UTC (permalink / raw)
  To: ocfs2-devel

Now the ocfs2_info patches has been generated atop of joel's fixes branch.

Major changes from v5 to v6:

Fix a ptr passing bug on PPC machine, that is, 32bits binary running
on 64bits kernel needs to use compat_ptr() to reterive the right ptr
address from userspace if we use u64 as a pointer.

The reason why we need this ioctl is to offer the none-privileged
end-user a possibility to get filesys info gathering.

We use OCFS2_IOC_INFO to manipulate the new ioctl, userspace passes a
structure to kernel containing an array of request pointers and request
count, such as,

* From userspace:

struct ocfs2_info_blocksize brq = {
        .ir_request = {
                .ir_magic = OCFS2_INFO_MAGIC,
                .ir_code = OCFS2_INFO_BLOCKSIZE,
                ...
        }
        ...
}

struct ocfs2_info_clustersize crq = {
        ...
}

uint64_t reqs[2] = {(unsigned long)&brq,
                    (unsigned long)&crq};

struct ocfs2_info info = {
        .ir_requests = reqs,
        .ir_count = 2,
}

ret = ioctl(fd, OCFS2_IOC_INFO, &info);

* In kernel:

Get the request pointers from *info*, then handle each request one bye one.

Idea here is to make the spearated request small enough to guarantee
a better backward&forward compatibility since a small piece of request
would be less likely to be broken if filesys on raw disk get changed.

Currently, following 8 ioctls get implemented per the requirement from
userspace tool o2info, and I believe it will grow over time:-)

        OCFS2_INFO_CLUSTERSIZE
        OCFS2_INFO_BLOCKSIZE
        OCFS2_INFO_SLOTNUM
        OCFS2_INFO_LABEL
        OCFS2_INFO_UUID
        OCFS2_INFO_FS_FEATURES
        OCFS2_INFO_FREEFRAG
        OCFS2_INFO_FREEINODE

This ioctl is only specific to OCFS2.

Signed-off-by: Tristan Ye <tristan.ye@oracle.com>
---
 fs/ocfs2/ioctl.c       |  678 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ocfs2/ocfs2_ioctl.h |  115 ++++++++
 2 files changed, 793 insertions(+), 0 deletions(-)

diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index d88e5f0..fa5813d 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -24,8 +24,13 @@
 #include "ocfs2_ioctl.h"
 #include "resize.h"
 #include "refcounttree.h"
+#include "sysfile.h"
+#include "buffer_head_io.h"
+#include "suballoc.h"
+
 
 #include <linux/ext2_fs.h>
+#include <linux/compat.h>
 
 static int ocfs2_get_inode_attr(struct inode *inode, unsigned *flags)
 {
@@ -110,6 +115,664 @@ bail:
 	return status;
 }
 
+int ocfs2_info_handle_blocksize(struct inode *inode,
+				struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_blocksize req_bs;
+
+	if (copy_from_user(&req_bs, user_req,
+			   sizeof(struct ocfs2_info_blocksize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_bs.ir_blocksize = inode->i_sb->s_blocksize;
+	req_bs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_blocksize __user *)user_req,
+			 &req_bs,
+			 sizeof(struct ocfs2_info_blocksize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_clustersize(struct inode *inode,
+				  struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_clustersize req_cs;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_cs, user_req,
+			   sizeof(struct ocfs2_info_clustersize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_cs.ir_clustersize = osb->s_clustersize;
+	req_cs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_clustersize __user *)user_req,
+			 &req_cs,
+			 sizeof(struct ocfs2_info_clustersize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_slotnum(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_slotnum req_sn;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_sn, user_req,
+			   sizeof(struct ocfs2_info_slotnum))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_sn.ir_slotnum = osb->max_slots;
+	req_sn.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_slotnum __user *)user_req,
+			 &req_sn,
+			 sizeof(struct ocfs2_info_slotnum))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_label(struct inode *inode,
+			    struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_label req_lb;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_lb, user_req,
+			   sizeof(struct ocfs2_info_label))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	memcpy(req_lb.ir_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN);
+	req_lb.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_label __user *)user_req,
+			 &req_lb,
+			 sizeof(struct ocfs2_info_label))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_uuid(struct inode *inode,
+			   struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_uuid req_uuid;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_uuid, user_req,
+			   sizeof(struct ocfs2_info_uuid))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	memcpy(req_uuid.ir_uuid_str, osb->uuid_str, OCFS2_INFO_VOL_UUIDSTR_LEN);
+	req_uuid.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_uuid __user *)user_req,
+			 &req_uuid,
+			 sizeof(struct ocfs2_info_uuid))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_fs_features(struct inode *inode,
+				  struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_fs_features req_fs;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_fs, user_req,
+			   sizeof(struct ocfs2_info_fs_features))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_fs.ir_compat_features = osb->s_feature_compat;
+	req_fs.ir_incompat_features = osb->s_feature_incompat;
+	req_fs.ir_ro_compat_features = osb->s_feature_ro_compat;
+	req_fs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_fs_features __user *)user_req,
+			 &req_fs,
+			 sizeof(struct ocfs2_info_fs_features))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg,
+			   unsigned int chunksize)
+{
+	int index;
+
+	index = __ilog2_u32(chunksize);
+	if (index >= OCFS2_INFO_MAX_HIST)
+		index = OCFS2_INFO_MAX_HIST - 1;
+
+	ffg->ir_ffg.ir_fc_hist.ir_fc_chunks[index]++;
+	ffg->ir_ffg.ir_fc_hist.ir_fc_clusters[index] += chunksize;
+
+	if (chunksize > ffg->ir_ffg.ir_max)
+		ffg->ir_ffg.ir_max = chunksize;
+
+	if (chunksize < ffg->ir_ffg.ir_min)
+		ffg->ir_ffg.ir_min = chunksize;
+
+	ffg->ir_ffg.ir_avg += chunksize;
+	ffg->ir_ffg.ir_free_chunks_real++;
+}
+
+int ocfs2_info_scan_chain(struct inode *gb_inode,
+			  struct ocfs2_dinode *gb_dinode,
+			  struct ocfs2_info_freefrag *ffg,
+			  struct ocfs2_chain_rec *rec,
+			  unsigned int chunks_in_group)
+{
+	int status = 0, used;
+	u64 blkno;
+
+	struct buffer_head *bh = 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 (!le32_to_cpu(rec->c_free))
+		goto bail;
+
+	do {
+		if (!bg)
+			blkno = le64_to_cpu(rec->c_blkno);
+		else
+			blkno = le64_to_cpu(bg->bg_next_group);
+
+		if (bh) {
+			brelse(bh);
+			bh = NULL;
+		}
+
+		status = ocfs2_read_group_descriptor(gb_inode, gb_dinode,
+						     blkno, &bh);
+		if (status < 0) {
+			mlog(ML_ERROR, "Can't read the group descriptor # "
+			     "%llu from device.", (unsigned long long)blkno);
+			status = -EIO;
+			goto bail;
+		}
+
+		bg = (struct ocfs2_group_desc *)bh->b_data;
+
+		if (!le16_to_cpu(bg->bg_free_bits_count))
+			continue;
+
+		max_bits = le16_to_cpu(bg->bg_bits);
+		offset = 0;
+
+		for (chunk = 0; chunk < chunks_in_group; chunk++) {
+
+			/* Last chunk may be not a entire one */
+			if ((offset + ffg->ir_chunksize) > max_bits)
+				num_clusters = max_bits - offset;
+			else
+				num_clusters = ffg->ir_chunksize;
+
+			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)) {
+					ocfs2_info_update_ffg(ffg,
+							      last_chunksize);
+					last_chunksize = 0;
+				}
+
+				offset++;
+			}
+
+			if (chunk_free == ffg->ir_chunksize)
+				ffg->ir_ffg.ir_free_chunks++;
+		}
+
+		/* we need to update the info of last free chunk */
+		if (last_chunksize)
+			ocfs2_info_update_ffg(ffg, last_chunksize);
+
+	} while (le64_to_cpu(bg->bg_next_group));
+
+bail:
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_scan_bitmap(struct inode *gb_inode,
+			   struct ocfs2_dinode *gb_dinode,
+			   struct ocfs2_info_freefrag *ffg,
+			   struct ocfs2_chain_list *cl)
+{
+	int status = 0, i;
+	unsigned int chunks_in_group;
+	struct ocfs2_chain_rec *rec = NULL;
+
+	chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->ir_chunksize + 1;
+
+	for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) {
+
+		rec = &(cl->cl_recs[i]);
+		status = ocfs2_info_scan_chain(gb_inode, gb_dinode,
+					       ffg, rec, chunks_in_group);
+		if (status)
+			goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_freefrag(struct inode *inode,
+			       struct ocfs2_info_request __user *user_req)
+{
+	int status = 0, unlock = 0;
+
+	struct ocfs2_info_freefrag req_ffg;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct buffer_head *bh = NULL;
+	struct inode *gb_inode = NULL;
+	struct ocfs2_dinode *gb_dinode = NULL;
+	struct ocfs2_chain_list *cl = NULL;
+
+	if (copy_from_user(&req_ffg, user_req,
+			   sizeof(struct ocfs2_info_freefrag))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	/*
+	 * chunksize from userspace should be power of 2,
+	 */
+	if ((req_ffg.ir_chunksize & (req_ffg.ir_chunksize - 1)) ||
+	    (!req_ffg.ir_chunksize)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	memset(&req_ffg.ir_ffg, 0, sizeof(struct ocfs2_info_freefrag_stats));
+	req_ffg.ir_ffg.ir_min = ~0U;
+
+	gb_inode = ocfs2_get_system_file_inode(osb,
+					       GLOBAL_BITMAP_SYSTEM_INODE,
+					       OCFS2_INVALID_SLOT);
+	if (!gb_inode) {
+		mlog(ML_ERROR, "failed to get bitmap inode\n");
+		status = -EIO;
+		goto bail;
+	}
+
+	mutex_lock(&gb_inode->i_mutex);
+
+	if (!(req_ffg.ir_request.ir_flags & OCFS2_INFO_FL_NON_COHERENT)) {
+		status = ocfs2_inode_lock(gb_inode, &bh, 0);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail_mutex_unlock;
+		}
+		unlock = 1;
+
+	} else {
+		status = ocfs2_read_inode_block(gb_inode, &bh);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail;
+		}
+	}
+
+	gb_dinode = (struct ocfs2_dinode *)bh->b_data;
+
+	req_ffg.ir_ffg.ir_clusters =
+			le32_to_cpu(gb_dinode->id1.bitmap1.i_total);
+	req_ffg.ir_ffg.ir_free_clusters = req_ffg.ir_ffg.ir_clusters -
+				le32_to_cpu(gb_dinode->id1.bitmap1.i_used);
+
+	cl = &(gb_dinode->id2.i_chain);
+
+	/* Chunksize from userspace should be less than clusters in a group */
+	if (req_ffg.ir_chunksize > le16_to_cpu(cl->cl_cpg)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	status = ocfs2_info_scan_bitmap(gb_inode, gb_dinode, &req_ffg, cl);
+	if (status)
+		goto bail;
+
+	if (req_ffg.ir_ffg.ir_free_chunks_real)
+		req_ffg.ir_ffg.ir_avg = (req_ffg.ir_ffg.ir_avg /
+					req_ffg.ir_ffg.ir_free_chunks_real);
+
+	req_ffg.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_freefrag __user *)user_req,
+			 &req_ffg,
+			 sizeof(struct ocfs2_info_freefrag))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	if (unlock)
+		ocfs2_inode_unlock(gb_inode, 0);
+
+bail_mutex_unlock:
+	if (gb_inode)
+		mutex_unlock(&gb_inode->i_mutex);
+
+	iput(gb_inode);
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_scan_inode_alloc(struct inode *inode_alloc,
+				struct ocfs2_info_freeinode *fi,
+				__u32 slotnum,
+				int flags)
+{
+	int status = 0, unlock = 0;
+
+	struct buffer_head *bh = NULL;
+	struct ocfs2_dinode *dinode_alloc = NULL;
+
+	mutex_lock(&inode_alloc->i_mutex);
+
+	if (!(flags & OCFS2_INFO_FL_NON_COHERENT)) {
+		status = ocfs2_inode_lock(inode_alloc, &bh, 0);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail_mutex_unlock;
+		}
+		unlock = 1;
+
+	} else {
+
+		status = ocfs2_read_inode_block(inode_alloc, &bh);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail;
+		}
+	}
+
+	dinode_alloc = (struct ocfs2_dinode *)bh->b_data;
+
+	fi->ir_fi_stat[slotnum].ir_total =
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total);
+	fi->ir_fi_stat[slotnum].ir_free =
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) -
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_used);
+bail:
+	if (unlock)
+		ocfs2_inode_unlock(inode_alloc, 0);
+
+bail_mutex_unlock:
+	mutex_unlock(&inode_alloc->i_mutex);
+
+	iput(inode_alloc);
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_freeinode(struct inode *inode,
+				struct ocfs2_info_request __user *user_req)
+{
+	int status = 0, i;
+
+	struct ocfs2_info_freeinode req_fi;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct inode *inode_alloc = NULL;
+
+
+	if (copy_from_user(&req_fi, user_req,
+			   sizeof(struct ocfs2_info_freeinode))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_fi.ir_slotnum = osb->max_slots;
+
+	for (i = 0; i < req_fi.ir_slotnum; i++) {
+		inode_alloc =
+			ocfs2_get_system_file_inode(osb,
+						    INODE_ALLOC_SYSTEM_INODE,
+						    i);
+		if (!inode_alloc) {
+			mlog(ML_ERROR, "unable to get alloc inode in slot %u\n",
+			     (u32)i);
+			status = -EIO;
+			goto bail;
+		}
+
+		status = ocfs2_info_scan_inode_alloc(inode_alloc, &req_fi, i,
+						req_fi.ir_request.ir_flags);
+		if (status < 0)
+			goto bail;
+	}
+
+	req_fi.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_freeinode __user *)user_req,
+			 &req_fi,
+			 sizeof(struct ocfs2_info_freeinode))) {
+		status = -EFAULT;
+	}
+
+bail:
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_unknown(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_request req;
+
+	if (copy_from_user(&req, user_req, sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req.ir_flags &= ~OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user(user_req, &req,
+			 sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_request(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_request req;
+
+	if (copy_from_user(&req, user_req, sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	if (req.ir_magic != OCFS2_INFO_MAGIC) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	switch (req.ir_code) {
+	case OCFS2_INFO_BLOCKSIZE:
+		if (req.ir_size != sizeof(struct ocfs2_info_blocksize)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_blocksize(inode, user_req);
+		break;
+	case OCFS2_INFO_CLUSTERSIZE:
+		if (req.ir_size != sizeof(struct ocfs2_info_clustersize)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_clustersize(inode, user_req);
+		break;
+	case OCFS2_INFO_SLOTNUM:
+		if (req.ir_size != sizeof(struct ocfs2_info_slotnum)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_slotnum(inode, user_req);
+		break;
+	case OCFS2_INFO_LABEL:
+		if (req.ir_size != sizeof(struct ocfs2_info_label)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_label(inode, user_req);
+		break;
+	case OCFS2_INFO_UUID:
+		if (req.ir_size != sizeof(struct ocfs2_info_uuid)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_uuid(inode, user_req);
+		break;
+	case OCFS2_INFO_FS_FEATURES:
+		if (req.ir_size != sizeof(struct ocfs2_info_fs_features)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_fs_features(inode, user_req);
+		break;
+	case OCFS2_INFO_FREEFRAG:
+		if (req.ir_size != sizeof(struct ocfs2_info_freefrag)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_freefrag(inode, user_req);
+		break;
+	case OCFS2_INFO_FREEINODE:
+		if (req.ir_size != sizeof(struct ocfs2_info_freeinode)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_freeinode(inode, user_req);
+		break;
+	default:
+		status = ocfs2_info_handle_unknown(inode, user_req);
+		break;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info,
+		      int compat_flag)
+{
+	int i, status = 0;
+	u64 req_addr;
+	struct ocfs2_info_request __user *reqp;
+
+	if ((info->info_count > OCFS2_INFO_MAX_REQUEST) ||
+	    (!info->info_requests)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	for (i = 0; i < info->info_count; i++) {
+		status = -EFAULT;
+		if (compat_flag) {
+			if (get_user(req_addr,
+			     (u64 __user *)compat_ptr(info->info_requests) + i))
+				goto bail;
+		} else {
+			if (get_user(req_addr,
+				     (u64 __user *)(info->info_requests) + i))
+				goto bail;
+		}
+
+		reqp = (struct ocfs2_info_request *)req_addr;
+		if (!reqp) {
+			status = -EINVAL;
+			goto bail;
+		}
+
+		status = ocfs2_info_handle_request(inode, reqp);
+		if (status)
+			goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
 long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	struct inode *inode = filp->f_path.dentry->d_inode;
@@ -121,6 +784,7 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 	struct reflink_arguments args;
 	const char *old_path, *new_path;
 	bool preserve;
+	struct ocfs2_info info;
 
 	switch (cmd) {
 	case OCFS2_IOC_GETFLAGS:
@@ -175,6 +839,12 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		preserve = (args.preserve != 0);
 
 		return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve);
+	case OCFS2_IOC_INFO:
+		if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
+				   sizeof(struct ocfs2_info)))
+			return -EFAULT;
+
+		return ocfs2_info_handle(inode, &info, 0);
 	default:
 		return -ENOTTY;
 	}
@@ -186,6 +856,7 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 	bool preserve;
 	struct reflink_arguments args;
 	struct inode *inode = file->f_path.dentry->d_inode;
+	struct ocfs2_info info;
 
 	switch (cmd) {
 	case OCFS2_IOC32_GETFLAGS:
@@ -210,6 +881,13 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 
 		return ocfs2_reflink_ioctl(inode, compat_ptr(args.old_path),
 					   compat_ptr(args.new_path), preserve);
+	case OCFS2_IOC_INFO:
+		if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
+				   sizeof(struct ocfs2_info)))
+			return -EFAULT;
+
+		return ocfs2_info_handle(inode, &info, 1);
+		
 	default:
 		return -ENOIOCTLCMD;
 	}
diff --git a/fs/ocfs2/ocfs2_ioctl.h b/fs/ocfs2/ocfs2_ioctl.h
index 2d3420a..0c1bd7d 100644
--- a/fs/ocfs2/ocfs2_ioctl.h
+++ b/fs/ocfs2/ocfs2_ioctl.h
@@ -76,4 +76,119 @@ struct reflink_arguments {
 };
 #define OCFS2_IOC_REFLINK	_IOW('o', 4, struct reflink_arguments)
 
+/* Following definitions dedicated for ocfs2_info_request ioctls. */
+
+#define OCFS2_INFO_VOL_UUID_LEN		(16)
+#define OCFS2_INFO_MAX_VOL_LABEL_LEN	(64)
+#define OCFS2_INFO_VOL_UUIDSTR_LEN	(OCFS2_INFO_VOL_UUID_LEN * 2 + 1)
+#define OCFS2_INFO_MAX_SLOTS		(255)
+#define OCFS2_INFO_MAX_HIST		(32)
+
+#define OCFS2_INFO_MAX_REQUEST		(50)
+
+/* Magic number of all requests */
+#define OCFS2_INFO_MAGIC		(0x4F32494E)
+
+/*
+ * Always try to separate info request into small pieces to
+ * guarantee the backward&forward compatibility.
+ */
+
+struct ocfs2_info {
+	__u64 info_requests;	/* Array of __u64 pointers to requests */
+	__u32 info_count;	/* Number of requests in info_requests */
+};
+
+struct ocfs2_info_request {
+/*00*/	__u32 ir_magic;	/* Magic number */
+	__u32 ir_code;	/* Info request code */
+	__u32 ir_size;	/* Size of request */
+	__u32 ir_flags;	/* Request flags */
+/*10*/	/* Request specific fields */
+};
+
+struct ocfs2_info_clustersize {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_clustersize;
+};
+
+struct ocfs2_info_blocksize {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_blocksize;
+};
+
+struct ocfs2_info_slotnum {
+	struct ocfs2_info_request ir_request;
+	__u16 ir_slotnum;
+};
+
+struct ocfs2_info_label {
+	struct ocfs2_info_request ir_request;
+	__u8	ir_label[OCFS2_INFO_MAX_VOL_LABEL_LEN];
+};
+
+struct ocfs2_info_uuid {
+	struct ocfs2_info_request ir_request;
+	__u8	ir_uuid_str[OCFS2_INFO_VOL_UUIDSTR_LEN];
+};
+
+struct ocfs2_info_fs_features {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_compat_features;
+	__u32 ir_incompat_features;
+	__u32 ir_ro_compat_features;
+};
+
+struct ocfs2_info_freefrag {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_chunksize; /* chunksize in clusters(in) */
+	struct ocfs2_info_freefrag_stats { /* (out) */
+		__u32 ir_clusters;
+		__u32 ir_free_clusters;
+		__u32 ir_free_chunks;
+		__u32 ir_free_chunks_real;
+		__u32 ir_min; /* Minimum free chunksize in clusters */
+		__u32 ir_max;
+		__u32 ir_avg;
+		struct ocfs2_info_free_chunk_list {
+			__u32 ir_fc_chunks[OCFS2_INFO_MAX_HIST];
+			__u32 ir_fc_clusters[OCFS2_INFO_MAX_HIST];
+		} ir_fc_hist;
+	} ir_ffg;
+};
+
+struct ocfs2_info_freeinode {
+	struct ocfs2_info_request ir_request;
+	__u32 ir_slotnum; /* out */
+	struct ocfs2_info_local_fi {
+		__u64 ir_total;
+		__u64 ir_free;
+	} ir_fi_stat[OCFS2_INFO_MAX_SLOTS];
+};
+
+/* Codes for ocfs2_info_request */
+
+#define OCFS2_INFO_CLUSTERSIZE	(0x00000001)
+#define OCFS2_INFO_BLOCKSIZE	(0x00000002)
+#define OCFS2_INFO_SLOTNUM	(0x00000004)
+#define OCFS2_INFO_LABEL	(0x00000008)
+#define OCFS2_INFO_UUID		(0x00000010)
+#define OCFS2_INFO_FS_FEATURES	(0x00000020)
+#define OCFS2_INFO_FREEFRAG	(0x00000040)
+#define OCFS2_INFO_FREEINODE	(0x00000080)
+
+/* Flags for struct ocfs2_info_request */
+/* Filled by the caller */
+#define OCFS2_INFO_FL_NON_COHERENT	(0x00000001)	/* Cluster coherency not
+							   required. This is a hint.
+							   It is up to ocfs2 whether
+							   the request can be fulfilled
+							   without locking. */
+/* Filled by ocfs2 */
+#define OCFS2_INFO_FL_FILLED		(0x80000000)	/* Filesystem understood
+							   this request and
+							   filled in the answer */
+
+#define OCFS2_IOC_INFO		_IOR('o', 5, struct ocfs2_info)
+
 #endif /* OCFS2_IOCTL_H */
-- 
1.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6.
  2010-02-05 16:27   ` Christoph Hellwig
  2010-02-05 19:26     ` Joel Becker
  2010-02-05 23:30     ` Joel Becker
@ 2010-02-06 13:42     ` tristan.ye
  2 siblings, 0 replies; 17+ messages in thread
From: tristan.ye @ 2010-02-06 13:42 UTC (permalink / raw)
  To: ocfs2-devel

Christoph Hellwig Wrote:
> On Fri, Feb 05, 2010 at 05:59:20PM +0800, Tristan Ye wrote:
>   
>> Major changes from v4 to v5:
>>
>> Fix a ptr passing bug on PPC machine, that is, 32bits binary running
>> on 64bits kernel needs to use compat_ptr() to reterive the right ptr
>> address from userspace if we use u64 as a pointer.
>>
>> The reason why we need this ioctl is to offer the none-privileged
>> end-user a possibility to get filesys info gathering.
>>
>> We use OCFS2_IOC_INFO to manipulate the new ioctl, userspace passes a
>> structure to kernel containing an array of request pointers and request
>> count, such as,
>>
>> * From userspace:
>>
>> struct ocfs2_info_blocksize brq = {
>> 	.ir_request = {
>> 		.ir_magic = OCFS2_INFO_MAGIC,
>> 		.ir_code = OCFS2_INFO_BLOCKSIZE,
>> 		...
>> 	}
>>     
>
> I think you've totally overshoot the target here.  Just add a normal
> ioctls returning a structure with all interesting information without
> all that crap around it.  If it needs to be revisited it's trivial to
> just add another ioctl containing more fields.
>   
Hi Christoph,

I'm totally agreeing with Joel's idea, you see, we're going to add a 
ioctl about ocfs2 INFO, INFO itself was not a separate 'blocksize' or 
'clustersize' anyway.
The ocfs2_info is aiming at reteriving some info pieces as a whole, 
what's more, above logic could be more flexible than separate ioctls one 
by one.
and it's all up to user's demands, folks can even  separate their 
requirements by calling ocfs2_info more than once, by querying one info 
each time, or they can also grab all info by one time if they wish so.  
that's the INFO exactly mean, does it make sense?

Thanks and regards,
Tristan

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6.
  2010-02-05 16:27   ` Christoph Hellwig
  2010-02-05 19:26     ` Joel Becker
@ 2010-02-05 23:30     ` Joel Becker
  2010-02-06 13:42     ` tristan.ye
  2 siblings, 0 replies; 17+ messages in thread
From: Joel Becker @ 2010-02-05 23:30 UTC (permalink / raw)
  To: ocfs2-devel

On Fri, Feb 05, 2010 at 05:27:39PM +0100, Christoph Hellwig wrote:
> On Fri, Feb 05, 2010 at 05:59:20PM +0800, Tristan Ye wrote:
> I think you've totally overshoot the target here.  Just add a normal
> ioctls returning a structure with all interesting information without
> all that crap around it.  If it needs to be revisited it's trivial to
> just add another ioctl containing more fields.

	Related question: I notice that dvb has decided to use 'o' as
their ioctl letter as well.  Are we supposed to be trying to avoid their
_IOW('o', XX, ...) numbers, or can we just overlap?

Joel

-- 

Life's Little Instruction Book #335

	"Every so often, push your luck."

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6.
  2010-02-05 16:27   ` Christoph Hellwig
@ 2010-02-05 19:26     ` Joel Becker
  2010-02-05 23:30     ` Joel Becker
  2010-02-06 13:42     ` tristan.ye
  2 siblings, 0 replies; 17+ messages in thread
From: Joel Becker @ 2010-02-05 19:26 UTC (permalink / raw)
  To: ocfs2-devel

On Fri, Feb 05, 2010 at 05:27:39PM +0100, Christoph Hellwig wrote:
> I think you've totally overshoot the target here.  Just add a normal
> ioctls returning a structure with all interesting information without
> all that crap around it.  If it needs to be revisited it's trivial to
> just add another ioctl containing more fields.

	There's going to be 10 or more request, and growing as we want to
add things.  I'm to blame for the above idea, because I'd rather pass in
an array of 10+ requests than have 10+ individual calls.

Joel

-- 

"Reality is merely an illusion, albeit a very persistent one."
        - Albert Einstien

Joel Becker
Principal Software Developer
Oracle
E-mail: joel.becker at oracle.com
Phone: (650) 506-8127

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6.
  2010-02-05  9:59 ` [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6 Tristan Ye
@ 2010-02-05 16:27   ` Christoph Hellwig
  2010-02-05 19:26     ` Joel Becker
                       ` (2 more replies)
  0 siblings, 3 replies; 17+ messages in thread
From: Christoph Hellwig @ 2010-02-05 16:27 UTC (permalink / raw)
  To: ocfs2-devel

On Fri, Feb 05, 2010 at 05:59:20PM +0800, Tristan Ye wrote:
> Major changes from v4 to v5:
> 
> Fix a ptr passing bug on PPC machine, that is, 32bits binary running
> on 64bits kernel needs to use compat_ptr() to reterive the right ptr
> address from userspace if we use u64 as a pointer.
> 
> The reason why we need this ioctl is to offer the none-privileged
> end-user a possibility to get filesys info gathering.
> 
> We use OCFS2_IOC_INFO to manipulate the new ioctl, userspace passes a
> structure to kernel containing an array of request pointers and request
> count, such as,
> 
> * From userspace:
> 
> struct ocfs2_info_blocksize brq = {
> 	.ir_request = {
> 		.ir_magic = OCFS2_INFO_MAGIC,
> 		.ir_code = OCFS2_INFO_BLOCKSIZE,
> 		...
> 	}

I think you've totally overshoot the target here.  Just add a normal
ioctls returning a structure with all interesting information without
all that crap around it.  If it needs to be revisited it's trivial to
just add another ioctl containing more fields.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6.
@ 2010-02-05 10:15 Tristan Ye
  0 siblings, 0 replies; 17+ messages in thread
From: Tristan Ye @ 2010-02-05 10:15 UTC (permalink / raw)
  To: ocfs2-devel

Please ignore the former patch(miss to include a header linux/compat.h),
sorry for the noise.

Major changes from v5 to v6:

Fix a ptr passing bug on PPC machine, that is, 32bits binary running
on 64bits kernel needs to use compat_ptr() to reterive the right ptr
address from userspace if we use u64 as a pointer.

The reason why we need this ioctl is to offer the none-privileged
end-user a possibility to get filesys info gathering.

We use OCFS2_IOC_INFO to manipulate the new ioctl, userspace passes a
structure to kernel containing an array of request pointers and request
count, such as,

* From userspace:

struct ocfs2_info_blocksize brq = {
	.ir_request = {
		.ir_magic = OCFS2_INFO_MAGIC,
		.ir_code = OCFS2_INFO_BLOCKSIZE,
		...
	}
	...
}

struct ocfs2_info_clustersize crq = {
	...
}

uint64_t reqs[2] = {(unsigned long)&brq,
		    (unsigned long)&crq};

struct ocfs2_info info = {
	.ir_requests = reqs,
	.ir_count = 2,
}

ret = ioctl(fd, OCFS2_IOC_INFO, &info);

* In kernel:

Get the request pointers from *info*, then handle each request one bye one.

Idea here is to make the spearated request small enough to guarantee
a better backward&forward compatibility since a small piece of request
would be less likely to be broken if filesys on raw disk get changed.

Currently, following 8 ioctls get implemented per the requirement from
userspace tool o2info, and I believe it will grow over time:-)

	OCFS2_INFO_CLUSTERSIZE
	OCFS2_INFO_BLOCKSIZE
	OCFS2_INFO_SLOTNUM
	OCFS2_INFO_LABEL
	OCFS2_INFO_UUID
	OCFS2_INFO_FS_FEATURES
	OCFS2_INFO_FREEFRAG
	OCFS2_INFO_FREEINODE

The ioctl is only specific to OCFS2.

Signed-off-by: Tristan Ye <tristan.ye@oracle.com>
---
 fs/ocfs2/ioctl.c |  668 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 668 insertions(+), 0 deletions(-)

diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 31fbb06..96c73f0 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -20,10 +20,15 @@
 
 #include "ocfs2_fs.h"
 #include "ioctl.h"
+#include "ocfs2_ioctl.h"
 #include "resize.h"
 #include "refcounttree.h"
+#include "sysfile.h"
+#include "buffer_head_io.h"
+#include "suballoc.h"
 
 #include <linux/ext2_fs.h>
+#include <linux/compat.h>
 
 static int ocfs2_get_inode_attr(struct inode *inode, unsigned *flags)
 {
@@ -108,6 +113,661 @@ bail:
 	return status;
 }
 
+int ocfs2_info_handle_blocksize(struct inode *inode,
+				struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_blocksize req_bs;
+
+	if (copy_from_user(&req_bs, user_req,
+			   sizeof(struct ocfs2_info_blocksize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_bs.ir_blocksize = inode->i_sb->s_blocksize;
+	req_bs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_blocksize __user *)user_req,
+			 &req_bs,
+			 sizeof(struct ocfs2_info_blocksize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_clustersize(struct inode *inode,
+				  struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_clustersize req_cs;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_cs, user_req,
+			   sizeof(struct ocfs2_info_clustersize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_cs.ir_clustersize = osb->s_clustersize;
+	req_cs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_clustersize __user *)user_req,
+			 &req_cs,
+			 sizeof(struct ocfs2_info_clustersize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_slotnum(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_slotnum req_sn;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_sn, user_req,
+			   sizeof(struct ocfs2_info_slotnum))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_sn.ir_slotnum = osb->max_slots;
+	req_sn.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_slotnum __user *)user_req,
+			 &req_sn,
+			 sizeof(struct ocfs2_info_slotnum))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_label(struct inode *inode,
+			    struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_label req_lb;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_lb, user_req,
+			   sizeof(struct ocfs2_info_label))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	memcpy(req_lb.ir_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN);
+	req_lb.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_label __user *)user_req,
+			 &req_lb,
+			 sizeof(struct ocfs2_info_label))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_uuid(struct inode *inode,
+			   struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_uuid req_uuid;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_uuid, user_req,
+			   sizeof(struct ocfs2_info_uuid))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	memcpy(req_uuid.ir_uuid_str, osb->uuid_str, OCFS2_INFO_VOL_UUIDSTR_LEN);
+	req_uuid.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_uuid __user *)user_req,
+			 &req_uuid,
+			 sizeof(struct ocfs2_info_uuid))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_fs_features(struct inode *inode,
+				  struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_fs_features req_fs;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_fs, user_req,
+			   sizeof(struct ocfs2_info_fs_features))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_fs.ir_compat_features = osb->s_feature_compat;
+	req_fs.ir_incompat_features = osb->s_feature_incompat;
+	req_fs.ir_ro_compat_features = osb->s_feature_ro_compat;
+	req_fs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_fs_features __user *)user_req,
+			 &req_fs,
+			 sizeof(struct ocfs2_info_fs_features))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg,
+			   unsigned int chunksize)
+{
+	int index;
+
+	index = __ilog2_u32(chunksize);
+	if (index >= OCFS2_INFO_MAX_HIST)
+		index = OCFS2_INFO_MAX_HIST - 1;
+
+	ffg->ir_ffg.ir_fc_hist.ir_fc_chunks[index]++;
+	ffg->ir_ffg.ir_fc_hist.ir_fc_clusters[index] += chunksize;
+
+	if (chunksize > ffg->ir_ffg.ir_max)
+		ffg->ir_ffg.ir_max = chunksize;
+
+	if (chunksize < ffg->ir_ffg.ir_min)
+		ffg->ir_ffg.ir_min = chunksize;
+
+	ffg->ir_ffg.ir_avg += chunksize;
+	ffg->ir_ffg.ir_free_chunks_real++;
+}
+
+int ocfs2_info_scan_chain(struct inode *gb_inode,
+			  struct ocfs2_dinode *gb_dinode,
+			  struct ocfs2_info_freefrag *ffg,
+			  struct ocfs2_chain_rec *rec,
+			  unsigned int chunks_in_group)
+{
+	int status = 0, used;
+	u64 blkno;
+
+	struct buffer_head *bh = 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 (!le32_to_cpu(rec->c_free))
+		goto bail;
+
+	do {
+		if (!bg)
+			blkno = le64_to_cpu(rec->c_blkno);
+		else
+			blkno = le64_to_cpu(bg->bg_next_group);
+
+		if (bh) {
+			brelse(bh);
+			bh = NULL;
+		}
+
+		status = ocfs2_read_group_descriptor(gb_inode, gb_dinode,
+						     blkno, &bh);
+		if (status < 0) {
+			mlog(ML_ERROR, "Can't read the group descriptor # "
+			     "%llu from device.", (unsigned long long)blkno);
+			status = -EIO;
+			goto bail;
+		}
+
+		bg = (struct ocfs2_group_desc *)bh->b_data;
+
+		if (!le16_to_cpu(bg->bg_free_bits_count))
+			continue;
+
+		max_bits = le16_to_cpu(bg->bg_bits);
+		offset = 0;
+
+		for (chunk = 0; chunk < chunks_in_group; chunk++) {
+
+			/* Last chunk may be not a entire one */
+			if ((offset + ffg->ir_chunksize) > max_bits)
+				num_clusters = max_bits - offset;
+			else
+				num_clusters = ffg->ir_chunksize;
+
+			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)) {
+					ocfs2_info_update_ffg(ffg,
+							      last_chunksize);
+					last_chunksize = 0;
+				}
+
+				offset++;
+			}
+
+			if (chunk_free == ffg->ir_chunksize)
+				ffg->ir_ffg.ir_free_chunks++;
+		}
+
+		/* we need to update the info of last free chunk */
+		if (last_chunksize)
+			ocfs2_info_update_ffg(ffg, last_chunksize);
+
+	} while (le64_to_cpu(bg->bg_next_group));
+
+bail:
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_scan_bitmap(struct inode *gb_inode,
+			   struct ocfs2_dinode *gb_dinode,
+			   struct ocfs2_info_freefrag *ffg,
+			   struct ocfs2_chain_list *cl)
+{
+	int status = 0, i;
+	unsigned int chunks_in_group;
+	struct ocfs2_chain_rec *rec = NULL;
+
+	chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->ir_chunksize + 1;
+
+	for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) {
+
+		rec = &(cl->cl_recs[i]);
+		status = ocfs2_info_scan_chain(gb_inode, gb_dinode,
+					       ffg, rec, chunks_in_group);
+		if (status)
+			goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_freefrag(struct inode *inode,
+			       struct ocfs2_info_request __user *user_req)
+{
+	int status = 0, unlock = 0;
+
+	struct ocfs2_info_freefrag req_ffg;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct buffer_head *bh = NULL;
+	struct inode *gb_inode = NULL;
+	struct ocfs2_dinode *gb_dinode = NULL;
+	struct ocfs2_chain_list *cl = NULL;
+
+	if (copy_from_user(&req_ffg, user_req,
+			   sizeof(struct ocfs2_info_freefrag))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	/*
+	 * chunksize from userspace should be power of 2,
+	 */
+	if ((req_ffg.ir_chunksize & (req_ffg.ir_chunksize - 1)) ||
+	    (!req_ffg.ir_chunksize)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	memset(&req_ffg.ir_ffg, 0, sizeof(struct ocfs2_info_freefrag_stats));
+	req_ffg.ir_ffg.ir_min = ~0U;
+
+	gb_inode = ocfs2_get_system_file_inode(osb,
+					       GLOBAL_BITMAP_SYSTEM_INODE,
+					       OCFS2_INVALID_SLOT);
+	if (!gb_inode) {
+		mlog(ML_ERROR, "failed to get bitmap inode\n");
+		status = -EIO;
+		goto bail;
+	}
+
+	mutex_lock(&gb_inode->i_mutex);
+
+	if (!(req_ffg.ir_request.ir_flags & OCFS2_INFO_FL_NON_COHERENT)) {
+		status = ocfs2_inode_lock(gb_inode, &bh, 0);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail_mutex_unlock;
+		}
+		unlock = 1;
+
+	} else {
+		status = ocfs2_read_inode_block(gb_inode, &bh);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail;
+		}
+	}
+
+	gb_dinode = (struct ocfs2_dinode *)bh->b_data;
+
+	req_ffg.ir_ffg.ir_clusters =
+			le32_to_cpu(gb_dinode->id1.bitmap1.i_total);
+	req_ffg.ir_ffg.ir_free_clusters = req_ffg.ir_ffg.ir_clusters -
+				le32_to_cpu(gb_dinode->id1.bitmap1.i_used);
+
+	cl = &(gb_dinode->id2.i_chain);
+
+	/* Chunksize from userspace should be less than clusters in a group */
+	if (req_ffg.ir_chunksize > le16_to_cpu(cl->cl_cpg)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	status = ocfs2_info_scan_bitmap(gb_inode, gb_dinode, &req_ffg, cl);
+	if (status)
+		goto bail;
+
+	if (req_ffg.ir_ffg.ir_free_chunks_real)
+		req_ffg.ir_ffg.ir_avg = (req_ffg.ir_ffg.ir_avg /
+					req_ffg.ir_ffg.ir_free_chunks_real);
+
+	req_ffg.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_freefrag __user *)user_req,
+			 &req_ffg,
+			 sizeof(struct ocfs2_info_freefrag))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	if (unlock)
+		ocfs2_inode_unlock(gb_inode, 0);
+
+bail_mutex_unlock:
+	if (gb_inode)
+		mutex_unlock(&gb_inode->i_mutex);
+
+	iput(gb_inode);
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_scan_inode_alloc(struct inode *inode_alloc,
+				struct ocfs2_info_freeinode *fi,
+				__u32 slotnum,
+				int flags)
+{
+	int status = 0, unlock = 0;
+
+	struct buffer_head *bh = NULL;
+	struct ocfs2_dinode *dinode_alloc = NULL;
+
+	mutex_lock(&inode_alloc->i_mutex);
+
+	if (!(flags & OCFS2_INFO_FL_NON_COHERENT)) {
+		status = ocfs2_inode_lock(inode_alloc, &bh, 0);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail_mutex_unlock;
+		}
+		unlock = 1;
+
+	} else {
+
+		status = ocfs2_read_inode_block(inode_alloc, &bh);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail;
+		}
+	}
+
+	dinode_alloc = (struct ocfs2_dinode *)bh->b_data;
+
+	fi->ir_fi_stat[slotnum].ir_total =
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total);
+	fi->ir_fi_stat[slotnum].ir_free =
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) -
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_used);
+bail:
+	if (unlock)
+		ocfs2_inode_unlock(inode_alloc, 0);
+
+bail_mutex_unlock:
+	mutex_unlock(&inode_alloc->i_mutex);
+
+	iput(inode_alloc);
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_freeinode(struct inode *inode,
+				struct ocfs2_info_request __user *user_req)
+{
+	int status = 0, i;
+
+	struct ocfs2_info_freeinode req_fi;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct inode *inode_alloc = NULL;
+
+
+	if (copy_from_user(&req_fi, user_req,
+			   sizeof(struct ocfs2_info_freeinode))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_fi.ir_slotnum = osb->max_slots;
+
+	for (i = 0; i < req_fi.ir_slotnum; i++) {
+		inode_alloc =
+			ocfs2_get_system_file_inode(osb,
+						    INODE_ALLOC_SYSTEM_INODE,
+						    i);
+		if (!inode_alloc) {
+			mlog(ML_ERROR, "unable to get alloc inode in slot %u\n",
+			     (u32)i);
+			status = -EIO;
+			goto bail;
+		}
+
+		status = ocfs2_info_scan_inode_alloc(inode_alloc, &req_fi, i,
+						req_fi.ir_request.ir_flags);
+		if (status < 0)
+			goto bail;
+	}
+
+	req_fi.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_freeinode __user *)user_req,
+			 &req_fi,
+			 sizeof(struct ocfs2_info_freeinode))) {
+		status = -EFAULT;
+	}
+
+bail:
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_unknown(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_request req;
+
+	if (copy_from_user(&req, user_req, sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req.ir_flags &= ~OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user(user_req, &req,
+			 sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_request(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_request req;
+
+	if (copy_from_user(&req, user_req, sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	if (req.ir_magic != OCFS2_INFO_MAGIC) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	switch (req.ir_code) {
+	case OCFS2_INFO_BLOCKSIZE:
+		if (req.ir_size != sizeof(struct ocfs2_info_blocksize)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_blocksize(inode, user_req);
+		break;
+	case OCFS2_INFO_CLUSTERSIZE:
+		if (req.ir_size != sizeof(struct ocfs2_info_clustersize)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_clustersize(inode, user_req);
+		break;
+	case OCFS2_INFO_SLOTNUM:
+		if (req.ir_size != sizeof(struct ocfs2_info_slotnum)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_slotnum(inode, user_req);
+		break;
+	case OCFS2_INFO_LABEL:
+		if (req.ir_size != sizeof(struct ocfs2_info_label)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_label(inode, user_req);
+		break;
+	case OCFS2_INFO_UUID:
+		if (req.ir_size != sizeof(struct ocfs2_info_uuid)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_uuid(inode, user_req);
+		break;
+	case OCFS2_INFO_FS_FEATURES:
+		if (req.ir_size != sizeof(struct ocfs2_info_fs_features)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_fs_features(inode, user_req);
+		break;
+	case OCFS2_INFO_FREEFRAG:
+		if (req.ir_size != sizeof(struct ocfs2_info_freefrag)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_freefrag(inode, user_req);
+		break;
+	case OCFS2_INFO_FREEINODE:
+		if (req.ir_size != sizeof(struct ocfs2_info_freeinode)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_freeinode(inode, user_req);
+		break;
+	default:
+		status = ocfs2_info_handle_unknown(inode, user_req);
+		break;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info)
+{
+	int i, status = 0;
+	u64 req_addr;
+	struct ocfs2_info_request __user *reqp;
+
+	if ((info->info_count > OCFS2_INFO_MAX_REQUEST) ||
+	    (!info->info_requests)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	for (i = 0; i < info->info_count; i++) {
+		status = -EFAULT;
+#ifdef CONFIG_COMPAT
+		if (get_user(req_addr,
+			     (u64 __user *)compat_ptr(info->info_requests) + i))
+#else
+		if (get_user(req_addr, (u64 __user *)(info->info_requests) + i))
+#endif
+			goto bail;
+
+		reqp = (struct ocfs2_info_request *)req_addr;
+		if (!reqp) {
+			status = -EINVAL;
+			goto bail;
+		}
+
+		status = ocfs2_info_handle_request(inode, reqp);
+		if (status)
+			goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
 long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	struct inode *inode = filp->f_path.dentry->d_inode;
@@ -117,6 +777,7 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 	struct ocfs2_space_resv sr;
 	struct ocfs2_new_group_input input;
 	struct reflink_arguments args;
+	struct ocfs2_info info;
 	const char *old_path, *new_path;
 	bool preserve;
 
@@ -173,6 +834,12 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		preserve = (args.preserve != 0);
 
 		return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve);
+	case OCFS2_IOC_INFO:
+		if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
+				   sizeof(struct ocfs2_info)))
+			return -EFAULT;
+
+		return ocfs2_info_handle(inode, &info);
 	default:
 		return -ENOTTY;
 	}
@@ -196,6 +863,7 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 	case OCFS2_IOC_GROUP_ADD:
 	case OCFS2_IOC_GROUP_ADD64:
 	case OCFS2_IOC_REFLINK:
+	case OCFS2_IOC_INFO:
 		break;
 	default:
 		return -ENOIOCTLCMD;
-- 
1.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6.
  2010-02-05  9:59 [Ocfs2-devel] [PATCH 1/2] Ocfs2: Move ocfs2 ioctl definitions from ocfs2_fs.h to new added ocfs2_ioctl.h Tristan Ye
@ 2010-02-05  9:59 ` Tristan Ye
  2010-02-05 16:27   ` Christoph Hellwig
  0 siblings, 1 reply; 17+ messages in thread
From: Tristan Ye @ 2010-02-05  9:59 UTC (permalink / raw)
  To: ocfs2-devel

Major changes from v4 to v5:

Fix a ptr passing bug on PPC machine, that is, 32bits binary running
on 64bits kernel needs to use compat_ptr() to reterive the right ptr
address from userspace if we use u64 as a pointer.

The reason why we need this ioctl is to offer the none-privileged
end-user a possibility to get filesys info gathering.

We use OCFS2_IOC_INFO to manipulate the new ioctl, userspace passes a
structure to kernel containing an array of request pointers and request
count, such as,

* From userspace:

struct ocfs2_info_blocksize brq = {
	.ir_request = {
		.ir_magic = OCFS2_INFO_MAGIC,
		.ir_code = OCFS2_INFO_BLOCKSIZE,
		...
	}
	...
}

struct ocfs2_info_clustersize crq = {
	...
}

uint64_t reqs[2] = {(unsigned long)&brq,
		    (unsigned long)&crq};

struct ocfs2_info info = {
	.ir_requests = reqs,
	.ir_count = 2,
}

ret = ioctl(fd, OCFS2_IOC_INFO, &info);

* In kernel:

Get the request pointers from *info*, then handle each request one bye one.

Idea here is to make the spearated request small enough to guarantee
a better backward&forward compatibility since a small piece of request
would be less likely to be broken if filesys on raw disk get changed.

Currently, following 8 ioctls get implemented per the requirement from
userspace tool o2info, and I believe it will grow over time:-)

	OCFS2_INFO_CLUSTERSIZE
	OCFS2_INFO_BLOCKSIZE
	OCFS2_INFO_SLOTNUM
	OCFS2_INFO_LABEL
	OCFS2_INFO_UUID
	OCFS2_INFO_FS_FEATURES
	OCFS2_INFO_FREEFRAG
	OCFS2_INFO_FREEINODE

The ioctl is only specific to OCFS2.

Signed-off-by: Tristan Ye <tristan.ye@oracle.com>
---
 fs/ocfs2/ioctl.c |  667 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 667 insertions(+), 0 deletions(-)

diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 31fbb06..3684ad6 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -20,8 +20,12 @@
 
 #include "ocfs2_fs.h"
 #include "ioctl.h"
+#include "ocfs2_ioctl.h"
 #include "resize.h"
 #include "refcounttree.h"
+#include "sysfile.h"
+#include "buffer_head_io.h"
+#include "suballoc.h"
 
 #include <linux/ext2_fs.h>
 
@@ -108,6 +112,661 @@ bail:
 	return status;
 }
 
+int ocfs2_info_handle_blocksize(struct inode *inode,
+				struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_blocksize req_bs;
+
+	if (copy_from_user(&req_bs, user_req,
+			   sizeof(struct ocfs2_info_blocksize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_bs.ir_blocksize = inode->i_sb->s_blocksize;
+	req_bs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_blocksize __user *)user_req,
+			 &req_bs,
+			 sizeof(struct ocfs2_info_blocksize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_clustersize(struct inode *inode,
+				  struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_clustersize req_cs;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_cs, user_req,
+			   sizeof(struct ocfs2_info_clustersize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_cs.ir_clustersize = osb->s_clustersize;
+	req_cs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_clustersize __user *)user_req,
+			 &req_cs,
+			 sizeof(struct ocfs2_info_clustersize))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_slotnum(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_slotnum req_sn;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_sn, user_req,
+			   sizeof(struct ocfs2_info_slotnum))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_sn.ir_slotnum = osb->max_slots;
+	req_sn.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_slotnum __user *)user_req,
+			 &req_sn,
+			 sizeof(struct ocfs2_info_slotnum))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_label(struct inode *inode,
+			    struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_label req_lb;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_lb, user_req,
+			   sizeof(struct ocfs2_info_label))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	memcpy(req_lb.ir_label, osb->vol_label, OCFS2_MAX_VOL_LABEL_LEN);
+	req_lb.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_label __user *)user_req,
+			 &req_lb,
+			 sizeof(struct ocfs2_info_label))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_uuid(struct inode *inode,
+			   struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_uuid req_uuid;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_uuid, user_req,
+			   sizeof(struct ocfs2_info_uuid))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	memcpy(req_uuid.ir_uuid_str, osb->uuid_str, OCFS2_INFO_VOL_UUIDSTR_LEN);
+	req_uuid.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_uuid __user *)user_req,
+			 &req_uuid,
+			 sizeof(struct ocfs2_info_uuid))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_fs_features(struct inode *inode,
+				  struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_fs_features req_fs;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+
+	if (copy_from_user(&req_fs, user_req,
+			   sizeof(struct ocfs2_info_fs_features))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_fs.ir_compat_features = osb->s_feature_compat;
+	req_fs.ir_incompat_features = osb->s_feature_incompat;
+	req_fs.ir_ro_compat_features = osb->s_feature_ro_compat;
+	req_fs.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_fs_features __user *)user_req,
+			 &req_fs,
+			 sizeof(struct ocfs2_info_fs_features))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+void ocfs2_info_update_ffg(struct ocfs2_info_freefrag *ffg,
+			   unsigned int chunksize)
+{
+	int index;
+
+	index = __ilog2_u32(chunksize);
+	if (index >= OCFS2_INFO_MAX_HIST)
+		index = OCFS2_INFO_MAX_HIST - 1;
+
+	ffg->ir_ffg.ir_fc_hist.ir_fc_chunks[index]++;
+	ffg->ir_ffg.ir_fc_hist.ir_fc_clusters[index] += chunksize;
+
+	if (chunksize > ffg->ir_ffg.ir_max)
+		ffg->ir_ffg.ir_max = chunksize;
+
+	if (chunksize < ffg->ir_ffg.ir_min)
+		ffg->ir_ffg.ir_min = chunksize;
+
+	ffg->ir_ffg.ir_avg += chunksize;
+	ffg->ir_ffg.ir_free_chunks_real++;
+}
+
+int ocfs2_info_scan_chain(struct inode *gb_inode,
+			  struct ocfs2_dinode *gb_dinode,
+			  struct ocfs2_info_freefrag *ffg,
+			  struct ocfs2_chain_rec *rec,
+			  unsigned int chunks_in_group)
+{
+	int status = 0, used;
+	u64 blkno;
+
+	struct buffer_head *bh = 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 (!le32_to_cpu(rec->c_free))
+		goto bail;
+
+	do {
+		if (!bg)
+			blkno = le64_to_cpu(rec->c_blkno);
+		else
+			blkno = le64_to_cpu(bg->bg_next_group);
+
+		if (bh) {
+			brelse(bh);
+			bh = NULL;
+		}
+
+		status = ocfs2_read_group_descriptor(gb_inode, gb_dinode,
+						     blkno, &bh);
+		if (status < 0) {
+			mlog(ML_ERROR, "Can't read the group descriptor # "
+			     "%llu from device.", (unsigned long long)blkno);
+			status = -EIO;
+			goto bail;
+		}
+
+		bg = (struct ocfs2_group_desc *)bh->b_data;
+
+		if (!le16_to_cpu(bg->bg_free_bits_count))
+			continue;
+
+		max_bits = le16_to_cpu(bg->bg_bits);
+		offset = 0;
+
+		for (chunk = 0; chunk < chunks_in_group; chunk++) {
+
+			/* Last chunk may be not a entire one */
+			if ((offset + ffg->ir_chunksize) > max_bits)
+				num_clusters = max_bits - offset;
+			else
+				num_clusters = ffg->ir_chunksize;
+
+			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)) {
+					ocfs2_info_update_ffg(ffg,
+							      last_chunksize);
+					last_chunksize = 0;
+				}
+
+				offset++;
+			}
+
+			if (chunk_free == ffg->ir_chunksize)
+				ffg->ir_ffg.ir_free_chunks++;
+		}
+
+		/* we need to update the info of last free chunk */
+		if (last_chunksize)
+			ocfs2_info_update_ffg(ffg, last_chunksize);
+
+	} while (le64_to_cpu(bg->bg_next_group));
+
+bail:
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_scan_bitmap(struct inode *gb_inode,
+			   struct ocfs2_dinode *gb_dinode,
+			   struct ocfs2_info_freefrag *ffg,
+			   struct ocfs2_chain_list *cl)
+{
+	int status = 0, i;
+	unsigned int chunks_in_group;
+	struct ocfs2_chain_rec *rec = NULL;
+
+	chunks_in_group = le16_to_cpu(cl->cl_cpg) / ffg->ir_chunksize + 1;
+
+	for (i = 0; i < le16_to_cpu(cl->cl_next_free_rec); i++) {
+
+		rec = &(cl->cl_recs[i]);
+		status = ocfs2_info_scan_chain(gb_inode, gb_dinode,
+					       ffg, rec, chunks_in_group);
+		if (status)
+			goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_freefrag(struct inode *inode,
+			       struct ocfs2_info_request __user *user_req)
+{
+	int status = 0, unlock = 0;
+
+	struct ocfs2_info_freefrag req_ffg;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct buffer_head *bh = NULL;
+	struct inode *gb_inode = NULL;
+	struct ocfs2_dinode *gb_dinode = NULL;
+	struct ocfs2_chain_list *cl = NULL;
+
+	if (copy_from_user(&req_ffg, user_req,
+			   sizeof(struct ocfs2_info_freefrag))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	/*
+	 * chunksize from userspace should be power of 2,
+	 */
+	if ((req_ffg.ir_chunksize & (req_ffg.ir_chunksize - 1)) ||
+	    (!req_ffg.ir_chunksize)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	memset(&req_ffg.ir_ffg, 0, sizeof(struct ocfs2_info_freefrag_stats));
+	req_ffg.ir_ffg.ir_min = ~0U;
+
+	gb_inode = ocfs2_get_system_file_inode(osb,
+					       GLOBAL_BITMAP_SYSTEM_INODE,
+					       OCFS2_INVALID_SLOT);
+	if (!gb_inode) {
+		mlog(ML_ERROR, "failed to get bitmap inode\n");
+		status = -EIO;
+		goto bail;
+	}
+
+	mutex_lock(&gb_inode->i_mutex);
+
+	if (!(req_ffg.ir_request.ir_flags & OCFS2_INFO_FL_NON_COHERENT)) {
+		status = ocfs2_inode_lock(gb_inode, &bh, 0);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail_mutex_unlock;
+		}
+		unlock = 1;
+
+	} else {
+		status = ocfs2_read_inode_block(gb_inode, &bh);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail;
+		}
+	}
+
+	gb_dinode = (struct ocfs2_dinode *)bh->b_data;
+
+	req_ffg.ir_ffg.ir_clusters =
+			le32_to_cpu(gb_dinode->id1.bitmap1.i_total);
+	req_ffg.ir_ffg.ir_free_clusters = req_ffg.ir_ffg.ir_clusters -
+				le32_to_cpu(gb_dinode->id1.bitmap1.i_used);
+
+	cl = &(gb_dinode->id2.i_chain);
+
+	/* Chunksize from userspace should be less than clusters in a group */
+	if (req_ffg.ir_chunksize > le16_to_cpu(cl->cl_cpg)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	status = ocfs2_info_scan_bitmap(gb_inode, gb_dinode, &req_ffg, cl);
+	if (status)
+		goto bail;
+
+	if (req_ffg.ir_ffg.ir_free_chunks_real)
+		req_ffg.ir_ffg.ir_avg = (req_ffg.ir_ffg.ir_avg /
+					req_ffg.ir_ffg.ir_free_chunks_real);
+
+	req_ffg.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_freefrag __user *)user_req,
+			 &req_ffg,
+			 sizeof(struct ocfs2_info_freefrag))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	if (unlock)
+		ocfs2_inode_unlock(gb_inode, 0);
+
+bail_mutex_unlock:
+	if (gb_inode)
+		mutex_unlock(&gb_inode->i_mutex);
+
+	iput(gb_inode);
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_scan_inode_alloc(struct inode *inode_alloc,
+				struct ocfs2_info_freeinode *fi,
+				__u32 slotnum,
+				int flags)
+{
+	int status = 0, unlock = 0;
+
+	struct buffer_head *bh = NULL;
+	struct ocfs2_dinode *dinode_alloc = NULL;
+
+	mutex_lock(&inode_alloc->i_mutex);
+
+	if (!(flags & OCFS2_INFO_FL_NON_COHERENT)) {
+		status = ocfs2_inode_lock(inode_alloc, &bh, 0);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail_mutex_unlock;
+		}
+		unlock = 1;
+
+	} else {
+
+		status = ocfs2_read_inode_block(inode_alloc, &bh);
+		if (status < 0) {
+			mlog_errno(status);
+			goto bail;
+		}
+	}
+
+	dinode_alloc = (struct ocfs2_dinode *)bh->b_data;
+
+	fi->ir_fi_stat[slotnum].ir_total =
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total);
+	fi->ir_fi_stat[slotnum].ir_free =
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_total) -
+		le32_to_cpu(dinode_alloc->id1.bitmap1.i_used);
+bail:
+	if (unlock)
+		ocfs2_inode_unlock(inode_alloc, 0);
+
+bail_mutex_unlock:
+	mutex_unlock(&inode_alloc->i_mutex);
+
+	iput(inode_alloc);
+	brelse(bh);
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_freeinode(struct inode *inode,
+				struct ocfs2_info_request __user *user_req)
+{
+	int status = 0, i;
+
+	struct ocfs2_info_freeinode req_fi;
+	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+	struct inode *inode_alloc = NULL;
+
+
+	if (copy_from_user(&req_fi, user_req,
+			   sizeof(struct ocfs2_info_freeinode))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req_fi.ir_slotnum = osb->max_slots;
+
+	for (i = 0; i < req_fi.ir_slotnum; i++) {
+		inode_alloc =
+			ocfs2_get_system_file_inode(osb,
+						    INODE_ALLOC_SYSTEM_INODE,
+						    i);
+		if (!inode_alloc) {
+			mlog(ML_ERROR, "unable to get alloc inode in slot %u\n",
+			     (u32)i);
+			status = -EIO;
+			goto bail;
+		}
+
+		status = ocfs2_info_scan_inode_alloc(inode_alloc, &req_fi, i,
+						req_fi.ir_request.ir_flags);
+		if (status < 0)
+			goto bail;
+	}
+
+	req_fi.ir_request.ir_flags |= OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user((struct ocfs2_info_freeinode __user *)user_req,
+			 &req_fi,
+			 sizeof(struct ocfs2_info_freeinode))) {
+		status = -EFAULT;
+	}
+
+bail:
+
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_unknown(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_request req;
+
+	if (copy_from_user(&req, user_req, sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	req.ir_flags &= ~OCFS2_INFO_FL_FILLED;
+
+	if (copy_to_user(user_req, &req,
+			 sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle_request(struct inode *inode,
+			      struct ocfs2_info_request __user *user_req)
+{
+	int status = 0;
+	struct ocfs2_info_request req;
+
+	if (copy_from_user(&req, user_req, sizeof(struct ocfs2_info_request))) {
+		status = -EFAULT;
+		goto bail;
+	}
+
+	if (req.ir_magic != OCFS2_INFO_MAGIC) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	switch (req.ir_code) {
+	case OCFS2_INFO_BLOCKSIZE:
+		if (req.ir_size != sizeof(struct ocfs2_info_blocksize)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_blocksize(inode, user_req);
+		break;
+	case OCFS2_INFO_CLUSTERSIZE:
+		if (req.ir_size != sizeof(struct ocfs2_info_clustersize)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_clustersize(inode, user_req);
+		break;
+	case OCFS2_INFO_SLOTNUM:
+		if (req.ir_size != sizeof(struct ocfs2_info_slotnum)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_slotnum(inode, user_req);
+		break;
+	case OCFS2_INFO_LABEL:
+		if (req.ir_size != sizeof(struct ocfs2_info_label)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_label(inode, user_req);
+		break;
+	case OCFS2_INFO_UUID:
+		if (req.ir_size != sizeof(struct ocfs2_info_uuid)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_uuid(inode, user_req);
+		break;
+	case OCFS2_INFO_FS_FEATURES:
+		if (req.ir_size != sizeof(struct ocfs2_info_fs_features)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_fs_features(inode, user_req);
+		break;
+	case OCFS2_INFO_FREEFRAG:
+		if (req.ir_size != sizeof(struct ocfs2_info_freefrag)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_freefrag(inode, user_req);
+		break;
+	case OCFS2_INFO_FREEINODE:
+		if (req.ir_size != sizeof(struct ocfs2_info_freeinode)) {
+			status = -EINVAL;
+			break;
+		}
+		status = ocfs2_info_handle_freeinode(inode, user_req);
+		break;
+	default:
+		status = ocfs2_info_handle_unknown(inode, user_req);
+		break;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
+int ocfs2_info_handle(struct inode *inode, struct ocfs2_info *info)
+{
+	int i, status = 0;
+	u64 req_addr;
+	struct ocfs2_info_request __user *reqp;
+
+	if ((info->info_count > OCFS2_INFO_MAX_REQUEST) ||
+	    (!info->info_requests)) {
+		status = -EINVAL;
+		goto bail;
+	}
+
+	for (i = 0; i < info->info_count; i++) {
+		status = -EFAULT;
+#ifdef CONFIG_COMPAT
+		if (get_user(req_addr,
+			     (u64 __user *)compat_ptr(info->info_requests) + i))
+#else
+		if (get_user(req_addr, (u64 __user *)(info->info_requests) + i))
+#endif
+			goto bail;
+
+		reqp = (struct ocfs2_info_request *)req_addr;
+		if (!reqp) {
+			status = -EINVAL;
+			goto bail;
+		}
+
+		status = ocfs2_info_handle_request(inode, reqp);
+		if (status)
+			goto bail;
+	}
+
+bail:
+	mlog_exit(status);
+	return status;
+}
+
 long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	struct inode *inode = filp->f_path.dentry->d_inode;
@@ -117,6 +776,7 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 	struct ocfs2_space_resv sr;
 	struct ocfs2_new_group_input input;
 	struct reflink_arguments args;
+	struct ocfs2_info info;
 	const char *old_path, *new_path;
 	bool preserve;
 
@@ -173,6 +833,12 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 		preserve = (args.preserve != 0);
 
 		return ocfs2_reflink_ioctl(inode, old_path, new_path, preserve);
+	case OCFS2_IOC_INFO:
+		if (copy_from_user(&info, (struct ocfs2_info __user *)arg,
+				   sizeof(struct ocfs2_info)))
+			return -EFAULT;
+
+		return ocfs2_info_handle(inode, &info);
 	default:
 		return -ENOTTY;
 	}
@@ -196,6 +862,7 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg)
 	case OCFS2_IOC_GROUP_ADD:
 	case OCFS2_IOC_GROUP_ADD64:
 	case OCFS2_IOC_REFLINK:
+	case OCFS2_IOC_INFO:
 		break;
 	default:
 		return -ENOIOCTLCMD;
-- 
1.5.5

^ permalink raw reply related	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2010-04-19  2:22 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-04-14  2:56 [Ocfs2-devel] [PATCH 1/2] Ocfs2: Move ocfs2 ioctl definitions from ocfs2_fs.h to newly added ocfs2_ioctl.h Tristan Ye
2010-04-14  2:56 ` [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6 Tristan Ye
2010-04-16 21:50   ` Sunil Mushran
2010-04-16 22:26     ` Sunil Mushran
2010-04-17  0:11       ` Joel Becker
2010-04-17  1:03         ` Sunil Mushran
2010-04-19  2:22       ` tristan
2010-04-19  1:48     ` tristan
  -- strict thread matches above, loose matches on Subject: below --
2010-03-02  5:59 [Ocfs2-devel] [PATCH 1/2] Ocfs2: Move ocfs2 ioctl definitions from ocfs2_fs.h to newly added ocfs2_ioctl.h Tristan Ye
2010-03-02  5:59 ` [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6 Tristan Ye
2010-02-24  7:51 [Ocfs2-devel] [PATCH 1/2] Ocfs2: Move ocfs2 ioctl definitions from ocfs2_fs.h to new added ocfs2_ioctl.h Tiger Yang
2010-02-24  7:51 ` [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6 Tiger Yang
2010-02-08  9:21 [Ocfs2-devel] [PATCH 1/2] Ocfs2: Move ocfs2 ioctl definitions from ocfs2_fs.h to new added ocfs2_ioctl.h Tristan Ye
2010-02-08  9:21 ` [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6 Tristan Ye
2010-02-05 10:15 Tristan Ye
2010-02-05  9:59 [Ocfs2-devel] [PATCH 1/2] Ocfs2: Move ocfs2 ioctl definitions from ocfs2_fs.h to new added ocfs2_ioctl.h Tristan Ye
2010-02-05  9:59 ` [Ocfs2-devel] [PATCH 2/2] Ocfs2: Add new OCFS2_IOC_INFO ioctl for ocfs2 v6 Tristan Ye
2010-02-05 16:27   ` Christoph Hellwig
2010-02-05 19:26     ` Joel Becker
2010-02-05 23:30     ` Joel Becker
2010-02-06 13:42     ` tristan.ye

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.