mirror of
https://github.com/koverstreet/bcachefs-tools.git
synced 2025-12-10 00:00:24 +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 <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <linux/string_helpers.h>
|
||||||
#include <linux/types.h> /* for size_t */
|
#include <linux/types.h> /* for size_t */
|
||||||
|
|
||||||
extern size_t strlcpy(char *dest, const char *src, size_t size);
|
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 kstrndup(s, n, gfp) strndup(s, n)
|
||||||
#define kstrdup(s, gfp) strdup(s)
|
#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_ */
|
#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)
|
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);
|
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)
|
static inline bool bkey_extent_is_inline_data(const struct bkey *k)
|
||||||
{
|
{
|
||||||
return k->type == KEY_TYPE_inline_data ||
|
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_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_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_device(const struct bch_fs *, struct bkey_s, unsigned);
|
||||||
void bch2_bkey_drop_ec(const struct bch_fs *, struct bkey_i *k, unsigned);
|
void bch2_bkey_drop_ec(const struct bch_fs *, struct bkey_i *k, unsigned);
|
||||||
|
|
||||||
|
|||||||
@ -28,40 +28,59 @@
|
|||||||
|
|
||||||
#include "init/progress.h"
|
#include "init/progress.h"
|
||||||
|
|
||||||
static int drop_dev_ptrs(struct bch_fs *c, struct bkey_s k, unsigned dev_idx,
|
static struct bkey_i *drop_dev_ptrs(struct btree_trans *trans, struct bkey_s_c k, unsigned dev_idx,
|
||||||
unsigned flags, struct printbuf *err, bool metadata)
|
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 replicas = metadata ? c->opts.metadata_replicas : c->opts.data_replicas;
|
||||||
unsigned lost = metadata ? BCH_FORCE_IF_METADATA_LOST : BCH_FORCE_IF_DATA_LOST;
|
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 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)) ||
|
if ((!nr_good && !(flags & lost)) ||
|
||||||
(nr_good < replicas && !(flags & degraded))) {
|
(nr_good < replicas && !(flags & degraded))) {
|
||||||
prt_str(err, "cannot drop device without degrading/losing data\n ");
|
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);
|
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,
|
static int drop_btree_ptrs(struct btree_trans *trans, struct btree_iter *iter,
|
||||||
struct btree *b, unsigned dev_idx,
|
struct btree *b, unsigned dev_idx,
|
||||||
unsigned flags, struct printbuf *err)
|
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);
|
return bch2_btree_node_update_key(trans, iter, b, n, 0, false);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bch2_dev_usrdata_drop_key(struct btree_trans *trans,
|
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 dev_idx,
|
||||||
unsigned flags, struct printbuf *err)
|
unsigned flags, struct printbuf *err)
|
||||||
{
|
{
|
||||||
struct bch_fs *c = trans->c;
|
struct bkey_i *n = errptr_try(drop_dev_ptrs(trans, k, dev_idx, flags, err));
|
||||||
|
if (!n)
|
||||||
if (!bch2_bkey_has_device_c(c, k, dev_idx))
|
|
||||||
return 0;
|
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
|
* Since we're not inserting through an extent iterator
|
||||||
* (BTREE_ITER_all_snapshots iterators aren't extent iterators),
|
* (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))
|
if (bkey_deleted(&n->k))
|
||||||
n->k.size = 0;
|
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,
|
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;
|
return 1;
|
||||||
|
|
||||||
try(bch2_progress_update_iter(trans, progress, iter, "dropping metadata"));
|
try(bch2_progress_update_iter(trans, progress, iter, "dropping metadata"));
|
||||||
|
try(drop_btree_ptrs(trans, iter, b, dev_idx, flags, err));
|
||||||
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));
|
|
||||||
return 0;
|
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 per_snapshot_io_opts *snapshot_opts, struct bkey_s_c k,
|
||||||
struct bch_inode_opts *opts)
|
struct bch_inode_opts *opts)
|
||||||
{
|
{
|
||||||
|
struct bch_fs *c = trans->c;
|
||||||
enum io_opts_mode {
|
enum io_opts_mode {
|
||||||
IO_OPTS_metadata,
|
IO_OPTS_metadata,
|
||||||
IO_OPTS_reflink,
|
IO_OPTS_reflink,
|
||||||
IO_OPTS_user,
|
IO_OPTS_user,
|
||||||
} mode = bkey_is_btree_ptr(k.k) ? IO_OPTS_metadata :
|
} mode;
|
||||||
!bkey_is_indirect(k.k) ? IO_OPTS_user :
|
|
||||||
IO_OPTS_reflink;
|
|
||||||
|
|
||||||
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) {
|
if (!snapshot_opts) {
|
||||||
bch2_inode_opts_get(c, opts, mode == IO_OPTS_metadata);
|
bch2_inode_opts_get(c, opts, mode == IO_OPTS_metadata);
|
||||||
|
|||||||
@ -214,6 +214,8 @@
|
|||||||
x(EINVAL, device_already_online) \
|
x(EINVAL, device_already_online) \
|
||||||
x(EINVAL, filesystem_uuid_already_open) \
|
x(EINVAL, filesystem_uuid_already_open) \
|
||||||
x(EINVAL, insufficient_devices_to_start) \
|
x(EINVAL, insufficient_devices_to_start) \
|
||||||
|
x(EINVAL, chardev_init_error) \
|
||||||
|
x(EINVAL, sysfs_init_error) \
|
||||||
x(EINVAL, invalid) \
|
x(EINVAL, invalid) \
|
||||||
x(EINVAL, internal_fsck_err) \
|
x(EINVAL, internal_fsck_err) \
|
||||||
x(EINVAL, opt_parse_error) \
|
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);
|
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) {
|
if (d.v->d_type == DT_SUBVOL) {
|
||||||
try(check_dirent_to_subvol(trans, iter, d));
|
try(check_dirent_to_subvol(trans, iter, d));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -333,6 +333,55 @@ fsck_err:
|
|||||||
return ret;
|
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,
|
int __bch2_str_hash_check_key(struct btree_trans *trans,
|
||||||
struct snapshots_seen *s,
|
struct snapshots_seen *s,
|
||||||
const struct bch_hash_desc *desc,
|
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,
|
return str_hash_bad_hash(trans, s, desc, hash_info, k_iter, hash_k,
|
||||||
updated_before_k_pos, &iter, hash);
|
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,
|
struct btree_iter *, struct bkey_s_c,
|
||||||
bool *);
|
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,
|
static inline int bch2_str_hash_check_key(struct btree_trans *trans,
|
||||||
struct snapshots_seen *s,
|
struct snapshots_seen *s,
|
||||||
const struct bch_hash_desc *desc,
|
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,
|
struct btree_iter *k_iter, struct bkey_s_c hash_k,
|
||||||
bool *updated_before_k_pos)
|
bool *updated_before_k_pos)
|
||||||
{
|
{
|
||||||
if (hash_k.k->type != desc->key_type)
|
return str_hash_key_needs_check(desc, hash_info, hash_k)
|
||||||
return 0;
|
? __bch2_str_hash_check_key(trans, s, desc, hash_info, k_iter, hash_k,
|
||||||
|
updated_before_k_pos)
|
||||||
if (likely(desc->hash_bkey(hash_info, hash_k) == hash_k.k->p.offset))
|
: 0;
|
||||||
return 0;
|
|
||||||
|
|
||||||
return __bch2_str_hash_check_key(trans, s, desc, hash_info, k_iter, hash_k,
|
|
||||||
updated_before_k_pos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* _BCACHEFS_STR_HASH_H */
|
#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;
|
struct inode_opt_set *s = p;
|
||||||
|
|
||||||
if (s->id == Inode_opt_casefold) {
|
if (s->id == Inode_opt_casefold)
|
||||||
int ret = bch2_inode_set_casefold(trans, inode_inum(inode), bi, s->v);
|
try(bch2_inode_set_casefold(trans, inode_inum(inode), bi, s->v));
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s->id == Inode_opt_inodes_32bit &&
|
if (s->id == Inode_opt_inodes_32bit &&
|
||||||
!bch2_request_incompat_feature(trans->c, bcachefs_metadata_version_31bit_dirent_offset)) {
|
!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
|
* Make sure the dir is empty, as otherwise we'd need to
|
||||||
* rehash everything and update the dirent keys.
|
* rehash everything and update the dirent keys.
|
||||||
*/
|
*/
|
||||||
int ret = bch2_empty_dir_trans(trans, inode_inum(inode));
|
try(bch2_empty_dir_trans(trans, inode_inum(inode)));
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (s->defined)
|
if (s->defined)
|
||||||
bi->bi_flags |= BCH_INODE_31bit_dirent_offset;
|
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);
|
c->minor = idr_alloc(&bch_chardev_minor, c, 0, 0, GFP_KERNEL);
|
||||||
if (c->minor < 0)
|
if (c->minor < 0)
|
||||||
return c->minor;
|
return bch_err_throw(c, chardev_init_error);
|
||||||
|
|
||||||
c->chardev = device_create(&bch_chardev_class, NULL,
|
c->chardev = device_create(&bch_chardev_class, NULL,
|
||||||
MKDEV(bch_chardev_major, c->minor), c,
|
MKDEV(bch_chardev_major, c->minor), c,
|
||||||
"bcachefs%u-ctl", c->minor);
|
"bcachefs%u-ctl", c->minor);
|
||||||
if (IS_ERR(c->chardev))
|
if (IS_ERR(c->chardev))
|
||||||
return PTR_ERR(c->chardev);
|
return bch_err_throw(c, chardev_init_error);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -397,21 +397,56 @@ int bch2_dev_alloc(struct bch_fs *c, unsigned dev_idx)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __bch2_dev_attach_bdev(struct bch_dev *ca, struct bch_sb_handle *sb,
|
static int read_file_str(const char *path, darray_char *ret)
|
||||||
struct printbuf *err)
|
{
|
||||||
|
/*
|
||||||
|
* 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)) {
|
if (bch2_dev_is_online(ca)) {
|
||||||
prt_printf(err, "already have device online in slot %u\n",
|
prt_printf(err, "Cannot attach %s: already have device %s online in slot %u\n",
|
||||||
sb->sb->dev_idx);
|
sb->sb_name, ca->name, sb->sb->dev_idx);
|
||||||
return bch_err_throw(ca->fs, device_already_online);
|
return bch_err_throw(ca->fs, device_already_online);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_capacity(sb->bdev->bd_disk) <
|
if (get_capacity(sb->bdev->bd_disk) <
|
||||||
ca->mi.bucket_size * ca->mi.nbuckets) {
|
ca->mi.bucket_size * ca->mi.nbuckets) {
|
||||||
prt_printf(err, "cannot online: device too small (capacity %llu filesystem size %llu nbuckets %llu)\n",
|
prt_printf(err, "Cannot online %s: device too small (capacity %llu filesystem size %llu nbuckets %llu)\n",
|
||||||
get_capacity(sb->bdev->bd_disk),
|
sb->sb_name,
|
||||||
ca->mi.bucket_size * ca->mi.nbuckets,
|
get_capacity(sb->bdev->bd_disk),
|
||||||
ca->mi.nbuckets);
|
ca->mi.bucket_size * ca->mi.nbuckets,
|
||||||
|
ca->mi.nbuckets);
|
||||||
return bch_err_throw(ca->fs, device_size_too_small);
|
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);
|
prt_bdevname(&name, sb->bdev);
|
||||||
strscpy(ca->name, name.buf, sizeof(ca->name));
|
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: */
|
/* Commit: */
|
||||||
ca->disk_sb = *sb;
|
ca->disk_sb = *sb;
|
||||||
memset(sb, 0, sizeof(*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);
|
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);
|
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)
|
int bch2_dev_add(struct bch_fs *c, const char *path, struct printbuf *err)
|
||||||
{
|
{
|
||||||
struct bch_opts opts = bch2_opts_empty();
|
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;
|
struct bch_dev *ca = NULL;
|
||||||
CLASS(printbuf, label)();
|
CLASS(printbuf, label)();
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@ -736,7 +791,7 @@ int bch2_dev_add(struct bch_fs *c, const char *path, struct printbuf *err)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = __bch2_dev_attach_bdev(ca, &sb, err);
|
ret = __bch2_dev_attach_bdev(c, ca, &sb, err);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@ -830,7 +885,6 @@ out:
|
|||||||
err:
|
err:
|
||||||
if (ca)
|
if (ca)
|
||||||
bch2_dev_free(ca);
|
bch2_dev_free(ca);
|
||||||
bch2_free_super(&sb);
|
|
||||||
goto out;
|
goto out;
|
||||||
err_late:
|
err_late:
|
||||||
ca = NULL;
|
ca = NULL;
|
||||||
@ -841,9 +895,7 @@ err_late:
|
|||||||
int bch2_dev_online(struct bch_fs *c, const char *path, struct printbuf *err)
|
int bch2_dev_online(struct bch_fs *c, const char *path, struct printbuf *err)
|
||||||
{
|
{
|
||||||
struct bch_opts opts = bch2_opts_empty();
|
struct bch_opts opts = bch2_opts_empty();
|
||||||
struct bch_sb_handle sb = { NULL };
|
struct bch_sb_handle sb __cleanup(bch2_free_super) = {};
|
||||||
struct bch_dev *ca;
|
|
||||||
unsigned dev_idx;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
guard(rwsem_write)(&c->state_lock);
|
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;
|
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);
|
ret = bch2_dev_in_fs(&c->disk_sb, &sb, &c->opts);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
prt_printf(err, "device not a member of fs: %s\n", bch2_err_str(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);
|
try(bch2_dev_attach_bdev(c, &sb, err));
|
||||||
if (ret)
|
|
||||||
goto 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);
|
ret = bch2_trans_mark_dev_sb(c, ca, BTREE_TRIGGER_transactional);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
prt_printf(err, "bch2_trans_mark_dev_sb() error: %s\n", bch2_err_str(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)
|
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);
|
ret = bch2_dev_freespace_init(c, ca, 0, ca->mi.nbuckets);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
prt_printf(err, "bch2_dev_freespace_init() error: %s\n", bch2_err_str(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);
|
ret = bch2_dev_journal_alloc(ca, false);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
prt_printf(err, "bch2_dev_journal_alloc() error: %s\n", bch2_err_str(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;
|
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)
|
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)
|
void bch2_print_str(struct bch_fs *c, const char *prefix, const char *str)
|
||||||
{
|
{
|
||||||
|
BUG_ON(!str);
|
||||||
if (!should_print_loglevel(c, prefix))
|
if (!should_print_loglevel(c, prefix))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -708,52 +709,37 @@ void bch2_fs_stop(struct bch_fs *c)
|
|||||||
|
|
||||||
static int bch2_fs_online(struct bch_fs *c)
|
static int bch2_fs_online(struct bch_fs *c)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
lockdep_assert_held(&bch2_fs_list_lock);
|
lockdep_assert_held(&bch2_fs_list_lock);
|
||||||
|
|
||||||
if (c->sb.multi_device &&
|
if (c->sb.multi_device &&
|
||||||
__bch2_uuid_to_fs(c->sb.uuid)) {
|
__bch2_uuid_to_fs(c->sb.uuid))
|
||||||
bch_err(c, "filesystem UUID already open");
|
|
||||||
return bch_err_throw(c, filesystem_uuid_already_open);
|
return bch_err_throw(c, filesystem_uuid_already_open);
|
||||||
}
|
|
||||||
|
|
||||||
ret = bch2_fs_chardev_init(c);
|
try(bch2_fs_chardev_init(c));
|
||||||
if (ret) {
|
|
||||||
bch_err(c, "error creating character device");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bch2_fs_debug_init(c);
|
bch2_fs_debug_init(c);
|
||||||
|
|
||||||
ret = (c->sb.multi_device
|
if ((c->sb.multi_device
|
||||||
? kobject_add(&c->kobj, NULL, "%pU", c->sb.user_uuid.b)
|
? kobject_add(&c->kobj, NULL, "%pU", c->sb.user_uuid.b)
|
||||||
: kobject_add(&c->kobj, NULL, "%s", c->name)) ?:
|
: kobject_add(&c->kobj, NULL, "%s", c->name)) ?:
|
||||||
kobject_add(&c->internal, &c->kobj, "internal") ?:
|
kobject_add(&c->internal, &c->kobj, "internal") ?:
|
||||||
kobject_add(&c->opts_dir, &c->kobj, "options") ?:
|
kobject_add(&c->opts_dir, &c->kobj, "options") ?:
|
||||||
#ifndef CONFIG_BCACHEFS_NO_LATENCY_ACCT
|
#ifndef CONFIG_BCACHEFS_NO_LATENCY_ACCT
|
||||||
kobject_add(&c->time_stats, &c->kobj, "time_stats") ?:
|
kobject_add(&c->time_stats, &c->kobj, "time_stats") ?:
|
||||||
#endif
|
#endif
|
||||||
kobject_add(&c->counters_kobj, &c->kobj, "counters") ?:
|
kobject_add(&c->counters_kobj, &c->kobj, "counters") ?:
|
||||||
bch2_opts_create_sysfs_files(&c->opts_dir, OPT_FS);
|
bch2_opts_create_sysfs_files(&c->opts_dir, OPT_FS))
|
||||||
if (ret) {
|
return bch_err_throw(c, sysfs_init_error);
|
||||||
bch_err(c, "error creating sysfs objects");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
guard(rwsem_write)(&c->state_lock);
|
guard(rwsem_write)(&c->state_lock);
|
||||||
|
|
||||||
for_each_member_device(c, ca) {
|
for_each_member_device(c, ca)
|
||||||
ret = bch2_dev_sysfs_online(c, ca);
|
if (bch2_dev_sysfs_online(c, ca))
|
||||||
if (ret) {
|
return bch_err_throw(c, sysfs_init_error);
|
||||||
bch_err(c, "error creating sysfs objects");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BUG_ON(!list_empty(&c->list));
|
BUG_ON(!list_empty(&c->list));
|
||||||
list_add(&c->list, &bch2_fs_list);
|
list_add(&c->list, &bch2_fs_list);
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bch2_fs_init_rw(struct bch_fs *c)
|
int bch2_fs_init_rw(struct bch_fs *c)
|
||||||
@ -788,7 +774,7 @@ int bch2_fs_init_rw(struct bch_fs *c)
|
|||||||
return 0;
|
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_version = bcachefs_metadata_version_current;
|
||||||
unsigned latest_compatible = min(latest_version,
|
unsigned latest_compatible = min(latest_version,
|
||||||
@ -818,26 +804,24 @@ static bool check_version_upgrade(struct bch_fs *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (new_version > old_version) {
|
if (new_version > old_version) {
|
||||||
CLASS(printbuf, buf)();
|
|
||||||
|
|
||||||
if (old_version < bcachefs_metadata_required_upgrade_below)
|
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) {
|
if (old_version != c->sb.version) {
|
||||||
prt_str(&buf, "Version upgrade from ");
|
prt_str_indented(out, "Version upgrade from ");
|
||||||
bch2_version_to_text(&buf, c->sb.version_upgrade_complete);
|
bch2_version_to_text(out, c->sb.version_upgrade_complete);
|
||||||
prt_str(&buf, " to ");
|
prt_str_indented(out, " to ");
|
||||||
bch2_version_to_text(&buf, c->sb.version);
|
bch2_version_to_text(out, c->sb.version);
|
||||||
prt_str(&buf, " incomplete\n");
|
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)
|
BCH_VERSION_MAJOR(old_version) != BCH_VERSION_MAJOR(new_version)
|
||||||
? "incompatible" : "compatible");
|
? "incompatible" : "compatible");
|
||||||
bch2_version_to_text(&buf, old_version);
|
bch2_version_to_text(out, old_version);
|
||||||
prt_str(&buf, " to ");
|
prt_str_indented(out, " to ");
|
||||||
bch2_version_to_text(&buf, new_version);
|
bch2_version_to_text(out, new_version);
|
||||||
prt_newline(&buf);
|
prt_newline(out);
|
||||||
|
|
||||||
struct bch_sb_field_ext *ext = bch2_sb_field_get(c->disk_sb.sb, ext);
|
struct bch_sb_field_ext *ext = bch2_sb_field_get(c->disk_sb.sb, ext);
|
||||||
__le64 passes = ext->recovery_passes_required[0];
|
__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;
|
passes = ext->recovery_passes_required[0] & ~passes;
|
||||||
|
|
||||||
if (passes) {
|
if (passes) {
|
||||||
prt_str(&buf, " running recovery passes: ");
|
prt_str_indented(out, "Upgrade requires recovery passes: ");
|
||||||
prt_bitflags(&buf, bch2_recovery_passes,
|
prt_bitflags(out, bch2_recovery_passes,
|
||||||
bch2_recovery_passes_from_stable(le64_to_cpu(passes)));
|
bch2_recovery_passes_from_stable(le64_to_cpu(passes)));
|
||||||
|
prt_newline(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
bch_notice(c, "%s", buf.buf);
|
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_version > c->sb.version_incompat_allowed &&
|
if (new_version > c->sb.version_incompat_allowed &&
|
||||||
c->opts.version_upgrade == BCH_VERSION_UPGRADE_incompatible) {
|
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;
|
ret = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -876,7 +857,7 @@ static bool check_version_upgrade(struct bch_fs *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
noinline_for_stack
|
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) {
|
if (c->opts.norecovery) {
|
||||||
c->opts.recovery_pass_last = c->opts.recovery_pass_last
|
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)) ||
|
bool may_upgrade_downgrade = !(c->sb.features & BIT_ULL(BCH_FEATURE_small_image)) ||
|
||||||
bch2_fs_will_resize_on_mount(c);
|
bch2_fs_will_resize_on_mount(c);
|
||||||
|
|
||||||
CLASS(printbuf, p)();
|
prt_str_indented(out, "starting version ");
|
||||||
bch2_log_msg_start(c, &p);
|
bch2_version_to_text(out, c->sb.version);
|
||||||
|
prt_newline(out);
|
||||||
prt_str(&p, "starting version ");
|
|
||||||
bch2_version_to_text(&p, c->sb.version);
|
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (enum bch_opt_id i = 0; i < bch2_opts_nr; i++) {
|
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))
|
if (v == bch2_opt_get_by_id(&bch2_opts_default, i))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
prt_str(&p, first ? " opts=" : ",");
|
prt_str_indented(out, first ? "with options: " : ",");
|
||||||
first = false;
|
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) {
|
if (c->sb.version_incompat_allowed != c->sb.version) {
|
||||||
prt_printf(&p, "\nallowing incompatible features up to ");
|
prt_printf(out, "allowing incompatible features up to ");
|
||||||
bch2_version_to_text(&p, c->sb.version_incompat_allowed);
|
bch2_version_to_text(out, c->sb.version_incompat_allowed);
|
||||||
|
prt_newline(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c->opts.verbose) {
|
if (c->opts.verbose) {
|
||||||
prt_printf(&p, "\nfeatures: ");
|
prt_printf(out, "features: ");
|
||||||
prt_bitflags(&p, bch2_sb_features, c->sb.features);
|
prt_bitflags(out, bch2_sb_features, c->sb.features);
|
||||||
|
prt_newline(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c->sb.multi_device) {
|
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) {
|
for_each_online_member(c, ca, BCH_DEV_READ_REF_bch2_online_devs) {
|
||||||
prt_char(&p, ' ');
|
if (!first)
|
||||||
prt_str(&p, ca->name);
|
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 */
|
/* cf_encoding log message should be here, but it breaks xfstests - sigh */
|
||||||
|
|
||||||
if (c->opts.journal_rewind)
|
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) {
|
scoped_guard(mutex, &c->sb_lock) {
|
||||||
struct bch_sb_field_ext *ext = bch2_sb_field_get_minsize(&c->disk_sb, ext,
|
struct bch_sb_field_ext *ext = bch2_sb_field_get(c->disk_sb.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));
|
|
||||||
|
|
||||||
__le64 now = cpu_to_le64(ktime_get_real_seconds());
|
__le64 now = cpu_to_le64(ktime_get_real_seconds());
|
||||||
scoped_guard(rcu)
|
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]));
|
u64 sb_passes = bch2_recovery_passes_from_stable(le64_to_cpu(ext->recovery_passes_required[0]));
|
||||||
if (sb_passes) {
|
if (sb_passes) {
|
||||||
prt_str(&p, "\nsuperblock requires following recovery passes to be run:\n ");
|
prt_str_indented(out, "superblock requires following recovery passes to be run: ");
|
||||||
prt_bitflags(&p, bch2_recovery_passes, sb_passes);
|
prt_bitflags(out, bch2_recovery_passes, sb_passes);
|
||||||
|
prt_newline(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 btrees_lost_data = le64_to_cpu(ext->btrees_lost_data);
|
u64 btrees_lost_data = le64_to_cpu(ext->btrees_lost_data);
|
||||||
if (btrees_lost_data) {
|
if (btrees_lost_data) {
|
||||||
prt_str(&p, "\nsuperblock indicates damage to following btrees:\n ");
|
prt_str_indented(out, "superblock indicates damage to following btrees: ");
|
||||||
prt_bitflags(&p, __bch2_btree_ids, btrees_lost_data);
|
prt_bitflags(out, __bch2_btree_ids, btrees_lost_data);
|
||||||
|
prt_newline(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (may_upgrade_downgrade) {
|
if (may_upgrade_downgrade) {
|
||||||
if (bch2_check_version_downgrade(c)) {
|
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];
|
__le64 passes = ext->recovery_passes_required[0];
|
||||||
bch2_sb_set_downgrade(c,
|
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));
|
BCH_VERSION_MINOR(c->sb.version));
|
||||||
passes = ext->recovery_passes_required[0] & ~passes;
|
passes = ext->recovery_passes_required[0] & ~passes;
|
||||||
if (passes) {
|
if (passes) {
|
||||||
prt_str(&p, "\nrunning recovery passes: ");
|
prt_str_indented(out, ", running recovery passes: ");
|
||||||
prt_bitflags(&p, bch2_recovery_passes,
|
prt_bitflags(out, bch2_recovery_passes,
|
||||||
bch2_recovery_passes_from_stable(le64_to_cpu(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]));
|
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_fsck, &c->flags);
|
||||||
set_bit(BCH_FS_in_recovery, &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 -
|
/* this really should be part of our one multi line mount message, but -
|
||||||
* xfstests... */
|
* xfstests... */
|
||||||
if (c->cf_encoding)
|
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 (BCH_SB_INITIALIZED(c->disk_sb.sb)) {
|
||||||
if (!(c->sb.features & (1ULL << BCH_FEATURE_new_extent_overwrite))) {
|
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;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c->sb.clean &&
|
if (!c->sb.clean &&
|
||||||
!(c->sb.features & (1ULL << BCH_FEATURE_extents_above_btree_updates))) {
|
!(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;
|
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,
|
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;
|
unsigned i, iter_size;
|
||||||
CLASS(printbuf, name)();
|
CLASS(printbuf, name)();
|
||||||
@ -1129,7 +1114,7 @@ static int bch2_fs_init(struct bch_fs *c, struct bch_sb *sb,
|
|||||||
#ifdef __KERNEL__
|
#ifdef __KERNEL__
|
||||||
if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
|
if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) &&
|
||||||
c->opts.block_size > PAGE_SIZE) {
|
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;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
#endif
|
#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);
|
c->btree_foreground_merge_threshold = BTREE_FOREGROUND_MERGE_THRESHOLD(c);
|
||||||
|
|
||||||
if (bch2_fs_init_fault("fs_alloc")) {
|
if (bch2_fs_init_fault("fs_alloc")) {
|
||||||
bch_err(c, "fs_alloc fault injected");
|
prt_printf(out, "fs_alloc fault injected\n");
|
||||||
return -EFAULT;
|
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. */
|
/* Default encoding until we can potentially have more as an option. */
|
||||||
c->cf_encoding = utf8_load(BCH_FS_DEFAULT_UTF8_ENCODING);
|
c->cf_encoding = utf8_load(BCH_FS_DEFAULT_UTF8_ENCODING);
|
||||||
if (IS_ERR(c->cf_encoding)) {
|
if (IS_ERR(c->cf_encoding)) {
|
||||||
printk(KERN_ERR "Cannot load UTF-8 encoding for filesystem. Version: %u.%u.%u",
|
prt_printf(out, "Cannot load UTF-8 encoding for filesystem. Version: %u.%u.%u\n",
|
||||||
unicode_major(BCH_FS_DEFAULT_UTF8_ENCODING),
|
unicode_major(BCH_FS_DEFAULT_UTF8_ENCODING),
|
||||||
unicode_minor(BCH_FS_DEFAULT_UTF8_ENCODING),
|
unicode_minor(BCH_FS_DEFAULT_UTF8_ENCODING),
|
||||||
unicode_rev(BCH_FS_DEFAULT_UTF8_ENCODING));
|
unicode_rev(BCH_FS_DEFAULT_UTF8_ENCODING));
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (c->sb.features & BIT_ULL(BCH_FEATURE_casefolding)) {
|
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;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -1228,17 +1213,19 @@ static int bch2_fs_init(struct bch_fs *c, struct bch_sb *sb,
|
|||||||
&c->clock_journal_res,
|
&c->clock_journal_res,
|
||||||
(sizeof(struct jset_entry_clock) / sizeof(u64)) * 2);
|
(sizeof(struct jset_entry_clock) / sizeof(u64)) * 2);
|
||||||
|
|
||||||
scoped_guard(rwsem_write, &c->state_lock)
|
scoped_guard(mutex, &c->sb_lock) {
|
||||||
darray_for_each(*sbs, sb) {
|
if (!bch2_sb_field_get_minsize(&c->disk_sb, ext,
|
||||||
CLASS(printbuf, err)();
|
sizeof(struct bch_sb_field_ext) / sizeof(u64)))
|
||||||
int ret = bch2_dev_attach_bdev(c, sb, &err);
|
return bch_err_throw(c, ENOSPC_sb);
|
||||||
if (ret) {
|
|
||||||
bch_err(bch2_dev_locked(c, sb->sb->dev_idx), "%s", err.buf);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
* 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,
|
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)
|
if (!c)
|
||||||
return ERR_PTR(-BCH_ERR_ENOMEM_fs_alloc);
|
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) {
|
if (ret) {
|
||||||
bch2_fs_free(c);
|
bch2_fs_free(c);
|
||||||
return ERR_PTR(ret);
|
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;
|
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;
|
unsigned flags = 0;
|
||||||
|
|
||||||
switch (c->opts.degraded) {
|
switch (c->opts.degraded) {
|
||||||
@ -1282,65 +1269,56 @@ static bool bch2_fs_may_start(struct bch_fs *c)
|
|||||||
case BCH_DEGRADED_yes:
|
case BCH_DEGRADED_yes:
|
||||||
flags |= BCH_FORCE_IF_DEGRADED;
|
flags |= BCH_FORCE_IF_DEGRADED;
|
||||||
break;
|
break;
|
||||||
default: {
|
default:
|
||||||
guard(mutex)(&c->sb_lock);
|
for_each_member_device(c, ca)
|
||||||
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);
|
|
||||||
|
|
||||||
if (!bch2_dev_is_online(ca) &&
|
if (!bch2_dev_is_online(ca) &&
|
||||||
(ca->mi.state == BCH_MEMBER_STATE_rw ||
|
(ca->mi.state != BCH_MEMBER_STATE_failed ||
|
||||||
ca->mi.state == BCH_MEMBER_STATE_ro))
|
bch2_dev_has_data(c, ca))) {
|
||||||
return false;
|
prt_printf(err, "Cannot mount without device %u\n", ca->dev_idx);
|
||||||
}
|
guard(printbuf_indent)(err);
|
||||||
break;
|
bch2_member_to_text_short(err, c, ca);
|
||||||
}
|
return bch_err_throw(c, insufficient_devices_to_start);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CLASS(printbuf, err)();
|
if (!bch2_have_enough_devs(c, c->online_devs, flags, err, !c->opts.read_only)) {
|
||||||
bool ret = bch2_have_enough_devs(c, c->online_devs, flags, &err, !c->opts.read_only);
|
prt_printf(err, "Missing devices\n");
|
||||||
if (!ret)
|
for_each_member_device(c, ca)
|
||||||
bch2_print_str(c, KERN_ERR, err.buf);
|
if (!bch2_dev_is_online(ca) && bch2_dev_has_data(c, ca)) {
|
||||||
return ret;
|
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));
|
BUG_ON(test_bit(BCH_FS_started, &c->flags));
|
||||||
|
|
||||||
if (!bch2_fs_may_start(c))
|
try(bch2_fs_may_start(c, err));
|
||||||
return bch_err_throw(c, insufficient_devices_to_start);
|
|
||||||
|
|
||||||
scoped_guard(rwsem_write, &c->state_lock) {
|
scoped_guard(rwsem_write, &c->state_lock) {
|
||||||
scoped_guard(rcu)
|
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)
|
if (ca->mi.state == BCH_MEMBER_STATE_rw)
|
||||||
bch2_dev_allocator_add(c, ca);
|
bch2_dev_allocator_add(c, ca);
|
||||||
}
|
|
||||||
|
|
||||||
bch2_recalc_capacity(c);
|
bch2_recalc_capacity(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = BCH_SB_INITIALIZED(c->disk_sb.sb)
|
try(BCH_SB_INITIALIZED(c->disk_sb.sb)
|
||||||
? bch2_fs_recovery(c)
|
? bch2_fs_recovery(c)
|
||||||
: bch2_fs_initialize(c);
|
: bch2_fs_initialize(c));
|
||||||
c->recovery_task = NULL;
|
|
||||||
|
|
||||||
if (ret)
|
try(bch2_opts_hooks_pre_set(c));
|
||||||
goto err;
|
|
||||||
|
|
||||||
ret = bch2_opts_hooks_pre_set(c);
|
if (bch2_fs_init_fault("fs_start"))
|
||||||
if (ret)
|
return bch_err_throw(c, injected_fs_start);
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (bch2_fs_init_fault("fs_start")) {
|
|
||||||
ret = bch_err_throw(c, injected_fs_start);
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
set_bit(BCH_FS_started, &c->flags);
|
set_bit(BCH_FS_started, &c->flags);
|
||||||
wake_up(&c->ro_ref_wait);
|
wake_up(&c->ro_ref_wait);
|
||||||
@ -1349,13 +1327,24 @@ int bch2_fs_start(struct bch_fs *c)
|
|||||||
if (c->opts.read_only)
|
if (c->opts.read_only)
|
||||||
bch2_fs_read_only(c);
|
bch2_fs_read_only(c);
|
||||||
else if (!test_bit(BCH_FS_rw, &c->flags))
|
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)
|
if (ret)
|
||||||
bch_err_msg(c, ret, "starting filesystem");
|
prt_printf(&err, "error starting filesystem: %s", bch2_err_str(ret));
|
||||||
else
|
bch2_print_str(c, KERN_ERR, err.buf);
|
||||||
bch_verbose(c, "done starting filesystem");
|
|
||||||
return ret;
|
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));
|
cmp_int(le64_to_cpu(l->write_time), le64_to_cpu(r->write_time));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct bch_fs *bch2_fs_open(darray_const_str *devices,
|
static struct bch_fs *__bch2_fs_open(darray_const_str *devices,
|
||||||
struct bch_opts *opts)
|
struct bch_opts *opts,
|
||||||
|
struct printbuf *out)
|
||||||
{
|
{
|
||||||
bch_sb_handles sbs = {};
|
bch_sb_handles sbs = {};
|
||||||
struct bch_fs *c = NULL;
|
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 ||
|
if (ret == -BCH_ERR_device_has_been_removed ||
|
||||||
ret == -BCH_ERR_device_splitbrain) {
|
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);
|
bch2_free_super(sb);
|
||||||
darray_remove_item(&sbs, sb);
|
darray_remove_item(&sbs, sb);
|
||||||
best -= best > sb;
|
best -= best > sb;
|
||||||
@ -1468,17 +1460,21 @@ struct bch_fs *bch2_fs_open(darray_const_str *devices,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret)
|
if (ret) {
|
||||||
goto err_print;
|
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);
|
ret = PTR_ERR_OR_ZERO(c);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (!c->opts.nostart) {
|
if (!c->opts.nostart) {
|
||||||
ret = bch2_fs_start(c);
|
ret = __bch2_fs_start(c, out);
|
||||||
|
c->recovery_task = NULL;
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@ -1488,9 +1484,6 @@ out:
|
|||||||
darray_exit(&sbs);
|
darray_exit(&sbs);
|
||||||
module_put(THIS_MODULE);
|
module_put(THIS_MODULE);
|
||||||
return c;
|
return c;
|
||||||
err_print:
|
|
||||||
pr_err("bch_fs_open err opening %s: %s",
|
|
||||||
devices->data[0], bch2_err_str(ret));
|
|
||||||
err:
|
err:
|
||||||
if (!IS_ERR_OR_NULL(c))
|
if (!IS_ERR_OR_NULL(c))
|
||||||
bch2_fs_stop(c);
|
bch2_fs_stop(c);
|
||||||
@ -1498,6 +1491,28 @@ err:
|
|||||||
goto out;
|
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 */
|
/* Global interfaces/init */
|
||||||
|
|
||||||
static void bcachefs_exit(void)
|
static void bcachefs_exit(void)
|
||||||
|
|||||||
@ -56,7 +56,7 @@ void bch2_version_to_text(struct printbuf *out, enum bcachefs_metadata_version v
|
|||||||
break;
|
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)
|
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)
|
static int read_one_super(struct bch_sb_handle *sb, u64 offset, struct printbuf *err)
|
||||||
{
|
{
|
||||||
size_t bytes;
|
while (true) {
|
||||||
reread:
|
bio_reset(sb->bio, sb->bdev, REQ_OP_READ|REQ_SYNC|REQ_META);
|
||||||
bio_reset(sb->bio, sb->bdev, REQ_OP_READ|REQ_SYNC|REQ_META);
|
sb->bio->bi_iter.bi_sector = offset;
|
||||||
sb->bio->bi_iter.bi_sector = offset;
|
bch2_bio_map(sb->bio, sb->sb, sb->buffer_size);
|
||||||
bch2_bio_map(sb->bio, sb->sb, sb->buffer_size);
|
|
||||||
|
|
||||||
int ret = submit_bio_wait(sb->bio);
|
int ret = submit_bio_wait(sb->bio);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
prt_printf(err, "IO error: %i", ret);
|
prt_printf(err, "IO error: %i", ret);
|
||||||
return 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,
|
static int read_backup_supers(struct bch_sb_handle *sb,
|
||||||
struct bch_sb_handle *sb, bool ignore_notbchfs_msg)
|
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;
|
struct bch_sb_layout layout;
|
||||||
CLASS(printbuf, err)();
|
memcpy(&layout, sb->sb, sizeof(layout));
|
||||||
CLASS(printbuf, err2)();
|
|
||||||
__le64 *i;
|
try(validate_sb_layout(&layout, err));
|
||||||
int ret;
|
|
||||||
#ifndef __KERNEL__
|
int ret = -BCH_ERR_invalid;
|
||||||
retry:
|
for (__le64 *i = layout.sb_offset; i < layout.sb_offset + layout.nr_superblocks; i++) {
|
||||||
#endif
|
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));
|
memset(sb, 0, sizeof(*sb));
|
||||||
sb->mode = BLK_OPEN_READ;
|
sb->mode = BLK_OPEN_READ;
|
||||||
sb->have_bio = true;
|
sb->have_bio = true;
|
||||||
@ -782,11 +811,8 @@ retry:
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
sb->sb_name = kstrdup(path, GFP_KERNEL);
|
sb->sb_name = kstrdup(path, GFP_KERNEL);
|
||||||
if (!sb->sb_name) {
|
if (!sb->sb_name)
|
||||||
ret = -ENOMEM;
|
return -ENOMEM;
|
||||||
prt_printf(&err, "error allocating memory for sb_name");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef __KERNEL__
|
#ifndef __KERNEL__
|
||||||
if (opt_get(*opts, direct_io) == false)
|
if (opt_get(*opts, direct_io) == false)
|
||||||
@ -810,118 +836,82 @@ retry:
|
|||||||
opt_set(*opts, nochanges, true);
|
opt_set(*opts, nochanges, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_ERR(sb->s_bdev_file)) {
|
if (IS_ERR(sb->s_bdev_file))
|
||||||
ret = PTR_ERR(sb->s_bdev_file);
|
return PTR_ERR(sb->s_bdev_file);
|
||||||
prt_printf(&err, "error opening %s: %s", path, bch2_err_str(ret));
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
sb->bdev = file_bdev(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) {
|
if (ret) {
|
||||||
prt_printf(&err, "error allocating memory for superblock");
|
if (opt_defined(*opts, sb))
|
||||||
goto err;
|
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 <
|
if (le16_to_cpu(sb->sb->block_size) << 9 <
|
||||||
bdev_logical_block_size(sb->bdev) &&
|
bdev_logical_block_size(sb->bdev) &&
|
||||||
opt_get(*opts, direct_io)) {
|
opt_get(*opts, direct_io)) {
|
||||||
#ifndef __KERNEL__
|
#ifndef __KERNEL__
|
||||||
opt_set(*opts, direct_io, false);
|
opt_set(*opts, direct_io, false);
|
||||||
bch2_free_super(sb);
|
return -EINTR;
|
||||||
goto retry;
|
|
||||||
#endif
|
#endif
|
||||||
prt_printf(&err, "block size (%u) smaller than device block size (%u)",
|
prt_printf(err, "block size (%u) smaller than device block size (%u)",
|
||||||
le16_to_cpu(sb->sb->block_size) << 9,
|
le16_to_cpu(sb->sb->block_size) << 9,
|
||||||
bdev_logical_block_size(sb->bdev));
|
bdev_logical_block_size(sb->bdev));
|
||||||
ret = -BCH_ERR_block_size_too_small;
|
return -BCH_ERR_block_size_too_small;
|
||||||
goto err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sb->have_layout = true;
|
sb->have_layout = true;
|
||||||
|
try(bch2_sb_validate(sb->sb, opts, offset, 0, err));
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
err:
|
}
|
||||||
bch2_print_opts(opts, KERN_ERR "bcachefs (%s): error reading superblock: %s\n",
|
|
||||||
path, err.buf);
|
static int __bch2_read_super(struct bch_sb_handle *sb,
|
||||||
err_no_print:
|
const char *path,
|
||||||
bch2_free_super(sb);
|
struct bch_opts *opts,
|
||||||
return ret;
|
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,
|
int bch2_read_super(const char *path, struct bch_opts *opts,
|
||||||
struct bch_sb_handle *sb)
|
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 */
|
/* 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,
|
int bch2_read_super_silent(const char *path, struct bch_opts *opts,
|
||||||
struct bch_sb_handle *sb)
|
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: */
|
/* 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, "Discard:\t%llu\n", BCH_MEMBER_DISCARD(m));
|
||||||
prt_printf(out, "Freespace initialized:\t%llu\n", BCH_MEMBER_FREESPACE_INITIALIZED(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, "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,
|
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_field_disk_groups *,
|
||||||
struct bch_sb *, unsigned);
|
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)
|
static inline bool bch2_dev_is_online(struct bch_dev *ca)
|
||||||
{
|
{
|
||||||
return !enumerated_ref_is_zero(&ca->io_ref[READ]);
|
return !enumerated_ref_is_zero(&ca->io_ref[READ]);
|
||||||
|
|||||||
@ -44,6 +44,10 @@ enum bch_member_error_type {
|
|||||||
BCH_MEMBER_ERROR_NR
|
BCH_MEMBER_ERROR_NR
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef __nonstring
|
||||||
|
#define __nonstring
|
||||||
|
#endif
|
||||||
|
|
||||||
struct bch_member {
|
struct bch_member {
|
||||||
__uuid_t uuid;
|
__uuid_t uuid;
|
||||||
__le64 nbuckets; /* device size */
|
__le64 nbuckets; /* device size */
|
||||||
@ -67,6 +71,9 @@ struct bch_member {
|
|||||||
*/
|
*/
|
||||||
__le32 last_journal_bucket;
|
__le32 last_journal_bucket;
|
||||||
__le32 last_journal_bucket_offset;
|
__le32 last_journal_bucket_offset;
|
||||||
|
|
||||||
|
__u8 device_name[16] __nonstring;
|
||||||
|
__u8 device_model[64] __nonstring;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user