Skip to content

Commit 846f997

Browse files
ebiedermtorvalds
authored andcommitted
sysfs: Add lockdep annotations for the sysfs active reference
Holding locks over device_del -> kobject_del -> sysfs_deactivate can cause deadlocks if those same locks are grabbed in sysfs show or store methods. The I model s_active count + completion as a sleeping read/write lock. I describe to lockdep sysfs_get_active as a read_trylock, sysfs_put_active as a read_unlock, and sysfs_deactivate as a write_lock and write_unlock pair. This seems to capture the essence for purposes of finding deadlocks, and in my testing gives finds real issues and ignores non-issues. This brings us back to holding locks over kobject_del is a problem that ideally we should find a way of addressing, but at least lockdep can tell us about the problems instead of requiring developers to debug rare strange system deadlocks, that happen when sysfs files are removed while being written to. Signed-off-by: Eric W. Biederman <[email protected]> Acked-by: Tejun Heo <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 3e27249 commit 846f997

File tree

2 files changed

+27
-2
lines changed

2 files changed

+27
-2
lines changed

fs/sysfs/dir.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,10 @@ static struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd)
106106
return NULL;
107107

108108
t = atomic_cmpxchg(&sd->s_active, v, v + 1);
109-
if (likely(t == v))
109+
if (likely(t == v)) {
110+
rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_);
110111
return sd;
112+
}
111113
if (t < 0)
112114
return NULL;
113115

@@ -130,6 +132,7 @@ static void sysfs_put_active(struct sysfs_dirent *sd)
130132
if (unlikely(!sd))
131133
return;
132134

135+
rwsem_release(&sd->dep_map, 1, _RET_IP_);
133136
v = atomic_dec_return(&sd->s_active);
134137
if (likely(v != SD_DEACTIVATED_BIAS))
135138
return;
@@ -194,15 +197,21 @@ static void sysfs_deactivate(struct sysfs_dirent *sd)
194197
BUG_ON(sd->s_sibling || !(sd->s_flags & SYSFS_FLAG_REMOVED));
195198
sd->s_sibling = (void *)&wait;
196199

200+
rwsem_acquire(&sd->dep_map, 0, 0, _RET_IP_);
197201
/* atomic_add_return() is a mb(), put_active() will always see
198202
* the updated sd->s_sibling.
199203
*/
200204
v = atomic_add_return(SD_DEACTIVATED_BIAS, &sd->s_active);
201205

202-
if (v != SD_DEACTIVATED_BIAS)
206+
if (v != SD_DEACTIVATED_BIAS) {
207+
lock_contended(&sd->dep_map, _RET_IP_);
203208
wait_for_completion(&wait);
209+
}
204210

205211
sd->s_sibling = NULL;
212+
213+
lock_acquired(&sd->dep_map, _RET_IP_);
214+
rwsem_release(&sd->dep_map, 1, _RET_IP_);
206215
}
207216

208217
static int sysfs_alloc_ino(ino_t *pino)
@@ -345,6 +354,7 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
345354

346355
atomic_set(&sd->s_count, 1);
347356
atomic_set(&sd->s_active, 0);
357+
sysfs_dirent_init_lockdep(sd);
348358

349359
sd->s_name = name;
350360
sd->s_mode = mode;

fs/sysfs/sysfs.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* This file is released under the GPLv2.
99
*/
1010

11+
#include <linux/lockdep.h>
1112
#include <linux/fs.h>
1213

1314
struct sysfs_open_dirent;
@@ -50,6 +51,9 @@ struct sysfs_inode_attrs {
5051
struct sysfs_dirent {
5152
atomic_t s_count;
5253
atomic_t s_active;
54+
#ifdef CONFIG_DEBUG_LOCK_ALLOC
55+
struct lockdep_map dep_map;
56+
#endif
5357
struct sysfs_dirent *s_parent;
5458
struct sysfs_dirent *s_sibling;
5559
const char *s_name;
@@ -84,6 +88,17 @@ static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
8488
return sd->s_flags & SYSFS_TYPE_MASK;
8589
}
8690

91+
#ifdef CONFIG_DEBUG_LOCK_ALLOC
92+
#define sysfs_dirent_init_lockdep(sd) \
93+
do { \
94+
static struct lock_class_key __key; \
95+
\
96+
lockdep_init_map(&sd->dep_map, "s_active", &__key, 0); \
97+
} while(0)
98+
#else
99+
#define sysfs_dirent_init_lockdep(sd) do {} while(0)
100+
#endif
101+
87102
/*
88103
* Context structure to be used while adding/removing nodes.
89104
*/

0 commit comments

Comments
 (0)