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