Shorten sg_ioctl() by adding some helper functions. sg_ioctl()
is the main entry point for ioctls used on this driver's
devices.

Signed-off-by: Douglas Gilbert <dgilb...@interlog.com>
---
 drivers/scsi/sg.c | 325 ++++++++++++++++++++++++++++------------------
 1 file changed, 200 insertions(+), 125 deletions(-)

diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 2796fef42837..90753f7759c7 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -72,6 +72,9 @@ static char *sg_version_date = "20190606";
  */
 #define SG_MAX_CDB_SIZE 252
 
+#define uptr64(val) ((void __user *)(uintptr_t)(val))
+#define cuptr64(val) ((const void __user *)(uintptr_t)(val))
+
 #define SG_DEFAULT_TIMEOUT mult_frac(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ)
 
 /* Bit positions (flags) for sg_device::fdev_bm bitmask follow */
@@ -1013,16 +1016,111 @@ sg_fill_request_table(struct sg_fd *sfp, struct 
sg_req_info *rinfo)
        }
 }
 
+/*
+ * Handles ioctl(SG_IO) for blocking (sync) usage of v3 or v4 interface.
+ * Returns 0 on success else a negated errno.
+ */
+static int
+sg_ctl_sg_io(struct file *filp, struct sg_device *sdp, struct sg_fd *sfp,
+            void __user *p)
+{
+       bool read_only = O_RDWR != (filp->f_flags & O_ACCMODE);
+       int res;
+       struct sg_request *srp;
+
+       res = sg_allow_if_err_recovery(sdp, false);
+       if (res)
+               return res;
+       res = sg_submit(sfp, filp, p, SZ_SG_IO_HDR, true, read_only,
+                       true, &srp);
+       if (res < 0)
+               return res;
+       res = wait_event_interruptible
+               (sfp->read_wait, (srp_done(sfp, srp) || SG_IS_DETACHING(sdp)));
+       if (SG_IS_DETACHING(sdp))
+               return -ENODEV;
+       spin_lock_irq(&sfp->rq_list_lock);
+       if (srp->done) {
+               srp->done = 2;
+               spin_unlock_irq(&sfp->rq_list_lock);
+               res = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp);
+               return (res < 0) ? res : 0;
+       }
+       srp->orphan = 1;
+       spin_unlock_irq(&sfp->rq_list_lock);
+       return res;     /* -ERESTARTSYS because signal hit process */
+}
+
+static int
+sg_set_reserved_sz(struct sg_fd *sfp, int want_rsv_sz)
+{
+       if (want_rsv_sz != sfp->reserve.buflen) {
+               if (sfp->mmap_called ||
+                   sfp->res_in_use) {
+                       return -EBUSY;
+               }
+
+               sg_remove_scat(sfp, &sfp->reserve);
+               sg_build_reserve(sfp, want_rsv_sz);
+       }
+       return 0;
+}
+
+static int
+sg_ctl_req_tbl(struct sg_fd *sfp, void __user *p)
+{
+       int result;
+       unsigned long iflags;
+       sg_req_info_t *rinfo;
+
+       rinfo = kcalloc(SG_MAX_QUEUE, SZ_SG_REQ_INFO,
+                       GFP_KERNEL);
+       if (!rinfo)
+               return -ENOMEM;
+       spin_lock_irqsave(&sfp->rq_list_lock, iflags);
+       sg_fill_request_table(sfp, rinfo);
+       spin_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+       result = copy_to_user(p, rinfo,
+                             SZ_SG_REQ_INFO * SG_MAX_QUEUE);
+       result = result ? -EFAULT : 0;
+       kfree(rinfo);
+       return result;
+}
+
+static int
+sg_ctl_scsi_id(struct scsi_device *sdev, struct sg_fd *sfp, void __user *p)
+{
+       struct sg_scsi_id ss_id;
+
+       SG_LOG(3, sfp, "%s:    SG_GET_SCSI_ID\n", __func__);
+       ss_id.host_no = sdev->host->host_no;
+       ss_id.channel = sdev->channel;
+       ss_id.scsi_id = sdev->id;
+       ss_id.lun = sdev->lun;
+       ss_id.scsi_type = sdev->type;
+       ss_id.h_cmd_per_lun = sdev->host->cmd_per_lun;
+       ss_id.d_queue_depth = sdev->queue_depth;
+       ss_id.unused[0] = 0;
+       ss_id.unused[1] = 0;
+       if (copy_to_user(p, &ss_id, sizeof(struct sg_scsi_id)))
+               return -EFAULT;
+       return 0;
+}
+
 static long
 sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
 {
-       void __user *p = (void __user *)arg;
+       bool read_only = O_RDWR != (filp->f_flags & O_ACCMODE);
+       int val;
+       int result = 0;
+       void __user *p = uptr64(arg);
        int __user *ip = p;
-       int result, val, read_only;
        struct sg_device *sdp;
        struct sg_fd *sfp;
        struct sg_request *srp;
+       struct scsi_device *sdev;
        unsigned long iflags;
+       __maybe_unused const char *pmlp = ", pass to mid-level";
 
        sfp = filp->private_data;
        sdp = sfp->parentdp;
@@ -1030,139 +1128,118 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, 
unsigned long arg)
               !!(filp->f_flags & O_NONBLOCK));
        if (!sdp)
                return -ENXIO;
-       read_only = (O_RDWR != (filp->f_flags & O_ACCMODE));
+       if (unlikely(SG_IS_DETACHING(sdp)))
+               return -ENODEV;
+       sdev = sdp->device;
 
        switch (cmd_in) {
        case SG_IO:
-               result = sg_allow_if_err_recovery(sdp, false);
-               if (result)
-                       return result;
-               result = sg_submit(sfp, filp, p, SZ_SG_IO_HDR, true, read_only,
-                                  true, &srp);
-               if (result < 0)
-                       return result;
-               result = wait_event_interruptible(sfp->read_wait,
-                       (srp_done(sfp, srp) || SG_IS_DETACHING(sdp)));
-               if (SG_IS_DETACHING(sdp))
-                       return -ENODEV;
-               spin_lock_irq(&sfp->rq_list_lock);
-               if (srp->done) {
-                       srp->done = 2;
-                       spin_unlock_irq(&sfp->rq_list_lock);
-                       result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp);
-                       return (result < 0) ? result : 0;
-               }
-               srp->orphan = 1;
-               spin_unlock_irq(&sfp->rq_list_lock);
-               return result;  /* -ERESTARTSYS because signal hit process */
-       case SG_SET_TIMEOUT:
-               result = get_user(val, ip);
-               if (result)
-                       return result;
-               if (val < 0)
-                       return -EIO;
-               if (val >= mult_frac((s64)INT_MAX, USER_HZ, HZ))
-                       val = min_t(s64, mult_frac((s64)INT_MAX, USER_HZ, HZ),
-                                   INT_MAX);
-               sfp->timeout_user = val;
-               sfp->timeout = mult_frac(val, HZ, USER_HZ);
-
-               return 0;
-       case SG_GET_TIMEOUT:    /* N.B. User receives timeout as return value */
-                               /* strange ..., for backward compatibility */
-               return sfp->timeout_user;
-       case SG_SET_FORCE_LOW_DMA:
-               /*
-                * N.B. This ioctl never worked properly, but failed to
-                * return an error value. So returning '0' to keep compability
-                * with legacy applications.
-                */
-               return 0;
-       case SG_GET_LOW_DMA:
-               return put_user((int) sdp->device->host->unchecked_isa_dma, ip);
+               return sg_ctl_sg_io(filp, sdp, sfp, p);
        case SG_GET_SCSI_ID:
-               if (!SG_IS_DETACHING(sdp)) {
-                       struct sg_scsi_id ss_id;
-
-                       ss_id.host_no = sdp->device->host->host_no;
-                       ss_id.channel = sdp->device->channel;
-                       ss_id.scsi_id = sdp->device->id;
-                       ss_id.lun = sdp->device->lun;
-                       ss_id.scsi_type = sdp->device->type;
-                       ss_id.h_cmd_per_lun = sdp->device->host->cmd_per_lun;
-                       ss_id.d_queue_depth = sdp->device->queue_depth;
-                       ss_id.unused[0] = 0;
-                       ss_id.unused[1] = 0;
-                       if (copy_to_user(p, &ss_id, sizeof(struct sg_scsi_id)))
-                               return -EFAULT;
-                       return 0;
-               }
-               return -ENODEV;
+               return sg_ctl_scsi_id(sdev, sfp, p);
        case SG_SET_FORCE_PACK_ID:
+               SG_LOG(3, sfp, "%s:    SG_SET_FORCE_PACK_ID\n", __func__);
                result = get_user(val, ip);
                if (result)
                        return result;
                sfp->force_packid = val ? 1 : 0;
                return 0;
        case SG_GET_PACK_ID:
+               val = -1;
                spin_lock_irqsave(&sfp->rq_list_lock, iflags);
                list_for_each_entry(srp, &sfp->rq_list, entry) {
                        if ((1 == srp->done) && (!srp->sg_io_owned)) {
-                               spin_unlock_irqrestore(&sfp->rq_list_lock,
-                                                      iflags);
-                               put_user(srp->header.pack_id, ip);
-                               return 0;
+                               val = srp->header.pack_id;
+                               break;
                        }
                }
                spin_unlock_irqrestore(&sfp->rq_list_lock, iflags);
-               put_user(-1, ip);
+               SG_LOG(3, sfp, "%s:    SG_GET_PACK_ID=%d\n", __func__, val);
+               put_user(val, ip);
                return 0;
        case SG_GET_NUM_WAITING:
                return put_user(atomic_read(&sfp->waiting), ip);
        case SG_GET_SG_TABLESIZE:
+               SG_LOG(3, sfp, "%s:    SG_GET_SG_TABLESIZE=%d\n", __func__,
+                      sdp->max_sgat_sz);
                return put_user(sdp->max_sgat_sz, ip);
        case SG_SET_RESERVED_SIZE:
-               result = get_user(val, ip);
-               if (result)
-                       return result;
-                if (val < 0)
-                        return -EINVAL;
-               val = min_t(int, val,
-                           max_sectors_bytes(sdp->device->request_queue));
                mutex_lock(&sfp->f_mutex);
-               if (val != sfp->reserve.buflen) {
-                       if (sfp->mmap_called ||
-                           sfp->res_in_use) {
-                               mutex_unlock(&sfp->f_mutex);
-                               return -EBUSY;
+               result = get_user(val, ip);
+               if (!result) {
+                       if (val >= 0 && val <= (1024 * 1024 * 1024)) {
+                               result = sg_set_reserved_sz(sfp, val);
+                       } else {
+                               SG_LOG(3, sfp, "%s: invalid size\n", __func__);
+                               result = -EINVAL;
                        }
-
-                       sg_remove_scat(sfp, &sfp->reserve);
-                       sg_build_reserve(sfp, val);
                }
                mutex_unlock(&sfp->f_mutex);
-               return 0;
+               return result;
        case SG_GET_RESERVED_SIZE:
                val = min_t(int, sfp->reserve.buflen,
-                           max_sectors_bytes(sdp->device->request_queue));
+                           max_sectors_bytes(sdev->request_queue));
+               SG_LOG(3, sfp, "%s:    SG_GET_RESERVED_SIZE=%d\n",
+                      __func__, val);
                return put_user(val, ip);
        case SG_SET_COMMAND_Q:
+               SG_LOG(3, sfp, "%s:    SG_SET_COMMAND_Q\n", __func__);
                result = get_user(val, ip);
                if (result)
                        return result;
                sfp->cmd_q = val ? 1 : 0;
                return 0;
        case SG_GET_COMMAND_Q:
+               SG_LOG(3, sfp, "%s:    SG_GET_COMMAND_Q\n", __func__);
                return put_user((int) sfp->cmd_q, ip);
        case SG_SET_KEEP_ORPHAN:
+               SG_LOG(3, sfp, "%s:    SG_SET_KEEP_ORPHAN\n", __func__);
                result = get_user(val, ip);
                if (result)
                        return result;
                sfp->keep_orphan = val;
                return 0;
        case SG_GET_KEEP_ORPHAN:
+               SG_LOG(3, sfp, "%s:    SG_GET_KEEP_ORPHAN\n", __func__);
                return put_user((int) sfp->keep_orphan, ip);
+       case SG_GET_VERSION_NUM:
+               SG_LOG(3, sfp, "%s:    SG_GET_VERSION_NUM\n", __func__);
+               return put_user(sg_version_num, ip);
+       case SG_GET_REQUEST_TABLE:
+               return sg_ctl_req_tbl(sfp, p);
+       case SG_SCSI_RESET:
+               SG_LOG(3, sfp, "%s:    SG_SCSI_RESET\n", __func__);
+               break;
+       case SG_SET_TIMEOUT:
+               SG_LOG(3, sfp, "%s:    SG_SET_TIMEOUT\n", __func__);
+               result = get_user(val, ip);
+               if (result)
+                       return result;
+               if (val < 0)
+                       return -EIO;
+               if (val >= mult_frac((s64)INT_MAX, USER_HZ, HZ))
+                       val = min_t(s64, mult_frac((s64)INT_MAX, USER_HZ, HZ),
+                                   INT_MAX);
+               sfp->timeout_user = val;
+               sfp->timeout = mult_frac(val, HZ, USER_HZ);
+               return 0;
+       case SG_GET_TIMEOUT:    /* N.B. User receives timeout as return value */
+                               /* strange ..., for backward compatibility */
+               SG_LOG(3, sfp, "%s:    SG_GET_TIMEOUT\n", __func__);
+               return sfp->timeout_user;
+       case SG_SET_FORCE_LOW_DMA:
+               /*
+                * N.B. This ioctl never worked properly, but failed to
+                * return an error value. So returning '0' to keep
+                * compatibility with legacy applications.
+                */
+               SG_LOG(3, sfp, "%s:    SG_SET_FORCE_LOW_DMA\n", __func__);
+               return 0;
+       case SG_GET_LOW_DMA:
+               SG_LOG(3, sfp, "%s:    SG_GET_LOW_DMA\n", __func__);
+               return put_user((int)sdev->host->unchecked_isa_dma, ip);
        case SG_NEXT_CMD_LEN:
+               SG_LOG(3, sfp, "%s:    SG_NEXT_CMD_LEN\n", __func__);
                result = get_user(val, ip);
                if (result)
                        return result;
@@ -1170,75 +1247,73 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, 
unsigned long arg)
                        return -ENOMEM;
                sfp->next_cmd_len = (val > 0) ? val : 0;
                return 0;
-       case SG_GET_VERSION_NUM:
-               return put_user(sg_version_num, ip);
        case SG_GET_ACCESS_COUNT:
+               SG_LOG(3, sfp, "%s:    SG_GET_ACCESS_COUNT\n", __func__);
                /* faked - we don't have a real access count anymore */
-               val = (sdp->device ? 1 : 0);
+               val = (sdev ? 1 : 0);
                return put_user(val, ip);
-       case SG_GET_REQUEST_TABLE:
-               {
-                       sg_req_info_t *rinfo;
-
-                       rinfo = kcalloc(SG_MAX_QUEUE, SZ_SG_REQ_INFO,
-                                       GFP_KERNEL);
-                       if (!rinfo)
-                               return -ENOMEM;
-                       spin_lock_irqsave(&sfp->rq_list_lock, iflags);
-                       sg_fill_request_table(sfp, rinfo);
-                       spin_unlock_irqrestore(&sfp->rq_list_lock, iflags);
-                       result = copy_to_user(p, rinfo,
-                                             SZ_SG_REQ_INFO * SG_MAX_QUEUE);
-                       result = result ? -EFAULT : 0;
-                       kfree(rinfo);
-                       return result;
-               }
        case SG_EMULATED_HOST:
-               if (SG_IS_DETACHING(sdp))
-                       return -ENODEV;
-               return put_user(sdp->device->host->hostt->emulated, ip);
+               SG_LOG(3, sfp, "%s:    SG_EMULATED_HOST\n", __func__);
+               return put_user(sdev->host->hostt->emulated, ip);
        case SCSI_IOCTL_SEND_COMMAND:
-               if (SG_IS_DETACHING(sdp))
-                       return -ENODEV;
-               return sg_scsi_ioctl(sdp->device->request_queue, NULL, 
filp->f_mode, p);
+               SG_LOG(3, sfp, "%s:    SCSI_IOCTL_SEND_COMMAND\n", __func__);
+               return sg_scsi_ioctl(sdev->request_queue, NULL, filp->f_mode,
+                                    p);
        case SG_SET_DEBUG:
+               SG_LOG(3, sfp, "%s:    SG_SET_DEBUG\n", __func__);
                result = get_user(val, ip);
                if (result)
                        return result;
                assign_bit(SG_FDEV_LOG_SENSE, sdp->fdev_bm, val);
                return 0;
        case BLKSECTGET:
-               return put_user(max_sectors_bytes(sdp->device->request_queue),
-                               ip);
+               SG_LOG(3, sfp, "%s:    BLKSECTGET\n", __func__);
+               return put_user(max_sectors_bytes(sdev->request_queue), ip);
        case BLKTRACESETUP:
-               return blk_trace_setup(sdp->device->request_queue,
+               SG_LOG(3, sfp, "%s:    BLKTRACESETUP\n", __func__);
+               return blk_trace_setup(sdev->request_queue,
                                       sdp->disk->disk_name,
                                       MKDEV(SCSI_GENERIC_MAJOR, sdp->index),
                                       NULL, p);
        case BLKTRACESTART:
-               return blk_trace_startstop(sdp->device->request_queue, 1);
+               SG_LOG(3, sfp, "%s:    BLKTRACESTART\n", __func__);
+               return blk_trace_startstop(sdev->request_queue, 1);
        case BLKTRACESTOP:
-               return blk_trace_startstop(sdp->device->request_queue, 0);
+               SG_LOG(3, sfp, "%s:    BLKTRACESTOP\n", __func__);
+               return blk_trace_startstop(sdev->request_queue, 0);
        case BLKTRACETEARDOWN:
-               return blk_trace_remove(sdp->device->request_queue);
+               SG_LOG(3, sfp, "%s:    BLKTRACETEARDOWN\n", __func__);
+               return blk_trace_remove(sdev->request_queue);
        case SCSI_IOCTL_GET_IDLUN:
+               SG_LOG(3, sfp, "%s:    SCSI_IOCTL_GET_IDLUN %s\n", __func__,
+                      pmlp);
+               break;
        case SCSI_IOCTL_GET_BUS_NUMBER:
+               SG_LOG(3, sfp, "%s:    SCSI_IOCTL_GET_BUS_NUMBER%s\n",
+                      __func__, pmlp);
+               break;
        case SCSI_IOCTL_PROBE_HOST:
+               SG_LOG(3, sfp, "%s:    SCSI_IOCTL_PROBE_HOST%s",
+                      __func__, pmlp);
+               break;
        case SG_GET_TRANSFORM:
-       case SG_SCSI_RESET:
-               if (SG_IS_DETACHING(sdp))
-                       return -ENODEV;
+               SG_LOG(3, sfp, "%s:    SG_GET_TRANSFORM%s\n", __func__, pmlp);
+               break;
+       case SG_SET_TRANSFORM:
+               SG_LOG(3, sfp, "%s:    SG_SET_TRANSFORM%s\n", __func__, pmlp);
                break;
        default:
+               SG_LOG(3, sfp, "%s:    unrecognized ioctl [0x%x]%s\n",
+                      __func__, cmd_in, pmlp);
                if (read_only)
-                       return -EPERM;  /* don't know so take safe approach */
+                       return -EPERM;  /* don't know, so take safer approach */
                break;
        }
 
        result = sg_allow_if_err_recovery(sdp, filp->f_flags & O_NDELAY);
        if (result)
                return result;
-       return scsi_ioctl(sdp->device, cmd_in, p);
+       return scsi_ioctl(sdev, cmd_in, p);
 }
 
 #if IS_ENABLED(CONFIG_COMPAT)
-- 
2.23.0

Reply via email to