diff options
Diffstat (limited to 'sgm_dd.c')
-rw-r--r-- | sgm_dd.c | 253 |
1 files changed, 193 insertions, 60 deletions
@@ -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) |