]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/ext4/super.c
ext4: Avoid double dirtying of super block in ext4_put_super()
[linux-2.6-omap-h63xx.git] / fs / ext4 / super.c
index d5d77958b861b9fc29114965a566a7e2dfdd4a84..7d865608e8175f2d9ed323517306c36e42b88ba4 100644 (file)
@@ -34,6 +34,8 @@
 #include <linux/namei.h>
 #include <linux/quotaops.h>
 #include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/marker.h>
 #include <linux/log2.h>
 #include <linux/crc16.h>
 #include <asm/uaccess.h>
@@ -45,6 +47,8 @@
 #include "namei.h"
 #include "group.h"
 
+struct proc_dir_entry *ext4_proc_root;
+
 static int ext4_load_journal(struct super_block *, struct ext4_super_block *,
                             unsigned long journal_devnum);
 static int ext4_create_journal(struct super_block *, struct ext4_super_block *,
@@ -508,10 +512,12 @@ static void ext4_put_super(struct super_block *sb)
        if (!(sb->s_flags & MS_RDONLY)) {
                EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER);
                es->s_state = cpu_to_le16(sbi->s_mount_state);
-               BUFFER_TRACE(sbi->s_sbh, "marking dirty");
-               mark_buffer_dirty(sbi->s_sbh);
                ext4_commit_super(sb, es, 1);
        }
+       if (sbi->s_proc) {
+               remove_proc_entry("inode_readahead_blks", sbi->s_proc);
+               remove_proc_entry(sb->s_id, ext4_proc_root);
+       }
 
        for (i = 0; i < sbi->s_gdb_count; i++)
                brelse(sbi->s_group_desc[i]);
@@ -520,6 +526,7 @@ static void ext4_put_super(struct super_block *sb)
        percpu_counter_destroy(&sbi->s_freeblocks_counter);
        percpu_counter_destroy(&sbi->s_freeinodes_counter);
        percpu_counter_destroy(&sbi->s_dirs_counter);
+       percpu_counter_destroy(&sbi->s_dirtyblocks_counter);
        brelse(sbi->s_sbh);
 #ifdef CONFIG_QUOTA
        for (i = 0; i < MAXQUOTAS; i++)
@@ -566,8 +573,8 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
        ei->i_acl = EXT4_ACL_NOT_CACHED;
        ei->i_default_acl = EXT4_ACL_NOT_CACHED;
 #endif
-       ei->i_block_alloc_info = NULL;
        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));
        INIT_LIST_HEAD(&ei->i_prealloc_list);
        spin_lock_init(&ei->i_prealloc_lock);
@@ -624,7 +631,6 @@ static void destroy_inodecache(void)
 
 static void ext4_clear_inode(struct inode *inode)
 {
-       struct ext4_block_alloc_info *rsv = EXT4_I(inode)->i_block_alloc_info;
 #ifdef CONFIG_EXT4DEV_FS_POSIX_ACL
        if (EXT4_I(inode)->i_acl &&
                        EXT4_I(inode)->i_acl != EXT4_ACL_NOT_CACHED) {
@@ -637,10 +643,7 @@ static void ext4_clear_inode(struct inode *inode)
                EXT4_I(inode)->i_default_acl = EXT4_ACL_NOT_CACHED;
        }
 #endif
-       ext4_discard_reservation(inode);
-       EXT4_I(inode)->i_block_alloc_info = NULL;
-       if (unlikely(rsv))
-               kfree(rsv);
+       ext4_discard_preallocations(inode);
        jbd2_journal_release_jbd_inode(EXT4_SB(inode->i_sb)->s_journal,
                                       &EXT4_I(inode)->jinode);
 }
@@ -653,7 +656,7 @@ static inline void ext4_show_quota_options(struct seq_file *seq,
 
        if (sbi->s_jquota_fmt)
                seq_printf(seq, ",jqfmt=%s",
-               (sbi->s_jquota_fmt == QFMT_VFS_OLD) ? "vfsold": "vfsv0");
+               (sbi->s_jquota_fmt == QFMT_VFS_OLD) ? "vfsold" : "vfsv0");
 
        if (sbi->s_qf_names[USRQUOTA])
                seq_printf(seq, ",usrjquota=%s", sbi->s_qf_names[USRQUOTA]);
@@ -751,8 +754,6 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs)
                seq_puts(seq, ",nobh");
        if (!test_opt(sb, EXTENTS))
                seq_puts(seq, ",noextents");
-       if (!test_opt(sb, MBALLOC))
-               seq_puts(seq, ",nomballoc");
        if (test_opt(sb, I_VERSION))
                seq_puts(seq, ",i_version");
        if (!test_opt(sb, DELALLOC))
@@ -772,6 +773,10 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs)
        else if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_WRITEBACK_DATA)
                seq_puts(seq, ",data=writeback");
 
+       if (sbi->s_inode_readahead_blks != EXT4_DEF_INODE_READAHEAD_BLKS)
+               seq_printf(seq, ",inode_readahead_blks=%u",
+                          sbi->s_inode_readahead_blks);
+
        ext4_show_quota_options(seq, sb);
        return 0;
 }
@@ -821,7 +826,7 @@ static struct dentry *ext4_fh_to_parent(struct super_block *sb, struct fid *fid,
 }
 
 #ifdef CONFIG_QUOTA
-#define QTYPE2NAME(t) ((t) == USRQUOTA?"user":"group")
+#define QTYPE2NAME(t) ((t) == USRQUOTA ? "user" : "group")
 #define QTYPE2MOPT(on, t) ((t) == USRQUOTA?((on)##USRJQUOTA):((on)##GRPJQUOTA))
 
 static int ext4_dquot_initialize(struct inode *inode, int type);
@@ -906,6 +911,7 @@ enum {
        Opt_ignore, Opt_barrier, Opt_err, Opt_resize, Opt_usrquota,
        Opt_grpquota, Opt_extents, Opt_noextents, Opt_i_version,
        Opt_mballoc, Opt_nomballoc, Opt_stripe, Opt_delalloc, Opt_nodelalloc,
+       Opt_inode_readahead_blks
 };
 
 static match_table_t tokens = {
@@ -966,6 +972,7 @@ static match_table_t tokens = {
        {Opt_resize, "resize"},
        {Opt_delalloc, "delalloc"},
        {Opt_nodelalloc, "nodelalloc"},
+       {Opt_inode_readahead_blks, "inode_readahead_blks=%u"},
        {Opt_err, NULL},
 };
 
@@ -980,7 +987,7 @@ static ext4_fsblk_t get_sb_block(void **data)
        /*todo: use simple_strtoll with >32bit ext4 */
        sb_block = simple_strtoul(options, &options, 0);
        if (*options && *options != ',') {
-               printk("EXT4-fs: Invalid sb specification: %s\n",
+               printk(KERN_ERR "EXT4-fs: Invalid sb specification: %s\n",
                       (char *) *data);
                return 1;
        }
@@ -1081,7 +1088,8 @@ static int parse_options(char *options, struct super_block *sb,
 #else
                case Opt_user_xattr:
                case Opt_nouser_xattr:
-                       printk("EXT4 (no)user_xattr options not supported\n");
+                       printk(KERN_ERR "EXT4 (no)user_xattr options "
+                              "not supported\n");
                        break;
 #endif
 #ifdef CONFIG_EXT4DEV_FS_POSIX_ACL
@@ -1094,7 +1102,8 @@ static int parse_options(char *options, struct super_block *sb,
 #else
                case Opt_acl:
                case Opt_noacl:
-                       printk("EXT4 (no)acl options not supported\n");
+                       printk(KERN_ERR "EXT4 (no)acl options "
+                              "not supported\n");
                        break;
 #endif
                case Opt_reservation:
@@ -1188,8 +1197,8 @@ set_qf_name:
                             sb_any_quota_suspended(sb)) &&
                            !sbi->s_qf_names[qtype]) {
                                printk(KERN_ERR
-                                       "EXT4-fs: Cannot change journaled "
-                                       "quota options when quota turned on.\n");
+                                      "EXT4-fs: Cannot change journaled "
+                                      "quota options when quota turned on.\n");
                                return 0;
                        }
                        qname = match_strdup(&args[0]);
@@ -1356,12 +1365,6 @@ set_qf_format:
                case Opt_nodelalloc:
                        clear_opt(sbi->s_mount_opt, DELALLOC);
                        break;
-               case Opt_mballoc:
-                       set_opt(sbi->s_mount_opt, MBALLOC);
-                       break;
-               case Opt_nomballoc:
-                       clear_opt(sbi->s_mount_opt, MBALLOC);
-                       break;
                case Opt_stripe:
                        if (match_int(&args[0], &option))
                                return 0;
@@ -1372,6 +1375,13 @@ set_qf_format:
                case Opt_delalloc:
                        set_opt(sbi->s_mount_opt, DELALLOC);
                        break;
+               case Opt_inode_readahead_blks:
+                       if (match_int(&args[0], &option))
+                               return 0;
+                       if (option < 0 || option > (1 << 30))
+                               return 0;
+                       sbi->s_inode_readahead_blks = option;
+                       break;
                default:
                        printk(KERN_ERR
                               "EXT4-fs: Unrecognized mount option \"%s\" "
@@ -1472,15 +1482,9 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,
                        EXT4_INODES_PER_GROUP(sb),
                        sbi->s_mount_opt);
 
-       printk(KERN_INFO "EXT4 FS on %s, ", sb->s_id);
-       if (EXT4_SB(sb)->s_journal->j_inode == NULL) {
-               char b[BDEVNAME_SIZE];
-
-               printk("external journal on %s\n",
-                       bdevname(EXT4_SB(sb)->s_journal->j_dev, b));
-       } else {
-               printk("internal journal\n");
-       }
+       printk(KERN_INFO "EXT4 FS on %s, %s journal on %s\n",
+              sb->s_id, EXT4_SB(sb)->s_journal->j_inode ? "internal" :
+              "external", EXT4_SB(sb)->s_journal->j_devname);
        return res;
 }
 
@@ -1503,8 +1507,11 @@ static int ext4_fill_flex_info(struct super_block *sb)
        sbi->s_log_groups_per_flex = sbi->s_es->s_log_groups_per_flex;
        groups_per_flex = 1 << sbi->s_log_groups_per_flex;
 
-       flex_group_count = (sbi->s_groups_count + groups_per_flex - 1) /
-               groups_per_flex;
+       /* We allocate both existing and potentially added groups */
+       flex_group_count = ((sbi->s_groups_count + groups_per_flex - 1) +
+                           ((sbi->s_es->s_reserved_gdt_blocks +1 ) <<
+                             EXT4_DESC_PER_BLOCK_BITS(sb))) /
+                          groups_per_flex;
        sbi->s_flex_groups = kzalloc(flex_group_count *
                                     sizeof(struct flex_groups), GFP_KERNEL);
        if (sbi->s_flex_groups == NULL) {
@@ -1583,7 +1590,7 @@ static int ext4_check_descriptors(struct super_block *sb)
        if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG))
                flexbg_flag = 1;
 
-       ext4_debug ("Checking group descriptors");
+       ext4_debug("Checking group descriptors");
 
        for (i = 0; i < sbi->s_groups_count; i++) {
                struct ext4_group_desc *gdp = ext4_get_group_desc(sb, i, NULL);
@@ -1622,8 +1629,10 @@ static int ext4_check_descriptors(struct super_block *sb)
                               "Checksum for group %lu failed (%u!=%u)\n",
                               i, le16_to_cpu(ext4_group_desc_csum(sbi, i,
                               gdp)), le16_to_cpu(gdp->bg_checksum));
-                       if (!(sb->s_flags & MS_RDONLY))
+                       if (!(sb->s_flags & MS_RDONLY)) {
+                               spin_unlock(sb_bgl_lock(sbi, i));
                                return 0;
+                       }
                }
                spin_unlock(sb_bgl_lock(sbi, i));
                if (!flexbg_flag)
@@ -1713,9 +1722,9 @@ static void ext4_orphan_cleanup(struct super_block *sb,
                DQUOT_INIT(inode);
                if (inode->i_nlink) {
                        printk(KERN_DEBUG
-                               "%s: truncating inode %lu to %Ld bytes\n",
+                               "%s: truncating inode %lu to %lld bytes\n",
                                __func__, inode->i_ino, inode->i_size);
-                       jbd_debug(2, "truncating inode %lu to %Ld bytes\n",
+                       jbd_debug(2, "truncating inode %lu to %lld bytes\n",
                                  inode->i_ino, inode->i_size);
                        ext4_truncate(inode);
                        nr_truncates++;
@@ -1913,6 +1922,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        unsigned long journal_devnum = 0;
        unsigned long def_mount_opts;
        struct inode *root;
+       char *cp;
        int ret = -EINVAL;
        int blocksize;
        int db_count;
@@ -1929,10 +1939,15 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        sbi->s_mount_opt = 0;
        sbi->s_resuid = EXT4_DEF_RESUID;
        sbi->s_resgid = EXT4_DEF_RESGID;
+       sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS;
        sbi->s_sb_block = sb_block;
 
        unlock_kernel();
 
+       /* Cleanup superblock name */
+       for (cp = sb->s_id; (cp = strchr(cp, '/'));)
+               *cp = '!';
+
        blocksize = sb_min_blocksize(sb, EXT4_MIN_BLOCK_SIZE);
        if (!blocksize) {
                printk(KERN_ERR "EXT4-fs: unable to set blocksize\n");
@@ -2011,11 +2026,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                ext4_warning(sb, __func__,
                        "extents feature not enabled on this filesystem, "
                        "use tune2fs.\n");
-       /*
-        * turn on mballoc code by default in ext4 filesystem
-        * Use -o nomballoc to turn it off
-        */
-       set_opt(sbi->s_mount_opt, MBALLOC);
 
        /*
         * enable delayed allocation by default
@@ -2218,6 +2228,14 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                goto failed_mount;
        }
 
+       if (ext4_proc_root)
+               sbi->s_proc = proc_mkdir(sb->s_id, ext4_proc_root);
+
+       if (sbi->s_proc)
+               proc_create_data("inode_readahead_blks", 0644, sbi->s_proc,
+                                &ext4_ui_proc_fops,
+                                &sbi->s_inode_readahead_blks);
+
        bgl_lock_init(&sbi->s_blockgroup_lock);
 
        for (i = 0; i < db_count; i++) {
@@ -2256,24 +2274,14 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                err = percpu_counter_init(&sbi->s_dirs_counter,
                                ext4_count_dirs(sb));
        }
+       if (!err) {
+               err = percpu_counter_init(&sbi->s_dirtyblocks_counter, 0);
+       }
        if (err) {
                printk(KERN_ERR "EXT4-fs: insufficient memory\n");
                goto failed_mount3;
        }
 
-       /* per fileystem reservation list head & lock */
-       spin_lock_init(&sbi->s_rsv_window_lock);
-       sbi->s_rsv_window_root = RB_ROOT;
-       /* Add a single, static dummy reservation to the start of the
-        * reservation window list --- it gives us a placeholder for
-        * append-at-start-of-list which makes the allocation logic
-        * _much_ simpler. */
-       sbi->s_rsv_window_head.rsv_start = EXT4_RESERVE_WINDOW_NOT_ALLOCATED;
-       sbi->s_rsv_window_head.rsv_end = EXT4_RESERVE_WINDOW_NOT_ALLOCATED;
-       sbi->s_rsv_window_head.rsv_alloc_hit = 0;
-       sbi->s_rsv_window_head.rsv_goal_size = 0;
-       ext4_rsv_window_add(sb, &sbi->s_rsv_window_head);
-
        sbi->s_stripe = ext4_get_stripe_size(sbi);
 
        /*
@@ -2470,7 +2478,12 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                printk(KERN_INFO "EXT4-fs: delayed allocation enabled\n");
 
        ext4_ext_init(sb);
-       ext4_mb_init(sb, needs_recovery);
+       err = ext4_mb_init(sb, needs_recovery);
+       if (err) {
+               printk(KERN_ERR "EXT4-fs: failed to initalize mballoc (%d)\n",
+                      err);
+               goto failed_mount4;
+       }
 
        lock_kernel();
        return 0;
@@ -2488,11 +2501,16 @@ failed_mount3:
        percpu_counter_destroy(&sbi->s_freeblocks_counter);
        percpu_counter_destroy(&sbi->s_freeinodes_counter);
        percpu_counter_destroy(&sbi->s_dirs_counter);
+       percpu_counter_destroy(&sbi->s_dirtyblocks_counter);
 failed_mount2:
        for (i = 0; i < db_count; i++)
                brelse(sbi->s_group_desc[i]);
        kfree(sbi->s_group_desc);
 failed_mount:
+       if (sbi->s_proc) {
+               remove_proc_entry("inode_readahead_blks", sbi->s_proc);
+               remove_proc_entry(sb->s_id, ext4_proc_root);
+       }
 #ifdef CONFIG_QUOTA
        for (i = 0; i < MAXQUOTAS; i++)
                kfree(sbi->s_qf_names[i]);
@@ -2551,7 +2569,7 @@ static journal_t *ext4_get_journal(struct super_block *sb,
                return NULL;
        }
 
-       jbd_debug(2, "Journal inode found at %p: %Ld bytes\n",
+       jbd_debug(2, "Journal inode found at %p: %lld bytes\n",
                  journal_inode, journal_inode->i_size);
        if (!S_ISREG(journal_inode->i_mode)) {
                printk(KERN_ERR "EXT4-fs: invalid journal inode.\n");
@@ -2714,6 +2732,11 @@ static int ext4_load_journal(struct super_block *sb,
                        return -EINVAL;
        }
 
+       if (journal->j_flags & JBD2_BARRIER)
+               printk(KERN_INFO "EXT4-fs: barriers enabled\n");
+       else
+               printk(KERN_INFO "EXT4-fs: barriers disabled\n");
+
        if (!really_read_only && test_opt(sb, UPDATE_JOURNAL)) {
                err = jbd2_journal_update_format(journal);
                if (err)  {
@@ -2798,13 +2821,34 @@ static void ext4_commit_super(struct super_block *sb,
 
        if (!sbh)
                return;
+       if (buffer_write_io_error(sbh)) {
+               /*
+                * Oh, dear.  A previous attempt to write the
+                * superblock failed.  This could happen because the
+                * USB device was yanked out.  Or it could happen to
+                * be a transient write error and maybe the block will
+                * be remapped.  Nothing we can do but to retry the
+                * write and hope for the best.
+                */
+               printk(KERN_ERR "ext4: previous I/O error to "
+                      "superblock detected for %s.\n", sb->s_id);
+               clear_buffer_write_io_error(sbh);
+               set_buffer_uptodate(sbh);
+       }
        es->s_wtime = cpu_to_le32(get_seconds());
        ext4_free_blocks_count_set(es, ext4_count_free_blocks(sb));
        es->s_free_inodes_count = cpu_to_le32(ext4_count_free_inodes(sb));
        BUFFER_TRACE(sbh, "marking dirty");
        mark_buffer_dirty(sbh);
-       if (sync)
+       if (sync) {
                sync_dirty_buffer(sbh);
+               if (buffer_write_io_error(sbh)) {
+                       printk(KERN_ERR "ext4: I/O error while writing "
+                              "superblock for %s.\n", sb->s_id);
+                       clear_buffer_write_io_error(sbh);
+                       set_buffer_uptodate(sbh);
+               }
+       }
 }
 
 
@@ -2906,6 +2950,7 @@ static int ext4_sync_fs(struct super_block *sb, int wait)
 {
        tid_t target;
 
+       trace_mark(ext4_sync_fs, "dev %s wait %d", sb->s_id, wait);
        sb->s_dirt = 0;
        if (jbd2_journal_start_commit(EXT4_SB(sb)->s_journal, &target)) {
                if (wait)
@@ -3161,7 +3206,8 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
        buf->f_type = EXT4_SUPER_MAGIC;
        buf->f_bsize = sb->s_blocksize;
        buf->f_blocks = ext4_blocks_count(es) - sbi->s_overhead_last;
-       buf->f_bfree = percpu_counter_sum_positive(&sbi->s_freeblocks_counter);
+       buf->f_bfree = percpu_counter_sum_positive(&sbi->s_freeblocks_counter) -
+                      percpu_counter_sum_positive(&sbi->s_dirtyblocks_counter);
        ext4_free_blocks_count_set(es, buf->f_bfree);
        buf->f_bavail = buf->f_bfree - ext4_r_blocks_count(es);
        if (buf->f_bfree < ext4_r_blocks_count(es))
@@ -3431,7 +3477,7 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,
        handle_t *handle = journal_current_handle();
 
        if (!handle) {
-               printk(KERN_WARNING "EXT4-fs: Quota write (off=%Lu, len=%Lu)"
+               printk(KERN_WARNING "EXT4-fs: Quota write (off=%llu, len=%llu)"
                        " cancelled because transaction is not started.\n",
                        (unsigned long long)off, (unsigned long long)len);
                return -EIO;
@@ -3492,6 +3538,48 @@ static int ext4_get_sb(struct file_system_type *fs_type,
        return get_sb_bdev(fs_type, flags, dev_name, data, ext4_fill_super, mnt);
 }
 
+#ifdef CONFIG_PROC_FS
+static int ext4_ui_proc_show(struct seq_file *m, void *v)
+{
+       unsigned int *p = m->private;
+
+       seq_printf(m, "%u\n", *p);
+       return 0;
+}
+
+static int ext4_ui_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, ext4_ui_proc_show, PDE(inode)->data);
+}
+
+static ssize_t ext4_ui_proc_write(struct file *file, const char __user *buf,
+                              size_t cnt, loff_t *ppos)
+{
+       unsigned int *p = PDE(file->f_path.dentry->d_inode)->data;
+       char str[32];
+       unsigned long value;
+
+       if (cnt >= sizeof(str))
+               return -EINVAL;
+       if (copy_from_user(str, buf, cnt))
+               return -EFAULT;
+       value = simple_strtol(str, NULL, 0);
+       if (value < 0)
+               return -ERANGE;
+       *p = value;
+       return cnt;
+}
+
+const struct file_operations ext4_ui_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = ext4_ui_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+       .write          = ext4_ui_proc_write,
+};
+#endif
+
 static struct file_system_type ext4dev_fs_type = {
        .owner          = THIS_MODULE,
        .name           = "ext4dev",
@@ -3504,6 +3592,7 @@ static int __init init_ext4_fs(void)
 {
        int err;
 
+       ext4_proc_root = proc_mkdir("fs/ext4", NULL);
        err = init_ext4_mballoc();
        if (err)
                return err;
@@ -3533,6 +3622,7 @@ static void __exit exit_ext4_fs(void)
        destroy_inodecache();
        exit_ext4_xattr();
        exit_ext4_mballoc();
+       remove_proc_entry("fs/ext4", NULL);
 }
 
 MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others");