1 /*
2 * A utility program for the Linux OS SCSI generic ("sg") device driver.
3 * Copyright (C) 1999-2010 D. Gilbert and P. Allworth
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 is a specialization of the Unix "dd" command in which
12 * one or both of the given files is a scsi generic device or a raw
13 * device. A block size ('bs') is assumed to be 512 if not given. This
14 * program complains if 'ibs' or 'obs' are given with some other value
15 * than 'bs'. If 'if' is not given or 'if=-' then stdin is assumed. If
16 * 'of' is not given or 'of=-' then stdout assumed. Multipliers:
17 * 'c','C' *1 'b','B' *512 'k' *1024 'K' *1000
18 * 'm' *(1024^2) 'M' *(1000^2) 'g' *(1024^3) 'G' *(1000^3)
19 *
20 * A non-standard argument "bpt" (blocks per transfer) is added to control
21 * the maximum number of blocks in each transfer. The default value is 128.
22 * For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16KB
23 * in this case) are transferred to or from the sg device in a single SCSI
24 * command.
25 *
26 * This version should compile with Linux sg drivers with version numbers
27 * >= 30000 . This version uses queuing within the Linux sg driver.
28 */
29
30 #define _XOPEN_SOURCE 500
31
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <stdint.h>
37 #include <stdbool.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <limits.h>
42 #include <signal.h>
43 #include <poll.h>
44 #include <sys/ioctl.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/sysmacros.h>
48 #include <linux/major.h>
49 #include <sys/time.h>
50 typedef uint8_t u_char; /* horrible, for scsi.h */
51 #include "sg_lib.h"
52 #include "sg_io_linux.h"
53 #include "sg_unaligned.h"
54
55
56 static char * version_str = "0.63 20190324";
57 /* resurrected from "0.55 20020509" */
58
59 #define DEF_BLOCK_SIZE 512
60 #define DEF_BLOCKS_PER_TRANSFER 128
61
62
63 #define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
64 #define DEF_TIMEOUT 60000 /* 60,000 millisecs == 60 seconds */
65 #define S_RW_LEN 10 /* Use SCSI READ(10) and WRITE(10) */
66
67 #define SGP_READ10 0x28
68 #define SGP_WRITE10 0x2a
69 #define DEF_NUM_THREADS 4 /* actually degree of concurrency */
70 #define MAX_NUM_THREADS 1024
71
72 #ifndef RAW_MAJOR
73 #define RAW_MAJOR 255 /*unlikely value */
74 #endif
75
76 #define FT_OTHER 0 /* filetype other than sg or raw device */
77 #define FT_SG 1 /* filetype is sg char device */
78 #define FT_RAW 2 /* filetype is raw char device */
79
80 #define QS_IDLE 0 /* ready to start a copy cycle */
81 #define QS_IN_STARTED 1 /* commenced read */
82 #define QS_IN_FINISHED 2 /* finished read, ready for write */
83 #define QS_OUT_STARTED 3 /* commenced write */
84
85 #define QS_IN_POLL 11
86 #define QS_OUT_POLL 12
87
88 #define STR_SZ 1024
89 #define INOUTF_SZ 512
90 #define EBUFF_SZ 512
91
92
93 struct request_element;
94
95 typedef struct request_collection
96 { /* one instance visible to all threads */
97 int infd;
98 int skip;
99 int in_type;
100 int in_scsi_type;
101 int in_blk; /* next block address to read */
102 int in_count; /* blocks remaining for next read */
103 int in_done_count; /* count of completed in blocks */
104 int in_partial;
105 int outfd;
106 int seek;
107 int out_type;
108 int out_scsi_type;
109 int out_blk; /* next block address to write */
110 int out_count; /* blocks remaining for next write */
111 int out_done_count; /* count of completed out blocks */
112 int out_partial;
113 int bs;
114 int bpt;
115 int dio;
116 int dio_incomplete;
117 int sum_of_resids;
118 int coe;
119 int debug;
120 int num_rq_elems;
121 struct request_element * req_arr;
122 } Rq_coll;
123
124 typedef struct request_element
125 { /* one instance per worker thread */
126 int qstate; /* "QS" state */
127 int infd;
128 int outfd;
129 int wr;
130 int blk;
131 int num_blks;
132 uint8_t * buffp;
133 uint8_t * alloc_bp;
134 sg_io_hdr_t io_hdr;
135 uint8_t cmd[S_RW_LEN];
136 uint8_t sb[SENSE_BUFF_LEN];
137 int bs;
138 int dio;
139 int dio_incomplete;
140 int resid;
141 int in_scsi_type;
142 int out_scsi_type;
143 int debug;
144 } Rq_elem;
145
146 static Rq_coll rcoll;
147 static struct pollfd in_pollfd_arr[MAX_NUM_THREADS];
148 static struct pollfd out_pollfd_arr[MAX_NUM_THREADS];
149 static int dd_count = -1;
150
151 static const char * sg_allow_dio = "/sys/module/sg/parameters/allow_dio";
152
153 static int sg_finish_io(int wr, Rq_elem * rep);
154
155
156 /* Returns the number of times 'ch' is found in string 's' given the
157 * string's length. */
158 static int
num_chs_in_str(const char * s,int slen,int ch)159 num_chs_in_str(const char * s, int slen, int ch)
160 {
161 int res = 0;
162
163 while (--slen >= 0) {
164 if (ch == s[slen])
165 ++res;
166 }
167 return res;
168 }
169
170 static void
install_handler(int sig_num,void (* sig_handler)(int sig))171 install_handler (int sig_num, void (*sig_handler) (int sig))
172 {
173 struct sigaction sigact;
174 sigaction (sig_num, NULL, &sigact);
175 if (sigact.sa_handler != SIG_IGN)
176 {
177 sigact.sa_handler = sig_handler;
178 sigemptyset (&sigact.sa_mask);
179 sigact.sa_flags = 0;
180 sigaction (sig_num, &sigact, NULL);
181 }
182 }
183
184 static void
print_stats()185 print_stats()
186 {
187 int infull, outfull;
188
189 if (0 != rcoll.out_count)
190 fprintf(stderr, " remaining block count=%d\n", rcoll.out_count);
191 infull = dd_count - rcoll.in_done_count - rcoll.in_partial;
192 fprintf(stderr, "%d+%d records in\n", infull, rcoll.in_partial);
193 outfull = dd_count - rcoll.out_done_count - rcoll.out_partial;
194 fprintf(stderr, "%d+%d records out\n", outfull, rcoll.out_partial);
195 }
196
197 static void
interrupt_handler(int sig)198 interrupt_handler(int sig)
199 {
200 struct sigaction sigact;
201
202 sigact.sa_handler = SIG_DFL;
203 sigemptyset (&sigact.sa_mask);
204 sigact.sa_flags = 0;
205 sigaction (sig, &sigact, NULL);
206 fprintf(stderr, "Interrupted by signal,");
207 print_stats ();
208 kill (getpid (), sig);
209 }
210
211 static void
siginfo_handler(int sig)212 siginfo_handler(int sig)
213 {
214 fprintf(stderr, "Progress report, continuing ...\n");
215 print_stats ();
216 if (sig) { } /* suppress unused warning */
217 }
218
219 static int
dd_filetype(const char * filename)220 dd_filetype(const char * filename)
221 {
222 struct stat st;
223
224 if (stat(filename, &st) < 0)
225 return FT_OTHER;
226 if (S_ISCHR(st.st_mode)) {
227 if (RAW_MAJOR == major(st.st_rdev))
228 return FT_RAW;
229 else if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
230 return FT_SG;
231 }
232 return FT_OTHER;
233 }
234
235 static void
usage()236 usage()
237 {
238 fprintf(stderr, "Usage: "
239 "sgq_dd [if=<infile>] [skip=<n>] [of=<ofile>] [seek=<n>] "
240 "[bs=<num>]\n"
241 " [bpt=<num>] [count=<n>] [dio=0|1] [thr=<n>] "
242 "[coe=0|1] [gen=<n>]\n"
243 " [time=0|1] [deb=<n>] [--version]\n"
244 " usually either 'if' or 'of' is a sg or raw device\n"
245 " 'bpt' is blocks_per_transfer (default is 128)\n"
246 " 'dio' is direct IO, 1->attempt, 0->indirect IO (def)\n"
247 " 'thr' is number of queues, must be > 0, default 4, max 1024\n");
248 fprintf(stderr, " 'coe' continue on sg error, 0->exit (def), "
249 "1->zero + continue\n"
250 " 'time' 0->no timing(def), 1->time plus calculate throughput\n"
251 " 'gen' 0-> 1 file is special(def), 1-> any files allowed\n"
252 " 'deb' is debug, 0->none (def), > 0->varying degrees of debug\n");
253 }
254
255 /* Returns -1 for error, 0 for nothing found, QS_IN_POLL or QS_OUT_POLL */
256 static int
do_poll(Rq_coll * clp,int timeout,int * req_indexp)257 do_poll(Rq_coll * clp, int timeout, int * req_indexp)
258 {
259 int k, res;
260
261 if (FT_SG == clp->out_type) {
262 while (((res = poll(out_pollfd_arr, clp->num_rq_elems, timeout)) < 0)
263 && (EINTR == errno))
264 ;
265 if (res < 0) {
266 perror("poll error on output fds");
267 return -1;
268 }
269 else if (res > 0) {
270 for (k = 0; k < clp->num_rq_elems; ++k) {
271 if (out_pollfd_arr[k].revents & POLLIN) {
272 if (req_indexp)
273 *req_indexp = k;
274 return QS_OUT_POLL;
275 }
276 }
277 }
278 }
279 if (FT_SG == clp->in_type) {
280 while (((res = poll(in_pollfd_arr, clp->num_rq_elems, timeout)) < 0)
281 && (EINTR == errno))
282 ;
283 if (res < 0) {
284 perror("poll error on input fds");
285 return -1;
286 }
287 else if (res > 0) {
288 for (k = 0; k < clp->num_rq_elems; ++k) {
289 if (in_pollfd_arr[k].revents & POLLIN) {
290 if (req_indexp)
291 *req_indexp = k;
292 return QS_IN_POLL;
293 }
294 }
295 }
296 }
297 return 0;
298 }
299
300
301 /* Return of 0 -> success, -1 -> failure, 2 -> try again */
302 static int
read_capacity(int sg_fd,int * num_sect,int * sect_sz)303 read_capacity(int sg_fd, int * num_sect, int * sect_sz)
304 {
305 int res;
306 uint8_t rc_cdb [10] = {0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0};
307 uint8_t rcBuff[64];
308 uint8_t sense_b[64];
309 sg_io_hdr_t io_hdr;
310
311 memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
312 io_hdr.interface_id = 'S';
313 io_hdr.cmd_len = sizeof(rc_cdb);
314 io_hdr.mx_sb_len = sizeof(sense_b);
315 io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
316 io_hdr.dxfer_len = sizeof(rcBuff);
317 io_hdr.dxferp = rcBuff;
318 io_hdr.cmdp = rc_cdb;
319 io_hdr.sbp = sense_b;
320 io_hdr.timeout = DEF_TIMEOUT;
321
322 if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
323 perror("read_capacity (SG_IO) error");
324 return -1;
325 }
326 res = sg_err_category3(&io_hdr);
327 if (SG_LIB_CAT_UNIT_ATTENTION == res)
328 return 2; /* probably have another go ... */
329 else if (SG_LIB_CAT_CLEAN != res) {
330 sg_chk_n_print3("read capacity", &io_hdr, 1);
331 return -1;
332 }
333 *num_sect = 1 + sg_get_unaligned_be32(rcBuff + 0);
334 *sect_sz = sg_get_unaligned_be32(rcBuff + 4);
335 #ifdef DEBUG
336 fprintf(stderr, "number of sectors=%d, sector size=%d\n",
337 *num_sect, *sect_sz);
338 #endif
339 return 0;
340 }
341
342 /* 0 -> ok, 1 -> short read, -1 -> error */
343 static int
normal_in_operation(Rq_coll * clp,Rq_elem * rep,int blocks)344 normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks)
345 {
346 int res;
347 int stop_after_write = 0;
348
349 rep->qstate = QS_IN_STARTED;
350 if (rep->debug > 8)
351 fprintf(stderr, "normal_in_operation: start blk=%d num_blks=%d\n",
352 rep->blk, rep->num_blks);
353 while (((res = read(rep->infd, rep->buffp,
354 blocks * rep->bs)) < 0) && (EINTR == errno))
355 ;
356 if (res < 0) {
357 fprintf(stderr, "sgq_dd: reading, in_blk=%d, errno=%d\n", rep->blk,
358 errno);
359 return -1;
360 }
361 if (res < blocks * rep->bs) {
362 int o_blocks = blocks;
363 stop_after_write = 1;
364 blocks = res / rep->bs;
365 if ((res % rep->bs) > 0) {
366 blocks++;
367 clp->in_partial++;
368 }
369 /* Reverse out + re-apply blocks on clp */
370 clp->in_blk -= o_blocks;
371 clp->in_count += o_blocks;
372 rep->num_blks = blocks;
373 clp->in_blk += blocks;
374 clp->in_count -= blocks;
375 }
376 clp->in_done_count -= blocks;
377 rep->qstate = QS_IN_FINISHED;
378 return stop_after_write;
379 }
380
381 /* 0 -> ok, -1 -> error */
382 static int
normal_out_operation(Rq_coll * clp,Rq_elem * rep,int blocks)383 normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks)
384 {
385 int res;
386
387 rep->qstate = QS_OUT_STARTED;
388 if (rep->debug > 8)
389 fprintf(stderr, "normal_out_operation: start blk=%d num_blks=%d\n",
390 rep->blk, rep->num_blks);
391 while (((res = write(rep->outfd, rep->buffp,
392 rep->num_blks * rep->bs)) < 0) && (EINTR == errno))
393 ;
394 if (res < 0) {
395 fprintf(stderr, "sgq_dd: output, out_blk=%d, errno=%d\n", rep->blk,
396 errno);
397 return -1;
398 }
399 if (res < blocks * rep->bs) {
400 blocks = res / rep->bs;
401 if ((res % rep->bs) > 0) {
402 blocks++;
403 clp->out_partial++;
404 }
405 rep->num_blks = blocks;
406 }
407 clp->out_done_count -= blocks;
408 rep->qstate = QS_IDLE;
409 return 0;
410 }
411
412 /* Returns 1 for retryable, 0 for ok, -ve for error */
413 static int
sg_fin_in_operation(Rq_coll * clp,Rq_elem * rep)414 sg_fin_in_operation(Rq_coll * clp, Rq_elem * rep)
415 {
416 int res;
417
418 rep->qstate = QS_IN_FINISHED;
419 res = sg_finish_io(rep->wr, rep);
420 if (res < 0) {
421 if (clp->coe) {
422 memset(rep->buffp, 0, rep->num_blks * rep->bs);
423 fprintf(stderr, ">> substituted zeros for in blk=%d for "
424 "%d bytes\n", rep->blk, rep->num_blks * rep->bs);
425 res = 0;
426 }
427 else {
428 fprintf(stderr, "error finishing sg in command\n");
429 return res;
430 }
431 }
432 if (0 == res) { /* looks good, going to return */
433 if (rep->dio_incomplete || rep->resid) {
434 clp->dio_incomplete += rep->dio_incomplete;
435 clp->sum_of_resids += rep->resid;
436 }
437 clp->in_done_count -= rep->num_blks;
438 }
439 return res;
440 }
441
442 /* Returns 1 for retryable, 0 for ok, -ve for error */
443 static int
sg_fin_out_operation(Rq_coll * clp,Rq_elem * rep)444 sg_fin_out_operation(Rq_coll * clp, Rq_elem * rep)
445 {
446 int res;
447
448 rep->qstate = QS_IDLE;
449 res = sg_finish_io(rep->wr, rep);
450 if (res < 0) {
451 if (clp->coe) {
452 fprintf(stderr, ">> ignored error for out blk=%d for "
453 "%d bytes\n", rep->blk, rep->num_blks * rep->bs);
454 res = 0;
455 }
456 else {
457 fprintf(stderr, "error finishing sg out command\n");
458 return res;
459 }
460 }
461 if (0 == res) {
462 if (rep->dio_incomplete || rep->resid) {
463 clp->dio_incomplete += rep->dio_incomplete;
464 clp->sum_of_resids += rep->resid;
465 }
466 clp->out_done_count -= rep->num_blks;
467 }
468 return res;
469 }
470
471 static int
sg_start_io(Rq_elem * rep)472 sg_start_io(Rq_elem * rep)
473 {
474 sg_io_hdr_t * hp = &rep->io_hdr;
475 int res;
476
477 rep->qstate = rep->wr ? QS_OUT_STARTED : QS_IN_STARTED;
478 memset(rep->cmd, 0, sizeof(rep->cmd));
479 rep->cmd[0] = rep->wr ? SGP_WRITE10 : SGP_READ10;
480 sg_put_unaligned_be32((uint32_t)rep->blk, rep->cmd + 2);
481 sg_put_unaligned_be16((uint16_t)rep->num_blks, rep->cmd + 7);
482 memset(hp, 0, sizeof(sg_io_hdr_t));
483 hp->interface_id = 'S';
484 hp->cmd_len = sizeof(rep->cmd);
485 hp->cmdp = rep->cmd;
486 hp->dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
487 hp->dxfer_len = rep->bs * rep->num_blks;
488 hp->dxferp = rep->buffp;
489 hp->mx_sb_len = sizeof(rep->sb);
490 hp->sbp = rep->sb;
491 hp->timeout = DEF_TIMEOUT;
492 hp->usr_ptr = rep;
493 hp->pack_id = rep->blk;
494 if (rep->dio)
495 hp->flags |= SG_FLAG_DIRECT_IO;
496 if (rep->debug > 8) {
497 fprintf(stderr, "sg_start_io: SCSI %s, blk=%d num_blks=%d\n",
498 rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks);
499 sg_print_command(hp->cmdp);
500 fprintf(stderr, " len=%d, dxfrp=%p, cmd_len=%d\n",
501 hp->dxfer_len, hp->dxferp, hp->cmd_len);
502 }
503
504 while (((res = write(rep->wr ? rep->outfd : rep->infd, hp,
505 sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno))
506 ;
507 if (res < 0) {
508 if (ENOMEM == errno)
509 return 1;
510 return res;
511 }
512 return 0;
513 }
514
515 /* -1 -> unrecoverable error, 0 -> successful, 1 -> try again */
516 static int
sg_finish_io(int wr,Rq_elem * rep)517 sg_finish_io(int wr, Rq_elem * rep)
518 {
519 int res;
520 sg_io_hdr_t io_hdr;
521 sg_io_hdr_t * hp;
522 #if 0
523 static int testing = 0; /* thread dubious! */
524 #endif
525
526 memset(&io_hdr, 0 , sizeof(sg_io_hdr_t));
527 /* FORCE_PACK_ID active set only read packet with matching pack_id */
528 io_hdr.interface_id = 'S';
529 io_hdr.dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
530 io_hdr.pack_id = rep->blk;
531
532 while (((res = read(wr ? rep->outfd : rep->infd, &io_hdr,
533 sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno))
534 ;
535 if (res < 0) {
536 perror("finishing io on sg device, error");
537 return -1;
538 }
539 if (rep != (Rq_elem *)io_hdr.usr_ptr) {
540 fprintf(stderr,
541 "sg_finish_io: bad usr_ptr, request-response mismatch\n");
542 exit(1);
543 }
544 memcpy(&rep->io_hdr, &io_hdr, sizeof(sg_io_hdr_t));
545 hp = &rep->io_hdr;
546
547 switch (sg_err_category3(hp)) {
548 case SG_LIB_CAT_CLEAN:
549 break;
550 case SG_LIB_CAT_RECOVERED:
551 fprintf(stderr, "Recovered error on block=%d, num=%d\n",
552 rep->blk, rep->num_blks);
553 break;
554 case SG_LIB_CAT_UNIT_ATTENTION:
555 return 1;
556 default:
557 {
558 char ebuff[EBUFF_SZ];
559 snprintf(ebuff, EBUFF_SZ, "%s blk=%d",
560 rep->wr ? "writing": "reading", rep->blk);
561 sg_chk_n_print3(ebuff, hp, 1);
562 return -1;
563 }
564 }
565 #if 0
566 if (0 == (++testing % 100)) return -1;
567 #endif
568 if (rep->dio &&
569 ((hp->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
570 rep->dio_incomplete = 1; /* count dios done as indirect IO */
571 else
572 rep->dio_incomplete = 0;
573 rep->resid = hp->resid;
574 if (rep->debug > 8)
575 fprintf(stderr, "sg_finish_io: completed %s, blk=%d\n",
576 wr ? "WRITE" : "READ", rep->blk);
577 return 0;
578 }
579
580 /* Returns scsi_type or -1 for error */
581 static int
sg_prepare(int fd,int sz)582 sg_prepare(int fd, int sz)
583 {
584 int res, t;
585 struct sg_scsi_id info;
586
587 res = ioctl(fd, SG_GET_VERSION_NUM, &t);
588 if ((res < 0) || (t < 30000)) {
589 fprintf(stderr, "sgq_dd: sg driver prior to 3.x.y\n");
590 return -1;
591 }
592 res = ioctl(fd, SG_SET_RESERVED_SIZE, &sz);
593 if (res < 0)
594 perror("sgq_dd: SG_SET_RESERVED_SIZE error");
595 #if 0
596 t = 1;
597 res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t);
598 if (res < 0)
599 perror("sgq_dd: SG_SET_FORCE_PACK_ID error");
600 #endif
601 res = ioctl(fd, SG_GET_SCSI_ID, &info);
602 if (res < 0) {
603 perror("sgq_dd: SG_SET_SCSI_ID error");
604 return -1;
605 }
606 else
607 return info.scsi_type;
608 }
609
610 /* Return 0 for ok, anything else for errors */
611 static int
prepare_rq_elems(Rq_coll * clp,const char * inf,const char * outf)612 prepare_rq_elems(Rq_coll * clp, const char * inf, const char * outf)
613 {
614 int k;
615 Rq_elem * rep;
616 size_t psz;
617 char ebuff[EBUFF_SZ];
618 int sz = clp->bpt * clp->bs;
619 int scsi_type;
620
621 clp->req_arr = malloc(sizeof(Rq_elem) * clp->num_rq_elems);
622 if (NULL == clp->req_arr)
623 return 1;
624 for (k = 0; k < clp->num_rq_elems; ++k) {
625 rep = &clp->req_arr[k];
626 memset(rep, 0, sizeof(Rq_elem));
627 psz = getpagesize();
628 if (NULL == (rep->alloc_bp = malloc(sz + psz)))
629 return 1;
630 rep->buffp = (uint8_t *)
631 (((unsigned long)rep->alloc_bp + psz - 1) & (~(psz - 1)));
632 rep->qstate = QS_IDLE;
633 rep->bs = clp->bs;
634 rep->dio = clp->dio;
635 rep->debug = clp->debug;
636 rep->out_scsi_type = clp->out_scsi_type;
637 if (FT_SG == clp->in_type) {
638 if (0 == k)
639 rep->infd = clp->infd;
640 else {
641 if ((rep->infd = open(inf, O_RDWR)) < 0) {
642 snprintf(ebuff, EBUFF_SZ,
643 "sgq_dd: could not open %s for sg reading", inf);
644 perror(ebuff);
645 return 1;
646 }
647 }
648 in_pollfd_arr[k].fd = rep->infd;
649 in_pollfd_arr[k].events = POLLIN;
650 if ((scsi_type = sg_prepare(rep->infd, sz)) < 0)
651 return 1;
652 if (0 == k)
653 clp->in_scsi_type = scsi_type;
654 rep->in_scsi_type = clp->in_scsi_type;
655 }
656 else
657 rep->infd = clp->infd;
658
659 if (FT_SG == clp->out_type) {
660 if (0 == k)
661 rep->outfd = clp->outfd;
662 else {
663 if ((rep->outfd = open(outf, O_RDWR)) < 0) {
664 snprintf(ebuff, EBUFF_SZ,
665 "sgq_dd: could not open %s for sg writing", outf);
666 perror(ebuff);
667 return 1;
668 }
669 }
670 out_pollfd_arr[k].fd = rep->outfd;
671 out_pollfd_arr[k].events = POLLIN;
672 if ((scsi_type = sg_prepare(rep->outfd, sz)) < 0)
673 return 1;
674 if (0 == k)
675 clp->out_scsi_type = scsi_type;
676 rep->out_scsi_type = clp->out_scsi_type;
677 }
678 else
679 rep->outfd = clp->outfd;
680 }
681 return 0;
682 }
683
684 /* Returns a "QS" code and req index, or QS_IDLE and position of first idle
685 (-1 if no idle position). Returns -1 on poll error. */
686 static int
decider(Rq_coll * clp,int first_xfer,int * req_indexp)687 decider(Rq_coll * clp, int first_xfer, int * req_indexp)
688 {
689 int k, res;
690 Rq_elem * rep;
691 int first_idle_index = -1;
692 int lowest_blk_index = -1;
693 int times;
694 int try_poll = 0;
695 int lowest_blk = INT_MAX;
696
697 times = first_xfer ? 1 : clp->num_rq_elems;
698 for (k = 0; k < times; ++k) {
699 rep = &clp->req_arr[k];
700 if ((QS_IN_STARTED == rep->qstate) ||
701 (QS_OUT_STARTED == rep->qstate))
702 try_poll = 1;
703 else if ((QS_IN_FINISHED == rep->qstate) && (rep->blk < lowest_blk)) {
704 lowest_blk = rep->blk;
705 lowest_blk_index = k;
706 }
707 else if ((QS_IDLE == rep->qstate) && (first_idle_index < 0))
708 first_idle_index = k;
709 }
710 if (try_poll) {
711 res = do_poll(clp, 0, req_indexp);
712 if (0 != res)
713 return res;
714 }
715
716 if (lowest_blk_index >= 0) {
717 if (req_indexp)
718 *req_indexp = lowest_blk_index;
719 return QS_IN_FINISHED;
720 }
721 #if 0
722 if (try_poll) {
723 res = do_poll(clp, 2, req_indexp);
724 if (0 != res)
725 return res;
726 }
727 #endif
728 if (req_indexp)
729 *req_indexp = first_idle_index;
730 return QS_IDLE;
731 }
732
733
734 int
main(int argc,char * argv[])735 main(int argc, char * argv[])
736 {
737 bool verbose_given = false;
738 bool version_given = false;
739 int skip = 0;
740 int seek = 0;
741 int ibs = 0;
742 int obs = 0;
743 char str[STR_SZ];
744 char * key;
745 char * buf;
746 char inf[INOUTF_SZ];
747 char outf[INOUTF_SZ];
748 int res, k, n, keylen;
749 int in_num_sect = 0;
750 int out_num_sect = 0;
751 int num_threads = DEF_NUM_THREADS;
752 int gen = 0;
753 int do_time = 0;
754 int in_sect_sz, out_sect_sz, first_xfer, qstate, req_index, seek_skip;
755 int blocks, stop_after_write, terminate;
756 char ebuff[EBUFF_SZ];
757 Rq_elem * rep;
758 struct timeval start_tm, end_tm;
759
760 memset(&rcoll, 0, sizeof(Rq_coll));
761 rcoll.bpt = DEF_BLOCKS_PER_TRANSFER;
762 rcoll.in_type = FT_OTHER;
763 rcoll.out_type = FT_OTHER;
764 inf[0] = '\0';
765 outf[0] = '\0';
766
767 for(k = 1; k < argc; k++) {
768 if (argv[k])
769 strncpy(str, argv[k], STR_SZ);
770 else
771 continue;
772 for(key = str, buf = key; *buf && *buf != '=';)
773 buf++;
774 if (*buf)
775 *buf++ = '\0';
776 keylen = strlen(key);
777 if (strcmp(key,"if") == 0)
778 strncpy(inf, buf, INOUTF_SZ);
779 else if (strcmp(key,"of") == 0)
780 strncpy(outf, buf, INOUTF_SZ);
781 else if (0 == strcmp(key,"ibs"))
782 ibs = sg_get_num(buf);
783 else if (0 == strcmp(key,"obs"))
784 obs = sg_get_num(buf);
785 else if (0 == strcmp(key,"bs"))
786 rcoll.bs = sg_get_num(buf);
787 else if (0 == strcmp(key,"bpt"))
788 rcoll.bpt = sg_get_num(buf);
789 else if (0 == strcmp(key,"skip"))
790 skip = sg_get_num(buf);
791 else if (0 == strcmp(key,"seek"))
792 seek = sg_get_num(buf);
793 else if (0 == strcmp(key,"count"))
794 dd_count = sg_get_num(buf);
795 else if (0 == strcmp(key,"dio"))
796 rcoll.dio = sg_get_num(buf);
797 else if (0 == strcmp(key,"thr"))
798 num_threads = sg_get_num(buf);
799 else if (0 == strcmp(key,"coe"))
800 rcoll.coe = sg_get_num(buf);
801 else if (0 == strcmp(key,"gen"))
802 gen = sg_get_num(buf);
803 else if ((0 == strncmp(key,"deb", 3)) || (0 == strncmp(key,"verb", 4)))
804 rcoll.debug = sg_get_num(buf);
805 else if (0 == strcmp(key,"time"))
806 do_time = sg_get_num(buf);
807 else if ((keylen > 1) && ('-' == key[0]) && ('-' != key[1])) {
808 res = 0;
809 n = num_chs_in_str(key + 1, keylen - 1, 'h');
810 if (n > 0) {
811 usage();
812 return 0;
813 }
814 n = num_chs_in_str(key + 1, keylen - 1, 'v');
815 if (n > 0)
816 verbose_given = true;
817 rcoll.debug += n;
818 res += n;
819 n = num_chs_in_str(key + 1, keylen - 1, 'V');
820 if (n > 0)
821 version_given = true;
822 res += n;
823 if (res < (keylen - 1)) {
824 fprintf(stderr, "Unrecognised short option in '%s', try "
825 "'--help'\n", key);
826 return SG_LIB_SYNTAX_ERROR;
827 }
828 } else if (0 == strncmp(key, "--help", 6)) {
829 usage();
830 return 0;
831 } else if (0 == strncmp(key, "--verb", 6)) {
832 verbose_given = true;
833 ++rcoll.debug;
834 } else if (0 == strncmp(key, "--vers", 6))
835 version_given = true;
836 else {
837 fprintf(stderr, "Unrecognized argument '%s'\n", key);
838 usage();
839 return 1;
840 }
841 }
842 #ifdef DEBUG
843 fprintf(stderr, "In DEBUG mode, ");
844 if (verbose_given && version_given) {
845 fprintf(stderr, "but override: '-vV' given, zero verbose and "
846 "continue\n");
847 verbose_given = false;
848 version_given = false;
849 rcoll.debug = 0;
850 } else if (! verbose_given) {
851 fprintf(stderr, "set '-vv'\n");
852 rcoll.debug = 2;
853 } else
854 fprintf(stderr, "keep verbose=%d\n", rcoll.debug);
855 #else
856 if (verbose_given && version_given)
857 fprintf(stderr, "Not in DEBUG mode, so '-vV' has no special action\n");
858 #endif
859 if (version_given) {
860 fprintf(stderr, "sgq_dd for sg version 3 driver: %s\n",
861 version_str);
862 return 0;
863 return 0;
864 }
865
866 if (argc < 2) {
867 usage();
868 return 1;
869 }
870 if (rcoll.bs <= 0) {
871 rcoll.bs = DEF_BLOCK_SIZE;
872 fprintf(stderr, "Assume default 'bs' (block size) of %d bytes\n",
873 rcoll.bs);
874 }
875 if ((ibs && (ibs != rcoll.bs)) || (obs && (obs != rcoll.bs))) {
876 fprintf(stderr, "If 'ibs' or 'obs' given must be same as 'bs'\n");
877 usage();
878 return 1;
879 }
880 if ((skip < 0) || (seek < 0)) {
881 fprintf(stderr, "skip and seek cannot be negative\n");
882 return 1;
883 }
884 if ((num_threads < 1) || (num_threads > MAX_NUM_THREADS)) {
885 fprintf(stderr, "too few or too many threads requested\n");
886 usage();
887 return 1;
888 }
889 if (rcoll.debug)
890 fprintf(stderr, "sgq_dd: if=%s skip=%d of=%s seek=%d count=%d\n",
891 inf, skip, outf, seek, dd_count);
892 install_handler (SIGINT, interrupt_handler);
893 install_handler (SIGQUIT, interrupt_handler);
894 install_handler (SIGPIPE, interrupt_handler);
895 install_handler (SIGUSR1, siginfo_handler);
896
897 rcoll.infd = STDIN_FILENO;
898 rcoll.outfd = STDOUT_FILENO;
899 if (inf[0] && ('-' != inf[0])) {
900 rcoll.in_type = dd_filetype(inf);
901
902 if (FT_SG == rcoll.in_type) {
903 if ((rcoll.infd = open(inf, O_RDWR)) < 0) {
904 snprintf(ebuff, EBUFF_SZ,
905 "sgq_dd: could not open %s for sg reading", inf);
906 perror(ebuff);
907 return 1;
908 }
909 }
910 if (FT_SG != rcoll.in_type) {
911 if ((rcoll.infd = open(inf, O_RDONLY)) < 0) {
912 snprintf(ebuff, EBUFF_SZ,
913 "sgq_dd: could not open %s for reading", inf);
914 perror(ebuff);
915 return 1;
916 }
917 else if (skip > 0) {
918 loff_t offset = skip;
919
920 offset *= rcoll.bs; /* could exceed 32 here! */
921 if (lseek(rcoll.infd, offset, SEEK_SET) < 0) {
922 snprintf(ebuff, EBUFF_SZ,
923 "sgq_dd: couldn't skip to required position on %s", inf);
924 perror(ebuff);
925 return 1;
926 }
927 }
928 }
929 }
930 if (outf[0] && ('-' != outf[0])) {
931 rcoll.out_type = dd_filetype(outf);
932
933 if (FT_SG == rcoll.out_type) {
934 if ((rcoll.outfd = open(outf, O_RDWR)) < 0) {
935 snprintf(ebuff, EBUFF_SZ,
936 "sgq_dd: could not open %s for sg writing", outf);
937 perror(ebuff);
938 return 1;
939 }
940 }
941 else {
942 if (FT_OTHER == rcoll.out_type) {
943 if ((rcoll.outfd = open(outf, O_WRONLY | O_CREAT, 0666)) < 0) {
944 snprintf(ebuff, EBUFF_SZ,
945 "sgq_dd: could not open %s for writing", outf);
946 perror(ebuff);
947 return 1;
948 }
949 }
950 else {
951 if ((rcoll.outfd = open(outf, O_WRONLY)) < 0) {
952 snprintf(ebuff, EBUFF_SZ,
953 "sgq_dd: could not open %s for raw writing", outf);
954 perror(ebuff);
955 return 1;
956 }
957 }
958 if (seek > 0) {
959 loff_t offset = seek;
960
961 offset *= rcoll.bs; /* could exceed 32 bits here! */
962 if (lseek(rcoll.outfd, offset, SEEK_SET) < 0) {
963 snprintf(ebuff, EBUFF_SZ,
964 "sgq_dd: couldn't seek to required position on %s", outf);
965 perror(ebuff);
966 return 1;
967 }
968 }
969 }
970 }
971 if ((STDIN_FILENO == rcoll.infd) && (STDOUT_FILENO == rcoll.outfd)) {
972 fprintf(stderr, "Disallow both if and of to be stdin and stdout\n");
973 return 1;
974 }
975 if ((FT_OTHER == rcoll.in_type) && (FT_OTHER == rcoll.out_type) && !gen) {
976 fprintf(stderr, "Either 'if' or 'of' must be a sg or raw device\n");
977 return 1;
978 }
979 if (0 == dd_count)
980 return 0;
981 else if (dd_count < 0) {
982 if (FT_SG == rcoll.in_type) {
983 res = read_capacity(rcoll.infd, &in_num_sect, &in_sect_sz);
984 if (2 == res) {
985 fprintf(stderr, "Unit attention, media changed(in), repeat\n");
986 res = read_capacity(rcoll.infd, &in_num_sect, &in_sect_sz);
987 }
988 if (0 != res) {
989 fprintf(stderr, "Unable to read capacity on %s\n", inf);
990 in_num_sect = -1;
991 }
992 else {
993 if (in_num_sect > skip)
994 in_num_sect -= skip;
995 }
996 }
997 if (FT_SG == rcoll.out_type) {
998 res = read_capacity(rcoll.outfd, &out_num_sect, &out_sect_sz);
999 if (2 == res) {
1000 fprintf(stderr, "Unit attention, media changed(out), "
1001 "repeat\n");
1002 res = read_capacity(rcoll.outfd, &out_num_sect, &out_sect_sz);
1003 }
1004 if (0 != res) {
1005 fprintf(stderr, "Unable to read capacity on %s\n", outf);
1006 out_num_sect = -1;
1007 }
1008 else {
1009 if (out_num_sect > seek)
1010 out_num_sect -= seek;
1011 }
1012 }
1013 if (in_num_sect > 0) {
1014 if (out_num_sect > 0)
1015 dd_count = (in_num_sect > out_num_sect) ? out_num_sect :
1016 in_num_sect;
1017 else
1018 dd_count = in_num_sect;
1019 }
1020 else
1021 dd_count = out_num_sect;
1022 }
1023 if (rcoll.debug > 1)
1024 fprintf(stderr, "Start of loop, count=%d, in_num_sect=%d, "
1025 "out_num_sect=%d\n", dd_count, in_num_sect, out_num_sect);
1026 if (dd_count <= 0) {
1027 fprintf(stderr, "Couldn't calculate count, please give one\n");
1028 return 1;
1029 }
1030
1031 rcoll.in_count = dd_count;
1032 rcoll.in_done_count = dd_count;
1033 rcoll.skip = skip;
1034 rcoll.in_blk = skip;
1035 rcoll.out_count = dd_count;
1036 rcoll.out_done_count = dd_count;
1037 rcoll.seek = seek;
1038 rcoll.out_blk = seek;
1039
1040 if ((FT_SG == rcoll.in_type) || (FT_SG == rcoll.out_type))
1041 rcoll.num_rq_elems = num_threads;
1042 else
1043 rcoll.num_rq_elems = 1;
1044 if (prepare_rq_elems(&rcoll, inf, outf)) {
1045 fprintf(stderr, "Setup failure, perhaps no memory\n");
1046 return 1;
1047 }
1048
1049 first_xfer = 1;
1050 stop_after_write = 0;
1051 terminate = 0;
1052 seek_skip = rcoll.seek - rcoll.skip;
1053 if (do_time) {
1054 start_tm.tv_sec = 0;
1055 start_tm.tv_usec = 0;
1056 gettimeofday(&start_tm, NULL);
1057 }
1058 while (rcoll.out_done_count > 0) { /* >>>>>>>>> main loop */
1059 req_index = -1;
1060 qstate = decider(&rcoll, first_xfer, &req_index);
1061 rep = (req_index < 0) ? NULL : (rcoll.req_arr + req_index);
1062 switch (qstate) {
1063 case QS_IDLE:
1064 if ((NULL == rep) || (rcoll.in_count <= 0)) {
1065 /* usleep(1000); */
1066 /* do_poll(&rcoll, 10, NULL); */
1067 /* do_poll(&rcoll, 0, NULL); */
1068 break;
1069 }
1070 if (rcoll.debug > 8)
1071 fprintf(stderr, " sgq_dd: non-sleeping QS_IDLE state, "
1072 "req_index=%d\n", req_index);
1073 if (first_xfer >= 2)
1074 first_xfer = 0;
1075 else if (1 == first_xfer)
1076 ++first_xfer;
1077 if (stop_after_write) {
1078 terminate = 1;
1079 break;
1080 }
1081 blocks = (rcoll.in_count > rcoll.bpt) ? rcoll.bpt : rcoll.in_count;
1082 rep->wr = 0;
1083 rep->blk = rcoll.in_blk;
1084 rep->num_blks = blocks;
1085 rcoll.in_blk += blocks;
1086 rcoll.in_count -= blocks;
1087
1088 if (FT_SG == rcoll.in_type) {
1089 res = sg_start_io(rep);
1090 if (0 != res) {
1091 if (1 == res)
1092 fprintf(stderr, "Out of memory starting sg io\n");
1093 terminate = 1;
1094 }
1095 }
1096 else {
1097 res = normal_in_operation(&rcoll, rep, blocks);
1098 if (res < 0)
1099 terminate = 1;
1100 else if (res > 0)
1101 stop_after_write = 1;
1102 }
1103 break;
1104 case QS_IN_FINISHED:
1105 if (rcoll.debug > 8)
1106 fprintf(stderr, " sgq_dd: state is QS_IN_FINISHED, "
1107 "req_index=%d\n", req_index);
1108 if ((rep->blk + seek_skip) != rcoll.out_blk) {
1109 /* if write would be out of sequence then wait */
1110 if (rcoll.debug > 4)
1111 fprintf(stderr, " sgq_dd: QS_IN_FINISHED, "
1112 "out of sequence\n");
1113 usleep(200);
1114 break;
1115 }
1116 rep->wr = 1;
1117 rep->blk = rcoll.out_blk;
1118 blocks = rep->num_blks;
1119 rcoll.out_blk += blocks;
1120 rcoll.out_count -= blocks;
1121
1122 if (FT_SG == rcoll.out_type) {
1123 res = sg_start_io(rep);
1124 if (0 != res) {
1125 if (1 == res)
1126 fprintf(stderr, "Out of memory starting sg io\n");
1127 terminate = 1;
1128 }
1129 }
1130 else {
1131 if (normal_out_operation(&rcoll, rep, blocks) < 0)
1132 terminate = 1;
1133 }
1134 break;
1135 case QS_IN_POLL:
1136 if (rcoll.debug > 8)
1137 fprintf(stderr, " sgq_dd: state is QS_IN_POLL, "
1138 "req_index=%d\n", req_index);
1139 res = sg_fin_in_operation(&rcoll, rep);
1140 if (res < 0)
1141 terminate = 1;
1142 else if (res > 1) {
1143 if (first_xfer) {
1144 /* only retry on first xfer */
1145 if (0 != sg_start_io(rep))
1146 terminate = 1;
1147 }
1148 else
1149 terminate = 1;
1150 }
1151 break;
1152 case QS_OUT_POLL:
1153 if (rcoll.debug > 8)
1154 fprintf(stderr, " sgq_dd: state is QS_OUT_POLL, "
1155 "req_index=%d\n", req_index);
1156 res = sg_fin_out_operation(&rcoll, rep);
1157 if (res < 0)
1158 terminate = 1;
1159 else if (res > 1) {
1160 if (first_xfer) {
1161 /* only retry on first xfer */
1162 if (0 != sg_start_io(rep))
1163 terminate = 1;
1164 }
1165 else
1166 terminate = 1;
1167 }
1168 break;
1169 default:
1170 if (rcoll.debug > 8)
1171 fprintf(stderr, " sgq_dd: state is ?????\n");
1172 terminate = 1;
1173 break;
1174 }
1175 if (terminate)
1176 break;
1177 } /* >>>>>>>>>>>>> end of main loop */
1178
1179 if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
1180 struct timeval res_tm;
1181 double a, b;
1182
1183 gettimeofday(&end_tm, NULL);
1184 res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
1185 res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
1186 if (res_tm.tv_usec < 0) {
1187 --res_tm.tv_sec;
1188 res_tm.tv_usec += 1000000;
1189 }
1190 a = res_tm.tv_sec;
1191 a += (0.000001 * res_tm.tv_usec);
1192 b = (double)rcoll.bs * (dd_count - rcoll.out_done_count);
1193 printf("time to transfer data was %d.%06d secs",
1194 (int)res_tm.tv_sec, (int)res_tm.tv_usec);
1195 if ((a > 0.00001) && (b > 511))
1196 printf(", %.2f MB/sec\n", b / (a * 1000000.0));
1197 else
1198 printf("\n");
1199 }
1200
1201 if (STDIN_FILENO != rcoll.infd)
1202 close(rcoll.infd);
1203 if (STDOUT_FILENO != rcoll.outfd)
1204 close(rcoll.outfd);
1205 res = 0;
1206 if (0 != rcoll.out_count) {
1207 fprintf(stderr, ">>>> Some error occurred,\n");
1208 res = 2;
1209 }
1210 print_stats();
1211 if (rcoll.dio_incomplete) {
1212 int fd;
1213 char c;
1214
1215 fprintf(stderr, ">> Direct IO requested but incomplete %d times\n",
1216 rcoll.dio_incomplete);
1217 if ((fd = open(sg_allow_dio, O_RDONLY)) >= 0) {
1218 if (1 == read(fd, &c, 1)) {
1219 if ('0' == c)
1220 fprintf(stderr, ">>> %s set to '0' but should be set "
1221 "to '1' for direct IO\n", sg_allow_dio);
1222 }
1223 close(fd);
1224 }
1225 }
1226 if (rcoll.sum_of_resids)
1227 fprintf(stderr, ">> Non-zero sum of residual counts=%d\n",
1228 rcoll.sum_of_resids);
1229 return res;
1230 }
1231