diff options
-rw-r--r-- | fs/bcachefs/bcachefs.h | 1 | ||||
-rw-r--r-- | fs/bcachefs/fs.c | 2 | ||||
-rw-r--r-- | fs/bcachefs/fsck.c | 53 | ||||
-rw-r--r-- | fs/bcachefs/recovery.c | 3 | ||||
-rw-r--r-- | fs/bcachefs/sb-errors_format.h | 4 | ||||
-rw-r--r-- | fs/bcachefs/sb-members.c | 29 | ||||
-rw-r--r-- | fs/bcachefs/sb-members.h | 1 | ||||
-rw-r--r-- | fs/bcachefs/super-io.c | 5 | ||||
-rw-r--r-- | fs/bcachefs/util.c | 5 | ||||
-rw-r--r-- | include/linux/workqueue.h | 12 | ||||
-rw-r--r-- | kernel/workqueue.c | 14 |
11 files changed, 103 insertions, 26 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/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; diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 6e30f275da77..e907c9bb840c 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -6,6 +6,7 @@ #ifndef _LINUX_WORKQUEUE_H #define _LINUX_WORKQUEUE_H +#include <linux/alloc_tag.h> #include <linux/timer.h> #include <linux/linkage.h> #include <linux/bitops.h> @@ -505,7 +506,8 @@ void workqueue_softirq_dead(unsigned int cpu); * Pointer to the allocated workqueue on success, %NULL on failure. */ __printf(1, 4) struct workqueue_struct * -alloc_workqueue(const char *fmt, unsigned int flags, int max_active, ...); +alloc_workqueue_noprof(const char *fmt, unsigned int flags, int max_active, ...); +#define alloc_workqueue(...) alloc_hooks(alloc_workqueue_noprof(__VA_ARGS__)) #ifdef CONFIG_LOCKDEP /** @@ -544,8 +546,8 @@ alloc_workqueue_lockdep_map(const char *fmt, unsigned int flags, int max_active, * Pointer to the allocated workqueue on success, %NULL on failure. */ #define alloc_ordered_workqueue_lockdep_map(fmt, flags, lockdep_map, args...) \ - alloc_workqueue_lockdep_map(fmt, WQ_UNBOUND | __WQ_ORDERED | (flags), \ - 1, lockdep_map, ##args) + alloc_hooks(alloc_workqueue_lockdep_map(fmt, WQ_UNBOUND | __WQ_ORDERED | (flags),\ + 1, lockdep_map, ##args)) #endif /** @@ -577,7 +579,9 @@ alloc_workqueue_lockdep_map(const char *fmt, unsigned int flags, int max_active, extern void destroy_workqueue(struct workqueue_struct *wq); -struct workqueue_attrs *alloc_workqueue_attrs(void); +struct workqueue_attrs *alloc_workqueue_attrs_noprof(void); +#define alloc_workqueue_attrs(...) alloc_hooks(alloc_workqueue_attrs_noprof(__VA_ARGS__)) + void free_workqueue_attrs(struct workqueue_attrs *attrs); int apply_workqueue_attrs(struct workqueue_struct *wq, const struct workqueue_attrs *attrs); diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 97f37b5bae66..bd195d4db685 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -4629,7 +4629,7 @@ void free_workqueue_attrs(struct workqueue_attrs *attrs) * * Return: The allocated new workqueue_attr on success. %NULL on failure. */ -struct workqueue_attrs *alloc_workqueue_attrs(void) +struct workqueue_attrs *alloc_workqueue_attrs_noprof(void) { struct workqueue_attrs *attrs; @@ -5682,12 +5682,12 @@ static struct workqueue_struct *__alloc_workqueue(const char *fmt, else wq_size = sizeof(*wq); - wq = kzalloc(wq_size, GFP_KERNEL); + wq = kzalloc_noprof(wq_size, GFP_KERNEL); if (!wq) return NULL; if (flags & WQ_UNBOUND) { - wq->unbound_attrs = alloc_workqueue_attrs(); + wq->unbound_attrs = alloc_workqueue_attrs_noprof(); if (!wq->unbound_attrs) goto err_free_wq; } @@ -5777,9 +5777,9 @@ err_destroy: } __printf(1, 4) -struct workqueue_struct *alloc_workqueue(const char *fmt, - unsigned int flags, - int max_active, ...) +struct workqueue_struct *alloc_workqueue_noprof(const char *fmt, + unsigned int flags, + int max_active, ...) { struct workqueue_struct *wq; va_list args; @@ -5794,7 +5794,7 @@ struct workqueue_struct *alloc_workqueue(const char *fmt, return wq; } -EXPORT_SYMBOL_GPL(alloc_workqueue); +EXPORT_SYMBOL_GPL(alloc_workqueue_noprof); #ifdef CONFIG_LOCKDEP __printf(1, 5) |