summaryrefslogtreecommitdiff
path: root/fs/bcachefs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/bcachefs')
-rw-r--r--fs/bcachefs/bcachefs.h1
-rw-r--r--fs/bcachefs/btree_gc.c2
-rw-r--r--fs/bcachefs/btree_types.h4
-rw-r--r--fs/bcachefs/data_update.c2
-rw-r--r--fs/bcachefs/error.c2
-rw-r--r--fs/bcachefs/io_read.c6
-rw-r--r--fs/bcachefs/io_write.c3
-rw-r--r--fs/bcachefs/journal_sb.c45
-rw-r--r--fs/bcachefs/journal_sb.h1
-rw-r--r--fs/bcachefs/move.c6
-rw-r--r--fs/bcachefs/recovery.c5
-rw-r--r--fs/bcachefs/super.c17
-rw-r--r--fs/bcachefs/super.h2
13 files changed, 85 insertions, 11 deletions
diff --git a/fs/bcachefs/bcachefs.h b/fs/bcachefs/bcachefs.h
index 16d08dfb5f19..0ede47f62129 100644
--- a/fs/bcachefs/bcachefs.h
+++ b/fs/bcachefs/bcachefs.h
@@ -523,6 +523,7 @@ struct discard_in_flight {
x(journal_read) \
x(fs_journal_alloc) \
x(fs_resize_on_mount) \
+ x(sb_journal_sort) \
x(btree_node_read) \
x(btree_node_read_all_replicas) \
x(btree_node_scrub) \
diff --git a/fs/bcachefs/btree_gc.c b/fs/bcachefs/btree_gc.c
index 43f294284d57..2b1b6b7ffdf0 100644
--- a/fs/bcachefs/btree_gc.c
+++ b/fs/bcachefs/btree_gc.c
@@ -720,7 +720,7 @@ static int bch2_gc_btree(struct btree_trans *trans,
enum btree_id btree, bool initial)
{
struct bch_fs *c = trans->c;
- unsigned target_depth = btree_node_type_has_triggers(__btree_node_type(0, btree)) ? 0 : 1;
+ unsigned target_depth = BIT_ULL(btree) & btree_leaf_has_triggers_mask ? 0 : 1;
int ret = 0;
/* We need to make sure every leaf node is readable before going RW */
diff --git a/fs/bcachefs/btree_types.h b/fs/bcachefs/btree_types.h
index e893eb938bb3..31ae24dcab75 100644
--- a/fs/bcachefs/btree_types.h
+++ b/fs/bcachefs/btree_types.h
@@ -840,6 +840,10 @@ static inline bool btree_node_type_has_triggers(enum btree_node_type type)
return BIT_ULL(type) & BTREE_NODE_TYPE_HAS_TRIGGERS;
}
+/* A mask of btree id bits that have triggers for their leaves */
+__maybe_unused
+static const u64 btree_leaf_has_triggers_mask = BTREE_NODE_TYPE_HAS_TRIGGERS >> 1;
+
static const u64 btree_is_extents_mask = 0
#define x(name, nr, flags, ...) |((!!((flags) & BTREE_IS_extents)) << nr)
BCH_BTREE_IDS()
diff --git a/fs/bcachefs/data_update.c b/fs/bcachefs/data_update.c
index 2c997fddefb3..d0f5a765cd4b 100644
--- a/fs/bcachefs/data_update.c
+++ b/fs/bcachefs/data_update.c
@@ -657,6 +657,8 @@ void bch2_data_update_to_text(struct printbuf *out, struct data_update *m)
prt_str_indented(out, "old key:\t");
bch2_bkey_val_to_text(out, m->op.c, bkey_i_to_s_c(m->k.k));
+
+ bch2_write_op_to_text(out, &m->op);
}
void bch2_data_update_inflight_to_text(struct printbuf *out, struct data_update *m)
diff --git a/fs/bcachefs/error.c b/fs/bcachefs/error.c
index e33f3166c48a..9e69263eb796 100644
--- a/fs/bcachefs/error.c
+++ b/fs/bcachefs/error.c
@@ -394,7 +394,7 @@ int bch2_fsck_err_opt(struct bch_fs *c,
flags |= fsck_flags_extra[err];
if (test_bit(BCH_FS_in_fsck, &c->flags) ||
- test_bit(BCH_FS_in_recovery, &c->flags)) {
+ c->opts.fix_errors != FSCK_FIX_exit) {
if (!(flags & (FSCK_CAN_FIX|FSCK_CAN_IGNORE)))
return bch_err_throw(c, fsck_repair_unimplemented);
diff --git a/fs/bcachefs/io_read.c b/fs/bcachefs/io_read.c
index e7d53ab1cf55..bae2e181c9ed 100644
--- a/fs/bcachefs/io_read.c
+++ b/fs/bcachefs/io_read.c
@@ -1076,8 +1076,10 @@ int __bch2_read_extent(struct btree_trans *trans, struct bch_read_bio *orig,
}
if ((bch2_bkey_extent_flags(k) & BIT_ULL(BCH_EXTENT_FLAG_poisoned)) &&
- !orig->data_update)
- return bch_err_throw(c, extent_poisoned);
+ !orig->data_update) {
+ ret = bch_err_throw(c, extent_poisoned);
+ goto err;
+ }
retry_pick:
ret = bch2_bkey_pick_read_device(c, k, failed, &pick, dev);
diff --git a/fs/bcachefs/io_write.c b/fs/bcachefs/io_write.c
index 1d83dcc9731e..501ab7fc9f69 100644
--- a/fs/bcachefs/io_write.c
+++ b/fs/bcachefs/io_write.c
@@ -1754,6 +1754,9 @@ void bch2_write_op_to_text(struct printbuf *out, struct bch_write_op *op)
prt_printf(out, "nr_replicas:\t%u\n", op->nr_replicas);
prt_printf(out, "nr_replicas_required:\t%u\n", op->nr_replicas_required);
+ prt_printf(out, "devs_have:\t");
+ bch2_devs_list_to_text(out, &op->devs_have);
+ prt_newline(out);
prt_printf(out, "ref:\t%u\n", closure_nr_remaining(&op->cl));
prt_printf(out, "ret\t%s\n", bch2_err_str(op->error));
diff --git a/fs/bcachefs/journal_sb.c b/fs/bcachefs/journal_sb.c
index 0cb9b93f13e7..dc0ecedb3a8f 100644
--- a/fs/bcachefs/journal_sb.c
+++ b/fs/bcachefs/journal_sb.c
@@ -30,7 +30,7 @@ static int bch2_sb_journal_validate(struct bch_sb *sb, struct bch_sb_field *f,
if (!nr)
return 0;
- b = kmalloc_array(nr, sizeof(u64), GFP_KERNEL);
+ b = kvmalloc_array(nr, sizeof(u64), GFP_KERNEL);
if (!b)
return -BCH_ERR_ENOMEM_sb_journal_validate;
@@ -64,7 +64,7 @@ static int bch2_sb_journal_validate(struct bch_sb *sb, struct bch_sb_field *f,
ret = 0;
err:
- kfree(b);
+ kvfree(b);
return ret;
}
@@ -113,7 +113,7 @@ static int bch2_sb_journal_v2_validate(struct bch_sb *sb, struct bch_sb_field *f
if (!nr)
return 0;
- b = kmalloc_array(nr, sizeof(*b), GFP_KERNEL);
+ b = kvmalloc_array(nr, sizeof(*b), GFP_KERNEL);
if (!b)
return -BCH_ERR_ENOMEM_sb_journal_v2_validate;
@@ -165,7 +165,7 @@ static int bch2_sb_journal_v2_validate(struct bch_sb *sb, struct bch_sb_field *f
ret = 0;
err:
- kfree(b);
+ kvfree(b);
return ret;
}
@@ -230,3 +230,40 @@ int bch2_journal_buckets_to_sb(struct bch_fs *c, struct bch_dev *ca,
BUG_ON(dst + 1 != nr_compacted);
return 0;
}
+
+static inline bool journal_v2_unsorted(struct bch_sb_field_journal_v2 *j)
+{
+ unsigned nr = bch2_sb_field_journal_v2_nr_entries(j);
+ for (unsigned i = 0; i + 1 < nr; i++)
+ if (le64_to_cpu(j->d[i].start) > le64_to_cpu(j->d[i + 1].start))
+ return true;
+ return false;
+}
+
+int bch2_sb_journal_sort(struct bch_fs *c)
+{
+ BUG_ON(!c->sb.clean);
+ BUG_ON(test_bit(BCH_FS_rw, &c->flags));
+
+ guard(mutex)(&c->sb_lock);
+ bool write_sb = false;
+
+ for_each_online_member(c, ca, BCH_DEV_READ_REF_sb_journal_sort) {
+ struct bch_sb_field_journal_v2 *j = bch2_sb_field_get(ca->disk_sb.sb, journal_v2);
+ if (!j)
+ continue;
+
+ if ((j && journal_v2_unsorted(j)) ||
+ bch2_sb_field_get(ca->disk_sb.sb, journal)) {
+ struct journal_device *ja = &ca->journal;
+
+ sort(ja->buckets, ja->nr, sizeof(ja->buckets[0]), u64_cmp, NULL);
+ bch2_journal_buckets_to_sb(c, ca, ja->buckets, ja->nr);
+ write_sb = true;
+ }
+ }
+
+ return write_sb
+ ? bch2_write_super(c)
+ : 0;
+}
diff --git a/fs/bcachefs/journal_sb.h b/fs/bcachefs/journal_sb.h
index ba40a7e8d90a..e0fc40652607 100644
--- a/fs/bcachefs/journal_sb.h
+++ b/fs/bcachefs/journal_sb.h
@@ -22,3 +22,4 @@ extern const struct bch_sb_field_ops bch_sb_field_ops_journal;
extern const struct bch_sb_field_ops bch_sb_field_ops_journal_v2;
int bch2_journal_buckets_to_sb(struct bch_fs *, struct bch_dev *, u64 *, unsigned);
+int bch2_sb_journal_sort(struct bch_fs *);
diff --git a/fs/bcachefs/move.c b/fs/bcachefs/move.c
index 4f41f1f6ec6c..101658cbe95a 100644
--- a/fs/bcachefs/move.c
+++ b/fs/bcachefs/move.c
@@ -121,6 +121,7 @@ struct moving_io {
static void move_free(struct moving_io *io)
{
struct moving_context *ctxt = io->write.ctxt;
+ struct bch_fs *c = io->write.op.c;
if (io->b)
atomic_dec(&io->b->count);
@@ -132,8 +133,9 @@ static void move_free(struct moving_io *io)
if (!io->write.data_opts.scrub) {
bch2_data_update_exit(&io->write);
} else {
- bch2_bio_free_pages_pool(io->write.op.c, &io->write.op.wbio.bio);
+ bch2_bio_free_pages_pool(c, &io->write.op.wbio.bio);
kfree(io->write.bvecs);
+ bch2_bkey_buf_exit(&io->write.k, c);
}
kfree(io);
}
@@ -427,7 +429,9 @@ int bch2_move_extent(struct moving_context *ctxt,
data_opts.scrub ? data_opts.read_dev : -1);
return 0;
err:
+ bch2_bkey_buf_exit(&io->write.k, c);
kfree(io);
+
if (bch2_err_matches(ret, EROFS) ||
bch2_err_matches(ret, BCH_ERR_transaction_restart))
return ret;
diff --git a/fs/bcachefs/recovery.c b/fs/bcachefs/recovery.c
index 6319144a440c..278d8a38d23e 100644
--- a/fs/bcachefs/recovery.c
+++ b/fs/bcachefs/recovery.c
@@ -15,6 +15,7 @@
#include "error.h"
#include "journal_io.h"
#include "journal_reclaim.h"
+#include "journal_sb.h"
#include "journal_seq_blacklist.h"
#include "logged_ops.h"
#include "move.h"
@@ -644,6 +645,10 @@ int bch2_fs_recovery(struct bch_fs *c)
bch_info(c, "recovering from clean shutdown, journal seq %llu",
le64_to_cpu(clean->journal_seq));
+
+ ret = bch2_sb_journal_sort(c);
+ if (ret)
+ goto err;
} else {
bch_info(c, "recovering from unclean shutdown");
}
diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c
index d640ae188722..7bd12e57e41f 100644
--- a/fs/bcachefs/super.c
+++ b/fs/bcachefs/super.c
@@ -277,6 +277,17 @@ struct bch_fs *bch2_uuid_to_fs(__uuid_t uuid)
return c;
}
+void bch2_devs_list_to_text(struct printbuf *out, struct bch_devs_list *d)
+{
+ prt_char(out, '[');
+ darray_for_each(*d, i) {
+ if (i != d->data)
+ prt_char(out, ' ');
+ prt_printf(out, "%u", *i);
+ }
+ prt_char(out, ']');
+}
+
/* Filesystem RO/RW: */
/*
@@ -461,9 +472,11 @@ static bool __bch2_fs_emergency_read_only2(struct bch_fs *c, struct printbuf *ou
bch2_fs_read_only_async(c);
wake_up(&bch2_read_only_wait);
- if (ret)
+ if (ret) {
prt_printf(out, "emergency read only at seq %llu\n",
journal_cur_seq(&c->journal));
+ bch2_prt_task_backtrace(out, current, 1, out->atomic ? GFP_ATOMIC : GFP_KERNEL);
+ }
return ret;
}
@@ -1769,7 +1782,7 @@ static int bch2_dev_alloc(struct bch_fs *c, unsigned dev_idx)
static int __bch2_dev_attach_bdev(struct bch_dev *ca, struct bch_sb_handle *sb,
struct printbuf *err)
{
- unsigned ret;
+ int ret;
if (bch2_dev_is_online(ca)) {
prt_printf(err, "already have device online in slot %u\n",
diff --git a/fs/bcachefs/super.h b/fs/bcachefs/super.h
index d13dbf2b8227..351dc5911645 100644
--- a/fs/bcachefs/super.h
+++ b/fs/bcachefs/super.h
@@ -16,6 +16,8 @@ extern const char * const bch2_dev_write_refs[];
struct bch_fs *bch2_dev_to_fs(dev_t);
struct bch_fs *bch2_uuid_to_fs(__uuid_t);
+void bch2_devs_list_to_text(struct printbuf *, struct bch_devs_list *);
+
bool bch2_dev_state_allowed(struct bch_fs *, struct bch_dev *,
enum bch_member_state, int,
struct printbuf *);