xref: /aosp_15_r20/external/sg3_utils/testing/sg_mrq_dd.cpp (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1*44704f69SBart Van Assche /*
2*44704f69SBart Van Assche  * A utility program for copying files. Specialised for "files" that
3*44704f69SBart Van Assche  * represent devices that understand the SCSI command set.
4*44704f69SBart Van Assche  *
5*44704f69SBart Van Assche  * Copyright (C) 2018-2022 D. Gilbert
6*44704f69SBart Van Assche  * This program is free software; you can redistribute it and/or modify
7*44704f69SBart Van Assche  * it under the terms of the GNU General Public License as published by
8*44704f69SBart Van Assche  * the Free Software Foundation; either version 2, or (at your option)
9*44704f69SBart Van Assche  * any later version.
10*44704f69SBart Van Assche  *
11*44704f69SBart Van Assche  * SPDX-License-Identifier: GPL-2.0-or-later
12*44704f69SBart Van Assche  *
13*44704f69SBart Van Assche  * This program is a specialisation of the Unix "dd" command in which
14*44704f69SBart Van Assche  * one or both of the given files is a scsi generic device.
15*44704f69SBart Van Assche  * A logical block size ('bs') is assumed to be 512 if not given. This
16*44704f69SBart Van Assche  * program complains if 'ibs' or 'obs' are given with some other value
17*44704f69SBart Van Assche  * than 'bs'. If 'if' is not given or 'if=-' then stdin is assumed. If
18*44704f69SBart Van Assche  * 'of' is not given or 'of=-' then stdout assumed.
19*44704f69SBart Van Assche  *
20*44704f69SBart Van Assche  * A non-standard argument "bpt" (blocks per transfer) is added to control
21*44704f69SBart Van Assche  * the maximum number of blocks in each transfer. The default value is 128.
22*44704f69SBart Van Assche  * For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16 KiB
23*44704f69SBart Van Assche  * in this case) are transferred to or from the sg device in a single SCSI
24*44704f69SBart Van Assche  * command.
25*44704f69SBart Van Assche  *
26*44704f69SBart Van Assche  * This version is designed for the linux kernel 4 and 5 series.
27*44704f69SBart Van Assche  *
28*44704f69SBart Van Assche  * sg_mrq_dd uses C++ threads and MRQ (multiple requests (in one invocation))
29*44704f69SBart Van Assche  * facilities in the sg version 4 driver to do "dd" type copies and verifies.
30*44704f69SBart Van Assche  *
31*44704f69SBart Van Assche  */
32*44704f69SBart Van Assche 
33*44704f69SBart Van Assche static const char * version_str = "1.44 20221020";
34*44704f69SBart Van Assche 
35*44704f69SBart Van Assche #define _XOPEN_SOURCE 600
36*44704f69SBart Van Assche #ifndef _GNU_SOURCE
37*44704f69SBart Van Assche #define _GNU_SOURCE 1
38*44704f69SBart Van Assche #endif
39*44704f69SBart Van Assche 
40*44704f69SBart Van Assche #include <unistd.h>
41*44704f69SBart Van Assche #include <fcntl.h>
42*44704f69SBart Van Assche #include <stdio.h>
43*44704f69SBart Van Assche #include <stdlib.h>
44*44704f69SBart Van Assche #include <stdarg.h>
45*44704f69SBart Van Assche #include <stdbool.h>
46*44704f69SBart Van Assche #include <string.h>
47*44704f69SBart Van Assche #include <ctype.h>
48*44704f69SBart Van Assche #include <errno.h>
49*44704f69SBart Van Assche #include <time.h>       /* for nanosleep() */
50*44704f69SBart Van Assche #include <poll.h>
51*44704f69SBart Van Assche #include <limits.h>
52*44704f69SBart Van Assche // #include <pthread.h>
53*44704f69SBart Van Assche #include <signal.h>
54*44704f69SBart Van Assche #define __STDC_FORMAT_MACROS 1
55*44704f69SBart Van Assche #include <inttypes.h>
56*44704f69SBart Van Assche #include <sys/ioctl.h>
57*44704f69SBart Van Assche #include <sys/stat.h>
58*44704f69SBart Van Assche #include <sys/sysmacros.h>
59*44704f69SBart Van Assche #ifndef major
60*44704f69SBart Van Assche #include <sys/types.h>
61*44704f69SBart Van Assche #endif
62*44704f69SBart Van Assche #include <sys/time.h>
63*44704f69SBart Van Assche #include <linux/major.h>        /* for MEM_MAJOR, SCSI_GENERIC_MAJOR, etc */
64*44704f69SBart Van Assche #include <linux/fs.h>           /* for BLKSSZGET and friends */
65*44704f69SBart Van Assche #include <sys/mman.h>           /* for mmap() system call */
66*44704f69SBart Van Assche 
67*44704f69SBart Van Assche #include <vector>
68*44704f69SBart Van Assche #include <array>
69*44704f69SBart Van Assche #include <atomic>       // C++ header replacing <stdatomic.h>
70*44704f69SBart Van Assche #include <random>
71*44704f69SBart Van Assche #include <thread>       // needed for std::this_thread::yield()
72*44704f69SBart Van Assche #include <mutex>
73*44704f69SBart Van Assche #include <condition_variable>   // for infant_cv: copy/verify first segment
74*44704f69SBart Van Assche                                 // single threaded
75*44704f69SBart Van Assche #include <chrono>
76*44704f69SBart Van Assche 
77*44704f69SBart Van Assche #ifdef HAVE_CONFIG_H
78*44704f69SBart Van Assche #include "config.h"
79*44704f69SBart Van Assche #endif
80*44704f69SBart Van Assche 
81*44704f69SBart Van Assche #ifdef HAVE_GETRANDOM
82*44704f69SBart Van Assche #include <sys/random.h>         /* for getrandom() system call */
83*44704f69SBart Van Assche #endif
84*44704f69SBart Van Assche 
85*44704f69SBart Van Assche #ifndef HAVE_LINUX_SG_V4_HDR
86*44704f69SBart Van Assche /* Kernel uapi header contain __user decorations on user space pointers
87*44704f69SBart Van Assche  * to indicate they are unsafe in the kernel space. However glibc takes
88*44704f69SBart Van Assche  * all those __user decorations out from headers in /usr/include/linux .
89*44704f69SBart Van Assche  * So to stop compile errors when directly importing include/uapi/scsi/sg.h
90*44704f69SBart Van Assche  * undef __user before doing that include. */
91*44704f69SBart Van Assche #define __user
92*44704f69SBart Van Assche 
93*44704f69SBart Van Assche /* Want to block the original sg.h header from also being included. That
94*44704f69SBart Van Assche  * causes lots of multiple definition errors. This will only work if this
95*44704f69SBart Van Assche  * header is included _before_ the original sg.h header.  */
96*44704f69SBart Van Assche #define _SCSI_GENERIC_H         /* original kernel header guard */
97*44704f69SBart Van Assche #define _SCSI_SG_H              /* glibc header guard */
98*44704f69SBart Van Assche 
99*44704f69SBart Van Assche #include "uapi_sg.h"    /* local copy of include/uapi/scsi/sg.h */
100*44704f69SBart Van Assche 
101*44704f69SBart Van Assche #else
102*44704f69SBart Van Assche #define __user
103*44704f69SBart Van Assche #endif  /* end of: ifndef HAVE_LINUX_SG_V4_HDR */
104*44704f69SBart Van Assche 
105*44704f69SBart Van Assche // C++ local header
106*44704f69SBart Van Assche #include "sg_scat_gath.h"
107*44704f69SBart Van Assche 
108*44704f69SBart Van Assche // C headers associated with sg3_utils library
109*44704f69SBart Van Assche #include "sg_lib.h"
110*44704f69SBart Van Assche #include "sg_cmds_basic.h"
111*44704f69SBart Van Assche #include "sg_io_linux.h"
112*44704f69SBart Van Assche #include "sg_unaligned.h"
113*44704f69SBart Van Assche #include "sg_pr2serr.h"
114*44704f69SBart Van Assche 
115*44704f69SBart Van Assche 
116*44704f69SBart Van Assche using namespace std;
117*44704f69SBart Van Assche 
118*44704f69SBart Van Assche // #ifdef __GNUC__
119*44704f69SBart Van Assche // #ifndef  __clang__
120*44704f69SBart Van Assche // #pragma GCC diagnostic ignored "-Wclobbered"
121*44704f69SBart Van Assche // #endif
122*44704f69SBart Van Assche // #endif
123*44704f69SBart Van Assche 
124*44704f69SBart Van Assche 
125*44704f69SBart Van Assche #ifndef SGV4_FLAG_POLLED
126*44704f69SBart Van Assche #define SGV4_FLAG_POLLED 0x800
127*44704f69SBart Van Assche #endif
128*44704f69SBart Van Assche 
129*44704f69SBart Van Assche #define MAX_SGL_NUM_VAL (INT32_MAX - 1)  /* should reduce for testing */
130*44704f69SBart Van Assche // #define MAX_SGL_NUM_VAL 7  /* should reduce for testing */
131*44704f69SBart Van Assche #if MAX_SGL_NUM_VAL > INT32_MAX
132*44704f69SBart Van Assche #error "MAX_SGL_NUM_VAL cannot exceed 2^31 - 1"
133*44704f69SBart Van Assche #endif
134*44704f69SBart Van Assche 
135*44704f69SBart Van Assche #define DEF_BLOCK_SIZE 512
136*44704f69SBart Van Assche #define DEF_BLOCKS_PER_TRANSFER 128
137*44704f69SBart Van Assche #define DEF_BLOCKS_PER_2048TRANSFER 32
138*44704f69SBart Van Assche #define DEF_SDT_ICT_MS 300
139*44704f69SBart Van Assche #define DEF_SDT_CRT_SEC 3
140*44704f69SBart Van Assche #define DEF_SCSI_CDB_SZ 10
141*44704f69SBart Van Assche #define MAX_SCSI_CDB_SZ 16      /* could be 32 */
142*44704f69SBart Van Assche #define PACK_ID_TID_MULTIPLIER (0x1000000)      /* 16,777,216 */
143*44704f69SBart Van Assche #define MAX_SLICES 16           /* number of IFILE,OFILE pairs */
144*44704f69SBart Van Assche #define MAX_BPT_VALUE (1 << 24)         /* used for maximum bs as well */
145*44704f69SBart Van Assche #define MAX_COUNT_SKIP_SEEK (1LL << 48) /* coverity wants upper bound */
146*44704f69SBart Van Assche 
147*44704f69SBart Van Assche #define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
148*44704f69SBart Van Assche #define READ_CAP_REPLY_LEN 8
149*44704f69SBart Van Assche #define RCAP16_REPLY_LEN 32
150*44704f69SBart Van Assche 
151*44704f69SBart Van Assche #define DEF_TIMEOUT 60000       /* 60,000 millisecs == 60 seconds */
152*44704f69SBart Van Assche 
153*44704f69SBart Van Assche #define SGP_READ10 0x28
154*44704f69SBart Van Assche #define SGP_PRE_FETCH10 0x34
155*44704f69SBart Van Assche #define SGP_PRE_FETCH16 0x90
156*44704f69SBart Van Assche #define SGP_VERIFY10 0x2f
157*44704f69SBart Van Assche #define SGP_WRITE10 0x2a
158*44704f69SBart Van Assche #define DEF_NUM_THREADS 4
159*44704f69SBart Van Assche #define MAX_NUM_THREADS 1024 /* was SG_MAX_QUEUE with v3 driver */
160*44704f69SBart Van Assche #define DEF_MRQ_NUM 16
161*44704f69SBart Van Assche 
162*44704f69SBart Van Assche #define FT_UNKNOWN 0            /* yet to be checked */
163*44704f69SBart Van Assche #define FT_OTHER 1              /* filetype other than one of the following */
164*44704f69SBart Van Assche #define FT_SG 2                 /* filetype is sg char device */
165*44704f69SBart Van Assche #define FT_DEV_NULL 4           /* either /dev/null, /dev/zero, or "." */
166*44704f69SBart Van Assche #define FT_ST 8                 /* filetype is st char device (tape) */
167*44704f69SBart Van Assche #define FT_BLOCK 16             /* filetype is a block device */
168*44704f69SBart Van Assche #define FT_FIFO 32              /* fifo (named or unnamed pipe (stdout)) */
169*44704f69SBart Van Assche #define FT_CHAR 64              /* fifo (named or unnamed pipe (stdout)) */
170*44704f69SBart Van Assche #define FT_RANDOM_0_FF 128      /* iflag=00, iflag=ff and iflag=random
171*44704f69SBart Van Assche                                    override if=IFILE */
172*44704f69SBart Van Assche #define FT_ERROR 256            /* couldn't "stat" file */
173*44704f69SBart Van Assche 
174*44704f69SBart Van Assche #define DEV_NULL_MINOR_NUM 3
175*44704f69SBart Van Assche #define DEV_ZERO_MINOR_NUM 5
176*44704f69SBart Van Assche 
177*44704f69SBart Van Assche #define EBUFF_SZ 768
178*44704f69SBart Van Assche 
179*44704f69SBart Van Assche #define PROC_SCSI_SG_VERSION "/proc/scsi/sg/version"
180*44704f69SBart Van Assche #define SYS_SCSI_SG_VERSION "/sys/module/sg/version"
181*44704f69SBart Van Assche 
182*44704f69SBart Van Assche 
183*44704f69SBart Van Assche struct flags_t {
184*44704f69SBart Van Assche     bool append;
185*44704f69SBart Van Assche     bool coe;
186*44704f69SBart Van Assche     bool dio;
187*44704f69SBart Van Assche     bool direct;
188*44704f69SBart Van Assche     bool dpo;
189*44704f69SBart Van Assche     bool dsync;
190*44704f69SBart Van Assche     bool excl;
191*44704f69SBart Van Assche     bool ff;
192*44704f69SBart Van Assche     bool fua;
193*44704f69SBart Van Assche     bool masync;        /* more async sg v4 driver fd flag */
194*44704f69SBart Van Assche     bool mout_if;       /* META_OUT_IF flag at mrq level */
195*44704f69SBart Van Assche     bool nocreat;
196*44704f69SBart Van Assche     bool no_dur;
197*44704f69SBart Van Assche     bool no_thresh;
198*44704f69SBart Van Assche     bool no_waitq;      /* dummy, no longer supported, just warn */
199*44704f69SBart Van Assche     bool order_wr;
200*44704f69SBart Van Assche     bool polled;        /* was previously 'hipri' */
201*44704f69SBart Van Assche     bool qhead;
202*44704f69SBart Van Assche     bool qtail;
203*44704f69SBart Van Assche     bool random;
204*44704f69SBart Van Assche     bool serial;
205*44704f69SBart Van Assche     bool same_fds;
206*44704f69SBart Van Assche     bool wq_excl;
207*44704f69SBart Van Assche     bool zero;
208*44704f69SBart Van Assche     int cdl;            /* command duration limits, 0 --> no cdl */
209*44704f69SBart Van Assche     int mmap;
210*44704f69SBart Van Assche };
211*44704f69SBart Van Assche 
212*44704f69SBart Van Assche typedef pair<int64_t, int> get_next_res_t;      /* LBA, num */
213*44704f69SBart Van Assche typedef array<uint8_t, MAX_SCSI_CDB_SZ> cdb_arr_t;
214*44704f69SBart Van Assche 
215*44704f69SBart Van Assche struct cp_ver_pair_t {
cp_ver_pair_tcp_ver_pair_t216*44704f69SBart Van Assche     cp_ver_pair_t() {}
217*44704f69SBart Van Assche 
218*44704f69SBart Van Assche     get_next_res_t get_next(int desired_num_blks);
219*44704f69SBart Van Assche 
220*44704f69SBart Van Assche     enum class my_state {empty,
221*44704f69SBart Van Assche                          init,
222*44704f69SBart Van Assche                          underway,
223*44704f69SBart Van Assche                          ignore,
224*44704f69SBart Van Assche                          finished} state = {my_state::empty};
225*44704f69SBart Van Assche 
226*44704f69SBart Van Assche     int my_index = 0;
227*44704f69SBart Van Assche     int in_fd = -1;
228*44704f69SBart Van Assche     int in_type = FT_UNKNOWN;
229*44704f69SBart Van Assche     int out_fd = -1;
230*44704f69SBart Van Assche     int out_type = FT_UNKNOWN;
231*44704f69SBart Van Assche 
232*44704f69SBart Van Assche     int64_t dd_count = 0;
233*44704f69SBart Van Assche     atomic<int64_t> next_count_pos {};
234*44704f69SBart Van Assche     atomic<int64_t> in_rem_count {};
235*44704f69SBart Van Assche     atomic<int64_t> out_rem_count {};
236*44704f69SBart Van Assche     atomic<int> in_partial {};
237*44704f69SBart Van Assche     atomic<int> out_partial {};
238*44704f69SBart Van Assche     atomic<int> sum_of_resids {};
239*44704f69SBart Van Assche };
240*44704f69SBart Van Assche 
241*44704f69SBart Van Assche typedef array<cp_ver_pair_t, MAX_SLICES> cp_ver_arr_t;
242*44704f69SBart Van Assche 
243*44704f69SBart Van Assche /* There is one instance of this structure and it is at file scope so it is
244*44704f69SBart Van Assche  * initialized to zero. The design of this copy multi-threaded copy algorithm
245*44704f69SBart Van Assche  * attempts to have no locks on the fast path. Contention in gcoll.get_next()
246*44704f69SBart Van Assche  * is resolved by the loser repeating its operation. Statistics and error
247*44704f69SBart Van Assche  * information is held in each thread until it shuts down and contention
248*44704f69SBart Van Assche  * can occur at that point. */
249*44704f69SBart Van Assche struct global_collection        /* one instance visible to all threads */
250*44704f69SBart Van Assche {
251*44704f69SBart Van Assche     cp_ver_arr_t cp_ver_arr;
252*44704f69SBart Van Assche 
253*44704f69SBart Van Assche     /* get_next() is the pivotal function for multi-threaded safety. It can
254*44704f69SBart Van Assche      * be safely called from all threads with the desired number of blocks
255*44704f69SBart Van Assche      * (typically mrq*bpt) and this function returns a pair. The first pair
256*44704f69SBart Van Assche      * value is the starting count value/index [0..dd_count) and the second
257*44704f69SBart Van Assche      * pair value is the number of blocks to copy. If desired_num_blks is
258*44704f69SBart Van Assche      * negative this flags an error has occurred. If the second value in the
259*44704f69SBart Van Assche      * returned pair is 0 then the calling thread should shutdown; a
260*44704f69SBart Van Assche      * negative value indicates an error has occurred (e.g. in another
261*44704f69SBart Van Assche      * thread) and the calling thread should shutdown. */
262*44704f69SBart Van Assche 
263*44704f69SBart Van Assche     int in0fd;
264*44704f69SBart Van Assche     int64_t dd_count;
265*44704f69SBart Van Assche     int in_type;                /* expect all IFILEs to have same type */
266*44704f69SBart Van Assche     int cdbsz_in;
267*44704f69SBart Van Assche     int help;
268*44704f69SBart Van Assche     struct flags_t in_flags;
269*44704f69SBart Van Assche     atomic<int> in_partial;           /*  | */
270*44704f69SBart Van Assche     off_t in_st_size;                 /* Only for FT_OTHER (regular) file */
271*44704f69SBart Van Assche     int mrq_num;                      /* if user gives 0, set this to 1 */
272*44704f69SBart Van Assche     int out0fd;
273*44704f69SBart Van Assche     int out_type;
274*44704f69SBart Van Assche     int cdbsz_out;
275*44704f69SBart Van Assche     struct flags_t out_flags;
276*44704f69SBart Van Assche     atomic<int> out_partial;          /*  | */
277*44704f69SBart Van Assche     off_t out_st_size;                /* Only for FT_OTHER (regular) file */
278*44704f69SBart Van Assche     condition_variable infant_cv;     /* after thread:0 does first segment */
279*44704f69SBart Van Assche     mutex infant_mut;
280*44704f69SBart Van Assche     int bs;
281*44704f69SBart Van Assche     int bpt;
282*44704f69SBart Van Assche     int cmd_timeout;            /* in milliseconds */
283*44704f69SBart Van Assche     int elem_sz;
284*44704f69SBart Van Assche     int outregfd;
285*44704f69SBart Van Assche     int outreg_type;
286*44704f69SBart Van Assche     off_t outreg_st_size;
287*44704f69SBart Van Assche     atomic<int> dio_incomplete_count;
288*44704f69SBart Van Assche     atomic<int> sum_of_resids;
289*44704f69SBart Van Assche     atomic<int> reason_res;
290*44704f69SBart Van Assche     atomic<int> most_recent_pack_id;
291*44704f69SBart Van Assche     uint32_t sdt_ict; /* stall detection; initial check time (milliseconds) */
292*44704f69SBart Van Assche     uint32_t sdt_crt; /* check repetition time (seconds), after first stall */
293*44704f69SBart Van Assche     int dry_run;
294*44704f69SBart Van Assche     int verbose;
295*44704f69SBart Van Assche     bool mrq_eq_0;              /* true when user gives mrq=0 */
296*44704f69SBart Van Assche     bool processed;
297*44704f69SBart Van Assche     bool cdbsz_given;
298*44704f69SBart Van Assche     bool cdl_given;
299*44704f69SBart Van Assche     bool count_given;
300*44704f69SBart Van Assche     bool ese;
301*44704f69SBart Van Assche     bool flexible;
302*44704f69SBart Van Assche     bool mrq_polled;
303*44704f69SBart Van Assche     bool ofile_given;
304*44704f69SBart Van Assche     bool unit_nanosec;          /* default duration unit is millisecond */
305*44704f69SBart Van Assche     bool verify;                /* don't copy, verify like Unix: cmp */
306*44704f69SBart Van Assche     bool prefetch;              /* for verify: do PF(b),RD(a),V(b)_a_data */
307*44704f69SBart Van Assche     vector<string> inf_v;
308*44704f69SBart Van Assche     vector<string> outf_v;
309*44704f69SBart Van Assche     const char * infp;
310*44704f69SBart Van Assche     const char * outfp;
311*44704f69SBart Van Assche     class scat_gath_list i_sgl;
312*44704f69SBart Van Assche     class scat_gath_list o_sgl;
313*44704f69SBart Van Assche };
314*44704f69SBart Van Assche 
315*44704f69SBart Van Assche typedef struct request_element
316*44704f69SBart Van Assche {       /* one instance per worker thread */
317*44704f69SBart Van Assche     struct global_collection *clp;
318*44704f69SBart Van Assche     bool has_share;
319*44704f69SBart Van Assche     bool both_sg;
320*44704f69SBart Van Assche     bool same_sg;
321*44704f69SBart Van Assche     bool only_in_sg;
322*44704f69SBart Van Assche     bool only_out_sg;
323*44704f69SBart Van Assche     bool stop_after_write;
324*44704f69SBart Van Assche     bool stop_now;
325*44704f69SBart Van Assche     int id;
326*44704f69SBart Van Assche     int bs;
327*44704f69SBart Van Assche     int infd;
328*44704f69SBart Van Assche     int outfd;
329*44704f69SBart Van Assche     int outregfd;
330*44704f69SBart Van Assche     uint8_t * buffp;
331*44704f69SBart Van Assche     uint8_t * alloc_bp;
332*44704f69SBart Van Assche     struct sg_io_v4 io_hdr4[2];
333*44704f69SBart Van Assche     uint8_t cmd[MAX_SCSI_CDB_SZ];
334*44704f69SBart Van Assche     uint8_t sb[SENSE_BUFF_LEN];
335*44704f69SBart Van Assche     int dio_incomplete_count;
336*44704f69SBart Van Assche     int mmap_active;
337*44704f69SBart Van Assche     int rd_p_id;
338*44704f69SBart Van Assche     int rep_count;
339*44704f69SBart Van Assche     int rq_id;
340*44704f69SBart Van Assche     int mmap_len;
341*44704f69SBart Van Assche     int mrq_id;
342*44704f69SBart Van Assche     int mrq_index;
343*44704f69SBart Van Assche     int mrq_pack_id_off;
344*44704f69SBart Van Assche     uint32_t a_mrq_din_blks;
345*44704f69SBart Van Assche     uint32_t a_mrq_dout_blks;
346*44704f69SBart Van Assche     int64_t in_follow_on;
347*44704f69SBart Van Assche     int64_t out_follow_on;
348*44704f69SBart Van Assche     int64_t in_local_count;
349*44704f69SBart Van Assche     int64_t out_local_count;
350*44704f69SBart Van Assche     int64_t in_rem_count;
351*44704f69SBart Van Assche     int64_t out_rem_count;
352*44704f69SBart Van Assche     int in_local_partial;
353*44704f69SBart Van Assche     int out_local_partial;
354*44704f69SBart Van Assche     int in_resid_bytes;
355*44704f69SBart Van Assche     long seed;
356*44704f69SBart Van Assche #ifdef HAVE_SRAND48_R   /* gcc extension. N.B. non-reentrant version slower */
357*44704f69SBart Van Assche     struct drand48_data drand;/* opaque, used by srand48_r and mrand48_r */
358*44704f69SBart Van Assche #endif
359*44704f69SBart Van Assche } Rq_elem;
360*44704f69SBart Van Assche 
361*44704f69SBart Van Assche /* Additional parameters for sg_start_io() and sg_finish_io() */
362*44704f69SBart Van Assche struct sg_io_extra {
363*44704f69SBart Van Assche     bool prefetch;
364*44704f69SBart Van Assche     bool dout_is_split;
365*44704f69SBart Van Assche     int hpv4_ind;
366*44704f69SBart Van Assche     int blk_offset;
367*44704f69SBart Van Assche     int blks;
368*44704f69SBart Van Assche };
369*44704f69SBart Van Assche 
370*44704f69SBart Van Assche #define MONO_MRQ_ID_INIT 0x10000
371*44704f69SBart Van Assche 
372*44704f69SBart Van Assche 
373*44704f69SBart Van Assche 
374*44704f69SBart Van Assche /* Use this class to wrap C++11 <random> features to produce uniform random
375*44704f69SBart Van Assche  * unsigned ints in the range [lo, hi] (inclusive) given a_seed */
376*44704f69SBart Van Assche class Rand_uint {
377*44704f69SBart Van Assche public:
Rand_uint(unsigned int lo,unsigned int hi,unsigned int a_seed)378*44704f69SBart Van Assche     Rand_uint(unsigned int lo, unsigned int hi, unsigned int a_seed)
379*44704f69SBart Van Assche         : uid(lo, hi), dre(a_seed) { }
380*44704f69SBart Van Assche     /* uid ctor takes inclusive range when integral type */
381*44704f69SBart Van Assche 
get()382*44704f69SBart Van Assche     unsigned int get() { return uid(dre); }
383*44704f69SBart Van Assche 
384*44704f69SBart Van Assche private:
385*44704f69SBart Van Assche     uniform_int_distribution<unsigned int> uid;
386*44704f69SBart Van Assche     default_random_engine dre;
387*44704f69SBart Van Assche };
388*44704f69SBart Van Assche 
389*44704f69SBart Van Assche static atomic<int> num_ebusy(0);
390*44704f69SBart Van Assche static atomic<int> num_start_eagain(0);
391*44704f69SBart Van Assche static atomic<int> num_fin_eagain(0);
392*44704f69SBart Van Assche static atomic<int> num_miscompare(0);
393*44704f69SBart Van Assche static atomic<int> num_fallthru_sigusr2(0);
394*44704f69SBart Van Assche static atomic<bool> vb_first_time(true);
395*44704f69SBart Van Assche 
396*44704f69SBart Van Assche static sigset_t signal_set;
397*44704f69SBart Van Assche static sigset_t orig_signal_set;
398*44704f69SBart Van Assche 
399*44704f69SBart Van Assche static const char * sg_allow_dio = "/sys/module/sg/parameters/allow_dio";
400*44704f69SBart Van Assche 
401*44704f69SBart Van Assche static int do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
402*44704f69SBart Van Assche                               scat_gath_iter & o_sg_it, int seg_blks,
403*44704f69SBart Van Assche                               vector<cdb_arr_t> & a_cdb,
404*44704f69SBart Van Assche                               vector<struct sg_io_v4> & a_v4);
405*44704f69SBart Van Assche static int do_both_sg_segment_mrq0(Rq_elem * rep, scat_gath_iter & i_sg_it,
406*44704f69SBart Van Assche                                    scat_gath_iter & o_sg_it, int seg_blks);
407*44704f69SBart Van Assche static int do_normal_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
408*44704f69SBart Van Assche                                 scat_gath_iter & o_sg_it, int seg_blks,
409*44704f69SBart Van Assche                                 vector<cdb_arr_t> & a_cdb,
410*44704f69SBart Van Assche                                 vector<struct sg_io_v4> & a_v4);
411*44704f69SBart Van Assche static int do_normal_normal_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
412*44704f69SBart Van Assche                                     scat_gath_iter & o_sg_it, int seg_blks);
413*44704f69SBart Van Assche 
414*44704f69SBart Van Assche #define STRERR_BUFF_LEN 128
415*44704f69SBart Van Assche 
416*44704f69SBart Van Assche static mutex strerr_mut;
417*44704f69SBart Van Assche 
418*44704f69SBart Van Assche static bool have_sg_version = false;
419*44704f69SBart Van Assche static int sg_version = 0;
420*44704f69SBart Van Assche static bool sg_version_ge_40045 = false;
421*44704f69SBart Van Assche static atomic<bool> shutting_down{false};
422*44704f69SBart Van Assche static bool do_sync = false;
423*44704f69SBart Van Assche static int do_time = 1;
424*44704f69SBart Van Assche static struct global_collection gcoll;
425*44704f69SBart Van Assche static struct timeval start_tm;
426*44704f69SBart Van Assche static int num_threads = DEF_NUM_THREADS;
427*44704f69SBart Van Assche static bool after1 = false;
428*44704f69SBart Van Assche static int listen_t_tid;
429*44704f69SBart Van Assche 
430*44704f69SBart Van Assche static const char * my_name = "sg_mrq_dd: ";
431*44704f69SBart Van Assche 
432*44704f69SBart Van Assche // static const char * mrq_blk_s = "mrq: ordinary blocking";
433*44704f69SBart Van Assche static const char * mrq_svb_s = "mrq: shared variable blocking (svb)";
434*44704f69SBart Van Assche static const char * mrq_ob_s = "mrq: ordered blocking";
435*44704f69SBart Van Assche static const char * mrq_vb_s = "mrq: variable blocking";
436*44704f69SBart Van Assche 
437*44704f69SBart Van Assche 
438*44704f69SBart Van Assche #ifdef __GNUC__
439*44704f69SBart Van Assche static int pr2serr_lk(const char * fmt, ...)
440*44704f69SBart Van Assche         __attribute__ ((format (printf, 1, 2)));
441*44704f69SBart Van Assche #else
442*44704f69SBart Van Assche static int pr2serr_lk(const char * fmt, ...);
443*44704f69SBart Van Assche #endif
444*44704f69SBart Van Assche 
445*44704f69SBart Van Assche 
446*44704f69SBart Van Assche static int
pr2serr_lk(const char * fmt,...)447*44704f69SBart Van Assche pr2serr_lk(const char * fmt, ...)
448*44704f69SBart Van Assche {
449*44704f69SBart Van Assche     int n;
450*44704f69SBart Van Assche     va_list args;
451*44704f69SBart Van Assche     lock_guard<mutex> lk(strerr_mut);
452*44704f69SBart Van Assche 
453*44704f69SBart Van Assche     va_start(args, fmt);
454*44704f69SBart Van Assche     n = vfprintf(stderr, fmt, args);
455*44704f69SBart Van Assche     va_end(args);
456*44704f69SBart Van Assche     return n;
457*44704f69SBart Van Assche }
458*44704f69SBart Van Assche 
459*44704f69SBart Van Assche static void
usage(int pg_num)460*44704f69SBart Van Assche usage(int pg_num)
461*44704f69SBart Van Assche {
462*44704f69SBart Van Assche     if (pg_num > 4)
463*44704f69SBart Van Assche         goto page5;
464*44704f69SBart Van Assche     if (pg_num > 3)
465*44704f69SBart Van Assche         goto page4;
466*44704f69SBart Van Assche     else if (pg_num > 2)
467*44704f69SBart Van Assche         goto page3;
468*44704f69SBart Van Assche     else if (pg_num > 1)
469*44704f69SBart Van Assche         goto page2;
470*44704f69SBart Van Assche 
471*44704f69SBart Van Assche     pr2serr("Usage: sg_mrq_dd  [bs=BS] [conv=CONV] [count=COUNT] [ibs=BS] "
472*44704f69SBart Van Assche             "[if=IFILE*]\n"
473*44704f69SBart Van Assche             "                  [iflag=FLAGS] [obs=BS] [of=OFILE*] "
474*44704f69SBart Van Assche             "[oflag=FLAGS]\n"
475*44704f69SBart Van Assche             "                  [seek=SEEK] [skip=SKIP] [--help] [--verify] "
476*44704f69SBart Van Assche             "[--version]\n\n");
477*44704f69SBart Van Assche     pr2serr("                  [bpt=BPT] [cdbsz=6|10|12|16] [cdl=CDL] "
478*44704f69SBart Van Assche             "[dio=0|1]\n"
479*44704f69SBart Van Assche             "                  [elemsz_kb=EKB] [ese=0|1] [fua=0|1|2|3] "
480*44704f69SBart Van Assche             "[polled=NRQS]\n"
481*44704f69SBart Van Assche             "                  [mrq=NRQS] [ofreg=OFREG] [sdt=SDT] "
482*44704f69SBart Van Assche             "[sync=0|1]\n"
483*44704f69SBart Van Assche             "                  [thr=THR] [time=0|1|2[,TO]] [verbose=VERB] "
484*44704f69SBart Van Assche             "[--dry-run]\n"
485*44704f69SBart Van Assche             "                  [--pre-fetch] [--verbose] [--version]\n\n"
486*44704f69SBart Van Assche             "  where: operands have the form name=value and are pecular to "
487*44704f69SBart Van Assche             "'dd'\n"
488*44704f69SBart Van Assche             "         style commands, and options start with one or "
489*44704f69SBart Van Assche             "two hyphens;\n"
490*44704f69SBart Van Assche             "         the main operands and options (shown in first group "
491*44704f69SBart Van Assche             "above) are:\n"
492*44704f69SBart Van Assche             "    bs          must be device logical block size (default "
493*44704f69SBart Van Assche             "512)\n"
494*44704f69SBart Van Assche             "    conv        comma separated list from: [nocreat,noerror,"
495*44704f69SBart Van Assche             "notrunc,\n"
496*44704f69SBart Van Assche             "                null,sync]\n"
497*44704f69SBart Van Assche             "    count       number of blocks to copy (def: device size)\n"
498*44704f69SBart Van Assche             "    if          file(s) or device(s) to read from (def: "
499*44704f69SBart Van Assche             "stdin)\n"
500*44704f69SBart Van Assche             "    iflag       comma separated list from: [00,coe,dio,"
501*44704f69SBart Van Assche             "direct,dpo,\n"
502*44704f69SBart Van Assche             "                dsync,excl,ff,fua,masync,mmap,mout_if,nodur,"
503*44704f69SBart Van Assche             "null,\n"
504*44704f69SBart Van Assche             "                order,qhead,qtail,random,same_fds,serial,"
505*44704f69SBart Van Assche             "wq_excl]\n"
506*44704f69SBart Van Assche             "    of          file(s) or device(s) to write to (def: "
507*44704f69SBart Van Assche             "/dev/null)\n"
508*44704f69SBart Van Assche             "                'of=.' also outputs to /dev/null\n"
509*44704f69SBart Van Assche             "    oflag       comma separated list from: [append,nocreat,\n"
510*44704f69SBart Van Assche             "                <<list from iflag>>]\n"
511*44704f69SBart Van Assche             "    seek        block position to start writing to OFILE\n"
512*44704f69SBart Van Assche             "    skip        block position to start reading from IFILE\n"
513*44704f69SBart Van Assche             "    --help|-h      output this usage message then exit\n"
514*44704f69SBart Van Assche             "    --verify|-x    do a verify (compare) operation [def: do a "
515*44704f69SBart Van Assche             "copy]\n"
516*44704f69SBart Van Assche             "    --version|-V   output version string then exit\n\n"
517*44704f69SBart Van Assche             "Copy IFILE to OFILE, similar to dd command. A comma separated "
518*44704f69SBart Van Assche             "list of files\n may be given for IFILE*, ditto for OFILE*. "
519*44704f69SBart Van Assche             "This utility is specialized for\nSCSI devices and uses the "
520*44704f69SBart Van Assche             "'multiple requests' (mrq) in a single invocation\nfacility in "
521*44704f69SBart Van Assche             "version 4 of the sg driver unless mrq=0. Usually one or both\n"
522*44704f69SBart Van Assche             "IFILE and OFILE will be sg devices. With the --verify option "
523*44704f69SBart Van Assche             "it does a\nverify/compare operation instead of a copy. This "
524*44704f69SBart Van Assche             "utility is Linux specific.\nUse '-hh', '-hhh', '-hhhh' or "
525*44704f69SBart Van Assche             "'-hhhhh' for more information.\n"
526*44704f69SBart Van Assche            );
527*44704f69SBart Van Assche     return;
528*44704f69SBart Van Assche page2:
529*44704f69SBart Van Assche     pr2serr("Syntax:  sg_mrq_dd [operands] [options]\n\n"
530*44704f69SBart Van Assche             "         the lesser used operands and option are:\n\n"
531*44704f69SBart Van Assche             "    bpt         is blocks_per_transfer (default is 128)\n"
532*44704f69SBart Van Assche             "    cdbsz       size of SCSI READ, WRITE or VERIFY cdb_s "
533*44704f69SBart Van Assche             "(default is 10)\n"
534*44704f69SBart Van Assche             "    cdl         command duration limits value 0 to 7 (def: "
535*44704f69SBart Van Assche             "0 (no cdl))\n"
536*44704f69SBart Van Assche             "    dio         is direct IO, 1->attempt, 0->indirect IO (def)\n"
537*44704f69SBart Van Assche             "    elemsz_kb=EKB    scatter gather list element size in "
538*44704f69SBart Van Assche             "kibibytes;\n"
539*44704f69SBart Van Assche             "                     must be power of two, >= page_size "
540*44704f69SBart Van Assche             "(typically 4)\n"
541*44704f69SBart Van Assche             "    ese=0|1     exit on secondary error when 1, else continue\n"
542*44704f69SBart Van Assche             "    fua         force unit access: 0->don't(def), 1->OFILE, "
543*44704f69SBart Van Assche             "2->IFILE,\n"
544*44704f69SBart Van Assche             "                3->OFILE+IFILE\n"
545*44704f69SBart Van Assche             "    ibs         IFILE logical block size, cannot differ from "
546*44704f69SBart Van Assche             "obs or bs\n"
547*44704f69SBart Van Assche             "    hipri       same as polled=NRQS; name 'hipri' is deprecated\n"
548*44704f69SBart Van Assche             "    mrq         NRQS is number of cmds placed in each sg "
549*44704f69SBart Van Assche             "ioctl\n"
550*44704f69SBart Van Assche             "                (def: 16). Does not set mrq hipri flag.\n"
551*44704f69SBart Van Assche             "                if mrq=0 does one-by-one, blocking "
552*44704f69SBart Van Assche             "ioctl(SG_IO)s\n"
553*44704f69SBart Van Assche             "    obs         OFILE logical block size, cannot differ from "
554*44704f69SBart Van Assche             "ibs or bs\n"
555*44704f69SBart Van Assche             "    ofreg       OFREG is regular file or pipe to send what is "
556*44704f69SBart Van Assche             "read from\n"
557*44704f69SBart Van Assche             "    polled      similar to mrq=NRQS operand but also sets "
558*44704f69SBart Van Assche             "polled flag\n"
559*44704f69SBart Van Assche             "                IFILE in the first half of each shared element\n"
560*44704f69SBart Van Assche             "    sdt         stall detection times: CRT[,ICT]. CRT: check "
561*44704f69SBart Van Assche             "repetition\n"
562*44704f69SBart Van Assche             "                time (after first) in seconds; ICT: initial "
563*44704f69SBart Van Assche             "check time\n"
564*44704f69SBart Van Assche             "                in milliseconds. Default: 3,300 . Use CRT=0 "
565*44704f69SBart Van Assche             "to disable\n"
566*44704f69SBart Van Assche             "    sync        0->no sync(def), 1->SYNCHRONIZE CACHE on OFILE "
567*44704f69SBart Van Assche             "after copy\n"
568*44704f69SBart Van Assche             "    thr         is number of threads, must be > 0, default 4, "
569*44704f69SBart Van Assche             "max 1024\n"
570*44704f69SBart Van Assche             "    time        0->no timing; 1/2->millisec/nanosec precision "
571*44704f69SBart Van Assche             "(def: 1);\n"
572*44704f69SBart Van Assche             "                TO is command timeout in seconds (def: 60)\n"
573*44704f69SBart Van Assche             "    verbose     increase verbosity (def: VERB=0)\n"
574*44704f69SBart Van Assche             "    --dry-run|-d     prepare but bypass copy/read\n"
575*44704f69SBart Van Assche             "    --prefetch|-p    with verify: do pre-fetch first\n"
576*44704f69SBart Van Assche             "    --verbose|-v     increase verbosity of utility\n\n"
577*44704f69SBart Van Assche             "Use '-hhh', '-hhhh' or '-hhhhh' for more information about "
578*44704f69SBart Van Assche             "flags.\n"
579*44704f69SBart Van Assche            );
580*44704f69SBart Van Assche     return;
581*44704f69SBart Van Assche page3:
582*44704f69SBart Van Assche     pr2serr("Syntax:  sg_mrq_dd [operands] [options]\n\n"
583*44704f69SBart Van Assche             "  where: 'iflag=<arg>' and 'oflag=<arg>' arguments are listed "
584*44704f69SBart Van Assche             "below:\n\n"
585*44704f69SBart Van Assche             "    00          use all zeros instead of if=IFILE (only in "
586*44704f69SBart Van Assche             "iflag)\n"
587*44704f69SBart Van Assche             "    00,ff       generates blocks that contain own (32 bit be) "
588*44704f69SBart Van Assche             "blk addr\n"
589*44704f69SBart Van Assche             "    append      append output to OFILE (assumes OFILE is "
590*44704f69SBart Van Assche             "regular file)\n"
591*44704f69SBart Van Assche             "    coe         continue of error (reading, fills with zeros)\n"
592*44704f69SBart Van Assche             "    dio         sets the SG_FLAG_DIRECT_IO in sg requests\n"
593*44704f69SBart Van Assche             "    direct      sets the O_DIRECT flag on open()\n"
594*44704f69SBart Van Assche             "    dpo         sets the DPO (disable page out) in SCSI READs "
595*44704f69SBart Van Assche             "and WRITEs\n"
596*44704f69SBart Van Assche             "    dsync       sets the O_SYNC flag on open()\n"
597*44704f69SBart Van Assche             "    excl        sets the O_EXCL flag on open()\n"
598*44704f69SBart Van Assche             "    ff          use all 0xff bytes instead of if=IFILE (only in "
599*44704f69SBart Van Assche             "iflag)\n"
600*44704f69SBart Van Assche             "    fua         sets the FUA (force unit access) in SCSI READs "
601*44704f69SBart Van Assche             "and WRITEs\n"
602*44704f69SBart Van Assche             "    hipri       same as 'polled'; name 'hipri' is deprecated\n"
603*44704f69SBart Van Assche             "    masync      set 'more async' flag on this sg device\n"
604*44704f69SBart Van Assche             "    mmap        setup mmap IO on IFILE or OFILE\n"
605*44704f69SBart Van Assche             "    mmap,mmap    when used twice, doesn't call munmap()\n"
606*44704f69SBart Van Assche             "    mout_if     set META_OUT_IF flag on control object\n"
607*44704f69SBart Van Assche             "    nocreat     will fail rather than create OFILE\n"
608*44704f69SBart Van Assche             "    nodur       turns off command duration calculations\n"
609*44704f69SBart Van Assche             "    no_thresh   skip checking per fd max data xfer size\n"
610*44704f69SBart Van Assche             "    order       require write ordering on sg->sg copy; only "
611*44704f69SBart Van Assche             "for oflag\n"
612*44704f69SBart Van Assche             "    polled      set POLLED flag and use blk_poll() for "
613*44704f69SBart Van Assche             "completions\n"
614*44704f69SBart Van Assche             "    qhead       queue new request at head of block queue\n"
615*44704f69SBart Van Assche             "    qtail       queue new request at tail of block queue (def: "
616*44704f69SBart Van Assche             "q at head)\n"
617*44704f69SBart Van Assche             "    random      use random data instead of if=IFILE (only in "
618*44704f69SBart Van Assche             "iflag)\n"
619*44704f69SBart Van Assche             "    same_fds    each thread of a IOFILE pair uses same fds\n"
620*44704f69SBart Van Assche             "    serial      serialize sg command execution (def: overlap)\n"
621*44704f69SBart Van Assche             "    wq_excl     set SG_CTL_FLAGM_EXCL_WAITQ on this sg fd\n"
622*44704f69SBart Van Assche             "\n"
623*44704f69SBart Van Assche             "Copies IFILE to OFILE (and to OFILE2 if given). If IFILE and "
624*44704f69SBart Van Assche             "OFILE are sg\ndevices 'shared' mode is selected. "
625*44704f69SBart Van Assche             "When sharing, the data stays in a\nsingle "
626*44704f69SBart Van Assche             "in-kernel buffer which is copied (or mmap-ed) to the user "
627*44704f69SBart Van Assche             "space\nif the 'ofreg=OFREG' is given. Use '-hhhh' or '-hhhhh' "
628*44704f69SBart Van Assche             "for more information.\n"
629*44704f69SBart Van Assche            );
630*44704f69SBart Van Assche     return;
631*44704f69SBart Van Assche page4:
632*44704f69SBart Van Assche     pr2serr("pack_id:\n"
633*44704f69SBart Van Assche             "These are ascending integers, starting at 1, associated with "
634*44704f69SBart Van Assche             "each issued\nSCSI command. When both IFILE and OFILE are sg "
635*44704f69SBart Van Assche             "devices, then the READ in\neach read-write pair is issued an "
636*44704f69SBart Van Assche             "even pack_id and its WRITE pair is\ngiven the pack_id one "
637*44704f69SBart Van Assche             "higher (i.e. an odd number). This enables a\n'dmesg -w' "
638*44704f69SBart Van Assche             "user to see that progress is being "
639*44704f69SBart Van Assche             "made.\n\n");
640*44704f69SBart Van Assche     pr2serr("Debugging:\n"
641*44704f69SBart Van Assche             "Apart from using one or more '--verbose' options which gets a "
642*44704f69SBart Van Assche             "bit noisy\n'dmesg -w' can give a good overview "
643*44704f69SBart Van Assche             "of what is happening.\nThat does a sg driver object tree "
644*44704f69SBart Van Assche             "traversal that does minimal locking\nto make sure that each "
645*44704f69SBart Van Assche             "traversal is 'safe'. So it is important to note\nthe whole "
646*44704f69SBart Van Assche             "tree is not locked. This means for fast devices the overall\n"
647*44704f69SBart Van Assche             "tree state may change while the traversal is occurring. For "
648*44704f69SBart Van Assche             "example,\nit has been observed that both the read- and write- "
649*44704f69SBart Van Assche             "sides of a request\nshare show they are in 'active' state "
650*44704f69SBart Van Assche             "which should not be possible.\nIt occurs because the read-side "
651*44704f69SBart Van Assche             "probably jumped out of active state and\nthe write-side "
652*44704f69SBart Van Assche             "request entered it while some other nodes were being "
653*44704f69SBart Van Assche             "printed.\n\n");
654*44704f69SBart Van Assche     pr2serr("Busy state:\n"
655*44704f69SBart Van Assche             "Busy state (abbreviated to 'bsy' in the dmesg "
656*44704f69SBart Van Assche             "output)\nis entered during request setup and completion. It "
657*44704f69SBart Van Assche             "is intended to be\na temporary state. It should not block "
658*44704f69SBart Van Assche             "but does sometimes (e.g. in\nblock_get_request()). Even so "
659*44704f69SBart Van Assche             "that blockage should be short and if not\nthere is a "
660*44704f69SBart Van Assche             "problem.\n\n");
661*44704f69SBart Van Assche     pr2serr("--verify :\n"
662*44704f69SBart Van Assche             "For comparing IFILE with OFILE. Does repeated sequences of: "
663*44704f69SBart Van Assche             "READ(ifile)\nand uses data returned to send to VERIFY(ofile, "
664*44704f69SBart Van Assche             "BYTCHK=1). So the OFILE\ndevice/disk is doing the actual "
665*44704f69SBart Van Assche             "comparison. Stops on first miscompare\nunless oflag=coe is "
666*44704f69SBart Van Assche             "given\n\n");
667*44704f69SBart Van Assche     pr2serr("--prefetch :\n"
668*44704f69SBart Van Assche             "Used with --verify option. Prepends a PRE-FETCH(ofile, IMMED) "
669*44704f69SBart Van Assche             "to verify\nsequence. This should speed the trailing VERIFY by "
670*44704f69SBart Van Assche             "making sure that\nthe data it needs for the comparison is "
671*44704f69SBart Van Assche             "already in its cache.\n");
672*44704f69SBart Van Assche     return;
673*44704f69SBart Van Assche page5:
674*44704f69SBart Van Assche     pr2serr("       IFILE and/or OFILE lists\n\n"
675*44704f69SBart Van Assche             "For dd, its if= operand takes a single file (or device), ditto "
676*44704f69SBart Van Assche             "for the of=\noperand. This utility extends that to "
677*44704f69SBart Van Assche             "allowing a comma separated list\nof files. Ideally if multiple "
678*44704f69SBart Van Assche             "IFILEs are given, the same number of OFILEs\nshould be given. "
679*44704f69SBart Van Assche             "Simple expansions occur to make the list lengths equal\n"
680*44704f69SBart Van Assche             "(e.g. if 5 IFILEs are given but no OFILEs, then OFILEs is "
681*44704f69SBart Van Assche             "expanded to 5\n'/dev/null' files). IFILE,OFILE pairs with "
682*44704f69SBart Van Assche             "the same list position are\ncalled a 'slice'. Each slice is "
683*44704f69SBart Van Assche             "processed (i.e. copy or verify) in one or\nmore threads. The "
684*44704f69SBart Van Assche             "number of threads must be >= the number of slices. Best\nif "
685*44704f69SBart Van Assche             "the number of threads is an integer multiple of the number of "
686*44704f69SBart Van Assche             "slices.\nThe file type of multiple IFILEs must be the same, "
687*44704f69SBart Van Assche             "ditto for OFILEs.\nSupport for slices is for testing rather "
688*44704f69SBart Van Assche             "than a general mechanism.\n");
689*44704f69SBart Van Assche }
690*44704f69SBart Van Assche 
691*44704f69SBart Van Assche static void
lk_print_command_len(const char * prefix,uint8_t * cmdp,int len,bool lock)692*44704f69SBart Van Assche lk_print_command_len(const char *prefix, uint8_t * cmdp, int len, bool lock)
693*44704f69SBart Van Assche {
694*44704f69SBart Van Assche     if (lock) {
695*44704f69SBart Van Assche         lock_guard<mutex> lk(strerr_mut);
696*44704f69SBart Van Assche 
697*44704f69SBart Van Assche         if (prefix && *prefix)
698*44704f69SBart Van Assche             fputs(prefix, stderr);
699*44704f69SBart Van Assche         sg_print_command_len(cmdp, len);
700*44704f69SBart Van Assche     } else {
701*44704f69SBart Van Assche         if (prefix && *prefix)
702*44704f69SBart Van Assche             fputs(prefix, stderr);
703*44704f69SBart Van Assche         sg_print_command_len(cmdp, len);
704*44704f69SBart Van Assche     }
705*44704f69SBart Van Assche }
706*44704f69SBart Van Assche 
707*44704f69SBart Van Assche static void
lk_chk_n_print4(const char * leadin,const struct sg_io_v4 * h4p,bool raw_sinfo)708*44704f69SBart Van Assche lk_chk_n_print4(const char * leadin, const struct sg_io_v4 * h4p,
709*44704f69SBart Van Assche                 bool raw_sinfo)
710*44704f69SBart Van Assche {
711*44704f69SBart Van Assche     lock_guard<mutex> lk(strerr_mut);
712*44704f69SBart Van Assche 
713*44704f69SBart Van Assche     if (h4p->usr_ptr) {
714*44704f69SBart Van Assche         const cdb_arr_t * cdbp = (const cdb_arr_t *)h4p->usr_ptr;
715*44704f69SBart Van Assche 
716*44704f69SBart Van Assche         pr2serr("Failed cdb: ");
717*44704f69SBart Van Assche         sg_print_command(cdbp->data());
718*44704f69SBart Van Assche     } else
719*44704f69SBart Van Assche         pr2serr("cdb: <null>\n");
720*44704f69SBart Van Assche     sg_linux_sense_print(leadin, h4p->device_status, h4p->transport_status,
721*44704f69SBart Van Assche                          h4p->driver_status, (const uint8_t *)h4p->response,
722*44704f69SBart Van Assche                          h4p->response_len, raw_sinfo);
723*44704f69SBart Van Assche }
724*44704f69SBart Van Assche 
725*44704f69SBart Van Assche static void
hex2stderr_lk(const uint8_t * b_str,int len,int no_ascii)726*44704f69SBart Van Assche hex2stderr_lk(const uint8_t * b_str, int len, int no_ascii)
727*44704f69SBart Van Assche {
728*44704f69SBart Van Assche     lock_guard<mutex> lk(strerr_mut);
729*44704f69SBart Van Assche 
730*44704f69SBart Van Assche     hex2stderr(b_str, len, no_ascii);
731*44704f69SBart Van Assche }
732*44704f69SBart Van Assche 
733*44704f69SBart Van Assche static int
system_wrapper(const char * cmd)734*44704f69SBart Van Assche system_wrapper(const char * cmd)
735*44704f69SBart Van Assche {
736*44704f69SBart Van Assche     int res;
737*44704f69SBart Van Assche 
738*44704f69SBart Van Assche     res = system(cmd);
739*44704f69SBart Van Assche     if (WIFSIGNALED(res) &&
740*44704f69SBart Van Assche         (WTERMSIG(res) == SIGINT || WTERMSIG(res) == SIGQUIT))
741*44704f69SBart Van Assche         raise(WTERMSIG(res));
742*44704f69SBart Van Assche     return WEXITSTATUS(res);
743*44704f69SBart Van Assche }
744*44704f69SBart Van Assche 
745*44704f69SBart Van Assche /* Flags decoded into abbreviations for those that are set, separated by
746*44704f69SBart Van Assche  * '|' . */
747*44704f69SBart Van Assche static char *
sg_flags_str(int flags,int b_len,char * b)748*44704f69SBart Van Assche sg_flags_str(int flags, int b_len, char * b)
749*44704f69SBart Van Assche {
750*44704f69SBart Van Assche     int n = 0;
751*44704f69SBart Van Assche 
752*44704f69SBart Van Assche     if ((b_len < 1) || (! b))
753*44704f69SBart Van Assche         return b;
754*44704f69SBart Van Assche     b[0] = '\0';
755*44704f69SBart Van Assche     if (SG_FLAG_DIRECT_IO & flags) {            /* 0x1 */
756*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "DIO|");
757*44704f69SBart Van Assche         if (n >= b_len)
758*44704f69SBart Van Assche             goto fini;
759*44704f69SBart Van Assche     }
760*44704f69SBart Van Assche     if (SG_FLAG_MMAP_IO & flags) {              /* 0x4 */
761*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "MMAP|");
762*44704f69SBart Van Assche         if (n >= b_len)
763*44704f69SBart Van Assche             goto fini;
764*44704f69SBart Van Assche     }
765*44704f69SBart Van Assche     if (SGV4_FLAG_YIELD_TAG & flags) {          /* 0x8 */
766*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "YTAG|");
767*44704f69SBart Van Assche         if (n >= b_len)
768*44704f69SBart Van Assche             goto fini;
769*44704f69SBart Van Assche     }
770*44704f69SBart Van Assche     if (SG_FLAG_Q_AT_TAIL & flags) {            /* 0x10 */
771*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "QTAI|");
772*44704f69SBart Van Assche         if (n >= b_len)
773*44704f69SBart Van Assche             goto fini;
774*44704f69SBart Van Assche     }
775*44704f69SBart Van Assche     if (SG_FLAG_Q_AT_HEAD & flags) {            /* 0x20 */
776*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "QHEA|");
777*44704f69SBart Van Assche         if (n >= b_len)
778*44704f69SBart Van Assche             goto fini;
779*44704f69SBart Van Assche     }
780*44704f69SBart Van Assche     if (SGV4_FLAG_DOUT_OFFSET & flags) {        /* 0x40 */
781*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "DOFF|");
782*44704f69SBart Van Assche         if (n >= b_len)
783*44704f69SBart Van Assche             goto fini;
784*44704f69SBart Van Assche     }
785*44704f69SBart Van Assche     if (SGV4_FLAG_EVENTFD & flags) {           /* 0x80 */
786*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "EVFD|");
787*44704f69SBart Van Assche         if (n >= b_len)
788*44704f69SBart Van Assche             goto fini;
789*44704f69SBart Van Assche     }
790*44704f69SBart Van Assche     if (SGV4_FLAG_COMPLETE_B4 & flags) {        /* 0x100 */
791*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "CPL_B4|");
792*44704f69SBart Van Assche         if (n >= b_len)
793*44704f69SBart Van Assche             goto fini;
794*44704f69SBart Van Assche     }
795*44704f69SBart Van Assche     if (SGV4_FLAG_SIGNAL & flags) {       /* 0x200 */
796*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "SIGNAL|");
797*44704f69SBart Van Assche         if (n >= b_len)
798*44704f69SBart Van Assche             goto fini;
799*44704f69SBart Van Assche     }
800*44704f69SBart Van Assche     if (SGV4_FLAG_IMMED & flags) {              /* 0x400 */
801*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "IMM|");
802*44704f69SBart Van Assche         if (n >= b_len)
803*44704f69SBart Van Assche             goto fini;
804*44704f69SBart Van Assche     }
805*44704f69SBart Van Assche     if (SGV4_FLAG_POLLED & flags) {             /* 0x800 */
806*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "POLLED|");
807*44704f69SBart Van Assche         if (n >= b_len)
808*44704f69SBart Van Assche             goto fini;
809*44704f69SBart Van Assche     }
810*44704f69SBart Van Assche     if (SGV4_FLAG_STOP_IF & flags) {            /* 0x1000 */
811*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "STOPIF|");
812*44704f69SBart Van Assche         if (n >= b_len)
813*44704f69SBart Van Assche             goto fini;
814*44704f69SBart Van Assche     }
815*44704f69SBart Van Assche     if (SGV4_FLAG_DEV_SCOPE & flags) {          /* 0x2000 */
816*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "DEV_SC|");
817*44704f69SBart Van Assche         if (n >= b_len)
818*44704f69SBart Van Assche             goto fini;
819*44704f69SBart Van Assche     }
820*44704f69SBart Van Assche     if (SGV4_FLAG_SHARE & flags) {              /* 0x4000 */
821*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "SHARE|");
822*44704f69SBart Van Assche         if (n >= b_len)
823*44704f69SBart Van Assche             goto fini;
824*44704f69SBart Van Assche     }
825*44704f69SBart Van Assche     if (SGV4_FLAG_DO_ON_OTHER & flags) {        /* 0x8000 */
826*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "DO_OTH|");
827*44704f69SBart Van Assche         if (n >= b_len)
828*44704f69SBart Van Assche             goto fini;
829*44704f69SBart Van Assche     }
830*44704f69SBart Van Assche     if (SGV4_FLAG_NO_DXFER & flags) {          /* 0x10000 */
831*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "NOXFER|");
832*44704f69SBart Van Assche         if (n >= b_len)
833*44704f69SBart Van Assche             goto fini;
834*44704f69SBart Van Assche     }
835*44704f69SBart Van Assche     if (SGV4_FLAG_KEEP_SHARE & flags) {        /* 0x20000 */
836*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "KEEP_SH|");
837*44704f69SBart Van Assche         if (n >= b_len)
838*44704f69SBart Van Assche             goto fini;
839*44704f69SBart Van Assche     }
840*44704f69SBart Van Assche     if (SGV4_FLAG_MULTIPLE_REQS & flags) {     /* 0x40000 */
841*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "MRQS|");
842*44704f69SBart Van Assche         if (n >= b_len)
843*44704f69SBart Van Assche             goto fini;
844*44704f69SBart Van Assche     }
845*44704f69SBart Van Assche     if (SGV4_FLAG_ORDERED_WR & flags) {        /* 0x80000 */
846*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "OWR|");
847*44704f69SBart Van Assche         if (n >= b_len)
848*44704f69SBart Van Assche             goto fini;
849*44704f69SBart Van Assche     }
850*44704f69SBart Van Assche     if (SGV4_FLAG_REC_ORDER & flags) {         /* 0x100000 */
851*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "REC_O|");
852*44704f69SBart Van Assche         if (n >= b_len)
853*44704f69SBart Van Assche             goto fini;
854*44704f69SBart Van Assche     }
855*44704f69SBart Van Assche     if (SGV4_FLAG_META_OUT_IF & flags) {       /* 0x200000 */
856*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "MOUT_IF|");
857*44704f69SBart Van Assche         if (n >= b_len)
858*44704f69SBart Van Assche             goto fini;
859*44704f69SBart Van Assche     }
860*44704f69SBart Van Assche     if (0 == n)
861*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "<none>");
862*44704f69SBart Van Assche fini:
863*44704f69SBart Van Assche     if (n < b_len) {    /* trim trailing '\' */
864*44704f69SBart Van Assche         if ('|' == b[n - 1])
865*44704f69SBart Van Assche             b[n - 1] = '\0';
866*44704f69SBart Van Assche     } else if ('|' == b[b_len - 1])
867*44704f69SBart Van Assche         b[b_len - 1] = '\0';
868*44704f69SBart Van Assche     return b;
869*44704f69SBart Van Assche }
870*44704f69SBart Van Assche 
871*44704f69SBart Van Assche /* Info field decoded into abbreviations for those bits that are set,
872*44704f69SBart Van Assche  * separated by '|' . */
873*44704f69SBart Van Assche static char *
sg_info_str(int info,int b_len,char * b)874*44704f69SBart Van Assche sg_info_str(int info, int b_len, char * b)
875*44704f69SBart Van Assche {
876*44704f69SBart Van Assche     int n = 0;
877*44704f69SBart Van Assche 
878*44704f69SBart Van Assche     if ((b_len < 1) || (! b))
879*44704f69SBart Van Assche         return b;
880*44704f69SBart Van Assche     b[0] = '\0';
881*44704f69SBart Van Assche     if (SG_INFO_CHECK & info) {               /* 0x1 */
882*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "CHK|");
883*44704f69SBart Van Assche         if (n >= b_len)
884*44704f69SBart Van Assche             goto fini;
885*44704f69SBart Van Assche     }
886*44704f69SBart Van Assche     if (SG_INFO_DIRECT_IO & info) {           /* 0x2 */
887*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "DIO|");
888*44704f69SBart Van Assche         if (n >= b_len)
889*44704f69SBart Van Assche             goto fini;
890*44704f69SBart Van Assche     }
891*44704f69SBart Van Assche     if (SG_INFO_MIXED_IO & info) {            /* 0x4 */
892*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "MIO|");
893*44704f69SBart Van Assche         if (n >= b_len)
894*44704f69SBart Van Assche             goto fini;
895*44704f69SBart Van Assche     }
896*44704f69SBart Van Assche     if (SG_INFO_DEVICE_DETACHING & info) {    /* 0x8 */
897*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "DETA|");
898*44704f69SBart Van Assche         if (n >= b_len)
899*44704f69SBart Van Assche             goto fini;
900*44704f69SBart Van Assche     }
901*44704f69SBart Van Assche     if (SG_INFO_ABORTED & info) {             /* 0x10 */
902*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "ABRT|");
903*44704f69SBart Van Assche         if (n >= b_len)
904*44704f69SBart Van Assche             goto fini;
905*44704f69SBart Van Assche     }
906*44704f69SBart Van Assche     if (SG_INFO_MRQ_FINI & info) {            /* 0x20 */
907*44704f69SBart Van Assche         n += sg_scnpr(b + n, b_len - n, "MRQF|");
908*44704f69SBart Van Assche         if (n >= b_len)
909*44704f69SBart Van Assche             goto fini;
910*44704f69SBart Van Assche     }
911*44704f69SBart Van Assche fini:
912*44704f69SBart Van Assche     if (n < b_len) {    /* trim trailing '\' */
913*44704f69SBart Van Assche         if ('|' == b[n - 1])
914*44704f69SBart Van Assche             b[n - 1] = '\0';
915*44704f69SBart Van Assche     } else if ('|' == b[b_len - 1])
916*44704f69SBart Van Assche         b[b_len - 1] = '\0';
917*44704f69SBart Van Assche     return b;
918*44704f69SBart Van Assche }
919*44704f69SBart Van Assche 
920*44704f69SBart Van Assche static void
v4hdr_out_lk(const char * leadin,const sg_io_v4 * h4p,int id,bool chk_info)921*44704f69SBart Van Assche v4hdr_out_lk(const char * leadin, const sg_io_v4 * h4p, int id, bool chk_info)
922*44704f69SBart Van Assche {
923*44704f69SBart Van Assche     lock_guard<mutex> lk(strerr_mut);
924*44704f69SBart Van Assche     char b[80];
925*44704f69SBart Van Assche 
926*44704f69SBart Van Assche     if (leadin)
927*44704f69SBart Van Assche         pr2serr("%s [id=%d]:\n", leadin, id);
928*44704f69SBart Van Assche     if (('Q' != h4p->guard) || (0 != h4p->protocol) ||
929*44704f69SBart Van Assche         (0 != h4p->subprotocol))
930*44704f69SBart Van Assche         pr2serr("  <<<sg_io_v4 _NOT_ properly set>>>\n");
931*44704f69SBart Van Assche     pr2serr("  pointers: cdb=%s  sense=%s  din=%p  dout=%p\n",
932*44704f69SBart Van Assche             (h4p->request ? "y" : "NULL"), (h4p->response ? "y" : "NULL"),
933*44704f69SBart Van Assche             (void *)h4p->din_xferp, (void *)h4p->dout_xferp);
934*44704f69SBart Van Assche     pr2serr("  lengths: cdb=%u  sense=%u  din=%u  dout=%u\n",
935*44704f69SBart Van Assche             h4p->request_len, h4p->max_response_len, h4p->din_xfer_len,
936*44704f69SBart Van Assche              h4p->dout_xfer_len);
937*44704f69SBart Van Assche     pr2serr("  flags=0x%x  request_extra{pack_id}=%d\n",
938*44704f69SBart Van Assche             h4p->flags, h4p->request_extra);
939*44704f69SBart Van Assche     pr2serr("  flags set: %s\n", sg_flags_str(h4p->flags, sizeof(b), b));
940*44704f69SBart Van Assche     pr2serr(" %s OUT:\n", leadin);
941*44704f69SBart Van Assche     pr2serr("  response_len=%d driver/transport/device_status="
942*44704f69SBart Van Assche             "0x%x/0x%x/0x%x\n", h4p->response_len, h4p->driver_status,
943*44704f69SBart Van Assche             h4p->transport_status, h4p->device_status);
944*44704f69SBart Van Assche     pr2serr("  info=0x%x  din_resid=%u  dout_resid=%u  spare_out=%u  "
945*44704f69SBart Van Assche             "dur=%u\n",
946*44704f69SBart Van Assche             h4p->info, h4p->din_resid, h4p->dout_resid, h4p->spare_out,
947*44704f69SBart Van Assche             h4p->duration);
948*44704f69SBart Van Assche     if (chk_info && (SG_INFO_CHECK & h4p->info))
949*44704f69SBart Van Assche         pr2serr("  >>>> info: %s\n", sg_info_str(h4p->info, sizeof(b), b));
950*44704f69SBart Van Assche }
951*44704f69SBart Van Assche 
952*44704f69SBart Van Assche static void
fetch_sg_version(void)953*44704f69SBart Van Assche fetch_sg_version(void)
954*44704f69SBart Van Assche {
955*44704f69SBart Van Assche     FILE * fp;
956*44704f69SBart Van Assche     char b[96];
957*44704f69SBart Van Assche 
958*44704f69SBart Van Assche     have_sg_version = false;
959*44704f69SBart Van Assche     sg_version = 0;
960*44704f69SBart Van Assche     fp = fopen(PROC_SCSI_SG_VERSION, "r");
961*44704f69SBart Van Assche     if (fp && fgets(b, sizeof(b) - 1, fp)) {
962*44704f69SBart Van Assche         if (1 == sscanf(b, "%d", &sg_version))
963*44704f69SBart Van Assche             have_sg_version = !!sg_version;
964*44704f69SBart Van Assche     } else {
965*44704f69SBart Van Assche         int j, k, l;
966*44704f69SBart Van Assche 
967*44704f69SBart Van Assche         if (fp)
968*44704f69SBart Van Assche             fclose(fp);
969*44704f69SBart Van Assche         fp = fopen(SYS_SCSI_SG_VERSION, "r");
970*44704f69SBart Van Assche         if (fp && fgets(b, sizeof(b) - 1, fp)) {
971*44704f69SBart Van Assche             if (3 == sscanf(b, "%d.%d.%d", &j, &k, &l)) {
972*44704f69SBart Van Assche                 sg_version = (j * 10000) + (k * 100) + l;
973*44704f69SBart Van Assche                 have_sg_version = !!sg_version;
974*44704f69SBart Van Assche             }
975*44704f69SBart Van Assche         }
976*44704f69SBart Van Assche         if (NULL == fp)
977*44704f69SBart Van Assche                 pr2serr("The sg driver may not be loaded\n");
978*44704f69SBart Van Assche     }
979*44704f69SBart Van Assche     if (fp)
980*44704f69SBart Van Assche         fclose(fp);
981*44704f69SBart Van Assche }
982*44704f69SBart Van Assche 
983*44704f69SBart Van Assche static void
calc_duration_throughput(int contin)984*44704f69SBart Van Assche calc_duration_throughput(int contin)
985*44704f69SBart Van Assche {
986*44704f69SBart Van Assche     struct timeval end_tm, res_tm;
987*44704f69SBart Van Assche     double a, b;
988*44704f69SBart Van Assche 
989*44704f69SBart Van Assche     gettimeofday(&end_tm, NULL);
990*44704f69SBart Van Assche     res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
991*44704f69SBart Van Assche     res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
992*44704f69SBart Van Assche     if (res_tm.tv_usec < 0) {
993*44704f69SBart Van Assche         --res_tm.tv_sec;
994*44704f69SBart Van Assche         res_tm.tv_usec += 1000000;
995*44704f69SBart Van Assche     }
996*44704f69SBart Van Assche     a = res_tm.tv_sec;
997*44704f69SBart Van Assche     a += (0.000001 * res_tm.tv_usec);
998*44704f69SBart Van Assche 
999*44704f69SBart Van Assche     b = 0.0;
1000*44704f69SBart Van Assche     for (auto && cvp : gcoll.cp_ver_arr) {
1001*44704f69SBart Van Assche         if (cvp.state == cp_ver_pair_t::my_state::empty)
1002*44704f69SBart Van Assche             break;
1003*44704f69SBart Van Assche         b += (double)(cvp.dd_count - cvp.out_rem_count.load());
1004*44704f69SBart Van Assche     }
1005*44704f69SBart Van Assche     b *= (double)gcoll.bs;
1006*44704f69SBart Van Assche     pr2serr("time to %s data %s %d.%06d secs",
1007*44704f69SBart Van Assche             (gcoll.verify ? "verify" : "copy"), (contin ? "so far" : "was"),
1008*44704f69SBart Van Assche             (int)res_tm.tv_sec, (int)res_tm.tv_usec);
1009*44704f69SBart Van Assche     if ((a > 0.00001) && (b > 511))
1010*44704f69SBart Van Assche         pr2serr(", %.2f MB/sec\n", b / (a * 1000000.0));
1011*44704f69SBart Van Assche     else
1012*44704f69SBart Van Assche         pr2serr("\n");
1013*44704f69SBart Van Assche }
1014*44704f69SBart Van Assche 
1015*44704f69SBart Van Assche static void
print_stats(const char * str)1016*44704f69SBart Van Assche print_stats(const char * str)
1017*44704f69SBart Van Assche {
1018*44704f69SBart Van Assche     bool show_slice = ((gcoll.cp_ver_arr.size() > 1) &&
1019*44704f69SBart Van Assche                        (gcoll.cp_ver_arr[1].state !=
1020*44704f69SBart Van Assche                         cp_ver_pair_t::my_state::empty));
1021*44704f69SBart Van Assche     int k = 0;
1022*44704f69SBart Van Assche     int64_t infull, outfull;
1023*44704f69SBart Van Assche 
1024*44704f69SBart Van Assche     for (auto && cvp : gcoll.cp_ver_arr) {
1025*44704f69SBart Van Assche         ++k;
1026*44704f69SBart Van Assche         if (cvp.state == cp_ver_pair_t::my_state::empty)
1027*44704f69SBart Van Assche             break;
1028*44704f69SBart Van Assche         if (cvp.state == cp_ver_pair_t::my_state::ignore) {
1029*44704f69SBart Van Assche             pr2serr(">>> IGNORING slice: %d\n", k);
1030*44704f69SBart Van Assche             continue;
1031*44704f69SBart Van Assche         }
1032*44704f69SBart Van Assche         if (show_slice)
1033*44704f69SBart Van Assche             pr2serr(">>> slice: %d\n", k);
1034*44704f69SBart Van Assche         if (0 != cvp.out_rem_count.load())
1035*44704f69SBart Van Assche             pr2serr("  remaining block count=%" PRId64 "\n",
1036*44704f69SBart Van Assche                     cvp.out_rem_count.load());
1037*44704f69SBart Van Assche         infull = cvp.dd_count - cvp.in_rem_count.load();
1038*44704f69SBart Van Assche         pr2serr("%s%" PRId64 "+%d records in\n", str,
1039*44704f69SBart Van Assche                 infull, cvp.in_partial.load());
1040*44704f69SBart Van Assche 
1041*44704f69SBart Van Assche         if (cvp.out_type == FT_DEV_NULL)
1042*44704f69SBart Van Assche             pr2serr("%s0+0 records out\n", str);
1043*44704f69SBart Van Assche         else {
1044*44704f69SBart Van Assche             outfull = cvp.dd_count - cvp.out_rem_count.load();
1045*44704f69SBart Van Assche             pr2serr("%s%" PRId64 "+%d records %s\n", str,
1046*44704f69SBart Van Assche                     outfull, cvp.out_partial.load(),
1047*44704f69SBart Van Assche                     (gcoll.verify ? "verified" : "out"));
1048*44704f69SBart Van Assche         }
1049*44704f69SBart Van Assche     }
1050*44704f69SBart Van Assche }
1051*44704f69SBart Van Assche 
1052*44704f69SBart Van Assche static void
interrupt_handler(int sig)1053*44704f69SBart Van Assche interrupt_handler(int sig)
1054*44704f69SBart Van Assche {
1055*44704f69SBart Van Assche     struct sigaction sigact;
1056*44704f69SBart Van Assche 
1057*44704f69SBart Van Assche     sigact.sa_handler = SIG_DFL;
1058*44704f69SBart Van Assche     sigemptyset(&sigact.sa_mask);
1059*44704f69SBart Van Assche     sigact.sa_flags = 0;
1060*44704f69SBart Van Assche     sigaction(sig, &sigact, NULL);
1061*44704f69SBart Van Assche     pr2serr("Interrupted by signal,");
1062*44704f69SBart Van Assche     if (do_time > 0)
1063*44704f69SBart Van Assche         calc_duration_throughput(0);
1064*44704f69SBart Van Assche     print_stats("");
1065*44704f69SBart Van Assche     kill(getpid(), sig);
1066*44704f69SBart Van Assche }
1067*44704f69SBart Van Assche 
1068*44704f69SBart Van Assche static void
siginfo_handler(int sig)1069*44704f69SBart Van Assche siginfo_handler(int sig)
1070*44704f69SBart Van Assche {
1071*44704f69SBart Van Assche     if (sig) { ; }      /* unused, dummy to suppress warning */
1072*44704f69SBart Van Assche     pr2serr("Progress report, continuing ...\n");
1073*44704f69SBart Van Assche     if (do_time > 0)
1074*44704f69SBart Van Assche         calc_duration_throughput(1);
1075*44704f69SBart Van Assche     print_stats("  ");
1076*44704f69SBart Van Assche }
1077*44704f69SBart Van Assche 
1078*44704f69SBart Van Assche /* Usually this signal (SIGUSR2) will be caught by the timed wait in the
1079*44704f69SBart Van Assche  * sig_listen_thread thread but some might slip through while the timed
1080*44704f69SBart Van Assche  * wait is being re-armed or after that thread is finished. This handler
1081*44704f69SBart Van Assche  * acts as a backstop. */
1082*44704f69SBart Van Assche static void
siginfo2_handler(int sig)1083*44704f69SBart Van Assche siginfo2_handler(int sig)
1084*44704f69SBart Van Assche {
1085*44704f69SBart Van Assche     if (sig) { ; }      /* unused, dummy to suppress warning */
1086*44704f69SBart Van Assche     ++num_fallthru_sigusr2;
1087*44704f69SBart Van Assche }
1088*44704f69SBart Van Assche 
1089*44704f69SBart Van Assche static void
install_handler(int sig_num,void (* sig_handler)(int sig))1090*44704f69SBart Van Assche install_handler(int sig_num, void (*sig_handler) (int sig))
1091*44704f69SBart Van Assche {
1092*44704f69SBart Van Assche     struct sigaction sigact;
1093*44704f69SBart Van Assche     sigaction (sig_num, NULL, &sigact);
1094*44704f69SBart Van Assche     if (sigact.sa_handler != SIG_IGN)
1095*44704f69SBart Van Assche     {
1096*44704f69SBart Van Assche         sigact.sa_handler = sig_handler;
1097*44704f69SBart Van Assche         sigemptyset (&sigact.sa_mask);
1098*44704f69SBart Van Assche         sigact.sa_flags = 0;
1099*44704f69SBart Van Assche         sigaction (sig_num, &sigact, NULL);
1100*44704f69SBart Van Assche     }
1101*44704f69SBart Van Assche }
1102*44704f69SBart Van Assche 
1103*44704f69SBart Van Assche /* Make safe_strerror() thread safe */
1104*44704f69SBart Van Assche static char *
tsafe_strerror(int code,char * ebp)1105*44704f69SBart Van Assche tsafe_strerror(int code, char * ebp)
1106*44704f69SBart Van Assche {
1107*44704f69SBart Van Assche     lock_guard<mutex> lk(strerr_mut);
1108*44704f69SBart Van Assche     char * cp;
1109*44704f69SBart Van Assche 
1110*44704f69SBart Van Assche     cp = safe_strerror(code);
1111*44704f69SBart Van Assche     strncpy(ebp, cp, STRERR_BUFF_LEN);
1112*44704f69SBart Van Assche     ebp[STRERR_BUFF_LEN - 1] = '\0';
1113*44704f69SBart Van Assche     return ebp;
1114*44704f69SBart Van Assche }
1115*44704f69SBart Van Assche 
1116*44704f69SBart Van Assche 
1117*44704f69SBart Van Assche static int
dd_filetype(const char * filename,off_t & st_size)1118*44704f69SBart Van Assche dd_filetype(const char * filename, off_t & st_size)
1119*44704f69SBart Van Assche {
1120*44704f69SBart Van Assche     struct stat st;
1121*44704f69SBart Van Assche     size_t len = strlen(filename);
1122*44704f69SBart Van Assche 
1123*44704f69SBart Van Assche     if ((1 == len) && ('.' == filename[0]))
1124*44704f69SBart Van Assche         return FT_DEV_NULL;
1125*44704f69SBart Van Assche     if (stat(filename, &st) < 0)
1126*44704f69SBart Van Assche         return FT_ERROR;
1127*44704f69SBart Van Assche     if (S_ISCHR(st.st_mode)) {
1128*44704f69SBart Van Assche         if ((MEM_MAJOR == major(st.st_rdev)) &&
1129*44704f69SBart Van Assche             ((DEV_NULL_MINOR_NUM == minor(st.st_rdev)) ||
1130*44704f69SBart Van Assche              (DEV_ZERO_MINOR_NUM == minor(st.st_rdev))))
1131*44704f69SBart Van Assche             return FT_DEV_NULL; /* treat /dev/null + /dev/zero the same */
1132*44704f69SBart Van Assche         if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
1133*44704f69SBart Van Assche             return FT_SG;
1134*44704f69SBart Van Assche         if (SCSI_TAPE_MAJOR == major(st.st_rdev))
1135*44704f69SBart Van Assche             return FT_ST;
1136*44704f69SBart Van Assche         return FT_CHAR;
1137*44704f69SBart Van Assche     } else if (S_ISBLK(st.st_mode))
1138*44704f69SBart Van Assche         return FT_BLOCK;
1139*44704f69SBart Van Assche     else if (S_ISFIFO(st.st_mode))
1140*44704f69SBart Van Assche         return FT_FIFO;
1141*44704f69SBart Van Assche     st_size = st.st_size;
1142*44704f69SBart Van Assche     return FT_OTHER;
1143*44704f69SBart Van Assche }
1144*44704f69SBart Van Assche 
1145*44704f69SBart Van Assche /* Returns reserved_buffer_size/mmap_size if success, else 0 for failure */
1146*44704f69SBart Van Assche static int
sg_prepare_resbuf(int fd,struct global_collection * clp,bool is_in,uint8_t ** mmpp)1147*44704f69SBart Van Assche sg_prepare_resbuf(int fd, struct global_collection *clp, bool is_in,
1148*44704f69SBart Van Assche                   uint8_t **mmpp)
1149*44704f69SBart Van Assche {
1150*44704f69SBart Van Assche     static bool done = false;
1151*44704f69SBart Van Assche     bool no_dur = is_in ? clp->in_flags.no_dur : clp->out_flags.no_dur;
1152*44704f69SBart Van Assche     bool masync = is_in ? clp->in_flags.masync : clp->out_flags.masync;
1153*44704f69SBart Van Assche     bool wq_excl = is_in ? clp->in_flags.wq_excl : clp->out_flags.wq_excl;
1154*44704f69SBart Van Assche     bool skip_thresh = is_in ? clp->in_flags.no_thresh :
1155*44704f69SBart Van Assche                                clp->out_flags.no_thresh;
1156*44704f69SBart Van Assche     int elem_sz = clp->elem_sz;
1157*44704f69SBart Van Assche     int res, t, num, err;
1158*44704f69SBart Van Assche     uint8_t *mmp;
1159*44704f69SBart Van Assche     struct sg_extended_info sei {};
1160*44704f69SBart Van Assche     struct sg_extended_info * seip = &sei;
1161*44704f69SBart Van Assche 
1162*44704f69SBart Van Assche     res = ioctl(fd, SG_GET_VERSION_NUM, &t);
1163*44704f69SBart Van Assche     if ((res < 0) || (t < 40000)) {
1164*44704f69SBart Van Assche         if (ioctl(fd, SG_GET_RESERVED_SIZE, &num) < 0) {
1165*44704f69SBart Van Assche             perror("SG_GET_RESERVED_SIZE ioctl failed");
1166*44704f69SBart Van Assche             return 0;
1167*44704f69SBart Van Assche         }
1168*44704f69SBart Van Assche         if (! done) {
1169*44704f69SBart Van Assche             done = true;
1170*44704f69SBart Van Assche             pr2serr_lk("%ssg driver prior to 4.0.00, reduced functionality\n",
1171*44704f69SBart Van Assche                        my_name);
1172*44704f69SBart Van Assche         }
1173*44704f69SBart Van Assche         goto bypass;
1174*44704f69SBart Van Assche     }
1175*44704f69SBart Van Assche     if (elem_sz >= 4096) {
1176*44704f69SBart Van Assche         seip->sei_rd_mask |= SG_SEIM_SGAT_ELEM_SZ;
1177*44704f69SBart Van Assche         res = ioctl(fd, SG_SET_GET_EXTENDED, seip);
1178*44704f69SBart Van Assche         if (res < 0)
1179*44704f69SBart Van Assche             pr2serr_lk("sg_mrq_dd: %s: SG_SET_GET_EXTENDED(SGAT_ELEM_SZ) rd "
1180*44704f69SBart Van Assche                        "error: %s\n", __func__, strerror(errno));
1181*44704f69SBart Van Assche         if (elem_sz != (int)seip->sgat_elem_sz) {
1182*44704f69SBart Van Assche             seip->sei_wr_mask |= SG_SEIM_SGAT_ELEM_SZ;
1183*44704f69SBart Van Assche             seip->sgat_elem_sz = elem_sz;
1184*44704f69SBart Van Assche             res = ioctl(fd, SG_SET_GET_EXTENDED, seip);
1185*44704f69SBart Van Assche             if (res < 0)
1186*44704f69SBart Van Assche                 pr2serr_lk("sg_mrq_dd: %s: SG_SET_GET_EXTENDED(SGAT_ELEM_SZ) "
1187*44704f69SBart Van Assche                            "wr error: %s\n", __func__, strerror(errno));
1188*44704f69SBart Van Assche         }
1189*44704f69SBart Van Assche     }
1190*44704f69SBart Van Assche     if (no_dur || masync || skip_thresh) {
1191*44704f69SBart Van Assche         seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS;
1192*44704f69SBart Van Assche         if (no_dur) {
1193*44704f69SBart Van Assche             seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_NO_DURATION;
1194*44704f69SBart Van Assche             seip->ctl_flags |= SG_CTL_FLAGM_NO_DURATION;
1195*44704f69SBart Van Assche         }
1196*44704f69SBart Van Assche         if (masync) {
1197*44704f69SBart Van Assche             seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_MORE_ASYNC;
1198*44704f69SBart Van Assche             seip->ctl_flags |= SG_CTL_FLAGM_MORE_ASYNC;
1199*44704f69SBart Van Assche         }
1200*44704f69SBart Van Assche         if (wq_excl) {
1201*44704f69SBart Van Assche             seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_EXCL_WAITQ;
1202*44704f69SBart Van Assche             seip->ctl_flags |= SG_CTL_FLAGM_EXCL_WAITQ;
1203*44704f69SBart Van Assche         }
1204*44704f69SBart Van Assche         if (skip_thresh) {
1205*44704f69SBart Van Assche             seip->tot_fd_thresh = 0;
1206*44704f69SBart Van Assche             sei.sei_wr_mask |= SG_SEIM_TOT_FD_THRESH;
1207*44704f69SBart Van Assche         }
1208*44704f69SBart Van Assche         res = ioctl(fd, SG_SET_GET_EXTENDED, seip);
1209*44704f69SBart Van Assche         if (res < 0)
1210*44704f69SBart Van Assche             pr2serr_lk("sg_mrq_dd: %s: SG_SET_GET_EXTENDED(NO_DURATION) "
1211*44704f69SBart Van Assche                        "error: %s\n", __func__, strerror(errno));
1212*44704f69SBart Van Assche     }
1213*44704f69SBart Van Assche bypass:
1214*44704f69SBart Van Assche     num = clp->bs * clp->bpt;
1215*44704f69SBart Van Assche     res = ioctl(fd, SG_SET_RESERVED_SIZE, &num);
1216*44704f69SBart Van Assche     if (res < 0) {
1217*44704f69SBart Van Assche         perror("sg_mrq_dd: SG_SET_RESERVED_SIZE error");
1218*44704f69SBart Van Assche         return 0;
1219*44704f69SBart Van Assche     } else {
1220*44704f69SBart Van Assche         int nn;
1221*44704f69SBart Van Assche 
1222*44704f69SBart Van Assche         res = ioctl(fd, SG_GET_RESERVED_SIZE, &nn);
1223*44704f69SBart Van Assche         if (res < 0) {
1224*44704f69SBart Van Assche             perror("sg_mrq_dd: SG_GET_RESERVED_SIZE error");
1225*44704f69SBart Van Assche             return 0;
1226*44704f69SBart Van Assche         }
1227*44704f69SBart Van Assche         if (nn < num) {
1228*44704f69SBart Van Assche             pr2serr_lk("%s: SG_GET_RESERVED_SIZE shows size truncated, "
1229*44704f69SBart Van Assche                        "wanted %d got %d\n", __func__, num, nn);
1230*44704f69SBart Van Assche             return 0;
1231*44704f69SBart Van Assche         }
1232*44704f69SBart Van Assche         if (mmpp) {
1233*44704f69SBart Van Assche             mmp = (uint8_t *)mmap(NULL, num, PROT_READ | PROT_WRITE,
1234*44704f69SBart Van Assche                                   MAP_SHARED, fd, 0);
1235*44704f69SBart Van Assche             if (MAP_FAILED == mmp) {
1236*44704f69SBart Van Assche                 err = errno;
1237*44704f69SBart Van Assche                 pr2serr_lk("sg_mrq_dd: %s: sz=%d, fd=%d, mmap() failed: %s\n",
1238*44704f69SBart Van Assche                            __func__, num, fd, strerror(err));
1239*44704f69SBart Van Assche                 return 0;
1240*44704f69SBart Van Assche             }
1241*44704f69SBart Van Assche             *mmpp = mmp;
1242*44704f69SBart Van Assche         }
1243*44704f69SBart Van Assche     }
1244*44704f69SBart Van Assche     t = 1;
1245*44704f69SBart Van Assche     res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t);
1246*44704f69SBart Van Assche     if (res < 0)
1247*44704f69SBart Van Assche         perror("sg_mrq_dd: SG_SET_FORCE_PACK_ID error");
1248*44704f69SBart Van Assche     if (clp->unit_nanosec) {
1249*44704f69SBart Van Assche         seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS;
1250*44704f69SBart Van Assche         seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_TIME_IN_NS;
1251*44704f69SBart Van Assche         seip->ctl_flags |= SG_CTL_FLAGM_TIME_IN_NS;
1252*44704f69SBart Van Assche         if (ioctl(fd, SG_SET_GET_EXTENDED, seip) < 0) {
1253*44704f69SBart Van Assche             res = -1;
1254*44704f69SBart Van Assche             pr2serr_lk("ioctl(EXTENDED(TIME_IN_NS)) failed, errno=%d %s\n",
1255*44704f69SBart Van Assche                        errno, strerror(errno));
1256*44704f69SBart Van Assche         }
1257*44704f69SBart Van Assche     }
1258*44704f69SBart Van Assche     if (clp->verbose) {
1259*44704f69SBart Van Assche         t = 1;
1260*44704f69SBart Van Assche         /* more info in the kernel log */
1261*44704f69SBart Van Assche         res = ioctl(fd, SG_SET_DEBUG, &t);
1262*44704f69SBart Van Assche         if (res < 0)
1263*44704f69SBart Van Assche             perror("sg_mrq_dd: SG_SET_DEBUG error");
1264*44704f69SBart Van Assche     }
1265*44704f69SBart Van Assche     return (res < 0) ? 0 : num;
1266*44704f69SBart Van Assche }
1267*44704f69SBart Van Assche 
1268*44704f69SBart Van Assche static int
sg_in_open(struct global_collection * clp,const string & inf,uint8_t ** mmpp,int * mmap_lenp)1269*44704f69SBart Van Assche sg_in_open(struct global_collection *clp, const string & inf, uint8_t **mmpp,
1270*44704f69SBart Van Assche            int * mmap_lenp)
1271*44704f69SBart Van Assche {
1272*44704f69SBart Van Assche     int fd, err, n;
1273*44704f69SBart Van Assche     int flags = O_RDWR;
1274*44704f69SBart Van Assche     char ebuff[EBUFF_SZ];
1275*44704f69SBart Van Assche     const char * fnp = inf.c_str();
1276*44704f69SBart Van Assche 
1277*44704f69SBart Van Assche     if (clp->in_flags.direct)
1278*44704f69SBart Van Assche         flags |= O_DIRECT;
1279*44704f69SBart Van Assche     if (clp->in_flags.excl)
1280*44704f69SBart Van Assche         flags |= O_EXCL;
1281*44704f69SBart Van Assche     if (clp->in_flags.dsync)
1282*44704f69SBart Van Assche         flags |= O_SYNC;
1283*44704f69SBart Van Assche 
1284*44704f69SBart Van Assche     if ((fd = open(fnp, flags)) < 0) {
1285*44704f69SBart Van Assche         err = errno;
1286*44704f69SBart Van Assche         snprintf(ebuff, EBUFF_SZ, "%s: could not open %s for sg reading",
1287*44704f69SBart Van Assche                  __func__, fnp);
1288*44704f69SBart Van Assche         perror(ebuff);
1289*44704f69SBart Van Assche         return -sg_convert_errno(err);
1290*44704f69SBart Van Assche     }
1291*44704f69SBart Van Assche     n = sg_prepare_resbuf(fd, clp, true, mmpp);
1292*44704f69SBart Van Assche     if (n <= 0) {
1293*44704f69SBart Van Assche         close(fd);
1294*44704f69SBart Van Assche         return -SG_LIB_FILE_ERROR;
1295*44704f69SBart Van Assche     }
1296*44704f69SBart Van Assche     if (mmap_lenp)
1297*44704f69SBart Van Assche         *mmap_lenp = n;
1298*44704f69SBart Van Assche     return fd;
1299*44704f69SBart Van Assche }
1300*44704f69SBart Van Assche 
1301*44704f69SBart Van Assche static int
sg_out_open(struct global_collection * clp,const string & outf,uint8_t ** mmpp,int * mmap_lenp)1302*44704f69SBart Van Assche sg_out_open(struct global_collection *clp, const string & outf,
1303*44704f69SBart Van Assche             uint8_t **mmpp, int * mmap_lenp)
1304*44704f69SBart Van Assche {
1305*44704f69SBart Van Assche     int fd, err, n;
1306*44704f69SBart Van Assche     int flags = O_RDWR;
1307*44704f69SBart Van Assche     char ebuff[EBUFF_SZ];
1308*44704f69SBart Van Assche     const char * fnp = outf.c_str();
1309*44704f69SBart Van Assche 
1310*44704f69SBart Van Assche     if (clp->out_flags.direct)
1311*44704f69SBart Van Assche         flags |= O_DIRECT;
1312*44704f69SBart Van Assche     if (clp->out_flags.excl)
1313*44704f69SBart Van Assche         flags |= O_EXCL;
1314*44704f69SBart Van Assche     if (clp->out_flags.dsync)
1315*44704f69SBart Van Assche         flags |= O_SYNC;
1316*44704f69SBart Van Assche 
1317*44704f69SBart Van Assche     if ((fd = open(fnp, flags)) < 0) {
1318*44704f69SBart Van Assche         err = errno;
1319*44704f69SBart Van Assche         snprintf(ebuff,  EBUFF_SZ, "%s: could not open %s for sg %s",
1320*44704f69SBart Van Assche                  __func__, fnp, (clp->verify ? "verifying" : "writing"));
1321*44704f69SBart Van Assche         perror(ebuff);
1322*44704f69SBart Van Assche         return -sg_convert_errno(err);
1323*44704f69SBart Van Assche     }
1324*44704f69SBart Van Assche     n = sg_prepare_resbuf(fd, clp, false, mmpp);
1325*44704f69SBart Van Assche     if (n <= 0) {
1326*44704f69SBart Van Assche         close(fd);
1327*44704f69SBart Van Assche         return -SG_LIB_FILE_ERROR;
1328*44704f69SBart Van Assche     }
1329*44704f69SBart Van Assche     if (mmap_lenp)
1330*44704f69SBart Van Assche         *mmap_lenp = n;
1331*44704f69SBart Van Assche     return fd;
1332*44704f69SBart Van Assche }
1333*44704f69SBart Van Assche 
1334*44704f69SBart Van Assche static int
reg_file_open(struct global_collection * clp,const string & fn_s,bool for_wr)1335*44704f69SBart Van Assche reg_file_open(struct global_collection *clp, const string & fn_s,
1336*44704f69SBart Van Assche               bool for_wr)
1337*44704f69SBart Van Assche {
1338*44704f69SBart Van Assche     int fd, flags;
1339*44704f69SBart Van Assche     char ebuff[EBUFF_SZ];
1340*44704f69SBart Van Assche 
1341*44704f69SBart Van Assche     if (for_wr) {
1342*44704f69SBart Van Assche         flags = O_WRONLY;
1343*44704f69SBart Van Assche         if (! clp->out_flags.nocreat)
1344*44704f69SBart Van Assche             flags |= O_CREAT;
1345*44704f69SBart Van Assche         if (clp->out_flags.append)
1346*44704f69SBart Van Assche             flags |= O_APPEND;
1347*44704f69SBart Van Assche     } else
1348*44704f69SBart Van Assche         flags = O_RDONLY;
1349*44704f69SBart Van Assche     if (clp->in_flags.direct)
1350*44704f69SBart Van Assche         flags |= O_DIRECT;
1351*44704f69SBart Van Assche     if (clp->in_flags.excl)
1352*44704f69SBart Van Assche         flags |= O_EXCL;
1353*44704f69SBart Van Assche     if (clp->in_flags.dsync)
1354*44704f69SBart Van Assche         flags |= O_SYNC;
1355*44704f69SBart Van Assche 
1356*44704f69SBart Van Assche     if (for_wr)
1357*44704f69SBart Van Assche         fd = open(fn_s.c_str(), flags, 0666);
1358*44704f69SBart Van Assche     else
1359*44704f69SBart Van Assche         fd = open(fn_s.c_str(), flags);
1360*44704f69SBart Van Assche     if (fd < 0) {
1361*44704f69SBart Van Assche         int err = errno;
1362*44704f69SBart Van Assche         snprintf(ebuff, EBUFF_SZ, "%scould not open %s for %sing ",
1363*44704f69SBart Van Assche                  my_name, fn_s.c_str(), (for_wr ? "writ" : "read"));
1364*44704f69SBart Van Assche         perror(ebuff);
1365*44704f69SBart Van Assche         return -err;
1366*44704f69SBart Van Assche     }
1367*44704f69SBart Van Assche     return fd;
1368*44704f69SBart Van Assche }
1369*44704f69SBart Van Assche 
1370*44704f69SBart Van Assche get_next_res_t
get_next(int desired_num_blks)1371*44704f69SBart Van Assche cp_ver_pair_t::get_next(int desired_num_blks)
1372*44704f69SBart Van Assche {
1373*44704f69SBart Van Assche     int64_t expected, desired;
1374*44704f69SBart Van Assche 
1375*44704f69SBart Van Assche     if (desired_num_blks <= 0) {
1376*44704f69SBart Van Assche         if (desired_num_blks < 0) {
1377*44704f69SBart Van Assche             if (next_count_pos.load() >= 0)     /* flag error detection */
1378*44704f69SBart Van Assche                 next_count_pos.store(desired_num_blks);
1379*44704f69SBart Van Assche         }
1380*44704f69SBart Van Assche         return make_pair(next_count_pos.load(), 0);
1381*44704f69SBart Van Assche     }
1382*44704f69SBart Van Assche 
1383*44704f69SBart Van Assche     expected = next_count_pos.load();
1384*44704f69SBart Van Assche     do {        /* allowed to race with other threads */
1385*44704f69SBart Van Assche         if (expected < 0)
1386*44704f69SBart Van Assche             return make_pair(0, (int)expected);
1387*44704f69SBart Van Assche         else if (expected >= dd_count)
1388*44704f69SBart Van Assche             return make_pair(expected, 0);      /* clean finish */
1389*44704f69SBart Van Assche         desired = expected + desired_num_blks;
1390*44704f69SBart Van Assche         if (desired > dd_count)
1391*44704f69SBart Van Assche             desired = dd_count;
1392*44704f69SBart Van Assche     } while (! next_count_pos.compare_exchange_strong(expected, desired));
1393*44704f69SBart Van Assche     return make_pair(expected, desired - expected);
1394*44704f69SBart Van Assche }
1395*44704f69SBart Van Assche 
1396*44704f69SBart Van Assche /* Return of 0 -> success, see sg_ll_read_capacity*() otherwise */
1397*44704f69SBart Van Assche static int
scsi_read_capacity(int sg_fd,int64_t * num_sect,int * sect_sz)1398*44704f69SBart Van Assche scsi_read_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
1399*44704f69SBart Van Assche {
1400*44704f69SBart Van Assche     int res;
1401*44704f69SBart Van Assche     uint8_t rcBuff[RCAP16_REPLY_LEN] = {};
1402*44704f69SBart Van Assche 
1403*44704f69SBart Van Assche     res = sg_ll_readcap_10(sg_fd, 0, 0, rcBuff, READ_CAP_REPLY_LEN, false, 0);
1404*44704f69SBart Van Assche     if (0 != res)
1405*44704f69SBart Van Assche         goto bad;
1406*44704f69SBart Van Assche 
1407*44704f69SBart Van Assche     if ((0xff == rcBuff[0]) && (0xff == rcBuff[1]) && (0xff == rcBuff[2]) &&
1408*44704f69SBart Van Assche         (0xff == rcBuff[3])) {
1409*44704f69SBart Van Assche 
1410*44704f69SBart Van Assche         res = sg_ll_readcap_16(sg_fd, 0, 0, rcBuff, RCAP16_REPLY_LEN, false,
1411*44704f69SBart Van Assche                                0);
1412*44704f69SBart Van Assche         if (0 != res)
1413*44704f69SBart Van Assche             goto bad;
1414*44704f69SBart Van Assche         *num_sect = sg_get_unaligned_be64(rcBuff + 0) + 1;
1415*44704f69SBart Van Assche         *sect_sz = sg_get_unaligned_be32(rcBuff + 8);
1416*44704f69SBart Van Assche     } else {
1417*44704f69SBart Van Assche         /* take care not to sign extend values > 0x7fffffff */
1418*44704f69SBart Van Assche         *num_sect = (int64_t)sg_get_unaligned_be32(rcBuff + 0) + 1;
1419*44704f69SBart Van Assche         *sect_sz = sg_get_unaligned_be32(rcBuff + 4);
1420*44704f69SBart Van Assche     }
1421*44704f69SBart Van Assche     return 0;
1422*44704f69SBart Van Assche bad:
1423*44704f69SBart Van Assche     *num_sect = 0;
1424*44704f69SBart Van Assche     *sect_sz = 0;
1425*44704f69SBart Van Assche     return res;
1426*44704f69SBart Van Assche }
1427*44704f69SBart Van Assche 
1428*44704f69SBart Van Assche /* Return of 0 -> success, -1 -> failure. BLKGETSIZE64, BLKGETSIZE and */
1429*44704f69SBart Van Assche /* BLKSSZGET macros problematic (from <linux/fs.h> or <sys/mount.h>). */
1430*44704f69SBart Van Assche static int
read_blkdev_capacity(int sg_fd,int64_t * num_sect,int * sect_sz)1431*44704f69SBart Van Assche read_blkdev_capacity(int sg_fd, int64_t * num_sect, int * sect_sz)
1432*44704f69SBart Van Assche {
1433*44704f69SBart Van Assche #ifdef BLKSSZGET
1434*44704f69SBart Van Assche     if ((ioctl(sg_fd, BLKSSZGET, sect_sz) < 0) && (*sect_sz > 0)) {
1435*44704f69SBart Van Assche         perror("BLKSSZGET ioctl error");
1436*44704f69SBart Van Assche         return -1;
1437*44704f69SBart Van Assche     } else {
1438*44704f69SBart Van Assche  #ifdef BLKGETSIZE64
1439*44704f69SBart Van Assche         uint64_t ull;
1440*44704f69SBart Van Assche 
1441*44704f69SBart Van Assche         if (ioctl(sg_fd, BLKGETSIZE64, &ull) < 0) {
1442*44704f69SBart Van Assche 
1443*44704f69SBart Van Assche             perror("BLKGETSIZE64 ioctl error");
1444*44704f69SBart Van Assche             return -1;
1445*44704f69SBart Van Assche         }
1446*44704f69SBart Van Assche         *num_sect = ((int64_t)ull / (int64_t)*sect_sz);
1447*44704f69SBart Van Assche  #else
1448*44704f69SBart Van Assche         unsigned long ul;
1449*44704f69SBart Van Assche 
1450*44704f69SBart Van Assche         if (ioctl(sg_fd, BLKGETSIZE, &ul) < 0) {
1451*44704f69SBart Van Assche             perror("BLKGETSIZE ioctl error");
1452*44704f69SBart Van Assche             return -1;
1453*44704f69SBart Van Assche         }
1454*44704f69SBart Van Assche         *num_sect = (int64_t)ul;
1455*44704f69SBart Van Assche  #endif
1456*44704f69SBart Van Assche     }
1457*44704f69SBart Van Assche     return 0;
1458*44704f69SBart Van Assche #else
1459*44704f69SBart Van Assche     *num_sect = 0;
1460*44704f69SBart Van Assche     *sect_sz = 0;
1461*44704f69SBart Van Assche     return -1;
1462*44704f69SBart Van Assche #endif
1463*44704f69SBart Van Assche }
1464*44704f69SBart Van Assche 
1465*44704f69SBart Van Assche static void
flag_all_stop(struct global_collection * clp)1466*44704f69SBart Van Assche flag_all_stop(struct global_collection * clp)
1467*44704f69SBart Van Assche {
1468*44704f69SBart Van Assche     for (auto && elem : clp->cp_ver_arr) {
1469*44704f69SBart Van Assche         if (elem.state == cp_ver_pair_t::my_state::empty)
1470*44704f69SBart Van Assche             break;
1471*44704f69SBart Van Assche         elem.next_count_pos.store(-1);
1472*44704f69SBart Van Assche     }
1473*44704f69SBart Van Assche }
1474*44704f69SBart Van Assche 
1475*44704f69SBart Van Assche /* Has an infinite loop doing a timed wait for any signals in signal_set.
1476*44704f69SBart Van Assche  * After each timeout (300 ms) checks if the most_recent_pack_id atomic
1477*44704f69SBart Van Assche  * integer has changed. If not after another two timeouts announces a stall
1478*44704f69SBart Van Assche  * has been detected. If shutting down atomic is true breaks out of loop and
1479*44704f69SBart Van Assche  * shuts down this thread. Other than that, this thread is normally cancelled
1480*44704f69SBart Van Assche  * by the main thread, after other threads have exited. */
1481*44704f69SBart Van Assche static void
sig_listen_thread(struct global_collection * clp)1482*44704f69SBart Van Assche sig_listen_thread(struct global_collection * clp)
1483*44704f69SBart Van Assche {
1484*44704f69SBart Van Assche     bool stall_reported = false;
1485*44704f69SBart Van Assche     int prev_pack_id = 0;
1486*44704f69SBart Van Assche     int sig_number, pack_id;
1487*44704f69SBart Van Assche     uint32_t ict_ms = (clp->sdt_ict ? clp->sdt_ict : DEF_SDT_ICT_MS);
1488*44704f69SBart Van Assche     struct timespec ts;
1489*44704f69SBart Van Assche     struct timespec * tsp = &ts;
1490*44704f69SBart Van Assche 
1491*44704f69SBart Van Assche     tsp->tv_sec = ict_ms / 1000;
1492*44704f69SBart Van Assche     tsp->tv_nsec = (ict_ms % 1000) * 1000 * 1000;   /* DEF_SDT_ICT_MS */
1493*44704f69SBart Van Assche     listen_t_tid = gettid();    // to facilitate sending SIGUSR2 to exit
1494*44704f69SBart Van Assche     while (1) {
1495*44704f69SBart Van Assche         sig_number = sigtimedwait(&signal_set, NULL, tsp);
1496*44704f69SBart Van Assche         if (sig_number < 0) {
1497*44704f69SBart Van Assche             int err = errno;
1498*44704f69SBart Van Assche 
1499*44704f69SBart Van Assche             /* EAGAIN implies a timeout */
1500*44704f69SBart Van Assche             if ((EAGAIN == err) && (clp->sdt_crt > 0)) {
1501*44704f69SBart Van Assche                 pack_id = clp->most_recent_pack_id.load();
1502*44704f69SBart Van Assche                 if ((pack_id > 0) && (pack_id == prev_pack_id)) {
1503*44704f69SBart Van Assche                     if (! stall_reported) {
1504*44704f69SBart Van Assche                         stall_reported = true;
1505*44704f69SBart Van Assche                         tsp->tv_sec = clp->sdt_crt;
1506*44704f69SBart Van Assche                         tsp->tv_nsec = 0;
1507*44704f69SBart Van Assche                         pr2serr_lk("%s: first stall at pack_id=%d detected\n",
1508*44704f69SBart Van Assche                                    __func__, pack_id);
1509*44704f69SBart Van Assche                     } else
1510*44704f69SBart Van Assche                         pr2serr_lk("%s: subsequent stall at pack_id=%d\n",
1511*44704f69SBart Van Assche                                    __func__, pack_id);
1512*44704f69SBart Van Assche                     // following command assumes linux bash or similar shell
1513*44704f69SBart Van Assche                     system_wrapper("cat /proc/scsi/sg/debug >> /dev/stderr\n");
1514*44704f69SBart Van Assche                     // system_wrapper("/usr/bin/dmesg\n");
1515*44704f69SBart Van Assche                 } else
1516*44704f69SBart Van Assche                     prev_pack_id = pack_id;
1517*44704f69SBart Van Assche             } else if (EAGAIN != err)
1518*44704f69SBart Van Assche                 pr2serr_lk("%s: sigtimedwait() errno=%d\n", __func__, err);
1519*44704f69SBart Van Assche         }
1520*44704f69SBart Van Assche         if (SIGINT == sig_number) {
1521*44704f69SBart Van Assche             pr2serr_lk("%sinterrupted by SIGINT\n", my_name);
1522*44704f69SBart Van Assche             flag_all_stop(clp);
1523*44704f69SBart Van Assche             shutting_down.store(true);
1524*44704f69SBart Van Assche             sigprocmask(SIG_SETMASK, &orig_signal_set, NULL);
1525*44704f69SBart Van Assche             raise(SIGINT);
1526*44704f69SBart Van Assche             break;
1527*44704f69SBart Van Assche         }
1528*44704f69SBart Van Assche         if (SIGUSR2 == sig_number) {
1529*44704f69SBart Van Assche             if (clp->verbose > 2)
1530*44704f69SBart Van Assche                 pr2serr_lk("%s: SIGUSR2 received\n", __func__);
1531*44704f69SBart Van Assche             break;
1532*44704f69SBart Van Assche         } if (shutting_down)
1533*44704f69SBart Van Assche             break;
1534*44704f69SBart Van Assche     }           /* end of while loop */
1535*44704f69SBart Van Assche     if (clp->verbose > 3)
1536*44704f69SBart Van Assche         pr2serr_lk("%s: exiting\n", __func__);
1537*44704f69SBart Van Assche }
1538*44704f69SBart Van Assche 
1539*44704f69SBart Van Assche static bool
sg_share_prepare(int write_side_fd,int read_side_fd,int id,bool vb_b)1540*44704f69SBart Van Assche sg_share_prepare(int write_side_fd, int read_side_fd, int id, bool vb_b)
1541*44704f69SBart Van Assche {
1542*44704f69SBart Van Assche     struct sg_extended_info sei {};
1543*44704f69SBart Van Assche     struct sg_extended_info * seip = &sei;
1544*44704f69SBart Van Assche 
1545*44704f69SBart Van Assche     seip->sei_wr_mask |= SG_SEIM_SHARE_FD;
1546*44704f69SBart Van Assche     seip->sei_rd_mask |= SG_SEIM_SHARE_FD;
1547*44704f69SBart Van Assche     seip->share_fd = read_side_fd;
1548*44704f69SBart Van Assche     if (ioctl(write_side_fd, SG_SET_GET_EXTENDED, seip) < 0) {
1549*44704f69SBart Van Assche         pr2serr_lk("tid=%d: ioctl(EXTENDED(shared_fd=%d), failed "
1550*44704f69SBart Van Assche                    "errno=%d %s\n", id, read_side_fd, errno,
1551*44704f69SBart Van Assche                    strerror(errno));
1552*44704f69SBart Van Assche         return false;
1553*44704f69SBart Van Assche     }
1554*44704f69SBart Van Assche     if (vb_b)
1555*44704f69SBart Van Assche         pr2serr_lk("%s: tid=%d: ioctl(EXTENDED(shared_fd)) ok, "
1556*44704f69SBart Van Assche                    "read_side_fd=%d, write_side_fd=%d\n", __func__, id,
1557*44704f69SBart Van Assche                    read_side_fd, write_side_fd);
1558*44704f69SBart Van Assche     return true;
1559*44704f69SBart Van Assche }
1560*44704f69SBart Van Assche 
1561*44704f69SBart Van Assche static void
sg_take_snap(int sg_fd,int id,bool vb_b)1562*44704f69SBart Van Assche sg_take_snap(int sg_fd, int id, bool vb_b)
1563*44704f69SBart Van Assche {
1564*44704f69SBart Van Assche     struct sg_extended_info sei {};
1565*44704f69SBart Van Assche     struct sg_extended_info * seip = &sei;
1566*44704f69SBart Van Assche 
1567*44704f69SBart Van Assche     seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS;
1568*44704f69SBart Van Assche     seip->sei_rd_mask |= SG_SEIM_CTL_FLAGS;
1569*44704f69SBart Van Assche     seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_SNAP_DEV;
1570*44704f69SBart Van Assche     seip->ctl_flags &= ~SG_CTL_FLAGM_SNAP_DEV;   /* 0 --> append */
1571*44704f69SBart Van Assche     if (ioctl(sg_fd, SG_SET_GET_EXTENDED, seip) < 0) {
1572*44704f69SBart Van Assche         pr2serr_lk("tid=%d: ioctl(EXTENDED(SNAP_DEV), failed errno=%d %s\n",
1573*44704f69SBart Van Assche                    id,  errno, strerror(errno));
1574*44704f69SBart Van Assche         return;
1575*44704f69SBart Van Assche     }
1576*44704f69SBart Van Assche     if (vb_b)
1577*44704f69SBart Van Assche         pr2serr_lk("tid=%d: ioctl(SNAP_DEV) ok\n", id);
1578*44704f69SBart Van Assche }
1579*44704f69SBart Van Assche 
1580*44704f69SBart Van Assche // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
1581*44704f69SBart Van Assche /* Each thread's "main" function */
1582*44704f69SBart Van Assche static void
read_write_thread(struct global_collection * clp,int thr_idx,int slice_idx,bool singleton)1583*44704f69SBart Van Assche read_write_thread(struct global_collection * clp, int thr_idx, int slice_idx,
1584*44704f69SBart Van Assche                   bool singleton)
1585*44704f69SBart Van Assche {
1586*44704f69SBart Van Assche     Rq_elem rel {};
1587*44704f69SBart Van Assche     Rq_elem * rep = &rel;
1588*44704f69SBart Van Assche     int n, sz, fd, vb, err, seg_blks;
1589*44704f69SBart Van Assche     int res = 0;
1590*44704f69SBart Van Assche     int num_sg = 0;
1591*44704f69SBart Van Assche     bool own_infd = false;
1592*44704f69SBart Van Assche     bool in_is_sg, in_mmap, out_is_sg, out_mmap;
1593*44704f69SBart Van Assche     bool own_outfd = false;
1594*44704f69SBart Van Assche     bool only_one_sg = false;
1595*44704f69SBart Van Assche     struct cp_ver_pair_t & cvp = clp->cp_ver_arr[slice_idx];
1596*44704f69SBart Van Assche     class scat_gath_iter i_sg_it(clp->i_sgl);
1597*44704f69SBart Van Assche     class scat_gath_iter o_sg_it(clp->o_sgl);
1598*44704f69SBart Van Assche     const string & inf = clp->inf_v[slice_idx];
1599*44704f69SBart Van Assche     const string & outf = clp->outf_v[slice_idx];
1600*44704f69SBart Van Assche     vector<cdb_arr_t> a_cdb;
1601*44704f69SBart Van Assche     vector<struct sg_io_v4> a_v4;
1602*44704f69SBart Van Assche 
1603*44704f69SBart Van Assche     vb = clp->verbose;
1604*44704f69SBart Van Assche     sz = clp->mrq_num * clp->bpt * clp->bs;
1605*44704f69SBart Van Assche     in_is_sg = (FT_SG == clp->in_type);
1606*44704f69SBart Van Assche     in_mmap = (in_is_sg && (clp->in_flags.mmap > 0));
1607*44704f69SBart Van Assche     out_is_sg = (FT_SG == clp->out_type);
1608*44704f69SBart Van Assche     out_mmap = (out_is_sg && (clp->out_flags.mmap > 0));
1609*44704f69SBart Van Assche     rep->clp = clp;
1610*44704f69SBart Van Assche     rep->id = thr_idx;
1611*44704f69SBart Van Assche     rep->bs = clp->bs;
1612*44704f69SBart Van Assche 
1613*44704f69SBart Van Assche     if (in_is_sg && out_is_sg)
1614*44704f69SBart Van Assche         rep->both_sg = true;
1615*44704f69SBart Van Assche     else if (in_is_sg || out_is_sg) {
1616*44704f69SBart Van Assche         only_one_sg = true;
1617*44704f69SBart Van Assche         if (in_is_sg)
1618*44704f69SBart Van Assche             rep->only_in_sg = true;
1619*44704f69SBart Van Assche         else
1620*44704f69SBart Van Assche             rep->only_out_sg = true;
1621*44704f69SBart Van Assche     }
1622*44704f69SBart Van Assche 
1623*44704f69SBart Van Assche     if (vb > 2) {
1624*44704f69SBart Van Assche         pr2serr_lk("%d <-- Starting worker thread, slice=%d\n", thr_idx,
1625*44704f69SBart Van Assche                    slice_idx);
1626*44704f69SBart Van Assche         if (vb > 3)
1627*44704f69SBart Van Assche             pr2serr_lk("   %s ---> %s\n", inf.c_str(), outf.c_str());
1628*44704f69SBart Van Assche     }
1629*44704f69SBart Van Assche     if (! (rep->both_sg || in_mmap)) {
1630*44704f69SBart Van Assche         rep->buffp = sg_memalign(sz, 0 /* page align */, &rep->alloc_bp,
1631*44704f69SBart Van Assche                                  false);
1632*44704f69SBart Van Assche         if (NULL == rep->buffp) {
1633*44704f69SBart Van Assche             pr2serr_lk("Failed to allocate %d bytes, exiting\n", sz);
1634*44704f69SBart Van Assche             return;
1635*44704f69SBart Van Assche         }
1636*44704f69SBart Van Assche     }
1637*44704f69SBart Van Assche     rep->infd = clp->in0fd;
1638*44704f69SBart Van Assche     rep->outfd = clp->out0fd;
1639*44704f69SBart Van Assche     rep->outregfd = clp->outregfd;
1640*44704f69SBart Van Assche     rep->rep_count = 0;
1641*44704f69SBart Van Assche     rep->in_follow_on = -1;
1642*44704f69SBart Van Assche     rep->out_follow_on = -1;
1643*44704f69SBart Van Assche     if (cvp.state == cp_ver_pair_t::my_state::init)
1644*44704f69SBart Van Assche         cvp.state = cp_ver_pair_t::my_state::underway;
1645*44704f69SBart Van Assche     if (FT_OTHER == cvp.in_type) {
1646*44704f69SBart Van Assche         fd = reg_file_open(clp, inf, false);
1647*44704f69SBart Van Assche         if (fd < 0) {
1648*44704f69SBart Van Assche             pr2serr_lk("[%d]: unable to open IFILE of slice=%d\n", thr_idx,
1649*44704f69SBart Van Assche                        slice_idx);
1650*44704f69SBart Van Assche             return;
1651*44704f69SBart Van Assche         }
1652*44704f69SBart Van Assche         rep->infd = fd;
1653*44704f69SBart Van Assche     }
1654*44704f69SBart Van Assche     if (FT_OTHER == cvp.out_type) {
1655*44704f69SBart Van Assche         fd = reg_file_open(clp, outf, true);
1656*44704f69SBart Van Assche         if (fd < 0) {
1657*44704f69SBart Van Assche             pr2serr_lk("[%d]: unable to open OFILE of slice=%d\n", thr_idx,
1658*44704f69SBart Van Assche                        slice_idx);
1659*44704f69SBart Van Assche             return;
1660*44704f69SBart Van Assche         }
1661*44704f69SBart Van Assche         rep->outfd = fd;
1662*44704f69SBart Van Assche     }
1663*44704f69SBart Van Assche 
1664*44704f69SBart Van Assche     if (rep->infd == rep->outfd) {
1665*44704f69SBart Van Assche         if (in_is_sg)
1666*44704f69SBart Van Assche             rep->same_sg = true;
1667*44704f69SBart Van Assche     }
1668*44704f69SBart Van Assche     if (clp->in_flags.random) {
1669*44704f69SBart Van Assche #ifdef HAVE_GETRANDOM
1670*44704f69SBart Van Assche         ssize_t ssz = getrandom(&rep->seed, sizeof(rep->seed), GRND_NONBLOCK);
1671*44704f69SBart Van Assche 
1672*44704f69SBart Van Assche         if (ssz < (ssize_t)sizeof(rep->seed)) {
1673*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: getrandom() failed, ret=%d\n", thr_idx,
1674*44704f69SBart Van Assche                        __func__, (int)ssz);
1675*44704f69SBart Van Assche             rep->seed = (long)time(NULL);
1676*44704f69SBart Van Assche         }
1677*44704f69SBart Van Assche #else
1678*44704f69SBart Van Assche         rep->seed = (long)time(NULL);    /* use seconds since epoch as proxy */
1679*44704f69SBart Van Assche #endif
1680*44704f69SBart Van Assche         if (vb > 1)
1681*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: seed=%ld\n", thr_idx, __func__, rep->seed);
1682*44704f69SBart Van Assche #ifdef HAVE_SRAND48_R
1683*44704f69SBart Van Assche         srand48_r(rep->seed, &rep->drand);
1684*44704f69SBart Van Assche #else
1685*44704f69SBart Van Assche         srand48(rep->seed);
1686*44704f69SBart Van Assche #endif
1687*44704f69SBart Van Assche     }
1688*44704f69SBart Van Assche 
1689*44704f69SBart Van Assche     if (in_is_sg && inf.size()) {
1690*44704f69SBart Van Assche         if ((clp->in_flags.same_fds || (0 == thr_idx)) &&
1691*44704f69SBart Van Assche             (cvp.in_fd >= 0))
1692*44704f69SBart Van Assche             fd = cvp.in_fd;
1693*44704f69SBart Van Assche         else {
1694*44704f69SBart Van Assche             fd = sg_in_open(clp, inf, (in_mmap ? &rep->buffp : NULL),
1695*44704f69SBart Van Assche                             (in_mmap ? &rep->mmap_len : NULL));
1696*44704f69SBart Van Assche             if (fd < 0)
1697*44704f69SBart Van Assche                 goto fini;
1698*44704f69SBart Van Assche             own_infd = true;
1699*44704f69SBart Van Assche             if (cvp.in_fd < 0)
1700*44704f69SBart Van Assche                 cvp.in_fd = fd;
1701*44704f69SBart Van Assche         }
1702*44704f69SBart Van Assche         rep->infd = fd;
1703*44704f69SBart Van Assche         rep->mmap_active = in_mmap ? clp->in_flags.mmap : 0;
1704*44704f69SBart Van Assche         if (in_mmap && (vb > 4))
1705*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: mmap buffp=%p\n", thr_idx, __func__,
1706*44704f69SBart Van Assche                        rep->buffp);
1707*44704f69SBart Van Assche         ++num_sg;
1708*44704f69SBart Van Assche         if (vb > 2)
1709*44704f69SBart Van Assche             pr2serr_lk("[%d]: opened local sg IFILE\n", thr_idx);
1710*44704f69SBart Van Assche     }
1711*44704f69SBart Van Assche     if (out_is_sg && outf.size()) {
1712*44704f69SBart Van Assche         if ((clp->out_flags.same_fds || (0 == thr_idx)) &&
1713*44704f69SBart Van Assche             (cvp.out_fd >= 0))
1714*44704f69SBart Van Assche             fd = cvp.out_fd;
1715*44704f69SBart Van Assche         else {
1716*44704f69SBart Van Assche             fd = sg_out_open(clp, outf, (out_mmap ? &rep->buffp : NULL),
1717*44704f69SBart Van Assche                              (out_mmap ? &rep->mmap_len : NULL));
1718*44704f69SBart Van Assche             if (fd < 0)
1719*44704f69SBart Van Assche                 goto fini;
1720*44704f69SBart Van Assche             own_outfd = true;
1721*44704f69SBart Van Assche             if (cvp.out_fd < 0)
1722*44704f69SBart Van Assche                 cvp.out_fd = fd;
1723*44704f69SBart Van Assche         }
1724*44704f69SBart Van Assche         rep->outfd = fd;
1725*44704f69SBart Van Assche         if (! rep->mmap_active)
1726*44704f69SBart Van Assche             rep->mmap_active = out_mmap ? clp->out_flags.mmap : 0;
1727*44704f69SBart Van Assche         if (out_mmap && (vb > 4))
1728*44704f69SBart Van Assche             pr2serr_lk("[%d]: mmap buffp=%p\n", thr_idx, rep->buffp);
1729*44704f69SBart Van Assche         ++num_sg;
1730*44704f69SBart Van Assche         if (vb > 2)
1731*44704f69SBart Van Assche             pr2serr_lk("[%d]: opened local sg OFILE\n", thr_idx);
1732*44704f69SBart Van Assche     }
1733*44704f69SBart Van Assche     if (vb > 2) {
1734*44704f69SBart Van Assche         if (in_is_sg && (! own_infd))
1735*44704f69SBart Van Assche             pr2serr_lk("[%d]: using global sg IFILE, fd=%d\n", thr_idx,
1736*44704f69SBart Van Assche                        rep->infd);
1737*44704f69SBart Van Assche         if (out_is_sg && (! own_outfd))
1738*44704f69SBart Van Assche             pr2serr_lk("[%d]: using global sg OFILE, fd=%d\n", thr_idx,
1739*44704f69SBart Van Assche                        rep->outfd);
1740*44704f69SBart Van Assche     }
1741*44704f69SBart Van Assche     if (rep->both_sg)
1742*44704f69SBart Van Assche         rep->has_share = sg_share_prepare(rep->outfd, rep->infd, thr_idx,
1743*44704f69SBart Van Assche                                           vb > 9);
1744*44704f69SBart Van Assche     if (vb > 9)
1745*44704f69SBart Van Assche         pr2serr_lk("[%d]: has_share=%s\n", thr_idx,
1746*44704f69SBart Van Assche                    (rep->has_share ? "true" : "false"));
1747*44704f69SBart Van Assche     // share_and_ofreg = (rep->has_share && (rep->outregfd >= 0));
1748*44704f69SBart Van Assche 
1749*44704f69SBart Van Assche     /* vvvvvvvvvvvvvv  Main segment copy loop  vvvvvvvvvvvvvvvvvvvvvvv */
1750*44704f69SBart Van Assche     while (! shutting_down) {
1751*44704f69SBart Van Assche         get_next_res_t gnr = cvp.get_next(clp->mrq_num * clp->bpt);
1752*44704f69SBart Van Assche 
1753*44704f69SBart Van Assche         seg_blks = gnr.second;
1754*44704f69SBart Van Assche         if (seg_blks <= 0) {
1755*44704f69SBart Van Assche             if (seg_blks < 0)
1756*44704f69SBart Van Assche                 res = -seg_blks;
1757*44704f69SBart Van Assche             else
1758*44704f69SBart Van Assche                 cvp.state = cp_ver_pair_t::my_state::finished;
1759*44704f69SBart Van Assche             break;
1760*44704f69SBart Van Assche         }
1761*44704f69SBart Van Assche         if (! i_sg_it.set_by_blk_idx(gnr.first)) {
1762*44704f69SBart Van Assche             lock_guard<mutex> lk(strerr_mut);
1763*44704f69SBart Van Assche 
1764*44704f69SBart Van Assche             pr2serr_lk("[%d]: input set_by_blk_idx() failed\n", thr_idx);
1765*44704f69SBart Van Assche             i_sg_it.dbg_print("input after set_by_blk_idx", false, vb > 5);
1766*44704f69SBart Van Assche             res = 2;
1767*44704f69SBart Van Assche             break;
1768*44704f69SBart Van Assche         }
1769*44704f69SBart Van Assche         if (! o_sg_it.set_by_blk_idx(gnr.first)) {
1770*44704f69SBart Van Assche             pr2serr_lk("[%d]: output set_by_blk_idx() failed\n", thr_idx);
1771*44704f69SBart Van Assche             res = 3;
1772*44704f69SBart Van Assche             break;
1773*44704f69SBart Van Assche         }
1774*44704f69SBart Van Assche         if (rep->both_sg) {
1775*44704f69SBart Van Assche             uint32_t nn = (2 * clp->mrq_num) + 4;
1776*44704f69SBart Van Assche 
1777*44704f69SBart Van Assche             if (a_cdb.capacity() < nn)
1778*44704f69SBart Van Assche                 a_cdb.reserve(nn);
1779*44704f69SBart Van Assche             if (a_v4.capacity() < nn)
1780*44704f69SBart Van Assche                 a_v4.reserve(nn);
1781*44704f69SBart Van Assche             if (clp->mrq_eq_0)
1782*44704f69SBart Van Assche                 res = do_both_sg_segment_mrq0(rep, i_sg_it, o_sg_it,
1783*44704f69SBart Van Assche                                               seg_blks);
1784*44704f69SBart Van Assche             else
1785*44704f69SBart Van Assche                 res = do_both_sg_segment(rep, i_sg_it, o_sg_it, seg_blks,
1786*44704f69SBart Van Assche                                          a_cdb, a_v4);
1787*44704f69SBart Van Assche             if (res < 0)
1788*44704f69SBart Van Assche                 break;
1789*44704f69SBart Van Assche         } else if (only_one_sg) {
1790*44704f69SBart Van Assche             uint32_t nn = clp->mrq_num + 4;
1791*44704f69SBart Van Assche 
1792*44704f69SBart Van Assche             if (a_cdb.capacity() < nn)
1793*44704f69SBart Van Assche                 a_cdb.reserve(nn);
1794*44704f69SBart Van Assche             if (a_v4.capacity() < nn)
1795*44704f69SBart Van Assche                 a_v4.reserve(nn);
1796*44704f69SBart Van Assche             res = do_normal_sg_segment(rep, i_sg_it, o_sg_it, seg_blks, a_cdb,
1797*44704f69SBart Van Assche                                        a_v4);
1798*44704f69SBart Van Assche             if (res < 0)
1799*44704f69SBart Van Assche                 break;
1800*44704f69SBart Van Assche         } else {
1801*44704f69SBart Van Assche             res = do_normal_normal_segment(rep, i_sg_it, o_sg_it, seg_blks);
1802*44704f69SBart Van Assche             if (res < 0)
1803*44704f69SBart Van Assche                 break;
1804*44704f69SBart Van Assche         }
1805*44704f69SBart Van Assche         if (singleton) {
1806*44704f69SBart Van Assche             {
1807*44704f69SBart Van Assche                 lock_guard<mutex> lk(clp->infant_mut);
1808*44704f69SBart Van Assche 
1809*44704f69SBart Van Assche                 clp->processed = true;
1810*44704f69SBart Van Assche             }   /* this unlocks lk */
1811*44704f69SBart Van Assche             clp->infant_cv.notify_one();
1812*44704f69SBart Van Assche             singleton = false;
1813*44704f69SBart Van Assche         }
1814*44704f69SBart Van Assche         if (rep->stop_after_write || rep->stop_now) {
1815*44704f69SBart Van Assche             shutting_down = true;
1816*44704f69SBart Van Assche             break;
1817*44704f69SBart Van Assche         }
1818*44704f69SBart Van Assche     }   /* ^^^^^^^^^^ end of main while loop which copies segments ^^^^^^ */
1819*44704f69SBart Van Assche 
1820*44704f69SBart Van Assche     if (shutting_down) {
1821*44704f69SBart Van Assche         if (vb > 3)
1822*44704f69SBart Van Assche             pr2serr_lk("%s: t=%d: shutting down\n", __func__, rep->id);
1823*44704f69SBart Van Assche         goto fini;
1824*44704f69SBart Van Assche     }
1825*44704f69SBart Van Assche     if (singleton) {
1826*44704f69SBart Van Assche         {
1827*44704f69SBart Van Assche             lock_guard<mutex> lk(clp->infant_mut);
1828*44704f69SBart Van Assche 
1829*44704f69SBart Van Assche             clp->processed = true;
1830*44704f69SBart Van Assche         }   /* this unlocks lk */
1831*44704f69SBart Van Assche         clp->infant_cv.notify_one();
1832*44704f69SBart Van Assche     }
1833*44704f69SBart Van Assche     if (res < 0) {
1834*44704f69SBart Van Assche         if (seg_blks >= 0)
1835*44704f69SBart Van Assche             cvp.get_next(-1);  /* flag error to main */
1836*44704f69SBart Van Assche         pr2serr_lk("%s: t=%d: aborting, res=%d\n", __func__, rep->id, res);
1837*44704f69SBart Van Assche     }
1838*44704f69SBart Van Assche 
1839*44704f69SBart Van Assche fini:
1840*44704f69SBart Van Assche 
1841*44704f69SBart Van Assche     if ((1 == rep->mmap_active) && (rep->mmap_len > 0)) {
1842*44704f69SBart Van Assche         if (munmap(rep->buffp, rep->mmap_len) < 0) {
1843*44704f69SBart Van Assche             err = errno;
1844*44704f69SBart Van Assche             char bb[64];
1845*44704f69SBart Van Assche 
1846*44704f69SBart Van Assche             pr2serr_lk("thread=%d: munmap() failed: %s\n", rep->id,
1847*44704f69SBart Van Assche                        tsafe_strerror(err, bb));
1848*44704f69SBart Van Assche         }
1849*44704f69SBart Van Assche         if (vb > 4)
1850*44704f69SBart Van Assche             pr2serr_lk("thread=%d: munmap(%p, %d)\n", rep->id, rep->buffp,
1851*44704f69SBart Van Assche                        rep->mmap_len);
1852*44704f69SBart Van Assche         rep->mmap_active = 0;
1853*44704f69SBart Van Assche     }
1854*44704f69SBart Van Assche 
1855*44704f69SBart Van Assche     if (own_infd && (rep->infd >= 0)) {
1856*44704f69SBart Van Assche         if (vb && in_is_sg) {
1857*44704f69SBart Van Assche             if (ioctl(rep->infd, SG_GET_NUM_WAITING, &n) >= 0) {
1858*44704f69SBart Van Assche                 if (n > 0)
1859*44704f69SBart Van Assche                     pr2serr_lk("%s: tid=%d: num_waiting=%d prior close(in)\n",
1860*44704f69SBart Van Assche                                __func__, rep->id, n);
1861*44704f69SBart Van Assche             } else {
1862*44704f69SBart Van Assche                 err = errno;
1863*44704f69SBart Van Assche                 pr2serr_lk("%s: [%d] ioctl(SG_GET_NUM_WAITING) errno=%d: "
1864*44704f69SBart Van Assche                            "%s\n", __func__, rep->id, err, strerror(err));
1865*44704f69SBart Van Assche             }
1866*44704f69SBart Van Assche         }
1867*44704f69SBart Van Assche         close(rep->infd);
1868*44704f69SBart Van Assche     }
1869*44704f69SBart Van Assche     if (own_outfd && (rep->outfd >= 0)) {
1870*44704f69SBart Van Assche         if (vb && out_is_sg) {
1871*44704f69SBart Van Assche             if (ioctl(rep->outfd, SG_GET_NUM_WAITING, &n) >= 0) {
1872*44704f69SBart Van Assche                 if (n > 0)
1873*44704f69SBart Van Assche                     pr2serr_lk("%s: tid=%d: num_waiting=%d prior "
1874*44704f69SBart Van Assche                                "close(out)\n", __func__, rep->id, n);
1875*44704f69SBart Van Assche             } else {
1876*44704f69SBart Van Assche                 err = errno;
1877*44704f69SBart Van Assche                 pr2serr_lk("%s: [%d] ioctl(SG_GET_NUM_WAITING) errno=%d: "
1878*44704f69SBart Van Assche                            "%s\n", __func__, rep->id, err, strerror(err));
1879*44704f69SBart Van Assche             }
1880*44704f69SBart Van Assche         }
1881*44704f69SBart Van Assche         close(rep->outfd);
1882*44704f69SBart Van Assche     }
1883*44704f69SBart Van Assche     /* pass stats back to read-side */
1884*44704f69SBart Van Assche     if (vb > 3)
1885*44704f69SBart Van Assche         pr2serr_lk("%s: [%d] leaving: in/out local count=%" PRId64 "/%"
1886*44704f69SBart Van Assche                    PRId64 "\n", __func__, rep->id, rep->in_local_count,
1887*44704f69SBart Van Assche                    rep->out_local_count);
1888*44704f69SBart Van Assche     cvp.in_rem_count -= rep->in_local_count;
1889*44704f69SBart Van Assche     cvp.out_rem_count -= rep->out_local_count;
1890*44704f69SBart Van Assche     cvp.in_partial += rep->in_local_partial;
1891*44704f69SBart Van Assche     cvp.out_partial += rep->out_local_partial;
1892*44704f69SBart Van Assche     cvp.sum_of_resids += rep->in_resid_bytes;
1893*44704f69SBart Van Assche     if (rep->alloc_bp)
1894*44704f69SBart Van Assche         free(rep->alloc_bp);
1895*44704f69SBart Van Assche }
1896*44704f69SBart Van Assche 
1897*44704f69SBart Van Assche /* N.B. Returns 'blocks' is successful, lesser positive number if there was
1898*44704f69SBart Van Assche  * a short read, or an error code which is negative. */
1899*44704f69SBart Van Assche static int
normal_in_rd(Rq_elem * rep,int64_t lba,int blocks,int d_boff)1900*44704f69SBart Van Assche normal_in_rd(Rq_elem * rep, int64_t lba, int blocks, int d_boff)
1901*44704f69SBart Van Assche {
1902*44704f69SBart Van Assche     struct global_collection * clp = rep->clp;
1903*44704f69SBart Van Assche     int res, err;
1904*44704f69SBart Van Assche     int id = rep->id;
1905*44704f69SBart Van Assche     uint8_t * bp;
1906*44704f69SBart Van Assche     char strerr_buff[STRERR_BUFF_LEN];
1907*44704f69SBart Van Assche 
1908*44704f69SBart Van Assche     if (clp->verbose > 4)
1909*44704f69SBart Van Assche         pr2serr_lk("[%d] %s: lba=%" PRIu64 ", blocks=%d, d_boff=%d\n", id,
1910*44704f69SBart Van Assche                    __func__, lba, blocks, d_boff);
1911*44704f69SBart Van Assche     if (FT_RANDOM_0_FF == clp->in_type) {
1912*44704f69SBart Van Assche         int k, j;
1913*44704f69SBart Van Assche         const int jbump = sizeof(uint32_t);
1914*44704f69SBart Van Assche         long rn;
1915*44704f69SBart Van Assche         uint8_t * bp;
1916*44704f69SBart Van Assche 
1917*44704f69SBart Van Assche         if (clp->in_flags.zero && clp->in_flags.ff && (rep->bs >= 4)) {
1918*44704f69SBart Van Assche             uint32_t pos = (uint32_t)lba;
1919*44704f69SBart Van Assche             uint32_t off;
1920*44704f69SBart Van Assche 
1921*44704f69SBart Van Assche             for (k = 0, off = 0; k < blocks; ++k, off += rep->bs, ++pos) {
1922*44704f69SBart Van Assche                 for (j = 0; j < (rep->bs - 3); j += 4)
1923*44704f69SBart Van Assche                     sg_put_unaligned_be32(pos, rep->buffp + off + j);
1924*44704f69SBart Van Assche             }
1925*44704f69SBart Van Assche         } else if (clp->in_flags.zero)
1926*44704f69SBart Van Assche             memset(rep->buffp + d_boff, 0, blocks * rep->bs);
1927*44704f69SBart Van Assche         else if (clp->in_flags.ff)
1928*44704f69SBart Van Assche             memset(rep->buffp + d_boff, 0xff, blocks * rep->bs);
1929*44704f69SBart Van Assche         else {
1930*44704f69SBart Van Assche             bp = rep->buffp + d_boff;
1931*44704f69SBart Van Assche             for (k = 0; k < blocks; ++k, bp += rep->bs) {
1932*44704f69SBart Van Assche                 for (j = 0; j < rep->bs; j += jbump) {
1933*44704f69SBart Van Assche                     /* mrand48 takes uniformly from [-2^31, 2^31) */
1934*44704f69SBart Van Assche #ifdef HAVE_SRAND48_R
1935*44704f69SBart Van Assche                     mrand48_r(&rep->drand, &rn);
1936*44704f69SBart Van Assche #else
1937*44704f69SBart Van Assche                     rn = mrand48();
1938*44704f69SBart Van Assche #endif
1939*44704f69SBart Van Assche                     *((uint32_t *)(bp + j)) = (uint32_t)rn;
1940*44704f69SBart Van Assche                 }
1941*44704f69SBart Van Assche             }
1942*44704f69SBart Van Assche         }
1943*44704f69SBart Van Assche         return blocks;
1944*44704f69SBart Van Assche     }
1945*44704f69SBart Van Assche 
1946*44704f69SBart Van Assche     if (clp->in_type != FT_FIFO) {
1947*44704f69SBart Van Assche         int64_t pos = lba * rep->bs;
1948*44704f69SBart Van Assche 
1949*44704f69SBart Van Assche         if (rep->in_follow_on != pos) {
1950*44704f69SBart Van Assche             if (lseek64(rep->infd, pos, SEEK_SET) < 0) {
1951*44704f69SBart Van Assche                 err = errno;
1952*44704f69SBart Van Assche                 pr2serr_lk("[%d] %s: >> lseek64(%" PRId64 "): %s\n", id,
1953*44704f69SBart Van Assche                            __func__, pos, safe_strerror(err));
1954*44704f69SBart Van Assche                 return -err;
1955*44704f69SBart Van Assche             }
1956*44704f69SBart Van Assche             rep->in_follow_on = pos;
1957*44704f69SBart Van Assche         }
1958*44704f69SBart Van Assche     }
1959*44704f69SBart Van Assche     bp = rep->buffp + d_boff;
1960*44704f69SBart Van Assche     while (((res = read(rep->infd, bp, blocks * rep->bs)) < 0) &&
1961*44704f69SBart Van Assche            ((EINTR == errno) || (EAGAIN == errno)))
1962*44704f69SBart Van Assche         std::this_thread::yield();/* another thread may be able to progress */
1963*44704f69SBart Van Assche     if (res < 0) {
1964*44704f69SBart Van Assche         err = errno;
1965*44704f69SBart Van Assche         if (clp->in_flags.coe) {
1966*44704f69SBart Van Assche             memset(bp, 0, blocks * rep->bs);
1967*44704f69SBart Van Assche             pr2serr_lk("[%d] %s : >> substituted zeros for in blk=%" PRId64
1968*44704f69SBart Van Assche                       " for %d bytes, %s\n", id, __func__, lba,
1969*44704f69SBart Van Assche                        blocks * rep->bs,
1970*44704f69SBart Van Assche                        tsafe_strerror(err, strerr_buff));
1971*44704f69SBart Van Assche             res = blocks * rep->bs;
1972*44704f69SBart Van Assche         } else {
1973*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: error in normal read, %s\n", id, __func__,
1974*44704f69SBart Van Assche                        tsafe_strerror(err, strerr_buff));
1975*44704f69SBart Van Assche             return -err;
1976*44704f69SBart Van Assche         }
1977*44704f69SBart Van Assche     }
1978*44704f69SBart Van Assche     rep->in_follow_on += res;
1979*44704f69SBart Van Assche     if (res < blocks * rep->bs) {
1980*44704f69SBart Van Assche         blocks = res / rep->bs;
1981*44704f69SBart Van Assche         if ((res % rep->bs) > 0) {
1982*44704f69SBart Van Assche             rep->in_local_partial++;
1983*44704f69SBart Van Assche             rep->in_resid_bytes = res % rep->bs;
1984*44704f69SBart Van Assche         }
1985*44704f69SBart Van Assche     }
1986*44704f69SBart Van Assche     return blocks;
1987*44704f69SBart Van Assche }
1988*44704f69SBart Van Assche 
1989*44704f69SBart Van Assche /* N.B. Returns 'blocks' is successful, lesser positive number if there was
1990*44704f69SBart Van Assche  * a short write, or an error code which is negative. */
1991*44704f69SBart Van Assche static int
normal_out_wr(Rq_elem * rep,int64_t lba,int blocks,int d_boff)1992*44704f69SBart Van Assche normal_out_wr(Rq_elem * rep, int64_t lba, int blocks, int d_boff)
1993*44704f69SBart Van Assche {
1994*44704f69SBart Van Assche     int res, err;
1995*44704f69SBart Van Assche     int id = rep->id;
1996*44704f69SBart Van Assche     struct global_collection * clp = rep->clp;
1997*44704f69SBart Van Assche     uint8_t * bp = rep->buffp + d_boff;
1998*44704f69SBart Van Assche     char strerr_buff[STRERR_BUFF_LEN];
1999*44704f69SBart Van Assche 
2000*44704f69SBart Van Assche     if (clp->verbose > 4)
2001*44704f69SBart Van Assche         pr2serr_lk("[%d] %s: lba=%" PRIu64 ", blocks=%d, d_boff=%d\n", id,
2002*44704f69SBart Van Assche                     __func__, lba, blocks, d_boff);
2003*44704f69SBart Van Assche 
2004*44704f69SBart Van Assche     if (clp->in_type != FT_FIFO) {
2005*44704f69SBart Van Assche         int64_t pos = lba * rep->bs;
2006*44704f69SBart Van Assche 
2007*44704f69SBart Van Assche         if (rep->out_follow_on != pos) {
2008*44704f69SBart Van Assche             if (lseek64(rep->outfd, pos, SEEK_SET) < 0) {
2009*44704f69SBart Van Assche                 err = errno;
2010*44704f69SBart Van Assche                 pr2serr_lk("[%d] %s: >> lseek64(%" PRId64 "): %s\n", id,
2011*44704f69SBart Van Assche                            __func__, pos, safe_strerror(err));
2012*44704f69SBart Van Assche                 return -err;
2013*44704f69SBart Van Assche             }
2014*44704f69SBart Van Assche             rep->out_follow_on = pos;
2015*44704f69SBart Van Assche         }
2016*44704f69SBart Van Assche     }
2017*44704f69SBart Van Assche     while (((res = write(rep->outfd, bp, blocks * rep->bs))
2018*44704f69SBart Van Assche             < 0) && ((EINTR == errno) || (EAGAIN == errno)))
2019*44704f69SBart Van Assche         std::this_thread::yield();/* another thread may be able to progress */
2020*44704f69SBart Van Assche     if (res < 0) {
2021*44704f69SBart Van Assche         err = errno;
2022*44704f69SBart Van Assche         if (clp->out_flags.coe) {
2023*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: >> ignored error for out lba=%" PRId64
2024*44704f69SBart Van Assche                        " for %d bytes, %s\n", id, __func__, lba,
2025*44704f69SBart Van Assche                        blocks * rep->bs, tsafe_strerror(err, strerr_buff));
2026*44704f69SBart Van Assche             res = blocks * rep->bs;
2027*44704f69SBart Van Assche         }
2028*44704f69SBart Van Assche         else {
2029*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: error normal write, %s\n", id, __func__,
2030*44704f69SBart Van Assche                        tsafe_strerror(err, strerr_buff));
2031*44704f69SBart Van Assche             return -err;
2032*44704f69SBart Van Assche         }
2033*44704f69SBart Van Assche     }
2034*44704f69SBart Van Assche     rep->out_follow_on += res;
2035*44704f69SBart Van Assche     if (res < blocks * rep->bs) {
2036*44704f69SBart Van Assche         blocks = res / rep->bs;
2037*44704f69SBart Van Assche         if ((res % rep->bs) > 0) {
2038*44704f69SBart Van Assche             blocks++;
2039*44704f69SBart Van Assche             rep->out_local_partial++;
2040*44704f69SBart Van Assche         }
2041*44704f69SBart Van Assche     }
2042*44704f69SBart Van Assche     return blocks;
2043*44704f69SBart Van Assche }
2044*44704f69SBart Van Assche 
2045*44704f69SBart Van Assche static int
extra_out_wr(Rq_elem * rep,int num_bytes,int d_boff)2046*44704f69SBart Van Assche extra_out_wr(Rq_elem * rep, int num_bytes, int d_boff)
2047*44704f69SBart Van Assche {
2048*44704f69SBart Van Assche     int res, err;
2049*44704f69SBart Van Assche     int id = rep->id;
2050*44704f69SBart Van Assche     struct global_collection * clp = rep->clp;
2051*44704f69SBart Van Assche     uint8_t * bp = rep->buffp + d_boff;
2052*44704f69SBart Van Assche     char strerr_buff[STRERR_BUFF_LEN];
2053*44704f69SBart Van Assche 
2054*44704f69SBart Van Assche     if (clp->verbose > 4)
2055*44704f69SBart Van Assche         pr2serr_lk("[%d] %s: num_bytes=%d, d_boff=%d\n", id, __func__,
2056*44704f69SBart Van Assche                    num_bytes, d_boff);
2057*44704f69SBart Van Assche 
2058*44704f69SBart Van Assche     while (((res = write(clp->out0fd, bp, num_bytes))
2059*44704f69SBart Van Assche             < 0) && ((EINTR == errno) || (EAGAIN == errno)))
2060*44704f69SBart Van Assche         std::this_thread::yield();/* another thread may be able to progress */
2061*44704f69SBart Van Assche     if (res < 0) {
2062*44704f69SBart Van Assche         err = errno;
2063*44704f69SBart Van Assche         pr2serr_lk("[%d] %s: error normal write, %s\n", id, __func__,
2064*44704f69SBart Van Assche                    tsafe_strerror(err, strerr_buff));
2065*44704f69SBart Van Assche         return -err;
2066*44704f69SBart Van Assche     }
2067*44704f69SBart Van Assche     if (res > 0)
2068*44704f69SBart Van Assche         rep->out_local_partial++;
2069*44704f69SBart Van Assche     return res;
2070*44704f69SBart Van Assche }
2071*44704f69SBart Van Assche 
2072*44704f69SBart Van Assche static int
sg_build_scsi_cdb(uint8_t * cdbp,int cdb_sz,unsigned int blocks,int64_t start_block,bool ver_true,bool write_true,bool fua,bool dpo,int cdl)2073*44704f69SBart Van Assche sg_build_scsi_cdb(uint8_t * cdbp, int cdb_sz, unsigned int blocks,
2074*44704f69SBart Van Assche                   int64_t start_block, bool ver_true, bool write_true,
2075*44704f69SBart Van Assche                   bool fua, bool dpo, int cdl)
2076*44704f69SBart Van Assche {
2077*44704f69SBart Van Assche     bool normal_rw = true;
2078*44704f69SBart Van Assche     int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88};
2079*44704f69SBart Van Assche     int ve_opcode[] = {0xff /* no VER(6) */, 0x2f, 0xaf, 0x8f};
2080*44704f69SBart Van Assche     int wr_opcode[] = {0xa, 0x2a, 0xaa, 0x8a};
2081*44704f69SBart Van Assche     int sz_ind;
2082*44704f69SBart Van Assche 
2083*44704f69SBart Van Assche     memset(cdbp, 0, cdb_sz);
2084*44704f69SBart Van Assche     if (ver_true) {     /* only support VERIFY(10) */
2085*44704f69SBart Van Assche         if (cdb_sz < 10) {
2086*44704f69SBart Van Assche             pr2serr_lk("%s only support VERIFY(10)\n", my_name);
2087*44704f69SBart Van Assche             return 1;
2088*44704f69SBart Van Assche         }
2089*44704f69SBart Van Assche         cdb_sz = 10;
2090*44704f69SBart Van Assche         fua = false;
2091*44704f69SBart Van Assche         cdbp[1] |= 0x2; /* BYTCHK=1 --> sending dout for comparison */
2092*44704f69SBart Van Assche         cdbp[0] = ve_opcode[1];
2093*44704f69SBart Van Assche         normal_rw = false;
2094*44704f69SBart Van Assche     }
2095*44704f69SBart Van Assche     if (dpo)
2096*44704f69SBart Van Assche         cdbp[1] |= 0x10;
2097*44704f69SBart Van Assche     if (fua)
2098*44704f69SBart Van Assche         cdbp[1] |= 0x8;
2099*44704f69SBart Van Assche     switch (cdb_sz) {
2100*44704f69SBart Van Assche     case 6:
2101*44704f69SBart Van Assche         sz_ind = 0;
2102*44704f69SBart Van Assche         cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
2103*44704f69SBart Van Assche                                          rd_opcode[sz_ind]);
2104*44704f69SBart Van Assche         sg_put_unaligned_be24(0x1fffff & start_block, cdbp + 1);
2105*44704f69SBart Van Assche         cdbp[4] = (256 == blocks) ? 0 : (uint8_t)blocks;
2106*44704f69SBart Van Assche         if (blocks > 256) {
2107*44704f69SBart Van Assche             pr2serr_lk("%sfor 6 byte commands, maximum number of blocks is "
2108*44704f69SBart Van Assche                        "256\n", my_name);
2109*44704f69SBart Van Assche             return 1;
2110*44704f69SBart Van Assche         }
2111*44704f69SBart Van Assche         if ((start_block + blocks - 1) & (~0x1fffff)) {
2112*44704f69SBart Van Assche             pr2serr_lk("%sfor 6 byte commands, can't address blocks beyond "
2113*44704f69SBart Van Assche                        "%d\n", my_name, 0x1fffff);
2114*44704f69SBart Van Assche             return 1;
2115*44704f69SBart Van Assche         }
2116*44704f69SBart Van Assche         if (dpo || fua) {
2117*44704f69SBart Van Assche             pr2serr_lk("%sfor 6 byte commands, neither dpo nor fua bits "
2118*44704f69SBart Van Assche                        "supported\n", my_name);
2119*44704f69SBart Van Assche             return 1;
2120*44704f69SBart Van Assche         }
2121*44704f69SBart Van Assche         break;
2122*44704f69SBart Van Assche     case 10:
2123*44704f69SBart Van Assche         if (! ver_true) {
2124*44704f69SBart Van Assche             sz_ind = 1;
2125*44704f69SBart Van Assche             cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
2126*44704f69SBart Van Assche                                              rd_opcode[sz_ind]);
2127*44704f69SBart Van Assche         }
2128*44704f69SBart Van Assche         sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2);
2129*44704f69SBart Van Assche         sg_put_unaligned_be16((uint16_t)blocks, cdbp + 7);
2130*44704f69SBart Van Assche         if (blocks & (~0xffff)) {
2131*44704f69SBart Van Assche             pr2serr_lk("%sfor 10 byte commands, maximum number of blocks is "
2132*44704f69SBart Van Assche                        "%d\n", my_name, 0xffff);
2133*44704f69SBart Van Assche             return 1;
2134*44704f69SBart Van Assche         }
2135*44704f69SBart Van Assche         break;
2136*44704f69SBart Van Assche     case 12:
2137*44704f69SBart Van Assche         sz_ind = 2;
2138*44704f69SBart Van Assche         cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
2139*44704f69SBart Van Assche                                          rd_opcode[sz_ind]);
2140*44704f69SBart Van Assche         sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2);
2141*44704f69SBart Van Assche         sg_put_unaligned_be32((uint32_t)blocks, cdbp + 6);
2142*44704f69SBart Van Assche         break;
2143*44704f69SBart Van Assche     case 16:
2144*44704f69SBart Van Assche         sz_ind = 3;
2145*44704f69SBart Van Assche         cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
2146*44704f69SBart Van Assche                                          rd_opcode[sz_ind]);
2147*44704f69SBart Van Assche         sg_put_unaligned_be64((uint64_t)start_block, cdbp + 2);
2148*44704f69SBart Van Assche         sg_put_unaligned_be32((uint32_t)blocks, cdbp + 10);
2149*44704f69SBart Van Assche         if (normal_rw && (cdl > 0)) {
2150*44704f69SBart Van Assche             if (cdl & 0x4)
2151*44704f69SBart Van Assche                 cdbp[1] |= 0x1;
2152*44704f69SBart Van Assche             if (cdl & 0x3)
2153*44704f69SBart Van Assche                 cdbp[14] |= ((cdl & 0x3) << 6);
2154*44704f69SBart Van Assche         }
2155*44704f69SBart Van Assche         break;
2156*44704f69SBart Van Assche     default:
2157*44704f69SBart Van Assche         pr2serr_lk("%sexpected cdb size of 6, 10, 12, or 16 but got %d\n",
2158*44704f69SBart Van Assche                    my_name, cdb_sz);
2159*44704f69SBart Van Assche         return 1;
2160*44704f69SBart Van Assche     }
2161*44704f69SBart Van Assche     return 0;
2162*44704f69SBart Van Assche }
2163*44704f69SBart Van Assche 
2164*44704f69SBart Van Assche static int
process_mrq_response(Rq_elem * rep,const struct sg_io_v4 * ctl_v4p,const struct sg_io_v4 * a_v4p,int num_mrq,uint32_t & good_inblks,uint32_t & good_outblks,bool & last_err_on_in)2165*44704f69SBart Van Assche process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p,
2166*44704f69SBart Van Assche                      const struct sg_io_v4 * a_v4p, int num_mrq,
2167*44704f69SBart Van Assche                      uint32_t & good_inblks, uint32_t & good_outblks,
2168*44704f69SBart Van Assche                      bool & last_err_on_in)
2169*44704f69SBart Van Assche {
2170*44704f69SBart Van Assche     struct global_collection * clp = rep->clp;
2171*44704f69SBart Van Assche     bool ok, all_good;
2172*44704f69SBart Van Assche     bool sb_in_co = !!(ctl_v4p->response);
2173*44704f69SBart Van Assche     int id = rep->id;
2174*44704f69SBart Van Assche     int resid = ctl_v4p->din_resid;
2175*44704f69SBart Van Assche     int sres = ctl_v4p->spare_out;
2176*44704f69SBart Van Assche     int n_subm = num_mrq - ctl_v4p->dout_resid;
2177*44704f69SBart Van Assche     int n_cmpl = ctl_v4p->info;
2178*44704f69SBart Van Assche     int n_good = 0;
2179*44704f69SBart Van Assche     int hole_count = 0;
2180*44704f69SBart Van Assche     int cat = 0;
2181*44704f69SBart Van Assche     int vb = clp->verbose;
2182*44704f69SBart Van Assche     int k, j, f1, slen;
2183*44704f69SBart Van Assche     char b[160];
2184*44704f69SBart Van Assche 
2185*44704f69SBart Van Assche     good_inblks = 0;
2186*44704f69SBart Van Assche     good_outblks = 0;
2187*44704f69SBart Van Assche     if (vb > 2)
2188*44704f69SBart Van Assche         pr2serr_lk("[thread_id=%d] %s: num_mrq=%d, n_subm=%d, n_cmpl=%d\n",
2189*44704f69SBart Van Assche                    id, __func__, num_mrq, n_subm, n_cmpl);
2190*44704f69SBart Van Assche     if (n_subm < 0) {
2191*44704f69SBart Van Assche         pr2serr_lk("[%d] co.dout_resid(%d) > num_mrq(%d)\n", id,
2192*44704f69SBart Van Assche                    ctl_v4p->dout_resid, num_mrq);
2193*44704f69SBart Van Assche         return -1;
2194*44704f69SBart Van Assche     }
2195*44704f69SBart Van Assche     if (n_cmpl != (num_mrq - resid))
2196*44704f69SBart Van Assche         pr2serr_lk("[%d] co.info(%d) != (num_mrq(%d) - co.din_resid(%d))\n"
2197*44704f69SBart Van Assche                    "will use co.info\n", id, n_cmpl, num_mrq, resid);
2198*44704f69SBart Van Assche     if (n_cmpl > n_subm) {
2199*44704f69SBart Van Assche         pr2serr_lk("[%d] n_cmpl(%d) > n_subm(%d), use n_subm for both\n",
2200*44704f69SBart Van Assche                    id, n_cmpl, n_subm);
2201*44704f69SBart Van Assche         n_cmpl = n_subm;
2202*44704f69SBart Van Assche     }
2203*44704f69SBart Van Assche     if (sres) {
2204*44704f69SBart Van Assche         pr2serr_lk("[%d] secondary error: %s [%d], info=0x%x\n", id,
2205*44704f69SBart Van Assche                    strerror(sres), sres, ctl_v4p->info);
2206*44704f69SBart Van Assche         if (E2BIG == sres) {
2207*44704f69SBart Van Assche             sg_take_snap(rep->infd, id, true);
2208*44704f69SBart Van Assche             sg_take_snap(rep->outfd, id, true);
2209*44704f69SBart Van Assche         }
2210*44704f69SBart Van Assche     }
2211*44704f69SBart Van Assche     /* Check if those submitted have finished or not. N.B. If there has been
2212*44704f69SBart Van Assche      * an error then there may be "holes" (i.e. info=0x0) in the array due
2213*44704f69SBart Van Assche      * to completions being out-of-order. */
2214*44704f69SBart Van Assche     for (k = 0, j = 0; ((k < num_mrq) && (j < n_subm));
2215*44704f69SBart Van Assche          ++k, j += f1, ++a_v4p) {
2216*44704f69SBart Van Assche         slen = a_v4p->response_len;
2217*44704f69SBart Van Assche         if (! (SG_INFO_MRQ_FINI & a_v4p->info))
2218*44704f69SBart Van Assche             ++hole_count;
2219*44704f69SBart Van Assche         ok = true;
2220*44704f69SBart Van Assche         f1 = !!(a_v4p->info);   /* want to skip n_subm count if info is 0x0 */
2221*44704f69SBart Van Assche         if (SG_INFO_CHECK & a_v4p->info) {
2222*44704f69SBart Van Assche             if ((0 == k) && (SGV4_FLAG_META_OUT_IF & ctl_v4p->flags) &&
2223*44704f69SBart Van Assche                 (UINT32_MAX == a_v4p->info)) {
2224*44704f69SBart Van Assche                 hole_count = 0;
2225*44704f69SBart Van Assche                 n_good = num_mrq;
2226*44704f69SBart Van Assche                 good_inblks = rep->a_mrq_din_blks;
2227*44704f69SBart Van Assche                 good_outblks = rep->a_mrq_dout_blks;
2228*44704f69SBart Van Assche                 break;
2229*44704f69SBart Van Assche             }
2230*44704f69SBart Van Assche             ok = false;
2231*44704f69SBart Van Assche             pr2serr_lk("[%d] a_v4[%d]: SG_INFO_CHECK set [%s]\n", id, k,
2232*44704f69SBart Van Assche                        sg_info_str(a_v4p->info, sizeof(b), b));
2233*44704f69SBart Van Assche         }
2234*44704f69SBart Van Assche         if (sg_scsi_status_is_bad(a_v4p->device_status) ||
2235*44704f69SBart Van Assche             a_v4p->transport_status || a_v4p->driver_status) {
2236*44704f69SBart Van Assche             ok = false;
2237*44704f69SBart Van Assche             last_err_on_in = ! (a_v4p->flags & SGV4_FLAG_DO_ON_OTHER);
2238*44704f69SBart Van Assche             if (SAM_STAT_CHECK_CONDITION != a_v4p->device_status) {
2239*44704f69SBart Van Assche                 pr2serr_lk("[%d] a_v4[%d]:\n", id, k);
2240*44704f69SBart Van Assche                 if (vb)
2241*44704f69SBart Van Assche                     lk_chk_n_print4("  >>", a_v4p, vb > 4);
2242*44704f69SBart Van Assche             }
2243*44704f69SBart Van Assche         }
2244*44704f69SBart Van Assche         if (slen > 0) {
2245*44704f69SBart Van Assche             struct sg_scsi_sense_hdr ssh;
2246*44704f69SBart Van Assche             const uint8_t *sbp = (const uint8_t *)
2247*44704f69SBart Van Assche                         (sb_in_co ? ctl_v4p->response : a_v4p->response);
2248*44704f69SBart Van Assche 
2249*44704f69SBart Van Assche             if (sg_scsi_normalize_sense(sbp, slen, &ssh) &&
2250*44704f69SBart Van Assche                 (ssh.response_code >= 0x70)) {
2251*44704f69SBart Van Assche                 if (ssh.response_code & 0x1) {
2252*44704f69SBart Van Assche                     ok = true;
2253*44704f69SBart Van Assche                     last_err_on_in = false;
2254*44704f69SBart Van Assche                 } else
2255*44704f69SBart Van Assche                     cat = sg_err_category_sense(sbp, slen);
2256*44704f69SBart Van Assche                 if (SPC_SK_MISCOMPARE == ssh.sense_key)
2257*44704f69SBart Van Assche                     ++num_miscompare;
2258*44704f69SBart Van Assche 
2259*44704f69SBart Van Assche                 pr2serr_lk("[%d] a_v4[%d]:\n", id, k);
2260*44704f69SBart Van Assche                 if (vb)
2261*44704f69SBart Van Assche                     lk_chk_n_print4("  >>", a_v4p, vb > 4);
2262*44704f69SBart Van Assche             }
2263*44704f69SBart Van Assche         } else if (! ok)
2264*44704f69SBart Van Assche             cat = SG_LIB_CAT_OTHER;
2265*44704f69SBart Van Assche         if (ok && f1) {
2266*44704f69SBart Van Assche             ++n_good;
2267*44704f69SBart Van Assche             if (a_v4p->dout_xfer_len >= (uint32_t)rep->bs)
2268*44704f69SBart Van Assche                 good_outblks += (a_v4p->dout_xfer_len - a_v4p->dout_resid) /
2269*44704f69SBart Van Assche                                 rep->bs;
2270*44704f69SBart Van Assche             if (a_v4p->din_xfer_len >= (uint32_t)rep->bs)
2271*44704f69SBart Van Assche                 good_inblks += (a_v4p->din_xfer_len - a_v4p->din_resid) /
2272*44704f69SBart Van Assche                                rep->bs;
2273*44704f69SBart Van Assche         }
2274*44704f69SBart Van Assche         if (! ok) {
2275*44704f69SBart Van Assche             if ((a_v4p->dout_xfer_len > 0) || (! clp->in_flags.coe))
2276*44704f69SBart Van Assche                 rep->stop_after_write = true;
2277*44704f69SBart Van Assche         }
2278*44704f69SBart Van Assche     }   /* end of request array scan loop */
2279*44704f69SBart Van Assche     if ((n_subm == num_mrq) || (vb < 3))
2280*44704f69SBart Van Assche         goto fini;
2281*44704f69SBart Van Assche     pr2serr_lk("[%d] checking response array _beyond_ number of "
2282*44704f69SBart Van Assche                "submissions [%d] to num_mrq:\n", id, k);
2283*44704f69SBart Van Assche     for (all_good = true; k < num_mrq; ++k, ++a_v4p) {
2284*44704f69SBart Van Assche         if (SG_INFO_MRQ_FINI & a_v4p->info) {
2285*44704f69SBart Van Assche             pr2serr_lk("[%d] a_v4[%d]: unexpected SG_INFO_MRQ_FINI set [%s]\n",
2286*44704f69SBart Van Assche                        id, k, sg_info_str(a_v4p->info, sizeof(b), b));
2287*44704f69SBart Van Assche             all_good = false;
2288*44704f69SBart Van Assche         }
2289*44704f69SBart Van Assche         if (a_v4p->device_status || a_v4p->transport_status ||
2290*44704f69SBart Van Assche             a_v4p->driver_status) {
2291*44704f69SBart Van Assche             pr2serr_lk("[%d] a_v4[%d]:\n", id, k);
2292*44704f69SBart Van Assche             lk_chk_n_print4("    ", a_v4p, vb > 4);
2293*44704f69SBart Van Assche             all_good = false;
2294*44704f69SBart Van Assche         }
2295*44704f69SBart Van Assche     }
2296*44704f69SBart Van Assche     if (all_good)
2297*44704f69SBart Van Assche         pr2serr_lk("    ... all good\n");
2298*44704f69SBart Van Assche fini:
2299*44704f69SBart Van Assche     if (cat > 0)
2300*44704f69SBart Van Assche         clp->reason_res.store(cat);
2301*44704f69SBart Van Assche     return n_good;
2302*44704f69SBart Van Assche }
2303*44704f69SBart Van Assche 
2304*44704f69SBart Van Assche /* Returns number of blocks successfully processed or a negative error
2305*44704f69SBart Van Assche  * number. */
2306*44704f69SBart Van Assche static int
sg_half_segment_mrq0(Rq_elem * rep,scat_gath_iter & sg_it,bool is_wr,int seg_blks,uint8_t * dp)2307*44704f69SBart Van Assche sg_half_segment_mrq0(Rq_elem * rep, scat_gath_iter & sg_it, bool is_wr,
2308*44704f69SBart Van Assche                      int seg_blks, uint8_t *dp)
2309*44704f69SBart Van Assche {
2310*44704f69SBart Van Assche     int k, res, fd, pack_id_base, id, rflags;
2311*44704f69SBart Van Assche     int num, kk, lin_blks, cdbsz, err;
2312*44704f69SBart Van Assche     uint32_t q_blks = 0;
2313*44704f69SBart Van Assche     struct global_collection * clp = rep->clp;
2314*44704f69SBart Van Assche     cdb_arr_t t_cdb {};
2315*44704f69SBart Van Assche     struct sg_io_v4 t_v4 {};
2316*44704f69SBart Van Assche     struct sg_io_v4 * t_v4p = &t_v4;
2317*44704f69SBart Van Assche     struct flags_t * flagsp = is_wr ? &clp->out_flags : &clp->in_flags;
2318*44704f69SBart Van Assche     int vb = clp->verbose;
2319*44704f69SBart Van Assche 
2320*44704f69SBart Van Assche     id = rep->id;
2321*44704f69SBart Van Assche     pack_id_base = id * PACK_ID_TID_MULTIPLIER;
2322*44704f69SBart Van Assche     rflags = 0;
2323*44704f69SBart Van Assche     fd = is_wr ? rep->outfd : rep->infd;
2324*44704f69SBart Van Assche     if (flagsp->mmap && (rep->outregfd >= 0))
2325*44704f69SBart Van Assche         rflags |= SGV4_FLAG_MMAP_IO;
2326*44704f69SBart Van Assche     if (flagsp->dio)
2327*44704f69SBart Van Assche         rflags |= SGV4_FLAG_DIRECT_IO;
2328*44704f69SBart Van Assche     if (flagsp->qhead)
2329*44704f69SBart Van Assche         rflags |= SGV4_FLAG_Q_AT_HEAD;
2330*44704f69SBart Van Assche     if (flagsp->qtail)
2331*44704f69SBart Van Assche         rflags |= SGV4_FLAG_Q_AT_TAIL;
2332*44704f69SBart Van Assche     if (flagsp->polled)
2333*44704f69SBart Van Assche         rflags |= SGV4_FLAG_POLLED;
2334*44704f69SBart Van Assche 
2335*44704f69SBart Van Assche     for (k = 0, num = 0; seg_blks > 0; ++k, seg_blks -= num) {
2336*44704f69SBart Van Assche         kk = min<int>(seg_blks, clp->bpt);
2337*44704f69SBart Van Assche         lin_blks = sg_it.linear_for_n_blks(kk);
2338*44704f69SBart Van Assche         num = lin_blks;
2339*44704f69SBart Van Assche         if (num <= 0) {
2340*44704f69SBart Van Assche             res = 0;
2341*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: unexpected num=%d\n", id, __func__, num);
2342*44704f69SBart Van Assche             break;
2343*44704f69SBart Van Assche         }
2344*44704f69SBart Van Assche 
2345*44704f69SBart Van Assche         /* First build the command/request for the read-side */
2346*44704f69SBart Van Assche         cdbsz = is_wr ? clp->cdbsz_out : clp->cdbsz_in;
2347*44704f69SBart Van Assche         res = sg_build_scsi_cdb(t_cdb.data(), cdbsz, num, sg_it.current_lba(),
2348*44704f69SBart Van Assche                                 false, is_wr, flagsp->fua, flagsp->dpo,
2349*44704f69SBart Van Assche                                 flagsp->cdl);
2350*44704f69SBart Van Assche         if (res) {
2351*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: sg_build_scsi_cdb() failed\n", id, __func__);
2352*44704f69SBart Van Assche             break;
2353*44704f69SBart Van Assche         } else if (vb > 3)
2354*44704f69SBart Van Assche             lk_print_command_len("cdb: ", t_cdb.data(), cdbsz, true);
2355*44704f69SBart Van Assche 
2356*44704f69SBart Van Assche         t_v4p->guard = 'Q';
2357*44704f69SBart Van Assche         t_v4p->request = (uint64_t)t_cdb.data();
2358*44704f69SBart Van Assche         t_v4p->usr_ptr = t_v4p->request;
2359*44704f69SBart Van Assche         t_v4p->response = (uint64_t)rep->sb;
2360*44704f69SBart Van Assche         t_v4p->max_response_len = sizeof(rep->sb);
2361*44704f69SBart Van Assche         t_v4p->flags = rflags;
2362*44704f69SBart Van Assche         t_v4p->request_len = cdbsz;
2363*44704f69SBart Van Assche         if (is_wr) {
2364*44704f69SBart Van Assche             t_v4p->dout_xfer_len = num * rep->bs;
2365*44704f69SBart Van Assche             t_v4p->dout_xferp = (uint64_t)(dp + (q_blks * rep->bs));
2366*44704f69SBart Van Assche             t_v4p->din_xfer_len = 0;
2367*44704f69SBart Van Assche         } else {
2368*44704f69SBart Van Assche             t_v4p->din_xfer_len = num * rep->bs;
2369*44704f69SBart Van Assche             t_v4p->din_xferp = (uint64_t)(dp + (q_blks * rep->bs));
2370*44704f69SBart Van Assche             t_v4p->dout_xfer_len = 0;
2371*44704f69SBart Van Assche         }
2372*44704f69SBart Van Assche         t_v4p->timeout = clp->cmd_timeout;
2373*44704f69SBart Van Assche         t_v4p->request_extra = pack_id_base + ++rep->mrq_pack_id_off;
2374*44704f69SBart Van Assche         clp->most_recent_pack_id.store(t_v4p->request_extra);
2375*44704f69SBart Van Assche mrq0_again:
2376*44704f69SBart Van Assche         res = ioctl(fd, SG_IO, t_v4p);
2377*44704f69SBart Van Assche         err = errno;
2378*44704f69SBart Van Assche         if (vb > 5)
2379*44704f69SBart Van Assche             v4hdr_out_lk("sg_half_segment_mrq0: >> after ioctl(SG_IO)",
2380*44704f69SBart Van Assche                          t_v4p, id, false);
2381*44704f69SBart Van Assche         if (res < 0) {
2382*44704f69SBart Van Assche             if (E2BIG == err)
2383*44704f69SBart Van Assche                 sg_take_snap(fd, id, true);
2384*44704f69SBart Van Assche             else if (EBUSY == err) {
2385*44704f69SBart Van Assche                 ++num_ebusy;
2386*44704f69SBart Van Assche                 std::this_thread::yield();/* so other threads can progress */
2387*44704f69SBart Van Assche                 goto mrq0_again;
2388*44704f69SBart Van Assche             }
2389*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: ioctl(SG_IO)-->%d, errno=%d: %s\n", id,
2390*44704f69SBart Van Assche                        __func__, res, err, strerror(err));
2391*44704f69SBart Van Assche             return -err;
2392*44704f69SBart Van Assche         }
2393*44704f69SBart Van Assche         if (t_v4p->device_status || t_v4p->transport_status ||
2394*44704f69SBart Van Assche             t_v4p->driver_status) {
2395*44704f69SBart Van Assche             rep->stop_now = true;
2396*44704f69SBart Van Assche             pr2serr_lk("[%d] t_v4[%d]:\n", id, k);
2397*44704f69SBart Van Assche             lk_chk_n_print4("    ", t_v4p, vb > 4);
2398*44704f69SBart Van Assche             return q_blks;
2399*44704f69SBart Van Assche         }
2400*44704f69SBart Van Assche         q_blks += num;
2401*44704f69SBart Van Assche         sg_it.add_blks(num);
2402*44704f69SBart Van Assche     }
2403*44704f69SBart Van Assche     return q_blks;
2404*44704f69SBart Van Assche }
2405*44704f69SBart Van Assche 
2406*44704f69SBart Van Assche /* Returns number of blocks successfully processed or a negative error
2407*44704f69SBart Van Assche  * number. */
2408*44704f69SBart Van Assche static int
sg_half_segment(Rq_elem * rep,scat_gath_iter & sg_it,bool is_wr,int seg_blks,uint8_t * dp,vector<cdb_arr_t> & a_cdb,vector<struct sg_io_v4> & a_v4)2409*44704f69SBart Van Assche sg_half_segment(Rq_elem * rep, scat_gath_iter & sg_it, bool is_wr,
2410*44704f69SBart Van Assche                 int seg_blks, uint8_t *dp, vector<cdb_arr_t> & a_cdb,
2411*44704f69SBart Van Assche                 vector<struct sg_io_v4> & a_v4)
2412*44704f69SBart Van Assche {
2413*44704f69SBart Van Assche     int num_mrq, k, res, fd, mrq_pack_id_base, id, b_len, rflags;
2414*44704f69SBart Van Assche     int num, kk, lin_blks, cdbsz, num_good, err;
2415*44704f69SBart Van Assche     int o_seg_blks = seg_blks;
2416*44704f69SBart Van Assche     uint32_t in_fin_blks, out_fin_blks;
2417*44704f69SBart Van Assche     uint32_t mrq_q_blks = 0;
2418*44704f69SBart Van Assche     uint32_t in_mrq_q_blks = 0;
2419*44704f69SBart Van Assche     uint32_t out_mrq_q_blks = 0;
2420*44704f69SBart Van Assche     const int max_cdb_sz = MAX_SCSI_CDB_SZ;
2421*44704f69SBart Van Assche     struct sg_io_v4 * a_v4p;
2422*44704f69SBart Van Assche     struct sg_io_v4 ctl_v4 {};     /* MRQ control object */
2423*44704f69SBart Van Assche     struct global_collection * clp = rep->clp;
2424*44704f69SBart Van Assche     const char * iosub_str = "SG_IOSUBMIT(variable blocking)";
2425*44704f69SBart Van Assche     char b[80];
2426*44704f69SBart Van Assche     cdb_arr_t t_cdb {};
2427*44704f69SBart Van Assche     struct sg_io_v4 t_v4 {};
2428*44704f69SBart Van Assche     struct sg_io_v4 * t_v4p = &t_v4;
2429*44704f69SBart Van Assche     struct flags_t * flagsp = is_wr ? &clp->out_flags : &clp->in_flags;
2430*44704f69SBart Van Assche     bool serial = flagsp->serial;
2431*44704f69SBart Van Assche     bool err_on_in = false;
2432*44704f69SBart Van Assche     int vb = clp->verbose;
2433*44704f69SBart Van Assche 
2434*44704f69SBart Van Assche     id = rep->id;
2435*44704f69SBart Van Assche     b_len = sizeof(b);
2436*44704f69SBart Van Assche     if (serial)
2437*44704f69SBart Van Assche         iosub_str = "SG_IO(ordered blocking)";
2438*44704f69SBart Van Assche 
2439*44704f69SBart Van Assche     a_cdb.clear();
2440*44704f69SBart Van Assche     a_v4.clear();
2441*44704f69SBart Van Assche     rep->a_mrq_din_blks = 0;
2442*44704f69SBart Van Assche     rep->a_mrq_dout_blks = 0;
2443*44704f69SBart Van Assche     mrq_pack_id_base = id * PACK_ID_TID_MULTIPLIER;
2444*44704f69SBart Van Assche 
2445*44704f69SBart Van Assche     rflags = 0;
2446*44704f69SBart Van Assche     if (flagsp->mmap && (rep->outregfd >= 0))
2447*44704f69SBart Van Assche         rflags |= SGV4_FLAG_MMAP_IO;
2448*44704f69SBart Van Assche     if (flagsp->dio)
2449*44704f69SBart Van Assche         rflags |= SGV4_FLAG_DIRECT_IO;
2450*44704f69SBart Van Assche     if (flagsp->qhead)
2451*44704f69SBart Van Assche         rflags |= SGV4_FLAG_Q_AT_HEAD;
2452*44704f69SBart Van Assche     if (flagsp->qtail)
2453*44704f69SBart Van Assche         rflags |= SGV4_FLAG_Q_AT_TAIL;
2454*44704f69SBart Van Assche     if (flagsp->polled)
2455*44704f69SBart Van Assche         rflags |= SGV4_FLAG_POLLED;
2456*44704f69SBart Van Assche 
2457*44704f69SBart Van Assche     for (k = 0, num = 0; seg_blks > 0; ++k, seg_blks -= num) {
2458*44704f69SBart Van Assche         kk = min<int>(seg_blks, clp->bpt);
2459*44704f69SBart Van Assche         lin_blks = sg_it.linear_for_n_blks(kk);
2460*44704f69SBart Van Assche         num = lin_blks;
2461*44704f69SBart Van Assche         if (num <= 0) {
2462*44704f69SBart Van Assche             res = 0;
2463*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: unexpected num=%d\n", id, __func__, num);
2464*44704f69SBart Van Assche             break;
2465*44704f69SBart Van Assche         }
2466*44704f69SBart Van Assche 
2467*44704f69SBart Van Assche         /* First build the command/request for the read-side */
2468*44704f69SBart Van Assche         cdbsz = is_wr ? clp->cdbsz_out : clp->cdbsz_in;
2469*44704f69SBart Van Assche         res = sg_build_scsi_cdb(t_cdb.data(), cdbsz, num, sg_it.current_lba(),
2470*44704f69SBart Van Assche                                 false, is_wr, flagsp->fua, flagsp->dpo,
2471*44704f69SBart Van Assche                                 flagsp->cdl);
2472*44704f69SBart Van Assche         if (res) {
2473*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: sg_build_scsi_cdb() failed\n", id, __func__);
2474*44704f69SBart Van Assche             break;
2475*44704f69SBart Van Assche         } else if (vb > 3)
2476*44704f69SBart Van Assche             lk_print_command_len("cdb: ", t_cdb.data(), cdbsz, true);
2477*44704f69SBart Van Assche         a_cdb.push_back(t_cdb);
2478*44704f69SBart Van Assche 
2479*44704f69SBart Van Assche         t_v4p->guard = 'Q';
2480*44704f69SBart Van Assche         t_v4p->flags = rflags;
2481*44704f69SBart Van Assche         t_v4p->request_len = cdbsz;
2482*44704f69SBart Van Assche         t_v4p->response = (uint64_t)rep->sb;
2483*44704f69SBart Van Assche         t_v4p->max_response_len = sizeof(rep->sb);
2484*44704f69SBart Van Assche         t_v4p->flags = rflags;
2485*44704f69SBart Van Assche         t_v4p->usr_ptr = (uint64_t)&a_cdb[a_cdb.size() - 1];
2486*44704f69SBart Van Assche         if (is_wr) {
2487*44704f69SBart Van Assche             rep->a_mrq_dout_blks += num;
2488*44704f69SBart Van Assche             t_v4p->dout_xfer_len = num * rep->bs;
2489*44704f69SBart Van Assche             t_v4p->dout_xferp = (uint64_t)(dp + (mrq_q_blks * rep->bs));
2490*44704f69SBart Van Assche             t_v4p->din_xfer_len = 0;
2491*44704f69SBart Van Assche         } else {
2492*44704f69SBart Van Assche             rep->a_mrq_din_blks += num;
2493*44704f69SBart Van Assche             t_v4p->din_xfer_len = num * rep->bs;
2494*44704f69SBart Van Assche             t_v4p->din_xferp = (uint64_t)(dp + (mrq_q_blks * rep->bs));
2495*44704f69SBart Van Assche             t_v4p->dout_xfer_len = 0;
2496*44704f69SBart Van Assche         }
2497*44704f69SBart Van Assche         t_v4p->timeout = clp->cmd_timeout;
2498*44704f69SBart Van Assche         mrq_q_blks += num;
2499*44704f69SBart Van Assche         t_v4p->request_extra = mrq_pack_id_base + ++rep->mrq_pack_id_off;
2500*44704f69SBart Van Assche         clp->most_recent_pack_id.store(t_v4p->request_extra);
2501*44704f69SBart Van Assche         a_v4.push_back(t_v4);
2502*44704f69SBart Van Assche 
2503*44704f69SBart Van Assche         sg_it.add_blks(num);
2504*44704f69SBart Van Assche     }
2505*44704f69SBart Van Assche 
2506*44704f69SBart Van Assche     if (rep->only_in_sg)
2507*44704f69SBart Van Assche         fd = rep->infd;
2508*44704f69SBart Van Assche     else if (rep->only_out_sg)
2509*44704f69SBart Van Assche         fd = rep->outfd;
2510*44704f69SBart Van Assche     else {
2511*44704f69SBart Van Assche         pr2serr_lk("[%d] %s: why am I here? No sg devices\n", id, __func__);
2512*44704f69SBart Van Assche         return -EINVAL;
2513*44704f69SBart Van Assche     }
2514*44704f69SBart Van Assche     num_mrq = a_v4.size();
2515*44704f69SBart Van Assche     a_v4p = a_v4.data();
2516*44704f69SBart Van Assche     res = 0;
2517*44704f69SBart Van Assche     ctl_v4.guard = 'Q';
2518*44704f69SBart Van Assche     ctl_v4.request_len = a_cdb.size() * max_cdb_sz;
2519*44704f69SBart Van Assche     ctl_v4.request = (uint64_t)a_cdb.data();
2520*44704f69SBart Van Assche     ctl_v4.max_response_len = sizeof(rep->sb);
2521*44704f69SBart Van Assche     ctl_v4.response = (uint64_t)rep->sb;
2522*44704f69SBart Van Assche     ctl_v4.flags = SGV4_FLAG_MULTIPLE_REQS;
2523*44704f69SBart Van Assche     if (! flagsp->coe)
2524*44704f69SBart Van Assche         ctl_v4.flags |= SGV4_FLAG_STOP_IF;
2525*44704f69SBart Van Assche     if (clp->mrq_polled)
2526*44704f69SBart Van Assche         ctl_v4.flags |= SGV4_FLAG_POLLED;
2527*44704f69SBart Van Assche     if (clp->in_flags.mout_if || clp->out_flags.mout_if) {
2528*44704f69SBart Van Assche         ctl_v4.flags |= SGV4_FLAG_META_OUT_IF;
2529*44704f69SBart Van Assche         if (num_mrq > 0)
2530*44704f69SBart Van Assche             a_v4[0].info = UINT32_MAX;
2531*44704f69SBart Van Assche     }
2532*44704f69SBart Van Assche     ctl_v4.dout_xferp = (uint64_t)a_v4.data();        /* request array */
2533*44704f69SBart Van Assche     ctl_v4.dout_xfer_len = a_v4.size() * sizeof(struct sg_io_v4);
2534*44704f69SBart Van Assche     ctl_v4.din_xferp = (uint64_t)a_v4.data();         /* response array */
2535*44704f69SBart Van Assche     ctl_v4.din_xfer_len = a_v4.size() * sizeof(struct sg_io_v4);
2536*44704f69SBart Van Assche     if (false /* allow_mrq_abort */) {
2537*44704f69SBart Van Assche         ctl_v4.request_extra = mrq_pack_id_base + ++rep->mrq_pack_id_off;
2538*44704f69SBart Van Assche         clp->most_recent_pack_id.store(ctl_v4.request_extra);
2539*44704f69SBart Van Assche     }
2540*44704f69SBart Van Assche 
2541*44704f69SBart Van Assche     if (vb && vb_first_time.load()) {
2542*44704f69SBart Van Assche         pr2serr_lk("First controlling object output by ioctl(%s), flags: "
2543*44704f69SBart Van Assche                    "%s\n", iosub_str, sg_flags_str(ctl_v4.flags, b_len, b));
2544*44704f69SBart Van Assche         vb_first_time.store(false);
2545*44704f69SBart Van Assche     } else if (vb > 4) {
2546*44704f69SBart Van Assche         pr2serr_lk("[%d] %s: >> Control object _before_ ioctl(%s):\n", id,
2547*44704f69SBart Van Assche                    __func__, iosub_str);
2548*44704f69SBart Van Assche     }
2549*44704f69SBart Van Assche     if (vb > 4) {
2550*44704f69SBart Van Assche         if (vb > 5)
2551*44704f69SBart Van Assche             hex2stderr_lk((const uint8_t *)&ctl_v4, sizeof(ctl_v4), 1);
2552*44704f69SBart Van Assche         v4hdr_out_lk(">> Control object before", &ctl_v4, id, false);
2553*44704f69SBart Van Assche     }
2554*44704f69SBart Van Assche 
2555*44704f69SBart Van Assche try_again:
2556*44704f69SBart Van Assche     if (!after1 && (vb > 1)) {
2557*44704f69SBart Van Assche         after1 = true;
2558*44704f69SBart Van Assche         pr2serr_lk("%s: %s\n", __func__, serial ? mrq_ob_s : mrq_vb_s);
2559*44704f69SBart Van Assche     }
2560*44704f69SBart Van Assche     if (serial)
2561*44704f69SBart Van Assche         res = ioctl(fd, SG_IO, &ctl_v4);
2562*44704f69SBart Van Assche     else
2563*44704f69SBart Van Assche         res = ioctl(fd, SG_IOSUBMIT, &ctl_v4);  /* overlapping commands */
2564*44704f69SBart Van Assche     if (res < 0) {
2565*44704f69SBart Van Assche         err = errno;
2566*44704f69SBart Van Assche         if (E2BIG == err)
2567*44704f69SBart Van Assche             sg_take_snap(fd, id, true);
2568*44704f69SBart Van Assche         else if (EBUSY == err) {
2569*44704f69SBart Van Assche             ++num_ebusy;
2570*44704f69SBart Van Assche             std::this_thread::yield();/* allow another thread to progress */
2571*44704f69SBart Van Assche             goto try_again;
2572*44704f69SBart Van Assche         }
2573*44704f69SBart Van Assche         pr2serr_lk("[%d] %s: ioctl(%s, %s)-->%d, errno=%d: %s\n", id,
2574*44704f69SBart Van Assche                    __func__, iosub_str, sg_flags_str(ctl_v4.flags, b_len, b),
2575*44704f69SBart Van Assche                    res, err, strerror(err));
2576*44704f69SBart Van Assche         return -err;
2577*44704f69SBart Van Assche     }
2578*44704f69SBart Van Assche     if (vb > 4) {
2579*44704f69SBart Van Assche         pr2serr_lk("%s: >> Control object after ioctl(%s) seg_blks=%d:\n",
2580*44704f69SBart Van Assche                    __func__, iosub_str, o_seg_blks);
2581*44704f69SBart Van Assche         if (vb > 5)
2582*44704f69SBart Van Assche             hex2stderr_lk((const uint8_t *)&ctl_v4, sizeof(ctl_v4), 1);
2583*44704f69SBart Van Assche         v4hdr_out_lk(">> Control object after", &ctl_v4, id, false);
2584*44704f69SBart Van Assche         if (vb > 5) {
2585*44704f69SBart Van Assche             for (k = 0; k < num_mrq; ++k) {
2586*44704f69SBart Van Assche                 if ((vb > 6) || a_v4p[k].info) {
2587*44704f69SBart Van Assche                     snprintf(b, b_len, "a_v4[%d/%d]", k, num_mrq);
2588*44704f69SBart Van Assche                     v4hdr_out_lk(b, (a_v4p + k), id, true);
2589*44704f69SBart Van Assche                 }
2590*44704f69SBart Van Assche             }
2591*44704f69SBart Van Assche         }
2592*44704f69SBart Van Assche     }
2593*44704f69SBart Van Assche     num_good = process_mrq_response(rep, &ctl_v4, a_v4p, num_mrq, in_fin_blks,
2594*44704f69SBart Van Assche                                     out_fin_blks, err_on_in);
2595*44704f69SBart Van Assche     if (is_wr)
2596*44704f69SBart Van Assche         out_mrq_q_blks = mrq_q_blks;
2597*44704f69SBart Van Assche     else
2598*44704f69SBart Van Assche         in_mrq_q_blks = mrq_q_blks;
2599*44704f69SBart Van Assche     if (vb > 2)
2600*44704f69SBart Van Assche         pr2serr_lk("%s: >>> seg_blks=%d, num_good=%d, in_q/fin blks=%u/%u;  "
2601*44704f69SBart Van Assche                    "out_q/fin blks=%u/%u\n", __func__, o_seg_blks, num_good,
2602*44704f69SBart Van Assche                    in_mrq_q_blks, in_fin_blks, out_mrq_q_blks, out_fin_blks);
2603*44704f69SBart Van Assche 
2604*44704f69SBart Van Assche     if (clp->ese) {
2605*44704f69SBart Van Assche         int sres = ctl_v4.spare_out;
2606*44704f69SBart Van Assche 
2607*44704f69SBart Van Assche         if (sres != 0) {
2608*44704f69SBart Van Assche             clp->reason_res.store(sg_convert_errno(sres));
2609*44704f69SBart Van Assche             pr2serr_lk("Exit due to secondary error [%d]\n", sres);
2610*44704f69SBart Van Assche             return -sres;
2611*44704f69SBart Van Assche         }
2612*44704f69SBart Van Assche     }
2613*44704f69SBart Van Assche     if (num_good < 0)
2614*44704f69SBart Van Assche         return -ENODATA;
2615*44704f69SBart Van Assche     else {
2616*44704f69SBart Van Assche         if (num_good < num_mrq) {
2617*44704f69SBart Van Assche             int resid_blks = in_mrq_q_blks - in_fin_blks;
2618*44704f69SBart Van Assche 
2619*44704f69SBart Van Assche             if (resid_blks > 0) {
2620*44704f69SBart Van Assche                 rep->in_rem_count += resid_blks;
2621*44704f69SBart Van Assche                 rep->stop_after_write = ! (err_on_in && clp->in_flags.coe);
2622*44704f69SBart Van Assche             }
2623*44704f69SBart Van Assche 
2624*44704f69SBart Van Assche             resid_blks = out_mrq_q_blks - out_fin_blks;
2625*44704f69SBart Van Assche             if (resid_blks > 0) {
2626*44704f69SBart Van Assche                 rep->out_rem_count += resid_blks;
2627*44704f69SBart Van Assche                 rep->stop_after_write = ! (! err_on_in && clp->out_flags.coe);
2628*44704f69SBart Van Assche             }
2629*44704f69SBart Van Assche         }
2630*44704f69SBart Van Assche     }
2631*44704f69SBart Van Assche     return is_wr ? out_fin_blks : in_fin_blks;
2632*44704f69SBart Van Assche }
2633*44704f69SBart Van Assche 
2634*44704f69SBart Van Assche /* Returns number of blocks successfully processed or a negative error
2635*44704f69SBart Van Assche  * number. */
2636*44704f69SBart Van Assche static int
do_normal_normal_segment(Rq_elem * rep,scat_gath_iter & i_sg_it,scat_gath_iter & o_sg_it,int seg_blks)2637*44704f69SBart Van Assche do_normal_normal_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
2638*44704f69SBart Van Assche                          scat_gath_iter & o_sg_it, int seg_blks)
2639*44704f69SBart Van Assche {
2640*44704f69SBart Van Assche     int k, kk, res, id, num, d_off;
2641*44704f69SBart Van Assche     int o_seg_blks = seg_blks;
2642*44704f69SBart Van Assche     uint32_t in_fin_blks = 0;
2643*44704f69SBart Van Assche     uint32_t out_fin_blks = 0;
2644*44704f69SBart Van Assche     struct global_collection * clp = rep->clp;
2645*44704f69SBart Van Assche 
2646*44704f69SBart Van Assche     id = rep->id;
2647*44704f69SBart Van Assche     d_off = 0;
2648*44704f69SBart Van Assche     for (k = 0; seg_blks > 0; ++k, seg_blks -= num, d_off += num) {
2649*44704f69SBart Van Assche         kk = min<int>(seg_blks, clp->bpt);
2650*44704f69SBart Van Assche         num = i_sg_it.linear_for_n_blks(kk);
2651*44704f69SBart Van Assche         res = normal_in_rd(rep, i_sg_it.current_lba(), num,
2652*44704f69SBart Van Assche                            d_off * rep->bs);
2653*44704f69SBart Van Assche         if (res < 0) {
2654*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: normal in failed d_off=%d, err=%d\n",
2655*44704f69SBart Van Assche                        id, __func__, d_off, -res);
2656*44704f69SBart Van Assche             break;
2657*44704f69SBart Van Assche         }
2658*44704f69SBart Van Assche         i_sg_it.add_blks(res);
2659*44704f69SBart Van Assche         if (res < num) {
2660*44704f69SBart Van Assche             d_off += res;
2661*44704f69SBart Van Assche             rep->stop_after_write = true;
2662*44704f69SBart Van Assche             break;
2663*44704f69SBart Van Assche         }
2664*44704f69SBart Van Assche     }
2665*44704f69SBart Van Assche     seg_blks = d_off;
2666*44704f69SBart Van Assche     in_fin_blks = seg_blks;
2667*44704f69SBart Van Assche 
2668*44704f69SBart Van Assche     if (FT_DEV_NULL == clp->out_type)
2669*44704f69SBart Van Assche         goto fini;
2670*44704f69SBart Van Assche     d_off = 0;
2671*44704f69SBart Van Assche     for (k = 0; seg_blks > 0; ++k, seg_blks -= num, d_off += num) {
2672*44704f69SBart Van Assche         kk = min<int>(seg_blks, clp->bpt);
2673*44704f69SBart Van Assche         num = o_sg_it.linear_for_n_blks(kk);
2674*44704f69SBart Van Assche         res = normal_out_wr(rep, o_sg_it.current_lba(), num,
2675*44704f69SBart Van Assche                             d_off * rep->bs);
2676*44704f69SBart Van Assche         if (res < num) {
2677*44704f69SBart Van Assche             if (res < 0) {
2678*44704f69SBart Van Assche                 pr2serr_lk("[%d] %s: normal out failed d_off=%d, err=%d\n",
2679*44704f69SBart Van Assche                            id, __func__, d_off, -res);
2680*44704f69SBart Van Assche                 break;
2681*44704f69SBart Van Assche             }
2682*44704f69SBart Van Assche         }
2683*44704f69SBart Van Assche         o_sg_it.add_blks(res);
2684*44704f69SBart Van Assche         if (res < num) {
2685*44704f69SBart Van Assche             d_off += res;
2686*44704f69SBart Van Assche             rep->stop_after_write = true;
2687*44704f69SBart Van Assche             break;
2688*44704f69SBart Van Assche         }
2689*44704f69SBart Van Assche     }
2690*44704f69SBart Van Assche     if (rep->in_resid_bytes > 0) {
2691*44704f69SBart Van Assche         res = extra_out_wr(rep, rep->in_resid_bytes, d_off * rep->bs);
2692*44704f69SBart Van Assche         if (res < 0)
2693*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: extr out failed d_off=%d, err=%d\n", id,
2694*44704f69SBart Van Assche                        __func__, d_off, -res);
2695*44704f69SBart Van Assche         rep->in_resid_bytes = 0;
2696*44704f69SBart Van Assche     }
2697*44704f69SBart Van Assche     seg_blks = d_off;
2698*44704f69SBart Van Assche     out_fin_blks = seg_blks;
2699*44704f69SBart Van Assche 
2700*44704f69SBart Van Assche fini:
2701*44704f69SBart Van Assche     rep->in_local_count += in_fin_blks;
2702*44704f69SBart Van Assche     rep->out_local_count += out_fin_blks;
2703*44704f69SBart Van Assche 
2704*44704f69SBart Van Assche     if ((in_fin_blks + out_fin_blks) < (uint32_t)o_seg_blks) {
2705*44704f69SBart Van Assche         int resid_blks = o_seg_blks - in_fin_blks;
2706*44704f69SBart Van Assche 
2707*44704f69SBart Van Assche         if (resid_blks > 0)
2708*44704f69SBart Van Assche             rep->in_rem_count += resid_blks;
2709*44704f69SBart Van Assche         resid_blks = o_seg_blks - out_fin_blks;
2710*44704f69SBart Van Assche         if (resid_blks > 0)
2711*44704f69SBart Van Assche             rep->out_rem_count += resid_blks;
2712*44704f69SBart Van Assche     }
2713*44704f69SBart Van Assche     return res < 0 ? res : (min<int>(in_fin_blks, out_fin_blks));
2714*44704f69SBart Van Assche }
2715*44704f69SBart Van Assche 
2716*44704f69SBart Van Assche /* Returns number of blocks successfully processed or a negative error
2717*44704f69SBart Van Assche  * number. */
2718*44704f69SBart Van Assche static int
do_normal_sg_segment(Rq_elem * rep,scat_gath_iter & i_sg_it,scat_gath_iter & o_sg_it,int seg_blks,vector<cdb_arr_t> & a_cdb,vector<struct sg_io_v4> & a_v4)2719*44704f69SBart Van Assche do_normal_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
2720*44704f69SBart Van Assche                      scat_gath_iter & o_sg_it, int seg_blks,
2721*44704f69SBart Van Assche                      vector<cdb_arr_t> & a_cdb,
2722*44704f69SBart Van Assche                      vector<struct sg_io_v4> & a_v4)
2723*44704f69SBart Van Assche {
2724*44704f69SBart Van Assche     bool in_is_normal = ! rep->only_in_sg;
2725*44704f69SBart Van Assche     int k, kk, res, id, num, d_off;
2726*44704f69SBart Van Assche     int o_seg_blks = seg_blks;
2727*44704f69SBart Van Assche     uint32_t in_fin_blks = 0;
2728*44704f69SBart Van Assche     uint32_t out_fin_blks = 0;
2729*44704f69SBart Van Assche     struct global_collection * clp = rep->clp;
2730*44704f69SBart Van Assche 
2731*44704f69SBart Van Assche     id = rep->id;
2732*44704f69SBart Van Assche     a_cdb.clear();
2733*44704f69SBart Van Assche     a_v4.clear();
2734*44704f69SBart Van Assche 
2735*44704f69SBart Van Assche     if (in_is_normal) {   /* in: normal --> out : sg */
2736*44704f69SBart Van Assche         d_off = 0;
2737*44704f69SBart Van Assche         for (k = 0; seg_blks > 0; ++k, seg_blks -= num, d_off += num) {
2738*44704f69SBart Van Assche             kk = min<int>(seg_blks, clp->bpt);
2739*44704f69SBart Van Assche             num = i_sg_it.linear_for_n_blks(kk);
2740*44704f69SBart Van Assche             res = normal_in_rd(rep, i_sg_it.current_lba(), num,
2741*44704f69SBart Van Assche                                d_off * rep->bs);
2742*44704f69SBart Van Assche             if (res < 0) {
2743*44704f69SBart Van Assche                 pr2serr_lk("[%d] %s: normal in failed d_off=%d, err=%d\n",
2744*44704f69SBart Van Assche                            id, __func__, d_off, -res);
2745*44704f69SBart Van Assche                 break;
2746*44704f69SBart Van Assche             }
2747*44704f69SBart Van Assche             i_sg_it.add_blks(res);
2748*44704f69SBart Van Assche             if (res < num) {
2749*44704f69SBart Van Assche                 d_off += res;
2750*44704f69SBart Van Assche                 rep->stop_after_write = true;
2751*44704f69SBart Van Assche                 break;
2752*44704f69SBart Van Assche             }
2753*44704f69SBart Van Assche         }
2754*44704f69SBart Van Assche         seg_blks = d_off;
2755*44704f69SBart Van Assche         in_fin_blks = seg_blks;
2756*44704f69SBart Van Assche 
2757*44704f69SBart Van Assche         if (rep->in_resid_bytes > 0) {
2758*44704f69SBart Van Assche             ++seg_blks;
2759*44704f69SBart Van Assche             rep->in_resid_bytes = 0;
2760*44704f69SBart Van Assche         }
2761*44704f69SBart Van Assche         if (clp->mrq_eq_0)
2762*44704f69SBart Van Assche             res = sg_half_segment_mrq0(rep, o_sg_it, true /* is_wr */,
2763*44704f69SBart Van Assche                                       seg_blks, rep->buffp);
2764*44704f69SBart Van Assche         else
2765*44704f69SBart Van Assche             res = sg_half_segment(rep, o_sg_it, true /* is_wr */, seg_blks,
2766*44704f69SBart Van Assche                                   rep->buffp, a_cdb, a_v4);
2767*44704f69SBart Van Assche         if (res < seg_blks) {
2768*44704f69SBart Van Assche             if (res < 0) {
2769*44704f69SBart Van Assche                 pr2serr_lk("[%d] %s: sg out failed d_off=%d, err=%d\n",
2770*44704f69SBart Van Assche                            id, __func__, d_off, -res);
2771*44704f69SBart Van Assche                 goto fini;
2772*44704f69SBart Van Assche             }
2773*44704f69SBart Van Assche             rep->stop_after_write = true;
2774*44704f69SBart Van Assche         }
2775*44704f69SBart Van Assche         seg_blks = res;
2776*44704f69SBart Van Assche         out_fin_blks = seg_blks;
2777*44704f69SBart Van Assche 
2778*44704f69SBart Van Assche     } else {      /* in: sg --> out: normal */
2779*44704f69SBart Van Assche         if (clp->mrq_eq_0)
2780*44704f69SBart Van Assche             res = sg_half_segment_mrq0(rep, i_sg_it, false, seg_blks,
2781*44704f69SBart Van Assche                                        rep->buffp);
2782*44704f69SBart Van Assche         else
2783*44704f69SBart Van Assche             res = sg_half_segment(rep, i_sg_it, false, seg_blks, rep->buffp,
2784*44704f69SBart Van Assche                                   a_cdb, a_v4);
2785*44704f69SBart Van Assche         if (res < seg_blks) {
2786*44704f69SBart Van Assche             if (res < 0) {
2787*44704f69SBart Van Assche                 pr2serr_lk("[%d] %s: sg in failed, err=%d\n", id, __func__,
2788*44704f69SBart Van Assche                            -res);
2789*44704f69SBart Van Assche                 goto fini;
2790*44704f69SBart Van Assche             }
2791*44704f69SBart Van Assche             rep->stop_after_write = true;
2792*44704f69SBart Van Assche         }
2793*44704f69SBart Van Assche         seg_blks = res;
2794*44704f69SBart Van Assche         in_fin_blks = seg_blks;
2795*44704f69SBart Van Assche 
2796*44704f69SBart Van Assche         if (FT_DEV_NULL == clp->out_type) {
2797*44704f69SBart Van Assche             out_fin_blks = seg_blks;/* so finish logic doesn't suspect ... */
2798*44704f69SBart Van Assche             goto bypass;
2799*44704f69SBart Van Assche         }
2800*44704f69SBart Van Assche         d_off = 0;
2801*44704f69SBart Van Assche         for (k = 0; seg_blks > 0; ++k, seg_blks -= num, d_off += num) {
2802*44704f69SBart Van Assche             kk = min<int>(seg_blks, clp->bpt);
2803*44704f69SBart Van Assche             num = o_sg_it.linear_for_n_blks(kk);
2804*44704f69SBart Van Assche             res = normal_out_wr(rep, o_sg_it.current_lba(), num,
2805*44704f69SBart Van Assche                                 d_off * rep->bs);
2806*44704f69SBart Van Assche             if (res < num) {
2807*44704f69SBart Van Assche                 if (res < 0) {
2808*44704f69SBart Van Assche                     pr2serr_lk("[%d] %s: normal out failed d_off=%d, err=%d\n",
2809*44704f69SBart Van Assche                                id, __func__, d_off, -res);
2810*44704f69SBart Van Assche                     break;
2811*44704f69SBart Van Assche                 }
2812*44704f69SBart Van Assche             }
2813*44704f69SBart Van Assche             o_sg_it.add_blks(res);
2814*44704f69SBart Van Assche             if (res < num) {
2815*44704f69SBart Van Assche                 d_off += res;
2816*44704f69SBart Van Assche                 rep->stop_after_write = true;
2817*44704f69SBart Van Assche                 break;
2818*44704f69SBart Van Assche             }
2819*44704f69SBart Van Assche         }
2820*44704f69SBart Van Assche         seg_blks = d_off;
2821*44704f69SBart Van Assche         out_fin_blks = seg_blks;
2822*44704f69SBart Van Assche     }
2823*44704f69SBart Van Assche bypass:
2824*44704f69SBart Van Assche     rep->in_local_count += in_fin_blks;
2825*44704f69SBart Van Assche     rep->out_local_count += out_fin_blks;
2826*44704f69SBart Van Assche 
2827*44704f69SBart Van Assche     if ((in_fin_blks + out_fin_blks) < (uint32_t)o_seg_blks) {
2828*44704f69SBart Van Assche         int resid_blks = o_seg_blks - in_fin_blks;
2829*44704f69SBart Van Assche 
2830*44704f69SBart Van Assche         if (resid_blks > 0)
2831*44704f69SBart Van Assche             rep->in_rem_count += resid_blks;
2832*44704f69SBart Van Assche         resid_blks = o_seg_blks - out_fin_blks;
2833*44704f69SBart Van Assche         if (resid_blks > 0)
2834*44704f69SBart Van Assche             rep->out_rem_count += resid_blks;
2835*44704f69SBart Van Assche     }
2836*44704f69SBart Van Assche fini:
2837*44704f69SBart Van Assche     return res < 0 ? res : (min<int>(in_fin_blks, out_fin_blks));
2838*44704f69SBart Van Assche }
2839*44704f69SBart Van Assche 
2840*44704f69SBart Van Assche /* This function sets up a multiple request (mrq) transaction and sends it
2841*44704f69SBart Van Assche  * to the pass-through. Returns number of blocks processed (==seg_blks for
2842*44704f69SBart Van Assche  * all good) or a negative error number. */
2843*44704f69SBart Van Assche static int
do_both_sg_segment_mrq0(Rq_elem * rep,scat_gath_iter & i_sg_it,scat_gath_iter & o_sg_it,int seg_blks)2844*44704f69SBart Van Assche do_both_sg_segment_mrq0(Rq_elem * rep, scat_gath_iter & i_sg_it,
2845*44704f69SBart Van Assche                         scat_gath_iter & o_sg_it, int seg_blks)
2846*44704f69SBart Van Assche {
2847*44704f69SBart Van Assche     int k, kk, res, pack_id_base, id, iflags, oflags;
2848*44704f69SBart Van Assche     int num, i_lin_blks, o_lin_blks, cdbsz, err;
2849*44704f69SBart Van Assche     uint32_t in_fin_blks = 0;
2850*44704f69SBart Van Assche     uint32_t out_fin_blks = 0;
2851*44704f69SBart Van Assche     struct global_collection * clp = rep->clp;
2852*44704f69SBart Van Assche     int vb = clp->verbose;
2853*44704f69SBart Van Assche     cdb_arr_t t_cdb {};
2854*44704f69SBart Van Assche     struct sg_io_v4 t_v4 {};
2855*44704f69SBart Van Assche     struct sg_io_v4 * t_v4p = &t_v4;
2856*44704f69SBart Van Assche     struct flags_t * iflagsp = &clp->in_flags;
2857*44704f69SBart Van Assche     struct flags_t * oflagsp = &clp->out_flags;
2858*44704f69SBart Van Assche     const char * const a_ioctl_s = "do_both_sg_segment_mrq0: after "
2859*44704f69SBart Van Assche                                    "ioctl(SG_IO)";
2860*44704f69SBart Van Assche 
2861*44704f69SBart Van Assche     id = rep->id;
2862*44704f69SBart Van Assche     pack_id_base = id * PACK_ID_TID_MULTIPLIER;
2863*44704f69SBart Van Assche 
2864*44704f69SBart Van Assche     iflags = SGV4_FLAG_SHARE;
2865*44704f69SBart Van Assche     if (iflagsp->mmap && (rep->outregfd >= 0))
2866*44704f69SBart Van Assche         iflags |= SGV4_FLAG_MMAP_IO;
2867*44704f69SBart Van Assche     else
2868*44704f69SBart Van Assche         iflags |= SGV4_FLAG_NO_DXFER;
2869*44704f69SBart Van Assche     if (iflagsp->dio)
2870*44704f69SBart Van Assche         iflags |= SGV4_FLAG_DIRECT_IO;
2871*44704f69SBart Van Assche     if (iflagsp->qhead)
2872*44704f69SBart Van Assche         iflags |= SGV4_FLAG_Q_AT_HEAD;
2873*44704f69SBart Van Assche     if (iflagsp->qtail)
2874*44704f69SBart Van Assche         iflags |= SGV4_FLAG_Q_AT_TAIL;
2875*44704f69SBart Van Assche     if (iflagsp->polled)
2876*44704f69SBart Van Assche         iflags |= SGV4_FLAG_POLLED;
2877*44704f69SBart Van Assche 
2878*44704f69SBart Van Assche     oflags = SGV4_FLAG_SHARE | SGV4_FLAG_NO_DXFER;
2879*44704f69SBart Van Assche     if (oflagsp->dio)
2880*44704f69SBart Van Assche         oflags |= SGV4_FLAG_DIRECT_IO;
2881*44704f69SBart Van Assche     if (oflagsp->qhead)
2882*44704f69SBart Van Assche         oflags |= SGV4_FLAG_Q_AT_HEAD;
2883*44704f69SBart Van Assche     if (oflagsp->qtail)
2884*44704f69SBart Van Assche         oflags |= SGV4_FLAG_Q_AT_TAIL;
2885*44704f69SBart Van Assche     if (oflagsp->polled)
2886*44704f69SBart Van Assche         oflags |= SGV4_FLAG_POLLED;
2887*44704f69SBart Van Assche 
2888*44704f69SBart Van Assche     for (k = 0; seg_blks > 0; ++k, seg_blks -= num) {
2889*44704f69SBart Van Assche         kk = min<int>(seg_blks, clp->bpt);
2890*44704f69SBart Van Assche         i_lin_blks = i_sg_it.linear_for_n_blks(kk);
2891*44704f69SBart Van Assche         o_lin_blks = o_sg_it.linear_for_n_blks(kk);
2892*44704f69SBart Van Assche         num = min<int>(i_lin_blks, o_lin_blks);
2893*44704f69SBart Van Assche         if (num <= 0) {
2894*44704f69SBart Van Assche             res = 0;
2895*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: min(i_lin_blks=%d o_lin_blks=%d) < 1\n", id,
2896*44704f69SBart Van Assche                        __func__, i_lin_blks, o_lin_blks);
2897*44704f69SBart Van Assche             break;
2898*44704f69SBart Van Assche         }
2899*44704f69SBart Van Assche 
2900*44704f69SBart Van Assche         /* First build the command/request for the read-side*/
2901*44704f69SBart Van Assche         cdbsz = clp->cdbsz_in;
2902*44704f69SBart Van Assche         res = sg_build_scsi_cdb(t_cdb.data(), cdbsz, num,
2903*44704f69SBart Van Assche                                 i_sg_it.current_lba(), false, false,
2904*44704f69SBart Van Assche                                 iflagsp->fua, iflagsp->dpo, iflagsp->cdl);
2905*44704f69SBart Van Assche         if (res) {
2906*44704f69SBart Van Assche             pr2serr_lk("%s: t=%d: input sg_build_scsi_cdb() failed\n",
2907*44704f69SBart Van Assche                        __func__, id);
2908*44704f69SBart Van Assche             break;
2909*44704f69SBart Van Assche         } else if (vb > 3)
2910*44704f69SBart Van Assche             lk_print_command_len("input cdb: ", t_cdb.data(), cdbsz, true);
2911*44704f69SBart Van Assche 
2912*44704f69SBart Van Assche         t_v4p->guard = 'Q';
2913*44704f69SBart Van Assche         t_v4p->request = (uint64_t)t_cdb.data();
2914*44704f69SBart Van Assche         t_v4p->usr_ptr = t_v4p->request;
2915*44704f69SBart Van Assche         t_v4p->response = (uint64_t)rep->sb;
2916*44704f69SBart Van Assche         t_v4p->max_response_len = sizeof(rep->sb);
2917*44704f69SBart Van Assche         t_v4p->flags = iflags;
2918*44704f69SBart Van Assche         t_v4p->request_len = cdbsz;
2919*44704f69SBart Van Assche         t_v4p->din_xfer_len = num * rep->bs;
2920*44704f69SBart Van Assche         t_v4p->dout_xfer_len = 0;
2921*44704f69SBart Van Assche         t_v4p->timeout = clp->cmd_timeout;
2922*44704f69SBart Van Assche         t_v4p->request_extra = pack_id_base + ++rep->mrq_pack_id_off;
2923*44704f69SBart Van Assche         clp->most_recent_pack_id.store(t_v4p->request_extra);
2924*44704f69SBart Van Assche mrq0_again:
2925*44704f69SBart Van Assche         res = ioctl(rep->infd, SG_IO, t_v4p);
2926*44704f69SBart Van Assche         err = errno;
2927*44704f69SBart Van Assche         if (vb > 5)
2928*44704f69SBart Van Assche             v4hdr_out_lk(a_ioctl_s, t_v4p, id, false);
2929*44704f69SBart Van Assche         if (res < 0) {
2930*44704f69SBart Van Assche             if (E2BIG == err)
2931*44704f69SBart Van Assche                 sg_take_snap(rep->infd, id, true);
2932*44704f69SBart Van Assche             else if (EBUSY == err) {
2933*44704f69SBart Van Assche                 ++num_ebusy;
2934*44704f69SBart Van Assche                 std::this_thread::yield();/* so other threads can progress */
2935*44704f69SBart Van Assche                 goto mrq0_again;
2936*44704f69SBart Van Assche             }
2937*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: ioctl(SG_IO, read-side)-->%d, errno=%d: "
2938*44704f69SBart Van Assche                        "%s\n", id, __func__, res, err, strerror(err));
2939*44704f69SBart Van Assche             return -err;
2940*44704f69SBart Van Assche         }
2941*44704f69SBart Van Assche         if (t_v4p->device_status || t_v4p->transport_status ||
2942*44704f69SBart Van Assche             t_v4p->driver_status) {
2943*44704f69SBart Van Assche             rep->stop_now = true;
2944*44704f69SBart Van Assche             pr2serr_lk("[%d] t_v4[%d]:\n", id, k);
2945*44704f69SBart Van Assche             lk_chk_n_print4("    ", t_v4p, vb > 4);
2946*44704f69SBart Van Assche             return min<int>(in_fin_blks, out_fin_blks);
2947*44704f69SBart Van Assche         }
2948*44704f69SBart Van Assche         rep->in_local_count += num;
2949*44704f69SBart Van Assche         in_fin_blks += num;
2950*44704f69SBart Van Assche 
2951*44704f69SBart Van Assche         /* Now build the command/request for write-side (WRITE or VERIFY) */
2952*44704f69SBart Van Assche         cdbsz = clp->cdbsz_out;
2953*44704f69SBart Van Assche         res = sg_build_scsi_cdb(t_cdb.data(), cdbsz, num,
2954*44704f69SBart Van Assche                                 o_sg_it.current_lba(), clp->verify, true,
2955*44704f69SBart Van Assche                                 oflagsp->fua, oflagsp->dpo, oflagsp->cdl);
2956*44704f69SBart Van Assche         if (res) {
2957*44704f69SBart Van Assche             pr2serr_lk("%s: t=%d: output sg_build_scsi_cdb() failed\n",
2958*44704f69SBart Van Assche                        __func__, id);
2959*44704f69SBart Van Assche             break;
2960*44704f69SBart Van Assche         } else if (vb > 3)
2961*44704f69SBart Van Assche             lk_print_command_len("output cdb: ", t_cdb.data(), cdbsz, true);
2962*44704f69SBart Van Assche 
2963*44704f69SBart Van Assche         t_v4p->guard = 'Q';
2964*44704f69SBart Van Assche         t_v4p->request = (uint64_t)t_cdb.data();
2965*44704f69SBart Van Assche         t_v4p->usr_ptr = t_v4p->request;
2966*44704f69SBart Van Assche         t_v4p->response = (uint64_t)rep->sb;
2967*44704f69SBart Van Assche         t_v4p->max_response_len = sizeof(rep->sb);
2968*44704f69SBart Van Assche         t_v4p->flags = oflags;
2969*44704f69SBart Van Assche         t_v4p->request_len = cdbsz;
2970*44704f69SBart Van Assche         t_v4p->din_xfer_len = 0;
2971*44704f69SBart Van Assche         t_v4p->dout_xfer_len = num * rep->bs;
2972*44704f69SBart Van Assche         t_v4p->timeout = clp->cmd_timeout;
2973*44704f69SBart Van Assche         t_v4p->request_extra = pack_id_base + ++rep->mrq_pack_id_off;
2974*44704f69SBart Van Assche         clp->most_recent_pack_id.store(t_v4p->request_extra);
2975*44704f69SBart Van Assche mrq0_again2:
2976*44704f69SBart Van Assche         res = ioctl(rep->outfd, SG_IO, t_v4p);
2977*44704f69SBart Van Assche         err = errno;
2978*44704f69SBart Van Assche         if (vb > 5)
2979*44704f69SBart Van Assche             v4hdr_out_lk(a_ioctl_s, t_v4p, id, false);
2980*44704f69SBart Van Assche         if (res < 0) {
2981*44704f69SBart Van Assche             if (E2BIG == err)
2982*44704f69SBart Van Assche                 sg_take_snap(rep->outfd, id, true);
2983*44704f69SBart Van Assche             else if (EBUSY == err) {
2984*44704f69SBart Van Assche                 ++num_ebusy;
2985*44704f69SBart Van Assche                 std::this_thread::yield();/* so other threads can progress */
2986*44704f69SBart Van Assche                 goto mrq0_again2;
2987*44704f69SBart Van Assche             }
2988*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: ioctl(SG_IO, write-side)-->%d, errno=%d: "
2989*44704f69SBart Van Assche                        "%s\n", id, __func__, res, err, strerror(err));
2990*44704f69SBart Van Assche             return -err;
2991*44704f69SBart Van Assche         }
2992*44704f69SBart Van Assche         if (t_v4p->device_status || t_v4p->transport_status ||
2993*44704f69SBart Van Assche             t_v4p->driver_status) {
2994*44704f69SBart Van Assche             rep->stop_now = true;
2995*44704f69SBart Van Assche             pr2serr_lk("[%d] t_v4[%d]:\n", id, k);
2996*44704f69SBart Van Assche             lk_chk_n_print4("    ", t_v4p, vb > 4);
2997*44704f69SBart Van Assche             return min<int>(in_fin_blks, out_fin_blks);
2998*44704f69SBart Van Assche         }
2999*44704f69SBart Van Assche         rep->out_local_count += num;
3000*44704f69SBart Van Assche         out_fin_blks += num;
3001*44704f69SBart Van Assche 
3002*44704f69SBart Van Assche         i_sg_it.add_blks(num);
3003*44704f69SBart Van Assche         o_sg_it.add_blks(num);
3004*44704f69SBart Van Assche     }
3005*44704f69SBart Van Assche     return min<int>(in_fin_blks, out_fin_blks);
3006*44704f69SBart Van Assche }
3007*44704f69SBart Van Assche 
3008*44704f69SBart Van Assche /* This function sets up a multiple request (mrq) transaction and sends it
3009*44704f69SBart Van Assche  * to the pass-through. Returns number of blocks processed (==seg_blks for
3010*44704f69SBart Van Assche  * all good) or a negative error number. */
3011*44704f69SBart Van Assche static int
do_both_sg_segment(Rq_elem * rep,scat_gath_iter & i_sg_it,scat_gath_iter & o_sg_it,int seg_blks,vector<cdb_arr_t> & a_cdb,vector<struct sg_io_v4> & a_v4)3012*44704f69SBart Van Assche do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it,
3013*44704f69SBart Van Assche                    scat_gath_iter & o_sg_it, int seg_blks,
3014*44704f69SBart Van Assche                    vector<cdb_arr_t> & a_cdb,
3015*44704f69SBart Van Assche                    vector<struct sg_io_v4> & a_v4)
3016*44704f69SBart Van Assche {
3017*44704f69SBart Van Assche     bool err_on_in = false;
3018*44704f69SBart Van Assche     int num_mrq, k, res, fd, mrq_pack_id_base, id, b_len, iflags, oflags;
3019*44704f69SBart Van Assche     int num, kk, i_lin_blks, o_lin_blks, cdbsz, num_good, err;
3020*44704f69SBart Van Assche     int o_seg_blks = seg_blks;
3021*44704f69SBart Van Assche     uint32_t in_fin_blks = 0;
3022*44704f69SBart Van Assche     uint32_t out_fin_blks = 0;;
3023*44704f69SBart Van Assche     uint32_t in_mrq_q_blks = 0;
3024*44704f69SBart Van Assche     uint32_t out_mrq_q_blks = 0;
3025*44704f69SBart Van Assche     const int max_cdb_sz = MAX_SCSI_CDB_SZ;
3026*44704f69SBart Van Assche     struct sg_io_v4 * a_v4p;
3027*44704f69SBart Van Assche     struct sg_io_v4 ctl_v4 {};     /* MRQ control object */
3028*44704f69SBart Van Assche     struct global_collection * clp = rep->clp;
3029*44704f69SBart Van Assche     const char * iosub_str = "SG_IOSUBMIT(svb)";
3030*44704f69SBart Van Assche     char b[80];
3031*44704f69SBart Van Assche     cdb_arr_t t_cdb {};
3032*44704f69SBart Van Assche     struct sg_io_v4 t_v4 {};
3033*44704f69SBart Van Assche     struct sg_io_v4 * t_v4p = &t_v4;
3034*44704f69SBart Van Assche     struct flags_t * iflagsp = &clp->in_flags;
3035*44704f69SBart Van Assche     struct flags_t * oflagsp = &clp->out_flags;
3036*44704f69SBart Van Assche     int vb = clp->verbose;
3037*44704f69SBart Van Assche 
3038*44704f69SBart Van Assche     id = rep->id;
3039*44704f69SBart Van Assche     b_len = sizeof(b);
3040*44704f69SBart Van Assche 
3041*44704f69SBart Van Assche     a_cdb.clear();
3042*44704f69SBart Van Assche     a_v4.clear();
3043*44704f69SBart Van Assche     rep->a_mrq_din_blks = 0;
3044*44704f69SBart Van Assche     rep->a_mrq_dout_blks = 0;
3045*44704f69SBart Van Assche     mrq_pack_id_base = id * PACK_ID_TID_MULTIPLIER;
3046*44704f69SBart Van Assche 
3047*44704f69SBart Van Assche     iflags = SGV4_FLAG_SHARE;
3048*44704f69SBart Van Assche     if (iflagsp->mmap && (rep->outregfd >= 0))
3049*44704f69SBart Van Assche         iflags |= SGV4_FLAG_MMAP_IO;
3050*44704f69SBart Van Assche     else
3051*44704f69SBart Van Assche         iflags |= SGV4_FLAG_NO_DXFER;
3052*44704f69SBart Van Assche     if (iflagsp->dio)
3053*44704f69SBart Van Assche         iflags |= SGV4_FLAG_DIRECT_IO;
3054*44704f69SBart Van Assche     if (iflagsp->qhead)
3055*44704f69SBart Van Assche         iflags |= SGV4_FLAG_Q_AT_HEAD;
3056*44704f69SBart Van Assche     if (iflagsp->qtail)
3057*44704f69SBart Van Assche         iflags |= SGV4_FLAG_Q_AT_TAIL;
3058*44704f69SBart Van Assche     if (iflagsp->polled)
3059*44704f69SBart Van Assche         iflags |= SGV4_FLAG_POLLED;
3060*44704f69SBart Van Assche 
3061*44704f69SBart Van Assche     oflags = SGV4_FLAG_SHARE | SGV4_FLAG_NO_DXFER;
3062*44704f69SBart Van Assche     if (oflagsp->dio)
3063*44704f69SBart Van Assche         oflags |= SGV4_FLAG_DIRECT_IO;
3064*44704f69SBart Van Assche     if (oflagsp->qhead)
3065*44704f69SBart Van Assche         oflags |= SGV4_FLAG_Q_AT_HEAD;
3066*44704f69SBart Van Assche     if (oflagsp->qtail)
3067*44704f69SBart Van Assche         oflags |= SGV4_FLAG_Q_AT_TAIL;
3068*44704f69SBart Van Assche     if (oflagsp->polled)
3069*44704f69SBart Van Assche         oflags |= SGV4_FLAG_POLLED;
3070*44704f69SBart Van Assche     oflags |= SGV4_FLAG_DO_ON_OTHER;
3071*44704f69SBart Van Assche 
3072*44704f69SBart Van Assche     for (k = 0; seg_blks > 0; ++k, seg_blks -= num) {
3073*44704f69SBart Van Assche         kk = min<int>(seg_blks, clp->bpt);
3074*44704f69SBart Van Assche         i_lin_blks = i_sg_it.linear_for_n_blks(kk);
3075*44704f69SBart Van Assche         o_lin_blks = o_sg_it.linear_for_n_blks(kk);
3076*44704f69SBart Van Assche         num = min<int>(i_lin_blks, o_lin_blks);
3077*44704f69SBart Van Assche         if (num <= 0) {
3078*44704f69SBart Van Assche             res = 0;
3079*44704f69SBart Van Assche             pr2serr_lk("[%d] %s: min(i_lin_blks=%d o_lin_blks=%d) < 1\n", id,
3080*44704f69SBart Van Assche                        __func__, i_lin_blks, o_lin_blks);
3081*44704f69SBart Van Assche             break;
3082*44704f69SBart Van Assche         }
3083*44704f69SBart Van Assche 
3084*44704f69SBart Van Assche         /* First build the command/request for the read-side*/
3085*44704f69SBart Van Assche         cdbsz = clp->cdbsz_in;
3086*44704f69SBart Van Assche         res = sg_build_scsi_cdb(t_cdb.data(), cdbsz, num,
3087*44704f69SBart Van Assche                                 i_sg_it.current_lba(), false, false,
3088*44704f69SBart Van Assche                                 iflagsp->fua, iflagsp->dpo, iflagsp->cdl);
3089*44704f69SBart Van Assche         if (res) {
3090*44704f69SBart Van Assche             pr2serr_lk("%s: t=%d: input sg_build_scsi_cdb() failed\n",
3091*44704f69SBart Van Assche                        __func__, id);
3092*44704f69SBart Van Assche             break;
3093*44704f69SBart Van Assche         } else if (vb > 3)
3094*44704f69SBart Van Assche             lk_print_command_len("input cdb: ", t_cdb.data(), cdbsz, true);
3095*44704f69SBart Van Assche         a_cdb.push_back(t_cdb);
3096*44704f69SBart Van Assche 
3097*44704f69SBart Van Assche         t_v4p->guard = 'Q';
3098*44704f69SBart Van Assche         t_v4p->flags = iflags;
3099*44704f69SBart Van Assche         t_v4p->request_len = cdbsz;
3100*44704f69SBart Van Assche         t_v4p->response = (uint64_t)rep->sb;
3101*44704f69SBart Van Assche         t_v4p->max_response_len = sizeof(rep->sb);
3102*44704f69SBart Van Assche         t_v4p->usr_ptr = (uint64_t)&a_cdb[a_cdb.size() - 1];
3103*44704f69SBart Van Assche         t_v4p->din_xfer_len = num * rep->bs;
3104*44704f69SBart Van Assche         rep->a_mrq_din_blks += num;
3105*44704f69SBart Van Assche         t_v4p->dout_xfer_len = 0;
3106*44704f69SBart Van Assche         t_v4p->timeout = clp->cmd_timeout;
3107*44704f69SBart Van Assche         in_mrq_q_blks += num;
3108*44704f69SBart Van Assche         t_v4p->request_extra = mrq_pack_id_base + ++rep->mrq_pack_id_off;
3109*44704f69SBart Van Assche         clp->most_recent_pack_id.store(t_v4p->request_extra);
3110*44704f69SBart Van Assche         a_v4.push_back(t_v4);
3111*44704f69SBart Van Assche 
3112*44704f69SBart Van Assche         /* Now build the command/request for write-side (WRITE or VERIFY) */
3113*44704f69SBart Van Assche         cdbsz = clp->cdbsz_out;
3114*44704f69SBart Van Assche         res = sg_build_scsi_cdb(t_cdb.data(), cdbsz, num,
3115*44704f69SBart Van Assche                                 o_sg_it.current_lba(), clp->verify, true,
3116*44704f69SBart Van Assche                                 oflagsp->fua, oflagsp->dpo, oflagsp->cdl);
3117*44704f69SBart Van Assche         if (res) {
3118*44704f69SBart Van Assche             pr2serr_lk("%s: t=%d: output sg_build_scsi_cdb() failed\n",
3119*44704f69SBart Van Assche                        __func__, id);
3120*44704f69SBart Van Assche             break;
3121*44704f69SBart Van Assche         } else if (vb > 3)
3122*44704f69SBart Van Assche             lk_print_command_len("output cdb: ", t_cdb.data(), cdbsz, true);
3123*44704f69SBart Van Assche         a_cdb.push_back(t_cdb);
3124*44704f69SBart Van Assche         t_v4p->guard = 'Q';
3125*44704f69SBart Van Assche         t_v4p->flags = oflags;
3126*44704f69SBart Van Assche         t_v4p->request_len = cdbsz;
3127*44704f69SBart Van Assche         t_v4p->response = (uint64_t)rep->sb;
3128*44704f69SBart Van Assche         t_v4p->max_response_len = sizeof(rep->sb);
3129*44704f69SBart Van Assche         t_v4p->usr_ptr = (uint64_t)&a_cdb[a_cdb.size() - 1];
3130*44704f69SBart Van Assche         t_v4p->din_xfer_len = 0;
3131*44704f69SBart Van Assche         t_v4p->dout_xfer_len = num * rep->bs;
3132*44704f69SBart Van Assche         rep->a_mrq_dout_blks += num;
3133*44704f69SBart Van Assche         t_v4p->timeout = clp->cmd_timeout;
3134*44704f69SBart Van Assche         out_mrq_q_blks += num;
3135*44704f69SBart Van Assche         t_v4p->request_extra = mrq_pack_id_base + ++rep->mrq_pack_id_off;
3136*44704f69SBart Van Assche         clp->most_recent_pack_id.store(t_v4p->request_extra);
3137*44704f69SBart Van Assche         a_v4.push_back(t_v4);
3138*44704f69SBart Van Assche 
3139*44704f69SBart Van Assche         i_sg_it.add_blks(num);
3140*44704f69SBart Van Assche         o_sg_it.add_blks(num);
3141*44704f69SBart Van Assche     }
3142*44704f69SBart Van Assche 
3143*44704f69SBart Van Assche     if (vb > 6) {
3144*44704f69SBart Van Assche         pr2serr_lk("%s: t=%d: a_v4 array contents:\n", __func__, id);
3145*44704f69SBart Van Assche         hex2stderr_lk((const uint8_t *)a_v4.data(),
3146*44704f69SBart Van Assche                       a_v4.size() * sizeof(struct sg_io_v4), 1);
3147*44704f69SBart Van Assche     }
3148*44704f69SBart Van Assche     if (rep->both_sg || rep->same_sg)
3149*44704f69SBart Van Assche         fd = rep->infd;         /* assume share to rep->outfd */
3150*44704f69SBart Van Assche     else {
3151*44704f69SBart Van Assche         pr2serr_lk("[%d] %s: why am I here? Want 2 sg devices\n", id,
3152*44704f69SBart Van Assche                    __func__);
3153*44704f69SBart Van Assche         res = -1;
3154*44704f69SBart Van Assche         goto fini;
3155*44704f69SBart Van Assche     }
3156*44704f69SBart Van Assche     num_mrq = a_v4.size();
3157*44704f69SBart Van Assche     a_v4p = a_v4.data();
3158*44704f69SBart Van Assche     res = 0;
3159*44704f69SBart Van Assche     ctl_v4.guard = 'Q';
3160*44704f69SBart Van Assche     ctl_v4.request_len = a_cdb.size() * max_cdb_sz;
3161*44704f69SBart Van Assche     ctl_v4.request = (uint64_t)a_cdb.data();
3162*44704f69SBart Van Assche     ctl_v4.max_response_len = sizeof(rep->sb);
3163*44704f69SBart Van Assche     ctl_v4.response = (uint64_t)rep->sb;
3164*44704f69SBart Van Assche     ctl_v4.flags = SGV4_FLAG_MULTIPLE_REQS | SGV4_FLAG_SHARE;
3165*44704f69SBart Van Assche     if (! (iflagsp->coe || oflagsp->coe))
3166*44704f69SBart Van Assche         ctl_v4.flags |= SGV4_FLAG_STOP_IF;
3167*44704f69SBart Van Assche     if ((! clp->verify) && clp->out_flags.order_wr)
3168*44704f69SBart Van Assche         ctl_v4.flags |= SGV4_FLAG_ORDERED_WR;
3169*44704f69SBart Van Assche     if (clp->mrq_polled)
3170*44704f69SBart Van Assche         ctl_v4.flags |= SGV4_FLAG_POLLED;
3171*44704f69SBart Van Assche     if (clp->in_flags.mout_if || clp->out_flags.mout_if) {
3172*44704f69SBart Van Assche         ctl_v4.flags |= SGV4_FLAG_META_OUT_IF;
3173*44704f69SBart Van Assche         if (num_mrq > 0)
3174*44704f69SBart Van Assche             a_v4[0].info = UINT32_MAX;
3175*44704f69SBart Van Assche     }
3176*44704f69SBart Van Assche     ctl_v4.dout_xferp = (uint64_t)a_v4.data();        /* request array */
3177*44704f69SBart Van Assche     ctl_v4.dout_xfer_len = a_v4.size() * sizeof(struct sg_io_v4);
3178*44704f69SBart Van Assche     ctl_v4.din_xferp = (uint64_t)a_v4.data();         /* response array */
3179*44704f69SBart Van Assche     ctl_v4.din_xfer_len = a_v4.size() * sizeof(struct sg_io_v4);
3180*44704f69SBart Van Assche     if (false /* allow_mrq_abort */) {
3181*44704f69SBart Van Assche         ctl_v4.request_extra = mrq_pack_id_base + ++rep->mrq_pack_id_off;
3182*44704f69SBart Van Assche         clp->most_recent_pack_id.store(ctl_v4.request_extra);
3183*44704f69SBart Van Assche     }
3184*44704f69SBart Van Assche 
3185*44704f69SBart Van Assche     if (vb && vb_first_time.load()) {
3186*44704f69SBart Van Assche         pr2serr_lk("First controlling object output by ioctl(%s), flags: "
3187*44704f69SBart Van Assche                    "%s\n", iosub_str, sg_flags_str(ctl_v4.flags, b_len, b));
3188*44704f69SBart Van Assche         vb_first_time.store(false);
3189*44704f69SBart Van Assche     } else if (vb > 4)
3190*44704f69SBart Van Assche         pr2serr_lk("%s: >> Control object _before_ ioctl(%s):\n", __func__,
3191*44704f69SBart Van Assche                    iosub_str);
3192*44704f69SBart Van Assche     if (vb > 4) {
3193*44704f69SBart Van Assche         if (vb > 5)
3194*44704f69SBart Van Assche             hex2stderr_lk((const uint8_t *)&ctl_v4, sizeof(ctl_v4), 1);
3195*44704f69SBart Van Assche         v4hdr_out_lk(">> Control object before", &ctl_v4, id, false);
3196*44704f69SBart Van Assche     }
3197*44704f69SBart Van Assche 
3198*44704f69SBart Van Assche try_again:
3199*44704f69SBart Van Assche     if (!after1 && (vb > 1)) {
3200*44704f69SBart Van Assche         after1 = true;
3201*44704f69SBart Van Assche         pr2serr_lk("%s: %s\n", __func__, mrq_svb_s);
3202*44704f69SBart Van Assche     }
3203*44704f69SBart Van Assche     res = ioctl(fd, SG_IOSUBMIT, &ctl_v4);
3204*44704f69SBart Van Assche     if (res < 0) {
3205*44704f69SBart Van Assche         err = errno;
3206*44704f69SBart Van Assche         if (E2BIG == err)
3207*44704f69SBart Van Assche                 sg_take_snap(fd, id, true);
3208*44704f69SBart Van Assche         else if (EBUSY == err) {
3209*44704f69SBart Van Assche             ++num_ebusy;
3210*44704f69SBart Van Assche             std::this_thread::yield();/* allow another thread to progress */
3211*44704f69SBart Van Assche             goto try_again;
3212*44704f69SBart Van Assche         }
3213*44704f69SBart Van Assche         pr2serr_lk("%s: ioctl(%s, %s)-->%d, errno=%d: %s\n", __func__,
3214*44704f69SBart Van Assche                    iosub_str, sg_flags_str(ctl_v4.flags, b_len, b), res, err,
3215*44704f69SBart Van Assche                    strerror(err));
3216*44704f69SBart Van Assche         res = -err;
3217*44704f69SBart Van Assche         goto fini;
3218*44704f69SBart Van Assche     }
3219*44704f69SBart Van Assche     if (vb > 4) {
3220*44704f69SBart Van Assche         pr2serr_lk("%s: >> Control object after ioctl(%s) seg_blks=%d:\n",
3221*44704f69SBart Van Assche                    __func__, iosub_str, o_seg_blks);
3222*44704f69SBart Van Assche         if (vb > 5)
3223*44704f69SBart Van Assche             hex2stderr_lk((const uint8_t *)&ctl_v4, sizeof(ctl_v4), 1);
3224*44704f69SBart Van Assche         v4hdr_out_lk(">> Control object after", &ctl_v4, id, false);
3225*44704f69SBart Van Assche         if (vb > 5) {
3226*44704f69SBart Van Assche             for (k = 0; k < num_mrq; ++k) {
3227*44704f69SBart Van Assche                 if ((vb > 6) || a_v4p[k].info) {
3228*44704f69SBart Van Assche                     snprintf(b, b_len, "a_v4[%d/%d]", k, num_mrq);
3229*44704f69SBart Van Assche                     v4hdr_out_lk(b, (a_v4p + k), id, true);
3230*44704f69SBart Van Assche                 }
3231*44704f69SBart Van Assche             }
3232*44704f69SBart Van Assche         }
3233*44704f69SBart Van Assche     }
3234*44704f69SBart Van Assche     num_good = process_mrq_response(rep, &ctl_v4, a_v4p, num_mrq, in_fin_blks,
3235*44704f69SBart Van Assche                                     out_fin_blks, err_on_in);
3236*44704f69SBart Van Assche     if (vb > 2)
3237*44704f69SBart Van Assche         pr2serr_lk("%s: >>> seg_blks=%d, num_good=%d, in_q/fin blks=%u/%u;  "
3238*44704f69SBart Van Assche                    "out_q/fin blks=%u/%u\n", __func__, o_seg_blks, num_good,
3239*44704f69SBart Van Assche                    in_mrq_q_blks, in_fin_blks, out_mrq_q_blks, out_fin_blks);
3240*44704f69SBart Van Assche 
3241*44704f69SBart Van Assche     if (clp->ese) {
3242*44704f69SBart Van Assche         int sres = ctl_v4.spare_out;
3243*44704f69SBart Van Assche 
3244*44704f69SBart Van Assche         if (sres != 0) {
3245*44704f69SBart Van Assche             clp->reason_res.store(sg_convert_errno(sres));
3246*44704f69SBart Van Assche             pr2serr_lk("Exit due to secondary error [%d]\n", sres);
3247*44704f69SBart Van Assche             return -sres;
3248*44704f69SBart Van Assche         }
3249*44704f69SBart Van Assche     }
3250*44704f69SBart Van Assche     if (num_good < 0)
3251*44704f69SBart Van Assche         res = -ENODATA;
3252*44704f69SBart Van Assche     else {
3253*44704f69SBart Van Assche         rep->in_local_count += in_fin_blks;
3254*44704f69SBart Van Assche         rep->out_local_count += out_fin_blks;
3255*44704f69SBart Van Assche 
3256*44704f69SBart Van Assche         if (num_good < num_mrq) {       /* reduced number completed */
3257*44704f69SBart Van Assche             int resid_blks = in_mrq_q_blks - in_fin_blks;
3258*44704f69SBart Van Assche 
3259*44704f69SBart Van Assche             if (resid_blks > 0) {
3260*44704f69SBart Van Assche                 rep->in_rem_count += resid_blks;
3261*44704f69SBart Van Assche                 rep->stop_after_write = ! (err_on_in && clp->in_flags.coe);
3262*44704f69SBart Van Assche             }
3263*44704f69SBart Van Assche 
3264*44704f69SBart Van Assche             resid_blks = out_mrq_q_blks - out_fin_blks;
3265*44704f69SBart Van Assche             if (resid_blks > 0) {
3266*44704f69SBart Van Assche                 rep->out_rem_count += resid_blks;
3267*44704f69SBart Van Assche                 rep->stop_after_write = ! ((! err_on_in) &&
3268*44704f69SBart Van Assche                                            clp->out_flags.coe);
3269*44704f69SBart Van Assche             }
3270*44704f69SBart Van Assche         }
3271*44704f69SBart Van Assche     }
3272*44704f69SBart Van Assche fini:
3273*44704f69SBart Van Assche     return res < 0 ? res : (min<int>(in_fin_blks, out_fin_blks));
3274*44704f69SBart Van Assche }
3275*44704f69SBart Van Assche 
3276*44704f69SBart Van Assche #if 0
3277*44704f69SBart Van Assche /* Returns number found and (partially) processed. 'num' is the number of
3278*44704f69SBart Van Assche  * completions to wait for when > 0. When 'num' is zero check all inflight
3279*44704f69SBart Van Assche  * request on 'fd' and return quickly if none completed (i.e. don't wait)
3280*44704f69SBart Van Assche  * If error return negative errno and if no request inflight or waiting
3281*44704f69SBart Van Assche  * then return -9999 . */
3282*44704f69SBart Van Assche static int
3283*44704f69SBart Van Assche sg_blk_poll(int fd, int num)
3284*44704f69SBart Van Assche {
3285*44704f69SBart Van Assche     int res;
3286*44704f69SBart Van Assche     struct sg_extended_info sei {};
3287*44704f69SBart Van Assche     struct sg_extended_info * seip = &sei;
3288*44704f69SBart Van Assche 
3289*44704f69SBart Van Assche     seip->sei_rd_mask |= SG_SEIM_BLK_POLL;
3290*44704f69SBart Van Assche     seip->sei_wr_mask |= SG_SEIM_BLK_POLL;
3291*44704f69SBart Van Assche     seip->num = (num < 0) ? 0 : num;
3292*44704f69SBart Van Assche     res = ioctl(fd, SG_SET_GET_EXTENDED, seip);
3293*44704f69SBart Van Assche     if (res < 0) {
3294*44704f69SBart Van Assche         pr2serr_lk("%s: SG_SET_GET_EXTENDED(BLK_POLL) error: %s\n",
3295*44704f69SBart Van Assche                    __func__, strerror(errno));
3296*44704f69SBart Van Assche         return res;
3297*44704f69SBart Van Assche     }
3298*44704f69SBart Van Assche     return (seip->num == -1) ? -9999 : seip->num;
3299*44704f69SBart Van Assche }
3300*44704f69SBart Van Assche #endif
3301*44704f69SBart Van Assche 
3302*44704f69SBart Van Assche /* Returns the number of times 'ch' is found in string 's' given the
3303*44704f69SBart Van Assche  * string's length. */
3304*44704f69SBart Van Assche static int
num_chs_in_str(const char * s,int slen,int ch)3305*44704f69SBart Van Assche num_chs_in_str(const char * s, int slen, int ch)
3306*44704f69SBart Van Assche {
3307*44704f69SBart Van Assche     int res = 0;
3308*44704f69SBart Van Assche 
3309*44704f69SBart Van Assche     while (--slen >= 0) {
3310*44704f69SBart Van Assche         if (ch == s[slen])
3311*44704f69SBart Van Assche             ++res;
3312*44704f69SBart Van Assche     }
3313*44704f69SBart Van Assche     return res;
3314*44704f69SBart Van Assche }
3315*44704f69SBart Van Assche 
3316*44704f69SBart Van Assche /* Returns the number of times either 'ch1' or 'ch2' is found in
3317*44704f69SBart Van Assche  * string 's' given the string's length. */
3318*44704f69SBart Van Assche int
num_either_ch_in_str(const char * s,int slen,int ch1,int ch2)3319*44704f69SBart Van Assche num_either_ch_in_str(const char * s, int slen, int ch1, int ch2)
3320*44704f69SBart Van Assche {
3321*44704f69SBart Van Assche     int k;
3322*44704f69SBart Van Assche     int res = 0;
3323*44704f69SBart Van Assche 
3324*44704f69SBart Van Assche     while (--slen >= 0) {
3325*44704f69SBart Van Assche         k = s[slen];
3326*44704f69SBart Van Assche         if ((ch1 == k) || (ch2 == k))
3327*44704f69SBart Van Assche             ++res;
3328*44704f69SBart Van Assche     }
3329*44704f69SBart Van Assche     return res;
3330*44704f69SBart Van Assche }
3331*44704f69SBart Van Assche 
3332*44704f69SBart Van Assche /* Allocates and then populates a scatter gether list (array) and returns
3333*44704f69SBart Van Assche  * it via *sgl_pp. Return of 0 is okay, else error number (in which case
3334*44704f69SBart Van Assche  * NULL is written to *sgl_pp) . */
3335*44704f69SBart Van Assche static int
skip_seek(struct global_collection * clp,const char * key,const char * buf,bool is_skip,bool ignore_verbose)3336*44704f69SBart Van Assche skip_seek(struct global_collection *clp, const char * key, const char * buf,
3337*44704f69SBart Van Assche           bool is_skip, bool ignore_verbose)
3338*44704f69SBart Van Assche {
3339*44704f69SBart Van Assche     bool def_hex = false;
3340*44704f69SBart Van Assche     int len;
3341*44704f69SBart Van Assche     int vb = clp->verbose;  /* needs to appear before skip/seek= on cl */
3342*44704f69SBart Van Assche     int64_t ll;
3343*44704f69SBart Van Assche     const char * cp;
3344*44704f69SBart Van Assche     class scat_gath_list & either_list = is_skip ? clp->i_sgl : clp->o_sgl;
3345*44704f69SBart Van Assche 
3346*44704f69SBart Van Assche     if (ignore_verbose)
3347*44704f69SBart Van Assche         vb = 0;
3348*44704f69SBart Van Assche     len = (int)strlen(buf);
3349*44704f69SBart Van Assche     if ((('-' == buf[0]) && (1 == len)) || ((len > 1) && ('@' == buf[0])) ||
3350*44704f69SBart Van Assche         ((len > 2) && ('H' == toupper(buf[0])) && ('@' == buf[1]))) {
3351*44704f69SBart Van Assche         if ('H' == toupper(buf[0])) {
3352*44704f69SBart Van Assche             cp = buf + 2;
3353*44704f69SBart Van Assche             def_hex = true;
3354*44704f69SBart Van Assche         } else if ('-' == buf[0])
3355*44704f69SBart Van Assche             cp = buf;
3356*44704f69SBart Van Assche         else
3357*44704f69SBart Van Assche             cp = buf + 1;
3358*44704f69SBart Van Assche         if (! either_list.load_from_file(cp, def_hex, clp->flexible, true)) {
3359*44704f69SBart Van Assche             pr2serr("bad argument to '%s=' [err=%d]\n", key,
3360*44704f69SBart Van Assche                     either_list.m_errno);
3361*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
3362*44704f69SBart Van Assche         }
3363*44704f69SBart Van Assche     } else if (num_either_ch_in_str(buf, len, ',', ' ') > 0) {
3364*44704f69SBart Van Assche         if (! either_list.load_from_cli(buf, vb > 0)) {
3365*44704f69SBart Van Assche             pr2serr("bad command line argument to '%s='\n", key);
3366*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
3367*44704f69SBart Van Assche         }
3368*44704f69SBart Van Assche     } else {    /* single number on command line (e.g. skip=1234) */
3369*44704f69SBart Van Assche         ll = sg_get_llnum(buf);
3370*44704f69SBart Van Assche         if ((ll < 0) || (ll > MAX_COUNT_SKIP_SEEK)) {
3371*44704f69SBart Van Assche             pr2serr("bad argument to '%s='\n", key);
3372*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
3373*44704f69SBart Van Assche         }
3374*44704f69SBart Van Assche         either_list.append_1or(0, ll);
3375*44704f69SBart Van Assche         if (vb > 1)
3376*44704f69SBart Van Assche             pr2serr("%s: singleton, half a degenerate sgl element\n", key);
3377*44704f69SBart Van Assche     }
3378*44704f69SBart Van Assche 
3379*44704f69SBart Van Assche     either_list.sum_scan(key, vb > 3 /* bool show_sgl */, vb > 1);
3380*44704f69SBart Van Assche     return 0;
3381*44704f69SBart Van Assche }
3382*44704f69SBart Van Assche 
3383*44704f69SBart Van Assche static bool
process_flags(const char * arg,struct flags_t * fp)3384*44704f69SBart Van Assche process_flags(const char * arg, struct flags_t * fp)
3385*44704f69SBart Van Assche {
3386*44704f69SBart Van Assche     char buff[256];
3387*44704f69SBart Van Assche     char * cp;
3388*44704f69SBart Van Assche     char * np;
3389*44704f69SBart Van Assche 
3390*44704f69SBart Van Assche     strncpy(buff, arg, sizeof(buff));
3391*44704f69SBart Van Assche     buff[sizeof(buff) - 1] = '\0';
3392*44704f69SBart Van Assche     if ('\0' == buff[0]) {
3393*44704f69SBart Van Assche         pr2serr("no flag found\n");
3394*44704f69SBart Van Assche         return false;
3395*44704f69SBart Van Assche     }
3396*44704f69SBart Van Assche     cp = buff;
3397*44704f69SBart Van Assche     do {
3398*44704f69SBart Van Assche         np = strchr(cp, ',');
3399*44704f69SBart Van Assche         if (np)
3400*44704f69SBart Van Assche             *np++ = '\0';
3401*44704f69SBart Van Assche         if (0 == strcmp(cp, "00"))
3402*44704f69SBart Van Assche             fp->zero = true;
3403*44704f69SBart Van Assche         else if (0 == strcmp(cp, "append"))
3404*44704f69SBart Van Assche             fp->append = true;
3405*44704f69SBart Van Assche         else if (0 == strcmp(cp, "coe"))
3406*44704f69SBart Van Assche             fp->coe = true;
3407*44704f69SBart Van Assche         else if (0 == strcmp(cp, "dio"))
3408*44704f69SBart Van Assche             fp->dio = true;
3409*44704f69SBart Van Assche         else if (0 == strcmp(cp, "direct"))
3410*44704f69SBart Van Assche             fp->direct = true;
3411*44704f69SBart Van Assche         else if (0 == strcmp(cp, "dpo"))
3412*44704f69SBart Van Assche             fp->dpo = true;
3413*44704f69SBart Van Assche         else if (0 == strcmp(cp, "dsync"))
3414*44704f69SBart Van Assche             fp->dsync = true;
3415*44704f69SBart Van Assche         else if (0 == strcmp(cp, "excl"))
3416*44704f69SBart Van Assche             fp->excl = true;
3417*44704f69SBart Van Assche         else if (0 == strcmp(cp, "ff"))
3418*44704f69SBart Van Assche             fp->ff = true;
3419*44704f69SBart Van Assche         else if (0 == strcmp(cp, "fua"))
3420*44704f69SBart Van Assche             fp->fua = true;
3421*44704f69SBart Van Assche         else if (0 == strcmp(cp, "hipri"))
3422*44704f69SBart Van Assche             fp->polled = true;
3423*44704f69SBart Van Assche         else if (0 == strcmp(cp, "masync"))
3424*44704f69SBart Van Assche             fp->masync = true;
3425*44704f69SBart Van Assche         else if (0 == strcmp(cp, "mmap"))
3426*44704f69SBart Van Assche             ++fp->mmap;         /* mmap > 1 stops munmap() being called */
3427*44704f69SBart Van Assche         else if (0 == strcmp(cp, "nocreat"))
3428*44704f69SBart Van Assche             fp->nocreat = true;
3429*44704f69SBart Van Assche         else if (0 == strcmp(cp, "nodur"))
3430*44704f69SBart Van Assche             fp->no_dur = true;
3431*44704f69SBart Van Assche         else if (0 == strcmp(cp, "no_dur"))
3432*44704f69SBart Van Assche             fp->no_dur = true;
3433*44704f69SBart Van Assche         else if (0 == strcmp(cp, "no-dur"))
3434*44704f69SBart Van Assche             fp->no_dur = true;
3435*44704f69SBart Van Assche         else if (0 == strcmp(cp, "nothresh"))
3436*44704f69SBart Van Assche             fp->no_thresh = true;
3437*44704f69SBart Van Assche         else if (0 == strcmp(cp, "no_thresh"))
3438*44704f69SBart Van Assche             fp->no_thresh = true;
3439*44704f69SBart Van Assche         else if (0 == strcmp(cp, "no-thresh"))
3440*44704f69SBart Van Assche             fp->no_thresh = true;
3441*44704f69SBart Van Assche         else if (0 == strcmp(cp, "noxfer"))
3442*44704f69SBart Van Assche             ;           /* accept but ignore */
3443*44704f69SBart Van Assche         else if (0 == strcmp(cp, "null"))
3444*44704f69SBart Van Assche             ;
3445*44704f69SBart Van Assche         else if (0 == strcmp(cp, "ordered"))
3446*44704f69SBart Van Assche             fp->order_wr = true;
3447*44704f69SBart Van Assche         else if (0 == strcmp(cp, "order"))
3448*44704f69SBart Van Assche             fp->order_wr = true;
3449*44704f69SBart Van Assche         else if (0 == strcmp(cp, "polled"))
3450*44704f69SBart Van Assche             fp->polled = true;
3451*44704f69SBart Van Assche         else if (0 == strcmp(cp, "qhead"))
3452*44704f69SBart Van Assche             fp->qhead = true;
3453*44704f69SBart Van Assche         else if (0 == strcmp(cp, "qtail"))
3454*44704f69SBart Van Assche             fp->qtail = true;
3455*44704f69SBart Van Assche         else if (0 == strcmp(cp, "random"))
3456*44704f69SBart Van Assche             fp->random = true;
3457*44704f69SBart Van Assche         else if ((0 == strcmp(cp, "mout_if")) || (0 == strcmp(cp, "mout-if")))
3458*44704f69SBart Van Assche             fp->mout_if = true;
3459*44704f69SBart Van Assche         else if ((0 == strcmp(cp, "same_fds")) ||
3460*44704f69SBart Van Assche                  (0 == strcmp(cp, "same-fds")))
3461*44704f69SBart Van Assche             fp->same_fds = true;
3462*44704f69SBart Van Assche         else if (0 == strcmp(cp, "serial"))
3463*44704f69SBart Van Assche             fp->serial = true;
3464*44704f69SBart Van Assche         else if (0 == strcmp(cp, "swait"))
3465*44704f69SBart Van Assche             ;           /* accept but ignore */
3466*44704f69SBart Van Assche         else if (0 == strcmp(cp, "wq_excl"))
3467*44704f69SBart Van Assche             fp->wq_excl = true;
3468*44704f69SBart Van Assche         else {
3469*44704f69SBart Van Assche             pr2serr("unrecognised flag: %s\n", cp);
3470*44704f69SBart Van Assche             return false;
3471*44704f69SBart Van Assche         }
3472*44704f69SBart Van Assche         cp = np;
3473*44704f69SBart Van Assche     } while (cp);
3474*44704f69SBart Van Assche     return true;
3475*44704f69SBart Van Assche }
3476*44704f69SBart Van Assche 
3477*44704f69SBart Van Assche /* Process arguments given to 'conv=" option. Returns 0 on success,
3478*44704f69SBart Van Assche  * 1 on error. */
3479*44704f69SBart Van Assche static int
process_conv(const char * arg,struct flags_t * ifp,struct flags_t * ofp)3480*44704f69SBart Van Assche process_conv(const char * arg, struct flags_t * ifp, struct flags_t * ofp)
3481*44704f69SBart Van Assche {
3482*44704f69SBart Van Assche     char buff[256];
3483*44704f69SBart Van Assche     char * cp;
3484*44704f69SBart Van Assche     char * np;
3485*44704f69SBart Van Assche 
3486*44704f69SBart Van Assche     strncpy(buff, arg, sizeof(buff));
3487*44704f69SBart Van Assche     buff[sizeof(buff) - 1] = '\0';
3488*44704f69SBart Van Assche     if ('\0' == buff[0]) {
3489*44704f69SBart Van Assche         pr2serr("no conversions found\n");
3490*44704f69SBart Van Assche         return 1;
3491*44704f69SBart Van Assche     }
3492*44704f69SBart Van Assche     cp = buff;
3493*44704f69SBart Van Assche     do {
3494*44704f69SBart Van Assche         np = strchr(cp, ',');
3495*44704f69SBart Van Assche         if (np)
3496*44704f69SBart Van Assche             *np++ = '\0';
3497*44704f69SBart Van Assche         if (0 == strcmp(cp, "nocreat"))
3498*44704f69SBart Van Assche             ofp->nocreat = true;
3499*44704f69SBart Van Assche         else if (0 == strcmp(cp, "noerror"))
3500*44704f69SBart Van Assche             ifp->coe = true;         /* will still fail on write error */
3501*44704f69SBart Van Assche         else if (0 == strcmp(cp, "notrunc"))
3502*44704f69SBart Van Assche             ;         /* this is the default action of sg_dd so ignore */
3503*44704f69SBart Van Assche         else if (0 == strcmp(cp, "null"))
3504*44704f69SBart Van Assche             ;
3505*44704f69SBart Van Assche         else if (0 == strcmp(cp, "sync"))
3506*44704f69SBart Van Assche             ;   /* dd(susv4): pad errored block(s) with zeros but sg_dd does
3507*44704f69SBart Van Assche                  * that by default. Typical dd use: 'conv=noerror,sync' */
3508*44704f69SBart Van Assche         else {
3509*44704f69SBart Van Assche             pr2serr("unrecognised flag: %s\n", cp);
3510*44704f69SBart Van Assche             return 1;
3511*44704f69SBart Van Assche         }
3512*44704f69SBart Van Assche         cp = np;
3513*44704f69SBart Van Assche     } while (cp);
3514*44704f69SBart Van Assche     return 0;
3515*44704f69SBart Van Assche }
3516*44704f69SBart Van Assche 
3517*44704f69SBart Van Assche #define STR_SZ 1024
3518*44704f69SBart Van Assche #define INOUTF_SZ 512
3519*44704f69SBart Van Assche 
3520*44704f69SBart Van Assche static int
parse_cmdline_sanity(int argc,char * argv[],struct global_collection * clp,char * outregf)3521*44704f69SBart Van Assche parse_cmdline_sanity(int argc, char * argv[], struct global_collection * clp,
3522*44704f69SBart Van Assche                      char * outregf)
3523*44704f69SBart Van Assche {
3524*44704f69SBart Van Assche     bool contra = false;
3525*44704f69SBart Van Assche     bool verbose_given = false;
3526*44704f69SBart Van Assche     bool version_given = false;
3527*44704f69SBart Van Assche     bool verify_given = false;
3528*44704f69SBart Van Assche     bool bpt_given = false;
3529*44704f69SBart Van Assche     int ibs = 0;
3530*44704f69SBart Van Assche     int obs = 0;
3531*44704f69SBart Van Assche     int ret = 0;
3532*44704f69SBart Van Assche     int k, keylen, n, res;
3533*44704f69SBart Van Assche     char str[STR_SZ];
3534*44704f69SBart Van Assche     char * key;
3535*44704f69SBart Van Assche     char * buf;
3536*44704f69SBart Van Assche     char * skip_buf = NULL;
3537*44704f69SBart Van Assche     char * seek_buf = NULL;
3538*44704f69SBart Van Assche     const char * cp;
3539*44704f69SBart Van Assche     const char * ccp;
3540*44704f69SBart Van Assche 
3541*44704f69SBart Van Assche     for (k = 1; k < argc; k++) {
3542*44704f69SBart Van Assche         if (argv[k]) {
3543*44704f69SBart Van Assche             strncpy(str, argv[k], STR_SZ);
3544*44704f69SBart Van Assche             str[STR_SZ - 1] = '\0';
3545*44704f69SBart Van Assche         } else
3546*44704f69SBart Van Assche             continue;
3547*44704f69SBart Van Assche 
3548*44704f69SBart Van Assche         for (key = str, buf = key; *buf && *buf != '=';)
3549*44704f69SBart Van Assche             buf++;
3550*44704f69SBart Van Assche         if (*buf)
3551*44704f69SBart Van Assche             *buf++ = '\0';
3552*44704f69SBart Van Assche         keylen = strlen(key);
3553*44704f69SBart Van Assche         if (0 == strcmp(key, "bpt")) {
3554*44704f69SBart Van Assche             clp->bpt = sg_get_num(buf);
3555*44704f69SBart Van Assche             if ((clp->bpt < 0) || (clp->bpt > MAX_BPT_VALUE)) {
3556*44704f69SBart Van Assche                 pr2serr("%sbad argument to 'bpt='\n", my_name);
3557*44704f69SBart Van Assche                 goto syn_err;
3558*44704f69SBart Van Assche             }
3559*44704f69SBart Van Assche             bpt_given = true;
3560*44704f69SBart Van Assche         } else if (0 == strcmp(key, "bs")) {
3561*44704f69SBart Van Assche             clp->bs = sg_get_num(buf);
3562*44704f69SBart Van Assche             if ((clp->bs < 0) || (clp->bs > MAX_BPT_VALUE)) {
3563*44704f69SBart Van Assche                 pr2serr("%sbad argument to 'bs='\n", my_name);
3564*44704f69SBart Van Assche                 goto syn_err;
3565*44704f69SBart Van Assche             }
3566*44704f69SBart Van Assche         } else if (0 == strcmp(key, "cdbsz")) {
3567*44704f69SBart Van Assche             ccp = strchr(buf, ',');
3568*44704f69SBart Van Assche             n = sg_get_num(buf);
3569*44704f69SBart Van Assche             if ((n < 0) || (n > 32)) {
3570*44704f69SBart Van Assche                 pr2serr("%s: bad argument to 'cdbsz=', expect 6, 10, 12 or "
3571*44704f69SBart Van Assche                         "16\n", my_name);
3572*44704f69SBart Van Assche                 goto syn_err;
3573*44704f69SBart Van Assche             }
3574*44704f69SBart Van Assche             clp->cdbsz_in = n;
3575*44704f69SBart Van Assche             if (ccp) {
3576*44704f69SBart Van Assche                 n = sg_get_num(ccp + 1);
3577*44704f69SBart Van Assche                 if ((n < 0) || (n > 32)) {
3578*44704f69SBart Van Assche                     pr2serr("%s: bad second argument to 'cdbsz=', expect 6, "
3579*44704f69SBart Van Assche                             "10, 12 or 16\n", my_name);
3580*44704f69SBart Van Assche                     goto syn_err;
3581*44704f69SBart Van Assche                 }
3582*44704f69SBart Van Assche             }
3583*44704f69SBart Van Assche             clp->cdbsz_out = n;
3584*44704f69SBart Van Assche             clp->cdbsz_given = true;
3585*44704f69SBart Van Assche         } else if (0 == strcmp(key, "cdl")) {
3586*44704f69SBart Van Assche             ccp = strchr(buf, ',');
3587*44704f69SBart Van Assche             n = sg_get_num(buf);
3588*44704f69SBart Van Assche             if ((n < 0) || (n > 7)) {
3589*44704f69SBart Van Assche                 pr2serr("%s: bad argument to 'cdl=', expect 0 to 7\n",
3590*44704f69SBart Van Assche                          my_name);
3591*44704f69SBart Van Assche                 goto syn_err;
3592*44704f69SBart Van Assche             }
3593*44704f69SBart Van Assche             clp->in_flags.cdl = n;
3594*44704f69SBart Van Assche             if (ccp) {
3595*44704f69SBart Van Assche                 n = sg_get_num(ccp + 1);
3596*44704f69SBart Van Assche                 if ((n < 0) || (n > 7)) {
3597*44704f69SBart Van Assche                     pr2serr("%s: bad second argument to 'cdl=', expect 0 "
3598*44704f69SBart Van Assche                             "to 7\n", my_name);
3599*44704f69SBart Van Assche                     goto syn_err;
3600*44704f69SBart Van Assche                 }
3601*44704f69SBart Van Assche             }
3602*44704f69SBart Van Assche             clp->out_flags.cdl = n;
3603*44704f69SBart Van Assche             clp->cdl_given = true;
3604*44704f69SBart Van Assche         } else if (0 == strcmp(key, "coe")) {
3605*44704f69SBart Van Assche             /* not documented, for compat with sgh_dd */
3606*44704f69SBart Van Assche             clp->in_flags.coe = !! sg_get_num(buf);
3607*44704f69SBart Van Assche             clp->out_flags.coe = clp->in_flags.coe;
3608*44704f69SBart Van Assche         } else if (0 == strcmp(key, "conv")) {
3609*44704f69SBart Van Assche             if (process_conv(buf, &clp->in_flags, &clp->out_flags)) {
3610*44704f69SBart Van Assche                 pr2serr("%s: bad argument to 'conv='\n", my_name);
3611*44704f69SBart Van Assche                 goto syn_err;
3612*44704f69SBart Van Assche             }
3613*44704f69SBart Van Assche         } else if (0 == strcmp(key, "count")) {
3614*44704f69SBart Van Assche             if (clp->count_given) {
3615*44704f69SBart Van Assche                 pr2serr("second 'count=' argument detected, only one "
3616*44704f69SBart Van Assche                         "please\n");
3617*44704f69SBart Van Assche                 contra = true;
3618*44704f69SBart Van Assche                 goto syn_err;
3619*44704f69SBart Van Assche             }
3620*44704f69SBart Van Assche             if (0 != strcmp("-1", buf)) {
3621*44704f69SBart Van Assche                 clp->dd_count = sg_get_llnum(buf);
3622*44704f69SBart Van Assche                 if ((clp->dd_count < 0) ||
3623*44704f69SBart Van Assche                     (clp->dd_count > MAX_COUNT_SKIP_SEEK)) {
3624*44704f69SBart Van Assche                     pr2serr("%sbad argument to 'count='\n", my_name);
3625*44704f69SBart Van Assche                     goto syn_err;
3626*44704f69SBart Van Assche                 }
3627*44704f69SBart Van Assche             }   /* treat 'count=-1' as calculate count (same as not given) */
3628*44704f69SBart Van Assche             clp->count_given = true;
3629*44704f69SBart Van Assche         } else if (0 == strcmp(key, "dio")) {
3630*44704f69SBart Van Assche             clp->in_flags.dio = !! sg_get_num(buf);
3631*44704f69SBart Van Assche             clp->out_flags.dio = clp->in_flags.dio;
3632*44704f69SBart Van Assche         } else if (0 == strcmp(key, "elemsz_kb")) {
3633*44704f69SBart Van Assche             n = sg_get_num(buf);
3634*44704f69SBart Van Assche             if ((n < 1) || (n > (MAX_BPT_VALUE / 1024))) {
3635*44704f69SBart Van Assche                 pr2serr("elemsz_kb=EKB wants an integer > 0\n");
3636*44704f69SBart Van Assche                 goto syn_err;
3637*44704f69SBart Van Assche             }
3638*44704f69SBart Van Assche             if (n & (n - 1)) {
3639*44704f69SBart Van Assche                 pr2serr("elemsz_kb=EKB wants EKB to be power of 2\n");
3640*44704f69SBart Van Assche                 goto syn_err;
3641*44704f69SBart Van Assche             }
3642*44704f69SBart Van Assche             clp->elem_sz = n * 1024;
3643*44704f69SBart Van Assche         } else if (0 == strcmp(key, "ese")) {
3644*44704f69SBart Van Assche             n = sg_get_num(buf);
3645*44704f69SBart Van Assche             if (n < 0) {
3646*44704f69SBart Van Assche                 pr2serr("ese= wants 0 (default) or 1\n");
3647*44704f69SBart Van Assche                 goto syn_err;
3648*44704f69SBart Van Assche             }
3649*44704f69SBart Van Assche             clp->ese = !!n;
3650*44704f69SBart Van Assche         } else if (0 == strcmp(key, "fua")) {
3651*44704f69SBart Van Assche             n = sg_get_num(buf);
3652*44704f69SBart Van Assche             if (n & 1)
3653*44704f69SBart Van Assche                 clp->out_flags.fua = true;
3654*44704f69SBart Van Assche             if (n & 2)
3655*44704f69SBart Van Assche                 clp->in_flags.fua = true;
3656*44704f69SBart Van Assche         } else if (0 == strcmp(key, "ibs")) {
3657*44704f69SBart Van Assche             ibs = sg_get_num(buf);
3658*44704f69SBart Van Assche             if ((ibs < 0) || (ibs > MAX_BPT_VALUE)) {
3659*44704f69SBart Van Assche                 pr2serr("%sbad argument to 'ibs='\n", my_name);
3660*44704f69SBart Van Assche                 goto syn_err;
3661*44704f69SBart Van Assche             }
3662*44704f69SBart Van Assche         } else if (0 == strcmp(key, "if")) {
3663*44704f69SBart Van Assche             if (clp->inf_v.size() > 0) {
3664*44704f69SBart Van Assche                 pr2serr("Second 'if=' argument??\n");
3665*44704f69SBart Van Assche                 goto syn_err;
3666*44704f69SBart Van Assche             } else {
3667*44704f69SBart Van Assche                 cp = buf;
3668*44704f69SBart Van Assche                 while ((ccp = strchr(cp, ','))) {
3669*44704f69SBart Van Assche                     clp->inf_v.push_back(string(cp , ccp - cp));
3670*44704f69SBart Van Assche                     cp = ccp + 1;
3671*44704f69SBart Van Assche                 }
3672*44704f69SBart Van Assche                 clp->inf_v.push_back(string(cp , strlen(cp)));
3673*44704f69SBart Van Assche             }
3674*44704f69SBart Van Assche         } else if (0 == strcmp(key, "iflag")) {
3675*44704f69SBart Van Assche             if (! process_flags(buf, &clp->in_flags)) {
3676*44704f69SBart Van Assche                 pr2serr("%sbad argument to 'iflag='\n", my_name);
3677*44704f69SBart Van Assche                 goto syn_err;
3678*44704f69SBart Van Assche             }
3679*44704f69SBart Van Assche         } else if ((0 == strcmp(key, "hipri")) ||
3680*44704f69SBart Van Assche                    (0 == strcmp(key, "mrq")) ||
3681*44704f69SBart Van Assche                    (0 == strcmp(key, "polled"))) {
3682*44704f69SBart Van Assche             if (isdigit(buf[0]))
3683*44704f69SBart Van Assche                 cp = buf;
3684*44704f69SBart Van Assche             else {
3685*44704f69SBart Van Assche                 pr2serr("%sonly mrq=NRQS or polled=NRQS which is a number "
3686*44704f69SBart Van Assche                         "allowed here\n", my_name);
3687*44704f69SBart Van Assche                 goto syn_err;
3688*44704f69SBart Van Assche             }
3689*44704f69SBart Van Assche             clp->mrq_num = sg_get_num(cp);
3690*44704f69SBart Van Assche             if (clp->mrq_num < 0) {
3691*44704f69SBart Van Assche                 pr2serr("%sbad argument to 'mrq='\n", my_name);
3692*44704f69SBart Van Assche                 goto syn_err;
3693*44704f69SBart Van Assche             }
3694*44704f69SBart Van Assche             if (0 == clp->mrq_num) {
3695*44704f69SBart Van Assche                 clp->mrq_eq_0 = true;
3696*44704f69SBart Van Assche                 clp->mrq_num = 1;
3697*44704f69SBart Van Assche                 pr2serr("note: send single, non-mrq commands\n");
3698*44704f69SBart Van Assche             }
3699*44704f69SBart Van Assche             if ('m' != key[0])
3700*44704f69SBart Van Assche                 clp->mrq_polled = true;
3701*44704f69SBart Van Assche         } else if ((0 == strcmp(key, "no_waitq")) ||
3702*44704f69SBart Van Assche                    (0 == strcmp(key, "no-waitq"))) {
3703*44704f69SBart Van Assche             n = sg_get_num(buf);
3704*44704f69SBart Van Assche             if (-1 == n) {
3705*44704f69SBart Van Assche                 pr2serr("%sbad argument to 'no_waitq=', expect 0 or 1\n",
3706*44704f69SBart Van Assche                         my_name);
3707*44704f69SBart Van Assche                 goto syn_err;
3708*44704f69SBart Van Assche             }
3709*44704f69SBart Van Assche             clp->in_flags.no_waitq = true;
3710*44704f69SBart Van Assche             clp->out_flags.no_waitq = true;
3711*44704f69SBart Van Assche         } else if (0 == strcmp(key, "obs")) {
3712*44704f69SBart Van Assche             obs = sg_get_num(buf);
3713*44704f69SBart Van Assche             if ((obs < 0) || (obs > MAX_BPT_VALUE)) {
3714*44704f69SBart Van Assche                 pr2serr("%sbad argument to 'obs='\n", my_name);
3715*44704f69SBart Van Assche                 goto syn_err;
3716*44704f69SBart Van Assche             }
3717*44704f69SBart Van Assche         } else if (strcmp(key, "ofreg") == 0) {
3718*44704f69SBart Van Assche             if ('\0' != outregf[0]) {
3719*44704f69SBart Van Assche                 pr2serr("Second OFREG argument??\n");
3720*44704f69SBart Van Assche                 contra = true;
3721*44704f69SBart Van Assche                 goto syn_err;
3722*44704f69SBart Van Assche             } else {
3723*44704f69SBart Van Assche                 memcpy(outregf, buf, INOUTF_SZ);
3724*44704f69SBart Van Assche                 outregf[INOUTF_SZ - 1] = '\0';  /* noisy compiler */
3725*44704f69SBart Van Assche             }
3726*44704f69SBart Van Assche         } else if (strcmp(key, "of") == 0) {
3727*44704f69SBart Van Assche             if (clp->outf_v.size() > 0) {
3728*44704f69SBart Van Assche                 pr2serr("Second 'of=' argument??\n");
3729*44704f69SBart Van Assche                 goto syn_err;
3730*44704f69SBart Van Assche             } else {
3731*44704f69SBart Van Assche                 cp = buf;
3732*44704f69SBart Van Assche                 while ((ccp = strchr(cp, ','))) {
3733*44704f69SBart Van Assche                     clp->outf_v.push_back(string(cp , ccp - cp));
3734*44704f69SBart Van Assche                     cp = ccp + 1;
3735*44704f69SBart Van Assche                 }
3736*44704f69SBart Van Assche                 clp->outf_v.push_back(string(cp , strlen(cp)));
3737*44704f69SBart Van Assche             }
3738*44704f69SBart Van Assche         } else if (0 == strcmp(key, "oflag")) {
3739*44704f69SBart Van Assche             if (! process_flags(buf, &clp->out_flags)) {
3740*44704f69SBart Van Assche                 pr2serr("%sbad argument to 'oflag='\n", my_name);
3741*44704f69SBart Van Assche                 goto syn_err;
3742*44704f69SBart Van Assche             }
3743*44704f69SBart Van Assche         } else if (0 == strcmp(key, "sdt")) {
3744*44704f69SBart Van Assche             ccp = strchr(buf, ',');
3745*44704f69SBart Van Assche             n = sg_get_num(buf);
3746*44704f69SBart Van Assche             if (n < 0) {
3747*44704f69SBart Van Assche                 pr2serr("%sbad argument to 'sdt=CRT[,ICT]'\n", my_name);
3748*44704f69SBart Van Assche                 goto syn_err;
3749*44704f69SBart Van Assche             }
3750*44704f69SBart Van Assche             clp->sdt_crt = n;
3751*44704f69SBart Van Assche             if (ccp) {
3752*44704f69SBart Van Assche                 n = sg_get_num(ccp + 1);
3753*44704f69SBart Van Assche                 if (n < 0) {
3754*44704f69SBart Van Assche                     pr2serr("%sbad 2nd argument to 'sdt=CRT,ICT'\n",
3755*44704f69SBart Van Assche                             my_name);
3756*44704f69SBart Van Assche                     goto syn_err;
3757*44704f69SBart Van Assche                 }
3758*44704f69SBart Van Assche                 clp->sdt_ict = n;
3759*44704f69SBart Van Assche             }
3760*44704f69SBart Van Assche         } else if (0 == strcmp(key, "seek")) {
3761*44704f69SBart Van Assche             n = strlen(buf);
3762*44704f69SBart Van Assche             if (n < 1) {
3763*44704f69SBart Van Assche                 pr2serr("%sneed argument to 'seek='\n", my_name);
3764*44704f69SBart Van Assche                 goto syn_err;
3765*44704f69SBart Van Assche             }
3766*44704f69SBart Van Assche             seek_buf = (char *)calloc(n + 16, 1);
3767*44704f69SBart Van Assche             if (NULL == seek_buf)
3768*44704f69SBart Van Assche                 goto syn_err;
3769*44704f69SBart Van Assche             memcpy(seek_buf, buf, n + 1);
3770*44704f69SBart Van Assche         } else if (0 == strcmp(key, "skip")) {
3771*44704f69SBart Van Assche             n = strlen(buf);
3772*44704f69SBart Van Assche             if (n < 1) {
3773*44704f69SBart Van Assche                 pr2serr("%sneed argument to 'skip='\n", my_name);
3774*44704f69SBart Van Assche                 goto syn_err;
3775*44704f69SBart Van Assche             }
3776*44704f69SBart Van Assche             skip_buf = (char *)calloc(n + 16, 1);
3777*44704f69SBart Van Assche             if (NULL == skip_buf)
3778*44704f69SBart Van Assche                 goto syn_err;
3779*44704f69SBart Van Assche             memcpy(skip_buf, buf, n + 1);
3780*44704f69SBart Van Assche         } else if (0 == strcmp(key, "sync"))
3781*44704f69SBart Van Assche             do_sync = !! sg_get_num(buf);
3782*44704f69SBart Van Assche         else if (0 == strcmp(key, "thr")) {
3783*44704f69SBart Van Assche             num_threads = sg_get_num(buf);
3784*44704f69SBart Van Assche             if ((num_threads < 0) || (num_threads > MAX_BPT_VALUE)) {
3785*44704f69SBart Van Assche                 pr2serr("%sneed argument to 'skip='\n", my_name);
3786*44704f69SBart Van Assche                 goto syn_err;
3787*44704f69SBart Van Assche             }
3788*44704f69SBart Van Assche         } else if (0 == strcmp(key, "time")) {
3789*44704f69SBart Van Assche             ccp = strchr(buf, ',');
3790*44704f69SBart Van Assche             do_time = sg_get_num(buf);
3791*44704f69SBart Van Assche             if (do_time < 0) {
3792*44704f69SBart Van Assche                 pr2serr("%sbad argument to 'time=0|1|2'\n", my_name);
3793*44704f69SBart Van Assche                 goto syn_err;
3794*44704f69SBart Van Assche             }
3795*44704f69SBart Van Assche             if (ccp) {
3796*44704f69SBart Van Assche                 n = sg_get_num(ccp + 1);
3797*44704f69SBart Van Assche                 if ((n < 0) || (n > (MAX_BPT_VALUE / 1000))) {
3798*44704f69SBart Van Assche                     pr2serr("%sbad argument to 'time=0|1|2,TO'\n", my_name);
3799*44704f69SBart Van Assche                     goto syn_err;
3800*44704f69SBart Van Assche                 }
3801*44704f69SBart Van Assche                 clp->cmd_timeout = n ? (n * 1000) : DEF_TIMEOUT;
3802*44704f69SBart Van Assche             }
3803*44704f69SBart Van Assche         } else if (0 == strncmp(key, "verb", 4))
3804*44704f69SBart Van Assche             clp->verbose = sg_get_num(buf);
3805*44704f69SBart Van Assche         else if ((keylen > 1) && ('-' == key[0]) && ('-' != key[1])) {
3806*44704f69SBart Van Assche             res = 0;
3807*44704f69SBart Van Assche             n = num_chs_in_str(key + 1, keylen - 1, 'd');
3808*44704f69SBart Van Assche             clp->dry_run += n;
3809*44704f69SBart Van Assche             res += n;
3810*44704f69SBart Van Assche             n = num_chs_in_str(key + 1, keylen - 1, 'h');
3811*44704f69SBart Van Assche             clp->help += n;
3812*44704f69SBart Van Assche             res += n;
3813*44704f69SBart Van Assche             n = num_chs_in_str(key + 1, keylen - 1, 'p');
3814*44704f69SBart Van Assche             if (n > 0)
3815*44704f69SBart Van Assche                 clp->prefetch = true;
3816*44704f69SBart Van Assche             res += n;
3817*44704f69SBart Van Assche             n = num_chs_in_str(key + 1, keylen - 1, 'v');
3818*44704f69SBart Van Assche             if (n > 0)
3819*44704f69SBart Van Assche                 verbose_given = true;
3820*44704f69SBart Van Assche             clp->verbose += n;   /* -v  ---> --verbose */
3821*44704f69SBart Van Assche             res += n;
3822*44704f69SBart Van Assche             n = num_chs_in_str(key + 1, keylen - 1, 'V');
3823*44704f69SBart Van Assche             if (n > 0)
3824*44704f69SBart Van Assche                 version_given = true;
3825*44704f69SBart Van Assche             res += n;
3826*44704f69SBart Van Assche             n = num_chs_in_str(key + 1, keylen - 1, 'x');
3827*44704f69SBart Van Assche             if (n > 0)
3828*44704f69SBart Van Assche                 verify_given = true;
3829*44704f69SBart Van Assche             res += n;
3830*44704f69SBart Van Assche 
3831*44704f69SBart Van Assche             if (res < (keylen - 1)) {
3832*44704f69SBart Van Assche                 pr2serr("Unrecognised short option in '%s', try '--help'\n",
3833*44704f69SBart Van Assche                         key);
3834*44704f69SBart Van Assche                 goto syn_err;
3835*44704f69SBart Van Assche             }
3836*44704f69SBart Van Assche         } else if ((0 == strncmp(key, "--dry-run", 9)) ||
3837*44704f69SBart Van Assche                    (0 == strncmp(key, "--dry_run", 9)))
3838*44704f69SBart Van Assche             ++clp->dry_run;
3839*44704f69SBart Van Assche         else if ((0 == strncmp(key, "--help", 6)) ||
3840*44704f69SBart Van Assche                    (0 == strcmp(key, "-?")))
3841*44704f69SBart Van Assche             ++clp->help;
3842*44704f69SBart Van Assche         else if ((0 == strncmp(key, "--prefetch", 10)) ||
3843*44704f69SBart Van Assche                  (0 == strncmp(key, "--pre-fetch", 11)))
3844*44704f69SBart Van Assche             clp->prefetch = true;
3845*44704f69SBart Van Assche         else if (0 == strncmp(key, "--verb", 6)) {
3846*44704f69SBart Van Assche             verbose_given = true;
3847*44704f69SBart Van Assche             ++clp->verbose;      /* --verbose */
3848*44704f69SBart Van Assche         } else if (0 == strncmp(key, "--veri", 6))
3849*44704f69SBart Van Assche             verify_given = true;
3850*44704f69SBart Van Assche         else if (0 == strncmp(key, "--vers", 6))
3851*44704f69SBart Van Assche             version_given = true;
3852*44704f69SBart Van Assche         else {
3853*44704f69SBart Van Assche             pr2serr("Unrecognized option '%s'\n", key);
3854*44704f69SBart Van Assche             pr2serr("For more information use '--help'\n");
3855*44704f69SBart Van Assche             goto syn_err;
3856*44704f69SBart Van Assche         }
3857*44704f69SBart Van Assche     }   /* end of parsing for loop */
3858*44704f69SBart Van Assche 
3859*44704f69SBart Van Assche     if (skip_buf) {
3860*44704f69SBart Van Assche         res = skip_seek(clp, "skip", skip_buf, true /* skip */, false);
3861*44704f69SBart Van Assche         free(skip_buf);
3862*44704f69SBart Van Assche         skip_buf = NULL;
3863*44704f69SBart Van Assche         if (res) {
3864*44704f69SBart Van Assche             pr2serr("%sbad argument to 'seek='\n", my_name);
3865*44704f69SBart Van Assche             goto syn_err;
3866*44704f69SBart Van Assche         }
3867*44704f69SBart Van Assche     }
3868*44704f69SBart Van Assche     if (seek_buf) {
3869*44704f69SBart Van Assche         res = skip_seek(clp, "seek", seek_buf, false /* skip */, false);
3870*44704f69SBart Van Assche         free(seek_buf);
3871*44704f69SBart Van Assche         seek_buf = NULL;
3872*44704f69SBart Van Assche         if (res) {
3873*44704f69SBart Van Assche             pr2serr("%sbad argument to 'seek='\n", my_name);
3874*44704f69SBart Van Assche             goto syn_err;
3875*44704f69SBart Van Assche         }
3876*44704f69SBart Van Assche     }
3877*44704f69SBart Van Assche     /* heap usage should be all freed up now */
3878*44704f69SBart Van Assche 
3879*44704f69SBart Van Assche #ifdef DEBUG
3880*44704f69SBart Van Assche     pr2serr("In DEBUG mode, ");
3881*44704f69SBart Van Assche     if (verbose_given && version_given) {
3882*44704f69SBart Van Assche         pr2serr("but override: '-vV' given, zero verbose and continue\n");
3883*44704f69SBart Van Assche         verbose_given = false;
3884*44704f69SBart Van Assche         version_given = false;
3885*44704f69SBart Van Assche         clp->verbose = 0;
3886*44704f69SBart Van Assche     } else if (! verbose_given) {
3887*44704f69SBart Van Assche         pr2serr("set '-vv'\n");
3888*44704f69SBart Van Assche         clp->verbose = 2;
3889*44704f69SBart Van Assche     } else
3890*44704f69SBart Van Assche         pr2serr("keep verbose=%d\n", clp->verbose);
3891*44704f69SBart Van Assche #else
3892*44704f69SBart Van Assche     if (verbose_given && version_given)
3893*44704f69SBart Van Assche         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
3894*44704f69SBart Van Assche #endif
3895*44704f69SBart Van Assche     if (version_given) {
3896*44704f69SBart Van Assche         pr2serr("%s%s\n", my_name, version_str);
3897*44704f69SBart Van Assche         ret = SG_LIB_OK_FALSE;
3898*44704f69SBart Van Assche         goto oth_err;
3899*44704f69SBart Van Assche     }
3900*44704f69SBart Van Assche     if (clp->help > 0) {
3901*44704f69SBart Van Assche         usage(clp->help);
3902*44704f69SBart Van Assche         ret = SG_LIB_OK_FALSE;
3903*44704f69SBart Van Assche         goto oth_err;
3904*44704f69SBart Van Assche     }
3905*44704f69SBart Van Assche     if (clp->bs <= 0) {
3906*44704f69SBart Van Assche         clp->bs = DEF_BLOCK_SIZE;
3907*44704f69SBart Van Assche         pr2serr("Assume default 'bs' ((logical) block size) of %d bytes\n",
3908*44704f69SBart Van Assche                 clp->bs);
3909*44704f69SBart Van Assche     }
3910*44704f69SBart Van Assche     if (verify_given) {
3911*44704f69SBart Van Assche         pr2serr("Doing verify/cmp rather than copy\n");
3912*44704f69SBart Van Assche         clp->verify = true;
3913*44704f69SBart Van Assche     }
3914*44704f69SBart Van Assche     if ((ibs && (ibs != clp->bs)) || (obs && (obs != clp->bs))) {
3915*44704f69SBart Van Assche         pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n");
3916*44704f69SBart Van Assche         usage(0);
3917*44704f69SBart Van Assche         goto syn_err;
3918*44704f69SBart Van Assche     }
3919*44704f69SBart Van Assche     if (clp->out_flags.append) {
3920*44704f69SBart Van Assche         if ((clp->o_sgl.lowest_lba > 0) ||
3921*44704f69SBart Van Assche             (clp->o_sgl.linearity != SGL_LINEAR)) {
3922*44704f69SBart Van Assche             pr2serr("Can't use both append and seek switches\n");
3923*44704f69SBart Van Assche             goto syn_err;
3924*44704f69SBart Van Assche         }
3925*44704f69SBart Van Assche         if (verify_given) {
3926*44704f69SBart Van Assche             pr2serr("Can't use both append and verify switches\n");
3927*44704f69SBart Van Assche             goto syn_err;
3928*44704f69SBart Van Assche         }
3929*44704f69SBart Van Assche     }
3930*44704f69SBart Van Assche     if (clp->bpt < 1) {
3931*44704f69SBart Van Assche         pr2serr("bpt must be greater than 0\n");
3932*44704f69SBart Van Assche         goto syn_err;
3933*44704f69SBart Van Assche     }
3934*44704f69SBart Van Assche     if (clp->in_flags.mmap && clp->out_flags.mmap) {
3935*44704f69SBart Van Assche         pr2serr("mmap flag on both IFILE and OFILE doesn't work\n");
3936*44704f69SBart Van Assche         goto syn_err;
3937*44704f69SBart Van Assche     }
3938*44704f69SBart Van Assche     /* defaulting transfer size to 128*2048 for CD/DVDs is too large
3939*44704f69SBart Van Assche      * for the block layer in lk 2.6 and results in an EIO on the
3940*44704f69SBart Van Assche      * SG_IO ioctl. So reduce it in that case. */
3941*44704f69SBart Van Assche     if ((clp->bs >= 2048) && (! bpt_given))
3942*44704f69SBart Van Assche         clp->bpt = DEF_BLOCKS_PER_2048TRANSFER;
3943*44704f69SBart Van Assche     if (clp->in_flags.order_wr && (! clp->out_flags.order_wr))
3944*44704f69SBart Van Assche         pr2serr("Warning iflag=order is ignored, use with oflag=\n");
3945*44704f69SBart Van Assche     if ((num_threads < 1) || (num_threads > MAX_NUM_THREADS)) {
3946*44704f69SBart Van Assche         pr2serr("too few or too many threads requested\n");
3947*44704f69SBart Van Assche         usage(1);
3948*44704f69SBart Van Assche         goto syn_err;
3949*44704f69SBart Van Assche     }
3950*44704f69SBart Van Assche     clp->unit_nanosec = (do_time > 1) || !!getenv("SG3_UTILS_LINUX_NANO");
3951*44704f69SBart Van Assche     return 0;
3952*44704f69SBart Van Assche 
3953*44704f69SBart Van Assche syn_err:
3954*44704f69SBart Van Assche     if (seek_buf)
3955*44704f69SBart Van Assche         free(seek_buf);
3956*44704f69SBart Van Assche     if (skip_buf)
3957*44704f69SBart Van Assche         free(skip_buf);
3958*44704f69SBart Van Assche     return contra ? SG_LIB_CONTRADICT : SG_LIB_SYNTAX_ERROR;
3959*44704f69SBart Van Assche oth_err:
3960*44704f69SBart Van Assche     if (seek_buf)
3961*44704f69SBart Van Assche         free(seek_buf);
3962*44704f69SBart Van Assche     if (skip_buf)
3963*44704f69SBart Van Assche         free(skip_buf);
3964*44704f69SBart Van Assche     return ret;
3965*44704f69SBart Van Assche }
3966*44704f69SBart Van Assche 
3967*44704f69SBart Van Assche static int
calc_count(struct global_collection * clp,const char * inf,int64_t & in_num_sect,const char * outf,int64_t & out_num_sect)3968*44704f69SBart Van Assche calc_count(struct global_collection * clp, const char * inf,
3969*44704f69SBart Van Assche            int64_t & in_num_sect, const char * outf, int64_t & out_num_sect)
3970*44704f69SBart Van Assche {
3971*44704f69SBart Van Assche     int in_sect_sz, out_sect_sz, res;
3972*44704f69SBart Van Assche 
3973*44704f69SBart Van Assche     if (clp->dd_count < 0) {
3974*44704f69SBart Van Assche         in_num_sect = -1;
3975*44704f69SBart Van Assche         out_num_sect = -1;
3976*44704f69SBart Van Assche     }
3977*44704f69SBart Van Assche     if (FT_SG == clp->in_type) {
3978*44704f69SBart Van Assche         res = scsi_read_capacity(clp->in0fd, &in_num_sect, &in_sect_sz);
3979*44704f69SBart Van Assche         if (2 == res) {
3980*44704f69SBart Van Assche             pr2serr("Unit attention, media changed(in), continuing\n");
3981*44704f69SBart Van Assche             res = scsi_read_capacity(clp->in0fd, &in_num_sect,
3982*44704f69SBart Van Assche                                      &in_sect_sz);
3983*44704f69SBart Van Assche         }
3984*44704f69SBart Van Assche         if (0 != res) {
3985*44704f69SBart Van Assche             if (res == SG_LIB_CAT_INVALID_OP)
3986*44704f69SBart Van Assche                 pr2serr("read capacity not supported on %s\n", inf);
3987*44704f69SBart Van Assche             else if (res == SG_LIB_CAT_NOT_READY)
3988*44704f69SBart Van Assche                 pr2serr("read capacity failed, %s not ready\n", inf);
3989*44704f69SBart Van Assche             else
3990*44704f69SBart Van Assche                 pr2serr("Unable to read capacity on %s\n", inf);
3991*44704f69SBart Van Assche             return SG_LIB_FILE_ERROR;
3992*44704f69SBart Van Assche         } else if (clp->bs != in_sect_sz) {
3993*44704f69SBart Van Assche             pr2serr(">> warning: logical block size on %s confusion: "
3994*44704f69SBart Van Assche                     "bs=%d, device claims=%d\n", clp->infp, clp->bs,
3995*44704f69SBart Van Assche                     in_sect_sz);
3996*44704f69SBart Van Assche             return SG_LIB_FILE_ERROR;
3997*44704f69SBart Van Assche         }
3998*44704f69SBart Van Assche     }
3999*44704f69SBart Van Assche     if (FT_SG == clp->out_type) {
4000*44704f69SBart Van Assche         res = scsi_read_capacity(clp->out0fd, &out_num_sect, &out_sect_sz);
4001*44704f69SBart Van Assche         if (2 == res) {
4002*44704f69SBart Van Assche             pr2serr("Unit attention, media changed(out), continuing\n");
4003*44704f69SBart Van Assche             res = scsi_read_capacity(clp->out0fd, &out_num_sect,
4004*44704f69SBart Van Assche                                      &out_sect_sz);
4005*44704f69SBart Van Assche         }
4006*44704f69SBart Van Assche         if (0 != res) {
4007*44704f69SBart Van Assche             if (res == SG_LIB_CAT_INVALID_OP)
4008*44704f69SBart Van Assche                 pr2serr("read capacity not supported on %s\n", outf);
4009*44704f69SBart Van Assche             else if (res == SG_LIB_CAT_NOT_READY)
4010*44704f69SBart Van Assche                 pr2serr("read capacity failed, %s not ready\n", outf);
4011*44704f69SBart Van Assche             else
4012*44704f69SBart Van Assche                 pr2serr("Unable to read capacity on %s\n", outf);
4013*44704f69SBart Van Assche             out_num_sect = -1;
4014*44704f69SBart Van Assche             return SG_LIB_FILE_ERROR;
4015*44704f69SBart Van Assche         } else if (clp->bs != out_sect_sz) {
4016*44704f69SBart Van Assche             pr2serr(">> warning: logical block size on %s confusion: "
4017*44704f69SBart Van Assche                     "bs=%d, device claims=%d\n", clp->outfp, clp->bs,
4018*44704f69SBart Van Assche                     out_sect_sz);
4019*44704f69SBart Van Assche             return SG_LIB_FILE_ERROR;
4020*44704f69SBart Van Assche         }
4021*44704f69SBart Van Assche     }
4022*44704f69SBart Van Assche 
4023*44704f69SBart Van Assche     if (clp->dd_count < 0) {
4024*44704f69SBart Van Assche         if (FT_SG == clp->in_type)
4025*44704f69SBart Van Assche             ;
4026*44704f69SBart Van Assche         else if (FT_BLOCK == clp->in_type) {
4027*44704f69SBart Van Assche             if (0 != read_blkdev_capacity(clp->in0fd, &in_num_sect,
4028*44704f69SBart Van Assche                                           &in_sect_sz)) {
4029*44704f69SBart Van Assche                 pr2serr("Unable to read block capacity on %s\n", inf);
4030*44704f69SBart Van Assche                 in_num_sect = -1;
4031*44704f69SBart Van Assche             }
4032*44704f69SBart Van Assche             if (clp->bs != in_sect_sz) {
4033*44704f69SBart Van Assche                 pr2serr("logical block size on %s confusion; bs=%d, from "
4034*44704f69SBart Van Assche                         "device=%d\n", inf, clp->bs, in_sect_sz);
4035*44704f69SBart Van Assche                 in_num_sect = -1;
4036*44704f69SBart Van Assche             }
4037*44704f69SBart Van Assche         }
4038*44704f69SBart Van Assche 
4039*44704f69SBart Van Assche         if (FT_SG == clp->out_type)
4040*44704f69SBart Van Assche             ;
4041*44704f69SBart Van Assche         else if (FT_BLOCK == clp->out_type) {
4042*44704f69SBart Van Assche             if (0 != read_blkdev_capacity(clp->out0fd, &out_num_sect,
4043*44704f69SBart Van Assche                                           &out_sect_sz)) {
4044*44704f69SBart Van Assche                 pr2serr("Unable to read block capacity on %s\n", outf);
4045*44704f69SBart Van Assche                 out_num_sect = -1;
4046*44704f69SBart Van Assche             }
4047*44704f69SBart Van Assche             if (clp->bs != out_sect_sz) {
4048*44704f69SBart Van Assche                 pr2serr("logical block size on %s confusion: bs=%d, from "
4049*44704f69SBart Van Assche                         "device=%d\n", outf, clp->bs, out_sect_sz);
4050*44704f69SBart Van Assche                 out_num_sect = -1;
4051*44704f69SBart Van Assche             }
4052*44704f69SBart Van Assche         }
4053*44704f69SBart Van Assche     }
4054*44704f69SBart Van Assche     return 0;
4055*44704f69SBart Van Assche }
4056*44704f69SBart Van Assche 
4057*44704f69SBart Van Assche static int
do_count_work(struct global_collection * clp,const char * inf,int64_t & in_num_sect,const char * outf,int64_t & out_num_sect)4058*44704f69SBart Van Assche do_count_work(struct global_collection * clp, const char * inf,
4059*44704f69SBart Van Assche               int64_t & in_num_sect, const char * outf,
4060*44704f69SBart Van Assche               int64_t & out_num_sect)
4061*44704f69SBart Van Assche {
4062*44704f69SBart Van Assche     int res;
4063*44704f69SBart Van Assche     class scat_gath_list * isglp = &clp->i_sgl;
4064*44704f69SBart Van Assche     class scat_gath_list * osglp = &clp->o_sgl;
4065*44704f69SBart Van Assche 
4066*44704f69SBart Van Assche     res = calc_count(clp, inf, in_num_sect, outf, out_num_sect);
4067*44704f69SBart Van Assche     if (res)
4068*44704f69SBart Van Assche         return res;
4069*44704f69SBart Van Assche 
4070*44704f69SBart Van Assche     if ((-1 == in_num_sect) && (FT_OTHER == clp->in_type)) {
4071*44704f69SBart Van Assche         in_num_sect = clp->in_st_size / clp->bs;
4072*44704f69SBart Van Assche         if (clp->in_st_size % clp->bs) {
4073*44704f69SBart Van Assche             ++in_num_sect;
4074*44704f69SBart Van Assche             pr2serr("Warning: the file size of %s is not a multiple of BS "
4075*44704f69SBart Van Assche                     "[%d]\n", inf, clp->bs);
4076*44704f69SBart Van Assche         }
4077*44704f69SBart Van Assche     }
4078*44704f69SBart Van Assche     if ((in_num_sect > 0) && (isglp->high_lba_p1 > in_num_sect)) {
4079*44704f69SBart Van Assche         pr2serr("%shighest LBA [0x%" PRIx64 "] exceeds input length: %"
4080*44704f69SBart Van Assche                 PRIx64 " blocks\n", my_name, isglp->high_lba_p1 - 1,
4081*44704f69SBart Van Assche                 in_num_sect);
4082*44704f69SBart Van Assche         return SG_LIB_CAT_OTHER;
4083*44704f69SBart Van Assche     }
4084*44704f69SBart Van Assche     if ((out_num_sect > 0) && (osglp->high_lba_p1 > out_num_sect)) {
4085*44704f69SBart Van Assche         pr2serr("%shighest LBA [0x%" PRIx64 "] exceeds output length: %"
4086*44704f69SBart Van Assche                 PRIx64 " blocks\n", my_name, osglp->high_lba_p1 - 1,
4087*44704f69SBart Van Assche                 out_num_sect);
4088*44704f69SBart Van Assche         return SG_LIB_CAT_OTHER;
4089*44704f69SBart Van Assche     }
4090*44704f69SBart Van Assche 
4091*44704f69SBart Van Assche     if (isglp->sum_hard || osglp->sum_hard) {
4092*44704f69SBart Van Assche         int64_t ccount;
4093*44704f69SBart Van Assche 
4094*44704f69SBart Van Assche         if (isglp->sum_hard && osglp->sum_hard) {
4095*44704f69SBart Van Assche             if (isglp->sum != osglp->sum) {
4096*44704f69SBart Van Assche                 pr2serr("%stwo hard sgl_s, sum of blocks differ: in=%" PRId64
4097*44704f69SBart Van Assche                         ", out=%" PRId64 "\n", my_name , isglp->sum,
4098*44704f69SBart Van Assche                         osglp->sum);
4099*44704f69SBart Van Assche                 return SG_LIB_CAT_OTHER;
4100*44704f69SBart Van Assche             }
4101*44704f69SBart Van Assche             ccount = isglp->sum;
4102*44704f69SBart Van Assche         } else if (isglp->sum_hard) {
4103*44704f69SBart Van Assche             if (osglp->sum > isglp->sum) {
4104*44704f69SBart Van Assche                 pr2serr("%soutput sgl already too many blocks [%" PRId64
4105*44704f69SBart Van Assche                         "]\n", my_name, osglp->sum);
4106*44704f69SBart Van Assche                 return SG_LIB_CAT_OTHER;
4107*44704f69SBart Van Assche             }
4108*44704f69SBart Van Assche             if (osglp->linearity != SGL_NON_MONOTONIC)
4109*44704f69SBart Van Assche                 osglp->append_1or(isglp->sum - osglp->sum);
4110*44704f69SBart Van Assche             else {
4111*44704f69SBart Van Assche                 pr2serr("%soutput sgl non-montonic: can't extend\n",
4112*44704f69SBart Van Assche                         my_name);
4113*44704f69SBart Van Assche                 return SG_LIB_CAT_OTHER;
4114*44704f69SBart Van Assche             }
4115*44704f69SBart Van Assche             ccount = isglp->sum;
4116*44704f69SBart Van Assche         } else {        /* only osglp hard */
4117*44704f69SBart Van Assche             if (isglp->sum > osglp->sum) {
4118*44704f69SBart Van Assche                 pr2serr("%sinput sgl already too many blocks [%" PRId64
4119*44704f69SBart Van Assche                         "]\n", my_name, isglp->sum);
4120*44704f69SBart Van Assche                 return SG_LIB_CAT_OTHER;
4121*44704f69SBart Van Assche             }
4122*44704f69SBart Van Assche             if (isglp->linearity != SGL_NON_MONOTONIC)
4123*44704f69SBart Van Assche                 isglp->append_1or(osglp->sum - isglp->sum);
4124*44704f69SBart Van Assche             else {
4125*44704f69SBart Van Assche                 pr2serr("%sinput sgl non-monotonic: can't extend\n",
4126*44704f69SBart Van Assche                         my_name);
4127*44704f69SBart Van Assche                 return SG_LIB_CAT_OTHER;
4128*44704f69SBart Van Assche             }
4129*44704f69SBart Van Assche             ccount = osglp->sum;
4130*44704f69SBart Van Assche         }
4131*44704f69SBart Van Assche         if (SG_COUNT_INDEFINITE == clp->dd_count)
4132*44704f69SBart Van Assche             clp->dd_count = ccount;
4133*44704f69SBart Van Assche         else if (ccount != clp->dd_count) {
4134*44704f69SBart Van Assche             pr2serr("%scount=COUNT disagrees with scatter gather list "
4135*44704f69SBart Van Assche                     "length [%" PRId64 "]\n", my_name, ccount);
4136*44704f69SBart Van Assche             return SG_LIB_CAT_OTHER;
4137*44704f69SBart Van Assche         }
4138*44704f69SBart Van Assche     } else if (clp->dd_count != 0) { /* and both input and output are soft */
4139*44704f69SBart Van Assche         int64_t iposs = INT64_MAX;
4140*44704f69SBart Van Assche         int64_t oposs = INT64_MAX;
4141*44704f69SBart Van Assche 
4142*44704f69SBart Van Assche         if (clp->dd_count > 0) {
4143*44704f69SBart Van Assche             if (isglp->sum > clp->dd_count) {
4144*44704f69SBart Van Assche                 pr2serr("%sskip sgl sum [%" PRId64 "] exceeds COUNT\n",
4145*44704f69SBart Van Assche                         my_name, isglp->sum);
4146*44704f69SBart Van Assche                 return SG_LIB_CAT_OTHER;
4147*44704f69SBart Van Assche             }
4148*44704f69SBart Van Assche             if (osglp->sum > clp->dd_count) {
4149*44704f69SBart Van Assche                 pr2serr("%sseek sgl sum [%" PRId64 "] exceeds COUNT\n",
4150*44704f69SBart Van Assche                         my_name, osglp->sum);
4151*44704f69SBart Van Assche                 return SG_LIB_CAT_OTHER;
4152*44704f69SBart Van Assche             }
4153*44704f69SBart Van Assche             goto fini;
4154*44704f69SBart Van Assche         }
4155*44704f69SBart Van Assche 
4156*44704f69SBart Van Assche         /* clp->dd_count == SG_COUNT_INDEFINITE */
4157*44704f69SBart Van Assche         if (in_num_sect > 0)
4158*44704f69SBart Van Assche             iposs = in_num_sect + isglp->sum - isglp->high_lba_p1;
4159*44704f69SBart Van Assche         if (out_num_sect > 0)
4160*44704f69SBart Van Assche             oposs = out_num_sect + osglp->sum - osglp->high_lba_p1;
4161*44704f69SBart Van Assche         clp->dd_count = iposs < oposs ? iposs : oposs;
4162*44704f69SBart Van Assche         if (INT64_MAX == clp->dd_count) {
4163*44704f69SBart Van Assche             pr2serr("%scan't deduce count=COUNT, please supply one\n",
4164*44704f69SBart Van Assche                     my_name);
4165*44704f69SBart Van Assche             return SG_LIB_CAT_OTHER;
4166*44704f69SBart Van Assche         }
4167*44704f69SBart Van Assche         if (isglp->sum > clp->dd_count) {
4168*44704f69SBart Van Assche             pr2serr("%sdeduced COUNT [%" PRId64 "] exceeds skip sgl sum\n",
4169*44704f69SBart Van Assche                     my_name, clp->dd_count);
4170*44704f69SBart Van Assche             return SG_LIB_CAT_OTHER;
4171*44704f69SBart Van Assche         }
4172*44704f69SBart Van Assche         if (osglp->sum > clp->dd_count) {
4173*44704f69SBart Van Assche             pr2serr("%sdeduced COUNT [%" PRId64 "] exceeds seek sgl sum\n",
4174*44704f69SBart Van Assche                     my_name, clp->dd_count);
4175*44704f69SBart Van Assche             return SG_LIB_CAT_OTHER;
4176*44704f69SBart Van Assche         }
4177*44704f69SBart Van Assche     }
4178*44704f69SBart Van Assche     if (clp->dd_count == 0)
4179*44704f69SBart Van Assche         return 0;
4180*44704f69SBart Van Assche fini:
4181*44704f69SBart Van Assche     if (clp->dd_count > isglp->sum)
4182*44704f69SBart Van Assche         isglp->append_1or(clp->dd_count - isglp->sum);
4183*44704f69SBart Van Assche     if (clp->dd_count > osglp->sum)
4184*44704f69SBart Van Assche         osglp->append_1or(clp->dd_count - osglp->sum);
4185*44704f69SBart Van Assche     return 0;
4186*44704f69SBart Van Assche }
4187*44704f69SBart Van Assche 
4188*44704f69SBart Van Assche 
4189*44704f69SBart Van Assche int
main(int argc,char * argv[])4190*44704f69SBart Van Assche main(int argc, char * argv[])
4191*44704f69SBart Van Assche {
4192*44704f69SBart Van Assche     bool fail_after_cli = false;
4193*44704f69SBart Van Assche     bool ifile_given = true;
4194*44704f69SBart Van Assche     // char inf[INOUTF_SZ];
4195*44704f69SBart Van Assche     // char outf[INOUTF_SZ];
4196*44704f69SBart Van Assche     char outregf[INOUTF_SZ];
4197*44704f69SBart Van Assche     int res, k, err;
4198*44704f69SBart Van Assche     size_t num_ifiles, num_ofiles, num_slices, inf0_sz;
4199*44704f69SBart Van Assche     int64_t in_num_sect = -1;
4200*44704f69SBart Van Assche     int64_t out_num_sect = -1;
4201*44704f69SBart Van Assche     const char * ccp = NULL;
4202*44704f69SBart Van Assche     const char * cc2p;
4203*44704f69SBart Van Assche     struct global_collection * clp = &gcoll;
4204*44704f69SBart Van Assche     thread sig_listen_thr;
4205*44704f69SBart Van Assche     vector<thread> work_thr_v;
4206*44704f69SBart Van Assche     vector<thread> listen_thr_v;
4207*44704f69SBart Van Assche     char ebuff[EBUFF_SZ];
4208*44704f69SBart Van Assche #if 0   /* SG_LIB_ANDROID */
4209*44704f69SBart Van Assche     struct sigaction actions;
4210*44704f69SBart Van Assche 
4211*44704f69SBart Van Assche     memset(&actions, 0, sizeof(actions));
4212*44704f69SBart Van Assche     sigemptyset(&actions.sa_mask);
4213*44704f69SBart Van Assche     actions.sa_flags = 0;
4214*44704f69SBart Van Assche     actions.sa_handler = thread_exit_handler;
4215*44704f69SBart Van Assche     sigaction(SIGUSR1, &actions, NULL);
4216*44704f69SBart Van Assche     sigaction(SIGUSR2, &actions, NULL);
4217*44704f69SBart Van Assche #endif
4218*44704f69SBart Van Assche     /* memset(clp, 0, sizeof(*clp)); */
4219*44704f69SBart Van Assche     clp->dd_count = SG_COUNT_INDEFINITE;
4220*44704f69SBart Van Assche     clp->bpt = DEF_BLOCKS_PER_TRANSFER;
4221*44704f69SBart Van Assche     clp->cmd_timeout = DEF_TIMEOUT;
4222*44704f69SBart Van Assche     clp->sdt_ict = DEF_SDT_ICT_MS;
4223*44704f69SBart Van Assche     clp->sdt_crt = DEF_SDT_CRT_SEC;
4224*44704f69SBart Van Assche     clp->in_type = FT_FIFO;
4225*44704f69SBart Van Assche     /* change dd's default: if of=OFILE not given, assume /dev/null */
4226*44704f69SBart Van Assche     clp->out_type = FT_DEV_NULL;
4227*44704f69SBart Van Assche     clp->cdbsz_in = DEF_SCSI_CDB_SZ;
4228*44704f69SBart Van Assche     clp->cdbsz_out = DEF_SCSI_CDB_SZ;
4229*44704f69SBart Van Assche     clp->mrq_num = DEF_MRQ_NUM;
4230*44704f69SBart Van Assche     // inf[0] = '\0';
4231*44704f69SBart Van Assche     // outf[0] = '\0';
4232*44704f69SBart Van Assche     outregf[0] = '\0';
4233*44704f69SBart Van Assche     fetch_sg_version();
4234*44704f69SBart Van Assche     if (sg_version >= 40045)
4235*44704f69SBart Van Assche         sg_version_ge_40045 = true;
4236*44704f69SBart Van Assche     else {
4237*44704f69SBart Van Assche         pr2serr(">>> %srequires an sg driver version of 4.0.45 or later\n\n",
4238*44704f69SBart Van Assche                 my_name);
4239*44704f69SBart Van Assche         fail_after_cli = true;
4240*44704f69SBart Van Assche     }
4241*44704f69SBart Van Assche 
4242*44704f69SBart Van Assche     res = parse_cmdline_sanity(argc, argv, clp, outregf);
4243*44704f69SBart Van Assche     if (SG_LIB_OK_FALSE == res)
4244*44704f69SBart Van Assche         return 0;
4245*44704f69SBart Van Assche     if (res)
4246*44704f69SBart Van Assche         return res;
4247*44704f69SBart Van Assche     if (fail_after_cli) {
4248*44704f69SBart Van Assche         pr2serr("%scommand line parsing was okay but sg driver is too old\n",
4249*44704f69SBart Van Assche                 my_name);
4250*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
4251*44704f69SBart Van Assche     }
4252*44704f69SBart Van Assche 
4253*44704f69SBart Van Assche     install_handler(SIGINT, interrupt_handler);
4254*44704f69SBart Van Assche     install_handler(SIGQUIT, interrupt_handler);
4255*44704f69SBart Van Assche     install_handler(SIGPIPE, interrupt_handler);
4256*44704f69SBart Van Assche     install_handler(SIGUSR1, siginfo_handler);
4257*44704f69SBart Van Assche     install_handler(SIGUSR2, siginfo2_handler);
4258*44704f69SBart Van Assche 
4259*44704f69SBart Van Assche     num_ifiles = clp->inf_v.size();
4260*44704f69SBart Van Assche     num_ofiles = clp->outf_v.size();
4261*44704f69SBart Van Assche     if (num_ifiles > MAX_SLICES) {
4262*44704f69SBart Van Assche         pr2serr("%sonly support %d slices but given %zd IFILEs\n", my_name,
4263*44704f69SBart Van Assche                 MAX_SLICES, num_ifiles);
4264*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
4265*44704f69SBart Van Assche     }
4266*44704f69SBart Van Assche     if (num_ofiles > MAX_SLICES) {
4267*44704f69SBart Van Assche         pr2serr("%sonly support %d slices but given %zd OFILEs\n", my_name,
4268*44704f69SBart Van Assche                 MAX_SLICES, num_ifiles);
4269*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
4270*44704f69SBart Van Assche     }
4271*44704f69SBart Van Assche     if (0 == num_ofiles) {
4272*44704f69SBart Van Assche         if (0 == num_ifiles) {
4273*44704f69SBart Van Assche             pr2serr("%sexpect either if= or of= to be given\n", my_name);
4274*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
4275*44704f69SBart Van Assche         }
4276*44704f69SBart Van Assche         for (k = 0; k < (int)num_ifiles; ++k)
4277*44704f69SBart Van Assche             clp->outf_v.push_back("."); /* same as /dev/null */
4278*44704f69SBart Van Assche     }
4279*44704f69SBart Van Assche     if (0 == num_ifiles) {
4280*44704f69SBart Van Assche         ifile_given = false;
4281*44704f69SBart Van Assche         for (k = 0; k < (int)num_ofiles; ++k)
4282*44704f69SBart Van Assche             clp->inf_v.push_back("");
4283*44704f69SBart Van Assche     }
4284*44704f69SBart Van Assche     if ((num_ifiles > 1) && (num_ofiles > 1) && (num_ifiles != num_ofiles)) {
4285*44704f69SBart Van Assche         pr2serr("%snumber of IFILEs [%zd] and number of OFILEs [%zd] > 1 "
4286*44704f69SBart Van Assche                 "and unequal\n", my_name, num_ifiles, num_ofiles);
4287*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
4288*44704f69SBart Van Assche     }
4289*44704f69SBart Van Assche     if ((num_ifiles > 1) && (1 == num_ofiles)) {
4290*44704f69SBart Van Assche         /* if many IFILEs and one OFILE, replicate OFILE till same size */
4291*44704f69SBart Van Assche         for (k = 1; k < (int)num_ifiles; ++k)
4292*44704f69SBart Van Assche             clp->outf_v.push_back(clp->outf_v[0]);
4293*44704f69SBart Van Assche         num_ofiles = clp->outf_v.size();
4294*44704f69SBart Van Assche     } else if ((num_ofiles > 1) && (1 == num_ifiles)) {
4295*44704f69SBart Van Assche         /* if many OFILEs and one IFILE, replicate IFILE till same size */
4296*44704f69SBart Van Assche         for (k = 1; k < (int)num_ofiles; ++k)
4297*44704f69SBart Van Assche             clp->inf_v.push_back(clp->inf_v[0]);
4298*44704f69SBart Van Assche         num_ifiles = clp->inf_v.size();
4299*44704f69SBart Van Assche     }
4300*44704f69SBart Van Assche     num_slices = (num_ifiles > num_ofiles) ? num_ifiles : num_ofiles;
4301*44704f69SBart Van Assche     if ((int)num_slices > num_threads) {
4302*44704f69SBart Van Assche         pr2serr("%sNumber of slices [%zd] exceeds number of threads [%d].\n",
4303*44704f69SBart Van Assche                 my_name, num_slices, num_threads);
4304*44704f69SBart Van Assche         pr2serr("Number of threads needs to be increased.\n");
4305*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
4306*44704f69SBart Van Assche     }
4307*44704f69SBart Van Assche     k = 0;
4308*44704f69SBart Van Assche     for (auto && cvp : clp->cp_ver_arr) {
4309*44704f69SBart Van Assche         if (k >= (int)num_slices)
4310*44704f69SBart Van Assche             break;
4311*44704f69SBart Van Assche         cvp.my_index = k++;
4312*44704f69SBart Van Assche         cvp.state = cp_ver_pair_t::my_state::init;
4313*44704f69SBart Van Assche     }
4314*44704f69SBart Van Assche     clp->in0fd = STDIN_FILENO;
4315*44704f69SBart Van Assche     clp->out0fd = STDOUT_FILENO;
4316*44704f69SBart Van Assche     if (clp->in_flags.ff && clp->in_flags.zero) {
4317*44704f69SBart Van Assche         ccp = "<addr_as_data>";
4318*44704f69SBart Van Assche         cc2p = "addr_as_data";
4319*44704f69SBart Van Assche     } else if (clp->in_flags.ff) {
4320*44704f69SBart Van Assche         ccp = "<0xff bytes>";
4321*44704f69SBart Van Assche         cc2p = "ff";
4322*44704f69SBart Van Assche     } else if (clp->in_flags.random) {
4323*44704f69SBart Van Assche         ccp = "<random>";
4324*44704f69SBart Van Assche         cc2p = "random";
4325*44704f69SBart Van Assche     } else if (clp->in_flags.zero) {
4326*44704f69SBart Van Assche         ccp = "<zero bytes>";
4327*44704f69SBart Van Assche         cc2p = "00";
4328*44704f69SBart Van Assche     }
4329*44704f69SBart Van Assche     inf0_sz = clp->inf_v.size() ? clp->inf_v[0].size() : 0;
4330*44704f69SBart Van Assche     if (ccp) {
4331*44704f69SBart Van Assche         if (ifile_given) {
4332*44704f69SBart Van Assche             pr2serr("%siflag=%s and if=%s contradict\n", my_name, cc2p,
4333*44704f69SBart Van Assche                     clp->inf_v[0].c_str());
4334*44704f69SBart Van Assche             return SG_LIB_CONTRADICT;
4335*44704f69SBart Van Assche         }
4336*44704f69SBart Van Assche         for (auto && cvp : clp->cp_ver_arr) {
4337*44704f69SBart Van Assche             if (cvp.state == cp_ver_pair_t::my_state::empty)
4338*44704f69SBart Van Assche                 break;
4339*44704f69SBart Van Assche             cvp.in_type = FT_RANDOM_0_FF;
4340*44704f69SBart Van Assche         }
4341*44704f69SBart Van Assche         clp->in_type = FT_RANDOM_0_FF;
4342*44704f69SBart Van Assche         clp->infp = ccp;
4343*44704f69SBart Van Assche         clp->in0fd = -1;
4344*44704f69SBart Van Assche     } else if (inf0_sz && ('-' != clp->inf_v[0].c_str()[0])) {
4345*44704f69SBart Van Assche         const string & inf_s = clp->inf_v[0];
4346*44704f69SBart Van Assche         const char * infp = inf_s.c_str();
4347*44704f69SBart Van Assche 
4348*44704f69SBart Van Assche         clp->in_type = dd_filetype(infp, clp->in_st_size);
4349*44704f69SBart Van Assche         if (FT_ERROR == clp->in_type) {
4350*44704f69SBart Van Assche             pr2serr("%sunable to access %s\n", my_name, infp);
4351*44704f69SBart Van Assche             return SG_LIB_FILE_ERROR;
4352*44704f69SBart Van Assche         } else if (FT_ST == clp->in_type) {
4353*44704f69SBart Van Assche             pr2serr("%sunable to use scsi tape device %s\n", my_name, infp);
4354*44704f69SBart Van Assche             return SG_LIB_FILE_ERROR;
4355*44704f69SBart Van Assche         } else if (FT_CHAR == clp->in_type) {
4356*44704f69SBart Van Assche             pr2serr("%sunable to use unknown char device %s\n", my_name, infp);
4357*44704f69SBart Van Assche             return SG_LIB_FILE_ERROR;
4358*44704f69SBart Van Assche         } else if (FT_SG == clp->in_type) {
4359*44704f69SBart Van Assche             clp->in0fd = sg_in_open(clp, inf_s, NULL, NULL);
4360*44704f69SBart Van Assche             if (clp->in0fd < 0)
4361*44704f69SBart Van Assche                 return -clp->in0fd;
4362*44704f69SBart Van Assche         } else {
4363*44704f69SBart Van Assche             clp->in0fd = reg_file_open(clp, infp, false /* read */);
4364*44704f69SBart Van Assche             if (clp->in0fd < 0)
4365*44704f69SBart Van Assche                 return sg_convert_errno(-clp->in0fd);
4366*44704f69SBart Van Assche         }
4367*44704f69SBart Van Assche         clp->infp = infp;
4368*44704f69SBart Van Assche     }
4369*44704f69SBart Van Assche     if (clp->cdl_given && (! clp->cdbsz_given)) {
4370*44704f69SBart Van Assche         bool changed = false;
4371*44704f69SBart Van Assche 
4372*44704f69SBart Van Assche         if ((clp->cdbsz_in < 16) && (clp->in_flags.cdl > 0)) {
4373*44704f69SBart Van Assche             clp->cdbsz_in = 16;
4374*44704f69SBart Van Assche             changed = true;
4375*44704f69SBart Van Assche         }
4376*44704f69SBart Van Assche         if ((clp->cdbsz_out < 16) && (! clp->verify) &&
4377*44704f69SBart Van Assche             (clp->out_flags.cdl > 0)) {
4378*44704f69SBart Van Assche             clp->cdbsz_out = 16;
4379*44704f69SBart Van Assche             changed = true;
4380*44704f69SBart Van Assche         }
4381*44704f69SBart Van Assche         if (changed)
4382*44704f69SBart Van Assche             pr2serr(">> increasing cdbsz to 16 due to cdl > 0\n");
4383*44704f69SBart Van Assche     }
4384*44704f69SBart Van Assche     if ((clp->verbose > 0) &&
4385*44704f69SBart Van Assche         (clp->in_flags.no_waitq || clp->out_flags.no_waitq))
4386*44704f69SBart Van Assche         pr2serr("no_waitq=<n> operand is now ignored\n");
4387*44704f69SBart Van Assche     if (clp->outf_v.size()) {
4388*44704f69SBart Van Assche         const string & outf_s = clp->outf_v[0].c_str();
4389*44704f69SBart Van Assche         const char * outfp = outf_s.c_str();
4390*44704f69SBart Van Assche 
4391*44704f69SBart Van Assche         clp->ofile_given = true;
4392*44704f69SBart Van Assche         if ('-' == outfp[0])
4393*44704f69SBart Van Assche             clp->out_type = FT_FIFO;
4394*44704f69SBart Van Assche         else
4395*44704f69SBart Van Assche             clp->out_type = dd_filetype(outfp, clp->out_st_size);
4396*44704f69SBart Van Assche 
4397*44704f69SBart Van Assche         if ((FT_SG != clp->out_type) && clp->verify) {
4398*44704f69SBart Van Assche             pr2serr("%s --verify only supported by sg OFILEs\n", my_name);
4399*44704f69SBart Van Assche             return SG_LIB_FILE_ERROR;
4400*44704f69SBart Van Assche         }
4401*44704f69SBart Van Assche         if (FT_FIFO == clp->out_type)
4402*44704f69SBart Van Assche             ;
4403*44704f69SBart Van Assche         else if (FT_ST == clp->out_type) {
4404*44704f69SBart Van Assche             pr2serr("%sunable to use scsi tape device %s\n", my_name, outfp);
4405*44704f69SBart Van Assche             return SG_LIB_FILE_ERROR;
4406*44704f69SBart Van Assche         } else if (FT_CHAR == clp->out_type) {
4407*44704f69SBart Van Assche             pr2serr("%sunable to use unknown char device %s\n", my_name,
4408*44704f69SBart Van Assche                     outfp);
4409*44704f69SBart Van Assche             return SG_LIB_FILE_ERROR;
4410*44704f69SBart Van Assche         } else if (FT_SG == clp->out_type) {
4411*44704f69SBart Van Assche             clp->out0fd = sg_out_open(clp, outf_s, NULL, NULL);
4412*44704f69SBart Van Assche             if (clp->out0fd < 0)
4413*44704f69SBart Van Assche                 return -clp->out0fd;
4414*44704f69SBart Van Assche         } else if (FT_DEV_NULL == clp->out_type)
4415*44704f69SBart Van Assche             clp->out0fd = -1; /* don't bother opening */
4416*44704f69SBart Van Assche         else {
4417*44704f69SBart Van Assche             clp->out0fd = reg_file_open(clp, outfp, true /* write */);
4418*44704f69SBart Van Assche             if (clp->out0fd < 0)
4419*44704f69SBart Van Assche                 return sg_convert_errno(-clp->out0fd);
4420*44704f69SBart Van Assche         }
4421*44704f69SBart Van Assche         clp->outfp = outfp;
4422*44704f69SBart Van Assche     }
4423*44704f69SBart Van Assche     if (clp->verify && (clp->out_type == FT_DEV_NULL)) {
4424*44704f69SBart Van Assche         pr2serr("Can't do verify when OFILE not given\n");
4425*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
4426*44704f69SBart Van Assche     }
4427*44704f69SBart Van Assche 
4428*44704f69SBart Van Assche     if ((FT_SG == clp->in_type) && (FT_SG == clp->out_type)) {
4429*44704f69SBart Van Assche         if (clp->in_flags.serial || clp->out_flags.serial)
4430*44704f69SBart Van Assche             pr2serr("serial flag ignored when both IFILE and OFILE are sg "
4431*44704f69SBart Van Assche                     "devices\n");
4432*44704f69SBart Van Assche         if (clp->in_flags.order_wr && (num_threads > 1))
4433*44704f69SBart Van Assche             pr2serr("Warning: write ordering only guaranteed for single "
4434*44704f69SBart Van Assche                     "thread\n");
4435*44704f69SBart Van Assche     } else if (clp->in_flags.order_wr)
4436*44704f69SBart Van Assche         pr2serr("Warning: oflag=order only active on sg->sg copies\n");
4437*44704f69SBart Van Assche 
4438*44704f69SBart Van Assche     if (outregf[0]) {
4439*44704f69SBart Van Assche         int ftyp = dd_filetype(outregf, clp->outreg_st_size);
4440*44704f69SBart Van Assche 
4441*44704f69SBart Van Assche         clp->outreg_type = ftyp;
4442*44704f69SBart Van Assche         if (! ((FT_OTHER == ftyp) || (FT_ERROR == ftyp) ||
4443*44704f69SBart Van Assche                (FT_DEV_NULL == ftyp))) {
4444*44704f69SBart Van Assche             pr2serr("File: %s can only be regular file or pipe (or "
4445*44704f69SBart Van Assche                     "/dev/null)\n", outregf);
4446*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
4447*44704f69SBart Van Assche         }
4448*44704f69SBart Van Assche         if ((clp->outregfd = open(outregf, O_WRONLY | O_CREAT, 0666)) < 0) {
4449*44704f69SBart Van Assche             err = errno;
4450*44704f69SBart Van Assche             snprintf(ebuff, EBUFF_SZ, "could not open %s for writing",
4451*44704f69SBart Van Assche                      outregf);
4452*44704f69SBart Van Assche             perror(ebuff);
4453*44704f69SBart Van Assche             return sg_convert_errno(err);
4454*44704f69SBart Van Assche         }
4455*44704f69SBart Van Assche         if (clp->verbose > 1)
4456*44704f69SBart Van Assche             pr2serr("ofreg=%s opened okay, fd=%d\n", outregf, clp->outregfd);
4457*44704f69SBart Van Assche         if (FT_ERROR == ftyp)
4458*44704f69SBart Van Assche             clp->outreg_type = FT_OTHER;        /* regular file created */
4459*44704f69SBart Van Assche     } else
4460*44704f69SBart Van Assche         clp->outregfd = -1;
4461*44704f69SBart Van Assche 
4462*44704f69SBart Van Assche     if ((STDIN_FILENO == clp->in0fd) && (STDOUT_FILENO == clp->out0fd)) {
4463*44704f69SBart Van Assche         pr2serr("Won't default both IFILE to stdin _and_ OFILE to "
4464*44704f69SBart Van Assche                 "/dev/null\n");
4465*44704f69SBart Van Assche         pr2serr("For more information use '--help'\n");
4466*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
4467*44704f69SBart Van Assche     }
4468*44704f69SBart Van Assche     if ((clp->in_type == FT_FIFO) && (! clp->i_sgl.is_pipe_suitable())) {
4469*44704f69SBart Van Assche         pr2serr("The skip= argument is not suitable for a pipe\n");
4470*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
4471*44704f69SBart Van Assche     }
4472*44704f69SBart Van Assche     if ((clp->out_type == FT_FIFO) && (! clp->o_sgl.is_pipe_suitable())) {
4473*44704f69SBart Van Assche         pr2serr("The seek= argument is not suitable for a pipe\n");
4474*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
4475*44704f69SBart Van Assche     }
4476*44704f69SBart Van Assche     res = do_count_work(clp, clp->inf_v[0].c_str(), in_num_sect,
4477*44704f69SBart Van Assche                         clp->outf_v[0].c_str(), out_num_sect);
4478*44704f69SBart Van Assche     if (res)
4479*44704f69SBart Van Assche         return res;
4480*44704f69SBart Van Assche 
4481*44704f69SBart Van Assche     if (clp->verbose > 2)
4482*44704f69SBart Van Assche         pr2serr("Start of loop, count=%" PRId64 ", in_num_sect=%" PRId64
4483*44704f69SBart Van Assche                 ", out_num_sect=%" PRId64 "\n", clp->dd_count, in_num_sect,
4484*44704f69SBart Van Assche                 out_num_sect);
4485*44704f69SBart Van Assche     if (clp->dd_count < 0) {
4486*44704f69SBart Van Assche         pr2serr("Couldn't calculate count, please give one\n");
4487*44704f69SBart Van Assche         return SG_LIB_CAT_OTHER;
4488*44704f69SBart Van Assche     }
4489*44704f69SBart Van Assche     if (! clp->cdbsz_given) {
4490*44704f69SBart Van Assche         if ((FT_SG == clp->in_type) && (MAX_SCSI_CDB_SZ != clp->cdbsz_in) &&
4491*44704f69SBart Van Assche             ((clp->i_sgl.high_lba_p1 > UINT_MAX) || (clp->bpt > USHRT_MAX))) {
4492*44704f69SBart Van Assche             pr2serr("Note: SCSI command size increased to 16 bytes (for "
4493*44704f69SBart Van Assche                     "'if')\n");
4494*44704f69SBart Van Assche             clp->cdbsz_in = MAX_SCSI_CDB_SZ;
4495*44704f69SBart Van Assche         }
4496*44704f69SBart Van Assche         if ((FT_SG == clp->out_type) && (MAX_SCSI_CDB_SZ != clp->cdbsz_out) &&
4497*44704f69SBart Van Assche             ((clp->o_sgl.high_lba_p1 > UINT_MAX) || (clp->bpt > USHRT_MAX))) {
4498*44704f69SBart Van Assche             pr2serr("Note: SCSI command size increased to 16 bytes (for "
4499*44704f69SBart Van Assche                     "'of')\n");
4500*44704f69SBart Van Assche             clp->cdbsz_out = MAX_SCSI_CDB_SZ;
4501*44704f69SBart Van Assche         }
4502*44704f69SBart Van Assche     }
4503*44704f69SBart Van Assche 
4504*44704f69SBart Van Assche     for (auto && cvp : clp->cp_ver_arr) {
4505*44704f69SBart Van Assche         cvp.in_type = clp->in_type;
4506*44704f69SBart Van Assche         cvp.out_type = clp->out_type;
4507*44704f69SBart Van Assche         cvp.dd_count = clp->dd_count;
4508*44704f69SBart Van Assche         cvp.in_rem_count = clp->dd_count;
4509*44704f69SBart Van Assche         cvp.out_rem_count = clp->dd_count;
4510*44704f69SBart Van Assche     }
4511*44704f69SBart Van Assche 
4512*44704f69SBart Van Assche     if (clp->dry_run > 0) {
4513*44704f69SBart Van Assche         pr2serr("Due to --dry-run option, bypass copy/read\n");
4514*44704f69SBart Van Assche         goto fini;
4515*44704f69SBart Van Assche     }
4516*44704f69SBart Van Assche     if (! clp->ofile_given)
4517*44704f69SBart Van Assche         pr2serr("of=OFILE not given so only read from IFILE, to output to "
4518*44704f69SBart Van Assche                 "stdout use 'of=-'\n");
4519*44704f69SBart Van Assche 
4520*44704f69SBart Van Assche     sigemptyset(&signal_set);
4521*44704f69SBart Van Assche     sigaddset(&signal_set, SIGINT);
4522*44704f69SBart Van Assche     sigaddset(&signal_set, SIGUSR2);
4523*44704f69SBart Van Assche 
4524*44704f69SBart Van Assche     res = sigprocmask(SIG_BLOCK, &signal_set, &orig_signal_set);
4525*44704f69SBart Van Assche     if (res < 0) {
4526*44704f69SBart Van Assche         pr2serr("sigprocmask failed: %s\n", safe_strerror(errno));
4527*44704f69SBart Van Assche         goto fini;
4528*44704f69SBart Van Assche     }
4529*44704f69SBart Van Assche 
4530*44704f69SBart Van Assche     listen_thr_v.emplace_back(sig_listen_thread, clp);
4531*44704f69SBart Van Assche 
4532*44704f69SBart Van Assche     if (do_time) {
4533*44704f69SBart Van Assche         start_tm.tv_sec = 0;
4534*44704f69SBart Van Assche         start_tm.tv_usec = 0;
4535*44704f69SBart Van Assche         gettimeofday(&start_tm, NULL);
4536*44704f69SBart Van Assche     }
4537*44704f69SBart Van Assche 
4538*44704f69SBart Van Assche /* vvvvvvvvvvv  Start worker threads  vvvvvvvvvvvvvvvvvvvvvvvv */
4539*44704f69SBart Van Assche     if (num_threads > 0) {
4540*44704f69SBart Van Assche         auto & cvp = clp->cp_ver_arr[0];
4541*44704f69SBart Van Assche 
4542*44704f69SBart Van Assche         cvp.in_fd = clp->in0fd;
4543*44704f69SBart Van Assche         cvp.out_fd = clp->out0fd;
4544*44704f69SBart Van Assche 
4545*44704f69SBart Van Assche         /* launch "infant" thread to catch early mortality, if any */
4546*44704f69SBart Van Assche         work_thr_v.emplace_back(read_write_thread, clp, 0, 0, true);
4547*44704f69SBart Van Assche         {
4548*44704f69SBart Van Assche             unique_lock<mutex> lk(clp->infant_mut);
4549*44704f69SBart Van Assche             clp->infant_cv.wait(lk, []{ return gcoll.processed; });
4550*44704f69SBart Van Assche         }
4551*44704f69SBart Van Assche         if (clp->cp_ver_arr[0].next_count_pos.load() < 0) {
4552*44704f69SBart Van Assche             /* infant thread error-ed out, join with it */
4553*44704f69SBart Van Assche             for (auto & t : work_thr_v) {
4554*44704f69SBart Van Assche                 if (t.joinable())
4555*44704f69SBart Van Assche                     t.join();
4556*44704f69SBart Van Assche             }
4557*44704f69SBart Van Assche             goto jump;
4558*44704f69SBart Van Assche         }
4559*44704f69SBart Van Assche 
4560*44704f69SBart Van Assche         /* now start the rest of the threads */
4561*44704f69SBart Van Assche         for (k = 1; k < num_threads; ++k)
4562*44704f69SBart Van Assche             work_thr_v.emplace_back(read_write_thread, clp, k,
4563*44704f69SBart Van Assche                                   k % (int)num_slices, false);
4564*44704f69SBart Van Assche 
4565*44704f69SBart Van Assche         /* now wait for worker threads to finish */
4566*44704f69SBart Van Assche         for (auto & t : work_thr_v) {
4567*44704f69SBart Van Assche             if (t.joinable())
4568*44704f69SBart Van Assche                 t.join();
4569*44704f69SBart Van Assche         }
4570*44704f69SBart Van Assche     }   /* worker threads hereafter have all exited */
4571*44704f69SBart Van Assche jump:
4572*44704f69SBart Van Assche     if (do_time && (start_tm.tv_sec || start_tm.tv_usec))
4573*44704f69SBart Van Assche         calc_duration_throughput(0);
4574*44704f69SBart Van Assche 
4575*44704f69SBart Van Assche     if (do_sync) {
4576*44704f69SBart Van Assche         if (FT_SG == clp->out_type) {
4577*44704f69SBart Van Assche             pr2serr_lk(">> Synchronizing cache on %s\n",
4578*44704f69SBart Van Assche                        (clp->outf_v.size() ? clp->outf_v[0].c_str() : "" ));
4579*44704f69SBart Van Assche             res = sg_ll_sync_cache_10(clp->out0fd, 0, 0, 0, 0, 0, false, 0);
4580*44704f69SBart Van Assche             if (SG_LIB_CAT_UNIT_ATTENTION == res) {
4581*44704f69SBart Van Assche                 pr2serr_lk("Unit attention(out), continuing\n");
4582*44704f69SBart Van Assche                 res = sg_ll_sync_cache_10(clp->out0fd, 0, 0, 0, 0, 0, false,
4583*44704f69SBart Van Assche                                           0);
4584*44704f69SBart Van Assche             }
4585*44704f69SBart Van Assche             if (0 != res)
4586*44704f69SBart Van Assche                 pr2serr_lk("Unable to synchronize cache\n");
4587*44704f69SBart Van Assche         }
4588*44704f69SBart Van Assche     }
4589*44704f69SBart Van Assche 
4590*44704f69SBart Van Assche     shutting_down = true;
4591*44704f69SBart Van Assche     for (auto & t : listen_thr_v) {
4592*44704f69SBart Van Assche         if (t.joinable()) {
4593*44704f69SBart Van Assche             t.detach();
4594*44704f69SBart Van Assche             if (listen_t_tid > 0)
4595*44704f69SBart Van Assche                 kill(listen_t_tid, SIGUSR2);
4596*44704f69SBart Van Assche             // t.~thread();        /* kill listening thread; doesn't work */
4597*44704f69SBart Van Assche         }
4598*44704f69SBart Van Assche         std::this_thread::yield();      // not enough it seems
4599*44704f69SBart Van Assche         {   /* allow time for SIGUSR2 signal to get through */
4600*44704f69SBart Van Assche             struct timespec tspec = {0, 1000000}; /* 1 msec */
4601*44704f69SBart Van Assche             struct timespec rem;
4602*44704f69SBart Van Assche 
4603*44704f69SBart Van Assche             while ((nanosleep(&tspec, &rem) < 0) && (EINTR == errno))
4604*44704f69SBart Van Assche                 tspec = rem;
4605*44704f69SBart Van Assche         }
4606*44704f69SBart Van Assche     }
4607*44704f69SBart Van Assche 
4608*44704f69SBart Van Assche fini:
4609*44704f69SBart Van Assche     if ((STDIN_FILENO != clp->in0fd) && (clp->in0fd >= 0))
4610*44704f69SBart Van Assche         close(clp->in0fd);
4611*44704f69SBart Van Assche     if ((STDOUT_FILENO != clp->out0fd) && (FT_DEV_NULL != clp->out_type) &&
4612*44704f69SBart Van Assche         (clp->out0fd >= 0))
4613*44704f69SBart Van Assche         close(clp->out0fd);
4614*44704f69SBart Van Assche     if ((clp->outregfd >= 0) && (STDOUT_FILENO != clp->outregfd) &&
4615*44704f69SBart Van Assche         (FT_DEV_NULL != clp->outreg_type))
4616*44704f69SBart Van Assche         close(clp->outregfd);
4617*44704f69SBart Van Assche     print_stats("");
4618*44704f69SBart Van Assche     if (clp->dio_incomplete_count.load()) {
4619*44704f69SBart Van Assche         int fd;
4620*44704f69SBart Van Assche         char c;
4621*44704f69SBart Van Assche 
4622*44704f69SBart Van Assche         pr2serr(">> Direct IO requested but incomplete %d times\n",
4623*44704f69SBart Van Assche                 clp->dio_incomplete_count.load());
4624*44704f69SBart Van Assche         if ((fd = open(sg_allow_dio, O_RDONLY)) >= 0) {
4625*44704f69SBart Van Assche             if (1 == read(fd, &c, 1)) {
4626*44704f69SBart Van Assche                 if ('0' == c)
4627*44704f69SBart Van Assche                     pr2serr(">>> %s set to '0' but should be set to '1' for "
4628*44704f69SBart Van Assche                             "direct IO\n", sg_allow_dio);
4629*44704f69SBart Van Assche             }
4630*44704f69SBart Van Assche             close(fd);
4631*44704f69SBart Van Assche         }
4632*44704f69SBart Van Assche     }
4633*44704f69SBart Van Assche 
4634*44704f69SBart Van Assche     k = 0;
4635*44704f69SBart Van Assche     for (auto && cvp : gcoll.cp_ver_arr) {
4636*44704f69SBart Van Assche         if (cvp.state == cp_ver_pair_t::my_state::empty)
4637*44704f69SBart Van Assche             break;
4638*44704f69SBart Van Assche         ++k;
4639*44704f69SBart Van Assche         if (cvp.sum_of_resids.load())
4640*44704f69SBart Van Assche             pr2serr(">> slice: %d, Non-zero sum of residual counts=%d\n",
4641*44704f69SBart Van Assche                     k, cvp.sum_of_resids.load());
4642*44704f69SBart Van Assche     }
4643*44704f69SBart Van Assche     if (clp->verbose && (num_start_eagain > 0))
4644*44704f69SBart Van Assche         pr2serr("Number of start EAGAINs: %d\n", num_start_eagain.load());
4645*44704f69SBart Van Assche     if (clp->verbose && (num_fin_eagain > 0))
4646*44704f69SBart Van Assche         pr2serr("Number of finish EAGAINs: %d\n", num_fin_eagain.load());
4647*44704f69SBart Van Assche     if (clp->verbose && (num_ebusy > 0))
4648*44704f69SBart Van Assche         pr2serr("Number of EBUSYs: %d\n", num_ebusy.load());
4649*44704f69SBart Van Assche     if (clp->verbose && (num_miscompare > 0))
4650*44704f69SBart Van Assche         pr2serr("Number of miscompare%s: %d\n",
4651*44704f69SBart Van Assche                 (num_miscompare > 1) ? "s" : "", num_miscompare.load());
4652*44704f69SBart Van Assche     if (clp->verify && (SG_LIB_CAT_MISCOMPARE == res))
4653*44704f69SBart Van Assche         pr2serr("Verify/compare failed due to miscompare\n");
4654*44704f69SBart Van Assche     if (0 == res)
4655*44704f69SBart Van Assche         res = clp->reason_res.load();
4656*44704f69SBart Van Assche     sigprocmask(SIG_SETMASK, &orig_signal_set, NULL);
4657*44704f69SBart Van Assche     if (clp->verbose) {
4658*44704f69SBart Van Assche         int num_sigusr2 = num_fallthru_sigusr2.load();
4659*44704f69SBart Van Assche         if (num_sigusr2 > 0)
4660*44704f69SBart Van Assche             pr2serr("Number of fall-through SIGUSR2 signals caught: %d\n",
4661*44704f69SBart Van Assche                     num_sigusr2);
4662*44704f69SBart Van Assche     }
4663*44704f69SBart Van Assche     return (res >= 0) ? res : SG_LIB_CAT_OTHER;
4664*44704f69SBart Van Assche }
4665