Re: [GIT PULL] ksmbd server fixes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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...

> > 	}
> > 	// 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;
}



[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux