mirror of
https://github.com/koverstreet/bcachefs-tools.git
synced 2025-12-08 00:00:12 +03:00
Update bcachefs sources to 5df84d32ad84 bcachefs: bch2_can_do_write_btree()
This commit is contained in:
parent
6500754c5c
commit
7094e625c0
@ -1 +1 @@
|
||||
d800fc8b69ff90543270cdad1ef95c18371d4168
|
||||
5df84d32ad84d74ababcd783bf92ed1a1853e74d
|
||||
|
||||
@ -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); }
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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 *,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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 *, ...);
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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__
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user