diff options
author | Joel Becker <joel.becker@oracle.com> | 2008-02-01 18:08:23 -0500 |
---|---|---|
committer | Mark Fasheh <mfasheh@suse.com> | 2008-04-18 11:56:05 -0400 |
commit | b61817e1166c5e19c08baf05196477cc345e1b1a (patch) | |
tree | 8da1c387086313aecdbb8f96fd0ab33417860620 /fs/ocfs2 | |
parent | 74ae4e104dfc57017783fc07d5f2f9129062207f (diff) |
ocfs2: Add the USERSPACE_STACK incompat bit.
The filesystem gains the USERSPACE_STACK incomat bit and the
s_cluster_info field on the superblock. When a userspace stack is in
use, the name of the stack is stored on-disk for mount-time
verification.
The "cluster_stack" option is added to mount(2) processing. The mount
process needs to pass the matching stack name. If the passed name and
the on-disk name do not match, the mount is failed.
When using the classic o2cb stack, the incompat bit is *not* set and no
mount option is used other than the usual heartbeat=local. Thus, the
filesystem is compatible with older tools.
Signed-off-by: Joel Becker <joel.becker@oracle.com>
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Diffstat (limited to 'fs/ocfs2')
-rw-r--r-- | fs/ocfs2/ocfs2.h | 7 | ||||
-rw-r--r-- | fs/ocfs2/ocfs2_fs.h | 40 | ||||
-rw-r--r-- | fs/ocfs2/super.c | 90 |
3 files changed, 134 insertions, 3 deletions
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index af929eca5412..9ff5811345a9 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h | |||
@@ -248,6 +248,7 @@ struct ocfs2_super | |||
248 | struct ocfs2_alloc_stats alloc_stats; | 248 | struct ocfs2_alloc_stats alloc_stats; |
249 | char dev_str[20]; /* "major,minor" of the device */ | 249 | char dev_str[20]; /* "major,minor" of the device */ |
250 | 250 | ||
251 | char osb_cluster_stack[OCFS2_STACK_LABEL_LEN + 1]; | ||
251 | struct ocfs2_cluster_connection *cconn; | 252 | struct ocfs2_cluster_connection *cconn; |
252 | struct ocfs2_lock_res osb_super_lockres; | 253 | struct ocfs2_lock_res osb_super_lockres; |
253 | struct ocfs2_lock_res osb_rename_lockres; | 254 | struct ocfs2_lock_res osb_rename_lockres; |
@@ -368,6 +369,12 @@ static inline int ocfs2_is_soft_readonly(struct ocfs2_super *osb) | |||
368 | return ret; | 369 | return ret; |
369 | } | 370 | } |
370 | 371 | ||
372 | static inline int ocfs2_userspace_stack(struct ocfs2_super *osb) | ||
373 | { | ||
374 | return (osb->s_feature_incompat & | ||
375 | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK); | ||
376 | } | ||
377 | |||
371 | static inline int ocfs2_mount_local(struct ocfs2_super *osb) | 378 | static inline int ocfs2_mount_local(struct ocfs2_super *osb) |
372 | { | 379 | { |
373 | return (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT); | 380 | return (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT); |
diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index c49502329ab0..52c426665154 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h | |||
@@ -89,7 +89,8 @@ | |||
89 | #define OCFS2_FEATURE_INCOMPAT_SUPP (OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT \ | 89 | #define OCFS2_FEATURE_INCOMPAT_SUPP (OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT \ |
90 | | OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC \ | 90 | | OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC \ |
91 | | OCFS2_FEATURE_INCOMPAT_INLINE_DATA \ | 91 | | OCFS2_FEATURE_INCOMPAT_INLINE_DATA \ |
92 | | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP) | 92 | | OCFS2_FEATURE_INCOMPAT_EXTENDED_SLOT_MAP \ |
93 | | OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK) | ||
93 | #define OCFS2_FEATURE_RO_COMPAT_SUPP OCFS2_FEATURE_RO_COMPAT_UNWRITTEN | 94 | #define OCFS2_FEATURE_RO_COMPAT_SUPP OCFS2_FEATURE_RO_COMPAT_UNWRITTEN |
94 | 95 | ||
95 | /* | 96 | /* |
@@ -131,6 +132,17 @@ | |||
131 | 132 | ||
132 | 133 | ||
133 | /* | 134 | /* |
135 | * Support for alternate, userspace cluster stacks. If set, the superblock | ||
136 | * field s_cluster_info contains a tag for the alternate stack in use as | ||
137 | * well as the name of the cluster being joined. | ||
138 | * mount.ocfs2 must pass in a matching stack name. | ||
139 | * | ||
140 | * If not set, the classic stack will be used. This is compatbile with | ||
141 | * all older versions. | ||
142 | */ | ||
143 | #define OCFS2_FEATURE_INCOMPAT_USERSPACE_STACK 0x0080 | ||
144 | |||
145 | /* | ||
134 | * backup superblock flag is used to indicate that this volume | 146 | * backup superblock flag is used to indicate that this volume |
135 | * has backup superblocks. | 147 | * has backup superblocks. |
136 | */ | 148 | */ |
@@ -272,6 +284,10 @@ struct ocfs2_new_group_input { | |||
272 | #define OCFS2_VOL_UUID_LEN 16 | 284 | #define OCFS2_VOL_UUID_LEN 16 |
273 | #define OCFS2_MAX_VOL_LABEL_LEN 64 | 285 | #define OCFS2_MAX_VOL_LABEL_LEN 64 |
274 | 286 | ||
287 | /* The alternate, userspace stack fields */ | ||
288 | #define OCFS2_STACK_LABEL_LEN 4 | ||
289 | #define OCFS2_CLUSTER_NAME_LEN 16 | ||
290 | |||
275 | /* Journal limits (in bytes) */ | 291 | /* Journal limits (in bytes) */ |
276 | #define OCFS2_MIN_JOURNAL_SIZE (4 * 1024 * 1024) | 292 | #define OCFS2_MIN_JOURNAL_SIZE (4 * 1024 * 1024) |
277 | 293 | ||
@@ -513,6 +529,13 @@ struct ocfs2_slot_map_extended { | |||
513 | */ | 529 | */ |
514 | }; | 530 | }; |
515 | 531 | ||
532 | struct ocfs2_cluster_info { | ||
533 | /*00*/ __u8 ci_stack[OCFS2_STACK_LABEL_LEN]; | ||
534 | __le32 ci_reserved; | ||
535 | /*08*/ __u8 ci_cluster[OCFS2_CLUSTER_NAME_LEN]; | ||
536 | /*18*/ | ||
537 | }; | ||
538 | |||
516 | /* | 539 | /* |
517 | * On disk superblock for OCFS2 | 540 | * On disk superblock for OCFS2 |
518 | * Note that it is contained inside an ocfs2_dinode, so all offsets | 541 | * Note that it is contained inside an ocfs2_dinode, so all offsets |
@@ -545,7 +568,20 @@ struct ocfs2_super_block { | |||
545 | * group header */ | 568 | * group header */ |
546 | /*50*/ __u8 s_label[OCFS2_MAX_VOL_LABEL_LEN]; /* Label for mounting, etc. */ | 569 | /*50*/ __u8 s_label[OCFS2_MAX_VOL_LABEL_LEN]; /* Label for mounting, etc. */ |
547 | /*90*/ __u8 s_uuid[OCFS2_VOL_UUID_LEN]; /* 128-bit uuid */ | 570 | /*90*/ __u8 s_uuid[OCFS2_VOL_UUID_LEN]; /* 128-bit uuid */ |
548 | /*A0*/ | 571 | /*A0*/ struct ocfs2_cluster_info s_cluster_info; /* Selected userspace |
572 | stack. Only valid | ||
573 | with INCOMPAT flag. */ | ||
574 | /*B8*/ __le64 s_reserved2[17]; /* Fill out superblock */ | ||
575 | /*140*/ | ||
576 | |||
577 | /* | ||
578 | * NOTE: As stated above, all offsets are relative to | ||
579 | * ocfs2_dinode.id2, which is at 0xC0 in the inode. | ||
580 | * 0xC0 + 0x140 = 0x200 or 512 bytes. A superblock must fit within | ||
581 | * our smallest blocksize, which is 512 bytes. To ensure this, | ||
582 | * we reserve the space in s_reserved2. Anything past s_reserved2 | ||
583 | * will not be available on the smallest blocksize. | ||
584 | */ | ||
549 | }; | 585 | }; |
550 | 586 | ||
551 | /* | 587 | /* |
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index e27a0d47ea2b..96ebe36d5d77 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c | |||
@@ -87,6 +87,7 @@ struct mount_options | |||
87 | unsigned int atime_quantum; | 87 | unsigned int atime_quantum; |
88 | signed short slot; | 88 | signed short slot; |
89 | unsigned int localalloc_opt; | 89 | unsigned int localalloc_opt; |
90 | char cluster_stack[OCFS2_STACK_LABEL_LEN + 1]; | ||
90 | }; | 91 | }; |
91 | 92 | ||
92 | static int ocfs2_parse_options(struct super_block *sb, char *options, | 93 | static int ocfs2_parse_options(struct super_block *sb, char *options, |
@@ -152,6 +153,7 @@ enum { | |||
152 | Opt_commit, | 153 | Opt_commit, |
153 | Opt_localalloc, | 154 | Opt_localalloc, |
154 | Opt_localflocks, | 155 | Opt_localflocks, |
156 | Opt_stack, | ||
155 | Opt_err, | 157 | Opt_err, |
156 | }; | 158 | }; |
157 | 159 | ||
@@ -170,6 +172,7 @@ static match_table_t tokens = { | |||
170 | {Opt_commit, "commit=%u"}, | 172 | {Opt_commit, "commit=%u"}, |
171 | {Opt_localalloc, "localalloc=%d"}, | 173 | {Opt_localalloc, "localalloc=%d"}, |
172 | {Opt_localflocks, "localflocks"}, | 174 | {Opt_localflocks, "localflocks"}, |
175 | {Opt_stack, "cluster_stack=%s"}, | ||
173 | {Opt_err, NULL} | 176 | {Opt_err, NULL} |
174 | }; | 177 | }; |
175 | 178 | ||
@@ -549,8 +552,17 @@ static int ocfs2_verify_heartbeat(struct ocfs2_super *osb) | |||
549 | } | 552 | } |
550 | } | 553 | } |
551 | 554 | ||
555 | if (ocfs2_userspace_stack(osb)) { | ||
556 | if (osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL) { | ||
557 | mlog(ML_ERROR, "Userspace stack expected, but " | ||
558 | "o2cb heartbeat arguments passed to mount\n"); | ||
559 | return -EINVAL; | ||
560 | } | ||
561 | } | ||
562 | |||
552 | if (!(osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL)) { | 563 | if (!(osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL)) { |
553 | if (!ocfs2_mount_local(osb) && !ocfs2_is_hard_readonly(osb)) { | 564 | if (!ocfs2_mount_local(osb) && !ocfs2_is_hard_readonly(osb) && |
565 | !ocfs2_userspace_stack(osb)) { | ||
554 | mlog(ML_ERROR, "Heartbeat has to be started to mount " | 566 | mlog(ML_ERROR, "Heartbeat has to be started to mount " |
555 | "a read-write clustered device.\n"); | 567 | "a read-write clustered device.\n"); |
556 | return -EINVAL; | 568 | return -EINVAL; |
@@ -560,6 +572,35 @@ static int ocfs2_verify_heartbeat(struct ocfs2_super *osb) | |||
560 | return 0; | 572 | return 0; |
561 | } | 573 | } |
562 | 574 | ||
575 | /* | ||
576 | * If we're using a userspace stack, mount should have passed | ||
577 | * a name that matches the disk. If not, mount should not | ||
578 | * have passed a stack. | ||
579 | */ | ||
580 | static int ocfs2_verify_userspace_stack(struct ocfs2_super *osb, | ||
581 | struct mount_options *mopt) | ||
582 | { | ||
583 | if (!ocfs2_userspace_stack(osb) && mopt->cluster_stack[0]) { | ||
584 | mlog(ML_ERROR, | ||
585 | "cluster stack passed to mount, but this filesystem " | ||
586 | "does not support it\n"); | ||
587 | return -EINVAL; | ||
588 | } | ||
589 | |||
590 | if (ocfs2_userspace_stack(osb) && | ||
591 | strncmp(osb->osb_cluster_stack, mopt->cluster_stack, | ||
592 | OCFS2_STACK_LABEL_LEN)) { | ||
593 | mlog(ML_ERROR, | ||
594 | "cluster stack passed to mount (\"%s\") does not " | ||
595 | "match the filesystem (\"%s\")\n", | ||
596 | mopt->cluster_stack, | ||
597 | osb->osb_cluster_stack); | ||
598 | return -EINVAL; | ||
599 | } | ||
600 | |||
601 | return 0; | ||
602 | } | ||
603 | |||
563 | static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) | 604 | static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) |
564 | { | 605 | { |
565 | struct dentry *root; | 606 | struct dentry *root; |
@@ -598,6 +639,10 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) | |||
598 | osb->osb_commit_interval = parsed_options.commit_interval; | 639 | osb->osb_commit_interval = parsed_options.commit_interval; |
599 | osb->local_alloc_size = parsed_options.localalloc_opt; | 640 | osb->local_alloc_size = parsed_options.localalloc_opt; |
600 | 641 | ||
642 | status = ocfs2_verify_userspace_stack(osb, &parsed_options); | ||
643 | if (status) | ||
644 | goto read_super_error; | ||
645 | |||
601 | sb->s_magic = OCFS2_SUPER_MAGIC; | 646 | sb->s_magic = OCFS2_SUPER_MAGIC; |
602 | 647 | ||
603 | /* Hard readonly mode only if: bdev_read_only, MS_RDONLY, | 648 | /* Hard readonly mode only if: bdev_read_only, MS_RDONLY, |
@@ -752,6 +797,7 @@ static int ocfs2_parse_options(struct super_block *sb, | |||
752 | mopt->atime_quantum = OCFS2_DEFAULT_ATIME_QUANTUM; | 797 | mopt->atime_quantum = OCFS2_DEFAULT_ATIME_QUANTUM; |
753 | mopt->slot = OCFS2_INVALID_SLOT; | 798 | mopt->slot = OCFS2_INVALID_SLOT; |
754 | mopt->localalloc_opt = OCFS2_DEFAULT_LOCAL_ALLOC_SIZE; | 799 | mopt->localalloc_opt = OCFS2_DEFAULT_LOCAL_ALLOC_SIZE; |
800 | mopt->cluster_stack[0] = '\0'; | ||
755 | 801 | ||
756 | if (!options) { | 802 | if (!options) { |
757 | status = 1; | 803 | status = 1; |
@@ -853,6 +899,25 @@ static int ocfs2_parse_options(struct super_block *sb, | |||
853 | if (!is_remount) | 899 | if (!is_remount) |
854 | mopt->mount_opt |= OCFS2_MOUNT_LOCALFLOCKS; | 900 | mopt->mount_opt |= OCFS2_MOUNT_LOCALFLOCKS; |
855 | break; | 901 | break; |
902 | case Opt_stack: | ||
903 | /* Check both that the option we were passed | ||
904 | * is of the right length and that it is a proper | ||
905 | * string of the right length. | ||
906 | */ | ||
907 | if (((args[0].to - args[0].from) != | ||
908 | OCFS2_STACK_LABEL_LEN) || | ||
909 | (strnlen(args[0].from, | ||
910 | OCFS2_STACK_LABEL_LEN) != | ||
911 | OCFS2_STACK_LABEL_LEN)) { | ||
912 | mlog(ML_ERROR, | ||
913 | "Invalid cluster_stack option\n"); | ||
914 | status = 0; | ||
915 | goto bail; | ||
916 | } | ||
917 | memcpy(mopt->cluster_stack, args[0].from, | ||
918 | OCFS2_STACK_LABEL_LEN); | ||
919 | mopt->cluster_stack[OCFS2_STACK_LABEL_LEN] = '\0'; | ||
920 | break; | ||
856 | default: | 921 | default: |
857 | mlog(ML_ERROR, | 922 | mlog(ML_ERROR, |
858 | "Unrecognized mount option \"%s\" " | 923 | "Unrecognized mount option \"%s\" " |
@@ -911,6 +976,10 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt) | |||
911 | if (opts & OCFS2_MOUNT_LOCALFLOCKS) | 976 | if (opts & OCFS2_MOUNT_LOCALFLOCKS) |
912 | seq_printf(s, ",localflocks,"); | 977 | seq_printf(s, ",localflocks,"); |
913 | 978 | ||
979 | if (osb->osb_cluster_stack[0]) | ||
980 | seq_printf(s, ",cluster_stack=%.*s", OCFS2_STACK_LABEL_LEN, | ||
981 | osb->osb_cluster_stack); | ||
982 | |||
914 | return 0; | 983 | return 0; |
915 | } | 984 | } |
916 | 985 | ||
@@ -1403,6 +1472,25 @@ static int ocfs2_initialize_super(struct super_block *sb, | |||
1403 | goto bail; | 1472 | goto bail; |
1404 | } | 1473 | } |
1405 | 1474 | ||
1475 | if (ocfs2_userspace_stack(osb)) { | ||
1476 | memcpy(osb->osb_cluster_stack, | ||
1477 | OCFS2_RAW_SB(di)->s_cluster_info.ci_stack, | ||
1478 | OCFS2_STACK_LABEL_LEN); | ||
1479 | osb->osb_cluster_stack[OCFS2_STACK_LABEL_LEN] = '\0'; | ||
1480 | if (strlen(osb->osb_cluster_stack) != OCFS2_STACK_LABEL_LEN) { | ||
1481 | mlog(ML_ERROR, | ||
1482 | "couldn't mount because of an invalid " | ||
1483 | "cluster stack label (%s) \n", | ||
1484 | osb->osb_cluster_stack); | ||
1485 | status = -EINVAL; | ||
1486 | goto bail; | ||
1487 | } | ||
1488 | } else { | ||
1489 | /* The empty string is identical with classic tools that | ||
1490 | * don't know about s_cluster_info. */ | ||
1491 | osb->osb_cluster_stack[0] = '\0'; | ||
1492 | } | ||
1493 | |||
1406 | get_random_bytes(&osb->s_next_generation, sizeof(u32)); | 1494 | get_random_bytes(&osb->s_next_generation, sizeof(u32)); |
1407 | 1495 | ||
1408 | /* FIXME | 1496 | /* FIXME |