Commit 971888c4 authored by Mike Snitzer's avatar Mike Snitzer

dm: hold DM table for duration of ioctl rather than use blkdev_get

Commit 519049af ("dm: use blkdev_get rather than bdgrab when issuing
pass-through ioctl") inadvertantly introduced a regression relative to
users of device cgroups that issue ioctls (e.g. libvirt).  Using
blkdev_get() in DM's passthrough ioctl support implicitly introduced a
cgroup permissions check that would fail unless care were taken to add
all devices in the IO stack to the device cgroup.  E.g. rather than just
adding the top-level DM multipath device to the cgroup all the
underlying devices would need to be allowed.

Fix this, to no longer require allowing all underlying devices, by
simply holding the live DM table (which includes the table's original
blkdev_get() reference on the blockdevice that the ioctl will be issued
to) for the duration of the ioctl.

Also, bump the DM ioctl version so a user can know that their device
cgroup allow workaround is no longer needed.
Reported-by: default avatarMichal Privoznik <mprivozn@redhat.com>
Suggested-by: default avatarMikulas Patocka <mpatocka@redhat.com>
Fixes: 519049af ("dm: use blkdev_get rather than bdgrab when issuing pass-through ioctl")
Cc: stable@vger.kernel.org # 4.16
Signed-off-by: default avatarMike Snitzer <snitzer@redhat.com>
parent 13bc62d4
...@@ -458,67 +458,56 @@ static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) ...@@ -458,67 +458,56 @@ static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
return dm_get_geometry(md, geo); return dm_get_geometry(md, geo);
} }
static char *_dm_claim_ptr = "I belong to device-mapper"; static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx,
struct block_device **bdev, fmode_t *mode)
static int dm_get_bdev_for_ioctl(struct mapped_device *md, __acquires(md->io_barrier)
struct block_device **bdev,
fmode_t *mode)
{ {
struct dm_target *tgt; struct dm_target *tgt;
struct dm_table *map; struct dm_table *map;
int srcu_idx, r, r2; int r;
retry: retry:
r = -ENOTTY; r = -ENOTTY;
map = dm_get_live_table(md, &srcu_idx); map = dm_get_live_table(md, srcu_idx);
if (!map || !dm_table_get_size(map)) if (!map || !dm_table_get_size(map))
goto out; return r;
/* We only support devices that have a single target */ /* We only support devices that have a single target */
if (dm_table_get_num_targets(map) != 1) if (dm_table_get_num_targets(map) != 1)
goto out; return r;
tgt = dm_table_get_target(map, 0); tgt = dm_table_get_target(map, 0);
if (!tgt->type->prepare_ioctl) if (!tgt->type->prepare_ioctl)
goto out; return r;
if (dm_suspended_md(md)) { if (dm_suspended_md(md))
r = -EAGAIN; return -EAGAIN;
goto out;
}
r = tgt->type->prepare_ioctl(tgt, bdev, mode); r = tgt->type->prepare_ioctl(tgt, bdev, mode);
if (r < 0)
goto out;
bdgrab(*bdev);
r2 = blkdev_get(*bdev, *mode, _dm_claim_ptr);
if (r2 < 0) {
r = r2;
goto out;
}
dm_put_live_table(md, srcu_idx);
return r;
out:
dm_put_live_table(md, srcu_idx);
if (r == -ENOTCONN && !fatal_signal_pending(current)) { if (r == -ENOTCONN && !fatal_signal_pending(current)) {
dm_put_live_table(md, *srcu_idx);
msleep(10); msleep(10);
goto retry; goto retry;
} }
return r; return r;
} }
static void dm_unprepare_ioctl(struct mapped_device *md, int srcu_idx)
__releases(md->io_barrier)
{
dm_put_live_table(md, srcu_idx);
}
static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode, static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
struct mapped_device *md = bdev->bd_disk->private_data; struct mapped_device *md = bdev->bd_disk->private_data;
int r; int r, srcu_idx;
r = dm_get_bdev_for_ioctl(md, &bdev, &mode); r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode);
if (r < 0) if (r < 0)
return r; goto out;
if (r > 0) { if (r > 0) {
/* /*
...@@ -536,7 +525,7 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode, ...@@ -536,7 +525,7 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
r = __blkdev_driver_ioctl(bdev, mode, cmd, arg); r = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
out: out:
blkdev_put(bdev, mode); dm_unprepare_ioctl(md, srcu_idx);
return r; return r;
} }
...@@ -710,6 +699,8 @@ static void dm_put_live_table_fast(struct mapped_device *md) __releases(RCU) ...@@ -710,6 +699,8 @@ static void dm_put_live_table_fast(struct mapped_device *md) __releases(RCU)
rcu_read_unlock(); rcu_read_unlock();
} }
static char *_dm_claim_ptr = "I belong to device-mapper";
/* /*
* Open a table device so we can use it as a map destination. * Open a table device so we can use it as a map destination.
*/ */
...@@ -3044,19 +3035,19 @@ static int dm_pr_reserve(struct block_device *bdev, u64 key, enum pr_type type, ...@@ -3044,19 +3035,19 @@ static int dm_pr_reserve(struct block_device *bdev, u64 key, enum pr_type type,
struct mapped_device *md = bdev->bd_disk->private_data; struct mapped_device *md = bdev->bd_disk->private_data;
const struct pr_ops *ops; const struct pr_ops *ops;
fmode_t mode; fmode_t mode;
int r; int r, srcu_idx;
r = dm_get_bdev_for_ioctl(md, &bdev, &mode); r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode);
if (r < 0) if (r < 0)
return r; goto out;
ops = bdev->bd_disk->fops->pr_ops; ops = bdev->bd_disk->fops->pr_ops;
if (ops && ops->pr_reserve) if (ops && ops->pr_reserve)
r = ops->pr_reserve(bdev, key, type, flags); r = ops->pr_reserve(bdev, key, type, flags);
else else
r = -EOPNOTSUPP; r = -EOPNOTSUPP;
out:
blkdev_put(bdev, mode); dm_unprepare_ioctl(md, srcu_idx);
return r; return r;
} }
...@@ -3065,19 +3056,19 @@ static int dm_pr_release(struct block_device *bdev, u64 key, enum pr_type type) ...@@ -3065,19 +3056,19 @@ static int dm_pr_release(struct block_device *bdev, u64 key, enum pr_type type)
struct mapped_device *md = bdev->bd_disk->private_data; struct mapped_device *md = bdev->bd_disk->private_data;
const struct pr_ops *ops; const struct pr_ops *ops;
fmode_t mode; fmode_t mode;
int r; int r, srcu_idx;
r = dm_get_bdev_for_ioctl(md, &bdev, &mode); r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode);
if (r < 0) if (r < 0)
return r; goto out;
ops = bdev->bd_disk->fops->pr_ops; ops = bdev->bd_disk->fops->pr_ops;
if (ops && ops->pr_release) if (ops && ops->pr_release)
r = ops->pr_release(bdev, key, type); r = ops->pr_release(bdev, key, type);
else else
r = -EOPNOTSUPP; r = -EOPNOTSUPP;
out:
blkdev_put(bdev, mode); dm_unprepare_ioctl(md, srcu_idx);
return r; return r;
} }
...@@ -3087,19 +3078,19 @@ static int dm_pr_preempt(struct block_device *bdev, u64 old_key, u64 new_key, ...@@ -3087,19 +3078,19 @@ static int dm_pr_preempt(struct block_device *bdev, u64 old_key, u64 new_key,
struct mapped_device *md = bdev->bd_disk->private_data; struct mapped_device *md = bdev->bd_disk->private_data;
const struct pr_ops *ops; const struct pr_ops *ops;
fmode_t mode; fmode_t mode;
int r; int r, srcu_idx;
r = dm_get_bdev_for_ioctl(md, &bdev, &mode); r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode);
if (r < 0) if (r < 0)
return r; goto out;
ops = bdev->bd_disk->fops->pr_ops; ops = bdev->bd_disk->fops->pr_ops;
if (ops && ops->pr_preempt) if (ops && ops->pr_preempt)
r = ops->pr_preempt(bdev, old_key, new_key, type, abort); r = ops->pr_preempt(bdev, old_key, new_key, type, abort);
else else
r = -EOPNOTSUPP; r = -EOPNOTSUPP;
out:
blkdev_put(bdev, mode); dm_unprepare_ioctl(md, srcu_idx);
return r; return r;
} }
...@@ -3108,19 +3099,19 @@ static int dm_pr_clear(struct block_device *bdev, u64 key) ...@@ -3108,19 +3099,19 @@ static int dm_pr_clear(struct block_device *bdev, u64 key)
struct mapped_device *md = bdev->bd_disk->private_data; struct mapped_device *md = bdev->bd_disk->private_data;
const struct pr_ops *ops; const struct pr_ops *ops;
fmode_t mode; fmode_t mode;
int r; int r, srcu_idx;
r = dm_get_bdev_for_ioctl(md, &bdev, &mode); r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode);
if (r < 0) if (r < 0)
return r; goto out;
ops = bdev->bd_disk->fops->pr_ops; ops = bdev->bd_disk->fops->pr_ops;
if (ops && ops->pr_clear) if (ops && ops->pr_clear)
r = ops->pr_clear(bdev, key); r = ops->pr_clear(bdev, key);
else else
r = -EOPNOTSUPP; r = -EOPNOTSUPP;
out:
blkdev_put(bdev, mode); dm_unprepare_ioctl(md, srcu_idx);
return r; return r;
} }
......
...@@ -270,9 +270,9 @@ enum { ...@@ -270,9 +270,9 @@ enum {
#define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
#define DM_VERSION_MAJOR 4 #define DM_VERSION_MAJOR 4
#define DM_VERSION_MINOR 38 #define DM_VERSION_MINOR 39
#define DM_VERSION_PATCHLEVEL 0 #define DM_VERSION_PATCHLEVEL 0
#define DM_VERSION_EXTRA "-ioctl (2018-02-28)" #define DM_VERSION_EXTRA "-ioctl (2018-04-03)"
/* Status bits */ /* Status bits */
#define DM_READONLY_FLAG (1 << 0) /* In/Out */ #define DM_READONLY_FLAG (1 << 0) /* In/Out */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment