X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fmd%2Fdm-exception-store.c;h=769ab677f8e05a4b3887093be5e98e0cf9cd4125;hb=0f32dc9dc2cfe3e4608fd5cf7afa5d2acb6e3c10;hp=99cdffa7fbfe0a4522330099dd80ca810f3dc796;hpb=4a61f17378c2cdd9bd8f34ef8bd7422861d0c1f1;p=linux-2.6-omap-h63xx.git diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c index 99cdffa7fbf..769ab677f8e 100644 --- a/drivers/md/dm-exception-store.c +++ b/drivers/md/dm-exception-store.c @@ -1,20 +1,21 @@ /* - * 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 */ @@ -107,12 +108,12 @@ struct pstore { * Used to keep track of which metadata area the data in * 'chunk' refers to. */ - uint32_t current_area; + chunk_t current_area; /* * The next free chunk for an exception. */ - uint32_t next_free; + chunk_t next_free; /* * The index of next free exception in the current @@ -123,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) @@ -154,34 +158,76 @@ static void free_area(struct pstore *ps) 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, chunk_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->snap->chunk_size * chunk; - where.count = ps->snap->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; +} + +/* + * Convert a metadata area index to a chunk index. + */ +static chunk_t area_location(struct pstore *ps, chunk_t area) +{ + return 1 + ((ps->exceptions_per_area + 1) * area); } /* * 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) +static int area_io(struct pstore *ps, chunk_t area, int rw) { int r; - uint32_t chunk; + chunk_t chunk; - /* convert a metadata area index to a chunk index */ - chunk = 1 + ((ps->exceptions_per_area + 1) * area); + chunk = area_location(ps, area); - r = chunk_io(ps, chunk, rw); + r = chunk_io(ps, chunk, rw, 0); if (r) return r; @@ -189,7 +235,7 @@ static int area_io(struct pstore *ps, uint32_t area, int rw) return 0; } -static int zero_area(struct pstore *ps, uint32_t area) +static int zero_area(struct pstore *ps, chunk_t area) { memset(ps->area, 0, ps->snap->chunk_size << SECTOR_SHIFT); return area_io(ps, area, WRITE); @@ -213,17 +259,18 @@ static int read_header(struct pstore *ps, int *new_snapshot) chunk_size_supplied = 0; } - r = dm_io_get(sectors_to_pages(ps->snap->chunk_size)); - if (r) - return r; + 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) - goto bad1; + return r; - r = chunk_io(ps, 0, READ); + r = chunk_io(ps, 0, READ, 1); if (r) - goto bad2; + goto bad; dh = (struct disk_header *) ps->area; @@ -235,7 +282,7 @@ static int read_header(struct pstore *ps, int *new_snapshot) if (le32_to_cpu(dh->magic) != SNAP_MAGIC) { DMWARN("Invalid or corrupt snapshot"); r = -ENXIO; - goto bad2; + goto bad; } *new_snapshot = 0; @@ -252,27 +299,22 @@ static int read_header(struct pstore *ps, int *new_snapshot) (unsigned long long)ps->snap->chunk_size); /* We had a bogus chunk_size. Fix stuff up. */ - dm_io_put(sectors_to_pages(ps->snap->chunk_size)); 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_get(sectors_to_pages(chunk_size)); + r = dm_io_client_resize(sectors_to_pages(ps->snap->chunk_size), + ps->io_client); if (r) return r; r = alloc_area(ps); - if (r) - goto bad1; - - return 0; + return r; -bad2: +bad: free_area(ps); -bad1: - dm_io_put(sectors_to_pages(ps->snap->chunk_size)); return r; } @@ -288,7 +330,7 @@ static int write_header(struct pstore *ps) dh->version = cpu_to_le32(ps->version); dh->chunk_size = cpu_to_le32(ps->snap->chunk_size); - return chunk_io(ps, 0, WRITE); + return chunk_io(ps, 0, WRITE, 1); } /* @@ -369,7 +411,7 @@ static int insert_exceptions(struct pstore *ps, int *full) static int read_exceptions(struct pstore *ps) { - uint32_t area; + chunk_t area; int r, full = 1; /* @@ -389,7 +431,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; } @@ -405,7 +447,8 @@ static void persistent_destroy(struct exception_store *store) { struct pstore *ps = get_info(store); - dm_io_put(sectors_to_pages(ps->snap->chunk_size)); + destroy_workqueue(ps->metadata_wq); + dm_io_client_destroy(ps->io_client); vfree(ps->callbacks); free_area(ps); kfree(ps); @@ -413,7 +456,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); /* @@ -453,17 +496,18 @@ 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); return -EINVAL; } + /* + * Metadata are valid, but snapshot is invalidated + */ + if (!ps->valid) + return 1; + /* * Read the metadata. */ @@ -476,10 +520,11 @@ 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; + chunk_t next_free; sector_t size = get_dev_size(store->snap->cow->bdev); /* Is there enough room ? */ @@ -493,7 +538,8 @@ static int persistent_prepare(struct exception_store *store, * into account the location of the metadata chunks. */ stride = (ps->exceptions_per_area + 1); - if ((++ps->next_free % stride) == 1) + next_free = ++ps->next_free; + if (sector_div(next_free, stride) == 1) ps->next_free++; atomic_inc(&ps->pending_count); @@ -501,7 +547,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) { @@ -584,6 +630,13 @@ int dm_create_persistent(struct exception_store *store) atomic_set(&ps->pending_count, 0); ps->callbacks = NULL; + 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; store->read_metadata = persistent_read_metadata; store->prepare_exception = persistent_prepare; @@ -612,7 +665,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); @@ -627,9 +681,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);