ext4: fix potential deadlock in ext4_nonda_switch()
[firefly-linux-kernel-4.4.55.git] / fs / ext4 / inode.c
index ca76b5ed6c9e93d11afbc4d6350089b26943d9bc..0a31197590d79200db7b7f7cb5b176eb9ce502e7 100644 (file)
@@ -2462,6 +2462,16 @@ static int ext4_nonda_switch(struct super_block *sb)
        free_blocks  = EXT4_C2B(sbi,
                percpu_counter_read_positive(&sbi->s_freeclusters_counter));
        dirty_blocks = percpu_counter_read_positive(&sbi->s_dirtyclusters_counter);
+       /*
+        * Start pushing delalloc when 1/2 of free blocks are dirty.
+        */
+       if (dirty_blocks && (free_blocks < 2 * dirty_blocks) &&
+           !writeback_in_progress(sb->s_bdi) &&
+           down_read_trylock(&sb->s_umount)) {
+               writeback_inodes_sb(sb, WB_REASON_FS_FREE_SPACE);
+               up_read(&sb->s_umount);
+       }
+
        if (2 * free_blocks < 3 * dirty_blocks ||
                free_blocks < (dirty_blocks + EXT4_FREECLUSTERS_WATERMARK)) {
                /*
@@ -2470,13 +2480,6 @@ static int ext4_nonda_switch(struct super_block *sb)
                 */
                return 1;
        }
-       /*
-        * Even if we don't switch but are nearing capacity,
-        * start pushing delalloc when 1/2 of free blocks are dirty.
-        */
-       if (free_blocks < 2 * dirty_blocks)
-               writeback_inodes_sb_if_idle(sb, WB_REASON_FS_FREE_SPACE);
-
        return 0;
 }