Merge tag 'clk-for-linus-3.11' of git://git.linaro.org/people/mturquette/linux
[firefly-linux-kernel-4.4.55.git] / fs / ext4 / super.c
index eac4d3081ba468bc71f79aa719c21c411480135c..85b3dd60169beba2b2535ae4342a436387273e32 100644 (file)
@@ -69,6 +69,7 @@ static void ext4_mark_recovery_complete(struct super_block *sb,
 static void ext4_clear_journal_err(struct super_block *sb,
                                   struct ext4_super_block *es);
 static int ext4_sync_fs(struct super_block *sb, int wait);
+static int ext4_sync_fs_nojournal(struct super_block *sb, int wait);
 static int ext4_remount(struct super_block *sb, int *flags, char *data);
 static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf);
 static int ext4_unfreeze(struct super_block *sb);
@@ -398,6 +399,11 @@ static void ext4_handle_error(struct super_block *sb)
        }
        if (test_opt(sb, ERRORS_RO)) {
                ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
+               /*
+                * Make sure updated value of ->s_mount_flags will be visible
+                * before ->s_flags update
+                */
+               smp_wmb();
                sb->s_flags |= MS_RDONLY;
        }
        if (test_opt(sb, ERRORS_PANIC))
@@ -422,9 +428,9 @@ void __ext4_error(struct super_block *sb, const char *function,
        ext4_handle_error(sb);
 }
 
-void ext4_error_inode(struct inode *inode, const char *function,
-                     unsigned int line, ext4_fsblk_t block,
-                     const char *fmt, ...)
+void __ext4_error_inode(struct inode *inode, const char *function,
+                       unsigned int line, ext4_fsblk_t block,
+                       const char *fmt, ...)
 {
        va_list args;
        struct va_format vaf;
@@ -451,9 +457,9 @@ void ext4_error_inode(struct inode *inode, const char *function,
        ext4_handle_error(inode->i_sb);
 }
 
-void ext4_error_file(struct file *file, const char *function,
-                    unsigned int line, ext4_fsblk_t block,
-                    const char *fmt, ...)
+void __ext4_error_file(struct file *file, const char *function,
+                      unsigned int line, ext4_fsblk_t block,
+                      const char *fmt, ...)
 {
        va_list args;
        struct va_format vaf;
@@ -570,8 +576,13 @@ void __ext4_abort(struct super_block *sb, const char *function,
 
        if ((sb->s_flags & MS_RDONLY) == 0) {
                ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
-               sb->s_flags |= MS_RDONLY;
                EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FS_ABORTED;
+               /*
+                * Make sure updated value of ->s_mount_flags will be visible
+                * before ->s_flags update
+                */
+               smp_wmb();
+               sb->s_flags |= MS_RDONLY;
                if (EXT4_SB(sb)->s_journal)
                        jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO);
                save_error_info(sb, function, line);
@@ -580,7 +591,8 @@ void __ext4_abort(struct super_block *sb, const char *function,
                panic("EXT4-fs panic from previous error\n");
 }
 
-void ext4_msg(struct super_block *sb, const char *prefix, const char *fmt, ...)
+void __ext4_msg(struct super_block *sb,
+               const char *prefix, const char *fmt, ...)
 {
        struct va_format vaf;
        va_list args;
@@ -750,8 +762,10 @@ static void ext4_put_super(struct super_block *sb)
        ext4_unregister_li_request(sb);
        dquot_disable(sb, -1, DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);
 
-       flush_workqueue(sbi->dio_unwritten_wq);
-       destroy_workqueue(sbi->dio_unwritten_wq);
+       flush_workqueue(sbi->unrsv_conversion_wq);
+       flush_workqueue(sbi->rsv_conversion_wq);
+       destroy_workqueue(sbi->unrsv_conversion_wq);
+       destroy_workqueue(sbi->rsv_conversion_wq);
 
        if (sbi->s_journal) {
                err = jbd2_journal_destroy(sbi->s_journal);
@@ -760,7 +774,7 @@ static void ext4_put_super(struct super_block *sb)
                        ext4_abort(sb, "Couldn't clean up the journal");
        }
 
-       ext4_es_unregister_shrinker(sb);
+       ext4_es_unregister_shrinker(sbi);
        del_timer(&sbi->s_err_report);
        ext4_release_system_zone(sb);
        ext4_mb_release(sb);
@@ -849,6 +863,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
        rwlock_init(&ei->i_es_lock);
        INIT_LIST_HEAD(&ei->i_es_lru);
        ei->i_es_lru_nr = 0;
+       ei->i_touch_when = 0;
        ei->i_reserved_data_blocks = 0;
        ei->i_reserved_meta_blocks = 0;
        ei->i_allocated_meta_blocks = 0;
@@ -859,13 +874,15 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
        ei->i_reserved_quota = 0;
 #endif
        ei->jinode = NULL;
-       INIT_LIST_HEAD(&ei->i_completed_io_list);
+       INIT_LIST_HEAD(&ei->i_rsv_conversion_list);
+       INIT_LIST_HEAD(&ei->i_unrsv_conversion_list);
        spin_lock_init(&ei->i_completed_io_lock);
        ei->i_sync_tid = 0;
        ei->i_datasync_tid = 0;
        atomic_set(&ei->i_ioend_count, 0);
        atomic_set(&ei->i_unwritten, 0);
-       INIT_WORK(&ei->i_unwritten_work, ext4_end_io_work);
+       INIT_WORK(&ei->i_rsv_conversion_work, ext4_end_io_rsv_work);
+       INIT_WORK(&ei->i_unrsv_conversion_work, ext4_end_io_unrsv_work);
 
        return &ei->vfs_inode;
 }
@@ -1093,6 +1110,7 @@ static const struct super_operations ext4_nojournal_sops = {
        .dirty_inode    = ext4_dirty_inode,
        .drop_inode     = ext4_drop_inode,
        .evict_inode    = ext4_evict_inode,
+       .sync_fs        = ext4_sync_fs_nojournal,
        .put_super      = ext4_put_super,
        .statfs         = ext4_statfs,
        .remount_fs     = ext4_remount,
@@ -1908,7 +1926,6 @@ static int ext4_fill_flex_info(struct super_block *sb)
        struct ext4_sb_info *sbi = EXT4_SB(sb);
        struct ext4_group_desc *gdp = NULL;
        ext4_group_t flex_group;
-       unsigned int groups_per_flex = 0;
        int i, err;
 
        sbi->s_log_groups_per_flex = sbi->s_es->s_log_groups_per_flex;
@@ -1916,7 +1933,6 @@ static int ext4_fill_flex_info(struct super_block *sb)
                sbi->s_log_groups_per_flex = 0;
                return 1;
        }
-       groups_per_flex = 1U << sbi->s_log_groups_per_flex;
 
        err = ext4_alloc_flex_bg_array(sb, sbi->s_groups_count);
        if (err)
@@ -3785,7 +3801,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        sbi->s_err_report.data = (unsigned long) sb;
 
        /* Register extent status tree shrinker */
-       ext4_es_register_shrinker(sb);
+       ext4_es_register_shrinker(sbi);
 
        err = percpu_counter_init(&sbi->s_freeclusters_counter,
                        ext4_count_free_clusters(sb));
@@ -3936,12 +3952,20 @@ no_journal:
         * The maximum number of concurrent works can be high and
         * concurrency isn't really necessary.  Limit it to 1.
         */
-       EXT4_SB(sb)->dio_unwritten_wq =
-               alloc_workqueue("ext4-dio-unwritten", WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
-       if (!EXT4_SB(sb)->dio_unwritten_wq) {
-               printk(KERN_ERR "EXT4-fs: failed to create DIO workqueue\n");
+       EXT4_SB(sb)->rsv_conversion_wq =
+               alloc_workqueue("ext4-rsv-conversion", WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
+       if (!EXT4_SB(sb)->rsv_conversion_wq) {
+               printk(KERN_ERR "EXT4-fs: failed to create workqueue\n");
                ret = -ENOMEM;
-               goto failed_mount_wq;
+               goto failed_mount4;
+       }
+
+       EXT4_SB(sb)->unrsv_conversion_wq =
+               alloc_workqueue("ext4-unrsv-conversion", WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
+       if (!EXT4_SB(sb)->unrsv_conversion_wq) {
+               printk(KERN_ERR "EXT4-fs: failed to create workqueue\n");
+               ret = -ENOMEM;
+               goto failed_mount4;
        }
 
        /*
@@ -4095,14 +4119,17 @@ failed_mount4a:
        sb->s_root = NULL;
 failed_mount4:
        ext4_msg(sb, KERN_ERR, "mount failed");
-       destroy_workqueue(EXT4_SB(sb)->dio_unwritten_wq);
+       if (EXT4_SB(sb)->rsv_conversion_wq)
+               destroy_workqueue(EXT4_SB(sb)->rsv_conversion_wq);
+       if (EXT4_SB(sb)->unrsv_conversion_wq)
+               destroy_workqueue(EXT4_SB(sb)->unrsv_conversion_wq);
 failed_mount_wq:
        if (sbi->s_journal) {
                jbd2_journal_destroy(sbi->s_journal);
                sbi->s_journal = NULL;
        }
 failed_mount3:
-       ext4_es_unregister_shrinker(sb);
+       ext4_es_unregister_shrinker(sbi);
        del_timer(&sbi->s_err_report);
        if (sbi->s_flex_groups)
                ext4_kvfree(sbi->s_flex_groups);
@@ -4538,19 +4565,52 @@ static int ext4_sync_fs(struct super_block *sb, int wait)
 {
        int ret = 0;
        tid_t target;
+       bool needs_barrier = false;
        struct ext4_sb_info *sbi = EXT4_SB(sb);
 
        trace_ext4_sync_fs(sb, wait);
-       flush_workqueue(sbi->dio_unwritten_wq);
+       flush_workqueue(sbi->rsv_conversion_wq);
+       flush_workqueue(sbi->unrsv_conversion_wq);
        /*
         * Writeback quota in non-journalled quota case - journalled quota has
         * no dirty dquots
         */
        dquot_writeback_dquots(sb, -1);
+       /*
+        * Data writeback is possible w/o journal transaction, so barrier must
+        * being sent at the end of the function. But we can skip it if
+        * transaction_commit will do it for us.
+        */
+       target = jbd2_get_latest_transaction(sbi->s_journal);
+       if (wait && sbi->s_journal->j_flags & JBD2_BARRIER &&
+           !jbd2_trans_will_send_data_barrier(sbi->s_journal, target))
+               needs_barrier = true;
+
        if (jbd2_journal_start_commit(sbi->s_journal, &target)) {
                if (wait)
-                       jbd2_log_wait_commit(sbi->s_journal, target);
+                       ret = jbd2_log_wait_commit(sbi->s_journal, target);
        }
+       if (needs_barrier) {
+               int err;
+               err = blkdev_issue_flush(sb->s_bdev, GFP_KERNEL, NULL);
+               if (!ret)
+                       ret = err;
+       }
+
+       return ret;
+}
+
+static int ext4_sync_fs_nojournal(struct super_block *sb, int wait)
+{
+       int ret = 0;
+
+       trace_ext4_sync_fs(sb, wait);
+       flush_workqueue(EXT4_SB(sb)->rsv_conversion_wq);
+       flush_workqueue(EXT4_SB(sb)->unrsv_conversion_wq);
+       dquot_writeback_dquots(sb, -1);
+       if (wait && test_opt(sb, BARRIER))
+               ret = blkdev_issue_flush(sb->s_bdev, GFP_KERNEL, NULL);
+
        return ret;
 }