Skip to content

Commit

Permalink
Adds blk_interposer to md.
Browse files Browse the repository at this point in the history
* The new flag DM_INTERPOSE_FLAG allows to specify that the dm target
will be attached using blk_interposer.
* The [interpose] option allows to specify which device will be
attached via the interposer.
* The connection and disconnection of the interrupter is performed in
the functions __dm_suspend() and __dm_resume(). The flag
DM_SUSPEND_DETACH_IP_FLAG was added for this purpose.
* dm_submit_bio() sets BIO_INTERPOSED for each bio from the interposer.

Signed-off-by: Sergei Shtepa <sergei.shtepa@veeam.com>
  • Loading branch information
SergeiShtepa authored and intel-lab-lkp committed Apr 9, 2021
1 parent 397ac13 commit df79fb3
Show file tree
Hide file tree
Showing 7 changed files with 375 additions and 58 deletions.
1 change: 1 addition & 0 deletions drivers/md/dm-core.h
Expand Up @@ -112,6 +112,7 @@ struct mapped_device {
/* for blk-mq request-based DM support */
struct blk_mq_tag_set *tag_set;
bool init_tio_pdu:1;
bool interpose:1;

struct srcu_struct io_barrier;
};
Expand Down
95 changes: 72 additions & 23 deletions drivers/md/dm-ioctl.c
Expand Up @@ -294,11 +294,29 @@ static void dm_hash_remove_all(bool keep_open_devices, bool mark_deferred, bool
md = hc->md;
dm_get(md);

if (keep_open_devices &&
dm_lock_for_deletion(md, mark_deferred, only_deferred)) {
dm_put(md);
dev_skipped++;
continue;
if (md->interpose) {
int r;

/*
* Interposer should be suspended and detached
* from the interposed block device.
*/
r = dm_suspend(md, DM_SUSPEND_DETACH_IP_FLAG |
DM_SUSPEND_LOCKFS_FLAG);
if (r) {
DMERR("%s: unable to suspend and detach interposer",
dm_device_name(md));
dm_put(md);
dev_skipped++;
continue;
}
} else {
if (keep_open_devices &&
dm_lock_for_deletion(md, mark_deferred, only_deferred)) {
dm_put(md);
dev_skipped++;
continue;
}
}

t = __hash_remove(hc);
Expand Down Expand Up @@ -732,6 +750,9 @@ static void __dev_status(struct mapped_device *md, struct dm_ioctl *param)
if (dm_test_deferred_remove_flag(md))
param->flags |= DM_DEFERRED_REMOVE;

if (dm_interposer_attached(md))
param->flags |= DM_INTERPOSE_FLAG;

param->dev = huge_encode_dev(disk_devt(disk));

/*
Expand Down Expand Up @@ -878,20 +899,37 @@ static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_si

md = hc->md;

/*
* Ensure the device is not open and nothing further can open it.
*/
r = dm_lock_for_deletion(md, !!(param->flags & DM_DEFERRED_REMOVE), false);
if (r) {
if (r == -EBUSY && param->flags & DM_DEFERRED_REMOVE) {
if (!md->interpose) {
/*
* Ensure the device is not open and nothing further can open it.
*/
r = dm_lock_for_deletion(md, !!(param->flags & DM_DEFERRED_REMOVE), false);
if (r) {
if (r == -EBUSY && param->flags & DM_DEFERRED_REMOVE) {
up_write(&_hash_lock);
dm_put(md);
return 0;
}
DMDEBUG_LIMIT("unable to remove open device %s",
hc->name);
up_write(&_hash_lock);
dm_put(md);
return 0;
return r;
}
} else {
/*
* Interposer should be suspended and detached from
* the interposed block device.
*/
r = dm_suspend(md, DM_SUSPEND_DETACH_IP_FLAG |
DM_SUSPEND_LOCKFS_FLAG);
if (r) {
DMERR("%s: unable to suspend and detach interposer",
dm_device_name(md));
up_write(&_hash_lock);
dm_put(md);
return r;
}
DMDEBUG_LIMIT("unable to remove open device %s", hc->name);
up_write(&_hash_lock);
dm_put(md);
return r;
}

t = __hash_remove(hc);
Expand Down Expand Up @@ -1050,6 +1088,7 @@ static int do_resume(struct dm_ioctl *param)

md = hc->md;


new_map = hc->new_map;
hc->new_map = NULL;
param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
Expand All @@ -1063,8 +1102,14 @@ static int do_resume(struct dm_ioctl *param)
suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
if (param->flags & DM_NOFLUSH_FLAG)
suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
if (!dm_suspended_md(md))
dm_suspend(md, suspend_flags);

if (md->interpose) {
if (!dm_suspended_md(md) || dm_interposer_attached(md))
dm_suspend(md, suspend_flags | DM_SUSPEND_DETACH_IP_FLAG);
} else {
if (!dm_suspended_md(md))
dm_suspend(md, suspend_flags);
}

old_map = dm_swap_table(md, new_map);
if (IS_ERR(old_map)) {
Expand Down Expand Up @@ -1267,6 +1312,11 @@ static inline fmode_t get_mode(struct dm_ioctl *param)
return mode;
}

static inline bool get_interpose_flag(struct dm_ioctl *param)
{
return (param->flags & DM_INTERPOSE_FLAG);
}

static int next_target(struct dm_target_spec *last, uint32_t next, void *end,
struct dm_target_spec **spec, char **target_params)
{
Expand All @@ -1289,11 +1339,6 @@ static int populate_table(struct dm_table *table,
void *end = (void *) param + param_size;
char *target_params;

if (!param->target_count) {
DMWARN("populate_table: no targets specified");
return -EINVAL;
}

for (i = 0; i < param->target_count; i++) {

r = next_target(spec, next, end, &spec, &target_params);
Expand Down Expand Up @@ -1338,6 +1383,8 @@ static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_si
if (!md)
return -ENXIO;

md->interpose = get_interpose_flag(param);

r = dm_table_create(&t, get_mode(param), param->target_count, md);
if (r)
goto err;
Expand Down Expand Up @@ -2098,6 +2145,8 @@ int __init dm_early_create(struct dm_ioctl *dmi,
if (r)
goto err_hash_remove;

md->interpose = get_interpose_flag(dmi);

/* add targets */
for (i = 0; i < dmi->target_count; i++) {
r = dm_table_add_target(t, spec_array[i]->target_type,
Expand Down
68 changes: 62 additions & 6 deletions drivers/md/dm-table.c
Expand Up @@ -327,14 +327,14 @@ static int device_area_is_invalid(struct dm_target *ti, struct dm_dev *dev,
* it is accessed concurrently.
*/
static int upgrade_mode(struct dm_dev_internal *dd, fmode_t new_mode,
struct mapped_device *md)
bool interpose, struct mapped_device *md)
{
int r;
struct dm_dev *old_dev, *new_dev;

old_dev = dd->dm_dev;

r = dm_get_table_device(md, dd->dm_dev->bdev->bd_dev,
r = dm_get_table_device(md, dd->dm_dev->bdev->bd_dev, interpose,
dd->dm_dev->mode | new_mode, &new_dev);
if (r)
return r;
Expand Down Expand Up @@ -367,20 +367,49 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
{
int r;
dev_t dev;
size_t ofs = 0;
bool interpose = false;
unsigned int major, minor;
char dummy;
struct dm_dev_internal *dd;
struct dm_table *t = ti->table;

BUG_ON(!t);

if (sscanf(path, "%u:%u%c", &major, &minor, &dummy) == 2) {
/*
* Extract extended options for device
*/
if (path[0] == '[') {
const char *interpose_opt = "interpose";
size_t opt_pos = 1;
size_t opt_len;

/*
* Because only one option is supported yet, the parser
* can be simplest.
*/
opt_len = strlen(interpose_opt);
if ((opt_pos + opt_len) < strlen(path) &&
memcmp(&path[opt_pos], interpose_opt, opt_len) == 0) {
interpose = true;

if (!t->md->interpose)
t->md->interpose = true;
} else {
DMERR("Invalid devices extended options %s", path);
return -EINVAL;
}

ofs = opt_pos + opt_len + 1;
}

if (sscanf(&path[ofs], "%u:%u%c", &major, &minor, &dummy) == 2) {
/* Extract the major/minor numbers */
dev = MKDEV(major, minor);
if (MAJOR(dev) != major || MINOR(dev) != minor)
return -EOVERFLOW;
} else {
dev = dm_get_dev_t(path);
dev = dm_get_dev_t(&path[ofs]);
if (!dev)
return -ENODEV;
}
Expand All @@ -391,7 +420,8 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
if (!dd)
return -ENOMEM;

if ((r = dm_get_table_device(t->md, dev, mode, &dd->dm_dev))) {
r = dm_get_table_device(t->md, dev, mode, interpose, &dd->dm_dev);
if (r) {
kfree(dd);
return r;
}
Expand All @@ -401,14 +431,40 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
goto out;

} else if (dd->dm_dev->mode != (mode | dd->dm_dev->mode)) {
r = upgrade_mode(dd, mode, t->md);
r = upgrade_mode(dd, mode, interpose, t->md);
if (r)
return r;
}
refcount_inc(&dd->count);
out:
if (interpose) {
struct block_device *original = dd->dm_dev->bdev;
/*
* Interposer target should cover all underlying device
*/
if (ti->begin != 0) {
DMERR("%s: target offset should be zero for dm interposer",
dm_device_name(t->md));
r = -EINVAL;
goto fail;
}
if (bdev_nr_sectors(original) != ti->len) {
DMERR("%s: interposer and interposed block device size should be equal",
dm_device_name(t->md));
r = -EINVAL;
goto fail;
}
}

*result = dd->dm_dev;
return 0;
fail:
if (refcount_dec_and_test(&dd->count)) {
dm_put_table_device(t->md, dd->dm_dev);
list_del(&dd->list);
kfree(dd);
}
return r;
}
EXPORT_SYMBOL(dm_get_device);

Expand Down

0 comments on commit df79fb3

Please sign in to comment.