Skip to content

Commit

Permalink
dm thin: add pool target flags to control discard
Browse files Browse the repository at this point in the history
Add dm thin target arguments to control discard support.

ignore_discard: Disables discard support

no_discard_passdown: Don't pass discards down to the underlying data
device, but just remove the mapping within the thin provisioning target.

Signed-off-by: Joe Thornber <ejt@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
  • Loading branch information
Joe Thornber authored and Alasdair G Kergon committed Mar 28, 2012
1 parent 104655f commit 67e2e2b
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 28 deletions.
8 changes: 7 additions & 1 deletion Documentation/device-mapper/thin-provisioning.txt
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,13 @@ i) Constructor
<low water mark (blocks)> [<number of feature args> [<arg>]*]

Optional feature arguments:
- 'skip_block_zeroing': skips the zeroing of newly-provisioned blocks.

skip_block_zeroing: Skip the zeroing of newly-provisioned blocks.

ignore_discard: Disable discard support.

no_discard_passdown: Don't pass discards down to the underlying
data device, but just remove the mapping.

Data block size must be between 64KB (128 sectors) and 1GB
(2097152 sectors) inclusive.
Expand Down
135 changes: 108 additions & 27 deletions drivers/md/dm-thin.c
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,13 @@ static void build_virtual_key(struct dm_thin_device *td, dm_block_t b,
* devices.
*/
struct new_mapping;

struct pool_features {
unsigned zero_new_blocks:1;
unsigned discard_enabled:1;
unsigned discard_passdown:1;
};

struct pool {
struct list_head list;
struct dm_target *ti; /* Only set if a pool target is bound */
Expand All @@ -502,7 +509,7 @@ struct pool {
dm_block_t offset_mask;
dm_block_t low_water_blocks;

unsigned zero_new_blocks:1;
struct pool_features pf;
unsigned low_water_triggered:1; /* A dm event has been sent */
unsigned no_free_space:1; /* A -ENOSPC warning has been issued */

Expand Down Expand Up @@ -543,7 +550,7 @@ struct pool_c {
struct dm_target_callbacks callbacks;

dm_block_t low_water_blocks;
unsigned zero_new_blocks:1;
struct pool_features pf;
};

/*
Expand Down Expand Up @@ -1051,7 +1058,7 @@ static void schedule_zero(struct thin_c *tc, dm_block_t virt_block,
* zeroing pre-existing data, we can issue the bio immediately.
* Otherwise we use kcopyd to zero the data first.
*/
if (!pool->zero_new_blocks)
if (!pool->pf.zero_new_blocks)
process_prepared_mapping(m);

else if (io_overwrites_block(pool, bio)) {
Expand Down Expand Up @@ -1202,7 +1209,7 @@ static void process_discard(struct thin_c *tc, struct bio *bio)
*/
m = get_next_mapping(pool);
m->tc = tc;
m->pass_discard = !lookup_result.shared;
m->pass_discard = (!lookup_result.shared) & pool->pf.discard_passdown;
m->virt_block = block;
m->data_block = lookup_result.block;
m->cell = cell;
Expand Down Expand Up @@ -1617,7 +1624,7 @@ static int bind_control_target(struct pool *pool, struct dm_target *ti)

pool->ti = ti;
pool->low_water_blocks = pt->low_water_blocks;
pool->zero_new_blocks = pt->zero_new_blocks;
pool->pf = pt->pf;

return 0;
}
Expand All @@ -1631,6 +1638,14 @@ static void unbind_control_target(struct pool *pool, struct dm_target *ti)
/*----------------------------------------------------------------
* Pool creation
*--------------------------------------------------------------*/
/* Initialize pool features. */
static void pool_features_init(struct pool_features *pf)
{
pf->zero_new_blocks = 1;
pf->discard_enabled = 1;
pf->discard_passdown = 1;
}

static void __pool_destroy(struct pool *pool)
{
__pool_table_remove(pool);
Expand Down Expand Up @@ -1678,7 +1693,7 @@ static struct pool *pool_create(struct mapped_device *pool_md,
pool->block_shift = ffs(block_size) - 1;
pool->offset_mask = block_size - 1;
pool->low_water_blocks = 0;
pool->zero_new_blocks = 1;
pool_features_init(&pool->pf);
pool->prison = prison_create(PRISON_CELLS);
if (!pool->prison) {
*error = "Error creating pool's bio prison";
Expand Down Expand Up @@ -1775,7 +1790,8 @@ static void __pool_dec(struct pool *pool)

static struct pool *__pool_find(struct mapped_device *pool_md,
struct block_device *metadata_dev,
unsigned long block_size, char **error)
unsigned long block_size, char **error,
int *created)
{
struct pool *pool = __pool_table_lookup_metadata_dev(metadata_dev);

Expand All @@ -1791,8 +1807,10 @@ static struct pool *__pool_find(struct mapped_device *pool_md,
return ERR_PTR(-EINVAL);
__pool_inc(pool);

} else
} else {
pool = pool_create(pool_md, metadata_dev, block_size, error);
*created = 1;
}
}

return pool;
Expand All @@ -1816,10 +1834,6 @@ static void pool_dtr(struct dm_target *ti)
mutex_unlock(&dm_thin_pool_table.mutex);
}

struct pool_features {
unsigned zero_new_blocks:1;
};

static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
struct dm_target *ti)
{
Expand All @@ -1828,7 +1842,7 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
const char *arg_name;

static struct dm_arg _args[] = {
{0, 1, "Invalid number of pool feature arguments"},
{0, 3, "Invalid number of pool feature arguments"},
};

/*
Expand All @@ -1848,6 +1862,12 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
if (!strcasecmp(arg_name, "skip_block_zeroing")) {
pf->zero_new_blocks = 0;
continue;
} else if (!strcasecmp(arg_name, "ignore_discard")) {
pf->discard_enabled = 0;
continue;
} else if (!strcasecmp(arg_name, "no_discard_passdown")) {
pf->discard_passdown = 0;
continue;
}

ti->error = "Unrecognised pool feature requested";
Expand All @@ -1865,10 +1885,12 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
*
* Optional feature arguments are:
* skip_block_zeroing: skips the zeroing of newly-provisioned blocks.
* ignore_discard: disable discard
* no_discard_passdown: don't pass discards down to the data device
*/
static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
{
int r;
int r, pool_created = 0;
struct pool_c *pt;
struct pool *pool;
struct pool_features pf;
Expand Down Expand Up @@ -1928,8 +1950,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
/*
* Set default pool features.
*/
memset(&pf, 0, sizeof(pf));
pf.zero_new_blocks = 1;
pool_features_init(&pf);

dm_consume_args(&as, 4);
r = parse_pool_features(&as, &pf, ti);
Expand All @@ -1943,21 +1964,58 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
}

pool = __pool_find(dm_table_get_md(ti->table), metadata_dev->bdev,
block_size, &ti->error);
block_size, &ti->error, &pool_created);
if (IS_ERR(pool)) {
r = PTR_ERR(pool);
goto out_free_pt;
}

/*
* 'pool_created' reflects whether this is the first table load.
* Top level discard support is not allowed to be changed after
* initial load. This would require a pool reload to trigger thin
* device changes.
*/
if (!pool_created && pf.discard_enabled != pool->pf.discard_enabled) {
ti->error = "Discard support cannot be disabled once enabled";
r = -EINVAL;
goto out_flags_changed;
}

/*
* If discard_passdown was enabled verify that the data device
* supports discards. Disable discard_passdown if not; otherwise
* -EOPNOTSUPP will be returned.
*/
if (pf.discard_passdown) {
struct request_queue *q = bdev_get_queue(data_dev->bdev);
if (!q || !blk_queue_discard(q)) {
DMWARN("Discard unsupported by data device: Disabling discard passdown.");
pf.discard_passdown = 0;
}
}

pt->pool = pool;
pt->ti = ti;
pt->metadata_dev = metadata_dev;
pt->data_dev = data_dev;
pt->low_water_blocks = low_water_blocks;
pt->zero_new_blocks = pf.zero_new_blocks;
pt->pf = pf;
ti->num_flush_requests = 1;
ti->num_discard_requests = 1;
ti->discards_supported = 1;
/*
* Only need to enable discards if the pool should pass
* them down to the data device. The thin device's discard
* processing will cause mappings to be removed from the btree.
*/
if (pf.discard_enabled && pf.discard_passdown) {
ti->num_discard_requests = 1;
/*
* Setting 'discards_supported' circumvents the normal
* stacking of discard limits (this keeps the pool and
* thin devices' discard limits consistent).
*/
ti->discards_supported = 1;
}
ti->private = pt;

pt->callbacks.congested_fn = pool_is_congested;
Expand All @@ -1967,6 +2025,8 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)

return 0;

out_flags_changed:
__pool_dec(pool);
out_free_pt:
kfree(pt);
out:
Expand Down Expand Up @@ -2255,7 +2315,7 @@ static int pool_message(struct dm_target *ti, unsigned argc, char **argv)
static int pool_status(struct dm_target *ti, status_type_t type,
char *result, unsigned maxlen)
{
int r;
int r, count;
unsigned sz = 0;
uint64_t transaction_id;
dm_block_t nr_free_blocks_data;
Expand Down Expand Up @@ -2318,10 +2378,19 @@ static int pool_status(struct dm_target *ti, status_type_t type,
(unsigned long)pool->sectors_per_block,
(unsigned long long)pt->low_water_blocks);

DMEMIT("%u ", !pool->zero_new_blocks);
count = !pool->pf.zero_new_blocks + !pool->pf.discard_enabled +
!pool->pf.discard_passdown;
DMEMIT("%u ", count);

if (!pool->zero_new_blocks)
if (!pool->pf.zero_new_blocks)
DMEMIT("skip_block_zeroing ");

if (!pool->pf.discard_enabled)
DMEMIT("ignore_discard ");

if (!pool->pf.discard_passdown)
DMEMIT("no_discard_passdown ");

break;
}

Expand Down Expand Up @@ -2352,13 +2421,17 @@ static int pool_merge(struct dm_target *ti, struct bvec_merge_data *bvm,

static void set_discard_limits(struct pool *pool, struct queue_limits *limits)
{
/*
* FIXME: these limits may be incompatible with the pool's data device
*/
limits->max_discard_sectors = pool->sectors_per_block;

/*
* This is just a hint, and not enforced. We have to cope with
* bios that overlap 2 blocks.
*/
limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
limits->discard_zeroes_data = pool->pf.zero_new_blocks;
}

static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
Expand All @@ -2368,14 +2441,15 @@ static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)

blk_limits_io_min(limits, 0);
blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT);
set_discard_limits(pool, limits);
if (pool->pf.discard_enabled)
set_discard_limits(pool, limits);
}

static struct target_type pool_target = {
.name = "thin-pool",
.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
DM_TARGET_IMMUTABLE,
.version = {1, 0, 0},
.version = {1, 1, 0},
.module = THIS_MODULE,
.ctr = pool_ctr,
.dtr = pool_dtr,
Expand Down Expand Up @@ -2417,6 +2491,9 @@ static void thin_dtr(struct dm_target *ti)
* pool_dev: the path to the pool (eg, /dev/mapper/my_pool)
* dev_id: the internal device identifier
* origin_dev: a device external to the pool that should act as the origin
*
* If the pool device has discards disabled, they get disabled for the thin
* device as well.
*/
static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
{
Expand Down Expand Up @@ -2485,8 +2562,12 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)

ti->split_io = tc->pool->sectors_per_block;
ti->num_flush_requests = 1;
ti->num_discard_requests = 1;
ti->discards_supported = 1;

/* In case the pool supports discards, pass them on. */
if (tc->pool->pf.discard_enabled) {
ti->discards_supported = 1;
ti->num_discard_requests = 1;
}

dm_put(pool_md);

Expand Down

0 comments on commit 67e2e2b

Please sign in to comment.