Skip to content

Commit f4acfcd

Browse files
committed
debugfs: annotate debugfs handlers vs. removal with lockdep
When you take a lock in a debugfs handler but also try to remove the debugfs file under that lock, things can deadlock since the removal has to wait for all users to finish. Add lockdep annotations in debugfs_file_get()/_put() to catch such issues. Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
1 parent 0ed04a1 commit f4acfcd

3 files changed

Lines changed: 28 additions & 0 deletions

File tree

fs/debugfs/file.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ int debugfs_file_get(struct dentry *dentry)
108108
kfree(fsd);
109109
fsd = READ_ONCE(dentry->d_fsdata);
110110
}
111+
#ifdef CONFIG_LOCKDEP
112+
fsd->lock_name = kasprintf(GFP_KERNEL, "debugfs:%pd", dentry);
113+
lockdep_register_key(&fsd->key);
114+
lockdep_init_map(&fsd->lockdep_map, fsd->lock_name ?: "debugfs",
115+
&fsd->key, 0);
116+
#endif
111117
}
112118

113119
/*
@@ -124,6 +130,8 @@ int debugfs_file_get(struct dentry *dentry)
124130
if (!refcount_inc_not_zero(&fsd->active_users))
125131
return -EIO;
126132

133+
lock_map_acquire_read(&fsd->lockdep_map);
134+
127135
return 0;
128136
}
129137
EXPORT_SYMBOL_GPL(debugfs_file_get);
@@ -141,6 +149,8 @@ void debugfs_file_put(struct dentry *dentry)
141149
{
142150
struct debugfs_fsdata *fsd = READ_ONCE(dentry->d_fsdata);
143151

152+
lock_map_release(&fsd->lockdep_map);
153+
144154
if (refcount_dec_and_test(&fsd->active_users))
145155
complete(&fsd->active_users_drained);
146156
}

fs/debugfs/inode.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,14 @@ static void debugfs_release_dentry(struct dentry *dentry)
241241
if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)
242242
return;
243243

244+
/* check it wasn't a dir (no fsdata) or automount (no real_fops) */
245+
if (fsd && fsd->real_fops) {
246+
#ifdef CONFIG_LOCKDEP
247+
lockdep_unregister_key(&fsd->key);
248+
kfree(fsd->lock_name);
249+
#endif
250+
}
251+
244252
kfree(fsd);
245253
}
246254

@@ -744,6 +752,10 @@ static void __debugfs_file_removed(struct dentry *dentry)
744752
fsd = READ_ONCE(dentry->d_fsdata);
745753
if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)
746754
return;
755+
756+
lock_map_acquire(&fsd->lockdep_map);
757+
lock_map_release(&fsd->lockdep_map);
758+
747759
if (!refcount_dec_and_test(&fsd->active_users))
748760
wait_for_completion(&fsd->active_users_drained);
749761
}

fs/debugfs/internal.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#ifndef _DEBUGFS_INTERNAL_H_
99
#define _DEBUGFS_INTERNAL_H_
10+
#include <linux/lockdep.h>
1011

1112
struct file_operations;
1213

@@ -23,6 +24,11 @@ struct debugfs_fsdata {
2324
struct {
2425
refcount_t active_users;
2526
struct completion active_users_drained;
27+
#ifdef CONFIG_LOCKDEP
28+
struct lockdep_map lockdep_map;
29+
struct lock_class_key key;
30+
char *lock_name;
31+
#endif
2632
};
2733
};
2834
};

0 commit comments

Comments
 (0)