aboutsummaryrefslogtreecommitdiff
path: root/sgm_dd.c
diff options
context:
space:
mode:
Diffstat (limited to 'sgm_dd.c')
-rw-r--r--sgm_dd.c253
1 files changed, 193 insertions, 60 deletions
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)