xref: /aosp_15_r20/external/sg3_utils/testing/sg_tst_excl.cpp (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1*44704f69SBart Van Assche /*
2*44704f69SBart Van Assche  * Copyright (c) 2013-2022 Douglas Gilbert.
3*44704f69SBart Van Assche  * All rights reserved.
4*44704f69SBart Van Assche  *
5*44704f69SBart Van Assche  * Redistribution and use in source and binary forms, with or without
6*44704f69SBart Van Assche  * modification, are permitted provided that the following conditions
7*44704f69SBart Van Assche  * are met:
8*44704f69SBart Van Assche  * 1. Redistributions of source code must retain the above copyright
9*44704f69SBart Van Assche  *    notice, this list of conditions and the following disclaimer.
10*44704f69SBart Van Assche  * 2. Redistributions in binary form must reproduce the above copyright
11*44704f69SBart Van Assche  *    notice, this list of conditions and the following disclaimer in the
12*44704f69SBart Van Assche  *    documentation and/or other materials provided with the distribution.
13*44704f69SBart Van Assche  *
14*44704f69SBart Van Assche  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*44704f69SBart Van Assche  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*44704f69SBart Van Assche  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*44704f69SBart Van Assche  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*44704f69SBart Van Assche  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*44704f69SBart Van Assche  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*44704f69SBart Van Assche  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*44704f69SBart Van Assche  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*44704f69SBart Van Assche  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*44704f69SBart Van Assche  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*44704f69SBart Van Assche  * SUCH DAMAGE.
25*44704f69SBart Van Assche  *
26*44704f69SBart Van Assche  * SPDX-License-Identifier: BSD-2-Clause
27*44704f69SBart Van Assche  */
28*44704f69SBart Van Assche 
29*44704f69SBart Van Assche #include <iostream>
30*44704f69SBart Van Assche #include <vector>
31*44704f69SBart Van Assche #include <system_error>
32*44704f69SBart Van Assche #include <thread>
33*44704f69SBart Van Assche #include <mutex>
34*44704f69SBart Van Assche #include <chrono>
35*44704f69SBart Van Assche 
36*44704f69SBart Van Assche #include <unistd.h>
37*44704f69SBart Van Assche #include <fcntl.h>
38*44704f69SBart Van Assche #include <stdio.h>
39*44704f69SBart Van Assche #include <stdlib.h>
40*44704f69SBart Van Assche #include <string.h>
41*44704f69SBart Van Assche #include <errno.h>
42*44704f69SBart Van Assche #include <ctype.h>
43*44704f69SBart Van Assche #include <sys/ioctl.h>
44*44704f69SBart Van Assche #include <sys/types.h>
45*44704f69SBart Van Assche #include <sys/stat.h>
46*44704f69SBart Van Assche 
47*44704f69SBart Van Assche #ifdef HAVE_CONFIG_H
48*44704f69SBart Van Assche #include "config.h"
49*44704f69SBart Van Assche #endif
50*44704f69SBart Van Assche 
51*44704f69SBart Van Assche #ifndef HAVE_LINUX_SG_V4_HDR
52*44704f69SBart Van Assche 
53*44704f69SBart Van Assche /* Kernel uapi header contain __user decorations on user space pointers
54*44704f69SBart Van Assche  * to indicate they are unsafe in the kernel space. However glibc takes
55*44704f69SBart Van Assche  * all those __user decorations out from headers in /usr/include/linux .
56*44704f69SBart Van Assche  * So to stop compile errors when directly importing include/uapi/scsi/sg.h
57*44704f69SBart Van Assche  * undef __user before doing that include. */
58*44704f69SBart Van Assche #define __user
59*44704f69SBart Van Assche 
60*44704f69SBart Van Assche /* Want to block the original sg.h header from also being included. That
61*44704f69SBart Van Assche  * causes lots of multiple definition errors. This will only work if this
62*44704f69SBart Van Assche  * header is included _before_ the original sg.h header.  */
63*44704f69SBart Van Assche #define _SCSI_GENERIC_H         /* original kernel header guard */
64*44704f69SBart Van Assche #define _SCSI_SG_H              /* glibc header guard */
65*44704f69SBart Van Assche 
66*44704f69SBart Van Assche #include "uapi_sg.h"    /* local copy of include/uapi/scsi/sg.h */
67*44704f69SBart Van Assche 
68*44704f69SBart Van Assche #else
69*44704f69SBart Van Assche #define __user
70*44704f69SBart Van Assche #endif  /* end of: ifndef HAVE_LINUX_SG_V4_HDR */
71*44704f69SBart Van Assche 
72*44704f69SBart Van Assche #include "sg_lib.h"
73*44704f69SBart Van Assche #include "sg_io_linux.h"
74*44704f69SBart Van Assche #include "sg_unaligned.h"
75*44704f69SBart Van Assche 
76*44704f69SBart Van Assche static const char * version_str = "1.14 20220425";
77*44704f69SBart Van Assche static const char * util_name = "sg_tst_excl";
78*44704f69SBart Van Assche 
79*44704f69SBart Van Assche /* This is a test program for checking O_EXCL on open() works. It uses
80*44704f69SBart Van Assche  * multiple threads and can be run as multiple processes and attempts
81*44704f69SBart Van Assche  * to "break" O_EXCL. The strategy is to open a device O_EXCL|O_NONBLOCK
82*44704f69SBart Van Assche  * and do a double increment on a LB then close it. Prior to the first
83*44704f69SBart Van Assche  * increment, the value is checked for even or odd. Assuming the count
84*44704f69SBart Van Assche  * starts as an even (typically 0) then it should remain even. Odd instances
85*44704f69SBart Van Assche  * are counted and reported at the end of the program, after all threads
86*44704f69SBart Van Assche  * have completed.
87*44704f69SBart Van Assche  *
88*44704f69SBart Van Assche  * This is C++ code with some things from C++11 (e.g. threads) and was
89*44704f69SBart Van Assche  * only just able to compile (when some things were reverted) with gcc/g++
90*44704f69SBart Van Assche  * version 4.7.3 found in Ubuntu 13.04 . C++11 "feature complete" support
91*44704f69SBart Van Assche  * was not available until g++ version 4.8.1 and that is only currently
92*44704f69SBart Van Assche  * found in Fedora 19 .
93*44704f69SBart Van Assche  *
94*44704f69SBart Van Assche  * The build uses various object files from the <sg3_utils>/lib directory
95*44704f69SBart Van Assche  * which is assumed to be a sibling of this examples directory. Those
96*44704f69SBart Van Assche  * object files in the lib directory can be built with:
97*44704f69SBart Van Assche  *   cd <sg3_utils> ; ./configure ; cd lib; make
98*44704f69SBart Van Assche  * Then:
99*44704f69SBart Van Assche  *   cd ../testing
100*44704f69SBart Van Assche  *   make sg_tst_excl
101*44704f69SBart Van Assche  *
102*44704f69SBart Van Assche  * Currently this utility is Linux only and assumes the SG_IO v3 interface
103*44704f69SBart Van Assche  * which is supported by sg and block devices (but not bsg devices which
104*44704f69SBart Van Assche  * require the SG_IO v4 interface). This restriction is relaxed in the
105*44704f69SBart Van Assche  * sg_tst_excl2 variant of this utility.
106*44704f69SBart Van Assche  *
107*44704f69SBart Van Assche  * BEWARE: this utility modifies a logical block (default LBA 1000) on the
108*44704f69SBart Van Assche  * given device.
109*44704f69SBart Van Assche  *
110*44704f69SBart Van Assche  */
111*44704f69SBart Van Assche 
112*44704f69SBart Van Assche using namespace std;
113*44704f69SBart Van Assche using namespace std::chrono;
114*44704f69SBart Van Assche 
115*44704f69SBart Van Assche #define DEF_NUM_PER_THREAD 200
116*44704f69SBart Van Assche #define DEF_NUM_THREADS 4
117*44704f69SBart Van Assche #define DEF_WAIT_MS 0          /* 0: yield; -1: don't wait; -2: sleep(0) */
118*44704f69SBart Van Assche 
119*44704f69SBart Van Assche 
120*44704f69SBart Van Assche #define DEF_LBA 1000U
121*44704f69SBart Van Assche 
122*44704f69SBart Van Assche #define EBUFF_SZ 256
123*44704f69SBart Van Assche 
124*44704f69SBart Van Assche static mutex odd_count_mutex;
125*44704f69SBart Van Assche static mutex console_mutex;
126*44704f69SBart Van Assche static unsigned int odd_count;
127*44704f69SBart Van Assche static unsigned int ebusy_count;
128*44704f69SBart Van Assche static unsigned int eagain_count;
129*44704f69SBart Van Assche static int sg_ifc_ver = 3;
130*44704f69SBart Van Assche 
131*44704f69SBart Van Assche 
132*44704f69SBart Van Assche static void
usage(void)133*44704f69SBart Van Assche usage(void)
134*44704f69SBart Van Assche {
135*44704f69SBart Van Assche     printf("Usage: %s [-b] [-f] [-h] [-i <sg_ver>] [-l <lba>] "
136*44704f69SBart Van Assche            "[-n <n_per_thr>]\n"
137*44704f69SBart Van Assche            "                   [-t <num_thrs>] [-V] [-w <wait_ms>] "
138*44704f69SBart Van Assche            "[-x] [-xx]\n"
139*44704f69SBart Van Assche            "                   <sg_disk_device>\n", util_name);
140*44704f69SBart Van Assche     printf("  where\n");
141*44704f69SBart Van Assche     printf("    -b                block on open (def: O_NONBLOCK)\n");
142*44704f69SBart Van Assche     printf("    -f                force: any SCSI disk (def: only "
143*44704f69SBart Van Assche            "scsi_debug)\n");
144*44704f69SBart Van Assche     printf("                      WARNING: <lba> written to\n");
145*44704f69SBart Van Assche     printf("    -h                print this usage message then exit\n");
146*44704f69SBart Van Assche     printf("    -i <sg_ver>       sg driver interface version (default: "
147*44704f69SBart Van Assche            "3)\n");
148*44704f69SBart Van Assche     printf("    -l <lba>          logical block to increment (def: %u)\n",
149*44704f69SBart Van Assche            DEF_LBA);
150*44704f69SBart Van Assche     printf("    -n <n_per_thr>    number of loops per thread "
151*44704f69SBart Van Assche            "(def: %d)\n", DEF_NUM_PER_THREAD);
152*44704f69SBart Van Assche     printf("    -t <num_thrs>     number of threads (def: %d)\n",
153*44704f69SBart Van Assche            DEF_NUM_THREADS);
154*44704f69SBart Van Assche     printf("    -V                print version number then exit\n");
155*44704f69SBart Van Assche     printf("    -w <wait_ms>      >0: sleep_for(<wait_ms>); =0: "
156*44704f69SBart Van Assche            "yield(); -1: no\n"
157*44704f69SBart Van Assche            "                      wait; -2: sleep(0)  (def: %d)\n",
158*44704f69SBart Van Assche            DEF_WAIT_MS);
159*44704f69SBart Van Assche     printf("    -x                don't use O_EXCL on first thread "
160*44704f69SBart Van Assche            "(def: use\n"
161*44704f69SBart Van Assche            "                      O_EXCL on all threads)\n"
162*44704f69SBart Van Assche            "    -xx               don't use O_EXCL on any thread\n\n");
163*44704f69SBart Van Assche     printf("Test O_EXCL open flag with Linux sg driver. Each open/close "
164*44704f69SBart Van Assche            "cycle with the\nO_EXCL flag does a double increment on "
165*44704f69SBart Van Assche            "lba (using its first 4 bytes).\nEach increment uses a READ_16, "
166*44704f69SBart Van Assche            "READ_16, increment, WRITE_16 cycle. The two\nREAD_16s are "
167*44704f69SBart Van Assche            "launched asynchronously. Note that '-xx' will run test\n"
168*44704f69SBart Van Assche            "without any O_EXCL flags.\n");
169*44704f69SBart Van Assche }
170*44704f69SBart Van Assche 
171*44704f69SBart Van Assche 
172*44704f69SBart Van Assche #define READ16_REPLY_LEN 512
173*44704f69SBart Van Assche #define READ16_CMD_LEN 16
174*44704f69SBart Van Assche #define WRITE16_REPLY_LEN 512
175*44704f69SBart Van Assche #define WRITE16_CMD_LEN 16
176*44704f69SBart Van Assche 
177*44704f69SBart Van Assche /* Opens dev_name and spins if busy (i.e. gets EBUSY), sleeping for
178*44704f69SBart Van Assche  * wait_ms milliseconds if wait_ms is positive.
179*44704f69SBart Van Assche  * Reads lba (twice) and treats the first 4 bytes as an int (SCSI endian),
180*44704f69SBart Van Assche  * increments it and writes it back. Repeats so that happens twice. Then
181*44704f69SBart Van Assche  * closes dev_name. If an error occurs returns -1 else returns 0 if
182*44704f69SBart Van Assche  * first int read from lba is even otherwise returns 1. */
183*44704f69SBart Van Assche static int
do_rd_inc_wr_twice_v3(const char * dev_name,unsigned int lba,int block,int excl,int wait_ms,int id,unsigned int & ebusy,unsigned int & eagains)184*44704f69SBart Van Assche do_rd_inc_wr_twice_v3(const char * dev_name, unsigned int lba, int block,
185*44704f69SBart Van Assche                       int excl, int wait_ms, int id, unsigned int & ebusy,
186*44704f69SBart Van Assche                       unsigned int & eagains)
187*44704f69SBart Van Assche {
188*44704f69SBart Van Assche     bool odd = false;
189*44704f69SBart Van Assche     int k, sg_fd;
190*44704f69SBart Van Assche     struct sg_io_hdr pt, pt2;
191*44704f69SBart Van Assche     unsigned char r16CmdBlk [READ16_CMD_LEN] =
192*44704f69SBart Van Assche                 {0x88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
193*44704f69SBart Van Assche     unsigned char w16CmdBlk [WRITE16_CMD_LEN] =
194*44704f69SBart Van Assche                 {0x8a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
195*44704f69SBart Van Assche     unsigned char sense_buffer[64] SG_C_CPP_ZERO_INIT;
196*44704f69SBart Van Assche     unsigned char lb[READ16_REPLY_LEN];
197*44704f69SBart Van Assche     int open_flags = O_RDWR;
198*44704f69SBart Van Assche 
199*44704f69SBart Van Assche     sg_put_unaligned_be64(lba, r16CmdBlk + 2);
200*44704f69SBart Van Assche     sg_put_unaligned_be64(lba, w16CmdBlk + 2);
201*44704f69SBart Van Assche     if (! block)
202*44704f69SBart Van Assche         open_flags |= O_NONBLOCK;
203*44704f69SBart Van Assche     if (excl)
204*44704f69SBart Van Assche         open_flags |= O_EXCL;
205*44704f69SBart Van Assche 
206*44704f69SBart Van Assche     while (((sg_fd = open(dev_name, open_flags)) < 0) &&
207*44704f69SBart Van Assche            (EBUSY == errno)) {
208*44704f69SBart Van Assche         ++ebusy;
209*44704f69SBart Van Assche         if (wait_ms > 0)
210*44704f69SBart Van Assche             this_thread::sleep_for(milliseconds{wait_ms});
211*44704f69SBart Van Assche         else if (0 == wait_ms)
212*44704f69SBart Van Assche             this_thread::yield();
213*44704f69SBart Van Assche         else if (-2 == wait_ms)
214*44704f69SBart Van Assche             sleep(0);                   // process yield ??
215*44704f69SBart Van Assche     }
216*44704f69SBart Van Assche     if (sg_fd < 0) {
217*44704f69SBart Van Assche         char ebuff[EBUFF_SZ];
218*44704f69SBart Van Assche 
219*44704f69SBart Van Assche         snprintf(ebuff, EBUFF_SZ, "%s: error opening file: %s", __func__,
220*44704f69SBart Van Assche                  dev_name);
221*44704f69SBart Van Assche         perror(ebuff);
222*44704f69SBart Van Assche         return -1;
223*44704f69SBart Van Assche     }
224*44704f69SBart Van Assche 
225*44704f69SBart Van Assche     for (k = 0; k < 2; ++k) {
226*44704f69SBart Van Assche         bool ok = false;
227*44704f69SBart Van Assche         int res;
228*44704f69SBart Van Assche         unsigned int u = 0;
229*44704f69SBart Van Assche 
230*44704f69SBart Van Assche         /* Prepare READ_16 command */
231*44704f69SBart Van Assche         memset(&pt, 0, sizeof(pt));
232*44704f69SBart Van Assche         pt.interface_id = 'S';
233*44704f69SBart Van Assche         pt.cmd_len = sizeof(r16CmdBlk);
234*44704f69SBart Van Assche         pt.mx_sb_len = sizeof(sense_buffer);
235*44704f69SBart Van Assche         pt.dxfer_direction = SG_DXFER_FROM_DEV;
236*44704f69SBart Van Assche         pt.dxfer_len = READ16_REPLY_LEN;
237*44704f69SBart Van Assche         pt.dxferp = lb;
238*44704f69SBart Van Assche         pt.cmdp = r16CmdBlk;
239*44704f69SBart Van Assche         pt.sbp = sense_buffer;
240*44704f69SBart Van Assche         pt.timeout = 20000;     /* 20000 millisecs == 20 seconds */
241*44704f69SBart Van Assche         pt.pack_id = id;
242*44704f69SBart Van Assche 
243*44704f69SBart Van Assche         // queue up two READ_16s to same LBA
244*44704f69SBart Van Assche         if (write(sg_fd, &pt, sizeof(pt)) < 0) {
245*44704f69SBart Van Assche             {
246*44704f69SBart Van Assche                 lock_guard<mutex> lg(console_mutex);
247*44704f69SBart Van Assche 
248*44704f69SBart Van Assche                 perror(" write(sg, READ_16)");
249*44704f69SBart Van Assche             }
250*44704f69SBart Van Assche             close(sg_fd);
251*44704f69SBart Van Assche             return -1;
252*44704f69SBart Van Assche         }
253*44704f69SBart Van Assche         pt2 = pt;
254*44704f69SBart Van Assche         if (write(sg_fd, &pt2, sizeof(pt2)) < 0) {
255*44704f69SBart Van Assche             {
256*44704f69SBart Van Assche                 lock_guard<mutex> lg(console_mutex);
257*44704f69SBart Van Assche 
258*44704f69SBart Van Assche                 perror(" write(sg, READ_16) 2");
259*44704f69SBart Van Assche             }
260*44704f69SBart Van Assche             close(sg_fd);
261*44704f69SBart Van Assche             return -1;
262*44704f69SBart Van Assche         }
263*44704f69SBart Van Assche 
264*44704f69SBart Van Assche         while (((res = read(sg_fd, &pt, sizeof(pt))) < 0) &&
265*44704f69SBart Van Assche                (EAGAIN == errno)) {
266*44704f69SBart Van Assche             ++eagains;
267*44704f69SBart Van Assche             if (wait_ms > 0)
268*44704f69SBart Van Assche                 this_thread::sleep_for(milliseconds{wait_ms});
269*44704f69SBart Van Assche             else if (0 == wait_ms)
270*44704f69SBart Van Assche                 this_thread::yield();
271*44704f69SBart Van Assche             else if (-2 == wait_ms)
272*44704f69SBart Van Assche                 sleep(0);                   // process yield ??
273*44704f69SBart Van Assche         }
274*44704f69SBart Van Assche         if (res < 0) {
275*44704f69SBart Van Assche             {
276*44704f69SBart Van Assche                 lock_guard<mutex> lg(console_mutex);
277*44704f69SBart Van Assche 
278*44704f69SBart Van Assche                 perror(" read(sg, READ_16)");
279*44704f69SBart Van Assche             }
280*44704f69SBart Van Assche             close(sg_fd);
281*44704f69SBart Van Assche             return -1;
282*44704f69SBart Van Assche         }
283*44704f69SBart Van Assche         /* now for the error processing */
284*44704f69SBart Van Assche         switch (sg_err_category3(&pt)) {
285*44704f69SBart Van Assche         case SG_LIB_CAT_CLEAN:
286*44704f69SBart Van Assche             ok = true;
287*44704f69SBart Van Assche             break;
288*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
289*44704f69SBart Van Assche             {
290*44704f69SBart Van Assche                 lock_guard<mutex> lg(console_mutex);
291*44704f69SBart Van Assche 
292*44704f69SBart Van Assche                 fprintf(stderr, "Recovered error on READ_16, continuing\n");
293*44704f69SBart Van Assche             }
294*44704f69SBart Van Assche             ok = true;
295*44704f69SBart Van Assche             break;
296*44704f69SBart Van Assche         default: /* won't bother decoding other categories */
297*44704f69SBart Van Assche             {
298*44704f69SBart Van Assche                 lock_guard<mutex> lg(console_mutex);
299*44704f69SBart Van Assche 
300*44704f69SBart Van Assche                 sg_chk_n_print3("READ_16 command error", &pt, 1);
301*44704f69SBart Van Assche             }
302*44704f69SBart Van Assche             break;
303*44704f69SBart Van Assche         }
304*44704f69SBart Van Assche         if (ok) {
305*44704f69SBart Van Assche             while (((res = read(sg_fd, &pt2, sizeof(pt2))) < 0) &&
306*44704f69SBart Van Assche                    (EAGAIN == errno)) {
307*44704f69SBart Van Assche                 ++eagains;
308*44704f69SBart Van Assche                 if (wait_ms > 0)
309*44704f69SBart Van Assche                     this_thread::sleep_for(milliseconds{wait_ms});
310*44704f69SBart Van Assche                 else if (0 == wait_ms)
311*44704f69SBart Van Assche                     this_thread::yield();
312*44704f69SBart Van Assche                 else if (-2 == wait_ms)
313*44704f69SBart Van Assche                     sleep(0);                   // process yield ??
314*44704f69SBart Van Assche             }
315*44704f69SBart Van Assche             if (res < 0) {
316*44704f69SBart Van Assche                 {
317*44704f69SBart Van Assche                     lock_guard<mutex> lg(console_mutex);
318*44704f69SBart Van Assche 
319*44704f69SBart Van Assche                     perror(" read(sg, READ_16) 2");
320*44704f69SBart Van Assche                 }
321*44704f69SBart Van Assche                 close(sg_fd);
322*44704f69SBart Van Assche                 return -1;
323*44704f69SBart Van Assche             }
324*44704f69SBart Van Assche             pt = pt2;
325*44704f69SBart Van Assche             /* now for the error processing */
326*44704f69SBart Van Assche             ok = false;
327*44704f69SBart Van Assche             switch (sg_err_category3(&pt)) {
328*44704f69SBart Van Assche             case SG_LIB_CAT_CLEAN:
329*44704f69SBart Van Assche                 ok = true;
330*44704f69SBart Van Assche                 break;
331*44704f69SBart Van Assche             case SG_LIB_CAT_RECOVERED:
332*44704f69SBart Van Assche                 {
333*44704f69SBart Van Assche                     lock_guard<mutex> lg(console_mutex);
334*44704f69SBart Van Assche 
335*44704f69SBart Van Assche                     fprintf(stderr, "%s: Recovered error on READ_16, "
336*44704f69SBart Van Assche                             "continuing 2\n", __func__);
337*44704f69SBart Van Assche                 }
338*44704f69SBart Van Assche                 ok = true;
339*44704f69SBart Van Assche                 break;
340*44704f69SBart Van Assche             default: /* won't bother decoding other categories */
341*44704f69SBart Van Assche                 {
342*44704f69SBart Van Assche                     lock_guard<mutex> lg(console_mutex);
343*44704f69SBart Van Assche 
344*44704f69SBart Van Assche                     sg_chk_n_print3("READ_16 command error 2", &pt, 1);
345*44704f69SBart Van Assche                 }
346*44704f69SBart Van Assche                 break;
347*44704f69SBart Van Assche             }
348*44704f69SBart Van Assche         }
349*44704f69SBart Van Assche         if (! ok) {
350*44704f69SBart Van Assche             close(sg_fd);
351*44704f69SBart Van Assche             return -1;
352*44704f69SBart Van Assche         }
353*44704f69SBart Van Assche 
354*44704f69SBart Van Assche         u = sg_get_unaligned_be32(lb);
355*44704f69SBart Van Assche         // Assuming u starts test as even (probably 0), expect it to stay even
356*44704f69SBart Van Assche         if (0 == k)
357*44704f69SBart Van Assche             odd = (1 == (u % 2));
358*44704f69SBart Van Assche         ++u;
359*44704f69SBart Van Assche         sg_put_unaligned_be32(u, lb);
360*44704f69SBart Van Assche 
361*44704f69SBart Van Assche         if (wait_ms > 0)       /* allow daylight for bad things ... */
362*44704f69SBart Van Assche             this_thread::sleep_for(milliseconds{wait_ms});
363*44704f69SBart Van Assche         else if (0 == wait_ms)
364*44704f69SBart Van Assche             this_thread::yield();
365*44704f69SBart Van Assche         else if (-2 == wait_ms)
366*44704f69SBart Van Assche             sleep(0);                   // process yield ??
367*44704f69SBart Van Assche 
368*44704f69SBart Van Assche         /* Prepare WRITE_16 command */
369*44704f69SBart Van Assche         memset(&pt, 0, sizeof(pt));
370*44704f69SBart Van Assche         pt.interface_id = 'S';
371*44704f69SBart Van Assche         pt.cmd_len = sizeof(w16CmdBlk);
372*44704f69SBart Van Assche         pt.mx_sb_len = sizeof(sense_buffer);
373*44704f69SBart Van Assche         pt.dxfer_direction = SG_DXFER_TO_DEV;
374*44704f69SBart Van Assche         pt.dxfer_len = WRITE16_REPLY_LEN;
375*44704f69SBart Van Assche         pt.dxferp = lb;
376*44704f69SBart Van Assche         pt.cmdp = w16CmdBlk;
377*44704f69SBart Van Assche         pt.sbp = sense_buffer;
378*44704f69SBart Van Assche         pt.timeout = 20000;     /* 20000 millisecs == 20 seconds */
379*44704f69SBart Van Assche         pt.pack_id = id;
380*44704f69SBart Van Assche 
381*44704f69SBart Van Assche         if (ioctl(sg_fd, SG_IO, &pt) < 0) {
382*44704f69SBart Van Assche             {
383*44704f69SBart Van Assche                 lock_guard<mutex> lg(console_mutex);
384*44704f69SBart Van Assche 
385*44704f69SBart Van Assche                 perror(" WRITE_16 SG_IO ioctl error");
386*44704f69SBart Van Assche             }
387*44704f69SBart Van Assche             close(sg_fd);
388*44704f69SBart Van Assche             return -1;
389*44704f69SBart Van Assche         }
390*44704f69SBart Van Assche         /* now for the error processing */
391*44704f69SBart Van Assche         ok = false;
392*44704f69SBart Van Assche         switch (sg_err_category3(&pt)) {
393*44704f69SBart Van Assche         case SG_LIB_CAT_CLEAN:
394*44704f69SBart Van Assche             ok = true;
395*44704f69SBart Van Assche             break;
396*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
397*44704f69SBart Van Assche             {
398*44704f69SBart Van Assche                 lock_guard<mutex> lg(console_mutex);
399*44704f69SBart Van Assche 
400*44704f69SBart Van Assche                 fprintf(stderr, "%s: Recovered error on WRITE_16, "
401*44704f69SBart Van Assche                         "continuing\n", __func__);
402*44704f69SBart Van Assche             }
403*44704f69SBart Van Assche             ok = true;
404*44704f69SBart Van Assche             break;
405*44704f69SBart Van Assche         default: /* won't bother decoding other categories */
406*44704f69SBart Van Assche             {
407*44704f69SBart Van Assche                 lock_guard<mutex> lg(console_mutex);
408*44704f69SBart Van Assche 
409*44704f69SBart Van Assche                 sg_chk_n_print3("WRITE_16 command error", &pt, 1);
410*44704f69SBart Van Assche             }
411*44704f69SBart Van Assche             break;
412*44704f69SBart Van Assche         }
413*44704f69SBart Van Assche         if (! ok) {
414*44704f69SBart Van Assche             close(sg_fd);
415*44704f69SBart Van Assche             return -1;
416*44704f69SBart Van Assche         }
417*44704f69SBart Van Assche     }
418*44704f69SBart Van Assche     close(sg_fd);
419*44704f69SBart Van Assche     return (int)odd;
420*44704f69SBart Van Assche }
421*44704f69SBart Van Assche 
422*44704f69SBart Van Assche /* Opens dev_name and spins if busy (i.e. gets EBUSY), sleeping for
423*44704f69SBart Van Assche  * wait_ms milliseconds if wait_ms is positive.
424*44704f69SBart Van Assche  * Reads lba (twice) and treats the first 4 bytes as an int (SCSI endian),
425*44704f69SBart Van Assche  * increments it and writes it back. Repeats so that happens twice. Then
426*44704f69SBart Van Assche  * closes dev_name. If an error occurs returns -1 else returns 0 if
427*44704f69SBart Van Assche  * first int read from lba is even otherwise returns 1. */
428*44704f69SBart Van Assche static int
do_rd_inc_wr_twice_v4(const char * dev_name,unsigned int lba,int block,int excl,int wait_ms,int id,unsigned int & ebusy,unsigned int & eagains)429*44704f69SBart Van Assche do_rd_inc_wr_twice_v4(const char * dev_name, unsigned int lba, int block,
430*44704f69SBart Van Assche                       int excl, int wait_ms, int id, unsigned int & ebusy,
431*44704f69SBart Van Assche                       unsigned int & eagains)
432*44704f69SBart Van Assche {
433*44704f69SBart Van Assche     bool odd = false;
434*44704f69SBart Van Assche     int k, sg_fd;
435*44704f69SBart Van Assche     struct sg_io_v4 pt, pt2;
436*44704f69SBart Van Assche     unsigned char r16CmdBlk [READ16_CMD_LEN] =
437*44704f69SBart Van Assche                 {0x88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
438*44704f69SBart Van Assche     unsigned char w16CmdBlk [WRITE16_CMD_LEN] =
439*44704f69SBart Van Assche                 {0x8a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
440*44704f69SBart Van Assche     unsigned char sense_buffer[64] SG_C_CPP_ZERO_INIT;
441*44704f69SBart Van Assche     unsigned char lb[READ16_REPLY_LEN];
442*44704f69SBart Van Assche     int open_flags = O_RDWR;
443*44704f69SBart Van Assche 
444*44704f69SBart Van Assche     sg_put_unaligned_be64(lba, r16CmdBlk + 2);
445*44704f69SBart Van Assche     sg_put_unaligned_be64(lba, w16CmdBlk + 2);
446*44704f69SBart Van Assche     if (! block)
447*44704f69SBart Van Assche         open_flags |= O_NONBLOCK;
448*44704f69SBart Van Assche     if (excl)
449*44704f69SBart Van Assche         open_flags |= O_EXCL;
450*44704f69SBart Van Assche 
451*44704f69SBart Van Assche     while (((sg_fd = open(dev_name, open_flags)) < 0) &&
452*44704f69SBart Van Assche            (EBUSY == errno)) {
453*44704f69SBart Van Assche         ++ebusy;
454*44704f69SBart Van Assche         if (wait_ms > 0)
455*44704f69SBart Van Assche             this_thread::sleep_for(milliseconds{wait_ms});
456*44704f69SBart Van Assche         else if (0 == wait_ms)
457*44704f69SBart Van Assche             this_thread::yield();
458*44704f69SBart Van Assche         else if (-2 == wait_ms)
459*44704f69SBart Van Assche             sleep(0);                   // process yield ??
460*44704f69SBart Van Assche     }
461*44704f69SBart Van Assche     if (sg_fd < 0) {
462*44704f69SBart Van Assche         char ebuff[EBUFF_SZ];
463*44704f69SBart Van Assche 
464*44704f69SBart Van Assche         snprintf(ebuff, EBUFF_SZ, "%s: error opening file: %s", __func__,
465*44704f69SBart Van Assche                  dev_name);
466*44704f69SBart Van Assche         perror(ebuff);
467*44704f69SBart Van Assche         return -1;
468*44704f69SBart Van Assche     }
469*44704f69SBart Van Assche 
470*44704f69SBart Van Assche     for (k = 0; k < 2; ++k) {
471*44704f69SBart Van Assche         bool ok = false;
472*44704f69SBart Van Assche         int res;
473*44704f69SBart Van Assche         unsigned int u = 0;
474*44704f69SBart Van Assche 
475*44704f69SBart Van Assche         /* Prepare READ_16 command */
476*44704f69SBart Van Assche         memset(&pt, 0, sizeof(pt));
477*44704f69SBart Van Assche         pt.guard = 'Q';
478*44704f69SBart Van Assche         pt.request_len = sizeof(r16CmdBlk);
479*44704f69SBart Van Assche         pt.max_response_len = sizeof(sense_buffer);
480*44704f69SBart Van Assche         // pt.dxfer_direction = SG_DXFER_FROM_DEV;
481*44704f69SBart Van Assche         pt.din_xfer_len = READ16_REPLY_LEN;
482*44704f69SBart Van Assche         pt.din_xferp = (uint64_t)(sg_uintptr_t)lb;
483*44704f69SBart Van Assche         pt.request = (uint64_t)(sg_uintptr_t)r16CmdBlk;
484*44704f69SBart Van Assche         pt.response = (uint64_t)(sg_uintptr_t)sense_buffer;
485*44704f69SBart Van Assche         pt.timeout = 20000;     /* 20000 millisecs == 20 seconds */
486*44704f69SBart Van Assche         pt.request_extra = id;  /* pack_id field */
487*44704f69SBart Van Assche 
488*44704f69SBart Van Assche         // queue up two READ_16s to same LBA
489*44704f69SBart Van Assche         if (ioctl(sg_fd, SG_IOSUBMIT, &pt) < 0) {
490*44704f69SBart Van Assche             {
491*44704f69SBart Van Assche                 lock_guard<mutex> lg(console_mutex);
492*44704f69SBart Van Assche 
493*44704f69SBart Van Assche                 perror(" write(sg, READ_16)");
494*44704f69SBart Van Assche             }
495*44704f69SBart Van Assche             close(sg_fd);
496*44704f69SBart Van Assche             return -1;
497*44704f69SBart Van Assche         }
498*44704f69SBart Van Assche         pt2 = pt;
499*44704f69SBart Van Assche         if (ioctl(sg_fd, SG_IOSUBMIT, &pt2) < 0) {
500*44704f69SBart Van Assche             {
501*44704f69SBart Van Assche                 lock_guard<mutex> lg(console_mutex);
502*44704f69SBart Van Assche 
503*44704f69SBart Van Assche                 perror(" write(sg, READ_16) 2");
504*44704f69SBart Van Assche             }
505*44704f69SBart Van Assche             close(sg_fd);
506*44704f69SBart Van Assche             return -1;
507*44704f69SBart Van Assche         }
508*44704f69SBart Van Assche 
509*44704f69SBart Van Assche         while (((res = ioctl(sg_fd, SG_IORECEIVE, &pt)) < 0) &&
510*44704f69SBart Van Assche                (EAGAIN == errno)) {
511*44704f69SBart Van Assche             ++eagains;
512*44704f69SBart Van Assche             if (wait_ms > 0)
513*44704f69SBart Van Assche                 this_thread::sleep_for(milliseconds{wait_ms});
514*44704f69SBart Van Assche             else if (0 == wait_ms)
515*44704f69SBart Van Assche                 this_thread::yield();
516*44704f69SBart Van Assche             else if (-2 == wait_ms)
517*44704f69SBart Van Assche                 sleep(0);                   // process yield ??
518*44704f69SBart Van Assche         }
519*44704f69SBart Van Assche         if (res < 0) {
520*44704f69SBart Van Assche             {
521*44704f69SBart Van Assche                 lock_guard<mutex> lg(console_mutex);
522*44704f69SBart Van Assche 
523*44704f69SBart Van Assche                 perror(" read(sg, READ_16)");
524*44704f69SBart Van Assche             }
525*44704f69SBart Van Assche             close(sg_fd);
526*44704f69SBart Van Assche             return -1;
527*44704f69SBart Van Assche         }
528*44704f69SBart Van Assche         /* now for the error processing */
529*44704f69SBart Van Assche         switch (sg_err_category_new(pt.device_status, pt.transport_status,
530*44704f69SBart Van Assche                 pt.driver_status, sense_buffer, pt.response_len)) {
531*44704f69SBart Van Assche         case SG_LIB_CAT_CLEAN:
532*44704f69SBart Van Assche             ok = true;
533*44704f69SBart Van Assche             break;
534*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
535*44704f69SBart Van Assche             {
536*44704f69SBart Van Assche                 lock_guard<mutex> lg(console_mutex);
537*44704f69SBart Van Assche 
538*44704f69SBart Van Assche                 fprintf(stderr, "Recovered error on READ_16, continuing\n");
539*44704f69SBart Van Assche             }
540*44704f69SBart Van Assche             ok = true;
541*44704f69SBart Van Assche             break;
542*44704f69SBart Van Assche         default: /* won't bother decoding other categories */
543*44704f69SBart Van Assche             {
544*44704f69SBart Van Assche                 lock_guard<mutex> lg(console_mutex);
545*44704f69SBart Van Assche 
546*44704f69SBart Van Assche                 sg_linux_sense_print("READ_16 command error",
547*44704f69SBart Van Assche                                      pt.device_status, pt.transport_status,
548*44704f69SBart Van Assche                                      pt.driver_status, sense_buffer,
549*44704f69SBart Van Assche                                      pt.response_len, true);
550*44704f69SBart Van Assche                 // sg_chk_n_print3("READ_16 command error", &pt, 1);
551*44704f69SBart Van Assche             }
552*44704f69SBart Van Assche             break;
553*44704f69SBart Van Assche         }
554*44704f69SBart Van Assche         if (ok) {
555*44704f69SBart Van Assche             while (((res = ioctl(sg_fd, SG_IORECEIVE, &pt2)) < 0) &&
556*44704f69SBart Van Assche                    (EAGAIN == errno)) {
557*44704f69SBart Van Assche                 ++eagains;
558*44704f69SBart Van Assche                 if (wait_ms > 0)
559*44704f69SBart Van Assche                     this_thread::sleep_for(milliseconds{wait_ms});
560*44704f69SBart Van Assche                 else if (0 == wait_ms)
561*44704f69SBart Van Assche                     this_thread::yield();
562*44704f69SBart Van Assche                 else if (-2 == wait_ms)
563*44704f69SBart Van Assche                     sleep(0);                   // process yield ??
564*44704f69SBart Van Assche             }
565*44704f69SBart Van Assche             if (res < 0) {
566*44704f69SBart Van Assche                 {
567*44704f69SBart Van Assche                     lock_guard<mutex> lg(console_mutex);
568*44704f69SBart Van Assche 
569*44704f69SBart Van Assche                     perror(" read(sg, READ_16) 2");
570*44704f69SBart Van Assche                 }
571*44704f69SBart Van Assche                 close(sg_fd);
572*44704f69SBart Van Assche                 return -1;
573*44704f69SBart Van Assche             }
574*44704f69SBart Van Assche             pt = pt2;
575*44704f69SBart Van Assche             /* now for the error processing */
576*44704f69SBart Van Assche             ok = false;
577*44704f69SBart Van Assche             switch (sg_err_category_new(pt.device_status, pt.transport_status,
578*44704f69SBart Van Assche                     pt.driver_status, sense_buffer, pt.response_len)) {
579*44704f69SBart Van Assche             case SG_LIB_CAT_CLEAN:
580*44704f69SBart Van Assche                 ok = true;
581*44704f69SBart Van Assche                 break;
582*44704f69SBart Van Assche             case SG_LIB_CAT_RECOVERED:
583*44704f69SBart Van Assche                 {
584*44704f69SBart Van Assche                     lock_guard<mutex> lg(console_mutex);
585*44704f69SBart Van Assche 
586*44704f69SBart Van Assche                     fprintf(stderr, "%s: Recovered error on READ_16, "
587*44704f69SBart Van Assche                             "continuing 2\n", __func__);
588*44704f69SBart Van Assche                 }
589*44704f69SBart Van Assche                 ok = true;
590*44704f69SBart Van Assche                 break;
591*44704f69SBart Van Assche             default: /* won't bother decoding other categories */
592*44704f69SBart Van Assche                 {
593*44704f69SBart Van Assche                     lock_guard<mutex> lg(console_mutex);
594*44704f69SBart Van Assche 
595*44704f69SBart Van Assche                     sg_linux_sense_print("READ_16 command error 2",
596*44704f69SBart Van Assche                                          pt.device_status,
597*44704f69SBart Van Assche                                          pt.transport_status,
598*44704f69SBart Van Assche                                          pt.driver_status, sense_buffer,
599*44704f69SBart Van Assche                                          pt.response_len, true);
600*44704f69SBart Van Assche                     // sg_chk_n_print3("READ_16 command error 2", &pt, 1);
601*44704f69SBart Van Assche                 }
602*44704f69SBart Van Assche                 break;
603*44704f69SBart Van Assche             }
604*44704f69SBart Van Assche         }
605*44704f69SBart Van Assche         if (! ok) {
606*44704f69SBart Van Assche             close(sg_fd);
607*44704f69SBart Van Assche             return -1;
608*44704f69SBart Van Assche         }
609*44704f69SBart Van Assche 
610*44704f69SBart Van Assche         u = sg_get_unaligned_be32(lb);
611*44704f69SBart Van Assche         // Assuming u starts test as even (probably 0), expect it to stay even
612*44704f69SBart Van Assche         if (0 == k)
613*44704f69SBart Van Assche             odd = (1 == (u % 2));
614*44704f69SBart Van Assche         ++u;
615*44704f69SBart Van Assche         sg_put_unaligned_be32(u, lb);
616*44704f69SBart Van Assche 
617*44704f69SBart Van Assche         if (wait_ms > 0)       /* allow daylight for bad things ... */
618*44704f69SBart Van Assche             this_thread::sleep_for(milliseconds{wait_ms});
619*44704f69SBart Van Assche         else if (0 == wait_ms)
620*44704f69SBart Van Assche             this_thread::yield();
621*44704f69SBart Van Assche         else if (-2 == wait_ms)
622*44704f69SBart Van Assche             sleep(0);                   // process yield ??
623*44704f69SBart Van Assche 
624*44704f69SBart Van Assche         /* Prepare WRITE_16 command */
625*44704f69SBart Van Assche         memset(&pt, 0, sizeof(pt));
626*44704f69SBart Van Assche         pt.guard = 'Q';
627*44704f69SBart Van Assche         pt.request_len = sizeof(w16CmdBlk);
628*44704f69SBart Van Assche         pt.max_response_len = sizeof(sense_buffer);
629*44704f69SBart Van Assche         // pt.dxfer_direction = SG_DXFER_TO_DEV;
630*44704f69SBart Van Assche         pt.dout_xfer_len = WRITE16_REPLY_LEN;
631*44704f69SBart Van Assche         pt.dout_xferp = (uint64_t)(sg_uintptr_t)lb;
632*44704f69SBart Van Assche         pt.request = (uint64_t)(sg_uintptr_t)w16CmdBlk;
633*44704f69SBart Van Assche         pt.response = (uint64_t)(sg_uintptr_t)sense_buffer;
634*44704f69SBart Van Assche         pt.timeout = 20000;     /* 20000 millisecs == 20 seconds */
635*44704f69SBart Van Assche         pt.request_extra = id;  /* pack_id field */
636*44704f69SBart Van Assche 
637*44704f69SBart Van Assche         if (ioctl(sg_fd, SG_IO, &pt) < 0) {
638*44704f69SBart Van Assche             {
639*44704f69SBart Van Assche                 lock_guard<mutex> lg(console_mutex);
640*44704f69SBart Van Assche 
641*44704f69SBart Van Assche                 perror(" WRITE_16 SG_IO ioctl error");
642*44704f69SBart Van Assche             }
643*44704f69SBart Van Assche             close(sg_fd);
644*44704f69SBart Van Assche             return -1;
645*44704f69SBart Van Assche         }
646*44704f69SBart Van Assche         /* now for the error processing */
647*44704f69SBart Van Assche         ok = false;
648*44704f69SBart Van Assche         switch (sg_err_category_new(pt.device_status, pt.transport_status,
649*44704f69SBart Van Assche                 pt.driver_status, sense_buffer, pt.response_len)) {
650*44704f69SBart Van Assche         case SG_LIB_CAT_CLEAN:
651*44704f69SBart Van Assche             ok = true;
652*44704f69SBart Van Assche             break;
653*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
654*44704f69SBart Van Assche             {
655*44704f69SBart Van Assche                 lock_guard<mutex> lg(console_mutex);
656*44704f69SBart Van Assche 
657*44704f69SBart Van Assche                 fprintf(stderr, "%s: Recovered error on WRITE_16, "
658*44704f69SBart Van Assche                         "continuing\n", __func__);
659*44704f69SBart Van Assche             }
660*44704f69SBart Van Assche             ok = true;
661*44704f69SBart Van Assche             break;
662*44704f69SBart Van Assche         default: /* won't bother decoding other categories */
663*44704f69SBart Van Assche             {
664*44704f69SBart Van Assche                 lock_guard<mutex> lg(console_mutex);
665*44704f69SBart Van Assche 
666*44704f69SBart Van Assche                 sg_linux_sense_print("WRITE_16 command error",
667*44704f69SBart Van Assche                                      pt.device_status, pt.transport_status,
668*44704f69SBart Van Assche                                      pt.driver_status, sense_buffer,
669*44704f69SBart Van Assche                                      pt.response_len, true);
670*44704f69SBart Van Assche             }
671*44704f69SBart Van Assche             break;
672*44704f69SBart Van Assche         }
673*44704f69SBart Van Assche         if (! ok) {
674*44704f69SBart Van Assche             close(sg_fd);
675*44704f69SBart Van Assche             return -1;
676*44704f69SBart Van Assche         }
677*44704f69SBart Van Assche     }
678*44704f69SBart Van Assche     close(sg_fd);
679*44704f69SBart Van Assche     return odd;
680*44704f69SBart Van Assche }
681*44704f69SBart Van Assche 
682*44704f69SBart Van Assche 
683*44704f69SBart Van Assche 
684*44704f69SBart Van Assche #define INQ_REPLY_LEN 96
685*44704f69SBart Van Assche #define INQ_CMD_LEN 6
686*44704f69SBart Van Assche 
687*44704f69SBart Van Assche /* Send INQUIRY and fetches response. If okay puts PRODUCT ID field
688*44704f69SBart Van Assche  * in b (up to m_blen bytes). Does not use O_EXCL flag. Returns 0 on success,
689*44704f69SBart Van Assche  * else -1 . */
690*44704f69SBart Van Assche static int
do_inquiry_prod_id(const char * dev_name,int block,int wait_ms,unsigned int & ebusys,char * b,int b_mlen)691*44704f69SBart Van Assche do_inquiry_prod_id(const char * dev_name, int block, int wait_ms,
692*44704f69SBart Van Assche                    unsigned int & ebusys, char * b, int b_mlen)
693*44704f69SBart Van Assche {
694*44704f69SBart Van Assche     int sg_fd, ok, ret;
695*44704f69SBart Van Assche     struct sg_io_hdr pt;
696*44704f69SBart Van Assche     unsigned char inqCmdBlk [INQ_CMD_LEN] =
697*44704f69SBart Van Assche                                 {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
698*44704f69SBart Van Assche     unsigned char inqBuff[INQ_REPLY_LEN];
699*44704f69SBart Van Assche     unsigned char sense_buffer[64] SG_C_CPP_ZERO_INIT;
700*44704f69SBart Van Assche     int open_flags = O_RDWR;    /* O_EXCL | O_RDONLY fails with EPERM */
701*44704f69SBart Van Assche 
702*44704f69SBart Van Assche     if (! block)
703*44704f69SBart Van Assche         open_flags |= O_NONBLOCK;
704*44704f69SBart Van Assche     while (((sg_fd = open(dev_name, open_flags)) < 0) &&
705*44704f69SBart Van Assche            (EBUSY == errno)) {
706*44704f69SBart Van Assche         ++ebusys;
707*44704f69SBart Van Assche         if (wait_ms > 0)
708*44704f69SBart Van Assche             this_thread::sleep_for(milliseconds{wait_ms});
709*44704f69SBart Van Assche         else if (0 == wait_ms)
710*44704f69SBart Van Assche             this_thread::yield();
711*44704f69SBart Van Assche         else if (-2 == wait_ms)
712*44704f69SBart Van Assche             sleep(0);                   // process yield ??
713*44704f69SBart Van Assche     }
714*44704f69SBart Van Assche     if (sg_fd < 0) {
715*44704f69SBart Van Assche         char ebuff[EBUFF_SZ];
716*44704f69SBart Van Assche 
717*44704f69SBart Van Assche         snprintf(ebuff, EBUFF_SZ,
718*44704f69SBart Van Assche                  "do_inquiry_prod_id: error opening file: %s", dev_name);
719*44704f69SBart Van Assche         perror(ebuff);
720*44704f69SBart Van Assche         return -1;
721*44704f69SBart Van Assche     }
722*44704f69SBart Van Assche     /* Prepare INQUIRY command */
723*44704f69SBart Van Assche     memset(&pt, 0, sizeof(pt));
724*44704f69SBart Van Assche     pt.interface_id = 'S';
725*44704f69SBart Van Assche     pt.cmd_len = sizeof(inqCmdBlk);
726*44704f69SBart Van Assche     /* pt.iovec_count = 0; */  /* memset takes care of this */
727*44704f69SBart Van Assche     pt.mx_sb_len = sizeof(sense_buffer);
728*44704f69SBart Van Assche     pt.dxfer_direction = SG_DXFER_FROM_DEV;
729*44704f69SBart Van Assche     pt.dxfer_len = INQ_REPLY_LEN;
730*44704f69SBart Van Assche     pt.dxferp = inqBuff;
731*44704f69SBart Van Assche     pt.cmdp = inqCmdBlk;
732*44704f69SBart Van Assche     pt.sbp = sense_buffer;
733*44704f69SBart Van Assche     pt.timeout = 20000;     /* 20000 millisecs == 20 seconds */
734*44704f69SBart Van Assche     /* pt.flags = 0; */     /* take defaults: indirect IO, etc */
735*44704f69SBart Van Assche     /* pt.pack_id = 0; */
736*44704f69SBart Van Assche     /* pt.usr_ptr = NULL; */
737*44704f69SBart Van Assche 
738*44704f69SBart Van Assche     if (ioctl(sg_fd, SG_IO, &pt) < 0) {
739*44704f69SBart Van Assche         perror("do_inquiry_prod_id: Inquiry SG_IO ioctl error");
740*44704f69SBart Van Assche         close(sg_fd);
741*44704f69SBart Van Assche         return -1;
742*44704f69SBart Van Assche     }
743*44704f69SBart Van Assche 
744*44704f69SBart Van Assche     /* now for the error processing */
745*44704f69SBart Van Assche     ok = 0;
746*44704f69SBart Van Assche     switch (sg_err_category3(&pt)) {
747*44704f69SBart Van Assche     case SG_LIB_CAT_CLEAN:
748*44704f69SBart Van Assche         ok = 1;
749*44704f69SBart Van Assche         break;
750*44704f69SBart Van Assche     case SG_LIB_CAT_RECOVERED:
751*44704f69SBart Van Assche         fprintf(stderr, "Recovered error on INQUIRY, continuing\n");
752*44704f69SBart Van Assche         ok = 1;
753*44704f69SBart Van Assche         break;
754*44704f69SBart Van Assche     default: /* won't bother decoding other categories */
755*44704f69SBart Van Assche         sg_chk_n_print3("INQUIRY command error", &pt, 1);
756*44704f69SBart Van Assche         break;
757*44704f69SBart Van Assche     }
758*44704f69SBart Van Assche     if (ok) {
759*44704f69SBart Van Assche         /* Good, so fetch Product ID from response, copy to 'b' */
760*44704f69SBart Van Assche         if (b_mlen > 0) {
761*44704f69SBart Van Assche             if (b_mlen > 16) {
762*44704f69SBart Van Assche                 memcpy(b, inqBuff + 16, 16);
763*44704f69SBart Van Assche                 b[16] = '\0';
764*44704f69SBart Van Assche             } else {
765*44704f69SBart Van Assche                 memcpy(b, inqBuff + 16, b_mlen - 1);
766*44704f69SBart Van Assche                 b[b_mlen - 1] = '\0';
767*44704f69SBart Van Assche             }
768*44704f69SBart Van Assche         }
769*44704f69SBart Van Assche         ret = 0;
770*44704f69SBart Van Assche     } else
771*44704f69SBart Van Assche         ret = -1;
772*44704f69SBart Van Assche     close(sg_fd);
773*44704f69SBart Van Assche     return ret;
774*44704f69SBart Van Assche }
775*44704f69SBart Van Assche 
776*44704f69SBart Van Assche static void
work_thread(const char * dev_name,unsigned int lba,int id,int block,int excl,int num,int wait_ms)777*44704f69SBart Van Assche work_thread(const char * dev_name, unsigned int lba, int id, int block,
778*44704f69SBart Van Assche             int excl, int num, int wait_ms)
779*44704f69SBart Van Assche {
780*44704f69SBart Van Assche     unsigned int thr_odd_count = 0;
781*44704f69SBart Van Assche     unsigned int thr_ebusy_count = 0;
782*44704f69SBart Van Assche     unsigned int thr_eagain_count = 0;
783*44704f69SBart Van Assche     int k, res;
784*44704f69SBart Van Assche 
785*44704f69SBart Van Assche     {
786*44704f69SBart Van Assche         lock_guard<mutex> lg(console_mutex);
787*44704f69SBart Van Assche 
788*44704f69SBart Van Assche         cerr << "Enter work_thread id=" << id << " excl=" << excl << " block="
789*44704f69SBart Van Assche              << block << endl;
790*44704f69SBart Van Assche     }
791*44704f69SBart Van Assche     for (k = 0; k < num; ++k) {
792*44704f69SBart Van Assche         if (sg_ifc_ver == 3)
793*44704f69SBart Van Assche             res = do_rd_inc_wr_twice_v3(dev_name, lba, block, excl, wait_ms,
794*44704f69SBart Van Assche                                         k, thr_ebusy_count, thr_eagain_count);
795*44704f69SBart Van Assche         else if (sg_ifc_ver == 4)
796*44704f69SBart Van Assche             res = do_rd_inc_wr_twice_v4(dev_name, lba, block, excl, wait_ms,
797*44704f69SBart Van Assche                                         k, thr_ebusy_count, thr_eagain_count);
798*44704f69SBart Van Assche         else {
799*44704f69SBart Van Assche             lock_guard<mutex> lg(console_mutex);
800*44704f69SBart Van Assche 
801*44704f69SBart Van Assche             cerr << "sg_ifc_ver=" << sg_ifc_ver << " not supported" << endl;
802*44704f69SBart Van Assche             res = -1;
803*44704f69SBart Van Assche         }
804*44704f69SBart Van Assche         if (res < 0)
805*44704f69SBart Van Assche             break;
806*44704f69SBart Van Assche         if (res)
807*44704f69SBart Van Assche             ++thr_odd_count;
808*44704f69SBart Van Assche     }
809*44704f69SBart Van Assche     {
810*44704f69SBart Van Assche         lock_guard<mutex> lg(console_mutex);
811*44704f69SBart Van Assche 
812*44704f69SBart Van Assche         if (k < num)
813*44704f69SBart Van Assche             cerr << "thread id=" << id << " FAILed at iteration: " << k <<
814*44704f69SBart Van Assche                     '\n';
815*44704f69SBart Van Assche         else
816*44704f69SBart Van Assche             cerr << "thread id=" << id << " normal exit" << '\n';
817*44704f69SBart Van Assche     }
818*44704f69SBart Van Assche     {
819*44704f69SBart Van Assche         lock_guard<mutex> lg(odd_count_mutex);
820*44704f69SBart Van Assche 
821*44704f69SBart Van Assche         odd_count += thr_odd_count;
822*44704f69SBart Van Assche         ebusy_count += thr_ebusy_count;
823*44704f69SBart Van Assche         eagain_count += thr_eagain_count;
824*44704f69SBart Van Assche     }
825*44704f69SBart Van Assche }
826*44704f69SBart Van Assche 
827*44704f69SBart Van Assche 
828*44704f69SBart Van Assche int
main(int argc,char * argv[])829*44704f69SBart Van Assche main(int argc, char * argv[])
830*44704f69SBart Van Assche {
831*44704f69SBart Van Assche     int k;
832*44704f69SBart Van Assche     int block = 0;
833*44704f69SBart Van Assche     int force = 0;
834*44704f69SBart Van Assche     unsigned int lba = DEF_LBA;
835*44704f69SBart Van Assche     int num_per_thread = DEF_NUM_PER_THREAD;
836*44704f69SBart Van Assche     int num_threads = DEF_NUM_THREADS;
837*44704f69SBart Van Assche     int wait_ms = DEF_WAIT_MS;
838*44704f69SBart Van Assche     int no_o_excl = 0;
839*44704f69SBart Van Assche     char * dev_name = NULL;
840*44704f69SBart Van Assche 
841*44704f69SBart Van Assche     for (k = 1; k < argc; ++k) {
842*44704f69SBart Van Assche         if (0 == memcmp("-b", argv[k], 2))
843*44704f69SBart Van Assche             ++block;
844*44704f69SBart Van Assche         else if (0 == memcmp("-f", argv[k], 2))
845*44704f69SBart Van Assche             ++force;
846*44704f69SBart Van Assche         else if (0 == memcmp("-h", argv[k], 2)) {
847*44704f69SBart Van Assche             usage();
848*44704f69SBart Van Assche             return 0;
849*44704f69SBart Van Assche         } else if (0 == memcmp("-i", argv[k], 2)) {
850*44704f69SBart Van Assche             ++k;
851*44704f69SBart Van Assche             if ((k < argc) && isdigit(*argv[k]))
852*44704f69SBart Van Assche                 sg_ifc_ver = atoi(argv[k]);
853*44704f69SBart Van Assche             else
854*44704f69SBart Van Assche                 break;
855*44704f69SBart Van Assche         } else if (0 == memcmp("-l", argv[k], 2)) {
856*44704f69SBart Van Assche             ++k;
857*44704f69SBart Van Assche             if ((k < argc) && isdigit(*argv[k]))
858*44704f69SBart Van Assche                 lba = (unsigned int)atoi(argv[k]);
859*44704f69SBart Van Assche             else
860*44704f69SBart Van Assche                 break;
861*44704f69SBart Van Assche         } else if (0 == memcmp("-n", argv[k], 2)) {
862*44704f69SBart Van Assche             ++k;
863*44704f69SBart Van Assche             if ((k < argc) && isdigit(*argv[k]))
864*44704f69SBart Van Assche                 num_per_thread = atoi(argv[k]);
865*44704f69SBart Van Assche             else
866*44704f69SBart Van Assche                 break;
867*44704f69SBart Van Assche         } else if (0 == memcmp("-t", argv[k], 2)) {
868*44704f69SBart Van Assche             ++k;
869*44704f69SBart Van Assche             if ((k < argc) && isdigit(*argv[k]))
870*44704f69SBart Van Assche                 num_threads = atoi(argv[k]);
871*44704f69SBart Van Assche             else
872*44704f69SBart Van Assche                 break;
873*44704f69SBart Van Assche         } else if (0 == memcmp("-V", argv[k], 2)) {
874*44704f69SBart Van Assche             printf("%s version: %s\n", util_name, version_str);
875*44704f69SBart Van Assche             return 0;
876*44704f69SBart Van Assche         } else if (0 == memcmp("-w", argv[k], 2)) {
877*44704f69SBart Van Assche             ++k;
878*44704f69SBart Van Assche             if ((k < argc) && (isdigit(*argv[k]) || ('-' == *argv[k]))) {
879*44704f69SBart Van Assche                 if ('-' == *argv[k])
880*44704f69SBart Van Assche                     wait_ms = - atoi(argv[k] + 1);
881*44704f69SBart Van Assche                 else
882*44704f69SBart Van Assche                     wait_ms = atoi(argv[k]);
883*44704f69SBart Van Assche             } else
884*44704f69SBart Van Assche                 break;
885*44704f69SBart Van Assche         } else if (0 == memcmp("-xxx", argv[k], 4))
886*44704f69SBart Van Assche             no_o_excl += 3;
887*44704f69SBart Van Assche         else if (0 == memcmp("-xx", argv[k], 3))
888*44704f69SBart Van Assche             no_o_excl += 2;
889*44704f69SBart Van Assche         else if (0 == memcmp("-x", argv[k], 2))
890*44704f69SBart Van Assche             ++no_o_excl;
891*44704f69SBart Van Assche         else if (*argv[k] == '-') {
892*44704f69SBart Van Assche             printf("Unrecognized switch: %s\n", argv[k]);
893*44704f69SBart Van Assche             dev_name = NULL;
894*44704f69SBart Van Assche             break;
895*44704f69SBart Van Assche         }
896*44704f69SBart Van Assche         else if (! dev_name)
897*44704f69SBart Van Assche             dev_name = argv[k];
898*44704f69SBart Van Assche         else {
899*44704f69SBart Van Assche             printf("too many arguments\n");
900*44704f69SBart Van Assche             dev_name = 0;
901*44704f69SBart Van Assche             break;
902*44704f69SBart Van Assche         }
903*44704f69SBart Van Assche     }
904*44704f69SBart Van Assche     if (0 == dev_name) {
905*44704f69SBart Van Assche         usage();
906*44704f69SBart Van Assche         return 1;
907*44704f69SBart Van Assche     }
908*44704f69SBart Van Assche     try {
909*44704f69SBart Van Assche         struct stat a_stat;
910*44704f69SBart Van Assche 
911*44704f69SBart Van Assche         if (stat(dev_name, &a_stat) < 0) {
912*44704f69SBart Van Assche             perror("stat() on dev_name failed");
913*44704f69SBart Van Assche             return 1;
914*44704f69SBart Van Assche         }
915*44704f69SBart Van Assche         if (! S_ISCHR(a_stat.st_mode)) {
916*44704f69SBart Van Assche             fprintf(stderr, "%s should be a sg device which is a char "
917*44704f69SBart Van Assche                     "device. %s\n", dev_name, dev_name);
918*44704f69SBart Van Assche             fprintf(stderr, "is not a char device and damage could be done "
919*44704f69SBart Van Assche                     "if it is a BLOCK\ndevice, exiting ...\n");
920*44704f69SBart Van Assche             return 1;
921*44704f69SBart Van Assche         }
922*44704f69SBart Van Assche         if (! force) {
923*44704f69SBart Van Assche             char b[64];
924*44704f69SBart Van Assche             int res = do_inquiry_prod_id(dev_name, block, wait_ms,
925*44704f69SBart Van Assche                                          ebusy_count, b, sizeof(b));
926*44704f69SBart Van Assche 
927*44704f69SBart Van Assche             if (res) {
928*44704f69SBart Van Assche                 fprintf(stderr, "INQUIRY failed on %s\n", dev_name);
929*44704f69SBart Van Assche                 return 1;
930*44704f69SBart Van Assche             }
931*44704f69SBart Van Assche             // For safety, since <lba> written to, only permit scsi_debug
932*44704f69SBart Van Assche             // devices. Bypass this with '-f' option.
933*44704f69SBart Van Assche             if (0 != memcmp("scsi_debug", b, 10)) {
934*44704f69SBart Van Assche                 fprintf(stderr, "Since this utility writes to LBA %u, only "
935*44704f69SBart Van Assche                         "devices with scsi_debug\nproduct ID accepted.\n",
936*44704f69SBart Van Assche                         lba);
937*44704f69SBart Van Assche                 return 2;
938*44704f69SBart Van Assche             }
939*44704f69SBart Van Assche         }
940*44704f69SBart Van Assche 
941*44704f69SBart Van Assche         vector<thread *> vt;
942*44704f69SBart Van Assche 
943*44704f69SBart Van Assche         for (k = 0; k < num_threads; ++k) {
944*44704f69SBart Van Assche             int excl = 1;
945*44704f69SBart Van Assche 
946*44704f69SBart Van Assche             if (no_o_excl > 1)
947*44704f69SBart Van Assche                 excl = 0;
948*44704f69SBart Van Assche             else if ((0 == k) && (1 == no_o_excl))
949*44704f69SBart Van Assche                 excl = 0;
950*44704f69SBart Van Assche 
951*44704f69SBart Van Assche             thread * tp = new thread {work_thread, dev_name, lba, k, block,
952*44704f69SBart Van Assche                                       excl, num_per_thread, wait_ms};
953*44704f69SBart Van Assche             vt.push_back(tp);
954*44704f69SBart Van Assche         }
955*44704f69SBart Van Assche 
956*44704f69SBart Van Assche         // g++ 4.7.3 didn't like range-for loop here
957*44704f69SBart Van Assche         for (k = 0; k < (int)vt.size(); ++k)
958*44704f69SBart Van Assche             vt[k]->join();
959*44704f69SBart Van Assche 
960*44704f69SBart Van Assche         for (k = 0; k < (int)vt.size(); ++k)
961*44704f69SBart Van Assche             delete vt[k];
962*44704f69SBart Van Assche 
963*44704f69SBart Van Assche         if (no_o_excl)
964*44704f69SBart Van Assche             cout << "Odd count: " << odd_count << endl;
965*44704f69SBart Van Assche         else
966*44704f69SBart Van Assche             cout << "Expecting odd count of 0, got " << odd_count << endl;
967*44704f69SBart Van Assche         cout << "Number of EBUSYs: " << ebusy_count << endl;
968*44704f69SBart Van Assche         cout << "Number of EAGAINs: " << eagain_count << endl;
969*44704f69SBart Van Assche 
970*44704f69SBart Van Assche     }
971*44704f69SBart Van Assche     catch(system_error& e)  {
972*44704f69SBart Van Assche         cerr << "got a system_error exception: " << e.what() << '\n';
973*44704f69SBart Van Assche         auto ec = e.code();
974*44704f69SBart Van Assche         cerr << "category: " << ec.category().name() << '\n';
975*44704f69SBart Van Assche         cerr << "value: " << ec.value() << '\n';
976*44704f69SBart Van Assche         cerr << "message: " << ec.message() << '\n';
977*44704f69SBart Van Assche         cerr << "\nNote: if g++ may need '-pthread' or similar in "
978*44704f69SBart Van Assche                 "compile/link line" << '\n';
979*44704f69SBart Van Assche     }
980*44704f69SBart Van Assche     catch(...) {
981*44704f69SBart Van Assche         cerr << "got another exception: " << '\n';
982*44704f69SBart Van Assche     }
983*44704f69SBart Van Assche     return 0;
984*44704f69SBart Van Assche }
985