cmd_image: finish_image()

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2025-07-06 14:28:34 -04:00
parent d0e71900a3
commit c1a25673e9

View File

@ -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);
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);
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;
}
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;
}