diff --git a/.bcachefs_revision b/.bcachefs_revision index b97d90f9..71cbc66a 100644 --- a/.bcachefs_revision +++ b/.bcachefs_revision @@ -1 +1 @@ -d800fc8b69ff90543270cdad1ef95c18371d4168 +5df84d32ad84d74ababcd783bf92ed1a1853e74d diff --git a/bch_bindgen/src/fs.rs b/bch_bindgen/src/fs.rs index 00c5d965..e7eb6eea 100644 --- a/bch_bindgen/src/fs.rs +++ b/bch_bindgen/src/fs.rs @@ -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); } } } diff --git a/c_src/cmd_counters.c b/c_src/cmd_counters.c index 9dc9a0c8..6a9f96fe 100644 --- a/c_src/cmd_counters.c +++ b/c_src/cmd_counters.c @@ -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; } diff --git a/c_src/cmd_device.c b/c_src/cmd_device.c index 2e31c90f..6d7f7084 100644 --- a/c_src/cmd_device.c +++ b/c_src/cmd_device.c @@ -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; } diff --git a/c_src/cmd_dump.c b/c_src/cmd_dump.c index d5f54393..ca8d2694 100644 --- a/c_src/cmd_dump.c +++ b/c_src/cmd_dump.c @@ -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); diff --git a/c_src/cmd_format.c b/c_src/cmd_format.c index 6b0a0124..5191545a 100644 --- a/c_src/cmd_format.c +++ b/c_src/cmd_format.c @@ -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); diff --git a/c_src/cmd_fsck.c b/c_src/cmd_fsck.c index d9a7acc6..14f04498 100644 --- a/c_src/cmd_fsck.c +++ b/c_src/cmd_fsck.c @@ -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; } diff --git a/c_src/cmd_fusemount.c b/c_src/cmd_fusemount.c index a6623bf5..17dbd7d8 100644 --- a/c_src/cmd_fusemount.c +++ b/c_src/cmd_fusemount.c @@ -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; } diff --git a/c_src/cmd_image.c b/c_src/cmd_image.c index f390489a..202236d8 100644 --- a/c_src/cmd_image.c +++ b/c_src/cmd_image.c @@ -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); diff --git a/c_src/cmd_key.c b/c_src/cmd_key.c index 06ab6f91..8e6ab135 100644 --- a/c_src/cmd_key.c +++ b/c_src/cmd_key.c @@ -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; } diff --git a/c_src/cmd_kill_btree_node.c b/c_src/cmd_kill_btree_node.c index 67cdcf3a..911970fc 100644 --- a/c_src/cmd_kill_btree_node.c +++ b/c_src/cmd_kill_btree_node.c @@ -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; } diff --git a/c_src/cmd_list_journal.c b/c_src/cmd_list_journal.c index 9b88a884..59ad7ad4 100644 --- a/c_src/cmd_list_journal.c +++ b/c_src/cmd_list_journal.c @@ -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; } diff --git a/c_src/cmd_migrate.c b/c_src/cmd_migrate.c index 2c52bf5c..0e35be3e 100644 --- a/c_src/cmd_migrate.c +++ b/c_src/cmd_migrate.c @@ -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; } diff --git a/c_src/cmd_option.c b/c_src/cmd_option.c index bd5fd3b1..e622375d 100644 --- a/c_src/cmd_option.c +++ b/c_src/cmd_option.c @@ -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; diff --git a/c_src/cmd_strip_alloc.c b/c_src/cmd_strip_alloc.c index d0344876..ff748cf4 100644 --- a/c_src/cmd_strip_alloc.c +++ b/c_src/cmd_strip_alloc.c @@ -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; } diff --git a/libbcachefs/data/move.c b/libbcachefs/data/move.c index 35192832..bead2463 100644 --- a/libbcachefs/data/move.c +++ b/libbcachefs/data/move.c @@ -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 diff --git a/libbcachefs/data/reconcile.c b/libbcachefs/data/reconcile.c index 7338882e..7a9f8c6a 100644 --- a/libbcachefs/data/reconcile.c +++ b/libbcachefs/data/reconcile.c @@ -1245,9 +1245,14 @@ static int reconcile_set_data_opts(struct btree_trans *trans, return 0; data_opts->type = BCH_DATA_UPDATE_reconcile; - if (!r->hipri) + 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; - data_opts->target = r->background_target; struct bkey_ptrs_c ptrs = bch2_bkey_ptrs_c(k); const union bch_extent_entry *entry; @@ -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)); diff --git a/libbcachefs/data/update.c b/libbcachefs/data/update.c index f6b15dee..ba84165f 100644 --- a/libbcachefs/data/update.c +++ b/libbcachefs/data/update.c @@ -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; diff --git a/libbcachefs/data/update.h b/libbcachefs/data/update.h index 66c2531d..5d681d3c 100644 --- a/libbcachefs/data/update.h +++ b/libbcachefs/data/update.h @@ -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 *, diff --git a/libbcachefs/errcode.h b/libbcachefs/errcode.h index 086ae6c6..bd427119 100644 --- a/libbcachefs/errcode.h +++ b/libbcachefs/errcode.h @@ -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, diff --git a/libbcachefs/fs/check.c b/libbcachefs/fs/check.c index b22625d2..747743de 100644 --- a/libbcachefs/fs/check.c +++ b/libbcachefs/fs/check.c @@ -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); + 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)); + } return ret; -err: - if (thr) - bch2_fsck_thread_exit(&thr->thr); - pr_err("ret %s", bch2_err_str(ret)); - goto out; } 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,33 +2052,20 @@ 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); + bch2_fsck_thread_exit(&thr->thr); up(&c->recovery.run_lock); bch2_ro_ref_put(c); } diff --git a/libbcachefs/fs/check.h b/libbcachefs/fs/check.h index 0f3a96e7..aeb170ac 100644 --- a/libbcachefs/fs/check.h +++ b/libbcachefs/fs/check.h @@ -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); diff --git a/libbcachefs/init/fs.c b/libbcachefs/init/fs.c index 5edaafb5..daee53f1 100644 --- a/libbcachefs/init/fs.c +++ b/libbcachefs/init/fs.c @@ -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,50 +642,59 @@ 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) { - bch_verbose(c, "shutting down"); + 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); - scoped_guard(rwsem_write, &c->state_lock) - bch2_fs_read_only(c); + for (unsigned i = 0; i < c->sb.nr_devices; i++) { + struct bch_dev *ca = rcu_dereference_protected(c->devs[i], true); + if (ca) + bch2_dev_io_ref_stop(ca, READ); + } - for (unsigned i = 0; i < c->sb.nr_devices; i++) { - struct bch_dev *ca = rcu_dereference_protected(c->devs[i], true); - if (ca) - bch2_dev_io_ref_stop(ca, READ); + for_each_member_device(c, ca) + bch2_dev_unlink(ca); + + if (c->kobj.state_in_sysfs) + kobject_del(&c->kobj); + + bch2_fs_debug_exit(c); + bch2_fs_chardev_exit(c); + + bch2_ro_ref_put(c); + wait_event(c->ro_ref_wait, !refcount_read(&c->ro_ref)); + + kobject_put(&c->counters_kobj); + kobject_put(&c->time_stats); + kobject_put(&c->opts_dir); + kobject_put(&c->internal); + + /* btree prefetch might have kicked off reads in the background: */ + bch2_btree_flush_all_reads(c); + + for_each_member_device(c, ca) + cancel_work_sync(&ca->io_error_work); + + cancel_work_sync(&c->read_only_work); + + flush_work(&c->btree_interior_update_work); } - for_each_member_device(c, ca) - bch2_dev_unlink(ca); - - if (c->kobj.state_in_sysfs) - kobject_del(&c->kobj); - - bch2_fs_debug_exit(c); - bch2_fs_chardev_exit(c); - - bch2_ro_ref_put(c); - wait_event(c->ro_ref_wait, !refcount_read(&c->ro_ref)); - - kobject_put(&c->counters_kobj); - kobject_put(&c->time_stats); - kobject_put(&c->opts_dir); - kobject_put(&c->internal); - - /* btree prefetch might have kicked off reads in the background: */ - bch2_btree_flush_all_reads(c); - - for_each_member_device(c, ca) - cancel_work_sync(&ca->io_error_work); - - cancel_work_sync(&c->read_only_work); - - flush_work(&c->btree_interior_update_work); + 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; } -void bch2_fs_free(struct bch_fs *c) +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; } diff --git a/libbcachefs/init/fs.h b/libbcachefs/init/fs.h index d138563a..ec88aea6 100644 --- a/libbcachefs/init/fs.h +++ b/libbcachefs/init/fs.h @@ -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 */ diff --git a/libbcachefs/journal/reclaim.c b/libbcachefs/journal/reclaim.c index 73bd1796..35209dc0 100644 --- a/libbcachefs/journal/reclaim.c +++ b/libbcachefs/journal/reclaim.c @@ -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)); diff --git a/libbcachefs/sb/io.c b/libbcachefs/sb/io.c index 2e91a1d0..a81d27b7 100644 --- a/libbcachefs/sb/io.c +++ b/libbcachefs/sb/io.c @@ -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); diff --git a/libbcachefs/sb/members.c b/libbcachefs/sb/members.c index be845944..9fe22ce7 100644 --- a/libbcachefs/sb/members.c +++ b/libbcachefs/sb/members.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, diff --git a/libbcachefs/sb/members.h b/libbcachefs/sb/members.h index 6f4b6552..912e6187 100644 --- a/libbcachefs/sb/members.h +++ b/libbcachefs/sb/members.h @@ -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) { diff --git a/libbcachefs/util/darray.h b/libbcachefs/util/darray.h index 68046bea..0cf2f300 100644 --- a/libbcachefs/util/darray.h +++ b/libbcachefs/util/darray.h @@ -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 */ diff --git a/libbcachefs/util/thread_with_file.c b/libbcachefs/util/thread_with_file.c index 7c2acf31..d4a908c3 100644 --- a/libbcachefs/util/thread_with_file.c +++ b/libbcachefs/util/thread_with_file.c @@ -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) { diff --git a/libbcachefs/util/thread_with_file.h b/libbcachefs/util/thread_with_file.h index 72497b92..83a0969f 100644 --- a/libbcachefs/util/thread_with_file.h +++ b/libbcachefs/util/thread_with_file.h @@ -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 *, ...); diff --git a/libbcachefs/util/util.c b/libbcachefs/util/util.c index cd743e74..c9891b70 100644 --- a/libbcachefs/util/util.c +++ b/libbcachefs/util/util.c @@ -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; } diff --git a/libbcachefs/util/util.h b/libbcachefs/util/util.h index c350dba3..bc8ff765 100644 --- a/libbcachefs/util/util.h +++ b/libbcachefs/util/util.h @@ -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__ diff --git a/libbcachefs/vfs/fs.c b/libbcachefs/vfs/fs.c index 3e6f263a..a64f471f 100644 --- a/libbcachefs/vfs/fs.c +++ b/libbcachefs/vfs/fs.c @@ -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)