diff options
author | Douglas Gilbert <dgilbert@interlog.com> | 2018-12-07 16:42:06 +0000 |
---|---|---|
committer | Douglas Gilbert <dgilbert@interlog.com> | 2018-12-07 16:42:06 +0000 |
commit | 4445eeca7f024eb55a9d5eb5ee6a7183816f0c2e (patch) | |
tree | 58e83f896645256c3925ca42ab5e2393aa1c66e9 /testing | |
parent | 8101850bc7cd2a05d35b4df3ecfd3f934cff584e (diff) | |
download | sg3_utils-4445eeca7f024eb55a9d5eb5ee6a7183816f0c2e.tar.gz |
add: SPDX-License-Identifier: BSD-2-Clause (or gpl-2-or-later); testing/sgs_dd.c work
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@797 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'testing')
-rw-r--r-- | testing/Makefile | 11 | ||||
-rw-r--r-- | testing/sg_chk_asc.c | 4 | ||||
-rw-r--r-- | testing/sg_iovec_tst.c | 34 | ||||
-rw-r--r-- | testing/sg_queue_tst.c | 35 | ||||
-rw-r--r-- | testing/sg_sense_test.c | 4 | ||||
-rw-r--r-- | testing/sg_tst_async.cpp | 6 | ||||
-rw-r--r-- | testing/sg_tst_context.cpp | 6 | ||||
-rw-r--r-- | testing/sg_tst_excl.cpp | 8 | ||||
-rw-r--r-- | testing/sg_tst_excl2.cpp | 6 | ||||
-rw-r--r-- | testing/sg_tst_excl3.cpp | 8 | ||||
-rw-r--r-- | testing/sg_tst_ioctl.c | 25 | ||||
-rw-r--r-- | testing/sg_tst_nvme.c | 4 | ||||
-rw-r--r-- | testing/sgs_dd.c | 732 | ||||
-rw-r--r-- | testing/tst_sg_lib.c | 4 | ||||
-rw-r--r-- | testing/uapi_sg.h | 4 |
15 files changed, 532 insertions, 359 deletions
diff --git a/testing/Makefile b/testing/Makefile index 9878c04e..c4dfbc30 100644 --- a/testing/Makefile +++ b/testing/Makefile @@ -4,15 +4,6 @@ PREFIX=/usr/local INSTDIR=$(DESTDIR)/$(PREFIX)/bin MANDIR=$(DESTDIR)/$(PREFIX)/man -# In Linux the default C compiler is GCC while in FreeBSD (since release 10 ?) -# the default C compiler is clang. Swap the comment marks (lines starting -# with '#') on the next 4 (non-blank) lines. -CC = gcc -# CC = clang - -LD = gcc -# LD = clang - EXECS = sg_iovec_tst sg_sense_test sg_queue_tst bsg_queue_tst sg_chk_asc \ sg_tst_nvme sg_tst_ioctl tst_sg_lib sgs_dd @@ -32,6 +23,8 @@ LARGE_FILE_FLAGS = -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 ## CC = clang ## CC = clang++ +LD = $(CC) + CPPFLAGS = -iquote ../include -iquote .. -D_REENTRANT $(LARGE_FILE_FLAGS) -DHAVE_CONFIG_H -DHAVE_NVME # CPPFLAGS = -iquote ../include -iquote .. -D_REENTRANT $(LARGE_FILE_FLAGS) -DHAVE_CONFIG_H -DHAVE_NVME -DDEBUG CFLAGS = -g -O2 -W -Wall diff --git a/testing/sg_chk_asc.c b/testing/sg_chk_asc.c index ef37729f..dd320fd5 100644 --- a/testing/sg_chk_asc.c +++ b/testing/sg_chk_asc.c @@ -3,6 +3,8 @@ * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. + * + * SPDX-License-Identifier: BSD-2-Clause */ #include <unistd.h> @@ -25,7 +27,7 @@ * http://www.t10.org/lists/asc-num.txt */ -static const char * version_str = "1.06 20180119"; +static const char * version_str = "1.07 20181207"; #define MAX_LINE_LEN 1024 diff --git a/testing/sg_iovec_tst.c b/testing/sg_iovec_tst.c index a968e385..b066e8b0 100644 --- a/testing/sg_iovec_tst.c +++ b/testing/sg_iovec_tst.c @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2003-2018 D. Gilbert + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg") + * device driver. + * This program will read a certain number of blocks of a given block size + * from a given sg device node and write what is retrieved out to a + * normal file. The purpose is to test the sg_iovec mechanism within the + * sg_io_hdr structure. + * + * Version 0.17 (20181207) + */ + #include <unistd.h> #include <signal.h> #include <fcntl.h> @@ -14,21 +33,6 @@ #include "sg_io_linux.h" #include "sg_unaligned.h" -/* Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg") - device driver. -* Copyright (C) 2003-2018 D. Gilbert -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2, or (at your option) -* any later version. - - This program will read a certain number of blocks of a given block size - from a given sg device node and write what is retrieved out to a - normal file. The purpose is to test the sg_iovec mechanism within the - sg_io_hdr structure. - - Version 0.16 (20180219) -*/ #define ME "sg_iovec_tst: " diff --git a/testing/sg_queue_tst.c b/testing/sg_queue_tst.c index e9c03af1..a3f8dc0c 100644 --- a/testing/sg_queue_tst.c +++ b/testing/sg_queue_tst.c @@ -1,3 +1,23 @@ +/* + * Copyright (C) 2010-2018 D. Gilbert + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * + * This program was used to test SCSI mid level queue ordering. + * The default behaviour is to "queue at head" which is useful for + * error processing but not for streaming READ and WRITE commands. + * + * Invocation: sg_queue_tst [-l=Q_LEN] [-t] <sg_device> + * -t queue at tail + * + * Version 0.93 (20181207) + */ + #include <unistd.h> #include <fcntl.h> #include <stdio.h> @@ -13,22 +33,7 @@ #include "sg_io_linux.h" #include "sg_linux_inc.h" -/* This program was used to test SCSI mid level queue ordering. - The default behaviour is to "queue at head" which is useful for - error processing but not for streaming READ and WRITE commands. - -* Copyright (C) 2010-2018 D. Gilbert -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2, or (at your option) -* any later version. - - Invocation: sg_queue_tst [-l=Q_LEN] [-t] <sg_device> - -t queue at tail - - Version 0.92 (20181011) -*/ #define INQ_REPLY_LEN 96 #define INQ_CMD_LEN 6 diff --git a/testing/sg_sense_test.c b/testing/sg_sense_test.c index ea8bfd54..ce66cf3e 100644 --- a/testing/sg_sense_test.c +++ b/testing/sg_sense_test.c @@ -4,6 +4,8 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. + * + * SPDX-License-Identifier: GPL-2.0-or-later */ /* This is a simple program that tests the sense data descriptor format @@ -24,7 +26,7 @@ #define ME "sg_sense_test: " -static const char * version_str = "2.03 20180220"; +static const char * version_str = "2.04 20181207"; static struct option long_options[] = { {"help", no_argument, 0, 'h'}, diff --git a/testing/sg_tst_async.cpp b/testing/sg_tst_async.cpp index 485bae83..a6ac65ee 100644 --- a/testing/sg_tst_async.cpp +++ b/testing/sg_tst_async.cpp @@ -10,8 +10,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -24,6 +22,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * SPDX-License-Identifier: BSD-2-Clause */ #include <iostream> @@ -82,7 +82,7 @@ #include "sg_pt.h" #include "sg_cmds.h" -static const char * version_str = "1.21 20181107"; +static const char * version_str = "1.22 20181207"; static const char * util_name = "sg_tst_async"; /* This is a test program for checking the async usage of the Linux sg diff --git a/testing/sg_tst_context.cpp b/testing/sg_tst_context.cpp index b811c56e..89b7a7d2 100644 --- a/testing/sg_tst_context.cpp +++ b/testing/sg_tst_context.cpp @@ -10,8 +10,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -24,6 +22,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * SPDX-License-Identifier: BSD-2-Clause */ #include <iostream> @@ -46,7 +46,7 @@ #include "sg_lib.h" #include "sg_pt.h" -static const char * version_str = "1.03 20180712"; +static const char * version_str = "1.04 20181207"; static const char * util_name = "sg_tst_context"; /* This is a test program for checking that file handles keep their diff --git a/testing/sg_tst_excl.cpp b/testing/sg_tst_excl.cpp index 5b4e6956..d5c0bec8 100644 --- a/testing/sg_tst_excl.cpp +++ b/testing/sg_tst_excl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 Douglas Gilbert. + * Copyright (c) 2013-2018 Douglas Gilbert. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -10,8 +10,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -24,6 +22,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * SPDX-License-Identifier: BSD-2-Clause */ #include <iostream> @@ -46,7 +46,7 @@ #include "sg_lib.h" #include "sg_io_linux.h" -static const char * version_str = "1.09 20140828"; +static const char * version_str = "1.10 20181207"; static const char * util_name = "sg_tst_excl"; /* This is a test program for checking O_EXCL on open() works. It uses diff --git a/testing/sg_tst_excl2.cpp b/testing/sg_tst_excl2.cpp index 24d3677b..9a03906c 100644 --- a/testing/sg_tst_excl2.cpp +++ b/testing/sg_tst_excl2.cpp @@ -10,8 +10,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -24,6 +22,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * SPDX-License-Identifier: BSD-2-Clause */ #include <iostream> @@ -46,7 +46,7 @@ #include "sg_lib.h" #include "sg_pt.h" -static const char * version_str = "1.07 20140828"; +static const char * version_str = "1.08 20181207"; static const char * util_name = "sg_tst_excl2"; /* This is a test program for checking O_EXCL on open() works. It uses diff --git a/testing/sg_tst_excl3.cpp b/testing/sg_tst_excl3.cpp index 6b6b880b..bd15389a 100644 --- a/testing/sg_tst_excl3.cpp +++ b/testing/sg_tst_excl3.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014 Douglas Gilbert. + * Copyright (c) 2013-2018 Douglas Gilbert. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -10,8 +10,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -24,6 +22,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * SPDX-License-Identifier: BSD-2-Clause */ #include <iostream> @@ -46,7 +46,7 @@ #include "sg_lib.h" #include "sg_pt.h" -static const char * version_str = "1.05 20140828"; +static const char * version_str = "1.06 20181207"; static const char * util_name = "sg_tst_excl3"; /* This is a test program for checking O_EXCL on open() works. It uses diff --git a/testing/sg_tst_ioctl.c b/testing/sg_tst_ioctl.c index b37be2e6..d7dc227b 100644 --- a/testing/sg_tst_ioctl.c +++ b/testing/sg_tst_ioctl.c @@ -1,3 +1,16 @@ +/* + * Copyright (C) 2018 D. Gilbert + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * SPDX-License-Identifier: BSD-2-Clause + * + * Invocation: See usage() function below. + * + */ + #include <unistd.h> #include <fcntl.h> #include <stdio.h> @@ -31,18 +44,8 @@ /* This program tests ioctl() calls added and modified in version 3.9 and * later of the Linux sg driver. */ -/* - * Copyright (C) 2018 D. Gilbert - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * Invocation: See usage() function below. - * - */ -static const char * version_str = "Version: 0.97 20181121"; +static const char * version_str = "Version: 0.98 20181207"; #define INQ_REPLY_LEN 96 #define INQ_CMD_LEN 6 diff --git a/testing/sg_tst_nvme.c b/testing/sg_tst_nvme.c index bded6b69..6ba16476 100644 --- a/testing/sg_tst_nvme.c +++ b/testing/sg_tst_nvme.c @@ -4,6 +4,8 @@ * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. * + * SPDX-License-Identifier: BSD-2-Clause + * * This program issues a NVMe Identify command (controller or namespace) * or a Device self-test command via the "SCSI" pass-through interface of * this packages sg_utils library. That interface is primarily shown in @@ -37,7 +39,7 @@ #include "sg_unaligned.h" #include "sg_pr2serr.h" -static const char * version_str = "1.04 20180220"; +static const char * version_str = "1.05 20181207"; #define ME "sg_tst_nvme: " diff --git a/testing/sgs_dd.c b/testing/sgs_dd.c index a82ca80a..95d36fc2 100644 --- a/testing/sgs_dd.c +++ b/testing/sgs_dd.c @@ -7,6 +7,8 @@ * the Free Software Foundation; either version 2, or (at your option) * any later version. * + * SPDX-License-Identifier: GPL-2.0-or-later + * * This program is a specialisation of the Unix "dd" command in which * one or both of the given files is a scsi generic device. * A block size ('bs') is assumed to be 512 if not given. This @@ -58,6 +60,7 @@ #include <sys/time.h> #include <linux/major.h> /* for MEM_MAJOR, SCSI_GENERIC_MAJOR, etc */ #include <linux/fs.h> /* for BLKSSZGET and friends */ +#include <sys/mman.h> /* for mmap() system call */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -85,7 +88,11 @@ #include "sg_pr2serr.h" -static const char * version_str = "1.03 20181126"; +static const char * version_str = "1.04 20181207"; + +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wclobbered" +#endif #define DEF_BLOCK_SIZE 512 #define DEF_BLOCKS_PER_TRANSFER 128 @@ -122,14 +129,17 @@ static const char * version_str = "1.03 20181126"; #define EBUFF_SZ 768 struct flags_t { + bool fds2; /* flag is called '2fds' */ bool append; bool coe; + bool defres; /* without this res_sz==bs*bpt */ bool dio; bool direct; bool dpo; bool dsync; bool excl; bool fua; + bool mmap; bool noshare; }; @@ -141,9 +151,9 @@ typedef struct global_collection int cdbsz_in; int help; struct flags_t in_flags; - int64_t in_blk; /* -\ next block address to read */ - int64_t in_count; /* | blocks remaining for next read */ - int64_t in_rem_count; /* | count of remaining in blocks */ + int64_t in_blk; /* -\ next block address to read */ + int64_t in_count; /* | blocks remaining for next read */ + int64_t in_rem_count; /* | count of remaining in blocks */ int in_partial; /* | */ bool in_stop; /* | */ pthread_mutex_t in_mutex; /* -/ */ @@ -154,20 +164,22 @@ typedef struct global_collection int out2_type; int cdbsz_out; struct flags_t out_flags; - int64_t out_blk; /* -\ next block address to write */ - int64_t out_count; /* | blocks remaining for next write */ - int64_t out_rem_count; /* | count of remaining out blocks */ + int64_t out_blk; /* -\ next block address to write */ + int64_t out_count; /* | blocks remaining for next write */ + int64_t out_rem_count; /* | count of remaining out blocks */ int out_partial; /* | */ bool out_stop; /* | */ pthread_mutex_t out_mutex; /* | */ - pthread_cond_t out_sync_cv; /* -/ hold writes until "in order" */ + pthread_cond_t out_sync_cv; /* | hold writes until "in order" */ + pthread_cond_t hold_1st_cv; /* -/ wait for 1st thread to do 1 seg */ int bs; int bpt; int dio_incomplete_count; /* -\ */ - int sum_of_resids; /* | */ - pthread_mutex_t aux_mutex; /* -/ (also serializes some printf()s */ + int sum_of_resids; /* -/ */ int debug; /* both -v and deb=VERB bump this field */ int dry_run; + const char * infp; + const char * outfp; } Gbl_coll; typedef struct request_element @@ -190,6 +202,7 @@ typedef struct request_element int resid; int cdbsz_in; int cdbsz_out; + int mmap_len; struct flags_t in_flags; struct flags_t out_flags; int debug; @@ -199,6 +212,7 @@ typedef struct thread_info { int id; Gbl_coll * gcp; + pthread_t a_pthr; } Thread_info; static sigset_t signal_set; @@ -206,12 +220,16 @@ static pthread_t sig_listen_thread_id; static const char * proc_allow_dio = "/proc/scsi/sg/allow_dio"; -static void sg_in_operation(Gbl_coll * clp, Rq_elem * rep); -static void sg_out_operation(Gbl_coll * clp, Rq_elem * rep); -static bool normal_in_operation(Gbl_coll * clp, Rq_elem * rep, int blocks); -static void normal_out_operation(Gbl_coll * clp, Rq_elem * rep, int blocks); +static void sg_in_rd_cmd(Gbl_coll * clp, Rq_elem * rep); +static void sg_out_wr_cmd(Gbl_coll * clp, Rq_elem * rep); +static bool normal_in_rd(Gbl_coll * clp, Rq_elem * rep, int blocks); +static void normal_out_wr(Gbl_coll * clp, Rq_elem * rep, int blocks); static int sg_start_io(Rq_elem * rep); -static int sg_finish_io(bool wr, Rq_elem * rep, pthread_mutex_t * a_mutp); +static int sg_finish_io(bool wr, Rq_elem * rep); +static int sg_in_open(Gbl_coll *clp, const char *inf, uint8_t **mmpp, + int *mmap_len); +static int sg_out_open(Gbl_coll *clp, const char *outf, uint8_t **mmpp, + int *mmap_len); #define STRERR_BUFF_LEN 128 @@ -275,6 +293,22 @@ pr_errno_lk(int e_no, const char * fmt, ...) #endif static void +lk_print_command(uint8_t * cmdp) +{ + pthread_mutex_lock(&strerr_mut); + sg_print_command(cmdp); + pthread_mutex_unlock(&strerr_mut); +} + +static void +lk_chk_n_print3(const char * leadin, struct sg_io_hdr * hp, bool raw_sinfo) +{ + pthread_mutex_lock(&strerr_mut); + sg_chk_n_print3(leadin, hp, raw_sinfo); + pthread_mutex_unlock(&strerr_mut); +} + +static void calc_duration_throughput(int contin) { struct timeval end_tm, res_tm; @@ -419,8 +453,11 @@ dd_filetype(const char * filename) static void usage(int pg_num) { - if (pg_num > 0) + if (pg_num > 2) + goto page3; + else if (pg_num > 1) goto page2; + pr2serr("Usage: sgs_dd [bs=BS] [count=COUNT] [ibs=BS] [if=IFILE]" " [iflag=FLAGS]\n" " [obs=BS] [of=OFILE] [oflag=FLAGS] " @@ -430,32 +467,28 @@ usage(int pg_num) "[deb=VERB] [dio=0|1]\n" " [fua=0|1|2|3] [of2=OFILE2] [sync=0|1] [thr=THR] " "[time=0|1]\n" - " [verbose=VERB] [--dry-run] [--verbose]\n" + " [verbose=VERB] [--dry-run] [--verbose]\n\n" " where the main options (shown in first group above) are:\n" " bs must be device block size (default 512)\n" " count number of blocks to copy (def: device size)\n" " if file or device to read from (def: stdin)\n" - " iflag comma separated list from: [coe,dio,direct,dpo," - "dsync,excl,\n" - " fua,noshare,null]\n" + " iflag comma separated list from: [2fds,coe,defres,dio," + "direct,dpo,\n" + " dsync,excl,fua,mmap,noshare,null]\n" " of file or device to write to (def: stdout), " "OFILE of '.'\n" " treated as /dev/null\n" - " oflag comma separated list from: [append,coe,dio," - "direct,dpo,dsync,\n" - " excl,fua,noshare,null]\n" + " oflag comma separated list from: [2fds,append,coe,dio," + "direct,dpo,\n" + " dsync,excl,fua,mmap,noshare,null]\n" " seek block position to start writing to OFILE\n" " skip block position to start reading from IFILE\n" " --help|-h output this usage message then exit\n" " --version|-V output version string then exit\n\n" "Copy from IFILE to OFILE, similar to dd command. This utility " "is specialized\nfor SCSI devices and uses multiple POSIX " - "threads. It expects one or both\nIFILE and OFILE to be SCSI " - "generic devices. If both are sg devices 'shared'\nmode is " - "selected unless 'noshare' is given to 'iflag=' or 'oflag='. " - "If\n'of2=OFLAG2' is given, the read-side is output to OFLAG2. " - "Without 'of2=' and\nwith IFILE and OFILE sg devices the copy " - "is via a single in-kernel buffer.\n" + "threads. It expects one or both\nIFILE and OFILE to be sg " + "devices. Use '-hh' or '-hhh' for more information.\n" ); return; page2: @@ -486,7 +519,40 @@ page2: "throughput\n" " verbose same as 'deb=VERB': increase verbosity\n" " --dry-run|-d prepare but bypass copy/read\n" - " --verbose|-v increase verbosity of utility\n" + " --verbose|-v increase verbosity of utility\n\n" + "Use '-hhh' for more information about flags.\n" + ); + return; +page3: + pr2serr("Syntax: sgs_dd [operands] [options]\n\n" + " where: iflag=' and 'oflag=' arguments are listed below:\n" + " 2fds only 2 file descriptors (1 each for IFILE and " + "OFILE) are\n" + " used. Default: a pair of fd_s for each thread\n" + " append append output to OFILE (assumes OFILE is " + "regular file)\n" + " coe continue of error (reading, fills with zeros)\n" + " defres keep default reserve buffer size (else its " + "bs*bpt)\n" + " dio sets the SG_FLAG_DIRECT_IO in sg requests\n" + " direct sets the O_DIRECT flag on open()\n" + " dpo sets the DPO (disable page out) in SCSI READs " + "and WRITEs\n" + " dsync sets the O_SYNC flag on open()\n" + " excl sets the O_EXCL flag on open()\n" + " fua sets the FUA (force unit access) in SCSI READs " + "and WRITEs\n" + " mmap setup mmap IO on IFILE or OFILE; OFILE only " + "with noshare\n" + " noshare if IFILE and OFILE are sg devices, don't set " + "up share\n" + " (def: do)\n" + "\n" + "If both are sg devices 'shared' mode is selected unless " + "'noshare' is given\nto 'iflag=' or 'oflag='. If 'of2=OFLAG2' " + "is given, the read-side is output\nto OFLAG2. Without 'of2=' " + "and with IFILE and OFILE sg devices the copy is\nvia a single " + "in-kernel buffer.\n" ); } @@ -609,12 +675,15 @@ sg_share_prepare(int slave_writer_fd, int master_reader_fd, int id, bool vb_b) seip->valid_rd_mask |= SG_SEIM_SHARE_FD; seip->share_fd = master_reader_fd; if (ioctl(slave_writer_fd, SG_SET_GET_EXTENDED, seip) < 0) { - if (vb_b) - pr2serr_lk("tid=%d: ioctl(EXTENDED(shared_fd=%d), failed " - "errno=%d %s\n", id, master_reader_fd, errno, - strerror(errno)); + pr2serr_lk("tid=%d: ioctl(EXTENDED(shared_fd=%d), failed " + "errno=%d %s\n", id, master_reader_fd, errno, + strerror(errno)); return false; } + if (vb_b) + pr2serr_lk("%s: tid=%d: ioctl(EXTENDED(shared_fd)) ok, master_fd=%d, " + "slave_fd=%d\n", __func__, id, master_reader_fd, + slave_writer_fd); return true; } @@ -635,7 +704,7 @@ cleanup_out(void * v_clp) { Gbl_coll * clp = (Gbl_coll *)v_clp; - pr2serr("thread cancelled while out mutex held\n"); + pr2serr("thread cancelled while out_mutex held\n"); clp->out_stop = true; pthread_mutex_unlock(&clp->out_mutex); guarded_stop_in(clp); @@ -649,9 +718,13 @@ read_write_thread(void * v_tip) Gbl_coll * clp; Rq_elem rel; Rq_elem * rep = &rel; - int sz, blocks, status, vb; + int sz, blocks, status, vb, err, res; int64_t seek_skip; volatile bool stop_after_write = false; + bool own_infd = false; + bool own_outfd = false; + bool is_first = true; + bool share_and_of2; tip = (Thread_info *)v_tip; clp = tip->gcp; @@ -659,12 +732,14 @@ read_write_thread(void * v_tip) sz = clp->bpt * clp->bs; seek_skip = clp->seek - clp->skip; memset(rep, 0, sizeof(Rq_elem)); - rep->buffp = sg_memalign(sz, 0 /* page align */, &rep->alloc_bp, false); - if (NULL == rep->buffp) - err_exit(ENOMEM, "out of memory creating user buffers\n"); - /* Following clp members are constant during lifetime of thread */ rep->id = tip->id; + if (! clp->in_flags.mmap) { + rep->buffp = sg_memalign(sz, 0 /* page align */, &rep->alloc_bp, + false); + if (NULL == rep->buffp) + err_exit(ENOMEM, "out of memory creating user buffers\n"); + } rep->bs = clp->bs; rep->infd = clp->infd; @@ -675,17 +750,56 @@ read_write_thread(void * v_tip) rep->cdbsz_out = clp->cdbsz_out; rep->in_flags = clp->in_flags; rep->out_flags = clp->out_flags; + if (rep->in_flags.fds2 || rep->out_flags.fds2) + ; + else { + int fd; + + if ((FT_SG == clp->in_type) && clp->infp) { + fd = sg_in_open(clp, clp->infp, + (rep->in_flags.mmap ? &rep->buffp : NULL), + (rep->in_flags.mmap ? &rep->mmap_len : NULL)); + if (fd < 0) + goto fini; + rep->infd = fd; + own_infd = true; + if (vb > 2) + pr2serr_lk("thread=%d: opened local sg IFILE\n", rep->id); + } + if ((FT_SG == clp->out_type) && clp->outfp) { + fd = sg_out_open(clp, clp->outfp, + (rep->out_flags.mmap ? &rep->buffp : NULL), + (rep->out_flags.mmap ? &rep->mmap_len : NULL)); + if (fd < 0) + goto fini; + rep->outfd = fd; + own_outfd = true; + if (vb > 2) + pr2serr_lk("thread=%d: opened local sg OFILE\n", rep->id); + } + } + if (vb > 2) { + if (! own_infd) + pr2serr_lk("thread=%d: using global sg IFILE\n", rep->id); + if (! own_outfd) + pr2serr_lk("thread=%d: using global sg OFILE\n", rep->id); + } if (rep->in_flags.noshare || rep->out_flags.noshare) { if (vb) - pr2serr_lk("Skipping share on both IFILE and OFILE\n"); - } else - rep->has_share = sg_share_prepare(clp->outfd, clp->infd, rep->id, - clp->debug); + pr2serr_lk("thread=%d: Skipping share on both IFILE and OFILE\n", + rep->id); + } else if ((FT_SG == clp->in_type) && (FT_SG == clp->out_type)) + rep->has_share = sg_share_prepare(rep->outfd, rep->infd, rep->id, + rep->debug); if (vb > 0) pr2serr_lk("starting thread %d, has_share=%s\n", rep->id, (rep->has_share ? "true" : "false")); + share_and_of2 = (rep->has_share && (rep->out2fd >= 0)); - while(1) { + /* vvvvvvvvvvvvvv Main segment copy loop vvvvvvvvvvvvvvvvvvvvvvv */ + while (1) { + rep->wr = false; + /* Start of READ half of a segment */ status = pthread_mutex_lock(&clp->in_mutex); if (0 != status) err_exit(status, "lock in_mutex"); if (clp->in_stop || (clp->in_count <= 0)) { @@ -695,7 +809,6 @@ read_write_thread(void * v_tip) break; } blocks = (clp->in_count > clp->bpt) ? clp->bpt : clp->in_count; - rep->wr = false; rep->blk = clp->in_blk; rep->num_blks = blocks; clp->in_blk += blocks; @@ -703,17 +816,19 @@ read_write_thread(void * v_tip) pthread_cleanup_push(cleanup_in, (void *)clp); if (FT_SG == clp->in_type) - sg_in_operation(clp, rep); /* go off in_mutex mid operation */ + sg_in_rd_cmd(clp, rep); /* unlocks in_mutex mid operation */ else { - stop_after_write = normal_in_operation(clp, rep, blocks); + stop_after_write = normal_in_rd(clp, rep, blocks); status = pthread_mutex_unlock(&clp->in_mutex); if (0 != status) err_exit(status, "unlock in_mutex"); } pthread_cleanup_pop(0); + /* Start of WRITE part of a segment */ + rep->wr = true; status = pthread_mutex_lock(&clp->out_mutex); if (0 != status) err_exit(status, "lock out_mutex"); - if (FT_DEV_NULL != clp->out_type) { + if (share_and_of2 || (FT_DEV_NULL != clp->out_type)) { while ((! clp->out_stop) && ((rep->blk + seek_skip) != clp->out_blk)) { /* if write would be out of sequence then wait */ @@ -733,7 +848,6 @@ read_write_thread(void * v_tip) } if (stop_after_write) clp->out_stop = true; - rep->wr = true; rep->blk = clp->out_blk; clp->out_blk += blocks; clp->out_count -= blocks; @@ -747,8 +861,15 @@ read_write_thread(void * v_tip) } pthread_cleanup_push(cleanup_out, (void *)clp); + if (share_and_of2) { + res = write(rep->out2fd, rep->buffp, rep->bs * rep->num_blks); + err = errno; + if (res < 0) + pr2serr_lk("%s: tid=%d: write(out2fd) failed: %s\n", __func__, + rep->id, strerror(err)); + } if (FT_SG == clp->out_type) - sg_out_operation(clp, rep); /* releases out_mutex mid oper */ + sg_out_wr_cmd(clp, rep); /* releases out_mutex mid oper */ else if (FT_DEV_NULL == clp->out_type) { /* skip actual write operation */ clp->out_rem_count -= blocks; @@ -756,17 +877,32 @@ read_write_thread(void * v_tip) if (0 != status) err_exit(status, "unlock out_mutex"); } else { - normal_out_operation(clp, rep, blocks); + normal_out_wr(clp, rep, blocks); status = pthread_mutex_unlock(&clp->out_mutex); if (0 != status) err_exit(status, "unlock out_mutex"); } pthread_cleanup_pop(0); + // if ((! rep->has_share) && (FT_DEV_NULL != clp->out_type)) + pthread_cond_broadcast(&clp->out_sync_cv); + if ((0 == rep->id) && is_first) { + is_first = false; /* allow rest of threads to start */ + pthread_cond_broadcast(&clp->hold_1st_cv); + } if (stop_after_write) break; - pthread_cond_broadcast(&clp->out_sync_cv); - } /* end of while loop */ - if (rep->alloc_bp) + } /* end of while loop which copies segments */ + + if (rep->mmap_len > 0) { + if (munmap(rep->buffp, rep->mmap_len) < 0) { + int err = errno; + char bb[64]; + + pr2serr_lk("thread=%d: munmap() failed: %s\n", rep->id, + tsafe_strerror(err, bb)); + } + + } else if (rep->alloc_bp) free(rep->alloc_bp); status = pthread_mutex_lock(&clp->in_mutex); if (0 != status) err_exit(status, "lock in_mutex"); @@ -774,12 +910,17 @@ read_write_thread(void * v_tip) clp->in_stop = true; /* flag other workers to stop */ status = pthread_mutex_unlock(&clp->in_mutex); if (0 != status) err_exit(status, "unlock in_mutex"); +fini: + if (own_infd) + close(rep->infd); + if (own_outfd) + close(rep->outfd); pthread_cond_broadcast(&clp->out_sync_cv); return stop_after_write ? NULL : clp; } static bool -normal_in_operation(Gbl_coll * clp, Rq_elem * rep, int blocks) +normal_in_rd(Gbl_coll * clp, Rq_elem * rep, int blocks) { bool stop_after_write = false; int res; @@ -826,7 +967,7 @@ normal_in_operation(Gbl_coll * clp, Rq_elem * rep, int blocks) } static void -normal_out_operation(Gbl_coll * clp, Rq_elem * rep, int blocks) +normal_out_wr(Gbl_coll * clp, Rq_elem * rep, int blocks) { int res; char strerr_buff[STRERR_BUFF_LEN]; @@ -932,13 +1073,13 @@ sg_build_scsi_cdb(uint8_t * cdbp, int cdb_sz, unsigned int blocks, return 0; } +/* Enters this function holding in_mutex */ static void -sg_in_operation(Gbl_coll * clp, Rq_elem * rep) +sg_in_rd_cmd(Gbl_coll * clp, Rq_elem * rep) { int res; int status; - /* enters holding in_mutex */ while (1) { res = sg_start_io(rep); if (1 == res) @@ -955,7 +1096,7 @@ sg_in_operation(Gbl_coll * clp, Rq_elem * rep) status = pthread_mutex_unlock(&clp->in_mutex); if (0 != status) err_exit(status, "unlock in_mutex"); - res = sg_finish_io(rep->wr, rep, &clp->aux_mutex); + res = sg_finish_io(rep->wr, rep); switch (res) { case SG_LIB_CAT_ABORTED_COMMAND: case SG_LIB_CAT_UNIT_ATTENTION: @@ -964,7 +1105,7 @@ sg_in_operation(Gbl_coll * clp, Rq_elem * rep) /* N.B. This re-read could now be out of read sequence */ status = pthread_mutex_lock(&clp->in_mutex); if (0 != status) err_exit(status, "lock in_mutex"); - break; + break; /* will loop again */ case SG_LIB_CAT_MEDIUM_HARD: if (0 == clp->in_flags.coe) { pr2serr_lk("error finishing sg in command (medium)\n"); @@ -985,16 +1126,12 @@ sg_in_operation(Gbl_coll * clp, Rq_elem * rep) #endif #endif case 0: + status = pthread_mutex_lock(&clp->in_mutex); + if (0 != status) err_exit(status, "lock in_mutex"); if (rep->dio_incomplete_count || rep->resid) { - status = pthread_mutex_lock(&clp->aux_mutex); - if (0 != status) err_exit(status, "lock aux_mutex"); clp->dio_incomplete_count += rep->dio_incomplete_count; clp->sum_of_resids += rep->resid; - status = pthread_mutex_unlock(&clp->aux_mutex); - if (0 != status) err_exit(status, "unlock aux_mutex"); } - status = pthread_mutex_lock(&clp->in_mutex); - if (0 != status) err_exit(status, "lock in_mutex"); clp->in_rem_count -= rep->num_blks; status = pthread_mutex_unlock(&clp->in_mutex); if (0 != status) err_exit(status, "unlock in_mutex"); @@ -1007,17 +1144,16 @@ sg_in_operation(Gbl_coll * clp, Rq_elem * rep) guarded_stop_both(clp); return; } - } + } /* end of while (1) loop */ } +/* Enters this function holding out_mutex */ static void -sg_out_operation(Gbl_coll * clp, Rq_elem * rep) +sg_out_wr_cmd(Gbl_coll * clp, Rq_elem * rep) { int res; int status; - - /* enters holding out_mutex */ while (1) { res = sg_start_io(rep); if (1 == res) @@ -1034,7 +1170,7 @@ sg_out_operation(Gbl_coll * clp, Rq_elem * rep) status = pthread_mutex_unlock(&clp->out_mutex); if (0 != status) err_exit(status, "unlock out_mutex"); - res = sg_finish_io(rep->wr, rep, &clp->aux_mutex); + res = sg_finish_io(rep->wr, rep); switch (res) { case SG_LIB_CAT_ABORTED_COMMAND: case SG_LIB_CAT_UNIT_ATTENTION: @@ -1043,7 +1179,7 @@ sg_out_operation(Gbl_coll * clp, Rq_elem * rep) /* N.B. This re-write could now be out of write sequence */ status = pthread_mutex_lock(&clp->out_mutex); if (0 != status) err_exit(status, "lock out_mutex"); - break; + break; /* loops around */ case SG_LIB_CAT_MEDIUM_HARD: if (0 == clp->out_flags.coe) { pr2serr_lk("error finishing sg out command (medium)\n"); @@ -1061,16 +1197,12 @@ sg_out_operation(Gbl_coll * clp, Rq_elem * rep) #endif #endif case 0: + status = pthread_mutex_lock(&clp->out_mutex); + if (0 != status) err_exit(status, "lock out_mutex"); if (rep->dio_incomplete_count || rep->resid) { - status = pthread_mutex_lock(&clp->aux_mutex); - if (0 != status) err_exit(status, "lock aux_mutex"); clp->dio_incomplete_count += rep->dio_incomplete_count; clp->sum_of_resids += rep->resid; - status = pthread_mutex_unlock(&clp->aux_mutex); - if (0 != status) err_exit(status, "unlock aux_mutex"); } - status = pthread_mutex_lock(&clp->out_mutex); - if (0 != status) err_exit(status, "lock out_mutex"); clp->out_rem_count -= rep->num_blks; status = pthread_mutex_unlock(&clp->out_mutex); if (0 != status) err_exit(status, "unlock out_mutex"); @@ -1082,7 +1214,7 @@ sg_out_operation(Gbl_coll * clp, Rq_elem * rep) guarded_stop_both(clp); return; } - } + } /* end of while (1) loop */ } static int @@ -1092,14 +1224,16 @@ sg_start_io(Rq_elem * rep) bool fua = wr ? rep->out_flags.fua : rep->in_flags.fua; bool dpo = wr ? rep->out_flags.dpo : rep->in_flags.dpo; bool dio = wr ? rep->out_flags.dio : rep->in_flags.dio; + bool mmap = wr ? rep->out_flags.mmap : rep->in_flags.mmap; int cdbsz = wr ? rep->cdbsz_out : rep->cdbsz_in; - int res; + int res, err; struct sg_io_hdr * hp = &rep->io_hdr; const char * cp = ""; const char * c2p = ""; + const char * c3p = ""; if (sg_build_scsi_cdb(rep->cmd, cdbsz, rep->num_blks, rep->blk, - rep->wr, fua, dpo)) { + wr, fua, dpo)) { pr2serr_lk("%sbad cdb build, start_blk=%" PRId64 ", blocks=%d\n", my_name, rep->blk, rep->num_blks); return -1; @@ -1116,6 +1250,10 @@ sg_start_io(Rq_elem * rep) hp->timeout = DEF_TIMEOUT; hp->usr_ptr = rep; hp->pack_id = (int)rep->blk; + if (mmap && (rep->out2fd >= 0)) { + hp->flags |= SG_FLAG_MMAP_IO; + c3p = " mmap"; + } if (dio) hp->flags |= SG_FLAG_DIRECT_IO; if (rep->has_share) { @@ -1131,20 +1269,22 @@ sg_start_io(Rq_elem * rep) } else cp = (wr ? " slave not sharing" : " master not sharing"); if (rep->debug > 3) { - pr2serr_lk("%s tid=%d: SCSI %s%s%s, blk=%" PRId64 " num_blks=%d\n", + pr2serr_lk("%s tid=%d: SCSI %s%s%s%s, blk=%" PRId64 " num_blks=%d\n", __func__, rep->id, (rep->wr ? "WRITE" : "READ"), cp, - c2p, rep->blk, rep->num_blks); - sg_print_command(hp->cmdp); + c2p, c3p, rep->blk, rep->num_blks); + lk_print_command(hp->cmdp); } while (((res = write(rep->wr ? rep->outfd : rep->infd, hp, sizeof(struct sg_io_hdr))) < 0) && ((EINTR == errno) || (EAGAIN == errno))) ; + err = errno; if (res < 0) { - if (ENOMEM == errno) + if (ENOMEM == err) return 1; - perror("starting io on sg device, error"); + pr2serr_lk("%s tid=%d: %s%s%s write(2) failed: %s\n", __func__, + rep->id, cp, c2p, c3p, strerror(err)); return -1; } return 0; @@ -1154,9 +1294,9 @@ sg_start_io(Rq_elem * rep) -> try again, SG_LIB_CAT_NOT_READY, SG_LIB_CAT_MEDIUM_HARD, -1 other errors */ static int -sg_finish_io(bool wr, Rq_elem * rep, pthread_mutex_t * a_mutp) +sg_finish_io(bool wr, Rq_elem * rep) { - int res, err, status; + int res; struct sg_io_hdr io_hdr; struct sg_io_hdr * hp; #if 0 @@ -1187,13 +1327,13 @@ sg_finish_io(bool wr, Rq_elem * rep, pthread_mutex_t * a_mutp) case SG_LIB_CAT_CLEAN: break; case SG_LIB_CAT_RECOVERED: - sg_chk_n_print3((wr ? "writing continuing": + lk_chk_n_print3((wr ? "writing continuing": "reading continuing"), hp, false); break; case SG_LIB_CAT_ABORTED_COMMAND: case SG_LIB_CAT_UNIT_ATTENTION: if (rep->debug > 3) - sg_chk_n_print3((wr ? "writing": "reading"), hp, false); + lk_chk_n_print3((wr ? "writing": "reading"), hp, false); return res; case SG_LIB_CAT_NOT_READY: default: @@ -1202,11 +1342,7 @@ sg_finish_io(bool wr, Rq_elem * rep, pthread_mutex_t * a_mutp) snprintf(ebuff, EBUFF_SZ, "%s blk=%" PRId64, wr ? "writing": "reading", rep->blk); - status = pthread_mutex_lock(a_mutp); - if (0 != status) err_exit(status, "lock aux_mutex"); - sg_chk_n_print3(ebuff, hp, false); - status = pthread_mutex_unlock(a_mutp); - if (0 != status) err_exit(status, "unlock aux_mutex"); + lk_chk_n_print3(ebuff, hp, false); return res; } } @@ -1219,45 +1355,47 @@ sg_finish_io(bool wr, Rq_elem * rep, pthread_mutex_t * a_mutp) else rep->dio_incomplete_count = 0; rep->resid = hp->resid; - if ((! wr) && (rep->out2fd >= 0)) { - status = pthread_mutex_lock(a_mutp); - if (0 != status) err_exit(status, "lock aux_mutex"); - res = write(rep->out2fd, rep->buffp, rep->bs * rep->num_blks); - err = errno; - status = pthread_mutex_unlock(a_mutp); - if (0 != status) err_exit(status, "unlock aux_mutex"); - if (res < 0) - pr2serr_lk("%s: tid=%d: write(out2fd) failed: %s\n", __func__, - rep->id, strerror(err)); - } if (rep->debug > 3) pr2serr_lk("%s: tid=%d: completed %s\n", __func__, rep->id, wr ? "WRITE" : "READ"); return 0; } +/* Returns reserved_buffer_size/mmap_size if success, else 0 for failure */ static int -sg_prepare(int fd, int bs, int bpt) +sg_prepare(int fd, int bs, int bpt, bool def_res, uint8_t **mmpp) { - int res, t; + int res, t, num; + uint8_t *mmp; res = ioctl(fd, SG_GET_VERSION_NUM, &t); if ((res < 0) || (t < 30902)) { pr2serr_lk("%ssg driver prior to 3.9.02\n", my_name); - return 1; + return 0; + } + if (! def_res) { + num = bs * bpt; + res = ioctl(fd, SG_SET_RESERVED_SIZE, &num); + if (res < 0) + perror("sgs_dd: SG_SET_RESERVED_SIZE error"); + else if (mmpp) { + mmp = (uint8_t *)mmap(NULL, num, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + if (MAP_FAILED == mmp) { + perror("error using mmap()"); + return 0; + } + *mmpp = mmp; + } } - t = bs * bpt; - res = ioctl(fd, SG_SET_RESERVED_SIZE, &t); - if (res < 0) - perror("sgs_dd: SG_SET_RESERVED_SIZE error"); t = 1; res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t); if (res < 0) perror("sgs_dd: SG_SET_FORCE_PACK_ID error"); - return 0; + return (res < 0) ? 0 : num; } -static int +static bool process_flags(const char * arg, struct flags_t * fp) { char buff[256]; @@ -1268,17 +1406,21 @@ process_flags(const char * arg, struct flags_t * fp) buff[sizeof(buff) - 1] = '\0'; if ('\0' == buff[0]) { pr2serr("no flag found\n"); - return 1; + return false; } cp = buff; do { np = strchr(cp, ','); if (np) *np++ = '\0'; - if (0 == strcmp(cp, "append")) + if (0 == strcmp(cp, "2fds")) + fp->fds2 = true; + else if (0 == strcmp(cp, "append")) fp->append = true; else if (0 == strcmp(cp, "coe")) fp->coe = true; + else if (0 == strcmp(cp, "defres")) + fp->defres = true; else if (0 == strcmp(cp, "dio")) fp->dio = true; else if (0 == strcmp(cp, "direct")) @@ -1291,17 +1433,19 @@ process_flags(const char * arg, struct flags_t * fp) fp->excl = true; else if (0 == strcmp(cp, "fua")) fp->fua = true; + else if (0 == strcmp(cp, "mmap")) + fp->mmap = true; else if (0 == strcmp(cp, "noshare")) fp->noshare = true; else if (0 == strcmp(cp, "null")) ; else { pr2serr("unrecognised flag: %s\n", cp); - return 1; + return false; } cp = np; } while (cp); - return 0; + return true; } /* Returns the number of times 'ch' is found in string 's' given the @@ -1319,9 +1463,9 @@ num_chs_in_str(const char * s, int slen, int ch) } static int -sg_in_open(Gbl_coll *clp, const char *inf) +sg_in_open(Gbl_coll *clp, const char *inf, uint8_t **mmpp, int * mmap_lenp) { - int fd, err; + int fd, err, n; int flags = O_RDWR; char ebuff[EBUFF_SZ]; @@ -1339,15 +1483,18 @@ sg_in_open(Gbl_coll *clp, const char *inf) perror(ebuff); return -sg_convert_errno(err);; } - if (sg_prepare(fd, clp->bs, clp->bpt)) + n = sg_prepare(fd, clp->bs, clp->bpt, clp->in_flags.defres, mmpp); + if (n <= 0) return -SG_LIB_FILE_ERROR; + if (mmap_lenp) + *mmap_lenp = n; return fd; } static int -sg_out_open(Gbl_coll *clp, const char *outf) +sg_out_open(Gbl_coll *clp, const char *outf, uint8_t **mmpp, int * mmap_lenp) { - int fd, err; + int fd, err, n; int flags = O_RDWR; char ebuff[EBUFF_SZ]; @@ -1365,11 +1512,11 @@ sg_out_open(Gbl_coll *clp, const char *outf) perror(ebuff); return -sg_convert_errno(err); } - if (sg_prepare(fd, clp->bs, clp->bpt)) + n = sg_prepare(fd, clp->bs, clp->bpt, clp->out_flags.defres, mmpp); + if (n <= 0) return -SG_LIB_FILE_ERROR; - else if (gcoll.debug > 1) - pr2serr("Sharing writer's fd (%d) with reader (fd=%d) " - "successful\n", clp->infd, fd); + if (mmap_lenp) + *mmap_lenp = n; return fd; } @@ -1398,7 +1545,8 @@ main(int argc, char * argv[]) int64_t out_num_sect = 0; int in_sect_sz, out_sect_sz, status, n, flags; void * vp; - pthread_t threads[MAX_NUM_THREADS]; + Gbl_coll * clp = &gcoll; + Thread_info thread_arr[MAX_NUM_THREADS]; char ebuff[EBUFF_SZ]; #if SG_LIB_ANDROID struct sigaction actions; @@ -1409,12 +1557,13 @@ main(int argc, char * argv[]) actions.sa_handler = thread_exit_handler; sigaction(SIGUSR1, &actions, NULL); #endif - memset(&gcoll, 0, sizeof(Gbl_coll)); - gcoll.bpt = DEF_BLOCKS_PER_TRANSFER; - gcoll.in_type = FT_OTHER; - gcoll.out_type = FT_OTHER; - gcoll.cdbsz_in = DEF_SCSI_CDBSZ; - gcoll.cdbsz_out = DEF_SCSI_CDBSZ; + memset(clp, 0, sizeof(*clp)); + memset(thread_arr, 0, sizeof(thread_arr)); + clp->bpt = DEF_BLOCKS_PER_TRANSFER; + clp->in_type = FT_OTHER; + clp->out_type = FT_OTHER; + clp->cdbsz_in = DEF_SCSI_CDBSZ; + clp->cdbsz_out = DEF_SCSI_CDBSZ; inf[0] = '\0'; outf[0] = '\0'; out2f[0] = '\0'; @@ -1432,25 +1581,25 @@ main(int argc, char * argv[]) *buf++ = '\0'; keylen = strlen(key); if (0 == strcmp(key,"bpt")) { - gcoll.bpt = sg_get_num(buf); - if (-1 == gcoll.bpt) { + clp->bpt = sg_get_num(buf); + if (-1 == clp->bpt) { pr2serr("%sbad argument to 'bpt='\n", my_name); return SG_LIB_SYNTAX_ERROR; } bpt_given = 1; } else if (0 == strcmp(key,"bs")) { - gcoll.bs = sg_get_num(buf); - if (-1 == gcoll.bs) { + clp->bs = sg_get_num(buf); + if (-1 == clp->bs) { pr2serr("%sbad argument to 'bs='\n", my_name); return SG_LIB_SYNTAX_ERROR; } } else if (0 == strcmp(key,"cdbsz")) { - gcoll.cdbsz_in = sg_get_num(buf); - gcoll.cdbsz_out = gcoll.cdbsz_in; + clp->cdbsz_in = sg_get_num(buf); + clp->cdbsz_out = clp->cdbsz_in; cdbsz_given = 1; } else if (0 == strcmp(key,"coe")) { - gcoll.in_flags.coe = !! sg_get_num(buf); - gcoll.out_flags.coe = gcoll.in_flags.coe; + clp->in_flags.coe = !! sg_get_num(buf); + clp->out_flags.coe = clp->in_flags.coe; } else if (0 == strcmp(key,"count")) { if (0 != strcmp("-1", buf)) { dd_count = sg_get_llnum(buf); @@ -1461,16 +1610,16 @@ main(int argc, char * argv[]) } /* treat 'count=-1' as calculate count (same as not given) */ } else if ((0 == strncmp(key,"deb", 3)) || (0 == strncmp(key,"verb", 4))) - gcoll.debug = sg_get_num(buf); + clp->debug = sg_get_num(buf); else if (0 == strcmp(key,"dio")) { - gcoll.in_flags.dio = !! sg_get_num(buf); - gcoll.out_flags.dio = gcoll.in_flags.dio; + clp->in_flags.dio = !! sg_get_num(buf); + clp->out_flags.dio = clp->in_flags.dio; } else if (0 == strcmp(key,"fua")) { n = sg_get_num(buf); if (n & 1) - gcoll.out_flags.fua = true; + clp->out_flags.fua = true; if (n & 2) - gcoll.in_flags.fua = true; + clp->in_flags.fua = true; } else if (0 == strcmp(key,"ibs")) { ibs = sg_get_num(buf); if (-1 == ibs) { @@ -1484,7 +1633,7 @@ main(int argc, char * argv[]) } else snprintf(inf, INOUTF_SZ, "%s", buf); } else if (0 == strcmp(key, "iflag")) { - if (process_flags(buf, &gcoll.in_flags)) { + if (! process_flags(buf, &clp->in_flags)) { pr2serr("%sbad argument to 'iflag='\n", my_name); return SG_LIB_SYNTAX_ERROR; } @@ -1507,7 +1656,7 @@ main(int argc, char * argv[]) } else snprintf(outf, INOUTF_SZ, "%s", buf); } else if (0 == strcmp(key, "oflag")) { - if (process_flags(buf, &gcoll.out_flags)) { + if (! process_flags(buf, &clp->out_flags)) { pr2serr("%sbad argument to 'oflag='\n", my_name); return SG_LIB_SYNTAX_ERROR; } @@ -1532,15 +1681,15 @@ main(int argc, char * argv[]) else if ((keylen > 1) && ('-' == key[0]) && ('-' != key[1])) { res = 0; n = num_chs_in_str(key + 1, keylen - 1, 'd'); - gcoll.dry_run += n; + clp->dry_run += n; res += n; n = num_chs_in_str(key + 1, keylen - 1, 'h'); - gcoll.help += n; + clp->help += n; res += n; n = num_chs_in_str(key + 1, keylen - 1, 'v'); if (n > 0) verbose_given = true; - gcoll.debug += n; /* -v ---> --verbose */ + clp->debug += n; /* -v ---> --verbose */ res += n; n = num_chs_in_str(key + 1, keylen - 1, 'V'); if (n > 0) @@ -1554,13 +1703,13 @@ main(int argc, char * argv[]) } } else if ((0 == strncmp(key, "--dry-run", 9)) || (0 == strncmp(key, "--dry_run", 9))) - ++gcoll.dry_run; + ++clp->dry_run; else if ((0 == strncmp(key, "--help", 6)) || (0 == strcmp(key, "-?"))) - ++gcoll.help; + ++clp->help; else if (0 == strncmp(key, "--verb", 6)) { verbose_given = true; - ++gcoll.debug; /* --verbose */ + ++clp->debug; /* --verbose */ } else if (0 == strncmp(key, "--vers", 6)) version_given = true; else { @@ -1576,12 +1725,12 @@ main(int argc, char * argv[]) pr2serr("but override: '-vV' given, zero verbose and continue\n"); verbose_given = false; version_given = false; - gcoll.debug = 0; + clp->debug = 0; } else if (! verbose_given) { pr2serr("set '-vv'\n"); - gcoll.debug = 2; + clp->debug = 2; } else - pr2serr("keep verbose=%d\n", gcoll.debug); + pr2serr("keep verbose=%d\n", clp->debug); #else if (verbose_given && version_given) pr2serr("Not in DEBUG mode, so '-vV' has no special action\n"); @@ -1590,18 +1739,15 @@ main(int argc, char * argv[]) pr2serr("%s%s\n", my_name, version_str); return 0; } - if (gcoll.help > 0) { - if (1 == gcoll.help) - usage(0); - else - usage(1); + if (clp->help > 0) { + usage(clp->help); return 0; } - if (gcoll.bs <= 0) { - gcoll.bs = DEF_BLOCK_SIZE; - pr2serr("Assume default 'bs' (block size) of %d bytes\n", gcoll.bs); + if (clp->bs <= 0) { + clp->bs = DEF_BLOCK_SIZE; + pr2serr("Assume default 'bs' (block size) of %d bytes\n", clp->bs); } - if ((ibs && (ibs != gcoll.bs)) || (obs && (obs != gcoll.bs))) { + if ((ibs && (ibs != clp->bs)) || (obs && (obs != clp->bs))) { pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n"); usage(0); return SG_LIB_SYNTAX_ERROR; @@ -1610,25 +1756,39 @@ main(int argc, char * argv[]) pr2serr("skip and seek cannot be negative\n"); return SG_LIB_SYNTAX_ERROR; } - if (gcoll.out_flags.append && (seek > 0)) { + if (clp->out_flags.append && (seek > 0)) { pr2serr("Can't use both append and seek switches\n"); return SG_LIB_SYNTAX_ERROR; } - if (gcoll.bpt < 1) { + if (clp->bpt < 1) { pr2serr("bpt must be greater than 0\n"); return SG_LIB_SYNTAX_ERROR; } + if (clp->in_flags.mmap && clp->out_flags.mmap) { + pr2serr("mmap flag on both IFILE and OFILE doesn't work\n"); + return SG_LIB_SYNTAX_ERROR; + } + if (clp->out_flags.mmap && !(clp->in_flags.noshare || + clp->out_flags.noshare)) { + pr2serr("oflag=mmap needs either iflag=noshare or oflag=noshare\n"); + return SG_LIB_SYNTAX_ERROR; + } + if ((clp->in_flags.mmap || clp->out_flags.mmap) && + (clp->in_flags.fds2 || clp->in_flags.fds2)) { + pr2serr("can't have both 'mmap' and '2fds' flags\n"); + return SG_LIB_SYNTAX_ERROR; + } /* defaulting transfer size to 128*2048 for CD/DVDs is too large for the block layer in lk 2.6 and results in an EIO on the SG_IO ioctl. So reduce it in that case. */ - if ((gcoll.bs >= 2048) && (0 == bpt_given)) - gcoll.bpt = DEF_BLOCKS_PER_2048TRANSFER; + if ((clp->bs >= 2048) && (0 == bpt_given)) + clp->bpt = DEF_BLOCKS_PER_2048TRANSFER; if ((num_threads < 1) || (num_threads > MAX_NUM_THREADS)) { pr2serr("too few or too many threads requested\n"); usage(1); return SG_LIB_SYNTAX_ERROR; } - if (gcoll.debug) + if (clp->debug) pr2serr("%sif=%s skip=%" PRId64 " of=%s seek=%" PRId64 " count=%" PRId64 "\n", my_name, inf, skip, outf, seek, dd_count); @@ -1637,42 +1797,41 @@ main(int argc, char * argv[]) install_handler(SIGPIPE, interrupt_handler); install_handler(SIGUSR1, siginfo_handler); - gcoll.infd = STDIN_FILENO; - gcoll.outfd = STDOUT_FILENO; + clp->infd = STDIN_FILENO; + clp->outfd = STDOUT_FILENO; if (inf[0] && ('-' != inf[0])) { - gcoll.in_type = dd_filetype(inf); + clp->in_type = dd_filetype(inf); - if (FT_ERROR == gcoll.in_type) { + if (FT_ERROR == clp->in_type) { pr2serr("%sunable to access %s\n", my_name, inf); return SG_LIB_FILE_ERROR; - } else if (FT_ST == gcoll.in_type) { + } else if (FT_ST == clp->in_type) { pr2serr("%sunable to use scsi tape device %s\n", my_name, inf); return SG_LIB_FILE_ERROR; - } else if (FT_SG == gcoll.in_type) { - gcoll.infd = sg_in_open(&gcoll, inf); - if (gcoll.infd < 0) - return -gcoll.infd; + } else if (FT_SG == clp->in_type) { + clp->infd = sg_in_open(clp, inf, NULL, NULL); + if (clp->infd < 0) + return -clp->infd; } else { flags = O_RDONLY; - if (gcoll.in_flags.direct) + if (clp->in_flags.direct) flags |= O_DIRECT; - if (gcoll.in_flags.excl) + if (clp->in_flags.excl) flags |= O_EXCL; - if (gcoll.in_flags.dsync) + if (clp->in_flags.dsync) flags |= O_SYNC; - if ((gcoll.infd = open(inf, flags)) < 0) { + if ((clp->infd = open(inf, flags)) < 0) { err = errno; snprintf(ebuff, EBUFF_SZ, "%scould not open %s for reading", my_name, inf); perror(ebuff); return sg_convert_errno(err); - } - else if (skip > 0) { + } else if (skip > 0) { off64_t offset = skip; - offset *= gcoll.bs; /* could exceed 32 here! */ - if (lseek64(gcoll.infd, offset, SEEK_SET) < 0) { + offset *= clp->bs; /* could exceed 32 here! */ + if (lseek64(clp->infd, offset, SEEK_SET) < 0) { err = errno; snprintf(ebuff, EBUFF_SZ, "%scouldn't skip to required " "position on %s", my_name, inf); @@ -1681,34 +1840,35 @@ main(int argc, char * argv[]) } } } + clp->infp = inf; } if (outf[0] && ('-' != outf[0])) { - gcoll.out_type = dd_filetype(outf); + clp->out_type = dd_filetype(outf); - if (FT_ST == gcoll.out_type) { + if (FT_ST == clp->out_type) { pr2serr("%sunable to use scsi tape device %s\n", my_name, outf); return SG_LIB_FILE_ERROR; } - else if (FT_SG == gcoll.out_type) { - gcoll.outfd = sg_out_open(&gcoll, outf); - if (gcoll.outfd < 0) - return -gcoll.outfd; + else if (FT_SG == clp->out_type) { + clp->outfd = sg_out_open(clp, outf, NULL, NULL); + if (clp->outfd < 0) + return -clp->outfd; } - else if (FT_DEV_NULL == gcoll.out_type) - gcoll.outfd = -1; /* don't bother opening */ + else if (FT_DEV_NULL == clp->out_type) + clp->outfd = -1; /* don't bother opening */ else { - if (FT_RAW != gcoll.out_type) { + if (FT_RAW != clp->out_type) { flags = O_WRONLY | O_CREAT; - if (gcoll.out_flags.direct) + if (clp->out_flags.direct) flags |= O_DIRECT; - if (gcoll.out_flags.excl) + if (clp->out_flags.excl) flags |= O_EXCL; - if (gcoll.out_flags.dsync) + if (clp->out_flags.dsync) flags |= O_SYNC; - if (gcoll.out_flags.append) + if (clp->out_flags.append) flags |= O_APPEND; - if ((gcoll.outfd = open(outf, flags, 0666)) < 0) { + if ((clp->outfd = open(outf, flags, 0666)) < 0) { err = errno; snprintf(ebuff, EBUFF_SZ, "%scould not open %s for " "writing", my_name, outf); @@ -1717,7 +1877,7 @@ main(int argc, char * argv[]) } } else { /* raw output file */ - if ((gcoll.outfd = open(outf, O_WRONLY)) < 0) { + if ((clp->outfd = open(outf, O_WRONLY)) < 0) { err = errno; snprintf(ebuff, EBUFF_SZ, "%scould not open %s for raw " "writing", my_name, outf); @@ -1728,8 +1888,8 @@ main(int argc, char * argv[]) if (seek > 0) { off64_t offset = seek; - offset *= gcoll.bs; /* could exceed 32 bits here! */ - if (lseek64(gcoll.outfd, offset, SEEK_SET) < 0) { + offset *= clp->bs; /* could exceed 32 bits here! */ + if (lseek64(clp->outfd, offset, SEEK_SET) < 0) { err = errno; snprintf(ebuff, EBUFF_SZ, "%scouldn't seek to required " "position on %s", my_name, outf); @@ -1738,30 +1898,31 @@ main(int argc, char * argv[]) } } } + clp->outfp = outf; } if (out2f[0]) { - gcoll.out2_type = dd_filetype(out2f); - if ((gcoll.out2fd = open(out2f, O_WRONLY | O_CREAT, 0666)) < 0) { + clp->out2_type = dd_filetype(out2f); + if ((clp->out2fd = open(out2f, O_WRONLY | O_CREAT, 0666)) < 0) { err = errno; snprintf(ebuff, EBUFF_SZ, "could not open %s for writing", out2f); perror(ebuff); return sg_convert_errno(err); } } else - gcoll.out2fd = -1; + clp->out2fd = -1; - if ((STDIN_FILENO == gcoll.infd) && (STDOUT_FILENO == gcoll.outfd)) { + if ((STDIN_FILENO == clp->infd) && (STDOUT_FILENO == clp->outfd)) { pr2serr("Won't default both IFILE to stdin _and_ OFILE to stdout\n"); pr2serr("For more information use '--help'\n"); return SG_LIB_SYNTAX_ERROR; } if (dd_count < 0) { in_num_sect = -1; - if (FT_SG == gcoll.in_type) { - res = scsi_read_capacity(gcoll.infd, &in_num_sect, &in_sect_sz); + if (FT_SG == clp->in_type) { + res = scsi_read_capacity(clp->infd, &in_num_sect, &in_sect_sz); if (2 == res) { pr2serr("Unit attention, media changed(in), continuing\n"); - res = scsi_read_capacity(gcoll.infd, &in_num_sect, + res = scsi_read_capacity(clp->infd, &in_num_sect, &in_sect_sz); } if (0 != res) { @@ -1773,15 +1934,15 @@ main(int argc, char * argv[]) pr2serr("Unable to read capacity on %s\n", inf); in_num_sect = -1; } - } else if (FT_BLOCK == gcoll.in_type) { - if (0 != read_blkdev_capacity(gcoll.infd, &in_num_sect, + } else if (FT_BLOCK == clp->in_type) { + if (0 != read_blkdev_capacity(clp->infd, &in_num_sect, &in_sect_sz)) { pr2serr("Unable to read block capacity on %s\n", inf); in_num_sect = -1; } - if (gcoll.bs != in_sect_sz) { + if (clp->bs != in_sect_sz) { pr2serr("block size on %s confusion; bs=%d, from device=%d\n", - inf, gcoll.bs, in_sect_sz); + inf, clp->bs, in_sect_sz); in_num_sect = -1; } } @@ -1789,11 +1950,11 @@ main(int argc, char * argv[]) in_num_sect -= skip; out_num_sect = -1; - if (FT_SG == gcoll.out_type) { - res = scsi_read_capacity(gcoll.outfd, &out_num_sect, &out_sect_sz); + if (FT_SG == clp->out_type) { + res = scsi_read_capacity(clp->outfd, &out_num_sect, &out_sect_sz); if (2 == res) { pr2serr("Unit attention, media changed(out), continuing\n"); - res = scsi_read_capacity(gcoll.outfd, &out_num_sect, + res = scsi_read_capacity(clp->outfd, &out_num_sect, &out_sect_sz); } if (0 != res) { @@ -1805,15 +1966,15 @@ main(int argc, char * argv[]) pr2serr("Unable to read capacity on %s\n", outf); out_num_sect = -1; } - } else if (FT_BLOCK == gcoll.out_type) { - if (0 != read_blkdev_capacity(gcoll.outfd, &out_num_sect, + } else if (FT_BLOCK == clp->out_type) { + if (0 != read_blkdev_capacity(clp->outfd, &out_num_sect, &out_sect_sz)) { pr2serr("Unable to read block capacity on %s\n", outf); out_num_sect = -1; } - if (gcoll.bs != out_sect_sz) { + if (clp->bs != out_sect_sz) { pr2serr("block size on %s confusion: bs=%d, from device=%d\n", - outf, gcoll.bs, out_sect_sz); + outf, clp->bs, out_sect_sz); out_num_sect = -1; } } @@ -1830,7 +1991,7 @@ main(int argc, char * argv[]) else dd_count = out_num_sect; } - if (gcoll.debug > 2) + if (clp->debug > 2) pr2serr("Start of loop, count=%" PRId64 ", in_num_sect=%" PRId64 ", out_num_sect=%" PRId64 "\n", dd_count, in_num_sect, out_num_sect); @@ -1839,38 +2000,36 @@ main(int argc, char * argv[]) return SG_LIB_CAT_OTHER; } if (! cdbsz_given) { - if ((FT_SG == gcoll.in_type) && (MAX_SCSI_CDBSZ != gcoll.cdbsz_in) && - (((dd_count + skip) > UINT_MAX) || (gcoll.bpt > USHRT_MAX))) { + if ((FT_SG == clp->in_type) && (MAX_SCSI_CDBSZ != clp->cdbsz_in) && + (((dd_count + skip) > UINT_MAX) || (clp->bpt > USHRT_MAX))) { pr2serr("Note: SCSI command size increased to 16 bytes (for " "'if')\n"); - gcoll.cdbsz_in = MAX_SCSI_CDBSZ; + clp->cdbsz_in = MAX_SCSI_CDBSZ; } - if ((FT_SG == gcoll.out_type) && (MAX_SCSI_CDBSZ != gcoll.cdbsz_out) && - (((dd_count + seek) > UINT_MAX) || (gcoll.bpt > USHRT_MAX))) { + if ((FT_SG == clp->out_type) && (MAX_SCSI_CDBSZ != clp->cdbsz_out) && + (((dd_count + seek) > UINT_MAX) || (clp->bpt > USHRT_MAX))) { pr2serr("Note: SCSI command size increased to 16 bytes (for " "'of')\n"); - gcoll.cdbsz_out = MAX_SCSI_CDBSZ; + clp->cdbsz_out = MAX_SCSI_CDBSZ; } } - gcoll.in_count = dd_count; - gcoll.in_rem_count = dd_count; - gcoll.skip = skip; - gcoll.in_blk = skip; - gcoll.out_count = dd_count; - gcoll.out_rem_count = dd_count; - gcoll.seek = seek; - gcoll.out_blk = seek; - status = pthread_mutex_init(&gcoll.in_mutex, NULL); + clp->in_count = dd_count; + clp->in_rem_count = dd_count; + clp->skip = skip; + clp->in_blk = skip; + clp->out_count = dd_count; + clp->out_rem_count = dd_count; + clp->seek = seek; + clp->out_blk = seek; + status = pthread_mutex_init(&clp->in_mutex, NULL); if (0 != status) err_exit(status, "init in_mutex"); - status = pthread_mutex_init(&gcoll.out_mutex, NULL); + status = pthread_mutex_init(&clp->out_mutex, NULL); if (0 != status) err_exit(status, "init out_mutex"); - status = pthread_mutex_init(&gcoll.aux_mutex, NULL); - if (0 != status) err_exit(status, "init aux_mutex"); - status = pthread_cond_init(&gcoll.out_sync_cv, NULL); + status = pthread_cond_init(&clp->out_sync_cv, NULL); if (0 != status) err_exit(status, "init out_sync_cv"); - if (gcoll.dry_run > 0) { + if (clp->dry_run > 0) { pr2serr("Due to --dry-run option, bypass copy/read\n"); goto fini; } @@ -1879,7 +2038,7 @@ main(int argc, char * argv[]) status = pthread_sigmask(SIG_BLOCK, &signal_set, NULL); if (0 != status) err_exit(status, "pthread_sigmask"); status = pthread_create(&sig_listen_thread_id, NULL, - sig_listen_thread, (void *)&gcoll); + sig_listen_thread, (void *)clp); if (0 != status) err_exit(status, "pthread_create, sig..."); if (do_time) { @@ -1889,46 +2048,47 @@ main(int argc, char * argv[]) } /* vvvvvvvvvvv Start worker threads vvvvvvvvvvvvvvvvvvvvvvvv */ - if ((gcoll.out_rem_count > 0) && (num_threads > 0)) { - Thread_info ti; + if ((clp->out_rem_count > 0) && (num_threads > 0)) { + Thread_info *tip = thread_arr + 0; - ti.gcp = &gcoll; - ti.id = 0; + tip->gcp = clp; + tip->id = 0; /* Run 1 work thread to shake down infant retryable stuff */ - status = pthread_mutex_lock(&gcoll.out_mutex); + status = pthread_mutex_lock(&clp->out_mutex); if (0 != status) err_exit(status, "lock out_mutex"); - status = pthread_create(&threads[0], NULL, read_write_thread, - (void *)&ti); + status = pthread_create(&tip->a_pthr, NULL, read_write_thread, + (void *)tip); if (0 != status) err_exit(status, "pthread_create"); - if (gcoll.debug > 1) - pr2serr_lk("Starting worker thread k=0\n"); + if (clp->debug > 1) + pr2serr_lk("Starting worker thread k=0 alone\n"); - /* wait for any broadcast */ - pthread_cleanup_push(cleanup_out, (void *)&gcoll); - status = pthread_cond_wait(&gcoll.out_sync_cv, &gcoll.out_mutex); - if (0 != status) err_exit(status, "cond out_sync_cv"); + /* wait for a broadcast on hold_1st_cv, implies tid=0 thread has + * completed one READ/WRITE cycle (segment). */ + pthread_cleanup_push(cleanup_out, (void *)clp); + status = pthread_cond_wait(&clp->hold_1st_cv, &clp->out_mutex); + if (0 != status) err_exit(status, "cond hold_1st_cv"); pthread_cleanup_pop(0); - status = pthread_mutex_unlock(&gcoll.out_mutex); + status = pthread_mutex_unlock(&clp->out_mutex); if (0 != status) err_exit(status, "unlock out_mutex"); /* now start the rest of the threads */ for (k = 1; k < num_threads; ++k) { - Thread_info tinfo; - - tinfo.gcp = &gcoll; - tinfo.id = k; - status = pthread_create(&threads[k], NULL, read_write_thread, - (void *)&tinfo); + tip = thread_arr + k; + tip->gcp = clp; + tip->id = k; + status = pthread_create(&tip->a_pthr, NULL, read_write_thread, + (void *)tip); if (0 != status) err_exit(status, "pthread_create"); - if (gcoll.debug > 1) + if (clp->debug > 1) pr2serr_lk("Starting worker thread k=%d\n", k); } /* now wait for worker threads to finish */ for (k = 0; k < num_threads; ++k) { - status = pthread_join(threads[k], &vp); + tip = thread_arr + k; + status = pthread_join(tip->a_pthr, &vp); if (0 != status) err_exit(status, "pthread_join"); - if (gcoll.debug > 0) + if (clp->debug > 0) pr2serr_lk("Worker thread k=%d terminated\n", k); } } /* started worker threads and here after they have all exited */ @@ -1937,12 +2097,12 @@ main(int argc, char * argv[]) calc_duration_throughput(0); if (do_sync) { - if (FT_SG == gcoll.out_type) { + if (FT_SG == clp->out_type) { pr2serr_lk(">> Synchronizing cache on %s\n", outf); - res = sg_ll_sync_cache_10(gcoll.outfd, 0, 0, 0, 0, 0, false, 0); + res = sg_ll_sync_cache_10(clp->outfd, 0, 0, 0, 0, 0, false, 0); if (SG_LIB_CAT_UNIT_ATTENTION == res) { pr2serr_lk("Unit attention(out), continuing\n"); - res = sg_ll_sync_cache_10(gcoll.outfd, 0, 0, 0, 0, 0, false, + res = sg_ll_sync_cache_10(clp->outfd, 0, 0, 0, 0, 0, false, 0); } if (0 != res) @@ -1958,26 +2118,26 @@ main(int argc, char * argv[]) fini: - if (STDIN_FILENO != gcoll.infd) - close(gcoll.infd); - if ((STDOUT_FILENO != gcoll.outfd) && (FT_DEV_NULL != gcoll.out_type)) - close(gcoll.outfd); - if ((STDOUT_FILENO != gcoll.out2fd) && (FT_DEV_NULL != gcoll.out2_type)) - close(gcoll.out2fd); + if (STDIN_FILENO != clp->infd) + close(clp->infd); + if ((STDOUT_FILENO != clp->outfd) && (FT_DEV_NULL != clp->out_type)) + close(clp->outfd); + if ((STDOUT_FILENO != clp->out2fd) && (FT_DEV_NULL != clp->out2_type)) + close(clp->out2fd); res = exit_status; - if ((0 != gcoll.out_count) && (0 == gcoll.dry_run)) { + if ((0 != clp->out_count) && (0 == clp->dry_run)) { pr2serr(">>>> Some error occurred, remaining blocks=%" PRId64 "\n", - gcoll.out_count); + clp->out_count); if (0 == res) res = SG_LIB_CAT_OTHER; } print_stats(""); - if (gcoll.dio_incomplete_count) { + if (clp->dio_incomplete_count) { int fd; char c; pr2serr(">> Direct IO requested but incomplete %d times\n", - gcoll.dio_incomplete_count); + clp->dio_incomplete_count); if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) { if (1 == read(fd, &c, 1)) { if ('0' == c) @@ -1987,8 +2147,8 @@ fini: close(fd); } } - if (gcoll.sum_of_resids) + if (clp->sum_of_resids) pr2serr(">> Non-zero sum of residual counts=%d\n", - gcoll.sum_of_resids); + clp->sum_of_resids); return (res >= 0) ? res : SG_LIB_CAT_OTHER; } diff --git a/testing/tst_sg_lib.c b/testing/tst_sg_lib.c index 027e6fb2..ff59ebc7 100644 --- a/testing/tst_sg_lib.c +++ b/testing/tst_sg_lib.c @@ -3,6 +3,8 @@ * All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the BSD_LICENSE file. + * + * SPDX-License-Identifier: BSD-2-Clause */ #include <unistd.h> @@ -42,7 +44,7 @@ * related to snprintf(). */ -static const char * version_str = "1.11 20180715"; +static const char * version_str = "1.12 20181207"; #define MAX_LINE_LEN 1024 diff --git a/testing/uapi_sg.h b/testing/uapi_sg.h index 1485c2e8..38087a71 100644 --- a/testing/uapi_sg.h +++ b/testing/uapi_sg.h @@ -13,7 +13,7 @@ * Version 2 and 3 extensions to driver: * Copyright (C) 1998 - 2018 Douglas Gilbert * - * Version: 3.9.02 (20181118) + * Version: 3.9.02 (20181203) * This version is for Linux 2.6, 3 and 4 series kernels. * * Documentation @@ -105,7 +105,7 @@ typedef struct sg_io_hdr { */ #define SGV4_FLAG_DIRECT_IO SG_FLAG_DIRECT_IO #define SGV4_FLAG_MMAP_IO SG_FLAG_MMAP_IO -#define SGV4_FLAG_SYNC 0x8 /* similar to ioctl(SG_IO) */ +#define SGV4_FLAG_V3_MAP 0x8 /* used internally */ #define SGV4_FLAG_Q_AT_TAIL SG_FLAG_Q_AT_TAIL #define SGV4_FLAG_Q_AT_HEAD SG_FLAG_Q_AT_HEAD /* Following 2 flags for request::tag (type: int) manipulations */ |