Skip to content

Commit 56846bf

Browse files
Sebastian Andrzej Siewiorjnettlet
authored andcommitted
fs/dcache: disable preemption on i_dir_seq's write side
i_dir_seq is an opencoded seqcounter. Based on the code it looks like we could have two writers in parallel despite the fact that the d_lock is held. The problem is that during the write process on RT the preemption is still enabled and if this process is interrupted by a reader with RT priority then we lock up. To avoid that lock up I am disabling the preemption during the update. The rename of i_dir_seq is here to ensure to catch new write sides in future. Cc: [email protected] Reported-by: [email protected] Signed-off-by: Sebastian Andrzej Siewior <[email protected]> Signed-off-by: Steven Rostedt (VMware) <[email protected]>
1 parent 5dc4f7f commit 56846bf

File tree

4 files changed

+13
-9
lines changed

4 files changed

+13
-9
lines changed

fs/dcache.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2442,17 +2442,19 @@ EXPORT_SYMBOL(d_rehash);
24422442
static inline unsigned start_dir_add(struct inode *dir)
24432443
{
24442444

2445+
preempt_disable_rt();
24452446
for (;;) {
2446-
unsigned n = dir->i_dir_seq;
2447-
if (!(n & 1) && cmpxchg(&dir->i_dir_seq, n, n + 1) == n)
2447+
unsigned n = dir->__i_dir_seq;
2448+
if (!(n & 1) && cmpxchg(&dir->__i_dir_seq, n, n + 1) == n)
24482449
return n;
24492450
cpu_relax();
24502451
}
24512452
}
24522453

24532454
static inline void end_dir_add(struct inode *dir, unsigned n)
24542455
{
2455-
smp_store_release(&dir->i_dir_seq, n + 2);
2456+
smp_store_release(&dir->__i_dir_seq, n + 2);
2457+
preempt_enable_rt();
24562458
}
24572459

24582460
static void d_wait_lookup(struct dentry *dentry)
@@ -2488,7 +2490,7 @@ struct dentry *d_alloc_parallel(struct dentry *parent,
24882490

24892491
retry:
24902492
rcu_read_lock();
2491-
seq = smp_load_acquire(&parent->d_inode->i_dir_seq);
2493+
seq = smp_load_acquire(&parent->d_inode->__i_dir_seq);
24922494
r_seq = read_seqbegin(&rename_lock);
24932495
dentry = __d_lookup_rcu(parent, name, &d_seq);
24942496
if (unlikely(dentry)) {
@@ -2516,7 +2518,7 @@ struct dentry *d_alloc_parallel(struct dentry *parent,
25162518
}
25172519

25182520
hlist_bl_lock(b);
2519-
if (unlikely(READ_ONCE(parent->d_inode->i_dir_seq) != seq)) {
2521+
if (unlikely(READ_ONCE(parent->d_inode->__i_dir_seq) != seq)) {
25202522
hlist_bl_unlock(b);
25212523
rcu_read_unlock();
25222524
goto retry;

fs/inode.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode)
153153
inode->i_bdev = NULL;
154154
inode->i_cdev = NULL;
155155
inode->i_link = NULL;
156-
inode->i_dir_seq = 0;
156+
inode->__i_dir_seq = 0;
157157
inode->i_rdev = 0;
158158
inode->dirtied_when = 0;
159159

fs/libfs.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ static struct dentry *next_positive(struct dentry *parent,
8989
struct list_head *from,
9090
int count)
9191
{
92-
unsigned *seq = &parent->d_inode->i_dir_seq, n;
92+
unsigned *seq = &parent->d_inode->__i_dir_seq, n;
9393
struct dentry *res;
9494
struct list_head *p;
9595
bool skipped;
@@ -122,8 +122,9 @@ static struct dentry *next_positive(struct dentry *parent,
122122
static void move_cursor(struct dentry *cursor, struct list_head *after)
123123
{
124124
struct dentry *parent = cursor->d_parent;
125-
unsigned n, *seq = &parent->d_inode->i_dir_seq;
125+
unsigned n, *seq = &parent->d_inode->__i_dir_seq;
126126
spin_lock(&parent->d_lock);
127+
preempt_disable_rt();
127128
for (;;) {
128129
n = *seq;
129130
if (!(n & 1) && cmpxchg(seq, n, n + 1) == n)
@@ -136,6 +137,7 @@ static void move_cursor(struct dentry *cursor, struct list_head *after)
136137
else
137138
list_add_tail(&cursor->d_child, &parent->d_subdirs);
138139
smp_store_release(seq, n + 2);
140+
preempt_enable_rt();
139141
spin_unlock(&parent->d_lock);
140142
}
141143

include/linux/fs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,7 @@ struct inode {
689689
struct block_device *i_bdev;
690690
struct cdev *i_cdev;
691691
char *i_link;
692-
unsigned i_dir_seq;
692+
unsigned __i_dir_seq;
693693
};
694694

695695
__u32 i_generation;

0 commit comments

Comments
 (0)