--- diff/drivers/md/dm-snapshot.c	2003-07-04 15:38:21.000000000 +0100
+++ source/drivers/md/dm-snapshot.c	2003-07-03 15:27:39.000000000 +0100
@@ -81,131 +81,124 @@
 static kmem_cache_t *pending_cache;
 static mempool_t *pending_pool;
 
-/*
- * One of these per registered origin, held in the snapshot_origins hash
- */
+/*-----------------------------------------------------------------
+ * A hash table for looking up the origin objects.
+ *---------------------------------------------------------------*/
 struct origin {
-	/* The origin device */
 	kdev_t dev;
+	unsigned count;
+	struct rw_semaphore lock;
 
+	struct dm_dev *dev;???
 	struct list_head hash_list;
-
-	/* List of snapshots for this origin */
 	struct list_head snapshots;
 };
 
-/*
- * Size of the hash table for origin volumes. If we make this
- * the size of the minors list then it should be nearly perfect
- */
-#define ORIGIN_HASH_SIZE 256
-#define ORIGIN_MASK      0xFF
-static struct list_head *_origins;
-static struct rw_semaphore _origins_lock;
+#define ORIGIN_HASH_SIZE 64
+#define ORIGIN_MASK (ORIGIN_HASH_SIZE - 1)
+static struct list_head *_origins[ORIGIN_HASH_SIZE];
+static spinlock_t _origin_lock = SPIN_LOCK_UNLOCKED;
 
 static int init_origin_hash(void)
 {
 	int i;
 
-	_origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head),
-			   GFP_KERNEL);
-	if (!_origins) {
-		DMERR("Device mapper: Snapshot: unable to allocate memory");
-		return -ENOMEM;
-	}
-
 	for (i = 0; i < ORIGIN_HASH_SIZE; i++)
 		INIT_LIST_HEAD(_origins + i);
-	init_rwsem(&_origins_lock);
 
 	return 0;
 }
 
-static void exit_origin_hash(void)
-{
-	kfree(_origins);
-}
-
 static inline unsigned int origin_hash(kdev_t dev)
 {
 	return MINOR(dev) & ORIGIN_MASK;
 }
 
-static struct origin *__lookup_origin(kdev_t origin)
+static struct origin *__lookup(kdev_t origin)
 {
-	struct list_head *slist;
 	struct list_head *ol;
 	struct origin *o;
 
-	ol = &_origins[origin_hash(origin)];
-	list_for_each(slist, ol) {
-		o = list_entry(slist, struct origin, hash_list);
-
+	ol = _origins + origin_hash(origin);
+	list_for_each_entry(o, ol, hash_list)
 		if (o->dev == origin)
-			return o;
-	}
+			break;
 
-	return NULL;
-}
-
-static void __insert_origin(struct origin *o)
-{
-	struct list_head *sl = &_origins[origin_hash(o->dev)];
-	list_add_tail(&o->hash_list, sl);
+	return o;
 }
 
 /*
- * Make a note of the snapshot and its origin so we can look it
- * up when the origin has a write on it.
+ * This will allocate the origin object if it doesn't already
+ * exist.
  */
-static int register_snapshot(struct dm_snapshot *snap)
+static struct origin *get_origin(kdev_t origin)
 {
-	struct origin *o;
-	kdev_t dev = snap->origin->dev;
-
-	down_write(&_origins_lock);
-	o = __lookup_origin(dev);
+	struct origin *o, *o2;
 
+	spin_lock(&_origin_lock);
+	o = __lookup(origin);
 	if (!o) {
-		/* New origin */
+		spin_unlock(&_origin_lock);
+
 		o = kmalloc(sizeof(*o), GFP_KERNEL);
-		if (!o) {
-			up_write(&_origins_lock);
-			return -ENOMEM;
-		}
+		if (!o)
+			return NULL;
 
 		/* Initialise the struct */
+		o->count = 0;
+		init_rwsem(&o->lock);
+		o->dev = origin;
 		INIT_LIST_HEAD(&o->snapshots);
-		o->dev = dev;
 
-		__insert_origin(o);
+		spin_lock(&_origin_lock);
+		o2 = __lookup(origin);
+		if (!o2)
+			list_add(&o->hash_list,
+				 _origins + origin_hash(o->dev));
+
+		else {
+			kfree(o);
+			o = o2;
+		}
 	}
 
-	list_add_tail(&snap->list, &o->snapshots);
+	o->count++;
+	spin_unlock(&_origin_lock);
 
-	up_write(&_origins_lock);
-	return 0;
+	return o;
 }
 
-static void unregister_snapshot(struct dm_snapshot *s)
+static void put_origin(struct origin *o)
 {
-	struct origin *o;
+	spin_lock(&_origin_lock);
+	if (!--o->count)
+		list_del(&o->hash_list);
+	spin_unlock(&_origin_lock);
 
-	down_write(&_origins_lock);
-	o = __lookup_origin(s->origin->dev);
+	kfree(o);
+}
 
-	list_del(&s->list);
-	if (list_empty(&o->snapshots)) {
-		list_del(&o->hash_list);
-		kfree(o);
-	}
+static int attach_snapshot(struct dm_snapshot *s)
+{
+	struct origin *o;
+	kdev_t dev = s->origin->dev;
 
-	up_write(&_origins_lock);
+	o = get_origin(dev);
+	list_add_tail(&snap->list, &o->snapshots);
+	return 0;
 }
 
-/*
+static void detach_snapshot(struct dm_snapshot *s)
+{
+	/* FIXME: why not just store the origin in the snapshot struct ? */
+	struct origin *o = get_origin(s->origin->dev);
+	put_origin(o);
+	put_origin(o);
+}
+
+/*-----------------------------------------------------------------
  * Implementation of the exception hash tables.
- */
+ *---------------------------------------------------------------*/
 static int init_exception_table(struct exception_table *et, __u32 size)
 {
 	unsigned int i;
@@ -528,9 +521,9 @@
 	fsync_dev_lockfs(s->origin->dev);
 
 	/* Add snapshot to the list of snapshots for this origin */
-	if (register_snapshot(s)) {
+	if (attach_snapshot(s)) {
 		r = -EINVAL;
-		ti->error = "Cannot register snapshot origin";
+		ti->error = "Cannot attach snapshot to origin";
 		goto bad6;
 	}
 	unlockfs(s->origin->dev);
@@ -566,7 +559,7 @@
 
 	dm_table_event(ti->table);
 
-	unregister_snapshot(s);
+	detach_snapshot(s);
 
 	exit_exception_table(&s->pending, pending_cache);
 	exit_exception_table(&s->complete, exception_cache);
@@ -927,6 +920,45 @@
 /*-----------------------------------------------------------------
  * Origin methods
  *---------------------------------------------------------------*/
+
+/*
+ * Construct an origin mapping: <dev_path>
+ */
+static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	int r;
+	struct origin *o;
+	struct dm_dev *dev;
+
+	if (argc != 1) {
+		ti->error = "dm-origin: incorrect number of arguments";
+		return -EINVAL;
+	}
+
+	r = dm_get_device(ti, argv[0], 0, ti->len,
+			  dm_table_get_mode(ti->table), &dev);
+	if (r) {
+		ti->error = "Cannot get target device";
+		return r;
+	}
+
+	o = get_origin(dev);
+	if (!o) {
+		ti->error = "Couldn't allocate origin object";
+		dm_put_device(ti, dev);
+		return -ENOMEM;
+	}
+
+	ti->private = o;
+	return 0;
+}
+
+static void origin_dtr(struct dm_target *ti)
+{
+	struct origin *o = (struct origin *) ti->private;
+	put_origin(o);
+}
+
 static void list_merge(struct list_head *l1, struct list_head *l2)
 {
 	struct list_head *l1_n, *l2_p;
@@ -1033,46 +1065,38 @@
  * Origin: maps a linear range of a device, with hooks for snapshotting.
  */
 
-/*
- * Construct an origin mapping: <dev_path>
- * The context for an origin is merely a 'struct dm_dev *'
- * pointing to the real device.
- */
-static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+static int origin_map(struct dm_target *ti, struct buffer_head *bh, int rw,
+		      union map_info *map_context)
 {
-	int r;
-	struct dm_dev *dev;
-
-	if (argc != 1) {
-		ti->error = "dm-origin: incorrect number of arguments";
-		return -EINVAL;
-	}
-
-	r = dm_get_device(ti, argv[0], 0, ti->len,
-			  dm_table_get_mode(ti->table), &dev);
-	if (r) {
-		ti->error = "Cannot get target device";
-		return r;
-	}
+	struct dm_dev *dev = (struct dm_dev *) ti->private;
+	bh->b_rdev = dev->dev;
 
-	ti->private = dev;
-	return 0;
+	/* Only tell snapshots if this is a write */
+	return (rw == WRITE) ? do_origin(dev, bh) : 1;
 }
 
-static void origin_dtr(struct dm_target *ti)
+void origin_suspend(struct dm_target *ti)
 {
-	struct dm_dev *dev = (struct dm_dev *) ti->private;
-	dm_put_device(ti, dev);
+	struct snapshot *s;
+	struct origin *o = (struct origin *) ti->private;
+
+	/*
+	 * Drop the reference to all our snapshots, so that they
+	 * may be removed.
+	 */
+	list_for_each_entry (s, o->snapshots, list) {
+		dm_put_device(s->dev);
+	}
 }
 
-static int origin_map(struct dm_target *ti, struct buffer_head *bh, int rw,
-		      union map_info *map_context)
+void origin_resume(struct dm_target *ti)
 {
-	struct dm_dev *dev = (struct dm_dev *) ti->private;
-	bh->b_rdev = dev->dev;
+	struct origin *o = (struct origin *) ti->private;
+
+	/*
+	 * Grab the references to any snapshots.
+	 */
 
-	/* Only tell snapshots if this is a write */
-	return (rw == WRITE) ? do_origin(dev, bh) : 1;
 }
 
 static int origin_status(struct dm_target *ti, status_type_t type, char *result,
@@ -1141,7 +1165,7 @@
 	if (!exception_cache) {
 		DMERR("Couldn't create exception cache.");
 		r = -ENOMEM;
-		goto bad3;
+		goto bad2;
 	}
 
 	pending_cache =
@@ -1152,7 +1176,7 @@
 	if (!pending_cache) {
 		DMERR("Couldn't create pending cache.");
 		r = -ENOMEM;
-		goto bad4;
+		goto bad3;
 	}
 
 	pending_pool = mempool_create(128, mempool_alloc_slab,
@@ -1160,17 +1184,15 @@
 	if (!pending_pool) {
 		DMERR("Couldn't create pending pool.");
 		r = -ENOMEM;
-		goto bad5;
+		goto bad4;
 	}
 
 	return 0;
 
-      bad5:
-	kmem_cache_destroy(pending_cache);
       bad4:
-	kmem_cache_destroy(exception_cache);
+	kmem_cache_destroy(pending_cache);
       bad3:
-	exit_origin_hash();
+	kmem_cache_destroy(exception_cache);
       bad2:
 	dm_unregister_target(&origin_target);
       bad1:
@@ -1190,7 +1212,6 @@
 	if (r)
 		DMERR("origin unregister failed %d", r);
 
-	exit_origin_hash();
 	mempool_destroy(pending_pool);
 	kmem_cache_destroy(pending_cache);
 	kmem_cache_destroy(exception_cache);