summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/mbcache2.c50
1 files changed, 45 insertions, 5 deletions
diff --git a/fs/mbcache2.c b/fs/mbcache2.c
index 5c3e1a8c38f6..3e3198d6b9d6 100644
--- a/fs/mbcache2.c
+++ b/fs/mbcache2.c
@@ -4,6 +4,7 @@
#include <linux/list_bl.h>
#include <linux/module.h>
#include <linux/sched.h>
+#include <linux/workqueue.h>
#include <linux/mbcache2.h>
/*
@@ -27,16 +28,29 @@ struct mb2_cache {
struct hlist_bl_head *c_hash;
/* log2 of hash table size */
int c_bucket_bits;
+ /* Maximum entries in cache to avoid degrading hash too much */
+ int c_max_entries;
/* Protects c_lru_list, c_entry_count */
spinlock_t c_lru_list_lock;
struct list_head c_lru_list;
/* Number of entries in cache */
unsigned long c_entry_count;
struct shrinker c_shrink;
+ /* Work for shrinking when the cache has too many entries */
+ struct work_struct c_shrink_work;
};
static struct kmem_cache *mb2_entry_cache;
+static unsigned long mb2_cache_shrink(struct mb2_cache *cache,
+ unsigned int nr_to_scan);
+
+/*
+ * Number of entries to reclaim synchronously when there are too many entries
+ * in cache
+ */
+#define SYNC_SHRINK_BATCH 64
+
/*
* mb2_cache_entry_create - create entry in cache
* @cache - cache where the entry should be created
@@ -55,6 +69,13 @@ int mb2_cache_entry_create(struct mb2_cache *cache, gfp_t mask, u32 key,
struct hlist_bl_node *dup_node;
struct hlist_bl_head *head;
+ /* Schedule background reclaim if there are too many entries */
+ if (cache->c_entry_count >= cache->c_max_entries)
+ schedule_work(&cache->c_shrink_work);
+ /* Do some sync reclaim if background reclaim cannot keep up */
+ if (cache->c_entry_count >= 2*cache->c_max_entries)
+ mb2_cache_shrink(cache, SYNC_SHRINK_BATCH);
+
entry = kmem_cache_alloc(mb2_entry_cache, mask);
if (!entry)
return -ENOMEM;
@@ -223,12 +244,9 @@ static unsigned long mb2_cache_count(struct shrinker *shrink,
}
/* Shrink number of entries in cache */
-static unsigned long mb2_cache_scan(struct shrinker *shrink,
- struct shrink_control *sc)
+static unsigned long mb2_cache_shrink(struct mb2_cache *cache,
+ unsigned int nr_to_scan)
{
- int nr_to_scan = sc->nr_to_scan;
- struct mb2_cache *cache = container_of(shrink, struct mb2_cache,
- c_shrink);
struct mb2_cache_entry *entry;
struct hlist_bl_head *head;
unsigned int shrunk = 0;
@@ -261,6 +279,25 @@ static unsigned long mb2_cache_scan(struct shrinker *shrink,
return shrunk;
}
+static unsigned long mb2_cache_scan(struct shrinker *shrink,
+ struct shrink_control *sc)
+{
+ int nr_to_scan = sc->nr_to_scan;
+ struct mb2_cache *cache = container_of(shrink, struct mb2_cache,
+ c_shrink);
+ return mb2_cache_shrink(cache, nr_to_scan);
+}
+
+/* We shrink 1/X of the cache when we have too many entries in it */
+#define SHRINK_DIVISOR 16
+
+static void mb2_cache_shrink_worker(struct work_struct *work)
+{
+ struct mb2_cache *cache = container_of(work, struct mb2_cache,
+ c_shrink_work);
+ mb2_cache_shrink(cache, cache->c_max_entries / SHRINK_DIVISOR);
+}
+
/*
* mb2_cache_create - create cache
* @bucket_bits: log2 of the hash table size
@@ -280,6 +317,7 @@ struct mb2_cache *mb2_cache_create(int bucket_bits)
if (!cache)
goto err_out;
cache->c_bucket_bits = bucket_bits;
+ cache->c_max_entries = bucket_count << 4;
INIT_LIST_HEAD(&cache->c_lru_list);
spin_lock_init(&cache->c_lru_list_lock);
cache->c_hash = kmalloc(bucket_count * sizeof(struct hlist_bl_head),
@@ -296,6 +334,8 @@ struct mb2_cache *mb2_cache_create(int bucket_bits)
cache->c_shrink.seeks = DEFAULT_SEEKS;
register_shrinker(&cache->c_shrink);
+ INIT_WORK(&cache->c_shrink_work, mb2_cache_shrink_worker);
+
return cache;
err_out: