Skip to content

Commit

Permalink
dm thin: relax external origin size constraints
Browse files Browse the repository at this point in the history
Track the size of any external origin.  Previously the external origin's
size had to be a multiple of the thin-pool's block size, that is no
longer a requirement.  In addition, snapshots that are larger than the
external origin are now supported.

Signed-off-by: Joe Thornber <ejt@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
  • Loading branch information
Joe Thornber authored and Mike Snitzer committed Aug 1, 2014
1 parent 50f3c3e commit e5aea7b
Showing 1 changed file with 115 additions and 43 deletions.
158 changes: 115 additions & 43 deletions drivers/md/dm-thin.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ struct thin_c {
struct list_head list;
struct dm_dev *pool_dev;
struct dm_dev *origin_dev;
sector_t origin_size;
dm_thin_id dev_id;

struct pool *pool;
Expand Down Expand Up @@ -590,31 +591,31 @@ static void __complete_mapping_preparation(struct dm_thin_new_mapping *m)
}
}

static void copy_complete(int read_err, unsigned long write_err, void *context)
static void complete_mapping_preparation(struct dm_thin_new_mapping *m)
{
unsigned long flags;
struct dm_thin_new_mapping *m = context;
struct pool *pool = m->tc->pool;

m->err = read_err || write_err ? -EIO : 0;

spin_lock_irqsave(&pool->lock, flags);
__complete_mapping_preparation(m);
spin_unlock_irqrestore(&pool->lock, flags);
}

static void copy_complete(int read_err, unsigned long write_err, void *context)
{
struct dm_thin_new_mapping *m = context;

m->err = read_err || write_err ? -EIO : 0;
complete_mapping_preparation(m);
}

static void overwrite_endio(struct bio *bio, int err)
{
unsigned long flags;
struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));
struct dm_thin_new_mapping *m = h->overwrite_mapping;
struct pool *pool = m->tc->pool;

m->err = err;

spin_lock_irqsave(&pool->lock, flags);
__complete_mapping_preparation(m);
spin_unlock_irqrestore(&pool->lock, flags);
complete_mapping_preparation(m);
}

/*----------------------------------------------------------------*/
Expand Down Expand Up @@ -824,10 +825,31 @@ static struct dm_thin_new_mapping *get_next_mapping(struct pool *pool)
return m;
}

static void ll_zero(struct thin_c *tc, struct dm_thin_new_mapping *m,
sector_t begin, sector_t end)
{
int r;
struct dm_io_region to;

to.bdev = tc->pool_dev->bdev;
to.sector = begin;
to.count = end - begin;

r = dm_kcopyd_zero(tc->pool->copier, 1, &to, 0, copy_complete, m);
if (r < 0) {
DMERR_LIMIT("dm_kcopyd_zero() failed");
copy_complete(1, 1, m);
}
}

/*
* A partial copy also needs to zero the uncopied region.
*/
static void schedule_copy(struct thin_c *tc, dm_block_t virt_block,
struct dm_dev *origin, dm_block_t data_origin,
dm_block_t data_dest,
struct dm_bio_prison_cell *cell, struct bio *bio)
struct dm_bio_prison_cell *cell, struct bio *bio,
sector_t len)
{
int r;
struct pool *pool = tc->pool;
Expand All @@ -838,10 +860,15 @@ static void schedule_copy(struct thin_c *tc, dm_block_t virt_block,
m->data_block = data_dest;
m->cell = cell;

/*
* quiesce action + copy action + an extra reference held for the
* duration of this function (we may need to inc later for a
* partial zero).
*/
atomic_set(&m->prepare_actions, 3);

if (!dm_deferred_set_add_work(pool->shared_read_ds, &m->list))
atomic_set(&m->prepare_actions, 1); /* copy only */
else
atomic_set(&m->prepare_actions, 2); /* quiesce + copy */
complete_mapping_preparation(m); /* already quiesced */

/*
* IO to pool_dev remaps to the pool target's data_dev.
Expand All @@ -862,36 +889,47 @@ static void schedule_copy(struct thin_c *tc, dm_block_t virt_block,

from.bdev = origin->bdev;
from.sector = data_origin * pool->sectors_per_block;
from.count = pool->sectors_per_block;
from.count = len;

to.bdev = tc->pool_dev->bdev;
to.sector = data_dest * pool->sectors_per_block;
to.count = pool->sectors_per_block;
to.count = len;

r = dm_kcopyd_copy(pool->copier, &from, 1, &to,
0, copy_complete, m);
if (r < 0) {
mempool_free(m, pool->mapping_pool);
DMERR_LIMIT("dm_kcopyd_copy() failed");
cell_error(pool, cell);
copy_complete(1, 1, m);

/*
* We allow the zero to be issued, to simplify the
* error path. Otherwise we'd need to start
* worrying about decrementing the prepare_actions
* counter.
*/
}

/*
* Do we need to zero a tail region?
*/
if (len < pool->sectors_per_block && pool->pf.zero_new_blocks) {
atomic_inc(&m->prepare_actions);
ll_zero(tc, m,
data_dest * pool->sectors_per_block + len,
(data_dest + 1) * pool->sectors_per_block);
}
}

complete_mapping_preparation(m); /* drop our ref */
}

static void schedule_internal_copy(struct thin_c *tc, dm_block_t virt_block,
dm_block_t data_origin, dm_block_t data_dest,
struct dm_bio_prison_cell *cell, struct bio *bio)
{
schedule_copy(tc, virt_block, tc->pool_dev,
data_origin, data_dest, cell, bio);
}

static void schedule_external_copy(struct thin_c *tc, dm_block_t virt_block,
dm_block_t data_dest,
struct dm_bio_prison_cell *cell, struct bio *bio)
{
schedule_copy(tc, virt_block, tc->origin_dev,
virt_block, data_dest, cell, bio);
data_origin, data_dest, cell, bio,
tc->pool->sectors_per_block);
}

static void schedule_zero(struct thin_c *tc, dm_block_t virt_block,
Expand Down Expand Up @@ -923,21 +961,33 @@ static void schedule_zero(struct thin_c *tc, dm_block_t virt_block,
save_and_set_endio(bio, &m->saved_bi_end_io, overwrite_endio);
inc_all_io_entry(pool, bio);
remap_and_issue(tc, bio, data_block);
} else {
int r;
struct dm_io_region to;

to.bdev = tc->pool_dev->bdev;
to.sector = data_block * pool->sectors_per_block;
to.count = pool->sectors_per_block;
} else
ll_zero(tc, m,
data_block * pool->sectors_per_block,
(data_block + 1) * pool->sectors_per_block);
}

r = dm_kcopyd_zero(pool->copier, 1, &to, 0, copy_complete, m);
if (r < 0) {
mempool_free(m, pool->mapping_pool);
DMERR_LIMIT("dm_kcopyd_zero() failed");
cell_error(pool, cell);
}
}
static void schedule_external_copy(struct thin_c *tc, dm_block_t virt_block,
dm_block_t data_dest,
struct dm_bio_prison_cell *cell, struct bio *bio)
{
struct pool *pool = tc->pool;
sector_t virt_block_begin = virt_block * pool->sectors_per_block;
sector_t virt_block_end = (virt_block + 1) * pool->sectors_per_block;

if (virt_block_end <= tc->origin_size)
schedule_copy(tc, virt_block, tc->origin_dev,
virt_block, data_dest, cell, bio,
pool->sectors_per_block);

else if (virt_block_begin < tc->origin_size)
schedule_copy(tc, virt_block, tc->origin_dev,
virt_block, data_dest, cell, bio,
tc->origin_size - virt_block_begin);

else
schedule_zero(tc, virt_block, data_dest, cell, bio);
}

/*
Expand Down Expand Up @@ -1319,7 +1369,18 @@ static void process_bio(struct thin_c *tc, struct bio *bio)
inc_all_io_entry(pool, bio);
cell_defer_no_holder(tc, cell);

remap_to_origin_and_issue(tc, bio);
if (bio_end_sector(bio) <= tc->origin_size)
remap_to_origin_and_issue(tc, bio);

else if (bio->bi_iter.bi_sector < tc->origin_size) {
zero_fill_bio(bio);
bio->bi_iter.bi_size = (tc->origin_size - bio->bi_iter.bi_sector) << SECTOR_SHIFT;
remap_to_origin_and_issue(tc, bio);

} else {
zero_fill_bio(bio);
bio_endio(bio, 0);
}
} else
provision_block(tc, bio, block, cell);
break;
Expand Down Expand Up @@ -3145,7 +3206,7 @@ static struct target_type pool_target = {
.name = "thin-pool",
.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
DM_TARGET_IMMUTABLE,
.version = {1, 12, 0},
.version = {1, 13, 0},
.module = THIS_MODULE,
.ctr = pool_ctr,
.dtr = pool_dtr,
Expand Down Expand Up @@ -3404,6 +3465,16 @@ static void thin_postsuspend(struct dm_target *ti)
noflush_work(tc, do_noflush_stop);
}

static int thin_preresume(struct dm_target *ti)
{
struct thin_c *tc = ti->private;

if (tc->origin_dev)
tc->origin_size = get_dev_size(tc->origin_dev->bdev);

return 0;
}

/*
* <nr mapped sectors> <highest mapped sector>
*/
Expand Down Expand Up @@ -3486,12 +3557,13 @@ static int thin_iterate_devices(struct dm_target *ti,

static struct target_type thin_target = {
.name = "thin",
.version = {1, 12, 0},
.version = {1, 13, 0},
.module = THIS_MODULE,
.ctr = thin_ctr,
.dtr = thin_dtr,
.map = thin_map,
.end_io = thin_endio,
.preresume = thin_preresume,
.presuspend = thin_presuspend,
.postsuspend = thin_postsuspend,
.status = thin_status,
Expand Down

0 comments on commit e5aea7b

Please sign in to comment.