diff options
Diffstat (limited to 'c_src/cmd_migrate.c')
-rw-r--r-- | c_src/cmd_migrate.c | 116 |
1 files changed, 76 insertions, 40 deletions
diff --git a/c_src/cmd_migrate.c b/c_src/cmd_migrate.c index aa17a160..4e4fbcba 100644 --- a/c_src/cmd_migrate.c +++ b/c_src/cmd_migrate.c @@ -86,6 +86,15 @@ found: return ret; } +static void mark_nouse_range(struct bch_dev *ca, u64 sector_from, u64 sector_to) +{ + u64 b = sector_to_bucket(ca, sector_from); + do { + set_bit(b, ca->buckets_nouse); + b++; + } while (bucket_to_sector(ca, b) < sector_to); +} + static void mark_unreserved_space(struct bch_fs *c, ranges extents) { struct bch_dev *ca = c->devs[0]; @@ -93,17 +102,16 @@ static void mark_unreserved_space(struct bch_fs *c, ranges extents) struct range i; for_each_hole(iter, extents, bucket_to_sector(ca, ca->mi.nbuckets) << 9, i) { - u64 b; - if (i.start == i.end) return; - b = sector_to_bucket(ca, i.start >> 9); - do { - set_bit(b, ca->buckets_nouse); - b++; - } while (bucket_to_sector(ca, b) << 9 < i.end); + mark_nouse_range(ca, i.start >> 9, + round_up(i.end, 1 << 9) >> 9); } + + /* Also be sure to mark the space for the default sb layout */ + unsigned sb_size = 1U << ca->disk_sb.sb->layout.sb_max_size_bits; + mark_nouse_range(ca, 0, BCH_SB_SECTOR + sb_size * 2); } static ranges reserve_new_fs_space(const char *file_path, unsigned block_size, @@ -279,7 +287,7 @@ static int migrate_fs(const char *fs_path, .dev = stat.st_dev, .extents = extents, .type = BCH_MIGRATE_migrate, - .reserve_start = roundup((format_opts.superblock_size * 2 + 8) << 9, + .reserve_start = roundup((format_opts.superblock_size * 2 + BCH_SB_SECTOR) << 9, bucket_bytes(c->devs[0])), }; @@ -381,6 +389,28 @@ static void migrate_superblock_usage(void) "Report bugs to <linux-bcachefs@vger.kernel.org>"); } +static void add_default_sb_layout(struct bch_sb* sb, unsigned *out_sb_size) +{ + unsigned sb_size = 1U << sb->layout.sb_max_size_bits; + if (out_sb_size) + *out_sb_size = sb_size; + + if (sb->layout.nr_superblocks >= ARRAY_SIZE(sb->layout.sb_offset)) + die("Can't add superblock: no space left in superblock layout"); + + for (unsigned i = 0; i < sb->layout.nr_superblocks; i++) + if (le64_to_cpu(sb->layout.sb_offset[i]) == BCH_SB_SECTOR || + le64_to_cpu(sb->layout.sb_offset[i]) == BCH_SB_SECTOR + sb_size) + die("Superblock layout already has default superblocks"); + + memmove(&sb->layout.sb_offset[2], + &sb->layout.sb_offset[0], + sb->layout.nr_superblocks * sizeof(u64)); + sb->layout.nr_superblocks += 2; + sb->layout.sb_offset[0] = cpu_to_le64(BCH_SB_SECTOR); + sb->layout.sb_offset[1] = cpu_to_le64(BCH_SB_SECTOR + sb_size); +} + int cmd_migrate_superblock(int argc, char *argv[]) { static const struct option longopts[] = { @@ -414,34 +444,23 @@ int cmd_migrate_superblock(int argc, char *argv[]) if (!sb_offset) die("Please specify offset of existing superblock"); - int fd = xopen(devs.data[0], O_RDWR); + int fd = xopen(devs.data[0], O_RDWR | O_EXCL); struct bch_sb *sb = __bch2_super_read(fd, sb_offset); - unsigned sb_size = 1U << sb->layout.sb_max_size_bits; + unsigned sb_size; + /* Check for invocation errors early */ + add_default_sb_layout(sb, &sb_size); - if (sb->layout.nr_superblocks >= ARRAY_SIZE(sb->layout.sb_offset)) - die("Can't add superblock: no space left in superblock layout"); - - for (unsigned i = 0; i < sb->layout.nr_superblocks; i++) - if (le64_to_cpu(sb->layout.sb_offset[i]) == BCH_SB_SECTOR || - le64_to_cpu(sb->layout.sb_offset[i]) == BCH_SB_SECTOR + sb_size) - die("Superblock layout already has default superblocks"); - - memmove(&sb->layout.sb_offset[2], - &sb->layout.sb_offset[0], - sb->layout.nr_superblocks * sizeof(u64)); - sb->layout.nr_superblocks += 2; - sb->layout.sb_offset[0] = cpu_to_le64(BCH_SB_SECTOR); - sb->layout.sb_offset[1] = cpu_to_le64(BCH_SB_SECTOR + sb_size); + /* Rewrite first 0-3.5k bytes with zeroes, ensuring we blow away + * the old superblock */ + // TODO: fix the "Superblock write was silently dropped" warning properly + static const char zeroes[(BCH_SB_SECTOR << 9) + sizeof(struct bch_sb)]; + xpwrite(fd, zeroes, ARRAY_SIZE(zeroes), 0, "zeroing start of disk"); - /* also write first 0-3.5k bytes with zeroes, ensure we blow away old - * superblock */ - static const char zeroes[BCH_SB_SECTOR << 9]; - xpwrite(fd, zeroes, BCH_SB_SECTOR << 9, 0, "zeroing start of disk"); - - bch2_super_write(fd, sb); xclose(fd); - /* mark new superblocks */ + /* We start a normal FS instance with the sb buckets temporarily + * prohibited from allocation, performing any recovery/upgrade/downgrade + * as needed, and only then change the superblock layout */ struct bch_opts opts = bch2_opts_empty(); opt_set(opts, nostart, true); @@ -454,29 +473,46 @@ int cmd_migrate_superblock(int argc, char *argv[]) die("error opening filesystem: %s", bch2_err_str(ret)); struct bch_dev *ca = c->devs[0]; - for (u64 b = 0; bucket_to_sector(ca, b) < BCH_SB_SECTOR + sb_size * 2; b++) - set_bit(b, ca->buckets_nouse); + mark_nouse_range(ca, 0, BCH_SB_SECTOR + sb_size * 2); ret = bch2_fs_start(c); if (ret) die("Error starting filesystem: %s", bch2_err_str(ret)); + BUG_ON(1U << ca->disk_sb.sb->layout.sb_max_size_bits != sb_size); + + /* Here the FS is already RW. + * Apply the superblock layout changes first, everything else can be + * repaired on a subsequent recovery */ + add_default_sb_layout(ca->disk_sb.sb, NULL); + ret = bch2_write_super(c); + if (ret) + die("Error writing superblock: %s", bch2_err_str(ret)); + + /* Now explicitly mark the new sb buckets in FS metadata */ + ret = bch2_trans_mark_dev_sb(c, ca, BTREE_TRIGGER_transactional); + if (ret) + die("Error marking superblock buckets: %s", bch2_err_str(ret)); + bch2_fs_stop(c); +#if CONFIG_BCACHEFS_DEBUG + /* Verify that filesystem is clean and consistent */ + opts = bch2_opts_empty(); opt_set(opts, fsck, true); opt_set(opts, fix_errors, true); - - /* - * Hack: the free space counters are coming out wrong after marking the - * new superblock, but it's just the device counters so it's - * inconsequential: - */ + opt_set(opts, nochanges, true); c = bch2_fs_open(&devs, &opts); ret = PTR_ERR_OR_ZERO(c); if (ret) - die("error opening filesystem: %s", bch2_err_str(ret)); + die("error checking filesystem: %s", bch2_err_str(ret)); + + 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); +#endif return 0; } |