• Akira Fujita's avatar
    ext4: fix lock order problem in ext4_move_extents() · fc04cb49
    Akira Fujita authored
    ext4_move_extents() checks the logical block contiguousness
    of original file with ext4_find_extent() and mext_next_extent().
    Therefore the extent which ext4_ext_path structure indicates
    must not be changed between above functions.
    
    But in current implementation, there is no i_data_sem protection
    between ext4_ext_find_extent() and mext_next_extent().  So the extent
    which ext4_ext_path structure indicates may be overwritten by
    delalloc.  As a result, ext4_move_extents() will exchange wrong blocks
    between original and donor files.  I change the place where
    acquire/release i_data_sem to solve this problem.
    
    Moreover, I changed move_extent_per_page() to start transaction first,
    and then acquire i_data_sem.  Without this change, there is a
    possibility of the deadlock between mmap() and ext4_move_extents():
    
    * NOTE: "A", "B" and "C" mean different processes
    
    A-1: ext4_ext_move_extents() acquires i_data_sem of two inodes.
    
    B:   do_page_fault() starts the transaction (T),
         and then tries to acquire i_data_sem.
         But process "A" is already holding it, so it is kept waiting.
    
    C:   While "A" and "B" running, kjournald2 tries to commit transaction (T)
         but it is under updating, so kjournald2 waits for it.
    
    A-2: Call ext4_journal_start with holding i_data_sem,
         but transaction (T) is locked.
    Signed-off-by: default avatarAkira Fujita <a-fujita@rs.jp.nec.com>
    Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
    fc04cb49
move_extent.c 41 KB