• Jan Kara's avatar
    buffer_head: fix private_list handling · 535ee2fb
    Jan Kara authored
    There are two possible races in handling of private_list in buffer cache.
    
    1) When fsync_buffers_list() processes a private_list, it clears
       b_assoc_mapping and moves buffer to its private list.  Now
       drop_buffers() comes, sees a buffer is on list so it calls
       __remove_assoc_queue() which complains about b_assoc_mapping being
       cleared (as it cannot propagate possible IO error).  This race has been
       actually observed in the wild.
    
    2) When fsync_buffers_list() processes a private_list,
       mark_buffer_dirty_inode() can be called on bh which is already on the
       private list of fsync_buffers_list().  As buffer is on some list (note
       that the check is performed without private_lock), it is not readded to
       the mapping's private_list and after fsync_buffers_list() finishes, we
       have a dirty buffer which should be on private_list but it isn't.  This
       race has not been reported, probably because most (but not all) callers
       of mark_buffer_dirty_inode() hold i_mutex and thus are serialized with
       fsync().
    
    Fix these issues by not clearing b_assoc_map when fsync_buffers_list()
    moves buffer to a dedicated list and by reinserting buffer in private_list
    when it is found dirty after we have submitted buffer for IO.  We also
    change the tests whether a buffer is on a private list from
    !list_empty(&bh->b_assoc_buffers) to bh->b_assoc_map so that they are
    single word reads and hence lockless checks are safe.
    Signed-off-by: default avatarJan Kara <jack@suse.cz>
    Cc: Nick Piggin <npiggin@suse.de>
    Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
    Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    535ee2fb
buffer.c 86.5 KB