summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/bcachefs/bcachefs.h1
-rw-r--r--fs/bcachefs/fs.c2
-rw-r--r--fs/bcachefs/fsck.c53
-rw-r--r--fs/bcachefs/inode.c9
-rw-r--r--fs/bcachefs/recovery.c3
-rw-r--r--fs/bcachefs/sb-errors_format.h4
-rw-r--r--fs/bcachefs/sb-members.c29
-rw-r--r--fs/bcachefs/sb-members.h1
-rw-r--r--fs/bcachefs/super-io.c5
-rw-r--r--fs/bcachefs/util.c5
10 files changed, 96 insertions, 16 deletions
diff --git a/fs/bcachefs/bcachefs.h b/fs/bcachefs/bcachefs.h
index 004044e105ea..41c6d8865a74 100644
--- a/fs/bcachefs/bcachefs.h
+++ b/fs/bcachefs/bcachefs.h
@@ -819,6 +819,7 @@ struct bch_fs {
struct work_struct read_only_work;
struct bch_dev __rcu *devs[BCH_SB_MEMBERS_MAX];
+ struct bch_devs_mask devs_removed;
struct bch_accounting_mem accounting;
diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c
index 28a86bb644f7..f9bc99eb2d02 100644
--- a/fs/bcachefs/fs.c
+++ b/fs/bcachefs/fs.c
@@ -2563,9 +2563,11 @@ got_sb:
sb->s_shrink->seeks = 0;
+#if IS_ENABLED(CONFIG_UNICODE)
if (!bch2_fs_casefold_enabled(c))
sb->s_encoding = c->cf_encoding;
generic_set_sb_d_ops(sb);
+#endif
vinode = bch2_vfs_inode_get(c, BCACHEFS_ROOT_SUBVOL_INUM);
ret = PTR_ERR_OR_ZERO(vinode);
diff --git a/fs/bcachefs/fsck.c b/fs/bcachefs/fsck.c
index 62756f465e56..00afe0a3593f 100644
--- a/fs/bcachefs/fsck.c
+++ b/fs/bcachefs/fsck.c
@@ -1500,6 +1500,10 @@ static int check_key_has_inode(struct btree_trans *trans,
SPOS(k.k->p.inode, 0, k.k->p.snapshot),
POS(k.k->p.inode, U64_MAX),
0, k2, ret) {
+ if (k.k->type == KEY_TYPE_error ||
+ k.k->type == KEY_TYPE_hash_whiteout)
+ continue;
+
nr_keys++;
if (nr_keys <= 10) {
bch2_bkey_val_to_text(&buf, c, k2);
@@ -1512,9 +1516,11 @@ static int check_key_has_inode(struct btree_trans *trans,
if (ret)
goto err;
+ unsigned reconstruct_limit = iter->btree_id == BTREE_ID_extents ? 3 : 0;
+
if (nr_keys > 100)
prt_printf(&buf, "found > %u keys for this missing inode\n", nr_keys);
- else if (nr_keys > 10)
+ else if (nr_keys > reconstruct_limit)
prt_printf(&buf, "found %u keys for this missing inode\n", nr_keys);
if (!have_inode) {
@@ -1572,6 +1578,44 @@ reconstruct:
goto out;
}
+static int maybe_reconstruct_inum_btree(struct btree_trans *trans,
+ u64 inum, u32 snapshot,
+ enum btree_id btree)
+{
+ struct btree_iter iter;
+ struct bkey_s_c k;
+ int ret = 0;
+
+ for_each_btree_key_max_norestart(trans, iter, btree,
+ SPOS(inum, 0, snapshot),
+ POS(inum, U64_MAX),
+ 0, k, ret) {
+ ret = 1;
+ break;
+ }
+ bch2_trans_iter_exit(trans, &iter);
+
+ if (ret <= 0)
+ return ret;
+
+ if (fsck_err(trans, missing_inode_with_contents,
+ "inode %llu:%u type %s missing, but contents found: reconstruct?",
+ inum, snapshot,
+ btree == BTREE_ID_extents ? "reg" : "dir"))
+ return reconstruct_inode(trans, btree, snapshot, inum) ?:
+ bch2_trans_commit(trans, NULL, NULL, BCH_TRANS_COMMIT_no_enospc) ?:
+ bch_err_throw(trans->c, transaction_restart_commit);
+fsck_err:
+ return ret;
+}
+
+static int maybe_reconstruct_inum(struct btree_trans *trans,
+ u64 inum, u32 snapshot)
+{
+ return maybe_reconstruct_inum_btree(trans, inum, snapshot, BTREE_ID_extents) ?:
+ maybe_reconstruct_inum_btree(trans, inum, snapshot, BTREE_ID_dirents);
+}
+
static int check_i_sectors_notnested(struct btree_trans *trans, struct inode_walker *w)
{
struct bch_fs *c = trans->c;
@@ -2366,6 +2410,13 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter,
if (ret)
goto err;
+ if (!target->inodes.nr) {
+ ret = maybe_reconstruct_inum(trans, le64_to_cpu(d.v->d_inum),
+ d.k->p.snapshot);
+ if (ret)
+ return ret;
+ }
+
if (fsck_err_on(!target->inodes.nr,
trans, dirent_to_missing_inode,
"dirent points to missing inode:\n%s",
diff --git a/fs/bcachefs/inode.c b/fs/bcachefs/inode.c
index 1c1b6e4533fc..307fb0c95656 100644
--- a/fs/bcachefs/inode.c
+++ b/fs/bcachefs/inode.c
@@ -1018,6 +1018,7 @@ int bch2_inode_create(struct btree_trans *trans,
u64 start = le64_to_cpu(cursor->v.idx);
u64 pos = start;
+ u64 gen = 0;
bch2_trans_iter_init(trans, iter, BTREE_ID_inodes, POS(0, pos),
BTREE_ITER_all_snapshots|
@@ -1030,6 +1031,12 @@ again:
if (pos < iter->pos.offset)
goto found_slot;
+ if (bch2_snapshot_is_ancestor(trans->c, snapshot, k.k->p.snapshot) &&
+ k.k->type == KEY_TYPE_inode_generation) {
+ gen = le32_to_cpu(bkey_s_c_to_inode_generation(k).v->bi_generation);
+ goto found_slot;
+ }
+
/*
* We don't need to iterate over keys in every snapshot once
* we've found just one:
@@ -1064,7 +1071,7 @@ found_slot:
}
inode_u->bi_inum = k.k->p.offset;
- inode_u->bi_generation = le64_to_cpu(cursor->v.gen);
+ inode_u->bi_generation = max(gen, le64_to_cpu(cursor->v.gen));
cursor->v.idx = cpu_to_le64(k.k->p.offset + 1);
return 0;
}
diff --git a/fs/bcachefs/recovery.c b/fs/bcachefs/recovery.c
index d0b7e3a36a54..974f8bf9a574 100644
--- a/fs/bcachefs/recovery.c
+++ b/fs/bcachefs/recovery.c
@@ -1177,9 +1177,10 @@ int bch2_fs_initialize(struct bch_fs *c)
for_each_member_device(c, ca) {
m = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx);
SET_BCH_MEMBER_FREESPACE_INITIALIZED(m, false);
- ca->mi = bch2_mi_to_cpu(m);
}
+ bch2_sb_members_to_cpu(c);
+
bch2_write_super(c);
mutex_unlock(&c->sb_lock);
diff --git a/fs/bcachefs/sb-errors_format.h b/fs/bcachefs/sb-errors_format.h
index 3ecac2524118..dd4ee46606d7 100644
--- a/fs/bcachefs/sb-errors_format.h
+++ b/fs/bcachefs/sb-errors_format.h
@@ -158,6 +158,7 @@ enum bch_fsck_flags {
x(extent_ptrs_unwritten, 140, 0) \
x(extent_ptrs_written_and_unwritten, 141, 0) \
x(ptr_to_invalid_device, 142, 0) \
+ x(ptr_to_removed_device, 322, 0) \
x(ptr_to_duplicate_device, 143, 0) \
x(ptr_after_last_bucket, 144, 0) \
x(ptr_before_first_bucket, 145, 0) \
@@ -291,6 +292,7 @@ enum bch_fsck_flags {
x(inode_points_to_missing_dirent, 249, FSCK_AUTOFIX) \
x(inode_points_to_wrong_dirent, 250, FSCK_AUTOFIX) \
x(inode_bi_parent_nonzero, 251, 0) \
+ x(missing_inode_with_contents, 321, FSCK_AUTOFIX) \
x(dirent_to_missing_parent_subvol, 252, 0) \
x(dirent_not_visible_in_parent_subvol, 253, 0) \
x(subvol_fs_path_parent_wrong, 254, 0) \
@@ -332,7 +334,7 @@ enum bch_fsck_flags {
x(dirent_stray_data_after_cf_name, 305, 0) \
x(rebalance_work_incorrectly_set, 309, FSCK_AUTOFIX) \
x(rebalance_work_incorrectly_unset, 310, FSCK_AUTOFIX) \
- x(MAX, 321, 0)
+ x(MAX, 323, 0)
enum bch_sb_error_id {
#define x(t, n, ...) BCH_FSCK_ERR_##t = n,
diff --git a/fs/bcachefs/sb-members.c b/fs/bcachefs/sb-members.c
index 6245e342a8a8..f2abe92ca130 100644
--- a/fs/bcachefs/sb-members.c
+++ b/fs/bcachefs/sb-members.c
@@ -15,10 +15,15 @@ int bch2_dev_missing_bkey(struct bch_fs *c, struct bkey_s_c k, unsigned dev)
struct printbuf buf = PRINTBUF;
bch2_log_msg_start(c, &buf);
- prt_printf(&buf, "pointer to nonexistent device %u in key\n", dev);
+ bool removed = test_bit(dev, c->devs_removed.d);
+
+ prt_printf(&buf, "pointer to %s device %u in key\n",
+ removed ? "removed" : "nonexistent", dev);
bch2_bkey_val_to_text(&buf, c, k);
- bool print = bch2_count_fsck_err(c, ptr_to_invalid_device, &buf);
+ bool print = removed
+ ? bch2_count_fsck_err(c, ptr_to_removed_device, &buf)
+ : bch2_count_fsck_err(c, ptr_to_invalid_device, &buf);
int ret = bch2_run_explicit_recovery_pass(c, &buf,
BCH_RECOVERY_PASS_check_allocations, 0);
@@ -32,7 +37,9 @@ int bch2_dev_missing_bkey(struct bch_fs *c, struct bkey_s_c k, unsigned dev)
void bch2_dev_missing_atomic(struct bch_fs *c, unsigned dev)
{
if (dev != BCH_SB_MEMBER_INVALID)
- bch2_fs_inconsistent(c, "pointer to nonexistent device %u", dev);
+ bch2_fs_inconsistent(c, "pointer to %s device %u",
+ test_bit(dev, c->devs_removed.d)
+ ? "removed" : "nonexistent", dev);
}
void bch2_dev_bucket_missing(struct bch_dev *ca, u64 bucket)
@@ -413,6 +420,22 @@ void bch2_sb_members_from_cpu(struct bch_fs *c)
}
}
+void bch2_sb_members_to_cpu(struct bch_fs *c)
+{
+ for_each_member_device(c, ca) {
+ struct bch_member m = bch2_sb_member_get(c->disk_sb.sb, ca->dev_idx);
+ ca->mi = bch2_mi_to_cpu(&m);
+ }
+
+ struct bch_sb_field_members_v2 *mi2 = bch2_sb_field_get(c->disk_sb.sb, members_v2);
+ if (mi2)
+ for (unsigned i = 0; i < c->sb.nr_devices; i++) {
+ struct bch_member m = members_v2_get(mi2, i);
+ bool removed = uuid_equal(&m.uuid, &BCH_SB_MEMBER_DELETED_UUID);
+ mod_bit(i, c->devs_removed.d, removed);
+ }
+}
+
void bch2_dev_io_errors_to_text(struct printbuf *out, struct bch_dev *ca)
{
struct bch_fs *c = ca->fs;
diff --git a/fs/bcachefs/sb-members.h b/fs/bcachefs/sb-members.h
index 5dcc2017f85b..0d363a1cdd47 100644
--- a/fs/bcachefs/sb-members.h
+++ b/fs/bcachefs/sb-members.h
@@ -365,6 +365,7 @@ static inline struct bch_member_cpu bch2_mi_to_cpu(struct bch_member *mi)
}
void bch2_sb_members_from_cpu(struct bch_fs *);
+void bch2_sb_members_to_cpu(struct bch_fs *);
void bch2_dev_io_errors_to_text(struct printbuf *, struct bch_dev *);
void bch2_dev_errors_reset(struct bch_dev *);
diff --git a/fs/bcachefs/super-io.c b/fs/bcachefs/super-io.c
index 6c2e1d647403..85e460d10e9d 100644
--- a/fs/bcachefs/super-io.c
+++ b/fs/bcachefs/super-io.c
@@ -632,10 +632,7 @@ static void bch2_sb_update(struct bch_fs *c)
c->sb.btrees_lost_data = le64_to_cpu(ext->btrees_lost_data);
}
- for_each_member_device(c, ca) {
- struct bch_member m = bch2_sb_member_get(src, ca->dev_idx);
- ca->mi = bch2_mi_to_cpu(&m);
- }
+ bch2_sb_members_to_cpu(c);
}
static int __copy_super(struct bch_sb_handle *dst_handle, struct bch_sb *src)
diff --git a/fs/bcachefs/util.c b/fs/bcachefs/util.c
index 05b40debf211..7a4436fd4441 100644
--- a/fs/bcachefs/util.c
+++ b/fs/bcachefs/util.c
@@ -299,17 +299,12 @@ int bch2_save_backtrace(bch_stacktrace *stack, struct task_struct *task, unsigne
if (ret)
return ret;
- if (!down_read_trylock(&task->signal->exec_update_lock))
- return -1;
-
do {
nr_entries = stack_trace_save_tsk(task, stack->data, stack->size, skipnr + 1);
} while (nr_entries == stack->size &&
!(ret = darray_make_room_gfp(stack, stack->size * 2, gfp)));
stack->nr = nr_entries;
- up_read(&task->signal->exec_update_lock);
-
return ret;
#else
return 0;