This patch finishes the integration of the snapshare structure. It
removes the exception store from the snapshot struct and relies
on the snapshare and creates the pending exception cache for the
individual snapshots (located in the snapshare structure).
Signed-off-by: Jonathan Brassow <jbrassow@redhat.com>
+/*
+ * Exception table hash sizes for pending exceptions
+ * The snapshot pending exception table holds pending exceptions
+ * that affect all snapshots in the share group (due to origin write).
+ * The snapshare pending exception table holds pending exceptions
+ * that affect just one snapshot in the share group (due to a
+ * write to one of the snapshots).
+ */
+#define DM_SNAPSHARE_HASH_SIZE 16
+#define DM_SNAPSHOT_HASH_SIZE 64
+
struct dm_snapshot {
struct rw_semaphore lock;
+ uint64_t shared_uuid;
+ struct list_head shared_list;
+
/*
* pe_lock protects all pending_exception operations and access
* as well as the snapshot_bios list.
*/
spinlock_t pe_lock;
- /* The on disk metadata handler */
- struct dm_exception_store *store;
-
struct dm_kcopyd_client *kcopyd_client;
/* Queue of snapshot writes for ksnapd to flush */
@@ -109,6 +118,19 @@ struct dm_snapshare {
static struct workqueue_struct *ksnapd;
static void flush_queued_bios(struct work_struct *work);
- /* Pointer back to snapshot context */
+ /*
+ * Pointer back to snapshot or snapshare context
+ * Only one of 'ss' or 'snap' may be populated.
+ */
struct dm_snapshot *snap;
+ struct dm_snapshare *ss;
/*
* 1 indicates the exception has already been sent to
@@ -299,13 +325,21 @@ static void __insert_origin(struct origi
}
/*
+ * register_snapshare
+ * @ss: snapshare - initialized and populated with 's'
+ *
* Make a note of the snapshot and its origin so we can look it
* up when the origin has a write on it.
+ *
+ * Returns: 0 on success, -Exxx on failure
*/
-static int register_snapshot(struct dm_snapshot *snap)
+static void dealloc_snapshot(struct dm_snapshot *s);
+static int register_snapshare(struct dm_snapshare *ss)
{
+ int found = 0;
struct origin *o, *new_o;
- struct block_device *bdev = snap->origin->bdev;
+ struct dm_snapshot *s;
+ struct block_device *bdev = ss->origin->bdev;
new_o = kmalloc(sizeof(*new_o), GFP_KERNEL);
if (!new_o)
@@ -327,20 +361,56 @@ static int register_snapshot(struct dm_s
__insert_origin(o);
}
+ /*
+ * Always origin lock, then snapshot lock
+ */
down_write(&_origins_lock);
- o = __lookup_origin(s->origin->bdev);
+ o = __lookup_origin(ss->origin->bdev);
+
+ down_write(&ss->snap->lock);
+
+ /*
+ * Remove the snapshare, then if there are no
+ * more snapshares left, remove the snapshot
+ * from the origin's list
+ */
+ list_del(&ss->shared_list);
+
+ if (list_empty(&ss->snap->shared_list))
+ list_del(&ss->snap->list);
+ up_write(&ss->snap->lock);
- r = dm_get_device(ti, origin_path, 0, ti->len, FMODE_READ, &origin);
+ r = dm_get_device(ti, origin_path, 0, ti->len, FMODE_READ, &ss->origin);
if (r) {
ti->error = "Cannot get origin device";
goto bad_origin;
}
/*
- * Calculate based on the size of the original volume or
- * the COW volume...
- */
- cow_dev_size = get_dev_size(store->cow->bdev);
- origin_dev_size = get_dev_size(origin->bdev);
- max_buckets = calc_max_buckets();
-
- hash_size = min(origin_dev_size, cow_dev_size) >> store->chunk_shift;
- hash_size = min(hash_size, max_buckets);
-
- hash_size = rounddown_pow_of_two(hash_size);
- hash_size >>= 3;
- if (hash_size < 64)
- hash_size = 64;
-
- /*
* Allocate the snapshot
*/
- s = alloc_snapshot(hash_size);
+ s = alloc_snapshot();
if (!s) {
r = -ENOMEM;
ti->error = "Failed to create snapshot structure";
goto bad_alloc_snapshot;
}
ss->snap = s;
- s->origin = origin;
- s->store = ss->store;
+ s->o_bdev = ss->origin->bdev;
+ s->shared_uuid = store->shared_uuid;
+ list_add(&ss->shared_list, &s->shared_list);
/* Add snapshot to the list of snapshots for this origin */
/* Exceptions aren't triggered till snapshot_resume() is called */
- if (register_snapshot(s)) {
+ if (register_snapshare(ss)) {
r = -EINVAL;
ti->error = "Cannot register snapshot with origin";
goto bad_load_and_register;
@@ -631,12 +713,15 @@ bad_load_and_register:
dealloc_snapshot(s);
/* Prevent further origin writes from using this snapshot. */
/* After this returns there can be no new kcopyd jobs. */
- unregister_snapshot(s);
+ unregister_snapshare(ss);
- while (atomic_read(&s->pending_exceptions_count))
+ while (atomic_read(&ss->pending_exceptions_count))
msleep(1);
/*
* Ensure instructions in mempool_destroy aren't reordered
@@ -664,17 +749,20 @@ static void snapshot_dtr(struct dm_targe
*/
smp_mb();
+ if (list_empty(&s->shared_list)) {
#ifdef CONFIG_DM_DEBUG
- for (i = 0; i < DM_TRACKED_CHUNK_HASH_SIZE; i++)
- BUG_ON(!hlist_empty(&s->tracked_chunk_hash[i]));
+ for (i = 0; i < DM_TRACKED_CHUNK_HASH_SIZE; i++)
+ BUG_ON(!hlist_empty(&s->tracked_chunk_hash[i]));
#endif
+ dealloc_snapshot(s);
+ }
/* Hand over to kcopyd */
@@ -876,14 +977,17 @@ static struct dm_snap_pending_exception
__find_pending_exception(struct dm_snapshot *s, struct bio *bio,
struct dm_snapshare *ss)
{
+ int r;
struct dm_exception *e, *tmp_e;
struct dm_snap_pending_exception *pe;
- chunk_t chunk = sector_to_chunk(s->store, bio->bi_sector);
+ struct dm_exception_store *store = ss ? ss->store : get_first_store(s);
+ struct dm_exception_table *table = ss ? ss->pending : s->pending;
+ chunk_t chunk = sector_to_chunk(store, bio->bi_sector);
/*
* Is there a pending exception for this already ?
*/
- e = dm_lookup_exception(s->pending, chunk);
+ e = dm_lookup_exception(table, chunk);
if (e) {
/* cast the exception to a pending exception */
pe = container_of(e, struct dm_snap_pending_exception, e);
@@ -895,18 +999,18 @@ __find_pending_exception(struct dm_snaps
* to hold the lock while we do this.
*/
up_write(&s->lock);
- tmp_e = dm_alloc_exception(s->pending);
+ tmp_e = dm_alloc_exception(table);
pe = container_of(tmp_e, struct dm_snap_pending_exception, e);
down_write(&s->lock);
@@ -1097,7 +1203,7 @@ static int snapshot_status(struct dm_tar
* to make private copies if the output is to
* make sense.
*/
- DMEMIT("%s ", s->origin->name);
+ DMEMIT("%s ", ss->origin->name);
ss->store->type->status(ss->store, type,
result+sz, maxlen-sz);
break;
@@ -1124,6 +1230,7 @@ static int __origin_write(struct list_he
{
int rtn, r = DM_MAPIO_REMAPPED, first = 0;
struct dm_snapshot *snap;
+ struct dm_exception_store *store;
struct dm_snap_pending_exception *pe, *next_pe, *primary_pe = NULL;
chunk_t chunk;
LIST_HEAD(pe_queue);
@@ -1137,26 +1244,26 @@ static int __origin_write(struct list_he
if (!snap->valid || !snap->active)
goto next_snapshot;
+ store = get_first_store(snap);
+
/* Nothing to do if writing beyond end of snapshot */
- if (bio->bi_sector >= dm_table_get_size(snap->store->ti->table))
+ if (bio->bi_sector >= dm_table_get_size(store->ti->table))
goto next_snapshot;
/*
* Remember, different snapshots can have
* different chunk sizes.
*/
- chunk = sector_to_chunk(snap->store, bio->bi_sector);
+ chunk = sector_to_chunk(store, bio->bi_sector);
/*
- * Check exception table to see if block
- * is already remapped in this snapshot
- * and trigger an exception if not.
+ * Check exception table to see if block is already
+ * remapped in this snapshot and trigger an exception if not.
*
* ref_count is initialised to 1 so pending_complete()
* won't destroy the primary_pe while we're inside this loop.
*/
- rtn = snap->store->type->lookup_exception(snap->store, chunk,
- NULL, 1, 0);
+ rtn = store->type->lookup_exception(store, chunk, NULL, 1, 0);
if (!rtn)
goto next_snapshot;
--
dm-devel mailing list
dm-devel@redhat.com
https://www.redhat.com/mailman/listinfo/dm-devel
03-25-2009, 08:39 PM
Jonathan Brassow
DM Snapshot: utilize snapshare
Patch name: dm-snap-utilize-snapshare.patch
This patch finishes the integration of the snapshare structure. It
removes the exception store from the snapshot struct and relies
on the snapshare and creates the pending exception cache for the
individual snapshots (located in the snapshare structure).
Signed-off-by: Jonathan Brassow <jbrassow@redhat.com>
+/*
+ * Exception table hash sizes for pending exceptions
+ * The snapshot pending exception table holds pending exceptions
+ * that affect all snapshots in the share group (due to origin write).
+ * The snapshare pending exception table holds pending exceptions
+ * that affect just one snapshot in the share group (due to a
+ * write to one of the snapshots).
+ */
+#define DM_SNAPSHARE_HASH_SIZE 16
+#define DM_SNAPSHOT_HASH_SIZE 64
+
struct dm_snapshot {
struct rw_semaphore lock;
+ uint64_t shared_uuid;
+ struct list_head shared_list;
+
/*
* pe_lock protects all pending_exception operations and access
* as well as the snapshot_bios list.
*/
spinlock_t pe_lock;
- /* The on disk metadata handler */
- struct dm_exception_store *store;
-
struct dm_kcopyd_client *kcopyd_client;
/* Queue of snapshot writes for ksnapd to flush */
@@ -109,6 +118,19 @@ struct dm_snapshare {
static struct workqueue_struct *ksnapd;
static void flush_queued_bios(struct work_struct *work);
- /* Pointer back to snapshot context */
+ /*
+ * Pointer back to snapshot or snapshare context
+ * Only one of 'ss' or 'snap' may be populated.
+ */
struct dm_snapshot *snap;
+ struct dm_snapshare *ss;
/*
* 1 indicates the exception has already been sent to
@@ -299,13 +325,21 @@ static void __insert_origin(struct origi
}
/*
+ * register_snapshare
+ * @ss: snapshare - initialized and populated with 's'
+ *
* Make a note of the snapshot and its origin so we can look it
* up when the origin has a write on it.
+ *
+ * Returns: 0 on success, -Exxx on failure
*/
-static int register_snapshot(struct dm_snapshot *snap)
+static void dealloc_snapshot(struct dm_snapshot *s);
+static int register_snapshare(struct dm_snapshare *ss)
{
+ int found = 0;
struct origin *o, *new_o;
- struct block_device *bdev = snap->origin->bdev;
+ struct dm_snapshot *s;
+ struct block_device *bdev = ss->origin->bdev;
new_o = kmalloc(sizeof(*new_o), GFP_KERNEL);
if (!new_o)
@@ -327,20 +361,56 @@ static int register_snapshot(struct dm_s
__insert_origin(o);
}
+ /*
+ * Always origin lock, then snapshot lock
+ */
down_write(&_origins_lock);
- o = __lookup_origin(s->origin->bdev);
+ o = __lookup_origin(ss->origin->bdev);
+
+ down_write(&ss->snap->lock);
+
+ /*
+ * Remove the snapshare, then if there are no
+ * more snapshares left, remove the snapshot
+ * from the origin's list
+ */
+ list_del(&ss->shared_list);
+
+ if (list_empty(&ss->snap->shared_list))
+ list_del(&ss->snap->list);
+ up_write(&ss->snap->lock);
- r = dm_get_device(ti, origin_path, 0, ti->len, FMODE_READ, &origin);
+ r = dm_get_device(ti, origin_path, 0, ti->len, FMODE_READ, &ss->origin);
if (r) {
ti->error = "Cannot get origin device";
goto bad_origin;
}
/*
- * Calculate based on the size of the original volume or
- * the COW volume...
- */
- cow_dev_size = get_dev_size(store->cow->bdev);
- origin_dev_size = get_dev_size(origin->bdev);
- max_buckets = calc_max_buckets();
-
- hash_size = min(origin_dev_size, cow_dev_size) >> store->chunk_shift;
- hash_size = min(hash_size, max_buckets);
-
- hash_size = rounddown_pow_of_two(hash_size);
- hash_size >>= 3;
- if (hash_size < 64)
- hash_size = 64;
-
- /*
* Allocate the snapshot
*/
- s = alloc_snapshot(hash_size);
+ s = alloc_snapshot();
if (!s) {
r = -ENOMEM;
ti->error = "Failed to create snapshot structure";
goto bad_alloc_snapshot;
}
ss->snap = s;
- s->origin = origin;
- s->store = ss->store;
+ s->o_bdev = ss->origin->bdev;
+ s->shared_uuid = store->shared_uuid;
+ list_add(&ss->shared_list, &s->shared_list);
/* Add snapshot to the list of snapshots for this origin */
/* Exceptions aren't triggered till snapshot_resume() is called */
- if (register_snapshot(s)) {
+ if (register_snapshare(ss)) {
r = -EINVAL;
ti->error = "Cannot register snapshot with origin";
goto bad_load_and_register;
@@ -631,12 +713,15 @@ bad_load_and_register:
dealloc_snapshot(s);
/* Prevent further origin writes from using this snapshot. */
/* After this returns there can be no new kcopyd jobs. */
- unregister_snapshot(s);
+ unregister_snapshare(ss);
- while (atomic_read(&s->pending_exceptions_count))
+ while (atomic_read(&ss->pending_exceptions_count))
msleep(1);
/*
* Ensure instructions in mempool_destroy aren't reordered
@@ -664,17 +749,20 @@ static void snapshot_dtr(struct dm_targe
*/
smp_mb();
+ if (list_empty(&s->shared_list)) {
#ifdef CONFIG_DM_DEBUG
- for (i = 0; i < DM_TRACKED_CHUNK_HASH_SIZE; i++)
- BUG_ON(!hlist_empty(&s->tracked_chunk_hash[i]));
+ for (i = 0; i < DM_TRACKED_CHUNK_HASH_SIZE; i++)
+ BUG_ON(!hlist_empty(&s->tracked_chunk_hash[i]));
#endif
+ dealloc_snapshot(s);
+ }
@@ -1112,7 +1229,7 @@ static int snapshot_status(struct dm_tar
* to make private copies if the output is to
* make sense.
*/
- DMEMIT("%s", s->origin->name);
+ DMEMIT("%s", ss->origin->name);
ss->store->type->status(ss->store, type, result + sz,
maxlen - sz);
break;
@@ -1140,6 +1257,7 @@ static int __origin_write(struct list_he
int rtn, r = DM_MAPIO_REMAPPED, first = 0;
struct dm_snapshot *snap;
struct dm_exception *tmp_e;
+ struct dm_exception_store *store;
struct dm_snap_pending_exception *pe, *next_pe, *primary_pe = NULL;
chunk_t chunk;
LIST_HEAD(pe_queue);
@@ -1153,26 +1271,26 @@ static int __origin_write(struct list_he
if (!snap->valid || !snap->active)
goto next_snapshot;
+ store = get_first_store(snap);
+
/* Nothing to do if writing beyond end of snapshot */
- if (bio->bi_sector >= dm_table_get_size(snap->store->ti->table))
+ if (bio->bi_sector >= dm_table_get_size(store->ti->table))
goto next_snapshot;
/*
* Remember, different snapshots can have
* different chunk sizes.
*/
- chunk = sector_to_chunk(snap->store, bio->bi_sector);
+ chunk = sector_to_chunk(store, bio->bi_sector);
/*
- * Check exception table to see if block
- * is already remapped in this snapshot
- * and trigger an exception if not.
+ * Check exception table to see if block is already
+ * remapped in this snapshot and trigger an exception if not.
*
* ref_count is initialised to 1 so pending_complete()
* won't destroy the primary_pe while we're inside this loop.
*/
- rtn = snap->store->type->lookup_exception(snap->store, chunk,
- NULL, 1, 0);
+ rtn = store->type->lookup_exception(store, chunk, NULL, 1, 0);
if (!rtn)
goto next_snapshot;