• Hugh Dickins's avatar
    tmpfs: fix shmem_swaplist races · 1b1b32f2
    Hugh Dickins authored
    Intensive swapoff testing shows shmem_unuse spinning on an entry in
    shmem_swaplist pointing to itself: how does that come about?  Days pass...
    
    First guess is this: shmem_delete_inode tests list_empty without taking the
    global mutex (so the swapping case doesn't slow down the common case); but
    there's an instant in shmem_unuse_inode's list_move_tail when the list entry
    may appear empty (a rare case, because it's actually moving the head not the
    the list member).  So there's a danger of leaving the inode on the swaplist
    when it's freed, then reinitialized to point to itself when reused.  Fix that
    by skipping the list_move_tail when it's a no-op, which happens to plug this.
    
    But this same spinning then surfaces on another machine.  Ah, I'd never
    suspected it, but shmem_writepage's swaplist manipulation is unsafe: though we
    still hold page lock, which would hold off inode deletion if the page were in
    pagecache, it doesn't hold off once it's in swapcache (free_swap_and_cache
    doesn't wait on locked pages).  Hmm: we could put the the inode on swaplist
    earlier, but then shmem_unuse_inode could never prune unswapped inodes.
    
    Fix this with an igrab before dropping info->lock, as in shmem_unuse_inode;
    though I am a little uneasy about the iput which has to follow - it works, and
    I see nothing wrong with it, but it is surprising that shmem inode deletion
    may now occur below shmem_writepage.  Revisit this fix later?
    
    And while we're looking at these races: the way shmem_unuse tests swapped
    without holding info->lock looks unsafe, if we've more than one swap area: a
    racing shmem_writepage on another page of the same inode could be putting it
    in swapcache, just as we're deciding to remove the inode from swaplist -
    there's a danger of going on swap without being listed, so a later swapoff
    would hang, being unable to locate the entry.  Move that test and removal down
    into shmem_unuse_inode, once info->lock is held.
    Signed-off-by: default avatarHugh Dickins <hugh@veritas.com>
    Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
    Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    1b1b32f2
shmem.c 64.6 KB