From: Alasdair Kergon Date: Mon, 20 May 2002 14:11:34 +0000 (+0000) Subject: Snapshot fixes (sync with bk). X-Git-Tag: beta3~3 X-Git-Url: https://sourceware.org/git/?a=commitdiff_plain;h=a22f3063e7ee0bc8b65b2db3bc5c228a55f56974;p=dm.git Snapshot fixes (sync with bk). --- diff --git a/kernel/VERSION b/kernel/VERSION index 7584a01..e4008f9 100644 --- a/kernel/VERSION +++ b/kernel/VERSION @@ -1 +1 @@ -0.94.10-cvs (2002-04-23) +0.94.11-cvs (2002-05-20) diff --git a/kernel/common/device-mapper.h b/kernel/common/device-mapper.h index e982191..8e1ff90 100644 --- a/kernel/common/device-mapper.h +++ b/kernel/common/device-mapper.h @@ -28,8 +28,8 @@ typedef int (*dm_ctr_fn) (struct dm_table *t, offset_t b, offset_t l, typedef void (*dm_dtr_fn) (struct dm_table *t, void *c); typedef int (*dm_map_fn) (struct buffer_head *bh, int rw, void *context); typedef int (*dm_err_fn) (struct buffer_head *bh, int rw, void *context); -typedef int (*dm_sts_fn) (status_type_t sts_type, char *, int maxlen, - void *context); +typedef int (*dm_status_fn) (status_type_t status_type, char *result, + int maxlen, void *context); typedef int (*dm_wait_fn) (void *context, wait_queue_t *wq, int add); void dm_error(const char *message); @@ -52,7 +52,7 @@ struct target_type { dm_dtr_fn dtr; dm_map_fn map; dm_err_fn err; - dm_sts_fn sts; + dm_status_fn status; dm_wait_fn wait; }; diff --git a/kernel/common/dm-exception-store.c b/kernel/common/dm-exception-store.c index ae47e4e..87a0076 100644 --- a/kernel/common/dm-exception-store.c +++ b/kernel/common/dm-exception-store.c @@ -9,6 +9,7 @@ #include "dm-snapshot.h" #include "kcopyd.h" #include +#include #define SECTOR_SIZE 512 #define SECTOR_SHIFT 9 @@ -31,12 +32,15 @@ * the start of a fresh COW device before calling the snapshot * constructor. * - * The very first metadata block, which is also the first chunk - * on the COW device, will include a header struct followed by - * exception info. All other metadata chunks will solely consist - * of exception info. All on disk structures are in - * little-endian format. The end of the exceptions info is - * indicated by an exception with a new_chunk of 0. + * The first chunk of the COW device just contains the header. + * After this there is a chunk filled with exception metadata, + * followed by as many exception chunks as can fit in the + * metadata areas. + * + * All on disk structures are in little-endian format. The end + * of the exceptions info is indicated by an exception with a + * new_chunk of 0, which is invalid since it would point to the + * header chunk. */ /* @@ -49,7 +53,6 @@ */ #define SNAPSHOT_DISK_VERSION 1 -#if 0 struct disk_header { uint32_t magic; @@ -68,18 +71,23 @@ struct disk_header { /* In sectors */ uint32_t chunk_size; }; -#endif struct disk_exception { uint64_t old_chunk; uint64_t new_chunk; }; +struct commit_callback { + void (*callback)(void *, int success); + void *context; +}; + /* * The top level structure for a persistent exception store. */ struct pstore { struct dm_snapshot *snap; /* up pointer to my snapshot */ + int version; int valid; uint32_t chunk_size; uint32_t exceptions_per_area; @@ -108,6 +116,10 @@ struct pstore { * metadata area. */ uint32_t current_committed; + + atomic_t pending_count; + uint32_t callback_count; + struct commit_callback *callbacks; }; /* @@ -134,6 +146,7 @@ static int do_io(int rw, struct kcopyd_region *where, struct kiobuf *iobuf) iobuf->blocks[i] = start++; iobuf->length = where->count << 9; + iobuf->locked = 1; status = brw_kiovec(rw, 1, &iobuf, where->dev, iobuf->blocks, blocksize); @@ -143,6 +156,7 @@ static int do_io(int rw, struct kcopyd_region *where, struct kiobuf *iobuf) return 0; } +#if LINUX_VERSION_CODE < KERNEL_VERSION ( 2, 4, 19) /* * FIXME: Remove once 2.4.19 has been released. */ @@ -166,13 +180,14 @@ struct page *vmalloc_to_page(void *vmalloc_addr) } return page; } +#endif static int allocate_iobuf(struct pstore *ps) { - size_t i, r, len, nr_pages; + size_t i, r = -ENOMEM, len, nr_pages; struct page *page; - len = ps->chunk_size * SECTOR_SIZE; + len = ps->chunk_size << SECTOR_SHIFT; /* * Allocate the chunk_size block of memory that will hold @@ -180,37 +195,43 @@ static int allocate_iobuf(struct pstore *ps) */ ps->area = vmalloc(len); if (!ps->area) - return -ENOMEM; + return r; - if (alloc_kiovec(1, &ps->iobuf)) { - vfree(ps->area); - return -ENOMEM; - } + if (alloc_kiovec(1, &ps->iobuf)) + goto bad; + + if (alloc_kiobuf_bhs(ps->iobuf)) + goto bad; nr_pages = ps->chunk_size / (PAGE_SIZE / SECTOR_SIZE); r = expand_kiobuf(ps->iobuf, nr_pages); - if (r) { - vfree(ps->area); - return -ENOMEM; - } + if (r) + goto bad; /* * We lock the pages for ps->area into memory since they'll be * doing a lot of io. - * - * FIXME: Check that there's no race, ie. the pages can't - * be swapped out before we lock them, we may have to - * allocate them as seperate pages after all :( */ - for (i = 0; i < len; i += PAGE_SIZE) { - page = vmalloc_to_page(ps->area + i); + for (i = 0; i < nr_pages; i++) { + page = vmalloc_to_page(ps->area + (i * PAGE_SIZE)); LockPage(page); ps->iobuf->maplist[i] = page; ps->iobuf->nr_pages++; } + ps->iobuf->nr_pages = nr_pages; ps->iobuf->offset = 0; + return 0; + + bad: + if (ps->iobuf) + free_kiovec(1, &ps->iobuf); + + if (ps->area) + vfree(ps->area); + ps->iobuf = NULL; + return r; } static void free_iobuf(struct pstore *ps) @@ -219,24 +240,44 @@ static void free_iobuf(struct pstore *ps) for (i = 0; i < ps->iobuf->nr_pages; i++) UnlockPage(ps->iobuf->maplist[i]); + ps->iobuf->locked = 0; free_kiovec(1, &ps->iobuf); vfree(ps->area); } /* - * Read or write a metadata area. + * Read or write a chunk aligned and sized block of data from a device. */ -static int area_io(struct pstore *ps, uint32_t area, int rw) +static int chunk_io(struct pstore *ps, uint32_t chunk, int rw) { int r; struct kcopyd_region where; where.dev = ps->snap->cow->dev; - where.sector = ((ps->exceptions_per_area + 1) * ps->chunk_size) * area; + where.sector = ps->chunk_size * chunk; where.count = ps->chunk_size; - r = do_io(rw, &where, ps->area); + r = do_io(rw, &where, ps->iobuf); + if (r) + return r; + + return 0; +} + +/* + * Read or write a metadata area. Remembering to skip the first + * chunk which holds the header. + */ +static int area_io(struct pstore *ps, uint32_t area, int rw) +{ + int r; + uint32_t chunk; + + /* convert a metadata area index to a chunk index */ + chunk = 1 + ((ps->exceptions_per_area + 1) * area); + + r = chunk_io(ps, chunk, rw); if (r) return r; @@ -246,16 +287,59 @@ static int area_io(struct pstore *ps, uint32_t area, int rw) static int zero_area(struct pstore *ps, uint32_t area) { - memset(ps, 0, ps->chunk_size); + memset(ps->area, 0, ps->chunk_size << SECTOR_SHIFT); return area_io(ps, area, WRITE); } +static int read_header(struct pstore *ps, int *new_snapshot) +{ + int r; + struct disk_header *dh; + + r = chunk_io(ps, 0, READ); + if (r) + return r; + + dh = (struct disk_header *) ps->area; + + if (dh->magic == 0) { + *new_snapshot = 1; + + } else if (dh->magic == SNAP_MAGIC) { + *new_snapshot = 0; + ps->valid = dh->valid; + ps->version = dh->version; + ps->chunk_size = dh->chunk_size; + + } else { + DMWARN("Invalid/corrupt snapshot"); + r = -ENXIO; + } + + return r; +} + +static int write_header(struct pstore *ps) +{ + struct disk_header *dh; + + memset(ps->area, 0, ps->chunk_size << SECTOR_SHIFT); + + dh = (struct disk_header *) ps->area; + dh->magic = SNAP_MAGIC; + dh->valid = ps->valid; + dh->version = ps->version; + dh->chunk_size = ps->chunk_size; + + return chunk_io(ps, 0, WRITE); +} + /* * Access functions for the disk exceptions, these do the endian conversions. */ static struct disk_exception *get_exception(struct pstore *ps, uint32_t index) { - if (index > ps->exceptions_per_area) + if (index >= ps->exceptions_per_area) return NULL; return ((struct disk_exception *) ps->area) + index; @@ -271,10 +355,8 @@ static int read_exception(struct pstore *ps, return -EINVAL; /* copy it */ - memcpy(result, e, sizeof(&result)); - - result->old_chunk = le64_to_cpu(result->old_chunk); - result->new_chunk = le64_to_cpu(result->new_chunk); + result->old_chunk = le64_to_cpu(e->old_chunk); + result->new_chunk = le64_to_cpu(e->new_chunk); return 0; } @@ -289,8 +371,8 @@ static int write_exception(struct pstore *ps, return -EINVAL; /* copy it */ - e->old_chunk = cpu_to_le64(e->old_chunk); - e->new_chunk = cpu_to_le64(e->new_chunk); + e->old_chunk = cpu_to_le64(de->old_chunk); + e->new_chunk = cpu_to_le64(de->new_chunk); return 0; } @@ -318,9 +400,9 @@ static int insert_exceptions(struct pstore *ps, int *full) * If the new_chunk is pointing at the start of * the COW device, where the first metadata area * is we know that we've hit the end of the - * exceptions. Therefor the area is not full. + * exceptions. Therefore the area is not full. */ - if (de.new_chunk) { + if (de.new_chunk == 0LL) { ps->current_committed = i; *full = 0; break; @@ -346,13 +428,13 @@ static int insert_exceptions(struct pstore *ps, int *full) static int read_exceptions(struct pstore *ps) { uint32_t area; - int r, full = 0; + int r, full = 1; /* * Keeping reading chunks and inserting exceptions until * we find a partially full area. */ - for (area = 0; !full; area++) { + for (area = 0; full; area++) { r = area_io(ps, area, READ); if (r) return r; @@ -372,12 +454,19 @@ static inline struct pstore *get_info(struct exception_store *store) return (struct pstore *) store->context; } +static int persistent_percentfull(struct exception_store *store) +{ + struct pstore *ps = get_info(store); + return (ps->next_free * store->snap->chunk_size * 100) / + get_dev_size(store->snap->cow->dev); +} + static void persistent_destroy(struct exception_store *store) { struct pstore *ps = get_info(store); + vfree(ps->callbacks); free_iobuf(ps); - vfree(ps->area); kfree(ps); } @@ -386,6 +475,11 @@ static int persistent_prepare(struct exception_store *store, { struct pstore *ps = get_info(store); uint32_t stride; + offset_t size = get_dev_size(store->snap->cow->dev); + + /* Is there enough room ? */ + if (size <= (ps->next_free * store->snap->chunk_size)) + return -ENOSPC; e->new_chunk = ps->next_free; @@ -397,6 +491,7 @@ static int persistent_prepare(struct exception_store *store, if (!(++ps->next_free % stride)) ps->next_free++; + atomic_inc(&ps->pending_count); return 0; } @@ -405,59 +500,67 @@ static void persistent_commit(struct exception_store *store, void (*callback) (void *, int success), void *callback_context) { - int r; + int r, i; struct pstore *ps = get_info(store); struct disk_exception de; + struct commit_callback *cb; de.old_chunk = e->old_chunk; de.new_chunk = e->new_chunk; - write_exception(ps, ps->current_committed, &de); + write_exception(ps, ps->current_committed++, &de); /* - * Write the whole area to the disk for now, later we'll - * try and defer the write. + * 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. */ - r = area_io(ps, ps->current_area, WRITE); - if (r) - goto bad; + cb = ps->callbacks + ps->callback_count++; + cb->callback = callback; + cb->context = callback_context; /* - * Notify the snapshot that the commit has actually - * happened. + * If there are no more exceptions in flight, or we have + * filled this metadata area we commit the exceptions to + * disk. */ - callback(callback_context, 1); + if (atomic_dec_and_test(&ps->pending_count) || + (ps->current_committed == ps->exceptions_per_area)) { + r = area_io(ps, ps->current_area, WRITE); + if (r) + ps->valid = 0; + + for (i = 0; i < ps->callback_count; i++) { + cb = ps->callbacks + i; + cb->callback(cb->context, r == 0 ? 1 : 0); + } + + ps->callback_count = 0; + } /* * Have we completely filled the current area ? */ - if (++ps->current_committed > ps->exceptions_per_area) { + if (ps->current_committed == ps->exceptions_per_area) { ps->current_committed = 0; r = zero_area(ps, ps->current_area + 1); if (r) - goto bad; + ps->valid = 0; } - - return; - - bad: - ps->valid = 0; - callback(callback_context, 0); } static void persistent_drop(struct exception_store *store) { struct pstore *ps = get_info(store); - /* - * FIXME: This function is pointless until we have the - * header. - */ ps->valid = 0; + if (write_header(ps)) + DMWARN("write header failed"); } -int persistent_init(struct exception_store *store, uint32_t chunk_size) +int dm_create_persistent(struct exception_store *store, uint32_t chunk_size) { - int r; + int r, new_snapshot; struct pstore *ps; /* allocate the pstore */ @@ -465,34 +568,99 @@ int persistent_init(struct exception_store *store, uint32_t chunk_size) if (!ps) return -ENOMEM; - r = allocate_iobuf(ps); - if (r) - return r; - ps->snap = store->snap; ps->valid = 1; + ps->version = SNAPSHOT_DISK_VERSION; ps->chunk_size = chunk_size; ps->exceptions_per_area = (chunk_size << SECTOR_SHIFT) / sizeof(struct disk_exception); - ps->next_free = 1; + ps->next_free = 2; /* skipping the header and first area */ ps->current_committed = 0; + r = allocate_iobuf(ps); + if (r) + goto bad; + /* - * Read the metadata. + * Allocate space for all the callbacks. */ - r = read_exceptions(ps); - if (r) { - free_iobuf(ps); - kfree(ps); + ps->callback_count = 0; + atomic_set(&ps->pending_count, 0); + ps->callbacks = vmalloc(sizeof(*ps->callbacks) * + ps->exceptions_per_area); + + if (!ps->callbacks) + goto bad; + + /* + * Read the snapshot header. + */ + r = read_header(ps, &new_snapshot); + if (r) + goto bad; + + /* + * Do we need to setup a new snapshot ? + */ + if (new_snapshot) { + r = write_header(ps); + if (r) { + DMWARN("write_header failed"); + goto bad; + } + + r = zero_area(ps, 0); + if (r) { + DMWARN("zero_area(0) failed"); + goto bad; + } + + } else { + /* + * Sanity checks. + */ + if (ps->chunk_size != chunk_size) { + DMWARN("chunk size for existing snapshot different " + "from that requested"); + r = -EINVAL; + goto bad; + } + + if (ps->version != SNAPSHOT_DISK_VERSION) { + DMWARN("unable to handle snapshot disk version %d", + ps->version); + r = -EINVAL; + goto bad; + } + + /* + * Read the metadata. + */ + r = read_exceptions(ps); + if (r) + goto bad; } store->destroy = persistent_destroy; store->prepare_exception = persistent_prepare; store->commit_exception = persistent_commit; store->drop_snapshot = persistent_drop; + store->percent_full = persistent_percentfull; store->context = ps; return r; + + bad: + if (ps) { + if (ps->callbacks) + vfree(ps->callbacks); + + if (ps->iobuf) + free_iobuf(ps); + + kfree(ps); + } + return r; } /*----------------------------------------------------------------- @@ -502,12 +670,12 @@ struct transient_c { offset_t next_free; }; -void destroy_transient(struct exception_store *store) +void transient_destroy(struct exception_store *store) { kfree(store->context); } -int prepare_transient(struct exception_store *store, struct exception *e) +int transient_prepare(struct exception_store *store, struct exception *e) { struct transient_c *tc = (struct transient_c *) store->context; offset_t size = get_dev_size(store->snap->cow->dev); @@ -521,7 +689,7 @@ int prepare_transient(struct exception_store *store, struct exception *e) return 0; } -void commit_transient(struct exception_store *store, +void transient_commit(struct exception_store *store, struct exception *e, void (*callback) (void *, int success), void *callback_context) @@ -530,7 +698,7 @@ void commit_transient(struct exception_store *store, callback(callback_context, 1); } -int percentfull_transient(struct exception_store *store) +static int transient_percentfull(struct exception_store *store) { struct transient_c *tc = (struct transient_c *) store->context; return (tc->next_free * 100) / get_dev_size(store->snap->cow->dev); @@ -542,10 +710,10 @@ int dm_create_transient(struct exception_store *store, struct transient_c *tc; memset(store, 0, sizeof(*store)); - store->destroy = destroy_transient; - store->prepare_exception = prepare_transient; - store->commit_exception = commit_transient; - store->percent_full = percentfull_transient; + store->destroy = transient_destroy; + store->prepare_exception = transient_prepare; + store->commit_exception = transient_commit; + store->percent_full = transient_percentfull; store->snap = s; tc = kmalloc(sizeof(struct transient_c), GFP_KERNEL); diff --git a/kernel/common/dm-linear.c b/kernel/common/dm-linear.c index ba10296..2a69b4e 100644 --- a/kernel/common/dm-linear.c +++ b/kernel/common/dm-linear.c @@ -79,12 +79,12 @@ static int linear_map(struct buffer_head *bh, int rw, void *context) return 1; } -static int linear_sts(status_type_t sts_type, char *result, int maxlen, - void *context) +static int linear_status(status_type_t type, char *result, int maxlen, + void *context) { struct linear_c *lc = (struct linear_c *) context; - switch (sts_type) { + switch (type) { case STATUSTYPE_INFO: result[0] = '\0'; break; @@ -103,7 +103,7 @@ static struct target_type linear_target = { ctr: linear_ctr, dtr: linear_dtr, map: linear_map, - sts: linear_sts, + status: linear_status, wait: NULL, /* No wait function */ }; diff --git a/kernel/common/dm-snapshot.c b/kernel/common/dm-snapshot.c index c1bd381..8168101 100644 --- a/kernel/common/dm-snapshot.c +++ b/kernel/common/dm-snapshot.c @@ -504,6 +504,7 @@ static int snapshot_ctr(struct dm_table *t, offset_t b, offset_t l, init_waitqueue_head(&s->waitq); s->chunk_size = chunk_size; s->chunk_mask = chunk_size - 1; + s->type = *persistent; for (s->chunk_shift = 0; chunk_size; s->chunk_shift++, chunk_size >>= 1) ; @@ -524,12 +525,11 @@ static int snapshot_ctr(struct dm_table *t, offset_t b, offset_t l, * Check the persistent flag - done here because we need the iobuf * to check the LV header */ -#if 0 + s->store.snap = s; + if ((*persistent & 0x5f) == 'P') - r = dm_create_persistent(&s->store, s, blocksize, - extent_size, context); + r = dm_create_persistent(&s->store, s->chunk_size); else -#endif r = dm_create_transient(&s->store, s, blocksize, context); if (r) { @@ -554,6 +554,7 @@ static int snapshot_ctr(struct dm_table *t, offset_t b, offset_t l, #if LVM_VFS_ENHANCEMENT unlockfs(s->origin->dev); #endif + kcopyd_inc_client_count(); *context = s; return 0; @@ -593,6 +594,8 @@ static void snapshot_dtr(struct dm_table *t, void *context) dm_table_put_device(t, s->origin); dm_table_put_device(t, s->cow); kfree(s); + + kcopyd_dec_client_count(); } /* @@ -620,7 +623,6 @@ static void flush_buffers(struct buffer_head *bh) bh = n; } - /* FIXME: not sure we can this from this context. */ run_task_queue(&tq_disk); } @@ -873,6 +875,20 @@ static int snapshot_map(struct buffer_head *bh, int rw, void *context) return r; } +static void list_merge(struct list_head *l1, struct list_head *l2) +{ + struct list_head *l1_n, *l2_p; + + l1_n = l1->next; + l2_p = l2->prev; + + l1->next = l2; + l2->prev = l1; + + l2_p->next = l1_n; + l1_n->prev = l2_p; +} + static int __origin_write(struct list_head *snapshots, struct buffer_head *bh) { int r = 1; @@ -912,8 +928,8 @@ static int __origin_write(struct list_head *snapshots, struct buffer_head *bh) } else { if (last) - list_splice(&pe->siblings, - &last->siblings); + list_merge(&pe->siblings, + &last->siblings); last = pe; r = 0; @@ -942,14 +958,14 @@ static int __origin_write(struct list_head *snapshots, struct buffer_head *bh) return r; } -static int snapshot_sts(status_type_t sts_type, char *result, - int maxlen, void *context) +static int snapshot_status(status_type_t type, char *result, + int maxlen, void *context) { struct dm_snapshot *snap = (struct dm_snapshot *) context; - char cowdevname[PATH_MAX]; - char orgdevname[PATH_MAX]; + char cow[16]; + char org[16]; - switch (sts_type) { + switch (type) { case STATUSTYPE_INFO: if (!snap->valid) snprintf(result, maxlen, "Invalid"); @@ -964,13 +980,15 @@ static int snapshot_sts(status_type_t sts_type, char *result, break; case STATUSTYPE_TABLE: - /* kdevname returns a static pointer so we - need to make private copies if the output is to make sense */ - strcpy(cowdevname, kdevname(snap->cow->dev)); - strcpy(orgdevname, kdevname(snap->origin->dev)); - snprintf(result, maxlen, "%s %s %c %ld", orgdevname, cowdevname, - 'N', /* TODO persistent snaps */ - snap->chunk_size); + /* + * kdevname returns a static pointer so we need + * to make private copies if the output is to + * make sense. + */ + strncpy(cow, kdevname(snap->cow->dev), sizeof(cow)); + strncpy(org, kdevname(snap->origin->dev), sizeof(org)); + snprintf(result, maxlen, "%s %s %c %ld", org, cow, + snap->type, snap->chunk_size); break; } @@ -1054,12 +1072,12 @@ static int origin_map(struct buffer_head *bh, int rw, void *context) return (rw == WRITE) ? do_origin(dev, bh) : 1; } -static int origin_sts(status_type_t sts_type, char *result, - int maxlen, void *context) +static int origin_status(status_type_t type, char *result, + int maxlen, void *context) { struct dm_dev *dev = (struct dm_dev *) context; - switch (sts_type) { + switch (type) { case STATUSTYPE_INFO: result[0] = '\0'; break; @@ -1078,7 +1096,7 @@ static struct target_type origin_target = { ctr: origin_ctr, dtr: origin_dtr, map: origin_map, - sts: origin_sts, + status: origin_status, wait: NULL, err: NULL }; @@ -1089,7 +1107,7 @@ static struct target_type snapshot_target = { ctr: snapshot_ctr, dtr: snapshot_dtr, map: snapshot_map, - sts: snapshot_sts, + status: snapshot_status, wait: snapshot_wait, err: NULL }; diff --git a/kernel/common/dm-snapshot.h b/kernel/common/dm-snapshot.h index 6741f96..e72ae59 100644 --- a/kernel/common/dm-snapshot.h +++ b/kernel/common/dm-snapshot.h @@ -94,6 +94,9 @@ struct dm_snapshot { /* You can't use a snapshot if this is 0 (e.g. if full) */ int valid; + /* Used for display of table */ + char type; + /* The last percentage we notified */ int last_percent; @@ -114,9 +117,7 @@ int dm_add_exception(struct dm_snapshot *s, chunk_t old, chunk_t new); * Constructor and destructor for the default persistent * store. */ -int dm_create_persistent(struct exception_store *store, - struct dm_snapshot *s, - int blocksize, offset_t extent_size, void **error); +int dm_create_persistent(struct exception_store *store, uint32_t chunk_size); int dm_create_transient(struct exception_store *store, struct dm_snapshot *s, int blocksize, void **error); diff --git a/kernel/common/dm-stripe.c b/kernel/common/dm-stripe.c index a0ae1d6..337aea2 100644 --- a/kernel/common/dm-stripe.c +++ b/kernel/common/dm-stripe.c @@ -173,14 +173,14 @@ static int stripe_map(struct buffer_head *bh, int rw, void *context) return 1; } -static int stripe_sts(status_type_t sts_type, char *result, int maxlen, - void *context) +static int stripe_status(status_type_t type, char *result, int maxlen, + void *context) { struct stripe_c *sc = (struct stripe_c *) context; int offset; int i; - switch (sts_type) { + switch (type) { case STATUSTYPE_INFO: result[0] = '\0'; break; @@ -206,7 +206,7 @@ static struct target_type stripe_target = { ctr: stripe_ctr, dtr: stripe_dtr, map: stripe_map, - sts: stripe_sts, + status: stripe_status, wait: NULL, }; diff --git a/kernel/common/dm-target.c b/kernel/common/dm-target.c index cb60948..fc5c640 100644 --- a/kernel/common/dm-target.c +++ b/kernel/common/dm-target.c @@ -224,8 +224,8 @@ static struct target_type error_target = { ctr: io_err_ctr, dtr: io_err_dtr, map: io_err_map, - sts: NULL, - wait: NULL, + status: NULL, + wait: NULL, }; int dm_target_init(void) diff --git a/kernel/common/dm.c.in b/kernel/common/dm.c.in index d3385dc..c59fbc4 100644 --- a/kernel/common/dm.c.in +++ b/kernel/common/dm.c.in @@ -272,7 +272,6 @@ static struct { xx(dm_target) xx(dm_linear) xx(dm_stripe) - xx(kcopyd) xx(dm_snapshot) xx(dm_interface) #undef xx diff --git a/kernel/common/kcopyd.c b/kernel/common/kcopyd.c index 64372e1..cc50b75 100644 --- a/kernel/common/kcopyd.c +++ b/kernel/common/kcopyd.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include "kcopyd.h" @@ -25,7 +27,8 @@ */ #define SECTOR_SIZE 512 #define SECTOR_SHIFT 9 -static int wake_kcopyd(void); + +static void wake_kcopyd(void); /*----------------------------------------------------------------- * We reserve our own pool of preallocated pages that are @@ -40,8 +43,9 @@ static int wake_kcopyd(void); static DECLARE_MUTEX(_pages_lock); static int _num_free_pages; static struct page *_pages_array[NUM_PAGES]; +static DECLARE_MUTEX(start_lock); -static __init int init_pages(void) +static int init_pages(void) { int i; struct page *p; @@ -123,7 +127,7 @@ static spinlock_t _buffer_lock = SPIN_LOCK_UNLOCKED; static struct buffer_head *_all_buffers; static struct buffer_head *_free_buffers; -static __init int init_buffers(void) +static int init_buffers(void) { int i; struct buffer_head *buffers; @@ -215,7 +219,7 @@ static LIST_HEAD(_complete_jobs); static LIST_HEAD(_io_jobs); static LIST_HEAD(_pages_jobs); -static __init int init_jobs(void) +static int init_jobs(void) { INIT_LIST_HEAD(&_complete_jobs); INIT_LIST_HEAD(&_io_jobs); @@ -458,24 +462,23 @@ static void do_work(void) /*----------------------------------------------------------------- * The daemon *---------------------------------------------------------------*/ -static struct task_struct *_kcopyd_task; static atomic_t _kcopyd_must_die; static DECLARE_MUTEX(_run_lock); static DECLARE_WAIT_QUEUE_HEAD(_job_queue); -static int kcopyd(void *start_lock) +static int kcopyd(void *arg) { DECLARE_WAITQUEUE(wq, current); daemonize(); strcpy(current->comm, "kcopyd"); - _kcopyd_task = current; atomic_set(&_kcopyd_must_die, 0); - down(&_run_lock); - up((struct semaphore *) start_lock); add_wait_queue(&_job_queue, &wq); + down(&_run_lock); + up(&start_lock); + while (1) { set_current_state(TASK_INTERRUPTIBLE); @@ -490,17 +493,17 @@ static int kcopyd(void *start_lock) remove_wait_queue(&_job_queue, &wq); up(&_run_lock); - DMINFO("kcopyd shutting down"); + return 0; } static int start_daemon(void) { static pid_t pid = 0; - DECLARE_MUTEX(start_lock); down(&start_lock); - pid = kernel_thread(kcopyd, &start_lock, 0); + + pid = kernel_thread(kcopyd, NULL, 0); if (pid <= 0) { DMERR("Failed to start kcopyd thread"); return -EAGAIN; @@ -510,34 +513,24 @@ static int start_daemon(void) * wait for the daemon to up this mutex. */ down(&start_lock); - DMINFO("Started kcopyd thread"); + up(&start_lock); return 0; } static int stop_daemon(void) { - if (_kcopyd_task) { - atomic_set(&_kcopyd_must_die, 1); - wake_kcopyd(); - down(&_run_lock); - } + atomic_set(&_kcopyd_must_die, 1); + wake_kcopyd(); + down(&_run_lock); + up(&_run_lock); return 0; } -static int wake_kcopyd(void) +static void wake_kcopyd(void) { - int r = 0; - - /* Start the thread if we don't have one already */ - if (!_kcopyd_task) - r = start_daemon(); - - if (!r) - wake_up_interruptible(&_job_queue); - - return r; + wake_up_interruptible(&_job_queue); } static int calc_shift(unsigned int n) @@ -584,7 +577,7 @@ struct copy_info { static kmem_cache_t *_copy_cache = NULL; static mempool_t *_copy_pool = NULL; -static __init int init_copier(void) +static int init_copier(void) { _copy_cache = kmem_cache_create("kcopyd-info", sizeof(struct copy_info), @@ -636,7 +629,6 @@ void copy_complete(struct kcopyd_job *job) /* * These callback functions implement the state machine that copies regions. - * FIXME: handle large regions. */ void copy_write(struct kcopyd_job *job) { @@ -717,24 +709,22 @@ static struct { #undef xx }; -static int _has_initialised = 0; +static int _client_count = 0; +static DECLARE_MUTEX(_client_count_sem); -int __init kcopyd_init(void) +static int kcopyd_init(void) { const int count = sizeof(_inits) / sizeof(*_inits); int r, i; - if (_has_initialised) - return 0; - for (i = 0; i < count; i++) { r = _inits[i].init(); if (r) goto bad; } - _has_initialised = 1; + start_daemon(); return 0; bad: @@ -744,7 +734,7 @@ int __init kcopyd_init(void) return r; } -void kcopyd_exit(void) +static void kcopyd_exit(void) { int i = sizeof(_inits) / sizeof(*_inits); @@ -753,6 +743,28 @@ void kcopyd_exit(void) while (i--) _inits[i].exit(); +} + +void kcopyd_inc_client_count(void) +{ + /* + * What I need here is an atomic_test_and_inc that returns + * the previous value of the atomic... In its absence I lock + * an int with a semaphore. :-( + */ + down(&_client_count_sem); + if (_client_count == 0) + kcopyd_init(); + _client_count++; + + up(&_client_count_sem); +} + +void kcopyd_dec_client_count(void) +{ + down(&_client_count_sem); + if (--_client_count == 0) + kcopyd_exit(); - _has_initialised = 0; + up(&_client_count_sem); } diff --git a/kernel/common/kcopyd.h b/kernel/common/kcopyd.h index dcfdbae..be316f7 100644 --- a/kernel/common/kcopyd.h +++ b/kernel/common/kcopyd.h @@ -66,7 +66,7 @@ struct kcopyd_job { * Set this to ensure you are notified when the job has * completed. 'context' is for callback to use. */ - void (*callback) (struct kcopyd_job *job); + void (*callback)(struct kcopyd_job *job); void *context; }; @@ -82,15 +82,16 @@ int kcopyd_queue_job(struct kcopyd_job *job); * Submit a copy job to kcopyd. This is built on top of the * previous three fns. */ -typedef void (*kcopyd_notify_fn) (int err, void *context); +typedef void (*kcopyd_notify_fn)(int err, void *context); -int kcopyd_copy(struct kcopyd_region *from, - struct kcopyd_region *to, kcopyd_notify_fn fn, void *context); +int kcopyd_copy(struct kcopyd_region *from, struct kcopyd_region *to, + kcopyd_notify_fn fn, void *context); /* - * Setup/teardown. + * We only want kcopyd to reserve resources if someone is + * actually using it. */ -int kcopyd_init(void); -void kcopyd_exit(void); +void kcopyd_inc_client_count(void); +void kcopyd_dec_client_count(void); #endif diff --git a/kernel/ioctl/dm-ioctl.c b/kernel/ioctl/dm-ioctl.c index 41d582e..6001621 100644 --- a/kernel/ioctl/dm-ioctl.c +++ b/kernel/ioctl/dm-ioctl.c @@ -283,7 +283,7 @@ static int __status(struct mapped_device *md, struct dm_ioctl *param, if (outptr - outbuf + sizeof(struct dm_target_spec) > param->data_size) - return -ENOMEM; + return -ENOMEM; spec = (struct dm_target_spec *) outptr; @@ -295,10 +295,10 @@ static int __status(struct mapped_device *md, struct dm_ioctl *param, outptr += sizeof(struct dm_target_spec); /* Get the status/table string from the target driver */ - if (tt->sts) - tt->sts(type, outptr, - outbuf + param->data_size - outptr, - md->map->targets[i].private); + if (tt->status) + tt->status(type, outptr, + outbuf + param->data_size - outptr, + md->map->targets[i].private); else outptr[0] = '\0'; @@ -331,7 +331,7 @@ static int __wait(struct mapped_device *md, struct dm_ioctl *param) /* Add ourself to the target's wait queue */ if (tt->wait && (!tt->wait(md->map->targets[i].private, &waitq, 1))) - waiting = 1; + waiting = 1; } /* If at least one call succeeded then sleep */ @@ -525,7 +525,7 @@ static int create(struct dm_ioctl *param, struct dm_ioctl *user) } minor = (param->flags & DM_PERSISTENT_DEV_FLAG) ? - MINOR(to_kdev_t(param->dev)) : -1; + MINOR(to_kdev_t(param->dev)) : -1; ro = (param->flags & DM_READONLY_FLAG) ? 1 : 0;