--- 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: + */ +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: - * 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);