]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - c_src/libbcachefs/journal_sb.c
move Rust sources to top level, C sources into c_src
[bcachefs-tools-debian] / c_src / libbcachefs / journal_sb.c
diff --git a/c_src/libbcachefs/journal_sb.c b/c_src/libbcachefs/journal_sb.c
new file mode 100644 (file)
index 0000000..ae4fb8c
--- /dev/null
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "bcachefs.h"
+#include "journal_sb.h"
+#include "darray.h"
+
+#include <linux/sort.h>
+
+/* BCH_SB_FIELD_journal: */
+
+static int u64_cmp(const void *_l, const void *_r)
+{
+       const u64 *l = _l;
+       const u64 *r = _r;
+
+       return cmp_int(*l, *r);
+}
+
+static int bch2_sb_journal_validate(struct bch_sb *sb,
+                                   struct bch_sb_field *f,
+                                   struct printbuf *err)
+{
+       struct bch_sb_field_journal *journal = field_to_type(f, journal);
+       struct bch_member m = bch2_sb_member_get(sb, sb->dev_idx);
+       int ret = -BCH_ERR_invalid_sb_journal;
+       unsigned nr;
+       unsigned i;
+       u64 *b;
+
+       nr = bch2_nr_journal_buckets(journal);
+       if (!nr)
+               return 0;
+
+       b = kmalloc_array(nr, sizeof(u64), GFP_KERNEL);
+       if (!b)
+               return -BCH_ERR_ENOMEM_sb_journal_validate;
+
+       for (i = 0; i < nr; i++)
+               b[i] = le64_to_cpu(journal->buckets[i]);
+
+       sort(b, nr, sizeof(u64), u64_cmp, NULL);
+
+       if (!b[0]) {
+               prt_printf(err, "journal bucket at sector 0");
+               goto err;
+       }
+
+       if (b[0] < le16_to_cpu(m.first_bucket)) {
+               prt_printf(err, "journal bucket %llu before first bucket %u",
+                      b[0], le16_to_cpu(m.first_bucket));
+               goto err;
+       }
+
+       if (b[nr - 1] >= le64_to_cpu(m.nbuckets)) {
+               prt_printf(err, "journal bucket %llu past end of device (nbuckets %llu)",
+                      b[nr - 1], le64_to_cpu(m.nbuckets));
+               goto err;
+       }
+
+       for (i = 0; i + 1 < nr; i++)
+               if (b[i] == b[i + 1]) {
+                       prt_printf(err, "duplicate journal buckets %llu", b[i]);
+                       goto err;
+               }
+
+       ret = 0;
+err:
+       kfree(b);
+       return ret;
+}
+
+static void bch2_sb_journal_to_text(struct printbuf *out, struct bch_sb *sb,
+                                   struct bch_sb_field *f)
+{
+       struct bch_sb_field_journal *journal = field_to_type(f, journal);
+       unsigned i, nr = bch2_nr_journal_buckets(journal);
+
+       prt_printf(out, "Buckets: ");
+       for (i = 0; i < nr; i++)
+               prt_printf(out, " %llu", le64_to_cpu(journal->buckets[i]));
+       prt_newline(out);
+}
+
+const struct bch_sb_field_ops bch_sb_field_ops_journal = {
+       .validate       = bch2_sb_journal_validate,
+       .to_text        = bch2_sb_journal_to_text,
+};
+
+struct u64_range {
+       u64     start;
+       u64     end;
+};
+
+static int u64_range_cmp(const void *_l, const void *_r)
+{
+       const struct u64_range *l = _l;
+       const struct u64_range *r = _r;
+
+       return cmp_int(l->start, r->start);
+}
+
+static int bch2_sb_journal_v2_validate(struct bch_sb *sb,
+                                   struct bch_sb_field *f,
+                                   struct printbuf *err)
+{
+       struct bch_sb_field_journal_v2 *journal = field_to_type(f, journal_v2);
+       struct bch_member m = bch2_sb_member_get(sb, sb->dev_idx);
+       int ret = -BCH_ERR_invalid_sb_journal;
+       unsigned nr;
+       unsigned i;
+       struct u64_range *b;
+
+       nr = bch2_sb_field_journal_v2_nr_entries(journal);
+       if (!nr)
+               return 0;
+
+       b = kmalloc_array(nr, sizeof(*b), GFP_KERNEL);
+       if (!b)
+               return -BCH_ERR_ENOMEM_sb_journal_v2_validate;
+
+       for (i = 0; i < nr; i++) {
+               b[i].start = le64_to_cpu(journal->d[i].start);
+               b[i].end = b[i].start + le64_to_cpu(journal->d[i].nr);
+       }
+
+       sort(b, nr, sizeof(*b), u64_range_cmp, NULL);
+
+       if (!b[0].start) {
+               prt_printf(err, "journal bucket at sector 0");
+               goto err;
+       }
+
+       if (b[0].start < le16_to_cpu(m.first_bucket)) {
+               prt_printf(err, "journal bucket %llu before first bucket %u",
+                      b[0].start, le16_to_cpu(m.first_bucket));
+               goto err;
+       }
+
+       if (b[nr - 1].end > le64_to_cpu(m.nbuckets)) {
+               prt_printf(err, "journal bucket %llu past end of device (nbuckets %llu)",
+                      b[nr - 1].end - 1, le64_to_cpu(m.nbuckets));
+               goto err;
+       }
+
+       for (i = 0; i + 1 < nr; i++) {
+               if (b[i].end > b[i + 1].start) {
+                       prt_printf(err, "duplicate journal buckets in ranges %llu-%llu, %llu-%llu",
+                              b[i].start, b[i].end, b[i + 1].start, b[i + 1].end);
+                       goto err;
+               }
+       }
+
+       ret = 0;
+err:
+       kfree(b);
+       return ret;
+}
+
+static void bch2_sb_journal_v2_to_text(struct printbuf *out, struct bch_sb *sb,
+                                   struct bch_sb_field *f)
+{
+       struct bch_sb_field_journal_v2 *journal = field_to_type(f, journal_v2);
+       unsigned i, nr = bch2_sb_field_journal_v2_nr_entries(journal);
+
+       prt_printf(out, "Buckets: ");
+       for (i = 0; i < nr; i++)
+               prt_printf(out, " %llu-%llu",
+                      le64_to_cpu(journal->d[i].start),
+                      le64_to_cpu(journal->d[i].start) + le64_to_cpu(journal->d[i].nr));
+       prt_newline(out);
+}
+
+const struct bch_sb_field_ops bch_sb_field_ops_journal_v2 = {
+       .validate       = bch2_sb_journal_v2_validate,
+       .to_text        = bch2_sb_journal_v2_to_text,
+};
+
+int bch2_journal_buckets_to_sb(struct bch_fs *c, struct bch_dev *ca,
+                              u64 *buckets, unsigned nr)
+{
+       struct bch_sb_field_journal_v2 *j;
+       unsigned i, dst = 0, nr_compacted = 1;
+
+       if (c)
+               lockdep_assert_held(&c->sb_lock);
+
+       if (!nr) {
+               bch2_sb_field_delete(&ca->disk_sb, BCH_SB_FIELD_journal);
+               bch2_sb_field_delete(&ca->disk_sb, BCH_SB_FIELD_journal_v2);
+               return 0;
+       }
+
+       for (i = 0; i + 1 < nr; i++)
+               if (buckets[i] + 1 != buckets[i + 1])
+                       nr_compacted++;
+
+       j = bch2_sb_field_resize(&ca->disk_sb, journal_v2,
+                        (sizeof(*j) + sizeof(j->d[0]) * nr_compacted) / sizeof(u64));
+       if (!j)
+               return -BCH_ERR_ENOSPC_sb_journal;
+
+       bch2_sb_field_delete(&ca->disk_sb, BCH_SB_FIELD_journal);
+
+       j->d[dst].start = cpu_to_le64(buckets[0]);
+       j->d[dst].nr    = cpu_to_le64(1);
+
+       for (i = 1; i < nr; i++) {
+               if (buckets[i] == buckets[i - 1] + 1) {
+                       le64_add_cpu(&j->d[dst].nr, 1);
+               } else {
+                       dst++;
+                       j->d[dst].start = cpu_to_le64(buckets[i]);
+                       j->d[dst].nr    = cpu_to_le64(1);
+               }
+       }
+
+       BUG_ON(dst + 1 != nr_compacted);
+       return 0;
+}