Merge branch 'vfs' of git://git.kernel.org/pub/scm/linux/kernel/git/arnd/bkl
[linux-3.10.git] / fs / ocfs2 / super.c
index b7d7243..56f0cb3 100644 (file)
@@ -162,6 +162,7 @@ enum {
        Opt_nointr,
        Opt_hb_none,
        Opt_hb_local,
+       Opt_hb_global,
        Opt_data_ordered,
        Opt_data_writeback,
        Opt_atime_quantum,
@@ -177,6 +178,8 @@ enum {
        Opt_noacl,
        Opt_usrquota,
        Opt_grpquota,
+       Opt_coherency_buffered,
+       Opt_coherency_full,
        Opt_resv_level,
        Opt_dir_resv_level,
        Opt_err,
@@ -190,6 +193,7 @@ static const match_table_t tokens = {
        {Opt_nointr, "nointr"},
        {Opt_hb_none, OCFS2_HB_NONE},
        {Opt_hb_local, OCFS2_HB_LOCAL},
+       {Opt_hb_global, OCFS2_HB_GLOBAL},
        {Opt_data_ordered, "data=ordered"},
        {Opt_data_writeback, "data=writeback"},
        {Opt_atime_quantum, "atime_quantum=%u"},
@@ -205,6 +209,8 @@ static const match_table_t tokens = {
        {Opt_noacl, "noacl"},
        {Opt_usrquota, "usrquota"},
        {Opt_grpquota, "grpquota"},
+       {Opt_coherency_buffered, "coherency=buffered"},
+       {Opt_coherency_full, "coherency=full"},
        {Opt_resv_level, "resv_level=%u"},
        {Opt_dir_resv_level, "dir_resv_level=%u"},
        {Opt_err, NULL}
@@ -514,11 +520,11 @@ static void ocfs2_release_system_inodes(struct ocfs2_super *osb)
 
        mlog_entry_void();
 
-       for (i = 0; i < NUM_SYSTEM_INODES; i++) {
-               inode = osb->system_inodes[i];
+       for (i = 0; i < NUM_GLOBAL_SYSTEM_INODES; i++) {
+               inode = osb->global_system_inodes[i];
                if (inode) {
                        iput(inode);
-                       osb->system_inodes[i] = NULL;
+                       osb->global_system_inodes[i] = NULL;
                }
        }
 
@@ -534,6 +540,20 @@ static void ocfs2_release_system_inodes(struct ocfs2_super *osb)
                osb->root_inode = NULL;
        }
 
+       if (!osb->local_system_inodes)
+               goto out;
+
+       for (i = 0; i < NUM_LOCAL_SYSTEM_INODES * osb->max_slots; i++) {
+               if (osb->local_system_inodes[i]) {
+                       iput(osb->local_system_inodes[i]);
+                       osb->local_system_inodes[i] = NULL;
+               }
+       }
+
+       kfree(osb->local_system_inodes);
+       osb->local_system_inodes = NULL;
+
+out:
        mlog_exit(0);
 }
 
@@ -608,6 +628,7 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data)
        int ret = 0;
        struct mount_options parsed_options;
        struct ocfs2_super *osb = OCFS2_SB(sb);
+       u32 tmp;
 
        if (!ocfs2_parse_options(sb, data, &parsed_options, 1) ||
            !ocfs2_check_set_options(sb, &parsed_options)) {
@@ -615,8 +636,9 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data)
                goto out;
        }
 
-       if ((osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL) !=
-           (parsed_options.mount_opt & OCFS2_MOUNT_HB_LOCAL)) {
+       tmp = OCFS2_MOUNT_HB_LOCAL | OCFS2_MOUNT_HB_GLOBAL |
+               OCFS2_MOUNT_HB_NONE;
+       if ((osb->s_mount_opt & tmp) != (parsed_options.mount_opt & tmp)) {
                ret = -EINVAL;
                mlog(ML_ERROR, "Cannot change heartbeat mode on remount\n");
                goto out;
@@ -806,23 +828,29 @@ bail:
 
 static int ocfs2_verify_heartbeat(struct ocfs2_super *osb)
 {
-       if (ocfs2_mount_local(osb)) {
-               if (osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL) {
+       u32 hb_enabled = OCFS2_MOUNT_HB_LOCAL | OCFS2_MOUNT_HB_GLOBAL;
+
+       if (osb->s_mount_opt & hb_enabled) {
+               if (ocfs2_mount_local(osb)) {
                        mlog(ML_ERROR, "Cannot heartbeat on a locally "
                             "mounted device.\n");
                        return -EINVAL;
                }
-       }
-
-       if (ocfs2_userspace_stack(osb)) {
-               if (osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL) {
+               if (ocfs2_userspace_stack(osb)) {
                        mlog(ML_ERROR, "Userspace stack expected, but "
                             "o2cb heartbeat arguments passed to mount\n");
                        return -EINVAL;
                }
+               if (((osb->s_mount_opt & OCFS2_MOUNT_HB_GLOBAL) &&
+                    !ocfs2_cluster_o2cb_global_heartbeat(osb)) ||
+                   ((osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL) &&
+                    ocfs2_cluster_o2cb_global_heartbeat(osb))) {
+                       mlog(ML_ERROR, "Mismatching o2cb heartbeat modes\n");
+                       return -EINVAL;
+               }
        }
 
-       if (!(osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL)) {
+       if (!(osb->s_mount_opt & hb_enabled)) {
                if (!ocfs2_mount_local(osb) && !ocfs2_is_hard_readonly(osb) &&
                    !ocfs2_userspace_stack(osb)) {
                        mlog(ML_ERROR, "Heartbeat has to be started to mount "
@@ -1288,6 +1316,7 @@ static int ocfs2_parse_options(struct super_block *sb,
 {
        int status;
        char *p;
+       u32 tmp;
 
        mlog_entry("remount: %d, options: \"%s\"\n", is_remount,
                   options ? options : "(none)");
@@ -1319,7 +1348,10 @@ static int ocfs2_parse_options(struct super_block *sb,
                        mopt->mount_opt |= OCFS2_MOUNT_HB_LOCAL;
                        break;
                case Opt_hb_none:
-                       mopt->mount_opt &= ~OCFS2_MOUNT_HB_LOCAL;
+                       mopt->mount_opt |= OCFS2_MOUNT_HB_NONE;
+                       break;
+               case Opt_hb_global:
+                       mopt->mount_opt |= OCFS2_MOUNT_HB_GLOBAL;
                        break;
                case Opt_barrier:
                        if (match_int(&args[0], &option)) {
@@ -1435,6 +1467,12 @@ static int ocfs2_parse_options(struct super_block *sb,
                case Opt_grpquota:
                        mopt->mount_opt |= OCFS2_MOUNT_GRPQUOTA;
                        break;
+               case Opt_coherency_buffered:
+                       mopt->mount_opt |= OCFS2_MOUNT_COHERENCY_BUFFERED;
+                       break;
+               case Opt_coherency_full:
+                       mopt->mount_opt &= ~OCFS2_MOUNT_COHERENCY_BUFFERED;
+                       break;
                case Opt_acl:
                        mopt->mount_opt |= OCFS2_MOUNT_POSIX_ACL;
                        mopt->mount_opt &= ~OCFS2_MOUNT_NO_POSIX_ACL;
@@ -1474,6 +1512,15 @@ static int ocfs2_parse_options(struct super_block *sb,
                }
        }
 
+       /* Ensure only one heartbeat mode */
+       tmp = mopt->mount_opt & (OCFS2_MOUNT_HB_LOCAL | OCFS2_MOUNT_HB_GLOBAL |
+                                OCFS2_MOUNT_HB_NONE);
+       if (hweight32(tmp) != 1) {
+               mlog(ML_ERROR, "Invalid heartbeat mount options\n");
+               status = 0;
+               goto bail;
+       }
+
        status = 1;
 
 bail:
@@ -1487,10 +1534,14 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt)
        unsigned long opts = osb->s_mount_opt;
        unsigned int local_alloc_megs;
 
-       if (opts & OCFS2_MOUNT_HB_LOCAL)
-               seq_printf(s, ",_netdev,heartbeat=local");
-       else
-               seq_printf(s, ",heartbeat=none");
+       if (opts & (OCFS2_MOUNT_HB_LOCAL | OCFS2_MOUNT_HB_GLOBAL)) {
+               seq_printf(s, ",_netdev");
+               if (opts & OCFS2_MOUNT_HB_LOCAL)
+                       seq_printf(s, ",%s", OCFS2_HB_LOCAL);
+               else
+                       seq_printf(s, ",%s", OCFS2_HB_GLOBAL);
+       } else
+               seq_printf(s, ",%s", OCFS2_HB_NONE);
 
        if (opts & OCFS2_MOUNT_NOINTR)
                seq_printf(s, ",nointr");
@@ -1533,6 +1584,11 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt)
        if (opts & OCFS2_MOUNT_GRPQUOTA)
                seq_printf(s, ",grpquota");
 
+       if (opts & OCFS2_MOUNT_COHERENCY_BUFFERED)
+               seq_printf(s, ",coherency=buffered");
+       else
+               seq_printf(s, ",coherency=full");
+
        if (opts & OCFS2_MOUNT_NOUSERXATTR)
                seq_printf(s, ",nouser_xattr");
        else
@@ -1983,6 +2039,36 @@ static int ocfs2_setup_osb_uuid(struct ocfs2_super *osb, const unsigned char *uu
        return 0;
 }
 
+/* Make sure entire volume is addressable by our journal.  Requires
+   osb_clusters_at_boot to be valid and for the journal to have been
+   initialized by ocfs2_journal_init(). */
+static int ocfs2_journal_addressable(struct ocfs2_super *osb)
+{
+       int status = 0;
+       u64 max_block =
+               ocfs2_clusters_to_blocks(osb->sb,
+                                        osb->osb_clusters_at_boot) - 1;
+
+       /* 32-bit block number is always OK. */
+       if (max_block <= (u32)~0ULL)
+               goto out;
+
+       /* Volume is "huge", so see if our journal is new enough to
+          support it. */
+       if (!(OCFS2_HAS_COMPAT_FEATURE(osb->sb,
+                                      OCFS2_FEATURE_COMPAT_JBD2_SB) &&
+             jbd2_journal_check_used_features(osb->journal->j_journal, 0, 0,
+                                              JBD2_FEATURE_INCOMPAT_64BIT))) {
+               mlog(ML_ERROR, "The journal cannot address the entire volume. "
+                    "Enable the 'block64' journal option with tunefs.ocfs2");
+               status = -EFBIG;
+               goto out;
+       }
+
+ out:
+       return status;
+}
+
 static int ocfs2_initialize_super(struct super_block *sb,
                                  struct buffer_head *bh,
                                  int sector_size,
@@ -1995,6 +2081,7 @@ static int ocfs2_initialize_super(struct super_block *sb,
        struct ocfs2_journal *journal;
        __le32 uuid_net_key;
        struct ocfs2_super *osb;
+       u64 total_blocks;
 
        mlog_entry_void();
 
@@ -2053,6 +2140,15 @@ static int ocfs2_initialize_super(struct super_block *sb,
        snprintf(osb->dev_str, sizeof(osb->dev_str), "%u,%u",
                 MAJOR(osb->sb->s_dev), MINOR(osb->sb->s_dev));
 
+       osb->max_slots = le16_to_cpu(di->id2.i_super.s_max_slots);
+       if (osb->max_slots > OCFS2_MAX_SLOTS || osb->max_slots == 0) {
+               mlog(ML_ERROR, "Invalid number of node slots (%u)\n",
+                    osb->max_slots);
+               status = -EINVAL;
+               goto bail;
+       }
+       mlog(0, "max_slots for this device: %u\n", osb->max_slots);
+
        ocfs2_orphan_scan_init(osb);
 
        status = ocfs2_recovery_init(osb);
@@ -2091,15 +2187,6 @@ static int ocfs2_initialize_super(struct super_block *sb,
                goto bail;
        }
 
-       osb->max_slots = le16_to_cpu(di->id2.i_super.s_max_slots);
-       if (osb->max_slots > OCFS2_MAX_SLOTS || osb->max_slots == 0) {
-               mlog(ML_ERROR, "Invalid number of node slots (%u)\n",
-                    osb->max_slots);
-               status = -EINVAL;
-               goto bail;
-       }
-       mlog(0, "max_slots for this device: %u\n", osb->max_slots);
-
        osb->slot_recovery_generations =
                kcalloc(osb->max_slots, sizeof(*osb->slot_recovery_generations),
                        GFP_KERNEL);
@@ -2142,7 +2229,9 @@ static int ocfs2_initialize_super(struct super_block *sb,
                goto bail;
        }
 
-       if (ocfs2_userspace_stack(osb)) {
+       if (ocfs2_clusterinfo_valid(osb)) {
+               osb->osb_stackflags =
+                       OCFS2_RAW_SB(di)->s_cluster_info.ci_stackflags;
                memcpy(osb->osb_cluster_stack,
                       OCFS2_RAW_SB(di)->s_cluster_info.ci_stack,
                       OCFS2_STACK_LABEL_LEN);
@@ -2207,11 +2296,15 @@ static int ocfs2_initialize_super(struct super_block *sb,
                goto bail;
        }
 
-       if (ocfs2_clusters_to_blocks(osb->sb, le32_to_cpu(di->i_clusters) - 1)
-           > (u32)~0UL) {
-               mlog(ML_ERROR, "Volume might try to write to blocks beyond "
-                    "what jbd can address in 32 bits.\n");
-               status = -EINVAL;
+       total_blocks = ocfs2_clusters_to_blocks(osb->sb,
+                                               le32_to_cpu(di->i_clusters));
+
+       status = generic_check_addressable(osb->sb->s_blocksize_bits,
+                                          total_blocks);
+       if (status) {
+               mlog(ML_ERROR, "Volume too large "
+                    "to mount safely on this system");
+               status = -EFBIG;
                goto bail;
        }
 
@@ -2373,6 +2466,12 @@ static int ocfs2_check_volume(struct ocfs2_super *osb)
                goto finally;
        }
 
+       /* Now that journal has been initialized, check to make sure
+          entire volume is addressable. */
+       status = ocfs2_journal_addressable(osb);
+       if (status)
+               goto finally;
+
        /* If the journal was unmounted cleanly then we don't want to
         * recover anything. Otherwise, journal_load will do that
         * dirty work for us :) */