1 // SPDX-License-Identifier: GPL-2.0
4 #include "btree_update_interior.h"
7 #include "journal_io.h"
15 * Btree roots, and a few other things, are recovered from the journal after an
16 * unclean shutdown - but after a clean shutdown, to avoid having to read the
17 * journal, we can store them in the superblock.
19 * bch_sb_field_clean simply contains a list of journal entries, stored exactly
20 * as they would be in the journal:
23 int bch2_sb_clean_validate_late(struct bch_fs
*c
, struct bch_sb_field_clean
*clean
,
26 struct jset_entry
*entry
;
29 for (entry
= clean
->start
;
30 entry
< (struct jset_entry
*) vstruct_end(&clean
->field
);
31 entry
= vstruct_next(entry
)) {
32 if (vstruct_end(entry
) > vstruct_end(&clean
->field
)) {
33 bch_err(c
, "journal entry (u64s %u) overran end of superblock clean section (u64s %u) by %zu",
34 le16_to_cpu(entry
->u64s
), le32_to_cpu(clean
->field
.u64s
),
35 (u64
*) vstruct_end(entry
) - (u64
*) vstruct_end(&clean
->field
));
36 bch2_sb_error_count(c
, BCH_FSCK_ERR_sb_clean_entry_overrun
);
37 return -BCH_ERR_fsck_repair_unimplemented
;
40 ret
= bch2_journal_entry_validate(c
, NULL
, entry
,
41 le16_to_cpu(c
->disk_sb
.sb
->version
),
42 BCH_SB_BIG_ENDIAN(c
->disk_sb
.sb
),
51 static struct bkey_i
*btree_root_find(struct bch_fs
*c
,
52 struct bch_sb_field_clean
*clean
,
54 enum btree_id id
, unsigned *level
)
57 struct jset_entry
*entry
, *start
, *end
;
61 end
= vstruct_end(&clean
->field
);
64 end
= vstruct_last(j
);
67 for (entry
= start
; entry
< end
; entry
= vstruct_next(entry
))
68 if (entry
->type
== BCH_JSET_ENTRY_btree_root
&&
69 entry
->btree_id
== id
)
75 return ERR_PTR(-EINVAL
);
78 *level
= entry
->level
;
82 int bch2_verify_superblock_clean(struct bch_fs
*c
,
83 struct bch_sb_field_clean
**cleanp
,
87 struct bch_sb_field_clean
*clean
= *cleanp
;
88 struct printbuf buf1
= PRINTBUF
;
89 struct printbuf buf2
= PRINTBUF
;
92 if (mustfix_fsck_err_on(j
->seq
!= clean
->journal_seq
, c
,
93 sb_clean_journal_seq_mismatch
,
94 "superblock journal seq (%llu) doesn't match journal (%llu) after clean shutdown",
95 le64_to_cpu(clean
->journal_seq
),
96 le64_to_cpu(j
->seq
))) {
102 for (i
= 0; i
< BTREE_ID_NR
; i
++) {
103 struct bkey_i
*k1
, *k2
;
104 unsigned l1
= 0, l2
= 0;
106 k1
= btree_root_find(c
, clean
, NULL
, i
, &l1
);
107 k2
= btree_root_find(c
, NULL
, j
, i
, &l2
);
112 printbuf_reset(&buf1
);
113 printbuf_reset(&buf2
);
116 bch2_bkey_val_to_text(&buf1
, c
, bkey_i_to_s_c(k1
));
118 prt_printf(&buf1
, "(none)");
121 bch2_bkey_val_to_text(&buf2
, c
, bkey_i_to_s_c(k2
));
123 prt_printf(&buf2
, "(none)");
125 mustfix_fsck_err_on(!k1
|| !k2
||
128 k1
->k
.u64s
!= k2
->k
.u64s
||
129 memcmp(k1
, k2
, bkey_bytes(&k1
->k
)) ||
131 sb_clean_btree_root_mismatch
,
132 "superblock btree root %u doesn't match journal after clean shutdown\n"
134 "journal: l=%u %s\n", i
,
139 printbuf_exit(&buf2
);
140 printbuf_exit(&buf1
);
144 struct bch_sb_field_clean
*bch2_read_superblock_clean(struct bch_fs
*c
)
146 struct bch_sb_field_clean
*clean
, *sb_clean
;
149 mutex_lock(&c
->sb_lock
);
150 sb_clean
= bch2_sb_field_get(c
->disk_sb
.sb
, clean
);
152 if (fsck_err_on(!sb_clean
, c
,
154 "superblock marked clean but clean section not present")) {
155 SET_BCH_SB_CLEAN(c
->disk_sb
.sb
, false);
157 mutex_unlock(&c
->sb_lock
);
158 return ERR_PTR(-BCH_ERR_invalid_sb_clean
);
161 clean
= kmemdup(sb_clean
, vstruct_bytes(&sb_clean
->field
),
164 mutex_unlock(&c
->sb_lock
);
165 return ERR_PTR(-BCH_ERR_ENOMEM_read_superblock_clean
);
168 ret
= bch2_sb_clean_validate_late(c
, clean
, READ
);
171 mutex_unlock(&c
->sb_lock
);
175 mutex_unlock(&c
->sb_lock
);
179 mutex_unlock(&c
->sb_lock
);
183 void bch2_journal_super_entries_add_common(struct bch_fs
*c
,
184 struct jset_entry
**end
,
188 struct jset_entry_usage
*u
=
189 container_of(jset_entry_init(end
, sizeof(*u
)),
190 struct jset_entry_usage
, entry
);
192 u
->entry
.type
= BCH_JSET_ENTRY_usage
;
193 u
->entry
.btree_id
= BCH_FS_USAGE_key_version
;
194 u
->v
= cpu_to_le64(atomic64_read(&c
->key_version
));
197 for (unsigned i
= 0; i
< 2; i
++) {
198 struct jset_entry_clock
*clock
=
199 container_of(jset_entry_init(end
, sizeof(*clock
)),
200 struct jset_entry_clock
, entry
);
202 clock
->entry
.type
= BCH_JSET_ENTRY_clock
;
204 clock
->time
= cpu_to_le64(atomic64_read(&c
->io_clock
[i
].now
));
208 static int bch2_sb_clean_validate(struct bch_sb
*sb
, struct bch_sb_field
*f
,
209 enum bch_validate_flags flags
, struct printbuf
*err
)
211 struct bch_sb_field_clean
*clean
= field_to_type(f
, clean
);
213 if (vstruct_bytes(&clean
->field
) < sizeof(*clean
)) {
214 prt_printf(err
, "wrong size (got %zu should be %zu)",
215 vstruct_bytes(&clean
->field
), sizeof(*clean
));
216 return -BCH_ERR_invalid_sb_clean
;
219 for (struct jset_entry
*entry
= clean
->start
;
220 entry
!= vstruct_end(&clean
->field
);
221 entry
= vstruct_next(entry
)) {
222 if ((void *) vstruct_next(entry
) > vstruct_end(&clean
->field
)) {
223 prt_str(err
, "entry type ");
224 bch2_prt_jset_entry_type(err
, entry
->type
);
225 prt_str(err
, " overruns end of section");
226 return -BCH_ERR_invalid_sb_clean
;
233 static void bch2_sb_clean_to_text(struct printbuf
*out
, struct bch_sb
*sb
,
234 struct bch_sb_field
*f
)
236 struct bch_sb_field_clean
*clean
= field_to_type(f
, clean
);
237 struct jset_entry
*entry
;
239 prt_printf(out
, "flags: %x\n", le32_to_cpu(clean
->flags
));
240 prt_printf(out
, "journal_seq: %llu\n", le64_to_cpu(clean
->journal_seq
));
242 for (entry
= clean
->start
;
243 entry
!= vstruct_end(&clean
->field
);
244 entry
= vstruct_next(entry
)) {
245 if ((void *) vstruct_next(entry
) > vstruct_end(&clean
->field
))
248 if (entry
->type
== BCH_JSET_ENTRY_btree_keys
&&
252 bch2_journal_entry_to_text(out
, NULL
, entry
);
257 const struct bch_sb_field_ops bch_sb_field_ops_clean
= {
258 .validate
= bch2_sb_clean_validate
,
259 .to_text
= bch2_sb_clean_to_text
,
262 int bch2_fs_mark_dirty(struct bch_fs
*c
)
267 * Unconditionally write superblock, to verify it hasn't changed before
271 mutex_lock(&c
->sb_lock
);
272 SET_BCH_SB_CLEAN(c
->disk_sb
.sb
, false);
273 c
->disk_sb
.sb
->features
[0] |= cpu_to_le64(BCH_SB_FEATURES_ALWAYS
);
275 ret
= bch2_write_super(c
);
276 mutex_unlock(&c
->sb_lock
);
281 void bch2_fs_mark_clean(struct bch_fs
*c
)
283 struct bch_sb_field_clean
*sb_clean
;
284 struct jset_entry
*entry
;
288 mutex_lock(&c
->sb_lock
);
289 if (BCH_SB_CLEAN(c
->disk_sb
.sb
))
292 SET_BCH_SB_CLEAN(c
->disk_sb
.sb
, true);
294 c
->disk_sb
.sb
->compat
[0] |= cpu_to_le64(1ULL << BCH_COMPAT_alloc_info
);
295 c
->disk_sb
.sb
->compat
[0] |= cpu_to_le64(1ULL << BCH_COMPAT_alloc_metadata
);
296 c
->disk_sb
.sb
->features
[0] &= cpu_to_le64(~(1ULL << BCH_FEATURE_extents_above_btree_updates
));
297 c
->disk_sb
.sb
->features
[0] &= cpu_to_le64(~(1ULL << BCH_FEATURE_btree_updates_journalled
));
299 u64s
= sizeof(*sb_clean
) / sizeof(u64
) + c
->journal
.entry_u64s_reserved
;
301 sb_clean
= bch2_sb_field_resize(&c
->disk_sb
, clean
, u64s
);
303 bch_err(c
, "error resizing superblock while setting filesystem clean");
308 sb_clean
->journal_seq
= cpu_to_le64(atomic64_read(&c
->journal
.seq
));
310 /* Trying to catch outstanding bug: */
311 BUG_ON(le64_to_cpu(sb_clean
->journal_seq
) > S64_MAX
);
313 entry
= sb_clean
->start
;
314 bch2_journal_super_entries_add_common(c
, &entry
, 0);
315 entry
= bch2_btree_roots_to_journal_entries(c
, entry
, 0);
316 BUG_ON((void *) entry
> vstruct_end(&sb_clean
->field
));
319 vstruct_end(&sb_clean
->field
) - (void *) entry
);
322 * this should be in the write path, and we should be validating every
323 * superblock section:
325 ret
= bch2_sb_clean_validate_late(c
, sb_clean
, WRITE
);
327 bch_err(c
, "error writing marking filesystem clean: validate error");
331 bch2_journal_pos_from_member_info_set(c
);
335 mutex_unlock(&c
->sb_lock
);