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