--- drivers/md/multisnap/dm-multisnap-commit.c | 2 drivers/md/multisnap/dm-multisnap-delete.c | 92 ++++++++++++++++++++- drivers/md/multisnap/dm-multisnap-mikulas-struct.h | 4 drivers/md/multisnap/dm-multisnap-mikulas.c | 14 +++ drivers/md/multisnap/dm-multisnap-mikulas.h | 4 drivers/md/multisnap/dm-multisnap-snaps.c | 36 ++++++++ 6 files changed, 147 insertions(+), 5 deletions(-) Index: linux-2.6.36-rc7-fast/drivers/md/multisnap/dm-multisnap-mikulas.c =================================================================== --- linux-2.6.36-rc7-fast.orig/drivers/md/multisnap/dm-multisnap-mikulas.c 2010-10-15 02:31:37.000000000 +0200 +++ linux-2.6.36-rc7-fast/drivers/md/multisnap/dm-multisnap-mikulas.c 2010-10-15 02:31:37.000000000 +0200 @@ -109,6 +109,7 @@ static void load_commit_block(struct dm_ s->bt_root = read_48(cb, bt_root); s->bt_depth = cb->bt_depth; s->flags = cb->flags; + s->merging_snapid = le64_to_cpu(cb->merging_snapid); if (s->bt_depth > DM_MULTISNAP_MAX_BT_DEPTH || !s->bt_depth) { dm_bufio_release(bp); @@ -281,6 +282,7 @@ static void initialize_device(struct dm_ write_48(cb, bt_root, s->bt_root); cb->bt_depth = s->bt_depth; cb->flags = 0; + cb->merging_snapid = cpu_to_le64(0); } dm_bufio_mark_buffer_dirty(bp); dm_bufio_release(bp); @@ -658,6 +660,15 @@ bad_private: return r; } +static void dm_multisnap_mikulas_postinit(struct dm_exception_store *s) +{ + if (s->flags & DM_MULTISNAP_FLAG_MERGE_SNAPSHOT && + !(s->flags & DM_MULTISNAP_FLAG_MERGE_IN_PROGRESS)) { + s->flags &= ~DM_MULTISNAP_FLAG_MERGE_SNAPSHOT; + dm_multisnap_commit(s); + } +} + /* * Exit the exception store. */ @@ -707,6 +718,7 @@ struct dm_multisnap_exception_store dm_m .name = "mikulas", .module = THIS_MODULE, .init_exception_store = dm_multisnap_mikulas_init, + .postinit_exception_store = dm_multisnap_mikulas_postinit, .exit_exception_store = dm_multisnap_mikulas_exit, .store_lock_acquired = dm_multisnap_mikulas_lock_acquired, #ifdef CONFIG_DM_MULTISNAPSHOT_MIKULAS_SNAP_OF_SNAP @@ -717,6 +729,8 @@ struct dm_multisnap_exception_store dm_m .allocate_snapid = dm_multisnap_allocate_snapid, .create_snapshot = dm_multisnap_create_snapshot, .delete_snapshot = dm_multisnap_delete_snapshot, + .prepare_merge = dm_multisnap_prepare_merge, + .merge_status = dm_multisnap_merge_status, .get_next_snapid = dm_multisnap_get_next_snapid, .compare_snapids_for_create = dm_multisnap_compare_snapids_for_create, .find_snapshot_chunk = dm_multisnap_find_snapshot_chunk, Index: linux-2.6.36-rc7-fast/drivers/md/multisnap/dm-multisnap-mikulas.h =================================================================== --- linux-2.6.36-rc7-fast.orig/drivers/md/multisnap/dm-multisnap-mikulas.h 2010-10-15 02:31:36.000000000 +0200 +++ linux-2.6.36-rc7-fast/drivers/md/multisnap/dm-multisnap-mikulas.h 2010-10-15 02:31:37.000000000 +0200 @@ -85,6 +85,8 @@ struct dm_exception_store { chunk_t total_allocated; chunk_t data_allocated; + mikulas_snapid_t merging_snapid; + __u64 commit_sequence; void *tmp_chunk; @@ -235,6 +237,8 @@ int dm_multisnap_allocate_snapid(struct int snap_of_snap, snapid_t master); int dm_multisnap_create_snapshot(struct dm_exception_store *s, snapid_t snapid); int dm_multisnap_delete_snapshot(struct dm_exception_store *s, snapid_t snapid); +int dm_multisnap_prepare_merge(struct dm_exception_store *s, snapid_t snapid); +int dm_multisnap_merge_status(struct dm_exception_store *s); void dm_multisnap_get_space(struct dm_exception_store *s, unsigned long long *chunks_total, unsigned long long *chunks_allocated, Index: linux-2.6.36-rc7-fast/drivers/md/multisnap/dm-multisnap-snaps.c =================================================================== --- linux-2.6.36-rc7-fast.orig/drivers/md/multisnap/dm-multisnap-snaps.c 2010-10-15 02:31:36.000000000 +0200 +++ linux-2.6.36-rc7-fast/drivers/md/multisnap/dm-multisnap-snaps.c 2010-10-15 02:31:37.000000000 +0200 @@ -544,6 +544,12 @@ int dm_multisnap_delete_snapshot(struct if (r) return r; + if (s->flags & DM_MULTISNAP_FLAG_MERGE_SNAPSHOT && s->merging_snapid == snapid) { + s->flags |= DM_MULTISNAP_FLAG_MERGE_IN_PROGRESS; + /* This will actually restart the scan from the beginning */ + s->flags &= ~DM_MULTISNAP_FLAG_DELETING; + } + s->flags |= DM_MULTISNAP_FLAG_PENDING_DELETE; dm_multisnap_queue_work(s->dm, &s->delete_work); @@ -553,6 +559,36 @@ int dm_multisnap_delete_snapshot(struct } /* + * Prepare the snapshot for merge. + */ +int dm_multisnap_prepare_merge(struct dm_exception_store *s, snapid_t snapid) +{ + if (s->flags & DM_MULTISNAP_FLAG_MERGE_SNAPSHOT) { + DMERR("%s: snapshot id %llx is already merging", + __func__, (unsigned long long)s->merging_snapid); + return -EINVAL; + } + + s->flags |= DM_MULTISNAP_FLAG_MERGE_SNAPSHOT; + s->merging_snapid = snapid; + + dm_multisnap_commit(s); + + return 0; +} + +/* + * Return 1 if merging. + */ +int dm_multisnap_merge_status(struct dm_exception_store *s) +{ + if (s->flags & DM_MULTISNAP_FLAG_MERGE_IN_PROGRESS) + return 1; + + return 0; +} + +/* * Sort the snapids for creating. Sort them linearly except that the master * goes before all subsnapshots. */ Index: linux-2.6.36-rc7-fast/drivers/md/multisnap/dm-multisnap-delete.c =================================================================== --- linux-2.6.36-rc7-fast.orig/drivers/md/multisnap/dm-multisnap-delete.c 2010-10-15 02:31:36.000000000 +0200 +++ linux-2.6.36-rc7-fast/drivers/md/multisnap/dm-multisnap-delete.c 2010-10-15 02:31:37.000000000 +0200 @@ -8,6 +8,44 @@ #include "dm-multisnap-mikulas.h" +/* !!! FIXME: hackhackhackhackhack */ +#include +struct dm_bufio_client { + /* + * Linking of buffers: + * all buffers are linked to cache_hash with their hash_list field. + * clean buffers that are not being written (B_WRITING not set) + * are linked to lru with their lru_list field. + * dirty and clean buffers that are being written are linked + * to dirty_lru with their lru_list field. When the write + * finishes, the buffer cannot be immediately relinked + * (because we are in an interrupt context and relinking + * requires process context), so some clean-not-writing + * buffers can be held on dirty_lru too. They are later + * added to lru in the process context. + */ + struct list_head lru; + struct list_head dirty_lru; + struct mutex lock; + struct block_device *bdev; + unsigned block_size; + unsigned char sectors_per_block_bits; + unsigned char pages_per_block_bits; + + unsigned long n_buffers; + + struct dm_io_client *dm_io; + + struct dm_buffer *reserved_buffer; + struct hlist_head *cache_hash; + wait_queue_head_t free_buffer_wait; + + int async_write_error; + + struct list_head client_list; +}; + + /* * Commit after this number of deleted entries. * Too big number causes spurious overflows on nearly-full device. @@ -22,7 +60,8 @@ struct list_cookie { #define RET_END 1 #define RET_DO_FREE 2 -#define RET_RESCHEDULE 3 +#define RET_DO_MERGE 3 +#define RET_RESCHEDULE 4 static int list_callback(struct dm_exception_store *s, struct dm_multisnap_bt_node *node, @@ -34,6 +73,7 @@ static int list_callback(struct dm_excep lc->key.chunk = read_48(bt, orig_chunk); lc->key.snap_from = mikulas_snapid_to_cpu(bt->snap_from); lc->key.snap_to = mikulas_snapid_to_cpu(bt->snap_to); + lc->new_chunk = read_48(bt, new_chunk); if (unlikely(lc->key.chunk > DM_CHUNK_T_MAX)) return RET_END; @@ -43,6 +83,14 @@ static int list_callback(struct dm_excep if (unlikely(!s->delete_rover_snapid)) s->delete_rover_chunk++; + if (s->flags & DM_MULTISNAP_FLAG_MERGE_SNAPSHOT && + lc->key.snap_from <= s->merging_snapid && + lc->key.snap_to >= s->merging_snapid) { + if (!(s->flags & DM_MULTISNAP_FLAG_MERGE_IN_PROGRESS)) + goto skip_this; + return RET_DO_MERGE; + } + if (!dm_multisnap_find_next_snapid_range(s, lc->key.snap_from, &found_from, &found_to) || found_from > lc->key.snap_to) { @@ -50,12 +98,12 @@ static int list_callback(struct dm_excep * This range maps unused snapshots, delete it. * But we can't do it now, so submit it to the caller; */ - lc->new_chunk = read_48(bt, new_chunk); return RET_DO_FREE; } +skip_this: /* - * If we are at a last entry in the btree node, drop the lock and + * If we are at the last entry in the btree node, drop the lock and * allow other requests to be processed. * * This avoids a starvation when there are no nodes to delete. @@ -85,12 +133,50 @@ static void delete_step(struct dm_except case RET_END: s->flags &= ~DM_MULTISNAP_FLAG_DELETING; + s->flags &= ~DM_MULTISNAP_FLAG_MERGE_IN_PROGRESS; /* If we finished the job and there is no pending I/O, commit */ if (dm_multisnap_can_commit(s->dm)) dm_multisnap_call_commit(s->dm); return; + case RET_DO_MERGE: + { + unsigned chunk = s->chunk_size; + int r1, r2; + { + struct dm_io_region reg = { + .bdev = dm_multisnap_snapshot_bdev(s->dm), + .sector = lc.new_chunk * (chunk / 512), + .count = chunk / 512, + }; + struct dm_io_request io_req = { + .bi_rw = READ, + .mem.type = DM_IO_VMA, + .mem.ptr.vma = s->tmp_chunk, + .client = s->bufio->dm_io, + .notify.fn = NULL, + }; + r1 = dm_io(&io_req, 1, ®, NULL); + } + { + struct dm_io_region reg = { + .bdev = dm_multisnap_origin_bdev(s->dm), + .sector = lc.key.chunk * (chunk / 512), + .count = chunk / 512, + }; + struct dm_io_request io_req = { + .bi_rw = WRITE, + .mem.type = DM_IO_VMA, + .mem.ptr.vma = s->tmp_chunk, + .client = s->bufio->dm_io, + .notify.fn = NULL, + }; + r2 = dm_io(&io_req, 1, ®, NULL); + } + printk("merged %lx -> %lx: %d %d\n", lc.new_chunk * (chunk / 512), lc.key.chunk * (chunk / 512), r1, r2); + } + /* fall through */ case RET_DO_FREE: if (unlikely(dm_multisnap_has_error(s->dm))) return; Index: linux-2.6.36-rc7-fast/drivers/md/multisnap/dm-multisnap-commit.c =================================================================== --- linux-2.6.36-rc7-fast.orig/drivers/md/multisnap/dm-multisnap-commit.c 2010-10-15 02:31:36.000000000 +0200 +++ linux-2.6.36-rc7-fast/drivers/md/multisnap/dm-multisnap-commit.c 2010-10-15 02:31:37.000000000 +0200 @@ -178,7 +178,7 @@ void dm_multisnap_commit(struct dm_excep cb->bt_depth = s->bt_depth; cb->flags = s->flags; write_48(cb, self, cb_addr); - memset(cb->pad, 0, sizeof cb->pad); + cb->merging_snapid = cpu_to_le64(s->merging_snapid); idx = 0; list_for_each_entry(t, &s->used_bitmap_tmp_remaps, list) { BUG_ON(idx >= N_REMAPS); Index: linux-2.6.36-rc7-fast/drivers/md/multisnap/dm-multisnap-mikulas-struct.h =================================================================== --- linux-2.6.36-rc7-fast.orig/drivers/md/multisnap/dm-multisnap-mikulas-struct.h 2010-10-15 02:31:36.000000000 +0200 +++ linux-2.6.36-rc7-fast/drivers/md/multisnap/dm-multisnap-mikulas-struct.h 2010-10-15 02:31:37.000000000 +0200 @@ -297,13 +297,15 @@ struct multisnap_commit_block { __u8 flags; /* DM_MULTISNAP_FLAG_* */ __u16 self2; __u32 self1; - __u8 pad[8]; + __u64 merging_snapid; /* the snapshot ID that is being merged */ struct commit_block_tmp_remap tmp_remap[N_REMAPS]; }; #define DM_MULTISNAP_FLAG_DELETING 0x01 #define DM_MULTISNAP_FLAG_PENDING_DELETE 0x02 +#define DM_MULTISNAP_FLAG_MERGE_SNAPSHOT 0x04 +#define DM_MULTISNAP_FLAG_MERGE_IN_PROGRESS 0x08 #define DM_MULTISNAP_MAX_BITMAP_DEPTH 6