• Al Viro's avatar
    Teach shrink_dcache_parent() to cope with mixed-filesystem shrink lists · 9bdebc2b
    Al Viro authored
    Currently, running into a shrink list that contains dentries from different
    filesystems can cause several unpleasant things for shrink_dcache_parent()
    and for umount(2).
    
    The first problem is that there's a window during shrink_dentry_list() between
    __dentry_kill() takes a victim out and dropping reference to its parent.  During
    that window the parent looks like a genuine busy dentry.  shrink_dcache_parent()
    (or, worse yet, shrink_dcache_for_umount()) coming at that time will see no
    eviction candidates and no indication that it needs to wait for some
    shrink_dentry_list() to proceed further.
    
    That applies for any shrink list that might intersect with the subtree we are
    trying to shrink; the only reason it does not blow on umount(2) in the mainline
    is that we unregister the memory shrinker before hitting shrink_dcache_for_umount().
    
    Another problem happens if something in a mixed-filesystem shrink list gets
    be stuck in e.g. iput(), getting umount of unrelated fs to spin waiting for
    the stuck shrinker to get around to our dentries.
    
    Solution:
            1) have shrink_dentry_list() decrement the parent's refcount and
    make sure it's on a shrink list (ours unless it already had been on some
    other) before calling __dentry_kill().  That eliminates the window when
    shrink_dcache_parent() would've blown past the entire subtree without
    noticing anything with zero refcount not on shrink lists.
    	2) when shrink_dcache_parent() has found no eviction candidates,
    but some dentries are still sitting on shrink lists, rather than
    repeating the scan in hope that shrinkers have progressed, scan looking
    for something on shrink lists with zero refcount.  If such a thing is
    found, grab rcu_read_lock() and stop the scan, with caller locking
    it for eviction, dropping out of RCU and doing __dentry_kill(), with
    the same treatment for parent as shrink_dentry_list() would do.
    
    Note that right now mixed-filesystem shrink lists do not occur, so this
    is not a mainline bug.  Howevere, there's a bunch of uses for such
    beasts (e.g. the "try and evict everything we can out of given page"
    patches; there are potential uses in mount-related code, considerably
    simplifying the life in fs/namespace.c, etc.)
    Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
    9bdebc2b
dcache.c 84 KB