]> git.sesse.net Git - bcachefs-tools-debian/commitdiff
Update bcachefs sources to 3e0c5b0722 fixup! bcachefs: Btree write buffer
authorKent Overstreet <kent.overstreet@linux.dev>
Mon, 13 Feb 2023 02:40:59 +0000 (21:40 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Mon, 13 Feb 2023 02:41:03 +0000 (21:41 -0500)
32 files changed:
.bcachefs_revision
include/trace/events/bcachefs.h
libbcachefs/alloc_background.c
libbcachefs/backpointers.c
libbcachefs/backpointers.h
libbcachefs/bcachefs.h
libbcachefs/bcachefs_format.h
libbcachefs/btree_io.c
libbcachefs/btree_iter.c
libbcachefs/btree_types.h
libbcachefs/btree_update.h
libbcachefs/btree_update_interior.c
libbcachefs/btree_update_interior.h
libbcachefs/btree_update_leaf.c
libbcachefs/btree_write_buffer.c [new file with mode: 0644]
libbcachefs/btree_write_buffer.h [new file with mode: 0644]
libbcachefs/btree_write_buffer_types.h [new file with mode: 0644]
libbcachefs/buckets.c
libbcachefs/buckets.h
libbcachefs/data_update.c
libbcachefs/ec.c
libbcachefs/errcode.h
libbcachefs/fsck.c
libbcachefs/io.c
libbcachefs/journal.c
libbcachefs/move.c
libbcachefs/opts.h
libbcachefs/recovery.c
libbcachefs/replicas.c
libbcachefs/super.c
libbcachefs/sysfs.c
libbcachefs/tests.c

index b5ff7667b0402a79e7d8b10f8f1727bc4b38a62b..f6330b43bf426d6285a6d9e04654227dc93d6f1b 100644 (file)
@@ -1 +1 @@
-dab31ca168df1c7a492a0a2a626ba6cd983b2a7a
+3e0c5b0722d7fccf0ba83435c5da8892b00c0fe0
index ca5d6c8a4ddbe7503e15cdb6af41abe5c6184d68..10e51bb37557ce990c610fa4580ebba66538bf88 100644 (file)
@@ -1145,6 +1145,51 @@ TRACE_EVENT(trans_restart_key_cache_key_realloced,
                  __entry->new_u64s)
 );
 
+DEFINE_EVENT(transaction_event,        trans_restart_write_buffer_flush,
+       TP_PROTO(struct btree_trans *trans,
+                unsigned long caller_ip),
+       TP_ARGS(trans, caller_ip)
+);
+
+TRACE_EVENT(write_buffer_flush,
+       TP_PROTO(struct btree_trans *trans, size_t nr, size_t skipped, size_t fast, size_t size),
+       TP_ARGS(trans, nr, skipped, fast, size),
+
+       TP_STRUCT__entry(
+               __field(size_t,         nr              )
+               __field(size_t,         skipped         )
+               __field(size_t,         fast            )
+               __field(size_t,         size            )
+       ),
+
+       TP_fast_assign(
+               __entry->nr     = nr;
+               __entry->skipped = skipped;
+               __entry->fast   = fast;
+               __entry->size   = size;
+       ),
+
+       TP_printk("%zu/%zu skipped %zu fast %zu",
+                 __entry->nr, __entry->size, __entry->skipped, __entry->fast)
+);
+
+TRACE_EVENT(write_buffer_flush_slowpath,
+       TP_PROTO(struct btree_trans *trans, size_t nr, size_t size),
+       TP_ARGS(trans, nr, size),
+
+       TP_STRUCT__entry(
+               __field(size_t,         nr              )
+               __field(size_t,         size            )
+       ),
+
+       TP_fast_assign(
+               __entry->nr     = nr;
+               __entry->size   = size;
+       ),
+
+       TP_printk("%zu/%zu", __entry->nr, __entry->size)
+);
+
 #endif /* _TRACE_BCACHE_H */
 
 /* This part must be outside protection */
index 6fd948f11c0eb284126b85a2bf7405df58999870..2db44365a4ec4afe819a1783cd20fd8704b9d971 100644 (file)
@@ -414,14 +414,13 @@ void bch2_alloc_to_text(struct printbuf *out, struct bch_fs *c, struct bkey_s_c
        prt_newline(out);
        prt_printf(out, "io_time[WRITE]    %llu",       a->io_time[WRITE]);
        prt_newline(out);
+       prt_printf(out, "bp_start          %llu", BCH_ALLOC_V4_BACKPOINTERS_START(a));
+       prt_newline(out);
 
-       if (k.k->type == KEY_TYPE_alloc_v4) {
+       if (BCH_ALLOC_V4_NR_BACKPOINTERS(a)) {
                struct bkey_s_c_alloc_v4 a_raw = bkey_s_c_to_alloc_v4(k);
                const struct bch_backpointer *bps = alloc_v4_backpointers_c(a_raw.v);
 
-               prt_printf(out, "bp_start          %llu", BCH_ALLOC_V4_BACKPOINTERS_START(a_raw.v));
-               prt_newline(out);
-
                prt_printf(out, "backpointers:     %llu", BCH_ALLOC_V4_NR_BACKPOINTERS(a_raw.v));
                printbuf_indent_add(out, 2);
 
@@ -674,7 +673,7 @@ int bch2_bucket_gens_init(struct bch_fs *c)
                        ret = commit_do(&trans, NULL, NULL,
                                        BTREE_INSERT_NOFAIL|
                                        BTREE_INSERT_LAZY_RW,
-                               __bch2_btree_insert(&trans, BTREE_ID_bucket_gens, &g.k_i));
+                               __bch2_btree_insert(&trans, BTREE_ID_bucket_gens, &g.k_i, 0));
                        if (ret)
                                break;
                        have_bucket_gens_key = false;
@@ -694,7 +693,7 @@ int bch2_bucket_gens_init(struct bch_fs *c)
                ret = commit_do(&trans, NULL, NULL,
                                BTREE_INSERT_NOFAIL|
                                BTREE_INSERT_LAZY_RW,
-                       __bch2_btree_insert(&trans, BTREE_ID_bucket_gens, &g.k_i));
+                       __bch2_btree_insert(&trans, BTREE_ID_bucket_gens, &g.k_i, 0));
 
        bch2_trans_exit(&trans);
 
@@ -1934,7 +1933,7 @@ static int bch2_dev_freespace_init(struct bch_fs *c, struct bch_dev *ca)
                        freespace->k.p          = k.k->p;
                        freespace->k.size       = k.k->size;
 
-                       ret = __bch2_btree_insert(&trans, BTREE_ID_freespace, freespace) ?:
+                       ret = __bch2_btree_insert(&trans, BTREE_ID_freespace, freespace, 0) ?:
                                bch2_trans_commit(&trans, NULL, NULL,
                                                  BTREE_INSERT_LAZY_RW|
                                                  BTREE_INSERT_NOFAIL);
index 0f8ffdf45ca809f45c3d5157e465353988d37705..c269fc73a41dd038e97e665bf5736950b6bda01a 100644 (file)
@@ -5,42 +5,11 @@
 #include "backpointers.h"
 #include "btree_cache.h"
 #include "btree_update.h"
+#include "btree_write_buffer.h"
 #include "error.h"
 
 #include <linux/mm.h>
 
-/*
- * Convert from pos in backpointer btree to pos of corresponding bucket in alloc
- * btree:
- */
-static inline struct bpos bp_pos_to_bucket(const struct bch_fs *c,
-                                          struct bpos bp_pos)
-{
-       struct bch_dev *ca = bch_dev_bkey_exists(c, bp_pos.inode);
-       u64 bucket_sector = bp_pos.offset >> MAX_EXTENT_COMPRESS_RATIO_SHIFT;
-
-       return POS(bp_pos.inode, sector_to_bucket(ca, bucket_sector));
-}
-
-/*
- * Convert from pos in alloc btree + bucket offset to pos in backpointer btree:
- */
-static inline struct bpos bucket_pos_to_bp(const struct bch_fs *c,
-                                          struct bpos bucket,
-                                          u64 bucket_offset)
-{
-       struct bch_dev *ca = bch_dev_bkey_exists(c, bucket.inode);
-       struct bpos ret;
-
-       ret = POS(bucket.inode,
-                 (bucket_to_sector(ca, bucket.offset) <<
-                  MAX_EXTENT_COMPRESS_RATIO_SHIFT) + bucket_offset);
-
-       BUG_ON(!bkey_eq(bucket, bp_pos_to_bucket(c, ret)));
-
-       return ret;
-}
-
 static bool extent_matches_bp(struct bch_fs *c,
                              enum btree_id btree_id, unsigned level,
                              struct bkey_s_c k,
@@ -200,51 +169,53 @@ err:
        return ret;
 }
 
-int bch2_bucket_backpointer_del(struct btree_trans *trans,
-                               struct bkey_i_alloc_v4 *a,
-                               struct bch_backpointer bp,
-                               struct bkey_s_c orig_k)
+bool bch2_bucket_backpointer_del(struct btree_trans *trans,
+                                struct bkey_i_alloc_v4 *a,
+                                struct bch_backpointer bp)
 {
-       struct bch_fs *c = trans->c;
        struct bch_backpointer *bps = alloc_v4_backpointers(&a->v);
        unsigned i, nr = BCH_ALLOC_V4_NR_BACKPOINTERS(&a->v);
-       struct btree_iter bp_iter;
-       struct bkey_s_c k;
-       int ret;
 
        for (i = 0; i < nr; i++) {
                int cmp = backpointer_cmp(bps[i], bp) ?:
                        memcmp(&bps[i], &bp, sizeof(bp));
-               if (!cmp)
-                       goto found;
+               if (!cmp) {
+                       array_remove_item(bps, nr, i);
+                       SET_BCH_ALLOC_V4_NR_BACKPOINTERS(&a->v, nr);
+                       set_alloc_v4_u64s(a);
+                       return true;
+               }
                if (cmp >= 0)
                        break;
        }
 
-       goto btree;
-found:
-       array_remove_item(bps, nr, i);
-       SET_BCH_ALLOC_V4_NR_BACKPOINTERS(&a->v, nr);
-       set_alloc_v4_u64s(a);
-       return 0;
-btree:
-       bch2_trans_iter_init(trans, &bp_iter, BTREE_ID_backpointers,
-                            bucket_pos_to_bp(c, a->k.p, bp.bucket_offset),
-                            BTREE_ITER_INTENT|
-                            BTREE_ITER_SLOTS|
-                            BTREE_ITER_WITH_UPDATES);
-       k = bch2_btree_iter_peek_slot(&bp_iter);
-       ret = bkey_err(k);
-       if (ret)
-               goto err;
+       return false;
+}
 
-       if (k.k->type != KEY_TYPE_backpointer ||
-           memcmp(bkey_s_c_to_backpointer(k).v, &bp, sizeof(bp))) {
-               struct printbuf buf = PRINTBUF;
+static noinline int backpointer_mod_err(struct btree_trans *trans,
+                                       struct bch_backpointer bp,
+                                       struct bkey_s_c bp_k,
+                                       struct bkey_s_c orig_k,
+                                       bool insert)
+{
+       struct bch_fs *c = trans->c;
+       struct printbuf buf = PRINTBUF;
 
-               if (!test_bit(BCH_FS_CHECK_BACKPOINTERS_DONE, &c->flags))
-                       goto err;
+       if (insert) {
+               prt_printf(&buf, "existing backpointer found when inserting ");
+               bch2_backpointer_to_text(&buf, &bp);
+               prt_newline(&buf);
+               printbuf_indent_add(&buf, 2);
+
+               prt_printf(&buf, "found ");
+               bch2_bkey_val_to_text(&buf, c, bp_k);
+               prt_newline(&buf);
+
+               prt_printf(&buf, "for ");
+               bch2_bkey_val_to_text(&buf, c, orig_k);
 
+               bch_err(c, "%s", buf.buf);
+       } else if (test_bit(BCH_FS_CHECK_BACKPOINTERS_DONE, &c->flags)) {
                prt_printf(&buf, "backpointer not found when deleting");
                prt_newline(&buf);
                printbuf_indent_add(&buf, 2);
@@ -254,85 +225,51 @@ btree:
                prt_newline(&buf);
 
                prt_printf(&buf, "got ");
-               bch2_bkey_val_to_text(&buf, c, k);
-               prt_newline(&buf);
-
-               prt_str(&buf, "alloc ");
-               bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&a->k_i));
+               bch2_bkey_val_to_text(&buf, c, bp_k);
                prt_newline(&buf);
 
                prt_printf(&buf, "for ");
                bch2_bkey_val_to_text(&buf, c, orig_k);
 
                bch_err(c, "%s", buf.buf);
-               bch2_inconsistent_error(c);
-               ret = -EIO;
-               printbuf_exit(&buf);
-               goto err;
        }
 
-       ret = bch2_btree_delete_at(trans, &bp_iter, 0);
-err:
-       bch2_trans_iter_exit(trans, &bp_iter);
-       return ret;
+       printbuf_exit(&buf);
+
+       if (test_bit(BCH_FS_CHECK_BACKPOINTERS_DONE, &c->flags)) {
+               bch2_inconsistent_error(c);
+               return -EIO;
+       } else {
+               return 0;
+       }
 }
 
-int bch2_bucket_backpointer_add(struct btree_trans *trans,
+int bch2_bucket_backpointer_mod_nowritebuffer(struct btree_trans *trans,
                                struct bkey_i_alloc_v4 *a,
                                struct bch_backpointer bp,
-                               struct bkey_s_c orig_k)
+                               struct bkey_s_c orig_k,
+                               bool insert)
 {
        struct bch_fs *c = trans->c;
-       struct bch_backpointer *bps = alloc_v4_backpointers(&a->v);
-       unsigned i, nr = BCH_ALLOC_V4_NR_BACKPOINTERS(&a->v);
        struct bkey_i_backpointer *bp_k;
        struct btree_iter bp_iter;
        struct bkey_s_c k;
        int ret;
 
-       /* Check for duplicates: */
-       for (i = 0; i < nr; i++) {
-               int cmp = backpointer_cmp(bps[i], bp);
-               if (cmp >= 0)
-                       break;
-       }
-
-       if ((i &&
-            (bps[i - 1].bucket_offset +
-             bps[i - 1].bucket_len > bp.bucket_offset)) ||
-           (i < nr &&
-            (bp.bucket_offset + bp.bucket_len > bps[i].bucket_offset))) {
-               struct printbuf buf = PRINTBUF;
-
-               prt_printf(&buf, "overlapping backpointer found when inserting ");
-               bch2_backpointer_to_text(&buf, &bp);
-               prt_newline(&buf);
-               printbuf_indent_add(&buf, 2);
-
-               prt_printf(&buf, "into ");
-               bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(&a->k_i));
-               prt_newline(&buf);
-
-               prt_printf(&buf, "for ");
-               bch2_bkey_val_to_text(&buf, c, orig_k);
+       bp_k = bch2_trans_kmalloc_nomemzero(trans, sizeof(struct bkey_i_backpointer));
+       ret = PTR_ERR_OR_ZERO(bp_k);
+       if (ret)
+               return ret;
 
-               bch_err(c, "%s", buf.buf);
-               printbuf_exit(&buf);
-               if (test_bit(BCH_FS_CHECK_BACKPOINTERS_DONE, &c->flags)) {
-                       bch2_inconsistent_error(c);
-                       return -EIO;
-               }
-       }
+       bkey_backpointer_init(&bp_k->k_i);
+       bp_k->k.p = bucket_pos_to_bp(c, a->k.p, bp.bucket_offset);
+       bp_k->v = bp;
 
-       if (nr < BCH_ALLOC_V4_NR_BACKPOINTERS_MAX) {
-               array_insert_item(bps, nr, i, bp);
-               SET_BCH_ALLOC_V4_NR_BACKPOINTERS(&a->v, nr);
-               set_alloc_v4_u64s(a);
-               return 0;
+       if (!insert) {
+               bp_k->k.type = KEY_TYPE_deleted;
+               set_bkey_val_u64s(&bp_k->k, 0);
        }
 
-       /* Overflow: use backpointer btree */
-
        bch2_trans_iter_init(trans, &bp_iter, BTREE_ID_backpointers,
                             bucket_pos_to_bp(c, a->k.p, bp.bucket_offset),
                             BTREE_ITER_INTENT|
@@ -343,37 +280,15 @@ int bch2_bucket_backpointer_add(struct btree_trans *trans,
        if (ret)
                goto err;
 
-       if (k.k->type) {
-               struct printbuf buf = PRINTBUF;
-
-               prt_printf(&buf, "existing btree backpointer key found when inserting ");
-               bch2_backpointer_to_text(&buf, &bp);
-               prt_newline(&buf);
-               printbuf_indent_add(&buf, 2);
-
-               prt_printf(&buf, "found ");
-               bch2_bkey_val_to_text(&buf, c, k);
-               prt_newline(&buf);
-
-               prt_printf(&buf, "for ");
-               bch2_bkey_val_to_text(&buf, c, orig_k);
-
-               bch_err(c, "%s", buf.buf);
-               printbuf_exit(&buf);
-               if (test_bit(BCH_FS_CHECK_BACKPOINTERS_DONE, &c->flags)) {
-                       bch2_inconsistent_error(c);
-                       ret = -EIO;
+       if (insert
+           ? k.k->type
+           : (k.k->type != KEY_TYPE_backpointer ||
+              memcmp(bkey_s_c_to_backpointer(k).v, &bp, sizeof(bp)))) {
+               ret = backpointer_mod_err(trans, bp, k, orig_k, insert);
+               if (ret)
                        goto err;
-               }
        }
 
-       bp_k = bch2_bkey_alloc(trans, &bp_iter, backpointer);
-       ret = PTR_ERR_OR_ZERO(bp_k);
-       if (ret)
-               goto err;
-
-       bp_k->v = bp;
-
        ret = bch2_trans_update(trans, &bp_iter, &bp_k->k_i, 0);
 err:
        bch2_trans_iter_exit(trans, &bp_iter);
@@ -457,6 +372,9 @@ static void backpointer_not_found(struct btree_trans *trans,
        struct bch_fs *c = trans->c;
        struct printbuf buf = PRINTBUF;
 
+       if (likely(!bch2_backpointers_no_use_write_buffer))
+               return;
+
        prt_printf(&buf, "backpointer doesn't match %s it points to:\n  ",
                   thing_it_points_to);
        prt_printf(&buf, "bucket: ");
@@ -512,27 +430,30 @@ struct bkey_s_c bch2_backpointer_get_key(struct btree_trans *trans,
 
        bch2_trans_iter_exit(trans, iter);
 
-       if (bp.level) {
-               struct btree *b;
-
-               /*
-                * If a backpointer for a btree node wasn't found, it may be
-                * because it was overwritten by a new btree node that hasn't
-                * been written out yet - backpointer_get_node() checks for
-                * this:
-                */
-               b = bch2_backpointer_get_node(trans, iter, bucket, bp_offset, bp);
-               if (!IS_ERR_OR_NULL(b))
-                       return bkey_i_to_s_c(&b->key);
-
-               bch2_trans_iter_exit(trans, iter);
+       if (unlikely(bch2_backpointers_no_use_write_buffer)) {
+               if (bp.level) {
+                       struct btree *b;
+
+                       /*
+                        * If a backpointer for a btree node wasn't found, it may be
+                        * because it was overwritten by a new btree node that hasn't
+                        * been written out yet - backpointer_get_node() checks for
+                        * this:
+                        */
+                       b = bch2_backpointer_get_node(trans, iter, bucket, bp_offset, bp);
+                       if (!IS_ERR_OR_NULL(b))
+                               return bkey_i_to_s_c(&b->key);
+
+                       bch2_trans_iter_exit(trans, iter);
+
+                       if (IS_ERR(b))
+                               return bkey_s_c_err(PTR_ERR(b));
+                       return bkey_s_c_null;
+               }
 
-               if (IS_ERR(b))
-                       return bkey_s_c_err(PTR_ERR(b));
-               return bkey_s_c_null;
+               backpointer_not_found(trans, bucket, bp_offset, bp, k, "extent");
        }
 
-       backpointer_not_found(trans, bucket, bp_offset, bp, k, "extent");
        return bkey_s_c_null;
 }
 
@@ -633,7 +554,8 @@ static int check_bp_exists(struct btree_trans *trans,
                           struct bch_backpointer bp,
                           struct bkey_s_c orig_k,
                           struct bpos bucket_start,
-                          struct bpos bucket_end)
+                          struct bpos bucket_end,
+                          struct bpos *last_flushed_pos)
 {
        struct bch_fs *c = trans->c;
        struct btree_iter alloc_iter, bp_iter = { NULL };
@@ -677,8 +599,15 @@ static int check_bp_exists(struct btree_trans *trans,
                goto err;
 
        if (bp_k.k->type != KEY_TYPE_backpointer ||
-           memcmp(bkey_s_c_to_backpointer(bp_k).v, &bp, sizeof(bp)))
+           memcmp(bkey_s_c_to_backpointer(bp_k).v, &bp, sizeof(bp))) {
+               if (!bpos_eq(*last_flushed_pos, orig_k.k->p)) {
+                       *last_flushed_pos = orig_k.k->p;
+                       ret = bch2_btree_write_buffer_flush_sync(trans) ?:
+                               -BCH_ERR_transaction_restart_write_buffer_flush;
+                       goto out;
+               }
                goto missing;
+       }
 out:
 err:
 fsck_err:
@@ -687,11 +616,12 @@ fsck_err:
        printbuf_exit(&buf);
        return ret;
 missing:
+
        prt_printf(&buf, "missing backpointer for btree=%s l=%u ",
               bch2_btree_ids[bp.btree_id], bp.level);
        bch2_bkey_val_to_text(&buf, c, orig_k);
-       prt_printf(&buf, "\nin alloc key ");
-       bch2_bkey_val_to_text(&buf, c, alloc_k);
+       prt_printf(&buf, "\nbp pos ");
+       bch2_bpos_to_text(&buf, bp_iter.pos);
 
        if (c->sb.version < bcachefs_metadata_version_backpointers ||
            c->opts.reconstruct_alloc ||
@@ -699,8 +629,7 @@ missing:
                struct bkey_i_alloc_v4 *a = bch2_alloc_to_v4_mut(trans, alloc_k);
 
                ret   = PTR_ERR_OR_ZERO(a) ?:
-                       bch2_bucket_backpointer_add(trans, a, bp, orig_k) ?:
-                       bch2_trans_update(trans, &alloc_iter, &a->k_i, 0);
+                       bch2_bucket_backpointer_mod(trans, a, bp, orig_k, true);
        }
 
        goto out;
@@ -709,7 +638,8 @@ missing:
 static int check_extent_to_backpointers(struct btree_trans *trans,
                                        struct btree_iter *iter,
                                        struct bpos bucket_start,
-                                       struct bpos bucket_end)
+                                       struct bpos bucket_end,
+                                       struct bpos *last_flushed_pos)
 {
        struct bch_fs *c = trans->c;
        struct bkey_ptrs_c ptrs;
@@ -736,7 +666,9 @@ static int check_extent_to_backpointers(struct btree_trans *trans,
                bch2_extent_ptr_to_bp(c, iter->btree_id, iter->path->level,
                                      k, p, &bucket_pos, &bp);
 
-               ret = check_bp_exists(trans, bucket_pos, bp, k, bucket_start, bucket_end);
+               ret = check_bp_exists(trans, bucket_pos, bp, k,
+                                     bucket_start, bucket_end,
+                                     last_flushed_pos);
                if (ret)
                        return ret;
        }
@@ -747,7 +679,8 @@ static int check_extent_to_backpointers(struct btree_trans *trans,
 static int check_btree_root_to_backpointers(struct btree_trans *trans,
                                            enum btree_id btree_id,
                                            struct bpos bucket_start,
-                                           struct bpos bucket_end)
+                                           struct bpos bucket_end,
+                                           struct bpos *last_flushed_pos)
 {
        struct bch_fs *c = trans->c;
        struct btree_iter iter;
@@ -779,7 +712,9 @@ static int check_btree_root_to_backpointers(struct btree_trans *trans,
                bch2_extent_ptr_to_bp(c, iter.btree_id, iter.path->level + 1,
                                      k, p, &bucket_pos, &bp);
 
-               ret = check_bp_exists(trans, bucket_pos, bp, k, bucket_start, bucket_end);
+               ret = check_bp_exists(trans, bucket_pos, bp, k,
+                                     bucket_start, bucket_end,
+                                     last_flushed_pos);
                if (ret)
                        goto err;
        }
@@ -859,6 +794,7 @@ static int bch2_check_extents_to_backpointers_pass(struct btree_trans *trans,
 {
        struct btree_iter iter;
        enum btree_id btree_id;
+       struct bpos last_flushed_pos = SPOS_MAX;
        int ret = 0;
 
        for (btree_id = 0; btree_id < BTREE_ID_NR; btree_id++) {
@@ -874,7 +810,8 @@ static int bch2_check_extents_to_backpointers_pass(struct btree_trans *trans,
                                        BTREE_INSERT_LAZY_RW|
                                        BTREE_INSERT_NOFAIL,
                                        check_extent_to_backpointers(trans, &iter,
-                                                               bucket_start, bucket_end));
+                                                               bucket_start, bucket_end,
+                                                               &last_flushed_pos));
                        if (ret)
                                break;
                } while (!bch2_btree_iter_advance(&iter));
@@ -888,7 +825,8 @@ static int bch2_check_extents_to_backpointers_pass(struct btree_trans *trans,
                                BTREE_INSERT_LAZY_RW|
                                BTREE_INSERT_NOFAIL,
                                check_btree_root_to_backpointers(trans, btree_id,
-                                                       bucket_start, bucket_end));
+                                                       bucket_start, bucket_end,
+                                                       &last_flushed_pos));
                if (ret)
                        break;
        }
index ac7b09321aabee73a3b8d7349b3fe3ebebc0866a..ded1ab7fb0bc0e1dad331e01f1539427dea3bf3d 100644 (file)
@@ -2,6 +2,8 @@
 #ifndef _BCACHEFS_BACKPOINTERS_BACKGROUND_H
 #define _BCACHEFS_BACKPOINTERS_BACKGROUND_H
 
+#include "btree_iter.h"
+#include "btree_update.h"
 #include "buckets.h"
 #include "super.h"
 
@@ -19,6 +21,81 @@ void bch2_backpointer_swab(struct bkey_s);
 
 #define MAX_EXTENT_COMPRESS_RATIO_SHIFT                10
 
+/*
+ * Convert from pos in backpointer btree to pos of corresponding bucket in alloc
+ * btree:
+ */
+static inline struct bpos bp_pos_to_bucket(const struct bch_fs *c,
+                                          struct bpos bp_pos)
+{
+       struct bch_dev *ca = bch_dev_bkey_exists(c, bp_pos.inode);
+       u64 bucket_sector = bp_pos.offset >> MAX_EXTENT_COMPRESS_RATIO_SHIFT;
+
+       return POS(bp_pos.inode, sector_to_bucket(ca, bucket_sector));
+}
+
+/*
+ * Convert from pos in alloc btree + bucket offset to pos in backpointer btree:
+ */
+static inline struct bpos bucket_pos_to_bp(const struct bch_fs *c,
+                                          struct bpos bucket,
+                                          u64 bucket_offset)
+{
+       struct bch_dev *ca = bch_dev_bkey_exists(c, bucket.inode);
+       struct bpos ret;
+
+       ret = POS(bucket.inode,
+                 (bucket_to_sector(ca, bucket.offset) <<
+                  MAX_EXTENT_COMPRESS_RATIO_SHIFT) + bucket_offset);
+
+       BUG_ON(!bkey_eq(bucket, bp_pos_to_bucket(c, ret)));
+
+       return ret;
+}
+
+bool bch2_bucket_backpointer_del(struct btree_trans *,
+                               struct bkey_i_alloc_v4 *,
+                               struct bch_backpointer);
+
+int bch2_bucket_backpointer_mod_nowritebuffer(struct btree_trans *,
+                               struct bkey_i_alloc_v4 *,
+                               struct bch_backpointer, struct bkey_s_c, bool);
+
+static inline int bch2_bucket_backpointer_mod(struct btree_trans *trans,
+                               struct bkey_i_alloc_v4 *a,
+                               struct bch_backpointer bp,
+                               struct bkey_s_c orig_k,
+                               bool insert)
+{
+       struct bch_fs *c = trans->c;
+       struct bkey_i_backpointer *bp_k;
+       int ret;
+
+       if (!insert &&
+           unlikely(BCH_ALLOC_V4_NR_BACKPOINTERS(&a->v)) &&
+           bch2_bucket_backpointer_del(trans, a, bp))
+               return 0;
+
+       if (unlikely(bch2_backpointers_no_use_write_buffer))
+               return bch2_bucket_backpointer_mod_nowritebuffer(trans, a, bp, orig_k, insert);
+
+       bp_k = bch2_trans_kmalloc_nomemzero(trans, sizeof(struct bkey_i_backpointer));
+       ret = PTR_ERR_OR_ZERO(bp_k);
+       if (ret)
+               return ret;
+
+       bkey_backpointer_init(&bp_k->k_i);
+       bp_k->k.p = bucket_pos_to_bp(c, a->k.p, bp.bucket_offset);
+       bp_k->v = bp;
+
+       if (!insert) {
+               bp_k->k.type = KEY_TYPE_deleted;
+               set_bkey_val_u64s(&bp_k->k, 0);
+       }
+
+       return bch2_trans_update_buffered(trans, BTREE_ID_backpointers, &bp_k->k_i);
+}
+
 static inline void bch2_extent_ptr_to_bp(struct bch_fs *c,
                           enum btree_id btree_id, unsigned level,
                           struct bkey_s_c k, struct extent_ptr_decoded p,
@@ -40,10 +117,6 @@ static inline void bch2_extent_ptr_to_bp(struct bch_fs *c,
        };
 }
 
-int bch2_bucket_backpointer_del(struct btree_trans *, struct bkey_i_alloc_v4 *,
-                               struct bch_backpointer, struct bkey_s_c);
-int bch2_bucket_backpointer_add(struct btree_trans *, struct bkey_i_alloc_v4 *,
-                               struct bch_backpointer, struct bkey_s_c);
 int bch2_get_next_backpointer(struct btree_trans *, struct bpos, int,
                              u64 *, struct bch_backpointer *, unsigned);
 struct bkey_s_c bch2_backpointer_get_key(struct btree_trans *, struct btree_iter *,
index f513173375803718021a6e65afebcafcae73d790..c8620127f1aef8f3ceb081ad634837d885a2b677 100644 (file)
@@ -316,7 +316,10 @@ do {                                                                       \
                "done in memory")                                       \
        BCH_DEBUG_PARAM(verify_all_btree_replicas,                      \
                "When reading btree nodes, read all replicas and "      \
-               "compare them")
+               "compare them")                                         \
+       BCH_DEBUG_PARAM(backpointers_no_use_write_buffer,               \
+               "Don't use the write buffer for backpointers, enabling "\
+               "extra runtime checks")
 
 /* Parameters that should only be compiled in debug mode: */
 #define BCH_DEBUG_PARAMS_DEBUG()                                       \
@@ -393,6 +396,7 @@ enum bch_time_stats {
 
 #include "alloc_types.h"
 #include "btree_types.h"
+#include "btree_write_buffer_types.h"
 #include "buckets_types.h"
 #include "buckets_waiting_for_journal_types.h"
 #include "clock_types.h"
@@ -581,6 +585,7 @@ struct btree_transaction_stats {
        struct bch2_time_stats  lock_hold_times;
        struct mutex            lock;
        unsigned                nr_max_paths;
+       unsigned                wb_updates_size;
        unsigned                max_mem;
        char                    *max_paths_text;
 };
@@ -775,6 +780,9 @@ struct bch_fs {
        struct workqueue_struct *btree_interior_update_worker;
        struct work_struct      btree_interior_update_work;
 
+       struct list_head        pending_node_rewrites;
+       struct mutex            pending_node_rewrites_lock;
+
        /* btree_io.c: */
        spinlock_t              btree_write_error_lock;
        struct btree_write_stats {
@@ -795,6 +803,8 @@ struct bch_fs {
        struct btree_key_cache  btree_key_cache;
        unsigned                btree_key_cache_btrees;
 
+       struct btree_write_buffer btree_write_buffer;
+
        struct workqueue_struct *btree_update_wq;
        struct workqueue_struct *btree_io_complete_wq;
        /* copygc needs its own workqueue for index updates.. */
index ffd913733e97c37d581e7a84086bdd46252856e2..7574eccd25d4d447ee9500e9f042595fcd2db920 100644 (file)
@@ -1472,7 +1472,8 @@ struct bch_sb_field_disk_groups {
        x(trans_traverse_all,                           71)     \
        x(transaction_commit,                           72)     \
        x(write_super,                                  73)     \
-       x(trans_restart_would_deadlock_recursion_limit, 74)
+       x(trans_restart_would_deadlock_recursion_limit, 74)     \
+       x(trans_restart_write_buffer_flush,             75)
 
 enum bch_persistent_counters {
 #define x(t, n, ...) BCH_COUNTER_##t,
@@ -1707,6 +1708,7 @@ LE64_BITMASK(BCH_SB_JOURNAL_FLUSH_DISABLED,struct bch_sb, flags[3], 62, 63);
 LE64_BITMASK(BCH_SB_JOURNAL_RECLAIM_DELAY,struct bch_sb, flags[4], 0, 32);
 LE64_BITMASK(BCH_SB_JOURNAL_TRANSACTION_NAMES,struct bch_sb, flags[4], 32, 33);
 LE64_BITMASK(BCH_SB_NOCOW,             struct bch_sb, flags[4], 33, 34);
+LE64_BITMASK(BCH_SB_WRITE_BUFFER_SIZE, struct bch_sb, flags[4], 34, 54);
 
 /*
  * Features:
index 19565b94cdefcdbf094baa8f2abcfa4274c19156..f9a3f1312599685f67b3471b43ecfed266d63dc6 100644 (file)
@@ -1246,9 +1246,7 @@ start:
        bio_put(&rb->bio);
        printbuf_exit(&buf);
 
-       if (!btree_node_read_error(b) &&
-           (saw_error ||
-            btree_node_need_rewrite(b))) {
+       if (saw_error && !btree_node_read_error(b)) {
                struct printbuf buf = PRINTBUF;
 
                bch2_bpos_to_text(&buf, b->key.k.p);
index 077d72bfa6826088da1be6ee27a95a7177a388df..dc2b2a0819bd886330b6a910dcc74448c6c2b870 100644 (file)
@@ -1374,6 +1374,7 @@ noinline __cold
 void bch2_trans_updates_to_text(struct printbuf *buf, struct btree_trans *trans)
 {
        struct btree_insert_entry *i;
+       struct btree_write_buffered_key *wb;
 
        prt_printf(buf, "transaction updates for %s journal seq %llu",
               trans->fn, trans->journal_res.seq);
@@ -1398,6 +1399,17 @@ void bch2_trans_updates_to_text(struct printbuf *buf, struct btree_trans *trans)
                prt_newline(buf);
        }
 
+       trans_for_each_wb_update(trans, wb) {
+               prt_printf(buf, "update: btree=%s wb=1 %pS",
+                      bch2_btree_ids[wb->btree],
+                      (void *) i->ip_allocated);
+               prt_newline(buf);
+
+               prt_printf(buf, "  new ");
+               bch2_bkey_val_to_text(buf, trans->c, bkey_i_to_s_c(&wb->k));
+               prt_newline(buf);
+       }
+
        printbuf_indent_sub(buf, 2);
 }
 
@@ -2931,8 +2943,11 @@ void __bch2_trans_init(struct btree_trans *trans, struct bch_fs *c, unsigned fn_
                        trans->mem_bytes = expected_mem_bytes;
                }
        }
-       if (s)
+
+       if (s) {
                trans->nr_max_paths = s->nr_max_paths;
+               trans->wb_updates_size = s->wb_updates_size;
+       }
 
        trans->srcu_idx = srcu_read_lock(&c->btree_trans_barrier);
        trans->srcu_lock_time   = jiffies;
index 23e7f0cad6e3244562e2e053d236062307829db2..ad73cd2e81475dea677a62f0617ab74c996a223d 100644 (file)
@@ -9,6 +9,7 @@
 //#include "bkey_methods.h"
 #include "buckets_types.h"
 #include "darray.h"
+#include "errcode.h"
 #include "journal_types.h"
 #include "replicas_types.h"
 
@@ -429,6 +430,8 @@ struct btree_trans {
        u8                      fn_idx;
        u8                      nr_sorted;
        u8                      nr_updates;
+       u8                      nr_wb_updates;
+       u8                      wb_updates_size;
        bool                    used_mempool:1;
        bool                    in_traverse_all:1;
        bool                    paths_sorted:1;
@@ -459,6 +462,7 @@ struct btree_trans {
        u8                      sorted[BTREE_ITER_MAX + 8];
        struct btree_path       *paths;
        struct btree_insert_entry *updates;
+       struct btree_write_buffered_key *wb_updates;
 
        /* update path: */
        struct btree_trans_commit_hook *hooks;
index 673c3a78aae258cbb7fe9e32d8fca8e86ba0750f..771e4b239c66b234883363c1d222662fdbc2948b 100644 (file)
@@ -15,6 +15,9 @@ bool bch2_btree_bset_insert_key(struct btree_trans *, struct btree_path *,
                                struct bkey_i *);
 void bch2_btree_add_journal_pin(struct bch_fs *, struct btree *, u64);
 
+void bch2_btree_insert_key_leaf(struct btree_trans *, struct btree_path *,
+                               struct bkey_i *, u64);
+
 enum btree_insert_flags {
        /* First two bits for journal watermark: */
        __BTREE_INSERT_NOFAIL = 2,
@@ -55,7 +58,8 @@ int bch2_btree_delete_extent_at(struct btree_trans *, struct btree_iter *,
                                unsigned, unsigned);
 int bch2_btree_delete_at(struct btree_trans *, struct btree_iter *, unsigned);
 
-int __bch2_btree_insert(struct btree_trans *, enum btree_id, struct bkey_i *);
+int __bch2_btree_insert(struct btree_trans *, enum btree_id, struct bkey_i *,
+                       enum btree_update_flags);
 int bch2_btree_insert(struct bch_fs *, enum btree_id, struct bkey_i *,
                     struct disk_reservation *, u64 *, int flags);
 
@@ -77,6 +81,8 @@ int bch2_trans_update_extent(struct btree_trans *, struct btree_iter *,
 
 int __must_check bch2_trans_update(struct btree_trans *, struct btree_iter *,
                                   struct bkey_i *, enum btree_update_flags);
+int __must_check bch2_trans_update_buffered(struct btree_trans *,
+                                           enum btree_id, struct bkey_i *);
 
 void bch2_trans_commit_hook(struct btree_trans *,
                            struct btree_trans_commit_hook *);
@@ -142,6 +148,11 @@ static inline int bch2_trans_commit(struct btree_trans *trans,
             (_i) < (_trans)->updates + (_trans)->nr_updates;           \
             (_i)++)
 
+#define trans_for_each_wb_update(_trans, _i)                           \
+       for ((_i) = (_trans)->wb_updates;                               \
+            (_i) < (_trans)->wb_updates + (_trans)->nr_wb_updates;     \
+            (_i)++)
+
 static inline void bch2_trans_reset_updates(struct btree_trans *trans)
 {
        struct btree_insert_entry *i;
@@ -151,6 +162,8 @@ static inline void bch2_trans_reset_updates(struct btree_trans *trans)
 
        trans->extra_journal_res        = 0;
        trans->nr_updates               = 0;
+       trans->nr_wb_updates            = 0;
+       trans->wb_updates               = NULL;
        trans->hooks                    = NULL;
        trans->extra_journal_entries.nr = 0;
 
index a40a0f5ca4bdb5d632eb1841fc3d17016042f685..172fd3b6cfc258eabd1d3bcf9c454fb0f361590f 100644 (file)
@@ -1997,6 +1997,7 @@ err:
 struct async_btree_rewrite {
        struct bch_fs           *c;
        struct work_struct      work;
+       struct list_head        list;
        enum btree_id           btree_id;
        unsigned                level;
        struct bpos             pos;
@@ -2056,15 +2057,10 @@ void async_btree_node_rewrite_work(struct work_struct *work)
 void bch2_btree_node_rewrite_async(struct bch_fs *c, struct btree *b)
 {
        struct async_btree_rewrite *a;
-
-       if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_node_rewrite)) {
-               bch_err(c, "%s: error getting c->writes ref", __func__);
-               return;
-       }
+       int ret;
 
        a = kmalloc(sizeof(*a), GFP_NOFS);
        if (!a) {
-               bch2_write_ref_put(c, BCH_WRITE_REF_node_rewrite);
                bch_err(c, "%s: error allocating memory", __func__);
                return;
        }
@@ -2074,11 +2070,63 @@ void bch2_btree_node_rewrite_async(struct bch_fs *c, struct btree *b)
        a->level        = b->c.level;
        a->pos          = b->key.k.p;
        a->seq          = b->data->keys.seq;
-
        INIT_WORK(&a->work, async_btree_node_rewrite_work);
+
+       if (unlikely(!test_bit(BCH_FS_MAY_GO_RW, &c->flags))) {
+               mutex_lock(&c->pending_node_rewrites_lock);
+               list_add(&a->list, &c->pending_node_rewrites);
+               mutex_unlock(&c->pending_node_rewrites_lock);
+               return;
+       }
+
+       if (!bch2_write_ref_tryget(c, BCH_WRITE_REF_node_rewrite)) {
+               if (test_bit(BCH_FS_STARTED, &c->flags)) {
+                       bch_err(c, "%s: error getting c->writes ref", __func__);
+                       kfree(a);
+                       return;
+               }
+
+               ret = bch2_fs_read_write_early(c);
+               if (ret) {
+                       bch_err(c, "%s: error going read-write: %s",
+                               __func__, bch2_err_str(ret));
+                       kfree(a);
+                       return;
+               }
+
+               bch2_write_ref_get(c, BCH_WRITE_REF_node_rewrite);
+       }
+
        queue_work(c->btree_interior_update_worker, &a->work);
 }
 
+void bch2_do_pending_node_rewrites(struct bch_fs *c)
+{
+       struct async_btree_rewrite *a, *n;
+
+       mutex_lock(&c->pending_node_rewrites_lock);
+       list_for_each_entry_safe(a, n, &c->pending_node_rewrites, list) {
+               list_del(&a->list);
+
+               bch2_write_ref_get(c, BCH_WRITE_REF_node_rewrite);
+               queue_work(c->btree_interior_update_worker, &a->work);
+       }
+       mutex_unlock(&c->pending_node_rewrites_lock);
+}
+
+void bch2_free_pending_node_rewrites(struct bch_fs *c)
+{
+       struct async_btree_rewrite *a, *n;
+
+       mutex_lock(&c->pending_node_rewrites_lock);
+       list_for_each_entry_safe(a, n, &c->pending_node_rewrites, list) {
+               list_del(&a->list);
+
+               kfree(a);
+       }
+       mutex_unlock(&c->pending_node_rewrites_lock);
+}
+
 static int __bch2_btree_node_update_key(struct btree_trans *trans,
                                        struct btree_iter *iter,
                                        struct btree *b, struct btree *new_hash,
@@ -2416,6 +2464,9 @@ int bch2_fs_btree_interior_update_init(struct bch_fs *c)
        mutex_init(&c->btree_interior_update_lock);
        INIT_WORK(&c->btree_interior_update_work, btree_interior_update_work);
 
+       INIT_LIST_HEAD(&c->pending_node_rewrites);
+       mutex_init(&c->pending_node_rewrites_lock);
+
        c->btree_interior_update_worker =
                alloc_workqueue("btree_update", WQ_UNBOUND|WQ_MEM_RECLAIM, 1);
        if (!c->btree_interior_update_worker)
index 2e6d220c3bcd6e005889b22a60361fb34f32c1aa..30e9c137b0e27bde3e846dd8188ed143af72e10b 100644 (file)
@@ -318,6 +318,9 @@ void bch2_journal_entries_to_btree_roots(struct bch_fs *, struct jset *);
 struct jset_entry *bch2_btree_roots_to_journal_entries(struct bch_fs *,
                                        struct jset_entry *, struct jset_entry *);
 
+void bch2_do_pending_node_rewrites(struct bch_fs *);
+void bch2_free_pending_node_rewrites(struct bch_fs *);
+
 void bch2_fs_btree_interior_update_exit(struct bch_fs *);
 int bch2_fs_btree_interior_update_init(struct bch_fs *);
 
index 6cbc0e5e1fe819a703625fc7563823ee88201c6c..0195b13de1523ade6f91b528ad77a0b88ca013c8 100644 (file)
@@ -8,6 +8,7 @@
 #include "btree_iter.h"
 #include "btree_key_cache.h"
 #include "btree_locking.h"
+#include "btree_write_buffer.h"
 #include "buckets.h"
 #include "debug.h"
 #include "errcode.h"
@@ -100,9 +101,6 @@ inline void bch2_btree_node_prep_for_write(struct btree_trans *trans,
 {
        struct bch_fs *c = trans->c;
 
-       if (path->cached)
-               return;
-
        if (unlikely(btree_node_just_written(b)) &&
            bch2_btree_post_write_cleanup(c, b))
                bch2_trans_node_reinit_iter(trans, b);
@@ -252,25 +250,26 @@ inline void bch2_btree_add_journal_pin(struct bch_fs *c,
 /**
  * btree_insert_key - insert a key one key into a leaf node
  */
-static void btree_insert_key_leaf(struct btree_trans *trans,
-                                 struct btree_insert_entry *insert)
+inline void bch2_btree_insert_key_leaf(struct btree_trans *trans,
+                                      struct btree_path *path,
+                                      struct bkey_i *insert,
+                                      u64 journal_seq)
 {
        struct bch_fs *c = trans->c;
-       struct btree *b = insert_l(insert)->b;
+       struct btree *b = path_l(path)->b;
        struct bset_tree *t = bset_tree_last(b);
        struct bset *i = bset(b, t);
        int old_u64s = bset_u64s(t);
        int old_live_u64s = b->nr.live_u64s;
        int live_u64s_added, u64s_added;
 
-       if (unlikely(!bch2_btree_bset_insert_key(trans, insert->path, b,
-                                       &insert_l(insert)->iter, insert->k)))
+       if (unlikely(!bch2_btree_bset_insert_key(trans, path, b,
+                                       &path_l(path)->iter, insert)))
                return;
 
-       i->journal_seq = cpu_to_le64(max(trans->journal_res.seq,
-                                        le64_to_cpu(i->journal_seq)));
+       i->journal_seq = cpu_to_le64(max(journal_seq, le64_to_cpu(i->journal_seq)));
 
-       bch2_btree_add_journal_pin(c, b, trans->journal_res.seq);
+       bch2_btree_add_journal_pin(c, b, journal_seq);
 
        if (unlikely(!btree_node_dirty(b)))
                set_btree_node_dirty_acct(c, b);
@@ -288,6 +287,12 @@ static void btree_insert_key_leaf(struct btree_trans *trans,
                bch2_trans_node_reinit_iter(trans, b);
 }
 
+static void btree_insert_key_leaf(struct btree_trans *trans,
+                                 struct btree_insert_entry *insert)
+{
+       bch2_btree_insert_key_leaf(trans, insert->path, insert->k, trans->journal_res.seq);
+}
+
 /* Cached btree updates: */
 
 /* Normal update interface: */
@@ -594,6 +599,7 @@ bch2_trans_commit_write_locked(struct btree_trans *trans, unsigned flags,
 {
        struct bch_fs *c = trans->c;
        struct btree_insert_entry *i;
+       struct btree_write_buffered_key *wb;
        struct btree_trans_commit_hook *h;
        unsigned u64s = 0;
        bool marking = false;
@@ -638,6 +644,10 @@ bch2_trans_commit_write_locked(struct btree_trans *trans, unsigned flags,
                        marking = true;
        }
 
+       if (trans->nr_wb_updates &&
+           trans->nr_wb_updates + c->btree_write_buffer.state.nr > c->btree_write_buffer.size)
+               return -BCH_ERR_btree_insert_need_flush_buffer;
+
        /*
         * Don't get journal reservation until after we know insert will
         * succeed:
@@ -674,17 +684,25 @@ bch2_trans_commit_write_locked(struct btree_trans *trans, unsigned flags,
            bch2_trans_fs_usage_apply(trans, trans->fs_usage_deltas))
                return -BCH_ERR_btree_insert_need_mark_replicas;
 
+       if (trans->nr_wb_updates) {
+               EBUG_ON(flags & BTREE_INSERT_JOURNAL_REPLAY);
+
+               ret = bch2_btree_insert_keys_write_buffer(trans);
+               if (ret)
+                       goto revert_fs_usage;
+       }
+
        trans_for_each_update(trans, i)
                if (BTREE_NODE_TYPE_HAS_MEM_TRIGGERS & (1U << i->bkey_type)) {
                        ret = run_one_mem_trigger(trans, i, i->flags);
                        if (ret)
-                               return ret;
+                               goto fatal_err;
                }
 
        if (unlikely(c->gc_pos.phase)) {
                ret = bch2_trans_commit_run_gc_triggers(trans);
                if  (ret)
-                       return ret;
+                       goto fatal_err;
        }
 
        if (unlikely(trans->extra_journal_entries.nr)) {
@@ -697,10 +715,10 @@ bch2_trans_commit_write_locked(struct btree_trans *trans, unsigned flags,
        }
 
        if (likely(!(flags & BTREE_INSERT_JOURNAL_REPLAY))) {
-               trans_for_each_update(trans, i) {
-                       struct journal *j = &c->journal;
-                       struct jset_entry *entry;
+               struct journal *j = &c->journal;
+               struct jset_entry *entry;
 
+               trans_for_each_update(trans, i) {
                        if (i->key_cache_already_flushed)
                                continue;
 
@@ -725,6 +743,14 @@ bch2_trans_commit_write_locked(struct btree_trans *trans, unsigned flags,
                        bkey_copy(&entry->start[0], i->k);
                }
 
+               trans_for_each_wb_update(trans, wb) {
+                       entry = bch2_journal_add_entry(j, &trans->journal_res,
+                                              BCH_JSET_ENTRY_btree_keys,
+                                              wb->btree, 0,
+                                              wb->k.k.u64s);
+                       bkey_copy(&entry->start[0], &wb->k);
+               }
+
                if (trans->journal_seq)
                        *trans->journal_seq = trans->journal_res.seq;
        }
@@ -742,6 +768,12 @@ bch2_trans_commit_write_locked(struct btree_trans *trans, unsigned flags,
                }
        }
 
+       return 0;
+fatal_err:
+       bch2_fatal_error(c);
+revert_fs_usage:
+       if (trans->fs_usage_deltas)
+               bch2_trans_fs_usage_revert(trans, trans->fs_usage_deltas);
        return ret;
 }
 
@@ -769,7 +801,8 @@ static inline int trans_lock_write(struct btree_trans *trans)
                if (bch2_btree_node_lock_write(trans, i->path, &insert_l(i)->b->c))
                        return trans_lock_write_fail(trans, i);
 
-               bch2_btree_node_prep_for_write(trans, i->path, insert_l(i)->b);
+               if (!i->cached)
+                       bch2_btree_node_prep_for_write(trans, i->path, insert_l(i)->b);
        }
 
        return 0;
@@ -778,9 +811,13 @@ static inline int trans_lock_write(struct btree_trans *trans)
 static noinline void bch2_drop_overwrites_from_journal(struct btree_trans *trans)
 {
        struct btree_insert_entry *i;
+       struct btree_write_buffered_key *wb;
 
        trans_for_each_update(trans, i)
                bch2_journal_key_overwritten(trans->c, i->btree_id, i->level, i->k->k.p);
+
+       trans_for_each_wb_update(trans, wb)
+               bch2_journal_key_overwritten(trans->c, wb->btree, 0, wb->k.k.p);
 }
 
 #ifdef CONFIG_BCACHEFS_DEBUG
@@ -821,10 +858,11 @@ static inline int do_bch2_trans_commit(struct btree_trans *trans, unsigned flags
 {
        struct bch_fs *c = trans->c;
        struct btree_insert_entry *i;
-       struct printbuf buf = PRINTBUF;
        int ret, u64s_delta = 0;
 
 #ifdef CONFIG_BCACHEFS_DEBUG
+       struct printbuf buf = PRINTBUF;
+
        trans_for_each_update(trans, i) {
                int rw = (flags & BTREE_INSERT_JOURNAL_REPLAY) ? READ : WRITE;
 
@@ -833,8 +871,8 @@ static inline int do_bch2_trans_commit(struct btree_trans *trans, unsigned flags
                        return bch2_trans_commit_bkey_invalid(trans, flags, i, &buf);
                btree_insert_entry_checks(trans, i);
        }
-#endif
        printbuf_exit(&buf);
+#endif
 
        trans_for_each_update(trans, i) {
                if (i->cached)
@@ -962,6 +1000,30 @@ int bch2_trans_commit_error(struct btree_trans *trans, unsigned flags,
                if (ret)
                        trace_and_count(c, trans_restart_journal_reclaim, trans, trace_ip);
                break;
+       case -BCH_ERR_btree_insert_need_flush_buffer: {
+               struct btree_write_buffer *wb = &c->btree_write_buffer;
+
+               ret = 0;
+
+               if (wb->state.nr > wb->size * 3 / 4) {
+                       bch2_trans_reset_updates(trans);
+                       bch2_trans_unlock(trans);
+
+                       mutex_lock(&wb->flush_lock);
+
+                       if (wb->state.nr > wb->size * 3 / 4)
+                               ret = __bch2_btree_write_buffer_flush(trans,
+                                               flags|BTREE_INSERT_NOCHECK_RW, true);
+                       else
+                               mutex_unlock(&wb->flush_lock);
+
+                       if (!ret) {
+                               trace_and_count(c, trans_restart_write_buffer_flush, trans, _THIS_IP_);
+                               ret = btree_trans_restart(trans, BCH_ERR_transaction_restart_write_buffer_flush);
+                       }
+               }
+               break;
+       }
        default:
                BUG_ON(ret >= 0);
                break;
@@ -1023,10 +1085,12 @@ int __bch2_trans_commit(struct btree_trans *trans, unsigned flags)
 {
        struct bch_fs *c = trans->c;
        struct btree_insert_entry *i = NULL;
+       struct btree_write_buffered_key *wb;
        unsigned u64s;
        int ret = 0;
 
        if (!trans->nr_updates &&
+           !trans->nr_wb_updates &&
            !trans->extra_journal_entries.nr)
                goto out_reset;
 
@@ -1049,6 +1113,20 @@ int __bch2_trans_commit(struct btree_trans *trans, unsigned flags)
                        goto out_reset;
        }
 
+       if (c->btree_write_buffer.state.nr > c->btree_write_buffer.size / 2 &&
+           mutex_trylock(&c->btree_write_buffer.flush_lock)) {
+               bch2_trans_begin(trans);
+               bch2_trans_unlock(trans);
+
+               ret = __bch2_btree_write_buffer_flush(trans,
+                                       flags|BTREE_INSERT_NOCHECK_RW, true);
+               if (!ret) {
+                       trace_and_count(c, trans_restart_write_buffer_flush, trans, _THIS_IP_);
+                       ret = btree_trans_restart(trans, BCH_ERR_transaction_restart_write_buffer_flush);
+               }
+               goto out;
+       }
+
        EBUG_ON(test_bit(BCH_FS_CLEAN_SHUTDOWN, &c->flags));
 
        memset(&trans->journal_preres, 0, sizeof(trans->journal_preres));
@@ -1089,6 +1167,9 @@ int __bch2_trans_commit(struct btree_trans *trans, unsigned flags)
                        trans->journal_u64s += jset_u64s(i->old_k.u64s);
        }
 
+       trans_for_each_wb_update(trans, wb)
+               trans->journal_u64s += jset_u64s(wb->k.k.u64s);
+
        if (trans->extra_journal_res) {
                ret = bch2_disk_reservation_add(c, trans->disk_res,
                                trans->extra_journal_res,
@@ -1606,6 +1687,59 @@ int __must_check bch2_trans_update(struct btree_trans *trans, struct btree_iter
        return bch2_trans_update_by_path(trans, path, k, flags);
 }
 
+int __must_check bch2_trans_update_buffered(struct btree_trans *trans,
+                                           enum btree_id btree,
+                                           struct bkey_i *k)
+{
+       struct btree_write_buffered_key *i;
+       int ret;
+
+       EBUG_ON(trans->nr_wb_updates > trans->wb_updates_size);
+       EBUG_ON(k->k.u64s > BTREE_WRITE_BUFERED_U64s_MAX);
+
+       trans_for_each_wb_update(trans, i) {
+               if (i->btree == btree && bpos_eq(i->k.k.p, k->k.p)) {
+                       bkey_copy(&i->k, k);
+                       return 0;
+               }
+       }
+
+       if (!trans->wb_updates ||
+           trans->nr_wb_updates == trans->wb_updates_size) {
+               struct btree_write_buffered_key *u;
+
+               if (trans->nr_wb_updates == trans->wb_updates_size) {
+                       struct btree_transaction_stats *s = btree_trans_stats(trans);
+
+                       BUG_ON(trans->wb_updates_size > U8_MAX / 2);
+                       trans->wb_updates_size = max(1, trans->wb_updates_size * 2);
+                       if (s)
+                               s->wb_updates_size = trans->wb_updates_size;
+               }
+
+               u = bch2_trans_kmalloc_nomemzero(trans,
+                                       trans->wb_updates_size *
+                                       sizeof(struct btree_write_buffered_key));
+               ret = PTR_ERR_OR_ZERO(u);
+               if (ret)
+                       return ret;
+
+               if (trans->nr_wb_updates)
+                       memcpy(u, trans->wb_updates, trans->nr_wb_updates *
+                              sizeof(struct btree_write_buffered_key));
+               trans->wb_updates = u;
+       }
+
+       trans->wb_updates[trans->nr_wb_updates] = (struct btree_write_buffered_key) {
+               .btree  = btree,
+       };
+
+       bkey_copy(&trans->wb_updates[trans->nr_wb_updates].k, k);
+       trans->nr_wb_updates++;
+
+       return 0;
+}
+
 void bch2_trans_commit_hook(struct btree_trans *trans,
                            struct btree_trans_commit_hook *h)
 {
@@ -1614,7 +1748,8 @@ void bch2_trans_commit_hook(struct btree_trans *trans,
 }
 
 int __bch2_btree_insert(struct btree_trans *trans,
-                       enum btree_id id, struct bkey_i *k)
+                       enum btree_id id,
+                       struct bkey_i *k, enum btree_update_flags flags)
 {
        struct btree_iter iter;
        int ret;
@@ -1622,7 +1757,7 @@ int __bch2_btree_insert(struct btree_trans *trans,
        bch2_trans_iter_init(trans, &iter, id, bkey_start_pos(&k->k),
                             BTREE_ITER_INTENT);
        ret   = bch2_btree_iter_traverse(&iter) ?:
-               bch2_trans_update(trans, &iter, k, 0);
+               bch2_trans_update(trans, &iter, k, flags);
        bch2_trans_iter_exit(trans, &iter);
        return ret;
 }
@@ -1640,7 +1775,7 @@ int bch2_btree_insert(struct bch_fs *c, enum btree_id id,
                      u64 *journal_seq, int flags)
 {
        return bch2_trans_do(c, disk_res, journal_seq, flags,
-                            __bch2_btree_insert(&trans, id, k));
+                            __bch2_btree_insert(&trans, id, k, 0));
 }
 
 int bch2_btree_delete_extent_at(struct btree_trans *trans, struct btree_iter *iter,
diff --git a/libbcachefs/btree_write_buffer.c b/libbcachefs/btree_write_buffer.c
new file mode 100644 (file)
index 0000000..05b755a
--- /dev/null
@@ -0,0 +1,328 @@
+// 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_reclaim.h"
+
+#include <linux/sort.h>
+
+static int btree_write_buffered_key_cmp(const void *_l, const void *_r)
+{
+       const struct btree_write_buffered_key *l = _l;
+       const struct btree_write_buffered_key *r = _r;
+
+       return  cmp_int(l->btree, r->btree) ?:
+               bpos_cmp(l->k.k.p, r->k.k.p) ?:
+               cmp_int(l->journal_seq, r->journal_seq) ?:
+               cmp_int(l->journal_offset, r->journal_offset);
+}
+
+static int btree_write_buffered_journal_cmp(const void *_l, const void *_r)
+{
+       const struct btree_write_buffered_key *l = _l;
+       const struct btree_write_buffered_key *r = _r;
+
+       return  cmp_int(l->journal_seq, r->journal_seq);
+}
+
+static int bch2_btree_write_buffer_flush_one(struct btree_trans *trans,
+                                            struct btree_iter *iter,
+                                            struct btree_write_buffered_key *wb,
+                                            unsigned commit_flags,
+                                            bool *write_locked,
+                                            size_t *fast)
+{
+       struct bch_fs *c = trans->c;
+       struct btree_path *path;
+       int ret;
+
+       ret = bch2_btree_iter_traverse(iter);
+       if (ret)
+               return ret;
+
+       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 (!bch2_btree_node_insert_fits(c, path->l[0].b, wb->k.k.u64s)) {
+               bch2_btree_node_unlock_write(trans, path, path->l[0].b);
+               *write_locked = false;
+               goto trans_commit;
+       }
+
+       bch2_btree_insert_key_leaf(trans, path, &wb->k, wb->journal_seq);
+       (*fast)++;
+       return 0;
+trans_commit:
+       return  bch2_trans_update(trans, iter, &wb->k, 0) ?:
+               bch2_trans_commit(trans, NULL, NULL,
+                                 commit_flags|
+                                 BTREE_INSERT_NOFAIL|
+                                 BTREE_INSERT_JOURNAL_RECLAIM);
+}
+
+static union btree_write_buffer_state btree_write_buffer_switch(struct btree_write_buffer *wb)
+{
+       union btree_write_buffer_state old, new;
+       u64 v = READ_ONCE(wb->state.v);
+
+       do {
+               old.v = new.v = v;
+
+               new.nr = 0;
+               new.idx++;
+       } while ((v = atomic64_cmpxchg_acquire(&wb->state.counter, old.v, new.v)) != old.v);
+
+       while (old.idx == 0 ? wb->state.ref0 : wb->state.ref1)
+               cpu_relax();
+
+       return old;
+}
+
+int __bch2_btree_write_buffer_flush(struct btree_trans *trans, unsigned commit_flags,
+                                   bool locked)
+{
+       struct bch_fs *c = trans->c;
+       struct journal *j = &c->journal;
+       struct btree_write_buffer *wb = &c->btree_write_buffer;
+       struct journal_entry_pin pin;
+       struct btree_write_buffered_key *i, *dst, *keys;
+       struct btree_iter iter = { NULL };
+       size_t nr = 0, skipped = 0, fast = 0;
+       bool write_locked = false;
+       union btree_write_buffer_state s;
+       int ret = 0;
+
+       memset(&pin, 0, sizeof(pin));
+
+       if (!locked && !mutex_trylock(&wb->flush_lock))
+               return 0;
+
+       bch2_journal_pin_copy(j, &pin, &wb->journal_pin, NULL);
+       bch2_journal_pin_drop(j, &wb->journal_pin);
+
+       s = btree_write_buffer_switch(wb);
+       keys = wb->keys[s.idx];
+       nr = s.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 BTREE_INSERT_JORUNAL_RECLAIM. This causes the update to fail
+        * if it would block taking a journal reservation.
+        *
+        * If that happens, we sort them by the order they appeared in the
+        * journal - after dropping redundant entries - and then restart
+        * flushing, this time dropping journal pins as we go.
+        */
+
+       sort(keys, nr, sizeof(keys[0]),
+            btree_write_buffered_key_cmp, NULL);
+
+       for (i = keys; i < keys + nr; i++) {
+               if (i + 1 < keys + nr &&
+                   i[0].btree == i[1].btree &&
+                   bpos_eq(i[0].k.k.p, i[1].k.k.p)) {
+                       skipped++;
+                       continue;
+               }
+
+               if (write_locked &&
+                   (iter.path->btree_id != i->btree ||
+                    bpos_gt(i->k.k.p, iter.path->l[0].b->key.k.p))) {
+                       bch2_btree_node_unlock_write(trans, iter.path, iter.path->l[0].b);
+                       write_locked = false;
+               }
+
+               if (!iter.path || iter.path->btree_id != i->btree) {
+                       bch2_trans_iter_exit(trans, &iter);
+                       bch2_trans_iter_init(trans, &iter, i->btree, i->k.k.p, BTREE_ITER_INTENT);
+               }
+
+               bch2_btree_iter_set_pos(&iter, i->k.k.p);
+               iter.path->preserve = false;
+
+               do {
+                       ret = bch2_btree_write_buffer_flush_one(trans, &iter, i,
+                                               commit_flags, &write_locked, &fast);
+                       if (!write_locked)
+                               bch2_trans_begin(trans);
+               } while (bch2_err_matches(ret, BCH_ERR_transaction_restart));
+
+               if (ret)
+                       break;
+       }
+
+       if (write_locked)
+               bch2_btree_node_unlock_write(trans, iter.path, iter.path->l[0].b);
+       bch2_trans_iter_exit(trans, &iter);
+
+       trace_write_buffer_flush(trans, nr, skipped, fast, wb->size);
+
+       if (ret == -BCH_ERR_journal_reclaim_would_deadlock)
+               goto slowpath;
+
+       bch2_fs_fatal_err_on(ret, c, "%s: insert error %s", __func__, bch2_err_str(ret));
+out:
+       bch2_journal_pin_drop(j, &pin);
+       mutex_unlock(&wb->flush_lock);
+       return ret;
+slowpath:
+       trace_write_buffer_flush_slowpath(trans, i - keys, nr);
+
+       dst = keys;
+       for (; i < keys + nr; i++) {
+               if (i + 1 < keys + nr &&
+                   i[0].btree == i[1].btree &&
+                   bpos_eq(i[0].k.k.p, i[1].k.k.p))
+                       continue;
+
+               *dst = *i;
+               dst++;
+       }
+       nr = dst - keys;
+
+       sort(keys, nr, sizeof(keys[0]),
+            btree_write_buffered_journal_cmp,
+            NULL);
+
+       for (i = keys; i < keys + nr; i++) {
+               if (i->journal_seq > pin.seq) {
+                       struct journal_entry_pin pin2;
+
+                       memset(&pin2, 0, sizeof(pin2));
+
+                       bch2_journal_pin_add(j, i->journal_seq, &pin2, NULL);
+                       bch2_journal_pin_drop(j, &pin);
+                       bch2_journal_pin_copy(j, &pin, &pin2, NULL);
+                       bch2_journal_pin_drop(j, &pin2);
+               }
+
+               ret = commit_do(trans, NULL, NULL,
+                               commit_flags|
+                               BTREE_INSERT_NOFAIL|
+                               BTREE_INSERT_JOURNAL_RECLAIM|
+                               JOURNAL_WATERMARK_reserved,
+                               __bch2_btree_insert(trans, i->btree, &i->k, 0));
+               if (bch2_fs_fatal_err_on(ret, c, "%s: insert error %s", __func__, bch2_err_str(ret)))
+                       break;
+       }
+
+       goto out;
+}
+
+int bch2_btree_write_buffer_flush_sync(struct btree_trans *trans)
+{
+       bch2_trans_unlock(trans);
+       mutex_lock(&trans->c->btree_write_buffer.flush_lock);
+       return __bch2_btree_write_buffer_flush(trans, 0, true);
+}
+
+int bch2_btree_write_buffer_flush(struct btree_trans *trans)
+{
+       return __bch2_btree_write_buffer_flush(trans, 0, false);
+}
+
+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);
+       struct btree_write_buffer *wb = &c->btree_write_buffer;
+
+       mutex_lock(&wb->flush_lock);
+
+       return bch2_trans_run(c,
+                       __bch2_btree_write_buffer_flush(&trans, BTREE_INSERT_NOCHECK_RW, true));
+}
+
+static inline u64 btree_write_buffer_ref(int idx)
+{
+       return ((union btree_write_buffer_state) {
+               .ref0 = idx == 0,
+               .ref1 = idx == 1,
+       }).v;
+}
+
+int bch2_btree_insert_keys_write_buffer(struct btree_trans *trans)
+{
+       struct bch_fs *c = trans->c;
+       struct btree_write_buffer *wb = &c->btree_write_buffer;
+       struct btree_write_buffered_key *i;
+       union btree_write_buffer_state old, new;
+       int ret = 0;
+       u64 v;
+
+       trans_for_each_wb_update(trans, i) {
+               EBUG_ON(i->k.k.u64s > BTREE_WRITE_BUFERED_U64s_MAX);
+
+               i->journal_seq          = trans->journal_res.seq;
+               i->journal_offset       = trans->journal_res.offset;
+       }
+
+       preempt_disable();
+       v = READ_ONCE(wb->state.v);
+       do {
+               old.v = new.v = v;
+
+               new.v += btree_write_buffer_ref(new.idx);
+               new.nr += trans->nr_wb_updates;
+               if (new.nr > wb->size) {
+                       ret = -BCH_ERR_btree_insert_need_flush_buffer;
+                       goto out;
+               }
+       } while ((v = atomic64_cmpxchg_acquire(&wb->state.counter, old.v, new.v)) != old.v);
+
+       memcpy(wb->keys[new.idx] + old.nr,
+              trans->wb_updates,
+              sizeof(trans->wb_updates[0]) * trans->nr_wb_updates);
+
+       bch2_journal_pin_add(&c->journal, trans->journal_res.seq, &wb->journal_pin,
+                            bch2_btree_write_buffer_journal_flush);
+
+       atomic64_sub_return_release(btree_write_buffer_ref(new.idx), &wb->state.counter);
+out:
+       preempt_enable();
+       return ret;
+}
+
+void bch2_fs_btree_write_buffer_exit(struct bch_fs *c)
+{
+       struct btree_write_buffer *wb = &c->btree_write_buffer;
+
+       BUG_ON(wb->state.nr && !bch2_journal_error(&c->journal));
+
+       kvfree(wb->keys[1]);
+       kvfree(wb->keys[0]);
+}
+
+int bch2_fs_btree_write_buffer_init(struct bch_fs *c)
+{
+       struct btree_write_buffer *wb = &c->btree_write_buffer;
+
+       mutex_init(&wb->flush_lock);
+       wb->size = c->opts.btree_write_buffer_size;
+
+       wb->keys[0] = kvmalloc_array(wb->size, sizeof(*wb->keys[0]), GFP_KERNEL);
+       wb->keys[1] = kvmalloc_array(wb->size, sizeof(*wb->keys[1]), GFP_KERNEL);
+       if (!wb->keys[0] || !wb->keys[1])
+               return -ENOMEM;
+
+       return 0;
+}
diff --git a/libbcachefs/btree_write_buffer.h b/libbcachefs/btree_write_buffer.h
new file mode 100644 (file)
index 0000000..322df1c
--- /dev/null
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _BCACHEFS_BTREE_WRITE_BUFFER_H
+#define _BCACHEFS_BTREE_WRITE_BUFFER_H
+
+int __bch2_btree_write_buffer_flush(struct btree_trans *, unsigned, bool);
+int bch2_btree_write_buffer_flush_sync(struct btree_trans *);
+int bch2_btree_write_buffer_flush(struct btree_trans *);
+
+int bch2_btree_insert_keys_write_buffer(struct btree_trans *);
+
+void bch2_fs_btree_write_buffer_exit(struct bch_fs *);
+int bch2_fs_btree_write_buffer_init(struct bch_fs *);
+
+#endif /* _BCACHEFS_BTREE_WRITE_BUFFER_H */
diff --git a/libbcachefs/btree_write_buffer_types.h b/libbcachefs/btree_write_buffer_types.h
new file mode 100644 (file)
index 0000000..99993ba
--- /dev/null
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _BCACHEFS_BTREE_WRITE_BUFFER_TYPES_H
+#define _BCACHEFS_BTREE_WRITE_BUFFER_TYPES_H
+
+#include "journal_types.h"
+
+#define BTREE_WRITE_BUFERED_VAL_U64s_MAX       4
+#define BTREE_WRITE_BUFERED_U64s_MAX   (BKEY_U64s + BTREE_WRITE_BUFERED_VAL_U64s_MAX)
+
+struct btree_write_buffered_key {
+       u64                     journal_seq;
+       unsigned                journal_offset;
+       enum btree_id           btree;
+       __BKEY_PADDED(k, BTREE_WRITE_BUFERED_VAL_U64s_MAX);
+};
+
+union btree_write_buffer_state {
+       struct {
+               atomic64_t      counter;
+       };
+
+       struct {
+               u64             v;
+       };
+
+       struct {
+               u64                     nr:23;
+               u64                     idx:1;
+               u64                     ref0:20;
+               u64                     ref1:20;
+       };
+};
+
+struct btree_write_buffer {
+       struct mutex                    flush_lock;
+       struct journal_entry_pin        journal_pin;
+
+       union btree_write_buffer_state  state;
+       size_t                          size;
+
+       struct btree_write_buffered_key *keys[2];
+};
+
+#endif /* _BCACHEFS_BTREE_WRITE_BUFFER_TYPES_H */
index ac0328c4f2c18c6c7b22641cf1f3a45267608385..ebfbfd97b0a396d21425b9a4ba3470f75cf4ceda 100644 (file)
@@ -663,13 +663,14 @@ err:
        return ret;
 }
 
-static int check_bucket_ref(struct bch_fs *c,
+static int check_bucket_ref(struct btree_trans *trans,
                            struct bkey_s_c k,
                            const struct bch_extent_ptr *ptr,
                            s64 sectors, enum bch_data_type ptr_data_type,
                            u8 b_gen, u8 bucket_data_type,
                            u32 dirty_sectors, u32 cached_sectors)
 {
+       struct bch_fs *c = trans->c;
        struct bch_dev *ca = bch_dev_bkey_exists(c, ptr->dev);
        size_t bucket_nr = PTR_BUCKET_NR(ca, ptr);
        u16 bucket_sectors = !ptr->cached
@@ -756,9 +757,12 @@ static int check_bucket_ref(struct bch_fs *c,
                ret = -EIO;
                goto err;
        }
-err:
+out:
        printbuf_exit(&buf);
        return ret;
+err:
+       bch2_dump_trans_updates(trans);
+       goto out;
 }
 
 static int mark_stripe_bucket(struct btree_trans *trans,
@@ -800,7 +804,7 @@ static int mark_stripe_bucket(struct btree_trans *trans,
        bucket_lock(g);
        old = *g;
 
-       ret = check_bucket_ref(c, k, ptr, sectors, data_type,
+       ret = check_bucket_ref(trans, k, ptr, sectors, data_type,
                               g->gen, g->data_type,
                               g->dirty_sectors, g->cached_sectors);
        if (ret)
@@ -832,7 +836,7 @@ static int __mark_pointer(struct btree_trans *trans,
        u32 *dst_sectors = !ptr->cached
                ? dirty_sectors
                : cached_sectors;
-       int ret = check_bucket_ref(trans->c, k, ptr, sectors, ptr_data_type,
+       int ret = check_bucket_ref(trans, k, ptr, sectors, ptr_data_type,
                                   bucket_gen, *bucket_data_type,
                                   *dirty_sectors, *cached_sectors);
 
@@ -1220,7 +1224,8 @@ not_found:
                new->k.p                = bkey_start_pos(p.k);
                new->k.p.offset += *idx - start;
                bch2_key_resize(&new->k, next_idx - *idx);
-               ret = __bch2_btree_insert(trans, BTREE_ID_extents, &new->k_i);
+               ret = __bch2_btree_insert(trans, BTREE_ID_extents, &new->k_i,
+                                         BTREE_TRIGGER_NORUN);
        }
 
        *idx = next_idx;
@@ -1269,6 +1274,47 @@ int bch2_mark_reflink_p(struct btree_trans *trans,
        return ret;
 }
 
+void bch2_trans_fs_usage_revert(struct btree_trans *trans,
+                               struct replicas_delta_list *deltas)
+{
+       struct bch_fs *c = trans->c;
+       struct bch_fs_usage *dst;
+       struct replicas_delta *d, *top = (void *) deltas->d + deltas->used;
+       s64 added = 0;
+       unsigned i;
+
+       percpu_down_read(&c->mark_lock);
+       preempt_disable();
+       dst = fs_usage_ptr(c, trans->journal_res.seq, false);
+
+       /* revert changes: */
+       for (d = deltas->d; d != top; d = replicas_delta_next(d)) {
+               switch (d->r.data_type) {
+               case BCH_DATA_btree:
+               case BCH_DATA_user:
+               case BCH_DATA_parity:
+                       added += d->delta;
+               }
+               BUG_ON(__update_replicas(c, dst, &d->r, -d->delta));
+       }
+
+       dst->nr_inodes -= deltas->nr_inodes;
+
+       for (i = 0; i < BCH_REPLICAS_MAX; i++) {
+               added                           -= deltas->persistent_reserved[i];
+               dst->reserved                   -= deltas->persistent_reserved[i];
+               dst->persistent_reserved[i]     -= deltas->persistent_reserved[i];
+       }
+
+       if (added > 0) {
+               trans->disk_res->sectors += added;
+               this_cpu_add(*c->online_reserved, added);
+       }
+
+       preempt_enable();
+       percpu_up_read(&c->mark_lock);
+}
+
 int bch2_trans_fs_usage_apply(struct btree_trans *trans,
                              struct replicas_delta_list *deltas)
 {
@@ -1349,7 +1395,7 @@ need_mark:
 
 /* trans_mark: */
 
-static int bch2_trans_mark_pointer(struct btree_trans *trans,
+static inline int bch2_trans_mark_pointer(struct btree_trans *trans,
                                   enum btree_id btree_id, unsigned level,
                                   struct bkey_s_c k, struct extent_ptr_decoded p,
                                   unsigned flags)
@@ -1378,9 +1424,7 @@ static int bch2_trans_mark_pointer(struct btree_trans *trans,
                goto err;
 
        if (!p.ptr.cached) {
-               ret = insert
-                       ? bch2_bucket_backpointer_add(trans, a, bp, k)
-                       : bch2_bucket_backpointer_del(trans, a, bp, k);
+               ret = bch2_bucket_backpointer_mod(trans, a, bp, k, insert);
                if (ret)
                        goto err;
        }
@@ -1518,7 +1562,7 @@ static int bch2_trans_mark_stripe_bucket(struct btree_trans *trans,
        if (IS_ERR(a))
                return PTR_ERR(a);
 
-       ret = check_bucket_ref(c, s.s_c, ptr, sectors, data_type,
+       ret = check_bucket_ref(trans, s.s_c, ptr, sectors, data_type,
                               a->v.gen, a->v.data_type,
                               a->v.dirty_sectors, a->v.cached_sectors);
        if (ret)
index b6a1db7619ff659666eb385595a0beb7c983daf9..3398c9c3a81b353cbb1fc3bfc15d34f5c4612274 100644 (file)
@@ -248,6 +248,7 @@ int bch2_trans_mark_inode(struct btree_trans *, enum btree_id, unsigned, struct
 int bch2_trans_mark_reservation(struct btree_trans *, enum btree_id, unsigned, struct bkey_s_c, struct bkey_i *, unsigned);
 int bch2_trans_mark_reflink_p(struct btree_trans *, enum btree_id, unsigned, struct bkey_s_c, struct bkey_i *, unsigned);
 
+void bch2_trans_fs_usage_revert(struct btree_trans *, struct replicas_delta_list *);
 int bch2_trans_fs_usage_apply(struct btree_trans *, struct replicas_delta_list *);
 
 int bch2_trans_mark_metadata_bucket(struct btree_trans *, struct bch_dev *,
index 9df958b43dfe5a31deefc238aaa602b1a48f13da..199e894e0f8b31bb4bc8b3b77419149ccc2cba2d 100644 (file)
@@ -309,9 +309,11 @@ void bch2_data_update_exit(struct data_update *update)
                bch2_bkey_ptrs_c(bkey_i_to_s_c(update->k.k));
        const struct bch_extent_ptr *ptr;
 
-       bkey_for_each_ptr(ptrs, ptr)
+       bkey_for_each_ptr(ptrs, ptr) {
                bch2_bucket_nocow_unlock(&c->nocow_locks,
-                                      PTR_BUCKET_POS(c, ptr), 0);
+                                        PTR_BUCKET_POS(c, ptr), 0);
+               percpu_ref_put(&bch_dev_bkey_exists(c, ptr->dev)->ref);
+       }
 
        bch2_bkey_buf_exit(&update->k, c);
        bch2_disk_reservation_put(c, &update->op.res);
@@ -411,6 +413,7 @@ int bch2_data_update_init(struct btree_trans *trans,
        struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
        const union bch_extent_entry *entry;
        struct extent_ptr_decoded p;
+       const struct bch_extent_ptr *ptr;
        unsigned i, reserve_sectors = k.k->size * data_opts.extra_replicas;
        unsigned int ptrs_locked = 0;
        int ret;
@@ -436,6 +439,9 @@ int bch2_data_update_init(struct btree_trans *trans,
        if (m->data_opts.btree_insert_flags & BTREE_INSERT_USE_RESERVE)
                m->op.alloc_reserve = RESERVE_movinggc;
 
+       bkey_for_each_ptr(ptrs, ptr)
+               percpu_ref_get(&bch_dev_bkey_exists(c, ptr->dev)->ref);
+
        i = 0;
        bkey_for_each_ptr_decode(k.k, ptrs, p, entry) {
                bool locked;
@@ -508,7 +514,8 @@ err:
        bkey_for_each_ptr_decode(k.k, ptrs, p, entry) {
                if ((1U << i) & ptrs_locked)
                        bch2_bucket_nocow_unlock(&c->nocow_locks,
-                                               PTR_BUCKET_POS(c, &p.ptr), 0);
+                                                PTR_BUCKET_POS(c, &p.ptr), 0);
+               percpu_ref_put(&bch_dev_bkey_exists(c, p.ptr.dev)->ref);
                i++;
        }
 
index 9f5a27dab351d7c67cd0f9495ba0d8dffbd71454..3ebe699ff0a2e63abfab60ceadaec764124173a6 100644 (file)
@@ -9,6 +9,7 @@
 #include "bset.h"
 #include "btree_gc.h"
 #include "btree_update.h"
+#include "btree_write_buffer.h"
 #include "buckets.h"
 #include "disk_groups.h"
 #include "ec.h"
@@ -921,13 +922,16 @@ static int ec_stripe_update_extents(struct bch_fs *c, struct ec_stripe_buf *s)
 
        bch2_trans_init(&trans, c, 0, 0);
 
+       ret = bch2_btree_write_buffer_flush(&trans);
+       if (ret)
+               goto err;
+
        for (i = 0; i < nr_data; i++) {
                ret = ec_stripe_update_bucket(&trans, s, i);
                if (ret)
                        break;
        }
-
-
+err:
        bch2_trans_exit(&trans);
 
        return ret;
index b8c24f517f0316d80b70d39464b407973605ccf6..6129af6129c3992148ee8a926fd7f443f7d4f87e 100644 (file)
@@ -42,6 +42,7 @@
        x(BCH_ERR_transaction_restart,  transaction_restart_key_cache_realloced)\
        x(BCH_ERR_transaction_restart,  transaction_restart_journal_preres_get) \
        x(BCH_ERR_transaction_restart,  transaction_restart_split_race)         \
+       x(BCH_ERR_transaction_restart,  transaction_restart_write_buffer_flush) \
        x(BCH_ERR_transaction_restart,  transaction_restart_nested)             \
        x(0,                            no_btree_node)                          \
        x(BCH_ERR_no_btree_node,        no_btree_node_relock)                   \
@@ -58,6 +59,7 @@
        x(BCH_ERR_btree_insert_fail,    btree_insert_need_mark_replicas)        \
        x(BCH_ERR_btree_insert_fail,    btree_insert_need_journal_res)          \
        x(BCH_ERR_btree_insert_fail,    btree_insert_need_journal_reclaim)      \
+       x(BCH_ERR_btree_insert_fail,    btree_insert_need_flush_buffer)         \
        x(0,                            backpointer_to_overwritten_btree_node)  \
        x(0,                            lock_fail_root_changed)                 \
        x(0,                            journal_reclaim_would_deadlock)         \
index 5887d78190eb971176276ef31dea635ba05baa00..52bb00b52b900bedcf35faf338ce037953fd5f46 100644 (file)
@@ -1797,7 +1797,8 @@ static int check_root_trans(struct btree_trans *trans)
                ret = commit_do(trans, NULL, NULL,
                                      BTREE_INSERT_NOFAIL|
                                      BTREE_INSERT_LAZY_RW,
-                       __bch2_btree_insert(trans, BTREE_ID_subvolumes, &root_subvol.k_i));
+                       __bch2_btree_insert(trans, BTREE_ID_subvolumes,
+                                           &root_subvol.k_i, 0));
                if (ret) {
                        bch_err(c, "error writing root subvol: %s", bch2_err_str(ret));
                        goto err;
index c0c33f788d44db01c84094e9346994fe18fbf1dc..64925db22cdc937419881e5ed1ae78c4a5a1f3ad 100644 (file)
@@ -684,7 +684,7 @@ void bch2_submit_wbio_replicas(struct bch_write_bio *wbio, struct bch_fs *c,
 
                        bio_set_dev(&n->bio, ca->disk_sb.bdev);
 
-                       if (IS_ENABLED(CONFIG_BCACHEFS_NO_IO) && type != BCH_DATA_btree) {
+                       if (type != BCH_DATA_btree && unlikely(c->opts.no_data_io)) {
                                bio_endio(&n->bio);
                                continue;
                        }
@@ -2356,8 +2356,7 @@ static void __bch2_read_endio(struct work_struct *work)
        }
 
        csum = bch2_checksum_bio(c, crc.csum_type, nonce, src);
-       if (bch2_crc_cmp(csum, rbio->pick.crc.csum) &&
-           !IS_ENABLED(CONFIG_BCACHEFS_NO_IO))
+       if (bch2_crc_cmp(csum, rbio->pick.crc.csum) && !c->opts.no_data_io)
                goto csum_err;
 
        /*
@@ -2808,7 +2807,7 @@ get_bio:
                             bio_sectors(&rbio->bio));
                bio_set_dev(&rbio->bio, ca->disk_sb.bdev);
 
-               if (IS_ENABLED(CONFIG_BCACHEFS_NO_IO)) {
+               if (unlikely(c->opts.no_data_io)) {
                        if (likely(!(flags & BCH_READ_IN_RETRY)))
                                bio_endio(&rbio->bio);
                } else {
index 6214a919da8072b7a06f8f92ff63462725b495e8..957eeece4d98f56c827f6063d074998dd5401e4c 100644 (file)
@@ -769,6 +769,7 @@ static int __bch2_set_nr_journal_buckets(struct bch_dev *ca, unsigned nr,
        if (c) {
                bch2_journal_flush_all_pins(&c->journal);
                bch2_journal_block(&c->journal);
+               mutex_lock(&c->sb_lock);
        }
 
        bu              = kcalloc(nr_want, sizeof(*bu), GFP_KERNEL);
@@ -849,6 +850,9 @@ static int __bch2_set_nr_journal_buckets(struct bch_dev *ca, unsigned nr,
        if (!new_fs)
                spin_unlock(&c->journal.lock);
 
+       if (ja->nr != old_nr && !new_fs)
+               bch2_write_super(c);
+
        if (c)
                bch2_journal_unblock(&c->journal);
 
@@ -868,6 +872,9 @@ static int __bch2_set_nr_journal_buckets(struct bch_dev *ca, unsigned nr,
                }
        }
 err:
+       if (c)
+               mutex_unlock(&c->sb_lock);
+
        if (ob && !new_fs)
                for (i = 0; i < nr_got; i++)
                        bch2_open_bucket_put(c, ob[i]);
@@ -893,7 +900,6 @@ int bch2_set_nr_journal_buckets(struct bch_fs *c, struct bch_dev *ca,
 {
        struct journal_device *ja = &ca->journal;
        struct closure cl;
-       unsigned current_nr;
        int ret = 0;
 
        /* don't handle reducing nr of buckets yet: */
@@ -902,44 +908,44 @@ int bch2_set_nr_journal_buckets(struct bch_fs *c, struct bch_dev *ca,
 
        closure_init_stack(&cl);
 
-       while (ja->nr != nr && (ret == 0 || ret == -BCH_ERR_bucket_alloc_blocked)) {
+       while (ja->nr != nr) {
                struct disk_reservation disk_res = { 0, 0 };
 
-               closure_sync(&cl);
-
-               mutex_lock(&c->sb_lock);
-               current_nr = ja->nr;
-
                /*
                 * note: journal buckets aren't really counted as _sectors_ used yet, so
                 * we don't need the disk reservation to avoid the BUG_ON() in buckets.c
                 * when space used goes up without a reservation - but we do need the
                 * reservation to ensure we'll actually be able to allocate:
+                *
+                * XXX: that's not right, disk reservations only ensure a
+                * filesystem-wide allocation will succeed, this is a device
+                * specific allocation - we can hang here:
                 */
 
                ret = bch2_disk_reservation_get(c, &disk_res,
                                                bucket_to_sector(ca, nr - ja->nr), 1, 0);
-               if (ret) {
-                       mutex_unlock(&c->sb_lock);
-                       return ret;
-               }
+               if (ret)
+                       break;
 
                ret = __bch2_set_nr_journal_buckets(ca, nr, false, &cl);
 
                bch2_disk_reservation_put(c, &disk_res);
 
-               if (ja->nr != current_nr)
-                       bch2_write_super(c);
-               mutex_unlock(&c->sb_lock);
+               closure_sync(&cl);
+
+               if (ret && ret != -BCH_ERR_bucket_alloc_blocked)
+                       break;
        }
 
+       if (ret)
+               bch_err(c, "%s: err %s", __func__, bch2_err_str(ret));
+
        return ret;
 }
 
 int bch2_dev_journal_alloc(struct bch_dev *ca)
 {
        unsigned nr;
-       int ret;
 
        if (dynamic_fault("bcachefs:add:journal_alloc"))
                return -ENOMEM;
@@ -956,15 +962,7 @@ int bch2_dev_journal_alloc(struct bch_dev *ca)
                     min(1 << 13,
                         (1 << 24) / ca->mi.bucket_size));
 
-       if (ca->fs)
-               mutex_lock(&ca->fs->sb_lock);
-
-       ret = __bch2_set_nr_journal_buckets(ca, nr, true, NULL);
-
-       if (ca->fs)
-               mutex_unlock(&ca->fs->sb_lock);
-
-       return ret;
+       return __bch2_set_nr_journal_buckets(ca, nr, true, NULL);
 }
 
 /* startup/shutdown: */
index 8321563d018722b00e35fda24176f40b41e61cc4..7e7e9042de402f0a92787dd080d3284159ee4d50 100644 (file)
@@ -8,6 +8,7 @@
 #include "btree_gc.h"
 #include "btree_update.h"
 #include "btree_update_interior.h"
+#include "btree_write_buffer.h"
 #include "disk_groups.h"
 #include "ec.h"
 #include "errcode.h"
@@ -680,10 +681,19 @@ int __bch2_evacuate_bucket(struct moving_context *ctxt,
                        bkey_err(k = bch2_btree_iter_peek_slot(&iter)));
        bch2_trans_iter_exit(&trans, &iter);
 
-       if (!ret) {
-               a = bch2_alloc_to_v4(k, &a_convert);
-               dirty_sectors = a->dirty_sectors;
-               bucket_size = bch_dev_bkey_exists(c, bucket.inode)->mi.bucket_size;
+       if (ret) {
+               bch_err(c, "%s: error looking up alloc key: %s", __func__, bch2_err_str(ret));
+               goto err;
+       }
+
+       a = bch2_alloc_to_v4(k, &a_convert);
+       dirty_sectors = a->dirty_sectors;
+       bucket_size = bch_dev_bkey_exists(c, bucket.inode)->mi.bucket_size;
+
+       ret = bch2_btree_write_buffer_flush(&trans);
+       if (ret) {
+               bch_err(c, "%s: error flushing btree write buffer: %s", __func__, bch2_err_str(ret));
+               goto err;
        }
 
        while (!(ret = move_ratelimit(&trans, ctxt))) {
@@ -712,7 +722,7 @@ int __bch2_evacuate_bucket(struct moving_context *ctxt,
                        if (ret)
                                goto err;
                        if (!k.k)
-                               continue;
+                               goto next;
 
                        bch2_bkey_buf_reassemble(&sk, c, k);
                        k = bkey_i_to_s_c(sk.k);
@@ -763,7 +773,7 @@ int __bch2_evacuate_bucket(struct moving_context *ctxt,
                        if (ret)
                                goto err;
                        if (!b)
-                               continue;
+                               goto next;
 
                        ret = bch2_btree_node_rewrite(&trans, &iter, b, 0);
                        bch2_trans_iter_exit(&trans, &iter);
@@ -779,7 +789,7 @@ int __bch2_evacuate_bucket(struct moving_context *ctxt,
                        atomic64_add(c->opts.btree_node_size >> 9, &ctxt->stats->sectors_seen);
                        atomic64_add(c->opts.btree_node_size >> 9, &ctxt->stats->sectors_moved);
                }
-
+next:
                bp_offset++;
        }
 
index 9d4bbec7e6820ec7ea3cd021d766a8c6d53719e5..304718a0b599b50b60d80631d98f515bbc7b6d77 100644 (file)
@@ -206,6 +206,11 @@ enum opt_type {
          OPT_BOOL(),                                                   \
          BCH2_NO_SB_OPT,               true,                           \
          NULL,         "Stash pointer to in memory btree node in btree ptr")\
+       x(btree_write_buffer_size, u32,                                 \
+         OPT_FS|OPT_MOUNT,                                             \
+         OPT_UINT(16, (1U << 20) - 1),                                 \
+         BCH2_NO_SB_OPT,               1U << 13,                       \
+         NULL,         "Number of btree write buffer entries")         \
        x(gc_reserve_percent,           u8,                             \
          OPT_FS|OPT_FORMAT|OPT_MOUNT|OPT_RUNTIME,                      \
          OPT_UINT(5, 21),                                              \
@@ -399,6 +404,12 @@ enum opt_type {
          NULL,         "Nocow mode: Writes will be done in place when possible.\n"\
                        "Snapshots and reflink will still caused writes to be COW\n"\
                        "Implicitly disables data checksumming, compression and encryption")\
+       x(no_data_io,                   u8,                             \
+         OPT_FS|OPT_MOUNT,                                             \
+         OPT_BOOL(),                                                   \
+         BCH2_NO_SB_OPT,                       false,                  \
+         NULL,         "Skip submit_bio() for data reads and writes, " \
+                       "for performance testing purposes")             \
        x(fs_size,                      u64,                            \
          OPT_DEVICE,                                                   \
          OPT_UINT(0, S64_MAX),                                         \
index 8a78377bf9c5991846c7e4202107cac7d1b00656..178f064244601ec302521213f8bac5d80b4a83a8 100644 (file)
@@ -974,9 +974,15 @@ static int read_btree_roots(struct bch_fs *c)
                }
        }
 
-       for (i = 0; i < BTREE_ID_NR; i++)
-               if (!c->btree_roots[i].b)
+       for (i = 0; i < BTREE_ID_NR; i++) {
+               struct btree_root *r = &c->btree_roots[i];
+
+               if (!r->b) {
+                       r->alive = false;
+                       r->level = 0;
                        bch2_btree_root_alloc(c, i);
+               }
+       }
 fsck_err:
        return ret;
 }
index 482bedf4be8ba2cd696dd5afae09230281ae8aa0..3bff21959d986d388043e978d1525888702b2be8 100644 (file)
@@ -299,13 +299,6 @@ static int replicas_table_update(struct bch_fs *c,
 
        memset(new_usage, 0, sizeof(new_usage));
 
-       for (i = 0; i < ARRAY_SIZE(new_usage); i++)
-               if (!(new_usage[i] = __alloc_percpu_gfp(bytes,
-                                       sizeof(u64), GFP_KERNEL)))
-                       goto err;
-
-       memset(new_usage, 0, sizeof(new_usage));
-
        for (i = 0; i < ARRAY_SIZE(new_usage); i++)
                if (!(new_usage[i] = __alloc_percpu_gfp(bytes,
                                        sizeof(u64), GFP_KERNEL)))
index 08bfed1bbf88e4c15dbbb48b885c3b23af9fefe3..8bed11857bacfb824d98cffd3486d1e3ea75731b 100644 (file)
@@ -16,6 +16,7 @@
 #include "btree_key_cache.h"
 #include "btree_update_interior.h"
 #include "btree_io.h"
+#include "btree_write_buffer.h"
 #include "buckets_waiting_for_journal.h"
 #include "chardev.h"
 #include "checksum.h"
@@ -418,6 +419,7 @@ static int __bch2_fs_read_write(struct bch_fs *c, bool early)
        bch2_do_discards(c);
        bch2_do_invalidates(c);
        bch2_do_stripe_deletes(c);
+       bch2_do_pending_node_rewrites(c);
        return 0;
 err:
        __bch2_fs_read_only(c);
@@ -446,6 +448,7 @@ static void __bch2_fs_free(struct bch_fs *c)
        for (i = 0; i < BCH_TIME_STAT_NR; i++)
                bch2_time_stats_exit(&c->times[i]);
 
+       bch2_free_pending_node_rewrites(c);
        bch2_fs_counters_exit(c);
        bch2_fs_snapshots_exit(c);
        bch2_fs_quota_exit(c);
@@ -465,6 +468,7 @@ static void __bch2_fs_free(struct bch_fs *c)
        bch2_fs_compress_exit(c);
        bch2_journal_keys_free(&c->journal_keys);
        bch2_journal_entries_free(c);
+       bch2_fs_btree_write_buffer_exit(c);
        percpu_free_rwsem(&c->mark_lock);
        free_percpu(c->online_reserved);
 
@@ -817,6 +821,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
            bch2_fs_btree_iter_init(c) ?:
            bch2_fs_btree_interior_update_init(c) ?:
            bch2_fs_buckets_waiting_for_journal_init(c) ?:
+           bch2_fs_btree_write_buffer_init(c) ?:
            bch2_fs_subvolumes_init(c) ?:
            bch2_fs_io_init(c) ?:
            bch2_fs_nocow_locking_init(c) ?:
index ebd10cd52b9ba91dbd3f2ce76418be2aef0053e5..b981c87edd1f7f316c5f820b56d33f9427997fb4 100644 (file)
@@ -473,14 +473,14 @@ SHOW(bch2_fs)
        if (attr == &sysfs_data_jobs)
                data_progress_to_text(out, c);
 
-       if (attr == &sysfs_nocow_lock_table)
-               bch2_nocow_locks_to_text(out, &c->nocow_locks);
-
 #ifdef BCH_WRITE_REF_DEBUG
        if (attr == &sysfs_write_refs)
                bch2_write_refs_to_text(out, c);
 #endif
 
+       if (attr == &sysfs_nocow_lock_table)
+               bch2_nocow_locks_to_text(out, &c->nocow_locks);
+
        return 0;
 }
 
@@ -664,10 +664,10 @@ struct attribute *bch2_fs_internal_files[] = {
        &sysfs_stripes_heap,
        &sysfs_open_buckets,
        &sysfs_write_points,
-       &sysfs_nocow_lock_table,
 #ifdef BCH_WRITE_REF_DEBUG
        &sysfs_write_refs,
 #endif
+       &sysfs_nocow_lock_table,
        &sysfs_io_timers_read,
        &sysfs_io_timers_write,
 
index 80fce1c954709fb45fd2139442c210902ae72258..d352821d5614005aee064052fe841a6c87a64189 100644 (file)
@@ -592,7 +592,7 @@ static int rand_insert(struct bch_fs *c, u64 nr)
                k.k.p.snapshot = U32_MAX;
 
                ret = commit_do(&trans, NULL, NULL, 0,
-                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k.k_i));
+                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k.k_i, 0));
                if (ret) {
                        bch_err(c, "%s(): error %s", __func__, bch2_err_str(ret));
                        break;
@@ -621,14 +621,14 @@ static int rand_insert_multi(struct bch_fs *c, u64 nr)
                }
 
                ret = commit_do(&trans, NULL, NULL, 0,
-                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k[0].k_i) ?:
-                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k[1].k_i) ?:
-                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k[2].k_i) ?:
-                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k[3].k_i) ?:
-                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k[4].k_i) ?:
-                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k[5].k_i) ?:
-                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k[6].k_i) ?:
-                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k[7].k_i));
+                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k[0].k_i, 0) ?:
+                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k[1].k_i, 0) ?:
+                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k[2].k_i, 0) ?:
+                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k[3].k_i, 0) ?:
+                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k[4].k_i, 0) ?:
+                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k[5].k_i, 0) ?:
+                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k[6].k_i, 0) ?:
+                       __bch2_btree_insert(&trans, BTREE_ID_xattrs, &k[7].k_i, 0));
                if (ret) {
                        bch_err(c, "%s(): error %s", __func__, bch2_err_str(ret));
                        break;