Commit 47ad9591 authored by Nick Piggin's avatar Nick Piggin Committed by Linus Torvalds

[PATCH] make shrinker_sem an rwsem

Use an rwsem to protect the shrinker list instead of a regular
semaphore.  Modifications to the list are now done under the write lock,
shrink_slab takes the read lock, and access to shrinker->nr becomes racy
(which is no different to how the page lru scanner is implemented).  The
shrinker functions become concurrent.

Previously, having the slab scanner get preempted or scheduling while
holding the semaphore would cause other tasks to skip putting pressure on
the slab.

Also, make shrink_icache_memory return -1 if it can't do anything in order
to hold pressure on this cache and prevent useless looping in shrink_slab.
Signed-off-by: default avatarNick Piggin <nickpiggin@yahoo.com.au>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 7c0fe360
...@@ -485,8 +485,9 @@ static int shrink_icache_memory(int nr, unsigned int gfp_mask) ...@@ -485,8 +485,9 @@ static int shrink_icache_memory(int nr, unsigned int gfp_mask)
* and we don't want to recurse into the FS that called us * and we don't want to recurse into the FS that called us
* in clear_inode() and friends.. * in clear_inode() and friends..
*/ */
if (gfp_mask & __GFP_FS) if (!(gfp_mask & __GFP_FS))
prune_icache(nr); return -1;
prune_icache(nr);
} }
return (inodes_stat.nr_unused / 100) * sysctl_vfs_cache_pressure; return (inodes_stat.nr_unused / 100) * sysctl_vfs_cache_pressure;
} }
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <linux/topology.h> #include <linux/topology.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/rwsem.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
#include <asm/div64.h> #include <asm/div64.h>
...@@ -122,7 +123,7 @@ int vm_swappiness = 60; ...@@ -122,7 +123,7 @@ int vm_swappiness = 60;
static long total_memory; static long total_memory;
static LIST_HEAD(shrinker_list); static LIST_HEAD(shrinker_list);
static DECLARE_MUTEX(shrinker_sem); static DECLARE_RWSEM(shrinker_rwsem);
/* /*
* Add a shrinker callback to be called from the vm * Add a shrinker callback to be called from the vm
...@@ -136,9 +137,9 @@ struct shrinker *set_shrinker(int seeks, shrinker_t theshrinker) ...@@ -136,9 +137,9 @@ struct shrinker *set_shrinker(int seeks, shrinker_t theshrinker)
shrinker->shrinker = theshrinker; shrinker->shrinker = theshrinker;
shrinker->seeks = seeks; shrinker->seeks = seeks;
shrinker->nr = 0; shrinker->nr = 0;
down(&shrinker_sem); down_write(&shrinker_rwsem);
list_add(&shrinker->list, &shrinker_list); list_add(&shrinker->list, &shrinker_list);
up(&shrinker_sem); up_write(&shrinker_rwsem);
} }
return shrinker; return shrinker;
} }
...@@ -149,13 +150,13 @@ EXPORT_SYMBOL(set_shrinker); ...@@ -149,13 +150,13 @@ EXPORT_SYMBOL(set_shrinker);
*/ */
void remove_shrinker(struct shrinker *shrinker) void remove_shrinker(struct shrinker *shrinker)
{ {
down(&shrinker_sem); down_write(&shrinker_rwsem);
list_del(&shrinker->list); list_del(&shrinker->list);
up(&shrinker_sem); up_write(&shrinker_rwsem);
kfree(shrinker); kfree(shrinker);
} }
EXPORT_SYMBOL(remove_shrinker); EXPORT_SYMBOL(remove_shrinker);
#define SHRINK_BATCH 128 #define SHRINK_BATCH 128
/* /*
* Call the shrink functions to age shrinkable caches * Call the shrink functions to age shrinkable caches
...@@ -179,11 +180,15 @@ static int shrink_slab(unsigned long scanned, unsigned int gfp_mask, ...@@ -179,11 +180,15 @@ static int shrink_slab(unsigned long scanned, unsigned int gfp_mask,
{ {
struct shrinker *shrinker; struct shrinker *shrinker;
if (down_trylock(&shrinker_sem)) if (scanned == 0)
return 0;
if (!down_read_trylock(&shrinker_rwsem))
return 0; return 0;
list_for_each_entry(shrinker, &shrinker_list, list) { list_for_each_entry(shrinker, &shrinker_list, list) {
unsigned long long delta; unsigned long long delta;
unsigned long total_scan;
delta = (4 * scanned) / shrinker->seeks; delta = (4 * scanned) / shrinker->seeks;
delta *= (*shrinker->shrinker)(0, gfp_mask); delta *= (*shrinker->shrinker)(0, gfp_mask);
...@@ -192,23 +197,25 @@ static int shrink_slab(unsigned long scanned, unsigned int gfp_mask, ...@@ -192,23 +197,25 @@ static int shrink_slab(unsigned long scanned, unsigned int gfp_mask,
if (shrinker->nr < 0) if (shrinker->nr < 0)
shrinker->nr = LONG_MAX; /* It wrapped! */ shrinker->nr = LONG_MAX; /* It wrapped! */
if (shrinker->nr <= SHRINK_BATCH) total_scan = shrinker->nr;
continue; shrinker->nr = 0;
while (shrinker->nr) {
long this_scan = shrinker->nr; while (total_scan >= SHRINK_BATCH) {
long this_scan = SHRINK_BATCH;
int shrink_ret; int shrink_ret;
if (this_scan > 128)
this_scan = 128;
shrink_ret = (*shrinker->shrinker)(this_scan, gfp_mask); shrink_ret = (*shrinker->shrinker)(this_scan, gfp_mask);
mod_page_state(slabs_scanned, this_scan);
shrinker->nr -= this_scan;
if (shrink_ret == -1) if (shrink_ret == -1)
break; break;
mod_page_state(slabs_scanned, this_scan);
total_scan -= this_scan;
cond_resched(); cond_resched();
} }
shrinker->nr += total_scan;
} }
up(&shrinker_sem); up_read(&shrinker_rwsem);
return 0; return 0;
} }
......
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