提交 67e2e2b2 编写于 作者: J Joe Thornber 提交者: Alasdair G Kergon

dm thin: add pool target flags to control discard

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: NJoe Thornber <ejt@redhat.com>
Signed-off-by: NMike Snitzer <snitzer@redhat.com>
Signed-off-by: NAlasdair G Kergon <agk@redhat.com>
上级 104655fd
...@@ -223,7 +223,13 @@ i) Constructor ...@@ -223,7 +223,13 @@ i) Constructor
<low water mark (blocks)> [<number of feature args> [<arg>]*] <low water mark (blocks)> [<number of feature args> [<arg>]*]
Optional feature arguments: 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 Data block size must be between 64KB (128 sectors) and 1GB
(2097152 sectors) inclusive. (2097152 sectors) inclusive.
......
...@@ -489,6 +489,13 @@ static void build_virtual_key(struct dm_thin_device *td, dm_block_t b, ...@@ -489,6 +489,13 @@ static void build_virtual_key(struct dm_thin_device *td, dm_block_t b,
* devices. * devices.
*/ */
struct new_mapping; struct new_mapping;
struct pool_features {
unsigned zero_new_blocks:1;
unsigned discard_enabled:1;
unsigned discard_passdown:1;
};
struct pool { struct pool {
struct list_head list; struct list_head list;
struct dm_target *ti; /* Only set if a pool target is bound */ struct dm_target *ti; /* Only set if a pool target is bound */
...@@ -502,7 +509,7 @@ struct pool { ...@@ -502,7 +509,7 @@ struct pool {
dm_block_t offset_mask; dm_block_t offset_mask;
dm_block_t low_water_blocks; 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 low_water_triggered:1; /* A dm event has been sent */
unsigned no_free_space:1; /* A -ENOSPC warning has been issued */ unsigned no_free_space:1; /* A -ENOSPC warning has been issued */
...@@ -543,7 +550,7 @@ struct pool_c { ...@@ -543,7 +550,7 @@ struct pool_c {
struct dm_target_callbacks callbacks; struct dm_target_callbacks callbacks;
dm_block_t low_water_blocks; dm_block_t low_water_blocks;
unsigned zero_new_blocks:1; struct pool_features pf;
}; };
/* /*
...@@ -1051,7 +1058,7 @@ static void schedule_zero(struct thin_c *tc, dm_block_t virt_block, ...@@ -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. * zeroing pre-existing data, we can issue the bio immediately.
* Otherwise we use kcopyd to zero the data first. * Otherwise we use kcopyd to zero the data first.
*/ */
if (!pool->zero_new_blocks) if (!pool->pf.zero_new_blocks)
process_prepared_mapping(m); process_prepared_mapping(m);
else if (io_overwrites_block(pool, bio)) { else if (io_overwrites_block(pool, bio)) {
...@@ -1202,7 +1209,7 @@ static void process_discard(struct thin_c *tc, struct bio *bio) ...@@ -1202,7 +1209,7 @@ static void process_discard(struct thin_c *tc, struct bio *bio)
*/ */
m = get_next_mapping(pool); m = get_next_mapping(pool);
m->tc = tc; m->tc = tc;
m->pass_discard = !lookup_result.shared; m->pass_discard = (!lookup_result.shared) & pool->pf.discard_passdown;
m->virt_block = block; m->virt_block = block;
m->data_block = lookup_result.block; m->data_block = lookup_result.block;
m->cell = cell; m->cell = cell;
...@@ -1617,7 +1624,7 @@ static int bind_control_target(struct pool *pool, struct dm_target *ti) ...@@ -1617,7 +1624,7 @@ static int bind_control_target(struct pool *pool, struct dm_target *ti)
pool->ti = ti; pool->ti = ti;
pool->low_water_blocks = pt->low_water_blocks; pool->low_water_blocks = pt->low_water_blocks;
pool->zero_new_blocks = pt->zero_new_blocks; pool->pf = pt->pf;
return 0; return 0;
} }
...@@ -1631,6 +1638,14 @@ static void unbind_control_target(struct pool *pool, struct dm_target *ti) ...@@ -1631,6 +1638,14 @@ static void unbind_control_target(struct pool *pool, struct dm_target *ti)
/*---------------------------------------------------------------- /*----------------------------------------------------------------
* Pool creation * 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) static void __pool_destroy(struct pool *pool)
{ {
__pool_table_remove(pool); __pool_table_remove(pool);
...@@ -1678,7 +1693,7 @@ static struct pool *pool_create(struct mapped_device *pool_md, ...@@ -1678,7 +1693,7 @@ static struct pool *pool_create(struct mapped_device *pool_md,
pool->block_shift = ffs(block_size) - 1; pool->block_shift = ffs(block_size) - 1;
pool->offset_mask = block_size - 1; pool->offset_mask = block_size - 1;
pool->low_water_blocks = 0; pool->low_water_blocks = 0;
pool->zero_new_blocks = 1; pool_features_init(&pool->pf);
pool->prison = prison_create(PRISON_CELLS); pool->prison = prison_create(PRISON_CELLS);
if (!pool->prison) { if (!pool->prison) {
*error = "Error creating pool's bio prison"; *error = "Error creating pool's bio prison";
...@@ -1775,7 +1790,8 @@ static void __pool_dec(struct pool *pool) ...@@ -1775,7 +1790,8 @@ static void __pool_dec(struct pool *pool)
static struct pool *__pool_find(struct mapped_device *pool_md, static struct pool *__pool_find(struct mapped_device *pool_md,
struct block_device *metadata_dev, 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); struct pool *pool = __pool_table_lookup_metadata_dev(metadata_dev);
...@@ -1791,8 +1807,10 @@ static struct pool *__pool_find(struct mapped_device *pool_md, ...@@ -1791,8 +1807,10 @@ static struct pool *__pool_find(struct mapped_device *pool_md,
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
__pool_inc(pool); __pool_inc(pool);
} else } else {
pool = pool_create(pool_md, metadata_dev, block_size, error); pool = pool_create(pool_md, metadata_dev, block_size, error);
*created = 1;
}
} }
return pool; return pool;
...@@ -1816,10 +1834,6 @@ static void pool_dtr(struct dm_target *ti) ...@@ -1816,10 +1834,6 @@ static void pool_dtr(struct dm_target *ti)
mutex_unlock(&dm_thin_pool_table.mutex); 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, static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
struct dm_target *ti) struct dm_target *ti)
{ {
...@@ -1828,7 +1842,7 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf, ...@@ -1828,7 +1842,7 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
const char *arg_name; const char *arg_name;
static struct dm_arg _args[] = { static struct dm_arg _args[] = {
{0, 1, "Invalid number of pool feature arguments"}, {0, 3, "Invalid number of pool feature arguments"},
}; };
/* /*
...@@ -1848,6 +1862,12 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf, ...@@ -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")) { if (!strcasecmp(arg_name, "skip_block_zeroing")) {
pf->zero_new_blocks = 0; pf->zero_new_blocks = 0;
continue; 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"; ti->error = "Unrecognised pool feature requested";
...@@ -1865,10 +1885,12 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf, ...@@ -1865,10 +1885,12 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
* *
* Optional feature arguments are: * Optional feature arguments are:
* skip_block_zeroing: skips the zeroing of newly-provisioned blocks. * 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) 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_c *pt;
struct pool *pool; struct pool *pool;
struct pool_features pf; struct pool_features pf;
...@@ -1928,8 +1950,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv) ...@@ -1928,8 +1950,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
/* /*
* Set default pool features. * Set default pool features.
*/ */
memset(&pf, 0, sizeof(pf)); pool_features_init(&pf);
pf.zero_new_blocks = 1;
dm_consume_args(&as, 4); dm_consume_args(&as, 4);
r = parse_pool_features(&as, &pf, ti); r = parse_pool_features(&as, &pf, ti);
...@@ -1943,21 +1964,58 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv) ...@@ -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, 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)) { if (IS_ERR(pool)) {
r = PTR_ERR(pool); r = PTR_ERR(pool);
goto out_free_pt; 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->pool = pool;
pt->ti = ti; pt->ti = ti;
pt->metadata_dev = metadata_dev; pt->metadata_dev = metadata_dev;
pt->data_dev = data_dev; pt->data_dev = data_dev;
pt->low_water_blocks = low_water_blocks; 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_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; ti->private = pt;
pt->callbacks.congested_fn = pool_is_congested; pt->callbacks.congested_fn = pool_is_congested;
...@@ -1967,6 +2025,8 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv) ...@@ -1967,6 +2025,8 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
return 0; return 0;
out_flags_changed:
__pool_dec(pool);
out_free_pt: out_free_pt:
kfree(pt); kfree(pt);
out: out:
...@@ -2255,7 +2315,7 @@ static int pool_message(struct dm_target *ti, unsigned argc, char **argv) ...@@ -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, static int pool_status(struct dm_target *ti, status_type_t type,
char *result, unsigned maxlen) char *result, unsigned maxlen)
{ {
int r; int r, count;
unsigned sz = 0; unsigned sz = 0;
uint64_t transaction_id; uint64_t transaction_id;
dm_block_t nr_free_blocks_data; dm_block_t nr_free_blocks_data;
...@@ -2318,10 +2378,19 @@ static int pool_status(struct dm_target *ti, status_type_t type, ...@@ -2318,10 +2378,19 @@ static int pool_status(struct dm_target *ti, status_type_t type,
(unsigned long)pool->sectors_per_block, (unsigned long)pool->sectors_per_block,
(unsigned long long)pt->low_water_blocks); (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 "); DMEMIT("skip_block_zeroing ");
if (!pool->pf.discard_enabled)
DMEMIT("ignore_discard ");
if (!pool->pf.discard_passdown)
DMEMIT("no_discard_passdown ");
break; break;
} }
...@@ -2352,6 +2421,9 @@ static int pool_merge(struct dm_target *ti, struct bvec_merge_data *bvm, ...@@ -2352,6 +2421,9 @@ 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) 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; limits->max_discard_sectors = pool->sectors_per_block;
/* /*
...@@ -2359,6 +2431,7 @@ static void set_discard_limits(struct pool *pool, struct queue_limits *limits) ...@@ -2359,6 +2431,7 @@ static void set_discard_limits(struct pool *pool, struct queue_limits *limits)
* bios that overlap 2 blocks. * bios that overlap 2 blocks.
*/ */
limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT; 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) static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
...@@ -2368,14 +2441,15 @@ static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits) ...@@ -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_min(limits, 0);
blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT); 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 = { static struct target_type pool_target = {
.name = "thin-pool", .name = "thin-pool",
.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE | .features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
DM_TARGET_IMMUTABLE, DM_TARGET_IMMUTABLE,
.version = {1, 0, 0}, .version = {1, 1, 0},
.module = THIS_MODULE, .module = THIS_MODULE,
.ctr = pool_ctr, .ctr = pool_ctr,
.dtr = pool_dtr, .dtr = pool_dtr,
...@@ -2417,6 +2491,9 @@ static void thin_dtr(struct dm_target *ti) ...@@ -2417,6 +2491,9 @@ static void thin_dtr(struct dm_target *ti)
* pool_dev: the path to the pool (eg, /dev/mapper/my_pool) * pool_dev: the path to the pool (eg, /dev/mapper/my_pool)
* dev_id: the internal device identifier * dev_id: the internal device identifier
* origin_dev: a device external to the pool that should act as the origin * 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) static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
{ {
...@@ -2485,8 +2562,12 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv) ...@@ -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->split_io = tc->pool->sectors_per_block;
ti->num_flush_requests = 1; 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); dm_put(pool_md);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册