Commit b61817e1166c5e19c08baf05196477cc345e1b1a
Committed by
Mark Fasheh
1 parent
74ae4e104d
Exists in
master
and in
7 other branches
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>
Showing 3 changed files with 134 additions and 3 deletions Side-by-side Diff
fs/ocfs2/ocfs2.h
... | ... | @@ -248,6 +248,7 @@ |
248 | 248 | struct ocfs2_alloc_stats alloc_stats; |
249 | 249 | char dev_str[20]; /* "major,minor" of the device */ |
250 | 250 | |
251 | + char osb_cluster_stack[OCFS2_STACK_LABEL_LEN + 1]; | |
251 | 252 | struct ocfs2_cluster_connection *cconn; |
252 | 253 | struct ocfs2_lock_res osb_super_lockres; |
253 | 254 | struct ocfs2_lock_res osb_rename_lockres; |
... | ... | @@ -366,6 +367,12 @@ |
366 | 367 | spin_unlock(&osb->osb_lock); |
367 | 368 | |
368 | 369 | return ret; |
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); | |
369 | 376 | } |
370 | 377 | |
371 | 378 | static inline int ocfs2_mount_local(struct ocfs2_super *osb) |
fs/ocfs2/ocfs2_fs.h
... | ... | @@ -89,7 +89,8 @@ |
89 | 89 | #define OCFS2_FEATURE_INCOMPAT_SUPP (OCFS2_FEATURE_INCOMPAT_LOCAL_MOUNT \ |
90 | 90 | | OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC \ |
91 | 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 | 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 | 146 | * backup superblock flag is used to indicate that this volume |
135 | 147 | * has backup superblocks. |
136 | 148 | */ |
... | ... | @@ -272,6 +284,10 @@ |
272 | 284 | #define OCFS2_VOL_UUID_LEN 16 |
273 | 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 | 291 | /* Journal limits (in bytes) */ |
276 | 292 | #define OCFS2_MIN_JOURNAL_SIZE (4 * 1024 * 1024) |
277 | 293 | |
... | ... | @@ -513,6 +529,13 @@ |
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 | 540 | * On disk superblock for OCFS2 |
518 | 541 | * Note that it is contained inside an ocfs2_dinode, so all offsets |
... | ... | @@ -545,7 +568,20 @@ |
545 | 568 | * group header */ |
546 | 569 | /*50*/ __u8 s_label[OCFS2_MAX_VOL_LABEL_LEN]; /* Label for mounting, etc. */ |
547 | 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 | /* |
fs/ocfs2/super.c
... | ... | @@ -87,6 +87,7 @@ |
87 | 87 | unsigned int atime_quantum; |
88 | 88 | signed short slot; |
89 | 89 | unsigned int localalloc_opt; |
90 | + char cluster_stack[OCFS2_STACK_LABEL_LEN + 1]; | |
90 | 91 | }; |
91 | 92 | |
92 | 93 | static int ocfs2_parse_options(struct super_block *sb, char *options, |
... | ... | @@ -152,6 +153,7 @@ |
152 | 153 | Opt_commit, |
153 | 154 | Opt_localalloc, |
154 | 155 | Opt_localflocks, |
156 | + Opt_stack, | |
155 | 157 | Opt_err, |
156 | 158 | }; |
157 | 159 | |
... | ... | @@ -170,6 +172,7 @@ |
170 | 172 | {Opt_commit, "commit=%u"}, |
171 | 173 | {Opt_localalloc, "localalloc=%d"}, |
172 | 174 | {Opt_localflocks, "localflocks"}, |
175 | + {Opt_stack, "cluster_stack=%s"}, | |
173 | 176 | {Opt_err, NULL} |
174 | 177 | }; |
175 | 178 | |
176 | 179 | |
... | ... | @@ -549,8 +552,17 @@ |
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 | 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 | 566 | mlog(ML_ERROR, "Heartbeat has to be started to mount " |
555 | 567 | "a read-write clustered device.\n"); |
556 | 568 | return -EINVAL; |
... | ... | @@ -560,6 +572,35 @@ |
560 | 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 | 604 | static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) |
564 | 605 | { |
565 | 606 | struct dentry *root; |
... | ... | @@ -598,6 +639,10 @@ |
598 | 639 | osb->osb_commit_interval = parsed_options.commit_interval; |
599 | 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 | 646 | sb->s_magic = OCFS2_SUPER_MAGIC; |
602 | 647 | |
603 | 648 | /* Hard readonly mode only if: bdev_read_only, MS_RDONLY, |
... | ... | @@ -752,6 +797,7 @@ |
752 | 797 | mopt->atime_quantum = OCFS2_DEFAULT_ATIME_QUANTUM; |
753 | 798 | mopt->slot = OCFS2_INVALID_SLOT; |
754 | 799 | mopt->localalloc_opt = OCFS2_DEFAULT_LOCAL_ALLOC_SIZE; |
800 | + mopt->cluster_stack[0] = '\0'; | |
755 | 801 | |
756 | 802 | if (!options) { |
757 | 803 | status = 1; |
... | ... | @@ -853,6 +899,25 @@ |
853 | 899 | if (!is_remount) |
854 | 900 | mopt->mount_opt |= OCFS2_MOUNT_LOCALFLOCKS; |
855 | 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 | 921 | default: |
857 | 922 | mlog(ML_ERROR, |
858 | 923 | "Unrecognized mount option \"%s\" " |
... | ... | @@ -911,6 +976,10 @@ |
911 | 976 | if (opts & OCFS2_MOUNT_LOCALFLOCKS) |
912 | 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 | 983 | return 0; |
915 | 984 | } |
916 | 985 | |
... | ... | @@ -1401,6 +1470,25 @@ |
1401 | 1470 | "unsupported optional features (%x).\n", i); |
1402 | 1471 | status = -EINVAL; |
1403 | 1472 | goto bail; |
1473 | + } | |
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'; | |
1404 | 1492 | } |
1405 | 1493 | |
1406 | 1494 | get_random_bytes(&osb->s_next_generation, sizeof(u32)); |