Skip to content

Commit 54541c1

Browse files
author
Kent Overstreet
committed
bcachefs: Fix race in bch2_write_super()
bch2_write_super() was looping over online devices multiple times - dropping and retaking io_ref each time. This meant it could race with device removal; it could increment the sequence number on a device but fail to write it - and then if the device was re-added, it would get confused the next time around thinking a superblock write was silently dropped. Fix this by taking io_ref once, and stashing pointers to online devices in a darray. Signed-off-by: Kent Overstreet <[email protected]>
1 parent 71dac24 commit 54541c1

File tree

1 file changed

+32
-15
lines changed

1 file changed

+32
-15
lines changed

fs/bcachefs/super-io.c

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,7 @@ int bch2_write_super(struct bch_fs *c)
923923
struct bch_devs_mask sb_written;
924924
bool wrote, can_mount_without_written, can_mount_with_written;
925925
unsigned degraded_flags = BCH_FORCE_IF_DEGRADED;
926+
DARRAY(struct bch_dev *) online_devices = {};
926927
int ret = 0;
927928

928929
trace_and_count(c, write_super, c, _RET_IP_);
@@ -935,15 +936,24 @@ int bch2_write_super(struct bch_fs *c)
935936
closure_init_stack(cl);
936937
memset(&sb_written, 0, sizeof(sb_written));
937938

939+
for_each_online_member(c, ca) {
940+
ret = darray_push(&online_devices, ca);
941+
if (bch2_fs_fatal_err_on(ret, c, "%s: error allocating online devices", __func__)) {
942+
percpu_ref_put(&ca->io_ref);
943+
goto out;
944+
}
945+
percpu_ref_get(&ca->io_ref);
946+
}
947+
938948
/* Make sure we're using the new magic numbers: */
939949
c->disk_sb.sb->magic = BCHFS_MAGIC;
940950
c->disk_sb.sb->layout.magic = BCHFS_MAGIC;
941951

942952
le64_add_cpu(&c->disk_sb.sb->seq, 1);
943953

944954
struct bch_sb_field_members_v2 *mi = bch2_sb_field_get(c->disk_sb.sb, members_v2);
945-
for_each_online_member(c, ca)
946-
__bch2_members_v2_get_mut(mi, ca->dev_idx)->seq = c->disk_sb.sb->seq;
955+
darray_for_each(online_devices, ca)
956+
__bch2_members_v2_get_mut(mi, (*ca)->dev_idx)->seq = c->disk_sb.sb->seq;
947957
c->disk_sb.sb->write_time = cpu_to_le64(ktime_get_real_seconds());
948958

949959
if (test_bit(BCH_FS_error, &c->flags))
@@ -959,16 +969,15 @@ int bch2_write_super(struct bch_fs *c)
959969
bch2_sb_errors_from_cpu(c);
960970
bch2_sb_downgrade_update(c);
961971

962-
for_each_online_member(c, ca)
963-
bch2_sb_from_fs(c, ca);
972+
darray_for_each(online_devices, ca)
973+
bch2_sb_from_fs(c, (*ca));
964974

965-
for_each_online_member(c, ca) {
975+
darray_for_each(online_devices, ca) {
966976
printbuf_reset(&err);
967977

968-
ret = bch2_sb_validate(&ca->disk_sb, &err, WRITE);
978+
ret = bch2_sb_validate(&(*ca)->disk_sb, &err, WRITE);
969979
if (ret) {
970980
bch2_fs_inconsistent(c, "sb invalid before write: %s", err.buf);
971-
percpu_ref_put(&ca->io_ref);
972981
goto out;
973982
}
974983
}
@@ -995,16 +1004,18 @@ int bch2_write_super(struct bch_fs *c)
9951004
return -BCH_ERR_sb_not_downgraded;
9961005
}
9971006

998-
for_each_online_member(c, ca) {
999-
__set_bit(ca->dev_idx, sb_written.d);
1000-
ca->sb_write_error = 0;
1007+
darray_for_each(online_devices, ca) {
1008+
__set_bit((*ca)->dev_idx, sb_written.d);
1009+
(*ca)->sb_write_error = 0;
10011010
}
10021011

1003-
for_each_online_member(c, ca)
1004-
read_back_super(c, ca);
1012+
darray_for_each(online_devices, ca)
1013+
read_back_super(c, *ca);
10051014
closure_sync(cl);
10061015

1007-
for_each_online_member(c, ca) {
1016+
darray_for_each(online_devices, cap) {
1017+
struct bch_dev *ca = *cap;
1018+
10081019
if (ca->sb_write_error)
10091020
continue;
10101021

@@ -1031,17 +1042,20 @@ int bch2_write_super(struct bch_fs *c)
10311042

10321043
do {
10331044
wrote = false;
1034-
for_each_online_member(c, ca)
1045+
darray_for_each(online_devices, cap) {
1046+
struct bch_dev *ca = *cap;
10351047
if (!ca->sb_write_error &&
10361048
sb < ca->disk_sb.sb->layout.nr_superblocks) {
10371049
write_one_super(c, ca, sb);
10381050
wrote = true;
10391051
}
1052+
}
10401053
closure_sync(cl);
10411054
sb++;
10421055
} while (wrote);
10431056

1044-
for_each_online_member(c, ca) {
1057+
darray_for_each(online_devices, cap) {
1058+
struct bch_dev *ca = *cap;
10451059
if (ca->sb_write_error)
10461060
__clear_bit(ca->dev_idx, sb_written.d);
10471061
else
@@ -1077,6 +1091,9 @@ int bch2_write_super(struct bch_fs *c)
10771091
out:
10781092
/* Make new options visible after they're persistent: */
10791093
bch2_sb_update(c);
1094+
darray_for_each(online_devices, ca)
1095+
percpu_ref_put(&(*ca)->io_ref);
1096+
darray_exit(&online_devices);
10801097
printbuf_exit(&err);
10811098
return ret;
10821099
}

0 commit comments

Comments
 (0)