ext4: Avoid updating the inode table bh twice in no journal mode
[safe/jmp/linux-2.6] / fs / ext4 / super.c
index 4c364ae..f095c60 100644 (file)
@@ -45,6 +45,7 @@
 #include "ext4_jbd2.h"
 #include "xattr.h"
 #include "acl.h"
+#include "mballoc.h"
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/ext4.h>
@@ -303,7 +304,7 @@ static void ext4_handle_error(struct super_block *sb)
        if (!test_opt(sb, ERRORS_CONT)) {
                journal_t *journal = EXT4_SB(sb)->s_journal;
 
-               EXT4_SB(sb)->s_mount_opt |= EXT4_MOUNT_ABORT;
+               EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FS_ABORTED;
                if (journal)
                        jbd2_journal_abort(journal, -EIO);
        }
@@ -344,7 +345,8 @@ static const char *ext4_decode_error(struct super_block *sb, int errno,
                errstr = "Out of memory";
                break;
        case -EROFS:
-               if (!sb || EXT4_SB(sb)->s_journal->j_flags & JBD2_ABORT)
+               if (!sb || (EXT4_SB(sb)->s_journal &&
+                           EXT4_SB(sb)->s_journal->j_flags & JBD2_ABORT))
                        errstr = "Journal has aborted";
                else
                        errstr = "Readonly filesystem";
@@ -416,7 +418,7 @@ void ext4_abort(struct super_block *sb, const char *function,
        ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
        EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
        sb->s_flags |= MS_RDONLY;
-       EXT4_SB(sb)->s_mount_opt |= EXT4_MOUNT_ABORT;
+       EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FS_ABORTED;
        if (EXT4_SB(sb)->s_journal)
                jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO);
 }
@@ -578,6 +580,9 @@ static void ext4_put_super(struct super_block *sb)
        struct ext4_super_block *es = sbi->s_es;
        int i, err;
 
+       flush_workqueue(sbi->dio_unwritten_wq);
+       destroy_workqueue(sbi->dio_unwritten_wq);
+
        lock_super(sb);
        lock_kernel();
        if (sb->s_dirt)
@@ -666,10 +671,6 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
        if (!ei)
                return NULL;
 
-#ifdef CONFIG_EXT4_FS_POSIX_ACL
-       ei->i_acl = EXT4_ACL_NOT_CACHED;
-       ei->i_default_acl = EXT4_ACL_NOT_CACHED;
-#endif
        ei->vfs_inode.i_version = 1;
        ei->vfs_inode.i_data.writeback_index = 0;
        memset(&ei->i_cached_extent, 0, sizeof(struct ext4_ext_cache));
@@ -686,6 +687,8 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
        ei->i_allocated_meta_blocks = 0;
        ei->i_delalloc_reserved_flag = 0;
        spin_lock_init(&(ei->i_block_reservation_lock));
+       INIT_LIST_HEAD(&ei->i_aio_dio_complete_list);
+       ei->cur_aio_dio = NULL;
 
        return &ei->vfs_inode;
 }
@@ -735,18 +738,6 @@ static void destroy_inodecache(void)
 
 static void ext4_clear_inode(struct inode *inode)
 {
-#ifdef CONFIG_EXT4_FS_POSIX_ACL
-       if (EXT4_I(inode)->i_acl &&
-                       EXT4_I(inode)->i_acl != EXT4_ACL_NOT_CACHED) {
-               posix_acl_release(EXT4_I(inode)->i_acl);
-               EXT4_I(inode)->i_acl = EXT4_ACL_NOT_CACHED;
-       }
-       if (EXT4_I(inode)->i_default_acl &&
-                       EXT4_I(inode)->i_default_acl != EXT4_ACL_NOT_CACHED) {
-               posix_acl_release(EXT4_I(inode)->i_default_acl);
-               EXT4_I(inode)->i_default_acl = EXT4_ACL_NOT_CACHED;
-       }
-#endif
        ext4_discard_preallocations(inode);
        if (EXT4_JOURNAL(inode))
                jbd2_journal_release_jbd_inode(EXT4_SB(inode->i_sb)->s_journal,
@@ -978,7 +969,7 @@ static ssize_t ext4_quota_read(struct super_block *sb, int type, char *data,
 static ssize_t ext4_quota_write(struct super_block *sb, int type,
                                const char *data, size_t len, loff_t off);
 
-static struct dquot_operations ext4_quota_operations = {
+static const struct dquot_operations ext4_quota_operations = {
        .initialize     = dquot_initialize,
        .drop           = dquot_drop,
        .alloc_space    = dquot_alloc_space,
@@ -999,7 +990,7 @@ static struct dquot_operations ext4_quota_operations = {
        .destroy_dquot  = dquot_destroy,
 };
 
-static struct quotactl_ops ext4_qctl_operations = {
+static const struct quotactl_ops ext4_qctl_operations = {
        .quota_on       = ext4_quota_on,
        .quota_off      = vfs_quota_off,
        .quota_sync     = vfs_quota_sync,
@@ -1295,11 +1286,9 @@ static int parse_options(char *options, struct super_block *sb,
                        *journal_devnum = option;
                        break;
                case Opt_journal_checksum:
-                       set_opt(sbi->s_mount_opt, JOURNAL_CHECKSUM);
-                       break;
+                       break;  /* Kept for backwards compatibility */
                case Opt_journal_async_commit:
                        set_opt(sbi->s_mount_opt, JOURNAL_ASYNC_COMMIT);
-                       set_opt(sbi->s_mount_opt, JOURNAL_CHECKSUM);
                        break;
                case Opt_noload:
                        set_opt(sbi->s_mount_opt, NOLOAD);
@@ -1476,7 +1465,7 @@ set_qf_format:
                        break;
 #endif
                case Opt_abort:
-                       set_opt(sbi->s_mount_opt, ABORT);
+                       sbi->s_mount_flags |= EXT4_MF_FS_ABORTED;
                        break;
                case Opt_nobarrier:
                        clear_opt(sbi->s_mount_opt, BARRIER);
@@ -1711,12 +1700,12 @@ static int ext4_fill_flex_info(struct super_block *sb)
                gdp = ext4_get_group_desc(sb, i, NULL);
 
                flex_group = ext4_flex_group(sbi, i);
-               atomic_set(&sbi->s_flex_groups[flex_group].free_inodes,
-                          ext4_free_inodes_count(sb, gdp));
-               atomic_set(&sbi->s_flex_groups[flex_group].free_blocks,
-                          ext4_free_blks_count(sb, gdp));
-               atomic_set(&sbi->s_flex_groups[flex_group].used_dirs,
-                          ext4_used_dirs_count(sb, gdp));
+               atomic_add(ext4_free_inodes_count(sb, gdp),
+                          &sbi->s_flex_groups[flex_group].free_inodes);
+               atomic_add(ext4_free_blks_count(sb, gdp),
+                          &sbi->s_flex_groups[flex_group].free_blocks);
+               atomic_add(ext4_used_dirs_count(sb, gdp),
+                          &sbi->s_flex_groups[flex_group].used_dirs);
        }
 
        return 1;
@@ -1959,7 +1948,7 @@ static loff_t ext4_max_size(int blkbits, int has_huge_files)
        /* small i_blocks in vfs inode? */
        if (!has_huge_files || sizeof(blkcnt_t) < sizeof(u64)) {
                /*
-                * CONFIG_LBD is not enabled implies the inode
+                * CONFIG_LBDAF is not enabled implies the inode
                 * i_block represent total blocks in 512 bytes
                 * 32 == size of vfs inode i_blocks * 8
                 */
@@ -2002,7 +1991,7 @@ static loff_t ext4_max_bitmap_size(int bits, int has_huge_files)
 
        if (!has_huge_files || sizeof(blkcnt_t) < sizeof(u64)) {
                /*
-                * !has_huge_files or CONFIG_LBD not enabled implies that
+                * !has_huge_files or CONFIG_LBDAF not enabled implies that
                 * the inode i_block field represents total file blocks in
                 * 2^32 512-byte sectors == size of vfs inode i_blocks * 8
                 */
@@ -2206,24 +2195,28 @@ EXT4_RO_ATTR(session_write_kbytes);
 EXT4_RO_ATTR(lifetime_write_kbytes);
 EXT4_ATTR_OFFSET(inode_readahead_blks, 0644, sbi_ui_show,
                 inode_readahead_blks_store, s_inode_readahead_blks);
+EXT4_RW_ATTR_SBI_UI(inode_goal, s_inode_goal);
 EXT4_RW_ATTR_SBI_UI(mb_stats, s_mb_stats);
 EXT4_RW_ATTR_SBI_UI(mb_max_to_scan, s_mb_max_to_scan);
 EXT4_RW_ATTR_SBI_UI(mb_min_to_scan, s_mb_min_to_scan);
 EXT4_RW_ATTR_SBI_UI(mb_order2_req, s_mb_order2_reqs);
 EXT4_RW_ATTR_SBI_UI(mb_stream_req, s_mb_stream_request);
 EXT4_RW_ATTR_SBI_UI(mb_group_prealloc, s_mb_group_prealloc);
+EXT4_RW_ATTR_SBI_UI(max_writeback_mb_bump, s_max_writeback_mb_bump);
 
 static struct attribute *ext4_attrs[] = {
        ATTR_LIST(delayed_allocation_blocks),
        ATTR_LIST(session_write_kbytes),
        ATTR_LIST(lifetime_write_kbytes),
        ATTR_LIST(inode_readahead_blks),
+       ATTR_LIST(inode_goal),
        ATTR_LIST(mb_stats),
        ATTR_LIST(mb_max_to_scan),
        ATTR_LIST(mb_min_to_scan),
        ATTR_LIST(mb_order2_req),
        ATTR_LIST(mb_stream_req),
        ATTR_LIST(mb_group_prealloc),
+       ATTR_LIST(max_writeback_mb_bump),
        NULL,
 };
 
@@ -2267,6 +2260,49 @@ static struct kobj_type ext4_ktype = {
        .release        = ext4_sb_release,
 };
 
+/*
+ * Check whether this filesystem can be mounted based on
+ * the features present and the RDONLY/RDWR mount requested.
+ * Returns 1 if this filesystem can be mounted as requested,
+ * 0 if it cannot be.
+ */
+static int ext4_feature_set_ok(struct super_block *sb, int readonly)
+{
+       if (EXT4_HAS_INCOMPAT_FEATURE(sb, ~EXT4_FEATURE_INCOMPAT_SUPP)) {
+               ext4_msg(sb, KERN_ERR,
+                       "Couldn't mount because of "
+                       "unsupported optional features (%x)",
+                       (le32_to_cpu(EXT4_SB(sb)->s_es->s_feature_incompat) &
+                       ~EXT4_FEATURE_INCOMPAT_SUPP));
+               return 0;
+       }
+
+       if (readonly)
+               return 1;
+
+       /* Check that feature set is OK for a read-write mount */
+       if (EXT4_HAS_RO_COMPAT_FEATURE(sb, ~EXT4_FEATURE_RO_COMPAT_SUPP)) {
+               ext4_msg(sb, KERN_ERR, "couldn't mount RDWR because of "
+                        "unsupported optional features (%x)",
+                        (le32_to_cpu(EXT4_SB(sb)->s_es->s_feature_ro_compat) &
+                               ~EXT4_FEATURE_RO_COMPAT_SUPP));
+               return 0;
+       }
+       /*
+        * Large file size enabled file system can only be mounted
+        * read-write on 32-bit systems if kernel is built with CONFIG_LBDAF
+        */
+       if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) {
+               if (sizeof(blkcnt_t) < sizeof(u64)) {
+                       ext4_msg(sb, KERN_ERR, "Filesystem with huge files "
+                                "cannot be mounted RDWR without "
+                                "CONFIG_LBDAF");
+                       return 0;
+               }
+       }
+       return 1;
+}
+
 static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                                __releases(kernel_lock)
                                __acquires(kernel_lock)
@@ -2288,7 +2324,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        unsigned int db_count;
        unsigned int i;
        int needs_recovery, has_huge_files;
-       int features;
        __u64 blocks_count;
        int err;
        unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
@@ -2415,39 +2450,9 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
         * previously didn't change the revision level when setting the flags,
         * so there is a chance incompat flags are set on a rev 0 filesystem.
         */
-       features = EXT4_HAS_INCOMPAT_FEATURE(sb, ~EXT4_FEATURE_INCOMPAT_SUPP);
-       if (features) {
-               ext4_msg(sb, KERN_ERR,
-                       "Couldn't mount because of "
-                       "unsupported optional features (%x)",
-                       (le32_to_cpu(EXT4_SB(sb)->s_es->s_feature_incompat) &
-                       ~EXT4_FEATURE_INCOMPAT_SUPP));
-               goto failed_mount;
-       }
-       features = EXT4_HAS_RO_COMPAT_FEATURE(sb, ~EXT4_FEATURE_RO_COMPAT_SUPP);
-       if (!(sb->s_flags & MS_RDONLY) && features) {
-               ext4_msg(sb, KERN_ERR,
-                       "Couldn't mount RDWR because of "
-                       "unsupported optional features (%x)",
-                       (le32_to_cpu(EXT4_SB(sb)->s_es->s_feature_ro_compat) &
-                       ~EXT4_FEATURE_RO_COMPAT_SUPP));
+       if (!ext4_feature_set_ok(sb, (sb->s_flags & MS_RDONLY)))
                goto failed_mount;
-       }
-       has_huge_files = EXT4_HAS_RO_COMPAT_FEATURE(sb,
-                                   EXT4_FEATURE_RO_COMPAT_HUGE_FILE);
-       if (has_huge_files) {
-               /*
-                * Large file size enabled file system can only be
-                * mount if kernel is build with CONFIG_LBD
-                */
-               if (sizeof(root->i_blocks) < sizeof(u64) &&
-                               !(sb->s_flags & MS_RDONLY)) {
-                       ext4_msg(sb, KERN_ERR, "Filesystem with huge "
-                                       "files cannot be mounted read-write "
-                                       "without CONFIG_LBD");
-                       goto failed_mount;
-               }
-       }
+
        blocksize = BLOCK_SIZE << le32_to_cpu(es->s_log_block_size);
 
        if (blocksize < EXT4_MIN_BLOCK_SIZE ||
@@ -2483,6 +2488,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                }
        }
 
+       has_huge_files = EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                               EXT4_FEATURE_RO_COMPAT_HUGE_FILE);
        sbi->s_bitmap_maxbytes = ext4_max_bitmap_size(sb->s_blocksize_bits,
                                                      has_huge_files);
        sb->s_maxbytes = ext4_max_size(sb->s_blocksize_bits, has_huge_files);
@@ -2563,12 +2570,19 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                goto failed_mount;
        }
 
-       if (ext4_blocks_count(es) >
-                   (sector_t)(~0ULL) >> (sb->s_blocksize_bits - 9)) {
+       /*
+        * Test whether we have more sectors than will fit in sector_t,
+        * and whether the max offset is addressable by the page cache.
+        */
+       if ((ext4_blocks_count(es) >
+            (sector_t)(~0ULL) >> (sb->s_blocksize_bits - 9)) ||
+           (ext4_blocks_count(es) >
+            (pgoff_t)(~0ULL) >> (PAGE_CACHE_SHIFT - sb->s_blocksize_bits))) {
                ext4_msg(sb, KERN_ERR, "filesystem"
-                       " too large to mount safely");
+                        " too large to mount safely on this system");
                if (sizeof(sector_t) < 8)
-                       ext4_msg(sb, KERN_WARNING, "CONFIG_LBD not enabled");
+                       ext4_msg(sb, KERN_WARNING, "CONFIG_LBDAF not enabled");
+               ret = -EFBIG;
                goto failed_mount;
        }
 
@@ -2609,6 +2623,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                goto failed_mount;
        }
        sbi->s_groups_count = blocks_count;
+       sbi->s_blockfile_groups = min_t(ext4_group_t, sbi->s_groups_count,
+                       (EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb)));
        db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) /
                   EXT4_DESC_PER_BLOCK(sb);
        sbi->s_group_desc = kmalloc(db_count * sizeof(struct buffer_head *),
@@ -2670,6 +2686,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        }
 
        sbi->s_stripe = ext4_get_stripe_size(sbi);
+       sbi->s_max_writeback_mb_bump = 128;
 
        /*
         * set up enough so that it can read an inode
@@ -2743,20 +2760,14 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                goto failed_mount4;
        }
 
-       if (test_opt(sb, JOURNAL_ASYNC_COMMIT)) {
-               jbd2_journal_set_features(sbi->s_journal,
-                               JBD2_FEATURE_COMPAT_CHECKSUM, 0,
+       jbd2_journal_set_features(sbi->s_journal,
+                                 JBD2_FEATURE_COMPAT_CHECKSUM, 0, 0);
+       if (test_opt(sb, JOURNAL_ASYNC_COMMIT))
+               jbd2_journal_set_features(sbi->s_journal, 0, 0,
                                JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT);
-       } else if (test_opt(sb, JOURNAL_CHECKSUM)) {
-               jbd2_journal_set_features(sbi->s_journal,
-                               JBD2_FEATURE_COMPAT_CHECKSUM, 0, 0);
+       else
                jbd2_journal_clear_features(sbi->s_journal, 0, 0,
                                JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT);
-       } else {
-               jbd2_journal_clear_features(sbi->s_journal,
-                               JBD2_FEATURE_COMPAT_CHECKSUM, 0,
-                               JBD2_FEATURE_INCOMPAT_ASYNC_COMMIT);
-       }
 
        /* We have now updated the journal if required, so we can
         * validate the data journaling mode. */
@@ -2795,6 +2806,12 @@ no_journal:
                        clear_opt(sbi->s_mount_opt, NOBH);
                }
        }
+       EXT4_SB(sb)->dio_unwritten_wq = create_workqueue("ext4-dio-unwritten");
+       if (!EXT4_SB(sb)->dio_unwritten_wq) {
+               printk(KERN_ERR "EXT4-fs: failed to create DIO workqueue\n");
+               goto failed_mount_wq;
+       }
+
        /*
         * The jbd2_journal_load will have done any necessary log recovery,
         * so we can safely mount the rest of the filesystem now.
@@ -2907,6 +2924,8 @@ cantfind_ext4:
 
 failed_mount4:
        ext4_msg(sb, KERN_ERR, "mount failed");
+       destroy_workqueue(EXT4_SB(sb)->dio_unwritten_wq);
+failed_mount_wq:
        ext4_release_system_zone(sb);
        if (sbi->s_journal) {
                jbd2_journal_destroy(sbi->s_journal);
@@ -3222,7 +3241,18 @@ static int ext4_commit_super(struct super_block *sb, int sync)
                clear_buffer_write_io_error(sbh);
                set_buffer_uptodate(sbh);
        }
-       es->s_wtime = cpu_to_le32(get_seconds());
+       /*
+        * If the file system is mounted read-only, don't update the
+        * superblock write time.  This avoids updating the superblock
+        * write time when we are mounting the root file system
+        * read/only but we need to replay the journal; at that point,
+        * for people who are east of GMT and who make their clock
+        * tick in localtime for Windows bug-for-bug compatibility,
+        * the clock is set in the future, and this will cause e2fsck
+        * to complain and force a full file system check.
+        */
+       if (!(sb->s_flags & MS_RDONLY))
+               es->s_wtime = cpu_to_le32(get_seconds());
        es->s_kbytes_written =
                cpu_to_le64(EXT4_SB(sb)->s_kbytes_written + 
                            ((part_stat_read(sb->s_bdev->bd_part, sectors[1]) -
@@ -3347,11 +3377,13 @@ static int ext4_sync_fs(struct super_block *sb, int wait)
 {
        int ret = 0;
        tid_t target;
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
 
        trace_ext4_sync_fs(sb, wait);
-       if (jbd2_journal_start_commit(EXT4_SB(sb)->s_journal, &target)) {
+       flush_workqueue(sbi->dio_unwritten_wq);
+       if (jbd2_journal_start_commit(sbi->s_journal, &target)) {
                if (wait)
-                       jbd2_log_wait_commit(EXT4_SB(sb)->s_journal, target);
+                       jbd2_log_wait_commit(sbi->s_journal, target);
        }
        return ret;
 }
@@ -3452,7 +3484,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
                goto restore_opts;
        }
 
-       if (sbi->s_mount_opt & EXT4_MOUNT_ABORT)
+       if (sbi->s_mount_flags & EXT4_MF_FS_ABORTED)
                ext4_abort(sb, __func__, "Abort forced by user");
 
        sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
@@ -3467,7 +3499,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
 
        if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY) ||
                n_blocks_count > ext4_blocks_count(es)) {
-               if (sbi->s_mount_opt & EXT4_MOUNT_ABORT) {
+               if (sbi->s_mount_flags & EXT4_MF_FS_ABORTED) {
                        err = -EROFS;
                        goto restore_opts;
                }
@@ -3491,18 +3523,11 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
                        if (sbi->s_journal)
                                ext4_mark_recovery_complete(sb, es);
                } else {
-                       int ret;
-                       if ((ret = EXT4_HAS_RO_COMPAT_FEATURE(sb,
-                                       ~EXT4_FEATURE_RO_COMPAT_SUPP))) {
-                               ext4_msg(sb, KERN_WARNING, "couldn't "
-                                      "remount RDWR because of unsupported "
-                                      "optional features (%x)",
-                               (le32_to_cpu(sbi->s_es->s_feature_ro_compat) &
-                                       ~EXT4_FEATURE_RO_COMPAT_SUPP));
+                       /* Make sure we can mount this feature set readwrite */
+                       if (!ext4_feature_set_ok(sb, 0)) {
                                err = -EROFS;
                                goto restore_opts;
                        }
-
                        /*
                         * Make sure the group descriptor checksums
                         * are sane.  If they aren't, refuse to remount r/w.