Common operations with blocks. Management of tmp_remap array and some helper functions. Signed-off-by: Mikulas Patocka --- drivers/md/dm-multisnap-blocks.c | 198 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) Index: linux-2.6.32/drivers/md/dm-multisnap-blocks.c =================================================================== --- /dev/null +++ linux-2.6.32/drivers/md/dm-multisnap-blocks.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2009 Red Hat Czech, s.r.o. + * + * Mikulas Patocka + * + * This file is released under the GPL. + */ + +#include "dm-multisnap-mikulas.h" + +/* + * Check that the block is valid. + */ +static int check_invalid(struct dm_exception_store *s, chunk_t block) +{ + if (unlikely(block >= s->dev_size) || + unlikely(block == SB_BLOCK) || + unlikely(dm_multisnap_is_commit_block(s, block))) { + DMERR("check_invalid: access to invalid part of the device: %llx, size %llx", (unsigned long long)block, (unsigned long long)s->dev_size); + dm_multisnap_set_error(s->dm, -EFSERROR); + return 1; + } + return 0; +} + +static struct tmp_remap *find_tmp_remap(struct dm_exception_store *s, chunk_t block) +{ + struct tmp_remap *t; + struct hlist_node *hn; + unsigned hash = TMP_REMAP_HASH(block); + hlist_for_each_entry(t, hn, &s->tmp_remap[hash], hash_list) { + if (t->old == block) + return t; + cond_resched(); + } + return NULL; +} + +chunk_t dm_multisnap_remap_block(struct dm_exception_store *s, chunk_t block) +{ + struct tmp_remap *t; + t = find_tmp_remap(s, block); + if (t) + return t->new; + return block; +} + +void *dm_multisnap_read_block(struct dm_exception_store *s, chunk_t block, struct dm_buffer **bp) +{ + void *buf; + cond_resched(); + + if (check_invalid(s, block)) + return NULL; + + block = dm_multisnap_remap_block(s, block); + + if (check_invalid(s, block)) + return NULL; + + buf = dm_bufio_read(s->bufio, block, bp); + if (unlikely(IS_ERR(buf))) { + DMERR("dm_multisnap_read_block: error read chunk %llx", (unsigned long long)block); + dm_multisnap_set_error(s->dm, PTR_ERR(buf)); + return NULL; + } + return buf; +} + +int dm_multisnap_block_is_uncommitted(struct dm_exception_store *s, chunk_t chunk) +{ + struct tmp_remap *t; + check_invalid(s, chunk); + t = find_tmp_remap(s, chunk); + return t && t->uncommitted; +} + +void *dm_multisnap_duplicate_block(struct dm_exception_store *s, chunk_t old_chunk, chunk_t new_chunk, bitmap_t bitmap_idx, struct dm_buffer **bp, chunk_t *to_free_ptr) +{ + chunk_t to_free_val; + void *buf; + struct tmp_remap *t; + + if (unlikely(check_invalid(s, old_chunk)) || + unlikely(check_invalid(s, new_chunk))) + return NULL; + + if (!to_free_ptr) + to_free_ptr = &to_free_val; + *to_free_ptr = 0; + + t = find_tmp_remap(s, old_chunk); + if (t) { + if (unlikely(t->bitmap_idx != bitmap_idx)) { + DMERR("dm_multisnap_duplicate_block: bitmap_idx doesn't match, %X != %X", t->bitmap_idx, bitmap_idx); + dm_multisnap_set_error(s->dm, -EFSERROR); + return NULL; + } + *to_free_ptr = t->new; + t->new = new_chunk; + } else { + if (unlikely(list_empty(&s->free_tmp_remaps))) { + DMERR("dm_multisnap_duplicate_block: all remap blocks used"); + dm_multisnap_set_error(s->dm, -EFSERROR); + return NULL; + } + t = list_first_entry(&s->free_tmp_remaps, struct tmp_remap, list); + t->new = new_chunk; + t->old = old_chunk; + t->bitmap_idx = bitmap_idx; + hlist_add_head(&t->hash_list, &s->tmp_remap[TMP_REMAP_HASH(old_chunk)]); + s->n_used_tmp_remaps++; + } + list_del(&t->list); + if (bitmap_idx == CB_BITMAP_IDX_NONE) + list_add_tail(&t->list, &s->used_bt_tmp_remaps); + else + list_add_tail(&t->list, &s->used_bitmap_tmp_remaps); + t->uncommitted = 1; + dm_bufio_release_move(*bp, new_chunk); + + if (to_free_ptr == &to_free_val && to_free_val) + dm_multisnap_free_block(s, to_free_val, 0); + + buf = dm_bufio_read(s->bufio, new_chunk, bp); + if (IS_ERR(buf)) { + DMERR("dm_multisnap_duplicate_block: error reading chunk %llx", (unsigned long long)new_chunk); + dm_multisnap_set_error(s->dm, PTR_ERR(buf)); + return NULL; + } + return buf; +} + +void dm_multisnap_free_tmp_remap(struct dm_exception_store *s, struct tmp_remap *t) +{ + list_del(&t->list); + hlist_del(&t->hash_list); + s->n_used_tmp_remaps--; + list_add(&t->list, &s->free_tmp_remaps); +} + +void *dm_multisnap_make_block(struct dm_exception_store *s, chunk_t new_chunk, struct dm_buffer **bp) +{ + void *buf; + + if (unlikely(check_invalid(s, new_chunk))) + return NULL; + + /* !!! TODO: add it to the list of recently allocated blocks */ + + buf = dm_bufio_new(s->bufio, new_chunk, bp); + if (unlikely(IS_ERR(buf))) { + DMERR("dm_multisnap_make_block: error creating new block at chunk %llx", (unsigned long long)new_chunk); + dm_multisnap_set_error(s->dm, PTR_ERR(buf)); + return NULL; + } + return buf; +} + +void dm_multisnap_free_block_and_duplicates(struct dm_exception_store *s, chunk_t chunk) +{ + struct tmp_remap *t; + + if (unlikely(check_invalid(s, chunk))) + return; + + t = find_tmp_remap(s, chunk); + if (t) { + dm_multisnap_free_block(s, t->new, 0); + dm_multisnap_free_tmp_remap(s, t); + } + dm_multisnap_free_block(s, chunk, 0); +} + +int dm_multisnap_is_commit_block(struct dm_exception_store *s, chunk_t block) +{ + if (unlikely(block < FIRST_CB_BLOCK)) + return 0; + if (likely(!(s->cb_stride & (s->cb_stride - 1)))) + return (block & (s->cb_stride - 1)) == (FIRST_CB_BLOCK & (s->cb_stride - 1)); + else + return sector_div(block, s->cb_stride) == FIRST_CB_BLOCK % s->cb_stride; +} + +void dm_multisnap_init_stop_cycles(stop_cycles_t *cy) +{ + (*cy)[1] = 0; +} + +int dm_multisnap_stop_cycles(struct dm_exception_store *s, stop_cycles_t *cy, chunk_t key) +{ + if (unlikely((*cy)[0] == key) && unlikely((*cy)[1] != 0)) { + DMERR("dm_multisnap_stop_cycles: cycle detected at chunk %llx", (unsigned long long)key); + dm_multisnap_set_error(s->dm, -EFSERROR); + return -1; + } + return 0; +}