Writing the commit block. Signed-off-by: Mikulas Patocka --- drivers/md/dm-multisnap-commit.c | 246 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) Index: linux-2.6.34-rc1-devel/drivers/md/dm-multisnap-commit.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.34-rc1-devel/drivers/md/dm-multisnap-commit.c 2010-03-17 15:40:53.000000000 +0100 @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2009 Red Hat Czech, s.r.o. + * + * Mikulas Patocka + * + * This file is released under the GPL. + */ + +#include "dm-multisnap-mikulas.h" + +/* + * Flush existing tmp_remaps. + */ +static void dm_multisnap_finalize_tmp_remaps(struct dm_exception_store *s) +{ + struct tmp_remap *t; + int i; + + while (s->n_used_tmp_remaps) { + if (dm_multisnap_has_error(s->dm)) + return; + if (s->n_used_tmp_remaps < N_REMAPS - 1) { + /* + * prefer btree remaps ... + * if there are none, do bitmap remaps + */ + if (!list_empty(&s->used_bt_tmp_remaps)) { + t = container_of(s->used_bt_tmp_remaps.next, + struct tmp_remap, list); + dm_multisnap_bt_finalize_tmp_remap(s, t); + dm_multisnap_free_tmp_remap(s, t); + continue; + } + } + + /* else: 0 or 1 free remaps : finalize bitmaps */ + if (!list_empty(&s->used_bitmap_tmp_remaps)) { + t = container_of(s->used_bitmap_tmp_remaps.next, + struct tmp_remap, list); + dm_multisnap_bitmap_finalize_tmp_remap(s, t); + dm_multisnap_free_tmp_remap(s, t); + continue; + } else { + DM_MULTISNAP_SET_ERROR(s->dm, -EFSERROR, + ("%s: no bitmap tmp_remaps, n_used_tmp_remaps %u", + __func__, s->n_used_tmp_remaps)); + return; + } + } + + if (dm_multisnap_has_error(s->dm)) + return; + + for (i = s->n_preallocated_blocks - 1; i >= 0; i--) + dm_multisnap_free_blocks_immediate(s, s->preallocated_blocks[i], 1); + s->n_preallocated_blocks = 0; +} + +/* + * This function must be called before any two b+tree modification at a point + * when b+tree is consistent. It flushes tmp_remaps, so that tmp_remap array + * doesn't overflow. This function doesn't commit anything. + */ +void dm_multisnap_transition_mark(struct dm_exception_store *s) +{ + /* + * Accounting: + * max number of modified/allocated blocks during btree add: + * s->bt_depth * 2 + 1 + * one additional entry for newly allocated data chunk + * one additional entry for bitmap finalization + */ + if (unlikely(N_REMAPS - s->n_used_tmp_remaps < s->bt_depth * 2 + 3)) + dm_multisnap_finalize_tmp_remaps(s); +} + +/* + * Flush buffers. This is called without the lock to reduce lock contention. + * The buffers will be flushed again, with the lock. + */ +void dm_multisnap_prepare_for_commit(struct dm_exception_store *s) +{ + int r; + + r = dm_bufio_write_dirty_buffers(s->bufio); + if (unlikely(r < 0)) { + DM_MULTISNAP_SET_ERROR(s->dm, r, + ("%s: error writing data", __func__)); + return; + } +} + +/* + * This function makes any modifications make so far permanent. + * + * It is valid to make multiple modifications to the exception store and + * then commit them atomically at once with this function. + */ +void dm_multisnap_commit(struct dm_exception_store *s) +{ + struct tmp_remap *t; + chunk_t cb_addr; + chunk_t cb_div, cb_offset; + struct multisnap_commit_block *cb; + struct multisnap_superblock *sb; + unsigned idx; + struct dm_buffer *bp; + int r; + + dm_multisnap_transition_mark(s); + + /* Forget all uncommitted blocks --- they are going to be committed */ + dm_multisnap_clear_uncommitted(s); + + dm_multisnap_flush_freelist_before_commit(s); + + if (dm_multisnap_has_error(s->dm)) { + if (!dm_multisnap_drop_on_error(s->dm)) + return; + + sb = dm_bufio_read(s->bufio, SB_BLOCK, &bp); + if (IS_ERR(sb)) + return; + + if (!le32_to_cpu(sb->error)) { + sb->error = cpu_to_le32(dm_multisnap_has_error(s->dm)); + dm_bufio_mark_buffer_dirty(bp); + } + + dm_bufio_release(bp); + return; + } + + list_for_each_entry(t, &s->used_bitmap_tmp_remaps, list) + t->uncommitted = 0; + + list_for_each_entry(t, &s->used_bt_tmp_remaps, list) + t->uncommitted = 0; + + r = dm_bufio_write_dirty_buffers(s->bufio); + if (unlikely(r < 0)) { + DM_MULTISNAP_SET_ERROR(s->dm, r, + ("%s: error writing data", __func__)); + return; + } + + cb_addr = s->alloc_rover; + + if (cb_addr < FIRST_CB_BLOCK) + cb_addr = FIRST_CB_BLOCK; + cb_div = cb_addr - FIRST_CB_BLOCK; + cb_offset = sector_div(cb_div, s->cb_stride); + cb_addr += s->cb_stride - cb_offset; + if (cb_offset < s->cb_stride / 2 || cb_addr >= s->dev_size) + cb_addr -= s->cb_stride; + + cb = dm_bufio_new(s->bufio, cb_addr, &bp); + if (IS_ERR(cb)) { + DM_MULTISNAP_SET_ERROR(s->dm, PTR_ERR(cb), + ("%s: can't allocate new commit block at %llx", + __func__, (unsigned long long)cb_addr)); + return; + } + + s->commit_sequence++; + + cb->signature = CB_SIGNATURE; + cb->snapshot_num = cpu_to_le32(s->snapshot_num); + cb->sequence = cpu_to_le64(s->commit_sequence); + write_48(cb, dev_size, s->dev_size); + write_48(cb, total_allocated, s->total_allocated); + write_48(cb, data_allocated, s->data_allocated); + write_48(cb, bitmap_root, s->bitmap_root); + write_48(cb, alloc_rover, s->alloc_rover); + write_48(cb, freelist, s->freelist_ptr); + write_48(cb, delete_rover, s->delete_rover_chunk); + write_48(cb, bt_root, s->bt_root); + cb->bt_depth = s->bt_depth; + cb->flags = s->flags; + memset(cb->pad, 0, sizeof cb->pad); + idx = 0; + list_for_each_entry(t, &s->used_bitmap_tmp_remaps, list) { + BUG_ON(idx >= N_REMAPS); + write_48(&cb->tmp_remap[idx], old, t->old); + write_48(&cb->tmp_remap[idx], new, t->new); + cb->tmp_remap[idx].bitmap_idx = cpu_to_le32(t->bitmap_idx); + idx++; + } + list_for_each_entry(t, &s->used_bt_tmp_remaps, list) { + BUG_ON(idx >= N_REMAPS); + write_48(&cb->tmp_remap[idx], old, t->old); + write_48(&cb->tmp_remap[idx], new, t->new); + cb->tmp_remap[idx].bitmap_idx = cpu_to_le32(t->bitmap_idx); + idx++; + } + for (; idx < N_REMAPS; idx++) { + write_48(&cb->tmp_remap[idx], old, 0); + write_48(&cb->tmp_remap[idx], new, 0); + cb->tmp_remap[idx].bitmap_idx = cpu_to_le32(0); + } + dm_bufio_mark_buffer_dirty(bp); + dm_bufio_release(bp); + r = dm_bufio_write_dirty_buffers(s->bufio); + if (unlikely(r < 0)) { + DM_MULTISNAP_SET_ERROR(s->dm, r, + ("%s: can't write commit block at %llx", + __func__, (unsigned long long)cb_addr)); + return; + } + + if (likely(cb_addr == s->valid_commit_block) || + likely(cb_addr == s->valid_commit_block + s->cb_stride)) + goto return_success; + + sb = dm_bufio_read(s->bufio, SB_BLOCK, &bp); + if (IS_ERR(sb)) { + DM_MULTISNAP_SET_ERROR(s->dm, PTR_ERR(sb), + ("%s: can't read super block", __func__)); + return; + } + + if (unlikely(sb->signature != SB_SIGNATURE)) { + dm_bufio_release(bp); + DM_MULTISNAP_SET_ERROR(s->dm, -EFSERROR, + ("%s: invalid super block signature when committing", + __func__)); + return; + } + + sb->commit_block = cpu_to_le64(cb_addr); + + dm_bufio_mark_buffer_dirty(bp); + dm_bufio_release(bp); + r = dm_bufio_write_dirty_buffers(s->bufio); + if (unlikely(r < 0)) { + DM_MULTISNAP_SET_ERROR(s->dm, r, ("%s: can't write super block", __func__)); + return; + } + +return_success: + s->valid_commit_block = cb_addr; + + dm_multisnap_load_freelist(s); + + return; +}