diff options
author | Milan Broz <mbroz@redhat.com> | 2007-07-12 17:28:00 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-12 15:01:08 -0700 |
commit | fcac03abd325e4f7a4cc8fe05fea2793b1c8eb75 (patch) | |
tree | 79965ed6b2b03468e629a63fca1c14f795091436 /drivers/md/dm-exception-store.c | |
parent | 596f138eede0c113aa655937c8be85fc15ccd61c (diff) |
dm snapshot: fix invalidation deadlock
Process persistent exception store metadata IOs in a separate thread.
A snapshot may become invalid while inside generic_make_request().
A synchronous write is then needed to update the metadata while still
inside that function. Since the introduction of
md-dm-reduce-stack-usage-with-stacked-block-devices.patch this has to
be performed by a separate thread to avoid deadlock.
Signed-off-by: Milan Broz <mbroz@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/md/dm-exception-store.c')
-rw-r--r-- | drivers/md/dm-exception-store.c | 48 |
1 files changed, 43 insertions, 5 deletions
diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c index cb05b744dea..8c25c2ff724 100644 --- a/drivers/md/dm-exception-store.c +++ b/drivers/md/dm-exception-store.c @@ -125,6 +125,8 @@ struct pstore { uint32_t callback_count; struct commit_callback *callbacks; struct dm_io_client *io_client; + + struct workqueue_struct *metadata_wq; }; static unsigned sectors_to_pages(unsigned sectors) @@ -156,10 +158,24 @@ static void free_area(struct pstore *ps) ps->area = NULL; } +struct mdata_req { + struct 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 = { .bdev = ps->snap->cow->bdev, @@ -173,8 +189,23 @@ static int chunk_io(struct pstore *ps, uint32_t chunk, int rw) .client = ps->io_client, .notify.fn = NULL, }; + struct mdata_req req; + + if (!metadata) + return dm_io(&io_req, 1, &where, NULL); - return dm_io(&io_req, 1, &where, NULL); + req.where = &where; + req.io_req = &io_req; + + /* + * 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 req.result; } /* @@ -189,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; @@ -230,7 +261,7 @@ static int read_header(struct pstore *ps, int *new_snapshot) if (r) return r; - r = chunk_io(ps, 0, READ); + r = chunk_io(ps, 0, READ, 1); if (r) goto bad; @@ -292,7 +323,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); } /* @@ -409,6 +440,7 @@ static void persistent_destroy(struct exception_store *store) { struct pstore *ps = get_info(store); + destroy_workqueue(ps->metadata_wq); dm_io_client_destroy(ps->io_client); vfree(ps->callbacks); free_area(ps); @@ -588,6 +620,12 @@ 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) { + 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; |