Merge branches 'work.dcache-misc' and 'work.dcache2' into work.dcache
This commit is contained in:
commit
119dcc73a9
|
@ -1061,3 +1061,37 @@ export_operations ->encode_fh() no longer has a default implementation to
|
|||
encode FILEID_INO32_GEN* file handles.
|
||||
Filesystems that used the default implementation may use the generic helper
|
||||
generic_encode_ino32_fh() explicitly.
|
||||
|
||||
---
|
||||
|
||||
**mandatory**
|
||||
|
||||
The list of children anchored in parent dentry got turned into hlist now.
|
||||
Field names got changed (->d_children/->d_sib instead of ->d_subdirs/->d_child
|
||||
for anchor/entries resp.), so any affected places will be immediately caught
|
||||
by compiler.
|
||||
|
||||
---
|
||||
|
||||
**mandatory**
|
||||
|
||||
->d_delete() instances are now called for dentries with ->d_lock held
|
||||
and refcount equal to 0. They are not permitted to drop/regain ->d_lock.
|
||||
None of in-tree instances did anything of that sort. Make sure yours do not...
|
||||
|
||||
--
|
||||
|
||||
**mandatory**
|
||||
|
||||
->d_prune() instances are now called without ->d_lock held on the parent.
|
||||
->d_lock on dentry itself is still held; if you need per-parent exclusions (none
|
||||
of the in-tree instances did), use your own spinlock.
|
||||
|
||||
->d_iput() and ->d_release() are called with victim dentry still in the
|
||||
list of parent's children. It is still unhashed, marked killed, etc., just not
|
||||
removed from parent's ->d_children yet.
|
||||
|
||||
Anyone iterating through the list of children needs to be aware of the
|
||||
half-killed dentries that might be seen there; taking ->d_lock on those will
|
||||
see them negative, unhashed and with negative refcount, which means that most
|
||||
of the in-kernel users would've done the right thing anyway without any adjustment.
|
||||
|
|
|
@ -145,10 +145,11 @@ spufs_evict_inode(struct inode *inode)
|
|||
|
||||
static void spufs_prune_dir(struct dentry *dir)
|
||||
{
|
||||
struct dentry *dentry, *tmp;
|
||||
struct dentry *dentry;
|
||||
struct hlist_node *n;
|
||||
|
||||
inode_lock(d_inode(dir));
|
||||
list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_child) {
|
||||
hlist_for_each_entry_safe(dentry, n, &dir->d_children, d_sib) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (simple_positive(dentry)) {
|
||||
dget_dlock(dentry);
|
||||
|
|
|
@ -370,7 +370,7 @@ error:
|
|||
void afs_dynroot_depopulate(struct super_block *sb)
|
||||
{
|
||||
struct afs_net *net = afs_sb2net(sb);
|
||||
struct dentry *root = sb->s_root, *subdir, *tmp;
|
||||
struct dentry *root = sb->s_root, *subdir;
|
||||
|
||||
/* Prevent more subdirs from being created */
|
||||
mutex_lock(&net->proc_cells_lock);
|
||||
|
@ -379,10 +379,11 @@ void afs_dynroot_depopulate(struct super_block *sb)
|
|||
mutex_unlock(&net->proc_cells_lock);
|
||||
|
||||
if (root) {
|
||||
struct hlist_node *n;
|
||||
inode_lock(root->d_inode);
|
||||
|
||||
/* Remove all the pins for dirs created for manually added cells */
|
||||
list_for_each_entry_safe(subdir, tmp, &root->d_subdirs, d_child) {
|
||||
hlist_for_each_entry_safe(subdir, n, &root->d_children, d_sib) {
|
||||
if (subdir->d_fsdata) {
|
||||
subdir->d_fsdata = NULL;
|
||||
dput(subdir);
|
||||
|
|
|
@ -73,12 +73,9 @@ done:
|
|||
/* p->d_lock held */
|
||||
static struct dentry *positive_after(struct dentry *p, struct dentry *child)
|
||||
{
|
||||
if (child)
|
||||
child = list_next_entry(child, d_child);
|
||||
else
|
||||
child = list_first_entry(&p->d_subdirs, struct dentry, d_child);
|
||||
child = child ? d_next_sibling(child) : d_first_child(p);
|
||||
|
||||
list_for_each_entry_from(child, &p->d_subdirs, d_child) {
|
||||
hlist_for_each_entry_from(child, d_sib) {
|
||||
spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
if (simple_positive(child)) {
|
||||
dget_dlock(child);
|
||||
|
|
|
@ -174,7 +174,7 @@ __dcache_find_get_entry(struct dentry *parent, u64 idx,
|
|||
/*
|
||||
* When possible, we try to satisfy a readdir by peeking at the
|
||||
* dcache. We make this work by carefully ordering dentries on
|
||||
* d_child when we initially get results back from the MDS, and
|
||||
* d_children when we initially get results back from the MDS, and
|
||||
* falling back to a "normal" sync readdir if any dentries in the dir
|
||||
* are dropped.
|
||||
*
|
||||
|
|
|
@ -2128,7 +2128,7 @@ static bool drop_negative_children(struct dentry *dentry)
|
|||
goto out;
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
|
||||
hlist_for_each_entry(child, &dentry->d_children, d_sib) {
|
||||
if (d_really_is_positive(child)) {
|
||||
all_negative = false;
|
||||
break;
|
||||
|
|
|
@ -93,13 +93,13 @@ static void coda_flag_children(struct dentry *parent, int flag)
|
|||
struct dentry *de;
|
||||
|
||||
spin_lock(&parent->d_lock);
|
||||
list_for_each_entry(de, &parent->d_subdirs, d_child) {
|
||||
hlist_for_each_entry(de, &parent->d_children, d_sib) {
|
||||
struct inode *inode = d_inode_rcu(de);
|
||||
/* don't know what to do with negative dentries */
|
||||
if (d_inode(de) )
|
||||
coda_flag_inode(d_inode(de), flag);
|
||||
if (inode)
|
||||
coda_flag_inode(inode, flag);
|
||||
}
|
||||
spin_unlock(&parent->d_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
void coda_flag_inode_children(struct inode *inode, int flag)
|
||||
|
|
493
fs/dcache.c
493
fs/dcache.c
|
@ -51,8 +51,8 @@
|
|||
* - d_lru
|
||||
* - d_count
|
||||
* - d_unhashed()
|
||||
* - d_parent and d_subdirs
|
||||
* - childrens' d_child and d_parent
|
||||
* - d_parent and d_chilren
|
||||
* - childrens' d_sib and d_parent
|
||||
* - d_u.d_alias, d_inode
|
||||
*
|
||||
* Ordering:
|
||||
|
@ -537,7 +537,7 @@ void d_drop(struct dentry *dentry)
|
|||
}
|
||||
EXPORT_SYMBOL(d_drop);
|
||||
|
||||
static inline void dentry_unlist(struct dentry *dentry, struct dentry *parent)
|
||||
static inline void dentry_unlist(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *next;
|
||||
/*
|
||||
|
@ -545,12 +545,12 @@ static inline void dentry_unlist(struct dentry *dentry, struct dentry *parent)
|
|||
* attached to the dentry tree
|
||||
*/
|
||||
dentry->d_flags |= DCACHE_DENTRY_KILLED;
|
||||
if (unlikely(list_empty(&dentry->d_child)))
|
||||
if (unlikely(hlist_unhashed(&dentry->d_sib)))
|
||||
return;
|
||||
__list_del_entry(&dentry->d_child);
|
||||
__hlist_del(&dentry->d_sib);
|
||||
/*
|
||||
* Cursors can move around the list of children. While we'd been
|
||||
* a normal list member, it didn't matter - ->d_child.next would've
|
||||
* a normal list member, it didn't matter - ->d_sib.next would've
|
||||
* been updated. However, from now on it won't be and for the
|
||||
* things like d_walk() it might end up with a nasty surprise.
|
||||
* Normally d_walk() doesn't care about cursors moving around -
|
||||
|
@ -558,29 +558,27 @@ static inline void dentry_unlist(struct dentry *dentry, struct dentry *parent)
|
|||
* of its own, we get through it without ever unlocking the parent.
|
||||
* There is one exception, though - if we ascend from a child that
|
||||
* gets killed as soon as we unlock it, the next sibling is found
|
||||
* using the value left in its ->d_child.next. And if _that_
|
||||
* using the value left in its ->d_sib.next. And if _that_
|
||||
* pointed to a cursor, and cursor got moved (e.g. by lseek())
|
||||
* before d_walk() regains parent->d_lock, we'll end up skipping
|
||||
* everything the cursor had been moved past.
|
||||
*
|
||||
* Solution: make sure that the pointer left behind in ->d_child.next
|
||||
* Solution: make sure that the pointer left behind in ->d_sib.next
|
||||
* points to something that won't be moving around. I.e. skip the
|
||||
* cursors.
|
||||
*/
|
||||
while (dentry->d_child.next != &parent->d_subdirs) {
|
||||
next = list_entry(dentry->d_child.next, struct dentry, d_child);
|
||||
while (dentry->d_sib.next) {
|
||||
next = hlist_entry(dentry->d_sib.next, struct dentry, d_sib);
|
||||
if (likely(!(next->d_flags & DCACHE_DENTRY_CURSOR)))
|
||||
break;
|
||||
dentry->d_child.next = next->d_child.next;
|
||||
dentry->d_sib.next = next->d_sib.next;
|
||||
}
|
||||
}
|
||||
|
||||
static void __dentry_kill(struct dentry *dentry)
|
||||
static struct dentry *__dentry_kill(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *parent = NULL;
|
||||
bool can_free = true;
|
||||
if (!IS_ROOT(dentry))
|
||||
parent = dentry->d_parent;
|
||||
|
||||
/*
|
||||
* The dentry is now unrecoverably dead to the world.
|
||||
|
@ -600,9 +598,6 @@ static void __dentry_kill(struct dentry *dentry)
|
|||
}
|
||||
/* if it was on the hash then remove it */
|
||||
__d_drop(dentry);
|
||||
dentry_unlist(dentry, parent);
|
||||
if (parent)
|
||||
spin_unlock(&parent->d_lock);
|
||||
if (dentry->d_inode)
|
||||
dentry_unlink_inode(dentry);
|
||||
else
|
||||
|
@ -611,7 +606,14 @@ static void __dentry_kill(struct dentry *dentry)
|
|||
if (dentry->d_op && dentry->d_op->d_release)
|
||||
dentry->d_op->d_release(dentry);
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
cond_resched();
|
||||
/* now that it's negative, ->d_parent is stable */
|
||||
if (!IS_ROOT(dentry)) {
|
||||
parent = dentry->d_parent;
|
||||
spin_lock(&parent->d_lock);
|
||||
}
|
||||
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
dentry_unlist(dentry);
|
||||
if (dentry->d_flags & DCACHE_SHRINK_LIST) {
|
||||
dentry->d_flags |= DCACHE_MAY_FREE;
|
||||
can_free = false;
|
||||
|
@ -619,72 +621,101 @@ static void __dentry_kill(struct dentry *dentry)
|
|||
spin_unlock(&dentry->d_lock);
|
||||
if (likely(can_free))
|
||||
dentry_free(dentry);
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
static struct dentry *__lock_parent(struct dentry *dentry)
|
||||
{
|
||||
struct dentry *parent;
|
||||
rcu_read_lock();
|
||||
spin_unlock(&dentry->d_lock);
|
||||
again:
|
||||
parent = READ_ONCE(dentry->d_parent);
|
||||
spin_lock(&parent->d_lock);
|
||||
/*
|
||||
* We can't blindly lock dentry until we are sure
|
||||
* that we won't violate the locking order.
|
||||
* Any changes of dentry->d_parent must have
|
||||
* been done with parent->d_lock held, so
|
||||
* spin_lock() above is enough of a barrier
|
||||
* for checking if it's still our child.
|
||||
*/
|
||||
if (unlikely(parent != dentry->d_parent)) {
|
||||
if (parent && --parent->d_lockref.count) {
|
||||
spin_unlock(&parent->d_lock);
|
||||
goto again;
|
||||
return NULL;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
if (parent != dentry)
|
||||
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
else
|
||||
parent = NULL;
|
||||
return parent;
|
||||
}
|
||||
|
||||
static inline struct dentry *lock_parent(struct dentry *dentry)
|
||||
/*
|
||||
* 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 dentry *parent = dentry->d_parent;
|
||||
if (IS_ROOT(dentry))
|
||||
return NULL;
|
||||
if (likely(spin_trylock(&parent->d_lock)))
|
||||
return parent;
|
||||
return __lock_parent(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;
|
||||
}
|
||||
|
||||
static inline bool retain_dentry(struct dentry *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
|
||||
* without a lock. False in this case means "punt to locked path and recheck".
|
||||
*
|
||||
* In case we aren't locked, these predicates are not "stable". However, it is
|
||||
* sufficient that at some point after we dropped the reference the dentry was
|
||||
* hashed and the flags had the proper value. Other dentry users may have
|
||||
* re-gotten a reference to the dentry and change that, but our work is done -
|
||||
* we can leave the dentry around with a zero refcount.
|
||||
*/
|
||||
static inline bool retain_dentry(struct dentry *dentry, bool locked)
|
||||
{
|
||||
WARN_ON(d_in_lookup(dentry));
|
||||
unsigned int d_flags;
|
||||
|
||||
/* Unreachable? Get rid of it */
|
||||
smp_rmb();
|
||||
d_flags = READ_ONCE(dentry->d_flags);
|
||||
|
||||
// Unreachable? Nobody would be able to look it up, no point retaining
|
||||
if (unlikely(d_unhashed(dentry)))
|
||||
return false;
|
||||
|
||||
if (unlikely(dentry->d_flags & DCACHE_DISCONNECTED))
|
||||
// Same if it's disconnected
|
||||
if (unlikely(d_flags & DCACHE_DISCONNECTED))
|
||||
return false;
|
||||
|
||||
if (unlikely(dentry->d_flags & DCACHE_OP_DELETE)) {
|
||||
if (dentry->d_op->d_delete(dentry))
|
||||
// ->d_delete() might tell us not to bother, but that requires
|
||||
// ->d_lock; can't decide without it
|
||||
if (unlikely(d_flags & DCACHE_OP_DELETE)) {
|
||||
if (!locked || dentry->d_op->d_delete(dentry))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (unlikely(dentry->d_flags & DCACHE_DONTCACHE))
|
||||
// Explicitly told not to bother
|
||||
if (unlikely(d_flags & DCACHE_DONTCACHE))
|
||||
return false;
|
||||
|
||||
/* retain; LRU fodder */
|
||||
dentry->d_lockref.count--;
|
||||
if (unlikely(!(dentry->d_flags & DCACHE_LRU_LIST)))
|
||||
// At this point it looks like we ought to keep it. We also might
|
||||
// need to do something - put it on LRU if it wasn't there already
|
||||
// and mark it referenced if it was on LRU, but not marked yet.
|
||||
// Unfortunately, both actions require ->d_lock, so in lockless
|
||||
// case we'd have to punt rather than doing those.
|
||||
if (unlikely(!(d_flags & DCACHE_LRU_LIST))) {
|
||||
if (!locked)
|
||||
return false;
|
||||
d_lru_add(dentry);
|
||||
else if (unlikely(!(dentry->d_flags & DCACHE_REFERENCED)))
|
||||
} else if (unlikely(!(d_flags & DCACHE_REFERENCED))) {
|
||||
if (!locked)
|
||||
return false;
|
||||
dentry->d_flags |= DCACHE_REFERENCED;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -703,61 +734,12 @@ void d_mark_dontcache(struct inode *inode)
|
|||
}
|
||||
EXPORT_SYMBOL(d_mark_dontcache);
|
||||
|
||||
/*
|
||||
* Finish off a dentry we've decided to kill.
|
||||
* dentry->d_lock must be held, returns with it unlocked.
|
||||
* Returns dentry requiring refcount drop, or NULL if we're done.
|
||||
*/
|
||||
static struct dentry *dentry_kill(struct dentry *dentry)
|
||||
__releases(dentry->d_lock)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct dentry *parent = NULL;
|
||||
|
||||
if (inode && unlikely(!spin_trylock(&inode->i_lock)))
|
||||
goto slow_positive;
|
||||
|
||||
if (!IS_ROOT(dentry)) {
|
||||
parent = dentry->d_parent;
|
||||
if (unlikely(!spin_trylock(&parent->d_lock))) {
|
||||
parent = __lock_parent(dentry);
|
||||
if (likely(inode || !dentry->d_inode))
|
||||
goto got_locks;
|
||||
/* negative that became positive */
|
||||
if (parent)
|
||||
spin_unlock(&parent->d_lock);
|
||||
inode = dentry->d_inode;
|
||||
goto slow_positive;
|
||||
}
|
||||
}
|
||||
__dentry_kill(dentry);
|
||||
return parent;
|
||||
|
||||
slow_positive:
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_lock(&inode->i_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
parent = lock_parent(dentry);
|
||||
got_locks:
|
||||
if (unlikely(dentry->d_lockref.count != 1)) {
|
||||
dentry->d_lockref.count--;
|
||||
} else if (likely(!retain_dentry(dentry))) {
|
||||
__dentry_kill(dentry);
|
||||
return parent;
|
||||
}
|
||||
/* we are keeping it, after all */
|
||||
if (inode)
|
||||
spin_unlock(&inode->i_lock);
|
||||
if (parent)
|
||||
spin_unlock(&parent->d_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to do a lockless dput(), and return whether that was successful.
|
||||
*
|
||||
* If unsuccessful, we return false, having already taken the dentry lock.
|
||||
* In that case refcount is guaranteed to be zero and we have already
|
||||
* 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!
|
||||
|
@ -765,18 +747,9 @@ got_locks:
|
|||
static inline bool fast_dput(struct dentry *dentry)
|
||||
{
|
||||
int ret;
|
||||
unsigned int d_flags;
|
||||
|
||||
/*
|
||||
* If we have a d_op->d_delete() operation, we sould not
|
||||
* let the dentry count go to zero, so use "put_or_lock".
|
||||
*/
|
||||
if (unlikely(dentry->d_flags & DCACHE_OP_DELETE))
|
||||
return lockref_put_or_lock(&dentry->d_lockref);
|
||||
|
||||
/*
|
||||
* .. otherwise, we can try to just decrement the
|
||||
* lockref optimistically.
|
||||
* try to decrement the lockref optimistically.
|
||||
*/
|
||||
ret = lockref_put_return(&dentry->d_lockref);
|
||||
|
||||
|
@ -787,12 +760,12 @@ static inline bool fast_dput(struct dentry *dentry)
|
|||
*/
|
||||
if (unlikely(ret < 0)) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (dentry->d_lockref.count > 1) {
|
||||
dentry->d_lockref.count--;
|
||||
if (WARN_ON_ONCE(dentry->d_lockref.count <= 0)) {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
dentry->d_lockref.count--;
|
||||
goto locked;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -802,45 +775,18 @@ static inline bool fast_dput(struct dentry *dentry)
|
|||
return true;
|
||||
|
||||
/*
|
||||
* Careful, careful. The reference count went down
|
||||
* to zero, but we don't hold the dentry lock, so
|
||||
* somebody else could get it again, and do another
|
||||
* dput(), and we need to not race with that.
|
||||
*
|
||||
* However, there is a very special and common case
|
||||
* where we don't care, because there is nothing to
|
||||
* do: the dentry is still hashed, it does not have
|
||||
* a 'delete' op, and it's referenced and already on
|
||||
* the LRU list.
|
||||
*
|
||||
* NOTE! Since we aren't locked, these values are
|
||||
* not "stable". However, it is sufficient that at
|
||||
* some point after we dropped the reference the
|
||||
* dentry was hashed and the flags had the proper
|
||||
* value. Other dentry users may have re-gotten
|
||||
* a reference to the dentry and change that, but
|
||||
* our work is done - we can leave the dentry
|
||||
* around with a zero refcount.
|
||||
*
|
||||
* Nevertheless, there are two cases that we should kill
|
||||
* the dentry anyway.
|
||||
* 1. free disconnected dentries as soon as their refcount
|
||||
* reached zero.
|
||||
* 2. free dentries if they should not be cached.
|
||||
* Can we decide that decrement of refcount is all we needed without
|
||||
* taking the lock? There's a very common case when it's all we need -
|
||||
* dentry looks like it ought to be retained and there's nothing else
|
||||
* to do.
|
||||
*/
|
||||
smp_rmb();
|
||||
d_flags = READ_ONCE(dentry->d_flags);
|
||||
d_flags &= DCACHE_REFERENCED | DCACHE_LRU_LIST |
|
||||
DCACHE_DISCONNECTED | DCACHE_DONTCACHE;
|
||||
|
||||
/* Nothing to do? Dropping the reference was all we needed? */
|
||||
if (d_flags == (DCACHE_REFERENCED | DCACHE_LRU_LIST) && !d_unhashed(dentry))
|
||||
if (retain_dentry(dentry, false))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Not the fast normal case? Get the lock. We've already decremented
|
||||
* the refcount, but we'll need to re-check the situation after
|
||||
* getting the lock.
|
||||
* Either not worth retaining or we can't tell without the lock.
|
||||
* Get the lock, then. We've already decremented the refcount to 0,
|
||||
* but we'll need to re-check the situation after getting the lock.
|
||||
*/
|
||||
spin_lock(&dentry->d_lock);
|
||||
|
||||
|
@ -850,17 +796,11 @@ static inline bool fast_dput(struct dentry *dentry)
|
|||
* else could have killed it and marked it dead. Either way, we
|
||||
* don't need to do anything else.
|
||||
*/
|
||||
if (dentry->d_lockref.count) {
|
||||
locked:
|
||||
if (dentry->d_lockref.count || retain_dentry(dentry, true)) {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-get the reference we optimistically dropped. We hold the
|
||||
* lock, and we just tested that it was zero, so we can just
|
||||
* set it to 1.
|
||||
*/
|
||||
dentry->d_lockref.count = 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -893,39 +833,37 @@ static inline bool fast_dput(struct dentry *dentry)
|
|||
*/
|
||||
void dput(struct dentry *dentry)
|
||||
{
|
||||
while (dentry) {
|
||||
might_sleep();
|
||||
|
||||
rcu_read_lock();
|
||||
if (likely(fast_dput(dentry))) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Slow case: now with the dentry lock held */
|
||||
if (!dentry)
|
||||
return;
|
||||
might_sleep();
|
||||
rcu_read_lock();
|
||||
if (likely(fast_dput(dentry))) {
|
||||
rcu_read_unlock();
|
||||
|
||||
if (likely(retain_dentry(dentry))) {
|
||||
return;
|
||||
}
|
||||
while (lock_for_kill(dentry)) {
|
||||
rcu_read_unlock();
|
||||
dentry = __dentry_kill(dentry);
|
||||
if (!dentry)
|
||||
return;
|
||||
if (retain_dentry(dentry, true)) {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
dentry = dentry_kill(dentry);
|
||||
rcu_read_lock();
|
||||
}
|
||||
rcu_read_unlock();
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(dput);
|
||||
|
||||
static void __dput_to_list(struct dentry *dentry, struct list_head *list)
|
||||
static void to_shrink_list(struct dentry *dentry, struct list_head *list)
|
||||
__must_hold(&dentry->d_lock)
|
||||
{
|
||||
if (dentry->d_flags & DCACHE_SHRINK_LIST) {
|
||||
/* let the owner of the list it's on deal with it */
|
||||
--dentry->d_lockref.count;
|
||||
} else {
|
||||
if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) {
|
||||
if (dentry->d_flags & DCACHE_LRU_LIST)
|
||||
d_lru_del(dentry);
|
||||
if (!--dentry->d_lockref.count)
|
||||
d_shrink_add(dentry, list);
|
||||
d_shrink_add(dentry, list);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -937,8 +875,7 @@ void dput_to_list(struct dentry *dentry, struct list_head *list)
|
|||
return;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
if (!retain_dentry(dentry))
|
||||
__dput_to_list(dentry, list);
|
||||
to_shrink_list(dentry, list);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
|
||||
|
@ -1096,104 +1033,53 @@ struct dentry *d_find_alias_rcu(struct inode *inode)
|
|||
*/
|
||||
void d_prune_aliases(struct inode *inode)
|
||||
{
|
||||
LIST_HEAD(dispose);
|
||||
struct dentry *dentry;
|
||||
restart:
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (!dentry->d_lockref.count) {
|
||||
struct dentry *parent = lock_parent(dentry);
|
||||
if (likely(!dentry->d_lockref.count)) {
|
||||
__dentry_kill(dentry);
|
||||
dput(parent);
|
||||
goto restart;
|
||||
}
|
||||
if (parent)
|
||||
spin_unlock(&parent->d_lock);
|
||||
}
|
||||
if (!dentry->d_lockref.count)
|
||||
to_shrink_list(dentry, &dispose);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
spin_unlock(&inode->i_lock);
|
||||
shrink_dentry_list(&dispose);
|
||||
}
|
||||
EXPORT_SYMBOL(d_prune_aliases);
|
||||
|
||||
/*
|
||||
* Lock a dentry from shrink list.
|
||||
* 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 has been disrupted or grabbed, leaving
|
||||
* the caller to kick it off-list. Otherwise, return true and have
|
||||
* that dentry's inode and parent both locked.
|
||||
*/
|
||||
static bool shrink_lock_dentry(struct dentry *dentry)
|
||||
static inline void shrink_kill(struct dentry *victim)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct dentry *parent;
|
||||
|
||||
if (dentry->d_lockref.count)
|
||||
return false;
|
||||
|
||||
inode = dentry->d_inode;
|
||||
if (inode && unlikely(!spin_trylock(&inode->i_lock))) {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_lock(&inode->i_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (unlikely(dentry->d_lockref.count))
|
||||
goto out;
|
||||
/* changed inode means that somebody had grabbed it */
|
||||
if (unlikely(inode != dentry->d_inode))
|
||||
goto out;
|
||||
}
|
||||
|
||||
parent = dentry->d_parent;
|
||||
if (IS_ROOT(dentry) || likely(spin_trylock(&parent->d_lock)))
|
||||
return true;
|
||||
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_lock(&parent->d_lock);
|
||||
if (unlikely(parent != dentry->d_parent)) {
|
||||
spin_unlock(&parent->d_lock);
|
||||
spin_lock(&dentry->d_lock);
|
||||
goto out;
|
||||
}
|
||||
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
if (likely(!dentry->d_lockref.count))
|
||||
return true;
|
||||
spin_unlock(&parent->d_lock);
|
||||
out:
|
||||
if (inode)
|
||||
spin_unlock(&inode->i_lock);
|
||||
return false;
|
||||
do {
|
||||
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)
|
||||
{
|
||||
while (!list_empty(list)) {
|
||||
struct dentry *dentry, *parent;
|
||||
struct dentry *dentry;
|
||||
|
||||
dentry = list_entry(list->prev, struct dentry, d_lru);
|
||||
spin_lock(&dentry->d_lock);
|
||||
rcu_read_lock();
|
||||
if (!shrink_lock_dentry(dentry)) {
|
||||
bool can_free = false;
|
||||
if (!lock_for_kill(dentry)) {
|
||||
bool can_free;
|
||||
rcu_read_unlock();
|
||||
d_shrink_del(dentry);
|
||||
if (dentry->d_lockref.count < 0)
|
||||
can_free = dentry->d_flags & DCACHE_MAY_FREE;
|
||||
can_free = dentry->d_flags & DCACHE_MAY_FREE;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
if (can_free)
|
||||
dentry_free(dentry);
|
||||
continue;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
d_shrink_del(dentry);
|
||||
parent = dentry->d_parent;
|
||||
if (parent != dentry)
|
||||
__dput_to_list(parent, list);
|
||||
__dentry_kill(dentry);
|
||||
shrink_kill(dentry);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1343,8 +1229,7 @@ enum d_walk_ret {
|
|||
static void d_walk(struct dentry *parent, void *data,
|
||||
enum d_walk_ret (*enter)(void *, struct dentry *))
|
||||
{
|
||||
struct dentry *this_parent;
|
||||
struct list_head *next;
|
||||
struct dentry *this_parent, *dentry;
|
||||
unsigned seq = 0;
|
||||
enum d_walk_ret ret;
|
||||
bool retry = true;
|
||||
|
@ -1366,13 +1251,9 @@ again:
|
|||
break;
|
||||
}
|
||||
repeat:
|
||||
next = this_parent->d_subdirs.next;
|
||||
dentry = d_first_child(this_parent);
|
||||
resume:
|
||||
while (next != &this_parent->d_subdirs) {
|
||||
struct list_head *tmp = next;
|
||||
struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
|
||||
next = tmp->next;
|
||||
|
||||
hlist_for_each_entry_from(dentry, d_sib) {
|
||||
if (unlikely(dentry->d_flags & DCACHE_DENTRY_CURSOR))
|
||||
continue;
|
||||
|
||||
|
@ -1393,7 +1274,7 @@ resume:
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!list_empty(&dentry->d_subdirs)) {
|
||||
if (!hlist_empty(&dentry->d_children)) {
|
||||
spin_unlock(&this_parent->d_lock);
|
||||
spin_release(&dentry->d_lock.dep_map, _RET_IP_);
|
||||
this_parent = dentry;
|
||||
|
@ -1408,24 +1289,23 @@ resume:
|
|||
rcu_read_lock();
|
||||
ascend:
|
||||
if (this_parent != parent) {
|
||||
struct dentry *child = this_parent;
|
||||
this_parent = child->d_parent;
|
||||
dentry = this_parent;
|
||||
this_parent = dentry->d_parent;
|
||||
|
||||
spin_unlock(&child->d_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_lock(&this_parent->d_lock);
|
||||
|
||||
/* might go back up the wrong parent if we have had a rename. */
|
||||
if (need_seqretry(&rename_lock, seq))
|
||||
goto rename_retry;
|
||||
/* go into the first sibling still alive */
|
||||
do {
|
||||
next = child->d_child.next;
|
||||
if (next == &this_parent->d_subdirs)
|
||||
goto ascend;
|
||||
child = list_entry(next, struct dentry, d_child);
|
||||
} while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED));
|
||||
rcu_read_unlock();
|
||||
goto resume;
|
||||
hlist_for_each_entry_continue(dentry, d_sib) {
|
||||
if (likely(!(dentry->d_flags & DCACHE_DENTRY_KILLED))) {
|
||||
rcu_read_unlock();
|
||||
goto resume;
|
||||
}
|
||||
}
|
||||
goto ascend;
|
||||
}
|
||||
if (need_seqretry(&rename_lock, seq))
|
||||
goto rename_retry;
|
||||
|
@ -1525,7 +1405,7 @@ out:
|
|||
* Search the dentry child list of the specified parent,
|
||||
* and move any unused dentries to the end of the unused
|
||||
* list for prune_dcache(). We descend to the next level
|
||||
* whenever the d_subdirs list is non-empty and continue
|
||||
* whenever the d_children list is non-empty and continue
|
||||
* searching.
|
||||
*
|
||||
* It returns zero iff there are no unused children,
|
||||
|
@ -1555,13 +1435,11 @@ static enum d_walk_ret select_collect(void *_data, struct dentry *dentry)
|
|||
|
||||
if (dentry->d_flags & DCACHE_SHRINK_LIST) {
|
||||
data->found++;
|
||||
} else {
|
||||
if (dentry->d_flags & DCACHE_LRU_LIST)
|
||||
d_lru_del(dentry);
|
||||
if (!dentry->d_lockref.count) {
|
||||
d_shrink_add(dentry, &data->dispose);
|
||||
data->found++;
|
||||
}
|
||||
} else if (!dentry->d_lockref.count) {
|
||||
to_shrink_list(dentry, &data->dispose);
|
||||
data->found++;
|
||||
} else if (dentry->d_lockref.count < 0) {
|
||||
data->found++;
|
||||
}
|
||||
/*
|
||||
* We can return to the caller if we have found some (this
|
||||
|
@ -1582,17 +1460,13 @@ static enum d_walk_ret select_collect2(void *_data, struct dentry *dentry)
|
|||
if (data->start == dentry)
|
||||
goto out;
|
||||
|
||||
if (dentry->d_flags & DCACHE_SHRINK_LIST) {
|
||||
if (!dentry->d_lockref.count) {
|
||||
if (!dentry->d_lockref.count) {
|
||||
if (dentry->d_flags & DCACHE_SHRINK_LIST) {
|
||||
rcu_read_lock();
|
||||
data->victim = dentry;
|
||||
return D_WALK_QUIT;
|
||||
}
|
||||
} else {
|
||||
if (dentry->d_flags & DCACHE_LRU_LIST)
|
||||
d_lru_del(dentry);
|
||||
if (!dentry->d_lockref.count)
|
||||
d_shrink_add(dentry, &data->dispose);
|
||||
to_shrink_list(dentry, &data->dispose);
|
||||
}
|
||||
/*
|
||||
* We can return to the caller if we have found some (this
|
||||
|
@ -1630,17 +1504,12 @@ void shrink_dcache_parent(struct dentry *parent)
|
|||
data.victim = NULL;
|
||||
d_walk(parent, &data, select_collect2);
|
||||
if (data.victim) {
|
||||
struct dentry *parent;
|
||||
spin_lock(&data.victim->d_lock);
|
||||
if (!shrink_lock_dentry(data.victim)) {
|
||||
if (!lock_for_kill(data.victim)) {
|
||||
spin_unlock(&data.victim->d_lock);
|
||||
rcu_read_unlock();
|
||||
} else {
|
||||
rcu_read_unlock();
|
||||
parent = data.victim->d_parent;
|
||||
if (parent != data.victim)
|
||||
__dput_to_list(parent, &data.dispose);
|
||||
__dentry_kill(data.victim);
|
||||
shrink_kill(data.victim);
|
||||
}
|
||||
}
|
||||
if (!list_empty(&data.dispose))
|
||||
|
@ -1652,7 +1521,7 @@ EXPORT_SYMBOL(shrink_dcache_parent);
|
|||
static enum d_walk_ret umount_check(void *_data, struct dentry *dentry)
|
||||
{
|
||||
/* it has busy descendents; complain about those instead */
|
||||
if (!list_empty(&dentry->d_subdirs))
|
||||
if (!hlist_empty(&dentry->d_children))
|
||||
return D_WALK_CONTINUE;
|
||||
|
||||
/* root with refcount 1 is fine */
|
||||
|
@ -1809,9 +1678,9 @@ static struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
|
|||
dentry->d_fsdata = NULL;
|
||||
INIT_HLIST_BL_NODE(&dentry->d_hash);
|
||||
INIT_LIST_HEAD(&dentry->d_lru);
|
||||
INIT_LIST_HEAD(&dentry->d_subdirs);
|
||||
INIT_HLIST_HEAD(&dentry->d_children);
|
||||
INIT_HLIST_NODE(&dentry->d_u.d_alias);
|
||||
INIT_LIST_HEAD(&dentry->d_child);
|
||||
INIT_HLIST_NODE(&dentry->d_sib);
|
||||
d_set_d_op(dentry, dentry->d_sb->s_d_op);
|
||||
|
||||
if (dentry->d_op && dentry->d_op->d_init) {
|
||||
|
@ -1850,7 +1719,7 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
|
|||
*/
|
||||
__dget_dlock(parent);
|
||||
dentry->d_parent = parent;
|
||||
list_add(&dentry->d_child, &parent->d_subdirs);
|
||||
hlist_add_head(&dentry->d_sib, &parent->d_children);
|
||||
spin_unlock(&parent->d_lock);
|
||||
|
||||
return dentry;
|
||||
|
@ -2958,11 +2827,15 @@ static void __d_move(struct dentry *dentry, struct dentry *target,
|
|||
} else {
|
||||
target->d_parent = old_parent;
|
||||
swap_names(dentry, target);
|
||||
list_move(&target->d_child, &target->d_parent->d_subdirs);
|
||||
if (!hlist_unhashed(&target->d_sib))
|
||||
__hlist_del(&target->d_sib);
|
||||
hlist_add_head(&target->d_sib, &target->d_parent->d_children);
|
||||
__d_rehash(target);
|
||||
fsnotify_update_flags(target);
|
||||
}
|
||||
list_move(&dentry->d_child, &dentry->d_parent->d_subdirs);
|
||||
if (!hlist_unhashed(&dentry->d_sib))
|
||||
__hlist_del(&dentry->d_sib);
|
||||
hlist_add_head(&dentry->d_sib, &dentry->d_parent->d_children);
|
||||
__d_rehash(dentry);
|
||||
fsnotify_update_flags(dentry);
|
||||
fscrypt_handle_d_move(dentry);
|
||||
|
|
45
fs/libfs.c
45
fs/libfs.c
|
@ -104,15 +104,16 @@ EXPORT_SYMBOL(dcache_dir_close);
|
|||
* If no such element exists, NULL is returned.
|
||||
*/
|
||||
static struct dentry *scan_positives(struct dentry *cursor,
|
||||
struct list_head *p,
|
||||
struct hlist_node **p,
|
||||
loff_t count,
|
||||
struct dentry *last)
|
||||
{
|
||||
struct dentry *dentry = cursor->d_parent, *found = NULL;
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
while ((p = p->next) != &dentry->d_subdirs) {
|
||||
struct dentry *d = list_entry(p, struct dentry, d_child);
|
||||
while (*p) {
|
||||
struct dentry *d = hlist_entry(*p, struct dentry, d_sib);
|
||||
p = &d->d_sib.next;
|
||||
// we must at least skip cursors, to avoid livelocks
|
||||
if (d->d_flags & DCACHE_DENTRY_CURSOR)
|
||||
continue;
|
||||
|
@ -126,8 +127,10 @@ static struct dentry *scan_positives(struct dentry *cursor,
|
|||
count = 1;
|
||||
}
|
||||
if (need_resched()) {
|
||||
list_move(&cursor->d_child, p);
|
||||
p = &cursor->d_child;
|
||||
if (!hlist_unhashed(&cursor->d_sib))
|
||||
__hlist_del(&cursor->d_sib);
|
||||
hlist_add_behind(&cursor->d_sib, &d->d_sib);
|
||||
p = &cursor->d_sib.next;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
cond_resched();
|
||||
spin_lock(&dentry->d_lock);
|
||||
|
@ -159,13 +162,12 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int whence)
|
|||
inode_lock_shared(dentry->d_inode);
|
||||
|
||||
if (offset > 2)
|
||||
to = scan_positives(cursor, &dentry->d_subdirs,
|
||||
to = scan_positives(cursor, &dentry->d_children.first,
|
||||
offset - 2, NULL);
|
||||
spin_lock(&dentry->d_lock);
|
||||
hlist_del_init(&cursor->d_sib);
|
||||
if (to)
|
||||
list_move(&cursor->d_child, &to->d_child);
|
||||
else
|
||||
list_del_init(&cursor->d_child);
|
||||
hlist_add_behind(&cursor->d_sib, &to->d_sib);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
dput(to);
|
||||
|
||||
|
@ -187,19 +189,16 @@ int dcache_readdir(struct file *file, struct dir_context *ctx)
|
|||
{
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct dentry *cursor = file->private_data;
|
||||
struct list_head *anchor = &dentry->d_subdirs;
|
||||
struct dentry *next = NULL;
|
||||
struct list_head *p;
|
||||
struct hlist_node **p;
|
||||
|
||||
if (!dir_emit_dots(file, ctx))
|
||||
return 0;
|
||||
|
||||
if (ctx->pos == 2)
|
||||
p = anchor;
|
||||
else if (!list_empty(&cursor->d_child))
|
||||
p = &cursor->d_child;
|
||||
p = &dentry->d_children.first;
|
||||
else
|
||||
return 0;
|
||||
p = &cursor->d_sib.next;
|
||||
|
||||
while ((next = scan_positives(cursor, p, 1, next)) != NULL) {
|
||||
if (!dir_emit(ctx, next->d_name.name, next->d_name.len,
|
||||
|
@ -207,13 +206,12 @@ int dcache_readdir(struct file *file, struct dir_context *ctx)
|
|||
fs_umode_to_dtype(d_inode(next)->i_mode)))
|
||||
break;
|
||||
ctx->pos++;
|
||||
p = &next->d_child;
|
||||
p = &next->d_sib.next;
|
||||
}
|
||||
spin_lock(&dentry->d_lock);
|
||||
hlist_del_init(&cursor->d_sib);
|
||||
if (next)
|
||||
list_move_tail(&cursor->d_child, &next->d_child);
|
||||
else
|
||||
list_del_init(&cursor->d_child);
|
||||
hlist_add_before(&cursor->d_sib, &next->d_sib);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
dput(next);
|
||||
|
||||
|
@ -492,12 +490,11 @@ const struct file_operations simple_offset_dir_operations = {
|
|||
|
||||
static struct dentry *find_next_child(struct dentry *parent, struct dentry *prev)
|
||||
{
|
||||
struct dentry *child = NULL;
|
||||
struct list_head *p = prev ? &prev->d_child : &parent->d_subdirs;
|
||||
struct dentry *child = NULL, *d;
|
||||
|
||||
spin_lock(&parent->d_lock);
|
||||
while ((p = p->next) != &parent->d_subdirs) {
|
||||
struct dentry *d = container_of(p, struct dentry, d_child);
|
||||
d = prev ? d_next_sibling(prev) : d_first_child(parent);
|
||||
hlist_for_each_entry_from(d, d_sib) {
|
||||
if (simple_positive(d)) {
|
||||
spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
if (simple_positive(d))
|
||||
|
@ -658,7 +655,7 @@ int simple_empty(struct dentry *dentry)
|
|||
int ret = 0;
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
|
||||
hlist_for_each_entry(child, &dentry->d_children, d_sib) {
|
||||
spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
if (simple_positive(child)) {
|
||||
spin_unlock(&child->d_lock);
|
||||
|
|
|
@ -1236,63 +1236,34 @@ static inline void _nfsd_symlink(struct dentry *parent, const char *name,
|
|||
|
||||
#endif
|
||||
|
||||
static void clear_ncl(struct inode *inode)
|
||||
static void clear_ncl(struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct nfsdfs_client *ncl = inode->i_private;
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
inode->i_private = NULL;
|
||||
spin_unlock(&inode->i_lock);
|
||||
kref_put(&ncl->cl_ref, ncl->cl_release);
|
||||
}
|
||||
|
||||
static struct nfsdfs_client *__get_nfsdfs_client(struct inode *inode)
|
||||
{
|
||||
struct nfsdfs_client *nc = inode->i_private;
|
||||
|
||||
if (nc)
|
||||
kref_get(&nc->cl_ref);
|
||||
return nc;
|
||||
}
|
||||
|
||||
struct nfsdfs_client *get_nfsdfs_client(struct inode *inode)
|
||||
{
|
||||
struct nfsdfs_client *nc;
|
||||
|
||||
inode_lock_shared(inode);
|
||||
nc = __get_nfsdfs_client(inode);
|
||||
inode_unlock_shared(inode);
|
||||
spin_lock(&inode->i_lock);
|
||||
nc = inode->i_private;
|
||||
if (nc)
|
||||
kref_get(&nc->cl_ref);
|
||||
spin_unlock(&inode->i_lock);
|
||||
return nc;
|
||||
}
|
||||
/* from __rpc_unlink */
|
||||
static void nfsdfs_remove_file(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
int ret;
|
||||
|
||||
clear_ncl(d_inode(dentry));
|
||||
dget(dentry);
|
||||
ret = simple_unlink(dir, dentry);
|
||||
d_drop(dentry);
|
||||
fsnotify_unlink(dir, dentry);
|
||||
dput(dentry);
|
||||
WARN_ON_ONCE(ret);
|
||||
}
|
||||
|
||||
static void nfsdfs_remove_files(struct dentry *root)
|
||||
{
|
||||
struct dentry *dentry, *tmp;
|
||||
|
||||
list_for_each_entry_safe(dentry, tmp, &root->d_subdirs, d_child) {
|
||||
if (!simple_positive(dentry)) {
|
||||
WARN_ON_ONCE(1); /* I think this can't happen? */
|
||||
continue;
|
||||
}
|
||||
nfsdfs_remove_file(d_inode(root), dentry);
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX: cut'n'paste from simple_fill_super; figure out if we could share
|
||||
* code instead. */
|
||||
static int nfsdfs_create_files(struct dentry *root,
|
||||
const struct tree_descr *files,
|
||||
struct nfsdfs_client *ncl,
|
||||
struct dentry **fdentries)
|
||||
{
|
||||
struct inode *dir = d_inode(root);
|
||||
|
@ -1311,8 +1282,9 @@ static int nfsdfs_create_files(struct dentry *root,
|
|||
dput(dentry);
|
||||
goto out;
|
||||
}
|
||||
kref_get(&ncl->cl_ref);
|
||||
inode->i_fop = files->ops;
|
||||
inode->i_private = __get_nfsdfs_client(dir);
|
||||
inode->i_private = ncl;
|
||||
d_add(dentry, inode);
|
||||
fsnotify_create(dir, dentry);
|
||||
if (fdentries)
|
||||
|
@ -1321,7 +1293,6 @@ static int nfsdfs_create_files(struct dentry *root,
|
|||
inode_unlock(dir);
|
||||
return 0;
|
||||
out:
|
||||
nfsdfs_remove_files(root);
|
||||
inode_unlock(dir);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
@ -1341,7 +1312,7 @@ struct dentry *nfsd_client_mkdir(struct nfsd_net *nn,
|
|||
dentry = nfsd_mkdir(nn->nfsd_client_dir, ncl, name);
|
||||
if (IS_ERR(dentry)) /* XXX: tossing errors? */
|
||||
return NULL;
|
||||
ret = nfsdfs_create_files(dentry, files, fdentries);
|
||||
ret = nfsdfs_create_files(dentry, files, ncl, fdentries);
|
||||
if (ret) {
|
||||
nfsd_client_rmdir(dentry);
|
||||
return NULL;
|
||||
|
@ -1352,20 +1323,7 @@ struct dentry *nfsd_client_mkdir(struct nfsd_net *nn,
|
|||
/* Taken from __rpc_rmdir: */
|
||||
void nfsd_client_rmdir(struct dentry *dentry)
|
||||
{
|
||||
struct inode *dir = d_inode(dentry->d_parent);
|
||||
struct inode *inode = d_inode(dentry);
|
||||
int ret;
|
||||
|
||||
inode_lock(dir);
|
||||
nfsdfs_remove_files(dentry);
|
||||
clear_ncl(inode);
|
||||
dget(dentry);
|
||||
ret = simple_rmdir(dir, dentry);
|
||||
WARN_ON_ONCE(ret);
|
||||
d_drop(dentry);
|
||||
fsnotify_rmdir(dir, dentry);
|
||||
dput(dentry);
|
||||
inode_unlock(dir);
|
||||
simple_recursive_removal(dentry, clear_ncl);
|
||||
}
|
||||
|
||||
static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
|
|
|
@ -124,7 +124,7 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode)
|
|||
* d_flags to indicate parental interest (their parent is the
|
||||
* original inode) */
|
||||
spin_lock(&alias->d_lock);
|
||||
list_for_each_entry(child, &alias->d_subdirs, d_child) {
|
||||
hlist_for_each_entry(child, &alias->d_children, d_sib) {
|
||||
if (!child->d_inode)
|
||||
continue;
|
||||
|
||||
|
|
|
@ -199,26 +199,21 @@ static void change_gid(struct dentry *dentry, kgid_t gid)
|
|||
*/
|
||||
static void set_gid(struct dentry *parent, kgid_t gid)
|
||||
{
|
||||
struct dentry *this_parent;
|
||||
struct list_head *next;
|
||||
struct dentry *this_parent, *dentry;
|
||||
|
||||
this_parent = parent;
|
||||
spin_lock(&this_parent->d_lock);
|
||||
|
||||
change_gid(this_parent, gid);
|
||||
repeat:
|
||||
next = this_parent->d_subdirs.next;
|
||||
dentry = d_first_child(this_parent);
|
||||
resume:
|
||||
while (next != &this_parent->d_subdirs) {
|
||||
struct list_head *tmp = next;
|
||||
struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
|
||||
next = tmp->next;
|
||||
|
||||
hlist_for_each_entry_from(dentry, d_sib) {
|
||||
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
|
||||
|
||||
change_gid(dentry, gid);
|
||||
|
||||
if (!list_empty(&dentry->d_subdirs)) {
|
||||
if (!hlist_empty(&dentry->d_children)) {
|
||||
spin_unlock(&this_parent->d_lock);
|
||||
spin_release(&dentry->d_lock.dep_map, _RET_IP_);
|
||||
this_parent = dentry;
|
||||
|
@ -233,21 +228,20 @@ resume:
|
|||
rcu_read_lock();
|
||||
ascend:
|
||||
if (this_parent != parent) {
|
||||
struct dentry *child = this_parent;
|
||||
this_parent = child->d_parent;
|
||||
dentry = this_parent;
|
||||
this_parent = dentry->d_parent;
|
||||
|
||||
spin_unlock(&child->d_lock);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
spin_lock(&this_parent->d_lock);
|
||||
|
||||
/* go into the first sibling still alive */
|
||||
do {
|
||||
next = child->d_child.next;
|
||||
if (next == &this_parent->d_subdirs)
|
||||
goto ascend;
|
||||
child = list_entry(next, struct dentry, d_child);
|
||||
} while (unlikely(child->d_flags & DCACHE_DENTRY_KILLED));
|
||||
rcu_read_unlock();
|
||||
goto resume;
|
||||
hlist_for_each_entry_continue(dentry, d_sib) {
|
||||
if (likely(!(dentry->d_flags & DCACHE_DENTRY_KILLED))) {
|
||||
rcu_read_unlock();
|
||||
goto resume;
|
||||
}
|
||||
}
|
||||
goto ascend;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
spin_unlock(&this_parent->d_lock);
|
||||
|
|
|
@ -68,12 +68,12 @@ extern const struct qstr dotdot_name;
|
|||
* large memory footprint increase).
|
||||
*/
|
||||
#ifdef CONFIG_64BIT
|
||||
# define DNAME_INLINE_LEN 32 /* 192 bytes */
|
||||
# define DNAME_INLINE_LEN 40 /* 192 bytes */
|
||||
#else
|
||||
# ifdef CONFIG_SMP
|
||||
# define DNAME_INLINE_LEN 36 /* 128 bytes */
|
||||
# else
|
||||
# define DNAME_INLINE_LEN 40 /* 128 bytes */
|
||||
# else
|
||||
# define DNAME_INLINE_LEN 44 /* 128 bytes */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
@ -101,8 +101,8 @@ struct dentry {
|
|||
struct list_head d_lru; /* LRU list */
|
||||
wait_queue_head_t *d_wait; /* in-lookup ones only */
|
||||
};
|
||||
struct list_head d_child; /* child of parent list */
|
||||
struct list_head d_subdirs; /* our children */
|
||||
struct hlist_node d_sib; /* child of parent list */
|
||||
struct hlist_head d_children; /* our children */
|
||||
/*
|
||||
* d_alias and d_rcu can share memory
|
||||
*/
|
||||
|
@ -564,4 +564,14 @@ struct name_snapshot {
|
|||
void take_dentry_name_snapshot(struct name_snapshot *, struct dentry *);
|
||||
void release_dentry_name_snapshot(struct name_snapshot *);
|
||||
|
||||
static inline struct dentry *d_first_child(const struct dentry *dentry)
|
||||
{
|
||||
return hlist_entry_safe(dentry->d_children.first, struct dentry, d_sib);
|
||||
}
|
||||
|
||||
static inline struct dentry *d_next_sibling(const struct dentry *dentry)
|
||||
{
|
||||
return hlist_entry_safe(dentry->d_sib.next, struct dentry, d_sib);
|
||||
}
|
||||
|
||||
#endif /* __LINUX_DCACHE_H */
|
||||
|
|
Loading…
Reference in New Issue