mirror of
https://github.com/koverstreet/bcachefs-tools.git
synced 2025-12-08 00:00:12 +03:00
Update bcachefs sources to 7604cb70e590 fixup! bcachefs: bch_member.device_name
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
391aa439e4
commit
1f269ad1aa
@ -1 +1 @@
|
||||
99a43760af01b64e736624b984240eefcc821148
|
||||
7604cb70e5909aed4647acf0199c60ac5776dc7c
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <linux/string_helpers.h>
|
||||
#include <linux/types.h> /* 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_ */
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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) \
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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: */
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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]);
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user