Re: [PATCH] Fix shrink_dcache_parent() against shrink_dcache_memory() race (3rd updated patch)]

From: Neil Brown
Date: Mon Mar 13 2006 - 00:44:09 EST


On Monday March 13, jblunck@xxxxxxx wrote:
> On Mon, Mar 13, Neil Brown wrote:
>
> > No you don't.
>
> Err, you are right. But this brings a new idea to my mind: why don't we use
> s_umount to prevent umounting while we prune one dentry? Something like:
>
> if (down_read_trylock()) {
> if (s_root)
> prune_one_dentry()
> up_read()
> }
> // else just skip it
>
> So maybe our prunes counter isn't the only way to go. Comments?

Hmmm.... yes, I think that could work. Patch below against
2.6.16-rc6-mm1.

I cannot see down_read_trylock being slower than dcache_lock, so I
suspect this shouldn't impact performance.

Does this meet with everyone's approval?

NeilBrown




Signed-off-by: Neil Brown <neilb@xxxxxxx>

### Diffstat output
./fs/dcache.c | 40 ++++++++++++++++++++++++++++------------
1 file changed, 28 insertions(+), 12 deletions(-)

diff ./fs/dcache.c~current~ ./fs/dcache.c
--- ./fs/dcache.c~current~ 2006-03-13 16:19:29.000000000 +1100
+++ ./fs/dcache.c 2006-03-13 16:43:59.000000000 +1100
@@ -383,6 +383,8 @@ static inline void prune_one_dentry(stru
/**
* prune_dcache - shrink the dcache
* @count: number of entries to try and free
+ * @sb: if given, ignore dentries for other superblocks
+ * which are being unmounted.
*
* Shrink the dcache. This is done when we need
* more memory, or simply when we need to unmount
@@ -393,7 +395,7 @@ static inline void prune_one_dentry(stru
* all the dentries are in use.
*/

-static void prune_dcache(int count)
+static void prune_dcache(int count, struct super_block *sb)
{
spin_lock(&dcache_lock);
for (; count ; count--) {
@@ -420,15 +422,27 @@ static void prune_dcache(int count)
spin_unlock(&dentry->d_lock);
continue;
}
- /* If the dentry was recently referenced, don't free it. */
- if (dentry->d_flags & DCACHE_REFERENCED) {
- dentry->d_flags &= ~DCACHE_REFERENCED;
- list_add(&dentry->d_lru, &dentry_unused);
- dentry_stat.nr_unused++;
- spin_unlock(&dentry->d_lock);
- continue;
+ if (!(dentry->d_flags & DCACHE_REFERENCED) &&
+ (!sb || dentry->d_sb == sb)) {
+ if (sb) {
+ prune_one_dentry(dentry);
+ continue;
+ }
+ /* Need to avoid race with generic_shutdown_super */
+ if (down_read_trylock(&dentry->d_sb->s_umount) &&
+ dentry->d_sb->s_root != NULL) {
+ prune_one_dentry(dentry);
+ up_read(&dentry->d_sb->s_umount);
+ continue;
+ }
}
- prune_one_dentry(dentry);
+ /* The dentry was recently referenced, or the filesystem
+ * is being unmounted, so don't free it. */
+
+ dentry->d_flags &= ~DCACHE_REFERENCED;
+ list_add(&dentry->d_lru, &dentry_unused);
+ dentry_stat.nr_unused++;
+ spin_unlock(&dentry->d_lock);
}
spin_unlock(&dcache_lock);
}
@@ -639,9 +653,10 @@ void shrink_dcache_parent(struct dentry
int found;

while ((found = select_parent(parent)) != 0)
- prune_dcache(found);
+ prune_dcache(found, parent->d_sb);
}

+#if 0
/**
* shrink_dcache_anon - further prune the cache
* @head: head of d_hash list of dentries to prune
@@ -677,9 +692,10 @@ void shrink_dcache_anon(struct hlist_hea
}
}
spin_unlock(&dcache_lock);
- prune_dcache(found);
+ prune_dcache(found, NULL);
} while(found);
}
+#endif

/*
* Scan `nr' dentries and return the number which remain.
@@ -698,7 +714,7 @@ static int shrink_dcache_memory(int nr,
if (nr) {
if (!(gfp_mask & __GFP_FS))
return -1;
- prune_dcache(nr);
+ prune_dcache(nr, NULL);
}
return (dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure;
}
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/