Skip to content

Commit 9fd4523

Browse files
author
Al Viro
committed
add locked_recursive_removal()
simple_recursive_removal() assumes that parent is not locked and locks it when it finally gets to removing the victim itself. Usually that's what we want, but there are places where the parent is *already* locked and we need it to stay that way. In those cases simple_recursive_removal() would, of course, deadlock, so we have to play racy games with unlocking/relocking the parent around the call or open-code the entire thing. A better solution is to provide a variant that expects to be called with the parent already locked by the caller. Parent should be locked with I_MUTEX_PARENT, to avoid false positives from lockdep. Reviewed-by: Christian Brauner <brauner@kernel.org> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
1 parent 2a8061e commit 9fd4523

2 files changed

Lines changed: 23 additions & 4 deletions

File tree

fs/libfs.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -605,8 +605,9 @@ struct dentry *find_next_child(struct dentry *parent, struct dentry *prev)
605605
}
606606
EXPORT_SYMBOL(find_next_child);
607607

608-
void simple_recursive_removal(struct dentry *dentry,
609-
void (*callback)(struct dentry *))
608+
static void __simple_recursive_removal(struct dentry *dentry,
609+
void (*callback)(struct dentry *),
610+
bool locked)
610611
{
611612
struct dentry *this = dget(dentry);
612613
while (true) {
@@ -625,7 +626,8 @@ void simple_recursive_removal(struct dentry *dentry,
625626
victim = this;
626627
this = this->d_parent;
627628
inode = this->d_inode;
628-
inode_lock_nested(inode, I_MUTEX_CHILD);
629+
if (!locked || victim != dentry)
630+
inode_lock_nested(inode, I_MUTEX_CHILD);
629631
if (simple_positive(victim)) {
630632
d_invalidate(victim); // avoid lost mounts
631633
if (callback)
@@ -638,7 +640,8 @@ void simple_recursive_removal(struct dentry *dentry,
638640
inode_set_ctime_current(inode));
639641
if (d_is_dir(dentry))
640642
drop_nlink(inode);
641-
inode_unlock(inode);
643+
if (!locked)
644+
inode_unlock(inode);
642645
dput(dentry);
643646
return;
644647
}
@@ -647,8 +650,22 @@ void simple_recursive_removal(struct dentry *dentry,
647650
this = child;
648651
}
649652
}
653+
654+
void simple_recursive_removal(struct dentry *dentry,
655+
void (*callback)(struct dentry *))
656+
{
657+
return __simple_recursive_removal(dentry, callback, false);
658+
}
650659
EXPORT_SYMBOL(simple_recursive_removal);
651660

661+
/* caller holds parent directory with I_MUTEX_PARENT */
662+
void locked_recursive_removal(struct dentry *dentry,
663+
void (*callback)(struct dentry *))
664+
{
665+
return __simple_recursive_removal(dentry, callback, true);
666+
}
667+
EXPORT_SYMBOL(locked_recursive_removal);
668+
652669
static const struct super_operations simple_super_operations = {
653670
.statfs = simple_statfs,
654671
};

include/linux/fs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3595,6 +3595,8 @@ extern int simple_rename(struct mnt_idmap *, struct inode *,
35953595
unsigned int);
35963596
extern void simple_recursive_removal(struct dentry *,
35973597
void (*callback)(struct dentry *));
3598+
extern void locked_recursive_removal(struct dentry *,
3599+
void (*callback)(struct dentry *));
35983600
extern int noop_fsync(struct file *, loff_t, loff_t, int);
35993601
extern ssize_t noop_direct_IO(struct kiocb *iocb, struct iov_iter *iter);
36003602
extern int simple_empty(struct dentry *);

0 commit comments

Comments
 (0)