Skip to content
  • Nick Piggin's avatar
    mm: fix pagecache write deadlocks · 08291429
    Nick Piggin authored
    
    
    Modify the core write() code so that it won't take a pagefault while holding a
    lock on the pagecache page. There are a number of different deadlocks possible
    if we try to do such a thing:
    
    1.  generic_buffered_write
    2.   lock_page
    3.    prepare_write
    4.     unlock_page+vmtruncate
    5.     copy_from_user
    6.      mmap_sem(r)
    7.       handle_mm_fault
    8.        lock_page (filemap_nopage)
    9.    commit_write
    10.  unlock_page
    
    a. sys_munmap / sys_mlock / others
    b.  mmap_sem(w)
    c.   make_pages_present
    d.    get_user_pages
    e.     handle_mm_fault
    f.      lock_page (filemap_nopage)
    
    2,8	- recursive deadlock if page is same
    2,8;2,8	- ABBA deadlock is page is different
    2,6;b,f	- ABBA deadlock if page is same
    
    The solution is as follows:
    1.  If we find the destination page is uptodate, continue as normal, but use
        atomic usercopies which do not take pagefaults and do not zero the uncopied
        tail of the destination. The destination is already uptodate, so we can
        commit_write the full length even if there was a partial copy: it does not
        matter that the tail was not modified, because if it is dirtied and written
        back to disk it will not cause any problems (uptodate *means* that the
        destination page is as new or newer than the copy on disk).
    
    1a. The above requires that fault_in_pages_readable correctly returns access
        information, because atomic usercopies cannot distinguish between
        non-present pages in a readable mapping, from lack of a readable mapping.
    
    2.  If we find the destination page is non uptodate, unlock it (this could be
        made slightly more optimal), then allocate a temporary page to copy the
        source data into. Relock the destination page and continue with the copy.
        However, instead of a usercopy (which might take a fault), copy the data
        from the pinned temporary page via the kernel address space.
    
    (also, rename maxlen to seglen, because it was confusing)
    
    This increases the CPU/memory copy cost by almost 50% on the affected
    workloads. That will be solved by introducing a new set of pagecache write
    aops in a subsequent patch.
    
    Signed-off-by: default avatarNick Piggin <npiggin@suse.de>
    Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
    Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    08291429