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