]> granicus.if.org Git - strace/commitdiff
Split scsi.c
authorDmitry V. Levin <ldv@altlinux.org>
Sun, 8 Jan 2017 00:01:03 +0000 (00:01 +0000)
committerDmitry V. Levin <ldv@altlinux.org>
Sun, 8 Jan 2017 00:01:03 +0000 (00:01 +0000)
Move decoders of SG_IO v3 and v4 APIs into separate files,
compile generic SG_IO decoder unconditionally.

* sg_io_v3.c: New file.
* sg_io_v4.c: Likewise.
* Makefile.am (strace_SOURCES): Add them.
* defs.h (decode_sg_io_v3, decode_sg_io_v4): New prototypes.
* ioctl.c (ioctl_decode): Remove [HAVE_SCSI_SG_H] condition.
* scsi.c: (print_sg_io_res): Remove.
(print_sg_io_req): Rename to decode_sg_io, replace print_sg_io_v3_req
with decode_sg_io_v3, replace print_sg_io_v4_req with decode_sg_io_v4,
compile uncoditionally.
(scsi_ioctl): Replace print_sg_io_req and print_sg_io_res
with decode_sg_io, compile uncoditionally.
Move [HAVE_SCSI_SG_H] code to sg_io_v3.c.
Move [HAVE_LINUX_BSG_H] code to sg_io_v4.c.

Makefile.am
defs.h
ioctl.c
scsi.c
sg_io_v3.c [new file with mode: 0644]
sg_io_v4.c [new file with mode: 0644]

index 3369e1ef887810c806cad47eb1c22603f547d3f9..da5bfa218b9afa04525d02e85260aca013bed9df 100644 (file)
@@ -214,6 +214,8 @@ strace_SOURCES =    \
        seccomp.c       \
        seccomp_fprog.h \
        sendfile.c      \
+       sg_io_v3.c      \
+       sg_io_v4.c      \
        sigaltstack.c   \
        sigevent.h      \
        signal.c        \
diff --git a/defs.h b/defs.h
index 0a39650fe3dd3526a8551390c7c9744c1d7480be..7a874c911095af4300df62ec48411413b7c8d4c7 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -638,6 +638,9 @@ DECL_IOCTL(ubi);
 DECL_IOCTL(uffdio);
 #undef DECL_IOCTL
 
+extern int decode_sg_io_v3(struct tcb *, const kernel_ulong_t arg);
+extern int decode_sg_io_v4(struct tcb *, const kernel_ulong_t arg);
+
 extern int tv_nz(const struct timeval *);
 extern int tv_cmp(const struct timeval *, const struct timeval *);
 extern double tv_float(const struct timeval *);
diff --git a/ioctl.c b/ioctl.c
index 88c75ff4c3249cad5acdd6e1330d8a6d7d3c4e03..aa1880f8de56d6eccf260aa8331f4f4c915eb597 100644 (file)
--- a/ioctl.c
+++ b/ioctl.c
@@ -255,10 +255,8 @@ ioctl_decode(struct tcb *tcp)
                return block_ioctl(tcp, code, arg);
        case 'X':
                return fs_x_ioctl(tcp, code, arg);
-#ifdef HAVE_SCSI_SG_H
        case 0x22:
                return scsi_ioctl(tcp, code, arg);
-#endif
        case 'L':
                return loop_ioctl(tcp, code, arg);
        case 'M':
diff --git a/scsi.c b/scsi.c
index 3431f3bfea721bd73e91ae54c2242ef31804b2f6..3e90d91cf8285d2cf61984b60a9dfbaf5342d131 100644 (file)
--- a/scsi.c
+++ b/scsi.c
 #include "defs.h"
 
 #ifdef HAVE_SCSI_SG_H
-
-# include <linux/ioctl.h>
 # include <scsi/sg.h>
+#endif
 
-# include "xlat/sg_io_dxfer_direction.h"
-
-# ifdef HAVE_LINUX_BSG_H
-#  include <linux/bsg.h>
-#  include <sys/uio.h>
-#  include "xlat/bsg_protocol.h"
-#  include "xlat/bsg_subprotocol.h"
-# endif
-
-static void
-print_sg_io_buffer(struct tcb *const tcp, const kernel_ulong_t addr,
-                  const unsigned int len)
-{
-       printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
-}
-
-static int
-print_sg_io_v3_req(struct tcb *const tcp, const kernel_ulong_t arg)
-{
-       struct sg_io_hdr sg_io;
-       static const size_t skip_iid =
-               offsetof(struct sg_io_hdr, dxfer_direction);
-
-       tprints("{interface_id='S', ");
-       if (umoven_or_printaddr(tcp, arg + skip_iid, sizeof(sg_io) - skip_iid,
-                               &sg_io.dxfer_direction)) {
-               tprints("}");
-               return RVAL_DECODED | 1;
-       }
-
-       tprints("dxfer_direction=");
-       printxval(sg_io_dxfer_direction, sg_io.dxfer_direction,
-                 "SG_DXFER_???");
-       tprintf(", cmd[%u]=", sg_io.cmd_len);
-       print_sg_io_buffer(tcp, ptr_to_kulong(sg_io.cmdp), sg_io.cmd_len);
-       tprintf(", mx_sb_len=%d", sg_io.mx_sb_len);
-       tprintf(", iovec_count=%d", sg_io.iovec_count);
-       tprintf(", dxfer_len=%u", sg_io.dxfer_len);
-       tprintf(", timeout=%u", sg_io.timeout);
-       tprintf(", flags=%#x", sg_io.flags);
-
-       if (sg_io.dxfer_direction == SG_DXFER_TO_DEV ||
-           sg_io.dxfer_direction == SG_DXFER_TO_FROM_DEV) {
-               tprintf(", data[%u]=", sg_io.dxfer_len);
-               if (sg_io.iovec_count)
-                       tprint_iov_upto(tcp, sg_io.iovec_count,
-                                       ptr_to_kulong(sg_io.dxferp),
-                                       IOV_DECODE_STR,
-                                       sg_io.dxfer_len);
-               else
-                       print_sg_io_buffer(tcp, ptr_to_kulong(sg_io.dxferp),
-                                          sg_io.dxfer_len);
-       }
-       return 1;
-}
-
-static void
-print_sg_io_v3_res(struct tcb *const tcp, const kernel_ulong_t arg)
-{
-       struct sg_io_hdr sg_io;
-
-       if (umove(tcp, arg, &sg_io) < 0) {
-               tprints(", ???");
-               return;
-       }
-
-       if (sg_io.interface_id != (unsigned char) 'S') {
-               tprintf(" => interface_id=%u", sg_io.interface_id);
-               return;
-       }
-
-       if (sg_io.dxfer_direction == SG_DXFER_FROM_DEV ||
-           sg_io.dxfer_direction == SG_DXFER_TO_FROM_DEV) {
-               uint32_t din_len = sg_io.dxfer_len;
-
-               if (sg_io.resid > 0)
-                       din_len -= sg_io.resid;
-               tprintf(", data[%u]=", din_len);
-               if (sg_io.iovec_count)
-                       tprint_iov_upto(tcp, sg_io.iovec_count,
-                                       ptr_to_kulong(sg_io.dxferp),
-                                       syserror(tcp) ? IOV_DECODE_ADDR :
-                                       IOV_DECODE_STR, din_len);
-               else
-                       print_sg_io_buffer(tcp, ptr_to_kulong(sg_io.dxferp),
-                                          din_len);
-       }
-       tprintf(", status=%02x", sg_io.status);
-       tprintf(", masked_status=%02x", sg_io.masked_status);
-       tprintf(", sb[%u]=", sg_io.sb_len_wr);
-       print_sg_io_buffer(tcp, ptr_to_kulong(sg_io.sbp), sg_io.sb_len_wr);
-       tprintf(", host_status=%#x", sg_io.host_status);
-       tprintf(", driver_status=%#x", sg_io.driver_status);
-       tprintf(", resid=%d", sg_io.resid);
-       tprintf(", duration=%d", sg_io.duration);
-       tprintf(", info=%#x", sg_io.info);
-}
-
-#ifdef HAVE_LINUX_BSG_H
-
-static int
-print_sg_io_v4_req(struct tcb *const tcp, const kernel_ulong_t arg)
-{
-       struct sg_io_v4 sg_io;
-       static const size_t skip_iid = offsetof(struct sg_io_v4, protocol);
-
-
-       tprints("{guard='Q', ");
-       if (umoven_or_printaddr(tcp, arg + skip_iid, sizeof(sg_io) - skip_iid,
-                               &sg_io.protocol)) {
-               tprints("}");
-               return RVAL_DECODED | 1;
-       }
-
-       tprints("protocol=");
-       printxval(bsg_protocol, sg_io.protocol, "BSG_PROTOCOL_???");
-       tprints(", subprotocol=");
-       printxval(bsg_subprotocol, sg_io.subprotocol, "BSG_SUB_PROTOCOL_???");
-       tprintf(", request[%u]=", sg_io.request_len);
-       print_sg_io_buffer(tcp, sg_io.request, sg_io.request_len);
-       tprintf(", request_tag=%" PRI__u64, sg_io.request_tag);
-       tprintf(", request_attr=%u", sg_io.request_attr);
-       tprintf(", request_priority=%u", sg_io.request_priority);
-       tprintf(", request_extra=%u", sg_io.request_extra);
-       tprintf(", max_response_len=%u", sg_io.max_response_len);
-
-       tprintf(", dout_iovec_count=%u", sg_io.dout_iovec_count);
-       tprintf(", dout_xfer_len=%u", sg_io.dout_xfer_len);
-       tprintf(", din_iovec_count=%u", sg_io.din_iovec_count);
-       tprintf(", din_xfer_len=%u", sg_io.din_xfer_len);
-       tprintf(", timeout=%u", sg_io.timeout);
-       tprintf(", flags=%u", sg_io.flags);
-       tprintf(", usr_ptr=%" PRI__u64, sg_io.usr_ptr);
-       tprintf(", spare_in=%u", sg_io.spare_in);
-       tprintf(", dout[%u]=", sg_io.dout_xfer_len);
-       if (sg_io.dout_iovec_count)
-               tprint_iov_upto(tcp, sg_io.dout_iovec_count, sg_io.dout_xferp,
-                               IOV_DECODE_STR, sg_io.dout_xfer_len);
-       else
-               print_sg_io_buffer(tcp, sg_io.dout_xferp, sg_io.dout_xfer_len);
-       return 1;
-}
-
-static void
-print_sg_io_v4_res(struct tcb *const tcp, const kernel_ulong_t arg)
-{
-       struct sg_io_v4 sg_io;
-       uint32_t din_len;
-
-       if (umove(tcp, arg, &sg_io) < 0) {
-               tprints(", ???");
-               return;
-       }
-
-       if (sg_io.guard != (unsigned char) 'Q') {
-               tprintf(" => guard=%u", sg_io.guard);
-               return;
-       }
-
-       tprintf(", response[%u]=", sg_io.response_len);
-       print_sg_io_buffer(tcp, sg_io.response, sg_io.response_len);
-       din_len = sg_io.din_xfer_len;
-       if (sg_io.din_resid > 0)
-               din_len -= sg_io.din_resid;
-       tprintf(", din[%u]=", din_len);
-       if (sg_io.din_iovec_count)
-               tprint_iov_upto(tcp, sg_io.din_iovec_count, sg_io.din_xferp,
-                               syserror(tcp) ? IOV_DECODE_ADDR :
-                               IOV_DECODE_STR, din_len);
-       else
-               print_sg_io_buffer(tcp, sg_io.din_xferp, din_len);
-       tprintf(", driver_status=%u", sg_io.driver_status);
-       tprintf(", transport_status=%u", sg_io.transport_status);
-       tprintf(", device_status=%u", sg_io.device_status);
-       tprintf(", retry_delay=%u", sg_io.retry_delay);
-       tprintf(", info=%u", sg_io.info);
-       tprintf(", duration=%u", sg_io.duration);
-       tprintf(", response_len=%u", sg_io.response_len);
-       tprintf(", din_resid=%u", sg_io.din_resid);
-       tprintf(", dout_resid=%u", sg_io.dout_resid);
-       tprintf(", generated_tag=%" PRI__u64, sg_io.generated_tag);
-       tprintf(", spare_out=%u", sg_io.spare_out);
-}
-
-#else /* !HAVE_LINUX_BSG_H */
-
-static int
-print_sg_io_v4_req(struct tcb *const tcp, const kernel_ulong_t arg)
-{
-       tprints("{guard='Q', ...}");
-       return RVAL_DECODED | 1;
-}
-
-static void
-print_sg_io_v4_res(struct tcb *const tcp, const kernel_ulong_t arg)
-{
-}
-
+#ifndef SG_IO
+# define SG_IO 0x2285
 #endif
 
 static int
-print_sg_io_req(struct tcb *const tcp, const uint32_t iid,
-               const kernel_ulong_t arg)
+decode_sg_io(struct tcb *const tcp, const uint32_t iid,
+            const kernel_ulong_t arg)
 {
        switch (iid) {
-       case 'S':
-               return print_sg_io_v3_req(tcp, arg);
-       case 'Q':
-               return print_sg_io_v4_req(tcp, arg);
-       default:
-               tprintf("[%u]", iid);
-               return RVAL_DECODED | 1;
+               case 'S':
+                       return decode_sg_io_v3(tcp, arg);
+               case 'Q':
+                       return decode_sg_io_v4(tcp, arg);
+               default:
+                       tprintf("[%u]", iid);
+                       return RVAL_DECODED | 1;
        }
 
 }
 
-static void
-print_sg_io_res(struct tcb *const tcp, const uint32_t iid,
-               const kernel_ulong_t arg)
-{
-       switch (iid) {
-       case 'S':
-               print_sg_io_v3_res(tcp, arg);
-               break;
-       case 'Q':
-               print_sg_io_v4_res(tcp, arg);
-               break;
-       }
-}
-
 int
 scsi_ioctl(struct tcb *const tcp, const unsigned int code,
           const kernel_ulong_t arg)
@@ -278,16 +67,14 @@ scsi_ioctl(struct tcb *const tcp, const unsigned int code,
                        return RVAL_DECODED | 1;
                } else {
                        set_tcb_priv_ulong(tcp, iid);
-                       return print_sg_io_req(tcp, iid, arg);
+                       return decode_sg_io(tcp, iid, arg);
                }
        } else {
                if (!syserror(tcp)) {
                        iid = get_tcb_priv_ulong(tcp);
-                       print_sg_io_res(tcp, iid, arg);
+                       decode_sg_io(tcp, iid, arg);
                }
                tprints("}");
                return RVAL_DECODED | 1;
        }
 }
-
-#endif /* HAVE_SCSI_SG_H */
diff --git a/sg_io_v3.c b/sg_io_v3.c
new file mode 100644 (file)
index 0000000..47d847d
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2007 Vladimir Nadvornik <nadvornik@suse.cz>
+ * Copyright (c) 2007-2017 Dmitry V. Levin <ldv@altlinux.org>
+ * Copyright (c) 2015 Bart Van Assche <bart.vanassche@sandisk.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "defs.h"
+
+#ifdef HAVE_SCSI_SG_H
+
+# include <scsi/sg.h>
+# include "xlat/sg_io_dxfer_direction.h"
+
+static void
+print_sg_io_buffer(struct tcb *const tcp, const kernel_ulong_t addr,
+                  const unsigned int len)
+{
+       printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
+}
+
+static int
+decode_request(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+       struct sg_io_hdr sg_io;
+       static const size_t skip_iid =
+               offsetof(struct sg_io_hdr, dxfer_direction);
+
+       tprints("{interface_id='S', ");
+       if (umoven_or_printaddr(tcp, arg + skip_iid, sizeof(sg_io) - skip_iid,
+                               &sg_io.dxfer_direction)) {
+               tprints("}");
+               return RVAL_DECODED | 1;
+       }
+
+       tprints("dxfer_direction=");
+       printxval(sg_io_dxfer_direction, sg_io.dxfer_direction,
+                 "SG_DXFER_???");
+       tprintf(", cmd[%u]=", sg_io.cmd_len);
+       print_sg_io_buffer(tcp, ptr_to_kulong(sg_io.cmdp), sg_io.cmd_len);
+       tprintf(", mx_sb_len=%d", sg_io.mx_sb_len);
+       tprintf(", iovec_count=%d", sg_io.iovec_count);
+       tprintf(", dxfer_len=%u", sg_io.dxfer_len);
+       tprintf(", timeout=%u", sg_io.timeout);
+       tprintf(", flags=%#x", sg_io.flags);
+
+       if (sg_io.dxfer_direction == SG_DXFER_TO_DEV ||
+           sg_io.dxfer_direction == SG_DXFER_TO_FROM_DEV) {
+               tprintf(", data[%u]=", sg_io.dxfer_len);
+               if (sg_io.iovec_count)
+                       tprint_iov_upto(tcp, sg_io.iovec_count,
+                                       ptr_to_kulong(sg_io.dxferp),
+                                       IOV_DECODE_STR,
+                                       sg_io.dxfer_len);
+               else
+                       print_sg_io_buffer(tcp, ptr_to_kulong(sg_io.dxferp),
+                                          sg_io.dxfer_len);
+       }
+       return 1;
+}
+
+static int
+decode_response(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+       struct sg_io_hdr sg_io;
+
+       if (umove(tcp, arg, &sg_io) < 0) {
+               tprints(", ???");
+               return RVAL_DECODED | 1;
+       }
+
+       if (sg_io.interface_id != (unsigned char) 'S') {
+               tprintf(" => interface_id=%u", sg_io.interface_id);
+               return RVAL_DECODED | 1;
+       }
+
+       if (sg_io.dxfer_direction == SG_DXFER_FROM_DEV ||
+           sg_io.dxfer_direction == SG_DXFER_TO_FROM_DEV) {
+               uint32_t din_len = sg_io.dxfer_len;
+
+               if (sg_io.resid > 0)
+                       din_len -= sg_io.resid;
+               tprintf(", data[%u]=", din_len);
+               if (sg_io.iovec_count)
+                       tprint_iov_upto(tcp, sg_io.iovec_count,
+                                       ptr_to_kulong(sg_io.dxferp),
+                                       syserror(tcp) ? IOV_DECODE_ADDR :
+                                       IOV_DECODE_STR, din_len);
+               else
+                       print_sg_io_buffer(tcp, ptr_to_kulong(sg_io.dxferp),
+                                          din_len);
+       }
+       tprintf(", status=%02x", sg_io.status);
+       tprintf(", masked_status=%02x", sg_io.masked_status);
+       tprintf(", sb[%u]=", sg_io.sb_len_wr);
+       print_sg_io_buffer(tcp, ptr_to_kulong(sg_io.sbp), sg_io.sb_len_wr);
+       tprintf(", host_status=%#x", sg_io.host_status);
+       tprintf(", driver_status=%#x", sg_io.driver_status);
+       tprintf(", resid=%d", sg_io.resid);
+       tprintf(", duration=%d", sg_io.duration);
+       tprintf(", info=%#x", sg_io.info);
+
+       return RVAL_DECODED | 1;
+}
+
+#else /* !HAVE_SCSI_SG_H */
+
+static int
+decode_request(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+       tprints("{interface_id='S', ...}");
+       return RVAL_DECODED | 1;
+}
+
+static int
+decode_response(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+       return 0;
+}
+
+#endif
+
+int
+decode_sg_io_v3(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+       return entering(tcp) ? decode_request(tcp, arg)
+                            : decode_response(tcp, arg);
+}
diff --git a/sg_io_v4.c b/sg_io_v4.c
new file mode 100644 (file)
index 0000000..ba4a071
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2015 Bart Van Assche <bart.vanassche@sandisk.com>
+ * Copyright (c) 2015-2017 Dmitry V. Levin <ldv@altlinux.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "defs.h"
+
+#ifdef HAVE_LINUX_BSG_H
+
+# include <linux/bsg.h>
+# include "xlat/bsg_protocol.h"
+# include "xlat/bsg_subprotocol.h"
+
+static void
+print_sg_io_buffer(struct tcb *const tcp, const kernel_ulong_t addr,
+                  const unsigned int len)
+{
+       printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
+}
+
+static int
+decode_request(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+       struct sg_io_v4 sg_io;
+       static const size_t skip_iid = offsetof(struct sg_io_v4, protocol);
+
+       tprints("{guard='Q', ");
+       if (umoven_or_printaddr(tcp, arg + skip_iid, sizeof(sg_io) - skip_iid,
+                               &sg_io.protocol)) {
+               tprints("}");
+               return RVAL_DECODED | 1;
+       }
+
+       tprints("protocol=");
+       printxval(bsg_protocol, sg_io.protocol, "BSG_PROTOCOL_???");
+       tprints(", subprotocol=");
+       printxval(bsg_subprotocol, sg_io.subprotocol, "BSG_SUB_PROTOCOL_???");
+       tprintf(", request[%u]=", sg_io.request_len);
+       print_sg_io_buffer(tcp, sg_io.request, sg_io.request_len);
+       tprintf(", request_tag=%" PRI__u64, sg_io.request_tag);
+       tprintf(", request_attr=%u", sg_io.request_attr);
+       tprintf(", request_priority=%u", sg_io.request_priority);
+       tprintf(", request_extra=%u", sg_io.request_extra);
+       tprintf(", max_response_len=%u", sg_io.max_response_len);
+
+       tprintf(", dout_iovec_count=%u", sg_io.dout_iovec_count);
+       tprintf(", dout_xfer_len=%u", sg_io.dout_xfer_len);
+       tprintf(", din_iovec_count=%u", sg_io.din_iovec_count);
+       tprintf(", din_xfer_len=%u", sg_io.din_xfer_len);
+       tprintf(", timeout=%u", sg_io.timeout);
+       tprintf(", flags=%u", sg_io.flags);
+       tprintf(", usr_ptr=%" PRI__u64, sg_io.usr_ptr);
+       tprintf(", spare_in=%u", sg_io.spare_in);
+       tprintf(", dout[%u]=", sg_io.dout_xfer_len);
+       if (sg_io.dout_iovec_count)
+               tprint_iov_upto(tcp, sg_io.dout_iovec_count, sg_io.dout_xferp,
+                               IOV_DECODE_STR, sg_io.dout_xfer_len);
+       else
+               print_sg_io_buffer(tcp, sg_io.dout_xferp, sg_io.dout_xfer_len);
+       return 1;
+}
+
+static int
+decode_response(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+       struct sg_io_v4 sg_io;
+       uint32_t din_len;
+
+       if (umove(tcp, arg, &sg_io) < 0) {
+               tprints(", ???");
+               return RVAL_DECODED | 1;
+       }
+
+       if (sg_io.guard != (unsigned char) 'Q') {
+               tprintf(" => guard=%u", sg_io.guard);
+               return RVAL_DECODED | 1;
+       }
+
+       tprintf(", response[%u]=", sg_io.response_len);
+       print_sg_io_buffer(tcp, sg_io.response, sg_io.response_len);
+       din_len = sg_io.din_xfer_len;
+       if (sg_io.din_resid > 0)
+               din_len -= sg_io.din_resid;
+       tprintf(", din[%u]=", din_len);
+       if (sg_io.din_iovec_count)
+               tprint_iov_upto(tcp, sg_io.din_iovec_count, sg_io.din_xferp,
+                               syserror(tcp) ? IOV_DECODE_ADDR :
+                               IOV_DECODE_STR, din_len);
+       else
+               print_sg_io_buffer(tcp, sg_io.din_xferp, din_len);
+       tprintf(", driver_status=%u", sg_io.driver_status);
+       tprintf(", transport_status=%u", sg_io.transport_status);
+       tprintf(", device_status=%u", sg_io.device_status);
+       tprintf(", retry_delay=%u", sg_io.retry_delay);
+       tprintf(", info=%u", sg_io.info);
+       tprintf(", duration=%u", sg_io.duration);
+       tprintf(", response_len=%u", sg_io.response_len);
+       tprintf(", din_resid=%u", sg_io.din_resid);
+       tprintf(", dout_resid=%u", sg_io.dout_resid);
+       tprintf(", generated_tag=%" PRI__u64, sg_io.generated_tag);
+       tprintf(", spare_out=%u", sg_io.spare_out);
+
+       return RVAL_DECODED | 1;
+}
+
+#else /* !HAVE_LINUX_BSG_H */
+
+static int
+decode_request(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+       tprints("{guard='Q', ...}");
+       return RVAL_DECODED | 1;
+}
+
+static int
+decode_response(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+       return 0;
+}
+
+#endif
+
+int
+decode_sg_io_v4(struct tcb *const tcp, const kernel_ulong_t arg)
+{
+       return entering(tcp) ? decode_request(tcp, arg)
+                            : decode_response(tcp, arg);
+}