mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 23:09:29 +02:00
Merge tag 'pull-dcache' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull dcache updates from Al Viro:
- d_alloc_parallel() API change (Neil's with my changes)
- NORCU fixes
- Reorganization and simplification of dentry eviction logic
- Simplifying rcu_read_lock() scopes in fs/dcache.c
- Secondary roots work - getting rid of NFS fake root dentries and
dealing with remaining shrink_dcache_for_umount() and
shrink_dentry_list() races
- making cursors NORCU (surprisingly easy)
* tag 'pull-dcache' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (22 commits)
make cursors NORCU
nfs: get rid of fake root dentries
wind ->s_roots via ->d_sib instead of ->d_hash
shrink_dentry_tree(): unify the calls of shrink_dentry_list()
shrinking rcu_read_lock() scope in d_alloc_parallel()
d_walk(): shrink rcu_read_lock() scope
document dentry_kill()
adjust calling conventions of lock_for_kill(), fold __dentry_kill() into dentry_kill()
Document rcu_read_lock() use in select_collect2()
Shift rcu_read_{,un}lock() inside fast_dput()
simplify safety for lock_for_kill() slowpath
fold lock_for_kill() and __dentry_kill() into common helper
fold lock_for_kill() into shrink_kill()
shrink_dentry_list(): start with removing from shrink list
d_prune_aliases(): make sure to skip NORCU aliases
kill d_dispose_if_unused()
make to_shrink_list() return whether it has moved dentry to list
select_collect(): ignore dentries on shrink lists if they have positive refcounts
find_acceptable_alias(): skip NORCU aliases with zero refcount
fix a race between d_find_any_alias() and final dput() of NORCU dentries
...
This commit is contained in:
@@ -1384,3 +1384,20 @@ for_each_alias(dentry, inode) instead of hlist_for_each_entry; better
|
|||||||
yet, see if any of the exported primitives could be used instead of
|
yet, see if any of the exported primitives could be used instead of
|
||||||
the entire loop. You still need to hold ->i_lock of the inode over
|
the entire loop. You still need to hold ->i_lock of the inode over
|
||||||
either form of manual loop.
|
either form of manual loop.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**mandatory**
|
||||||
|
|
||||||
|
d_alloc_parallel() no longer requires a waitqueue_head.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**mandatory**
|
||||||
|
|
||||||
|
d_dispose_if_unused() is gone; use __move_to_shrink_list() if you really
|
||||||
|
need that functionality, but watch out for memory safety issues - just
|
||||||
|
as with d_dispose_if_unused() these are not trivial; with this variant
|
||||||
|
of API it's more explicit, since grabbing ->d_lock is caller-side, but
|
||||||
|
d_dispose_if_unused() had all the same issues. It's a low-level primitive;
|
||||||
|
use only if you have no alternative.
|
||||||
|
|||||||
+1
-3
@@ -248,13 +248,11 @@ int afs_silly_iput(struct dentry *dentry, struct inode *inode)
|
|||||||
struct dentry *alias;
|
struct dentry *alias;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
|
|
||||||
|
|
||||||
_enter("%p{%pd},%llx", dentry, dentry, vnode->fid.vnode);
|
_enter("%p{%pd},%llx", dentry, dentry, vnode->fid.vnode);
|
||||||
|
|
||||||
down_read(&dvnode->rmdir_lock);
|
down_read(&dvnode->rmdir_lock);
|
||||||
|
|
||||||
alias = d_alloc_parallel(dentry->d_parent, &dentry->d_name, &wq);
|
alias = d_alloc_parallel(dentry->d_parent, &dentry->d_name);
|
||||||
if (IS_ERR(alias)) {
|
if (IS_ERR(alias)) {
|
||||||
up_read(&dvnode->rmdir_lock);
|
up_read(&dvnode->rmdir_lock);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+324
-225
@@ -43,8 +43,8 @@
|
|||||||
* - i_dentry, d_alias, d_inode of aliases
|
* - i_dentry, d_alias, d_inode of aliases
|
||||||
* dcache_hash_bucket lock protects:
|
* dcache_hash_bucket lock protects:
|
||||||
* - the dcache hash table
|
* - the dcache hash table
|
||||||
* s_roots bl list spinlock protects:
|
* s_roots_lock protects:
|
||||||
* - the s_roots list (see __d_drop)
|
* - the s_roots list (see __d_move()/dentry_unlist()/d_obtain_root())
|
||||||
* dentry->d_sb->s_dentry_lru_lock protects:
|
* dentry->d_sb->s_dentry_lru_lock protects:
|
||||||
* - the dcache lru lists and counters
|
* - the dcache lru lists and counters
|
||||||
* d_lock protects:
|
* d_lock protects:
|
||||||
@@ -462,14 +462,10 @@ static void dentry_unlink_inode(struct dentry * dentry)
|
|||||||
|
|
||||||
raw_write_seqcount_begin(&dentry->d_seq);
|
raw_write_seqcount_begin(&dentry->d_seq);
|
||||||
__d_clear_type_and_inode(dentry);
|
__d_clear_type_and_inode(dentry);
|
||||||
hlist_del_init(&dentry->d_alias);
|
__hlist_del(&dentry->d_alias);
|
||||||
/*
|
/*
|
||||||
* dentry becomes negative, so the space occupied by ->d_alias
|
* dentry becomes negative, so the space occupied by ->d_alias
|
||||||
* belongs to ->waiters now; we could use __hlist_del() instead
|
* belongs to ->waiters now.
|
||||||
* of hlist_del_init(), if not for the stunt pulled by nfs
|
|
||||||
* dummy root dentries - positive dentry *not* included into
|
|
||||||
* the alias list of its inode. Open-coding hlist_del_init()
|
|
||||||
* and removing zeroing would be too clumsy...
|
|
||||||
*/
|
*/
|
||||||
dentry->waiters = NULL;
|
dentry->waiters = NULL;
|
||||||
raw_write_seqcount_end(&dentry->d_seq);
|
raw_write_seqcount_end(&dentry->d_seq);
|
||||||
@@ -568,16 +564,7 @@ static void d_lru_shrink_move(struct list_lru_one *lru, struct dentry *dentry,
|
|||||||
|
|
||||||
static void ___d_drop(struct dentry *dentry)
|
static void ___d_drop(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct hlist_bl_head *b;
|
struct hlist_bl_head *b = d_hash(dentry->d_name.hash);
|
||||||
/*
|
|
||||||
* Hashed dentries are normally on the dentry hashtable,
|
|
||||||
* with the exception of those newly allocated by
|
|
||||||
* d_obtain_root, which are always IS_ROOT:
|
|
||||||
*/
|
|
||||||
if (unlikely(IS_ROOT(dentry)))
|
|
||||||
b = &dentry->d_sb->s_roots;
|
|
||||||
else
|
|
||||||
b = d_hash(dentry->d_name.hash);
|
|
||||||
|
|
||||||
hlist_bl_lock(b);
|
hlist_bl_lock(b);
|
||||||
__hlist_bl_del(&dentry->d_hash);
|
__hlist_bl_del(&dentry->d_hash);
|
||||||
@@ -636,12 +623,14 @@ struct completion_list {
|
|||||||
* Use ->waiters for a single-linked list of struct completion_list of
|
* Use ->waiters for a single-linked list of struct completion_list of
|
||||||
* waiters.
|
* waiters.
|
||||||
*/
|
*/
|
||||||
static inline void d_add_waiter(struct dentry *dentry, struct completion_list *p)
|
static inline bool d_add_waiter(struct dentry *dentry, struct completion_list *p)
|
||||||
{
|
{
|
||||||
struct completion_list *v = dentry->waiters;
|
if (unlikely(dentry->d_flags & DCACHE_DENTRY_KILLED))
|
||||||
|
return false;
|
||||||
init_completion(&p->completion);
|
init_completion(&p->completion);
|
||||||
p->next = v;
|
p->next = dentry->waiters;
|
||||||
dentry->waiters = p;
|
dentry->waiters = p;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void d_complete_waiters(struct dentry *dentry)
|
static inline void d_complete_waiters(struct dentry *dentry)
|
||||||
@@ -658,6 +647,13 @@ static inline void d_complete_waiters(struct dentry *dentry)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void unlink_secondary_root(struct dentry *dentry)
|
||||||
|
{
|
||||||
|
spin_lock(&dentry->d_sb->s_roots_lock);
|
||||||
|
hlist_del_init(&dentry->d_sib);
|
||||||
|
spin_unlock(&dentry->d_sb->s_roots_lock);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void dentry_unlist(struct dentry *dentry)
|
static inline void dentry_unlist(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct dentry *next;
|
struct dentry *next;
|
||||||
@@ -669,6 +665,10 @@ static inline void dentry_unlist(struct dentry *dentry)
|
|||||||
d_complete_waiters(dentry);
|
d_complete_waiters(dentry);
|
||||||
if (unlikely(hlist_unhashed(&dentry->d_sib)))
|
if (unlikely(hlist_unhashed(&dentry->d_sib)))
|
||||||
return;
|
return;
|
||||||
|
if (unlikely(IS_ROOT(dentry))) {
|
||||||
|
unlink_secondary_root(dentry); // secondary root goes away
|
||||||
|
return;
|
||||||
|
}
|
||||||
__hlist_del(&dentry->d_sib);
|
__hlist_del(&dentry->d_sib);
|
||||||
/*
|
/*
|
||||||
* Cursors can move around the list of children. While we'd been
|
* Cursors can move around the list of children. While we'd been
|
||||||
@@ -697,11 +697,113 @@ static inline void dentry_unlist(struct dentry *dentry)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dentry *__dentry_kill(struct dentry *dentry)
|
/*
|
||||||
|
* Prepare locking environment for killing a dentry.
|
||||||
|
* Called under dentry->d_lock. To proceed with eviction of a positive dentry
|
||||||
|
* we need to get ->i_lock of the inode of that dentry as well.
|
||||||
|
* However, ->i_lock nests outside of ->d_lock, so if trylock fails we might
|
||||||
|
* have to drop and regain the latter. Dentry state can change while its
|
||||||
|
* ->d_lock is not held - it might end up getting killed, becoming busy,
|
||||||
|
* negative, etc., so we need to be careful.
|
||||||
|
*
|
||||||
|
* For NORCU dentries memory safety relies upon having only one call of
|
||||||
|
* lock_for_kill() in the entire lifetime of dentry and dentry_free() being
|
||||||
|
* called only by the caller of lock_for_kill(). That this is NORCU-specific;
|
||||||
|
* the crucial part is that refcounts of NORCU dentries never grow once having
|
||||||
|
* dropped to zero.
|
||||||
|
*
|
||||||
|
* For normal dentries we can not assume that there won't be concurrent calls
|
||||||
|
* of dentry_free() - dentry might end up being evicted by another thread
|
||||||
|
* while we are dropping/retaking locks on the slow path. Memory safety is
|
||||||
|
* provided by keeping the RCU read-side critical area contiguous with
|
||||||
|
* an explicit rcu_read_lock() scope bridging over the break in spinlock scopes.
|
||||||
|
*
|
||||||
|
* If dentry is busy (or busy dying, or already dead), unlock dentry
|
||||||
|
* and return false. Otherwise, return true and have that dentry's
|
||||||
|
* inode (if any) locked in addition to dentry itself.
|
||||||
|
*/
|
||||||
|
static bool lock_for_kill(struct dentry *dentry)
|
||||||
|
{
|
||||||
|
struct inode *inode = dentry->d_inode;
|
||||||
|
|
||||||
|
if (unlikely(dentry->d_lockref.count)) {
|
||||||
|
spin_unlock(&dentry->d_lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inode || likely(spin_trylock(&inode->i_lock)))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Too bad - we need to drop ->d_lock and take locks in correct order.
|
||||||
|
// To avoid breaking RCU read-side critical area when we drop ->d_lock,
|
||||||
|
// take an explicit rcu_read_lock() while we are switching locks.
|
||||||
|
rcu_read_lock();
|
||||||
|
do {
|
||||||
|
spin_unlock(&dentry->d_lock);
|
||||||
|
spin_lock(&inode->i_lock);
|
||||||
|
spin_lock(&dentry->d_lock);
|
||||||
|
// make sure we'd locked the right inode - ->d_inode might've
|
||||||
|
// changed while we were not holding ->d_lock
|
||||||
|
if (likely(inode == dentry->d_inode))
|
||||||
|
break;
|
||||||
|
spin_unlock(&inode->i_lock);
|
||||||
|
inode = dentry->d_inode;
|
||||||
|
} while (inode);
|
||||||
|
rcu_read_unlock();
|
||||||
|
if (likely(!dentry->d_lockref.count))
|
||||||
|
return true;
|
||||||
|
if (inode)
|
||||||
|
spin_unlock(&inode->i_lock);
|
||||||
|
spin_unlock(&dentry->d_lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dentry_kill - evict a dentry
|
||||||
|
* @dentry: dentry to be evicted
|
||||||
|
*
|
||||||
|
* All dentry evictions are done by this function. The reference we are
|
||||||
|
* passed does not contribute to the refcount; the caller had either
|
||||||
|
* already decremented the refcount or it had never held one in the
|
||||||
|
* first place. @dentry->d_lock is held by the caller and dropped
|
||||||
|
* by dentry_kill(@dentry).
|
||||||
|
*
|
||||||
|
* We are guaranteed that nobody had called dentry_free(@dentry)
|
||||||
|
* prior to the beginning of RCU read-side critical area we are in.
|
||||||
|
*
|
||||||
|
* Caller must not access @dentry after the call.
|
||||||
|
*
|
||||||
|
* If eviction of @dentry drops the last reference to its parent,
|
||||||
|
* the reference to parent is returned to caller. In that case
|
||||||
|
* it is guaranteed to satisfy the requirements for dentry_kill()
|
||||||
|
* argument - its ->d_lock is held and we are guaranteed that nobody
|
||||||
|
* had passed it to dentry_free() prior to acquisition of its ->d_lock.
|
||||||
|
* Otherwise %NULL is returned.
|
||||||
|
*
|
||||||
|
* If @dentry is idle and remains such after we assemble the full
|
||||||
|
* locking environment for eviction (see lock_for_kill() for details)
|
||||||
|
* we mark it doomed (->d_lockref.count < 0) and proceed to detaching
|
||||||
|
* it from any filesystem objects. Otherwise we drop ->d_lock and
|
||||||
|
* return %NULL.
|
||||||
|
*
|
||||||
|
* Once @dentry is detached from the filesystem objects, we complete
|
||||||
|
* detaching it from dentry tree. The parent, if any, gets locked
|
||||||
|
* and its refcount is decremented; dentry is carefully removed from
|
||||||
|
* the tree (see dentry_unlist() for details) and marked killed
|
||||||
|
* (%DCACHE_DENTRY_KILLED set in ->d_flags). At that point it's just
|
||||||
|
* an inert chunk of memory, accessible only via RCU references
|
||||||
|
* and possibly via a shrink list. If it is not on any shrink lists,
|
||||||
|
* we call dentry_free(), which schedules actual freeing of memory.
|
||||||
|
* Othewise freeing is left to the owner of the shrink list in question.
|
||||||
|
*/
|
||||||
|
static struct dentry *dentry_kill(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct dentry *parent = NULL;
|
struct dentry *parent = NULL;
|
||||||
bool can_free = true;
|
bool can_free = true;
|
||||||
|
|
||||||
|
if (unlikely(!lock_for_kill(dentry)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The dentry is now unrecoverably dead to the world.
|
* The dentry is now unrecoverably dead to the world.
|
||||||
*/
|
*/
|
||||||
@@ -748,43 +850,6 @@ static struct dentry *__dentry_kill(struct dentry *dentry)
|
|||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Lock a dentry for feeding it to __dentry_kill().
|
|
||||||
* Called under rcu_read_lock() and dentry->d_lock; the former
|
|
||||||
* guarantees that nothing we access will be freed under us.
|
|
||||||
* Note that dentry is *not* protected from concurrent dentry_kill(),
|
|
||||||
* d_delete(), etc.
|
|
||||||
*
|
|
||||||
* Return false if dentry is busy. Otherwise, return true and have
|
|
||||||
* that dentry's inode locked.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static bool lock_for_kill(struct dentry *dentry)
|
|
||||||
{
|
|
||||||
struct inode *inode = dentry->d_inode;
|
|
||||||
|
|
||||||
if (unlikely(dentry->d_lockref.count))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!inode || likely(spin_trylock(&inode->i_lock)))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
do {
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
spin_lock(&inode->i_lock);
|
|
||||||
spin_lock(&dentry->d_lock);
|
|
||||||
if (likely(inode == dentry->d_inode))
|
|
||||||
break;
|
|
||||||
spin_unlock(&inode->i_lock);
|
|
||||||
inode = dentry->d_inode;
|
|
||||||
} while (inode);
|
|
||||||
if (likely(!dentry->d_lockref.count))
|
|
||||||
return true;
|
|
||||||
if (inode)
|
|
||||||
spin_unlock(&inode->i_lock);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Decide if dentry is worth retaining. Usually this is called with dentry
|
* Decide if dentry is worth retaining. Usually this is called with dentry
|
||||||
* locked; if not locked, we are more limited and might not be able to tell
|
* locked; if not locked, we are more limited and might not be able to tell
|
||||||
@@ -860,17 +925,17 @@ EXPORT_SYMBOL(d_mark_dontcache);
|
|||||||
* If unsuccessful, we return false, having already taken the dentry lock.
|
* If unsuccessful, we return false, having already taken the dentry lock.
|
||||||
* In that case refcount is guaranteed to be zero and we have already
|
* In that case refcount is guaranteed to be zero and we have already
|
||||||
* decided that it's not worth keeping around.
|
* decided that it's not worth keeping around.
|
||||||
*
|
|
||||||
* The caller needs to hold the RCU read lock, so that the dentry is
|
|
||||||
* guaranteed to stay around even if the refcount goes down to zero!
|
|
||||||
*/
|
*/
|
||||||
static inline bool fast_dput(struct dentry *dentry)
|
static inline bool fast_dput(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* try to decrement the lockref optimistically.
|
* Try to decrement the lockref optimistically.
|
||||||
|
* RCU read lock held so that dentry is guaranteed to stay around
|
||||||
|
* even if the refcount goes down to zero.
|
||||||
*/
|
*/
|
||||||
|
rcu_read_lock();
|
||||||
ret = lockref_put_return(&dentry->d_lockref);
|
ret = lockref_put_return(&dentry->d_lockref);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -880,6 +945,7 @@ static inline bool fast_dput(struct dentry *dentry)
|
|||||||
*/
|
*/
|
||||||
if (unlikely(ret < 0)) {
|
if (unlikely(ret < 0)) {
|
||||||
spin_lock(&dentry->d_lock);
|
spin_lock(&dentry->d_lock);
|
||||||
|
rcu_read_unlock();
|
||||||
if (WARN_ON_ONCE(dentry->d_lockref.count <= 0)) {
|
if (WARN_ON_ONCE(dentry->d_lockref.count <= 0)) {
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
return true;
|
return true;
|
||||||
@@ -891,8 +957,10 @@ static inline bool fast_dput(struct dentry *dentry)
|
|||||||
/*
|
/*
|
||||||
* If we weren't the last ref, we're done.
|
* If we weren't the last ref, we're done.
|
||||||
*/
|
*/
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
rcu_read_unlock();
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Can we decide that decrement of refcount is all we needed without
|
* Can we decide that decrement of refcount is all we needed without
|
||||||
@@ -900,8 +968,10 @@ static inline bool fast_dput(struct dentry *dentry)
|
|||||||
* dentry looks like it ought to be retained and there's nothing else
|
* dentry looks like it ought to be retained and there's nothing else
|
||||||
* to do.
|
* to do.
|
||||||
*/
|
*/
|
||||||
if (retain_dentry(dentry, false))
|
if (retain_dentry(dentry, false)) {
|
||||||
|
rcu_read_unlock();
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Either not worth retaining or we can't tell without the lock.
|
* Either not worth retaining or we can't tell without the lock.
|
||||||
@@ -909,6 +979,7 @@ static inline bool fast_dput(struct dentry *dentry)
|
|||||||
* but we'll need to re-check the situation after getting the lock.
|
* but we'll need to re-check the situation after getting the lock.
|
||||||
*/
|
*/
|
||||||
spin_lock(&dentry->d_lock);
|
spin_lock(&dentry->d_lock);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Did somebody else grab a reference to it in the meantime, and
|
* Did somebody else grab a reference to it in the meantime, and
|
||||||
@@ -926,21 +997,13 @@ locked:
|
|||||||
|
|
||||||
static void finish_dput(struct dentry *dentry)
|
static void finish_dput(struct dentry *dentry)
|
||||||
__releases(dentry->d_lock)
|
__releases(dentry->d_lock)
|
||||||
__releases(RCU)
|
|
||||||
{
|
{
|
||||||
while (lock_for_kill(dentry)) {
|
while ((dentry = dentry_kill(dentry)) != NULL) {
|
||||||
rcu_read_unlock();
|
|
||||||
dentry = __dentry_kill(dentry);
|
|
||||||
if (!dentry)
|
|
||||||
return;
|
|
||||||
if (retain_dentry(dentry, true)) {
|
if (retain_dentry(dentry, true)) {
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rcu_read_lock();
|
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -974,11 +1037,8 @@ void dput(struct dentry *dentry)
|
|||||||
if (!dentry)
|
if (!dentry)
|
||||||
return;
|
return;
|
||||||
might_sleep();
|
might_sleep();
|
||||||
rcu_read_lock();
|
if (likely(fast_dput(dentry)))
|
||||||
if (likely(fast_dput(dentry))) {
|
|
||||||
rcu_read_unlock();
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
finish_dput(dentry);
|
finish_dput(dentry);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dput);
|
EXPORT_SYMBOL(dput);
|
||||||
@@ -989,30 +1049,46 @@ void d_make_discardable(struct dentry *dentry)
|
|||||||
WARN_ON(!(dentry->d_flags & DCACHE_PERSISTENT));
|
WARN_ON(!(dentry->d_flags & DCACHE_PERSISTENT));
|
||||||
dentry->d_flags &= ~DCACHE_PERSISTENT;
|
dentry->d_flags &= ~DCACHE_PERSISTENT;
|
||||||
dentry->d_lockref.count--;
|
dentry->d_lockref.count--;
|
||||||
rcu_read_lock();
|
|
||||||
finish_dput(dentry);
|
finish_dput(dentry);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(d_make_discardable);
|
EXPORT_SYMBOL(d_make_discardable);
|
||||||
|
|
||||||
static void to_shrink_list(struct dentry *dentry, struct list_head *list)
|
/**
|
||||||
|
* __move_to_shrink_list - try to place a dentry into a shrink list
|
||||||
|
* @dentry: dentry to try putting into shrink list
|
||||||
|
* @list: the list to put @dentry into.
|
||||||
|
* Returns: true @dentry had been placed into @list, false otherwise
|
||||||
|
*
|
||||||
|
* If @dentry is idle and not already include into a shrink list, move
|
||||||
|
* it into @list and return %true; otherwise do nothing and return %false.
|
||||||
|
*
|
||||||
|
* Caller must be holding @dentry->d_lock. There must have been no calls of
|
||||||
|
* dentry_free(@dentry) prior to the beginning of the RCU read-side critical
|
||||||
|
* area in which __move_to_shrink_list(@dentry, @list) is called.
|
||||||
|
*
|
||||||
|
* @list should be thread-private and eventually emptied by passing it to
|
||||||
|
* shrink_dentry_list().
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool __move_to_shrink_list(struct dentry *dentry, struct list_head *list)
|
||||||
__must_hold(&dentry->d_lock)
|
__must_hold(&dentry->d_lock)
|
||||||
{
|
{
|
||||||
if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) {
|
if (likely(!dentry->d_lockref.count &&
|
||||||
|
!(dentry->d_flags & DCACHE_SHRINK_LIST))) {
|
||||||
if (dentry->d_flags & DCACHE_LRU_LIST)
|
if (dentry->d_flags & DCACHE_LRU_LIST)
|
||||||
d_lru_del(dentry);
|
d_lru_del(dentry);
|
||||||
d_shrink_add(dentry, list);
|
d_shrink_add(dentry, list);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(__move_to_shrink_list);
|
||||||
|
|
||||||
void dput_to_list(struct dentry *dentry, struct list_head *list)
|
void dput_to_list(struct dentry *dentry, struct list_head *list)
|
||||||
{
|
{
|
||||||
rcu_read_lock();
|
if (likely(fast_dput(dentry)))
|
||||||
if (likely(fast_dput(dentry))) {
|
|
||||||
rcu_read_unlock();
|
|
||||||
return;
|
return;
|
||||||
}
|
__move_to_shrink_list(dentry, list);
|
||||||
rcu_read_unlock();
|
|
||||||
to_shrink_list(dentry, list);
|
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1058,7 +1134,10 @@ repeat:
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dget_parent);
|
EXPORT_SYMBOL(dget_parent);
|
||||||
|
|
||||||
static struct dentry * __d_find_any_alias(struct inode *inode)
|
/*
|
||||||
|
* inode is a directory, inode->i_lock is held by the caller
|
||||||
|
*/
|
||||||
|
static struct dentry * __d_find_dir_alias(struct inode *inode)
|
||||||
{
|
{
|
||||||
struct dentry *alias;
|
struct dentry *alias;
|
||||||
|
|
||||||
@@ -1069,6 +1148,18 @@ static struct dentry * __d_find_any_alias(struct inode *inode)
|
|||||||
return alias;
|
return alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct dentry * __d_find_any_alias(struct inode *inode)
|
||||||
|
{
|
||||||
|
struct dentry *alias;
|
||||||
|
|
||||||
|
if (hlist_empty(&inode->i_dentry))
|
||||||
|
return NULL;
|
||||||
|
for_each_alias(alias, inode)
|
||||||
|
if (dget_alias_ilocked(alias))
|
||||||
|
return alias;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* d_find_any_alias - find any alias for a given inode
|
* d_find_any_alias - find any alias for a given inode
|
||||||
* @inode: inode to find an alias for
|
* @inode: inode to find an alias for
|
||||||
@@ -1092,7 +1183,7 @@ static struct dentry *__d_find_alias(struct inode *inode)
|
|||||||
struct dentry *alias;
|
struct dentry *alias;
|
||||||
|
|
||||||
if (S_ISDIR(inode->i_mode))
|
if (S_ISDIR(inode->i_mode))
|
||||||
return __d_find_any_alias(inode);
|
return __d_find_dir_alias(inode);
|
||||||
|
|
||||||
for_each_alias(alias, inode) {
|
for_each_alias(alias, inode) {
|
||||||
spin_lock(&alias->d_lock);
|
spin_lock(&alias->d_lock);
|
||||||
@@ -1158,25 +1249,6 @@ struct dentry *d_find_alias_rcu(struct inode *inode)
|
|||||||
return de;
|
return de;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* d_dispose_if_unused - move unreferenced dentries to shrink list
|
|
||||||
* @dentry: dentry in question
|
|
||||||
* @dispose: head of shrink list
|
|
||||||
*
|
|
||||||
* If dentry has no external references, move it to shrink list.
|
|
||||||
*
|
|
||||||
* NOTE!!! The caller is responsible for preventing eviction of the dentry by
|
|
||||||
* holding dentry->d_inode->i_lock or equivalent.
|
|
||||||
*/
|
|
||||||
void d_dispose_if_unused(struct dentry *dentry, struct list_head *dispose)
|
|
||||||
{
|
|
||||||
spin_lock(&dentry->d_lock);
|
|
||||||
if (!dentry->d_lockref.count)
|
|
||||||
to_shrink_list(dentry, dispose);
|
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(d_dispose_if_unused);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to kill dentries associated with this inode.
|
* Try to kill dentries associated with this inode.
|
||||||
* WARNING: you must own a reference to inode.
|
* WARNING: you must own a reference to inode.
|
||||||
@@ -1187,8 +1259,12 @@ void d_prune_aliases(struct inode *inode)
|
|||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
|
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
for_each_alias(dentry, inode)
|
for_each_alias(dentry, inode) {
|
||||||
d_dispose_if_unused(dentry, &dispose);
|
spin_lock(&dentry->d_lock);
|
||||||
|
if (likely(!(dentry->d_flags & DCACHE_NORCU)))
|
||||||
|
__move_to_shrink_list(dentry, &dispose);
|
||||||
|
spin_unlock(&dentry->d_lock);
|
||||||
|
}
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
shrink_dentry_list(&dispose);
|
shrink_dentry_list(&dispose);
|
||||||
}
|
}
|
||||||
@@ -1196,14 +1272,8 @@ EXPORT_SYMBOL(d_prune_aliases);
|
|||||||
|
|
||||||
static inline void shrink_kill(struct dentry *victim)
|
static inline void shrink_kill(struct dentry *victim)
|
||||||
{
|
{
|
||||||
do {
|
while ((victim = dentry_kill(victim)) != NULL)
|
||||||
rcu_read_unlock();
|
;
|
||||||
victim = __dentry_kill(victim);
|
|
||||||
rcu_read_lock();
|
|
||||||
} while (victim && lock_for_kill(victim));
|
|
||||||
rcu_read_unlock();
|
|
||||||
if (victim)
|
|
||||||
spin_unlock(&victim->d_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void shrink_dentry_list(struct list_head *list)
|
void shrink_dentry_list(struct list_head *list)
|
||||||
@@ -1213,18 +1283,12 @@ void shrink_dentry_list(struct list_head *list)
|
|||||||
|
|
||||||
dentry = list_entry(list->prev, struct dentry, d_lru);
|
dentry = list_entry(list->prev, struct dentry, d_lru);
|
||||||
spin_lock(&dentry->d_lock);
|
spin_lock(&dentry->d_lock);
|
||||||
rcu_read_lock();
|
d_shrink_del(dentry);
|
||||||
if (!lock_for_kill(dentry)) {
|
if (unlikely(dentry->d_flags & DCACHE_DENTRY_KILLED)) {
|
||||||
bool can_free;
|
|
||||||
rcu_read_unlock();
|
|
||||||
d_shrink_del(dentry);
|
|
||||||
can_free = dentry->d_flags & DCACHE_DENTRY_KILLED;
|
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
if (can_free)
|
dentry_free(dentry);
|
||||||
dentry_free(dentry);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
d_shrink_del(dentry);
|
|
||||||
shrink_kill(dentry);
|
shrink_kill(dentry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1385,6 +1449,8 @@ again:
|
|||||||
read_seqbegin_or_lock(&rename_lock, &seq);
|
read_seqbegin_or_lock(&rename_lock, &seq);
|
||||||
this_parent = parent;
|
this_parent = parent;
|
||||||
spin_lock(&this_parent->d_lock);
|
spin_lock(&this_parent->d_lock);
|
||||||
|
if (unlikely(this_parent->d_flags & DCACHE_DENTRY_CURSOR))
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
ret = enter(data, this_parent);
|
ret = enter(data, this_parent);
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
@@ -1433,14 +1499,15 @@ resume:
|
|||||||
/*
|
/*
|
||||||
* All done at this level ... ascend and resume the search.
|
* All done at this level ... ascend and resume the search.
|
||||||
*/
|
*/
|
||||||
rcu_read_lock();
|
|
||||||
ascend:
|
ascend:
|
||||||
if (this_parent != parent) {
|
if (this_parent != parent) {
|
||||||
dentry = this_parent;
|
dentry = this_parent;
|
||||||
this_parent = dentry->d_parent;
|
this_parent = dentry->d_parent;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
spin_lock(&this_parent->d_lock);
|
spin_lock(&this_parent->d_lock);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
/* might go back up the wrong parent if we have had a rename. */
|
/* might go back up the wrong parent if we have had a rename. */
|
||||||
if (need_seqretry(&rename_lock, seq))
|
if (need_seqretry(&rename_lock, seq))
|
||||||
@@ -1448,7 +1515,6 @@ ascend:
|
|||||||
/* go into the first sibling still alive */
|
/* go into the first sibling still alive */
|
||||||
hlist_for_each_entry_continue(dentry, d_sib) {
|
hlist_for_each_entry_continue(dentry, d_sib) {
|
||||||
if (likely(!(dentry->d_flags & DCACHE_DENTRY_KILLED))) {
|
if (likely(!(dentry->d_flags & DCACHE_DENTRY_KILLED))) {
|
||||||
rcu_read_unlock();
|
|
||||||
goto resume;
|
goto resume;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1456,7 +1522,6 @@ ascend:
|
|||||||
}
|
}
|
||||||
if (need_seqretry(&rename_lock, seq))
|
if (need_seqretry(&rename_lock, seq))
|
||||||
goto rename_retry;
|
goto rename_retry;
|
||||||
rcu_read_unlock();
|
|
||||||
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
spin_unlock(&this_parent->d_lock);
|
spin_unlock(&this_parent->d_lock);
|
||||||
@@ -1465,7 +1530,6 @@ out_unlock:
|
|||||||
|
|
||||||
rename_retry:
|
rename_retry:
|
||||||
spin_unlock(&this_parent->d_lock);
|
spin_unlock(&this_parent->d_lock);
|
||||||
rcu_read_unlock();
|
|
||||||
BUG_ON(seq & 1);
|
BUG_ON(seq & 1);
|
||||||
if (!retry)
|
if (!retry)
|
||||||
return;
|
return;
|
||||||
@@ -1580,12 +1644,8 @@ static enum d_walk_ret select_collect(void *_data, struct dentry *dentry)
|
|||||||
if (data->start == dentry)
|
if (data->start == dentry)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (dentry->d_flags & DCACHE_SHRINK_LIST) {
|
if (dentry->d_lockref.count <= 0) {
|
||||||
data->found++;
|
__move_to_shrink_list(dentry, &data->dispose);
|
||||||
} else if (!dentry->d_lockref.count) {
|
|
||||||
to_shrink_list(dentry, &data->dispose);
|
|
||||||
data->found++;
|
|
||||||
} else if (dentry->d_lockref.count < 0) {
|
|
||||||
data->found++;
|
data->found++;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
@@ -1616,17 +1676,21 @@ static enum d_walk_ret select_collect2(void *_data, struct dentry *dentry)
|
|||||||
if (data->start == dentry)
|
if (data->start == dentry)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (!dentry->d_lockref.count) {
|
if (dentry->d_lockref.count <= 0) {
|
||||||
if (dentry->d_flags & DCACHE_SHRINK_LIST) {
|
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();
|
rcu_read_lock();
|
||||||
data->victim = dentry;
|
data->victim = dentry;
|
||||||
return D_WALK_QUIT;
|
return D_WALK_QUIT;
|
||||||
}
|
}
|
||||||
to_shrink_list(dentry, &data->dispose);
|
|
||||||
} else if (dentry->d_lockref.count < 0) {
|
|
||||||
rcu_read_lock();
|
|
||||||
data->victim = dentry;
|
|
||||||
return D_WALK_QUIT;
|
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* We can return to the caller if we have found some (this
|
* We can return to the caller if we have found some (this
|
||||||
@@ -1649,7 +1713,9 @@ out:
|
|||||||
static void shrink_dcache_tree(struct dentry *parent, bool for_umount)
|
static void shrink_dcache_tree(struct dentry *parent, bool for_umount)
|
||||||
{
|
{
|
||||||
for (;;) {
|
for (;;) {
|
||||||
struct select_data data = {.start = parent};
|
struct completion_list wait;
|
||||||
|
bool need_wait = false;
|
||||||
|
struct select_data data = { .start = parent };
|
||||||
|
|
||||||
INIT_LIST_HEAD(&data.dispose);
|
INIT_LIST_HEAD(&data.dispose);
|
||||||
d_walk(parent, &data,
|
d_walk(parent, &data,
|
||||||
@@ -1667,30 +1733,32 @@ static void shrink_dcache_tree(struct dentry *parent, bool for_umount)
|
|||||||
d_walk(parent, &data, select_collect2);
|
d_walk(parent, &data, select_collect2);
|
||||||
if (data.victim) {
|
if (data.victim) {
|
||||||
struct dentry *v = 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);
|
spin_lock(&v->d_lock);
|
||||||
if (v->d_lockref.count < 0 &&
|
rcu_read_unlock();
|
||||||
!(v->d_flags & DCACHE_DENTRY_KILLED)) {
|
|
||||||
struct completion_list wait;
|
if (unlikely(v->d_lockref.count < 0)) {
|
||||||
// It's busy dying; have it notify us once
|
// It's doomed; if it isn't dead yet, notify us
|
||||||
// it becomes invisible to d_walk().
|
// once it becomes invisible to d_walk().
|
||||||
d_add_waiter(v, &wait);
|
need_wait = d_add_waiter(v, &wait);
|
||||||
spin_unlock(&v->d_lock);
|
spin_unlock(&v->d_lock);
|
||||||
rcu_read_unlock();
|
|
||||||
if (!list_empty(&data.dispose))
|
|
||||||
shrink_dentry_list(&data.dispose);
|
|
||||||
wait_for_completion(&wait.completion);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!lock_for_kill(v)) {
|
|
||||||
spin_unlock(&v->d_lock);
|
|
||||||
rcu_read_unlock();
|
|
||||||
} else {
|
} else {
|
||||||
shrink_kill(v);
|
shrink_kill(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!list_empty(&data.dispose))
|
shrink_dentry_list(&data.dispose);
|
||||||
shrink_dentry_list(&data.dispose);
|
if (unlikely(need_wait))
|
||||||
|
wait_for_completion(&wait.completion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1743,9 +1811,30 @@ void shrink_dcache_for_umount(struct super_block *sb)
|
|||||||
sb->s_root = NULL;
|
sb->s_root = NULL;
|
||||||
do_one_tree(dentry);
|
do_one_tree(dentry);
|
||||||
|
|
||||||
while (!hlist_bl_empty(&sb->s_roots)) {
|
for (;;) {
|
||||||
dentry = dget(hlist_bl_entry(hlist_bl_first(&sb->s_roots), struct dentry, d_hash));
|
spin_lock(&sb->s_roots_lock);
|
||||||
do_one_tree(dentry);
|
dentry = hlist_entry_safe(sb->s_roots.first,
|
||||||
|
struct dentry, d_sib);
|
||||||
|
if (!dentry) {
|
||||||
|
spin_unlock(&sb->s_roots_lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rcu_read_lock();
|
||||||
|
spin_unlock(&sb->s_roots_lock);
|
||||||
|
spin_lock(&dentry->d_lock);
|
||||||
|
rcu_read_unlock();
|
||||||
|
if (unlikely(dentry->d_lockref.count < 0)) {
|
||||||
|
struct completion_list wait;
|
||||||
|
bool need_wait = d_add_waiter(dentry, &wait);
|
||||||
|
|
||||||
|
spin_unlock(&dentry->d_lock);
|
||||||
|
if (need_wait)
|
||||||
|
wait_for_completion(&wait.completion);
|
||||||
|
} else {
|
||||||
|
dget_dlock(dentry);
|
||||||
|
spin_unlock(&dentry->d_lock);
|
||||||
|
do_one_tree(dentry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1915,7 +2004,7 @@ struct dentry *d_alloc_cursor(struct dentry * parent)
|
|||||||
{
|
{
|
||||||
struct dentry *dentry = d_alloc_anon(parent->d_sb);
|
struct dentry *dentry = d_alloc_anon(parent->d_sb);
|
||||||
if (dentry) {
|
if (dentry) {
|
||||||
dentry->d_flags |= DCACHE_DENTRY_CURSOR;
|
dentry->d_flags |= DCACHE_DENTRY_CURSOR | DCACHE_NORCU;
|
||||||
dentry->d_parent = dget(parent);
|
dentry->d_parent = dget(parent);
|
||||||
}
|
}
|
||||||
return dentry;
|
return dentry;
|
||||||
@@ -2166,9 +2255,9 @@ static struct dentry *__d_obtain_alias(struct inode *inode, bool disconnected)
|
|||||||
__d_set_inode_and_type(new, inode, add_flags);
|
__d_set_inode_and_type(new, inode, add_flags);
|
||||||
hlist_add_head(&new->d_alias, &inode->i_dentry);
|
hlist_add_head(&new->d_alias, &inode->i_dentry);
|
||||||
if (!disconnected) {
|
if (!disconnected) {
|
||||||
hlist_bl_lock(&sb->s_roots);
|
spin_lock(&sb->s_roots_lock);
|
||||||
hlist_bl_add_head(&new->d_hash, &sb->s_roots);
|
hlist_add_head(&new->d_sib, &sb->s_roots);
|
||||||
hlist_bl_unlock(&sb->s_roots);
|
spin_unlock(&sb->s_roots_lock);
|
||||||
}
|
}
|
||||||
spin_unlock(&new->d_lock);
|
spin_unlock(&new->d_lock);
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
@@ -2260,8 +2349,7 @@ struct dentry *d_add_ci(struct dentry *dentry, struct inode *inode,
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
if (d_in_lookup(dentry)) {
|
if (d_in_lookup(dentry)) {
|
||||||
found = d_alloc_parallel(dentry->d_parent, name,
|
found = d_alloc_parallel(dentry->d_parent, name);
|
||||||
dentry->d_wait);
|
|
||||||
if (IS_ERR(found) || !d_in_lookup(found)) {
|
if (IS_ERR(found) || !d_in_lookup(found)) {
|
||||||
iput(inode);
|
iput(inode);
|
||||||
return found;
|
return found;
|
||||||
@@ -2648,32 +2736,24 @@ static inline unsigned start_dir_add(struct inode *dir)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void end_dir_add(struct inode *dir, unsigned int n,
|
static inline void end_dir_add(struct inode *dir, unsigned int n)
|
||||||
wait_queue_head_t *d_wait)
|
|
||||||
{
|
{
|
||||||
smp_store_release(&dir->i_dir_seq, n + 2);
|
smp_store_release(&dir->i_dir_seq, n + 2);
|
||||||
preempt_enable_nested();
|
preempt_enable_nested();
|
||||||
if (wq_has_sleeper(d_wait))
|
|
||||||
wake_up_all(d_wait);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void d_wait_lookup(struct dentry *dentry)
|
static void d_wait_lookup(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
if (d_in_lookup(dentry)) {
|
if (likely(d_in_lookup(dentry))) {
|
||||||
DECLARE_WAITQUEUE(wait, current);
|
dentry->d_flags |= DCACHE_LOOKUP_WAITERS;
|
||||||
add_wait_queue(dentry->d_wait, &wait);
|
wait_var_event_spinlock(&dentry->d_flags,
|
||||||
do {
|
!d_in_lookup(dentry),
|
||||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
&dentry->d_lock);
|
||||||
spin_unlock(&dentry->d_lock);
|
|
||||||
schedule();
|
|
||||||
spin_lock(&dentry->d_lock);
|
|
||||||
} while (d_in_lookup(dentry));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct dentry *d_alloc_parallel(struct dentry *parent,
|
struct dentry *d_alloc_parallel(struct dentry *parent,
|
||||||
const struct qstr *name,
|
const struct qstr *name)
|
||||||
wait_queue_head_t *wq)
|
|
||||||
{
|
{
|
||||||
unsigned int hash = name->hash;
|
unsigned int hash = name->hash;
|
||||||
struct hlist_bl_head *b = in_lookup_hash(parent, hash);
|
struct hlist_bl_head *b = in_lookup_hash(parent, hash);
|
||||||
@@ -2694,38 +2774,33 @@ struct dentry *d_alloc_parallel(struct dentry *parent,
|
|||||||
spin_unlock(&parent->d_lock);
|
spin_unlock(&parent->d_lock);
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
rcu_read_lock();
|
|
||||||
seq = smp_load_acquire(&parent->d_inode->i_dir_seq);
|
seq = smp_load_acquire(&parent->d_inode->i_dir_seq);
|
||||||
r_seq = read_seqbegin(&rename_lock);
|
r_seq = read_seqbegin(&rename_lock);
|
||||||
|
rcu_read_lock();
|
||||||
dentry = __d_lookup_rcu(parent, name, &d_seq);
|
dentry = __d_lookup_rcu(parent, name, &d_seq);
|
||||||
if (unlikely(dentry)) {
|
if (unlikely(dentry)) {
|
||||||
if (!lockref_get_not_dead(&dentry->d_lockref)) {
|
if (!lockref_get_not_dead(&dentry->d_lockref)) {
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
if (read_seqcount_retry(&dentry->d_seq, d_seq)) {
|
if (read_seqcount_retry(&dentry->d_seq, d_seq)) {
|
||||||
rcu_read_unlock();
|
|
||||||
dput(dentry);
|
dput(dentry);
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
|
||||||
dput(new);
|
dput(new);
|
||||||
return dentry;
|
return dentry;
|
||||||
}
|
}
|
||||||
if (unlikely(read_seqretry(&rename_lock, r_seq))) {
|
rcu_read_unlock();
|
||||||
rcu_read_unlock();
|
if (unlikely(read_seqretry(&rename_lock, r_seq)))
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
|
||||||
|
|
||||||
if (unlikely(seq & 1)) {
|
if (unlikely(seq & 1))
|
||||||
rcu_read_unlock();
|
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
|
||||||
|
|
||||||
hlist_bl_lock(b);
|
hlist_bl_lock(b);
|
||||||
if (unlikely(READ_ONCE(parent->d_inode->i_dir_seq) != seq)) {
|
if (unlikely(READ_ONCE(parent->d_inode->i_dir_seq) != seq)) {
|
||||||
hlist_bl_unlock(b);
|
hlist_bl_unlock(b);
|
||||||
rcu_read_unlock();
|
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
@@ -2742,19 +2817,20 @@ retry:
|
|||||||
continue;
|
continue;
|
||||||
if (!d_same_name(dentry, parent, name))
|
if (!d_same_name(dentry, parent, name))
|
||||||
continue;
|
continue;
|
||||||
|
rcu_read_lock();
|
||||||
hlist_bl_unlock(b);
|
hlist_bl_unlock(b);
|
||||||
|
spin_lock(&dentry->d_lock);
|
||||||
|
rcu_read_unlock();
|
||||||
/* now we can try to grab a reference */
|
/* now we can try to grab a reference */
|
||||||
if (!lockref_get_not_dead(&dentry->d_lockref)) {
|
if (unlikely(dentry->d_lockref.count < 0)) {
|
||||||
rcu_read_unlock();
|
spin_unlock(&dentry->d_lock);
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
rcu_read_unlock();
|
|
||||||
/*
|
/*
|
||||||
* somebody is likely to be still doing lookup for it;
|
* somebody is likely to be still doing lookup for it;
|
||||||
* wait for them to finish
|
* pin it and wait for them to finish
|
||||||
*/
|
*/
|
||||||
spin_lock(&dentry->d_lock);
|
dget_dlock(dentry);
|
||||||
d_wait_lookup(dentry);
|
d_wait_lookup(dentry);
|
||||||
/*
|
/*
|
||||||
* it's not in-lookup anymore; in principle we should repeat
|
* it's not in-lookup anymore; in principle we should repeat
|
||||||
@@ -2775,8 +2851,6 @@ retry:
|
|||||||
dput(new);
|
dput(new);
|
||||||
return dentry;
|
return dentry;
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
|
||||||
new->d_wait = wq;
|
|
||||||
hlist_bl_add_head(&new->d_in_lookup_hash, b);
|
hlist_bl_add_head(&new->d_in_lookup_hash, b);
|
||||||
hlist_bl_unlock(b);
|
hlist_bl_unlock(b);
|
||||||
return new;
|
return new;
|
||||||
@@ -2788,13 +2862,26 @@ mismatch:
|
|||||||
EXPORT_SYMBOL(d_alloc_parallel);
|
EXPORT_SYMBOL(d_alloc_parallel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* - Unhash the dentry
|
* Move dentry from in-lookup state to busy-negative one.
|
||||||
* - Retrieve and clear the waitqueue head in dentry
|
*
|
||||||
* - Return the waitqueue head
|
* From now on d_in_lookup(dentry) will return false and dentry is gone from
|
||||||
|
* in-lookup hash.
|
||||||
|
*
|
||||||
|
* Anyone who had been waiting on it in d_alloc_parallel() is free to
|
||||||
|
* proceed after that. Note that waking such waiters up is left to
|
||||||
|
* the callers; PREEMPT_RT kernels can't have that wakeup done while
|
||||||
|
* in write-side critical area for ->i_dir_seq, so it's done by calling
|
||||||
|
* __d_wake_in_lookup_waiters() once it's safe to do so.
|
||||||
|
*
|
||||||
|
* Both __d_lookup_unhash() and __d_wake_in_lookup_waiters() should
|
||||||
|
* be called within the same ->d_lock scope. PAR_LOOKUP is cleared
|
||||||
|
* here, while LOOKUP_WAITERS (set by somebody finding dentry in
|
||||||
|
* the in-lookup hash and setting down to wait) is checked and cleared
|
||||||
|
* in __d_wake_in_lookup_waiters(). Both are gone by the end of
|
||||||
|
* ->d_lock scope.
|
||||||
*/
|
*/
|
||||||
static wait_queue_head_t *__d_lookup_unhash(struct dentry *dentry)
|
static void __d_lookup_unhash(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
wait_queue_head_t *d_wait;
|
|
||||||
struct hlist_bl_head *b;
|
struct hlist_bl_head *b;
|
||||||
|
|
||||||
lockdep_assert_held(&dentry->d_lock);
|
lockdep_assert_held(&dentry->d_lock);
|
||||||
@@ -2803,18 +2890,23 @@ static wait_queue_head_t *__d_lookup_unhash(struct dentry *dentry)
|
|||||||
hlist_bl_lock(b);
|
hlist_bl_lock(b);
|
||||||
dentry->d_flags &= ~DCACHE_PAR_LOOKUP;
|
dentry->d_flags &= ~DCACHE_PAR_LOOKUP;
|
||||||
__hlist_bl_del(&dentry->d_in_lookup_hash);
|
__hlist_bl_del(&dentry->d_in_lookup_hash);
|
||||||
d_wait = dentry->d_wait;
|
|
||||||
dentry->d_wait = NULL;
|
|
||||||
hlist_bl_unlock(b);
|
hlist_bl_unlock(b);
|
||||||
dentry->waiters = NULL;
|
dentry->waiters = NULL;
|
||||||
INIT_LIST_HEAD(&dentry->d_lru);
|
}
|
||||||
return d_wait;
|
|
||||||
|
static inline void __d_wake_in_lookup_waiters(struct dentry *dentry)
|
||||||
|
{
|
||||||
|
if (dentry->d_flags & DCACHE_LOOKUP_WAITERS) {
|
||||||
|
wake_up_var_locked(&dentry->d_flags, &dentry->d_lock);
|
||||||
|
dentry->d_flags &= ~DCACHE_LOOKUP_WAITERS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void __d_lookup_unhash_wake(struct dentry *dentry)
|
void __d_lookup_unhash_wake(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
spin_lock(&dentry->d_lock);
|
spin_lock(&dentry->d_lock);
|
||||||
wake_up_all(__d_lookup_unhash(dentry));
|
__d_lookup_unhash(dentry);
|
||||||
|
__d_wake_in_lookup_waiters(dentry);
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(__d_lookup_unhash_wake);
|
EXPORT_SYMBOL(__d_lookup_unhash_wake);
|
||||||
@@ -2824,14 +2916,13 @@ EXPORT_SYMBOL(__d_lookup_unhash_wake);
|
|||||||
static inline void __d_add(struct dentry *dentry, struct inode *inode,
|
static inline void __d_add(struct dentry *dentry, struct inode *inode,
|
||||||
const struct dentry_operations *ops)
|
const struct dentry_operations *ops)
|
||||||
{
|
{
|
||||||
wait_queue_head_t *d_wait;
|
|
||||||
struct inode *dir = NULL;
|
struct inode *dir = NULL;
|
||||||
unsigned n;
|
unsigned n;
|
||||||
spin_lock(&dentry->d_lock);
|
spin_lock(&dentry->d_lock);
|
||||||
if (unlikely(d_in_lookup(dentry))) {
|
if (unlikely(d_in_lookup(dentry))) {
|
||||||
dir = dentry->d_parent->d_inode;
|
dir = dentry->d_parent->d_inode;
|
||||||
n = start_dir_add(dir);
|
n = start_dir_add(dir);
|
||||||
d_wait = __d_lookup_unhash(dentry);
|
__d_lookup_unhash(dentry);
|
||||||
}
|
}
|
||||||
if (unlikely(ops))
|
if (unlikely(ops))
|
||||||
d_set_d_op(dentry, ops);
|
d_set_d_op(dentry, ops);
|
||||||
@@ -2844,8 +2935,10 @@ static inline void __d_add(struct dentry *dentry, struct inode *inode,
|
|||||||
fsnotify_update_flags(dentry);
|
fsnotify_update_flags(dentry);
|
||||||
}
|
}
|
||||||
__d_rehash(dentry);
|
__d_rehash(dentry);
|
||||||
if (dir)
|
if (dir) {
|
||||||
end_dir_add(dir, n, d_wait);
|
end_dir_add(dir, n);
|
||||||
|
__d_wake_in_lookup_waiters(dentry);
|
||||||
|
}
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
if (inode)
|
if (inode)
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
@@ -2958,7 +3051,6 @@ static void __d_move(struct dentry *dentry, struct dentry *target,
|
|||||||
bool exchange)
|
bool exchange)
|
||||||
{
|
{
|
||||||
struct dentry *old_parent, *p;
|
struct dentry *old_parent, *p;
|
||||||
wait_queue_head_t *d_wait;
|
|
||||||
struct inode *dir = NULL;
|
struct inode *dir = NULL;
|
||||||
unsigned n;
|
unsigned n;
|
||||||
|
|
||||||
@@ -2989,7 +3081,7 @@ static void __d_move(struct dentry *dentry, struct dentry *target,
|
|||||||
if (unlikely(d_in_lookup(target))) {
|
if (unlikely(d_in_lookup(target))) {
|
||||||
dir = target->d_parent->d_inode;
|
dir = target->d_parent->d_inode;
|
||||||
n = start_dir_add(dir);
|
n = start_dir_add(dir);
|
||||||
d_wait = __d_lookup_unhash(target);
|
__d_lookup_unhash(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
write_seqcount_begin(&dentry->d_seq);
|
write_seqcount_begin(&dentry->d_seq);
|
||||||
@@ -3028,9 +3120,10 @@ static void __d_move(struct dentry *dentry, struct dentry *target,
|
|||||||
write_seqcount_end(&target->d_seq);
|
write_seqcount_end(&target->d_seq);
|
||||||
write_seqcount_end(&dentry->d_seq);
|
write_seqcount_end(&dentry->d_seq);
|
||||||
|
|
||||||
if (dir)
|
if (dir) {
|
||||||
end_dir_add(dir, n, d_wait);
|
end_dir_add(dir, n);
|
||||||
|
__d_wake_in_lookup_waiters(target);
|
||||||
|
}
|
||||||
if (dentry->d_parent != old_parent)
|
if (dentry->d_parent != old_parent)
|
||||||
spin_unlock(&dentry->d_parent->d_lock);
|
spin_unlock(&dentry->d_parent->d_lock);
|
||||||
if (dentry != old_parent)
|
if (dentry != old_parent)
|
||||||
@@ -3151,7 +3244,7 @@ struct dentry *d_splice_alias_ops(struct inode *inode, struct dentry *dentry,
|
|||||||
security_d_instantiate(dentry, inode);
|
security_d_instantiate(dentry, inode);
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
if (S_ISDIR(inode->i_mode)) {
|
if (S_ISDIR(inode->i_mode)) {
|
||||||
struct dentry *new = __d_find_any_alias(inode);
|
struct dentry *new = __d_find_dir_alias(inode);
|
||||||
if (unlikely(new)) {
|
if (unlikely(new)) {
|
||||||
/* The reference to new ensures it remains an alias */
|
/* The reference to new ensures it remains an alias */
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
@@ -3176,6 +3269,12 @@ struct dentry *d_splice_alias_ops(struct inode *inode, struct dentry *dentry,
|
|||||||
}
|
}
|
||||||
dput(old_parent);
|
dput(old_parent);
|
||||||
} else {
|
} else {
|
||||||
|
if (unlikely(!hlist_unhashed(&new->d_sib))) {
|
||||||
|
// secondary root getting spliced
|
||||||
|
spin_lock(&new->d_lock);
|
||||||
|
unlink_secondary_root(new);
|
||||||
|
spin_unlock(&new->d_lock);
|
||||||
|
}
|
||||||
__d_move(new, dentry, false);
|
__d_move(new, dentry, false);
|
||||||
write_sequnlock(&rename_lock);
|
write_sequnlock(&rename_lock);
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-5
@@ -53,10 +53,10 @@ find_acceptable_alias(struct dentry *result,
|
|||||||
inode = result->d_inode;
|
inode = result->d_inode;
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
for_each_alias(dentry, inode) {
|
for_each_alias(dentry, inode) {
|
||||||
dget(dentry);
|
if (!dget_alias_ilocked(dentry))
|
||||||
|
continue;
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
if (toput)
|
dput(toput);
|
||||||
dput(toput);
|
|
||||||
if (dentry != result && acceptable(context, dentry)) {
|
if (dentry != result && acceptable(context, dentry)) {
|
||||||
dput(result);
|
dput(result);
|
||||||
return dentry;
|
return dentry;
|
||||||
@@ -66,8 +66,7 @@ find_acceptable_alias(struct dentry *result,
|
|||||||
}
|
}
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
|
|
||||||
if (toput)
|
dput(toput);
|
||||||
dput(toput);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -402,6 +402,8 @@ static struct file *alloc_file(const struct path *path, int flags,
|
|||||||
static inline int alloc_path_pseudo(const char *name, struct inode *inode,
|
static inline int alloc_path_pseudo(const char *name, struct inode *inode,
|
||||||
struct vfsmount *mnt, struct path *path)
|
struct vfsmount *mnt, struct path *path)
|
||||||
{
|
{
|
||||||
|
if (WARN_ON_ONCE(S_ISDIR(inode->i_mode)))
|
||||||
|
return -EINVAL;
|
||||||
path->dentry = d_alloc_pseudo(mnt->mnt_sb, &QSTR(name));
|
path->dentry = d_alloc_pseudo(mnt->mnt_sb, &QSTR(name));
|
||||||
if (!path->dentry)
|
if (!path->dentry)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|||||||
+1
-1
@@ -177,8 +177,8 @@ static void fuse_dentry_tree_work(struct work_struct *work)
|
|||||||
spin_lock(&fd->dentry->d_lock);
|
spin_lock(&fd->dentry->d_lock);
|
||||||
/* If dentry is still referenced, let next dput release it */
|
/* If dentry is still referenced, let next dput release it */
|
||||||
fd->dentry->d_flags |= DCACHE_OP_DELETE;
|
fd->dentry->d_flags |= DCACHE_OP_DELETE;
|
||||||
|
__move_to_shrink_list(fd->dentry, &dispose);
|
||||||
spin_unlock(&fd->dentry->d_lock);
|
spin_unlock(&fd->dentry->d_lock);
|
||||||
d_dispose_if_unused(fd->dentry, &dispose);
|
|
||||||
if (need_resched()) {
|
if (need_resched()) {
|
||||||
spin_unlock(&dentry_hash[i].lock);
|
spin_unlock(&dentry_hash[i].lock);
|
||||||
cond_resched();
|
cond_resched();
|
||||||
|
|||||||
+1
-2
@@ -164,7 +164,6 @@ static int fuse_direntplus_link(struct file *file,
|
|||||||
struct inode *dir = d_inode(parent);
|
struct inode *dir = d_inode(parent);
|
||||||
struct fuse_conn *fc;
|
struct fuse_conn *fc;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
|
|
||||||
int epoch;
|
int epoch;
|
||||||
|
|
||||||
if (!o->nodeid) {
|
if (!o->nodeid) {
|
||||||
@@ -201,7 +200,7 @@ static int fuse_direntplus_link(struct file *file,
|
|||||||
dentry = d_lookup(parent, &name);
|
dentry = d_lookup(parent, &name);
|
||||||
if (!dentry) {
|
if (!dentry) {
|
||||||
retry:
|
retry:
|
||||||
dentry = d_alloc_parallel(parent, &name, &wq);
|
dentry = d_alloc_parallel(parent, &name);
|
||||||
if (IS_ERR(dentry))
|
if (IS_ERR(dentry))
|
||||||
return PTR_ERR(dentry);
|
return PTR_ERR(dentry);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-4
@@ -1896,13 +1896,12 @@ static struct dentry *__lookup_slow(const struct qstr *name,
|
|||||||
{
|
{
|
||||||
struct dentry *dentry, *old;
|
struct dentry *dentry, *old;
|
||||||
struct inode *inode = dir->d_inode;
|
struct inode *inode = dir->d_inode;
|
||||||
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
|
|
||||||
|
|
||||||
/* Don't go there if it's already dead */
|
/* Don't go there if it's already dead */
|
||||||
if (unlikely(IS_DEADDIR(inode)))
|
if (unlikely(IS_DEADDIR(inode)))
|
||||||
return ERR_PTR(-ENOENT);
|
return ERR_PTR(-ENOENT);
|
||||||
again:
|
again:
|
||||||
dentry = d_alloc_parallel(dir, name, &wq);
|
dentry = d_alloc_parallel(dir, name);
|
||||||
if (IS_ERR(dentry))
|
if (IS_ERR(dentry))
|
||||||
return dentry;
|
return dentry;
|
||||||
if (unlikely(!d_in_lookup(dentry))) {
|
if (unlikely(!d_in_lookup(dentry))) {
|
||||||
@@ -4413,7 +4412,6 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
|
|||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
int error, create_error = 0;
|
int error, create_error = 0;
|
||||||
umode_t mode = op->mode;
|
umode_t mode = op->mode;
|
||||||
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
|
|
||||||
|
|
||||||
if (unlikely(IS_DEADDIR(dir_inode)))
|
if (unlikely(IS_DEADDIR(dir_inode)))
|
||||||
return ERR_PTR(-ENOENT);
|
return ERR_PTR(-ENOENT);
|
||||||
@@ -4422,7 +4420,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
|
|||||||
dentry = d_lookup(dir, &nd->last);
|
dentry = d_lookup(dir, &nd->last);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (!dentry) {
|
if (!dentry) {
|
||||||
dentry = d_alloc_parallel(dir, &nd->last, &wq);
|
dentry = d_alloc_parallel(dir, &nd->last);
|
||||||
if (IS_ERR(dentry))
|
if (IS_ERR(dentry))
|
||||||
return dentry;
|
return dentry;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-4
@@ -726,7 +726,6 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry,
|
|||||||
unsigned long dir_verifier)
|
unsigned long dir_verifier)
|
||||||
{
|
{
|
||||||
struct qstr filename = QSTR_INIT(entry->name, entry->len);
|
struct qstr filename = QSTR_INIT(entry->name, entry->len);
|
||||||
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
|
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
struct dentry *alias;
|
struct dentry *alias;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
@@ -755,7 +754,7 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry,
|
|||||||
dentry = d_lookup(parent, &filename);
|
dentry = d_lookup(parent, &filename);
|
||||||
again:
|
again:
|
||||||
if (!dentry) {
|
if (!dentry) {
|
||||||
dentry = d_alloc_parallel(parent, &filename, &wq);
|
dentry = d_alloc_parallel(parent, &filename);
|
||||||
if (IS_ERR(dentry))
|
if (IS_ERR(dentry))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2106,7 +2105,6 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
|
|||||||
struct file *file, unsigned open_flags,
|
struct file *file, unsigned open_flags,
|
||||||
umode_t mode)
|
umode_t mode)
|
||||||
{
|
{
|
||||||
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
|
|
||||||
struct nfs_open_context *ctx;
|
struct nfs_open_context *ctx;
|
||||||
struct dentry *res;
|
struct dentry *res;
|
||||||
struct iattr attr = { .ia_valid = ATTR_OPEN };
|
struct iattr attr = { .ia_valid = ATTR_OPEN };
|
||||||
@@ -2162,7 +2160,7 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
|
|||||||
d_drop(dentry);
|
d_drop(dentry);
|
||||||
switched = true;
|
switched = true;
|
||||||
dentry = d_alloc_parallel(dentry->d_parent,
|
dentry = d_alloc_parallel(dentry->d_parent,
|
||||||
&dentry->d_name, &wq);
|
&dentry->d_name);
|
||||||
if (IS_ERR(dentry))
|
if (IS_ERR(dentry))
|
||||||
return PTR_ERR(dentry);
|
return PTR_ERR(dentry);
|
||||||
if (unlikely(!d_in_lookup(dentry)))
|
if (unlikely(!d_in_lookup(dentry)))
|
||||||
|
|||||||
+2
-33
@@ -32,35 +32,6 @@
|
|||||||
|
|
||||||
#define NFSDBG_FACILITY NFSDBG_CLIENT
|
#define NFSDBG_FACILITY NFSDBG_CLIENT
|
||||||
|
|
||||||
/*
|
|
||||||
* Set the superblock root dentry.
|
|
||||||
* Note that this function frees the inode in case of error.
|
|
||||||
*/
|
|
||||||
static int nfs_superblock_set_dummy_root(struct super_block *sb, struct inode *inode)
|
|
||||||
{
|
|
||||||
/* The mntroot acts as the dummy root dentry for this superblock */
|
|
||||||
if (sb->s_root == NULL) {
|
|
||||||
sb->s_root = d_make_root(inode);
|
|
||||||
if (sb->s_root == NULL)
|
|
||||||
return -ENOMEM;
|
|
||||||
ihold(inode);
|
|
||||||
/*
|
|
||||||
* Ensure that this dentry is invisible to d_find_alias().
|
|
||||||
* Otherwise, it may be spliced into the tree by
|
|
||||||
* d_splice_alias if a parent directory from the same
|
|
||||||
* filesystem gets mounted at a later time.
|
|
||||||
* This again causes shrink_dcache_for_umount_subtree() to
|
|
||||||
* Oops, since the test for IS_ROOT() will fail.
|
|
||||||
*/
|
|
||||||
spin_lock(&d_inode(sb->s_root)->i_lock);
|
|
||||||
spin_lock(&sb->s_root->d_lock);
|
|
||||||
hlist_del_init(&sb->s_root->d_alias);
|
|
||||||
spin_unlock(&sb->s_root->d_lock);
|
|
||||||
spin_unlock(&d_inode(sb->s_root)->i_lock);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get a root dentry from the root filehandle
|
* get a root dentry from the root filehandle
|
||||||
*/
|
*/
|
||||||
@@ -99,10 +70,6 @@ int nfs_get_root(struct super_block *s, struct fs_context *fc)
|
|||||||
goto out_fattr;
|
goto out_fattr;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = nfs_superblock_set_dummy_root(s, inode);
|
|
||||||
if (error != 0)
|
|
||||||
goto out_fattr;
|
|
||||||
|
|
||||||
/* root dentries normally start off anonymous and get spliced in later
|
/* root dentries normally start off anonymous and get spliced in later
|
||||||
* if the dentry tree reaches them; however if the dentry already
|
* if the dentry tree reaches them; however if the dentry already
|
||||||
* exists, we'll pick it up at this point and use it as the root
|
* exists, we'll pick it up at this point and use it as the root
|
||||||
@@ -123,6 +90,8 @@ int nfs_get_root(struct super_block *s, struct fs_context *fc)
|
|||||||
name = NULL;
|
name = NULL;
|
||||||
}
|
}
|
||||||
spin_unlock(&root->d_lock);
|
spin_unlock(&root->d_lock);
|
||||||
|
if (!s->s_root)
|
||||||
|
s->s_root = dget(root);
|
||||||
fc->root = root;
|
fc->root = root;
|
||||||
if (server->caps & NFS_CAP_SECURITY_LABEL)
|
if (server->caps & NFS_CAP_SECURITY_LABEL)
|
||||||
kflags |= SECURITY_LSM_NATIVE_LABELS;
|
kflags |= SECURITY_LSM_NATIVE_LABELS;
|
||||||
|
|||||||
+1
-2
@@ -124,7 +124,7 @@ static int nfs_call_unlink(struct dentry *dentry, struct inode *inode, struct nf
|
|||||||
struct dentry *alias;
|
struct dentry *alias;
|
||||||
|
|
||||||
down_read_non_owner(&NFS_I(dir)->rmdir_sem);
|
down_read_non_owner(&NFS_I(dir)->rmdir_sem);
|
||||||
alias = d_alloc_parallel(dentry->d_parent, &data->args.name, &data->wq);
|
alias = d_alloc_parallel(dentry->d_parent, &data->args.name);
|
||||||
if (IS_ERR(alias)) {
|
if (IS_ERR(alias)) {
|
||||||
up_read_non_owner(&NFS_I(dir)->rmdir_sem);
|
up_read_non_owner(&NFS_I(dir)->rmdir_sem);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -185,7 +185,6 @@ nfs_async_unlink(struct dentry *dentry, const struct qstr *name)
|
|||||||
|
|
||||||
data->cred = get_current_cred();
|
data->cred = get_current_cred();
|
||||||
data->res.dir_attr = &data->dir_attr;
|
data->res.dir_attr = &data->dir_attr;
|
||||||
init_waitqueue_head(&data->wq);
|
|
||||||
|
|
||||||
status = -EBUSY;
|
status = -EBUSY;
|
||||||
spin_lock(&dentry->d_lock);
|
spin_lock(&dentry->d_lock);
|
||||||
|
|||||||
+1
-2
@@ -2111,8 +2111,7 @@ bool proc_fill_cache(struct file *file, struct dir_context *ctx,
|
|||||||
goto end_instantiate;
|
goto end_instantiate;
|
||||||
|
|
||||||
if (!child) {
|
if (!child) {
|
||||||
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
|
child = d_alloc_parallel(dir, &qname);
|
||||||
child = d_alloc_parallel(dir, &qname, &wq);
|
|
||||||
if (IS_ERR(child))
|
if (IS_ERR(child))
|
||||||
goto end_instantiate;
|
goto end_instantiate;
|
||||||
if (d_in_lookup(child)) {
|
if (d_in_lookup(child)) {
|
||||||
|
|||||||
@@ -692,8 +692,7 @@ static bool proc_sys_fill_cache(struct file *file,
|
|||||||
|
|
||||||
child = d_lookup(dir, &qname);
|
child = d_lookup(dir, &qname);
|
||||||
if (!child) {
|
if (!child) {
|
||||||
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
|
child = d_alloc_parallel(dir, &qname);
|
||||||
child = d_alloc_parallel(dir, &qname, &wq);
|
|
||||||
if (IS_ERR(child))
|
if (IS_ERR(child))
|
||||||
return false;
|
return false;
|
||||||
if (d_in_lookup(child)) {
|
if (d_in_lookup(child)) {
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
|
|||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||||
bool posix = cifs_sb_master_tcon(cifs_sb)->posix_extensions;
|
bool posix = cifs_sb_master_tcon(cifs_sb)->posix_extensions;
|
||||||
bool reparse_need_reval = false;
|
bool reparse_need_reval = false;
|
||||||
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
cifs_dbg(FYI, "%s: for %s\n", __func__, name->name);
|
cifs_dbg(FYI, "%s: for %s\n", __func__, name->name);
|
||||||
@@ -105,7 +104,7 @@ retry:
|
|||||||
(fattr->cf_flags & CIFS_FATTR_NEED_REVAL))
|
(fattr->cf_flags & CIFS_FATTR_NEED_REVAL))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dentry = d_alloc_parallel(parent, name, &wq);
|
dentry = d_alloc_parallel(parent, name);
|
||||||
}
|
}
|
||||||
if (IS_ERR(dentry))
|
if (IS_ERR(dentry))
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -359,6 +359,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,
|
|||||||
s->s_iflags |= SB_I_NODEV;
|
s->s_iflags |= SB_I_NODEV;
|
||||||
INIT_HLIST_NODE(&s->s_instances);
|
INIT_HLIST_NODE(&s->s_instances);
|
||||||
INIT_HLIST_BL_HEAD(&s->s_roots);
|
INIT_HLIST_BL_HEAD(&s->s_roots);
|
||||||
|
spin_lock_init(&s->s_roots_lock);
|
||||||
mutex_init(&s->s_sync_lock);
|
mutex_init(&s->s_sync_lock);
|
||||||
INIT_LIST_HEAD(&s->s_inodes);
|
INIT_LIST_HEAD(&s->s_inodes);
|
||||||
spin_lock_init(&s->s_inode_list_lock);
|
spin_lock_init(&s->s_inode_list_lock);
|
||||||
|
|||||||
+24
-7
@@ -116,10 +116,7 @@ struct dentry {
|
|||||||
* possible!
|
* possible!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
union {
|
struct list_head d_lru; /* LRU list */
|
||||||
struct list_head d_lru; /* LRU list */
|
|
||||||
wait_queue_head_t *d_wait; /* in-lookup ones only */
|
|
||||||
};
|
|
||||||
struct hlist_node d_sib; /* child of parent list */
|
struct hlist_node d_sib; /* child of parent list */
|
||||||
struct hlist_head d_children; /* our children */
|
struct hlist_head d_children; /* our children */
|
||||||
/*
|
/*
|
||||||
@@ -210,6 +207,9 @@ enum dentry_flags {
|
|||||||
DCACHE_REFERENCED = BIT(6), /* Recently used, don't discard. */
|
DCACHE_REFERENCED = BIT(6), /* Recently used, don't discard. */
|
||||||
DCACHE_DONTCACHE = BIT(7), /* Purge from memory on final dput() */
|
DCACHE_DONTCACHE = BIT(7), /* Purge from memory on final dput() */
|
||||||
DCACHE_CANT_MOUNT = BIT(8),
|
DCACHE_CANT_MOUNT = BIT(8),
|
||||||
|
DCACHE_LOOKUP_WAITERS = BIT(9), /* A thread is waiting for
|
||||||
|
* PAR_LOOKUP to clear
|
||||||
|
*/
|
||||||
DCACHE_SHRINK_LIST = BIT(10),
|
DCACHE_SHRINK_LIST = BIT(10),
|
||||||
DCACHE_OP_WEAK_REVALIDATE = BIT(11),
|
DCACHE_OP_WEAK_REVALIDATE = BIT(11),
|
||||||
/*
|
/*
|
||||||
@@ -256,8 +256,7 @@ extern void d_delete(struct dentry *);
|
|||||||
/* allocate/de-allocate */
|
/* allocate/de-allocate */
|
||||||
extern struct dentry * d_alloc(struct dentry *, const struct qstr *);
|
extern struct dentry * d_alloc(struct dentry *, const struct qstr *);
|
||||||
extern struct dentry * d_alloc_anon(struct super_block *);
|
extern struct dentry * d_alloc_anon(struct super_block *);
|
||||||
extern struct dentry * d_alloc_parallel(struct dentry *, const struct qstr *,
|
extern struct dentry * d_alloc_parallel(struct dentry *, const struct qstr *);
|
||||||
wait_queue_head_t *);
|
|
||||||
extern struct dentry * d_splice_alias(struct inode *, struct dentry *);
|
extern struct dentry * d_splice_alias(struct inode *, struct dentry *);
|
||||||
/* weird procfs mess; *NOT* exported */
|
/* weird procfs mess; *NOT* exported */
|
||||||
extern struct dentry * d_splice_alias_ops(struct inode *, struct dentry *,
|
extern struct dentry * d_splice_alias_ops(struct inode *, struct dentry *,
|
||||||
@@ -281,7 +280,7 @@ extern void d_tmpfile(struct file *, struct inode *);
|
|||||||
|
|
||||||
extern struct dentry *d_find_alias(struct inode *);
|
extern struct dentry *d_find_alias(struct inode *);
|
||||||
extern void d_prune_aliases(struct inode *);
|
extern void d_prune_aliases(struct inode *);
|
||||||
extern void d_dispose_if_unused(struct dentry *, struct list_head *);
|
extern bool __move_to_shrink_list(struct dentry *, struct list_head *);
|
||||||
extern void shrink_dentry_list(struct list_head *);
|
extern void shrink_dentry_list(struct list_head *);
|
||||||
|
|
||||||
extern struct dentry *d_find_alias_rcu(struct inode *);
|
extern struct dentry *d_find_alias_rcu(struct inode *);
|
||||||
@@ -366,6 +365,24 @@ static inline struct dentry *dget(struct dentry *dentry)
|
|||||||
return dentry;
|
return dentry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* dentry->d_inode->i_lock must be held by caller */
|
||||||
|
static inline bool dget_alias_ilocked(struct dentry *dentry)
|
||||||
|
{
|
||||||
|
if (likely(!(READ_ONCE(dentry->d_flags) & DCACHE_NORCU))) {
|
||||||
|
lockref_get(&dentry->d_lockref);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// NORCU dentries with zero refcount MUST NOT be grabbed
|
||||||
|
spin_lock(&dentry->d_lock);
|
||||||
|
if (dentry->d_lockref.count > 0) {
|
||||||
|
dget_dlock(dentry);
|
||||||
|
spin_unlock(&dentry->d_lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
spin_unlock(&dentry->d_lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
extern struct dentry *dget_parent(struct dentry *dentry);
|
extern struct dentry *dget_parent(struct dentry *dentry);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -162,7 +162,8 @@ struct super_block {
|
|||||||
struct unicode_map *s_encoding;
|
struct unicode_map *s_encoding;
|
||||||
__u16 s_encoding_flags;
|
__u16 s_encoding_flags;
|
||||||
#endif
|
#endif
|
||||||
struct hlist_bl_head s_roots; /* alternate root dentries for NFS */
|
struct hlist_head s_roots; /* alternate root dentries for NFS */
|
||||||
|
spinlock_t s_roots_lock;
|
||||||
struct mount *s_mounts; /* list of mounts; _not_ for fs use */
|
struct mount *s_mounts; /* list of mounts; _not_ for fs use */
|
||||||
struct block_device *s_bdev; /* can go away once we use an accessor for @s_bdev_file */
|
struct block_device *s_bdev; /* can go away once we use an accessor for @s_bdev_file */
|
||||||
struct file *s_bdev_file;
|
struct file *s_bdev_file;
|
||||||
|
|||||||
@@ -1745,7 +1745,6 @@ struct nfs_unlinkdata {
|
|||||||
struct nfs_removeargs args;
|
struct nfs_removeargs args;
|
||||||
struct nfs_removeres res;
|
struct nfs_removeres res;
|
||||||
struct dentry *dentry;
|
struct dentry *dentry;
|
||||||
wait_queue_head_t wq;
|
|
||||||
const struct cred *cred;
|
const struct cred *cred;
|
||||||
struct nfs_fattr dir_attr;
|
struct nfs_fattr dir_attr;
|
||||||
long timeout;
|
long timeout;
|
||||||
|
|||||||
Reference in New Issue
Block a user