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