Commit edb41998 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] shrink_slab: improved handling of GFP_NOFS allocations

Currently, shrink_slab() will decide that it needs to scan a certain number of
dentries, will call shrink_dcache_memory() requesting that this be done, and
shrink_dcache_memory() will simply bale out without doing anything because the
caller did not have __GFP_FS.

This has the potential to disrupt our lovely pagecache-vs-slab balancing act. 
So change things so that shrinker callouts can return -1, indicating that they
baled out.  This way, shrink_slab can remember that this slab was owed a
certain number of scannings and these will be correctly performed next time a
__GFP_FS caller comes by.
parent b528cea7
...@@ -643,24 +643,23 @@ void shrink_dcache_anon(struct hlist_head *head) ...@@ -643,24 +643,23 @@ void shrink_dcache_anon(struct hlist_head *head)
} }
/* /*
* This is called from kswapd when we think we need some more memory. * Scan `nr' dentries and return the number which remain.
*
* We need to avoid reentering the filesystem if the caller is performing a
* GFP_NOFS allocation attempt. One example deadlock is:
*
* ext2_new_block->getblk->GFP->shrink_dcache_memory->prune_dcache->
* prune_one_dentry->dput->dentry_iput->iput->inode->i_sb->s_op->put_inode->
* ext2_discard_prealloc->ext2_free_blocks->lock_super->DEADLOCK.
*
* In this case we return -1 to tell the caller that we baled.
*/ */
static int shrink_dcache_memory(int nr, unsigned int gfp_mask) static int shrink_dcache_memory(int nr, unsigned int gfp_mask)
{ {
if (nr) { if (nr) {
/* if (!(gfp_mask & __GFP_FS))
* Nasty deadlock avoidance. return -1;
* prune_dcache(nr);
* ext2_new_block->getblk->GFP->shrink_dcache_memory->
* prune_dcache->prune_one_dentry->dput->dentry_iput->iput->
* inode->i_sb->s_op->put_inode->ext2_discard_prealloc->
* ext2_free_blocks->lock_super->DEADLOCK.
*
* We should make sure we don't hold the superblock lock over
* block allocations, but for now:
*/
if (gfp_mask & __GFP_FS)
prune_dcache(nr);
} }
return dentry_stat.nr_unused; return dentry_stat.nr_unused;
} }
......
...@@ -153,20 +153,23 @@ static int shrink_slab(unsigned long scanned, unsigned int gfp_mask) ...@@ -153,20 +153,23 @@ static int shrink_slab(unsigned long scanned, unsigned int gfp_mask)
delta *= (*shrinker->shrinker)(0, gfp_mask); delta *= (*shrinker->shrinker)(0, gfp_mask);
do_div(delta, pages + 1); do_div(delta, pages + 1);
shrinker->nr += delta; shrinker->nr += delta;
if (shrinker->nr > SHRINK_BATCH) { if (shrinker->nr < 0)
long nr_to_scan = shrinker->nr; shrinker->nr = LONG_MAX; /* It wrapped! */
shrinker->nr = 0; if (shrinker->nr <= SHRINK_BATCH)
mod_page_state(slabs_scanned, nr_to_scan); continue;
while (nr_to_scan) { while (shrinker->nr) {
long this_scan = nr_to_scan; long this_scan = shrinker->nr;
int shrink_ret;
if (this_scan > 128)
this_scan = 128; if (this_scan > 128)
(*shrinker->shrinker)(this_scan, gfp_mask); this_scan = 128;
nr_to_scan -= this_scan; shrink_ret = (*shrinker->shrinker)(this_scan, gfp_mask);
cond_resched(); mod_page_state(slabs_scanned, this_scan);
} shrinker->nr -= this_scan;
if (shrink_ret == -1)
break;
cond_resched();
} }
} }
up(&shrinker_sem); up(&shrinker_sem);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment