ext4: enforce bigalloc restrictions (e.g., no online resizing, etc.)
[pandora-kernel.git] / fs / ext4 / super.c
index 4687fea..25a4bfe 100644 (file)
@@ -414,6 +414,22 @@ static void save_error_info(struct super_block *sb, const char *func,
        ext4_commit_super(sb, 1);
 }
 
+/*
+ * The del_gendisk() function uninitializes the disk-specific data
+ * structures, including the bdi structure, without telling anyone
+ * else.  Once this happens, any attempt to call mark_buffer_dirty()
+ * (for example, by ext4_commit_super), will cause a kernel OOPS.
+ * This is a kludge to prevent these oops until we can put in a proper
+ * hook in del_gendisk() to inform the VFS and file system layers.
+ */
+static int block_device_ejected(struct super_block *sb)
+{
+       struct inode *bd_inode = sb->s_bdev->bd_inode;
+       struct backing_dev_info *bdi = bd_inode->i_mapping->backing_dev_info;
+
+       return bdi->dev == NULL;
+}
+
 
 /* Deal with the reporting of failure conditions on a filesystem such as
  * inconsistencies detected or read IO failures.
@@ -919,7 +935,6 @@ static void ext4_i_callback(struct rcu_head *head)
 
 static void ext4_destroy_inode(struct inode *inode)
 {
-       ext4_ioend_wait(inode);
        if (!list_empty(&(EXT4_I(inode)->i_orphan))) {
                ext4_msg(inode->i_sb, KERN_ERR,
                         "Inode %lu (%p): orphan list check failed!",
@@ -1802,6 +1817,7 @@ set_qf_format:
                        break;
                case Opt_nodelalloc:
                        clear_opt(sb, DELALLOC);
+                       clear_opt2(sb, EXPLICIT_DELALLOC);
                        break;
                case Opt_mblk_io_submit:
                        set_opt(sb, MBLK_IO_SUBMIT);
@@ -1818,6 +1834,7 @@ set_qf_format:
                        break;
                case Opt_delalloc:
                        set_opt(sb, DELALLOC);
+                       set_opt2(sb, EXPLICIT_DELALLOC);
                        break;
                case Opt_block_validity:
                        set_opt(sb, BLOCK_VALIDITY);
@@ -1936,7 +1953,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,
                res = MS_RDONLY;
        }
        if (read_only)
-               return res;
+               goto done;
        if (!(sbi->s_mount_state & EXT4_VALID_FS))
                ext4_msg(sb, KERN_WARNING, "warning: mounting unchecked fs, "
                         "running e2fsck is recommended");
@@ -1967,6 +1984,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,
                EXT4_SET_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER);
 
        ext4_commit_super(sb, 1);
+done:
        if (test_opt(sb, DEBUG))
                printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, "
                                "bpg=%lu, ipg=%lu, mo=%04x, mo2=%04x]\n",
@@ -2683,6 +2701,13 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly)
                        return 0;
                }
        }
+       if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_BIGALLOC) &&
+           !EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_EXTENTS)) {
+               ext4_msg(sb, KERN_ERR,
+                        "Can't support bigalloc feature without "
+                        "extents feature\n");
+               return 0;
+       }
        return 1;
 }
 
@@ -3088,10 +3113,10 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        char *cp;
        const char *descr;
        int ret = -ENOMEM;
-       int blocksize;
+       int blocksize, clustersize;
        unsigned int db_count;
        unsigned int i;
-       int needs_recovery, has_huge_files;
+       int needs_recovery, has_huge_files, has_bigalloc;
        __u64 blocks_count;
        int err;
        unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
@@ -3225,6 +3250,33 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                           &journal_ioprio, NULL, 0))
                goto failed_mount;
 
+       if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) {
+               printk_once(KERN_WARNING "EXT4-fs: Warning: mounting "
+                           "with data=journal disables delayed "
+                           "allocation and O_DIRECT support!\n");
+               if (test_opt2(sb, EXPLICIT_DELALLOC)) {
+                       ext4_msg(sb, KERN_ERR, "can't mount with "
+                                "both data=journal and delalloc");
+                       goto failed_mount;
+               }
+               if (test_opt(sb, DIOREAD_NOLOCK)) {
+                       ext4_msg(sb, KERN_ERR, "can't mount with "
+                                "both data=journal and delalloc");
+                       goto failed_mount;
+               }
+               if (test_opt(sb, DELALLOC))
+                       clear_opt(sb, DELALLOC);
+       }
+
+       blocksize = BLOCK_SIZE << le32_to_cpu(es->s_log_block_size);
+       if (test_opt(sb, DIOREAD_NOLOCK)) {
+               if (blocksize < PAGE_SIZE) {
+                       ext4_msg(sb, KERN_ERR, "can't mount with "
+                                "dioread_nolock if block size != PAGE_SIZE");
+                       goto failed_mount;
+               }
+       }
+
        sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
                (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);
 
@@ -3266,8 +3318,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        if (!ext4_feature_set_ok(sb, (sb->s_flags & MS_RDONLY)))
                goto failed_mount;
 
-       blocksize = BLOCK_SIZE << le32_to_cpu(es->s_log_block_size);
-
        if (blocksize < EXT4_MIN_BLOCK_SIZE ||
            blocksize > EXT4_MAX_BLOCK_SIZE) {
                ext4_msg(sb, KERN_ERR,
@@ -3370,12 +3420,53 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                sb->s_dirt = 1;
        }
 
-       if (sbi->s_blocks_per_group > blocksize * 8) {
-               ext4_msg(sb, KERN_ERR,
-                      "#blocks per group too big: %lu",
-                      sbi->s_blocks_per_group);
-               goto failed_mount;
+       /* Handle clustersize */
+       clustersize = BLOCK_SIZE << le32_to_cpu(es->s_log_cluster_size);
+       has_bigalloc = EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                               EXT4_FEATURE_RO_COMPAT_BIGALLOC);
+       if (has_bigalloc) {
+               if (clustersize < blocksize) {
+                       ext4_msg(sb, KERN_ERR,
+                                "cluster size (%d) smaller than "
+                                "block size (%d)", clustersize, blocksize);
+                       goto failed_mount;
+               }
+               sbi->s_cluster_bits = le32_to_cpu(es->s_log_cluster_size) -
+                       le32_to_cpu(es->s_log_block_size);
+               sbi->s_clusters_per_group =
+                       le32_to_cpu(es->s_clusters_per_group);
+               if (sbi->s_clusters_per_group > blocksize * 8) {
+                       ext4_msg(sb, KERN_ERR,
+                                "#clusters per group too big: %lu",
+                                sbi->s_clusters_per_group);
+                       goto failed_mount;
+               }
+               if (sbi->s_blocks_per_group !=
+                   (sbi->s_clusters_per_group * (clustersize / blocksize))) {
+                       ext4_msg(sb, KERN_ERR, "blocks per group (%lu) and "
+                                "clusters per group (%lu) inconsistent",
+                                sbi->s_blocks_per_group,
+                                sbi->s_clusters_per_group);
+                       goto failed_mount;
+               }
+       } else {
+               if (clustersize != blocksize) {
+                       ext4_warning(sb, "fragment/cluster size (%d) != "
+                                    "block size (%d)", clustersize,
+                                    blocksize);
+                       clustersize = blocksize;
+               }
+               if (sbi->s_blocks_per_group > blocksize * 8) {
+                       ext4_msg(sb, KERN_ERR,
+                                "#blocks per group too big: %lu",
+                                sbi->s_blocks_per_group);
+                       goto failed_mount;
+               }
+               sbi->s_clusters_per_group = sbi->s_blocks_per_group;
+               sbi->s_cluster_bits = 0;
        }
+       sbi->s_cluster_ratio = clustersize / blocksize;
+
        if (sbi->s_inodes_per_group > blocksize * 8) {
                ext4_msg(sb, KERN_ERR,
                       "#inodes per group too big: %lu",
@@ -3680,25 +3771,6 @@ no_journal:
                         "available");
        }
 
-       if (test_opt(sb, DELALLOC) &&
-           (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)) {
-               ext4_msg(sb, KERN_WARNING, "Ignoring delalloc option - "
-                        "requested data journaling mode");
-               clear_opt(sb, DELALLOC);
-       }
-       if (test_opt(sb, DIOREAD_NOLOCK)) {
-               if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) {
-                       ext4_msg(sb, KERN_WARNING, "Ignoring dioread_nolock "
-                               "option - requested data journaling mode");
-                       clear_opt(sb, DIOREAD_NOLOCK);
-               }
-               if (sb->s_blocksize < PAGE_SIZE) {
-                       ext4_msg(sb, KERN_WARNING, "Ignoring dioread_nolock "
-                               "option - block size is too small");
-                       clear_opt(sb, DIOREAD_NOLOCK);
-               }
-       }
-
        err = ext4_setup_system_zone(sb);
        if (err) {
                ext4_msg(sb, KERN_ERR, "failed to initialize system "
@@ -4065,7 +4137,7 @@ static int ext4_commit_super(struct super_block *sb, int sync)
        struct buffer_head *sbh = EXT4_SB(sb)->s_sbh;
        int error = 0;
 
-       if (!sbh)
+       if (!sbh || block_device_ejected(sb))
                return error;
        if (buffer_write_io_error(sbh)) {
                /*