Skip to content

Commit 84f1638

Browse files
author
Kent Overstreet
committed
bcachefs: bch_sb_field_downgrade
Add a new superblock section that contains a list of { minor version, recovery passes, errors_to_fix } that is - a list of recovery passes that must be run when downgrading past a given version, and a list of errors to silently fix. The upcoming disk accounting rewrite is not going to be fully compatible: we're going to have to regenerate accounting both when upgrading to the new version, and also from downgrading from the new version, since the new method of doing disk space accounting is a completely different architecture based on deltas, and synchronizing them for every jounal entry write to maintain compatibility is going to be too expensive and impractical. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
1 parent 8b16413 commit 84f1638

File tree

10 files changed

+255
-9
lines changed

10 files changed

+255
-9
lines changed

fs/bcachefs/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ bcachefs-y := \
7171
reflink.o \
7272
replicas.o \
7373
sb-clean.o \
74+
sb-downgrade.o \
7475
sb-errors.o \
7576
sb-members.o \
7677
siphash.o \

fs/bcachefs/bcachefs_format.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1220,7 +1220,8 @@ struct bch_sb_field {
12201220
x(counters, 10) \
12211221
x(members_v2, 11) \
12221222
x(errors, 12) \
1223-
x(ext, 13)
1223+
x(ext, 13) \
1224+
x(downgrade, 14)
12241225

12251226
enum bch_sb_field_type {
12261227
#define x(f, nr) BCH_SB_FIELD_##f = nr,
@@ -1638,6 +1639,18 @@ struct bch_sb_field_ext {
16381639
__le64 errors_silent[8];
16391640
};
16401641

1642+
struct bch_sb_field_downgrade_entry {
1643+
__le16 version;
1644+
__le64 recovery_passes[2];
1645+
__le16 nr_errors;
1646+
__le16 errors[] __counted_by(nr_errors);
1647+
} __packed __aligned(2);
1648+
1649+
struct bch_sb_field_downgrade {
1650+
struct bch_sb_field field;
1651+
struct bch_sb_field_downgrade_entry entries[];
1652+
};
1653+
16411654
/* Superblock: */
16421655

16431656
/*
@@ -1651,6 +1664,11 @@ struct bch_sb_field_ext {
16511664

16521665
#define RECOVERY_PASS_ALL_FSCK (1ULL << 63)
16531666

1667+
/*
1668+
* field 1: version name
1669+
* field 2: BCH_VERSION(major, minor)
1670+
* field 3: recovery passess required on upgrade
1671+
*/
16541672
#define BCH_METADATA_VERSIONS() \
16551673
x(bkey_renumber, BCH_VERSION(0, 10), \
16561674
RECOVERY_PASS_ALL_FSCK) \

fs/bcachefs/errcode.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
x(ENOSPC, ENOSPC_sb_members) \
9696
x(ENOSPC, ENOSPC_sb_members_v2) \
9797
x(ENOSPC, ENOSPC_sb_crypt) \
98+
x(ENOSPC, ENOSPC_sb_downgrade) \
9899
x(ENOSPC, ENOSPC_btree_slot) \
99100
x(ENOSPC, ENOSPC_snapshot_tree) \
100101
x(ENOENT, ENOENT_bkey_type_mismatch) \
@@ -219,6 +220,7 @@
219220
x(BCH_ERR_invalid_sb, invalid_sb_errors) \
220221
x(BCH_ERR_invalid_sb, invalid_sb_opt_compression) \
221222
x(BCH_ERR_invalid_sb, invalid_sb_ext) \
223+
x(BCH_ERR_invalid_sb, invalid_sb_downgrade) \
222224
x(BCH_ERR_invalid, invalid_bkey) \
223225
x(BCH_ERR_operation_blocked, nocow_lock_blocked) \
224226
x(EIO, btree_node_read_err) \

fs/bcachefs/recovery.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "recovery.h"
2828
#include "replicas.h"
2929
#include "sb-clean.h"
30+
#include "sb-downgrade.h"
3031
#include "snapshot.h"
3132
#include "subvolume.h"
3233
#include "super-io.h"
@@ -744,6 +745,27 @@ int bch2_fs_recovery(struct bch_fs *c)
744745
printbuf_exit(&buf);
745746
}
746747

748+
if (bch2_check_version_downgrade(c)) {
749+
struct printbuf buf = PRINTBUF;
750+
751+
prt_str(&buf, "Version downgrade required:\n");
752+
753+
__le64 passes = ext->recovery_passes_required[0];
754+
bch2_sb_set_downgrade(c,
755+
BCH_VERSION_MINOR(bcachefs_metadata_version_current),
756+
BCH_VERSION_MINOR(c->sb.version));
757+
passes = ext->recovery_passes_required[0] & ~passes;
758+
if (passes) {
759+
prt_str(&buf, " running recovery passes: ");
760+
prt_bitflags(&buf, bch2_recovery_passes,
761+
bch2_recovery_passes_from_stable(le64_to_cpu(passes)));
762+
}
763+
764+
bch_info(c, "%s", buf.buf);
765+
printbuf_exit(&buf);
766+
write_sb = true;
767+
}
768+
747769
if (check_version_upgrade(c))
748770
write_sb = true;
749771

@@ -1022,7 +1044,7 @@ int bch2_fs_initialize(struct bch_fs *c)
10221044
c->disk_sb.sb->compat[0] |= cpu_to_le64(1ULL << BCH_COMPAT_extents_above_btree_updates_done);
10231045
c->disk_sb.sb->compat[0] |= cpu_to_le64(1ULL << BCH_COMPAT_bformat_overflow_done);
10241046

1025-
bch2_sb_maybe_downgrade(c);
1047+
bch2_check_version_downgrade(c);
10261048

10271049
if (c->opts.version_upgrade != BCH_VERSION_UPGRADE_none) {
10281050
bch2_sb_upgrade(c, bcachefs_metadata_version_current);

fs/bcachefs/sb-clean.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,6 @@ int bch2_fs_mark_dirty(struct bch_fs *c)
332332

333333
mutex_lock(&c->sb_lock);
334334
SET_BCH_SB_CLEAN(c->disk_sb.sb, false);
335-
336-
bch2_sb_maybe_downgrade(c);
337335
c->disk_sb.sb->features[0] |= cpu_to_le64(BCH_SB_FEATURES_ALWAYS);
338336

339337
ret = bch2_write_super(c);

fs/bcachefs/sb-downgrade.c

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
/*
4+
* Superblock section that contains a list of recovery passes to run when
5+
* downgrading past a given version
6+
*/
7+
8+
#include "bcachefs.h"
9+
#include "darray.h"
10+
#include "recovery.h"
11+
#include "sb-downgrade.h"
12+
#include "sb-errors.h"
13+
#include "super-io.h"
14+
15+
/*
16+
* Downgrade table:
17+
* When dowgrading past certain versions, we need to run certain recovery passes
18+
* and fix certain errors:
19+
*
20+
* x(version, recovery_passes, errors...)
21+
*/
22+
23+
#define DOWNGRADE_TABLE()
24+
25+
struct downgrade_entry {
26+
u64 recovery_passes;
27+
u16 version;
28+
u16 nr_errors;
29+
const u16 *errors;
30+
};
31+
32+
#define x(ver, passes, ...) static const u16 ver_##errors[] = { __VA_ARGS__ };
33+
DOWNGRADE_TABLE()
34+
#undef x
35+
36+
static const struct downgrade_entry downgrade_table[] = {
37+
#define x(ver, passes, ...) { \
38+
.recovery_passes = passes, \
39+
.version = bcachefs_metadata_version_##ver,\
40+
.nr_errors = ARRAY_SIZE(ver_##errors), \
41+
.errors = ver_##errors, \
42+
},
43+
DOWNGRADE_TABLE()
44+
#undef x
45+
};
46+
47+
static inline const struct bch_sb_field_downgrade_entry *
48+
downgrade_entry_next_c(const struct bch_sb_field_downgrade_entry *e)
49+
{
50+
return (void *) &e->errors[le16_to_cpu(e->nr_errors)];
51+
}
52+
53+
#define for_each_downgrade_entry(_d, _i) \
54+
for (const struct bch_sb_field_downgrade_entry *_i = (_d)->entries; \
55+
(void *) _i < vstruct_end(&(_d)->field) && \
56+
(void *) &_i->errors[0] < vstruct_end(&(_d)->field); \
57+
_i = downgrade_entry_next_c(_i))
58+
59+
static int bch2_sb_downgrade_validate(struct bch_sb *sb, struct bch_sb_field *f,
60+
struct printbuf *err)
61+
{
62+
struct bch_sb_field_downgrade *e = field_to_type(f, downgrade);
63+
64+
for_each_downgrade_entry(e, i) {
65+
if (BCH_VERSION_MAJOR(le16_to_cpu(i->version)) !=
66+
BCH_VERSION_MAJOR(le16_to_cpu(sb->version))) {
67+
prt_printf(err, "downgrade entry with mismatched major version (%u != %u)",
68+
BCH_VERSION_MAJOR(le16_to_cpu(i->version)),
69+
BCH_VERSION_MAJOR(le16_to_cpu(sb->version)));
70+
return -BCH_ERR_invalid_sb_downgrade;
71+
}
72+
}
73+
74+
return 0;
75+
}
76+
77+
static void bch2_sb_downgrade_to_text(struct printbuf *out, struct bch_sb *sb,
78+
struct bch_sb_field *f)
79+
{
80+
struct bch_sb_field_downgrade *e = field_to_type(f, downgrade);
81+
82+
if (out->nr_tabstops <= 1)
83+
printbuf_tabstop_push(out, 16);
84+
85+
for_each_downgrade_entry(e, i) {
86+
prt_str(out, "version:");
87+
prt_tab(out);
88+
bch2_version_to_text(out, le16_to_cpu(i->version));
89+
prt_newline(out);
90+
91+
prt_str(out, "recovery passes:");
92+
prt_tab(out);
93+
prt_bitflags(out, bch2_recovery_passes,
94+
bch2_recovery_passes_from_stable(le64_to_cpu(i->recovery_passes[0])));
95+
prt_newline(out);
96+
97+
prt_str(out, "errors:");
98+
prt_tab(out);
99+
bool first = true;
100+
for (unsigned j = 0; j < le16_to_cpu(i->nr_errors); j++) {
101+
if (!first)
102+
prt_char(out, ',');
103+
first = false;
104+
unsigned e = le16_to_cpu(i->errors[j]);
105+
prt_str(out, e < BCH_SB_ERR_MAX ? bch2_sb_error_strs[e] : "(unknown)");
106+
}
107+
prt_newline(out);
108+
}
109+
}
110+
111+
const struct bch_sb_field_ops bch_sb_field_ops_downgrade = {
112+
.validate = bch2_sb_downgrade_validate,
113+
.to_text = bch2_sb_downgrade_to_text,
114+
};
115+
116+
int bch2_sb_downgrade_update(struct bch_fs *c)
117+
{
118+
darray_char table = {};
119+
int ret = 0;
120+
121+
for (const struct downgrade_entry *src = downgrade_table;
122+
src < downgrade_table + ARRAY_SIZE(downgrade_table);
123+
src++) {
124+
if (BCH_VERSION_MAJOR(src->version) != BCH_VERSION_MAJOR(le16_to_cpu(c->disk_sb.sb->version)))
125+
continue;
126+
127+
struct bch_sb_field_downgrade_entry *dst;
128+
unsigned bytes = sizeof(*dst) + sizeof(dst->errors[0]) * src->nr_errors;
129+
130+
ret = darray_make_room(&table, bytes);
131+
if (ret)
132+
goto out;
133+
134+
dst = (void *) &darray_top(table);
135+
dst->version = cpu_to_le16(src->version);
136+
dst->recovery_passes[0] = cpu_to_le64(src->recovery_passes);
137+
dst->recovery_passes[1] = 0;
138+
dst->nr_errors = cpu_to_le16(src->nr_errors);
139+
for (unsigned i = 0; i < src->nr_errors; i++)
140+
dst->errors[i] = cpu_to_le16(src->errors[i]);
141+
142+
table.nr += bytes;
143+
}
144+
145+
struct bch_sb_field_downgrade *d = bch2_sb_field_get(c->disk_sb.sb, downgrade);
146+
147+
unsigned sb_u64s = DIV_ROUND_UP(sizeof(*d) + table.nr, sizeof(u64));
148+
149+
if (d && le32_to_cpu(d->field.u64s) > sb_u64s)
150+
goto out;
151+
152+
d = bch2_sb_field_resize(&c->disk_sb, downgrade, sb_u64s);
153+
if (!d) {
154+
ret = -BCH_ERR_ENOSPC_sb_downgrade;
155+
goto out;
156+
}
157+
158+
memcpy(d->entries, table.data, table.nr);
159+
memset_u64s_tail(d->entries, 0, table.nr);
160+
out:
161+
darray_exit(&table);
162+
return ret;
163+
}
164+
165+
void bch2_sb_set_downgrade(struct bch_fs *c, unsigned new_minor, unsigned old_minor)
166+
{
167+
struct bch_sb_field_downgrade *d = bch2_sb_field_get(c->disk_sb.sb, downgrade);
168+
if (!d)
169+
return;
170+
171+
struct bch_sb_field_ext *ext = bch2_sb_field_get(c->disk_sb.sb, ext);
172+
173+
for_each_downgrade_entry(d, i) {
174+
unsigned minor = BCH_VERSION_MINOR(le16_to_cpu(i->version));
175+
if (new_minor < minor && minor <= old_minor) {
176+
ext->recovery_passes_required[0] |= i->recovery_passes[0];
177+
ext->recovery_passes_required[1] |= i->recovery_passes[1];
178+
179+
for (unsigned j = 0; j < le16_to_cpu(i->nr_errors); j++) {
180+
unsigned e = le16_to_cpu(i->errors[j]);
181+
if (e < BCH_SB_ERR_MAX)
182+
__set_bit(e, c->sb.errors_silent);
183+
if (e < sizeof(ext->errors_silent) * 8)
184+
ext->errors_silent[e / 64] |= cpu_to_le64(BIT_ULL(e % 64));
185+
}
186+
}
187+
}
188+
}

fs/bcachefs/sb-downgrade.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _BCACHEFS_SB_DOWNGRADE_H
3+
#define _BCACHEFS_SB_DOWNGRADE_H
4+
5+
extern const struct bch_sb_field_ops bch_sb_field_ops_downgrade;
6+
7+
int bch2_sb_downgrade_update(struct bch_fs *);
8+
void bch2_sb_set_downgrade(struct bch_fs *, unsigned, unsigned);
9+
10+
#endif /* _BCACHEFS_SB_DOWNGRADE_H */

fs/bcachefs/sb-errors.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ static void bch2_sb_error_id_to_text(struct printbuf *out, enum bch_sb_error_id
2020

2121
static inline unsigned bch2_sb_field_errors_nr_entries(struct bch_sb_field_errors *e)
2222
{
23-
return e
24-
? (bch2_sb_field_bytes(&e->field) - sizeof(*e)) / sizeof(e->entries[0])
25-
: 0;
23+
return bch2_sb_field_nr_entries(e);
2624
}
2725

2826
static inline unsigned bch2_sb_field_errors_u64s(unsigned nr)

fs/bcachefs/super-io.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "replicas.h"
1414
#include "quota.h"
1515
#include "sb-clean.h"
16+
#include "sb-downgrade.h"
1617
#include "sb-errors.h"
1718
#include "sb-members.h"
1819
#include "super-io.h"
@@ -939,6 +940,7 @@ int bch2_write_super(struct bch_fs *c)
939940
bch2_sb_members_from_cpu(c);
940941
bch2_sb_members_cpy_v2_v1(&c->disk_sb);
941942
bch2_sb_errors_from_cpu(c);
943+
bch2_sb_downgrade_update(c);
942944

943945
for_each_online_member(ca, c, i)
944946
bch2_sb_from_fs(c, ca);
@@ -1062,8 +1064,10 @@ void __bch2_check_set_feature(struct bch_fs *c, unsigned feat)
10621064
}
10631065

10641066
/* Downgrade if superblock is at a higher version than currently supported: */
1065-
void bch2_sb_maybe_downgrade(struct bch_fs *c)
1067+
bool bch2_check_version_downgrade(struct bch_fs *c)
10661068
{
1069+
bool ret = bcachefs_metadata_version_current < c->sb.version;
1070+
10671071
lockdep_assert_held(&c->sb_lock);
10681072

10691073
/*
@@ -1077,12 +1081,17 @@ void bch2_sb_maybe_downgrade(struct bch_fs *c)
10771081
if (c->sb.version_min > bcachefs_metadata_version_current)
10781082
c->disk_sb.sb->version_min = cpu_to_le16(bcachefs_metadata_version_current);
10791083
c->disk_sb.sb->compat[0] &= cpu_to_le64((1ULL << BCH_COMPAT_NR) - 1);
1084+
return ret;
10801085
}
10811086

10821087
void bch2_sb_upgrade(struct bch_fs *c, unsigned new_version)
10831088
{
10841089
lockdep_assert_held(&c->sb_lock);
10851090

1091+
if (BCH_VERSION_MAJOR(new_version) >
1092+
BCH_VERSION_MAJOR(le16_to_cpu(c->disk_sb.sb->version)))
1093+
bch2_sb_field_resize(&c->disk_sb, downgrade, 0);
1094+
10861095
c->disk_sb.sb->version = cpu_to_le16(new_version);
10871096
c->disk_sb.sb->features[0] |= cpu_to_le64(BCH_SB_FEATURES_ALL);
10881097
}

fs/bcachefs/super-io.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ static inline void bch2_check_set_feature(struct bch_fs *c, unsigned feat)
9393
__bch2_check_set_feature(c, feat);
9494
}
9595

96-
void bch2_sb_maybe_downgrade(struct bch_fs *);
96+
bool bch2_check_version_downgrade(struct bch_fs *);
9797
void bch2_sb_upgrade(struct bch_fs *, unsigned);
9898

9999
void bch2_sb_field_to_text(struct printbuf *, struct bch_sb *,

0 commit comments

Comments
 (0)