X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fmd%2Fdm-exception-store.c;h=41f408068a7c9c2329c61eb7e3eb47a82af6e280;hb=cdf8803768db6f652d498628fe1421a23c025253;hp=cc07bbebbb1688156db22c88d746ae54a18b41ee;hpb=f9101210e7aa72daf92722d451a2f7e3af5f781f;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c index cc07bbe..41f4080 100644 --- a/drivers/md/dm-exception-store.c +++ b/drivers/md/dm-exception-store.c @@ -1,20 +1,24 @@ /* - * dm-snapshot.c + * dm-exception-store.c * * Copyright (C) 2001-2002 Sistina Software (UK) Limited. + * Copyright (C) 2006 Red Hat GmbH * * This file is released under the GPL. */ #include "dm.h" #include "dm-snap.h" -#include "dm-io.h" -#include "kcopyd.h" #include #include #include #include +#include +#include + +#define DM_MSG_PREFIX "snapshots" +#define DM_CHUNK_SIZE_DEFAULT_SECTORS 32 /* 16KB */ /*----------------------------------------------------------------- * Persistent snapshots, by persistent we mean that the snapshot @@ -91,7 +95,6 @@ struct pstore { struct dm_snapshot *snap; /* up pointer to my snapshot */ int version; int valid; - uint32_t chunk_size; uint32_t exceptions_per_area; /* @@ -121,11 +124,14 @@ struct pstore { atomic_t pending_count; uint32_t callback_count; struct commit_callback *callbacks; + struct dm_io_client *io_client; + + struct workqueue_struct *metadata_wq; }; -static inline unsigned int sectors_to_pages(unsigned int sectors) +static unsigned sectors_to_pages(unsigned sectors) { - return sectors / (PAGE_SIZE >> 9); + return DIV_ROUND_UP(sectors, PAGE_SIZE >> 9); } static int alloc_area(struct pstore *ps) @@ -133,7 +139,7 @@ static int alloc_area(struct pstore *ps) int r = -ENOMEM; size_t len; - len = ps->chunk_size << SECTOR_SHIFT; + len = ps->snap->chunk_size << SECTOR_SHIFT; /* * Allocate the chunk_size block of memory that will hold @@ -149,21 +155,57 @@ static int alloc_area(struct pstore *ps) static void free_area(struct pstore *ps) { vfree(ps->area); + ps->area = NULL; +} + +struct mdata_req { + struct dm_io_region *where; + struct dm_io_request *io_req; + struct work_struct work; + int result; +}; + +static void do_metadata(struct work_struct *work) +{ + struct mdata_req *req = container_of(work, struct mdata_req, work); + + req->result = dm_io(req->io_req, 1, req->where, NULL); } /* * Read or write a chunk aligned and sized block of data from a device. */ -static int chunk_io(struct pstore *ps, uint32_t chunk, int rw) +static int chunk_io(struct pstore *ps, uint32_t chunk, int rw, int metadata) { - struct io_region where; - unsigned long bits; + struct dm_io_region where = { + .bdev = ps->snap->cow->bdev, + .sector = ps->snap->chunk_size * chunk, + .count = ps->snap->chunk_size, + }; + struct dm_io_request io_req = { + .bi_rw = rw, + .mem.type = DM_IO_VMA, + .mem.ptr.vma = ps->area, + .client = ps->io_client, + .notify.fn = NULL, + }; + struct mdata_req req; + + if (!metadata) + return dm_io(&io_req, 1, &where, NULL); + + req.where = &where; + req.io_req = &io_req; - where.bdev = ps->snap->cow->bdev; - where.sector = ps->chunk_size * chunk; - where.count = ps->chunk_size; + /* + * Issue the synchronous I/O from a different thread + * to avoid generic_make_request recursion. + */ + INIT_WORK(&req.work, do_metadata); + queue_work(ps->metadata_wq, &req.work); + flush_workqueue(ps->metadata_wq); - return dm_io_sync_vm(1, &where, rw, ps->area, &bits); + return req.result; } /* @@ -178,7 +220,7 @@ static int area_io(struct pstore *ps, uint32_t area, int rw) /* convert a metadata area index to a chunk index */ chunk = 1 + ((ps->exceptions_per_area + 1) * area); - r = chunk_io(ps, chunk, rw); + r = chunk_io(ps, chunk, rw, 0); if (r) return r; @@ -188,7 +230,7 @@ static int area_io(struct pstore *ps, uint32_t area, int rw) static int zero_area(struct pstore *ps, uint32_t area) { - memset(ps->area, 0, ps->chunk_size << SECTOR_SHIFT); + memset(ps->area, 0, ps->snap->chunk_size << SECTOR_SHIFT); return area_io(ps, area, WRITE); } @@ -196,27 +238,76 @@ static int read_header(struct pstore *ps, int *new_snapshot) { int r; struct disk_header *dh; + chunk_t chunk_size; + int chunk_size_supplied = 1; - r = chunk_io(ps, 0, READ); + /* + * Use default chunk size (or hardsect_size, if larger) if none supplied + */ + if (!ps->snap->chunk_size) { + ps->snap->chunk_size = max(DM_CHUNK_SIZE_DEFAULT_SECTORS, + bdev_hardsect_size(ps->snap->cow->bdev) >> 9); + ps->snap->chunk_mask = ps->snap->chunk_size - 1; + ps->snap->chunk_shift = ffs(ps->snap->chunk_size) - 1; + chunk_size_supplied = 0; + } + + ps->io_client = dm_io_client_create(sectors_to_pages(ps->snap-> + chunk_size)); + if (IS_ERR(ps->io_client)) + return PTR_ERR(ps->io_client); + + r = alloc_area(ps); if (r) return r; + r = chunk_io(ps, 0, READ, 1); + if (r) + goto bad; + dh = (struct disk_header *) ps->area; if (le32_to_cpu(dh->magic) == 0) { *new_snapshot = 1; + return 0; + } - } 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"); + if (le32_to_cpu(dh->magic) != SNAP_MAGIC) { + DMWARN("Invalid or corrupt snapshot"); r = -ENXIO; + goto bad; } + *new_snapshot = 0; + ps->valid = le32_to_cpu(dh->valid); + ps->version = le32_to_cpu(dh->version); + chunk_size = le32_to_cpu(dh->chunk_size); + + if (!chunk_size_supplied || ps->snap->chunk_size == chunk_size) + return 0; + + DMWARN("chunk size %llu in device metadata overrides " + "table chunk size of %llu.", + (unsigned long long)chunk_size, + (unsigned long long)ps->snap->chunk_size); + + /* We had a bogus chunk_size. Fix stuff up. */ + free_area(ps); + + ps->snap->chunk_size = chunk_size; + ps->snap->chunk_mask = chunk_size - 1; + ps->snap->chunk_shift = ffs(chunk_size) - 1; + + r = dm_io_client_resize(sectors_to_pages(ps->snap->chunk_size), + ps->io_client); + if (r) + return r; + + r = alloc_area(ps); + return r; + +bad: + free_area(ps); return r; } @@ -224,15 +315,15 @@ static int write_header(struct pstore *ps) { struct disk_header *dh; - memset(ps->area, 0, ps->chunk_size << SECTOR_SHIFT); + memset(ps->area, 0, ps->snap->chunk_size << SECTOR_SHIFT); dh = (struct disk_header *) ps->area; dh->magic = cpu_to_le32(SNAP_MAGIC); dh->valid = cpu_to_le32(ps->valid); dh->version = cpu_to_le32(ps->version); - dh->chunk_size = cpu_to_le32(ps->chunk_size); + dh->chunk_size = cpu_to_le32(ps->snap->chunk_size); - return chunk_io(ps, 0, WRITE); + return chunk_io(ps, 0, WRITE, 1); } /* @@ -240,42 +331,29 @@ static int write_header(struct pstore *ps) */ static struct disk_exception *get_exception(struct pstore *ps, uint32_t index) { - if (index >= ps->exceptions_per_area) - return NULL; + BUG_ON(index >= ps->exceptions_per_area); return ((struct disk_exception *) ps->area) + index; } -static int read_exception(struct pstore *ps, - uint32_t index, struct disk_exception *result) +static void read_exception(struct pstore *ps, + uint32_t index, struct disk_exception *result) { - struct disk_exception *e; - - e = get_exception(ps, index); - if (!e) - return -EINVAL; + struct disk_exception *e = get_exception(ps, index); /* copy it */ result->old_chunk = le64_to_cpu(e->old_chunk); result->new_chunk = le64_to_cpu(e->new_chunk); - - return 0; } -static int write_exception(struct pstore *ps, - uint32_t index, struct disk_exception *de) +static void write_exception(struct pstore *ps, + uint32_t index, struct disk_exception *de) { - struct disk_exception *e; - - e = get_exception(ps, index); - if (!e) - return -EINVAL; + struct disk_exception *e = get_exception(ps, index); /* copy it */ e->old_chunk = cpu_to_le64(de->old_chunk); e->new_chunk = cpu_to_le64(de->new_chunk); - - return 0; } /* @@ -293,10 +371,7 @@ static int insert_exceptions(struct pstore *ps, int *full) *full = 1; for (i = 0; i < ps->exceptions_per_area; i++) { - r = read_exception(ps, i, &de); - - if (r) - return r; + read_exception(ps, i, &de); /* * If the new_chunk is pointing at the start of @@ -349,7 +424,7 @@ static int read_exceptions(struct pstore *ps) return 0; } -static inline struct pstore *get_info(struct exception_store *store) +static struct pstore *get_info(struct exception_store *store) { return (struct pstore *) store->context; } @@ -365,7 +440,8 @@ static void persistent_destroy(struct exception_store *store) { struct pstore *ps = get_info(store); - dm_io_put(sectors_to_pages(ps->chunk_size)); + destroy_workqueue(ps->metadata_wq); + dm_io_client_destroy(ps->io_client); vfree(ps->callbacks); free_area(ps); kfree(ps); @@ -373,7 +449,7 @@ static void persistent_destroy(struct exception_store *store) static int persistent_read_metadata(struct exception_store *store) { - int r, new_snapshot; + int r, uninitialized_var(new_snapshot); struct pstore *ps = get_info(store); /* @@ -384,6 +460,16 @@ static int persistent_read_metadata(struct exception_store *store) return r; /* + * Now we know correct chunk_size, complete the initialisation. + */ + ps->exceptions_per_area = (ps->snap->chunk_size << SECTOR_SHIFT) / + sizeof(struct disk_exception); + ps->callbacks = dm_vcalloc(ps->exceptions_per_area, + sizeof(*ps->callbacks)); + if (!ps->callbacks) + return -ENOMEM; + + /* * Do we need to setup a new snapshot ? */ if (new_snapshot) { @@ -403,11 +489,6 @@ static int persistent_read_metadata(struct exception_store *store) /* * Sanity checks. */ - if (!ps->valid) { - DMWARN("snapshot is marked invalid"); - return -EINVAL; - } - if (ps->version != SNAPSHOT_DISK_VERSION) { DMWARN("unable to handle snapshot disk version %d", ps->version); @@ -415,6 +496,12 @@ static int persistent_read_metadata(struct exception_store *store) } /* + * Metadata are valid, but snapshot is invalidated + */ + if (!ps->valid) + return 1; + + /* * Read the metadata. */ r = read_exceptions(ps); @@ -426,7 +513,7 @@ static int persistent_read_metadata(struct exception_store *store) } static int persistent_prepare(struct exception_store *store, - struct exception *e) + struct dm_snap_exception *e) { struct pstore *ps = get_info(store); uint32_t stride; @@ -451,7 +538,7 @@ static int persistent_prepare(struct exception_store *store, } static void persistent_commit(struct exception_store *store, - struct exception *e, + struct dm_snap_exception *e, void (*callback) (void *, int success), void *callback_context) { @@ -486,6 +573,16 @@ static void persistent_commit(struct exception_store *store, if (r) ps->valid = 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; + } + for (i = 0; i < ps->callback_count; i++) { cb = ps->callbacks + i; cb->callback(cb->context, r == 0 ? 1 : 0); @@ -493,16 +590,6 @@ static void persistent_commit(struct exception_store *store, 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; - } } static void persistent_drop(struct exception_store *store) @@ -514,46 +601,31 @@ static void persistent_drop(struct exception_store *store) DMWARN("write header failed"); } -int dm_create_persistent(struct exception_store *store, uint32_t chunk_size) +int dm_create_persistent(struct exception_store *store) { - int r; struct pstore *ps; - r = dm_io_get(sectors_to_pages(chunk_size)); - if (r) - return r; - /* allocate the pstore */ ps = kmalloc(sizeof(*ps), GFP_KERNEL); - if (!ps) { - r = -ENOMEM; - goto bad; - } + if (!ps) + return -ENOMEM; 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->area = NULL; ps->next_free = 2; /* skipping the header and first area */ ps->current_committed = 0; - r = alloc_area(ps); - if (r) - goto bad; - - /* - * Allocate space for all the callbacks. - */ ps->callback_count = 0; atomic_set(&ps->pending_count, 0); - ps->callbacks = dm_vcalloc(ps->exceptions_per_area, - sizeof(*ps->callbacks)); + ps->callbacks = NULL; - if (!ps->callbacks) { - r = -ENOMEM; - goto bad; + ps->metadata_wq = create_singlethread_workqueue("ksnaphd"); + if (!ps->metadata_wq) { + kfree(ps); + DMERR("couldn't start header metadata update thread"); + return -ENOMEM; } store->destroy = persistent_destroy; @@ -565,13 +637,6 @@ int dm_create_persistent(struct exception_store *store, uint32_t chunk_size) store->context = ps; return 0; - - bad: - dm_io_put(sectors_to_pages(chunk_size)); - if (ps && ps->area) - free_area(ps); - kfree(ps); - return r; } /*----------------------------------------------------------------- @@ -591,7 +656,8 @@ static int transient_read_metadata(struct exception_store *store) return 0; } -static int transient_prepare(struct exception_store *store, struct exception *e) +static int transient_prepare(struct exception_store *store, + struct dm_snap_exception *e) { struct transient_c *tc = (struct transient_c *) store->context; sector_t size = get_dev_size(store->snap->cow->bdev); @@ -606,9 +672,9 @@ static int transient_prepare(struct exception_store *store, struct exception *e) } static void transient_commit(struct exception_store *store, - struct exception *e, - void (*callback) (void *, int success), - void *callback_context) + struct dm_snap_exception *e, + void (*callback) (void *, int success), + void *callback_context) { /* Just succeed */ callback(callback_context, 1); @@ -621,18 +687,16 @@ static void transient_fraction_full(struct exception_store *store, *denominator = get_dev_size(store->snap->cow->bdev); } -int dm_create_transient(struct exception_store *store, - struct dm_snapshot *s, int blocksize) +int dm_create_transient(struct exception_store *store) { struct transient_c *tc; - memset(store, 0, sizeof(*store)); store->destroy = transient_destroy; store->read_metadata = transient_read_metadata; store->prepare_exception = transient_prepare; store->commit_exception = transient_commit; + store->drop_snapshot = NULL; store->fraction_full = transient_fraction_full; - store->snap = s; tc = kmalloc(sizeof(struct transient_c), GFP_KERNEL); if (!tc)