mirror of
https://github.com/torvalds/linux.git
synced 2026-09-25 17:42:03 +02:00
Document rcu_read_lock() use in select_collect2()
If select_collect2() finds something that is neither busy nor can be moved to shrink list, it needs to return that to caller's caller (shrink_dcache_tree()) ASAP and do so without grabbing references (among other things, it might be already dying, in which case refcount can't be incremented). We are called inside a ->d_lock scope, but that scope is going to be terminated as soon as we return to caller (d_walk()); ->d_lock will be retaken by shrink_dcache_tree(), but we need to bridge between these scopes, turning them into contiguous RCU read-side critical area. We do that with rcu_read_lock() scope - it spans from unbalanced rcu_read_lock() in select_collect2() to unbalanced rcu_read_unlock() in shrink_dcache_tree(). That works, but it really needs to be documented; it's rather unidiomatic and it had caused quite a bit of confusion - some of it in form of patches "fixing" the damn thing. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
b4143e0e71
commit
a5beeb64f2
22
fs/dcache.c
22
fs/dcache.c
|
|
@ -1638,6 +1638,15 @@ static enum d_walk_ret select_collect2(void *_data, struct dentry *dentry)
|
|||
|
||||
if (dentry->d_lockref.count <= 0) {
|
||||
if (!__move_to_shrink_list(dentry, &data->dispose)) {
|
||||
/*
|
||||
* We need an enter RCU read-side critical area that
|
||||
* would extend past the return from d_walk() and
|
||||
* we are in the scope of ->d_lock that will terminate
|
||||
* before that, so we use rcu_read_lock() to bridge
|
||||
* over to the scope of ->d_lock in d_walk() caller.
|
||||
* The scope of rcu_read_lock() spans from here to
|
||||
* paired rcu_read_unlock() in shrink_dcache_tree().
|
||||
*/
|
||||
rcu_read_lock();
|
||||
data->victim = dentry;
|
||||
return D_WALK_QUIT;
|
||||
|
|
@ -1682,9 +1691,20 @@ static void shrink_dcache_tree(struct dentry *parent, bool for_umount)
|
|||
d_walk(parent, &data, select_collect2);
|
||||
if (data.victim) {
|
||||
struct dentry *v = data.victim;
|
||||
|
||||
/*
|
||||
* select_collect2() has picked a dentry that was
|
||||
* either dying or on a shrink list and arranged
|
||||
* for it to be returned to us. We are still in
|
||||
* the RCU read-side critical area started there
|
||||
* (rcu_read_lock() scope opened in select_collect2()),
|
||||
* so dentry couldn't have been freed yet, but its
|
||||
* state might've changed since we dropped ->d_lock
|
||||
* on the way out. Switch over to ->d_lock scope
|
||||
* and recheck the dentry state.
|
||||
*/
|
||||
spin_lock(&v->d_lock);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (v->d_lockref.count < 0 &&
|
||||
!(v->d_flags & DCACHE_DENTRY_KILLED)) {
|
||||
struct completion_list wait;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user