xref: /aosp_15_r20/external/sg3_utils/testing/sg_queue_tst.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * Copyright (C) 2010-2019 D. Gilbert
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2, or (at your option)
6  * any later version.
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  *
10  *
11  * This program was used to test SCSI mid level queue ordering.
12  * The default behaviour is to "queue at head" which is useful for
13  * error processing but not for streaming READ and WRITE commands.
14  *
15  * Invocation: sg_queue_tst [-l=Q_LEN] [-t] <sg_device>
16  *      -t      queue at tail
17  *
18  * Version 0.96 (20190128)
19  */
20 
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdbool.h>
26 #include <stdint.h>
27 #include <stdarg.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <sys/ioctl.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 
34 #ifndef HAVE_LINUX_SG_V4_HDR
35 
36 /* Kernel uapi header contain __user decorations on user space pointers
37  * to indicate they are unsafe in the kernel space. However glibc takes
38  * all those __user decorations out from headers in /usr/include/linux .
39  * So to stop compile errors when directly importing include/uapi/scsi/sg.h
40  * undef __user before doing that include. */
41 #define __user
42 
43 /* Want to block the original sg.h header from also being included. That
44  * causes lots of multiple definition errors. This will only work if this
45  * header is included _before_ the original sg.h header.  */
46 #define _SCSI_GENERIC_H         /* original kernel header guard */
47 #define _SCSI_SG_H              /* glibc header guard */
48 
49 #include "uapi_sg.h"    /* local copy of include/uapi/scsi/sg.h */
50 
51 #else
52 #define __user
53 #endif  /* end of: ifndef HAVE_LINUX_SG_V4_HDR */
54 
55 #include "sg_lib.h"
56 #include "sg_io_linux.h"
57 #include "sg_linux_inc.h"
58 
59 
60 
61 #define INQ_REPLY_LEN 96
62 #define INQ_CMD_LEN 6
63 #define SDIAG_CMD_LEN 6
64 #define SENSE_BUFFER_LEN 96
65 
66 #define EBUFF_SZ 256
67 
68 #ifndef SG_FLAG_Q_AT_TAIL
69 #define SG_FLAG_Q_AT_TAIL 0x10
70 #endif
71 
72 #ifndef SG_FLAG_Q_AT_HEAD
73 #define SG_FLAG_Q_AT_HEAD 0x20
74 #endif
75 
76 #define DEF_Q_LEN 16    /* max in sg v3 and earlier */
77 #define MAX_Q_LEN 256
78 
79 static void
set_nanosecs(int sg_fd)80 set_nanosecs(int sg_fd)
81 {
82     struct sg_extended_info sei;
83     struct sg_extended_info * seip;
84 
85     seip = &sei;
86     memset(seip, 0, sizeof(*seip));
87     seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS;
88     seip->sei_rd_mask |= SG_SEIM_CTL_FLAGS; /* this or previous optional */
89     seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_TIME_IN_NS;
90     seip->ctl_flags |= SG_CTL_FLAGM_TIME_IN_NS;
91 
92     if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
93         fprintf(stderr, "ioctl(SG_SET_GET_EXTENDED) failed, errno=%d %s\n",
94                 errno, strerror(errno));
95     }
96 }
97 
98 
main(int argc,char * argv[])99 int main(int argc, char * argv[])
100 {
101     bool q_at_tail = false;
102     bool dur_in_nanosecs = false;
103     int sg_fd, k, ok;
104     uint8_t inq_cdb[INQ_CMD_LEN] =
105                                 {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
106     uint8_t sdiag_cdb[SDIAG_CMD_LEN] =
107                                 {0x1d, 0x10 /* PF */, 0, 0, 0, 0};
108     uint8_t inqBuff[MAX_Q_LEN][INQ_REPLY_LEN];
109     sg_io_hdr_t io_hdr[MAX_Q_LEN];
110     sg_io_hdr_t rio_hdr;
111     char * file_name = 0;
112     char ebuff[EBUFF_SZ];
113     uint8_t sense_buffer[MAX_Q_LEN][SENSE_BUFFER_LEN] SG_C_CPP_ZERO_INIT;
114     int q_len = DEF_Q_LEN;
115 
116     for (k = 1; k < argc; ++k) {
117         if (0 == memcmp("-n", argv[k], 2))
118             dur_in_nanosecs = true;
119         else if (0 == memcmp("-t", argv[k], 2))
120             q_at_tail = true;
121         else if (0 == memcmp("-l=", argv[k], 3)) {
122             q_len = atoi(argv[k] + 3);
123             if ((q_len > 511) || (q_len < 1)) {
124                 printf("Expect -l= to take a number (q length) between 1 "
125                        "and 511\n");
126                 file_name = 0;
127                 break;
128             }
129 
130         } else if (*argv[k] == '-') {
131             printf("Unrecognized switch: %s\n", argv[k]);
132             file_name = 0;
133             break;
134         }
135         else if (0 == file_name)
136             file_name = argv[k];
137         else {
138             printf("too many arguments\n");
139             file_name = 0;
140             break;
141         }
142     }
143     if (0 == file_name) {
144         printf("Usage: 'sg_queue_tst [-l=Q_LEN] [-n] [-t] <sg_device>'\n"
145                "where:\n"
146                "      -l=Q_LEN    queue length, between 1 and 511 "
147                "(def: 16)\n"
148                "      -n    duration in nanosecs (def: milliseconds)\n"
149                "      -t    queue_at_tail (def: q_at_head)\n");
150         return 1;
151     }
152 
153     /* An access mode of O_RDWR is required for write()/read() interface */
154     if ((sg_fd = open(file_name, O_RDWR)) < 0) {
155         snprintf(ebuff, EBUFF_SZ,
156                  "sg_queue_tst: error opening file: %s", file_name);
157         perror(ebuff);
158         return 1;
159     }
160     if (dur_in_nanosecs)
161         set_nanosecs(sg_fd);
162 
163     for (k = 0; k < q_len; ++k) {
164         /* Prepare INQUIRY command */
165         memset(&io_hdr[k], 0, sizeof(sg_io_hdr_t));
166         io_hdr[k].interface_id = 'S';
167         /* io_hdr[k].iovec_count = 0; */  /* memset takes care of this */
168         io_hdr[k].mx_sb_len = (uint8_t)sizeof(sense_buffer);
169         if (0 == (k % 3)) {
170             io_hdr[k].cmd_len = sizeof(sdiag_cdb);
171             io_hdr[k].cmdp = sdiag_cdb;
172             io_hdr[k].dxfer_direction = SG_DXFER_NONE;
173         } else {
174             io_hdr[k].cmd_len = sizeof(inq_cdb);
175             io_hdr[k].cmdp = inq_cdb;
176             io_hdr[k].dxfer_direction = SG_DXFER_FROM_DEV;
177             io_hdr[k].dxfer_len = INQ_REPLY_LEN;
178             io_hdr[k].dxferp = inqBuff[k];
179         }
180         io_hdr[k].sbp = sense_buffer[k];
181         io_hdr[k].mx_sb_len = SENSE_BUFFER_LEN;
182         io_hdr[k].timeout = 20000;     /* 20000 millisecs == 20 seconds */
183         io_hdr[k].pack_id = k;
184         /* default is to queue at head (in SCSI mid level) */
185         if (q_at_tail)
186             io_hdr[k].flags |= SG_FLAG_Q_AT_TAIL;
187         else
188             io_hdr[k].flags |= SG_FLAG_Q_AT_HEAD;
189         /* io_hdr[k].usr_ptr = NULL; */
190 
191         if (write(sg_fd, &io_hdr[k], sizeof(sg_io_hdr_t)) < 0) {
192             perror("sg_queue_tst: sg write error");
193             close(sg_fd);
194             return 1;
195         }
196     }
197     /* sleep(3); */
198     for (k = 0; k < q_len; ++k) {
199         memset(&rio_hdr, 0, sizeof(sg_io_hdr_t));
200         rio_hdr.interface_id = 'S';
201         if (read(sg_fd, &rio_hdr, sizeof(sg_io_hdr_t)) < 0) {
202             perror("sg_queue_tst: sg read error");
203             close(sg_fd);
204             return 1;
205         }
206         /* now for the error processing */
207         ok = 0;
208         switch (sg_err_category3(&rio_hdr)) {
209         case SG_LIB_CAT_CLEAN:
210             ok = 1;
211             break;
212         case SG_LIB_CAT_RECOVERED:
213             printf("Recovered error, continuing\n");
214             ok = 1;
215             break;
216         default: /* won't bother decoding other categories */
217             sg_chk_n_print3("command error", &rio_hdr, 1);
218             break;
219         }
220 
221         if (ok) { /* output result if it is available */
222             /* if (0 == rio_hdr.pack_id) */
223             if (0 == (rio_hdr.pack_id % 3))
224                 printf("SEND DIAGNOSTIC %d duration=%u %s\n", rio_hdr.pack_id,
225                        rio_hdr.duration, (dur_in_nanosecs ? "ns" : "ms"));
226             else
227                 printf("INQUIRY %d duration=%u %s\n", rio_hdr.pack_id,
228                        rio_hdr.duration, (dur_in_nanosecs ? "ns" : "ms"));
229         }
230     }
231 
232     close(sg_fd);
233     return 0;
234 }
235