Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fcac03ab authored by Milan Broz's avatar Milan Broz Committed by Linus Torvalds
Browse files

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: 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>
parent 596f138e
Loading
Loading
Loading
Loading
+43 −5
Original line number Original line Diff line number Diff line
@@ -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 unsigned sectors_to_pages(unsigned sectors)
static unsigned sectors_to_pages(unsigned sectors)
@@ -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)
		.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)
	/* 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)
	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)
	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)
{
{
	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);
@@ -588,6 +620,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;