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