Sorry, I forgot to include the patch... here it is.. On Wed, May 12, 2004 at 06:24:02PM -0700, Dave Olien wrote: > > Here's the patch I have come up with. If you can give it a try, that'd > be good. diff -ur linux-2.6.6-udm1-original/drivers/md/dm-exception-store.c linux-2.6.6-udm1-patched/drivers/md/dm-exception-store.c --- linux-2.6.6-udm1-original/drivers/md/dm-exception-store.c 2004-05-07 14:28:31.000000000 -0700 +++ linux-2.6.6-udm1-patched/drivers/md/dm-exception-store.c 2004-05-12 15:41:36.000000000 -0700 @@ -118,9 +118,12 @@ */ uint32_t current_committed; - atomic_t pending_count; + uint32_t pending_count; uint32_t callback_count; struct commit_callback *callbacks; + wait_queue_head_t pstore_wait_queue; + spinlock_t pstore_lock; + int pstore_area_write; }; static inline unsigned int sectors_to_pages(unsigned int sectors) @@ -131,7 +134,7 @@ static int alloc_area(struct pstore *ps) { int r = -ENOMEM; - size_t len, nr_pages; + size_t len; len = ps->chunk_size << SECTOR_SHIFT; @@ -143,15 +146,11 @@ if (!ps->area) return r; - nr_pages = sectors_to_pages(ps->chunk_size); return 0; } static void free_area(struct pstore *ps) { - size_t nr_pages; - - nr_pages = sectors_to_pages(ps->chunk_size); vfree(ps->area); } @@ -207,15 +206,13 @@ dh = (struct disk_header *) ps->area; - if (le32_to_cpu(dh->magic) == 0) { + if (le32_to_cpu(dh->magic) == 0) *new_snapshot = 1; - - } else if (le32_to_cpu(dh->magic) == SNAP_MAGIC) { + else if (le32_to_cpu(dh->magic) == SNAP_MAGIC) { *new_snapshot = 0; ps->valid = le32_to_cpu(dh->valid); ps->version = le32_to_cpu(dh->version); ps->chunk_size = le32_to_cpu(dh->chunk_size); - } else { DMWARN("Invalid/corrupt snapshot"); r = -ENXIO; @@ -241,6 +238,8 @@ /* * Access functions for the disk exceptions, these do the endian conversions. + * The disk exceptions reside in the current memory-resident exception + * buffer */ static struct disk_exception *get_exception(struct pstore *ps, uint32_t index) { @@ -437,8 +436,11 @@ sector_t size = get_dev_size(store->snap->cow->bdev); /* Is there enough room ? */ - if (size < ((ps->next_free + 1) * store->snap->chunk_size)) + spin_lock_irq(&ps->pstore_lock); + if (size < ((ps->next_free + 1) * store->snap->chunk_size)) { + spin_unlock_irq(&ps->pstore_lock); return -ENOSPC; + } e->new_chunk = ps->next_free; @@ -450,7 +452,8 @@ if ((++ps->next_free % stride) == 1) ps->next_free++; - atomic_inc(&ps->pending_count); + ps->pending_count++; + spin_unlock_irq(&ps->pstore_lock); return 0; } @@ -465,15 +468,33 @@ struct disk_exception de; struct commit_callback *cb; + spin_lock_irq(&ps->pstore_lock); + + /* + * This can be called concurrently by per-cpu threads of the kcopyd + * work queue. + */ + while (ps->pstore_area_write) { + DECLARE_WAITQUEUE(wait, current); + /* + * wait until the area write is complete + * and the next meta data area is zero'd. + */ + add_wait_queue(&ps->pstore_wait_queue, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + spin_unlock_irq(&ps->pstore_lock); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&ps->pstore_wait_queue, &wait); + spin_lock_irq(&ps->pstore_lock); + } + de.old_chunk = e->old_chunk; de.new_chunk = e->new_chunk; write_exception(ps, ps->current_committed++, &de); /* - * Add the callback to the back of the array. This code - * is the only place where the callback array is - * manipulated, and we know that it will never be called - * multiple times concurrently. + * Add the callback to the back of the array. */ cb = ps->callbacks + ps->callback_count++; cb->callback = callback; @@ -484,8 +505,14 @@ * filled this metadata area we commit the exceptions to * disk. */ - if (atomic_dec_and_test(&ps->pending_count) || + if ((--ps->pending_count) || (ps->current_committed == ps->exceptions_per_area)) { + ps->pstore_area_write = 1; + spin_unlock_irq(&ps->pstore_lock); + /* + * we are guaranteed that only ONE thread at a time + * goes through this code. + */ r = area_io(ps, ps->current_area, WRITE); if (r) ps->valid = 0; @@ -496,17 +523,20 @@ } ps->callback_count = 0; + /* + * Have we completely filled the current area ? + */ + if (ps->current_committed == ps->exceptions_per_area) { + ps->current_committed = 0; + r = zero_area(ps, ps->current_area + 1); + if (r) + ps->valid = 0; + } + spin_lock_irq(&ps->pstore_lock); + ps->pstore_area_write = 0; + wake_up_all(&ps->pstore_wait_queue); } - - /* - * Have we completely filled the current area ? - */ - if (ps->current_committed == ps->exceptions_per_area) { - ps->current_committed = 0; - r = zero_area(ps, ps->current_area + 1); - if (r) - ps->valid = 0; - } + spin_unlock_irq(&ps->pstore_lock); } static void persistent_drop(struct exception_store *store) @@ -542,6 +572,9 @@ sizeof(struct disk_exception); ps->next_free = 2; /* skipping the header and first area */ ps->current_committed = 0; + init_waitqueue_head(&ps->pstore_wait_queue); + spin_lock_init(&ps->pstore_lock); + ps->pstore_area_write = 0; r = alloc_area(ps); if (r) @@ -551,7 +584,7 @@ * Allocate space for all the callbacks. */ ps->callback_count = 0; - atomic_set(&ps->pending_count, 0); + ps->pending_count = 0; ps->callbacks = dm_vcalloc(ps->exceptions_per_area, sizeof(*ps->callbacks));