/* * An in core copy of the bits to save constantly doing look ups on * disk.
*/ unsignedlong *bits;
};
/* * This does not free off the on disk bitset as this will normally be done * after digesting into the era array.
*/ staticvoid writeset_free(struct writeset *ws)
{
vfree(ws->bits);
ws->bits = NULL;
}
/* * Allocates memory for the in core bitset.
*/ staticint writeset_alloc(struct writeset *ws, dm_block_t nr_blocks)
{
ws->bits = vzalloc(bitset_size(nr_blocks)); if (!ws->bits) {
DMERR("%s: couldn't allocate in memory bitset", __func__); return -ENOMEM;
}
return 0;
}
/* * Wipes the in-core bitset, and creates a new on disk bitset.
*/ staticint writeset_init(struct dm_disk_bitset *info, struct writeset *ws,
dm_block_t nr_blocks)
{ int r;
memset(ws->bits, 0, bitset_size(nr_blocks));
ws->md.nr_bits = nr_blocks;
r = setup_on_disk_bitset(info, ws->md.nr_bits, &ws->md.root); if (r) {
DMERR("%s: setup_on_disk_bitset failed", __func__); return r;
}
staticint writeset_marked_on_disk(struct dm_disk_bitset *info, struct writeset_metadata *m, dm_block_t block, bool *result)
{ int r;
dm_block_t old = m->root;
/* * The bitset was flushed when it was archived, so we know there'll * be no change to the root.
*/
r = dm_bitset_test_bit(info, m->root, block, &m->root, result); if (r) {
DMERR("%s: dm_bitset_test_bit failed", __func__); return r;
}
BUG_ON(m->root != old);
return r;
}
/* * Returns < 0 on error, 0 if the bit wasn't previously set, 1 if it was.
*/ staticint writeset_test_and_set(struct dm_disk_bitset *info, struct writeset *ws, uint32_t block)
{ int r;
if (!test_bit(block, ws->bits)) {
r = dm_bitset_set_bit(info, ws->md.root, block, &ws->md.root); if (r) { /* FIXME: fail mode */ return r;
}
return 0;
}
return 1;
}
/* *-------------------------------------------------------------- * On disk metadata layout *--------------------------------------------------------------
*/ #define SPACE_MAP_ROOT_SIZE 128 #define UUID_LEN 16
if (metadata_version < MIN_ERA_VERSION || metadata_version > MAX_ERA_VERSION) {
DMERR("Era metadata version %u found, but only versions between %u and %u supported.",
metadata_version, MIN_ERA_VERSION, MAX_ERA_VERSION); return -EINVAL;
}
/* * We preallocate 2 writesets. When an era rolls over we * switch between them. This means the allocation is done at * preresume time, rather than on the io path.
*/ struct writeset writesets[2]; struct writeset *current_writeset;
/* * A flag that is set whenever a writeset has been archived.
*/ bool archived_writesets;
/* * 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];
};
for (i = 0; i < count; i++) {
memcpy(&ws_d, value + (i * sizeof(ws_d)), sizeof(ws_d));
b = le64_to_cpu(ws_d.root);
dm_bitset_del(&md->bitset_info, b);
}
}
/* * Writes a superblock, including the static fields that don't get updated * with every commit (possible optimisation here). 'md' should be fully * constructed when this is called.
*/ staticvoid prepare_superblock(struct era_metadata *md, struct superblock_disk *disk)
{
disk->magic = cpu_to_le64(SUPERBLOCK_MAGIC);
disk->flags = cpu_to_le32(0ul);
/* FIXME: can't keep blanking the uuid (uuid is currently unused though) */
memset(disk->uuid, 0, sizeof(disk->uuid));
disk->version = cpu_to_le32(MAX_ERA_VERSION);
r = superblock_read_lock(md, &sblock); if (r) {
DMERR("couldn't read_lock superblock"); return r;
}
disk = dm_block_data(sblock);
/* Verify the data block size hasn't changed */ if (le32_to_cpu(disk->data_block_size) != md->block_size) {
DMERR("changing the data block size (from %u to %llu) is not supported",
le32_to_cpu(disk->data_block_size), md->block_size);
r = -EINVAL; goto bad;
}
r = dm_tm_open_with_sm(md->bm, SUPERBLOCK_LOCATION,
disk->metadata_space_map_root, sizeof(disk->metadata_space_map_root),
&md->tm, &md->sm); if (r) {
DMERR("dm_tm_open_with_sm failed"); goto bad;
}
/* * This waits until all era_map threads have picked up the new filter.
*/ staticvoid swap_writeset(struct era_metadata *md, struct writeset *new_writeset)
{
rcu_assign_pointer(md->current_writeset, new_writeset);
synchronize_rcu();
}
/* *------------------------------------------------------------------------ * Writesets get 'digested' into the main era array. * * We're using a coroutine here so the worker thread can do the digestion, * thus avoiding synchronisation of the metadata. Digesting a whole * writeset in one go would cause too much latency. *------------------------------------------------------------------------
*/ struct digest {
uint32_t era; unsignedint nr_bits, current_bit; struct writeset_metadata writeset;
__le32 value; struct dm_disk_bitset info;
int (*step)(struct era_metadata *md, struct digest *d);
};
staticbool valid_nr_blocks(dm_block_t n)
{ /* * dm_bitset restricts us to 2^32. test_bit & co. restrict us * further to 2^31 - 1
*/ return n < (1ull << 31);
}
rcu_read_lock();
ws = rcu_dereference(md->current_writeset);
r = writeset_marked(ws, block);
rcu_read_unlock();
return r;
}
staticint metadata_commit(struct era_metadata *md)
{ int r; struct dm_block *sblock;
if (md->current_writeset->md.root != INVALID_WRITESET_ROOT) {
r = dm_bitset_flush(&md->bitset_info, md->current_writeset->md.root,
&md->current_writeset->md.root); if (r) {
DMERR("%s: bitset flush failed", __func__); return r;
}
}
r = dm_tm_pre_commit(md->tm); if (r) {
DMERR("%s: pre commit failed", __func__); return r;
}
r = save_sm_root(md); if (r) {
DMERR("%s: save_sm_root failed", __func__); return r;
}
r = superblock_lock(md, &sblock); if (r) {
DMERR("%s: superblock lock failed", __func__); return r;
}
prepare_superblock(md, dm_block_data(sblock));
return dm_tm_commit(md->tm, sblock);
}
staticint metadata_checkpoint(struct era_metadata *md)
{ /* * For now we just rollover, but later I want to put a check in to * avoid this if the filter is still pretty fresh.
*/ return metadata_era_rollover(md);
}
/* * Metadata snapshots allow userland to access era data.
*/ staticint metadata_take_snap(struct era_metadata *md)
{ int r, inc; struct dm_block *clone;
while ((bio = bio_list_pop(&deferred_bios))) {
r = writeset_test_and_set(&era->md->bitset_info, ws,
get_block(era, bio)); if (r < 0) { /* * This is bad news, we need to rollback. * FIXME: finish.
*/
failed = true;
} elseif (r == 0)
commit_needed = true;
bio_list_add(&marked_bios, bio);
}
if (commit_needed) {
r = metadata_commit(era->md); if (r)
failed = true;
}
if (failed) while ((bio = bio_list_pop(&marked_bios)))
bio_io_error(bio); else {
blk_start_plug(&plug); while ((bio = bio_list_pop(&marked_bios))) { /* * Only update the in-core writeset if the on-disk one * was updated too.
*/ if (commit_needed)
set_bit(get_block(era, bio), ws->bits);
submit_bio_noacct(bio);
}
blk_finish_plug(&plug);
}
}
staticvoid process_rpc_calls(struct era *era)
{ int r; bool need_commit = false; struct list_head calls; struct rpc *rpc, *tmp;
staticvoid defer_bio(struct era *era, struct bio *bio)
{
spin_lock(&era->deferred_lock);
bio_list_add(&era->deferred_bios, bio);
spin_unlock(&era->deferred_lock);
wake_worker(era);
}
/* * Make an rpc call to the worker to change the metadata.
*/ staticint perform_rpc(struct era *era, struct rpc *rpc)
{
rpc->result = 0;
init_completion(&rpc->complete);
staticint era_map(struct dm_target *ti, struct bio *bio)
{ struct era *era = ti->private;
dm_block_t block = get_block(era, bio);
/* * All bios get remapped to the origin device. We do this now, but * it may not get issued until later. Depending on whether the * block is marked in this era.
*/
remap_to_origin(era, bio);
/* * REQ_PREFLUSH bios carry no data, so we're not interested in them.
*/ if (!(bio->bi_opf & REQ_PREFLUSH) &&
(bio_data_dir(bio) == WRITE) &&
!metadata_current_marked(era->md, block)) {
defer_bio(era, bio); return DM_MAPIO_SUBMITTED;
}
return DM_MAPIO_REMAPPED;
}
staticvoid era_postsuspend(struct dm_target *ti)
{ int r; struct era *era = ti->private;
r = in_worker0(era, metadata_era_archive); if (r) {
DMERR("%s: couldn't archive current era", __func__); /* FIXME: fail mode */
}
stop_worker(era);
r = metadata_commit(era->md); if (r) {
DMERR("%s: metadata_commit failed", __func__); /* FIXME: fail mode */
}
}
staticint era_preresume(struct dm_target *ti)
{ int r; struct era *era = ti->private;
dm_block_t new_size = calc_nr_blocks(era);
if (era->nr_blocks != new_size) {
r = metadata_resize(era->md, &new_size); if (r) {
DMERR("%s: metadata_resize failed", __func__); return r;
}
r = metadata_commit(era->md); if (r) {
DMERR("%s: metadata_commit failed", __func__); return r;
}
era->nr_blocks = new_size;
}
start_worker(era);
r = in_worker0(era, metadata_era_rollover); if (r) {
DMERR("%s: metadata_era_rollover failed", __func__); return r;
}
/* * If the system-determined stacked limits are compatible with the * era device's blocksize (io_opt is a factor) do not override them.
*/ if (io_opt_sectors < era->sectors_per_block ||
do_div(io_opt_sectors, era->sectors_per_block)) {
limits->io_min = 0;
limits->io_opt = era->sectors_per_block << SECTOR_SHIFT;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.