// SPDX-License-Identifier: GPL-2.0/* * bcache setup/teardown code, and some metadata io - read a superblock and * figure out what to do with it. * * Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com> * Copyright 2012 Google, Inc. */ #include "bcache.h"#include "btree.h"#include "debug.h"#include "extents.h"#include "request.h"#include "writeback.h"#include "features.h" #include <linux/blkdev.h>#include <linux/pagemap.h>#include <linux/debugfs.h>#include <linux/idr.h>#include <linux/kthread.h>#include <linux/workqueue.h>#include <linux/module.h>#include <linux/random.h>#include <linux/reboot.h>#include <linux/sysfs.h> unsigned int bch_cutoff_writeback;unsigned int bch_cutoff_writeback_sync; static const char bcache_magic[] = { 0xc6, 0x85, 0x73, 0xf6, 0x4e, 0x1a, 0x45, 0xca, 0x82, 0x65, 0xf5, 0x7f, 0x48, 0xba, 0x6d, 0x81}; static const char invalid_uuid[] = { 0xa0, 0x3e, 0xf8, 0xed, 0x3e, 0xe1, 0xb8, 0x78, 0xc8, 0x50, 0xfc, 0x5e, 0xcb, 0x16, 0xcd, 0x99}; static struct kobject *bcache_kobj;struct mutex bch_register_lock;bool bcache_is_reboot;LIST_HEAD(bch_cache_sets);static LIST_HEAD(uncached_devices); static int bcache_major;static DEFINE_IDA(bcache_device_idx);static wait_queue_head_t unregister_wait;struct workqueue_struct *bcache_wq;struct workqueue_struct *bch_flush_wq;struct workqueue_struct *bch_journal_wq; #define BTREE_MAX_PAGES (256 * 1024 / PAGE_SIZE)/* limitation of partitions number on single bcache device */#define BCACHE_MINORS 128/* limitation of bcache devices number on single system */#define BCACHE_DEVICE_IDX_MAX ((1U << MINORBITS)/BCACHE_MINORS) /* Superblock */ static unsigned int get_bucket_size(struct cache_sb *sb, struct cache_sb_disk *s){ unsigned int bucket_size = le16_to_cpu(s->bucket_size); if (sb->version >= BCACHE_SB_VERSION_CDEV_WITH_FEATURES) { if (bch_has_feature_large_bucket(sb)) { unsigned int max, order; max = sizeof(unsigned int) * BITS_PER_BYTE - 1; order = le16_to_cpu(s->bucket_size); /* * bcache tool will make sure the overflow won't * happen, an error message here is enough. */ if (order > max) pr_err("Bucket size (1 << %u) overflows\n", order); bucket_size = 1 << order; } else if (bch_has_feature_obso_large_bucket(sb)) { bucket_size += le16_to_cpu(s->obso_bucket_size_hi) << 16; } } return bucket_size;} static const char *read_super_common(struct cache_sb *sb, struct block_device *bdev, struct cache_sb_disk *s){ const char *err; unsigned int i; sb->first_bucket= le16_to_cpu(s->first_bucket); sb->nbuckets = le64_to_cpu(s->nbuckets); sb->bucket_size = get_bucket_size(sb, s); sb->nr_in_set = le16_to_cpu(s->nr_in_set); sb->nr_this_dev = le16_to_cpu(s->nr_this_dev); err = "Too many journal buckets"; if (sb->keys > SB_JOURNAL_BUCKETS) goto err; err = "Too many buckets"; if (sb->nbuckets > LONG_MAX) goto err; err = "Not enough buckets"; if (sb->nbuckets < 1 << 7) goto err; err = "Bad block size (not power of 2)"; if (!is_power_of_2(sb->block_size)) goto err; err = "Bad block size (larger than page size)"; if (sb->block_size > PAGE_SECTORS) goto err; err = "Bad bucket size (not power of 2)"; if (!is_power_of_2(sb->bucket_size)) goto err; err = "Bad bucket size (smaller than page size)"; if (sb->bucket_size < PAGE_SECTORS) goto err; err = "Invalid superblock: device too small"; if (get_capacity(bdev->bd_disk) < sb->bucket_size * sb->nbuckets) goto err; err = "Bad UUID"; if (bch_is_zero(sb->set_uuid, 16)) goto err; err = "Bad cache device number in set"; if (!sb->nr_in_set || sb->nr_in_set <= sb->nr_this_dev || sb->nr_in_set > MAX_CACHES_PER_SET) goto err; err = "Journal buckets not sequential"; for (i = 0; i < sb->keys; i++) if (sb->d[i] != sb->first_bucket + i) goto err; err = "Too many journal buckets"; if (sb->first_bucket + sb->keys > sb->nbuckets) goto err; err = "Invalid superblock: first bucket comes before end of super"; if (sb->first_bucket * sb->bucket_size < 16) goto err; err = NULL;err: return err;} static const char *read_super(struct cache_sb *sb, struct block_device *bdev, struct cache_sb_disk **res){ const char *err; struct cache_sb_disk *s; struct folio *folio; unsigned int i; folio = mapping_read_folio_gfp(bdev->bd_mapping, SB_OFFSET >> PAGE_SHIFT, GFP_KERNEL); if (IS_ERR(folio)) return "IO error"; s = folio_address(folio) + offset_in_folio(folio, SB_OFFSET); sb->offset = le64_to_cpu(s->offset); sb->version = le64_to_cpu(s->version); memcpy(sb->magic, s->magic, 16); memcpy(sb->uuid, s->uuid, 16); memcpy(sb->set_uuid, s->set_uuid, 16); memcpy(sb->label, s->label, SB_LABEL_SIZE); sb->flags = le64_to_cpu(s->flags); sb->seq = le64_to_cpu(s->seq); sb->last_mount = le32_to_cpu(s->last_mount); sb->keys = le16_to_cpu(s->keys); for (i = 0; i < SB_JOURNAL_BUCKETS; i++) sb->d[i] = le64_to_cpu(s->d[i]); pr_debug("read sb version %llu, flags %llu, seq %llu, journal size %u\n", sb->version, sb->flags, sb->seq, sb->keys); err = "Not a bcache superblock (bad offset)"; if (sb->offset != SB_SECTOR) goto err; err = "Not a bcache superblock (bad magic)"; if (memcmp(sb->magic, bcache_magic, 16)) goto err; err = "Bad checksum"; if (s->csum != csum_set(s)) goto err; err = "Bad UUID"; if (bch_is_zero(sb->uuid, 16)) goto err; sb->block_size = le16_to_cpu(s->block_size); err = "Superblock block size smaller than device block size"; if (sb->block_size << 9 < bdev_logical_block_size(bdev)) goto err; switch (sb->version) { case BCACHE_SB_VERSION_BDEV: sb->data_offset = BDEV_DATA_START_DEFAULT; break; case BCACHE_SB_VERSION_BDEV_WITH_OFFSET: case BCACHE_SB_VERSION_BDEV_WITH_FEATURES: sb->data_offset = le64_to_cpu(s->data_offset); err = "Bad data offset"; if (sb->data_offset < BDEV_DATA_START_DEFAULT) goto err; break; case BCACHE_SB_VERSION_CDEV: case BCACHE_SB_VERSION_CDEV_WITH_UUID: err = read_super_common(sb, bdev, s); if (err) goto err; break; case BCACHE_SB_VERSION_CDEV_WITH_FEATURES: /* * Feature bits are needed in read_super_common(), * convert them firstly. */ sb->feature_compat = le64_to_cpu(s->feature_compat); sb->feature_incompat = le64_to_cpu(s->feature_incompat); sb->feature_ro_compat = le64_to_cpu(s->feature_ro_compat); /* Check incompatible features */ err = "Unsupported compatible feature found"; if (bch_has_unknown_compat_features(sb)) goto err; err = "Unsupported read-only compatible feature found"; if (bch_has_unknown_ro_compat_features(sb)) goto err; err = "Unsupported incompatible feature found"; if (bch_has_unknown_incompat_features(sb)) goto err; err = read_super_common(sb, bdev, s); if (err) goto err; break; default: err = "Unsupported superblock version"; goto err; } sb->last_mount = (u32)ktime_get_real_seconds(); *res = s; return NULL;err: folio_put(folio); return err;} static void write_bdev_super_endio(struct bio *bio){ struct cached_dev *dc = bio->bi_private; if (bio->bi_status) bch_count_backing_io_errors(dc, bio); closure_put(&dc->sb_write);} static void __write_super(struct cache_sb *sb, struct cache_sb_disk *out, struct bio *bio){ unsigned int i; bio->bi_opf = REQ_OP_WRITE | REQ_SYNC | REQ_META; bio->bi_iter.bi_sector = SB_SECTOR; bio_add_virt_nofail(bio, out, SB_SIZE); out->offset = cpu_to_le64(sb->offset); memcpy(out->uuid, sb->uuid, 16); memcpy(out->set_uuid, sb->set_uuid, 16); memcpy(out->label, sb->label, SB_LABEL_SIZE); out->flags = cpu_to_le64(sb->flags); out->seq = cpu_to_le64(sb->seq); out->last_mount = cpu_to_le32(sb->last_mount); out->first_bucket = cpu_to_le16(sb->first_bucket); out->keys = cpu_to_le16(sb->keys); for (i = 0; i < sb->keys; i++) out->d[i] = cpu_to_le64(sb->d[i]); if (sb->version >= BCACHE_SB_VERSION_CDEV_WITH_FEATURES) { out->feature_compat = cpu_to_le64(sb->feature_compat); out->feature_incompat = cpu_to_le64(sb->feature_incompat); out->feature_ro_compat = cpu_to_le64(sb->feature_ro_compat); } out->version = cpu_to_le64(sb->version); out->csum = csum_set(out); pr_debug("ver %llu, flags %llu, seq %llu\n", sb->version, sb->flags, sb->seq); submit_bio(bio);} static CLOSURE_CALLBACK(bch_write_bdev_super_unlock){ closure_type(dc, struct cached_dev, sb_write); up(&dc->sb_write_mutex);} void bch_write_bdev_super(struct cached_dev *dc, struct closure *parent){ struct closure *cl = &dc->sb_write; struct bio *bio = &dc->sb_bio; down(&dc->sb_write_mutex); closure_init(cl, parent); bio_init(bio, dc->bdev, dc->sb_bv, 1, 0); bio->bi_end_io = write_bdev_super_endio; bio->bi_private = dc; closure_get(cl); /* I/O request sent to backing device */ __write_super(&dc->sb, dc->sb_disk, bio); closure_return_with_destructor(cl, bch_write_bdev_super_unlock);} static void write_super_endio(struct bio *bio){ struct cache *ca = bio->bi_private; /* is_read = 0 */ bch_count_io_errors(ca, bio->bi_status, 0, "writing superblock"); closure_put(&ca->set->sb_write);} static CLOSURE_CALLBACK(bcache_write_super_unlock){ closure_type(c, struct cache_set, sb_write); up(&c->sb_write_mutex);} void bcache_write_super(struct cache_set *c){ struct closure *cl = &c->sb_write; struct cache *ca = c->cache; struct bio *bio = &ca->sb_bio; unsigned int version = BCACHE_SB_VERSION_CDEV_WITH_UUID; down(&c->sb_write_mutex); closure_init(cl, &c->cl); ca->sb.seq++; if (ca->sb.version < version) ca->sb.version = version; bio_init(bio, ca->bdev, ca->sb_bv, 1, 0); bio->bi_end_io = write_super_endio; bio->bi_private = ca; closure_get(cl); __write_super(&ca->sb, ca->sb_disk, bio); closure_return_with_destructor(cl, bcache_write_super_unlock);} /* UUID io */ static void uuid_endio(struct bio *bio){ struct closure *cl = bio->bi_private; struct cache_set *c = container_of(cl, struct cache_set, uuid_write); cache_set_err_on(bio->bi_status, c, "accessing uuids"); bch_bbio_free(bio, c); closure_put(cl);} static CLOSURE_CALLBACK(uuid_io_unlock){ closure_type(c, struct cache_set, uuid_write); up(&c->uuid_write_mutex);} static void uuid_io(struct cache_set *c, blk_opf_t opf, struct bkey *k, struct closure *parent){ struct closure *cl = &c->uuid_write; struct uuid_entry *u; unsigned int i; char buf[80]; BUG_ON(!parent); down(&c->uuid_write_mutex); closure_init(cl, parent); for (i = 0; i < KEY_PTRS(k); i++) { struct bio *bio = bch_bbio_alloc(c); bio->bi_opf = opf | REQ_SYNC | REQ_META; bio->bi_iter.bi_size = KEY_SIZE(k) << 9; bio->bi_end_io = uuid_endio; bio->bi_private = cl; bch_bio_map(bio, c->uuids); bch_submit_bbio(bio, c, k, i); if ((opf & REQ_OP_MASK) != REQ_OP_WRITE) break; } bch_extent_to_text(buf, sizeof(buf), k); pr_debug("%s UUIDs at %s\n", (opf & REQ_OP_MASK) == REQ_OP_WRITE ? "wrote" : "read", buf); for (u = c->uuids; u < c->uuids + c->nr_uuids; u++) if (!bch_is_zero(u->uuid, 16)) pr_debug("Slot %zi: %pU: %s: 1st: %u last: %u inv: %u\n", u - c->uuids, u->uuid, u->label, u->first_reg, u->last_reg, u->invalidated); closure_return_with_destructor(cl, uuid_io_unlock);} static char *uuid_read(struct cache_set *c, struct jset *j, struct closure *cl){ struct bkey *k = &j->uuid_bucket; if (__bch_btree_ptr_invalid(c, k)) return "bad uuid pointer"; bkey_copy(&c->uuid_bucket, k); uuid_io(c, REQ_OP_READ, k, cl); if (j->version < BCACHE_JSET_VERSION_UUIDv1) { struct uuid_entry_v0 *u0 = (void *) c->uuids; struct uuid_entry *u1 = (void *) c->uuids; int i; closure_sync(cl); /* * Since the new uuid entry is bigger than the old, we have to * convert starting at the highest memory address and work down * in order to do it in place */ for (i = c->nr_uuids - 1; i >= 0; --i) { memcpy(u1[i].uuid, u0[i].uuid, 16); memcpy(u1[i].label, u0[i].label, 32); u1[i].first_reg = u0[i].first_reg; u1[i].last_reg = u0[i].last_reg; u1[i].invalidated = u0[i].invalidated; u1[i].flags = 0; u1[i].sectors = 0; } } return NULL;} static int __uuid_write(struct cache_set *c){ BKEY_PADDED(key) k;Lines 1–500 of 2975. Load more
// SPDX-License-Identifier: GPL-2.0 /* * bcache setup/teardown code, and some metadata io - read a superblock and * figure out what to do with it. * * Copyright 2010, 2011 Kent Overstreet <kent.overstreet@gmail.com> * Copyright 2012 Google, Inc. */ #include "bcache.h" #include "btree.h" #include "debug.h" #include "extents.h" #include "request.h" #include "writeback.h" #include "features.h" #include <linux/blkdev.h> #include <linux/pagemap.h> #include <linux/debugfs.h> #include <linux/idr.h> #include <linux/kthread.h> #include <linux/workqueue.h> #include <linux/module.h> #include <linux/random.h> #include <linux/reboot.h> #include <linux/sysfs.h> unsigned int bch_cutoff_writeback; unsigned int bch_cutoff_writeback_sync; static const char bcache_magic[] = { 0xc6, 0x85, 0x73, 0xf6, 0x4e, 0x1a, 0x45, 0xca, 0x82, 0x65, 0xf5, 0x7f, 0x48, 0xba, 0x6d, 0x81 }; static const char invalid_uuid[] = { 0xa0, 0x3e, 0xf8, 0xed, 0x3e, 0xe1, 0xb8, 0x78, 0xc8, 0x50, 0xfc, 0x5e, 0xcb, 0x16, 0xcd, 0x99 }; static struct kobject *bcache_kobj; struct mutex bch_register_lock; bool bcache_is_reboot; LIST_HEAD(bch_cache_sets); static LIST_HEAD(uncached_devices); static int bcache_major; static DEFINE_IDA(bcache_device_idx); static wait_queue_head_t unregister_wait; struct workqueue_struct *bcache_wq; struct workqueue_struct *bch_flush_wq; struct workqueue_struct *bch_journal_wq; #define BTREE_MAX_PAGES (256 * 1024 / PAGE_SIZE) /* limitation of partitions number on single bcache device */ #define BCACHE_MINORS 128 /* limitation of bcache devices number on single system */ #define BCACHE_DEVICE_IDX_MAX ((1U << MINORBITS)/BCACHE_MINORS) /* Superblock */ static unsigned int get_bucket_size(struct cache_sb *sb, struct cache_sb_disk *s) { unsigned int bucket_size = le16_to_cpu(s->bucket_size); if (sb->version >= BCACHE_SB_VERSION_CDEV_WITH_FEATURES) { if (bch_has_feature_large_bucket(sb)) { unsigned int max, order; max = sizeof(unsigned int) * BITS_PER_BYTE - 1; order = le16_to_cpu(s->bucket_size); /* * bcache tool will make sure the overflow won't * happen, an error message here is enough. */ if (order > max) pr_err("Bucket size (1 << %u) overflows\n", order); bucket_size = 1 << order; } else if (bch_has_feature_obso_large_bucket(sb)) { bucket_size += le16_to_cpu(s->obso_bucket_size_hi) << 16; } } return bucket_size; } static const char *read_super_common(struct cache_sb *sb, struct block_device *bdev, struct cache_sb_disk *s) { const char *err; unsigned int i; sb->first_bucket= le16_to_cpu(s->first_bucket); sb->nbuckets = le64_to_cpu(s->nbuckets); sb->bucket_size = get_bucket_size(sb, s); sb->nr_in_set = le16_to_cpu(s->nr_in_set); sb->nr_this_dev = le16_to_cpu(s->nr_this_dev); err = "Too many journal buckets"; if (sb->keys > SB_JOURNAL_BUCKETS) goto err; err = "Too many buckets"; if (sb->nbuckets > LONG_MAX) goto err; err = "Not enough buckets"; if (sb->nbuckets < 1 << 7) goto err; err = "Bad block size (not power of 2)"; if (!is_power_of_2(sb->block_size)) goto err; err = "Bad block size (larger than page size)"; if (sb->block_size > PAGE_SECTORS) goto err; err = "Bad bucket size (not power of 2)"; if (!is_power_of_2(sb->bucket_size)) goto err; err = "Bad bucket size (smaller than page size)"; if (sb->bucket_size < PAGE_SECTORS) goto err; err = "Invalid superblock: device too small"; if (get_capacity(bdev->bd_disk) < sb->bucket_size * sb->nbuckets) goto err; err = "Bad UUID"; if (bch_is_zero(sb->set_uuid, 16)) goto err; err = "Bad cache device number in set"; if (!sb->nr_in_set || sb->nr_in_set <= sb->nr_this_dev || sb->nr_in_set > MAX_CACHES_PER_SET) goto err; err = "Journal buckets not sequential"; for (i = 0; i < sb->keys; i++) if (sb->d[i] != sb->first_bucket + i) goto err; err = "Too many journal buckets"; if (sb->first_bucket + sb->keys > sb->nbuckets) goto err; err = "Invalid superblock: first bucket comes before end of super"; if (sb->first_bucket * sb->bucket_size < 16) goto err; err = NULL; err: return err; } static const char *read_super(struct cache_sb *sb, struct block_device *bdev, struct cache_sb_disk **res) { const char *err; struct cache_sb_disk *s; struct folio *folio; unsigned int i; folio = mapping_read_folio_gfp(bdev->bd_mapping, SB_OFFSET >> PAGE_SHIFT, GFP_KERNEL); if (IS_ERR(folio)) return "IO error"; s = folio_address(folio) + offset_in_folio(folio, SB_OFFSET); sb->offset = le64_to_cpu(s->offset); sb->version = le64_to_cpu(s->version); memcpy(sb->magic, s->magic, 16); memcpy(sb->uuid, s->uuid, 16); memcpy(sb->set_uuid, s->set_uuid, 16); memcpy(sb->label, s->label, SB_LABEL_SIZE); sb->flags = le64_to_cpu(s->flags); sb->seq = le64_to_cpu(s->seq); sb->last_mount = le32_to_cpu(s->last_mount); sb->keys = le16_to_cpu(s->keys); for (i = 0; i < SB_JOURNAL_BUCKETS; i++) sb->d[i] = le64_to_cpu(s->d[i]); pr_debug("read sb version %llu, flags %llu, seq %llu, journal size %u\n", sb->version, sb->flags, sb->seq, sb->keys); err = "Not a bcache superblock (bad offset)"; if (sb->offset != SB_SECTOR) goto err; err = "Not a bcache superblock (bad magic)"; if (memcmp(sb->magic, bcache_magic, 16)) goto err; err = "Bad checksum"; if (s->csum != csum_set(s)) goto err; err = "Bad UUID"; if (bch_is_zero(sb->uuid, 16)) goto err; sb->block_size = le16_to_cpu(s->block_size); err = "Superblock block size smaller than device block size"; if (sb->block_size << 9 < bdev_logical_block_size(bdev)) goto err; switch (sb->version) { case BCACHE_SB_VERSION_BDEV: sb->data_offset = BDEV_DATA_START_DEFAULT; break; case BCACHE_SB_VERSION_BDEV_WITH_OFFSET: case BCACHE_SB_VERSION_BDEV_WITH_FEATURES: sb->data_offset = le64_to_cpu(s->data_offset); err = "Bad data offset"; if (sb->data_offset < BDEV_DATA_START_DEFAULT) goto err; break; case BCACHE_SB_VERSION_CDEV: case BCACHE_SB_VERSION_CDEV_WITH_UUID: err = read_super_common(sb, bdev, s); if (err) goto err; break; case BCACHE_SB_VERSION_CDEV_WITH_FEATURES: /* * Feature bits are needed in read_super_common(), * convert them firstly. */ sb->feature_compat = le64_to_cpu(s->feature_compat); sb->feature_incompat = le64_to_cpu(s->feature_incompat); sb->feature_ro_compat = le64_to_cpu(s->feature_ro_compat); /* Check incompatible features */ err = "Unsupported compatible feature found"; if (bch_has_unknown_compat_features(sb)) goto err; err = "Unsupported read-only compatible feature found"; if (bch_has_unknown_ro_compat_features(sb)) goto err; err = "Unsupported incompatible feature found"; if (bch_has_unknown_incompat_features(sb)) goto err; err = read_super_common(sb, bdev, s); if (err) goto err; break; default: err = "Unsupported superblock version"; goto err; } sb->last_mount = (u32)ktime_get_real_seconds(); *res = s; return NULL; err: folio_put(folio); return err; } static void write_bdev_super_endio(struct bio *bio) { struct cached_dev *dc = bio->bi_private; if (bio->bi_status) bch_count_backing_io_errors(dc, bio); closure_put(&dc->sb_write); } static void __write_super(struct cache_sb *sb, struct cache_sb_disk *out, struct bio *bio) { unsigned int i; bio->bi_opf = REQ_OP_WRITE | REQ_SYNC | REQ_META; bio->bi_iter.bi_sector = SB_SECTOR; bio_add_virt_nofail(bio, out, SB_SIZE); out->offset = cpu_to_le64(sb->offset); memcpy(out->uuid, sb->uuid, 16); memcpy(out->set_uuid, sb->set_uuid, 16); memcpy(out->label, sb->label, SB_LABEL_SIZE); out->flags = cpu_to_le64(sb->flags); out->seq = cpu_to_le64(sb->seq); out->last_mount = cpu_to_le32(sb->last_mount); out->first_bucket = cpu_to_le16(sb->first_bucket); out->keys = cpu_to_le16(sb->keys); for (i = 0; i < sb->keys; i++) out->d[i] = cpu_to_le64(sb->d[i]); if (sb->version >= BCACHE_SB_VERSION_CDEV_WITH_FEATURES) { out->feature_compat = cpu_to_le64(sb->feature_compat); out->feature_incompat = cpu_to_le64(sb->feature_incompat); out->feature_ro_compat = cpu_to_le64(sb->feature_ro_compat); } out->version = cpu_to_le64(sb->version); out->csum = csum_set(out); pr_debug("ver %llu, flags %llu, seq %llu\n", sb->version, sb->flags, sb->seq); submit_bio(bio); } static CLOSURE_CALLBACK(bch_write_bdev_super_unlock) { closure_type(dc, struct cached_dev, sb_write); up(&dc->sb_write_mutex); } void bch_write_bdev_super(struct cached_dev *dc, struct closure *parent) { struct closure *cl = &dc->sb_write; struct bio *bio = &dc->sb_bio; down(&dc->sb_write_mutex); closure_init(cl, parent); bio_init(bio, dc->bdev, dc->sb_bv, 1, 0); bio->bi_end_io = write_bdev_super_endio; bio->bi_private = dc; closure_get(cl); /* I/O request sent to backing device */ __write_super(&dc->sb, dc->sb_disk, bio); closure_return_with_destructor(cl, bch_write_bdev_super_unlock); } static void write_super_endio(struct bio *bio) { struct cache *ca = bio->bi_private; /* is_read = 0 */ bch_count_io_errors(ca, bio->bi_status, 0, "writing superblock"); closure_put(&ca->set->sb_write); } static CLOSURE_CALLBACK(bcache_write_super_unlock) { closure_type(c, struct cache_set, sb_write); up(&c->sb_write_mutex); } void bcache_write_super(struct cache_set *c) { struct closure *cl = &c->sb_write; struct cache *ca = c->cache; struct bio *bio = &ca->sb_bio; unsigned int version = BCACHE_SB_VERSION_CDEV_WITH_UUID; down(&c->sb_write_mutex); closure_init(cl, &c->cl); ca->sb.seq++; if (ca->sb.version < version) ca->sb.version = version; bio_init(bio, ca->bdev, ca->sb_bv, 1, 0); bio->bi_end_io = write_super_endio; bio->bi_private = ca; closure_get(cl); __write_super(&ca->sb, ca->sb_disk, bio); closure_return_with_destructor(cl, bcache_write_super_unlock); } /* UUID io */ static void uuid_endio(struct bio *bio) { struct closure *cl = bio->bi_private; struct cache_set *c = container_of(cl, struct cache_set, uuid_write); cache_set_err_on(bio->bi_status, c, "accessing uuids"); bch_bbio_free(bio, c); closure_put(cl); } static CLOSURE_CALLBACK(uuid_io_unlock) { closure_type(c, struct cache_set, uuid_write); up(&c->uuid_write_mutex); } static void uuid_io(struct cache_set *c, blk_opf_t opf, struct bkey *k, struct closure *parent) { struct closure *cl = &c->uuid_write; struct uuid_entry *u; unsigned int i; char buf[80]; BUG_ON(!parent); down(&c->uuid_write_mutex); closure_init(cl, parent); for (i = 0; i < KEY_PTRS(k); i++) { struct bio *bio = bch_bbio_alloc(c); bio->bi_opf = opf | REQ_SYNC | REQ_META; bio->bi_iter.bi_size = KEY_SIZE(k) << 9; bio->bi_end_io = uuid_endio; bio->bi_private = cl; bch_bio_map(bio, c->uuids); bch_submit_bbio(bio, c, k, i); if ((opf & REQ_OP_MASK) != REQ_OP_WRITE) break; } bch_extent_to_text(buf, sizeof(buf), k); pr_debug("%s UUIDs at %s\n", (opf & REQ_OP_MASK) == REQ_OP_WRITE ? "wrote" : "read", buf); for (u = c->uuids; u < c->uuids + c->nr_uuids; u++) if (!bch_is_zero(u->uuid, 16)) pr_debug("Slot %zi: %pU: %s: 1st: %u last: %u inv: %u\n", u - c->uuids, u->uuid, u->label, u->first_reg, u->last_reg, u->invalidated); closure_return_with_destructor(cl, uuid_io_unlock); } static char *uuid_read(struct cache_set *c, struct jset *j, struct closure *cl) { struct bkey *k = &j->uuid_bucket; if (__bch_btree_ptr_invalid(c, k)) return "bad uuid pointer"; bkey_copy(&c->uuid_bucket, k); uuid_io(c, REQ_OP_READ, k, cl); if (j->version < BCACHE_JSET_VERSION_UUIDv1) { struct uuid_entry_v0 *u0 = (void *) c->uuids; struct uuid_entry *u1 = (void *) c->uuids; int i; closure_sync(cl); /* * Since the new uuid entry is bigger than the old, we have to * convert starting at the highest memory address and work down * in order to do it in place */ for (i = c->nr_uuids - 1; i >= 0; --i) { memcpy(u1[i].uuid, u0[i].uuid, 16); memcpy(u1[i].label, u0[i].label, 32); u1[i].first_reg = u0[i].first_reg; u1[i].last_reg = u0[i].last_reg; u1[i].invalidated = u0[i].invalidated; u1[i].flags = 0; u1[i].sectors = 0; } } return NULL; } static int __uuid_write(struct cache_set *c) { BKEY_PADDED(key) k;