aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG14
-rw-r--r--Makefile7
-rw-r--r--README16
-rw-r--r--archive/Makefile5
-rw-r--r--archive/README4
-rw-r--r--archive/sg_simple_andre.c33
-rw-r--r--archive/sgq_dd.c94
-rw-r--r--archive/sgs_dd.c45
-rw-r--r--scsi_devfs_scan.c27
-rw-r--r--sg_dd.844
-rw-r--r--sg_dd.c269
-rw-r--r--sg_debug.c10
-rw-r--r--sg_inq.c17
-rw-r--r--sg_map.c29
-rw-r--r--sg_read.8108
-rw-r--r--sg_read.c168
-rw-r--r--sg_readcap.c6
-rw-r--r--sg_reset.c (renamed from archive/sg_reset.c)68
-rw-r--r--sg_scan.c24
-rw-r--r--sg_simple1.c9
-rw-r--r--sg_simple16.c17
-rw-r--r--sg_simple2.c7
-rw-r--r--sg_simple3.c9
-rw-r--r--sg_simple4.c17
-rw-r--r--sg_turs.c12
-rw-r--r--sginfo.c69
-rw-r--r--sgm_dd.8121
-rw-r--r--sgm_dd.c253
-rw-r--r--sgp_dd.818
-rw-r--r--sgp_dd.c225
30 files changed, 1322 insertions, 423 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 07c65d5d..d1d7e737 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,12 +2,22 @@ Each utility has its own version number, date of last changes and
some description at the top of its ".c" file. "KG" indicates work by
Kurt Garloff <garloff@suse.de>.
+Changelog for sg3_utils-0.98 [20020216]
+----------------------------
+ - move sg_reset back from archive to main directory + build
+ - sprintf() to snprintf() changes
+ - add "time=<n>" argument to sg_dd, sgp_dd and sgm_dd to time transfer
+ - add man pages for sgm_dd and sg_read, update sg_dd and sgp_dd man pages
+ - add "cdbsz=" argument to sg_dd, sgp_dd, sgm_dd + sg_read
+ - add "gen=0|1" argument to sg_dd
+
Changelog for sg3_utils-0.97 [20011223]
- - move isosize to archive since introduced unto util-linux-2.10s
+----------------------------
+ - move isosize to archive since introduced into util-linux-2.10s
Changelog for sg3_utils-0.96 [20011221]
----------------------------
- - add '-p' switch to sg_inq to provode PCI slot_name
+ - add '-p' switch to sg_inq to provide PCI slot_name
- add '-n' switch to scsi_inquiry for non-blocking open
- new sgm_dd (dd variant) using mmap-ed IO
- sg_rbuf now has a '-m' argument to select mmap-ed IO
diff --git a/Makefile b/Makefile
index d4e44d16..5bce09e6 100644
--- a/Makefile
+++ b/Makefile
@@ -10,11 +10,11 @@ LD = gcc
EXECS = sg_simple1 sg_simple2 sg_simple3 sg_dd sg_debug \
sg_scan scsi_inquiry sg_rbuf sginfo sg_readcap \
sgp_dd sg_map sg_turs sg_inq sg_test_rwbuf scsi_devfs_scan \
- sg_start sgm_dd sg_simple4 sg_simple16 sg_read
+ sg_start sgm_dd sg_simple4 sg_simple16 sg_read sg_reset
COMMON = sg_scan scsi_inquiry sginfo sg_readcap sg_start
-MAN_PGS = sg_dd.8 sgp_dd.8 sg_map.8 sg_rbuf.8
+MAN_PGS = sg_dd.8 sgp_dd.8 sgm_dd.8 sg_read.8 sg_map.8 sg_rbuf.8
MAN_PREF = man8
CFLAGS = -g -O2 -Wall -D_REENTRANT
@@ -97,6 +97,9 @@ scsi_devfs_scan: scsi_devfs_scan.o sg_err.o
sg_read: sg_read.o sg_err.o llseek.o
$(LD) -o $@ $(LDFLAGS) $^
+sg_reset: sg_reset.o
+ $(LD) -o $@ $(LDFLAGS) $^
+
install: $(EXECS) $(COMMON)
install -d $(INSTDIR)
for name in $^; \
diff --git a/README b/README
index fda8e169..916d6206 100644
--- a/README
+++ b/README
@@ -72,6 +72,10 @@ Available dd options:
Extra options:
bpt=<n> blocks per transfer (default 128)
dio=<n> 0 or 1, request direct IO (default 0)
+ cdbsz=6|10|12|16 allow the command size of SCSI READ and WRITE commands
+ to be specified (default is 10)
+ gen=0|1 either "if" or "of" must be a sg or raw device when "gen=0"
+ (default). Set to 1 to remove this restriction
All numeric arguments can take multiplier suffixes:
"c", "C" * 1
@@ -111,7 +115,8 @@ signals are caught. This command has a "man" page [section 8].
"sgm_dd" is very similar to sg_dd but it uses mmap-ed IO on one of its
given sg device names. If both "if" and "of" are sg devices then mmap-ed
IO is selected on "if" while normal IO is selected on "of". Direct IO
-cannot be selected with this command.
+cannot be selected with this command. The "sgm_dd" command has a "man"
+page [section 8].
"sgq_dd" is yet another implementation found in the archive directory.
From the user point of view it is very similar to sgp_dd but uses a
@@ -162,9 +167,8 @@ up (or down) disks. See README.sg_start .
"sg_reset" exercises the SCSI device/bus/host reset capability. It is
supported by the sg driver in lk 2.2.16 and beyond but associated
SCSI middle level driver changes have not been accepted into the
-standard kernel at this time. Some distributions contain the patch to
-the mid-level that activates this feature. Due to this uncertainty
-the source is placed in the "archive" directory.
+standard kernel at this time. Many distributions contain the patch to
+the mid-level that activates this feature.
4) Timing and testing
@@ -187,7 +191,7 @@ disk manuafacturers, "MB" is 1,000,000 bytes in this context.] Its
command line syntax is modelled on "sg_dd". It allows SCSI device,
SCSI bus bandwidth and the SCSI sub-system throughput to be measured.
This can be done in 3 modes: normal transfer to user space, direct
-IO or mmap-ed IO.
+IO or mmap-ed IO. The "sg_read" command has a "man" page [section 8].
"sg_turs" executes a user specified number of TEST UNIT READY commands on
the given device. This can be used to time SCSI command overhead.
@@ -269,4 +273,4 @@ The include file path issues are now all addressed in one file called
Doug Gilbert
-23rd December 2001
+13th February 2002
diff --git a/archive/Makefile b/archive/Makefile
index a0a0cdd6..4c1b23e9 100644
--- a/archive/Makefile
+++ b/archive/Makefile
@@ -6,7 +6,7 @@ MANDIR=/usr/local/man
CC = gcc
LD = gcc
-EXECS = sgq_dd sg_poll sg_reset sg_bus_xfer sg_hold isosize
+EXECS = sgq_dd sg_poll sg_bus_xfer sg_hold isosize
COMMON = isosize
@@ -35,9 +35,6 @@ sg_poll: sg_poll.o ../sg_err.o
sgq_dd: sgq_dd.o ../sg_err.o ../llseek.o
$(LD) -o $@ $(LDFLAGS) $^
-sg_reset: sg_reset.o
- $(LD) -o $@ $(LDFLAGS) $^
-
sg_bus_xfer: sg_bus_xfer.o ../sg_err.o
$(LD) -o $@ $(LDFLAGS) $^
diff --git a/archive/README b/archive/README
index 89fc88cd..d6d6c205 100644
--- a/archive/README
+++ b/archive/README
@@ -14,8 +14,6 @@ Since I actively use some of these programs for testing, a Makefile has
been set up which builds:
- sgq_dd [a dd variant the uses a dispatch loop rather than
POSIX threads (as does sgp_dd)]
- - sg_reset [does SCSI bus/device/host resets if mid level SCSI patch
- in place]
- sg_hold holds a sg device open and periodically sends a TEST
UNIT READY command to the device
- sg_poll [internal testing]
@@ -31,4 +29,4 @@ to side step various buffer copies. See:
http://www.torque.net/sg/mem2disk.html
Doug Gilbert
-23rd December 2001
+24th January 2002
diff --git a/archive/sg_simple_andre.c b/archive/sg_simple_andre.c
new file mode 100644
index 00000000..d1630224
--- /dev/null
+++ b/archive/sg_simple_andre.c
@@ -0,0 +1,33 @@
+#include <scsi/sg.h>
+
+
+#define READ10_REPLY_LEN 512
+#define READ10_CMD_LEN 10
+
+// read 0x102 blocks from block # 0x3040506
+// [just to to you where the fields are]
+..............
+
+ unsigned char r10CmdBlk [READ10_CMD_LEN] =
+ {0x28, 0, 3, 4, 5, 6, 0, 1, 2, 0};
+ sg_io_hdr_t io_hdr;
+ unsigned char inBuff[READ10_REPLY_LEN];
+ unsigned char sense_buffer[32];
+
+ /* Prepare READ_10 command */
+ memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+ io_hdr.interface_id = 'S';
+ io_hdr.cmd_len = sizeof(r10CmdBlk);
+ io_hdr.mx_sb_len = sizeof(sense_buffer);
+ io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_hdr.dxfer_len = READ10_REPLY_LEN;
+ io_hdr.dxferp = inBuff;
+ io_hdr.cmdp = r10CmdBlk;
+ io_hdr.sbp = sense_buffer;
+ io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */
+
+ if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+ perror("READ_10 SG_IO ioctl error");
+ .....
+ }
+ // block should now be in 'inBuff'
diff --git a/archive/sgq_dd.c b/archive/sgq_dd.c
index 235e561b..7a8a09d7 100644
--- a/archive/sgq_dd.c
+++ b/archive/sgq_dd.c
@@ -15,13 +15,14 @@
#include <sys/sysmacros.h>
#include <sys/poll.h>
#include <linux/major.h>
+#include <sys/time.h>
typedef unsigned char u_char; /* horrible, for scsi.h */
#include "sg_include.h"
#include "sg_err.h"
#include "llseek.h"
/* A utility program for the Linux OS SCSI generic ("sg") device driver.
-* Copyright (C) 1999, 2000 D. Gilbert and P. Allworth
+* Copyright (C) 1999-2002 D. Gilbert and P. Allworth
* 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)
@@ -47,7 +48,7 @@ typedef unsigned char u_char; /* horrible, for scsi.h */
*/
-static char * version_str = "0.52 20010819";
+static char * version_str = "0.53 20020124";
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
@@ -79,6 +80,11 @@ static char * version_str = "0.52 20010819";
#define QS_IN_POLL 11
#define QS_OUT_POLL 12
+#define STR_SZ 1024
+#define INOUTF_SZ 512
+#define EBUFF_SZ 512
+
+
struct request_element;
typedef struct request_collection
@@ -209,11 +215,13 @@ int dd_filetype(const char * filename)
void usage()
{
fprintf(stderr, "Usage: "
- "sgq_dd [if=<infile>] [skip=<n>] [of=<ofile>] [seek=<n>]\n"
- " [bs=<num>] [bpt=<num>] [count=<n>]\n"
- " [dio=<n>] [thr=<n>] [coe=<n>] [gen=<n>]\n"
- " [deb=<n>] [--version]\n"
- " usually either 'if' or 'of' is a sg or raw device\n"
+ "sgq_dd [if=<infile>] [skip=<n>] [of=<ofile>] [seek=<n>] "
+ "[bs=<num>]\n"
+ " [bpt=<num>] [count=<n>] [dio=<n>] [thr=<n>] "
+ "[coe=<n>] [gen=<n>]\n"
+ " [dio=<n>] [thr=<n>] [coe=<n>] [gen=<n>] "
+ "[deb=<n>] [--version]\n"
+ " usually either 'if' or 'of' is a sg or raw device\n"
" 'bpt' is blocks_per_transfer (default is 128)\n"
" 'dio' is direct IO, 1->attempt, 0->indirect IO (def)\n"
" 'thr' is number of queues, must be > 0, default 4, max 32\n");
@@ -524,9 +532,9 @@ int sg_finish_io(int wr, Rq_elem * rep)
return 1;
default:
{
- char ebuff[64];
- sprintf(ebuff, "%s blk=%d", rep->wr ? "writing": "reading",
- rep->blk);
+ char ebuff[EBUFF_SZ];
+ snprintf(ebuff, EBUFF_SZ, "%s blk=%d",
+ rep->wr ? "writing": "reading", rep->blk);
sg_chk_n_print3(ebuff, hp);
return -1;
}
@@ -580,7 +588,7 @@ int prepare_rq_elems(Rq_coll * clp, const char * inf, const char * outf)
int k;
Rq_elem * rep;
size_t psz;
- char ebuff[256];
+ char ebuff[EBUFF_SZ];
int sz = clp->bpt * clp->bs;
int scsi_type;
@@ -605,8 +613,8 @@ int prepare_rq_elems(Rq_coll * clp, const char * inf, const char * outf)
rep->infd = clp->infd;
else {
if ((rep->infd = open(inf, O_RDWR)) < 0) {
- sprintf(ebuff, "sgq_dd: could not open %s for sg reading",
- inf);
+ snprintf(ebuff, EBUFF_SZ,
+ "sgq_dd: could not open %s for sg reading", inf);
perror(ebuff);
return 1;
}
@@ -627,8 +635,8 @@ int prepare_rq_elems(Rq_coll * clp, const char * inf, const char * outf)
rep->outfd = clp->outfd;
else {
if ((rep->outfd = open(outf, O_RDWR)) < 0) {
- sprintf(ebuff, "sgq_dd: could not open %s for sg writing",
- outf);
+ snprintf(ebuff, EBUFF_SZ,
+ "sgq_dd: could not open %s for sg writing", outf);
perror(ebuff);
return 1;
}
@@ -742,10 +750,12 @@ int main(int argc, char * argv[])
int out_num_sect = 0;
int num_threads = DEF_NUM_THREADS;
int gen = 0;
+ int do_time = 0;
int in_sect_sz, out_sect_sz, first_xfer, qstate, req_index, seek_skip;
int blocks, stop_after_write, terminate;
- char ebuff[256];
+ char ebuff[EBUFF_SZ];
Rq_elem * rep;
+ struct timeval start_tm, end_tm;
memset(&rcoll, 0, sizeof(Rq_coll));
rcoll.bpt = DEF_BLOCKS_PER_TRANSFER;
@@ -760,7 +770,7 @@ int main(int argc, char * argv[])
for(k = 1; k < argc; k++) {
if (argv[k])
- strcpy(str, argv[k]);
+ strncpy(str, argv[k], STR_SZ);
else
continue;
for(key = str, buf = key; *buf && *buf != '=';)
@@ -768,9 +778,9 @@ int main(int argc, char * argv[])
if (*buf)
*buf++ = '\0';
if (strcmp(key,"if") == 0)
- strcpy(inf, buf);
+ strncpy(inf, buf, INOUTF_SZ);
else if (strcmp(key,"of") == 0)
- strcpy(outf, buf);
+ strncpy(outf, buf, INOUTF_SZ);
else if (0 == strcmp(key,"ibs"))
ibs = get_num(buf);
else if (0 == strcmp(key,"obs"))
@@ -795,6 +805,8 @@ int main(int argc, char * argv[])
gen = get_num(buf);
else if (0 == strncmp(key,"deb", 3))
rcoll.debug = get_num(buf);
+ else if (0 == strcmp(key,"time"))
+ do_time = get_num(buf);
else if (0 == strncmp(key, "--vers", 6)) {
fprintf(stderr, "sgq_dd for sg version 3 driver: %s\n",
version_str);
@@ -840,15 +852,16 @@ int main(int argc, char * argv[])
if (FT_SG == rcoll.in_type) {
if ((rcoll.infd = open(inf, O_RDWR)) < 0) {
- sprintf(ebuff, "sgq_dd: could not open %s for sg reading",
- inf);
+ snprintf(ebuff, EBUFF_SZ,
+ "sgq_dd: could not open %s for sg reading", inf);
perror(ebuff);
return 1;
}
}
if (FT_SG != rcoll.in_type) {
if ((rcoll.infd = open(inf, O_RDONLY)) < 0) {
- sprintf(ebuff, "sgq_dd: could not open %s for reading", inf);
+ snprintf(ebuff, EBUFF_SZ,
+ "sgq_dd: could not open %s for reading", inf);
perror(ebuff);
return 1;
}
@@ -857,7 +870,7 @@ int main(int argc, char * argv[])
offset *= rcoll.bs; /* could exceed 32 here! */
if (llse_llseek(rcoll.infd, offset, SEEK_SET) < 0) {
- sprintf(ebuff,
+ snprintf(ebuff, EBUFF_SZ,
"sgq_dd: couldn't skip to required position on %s", inf);
perror(ebuff);
return 1;
@@ -870,7 +883,7 @@ int main(int argc, char * argv[])
if (FT_SG == rcoll.out_type) {
if ((rcoll.outfd = open(outf, O_RDWR)) < 0) {
- sprintf(ebuff,
+ snprintf(ebuff, EBUFF_SZ,
"sgq_dd: could not open %s for sg writing", outf);
perror(ebuff);
return 1;
@@ -879,7 +892,7 @@ int main(int argc, char * argv[])
else {
if (FT_OTHER == rcoll.out_type) {
if ((rcoll.outfd = open(outf, O_WRONLY | O_CREAT, 0666)) < 0) {
- sprintf(ebuff,
+ snprintf(ebuff, EBUFF_SZ,
"sgq_dd: could not open %s for writing", outf);
perror(ebuff);
return 1;
@@ -887,7 +900,7 @@ int main(int argc, char * argv[])
}
else {
if ((rcoll.outfd = open(outf, O_WRONLY)) < 0) {
- sprintf(ebuff,
+ snprintf(ebuff, EBUFF_SZ,
"sgq_dd: could not open %s for raw writing", outf);
perror(ebuff);
return 1;
@@ -898,7 +911,7 @@ int main(int argc, char * argv[])
offset *= rcoll.bs; /* could exceed 32 bits here! */
if (llse_llseek(rcoll.outfd, offset, SEEK_SET) < 0) {
- sprintf(ebuff,
+ snprintf(ebuff, EBUFF_SZ,
"sgq_dd: couldn't seek to required position on %s", outf);
perror(ebuff);
return 1;
@@ -987,6 +1000,11 @@ int main(int argc, char * argv[])
stop_after_write = 0;
terminate = 0;
seek_skip = rcoll.seek - rcoll.skip;
+ if (do_time) {
+ start_tm.tv_sec = 0;
+ start_tm.tv_usec = 0;
+ gettimeofday(&start_tm, NULL);
+ }
while (rcoll.out_done_count > 0) { /* >>>>>>>>> main loop */
req_index = -1;
qstate = decider(&rcoll, first_xfer, &req_index);
@@ -1104,6 +1122,28 @@ int main(int argc, char * argv[])
break;
} /* >>>>>>>>>>>>> end of main loop */
+ if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
+ struct timeval res_tm;
+ double a, b;
+
+ gettimeofday(&end_tm, NULL);
+ res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
+ res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
+ if (res_tm.tv_usec < 0) {
+ --res_tm.tv_sec;
+ res_tm.tv_usec += 1000000;
+ }
+ a = res_tm.tv_sec;
+ a += (0.000001 * res_tm.tv_usec);
+ b = (double)rcoll.bs * (dd_count - rcoll.out_done_count);
+ printf("time to transfer data was %d.%06d secs",
+ (int)res_tm.tv_sec, (int)res_tm.tv_usec);
+ if ((a > 0.00001) && (b > 511))
+ printf(", %.2f MB/sec\n", b / (a * 1000000.0));
+ else
+ printf("\n");
+ }
+
if (STDIN_FILENO != rcoll.infd)
close(rcoll.infd);
if (STDOUT_FILENO != rcoll.outfd)
diff --git a/archive/sgs_dd.c b/archive/sgs_dd.c
index b9b4e60e..ff329a33 100644
--- a/archive/sgs_dd.c
+++ b/archive/sgs_dd.c
@@ -44,7 +44,7 @@
This version should compile with Linux sg drivers with version numbers
>= 30000 . Also this version tries to use real time signals.
- Version 3.982 20000827
+ Version 3.99 20020126
6 byte commands [READ: 0x08, WRITE: 0x0a]:
@@ -79,6 +79,12 @@
#define SGQ_CAN_WRITE 2
#define SGQ_TIMEOUT 4
+
+#define STR_SZ 1024
+#define INOUTF_SZ 512
+#define EBUFF_SZ 512
+
+
typedef struct request_element
{
struct request_element * nextp;
@@ -362,7 +368,7 @@ int start_read(Rq_coll * clp)
int blocks = (clp->in_count > clp->bpt) ? clp->bpt : clp->in_count;
Rq_elem * rep = clp->rd_posp;
int buf_sz, res;
- char ebuff[256];
+ char ebuff[EBUFF_SZ];
#ifdef SG_DEBUG
printf("start_read, elem idx=%d\n", rep - clp->elem);
@@ -399,7 +405,7 @@ int start_read(Rq_coll * clp)
(EINTR == errno))
;
if (res < 0) {
- sprintf(ebuff, "sgs_dd: reading, in_blk=%d ", rep->blk);
+ snprintf(ebuff, EBUFF_SZ, "sgs_dd: reading, in_blk=%d ", rep->blk);
perror(ebuff);
rep->state = SGQ_IO_ERR;
return res;
@@ -430,7 +436,7 @@ int start_write(Rq_coll * clp)
{
Rq_elem * rep = clp->wr_posp;
int res, blocks;
- char ebuff[256];
+ char ebuff[EBUFF_SZ];
while ((0 != rep->wr) || (SGQ_IO_FINISHED != rep->state)) {
rep = rep->nextp;
@@ -461,7 +467,7 @@ int start_write(Rq_coll * clp)
rep->num_blks * clp->bs)) < 0) && (EINTR == errno))
;
if (res < 0) {
- sprintf(ebuff, "sgs_dd: output, out_blk=%d ", rep->blk);
+ snprintf(ebuff, EBUFF_SZ, "sgs_dd: output, out_blk=%d ", rep->blk);
perror(ebuff);
rep->state = SGQ_IO_ERR;
return res;
@@ -654,16 +660,16 @@ int main(int argc, char * argv[])
int ibs = 0;
int obs = 0;
int count = -1;
- char str[512];
+ char str[STR_SZ];
char * key;
char * buf;
- char inf[512];
- char outf[512];
+ char inf[INOUTF_SZ];
+ char outf[INOUTF_SZ];
int res, k;
int in_num_sect = 0;
int out_num_sect = 0;
int in_sect_sz, out_sect_sz, crw;
- char ebuff[256];
+ char ebuff[EBUFF_SZ];
Rq_coll rcoll;
memset(&rcoll, 0, sizeof(Rq_coll));
@@ -676,8 +682,10 @@ int main(int argc, char * argv[])
}
for(k = 1; k < argc; k++) {
- if (argv[k])
- strcpy(str, argv[k]);
+ if (argv[k]) {
+ strncpy(str, argv[k], STR_SZ);
+ str[STR_SZ - 1] = '\0';
+ }
else
continue;
for(key = str, buf = key; *buf && *buf != '=';)
@@ -685,9 +693,9 @@ int main(int argc, char * argv[])
if (*buf)
*buf++ = '\0';
if (strcmp(key,"if") == 0)
- strcpy(inf, buf);
+ strncpy(inf, buf, INOUTF_SZ);
else if (strcmp(key,"of") == 0)
- strcpy(outf, buf);
+ strncpy(outf, buf, INOUTF_SZ);
else if (0 == strcmp(key,"ibs"))
ibs = get_num(buf);
else if (0 == strcmp(key,"obs"))
@@ -733,7 +741,8 @@ int main(int argc, char * argv[])
rcoll.outfd = STDOUT_FILENO;
if (inf[0] && ('-' != inf[0])) {
if ((rcoll.infd = open(inf, O_RDONLY)) < 0) {
- sprintf(ebuff, "sgs_dd: could not open %s for reading", inf);
+ snprintf(ebuff, EBUFF_SZ, "sgs_dd: could not open %s for reading",
+ inf);
perror(ebuff);
return 1;
}
@@ -744,7 +753,7 @@ int main(int argc, char * argv[])
offset *= rcoll.bs; /* could overflow here! */
if (lseek(rcoll.infd, offset, SEEK_SET) < 0) {
- sprintf(ebuff,
+ snprintf(ebuff, EBUFF_SZ,
"sgs_dd: couldn't skip to required position on %s", inf);
perror(ebuff);
return 1;
@@ -777,8 +786,8 @@ int main(int argc, char * argv[])
}
if (! rcoll.out_is_sg) {
if ((rcoll.outfd = open(outf, O_WRONLY | O_CREAT, 0666)) < 0) {
- sprintf(ebuff,
- "sgs_dd: could not open %s for writing", outf);
+ snprintf(ebuff, EBUFF_SZ,
+ "sgs_dd: could not open %s for writing", outf);
perror(ebuff);
return 1;
}
@@ -787,7 +796,7 @@ int main(int argc, char * argv[])
offset *= rcoll.bs; /* could overflow here! */
if (lseek(rcoll.outfd, offset, SEEK_SET) < 0) {
- sprintf(ebuff,
+ snprintf(ebuff, EBUFF_SZ,
"sgs_dd: couldn't seek to required position on %s", outf);
perror(ebuff);
return 1;
diff --git a/scsi_devfs_scan.c b/scsi_devfs_scan.c
index 26b7f775..6a8f765a 100644
--- a/scsi_devfs_scan.c
+++ b/scsi_devfs_scan.c
@@ -23,7 +23,7 @@
This program scans the /dev directory structure looking for the
devfs "primary" scsi (and optionally IDE) device names.
- Version 0.10 20010217
+ Version 0.11 20020114
*/
void usage()
@@ -172,14 +172,14 @@ void leaf_dir(const char * lf, unsigned int * larr)
}
if (de_result == NULL)
break;
- strcpy(name, de_entry->d_name);
+ strncpy(name, de_entry->d_name, NAME_LEN_MAX * 2);
if ((0 == strcmp("..", name)) ||(0 == strcmp(".", name)))
continue;
if (do_extra) {
struct stat st;
char devname[NAME_LEN_MAX * 2];
- strcpy(devname, lf);
+ strncpy(devname, lf, NAME_LEN_MAX * 2);
strcat(devname, "/");
strcat(devname, name);
if (stat(devname, &st) < 0)
@@ -210,7 +210,7 @@ void leaf_dir(const char * lf, unsigned int * larr)
char buff[64];
memset(buff, 0, sizeof(buff));
- strcpy(name, lf);
+ strncpy(name, lf, NAME_LEN_MAX * 2);
strcat(name, "/generic");
if ((sg_fd = open(name, O_RDONLY)) < 0) {
if (! checked_sg) {
@@ -265,7 +265,7 @@ int hbtl_scan(const char * path, int level, unsigned int *larr)
if (0 == strncmp(level_arr[level], de_entry->d_name, level_slen)) {
if (1 != sscanf(de_entry->d_name + level_slen, "%u", larr + level))
larr[level] = UINT_MAX;
- strcpy(new_path, path);
+ strncpy(new_path, path, NAME_LEN_MAX * 2);
strcat(new_path, "/");
strcat(new_path, de_entry->d_name);
if ((level + 1) < LEVELS) {
@@ -282,15 +282,18 @@ int hbtl_scan(const char * path, int level, unsigned int *larr)
return res;
}
+#define D_ROOT_SZ 512
+
+
int main(int argc, char * argv[])
{
int k, res;
- char ds_root[512];
- char di_root[512];
+ char ds_root[D_ROOT_SZ];
+ char di_root[D_ROOT_SZ];
unsigned int larr[LEVELS];
struct stat st;
- strcpy(ds_root, "/dev");
+ strncpy(ds_root, "/dev", D_ROOT_SZ);
for (k = 1; k < argc; ++k) {
if (0 == strcmp("-ide", argv[k]))
do_ide = 1;
@@ -304,9 +307,9 @@ int main(int argc, char * argv[])
do_quiet = 1;
else if (0 == strncmp("-d", argv[k], 2)) {
if (strlen(argv[k]) > 2)
- strcpy(ds_root, argv[k] + 2);
+ strncpy(ds_root, argv[k] + 2, D_ROOT_SZ);
else if (++k < argc)
- strcpy(ds_root, argv[k]);
+ strncpy(ds_root, argv[k], D_ROOT_SZ);
}
else if ((0 == strcmp("-?", argv[k])) ||
(0 == strncmp("-h", argv[k], 2))) {
@@ -325,12 +328,12 @@ int main(int argc, char * argv[])
return 1;
}
}
- strcpy(di_root, ds_root);
+ strncpy(di_root, ds_root, D_ROOT_SZ);
strcat(di_root, "/.devfsd");
if (stat(di_root, &st) < 0)
printf("Didn't find %s so perhaps devfs is not present,"
" continuing ...\n", di_root);
- strcpy(di_root, ds_root);
+ strncpy(di_root, ds_root, D_ROOT_SZ);
strcat(ds_root, "/scsi");
strcat(di_root, "/ide");
diff --git a/sg_dd.8 b/sg_dd.8
index b4a2107d..cedd447d 100644
--- a/sg_dd.8
+++ b/sg_dd.8
@@ -1,6 +1,6 @@
-.TH SG_DD "8" "September 2001" "sg3_utils-0.95" SG3_UTILS
+.TH SG_DD "8" "February 2002" "sg3_utils-0.98" SG3_UTILS
.SH NAME
-copies data to and from sg and raw devices
+sg_dd \- copies data to and from sg and raw devices
.SH SYNOPSIS
.B sg_dd
[\fIOPTION\fR]...
@@ -25,6 +25,10 @@ which permits "bs" to be an integral multiple. Default is 512 which
is usually correct for disks but incorrect for cdroms (which normally
have 2048 byte blocks).
.TP
+cdbsz=6 | 10 | 12 | 16
+size of SCSI READ and/or WRITE commands issued on sg device names.
+Default is 10 byte SCSI command blocks
+.TP
count=BLOCKS
copy this number of blocks. Default is minimum number that sg devices
return from READ CAPACITY (if that works) or 0
@@ -35,6 +39,10 @@ IO which, if not available, falls back to indirect IO and notes this
at completion. If direct IO is selected and /proc/scsi/sg/allow_dio
has the value of 0 then a warning is issued (and indirect IO is performed)
.TP
+gen=0 | 1
+the default action (when "gen=0") is to require that either "if" or "of"
+is a sg or raw device name. To remove this restriction set "gen=1"
+.TP
ibs=BYTES
if given must be the same as bs
.TP
@@ -52,14 +60,22 @@ skip BLOCKS bs-sized blocks at start of output
.TP
skip=BLOCKS
skip BLOCKS bs-sized blocks at start of input
+.TP
+time=0 | 1
+when 1, times transfer and does throughput calculation, outputting the
+results (to stderr) at completion. When 0 (default) doesn't perform timing
+.TP
+--version
+outputs version number information and exits
.PP
-Either the input file or the output file must be a sg or raw device.
+Either the input file or the output file must be a sg or raw device,
+unless "gen=1".
A raw device must be bound to a block device prior to using sg_dd.
See
.B raw(8)
for more information about binding raw devices. To be safe, the sg device
-mapping to SCSI block devices should be checked with "cat /proc/scsi/scsi"
-before use.
+mapping to SCSI block devices should be checked with "cat /proc/scsi/scsi",
+or sg_map before use.
.PP
The count is only deduced for sg devices (minimum > 0 if both input and
output are sg devices) otherwise it defaults to 0. This is for safety!
@@ -77,10 +93,11 @@ this data into user memory (write operations reverse this sequence).
This is called "indirect IO" and there is a "dio" option to select
"direct IO" which will DMA directly into user memory. Due to some
issues "direct IO" is disabled in the sg driver and needs a
-configuration change to activate it.
+configuration change to activate it. This is typically done with
+"echo 1 > /proc/scsi/sg/allow_dio".
.PP
All informative, warning and error output is sent to stderr so that
-dd\'s output file can be stdout and remain unpolluted. If no options
+dd's output file can be stdout and remain unpolluted. If no options
are given, then the usage message is output and nothing else happens.
.SH EXAMPLES
.PP
@@ -95,7 +112,7 @@ equivalent to:
.PP
dd if=/dev/sda of=t bs=512 count=1000000
.PP
-although dd\'s speed may improve if bs was larger and count was suitably
+although dd's speed may improve if bs was larger and count was suitably
reduced. Using a raw device to do something similar on a IDE disk:
.PP
raw /dev/raw/raw1 /dev/hda
@@ -112,6 +129,15 @@ This assumes a valid partition is found on the SCSI disk at the given
skip block address (past the 5 GB point of that disk) and that
the partition goes to the end of the SCSI disk. An explicit count
is probably a safer option.
+.PP
+To time a streaming read of the first 1 GB on a disk this command
+could be used:
+.PP
+ sg_dd if=/dev/sg0 of=/dev/null bs=512 count=2m time=1
+.PP
+On completion this will output a line like:
+"time to transfer data was 26.020794 secs, 41.26 MB/sec". The "MB/sec"
+in this case is 1,000,000 bytes per second.
.SH NOTE
For sg devices this command issues READ_10 and WRITE_10 SCSI commands which
are appropriate for disks and CDROM players. Those commands are not
@@ -128,7 +154,7 @@ Written by Doug Gilbert and Peter Allworth.
.SH "REPORTING BUGS"
Report bugs to <dgilbert@interlog.com>.
.SH COPYRIGHT
-Copyright \(co 2000, 2001 Douglas Gilbert
+Copyright \(co 2000-2002 Douglas Gilbert
.br
This software is distributed under the GPL version 2. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/sg_dd.c b/sg_dd.c
index ff67118b..ad19d971 100644
--- a/sg_dd.c
+++ b/sg_dd.c
@@ -12,14 +12,15 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
-#include <linux/major.h>
+#include <sys/time.h>
+#include <linux/major.h>
typedef unsigned char u_char; /* horrible, for scsi.h */
#include "sg_include.h"
#include "sg_err.h"
#include "llseek.h"
/* A utility program for the Linux OS SCSI generic ("sg") device driver.
-* Copyright (C) 1999 - 2001 D. Gilbert and P. Allworth
+* Copyright (C) 1999 - 2002 D. Gilbert and P. Allworth
* 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)
@@ -39,16 +40,22 @@ typedef unsigned char u_char; /* horrible, for scsi.h */
the maximum number of blocks in each transfer. The default value is 128.
For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16KB
in this case) is transferred to or from the sg device in a single SCSI
- command.
+ command. The actual size of the SCSI READ or WRITE command block can be
+ selected with the "cdbsz" argument.
This version should compile with Linux sg drivers with version numbers
>= 30000 .
-
- Version 5.13 20010819
*/
+static char * version_str = "5.18 20020213";
+
+
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
+#define DEF_SCSI_CDBSZ 10
+#define MAX_SCSI_CDBSZ 16
+
+#define ME "sg_dd: "
// #define SG_DEBUG
@@ -133,12 +140,17 @@ int dd_filetype(const char * filename)
void usage()
{
fprintf(stderr, "Usage: "
- "sg_dd [if=<infile>] [skip=<n>] [of=<ofile>] [seek=<n>]\n"
- " [bs=<num>] [bpt=<num>] [count=<n>]"
+ "sg_dd [if=<infile>] [skip=<n>] [of=<ofile>] [seek=<n>] "
+ "[bs=<num>]\n"
+ " [bpt=<num>] [count=<n>] [time=<n>]"
" [dio=<n>]\n"
+ " [cdbsz=<6|10|12|16>] [gen=0|1] [--version]\n"
" either 'if' or 'of' must be a sg or raw device\n"
" 'bpt' is blocks_per_transfer (default is 128)\n"
- " 'dio' is direct IO, 1->attempt, 0->indirect IO (def)\n");
+ " 'dio' is direct IO, 1->attempt, 0->indirect IO (def)\n"
+ " 'gen' 0->either 'if' or 'of' must be sg or raw device(def)\n"
+ " 'time' 0->no timing(def), 1->time plus calculate throughput\n"
+ " 'cdbsz' size of SCSI READ or WRITE command (default is 10)\n");
}
/* Return of 0 -> success, -1 -> failure, 2 -> try again */
@@ -179,26 +191,104 @@ int read_capacity(int sg_fd, int * num_sect, int * sect_sz)
return 0;
}
+int sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz, unsigned int blocks,
+ unsigned int start_block, int write_true)
+{
+ int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88};
+ int wr_opcode[] = {0xa, 0x2a, 0xaa, 0x8a};
+ int sz_ind;
+
+ memset(cdbp, 0, cdb_sz);
+ switch (cdb_sz) {
+ case 6:
+ sz_ind = 0;
+ cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+ rd_opcode[sz_ind]);
+ cdbp[1] |= (unsigned char)((start_block >> 16) & 0x1f);
+ cdbp[2] = (unsigned char)((start_block >> 8) & 0xff);
+ cdbp[3] = (unsigned char)(start_block & 0xff);
+ cdbp[4] = (256 == blocks) ? 0 : (unsigned char)blocks;
+ if (blocks > 256) {
+ fprintf(stderr, ME "for 6 byte commands, maximum number of "
+ "blocks is 256\n");
+ return 1;
+ }
+ if ((start_block + blocks - 1) & (~0x1fffff)) {
+ fprintf(stderr, ME "for 6 byte commands, can't address blocks"
+ " beyond %d\n", 0x1fffff);
+ return 1;
+ }
+ break;
+ case 10:
+ sz_ind = 1;
+ cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+ rd_opcode[sz_ind]);
+ cdbp[2] = (unsigned char)((start_block >> 24) & 0xff);
+ cdbp[3] = (unsigned char)((start_block >> 16) & 0xff);
+ cdbp[4] = (unsigned char)((start_block >> 8) & 0xff);
+ cdbp[5] = (unsigned char)(start_block & 0xff);
+ cdbp[7] = (unsigned char)((blocks >> 8) & 0xff);
+ cdbp[8] = (unsigned char)(blocks & 0xff);
+ if (blocks & (~0xffff)) {
+ fprintf(stderr, ME "for 10 byte commands, maximum number of "
+ "blocks is %d\n", 0xffff);
+ return 1;
+ }
+ break;
+ case 12:
+ sz_ind = 2;
+ cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+ rd_opcode[sz_ind]);
+ cdbp[2] = (unsigned char)((start_block >> 24) & 0xff);
+ cdbp[3] = (unsigned char)((start_block >> 16) & 0xff);
+ cdbp[4] = (unsigned char)((start_block >> 8) & 0xff);
+ cdbp[5] = (unsigned char)(start_block & 0xff);
+ cdbp[6] = (unsigned char)((blocks >> 24) & 0xff);
+ cdbp[7] = (unsigned char)((blocks >> 16) & 0xff);
+ cdbp[8] = (unsigned char)((blocks >> 8) & 0xff);
+ cdbp[9] = (unsigned char)(blocks & 0xff);
+ break;
+ case 16:
+ sz_ind = 3;
+ cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+ rd_opcode[sz_ind]);
+ /* can't cope with block number > 32 bits (yet) */
+ cdbp[6] = (unsigned char)((start_block >> 24) & 0xff);
+ cdbp[7] = (unsigned char)((start_block >> 16) & 0xff);
+ cdbp[8] = (unsigned char)((start_block >> 8) & 0xff);
+ cdbp[9] = (unsigned char)(start_block & 0xff);
+ cdbp[10] = (unsigned char)((blocks >> 24) & 0xff);
+ cdbp[11] = (unsigned char)((blocks >> 16) & 0xff);
+ cdbp[12] = (unsigned char)((blocks >> 8) & 0xff);
+ cdbp[13] = (unsigned char)(blocks & 0xff);
+ break;
+ default:
+ fprintf(stderr, ME "expected cdb size of 6, 10, 12, or 16 but got"
+ "=%d\n", cdb_sz);
+ return 1;
+ }
+ return 0;
+}
+
/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
2 -> try again */
int sg_read(int sg_fd, unsigned char * buff, int blocks, int from_block,
- int bs, int * diop)
+ int bs, int cdbsz, int * diop)
{
- unsigned char rdCmd[10] = {0x28, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ unsigned char rdCmd[MAX_SCSI_CDBSZ];
unsigned char senseBuff[SENSE_BUFF_LEN];
sg_io_hdr_t io_hdr;
int res;
- rdCmd[2] = (unsigned char)((from_block >> 24) & 0xFF);
- rdCmd[3] = (unsigned char)((from_block >> 16) & 0xFF);
- rdCmd[4] = (unsigned char)((from_block >> 8) & 0xFF);
- rdCmd[5] = (unsigned char)(from_block & 0xFF);
- rdCmd[7] = (unsigned char)((blocks >> 8) & 0xff);
- rdCmd[8] = (unsigned char)(blocks & 0xff);
+ if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, 0)) {
+ fprintf(stderr, ME "bad rd cdb build, from_block=%d, blocks=%d\n",
+ from_block, blocks);
+ return -1;
+ }
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
io_hdr.interface_id = 'S';
- io_hdr.cmd_len = sizeof(rdCmd);
+ io_hdr.cmd_len = cdbsz;
io_hdr.cmdp = rdCmd;
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.dxfer_len = bs * blocks;
@@ -253,23 +343,22 @@ int sg_read(int sg_fd, unsigned char * buff, int blocks, int from_block,
/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
2 -> try again */
int sg_write(int sg_fd, unsigned char * buff, int blocks, int to_block,
- int bs, int * diop)
+ int bs, int cdbsz, int * diop)
{
- unsigned char wrCmd[10] = {0x2a, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ unsigned char wrCmd[MAX_SCSI_CDBSZ];
unsigned char senseBuff[SENSE_BUFF_LEN];
sg_io_hdr_t io_hdr;
int res;
- wrCmd[2] = (unsigned char)((to_block >> 24) & 0xFF);
- wrCmd[3] = (unsigned char)((to_block >> 16) & 0xFF);
- wrCmd[4] = (unsigned char)((to_block >> 8) & 0xFF);
- wrCmd[5] = (unsigned char)(to_block & 0xFF);
- wrCmd[7] = (unsigned char)((blocks >> 8) & 0xff);
- wrCmd[8] = (unsigned char)(blocks & 0xff);
+ if (sg_build_scsi_cdb(wrCmd, cdbsz, blocks, to_block, 1)) {
+ fprintf(stderr, ME "bad wr cdb build, to_block=%d, blocks=%d\n",
+ to_block, blocks);
+ return -1;
+ }
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
io_hdr.interface_id = 'S';
- io_hdr.cmd_len = sizeof(wrCmd);
+ io_hdr.cmd_len = cdbsz;
io_hdr.cmdp = wrCmd;
io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
io_hdr.dxfer_len = bs * blocks;
@@ -354,6 +443,10 @@ int get_num(char * buf)
}
}
+#define STR_SZ 1024
+#define INOUTF_SZ 512
+#define EBUFF_SZ 512
+
int main(int argc, char * argv[])
{
@@ -363,15 +456,18 @@ int main(int argc, char * argv[])
int ibs = 0;
int obs = 0;
int bpt = DEF_BLOCKS_PER_TRANSFER;
- char str[512];
+ char str[STR_SZ];
char * key;
char * buf;
- char inf[512];
+ char inf[INOUTF_SZ];
int in_type = FT_OTHER;
- char outf[512];
+ char outf[INOUTF_SZ];
int out_type = FT_OTHER;
int dio = 0;
int dio_incomplete = 0;
+ int do_time = 0;
+ int do_gen = 0;
+ int scsi_cdbsz = DEF_SCSI_CDBSZ;
int res, k, t, buf_sz, dio_tmp;
int infd, outfd, blocks;
unsigned char * wrkBuff;
@@ -379,8 +475,10 @@ int main(int argc, char * argv[])
int in_num_sect = 0;
int out_num_sect = 0;
int in_sect_sz, out_sect_sz;
- char ebuff[256];
+ char ebuff[EBUFF_SZ];
int blocks_per;
+ int req_count;
+ struct timeval start_tm, end_tm;
inf[0] = '\0';
outf[0] = '\0';
@@ -390,8 +488,10 @@ int main(int argc, char * argv[])
}
for(k = 1; k < argc; k++) {
- if (argv[k])
- strcpy(str, argv[k]);
+ if (argv[k]) {
+ strncpy(str, argv[k], STR_SZ);
+ str[STR_SZ - 1] = '\0';
+ }
else
continue;
for(key = str, buf = key; *buf && *buf != '=';)
@@ -399,9 +499,9 @@ int main(int argc, char * argv[])
if (*buf)
*buf++ = '\0';
if (strcmp(key,"if") == 0)
- strcpy(inf, buf);
+ strncpy(inf, buf, INOUTF_SZ);
else if (strcmp(key,"of") == 0)
- strcpy(outf, buf);
+ strncpy(outf, buf, INOUTF_SZ);
else if (0 == strcmp(key,"ibs"))
ibs = get_num(buf);
else if (0 == strcmp(key,"obs"))
@@ -418,6 +518,17 @@ int main(int argc, char * argv[])
dd_count = get_num(buf);
else if (0 == strcmp(key,"dio"))
dio = get_num(buf);
+ else if (0 == strcmp(key,"time"))
+ do_time = get_num(buf);
+ else if (0 == strcmp(key,"gen"))
+ do_gen = get_num(buf);
+ else if (0 == strcmp(key,"cdbsz"))
+ scsi_cdbsz = get_num(buf);
+ else if (0 == strncmp(key, "--vers", 6)) {
+ fprintf(stderr, ME "for Linux sg version 3 driver: %s\n",
+ version_str);
+ return 0;
+ }
else {
fprintf(stderr, "Unrecognized argument '%s'\n", key);
usage();
@@ -438,7 +549,7 @@ int main(int argc, char * argv[])
return 1;
}
#ifdef SG_DEBUG
- fprintf(stderr, "sg_dd: if=%s skip=%d of=%s seek=%d count=%d\n",
+ fprintf(stderr, ME "if=%s skip=%d of=%s seek=%d count=%d\n",
inf, skip, outf, seek, dd_count);
#endif
install_handler (SIGINT, interrupt_handler);
@@ -453,23 +564,25 @@ int main(int argc, char * argv[])
if (FT_SG == in_type) {
if ((infd = open(inf, O_RDWR)) < 0) {
- sprintf(ebuff, "sg_dd: could not open %s for sg reading", inf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for sg reading", inf);
perror(ebuff);
return 1;
}
t = bs * bpt;
res = ioctl(infd, SG_SET_RESERVED_SIZE, &t);
if (res < 0)
- perror("sg_dd: SG_SET_RESERVED_SIZE error");
+ perror(ME "SG_SET_RESERVED_SIZE error");
res = ioctl(infd, SG_GET_VERSION_NUM, &t);
if ((res < 0) || (t < 30000)) {
- fprintf(stderr, "sg_dd: sg driver prior to 3.x.y\n");
+ fprintf(stderr, ME "sg driver prior to 3.x.y\n");
return 1;
}
}
if (FT_SG != in_type) {
if ((infd = open(inf, O_RDONLY)) < 0) {
- sprintf(ebuff, "sg_dd: could not open %s for reading", inf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for reading", inf);
perror(ebuff);
return 1;
}
@@ -478,8 +591,8 @@ int main(int argc, char * argv[])
offset *= bs; /* could exceed 32 bits here! */
if (llse_llseek(infd, offset, SEEK_SET) < 0) {
- sprintf(ebuff,
- "sg_dd: couldn't skip to required position on %s", inf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "couldn't skip to required position on %s", inf);
perror(ebuff);
return 1;
}
@@ -492,33 +605,34 @@ int main(int argc, char * argv[])
if (FT_SG == out_type) {
if ((outfd = open(outf, O_RDWR)) < 0) {
- sprintf(ebuff, "sg_dd: could not open %s for sg writing", outf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for sg writing", outf);
perror(ebuff);
return 1;
}
t = bs * bpt;
res = ioctl(outfd, SG_SET_RESERVED_SIZE, &t);
if (res < 0)
- perror("sg_dd: SG_SET_RESERVED_SIZE error");
+ perror(ME "SG_SET_RESERVED_SIZE error");
res = ioctl(outfd, SG_GET_VERSION_NUM, &t);
if ((res < 0) || (t < 30000)) {
- fprintf(stderr, "sg_dd: sg driver prior to 3.x.y\n");
+ fprintf(stderr, ME "sg driver prior to 3.x.y\n");
return 1;
}
}
else {
if (FT_OTHER == out_type) {
if ((outfd = open(outf, O_WRONLY | O_CREAT, 0666)) < 0) {
- sprintf(ebuff,
- "sg_dd: could not open %s for writing", outf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for writing", outf);
perror(ebuff);
return 1;
}
}
else {
if ((outfd = open(outf, O_WRONLY)) < 0) {
- sprintf(ebuff,
- "sg_dd: could not open %s for raw writing", outf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for raw writing", outf);
perror(ebuff);
return 1;
}
@@ -528,8 +642,8 @@ int main(int argc, char * argv[])
offset *= bs; /* could exceed 32 bits here! */
if (llse_llseek(outfd, offset, SEEK_SET) < 0) {
- sprintf(ebuff,
- "sg_dd: couldn't seek to required position on %s", outf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "couldn't seek to required position on %s", outf);
perror(ebuff);
return 1;
}
@@ -541,12 +655,11 @@ int main(int argc, char * argv[])
"Can't have both 'if' as stdin _and_ 'of' as stdout\n");
return 1;
}
-#if 1
- if ((FT_OTHER == in_type) && (FT_OTHER == out_type)) {
+ if ((FT_OTHER == in_type) && (FT_OTHER == out_type) && !do_gen) {
fprintf(stderr, "Both 'if' and 'of' can't be ordinary files\n");
+ fprintf(stderr, "If you really want this set 'gen=1'\n");
return 1;
}
-#endif
if (0 == dd_count)
return 0;
else if (dd_count < 0) {
@@ -634,11 +747,19 @@ int main(int argc, char * argv[])
fprintf(stderr, "Start of loop, count=%d, blocks_per=%d\n",
dd_count, blocks_per);
#endif
+ if (do_time) {
+ start_tm.tv_sec = 0;
+ start_tm.tv_usec = 0;
+ gettimeofday(&start_tm, NULL);
+ }
+ req_count = dd_count;
+
while (dd_count > 0) {
blocks = (dd_count > blocks_per) ? blocks_per : dd_count;
if (FT_SG == in_type) {
dio_tmp = dio;
- res = sg_read(infd, wrkPos, blocks, skip, bs, &dio_tmp);
+ res = sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
+ &dio_tmp);
if (1 == res) { /* ENOMEM, find what's available+try that */
if (ioctl(infd, SG_GET_RESERVED_SIZE, &buf_sz) < 0) {
perror("RESERVED_SIZE ioctls failed");
@@ -648,12 +769,14 @@ int main(int argc, char * argv[])
blocks = blocks_per;
fprintf(stderr,
"Reducing read to %d blocks per loop\n", blocks_per);
- res = sg_read(infd, wrkPos, blocks, skip, bs, &dio_tmp);
+ res = sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
+ &dio_tmp);
}
else if (2 == res) {
fprintf(stderr,
"Unit attention, media changed, continuing (r)\n");
- res = sg_read(infd, wrkPos, blocks, skip, bs, &dio_tmp);
+ res = sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
+ &dio_tmp);
}
if (0 != res) {
fprintf(stderr, "sg_read failed, skip=%d\n", skip);
@@ -670,7 +793,7 @@ int main(int argc, char * argv[])
(EINTR == errno))
;
if (res < 0) {
- sprintf(ebuff, "sg_dd: reading, skip=%d ", skip);
+ snprintf(ebuff, EBUFF_SZ, ME "reading, skip=%d ", skip);
perror(ebuff);
break;
}
@@ -687,7 +810,8 @@ int main(int argc, char * argv[])
if (FT_SG == out_type) {
dio_tmp = dio;
- res = sg_write(outfd, wrkPos, blocks, seek, bs, &dio_tmp);
+ res = sg_write(outfd, wrkPos, blocks, seek, bs, scsi_cdbsz,
+ &dio_tmp);
if (1 == res) { /* ENOMEM, find what's available+try that */
if (ioctl(outfd, SG_GET_RESERVED_SIZE, &buf_sz) < 0) {
perror("RESERVED_SIZE ioctls failed");
@@ -697,12 +821,14 @@ int main(int argc, char * argv[])
blocks = blocks_per;
fprintf(stderr,
"Reducing write to %d blocks per loop\n", blocks);
- res = sg_write(outfd, wrkPos, blocks, seek, bs, &dio_tmp);
+ res = sg_write(outfd, wrkPos, blocks, seek, bs, scsi_cdbsz,
+ &dio_tmp);
}
else if (2 == res) {
fprintf(stderr,
"Unit attention, media changed, continuing (w)\n");
- res = sg_write(outfd, wrkPos, blocks, seek, bs, &dio_tmp);
+ res = sg_write(outfd, wrkPos, blocks, seek, bs, scsi_cdbsz,
+ &dio_tmp);
}
else if (0 != res) {
fprintf(stderr, "sg_write failed, seek=%d\n", seek);
@@ -719,7 +845,7 @@ int main(int argc, char * argv[])
&& (EINTR == errno))
;
if (res < 0) {
- sprintf(ebuff, "sg_dd: writing, seek=%d ", seek);
+ snprintf(ebuff, EBUFF_SZ, ME "writing, seek=%d ", seek);
perror(ebuff);
break;
}
@@ -739,6 +865,27 @@ int main(int argc, char * argv[])
skip += blocks;
seek += blocks;
}
+ if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
+ struct timeval res_tm;
+ double a, b;
+
+ gettimeofday(&end_tm, NULL);
+ res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
+ res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
+ if (res_tm.tv_usec < 0) {
+ --res_tm.tv_sec;
+ res_tm.tv_usec += 1000000;
+ }
+ a = res_tm.tv_sec;
+ a += (0.000001 * res_tm.tv_usec);
+ b = (double)bs * (req_count - dd_count);
+ printf("time to transfer data was %d.%06d secs",
+ (int)res_tm.tv_sec, (int)res_tm.tv_usec);
+ if ((a > 0.00001) && (b > 511))
+ printf(", %.2f MB/sec\n", b / (a * 1000000.0));
+ else
+ printf("\n");
+ }
free(wrkBuff);
if (STDIN_FILENO != infd)
diff --git a/sg_debug.c b/sg_debug.c
index 796a7c04..fb044feb 100644
--- a/sg_debug.c
+++ b/sg_debug.c
@@ -20,15 +20,16 @@
This program outputs debug information to the console/log for _all_
active sg devices.
- Version 3.54 (20010110)
+ Version 3.55 (20020115)
*/
+#define EBUFF_SZ 256
int main(int argc, char * argv[])
{
int fd, res, debug, t;
- char ebuff[256];
+ char ebuff[EBUFF_SZ];
if ((argc != 2) || ('-' == *argv[1])) {
printf("Usage: sg_debug <sg_device>\n");
@@ -39,7 +40,8 @@ int main(int argc, char * argv[])
if (EBUSY == errno)
printf("Failed trying to open %s because it is busy\n", argv[1]);
else {
- sprintf(ebuff, "sg_debug: Error trying to open %s ", argv[1]);
+ snprintf(ebuff, EBUFF_SZ, "sg_debug: Error trying to open %s ",
+ argv[1]);
perror(ebuff);
}
return 1;
@@ -61,7 +63,7 @@ int main(int argc, char * argv[])
res = close(fd);
if (res < 0) {
- sprintf(ebuff, "sg_debug: trying to close %s ", argv[1]);
+ snprintf(ebuff, EBUFF_SZ, "sg_debug: trying to close %s ", argv[1]);
perror(ebuff);
return 1;
}
diff --git a/sg_inq.c b/sg_inq.c
index 371f67a4..f978d8c6 100644
--- a/sg_inq.c
+++ b/sg_inq.c
@@ -12,7 +12,7 @@
#include "sg_err.h"
/* A utility program for the Linux OS SCSI generic ("sg") device driver.
-* Copyright (C) 2000 D. Gilbert
+* Copyright (C) 2000-2002 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)
@@ -24,7 +24,7 @@
*/
-static char * version_str = "0.15 20011221";
+static char * version_str = "0.16 20020114";
/* #define SG_DEBUG */
@@ -36,6 +36,8 @@ static char * version_str = "0.15 20011221";
#define INQUIRY_CMDLEN 6
#define MX_ALLOC_LEN 255
+#define EBUFF_SZ 256
+
#ifndef SCSI_IOCTL_GET_PCI
#define SCSI_IOCTL_GET_PCI 0x5387
#endif
@@ -77,9 +79,9 @@ static int do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
return 0;
default:
if (noisy) {
- char ebuff[256];
- sprintf(ebuff, "Inquiry error, CmdDt=%d, EVPD=%d, page_opcode=%x ",
- cmddt, evpd, pg_op);
+ char ebuff[EBUFF_SZ];
+ snprintf(ebuff, EBUFF_SZ, "Inquiry error, CmdDt=%d, "
+ "EVPD=%d, page_opcode=%x ", cmddt, evpd, pg_op);
sg_chk_n_print3(ebuff, &io_hdr);
}
return -1;
@@ -151,11 +153,12 @@ static void dStrHex(const char* str, int len)
}
+
int main(int argc, char * argv[])
{
int sg_fd, k, num, len;
char * file_name = 0;
- char ebuff[128];
+ char ebuff[EBUFF_SZ];
char buff[MX_ALLOC_LEN + 1];
unsigned char rsp_buff[MX_ALLOC_LEN + 1];
unsigned int num_opcode = 0;
@@ -211,7 +214,7 @@ int main(int argc, char * argv[])
if (do_pci)
oflags = O_RDWR;
if ((sg_fd = open(file_name, oflags)) < 0) {
- sprintf(ebuff, "sg_inq: error opening file: %s", file_name);
+ snprintf(ebuff, EBUFF_SZ, "sg_inq: error opening file: %s", file_name);
perror(ebuff);
return 1;
}
diff --git a/sg_map.c b/sg_map.c
index 787fab68..8043d4f3 100644
--- a/sg_map.c
+++ b/sg_map.c
@@ -79,8 +79,8 @@ typedef struct my_scsi_idlun {
} My_scsi_idlun;
-#define EBUFF_LEN 256
-static char ebuff[EBUFF_LEN];
+#define EBUFF_SZ 256
+static char ebuff[EBUFF_SZ];
static void scan_dev_type(const char * leadin, int max_dev, int do_numeric,
int lin_dev_type, int last_sg_ind);
@@ -206,7 +206,7 @@ int main(int argc, char * argv[])
for (k = 0, res = 0; (k < MAX_SG_DEVS) && (num_errors < MAX_ERRORS);
++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
if (res < 0) {
- sprintf(ebuff, "Error closing %s ", fname);
+ snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname);
perror("sg_map: close error");
return 1;
}
@@ -228,7 +228,7 @@ int main(int argc, char * argv[])
else {
if (EACCES == errno)
eacces_err = 1;
- sprintf(ebuff, "Error opening %s ", fname);
+ snprintf(ebuff, EBUFF_SZ, "Error opening %s ", fname);
perror(ebuff);
++num_errors;
continue;
@@ -236,7 +236,8 @@ int main(int argc, char * argv[])
}
res = ioctl(sg_fd, SG_GET_SCSI_ID, &map_arr[k].sg_dat);
if (res < 0) {
- sprintf(ebuff, "device %s failed on sg ioctl, skip", fname);
+ snprintf(ebuff, EBUFF_SZ,
+ "device %s failed on sg ioctl, skip", fname);
perror(ebuff);
++num_errors;
continue;
@@ -363,7 +364,7 @@ static void scan_dev_type(const char * leadin, int max_dev, int do_numeric,
for (k = 0, res = 0; (k < max_dev) && (num_errors < MAX_ERRORS);
++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
if (res < 0) {
- sprintf(ebuff, "Error closing %s ", fname);
+ snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname);
perror("sg_map: close error");
#ifndef IGN_CLOSE_ERR
return;
@@ -394,7 +395,7 @@ static void scan_dev_type(const char * leadin, int max_dev, int do_numeric,
continue;
}
else {
- sprintf(ebuff, "Error opening %s ", fname);
+ snprintf(ebuff, EBUFF_SZ, "Error opening %s ", fname);
perror(ebuff);
++num_errors;
continue;
@@ -403,8 +404,8 @@ static void scan_dev_type(const char * leadin, int max_dev, int do_numeric,
res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun);
if (res < 0) {
- sprintf(ebuff, "device %s failed on scsi ioctl(idlun), skip",
- fname);
+ snprintf(ebuff, EBUFF_SZ,
+ "device %s failed on scsi ioctl(idlun), skip", fname);
perror(ebuff);
++num_errors;
#ifdef DEBUG
@@ -414,8 +415,8 @@ static void scan_dev_type(const char * leadin, int max_dev, int do_numeric,
}
res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no);
if (res < 0) {
- sprintf(ebuff, "device %s failed on scsi ioctl(bus_number), skip",
- fname);
+ snprintf(ebuff, EBUFF_SZ,
+ "device %s failed on scsi ioctl(bus_number), skip", fname);
perror(ebuff);
++num_errors;
#ifdef DEBUG
@@ -481,9 +482,9 @@ static int do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
return 0;
default:
if (noisy) {
- char ebuff[256];
- sprintf(ebuff, "Inquiry error, CmdDt=%d, EVPD=%d, page_opcode=%x ",
- cmddt, evpd, pg_op);
+ char ebuff[EBUFF_SZ];
+ snprintf(ebuff, EBUFF_SZ, "Inquiry error, CmdDt=%d, "
+ "EVPD=%d, page_opcode=%x ", cmddt, evpd, pg_op);
sg_chk_n_print3(ebuff, &io_hdr);
}
return -1;
diff --git a/sg_read.8 b/sg_read.8
new file mode 100644
index 00000000..0d226389
--- /dev/null
+++ b/sg_read.8
@@ -0,0 +1,108 @@
+.TH SG_READ "8" "February 2002" "sg3_utils-0.98" SG3_UTILS
+.SH NAME
+sg_read \- read blocks of data continually from same offset
+.SH SYNOPSIS
+.B sg_read
+[\fIOPTION\fR]...
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Read data from a Linux SCSI generic (sg) device, a raw devices or
+a normal file with each read command issued to the same offset. This
+will test (or time) disk caching and/or SCSI (or some other) bus
+throughput.
+.TP
+bpt=BLOCKS
+each read operation will be made using this number of blocks (or less if
+near the end of count). Default is 128. Note also that each read operation
+starts at the same offset (as given by skip or 0).
+.TP
+bs=BYTES
+this
+.B must
+be the block size of the physical device (defaults to 512)
+.TP
+cdbsz=6 | 10 | 12 | 16
+size of SCSI READ commands issued on sg device names.
+Default is 10 byte SCSI READ command blocks
+.TP
+count=BLOCKS
+read this number of blocks. This argument must be given
+.TP
+dio=0 | 1
+default is 0 which selects indirect IO. Value of 1 attempts direct
+IO which, if not available, falls back to indirect IO and notes this
+at completion. If direct IO is selected and /proc/scsi/sg/allow_dio
+has the value of 0 then a warning is issued (and indirect IO is performed)
+.TP
+if=FILE
+read from this FILE. This argument must be given
+.TP
+mmap= 0 | 1
+default is 0 which selects indirect IO. Value of 1 causes memory mapped
+IO to be performed. Selecting both dio and mmap is an error
+.TP
+skip=BLOCKS
+all read operations will start offset by BLOCKS bs-sized blocks
+from the start of input (file or device)
+.TP
+time=0 | 1 | 2
+When 0 (default) doesn't perform timing.
+when 1, times transfer and does throughput calculation, starting at the
+first issued command until completion. When 2, times transfer and does
+throughput calculation, starting at the second issued command until
+completion
+.PP
+The input file must be either a sg device, a raw device or a normal file.
+A raw device must be bound to a block device prior to using sg_raw.
+See
+.B raw(8)
+for more information about binding raw devices.
+.PP
+BYTES and BLOCKS may be followed by the following multiplicative suffixes:
+c C *1; b B *512; k *1,024; K *1,000; m *1,048,576; M *1,000,000;
+g *1,073,741,824; and G *1,000,000,000
+.PP
+Data usually gets to the user space in a 2 stage process: first the
+SCSI adapter DMAs into kernel buffers and then the sg driver copies
+this data into user memory.
+This is called "indirect IO" and there is a "dio" option to select
+"direct IO" which will DMA directly into user memory. Due to some
+issues "direct IO" is disabled in the sg driver and needs a
+configuration change to activate it. This is typically done with
+"echo 1 > /proc/scsi/sg/allow_dio". An alternate way to avoid the
+2 stage copy is to select memory mapped IO.
+.SH EXAMPLES
+.PP
+Let us assume that /dev/sg0 is a disk and we wish to time the disk's
+cache performance.
+.PP
+ sg_read if=/dev/sg0 bs=512 count=1M mmap=1 time=2
+.PP
+This command will continually read 128 512 byte blocks from block 0.
+The "128" is the default value for "bpt" while "block 0" is chosen
+because the "skip" argument was not given. This will continue until
+1,000,000 blocks are read. The idea behind using "time=2" is that the
+first 64 KB read operation will involve reading the magnetic media
+while the remaining read operations will "hit" the disk's cache. The
+output of thid command will look like this:
+.PP
+ time from second command to end was 8.51 secs, 60.19 MB/sec
+.br
+ 1000000+0 records in, SCSI commands issued: 7813
+.SH AUTHORS
+Written by Doug Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert@interlog.com>.
+.SH COPYRIGHT
+Copyright \(co 2000-2002 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+To time streaming media read or write time see
+.B sg_dd
+is in the sg3_utils package. The lmbench package contains
+.B lmdd
+which is also interesting.
+.B raw(8), dd(1)
diff --git a/sg_read.c b/sg_read.c
index 335bb4e5..4545cbe8 100644
--- a/sg_read.c
+++ b/sg_read.c
@@ -21,7 +21,7 @@ typedef unsigned char u_char; /* horrible, for scsi.h */
#include "llseek.h"
/* A utility program for the Linux OS SCSI generic ("sg") device driver.
-* Copyright (C) 2001 D. Gilbert
+* Copyright (C) 2001, 2002 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)
@@ -48,10 +48,14 @@ typedef unsigned char u_char; /* horrible, for scsi.h */
*/
-static const char * version_str = "0.92 20011210";
+static const char * version_str = "0.94 20020204";
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
+#define DEF_SCSI_CDBSZ 10
+#define MAX_SCSI_CDBSZ 16
+
+#define ME "sg_read: "
#ifndef SG_FLAG_MMAP_IO
#define SG_FLAG_MMAP_IO 4
@@ -143,7 +147,8 @@ void usage()
fprintf(stderr, "Usage: "
"sg_read if=<infile> [skip=<num>] [bs=<num>] [bpt=<num>] "
"count=<num>\n"
- " [dio=<num>] [mmap=<num>] [time=<num>]\n"
+ " [dio=<num>] [mmap=<num>] [time=<num>]"
+ " [cdbsz=<6|10|12|16>]\n"
" 'if' is an sg or raw device, or a seekable file (not stdin)\n"
" 'bs' must match sector size (when 'if' is sg device) "
"(def=512)\n"
@@ -154,30 +159,103 @@ void usage()
" 'dio' is direct IO, 1->attempt, 0->indirect IO (def)\n"
" 'mmap' is mmap-ed IO, 1->perform, 0->indirect IO (def)\n"
" 'time' 0->do nothing(def), 1->time from 1st cmd, 2->time "
- "from 2nd cmd\n");
+ "from 2nd cmd\n"
+ " 'cdbsz' size of SCSI READ command (default is 10)\n");
fprintf(stderr, "\nVersion: %s\n", version_str);
}
+int sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz, unsigned int blocks,
+ unsigned int start_block)
+{
+ int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88};
+ int sz_ind;
+
+ memset(cdbp, 0, cdb_sz);
+ switch (cdb_sz) {
+ case 6:
+ sz_ind = 0;
+ cdbp[0] = (unsigned char)rd_opcode[sz_ind];
+ cdbp[1] |= (unsigned char)((start_block >> 16) & 0x1f);
+ cdbp[2] = (unsigned char)((start_block >> 8) & 0xff);
+ cdbp[3] = (unsigned char)(start_block & 0xff);
+ cdbp[4] = (256 == blocks) ? 0 : (unsigned char)blocks;
+ if (blocks > 256) {
+ fprintf(stderr, ME "for 6 byte commands, maximum number of "
+ "blocks is 256\n");
+ return 1;
+ }
+ if ((start_block + blocks - 1) & (~0x1fffff)) {
+ fprintf(stderr, ME "for 6 byte commands, can't address blocks"
+ " beyond %d\n", 0x1fffff);
+ return 1;
+ }
+ break;
+ case 10:
+ sz_ind = 1;
+ cdbp[0] = (unsigned char)rd_opcode[sz_ind];
+ cdbp[2] = (unsigned char)((start_block >> 24) & 0xff);
+ cdbp[3] = (unsigned char)((start_block >> 16) & 0xff);
+ cdbp[4] = (unsigned char)((start_block >> 8) & 0xff);
+ cdbp[5] = (unsigned char)(start_block & 0xff);
+ cdbp[7] = (unsigned char)((blocks >> 8) & 0xff);
+ cdbp[8] = (unsigned char)(blocks & 0xff);
+ if (blocks & (~0xffff)) {
+ fprintf(stderr, ME "for 10 byte commands, maximum number of "
+ "blocks is %d\n", 0xffff);
+ return 1;
+ }
+ break;
+ case 12:
+ sz_ind = 2;
+ cdbp[0] = (unsigned char)rd_opcode[sz_ind];
+ cdbp[2] = (unsigned char)((start_block >> 24) & 0xff);
+ cdbp[3] = (unsigned char)((start_block >> 16) & 0xff);
+ cdbp[4] = (unsigned char)((start_block >> 8) & 0xff);
+ cdbp[5] = (unsigned char)(start_block & 0xff);
+ cdbp[6] = (unsigned char)((blocks >> 24) & 0xff);
+ cdbp[7] = (unsigned char)((blocks >> 16) & 0xff);
+ cdbp[8] = (unsigned char)((blocks >> 8) & 0xff);
+ cdbp[9] = (unsigned char)(blocks & 0xff);
+ break;
+ case 16:
+ sz_ind = 3;
+ cdbp[0] = (unsigned char)rd_opcode[sz_ind];
+ /* can't cope with block number > 32 bits (yet) */
+ cdbp[6] = (unsigned char)((start_block >> 24) & 0xff);
+ cdbp[7] = (unsigned char)((start_block >> 16) & 0xff);
+ cdbp[8] = (unsigned char)((start_block >> 8) & 0xff);
+ cdbp[9] = (unsigned char)(start_block & 0xff);
+ cdbp[10] = (unsigned char)((blocks >> 24) & 0xff);
+ cdbp[11] = (unsigned char)((blocks >> 16) & 0xff);
+ cdbp[12] = (unsigned char)((blocks >> 8) & 0xff);
+ cdbp[13] = (unsigned char)(blocks & 0xff);
+ break;
+ default:
+ fprintf(stderr, ME "expected cdb size of 6, 10, 12, or 16 but got"
+ "=%d\n", cdb_sz);
+ return 1;
+ }
+ return 0;
+}
+
/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
2 -> try again */
int sg_bread(int sg_fd, unsigned char * buff, int blocks, int from_block,
- int bs, int * diop, int do_mmap)
+ int bs, int cdbsz, int * diop, int do_mmap)
{
- unsigned char rdCmd[10] = {0x28, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ unsigned char rdCmd[MAX_SCSI_CDBSZ];
unsigned char senseBuff[SENSE_BUFF_LEN];
sg_io_hdr_t io_hdr;
int res;
- rdCmd[2] = (unsigned char)((from_block >> 24) & 0xFF);
- rdCmd[3] = (unsigned char)((from_block >> 16) & 0xFF);
- rdCmd[4] = (unsigned char)((from_block >> 8) & 0xFF);
- rdCmd[5] = (unsigned char)(from_block & 0xFF);
- rdCmd[7] = (unsigned char)((blocks >> 8) & 0xff);
- rdCmd[8] = (unsigned char)(blocks & 0xff);
-
+ if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block)) {
+ fprintf(stderr, ME "bad cdb build, from_block=%d, blocks=%d\n",
+ from_block, blocks);
+ return -1;
+ }
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
io_hdr.interface_id = 'S';
- io_hdr.cmd_len = sizeof(rdCmd);
+ io_hdr.cmd_len = cdbsz;
io_hdr.cmdp = rdCmd;
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.dxfer_len = bs * blocks;
@@ -269,26 +347,31 @@ int get_num(char * buf)
}
}
+#define STR_SZ 1024
+#define INF_SZ 512
+#define EBUFF_SZ 512
+
int main(int argc, char * argv[])
{
int skip = 0;
int bs = 0;
int bpt = DEF_BLOCKS_PER_TRANSFER;
- char str[512];
+ char str[STR_SZ];
char * key;
char * buf;
- char inf[512];
+ char inf[INF_SZ];
int in_type = FT_OTHER;
int do_dio = 0;
int do_mmap = 0;
int do_time = 0;
+ int scsi_cdbsz = DEF_SCSI_CDBSZ;
int dio_incomplete = 0;
int res, k, t, buf_sz, dio_tmp, iters, orig_count;
int infd, blocks;
unsigned char * wrkBuff = NULL;
unsigned char * wrkPos;
- char ebuff[256];
+ char ebuff[EBUFF_SZ];
int blocks_per;
struct timeval start_tm, end_tm;
size_t psz = getpagesize();
@@ -301,8 +384,10 @@ int main(int argc, char * argv[])
}
for(k = 1; k < argc; k++) {
- if (argv[k])
- strcpy(str, argv[k]);
+ if (argv[k]) {
+ strncpy(str, argv[k], STR_SZ);
+ str[STR_SZ - 1] = '\0';
+ }
else
continue;
for(key = str, buf = key; *buf && *buf != '=';)
@@ -310,7 +395,7 @@ int main(int argc, char * argv[])
if (*buf)
*buf++ = '\0';
if (strcmp(key,"if") == 0)
- strcpy(inf, buf);
+ strncpy(inf, buf, INF_SZ);
else if (0 == strcmp(key,"bs"))
bs = get_num(buf);
else if (0 == strcmp(key,"bpt"))
@@ -325,6 +410,8 @@ int main(int argc, char * argv[])
do_mmap = get_num(buf);
else if (0 == strcmp(key,"time"))
do_time = get_num(buf);
+ else if (0 == strcmp(key,"cdbsz"))
+ scsi_cdbsz = get_num(buf);
else {
fprintf(stderr, "Unrecognized argument '%s'\n", key);
usage();
@@ -350,7 +437,7 @@ int main(int argc, char * argv[])
}
#ifdef SG_DEBUG
- fprintf(stderr, "sg_read: if=%s skip=%d count=%d\n", inf, skip, dd_count);
+ fprintf(stderr, ME "if=%s skip=%d count=%d\n", inf, skip, dd_count);
#endif
install_handler (SIGINT, interrupt_handler);
install_handler (SIGQUIT, interrupt_handler);
@@ -366,7 +453,8 @@ int main(int argc, char * argv[])
if (FT_SG == in_type) {
if ((infd = open(inf, O_RDWR)) < 0) {
- sprintf(ebuff, "sg_read: could not open %s for sg reading", inf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for sg reading", inf);
perror(ebuff);
return 1;
}
@@ -375,26 +463,27 @@ int main(int argc, char * argv[])
t = ((t / psz) + 1) * psz; /* round up to next pagesize */
res = ioctl(infd, SG_SET_RESERVED_SIZE, &t);
if (res < 0)
- perror("sg_read: SG_SET_RESERVED_SIZE error");
+ perror(ME "SG_SET_RESERVED_SIZE error");
res = ioctl(infd, SG_GET_VERSION_NUM, &t);
if ((res < 0) || (t < 30000)) {
- fprintf(stderr, "sg_read: sg driver prior to 3.x.y\n");
+ fprintf(stderr, ME "sg driver prior to 3.x.y\n");
return 1;
}
if (do_mmap && (t < 30122)) {
- fprintf(stderr, "sg_read: mmap-ed IO needs a sg driver version "
+ fprintf(stderr, ME "mmap-ed IO needs a sg driver version "
">= 3.1.22\n");
return 1;
}
}
else {
if (do_mmap) {
- fprintf(stderr, "sg_read: mmap-ed IO only support on sg "
+ fprintf(stderr, ME "mmap-ed IO only support on sg "
"devices\n");
return 1;
}
if ((infd = open(inf, O_RDONLY)) < 0) {
- sprintf(ebuff, "sg_read: could not open %s for reading", inf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for reading", inf);
perror(ebuff);
return 1;
}
@@ -403,8 +492,8 @@ int main(int argc, char * argv[])
offset *= bs; /* could exceed 32 bits here! */
if (llse_llseek(infd, offset, SEEK_SET) < 0) {
- sprintf(ebuff,
- "sg_read: couldn't skip to required position on %s", inf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "couldn't skip to required position on %s", inf);
perror(ebuff);
return 1;
}
@@ -428,7 +517,7 @@ int main(int argc, char * argv[])
wrkPos = mmap(NULL, bs * bpt, PROT_READ | PROT_WRITE,
MAP_SHARED, infd, 0);
if (MAP_FAILED == wrkPos) {
- perror("sg_read: error from mmap()");
+ perror(ME "error from mmap()");
return 1;
}
}
@@ -456,7 +545,8 @@ int main(int argc, char * argv[])
blocks = (dd_count > blocks_per) ? blocks_per : dd_count;
if (FT_SG == in_type) {
dio_tmp = do_dio;
- res = sg_bread(infd, wrkPos, blocks, skip, bs, &dio_tmp, do_mmap);
+ res = sg_bread(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
+ &dio_tmp, do_mmap);
if (1 == res) { /* ENOMEM, find what's available+try that */
if (ioctl(infd, SG_GET_RESERVED_SIZE, &buf_sz) < 0) {
perror("RESERVED_SIZE ioctls failed");
@@ -466,17 +556,17 @@ int main(int argc, char * argv[])
blocks = blocks_per;
fprintf(stderr,
"Reducing read to %d blocks per loop\n", blocks_per);
- res = sg_bread(infd, wrkPos, blocks, skip, bs, &dio_tmp,
- do_mmap);
+ res = sg_bread(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
+ &dio_tmp, do_mmap);
}
else if (2 == res) {
fprintf(stderr,
"Unit attention, media changed, continuing (r)\n");
- res = sg_bread(infd, wrkPos, blocks, skip, bs, &dio_tmp,
- do_mmap);
+ res = sg_bread(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
+ &dio_tmp, do_mmap);
}
if (0 != res) {
- fprintf(stderr, "sg_read failed, skip=%d\n", skip);
+ fprintf(stderr, ME "failed, skip=%d\n", skip);
break;
}
else {
@@ -491,7 +581,7 @@ int main(int argc, char * argv[])
offset *= bs; /* could exceed 32 bits here! */
if (llse_llseek(infd, offset, SEEK_SET) < 0) {
- perror("sg_read: could not reset skip position");
+ perror(ME "could not reset skip position");
break;
}
}
@@ -499,12 +589,12 @@ int main(int argc, char * argv[])
(EINTR == errno))
;
if (res < 0) {
- sprintf(ebuff, "sg_read: reading, skip=%d ", skip);
+ snprintf(ebuff, EBUFF_SZ, ME "reading, skip=%d ", skip);
perror(ebuff);
break;
}
else if (res < blocks * bs) {
- fprintf(stderr, "sg_read: short read: wanted/got=%d/%d bytes"
+ fprintf(stderr, ME "short read: wanted/got=%d/%d bytes"
", stop\n", blocks * bs, res);
blocks = res / bs;
if ((res % bs) > 0) {
diff --git a/sg_readcap.c b/sg_readcap.c
index dca1c556..4ed05b70 100644
--- a/sg_readcap.c
+++ b/sg_readcap.c
@@ -13,7 +13,7 @@
/* This code is does a SCSI READ CAPACITY command on the given device
and outputs the result.
-* Copyright (C) 1999 - 2001 D. Gilbert
+* Copyright (C) 1999 - 2002 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)
@@ -23,7 +23,7 @@
of Linux kernels no matter which of those environments it was
compiled and built under.
- Version 3.56 (20010115)
+ Version 3.57 (20020114)
10 byte READ CAPACITY command:
[0x25][ |lu][ ][ ][ ][ ][ ][ ][cntrl] {ignore PMI mode}
@@ -376,7 +376,7 @@ static int open_scsi_dev_as_sg(const char * devname)
int do_numeric = DEF_SCAN;
char name[MAX_FILENAME_LEN];
- strcpy(name, devname);
+ strncpy(name, devname, MAX_FILENAME_LEN);
if ((fd = open(name, O_RDONLY | O_NONBLOCK)) < 0) {
if (EACCES == errno) {
if ((fd = open(name, O_RDWR | O_NONBLOCK)) < 0)
diff --git a/archive/sg_reset.c b/sg_reset.c
index eafb84be..20a11d00 100644
--- a/archive/sg_reset.c
+++ b/sg_reset.c
@@ -11,7 +11,7 @@
/* Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
device driver.
-* Copyright (C) 1999 D. Gilbert
+* Copyright (C) 1999-2002 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)
@@ -20,7 +20,7 @@
This program send either device, bus or host resets to device,
or bus or host associated with the given sg device.
- Version 0.02 (20000105)
+ Version 0.52 (20020126)
*/
#ifndef SG_SCSI_RESET
@@ -38,11 +38,10 @@
int main(int argc, char * argv[])
{
- int sg_fd, res, k, j;
+ int sg_fd, res, k;
int do_device_reset = 0;
int do_bus_reset = 0;
int do_host_reset = 0;
- int do_wait = 0;
char * file_name = 0;
for (k = 1; k < argc; ++k) {
@@ -52,8 +51,6 @@ int main(int argc, char * argv[])
do_bus_reset = 1;
else if (0 == strcmp("-h", argv[k]))
do_host_reset = 1;
- else if (0 == strcmp("-w", argv[k]))
- do_wait = 1;
else if (*argv[k] == '-') {
printf("Unrecognized switch: %s\n", argv[k]);
file_name = 0;
@@ -64,28 +61,33 @@ int main(int argc, char * argv[])
}
if (0 == file_name) {
printf(
- "Usage: 'sg_reset [-d] [-b] [-h] [-w] <generic_device>'\n");
- printf(" where: -d attempt a scsi device reset\n");
- printf(" -b attempt a scsi bus reset\n");
+ "Usage: 'sg_reset [-d] [-b] [-h] <generic_device>'\n");
+ printf(" where: -d attempt a SCSI device reset\n");
+ printf(" -b attempt a SCSI bus reset\n");
printf(" -h attempt a host adapter reset\n");
- printf(" -w wait for one of the resets to complete\n");
printf(" {if no switch given then check if reset underway}\n");
return 1;
}
- sg_fd = open(file_name, O_RDWR);
+ sg_fd = open(file_name, O_RDWR | O_NONBLOCK);
if (sg_fd < 0) {
perror("sg_reset: open error");
return 1;
}
- /* Don't worry, being very careful not to write to a none-sg file ... */
+
k = SG_SCSI_RESET_NOTHING;
- if (do_device_reset)
+ if (do_device_reset) {
+ printf("sg_reset: starting device reset\n");
k = SG_SCSI_RESET_DEVICE;
- else if (do_bus_reset)
+ }
+ else if (do_bus_reset) {
+ printf("sg_reset: starting bus reset\n");
k = SG_SCSI_RESET_BUS;
- else if (do_host_reset)
+ }
+ else if (do_host_reset) {
+ printf("sg_reset: starting host reset\n");
k = SG_SCSI_RESET_HOST;
+ }
res = ioctl(sg_fd, SG_SCSI_RESET, &k);
if (res < 0) {
@@ -94,34 +96,24 @@ int main(int argc, char * argv[])
else if (EIO == errno)
printf("sg_reset: requested type of reset may not be available\n");
else if (EACCES == errno)
- printf("sg_reset: to do a reset needs root permission\n");
- else
+ printf("sg_reset: reset requires CAP_SYS_ADMIN (root) "
+ "permission\n");
+ else if (EINVAL == errno)
printf("sg_reset: SG_SCSI_RESET not supported\n");
+ else if (EIO == errno)
+ printf("sg_reset: scsi_reset_provider() call failed\n");
+ else
+ perror("sg_reset: SG_SCSI_RESET failed");
return 1;
}
if (SG_SCSI_RESET_NOTHING == k)
printf("sg_reset: did nothing, device is normal mode\n");
- else {
- if (SG_SCSI_RESET_DEVICE == k)
- printf("sg_reset: started device reset\n");
- else if (SG_SCSI_RESET_BUS == k)
- printf("sg_reset: started bus reset\n");
- else if (SG_SCSI_RESET_HOST == k)
- printf("sg_reset: started host reset\n");
-
- if (do_wait) {
- printf("waiting for the reset to complete...\n");
- j = 0;
- k = SG_SCSI_RESET_NOTHING;
- do {
- if (0 != j)
- sleep(1);
- res = ioctl(sg_fd, SG_SCSI_RESET, &k);
- ++j;
- } while ((res < 0) && (EBUSY == errno));
- printf(" ... reset seemingly completed\n");
- }
- }
+ else if (SG_SCSI_RESET_DEVICE == k)
+ printf("sg_reset: completed device reset\n");
+ else if (SG_SCSI_RESET_BUS == k)
+ printf("sg_reset: completed bus reset\n");
+ else if (SG_SCSI_RESET_HOST == k)
+ printf("sg_reset: completed host reset\n");
if (close(sg_fd) < 0) {
perror("sg_reset: close error");
diff --git a/sg_scan.c b/sg_scan.c
index ae20d780..dcfb2b3c 100644
--- a/sg_scan.c
+++ b/sg_scan.c
@@ -79,7 +79,7 @@ typedef struct my_sg_scsi_id {
int sg3_inq(int sg_fd, unsigned char * inqBuff, int do_extra);
#endif
-#define EBUFF_LEN 256
+#define EBUFF_SZ 256
static unsigned char inqCmdBlk [INQ_CMD_LEN] =
{0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
@@ -142,7 +142,7 @@ int main(int argc, char * argv[])
int num_silent = 0;
int eacces_err = 0;
char fname[64];
- char ebuff[EBUFF_LEN];
+ char ebuff[EBUFF_SZ];
My_scsi_idlun my_idlun;
int host_no;
int flags;
@@ -186,7 +186,7 @@ int main(int argc, char * argv[])
for (k = 0, res = 0; (k < 1000) && (num_errors < MAX_ERRORS);
++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
if (res < 0) {
- sprintf(ebuff, "Error closing %s ", fname);
+ snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname);
perror("sg_scan: close error");
return 1;
}
@@ -207,7 +207,7 @@ int main(int argc, char * argv[])
else {
if (EACCES == errno)
eacces_err = 1;
- sprintf(ebuff, "Error opening %s ", fname);
+ snprintf(ebuff, EBUFF_SZ, "Error opening %s ", fname);
perror(ebuff);
++num_errors;
continue;
@@ -215,15 +215,16 @@ int main(int argc, char * argv[])
}
res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun);
if (res < 0) {
- sprintf(ebuff, "device %s failed on scsi ioctl, skip", fname);
+ snprintf(ebuff, EBUFF_SZ,
+ "device %s failed on scsi ioctl, skip", fname);
perror(ebuff);
++num_errors;
continue;
}
res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no);
if (res < 0) {
- sprintf(ebuff, "device %s failed on scsi ioctl(2), skip",
- fname);
+ snprintf(ebuff, EBUFF_SZ, "device %s failed on scsi "
+ "ioctl(2), skip", fname);
perror(ebuff);
++num_errors;
continue;
@@ -231,7 +232,8 @@ int main(int argc, char * argv[])
#ifdef SG_EMULATED_HOST
res = ioctl(sg_fd, SG_EMULATED_HOST, &emul);
if (res < 0) {
- sprintf(ebuff, "device %s failed on sg ioctl(3), skip", fname);
+ snprintf(ebuff, EBUFF_SZ,
+ "device %s failed on sg ioctl(3), skip", fname);
perror(ebuff);
++num_errors;
continue;
@@ -253,7 +255,7 @@ int main(int argc, char * argv[])
res = ioctl(sg_fd, SG_GET_SCSI_ID, &m_id);
if (res < 0) {
- sprintf(ebuff, "device %s ioctls(4), skip", fname);
+ snprintf(ebuff, EBUFF_SZ, "device %s ioctls(4), skip", fname);
perror(ebuff);
++num_errors;
continue;
@@ -292,14 +294,14 @@ int main(int argc, char * argv[])
res = write(sg_fd, inqBuff, inqInLen);
if (res < 0) {
- sprintf(ebuff, "device %s writing, skip", fname);
+ snprintf(ebuff, EBUFF_SZ, "device %s writing, skip", fname);
perror(ebuff);
++num_errors;
continue;
}
res = read(sg_fd, inqBuff, inqOutLen);
if (res < 0) {
- sprintf(ebuff, "device %s reading, skip", fname);
+ snprintf(ebuff, EBUFF_SZ, "device %s reading, skip", fname);
perror(ebuff);
++num_errors;
continue;
diff --git a/sg_simple1.c b/sg_simple1.c
index 14748cca..5fde2068 100644
--- a/sg_simple1.c
+++ b/sg_simple1.c
@@ -24,7 +24,7 @@
Invocation: sg_simple1 [-x] <sg_device>
- Version 03.55 (991208)
+ Version 03.56 (20020113)
6 byte INQUIRY command:
[0x12][ |lu][pg cde][res ][al len][cntrl ]
@@ -38,6 +38,8 @@
#define INQ_CMD_LEN 6
#define TUR_CMD_LEN 6
+#define EBUFF_SZ 256
+
int main(int argc, char * argv[])
{
int sg_fd, k, ok;
@@ -48,7 +50,7 @@ int main(int argc, char * argv[])
unsigned char inqBuff[INQ_REPLY_LEN];
sg_io_hdr_t io_hdr;
char * file_name = 0;
- char ebuff[128];
+ char ebuff[EBUFF_SZ];
unsigned char sense_buffer[32];
int do_extra = 0;
@@ -74,7 +76,8 @@ int main(int argc, char * argv[])
}
if ((sg_fd = open(file_name, O_RDONLY)) < 0) {
- sprintf(ebuff, "sg_simple1: error opening file: %s", file_name);
+ snprintf(ebuff, EBUFF_SZ,
+ "sg_simple1: error opening file: %s", file_name);
perror(ebuff);
return 1;
}
diff --git a/sg_simple16.c b/sg_simple16.c
index a104f55f..7aa26202 100644
--- a/sg_simple16.c
+++ b/sg_simple16.c
@@ -19,15 +19,17 @@
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
- Invocation: sg_simple5 <sg_device>
+ Invocation: sg_simple16 <sg_device>
- Version 1.00 (20011123)
+ Version 1.02 (20020206)
*/
#define READ16_REPLY_LEN 512
#define READ16_CMD_LEN 16
+#define EBUFF_SZ 256
+
int main(int argc, char * argv[])
{
int sg_fd, k, ok;
@@ -35,7 +37,7 @@ int main(int argc, char * argv[])
{0x88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0};
sg_io_hdr_t io_hdr;
char * file_name = 0;
- char ebuff[128];
+ char ebuff[EBUFF_SZ];
unsigned char inBuff[READ16_REPLY_LEN];
unsigned char sense_buffer[32];
@@ -54,18 +56,19 @@ int main(int argc, char * argv[])
}
}
if (0 == file_name) {
- printf("Usage: 'sg_simple5 <sg_device>'\n");
+ printf("Usage: 'sg_simple16 <sg_device>'\n");
return 1;
}
if ((sg_fd = open(file_name, O_RDWR)) < 0) {
- sprintf(ebuff, "sg_simple5: error opening file: %s", file_name);
+ snprintf(ebuff, EBUFF_SZ,
+ "sg_simple16: error opening file: %s", file_name);
perror(ebuff);
return 1;
}
/* Just to be safe, check we have a new sg device by trying an ioctl */
if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
- printf("sg_simple5: %s doesn't seem to be an new sg device\n",
+ printf("sg_simple16: %s doesn't seem to be an new sg device\n",
file_name);
close(sg_fd);
return 1;
@@ -88,7 +91,7 @@ int main(int argc, char * argv[])
/* io_hdr.usr_ptr = NULL; */
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
- perror("sg_simple5: Inquiry SG_IO ioctl error");
+ perror("sg_simple16: Inquiry SG_IO ioctl error");
close(sg_fd);
return 1;
}
diff --git a/sg_simple2.c b/sg_simple2.c
index b6753cfa..1e1e41e6 100644
--- a/sg_simple2.c
+++ b/sg_simple2.c
@@ -38,6 +38,8 @@
#define INQ_CMD_LEN 6
#define TUR_CMD_LEN 6
+#define EBUFF_SZ 256
+
int main(int argc, char * argv[])
{
@@ -49,7 +51,7 @@ int main(int argc, char * argv[])
unsigned char inqBuff[INQ_REPLY_LEN];
sg_io_hdr_t io_hdr;
char * file_name = 0;
- char ebuff[128];
+ char ebuff[EBUFF_SZ];
unsigned char sense_buffer[32];
int do_extra = 0;
@@ -75,7 +77,8 @@ int main(int argc, char * argv[])
}
if ((sg_fd = open(file_name, O_RDONLY)) < 0) {
- sprintf(ebuff, "sg_simple2: error opening file: %s", file_name);
+ snprintf(ebuff, EBUFF_SZ,
+ "sg_simple2: error opening file: %s", file_name);
perror(ebuff);
return 1;
}
diff --git a/sg_simple3.c b/sg_simple3.c
index dc52b665..3abec270 100644
--- a/sg_simple3.c
+++ b/sg_simple3.c
@@ -25,7 +25,7 @@
Invocation: sg_simple3 [-x] <sg_device>
- Version 03.56 (20000323)
+ Version 03.57 (20020113)
6 byte INQUIRY command:
[0x12][ |lu][pg cde][res ][al len][cntrl ]
@@ -43,6 +43,8 @@
#define INQ_CMD_LEN 6
#define TUR_CMD_LEN 6
+#define EBUFF_SZ 256
+
int main(int argc, char * argv[])
{
int sg_fd, k, ok;
@@ -57,7 +59,7 @@ int main(int argc, char * argv[])
char inqPRevBuff[INQ_REPLY_PREV_LEN];
sg_io_hdr_t io_hdr;
char * file_name = 0;
- char ebuff[128];
+ char ebuff[EBUFF_SZ];
unsigned char sense_buffer[32];
int do_extra = 0;
@@ -83,7 +85,8 @@ int main(int argc, char * argv[])
}
if ((sg_fd = open(file_name, O_RDONLY)) < 0) {
- sprintf(ebuff, "sg_simple3: error opening file: %s", file_name);
+ snprintf(ebuff, EBUFF_SZ,
+ "sg_simple3: error opening file: %s", file_name);
perror(ebuff);
return 1;
}
diff --git a/sg_simple4.c b/sg_simple4.c
index 5af42de3..7798fed6 100644
--- a/sg_simple4.c
+++ b/sg_simple4.c
@@ -24,7 +24,7 @@
Invocation: sg_simple4 [-x] <sg_device>
- Version 1.00 (20011123)
+ Version 1.01 (20020113)
6 byte INQUIRY command:
[0x12][ |lu][pg cde][res ][al len][cntrl ]
@@ -42,6 +42,8 @@
#define INQ_CMD_LEN 6
#define TUR_CMD_LEN 6
+#define EBUFF_SZ 256
+
int main(int argc, char * argv[])
{
int sg_fd, k, ok;
@@ -53,7 +55,7 @@ int main(int argc, char * argv[])
unsigned char * inqBuff2;
sg_io_hdr_t io_hdr;
char * file_name = 0;
- char ebuff[128];
+ char ebuff[EBUFF_SZ];
unsigned char sense_buffer[32];
int do_extra = 0;
@@ -79,7 +81,8 @@ int main(int argc, char * argv[])
}
if ((sg_fd = open(file_name, O_RDWR)) < 0) {
- sprintf(ebuff, "sg_simple4: error opening file: %s", file_name);
+ snprintf(ebuff, EBUFF_SZ,
+ "sg_simple4: error opening file: %s", file_name);
perror(ebuff);
return 1;
}
@@ -95,8 +98,8 @@ int main(int argc, char * argv[])
PROT_READ rather than PROT_READ | PROT_WRITE */
inqBuff = mmap(NULL, 8000, PROT_READ | PROT_WRITE, MAP_SHARED, sg_fd, 0);
if (MAP_FAILED == inqBuff) {
- sprintf(ebuff, "sg_simple4: error using mmap() on file: %s",
- file_name);
+ snprintf(ebuff, EBUFF_SZ, "sg_simple4: error using mmap() on "
+ "file: %s", file_name);
perror(ebuff);
return 1;
}
@@ -203,8 +206,8 @@ int main(int argc, char * argv[])
#if 1
inqBuff2 = mmap(NULL, 8000, PROT_READ | PROT_WRITE, MAP_SHARED, sg_fd, 0);
if (MAP_FAILED == inqBuff2) {
- sprintf(ebuff, "sg_simple4: error using mmap() 2 on file: %s",
- file_name);
+ snprintf(ebuff, EBUFF_SZ, "sg_simple4: error using mmap() 2 on "
+ "file: %s", file_name);
perror(ebuff);
return 1;
}
diff --git a/sg_turs.c b/sg_turs.c
index fe8175c7..0d153f00 100644
--- a/sg_turs.c
+++ b/sg_turs.c
@@ -15,7 +15,7 @@
data transfer (and no REQUEST SENSE command iff the unit is ready)
then this can be used for timing per SCSI command overheads.
-* Copyright (C) 2000 D. Gilbert
+* Copyright (C) 2000-2002 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)
@@ -26,7 +26,7 @@
'c','C' *1 'b','B' *512 'k' *1024 'K' *1000
'm' *(1024^2) 'M' *(1000^2) 'g' *(1024^3) 'G' *(1000^3)
- Version 03.05 (20010914)
+ Version 03.06 (20020114)
6 byte TEST UNIT READY command:
[0x00][ |lu][res ][res ][res ][res ]
@@ -72,6 +72,9 @@ int get_num(char * buf)
}
}
+#define EBUFF_SZ 256
+
+
int main(int argc, char * argv[])
{
int sg_fd, k;
@@ -79,7 +82,7 @@ int main(int argc, char * argv[])
{0x00, 0, 0, 0, 0, 0};
sg_io_hdr_t io_hdr;
char * file_name = 0;
- char ebuff[128];
+ char ebuff[EBUFF_SZ];
unsigned char sense_buffer[32];
int num_turs = 0;
int num_errs = 0;
@@ -112,7 +115,8 @@ int main(int argc, char * argv[])
}
if ((sg_fd = open(file_name, O_RDONLY)) < 0) {
- sprintf(ebuff, "sg_turs: error opening file: %s", file_name);
+ snprintf(ebuff, EBUFF_SZ,
+ "sg_turs: error opening file: %s", file_name);
perror(ebuff);
return 1;
}
diff --git a/sginfo.c b/sginfo.c
index 930f4dff..2298b2b5 100644
--- a/sginfo.c
+++ b/sginfo.c
@@ -88,6 +88,9 @@
* sizeof(buffer) (which is sizeof(char*) == 4 or 32 bit archs)
* was used incorrectly all over the place. Fixed.
* [version 1.95]
+ *
+ * Douglas Gilbert (dgilbert@interlog.com)
+ * 20020113 snprintf() type cleanup
*/
#include <stdio.h>
@@ -383,16 +386,21 @@ static int putnbyte(unsigned char *pnt, unsigned int value,
return 0;
}
+#define REASON_SZ 128
+
static void check_parm_type(int i)
{
- char reason[128];
+ char reason[REASON_SZ];
+
if (i == 1 && is_hex[next_parameter] != 1) {
- sprintf (reason, "simple number (pos %i) instead of @ hexdatafield: %llu",
+ snprintf(reason, REASON_SZ,
+ "simple number (pos %i) instead of @ hexdatafield: %llu",
next_parameter, replacement_values[next_parameter]);
usage (reason, 1);
}
if (i != 1 && is_hex[next_parameter]) {
- sprintf (reason, "@ hexdatafield (pos %i) instead of a simple number: %llu",
+ snprintf(reason, REASON_SZ,
+ "@ hexdatafield (pos %i) instead of a simple number: %llu",
next_parameter, replacement_values[next_parameter]);
usage (reason, 1);
}
@@ -943,8 +951,8 @@ trytenbyte:
i = 0;
if ((buffer[9] & 7) == 4) {
while (len > 0) {
- sprintf((char *)buffer, "%6d:%3u:%8d", getnbyte(df, 3),
- df[3], getnbyte(df + 4, 4));
+ snprintf((char *)buffer, 40, "%6d:%3u:%8d", getnbyte(df, 3),
+ df[3], getnbyte(df + 4, 4));
printf("%19s", (char *)buffer);
len -= 8;
df += 8;
@@ -957,8 +965,8 @@ trytenbyte:
}
} else if ((buffer[9] & 7) == 5) {
while (len > 0) {
- sprintf((char *)buffer, "%6d:%2u:%5d", getnbyte(df, 3),
- df[3], getnbyte(df + 4, 4));
+ snprintf((char *)buffer, 40, "%6d:%2u:%5d", getnbyte(df, 3),
+ df[3], getnbyte(df + 4, 4));
printf("%15s", (char *)buffer);
len -= 8;
df += 8;
@@ -1177,7 +1185,7 @@ static int do_user_page(int page_code, int page_no)
name = "Vendor specific";
for (i = 2; i < pagestart[1]+2; i++)
{
- char nm[8]; sprintf (nm, "%02x", i);
+ char nm[8]; snprintf (nm, 8, "%02x", i);
hexdatafield (pagestart + i, 1, nm);
}
@@ -1337,15 +1345,18 @@ typedef struct my_scsi_idlun
} My_scsi_idlun;
+#define MDEV_NAME_SZ 256
+
static void make_dev_name(char * fname, int k, int do_numeric)
{
- char buff[64];
-
- strcpy(fname, "/dev/sg");
- if (do_numeric) {
- sprintf(buff, "%d", k);
- strcat(fname, buff);
- }
+ char buff[MDEV_NAME_SZ];
+ size_t len;
+
+ strncpy(fname, "/dev/sg", MDEV_NAME_SZ);
+ fname[MDEV_NAME_SZ - 1] = '\0';
+ len = strlen(fname);
+ if (do_numeric)
+ snprintf(buff + len, MDEV_NAME_SZ - len, "%d", k);
else {
if (k <= 26) {
buff[0] = 'a' + (char)k;
@@ -1373,13 +1384,15 @@ char *devices[] =
static Sg_map sg_map_arr[(sizeof(devices) / sizeof(char *)) + 1];
+#define EBUFF_SZ 256
+
/* Print out a list of the known devices on the system */
static void show_devices()
{
int k, j, fd, err, bus;
My_scsi_idlun m_idlun;
- char name[64];
- char ebuff[256];
+ char name[MDEV_NAME_SZ];
+ char ebuff[EBUFF_SZ];
int do_numeric = 1;
for (k = 0, j = 0; k < sizeof(devices) / sizeof(char *); k++) {
@@ -1388,14 +1401,16 @@ static void show_devices()
continue;
err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &(sg_map_arr[j].bus));
if (err < 0) {
- sprintf(ebuff, "SCSI(1) ioctl on %s failed", devices[k]);
+ snprintf(ebuff, EBUFF_SZ,
+ "SCSI(1) ioctl on %s failed", devices[k]);
perror(ebuff);
close(fd);
continue;
}
err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun);
if (err < 0) {
- sprintf(ebuff, "SCSI(2) ioctl on %s failed", devices[k]);
+ snprintf(ebuff, EBUFF_SZ,
+ "SCSI(2) ioctl on %s failed", devices[k]);
perror(ebuff);
close(fd);
continue;
@@ -1428,7 +1443,8 @@ static void show_devices()
continue; /* step over if O_EXCL already on it */
else {
#if 0
- sprintf(ebuff, "open on %s failed (%d)", name, errno);
+ snprintf(ebuff, EBUFF_SZ,
+ "open on %s failed (%d)", name, errno);
perror(ebuff);
#endif
break;
@@ -1437,14 +1453,14 @@ static void show_devices()
}
err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus);
if (err < 0) {
- sprintf(ebuff, "SCSI(3) ioctl on %s failed", name);
+ snprintf(ebuff, EBUFF_SZ, "SCSI(3) ioctl on %s failed", name);
perror(ebuff);
close(fd);
continue;
}
err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun);
if (err < 0) {
- sprintf(ebuff, "SCSI(3) ioctl on %s failed", name);
+ snprintf(ebuff, EBUFF_SZ, "SCSI(3) ioctl on %s failed", name);
perror(ebuff);
close(fd);
continue;
@@ -1634,14 +1650,17 @@ static int show_pages(int page_code)
return 0;
}
+#define DEVNAME_SZ 256
+
static int open_sg_dev(char * devname)
{
int fd, err, bus, bbus, k;
My_scsi_idlun m_idlun, mm_idlun;
int do_numeric = 0;
- char name[128];
+ char name[DEVNAME_SZ];
- strcpy(name, devname);
+ strncpy(name, devname, DEVNAME_SZ);
+ name[DEVNAME_SZ - 1] = '\0';
fd = open(name, O_RDONLY);
if (fd < 0)
return fd;
@@ -1897,7 +1916,7 @@ int main(int argc, char *argv[])
notch = 1;
/* fall through */
case 'v':
- fprintf(stdout, " Sginfo version 1.95\n");
+ fprintf(stdout, " Sginfo version 1.96\n");
break;
default:
fprintf(stdout, "Unknown option '-%c' (ascii %02xh)\n", c, c);
diff --git a/sgm_dd.8 b/sgm_dd.8
new file mode 100644
index 00000000..4d18f877
--- /dev/null
+++ b/sgm_dd.8
@@ -0,0 +1,121 @@
+.TH SGM_DD "8" "January 2002" "sg3_utils-0.98" SG3_UTILS
+.SH NAME
+sgm_dd \- copies data to and from sg and raw devices
+.SH SYNOPSIS
+.B sgm_dd
+[\fIOPTION\fR]...
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Copy data to and from Linux SCSI generic (sg) and raw devices
+using memory mapped IO. Similar syntax and semantics to
+.B dd(1)
+but does not perform any conversions.
+.TP
+bpt=BLOCKS
+each IO transaction will be made using this number of blocks (or less if
+near the end of count). Default is 128.
+.TP
+bs=BYTES
+this
+.B must
+be the block size of the physical device. Note that this differs from
+.B dd(1)
+which permits "bs" to be an integral multiple. Default is 512 which
+is usually correct for disks but incorrect for cdroms (which normally
+have 2048 byte blocks).
+.TP
+count=BLOCKS
+copy this number of blocks. Default is minimum number that sg devices
+return from READ CAPACITY (if that works) or 0
+.TP
+ibs=BYTES
+if given must be the same as bs
+.TP
+if=FILE
+read from FILE instead of stdin. A file name of - is taken to be stdin
+.TP
+obs=BYTES
+if given must be the same as bs
+.TP
+of=FILE
+write to FILE instead of stdout. A file name of - is taken to be stdout
+.TP
+seek=BLOCKS
+skip BLOCKS bs-sized blocks at start of output
+.TP
+skip=BLOCKS
+skip BLOCKS bs-sized blocks at start of input
+.TP
+time=0 | 1
+when 1, times transfer and does throughput calculation, outputting the
+results (to stderr) at completion. When 0 (default) doesn't perform timing
+.TP
+--version
+outputs version number information and exits
+.PP
+Either the input file or the output file must be a sg or raw device.
+A raw device must be bound to a block device prior to using sgm_dd.
+See
+.B raw(8)
+for more information about binding raw devices. To be safe, the sg device
+mapping to SCSI block devices should be checked with "cat /proc/scsi/scsi"
+before use.
+.PP
+The count is only deduced for sg devices (minimum > 0 if both input and
+output are sg devices) otherwise it defaults to 0. This is for safety!
+Raw device partition information can often be found with
+.B fdisk(8)
+[the "-ul" argument is useful in this respect].
+.PP
+BYTES and BLOCKS may be followed by the following multiplicative suffixes:
+c C *1; b B *512; k *1,024; K *1,000; m *1,048,576; M *1,000,000;
+g *1,073,741,824; and G *1,000,000,000
+.PP
+Data usually gets to the user space in a 2 stage process: first the
+SCSI adapter DMAs into kernel buffers and then the sg driver copies
+this data into user memory (write operations reverse this sequence).
+With memory mapped IO a kernel buffer reserved by sg is memory mapped
+(see the
+.B mmap(2)
+system call) into the user space. When this is done
+the second (redundant) copy from kernel buffers to user space is
+not needed. Hence the transfer is faster and requires less "grunt"
+from the CPU.
+.PP
+All informative, warning and error output is sent to stderr so that
+dd's output file can be stdout and remain unpolluted. If no options
+are given, then the usage message is output and nothing else happens.
+.SH EXAMPLES
+.PP
+See the examples given in the man page for
+.B sg_dd(8).
+.SH NOTE
+For sg devices this command issues READ_10 and WRITE_10 SCSI commands which
+are appropriate for disks and CDROM players. Those commands are not
+formatted correctly for tape devices so sgm_dd should not be used on
+tape devices.
+.SH SIGNALS
+The signal handling has been borrowed from dd: SIGINT, SIGQUIT and
+SIGPIPE output the number of remaining blocks to be transferred and
+the records in + out counts; then they have their default action.
+SIGUSR1 causes the same information to be output yet the copy continues.
+All output caused by signals is sent to stderr.
+.SH AUTHORS
+Written by Doug Gilbert and Peter Allworth.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert@interlog.com>.
+.SH COPYRIGHT
+Copyright \(co 2000-2002 Douglas Gilbert
+.br
+This software is distributed under the GPL version 2. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+The simplest variant of this command is called
+.B sg_dd.
+A POSIX threads version of this command called
+.B sgp_dd
+is in the sg3_utils package. The lmbench package contains
+.B lmdd
+which is also interesting.
+.B raw(8), dd(1)
diff --git a/sgm_dd.c b/sgm_dd.c
index 66ec7d46..d248faff 100644
--- a/sgm_dd.c
+++ b/sgm_dd.c
@@ -13,6 +13,7 @@
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/mman.h>
+#include <sys/time.h>
#include <linux/major.h>
typedef unsigned char u_char; /* horrible, for scsi.h */
#include "sg_include.h"
@@ -20,7 +21,7 @@ typedef unsigned char u_char; /* horrible, for scsi.h */
#include "llseek.h"
/* A utility program for the Linux OS SCSI generic ("sg") device driver.
-* Copyright (C) 1999 - 2001 D. Gilbert and P. Allworth
+* Copyright (C) 1999 - 2002 D. Gilbert and P. Allworth
* 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)
@@ -49,12 +50,16 @@ typedef unsigned char u_char; /* horrible, for scsi.h */
This version should compile with Linux sg drivers with version numbers
>= 30000 .
-
- Version 1.01 20011205
*/
+static char * version_str = "1.03 20020204";
+
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
+#define DEF_SCSI_CDBSZ 10
+#define MAX_SCSI_CDBSZ 16
+
+#define ME "sgm_dd: "
// #define SG_DEBUG
@@ -142,10 +147,13 @@ void usage()
{
fprintf(stderr, "Usage: "
"sgm_dd [if=<infile>] [skip=<n>] [of=<ofile>] [seek=<n>]\n"
- " [bs=<num>] [bpt=<num>] [count=<n>]\n"
+ " [bs=<num>] [bpt=<num>] [count=<n>] [time=<n>]\n"
+ " [cdbsz=<6|10|12|16>] [--version]\n"
" either 'if' or 'of' must be a sg or raw device\n"
" 'bs' must be device block size (default 512)\n"
- " 'bpt' is blocks_per_transfer (default is 128)\n");
+ " 'bpt' is blocks_per_transfer (default is 128)\n"
+ " 'time' 0->no timing(def), 1->time plus calculate throughput\n"
+ " 'cdbsz' size of SCSI READ or WRITE command (default is 10)\n");
}
/* Return of 0 -> success, -1 -> failure, 2 -> try again */
@@ -186,26 +194,103 @@ int read_capacity(int sg_fd, int * num_sect, int * sect_sz)
return 0;
}
+int sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz, unsigned int blocks,
+ unsigned int start_block, int write_true)
+{
+ int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88};
+ int wr_opcode[] = {0xa, 0x2a, 0xaa, 0x8a};
+ int sz_ind;
+
+ memset(cdbp, 0, cdb_sz);
+ switch (cdb_sz) {
+ case 6:
+ sz_ind = 0;
+ cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+ rd_opcode[sz_ind]);
+ cdbp[1] |= (unsigned char)((start_block >> 16) & 0x1f);
+ cdbp[2] = (unsigned char)((start_block >> 8) & 0xff);
+ cdbp[3] = (unsigned char)(start_block & 0xff);
+ cdbp[4] = (256 == blocks) ? 0 : (unsigned char)blocks;
+ if (blocks > 256) {
+ fprintf(stderr, ME "for 6 byte commands, maximum number of "
+ "blocks is 256\n");
+ return 1;
+ }
+ if ((start_block + blocks - 1) & (~0x1fffff)) {
+ fprintf(stderr, ME "for 6 byte commands, can't address blocks"
+ " beyond %d\n", 0x1fffff);
+ return 1;
+ }
+ break;
+ case 10:
+ sz_ind = 1;
+ cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+ rd_opcode[sz_ind]);
+ cdbp[2] = (unsigned char)((start_block >> 24) & 0xff);
+ cdbp[3] = (unsigned char)((start_block >> 16) & 0xff);
+ cdbp[4] = (unsigned char)((start_block >> 8) & 0xff);
+ cdbp[5] = (unsigned char)(start_block & 0xff);
+ cdbp[7] = (unsigned char)((blocks >> 8) & 0xff);
+ cdbp[8] = (unsigned char)(blocks & 0xff);
+ if (blocks & (~0xffff)) {
+ fprintf(stderr, ME "for 10 byte commands, maximum number of "
+ "blocks is %d\n", 0xffff);
+ return 1;
+ }
+ break;
+ case 12:
+ sz_ind = 2;
+ cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+ rd_opcode[sz_ind]);
+ cdbp[2] = (unsigned char)((start_block >> 24) & 0xff);
+ cdbp[3] = (unsigned char)((start_block >> 16) & 0xff);
+ cdbp[4] = (unsigned char)((start_block >> 8) & 0xff);
+ cdbp[5] = (unsigned char)(start_block & 0xff);
+ cdbp[6] = (unsigned char)((blocks >> 24) & 0xff);
+ cdbp[7] = (unsigned char)((blocks >> 16) & 0xff);
+ cdbp[8] = (unsigned char)((blocks >> 8) & 0xff);
+ cdbp[9] = (unsigned char)(blocks & 0xff);
+ break;
+ case 16:
+ sz_ind = 3;
+ cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+ rd_opcode[sz_ind]);
+ /* can't cope with block number > 32 bits (yet) */
+ cdbp[6] = (unsigned char)((start_block >> 24) & 0xff);
+ cdbp[7] = (unsigned char)((start_block >> 16) & 0xff);
+ cdbp[8] = (unsigned char)((start_block >> 8) & 0xff);
+ cdbp[9] = (unsigned char)(start_block & 0xff);
+ cdbp[10] = (unsigned char)((blocks >> 24) & 0xff);
+ cdbp[11] = (unsigned char)((blocks >> 16) & 0xff);
+ cdbp[12] = (unsigned char)((blocks >> 8) & 0xff);
+ cdbp[13] = (unsigned char)(blocks & 0xff);
+ break;
+ default:
+ fprintf(stderr, ME "expected cdb size of 6, 10, 12, or 16 but got"
+ "=%d\n", cdb_sz);
+ return 1;
+ }
+ return 0;
+}
+
/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
2 -> try again */
int sg_read(int sg_fd, unsigned char * buff, int blocks, int from_block,
- int bs, int do_mmap)
+ int bs, int cdbsz, int do_mmap)
{
- unsigned char rdCmd[10] = {0x28, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ unsigned char rdCmd[MAX_SCSI_CDBSZ];
unsigned char senseBuff[SENSE_BUFF_LEN];
sg_io_hdr_t io_hdr;
int res;
- rdCmd[2] = (unsigned char)((from_block >> 24) & 0xFF);
- rdCmd[3] = (unsigned char)((from_block >> 16) & 0xFF);
- rdCmd[4] = (unsigned char)((from_block >> 8) & 0xFF);
- rdCmd[5] = (unsigned char)(from_block & 0xFF);
- rdCmd[7] = (unsigned char)((blocks >> 8) & 0xff);
- rdCmd[8] = (unsigned char)(blocks & 0xff);
-
+ if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, 0)) {
+ fprintf(stderr, ME "bad rd cdb build, from_block=%d, blocks=%d\n",
+ from_block, blocks);
+ return -1;
+ }
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
io_hdr.interface_id = 'S';
- io_hdr.cmd_len = sizeof(rdCmd);
+ io_hdr.cmd_len = cdbsz;
io_hdr.cmdp = rdCmd;
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.dxfer_len = bs * blocks;
@@ -258,23 +343,22 @@ int sg_read(int sg_fd, unsigned char * buff, int blocks, int from_block,
/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
2 -> try again */
int sg_write(int sg_fd, unsigned char * buff, int blocks, int to_block,
- int bs, int do_mmap)
+ int bs, int cdbsz, int do_mmap)
{
- unsigned char wrCmd[10] = {0x2a, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ unsigned char wrCmd[MAX_SCSI_CDBSZ];
unsigned char senseBuff[SENSE_BUFF_LEN];
sg_io_hdr_t io_hdr;
int res;
- wrCmd[2] = (unsigned char)((to_block >> 24) & 0xFF);
- wrCmd[3] = (unsigned char)((to_block >> 16) & 0xFF);
- wrCmd[4] = (unsigned char)((to_block >> 8) & 0xFF);
- wrCmd[5] = (unsigned char)(to_block & 0xFF);
- wrCmd[7] = (unsigned char)((blocks >> 8) & 0xff);
- wrCmd[8] = (unsigned char)(blocks & 0xff);
+ if (sg_build_scsi_cdb(wrCmd, cdbsz, blocks, to_block, 1)) {
+ fprintf(stderr, ME "bad wr cdb build, to_block=%d, blocks=%d\n",
+ to_block, blocks);
+ return -1;
+ }
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
io_hdr.interface_id = 'S';
- io_hdr.cmd_len = sizeof(wrCmd);
+ io_hdr.cmd_len = cdbsz;
io_hdr.cmdp = wrCmd;
io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
io_hdr.dxfer_len = bs * blocks;
@@ -357,6 +441,10 @@ int get_num(char * buf)
}
}
+#define STR_SZ 512
+#define INOUTF_SZ 512
+#define EBUFF_SZ 256
+
int main(int argc, char * argv[])
{
@@ -366,12 +454,12 @@ int main(int argc, char * argv[])
int ibs = 0;
int obs = 0;
int bpt = DEF_BLOCKS_PER_TRANSFER;
- char str[512];
+ char str[STR_SZ];
char * key;
char * buf;
- char inf[512];
+ char inf[INOUTF_SZ];
int in_type = FT_OTHER;
- char outf[512];
+ char outf[INOUTF_SZ];
int out_type = FT_OTHER;
int res, k, t;
int infd, outfd, blocks;
@@ -382,10 +470,14 @@ int main(int argc, char * argv[])
int in_res_sz = 0;
int out_num_sect = 0;
int out_res_sz = 0;
+ int do_time = 0;
+ int scsi_cdbsz = DEF_SCSI_CDBSZ;
int in_sect_sz, out_sect_sz;
- char ebuff[256];
+ char ebuff[EBUFF_SZ];
int blocks_per;
+ int req_count;
size_t psz = getpagesize();
+ struct timeval start_tm, end_tm;
inf[0] = '\0';
outf[0] = '\0';
@@ -396,7 +488,7 @@ int main(int argc, char * argv[])
for(k = 1; k < argc; k++) {
if (argv[k])
- strcpy(str, argv[k]);
+ strncpy(str, argv[k], STR_SZ);
else
continue;
for(key = str, buf = key; *buf && *buf != '=';)
@@ -404,9 +496,9 @@ int main(int argc, char * argv[])
if (*buf)
*buf++ = '\0';
if (strcmp(key,"if") == 0)
- strcpy(inf, buf);
+ strncpy(inf, buf, INOUTF_SZ);
else if (strcmp(key,"of") == 0)
- strcpy(outf, buf);
+ strncpy(outf, buf, INOUTF_SZ);
else if (0 == strcmp(key,"ibs"))
ibs = get_num(buf);
else if (0 == strcmp(key,"obs"))
@@ -421,6 +513,15 @@ int main(int argc, char * argv[])
seek = get_num(buf);
else if (0 == strcmp(key,"count"))
dd_count = get_num(buf);
+ else if (0 == strcmp(key,"time"))
+ do_time = get_num(buf);
+ else if (0 == strcmp(key,"cdbsz"))
+ scsi_cdbsz = get_num(buf);
+ else if (0 == strncmp(key, "--vers", 6)) {
+ fprintf(stderr, ME "for Linux sg version 3 driver: %s\n",
+ version_str);
+ return 0;
+ }
else {
fprintf(stderr, "Unrecognized argument '%s'\n", key);
usage();
@@ -441,7 +542,7 @@ int main(int argc, char * argv[])
return 1;
}
#ifdef SG_DEBUG
- fprintf(stderr, "sgm_dd: if=%s skip=%d of=%s seek=%d count=%d\n",
+ fprintf(stderr, ME "if=%s skip=%d of=%s seek=%d count=%d\n",
inf, skip, outf, seek, dd_count);
#endif
install_handler (SIGINT, interrupt_handler);
@@ -456,40 +557,42 @@ int main(int argc, char * argv[])
if (FT_SG == in_type) {
if ((infd = open(inf, O_RDWR)) < 0) {
- sprintf(ebuff, "sgm_dd: could not open %s for sg reading",
- inf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for sg reading", inf);
perror(ebuff);
return 1;
}
res = ioctl(infd, SG_GET_VERSION_NUM, &t);
if ((res < 0) || (t < 30122)) {
- fprintf(stderr, "sgm_dd: sg driver prior to 3.1.22\n");
+ fprintf(stderr, ME "sg driver prior to 3.1.22\n");
return 1;
}
in_res_sz = bs * bpt;
if (0 != (in_res_sz % psz)) /* round up to next page */
in_res_sz = ((in_res_sz / psz) + 1) * psz;
if (ioctl(infd, SG_GET_RESERVED_SIZE, &t) < 0) {
- perror("sgm_dd: SG_GET_RESERVED_SIZE error");
+ perror(ME "SG_GET_RESERVED_SIZE error");
return 1;
}
if (in_res_sz > t) {
if (ioctl(infd, SG_SET_RESERVED_SIZE, &in_res_sz) < 0) {
- perror("sgm_dd: SG_SET_RESERVED_SIZE error");
+ perror(ME "SG_SET_RESERVED_SIZE error");
return 1;
}
}
wrkMmap = mmap(NULL, in_res_sz, PROT_READ | PROT_WRITE,
MAP_SHARED, infd, 0);
if (MAP_FAILED == wrkMmap) {
- sprintf(ebuff, "sgm_dd: error using mmap() on file: %s", inf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "error using mmap() on file: %s", inf);
perror(ebuff);
return 1;
}
}
if (FT_SG != in_type) {
if ((infd = open(inf, O_RDONLY)) < 0) {
- sprintf(ebuff, "sgm_dd: could not open %s for reading", inf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for reading", inf);
perror(ebuff);
return 1;
}
@@ -498,8 +601,8 @@ int main(int argc, char * argv[])
offset *= bs; /* could exceed 32 bits here! */
if (llse_llseek(infd, offset, SEEK_SET) < 0) {
- sprintf(ebuff, "sgm_dd: couldn't skip to required "
- "position on %s", inf);
+ snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to "
+ "required position on %s", inf);
perror(ebuff);
return 1;
}
@@ -512,24 +615,24 @@ int main(int argc, char * argv[])
if (FT_SG == out_type) {
if ((outfd = open(outf, O_RDWR)) < 0) {
- sprintf(ebuff, "sgm_dd: could not open %s for sg writing",
- outf);
+ snprintf(ebuff, EBUFF_SZ, ME "could not open %s for "
+ "sg writing", outf);
perror(ebuff);
return 1;
}
res = ioctl(outfd, SG_GET_VERSION_NUM, &t);
if ((res < 0) || (t < 30122)) {
- fprintf(stderr, "sgm_dd: sg driver prior to 3.1.22\n");
+ fprintf(stderr, ME "sg driver prior to 3.1.22\n");
return 1;
}
if (ioctl(outfd, SG_GET_RESERVED_SIZE, &t) < 0) {
- perror("sgm_dd: SG_GET_RESERVED_SIZE error");
+ perror(ME "SG_GET_RESERVED_SIZE error");
return 1;
}
out_res_sz = bs * bpt;
if (out_res_sz > t) {
if (ioctl(outfd, SG_SET_RESERVED_SIZE, &out_res_sz) < 0) {
- perror("sgm_dd: SG_SET_RESERVED_SIZE error");
+ perror(ME "SG_SET_RESERVED_SIZE error");
return 1;
}
}
@@ -537,8 +640,8 @@ int main(int argc, char * argv[])
wrkMmap = mmap(NULL, out_res_sz, PROT_READ | PROT_WRITE,
MAP_SHARED, outfd, 0);
if (MAP_FAILED == wrkMmap) {
- sprintf(ebuff, "sgm_dd: error using mmap() on file: %s",
- outf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "error using mmap() on file: %s", outf);
perror(ebuff);
return 1;
}
@@ -547,16 +650,16 @@ int main(int argc, char * argv[])
else {
if (FT_OTHER == out_type) {
if ((outfd = open(outf, O_WRONLY | O_CREAT, 0666)) < 0) {
- sprintf(ebuff,
- "sgm_dd: could not open %s for writing", outf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for writing", outf);
perror(ebuff);
return 1;
}
}
else {
if ((outfd = open(outf, O_WRONLY)) < 0) {
- sprintf(ebuff, "sgm_dd: could not open %s for raw"
- " writing", outf);
+ snprintf(ebuff, EBUFF_SZ, ME "could not open %s "
+ "for raw writing", outf);
perror(ebuff);
return 1;
}
@@ -566,8 +669,8 @@ int main(int argc, char * argv[])
offset *= bs; /* could exceed 32 bits here! */
if (llse_llseek(outfd, offset, SEEK_SET) < 0) {
- sprintf(ebuff,
- "sgm_dd: couldn't seek to required position on %s", outf);
+ snprintf(ebuff, EBUFF_SZ, ME "couldn't seek to "
+ "required position on %s", outf);
perror(ebuff);
return 1;
}
@@ -675,14 +778,21 @@ int main(int argc, char * argv[])
fprintf(stderr, "Start of loop, count=%d, blocks_per=%d\n",
dd_count, blocks_per);
#endif
+ if (do_time) {
+ start_tm.tv_sec = 0;
+ start_tm.tv_usec = 0;
+ gettimeofday(&start_tm, NULL);
+ }
+ req_count = dd_count;
+
while (dd_count > 0) {
blocks = (dd_count > blocks_per) ? blocks_per : dd_count;
if (FT_SG == in_type) {
- res = sg_read(infd, wrkPos, blocks, skip, bs, 1);
+ res = sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz, 1);
if (2 == res) {
fprintf(stderr,
"Unit attention, media changed, continuing (r)\n");
- res = sg_read(infd, wrkPos, blocks, skip, bs, 1);
+ res = sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz, 1);
}
if (0 != res) {
fprintf(stderr, "sg_read failed, skip=%d\n", skip);
@@ -696,7 +806,7 @@ int main(int argc, char * argv[])
(EINTR == errno))
;
if (res < 0) {
- sprintf(ebuff, "sgm_dd: reading, skip=%d ", skip);
+ snprintf(ebuff, EBUFF_SZ, ME "reading, skip=%d ", skip);
perror(ebuff);
break;
}
@@ -713,11 +823,13 @@ int main(int argc, char * argv[])
if (FT_SG == out_type) {
int do_mmap = (FT_SG == in_type) ? 0 : 1;
- res = sg_write(outfd, wrkPos, blocks, seek, bs, do_mmap);
+ res = sg_write(outfd, wrkPos, blocks, seek, bs, scsi_cdbsz,
+ do_mmap);
if (2 == res) {
fprintf(stderr,
"Unit attention, media changed, continuing (w)\n");
- res = sg_write(outfd, wrkPos, blocks, seek, bs, do_mmap);
+ res = sg_write(outfd, wrkPos, blocks, seek, bs, scsi_cdbsz,
+ do_mmap);
}
else if (0 != res) {
fprintf(stderr, "sg_write failed, seek=%d\n", seek);
@@ -731,7 +843,7 @@ int main(int argc, char * argv[])
&& (EINTR == errno))
;
if (res < 0) {
- sprintf(ebuff, "sgm_dd: writing, seek=%d ", seek);
+ snprintf(ebuff, EBUFF_SZ, ME "writing, seek=%d ", seek);
perror(ebuff);
break;
}
@@ -751,6 +863,27 @@ int main(int argc, char * argv[])
skip += blocks;
seek += blocks;
}
+ if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
+ struct timeval res_tm;
+ double a, b;
+
+ gettimeofday(&end_tm, NULL);
+ res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
+ res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
+ if (res_tm.tv_usec < 0) {
+ --res_tm.tv_sec;
+ res_tm.tv_usec += 1000000;
+ }
+ a = res_tm.tv_sec;
+ a += (0.000001 * res_tm.tv_usec);
+ b = (double)bs * (req_count - dd_count);
+ printf("time to transfer data was %d.%06d secs",
+ (int)res_tm.tv_sec, (int)res_tm.tv_usec);
+ if ((a > 0.00001) && (b > 511))
+ printf(", %.2f MB/sec\n", b / (a * 1000000.0));
+ else
+ printf("\n");
+ }
if (wrkBuff) free(wrkBuff);
if (STDIN_FILENO != infd)
diff --git a/sgp_dd.8 b/sgp_dd.8
index 6ec8858a..bcc1a376 100644
--- a/sgp_dd.8
+++ b/sgp_dd.8
@@ -1,6 +1,6 @@
-.TH SGP_DD "8" "September 2001" "sg3_utils-0.95" SG3_UTILS
+.TH SGP_DD "8" "February 2002" "sg3_utils-0.98" SG3_UTILS
.SH NAME
-copies data to and from sg and raw devices
+sgp_dd \- copies data to and from sg and raw devices
.SH SYNOPSIS
.B sgp_dd
[\fIOPTION\fR]...
@@ -25,10 +25,14 @@ be the block size of the physical device. Note that this differs from
which permits "bs" to be an integral multiple. Default is 512 which
is usually correct for disks but incorrect for cdroms (which normally
have 2048 byte blocks).
+cdbsz=6 | 10 | 12 | 16
+size of SCSI READ and/or WRITE commands issued on sg device names.
+Default is 10 byte SCSI command blocks
.TP
coe=0 | 1
continue on error is 0 (off) by default. When it is 1 read errors
-are stepped over (with a block (or blocks) of zeroes being output)
+are stepped over (with a block (or blocks) of zeroes being output).
+When 1 write errors are ignored (and alignment is maintained)
.TP
count=BLOCKS
copy this number of blocks. Default is minimum number that sg devices
@@ -70,6 +74,10 @@ thr=NUM
this is the number or worker threads (default 4) that attempt to
copy in parallel. Minimum is 0 and maximum is 16
.TP
+time=0 | 1
+when 1, times transfer and does throughput calculation, outputting the
+results (to stderr) at completion. When 0 (default) doesn't perform timing
+.TP
--version
outputs version number information and exits
.PP
@@ -101,7 +109,7 @@ issues "direct IO" is disabled in the sg driver and needs a
configuration change to activate it.
.PP
All informative, warning and error output is sent to stderr so that
-dd\'s output file can be stdout and remain unpolluted. If no options
+dd's output file can be stdout and remain unpolluted. If no options
are given, then the usage message is output and nothing else happens.
.PP
Why use sgp_dd? Because in some cases it is twice as fast as dd
@@ -121,7 +129,7 @@ equivalent to:
.PP
dd if=/dev/sda of=t bs=512 count=1000000
.PP
-although dd\'s speed may improve if bs was larger and count was suitably
+although dd's speed may improve if bs was larger and count was suitably
reduced. Using a raw device to do something similar on a IDE disk:
.PP
raw /dev/raw/raw1 /dev/hda
diff --git a/sgp_dd.c b/sgp_dd.c
index 39443acf..3ccc53af 100644
--- a/sgp_dd.c
+++ b/sgp_dd.c
@@ -13,6 +13,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
+#include <sys/time.h>
#include <linux/major.h>
typedef unsigned char u_char; /* horrible, for scsi.h */
#include "sg_include.h"
@@ -20,7 +21,7 @@ typedef unsigned char u_char; /* horrible, for scsi.h */
#include "llseek.h"
/* A utility program for the Linux OS SCSI generic ("sg") device driver.
-* Copyright (C) 1999 - 2001 D. Gilbert and P. Allworth
+* Copyright (C) 1999 - 2002 D. Gilbert and P. Allworth
* 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)
@@ -46,16 +47,19 @@ typedef unsigned char u_char; /* horrible, for scsi.h */
*/
-static char * version_str = "5.04 20010819";
+static char * version_str = "5.08 20020204";
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
+#define DEF_SCSI_CDBSZ 10
+#define MAX_SCSI_CDBSZ 16
+
+#define ME "sgp_dd: "
/* #define SG_DEBUG */
#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
#define DEF_TIMEOUT 60000 /* 60,000 millisecs == 60 seconds */
-#define S_RW_LEN 10 /* Use SCSI READ(10) and WRITE(10) */
#define SGP_READ10 0x28
#define SGP_WRITE10 0x2a
@@ -70,6 +74,8 @@ static char * version_str = "5.04 20010819";
#define FT_SG 1 /* filetype is sg char device */
#define FT_RAW 2 /* filetype is raw char device */
+#define EBUFF_SZ 512
+
typedef struct request_collection
{ /* one instance visible to all threads */
@@ -101,6 +107,7 @@ typedef struct request_collection
int sum_of_resids; /* | */
pthread_mutex_t aux_mutex; /* -/ (also serializes some printf()s */
int coe;
+ int cdbsz;
int debug;
} Rq_coll;
@@ -114,7 +121,7 @@ typedef struct request_element
unsigned char * buffp;
unsigned char * alloc_bp;
sg_io_hdr_t io_hdr;
- unsigned char cmd[S_RW_LEN];
+ unsigned char cmd[MAX_SCSI_CDBSZ];
unsigned char sb[SENSE_BUFF_LEN];
int bs;
int dio;
@@ -122,6 +129,7 @@ typedef struct request_element
int resid;
int in_scsi_type;
int out_scsi_type;
+ int cdbsz;
int debug;
} Rq_elem;
@@ -174,7 +182,8 @@ void usage()
"sgp_dd [if=<infile>] [skip=<n>] [of=<ofile>] [seek=<n>]\n"
" [bs=<num>] [bpt=<num>] [count=<n>]\n"
" [dio=<n>] [thr=<n>] [coe=<n>] [gen=<n>]\n"
- " [deb=<n>] [--version]\n"
+ " [time=<n>] [deb=<n>] [cdbsz=<6|10|12|16>] "
+ "[--version]\n"
" usually either 'if' or 'of' is a sg or raw device\n"
" 'bpt' is blocks_per_transfer (default is 128)\n"
" 'dio' is direct IO, 1->attempt, 0->indirect IO (def)\n"
@@ -182,6 +191,8 @@ void usage()
fprintf(stderr, " 'coe' continue on error, 0->exit (def), "
"1->zero + continue\n"
" 'gen' 0-> 1 file is special(def), 1-> any files allowed\n"
+ " 'time' 0->no timing(def), 1->time plus calculate throughput\n"
+ " 'cdbsz' size of SCSI READ or WRITE command (default is 10)\n"
" 'deb' is debug, 0->none (def), > 0->varying degrees of debug\n");
}
@@ -255,7 +266,7 @@ void * sig_listen_thread(void * v_clp)
while (1) {
sigwait(&signal_set, &sig_number);
if (SIGINT == sig_number) {
- fprintf(stderr, "sgp_dd interrupted by SIGINT\n");
+ fprintf(stderr, ME "interrupted by SIGINT\n");
guarded_stop_both(clp);
pthread_cond_broadcast(&clp->out_sync_cv);
}
@@ -310,6 +321,7 @@ void * read_write_thread(void * v_clp)
rep->debug = clp->debug;
rep->in_scsi_type = clp->in_scsi_type;
rep->out_scsi_type = clp->out_scsi_type;
+ rep->cdbsz = clp->cdbsz;
while(1) {
status = pthread_mutex_lock(&clp->in_mutex);
@@ -463,6 +475,85 @@ void normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks)
clp->out_done_count -= blocks;
}
+int sg_build_scsi_cdb(unsigned char * cdbp, int cdb_sz, unsigned int blocks,
+ unsigned int start_block, int write_true)
+{
+ int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88};
+ int wr_opcode[] = {0xa, 0x2a, 0xaa, 0x8a};
+ int sz_ind;
+
+ memset(cdbp, 0, cdb_sz);
+ switch (cdb_sz) {
+ case 6:
+ sz_ind = 0;
+ cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+ rd_opcode[sz_ind]);
+ cdbp[1] |= (unsigned char)((start_block >> 16) & 0x1f);
+ cdbp[2] = (unsigned char)((start_block >> 8) & 0xff);
+ cdbp[3] = (unsigned char)(start_block & 0xff);
+ cdbp[4] = (256 == blocks) ? 0 : (unsigned char)blocks;
+ if (blocks > 256) {
+ fprintf(stderr, ME "for 6 byte commands, maximum number of "
+ "blocks is 256\n");
+ return 1;
+ }
+ if ((start_block + blocks - 1) & (~0x1fffff)) {
+ fprintf(stderr, ME "for 6 byte commands, can't address blocks"
+ " beyond %d\n", 0x1fffff);
+ return 1;
+ }
+ break;
+ case 10:
+ sz_ind = 1;
+ cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+ rd_opcode[sz_ind]);
+ cdbp[2] = (unsigned char)((start_block >> 24) & 0xff);
+ cdbp[3] = (unsigned char)((start_block >> 16) & 0xff);
+ cdbp[4] = (unsigned char)((start_block >> 8) & 0xff);
+ cdbp[5] = (unsigned char)(start_block & 0xff);
+ cdbp[7] = (unsigned char)((blocks >> 8) & 0xff);
+ cdbp[8] = (unsigned char)(blocks & 0xff);
+ if (blocks & (~0xffff)) {
+ fprintf(stderr, ME "for 10 byte commands, maximum number of "
+ "blocks is %d\n", 0xffff);
+ return 1;
+ }
+ break;
+ case 12:
+ sz_ind = 2;
+ cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+ rd_opcode[sz_ind]);
+ cdbp[2] = (unsigned char)((start_block >> 24) & 0xff);
+ cdbp[3] = (unsigned char)((start_block >> 16) & 0xff);
+ cdbp[4] = (unsigned char)((start_block >> 8) & 0xff);
+ cdbp[5] = (unsigned char)(start_block & 0xff);
+ cdbp[6] = (unsigned char)((blocks >> 24) & 0xff);
+ cdbp[7] = (unsigned char)((blocks >> 16) & 0xff);
+ cdbp[8] = (unsigned char)((blocks >> 8) & 0xff);
+ cdbp[9] = (unsigned char)(blocks & 0xff);
+ break;
+ case 16:
+ sz_ind = 3;
+ cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
+ rd_opcode[sz_ind]);
+ /* can't cope with block number > 32 bits (yet) */
+ cdbp[6] = (unsigned char)((start_block >> 24) & 0xff);
+ cdbp[7] = (unsigned char)((start_block >> 16) & 0xff);
+ cdbp[8] = (unsigned char)((start_block >> 8) & 0xff);
+ cdbp[9] = (unsigned char)(start_block & 0xff);
+ cdbp[10] = (unsigned char)((blocks >> 24) & 0xff);
+ cdbp[11] = (unsigned char)((blocks >> 16) & 0xff);
+ cdbp[12] = (unsigned char)((blocks >> 8) & 0xff);
+ cdbp[13] = (unsigned char)(blocks & 0xff);
+ break;
+ default:
+ fprintf(stderr, ME "expected cdb size of 6, 10, 12, or 16 but got"
+ "=%d\n", cdb_sz);
+ return 1;
+ }
+ return 0;
+}
+
void sg_in_operation(Rq_coll * clp, Rq_elem * rep)
{
int res;
@@ -474,7 +565,7 @@ void sg_in_operation(Rq_coll * clp, Rq_elem * rep)
if (1 == res)
err_exit(ENOMEM, "sg starting in command");
else if (res < 0) {
- fprintf(stderr, "sgp_dd inputting to sg failed, blk=%d\n",
+ fprintf(stderr, ME "inputting to sg failed, blk=%d\n",
rep->blk);
status = pthread_mutex_unlock(&clp->in_mutex);
if (0 != status) err_exit(status, "unlock in_mutex");
@@ -533,7 +624,7 @@ void sg_out_operation(Rq_coll * clp, Rq_elem * rep)
if (1 == res)
err_exit(ENOMEM, "sg starting out command");
else if (res < 0) {
- fprintf(stderr, "sgp_dd outputting from sg failed, blk=%d\n",
+ fprintf(stderr, ME "outputting from sg failed, blk=%d\n",
rep->blk);
status = pthread_mutex_unlock(&clp->out_mutex);
if (0 != status) err_exit(status, "unlock out_mutex");
@@ -584,17 +675,15 @@ int sg_start_io(Rq_elem * rep)
sg_io_hdr_t * hp = &rep->io_hdr;
int res;
- memset(rep->cmd, 0, sizeof(rep->cmd));
- rep->cmd[0] = rep->wr ? SGP_WRITE10 : SGP_READ10;
- rep->cmd[2] = (unsigned char)((rep->blk >> 24) & 0xFF);
- rep->cmd[3] = (unsigned char)((rep->blk >> 16) & 0xFF);
- rep->cmd[4] = (unsigned char)((rep->blk >> 8) & 0xFF);
- rep->cmd[5] = (unsigned char)(rep->blk & 0xFF);
- rep->cmd[7] = (unsigned char)((rep->num_blks >> 8) & 0xff);
- rep->cmd[8] = (unsigned char)(rep->num_blks & 0xff);
+ if (sg_build_scsi_cdb(rep->cmd, rep->cdbsz, rep->num_blks, rep->blk,
+ rep->wr)) {
+ fprintf(stderr, ME "bad cdb build, start_blk=%d, blocks=%d\n",
+ rep->blk, rep->num_blks);
+ return -1;
+ }
memset(hp, 0, sizeof(sg_io_hdr_t));
hp->interface_id = 'S';
- hp->cmd_len = sizeof(rep->cmd);
+ hp->cmd_len = rep->cdbsz;
hp->cmdp = rep->cmd;
hp->dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
hp->dxfer_len = rep->bs * rep->num_blks;
@@ -665,9 +754,10 @@ int sg_finish_io(int wr, Rq_elem * rep, pthread_mutex_t * a_mutp)
return 1;
default:
{
- char ebuff[64];
- sprintf(ebuff, "%s blk=%d", rep->wr ? "writing": "reading",
- rep->blk);
+ char ebuff[EBUFF_SZ];
+
+ snprintf(ebuff, EBUFF_SZ,
+ "%s blk=%d", rep->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);
@@ -696,24 +786,24 @@ int sg_prepare(int fd, int bs, int bpt, int * scsi_typep)
res = ioctl(fd, SG_GET_VERSION_NUM, &t);
if ((res < 0) || (t < 30000)) {
- fprintf(stderr, "sgp_dd: sg driver prior to 3.x.y\n");
+ fprintf(stderr, ME "sg driver prior to 3.x.y\n");
return 1;
}
res = 0;
t = bs * bpt;
res = ioctl(fd, SG_SET_RESERVED_SIZE, &t);
if (res < 0)
- perror("sgp_dd: SG_SET_RESERVED_SIZE error");
+ perror(ME "SG_SET_RESERVED_SIZE error");
t = 1;
res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t);
if (res < 0)
- perror("sgp_dd: SG_SET_FORCE_PACK_ID error");
+ perror(ME "SG_SET_FORCE_PACK_ID error");
if (scsi_typep) {
struct sg_scsi_id info;
res = ioctl(fd, SG_GET_SCSI_ID, &info);
if (res < 0)
- perror("sgp_dd: SG_SET_SCSI_ID error");
+ perror(ME "SG_SET_SCSI_ID error");
*scsi_typep = info.scsi_type;
}
return 0;
@@ -756,6 +846,9 @@ int get_num(char * buf)
}
}
+#define STR_SZ 1024
+#define INOUTF_SZ 512
+
int main(int argc, char * argv[])
{
@@ -764,26 +857,29 @@ int main(int argc, char * argv[])
int ibs = 0;
int obs = 0;
int count = -1;
- char str[512];
+ char str[STR_SZ];
char * key;
char * buf;
- char inf[512];
- char outf[512];
+ char inf[INOUTF_SZ];
+ char outf[INOUTF_SZ];
int res, k;
int in_num_sect = 0;
int out_num_sect = 0;
int num_threads = DEF_NUM_THREADS;
pthread_t threads[MAX_NUM_THREADS];
int gen = 0;
+ int do_time = 0;
int in_sect_sz, out_sect_sz, status, infull, outfull;
void * vp;
- char ebuff[256];
+ char ebuff[EBUFF_SZ];
+ struct timeval start_tm, end_tm;
Rq_coll rcoll;
memset(&rcoll, 0, sizeof(Rq_coll));
rcoll.bpt = DEF_BLOCKS_PER_TRANSFER;
rcoll.in_type = FT_OTHER;
rcoll.out_type = FT_OTHER;
+ rcoll.cdbsz = DEF_SCSI_CDBSZ;
inf[0] = '\0';
outf[0] = '\0';
if (argc < 2) {
@@ -792,8 +888,10 @@ int main(int argc, char * argv[])
}
for(k = 1; k < argc; k++) {
- if (argv[k])
- strcpy(str, argv[k]);
+ if (argv[k]) {
+ strncpy(str, argv[k], STR_SZ);
+ str[STR_SZ - 1] = '\0';
+ }
else
continue;
for(key = str, buf = key; *buf && *buf != '=';)
@@ -801,9 +899,9 @@ int main(int argc, char * argv[])
if (*buf)
*buf++ = '\0';
if (strcmp(key,"if") == 0)
- strcpy(inf, buf);
+ strncpy(inf, buf, INOUTF_SZ);
else if (strcmp(key,"of") == 0)
- strcpy(outf, buf);
+ strncpy(outf, buf, INOUTF_SZ);
else if (0 == strcmp(key,"ibs"))
ibs = get_num(buf);
else if (0 == strcmp(key,"obs"))
@@ -826,10 +924,14 @@ int main(int argc, char * argv[])
rcoll.coe = get_num(buf);
else if (0 == strcmp(key,"gen"))
gen = get_num(buf);
+ else if (0 == strcmp(key,"time"))
+ do_time = get_num(buf);
+ else if (0 == strcmp(key,"cdbsz"))
+ rcoll.cdbsz = get_num(buf);
else if (0 == strncmp(key,"deb", 3))
rcoll.debug = get_num(buf);
else if (0 == strncmp(key, "--vers", 6)) {
- fprintf(stderr, "sgp_dd for sg version 3 driver: %s\n",
+ fprintf(stderr, ME "for sg version 3 driver: %s\n",
version_str);
return 0;
}
@@ -859,7 +961,7 @@ int main(int argc, char * argv[])
return 1;
}
if (rcoll.debug)
- fprintf(stderr, "sgp_dd: if=%s skip=%d of=%s seek=%d count=%d\n",
+ fprintf(stderr, ME "if=%s skip=%d of=%s seek=%d count=%d\n",
inf, skip, outf, seek, count);
rcoll.infd = STDIN_FILENO;
rcoll.outfd = STDOUT_FILENO;
@@ -868,8 +970,8 @@ int main(int argc, char * argv[])
if (FT_SG == rcoll.in_type) {
if ((rcoll.infd = open(inf, O_RDWR)) < 0) {
- sprintf(ebuff, "sgp_dd: could not open %s for sg reading",
- inf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for sg reading", inf);
perror(ebuff);
return 1;
}
@@ -879,7 +981,8 @@ int main(int argc, char * argv[])
}
if (FT_SG != rcoll.in_type) {
if ((rcoll.infd = open(inf, O_RDONLY)) < 0) {
- sprintf(ebuff, "sgp_dd: could not open %s for reading", inf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for reading", inf);
perror(ebuff);
return 1;
}
@@ -888,8 +991,8 @@ int main(int argc, char * argv[])
offset *= rcoll.bs; /* could exceed 32 here! */
if (llse_llseek(rcoll.infd, offset, SEEK_SET) < 0) {
- sprintf(ebuff,
- "sgp_dd: couldn't skip to required position on %s", inf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "couldn't skip to required position on %s", inf);
perror(ebuff);
return 1;
}
@@ -901,8 +1004,8 @@ int main(int argc, char * argv[])
if (FT_SG == rcoll.out_type) {
if ((rcoll.outfd = open(outf, O_RDWR)) < 0) {
- sprintf(ebuff,
- "sgp_dd: could not open %s for sg writing", outf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for sg writing", outf);
perror(ebuff);
return 1;
}
@@ -914,16 +1017,16 @@ int main(int argc, char * argv[])
else {
if (FT_OTHER == rcoll.out_type) {
if ((rcoll.outfd = open(outf, O_WRONLY | O_CREAT, 0666)) < 0) {
- sprintf(ebuff,
- "sgp_dd: could not open %s for writing", outf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for writing", outf);
perror(ebuff);
return 1;
}
}
else {
if ((rcoll.outfd = open(outf, O_WRONLY)) < 0) {
- sprintf(ebuff,
- "sgp_dd: could not open %s for raw writing", outf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "could not open %s for raw writing", outf);
perror(ebuff);
return 1;
}
@@ -933,8 +1036,8 @@ int main(int argc, char * argv[])
offset *= rcoll.bs; /* could exceed 32 bits here! */
if (llse_llseek(rcoll.outfd, offset, SEEK_SET) < 0) {
- sprintf(ebuff,
- "sgp_dd: couldn't seek to required position on %s", outf);
+ snprintf(ebuff, EBUFF_SZ,
+ ME "couldn't seek to required position on %s", outf);
perror(ebuff);
return 1;
}
@@ -1027,6 +1130,12 @@ int main(int argc, char * argv[])
sig_listen_thread, (void *)&rcoll);
if (0 != status) err_exit(status, "pthread_create, sig...");
+ if (do_time) {
+ start_tm.tv_sec = 0;
+ start_tm.tv_usec = 0;
+ gettimeofday(&start_tm, NULL);
+ }
+
/* vvvvvvvvvvv Start worker threads vvvvvvvvvvvvvvvvvvvvvvvv */
if ((rcoll.out_done_count > 0) && (num_threads > 0)) {
/* Run 1 work thread to shake down infant retryable stuff */
@@ -1064,6 +1173,28 @@ int main(int argc, char * argv[])
}
}
+ if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
+ struct timeval res_tm;
+ double a, b;
+
+ gettimeofday(&end_tm, NULL);
+ res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
+ res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
+ if (res_tm.tv_usec < 0) {
+ --res_tm.tv_sec;
+ res_tm.tv_usec += 1000000;
+ }
+ a = res_tm.tv_sec;
+ a += (0.000001 * res_tm.tv_usec);
+ b = (double)rcoll.bs * (count - rcoll.out_done_count);
+ printf("time to transfer data was %d.%06d secs",
+ (int)res_tm.tv_sec, (int)res_tm.tv_usec);
+ if ((a > 0.00001) && (b > 511))
+ printf(", %.2f MB/sec\n", b / (a * 1000000.0));
+ else
+ printf("\n");
+ }
+
status = pthread_cancel(sig_listen_thread_id);
if (0 != status) err_exit(status, "pthread_cancel");
if (STDIN_FILENO != rcoll.infd)