xref: /aosp_15_r20/external/sg3_utils/src/sgp_dd.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /* A utility program for copying files. Specialised for "files" that
2  * represent devices that understand the SCSI command set.
3  *
4  * Copyright (C) 1999 - 2022 D. Gilbert and P. Allworth
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later
11  *
12  * This program is a specialisation of the Unix "dd" command in which
13  * one or both of the given files is a scsi generic device or a raw
14  * device. A logical block size ('bs') is assumed to be 512 if not given.
15  * This program complains if 'ibs' or 'obs' are given with some other value
16  * than 'bs'. If 'if' is not given or 'if=-' then stdin is assumed. If
17  * 'of' is not given or 'of=-' then stdout assumed.
18  *
19  * A non-standard argument "bpt" (blocks per transfer) is added to control
20  * the maximum number of blocks in each transfer. The default value is 128.
21  * For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16 KiB
22  * in this case) are transferred to or from the sg device in a single SCSI
23  * command.
24  *
25  * This version is designed for the Linux kernel 2.4, 2.6, 3, 4 and 5 series.
26  *
27  * sgp_dd is a Posix threads specialization of the sg_dd utility. Both
28  * sgp_dd and sg_dd only perform special tasks when one or both of the given
29  * devices belong to the Linux sg driver
30  */
31 
32 #define _XOPEN_SOURCE 600
33 #ifndef _GNU_SOURCE
34 #define _GNU_SOURCE 1
35 #endif
36 
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <stdarg.h>
42 #include <stdbool.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <errno.h>
46 #include <limits.h>
47 #include <pthread.h>
48 #include <signal.h>
49 #define __STDC_FORMAT_MACROS 1
50 #include <inttypes.h>
51 #include <sys/mman.h>
52 #include <sys/ioctl.h>
53 #include <sys/stat.h>
54 #include <sys/sysmacros.h>
55 #ifndef major
56 #include <sys/types.h>
57 #endif
58 #include <sys/time.h>
59 #include <linux/major.h>        /* for MEM_MAJOR, SCSI_GENERIC_MAJOR, etc */
60 #include <linux/fs.h>           /* for BLKSSZGET and friends */
61 
62 #ifdef HAVE_CONFIG_H
63 #include "config.h"
64 #endif
65 
66 #ifdef __STDC_VERSION__
67 #if __STDC_VERSION__ >= 201112L && defined(HAVE_STDATOMIC_H)
68 #ifndef __STDC_NO_ATOMICS__
69 
70 #define HAVE_C11_ATOMICS
71 #include <stdatomic.h>
72 
73 #endif
74 #endif
75 #endif
76 
77 #ifndef HAVE_C11_ATOMICS
78 #warning "Don't have C11 Atomics, using mutex with pack_id"
79 #endif
80 
81 #include "sg_lib.h"
82 #include "sg_cmds_basic.h"
83 #include "sg_io_linux.h"
84 #include "sg_unaligned.h"
85 #include "sg_pr2serr.h"
86 
87 
88 static const char * version_str = "5.84 20220118";
89 
90 #define DEF_BLOCK_SIZE 512
91 #define DEF_BLOCKS_PER_TRANSFER 128
92 #define DEF_BLOCKS_PER_2048TRANSFER 32
93 #define DEF_SCSI_CDBSZ 10
94 #define MAX_SCSI_CDBSZ 16
95 #define MAX_BPT_VALUE (1 << 24)         /* used for maximum bs as well */
96 #define MAX_COUNT_SKIP_SEEK (1LL << 48) /* coverity wants upper bound */
97 
98 
99 #define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
100 #define READ_CAP_REPLY_LEN 8
101 #define RCAP16_REPLY_LEN 32
102 
103 #define DEF_TIMEOUT 60000       /* 60,000 millisecs == 60 seconds */
104 
105 #define SGP_READ10 0x28
106 #define SGP_WRITE10 0x2a
107 #define DEF_NUM_THREADS 4
108 #define MAX_NUM_THREADS 1024  /* was SG_MAX_QUEUE (16) but no longer applies */
109 
110 #ifndef RAW_MAJOR
111 #define RAW_MAJOR 255   /*unlikely value */
112 #endif
113 
114 #define FT_OTHER 1              /* filetype other than one of the following */
115 #define FT_SG 2                 /* filetype is sg char device */
116 #define FT_RAW 4                /* filetype is raw char device */
117 #define FT_DEV_NULL 8           /* either "/dev/null" or "." as filename */
118 #define FT_ST 16                /* filetype is st char device (tape) */
119 #define FT_BLOCK 32             /* filetype is a block device */
120 #define FT_ERROR 64             /* couldn't "stat" file */
121 
122 #define DEV_NULL_MINOR_NUM 3
123 
124 #define EBUFF_SZ 768
125 
126 #ifndef SG_FLAG_MMAP_IO
127 #define SG_FLAG_MMAP_IO 4
128 #endif
129 
130 #define STR_SZ 1024
131 #define INOUTF_SZ 512
132 
133 
134 struct flags_t {
135     bool append;
136     bool coe;
137     bool dio;
138     bool direct;
139     bool dpo;
140     bool dsync;
141     bool excl;
142     bool fua;
143     bool mmap;
144 };
145 
146 struct opts_t
147 {       /* one instance visible to all threads */
148     int infd;
149     int64_t skip;
150     int in_type;
151     int cdbsz_in;
152     struct flags_t in_flags;
153     int64_t in_blk;                 /* next block address to read */
154     int64_t in_count;               /* blocks remaining for next read */
155     int64_t in_rem_count;           /* count of remaining in blocks */
156     int in_partial;
157     pthread_mutex_t inout_mutex;
158     int outfd;
159     int64_t seek;
160     int out_type;
161     int cdbsz_out;
162     struct flags_t out_flags;
163     int64_t out_blk;                /* next block address to write */
164     int64_t out_count;              /* blocks remaining for next write */
165     int64_t out_rem_count;          /* count of remaining out blocks */
166     int out_partial;
167     pthread_cond_t out_sync_cv;
168     int bs;
169     int bpt;
170     int num_threads;
171     int dio_incomplete_count;
172     int sum_of_resids;
173     bool mmap_active;
174     int chkaddr;        /* check read data contains 4 byte, big endian block
175                          * addresses, once: check only 4 bytes per block */
176     int progress;       /* accept --progress or -p, does nothing */
177     int debug;
178     int dry_run;
179 };
180 
181 struct thread_arg
182 {       /* pointer to this argument passed to thread */
183     int id;
184     int64_t seek_skip;
185 };
186 
187 typedef struct request_element
188 {       /* one instance per worker thread */
189     bool wr;
190     bool in_stop;
191     bool in_err;
192     bool out_err;
193     bool use_no_dxfer;
194     int infd;
195     int outfd;
196     int64_t blk;
197     int num_blks;
198     uint8_t * buffp;
199     uint8_t * alloc_bp;
200     struct sg_io_hdr io_hdr;
201     uint8_t cdb[MAX_SCSI_CDBSZ];
202     uint8_t sb[SENSE_BUFF_LEN];
203     int bs;
204     int dio_incomplete_count;
205     int resid;
206     int cdbsz_in;
207     int cdbsz_out;
208     struct flags_t in_flags;
209     struct flags_t out_flags;
210     int debug;
211     uint32_t pack_id;
212 } Rq_elem;
213 
214 static sigset_t signal_set;
215 static pthread_t sig_listen_thread_id;
216 
217 static const char * sg_allow_dio = "/sys/module/sg/parameters/allow_dio";
218 
219 static void sg_in_operation(struct opts_t * clp, Rq_elem * rep);
220 static void sg_out_operation(struct opts_t * clp, Rq_elem * rep,
221                              bool bump_out_blk);
222 static void normal_in_operation(struct opts_t * clp, Rq_elem * rep,
223                                 int blocks);
224 static void normal_out_operation(struct opts_t * clp, Rq_elem * rep,
225                                  int blocks, bool bump_out_blk);
226 static int sg_start_io(Rq_elem * rep);
227 static int sg_finish_io(bool wr, Rq_elem * rep, pthread_mutex_t * a_mutp);
228 
229 #ifdef HAVE_C11_ATOMICS
230 
231 /* Assume initialized to 0, but want to start at 1, hence adding 1 in macro */
232 static atomic_uint ascending_val;
233 
234 static atomic_uint num_eintr;
235 static atomic_uint num_eagain;
236 static atomic_uint num_ebusy;
237 static atomic_bool exit_threads;
238 
239 #define GET_NEXT_PACK_ID(_v) (atomic_fetch_add(&ascending_val, _v) + (_v))
240 
241 #else
242 
243 static pthread_mutex_t av_mut = PTHREAD_MUTEX_INITIALIZER;
244 static int ascending_val = 1;
245 static volatile bool exit_threads;
246 
247 static unsigned int
GET_NEXT_PACK_ID(unsigned int val)248 GET_NEXT_PACK_ID(unsigned int val)
249 {
250     int res;
251 
252     pthread_mutex_lock(&av_mut);
253     res = ascending_val;
254     ascending_val += val;
255     pthread_mutex_unlock(&av_mut);
256     return res;
257 }
258 
259 #endif
260 
261 #define STRERR_BUFF_LEN 128
262 
263 static pthread_mutex_t strerr_mut = PTHREAD_MUTEX_INITIALIZER;
264 
265 static pthread_t threads[MAX_NUM_THREADS];
266 static struct thread_arg thr_arg_a[MAX_NUM_THREADS];
267 
268 static bool shutting_down = false;
269 static bool do_sync = false;
270 static bool do_time = false;
271 static struct opts_t my_opts;
272 static struct timeval start_tm;
273 static int64_t dd_count = -1;
274 static int exit_status = 0;
275 static char infn[INOUTF_SZ];
276 static char outfn[INOUTF_SZ];
277 
278 static const char * my_name = "sgp_dd: ";
279 
280 
281 static void
calc_duration_throughput(int contin)282 calc_duration_throughput(int contin)
283 {
284     struct timeval end_tm, res_tm;
285     double a, b;
286 
287     gettimeofday(&end_tm, NULL);
288     res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
289     res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
290     if (res_tm.tv_usec < 0) {
291         --res_tm.tv_sec;
292         res_tm.tv_usec += 1000000;
293     }
294     a = res_tm.tv_sec;
295     a += (0.000001 * res_tm.tv_usec);
296     b = (double)my_opts.bs * (dd_count - my_opts.out_rem_count);
297     pr2serr("time to transfer data %s %d.%06d secs",
298             (contin ? "so far" : "was"), (int)res_tm.tv_sec,
299             (int)res_tm.tv_usec);
300     if ((a > 0.00001) && (b > 511))
301         pr2serr(", %.2f MB/sec\n", b / (a * 1000000.0));
302     else
303         pr2serr("\n");
304 }
305 
306 static void
print_stats(const char * str)307 print_stats(const char * str)
308 {
309     int64_t infull, outfull;
310 
311     if (0 != my_opts.out_rem_count)
312         pr2serr("  remaining block count=%" PRId64 "\n",
313                 my_opts.out_rem_count);
314     infull = dd_count - my_opts.in_rem_count;
315     pr2serr("%s%" PRId64 "+%d records in\n", str,
316             infull - my_opts.in_partial, my_opts.in_partial);
317 
318     outfull = dd_count - my_opts.out_rem_count;
319     pr2serr("%s%" PRId64 "+%d records out\n", str,
320             outfull - my_opts.out_partial, my_opts.out_partial);
321 }
322 
323 static void
interrupt_handler(int sig)324 interrupt_handler(int sig)
325 {
326     struct sigaction sigact;
327 
328     sigact.sa_handler = SIG_DFL;
329     sigemptyset(&sigact.sa_mask);
330     sigact.sa_flags = 0;
331     sigaction(sig, &sigact, NULL);
332     pr2serr("Interrupted by signal,");
333     if (do_time)
334         calc_duration_throughput(0);
335     print_stats("");
336     kill(getpid (), sig);
337 }
338 
339 static void
siginfo_handler(int sig)340 siginfo_handler(int sig)
341 {
342     if (sig) { ; }      /* unused, dummy to suppress warning */
343     pr2serr("Progress report, continuing ...\n");
344     if (do_time)
345         calc_duration_throughput(1);
346     print_stats("  ");
347 }
348 
349 static void
install_handler(int sig_num,void (* sig_handler)(int sig))350 install_handler(int sig_num, void (*sig_handler) (int sig))
351 {
352     struct sigaction sigact;
353     sigaction (sig_num, NULL, &sigact);
354     if (sigact.sa_handler != SIG_IGN)
355     {
356         sigact.sa_handler = sig_handler;
357         sigemptyset (&sigact.sa_mask);
358         sigact.sa_flags = 0;
359         sigaction (sig_num, &sigact, NULL);
360     }
361 }
362 
363 #ifdef SG_LIB_ANDROID
364 static void
thread_exit_handler(int sig)365 thread_exit_handler(int sig)
366 {
367     pthread_exit(0);
368 }
369 #endif
370 
371 /* Make safe_strerror() thread safe */
372 static char *
tsafe_strerror(int code,char * ebp)373 tsafe_strerror(int code, char * ebp)
374 {
375     int status;
376     char * cp;
377 
378     status = pthread_mutex_lock(&strerr_mut);
379     if (0 != status) pr2serr("lock strerr_mut");
380     cp = safe_strerror(code);
381     strncpy(ebp, cp, STRERR_BUFF_LEN);
382     status = pthread_mutex_unlock(&strerr_mut);
383     if (0 != status) pr2serr("unlock strerr_mut");
384     ebp[STRERR_BUFF_LEN - 1] = '\0';
385     return ebp;
386 }
387 
388 
389 /* Following macro from D.R. Butenhof's POSIX threads book:
390  * ISBN 0-201-63392-2 . [Highly recommended book.] Changed __FILE__
391  * to __func__ */
392 #define err_exit(code,text) do { \
393     char _strerr_buff[STRERR_BUFF_LEN + 1]; \
394     pr2serr("%s at \"%s\":%d: %s\n", \
395         text, __func__, __LINE__, tsafe_strerror(code, _strerr_buff)); \
396     exit(1); \
397     } while (0)
398 
399 
400 static int
dd_filetype(const char * filename)401 dd_filetype(const char * filename)
402 {
403     struct stat st;
404     size_t len = strlen(filename);
405 
406     if ((1 == len) && ('.' == filename[0]))
407         return FT_DEV_NULL;
408     if (stat(filename, &st) < 0)
409         return FT_ERROR;
410     if (S_ISCHR(st.st_mode)) {
411         if ((MEM_MAJOR == major(st.st_rdev)) &&
412             (DEV_NULL_MINOR_NUM == minor(st.st_rdev)))
413             return FT_DEV_NULL;
414         if (RAW_MAJOR == major(st.st_rdev))
415             return FT_RAW;
416         if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
417             return FT_SG;
418         if (SCSI_TAPE_MAJOR == major(st.st_rdev))
419             return FT_ST;
420     } else if (S_ISBLK(st.st_mode))
421         return FT_BLOCK;
422     return FT_OTHER;
423 }
424 
425 static void
usage()426 usage()
427 {
428     pr2serr("Usage: sgp_dd  [bs=BS] [count=COUNT] [ibs=BS] [if=IFILE]"
429             " [iflag=FLAGS]\n"
430             "               [obs=BS] [of=OFILE] [oflag=FLAGS] "
431             "[seek=SEEK] [skip=SKIP]\n"
432             "               [--help] [--version]\n\n");
433     pr2serr("               [bpt=BPT] [cdbsz=6|10|12|16] [coe=0|1] "
434             "[deb=VERB] [dio=0|1]\n"
435             "               [fua=0|1|2|3] [sync=0|1] [thr=THR] "
436             "[time=0|1] [verbose=VERB]\n"
437             "               [--dry-run] [--verbose]\n"
438             "  where:\n"
439             "    bpt         is blocks_per_transfer (default is 128)\n"
440             "    bs          must be device logical block size (default "
441             "512)\n"
442             "    cdbsz       size of SCSI READ or WRITE cdb (default is 10)\n"
443             "    coe         continue on error, 0->exit (def), "
444             "1->zero + continue\n"
445             "    count       number of blocks to copy (def: device size)\n"
446             "    deb         for debug, 0->none (def), > 0->varying degrees "
447             "of debug\n");
448     pr2serr("    dio         is direct IO, 1->attempt, 0->indirect IO (def)\n"
449             "    fua         force unit access: 0->don't(def), 1->OFILE, "
450             "2->IFILE,\n"
451             "                3->OFILE+IFILE\n"
452             "    if          file or device to read from (def: stdin)\n"
453             "    iflag       comma separated list from: [coe,dio,direct,dpo,"
454             "dsync,excl,\n"
455             "                fua,mmap,null]\n"
456             "    of          file or device to write to (def: stdout), "
457             "OFILE of '.'\n"
458             "                treated as /dev/null\n"
459             "    oflag       comma separated list from: [append,coe,dio,"
460             "direct,dpo,\n"
461             "                dsync,excl,fua,mmap,null]\n"
462             "    seek        block position to start writing to OFILE\n"
463             "    skip        block position to start reading from IFILE\n"
464             "    sync        0->no sync(def), 1->SYNCHRONIZE CACHE on OFILE "
465             "after copy\n"
466             "    thr         is number of threads, must be > 0, default 4, "
467             "max 1024\n"
468             "    time        0->no timing(def), 1->time plus calculate "
469             "throughput\n"
470             "    verbose     same as 'deb=VERB': increase verbosity\n"
471             "    --chkaddr|-c    check read data contains blk address\n"
472             "    --dry-run|-d    prepare but bypass copy/read\n"
473             "    --help|-h      output this usage message then exit\n"
474             "    --verbose|-v   increase verbosity of utility\n"
475             "    --version|-V   output version string then exit\n"
476             "Copy from IFILE to OFILE, similar to dd command\n"
477             "specialized for SCSI devices, uses multiple POSIX threads\n");
478 }
479 
480 static int
sgp_mem_mmap(int fd,int res_sz,uint8_t ** mmpp)481 sgp_mem_mmap(int fd, int res_sz, uint8_t ** mmpp)
482 {
483     int t;
484 
485     if (ioctl(fd, SG_GET_RESERVED_SIZE, &t) < 0) {
486         perror("SG_GET_RESERVED_SIZE error");
487         return -1;
488     }
489     if (t < (int)sg_get_page_size())
490         t = sg_get_page_size();
491     if (res_sz > t) {
492         if (ioctl(fd, SG_SET_RESERVED_SIZE, &res_sz) < 0) {
493             perror("SG_SET_RESERVED_SIZE error");
494             return -1;
495         }
496     }
497     *mmpp = (uint8_t *)mmap(NULL, res_sz,
498                             PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
499     if (MAP_FAILED == *mmpp) {
500         perror("mmap() failed");
501         return -1;
502     }
503     return 0;
504 }
505 
506 /* Return of 0 -> success, see sg_ll_read_capacity*() otherwise */
507 static int
scsi_read_capacity(int sg_fd,int64_t * num_sect,int * sect_sz)508 scsi_read_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
509 {
510     int res;
511     uint8_t rcBuff[RCAP16_REPLY_LEN];
512 
513     res = sg_ll_readcap_10(sg_fd, 0, 0, rcBuff, READ_CAP_REPLY_LEN, false, 0);
514     if (0 != res)
515         return res;
516 
517     if ((0xff == rcBuff[0]) && (0xff == rcBuff[1]) && (0xff == rcBuff[2]) &&
518         (0xff == rcBuff[3])) {
519 
520         res = sg_ll_readcap_16(sg_fd, 0, 0, rcBuff, RCAP16_REPLY_LEN, false,
521                                0);
522         if (0 != res)
523             return res;
524         *num_sect = sg_get_unaligned_be64(rcBuff + 0) + 1;
525         *sect_sz = sg_get_unaligned_be32(rcBuff + 8);
526     } else {
527         /* take care not to sign extend values > 0x7fffffff */
528         *num_sect = (int64_t)sg_get_unaligned_be32(rcBuff + 0) + 1;
529         *sect_sz = sg_get_unaligned_be32(rcBuff + 4);
530     }
531     return 0;
532 }
533 
534 /* Return of 0 -> success, -1 -> failure. BLKGETSIZE64, BLKGETSIZE and */
535 /* BLKSSZGET macros problematic (from <linux/fs.h> or <sys/mount.h>). */
536 static int
read_blkdev_capacity(int sg_fd,int64_t * num_sect,int * sect_sz)537 read_blkdev_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
538 {
539 #ifdef BLKSSZGET
540     if ((ioctl(sg_fd, BLKSSZGET, sect_sz) < 0) && (*sect_sz > 0)) {
541         perror("BLKSSZGET ioctl error");
542         return -1;
543     } else {
544  #ifdef BLKGETSIZE64
545         uint64_t ull;
546 
547         if (ioctl(sg_fd, BLKGETSIZE64, &ull) < 0) {
548 
549             perror("BLKGETSIZE64 ioctl error");
550             return -1;
551         }
552         *num_sect = ((int64_t)ull / (int64_t)*sect_sz);
553  #else
554         unsigned long ul;
555 
556         if (ioctl(sg_fd, BLKGETSIZE, &ul) < 0) {
557             perror("BLKGETSIZE ioctl error");
558             return -1;
559         }
560         *num_sect = (int64_t)ul;
561  #endif
562     }
563     return 0;
564 #else
565     *num_sect = 0;
566     *sect_sz = 0;
567     return -1;
568 #endif
569 }
570 
571 static void *
sig_listen_thread(void * v_clp)572 sig_listen_thread(void * v_clp)
573 {
574     struct opts_t * clp = (struct opts_t *)v_clp;
575     int sig_number;
576 
577     while (1) {
578         sigwait(&signal_set, &sig_number);
579         if (shutting_down)
580             break;
581         if (SIGINT == sig_number) {
582             pr2serr("%sinterrupted by SIGINT\n", my_name);
583 #ifdef HAVE_C11_ATOMICS
584             atomic_store(&exit_threads, true);
585 #else
586             exit_threads = true;
587 #endif
588             pthread_cond_broadcast(&clp->out_sync_cv);
589         }
590     }
591     return NULL;
592 }
593 
594 static void
cleanup_in(void * v_clp)595 cleanup_in(void * v_clp)
596 {
597     struct opts_t * clp = (struct opts_t *)v_clp;
598 
599     pr2serr("thread cancelled while in mutex held\n");
600     pthread_mutex_unlock(&clp->inout_mutex);
601     pthread_cond_broadcast(&clp->out_sync_cv);
602 }
603 
604 static void
cleanup_out(void * v_clp)605 cleanup_out(void * v_clp)
606 {
607     struct opts_t * clp = (struct opts_t *)v_clp;
608 
609     pr2serr("thread cancelled while out mutex held\n");
610     pthread_mutex_unlock(&clp->inout_mutex);
611     pthread_cond_broadcast(&clp->out_sync_cv);
612 }
613 
614 static int
sg_prepare(int fd,int bs,int bpt)615 sg_prepare(int fd, int bs, int bpt)
616 {
617     int res, t;
618 
619     res = ioctl(fd, SG_GET_VERSION_NUM, &t);
620     if ((res < 0) || (t < 30000)) {
621         pr2serr("%ssg driver prior to 3.x.y\n", my_name);
622         return 1;
623     }
624     t = bs * bpt;
625     res = ioctl(fd, SG_SET_RESERVED_SIZE, &t);
626     if (res < 0)
627         perror("sgp_dd: SG_SET_RESERVED_SIZE error");
628     t = 1;
629     res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t);
630     if (res < 0)
631         perror("sgp_dd: SG_SET_FORCE_PACK_ID error");
632     return 0;
633 }
634 
635 static int
sg_in_open(const char * fnp,struct flags_t * flagp,int bs,int bpt)636 sg_in_open(const char * fnp, struct flags_t * flagp, int bs, int bpt)
637 {
638     int flags = O_RDWR;
639     int fd, err;
640     char ebuff[800];
641 
642     if (flagp->direct)
643         flags |= O_DIRECT;
644     if (flagp->excl)
645         flags |= O_EXCL;
646     if (flagp->dsync)
647         flags |= O_SYNC;
648 
649     if ((fd = open(fnp, flags)) < 0) {
650         err = errno;
651         snprintf(ebuff, EBUFF_SZ, "%scould not open %s for sg "
652                  "reading", my_name, fnp);
653         perror(ebuff);
654         return -sg_convert_errno(err);
655     }
656     if (sg_prepare(fd, bs, bpt)) {
657         close(fd);
658         return -SG_LIB_FILE_ERROR;
659     }
660     return fd;
661 }
662 
663 static int
sg_out_open(const char * fnp,struct flags_t * flagp,int bs,int bpt)664 sg_out_open(const char * fnp, struct flags_t * flagp, int bs, int bpt)
665 {
666     int flags = O_RDWR;
667     int fd, err;
668     char ebuff[800];
669 
670     if (flagp->direct)
671         flags |= O_DIRECT;
672     if (flagp->excl)
673         flags |= O_EXCL;
674     if (flagp->dsync)
675         flags |= O_SYNC;
676 
677     if ((fd = open(fnp, flags)) < 0) {
678         err = errno;
679         snprintf(ebuff, EBUFF_SZ, "%scould not open %s for sg "
680                  "writing", my_name, fnp);
681         perror(ebuff);
682         return -sg_convert_errno(err);
683     }
684     if (sg_prepare(fd, bs, bpt)) {
685         close(fd);
686         return -SG_LIB_FILE_ERROR;
687     }
688     return fd;
689 }
690 
691 static void *
read_write_thread(void * v_tap)692 read_write_thread(void * v_tap)
693 {
694     struct thread_arg * tap = (struct thread_arg *)v_tap;
695     struct opts_t * clp = &my_opts;
696     Rq_elem rel;
697     Rq_elem * rep = &rel;
698     volatile bool stop_after_write, bb;
699     bool enforce_write_ordering;
700     int sz, c_addr;
701     int64_t out_blk, out_count;
702     int64_t seek_skip = tap->seek_skip;
703     int blocks, status;
704 
705     stop_after_write = false;
706     enforce_write_ordering = (FT_DEV_NULL != clp->out_type) &&
707                              (FT_SG != clp->out_type);
708     c_addr = clp->chkaddr;
709     memset(rep, 0, sizeof(*rep));
710     /* Following clp members are constant during lifetime of thread */
711     rep->bs = clp->bs;
712     if ((clp->num_threads > 1) && clp->mmap_active) {
713         /* sg devices need separate file descriptor */
714         if (clp->in_flags.mmap && (FT_SG == clp->in_type)) {
715             rep->infd = sg_in_open(infn, &clp->in_flags, rep->bs, clp->bpt);
716             if (rep->infd < 0) err_exit(-rep->infd, "error opening infn");
717         } else
718             rep->infd = clp->infd;
719         if (clp->out_flags.mmap && (FT_SG == clp->out_type)) {
720             rep->outfd = sg_out_open(outfn, &clp->out_flags, rep->bs,
721                                      clp->bpt);
722             if (rep->outfd < 0) err_exit(-rep->outfd, "error opening outfn");
723 
724         } else
725             rep->outfd = clp->outfd;
726     } else {
727         rep->infd = clp->infd;
728         rep->outfd = clp->outfd;
729     }
730     sz = clp->bpt * rep->bs;
731     rep->debug = clp->debug;
732     rep->cdbsz_in = clp->cdbsz_in;
733     rep->cdbsz_out = clp->cdbsz_out;
734     rep->in_flags = clp->in_flags;
735     rep->out_flags = clp->out_flags;
736     rep->use_no_dxfer = (FT_DEV_NULL == clp->out_type);
737     if (clp->mmap_active) {
738         int fd = clp->in_flags.mmap ? rep->infd : rep->outfd;
739 
740         status = sgp_mem_mmap(fd, sz, &rep->buffp);
741         if (status) err_exit(status, "sgp_mem_mmap() failed");
742     } else {
743         rep->buffp = sg_memalign(sz, 0 /* page align */, &rep->alloc_bp,
744                                  false);
745         if (NULL == rep->buffp)
746             err_exit(ENOMEM, "out of memory creating user buffers\n");
747     }
748 
749     while(1) {
750         if ((rep->in_stop) || (rep->in_err) || (rep->out_err))
751             break;
752         status = pthread_mutex_lock(&clp->inout_mutex);
753         if (0 != status) err_exit(status, "lock inout_mutex");
754 #ifdef HAVE_C11_ATOMICS
755         bb = atomic_load(&exit_threads);
756 #else
757         bb = exit_threads;
758 #endif
759         if (bb || (clp->in_count <= 0)) {
760             /* no more to do, exit loop then thread */
761             status = pthread_mutex_unlock(&clp->inout_mutex);
762             if (0 != status) err_exit(status, "unlock inout_mutex");
763             break;
764         }
765         blocks = (clp->in_count > clp->bpt) ? clp->bpt : clp->in_count;
766         rep->wr = false;
767         rep->blk = clp->in_blk;
768         rep->num_blks = blocks;
769         clp->in_blk += blocks;
770         clp->in_count -= blocks;
771         /* while we have this lock, find corresponding out_blk */
772         out_blk = rep->blk + seek_skip;
773         out_count = clp->out_count;
774         if (! enforce_write_ordering)
775             clp->out_blk += blocks;
776         clp->out_count -= blocks;
777         status = pthread_mutex_unlock(&clp->inout_mutex);
778         if (0 != status) err_exit(status, "unlock inout_mutex");
779 
780         pthread_cleanup_push(cleanup_in, (void *)clp);
781         if (FT_SG == clp->in_type)
782             sg_in_operation(clp, rep);
783         else
784             normal_in_operation(clp, rep, blocks);
785         if (c_addr && (rep->bs > 3)) {
786             int k, j, off, num;
787             uint32_t addr = (uint32_t)rep->blk;
788 
789             num = (1 == c_addr) ? 4 : (rep->bs - 3);
790             for (k = 0, off = 0; k < blocks; ++k, ++addr, off += rep->bs) {
791                 for (j = 0; j < num; j += 4) {
792                     if (addr != sg_get_unaligned_be32(rep->buffp + off + j))
793                         break;
794                 }
795                 if (j < num)
796                     break;
797             }
798             if (k < blocks) {
799                 pr2serr("%s: chkaddr failure at addr=0x%x\n", __func__, addr);
800                 rep->in_err = true;
801             }
802         }
803         pthread_cleanup_pop(0);
804         if (rep->in_err) {
805             status = pthread_mutex_lock(&clp->inout_mutex);
806             if (0 != status) err_exit(status, "lock inout_mutex");
807             /* write-side not done, so undo changes to out_blk + out_count */
808             if (! enforce_write_ordering)
809                 clp->out_blk -= blocks;
810             clp->out_count += blocks;
811             status = pthread_mutex_unlock(&clp->inout_mutex);
812             if (0 != status) err_exit(status, "unlock inout_mutex");
813             break;
814         }
815 
816         if (enforce_write_ordering) {
817             status = pthread_mutex_lock(&clp->inout_mutex);
818             if (0 != status) err_exit(status, "lock inout_mutex");
819 #ifdef HAVE_C11_ATOMICS
820             bb = atomic_load(&exit_threads);
821 #else
822             bb = exit_threads;
823 #endif
824             while ((! bb) && (out_blk != clp->out_blk)) {
825                 /* if write would be out of sequence then wait */
826                 pthread_cleanup_push(cleanup_out, (void *)clp);
827                 status = pthread_cond_wait(&clp->out_sync_cv,
828                                            &clp->inout_mutex);
829                 if (0 != status) err_exit(status, "cond out_sync_cv");
830                 pthread_cleanup_pop(0);
831             }
832             status = pthread_mutex_unlock(&clp->inout_mutex);
833             if (0 != status) err_exit(status, "unlock inout_mutex");
834         }
835 
836 #ifdef HAVE_C11_ATOMICS
837         bb = atomic_load(&exit_threads);
838 #else
839         bb = exit_threads;
840 #endif
841         if (bb || (out_count <= 0))
842             break;
843 
844         rep->wr = true;
845         rep->blk = out_blk;
846 
847         if (0 == rep->num_blks) {
848             break;      /* read nothing so leave loop */
849         }
850 
851         pthread_cleanup_push(cleanup_out, (void *)clp);
852         if (FT_SG == clp->out_type)
853             sg_out_operation(clp, rep, enforce_write_ordering);
854         else if (FT_DEV_NULL == clp->out_type) {
855             /* skip actual write operation */
856             clp->out_rem_count -= blocks;
857         }
858         else
859             normal_out_operation(clp, rep, blocks, enforce_write_ordering);
860         pthread_cleanup_pop(0);
861 
862         if (enforce_write_ordering)
863             pthread_cond_broadcast(&clp->out_sync_cv);
864     } /* end of while loop */
865 
866     if (rep->alloc_bp)
867         free(rep->alloc_bp);
868     if (rep->in_err || rep->out_err) {
869         stop_after_write = true;
870 #ifdef HAVE_C11_ATOMICS
871         if (! atomic_load(&exit_threads))
872             atomic_store(&exit_threads, true);
873 #else
874         if (! exit_threads)
875             exit_threads = true;
876 #endif
877     }
878     pthread_cond_broadcast(&clp->out_sync_cv);
879     return (stop_after_write || rep->in_stop) ? NULL : clp;
880 }
881 
882 static void
normal_in_operation(struct opts_t * clp,Rq_elem * rep,int blocks)883 normal_in_operation(struct opts_t * clp, Rq_elem * rep, int blocks)
884 {
885     int res, status;
886     char strerr_buff[STRERR_BUFF_LEN + 1];
887 
888     while (((res = read(rep->infd, rep->buffp, blocks * rep->bs)) < 0) &&
889            ((EINTR == errno) || (EAGAIN == errno)))
890         ;
891     if (res < 0) {
892         if (rep->in_flags.coe) {
893             memset(rep->buffp, 0, rep->num_blks * rep->bs);
894             pr2serr(">> substituted zeros for in blk=%" PRId64 " for %d "
895                     "bytes, %s\n", rep->blk,
896                     rep->num_blks * rep->bs,
897                     tsafe_strerror(errno, strerr_buff));
898             res = rep->num_blks * rep->bs;
899         }
900         else {
901             pr2serr("error in normal read, %s\n",
902                     tsafe_strerror(errno, strerr_buff));
903             rep->in_stop = true;
904             rep->in_err = true;
905             return;
906         }
907     }
908     status = pthread_mutex_lock(&clp->inout_mutex);
909     if (0 != status) err_exit(status, "lock inout_mutex");
910     if (res < blocks * rep->bs) {
911         int o_blocks = blocks;
912 
913         rep->in_stop = true;
914         blocks = res / rep->bs;
915         if ((res % rep->bs) > 0) {
916             blocks++;
917             clp->in_partial++;
918         }
919         /* Reverse out + re-apply blocks on clp */
920         clp->in_blk -= o_blocks;
921         clp->in_count += o_blocks;
922         rep->num_blks = blocks;
923         clp->in_blk += blocks;
924         clp->in_count -= blocks;
925     }
926     clp->in_rem_count -= blocks;
927     status = pthread_mutex_unlock(&clp->inout_mutex);
928     if (0 != status) err_exit(status, "lock inout_mutex");
929 }
930 
931 static void
normal_out_operation(struct opts_t * clp,Rq_elem * rep,int blocks,bool bump_out_blk)932 normal_out_operation(struct opts_t * clp, Rq_elem * rep, int blocks,
933                      bool bump_out_blk)
934 {
935     int res, status;
936     char strerr_buff[STRERR_BUFF_LEN + 1];
937 
938     while (((res = write(rep->outfd, rep->buffp, rep->num_blks * rep->bs))
939             < 0) && ((EINTR == errno) || (EAGAIN == errno)))
940         ;
941     if (res < 0) {
942         if (rep->out_flags.coe) {
943             pr2serr(">> ignored error for out blk=%" PRId64 " for %d bytes, "
944                     "%s\n", rep->blk, rep->num_blks * rep->bs,
945                     tsafe_strerror(errno, strerr_buff));
946             res = rep->num_blks * rep->bs;
947         }
948         else {
949             pr2serr("error normal write, %s\n",
950                     tsafe_strerror(errno, strerr_buff));
951             rep->out_err = true;
952             return;
953         }
954     }
955     status = pthread_mutex_lock(&clp->inout_mutex);
956     if (0 != status) err_exit(status, "lock inout_mutex");
957     if (res < blocks * rep->bs) {
958         blocks = res / rep->bs;
959         if ((res % rep->bs) > 0) {
960             blocks++;
961             clp->out_partial++;
962         }
963         rep->num_blks = blocks;
964     }
965     clp->out_rem_count -= blocks;
966     if (bump_out_blk)
967         clp->out_blk += blocks;
968     status = pthread_mutex_unlock(&clp->inout_mutex);
969     if (0 != status) err_exit(status, "lock inout_mutex");
970 }
971 
972 static int
sg_build_scsi_cdb(uint8_t * cdbp,int cdb_sz,unsigned int blocks,int64_t start_block,bool write_true,bool fua,bool dpo)973 sg_build_scsi_cdb(uint8_t * cdbp, int cdb_sz, unsigned int blocks,
974                   int64_t start_block, bool write_true, bool fua, bool dpo)
975 {
976     int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88};
977     int wr_opcode[] = {0xa, 0x2a, 0xaa, 0x8a};
978     int sz_ind;
979 
980     memset(cdbp, 0, cdb_sz);
981     if (dpo)
982         cdbp[1] |= 0x10;
983     if (fua)
984         cdbp[1] |= 0x8;
985     switch (cdb_sz) {
986     case 6:
987         sz_ind = 0;
988         cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
989                                                rd_opcode[sz_ind]);
990         sg_put_unaligned_be24(0x1fffff & start_block, cdbp + 1);
991         cdbp[4] = (256 == blocks) ? 0 : (uint8_t)blocks;
992         if (blocks > 256) {
993             pr2serr("%sfor 6 byte commands, maximum number of blocks is "
994                     "256\n", my_name);
995             return 1;
996         }
997         if ((start_block + blocks - 1) & (~0x1fffff)) {
998             pr2serr("%sfor 6 byte commands, can't address blocks beyond "
999                     "%d\n", my_name, 0x1fffff);
1000             return 1;
1001         }
1002         if (dpo || fua) {
1003             pr2serr("%sfor 6 byte commands, neither dpo nor fua bits "
1004                     "supported\n", my_name);
1005             return 1;
1006         }
1007         break;
1008     case 10:
1009         sz_ind = 1;
1010         cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
1011                                                rd_opcode[sz_ind]);
1012         sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2);
1013         sg_put_unaligned_be16((uint16_t)blocks, cdbp + 7);
1014         if (blocks & (~0xffff)) {
1015             pr2serr("%sfor 10 byte commands, maximum number of blocks is "
1016                     "%d\n", my_name, 0xffff);
1017             return 1;
1018         }
1019         break;
1020     case 12:
1021         sz_ind = 2;
1022         cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
1023                                                rd_opcode[sz_ind]);
1024         sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2);
1025         sg_put_unaligned_be32((uint32_t)blocks, cdbp + 6);
1026         break;
1027     case 16:
1028         sz_ind = 3;
1029         cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
1030                                                rd_opcode[sz_ind]);
1031         sg_put_unaligned_be64((uint64_t)start_block, cdbp + 2);
1032         sg_put_unaligned_be32((uint32_t)blocks, cdbp + 10);
1033         break;
1034     default:
1035         pr2serr("%sexpected cdb size of 6, 10, 12, or 16 but got %d\n",
1036                 my_name, cdb_sz);
1037         return 1;
1038     }
1039     return 0;
1040 }
1041 
1042 static void
sg_in_operation(struct opts_t * clp,Rq_elem * rep)1043 sg_in_operation(struct opts_t * clp, Rq_elem * rep)
1044 {
1045     int res;
1046     int status;
1047 
1048     while (1) {
1049         res = sg_start_io(rep);
1050         if (1 == res)
1051             err_exit(ENOMEM, "sg starting in command");
1052         else if (res < 0) {
1053             pr2serr("%sinputting to sg failed, blk=%" PRId64 "\n", my_name,
1054                     rep->blk);
1055             rep->in_stop = true;
1056             rep->in_err = true;
1057             return;
1058         }
1059         res = sg_finish_io(rep->wr, rep, &clp->inout_mutex);
1060         switch (res) {
1061         case SG_LIB_CAT_ABORTED_COMMAND:
1062         case SG_LIB_CAT_UNIT_ATTENTION:
1063             /* try again with same addr, count info */
1064             /* now re-acquire in mutex for balance */
1065             /* N.B. This re-read could now be out of read sequence */
1066             break;
1067         case SG_LIB_CAT_MEDIUM_HARD:
1068             if (0 == rep->in_flags.coe) {
1069                 pr2serr("error finishing sg in command (medium)\n");
1070                 if (exit_status <= 0)
1071                     exit_status = res;
1072                 rep->in_stop = true;
1073                 rep->in_err = true;
1074                 return;
1075             } else {
1076                 memset(rep->buffp, 0, rep->num_blks * rep->bs);
1077                 pr2serr(">> substituted zeros for in blk=%" PRId64 " for %d "
1078                         "bytes\n", rep->blk, rep->num_blks * rep->bs);
1079             }
1080 #if defined(__GNUC__)
1081 #if (__GNUC__ >= 7)
1082             __attribute__((fallthrough));
1083             /* FALL THROUGH */
1084 #endif
1085 #endif
1086         case 0:
1087             status = pthread_mutex_lock(&clp->inout_mutex);
1088             if (0 != status) err_exit(status, "lock inout_mutex");
1089             if (rep->dio_incomplete_count || rep->resid) {
1090                 clp->dio_incomplete_count += rep->dio_incomplete_count;
1091                 clp->sum_of_resids += rep->resid;
1092             }
1093             clp->in_rem_count -= rep->num_blks;
1094             status = pthread_mutex_unlock(&clp->inout_mutex);
1095             if (0 != status) err_exit(status, "unlock inout_mutex");
1096             return;
1097         case SG_LIB_CAT_ILLEGAL_REQ:
1098             if (clp->debug)
1099                 sg_print_command_len(rep->cdb, rep->cdbsz_in);
1100             /* FALL THROUGH */
1101         default:
1102             pr2serr("error finishing sg in command (%d)\n", res);
1103             if (exit_status <= 0)
1104                 exit_status = res;
1105             rep->in_stop = true;
1106             rep->in_err = true;
1107             return;
1108         }
1109     }   /* end of while loop */
1110 }
1111 
1112 static void
sg_out_operation(struct opts_t * clp,Rq_elem * rep,bool bump_out_blk)1113 sg_out_operation(struct opts_t * clp, Rq_elem * rep, bool bump_out_blk)
1114 {
1115     int res;
1116     int status;
1117 
1118     while (1) {
1119         res = sg_start_io(rep);
1120         if (1 == res)
1121             err_exit(ENOMEM, "sg starting out command");
1122         else if (res < 0) {
1123             pr2serr("%soutputting from sg failed, blk=%" PRId64 "\n",
1124                     my_name, rep->blk);
1125             rep->out_err = true;
1126             return;
1127         }
1128         res = sg_finish_io(rep->wr, rep, &clp->inout_mutex);
1129         switch (res) {
1130         case SG_LIB_CAT_ABORTED_COMMAND:
1131         case SG_LIB_CAT_UNIT_ATTENTION:
1132             /* try again with same addr, count info */
1133             /* now re-acquire out mutex for balance */
1134             /* N.B. This re-write could now be out of write sequence */
1135             break;
1136         case SG_LIB_CAT_MEDIUM_HARD:
1137             if (0 == rep->out_flags.coe) {
1138                 pr2serr("error finishing sg out command (medium)\n");
1139                 if (exit_status <= 0)
1140                     exit_status = res;
1141                 rep->out_err = true;
1142                 return;
1143             } else
1144                 pr2serr(">> ignored error for out blk=%" PRId64 " for %d "
1145                         "bytes\n", rep->blk, rep->num_blks * rep->bs);
1146 #if defined(__GNUC__)
1147 #if (__GNUC__ >= 7)
1148             __attribute__((fallthrough));
1149             /* FALL THROUGH */
1150 #endif
1151 #endif
1152         case 0:
1153             status = pthread_mutex_lock(&clp->inout_mutex);
1154             if (0 != status) err_exit(status, "lock inout_mutex");
1155             if (rep->dio_incomplete_count || rep->resid) {
1156                 clp->dio_incomplete_count += rep->dio_incomplete_count;
1157                 clp->sum_of_resids += rep->resid;
1158             }
1159             clp->out_rem_count -= rep->num_blks;
1160             if (bump_out_blk)
1161                 clp->out_blk += rep->num_blks;
1162             status = pthread_mutex_unlock(&clp->inout_mutex);
1163             if (0 != status) err_exit(status, "unlock inout_mutex");
1164             return;
1165         case SG_LIB_CAT_ILLEGAL_REQ:
1166             if (clp->debug)
1167                 sg_print_command_len(rep->cdb, rep->cdbsz_out);
1168             /* FALL THROUGH */
1169         default:
1170             rep->out_err = true;
1171             pr2serr("error finishing sg out command (%d)\n", res);
1172             if (exit_status <= 0)
1173                 exit_status = res;
1174             return;
1175         }
1176     }
1177 }
1178 
1179 static int
sg_start_io(Rq_elem * rep)1180 sg_start_io(Rq_elem * rep)
1181 {
1182     struct sg_io_hdr * hp = &rep->io_hdr;
1183     bool fua = rep->wr ? rep->out_flags.fua : rep->in_flags.fua;
1184     bool dpo = rep->wr ? rep->out_flags.dpo : rep->in_flags.dpo;
1185     bool dio = rep->wr ? rep->out_flags.dio : rep->in_flags.dio;
1186     bool mmap = rep->wr ? rep->out_flags.mmap : rep->in_flags.mmap;
1187     bool no_dxfer = rep->wr ? false : rep->use_no_dxfer;
1188     int cdbsz = rep->wr ? rep->cdbsz_out : rep->cdbsz_in;
1189     int res;
1190 
1191     if (sg_build_scsi_cdb(rep->cdb, cdbsz, rep->num_blks, rep->blk,
1192                           rep->wr, fua, dpo)) {
1193         pr2serr("%sbad cdb build, start_blk=%" PRId64 ", blocks=%d\n",
1194                 my_name, rep->blk, rep->num_blks);
1195         return -1;
1196     }
1197     memset(hp, 0, sizeof(struct sg_io_hdr));
1198     hp->interface_id = 'S';
1199     hp->cmd_len = cdbsz;
1200     hp->cmdp = rep->cdb;
1201     hp->dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
1202     hp->dxfer_len = rep->bs * rep->num_blks;
1203     hp->dxferp = mmap ? NULL : rep->buffp;
1204     hp->mx_sb_len = sizeof(rep->sb);
1205     hp->sbp = rep->sb;
1206     hp->timeout = DEF_TIMEOUT;
1207     hp->usr_ptr = rep;
1208     rep->pack_id = GET_NEXT_PACK_ID(1);
1209     hp->pack_id = (int)rep->pack_id;
1210     if (dio)
1211         hp->flags |= SG_FLAG_DIRECT_IO;
1212     if (mmap)
1213         hp->flags |= SG_FLAG_MMAP_IO;
1214     if (no_dxfer)
1215         hp->flags |= SG_FLAG_NO_DXFER;
1216     if (rep->debug > 8) {
1217         pr2serr("%s: SCSI %s, blk=%" PRId64 " num_blks=%d\n", __func__,
1218                 rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks);
1219         sg_print_command(hp->cmdp);
1220     }
1221 
1222     while (((res = write(rep->wr ? rep->outfd : rep->infd, hp,
1223                          sizeof(struct sg_io_hdr))) < 0) &&
1224            ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno))) {
1225 #ifdef HAVE_C11_ATOMICS
1226         if (EINTR == errno)
1227             atomic_fetch_add(&num_eintr, 1);
1228         else if (EAGAIN == errno)
1229             atomic_fetch_add(&num_eagain, 1);
1230         else
1231             atomic_fetch_add(&num_ebusy, 1);
1232 #endif
1233     }
1234     if (res < 0) {
1235         if (ENOMEM == errno)
1236             return 1;
1237         perror("starting io on sg device, error");
1238         return -1;
1239     }
1240     return 0;
1241 }
1242 
1243 /* 0 -> successful, SG_LIB_CAT_UNIT_ATTENTION or SG_LIB_CAT_ABORTED_COMMAND
1244    -> try again, SG_LIB_CAT_NOT_READY, SG_LIB_CAT_MEDIUM_HARD,
1245    -1 other errors */
1246 static int
sg_finish_io(bool wr,Rq_elem * rep,pthread_mutex_t * a_mutp)1247 sg_finish_io(bool wr, Rq_elem * rep, pthread_mutex_t * a_mutp)
1248 {
1249     int res, status;
1250     struct sg_io_hdr io_hdr;
1251     struct sg_io_hdr * hp;
1252 #if 0
1253     static int testing = 0;     /* thread dubious! */
1254 #endif
1255 
1256     memset(&io_hdr, 0 , sizeof(struct sg_io_hdr));
1257     /* FORCE_PACK_ID active set only read packet with matching pack_id */
1258     io_hdr.interface_id = 'S';
1259     io_hdr.dxfer_direction = wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
1260     io_hdr.pack_id = (int)rep->pack_id;
1261 
1262     while (((res = read(wr ? rep->outfd : rep->infd, &io_hdr,
1263                         sizeof(struct sg_io_hdr))) < 0) &&
1264            ((EINTR == errno) || (EAGAIN == errno) || (EBUSY == errno)))
1265         ;
1266     if (res < 0) {
1267         perror("finishing io on sg device, error");
1268         return -1;
1269     }
1270     if (rep != (Rq_elem *)io_hdr.usr_ptr)
1271         err_exit(0, "sg_finish_io: bad usr_ptr, request-response mismatch\n");
1272     memcpy(&rep->io_hdr, &io_hdr, sizeof(struct sg_io_hdr));
1273     hp = &rep->io_hdr;
1274 
1275     res = sg_err_category3(hp);
1276     switch (res) {
1277         case SG_LIB_CAT_CLEAN:
1278             break;
1279         case SG_LIB_CAT_RECOVERED:
1280             sg_chk_n_print3((wr ? "writing continuing":
1281                                        "reading continuing"), hp, false);
1282             break;
1283         case SG_LIB_CAT_ABORTED_COMMAND:
1284         case SG_LIB_CAT_UNIT_ATTENTION:
1285             if (rep->debug)
1286                 sg_chk_n_print3((wr ? "writing": "reading"), hp, false);
1287             return res;
1288         case SG_LIB_CAT_NOT_READY:
1289         default:
1290             rep->out_err = false;
1291             if (rep->debug) {
1292                 char ebuff[EBUFF_SZ];
1293 
1294                 snprintf(ebuff, EBUFF_SZ, "%s blk=%" PRId64,
1295                          wr ? "writing": "reading", rep->blk);
1296                 status = pthread_mutex_lock(a_mutp);
1297                 if (0 != status) err_exit(status, "lock inout_mutex");
1298                 sg_chk_n_print3(ebuff, hp, false);
1299                 status = pthread_mutex_unlock(a_mutp);
1300                 if (0 != status) err_exit(status, "unlock inout_mutex");
1301             }
1302             return res;
1303     }
1304 #if 0
1305     if (0 == (++testing % 100)) return -1;
1306 #endif
1307     if ((wr ? rep->out_flags.dio : rep->in_flags.dio) &&
1308         ((hp->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
1309         rep->dio_incomplete_count = 1; /* count dios done as indirect IO */
1310     else
1311         rep->dio_incomplete_count = 0;
1312     rep->resid = hp->resid;
1313     if (rep->debug > 8)
1314         pr2serr("%s: completed %s\n", __func__, wr ? "WRITE" : "READ");
1315     return 0;
1316 }
1317 
1318 static int
process_flags(const char * arg,struct flags_t * fp)1319 process_flags(const char * arg, struct flags_t * fp)
1320 {
1321     char buff[256];
1322     char * cp;
1323     char * np;
1324 
1325     strncpy(buff, arg, sizeof(buff));
1326     buff[sizeof(buff) - 1] = '\0';
1327     if ('\0' == buff[0]) {
1328         pr2serr("no flag found\n");
1329         return 1;
1330     }
1331     cp = buff;
1332     do {
1333         np = strchr(cp, ',');
1334         if (np)
1335             *np++ = '\0';
1336         if (0 == strcmp(cp, "append"))
1337             fp->append = true;
1338         else if (0 == strcmp(cp, "coe"))
1339             fp->coe = true;
1340         else if (0 == strcmp(cp, "dio"))
1341             fp->dio = true;
1342         else if (0 == strcmp(cp, "direct"))
1343             fp->direct = true;
1344         else if (0 == strcmp(cp, "dpo"))
1345             fp->dpo = true;
1346         else if (0 == strcmp(cp, "dsync"))
1347             fp->dsync = true;
1348         else if (0 == strcmp(cp, "excl"))
1349             fp->excl = true;
1350         else if (0 == strcmp(cp, "fua"))
1351             fp->fua = true;
1352         else if (0 == strcmp(cp, "mmap"))
1353             fp->mmap = true;
1354         else if (0 == strcmp(cp, "null"))
1355             ;
1356         else {
1357             pr2serr("unrecognised flag: %s\n", cp);
1358             return 1;
1359         }
1360         cp = np;
1361     } while (cp);
1362     return 0;
1363 }
1364 
1365 /* Returns the number of times 'ch' is found in string 's' given the
1366  * string's length. */
1367 static int
num_chs_in_str(const char * s,int slen,int ch)1368 num_chs_in_str(const char * s, int slen, int ch)
1369 {
1370     int res = 0;
1371 
1372     while (--slen >= 0) {
1373         if (ch == s[slen])
1374             ++res;
1375     }
1376     return res;
1377 }
1378 
1379 
1380 int
main(int argc,char * argv[])1381 main(int argc, char * argv[])
1382 {
1383     bool verbose_given = false;
1384     bool version_given = false;
1385     int64_t skip = 0;
1386     int64_t seek = 0;
1387     int ibs = 0;
1388     int obs = 0;
1389     int bpt_given = 0;
1390     int cdbsz_given = 0;
1391     char str[STR_SZ];
1392     char * key;
1393     char * buf;
1394     int res, k, err, keylen;
1395     int64_t in_num_sect = 0;
1396     int64_t out_num_sect = 0;
1397     int64_t seek_skip;
1398     int in_sect_sz, out_sect_sz, status, n, flags;
1399     void * vp;
1400     struct opts_t * clp = &my_opts;
1401     char ebuff[EBUFF_SZ];
1402 #if SG_LIB_ANDROID
1403     struct sigaction actions;
1404 
1405     memset(&actions, 0, sizeof(actions));
1406     sigemptyset(&actions.sa_mask);
1407     actions.sa_flags = 0;
1408     actions.sa_handler = thread_exit_handler;
1409     sigaction(SIGUSR1, &actions, NULL);
1410 #endif
1411     memset(clp, 0, sizeof(*clp));
1412     clp->num_threads = DEF_NUM_THREADS;
1413     clp->bpt = DEF_BLOCKS_PER_TRANSFER;
1414     clp->in_type = FT_OTHER;
1415     clp->out_type = FT_OTHER;
1416     clp->cdbsz_in = DEF_SCSI_CDBSZ;
1417     clp->cdbsz_out = DEF_SCSI_CDBSZ;
1418     infn[0] = '\0';
1419     outfn[0] = '\0';
1420 
1421     for (k = 1; k < argc; k++) {
1422         if (argv[k]) {
1423             strncpy(str, argv[k], STR_SZ);
1424             str[STR_SZ - 1] = '\0';
1425         }
1426         else
1427             continue;
1428         for (key = str, buf = key; *buf && *buf != '=';)
1429             buf++;
1430         if (*buf)
1431             *buf++ = '\0';
1432         keylen = strlen(key);
1433         if (0 == strcmp(key,"bpt")) {
1434             clp->bpt = sg_get_num(buf);
1435             if ((clp->bpt < 0) || (clp->bpt > MAX_BPT_VALUE)) {
1436                 pr2serr("%sbad argument to 'bpt='\n", my_name);
1437                 return SG_LIB_SYNTAX_ERROR;
1438             }
1439             bpt_given = 1;
1440         } else if (0 == strcmp(key,"bs")) {
1441             clp->bs = sg_get_num(buf);
1442             if ((clp->bs < 0) || (clp->bs > MAX_BPT_VALUE)) {
1443                 pr2serr("%sbad argument to 'bs='\n", my_name);
1444                 return SG_LIB_SYNTAX_ERROR;
1445             }
1446         } else if (0 == strcmp(key,"cdbsz")) {
1447             clp->cdbsz_in = sg_get_num(buf);
1448             if ((clp->cdbsz_in < 6) || (clp->cdbsz_in > 32)) {
1449                 pr2serr("%s'cdbsz' expects 6, 10, 12, 16 or 32\n", my_name);
1450                 return SG_LIB_SYNTAX_ERROR;
1451             }
1452             clp->cdbsz_out = clp->cdbsz_in;
1453             cdbsz_given = 1;
1454         } else if (0 == strcmp(key,"coe")) {
1455             clp->in_flags.coe = !! sg_get_num(buf);
1456             clp->out_flags.coe = clp->in_flags.coe;
1457         } else if (0 == strcmp(key,"count")) {
1458             if (0 != strcmp("-1", buf)) {
1459                 dd_count = sg_get_llnum(buf);
1460                 if ((dd_count < 0) || (dd_count > MAX_COUNT_SKIP_SEEK)) {
1461                     pr2serr("%sbad argument to 'count='\n", my_name);
1462                     return SG_LIB_SYNTAX_ERROR;
1463                 }
1464             }   /* treat 'count=-1' as calculate count (same as not given) */
1465         } else if ((0 == strncmp(key,"deb", 3)) ||
1466                    (0 == strncmp(key,"verb", 4)))
1467             clp->debug = sg_get_num(buf);
1468         else if (0 == strcmp(key,"dio")) {
1469             clp->in_flags.dio = !! sg_get_num(buf);
1470             clp->out_flags.dio = clp->in_flags.dio;
1471         } else if (0 == strcmp(key,"fua")) {
1472             n = sg_get_num(buf);
1473             if (n & 1)
1474                 clp->out_flags.fua = true;
1475             if (n & 2)
1476                 clp->in_flags.fua = true;
1477         } else if (0 == strcmp(key,"ibs")) {
1478             ibs = sg_get_num(buf);
1479             if ((ibs < 0) || (ibs > MAX_BPT_VALUE)) {
1480                 pr2serr("%sbad argument to 'ibs='\n", my_name);
1481                 return SG_LIB_SYNTAX_ERROR;
1482             }
1483         } else if (strcmp(key,"if") == 0) {
1484             if ('\0' != infn[0]) {
1485                 pr2serr("Second 'if=' argument??\n");
1486                 return SG_LIB_SYNTAX_ERROR;
1487             } else {
1488                 memcpy(infn, buf, INOUTF_SZ);
1489                 infn[INOUTF_SZ - 1] = '\0';
1490             }
1491         } else if (0 == strcmp(key, "iflag")) {
1492             if (process_flags(buf, &clp->in_flags)) {
1493                 pr2serr("%sbad argument to 'iflag='\n", my_name);
1494                 return SG_LIB_SYNTAX_ERROR;
1495             }
1496         } else if (0 == strcmp(key,"obs")) {
1497             obs = sg_get_num(buf);
1498             if ((obs < 0) || (obs > MAX_BPT_VALUE)) {
1499                 pr2serr("%sbad argument to 'obs='\n", my_name);
1500                 return SG_LIB_SYNTAX_ERROR;
1501             }
1502         } else if (strcmp(key,"of") == 0) {
1503             if ('\0' != outfn[0]) {
1504                 pr2serr("Second 'of=' argument??\n");
1505                 return SG_LIB_SYNTAX_ERROR;
1506             } else {
1507                 memcpy(outfn, buf, INOUTF_SZ);
1508                 outfn[INOUTF_SZ - 1] = '\0';
1509             }
1510         } else if (0 == strcmp(key, "oflag")) {
1511             if (process_flags(buf, &clp->out_flags)) {
1512                 pr2serr("%sbad argument to 'oflag='\n", my_name);
1513                 return SG_LIB_SYNTAX_ERROR;
1514             }
1515         } else if (0 == strcmp(key,"seek")) {
1516             seek = sg_get_llnum(buf);
1517             if ((seek < 0) || (seek > MAX_COUNT_SKIP_SEEK)) {
1518                 pr2serr("%sbad argument to 'seek='\n", my_name);
1519                 return SG_LIB_SYNTAX_ERROR;
1520             }
1521         } else if (0 == strcmp(key,"skip")) {
1522             skip = sg_get_llnum(buf);
1523             if ((skip < 0) || (skip > MAX_COUNT_SKIP_SEEK)) {
1524                 pr2serr("%sbad argument to 'skip='\n", my_name);
1525                 return SG_LIB_SYNTAX_ERROR;
1526             }
1527         } else if (0 == strcmp(key,"sync"))
1528             do_sync = !! sg_get_num(buf);
1529         else if (0 == strcmp(key,"thr"))
1530             clp->num_threads = sg_get_num(buf);
1531         else if (0 == strcmp(key,"time"))
1532             do_time = !! sg_get_num(buf);
1533         else if ((keylen > 1) && ('-' == key[0]) && ('-' != key[1])) {
1534             res = 0;
1535             n = num_chs_in_str(key + 1, keylen - 1, 'c');
1536             clp->chkaddr += n;
1537             res += n;
1538             n = num_chs_in_str(key + 1, keylen - 1, 'd');
1539             clp->dry_run += n;
1540             res += n;
1541             n = num_chs_in_str(key + 1, keylen - 1, 'h');
1542             if (n > 0) {
1543                 usage();
1544                 return 0;
1545             }
1546             n = num_chs_in_str(key + 1, keylen - 1, 'p');
1547             clp->progress += n;
1548             res += n;
1549             n = num_chs_in_str(key + 1, keylen - 1, 'v');
1550             if (n > 0)
1551                 verbose_given = true;
1552             clp->debug += n;   /* -v  ---> --verbose */
1553             res += n;
1554             n = num_chs_in_str(key + 1, keylen - 1, 'V');
1555             if (n > 0)
1556                 version_given = true;
1557             res += n;
1558 
1559             if (res < (keylen - 1)) {
1560                 pr2serr("Unrecognised short option in '%s', try '--help'\n",
1561                         key);
1562                 return SG_LIB_SYNTAX_ERROR;
1563             }
1564         } else if (0 == strncmp(key, "--chkaddr", 9))
1565             ++clp->chkaddr;
1566         else if ((0 == strncmp(key, "--dry-run", 9)) ||
1567                    (0 == strncmp(key, "--dry_run", 9)))
1568             ++clp->dry_run;
1569         else if ((0 == strncmp(key, "--help", 6)) ||
1570                    (0 == strcmp(key, "-?"))) {
1571             usage();
1572             return 0;
1573         } else if (0 == strncmp(key, "--prog", 6))
1574             ++clp->progress;
1575         else if (0 == strncmp(key, "--verb", 6)) {
1576             verbose_given = true;
1577             ++clp->debug;      /* --verbose */
1578         } else if (0 == strncmp(key, "--vers", 6))
1579             version_given = true;
1580         else {
1581             pr2serr("Unrecognized option '%s'\n", key);
1582             pr2serr("For more information use '--help'\n");
1583             return SG_LIB_SYNTAX_ERROR;
1584         }
1585     }
1586 
1587 #ifdef DEBUG
1588     pr2serr("In DEBUG mode, ");
1589     if (verbose_given && version_given) {
1590         pr2serr("but override: '-vV' given, zero verbose and continue\n");
1591         verbose_given = false;
1592         version_given = false;
1593         clp->debug = 0;
1594     } else if (! verbose_given) {
1595         pr2serr("set '-vv'\n");
1596         clp->debug = 2;
1597     } else
1598         pr2serr("keep verbose=%d\n", clp->debug);
1599 #else
1600     if (verbose_given && version_given)
1601         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
1602 #endif
1603     if (version_given) {
1604         pr2serr("%s%s\n", my_name, version_str);
1605         return 0;
1606     }
1607 
1608     if (clp->bs <= 0) {
1609         clp->bs = DEF_BLOCK_SIZE;
1610         pr2serr("Assume default 'bs' ((logical) block size) of %d bytes\n",
1611                 clp->bs);
1612     }
1613     if ((ibs && (ibs != clp->bs)) || (obs && (obs != clp->bs))) {
1614         pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n");
1615         usage();
1616         return SG_LIB_SYNTAX_ERROR;
1617     }
1618     if ((skip < 0) || (seek < 0)) {
1619         pr2serr("skip and seek cannot be negative\n");
1620         return SG_LIB_SYNTAX_ERROR;
1621     }
1622     if (clp->out_flags.append && (seek > 0)) {
1623         pr2serr("Can't use both append and seek switches\n");
1624         return SG_LIB_SYNTAX_ERROR;
1625     }
1626     if ((clp->bpt < 1) || (clp->bpt > MAX_BPT_VALUE)) {
1627         pr2serr("bpt must be > 0 and <= %d\n", MAX_BPT_VALUE);
1628         return SG_LIB_SYNTAX_ERROR;
1629     }
1630     if (clp->in_flags.mmap && clp->out_flags.mmap) {
1631         pr2serr("can only use mmap flag in iflag= or oflag=, not both\n");
1632         return SG_LIB_SYNTAX_ERROR;
1633     } else if (clp->in_flags.mmap || clp->out_flags.mmap)
1634         clp->mmap_active = true;
1635     /* defaulting transfer size to 128*2048 for CD/DVDs is too large
1636        for the block layer in lk 2.6 and results in an EIO on the
1637        SG_IO ioctl. So reduce it in that case. */
1638     if ((clp->bs >= 2048) && (0 == bpt_given))
1639         clp->bpt = DEF_BLOCKS_PER_2048TRANSFER;
1640     if ((clp->num_threads < 1) || (clp->num_threads > MAX_NUM_THREADS)) {
1641         pr2serr("too few or too many threads requested\n");
1642         usage();
1643         return SG_LIB_SYNTAX_ERROR;
1644     }
1645     if (clp->debug > 2)
1646         pr2serr("%sif=%s skip=%" PRId64 " of=%s seek=%" PRId64 " count=%"
1647                 PRId64 "\n", my_name, infn, skip, outfn, seek, dd_count);
1648 
1649     install_handler(SIGINT, interrupt_handler);
1650     install_handler(SIGQUIT, interrupt_handler);
1651     install_handler(SIGPIPE, interrupt_handler);
1652     install_handler(SIGUSR1, siginfo_handler);
1653 
1654     clp->infd = STDIN_FILENO;
1655     clp->outfd = STDOUT_FILENO;
1656     if (infn[0] && ('-' != infn[0])) {
1657         clp->in_type = dd_filetype(infn);
1658 
1659         if (FT_ERROR == clp->in_type) {
1660             pr2serr("%sunable to access %s\n", my_name, infn);
1661             return SG_LIB_FILE_ERROR;
1662         } else if (FT_ST == clp->in_type) {
1663             pr2serr("%sunable to use scsi tape device %s\n", my_name, infn);
1664             return SG_LIB_FILE_ERROR;
1665         } else if (FT_SG == clp->in_type) {
1666             clp->infd = sg_in_open(infn, &clp->in_flags, clp->bs, clp->bpt);
1667             if (clp->infd < 0)
1668                 return -clp->infd;
1669         }
1670         else {
1671             flags = O_RDONLY;
1672             if (clp->in_flags.direct)
1673                 flags |= O_DIRECT;
1674             if (clp->in_flags.excl)
1675                 flags |= O_EXCL;
1676             if (clp->in_flags.dsync)
1677                 flags |= O_SYNC;
1678 
1679             if ((clp->infd = open(infn, flags)) < 0) {
1680                 err = errno;
1681                 snprintf(ebuff, EBUFF_SZ, "%scould not open %s for reading",
1682                          my_name, infn);
1683                 perror(ebuff);
1684                 return sg_convert_errno(err);
1685             }
1686             else if (skip > 0) {
1687                 off64_t offset = skip;
1688 
1689                 offset *= clp->bs;       /* could exceed 32 bits here! */
1690                 if (lseek64(clp->infd, offset, SEEK_SET) < 0) {
1691                     err = errno;
1692                     snprintf(ebuff, EBUFF_SZ, "%scouldn't skip to required "
1693                              "position on %s", my_name, infn);
1694                     perror(ebuff);
1695                     return sg_convert_errno(err);
1696                 }
1697             }
1698         }
1699     }
1700     if (outfn[0] && ('-' != outfn[0])) {
1701         clp->out_type = dd_filetype(outfn);
1702 
1703         if (FT_ST == clp->out_type) {
1704             pr2serr("%sunable to use scsi tape device %s\n", my_name, outfn);
1705             return SG_LIB_FILE_ERROR;
1706         } else if (FT_SG == clp->out_type) {
1707             clp->outfd = sg_out_open(outfn, &clp->out_flags, clp->bs,
1708                                      clp->bpt);
1709             if (clp->outfd < 0)
1710                 return -clp->outfd;
1711         } else if (FT_DEV_NULL == clp->out_type)
1712             clp->outfd = -1; /* don't bother opening */
1713         else {
1714             if (FT_RAW != clp->out_type) {
1715                 flags = O_WRONLY | O_CREAT;
1716                 if (clp->out_flags.direct)
1717                     flags |= O_DIRECT;
1718                 if (clp->out_flags.excl)
1719                     flags |= O_EXCL;
1720                 if (clp->out_flags.dsync)
1721                     flags |= O_SYNC;
1722                 if (clp->out_flags.append)
1723                     flags |= O_APPEND;
1724 
1725                 if ((clp->outfd = open(outfn, flags, 0666)) < 0) {
1726                     err = errno;
1727                     snprintf(ebuff, EBUFF_SZ, "%scould not open %s for "
1728                              "writing", my_name, outfn);
1729                     perror(ebuff);
1730                     return sg_convert_errno(err);
1731                 }
1732             }
1733             else {      /* raw output file */
1734                 if ((clp->outfd = open(outfn, O_WRONLY)) < 0) {
1735                     err = errno;
1736                     snprintf(ebuff, EBUFF_SZ, "%scould not open %s for raw "
1737                              "writing", my_name, outfn);
1738                     perror(ebuff);
1739                     return sg_convert_errno(err);
1740                 }
1741             }
1742             if (seek > 0) {
1743                 off64_t offset = seek;
1744 
1745                 offset *= clp->bs;       /* could exceed 32 bits here! */
1746                 if (lseek64(clp->outfd, offset, SEEK_SET) < 0) {
1747                     err = errno;
1748                     snprintf(ebuff, EBUFF_SZ, "%scouldn't seek to required "
1749                              "position on %s", my_name, outfn);
1750                     perror(ebuff);
1751                     return sg_convert_errno(err);
1752                 }
1753             }
1754         }
1755     }
1756     if ((STDIN_FILENO == clp->infd) && (STDOUT_FILENO == clp->outfd)) {
1757         pr2serr("Won't default both IFILE to stdin _and_ OFILE to stdout\n");
1758         pr2serr("For more information use '--help'\n");
1759         return SG_LIB_SYNTAX_ERROR;
1760     }
1761     if (dd_count < 0) {
1762         in_num_sect = -1;
1763         if (FT_SG == clp->in_type) {
1764             res = scsi_read_capacity(clp->infd, &in_num_sect, &in_sect_sz);
1765             if (2 == res) {
1766                 pr2serr("Unit attention, media changed(in), continuing\n");
1767                 res = scsi_read_capacity(clp->infd, &in_num_sect,
1768                                          &in_sect_sz);
1769             }
1770             if (0 != res) {
1771                 if (res == SG_LIB_CAT_INVALID_OP)
1772                     pr2serr("read capacity not supported on %s\n", infn);
1773                 else if (res == SG_LIB_CAT_NOT_READY)
1774                     pr2serr("read capacity failed, %s not ready\n", infn);
1775                 else
1776                     pr2serr("Unable to read capacity on %s\n", infn);
1777                 in_num_sect = -1;
1778             }
1779         } else if (FT_BLOCK == clp->in_type) {
1780             if (0 != read_blkdev_capacity(clp->infd, &in_num_sect,
1781                                           &in_sect_sz)) {
1782                 pr2serr("Unable to read block capacity on %s\n", infn);
1783                 in_num_sect = -1;
1784             }
1785             if (clp->bs != in_sect_sz) {
1786                 pr2serr("logical block size on %s confusion; bs=%d, from "
1787                         "device=%d\n", infn, clp->bs, in_sect_sz);
1788                 in_num_sect = -1;
1789             }
1790         }
1791         if (in_num_sect > skip)
1792             in_num_sect -= skip;
1793 
1794         out_num_sect = -1;
1795         if (FT_SG == clp->out_type) {
1796             res = scsi_read_capacity(clp->outfd, &out_num_sect, &out_sect_sz);
1797             if (2 == res) {
1798                 pr2serr("Unit attention, media changed(out), continuing\n");
1799                 res = scsi_read_capacity(clp->outfd, &out_num_sect,
1800                                          &out_sect_sz);
1801             }
1802             if (0 != res) {
1803                 if (res == SG_LIB_CAT_INVALID_OP)
1804                     pr2serr("read capacity not supported on %s\n", outfn);
1805                 else if (res == SG_LIB_CAT_NOT_READY)
1806                     pr2serr("read capacity failed, %s not ready\n", outfn);
1807                 else
1808                     pr2serr("Unable to read capacity on %s\n", outfn);
1809                 out_num_sect = -1;
1810             }
1811         } else if (FT_BLOCK == clp->out_type) {
1812             if (0 != read_blkdev_capacity(clp->outfd, &out_num_sect,
1813                                           &out_sect_sz)) {
1814                 pr2serr("Unable to read block capacity on %s\n", outfn);
1815                 out_num_sect = -1;
1816             }
1817             if (clp->bs != out_sect_sz) {
1818                 pr2serr("logical block size on %s confusion: bs=%d, from "
1819                         "device=%d\n", outfn, clp->bs, out_sect_sz);
1820                 out_num_sect = -1;
1821             }
1822         }
1823         if (out_num_sect > seek)
1824             out_num_sect -= seek;
1825 
1826         if (in_num_sect > 0) {
1827             if (out_num_sect > 0)
1828                 dd_count = (in_num_sect > out_num_sect) ? out_num_sect :
1829                                                           in_num_sect;
1830             else
1831                 dd_count = in_num_sect;
1832         }
1833         else
1834             dd_count = out_num_sect;
1835     }
1836     if (clp->debug > 1)
1837         pr2serr("Start of loop, count=%" PRId64 ", in_num_sect=%" PRId64
1838                 ", out_num_sect=%" PRId64 "\n", dd_count, in_num_sect,
1839                 out_num_sect);
1840     if (dd_count < 0) {
1841         pr2serr("Couldn't calculate count, please give one\n");
1842         return SG_LIB_CAT_OTHER;
1843     }
1844     if (! cdbsz_given) {
1845         if ((FT_SG == clp->in_type) && (MAX_SCSI_CDBSZ != clp->cdbsz_in) &&
1846             (((dd_count + skip) > UINT_MAX) || (clp->bpt > USHRT_MAX))) {
1847             pr2serr("Note: SCSI command size increased to 16 bytes (for "
1848                     "'if')\n");
1849             clp->cdbsz_in = MAX_SCSI_CDBSZ;
1850         }
1851         if ((FT_SG == clp->out_type) && (MAX_SCSI_CDBSZ != clp->cdbsz_out) &&
1852             (((dd_count + seek) > UINT_MAX) || (clp->bpt > USHRT_MAX))) {
1853             pr2serr("Note: SCSI command size increased to 16 bytes (for "
1854                     "'of')\n");
1855             clp->cdbsz_out = MAX_SCSI_CDBSZ;
1856         }
1857     }
1858 
1859     clp->in_count = dd_count;
1860     clp->in_rem_count = dd_count;
1861     clp->skip = skip;
1862     clp->in_blk = skip;
1863     clp->out_count = dd_count;
1864     clp->out_rem_count = dd_count;
1865     clp->seek = seek;
1866     status = pthread_mutex_init(&clp->inout_mutex, NULL);
1867     if (0 != status) err_exit(status, "init inout_mutex");
1868     status = pthread_mutex_lock(&clp->inout_mutex);
1869     if (0 != status) err_exit(status, "lock inout_mutex");
1870     clp->out_blk = seek;
1871     status = pthread_mutex_unlock(&clp->inout_mutex);
1872     if (0 != status) err_exit(status, "unlock inout_mutex");
1873 
1874     status = pthread_cond_init(&clp->out_sync_cv, NULL);
1875     if (0 != status) err_exit(status, "init out_sync_cv");
1876 
1877     if (clp->dry_run > 0) {
1878         pr2serr("Due to --dry-run option, bypass copy/read\n");
1879         goto fini;
1880     }
1881     sigemptyset(&signal_set);
1882     sigaddset(&signal_set, SIGINT);
1883     status = pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
1884     if (0 != status) err_exit(status, "pthread_sigmask");
1885     status = pthread_create(&sig_listen_thread_id, NULL,
1886                             sig_listen_thread, (void *)clp);
1887     if (0 != status) err_exit(status, "pthread_create, sig...");
1888 
1889     if (do_time) {
1890         start_tm.tv_sec = 0;
1891         start_tm.tv_usec = 0;
1892         gettimeofday(&start_tm, NULL);
1893     }
1894 
1895 /* vvvvvvvvvvv  Start worker threads  vvvvvvvvvvvvvvvvvvvvvvvv */
1896     if ((clp->out_rem_count > 0) && (clp->num_threads > 0)) {
1897         /* Run 1 work thread to shake down infant retryable stuff */
1898         status = pthread_mutex_lock(&clp->inout_mutex);
1899         if (0 != status) err_exit(status, "lock out_mutex");
1900         seek_skip = clp->seek - clp->skip;
1901         thr_arg_a[0].id = 0;
1902         thr_arg_a[0].seek_skip = seek_skip;
1903         status = pthread_create(&threads[0], NULL, read_write_thread,
1904                                 (void *)(thr_arg_a + 0));
1905         if (0 != status) err_exit(status, "pthread_create");
1906         if (clp->debug)
1907             pr2serr("Starting worker thread k=0\n");
1908 
1909         /* wait for any broadcast */
1910         pthread_cleanup_push(cleanup_out, (void *)clp);
1911         status = pthread_cond_wait(&clp->out_sync_cv, &clp->inout_mutex);
1912         if (0 != status) err_exit(status, "cond out_sync_cv");
1913         pthread_cleanup_pop(0);
1914         status = pthread_mutex_unlock(&clp->inout_mutex);
1915         if (0 != status) err_exit(status, "unlock out_mutex");
1916 
1917         /* now start the rest of the threads */
1918         for (k = 1; k < clp->num_threads; ++k) {
1919 
1920             thr_arg_a[k].id = k;
1921             thr_arg_a[k].seek_skip = seek_skip;
1922             status = pthread_create(&threads[k], NULL, read_write_thread,
1923                                     (void *)(thr_arg_a + k));
1924             if (0 != status) err_exit(status, "pthread_create");
1925             if (clp->debug > 2)
1926                 pr2serr("Starting worker thread k=%d\n", k);
1927         }
1928 
1929         /* now wait for worker threads to finish */
1930         for (k = 0; k < clp->num_threads; ++k) {
1931             status = pthread_join(threads[k], &vp);
1932             if (0 != status) err_exit(status, "pthread_join");
1933             if (clp->debug > 2)
1934                 pr2serr("Worker thread k=%d terminated\n", k);
1935         }
1936     }   /* started worker threads and here after they have all exited */
1937 
1938     if (do_time && (start_tm.tv_sec || start_tm.tv_usec))
1939         calc_duration_throughput(0);
1940 
1941     if (do_sync) {
1942         if (FT_SG == clp->out_type) {
1943             pr2serr(">> Synchronizing cache on %s\n", outfn);
1944             res = sg_ll_sync_cache_10(clp->outfd, 0, 0, 0, 0, 0, false, 0);
1945             if (SG_LIB_CAT_UNIT_ATTENTION == res) {
1946                 pr2serr("Unit attention(out), continuing\n");
1947                 res = sg_ll_sync_cache_10(clp->outfd, 0, 0, 0, 0, 0, false,
1948                                           0);
1949             }
1950             if (0 != res)
1951                 pr2serr("Unable to synchronize cache\n");
1952         }
1953     }
1954 
1955 #if 0
1956 #if SG_LIB_ANDROID
1957     /* Android doesn't have pthread_cancel() so use pthread_kill() instead.
1958      * Also there is no need to link with -lpthread in Android */
1959     status = pthread_kill(sig_listen_thread_id, SIGUSR1);
1960     if (0 != status) err_exit(status, "pthread_kill");
1961 #else
1962     status = pthread_cancel(sig_listen_thread_id);
1963     if (0 != status) err_exit(status, "pthread_cancel");
1964 #endif
1965 #endif  /* 0, because always do pthread_kill() next */
1966 
1967     shutting_down = true;
1968     status = pthread_kill(sig_listen_thread_id, SIGINT);
1969     if (0 != status) err_exit(status, "pthread_kill");
1970     /* valgrind says the above _kill() leaks; web says it needs a following
1971      * _join() to clear heap taken by associated _create() */
1972 
1973 fini:
1974     if ((STDIN_FILENO != clp->infd) && (clp->infd >= 0))
1975         close(clp->infd);
1976     if ((STDOUT_FILENO != clp->outfd) && (FT_DEV_NULL != clp->out_type)) {
1977         if (clp->outfd >= 0)
1978             close(clp->outfd);
1979     }
1980     res = exit_status;
1981     if ((0 != clp->out_count) && (0 == clp->dry_run)) {
1982         pr2serr(">>>> Some error occurred, remaining blocks=%" PRId64 "\n",
1983                 clp->out_count);
1984         if (0 == res)
1985             res = SG_LIB_CAT_OTHER;
1986     }
1987     print_stats("");
1988     if (clp->dio_incomplete_count) {
1989         int fd;
1990         char c;
1991 
1992         pr2serr(">> Direct IO requested but incomplete %d times\n",
1993                 clp->dio_incomplete_count);
1994         if ((fd = open(sg_allow_dio, O_RDONLY)) >= 0) {
1995             if (1 == read(fd, &c, 1)) {
1996                 if ('0' == c)
1997                     pr2serr(">>> %s set to '0' but should be set to '1' for "
1998                             "direct IO\n", sg_allow_dio);
1999             }
2000             close(fd);
2001         }
2002     }
2003     if (clp->sum_of_resids)
2004         pr2serr(">> Non-zero sum of residual counts=%d\n",
2005                clp->sum_of_resids);
2006 #ifdef HAVE_C11_ATOMICS
2007     {
2008         unsigned int ui;
2009 
2010         if ((ui = atomic_load(&num_eagain)) > 0)
2011             pr2serr(">> number of IO call yielding EAGAIN %u\n", ui);
2012         if ((ui = atomic_load(&num_ebusy)) > 0)
2013             pr2serr(">> number of IO call yielding EBUSY %u\n", ui);
2014         if ((ui = atomic_load(&num_eintr)) > 0)
2015             pr2serr(">> number of IO call yielding EINTR %u\n", ui);
2016     }
2017 #endif
2018     return (res >= 0) ? res : SG_LIB_CAT_OTHER;
2019 }
2020