aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Snitzer <snitzer@kernel.org>2023-06-01 13:39:56 -0400
committerMike Snitzer <snitzer@kernel.org>2023-06-09 13:33:09 -0400
commitbe100020551ba06ac9e0e6ea6bad1334fe2f3524 (patch)
tree585268888b8dc34f8ad6c751c63006bb74872b14
parent3ba45f766d40978394ea78b28eaed45301562bce (diff)
downloadlinux-dm-6.5-provision-support.tar.gz
dm thin: complete interface for REQ_OP_PROVISION supportdm-6.5-provision-support
Add disable_provision and no_provision_passdown features. But best to wait for support that properly provides guarantee upper layers (e.g. XFS) require: that all REQ_OP_PROVISION'd mappings are inherited at time of snapshot. Signed-off-by: Mike Snitzer <snitzer@kernel.org>
-rw-r--r--Documentation/admin-guide/device-mapper/thin-provisioning.rst16
-rw-r--r--drivers/md/dm-thin.c144
2 files changed, 130 insertions, 30 deletions
diff --git a/Documentation/admin-guide/device-mapper/thin-provisioning.rst b/Documentation/admin-guide/device-mapper/thin-provisioning.rst
index bafebf79da4b3d..4788973d8510f1 100644
--- a/Documentation/admin-guide/device-mapper/thin-provisioning.rst
+++ b/Documentation/admin-guide/device-mapper/thin-provisioning.rst
@@ -281,6 +281,13 @@ i) Constructor
Don't pass discards down to the underlying
data device, but just remove the mapping.
+ ignore_provision:
+ Disable provision support.
+
+ no_provision_passdown:
+ Don't pass provisions down to the underlying
+ data device, but just add the mapping.
+
read_only:
Don't allow any changes to be made to the pool
metadata. This mode is only available after the
@@ -301,8 +308,8 @@ ii) Status
<transaction id> <used metadata blocks>/<total metadata blocks>
<used data blocks>/<total data blocks> <held metadata root>
- ro|rw|out_of_data_space [no_]discard_passdown [error|queue]_if_no_space
- needs_check|- metadata_low_watermark
+ ro|rw|out_of_data_space [no_]discard_passdown [no_]provision_passdown
+ [error|queue]_if_no_space needs_check|- metadata_low_watermark
transaction id:
A 64-bit number used by userspace to help synchronise with metadata
@@ -324,6 +331,11 @@ ii) Status
underlying device. When this is enabled when loading the table,
it can get disabled if the underlying device doesn't support it.
+ provision_passdown|no_provision_passdown
+ Whether or not provisions are actually being passed down to the
+ underlying device. When this is enabled when loading the table,
+ it can get disabled if the underlying device doesn't support it.
+
ro|rw|out_of_data_space
If the pool encounters certain types of device failures it will
drop into a read-only metadata mode in which no changes to
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index 2525172e38744e..ad97c02ca07555 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -219,6 +219,8 @@ struct pool_features {
bool zero_new_blocks:1;
bool discard_enabled:1;
bool discard_passdown:1;
+ bool provision_enabled:1;
+ bool provision_passdown:1;
bool error_if_no_space:1;
};
@@ -1325,8 +1327,10 @@ static void schedule_copy(struct thin_c *tc, dm_block_t virt_block,
m->data_block = data_dest;
m->cell = cell;
- if (bio_op(bio) == REQ_OP_PROVISION)
- m->bio = bio;
+ if (bio_op(bio) == REQ_OP_PROVISION) {
+ if (!pool->pf.provision_passdown)
+ m->bio = bio; /* no_provision_passdown */
+ }
/*
* quiesce action + copy action + an extra reference held for the
@@ -1397,8 +1401,10 @@ static void schedule_zero(struct thin_c *tc, dm_block_t virt_block,
m->data_block = data_block;
m->cell = cell;
- if (bio && bio_op(bio) == REQ_OP_PROVISION)
- m->bio = bio;
+ if (bio && bio_op(bio) == REQ_OP_PROVISION) {
+ if (!pool->pf.provision_passdown)
+ m->bio = bio; /* no_provision_passdown */
+ }
/*
* If the whole block of data is being overwritten or we are not
@@ -1969,6 +1975,15 @@ static void process_provision_bio(struct thin_c *tc, struct bio *bio)
struct dm_thin_lookup_result lookup_result;
/*
+ * FIXME:
+ * Current REQ_OP_PROVISION support does _not_ make any guarantee
+ * that a snapshot device inherits all previous reservations.
+ *
+ * Future support will implement this reservation design:
+ * https://listman.redhat.com/archives/dm-devel/2023-May/054351.html
+ */
+
+ /*
* If cell is already occupied, then the block is already
* being provisioned so we have nothing further to do here.
*/
@@ -2582,16 +2597,11 @@ static void noflush_work(struct thin_c *tc, void (*fn)(struct work_struct *))
/*----------------------------------------------------------------*/
-static bool passdown_enabled(struct pool_c *pt)
-{
- return pt->adjusted_pf.discard_passdown;
-}
-
static void set_discard_callbacks(struct pool *pool)
{
struct pool_c *pt = pool->ti->private;
- if (passdown_enabled(pt)) {
+ if (pt->adjusted_pf.discard_passdown) {
pool->process_discard_cell = process_discard_cell_passdown;
pool->process_prepared_discard = process_prepared_discard_passdown_pt1;
pool->process_prepared_discard_pt2 = process_prepared_discard_passdown_pt2;
@@ -2905,7 +2915,7 @@ static bool is_factor(sector_t block_size, uint32_t n)
* If discard_passdown was enabled verify that the data device
* supports discards. Disable discard_passdown if not.
*/
-static void disable_passdown_if_not_supported(struct pool_c *pt)
+static void disable_discard_passdown_if_not_supported(struct pool_c *pt)
{
struct pool *pool = pt->pool;
struct block_device *data_bdev = pt->data_dev->bdev;
@@ -2927,6 +2937,32 @@ static void disable_passdown_if_not_supported(struct pool_c *pt)
}
}
+/*
+ * If provision_passdown was enabled verify that the data device
+ * supports provisions. Disable provision_passdown if not.
+ */
+static void disable_provision_passdown_if_not_supported(struct pool_c *pt)
+{
+ struct pool *pool = pt->pool;
+ struct block_device *data_bdev = pt->data_dev->bdev;
+ struct queue_limits *data_limits = &bdev_get_queue(data_bdev)->limits;
+ const char *reason = NULL;
+
+ if (!pt->adjusted_pf.provision_passdown)
+ return;
+
+ if (!bdev_max_provision_sectors(pt->data_dev->bdev))
+ reason = "provision unsupported";
+
+ else if (data_limits->max_provision_sectors < pool->sectors_per_block)
+ reason = "max provision sectors smaller than a block";
+
+ if (reason) {
+ DMWARN("Data device (%pg) %s: Disabling provision passdown.", data_bdev, reason);
+ pt->adjusted_pf.provision_passdown = false;
+ }
+}
+
static int bind_control_target(struct pool *pool, struct dm_target *ti)
{
struct pool_c *pt = ti->private;
@@ -2971,6 +3007,8 @@ static void pool_features_init(struct pool_features *pf)
pf->zero_new_blocks = true;
pf->discard_enabled = true;
pf->discard_passdown = true;
+ pf->provision_enabled = true;
+ pf->provision_passdown = true;
pf->error_if_no_space = false;
}
@@ -3215,7 +3253,7 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
const char *arg_name;
static const struct dm_arg _args[] = {
- {0, 4, "Invalid number of pool feature arguments"},
+ {0, 5, "Invalid number of pool feature arguments"},
};
/*
@@ -3241,6 +3279,12 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
else if (!strcasecmp(arg_name, "no_discard_passdown"))
pf->discard_passdown = false;
+ else if (!strcasecmp(arg_name, "ignore_provision"))
+ pf->provision_enabled = false;
+
+ else if (!strcasecmp(arg_name, "no_provision_passdown"))
+ pf->provision_passdown = false;
+
else if (!strcasecmp(arg_name, "read_only"))
pf->mode = PM_READ_ONLY;
@@ -3437,16 +3481,23 @@ static int pool_ctr(struct dm_target *ti, unsigned int argc, char **argv)
/*
* '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.
+ * Top level discard and provision 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 (!pool_created && pf.provision_enabled != pool->pf.provision_enabled) {
+ ti->error = "Provision support cannot be disabled once enabled";
+ r = -EINVAL;
+ goto out_flags_changed;
+ }
+
pt->pool = pool;
pt->ti = ti;
pt->metadata_dev = metadata_dev;
@@ -3455,9 +3506,6 @@ static int pool_ctr(struct dm_target *ti, unsigned int argc, char **argv)
pt->adjusted_pf = pt->requested_pf = pf;
ti->num_flush_bios = 1;
ti->limit_swap_bios = true;
- ti->num_provision_bios = 1;
- ti->provision_supported = true;
- ti->max_provision_granularity = true;
/*
* Only need to enable discards if the pool should pass
@@ -3474,6 +3522,18 @@ static int pool_ctr(struct dm_target *ti, unsigned int argc, char **argv)
ti->discards_supported = true;
ti->max_discard_granularity = true;
}
+
+ /*
+ * Only need to enable provisions if the pool should pass
+ * them down to the data device. The thin device's provision
+ * processing will cause mappings to be added to the btree.
+ */
+ if (pf.provision_enabled && pf.provision_passdown) {
+ ti->num_provision_bios = 1;
+ ti->provision_supported = true;
+ ti->max_provision_granularity = true;
+ }
+
ti->private = pt;
r = dm_pool_register_metadata_threshold(pt->pool->pmd,
@@ -3953,9 +4013,10 @@ static int pool_message(struct dm_target *ti, unsigned int argc, char **argv,
static void emit_flags(struct pool_features *pf, char *result,
unsigned int sz, unsigned int maxlen)
{
- unsigned int count = !pf->zero_new_blocks + !pf->discard_enabled +
- !pf->discard_passdown + (pf->mode == PM_READ_ONLY) +
- pf->error_if_no_space;
+ unsigned int count = !pf->zero_new_blocks +
+ !pf->discard_enabled + !pf->discard_passdown +
+ !pf->provision_enabled + !pf->provision_passdown +
+ (pf->mode == PM_READ_ONLY) + pf->error_if_no_space;
DMEMIT("%u ", count);
if (!pf->zero_new_blocks)
@@ -3967,6 +4028,12 @@ static void emit_flags(struct pool_features *pf, char *result,
if (!pf->discard_passdown)
DMEMIT("no_discard_passdown ");
+ if (!pf->provision_enabled)
+ DMEMIT("ignore_provision ");
+
+ if (!pf->provision_passdown)
+ DMEMIT("no_provision_passdown ");
+
if (pf->mode == PM_READ_ONLY)
DMEMIT("read_only ");
@@ -4077,6 +4144,13 @@ static void pool_status(struct dm_target *ti, status_type_t type,
else
DMEMIT("no_discard_passdown ");
+ if (!pool->pf.provision_enabled)
+ DMEMIT("ignore_provision ");
+ else if (pool->pf.provision_passdown)
+ DMEMIT("provision_passdown ");
+ else
+ DMEMIT("no_provision_passdown ");
+
if (pool->pf.error_if_no_space)
DMEMIT("error_if_no_space ");
else
@@ -4154,8 +4228,6 @@ static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT);
}
- limits->max_provision_sectors = pool->sectors_per_block;
-
/*
* pt->adjusted_pf is a staging area for the actual features to use.
* They get transferred to the live pool in bind_control_target()
@@ -4163,7 +4235,7 @@ static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
*/
if (pt->adjusted_pf.discard_enabled) {
- disable_passdown_if_not_supported(pt);
+ disable_discard_passdown_if_not_supported(pt);
/*
* The pool uses the same discard limits as the underlying data
* device. DM core has already set this up.
@@ -4175,6 +4247,18 @@ static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
*/
limits->discard_granularity = 0;
}
+
+ if (pt->adjusted_pf.provision_enabled) {
+ disable_provision_passdown_if_not_supported(pt);
+ if (!pt->adjusted_pf.provision_passdown)
+ limits->max_provision_sectors = 0;
+ } else {
+ /*
+ * Must explicitly disallow stacking provision limits otherwise the
+ * block layer will stack them if pool's data device has support.
+ */
+ limits->max_provision_sectors = 0;
+ }
}
static struct target_type pool_target = {
@@ -4342,16 +4426,17 @@ static int thin_ctr(struct dm_target *ti, unsigned int argc, char **argv)
ti->accounts_remapped_io = true;
ti->per_io_data_size = sizeof(struct dm_thin_endio_hook);
- /* In case the pool supports discards, pass them on. */
if (tc->pool->pf.discard_enabled) {
ti->discards_supported = true;
ti->num_discard_bios = 1;
ti->max_discard_granularity = true;
}
- ti->num_provision_bios = 1;
- ti->provision_supported = true;
- ti->max_provision_granularity = true;
+ if (tc->pool->pf.provision_enabled) {
+ ti->provision_supported = true;
+ ti->num_provision_bios = 1;
+ ti->max_provision_granularity = true;
+ }
mutex_unlock(&dm_thin_pool_table.mutex);
@@ -4566,6 +4651,9 @@ static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
limits->max_discard_sectors = pool->sectors_per_block * BIO_PRISON_MAX_RANGE;
}
+
+ if (pool->pf.provision_enabled)
+ limits->max_provision_sectors = pool->sectors_per_block;
}
static struct target_type thin_target = {