dm snapshot: fix invalidation deadlock
authorMilan Broz <mbroz@redhat.com>
Thu, 12 Jul 2007 16:28:00 +0000 (17:28 +0100)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Thu, 12 Jul 2007 22:01:08 +0000 (15:01 -0700)
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>
drivers/md/dm-exception-store.c

index cb05b744deaa3395199f09d10997265bd6b205e1..8c25c2ff724a9f3366fa00d4ad0fe2eb781d6fbd 100644 (file)
@@ -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;