Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 77812a1e authored by Nick Piggin's avatar Nick Piggin
Browse files

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: default avatarNick Piggin <npiggin@kernel.dk>
parent ec33679d
Loading
Loading
Loading
Loading
+61 −74
Original line number Diff line number Diff line
@@ -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 @@ void dput(struct dentry *dentry)
	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 @@ void d_prune_aliases(struct inode *inode)
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();