[Ocfs2-tools-devel] [PATCH 25/32] mkfs.ocfs2: Add --global-heartbeat option

Sunil Mushran sunil.mushran at oracle.com
Tue Sep 14 15:54:55 PDT 2010


In normal course, we expect users to use tunefs.ocfs2 --update-cluster-stack
to update the disk with the running cluster information. But starting an
o2cb cluster stack with global heartbeat requires a ocfs2 volume with global
heartbeat enabled. The mkfs option has been provided for this reason.

Signed-off-by: Sunil Mushran <sunil.mushran at oracle.com>
---
 mkfs.ocfs2/check.c         |  258 ++++++++++++++++++++++++++------------------
 mkfs.ocfs2/mkfs.c          |   52 +++++++--
 mkfs.ocfs2/mkfs.h          |    4 +
 mkfs.ocfs2/mkfs.ocfs2.8.in |   24 ++++-
 4 files changed, 219 insertions(+), 119 deletions(-)

diff --git a/mkfs.ocfs2/check.c b/mkfs.ocfs2/check.c
index 597b87b..f05dc72 100644
--- a/mkfs.ocfs2/check.c
+++ b/mkfs.ocfs2/check.c
@@ -27,12 +27,21 @@
 
 #define WHOAMI "mkfs.ocfs2"
 
+int is_classic_stack(char *stack_name)
+{
+	return !strcmp(stack_name, OCFS2_CLASSIC_CLUSTER_STACK);
+}
+
 /* For ocfs2_fill_cluster_information().  Errors are to be ignored */
-static void cluster_fill(char **stack_name, char **cluster_name)
+void cluster_fill(char **stack_name, char **cluster_name, uint8_t *stack_flags)
 {
 	errcode_t err;
 	struct o2cb_cluster_desc cluster;
 
+	*stack_name = NULL;
+	*cluster_name = NULL;
+	*stack_flags = 0;
+
 	err = o2cb_init();
 	if (err)
 		return;
@@ -48,26 +57,28 @@ static void cluster_fill(char **stack_name, char **cluster_name)
 		 */
 		*stack_name = cluster.c_stack;
 		*cluster_name = cluster.c_cluster;
-	} else
-		*stack_name = strdup("o2cb");
+		*stack_flags = cluster.c_flags;
+	}
 }
 
 /* For ocfs2_fill_cluster_information().  Errors are to be ignored */
 static void disk_fill(const char *device, char **stack_name,
-		      char **cluster_name)
+		      char **cluster_name, uint8_t *stack_flags)
 {
 	errcode_t err;
 	ocfs2_filesys *fs = NULL;
 	struct o2cb_cluster_desc desc;
 
+	*stack_name = NULL;
+	*cluster_name = NULL;
+	*stack_flags = 0;
+
 	err = ocfs2_open(device, OCFS2_FLAG_RO, 0, 0, &fs);
 	if (err)
 		return;
 
-	if (!ocfs2_clusterinfo_valid(OCFS2_RAW_SB(fs->fs_super))) {
-		*stack_name = strdup("o2cb");
+	if (!ocfs2_clusterinfo_valid(OCFS2_RAW_SB(fs->fs_super)))
 		goto close;
-	}
 
 	err = ocfs2_fill_cluster_desc(fs, &desc);
 	if (err)
@@ -75,152 +86,185 @@ static void disk_fill(const char *device, char **stack_name,
 
 	*stack_name = strdup(desc.c_stack);
 	*cluster_name = strdup(desc.c_cluster);
+	*stack_flags = desc.c_flags;
 
 close:
 	ocfs2_close(fs);
 }
 
-static int pick_one(State *s, const char *what_is_it,
-		    const char *user_value, const char *o2cb_value,
-		    const char *disk_value, char **ret_value)
+static int check_cluster_compatibility(State *s, char *active, char *other,
+				       const char *other_desc)
 {
-	int rc = -1;
+	int ret = -1;
 
-	/*
-	 * First, compare o2cb and disk values.  If we get past this
-	 * block (via match or override), the o2cb value takes precedence.
-	 */
-	if (disk_value && o2cb_value && strcmp(o2cb_value, disk_value)) {
+	if (strlen(other) && strlen(active) && strcmp(active, other)) {
 		fprintf(stderr,
-			"%s is configured to use %s \"%s\", but \"%s\" is currently running.\n"
-			"%s will not be able to determine if the filesystem is in use.\n",
-			s->device_name, what_is_it,
-			disk_value, o2cb_value,
-			s->progname);
+			"%s cluster (%s) does not match the active cluster "
+			"(%s).\n%s will not be able to determine if this "
+			"operation can be done safely.\n",
+			other_desc, other, active, s->progname);
 		if (!s->force) {
 			fprintf(stderr, "To skip this check, use --force or -F\n");
 			goto out;
 		}
-		fprintf(stdout, "Overwrite of disk information forced\n");
+		fprintf(stdout, "Format is forced.\n");
 	}
 
-	if (user_value) {
-		if (o2cb_value) {
-			if (strcmp(o2cb_value, user_value)) {
-				fprintf(stderr, "%s \"%s\" was requested, but \"%s\" is running.\n",
-				what_is_it, user_value, o2cb_value);
-				if (!s->force) {
-					fprintf(stderr,
-						"To skip this check, use --force or -F\n");
-					goto out;
-				}
-				fprintf(stdout, "%s forced\n", what_is_it);
-			}
-		} else if (disk_value) {
-			if (strcmp(disk_value, user_value)) {
-				fprintf(stderr, "%s \"%s\" was requested, but %s is configured for \"%s\".\n",
-					what_is_it, user_value,
-					s->device_name, disk_value);
-				if (!s->force) {
-					fprintf(stderr,
-						"To skip this check, use --force or -F\n");
-					goto out;
-				}
-				fprintf(stderr, "%s forced\n", what_is_it);
-			}
-		}
-		*ret_value = strdup(user_value);
-	} else if (o2cb_value)
-		*ret_value = strdup(o2cb_value);
-	else if (disk_value)
-		*ret_value = strdup(disk_value);
-
-	rc = 0;
-
+	ret = 0;
 out:
-	return rc;;
+	return ret;
 }
 
 /*
  * Try to connect to the cluster and look at the disk to fill in default
  * cluster values.  If we can't connect, that's OK for now.  The only errors
  * are when values are missing or conflict with option arguments.
+ *
+ * This function assumes that each set of cluster stack values (stack and
+ * cluster name) are either both set or both unset. As in, if the user
+ * specifies a cluster stack, he must specify the cluster name too.
  */
 int ocfs2_fill_cluster_information(State *s)
 {
-	int rc = -1;
-	char *user_cluster_name = NULL;
-	char *user_stack_name = NULL;
-	char *o2cb_cluster_name = NULL;
-	char *o2cb_stack_name = NULL;
-	char *disk_cluster_name = NULL;
-	char *disk_stack_name = NULL;
+	char *user_cluster_name, *user_stack_name, user_value[100];
+	char *o2cb_cluster_name, *o2cb_stack_name, o2cb_value[100];
+	char *disk_cluster_name, *disk_stack_name, disk_value[100];
+	uint8_t user_stack_flags, o2cb_stack_flags, disk_stack_flags;
+	int clusterinfo = 0, userspace = 0;
+	int ret = -1;
 
 	if (s->mount == MOUNT_LOCAL)
 		return 0;
 
-	cluster_fill(&o2cb_stack_name, &o2cb_cluster_name);
-	disk_fill(s->device_name, &disk_stack_name, &disk_cluster_name);
+	*user_value = *o2cb_value = *disk_value = '\0';
+
+	/* get currently active cluster stack */
+	cluster_fill(&o2cb_stack_name, &o2cb_cluster_name, &o2cb_stack_flags);
+
+	/* get cluster stack configured on disk */
+	disk_fill(s->device_name, &disk_stack_name, &disk_cluster_name,
+		  &disk_stack_flags);
+
+	/* cluster stack as provided by the user */
 	user_stack_name = s->cluster_stack;
 	user_cluster_name = s->cluster_name;
+	user_stack_flags = s->stack_flags;
 
-	if (pick_one(s, "cluster stack", user_stack_name, o2cb_stack_name,
-		     disk_stack_name, &s->cluster_stack))
-		return -1;
+	s->cluster_stack = s->cluster_name = NULL;
+	s->stack_flags = 0;
 
-	if (pick_one(s, "cluster name", user_cluster_name,
-		     o2cb_cluster_name, disk_cluster_name,
-		     &s->cluster_name))
-		return -1;
+	/*
+	 * If the user specifies global heartbeat, while we can assume o2cb
+	 * stack, we still need to find the cluster name.
+	 */
+	if (s->global_heartbeat && !user_stack_name) {
+		if (!o2cb_stack_name) {
+			com_err(s->progname, 0, "Global heartbeat cannot be "
+				"enabled without either starting the o2cb "
+				"cluster stack or providing the cluster stack "
+				"info.");
+			goto out;
+		}
+		if (strcmp(o2cb_stack_name, OCFS2_CLASSIC_CLUSTER_STACK)) {
+			com_err(s->progname, 0, "Global heartbeat is "
+				"incompatible with the active cluster stack "
+				"\"%s\".\n", o2cb_stack_name);
+			goto out;
+		}
 
-	if (s->cluster_stack) {
-		if (!strcmp(s->cluster_stack, "o2cb")) {
-			/*
-			 * We've already checked for conflicts above.  Now
-			 * clear out the stack so that fill_super knows
-			 * it's a classic filesystem.
-			 */
-			free(s->cluster_stack);
-			s->cluster_stack = NULL;
-		} else if (!s->cluster_name) {
-			fprintf(stderr,
-				"Cluster name required for stack \"%s\".\n",
-				s->cluster_stack);
+		user_stack_name = strdup(o2cb_stack_name);
+		user_cluster_name = strdup(o2cb_cluster_name);
+		user_stack_flags |= OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT;
+	}
+
+	/* User specifically asked for clusterinfo */
+	if (s->feature_flags.opt_incompat & OCFS2_FEATURE_INCOMPAT_CLUSTERINFO)
+		clusterinfo++;
+
+	/* User specifically asked for usersapce stack */
+	if (s->feature_flags.opt_incompat &
+	    OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK)
+		userspace++;
+
+	/* merge - easier to compare */
+	if (user_stack_name && user_cluster_name) {
+		snprintf(user_value, sizeof(user_value), "%s,%s,%d",
+			 user_stack_name, user_cluster_name, user_stack_flags);
+	}
+	if (o2cb_stack_name && o2cb_cluster_name) {
+		snprintf(o2cb_value, sizeof(o2cb_value), "%s,%s,%d",
+			 o2cb_stack_name, o2cb_cluster_name, o2cb_stack_flags);
+	}
+	if (disk_stack_name && disk_cluster_name) {
+		snprintf(disk_value, sizeof(disk_value), "%s,%s,%d",
+			 disk_stack_name, disk_cluster_name, disk_stack_flags);
+	}
+
+	/* if disk and o2cb are not the same, continue only if force set */
+	if (check_cluster_compatibility(s, o2cb_value, disk_value, "On disk"))
+		goto out;
+
+	/* if user and o2cb are not the same, continue only if force set */
+	if (check_cluster_compatibility(s, o2cb_value, user_value,
+					"User requested"))
+		goto out;
+
+	if (strlen(user_value)) {
+		s->cluster_stack = strdup(user_stack_name);
+		s->cluster_name = strdup(user_cluster_name);
+		s->stack_flags = user_stack_flags;
+	} else if (strlen(o2cb_value)) {
+		s->cluster_stack = strdup(o2cb_stack_name);
+		s->cluster_name = strdup(o2cb_cluster_name);
+		s->stack_flags = o2cb_stack_flags;
+	} else if (strlen(disk_value)) {
+		s->cluster_stack = strdup(disk_stack_name);
+		s->cluster_name = strdup(disk_cluster_name);
+		s->stack_flags = disk_stack_flags;
+	} else { /* default */
+		if (clusterinfo || userspace) {
+			fprintf(stderr, "The clusterinfo or userspace stack "
+				"features cannot be enabled. Please rerun with "
+				"the cluster stack details or after starting "
+				"the cluster stack.\n");
 			goto out;
 		}
 	}
-	if (!s->cluster_stack && s->cluster_name) {
-		/* The classic stack doesn't write a name */
-		free(s->cluster_name);
-		s->cluster_name = NULL;
+
+	/*
+	 * If it is the o2cb stack and the user has not specifically asked
+	 * for the clusterinfo feature, then go default.
+	 */
+	if (!strlen(user_value) && s->cluster_stack) {
+		if (is_classic_stack(s->cluster_stack) && !clusterinfo &&
+		    !s->stack_flags) {
+			free(s->cluster_stack);
+			free(s->cluster_name);
+			s->cluster_stack = s->cluster_name = NULL;
+			s->stack_flags = 0;
+		}
 	}
+
 	if (s->cluster_stack) {
 		fprintf(stdout,
 			"Cluster stack: %s\n"
 			"Cluster name: %s\n"
-			"NOTE: Selecting extended slot map for userspace "
-			"cluster stack\n",
-			s->cluster_stack, s->cluster_name);
+			"Stack Flags: 0x%x\n"
+			"NOTE: Feature extended slot map may be enabled\n",
+			s->cluster_stack, s->cluster_name, s->stack_flags);
 	} else
 		fprintf(stdout, "Cluster stack: classic o2cb\n");
 
-	rc = 0;
+	ret = 0;
 
 out:
-	if (user_stack_name)
-		free(user_stack_name);
-	if (user_cluster_name)
-		free(user_cluster_name);
-	if (o2cb_stack_name)
-		free(o2cb_stack_name);
-	if (o2cb_cluster_name)
-		free(o2cb_cluster_name);
-	if (disk_stack_name)
-		free(disk_stack_name);
-	if (disk_cluster_name)
-		free(disk_cluster_name);
-
-	return rc;
+	free(user_cluster_name);
+	free(user_stack_name);
+	free(o2cb_cluster_name);
+	free(o2cb_stack_name);
+	free(disk_cluster_name);
+	free(disk_stack_name);
+	return ret;
 }
 
 int ocfs2_check_volume(State *s)
diff --git a/mkfs.ocfs2/mkfs.c b/mkfs.ocfs2/mkfs.c
index 012fbe6..5cc2bdf 100644
--- a/mkfs.ocfs2/mkfs.c
+++ b/mkfs.ocfs2/mkfs.c
@@ -129,6 +129,7 @@ enum {
 	FEATURES_OPTION,
 	CLUSTER_STACK_OPTION,
 	CLUSTER_NAME_OPTION,
+	GLOBAL_HEARTBEAT_OPTION,
 };
 
 static uint64_t align_bytes_to_clusters_ceil(State *s,
@@ -782,6 +783,7 @@ get_state(int argc, char **argv)
 	char *vol_label = NULL;
 	char *stack_name = NULL;
 	char *cluster_name = NULL;
+	int globalhb = 0;
 	unsigned int initial_slots = 0;
 	char *dummy;
 	State *s;
@@ -817,6 +819,7 @@ get_state(int argc, char **argv)
 		{ "fs-features=", 1, 0, FEATURES_OPTION },
 		{ "cluster-stack=", 1, 0, CLUSTER_STACK_OPTION },
 		{ "cluster-name=", 1, 0, CLUSTER_NAME_OPTION },
+		{ "global-heartbeat", 0, 0, GLOBAL_HEARTBEAT_OPTION },
 		{ 0, 0, 0, 0}
 	};
 
@@ -1008,6 +1011,10 @@ get_state(int argc, char **argv)
 			cluster_name = strdup(optarg);
 			break;
 
+		case GLOBAL_HEARTBEAT_OPTION:
+			globalhb = 1;
+			break;
+
 		default:
 			usage(progname);
 			break;
@@ -1110,15 +1117,31 @@ get_state(int argc, char **argv)
 	if (mount != -1)
 		s->mount = mount;
 
-	if ((stack_name || cluster_name) && (s->mount == MOUNT_LOCAL)) {
+	if ((stack_name || cluster_name || globalhb) &&
+	    (s->mount == MOUNT_LOCAL)) {
 		com_err(progname, 0,
 			"Local mount is incompatible with specifying a cluster stack");
 		exit(1);
 	}
-	if (stack_name)
-		s->cluster_stack = stack_name;
-	if (cluster_name)
-		s->cluster_name = cluster_name;
+
+	if (globalhb && stack_name &&
+	    strcmp(stack_name, OCFS2_CLASSIC_CLUSTER_STACK)) {
+		com_err(progname, 0, "Global heartbeat is incompatible "
+			"with the cluster stack, %s", stack_name);
+		exit(1);
+	}
+
+	if ((stack_name && !cluster_name) || (!stack_name && cluster_name)) {
+		com_err(progname, 0, "Both cluster stack and the cluster name "
+			"need to be specified");
+		exit(1);
+	}
+
+	s->cluster_stack = stack_name;
+	s->cluster_name = cluster_name;
+	if (globalhb)
+		s->stack_flags |= OCFS2_CLUSTER_O2CB_GLOBAL_HEARTBEAT;
+	s->global_heartbeat = globalhb;
 
 	if (no_backup_super != -1)
 		s->no_backup_super = no_backup_super;
@@ -1258,8 +1281,9 @@ usage(const char *progname)
 		"[-J journal-options]\n\t\t[-L volume-label] [-M mount-type] "
 		"[-N number-of-node-slots]\n\t\t[-T filesystem-type] [-HFqvV] "
 		"\n\t\t[--fs-feature-level=[default|max-compat|max-features]] "
-		"\n\t\t[--fs-features=[[no]sparse,...]]"
-		"[--no-backup-super] device [blocks-count]\n", progname);
+		"\n\t\t[--fs-features=[[no]sparse,...]] [--global-heartbeat]"
+		"\n\t\t[--cluster-stack=stackname] [--cluster-name=clustername]"
+		"\n\t\t[--no-backup-super] device [blocks-count]\n", progname);
 	exit(0);
 }
 
@@ -2264,16 +2288,22 @@ format_superblock(State *s, SystemFileDiskRecord *rec,
 		s->feature_flags.opt_incompat |=
 			OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP;
 
+		/* Selectively enable clusterinfo or userspace stack */
 		if (!(s->feature_flags.opt_incompat &
-		      OCFS2_FEATURE_INCOMPAT_CLUSTERINFO) &&
-		    (strcmp(s->cluster_stack, OCFS2_CLASSIC_CLUSTER_STACK))) {
-			s->feature_flags.opt_incompat |=
-				OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK;
+		      OCFS2_FEATURE_INCOMPAT_CLUSTERINFO)) {
+			if (!is_classic_stack(s->cluster_stack))
+				s->feature_flags.opt_incompat |=
+					OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK;
+			else
+				s->feature_flags.opt_incompat |=
+					OCFS2_FEATURE_INCOMPAT_CLUSTERINFO;
 		}
+
 		memcpy(di->id2.i_super.s_cluster_info.ci_stack,
 		       s->cluster_stack, OCFS2_STACK_LABEL_LEN);
 		memcpy(di->id2.i_super.s_cluster_info.ci_cluster,
 		       s->cluster_name, OCFS2_CLUSTER_NAME_LEN);
+		di->id2.i_super.s_cluster_info.ci_stackflags = s->stack_flags;
 	}
 
 	/*
diff --git a/mkfs.ocfs2/mkfs.h b/mkfs.ocfs2/mkfs.h
index c3aecd6..eeb5bcc 100644
--- a/mkfs.ocfs2/mkfs.h
+++ b/mkfs.ocfs2/mkfs.h
@@ -218,6 +218,8 @@ struct _State {
 	unsigned char *uuid;
 	char *cluster_stack;
 	char *cluster_name;
+	uint8_t stack_flags;
+	int global_heartbeat;
 	uint32_t vol_generation;
 
 	int fd;
@@ -237,5 +239,7 @@ struct _State {
 	enum ocfs2_mkfs_types fs_type;
 };
 
+int is_classic_stack(char *stack_name);
+void cluster_fill(char **stack_name, char **cluster_name, uint8_t *stack_flags);
 int ocfs2_fill_cluster_information(State *s);
 int ocfs2_check_volume(State *s);
diff --git a/mkfs.ocfs2/mkfs.ocfs2.8.in b/mkfs.ocfs2/mkfs.ocfs2.8.in
index a148133..df4e6e5 100644
--- a/mkfs.ocfs2/mkfs.ocfs2.8.in
+++ b/mkfs.ocfs2/mkfs.ocfs2.8.in
@@ -2,7 +2,7 @@
 .SH "NAME"
 mkfs.ocfs2 \- Creates an \fIOCFS2\fR file system.
 .SH "SYNOPSIS"
-\fBmkfs.ocfs2\fR [\fB\-b\fR \fIblock\-size\fR] [\fB\-C\fR \fIcluster\-size\fR] [\fB\-L\fR \fIvolume\-label\fR] [\fB\-M\fR \fImount-type\fR] [\fB\-N\fR \fInumber\-of\-nodes\fR] [\fB\-J\fR \fIjournal\-options\fR] [\fB\-\-fs\-features=\fR\fI[no]sparse...\fR] [\fB\-\-fs\-feature\-level=\fR\fIfeature\-level\fR] [\fB\-T\fR \fIfilesystem\-type\fR] [\fB\-FqvV\fR] \fIdevice\fR [\fIblocks-count\fI]
+\fBmkfs.ocfs2\fR [\fB\-b\fR \fIblock\-size\fR] [\fB\-C\fR \fIcluster\-size\fR] [\fB\-L\fR \fIvolume\-label\fR] [\fB\-M\fR \fImount-type\fR] [\fB\-N\fR \fInumber\-of\-nodes\fR] [\fB\-J\fR \fIjournal\-options\fR] [\fB\-\-fs\-features=\fR\fI[no]sparse...\fR] [\fB\-\-fs\-feature\-level=\fR\fIfeature\-level\fR] [\fB\-T\fR \fIfilesystem\-type\fR] [\fB\-\-cluster\-stack=\fR\fIstackname\fR] [\fB\-\-cluster\-name=\fR\fIclustername\fR] [\fB\-\-global\-heartbeat\fR] [\fB\-FqvV\fR] \fIdevice\fR [\fIblocks-count\fI]
 .SH "DESCRIPTION"
 .PP
 \fBmkfs.ocfs2\fR is used to create an \fIOCFS2\fR file system on a \fIdevice\fR,
@@ -175,6 +175,7 @@ and number of inodes (files, directories, symbolic links) each group owns. It is
 to limit the maximum amount of space or inodes user can have. See a documentation of
 quota-tools package for more details.
 .RE
+.RS 1.2i
 .TP
 \fBindexed-dirs\fR
 Enable directory indexing support. With this feature enabled, the file system creates indexed tree for non-inline directory entries. For large scale directories, directory entry lookup perfromance from the indexed tree is faster then from the legacy directory blocks.
@@ -200,6 +201,27 @@ Choose the maximum amount of features available. This will typically provide the
 .RE
 
 .TP
+\fB\-\-cluster\-stack\fR
+Specify the cluster stack. This option is normally not required as \fBmkfs.ocfs2\fR
+chooses the currently active cluster stack. It is required only if the cluster stack
+is not online and the user wishes to use a stack other than the default,
+\fBO2CB\fR. Other cluster stacks known to work with \fIOCFS2\fR are \fBPCMK\fR
+\fI(Pacemaker)\fR and \fBCMAN\fR. Once set, \fIOCFS2\fR will only allow mounting
+the volume if the active cluster stack (and name) matches the one specified on-disk.
+
+.TP
+\fB\-\-cluster\-name\fR
+Specify the name of the cluster. This option is mandatory if the user has specified
+a \fIcluster\-stack\fR.
+
+.TP
+\fB\-\-global\-heartbeat\fR
+Enable global heartbeat for the \fBO2CB\fR cluster stack. This option is not required
+if the \fBO2CB\fR cluster stack with global heartbeat is online. However, if the
+cluster stack is not up, then this option is required as are \fIcluster\-stack\fR and
+\fIcluster\-name\fR. For more, refer to \fBo2cb(7)\fR.
+
+.TP
 \fB\-\-no-backup-super\fR
 This option is deprecated, please use \fB--fs-features=nobackup-super\fR instead.
 
-- 
1.7.0.4




More information about the Ocfs2-tools-devel mailing list