dm snapshot: move cow ref from exception store to snap core
Store the reference to the snapshot cow device in the core snapshot code instead of each exception store. It can be accessed through the new function dm_snap_cow(). Exception stores should each now maintain a reference to their parent snapshot struct. This is cleaner and makes part of the forthcoming snapshot merge code simpler. Signed-off-by: Mike Snitzer <snitzer@redhat.com> Signed-off-by: Alasdair G Kergon <agk@redhat.com> Reviewed-by: Jonathan Brassow <jbrassow@redhat.com> Cc: Mikulas Patocka <mpatocka@redhat.com>
This commit is contained in:
Родитель
985903bb3a
Коммит
fc56f6fbcc
|
@ -172,7 +172,8 @@ int dm_exception_store_set_chunk_size(struct dm_exception_store *store,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Validate the chunk size against the device block size */
|
/* Validate the chunk size against the device block size */
|
||||||
if (chunk_size % (bdev_logical_block_size(store->cow->bdev) >> 9)) {
|
if (chunk_size %
|
||||||
|
(bdev_logical_block_size(dm_snap_cow(store->snap)->bdev) >> 9)) {
|
||||||
*error = "Chunk size is not a multiple of device blocksize";
|
*error = "Chunk size is not a multiple of device blocksize";
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -190,6 +191,7 @@ int dm_exception_store_set_chunk_size(struct dm_exception_store *store,
|
||||||
}
|
}
|
||||||
|
|
||||||
int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
|
int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
|
||||||
|
struct dm_snapshot *snap,
|
||||||
unsigned *args_used,
|
unsigned *args_used,
|
||||||
struct dm_exception_store **store)
|
struct dm_exception_store **store)
|
||||||
{
|
{
|
||||||
|
@ -198,7 +200,7 @@ int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
|
||||||
struct dm_exception_store *tmp_store;
|
struct dm_exception_store *tmp_store;
|
||||||
char persistent;
|
char persistent;
|
||||||
|
|
||||||
if (argc < 3) {
|
if (argc < 2) {
|
||||||
ti->error = "Insufficient exception store arguments";
|
ti->error = "Insufficient exception store arguments";
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -209,7 +211,7 @@ int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
persistent = toupper(*argv[1]);
|
persistent = toupper(*argv[0]);
|
||||||
if (persistent == 'P')
|
if (persistent == 'P')
|
||||||
type = get_type("P");
|
type = get_type("P");
|
||||||
else if (persistent == 'N')
|
else if (persistent == 'N')
|
||||||
|
@ -227,32 +229,23 @@ int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp_store->type = type;
|
tmp_store->type = type;
|
||||||
tmp_store->ti = ti;
|
tmp_store->snap = snap;
|
||||||
|
|
||||||
r = dm_get_device(ti, argv[0], 0, 0,
|
r = set_chunk_size(tmp_store, argv[1], &ti->error);
|
||||||
FMODE_READ | FMODE_WRITE, &tmp_store->cow);
|
|
||||||
if (r) {
|
|
||||||
ti->error = "Cannot get COW device";
|
|
||||||
goto bad_cow;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = set_chunk_size(tmp_store, argv[2], &ti->error);
|
|
||||||
if (r)
|
if (r)
|
||||||
goto bad_ctr;
|
goto bad;
|
||||||
|
|
||||||
r = type->ctr(tmp_store, 0, NULL);
|
r = type->ctr(tmp_store, 0, NULL);
|
||||||
if (r) {
|
if (r) {
|
||||||
ti->error = "Exception store type constructor failed";
|
ti->error = "Exception store type constructor failed";
|
||||||
goto bad_ctr;
|
goto bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
*args_used = 3;
|
*args_used = 2;
|
||||||
*store = tmp_store;
|
*store = tmp_store;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
bad_ctr:
|
bad:
|
||||||
dm_put_device(ti, tmp_store->cow);
|
|
||||||
bad_cow:
|
|
||||||
put_type(type);
|
put_type(type);
|
||||||
bad_type:
|
bad_type:
|
||||||
kfree(tmp_store);
|
kfree(tmp_store);
|
||||||
|
@ -263,7 +256,6 @@ EXPORT_SYMBOL(dm_exception_store_create);
|
||||||
void dm_exception_store_destroy(struct dm_exception_store *store)
|
void dm_exception_store_destroy(struct dm_exception_store *store)
|
||||||
{
|
{
|
||||||
store->type->dtr(store);
|
store->type->dtr(store);
|
||||||
dm_put_device(store->ti, store->cow);
|
|
||||||
put_type(store->type);
|
put_type(store->type);
|
||||||
kfree(store);
|
kfree(store);
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,11 +94,11 @@ struct dm_exception_store_type {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dm_snapshot;
|
||||||
|
|
||||||
struct dm_exception_store {
|
struct dm_exception_store {
|
||||||
struct dm_exception_store_type *type;
|
struct dm_exception_store_type *type;
|
||||||
struct dm_target *ti;
|
struct dm_snapshot *snap;
|
||||||
|
|
||||||
struct dm_dev *cow;
|
|
||||||
|
|
||||||
/* Size of data blocks saved - must be a power of 2 */
|
/* Size of data blocks saved - must be a power of 2 */
|
||||||
unsigned chunk_size;
|
unsigned chunk_size;
|
||||||
|
@ -108,6 +108,11 @@ struct dm_exception_store {
|
||||||
void *context;
|
void *context;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Obtain the cow device used by a given snapshot.
|
||||||
|
*/
|
||||||
|
struct dm_dev *dm_snap_cow(struct dm_snapshot *snap);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Funtions to manipulate consecutive chunks
|
* Funtions to manipulate consecutive chunks
|
||||||
*/
|
*/
|
||||||
|
@ -173,6 +178,7 @@ int dm_exception_store_set_chunk_size(struct dm_exception_store *store,
|
||||||
char **error);
|
char **error);
|
||||||
|
|
||||||
int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
|
int dm_exception_store_create(struct dm_target *ti, int argc, char **argv,
|
||||||
|
struct dm_snapshot *snap,
|
||||||
unsigned *args_used,
|
unsigned *args_used,
|
||||||
struct dm_exception_store **store);
|
struct dm_exception_store **store);
|
||||||
void dm_exception_store_destroy(struct dm_exception_store *store);
|
void dm_exception_store_destroy(struct dm_exception_store *store);
|
||||||
|
|
|
@ -214,7 +214,7 @@ static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw,
|
||||||
int metadata)
|
int metadata)
|
||||||
{
|
{
|
||||||
struct dm_io_region where = {
|
struct dm_io_region where = {
|
||||||
.bdev = ps->store->cow->bdev,
|
.bdev = dm_snap_cow(ps->store->snap)->bdev,
|
||||||
.sector = ps->store->chunk_size * chunk,
|
.sector = ps->store->chunk_size * chunk,
|
||||||
.count = ps->store->chunk_size,
|
.count = ps->store->chunk_size,
|
||||||
};
|
};
|
||||||
|
@ -294,7 +294,8 @@ static int read_header(struct pstore *ps, int *new_snapshot)
|
||||||
*/
|
*/
|
||||||
if (!ps->store->chunk_size) {
|
if (!ps->store->chunk_size) {
|
||||||
ps->store->chunk_size = max(DM_CHUNK_SIZE_DEFAULT_SECTORS,
|
ps->store->chunk_size = max(DM_CHUNK_SIZE_DEFAULT_SECTORS,
|
||||||
bdev_logical_block_size(ps->store->cow->bdev) >> 9);
|
bdev_logical_block_size(dm_snap_cow(ps->store->snap)->
|
||||||
|
bdev) >> 9);
|
||||||
ps->store->chunk_mask = ps->store->chunk_size - 1;
|
ps->store->chunk_mask = ps->store->chunk_size - 1;
|
||||||
ps->store->chunk_shift = ffs(ps->store->chunk_size) - 1;
|
ps->store->chunk_shift = ffs(ps->store->chunk_size) - 1;
|
||||||
chunk_size_supplied = 0;
|
chunk_size_supplied = 0;
|
||||||
|
@ -497,7 +498,7 @@ static void persistent_usage(struct dm_exception_store *store,
|
||||||
struct pstore *ps = get_info(store);
|
struct pstore *ps = get_info(store);
|
||||||
|
|
||||||
*sectors_allocated = ps->next_free * store->chunk_size;
|
*sectors_allocated = ps->next_free * store->chunk_size;
|
||||||
*total_sectors = get_dev_size(store->cow->bdev);
|
*total_sectors = get_dev_size(dm_snap_cow(store->snap)->bdev);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* First chunk is the fixed header.
|
* First chunk is the fixed header.
|
||||||
|
@ -596,7 +597,7 @@ static int persistent_prepare_exception(struct dm_exception_store *store,
|
||||||
struct pstore *ps = get_info(store);
|
struct pstore *ps = get_info(store);
|
||||||
uint32_t stride;
|
uint32_t stride;
|
||||||
chunk_t next_free;
|
chunk_t next_free;
|
||||||
sector_t size = get_dev_size(store->cow->bdev);
|
sector_t size = get_dev_size(dm_snap_cow(store->snap)->bdev);
|
||||||
|
|
||||||
/* Is there enough room ? */
|
/* Is there enough room ? */
|
||||||
if (size < ((ps->next_free + 1) * store->chunk_size))
|
if (size < ((ps->next_free + 1) * store->chunk_size))
|
||||||
|
@ -733,8 +734,7 @@ static unsigned persistent_status(struct dm_exception_store *store,
|
||||||
case STATUSTYPE_INFO:
|
case STATUSTYPE_INFO:
|
||||||
break;
|
break;
|
||||||
case STATUSTYPE_TABLE:
|
case STATUSTYPE_TABLE:
|
||||||
DMEMIT(" %s P %llu", store->cow->name,
|
DMEMIT(" P %llu", (unsigned long long)store->chunk_size);
|
||||||
(unsigned long long)store->chunk_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sz;
|
return sz;
|
||||||
|
|
|
@ -39,7 +39,7 @@ static int transient_prepare_exception(struct dm_exception_store *store,
|
||||||
struct dm_exception *e)
|
struct dm_exception *e)
|
||||||
{
|
{
|
||||||
struct transient_c *tc = store->context;
|
struct transient_c *tc = store->context;
|
||||||
sector_t size = get_dev_size(store->cow->bdev);
|
sector_t size = get_dev_size(dm_snap_cow(store->snap)->bdev);
|
||||||
|
|
||||||
if (size < (tc->next_free + store->chunk_size))
|
if (size < (tc->next_free + store->chunk_size))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -65,7 +65,7 @@ static void transient_usage(struct dm_exception_store *store,
|
||||||
sector_t *metadata_sectors)
|
sector_t *metadata_sectors)
|
||||||
{
|
{
|
||||||
*sectors_allocated = ((struct transient_c *) store->context)->next_free;
|
*sectors_allocated = ((struct transient_c *) store->context)->next_free;
|
||||||
*total_sectors = get_dev_size(store->cow->bdev);
|
*total_sectors = get_dev_size(dm_snap_cow(store->snap)->bdev);
|
||||||
*metadata_sectors = 0;
|
*metadata_sectors = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,8 +94,7 @@ static unsigned transient_status(struct dm_exception_store *store,
|
||||||
case STATUSTYPE_INFO:
|
case STATUSTYPE_INFO:
|
||||||
break;
|
break;
|
||||||
case STATUSTYPE_TABLE:
|
case STATUSTYPE_TABLE:
|
||||||
DMEMIT(" %s N %llu", store->cow->name,
|
DMEMIT(" N %llu", (unsigned long long)store->chunk_size);
|
||||||
(unsigned long long)store->chunk_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sz;
|
return sz;
|
||||||
|
|
|
@ -59,6 +59,9 @@ struct dm_snapshot {
|
||||||
struct rw_semaphore lock;
|
struct rw_semaphore lock;
|
||||||
|
|
||||||
struct dm_dev *origin;
|
struct dm_dev *origin;
|
||||||
|
struct dm_dev *cow;
|
||||||
|
|
||||||
|
struct dm_target *ti;
|
||||||
|
|
||||||
/* List of snapshots per Origin */
|
/* List of snapshots per Origin */
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
@ -97,6 +100,12 @@ struct dm_snapshot {
|
||||||
struct hlist_head tracked_chunk_hash[DM_TRACKED_CHUNK_HASH_SIZE];
|
struct hlist_head tracked_chunk_hash[DM_TRACKED_CHUNK_HASH_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dm_dev *dm_snap_cow(struct dm_snapshot *s)
|
||||||
|
{
|
||||||
|
return s->cow;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(dm_snap_cow);
|
||||||
|
|
||||||
static struct workqueue_struct *ksnapd;
|
static struct workqueue_struct *ksnapd;
|
||||||
static void flush_queued_bios(struct work_struct *work);
|
static void flush_queued_bios(struct work_struct *work);
|
||||||
|
|
||||||
|
@ -558,7 +567,7 @@ static int init_hash_tables(struct dm_snapshot *s)
|
||||||
* Calculate based on the size of the original volume or
|
* Calculate based on the size of the original volume or
|
||||||
* the COW volume...
|
* the COW volume...
|
||||||
*/
|
*/
|
||||||
cow_dev_size = get_dev_size(s->store->cow->bdev);
|
cow_dev_size = get_dev_size(s->cow->bdev);
|
||||||
origin_dev_size = get_dev_size(s->origin->bdev);
|
origin_dev_size = get_dev_size(s->origin->bdev);
|
||||||
max_buckets = calc_max_buckets();
|
max_buckets = calc_max_buckets();
|
||||||
|
|
||||||
|
@ -596,45 +605,55 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
||||||
struct dm_snapshot *s;
|
struct dm_snapshot *s;
|
||||||
int i;
|
int i;
|
||||||
int r = -EINVAL;
|
int r = -EINVAL;
|
||||||
char *origin_path;
|
char *origin_path, *cow_path;
|
||||||
struct dm_exception_store *store;
|
|
||||||
unsigned args_used;
|
unsigned args_used;
|
||||||
|
|
||||||
if (argc != 4) {
|
if (argc != 4) {
|
||||||
ti->error = "requires exactly 4 arguments";
|
ti->error = "requires exactly 4 arguments";
|
||||||
r = -EINVAL;
|
r = -EINVAL;
|
||||||
goto bad_args;
|
goto bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
origin_path = argv[0];
|
origin_path = argv[0];
|
||||||
argv++;
|
argv++;
|
||||||
argc--;
|
argc--;
|
||||||
|
|
||||||
r = dm_exception_store_create(ti, argc, argv, &args_used, &store);
|
|
||||||
if (r) {
|
|
||||||
ti->error = "Couldn't create exception store";
|
|
||||||
r = -EINVAL;
|
|
||||||
goto bad_args;
|
|
||||||
}
|
|
||||||
|
|
||||||
argv += args_used;
|
|
||||||
argc -= args_used;
|
|
||||||
|
|
||||||
s = kmalloc(sizeof(*s), GFP_KERNEL);
|
s = kmalloc(sizeof(*s), GFP_KERNEL);
|
||||||
if (!s) {
|
if (!s) {
|
||||||
ti->error = "Cannot allocate snapshot context private "
|
ti->error = "Cannot allocate snapshot context private "
|
||||||
"structure";
|
"structure";
|
||||||
r = -ENOMEM;
|
r = -ENOMEM;
|
||||||
goto bad_snap;
|
goto bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cow_path = argv[0];
|
||||||
|
argv++;
|
||||||
|
argc--;
|
||||||
|
|
||||||
|
r = dm_get_device(ti, cow_path, 0, 0,
|
||||||
|
FMODE_READ | FMODE_WRITE, &s->cow);
|
||||||
|
if (r) {
|
||||||
|
ti->error = "Cannot get COW device";
|
||||||
|
goto bad_cow;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dm_exception_store_create(ti, argc, argv, s, &args_used, &s->store);
|
||||||
|
if (r) {
|
||||||
|
ti->error = "Couldn't create exception store";
|
||||||
|
r = -EINVAL;
|
||||||
|
goto bad_store;
|
||||||
|
}
|
||||||
|
|
||||||
|
argv += args_used;
|
||||||
|
argc -= args_used;
|
||||||
|
|
||||||
r = dm_get_device(ti, origin_path, 0, ti->len, FMODE_READ, &s->origin);
|
r = dm_get_device(ti, origin_path, 0, ti->len, FMODE_READ, &s->origin);
|
||||||
if (r) {
|
if (r) {
|
||||||
ti->error = "Cannot get origin device";
|
ti->error = "Cannot get origin device";
|
||||||
goto bad_origin;
|
goto bad_origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->store = store;
|
s->ti = ti;
|
||||||
s->valid = 1;
|
s->valid = 1;
|
||||||
s->active = 0;
|
s->active = 0;
|
||||||
atomic_set(&s->pending_exceptions_count, 0);
|
atomic_set(&s->pending_exceptions_count, 0);
|
||||||
|
@ -723,12 +742,15 @@ bad_hash_tables:
|
||||||
dm_put_device(ti, s->origin);
|
dm_put_device(ti, s->origin);
|
||||||
|
|
||||||
bad_origin:
|
bad_origin:
|
||||||
|
dm_exception_store_destroy(s->store);
|
||||||
|
|
||||||
|
bad_store:
|
||||||
|
dm_put_device(ti, s->cow);
|
||||||
|
|
||||||
|
bad_cow:
|
||||||
kfree(s);
|
kfree(s);
|
||||||
|
|
||||||
bad_snap:
|
bad:
|
||||||
dm_exception_store_destroy(store);
|
|
||||||
|
|
||||||
bad_args:
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -777,6 +799,8 @@ static void snapshot_dtr(struct dm_target *ti)
|
||||||
|
|
||||||
dm_exception_store_destroy(s->store);
|
dm_exception_store_destroy(s->store);
|
||||||
|
|
||||||
|
dm_put_device(ti, s->cow);
|
||||||
|
|
||||||
kfree(s);
|
kfree(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -839,7 +863,7 @@ static void __invalidate_snapshot(struct dm_snapshot *s, int err)
|
||||||
|
|
||||||
s->valid = 0;
|
s->valid = 0;
|
||||||
|
|
||||||
dm_table_event(s->store->ti->table);
|
dm_table_event(s->ti->table);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_pending_exception(struct dm_snap_pending_exception *pe)
|
static void get_pending_exception(struct dm_snap_pending_exception *pe)
|
||||||
|
@ -977,7 +1001,7 @@ static void start_copy(struct dm_snap_pending_exception *pe)
|
||||||
src.sector = chunk_to_sector(s->store, pe->e.old_chunk);
|
src.sector = chunk_to_sector(s->store, pe->e.old_chunk);
|
||||||
src.count = min((sector_t)s->store->chunk_size, dev_size - src.sector);
|
src.count = min((sector_t)s->store->chunk_size, dev_size - src.sector);
|
||||||
|
|
||||||
dest.bdev = s->store->cow->bdev;
|
dest.bdev = s->cow->bdev;
|
||||||
dest.sector = chunk_to_sector(s->store, pe->e.new_chunk);
|
dest.sector = chunk_to_sector(s->store, pe->e.new_chunk);
|
||||||
dest.count = src.count;
|
dest.count = src.count;
|
||||||
|
|
||||||
|
@ -1038,7 +1062,7 @@ __find_pending_exception(struct dm_snapshot *s,
|
||||||
static void remap_exception(struct dm_snapshot *s, struct dm_exception *e,
|
static void remap_exception(struct dm_snapshot *s, struct dm_exception *e,
|
||||||
struct bio *bio, chunk_t chunk)
|
struct bio *bio, chunk_t chunk)
|
||||||
{
|
{
|
||||||
bio->bi_bdev = s->store->cow->bdev;
|
bio->bi_bdev = s->cow->bdev;
|
||||||
bio->bi_sector = chunk_to_sector(s->store,
|
bio->bi_sector = chunk_to_sector(s->store,
|
||||||
dm_chunk_number(e->new_chunk) +
|
dm_chunk_number(e->new_chunk) +
|
||||||
(chunk - e->old_chunk)) +
|
(chunk - e->old_chunk)) +
|
||||||
|
@ -1056,7 +1080,7 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
|
||||||
struct dm_snap_pending_exception *pe = NULL;
|
struct dm_snap_pending_exception *pe = NULL;
|
||||||
|
|
||||||
if (unlikely(bio_empty_barrier(bio))) {
|
if (unlikely(bio_empty_barrier(bio))) {
|
||||||
bio->bi_bdev = s->store->cow->bdev;
|
bio->bi_bdev = s->cow->bdev;
|
||||||
return DM_MAPIO_REMAPPED;
|
return DM_MAPIO_REMAPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1200,7 +1224,7 @@ static int snapshot_status(struct dm_target *ti, status_type_t type,
|
||||||
* to make private copies if the output is to
|
* to make private copies if the output is to
|
||||||
* make sense.
|
* make sense.
|
||||||
*/
|
*/
|
||||||
DMEMIT("%s", snap->origin->name);
|
DMEMIT("%s %s", snap->origin->name, snap->cow->name);
|
||||||
snap->store->type->status(snap->store, type, result + sz,
|
snap->store->type->status(snap->store, type, result + sz,
|
||||||
maxlen - sz);
|
maxlen - sz);
|
||||||
break;
|
break;
|
||||||
|
@ -1240,7 +1264,7 @@ static int __origin_write(struct list_head *snapshots, struct bio *bio)
|
||||||
goto next_snapshot;
|
goto next_snapshot;
|
||||||
|
|
||||||
/* Nothing to do if writing beyond end of snapshot */
|
/* 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(snap->ti->table))
|
||||||
goto next_snapshot;
|
goto next_snapshot;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Загрузка…
Ссылка в новой задаче