Update bcachefs sources to 5df84d32ad84 bcachefs: bch2_can_do_write_btree()

This commit is contained in:
Kent Overstreet 2025-11-27 11:42:09 -05:00
parent 6500754c5c
commit 7094e625c0
34 changed files with 437 additions and 300 deletions

View File

@ -1 +1 @@
d800fc8b69ff90543270cdad1ef95c18371d4168
5df84d32ad84d74ababcd783bf92ed1a1853e74d

View File

@ -35,6 +35,6 @@ impl Fs {
impl Drop for Fs {
fn drop(&mut self) {
unsafe { c::bch2_fs_stop(self.raw) }
unsafe { c::bch2_fs_exit(self.raw); }
}
}

View File

@ -79,6 +79,6 @@ int cmd_reset_counters(int argc, char *argv[])
scoped_guard(mutex, &c->sb_lock)
bch2_write_super(c);
bch2_fs_stop(c);
bch2_fs_exit(c);
return 0;
}

View File

@ -473,7 +473,7 @@ static int cmd_device_set_state(int argc, char *argv[])
bch2_write_super(c);
}
bch2_fs_stop(c);
bch2_fs_exit(c);
return ret;
}
@ -601,7 +601,7 @@ static int cmd_device_resize(int argc, char *argv[])
fprintf(stderr, "resize error: %s\n%s", bch2_err_str(ret), err.buf);
enumerated_ref_put(&resize->io_ref[READ], 0);
bch2_fs_stop(c);
bch2_fs_exit(c);
}
return 0;
}
@ -700,7 +700,7 @@ static int cmd_device_resize_journal(int argc, char *argv[])
fprintf(stderr, "resize error: %s\n", bch2_err_str(ret));
enumerated_ref_put(&resize->io_ref[READ], 0);
bch2_fs_stop(c);
bch2_fs_exit(c);
}
return 0;
}

View File

@ -300,7 +300,7 @@ static int dump_fs(struct bch_fs *c, struct dump_opts opts)
up_read(&c->state_lock);
bch2_fs_stop(c);
bch2_fs_exit(c);
darray_for_each(devs, d) {
darray_exit(&d->sb);

View File

@ -323,7 +323,7 @@ int cmd_format(int argc, char *argv[])
if (opts.source)
build_fs(c, opts.source);
bch2_fs_stop(c);
bch2_fs_exit(c);
}
bch2_opt_strs_free(&fs_opt_strs);
darray_exit(&devices);

View File

@ -154,7 +154,7 @@ static bool should_use_kernel_fsck(darray_const_str devs)
printbuf_exit(&buf);
}
bch2_fs_stop(c);
bch2_fs_exit(c);
return ret;
}
@ -369,7 +369,7 @@ userland_fsck:
ret |= 4;
}
bch2_fs_stop(c);
bch2_fs_exit(c);
}
printbuf_exit(&opts_str);
@ -463,7 +463,7 @@ int cmd_recovery_pass(int argc, char *argv[])
printf("%s\n", buf.buf);
}
err:
bch2_fs_stop(c);
bch2_fs_exit(c);
return ret;
}

View File

@ -97,7 +97,7 @@ static void bcachefs_fuse_destroy(void *arg)
{
struct bch_fs *c = arg;
bch2_fs_stop(c);
bch2_fs_exit(c);
}
static void bcachefs_fuse_lookup(fuse_req_t req, fuse_ino_t dir_ino,
@ -1297,7 +1297,7 @@ out:
return ret ? 1 : 0;
err:
bch2_fs_stop(c);
bch2_fs_exit(c);
goto out;
}

View File

@ -489,7 +489,7 @@ static void image_create(struct bch_opt_strs fs_opt_strs,
if (ret)
goto err;
bch2_fs_stop(c);
bch2_fs_exit(c);
darray_exit(&device_paths);
xclose(src_fd);
return;
@ -731,12 +731,12 @@ static int image_update(const char *src_path, const char *dst_image,
if (ret)
goto err;
bch2_fs_stop(c);
bch2_fs_exit(c);
unlink(dev_opts.path);
xclose(src_fd);
return 0;
err_stop:
bch2_fs_stop(c);
bch2_fs_exit(c);
err:
unlink(dev_opts.path);
exit(EXIT_FAILURE);

View File

@ -159,7 +159,7 @@ int cmd_set_passphrase(int argc, char *argv[])
bch2_revoke_key(c->disk_sb.sb);
bch2_write_super(c);
bch2_fs_stop(c);
bch2_fs_exit(c);
return 0;
}
@ -217,6 +217,6 @@ int cmd_remove_passphrase(int argc, char *argv[])
bch_crypt_update_passphrase(sb, crypt, &key, NULL);
bch2_write_super(c);
bch2_fs_stop(c);
bch2_fs_exit(c);
return 0;
}

View File

@ -150,7 +150,7 @@ int cmd_kill_btree_node(int argc, char *argv[])
}
bch2_trans_put(trans);
bch2_fs_stop(c);
bch2_fs_exit(c);
darray_exit(&kill_nodes);
return ret < 0 ? ret : 0;
}

View File

@ -660,6 +660,6 @@ int cmd_list_journal(int argc, char *argv[])
journal_replay_print(c, f, p);
}
bch2_fs_stop(c);
bch2_fs_exit(c);
return 0;
}

View File

@ -297,7 +297,7 @@ static int migrate_fs(const char *fs_path,
ret = copy_fs(c, &s, fs_fd, fs_path);
bch2_fs_stop(c);
bch2_fs_exit(c);
if (ret)
return ret;
@ -311,7 +311,7 @@ static int migrate_fs(const char *fs_path,
if (IS_ERR(c))
die("Error opening new filesystem: %s", bch2_err_str(PTR_ERR(c)));
bch2_fs_stop(c);
bch2_fs_exit(c);
printf("fsck complete\n");
printf("To mount the new filesystem, run\n"
@ -497,7 +497,7 @@ int cmd_migrate_superblock(int argc, char *argv[])
if (ret)
die("Error marking superblock buckets: %s", bch2_err_str(ret));
bch2_fs_stop(c);
bch2_fs_exit(c);
#if CONFIG_BCACHEFS_DEBUG
/* Verify that filesystem is clean and consistent */
@ -515,7 +515,7 @@ int cmd_migrate_superblock(int argc, char *argv[])
if (test_bit(BCH_FS_errors, &c->flags) || test_bit(BCH_FS_errors_fixed, &c->flags))
die("Filesystem has errors after migration");
bch2_fs_stop(c);
bch2_fs_exit(c);
#endif
return 0;
}

View File

@ -161,7 +161,7 @@ int cmd_set_option(int argc, char *argv[])
}
}
bch2_fs_stop(c);
bch2_fs_exit(c);
return ret;
} else {
unsigned dev_idx;

View File

@ -126,7 +126,7 @@ reopen:
fprintf(stderr, "Error starting filesystem: %s\n", bch2_err_str(ret));
goto err_stop;
}
bch2_fs_stop(c);
bch2_fs_exit(c);
goto reopen;
}
@ -146,6 +146,6 @@ reopen:
bch2_write_super(c);
mutex_unlock(&c->sb_lock);
err_stop:
bch2_fs_stop(c);
bch2_fs_exit(c);
return ret;
}

View File

@ -319,7 +319,7 @@ int bch2_move_extent(struct moving_context *ctxt,
else if (data_opts.type != BCH_DATA_UPDATE_scrub) {
struct bch_devs_list devs_have = bch2_data_update_devs_keeping(c, &data_opts, k);
ret = bch2_can_do_write(c, &data_opts, &devs_have) ?:
ret = bch2_can_do_write(c, &data_opts, k, &devs_have) ?:
bch2_btree_node_rewrite_pos(trans, iter->btree_id, level, k.k->p,
data_opts.target, 0, data_opts.write_flags);
} else

View File

@ -1245,10 +1245,15 @@ static int reconcile_set_data_opts(struct btree_trans *trans,
return 0;
data_opts->type = BCH_DATA_UPDATE_reconcile;
if (!r->hipri)
data_opts->write_flags |= BCH_WRITE_only_specified_devs;
data_opts->target = r->background_target;
/*
* we can't add/drop replicas from btree nodes incrementally, we always
* need to be able to spill over to the whole fs
*/
if (!r->hipri && !bkey_is_btree_ptr(k.k))
data_opts->write_flags |= BCH_WRITE_only_specified_devs;
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
const union bch_extent_entry *entry;
struct extent_ptr_decoded p;
@ -1460,7 +1465,7 @@ static int do_reconcile_extent(struct moving_context *ctxt,
reconcile_set_data_opts(trans, NULL, data_pos.btree, k, &opts, &data_opts);
struct bch_devs_list devs_have = bch2_data_update_devs_keeping(c, &data_opts, k);
int ret = bch2_can_do_write(c, &data_opts, &devs_have);
int ret = bch2_can_do_write(c, &data_opts, k, &devs_have);
if (ret) {
if (is_reconcile_pending_err(c, k, ret))
return 0;
@ -1564,6 +1569,35 @@ static int do_reconcile_scan_bp(struct btree_trans *trans,
return update_reconcile_opts_scan(trans, NULL, &opts, &iter, bp.v->level, k, s);
}
static int do_reconcile_scan_bps(struct moving_context *ctxt,
struct reconcile_scan s,
struct wb_maybe_flush *last_flushed)
{
struct btree_trans *trans = ctxt->trans;
struct bch_fs *c = trans->c;
struct bch_fs_reconcile *r = &c->reconcile;
r->scan_start = BBPOS(BTREE_ID_backpointers, POS(s.dev, 0));
r->scan_end = BBPOS(BTREE_ID_backpointers, POS(s.dev, U64_MAX));
bch2_btree_write_buffer_flush_sync(trans);
CLASS(disk_reservation, res)(c);
return for_each_btree_key_max_commit(trans, iter, BTREE_ID_backpointers,
POS(s.dev, 0), POS(s.dev, U64_MAX),
BTREE_ITER_prefetch, k,
&res.r, NULL, BCH_TRANS_COMMIT_no_enospc, ({
ctxt->stats->pos = BBPOS(iter.btree_id, iter.pos);
if (k.k->type != KEY_TYPE_backpointer)
continue;
bch2_disk_reservation_put(c, &res.r);
do_reconcile_scan_bp(trans, s, bkey_s_c_to_backpointer(k), last_flushed);
}));
}
static int do_reconcile_scan_indirect(struct moving_context *ctxt,
struct reconcile_scan s,
struct disk_reservation *res,
@ -1714,25 +1748,7 @@ static int do_reconcile_scan(struct moving_context *ctxt,
} else if (s.type == RECONCILE_SCAN_metadata) {
try(do_reconcile_scan_fs(ctxt, s, snapshot_io_opts, true));
} else if (s.type == RECONCILE_SCAN_device) {
r->scan_start = BBPOS(BTREE_ID_backpointers, POS(s.dev, 0));
r->scan_end = BBPOS(BTREE_ID_backpointers, POS(s.dev, U64_MAX));
bch2_btree_write_buffer_flush_sync(trans);
CLASS(disk_reservation, res)(c);
try(for_each_btree_key_max_commit(trans, iter, BTREE_ID_backpointers,
POS(s.dev, 0), POS(s.dev, U64_MAX),
BTREE_ITER_prefetch, k,
&res.r, NULL, BCH_TRANS_COMMIT_no_enospc, ({
ctxt->stats->pos = BBPOS(iter.btree_id, iter.pos);
if (k.k->type != KEY_TYPE_backpointer)
continue;
bch2_disk_reservation_put(c, &res.r);
do_reconcile_scan_bp(trans, s, bkey_s_c_to_backpointer(k), last_flushed);
})));
try(do_reconcile_scan_bps(ctxt, s, last_flushed));
} else if (s.type == RECONCILE_SCAN_inum) {
r->scan_start = BBPOS(BTREE_ID_extents, POS(s.inum, 0));
r->scan_end = BBPOS(BTREE_ID_extents, POS(s.inum, U64_MAX));

View File

@ -681,6 +681,11 @@ struct bch_devs_list bch2_data_update_devs_keeping(struct bch_fs *c,
struct bkey_s_c k)
{
struct bch_devs_list ret = (struct bch_devs_list) { 0 };
/* We always rewrite btree nodes entirely */
if (bkey_is_btree_ptr(k.k))
return ret;
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
unsigned ptr_bit = 1;
@ -694,8 +699,65 @@ struct bch_devs_list bch2_data_update_devs_keeping(struct bch_fs *c,
return ret;
}
static unsigned durability_available_on_target(struct bch_fs *c,
enum bch_watermark watermark,
unsigned target)
{
struct bch_devs_mask devs = target_rw_devs(c, BCH_DATA_user, target);
unsigned ret = 0;
unsigned i;
for_each_set_bit(i, devs.d, BCH_SB_MEMBERS_MAX) {
struct bch_dev *ca = bch2_dev_rcu_noerror(c, i);
if (!ca)
continue;
struct bch_dev_usage usage;
bch2_dev_usage_read_fast(ca, &usage);
if (dev_buckets_free(ca, usage, watermark))
ret += ca->mi.durability;
}
return ret;
}
static unsigned bch2_bkey_durability_on_target(struct bch_fs *c, struct bkey_s_c k,
unsigned target)
{
struct bch_devs_mask devs = target_rw_devs(c, BCH_DATA_user, target);
struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k);
const union bch_extent_entry *entry;
struct extent_ptr_decoded p;
unsigned durability = 0;
bkey_for_each_ptr_decode(k.k, ptrs, p, entry) {
if (p.ptr.dev != BCH_SB_MEMBER_INVALID &&
test_bit(p.ptr.dev, devs.d))
durability += bch2_extent_ptr_durability(c, &p);
}
return durability;
}
static int bch2_can_do_write_btree(struct bch_fs *c, struct data_update_opts *opts, struct bkey_s_c k)
{
enum bch_watermark watermark = opts->commit_flags & BCH_WATERMARK_MASK;
if (opts->target)
if (durability_available_on_target(c, watermark, opts->target) >
bch2_bkey_durability_on_target(c, k, opts->target))
return 0;
if (!opts->target || !(opts->write_flags & BCH_WRITE_only_specified_devs))
if (durability_available_on_target(c, watermark, 0) >
bch2_bkey_durability(c, k))
return 0;
return bch_err_throw(c, data_update_fail_no_rw_devs);
}
int bch2_can_do_write(struct bch_fs *c, struct data_update_opts *opts,
struct bch_devs_list *devs_have)
struct bkey_s_c k, struct bch_devs_list *devs_have)
{
enum bch_watermark watermark = opts->commit_flags & BCH_WATERMARK_MASK;
@ -703,6 +765,11 @@ int bch2_can_do_write(struct bch_fs *c, struct data_update_opts *opts,
unlikely(c->open_buckets_nr_free <= bch2_open_buckets_reserved(watermark)))
return bch_err_throw(c, data_update_fail_would_block);
guard(rcu)();
if (bkey_is_btree_ptr(k.k))
return bch2_can_do_write_btree(c, opts, k);
unsigned target = opts->write_flags & BCH_WRITE_only_specified_devs
? opts->target
: 0;
@ -712,8 +779,6 @@ int bch2_can_do_write(struct bch_fs *c, struct data_update_opts *opts,
if (*i != BCH_SB_MEMBER_INVALID)
__clear_bit(*i, devs.d);
guard(rcu)();
unsigned i;
for_each_set_bit(i, devs.d, BCH_SB_MEMBERS_MAX) {
struct bch_dev *ca = bch2_dev_rcu_noerror(c, i);
@ -934,7 +999,7 @@ int bch2_data_update_init(struct btree_trans *trans,
* (i.e. trying to move a durability=2 replica to a target with a
* single durability=2 device)
*/
ret = bch2_can_do_write(c, &m->opts, &m->op.devs_have);
ret = bch2_can_do_write(c, &m->opts, k, &m->op.devs_have);
if (ret)
goto out;

View File

@ -89,7 +89,7 @@ struct bch_devs_list bch2_data_update_devs_keeping(struct bch_fs *,
struct data_update_opts *,
struct bkey_s_c);
int bch2_can_do_write(struct bch_fs *, struct data_update_opts *,
struct bch_devs_list *);
struct bkey_s_c, struct bch_devs_list *);
void bch2_data_update_exit(struct data_update *, int);
int bch2_data_update_init(struct btree_trans *, struct btree_iter *,

View File

@ -416,7 +416,11 @@
x(0, nocow_trylock_fail) \
x(BCH_ERR_nocow_trylock_fail, nocow_trylock_contended) \
x(BCH_ERR_nocow_trylock_fail, nocow_trylock_bucket_full) \
x(EINTR, recovery_cancelled)
x(EINTR, recovery_cancelled) \
x(0, shutdown_with_errors) \
x(BCH_ERR_shutdown_with_errors, shutdown_with_errors_fixed) \
x(BCH_ERR_shutdown_with_errors, shutdown_with_errors_unfixed) \
x(BCH_ERR_shutdown_with_errors, shutdown_with_emergency_ro)
enum bch_errcode {
BCH_ERR_START = 2048,

View File

@ -1851,6 +1851,27 @@ int bch2_fix_reflink_p(struct bch_fs *c)
fix_reflink_p_key(trans, &iter, k));
}
/* translate to return code of fsck commad - man(8) fsck */
int bch2_fs_fsck_errcode(struct bch_fs *c, struct printbuf *msg)
{
int ret = 0;
if (test_bit(BCH_FS_errors_fixed, &c->flags)) {
prt_printf(msg, "%s: errors fixed\n", c->name);
ret |= 1;
}
if (test_bit(BCH_FS_error, &c->flags)) {
prt_printf(msg, "%s: still has errors\n", c->name);
ret |= 4;
}
if (test_bit(BCH_FS_emergency_ro, &c->flags)) {
prt_printf(msg, "%s: fatal error (went emergency read-only)\n", c->name);
ret |= 4;
}
return ret;
}
#ifndef NO_BCACHEFS_CHARDEV
struct fsck_thread {
@ -1870,26 +1891,21 @@ static int bch2_fsck_offline_thread_fn(struct thread_with_stdio *stdio)
struct fsck_thread *thr = container_of(stdio, struct fsck_thread, thr);
struct bch_fs *c = thr->c;
int ret = PTR_ERR_OR_ZERO(c);
errptr_try(c);
c->recovery_task = current;
int ret = bch2_fs_start(c);
CLASS(printbuf, buf)();
if (ret)
return ret;
thr->c->recovery_task = current;
ret = bch2_fs_start(thr->c);
prt_printf(&buf, "%s: error starting filesystem: %s\n", c->name, bch2_err_str(ret));
else
ret = bch2_fs_fsck_errcode(c, &buf);
if (ret)
goto err;
bch2_stdio_redirect_write(&stdio->stdio, false, buf.buf, buf.pos);
if (test_bit(BCH_FS_errors_fixed, &c->flags)) {
bch2_stdio_redirect_printf(&stdio->stdio, false, "%s: errors fixed\n", c->name);
ret |= 1;
}
if (test_bit(BCH_FS_error, &c->flags)) {
bch2_stdio_redirect_printf(&stdio->stdio, false, "%s: still has errors\n", c->name);
ret |= 4;
}
err:
bch2_fs_stop(c);
bch2_fs_exit(c);
return ret;
}
@ -1898,12 +1914,16 @@ static const struct thread_with_stdio_ops bch2_offline_fsck_ops = {
.fn = bch2_fsck_offline_thread_fn,
};
static int parse_mount_opts_user(char __user *optstr_user, struct bch_opts *opts)
{
char *optstr __free(kfree) = errptr_try(strndup_user(optstr_user, 1 << 16));
return bch2_parse_mount_opts(NULL, opts, NULL, optstr, false);
}
long bch2_ioctl_fsck_offline(struct bch_ioctl_fsck_offline __user *user_arg)
{
struct bch_ioctl_fsck_offline arg;
struct fsck_thread *thr = NULL;
darray_const_str devs = {};
long ret = 0;
if (copy_from_user(&arg, user_arg, sizeof(arg)))
return -EFAULT;
@ -1914,42 +1934,30 @@ long bch2_ioctl_fsck_offline(struct bch_ioctl_fsck_offline __user *user_arg)
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
struct bch_opts opts = bch2_opts_empty();
if (arg.opts)
try(parse_mount_opts_user((char __user *)(unsigned long) arg.opts, &opts));
CLASS(darray_const_str, devs)();
for (size_t i = 0; i < arg.nr_devs; i++) {
u64 dev_u64;
ret = copy_from_user_errcode(&dev_u64, &user_arg->devs[i], sizeof(u64));
if (ret)
goto err;
try(copy_from_user_errcode(&dev_u64, &user_arg->devs[i], sizeof(u64)));
char *dev_str = strndup_user((char __user *)(unsigned long) dev_u64, PATH_MAX);
ret = PTR_ERR_OR_ZERO(dev_str);
if (ret)
goto err;
char *dev_str =
errptr_try(strndup_user((char __user *)(unsigned long) dev_u64, PATH_MAX));
ret = darray_push(&devs, dev_str);
int ret = darray_push(&devs, dev_str);
if (ret) {
kfree(dev_str);
goto err;
return ret;
}
}
thr = kzalloc(sizeof(*thr), GFP_KERNEL);
if (!thr) {
ret = -ENOMEM;
goto err;
}
struct fsck_thread *thr = kzalloc(sizeof(*thr), GFP_KERNEL);
if (!thr)
return -ENOMEM;
thr->opts = bch2_opts_empty();
if (arg.opts) {
char *optstr = strndup_user((char __user *)(unsigned long) arg.opts, 1 << 16);
ret = PTR_ERR_OR_ZERO(optstr) ?:
bch2_parse_mount_opts(NULL, &thr->opts, NULL, optstr, false);
if (!IS_ERR(optstr))
kfree(optstr);
if (ret)
goto err;
}
thr->opts = opts;
opt_set(thr->opts, stdio, (u64)(unsigned long)&thr->thr.stdio);
opt_set(thr->opts, read_only, 1);
@ -1966,17 +1974,13 @@ long bch2_ioctl_fsck_offline(struct bch_ioctl_fsck_offline __user *user_arg)
thr->c->opts.errors == BCH_ON_ERROR_panic)
thr->c->opts.errors = BCH_ON_ERROR_ro;
ret = __bch2_run_thread_with_stdio(&thr->thr);
out:
darray_for_each(devs, i)
kfree(*i);
darray_exit(&devs);
return ret;
err:
int ret = __bch2_run_thread_with_stdio(&thr->thr);
if (ret < 0) {
if (thr)
bch2_fsck_thread_exit(&thr->thr);
pr_err("ret %s", bch2_err_str(ret));
goto out;
}
return ret;
}
static int bch2_fsck_online_thread_fn(struct thread_with_stdio *stdio)
@ -2005,7 +2009,14 @@ static int bch2_fsck_online_thread_fn(struct thread_with_stdio *stdio)
true);
clear_bit(BCH_FS_in_fsck, &c->flags);
bch_err_fn(c, ret);
CLASS(printbuf, buf)();
if (ret)
prt_printf(&buf, "%s: error running recovery passes: %s\n", c->name, bch2_err_str(ret));
else
ret = bch2_fs_fsck_errcode(c, &buf);
if (ret)
bch2_stdio_redirect_write(&stdio->stdio, false, buf.buf, buf.pos);
c->stdio = NULL;
c->stdio_filter = NULL;
@ -2023,15 +2034,16 @@ static const struct thread_with_stdio_ops bch2_online_fsck_ops = {
long bch2_ioctl_fsck_online(struct bch_fs *c, struct bch_ioctl_fsck_online arg)
{
struct fsck_thread *thr = NULL;
long ret = 0;
if (arg.flags)
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
struct bch_opts opts = bch2_opts_empty();
if (arg.opts)
try(parse_mount_opts_user((char __user *)(unsigned long) arg.opts, &opts));
if (!bch2_ro_ref_tryget(c))
return -EROFS;
@ -2040,32 +2052,19 @@ long bch2_ioctl_fsck_online(struct bch_fs *c, struct bch_ioctl_fsck_online arg)
return -EAGAIN;
}
thr = kzalloc(sizeof(*thr), GFP_KERNEL);
struct fsck_thread *thr = kzalloc(sizeof(*thr), GFP_KERNEL);
if (!thr) {
ret = -ENOMEM;
goto err;
up(&c->recovery.run_lock);
bch2_ro_ref_put(c);
return -ENOMEM;
}
thr->c = c;
thr->opts = bch2_opts_empty();
thr->opts = opts;
if (arg.opts) {
char *optstr = strndup_user((char __user *)(unsigned long) arg.opts, 1 << 16);
ret = PTR_ERR_OR_ZERO(optstr) ?:
bch2_parse_mount_opts(c, &thr->opts, NULL, optstr, false);
if (!IS_ERR(optstr))
kfree(optstr);
if (ret)
goto err;
}
ret = bch2_run_thread_with_stdio(&thr->thr, &bch2_online_fsck_ops);
err:
int ret = bch2_run_thread_with_stdio(&thr->thr, &bch2_online_fsck_ops);
if (ret < 0) {
bch_err_fn(c, ret);
if (thr)
bch2_fsck_thread_exit(&thr->thr);
up(&c->recovery.run_lock);
bch2_ro_ref_put(c);

View File

@ -100,6 +100,7 @@ int bch2_check_directory_structure(struct bch_fs *);
int bch2_check_nlinks(struct bch_fs *);
int bch2_fix_reflink_p(struct bch_fs *);
int bch2_fs_fsck_errcode(struct bch_fs *, struct printbuf *);
long bch2_ioctl_fsck_offline(struct bch_ioctl_fsck_offline __user *);
long bch2_ioctl_fsck_online(struct bch_fs *, struct bch_ioctl_fsck_online);

View File

@ -137,7 +137,7 @@ void bch2_print_str(struct bch_fs *c, const char *prefix, const char *str)
struct stdio_redirect *stdio = bch2_fs_stdio_redirect(c);
if (unlikely(stdio)) {
bch2_stdio_redirect_printf(stdio, true, "%s", str);
bch2_stdio_redirect_write(stdio, true, str, strlen(str));
return;
}
#endif
@ -438,6 +438,7 @@ static bool __bch2_fs_emergency_read_only2(struct bch_fs *c, struct printbuf *ou
return ret;
}
/* Returns true if going ERO, false if we already are */
bool bch2_fs_emergency_read_only2(struct bch_fs *c, struct printbuf *out)
{
return __bch2_fs_emergency_read_only2(c, out, false);
@ -641,12 +642,12 @@ static void bch2_fs_release(struct kobject *kobj)
__bch2_fs_free(c);
}
void __bch2_fs_stop(struct bch_fs *c)
int bch2_fs_stop(struct bch_fs *c)
{
if (!test_and_set_bit(BCH_FS_stopping, &c->flags)) {
if (test_bit(BCH_FS_started, &c->flags))
bch_verbose(c, "shutting down");
set_bit(BCH_FS_stopping, &c->flags);
scoped_guard(rwsem_write, &c->state_lock)
bch2_fs_read_only(c);
@ -684,7 +685,16 @@ void __bch2_fs_stop(struct bch_fs *c)
flush_work(&c->btree_interior_update_work);
}
void bch2_fs_free(struct bch_fs *c)
if (test_bit(BCH_FS_emergency_ro, &c->flags))
return bch_err_throw(c, shutdown_with_emergency_ro);
if (test_bit(BCH_FS_error, &c->flags))
return bch_err_throw(c, shutdown_with_errors_unfixed);
if (test_bit(BCH_FS_errors_fixed, &c->flags))
return bch_err_throw(c, shutdown_with_errors_fixed);
return 0;
}
static void bch2_fs_free(struct bch_fs *c)
{
scoped_guard(mutex, &bch2_fs_list_lock)
list_del(&c->list);
@ -708,10 +718,11 @@ void bch2_fs_free(struct bch_fs *c)
kobject_put(&c->kobj);
}
void bch2_fs_stop(struct bch_fs *c)
int bch2_fs_exit(struct bch_fs *c)
{
__bch2_fs_stop(c);
int ret = bch2_fs_stop(c);
bch2_fs_free(c);
return ret;
}
static int bch2_fs_online(struct bch_fs *c)
@ -1260,7 +1271,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts *opts,
int ret = bch2_fs_init(c, sb, opts, sbs, out);
if (ret) {
bch2_fs_free(c);
bch2_fs_exit(c);
return ERR_PTR(ret);
}
@ -1516,7 +1527,7 @@ out:
return c;
err:
if (!IS_ERR_OR_NULL(c))
bch2_fs_stop(c);
bch2_fs_exit(c);
c = ERR_PTR(ret);
goto out;
}

View File

@ -43,15 +43,14 @@ void bch2_fs_read_only(struct bch_fs *);
int bch2_fs_read_write(struct bch_fs *);
int bch2_fs_read_write_early(struct bch_fs *);
int bch2_fs_init_rw(struct bch_fs *);
int bch2_fs_resize_on_mount(struct bch_fs *);
void __bch2_fs_stop(struct bch_fs *);
void bch2_fs_free(struct bch_fs *);
void bch2_fs_stop(struct bch_fs *);
int bch2_fs_init_rw(struct bch_fs *);
int bch2_fs_start(struct bch_fs *);
int bch2_fs_stop(struct bch_fs *);
int bch2_fs_exit(struct bch_fs *);
struct bch_fs *bch2_fs_open(darray_const_str *, struct bch_opts *);
#endif /* _BCACHEFS_SUPER_H */

View File

@ -358,8 +358,6 @@ int bch2_journal_update_last_seq_ondisk(struct journal *j, u64 last_seq_ondisk,
for (u64 seq = j->last_seq_ondisk; seq < last_seq_ondisk; seq++) {
struct journal_entry_pin_list *pin_list = journal_seq_pin(j, seq);
BUG_ON(atomic_read(&pin_list->count));
if (pin_list->devs.e.nr_devs) {
replicas_entry_refs *e = darray_find_p(*refs, i,
bch2_replicas_entry_eq(&i->replicas.e, &pin_list->devs.e));

View File

@ -17,6 +17,7 @@
#include "init/dev.h"
#include "init/error.h"
#include "init/fs.h"
#include "init/passes.h"
#include "sb/clean.h"
@ -1001,9 +1002,9 @@ int bch2_write_super(struct bch_fs *c)
{
struct closure *cl = &c->sb_write;
CLASS(printbuf, err)();
unsigned sb = 0, nr_wrote;
unsigned sb = 0;
struct bch_devs_mask sb_written;
bool wrote, can_mount_without_written, can_mount_with_written;
bool wrote;
unsigned degraded_flags = BCH_FORCE_IF_DEGRADED;
DARRAY(struct bch_dev *) online_devices = {};
int ret = 0;
@ -1174,32 +1175,35 @@ int bch2_write_super(struct bch_fs *c)
ca->disk_sb.seq = le64_to_cpu(ca->disk_sb.sb->seq);
}
nr_wrote = dev_mask_nr(&sb_written);
can_mount_with_written =
bch2_can_read_fs_with_devs(c, sb_written, degraded_flags, NULL);
struct bch_devs_mask sb_unwritten;
for (unsigned i = 0; i < ARRAY_SIZE(sb_written.d); i++)
sb_written.d[i] = ~sb_written.d[i];
sb_unwritten.d[i] = ~sb_written.d[i];
can_mount_without_written =
bch2_can_read_fs_with_devs(c, sb_written, degraded_flags, NULL);
printbuf_reset(&err);
bch2_log_msg_start(c, &err);
/*
* If we would be able to mount _without_ the devices we successfully
* wrote superblocks to, we weren't able to write to enough devices:
*
* Exception: if we can mount without the successes because we haven't
* written anything (new filesystem), we continue if we'd be able to
* mount with the devices we did successfully write to:
*/
if (bch2_fs_fatal_err_on(!nr_wrote ||
!can_mount_with_written ||
(can_mount_without_written &&
!can_mount_with_written), c,
": Unable to write superblock to sufficient devices (from %ps)",
(void *) _RET_IP_))
ret = bch_err_throw(c, erofs_sb_err);
unsigned nr_wrote = dev_mask_nr(&sb_written);
unsigned nr_members = bch2_sb_nr_devices(c->disk_sb.sb);
if (!nr_wrote ||
!bch2_can_read_fs_with_devs(c, sb_written, degraded_flags, NULL)) {
prt_printf(&err, "Unable to write superblock to sufficient devices (from %ps)\n",
(void *) _RET_IP_);
prt_printf(&err, "Would not be able to mount with written devices\n");
bch2_can_read_fs_with_devs(c, sb_written, degraded_flags, &err);
prt_printf(&err, "Wrote to %u/%u devices:\n", nr_wrote, nr_members);
scoped_guard(printbuf_indent, &err)
bch2_devs_mask_to_text_locked(&err, c, &sb_written);
prt_printf(&err, "Failed to write to devices:\n");
scoped_guard(printbuf_indent, &err)
bch2_devs_mask_to_text_locked(&err, c, &sb_unwritten);
if (bch2_fs_emergency_read_only2(c, &err))
bch2_print_str(c, KERN_ERR, err.buf);
}
out:
/* Make new options visible after they're persistent: */
bch2_sb_update(c);

View File

@ -319,11 +319,10 @@ static void bch2_member_to_text_short_sb(struct printbuf *out,
prt_newline(out);
}
void bch2_member_to_text_short(struct printbuf *out,
static void bch2_member_to_text_short_locked(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),
@ -331,6 +330,22 @@ void bch2_member_to_text_short(struct printbuf *out,
ca->dev_idx);
}
void bch2_member_to_text_short(struct printbuf *out,
struct bch_fs *c,
struct bch_dev *ca)
{
guard(mutex)(&c->sb_lock);
bch2_member_to_text_short_locked(out, c, ca);
}
void bch2_devs_mask_to_text_locked(struct printbuf *out, struct bch_fs *c,
struct bch_devs_mask *devs)
{
for_each_member_device(c, ca)
if (test_bit(ca->dev_idx, devs->d))
bch2_member_to_text_short_locked(out, c, ca);
}
static void member_to_text(struct printbuf *out,
struct bch_member m,
struct bch_sb_field_disk_groups *gi,

View File

@ -45,6 +45,7 @@ void bch2_member_to_text(struct printbuf *, struct bch_member *,
struct bch_sb *, unsigned);
void bch2_member_to_text_short(struct printbuf *, struct bch_fs *, struct bch_dev *);
void bch2_devs_mask_to_text_locked(struct printbuf *, struct bch_fs *, struct bch_devs_mask *);
static inline bool bch2_dev_is_online(struct bch_dev *ca)
{

View File

@ -20,19 +20,81 @@ struct { \
#define DARRAY(_type) DARRAY_PREALLOCATED(_type, 0)
typedef DARRAY(char) darray_char;
typedef DARRAY(char *) darray_str;
typedef DARRAY(const char *) darray_const_str;
#define __darray_for_each(_d, _i) \
for ((_i) = (_d).data; _i < (_d).data + (_d).nr; _i++)
typedef DARRAY(u8) darray_u8;
typedef DARRAY(u16) darray_u16;
typedef DARRAY(u32) darray_u32;
typedef DARRAY(u64) darray_u64;
#define darray_for_each_from(_d, _i, _start) \
for (typeof(&(_d).data[0]) _i = _start; _i < (_d).data + (_d).nr; _i++)
typedef DARRAY(s8) darray_s8;
typedef DARRAY(s16) darray_s16;
typedef DARRAY(s32) darray_s32;
typedef DARRAY(s64) darray_s64;
#define darray_for_each(_d, _i) \
darray_for_each_from(_d, _i, (_d).data)
#define darray_for_each_max(_d, _i, _nr) \
for (typeof(&(_d).data[0]) _i = (_d).data; _i < (_d).data + min(_nr, (_d).nr); _i++)
#define darray_for_each_reverse(_d, _i) \
for (typeof(&(_d).data[0]) _i = (_d).data + (_d).nr - 1; _i >= (_d).data && (_d).nr; --_i)
#define darray_init(_d) \
do { \
(_d)->nr = 0; \
(_d)->size = ARRAY_SIZE((_d)->preallocated); \
(_d)->data = (_d)->size ? (_d)->preallocated : NULL; \
} while (0)
#define darray_exit(_d) \
do { \
if (!ARRAY_SIZE((_d)->preallocated) || \
(_d)->data != (_d)->preallocated) \
kvfree((_d)->data); \
darray_init(_d); \
} while (0)
#define darray_exit_free_item(_d, _free) \
do { \
darray_for_each(*(_d), i) \
_free(*i); \
if (!ARRAY_SIZE((_d)->preallocated) || \
(_d)->data != (_d)->preallocated) \
kvfree((_d)->data); \
darray_init(_d); \
} while (0)
#define DEFINE_DARRAY_CLASS(_type) \
DEFINE_CLASS(_type, _type, darray_exit(&(_T)), (_type) {}, void)
#define DEFINE_DARRAY_CLASS_FREE_ITEM(_type, _free) \
DEFINE_CLASS(_type, _type, darray_exit_free_item(&(_T), _free), (_type) {}, void)
#define DEFINE_DARRAY(_type) \
typedef DARRAY(_type) darray_##_type; \
DEFINE_DARRAY_CLASS(darray_##_type)
#define DEFINE_DARRAY_PREALLOCATED(_type, _nr) \
typedef DARRAY_PREALLOCATED(_type, _nr) darray_##_type; \
DEFINE_DARRAY_CLASS(darray_##_type)
#define DEFINE_DARRAY_NAMED(_name, _type) \
typedef DARRAY(_type) _name; \
DEFINE_DARRAY_CLASS(_name)
#define DEFINE_DARRAY_NAMED_FREE_ITEM(_name, _type, _free) \
typedef DARRAY(_type) _name; \
DEFINE_DARRAY_CLASS_FREE_ITEM(_name, _free)
DEFINE_DARRAY(char);
DEFINE_DARRAY(u8)
DEFINE_DARRAY(u16)
DEFINE_DARRAY(u32)
DEFINE_DARRAY(u64)
DEFINE_DARRAY(s8)
DEFINE_DARRAY(s16)
DEFINE_DARRAY(s32)
DEFINE_DARRAY(s64)
DEFINE_DARRAY_NAMED_FREE_ITEM(darray_str, char *, kfree);
DEFINE_DARRAY_NAMED_FREE_ITEM(darray_const_str, const char *, kfree);
int __bch2_darray_resize_noprof(darray_char *, size_t, size_t, gfp_t, bool);
@ -108,70 +170,7 @@ int __bch2_darray_resize_noprof(darray_char *, size_t, size_t, gfp_t, bool);
#define darray_find(_d, _item) darray_find_p(_d, _i, *_i == _item)
/* Iteration: */
#define __darray_for_each(_d, _i) \
for ((_i) = (_d).data; _i < (_d).data + (_d).nr; _i++)
#define darray_for_each_from(_d, _i, _start) \
for (typeof(&(_d).data[0]) _i = _start; _i < (_d).data + (_d).nr; _i++)
#define darray_for_each(_d, _i) \
darray_for_each_from(_d, _i, (_d).data)
#define darray_for_each_max(_d, _i, _nr) \
for (typeof(&(_d).data[0]) _i = (_d).data; _i < (_d).data + min(_nr, (_d).nr); _i++)
#define darray_for_each_reverse(_d, _i) \
for (typeof(&(_d).data[0]) _i = (_d).data + (_d).nr - 1; _i >= (_d).data && (_d).nr; --_i)
#define darray_sort(_d, _cmp) \
sort((_d).data, (_d).nr, sizeof((_d).data[0]), _cmp, NULL)
/* Init/exit */
#define darray_init(_d) \
do { \
(_d)->nr = 0; \
(_d)->size = ARRAY_SIZE((_d)->preallocated); \
(_d)->data = (_d)->size ? (_d)->preallocated : NULL; \
} while (0)
#define darray_exit(_d) \
do { \
if (!ARRAY_SIZE((_d)->preallocated) || \
(_d)->data != (_d)->preallocated) \
kvfree((_d)->data); \
darray_init(_d); \
} while (0)
#define DEFINE_DARRAY_CLASS(_type) \
DEFINE_CLASS(_type, _type, darray_exit(&(_T)), (_type) {}, void)
#define DEFINE_DARRAY(_type) \
typedef DARRAY(_type) darray_##_type; \
DEFINE_DARRAY_CLASS(darray_##_type)
#define DEFINE_DARRAY_PREALLOCATED(_type, _nr) \
typedef DARRAY_PREALLOCATED(_type, _nr) darray_##_type; \
DEFINE_DARRAY_CLASS(darray_##_type)
#define DEFINE_DARRAY_NAMED(_name, _type) \
typedef DARRAY(_type) _name; \
DEFINE_DARRAY_CLASS(_name)
DEFINE_DARRAY_CLASS(darray_char);
DEFINE_DARRAY_CLASS(darray_str)
DEFINE_DARRAY_CLASS(darray_const_str)
DEFINE_DARRAY_CLASS(darray_u8)
DEFINE_DARRAY_CLASS(darray_u16)
DEFINE_DARRAY_CLASS(darray_u32)
DEFINE_DARRAY_CLASS(darray_u64)
DEFINE_DARRAY_CLASS(darray_s8)
DEFINE_DARRAY_CLASS(darray_s16)
DEFINE_DARRAY_CLASS(darray_s32)
DEFINE_DARRAY_CLASS(darray_s64)
#endif /* _BCACHEFS_DARRAY_H */

View File

@ -427,6 +427,38 @@ int bch2_stdio_redirect_readline(struct stdio_redirect *stdio, darray_char *line
return bch2_stdio_redirect_readline_timeout(stdio, line, MAX_SCHEDULE_TIMEOUT);
}
/* Always writes output atomically: return code is @len or an error */
ssize_t bch2_stdio_redirect_write(struct stdio_redirect *stdio, bool nonblocking,
const char *ubuf, size_t len)
{
struct stdio_buf *buf = &stdio->output;
while (true) {
if (stdio->done)
return -EPIPE;
bool wrote;
scoped_guard(spinlock_irqsave, &buf->lock) {
wrote = !darray_make_room_gfp(&buf->buf, len, GFP_NOWAIT);
if (wrote) {
memcpy(&darray_top(buf->buf), ubuf, len);
buf->buf.nr += len;
}
}
if (wrote) {
wake_up(&buf->wait);
return len;
}
if (nonblocking)
return -EAGAIN;
try(wait_event_interruptible(buf->wait,
stdio_redirect_has_output_space(stdio)));
}
}
__printf(3, 0)
static ssize_t bch2_darray_vprintf(darray_char *out, gfp_t gfp, const char *fmt, va_list args)
{

View File

@ -75,6 +75,7 @@ int bch2_stdio_redirect_read(struct stdio_redirect *, char *, size_t);
int bch2_stdio_redirect_readline_timeout(struct stdio_redirect *, darray_char *, unsigned long);
int bch2_stdio_redirect_readline(struct stdio_redirect *, darray_char *);
ssize_t bch2_stdio_redirect_write(struct stdio_redirect *, bool, const char *, size_t);
__printf(3, 0) ssize_t bch2_stdio_redirect_vprintf(struct stdio_redirect *, bool, const char *, va_list);
__printf(3, 4) ssize_t bch2_stdio_redirect_printf(struct stdio_redirect *, bool, const char *, ...);

View File

@ -989,13 +989,6 @@ u64 *bch2_acc_percpu_u64s(u64 __percpu *p, unsigned nr)
return ret;
}
void bch2_darray_str_exit(darray_const_str *d)
{
darray_for_each(*d, i)
kfree(*i);
darray_exit(d);
}
int bch2_split_devs(const char *_dev_name, darray_const_str *ret)
{
darray_init(ret);
@ -1019,6 +1012,6 @@ int bch2_split_devs(const char *_dev_name, darray_const_str *ret)
return 0;
err:
bch2_darray_str_exit(ret);
darray_exit_free_item(ret, kfree);
return -ENOMEM;
}

View File

@ -704,7 +704,6 @@ static inline bool qstr_eq(const struct qstr l, const struct qstr r)
return l.len == r.len && !memcmp(l.name, r.name, l.len);
}
void bch2_darray_str_exit(darray_const_str *);
int bch2_split_devs(const char *, darray_const_str *);
#ifdef __KERNEL__

View File

@ -2033,7 +2033,7 @@ static void bch2_put_super(struct super_block *sb)
{
struct bch_fs *c = sb->s_fs_info;
__bch2_fs_stop(c);
bch2_fs_stop(c);
}
/*
@ -2112,7 +2112,7 @@ static int bch2_fs_get_tree(struct fs_context *fc)
struct inode *vinode;
struct bch2_opts_parse *opts_parse = fc->fs_private;
struct bch_opts opts = opts_parse->opts;
darray_const_str devs;
darray_const_str devs = {};
darray_fs devs_to_fs = {};
int ret;
@ -2250,7 +2250,7 @@ out:
fc->root = dget(sb->s_root);
err:
darray_exit(&devs_to_fs);
bch2_darray_str_exit(&devs);
darray_exit_free_item(&devs, kfree);
if (ret)
pr_err("error: %s", bch2_err_str(ret));
/*
@ -2264,12 +2264,12 @@ err:
return bch2_err_class(ret);
err_stop_fs:
bch2_fs_stop(c);
bch2_fs_exit(c);
goto err;
err_put_super:
if (!sb->s_root)
__bch2_fs_stop(c);
bch2_fs_stop(c);
deactivate_locked_super(sb);
goto err;
}
@ -2279,7 +2279,7 @@ static void bch2_kill_sb(struct super_block *sb)
struct bch_fs *c = sb->s_fs_info;
generic_shutdown_super(sb);
bch2_fs_free(c);
bch2_fs_exit(c);
}
static void bch2_fs_context_free(struct fs_context *fc)