aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Snitzer <snitzer@redhat.com>2018-04-03 15:05:12 -0400
committerMike Snitzer <snitzer@redhat.com>2018-04-04 12:12:38 -0400
commit971888c46993f871f20d02d1fe43486a924fad11 (patch)
treeb327b79b5e3915f7912dd7f48006df08b0028837
parent13bc62d4a6c79b95ec299591df1bae0a505f2d07 (diff)
dm: hold DM table for duration of ioctl rather than use blkdev_get
Commit 519049afead ("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: 519049afead ("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>
-rw-r--r--drivers/md/dm.c97
-rw-r--r--include/uapi/linux/dm-ioctl.h4
2 files changed, 46 insertions, 55 deletions
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 3b3cbd1a1659..d4438188d088 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -458,67 +458,56 @@ static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
458 return dm_get_geometry(md, geo); 458 return dm_get_geometry(md, geo);
459} 459}
460 460
461static char *_dm_claim_ptr = "I belong to device-mapper"; 461static int dm_prepare_ioctl(struct mapped_device *md, int *srcu_idx,
462 462 struct block_device **bdev, fmode_t *mode)
463static int dm_get_bdev_for_ioctl(struct mapped_device *md, 463 __acquires(md->io_barrier)
464 struct block_device **bdev,
465 fmode_t *mode)
466{ 464{
467 struct dm_target *tgt; 465 struct dm_target *tgt;
468 struct dm_table *map; 466 struct dm_table *map;
469 int srcu_idx, r, r2; 467 int r;
470 468
471retry: 469retry:
472 r = -ENOTTY; 470 r = -ENOTTY;
473 map = dm_get_live_table(md, &srcu_idx); 471 map = dm_get_live_table(md, srcu_idx);
474 if (!map || !dm_table_get_size(map)) 472 if (!map || !dm_table_get_size(map))
475 goto out; 473 return r;
476 474
477 /* We only support devices that have a single target */ 475 /* We only support devices that have a single target */
478 if (dm_table_get_num_targets(map) != 1) 476 if (dm_table_get_num_targets(map) != 1)
479 goto out; 477 return r;
480 478
481 tgt = dm_table_get_target(map, 0); 479 tgt = dm_table_get_target(map, 0);
482 if (!tgt->type->prepare_ioctl) 480 if (!tgt->type->prepare_ioctl)
483 goto out; 481 return r;
484 482
485 if (dm_suspended_md(md)) { 483 if (dm_suspended_md(md))
486 r = -EAGAIN; 484 return -EAGAIN;
487 goto out;
488 }
489 485
490 r = tgt->type->prepare_ioctl(tgt, bdev, mode); 486 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
504out:
505 dm_put_live_table(md, srcu_idx);
506 if (r == -ENOTCONN && !fatal_signal_pending(current)) { 487 if (r == -ENOTCONN && !fatal_signal_pending(current)) {
488 dm_put_live_table(md, *srcu_idx);
507 msleep(10); 489 msleep(10);
508 goto retry; 490 goto retry;
509 } 491 }
492
510 return r; 493 return r;
511} 494}
512 495
496static 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
513static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode, 502static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
514 unsigned int cmd, unsigned long arg) 503 unsigned int cmd, unsigned long arg)
515{ 504{
516 struct mapped_device *md = bdev->bd_disk->private_data; 505 struct mapped_device *md = bdev->bd_disk->private_data;
517 int r; 506 int r, srcu_idx;
518 507
519 r = dm_get_bdev_for_ioctl(md, &bdev, &mode); 508 r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode);
520 if (r < 0) 509 if (r < 0)
521 return r; 510 goto out;
522 511
523 if (r > 0) { 512 if (r > 0) {
524 /* 513 /*
@@ -536,7 +525,7 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
536 525
537 r = __blkdev_driver_ioctl(bdev, mode, cmd, arg); 526 r = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
538out: 527out:
539 blkdev_put(bdev, mode); 528 dm_unprepare_ioctl(md, srcu_idx);
540 return r; 529 return r;
541} 530}
542 531
@@ -710,6 +699,8 @@ static void dm_put_live_table_fast(struct mapped_device *md) __releases(RCU)
710 rcu_read_unlock(); 699 rcu_read_unlock();
711} 700}
712 701
702static char *_dm_claim_ptr = "I belong to device-mapper";
703
713/* 704/*
714 * Open a table device so we can use it as a map destination. 705 * Open a table device so we can use it as a map destination.
715 */ 706 */
@@ -3044,19 +3035,19 @@ static int dm_pr_reserve(struct block_device *bdev, u64 key, enum pr_type type,
3044 struct mapped_device *md = bdev->bd_disk->private_data; 3035 struct mapped_device *md = bdev->bd_disk->private_data;
3045 const struct pr_ops *ops; 3036 const struct pr_ops *ops;
3046 fmode_t mode; 3037 fmode_t mode;
3047 int r; 3038 int r, srcu_idx;
3048 3039
3049 r = dm_get_bdev_for_ioctl(md, &bdev, &mode); 3040 r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode);
3050 if (r < 0) 3041 if (r < 0)
3051 return r; 3042 goto out;
3052 3043
3053 ops = bdev->bd_disk->fops->pr_ops; 3044 ops = bdev->bd_disk->fops->pr_ops;
3054 if (ops && ops->pr_reserve) 3045 if (ops && ops->pr_reserve)
3055 r = ops->pr_reserve(bdev, key, type, flags); 3046 r = ops->pr_reserve(bdev, key, type, flags);
3056 else 3047 else
3057 r = -EOPNOTSUPP; 3048 r = -EOPNOTSUPP;
3058 3049out:
3059 blkdev_put(bdev, mode); 3050 dm_unprepare_ioctl(md, srcu_idx);
3060 return r; 3051 return r;
3061} 3052}
3062 3053
@@ -3065,19 +3056,19 @@ static int dm_pr_release(struct block_device *bdev, u64 key, enum pr_type type)
3065 struct mapped_device *md = bdev->bd_disk->private_data; 3056 struct mapped_device *md = bdev->bd_disk->private_data;
3066 const struct pr_ops *ops; 3057 const struct pr_ops *ops;
3067 fmode_t mode; 3058 fmode_t mode;
3068 int r; 3059 int r, srcu_idx;
3069 3060
3070 r = dm_get_bdev_for_ioctl(md, &bdev, &mode); 3061 r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode);
3071 if (r < 0) 3062 if (r < 0)
3072 return r; 3063 goto out;
3073 3064
3074 ops = bdev->bd_disk->fops->pr_ops; 3065 ops = bdev->bd_disk->fops->pr_ops;
3075 if (ops && ops->pr_release) 3066 if (ops && ops->pr_release)
3076 r = ops->pr_release(bdev, key, type); 3067 r = ops->pr_release(bdev, key, type);
3077 else 3068 else
3078 r = -EOPNOTSUPP; 3069 r = -EOPNOTSUPP;
3079 3070out:
3080 blkdev_put(bdev, mode); 3071 dm_unprepare_ioctl(md, srcu_idx);
3081 return r; 3072 return r;
3082} 3073}
3083 3074
@@ -3087,19 +3078,19 @@ static int dm_pr_preempt(struct block_device *bdev, u64 old_key, u64 new_key,
3087 struct mapped_device *md = bdev->bd_disk->private_data; 3078 struct mapped_device *md = bdev->bd_disk->private_data;
3088 const struct pr_ops *ops; 3079 const struct pr_ops *ops;
3089 fmode_t mode; 3080 fmode_t mode;
3090 int r; 3081 int r, srcu_idx;
3091 3082
3092 r = dm_get_bdev_for_ioctl(md, &bdev, &mode); 3083 r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode);
3093 if (r < 0) 3084 if (r < 0)
3094 return r; 3085 goto out;
3095 3086
3096 ops = bdev->bd_disk->fops->pr_ops; 3087 ops = bdev->bd_disk->fops->pr_ops;
3097 if (ops && ops->pr_preempt) 3088 if (ops && ops->pr_preempt)
3098 r = ops->pr_preempt(bdev, old_key, new_key, type, abort); 3089 r = ops->pr_preempt(bdev, old_key, new_key, type, abort);
3099 else 3090 else
3100 r = -EOPNOTSUPP; 3091 r = -EOPNOTSUPP;
3101 3092out:
3102 blkdev_put(bdev, mode); 3093 dm_unprepare_ioctl(md, srcu_idx);
3103 return r; 3094 return r;
3104} 3095}
3105 3096
@@ -3108,19 +3099,19 @@ static int dm_pr_clear(struct block_device *bdev, u64 key)
3108 struct mapped_device *md = bdev->bd_disk->private_data; 3099 struct mapped_device *md = bdev->bd_disk->private_data;
3109 const struct pr_ops *ops; 3100 const struct pr_ops *ops;
3110 fmode_t mode; 3101 fmode_t mode;
3111 int r; 3102 int r, srcu_idx;
3112 3103
3113 r = dm_get_bdev_for_ioctl(md, &bdev, &mode); 3104 r = dm_prepare_ioctl(md, &srcu_idx, &bdev, &mode);
3114 if (r < 0) 3105 if (r < 0)
3115 return r; 3106 goto out;
3116 3107
3117 ops = bdev->bd_disk->fops->pr_ops; 3108 ops = bdev->bd_disk->fops->pr_ops;
3118 if (ops && ops->pr_clear) 3109 if (ops && ops->pr_clear)
3119 r = ops->pr_clear(bdev, key); 3110 r = ops->pr_clear(bdev, key);
3120 else 3111 else
3121 r = -EOPNOTSUPP; 3112 r = -EOPNOTSUPP;
3122 3113out:
3123 blkdev_put(bdev, mode); 3114 dm_unprepare_ioctl(md, srcu_idx);
3124 return r; 3115 return r;
3125} 3116}
3126 3117
diff --git a/include/uapi/linux/dm-ioctl.h b/include/uapi/linux/dm-ioctl.h
index 5108da02cd32..d1e49514977b 100644
--- a/include/uapi/linux/dm-ioctl.h
+++ b/include/uapi/linux/dm-ioctl.h
@@ -270,9 +270,9 @@ enum {
270#define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) 270#define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
271 271
272#define DM_VERSION_MAJOR 4 272#define DM_VERSION_MAJOR 4
273#define DM_VERSION_MINOR 38 273#define DM_VERSION_MINOR 39
274#define DM_VERSION_PATCHLEVEL 0 274#define DM_VERSION_PATCHLEVEL 0
275#define DM_VERSION_EXTRA "-ioctl (2018-02-28)" 275#define DM_VERSION_EXTRA "-ioctl (2018-04-03)"
276 276
277/* Status bits */ 277/* Status bits */
278#define DM_READONLY_FLAG (1 << 0) /* In/Out */ 278#define DM_READONLY_FLAG (1 << 0) /* In/Out */