diff options
author | Kent Overstreet <kent.overstreet@linux.dev> | 2023-03-15 11:02:00 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2023-10-22 17:09:57 -0400 |
commit | 26559553e47c9f2a14b5254cb307fc755ac316c8 (patch) | |
tree | 1335ce4efafbd822e300e8db3dca26908c7cc6d0 | |
parent | 2f0815840c80075bc35f210a7acfa8b48717be5a (diff) | |
download | lwn-26559553e47c9f2a14b5254cb307fc755ac316c8.tar.gz lwn-26559553e47c9f2a14b5254cb307fc755ac316c8.zip |
bcachefs: Add a fallback when journal_keys doesn't fit in ram
We may end up in a situation where allocating the buffer for the sorted
journal_keys fails - but it would likely succeed, post compaction where
we drop duplicates.
We've had reports of this allocation failing, so this adds a slowpath to
do the compaction incrementally.
This is only a band-aid fix; we need to look at limiting the number of
keys in the journal based on the amount of system RAM.
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
-rw-r--r-- | fs/bcachefs/recovery.c | 67 |
1 files changed, 49 insertions, 18 deletions
diff --git a/fs/bcachefs/recovery.c b/fs/bcachefs/recovery.c index 73f7663cbd3f..6aa99f57a001 100644 --- a/fs/bcachefs/recovery.c +++ b/fs/bcachefs/recovery.c @@ -476,6 +476,26 @@ void bch2_journal_keys_free(struct journal_keys *keys) keys->nr = keys->gap = keys->size = 0; } +static void __journal_keys_sort(struct journal_keys *keys) +{ + struct journal_key *src, *dst; + + sort(keys->d, keys->nr, sizeof(keys->d[0]), journal_sort_key_cmp, NULL); + + src = dst = keys->d; + while (src < keys->d + keys->nr) { + while (src + 1 < keys->d + keys->nr && + src[0].btree_id == src[1].btree_id && + src[0].level == src[1].level && + bpos_eq(src[0].k->k.p, src[1].k->k.p)) + src++; + + *dst++ = *src++; + } + + keys->nr = dst - keys->d; +} + static int journal_keys_sort(struct bch_fs *c) { struct genradix_iter iter; @@ -483,8 +503,7 @@ static int journal_keys_sort(struct bch_fs *c) struct jset_entry *entry; struct bkey_i *k; struct journal_keys *keys = &c->journal_keys; - struct journal_key *src, *dst; - size_t nr_keys = 0; + size_t nr_keys = 0, nr_read = 0; genradix_for_each(&c->journal_entries, iter, _i) { i = *_i; @@ -503,9 +522,19 @@ static int journal_keys_sort(struct bch_fs *c) keys->d = kvmalloc_array(keys->size, sizeof(keys->d[0]), GFP_KERNEL); if (!keys->d) { - bch_err(c, "Failed to allocate buffer for sorted journal keys (%zu keys)", + bch_err(c, "Failed to allocate buffer for sorted journal keys (%zu keys); trying slowpath", nr_keys); - return -BCH_ERR_ENOMEM_journal_keys_sort; + + do { + keys->size >>= 1; + keys->d = kvmalloc_array(keys->size, sizeof(keys->d[0]), GFP_KERNEL); + } while (!keys->d && keys->size > nr_keys / 8); + + if (!keys->d) { + bch_err(c, "Failed to allocate %zu size buffer for sorted journal keys; exiting", + keys->size); + return -BCH_ERR_ENOMEM_journal_keys_sort; + } } genradix_for_each(&c->journal_entries, iter, _i) { @@ -514,7 +543,17 @@ static int journal_keys_sort(struct bch_fs *c) if (!i || i->ignore) continue; - for_each_jset_key(k, entry, &i->j) + for_each_jset_key(k, entry, &i->j) { + if (keys->nr == keys->size) { + __journal_keys_sort(keys); + + if (keys->nr > keys->size * 7 / 8) { + bch_err(c, "Too many journal keys for slowpath; have %zu compacted, buf size %zu, processed %zu/%zu", + keys->nr, keys->size, nr_read, nr_keys); + return -BCH_ERR_ENOMEM_journal_keys_sort; + } + } + keys->d[keys->nr++] = (struct journal_key) { .btree_id = entry->btree_id, .level = entry->level, @@ -522,23 +561,15 @@ static int journal_keys_sort(struct bch_fs *c) .journal_seq = le64_to_cpu(i->j.seq), .journal_offset = k->_data - i->j._data, }; - } - - sort(keys->d, keys->nr, sizeof(keys->d[0]), journal_sort_key_cmp, NULL); - src = dst = keys->d; - while (src < keys->d + keys->nr) { - while (src + 1 < keys->d + keys->nr && - src[0].btree_id == src[1].btree_id && - src[0].level == src[1].level && - bpos_eq(src[0].k->k.p, src[1].k->k.p)) - src++; - - *dst++ = *src++; + nr_read++; + } } - keys->nr = dst - keys->d; + __journal_keys_sort(keys); keys->gap = keys->nr; + + bch_verbose(c, "Journal keys: %zu read, %zu after sorting and compacting", nr_keys, keys->nr); return 0; } |