Skip to content
  • Al Viro's avatar
    make non-exchanging __d_move() copy ->d_parent rather than swap them · 076515fc
    Al Viro authored
    
    
    Currently d_move(from, to) does the following:
    	* name/parent of from <- old name/parent of to, from hashed there
    	* to is unhashed
    	* name of to is preserved
    	* if from used to be detached, to gets detached
    	* if from used to be attached, parent of to <- old parent of from.
    
    That's both user-visibly bogus and complicates reasoning a lot.
    Much saner semantics would be
    	* name/parent of from <- name/parent of to, from hashed there.
    	* to is unhashed
    	* name/parent of to is unchanged.
    
    The price, of course, is that old parent of from might lose a reference.
    However,
    	* all potentially cross-directory callers of d_move() have both
    parents pinned directly; typically, dentries themselves are grabbed
    only after we have grabbed and locked both parents.  IOW, the decrement
    of old parent's refcount in case of d_move() won't reach zero.
    	* __d_move() from d_splice_alias() is done to detached alias.
    No refcount decrements in that case
    	* __d_move() from __d_unalias() *can* get the refcount to zero.
    So let's grab a reference to alias' old parent before calling __d_unalias()
    and dput() it after we'd dropped rename_lock.
    
    That does make d_splice_alias() potentially blocking.  However, it has
    no callers in non-sleepable contexts (and the case where we'd grown
    that dget/dput pair is _very_ rare, so performance is not an issue).
    
    Another thing that needs adjustment is unlocking in the end of __d_move();
    folded it in.  And cleaned the remnants of bogus ordering from the
    "lock them in the beginning" counterpart - it's never been right and
    now (well, for 7 years now) we have that thing always serialized on
    rename_lock anyway.
    
    Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
    076515fc