233 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#include "bcachefs.h"4#include "journal_sb.h"5#include "darray.h"6 7#include <linux/sort.h>8 9/* BCH_SB_FIELD_journal: */10 11static int u64_cmp(const void *_l, const void *_r)12{13 const u64 *l = _l;14 const u64 *r = _r;15 16 return cmp_int(*l, *r);17}18 19static int bch2_sb_journal_validate(struct bch_sb *sb, struct bch_sb_field *f,20 enum bch_validate_flags flags, struct printbuf *err)21{22 struct bch_sb_field_journal *journal = field_to_type(f, journal);23 struct bch_member m = bch2_sb_member_get(sb, sb->dev_idx);24 int ret = -BCH_ERR_invalid_sb_journal;25 unsigned nr;26 unsigned i;27 u64 *b;28 29 nr = bch2_nr_journal_buckets(journal);30 if (!nr)31 return 0;32 33 b = kmalloc_array(nr, sizeof(u64), GFP_KERNEL);34 if (!b)35 return -BCH_ERR_ENOMEM_sb_journal_validate;36 37 for (i = 0; i < nr; i++)38 b[i] = le64_to_cpu(journal->buckets[i]);39 40 sort(b, nr, sizeof(u64), u64_cmp, NULL);41 42 if (!b[0]) {43 prt_printf(err, "journal bucket at sector 0");44 goto err;45 }46 47 if (b[0] < le16_to_cpu(m.first_bucket)) {48 prt_printf(err, "journal bucket %llu before first bucket %u",49 b[0], le16_to_cpu(m.first_bucket));50 goto err;51 }52 53 if (b[nr - 1] >= le64_to_cpu(m.nbuckets)) {54 prt_printf(err, "journal bucket %llu past end of device (nbuckets %llu)",55 b[nr - 1], le64_to_cpu(m.nbuckets));56 goto err;57 }58 59 for (i = 0; i + 1 < nr; i++)60 if (b[i] == b[i + 1]) {61 prt_printf(err, "duplicate journal buckets %llu", b[i]);62 goto err;63 }64 65 ret = 0;66err:67 kfree(b);68 return ret;69}70 71static void bch2_sb_journal_to_text(struct printbuf *out, struct bch_sb *sb,72 struct bch_sb_field *f)73{74 struct bch_sb_field_journal *journal = field_to_type(f, journal);75 unsigned i, nr = bch2_nr_journal_buckets(journal);76 77 prt_printf(out, "Buckets: ");78 for (i = 0; i < nr; i++)79 prt_printf(out, " %llu", le64_to_cpu(journal->buckets[i]));80 prt_newline(out);81}82 83const struct bch_sb_field_ops bch_sb_field_ops_journal = {84 .validate = bch2_sb_journal_validate,85 .to_text = bch2_sb_journal_to_text,86};87 88struct u64_range {89 u64 start;90 u64 end;91};92 93static int u64_range_cmp(const void *_l, const void *_r)94{95 const struct u64_range *l = _l;96 const struct u64_range *r = _r;97 98 return cmp_int(l->start, r->start);99}100 101static int bch2_sb_journal_v2_validate(struct bch_sb *sb, struct bch_sb_field *f,102 enum bch_validate_flags flags, struct printbuf *err)103{104 struct bch_sb_field_journal_v2 *journal = field_to_type(f, journal_v2);105 struct bch_member m = bch2_sb_member_get(sb, sb->dev_idx);106 int ret = -BCH_ERR_invalid_sb_journal;107 u64 sum = 0;108 unsigned nr;109 unsigned i;110 struct u64_range *b;111 112 nr = bch2_sb_field_journal_v2_nr_entries(journal);113 if (!nr)114 return 0;115 116 b = kmalloc_array(nr, sizeof(*b), GFP_KERNEL);117 if (!b)118 return -BCH_ERR_ENOMEM_sb_journal_v2_validate;119 120 for (i = 0; i < nr; i++) {121 b[i].start = le64_to_cpu(journal->d[i].start);122 b[i].end = b[i].start + le64_to_cpu(journal->d[i].nr);123 124 if (b[i].end <= b[i].start) {125 prt_printf(err, "journal buckets entry with bad nr: %llu+%llu",126 le64_to_cpu(journal->d[i].start),127 le64_to_cpu(journal->d[i].nr));128 goto err;129 }130 131 sum += le64_to_cpu(journal->d[i].nr);132 }133 134 sort(b, nr, sizeof(*b), u64_range_cmp, NULL);135 136 if (!b[0].start) {137 prt_printf(err, "journal bucket at sector 0");138 goto err;139 }140 141 if (b[0].start < le16_to_cpu(m.first_bucket)) {142 prt_printf(err, "journal bucket %llu before first bucket %u",143 b[0].start, le16_to_cpu(m.first_bucket));144 goto err;145 }146 147 if (b[nr - 1].end > le64_to_cpu(m.nbuckets)) {148 prt_printf(err, "journal bucket %llu past end of device (nbuckets %llu)",149 b[nr - 1].end - 1, le64_to_cpu(m.nbuckets));150 goto err;151 }152 153 for (i = 0; i + 1 < nr; i++) {154 if (b[i].end > b[i + 1].start) {155 prt_printf(err, "duplicate journal buckets in ranges %llu-%llu, %llu-%llu",156 b[i].start, b[i].end, b[i + 1].start, b[i + 1].end);157 goto err;158 }159 }160 161 if (sum > UINT_MAX) {162 prt_printf(err, "too many journal buckets: %llu > %u", sum, UINT_MAX);163 goto err;164 }165 166 ret = 0;167err:168 kfree(b);169 return ret;170}171 172static void bch2_sb_journal_v2_to_text(struct printbuf *out, struct bch_sb *sb,173 struct bch_sb_field *f)174{175 struct bch_sb_field_journal_v2 *journal = field_to_type(f, journal_v2);176 unsigned i, nr = bch2_sb_field_journal_v2_nr_entries(journal);177 178 prt_printf(out, "Buckets: ");179 for (i = 0; i < nr; i++)180 prt_printf(out, " %llu-%llu",181 le64_to_cpu(journal->d[i].start),182 le64_to_cpu(journal->d[i].start) + le64_to_cpu(journal->d[i].nr));183 prt_newline(out);184}185 186const struct bch_sb_field_ops bch_sb_field_ops_journal_v2 = {187 .validate = bch2_sb_journal_v2_validate,188 .to_text = bch2_sb_journal_v2_to_text,189};190 191int bch2_journal_buckets_to_sb(struct bch_fs *c, struct bch_dev *ca,192 u64 *buckets, unsigned nr)193{194 struct bch_sb_field_journal_v2 *j;195 unsigned i, dst = 0, nr_compacted = 1;196 197 if (c)198 lockdep_assert_held(&c->sb_lock);199 200 if (!nr) {201 bch2_sb_field_delete(&ca->disk_sb, BCH_SB_FIELD_journal);202 bch2_sb_field_delete(&ca->disk_sb, BCH_SB_FIELD_journal_v2);203 return 0;204 }205 206 for (i = 0; i + 1 < nr; i++)207 if (buckets[i] + 1 != buckets[i + 1])208 nr_compacted++;209 210 j = bch2_sb_field_resize(&ca->disk_sb, journal_v2,211 (sizeof(*j) + sizeof(j->d[0]) * nr_compacted) / sizeof(u64));212 if (!j)213 return -BCH_ERR_ENOSPC_sb_journal;214 215 bch2_sb_field_delete(&ca->disk_sb, BCH_SB_FIELD_journal);216 217 j->d[dst].start = cpu_to_le64(buckets[0]);218 j->d[dst].nr = cpu_to_le64(1);219 220 for (i = 1; i < nr; i++) {221 if (buckets[i] == buckets[i - 1] + 1) {222 le64_add_cpu(&j->d[dst].nr, 1);223 } else {224 dst++;225 j->d[dst].start = cpu_to_le64(buckets[i]);226 j->d[dst].nr = cpu_to_le64(1);227 }228 }229 230 BUG_ON(dst + 1 != nr_compacted);231 return 0;232}233