X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libbcachefs%2Fsuper-io.c;h=b9af78203fb895fbec60da9d6d8435e02e271cd8;hb=5966e59eaeb4033f40ddd84fbe6445598a908163;hp=c917bdda514531654248ebf65987d35d379d5afa;hpb=74148a8ee52526c44752f1773365963f18734ac9;p=bcachefs-tools-debian diff --git a/libbcachefs/super-io.c b/libbcachefs/super-io.c index c917bdd..b9af782 100644 --- a/libbcachefs/super-io.c +++ b/libbcachefs/super-io.c @@ -17,10 +17,13 @@ #include "super-io.h" #include "super.h" #include "vstructs.h" +#include "counters.h" #include #include +#include + const char * const bch2_sb_fields[] = { #define x(name, nr) #name, BCH_SB_FIELDS() @@ -96,8 +99,7 @@ void bch2_sb_field_delete(struct bch_sb_handle *sb, void bch2_free_super(struct bch_sb_handle *sb) { - if (sb->bio) - bio_put(sb->bio); + kfree(sb->bio); if (!IS_ERR_OR_NULL(sb->bdev)) blkdev_put(sb->bdev, sb->mode); @@ -124,11 +126,9 @@ int bch2_sb_realloc(struct bch_sb_handle *sb, unsigned u64s) u64 max_bytes = 512 << sb->sb->layout.sb_max_size_bits; if (new_bytes > max_bytes) { - char buf[BDEVNAME_SIZE]; - - pr_err("%s: superblock too big: want %zu but have %llu", - bdevname(sb->bdev, buf), new_bytes, max_bytes); - return -ENOSPC; + pr_err("%pg: superblock too big: want %zu but have %llu", + sb->bdev, new_bytes, max_bytes); + return -BCH_ERR_ENOSPC_sb; } } @@ -139,13 +139,15 @@ int bch2_sb_realloc(struct bch_sb_handle *sb, unsigned u64s) return -ENOMEM; if (sb->have_bio) { - bio = bio_kmalloc(GFP_KERNEL, - DIV_ROUND_UP(new_buffer_size, PAGE_SIZE)); + unsigned nr_bvecs = DIV_ROUND_UP(new_buffer_size, PAGE_SIZE); + + bio = bio_kmalloc(nr_bvecs, GFP_KERNEL); if (!bio) return -ENOMEM; - if (sb->bio) - bio_put(sb->bio); + bio_init(bio, NULL, bio->bi_inline_vecs, nr_bvecs, 0); + + kfree(sb->bio); sb->bio = bio; } @@ -208,25 +210,26 @@ static int validate_sb_layout(struct bch_sb_layout *layout, struct printbuf *out u64 offset, prev_offset, max_sectors; unsigned i; - if (uuid_le_cmp(layout->magic, BCACHE_MAGIC)) { - pr_buf(out, "Not a bcachefs superblock layout"); - return -EINVAL; + if (uuid_le_cmp(layout->magic, BCACHE_MAGIC) && + uuid_le_cmp(layout->magic, BCHFS_MAGIC)) { + prt_printf(out, "Not a bcachefs superblock layout"); + return -BCH_ERR_invalid_sb_layout; } if (layout->layout_type != 0) { - pr_buf(out, "Invalid superblock layout type %u", + prt_printf(out, "Invalid superblock layout type %u", layout->layout_type); - return -EINVAL; + return -BCH_ERR_invalid_sb_layout_type; } if (!layout->nr_superblocks) { - pr_buf(out, "Invalid superblock layout: no superblocks"); - return -EINVAL; + prt_printf(out, "Invalid superblock layout: no superblocks"); + return -BCH_ERR_invalid_sb_layout_nr_superblocks; } if (layout->nr_superblocks > ARRAY_SIZE(layout->sb_offset)) { - pr_buf(out, "Invalid superblock layout: too many superblocks"); - return -EINVAL; + prt_printf(out, "Invalid superblock layout: too many superblocks"); + return -BCH_ERR_invalid_sb_layout_nr_superblocks; } max_sectors = 1 << layout->sb_max_size_bits; @@ -237,10 +240,10 @@ static int validate_sb_layout(struct bch_sb_layout *layout, struct printbuf *out offset = le64_to_cpu(layout->sb_offset[i]); if (offset < prev_offset + max_sectors) { - pr_buf(out, "Invalid superblock layout: superblocks overlap\n" + prt_printf(out, "Invalid superblock layout: superblocks overlap\n" " (sb %u ends at %llu next starts at %llu", i - 1, prev_offset + max_sectors, offset); - return -EINVAL; + return -BCH_ERR_invalid_sb_layout_superblocks_overlap; } prev_offset = offset; } @@ -248,7 +251,8 @@ static int validate_sb_layout(struct bch_sb_layout *layout, struct printbuf *out return 0; } -static int bch2_sb_validate(struct bch_sb_handle *disk_sb, struct printbuf *out) +static int bch2_sb_validate(struct bch_sb_handle *disk_sb, struct printbuf *out, + int rw) { struct bch_sb *sb = disk_sb->sb; struct bch_sb_field *f; @@ -264,65 +268,77 @@ static int bch2_sb_validate(struct bch_sb_handle *disk_sb, struct printbuf *out) : version; if (version >= bcachefs_metadata_version_max) { - pr_buf(out, "Unsupported superblock version %u (min %u, max %u)", + prt_printf(out, "Unsupported superblock version %u (min %u, max %u)", version, bcachefs_metadata_version_min, bcachefs_metadata_version_max); - return -EINVAL; + return -BCH_ERR_invalid_sb_version; } if (version_min < bcachefs_metadata_version_min) { - pr_buf(out, "Unsupported superblock version %u (min %u, max %u)", + prt_printf(out, "Unsupported superblock version %u (min %u, max %u)", version_min, bcachefs_metadata_version_min, bcachefs_metadata_version_max); - return -EINVAL; + return -BCH_ERR_invalid_sb_version; } if (version_min > version) { - pr_buf(out, "Bad minimum version %u, greater than version field %u", + prt_printf(out, "Bad minimum version %u, greater than version field %u", version_min, version); - return -EINVAL; + return -BCH_ERR_invalid_sb_version; } if (sb->features[1] || (le64_to_cpu(sb->features[0]) & (~0ULL << BCH_FEATURE_NR))) { - pr_buf(out, "Filesystem has incompatible features"); - return -EINVAL; + prt_printf(out, "Filesystem has incompatible features"); + return -BCH_ERR_invalid_sb_features; } block_size = le16_to_cpu(sb->block_size); if (block_size > PAGE_SECTORS) { - pr_buf(out, "Block size too big (got %u, max %u)", + prt_printf(out, "Block size too big (got %u, max %u)", block_size, PAGE_SECTORS); - return -EINVAL; + return -BCH_ERR_invalid_sb_block_size; } if (bch2_is_zero(sb->user_uuid.b, sizeof(uuid_le))) { - pr_buf(out, "Bad user UUID (got zeroes)"); - return -EINVAL; + prt_printf(out, "Bad user UUID (got zeroes)"); + return -BCH_ERR_invalid_sb_uuid; } if (bch2_is_zero(sb->uuid.b, sizeof(uuid_le))) { - pr_buf(out, "Bad intenal UUID (got zeroes)"); - return -EINVAL; + prt_printf(out, "Bad intenal UUID (got zeroes)"); + return -BCH_ERR_invalid_sb_uuid; } if (!sb->nr_devices || sb->nr_devices > BCH_SB_MEMBERS_MAX) { - pr_buf(out, "Bad number of member devices %u (max %u)", + prt_printf(out, "Bad number of member devices %u (max %u)", sb->nr_devices, BCH_SB_MEMBERS_MAX); - return -EINVAL; + return -BCH_ERR_invalid_sb_too_many_members; } if (sb->dev_idx >= sb->nr_devices) { - pr_buf(out, "Bad dev_idx (got %u, nr_devices %u)", + prt_printf(out, "Bad dev_idx (got %u, nr_devices %u)", sb->dev_idx, sb->nr_devices); - return -EINVAL; + return -BCH_ERR_invalid_sb_dev_idx; } if (!sb->time_precision || le32_to_cpu(sb->time_precision) > NSEC_PER_SEC) { - pr_buf(out, "Invalid time precision: %u (min 1, max %lu)", + prt_printf(out, "Invalid time precision: %u (min 1, max %lu)", le32_to_cpu(sb->time_precision), NSEC_PER_SEC); - return -EINVAL; + return -BCH_ERR_invalid_sb_time_precision; + } + + if (rw == READ) { + /* + * Been seeing a bug where these are getting inexplicably + * zeroed, so we'r now validating them, but we have to be + * careful not to preven people's filesystems from mounting: + */ + if (!BCH_SB_JOURNAL_FLUSH_DELAY(sb)) + SET_BCH_SB_JOURNAL_FLUSH_DELAY(sb, 1000); + if (!BCH_SB_JOURNAL_RECLAIM_DELAY(sb)) + SET_BCH_SB_JOURNAL_RECLAIM_DELAY(sb, 1000); } for (opt_id = 0; opt_id < bch2_opts_nr; opt_id++) { @@ -331,7 +347,7 @@ static int bch2_sb_validate(struct bch_sb_handle *disk_sb, struct printbuf *out) if (opt->get_sb != BCH2_NO_SB_OPT) { u64 v = bch2_opt_from_sb(sb, opt_id); - pr_buf(out, "Invalid option "); + prt_printf(out, "Invalid option "); ret = bch2_opt_validate(opt, v, out); if (ret) return ret; @@ -347,23 +363,23 @@ static int bch2_sb_validate(struct bch_sb_handle *disk_sb, struct printbuf *out) vstruct_for_each(sb, f) { if (!f->u64s) { - pr_buf(out, "Invalid superblock: optional with size 0 (type %u)", + prt_printf(out, "Invalid superblock: optional field with size 0 (type %u)", le32_to_cpu(f->type)); - return -EINVAL; + return -BCH_ERR_invalid_sb_field_size; } if (vstruct_next(f) > vstruct_last(sb)) { - pr_buf(out, "Invalid superblock: optional field extends past end of superblock (type %u)", + prt_printf(out, "Invalid superblock: optional field extends past end of superblock (type %u)", le32_to_cpu(f->type)); - return -EINVAL; + return -BCH_ERR_invalid_sb_field_size; } } /* members must be validated first: */ mi = bch2_sb_get_members(sb); if (!mi) { - pr_buf(out, "Invalid superblock: member info area missing"); - return -EINVAL; + prt_printf(out, "Invalid superblock: member info area missing"); + return -BCH_ERR_invalid_sb_members_missing; } ret = bch2_sb_field_validate(sb, &mi->field, out); @@ -416,7 +432,7 @@ static void bch2_sb_update(struct bch_fs *c) ca->mi = bch2_mi_to_cpu(mi->members + i); } -static void __copy_super(struct bch_sb_handle *dst_handle, struct bch_sb *src) +static int __copy_super(struct bch_sb_handle *dst_handle, struct bch_sb *src) { struct bch_sb_field *src_f, *dst_f; struct bch_sb *dst = dst_handle->sb; @@ -441,42 +457,45 @@ static void __copy_super(struct bch_sb_handle *dst_handle, struct bch_sb *src) memcpy(dst->compat, src->compat, sizeof(dst->compat)); for (i = 0; i < BCH_SB_FIELD_NR; i++) { + int d; + if ((1U << i) & BCH_SINGLE_DEVICE_SB_FIELDS) continue; src_f = bch2_sb_field_get(src, i); dst_f = bch2_sb_field_get(dst, i); + + d = (src_f ? le32_to_cpu(src_f->u64s) : 0) - + (dst_f ? le32_to_cpu(dst_f->u64s) : 0); + if (d > 0) { + int ret = bch2_sb_realloc(dst_handle, le32_to_cpu(dst_handle->sb->u64s) + d); + if (ret) + return ret; + + dst = dst_handle->sb; + dst_f = bch2_sb_field_get(dst, i); + } + dst_f = __bch2_sb_field_resize(dst_handle, dst_f, src_f ? le32_to_cpu(src_f->u64s) : 0); if (src_f) memcpy(dst_f, src_f, vstruct_bytes(src_f)); } + + return 0; } int bch2_sb_to_fs(struct bch_fs *c, struct bch_sb *src) { - struct bch_sb_field_journal *journal_buckets = - bch2_sb_get_journal(src); - unsigned journal_u64s = journal_buckets - ? le32_to_cpu(journal_buckets->field.u64s) - : 0; int ret; lockdep_assert_held(&c->sb_lock); - ret = bch2_sb_realloc(&c->disk_sb, - le32_to_cpu(src->u64s) - journal_u64s); - if (ret) - return ret; - - __copy_super(&c->disk_sb, src); - - ret = bch2_sb_replicas_to_cpu_replicas(c); - if (ret) - return ret; - - ret = bch2_sb_disk_groups_to_cpu(c); + ret = bch2_sb_realloc(&c->disk_sb, 0) ?: + __copy_super(&c->disk_sb, src) ?: + bch2_sb_replicas_to_cpu_replicas(c) ?: + bch2_sb_disk_groups_to_cpu(c); if (ret) return ret; @@ -486,21 +505,7 @@ int bch2_sb_to_fs(struct bch_fs *c, struct bch_sb *src) int bch2_sb_from_fs(struct bch_fs *c, struct bch_dev *ca) { - struct bch_sb *src = c->disk_sb.sb, *dst = ca->disk_sb.sb; - struct bch_sb_field_journal *journal_buckets = - bch2_sb_get_journal(dst); - unsigned journal_u64s = journal_buckets - ? le32_to_cpu(journal_buckets->field.u64s) - : 0; - unsigned u64s = le32_to_cpu(src->u64s) + journal_u64s; - int ret; - - ret = bch2_sb_realloc(&ca->disk_sb, u64s); - if (ret) - return ret; - - __copy_super(&ca->disk_sb, src); - return 0; + return __copy_super(&ca->disk_sb, c->disk_sb.sb); } /* read superblock: */ @@ -512,21 +517,20 @@ static int read_one_super(struct bch_sb_handle *sb, u64 offset, struct printbuf size_t bytes; int ret; reread: - bio_reset(sb->bio); - bio_set_dev(sb->bio, sb->bdev); + bio_reset(sb->bio, sb->bdev, REQ_OP_READ|REQ_SYNC|REQ_META); sb->bio->bi_iter.bi_sector = offset; - bio_set_op_attrs(sb->bio, REQ_OP_READ, REQ_SYNC|REQ_META); bch2_bio_map(sb->bio, sb->sb, sb->buffer_size); ret = submit_bio_wait(sb->bio); if (ret) { - pr_buf(err, "IO error: %i", ret); + prt_printf(err, "IO error: %i", ret); return ret; } - if (uuid_le_cmp(sb->sb->magic, BCACHE_MAGIC)) { - pr_buf(err, "Not a bcachefs superblock"); - return -EINVAL; + if (uuid_le_cmp(sb->sb->magic, BCACHE_MAGIC) && + uuid_le_cmp(sb->sb->magic, BCHFS_MAGIC)) { + prt_printf(err, "Not a bcachefs superblock"); + return -BCH_ERR_invalid_sb_magic; } version = le16_to_cpu(sb->sb->version); @@ -535,23 +539,23 @@ reread: : version; if (version >= bcachefs_metadata_version_max) { - pr_buf(err, "Unsupported superblock version %u (min %u, max %u)", + prt_printf(err, "Unsupported superblock version %u (min %u, max %u)", version, bcachefs_metadata_version_min, bcachefs_metadata_version_max); - return -EINVAL; + return -BCH_ERR_invalid_sb_version; } if (version_min < bcachefs_metadata_version_min) { - pr_buf(err, "Unsupported superblock version %u (min %u, max %u)", + prt_printf(err, "Unsupported superblock version %u (min %u, max %u)", version_min, bcachefs_metadata_version_min, bcachefs_metadata_version_max); - return -EINVAL; + return -BCH_ERR_invalid_sb_version; } bytes = vstruct_bytes(sb->sb); if (bytes > 512 << sb->sb->layout.sb_max_size_bits) { - pr_buf(err, "Invalid superblock: too big (got %zu bytes, layout max %lu)", + prt_printf(err, "Invalid superblock: too big (got %zu bytes, layout max %lu)", bytes, 512UL << sb->sb->layout.sb_max_size_bits); - return -EINVAL; + return -BCH_ERR_invalid_sb_too_big; } if (bytes > sb->buffer_size) { @@ -561,8 +565,8 @@ reread: } if (BCH_SB_CSUM_TYPE(sb->sb) >= BCH_CSUM_NR) { - pr_buf(err, "unknown checksum type %llu", BCH_SB_CSUM_TYPE(sb->sb)); - return -EINVAL; + prt_printf(err, "unknown checksum type %llu", BCH_SB_CSUM_TYPE(sb->sb)); + return -BCH_ERR_invalid_sb_csum_type; } /* XXX: verify MACs */ @@ -570,8 +574,8 @@ reread: null_nonce(), sb->sb); if (bch2_crc_cmp(csum, sb->sb->csum)) { - pr_buf(err, "bad checksum"); - return -EINVAL; + prt_printf(err, "bad checksum"); + return -BCH_ERR_invalid_sb_csum; } sb->seq = le64_to_cpu(sb->sb->seq); @@ -618,12 +622,12 @@ int bch2_read_super(const char *path, struct bch_opts *opts, ret = bch2_sb_realloc(sb, 0); if (ret) { - pr_buf(&err, "error allocating memory for superblock"); + prt_printf(&err, "error allocating memory for superblock"); goto err; } if (bch2_fs_init_fault("read_super")) { - pr_buf(&err, "dynamic fault"); + prt_printf(&err, "dynamic fault"); ret = -EFAULT; goto err; } @@ -643,10 +647,8 @@ int bch2_read_super(const char *path, struct bch_opts *opts, * Error reading primary superblock - read location of backup * superblocks: */ - bio_reset(sb->bio); - bio_set_dev(sb->bio, sb->bdev); + bio_reset(sb->bio, sb->bdev, REQ_OP_READ|REQ_SYNC|REQ_META); sb->bio->bi_iter.bi_sector = BCH_SB_LAYOUT_SECTOR; - bio_set_op_attrs(sb->bio, REQ_OP_READ, REQ_SYNC|REQ_META); /* * use sb buffer to read layout, since sb buffer is page aligned but * layout won't be: @@ -655,7 +657,7 @@ int bch2_read_super(const char *path, struct bch_opts *opts, ret = submit_bio_wait(sb->bio); if (ret) { - pr_buf(&err, "IO error: %i", ret); + prt_printf(&err, "IO error: %i", ret); goto err; } @@ -681,17 +683,17 @@ int bch2_read_super(const char *path, struct bch_opts *opts, got_super: if (le16_to_cpu(sb->sb->block_size) << 9 < bdev_logical_block_size(sb->bdev)) { - pr_buf(&err, "block size (%u) smaller than device block size (%u)", + prt_printf(&err, "block size (%u) smaller than device block size (%u)", le16_to_cpu(sb->sb->block_size) << 9, bdev_logical_block_size(sb->bdev)); - ret = -EINVAL; + ret = -BCH_ERR_block_size_too_small; goto err; } ret = 0; sb->have_layout = true; - ret = bch2_sb_validate(sb, &err); + ret = bch2_sb_validate(sb, &err, READ); if (ret) { printk(KERN_ERR "bcachefs (%s): error validating superblock: %s", path, err.buf); @@ -730,12 +732,10 @@ static void read_back_super(struct bch_fs *c, struct bch_dev *ca) struct bch_sb *sb = ca->disk_sb.sb; struct bio *bio = ca->disk_sb.bio; - bio_reset(bio); - bio_set_dev(bio, ca->disk_sb.bdev); + bio_reset(bio, ca->disk_sb.bdev, REQ_OP_READ|REQ_SYNC|REQ_META); bio->bi_iter.bi_sector = le64_to_cpu(sb->layout.sb_offset[0]); bio->bi_end_io = write_super_endio; bio->bi_private = ca; - bio_set_op_attrs(bio, REQ_OP_READ, REQ_SYNC|REQ_META); bch2_bio_map(bio, ca->sb_read_scratch, PAGE_SIZE); this_cpu_add(ca->io_done->sectors[READ][BCH_DATA_sb], @@ -756,12 +756,10 @@ static void write_one_super(struct bch_fs *c, struct bch_dev *ca, unsigned idx) sb->csum = csum_vstruct(c, BCH_SB_CSUM_TYPE(sb), null_nonce(), sb); - bio_reset(bio); - bio_set_dev(bio, ca->disk_sb.bdev); + bio_reset(bio, ca->disk_sb.bdev, REQ_OP_WRITE|REQ_SYNC|REQ_META); bio->bi_iter.bi_sector = le64_to_cpu(sb->offset); bio->bi_end_io = write_super_endio; bio->bi_private = ca; - bio_set_op_attrs(bio, REQ_OP_WRITE, REQ_SYNC|REQ_META); bch2_bio_map(bio, sb, roundup((size_t) vstruct_bytes(sb), bdev_logical_block_size(ca->disk_sb.bdev))); @@ -784,6 +782,8 @@ int bch2_write_super(struct bch_fs *c) unsigned degraded_flags = BCH_FORCE_IF_DEGRADED; int ret = 0; + trace_and_count(c, write_super, c, _RET_IP_); + if (c->opts.very_degraded) degraded_flags |= BCH_FORCE_IF_LOST; @@ -792,6 +792,11 @@ int bch2_write_super(struct bch_fs *c) closure_init_stack(cl); memset(&sb_written, 0, sizeof(sb_written)); + if (c->opts.version_upgrade) { + c->disk_sb.sb->magic = BCHFS_MAGIC; + c->disk_sb.sb->layout.magic = BCHFS_MAGIC; + } + le64_add_cpu(&c->disk_sb.sb->seq, 1); if (test_bit(BCH_FS_ERROR, &c->flags)) @@ -801,13 +806,15 @@ int bch2_write_super(struct bch_fs *c) SET_BCH_SB_BIG_ENDIAN(c->disk_sb.sb, CPU_BIG_ENDIAN); + bch2_sb_counters_from_cpu(c); + for_each_online_member(ca, c, i) bch2_sb_from_fs(c, ca); for_each_online_member(ca, c, i) { printbuf_reset(&err); - ret = bch2_sb_validate(&ca->disk_sb, &err); + ret = bch2_sb_validate(&ca->disk_sb, &err, WRITE); if (ret) { bch2_fs_inconsistent(c, "sb invalid before write: %s", err.buf); percpu_ref_put(&ca->io_ref); @@ -818,6 +825,13 @@ int bch2_write_super(struct bch_fs *c) if (c->opts.nochanges) goto out; + /* + * Defer writing the superblock until filesystem initialization is + * complete - don't write out a partly initialized superblock: + */ + if (!BCH_SB_INITIALIZED(c->disk_sb.sb)) + goto out; + for_each_online_member(ca, c, i) { __set_bit(ca->dev_idx, sb_written.d); ca->sb_write_error = 0; @@ -837,7 +851,7 @@ int bch2_write_super(struct bch_fs *c) le64_to_cpu(ca->sb_read_scratch->seq), ca->disk_sb.seq); percpu_ref_put(&ca->io_ref); - ret = -EROFS; + ret = -BCH_ERR_erofs_sb_err; goto out; } @@ -847,7 +861,7 @@ int bch2_write_super(struct bch_fs *c) le64_to_cpu(ca->sb_read_scratch->seq), ca->disk_sb.seq); percpu_ref_put(&ca->io_ref); - ret = -EROFS; + ret = -BCH_ERR_erofs_sb_err; goto out; } } @@ -926,8 +940,8 @@ static int bch2_sb_members_validate(struct bch_sb *sb, if ((void *) (mi->members + sb->nr_devices) > vstruct_end(&mi->field)) { - pr_buf(err, "too many devices for section size"); - return -EINVAL; + prt_printf(err, "too many devices for section size"); + return -BCH_ERR_invalid_sb_members; } for (i = 0; i < sb->nr_devices; i++) { @@ -937,30 +951,30 @@ static int bch2_sb_members_validate(struct bch_sb *sb, continue; if (le64_to_cpu(m->nbuckets) > LONG_MAX) { - pr_buf(err, "device %u: too many buckets (got %llu, max %lu)", + prt_printf(err, "device %u: too many buckets (got %llu, max %lu)", i, le64_to_cpu(m->nbuckets), LONG_MAX); - return -EINVAL; + return -BCH_ERR_invalid_sb_members; } if (le64_to_cpu(m->nbuckets) - le16_to_cpu(m->first_bucket) < BCH_MIN_NR_NBUCKETS) { - pr_buf(err, "device %u: not enough buckets (got %llu, max %u)", + prt_printf(err, "device %u: not enough buckets (got %llu, max %u)", i, le64_to_cpu(m->nbuckets), BCH_MIN_NR_NBUCKETS); - return -EINVAL; + return -BCH_ERR_invalid_sb_members; } if (le16_to_cpu(m->bucket_size) < le16_to_cpu(sb->block_size)) { - pr_buf(err, "device %u: bucket size %u smaller than block size %u", + prt_printf(err, "device %u: bucket size %u smaller than block size %u", i, le16_to_cpu(m->bucket_size), le16_to_cpu(sb->block_size)); - return -EINVAL; + return -BCH_ERR_invalid_sb_members; } if (le16_to_cpu(m->bucket_size) < BCH_SB_BTREE_NODE_SIZE(sb)) { - pr_buf(err, "device %u: bucket size %u smaller than btree node size %llu", + prt_printf(err, "device %u: bucket size %u smaller than btree node size %llu", i, le16_to_cpu(m->bucket_size), BCH_SB_BTREE_NODE_SIZE(sb)); - return -EINVAL; + return -BCH_ERR_invalid_sb_members; } } @@ -983,97 +997,96 @@ static void bch2_sb_members_to_text(struct printbuf *out, struct bch_sb *sb, if (!bch2_member_exists(m)) continue; - pr_buf(out, "Device:"); - pr_tab(out); - pr_buf(out, "%u", i); - pr_newline(out); + prt_printf(out, "Device:"); + prt_tab(out); + prt_printf(out, "%u", i); + prt_newline(out); - pr_indent_push(out, 2); + printbuf_indent_add(out, 2); - pr_buf(out, "UUID:"); - pr_tab(out); + prt_printf(out, "UUID:"); + prt_tab(out); pr_uuid(out, m->uuid.b); - pr_newline(out); - - pr_buf(out, "Size:"); - pr_tab(out); - pr_units(out, device_size, device_size << 9); - pr_newline(out); - - pr_buf(out, "Bucket size:"); - pr_tab(out); - pr_units(out, bucket_size, bucket_size << 9); - pr_newline(out); - - pr_buf(out, "First bucket:"); - pr_tab(out); - pr_buf(out, "%u", le16_to_cpu(m->first_bucket)); - pr_newline(out); - - pr_buf(out, "Buckets:"); - pr_tab(out); - pr_buf(out, "%llu", le64_to_cpu(m->nbuckets)); - pr_newline(out); - - pr_buf(out, "Last mount:"); - pr_tab(out); + prt_newline(out); + + prt_printf(out, "Size:"); + prt_tab(out); + prt_units_u64(out, device_size << 9); + prt_newline(out); + + prt_printf(out, "Bucket size:"); + prt_tab(out); + prt_units_u64(out, bucket_size << 9); + prt_newline(out); + + prt_printf(out, "First bucket:"); + prt_tab(out); + prt_printf(out, "%u", le16_to_cpu(m->first_bucket)); + prt_newline(out); + + prt_printf(out, "Buckets:"); + prt_tab(out); + prt_printf(out, "%llu", le64_to_cpu(m->nbuckets)); + prt_newline(out); + + prt_printf(out, "Last mount:"); + prt_tab(out); if (m->last_mount) pr_time(out, le64_to_cpu(m->last_mount)); else - pr_buf(out, "(never)"); - pr_newline(out); + prt_printf(out, "(never)"); + prt_newline(out); - pr_buf(out, "State:"); - pr_tab(out); - pr_buf(out, "%s", + prt_printf(out, "State:"); + prt_tab(out); + prt_printf(out, "%s", BCH_MEMBER_STATE(m) < BCH_MEMBER_STATE_NR ? bch2_member_states[BCH_MEMBER_STATE(m)] : "unknown"); - pr_newline(out); + prt_newline(out); - pr_buf(out, "Group:"); - pr_tab(out); + prt_printf(out, "Label:"); + prt_tab(out); if (BCH_MEMBER_GROUP(m)) { unsigned idx = BCH_MEMBER_GROUP(m) - 1; if (idx < disk_groups_nr(gi)) - pr_buf(out, "%s (%u)", + prt_printf(out, "%s (%u)", gi->entries[idx].label, idx); else - pr_buf(out, "(bad disk labels section)"); + prt_printf(out, "(bad disk labels section)"); } else { - pr_buf(out, "(none)"); + prt_printf(out, "(none)"); } - pr_newline(out); + prt_newline(out); - pr_buf(out, "Data allowed:"); - pr_tab(out); + prt_printf(out, "Data allowed:"); + prt_tab(out); if (BCH_MEMBER_DATA_ALLOWED(m)) - bch2_flags_to_text(out, bch2_data_types, - BCH_MEMBER_DATA_ALLOWED(m)); + prt_bitflags(out, bch2_data_types, BCH_MEMBER_DATA_ALLOWED(m)); else - pr_buf(out, "(none)"); - pr_newline(out); + prt_printf(out, "(none)"); + prt_newline(out); - pr_buf(out, "Has data:"); - pr_tab(out); + prt_printf(out, "Has data:"); + prt_tab(out); if (data_have) - bch2_flags_to_text(out, bch2_data_types, data_have); + prt_bitflags(out, bch2_data_types, data_have); else - pr_buf(out, "(none)"); - pr_newline(out); + prt_printf(out, "(none)"); + prt_newline(out); - pr_buf(out, "Discard:"); - pr_tab(out); - pr_buf(out, "%llu", BCH_MEMBER_DISCARD(m)); - pr_newline(out); + prt_printf(out, "Discard:"); + prt_tab(out); + prt_printf(out, "%llu", BCH_MEMBER_DISCARD(m)); + prt_newline(out); - pr_buf(out, "Freespace initialized:"); - pr_tab(out); - pr_buf(out, "%llu", BCH_MEMBER_FREESPACE_INITIALIZED(m)); - pr_newline(out); + prt_printf(out, "Freespace initialized:"); + prt_tab(out); + prt_printf(out, "%llu", BCH_MEMBER_FREESPACE_INITIALIZED(m)); + prt_newline(out); - pr_indent_pop(out, 2); + printbuf_indent_sub(out, 2); } } @@ -1091,14 +1104,14 @@ static int bch2_sb_crypt_validate(struct bch_sb *sb, struct bch_sb_field_crypt *crypt = field_to_type(f, crypt); if (vstruct_bytes(&crypt->field) < sizeof(*crypt)) { - pr_buf(err, "wrong size (got %zu should be %zu)", + prt_printf(err, "wrong size (got %zu should be %zu)", vstruct_bytes(&crypt->field), sizeof(*crypt)); - return -EINVAL; + return -BCH_ERR_invalid_sb_crypt; } if (BCH_CRYPT_KDF_TYPE(crypt)) { - pr_buf(err, "bad kdf type %llu", BCH_CRYPT_KDF_TYPE(crypt)); - return -EINVAL; + prt_printf(err, "bad kdf type %llu", BCH_CRYPT_KDF_TYPE(crypt)); + return -BCH_ERR_invalid_sb_crypt; } return 0; @@ -1109,14 +1122,14 @@ static void bch2_sb_crypt_to_text(struct printbuf *out, struct bch_sb *sb, { struct bch_sb_field_crypt *crypt = field_to_type(f, crypt); - pr_buf(out, "KFD: %llu", BCH_CRYPT_KDF_TYPE(crypt)); - pr_newline(out); - pr_buf(out, "scrypt n: %llu", BCH_KDF_SCRYPT_N(crypt)); - pr_newline(out); - pr_buf(out, "scrypt r: %llu", BCH_KDF_SCRYPT_R(crypt)); - pr_newline(out); - pr_buf(out, "scrypt p: %llu", BCH_KDF_SCRYPT_P(crypt)); - pr_newline(out); + prt_printf(out, "KFD: %llu", BCH_CRYPT_KDF_TYPE(crypt)); + prt_newline(out); + prt_printf(out, "scrypt n: %llu", BCH_KDF_SCRYPT_N(crypt)); + prt_newline(out); + prt_printf(out, "scrypt r: %llu", BCH_KDF_SCRYPT_R(crypt)); + prt_newline(out); + prt_printf(out, "scrypt p: %llu", BCH_KDF_SCRYPT_P(crypt)); + prt_newline(out); } static const struct bch_sb_field_ops bch_sb_field_ops_crypt = { @@ -1134,7 +1147,7 @@ int bch2_sb_clean_validate_late(struct bch_fs *c, struct bch_sb_field_clean *cle for (entry = clean->start; entry < (struct jset_entry *) vstruct_end(&clean->field); entry = vstruct_next(entry)) { - ret = bch2_journal_entry_validate(c, "superblock", entry, + ret = bch2_journal_entry_validate(c, NULL, entry, le16_to_cpu(c->disk_sb.sb->version), BCH_SB_BIG_ENDIAN(c->disk_sb.sb), write); @@ -1236,7 +1249,8 @@ void bch2_journal_super_entries_add_common(struct bch_fs *c, u->entry.type = BCH_JSET_ENTRY_data_usage; u->v = cpu_to_le64(c->usage_base->replicas[i]); - memcpy(&u->r, e, replicas_entry_bytes(e)); + unsafe_memcpy(&u->r, e, replicas_entry_bytes(e), + "embedded variable length struct"); } for_each_member_device(ca, c, dev) { @@ -1249,7 +1263,6 @@ void bch2_journal_super_entries_add_common(struct bch_fs *c, u->entry.type = BCH_JSET_ENTRY_dev_usage; u->dev = cpu_to_le32(dev); u->buckets_ec = cpu_to_le64(ca->usage_base->buckets_ec); - u->buckets_unavailable = cpu_to_le64(ca->usage_base->buckets_unavailable); for (i = 0; i < BCH_DATA_NR; i++) { u->d[i].buckets = cpu_to_le64(ca->usage_base->d[i].buckets); @@ -1333,9 +1346,9 @@ static int bch2_sb_clean_validate(struct bch_sb *sb, struct bch_sb_field_clean *clean = field_to_type(f, clean); if (vstruct_bytes(&clean->field) < sizeof(*clean)) { - pr_buf(err, "wrong size (got %zu should be %zu)", + prt_printf(err, "wrong size (got %zu should be %zu)", vstruct_bytes(&clean->field), sizeof(*clean)); - return -EINVAL; + return -BCH_ERR_invalid_sb_clean; } return 0; @@ -1347,10 +1360,10 @@ static void bch2_sb_clean_to_text(struct printbuf *out, struct bch_sb *sb, struct bch_sb_field_clean *clean = field_to_type(f, clean); struct jset_entry *entry; - pr_buf(out, "flags: %x", le32_to_cpu(clean->flags)); - pr_newline(out); - pr_buf(out, "journal_seq: %llu", le64_to_cpu(clean->journal_seq)); - pr_newline(out); + prt_printf(out, "flags: %x", le32_to_cpu(clean->flags)); + prt_newline(out); + prt_printf(out, "journal_seq: %llu", le64_to_cpu(clean->journal_seq)); + prt_newline(out); for (entry = clean->start; entry != vstruct_end(&clean->field); @@ -1360,7 +1373,7 @@ static void bch2_sb_clean_to_text(struct printbuf *out, struct bch_sb *sb, continue; bch2_journal_entry_to_text(out, NULL, entry); - pr_newline(out); + prt_newline(out); } } @@ -1388,10 +1401,10 @@ static int bch2_sb_field_validate(struct bch_sb *sb, struct bch_sb_field *f, ret = bch2_sb_field_ops[type]->validate(sb, f, &field_err); if (ret) { - pr_buf(err, "Invalid superblock section %s: %s", + prt_printf(err, "Invalid superblock section %s: %s", bch2_sb_fields[type], field_err.buf); - pr_newline(err); + prt_newline(err); bch2_sb_field_to_text(err, sb, f); } @@ -1406,21 +1419,21 @@ void bch2_sb_field_to_text(struct printbuf *out, struct bch_sb *sb, const struct bch_sb_field_ops *ops = type < BCH_SB_FIELD_NR ? bch2_sb_field_ops[type] : NULL; - if (!out->tabstops[0]) - out->tabstops[0] = 32; + if (!out->nr_tabstops) + printbuf_tabstop_push(out, 32); if (ops) - pr_buf(out, "%s", bch2_sb_fields[type]); + prt_printf(out, "%s", bch2_sb_fields[type]); else - pr_buf(out, "(unknown field %u)", type); + prt_printf(out, "(unknown field %u)", type); - pr_buf(out, " (size %zu):", vstruct_bytes(f)); - pr_newline(out); + prt_printf(out, " (size %zu):", vstruct_bytes(f)); + prt_newline(out); if (ops && ops->to_text) { - pr_indent_push(out, 2); + printbuf_indent_add(out, 2); bch2_sb_field_ops[type]->to_text(out, sb, f); - pr_indent_pop(out, 2); + printbuf_indent_sub(out, 2); } } @@ -1428,25 +1441,23 @@ void bch2_sb_layout_to_text(struct printbuf *out, struct bch_sb_layout *l) { unsigned i; - pr_buf(out, "Type: %u", l->layout_type); - pr_newline(out); + prt_printf(out, "Type: %u", l->layout_type); + prt_newline(out); - pr_buf(out, "Superblock max size: "); - pr_units(out, - 1 << l->sb_max_size_bits, - 512 << l->sb_max_size_bits); - pr_newline(out); + prt_str(out, "Superblock max size: "); + prt_units_u64(out, 512 << l->sb_max_size_bits); + prt_newline(out); - pr_buf(out, "Nr superblocks: %u", l->nr_superblocks); - pr_newline(out); + prt_printf(out, "Nr superblocks: %u", l->nr_superblocks); + prt_newline(out); - pr_buf(out, "Offsets: "); + prt_str(out, "Offsets: "); for (i = 0; i < l->nr_superblocks; i++) { if (i) - pr_buf(out, ", "); - pr_buf(out, "%llu", le64_to_cpu(l->sb_offset[i])); + prt_str(out, ", "); + prt_printf(out, "%llu", le64_to_cpu(l->sb_offset[i])); } - pr_newline(out); + prt_newline(out); } void bch2_sb_to_text(struct printbuf *out, struct bch_sb *sb, @@ -1457,8 +1468,8 @@ void bch2_sb_to_text(struct printbuf *out, struct bch_sb *sb, u64 fields_have = 0; unsigned nr_devices = 0; - if (!out->tabstops[0]) - out->tabstops[0] = 32; + if (!out->nr_tabstops) + printbuf_tabstop_push(out, 44); mi = bch2_sb_get_members(sb); if (mi) { @@ -1470,87 +1481,85 @@ void bch2_sb_to_text(struct printbuf *out, struct bch_sb *sb, nr_devices += bch2_member_exists(m); } - pr_buf(out, "External UUID:"); - pr_tab(out); + prt_printf(out, "External UUID:"); + prt_tab(out); pr_uuid(out, sb->user_uuid.b); - pr_newline(out); + prt_newline(out); - pr_buf(out, "Internal UUID:"); - pr_tab(out); + prt_printf(out, "Internal UUID:"); + prt_tab(out); pr_uuid(out, sb->uuid.b); - pr_newline(out); - - pr_buf(out, "Device index:"); - pr_tab(out); - pr_buf(out, "%u", sb->dev_idx); - pr_newline(out); - - pr_buf(out, "Label:"); - pr_tab(out); - pr_buf(out, "%.*s", (int) sizeof(sb->label), sb->label); - pr_newline(out); - - pr_buf(out, "Version:"); - pr_tab(out); - pr_buf(out, "%s", bch2_metadata_versions[le16_to_cpu(sb->version)]); - pr_newline(out); - - pr_buf(out, "Oldest version on disk:"); - pr_tab(out); - pr_buf(out, "%u", bch2_metadata_versions[le16_to_cpu(sb->version_min)]); - pr_newline(out); - - pr_buf(out, "Created:"); - pr_tab(out); + prt_newline(out); + + prt_str(out, "Device index:"); + prt_tab(out); + prt_printf(out, "%u", sb->dev_idx); + prt_newline(out); + + prt_str(out, "Label:"); + prt_tab(out); + prt_printf(out, "%.*s", (int) sizeof(sb->label), sb->label); + prt_newline(out); + + prt_str(out, "Version:"); + prt_tab(out); + prt_printf(out, "%s", bch2_metadata_versions[le16_to_cpu(sb->version)]); + prt_newline(out); + + prt_printf(out, "Oldest version on disk:"); + prt_tab(out); + prt_printf(out, "%s", bch2_metadata_versions[le16_to_cpu(sb->version_min)]); + prt_newline(out); + + prt_printf(out, "Created:"); + prt_tab(out); if (sb->time_base_lo) pr_time(out, div_u64(le64_to_cpu(sb->time_base_lo), NSEC_PER_SEC)); else - pr_buf(out, "(not set)"); - pr_newline(out); - - pr_buf(out, "Sequence number:"); - pr_tab(out); - pr_buf(out, "%llu", le64_to_cpu(sb->seq)); - pr_newline(out); - - pr_buf(out, "Superblock size:"); - pr_tab(out); - pr_buf(out, "%zu", vstruct_bytes(sb)); - pr_newline(out); - - pr_buf(out, "Clean:"); - pr_tab(out); - pr_buf(out, "%llu", BCH_SB_CLEAN(sb)); - pr_newline(out); - - pr_buf(out, "Devices:"); - pr_tab(out); - pr_buf(out, "%u", nr_devices); - pr_newline(out); - - pr_buf(out, "Sections:"); + prt_printf(out, "(not set)"); + prt_newline(out); + + prt_printf(out, "Sequence number:"); + prt_tab(out); + prt_printf(out, "%llu", le64_to_cpu(sb->seq)); + prt_newline(out); + + prt_printf(out, "Superblock size:"); + prt_tab(out); + prt_printf(out, "%zu", vstruct_bytes(sb)); + prt_newline(out); + + prt_printf(out, "Clean:"); + prt_tab(out); + prt_printf(out, "%llu", BCH_SB_CLEAN(sb)); + prt_newline(out); + + prt_printf(out, "Devices:"); + prt_tab(out); + prt_printf(out, "%u", nr_devices); + prt_newline(out); + + prt_printf(out, "Sections:"); vstruct_for_each(sb, f) fields_have |= 1 << le32_to_cpu(f->type); - pr_tab(out); - bch2_flags_to_text(out, bch2_sb_fields, fields_have); - pr_newline(out); - - pr_buf(out, "Features:"); - pr_tab(out); - bch2_flags_to_text(out, bch2_sb_features, - le64_to_cpu(sb->features[0])); - pr_newline(out); - - pr_buf(out, "Compat features:"); - pr_tab(out); - bch2_flags_to_text(out, bch2_sb_compat, - le64_to_cpu(sb->compat[0])); - pr_newline(out); - - pr_newline(out); - pr_buf(out, "Options:"); - pr_newline(out); - pr_indent_push(out, 2); + prt_tab(out); + prt_bitflags(out, bch2_sb_fields, fields_have); + prt_newline(out); + + prt_printf(out, "Features:"); + prt_tab(out); + prt_bitflags(out, bch2_sb_features, le64_to_cpu(sb->features[0])); + prt_newline(out); + + prt_printf(out, "Compat features:"); + prt_tab(out); + prt_bitflags(out, bch2_sb_compat, le64_to_cpu(sb->compat[0])); + prt_newline(out); + + prt_newline(out); + prt_printf(out, "Options:"); + prt_newline(out); + printbuf_indent_add(out, 2); { enum bch_opt_id id; @@ -1560,29 +1569,29 @@ void bch2_sb_to_text(struct printbuf *out, struct bch_sb *sb, if (opt->get_sb != BCH2_NO_SB_OPT) { u64 v = bch2_opt_from_sb(sb, id); - pr_buf(out, "%s:", opt->attr.name); - pr_tab(out); + prt_printf(out, "%s:", opt->attr.name); + prt_tab(out); bch2_opt_to_text(out, NULL, sb, opt, v, OPT_HUMAN_READABLE|OPT_SHOW_FULL_LIST); - pr_newline(out); + prt_newline(out); } } } - pr_indent_pop(out, 2); + printbuf_indent_sub(out, 2); if (print_layout) { - pr_newline(out); - pr_buf(out, "layout:"); - pr_newline(out); - pr_indent_push(out, 2); + prt_newline(out); + prt_printf(out, "layout:"); + prt_newline(out); + printbuf_indent_add(out, 2); bch2_sb_layout_to_text(out, &sb->layout); - pr_indent_pop(out, 2); + printbuf_indent_sub(out, 2); } vstruct_for_each(sb, f) if (fields & (1 << le32_to_cpu(f->type))) { - pr_newline(out); + prt_newline(out); bch2_sb_field_to_text(out, sb, f); } }