/*
* 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>
/*----------------------------------------------------------------*/
#define DM_MSG_PREFIX "cache metadata"
#define CACHE_SUPERBLOCK_MAGIC 06142003
#define CACHE_SUPERBLOCK_LOCATION 0
#define CACHE_VERSION 1
#define CACHE_METADATA_CACHE_SIZE 64
/*
* 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,
};
/*
* 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.
*/
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;
} __packed;
struct dm_cache_metadata {
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;
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];
size_t policy_hint_size;
struct dm_cache_statistics stats;
};
/*-------------------------------------------------------------------
* superblock validator
*-----------------------------------------------------------------*/
#define SUPERBLOCK_CSUM_XOR 9031977
static void sb_prepare_for_write(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 sb_check(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("sb_check failed: blocknr %llu: wanted %llu",
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("sb_check failed: magic %llu: wanted %llu",
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