Skip to content

Commit 200e340

Browse files
committed
Merge tag 'pull-work.dcache' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs dcache updates from Al Viro: "The main part here is making parallel lookups safe for RT - making sure preemption is disabled in start_dir_add()/ end_dir_add() sections (on non-RT it's automatic, on RT it needs to to be done explicitly) and moving wakeups from __d_lookup_done() inside of such to the end of those sections. Wakeups can be safely delayed for as long as ->d_lock on in-lookup dentry is held; proving that has caught a bug in d_add_ci() that allows memory corruption when sufficiently bogus ntfs (or case-insensitive xfs) image is mounted. Easily fixed, fortunately" * tag 'pull-work.dcache' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: fs/dcache: Move wakeup out of i_seq_dir write held region. fs/dcache: Move the wakeup from __d_lookup_done() to the caller. fs/dcache: Disable preemption on i_dir_seq write side on PREEMPT_RT d_add_ci(): make sure we don't miss d_lookup_done()
2 parents a782e86 + 50417d2 commit 200e340

File tree

2 files changed

+46
-17
lines changed

2 files changed

+46
-17
lines changed

fs/dcache.c

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2240,6 +2240,7 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode,
22402240
}
22412241
res = d_splice_alias(inode, found);
22422242
if (res) {
2243+
d_lookup_done(found);
22432244
dput(found);
22442245
return res;
22452246
}
@@ -2563,7 +2564,15 @@ EXPORT_SYMBOL(d_rehash);
25632564

25642565
static inline unsigned start_dir_add(struct inode *dir)
25652566
{
2566-
2567+
/*
2568+
* The caller holds a spinlock (dentry::d_lock). On !PREEMPT_RT
2569+
* kernels spin_lock() implicitly disables preemption, but not on
2570+
* PREEMPT_RT. So for RT it has to be done explicitly to protect
2571+
* the sequence count write side critical section against a reader
2572+
* or another writer preempting, which would result in a live lock.
2573+
*/
2574+
if (IS_ENABLED(CONFIG_PREEMPT_RT))
2575+
preempt_disable();
25672576
for (;;) {
25682577
unsigned n = dir->i_dir_seq;
25692578
if (!(n & 1) && cmpxchg(&dir->i_dir_seq, n, n + 1) == n)
@@ -2572,9 +2581,13 @@ static inline unsigned start_dir_add(struct inode *dir)
25722581
}
25732582
}
25742583

2575-
static inline void end_dir_add(struct inode *dir, unsigned n)
2584+
static inline void end_dir_add(struct inode *dir, unsigned int n,
2585+
wait_queue_head_t *d_wait)
25762586
{
25772587
smp_store_release(&dir->i_dir_seq, n + 2);
2588+
if (IS_ENABLED(CONFIG_PREEMPT_RT))
2589+
preempt_enable();
2590+
wake_up_all(d_wait);
25782591
}
25792592

25802593
static void d_wait_lookup(struct dentry *dentry)
@@ -2701,32 +2714,50 @@ struct dentry *d_alloc_parallel(struct dentry *parent,
27012714
}
27022715
EXPORT_SYMBOL(d_alloc_parallel);
27032716

2704-
void __d_lookup_done(struct dentry *dentry)
2717+
/*
2718+
* - Unhash the dentry
2719+
* - Retrieve and clear the waitqueue head in dentry
2720+
* - Return the waitqueue head
2721+
*/
2722+
static wait_queue_head_t *__d_lookup_unhash(struct dentry *dentry)
27052723
{
2706-
struct hlist_bl_head *b = in_lookup_hash(dentry->d_parent,
2707-
dentry->d_name.hash);
2724+
wait_queue_head_t *d_wait;
2725+
struct hlist_bl_head *b;
2726+
2727+
lockdep_assert_held(&dentry->d_lock);
2728+
2729+
b = in_lookup_hash(dentry->d_parent, dentry->d_name.hash);
27082730
hlist_bl_lock(b);
27092731
dentry->d_flags &= ~DCACHE_PAR_LOOKUP;
27102732
__hlist_bl_del(&dentry->d_u.d_in_lookup_hash);
2711-
wake_up_all(dentry->d_wait);
2733+
d_wait = dentry->d_wait;
27122734
dentry->d_wait = NULL;
27132735
hlist_bl_unlock(b);
27142736
INIT_HLIST_NODE(&dentry->d_u.d_alias);
27152737
INIT_LIST_HEAD(&dentry->d_lru);
2738+
return d_wait;
2739+
}
2740+
2741+
void __d_lookup_unhash_wake(struct dentry *dentry)
2742+
{
2743+
spin_lock(&dentry->d_lock);
2744+
wake_up_all(__d_lookup_unhash(dentry));
2745+
spin_unlock(&dentry->d_lock);
27162746
}
2717-
EXPORT_SYMBOL(__d_lookup_done);
2747+
EXPORT_SYMBOL(__d_lookup_unhash_wake);
27182748

27192749
/* inode->i_lock held if inode is non-NULL */
27202750

27212751
static inline void __d_add(struct dentry *dentry, struct inode *inode)
27222752
{
2753+
wait_queue_head_t *d_wait;
27232754
struct inode *dir = NULL;
27242755
unsigned n;
27252756
spin_lock(&dentry->d_lock);
27262757
if (unlikely(d_in_lookup(dentry))) {
27272758
dir = dentry->d_parent->d_inode;
27282759
n = start_dir_add(dir);
2729-
__d_lookup_done(dentry);
2760+
d_wait = __d_lookup_unhash(dentry);
27302761
}
27312762
if (inode) {
27322763
unsigned add_flags = d_flags_for_inode(inode);
@@ -2738,7 +2769,7 @@ static inline void __d_add(struct dentry *dentry, struct inode *inode)
27382769
}
27392770
__d_rehash(dentry);
27402771
if (dir)
2741-
end_dir_add(dir, n);
2772+
end_dir_add(dir, n, d_wait);
27422773
spin_unlock(&dentry->d_lock);
27432774
if (inode)
27442775
spin_unlock(&inode->i_lock);
@@ -2885,6 +2916,7 @@ static void __d_move(struct dentry *dentry, struct dentry *target,
28852916
bool exchange)
28862917
{
28872918
struct dentry *old_parent, *p;
2919+
wait_queue_head_t *d_wait;
28882920
struct inode *dir = NULL;
28892921
unsigned n;
28902922

@@ -2915,7 +2947,7 @@ static void __d_move(struct dentry *dentry, struct dentry *target,
29152947
if (unlikely(d_in_lookup(target))) {
29162948
dir = target->d_parent->d_inode;
29172949
n = start_dir_add(dir);
2918-
__d_lookup_done(target);
2950+
d_wait = __d_lookup_unhash(target);
29192951
}
29202952

29212953
write_seqcount_begin(&dentry->d_seq);
@@ -2951,7 +2983,7 @@ static void __d_move(struct dentry *dentry, struct dentry *target,
29512983
write_seqcount_end(&dentry->d_seq);
29522984

29532985
if (dir)
2954-
end_dir_add(dir, n);
2986+
end_dir_add(dir, n, d_wait);
29552987

29562988
if (dentry->d_parent != old_parent)
29572989
spin_unlock(&dentry->d_parent->d_lock);

include/linux/dcache.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ static inline void dont_mount(struct dentry *dentry)
349349
spin_unlock(&dentry->d_lock);
350350
}
351351

352-
extern void __d_lookup_done(struct dentry *);
352+
extern void __d_lookup_unhash_wake(struct dentry *dentry);
353353

354354
static inline int d_in_lookup(const struct dentry *dentry)
355355
{
@@ -358,11 +358,8 @@ static inline int d_in_lookup(const struct dentry *dentry)
358358

359359
static inline void d_lookup_done(struct dentry *dentry)
360360
{
361-
if (unlikely(d_in_lookup(dentry))) {
362-
spin_lock(&dentry->d_lock);
363-
__d_lookup_done(dentry);
364-
spin_unlock(&dentry->d_lock);
365-
}
361+
if (unlikely(d_in_lookup(dentry)))
362+
__d_lookup_unhash_wake(dentry);
366363
}
367364

368365
extern void dput(struct dentry *);

0 commit comments

Comments
 (0)