Skip to content

Commit 971888c

Browse files
committed
dm: hold DM table for duration of ioctl rather than use blkdev_get
Commit 519049a ("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: Michal Privoznik <mprivozn@redhat.com> Suggested-by: Mikulas Patocka <mpatocka@redhat.com> Fixes: 519049a ("dm: use blkdev_get rather than bdgrab when issuing pass-through ioctl") Cc: stable@vger.kernel.org # 4.16 Signed-off-by: Mike Snitzer <snitzer@redhat.com>
1 parent 13bc62d commit 971888c

File tree

2 files changed

+46
-55
lines changed

2 files changed

+46
-55
lines changed

drivers/md/dm.c

+44-53
Original file line numberDiff line numberDiff line change
@@ -458,67 +458,56 @@ static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
458458
return dm_get_geometry(md, geo);
459459
}
460460

461-
static char *_dm_claim_ptr = "I belong to device-mapper";
462-
463-
static int dm_get_bdev_for_ioctl(struct mapped_device *md,
464-
struct block_device **bdev,
465-
fmode_t *mode)
461+
static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx,
462+
struct block_device **bdev, fmode_t *mode)
463+
__acquires(md->io_barrier)
466464
{
467465
struct dm_target *tgt;
468466
struct dm_table *map;
469-
int srcu_idx, r, r2;
467+
int r;
470468

471469
retry:
472470
r = -ENOTTY;
473-
map = dm_get_live_table(md, &srcu_idx);
471+
map = dm_get_live_table(md, srcu_idx);
474472
if (!map || !dm_table_get_size(map))
475-
goto out;
473+
return r;
476474

477475
/* We only support devices that have a single target */
478476
if (dm_table_get_num_targets(map) != 1)
479-
goto out;
477+
return r;
480478

481479
tgt = dm_table_get_target(map, 0);
482480
if (!tgt->type->prepare_ioctl)
483-
goto out;
481+
return r;
484482

485-
if (dm_suspended_md(md)) {
486-
r = -EAGAIN;
487-
goto out;
488-
}
483+
if (dm_suspended_md(md))
484+
return -EAGAIN;
489485

490486
r = tgt->type->prepare_ioctl(tgt, bdev, mode);
491-
if (r < 0)
492-
goto out;
493-
494-
bdgrab(*bdev);
495-
r2 = blkdev_get(*bdev, *mode, _dm_claim_ptr);
496-
if (r2 < 0) {
497-
r = r2;
498-
goto out;
499-
}
500-
501-
dm_put_live_table(md, srcu_idx);
502-
return r;
503-
504-
out:
505-
dm_put_live_table(md, srcu_idx);
506487
if (r == -ENOTCONN && !fatal_signal_pending(current)) {
488+
dm_put_live_table(md, *srcu_idx);
507489
msleep(10);
508490
goto retry;
509491
}
492+
510493
return r;
511494
}
512495

496+
static void dm_unprepare_ioctl(struct mapped_device *md, int srcu_idx)
497+
__releases(md->io_barrier)
498+
{
499+
dm_put_live_table(md, srcu_idx);
500+
}
501+
513502
static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
514503
unsigned int cmd, unsigned long arg)
515504
{
516505
struct mapped_device *md = bdev->bd_disk->private_data;
517-
int r;
506+
int r, srcu_idx;
518507

519-
r = dm_get_bdev_for_ioctl(md, &bdev, &mode);
508+
r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode);
520509
if (r < 0)
521-
return r;
510+
goto out;
522511

523512
if (r > 0) {
524513
/*
@@ -536,7 +525,7 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
536525

537526
r = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
538527
out:
539-
blkdev_put(bdev, mode);
528+
dm_unprepare_ioctl(md, srcu_idx);
540529
return r;
541530
}
542531

@@ -710,6 +699,8 @@ static void dm_put_live_table_fast(struct mapped_device *md) __releases(RCU)
710699
rcu_read_unlock();
711700
}
712701

702+
static char *_dm_claim_ptr = "I belong to device-mapper";
703+
713704
/*
714705
* Open a table device so we can use it as a map destination.
715706
*/
@@ -3044,19 +3035,19 @@ static int dm_pr_reserve(struct block_device *bdev, u64 key, enum pr_type type,
30443035
struct mapped_device *md = bdev->bd_disk->private_data;
30453036
const struct pr_ops *ops;
30463037
fmode_t mode;
3047-
int r;
3038+
int r, srcu_idx;
30483039

3049-
r = dm_get_bdev_for_ioctl(md, &bdev, &mode);
3040+
r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode);
30503041
if (r < 0)
3051-
return r;
3042+
goto out;
30523043

30533044
ops = bdev->bd_disk->fops->pr_ops;
30543045
if (ops && ops->pr_reserve)
30553046
r = ops->pr_reserve(bdev, key, type, flags);
30563047
else
30573048
r = -EOPNOTSUPP;
3058-
3059-
blkdev_put(bdev, mode);
3049+
out:
3050+
dm_unprepare_ioctl(md, srcu_idx);
30603051
return r;
30613052
}
30623053

@@ -3065,19 +3056,19 @@ static int dm_pr_release(struct block_device *bdev, u64 key, enum pr_type type)
30653056
struct mapped_device *md = bdev->bd_disk->private_data;
30663057
const struct pr_ops *ops;
30673058
fmode_t mode;
3068-
int r;
3059+
int r, srcu_idx;
30693060

3070-
r = dm_get_bdev_for_ioctl(md, &bdev, &mode);
3061+
r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode);
30713062
if (r < 0)
3072-
return r;
3063+
goto out;
30733064

30743065
ops = bdev->bd_disk->fops->pr_ops;
30753066
if (ops && ops->pr_release)
30763067
r = ops->pr_release(bdev, key, type);
30773068
else
30783069
r = -EOPNOTSUPP;
3079-
3080-
blkdev_put(bdev, mode);
3070+
out:
3071+
dm_unprepare_ioctl(md, srcu_idx);
30813072
return r;
30823073
}
30833074

@@ -3087,19 +3078,19 @@ static int dm_pr_preempt(struct block_device *bdev, u64 old_key, u64 new_key,
30873078
struct mapped_device *md = bdev->bd_disk->private_data;
30883079
const struct pr_ops *ops;
30893080
fmode_t mode;
3090-
int r;
3081+
int r, srcu_idx;
30913082

3092-
r = dm_get_bdev_for_ioctl(md, &bdev, &mode);
3083+
r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode);
30933084
if (r < 0)
3094-
return r;
3085+
goto out;
30953086

30963087
ops = bdev->bd_disk->fops->pr_ops;
30973088
if (ops && ops->pr_preempt)
30983089
r = ops->pr_preempt(bdev, old_key, new_key, type, abort);
30993090
else
31003091
r = -EOPNOTSUPP;
3101-
3102-
blkdev_put(bdev, mode);
3092+
out:
3093+
dm_unprepare_ioctl(md, srcu_idx);
31033094
return r;
31043095
}
31053096

@@ -3108,19 +3099,19 @@ static int dm_pr_clear(struct block_device *bdev, u64 key)
31083099
struct mapped_device *md = bdev->bd_disk->private_data;
31093100
const struct pr_ops *ops;
31103101
fmode_t mode;
3111-
int r;
3102+
int r, srcu_idx;
31123103

3113-
r = dm_get_bdev_for_ioctl(md, &bdev, &mode);
3104+
r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode);
31143105
if (r < 0)
3115-
return r;
3106+
goto out;
31163107

31173108
ops = bdev->bd_disk->fops->pr_ops;
31183109
if (ops && ops->pr_clear)
31193110
r = ops->pr_clear(bdev, key);
31203111
else
31213112
r = -EOPNOTSUPP;
3122-
3123-
blkdev_put(bdev, mode);
3113+
out:
3114+
dm_unprepare_ioctl(md, srcu_idx);
31243115
return r;
31253116
}
31263117

include/uapi/linux/dm-ioctl.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,9 @@ enum {
270270
#define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
271271

272272
#define DM_VERSION_MAJOR 4
273-
#define DM_VERSION_MINOR 38
273+
#define DM_VERSION_MINOR 39
274274
#define DM_VERSION_PATCHLEVEL 0
275-
#define DM_VERSION_EXTRA "-ioctl (2018-02-28)"
275+
#define DM_VERSION_EXTRA "-ioctl (2018-04-03)"
276276

277277
/* Status bits */
278278
#define DM_READONLY_FLAG (1 << 0) /* In/Out */

0 commit comments

Comments
 (0)