summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@linux.dev>2025-07-06 14:28:34 -0400
committerKent Overstreet <kent.overstreet@linux.dev>2025-07-12 19:49:41 -0400
commitc1a25673e937090449d4cd858c7cd89bc570d59d (patch)
treec10417e3a491bada992d0d042599b86e13bc5acd
parentd0e71900a3b3f65850a58141f814a38a4eb3ee79 (diff)
cmd_image: finish_image()
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
-rw-r--r--c_src/cmd_image.c276
1 files changed, 199 insertions, 77 deletions
diff --git a/c_src/cmd_image.c b/c_src/cmd_image.c
index a35ccd6f..2d1839ad 100644
--- a/c_src/cmd_image.c
+++ b/c_src/cmd_image.c
@@ -27,6 +27,7 @@
#include "libbcachefs/alloc_background.h"
#include "libbcachefs/alloc_foreground.h"
#include "libbcachefs/data_update.h"
+#include "libbcachefs/disk_accounting.h"
#include "libbcachefs/errcode.h"
#include "libbcachefs/journal_reclaim.h"
#include "libbcachefs/move.h"
@@ -168,19 +169,207 @@ err:
}
-static void print_dev_usage_all(struct bch_fs *c)
+static void prt_sectors(struct printbuf *out, u64 v)
+{
+ prt_tab(out);
+ prt_human_readable_u64(out, v << 9);
+ prt_tab_rjust(out);
+ prt_newline(out);
+}
+
+static void print_data_type_usage(struct printbuf *out,
+ struct bch_dev *ca,
+ struct bch_dev_usage_full u,
+ unsigned i)
+{
+ if (u.d[i].buckets) {
+ bch2_prt_data_type(out, i);
+ prt_sectors(out, bucket_to_sector(ca, u.d[i].buckets));
+ }
+
+ if (u.d[i].fragmented) {
+ bch2_prt_data_type(out, i);
+ prt_str(out, " fragmented");
+ prt_sectors(out, u.d[i].fragmented);
+ }
+}
+
+static void print_image_usage(struct bch_fs *c, bool keep_alloc, u64 nbuckets)
{
struct printbuf buf = PRINTBUF;
+ printbuf_tabstop_push(&buf, 24);
+ printbuf_tabstop_push(&buf, 16);
+ printbuf_tabstop_push(&buf, 16);
+ printbuf_tabstop_push(&buf, 8);
+
+ struct bch_dev *ca = c->devs[0];
+ struct bch_dev_usage_full dev_stats = bch2_dev_usage_full_read(ca);
+
+ print_data_type_usage(&buf, ca, dev_stats, BCH_DATA_sb);
+ print_data_type_usage(&buf, ca, dev_stats, BCH_DATA_journal);
+ print_data_type_usage(&buf, ca, dev_stats, BCH_DATA_btree);
+
+ printbuf_indent_add(&buf, 2);
+
+ for (unsigned i = 0; i < BTREE_ID_NR; i++) {
+ if (btree_id_is_alloc(i) && !keep_alloc)
+ continue;
+
+ struct disk_accounting_pos a;
+ disk_accounting_key_init(a, btree, i);
+
+ u64 v = 0;
+ bch2_accounting_mem_read(c, disk_accounting_pos_to_bpos(&a), &v, 1);
+
+ if (v) {
+ bch2_btree_id_to_text(&buf, i);
+ prt_sectors(&buf, v);
+ }
+ }
+
+ printbuf_indent_sub(&buf, 2);
+
+ struct disk_accounting_pos acc_replicas_key;
+ memset(&acc_replicas_key, 0, sizeof(acc_replicas_key));
+ acc_replicas_key.type = BCH_DISK_ACCOUNTING_replicas;
+ acc_replicas_key.replicas.data_type = BCH_DATA_user;
+ acc_replicas_key.replicas.nr_devs = 0;
+ acc_replicas_key.replicas.nr_required = 1;
+ acc_replicas_key.replicas.nr_required = 1;
+ replicas_entry_add_dev(&acc_replicas_key.replicas, 0);
+
+ u64 v = 0;
+ bch2_accounting_mem_read(c, disk_accounting_pos_to_bpos(&acc_replicas_key), &v, 1);
+ prt_printf(&buf, "user");
+ prt_sectors(&buf, v);
+
+ if (dev_stats.d[BCH_DATA_user].fragmented) {
+ prt_printf(&buf, "user fragmented");
+ prt_sectors(&buf, dev_stats.d[BCH_DATA_user].fragmented);
+ }
+
+ bool compression_header = false;
+ for (unsigned i = 1; i < BCH_COMPRESSION_TYPE_NR; i++) {
+ struct disk_accounting_pos a;
+ disk_accounting_key_init(a, compression, .type = i);
+ struct bpos p = disk_accounting_pos_to_bpos(&a);
+ u64 v[3];
+ bch2_accounting_mem_read(c, p, v, ARRAY_SIZE(v));
+
+ if (!v[0])
+ continue;
+
+ if (!compression_header) {
+ prt_printf(&buf, "compression type\tcompressed\runcompressed\rratio\r\n");
+ printbuf_indent_add(&buf, 2);
+ }
+ compression_header = true;
+
+ u64 sectors_uncompressed = v[1];
+ u64 sectors_compressed = v[2];
+
+ bch2_prt_compression_type(&buf, i);
+ prt_tab(&buf);
+
+ prt_human_readable_u64(&buf, sectors_compressed << 9);
+ prt_tab_rjust(&buf);
+
+ if (i == BCH_COMPRESSION_TYPE_incompressible) {
+ prt_newline(&buf);
+ continue;
+ }
- for_each_member_device(c, ca) {
- struct bch_dev_usage_full stats = bch2_dev_usage_full_read(ca);
- bch2_dev_usage_to_text(&buf, ca, &stats);
+ prt_human_readable_u64(&buf, sectors_uncompressed << 9);
+ prt_printf(&buf, "\r%llu%%\r\n",
+ div64_u64(sectors_compressed * 100,
+ sectors_uncompressed));
}
+ if (compression_header)
+ printbuf_indent_sub(&buf, 2);
+
+ prt_printf(&buf, "image size");
+ prt_sectors(&buf, bucket_to_sector(c->devs[0], nbuckets));
+
printf("%s", buf.buf);
printbuf_exit(&buf);
}
+static int finish_image(struct bch_fs *c,
+ bool keep_alloc,
+ unsigned verbosity)
+{
+ if (verbosity > 1)
+ printf("moving %stree to primary device\n",
+ keep_alloc ? "" : "non-alloc ");
+
+ mutex_lock(&c->sb_lock);
+ struct bch_member *m = bch2_members_v2_get_mut(c->disk_sb.sb, 0);
+ SET_BCH_MEMBER_DATA_ALLOWED(m, BCH_MEMBER_DATA_ALLOWED(m)|BIT(BCH_DATA_btree));
+ bch2_write_super(c);
+ mutex_unlock(&c->sb_lock);
+
+ bch2_dev_allocator_set_rw(c, c->devs[0], true);
+
+ int ret = move_btree(c, keep_alloc, 0);
+ bch_err_msg(c, ret, "migrating btree from temporary device");
+ if (ret)
+ return ret;
+
+ bch2_fs_read_only(c);
+
+ if (0)
+ check_gaps(c);
+
+ u64 nbuckets;
+ ret = get_nbuckets_used(c, &nbuckets);
+ if (ret)
+ return ret;
+
+ if (verbosity)
+ print_image_usage(c, keep_alloc, nbuckets);
+
+ if (ftruncate(c->devs[0]->disk_sb.bdev->bd_fd, nbuckets * bucket_bytes(c->devs[0]))) {
+ fprintf(stderr, "truncate error: %m\n");
+ return -errno;
+ }
+
+ mutex_lock(&c->sb_lock);
+ if (!keep_alloc) {
+ if (verbosity > 1)
+ printf("Stripping alloc info\n");
+ strip_fs_alloc(c);
+ }
+
+ rcu_assign_pointer(c->devs[1], NULL);
+
+ m = bch2_members_v2_get_mut(c->disk_sb.sb, 0);
+ SET_BCH_MEMBER_DATA_ALLOWED(m, BCH_MEMBER_DATA_ALLOWED(m)|BIT(BCH_DATA_journal));
+
+ bch2_members_v2_get_mut(c->disk_sb.sb, 0)->nbuckets = cpu_to_le64(nbuckets);
+
+ for_each_online_member(c, ca, 0) {
+ struct bch_member *m = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx);
+ SET_BCH_MEMBER_RESIZE_ON_MOUNT(m, true);
+ }
+
+ c->disk_sb.sb->features[0] |= cpu_to_le64(BIT_ULL(BCH_FEATURE_small_image));
+
+ /*
+ * sb->nr_devices must be 1 so that it can be mounted without UUID
+ * conflicts
+ */
+ unsigned u64s = DIV_ROUND_UP(sizeof(struct bch_sb_field_members_v2) +
+ sizeof(struct bch_member), sizeof(u64));
+ bch2_sb_field_resize(&c->disk_sb, members_v2, u64s);
+ c->disk_sb.sb->nr_devices = 1;
+ SET_BCH_SB_MULTI_DEVICE(c->disk_sb.sb, false);
+
+ bch2_write_super(c);
+ mutex_unlock(&c->sb_lock);
+ return 0;
+}
+
/*
* Build an image file:
*
@@ -195,9 +384,9 @@ static void print_dev_usage_all(struct bch_fs *c)
* metadata device is dropped.
*/
static void image_create(struct bch_opt_strs fs_opt_strs,
- struct bch_opts fs_opts,
+ struct bch_opts fs_opts,
struct format_opts format_opts,
- struct dev_opts dev_opts,
+ struct dev_opts dev_opts,
const char *src_path,
bool keep_alloc,
unsigned verbosity)
@@ -273,79 +462,11 @@ static void image_create(struct bch_opt_strs fs_opt_strs,
goto err;
struct copy_fs_state s = {};
- ret = copy_fs(c, &s, src_fd, src_path);
+ ret = copy_fs(c, &s, src_fd, src_path) ?:
+ finish_image(c, keep_alloc, verbosity);
if (ret)
goto err;
- if (verbosity > 1)
- printf("moving non-alloc btree to primary device\n");
-
- mutex_lock(&c->sb_lock);
- struct bch_member *m = bch2_members_v2_get_mut(c->disk_sb.sb, 0);
- SET_BCH_MEMBER_DATA_ALLOWED(m, BCH_MEMBER_DATA_ALLOWED(m)|BIT(BCH_DATA_btree));
- bch2_write_super(c);
- mutex_unlock(&c->sb_lock);
-
- bch2_dev_allocator_set_rw(c, c->devs[0], true);
-
- ret = move_btree(c, keep_alloc, 0);
- if (ret) {
- fprintf(stderr, "error migrating btree from temporary device: %s\n",
- bch2_err_str(ret));
- goto err;
- }
-
- bch2_fs_read_only(c);
-
- if (verbosity > 1)
- print_dev_usage_all(c);
-
- if (0)
- check_gaps(c);
-
- u64 nbuckets;
- ret = get_nbuckets_used(c, &nbuckets);
- if (ret)
- goto err;
-
- if (ftruncate(c->devs[0]->disk_sb.bdev->bd_fd, nbuckets * bucket_bytes(c->devs[0]))) {
- fprintf(stderr, "truncate error: %m\n");
- goto err;
- }
-
- mutex_lock(&c->sb_lock);
- if (!keep_alloc) {
- printf("Stripping alloc info\n");
- strip_fs_alloc(c);
- }
-
- rcu_assign_pointer(c->devs[1], NULL);
-
- m = bch2_members_v2_get_mut(c->disk_sb.sb, 0);
- SET_BCH_MEMBER_DATA_ALLOWED(m, BCH_MEMBER_DATA_ALLOWED(m)|BIT(BCH_DATA_journal));
-
- bch2_members_v2_get_mut(c->disk_sb.sb, 0)->nbuckets = cpu_to_le64(nbuckets);
-
- for_each_online_member(c, ca, 0) {
- struct bch_member *m = bch2_members_v2_get_mut(c->disk_sb.sb, ca->dev_idx);
- SET_BCH_MEMBER_RESIZE_ON_MOUNT(m, true);
- }
-
- c->disk_sb.sb->features[0] |= cpu_to_le64(BIT_ULL(BCH_FEATURE_small_image));
-
- /*
- * sb->nr_devices must be 1 so that it can be mounted without UUID
- * conflicts
- */
- unsigned u64s = DIV_ROUND_UP(sizeof(struct bch_sb_field_members_v2) +
- sizeof(struct bch_member), sizeof(u64));
- bch2_sb_field_resize(&c->disk_sb, members_v2, u64s);
- c->disk_sb.sb->nr_devices = 1;
- SET_BCH_SB_MULTI_DEVICE(c->disk_sb.sb, false);
-
- bch2_write_super(c);
- mutex_unlock(&c->sb_lock);
-
bch2_fs_stop(c);
darray_exit(&device_paths);
xclose(src_fd);
@@ -481,7 +602,8 @@ static int cmd_image_create(int argc, char *argv[])
dev_opts.path = argv[0];
- image_create(fs_opt_strs, fs_opts, opts, dev_opts, opts.source, keep_alloc, verbosity);
+ image_create(fs_opt_strs, fs_opts, opts, dev_opts, opts.source,
+ keep_alloc, verbosity);
bch2_opt_strs_free(&fs_opt_strs);
return 0;
}