From 1f269ad1aababd4c2af7ac8775a367153f58a763 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Mon, 10 Nov 2025 23:25:16 -0500 Subject: [PATCH] Update bcachefs sources to 7604cb70e590 fixup! bcachefs: bch_member.device_name Signed-off-by: Kent Overstreet --- .bcachefs_revision | 2 +- include/linux/string.h | 11 ++ libbcachefs/data/extents.c | 5 + libbcachefs/data/extents.h | 8 + libbcachefs/data/migrate.c | 69 ++++--- libbcachefs/data/rebalance.c | 25 ++- libbcachefs/errcode.h | 2 + libbcachefs/fs/check.c | 34 ---- libbcachefs/fs/str_hash.c | 60 +++++- libbcachefs/fs/str_hash.h | 33 +++- libbcachefs/fs/xattr.c | 11 +- libbcachefs/init/chardev.c | 4 +- libbcachefs/init/dev.c | 101 +++++++--- libbcachefs/init/fs.c | 339 +++++++++++++++++--------------- libbcachefs/sb/io.c | 312 ++++++++++++++--------------- libbcachefs/sb/members.c | 48 +++++ libbcachefs/sb/members.h | 2 + libbcachefs/sb/members_format.h | 7 + 18 files changed, 636 insertions(+), 437 deletions(-) diff --git a/.bcachefs_revision b/.bcachefs_revision index f8d42c9e..9c90c788 100644 --- a/.bcachefs_revision +++ b/.bcachefs_revision @@ -1 +1 @@ -99a43760af01b64e736624b984240eefcc821148 +7604cb70e5909aed4647acf0199c60ac5776dc7c diff --git a/include/linux/string.h b/include/linux/string.h index f6ce8dde..3a7610bb 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -3,6 +3,7 @@ #include #include +#include #include /* for size_t */ extern size_t strlcpy(char *dest, const char *src, size_t size); @@ -15,4 +16,14 @@ extern void * memscan(void *,int, size_t); #define kstrndup(s, n, gfp) strndup(s, n) #define kstrdup(s, gfp) strdup(s) +#define strtomem_pad(dest, src, pad) do { \ + const size_t _dest_len = ARRAY_SIZE(dest); \ + const size_t _src_len = __builtin_object_size(src, 1); \ + \ + BUILD_BUG_ON(!__builtin_constant_p(_dest_len) || \ + _dest_len == (size_t)-1); \ + memcpy_and_pad(dest, _dest_len, src, \ + strnlen(src, min(_src_len, _dest_len)), pad); \ +} while (0) + #endif /* _LINUX_STRING_H_ */ diff --git a/libbcachefs/data/extents.c b/libbcachefs/data/extents.c index f35e0c9a..0c6e9715 100644 --- a/libbcachefs/data/extents.c +++ b/libbcachefs/data/extents.c @@ -1007,6 +1007,11 @@ void bch2_bkey_drop_ptr(const struct bch_fs *c, struct bkey_s k, struct bch_exte } } +void bch2_bkey_drop_device_noerror(const struct bch_fs *c, struct bkey_s k, unsigned dev) +{ + bch2_bkey_drop_ptrs_noerror(k, p, entry, p.ptr.dev == dev); +} + void bch2_bkey_drop_device(const struct bch_fs *c, struct bkey_s k, unsigned dev) { bch2_bkey_drop_ptrs(k, p, entry, p.ptr.dev == dev); diff --git a/libbcachefs/data/extents.h b/libbcachefs/data/extents.h index c63628a4..fe01012a 100644 --- a/libbcachefs/data/extents.h +++ b/libbcachefs/data/extents.h @@ -481,6 +481,13 @@ static inline bool bkey_extent_is_direct_data(const struct bkey *k) } } +static inline bool bkey_is_user_data(const struct bkey *k) +{ + return k->type == KEY_TYPE_extent || + k->type == KEY_TYPE_inline_data || + k->type == KEY_TYPE_reservation; +} + static inline bool bkey_extent_is_inline_data(const struct bkey *k) { return k->type == KEY_TYPE_inline_data || @@ -625,6 +632,7 @@ void bch2_extent_ptr_decoded_append(const struct bch_fs *, struct bkey_i *, void bch2_bkey_drop_ptr_noerror(const struct bch_fs *, struct bkey_s, struct bch_extent_ptr *); void bch2_bkey_drop_ptr(const struct bch_fs *, struct bkey_s, struct bch_extent_ptr *); +void bch2_bkey_drop_device_noerror(const struct bch_fs *, struct bkey_s, unsigned); void bch2_bkey_drop_device(const struct bch_fs *, struct bkey_s, unsigned); void bch2_bkey_drop_ec(const struct bch_fs *, struct bkey_i *k, unsigned); diff --git a/libbcachefs/data/migrate.c b/libbcachefs/data/migrate.c index 101258c4..7e1665ac 100644 --- a/libbcachefs/data/migrate.c +++ b/libbcachefs/data/migrate.c @@ -28,40 +28,59 @@ #include "init/progress.h" -static int drop_dev_ptrs(struct bch_fs *c, struct bkey_s k, unsigned dev_idx, - unsigned flags, struct printbuf *err, bool metadata) +static struct bkey_i *drop_dev_ptrs(struct btree_trans *trans, struct bkey_s_c k, unsigned dev_idx, + unsigned flags, struct printbuf *err) { + struct bch_fs *c = trans->c; + bool metadata = bkey_is_btree_ptr(k.k); unsigned replicas = metadata ? c->opts.metadata_replicas : c->opts.data_replicas; unsigned lost = metadata ? BCH_FORCE_IF_METADATA_LOST : BCH_FORCE_IF_DATA_LOST; unsigned degraded = metadata ? BCH_FORCE_IF_METADATA_DEGRADED : BCH_FORCE_IF_DATA_DEGRADED; - unsigned nr_good; - bch2_bkey_drop_device(c, k, dev_idx); + if (!bch2_bkey_has_device_c(c, k, dev_idx)) + return NULL; - nr_good = bch2_bkey_durability(c, k.s_c); + struct bkey_i *n = bch2_trans_kmalloc(trans, bkey_bytes(k.k) + + sizeof(struct bch_extent_rebalance)); + if (IS_ERR(n)) + return n; + bkey_reassemble(n, k); + + if (!metadata) + bch2_bkey_drop_device(c, bkey_i_to_s(n), dev_idx); + else + bch2_bkey_drop_device_noerror(c, bkey_i_to_s(n), dev_idx); + + unsigned nr_good = bch2_bkey_durability(c, bkey_i_to_s_c(n)); if ((!nr_good && !(flags & lost)) || (nr_good < replicas && !(flags & degraded))) { prt_str(err, "cannot drop device without degrading/losing data\n "); - bch2_bkey_val_to_text(err, c, k.s_c); + bch2_bkey_val_to_text(err, c, k); prt_newline(err); - return bch_err_throw(c, remove_would_lose_data); + return ERR_PTR(bch_err_throw(c, remove_would_lose_data)); } - return 0; + if (n->k.type != KEY_TYPE_error) { + struct bch_inode_opts opts; + int ret = bch2_bkey_get_io_opts(trans, NULL, k, &opts) ?: + bch2_bkey_set_needs_rebalance(c, &opts, n, + SET_NEEDS_REBALANCE_opt_change, 0); + if (ret) + return ERR_PTR(ret); + } + + return n; } static int drop_btree_ptrs(struct btree_trans *trans, struct btree_iter *iter, struct btree *b, unsigned dev_idx, unsigned flags, struct printbuf *err) { - struct bch_fs *c = trans->c; + struct bkey_i *n = errptr_try(drop_dev_ptrs(trans, bkey_i_to_s_c(&b->key), dev_idx, flags, err)); + if (!n) + return 0; - struct bkey_buf k __cleanup(bch2_bkey_buf_exit); - bch2_bkey_buf_init(&k); - bch2_bkey_buf_copy(&k, &b->key); - - return drop_dev_ptrs(c, bkey_i_to_s(k.k), dev_idx, flags, err, true) ?: - bch2_btree_node_update_key(trans, iter, b, k.k, 0, false); + return bch2_btree_node_update_key(trans, iter, b, n, 0, false); } static int bch2_dev_usrdata_drop_key(struct btree_trans *trans, @@ -70,20 +89,10 @@ static int bch2_dev_usrdata_drop_key(struct btree_trans *trans, unsigned dev_idx, unsigned flags, struct printbuf *err) { - struct bch_fs *c = trans->c; - - if (!bch2_bkey_has_device_c(c, k, dev_idx)) + struct bkey_i *n = errptr_try(drop_dev_ptrs(trans, k, dev_idx, flags, err)); + if (!n) return 0; - struct bkey_i *n = - errptr_try(bch2_bkey_make_mut(trans, iter, &k, BTREE_UPDATE_internal_snapshot_node)); - - try(drop_dev_ptrs(c, bkey_i_to_s(n), dev_idx, flags, err, false)); - - struct bch_inode_opts opts; - try(bch2_bkey_get_io_opts(trans, NULL, k, &opts)); - try(bch2_bkey_set_needs_rebalance(c, &opts, n, SET_NEEDS_REBALANCE_opt_change, 0)); - /* * Since we're not inserting through an extent iterator * (BTREE_ITER_all_snapshots iterators aren't extent iterators), @@ -92,7 +101,7 @@ static int bch2_dev_usrdata_drop_key(struct btree_trans *trans, */ if (bkey_deleted(&n->k)) n->k.size = 0; - return 0; + return bch2_trans_update(trans, iter, n, BTREE_UPDATE_internal_snapshot_node); } static int bch2_dev_btree_drop_key(struct btree_trans *trans, @@ -150,9 +159,7 @@ static int dev_metadata_drop_one(struct btree_trans *trans, return 1; try(bch2_progress_update_iter(trans, progress, iter, "dropping metadata")); - - if (bch2_bkey_has_device_c(trans->c, bkey_i_to_s_c(&b->key), dev_idx)) - try(drop_btree_ptrs(trans, iter, b, dev_idx, flags, err)); + try(drop_btree_ptrs(trans, iter, b, dev_idx, flags, err)); return 0; } diff --git a/libbcachefs/data/rebalance.c b/libbcachefs/data/rebalance.c index bcc041a5..f19188f6 100644 --- a/libbcachefs/data/rebalance.c +++ b/libbcachefs/data/rebalance.c @@ -292,15 +292,32 @@ int bch2_bkey_get_io_opts(struct btree_trans *trans, struct per_snapshot_io_opts *snapshot_opts, struct bkey_s_c k, struct bch_inode_opts *opts) { + struct bch_fs *c = trans->c; enum io_opts_mode { IO_OPTS_metadata, IO_OPTS_reflink, IO_OPTS_user, - } mode = bkey_is_btree_ptr(k.k) ? IO_OPTS_metadata : - !bkey_is_indirect(k.k) ? IO_OPTS_user : - IO_OPTS_reflink; + } mode; - struct bch_fs *c = trans->c; + if (bkey_is_btree_ptr(k.k)) + mode = IO_OPTS_metadata; + else if (bkey_is_indirect(k.k)) + mode = IO_OPTS_reflink; + else if (bkey_is_user_data(k.k)) { + mode = IO_OPTS_user; + + if (unlikely(!k.k->p.snapshot)) { + CLASS(printbuf, buf)(); + bch2_bkey_val_to_text(&buf, trans->c, k); + WARN(1, "user data key with snapshot == 0\n%s", buf.buf); + bch2_inode_opts_get(c, opts, false); + return 0; + } + } else { + /* KEY_TYPE_error? */ + bch2_inode_opts_get(c, opts, false); + return 0; + } if (!snapshot_opts) { bch2_inode_opts_get(c, opts, mode == IO_OPTS_metadata); diff --git a/libbcachefs/errcode.h b/libbcachefs/errcode.h index 167cc923..9ba6e647 100644 --- a/libbcachefs/errcode.h +++ b/libbcachefs/errcode.h @@ -214,6 +214,8 @@ x(EINVAL, device_already_online) \ x(EINVAL, filesystem_uuid_already_open) \ x(EINVAL, insufficient_devices_to_start) \ + x(EINVAL, chardev_init_error) \ + x(EINVAL, sysfs_init_error) \ x(EINVAL, invalid) \ x(EINVAL, internal_fsck_err) \ x(EINVAL, opt_parse_error) \ diff --git a/libbcachefs/fs/check.c b/libbcachefs/fs/check.c index 24f6b89f..a38e65d8 100644 --- a/libbcachefs/fs/check.c +++ b/libbcachefs/fs/check.c @@ -1606,40 +1606,6 @@ static int check_dirent(struct btree_trans *trans, struct btree_iter *iter, struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k); - /* check casefold */ - if (fsck_err_on(d.v->d_casefold != !!hash_info->cf_encoding, - trans, dirent_casefold_mismatch, - "dirent casefold does not match dir casefold\n%s", - (printbuf_reset(&buf), - bch2_bkey_val_to_text(&buf, c, k), - buf.buf))) { - subvol_inum dir_inum = { .subvol = d.v->d_type == DT_SUBVOL - ? le32_to_cpu(d.v->d_parent_subvol) - : 0, - }; - u64 target = d.v->d_type == DT_SUBVOL - ? le32_to_cpu(d.v->d_child_subvol) - : le64_to_cpu(d.v->d_inum); - struct qstr name = bch2_dirent_get_name(d); - - struct bkey_i_dirent *new_d = - errptr_try(bch2_dirent_create_key(trans, hash_info, dir_inum, - d.v->d_type, &name, NULL, target)); - - new_d->k.p.inode = d.k->p.inode; - new_d->k.p.snapshot = d.k->p.snapshot; - - CLASS(btree_iter_uninit, dup_iter)(trans); - return bch2_hash_delete_at(trans, - bch2_dirent_hash_desc, hash_info, iter, - BTREE_UPDATE_internal_snapshot_node) ?: - bch2_str_hash_repair_key(trans, s, - &bch2_dirent_hash_desc, hash_info, - iter, bkey_i_to_s_c(&new_d->k_i), - &dup_iter, bkey_s_c_null, - need_second_pass); - } - if (d.v->d_type == DT_SUBVOL) { try(check_dirent_to_subvol(trans, iter, d)); } else { diff --git a/libbcachefs/fs/str_hash.c b/libbcachefs/fs/str_hash.c index 3d99c914..20377856 100644 --- a/libbcachefs/fs/str_hash.c +++ b/libbcachefs/fs/str_hash.c @@ -333,6 +333,55 @@ fsck_err: return ret; } +/* XXX: should move to dirent.c */ +static int str_hash_check_dirent(struct btree_trans *trans, + struct snapshots_seen *s, + struct bch_hash_info *hash_info, + struct btree_iter *iter, struct bkey_s_c k, + bool *updated_before_k_pos) +{ + struct bch_fs *c = trans->c; + struct bkey_s_c_dirent d = bkey_s_c_to_dirent(k); + + CLASS(printbuf, buf)(); + if (ret_fsck_err_on(d.v->d_casefold != !!hash_info->cf_encoding, + trans, dirent_casefold_mismatch, + "dirent casefold does not match dir casefold\n%s", + (bch2_bkey_val_to_text(&buf, c, k), + buf.buf))) { + subvol_inum dir_inum = { .subvol = d.v->d_type == DT_SUBVOL + ? le32_to_cpu(d.v->d_parent_subvol) + : 0, + }; + u64 target = d.v->d_type == DT_SUBVOL + ? le32_to_cpu(d.v->d_child_subvol) + : le64_to_cpu(d.v->d_inum); + struct qstr name = bch2_dirent_get_name(d); + + struct bkey_i_dirent *new_d = + errptr_try(bch2_dirent_create_key(trans, hash_info, dir_inum, + d.v->d_type, &name, NULL, target)); + + new_d->k.p.inode = d.k->p.inode; + new_d->k.p.snapshot = d.k->p.snapshot; + + CLASS(btree_iter_uninit, dup_iter)(trans); + try(bch2_hash_delete_at(trans, + bch2_dirent_hash_desc, hash_info, iter, + BTREE_UPDATE_internal_snapshot_node)); + try(bch2_str_hash_repair_key(trans, s, + &bch2_dirent_hash_desc, hash_info, + iter, bkey_i_to_s_c(&new_d->k_i), + &dup_iter, bkey_s_c_null, + updated_before_k_pos)); + + /* skip this key, it moved */ + return 1; + } + + return 0; +} + int __bch2_str_hash_check_key(struct btree_trans *trans, struct snapshots_seen *s, const struct bch_hash_desc *desc, @@ -370,6 +419,15 @@ int __bch2_str_hash_check_key(struct btree_trans *trans, return str_hash_bad_hash(trans, s, desc, hash_info, k_iter, hash_k, updated_before_k_pos, &iter, hash); } + if (ret) + return ret; - return ret; + switch (k.k->type) { + case KEY_TYPE_dirent: + try(str_hash_check_dirent(trans, s, hash_info, k_iter, hash_k, + updated_before_k_pos)); + break; + } + + return 0; } diff --git a/libbcachefs/fs/str_hash.h b/libbcachefs/fs/str_hash.h index 1ed829cc..1cba97b4 100644 --- a/libbcachefs/fs/str_hash.h +++ b/libbcachefs/fs/str_hash.h @@ -404,6 +404,27 @@ int __bch2_str_hash_check_key(struct btree_trans *, struct btree_iter *, struct bkey_s_c, bool *); +static inline bool str_hash_key_needs_check(const struct bch_hash_desc *desc, + struct bch_hash_info *info, + struct bkey_s_c k) +{ + if (k.k->type != desc->key_type) + return false; + + if (unlikely(desc->hash_bkey(info, k) != k.k->p.offset)) + return true; + + switch (k.k->type) { + case KEY_TYPE_dirent: + if (bkey_s_c_to_dirent(k).v->d_casefold != !!info->cf_encoding) + return true; + break; + } + + return false; +} + + static inline int bch2_str_hash_check_key(struct btree_trans *trans, struct snapshots_seen *s, const struct bch_hash_desc *desc, @@ -411,14 +432,10 @@ static inline int bch2_str_hash_check_key(struct btree_trans *trans, struct btree_iter *k_iter, struct bkey_s_c hash_k, bool *updated_before_k_pos) { - if (hash_k.k->type != desc->key_type) - return 0; - - if (likely(desc->hash_bkey(hash_info, hash_k) == hash_k.k->p.offset)) - return 0; - - return __bch2_str_hash_check_key(trans, s, desc, hash_info, k_iter, hash_k, - updated_before_k_pos); + return str_hash_key_needs_check(desc, hash_info, hash_k) + ? __bch2_str_hash_check_key(trans, s, desc, hash_info, k_iter, hash_k, + updated_before_k_pos) + : 0; } #endif /* _BCACHEFS_STR_HASH_H */ diff --git a/libbcachefs/fs/xattr.c b/libbcachefs/fs/xattr.c index 5e7c541d..d7dd53fd 100644 --- a/libbcachefs/fs/xattr.c +++ b/libbcachefs/fs/xattr.c @@ -473,11 +473,8 @@ static int inode_opt_set_fn(struct btree_trans *trans, { struct inode_opt_set *s = p; - if (s->id == Inode_opt_casefold) { - int ret = bch2_inode_set_casefold(trans, inode_inum(inode), bi, s->v); - if (ret) - return ret; - } + if (s->id == Inode_opt_casefold) + try(bch2_inode_set_casefold(trans, inode_inum(inode), bi, s->v)); if (s->id == Inode_opt_inodes_32bit && !bch2_request_incompat_feature(trans->c, bcachefs_metadata_version_31bit_dirent_offset)) { @@ -485,9 +482,7 @@ static int inode_opt_set_fn(struct btree_trans *trans, * Make sure the dir is empty, as otherwise we'd need to * rehash everything and update the dirent keys. */ - int ret = bch2_empty_dir_trans(trans, inode_inum(inode)); - if (ret < 0) - return ret; + try(bch2_empty_dir_trans(trans, inode_inum(inode))); if (s->defined) bi->bi_flags |= BCH_INODE_31bit_dirent_offset; diff --git a/libbcachefs/init/chardev.c b/libbcachefs/init/chardev.c index 8186dc68..939ae500 100644 --- a/libbcachefs/init/chardev.c +++ b/libbcachefs/init/chardev.c @@ -784,13 +784,13 @@ int bch2_fs_chardev_init(struct bch_fs *c) { c->minor = idr_alloc(&bch_chardev_minor, c, 0, 0, GFP_KERNEL); if (c->minor < 0) - return c->minor; + return bch_err_throw(c, chardev_init_error); c->chardev = device_create(&bch_chardev_class, NULL, MKDEV(bch_chardev_major, c->minor), c, "bcachefs%u-ctl", c->minor); if (IS_ERR(c->chardev)) - return PTR_ERR(c->chardev); + return bch_err_throw(c, chardev_init_error); return 0; } diff --git a/libbcachefs/init/dev.c b/libbcachefs/init/dev.c index 6a291dd3..960df67f 100644 --- a/libbcachefs/init/dev.c +++ b/libbcachefs/init/dev.c @@ -397,21 +397,56 @@ int bch2_dev_alloc(struct bch_fs *c, unsigned dev_idx) return 0; } -static int __bch2_dev_attach_bdev(struct bch_dev *ca, struct bch_sb_handle *sb, - struct printbuf *err) +static int read_file_str(const char *path, darray_char *ret) +{ + /* + * TODO: unify this with read_file_str() in bcachefs-tools tools-util.c + * + * Unfortunately, we don't have openat() in kernel + */ +#ifdef __KERNEL__ + struct file *file = errptr_try(filp_open(path, O_RDONLY, 0)); + + loff_t pos = 0; + ssize_t r = kernel_read(file, ret->data, ret->size, &pos); + fput(file); +#else + int fd = open(path, O_RDONLY); + if (fd < 0) + return fd; + + ssize_t r = read(fd, ret->data, ret->size); + close(fd); +#endif + + if (r > 0) { + ret->nr = r; + if (ret->data[r - 1]) { + /* null terminate */ + if (ret->nr >= ret->size) + ret->nr = ret->size -1; + ret->data[ret->nr] = '\0'; + } + } + return r < 0 ? r : 0; +} + +static int __bch2_dev_attach_bdev(struct bch_fs *c, struct bch_dev *ca, + struct bch_sb_handle *sb, struct printbuf *err) { if (bch2_dev_is_online(ca)) { - prt_printf(err, "already have device online in slot %u\n", - sb->sb->dev_idx); + prt_printf(err, "Cannot attach %s: already have device %s online in slot %u\n", + sb->sb_name, ca->name, sb->sb->dev_idx); return bch_err_throw(ca->fs, device_already_online); } if (get_capacity(sb->bdev->bd_disk) < ca->mi.bucket_size * ca->mi.nbuckets) { - prt_printf(err, "cannot online: device too small (capacity %llu filesystem size %llu nbuckets %llu)\n", - get_capacity(sb->bdev->bd_disk), - ca->mi.bucket_size * ca->mi.nbuckets, - ca->mi.nbuckets); + prt_printf(err, "Cannot online %s: device too small (capacity %llu filesystem size %llu nbuckets %llu)\n", + sb->sb_name, + get_capacity(sb->bdev->bd_disk), + ca->mi.bucket_size * ca->mi.nbuckets, + ca->mi.nbuckets); return bch_err_throw(ca->fs, device_size_too_small); } @@ -424,6 +459,26 @@ static int __bch2_dev_attach_bdev(struct bch_dev *ca, struct bch_sb_handle *sb, prt_bdevname(&name, sb->bdev); strscpy(ca->name, name.buf, sizeof(ca->name)); + CLASS(darray_char, model)(); + darray_make_room(&model, 128); + + CLASS(printbuf, model_path)(); + prt_printf(&model_path, "/sys/block/%s/device/model", name.buf); + + read_file_str(model_path.buf, &model); + + if (model.nr && model.data[model.nr - 1] == '\n') + model.data[--model.nr] = '\0'; + + scoped_guard(mutex, &c->sb_lock) { + struct bch_member *m = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx); + + strtomem_pad(m->device_name, name.buf, '\0'); + + if (model.nr) + strtomem_pad(m->device_model, model.data, '\0'); + } + /* Commit: */ ca->disk_sb = *sb; memset(sb, 0, sizeof(*sb)); @@ -459,7 +514,7 @@ int bch2_dev_attach_bdev(struct bch_fs *c, struct bch_sb_handle *sb, struct prin struct bch_dev *ca = bch2_dev_locked(c, sb->sb->dev_idx); - try(__bch2_dev_attach_bdev(ca, sb, err)); + try(__bch2_dev_attach_bdev(c, ca, sb, err)); set_bit(ca->dev_idx, c->online_devs.d); @@ -691,7 +746,7 @@ err: int bch2_dev_add(struct bch_fs *c, const char *path, struct printbuf *err) { struct bch_opts opts = bch2_opts_empty(); - struct bch_sb_handle sb = {}; + struct bch_sb_handle sb __cleanup(bch2_free_super) = {}; struct bch_dev *ca = NULL; CLASS(printbuf, label)(); int ret = 0; @@ -736,7 +791,7 @@ int bch2_dev_add(struct bch_fs *c, const char *path, struct printbuf *err) goto err; } - ret = __bch2_dev_attach_bdev(ca, &sb, err); + ret = __bch2_dev_attach_bdev(c, ca, &sb, err); if (ret) goto err; @@ -830,7 +885,6 @@ out: err: if (ca) bch2_dev_free(ca); - bch2_free_super(&sb); goto out; err_late: ca = NULL; @@ -841,9 +895,7 @@ err_late: int bch2_dev_online(struct bch_fs *c, const char *path, struct printbuf *err) { struct bch_opts opts = bch2_opts_empty(); - struct bch_sb_handle sb = { NULL }; - struct bch_dev *ca; - unsigned dev_idx; + struct bch_sb_handle sb __cleanup(bch2_free_super) = {}; int ret; guard(rwsem_write)(&c->state_lock); @@ -854,24 +906,22 @@ int bch2_dev_online(struct bch_fs *c, const char *path, struct printbuf *err) return ret; } - dev_idx = sb.sb->dev_idx; + unsigned dev_idx = sb.sb->dev_idx; ret = bch2_dev_in_fs(&c->disk_sb, &sb, &c->opts); if (ret) { prt_printf(err, "device not a member of fs: %s\n", bch2_err_str(ret)); - goto err; + return ret; } - ret = bch2_dev_attach_bdev(c, &sb, err); - if (ret) - goto err; + try(bch2_dev_attach_bdev(c, &sb, err)); - ca = bch2_dev_locked(c, dev_idx); + struct bch_dev *ca = bch2_dev_locked(c, dev_idx); ret = bch2_trans_mark_dev_sb(c, ca, BTREE_TRIGGER_transactional); if (ret) { prt_printf(err, "bch2_trans_mark_dev_sb() error: %s\n", bch2_err_str(ret)); - goto err; + return ret; } if (ca->mi.state == BCH_MEMBER_STATE_rw) @@ -881,7 +931,7 @@ int bch2_dev_online(struct bch_fs *c, const char *path, struct printbuf *err) ret = bch2_dev_freespace_init(c, ca, 0, ca->mi.nbuckets); if (ret) { prt_printf(err, "bch2_dev_freespace_init() error: %s\n", bch2_err_str(ret)); - goto err; + return ret; } } @@ -889,7 +939,7 @@ int bch2_dev_online(struct bch_fs *c, const char *path, struct printbuf *err) ret = bch2_dev_journal_alloc(ca, false); if (ret) { prt_printf(err, "bch2_dev_journal_alloc() error: %s\n", bch2_err_str(ret)); - goto err; + return ret; } } @@ -900,9 +950,6 @@ int bch2_dev_online(struct bch_fs *c, const char *path, struct printbuf *err) } return 0; -err: - bch2_free_super(&sb); - return ret; } int bch2_dev_offline(struct bch_fs *c, struct bch_dev *ca, int flags, struct printbuf *err) diff --git a/libbcachefs/init/fs.c b/libbcachefs/init/fs.c index c42ab265..abf67029 100644 --- a/libbcachefs/init/fs.c +++ b/libbcachefs/init/fs.c @@ -122,6 +122,7 @@ static bool should_print_loglevel(struct bch_fs *c, const char *fmt) void bch2_print_str(struct bch_fs *c, const char *prefix, const char *str) { + BUG_ON(!str); if (!should_print_loglevel(c, prefix)) return; @@ -708,52 +709,37 @@ void bch2_fs_stop(struct bch_fs *c) static int bch2_fs_online(struct bch_fs *c) { - int ret = 0; - lockdep_assert_held(&bch2_fs_list_lock); if (c->sb.multi_device && - __bch2_uuid_to_fs(c->sb.uuid)) { - bch_err(c, "filesystem UUID already open"); + __bch2_uuid_to_fs(c->sb.uuid)) return bch_err_throw(c, filesystem_uuid_already_open); - } - ret = bch2_fs_chardev_init(c); - if (ret) { - bch_err(c, "error creating character device"); - return ret; - } + try(bch2_fs_chardev_init(c)); bch2_fs_debug_init(c); - ret = (c->sb.multi_device - ? kobject_add(&c->kobj, NULL, "%pU", c->sb.user_uuid.b) - : kobject_add(&c->kobj, NULL, "%s", c->name)) ?: + if ((c->sb.multi_device + ? kobject_add(&c->kobj, NULL, "%pU", c->sb.user_uuid.b) + : kobject_add(&c->kobj, NULL, "%s", c->name)) ?: kobject_add(&c->internal, &c->kobj, "internal") ?: kobject_add(&c->opts_dir, &c->kobj, "options") ?: #ifndef CONFIG_BCACHEFS_NO_LATENCY_ACCT kobject_add(&c->time_stats, &c->kobj, "time_stats") ?: #endif kobject_add(&c->counters_kobj, &c->kobj, "counters") ?: - bch2_opts_create_sysfs_files(&c->opts_dir, OPT_FS); - if (ret) { - bch_err(c, "error creating sysfs objects"); - return ret; - } + bch2_opts_create_sysfs_files(&c->opts_dir, OPT_FS)) + return bch_err_throw(c, sysfs_init_error); guard(rwsem_write)(&c->state_lock); - for_each_member_device(c, ca) { - ret = bch2_dev_sysfs_online(c, ca); - if (ret) { - bch_err(c, "error creating sysfs objects"); - return ret; - } - } + for_each_member_device(c, ca) + if (bch2_dev_sysfs_online(c, ca)) + return bch_err_throw(c, sysfs_init_error); BUG_ON(!list_empty(&c->list)); list_add(&c->list, &bch2_fs_list); - return ret; + return 0; } int bch2_fs_init_rw(struct bch_fs *c) @@ -788,7 +774,7 @@ int bch2_fs_init_rw(struct bch_fs *c) return 0; } -static bool check_version_upgrade(struct bch_fs *c) +static bool check_version_upgrade(struct bch_fs *c, struct printbuf *out) { unsigned latest_version = bcachefs_metadata_version_current; unsigned latest_compatible = min(latest_version, @@ -818,26 +804,24 @@ static bool check_version_upgrade(struct bch_fs *c) } if (new_version > old_version) { - CLASS(printbuf, buf)(); - if (old_version < bcachefs_metadata_required_upgrade_below) - prt_str(&buf, "Version upgrade required:\n"); + prt_str_indented(out, "Version upgrade required:\n"); if (old_version != c->sb.version) { - prt_str(&buf, "Version upgrade from "); - bch2_version_to_text(&buf, c->sb.version_upgrade_complete); - prt_str(&buf, " to "); - bch2_version_to_text(&buf, c->sb.version); - prt_str(&buf, " incomplete\n"); + prt_str_indented(out, "Version upgrade from "); + bch2_version_to_text(out, c->sb.version_upgrade_complete); + prt_str_indented(out, " to "); + bch2_version_to_text(out, c->sb.version); + prt_str_indented(out, " incomplete\n"); } - prt_printf(&buf, "Doing %s version upgrade from ", + prt_printf(out, "Doing %s version upgrade from ", BCH_VERSION_MAJOR(old_version) != BCH_VERSION_MAJOR(new_version) ? "incompatible" : "compatible"); - bch2_version_to_text(&buf, old_version); - prt_str(&buf, " to "); - bch2_version_to_text(&buf, new_version); - prt_newline(&buf); + bch2_version_to_text(out, old_version); + prt_str_indented(out, " to "); + bch2_version_to_text(out, new_version); + prt_newline(out); struct bch_sb_field_ext *ext = bch2_sb_field_get(c->disk_sb.sb, ext); __le64 passes = ext->recovery_passes_required[0]; @@ -845,26 +829,23 @@ static bool check_version_upgrade(struct bch_fs *c) passes = ext->recovery_passes_required[0] & ~passes; if (passes) { - prt_str(&buf, " running recovery passes: "); - prt_bitflags(&buf, bch2_recovery_passes, + prt_str_indented(out, "Upgrade requires recovery passes: "); + prt_bitflags(out, bch2_recovery_passes, bch2_recovery_passes_from_stable(le64_to_cpu(passes))); + prt_newline(out); } - bch_notice(c, "%s", buf.buf); ret = true; } if (new_version > c->sb.version_incompat_allowed && c->opts.version_upgrade == BCH_VERSION_UPGRADE_incompatible) { - CLASS(printbuf, buf)(); + prt_str_indented(out, "Now allowing incompatible features up to "); + bch2_version_to_text(out, new_version); + prt_str_indented(out, ", previously allowed up to "); + bch2_version_to_text(out, c->sb.version_incompat_allowed); + prt_newline(out); - prt_str(&buf, "Now allowing incompatible features up to "); - bch2_version_to_text(&buf, new_version); - prt_str(&buf, ", previously allowed up to "); - bch2_version_to_text(&buf, c->sb.version_incompat_allowed); - prt_newline(&buf); - - bch_notice(c, "%s", buf.buf); ret = true; } @@ -876,7 +857,7 @@ static bool check_version_upgrade(struct bch_fs *c) } noinline_for_stack -static int bch2_fs_opt_version_init(struct bch_fs *c) +static int bch2_fs_opt_version_init(struct bch_fs *c, struct printbuf *out) { if (c->opts.norecovery) { c->opts.recovery_pass_last = c->opts.recovery_pass_last @@ -894,11 +875,9 @@ static int bch2_fs_opt_version_init(struct bch_fs *c) bool may_upgrade_downgrade = !(c->sb.features & BIT_ULL(BCH_FEATURE_small_image)) || bch2_fs_will_resize_on_mount(c); - CLASS(printbuf, p)(); - bch2_log_msg_start(c, &p); - - prt_str(&p, "starting version "); - bch2_version_to_text(&p, c->sb.version); + prt_str_indented(out, "starting version "); + bch2_version_to_text(out, c->sb.version); + prt_newline(out); bool first = true; for (enum bch_opt_id i = 0; i < bch2_opts_nr; i++) { @@ -911,41 +890,45 @@ static int bch2_fs_opt_version_init(struct bch_fs *c) if (v == bch2_opt_get_by_id(&bch2_opts_default, i)) continue; - prt_str(&p, first ? " opts=" : ","); + prt_str_indented(out, first ? "with options: " : ","); first = false; - bch2_opt_to_text(&p, c, c->disk_sb.sb, opt, v, OPT_SHOW_MOUNT_STYLE); + bch2_opt_to_text(out, c, c->disk_sb.sb, opt, v, OPT_SHOW_MOUNT_STYLE); } + if (!first) + prt_newline(out); + if (c->sb.version_incompat_allowed != c->sb.version) { - prt_printf(&p, "\nallowing incompatible features up to "); - bch2_version_to_text(&p, c->sb.version_incompat_allowed); + prt_printf(out, "allowing incompatible features up to "); + bch2_version_to_text(out, c->sb.version_incompat_allowed); + prt_newline(out); } if (c->opts.verbose) { - prt_printf(&p, "\nfeatures: "); - prt_bitflags(&p, bch2_sb_features, c->sb.features); + prt_printf(out, "features: "); + prt_bitflags(out, bch2_sb_features, c->sb.features); + prt_newline(out); } if (c->sb.multi_device) { - prt_printf(&p, "\nwith devices"); + first = true; + prt_printf(out, "with devices: "); for_each_online_member(c, ca, BCH_DEV_READ_REF_bch2_online_devs) { - prt_char(&p, ' '); - prt_str(&p, ca->name); + if (!first) + prt_char(out, ','); + first = false; + prt_str(out, ca->name); } + prt_newline(out); } /* cf_encoding log message should be here, but it breaks xfstests - sigh */ if (c->opts.journal_rewind) - prt_printf(&p, "\nrewinding journal, fsck required"); + prt_printf(out, "rewinding journal, fsck required\n"); scoped_guard(mutex, &c->sb_lock) { - struct bch_sb_field_ext *ext = bch2_sb_field_get_minsize(&c->disk_sb, ext, - sizeof(struct bch_sb_field_ext) / sizeof(u64)); - if (!ext) - return bch_err_throw(c, ENOSPC_sb); - - try(bch2_sb_members_v2_init(c)); + struct bch_sb_field_ext *ext = bch2_sb_field_get(c->disk_sb.sb, ext); __le64 now = cpu_to_le64(ktime_get_real_seconds()); scoped_guard(rcu) @@ -958,19 +941,21 @@ static int bch2_fs_opt_version_init(struct bch_fs *c) u64 sb_passes = bch2_recovery_passes_from_stable(le64_to_cpu(ext->recovery_passes_required[0])); if (sb_passes) { - prt_str(&p, "\nsuperblock requires following recovery passes to be run:\n "); - prt_bitflags(&p, bch2_recovery_passes, sb_passes); + prt_str_indented(out, "superblock requires following recovery passes to be run: "); + prt_bitflags(out, bch2_recovery_passes, sb_passes); + prt_newline(out); } u64 btrees_lost_data = le64_to_cpu(ext->btrees_lost_data); if (btrees_lost_data) { - prt_str(&p, "\nsuperblock indicates damage to following btrees:\n "); - prt_bitflags(&p, __bch2_btree_ids, btrees_lost_data); + prt_str_indented(out, "superblock indicates damage to following btrees: "); + prt_bitflags(out, __bch2_btree_ids, btrees_lost_data); + prt_newline(out); } if (may_upgrade_downgrade) { if (bch2_check_version_downgrade(c)) { - prt_str(&p, "\nVersion downgrade required:"); + prt_str_indented(out, "Version downgrade required"); __le64 passes = ext->recovery_passes_required[0]; bch2_sb_set_downgrade(c, @@ -978,13 +963,14 @@ static int bch2_fs_opt_version_init(struct bch_fs *c) BCH_VERSION_MINOR(c->sb.version)); passes = ext->recovery_passes_required[0] & ~passes; if (passes) { - prt_str(&p, "\nrunning recovery passes: "); - prt_bitflags(&p, bch2_recovery_passes, + prt_str_indented(out, ", running recovery passes: "); + prt_bitflags(out, bch2_recovery_passes, bch2_recovery_passes_from_stable(le64_to_cpu(passes))); } + prt_newline(out); } - check_version_upgrade(c); + check_version_upgrade(c, out); } c->opts.recovery_passes |= bch2_recovery_passes_from_stable(le64_to_cpu(ext->recovery_passes_required[0])); @@ -1001,8 +987,6 @@ static int bch2_fs_opt_version_init(struct bch_fs *c) set_bit(BCH_FS_in_fsck, &c->flags); set_bit(BCH_FS_in_recovery, &c->flags); - bch2_print_str(c, KERN_INFO, p.buf); - /* this really should be part of our one multi line mount message, but - * xfstests... */ if (c->cf_encoding) @@ -1013,13 +997,13 @@ static int bch2_fs_opt_version_init(struct bch_fs *c) if (BCH_SB_INITIALIZED(c->disk_sb.sb)) { if (!(c->sb.features & (1ULL << BCH_FEATURE_new_extent_overwrite))) { - bch_err(c, "feature new_extent_overwrite not set, filesystem no longer supported"); + prt_str_indented(out, "feature new_extent_overwrite not set, filesystem no longer supported\n"); return -EINVAL; } if (!c->sb.clean && !(c->sb.features & (1ULL << BCH_FEATURE_extents_above_btree_updates))) { - bch_err(c, "filesystem needs recovery from older version; run fsck from older bcachefs-tools to fix"); + prt_str_indented(out, "filesystem needs recovery from older version; run fsck from older bcachefs-tools to fix\n"); return -EINVAL; } } @@ -1028,7 +1012,8 @@ static int bch2_fs_opt_version_init(struct bch_fs *c) } static int bch2_fs_init(struct bch_fs *c, struct bch_sb *sb, - struct bch_opts *opts, bch_sb_handles *sbs) + struct bch_opts *opts, bch_sb_handles *sbs, + struct printbuf *out) { unsigned i, iter_size; CLASS(printbuf, name)(); @@ -1129,7 +1114,7 @@ static int bch2_fs_init(struct bch_fs *c, struct bch_sb *sb, #ifdef __KERNEL__ if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && c->opts.block_size > PAGE_SIZE) { - bch_err(c, "cannot mount bs > ps filesystem without CONFIG_TRANSPARENT_HUGEPAGE"); + prt_printf(out, "cannot mount bs > ps filesystem without CONFIG_TRANSPARENT_HUGEPAGE\n"); return -EINVAL; } #endif @@ -1143,7 +1128,7 @@ static int bch2_fs_init(struct bch_fs *c, struct bch_sb *sb, c->btree_foreground_merge_threshold = BTREE_FOREGROUND_MERGE_THRESHOLD(c); if (bch2_fs_init_fault("fs_alloc")) { - bch_err(c, "fs_alloc fault injected"); + prt_printf(out, "fs_alloc fault injected\n"); return -EFAULT; } @@ -1201,16 +1186,16 @@ static int bch2_fs_init(struct bch_fs *c, struct bch_sb *sb, /* Default encoding until we can potentially have more as an option. */ c->cf_encoding = utf8_load(BCH_FS_DEFAULT_UTF8_ENCODING); if (IS_ERR(c->cf_encoding)) { - printk(KERN_ERR "Cannot load UTF-8 encoding for filesystem. Version: %u.%u.%u", - unicode_major(BCH_FS_DEFAULT_UTF8_ENCODING), - unicode_minor(BCH_FS_DEFAULT_UTF8_ENCODING), - unicode_rev(BCH_FS_DEFAULT_UTF8_ENCODING)); + prt_printf(out, "Cannot load UTF-8 encoding for filesystem. Version: %u.%u.%u\n", + unicode_major(BCH_FS_DEFAULT_UTF8_ENCODING), + unicode_minor(BCH_FS_DEFAULT_UTF8_ENCODING), + unicode_rev(BCH_FS_DEFAULT_UTF8_ENCODING)); return -EINVAL; } } #else if (c->sb.features & BIT_ULL(BCH_FEATURE_casefolding)) { - printk(KERN_ERR "Cannot mount a filesystem with casefolding on a kernel without CONFIG_UNICODE\n"); + prt_printf(out, "Cannot mount a filesystem with casefolding on a kernel without CONFIG_UNICODE\n"); return -EINVAL; } #endif @@ -1228,17 +1213,19 @@ static int bch2_fs_init(struct bch_fs *c, struct bch_sb *sb, &c->clock_journal_res, (sizeof(struct jset_entry_clock) / sizeof(u64)) * 2); - scoped_guard(rwsem_write, &c->state_lock) - darray_for_each(*sbs, sb) { - CLASS(printbuf, err)(); - int ret = bch2_dev_attach_bdev(c, sb, &err); - if (ret) { - bch_err(bch2_dev_locked(c, sb->sb->dev_idx), "%s", err.buf); - return ret; - } - } + scoped_guard(mutex, &c->sb_lock) { + if (!bch2_sb_field_get_minsize(&c->disk_sb, ext, + sizeof(struct bch_sb_field_ext) / sizeof(u64))) + return bch_err_throw(c, ENOSPC_sb); - try(bch2_fs_opt_version_init(c)); + try(bch2_sb_members_v2_init(c)); + } + + scoped_guard(rwsem_write, &c->state_lock) + darray_for_each(*sbs, sb) + try(bch2_dev_attach_bdev(c, sb, out)); + + try(bch2_fs_opt_version_init(c, out)); /* * just make sure this is always allocated if we might need it - mount @@ -1255,13 +1242,14 @@ static int bch2_fs_init(struct bch_fs *c, struct bch_sb *sb, } static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts *opts, - bch_sb_handles *sbs) + bch_sb_handles *sbs, + struct printbuf *out) { - struct bch_fs *c = kvmalloc(sizeof(struct bch_fs), GFP_KERNEL|__GFP_ZERO); + struct bch_fs *c = kvzalloc(sizeof(struct bch_fs), GFP_KERNEL); if (!c) return ERR_PTR(-BCH_ERR_ENOMEM_fs_alloc); - int ret = bch2_fs_init(c, sb, opts, sbs); + int ret = bch2_fs_init(c, sb, opts, sbs, out); if (ret) { bch2_fs_free(c); return ERR_PTR(ret); @@ -1270,9 +1258,8 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts *opts, return c; } -static bool bch2_fs_may_start(struct bch_fs *c) +static bool bch2_fs_may_start(struct bch_fs *c, struct printbuf *err) { - struct bch_dev *ca; unsigned flags = 0; switch (c->opts.degraded) { @@ -1282,65 +1269,56 @@ static bool bch2_fs_may_start(struct bch_fs *c) case BCH_DEGRADED_yes: flags |= BCH_FORCE_IF_DEGRADED; break; - default: { - guard(mutex)(&c->sb_lock); - for (unsigned i = 0; i < c->disk_sb.sb->nr_devices; i++) { - if (!bch2_member_exists(c->disk_sb.sb, i)) - continue; - - ca = bch2_dev_locked(c, i); - + default: + for_each_member_device(c, ca) if (!bch2_dev_is_online(ca) && - (ca->mi.state == BCH_MEMBER_STATE_rw || - ca->mi.state == BCH_MEMBER_STATE_ro)) - return false; - } - break; - } + (ca->mi.state != BCH_MEMBER_STATE_failed || + bch2_dev_has_data(c, ca))) { + prt_printf(err, "Cannot mount without device %u\n", ca->dev_idx); + guard(printbuf_indent)(err); + bch2_member_to_text_short(err, c, ca); + return bch_err_throw(c, insufficient_devices_to_start); + } } - CLASS(printbuf, err)(); - bool ret = bch2_have_enough_devs(c, c->online_devs, flags, &err, !c->opts.read_only); - if (!ret) - bch2_print_str(c, KERN_ERR, err.buf); - return ret; + if (!bch2_have_enough_devs(c, c->online_devs, flags, err, !c->opts.read_only)) { + prt_printf(err, "Missing devices\n"); + for_each_member_device(c, ca) + if (!bch2_dev_is_online(ca) && bch2_dev_has_data(c, ca)) { + prt_printf(err, "Device %u\n", ca->dev_idx); + guard(printbuf_indent)(err); + bch2_member_to_text_short(err, c, ca); + } + + return bch_err_throw(c, insufficient_devices_to_start); + } + + return 0; } -int bch2_fs_start(struct bch_fs *c) +static int __bch2_fs_start(struct bch_fs *c, struct printbuf *err) { - int ret = 0; - BUG_ON(test_bit(BCH_FS_started, &c->flags)); - if (!bch2_fs_may_start(c)) - return bch_err_throw(c, insufficient_devices_to_start); + try(bch2_fs_may_start(c, err)); scoped_guard(rwsem_write, &c->state_lock) { scoped_guard(rcu) - for_each_online_member_rcu(c, ca) { + for_each_online_member_rcu(c, ca) if (ca->mi.state == BCH_MEMBER_STATE_rw) bch2_dev_allocator_add(c, ca); - } bch2_recalc_capacity(c); } - ret = BCH_SB_INITIALIZED(c->disk_sb.sb) - ? bch2_fs_recovery(c) - : bch2_fs_initialize(c); - c->recovery_task = NULL; + try(BCH_SB_INITIALIZED(c->disk_sb.sb) + ? bch2_fs_recovery(c) + : bch2_fs_initialize(c)); - if (ret) - goto err; + try(bch2_opts_hooks_pre_set(c)); - ret = bch2_opts_hooks_pre_set(c); - if (ret) - goto err; - - if (bch2_fs_init_fault("fs_start")) { - ret = bch_err_throw(c, injected_fs_start); - goto err; - } + if (bch2_fs_init_fault("fs_start")) + return bch_err_throw(c, injected_fs_start); set_bit(BCH_FS_started, &c->flags); wake_up(&c->ro_ref_wait); @@ -1349,13 +1327,24 @@ int bch2_fs_start(struct bch_fs *c) if (c->opts.read_only) bch2_fs_read_only(c); else if (!test_bit(BCH_FS_rw, &c->flags)) - ret = bch2_fs_read_write(c); + try(bch2_fs_read_write(c)); } -err: + + return 0; +} + +int bch2_fs_start(struct bch_fs *c) +{ + CLASS(printbuf, err)(); + bch2_log_msg_start(c, &err); + + int ret = __bch2_fs_start(c, &err); + c->recovery_task = NULL; + if (ret) - bch_err_msg(c, ret, "starting filesystem"); - else - bch_verbose(c, "done starting filesystem"); + prt_printf(&err, "error starting filesystem: %s", bch2_err_str(ret)); + bch2_print_str(c, KERN_ERR, err.buf); + return ret; } @@ -1422,8 +1411,9 @@ static inline int sb_cmp(struct bch_sb *l, struct bch_sb *r) cmp_int(le64_to_cpu(l->write_time), le64_to_cpu(r->write_time)); } -struct bch_fs *bch2_fs_open(darray_const_str *devices, - struct bch_opts *opts) +static struct bch_fs *__bch2_fs_open(darray_const_str *devices, + struct bch_opts *opts, + struct printbuf *out) { bch_sb_handles sbs = {}; struct bch_fs *c = NULL; @@ -1461,6 +1451,8 @@ struct bch_fs *bch2_fs_open(darray_const_str *devices, if (ret == -BCH_ERR_device_has_been_removed || ret == -BCH_ERR_device_splitbrain) { + prt_printf(out, "Not using device %s: %s\n", + sb->sb_name, bch2_err_str(ret)); bch2_free_super(sb); darray_remove_item(&sbs, sb); best -= best > sb; @@ -1468,17 +1460,21 @@ struct bch_fs *bch2_fs_open(darray_const_str *devices, continue; } - if (ret) - goto err_print; + if (ret) { + prt_printf(out, "Cannot mount with device %s: %s\n", + sb->sb_name, bch2_err_str(ret)); + goto err; + } } - c = bch2_fs_alloc(best->sb, opts, &sbs); + c = bch2_fs_alloc(best->sb, opts, &sbs, out); ret = PTR_ERR_OR_ZERO(c); if (ret) goto err; if (!c->opts.nostart) { - ret = bch2_fs_start(c); + ret = __bch2_fs_start(c, out); + c->recovery_task = NULL; if (ret) goto err; } @@ -1488,9 +1484,6 @@ out: darray_exit(&sbs); module_put(THIS_MODULE); return c; -err_print: - pr_err("bch_fs_open err opening %s: %s", - devices->data[0], bch2_err_str(ret)); err: if (!IS_ERR_OR_NULL(c)) bch2_fs_stop(c); @@ -1498,6 +1491,28 @@ err: goto out; } +struct bch_fs *bch2_fs_open(darray_const_str *devices, + struct bch_opts *opts) +{ + CLASS(printbuf, msg)(); + printbuf_indent_add_nextline(&msg, 2); + + struct bch_fs *c = __bch2_fs_open(devices, opts, &msg); + int ret = PTR_ERR_OR_ZERO(c); + + if (ret) { + prt_printf(&msg, "error starting filesystem: %s", bch2_err_str(ret)); + bch2_print_string_as_lines(KERN_ERR, msg.buf); + } else { + CLASS(printbuf, msg_with_prefix)(); + bch2_log_msg_start(c, &msg_with_prefix); + prt_str(&msg_with_prefix, msg.buf); + bch2_print_str(c, KERN_INFO, msg_with_prefix.buf); + } + + return c; +} + /* Global interfaces/init */ static void bcachefs_exit(void) diff --git a/libbcachefs/sb/io.c b/libbcachefs/sb/io.c index 02059426..f1b7b0cb 100644 --- a/libbcachefs/sb/io.c +++ b/libbcachefs/sb/io.c @@ -56,7 +56,7 @@ void bch2_version_to_text(struct printbuf *out, enum bcachefs_metadata_version v break; } - prt_printf(out, "%u.%u: %s", BCH_VERSION_MAJOR(v), BCH_VERSION_MINOR(v), str); + prt_printf(out, "%s (%u.%u)", str, BCH_VERSION_MAJOR(v), BCH_VERSION_MINOR(v)); } enum bcachefs_metadata_version bch2_latest_compatible_version(enum bcachefs_metadata_version v) @@ -707,73 +707,102 @@ int bch2_sb_from_fs(struct bch_fs *c, struct bch_dev *ca) static int read_one_super(struct bch_sb_handle *sb, u64 offset, struct printbuf *err) { - size_t bytes; -reread: - bio_reset(sb->bio, sb->bdev, REQ_OP_READ|REQ_SYNC|REQ_META); - sb->bio->bi_iter.bi_sector = offset; - bch2_bio_map(sb->bio, sb->sb, sb->buffer_size); + while (true) { + bio_reset(sb->bio, sb->bdev, REQ_OP_READ|REQ_SYNC|REQ_META); + sb->bio->bi_iter.bi_sector = offset; + bch2_bio_map(sb->bio, sb->sb, sb->buffer_size); - int ret = submit_bio_wait(sb->bio); - if (ret) { - prt_printf(err, "IO error: %i", ret); - return ret; + int ret = submit_bio_wait(sb->bio); + if (ret) { + prt_printf(err, "IO error: %i", ret); + return ret; + } + + if (!uuid_equal(&sb->sb->magic, &BCACHE_MAGIC) && + !uuid_equal(&sb->sb->magic, &BCHFS_MAGIC)) { + prt_str(err, "Not a bcachefs superblock (got magic "); + pr_uuid(err, sb->sb->magic.b); + prt_str(err, ")"); + return -BCH_ERR_invalid_sb_magic; + } + + try(bch2_sb_compatible(sb->sb, err)); + + size_t bytes = vstruct_bytes(sb->sb); + + u64 sb_size = 512ULL << min(BCH_SB_LAYOUT_SIZE_BITS_MAX, sb->sb->layout.sb_max_size_bits); + if (bytes > sb_size) { + prt_printf(err, "Invalid superblock: too big (got %zu bytes, layout max %llu)", + bytes, sb_size); + return -BCH_ERR_invalid_sb_too_big; + } + + if (bytes > sb->buffer_size) { + try(bch2_sb_realloc(sb, le32_to_cpu(sb->sb->u64s))); + continue; + } + + enum bch_csum_type csum_type = BCH_SB_CSUM_TYPE(sb->sb); + if (csum_type >= BCH_CSUM_NR || + bch2_csum_type_is_encryption(csum_type)) { + prt_printf(err, "unknown checksum type %llu", BCH_SB_CSUM_TYPE(sb->sb)); + return -BCH_ERR_invalid_sb_csum_type; + } + + /* XXX: verify MACs */ + struct bch_csum csum = csum_vstruct(NULL, csum_type, null_nonce(), sb->sb); + if (bch2_crc_cmp(csum, sb->sb->csum)) { + bch2_csum_err_msg(err, csum_type, sb->sb->csum, csum); + return -BCH_ERR_invalid_sb_csum; + } + + sb->seq = le64_to_cpu(sb->sb->seq); + return 0; } - - if (!uuid_equal(&sb->sb->magic, &BCACHE_MAGIC) && - !uuid_equal(&sb->sb->magic, &BCHFS_MAGIC)) { - prt_str(err, "Not a bcachefs superblock (got magic "); - pr_uuid(err, sb->sb->magic.b); - prt_str(err, ")"); - return -BCH_ERR_invalid_sb_magic; - } - - try(bch2_sb_compatible(sb->sb, err)); - - bytes = vstruct_bytes(sb->sb); - - u64 sb_size = 512ULL << min(BCH_SB_LAYOUT_SIZE_BITS_MAX, sb->sb->layout.sb_max_size_bits); - if (bytes > sb_size) { - prt_printf(err, "Invalid superblock: too big (got %zu bytes, layout max %llu)", - bytes, sb_size); - return -BCH_ERR_invalid_sb_too_big; - } - - if (bytes > sb->buffer_size) { - try(bch2_sb_realloc(sb, le32_to_cpu(sb->sb->u64s))); - goto reread; - } - - enum bch_csum_type csum_type = BCH_SB_CSUM_TYPE(sb->sb); - if (csum_type >= BCH_CSUM_NR || - bch2_csum_type_is_encryption(csum_type)) { - prt_printf(err, "unknown checksum type %llu", BCH_SB_CSUM_TYPE(sb->sb)); - return -BCH_ERR_invalid_sb_csum_type; - } - - /* XXX: verify MACs */ - struct bch_csum csum = csum_vstruct(NULL, csum_type, null_nonce(), sb->sb); - if (bch2_crc_cmp(csum, sb->sb->csum)) { - bch2_csum_err_msg(err, csum_type, sb->sb->csum, csum); - return -BCH_ERR_invalid_sb_csum; - } - - sb->seq = le64_to_cpu(sb->sb->seq); - - return 0; } -static int __bch2_read_super(const char *path, struct bch_opts *opts, - struct bch_sb_handle *sb, bool ignore_notbchfs_msg) +static int read_backup_supers(struct bch_sb_handle *sb, + struct bch_opts *opts, + struct printbuf *err) { - u64 offset = opt_get(*opts, sb); + /* + * Error reading primary superblock - read location of backup + * superblocks: + */ + bio_reset(sb->bio, sb->bdev, REQ_OP_READ|REQ_SYNC|REQ_META); + sb->bio->bi_iter.bi_sector = BCH_SB_LAYOUT_SECTOR; + /* + * use sb buffer to read layout, since sb buffer is page aligned but + * layout won't be: + */ + bch2_bio_map(sb->bio, sb->sb, sizeof(struct bch_sb_layout)); + + try(submit_bio_wait(sb->bio)); + struct bch_sb_layout layout; - CLASS(printbuf, err)(); - CLASS(printbuf, err2)(); - __le64 *i; - int ret; -#ifndef __KERNEL__ -retry: -#endif + memcpy(&layout, sb->sb, sizeof(layout)); + + try(validate_sb_layout(&layout, err)); + + int ret = -BCH_ERR_invalid; + for (__le64 *i = layout.sb_offset; i < layout.sb_offset + layout.nr_superblocks; i++) { + u64 offset = le64_to_cpu(*i); + if (offset == opt_get(*opts, sb)) + continue; + + ret = read_one_super(sb, offset, err); + if (!ret) + break; + } + + return ret; +} + +static int read_super_and_backups(struct bch_sb_handle *sb, + const char *path, + struct bch_opts *opts, + struct printbuf *err) +{ memset(sb, 0, sizeof(*sb)); sb->mode = BLK_OPEN_READ; sb->have_bio = true; @@ -782,11 +811,8 @@ retry: return -ENOMEM; sb->sb_name = kstrdup(path, GFP_KERNEL); - if (!sb->sb_name) { - ret = -ENOMEM; - prt_printf(&err, "error allocating memory for sb_name"); - goto err; - } + if (!sb->sb_name) + return -ENOMEM; #ifndef __KERNEL__ if (opt_get(*opts, direct_io) == false) @@ -810,118 +836,82 @@ retry: opt_set(*opts, nochanges, true); } - if (IS_ERR(sb->s_bdev_file)) { - ret = PTR_ERR(sb->s_bdev_file); - prt_printf(&err, "error opening %s: %s", path, bch2_err_str(ret)); - goto err; - } + if (IS_ERR(sb->s_bdev_file)) + return PTR_ERR(sb->s_bdev_file); + sb->bdev = file_bdev(sb->s_bdev_file); - ret = bch2_sb_realloc(sb, 0); + try(bch2_sb_realloc(sb, 0)); + + if (bch2_fs_init_fault("read_super")) + return -EFAULT; + + u64 offset = opt_get(*opts, sb); + int ret = read_one_super(sb, offset, err); if (ret) { - prt_printf(&err, "error allocating memory for superblock"); - goto err; + if (opt_defined(*opts, sb)) + return ret; + + prt_printf(err, "attempting backup superblocks\n"); + try(read_backup_supers(sb, opts, err)); } - if (bch2_fs_init_fault("read_super")) { - prt_printf(&err, "dynamic fault"); - ret = -EFAULT; - goto err; - } - - ret = read_one_super(sb, offset, &err); - if (!ret) - goto got_super; - - if (opt_defined(*opts, sb)) - goto err; - - prt_printf(&err2, "bcachefs (%s): error reading default superblock: %s\n", - path, err.buf); - if (ret == -BCH_ERR_invalid_sb_magic && ignore_notbchfs_msg) - bch2_print_opts(opts, KERN_INFO "%s", err2.buf); - else - bch2_print_opts(opts, KERN_ERR "%s", err2.buf); - - printbuf_reset(&err); - - /* - * Error reading primary superblock - read location of backup - * superblocks: - */ - bio_reset(sb->bio, sb->bdev, REQ_OP_READ|REQ_SYNC|REQ_META); - sb->bio->bi_iter.bi_sector = BCH_SB_LAYOUT_SECTOR; - /* - * use sb buffer to read layout, since sb buffer is page aligned but - * layout won't be: - */ - bch2_bio_map(sb->bio, sb->sb, sizeof(struct bch_sb_layout)); - - ret = submit_bio_wait(sb->bio); - if (ret) { - prt_printf(&err, "IO error: %i", ret); - goto err; - } - - memcpy(&layout, sb->sb, sizeof(layout)); - ret = validate_sb_layout(&layout, &err); - if (ret) - goto err; - - for (i = layout.sb_offset; - i < layout.sb_offset + layout.nr_superblocks; i++) { - offset = le64_to_cpu(*i); - - if (offset == opt_get(*opts, sb)) { - ret = -BCH_ERR_invalid; - continue; - } - - ret = read_one_super(sb, offset, &err); - if (!ret) - goto got_super; - } - - goto err; - -got_super: if (le16_to_cpu(sb->sb->block_size) << 9 < bdev_logical_block_size(sb->bdev) && opt_get(*opts, direct_io)) { #ifndef __KERNEL__ opt_set(*opts, direct_io, false); - bch2_free_super(sb); - goto retry; + return -EINTR; #endif - prt_printf(&err, "block size (%u) smaller than device block size (%u)", - le16_to_cpu(sb->sb->block_size) << 9, - bdev_logical_block_size(sb->bdev)); - ret = -BCH_ERR_block_size_too_small; - goto err; + prt_printf(err, "block size (%u) smaller than device block size (%u)", + le16_to_cpu(sb->sb->block_size) << 9, + bdev_logical_block_size(sb->bdev)); + return -BCH_ERR_block_size_too_small; } sb->have_layout = true; - - ret = bch2_sb_validate(sb->sb, opts, offset, 0, &err); - if (ret) { - bch2_print_opts(opts, KERN_ERR "bcachefs (%s): error validating superblock: %s\n", - path, err.buf); - goto err_no_print; - } + try(bch2_sb_validate(sb->sb, opts, offset, 0, err)); return 0; -err: - bch2_print_opts(opts, KERN_ERR "bcachefs (%s): error reading superblock: %s\n", - path, err.buf); -err_no_print: - bch2_free_super(sb); - return ret; +} + +static int __bch2_read_super(struct bch_sb_handle *sb, + const char *path, + struct bch_opts *opts, + struct printbuf *err) +{ + while (true) { + int ret = read_super_and_backups(sb, path, opts, err); + if (ret) + bch2_free_super(sb); + if (ret != -EINTR) + return ret; + + printbuf_reset(err); + /* fallback to buffered IO */ + } } int bch2_read_super(const char *path, struct bch_opts *opts, struct bch_sb_handle *sb) { - return __bch2_read_super(path, opts, sb, false); + CLASS(printbuf, err)(); + int ret = __bch2_read_super(sb, path, opts, &err); + if (ret) + bch2_free_super(sb); + + if (ret && err.pos) + bch2_print_opts(opts, KERN_ERR "bcachefs (%s): error reading superblock: %s\n%s", + path, bch2_err_str(ret), err.buf); + else if (ret) + bch2_print_opts(opts, KERN_ERR "bcachefs (%s): error reading superblock: %s", + path, bch2_err_str(ret)); + else if (err.pos) { + prt_printf(&err, "successful read from backup"); + bch2_print_opts(opts, KERN_NOTICE "bcachefs (%s): %s", path, err.buf); + } + + return ret; } /* provide a silenced version for mount.bcachefs */ @@ -929,7 +919,11 @@ int bch2_read_super(const char *path, struct bch_opts *opts, int bch2_read_super_silent(const char *path, struct bch_opts *opts, struct bch_sb_handle *sb) { - return __bch2_read_super(path, opts, sb, true); + CLASS(printbuf, err)(); + int ret = __bch2_read_super(sb, path, opts, &err); + if (ret) + bch2_free_super(sb); + return ret; } /* write superblock: */ diff --git a/libbcachefs/sb/members.c b/libbcachefs/sb/members.c index 9bdf6a95..8da7f573 100644 --- a/libbcachefs/sb/members.c +++ b/libbcachefs/sb/members.c @@ -276,6 +276,54 @@ void bch2_member_to_text(struct printbuf *out, prt_printf(out, "Discard:\t%llu\n", BCH_MEMBER_DISCARD(m)); prt_printf(out, "Freespace initialized:\t%llu\n", BCH_MEMBER_FREESPACE_INITIALIZED(m)); prt_printf(out, "Resize on mount:\t%llu\n", BCH_MEMBER_RESIZE_ON_MOUNT(m)); + + prt_printf(out, "Last device name:\t%.*s", (int) sizeof(m->device_name), m->device_name); + prt_printf(out, "Last device model:\t%.*s", (int) sizeof(m->device_model), m->device_model); +} + +static void bch2_member_to_text_short_sb(struct printbuf *out, + struct bch_member *m, + struct bch_sb_field_disk_groups *gi, + struct bch_sb *sb, + unsigned idx) +{ + if (!out->nr_tabstops) + printbuf_tabstop_push(out, 16 + out->indent); + + if (BCH_MEMBER_GROUP(m)) { + prt_printf(out, "Label:\t"); + bch2_disk_path_to_text_sb(out, sb, + BCH_MEMBER_GROUP(m) - 1); + prt_newline(out); + } + + prt_printf(out, "Device:\t%.*s", (int) sizeof(m->device_name), m->device_name); + prt_printf(out, "Model:\t%.*s", (int) sizeof(m->device_model), m->device_model); + + prt_printf(out, "State:\t%s\n", + BCH_MEMBER_STATE(m) < BCH_MEMBER_STATE_NR + ? bch2_member_states[BCH_MEMBER_STATE(m)] + : "unknown"); + + prt_printf(out, "Has data:\t"); + unsigned data_have = bch2_sb_dev_has_data(sb, idx); + if (data_have) + prt_bitflags(out, __bch2_data_types, data_have); + else + prt_printf(out, "(none)"); + prt_newline(out); +} + +void bch2_member_to_text_short(struct printbuf *out, + struct bch_fs *c, + struct bch_dev *ca) +{ + guard(mutex)(&c->sb_lock); + struct bch_member m = bch2_sb_member_get(c->disk_sb.sb, ca->dev_idx); + bch2_member_to_text_short_sb(out, &m, + bch2_sb_field_get(c->disk_sb.sb, disk_groups), + c->disk_sb.sb, + ca->dev_idx); } static void member_to_text(struct printbuf *out, diff --git a/libbcachefs/sb/members.h b/libbcachefs/sb/members.h index 3a4b3ead..7fc363ca 100644 --- a/libbcachefs/sb/members.h +++ b/libbcachefs/sb/members.h @@ -44,6 +44,8 @@ void bch2_member_to_text(struct printbuf *, struct bch_member *, struct bch_sb_field_disk_groups *, struct bch_sb *, unsigned); +void bch2_member_to_text_short(struct printbuf *, struct bch_fs *, struct bch_dev *); + static inline bool bch2_dev_is_online(struct bch_dev *ca) { return !enumerated_ref_is_zero(&ca->io_ref[READ]); diff --git a/libbcachefs/sb/members_format.h b/libbcachefs/sb/members_format.h index b2b89268..6c1b671f 100644 --- a/libbcachefs/sb/members_format.h +++ b/libbcachefs/sb/members_format.h @@ -44,6 +44,10 @@ enum bch_member_error_type { BCH_MEMBER_ERROR_NR }; +#ifndef __nonstring +#define __nonstring +#endif + struct bch_member { __uuid_t uuid; __le64 nbuckets; /* device size */ @@ -67,6 +71,9 @@ struct bch_member { */ __le32 last_journal_bucket; __le32 last_journal_bucket_offset; + + __u8 device_name[16] __nonstring; + __u8 device_model[64] __nonstring; }; /*