xref: /aosp_15_r20/external/sg3_utils/testing/sg_tst_context.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 #include "sg_lib.h"
47*44704f69SBart Van Assche #include "sg_pt.h"
48*44704f69SBart Van Assche 
49*44704f69SBart Van Assche static const char * version_str = "1.06 20220425";
50*44704f69SBart Van Assche static const char * util_name = "sg_tst_context";
51*44704f69SBart Van Assche 
52*44704f69SBart Van Assche /* This is a test program for checking that file handles keep their
53*44704f69SBart Van Assche  * context properly when sent (synchronous) SCSI pass-through commands.
54*44704f69SBart Van Assche  * A disk device is assumed and even-numbered threads send TEST UNIT
55*44704f69SBart Van Assche  * READY commands while odd-numbered threads send alternating START STOP
56*44704f69SBart Van Assche  * UNIT commands (i.e. start then stop then start, etc). The point is to
57*44704f69SBart Van Assche  * check the results to make sure that they don't get the other command's
58*44704f69SBart Van Assche  * response. For example a START STOP UNIT command should not see a "not
59*44704f69SBart Van Assche  * ready" sense key.
60*44704f69SBart Van Assche  *
61*44704f69SBart Van Assche  * This is C++ code with some things from C++11 (e.g. threads) and was
62*44704f69SBart Van Assche  * only just able to compile (when some things were reverted) with gcc/g++
63*44704f69SBart Van Assche  * version 4.7.3 found in Ubuntu 13.04 . C++11 "feature complete" support
64*44704f69SBart Van Assche  * was not available until g++ version 4.8.1 and that is found in Fedora
65*44704f69SBart Van Assche  * 19 and Ubuntu 13.10 .
66*44704f69SBart Van Assche  *
67*44704f69SBart Van Assche  * The build uses various object files from the <sg3_utils>/lib directory
68*44704f69SBart Van Assche  * which is assumed to be a sibling of this examples directory. Those
69*44704f69SBart Van Assche  * object files in the lib directory can be built with:
70*44704f69SBart Van Assche  *   cd <sg3_utils> ; ./configure ; cd lib; make
71*44704f69SBart Van Assche  * Then:
72*44704f69SBart Van Assche  *   cd ../testing
73*44704f69SBart Van Assche  *   make sg_tst_context
74*44704f69SBart Van Assche  *
75*44704f69SBart Van Assche  */
76*44704f69SBart Van Assche 
77*44704f69SBart Van Assche using namespace std;
78*44704f69SBart Van Assche using namespace std::chrono;
79*44704f69SBart Van Assche 
80*44704f69SBart Van Assche #define DEF_NUM_PER_THREAD 200
81*44704f69SBart Van Assche #define DEF_NUM_THREADS 2
82*44704f69SBart Van Assche 
83*44704f69SBart Van Assche #define EBUFF_SZ 256
84*44704f69SBart Van Assche 
85*44704f69SBart Van Assche 
86*44704f69SBart Van Assche static mutex count_mutex;
87*44704f69SBart Van Assche static mutex console_mutex;
88*44704f69SBart Van Assche static unsigned int even_notreadys;
89*44704f69SBart Van Assche static unsigned int odd_notreadys;
90*44704f69SBart Van Assche static unsigned int ebusy_count;
91*44704f69SBart Van Assche static int verbose;
92*44704f69SBart Van Assche 
93*44704f69SBart Van Assche 
94*44704f69SBart Van Assche static void
usage(void)95*44704f69SBart Van Assche usage(void)
96*44704f69SBart Van Assche {
97*44704f69SBart Van Assche     printf("Usage: %s [-e] [-h] [-n <n_per_thr>] [-N] [-R] [-s]\n"
98*44704f69SBart Van Assche            "                      [-t <num_thrs>] [-v] [-V] <disk_device>\n",
99*44704f69SBart Van Assche            util_name);
100*44704f69SBart Van Assche     printf("  where\n");
101*44704f69SBart Van Assche     printf("    -e                use O_EXCL on open (def: don't)\n");
102*44704f69SBart Van Assche     printf("    -h                print this usage message then exit\n");
103*44704f69SBart Van Assche     printf("    -n <n_per_thr>    number of loops per thread "
104*44704f69SBart Van Assche            "(def: %d)\n", DEF_NUM_PER_THREAD);
105*44704f69SBart Van Assche     printf("    -N                use O_NONBLOCK on open (def: don't)\n");
106*44704f69SBart Van Assche     printf("    -R                make sure device in ready (started) "
107*44704f69SBart Van Assche            "state after\n"
108*44704f69SBart Van Assche            "                      test (do extra iteration if "
109*44704f69SBart Van Assche            "necessary)\n");
110*44704f69SBart Van Assche     printf("    -s                share an open file handle (def: one "
111*44704f69SBart Van Assche            "per thread)\n");
112*44704f69SBart Van Assche     printf("    -t <num_thrs>     number of threads (def: %d)\n",
113*44704f69SBart Van Assche            DEF_NUM_THREADS);
114*44704f69SBart Van Assche     printf("    -v                increase verbosity\n");
115*44704f69SBart Van Assche     printf("    -V                print version number then exit\n\n");
116*44704f69SBart Van Assche     printf("Test if file handles keep context through to their responses. "
117*44704f69SBart Van Assche            "Sends\nTEST UNIT READY commands on even threads (origin 0) and "
118*44704f69SBart Van Assche            "START STOP\nUNIT commands on odd threads. Expect NOT READY "
119*44704f69SBart Van Assche            "sense keys only\nfrom the even threads (i.e from TUR)\n");
120*44704f69SBart Van Assche }
121*44704f69SBart Van Assche 
122*44704f69SBart Van Assche static int
pt_err(int res)123*44704f69SBart Van Assche pt_err(int res)
124*44704f69SBart Van Assche {
125*44704f69SBart Van Assche     if (res < 0)
126*44704f69SBart Van Assche         fprintf(stderr, "  pass through OS error: %s\n", safe_strerror(-res));
127*44704f69SBart Van Assche     else if (SCSI_PT_DO_BAD_PARAMS == res)
128*44704f69SBart Van Assche         fprintf(stderr, "  bad pass through setup\n");
129*44704f69SBart Van Assche     else if (SCSI_PT_DO_TIMEOUT == res)
130*44704f69SBart Van Assche         fprintf(stderr, "  pass through timeout\n");
131*44704f69SBart Van Assche     else
132*44704f69SBart Van Assche         fprintf(stderr, "  do_scsi_pt error=%d\n", res);
133*44704f69SBart Van Assche     return (res < 0) ? res : -EPERM /* -1 */;
134*44704f69SBart Van Assche }
135*44704f69SBart Van Assche 
136*44704f69SBart Van Assche static int
pt_cat_no_good(int cat,struct sg_pt_base * ptp,const unsigned char * sbp)137*44704f69SBart Van Assche pt_cat_no_good(int cat, struct sg_pt_base * ptp, const unsigned char * sbp)
138*44704f69SBart Van Assche {
139*44704f69SBart Van Assche     int slen;
140*44704f69SBart Van Assche     char b[256];
141*44704f69SBart Van Assche     const int bl = (int)sizeof(b);
142*44704f69SBart Van Assche     const char * cp = NULL;
143*44704f69SBart Van Assche 
144*44704f69SBart Van Assche     b[0] = '\0';
145*44704f69SBart Van Assche     switch (cat) {
146*44704f69SBart Van Assche     case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */
147*44704f69SBart Van Assche         sg_get_scsi_status_str(get_scsi_pt_status_response(ptp), bl, b);
148*44704f69SBart Van Assche         cp = "  scsi status: %s\n";
149*44704f69SBart Van Assche         break;
150*44704f69SBart Van Assche     case SCSI_PT_RESULT_SENSE:
151*44704f69SBart Van Assche         slen = get_scsi_pt_sense_len(ptp);
152*44704f69SBart Van Assche         sg_get_sense_str("", sbp, slen, 1, bl, b);
153*44704f69SBart Van Assche         cp = "%s\n";
154*44704f69SBart Van Assche         break;
155*44704f69SBart Van Assche     case SCSI_PT_RESULT_TRANSPORT_ERR:
156*44704f69SBart Van Assche         get_scsi_pt_transport_err_str(ptp, bl, b);
157*44704f69SBart Van Assche         cp = "  transport: %s\n";
158*44704f69SBart Van Assche         break;
159*44704f69SBart Van Assche     case SCSI_PT_RESULT_OS_ERR:
160*44704f69SBart Van Assche         get_scsi_pt_os_err_str(ptp, bl, b);
161*44704f69SBart Van Assche         cp = "  os: %s\n";
162*44704f69SBart Van Assche         break;
163*44704f69SBart Van Assche     default:
164*44704f69SBart Van Assche         cp = "  unknown pt result category (%d)\n";
165*44704f69SBart Van Assche         break;
166*44704f69SBart Van Assche     }
167*44704f69SBart Van Assche     if (cp) {
168*44704f69SBart Van Assche         lock_guard<mutex> lg(console_mutex);
169*44704f69SBart Van Assche 
170*44704f69SBart Van Assche         fprintf(stderr, cp, b);
171*44704f69SBart Van Assche     }
172*44704f69SBart Van Assche     return -EIO /* -5 */;
173*44704f69SBart Van Assche }
174*44704f69SBart Van Assche 
175*44704f69SBart Van Assche #define TUR_CMD_LEN 6
176*44704f69SBart Van Assche #define SSU_CMD_LEN 6
177*44704f69SBart Van Assche #define NOT_READY SG_LIB_CAT_NOT_READY
178*44704f69SBart Van Assche 
179*44704f69SBart Van Assche /* Returns 0 for good, 1024 for a sense key of NOT_READY, or a negative
180*44704f69SBart Van Assche  * errno */
181*44704f69SBart Van Assche static int
do_tur(struct sg_pt_base * ptp,int id)182*44704f69SBart Van Assche do_tur(struct sg_pt_base * ptp, int id)
183*44704f69SBart Van Assche {
184*44704f69SBart Van Assche     int slen, res, cat;
185*44704f69SBart Van Assche     unsigned char turCmdBlk [TUR_CMD_LEN] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
186*44704f69SBart Van Assche     unsigned char sense_buffer[64] SG_C_CPP_ZERO_INIT;
187*44704f69SBart Van Assche 
188*44704f69SBart Van Assche     clear_scsi_pt_obj(ptp);
189*44704f69SBart Van Assche     set_scsi_pt_cdb(ptp, turCmdBlk, sizeof(turCmdBlk));
190*44704f69SBart Van Assche     set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer));
191*44704f69SBart Van Assche     res = do_scsi_pt(ptp, -1, 20 /* secs timeout */, verbose);
192*44704f69SBart Van Assche     if (res) {
193*44704f69SBart Van Assche         {
194*44704f69SBart Van Assche             lock_guard<mutex> lg(console_mutex);
195*44704f69SBart Van Assche 
196*44704f69SBart Van Assche             fprintf(stderr, "TEST UNIT READY do_scsi_pt() submission error, "
197*44704f69SBart Van Assche                     "id=%d\n", id);
198*44704f69SBart Van Assche         }
199*44704f69SBart Van Assche         res = pt_err(res);
200*44704f69SBart Van Assche         goto err;
201*44704f69SBart Van Assche     }
202*44704f69SBart Van Assche     cat = get_scsi_pt_result_category(ptp);
203*44704f69SBart Van Assche     if (SCSI_PT_RESULT_GOOD != cat) {
204*44704f69SBart Van Assche         slen = get_scsi_pt_sense_len(ptp);
205*44704f69SBart Van Assche         if ((SCSI_PT_RESULT_SENSE == cat) &&
206*44704f69SBart Van Assche             (NOT_READY == sg_err_category_sense(sense_buffer, slen))) {
207*44704f69SBart Van Assche             res = 1024;
208*44704f69SBart Van Assche             goto err;
209*44704f69SBart Van Assche         }
210*44704f69SBart Van Assche         {
211*44704f69SBart Van Assche             lock_guard<mutex> lg(console_mutex);
212*44704f69SBart Van Assche 
213*44704f69SBart Van Assche             fprintf(stderr, "TEST UNIT READY do_scsi_pt() category problem, "
214*44704f69SBart Van Assche                     "id=%d\n", id);
215*44704f69SBart Van Assche         }
216*44704f69SBart Van Assche         res = pt_cat_no_good(cat, ptp, sense_buffer);
217*44704f69SBart Van Assche         goto err;
218*44704f69SBart Van Assche     }
219*44704f69SBart Van Assche     res = 0;
220*44704f69SBart Van Assche err:
221*44704f69SBart Van Assche     return res;
222*44704f69SBart Van Assche }
223*44704f69SBart Van Assche 
224*44704f69SBart Van Assche /* Returns 0 for good, 1024 for a sense key of NOT_READY, or a negative
225*44704f69SBart Van Assche  * errno */
226*44704f69SBart Van Assche static int
do_ssu(struct sg_pt_base * ptp,int id,bool start)227*44704f69SBart Van Assche do_ssu(struct sg_pt_base * ptp, int id, bool start)
228*44704f69SBart Van Assche {
229*44704f69SBart Van Assche     int slen, res, cat;
230*44704f69SBart Van Assche     unsigned char ssuCmdBlk [SSU_CMD_LEN] = {0x1b, 0x0, 0x0, 0x0, 0x0, 0x0};
231*44704f69SBart Van Assche     unsigned char sense_buffer[64] SG_C_CPP_ZERO_INIT;
232*44704f69SBart Van Assche 
233*44704f69SBart Van Assche     if (start)
234*44704f69SBart Van Assche         ssuCmdBlk[4] |= 0x1;
235*44704f69SBart Van Assche     clear_scsi_pt_obj(ptp);
236*44704f69SBart Van Assche     set_scsi_pt_cdb(ptp, ssuCmdBlk, sizeof(ssuCmdBlk));
237*44704f69SBart Van Assche     set_scsi_pt_sense(ptp, sense_buffer, sizeof(sense_buffer));
238*44704f69SBart Van Assche     res = do_scsi_pt(ptp, -1, 40 /* secs timeout */, verbose);
239*44704f69SBart Van Assche     if (res) {
240*44704f69SBart Van Assche         {
241*44704f69SBart Van Assche             lock_guard<mutex> lg(console_mutex);
242*44704f69SBart Van Assche 
243*44704f69SBart Van Assche             fprintf(stderr, "START STOP UNIT do_scsi_pt() submission error, "
244*44704f69SBart Van Assche                     "id=%d\n", id);
245*44704f69SBart Van Assche         }
246*44704f69SBart Van Assche         res = pt_err(res);
247*44704f69SBart Van Assche         goto err;
248*44704f69SBart Van Assche     }
249*44704f69SBart Van Assche     cat = get_scsi_pt_result_category(ptp);
250*44704f69SBart Van Assche     if (SCSI_PT_RESULT_GOOD != cat) {
251*44704f69SBart Van Assche         slen = get_scsi_pt_sense_len(ptp);
252*44704f69SBart Van Assche         if ((SCSI_PT_RESULT_SENSE == cat) &&
253*44704f69SBart Van Assche             (NOT_READY == sg_err_category_sense(sense_buffer, slen))) {
254*44704f69SBart Van Assche             res = 1024;
255*44704f69SBart Van Assche             goto err;
256*44704f69SBart Van Assche         }
257*44704f69SBart Van Assche         {
258*44704f69SBart Van Assche             lock_guard<mutex> lg(console_mutex);
259*44704f69SBart Van Assche 
260*44704f69SBart Van Assche             fprintf(stderr, "START STOP UNIT do_scsi_pt() category problem, "
261*44704f69SBart Van Assche                     "id=%d\n", id);
262*44704f69SBart Van Assche         }
263*44704f69SBart Van Assche         res = pt_cat_no_good(cat, ptp, sense_buffer);
264*44704f69SBart Van Assche         goto err;
265*44704f69SBart Van Assche     }
266*44704f69SBart Van Assche     res = 0;
267*44704f69SBart Van Assche err:
268*44704f69SBart Van Assche     return res;
269*44704f69SBart Van Assche }
270*44704f69SBart Van Assche 
271*44704f69SBart Van Assche static void
work_thread(const char * dev_name,int id,int num,bool share,int pt_fd,int nonblock,int oexcl,bool ready_after)272*44704f69SBart Van Assche work_thread(const char * dev_name, int id, int num, bool share,
273*44704f69SBart Van Assche             int pt_fd, int nonblock, int oexcl, bool ready_after)
274*44704f69SBart Van Assche {
275*44704f69SBart Van Assche     bool started = true;
276*44704f69SBart Van Assche     int k;
277*44704f69SBart Van Assche     int res = 0;
278*44704f69SBart Van Assche     unsigned int thr_even_notreadys = 0;
279*44704f69SBart Van Assche     unsigned int thr_odd_notreadys = 0;
280*44704f69SBart Van Assche     struct sg_pt_base * ptp = NULL;
281*44704f69SBart Van Assche 
282*44704f69SBart Van Assche     {
283*44704f69SBart Van Assche         lock_guard<mutex> lg(console_mutex);
284*44704f69SBart Van Assche 
285*44704f69SBart Van Assche         cerr << "Enter work_thread id=" << id << " num=" << num << " share="
286*44704f69SBart Van Assche              << share << endl;
287*44704f69SBart Van Assche     }
288*44704f69SBart Van Assche     if (! share) {      /* ignore passed ptp, make this thread's own */
289*44704f69SBart Van Assche         int oflags = O_RDWR;
290*44704f69SBart Van Assche         unsigned int thr_ebusy_count = 0;
291*44704f69SBart Van Assche 
292*44704f69SBart Van Assche         if (nonblock)
293*44704f69SBart Van Assche             oflags |= O_NONBLOCK;
294*44704f69SBart Van Assche         if (oexcl)
295*44704f69SBart Van Assche             oflags |= O_EXCL;
296*44704f69SBart Van Assche         while (((pt_fd = scsi_pt_open_flags(dev_name, oflags, verbose)) < 0)
297*44704f69SBart Van Assche                && (-EBUSY == pt_fd)) {
298*44704f69SBart Van Assche             ++thr_ebusy_count;
299*44704f69SBart Van Assche             this_thread::yield();       // give other threads a chance
300*44704f69SBart Van Assche         }
301*44704f69SBart Van Assche         if (pt_fd < 0) {
302*44704f69SBart Van Assche             char ebuff[EBUFF_SZ];
303*44704f69SBart Van Assche 
304*44704f69SBart Van Assche             snprintf(ebuff, EBUFF_SZ, "work_thread id=%d: error opening: %s",
305*44704f69SBart Van Assche                      id, dev_name);
306*44704f69SBart Van Assche             perror(ebuff);
307*44704f69SBart Van Assche             return;
308*44704f69SBart Van Assche         }
309*44704f69SBart Van Assche         if (thr_ebusy_count) {
310*44704f69SBart Van Assche             lock_guard<mutex> lg(count_mutex);
311*44704f69SBart Van Assche 
312*44704f69SBart Van Assche             ebusy_count += thr_ebusy_count;
313*44704f69SBart Van Assche         }
314*44704f69SBart Van Assche     }
315*44704f69SBart Van Assche     /* The instance of 'struct sg_pt_base' is local to this thread but the
316*44704f69SBart Van Assche      * pt_fd it contains may be shared, depending on the 'share' boolean. */
317*44704f69SBart Van Assche     ptp = construct_scsi_pt_obj_with_fd(pt_fd, verbose);
318*44704f69SBart Van Assche     if (NULL == ptp) {
319*44704f69SBart Van Assche         fprintf(stderr, "work_thread id=%d: "
320*44704f69SBart Van Assche                 "construct_scsi_pt_obj_with_fd() failed, memory?\n", id);
321*44704f69SBart Van Assche         return;
322*44704f69SBart Van Assche     }
323*44704f69SBart Van Assche     for (k = 0; k < num; ++k) {
324*44704f69SBart Van Assche         if (0 == (id % 2)) {
325*44704f69SBart Van Assche             /* Even thread ids do TEST UNIT READYs */
326*44704f69SBart Van Assche             res = do_tur(ptp, id);
327*44704f69SBart Van Assche             if (1024 == res) {
328*44704f69SBart Van Assche                 ++thr_even_notreadys;
329*44704f69SBart Van Assche                 res = 0;
330*44704f69SBart Van Assche             }
331*44704f69SBart Van Assche         } else {
332*44704f69SBart Van Assche             /* Odd thread ids do START STOP UNITs, alternating between
333*44704f69SBart Van Assche              * starts and stops */
334*44704f69SBart Van Assche             started = (0 == (k % 2));
335*44704f69SBart Van Assche             res = do_ssu(ptp, id, started);
336*44704f69SBart Van Assche             if (1024 == res) {
337*44704f69SBart Van Assche                 ++thr_odd_notreadys;
338*44704f69SBart Van Assche                 res = 0;
339*44704f69SBart Van Assche             }
340*44704f69SBart Van Assche         }
341*44704f69SBart Van Assche         if (res)
342*44704f69SBart Van Assche             break;
343*44704f69SBart Van Assche         if (ready_after && (! started))
344*44704f69SBart Van Assche             do_ssu(ptp, id, true);
345*44704f69SBart Van Assche     }
346*44704f69SBart Van Assche     if (ptp)
347*44704f69SBart Van Assche         destruct_scsi_pt_obj(ptp);
348*44704f69SBart Van Assche     if ((! share) && (pt_fd >= 0))
349*44704f69SBart Van Assche         close(pt_fd);
350*44704f69SBart Van Assche 
351*44704f69SBart Van Assche     {
352*44704f69SBart Van Assche         lock_guard<mutex> lg(count_mutex);
353*44704f69SBart Van Assche 
354*44704f69SBart Van Assche         even_notreadys += thr_even_notreadys;
355*44704f69SBart Van Assche         odd_notreadys += thr_odd_notreadys;
356*44704f69SBart Van Assche     }
357*44704f69SBart Van Assche 
358*44704f69SBart Van Assche     {
359*44704f69SBart Van Assche         lock_guard<mutex> lg(console_mutex);
360*44704f69SBart Van Assche 
361*44704f69SBart Van Assche         if (k < num)
362*44704f69SBart Van Assche             cerr << "thread id=" << id << " FAILed at iteration: " << k
363*44704f69SBart Van Assche                  << "  [negated errno: " << res << " <"
364*44704f69SBart Van Assche                  <<  safe_strerror(-res) << ">]" << endl;
365*44704f69SBart Van Assche         else
366*44704f69SBart Van Assche             cerr << "thread id=" << id << " normal exit" << '\n';
367*44704f69SBart Van Assche     }
368*44704f69SBart Van Assche }
369*44704f69SBart Van Assche 
370*44704f69SBart Van Assche 
371*44704f69SBart Van Assche int
main(int argc,char * argv[])372*44704f69SBart Van Assche main(int argc, char * argv[])
373*44704f69SBart Van Assche {
374*44704f69SBart Van Assche     int k;
375*44704f69SBart Van Assche     int pt_fd = -1;
376*44704f69SBart Van Assche     int oexcl = 0;
377*44704f69SBart Van Assche     int nonblock = 0;
378*44704f69SBart Van Assche     int num_per_thread = DEF_NUM_PER_THREAD;
379*44704f69SBart Van Assche     bool ready_after = false;
380*44704f69SBart Van Assche     bool share = false;
381*44704f69SBart Van Assche     int num_threads = DEF_NUM_THREADS;
382*44704f69SBart Van Assche     char * dev_name = NULL;
383*44704f69SBart Van Assche 
384*44704f69SBart Van Assche     for (k = 1; k < argc; ++k) {
385*44704f69SBart Van Assche         if (0 == memcmp("-e", argv[k], 2))
386*44704f69SBart Van Assche             ++oexcl;
387*44704f69SBart Van Assche         else if (0 == memcmp("-h", argv[k], 2)) {
388*44704f69SBart Van Assche             usage();
389*44704f69SBart Van Assche             return 0;
390*44704f69SBart Van Assche         } else if (0 == memcmp("-n", argv[k], 2)) {
391*44704f69SBart Van Assche             ++k;
392*44704f69SBart Van Assche             if ((k < argc) && isdigit(*argv[k])) {
393*44704f69SBart Van Assche                 num_per_thread = sg_get_num(argv[k]);
394*44704f69SBart Van Assche                 if (num_per_thread<= 0) {
395*44704f69SBart Van Assche                     fprintf(stderr, "want positive integer for number "
396*44704f69SBart Van Assche                             "per thread\n");
397*44704f69SBart Van Assche                     return 1;
398*44704f69SBart Van Assche                 }
399*44704f69SBart Van Assche             } else
400*44704f69SBart Van Assche                 break;
401*44704f69SBart Van Assche         } else if (0 == memcmp("-N", argv[k], 2))
402*44704f69SBart Van Assche             ++nonblock;
403*44704f69SBart Van Assche         else if (0 == memcmp("-R", argv[k], 2))
404*44704f69SBart Van Assche             ready_after = true;
405*44704f69SBart Van Assche         else if (0 == memcmp("-s", argv[k], 2))
406*44704f69SBart Van Assche             share = true;
407*44704f69SBart Van Assche         else if (0 == memcmp("-t", argv[k], 2)) {
408*44704f69SBart Van Assche             ++k;
409*44704f69SBart Van Assche             if ((k < argc) && isdigit(*argv[k]))
410*44704f69SBart Van Assche                 num_threads = atoi(argv[k]);
411*44704f69SBart Van Assche             else
412*44704f69SBart Van Assche                 break;
413*44704f69SBart Van Assche         } else if (0 == memcmp("-v", argv[k], 2))
414*44704f69SBart Van Assche             ++verbose;
415*44704f69SBart Van Assche         else if (0 == memcmp("-V", argv[k], 2)) {
416*44704f69SBart Van Assche             printf("%s version: %s\n", util_name, version_str);
417*44704f69SBart Van Assche             return 0;
418*44704f69SBart Van Assche         } else if (*argv[k] == '-') {
419*44704f69SBart Van Assche             printf("Unrecognized switch: %s\n", argv[k]);
420*44704f69SBart Van Assche             dev_name = NULL;
421*44704f69SBart Van Assche             break;
422*44704f69SBart Van Assche         }
423*44704f69SBart Van Assche         else if (! dev_name)
424*44704f69SBart Van Assche             dev_name = argv[k];
425*44704f69SBart Van Assche         else {
426*44704f69SBart Van Assche             printf("too many arguments\n");
427*44704f69SBart Van Assche             dev_name = 0;
428*44704f69SBart Van Assche             break;
429*44704f69SBart Van Assche         }
430*44704f69SBart Van Assche     }
431*44704f69SBart Van Assche     if (0 == dev_name) {
432*44704f69SBart Van Assche         usage();
433*44704f69SBart Van Assche         return 1;
434*44704f69SBart Van Assche     }
435*44704f69SBart Van Assche     try {
436*44704f69SBart Van Assche         if (share) {
437*44704f69SBart Van Assche             int oflags = O_RDWR;
438*44704f69SBart Van Assche 
439*44704f69SBart Van Assche             if (nonblock)
440*44704f69SBart Van Assche                 oflags |= O_NONBLOCK;
441*44704f69SBart Van Assche             if (oexcl)
442*44704f69SBart Van Assche                 oflags |= O_EXCL;
443*44704f69SBart Van Assche             while (((pt_fd = scsi_pt_open_flags(dev_name, oflags, verbose))
444*44704f69SBart Van Assche                     < 0) && (-EBUSY == pt_fd)) {
445*44704f69SBart Van Assche                 ++ebusy_count;
446*44704f69SBart Van Assche                 sleep(0);                   // process yield ??
447*44704f69SBart Van Assche             }
448*44704f69SBart Van Assche             if (pt_fd < 0) {
449*44704f69SBart Van Assche                 char ebuff[EBUFF_SZ];
450*44704f69SBart Van Assche 
451*44704f69SBart Van Assche                 snprintf(ebuff, EBUFF_SZ, "main: error opening: %s",
452*44704f69SBart Van Assche                          dev_name);
453*44704f69SBart Van Assche                 perror(ebuff);
454*44704f69SBart Van Assche                 return 1;
455*44704f69SBart Van Assche             }
456*44704f69SBart Van Assche             /* Tried calling construct_scsi_pt_obj_with_fd() here but that
457*44704f69SBart Van Assche              * doesn't work since 'struct sg_pt_base' objects aren't
458*44704f69SBart Van Assche              * thread-safe without user space intervention (e.g. mutexes). */
459*44704f69SBart Van Assche         }
460*44704f69SBart Van Assche 
461*44704f69SBart Van Assche         vector<thread *> vt;
462*44704f69SBart Van Assche 
463*44704f69SBart Van Assche         for (k = 0; k < num_threads; ++k) {
464*44704f69SBart Van Assche             thread * tp = new thread {work_thread, dev_name, k,
465*44704f69SBart Van Assche                                       num_per_thread, share, pt_fd, nonblock,
466*44704f69SBart Van Assche                                       oexcl, ready_after};
467*44704f69SBart Van Assche             vt.push_back(tp);
468*44704f69SBart Van Assche         }
469*44704f69SBart Van Assche 
470*44704f69SBart Van Assche         for (k = 0; k < (int)vt.size(); ++k)
471*44704f69SBart Van Assche             vt[k]->join();
472*44704f69SBart Van Assche 
473*44704f69SBart Van Assche         for (k = 0; k < (int)vt.size(); ++k)
474*44704f69SBart Van Assche             delete vt[k];
475*44704f69SBart Van Assche 
476*44704f69SBart Van Assche         if (share)
477*44704f69SBart Van Assche             scsi_pt_close_device(pt_fd);
478*44704f69SBart Van Assche 
479*44704f69SBart Van Assche         cout << "Expected not_readys on TEST UNIT READY: " << even_notreadys
480*44704f69SBart Van Assche              << endl;
481*44704f69SBart Van Assche         cout << "UNEXPECTED not_readys on START STOP UNIT: "
482*44704f69SBart Van Assche              << odd_notreadys << endl;
483*44704f69SBart Van Assche         if (ebusy_count)
484*44704f69SBart Van Assche             cout << "Number of EBUSYs (on open): " << ebusy_count << endl;
485*44704f69SBart Van Assche 
486*44704f69SBart Van Assche     }
487*44704f69SBart Van Assche     catch(system_error& e)  {
488*44704f69SBart Van Assche         cerr << "got a system_error exception: " << e.what() << '\n';
489*44704f69SBart Van Assche         auto ec = e.code();
490*44704f69SBart Van Assche         cerr << "category: " << ec.category().name() << '\n';
491*44704f69SBart Van Assche         cerr << "value: " << ec.value() << '\n';
492*44704f69SBart Van Assche         cerr << "message: " << ec.message() << '\n';
493*44704f69SBart Van Assche         cerr << "\nNote: if g++ may need '-pthread' or similar in "
494*44704f69SBart Van Assche                 "compile/link line" << '\n';
495*44704f69SBart Van Assche     }
496*44704f69SBart Van Assche     catch(...) {
497*44704f69SBart Van Assche         cerr << "got another exception: " << '\n';
498*44704f69SBart Van Assche     }
499*44704f69SBart Van Assche     if (pt_fd >= 0)
500*44704f69SBart Van Assche         close(pt_fd);
501*44704f69SBart Van Assche     return 0;
502*44704f69SBart Van Assche }
503