2975 lines · 73.2 KB · C
RawDownload
// 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t