1 /*
2 * A utility program for the Linux OS SCSI generic ("sg") device driver.
3 * Copyright (C) 2001 - 2022 D. Gilbert
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
7 * any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10
11 This program reads data from the given SCSI device (typically a disk
12 or cdrom) and discards that data. Its primary goal is to time
13 multiple reads all starting from the same logical address. Its interface
14 is a subset of another member of this package: sg_dd which is a
15 "dd" variant. The input file can be a scsi generic device, a block device,
16 or a seekable file. Streams such as stdin are not acceptable. The block
17 size ('bs') is assumed to be 512 if not given.
18
19 This version should compile with Linux sg drivers with version numbers
20 >= 30000 . For mmap-ed IO the sg version number >= 30122 .
21
22 */
23
24 #define _XOPEN_SOURCE 600
25 #ifndef _GNU_SOURCE
26 #define _GNU_SOURCE 1
27 #endif
28
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <stdbool.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #define __STDC_FORMAT_MACROS 1
40 #include <inttypes.h>
41 #include <sys/ioctl.h>
42 #include <sys/stat.h>
43 #include <sys/sysmacros.h>
44 #ifndef major
45 #include <sys/types.h>
46 #endif
47 #include <sys/mman.h>
48 #include <sys/time.h>
49 #include <linux/major.h>
50
51 #ifdef HAVE_CONFIG_H
52 #include "config.h"
53 #endif
54
55 #include "sg_lib.h"
56 #include "sg_io_linux.h"
57 #include "sg_unaligned.h"
58 #include "sg_pr2serr.h"
59
60
61 static const char * version_str = "1.38 20220118";
62
63 #define DEF_BLOCK_SIZE 512
64 #define DEF_BLOCKS_PER_TRANSFER 128
65 #define DEF_SCSI_CDBSZ 10
66 #define MAX_SCSI_CDBSZ 16
67 #define MAX_BPT_VALUE (1 << 24) /* used for maximum bs as well */
68 #define MAX_COUNT_SKIP_SEEK (1LL << 48) /* coverity wants upper bound */
69
70 #define ME "sg_read: "
71
72 #ifndef SG_FLAG_MMAP_IO
73 #define SG_FLAG_MMAP_IO 4
74 #endif
75
76 #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
77 #define DEF_TIMEOUT 40000 /* 40,000 millisecs == 40 seconds */
78
79 #ifndef RAW_MAJOR
80 #define RAW_MAJOR 255 /*unlikely value */
81 #endif
82
83 #define FT_OTHER 1 /* filetype other than sg and ... */
84 #define FT_SG 2 /* filetype is sg char device */
85 #define FT_RAW 4 /* filetype is raw char device */
86 #define FT_BLOCK 8 /* filetype is block device */
87 #define FT_ERROR 64 /* couldn't "stat" file */
88
89 #define MIN_RESERVED_SIZE 8192
90
91 static int sum_of_resids = 0;
92
93 static int64_t dd_count = -1;
94 static int64_t orig_count = 0;
95 static int64_t in_full = 0;
96 static int in_partial = 0;
97
98 static int pack_id_count = 0;
99 static int verbose = 0;
100
101 static const char * sg_allow_dio = "/sys/module/sg/parameters/allow_dio";
102
103
104 static void
install_handler(int sig_num,void (* sig_handler)(int sig))105 install_handler (int sig_num, void (*sig_handler) (int sig))
106 {
107 struct sigaction sigact;
108
109 sigaction (sig_num, NULL, &sigact);
110 if (sigact.sa_handler != SIG_IGN) {
111 sigact.sa_handler = sig_handler;
112 sigemptyset (&sigact.sa_mask);
113 sigact.sa_flags = 0;
114 sigaction (sig_num, &sigact, NULL);
115 }
116 }
117
118 static void
print_stats(int iters,const char * str)119 print_stats(int iters, const char * str)
120 {
121 if (orig_count > 0) {
122 if (0 != dd_count)
123 pr2serr(" remaining block count=%" PRId64 "\n", dd_count);
124 pr2serr("%" PRId64 "+%d records in", in_full - in_partial,
125 in_partial);
126 if (iters > 0)
127 pr2serr(", %s commands issued: %d\n", (str ? str : ""), iters);
128 else
129 pr2serr("\n");
130 } else if (iters > 0)
131 pr2serr("%s commands issued: %d\n", (str ? str : ""), iters);
132 }
133
134 static void
interrupt_handler(int sig)135 interrupt_handler(int sig)
136 {
137 struct sigaction sigact;
138
139 sigact.sa_handler = SIG_DFL;
140 sigemptyset (&sigact.sa_mask);
141 sigact.sa_flags = 0;
142 sigaction (sig, &sigact, NULL);
143 pr2serr("Interrupted by signal,");
144 print_stats(0, NULL);
145 kill (getpid (), sig);
146 }
147
148 static void
siginfo_handler(int sig)149 siginfo_handler(int sig)
150 {
151 if (sig) { ; } /* unused, dummy to suppress warning */
152 pr2serr("Progress report, continuing ...\n");
153 print_stats(0, NULL);
154 }
155
156 static int
dd_filetype(const char * filename)157 dd_filetype(const char * filename)
158 {
159 struct stat st;
160
161 if (stat(filename, &st) < 0)
162 return FT_ERROR;
163 if (S_ISCHR(st.st_mode)) {
164 if (RAW_MAJOR == major(st.st_rdev))
165 return FT_RAW;
166 else if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
167 return FT_SG;
168 } else if (S_ISBLK(st.st_mode))
169 return FT_BLOCK;
170 return FT_OTHER;
171 }
172
173 static void
usage()174 usage()
175 {
176 pr2serr("Usage: sg_read [blk_sgio=0|1] [bpt=BPT] [bs=BS] "
177 "[cdbsz=6|10|12|16]\n"
178 " count=COUNT [dio=0|1] [dpo=0|1] [fua=0|1] "
179 "if=IFILE\n"
180 " [mmap=0|1] [no_dfxer=0|1] [odir=0|1] "
181 "[skip=SKIP]\n"
182 " [time=TI] [verbose=VERB] [--help] "
183 "[--verbose]\n"
184 " [--version] "
185 " where:\n"
186 " blk_sgio 0->normal IO for block devices, 1->SCSI commands "
187 "via SG_IO\n"
188 " bpt is blocks_per_transfer (default is 128, or 64 KiB "
189 "for default BS)\n"
190 " setting 'bpt=0' will do COUNT zero block SCSI "
191 "READs\n"
192 " bs must match sector size if IFILE accessed via SCSI "
193 "commands\n"
194 " (def=512)\n"
195 " cdbsz size of SCSI READ command (default is 10)\n"
196 " count total bytes read will be BS*COUNT (if no "
197 "error)\n"
198 " (if negative, do |COUNT| zero block SCSI READs)\n"
199 " dio 1-> attempt direct IO on sg device, 0->indirect IO "
200 "(def)\n");
201 pr2serr(" dpo 1-> set disable page out (DPO) in SCSI READs\n"
202 " fua 1-> set force unit access (FUA) in SCSI READs\n"
203 " if an sg, block or raw device, or a seekable file (not "
204 "stdin)\n"
205 " mmap 1->perform mmap-ed IO on sg device, 0->indirect IO "
206 "(def)\n"
207 " no_dxfer 1->DMA to kernel buffers only, not user space, "
208 "0->normal(def)\n"
209 " odir 1->open block device O_DIRECT, 0->don't (def)\n"
210 " skip each transfer starts at this logical address "
211 "(def=0)\n"
212 " time 0->do nothing(def), 1->time from 1st cmd, 2->time "
213 "from 2nd, ...\n"
214 " verbose increase level of verbosity (def: 0)\n"
215 " --help|-h print this usage message then exit\n"
216 " --verbose|-v increase level of verbosity (def: 0)\n"
217 " --version|-V print version number then exit\n\n"
218 "Issue SCSI READ commands, each starting from the same logical "
219 "block address\n");
220 }
221
222 static int
sg_build_scsi_cdb(uint8_t * cdbp,int cdb_sz,unsigned int blocks,int64_t start_block,bool write_true,bool fua,bool dpo)223 sg_build_scsi_cdb(uint8_t * cdbp, int cdb_sz, unsigned int blocks,
224 int64_t start_block, bool write_true, bool fua, bool dpo)
225 {
226 int sz_ind;
227 int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88};
228 int wr_opcode[] = {0xa, 0x2a, 0xaa, 0x8a};
229
230 memset(cdbp, 0, cdb_sz);
231 if (dpo)
232 cdbp[1] |= 0x10;
233 if (fua)
234 cdbp[1] |= 0x8;
235 switch (cdb_sz) {
236 case 6:
237 sz_ind = 0;
238 cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
239 rd_opcode[sz_ind]);
240 sg_put_unaligned_be24(0x1fffff & start_block, cdbp + 1);
241 cdbp[4] = (256 == blocks) ? 0 : (uint8_t)blocks;
242 if (blocks > 256) {
243 pr2serr(ME "for 6 byte commands, maximum number of blocks is "
244 "256\n");
245 return 1;
246 }
247 if ((start_block + blocks - 1) & (~0x1fffff)) {
248 pr2serr(ME "for 6 byte commands, can't address blocks beyond "
249 "%d\n", 0x1fffff);
250 return 1;
251 }
252 if (dpo || fua) {
253 pr2serr(ME "for 6 byte commands, neither dpo nor fua bits "
254 "supported\n");
255 return 1;
256 }
257 break;
258 case 10:
259 sz_ind = 1;
260 cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
261 rd_opcode[sz_ind]);
262 sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2);
263 sg_put_unaligned_be16((uint16_t)blocks, cdbp + 7);
264 if (blocks & (~0xffff)) {
265 pr2serr(ME "for 10 byte commands, maximum number of blocks is "
266 "%d\n", 0xffff);
267 return 1;
268 }
269 break;
270 case 12:
271 sz_ind = 2;
272 cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
273 rd_opcode[sz_ind]);
274 sg_put_unaligned_be32((uint32_t)start_block, cdbp + 2);
275 sg_put_unaligned_be32((uint32_t)blocks, cdbp + 6);
276 break;
277 case 16:
278 sz_ind = 3;
279 cdbp[0] = (uint8_t)(write_true ? wr_opcode[sz_ind] :
280 rd_opcode[sz_ind]);
281 sg_put_unaligned_be64(start_block, cdbp + 2);
282 sg_put_unaligned_be32((uint32_t)blocks, cdbp + 10);
283 break;
284 default:
285 pr2serr(ME "expected cdb size of 6, 10, 12, or 16 but got %d\n",
286 cdb_sz);
287 return 1;
288 }
289 return 0;
290 }
291
292 /* -3 medium/hardware error, -2 -> not ready, 0 -> successful,
293 1 -> recoverable (ENOMEM), 2 -> try again (e.g. unit attention),
294 3 -> try again (e.g. aborted command), -1 -> other unrecoverable error */
295 static int
sg_bread(int sg_fd,uint8_t * buff,int blocks,int64_t from_block,int bs,int cdbsz,bool fua,bool dpo,bool * diop,bool do_mmap,bool no_dxfer)296 sg_bread(int sg_fd, uint8_t * buff, int blocks, int64_t from_block, int bs,
297 int cdbsz, bool fua, bool dpo, bool * diop, bool do_mmap,
298 bool no_dxfer)
299 {
300 uint8_t rdCmd[MAX_SCSI_CDBSZ];
301 uint8_t senseBuff[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
302 struct sg_io_hdr io_hdr;
303
304 if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, false, fua,
305 dpo)) {
306 pr2serr(ME "bad cdb build, from_block=%" PRId64 ", blocks=%d\n",
307 from_block, blocks);
308 return -1;
309 }
310 memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
311 io_hdr.interface_id = 'S';
312 io_hdr.cmd_len = cdbsz;
313 io_hdr.cmdp = rdCmd;
314 if (blocks > 0) {
315 io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
316 io_hdr.dxfer_len = bs * blocks;
317 /* next: shows dxferp unused during mmap-ed IO */
318 if (! do_mmap)
319 io_hdr.dxferp = buff;
320 if (diop && *diop)
321 io_hdr.flags |= SG_FLAG_DIRECT_IO;
322 else if (do_mmap)
323 io_hdr.flags |= SG_FLAG_MMAP_IO;
324 else if (no_dxfer)
325 io_hdr.flags |= SG_FLAG_NO_DXFER;
326 } else
327 io_hdr.dxfer_direction = SG_DXFER_NONE;
328 io_hdr.mx_sb_len = SENSE_BUFF_LEN;
329 io_hdr.sbp = senseBuff;
330 io_hdr.timeout = DEF_TIMEOUT;
331 io_hdr.pack_id = pack_id_count++;
332 if (verbose > 1) {
333 char b[128];
334
335 pr2serr(" READ cdb: %s\n",
336 sg_get_command_str(rdCmd, cdbsz, false, sizeof(b), b));
337 }
338
339 if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
340 if (ENOMEM == errno)
341 return 1;
342 perror("reading (SG_IO) on sg device, error");
343 return -1;
344 }
345
346 if (verbose > 2)
347 pr2serr( " duration=%u ms\n", io_hdr.duration);
348 switch (sg_err_category3(&io_hdr)) {
349 case SG_LIB_CAT_CLEAN:
350 break;
351 case SG_LIB_CAT_RECOVERED:
352 if (verbose > 1)
353 sg_chk_n_print3("reading, continue", &io_hdr, true);
354 break;
355 case SG_LIB_CAT_UNIT_ATTENTION:
356 if (verbose)
357 sg_chk_n_print3("reading", &io_hdr, (verbose > 1));
358 return 2;
359 case SG_LIB_CAT_ABORTED_COMMAND:
360 if (verbose)
361 sg_chk_n_print3("reading", &io_hdr, (verbose > 1));
362 return 3;
363 case SG_LIB_CAT_NOT_READY:
364 if (verbose)
365 sg_chk_n_print3("reading", &io_hdr, (verbose > 1));
366 return -2;
367 case SG_LIB_CAT_MEDIUM_HARD:
368 if (verbose)
369 sg_chk_n_print3("reading", &io_hdr, (verbose > 1));
370 return -3;
371 default:
372 sg_chk_n_print3("reading", &io_hdr, !! verbose);
373 return -1;
374 }
375 if (blocks > 0) {
376 if (diop && *diop &&
377 ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
378 *diop = 0; /* flag that dio not done (completely) */
379 sum_of_resids += io_hdr.resid;
380 }
381 return 0;
382 }
383
384 /* Returns the number of times 'ch' is found in string 's' given the
385 * string's length. */
386 static int
num_chs_in_str(const char * s,int slen,int ch)387 num_chs_in_str(const char * s, int slen, int ch)
388 {
389 int res = 0;
390
391 while (--slen >= 0) {
392 if (ch == s[slen])
393 ++res;
394 }
395 return res;
396 }
397
398 #define STR_SZ 1024
399 #define INF_SZ 512
400 #define EBUFF_SZ 768
401
402
403 int
main(int argc,char * argv[])404 main(int argc, char * argv[])
405 {
406 bool count_given = false;
407 bool dio_tmp;
408 bool do_blk_sgio = false;
409 bool do_dio = false;
410 bool do_mmap = false;
411 bool do_odir = false;
412 bool dpo = false;
413 bool fua = false;
414 bool no_dxfer = false;
415 bool verbose_given = false;
416 bool version_given = false;
417 int bs = 0;
418 int bpt = DEF_BLOCKS_PER_TRANSFER;
419 int dio_incomplete = 0;
420 int do_time = 0;
421 int in_type = FT_OTHER;
422 int ret = 0;
423 int scsi_cdbsz = DEF_SCSI_CDBSZ;
424 int res, k, t, buf_sz, iters, infd, blocks, flags, blocks_per, err;
425 int n, keylen;
426 size_t psz;
427 int64_t skip = 0;
428 char * key;
429 char * buf;
430 uint8_t * wrkBuff = NULL;
431 uint8_t * wrkPos = NULL;
432 char inf[INF_SZ];
433 char outf[INF_SZ];
434 char str[STR_SZ];
435 char ebuff[EBUFF_SZ];
436 const char * read_str;
437 struct timeval start_tm, end_tm;
438
439 #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
440 psz = sysconf(_SC_PAGESIZE); /* POSIX.1 (was getpagesize()) */
441 #else
442 psz = 4096; /* give up, pick likely figure */
443 #endif
444 inf[0] = '\0';
445
446 for (k = 1; k < argc; k++) {
447 if (argv[k]) {
448 strncpy(str, argv[k], STR_SZ);
449 str[STR_SZ - 1] = '\0';
450 } else
451 continue;
452 for (key = str, buf = key; (*buf && (*buf != '=')); )
453 buf++;
454 if (*buf)
455 *buf++ = '\0';
456 keylen = strlen(key);
457 if (0 == strcmp(key,"blk_sgio"))
458 do_blk_sgio = !! sg_get_num(buf);
459 else if (0 == strcmp(key,"bpt")) {
460 bpt = sg_get_num(buf);
461 if ((bpt < 0) || (bpt > MAX_BPT_VALUE)) {
462 pr2serr( ME "bad argument to 'bpt'\n");
463 return SG_LIB_SYNTAX_ERROR;
464 }
465 } else if (0 == strcmp(key,"bs")) {
466 bs = sg_get_num(buf);
467 if ((bs < 0) || (bs > MAX_BPT_VALUE)) {
468 pr2serr( ME "bad argument to 'bs'\n");
469 return SG_LIB_SYNTAX_ERROR;
470 }
471 } else if (0 == strcmp(key,"cdbsz")) {
472 scsi_cdbsz = sg_get_num(buf);
473 if ((scsi_cdbsz < 0) || (scsi_cdbsz > 32)) {
474 pr2serr( ME "bad argument to 'cdbsz', expect 6, 10, 12, 16 "
475 "or 32\n");
476 return SG_LIB_SYNTAX_ERROR;
477 }
478 } else if (0 == strcmp(key,"count")) {
479 count_given = true;
480 if ('-' == *buf) {
481 dd_count = sg_get_llnum(buf + 1);
482 if ((dd_count < 0) || (dd_count > MAX_COUNT_SKIP_SEEK)) {
483 pr2serr( ME "bad argument to 'count'\n");
484 return SG_LIB_SYNTAX_ERROR;
485 }
486 dd_count = - dd_count;
487 } else {
488 dd_count = sg_get_llnum(buf);
489 if ((dd_count < 0) || (dd_count > MAX_COUNT_SKIP_SEEK)) {
490 pr2serr( ME "bad argument to 'count'\n");
491 return SG_LIB_SYNTAX_ERROR;
492 }
493 }
494 } else if (0 == strcmp(key,"dio"))
495 do_dio = !! sg_get_num(buf);
496 else if (0 == strcmp(key,"dpo"))
497 dpo = !! sg_get_num(buf);
498 else if (0 == strcmp(key,"fua"))
499 fua = !! sg_get_num(buf);
500 else if (strcmp(key,"if") == 0) {
501 memcpy(inf, buf, INF_SZ - 1);
502 inf[INF_SZ - 1] = '\0';
503 } else if (0 == strcmp(key,"mmap"))
504 do_mmap = !! sg_get_num(buf);
505 else if (0 == strcmp(key,"no_dxfer"))
506 no_dxfer = !! sg_get_num(buf);
507 else if (0 == strcmp(key,"odir"))
508 do_odir = !! sg_get_num(buf);
509 else if (strcmp(key,"of") == 0) {
510 memcpy(outf, buf, INF_SZ - 1);
511 outf[INF_SZ - 1] = '\0';
512 } else if (0 == strcmp(key,"skip")) {
513 skip = sg_get_llnum(buf);
514 if ((skip < 0) || (skip > MAX_COUNT_SKIP_SEEK)) {
515 pr2serr( ME "bad argument to 'skip'\n");
516 return SG_LIB_SYNTAX_ERROR;
517 }
518 } else if (0 == strcmp(key,"time"))
519 do_time = sg_get_num(buf);
520 else if (0 == strncmp(key, "verb", 4)) {
521 verbose_given = true;
522 verbose = sg_get_num(buf);
523 } else if (0 == strncmp(key, "--help", 6)) {
524 usage();
525 return 0;
526 } else if (0 == strncmp(key, "--verb", 6)) {
527 verbose_given = true;
528 ++verbose;
529 } else if (0 == strncmp(key, "--vers", 6))
530 version_given = true;
531 else if ((keylen > 1) && ('-' == key[0]) && ('-' != key[1])) {
532 res = 0;
533 n = num_chs_in_str(key + 1, keylen - 1, 'h');
534 if (n > 0) {
535 usage();
536 return 0;
537 }
538 n = num_chs_in_str(key + 1, keylen - 1, 'v');
539 if (n > 0)
540 verbose_given = true;
541 verbose += n;
542 res += n;
543 n = num_chs_in_str(key + 1, keylen - 1, 'V');
544 if (n > 0)
545 version_given = true;
546 res += n;
547 if (res < (keylen - 1)) {
548 pr2serr("Unrecognised short option in '%s', try '--help'\n",
549 key);
550 return SG_LIB_SYNTAX_ERROR;
551 }
552 } else {
553 pr2serr( "Unrecognized argument '%s'\n", key);
554 usage();
555 return SG_LIB_SYNTAX_ERROR;
556 }
557 }
558
559 #ifdef DEBUG
560 pr2serr("In DEBUG mode, ");
561 if (verbose_given && version_given) {
562 pr2serr("but override: '-vV' given, zero verbose and continue\n");
563 verbose_given = false;
564 version_given = false;
565 verbose = 0;
566 } else if (! verbose_given) {
567 pr2serr("set '-vv'\n");
568 verbose = 2;
569 } else
570 pr2serr("keep verbose=%d\n", verbose);
571 #else
572 if (verbose_given && version_given)
573 pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
574 #endif
575 if (version_given) {
576 pr2serr( ME ": %s\n", version_str);
577 return 0;
578 }
579
580 if (bs <= 0) {
581 bs = DEF_BLOCK_SIZE;
582 if ((dd_count > 0) && (bpt > 0))
583 pr2serr( "Assume default 'bs' (block size) of %d bytes\n", bs);
584 }
585 if (! count_given) {
586 pr2serr("'count' must be given\n");
587 usage();
588 return SG_LIB_SYNTAX_ERROR;
589 }
590 if (skip < 0) {
591 pr2serr("skip cannot be negative\n");
592 return SG_LIB_SYNTAX_ERROR;
593 }
594 if (bpt < 1) {
595 if (0 == bpt) {
596 if (dd_count > 0)
597 dd_count = - dd_count;
598 } else {
599 pr2serr("bpt must be greater than 0\n");
600 return SG_LIB_SYNTAX_ERROR;
601 }
602 }
603 if (do_dio && do_mmap) {
604 pr2serr("cannot select both dio and mmap\n");
605 return SG_LIB_CONTRADICT;
606 }
607 if (no_dxfer && (do_dio || do_mmap)) {
608 pr2serr("cannot select no_dxfer with dio or mmap\n");
609 return SG_LIB_CONTRADICT;
610 }
611
612 install_handler (SIGINT, interrupt_handler);
613 install_handler (SIGQUIT, interrupt_handler);
614 install_handler (SIGPIPE, interrupt_handler);
615 install_handler (SIGUSR1, siginfo_handler);
616
617 if (! inf[0]) {
618 pr2serr("must provide 'if=<filename>'\n");
619 usage();
620 return SG_LIB_SYNTAX_ERROR;
621 }
622 if (0 == strcmp("-", inf)) {
623 pr2serr("'-' (stdin) invalid as <filename>\n");
624 usage();
625 return SG_LIB_SYNTAX_ERROR;
626 }
627 in_type = dd_filetype(inf);
628 if (FT_ERROR == in_type) {
629 pr2serr("Unable to access: %s\n", inf);
630 return SG_LIB_FILE_ERROR;
631 } else if ((FT_BLOCK & in_type) && do_blk_sgio)
632 in_type |= FT_SG;
633
634 if (FT_SG & in_type) {
635 if ((dd_count < 0) && (6 == scsi_cdbsz)) {
636 pr2serr(ME "SCSI READ (6) can't do zero block reads\n");
637 return SG_LIB_SYNTAX_ERROR;
638 }
639 flags = O_RDWR;
640 if (do_odir)
641 flags |= O_DIRECT;
642 if ((infd = open(inf, flags)) < 0) {
643 flags = O_RDONLY;
644 if (do_odir)
645 flags |= O_DIRECT;
646 if ((infd = open(inf, flags)) < 0) {
647 err = errno;
648 snprintf(ebuff, EBUFF_SZ,
649 ME "could not open %s for sg reading", inf);
650 perror(ebuff);
651 return sg_convert_errno(err);
652 }
653 }
654 if (verbose)
655 pr2serr("Opened %s for SG_IO with flags=0x%x\n", inf, flags);
656 if ((dd_count > 0) && (! (FT_BLOCK & in_type))) {
657 if (verbose > 2) {
658 if (ioctl(infd, SG_GET_RESERVED_SIZE, &t) >= 0)
659 pr2serr(" SG_GET_RESERVED_SIZE yields: %d\n", t);
660 }
661 t = bs * bpt;
662 if ((do_mmap) && (0 != (t % psz)))
663 t = ((t / psz) + 1) * psz; /* round up to next pagesize */
664 res = ioctl(infd, SG_SET_RESERVED_SIZE, &t);
665 if (res < 0)
666 perror(ME "SG_SET_RESERVED_SIZE error");
667 res = ioctl(infd, SG_GET_VERSION_NUM, &t);
668 if ((res < 0) || (t < 30000)) {
669 pr2serr(ME "sg driver prior to 3.x.y\n");
670 return SG_LIB_CAT_OTHER;
671 }
672 if (do_mmap && (t < 30122)) {
673 pr2serr(ME "mmap-ed IO needs a sg driver version >= 3.1.22\n");
674 return SG_LIB_CAT_OTHER;
675 }
676 }
677 } else {
678 if (do_mmap) {
679 pr2serr(ME "mmap-ed IO only support on sg devices\n");
680 return SG_LIB_CAT_OTHER;
681 }
682 if (dd_count < 0) {
683 pr2serr(ME "negative 'count' only supported with SCSI READs\n");
684 return SG_LIB_CAT_OTHER;
685 }
686 flags = O_RDONLY;
687 if (do_odir)
688 flags |= O_DIRECT;
689 if ((infd = open(inf, flags)) < 0) {
690 err = errno;
691 snprintf(ebuff, EBUFF_SZ,
692 ME "could not open %s for reading", inf);
693 perror(ebuff);
694 return sg_convert_errno(err);
695 }
696 if (verbose)
697 pr2serr("Opened %s for Unix reads with flags=0x%x\n", inf, flags);
698 if (skip > 0) {
699 off64_t offset = skip;
700
701 offset *= bs; /* could exceed 32 bits here! */
702 if (lseek64(infd, offset, SEEK_SET) < 0) {
703 err = errno;
704 snprintf(ebuff, EBUFF_SZ,
705 ME "couldn't skip to required position on %s", inf);
706 perror(ebuff);
707 return sg_convert_errno(err);
708 }
709 }
710 }
711
712 if (0 == dd_count)
713 return 0;
714 orig_count = dd_count;
715
716 if (dd_count > 0) {
717 if (do_dio || do_odir || (FT_RAW & in_type)) {
718 wrkBuff = (uint8_t *)malloc(bs * bpt + psz);
719 if (0 == wrkBuff) {
720 pr2serr("Not enough user memory for aligned storage\n");
721 return SG_LIB_CAT_OTHER;
722 }
723 /* perhaps use posix_memalign() instead */
724 wrkPos = (uint8_t *)(((sg_uintptr_t)wrkBuff + psz - 1) &
725 (~(psz - 1)));
726 } else if (do_mmap) {
727 wrkPos = (uint8_t *)mmap(NULL, bs * bpt,
728 PROT_READ | PROT_WRITE, MAP_SHARED, infd, 0);
729 if (MAP_FAILED == wrkPos) {
730 perror(ME "error from mmap()");
731 return SG_LIB_CAT_OTHER;
732 }
733 } else {
734 wrkBuff = (uint8_t *)malloc(bs * bpt);
735 if (0 == wrkBuff) {
736 pr2serr("Not enough user memory\n");
737 return SG_LIB_CAT_OTHER;
738 }
739 wrkPos = wrkBuff;
740 }
741 }
742
743 blocks_per = bpt;
744 start_tm.tv_sec = 0; /* just in case start set condition not met */
745 start_tm.tv_usec = 0;
746
747 if (verbose && (dd_count < 0))
748 pr2serr("About to issue %" PRId64 " zero block SCSI READs\n",
749 0 - dd_count);
750
751 /* main loop */
752 for (iters = 0; dd_count != 0; ++iters) {
753 if ((do_time > 0) && (iters == (do_time - 1)))
754 gettimeofday(&start_tm, NULL);
755 if (dd_count < 0)
756 blocks = 0;
757 else
758 blocks = (dd_count > blocks_per) ? blocks_per : dd_count;
759 if (FT_SG & in_type) {
760 dio_tmp = do_dio;
761 res = sg_bread(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
762 fua, dpo, &dio_tmp, do_mmap, no_dxfer);
763 if (1 == res) { /* ENOMEM, find what's available+try that */
764 if (ioctl(infd, SG_GET_RESERVED_SIZE, &buf_sz) < 0) {
765 perror("RESERVED_SIZE ioctls failed");
766 break;
767 }
768 if (buf_sz < MIN_RESERVED_SIZE)
769 buf_sz = MIN_RESERVED_SIZE;
770 blocks_per = (buf_sz + bs - 1) / bs;
771 blocks = blocks_per;
772 pr2serr("Reducing read to %d blocks per loop\n", blocks_per);
773 res = sg_bread(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
774 fua, dpo, &dio_tmp, do_mmap, no_dxfer);
775 } else if (2 == res) {
776 pr2serr("Unit attention, try again (r)\n");
777 res = sg_bread(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
778 fua, dpo, &dio_tmp, do_mmap, no_dxfer);
779 }
780 if (0 != res) {
781 switch (res) {
782 case -3:
783 ret = SG_LIB_CAT_MEDIUM_HARD;
784 pr2serr(ME "SCSI READ medium/hardware error\n");
785 break;
786 case -2:
787 ret = SG_LIB_CAT_NOT_READY;
788 pr2serr(ME "device not ready\n");
789 break;
790 case 2:
791 ret = SG_LIB_CAT_UNIT_ATTENTION;
792 pr2serr(ME "SCSI READ unit attention\n");
793 break;
794 case 3:
795 ret = SG_LIB_CAT_ABORTED_COMMAND;
796 pr2serr(ME "SCSI READ aborted command\n");
797 break;
798 default:
799 ret = SG_LIB_CAT_OTHER;
800 pr2serr(ME "SCSI READ failed\n");
801 break;
802 }
803 break;
804 } else {
805 in_full += blocks;
806 if (do_dio && (0 == dio_tmp))
807 dio_incomplete++;
808 }
809 } else {
810 if (iters > 0) { /* subsequent iteration reset skip position */
811 off64_t offset = skip;
812
813 offset *= bs; /* could exceed 32 bits here! */
814 if (lseek64(infd, offset, SEEK_SET) < 0) {
815 perror(ME "could not reset skip position");
816 break;
817 }
818 }
819 while (((res = read(infd, wrkPos, blocks * bs)) < 0) &&
820 (EINTR == errno))
821 ;
822 if (res < 0) {
823 snprintf(ebuff, EBUFF_SZ, ME "reading, skip=%" PRId64 " ",
824 skip);
825 perror(ebuff);
826 break;
827 } else if (res < blocks * bs) {
828 pr2serr(ME "short read: wanted/got=%d/%d bytes, stop\n",
829 blocks * bs, res);
830 blocks = res / bs;
831 if ((res % bs) > 0) {
832 blocks++;
833 in_partial++;
834 }
835 dd_count -= blocks;
836 in_full += blocks;
837 break;
838 }
839 in_full += blocks;
840 }
841 if (dd_count > 0)
842 dd_count -= blocks;
843 else if (dd_count < 0)
844 ++dd_count;
845 }
846 read_str = (FT_SG & in_type) ? "SCSI READ" : "read";
847 if (do_time > 0) {
848 gettimeofday(&end_tm, NULL);
849 if (start_tm.tv_sec || start_tm.tv_usec) {
850 struct timeval res_tm;
851 double a, b, c;
852
853 res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
854 res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
855 if (res_tm.tv_usec < 0) {
856 --res_tm.tv_sec;
857 res_tm.tv_usec += 1000000;
858 }
859 a = res_tm.tv_sec;
860 a += (0.000001 * res_tm.tv_usec);
861 if (orig_count > 0) {
862 b = (double)bs * (orig_count - dd_count);
863 if (do_time > 1)
864 c = b - ((double)bs * ((do_time - 1.0) * bpt));
865 else
866 c = 0.0;
867 } else {
868 b = 0.0;
869 c = 0.0;
870 }
871
872 if (1 == do_time) {
873 pr2serr("Time for all %s commands was %d.%06d secs", read_str,
874 (int)res_tm.tv_sec, (int)res_tm.tv_usec);
875 if ((orig_count > 0) && (a > 0.00001) && (b > 511))
876 pr2serr(", %.2f MB/sec\n", b / (a * 1000000.0));
877 else
878 pr2serr("\n");
879 } else if (2 == do_time) {
880 pr2serr("Time from second %s command to end was %d.%06d secs",
881 read_str, (int)res_tm.tv_sec,
882 (int)res_tm.tv_usec);
883 if ((orig_count > 0) && (a > 0.00001) && (c > 511))
884 pr2serr(", %.2f MB/sec\n", c / (a * 1000000.0));
885 else
886 pr2serr("\n");
887 } else {
888 pr2serr("Time from start of %s command "
889 "#%d to end was %d.%06d secs", read_str, do_time,
890 (int)res_tm.tv_sec, (int)res_tm.tv_usec);
891 if ((orig_count > 0) && (a > 0.00001) && (c > 511))
892 pr2serr(", %.2f MB/sec\n", c / (a * 1000000.0));
893 else
894 pr2serr("\n");
895 }
896 if ((iters > 0) && (a > 0.00001))
897 pr2serr("Average number of %s commands per second was %.2f\n",
898 read_str, (double)iters / a);
899 }
900 }
901
902 if (wrkBuff)
903 free(wrkBuff);
904
905 close(infd);
906 if (0 != dd_count) {
907 pr2serr("Some error occurred,");
908 if (0 == ret)
909 ret = SG_LIB_CAT_OTHER;
910 }
911 print_stats(iters, read_str);
912
913 if (dio_incomplete) {
914 int fd;
915 char c;
916
917 pr2serr(">> Direct IO requested but incomplete %d times\n",
918 dio_incomplete);
919 if ((fd = open(sg_allow_dio, O_RDONLY)) >= 0) {
920 if (1 == read(fd, &c, 1)) {
921 if ('0' == c)
922 pr2serr(">>> %s set to '0' but should be set to '1' for "
923 "direct IO\n", sg_allow_dio);
924 }
925 close(fd);
926 }
927 }
928 if (sum_of_resids)
929 pr2serr(">> Non-zero sum of residual counts=%d\n", sum_of_resids);
930 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
931 }
932