On Wed, Dec 11, 2019 at 09:42:49PM +0100, Arnd Bergmann wrote: > Each driver calling scsi_ioctl() gets an equivalent compat_ioctl() > handler that implements the same commands by calling scsi_compat_ioctl(). > > The scsi_cmd_ioctl() and scsi_cmd_blk_ioctl() functions are compatible > at this point, so any driver that calls those can do so for both native > and compat mode, with the argument passed through compat_ptr(). > > With this, we can remove the entries from fs/compat_ioctl.c. The new > code is larger, but should be easier to maintain and keep updated with > newly added commands. > > Signed-off-by: Arnd Bergmann <arnd@xxxxxxxx> > --- > drivers/block/virtio_blk.c | 3 + > drivers/scsi/ch.c | 9 ++- > drivers/scsi/sd.c | 50 ++++++-------- > drivers/scsi/sg.c | 44 ++++++++----- > drivers/scsi/sr.c | 57 ++++++++++++++-- > drivers/scsi/st.c | 51 ++++++++------ > fs/compat_ioctl.c | 132 +------------------------------------ > 7 files changed, 142 insertions(+), 204 deletions(-) > > diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c > index 7ffd719d89de..fbbf18ac1d5d 100644 > --- a/drivers/block/virtio_blk.c > +++ b/drivers/block/virtio_blk.c > @@ -405,6 +405,9 @@ static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo) > > static const struct block_device_operations virtblk_fops = { > .ioctl = virtblk_ioctl, > +#ifdef CONFIG_COMPAT > + .compat_ioctl = blkdev_compat_ptr_ioctl, > +#endif > .owner = THIS_MODULE, > .getgeo = virtblk_getgeo, > }; virtio part: Acked-by: Michael S. Tsirkin <mst@xxxxxxxxxx> > diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c > index 76751d6c7f0d..ed5f4a6ae270 100644 > --- a/drivers/scsi/ch.c > +++ b/drivers/scsi/ch.c > @@ -872,6 +872,10 @@ static long ch_ioctl_compat(struct file * file, > unsigned int cmd, unsigned long arg) > { > scsi_changer *ch = file->private_data; > + int retval = scsi_ioctl_block_when_processing_errors(ch->device, cmd, > + file->f_flags & O_NDELAY); > + if (retval) > + return retval; > > switch (cmd) { > case CHIOGPARAMS: > @@ -883,7 +887,7 @@ static long ch_ioctl_compat(struct file * file, > case CHIOINITELEM: > case CHIOSVOLTAG: > /* compatible */ > - return ch_ioctl(file, cmd, arg); > + return ch_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); > case CHIOGSTATUS32: > { > struct changer_element_status32 ces32; > @@ -898,8 +902,7 @@ static long ch_ioctl_compat(struct file * file, > return ch_gstatus(ch, ces32.ces_type, data); > } > default: > - // return scsi_ioctl_compat(ch->device, cmd, (void*)arg); > - return -ENOIOCTLCMD; > + return scsi_compat_ioctl(ch->device, cmd, compat_ptr(arg)); > > } > } > diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c > index cea625906440..5afb0046b12a 100644 > --- a/drivers/scsi/sd.c > +++ b/drivers/scsi/sd.c > @@ -1465,13 +1465,12 @@ static int sd_getgeo(struct block_device *bdev, struct hd_geometry *geo) > * Note: most ioctls are forward onto the block subsystem or further > * down in the scsi subsystem. > **/ > -static int sd_ioctl(struct block_device *bdev, fmode_t mode, > - unsigned int cmd, unsigned long arg) > +static int sd_ioctl_common(struct block_device *bdev, fmode_t mode, > + unsigned int cmd, void __user *p) > { > struct gendisk *disk = bdev->bd_disk; > struct scsi_disk *sdkp = scsi_disk(disk); > struct scsi_device *sdp = sdkp->device; > - void __user *p = (void __user *)arg; > int error; > > SCSI_LOG_IOCTL(1, sd_printk(KERN_INFO, sdkp, "sd_ioctl: disk=%s, " > @@ -1507,9 +1506,6 @@ static int sd_ioctl(struct block_device *bdev, fmode_t mode, > break; > default: > error = scsi_cmd_blk_ioctl(bdev, mode, cmd, p); > - if (error != -ENOTTY) > - break; > - error = scsi_ioctl(sdp, cmd, p); > break; > } > out: > @@ -1691,39 +1687,31 @@ static void sd_rescan(struct device *dev) > revalidate_disk(sdkp->disk); > } > > +static int sd_ioctl(struct block_device *bdev, fmode_t mode, > + unsigned int cmd, unsigned long arg) > +{ > + void __user *p = (void __user *)arg; > + int ret; > + > + ret = sd_ioctl_common(bdev, mode, cmd, p); > + if (ret != -ENOTTY) > + return ret; > + > + return scsi_ioctl(scsi_disk(bdev->bd_disk)->device, cmd, p); > +} > > #ifdef CONFIG_COMPAT > -/* > - * This gets directly called from VFS. When the ioctl > - * is not recognized we go back to the other translation paths. > - */ > static int sd_compat_ioctl(struct block_device *bdev, fmode_t mode, > unsigned int cmd, unsigned long arg) > { > - struct gendisk *disk = bdev->bd_disk; > - struct scsi_disk *sdkp = scsi_disk(disk); > - struct scsi_device *sdev = sdkp->device; > void __user *p = compat_ptr(arg); > - int error; > - > - error = scsi_verify_blk_ioctl(bdev, cmd); > - if (error < 0) > - return error; > + int ret; > > - error = scsi_ioctl_block_when_processing_errors(sdev, cmd, > - (mode & FMODE_NDELAY) != 0); > - if (error) > - return error; > + ret = sd_ioctl_common(bdev, mode, cmd, p); > + if (ret != -ENOTTY) > + return ret; > > - if (is_sed_ioctl(cmd)) > - return sed_ioctl(sdkp->opal_dev, cmd, p); > - > - /* > - * Let the static ioctl translation table take care of it. > - */ > - if (!sdev->host->hostt->compat_ioctl) > - return -ENOIOCTLCMD; > - return sdev->host->hostt->compat_ioctl(sdev, cmd, p); > + return scsi_compat_ioctl(scsi_disk(bdev->bd_disk)->device, cmd, p); > } > #endif > > diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c > index 985546aac236..08efcee7a34d 100644 > --- a/drivers/scsi/sg.c > +++ b/drivers/scsi/sg.c > @@ -910,19 +910,14 @@ static int put_compat_request_table(struct compat_sg_req_info __user *o, > #endif > > static long > -sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) > +sg_ioctl_common(struct file *filp, Sg_device *sdp, Sg_fd *sfp, > + unsigned int cmd_in, void __user *p) > { > - void __user *p = (void __user *)arg; > int __user *ip = p; > int result, val, read_only; > - Sg_device *sdp; > - Sg_fd *sfp; > Sg_request *srp; > unsigned long iflags; > > - if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) > - return -ENXIO; > - > SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, > "sg_ioctl: cmd=0x%x\n", (int) cmd_in)); > read_only = (O_RDWR != (filp->f_flags & O_ACCMODE)); > @@ -1145,29 +1140,44 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) > cmd_in, filp->f_flags & O_NDELAY); > if (result) > return result; > + > + return -ENOIOCTLCMD; > +} > + > +static long > +sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) > +{ > + void __user *p = (void __user *)arg; > + Sg_device *sdp; > + Sg_fd *sfp; > + int ret; > + > + if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) > + return -ENXIO; > + > + ret = sg_ioctl_common(filp, sdp, sfp, cmd_in, p); > + if (ret != -ENOIOCTLCMD) > + return ret; > + > return scsi_ioctl(sdp->device, cmd_in, p); > } > > #ifdef CONFIG_COMPAT > static long sg_compat_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) > { > + void __user *p = compat_ptr(arg); > Sg_device *sdp; > Sg_fd *sfp; > - struct scsi_device *sdev; > + int ret; > > if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) > return -ENXIO; > > - sdev = sdp->device; > - if (sdev->host->hostt->compat_ioctl) { > - int ret; > - > - ret = sdev->host->hostt->compat_ioctl(sdev, cmd_in, (void __user *)arg); > - > + ret = sg_ioctl_common(filp, sdp, sfp, cmd_in, p); > + if (ret != -ENOIOCTLCMD) > return ret; > - } > - > - return -ENOIOCTLCMD; > + > + return scsi_compat_ioctl(sdp->device, cmd_in, p); > } > #endif > > diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c > index 4664fdf75c0f..6033a886c42c 100644 > --- a/drivers/scsi/sr.c > +++ b/drivers/scsi/sr.c > @@ -38,6 +38,7 @@ > #include <linux/kernel.h> > #include <linux/mm.h> > #include <linux/bio.h> > +#include <linux/compat.h> > #include <linux/string.h> > #include <linux/errno.h> > #include <linux/cdrom.h> > @@ -598,6 +599,55 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, > return ret; > } > > +#ifdef CONFIG_COMPAT > +static int sr_block_compat_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, > + unsigned long arg) > +{ > + struct scsi_cd *cd = scsi_cd(bdev->bd_disk); > + struct scsi_device *sdev = cd->device; > + void __user *argp = compat_ptr(arg); > + int ret; > + > + mutex_lock(&sr_mutex); > + > + ret = scsi_ioctl_block_when_processing_errors(sdev, cmd, > + (mode & FMODE_NDELAY) != 0); > + if (ret) > + goto out; > + > + scsi_autopm_get_device(sdev); > + > + /* > + * Send SCSI addressing ioctls directly to mid level, send other > + * ioctls to cdrom/block level. > + */ > + switch (cmd) { > + case SCSI_IOCTL_GET_IDLUN: > + case SCSI_IOCTL_GET_BUS_NUMBER: > + ret = scsi_compat_ioctl(sdev, cmd, argp); > + goto put; > + } > + > + /* > + * CDROM ioctls are handled in the block layer, but > + * do the scsi blk ioctls here. > + */ > + ret = scsi_cmd_blk_ioctl(bdev, mode, cmd, argp); > + if (ret != -ENOTTY) > + return ret; > + > + ret = scsi_compat_ioctl(sdev, cmd, argp); > + > +put: > + scsi_autopm_put_device(sdev); > + > +out: > + mutex_unlock(&sr_mutex); > + return ret; > + > +} > +#endif > + > static unsigned int sr_block_check_events(struct gendisk *disk, > unsigned int clearing) > { > @@ -641,12 +691,11 @@ static const struct block_device_operations sr_bdops = > .open = sr_block_open, > .release = sr_block_release, > .ioctl = sr_block_ioctl, > +#ifdef CONFIG_COMPAT > + .ioctl = sr_block_compat_ioctl, > +#endif > .check_events = sr_block_check_events, > .revalidate_disk = sr_block_revalidate_disk, > - /* > - * No compat_ioctl for now because sr_block_ioctl never > - * seems to pass arbitrary ioctls down to host drivers. > - */ > }; > > static int sr_open(struct cdrom_device_info *cdi, int purpose) > diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c > index 9e3fff2de83e..393f3019ccac 100644 > --- a/drivers/scsi/st.c > +++ b/drivers/scsi/st.c > @@ -3501,7 +3501,7 @@ static int partition_tape(struct scsi_tape *STp, int size) > > > /* The ioctl command */ > -static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) > +static long st_ioctl_common(struct file *file, unsigned int cmd_in, void __user *p) > { > int i, cmd_nr, cmd_type, bt; > int retval = 0; > @@ -3509,7 +3509,6 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) > struct scsi_tape *STp = file->private_data; > struct st_modedef *STm; > struct st_partstat *STps; > - void __user *p = (void __user *)arg; > > if (mutex_lock_interruptible(&STp->lock)) > return -ERESTARTSYS; > @@ -3824,9 +3823,19 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) > } > mutex_unlock(&STp->lock); > switch (cmd_in) { > + case SCSI_IOCTL_STOP_UNIT: > + /* unload */ > + retval = scsi_ioctl(STp->device, cmd_in, p); > + if (!retval) { > + STp->rew_at_close = 0; > + STp->ready = ST_NO_TAPE; > + } > + return retval; > + > case SCSI_IOCTL_GET_IDLUN: > case SCSI_IOCTL_GET_BUS_NUMBER: > break; > + > default: > if ((cmd_in == SG_IO || > cmd_in == SCSI_IOCTL_SEND_COMMAND || > @@ -3840,42 +3849,46 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) > return i; > break; > } > - retval = scsi_ioctl(STp->device, cmd_in, p); > - if (!retval && cmd_in == SCSI_IOCTL_STOP_UNIT) { /* unload */ > - STp->rew_at_close = 0; > - STp->ready = ST_NO_TAPE; > - } > - return retval; > + return -ENOTTY; > > out: > mutex_unlock(&STp->lock); > return retval; > } > > +static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) > +{ > + void __user *p = (void __user *)arg; > + struct scsi_tape *STp = file->private_data; > + int ret; > + > + ret = st_ioctl_common(file, cmd_in, p); > + if (ret != -ENOTTY) > + return ret; > + > + return scsi_ioctl(STp->device, cmd_in, p); > +} > + > #ifdef CONFIG_COMPAT > static long st_compat_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) > { > void __user *p = compat_ptr(arg); > struct scsi_tape *STp = file->private_data; > - struct scsi_device *sdev = STp->device; > - int ret = -ENOIOCTLCMD; > + int ret; > > /* argument conversion is handled using put_user_mtpos/put_user_mtget */ > switch (cmd_in) { > - case MTIOCTOP: > - return st_ioctl(file, MTIOCTOP, (unsigned long)p); > case MTIOCPOS32: > - return st_ioctl(file, MTIOCPOS, (unsigned long)p); > + return st_ioctl_common(file, MTIOCPOS, p); > case MTIOCGET32: > - return st_ioctl(file, MTIOCGET, (unsigned long)p); > + return st_ioctl_common(file, MTIOCGET, p); > } > > - if (sdev->host->hostt->compat_ioctl) { > + ret = st_ioctl_common(file, cmd_in, p); > + if (ret != -ENOTTY) > + return ret; > > - ret = sdev->host->hostt->compat_ioctl(sdev, cmd_in, (void __user *)arg); > - > - } > - return ret; > + return scsi_compat_ioctl(STp->device, cmd_in, p); > } > #endif > > diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c > index 358ea2ecf36b..ab4471f469e6 100644 > --- a/fs/compat_ioctl.c > +++ b/fs/compat_ioctl.c > @@ -36,109 +36,11 @@ > > #include "internal.h" > > -#ifdef CONFIG_BLOCK > -#include <linux/cdrom.h> > -#include <linux/fd.h> > -#include <scsi/scsi.h> > -#include <scsi/scsi_ioctl.h> > -#include <scsi/sg.h> > -#endif > - > #include <linux/uaccess.h> > #include <linux/watchdog.h> > > #include <linux/hiddev.h> > > - > -#include <linux/sort.h> > - > -/* > - * simple reversible transform to make our table more evenly > - * distributed after sorting. > - */ > -#define XFORM(i) (((i) ^ ((i) << 27) ^ ((i) << 17)) & 0xffffffff) > - > -#define COMPATIBLE_IOCTL(cmd) XFORM((u32)cmd), > -static unsigned int ioctl_pointer[] = { > -#ifdef CONFIG_BLOCK > -/* Big S */ > -COMPATIBLE_IOCTL(SCSI_IOCTL_GET_IDLUN) > -COMPATIBLE_IOCTL(SCSI_IOCTL_DOORLOCK) > -COMPATIBLE_IOCTL(SCSI_IOCTL_DOORUNLOCK) > -COMPATIBLE_IOCTL(SCSI_IOCTL_TEST_UNIT_READY) > -COMPATIBLE_IOCTL(SCSI_IOCTL_GET_BUS_NUMBER) > -COMPATIBLE_IOCTL(SCSI_IOCTL_SEND_COMMAND) > -COMPATIBLE_IOCTL(SCSI_IOCTL_PROBE_HOST) > -COMPATIBLE_IOCTL(SCSI_IOCTL_GET_PCI) > -#endif > -#ifdef CONFIG_BLOCK > -/* SG stuff */ > -COMPATIBLE_IOCTL(SG_IO) > -COMPATIBLE_IOCTL(SG_GET_REQUEST_TABLE) > -COMPATIBLE_IOCTL(SG_SET_TIMEOUT) > -COMPATIBLE_IOCTL(SG_GET_TIMEOUT) > -COMPATIBLE_IOCTL(SG_EMULATED_HOST) > -COMPATIBLE_IOCTL(SG_GET_TRANSFORM) > -COMPATIBLE_IOCTL(SG_SET_RESERVED_SIZE) > -COMPATIBLE_IOCTL(SG_GET_RESERVED_SIZE) > -COMPATIBLE_IOCTL(SG_GET_SCSI_ID) > -COMPATIBLE_IOCTL(SG_SET_FORCE_LOW_DMA) > -COMPATIBLE_IOCTL(SG_GET_LOW_DMA) > -COMPATIBLE_IOCTL(SG_SET_FORCE_PACK_ID) > -COMPATIBLE_IOCTL(SG_GET_PACK_ID) > -COMPATIBLE_IOCTL(SG_GET_NUM_WAITING) > -COMPATIBLE_IOCTL(SG_SET_DEBUG) > -COMPATIBLE_IOCTL(SG_GET_SG_TABLESIZE) > -COMPATIBLE_IOCTL(SG_GET_COMMAND_Q) > -COMPATIBLE_IOCTL(SG_SET_COMMAND_Q) > -COMPATIBLE_IOCTL(SG_GET_VERSION_NUM) > -COMPATIBLE_IOCTL(SG_NEXT_CMD_LEN) > -COMPATIBLE_IOCTL(SG_SCSI_RESET) > -COMPATIBLE_IOCTL(SG_GET_REQUEST_TABLE) > -COMPATIBLE_IOCTL(SG_SET_KEEP_ORPHAN) > -COMPATIBLE_IOCTL(SG_GET_KEEP_ORPHAN) > -#endif > -}; > - > -/* > - * Convert common ioctl arguments based on their command number > - * > - * Please do not add any code in here. Instead, implement > - * a compat_ioctl operation in the place that handleѕ the > - * ioctl for the native case. > - */ > -static long do_ioctl_trans(unsigned int cmd, > - unsigned long arg, struct file *file) > -{ > - return -ENOIOCTLCMD; > -} > - > -static int compat_ioctl_check_table(unsigned int xcmd) > -{ > -#ifdef CONFIG_BLOCK > - int i; > - const int max = ARRAY_SIZE(ioctl_pointer) - 1; > - > - BUILD_BUG_ON(max >= (1 << 16)); > - > - /* guess initial offset into table, assuming a > - normalized distribution */ > - i = ((xcmd >> 16) * max) >> 16; > - > - /* do linear search up first, until greater or equal */ > - while (ioctl_pointer[i] < xcmd && i < max) > - i++; > - > - /* then do linear search down */ > - while (ioctl_pointer[i] > xcmd && i > 0) > - i--; > - > - return ioctl_pointer[i] == xcmd; > -#else > - return 0; > -#endif > -} > - > COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, > compat_ulong_t, arg32) > { > @@ -216,19 +118,9 @@ COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, > goto out_fput; > } > > - if (!f.file->f_op->unlocked_ioctl) > - goto do_ioctl; > - break; > - } > - > - if (compat_ioctl_check_table(XFORM(cmd))) > - goto found_handler; > - > - error = do_ioctl_trans(cmd, arg, f.file); > - if (error == -ENOIOCTLCMD) > error = -ENOTTY; > - > - goto out_fput; > + goto out_fput; > + } > > found_handler: > arg = (unsigned long)compat_ptr(arg); > @@ -239,23 +131,3 @@ COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, > out: > return error; > } > - > -static int __init init_sys32_ioctl_cmp(const void *p, const void *q) > -{ > - unsigned int a, b; > - a = *(unsigned int *)p; > - b = *(unsigned int *)q; > - if (a > b) > - return 1; > - if (a < b) > - return -1; > - return 0; > -} > - > -static int __init init_sys32_ioctl(void) > -{ > - sort(ioctl_pointer, ARRAY_SIZE(ioctl_pointer), sizeof(*ioctl_pointer), > - init_sys32_ioctl_cmp, NULL); > - return 0; > -} > -__initcall(init_sys32_ioctl); > -- > 2.20.0