Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 1770

kernel-2.6.18-238.el5.src.rpm

From: Mikulas Patocka <mpatocka@redhat.com>
Date: Mon, 1 Sep 2008 18:54:32 -0400
Subject: [md] dm snapshot: use per device mempools
Message-id: Pine.LNX.4.64.0809011845570.12369@hs20-bc2-1.build.redhat.com
O-Subject: [RHEL 5.3] bz460846 Fix deadlock with nested LVMs in snapshot
Bugzilla: 460846
RH-Acked-by: Rik van Riel <riel@redhat.com>
RH-Acked-by: Alasdair G Kergon <agk@redhat.com>

When using nested LVMs (volume group built from physical devices that are
logical devices in another volume group), there is possibility of deadlock
in snapshot target because it uses shared mempool. If the upper-layer LVM
drains the mempool, the lower layer can deadlock unable to allocate any
memory. This patch changes it so that there is per-device mempool.

There were several bugs of this type (shared mempools) in the past, this
is the fix for the last known.

Upstream commit: 92e868122edf08b9fc06b112e7e0c80ab94c1f93 (2.6.27-rc1)

testing: compiled on a workstation and tested that snapshots work.

kABI considerations: the file drivers/md/dm-snap.h shouldn't be included
by any external modules, so the change is safe.

Mikulas

diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 1353540..52ea785 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -95,7 +95,6 @@ struct pending_exception {
  */
 static kmem_cache_t *exception_cache;
 static kmem_cache_t *pending_cache;
-static mempool_t *pending_pool;
 
 struct dm_snap_tracked_chunk {
 	struct hlist_node node;
@@ -362,14 +361,18 @@ static inline void free_exception(struct exception *e)
 	kmem_cache_free(exception_cache, e);
 }
 
-static inline struct pending_exception *alloc_pending_exception(void)
+static struct pending_exception *alloc_pending_exception(struct dm_snapshot *s)
 {
-	return mempool_alloc(pending_pool, GFP_NOIO);
+	struct pending_exception *pe = mempool_alloc(s->pending_pool, GFP_NOIO);
+
+	pe->snap = s;
+
+	return pe;
 }
 
-static inline void free_pending_exception(struct pending_exception *pe)
+static void free_pending_exception(struct pending_exception *pe)
 {
-	mempool_free(pe, pending_pool);
+	mempool_free(pe, pe->snap->pending_pool);
 }
 
 static void insert_completed_exception(struct dm_snapshot *s,
@@ -636,12 +639,18 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 		goto bad5;
 	}
 
+	s->pending_pool = mempool_create_slab_pool(MIN_IOS, pending_cache);
+	if (!s->pending_pool) {
+		ti->error = "Could not allocate mempool for pending exceptions";
+		goto bad6;
+	}
+
 	s->tracked_chunk_pool = mempool_create_slab_pool(MIN_IOS,
 							 tracked_chunk_cache);
 	if (!s->tracked_chunk_pool) {
 		ti->error = "Could not allocate tracked_chunk mempool for "
 			    "tracking reads";
-		goto bad6;
+		goto bad_tracked_chunk_pool;
 	}
 
 	for (i = 0; i < DM_TRACKED_CHUNK_HASH_SIZE; i++)
@@ -678,6 +687,9 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
  bad_load_and_register:
 	mempool_destroy(s->tracked_chunk_pool);
 
+ bad_tracked_chunk_pool:
+	mempool_destroy(s->pending_pool);
+
  bad6:
 	kcopyd_client_destroy(s->kcopyd_client);
 
@@ -727,6 +739,8 @@ static void snapshot_dtr(struct dm_target *ti)
 	/* Deallocate memory used */
 	s->store.destroy(&s->store);
 
+	mempool_destroy(s->pending_pool);
+
 	dm_put_device(ti, s->origin);
 	dm_put_device(ti, s->cow);
 
@@ -971,7 +985,7 @@ __find_pending_exception(struct dm_snapshot *s, struct bio *bio)
 	 * to hold the lock while we do this.
 	 */
 	up_write(&s->lock);
-	pe = alloc_pending_exception();
+	pe = alloc_pending_exception(s);
 	down_write(&s->lock);
 
 	if (!s->valid) {
@@ -991,7 +1005,6 @@ __find_pending_exception(struct dm_snapshot *s, struct bio *bio)
 	bio_list_init(&pe->snapshot_bios);
 	pe->primary_pe = NULL;
 	atomic_set(&pe->ref_count, 0);
-	pe->snap = s;
 	pe->started = 0;
 
 	if (s->store.prepare_exception(&s->store, &pe->e)) {
@@ -1431,24 +1444,15 @@ static int __init dm_snapshot_init(void)
 		goto bad5;
 	}
 
-	pending_pool = mempool_create_slab_pool(128, pending_cache);
-	if (!pending_pool) {
-		DMERR("Couldn't create pending pool.");
-		r = -ENOMEM;
-		goto bad_pending_pool;
-	}
-
 	ksnapd = create_singlethread_workqueue("ksnapd");
 	if (!ksnapd) {
 		DMERR("Failed to create ksnapd workqueue.");
 		r = -ENOMEM;
-		goto bad6;
+		goto bad_pending_pool;
 	}
 
 	return 0;
 
-      bad6:
-	mempool_destroy(pending_pool);
       bad_pending_pool:
 	kmem_cache_destroy(tracked_chunk_cache);
       bad5:
@@ -1479,7 +1483,6 @@ static void __exit dm_snapshot_exit(void)
 		DMERR("origin unregister failed %d", r);
 
 	exit_origin_hash();
-	mempool_destroy(pending_pool);
 	kmem_cache_destroy(pending_cache);
 	kmem_cache_destroy(exception_cache);
 	kmem_cache_destroy(tracked_chunk_cache);
diff --git a/drivers/md/dm-snap.h b/drivers/md/dm-snap.h
index 1cb1b2a..c16a111 100644
--- a/drivers/md/dm-snap.h
+++ b/drivers/md/dm-snap.h
@@ -164,6 +164,8 @@ struct dm_snapshot {
 	/* The last percentage we notified */
 	int last_percent;
 
+	mempool_t *pending_pool;
+
 	struct exception_table pending;
 	struct exception_table complete;