Date: Thu, 21 Sep 2006 16:41:43 +0100 From: Alasdair G Kergon <agk@redhat.com> Subject: [PATCH 01/30] dm: support ioctls on mapped devices Extend the core device-mapper infrastructure to accept arbitrary ioctls on a mapped device provided that it has exactly one target and it is capable of supporting ioctls. Index: linux-2.6.18.noarch/drivers/md/dm.c =================================================================== --- linux-2.6.18.noarch.orig/drivers/md/dm.c +++ linux-2.6.18.noarch/drivers/md/dm.c @@ -20,6 +20,7 @@ #include <linux/idr.h> #include <linux/hdreg.h> #include <linux/blktrace_api.h> +#include <linux/smp_lock.h> #define DM_MSG_PREFIX "core" @@ -288,6 +289,45 @@ static int dm_blk_getgeo(struct block_de return dm_get_geometry(md, geo); } +static int dm_blk_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct mapped_device *md; + struct dm_table *map; + struct dm_target *tgt; + int r = -ENOTTY; + + /* We don't really need this lock, but we do need 'inode'. */ + unlock_kernel(); + + md = inode->i_bdev->bd_disk->private_data; + + map = dm_get_table(md); + + if (!map || !dm_table_get_size(map)) + goto out; + + /* We only support devices that have a single target */ + if (dm_table_get_num_targets(map) != 1) + goto out; + + tgt = dm_table_get_target(map, 0); + + if (dm_suspended(md)) { + r = -EAGAIN; + goto out; + } + + if (tgt->type->ioctl) + r = tgt->type->ioctl(tgt, inode, file, cmd, arg); + +out: + dm_table_put(map); + + lock_kernel(); + return r; +} + static inline struct dm_io *alloc_io(struct mapped_device *md) { return mempool_alloc(md->io_pool, GFP_NOIO); @@ -1377,6 +1417,7 @@ int dm_suspended(struct mapped_device *m static struct block_device_operations dm_blk_dops = { .open = dm_blk_open, .release = dm_blk_close, + .ioctl = dm_blk_ioctl, .getgeo = dm_blk_getgeo, .owner = THIS_MODULE }; Index: linux-2.6.18.noarch/include/linux/device-mapper.h =================================================================== --- linux-2.6.18.noarch.orig/include/linux/device-mapper.h +++ linux-2.6.18.noarch/include/linux/device-mapper.h @@ -64,6 +64,10 @@ typedef int (*dm_status_fn) (struct dm_t typedef int (*dm_message_fn) (struct dm_target *ti, unsigned argc, char **argv); +typedef int (*dm_ioctl_fn) (struct dm_target *ti, struct inode *inode, + struct file *filp, unsigned int cmd, + unsigned long arg); + void dm_error(const char *message); /* @@ -91,6 +95,7 @@ struct target_type { dm_resume_fn resume; dm_status_fn status; dm_message_fn message; + dm_ioctl_fn ioctl; }; struct io_restrictions { Index: linux-2.6.18.noarch/include/linux/dm-ioctl.h =================================================================== --- linux-2.6.18.noarch.orig/include/linux/dm-ioctl.h +++ linux-2.6.18.noarch/include/linux/dm-ioctl.h @@ -285,7 +285,7 @@ typedef char ioctl_struct[308]; #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) #define DM_VERSION_MAJOR 4 -#define DM_VERSION_MINOR 7 +#define DM_VERSION_MINOR 8 #define DM_VERSION_PATCHLEVEL 0 #define DM_VERSION_EXTRA "-ioctl (2006-06-24)" Date: Thu, 21 Sep 2006 16:42:04 +0100 From: Alasdair G Kergon <agk@redhat.com> Subject: [PATCH 02/30] dm linear: support ioctls When an ioctl is performed on a device with a linear target, simply pass it on to the underlying block device. Note that the ioctl will pass through the filtering in blkdev_ioctl() twice. Index: linux-2.6.18.noarch/drivers/md/dm-linear.c =================================================================== --- linux-2.6.18.noarch.orig/drivers/md/dm-linear.c +++ linux-2.6.18.noarch/drivers/md/dm-linear.c @@ -98,14 +98,25 @@ static int linear_status(struct dm_targe return 0; } +static int linear_ioctl(struct dm_target *ti, struct inode *inode, + struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct linear_c *lc = (struct linear_c *) ti->private; + struct block_device *bdev = lc->dev->bdev; + + return blkdev_ioctl(bdev->bd_inode, filp, cmd, arg); +} + static struct target_type linear_target = { .name = "linear", - .version= {1, 0, 1}, + .version= {1, 0, 2}, .module = THIS_MODULE, .ctr = linear_ctr, .dtr = linear_dtr, .map = linear_map, .status = linear_status, + .ioctl = linear_ioctl, }; int __init dm_linear_init(void) Date: Thu, 21 Sep 2006 16:42:20 +0100 From: Alasdair G Kergon <agk@redhat.com> Subject: [PATCH 03/30] dm mpath: support ioctls When an ioctl is performed on a multipath device simply pass it on to the underlying block device through current_path. If current path is not yet selected, select it. Index: linux-2.6.18.noarch/drivers/md/dm-mpath.c =================================================================== --- linux-2.6.18.noarch.orig/drivers/md/dm-mpath.c +++ linux-2.6.18.noarch/drivers/md/dm-mpath.c @@ -1266,12 +1266,39 @@ error: return -EINVAL; } +static int multipath_ioctl(struct dm_target *ti, struct inode *inode, + struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct multipath *m = (struct multipath *) ti->private; + struct block_device *bdev = NULL; + unsigned long flags; + int r = 0; + + spin_lock_irqsave(&m->lock, flags); + + if (!m->current_pgpath) + __choose_pgpath(m); + + if (m->current_pgpath) + bdev = m->current_pgpath->path.dev->bdev; + + if (m->queue_io) + r = -EAGAIN; + else if (!bdev) + r = -EIO; + + spin_unlock_irqrestore(&m->lock, flags); + + return r ? : blkdev_ioctl(bdev->bd_inode, filp, cmd, arg); +} + /*----------------------------------------------------------------- * Module setup *---------------------------------------------------------------*/ static struct target_type multipath_target = { .name = "multipath", - .version = {1, 0, 4}, + .version = {1, 0, 5}, .module = THIS_MODULE, .ctr = multipath_ctr, .dtr = multipath_dtr, @@ -1281,6 +1308,7 @@ static struct target_type multipath_targ .resume = multipath_resume, .status = multipath_status, .message = multipath_message, + .ioctl = multipath_ioctl, }; static int __init dm_multipath_init(void) Date: Thu, 21 Sep 2006 16:43:00 +0100 From: Alasdair G Kergon <agk@redhat.com> Subject: [PATCH 04/30] dm: export-blkdev driver ioctl Export blkdev_driver_ioctl for device-mapper. If we get as far as the device-mapper ioctl handler, we know the ioctl is not a standard block layer BLK* one, so we don't need to check for them a second time and can call blkdev_driver_ioctl() directly. Index: linux-2.6.18.noarch/block/ioctl.c =================================================================== --- linux-2.6.18.noarch.orig/block/ioctl.c +++ linux-2.6.18.noarch/block/ioctl.c @@ -199,8 +199,8 @@ static int blkdev_locked_ioctl(struct fi return -ENOIOCTLCMD; } -static int blkdev_driver_ioctl(struct inode *inode, struct file *file, - struct gendisk *disk, unsigned cmd, unsigned long arg) +int blkdev_driver_ioctl(struct inode *inode, struct file *file, + struct gendisk *disk, unsigned cmd, unsigned long arg) { int ret; if (disk->fops->unlocked_ioctl) @@ -215,6 +215,7 @@ static int blkdev_driver_ioctl(struct in return -ENOTTY; } +EXPORT_SYMBOL_GPL(blkdev_driver_ioctl); int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd, unsigned long arg) Index: linux-2.6.18.noarch/drivers/md/dm-linear.c =================================================================== --- linux-2.6.18.noarch.orig/drivers/md/dm-linear.c +++ linux-2.6.18.noarch/drivers/md/dm-linear.c @@ -105,7 +105,7 @@ static int linear_ioctl(struct dm_target struct linear_c *lc = (struct linear_c *) ti->private; struct block_device *bdev = lc->dev->bdev; - return blkdev_ioctl(bdev->bd_inode, filp, cmd, arg); + return blkdev_driver_ioctl(bdev->bd_inode, filp, bdev->bd_disk, cmd, arg); } static struct target_type linear_target = { Index: linux-2.6.18.noarch/drivers/md/dm-mpath.c =================================================================== --- linux-2.6.18.noarch.orig/drivers/md/dm-mpath.c +++ linux-2.6.18.noarch/drivers/md/dm-mpath.c @@ -1290,7 +1290,8 @@ static int multipath_ioctl(struct dm_tar spin_unlock_irqrestore(&m->lock, flags); - return r ? : blkdev_ioctl(bdev->bd_inode, filp, cmd, arg); + return r ? : blkdev_driver_ioctl(bdev->bd_inode, filp, bdev->bd_disk, + cmd, arg); } /*----------------------------------------------------------------- Index: linux-2.6.18.noarch/include/linux/fs.h =================================================================== --- linux-2.6.18.noarch.orig/include/linux/fs.h +++ linux-2.6.18.noarch/include/linux/fs.h @@ -1455,6 +1455,9 @@ extern const struct file_operations bad_ extern const struct file_operations def_fifo_fops; extern int ioctl_by_bdev(struct block_device *, unsigned, unsigned long); extern int blkdev_ioctl(struct inode *, struct file *, unsigned, unsigned long); +extern int blkdev_driver_ioctl(struct inode *inode, struct file *file, + struct gendisk *disk, unsigned cmd, + unsigned long arg); extern long compat_blkdev_ioctl(struct file *, unsigned, unsigned long); extern int blkdev_get(struct block_device *, mode_t, unsigned); extern int blkdev_put(struct block_device *); Date: Thu, 21 Sep 2006 16:44:55 +0100 From: Alasdair G Kergon <agk@redhat.com> Subject: [RHEL5 PATCH 05/30] dm: fake file in targets We pass the wrong file pointer to the underlying device. No file pointer is available so use a temporary fake one. Index: linux-2.6.18.noarch/drivers/md/dm-mpath.c =================================================================== --- linux-2.6.18.noarch.orig/drivers/md/dm-mpath.c +++ linux-2.6.18.noarch/drivers/md/dm-mpath.c @@ -1273,15 +1273,22 @@ static int multipath_ioctl(struct dm_tar struct multipath *m = (struct multipath *) ti->private; struct block_device *bdev = NULL; unsigned long flags; + struct file fake_file = {}; + struct dentry fake_dentry = {}; int r = 0; + fake_file.f_dentry = &fake_dentry; + spin_lock_irqsave(&m->lock, flags); if (!m->current_pgpath) __choose_pgpath(m); - if (m->current_pgpath) + if (m->current_pgpath) { bdev = m->current_pgpath->path.dev->bdev; + fake_dentry.d_inode = bdev->bd_inode; + fake_file.f_mode = m->current_pgpath->path.dev->mode; + } if (m->queue_io) r = -EAGAIN; @@ -1290,8 +1297,8 @@ static int multipath_ioctl(struct dm_tar spin_unlock_irqrestore(&m->lock, flags); - return r ? : blkdev_driver_ioctl(bdev->bd_inode, filp, bdev->bd_disk, - cmd, arg); + return r ? : blkdev_driver_ioctl(bdev->bd_inode, &fake_file, + bdev->bd_disk, cmd, arg); } /*----------------------------------------------------------------- Index: linux-2.6.18.noarch/drivers/md/dm-linear.c =================================================================== --- linux-2.6.18.noarch.orig/drivers/md/dm-linear.c +++ linux-2.6.18.noarch/drivers/md/dm-linear.c @@ -104,8 +104,14 @@ static int linear_ioctl(struct dm_target { struct linear_c *lc = (struct linear_c *) ti->private; struct block_device *bdev = lc->dev->bdev; + struct file fake_file = {}; + struct dentry fake_dentry = {}; - return blkdev_driver_ioctl(bdev->bd_inode, filp, bdev->bd_disk, cmd, arg); + fake_file.f_mode = lc->dev->mode; + fake_file.f_dentry = &fake_dentry; + fake_dentry.d_inode = bdev->bd_inode; + + return blkdev_driver_ioctl(bdev->bd_inode, &fake_file, bdev->bd_disk, cmd, arg); } static struct target_type linear_target = {