Commit d91447ee authored by Milan Broz's avatar Milan Broz Committed by Greg Kroah-Hartman

dm snapshot: fix invalidation deadlock

patch fcac03ab in mainline

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: default avatarMilan Broz <mbroz@redhat.com>
Signed-off-by: default avatarAlasdair G Kergon <agk@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Cc: Chuck Ebbert <cebbert@redhat.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent e2b35e3a
...@@ -125,6 +125,8 @@ struct pstore { ...@@ -125,6 +125,8 @@ struct pstore {
uint32_t callback_count; uint32_t callback_count;
struct commit_callback *callbacks; struct commit_callback *callbacks;
struct dm_io_client *io_client; struct dm_io_client *io_client;
struct workqueue_struct *metadata_wq;
}; };
static inline unsigned int sectors_to_pages(unsigned int sectors) static inline unsigned int sectors_to_pages(unsigned int sectors)
...@@ -156,10 +158,24 @@ static void free_area(struct pstore *ps) ...@@ -156,10 +158,24 @@ static void free_area(struct pstore *ps)
ps->area = NULL; 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. * 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 = { struct io_region where = {
.bdev = ps->snap->cow->bdev, .bdev = ps->snap->cow->bdev,
...@@ -173,8 +189,23 @@ static int chunk_io(struct pstore *ps, uint32_t chunk, int rw) ...@@ -173,8 +189,23 @@ static int chunk_io(struct pstore *ps, uint32_t chunk, int rw)
.client = ps->io_client, .client = ps->io_client,
.notify.fn = NULL, .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) ...@@ -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 */ /* convert a metadata area index to a chunk index */
chunk = 1 + ((ps->exceptions_per_area + 1) * area); chunk = 1 + ((ps->exceptions_per_area + 1) * area);
r = chunk_io(ps, chunk, rw); r = chunk_io(ps, chunk, rw, 0);
if (r) if (r)
return r; return r;
...@@ -230,7 +261,7 @@ static int read_header(struct pstore *ps, int *new_snapshot) ...@@ -230,7 +261,7 @@ static int read_header(struct pstore *ps, int *new_snapshot)
if (r) if (r)
return r; return r;
r = chunk_io(ps, 0, READ); r = chunk_io(ps, 0, READ, 1);
if (r) if (r)
goto bad; goto bad;
...@@ -292,7 +323,7 @@ static int write_header(struct pstore *ps) ...@@ -292,7 +323,7 @@ static int write_header(struct pstore *ps)
dh->version = cpu_to_le32(ps->version); dh->version = cpu_to_le32(ps->version);
dh->chunk_size = cpu_to_le32(ps->snap->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);
} }
/* /*
...@@ -409,6 +440,7 @@ static void persistent_destroy(struct exception_store *store) ...@@ -409,6 +440,7 @@ static void persistent_destroy(struct exception_store *store)
{ {
struct pstore *ps = get_info(store); struct pstore *ps = get_info(store);
destroy_workqueue(ps->metadata_wq);
dm_io_client_destroy(ps->io_client); dm_io_client_destroy(ps->io_client);
vfree(ps->callbacks); vfree(ps->callbacks);
free_area(ps); free_area(ps);
...@@ -589,6 +621,12 @@ int dm_create_persistent(struct exception_store *store) ...@@ -589,6 +621,12 @@ int dm_create_persistent(struct exception_store *store)
atomic_set(&ps->pending_count, 0); atomic_set(&ps->pending_count, 0);
ps->callbacks = NULL; 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->destroy = persistent_destroy;
store->read_metadata = persistent_read_metadata; store->read_metadata = persistent_read_metadata;
store->prepare_exception = persistent_prepare; store->prepare_exception = persistent_prepare;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment