Commit be4f27d3 authored by Yongqiang Yang's avatar Yongqiang Yang Committed by Theodore Ts'o

ext4: allow an active handle to be started when freezing

ext4_journal_start_sb() should not prevent an active handle from being
started due to s_frozen.  Otherwise, deadlock is easy to happen, below
is a situation.

================================================
     freeze         |       truncate
================================================
                    |  ext4_ext_truncate()
    freeze_super()  |   starts a handle
    sets s_frozen   |
                    |  ext4_ext_truncate()
                    |  holds i_data_sem
  ext4_freeze()     |
  waits for updates |
                    |  ext4_free_blocks()
                    |  calls dquot_free_block()
                    |
                    |  dquot_free_blocks()
                    |  calls ext4_dirty_inode()
                    |
                    |  ext4_dirty_inode()
                    |  trys to start an active
                    |  handle
                    |
                    |  block due to s_frozen
================================================
Signed-off-by: default avatarYongqiang Yang <xiaoqiangnk@gmail.com>
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
Reported-by: default avatarAmir Goldstein <amir73il@users.sf.net>
Reviewed-by: default avatarJan Kara <jack@suse.cz>
Reviewed-by: default avatarAndreas Dilger <adilger@dilger.ca>
parent 0893ed45
...@@ -242,27 +242,44 @@ static void ext4_put_nojournal(handle_t *handle) ...@@ -242,27 +242,44 @@ static void ext4_put_nojournal(handle_t *handle)
* journal_end calls result in the superblock being marked dirty, so * journal_end calls result in the superblock being marked dirty, so
* that sync() will call the filesystem's write_super callback if * that sync() will call the filesystem's write_super callback if
* appropriate. * appropriate.
*
* To avoid j_barrier hold in userspace when a user calls freeze(),
* ext4 prevents a new handle from being started by s_frozen, which
* is in an upper layer.
*/ */
handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks) handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks)
{ {
journal_t *journal; journal_t *journal;
handle_t *handle;
if (sb->s_flags & MS_RDONLY) if (sb->s_flags & MS_RDONLY)
return ERR_PTR(-EROFS); return ERR_PTR(-EROFS);
journal = EXT4_SB(sb)->s_journal;
handle = ext4_journal_current_handle();
/*
* If a handle has been started, it should be allowed to
* finish, otherwise deadlock could happen between freeze
* and others(e.g. truncate) due to the restart of the
* journal handle if the filesystem is forzen and active
* handles are not stopped.
*/
if (!handle)
vfs_check_frozen(sb, SB_FREEZE_TRANS); vfs_check_frozen(sb, SB_FREEZE_TRANS);
/* Special case here: if the journal has aborted behind our
if (!journal)
return ext4_get_nojournal();
/*
* Special case here: if the journal has aborted behind our
* backs (eg. EIO in the commit thread), then we still need to * backs (eg. EIO in the commit thread), then we still need to
* take the FS itself readonly cleanly. */ * take the FS itself readonly cleanly.
journal = EXT4_SB(sb)->s_journal; */
if (journal) {
if (is_journal_aborted(journal)) { if (is_journal_aborted(journal)) {
ext4_abort(sb, "Detected aborted journal"); ext4_abort(sb, "Detected aborted journal");
return ERR_PTR(-EROFS); return ERR_PTR(-EROFS);
} }
return jbd2_journal_start(journal, nblocks); return jbd2_journal_start(journal, nblocks);
}
return ext4_get_nojournal();
} }
/* /*
...@@ -4146,6 +4163,11 @@ static int ext4_sync_fs(struct super_block *sb, int wait) ...@@ -4146,6 +4163,11 @@ static int ext4_sync_fs(struct super_block *sb, int wait)
/* /*
* LVM calls this function before a (read-only) snapshot is created. This * LVM calls this function before a (read-only) snapshot is created. This
* gives us a chance to flush the journal completely and mark the fs clean. * gives us a chance to flush the journal completely and mark the fs clean.
*
* Note that only this function cannot bring a filesystem to be in a clean
* state independently, because ext4 prevents a new handle from being started
* by @sb->s_frozen, which stays in an upper layer. It thus needs help from
* the upper layer.
*/ */
static int ext4_freeze(struct super_block *sb) static int ext4_freeze(struct super_block *sb)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment