2022-04-01 22:34 GMT+09:00, Al Viro <viro@xxxxxxxxxxxxxxxxxx>: > On Fri, Apr 01, 2022 at 09:52:09PM +0900, Namjae Jeon wrote: >> > take source and new parent and do the following: >> > >> > if (READ_ONCE(source->d_parent) == new_parent) { >> > inode_lock_nested(new_parent->d_inode, I_MUTEX_PARENT); >> > if (likely(source->d_parent == new_parent)) >> > return NULL; > and > inode_unlock(new_parent->d_inode); > to do locking in proper order... Okay, Let me check it with these codes. Thanks for sharing the codes! > >> > } >> > // fuck that, looks like a cross-rename one. >> > mutex_lock(&source->d_sb->s_vfs_rename_mutex); >> > // now all ->d_parent are stable >> > if (unlikely(source->d_parent == new_parent)) { >> > inode_lock_nested(new_parent->d_inode, I_MUTEX_PARENT); >> > // we want the same rules as for lock_rename() >> > mutex_unlock(&source->d_sb->s_vfs_rename_mutex); >> > return NULL; >> > } >> > // cross-directory it is... >> > same as lock_rename() after having grabbed ->s_vfs_rename_mutex > > // p1 != p2, p1->d_sb == p2->d_sb, p1->d_sb->s_vfs_rename_mutex held > static struct dentry *lock_two_directories(struct dentry *p1, struct dentry > *p2) > { > struct dentry *p; > > p = d_ancestor(p2, p1); > if (p) { > inode_lock_nested(p2->d_inode, I_MUTEX_PARENT); > inode_lock_nested(p1->d_inode, I_MUTEX_CHILD); > return p; > } > > p = d_ancestor(p1, p2); > if (p) { > inode_lock_nested(p1->d_inode, I_MUTEX_PARENT); > inode_lock_nested(p2->d_inode, I_MUTEX_CHILD); > return p; > } > > inode_lock_nested(p1->d_inode, I_MUTEX_PARENT); > inode_lock_nested(p2->d_inode, I_MUTEX_PARENT2); > return NULL; > } > > struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) > { > if (p1 == p2) { > inode_lock_nested(p1->d_inode, I_MUTEX_PARENT); > return NULL; > } > > mutex_lock(&p1->d_sb->s_vfs_rename_mutex); > return lock_two_directories(p1, p2); > } > > struct dentry *lock_rename_child(struct dentry *c1, struct dentry *p2) > { > if (READ_ONCE(c1->d_parent) == p2) { > inode_lock_nested(p2->d_inode, I_MUTEX_PARENT); > if (likely(c1->d_parent == p2)) > return NULL; > > // too bad, we'd raced with another rename > inode_unlock(p2->d_inode); > } > > // looks like it's cross-directory > mutex_lock(&c1->d_sb->s_vfs_rename_mutex); > > // recheck, now that ->d_parent is stable > if (likely(c1->d_parent != p2)) > return lock_two_directories(c1->d_parent, p2); > > // it's not cross-directory, after all - raced with another rename > inode_lock_nested(p2->d_inode, I_MUTEX_PARENT); > // drop ->s_vfs_rename_mutex, so we won't confuse unlock_rename() > // note that locked p2 alone is enough to prevent moves to or from > // p2, so c1->d_parent will remain p2 until we unlock p2 > mutex_unlock(&c1->d_sb->s_vfs_rename_mutex); > return NULL; > } >