Jan Schmidt
2011-Feb-01  13:54 UTC
[PATCH 0/7] Btrfs: Progs modifications to introduce speed profiles and dedicated log devices
This patch set for progs accompanies the (largish) kernel patch
submitted by Arne Jansen three days ago (Subject: [PATCH] Btrfs:
introducing speed profiles and dedicated log devices). It provides a
first implementation of (yet static) speed profiles:
"mkfs.btrfs" and "btrfs device add" now both support device
classes.
Thus, it is posible to dedicate a device to the log tree (e.g. a fast
ssd), or decide another should hold metadata only.
Fully configurable speed profiles can be the next step.
Jan Schmidt (7):
  moved parse_size() to utils.c
  pulled current kernel version of ioctl.h
  check open_ctree() right after it returned
  speed classes (needed for profiles) for device add. subsequent patch
    needed to fix mkfs
  speed classes (needed for profiles) for mkfs
  debug-tree output: device speed added; type output switched to hex
  made btrfs-vol compile. looks unused, so no speed class support here
    for now.
 btrfs-vol.c   |    3 +-
 btrfs.c       |    2 +-
 btrfs_cmds.c  |   93 ++++++++++++++++++++--------------
 ctree.h       |   20 +++++++
 extent-tree.c |   11 +++--
 ioctl-test.c  |    1 +
 ioctl.h       |   57 +++++++++++++++++---
 mkfs.c        |  158 +++++++++++++++++++++++++++++++++++----------------------
 print-tree.c  |    7 ++-
 utils.c       |   55 ++++++++++++++++++--
 utils.h       |   13 ++++-
 volumes.c     |   13 ++++-
 volumes.h     |    6 ++-
 13 files changed, 311 insertions(+), 128 deletions(-)
-- 
1.7.2.2
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs"
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
---
 btrfs_cmds.c |   26 --------------------------
 mkfs.c       |   26 --------------------------
 utils.c      |   26 ++++++++++++++++++++++++++
 utils.h      |    1 +
 4 files changed, 27 insertions(+), 52 deletions(-)
diff --git a/btrfs_cmds.c b/btrfs_cmds.c
index 8031c58..d707a7d 100644
--- a/btrfs_cmds.c
+++ b/btrfs_cmds.c
@@ -116,32 +116,6 @@ static int open_file_or_dir(const char *fname)
 	return fd;
 }
 
-static u64 parse_size(char *s)
-{
-	int len = strlen(s);
-	char c;
-	u64 mult = 1;
-
-	if (!isdigit(s[len - 1])) {
-		c = tolower(s[len - 1]);
-		switch (c) {
-		case ''g'':
-			mult *= 1024;
-		case ''m'':
-			mult *= 1024;
-		case ''k'':
-			mult *= 1024;
-		case ''b'':
-			break;
-		default:
-			fprintf(stderr, "Unknown size descriptor %c\n", c);
-			exit(1);
-		}
-		s[len - 1] = ''\0'';
-	}
-	return atoll(s) * mult;
-}
-
 int do_defrag(int ac, char **av)
 {
 	int fd;
diff --git a/mkfs.c b/mkfs.c
index 2e99b95..d1dd4a3 100644
--- a/mkfs.c
+++ b/mkfs.c
@@ -43,32 +43,6 @@
 #include "utils.h"
 #include "version.h"
 
-static u64 parse_size(char *s)
-{
-	int len = strlen(s);
-	char c;
-	u64 mult = 1;
-
-	if (!isdigit(s[len - 1])) {
-		c = tolower(s[len - 1]);
-		switch (c) {
-		case ''g'':
-			mult *= 1024;
-		case ''m'':
-			mult *= 1024;
-		case ''k'':
-			mult *= 1024;
-		case ''b'':
-			break;
-		default:
-			fprintf(stderr, "Unknown size descriptor %c\n", c);
-			exit(1);
-		}
-		s[len - 1] = ''\0'';
-	}
-	return atol(s) * mult;
-}
-
 static int make_root_dir(struct btrfs_root *root)
 {
 	struct btrfs_trans_handle *trans;
diff --git a/utils.c b/utils.c
index 815b967..5fb82dc 100644
--- a/utils.c
+++ b/utils.c
@@ -35,6 +35,7 @@
 #include <linux/major.h>
 #include <linux/kdev_t.h>
 #include <limits.h>
+#include <ctype.h>
 #include "kerncompat.h"
 #include "radix-tree.h"
 #include "ctree.h"
@@ -1003,3 +1004,28 @@ char *pretty_sizes(u64 size)
 	return pretty;
 }
 
+u64 parse_size(char *s)
+{
+	int len = strlen(s);
+	char c;
+	u64 mult = 1;
+
+	if (!isdigit(s[len - 1])) {
+		c = tolower(s[len - 1]);
+		switch (c) {
+		case ''g'':
+			mult *= 1024;
+		case ''m'':
+			mult *= 1024;
+		case ''k'':
+			mult *= 1024;
+		case ''b'':
+			break;
+		default:
+			fprintf(stderr, "Unknown size descriptor %c\n", c);
+			exit(1);
+		}
+		s[len - 1] = ''\0'';
+	}
+	return atoll(s) * mult;
+}
diff --git a/utils.h b/utils.h
index 9dce5b0..dc1b41d 100644
--- a/utils.h
+++ b/utils.h
@@ -40,4 +40,5 @@ int check_mounted(const char *devicename);
 int btrfs_device_already_in_root(struct btrfs_root *root, int fd,
 				 int super_offset);
 char *pretty_sizes(u64 size);
+u64 parse_size(char *s);
 #endif
-- 
1.7.2.2
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs"
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
---
 ioctl.h |   53 ++++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 44 insertions(+), 9 deletions(-)
diff --git a/ioctl.h b/ioctl.h
index 776d7a9..b66cc29 100644
--- a/ioctl.h
+++ b/ioctl.h
@@ -23,13 +23,33 @@
 
 #define BTRFS_IOCTL_MAGIC 0x94
 #define BTRFS_VOL_NAME_MAX 255
-#define BTRFS_PATH_NAME_MAX 4087
 
+/* this should be 4k */
+#define BTRFS_PATH_NAME_MAX 4087
 struct btrfs_ioctl_vol_args {
 	__s64 fd;
 	char name[BTRFS_PATH_NAME_MAX + 1];
 };
 
+#define BTRFS_SUBVOL_CREATE_ASYNC	(1ULL << 0)
+#define BTRFS_SUBVOL_RDONLY		(1ULL << 1)
+
+#define BTRFS_SUBVOL_NAME_MAX 4039
+struct btrfs_ioctl_vol_args_v2 {
+	__s64 fd;
+	__u64 transid;
+	__u64 flags;
+	__u64 unused[4];
+	char name[BTRFS_SUBVOL_NAME_MAX + 1];
+};
+
+#define BTRFS_INO_LOOKUP_PATH_MAX 4080
+struct btrfs_ioctl_ino_lookup_args {
+	__u64 treeid;
+	__u64 objectid;
+	char name[BTRFS_INO_LOOKUP_PATH_MAX];
+};
+
 struct btrfs_ioctl_search_key {
 	/* which root are we searching.  0 is the tree of tree roots */
 	__u64 tree_id;
@@ -85,11 +105,10 @@ struct btrfs_ioctl_search_args {
 	char buf[BTRFS_SEARCH_ARGS_BUFSIZE];
 };
 
-#define BTRFS_INO_LOOKUP_PATH_MAX 4080
-struct btrfs_ioctl_ino_lookup_args {
-	__u64 treeid;
-	__u64 objectid;
-	char name[BTRFS_INO_LOOKUP_PATH_MAX];
+struct btrfs_ioctl_clone_range_args {
+  __s64 src_fd;
+  __u64 src_offset, src_length;
+  __u64 dest_offset;
 };
 
 /* flags for the defrag range ioctl */
@@ -116,8 +135,15 @@ struct btrfs_ioctl_defrag_range_args {
 	 */
 	__u32 extent_thresh;
 
+	/*
+	 * which compression method to use if turning on compression
+	 * for this defrag operation.  If unspecified, zlib will
+	 * be used
+	 */
+	__u32 compress_type;
+
 	/* spare for later */
-	__u32 unused[5];
+	__u32 unused[4];
 };
 
 struct btrfs_ioctl_space_info {
@@ -155,11 +181,14 @@ struct btrfs_ioctl_space_args {
 				   struct btrfs_ioctl_vol_args)
 #define BTRFS_IOC_BALANCE _IOW(BTRFS_IOCTL_MAGIC, 12, \
 				   struct btrfs_ioctl_vol_args)
-/* 13 is for CLONE_RANGE */
+
+#define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \
+				  struct btrfs_ioctl_clone_range_args)
+
 #define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \
 				   struct btrfs_ioctl_vol_args)
 #define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
-				   struct btrfs_ioctl_vol_args)
+				struct btrfs_ioctl_vol_args)
 #define BTRFS_IOC_DEFRAG_RANGE _IOW(BTRFS_IOCTL_MAGIC, 16, \
 				struct btrfs_ioctl_defrag_range_args)
 #define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \
@@ -169,4 +198,10 @@ struct btrfs_ioctl_space_args {
 #define BTRFS_IOC_DEFAULT_SUBVOL _IOW(BTRFS_IOCTL_MAGIC, 19, u64)
 #define BTRFS_IOC_SPACE_INFO _IOWR(BTRFS_IOCTL_MAGIC, 20, \
 				    struct btrfs_ioctl_space_args)
+#define BTRFS_IOC_START_SYNC _IOR(BTRFS_IOCTL_MAGIC, 24, __u64)
+#define BTRFS_IOC_WAIT_SYNC  _IOW(BTRFS_IOCTL_MAGIC, 22, __u64)
+#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
+				   struct btrfs_ioctl_vol_args_v2)
+#define BTRFS_IOC_SUBVOL_GETFLAGS _IOW(BTRFS_IOCTL_MAGIC, 25, __u64)
+#define BTRFS_IOC_SUBVOL_SETFLAGS _IOW(BTRFS_IOCTL_MAGIC, 26, __u64)
 #endif
-- 
1.7.2.2
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs"
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
---
 mkfs.c |   10 ++++++----
 1 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/mkfs.c b/mkfs.c
index d1dd4a3..0a80d1c 100644
--- a/mkfs.c
+++ b/mkfs.c
@@ -430,7 +430,13 @@ int main(int ac, char **av)
 		fprintf(stderr, "error during mkfs %d\n", ret);
 		exit(1);
 	}
+
 	root = open_ctree(file, 0, O_RDWR);
+	if (!root) {
+		fprintf(stderr, "ctree init failed\n");
+		return -1;
+	}
+
 	root->fs_info->alloc_start = alloc_start;
 
 	ret = make_root_dir(root);
@@ -445,10 +451,6 @@ int main(int ac, char **av)
 		goto raid_groups;
 
 	btrfs_register_one_device(file);
-	if (!root) {
-		fprintf(stderr, "ctree init failed\n");
-		return -1;
-	}
 
 	zero_end = 1;
 	while(ac-- > 0) {
-- 
1.7.2.2
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs"
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jan Schmidt
2011-Feb-01  13:54 UTC
[PATCH 4/7] speed classes (needed for profiles) for device add. subsequent patch needed to fix mkfs
Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
---
 btrfs.c      |    2 +-
 btrfs_cmds.c |   67 +++++++++++++++++++++++++++++++++++++++++++++++----------
 ctree.h      |   20 +++++++++++++++++
 ioctl-test.c |    1 +
 ioctl.h      |    8 +++++-
 utils.c      |   22 ++++++++++++++++--
 utils.h      |    8 ++++++-
 7 files changed, 109 insertions(+), 19 deletions(-)
diff --git a/btrfs.c b/btrfs.c
index 46314cf..b66202c 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -101,7 +101,7 @@ static struct Command commands[] = {
 		"filesystem."
 	},
 	{ do_add_volume, -2,
-	  "device add", "<dev> [<dev>..]
<path>\n"
+	  "device add", "[-c <class>] <dev> [[-c
<class>] <dev> ...] <path>\n"
 		"Add a device to a filesystem."
 	},
 	{ do_remove_volume, -2,
diff --git a/btrfs_cmds.c b/btrfs_cmds.c
index d707a7d..639debd 100644
--- a/btrfs_cmds.c
+++ b/btrfs_cmds.c
@@ -666,7 +666,10 @@ int do_add_volume(int nargs, char **args)
 
 	char	*mntpnt = args[nargs-1];
 	int	i, fdmnt, ret=0;
-
+	int	devcnt = 0;
+	int	alloc_dev = 0;
+	int	class = BTRFS_DEVCLASS_ALL;
+	struct btrfs_class_dev *devs = NULL;
 
 	fdmnt = open_file_or_dir(mntpnt);
 	if (fdmnt < 0) {
@@ -674,46 +677,86 @@ int do_add_volume(int nargs, char **args)
 		return 12;
 	}
 
-	for(i=1 ; i < (nargs-1) ; i++ ){
-		struct btrfs_ioctl_vol_args ioctl_args;
+	optind = 1;
+	while(1) {
+		int c = getopt(nargs, args, "-c:");
+		if (c < 0)
+			break;
+		switch(c) {
+		case 1:
+			if (devcnt >= alloc_dev) {
+				alloc_dev += 10;
+				devs = realloc(devs,
+					alloc_dev*sizeof(*devs));
+			}
+			devs[devcnt].file = args[optind-1];
+			devs[devcnt].class = class;
+			devcnt++;
+			break;
+		case ''c'':
+			class = parse_class(optarg);
+			if (class == -1) {
+				fprintf(stderr, "Unknown class %s\n", optarg);
+				return 1;
+			}
+			break;
+		default:
+			fprintf(stderr, "Invalid arguments for add\n");
+			return 1;
+		}
+	}
+	if (!devcnt) {
+		fprintf(stderr, "Invalid arguments for add\n");
+		return 1;
+	}
+	for(i=0 ; i < devcnt ; i++ ){
+		struct btrfs_ioctl_vol_args_v2 ioctl_args;
 		int	devfd, res;
 		u64 dev_block_count = 0;
 		struct stat st;
 
-		devfd = open(args[i], O_RDWR);
+		devfd = open(devs[i].file, O_RDWR);
 		if (!devfd) {
-			fprintf(stderr, "ERROR: Unable to open device
''%s''\n", args[i]);
+			fprintf(stderr, "ERROR: Unable to open device
''%s''\n",
+				devs[i].file);
 			close(devfd);
 			ret++;
 			continue;
 		}
 		ret = fstat(devfd, &st);
 		if (ret) {
-			fprintf(stderr, "ERROR: Unable to stat ''%s''\n",
args[i]);
+			fprintf(stderr, "ERROR: Unable to stat ''%s''\n",
+				devs[i].file);
 			close(devfd);
 			ret++;
 			continue;
 		}
 		if (!S_ISBLK(st.st_mode)) {
-			fprintf(stderr, "ERROR: ''%s'' is not a block
device\n", args[i]);
+			fprintf(stderr, "ERROR: ''%s'' is not a block
device\n",
+				devs[i].file);
 			close(devfd);
 			ret++;
 			continue;
 		}
 
-		res = btrfs_prepare_device(devfd, args[i], 1, &dev_block_count);
+		res = btrfs_prepare_device(devfd, devs[i].file, 1,
+		                           &dev_block_count, devs[i].class);
 		if (res) {
-			fprintf(stderr, "ERROR: Unable to init ''%s''\n",
args[i]);
+			fprintf(stderr, "ERROR: Unable to init ''%s''\n",
+				devs[i].file);
 			close(devfd);
 			ret++;
 			continue;
 		}
 		close(devfd);
 
-		strcpy(ioctl_args.name, args[i]);
-		res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV, &ioctl_args);
+		memset(&ioctl_args, 0, sizeof(ioctl_args));
+		ioctl_args.seek_speed = devs[i].class;
+		strcpy(ioctl_args.name, devs[i].file);
+		res = ioctl(fdmnt, BTRFS_IOC_ADD_DEV_V2, &ioctl_args);
 		if(res<0){
-			fprintf(stderr, "ERROR: error adding the device
''%s''\n", args[i]);
+			fprintf(stderr, "ERROR: error adding the device
''%s''\n",
+				devs[i].file);
 			ret++;
 		}
 
diff --git a/ctree.h b/ctree.h
index b79e238..192e8e8 100644
--- a/ctree.h
+++ b/ctree.h
@@ -130,6 +130,24 @@ static int btrfs_csum_sizes[] = { 4, 0 };
 #define BTRFS_FT_MAX		9
 
 /*
+ * default device classes (seek_speed indices)
+ * system chunks are considered metadata for this classification
+ */
+
+#define BTRFS_DEVCLASS_LOG	75	/* dedicated to log data */
+#define BTRFS_DEVCLASS_META	45	/* metadata or log data */
+#define BTRFS_DEVCLASS_DATA	35	/* dedicated to user data */
+#define BTRFS_DEVCLASS_ALL	30 	/* data, metadata or log data */
+
+#define BTRFS_DEVCLASS_ALLOW_LOG(x) ((x) == BTRFS_DEVCLASS_LOG || \
+                                     (x) == BTRFS_DEVCLASS_META || \
+                                     (x) == BTRFS_DEVCLASS_ALL)
+#define BTRFS_DEVCLASS_ALLOW_META(x) ((x) == BTRFS_DEVCLASS_META || \
+                                      (x) == BTRFS_DEVCLASS_ALL)
+#define BTRFS_DEVCLASS_ALLOW_DATA(x) ((x) == BTRFS_DEVCLASS_DATA || \
+                                      (x) == BTRFS_DEVCLASS_ALL)
+
+/*
  * the key defines the order in the tree, and so it also defines (optimal)
  * block layout.  objectid corresonds to the inode number.  The flags
  * tells us things about the object, and is a kind of stream selector.
@@ -755,6 +773,8 @@ struct btrfs_root {
 	/* leaf allocations are done in leafsize units */
 	u32 stripesize;
 
+	int seek_speed;
+
 	int ref_cows;
 	int track_dirty;
 
diff --git a/ioctl-test.c b/ioctl-test.c
index 7cf3bc2..c47469a 100644
--- a/ioctl-test.c
+++ b/ioctl-test.c
@@ -22,6 +22,7 @@ unsigned long ioctls[] = {
 	BTRFS_IOC_INO_LOOKUP,
 	BTRFS_IOC_DEFAULT_SUBVOL,
 	BTRFS_IOC_SPACE_INFO,
+	BTRFS_IOC_ADD_DEV_V2,
 	0 };
 
 int main(int ac, char **av)
diff --git a/ioctl.h b/ioctl.h
index b66cc29..2f38725 100644
--- a/ioctl.h
+++ b/ioctl.h
@@ -39,8 +39,10 @@ struct btrfs_ioctl_vol_args_v2 {
 	__s64 fd;
 	__u64 transid;
 	__u64 flags;
-	__u64 unused[4];
-	char name[BTRFS_SUBVOL_NAME_MAX + 1];
+	__u8  seek_speed;
+	__u8  unused_u8[3];
+	__u64 unused_u64[3];
+	char name[BTRFS_PATH_NAME_MAX + 1];
 };
 
 #define BTRFS_INO_LOOKUP_PATH_MAX 4080
@@ -204,4 +206,6 @@ struct btrfs_ioctl_space_args {
 				   struct btrfs_ioctl_vol_args_v2)
 #define BTRFS_IOC_SUBVOL_GETFLAGS _IOW(BTRFS_IOCTL_MAGIC, 25, __u64)
 #define BTRFS_IOC_SUBVOL_SETFLAGS _IOW(BTRFS_IOCTL_MAGIC, 26, __u64)
+#define BTRFS_IOC_ADD_DEV_V2 _IOW(BTRFS_IOCTL_MAGIC, 27, \
+				   struct btrfs_ioctl_vol_args_v2)
 #endif
diff --git a/utils.c b/utils.c
index 5fb82dc..f1477f3 100644
--- a/utils.c
+++ b/utils.c
@@ -522,12 +522,14 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
 	return 0;
 }
 
-int btrfs_prepare_device(int fd, char *file, int zero_end, u64
*block_count_ret)
+int btrfs_prepare_device(int fd, char *file, int zero_end, u64
*block_count_ret,
+                         int class)
 {
 	u64 block_count;
 	u64 bytenr;
 	struct stat st;
 	int i, ret;
+	int mb_min = class == BTRFS_DEVCLASS_LOG ? 32 : 256;
 
 	ret = fstat(fd, &st);
 	if (ret < 0) {
@@ -542,9 +544,9 @@ int btrfs_prepare_device(int fd, char *file, int zero_end,
u64 *block_count_ret)
 	}
 	zero_end = 1;
 
-	if (block_count < 256 * 1024 * 1024) {
+	if (block_count < mb_min * 1024 * 1024) {
 		fprintf(stderr, "device %s is too small "
-		        "(must be at least 256 MB)\n", file);
+		        "(must be at least %d MB)\n", file, mb_min);
 		exit(1);
 	}
 	ret = zero_dev_start(fd);
@@ -1029,3 +1031,17 @@ u64 parse_size(char *s)
 	}
 	return atoll(s) * mult;
 }
+
+int parse_class(const char *s)
+{
+	if (strcmp(s, "data") == 0) {
+		return BTRFS_DEVCLASS_DATA;
+	} else if (strcmp(s, "meta") == 0) {
+		return BTRFS_DEVCLASS_META;
+	} else if (strcmp(s, "all") == 0) {
+		return BTRFS_DEVCLASS_ALL;
+	} else if (strcmp(s, "log") == 0) {
+		return BTRFS_DEVCLASS_LOG;
+	}
+	return -1;
+}
diff --git a/utils.h b/utils.h
index dc1b41d..981ffdb 100644
--- a/utils.h
+++ b/utils.h
@@ -27,7 +27,7 @@ int make_btrfs(int fd, const char *device, const char *label,
 int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
 			struct btrfs_root *root, u64 objectid);
 int btrfs_prepare_device(int fd, char *file, int zero_end,
-			 u64 *block_count_ret);
+			 u64 *block_count_ret, int class);
 int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
 		      struct btrfs_root *root, int fd, char *path,
 		      u64 block_count, u32 io_width, u32 io_align,
@@ -41,4 +41,10 @@ int btrfs_device_already_in_root(struct btrfs_root *root, int
fd,
 				 int super_offset);
 char *pretty_sizes(u64 size);
 u64 parse_size(char *s);
+int parse_class(const char *s);
+struct btrfs_class_dev {
+	char *file;
+	int class;
+};
+
 #endif
-- 
1.7.2.2
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs"
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jan Schmidt
2011-Feb-01  13:54 UTC
[PATCH 5/7] speed classes (needed for profiles) for mkfs
Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
---
 extent-tree.c |   11 +++--
 mkfs.c        |  122 ++++++++++++++++++++++++++++++++++++++++++--------------
 utils.c       |    7 ++-
 utils.h       |    4 +-
 volumes.c     |   13 +++++-
 volumes.h     |    6 ++-
 6 files changed, 121 insertions(+), 42 deletions(-)
diff --git a/extent-tree.c b/extent-tree.c
index b2f9bb2..b580b0c 100644
--- a/extent-tree.c
+++ b/extent-tree.c
@@ -1788,7 +1788,7 @@ static void set_avail_alloc_bits(struct btrfs_fs_info
*fs_info, u64 flags)
 
 static int do_chunk_alloc(struct btrfs_trans_handle *trans,
 			  struct btrfs_root *extent_root, u64 alloc_bytes,
-			  u64 flags)
+			  u64 flags, int seek_speed)
 {
 	struct btrfs_space_info *space_info;
 	u64 thresh;
@@ -1812,7 +1812,8 @@ static int do_chunk_alloc(struct btrfs_trans_handle
*trans,
 	    thresh)
 		return 0;
 
-	ret = btrfs_alloc_chunk(trans, extent_root, &start, &num_bytes,
flags);
+	ret = btrfs_alloc_chunk(trans, extent_root, &start, &num_bytes, flags,
+				seek_speed);
 	if (ret == -ENOSPC) {
 		space_info->full = 1;
 		return 0;
@@ -2513,11 +2514,13 @@ static int btrfs_reserve_extent(struct
btrfs_trans_handle *trans,
 		if (!(data & BTRFS_BLOCK_GROUP_METADATA)) {
 			ret = do_chunk_alloc(trans, root->fs_info->extent_root,
 					     num_bytes,
-					     BTRFS_BLOCK_GROUP_METADATA);
+					     BTRFS_BLOCK_GROUP_METADATA,
+					     root->seek_speed);
 			BUG_ON(ret);
 		}
 		ret = do_chunk_alloc(trans, root->fs_info->extent_root,
-				     num_bytes + 2 * 1024 * 1024, data);
+				     num_bytes + 2 * 1024 * 1024, data,
+				     root->seek_speed);
 		BUG_ON(ret);
 	}
 
diff --git a/mkfs.c b/mkfs.c
index 0a80d1c..231448b 100644
--- a/mkfs.c
+++ b/mkfs.c
@@ -64,7 +64,8 @@ static int make_root_dir(struct btrfs_root *root)
 
 	ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
 				&chunk_start, &chunk_size,
-				BTRFS_BLOCK_GROUP_METADATA);
+				BTRFS_BLOCK_GROUP_METADATA,
+				root->seek_speed);
 	BUG_ON(ret);
 	ret = btrfs_make_block_group(trans, root, 0,
 				     BTRFS_BLOCK_GROUP_METADATA,
@@ -77,16 +78,6 @@ static int make_root_dir(struct btrfs_root *root)
 	trans = btrfs_start_transaction(root, 1);
 	BUG_ON(!trans);
 
-	ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
-				&chunk_start, &chunk_size,
-				BTRFS_BLOCK_GROUP_DATA);
-	BUG_ON(ret);
-	ret = btrfs_make_block_group(trans, root, 0,
-				     BTRFS_BLOCK_GROUP_DATA,
-				     BTRFS_FIRST_CHUNK_TREE_OBJECTID,
-				     chunk_start, chunk_size);
-	BUG_ON(ret);
-
 	ret = btrfs_make_root_dir(trans, root->fs_info->tree_root,
 			      BTRFS_ROOT_TREE_DIR_OBJECTID);
 	if (ret)
@@ -156,19 +147,19 @@ static int recow_roots(struct btrfs_trans_handle *trans,
 }
 
 static int create_one_raid_group(struct btrfs_trans_handle *trans,
-			      struct btrfs_root *root, u64 type)
+			      struct btrfs_root *root, u64 type, int seek_speed)
 {
 	u64 chunk_start;
 	u64 chunk_size;
 	int ret;
 
 	ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
-				&chunk_start, &chunk_size, type);
-	BUG_ON(ret);
+				&chunk_start, &chunk_size, type, seek_speed);
+	if (ret)
+		return ret;
 	ret = btrfs_make_block_group(trans, root->fs_info->extent_root, 0,
 				     type, BTRFS_FIRST_CHUNK_TREE_OBJECTID,
 				     chunk_start, chunk_size);
-	BUG_ON(ret);
 	return ret;
 }
 
@@ -191,12 +182,14 @@ static int create_raid_groups(struct btrfs_trans_handle
*trans,
 	if (allowed & metadata_profile) {
 		ret = create_one_raid_group(trans, root,
 					    BTRFS_BLOCK_GROUP_SYSTEM |
-					    (allowed & metadata_profile));
+					    (allowed & metadata_profile),
+					    root->seek_speed);
 		BUG_ON(ret);
 
 		ret = create_one_raid_group(trans, root,
 					    BTRFS_BLOCK_GROUP_METADATA |
-					    (allowed & metadata_profile));
+					    (allowed & metadata_profile),
+					    root->seek_speed);
 		BUG_ON(ret);
 
 		ret = recow_roots(trans, root);
@@ -205,7 +198,8 @@ static int create_raid_groups(struct btrfs_trans_handle
*trans,
 	if (num_devices > 1 && (allowed & data_profile)) {
 		ret = create_one_raid_group(trans, root,
 					    BTRFS_BLOCK_GROUP_DATA |
-					    (allowed & data_profile));
+					    (allowed & data_profile),
+					    root->seek_speed);
 		BUG_ON(ret);
 	}
 	return 0;
@@ -244,6 +238,8 @@ static void print_usage(void)
 	fprintf(stderr, "options:\n");
 	fprintf(stderr, "\t -A --alloc-start the offset to start the FS\n");
 	fprintf(stderr, "\t -b --byte-count total number of bytes in the
FS\n");
+	fprintf(stderr, "\t -c --class {data|meta|log|all} valid until overridden
by\n");
+	fprintf(stderr, "\t                                next -c, default:
all\n");
 	fprintf(stderr, "\t -d --data data profile, raid0, raid1, raid10 or
single\n");
 	fprintf(stderr, "\t -l --leafsize size of btree leaves\n");
 	fprintf(stderr, "\t -L --label set a label\n");
@@ -305,10 +301,16 @@ static struct option long_options[] = {
 	{ "nodesize", 1, NULL, ''n'' },
 	{ "sectorsize", 1, NULL, ''s'' },
 	{ "data", 1, NULL, ''d'' },
+	{ "class", 1, NULL, ''c'' },
 	{ "version", 0, NULL, ''V'' },
 	{ 0, 0, 0, 0}
 };
 
+static const char opts_allowed_in_devlist[256] = {
+	[1] = 1,
+	[''c''] = 1,
+};
+
 int main(int ac, char **av)
 {
 	char *file;
@@ -326,23 +328,61 @@ int main(int ac, char **av)
 	u32 sectorsize = 4096;
 	u32 nodesize = leafsize;
 	u32 stripesize = 4096;
+	int class = BTRFS_DEVCLASS_ALL;
 	int zero_end = 1;
 	int option_index = 0;
 	int fd;
 	int first_fd;
 	int ret;
 	int i;
+	int devcnt = 0;
+	int devind = 0;
+	int alloc_dev = 0;
+	int first_dev = -1;
+	int data_devs = 0;
+	int meta_devs = 0;
+	struct btrfs_class_dev *devs = NULL;
 
 	while(1) {
 		int c;
-		c = getopt_long(ac, av, "A:b:l:n:s:m:d:L:V", long_options,
+		c = getopt_long(ac, av, "-A:b:c:l:n:s:m:d:L:V", long_options,
 				&option_index);
 		if (c < 0)
 			break;
+		if (devcnt && !opts_allowed_in_devlist[c]) {
+			print_usage();
+		}
 		switch(c) {
+			case 1:
+				if (devcnt >= alloc_dev) {
+					alloc_dev += 10;
+					devs = realloc(devs,
+						alloc_dev*sizeof(*devs));
+				}
+				devs[devcnt].file = av[optind-1];
+				devs[devcnt].class = class;
+				if (BTRFS_DEVCLASS_ALLOW_DATA(class))
+					data_devs++;
+				if (BTRFS_DEVCLASS_ALLOW_META(class))
+					meta_devs++;
+
+				if ((first_dev < 0 ||
+				     devs[first_dev].class!=BTRFS_DEVCLASS_META)
+				    && BTRFS_DEVCLASS_ALLOW_META(class))
+					first_dev = devcnt;
+				devcnt++;
+				break;
 			case ''A'':
 				alloc_start = parse_size(optarg);
 				break;
+			case ''c'':
+				class = parse_class(optarg);
+				if (class == -1) {
+					fprintf(stderr, "Unknown class %s\n",
+						optarg);
+					print_usage();
+				}
+				break;
 			case ''d'':
 				data_profile = parse_profile(optarg);
 				break;
@@ -388,14 +428,30 @@ int main(int ac, char **av)
 		fprintf(stderr, "Illegal nodesize %u\n", nodesize);
 		exit(1);
 	}
-	ac = ac - optind;
-	if (ac == 0)
+	if (devcnt == 0)
 		print_usage();
+	if (meta_devs == 0 || data_devs == 0) {
+		fprintf(stderr, "Illegal setup. You will need space for data "
+		                "and space for metadata.\n\n");
+		print_usage();
+	}
 
 	printf("\nWARNING! - %s IS EXPERIMENTAL\n", BTRFS_BUILD_VERSION);
 	printf("WARNING! - see http://btrfs.wiki.kernel.org before
using\n\n");
 
-	file = av[optind++];
+	if (first_dev > 0) {
+		/*
+		 * bootstrap chunks (meta/system) will be created on the first
+		 * device during make_btrfs(). to create them on the preferred
+		 * metadata device bring that to the front of the list.
+		 */
+		struct btrfs_class_dev tmp;
+		tmp = devs[0];
+		devs[0] = devs[first_dev];
+		devs[first_dev] = tmp;
+	}
+
+	file = devs[0].file;
 	ret = check_mounted(file);
 	if (ret < 0) {
 		fprintf(stderr, "error checking %s mount status\n", file);
@@ -405,7 +461,6 @@ int main(int ac, char **av)
 		fprintf(stderr, "%s is mounted\n", file);
 		exit(1);
 	}
-	ac--;
 	fd = open(file, O_RDWR);
 	if (fd < 0) {
 		fprintf(stderr, "unable to open %s\n", file);
@@ -413,7 +468,8 @@ int main(int ac, char **av)
 	}
 	first_fd = fd;
 	first_file = file;
-	ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count);
+	ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count,
+	                           devs[0].class);
 	if (block_count == 0)
 		block_count = dev_block_count;
 
@@ -423,9 +479,11 @@ int main(int ac, char **av)
 			leafsize * i;
 	}
 
+	printf("creating fs on %s, id 1\n", file);
+
 	ret = make_btrfs(fd, file, label, blocks, block_count,
 			 nodesize, leafsize,
-			 sectorsize, stripesize);
+			 sectorsize, stripesize, devs[0].class);
 	if (ret) {
 		fprintf(stderr, "error during mkfs %d\n", ret);
 		exit(1);
@@ -438,6 +496,7 @@ int main(int ac, char **av)
 	}
 
 	root->fs_info->alloc_start = alloc_start;
+	root->seek_speed = devs[0].class;
 
 	ret = make_root_dir(root);
 	if (ret) {
@@ -447,14 +506,14 @@ int main(int ac, char **av)
 
 	trans = btrfs_start_transaction(root, 1);
 
-	if (ac == 0)
+	if (devcnt == 1)
 		goto raid_groups;
 
 	btrfs_register_one_device(file);
 
 	zero_end = 1;
-	while(ac-- > 0) {
-		file = av[optind++];
+	while(++devind < devcnt) {
+		file = devs[devind].file;
 		ret = check_mounted(file);
 		if (ret < 0) {
 			fprintf(stderr, "error checking %s mount status\n",
@@ -478,13 +537,14 @@ int main(int ac, char **av)
 			close(fd);
 			continue;
 		}
-		ret = btrfs_prepare_device(fd, file, zero_end,
-					   &dev_block_count);
+		ret = btrfs_prepare_device(fd, file, zero_end, &dev_block_count,
+		                           devs[devind].class);
 
 		BUG_ON(ret);
 
 		ret = btrfs_add_to_fsid(trans, root, fd, file, dev_block_count,
-					sectorsize, sectorsize, sectorsize);
+					sectorsize, sectorsize, sectorsize,
+					devs[devind].class);
 		BUG_ON(ret);
 		btrfs_register_one_device(file);
 	}
diff --git a/utils.c b/utils.c
index f1477f3..7134f03 100644
--- a/utils.c
+++ b/utils.c
@@ -62,7 +62,7 @@ static u64 reference_root_table[] = {
 
 int make_btrfs(int fd, const char *device, const char *label,
 	       u64 blocks[7], u64 num_bytes, u32 nodesize,
-	       u32 leafsize, u32 sectorsize, u32 stripesize)
+	       u32 leafsize, u32 sectorsize, u32 stripesize, int seek_speed)
 {
 	struct btrfs_super_block super;
 	struct extent_buffer *buf;
@@ -269,6 +269,7 @@ int make_btrfs(int fd, const char *device, const char
*label,
 	btrfs_set_device_io_width(buf, dev_item, sectorsize);
 	btrfs_set_device_sector_size(buf, dev_item, sectorsize);
 	btrfs_set_device_type(buf, dev_item, 0);
+	btrfs_set_device_seek_speed(buf, dev_item, seek_speed);
 
 	write_extent_buffer(buf, super.dev_item.uuid,
 			    (unsigned long)btrfs_device_uuid(dev_item),
@@ -451,7 +452,7 @@ static int zero_dev_end(int fd, u64 dev_size)
 int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
 		      struct btrfs_root *root, int fd, char *path,
 		      u64 block_count, u32 io_width, u32 io_align,
-		      u32 sectorsize)
+		      u32 sectorsize, int seek_speed)
 {
 	struct btrfs_super_block *disk_super;
 	struct btrfs_super_block *super = &root->fs_info->super_copy;
@@ -487,6 +488,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
 	device->total_bytes = block_count;
 	device->bytes_used = 0;
 	device->total_ios = 0;
+	device->seek_speed = seek_speed;
 	device->dev_root = root->fs_info->dev_root;
 
 	ret = btrfs_add_device(trans, root, device);
@@ -511,6 +513,7 @@ int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
 	btrfs_set_stack_device_sector_size(dev_item, device->sector_size);
 	btrfs_set_stack_device_total_bytes(dev_item, device->total_bytes);
 	btrfs_set_stack_device_bytes_used(dev_item, device->bytes_used);
+	btrfs_set_stack_device_seek_speed(dev_item, device->seek_speed);
 	memcpy(&dev_item->uuid, device->uuid, BTRFS_UUID_SIZE);
 
 	ret = pwrite(fd, buf, sectorsize, BTRFS_SUPER_INFO_OFFSET);
diff --git a/utils.h b/utils.h
index 981ffdb..6542f39 100644
--- a/utils.h
+++ b/utils.h
@@ -23,7 +23,7 @@
 
 int make_btrfs(int fd, const char *device, const char *label,
 	       u64 blocks[6], u64 num_bytes, u32 nodesize,
-	       u32 leafsize, u32 sectorsize, u32 stripesize);
+	       u32 leafsize, u32 sectorsize, u32 stripesize, int seek_speed);
 int btrfs_make_root_dir(struct btrfs_trans_handle *trans,
 			struct btrfs_root *root, u64 objectid);
 int btrfs_prepare_device(int fd, char *file, int zero_end,
@@ -31,7 +31,7 @@ int btrfs_prepare_device(int fd, char *file, int zero_end,
 int btrfs_add_to_fsid(struct btrfs_trans_handle *trans,
 		      struct btrfs_root *root, int fd, char *path,
 		      u64 block_count, u32 io_width, u32 io_align,
-		      u32 sectorsize);
+		      u32 sectorsize, int seek_speed);
 int btrfs_scan_for_fsid(struct btrfs_fs_devices *fs_devices, u64 total_devs,
 			int run_ioctls);
 void btrfs_register_one_device(char *fname);
diff --git a/volumes.c b/volumes.c
index 7671855..8507a84 100644
--- a/volumes.c
+++ b/volumes.c
@@ -29,6 +29,7 @@
 #include "transaction.h"
 #include "print-tree.h"
 #include "volumes.h"
+#include "utils.h"
 
 struct stripe {
 	struct btrfs_device *dev;
@@ -522,7 +523,7 @@ int btrfs_add_device(struct btrfs_trans_handle *trans,
 	btrfs_set_device_total_bytes(leaf, dev_item, device->total_bytes);
 	btrfs_set_device_bytes_used(leaf, dev_item, device->bytes_used);
 	btrfs_set_device_group(leaf, dev_item, 0);
-	btrfs_set_device_seek_speed(leaf, dev_item, 0);
+	btrfs_set_device_seek_speed(leaf, dev_item, device->seek_speed);
 	btrfs_set_device_bandwidth(leaf, dev_item, 0);
 	btrfs_set_device_start_offset(leaf, dev_item, 0);
 
@@ -577,6 +578,8 @@ int btrfs_update_device(struct btrfs_trans_handle *trans,
 	btrfs_set_device_sector_size(leaf, dev_item, device->sector_size);
 	btrfs_set_device_total_bytes(leaf, dev_item, device->total_bytes);
 	btrfs_set_device_bytes_used(leaf, dev_item, device->bytes_used);
+	btrfs_set_device_seek_speed(leaf, dev_item, device->seek_speed);
+	btrfs_set_device_bandwidth(leaf, dev_item, 0);
 	btrfs_mark_buffer_dirty(leaf);
 
 out:
@@ -630,7 +633,7 @@ static u64 chunk_bytes_by_type(u64 type, u64 calc_size, int
num_stripes,
 
 int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
 		      struct btrfs_root *extent_root, u64 *start,
-		      u64 *num_bytes, u64 type)
+		      u64 *num_bytes, u64 type, int seek_speed)
 {
 	u64 dev_offset;
 	struct btrfs_fs_info *info = extent_root->fs_info;
@@ -659,6 +662,8 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
 	int stripe_len = 64 * 1024;
 	struct btrfs_key key;
 
+	BUG_ON(seek_speed <= 1);
+
 	if (list_empty(dev_list)) {
 		return -ENOSPC;
 	}
@@ -735,6 +740,8 @@ again:
 		device = list_entry(cur, struct btrfs_device, dev_list);
 		avail = device->total_bytes - device->bytes_used;
 		cur = cur->next;
+		if (device->seek_speed != seek_speed)
+			goto next;
 		if (avail >= min_free) {
 			list_move_tail(&device->dev_list, &private_devs);
 			index++;
@@ -742,6 +749,7 @@ again:
 				index++;
 		} else if (avail > max_avail)
 			max_avail = avail;
+next:
 		if (cur == dev_list)
 			break;
 	}
@@ -1233,6 +1241,7 @@ static int fill_device_from_item(struct extent_buffer
*leaf,
 	device->io_align = btrfs_device_io_align(leaf, dev_item);
 	device->io_width = btrfs_device_io_width(leaf, dev_item);
 	device->sector_size = btrfs_device_sector_size(leaf, dev_item);
+	device->seek_speed = btrfs_device_seek_speed(leaf, dev_item);
 
 	ptr = (unsigned long)btrfs_device_uuid(dev_item);
 	read_extent_buffer(leaf, device->uuid, ptr, BTRFS_UUID_SIZE);
diff --git a/volumes.h b/volumes.h
index bb78751..2ba9a24 100644
--- a/volumes.h
+++ b/volumes.h
@@ -57,6 +57,10 @@ struct btrfs_device {
 	/* type and info about this device */
 	u64 type;
 
+	/* the speed is used to determine if the device should be a preferred
+	 * log device */
+	u8 seek_speed;
+
 	/* physical drive uuid (or lvm uuid) */
 	u8 uuid[BTRFS_UUID_SIZE];
 };
@@ -106,7 +110,7 @@ int btrfs_read_sys_array(struct btrfs_root *root);
 int btrfs_read_chunk_tree(struct btrfs_root *root);
 int btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
 		      struct btrfs_root *extent_root, u64 *start,
-		      u64 *num_bytes, u64 type);
+		      u64 *num_bytes, u64 type, int seek_speed);
 int btrfs_read_super_device(struct btrfs_root *root, struct extent_buffer
*buf);
 int btrfs_add_device(struct btrfs_trans_handle *trans,
 		     struct btrfs_root *root,
-- 
1.7.2.2
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs"
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jan Schmidt
2011-Feb-01  13:54 UTC
[PATCH 6/7] debug-tree output: device speed added; type output switched to hex
Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
---
 print-tree.c |    7 ++++---
 1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/print-tree.c b/print-tree.c
index ac575d5..79a2f21 100644
--- a/print-tree.c
+++ b/print-tree.c
@@ -83,7 +83,7 @@ static void print_chunk(struct extent_buffer *eb, struct
btrfs_chunk *chunk)
 {
 	int num_stripes = btrfs_chunk_num_stripes(eb, chunk);
 	int i;
-	printf("\t\tchunk length %llu owner %llu type %llu num_stripes
%d\n",
+	printf("\t\tchunk length %llu owner %llu type 0x%llx num_stripes
%d\n",
 	       (unsigned long long)btrfs_chunk_length(eb, chunk),
 	       (unsigned long long)btrfs_chunk_owner(eb, chunk),
 	       (unsigned long long)btrfs_chunk_type(eb, chunk),
@@ -98,10 +98,11 @@ static void print_dev_item(struct extent_buffer *eb,
 			   struct btrfs_dev_item *dev_item)
 {
 	printf("\t\tdev item devid %llu "
-	       "total_bytes %llu bytes used %Lu\n",
+	       "total_bytes %llu bytes used %Lu speed %u\n",
 	       (unsigned long long)btrfs_device_id(eb, dev_item),
 	       (unsigned long long)btrfs_device_total_bytes(eb, dev_item),
-	       (unsigned long long)btrfs_device_bytes_used(eb, dev_item));
+	       (unsigned long long)btrfs_device_bytes_used(eb, dev_item),
+	       btrfs_device_seek_speed(eb, dev_item));
 }
 
 static void print_uuids(struct extent_buffer *eb)
-- 
1.7.2.2
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs"
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jan Schmidt
2011-Feb-01  13:54 UTC
[PATCH 7/7] made btrfs-vol compile. looks unused, so no speed class support here for now.
Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
---
 btrfs-vol.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/btrfs-vol.c b/btrfs-vol.c
index 4ed799d..138c69c 100644
--- a/btrfs-vol.c
+++ b/btrfs-vol.c
@@ -143,7 +143,8 @@ int main(int ac, char **av)
 		exit(1);
 	}
 	if (cmd == BTRFS_IOC_ADD_DEV) {
-		ret = btrfs_prepare_device(devfd, device, 1, &dev_block_count);
+		ret = btrfs_prepare_device(devfd, device, 1, &dev_block_count,
+		                           BTRFS_DEVCLASS_ALL);
 		if (ret) {
 			fprintf(stderr, "Unable to init %s\n", device);
 			exit(1);
-- 
1.7.2.2
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs"
in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html