3) object removal.  Locking rules: caller locks parent, finds victim,
 locks victim and calls the method.  Locks are exclusive.
 
-4) rename() that is _not_ cross-directory.  Locking rules: caller locks
-the parent and finds source and target.  In case of exchange (with
-RENAME_EXCHANGE in flags argument) lock both.  In any case,
-if the target already exists, lock it.  If the source is a non-directory,
-lock it.  If we need to lock both, lock them in inode pointer order.
-Then call the method.  All locks are exclusive.
+4) rename() that is _not_ cross-directory.  Locking rules: caller locks the
+parent and finds source and target.  We lock both (provided they exist).  If we
+need to lock two inodes of different type (dir vs non-dir), we lock directory
+first.  If we need to lock two inodes of the same type, lock them in inode
+pointer order.  Then call the method.  All locks are exclusive.
 NB: we might get away with locking the source (and target in exchange
 case) shared.
 
 rules:
 
        * lock the filesystem
-       * lock parents in "ancestors first" order.
+       * lock parents in "ancestors first" order. If one is not ancestor of
+         the other, lock them in inode pointer order.
        * find source and target.
        * if old parent is equal to or is a descendent of target
          fail with -ENOTEMPTY
        * if new parent is equal to or is a descendent of source
          fail with -ELOOP
-       * If it's an exchange, lock both the source and the target.
-       * If the target exists, lock it.  If the source is a non-directory,
-         lock it.  If we need to lock both, do so in inode pointer order.
+       * Lock both the source and the target provided they exist. If we
+         need to lock two inodes of different type (dir vs non-dir), we lock
+         the directory first. If we need to lock two inodes of the same type,
+         lock them in inode pointer order.
        * call the method.
 
 All ->i_rwsem are taken exclusive.  Again, we might get away with locking
 
 Proof:
 
-       First of all, at any moment we have a partial ordering of the
-       objects - A < B iff A is an ancestor of B.
+       First of all, at any moment we have a linear ordering of the
+       objects - A < B iff (A is an ancestor of B) or (B is not an ancestor
+        of A and ptr(A) < ptr(B)).
 
        That ordering can change.  However, the following is true:
 
 
  *        sb->s_vfs_rename_mutex. We might be more accurate, but that's another
  *        story.
  *     c) we have to lock _four_ objects - parents and victim (if it exists),
- *        and source (if it is not a directory).
+ *        and source.
  *        And that - after we got ->i_mutex on parents (until then we don't know
  *        whether the target exists).  Solution: try to be smart with locking
  *        order for inodes.  We rely on the fact that tree topology may change
 
        take_dentry_name_snapshot(&old_name, old_dentry);
        dget(new_dentry);
-       if (!is_dir || (flags & RENAME_EXCHANGE))
-               lock_two_nondirectories(source, target);
-       else if (target)
-               inode_lock(target);
+       /*
+        * Lock all moved children. Moved directories may need to change parent
+        * pointer so they need the lock to prevent against concurrent
+        * directory changes moving parent pointer. For regular files we've
+        * historically always done this. The lockdep locking subclasses are
+        * somewhat arbitrary but RENAME_EXCHANGE in particular can swap
+        * regular files and directories so it's difficult to tell which
+        * subclasses to use.
+        */
+       lock_two_inodes(source, target, I_MUTEX_NORMAL, I_MUTEX_NONDIR2);
 
        error = -EPERM;
        if (IS_SWAPFILE(source) || (target && IS_SWAPFILE(target)))
                        d_exchange(old_dentry, new_dentry);
        }
 out:
-       if (!is_dir || (flags & RENAME_EXCHANGE))
-               unlock_two_nondirectories(source, target);
-       else if (target)
+       if (source)
+               inode_unlock(source);
+       if (target)
                inode_unlock(target);
        dput(new_dentry);
        if (!error) {