Add ioctl(SG_IOSUBMIT_V3) and ioctl(SG_IORECEIVE_V3). These ioctls
are meant to be (almost) drop-in replacements for the write()/read()
async version 3 interface. They only accept the version 3 interface.

See the webpage at: http://sg.danny.cz/sg/sg_v40.html
specifically the table in the section titled: "12 SG interface
support changes".

If sgv3 is a struct sg_io_hdr object, suitably configured, then
    res = write(sg_fd, &sgv3, sizeof(sgv3));
and
    res = ioctl(sg_fd, SG_IOSUBMIT_V3, &sgv3);
are equivalent. Dito for read() and ioctl(SG_IORECEIVE_V3).

Signed-off-by: Douglas Gilbert <dgilb...@interlog.com>
Reported-by: kbuild test robot <l...@intel.com>

-
kbuild had two complaints about this commit:
  - casting a pointer to u64: add intermediate cast to
    unsigned long. Would only seem to be a problem if
    a pointer couldn't fit in u64 in which case the
    sg v4 interface would be broken and lots of other
    things besides ...
  - a possible uninitialized  variable:
        __maybe_unused bool is_bad_st = false;
        __maybe_unused enum sg_rq_state bad_sr_st; /* <-- here */
    The boolean (is_bad_st) tracks whether it has a "valid"
    string or not. kbuild doesn't undrstand that. Give bad_sr_st
    an initial value to keep kbuild quiet at the expense of a
    machine cycle or two.

Signed-off-by: Douglas Gilbert <dgilb...@interlog.com>
---
 drivers/scsi/sg.c      | 79 ++++++++++++++++++++++++++++++++++++++++--
 include/uapi/scsi/sg.h |  6 ++++
 2 files changed, 83 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index a60c2bcc5430..aa3512b2cb4f 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -806,6 +806,24 @@ sg_ctl_iosubmit(struct file *filp, struct sg_fd *sfp, void 
__user *p)
        return -EPERM;
 }
 
+static int
+sg_ctl_iosubmit_v3(struct file *filp, struct sg_fd *sfp, void __user *p)
+{
+       int res;
+       u8 hdr_store[SZ_SG_IO_V4];      /* max(v3interface, v4interface) */
+       struct sg_io_hdr *h3p = (struct sg_io_hdr *)hdr_store;
+       struct sg_device *sdp = sfp->parentdp;
+
+       res = sg_allow_if_err_recovery(sdp, (filp->f_flags & O_NONBLOCK));
+       if (unlikely(res))
+               return res;
+       if (copy_from_user(h3p, p, SZ_SG_IO_HDR))
+               return -EFAULT;
+       if (h3p->interface_id == 'S')
+               return sg_v3_submit(filp, sfp, h3p, false, NULL);
+       return -EPERM;
+}
+
 static void
 sg_execute_cmd(struct sg_fd *sfp, struct sg_request *srp)
 {
@@ -1107,7 +1125,7 @@ sg_v4_receive(struct sg_fd *sfp, struct sg_request *srp, 
void __user *p,
        h4p->din_resid = srp->in_resid;
        h4p->dout_resid = srp->s_hdr4.out_resid;
        h4p->usr_ptr = srp->s_hdr4.usr_ptr;
-       h4p->response = (u64)srp->s_hdr4.sbp;
+       h4p->response = (u64)(unsigned long)srp->s_hdr4.sbp;
        h4p->request_extra = srp->pack_id;
        if (p) {
                if (copy_to_user(p, h4p, SZ_SG_IO_V4))
@@ -1170,6 +1188,57 @@ sg_ctl_ioreceive(struct file *filp, struct sg_fd *sfp, 
void __user *p)
        return sg_v4_receive(sfp, srp, p, h4p);
 }
 
+/*
+ * Called when ioctl(SG_IORECEIVE_V3) received. Expects a v3 interface.
+ * Checks if O_NONBLOCK file flag given, if not checks given flags field
+ * to see if SGV4_FLAG_IMMED is set. Either of these implies non blocking.
+ * When non-blocking and there is no request waiting, yields EAGAIN;
+ * otherwise it waits.
+ */
+static int
+sg_ctl_ioreceive_v3(struct file *filp, struct sg_fd *sfp, void __user *p)
+{
+       bool non_block = !!(filp->f_flags & O_NONBLOCK);
+       int res;
+       int pack_id = SG_PACK_ID_WILDCARD;
+       u8 v3_holder[SZ_SG_IO_HDR];
+       struct sg_io_hdr *h3p = (struct sg_io_hdr *)v3_holder;
+       struct sg_device *sdp = sfp->parentdp;
+       struct sg_request *srp;
+
+       res = sg_allow_if_err_recovery(sdp, non_block);
+       if (unlikely(res))
+               return res;
+       /* Get first three 32 bit integers: guard, proto+subproto */
+       if (copy_from_user(h3p, p, SZ_SG_IO_HDR))
+               return -EFAULT;
+       /* for v3: interface_id=='S' (in a 32 bit int) */
+       if (h3p->interface_id != 'S')
+               return -EPERM;
+       if (h3p->flags & SGV4_FLAG_IMMED)
+               non_block = true;       /* set by either this or O_NONBLOCK */
+       SG_LOG(3, sfp, "%s: non_block(+IMMED)=%d\n", __func__, non_block);
+
+       if (test_bit(SG_FFD_FORCE_PACKID, sfp->ffd_bm))
+               pack_id = h3p->pack_id;
+
+       srp = sg_find_srp_by_id(sfp, pack_id);
+       if (!srp) {     /* nothing available so wait on packet or */
+               if (unlikely(SG_IS_DETACHING(sdp)))
+                       return -ENODEV;
+               if (non_block)
+                       return -EAGAIN;
+               res = wait_event_interruptible
+                               (sfp->read_wait,
+                                sg_get_ready_srp(sfp, &srp, pack_id));
+               if (unlikely(SG_IS_DETACHING(sdp)))
+                       return -ENODEV;
+               if (unlikely(res))
+                       return res;     /* signal --> -ERESTARTSYS */
+       }       /* now srp should be valid */
+       return sg_v3_receive(sfp, srp, p);
+}
+
 static int
 sg_rd_v1v2(void __user *buf, int count, struct sg_fd *sfp,
           struct sg_request *srp)
@@ -1731,9 +1800,15 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, 
unsigned long arg)
        case SG_IOSUBMIT:
                SG_LOG(3, sfp, "%s:    SG_IOSUBMIT\n", __func__);
                return sg_ctl_iosubmit(filp, sfp, p);
+       case SG_IOSUBMIT_V3:
+               SG_LOG(3, sfp, "%s:    SG_IOSUBMIT_V3\n", __func__);
+               return sg_ctl_iosubmit_v3(filp, sfp, p);
        case SG_IORECEIVE:
                SG_LOG(3, sfp, "%s:    SG_IORECEIVE\n", __func__);
                return sg_ctl_ioreceive(filp, sfp, p);
+       case SG_IORECEIVE_V3:
+               SG_LOG(3, sfp, "%s:    SG_IORECEIVE_V3\n", __func__);
+               return sg_ctl_ioreceive_v3(filp, sfp, p);
        case SG_GET_SCSI_ID:
                return sg_ctl_scsi_id(sdev, sfp, p);
        case SG_SET_FORCE_PACK_ID:
@@ -3000,7 +3075,7 @@ static struct sg_request *
 sg_find_srp_by_id(struct sg_fd *sfp, int pack_id)
 {
        __maybe_unused bool is_bad_st = false;
-       __maybe_unused enum sg_rq_state bad_sr_st;
+       __maybe_unused enum sg_rq_state bad_sr_st = SG_RS_INACTIVE;
        bool search_for_1 = (pack_id != SG_PACK_ID_WILDCARD);
        enum sg_rq_state sr_st;
        int res;
diff --git a/include/uapi/scsi/sg.h b/include/uapi/scsi/sg.h
index 0d6fbf976e5c..f2f898a57ce8 100644
--- a/include/uapi/scsi/sg.h
+++ b/include/uapi/scsi/sg.h
@@ -356,6 +356,12 @@ struct sg_header {
 /* Gives some v4 identifying info to driver, receives associated response */
 #define SG_IORECEIVE _IOWR(SG_IOCTL_MAGIC_NUM, 0x42, struct sg_io_v4)
 
+/* Submits a v3 interface object to driver */
+#define SG_IOSUBMIT_V3 _IOWR(SG_IOCTL_MAGIC_NUM, 0x45, struct sg_io_hdr)
+
+/* Gives some v3 identifying info to driver, receives associated response */
+#define SG_IORECEIVE_V3 _IOWR(SG_IOCTL_MAGIC_NUM, 0x46, struct sg_io_hdr)
+
 /* command queuing is always on when the v3 or v4 interface is used */
 #define SG_DEF_COMMAND_Q 0
 
-- 
2.22.0

Reply via email to