mirror of
https://github.com/torvalds/linux.git
synced 2025-11-02 17:49:03 +02:00
Previosuly, the transaction commit path would have to add keys to the btree write buffer as a separate operation, requiring additional global synchronization. This patch introduces a new journal entry type, which indicates that the keys need to be copied into the btree write buffer prior to being written out. We switch the journal entry type back to JSET_ENTRY_btree_keys prior to write, so this is not an on disk format change. Flushing the btree write buffer may require pulling keys out of journal entries yet to be written, and quiescing outstanding journal reservations; we previously added journal->buf_lock for synchronization with the journal write path. We also can't put strict bounds on the number of keys in the journal destined for the write buffer, which means we might overflow the size of the preallocated buffer and have to reallocate - this introduces a potentially fatal memory allocation failure. This is something we'll have to watch for, if it becomes an issue in practice we can do additional mitigation. The transaction commit path no longer has to explicitly check if the write buffer is full and wait on flushing; this is another performance optimization. Instead, when the btree write buffer is close to full we change the journal watermark, so that only reservations for journal reclaim are allowed. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
574 lines
16 KiB
C
574 lines
16 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#include "bcachefs.h"
|
|
#include "btree_locking.h"
|
|
#include "btree_update.h"
|
|
#include "btree_update_interior.h"
|
|
#include "btree_write_buffer.h"
|
|
#include "error.h"
|
|
#include "journal.h"
|
|
#include "journal_io.h"
|
|
#include "journal_reclaim.h"
|
|
|
|
#include <linux/prefetch.h>
|
|
#include <linux/sort.h>
|
|
|
|
static int bch2_btree_write_buffer_journal_flush(struct journal *,
|
|
struct journal_entry_pin *, u64);
|
|
|
|
static int bch2_journal_keys_to_write_buffer(struct bch_fs *, struct journal_buf *);
|
|
|
|
static inline int wb_key_cmp(const void *_l, const void *_r)
|
|
{
|
|
const struct wb_key_ref *l = _l;
|
|
const struct wb_key_ref *r = _r;
|
|
|
|
return cmp_int(l->hi, r->hi) ?:
|
|
cmp_int(l->mi, r->mi) ?:
|
|
cmp_int(l->lo, r->lo);
|
|
}
|
|
|
|
static noinline int wb_flush_one_slowpath(struct btree_trans *trans,
|
|
struct btree_iter *iter,
|
|
struct btree_write_buffered_key *wb)
|
|
{
|
|
bch2_btree_node_unlock_write(trans, iter->path, iter->path->l[0].b);
|
|
|
|
trans->journal_res.seq = wb->journal_seq;
|
|
|
|
return bch2_trans_update(trans, iter, &wb->k,
|
|
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE) ?:
|
|
bch2_trans_commit(trans, NULL, NULL,
|
|
BCH_TRANS_COMMIT_no_enospc|
|
|
BCH_TRANS_COMMIT_no_check_rw|
|
|
BCH_TRANS_COMMIT_no_journal_res|
|
|
BCH_TRANS_COMMIT_journal_reclaim);
|
|
}
|
|
|
|
static inline int wb_flush_one(struct btree_trans *trans, struct btree_iter *iter,
|
|
struct btree_write_buffered_key *wb,
|
|
bool *write_locked, size_t *fast)
|
|
{
|
|
struct bch_fs *c = trans->c;
|
|
struct btree_path *path;
|
|
int ret;
|
|
|
|
EBUG_ON(!wb->journal_seq);
|
|
EBUG_ON(!c->btree_write_buffer.flushing.pin.seq);
|
|
EBUG_ON(c->btree_write_buffer.flushing.pin.seq > wb->journal_seq);
|
|
|
|
ret = bch2_btree_iter_traverse(iter);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/*
|
|
* We can't clone a path that has write locks: unshare it now, before
|
|
* set_pos and traverse():
|
|
*/
|
|
if (iter->path->ref > 1)
|
|
iter->path = __bch2_btree_path_make_mut(trans, iter->path, true, _THIS_IP_);
|
|
|
|
path = iter->path;
|
|
|
|
if (!*write_locked) {
|
|
ret = bch2_btree_node_lock_write(trans, path, &path->l[0].b->c);
|
|
if (ret)
|
|
return ret;
|
|
|
|
bch2_btree_node_prep_for_write(trans, path, path->l[0].b);
|
|
*write_locked = true;
|
|
}
|
|
|
|
if (unlikely(!bch2_btree_node_insert_fits(c, path->l[0].b, wb->k.k.u64s))) {
|
|
*write_locked = false;
|
|
return wb_flush_one_slowpath(trans, iter, wb);
|
|
}
|
|
|
|
bch2_btree_insert_key_leaf(trans, path, &wb->k, wb->journal_seq);
|
|
(*fast)++;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Update a btree with a write buffered key using the journal seq of the
|
|
* original write buffer insert.
|
|
*
|
|
* It is not safe to rejournal the key once it has been inserted into the write
|
|
* buffer because that may break recovery ordering. For example, the key may
|
|
* have already been modified in the active write buffer in a seq that comes
|
|
* before the current transaction. If we were to journal this key again and
|
|
* crash, recovery would process updates in the wrong order.
|
|
*/
|
|
static int
|
|
btree_write_buffered_insert(struct btree_trans *trans,
|
|
struct btree_write_buffered_key *wb)
|
|
{
|
|
struct btree_iter iter;
|
|
int ret;
|
|
|
|
bch2_trans_iter_init(trans, &iter, wb->btree, bkey_start_pos(&wb->k.k),
|
|
BTREE_ITER_CACHED|BTREE_ITER_INTENT);
|
|
|
|
trans->journal_res.seq = wb->journal_seq;
|
|
|
|
ret = bch2_btree_iter_traverse(&iter) ?:
|
|
bch2_trans_update(trans, &iter, &wb->k,
|
|
BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE);
|
|
bch2_trans_iter_exit(trans, &iter);
|
|
return ret;
|
|
}
|
|
|
|
static void move_keys_from_inc_to_flushing(struct btree_write_buffer *wb)
|
|
{
|
|
struct bch_fs *c = container_of(wb, struct bch_fs, btree_write_buffer);
|
|
struct journal *j = &c->journal;
|
|
|
|
if (!wb->inc.keys.nr)
|
|
return;
|
|
|
|
bch2_journal_pin_add(j, wb->inc.keys.data[0].journal_seq, &wb->flushing.pin,
|
|
bch2_btree_write_buffer_journal_flush);
|
|
|
|
darray_resize(&wb->flushing.keys, min_t(size_t, 1U << 20, wb->flushing.keys.nr + wb->inc.keys.nr));
|
|
darray_resize(&wb->sorted, wb->flushing.keys.size);
|
|
|
|
if (!wb->flushing.keys.nr && wb->sorted.size >= wb->inc.keys.nr) {
|
|
swap(wb->flushing.keys, wb->inc.keys);
|
|
goto out;
|
|
}
|
|
|
|
size_t nr = min(darray_room(wb->flushing.keys),
|
|
wb->sorted.size - wb->flushing.keys.nr);
|
|
nr = min(nr, wb->inc.keys.nr);
|
|
|
|
memcpy(&darray_top(wb->flushing.keys),
|
|
wb->inc.keys.data,
|
|
sizeof(wb->inc.keys.data[0]) * nr);
|
|
|
|
memmove(wb->inc.keys.data,
|
|
wb->inc.keys.data + nr,
|
|
sizeof(wb->inc.keys.data[0]) * (wb->inc.keys.nr - nr));
|
|
|
|
wb->flushing.keys.nr += nr;
|
|
wb->inc.keys.nr -= nr;
|
|
out:
|
|
if (!wb->inc.keys.nr)
|
|
bch2_journal_pin_drop(j, &wb->inc.pin);
|
|
else
|
|
bch2_journal_pin_update(j, wb->inc.keys.data[0].journal_seq, &wb->inc.pin,
|
|
bch2_btree_write_buffer_journal_flush);
|
|
|
|
if (j->watermark) {
|
|
spin_lock(&j->lock);
|
|
bch2_journal_set_watermark(j);
|
|
spin_unlock(&j->lock);
|
|
}
|
|
|
|
BUG_ON(wb->sorted.size < wb->flushing.keys.nr);
|
|
}
|
|
|
|
static int bch2_btree_write_buffer_flush_locked(struct btree_trans *trans)
|
|
{
|
|
struct bch_fs *c = trans->c;
|
|
struct journal *j = &c->journal;
|
|
struct btree_write_buffer *wb = &c->btree_write_buffer;
|
|
struct wb_key_ref *i;
|
|
struct btree_iter iter = { NULL };
|
|
size_t skipped = 0, fast = 0, slowpath = 0;
|
|
bool write_locked = false;
|
|
int ret = 0;
|
|
|
|
bch2_trans_unlock(trans);
|
|
bch2_trans_begin(trans);
|
|
|
|
mutex_lock(&wb->inc.lock);
|
|
move_keys_from_inc_to_flushing(wb);
|
|
mutex_unlock(&wb->inc.lock);
|
|
|
|
for (size_t i = 0; i < wb->flushing.keys.nr; i++) {
|
|
wb->sorted.data[i].idx = i;
|
|
wb->sorted.data[i].btree = wb->flushing.keys.data[i].btree;
|
|
wb->sorted.data[i].pos = wb->flushing.keys.data[i].k.k.p;
|
|
}
|
|
wb->sorted.nr = wb->flushing.keys.nr;
|
|
|
|
/*
|
|
* We first sort so that we can detect and skip redundant updates, and
|
|
* then we attempt to flush in sorted btree order, as this is most
|
|
* efficient.
|
|
*
|
|
* However, since we're not flushing in the order they appear in the
|
|
* journal we won't be able to drop our journal pin until everything is
|
|
* flushed - which means this could deadlock the journal if we weren't
|
|
* passing BCH_TRANS_COMMIT_journal_reclaim. This causes the update to fail
|
|
* if it would block taking a journal reservation.
|
|
*
|
|
* If that happens, simply skip the key so we can optimistically insert
|
|
* as many keys as possible in the fast path.
|
|
*/
|
|
sort(wb->sorted.data, wb->sorted.nr,
|
|
sizeof(wb->sorted.data[0]),
|
|
wb_key_cmp, NULL);
|
|
|
|
darray_for_each(wb->sorted, i) {
|
|
struct btree_write_buffered_key *k = &wb->flushing.keys.data[i->idx];
|
|
|
|
for (struct wb_key_ref *n = i + 1; n < min(i + 4, &darray_top(wb->sorted)); n++)
|
|
prefetch(&wb->flushing.keys.data[n->idx]);
|
|
|
|
BUG_ON(!k->journal_seq);
|
|
|
|
if (i + 1 < &darray_top(wb->sorted) &&
|
|
i[0].btree == i[1].btree &&
|
|
bpos_eq(i[0].pos, i[1].pos)) {
|
|
struct btree_write_buffered_key *n = &wb->flushing.keys.data[i[1].idx];
|
|
|
|
skipped++;
|
|
n->journal_seq = min_t(u64, n->journal_seq, k->journal_seq);
|
|
k->journal_seq = 0;
|
|
continue;
|
|
}
|
|
|
|
if (write_locked) {
|
|
struct btree_path *path = iter.path;
|
|
|
|
if (path->btree_id != i->btree ||
|
|
bpos_gt(k->k.k.p, path->l[0].b->key.k.p)) {
|
|
bch2_btree_node_unlock_write(trans, path, path->l[0].b);
|
|
write_locked = false;
|
|
}
|
|
}
|
|
|
|
if (!iter.path || iter.btree_id != k->btree) {
|
|
bch2_trans_iter_exit(trans, &iter);
|
|
bch2_trans_iter_init(trans, &iter, k->btree, k->k.k.p,
|
|
BTREE_ITER_INTENT|BTREE_ITER_ALL_SNAPSHOTS);
|
|
}
|
|
|
|
bch2_btree_iter_set_pos(&iter, k->k.k.p);
|
|
iter.path->preserve = false;
|
|
|
|
do {
|
|
if (race_fault()) {
|
|
ret = -BCH_ERR_journal_reclaim_would_deadlock;
|
|
break;
|
|
}
|
|
|
|
ret = wb_flush_one(trans, &iter, k, &write_locked, &fast);
|
|
if (!write_locked)
|
|
bch2_trans_begin(trans);
|
|
} while (bch2_err_matches(ret, BCH_ERR_transaction_restart));
|
|
|
|
if (!ret) {
|
|
k->journal_seq = 0;
|
|
} else if (ret == -BCH_ERR_journal_reclaim_would_deadlock) {
|
|
slowpath++;
|
|
ret = 0;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if (write_locked)
|
|
bch2_btree_node_unlock_write(trans, iter.path, iter.path->l[0].b);
|
|
bch2_trans_iter_exit(trans, &iter);
|
|
|
|
if (ret)
|
|
goto err;
|
|
|
|
if (slowpath) {
|
|
/*
|
|
* Flush in the order they were present in the journal, so that
|
|
* we can release journal pins:
|
|
* The fastpath zapped the seq of keys that were successfully flushed so
|
|
* we can skip those here.
|
|
*/
|
|
trace_and_count(c, write_buffer_flush_slowpath, trans, slowpath, wb->flushing.keys.nr);
|
|
|
|
struct btree_write_buffered_key *i;
|
|
darray_for_each(wb->flushing.keys, i) {
|
|
if (!i->journal_seq)
|
|
continue;
|
|
|
|
bch2_journal_pin_update(j, i->journal_seq, &wb->flushing.pin,
|
|
bch2_btree_write_buffer_journal_flush);
|
|
|
|
bch2_trans_begin(trans);
|
|
|
|
ret = commit_do(trans, NULL, NULL,
|
|
BCH_WATERMARK_reclaim|
|
|
BCH_TRANS_COMMIT_no_check_rw|
|
|
BCH_TRANS_COMMIT_no_enospc|
|
|
BCH_TRANS_COMMIT_no_journal_res|
|
|
BCH_TRANS_COMMIT_journal_reclaim,
|
|
btree_write_buffered_insert(trans, i));
|
|
if (ret)
|
|
goto err;
|
|
}
|
|
}
|
|
err:
|
|
bch2_fs_fatal_err_on(ret, c, "%s: insert error %s", __func__, bch2_err_str(ret));
|
|
trace_write_buffer_flush(trans, wb->flushing.keys.nr, skipped, fast, 0);
|
|
bch2_journal_pin_drop(j, &wb->flushing.pin);
|
|
wb->flushing.keys.nr = 0;
|
|
return ret;
|
|
}
|
|
|
|
static int fetch_wb_keys_from_journal(struct bch_fs *c, u64 seq)
|
|
{
|
|
struct journal *j = &c->journal;
|
|
struct journal_buf *buf;
|
|
int ret = 0;
|
|
|
|
while (!ret && (buf = bch2_next_write_buffer_flush_journal_buf(j, seq))) {
|
|
ret = bch2_journal_keys_to_write_buffer(c, buf);
|
|
mutex_unlock(&j->buf_lock);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int btree_write_buffer_flush_seq(struct btree_trans *trans, u64 seq)
|
|
{
|
|
struct bch_fs *c = trans->c;
|
|
struct btree_write_buffer *wb = &c->btree_write_buffer;
|
|
int ret = 0, fetch_from_journal_err;
|
|
|
|
do {
|
|
bch2_trans_unlock(trans);
|
|
|
|
fetch_from_journal_err = fetch_wb_keys_from_journal(c, seq);
|
|
|
|
/*
|
|
* On memory allocation failure, bch2_btree_write_buffer_flush_locked()
|
|
* is not guaranteed to empty wb->inc:
|
|
*/
|
|
mutex_lock(&wb->flushing.lock);
|
|
ret = bch2_btree_write_buffer_flush_locked(trans);
|
|
mutex_unlock(&wb->flushing.lock);
|
|
} while (!ret &&
|
|
(fetch_from_journal_err ||
|
|
(wb->inc.pin.seq && wb->inc.pin.seq <= seq) ||
|
|
(wb->flushing.pin.seq && wb->flushing.pin.seq <= seq)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int bch2_btree_write_buffer_journal_flush(struct journal *j,
|
|
struct journal_entry_pin *_pin, u64 seq)
|
|
{
|
|
struct bch_fs *c = container_of(j, struct bch_fs, journal);
|
|
|
|
return bch2_trans_run(c, btree_write_buffer_flush_seq(trans, seq));
|
|
}
|
|
|
|
int bch2_btree_write_buffer_flush_sync(struct btree_trans *trans)
|
|
{
|
|
struct bch_fs *c = trans->c;
|
|
|
|
trace_and_count(c, write_buffer_flush_sync, trans, _RET_IP_);
|
|
|
|
return btree_write_buffer_flush_seq(trans, journal_cur_seq(&c->journal));
|
|
}
|
|
|
|
int bch2_btree_write_buffer_flush_nocheck_rw(struct btree_trans *trans)
|
|
{
|
|
struct bch_fs *c = trans->c;
|
|
struct btree_write_buffer *wb = &c->btree_write_buffer;
|
|
int ret = 0;
|
|
|
|
if (mutex_trylock(&wb->flushing.lock)) {
|
|
ret = bch2_btree_write_buffer_flush_locked(trans);
|
|
mutex_unlock(&wb->flushing.lock);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int bch2_btree_write_buffer_tryflush(struct btree_trans *trans)
|
|
{
|
|
struct bch_fs *c = trans->c;
|
|
|
|
if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_btree_write_buffer))
|
|
return -BCH_ERR_erofs_no_writes;
|
|
|
|
int ret = bch2_btree_write_buffer_flush_nocheck_rw(trans);
|
|
bch2_write_ref_put(c, BCH_WRITE_REF_btree_write_buffer);
|
|
return ret;
|
|
}
|
|
|
|
static void bch2_btree_write_buffer_flush_work(struct work_struct *work)
|
|
{
|
|
struct bch_fs *c = container_of(work, struct bch_fs, btree_write_buffer.flush_work);
|
|
struct btree_write_buffer *wb = &c->btree_write_buffer;
|
|
int ret;
|
|
|
|
mutex_lock(&wb->flushing.lock);
|
|
do {
|
|
ret = bch2_trans_run(c, bch2_btree_write_buffer_flush_locked(trans));
|
|
} while (!ret && bch2_btree_write_buffer_should_flush(c));
|
|
mutex_unlock(&wb->flushing.lock);
|
|
|
|
bch2_write_ref_put(c, BCH_WRITE_REF_btree_write_buffer);
|
|
}
|
|
|
|
int __bch2_journal_key_to_wb(struct bch_fs *c,
|
|
struct journal_keys_to_wb *dst,
|
|
enum btree_id btree, struct bkey_i *k)
|
|
{
|
|
struct btree_write_buffer *wb = &c->btree_write_buffer;
|
|
int ret;
|
|
retry:
|
|
ret = darray_make_room_gfp(&dst->wb->keys, 1, GFP_KERNEL);
|
|
if (!ret && dst->wb == &wb->flushing)
|
|
ret = darray_resize(&wb->sorted, wb->flushing.keys.size);
|
|
|
|
if (unlikely(ret)) {
|
|
if (dst->wb == &c->btree_write_buffer.flushing) {
|
|
mutex_unlock(&dst->wb->lock);
|
|
dst->wb = &c->btree_write_buffer.inc;
|
|
bch2_journal_pin_add(&c->journal, dst->seq, &dst->wb->pin,
|
|
bch2_btree_write_buffer_journal_flush);
|
|
goto retry;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
dst->room = darray_room(dst->wb->keys);
|
|
if (dst->wb == &wb->flushing)
|
|
dst->room = min(dst->room, wb->sorted.size - wb->flushing.keys.nr);
|
|
BUG_ON(!dst->room);
|
|
BUG_ON(!dst->seq);
|
|
|
|
struct btree_write_buffered_key *wb_k = &darray_top(dst->wb->keys);
|
|
wb_k->journal_seq = dst->seq;
|
|
wb_k->btree = btree;
|
|
bkey_copy(&wb_k->k, k);
|
|
dst->wb->keys.nr++;
|
|
dst->room--;
|
|
return 0;
|
|
}
|
|
|
|
void bch2_journal_keys_to_write_buffer_start(struct bch_fs *c, struct journal_keys_to_wb *dst, u64 seq)
|
|
{
|
|
struct btree_write_buffer *wb = &c->btree_write_buffer;
|
|
|
|
if (mutex_trylock(&wb->flushing.lock)) {
|
|
mutex_lock(&wb->inc.lock);
|
|
move_keys_from_inc_to_flushing(wb);
|
|
|
|
/*
|
|
* Attempt to skip wb->inc, and add keys directly to
|
|
* wb->flushing, saving us a copy later:
|
|
*/
|
|
|
|
if (!wb->inc.keys.nr) {
|
|
dst->wb = &wb->flushing;
|
|
} else {
|
|
mutex_unlock(&wb->flushing.lock);
|
|
dst->wb = &wb->inc;
|
|
}
|
|
} else {
|
|
mutex_lock(&wb->inc.lock);
|
|
dst->wb = &wb->inc;
|
|
}
|
|
|
|
dst->room = darray_room(dst->wb->keys);
|
|
if (dst->wb == &wb->flushing)
|
|
dst->room = min(dst->room, wb->sorted.size - wb->flushing.keys.nr);
|
|
dst->seq = seq;
|
|
|
|
bch2_journal_pin_add(&c->journal, seq, &dst->wb->pin,
|
|
bch2_btree_write_buffer_journal_flush);
|
|
}
|
|
|
|
void bch2_journal_keys_to_write_buffer_end(struct bch_fs *c, struct journal_keys_to_wb *dst)
|
|
{
|
|
struct btree_write_buffer *wb = &c->btree_write_buffer;
|
|
|
|
if (!dst->wb->keys.nr)
|
|
bch2_journal_pin_drop(&c->journal, &dst->wb->pin);
|
|
|
|
if (bch2_btree_write_buffer_should_flush(c) &&
|
|
__bch2_write_ref_tryget(c, BCH_WRITE_REF_btree_write_buffer) &&
|
|
!queue_work(system_unbound_wq, &c->btree_write_buffer.flush_work))
|
|
bch2_write_ref_put(c, BCH_WRITE_REF_btree_write_buffer);
|
|
|
|
if (dst->wb == &wb->flushing)
|
|
mutex_unlock(&wb->flushing.lock);
|
|
mutex_unlock(&wb->inc.lock);
|
|
}
|
|
|
|
static int bch2_journal_keys_to_write_buffer(struct bch_fs *c, struct journal_buf *buf)
|
|
{
|
|
struct journal_keys_to_wb dst;
|
|
struct jset_entry *entry;
|
|
struct bkey_i *k;
|
|
int ret = 0;
|
|
|
|
bch2_journal_keys_to_write_buffer_start(c, &dst, le64_to_cpu(buf->data->seq));
|
|
|
|
for_each_jset_entry_type(entry, buf->data, BCH_JSET_ENTRY_write_buffer_keys) {
|
|
jset_entry_for_each_key(entry, k) {
|
|
ret = bch2_journal_key_to_wb(c, &dst, entry->btree_id, k);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
entry->type = BCH_JSET_ENTRY_btree_keys;
|
|
}
|
|
|
|
buf->need_flush_to_write_buffer = false;
|
|
out:
|
|
bch2_journal_keys_to_write_buffer_end(c, &dst);
|
|
return ret;
|
|
}
|
|
|
|
static int wb_keys_resize(struct btree_write_buffer_keys *wb, size_t new_size)
|
|
{
|
|
if (wb->keys.size >= new_size)
|
|
return 0;
|
|
|
|
if (!mutex_trylock(&wb->lock))
|
|
return -EINTR;
|
|
|
|
int ret = darray_resize(&wb->keys, new_size);
|
|
mutex_unlock(&wb->lock);
|
|
return ret;
|
|
}
|
|
|
|
int bch2_btree_write_buffer_resize(struct bch_fs *c, size_t new_size)
|
|
{
|
|
struct btree_write_buffer *wb = &c->btree_write_buffer;
|
|
|
|
return wb_keys_resize(&wb->flushing, new_size) ?:
|
|
wb_keys_resize(&wb->inc, new_size);
|
|
}
|
|
|
|
void bch2_fs_btree_write_buffer_exit(struct bch_fs *c)
|
|
{
|
|
struct btree_write_buffer *wb = &c->btree_write_buffer;
|
|
|
|
BUG_ON((wb->inc.keys.nr || wb->flushing.keys.nr) &&
|
|
!bch2_journal_error(&c->journal));
|
|
|
|
darray_exit(&wb->sorted);
|
|
darray_exit(&wb->flushing.keys);
|
|
darray_exit(&wb->inc.keys);
|
|
}
|
|
|
|
int bch2_fs_btree_write_buffer_init(struct bch_fs *c)
|
|
{
|
|
struct btree_write_buffer *wb = &c->btree_write_buffer;
|
|
|
|
mutex_init(&wb->inc.lock);
|
|
mutex_init(&wb->flushing.lock);
|
|
INIT_WORK(&wb->flush_work, bch2_btree_write_buffer_flush_work);
|
|
|
|
/* Will be resized by journal as needed: */
|
|
unsigned initial_size = 1 << 16;
|
|
|
|
return darray_make_room(&wb->inc.keys, initial_size) ?:
|
|
darray_make_room(&wb->flushing.keys, initial_size) ?:
|
|
darray_make_room(&wb->sorted, initial_size);
|
|
}
|