diff options
| author | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 17:49:48 +1100 | 
|---|---|---|
| committer | Nick Piggin <npiggin@kernel.dk> | 2011-01-07 17:50:25 +1100 | 
| commit | 77812a1ef139d84270d27faacc0630c887411013 (patch) | |
| tree | a051134c0d1c74425a5f60adc0ca252c5db15b35 | |
| parent | ec33679d78f9d653a44ddba10b5fb824c06330a1 (diff) | |
fs: consolidate dentry kill sequence
The tricky locking for disposing of a dentry is duplicated 3 times in the
dcache (dput, pruning a dentry from the LRU, and pruning its ancestors).
Consolidate them all into a single function dentry_kill.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
| -rw-r--r-- | fs/dcache.c | 135 | 
1 files changed, 61 insertions, 74 deletions
| diff --git a/fs/dcache.c b/fs/dcache.c index d1840b30c673..dc0551c9755d 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -284,6 +284,40 @@ void d_drop(struct dentry *dentry)  }  EXPORT_SYMBOL(d_drop); +/* + * Finish off a dentry we've decided to kill. + * dentry->d_lock must be held, returns with it unlocked. + * If ref is non-zero, then decrement the refcount too. + * Returns dentry requiring refcount drop, or NULL if we're done. + */ +static inline struct dentry *dentry_kill(struct dentry *dentry, int ref) +	__releases(dentry->d_lock) +{ +	struct dentry *parent; + +	if (!spin_trylock(&dcache_inode_lock)) { +relock: +		spin_unlock(&dentry->d_lock); +		cpu_relax(); +		return dentry; /* try again with same dentry */ +	} +	if (IS_ROOT(dentry)) +		parent = NULL; +	else +		parent = dentry->d_parent; +	if (parent && !spin_trylock(&parent->d_lock)) { +		spin_unlock(&dcache_inode_lock); +		goto relock; +	} +	if (ref) +		dentry->d_count--; +	/* if dentry was on the d_lru list delete it from there */ +	dentry_lru_del(dentry); +	/* if it was on the hash then remove it */ +	__d_drop(dentry); +	return d_kill(dentry, parent); +} +  /*    * This is dput   * @@ -309,13 +343,9 @@ EXPORT_SYMBOL(d_drop);   * call the dentry unlink method as well as removing it from the queues and   * releasing its resources. If the parent dentries were scheduled for release   * they too may now get deleted. - * - * no dcache lock, please.   */ -  void dput(struct dentry *dentry)  { -	struct dentry *parent;  	if (!dentry)  		return; @@ -348,26 +378,7 @@ repeat:  	return;  kill_it: -	if (!spin_trylock(&dcache_inode_lock)) { -relock: -		spin_unlock(&dentry->d_lock); -		cpu_relax(); -		goto repeat; -	} -	if (IS_ROOT(dentry)) -		parent = NULL; -	else -		parent = dentry->d_parent; -	if (parent && !spin_trylock(&parent->d_lock)) { -		spin_unlock(&dcache_inode_lock); -		goto relock; -	} -	dentry->d_count--; -	/* if dentry was on the d_lru list delete it from there */ -	dentry_lru_del(dentry); -	/* if it was on the hash (d_delete case), then remove it */ -	__d_drop(dentry); -	dentry = d_kill(dentry, parent); +	dentry = dentry_kill(dentry, 1);  	if (dentry)  		goto repeat;  } @@ -563,51 +574,43 @@ restart:  EXPORT_SYMBOL(d_prune_aliases);  /* - * Throw away a dentry - free the inode, dput the parent.  This requires that - * the LRU list has already been removed. + * Try to throw away a dentry - free the inode, dput the parent. + * Requires dentry->d_lock is held, and dentry->d_count == 0. + * Releases dentry->d_lock.   * - * Try to prune ancestors as well.  This is necessary to prevent - * quadratic behavior of shrink_dcache_parent(), but is also expected - * to be beneficial in reducing dentry cache fragmentation. + * This may fail if locks cannot be acquired no problem, just try again.   */ -static void prune_one_dentry(struct dentry *dentry, struct dentry *parent) +static void try_prune_one_dentry(struct dentry *dentry)  	__releases(dentry->d_lock) -	__releases(parent->d_lock) -	__releases(dcache_inode_lock)  { -	__d_drop(dentry); -	dentry = d_kill(dentry, parent); +	struct dentry *parent; +	parent = dentry_kill(dentry, 0);  	/* -	 * Prune ancestors. +	 * If dentry_kill returns NULL, we have nothing more to do. +	 * if it returns the same dentry, trylocks failed. In either +	 * case, just loop again. +	 * +	 * Otherwise, we need to prune ancestors too. This is necessary +	 * to prevent quadratic behavior of shrink_dcache_parent(), but +	 * is also expected to be beneficial in reducing dentry cache +	 * fragmentation.  	 */ +	if (!parent) +		return; +	if (parent == dentry) +		return; + +	/* Prune ancestors. */ +	dentry = parent;  	while (dentry) { -relock:  		spin_lock(&dentry->d_lock);  		if (dentry->d_count > 1) {  			dentry->d_count--;  			spin_unlock(&dentry->d_lock);  			return;  		} -		if (!spin_trylock(&dcache_inode_lock)) { -relock2: -			spin_unlock(&dentry->d_lock); -			cpu_relax(); -			goto relock; -		} - -		if (IS_ROOT(dentry)) -			parent = NULL; -		else -			parent = dentry->d_parent; -		if (parent && !spin_trylock(&parent->d_lock)) { -			spin_unlock(&dcache_inode_lock); -			goto relock2; -		} -		dentry->d_count--; -		dentry_lru_del(dentry); -		__d_drop(dentry); -		dentry = d_kill(dentry, parent); +		dentry = dentry_kill(dentry, 1);  	}  } @@ -617,8 +620,6 @@ static void shrink_dentry_list(struct list_head *list)  	rcu_read_lock();  	for (;;) { -		struct dentry *parent; -  		dentry = list_entry_rcu(list->prev, struct dentry, d_lru);  		if (&dentry->d_lru == list)  			break; /* empty */ @@ -639,24 +640,10 @@ static void shrink_dentry_list(struct list_head *list)  			continue;  		} -		if (!spin_trylock(&dcache_inode_lock)) { -relock: -			spin_unlock(&dentry->d_lock); -			cpu_relax(); -			continue; -		} -		if (IS_ROOT(dentry)) -			parent = NULL; -		else -			parent = dentry->d_parent; -		if (parent && !spin_trylock(&parent->d_lock)) { -			spin_unlock(&dcache_inode_lock); -			goto relock; -		} -		dentry_lru_del(dentry); -  		rcu_read_unlock(); -		prune_one_dentry(dentry, parent); + +		try_prune_one_dentry(dentry); +  		rcu_read_lock();  	}  	rcu_read_unlock(); | 
