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