// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (C) 2012 Red Hat, Inc. * * This file is released under the GPL. */ #include "dm-cache-metadata.h" #include "persistent-data/dm-array.h"#include "persistent-data/dm-bitset.h"#include "persistent-data/dm-space-map.h"#include "persistent-data/dm-space-map-disk.h"#include "persistent-data/dm-transaction-manager.h" #include <linux/device-mapper.h>#include <linux/refcount.h> /*----------------------------------------------------------------*/ #define DM_MSG_PREFIX "cache metadata" #define CACHE_SUPERBLOCK_MAGIC 06142003#define CACHE_SUPERBLOCK_LOCATION 0 /* * defines a range of metadata versions that this module can handle. */#define MIN_CACHE_VERSION 1#define MAX_CACHE_VERSION 2 /* * 3 for btree insert + * 2 for btree lookup used within space map */#define CACHE_MAX_CONCURRENT_LOCKS 5#define SPACE_MAP_ROOT_SIZE 128 enum superblock_flag_bits { /* for spotting crashes that would invalidate the dirty bitset */ CLEAN_SHUTDOWN, /* metadata must be checked using the tools */ NEEDS_CHECK,}; /* * Each mapping from cache block -> origin block carries a set of flags. */enum mapping_bits { /* * A valid mapping. Because we're using an array we clear this * flag for an non existant mapping. */ M_VALID = 1, /* * The data on the cache is different from that on the origin. * This flag is only used by metadata format 1. */ M_DIRTY = 2}; struct cache_disk_superblock { __le32 csum; __le32 flags; __le64 blocknr; __u8 uuid[16]; __le64 magic; __le32 version; __u8 policy_name[CACHE_POLICY_NAME_SIZE]; __le32 policy_hint_size; __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE]; __le64 mapping_root; __le64 hint_root; __le64 discard_root; __le64 discard_block_size; __le64 discard_nr_blocks; __le32 data_block_size; __le32 metadata_block_size; __le32 cache_blocks; __le32 compat_flags; __le32 compat_ro_flags; __le32 incompat_flags; __le32 read_hits; __le32 read_misses; __le32 write_hits; __le32 write_misses; __le32 policy_version[CACHE_POLICY_VERSION_SIZE]; /* * Metadata format 2 fields. */ __le64 dirty_root;} __packed; struct dm_cache_metadata { refcount_t ref_count; struct list_head list; unsigned int version; struct block_device *bdev; struct dm_block_manager *bm; struct dm_space_map *metadata_sm; struct dm_transaction_manager *tm; struct dm_array_info info; struct dm_array_info hint_info; struct dm_disk_bitset discard_info; struct rw_semaphore root_lock; unsigned long flags; dm_block_t root; dm_block_t hint_root; dm_block_t discard_root; sector_t discard_block_size; dm_dblock_t discard_nr_blocks; sector_t data_block_size; dm_cblock_t cache_blocks; bool changed:1; bool clean_when_opened:1; char policy_name[CACHE_POLICY_NAME_SIZE]; unsigned int policy_version[CACHE_POLICY_VERSION_SIZE]; size_t policy_hint_size; struct dm_cache_statistics stats; /* * Reading the space map root can fail, so we read it into this * buffer before the superblock is locked and updated. */ __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE]; /* * Set if a transaction has to be aborted but the attempt to roll * back to the previous (good) transaction failed. The only * metadata operation permissible in this state is the closing of * the device. */ bool fail_io:1; /* * Metadata format 2 fields. */ dm_block_t dirty_root; struct dm_disk_bitset dirty_info; /* * These structures are used when loading metadata. They're too * big to put on the stack. */ struct dm_array_cursor mapping_cursor; struct dm_array_cursor hint_cursor; struct dm_bitset_cursor dirty_cursor;}; /* *----------------------------------------------------------------- * superblock validator *----------------------------------------------------------------- */#define SUPERBLOCK_CSUM_XOR 9031977 static void sb_prepare_for_write(const struct dm_block_validator *v, struct dm_block *b, size_t sb_block_size){ struct cache_disk_superblock *disk_super = dm_block_data(b); disk_super->blocknr = cpu_to_le64(dm_block_location(b)); disk_super->csum = cpu_to_le32(dm_bm_checksum(&disk_super->flags, sb_block_size - sizeof(__le32), SUPERBLOCK_CSUM_XOR));} static int check_metadata_version(struct cache_disk_superblock *disk_super){ uint32_t metadata_version = le32_to_cpu(disk_super->version); if (metadata_version < MIN_CACHE_VERSION || metadata_version > MAX_CACHE_VERSION) { DMERR("Cache metadata version %u found, but only versions between %u and %u supported.", metadata_version, MIN_CACHE_VERSION, MAX_CACHE_VERSION); return -EINVAL; } return 0;} static int sb_check(const struct dm_block_validator *v, struct dm_block *b, size_t sb_block_size){ struct cache_disk_superblock *disk_super = dm_block_data(b); __le32 csum_le; if (dm_block_location(b) != le64_to_cpu(disk_super->blocknr)) { DMERR("%s failed: blocknr %llu: wanted %llu", __func__, le64_to_cpu(disk_super->blocknr), (unsigned long long)dm_block_location(b)); return -ENOTBLK; } if (le64_to_cpu(disk_super->magic) != CACHE_SUPERBLOCK_MAGIC) { DMERR("%s failed: magic %llu: wanted %llu", __func__, le64_to_cpu(disk_super->magic), (unsigned long long)CACHE_SUPERBLOCK_MAGIC); return -EILSEQ; } csum_le = cpu_to_le32(dm_bm_checksum(&disk_super->flags, sb_block_size - sizeof(__le32), SUPERBLOCK_CSUM_XOR)); if (csum_le != disk_super->csum) { DMERR("%s failed: csum %u: wanted %u", __func__, le32_to_cpu(csum_le), le32_to_cpu(disk_super->csum)); return -EILSEQ; } return check_metadata_version(disk_super);} static const struct dm_block_validator sb_validator = { .name = "superblock", .prepare_for_write = sb_prepare_for_write, .check = sb_check}; /*----------------------------------------------------------------*/ static int superblock_read_lock(struct dm_cache_metadata *cmd, struct dm_block **sblock){ return dm_bm_read_lock(cmd->bm, CACHE_SUPERBLOCK_LOCATION, &sb_validator, sblock);} static int superblock_lock_zero(struct dm_cache_metadata *cmd, struct dm_block **sblock){ return dm_bm_write_lock_zero(cmd->bm, CACHE_SUPERBLOCK_LOCATION, &sb_validator, sblock);} static int superblock_lock(struct dm_cache_metadata *cmd, struct dm_block **sblock){ return dm_bm_write_lock(cmd->bm, CACHE_SUPERBLOCK_LOCATION, &sb_validator, sblock);} /*----------------------------------------------------------------*/ static int __superblock_all_zeroes(struct dm_block_manager *bm, bool *result){ int r; unsigned int i; struct dm_block *b; __le64 *data_le, zero = cpu_to_le64(0); unsigned int sb_block_size = dm_bm_block_size(bm) / sizeof(__le64); /* * We can't use a validator here - it may be all zeroes. */ r = dm_bm_read_lock(bm, CACHE_SUPERBLOCK_LOCATION, NULL, &b); if (r) return r; data_le = dm_block_data(b); *result = true; for (i = 0; i < sb_block_size; i++) { if (data_le[i] != zero) { *result = false; break; } } dm_bm_unlock(b); return 0;} static void __setup_mapping_info(struct dm_cache_metadata *cmd){ struct dm_btree_value_type vt; vt.context = NULL; vt.size = sizeof(__le64); vt.inc = NULL; vt.dec = NULL; vt.equal = NULL; dm_array_info_init(&cmd->info, cmd->tm, &vt); if (cmd->policy_hint_size) { vt.size = sizeof(__le32); dm_array_info_init(&cmd->hint_info, cmd->tm, &vt); }} static int __save_sm_root(struct dm_cache_metadata *cmd){ int r; size_t metadata_len; r = dm_sm_root_size(cmd->metadata_sm, &metadata_len); if (r < 0) return r; return dm_sm_copy_root(cmd->metadata_sm, &cmd->metadata_space_map_root, metadata_len);} static void __copy_sm_root(struct dm_cache_metadata *cmd, struct cache_disk_superblock *disk_super){ memcpy(&disk_super->metadata_space_map_root, &cmd->metadata_space_map_root, sizeof(cmd->metadata_space_map_root));} static bool separate_dirty_bits(struct dm_cache_metadata *cmd){ return cmd->version >= 2;} static int __write_initial_superblock(struct dm_cache_metadata *cmd){ int r; struct dm_block *sblock; struct cache_disk_superblock *disk_super; sector_t bdev_size = bdev_nr_sectors(cmd->bdev); /* FIXME: see if we can lose the max sectors limit */ if (bdev_size > DM_CACHE_METADATA_MAX_SECTORS) bdev_size = DM_CACHE_METADATA_MAX_SECTORS; r = dm_tm_pre_commit(cmd->tm); if (r < 0) return r; /* * dm_sm_copy_root() can fail. So we need to do it before we start * updating the superblock. */ r = __save_sm_root(cmd); if (r) return r; r = superblock_lock_zero(cmd, &sblock); if (r) return r; disk_super = dm_block_data(sblock); disk_super->flags = 0; memset(disk_super->uuid, 0, sizeof(disk_super->uuid)); disk_super->magic = cpu_to_le64(CACHE_SUPERBLOCK_MAGIC); disk_super->version = cpu_to_le32(cmd->version); memset(disk_super->policy_name, 0, sizeof(disk_super->policy_name)); memset(disk_super->policy_version, 0, sizeof(disk_super->policy_version)); disk_super->policy_hint_size = cpu_to_le32(0); __copy_sm_root(cmd, disk_super); disk_super->mapping_root = cpu_to_le64(cmd->root); disk_super->hint_root = cpu_to_le64(cmd->hint_root); disk_super->discard_root = cpu_to_le64(cmd->discard_root); disk_super->discard_block_size = cpu_to_le64(cmd->discard_block_size); disk_super->discard_nr_blocks = cpu_to_le64(from_dblock(cmd->discard_nr_blocks)); disk_super->metadata_block_size = cpu_to_le32(DM_CACHE_METADATA_BLOCK_SIZE); disk_super->data_block_size = cpu_to_le32(cmd->data_block_size); disk_super->cache_blocks = cpu_to_le32(0); disk_super->read_hits = cpu_to_le32(0); disk_super->read_misses = cpu_to_le32(0); disk_super->write_hits = cpu_to_le32(0); disk_super->write_misses = cpu_to_le32(0); if (separate_dirty_bits(cmd)) disk_super->dirty_root = cpu_to_le64(cmd->dirty_root); return dm_tm_commit(cmd->tm, sblock);} static int __format_metadata(struct dm_cache_metadata *cmd){ int r; r = dm_tm_create_with_sm(cmd->bm, CACHE_SUPERBLOCK_LOCATION, &cmd->tm, &cmd->metadata_sm); if (r < 0) { DMERR("tm_create_with_sm failed"); return r; } __setup_mapping_info(cmd); r = dm_array_empty(&cmd->info, &cmd->root); if (r < 0) goto bad; if (separate_dirty_bits(cmd)) { dm_disk_bitset_init(cmd->tm, &cmd->dirty_info); r = dm_bitset_empty(&cmd->dirty_info, &cmd->dirty_root); if (r < 0) goto bad; } dm_disk_bitset_init(cmd->tm, &cmd->discard_info); r = dm_bitset_empty(&cmd->discard_info, &cmd->discard_root); if (r < 0) goto bad; cmd->discard_block_size = 0; cmd->discard_nr_blocks = 0; r = __write_initial_superblock(cmd); if (r) goto bad; cmd->clean_when_opened = true; return 0; bad: dm_tm_destroy(cmd->tm); dm_sm_destroy(cmd->metadata_sm); return r;} static int __check_incompat_features(struct cache_disk_superblock *disk_super, struct dm_cache_metadata *cmd){ uint32_t incompat_flags, features; incompat_flags = le32_to_cpu(disk_super->incompat_flags); features = incompat_flags & ~DM_CACHE_FEATURE_INCOMPAT_SUPP; if (features) { DMERR("could not access metadata due to unsupported optional features (%lx).", (unsigned long)features); return -EINVAL; } /* * Check for read-only metadata to skip the following RDWR checks. */ if (bdev_read_only(cmd->bdev)) return 0; features = le32_to_cpu(disk_super->compat_ro_flags) & ~DM_CACHE_FEATURE_COMPAT_RO_SUPP; if (features) { DMERR("could not access metadata RDWR due to unsupported optional features (%lx).", (unsigned long)features); return -EINVAL; } return 0;} static int __open_metadata(struct dm_cache_metadata *cmd){ int r; struct dm_block *sblock; struct cache_disk_superblock *disk_super; unsigned long sb_flags; r = superblock_read_lock(cmd, &sblock); if (r < 0) { DMERR("couldn't read lock superblock"); return r; } disk_super = dm_block_data(sblock); /* Verify the data block size hasn't changed */ if (le32_to_cpu(disk_super->data_block_size) != cmd->data_block_size) { DMERR("changing the data block size (from %u to %llu) is not supported", le32_to_cpu(disk_super->data_block_size), (unsigned long long)cmd->data_block_size); r = -EINVAL; goto bad; } r = __check_incompat_features(disk_super, cmd); if (r < 0) goto bad; r = dm_tm_open_with_sm(cmd->bm, CACHE_SUPERBLOCK_LOCATION, disk_super->metadata_space_map_root, sizeof(disk_super->metadata_space_map_root), &cmd->tm, &cmd->metadata_sm); if (r < 0) { DMERR("tm_open_with_sm failed");Lines 1–500 of 1827. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 Red Hat, Inc. * * This file is released under the GPL. */ #include "dm-cache-metadata.h" #include "persistent-data/dm-array.h" #include "persistent-data/dm-bitset.h" #include "persistent-data/dm-space-map.h" #include "persistent-data/dm-space-map-disk.h" #include "persistent-data/dm-transaction-manager.h" #include <linux/device-mapper.h> #include <linux/refcount.h> /*----------------------------------------------------------------*/ #define DM_MSG_PREFIX "cache metadata" #define CACHE_SUPERBLOCK_MAGIC 06142003 #define CACHE_SUPERBLOCK_LOCATION 0 /* * defines a range of metadata versions that this module can handle. */ #define MIN_CACHE_VERSION 1 #define MAX_CACHE_VERSION 2 /* * 3 for btree insert + * 2 for btree lookup used within space map */ #define CACHE_MAX_CONCURRENT_LOCKS 5 #define SPACE_MAP_ROOT_SIZE 128 enum superblock_flag_bits { /* for spotting crashes that would invalidate the dirty bitset */ CLEAN_SHUTDOWN, /* metadata must be checked using the tools */ NEEDS_CHECK, }; /* * Each mapping from cache block -> origin block carries a set of flags. */ enum mapping_bits { /* * A valid mapping. Because we're using an array we clear this * flag for an non existant mapping. */ M_VALID = 1, /* * The data on the cache is different from that on the origin. * This flag is only used by metadata format 1. */ M_DIRTY = 2 }; struct cache_disk_superblock { __le32 csum; __le32 flags; __le64 blocknr; __u8 uuid[16]; __le64 magic; __le32 version; __u8 policy_name[CACHE_POLICY_NAME_SIZE]; __le32 policy_hint_size; __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE]; __le64 mapping_root; __le64 hint_root; __le64 discard_root; __le64 discard_block_size; __le64 discard_nr_blocks; __le32 data_block_size; __le32 metadata_block_size; __le32 cache_blocks; __le32 compat_flags; __le32 compat_ro_flags; __le32 incompat_flags; __le32 read_hits; __le32 read_misses; __le32 write_hits; __le32 write_misses; __le32 policy_version[CACHE_POLICY_VERSION_SIZE]; /* * Metadata format 2 fields. */ __le64 dirty_root; } __packed; struct dm_cache_metadata { refcount_t ref_count; struct list_head list; unsigned int version; struct block_device *bdev; struct dm_block_manager *bm; struct dm_space_map *metadata_sm; struct dm_transaction_manager *tm; struct dm_array_info info; struct dm_array_info hint_info; struct dm_disk_bitset discard_info; struct rw_semaphore root_lock; unsigned long flags; dm_block_t root; dm_block_t hint_root; dm_block_t discard_root; sector_t discard_block_size; dm_dblock_t discard_nr_blocks; sector_t data_block_size; dm_cblock_t cache_blocks; bool changed:1; bool clean_when_opened:1; char policy_name[CACHE_POLICY_NAME_SIZE]; unsigned int policy_version[CACHE_POLICY_VERSION_SIZE]; size_t policy_hint_size; struct dm_cache_statistics stats; /* * Reading the space map root can fail, so we read it into this * buffer before the superblock is locked and updated. */ __u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE]; /* * Set if a transaction has to be aborted but the attempt to roll * back to the previous (good) transaction failed. The only * metadata operation permissible in this state is the closing of * the device. */ bool fail_io:1; /* * Metadata format 2 fields. */ dm_block_t dirty_root; struct dm_disk_bitset dirty_info; /* * These structures are used when loading metadata. They're too * big to put on the stack. */ struct dm_array_cursor mapping_cursor; struct dm_array_cursor hint_cursor; struct dm_bitset_cursor dirty_cursor; }; /* *----------------------------------------------------------------- * superblock validator *----------------------------------------------------------------- */ #define SUPERBLOCK_CSUM_XOR 9031977 static void sb_prepare_for_write(const struct dm_block_validator *v, struct dm_block *b, size_t sb_block_size) { struct cache_disk_superblock *disk_super = dm_block_data(b); disk_super->blocknr = cpu_to_le64(dm_block_location(b)); disk_super->csum = cpu_to_le32(dm_bm_checksum(&disk_super->flags, sb_block_size - sizeof(__le32), SUPERBLOCK_CSUM_XOR)); } static int check_metadata_version(struct cache_disk_superblock *disk_super) { uint32_t metadata_version = le32_to_cpu(disk_super->version); if (metadata_version < MIN_CACHE_VERSION || metadata_version > MAX_CACHE_VERSION) { DMERR("Cache metadata version %u found, but only versions between %u and %u supported.", metadata_version, MIN_CACHE_VERSION, MAX_CACHE_VERSION); return -EINVAL; } return 0; } static int sb_check(const struct dm_block_validator *v, struct dm_block *b, size_t sb_block_size) { struct cache_disk_superblock *disk_super = dm_block_data(b); __le32 csum_le; if (dm_block_location(b) != le64_to_cpu(disk_super->blocknr)) { DMERR("%s failed: blocknr %llu: wanted %llu", __func__, le64_to_cpu(disk_super->blocknr), (unsigned long long)dm_block_location(b)); return -ENOTBLK; } if (le64_to_cpu(disk_super->magic) != CACHE_SUPERBLOCK_MAGIC) { DMERR("%s failed: magic %llu: wanted %llu", __func__, le64_to_cpu(disk_super->magic), (unsigned long long)CACHE_SUPERBLOCK_MAGIC); return -EILSEQ; } csum_le = cpu_to_le32(dm_bm_checksum(&disk_super->flags, sb_block_size - sizeof(__le32), SUPERBLOCK_CSUM_XOR)); if (csum_le != disk_super->csum) { DMERR("%s failed: csum %u: wanted %u", __func__, le32_to_cpu(csum_le), le32_to_cpu(disk_super->csum)); return -EILSEQ; } return check_metadata_version(disk_super); } static const struct dm_block_validator sb_validator = { .name = "superblock", .prepare_for_write = sb_prepare_for_write, .check = sb_check }; /*----------------------------------------------------------------*/ static int superblock_read_lock(struct dm_cache_metadata *cmd, struct dm_block **sblock) { return dm_bm_read_lock(cmd->bm, CACHE_SUPERBLOCK_LOCATION, &sb_validator, sblock); } static int superblock_lock_zero(struct dm_cache_metadata *cmd, struct dm_block **sblock) { return dm_bm_write_lock_zero(cmd->bm, CACHE_SUPERBLOCK_LOCATION, &sb_validator, sblock); } static int superblock_lock(struct dm_cache_metadata *cmd, struct dm_block **sblock) { return dm_bm_write_lock(cmd->bm, CACHE_SUPERBLOCK_LOCATION, &sb_validator, sblock); } /*----------------------------------------------------------------*/ static int __superblock_all_zeroes(struct dm_block_manager *bm, bool *result) { int r; unsigned int i; struct dm_block *b; __le64 *data_le, zero = cpu_to_le64(0); unsigned int sb_block_size = dm_bm_block_size(bm) / sizeof(__le64); /* * We can't use a validator here - it may be all zeroes. */ r = dm_bm_read_lock(bm, CACHE_SUPERBLOCK_LOCATION, NULL, &b); if (r) return r; data_le = dm_block_data(b); *result = true; for (i = 0; i < sb_block_size; i++) { if (data_le[i] != zero) { *result = false; break; } } dm_bm_unlock(b); return 0; } static void __setup_mapping_info(struct dm_cache_metadata *cmd) { struct dm_btree_value_type vt; vt.context = NULL; vt.size = sizeof(__le64); vt.inc = NULL; vt.dec = NULL; vt.equal = NULL; dm_array_info_init(&cmd->info, cmd->tm, &vt); if (cmd->policy_hint_size) { vt.size = sizeof(__le32); dm_array_info_init(&cmd->hint_info, cmd->tm, &vt); } } static int __save_sm_root(struct dm_cache_metadata *cmd) { int r; size_t metadata_len; r = dm_sm_root_size(cmd->metadata_sm, &metadata_len); if (r < 0) return r; return dm_sm_copy_root(cmd->metadata_sm, &cmd->metadata_space_map_root, metadata_len); } static void __copy_sm_root(struct dm_cache_metadata *cmd, struct cache_disk_superblock *disk_super) { memcpy(&disk_super->metadata_space_map_root, &cmd->metadata_space_map_root, sizeof(cmd->metadata_space_map_root)); } static bool separate_dirty_bits(struct dm_cache_metadata *cmd) { return cmd->version >= 2; } static int __write_initial_superblock(struct dm_cache_metadata *cmd) { int r; struct dm_block *sblock; struct cache_disk_superblock *disk_super; sector_t bdev_size = bdev_nr_sectors(cmd->bdev); /* FIXME: see if we can lose the max sectors limit */ if (bdev_size > DM_CACHE_METADATA_MAX_SECTORS) bdev_size = DM_CACHE_METADATA_MAX_SECTORS; r = dm_tm_pre_commit(cmd->tm); if (r < 0) return r; /* * dm_sm_copy_root() can fail. So we need to do it before we start * updating the superblock. */ r = __save_sm_root(cmd); if (r) return r; r = superblock_lock_zero(cmd, &sblock); if (r) return r; disk_super = dm_block_data(sblock); disk_super->flags = 0; memset(disk_super->uuid, 0, sizeof(disk_super->uuid)); disk_super->magic = cpu_to_le64(CACHE_SUPERBLOCK_MAGIC); disk_super->version = cpu_to_le32(cmd->version); memset(disk_super->policy_name, 0, sizeof(disk_super->policy_name)); memset(disk_super->policy_version, 0, sizeof(disk_super->policy_version)); disk_super->policy_hint_size = cpu_to_le32(0); __copy_sm_root(cmd, disk_super); disk_super->mapping_root = cpu_to_le64(cmd->root); disk_super->hint_root = cpu_to_le64(cmd->hint_root); disk_super->discard_root = cpu_to_le64(cmd->discard_root); disk_super->discard_block_size = cpu_to_le64(cmd->discard_block_size); disk_super->discard_nr_blocks = cpu_to_le64(from_dblock(cmd->discard_nr_blocks)); disk_super->metadata_block_size = cpu_to_le32(DM_CACHE_METADATA_BLOCK_SIZE); disk_super->data_block_size = cpu_to_le32(cmd->data_block_size); disk_super->cache_blocks = cpu_to_le32(0); disk_super->read_hits = cpu_to_le32(0); disk_super->read_misses = cpu_to_le32(0); disk_super->write_hits = cpu_to_le32(0); disk_super->write_misses = cpu_to_le32(0); if (separate_dirty_bits(cmd)) disk_super->dirty_root = cpu_to_le64(cmd->dirty_root); return dm_tm_commit(cmd->tm, sblock); } static int __format_metadata(struct dm_cache_metadata *cmd) { int r; r = dm_tm_create_with_sm(cmd->bm, CACHE_SUPERBLOCK_LOCATION, &cmd->tm, &cmd->metadata_sm); if (r < 0) { DMERR("tm_create_with_sm failed"); return r; } __setup_mapping_info(cmd); r = dm_array_empty(&cmd->info, &cmd->root); if (r < 0) goto bad; if (separate_dirty_bits(cmd)) { dm_disk_bitset_init(cmd->tm, &cmd->dirty_info); r = dm_bitset_empty(&cmd->dirty_info, &cmd->dirty_root); if (r < 0) goto bad; } dm_disk_bitset_init(cmd->tm, &cmd->discard_info); r = dm_bitset_empty(&cmd->discard_info, &cmd->discard_root); if (r < 0) goto bad; cmd->discard_block_size = 0; cmd->discard_nr_blocks = 0; r = __write_initial_superblock(cmd); if (r) goto bad; cmd->clean_when_opened = true; return 0; bad: dm_tm_destroy(cmd->tm); dm_sm_destroy(cmd->metadata_sm); return r; } static int __check_incompat_features(struct cache_disk_superblock *disk_super, struct dm_cache_metadata *cmd) { uint32_t incompat_flags, features; incompat_flags = le32_to_cpu(disk_super->incompat_flags); features = incompat_flags & ~DM_CACHE_FEATURE_INCOMPAT_SUPP; if (features) { DMERR("could not access metadata due to unsupported optional features (%lx).", (unsigned long)features); return -EINVAL; } /* * Check for read-only metadata to skip the following RDWR checks. */ if (bdev_read_only(cmd->bdev)) return 0; features = le32_to_cpu(disk_super->compat_ro_flags) & ~DM_CACHE_FEATURE_COMPAT_RO_SUPP; if (features) { DMERR("could not access metadata RDWR due to unsupported optional features (%lx).", (unsigned long)features); return -EINVAL; } return 0; } static int __open_metadata(struct dm_cache_metadata *cmd) { int r; struct dm_block *sblock; struct cache_disk_superblock *disk_super; unsigned long sb_flags; r = superblock_read_lock(cmd, &sblock); if (r < 0) { DMERR("couldn't read lock superblock"); return r; } disk_super = dm_block_data(sblock); /* Verify the data block size hasn't changed */ if (le32_to_cpu(disk_super->data_block_size) != cmd->data_block_size) { DMERR("changing the data block size (from %u to %llu) is not supported", le32_to_cpu(disk_super->data_block_size), (unsigned long long)cmd->data_block_size); r = -EINVAL; goto bad; } r = __check_incompat_features(disk_super, cmd); if (r < 0) goto bad; r = dm_tm_open_with_sm(cmd->bm, CACHE_SUPERBLOCK_LOCATION, disk_super->metadata_space_map_root, sizeof(disk_super->metadata_space_map_root), &cmd->tm, &cmd->metadata_sm); if (r < 0) { DMERR("tm_open_with_sm failed");