summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2020-11-12 17:19:47 -0500
committerKent Overstreet <kent.overstreet@gmail.com>2020-11-12 17:23:04 -0500
commita437b292d43139eabbd30e2dbcb4c37c1c1225af (patch)
tree6415064c5287ea2c2409fdb29f8580e9b2ba2752
parenta023b081aa07179f96e88197ace65de6a6cf1a8b (diff)
bcachefs: Add a shrinker for the btree key cachesrcu
Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
-rw-r--r--fs/bcachefs/btree_key_cache.c75
-rw-r--r--fs/bcachefs/btree_types.h1
2 files changed, 75 insertions, 1 deletions
diff --git a/fs/bcachefs/btree_key_cache.c b/fs/bcachefs/btree_key_cache.c
index 22163163ed4f..59d8c0fc2373 100644
--- a/fs/bcachefs/btree_key_cache.c
+++ b/fs/bcachefs/btree_key_cache.c
@@ -9,6 +9,7 @@
#include "journal.h"
#include "journal_reclaim.h"
+#include <linux/sched/mm.h>
#include <trace/events/bcachefs.h>
static int bch2_btree_key_cache_cmp_fn(struct rhashtable_compare_arg *arg,
@@ -485,11 +486,79 @@ void bch2_btree_key_cache_verify_clean(struct btree_trans *trans,
}
#endif
+static unsigned long bch2_btree_key_cache_scan(struct shrinker *shrink,
+ struct shrink_control *sc)
+{
+ struct bch_fs *c = container_of(shrink, struct bch_fs,
+ btree_key_cache.shrink);
+ struct btree_key_cache *bc = &c->btree_key_cache;
+ struct bkey_cached *ck, *t;
+ size_t scanned = 0, freed = 0, nr = sc->nr_to_scan;
+ unsigned flags;
+ u32 seq;
+
+ seq = bch2_btree_trans_barrier_seq(c);
+
+ /* Return -1 if we can't do anything right now */
+ if (sc->gfp_mask & __GFP_FS)
+ mutex_lock(&bc->lock);
+ else if (!mutex_trylock(&bc->lock))
+ return -1;
+
+ flags = memalloc_nofs_save();
+
+ list_for_each_entry_safe(ck, t, &bc->freed, list) {
+ scanned++;
+
+ if (seq != ck->btree_trans_barrier_seq) {
+ list_del(&ck->list);
+ kfree(ck);
+ freed++;
+ }
+
+ if (scanned >= nr)
+ goto out;
+ }
+
+ list_for_each_entry_safe(ck, t, &bc->clean, list) {
+ scanned++;
+
+ if (bkey_cached_lock_for_evict(ck)) {
+ bkey_cached_evict(bc, ck);
+ bkey_cached_free(bc, ck);
+ }
+
+ if (scanned >= nr) {
+ if (&t->list != &bc->clean)
+ list_move_tail(&bc->clean, &t->list);
+ goto out;
+ }
+ }
+out:
+ memalloc_nofs_restore(flags);
+ mutex_unlock(&bc->lock);
+
+ return freed;
+}
+
+static unsigned long bch2_btree_key_cache_count(struct shrinker *shrink,
+ struct shrink_control *sc)
+{
+ struct bch_fs *c = container_of(shrink, struct bch_fs,
+ btree_key_cache.shrink);
+ struct btree_key_cache *bc = &c->btree_key_cache;
+
+ return bc->nr_keys;
+}
+
void bch2_fs_btree_key_cache_exit(struct btree_key_cache *bc)
{
struct bch_fs *c = container_of(bc, struct bch_fs, btree_key_cache);
struct bkey_cached *ck, *n;
+ if (bc->shrink.list.next)
+ unregister_shrinker(&bc->shrink);
+
mutex_lock(&bc->lock);
list_splice(&bc->dirty, &bc->clean);
@@ -522,7 +591,11 @@ void bch2_fs_btree_key_cache_init_early(struct btree_key_cache *c)
int bch2_fs_btree_key_cache_init(struct btree_key_cache *c)
{
- return rhashtable_init(&c->table, &bch2_btree_key_cache_params);
+ c->shrink.count_objects = bch2_btree_key_cache_count;
+ c->shrink.scan_objects = bch2_btree_key_cache_scan;
+
+ return register_shrinker(&c->shrink) ?:
+ rhashtable_init(&c->table, &bch2_btree_key_cache_params);
}
void bch2_btree_key_cache_to_text(struct printbuf *out, struct btree_key_cache *c)
diff --git a/fs/bcachefs/btree_types.h b/fs/bcachefs/btree_types.h
index 2211951f6fec..a1d0d0c56124 100644
--- a/fs/bcachefs/btree_types.h
+++ b/fs/bcachefs/btree_types.h
@@ -297,6 +297,7 @@ struct btree_key_cache {
struct list_head freed;
struct list_head clean;
struct list_head dirty;
+ struct shrinker shrink;
size_t nr_keys;
size_t nr_dirty;