aboutsummaryrefslogtreecommitdiff
path: root/archive
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2007-06-27 02:36:11 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2007-06-27 02:36:11 +0000
commitded737d99ef066a8aff068d73365aae2fb5bbba4 (patch)
tree53c24bd1d06f64b54de04ee2173be55cdd3e6383 /archive
parent8a61a4beb73befb59945e19f70937877c86f5dc8 (diff)
downloadsg3_utils-ded737d99ef066a8aff068d73365aae2fb5bbba4.tar.gz
Load sg3_utils-0.92 into trunk/.
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@8 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'archive')
-rw-r--r--archive/Makefile62
-rw-r--r--archive/README15
-rw-r--r--archive/llseek.h10
-rw-r--r--archive/sg_dd2048.c1
-rw-r--r--archive/sg_dd512.c1
-rw-r--r--archive/sg_err.h140
-rw-r--r--archive/sg_poll.c351
-rw-r--r--archive/sgq_dd.c1291
-rw-r--r--archive/sgq_old_dd.c865
9 files changed, 2217 insertions, 519 deletions
diff --git a/archive/Makefile b/archive/Makefile
new file mode 100644
index 00000000..b1232a90
--- /dev/null
+++ b/archive/Makefile
@@ -0,0 +1,62 @@
+SHELL = /bin/sh
+
+INSTDIR=/usr/local/bin
+MANDIR=/usr/local/man
+
+CC = gcc
+LD = gcc
+
+EXECS = sgq_dd sg_poll sg_reset
+
+COMMON =
+
+MAN_PGS =
+MAN_PREF = man8
+
+CFLAGS = -g -O2 -Wall -D_REENTRANT
+# CFLAGS = -g -O2 -Wall -pedantic -D_REENTRANT
+
+LDFLAGS =
+
+all: $(EXECS)
+
+common: $(COMMON)
+
+depend dep:
+ for i in *.c; do $(CC) $(INCLUDES) $(CFLAGS) -M $$i; \
+ done > .depend
+
+clean:
+ /bin/rm -f *.o $(EXECS) core .depend
+
+sg_poll: sg_poll.o ../sg_err.o
+ $(LD) -o $@ $(LDFLAGS) $^
+
+sgq_dd: sgq_dd.o ../sg_err.o ../llseek.o
+ $(LD) -o $@ $(LDFLAGS) $^
+
+sg_reset: sg_reset.o
+ $(LD) -o $@ $(LDFLAGS) $^
+
+install: $(EXECS) $(COMMON)
+ install -d $(INSTDIR)
+ for name in $^; \
+ do install -s -o root -g root -m 755 $$name $(INSTDIR); \
+ done
+ for mp in $(MAN_PGS); \
+ do install -o root -g root -m 644 $$mp $(MANDIR)/$(MAN_PREF); \
+ gzip -9f $(MANDIR)/$(MAN_PREF)/$$mp; \
+ done
+
+uninstall:
+ dists="$(EXECS)"; \
+ for name in $$dists; do \
+ rm -f $(INSTDIR)/$$name; \
+ done
+ for mp in $(MAN_PGS); do \
+ rm -f $(MANDIR)/$(MAN_PREF)/$$mp.gz; \
+ done
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/archive/README b/archive/README
index a940f92c..7b1d8283 100644
--- a/archive/README
+++ b/archive/README
@@ -3,12 +3,23 @@ subsystem and typically use the SCSI generic interface (sg) as
well.
The programs in this directory are fully functional but have been
-"retired" from the main sg_utils directory most likely because
+"retired" from the main sg3_utils directory most likely because
their functionality is done in another (perhaps better) way.
The last time I checked these programs compiled and built and
didn't depend on too many other things (see the Makefile in the
parent directory for ideas).
+Since I actively use some of these programs for testing, a Makefile has
+been set up which builds:
+ - sgq_dd [a dd variant the uses a dispatch loop rather than
+ POSIX threads (as does sgp_dd)]
+ - sg_reset [does SCSI bus/device/host resets if mid level SCSI patch
+ in place]
+ - sg_poll [internal testing]
+
+The sg_err.h and llseek.h headers have simply been copied from the parent
+directory.
+
Doug Gilbert
-8th December 2000
+13th January 2001
diff --git a/archive/llseek.h b/archive/llseek.h
new file mode 100644
index 00000000..cdf6fcea
--- /dev/null
+++ b/archive/llseek.h
@@ -0,0 +1,10 @@
+
+#if defined(__GNUC__) || defined(HAS_LONG_LONG)
+typedef long long llse_loff_t;
+#else
+typedef long llse_loff_t;
+#endif
+
+extern llse_loff_t llse_llseek(unsigned int fd,
+ llse_loff_t offset,
+ unsigned int origin);
diff --git a/archive/sg_dd2048.c b/archive/sg_dd2048.c
index ecbe2922..3988f59e 100644
--- a/archive/sg_dd2048.c
+++ b/archive/sg_dd2048.c
@@ -9,6 +9,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/../scsi/sg.h> /* cope with silly includes */
+#include <linux/../scsi/scsi.h>
#include "sg_err.h"
/* Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
diff --git a/archive/sg_dd512.c b/archive/sg_dd512.c
index 70636761..b8506256 100644
--- a/archive/sg_dd512.c
+++ b/archive/sg_dd512.c
@@ -9,6 +9,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/../scsi/sg.h> /* cope with silly includes */
+#include <linux/../scsi/scsi.h>
#include "sg_err.h"
/* Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
diff --git a/archive/sg_err.h b/archive/sg_err.h
new file mode 100644
index 00000000..fd59e3b8
--- /dev/null
+++ b/archive/sg_err.h
@@ -0,0 +1,140 @@
+#ifndef SG_ERR_H
+#define SG_ERR_H
+
+/* Feel free to copy and modify this GPL-ed code into your applications. */
+
+/* Version 0.84 (20010115)
+ - all output now sent to stderr rather thatn stdout
+ - remove header files included in this file
+*/
+
+
+/* Some of the following error/status codes are exchanged between the
+ various layers of the SCSI sub-system in Linux and should never
+ reach the user. They are placed here for completeness. What appears
+ here is copied from drivers/scsi/scsi.h which is not visible in
+ the user space. */
+
+/* The following are 'host_status' codes */
+#ifndef DID_OK
+#define DID_OK 0x00
+#endif
+#ifndef DID_NO_CONNECT
+#define DID_NO_CONNECT 0x01 /* Unable to connect before timeout */
+#define DID_BUS_BUSY 0x02 /* Bus remain busy until timeout */
+#define DID_TIME_OUT 0x03 /* Timed out for some other reason */
+#define DID_BAD_TARGET 0x04 /* Bad target (id?) */
+#define DID_ABORT 0x05 /* Told to abort for some other reason */
+#define DID_PARITY 0x06 /* Parity error (on SCSI bus) */
+#define DID_ERROR 0x07 /* Internal error */
+#define DID_RESET 0x08 /* Reset by somebody */
+#define DID_BAD_INTR 0x09 /* Received an unexpected interrupt */
+#define DID_PASSTHROUGH 0x0a /* Force command past mid-level */
+#define DID_SOFT_ERROR 0x0b /* The low-level driver wants a retry */
+#endif
+
+/* These defines are to isolate applictaions from kernel define changes */
+#define SG_ERR_DID_OK DID_OK
+#define SG_ERR_DID_NO_CONNECT DID_NO_CONNECT
+#define SG_ERR_DID_BUS_BUSY DID_BUS_BUSY
+#define SG_ERR_DID_TIME_OUT DID_TIME_OUT
+#define SG_ERR_DID_BAD_TARGET DID_BAD_TARGET
+#define SG_ERR_DID_ABORT DID_ABORT
+#define SG_ERR_DID_PARITY DID_PARITY
+#define SG_ERR_DID_ERROR DID_ERROR
+#define SG_ERR_DID_RESET DID_RESET
+#define SG_ERR_DID_BAD_INTR DID_BAD_INTR
+#define SG_ERR_DID_PASSTHROUGH DID_PASSTHROUGH
+#define SG_ERR_DID_SOFT_ERROR DID_SOFT_ERROR
+
+/* The following are 'driver_status' codes */
+#ifndef DRIVER_OK
+#define DRIVER_OK 0x00
+#endif
+#ifndef DRIVER_BUSY
+#define DRIVER_BUSY 0x01
+#define DRIVER_SOFT 0x02
+#define DRIVER_MEDIA 0x03
+#define DRIVER_ERROR 0x04
+#define DRIVER_INVALID 0x05
+#define DRIVER_TIMEOUT 0x06
+#define DRIVER_HARD 0x07
+#define DRIVER_SENSE 0x08 /* Sense_buffer has been set */
+
+/* Following "suggests" are "or-ed" with one of previous 8 entries */
+#define SUGGEST_RETRY 0x10
+#define SUGGEST_ABORT 0x20
+#define SUGGEST_REMAP 0x30
+#define SUGGEST_DIE 0x40
+#define SUGGEST_SENSE 0x80
+#define SUGGEST_IS_OK 0xff
+#endif
+#ifndef DRIVER_MASK
+#define DRIVER_MASK 0x0f
+#endif
+#ifndef SUGGEST_MASK
+#define SUGGEST_MASK 0xf0
+#endif
+
+/* These defines are to isolate applictaions from kernel define changes */
+#define SG_ERR_DRIVER_OK DRIVER_OK
+#define SG_ERR_DRIVER_BUSY DRIVER_BUSY
+#define SG_ERR_DRIVER_SOFT DRIVER_SOFT
+#define SG_ERR_DRIVER_MEDIA DRIVER_MEDIA
+#define SG_ERR_DRIVER_ERROR DRIVER_ERROR
+#define SG_ERR_DRIVER_INVALID DRIVER_INVALID
+#define SG_ERR_DRIVER_TIMEOUT DRIVER_TIMEOUT
+#define SG_ERR_DRIVER_HARD DRIVER_HARD
+#define SG_ERR_DRIVER_SENSE DRIVER_SENSE
+#define SG_ERR_SUGGEST_RETRY SUGGEST_RETRY
+#define SG_ERR_SUGGEST_ABORT SUGGEST_ABORT
+#define SG_ERR_SUGGEST_REMAP SUGGEST_REMAP
+#define SG_ERR_SUGGEST_DIE SUGGEST_DIE
+#define SG_ERR_SUGGEST_SENSE SUGGEST_SENSE
+#define SG_ERR_SUGGEST_IS_OK SUGGEST_IS_OK
+#define SG_ERR_DRIVER_MASK DRIVER_MASK
+#define SG_ERR_SUGGEST_MASK SUGGEST_MASK
+
+
+
+/* The following "print" functions send ACSII to stdout */
+extern void sg_print_command(const unsigned char * command);
+extern void sg_print_sense(const char * leadin,
+ const unsigned char * sense_buffer, int sb_len);
+extern void sg_print_status(int masked_status);
+extern void sg_print_host_status(int host_status);
+extern void sg_print_driver_status(int driver_status);
+
+/* sg_chk_n_print() returns 1 quietly if there are no errors/warnings
+ else it prints to standard output and returns 0. */
+extern int sg_chk_n_print(const char * leadin, int masked_status,
+ int host_status, int driver_status,
+ const unsigned char * sense_buffer, int sb_len);
+
+/* The following function declaration is for the sg version 3 driver.
+ Only version 3 sg_err.c defines it. */
+struct sg_io_hdr;
+extern int sg_chk_n_print3(const char * leadin, struct sg_io_hdr * hp);
+
+
+/* The following "category" function returns one of the following */
+#define SG_ERR_CAT_CLEAN 0 /* No errors or other information */
+#define SG_ERR_CAT_MEDIA_CHANGED 1 /* interpreted from sense buffer */
+#define SG_ERR_CAT_RESET 2 /* interpreted from sense buffer */
+#define SG_ERR_CAT_TIMEOUT 3
+#define SG_ERR_CAT_RECOVERED 4 /* Successful command after recovered err */
+#define SG_ERR_CAT_SENSE 98 /* Something else is in the sense buffer */
+#define SG_ERR_CAT_OTHER 99 /* Some other error/warning has occurred */
+
+extern int sg_err_category(int masked_status, int host_status,
+ int driver_status, const unsigned char * sense_buffer,
+ int sb_len);
+
+/* The following function declaration is for the sg version 3 driver.
+ Only version 3 sg_err.c defines it. */
+extern int sg_err_category3(struct sg_io_hdr * hp);
+
+/* Returns length of SCSI command given the opcode (first byte) */
+int sg_get_command_size(unsigned char opcode);
+
+#endif
diff --git a/archive/sg_poll.c b/archive/sg_poll.c
new file mode 100644
index 00000000..ed9c7af2
--- /dev/null
+++ b/archive/sg_poll.c
@@ -0,0 +1,351 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <linux/../scsi/sg.h> /* cope with silly includes */
+#include "sg_err.h"
+
+/* Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
+ device driver.
+* Copyright (C) 1999-2001 D. Gilbert
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2, or (at your option)
+* any later version.
+
+ This program tests out asynchronous parts of the 'sg' device driver.
+ It only uses the SCSI read command on the 'sg' device.
+ This program performs unbalanced, non-polling "write-write-read"
+ sequences. Asynchronous notification is turned on and signals are
+ counted. Due to the imbalance, when the close() is executed there
+ are several packets still to be read() [some of which may not yet
+ be awaiting a read()]. This tests how the device driver cleans up
+ after an unexpected close().
+ If the "-deb" flag is given then outputs state to console/log
+ (for all active sg devices).
+
+ Version 0.76 20010112
+*/
+
+
+/*
+6 byte commands [READ: 0x08, WRITE: 0x0a]:
+[cmd ][had|lu][midAdd][lowAdd][count ][flags ]
+10 byte commands [EREAD: 0x28, EWRITE: 0x2a, READ_CAPACITY 0x25]:
+[cmd ][ |lu][hiAddr][hmAddr][lmAddr][lowAdd][ ][hiCnt ][lowCnt][flags ]
+12 byte commands [LREAD: 0xd8, LWRITE: 0xda]:
+[cmd ][ |lu][hiAddr][hmAddr][lmAddr][lowAdd][hiCnt ][hmCnt ][lmCnt ][lowCnt]
+ ... [ ][flags ]
+*/
+
+#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
+ /* union semun is defined by including <sys/sem.h> */
+#else
+ /* according to X/OPEN we have to define it ourselves */
+union semun {
+ int val; /* value for SETVAL */
+ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
+ unsigned short int *array; /* array for GETALL, SETALL */
+ struct seminfo *__buf; /* buffer for IPC_INFO */
+};
+#endif
+
+#ifdef O_ASYNC
+#define MY_ASYNC O_ASYNC
+#else
+#define MY_ASYNC FASYNC
+#endif
+
+
+// #define SG_DEBUG
+
+#define OFF sizeof(struct sg_header)
+// #define NUM_SECTORS 7777
+// #define NUM_SECTORS 577
+// #define NUM_SECTORS 97
+#define NUM_SECTORS 150
+#define BLOCK_SIZE 2048
+
+volatile int hand_count = 0;
+volatile int signo = 0;
+volatile int poll_res = 0;
+volatile short revents = 0;
+volatile int sg_fd = 0;
+int semset_id = 0;
+
+int do_poll()
+{
+ struct pollfd a_pollfd = {0, POLLIN | POLLOUT, 0};
+
+ a_pollfd.fd = sg_fd;
+ if ((poll_res = poll(&a_pollfd, 1, 0)) < 0) {
+ perror("poll error");
+ return 0;
+ }
+ revents = a_pollfd.revents;
+ return (a_pollfd.revents & POLLIN) ? 1 : 0;
+}
+
+void sg_sa_handler(int sig, siginfo_t *si, void * data)
+{
+ signo = sig;
+ if (SIGRTMIN != sig)
+ fprintf(stderr, "Unexpected signal, signum=%d\n", sig);
+ if (sg_fd != si->si_fd)
+ fprintf(stderr, "Unexpected fd, fd=%d\n", si->si_fd);
+ ++hand_count;
+ if (do_poll()) {
+ struct sembuf a_sembuf;
+
+ a_sembuf.sem_num = 0;
+ a_sembuf.sem_op = 1;
+ a_sembuf.sem_flg = 0;
+ if (semop(semset_id, &a_sembuf, 1) < 0)
+ perror("semop(sh) error");
+ }
+}
+
+int main(int argc, char * argv[])
+{
+ int flags;
+ int res;
+ int k;
+ unsigned char rdCmdBlk [10] = {0x28, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ unsigned char * rdBuff = malloc(OFF + sizeof(rdCmdBlk) +
+ (BLOCK_SIZE * NUM_SECTORS));
+ unsigned char * rdBuff2 = malloc(OFF + sizeof(rdCmdBlk) +
+ (BLOCK_SIZE * NUM_SECTORS));
+ int rdInLen = OFF + sizeof(rdCmdBlk);
+ int rdOutLen;
+ unsigned char * rdCmd = rdBuff + OFF;
+ unsigned char * rdCmd2 = rdBuff + OFF;
+ struct sg_header * rsghp = (struct sg_header *)rdBuff;
+ struct sg_header * rsghp2 = (struct sg_header *)rdBuff2;
+ int sectorNo = 10000;
+ int sectorNo2;
+ int numSectors = NUM_SECTORS;
+ const int times = 3;
+ struct sigaction s_action;
+ union semun a_semun;
+ struct sembuf a_sembuf;
+ struct sg_scsi_id sg_id;
+ char ebuff[256];
+ int deb = 0;
+ char * file_name = 0;
+
+ for (k = 1; k < argc; ++k) {
+ if (0 == memcmp("-deb", argv[k], 4))
+ deb = 10;
+ else if (*argv[k] != '-')
+ file_name = argv[k];
+ }
+ if (0 == file_name) {
+printf("Usage: 'sg_poll [-deb] <generic_device>' eg: sg_poll /dev/sg0\n");
+ return 1;
+ }
+
+ semset_id = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
+ if (-1 == semset_id) {
+ perror("semget error");
+ return 1;
+ }
+ a_semun.val = 0;
+ res = semctl(semset_id, 0, SETVAL, a_semun);
+ if (-1 == res) {
+ perror("semctl(val) error");
+ return 1;
+ }
+
+
+ sg_fd = open(file_name, O_RDWR | O_NONBLOCK);
+ if (sg_fd < 0) {
+ sprintf(ebuff, "sg_poll: open error on %s", file_name);
+ perror(ebuff);
+ return 1;
+ }
+ res = ioctl(sg_fd, SG_GET_SCSI_ID, &sg_id);
+ if (res < 0) {
+ /* perror("ioctl on generic device, error"); */
+ printf("sg_poll: %s not a scsi generic device\n", file_name);
+ return 1;
+ }
+ printf("scsi%d, channel=%d, device=%d, lun=%d, scsi_type=%d\n",
+ sg_id.host_no, sg_id.channel, sg_id.scsi_id, sg_id.lun,
+ sg_id.scsi_type);
+
+#ifdef SG_DEBUG
+ ioctl(sg_fd, SG_SET_DEBUG, &deb);
+#endif
+ res = ioctl(sg_fd, SG_GET_COMMAND_Q, &k);
+ if (res < 0) {
+ perror("SG_GET_COMMAND_Q ioctl error");
+ return 1;
+ }
+ if (0 == k) {
+ k = 1;
+ res = ioctl(sg_fd, SG_SET_COMMAND_Q, &k);
+ if (res < 0) {
+ perror("SG_SET_COMMAND_Q ioctl error");
+ return 1;
+ }
+ }
+
+ s_action.sa_flags = SA_SIGINFO;
+ s_action.sa_sigaction = sg_sa_handler;
+ sigemptyset(&s_action.sa_mask);
+ res = sigaction(SIGRTMIN, &s_action, NULL);
+ if (res == -1) {
+ perror("sg_poll: sigaction error");
+ return 1;
+ }
+ res = fcntl(sg_fd, F_SETOWN, getpid());
+ if (res == -1) {
+ perror("sg_poll: fcntl(setown) error");
+ return 1;
+ }
+ flags = fcntl(sg_fd, F_GETFL);
+ res = fcntl(sg_fd, F_SETFL, flags | MY_ASYNC);
+ if (res == -1) {
+ perror("sg_poll: fcntl(setfl) error");
+ return 1;
+ }
+ fcntl(sg_fd, F_SETSIG, SIGRTMIN);
+
+ do_poll();
+ printf("pre-loop check, poll_res=%d, revents=%d\n", poll_res, (int)revents);
+
+
+ for (k = 0; k < times; ++k, sectorNo += numSectors) {
+
+ rdOutLen = OFF + (BLOCK_SIZE * numSectors);
+ rsghp->pack_len = 999; /* don't care */
+ rsghp->pack_id = k;
+ rsghp->reply_len = rdOutLen;
+ rsghp->twelve_byte = 0;
+ rsghp->result = 0;
+ memcpy(rdBuff + OFF, rdCmdBlk, sizeof(rdCmdBlk));
+ rdCmd[3] = (unsigned char)((sectorNo >> 16) & 0xFF);
+ rdCmd[4] = (unsigned char)((sectorNo >> 8) & 0xFF);
+ rdCmd[5] = (unsigned char)(sectorNo & 0xFF);
+ rdCmd[7] = (unsigned char)((numSectors >> 8) & 0xff);
+ rdCmd[8] = (unsigned char)(numSectors & 0xff);
+
+ res = write(sg_fd, rdBuff, rdInLen);
+ if (res < 0) {
+ perror("sg_poll: write (rd) error");
+ return 1;
+ }
+ if (res < rdInLen) {
+ printf("sg_poll: wrote less (rd), ask=%d, got=%d", rdInLen, res);
+ return 1;
+ }
+
+ rsghp2->pack_len = 888; /* don't care */
+ rsghp2->pack_id = k + 100;
+ rsghp2->reply_len = rdOutLen;
+ rsghp2->twelve_byte = 0;
+ rsghp2->result = 0;
+ memcpy(rdBuff2 + OFF, rdCmdBlk, sizeof(rdCmdBlk));
+ sectorNo2 = sectorNo + 6666;
+ rdCmd2[3] = (unsigned char)((sectorNo2 >> 16) & 0xFF);
+ rdCmd2[4] = (unsigned char)((sectorNo2 >> 8) & 0xFF);
+ rdCmd2[5] = (unsigned char)(sectorNo2 & 0xFF);
+ rdCmd2[7] = (unsigned char)((numSectors >> 8) & 0xff);
+ rdCmd2[8] = (unsigned char)(numSectors & 0xff);
+
+#if 1
+ res = write(sg_fd, rdBuff2, rdInLen);
+ if (res < 0) {
+ perror("sg_poll: write2 (rd) error");
+ return 1;
+ }
+ if (res < rdInLen) {
+ printf("sg_poll: wrote less (rd), ask=%d, got=%d", rdInLen, res);
+ return 1;
+ }
+#endif
+
+ do_poll();
+ printf("pre-write pause, k=%d, "
+ "hand_count=%d, signo=%d, poll_res=%d, revents=%d\n",
+ k, hand_count, signo, poll_res, (int)revents);
+#ifdef SG_DEBUG
+ ioctl(sg_fd, SG_SET_DEBUG, &deb);
+#endif
+ system("cat /proc/scsi/sg/debug");
+
+ a_sembuf.sem_num = 0;
+ a_sembuf.sem_op = -1;
+ a_sembuf.sem_flg = 0;
+ while (semop(semset_id, &a_sembuf, 1) < 0) {
+ if (EINTR != errno) {
+ perror("semop(main) error");
+ return 0;
+ }
+ }
+ /* pause(); */
+
+ printf("post-write pause, k=%d, "
+ "hand_count=%d, signo=%d, poll_res=%d, revents=%d\n",
+ k, hand_count, signo, poll_res, (int)revents);
+#ifdef SG_DEBUG
+ ioctl(sg_fd, SG_SET_DEBUG, &deb);
+#endif
+
+ res = read(sg_fd, rdBuff, rdOutLen);
+ if (res < 0) {
+ perror("sg_poll: read (rd) error");
+ return 1;
+ }
+ if (res < rdOutLen) {
+ printf("sg_poll: read less (rd), ask=%d, got=%d", rdOutLen, res);
+ return 1;
+ }
+ sg_chk_n_print("after read(rd)", rsghp->target_status,
+ rsghp->host_status, rsghp->driver_status,
+ rsghp->sense_buffer, SG_MAX_SENSE);
+
+ }
+ printf("\treq_len=%d, dma_count=%d\n", rsghp->reply_len, rsghp->pack_len);
+
+#ifdef SG_DEBUG
+ ioctl(sg_fd, SG_SET_DEBUG, &deb);
+#endif
+ res = close(sg_fd);
+ if (res < 0) {
+ perror("sg_poll: close error");
+ return 1;
+ }
+
+ if (deb > 0) {
+ sg_fd = open(file_name, O_RDONLY);
+ if (sg_fd < 0) {
+ sprintf(ebuff, "sg_poll: open (2) error on %s", file_name);
+ perror(ebuff);
+ return 1;
+ }
+ res = ioctl(sg_fd, SG_SET_DEBUG, &deb);
+ if (res < 0) {
+ perror("ioctl (2) error");
+ return 1;
+ }
+ res = close(sg_fd);
+ if (res < 0) {
+ perror("sg_poll: close (2) error");
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/archive/sgq_dd.c b/archive/sgq_dd.c
index 2af81058..905e7eaf 100644
--- a/archive/sgq_dd.c
+++ b/archive/sgq_dd.c
@@ -1,3 +1,5 @@
+#define _XOPEN_SOURCE 500
+
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
@@ -5,28 +7,35 @@
#include <string.h>
#include <ctype.h>
#include <errno.h>
-#include <poll.h>
+#include <limits.h>
+#include <signal.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/poll.h>
#include <linux/../scsi/sg.h> /* cope with silly includes */
+#include <linux/major.h>
+typedef unsigned char u_char; /* horrible, for scsi.h */
#include "sg_err.h"
+#include "llseek.h"
/* A utility program for the Linux OS SCSI generic ("sg") device driver.
-* Copyright (C) 1999 D. Gilbert and P. Allworth
+* Copyright (C) 1999, 2000 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)
* any later version.
This program is a specialization of the Unix "dd" command in which
- one or both of the given files is a scsi generic device. A block size
- ('bs') is assumed to be 512 if not given. This program complains if
- 'ibs' or 'obs' are given with some other value than 'bs'.
- If 'if' is not given or 'if=-' then stdin is assumed. If 'of' is
- not given of 'of=-' then stdout assumed. The multipliers "c, b, k, m"
- are recognized on numeric arguments.
-
+ one or both of the given files is a scsi generic device or a raw
+ device. A block size ('bs') is assumed to be 512 if not given. This
+ program complains if 'ibs' or 'obs' are given with some other value
+ than 'bs'. If 'if' is not given or 'if=-' then stdin is assumed. If
+ 'of' is not given or 'of=-' then stdout assumed. Multipliers:
+ 'c','C' *1 'b','B' *512 'k' *1024 'K' *1000
+ 'm' *(1024^2) 'M' *(1000^2) 'g' *(1024^3) 'G' *(1000^3)
+
A non-standard argument "bpt" (blocks per transfer) is added to control
the maximum number of blocks in each transfer. The default value is 128.
For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16KB
@@ -34,90 +43,229 @@
command.
This version should compile with Linux sg drivers with version numbers
- >= 30000 . This version uses usleep() for a delay loop.
+ >= 30000 . This version uses posix threads.
- Version 3.982 20000827
*/
+static char * version_str = "0.51 20010114";
+
#define DEF_BLOCK_SIZE 512
#define DEF_BLOCKS_PER_TRANSFER 128
/* #define SG_DEBUG */
#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
-#define DEF_TIMEOUT 40000 /* 40,000 millisecs == 40 seconds */
+#define DEF_TIMEOUT 60000 /* 60,000 millisecs == 60 seconds */
#define S_RW_LEN 10 /* Use SCSI READ(10) and WRITE(10) */
-#define RCAP_REPLY_LEN 8
-#define SGQ_MAX_RD_AHEAD 4
-#define SGQ_MAX_WR_AHEAD 4
-#define SGQ_NUM_ELEMS (SGQ_MAX_RD_AHEAD+ SGQ_MAX_WR_AHEAD + 1)
-
-#define SGQ_FREE 0
-#define SGQ_IO_STARTED 1
-#define SGQ_IO_FINISHED 2
-#define SGQ_IO_ERR 3
-#define SGQ_IO_WAIT 4
-
-#define SGQ_CAN_DO_NOTHING 0 /* only temporarily in use */
-#define SGQ_CAN_READ 1
-#define SGQ_CAN_WRITE 2
-#define SGQ_TIMEOUT 4
-typedef struct request_element
-{
- struct request_element * nextp;
- int state;
- int wr;
- int blk;
- int num_blks;
- unsigned char * buffp;
- unsigned char * alloc_bp;
- sg_io_hdr_t io_hdr;
- unsigned char cmd[S_RW_LEN];
- unsigned char sb[SENSE_BUFF_LEN];
- int result;
- int stop_after_wr;
-} Rq_elem;
+#define SGP_READ10 0x28
+#define SGP_WRITE10 0x2a
+#define DEF_NUM_THREADS 4 /* actually degree of concurrency */
+#define MAX_NUM_THREADS 32
+
+#ifndef RAW_MAJOR
+#define RAW_MAJOR 255 /*unlikey value */
+#endif
+
+#define FT_OTHER 0 /* filetype other than sg or raw device */
+#define FT_SG 1 /* filetype is sg char device */
+#define FT_RAW 2 /* filetype is raw char device */
+
+#define QS_IDLE 0 /* ready to start a copy cycle */
+#define QS_IN_STARTED 1 /* commenced read */
+#define QS_IN_FINISHED 2 /* finished read, ready for write */
+#define QS_OUT_STARTED 3 /* commenced write */
+
+#define QS_IN_POLL 11
+#define QS_OUT_POLL 12
+
+struct request_element;
typedef struct request_collection
-{
+{ /* one instance visible to all threads */
int infd;
- int in_is_sg;
+ int skip;
+ int in_type;
+ int in_scsi_type;
int in_blk; /* next block address to read */
int in_count; /* blocks remaining for next read */
int in_done_count; /* count of completed in blocks */
int in_partial;
int outfd;
- int out_is_sg;
- int lowest_seek;
+ int seek;
+ int out_type;
+ int out_scsi_type;
int out_blk; /* next block address to write */
int out_count; /* blocks remaining for next write */
int out_done_count; /* count of completed out blocks */
- int out_partial;
+ int out_partial;
int bs;
int bpt;
int dio;
- int dio_incomplete;
+ int dio_incomplete;
int sum_of_resids;
+ int coe;
int debug;
- Rq_elem * rd_posp;
- Rq_elem * wr_posp;
- Rq_elem elem[SGQ_NUM_ELEMS];
+ int num_rq_elems;
+ struct request_element * req_arr;
} Rq_coll;
+typedef struct request_element
+{ /* one instance per worker thread */
+ int qstate; /* "QS" state */
+ int infd;
+ int outfd;
+ int wr;
+ int blk;
+ int num_blks;
+ unsigned char * buffp;
+ unsigned char * alloc_bp;
+ sg_io_hdr_t io_hdr;
+ unsigned char cmd[S_RW_LEN];
+ unsigned char sb[SENSE_BUFF_LEN];
+ int bs;
+ int dio;
+ int dio_incomplete;
+ int resid;
+ int in_scsi_type;
+ int out_scsi_type;
+ int debug;
+} Rq_elem;
+
+static Rq_coll rcoll;
+static struct pollfd in_pollfd_arr[MAX_NUM_THREADS];
+static struct pollfd out_pollfd_arr[MAX_NUM_THREADS];
+static int dd_count = -1;
+
+int sg_fin_in_operation(Rq_coll * clp, Rq_elem * rep);
+int sg_fin_out_operation(Rq_coll * clp, Rq_elem * rep);
+int normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks);
+int normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks);
+int sg_start_io(Rq_elem * rep);
+int sg_finish_io(int wr, Rq_elem * rep);
+
+
+static void install_handler (int sig_num, void (*sig_handler) (int sig))
+{
+ struct sigaction sigact;
+ sigaction (sig_num, NULL, &sigact);
+ if (sigact.sa_handler != SIG_IGN)
+ {
+ sigact.sa_handler = sig_handler;
+ sigemptyset (&sigact.sa_mask);
+ sigact.sa_flags = 0;
+ sigaction (sig_num, &sigact, NULL);
+ }
+}
+
+void print_stats()
+{
+ int infull, outfull;
+
+ if (0 != rcoll.out_count)
+ fprintf(stderr, " remaining block count=%d\n", rcoll.out_count);
+ infull = dd_count - rcoll.in_done_count - rcoll.in_partial;
+ fprintf(stderr, "%d+%d records in\n", infull, rcoll.in_partial);
+ outfull = dd_count - rcoll.out_done_count - rcoll.out_partial;
+ fprintf(stderr, "%d+%d records out\n", outfull, rcoll.out_partial);
+}
+
+static void interrupt_handler(int sig)
+{
+ struct sigaction sigact;
+
+ sigact.sa_handler = SIG_DFL;
+ sigemptyset (&sigact.sa_mask);
+ sigact.sa_flags = 0;
+ sigaction (sig, &sigact, NULL);
+ fprintf(stderr, "Interrupted by signal,");
+ print_stats ();
+ kill (getpid (), sig);
+}
+
+static void siginfo_handler(int sig)
+{
+ fprintf(stderr, "Progress report, continuing ...\n");
+ print_stats ();
+}
+
+int dd_filetype(const char * filename)
+{
+ struct stat st;
+
+ if (stat(filename, &st) < 0)
+ return FT_OTHER;
+ if (S_ISCHR(st.st_mode)) {
+ if (RAW_MAJOR == major(st.st_rdev))
+ return FT_RAW;
+ else if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
+ return FT_SG;
+ }
+ return FT_OTHER;
+}
void usage()
{
- printf("Usage: "
+ fprintf(stderr, "Usage: "
"sgq_dd [if=<infile>] [skip=<n>] [of=<ofile>] [seek=<n>]\n"
- " [bs=<num>] [bpt=<num>] [count=<n>]"
- " [dio=<n>] [deb=<n>]\n"
- " either 'if' or 'of' must be a scsi generic device\n"
+ " [bs=<num>] [bpt=<num>] [count=<n>]\n"
+ " [dio=<n>] [thr=<n>] [coe=<n>] [gen=<n>]\n"
+ " [deb=<n>] [--version]\n"
+ " usually either 'if' or 'of' is a sg or raw device\n"
" 'bpt' is blocks_per_transfer (default is 128)\n"
" 'dio' is direct IO, 1->attempt, 0->indirect IO (def)\n"
- " 'deb' is debug, 1->output some, 0->no debug (def)\n");
+ " 'thr' is number of queues, must be > 0, default 4, max 32\n");
+ fprintf(stderr, " 'coe' continue on sg error, 0->exit (def), "
+ "1->zero + continue\n"
+ " 'gen' 0-> 1 file is special(def), 1-> any files allowed\n"
+ " 'deb' is debug, 0->none (def), > 0->varying degrees of debug\n");
+}
+
+/* Returns -1 for error, 0 for nothing found, QS_IN_POLL or QS_OUT_POLL */
+int do_poll(Rq_coll * clp, int timeout, int * req_indexp)
+{
+ int k, res;
+
+ if (FT_SG == clp->out_type) {
+ while (((res = poll(out_pollfd_arr, clp->num_rq_elems, timeout)) < 0)
+ && (EINTR == errno))
+ ;
+ if (res < 0) {
+ perror("poll error on output fds");
+ return -1;
+ }
+ else if (res > 0) {
+ for (k = 0; k < clp->num_rq_elems; ++k) {
+ if (out_pollfd_arr[k].revents & POLLIN) {
+ if (req_indexp)
+ *req_indexp = k;
+ return QS_OUT_POLL;
+ }
+ }
+ }
+ }
+ if (FT_SG == clp->in_type) {
+ while (((res = poll(in_pollfd_arr, clp->num_rq_elems, timeout)) < 0)
+ && (EINTR == errno))
+ ;
+ if (res < 0) {
+ perror("poll error on input fds");
+ return -1;
+ }
+ else if (res > 0) {
+ for (k = 0; k < clp->num_rq_elems; ++k) {
+ if (in_pollfd_arr[k].revents & POLLIN) {
+ if (req_indexp)
+ *req_indexp = k;
+ return QS_IN_POLL;
+ }
+ }
+ }
+ }
+ return 0;
}
+
/* Return of 0 -> success, -1 -> failure, 2 -> try again */
int read_capacity(int sg_fd, int * num_sect, int * sect_sz)
{
@@ -132,10 +280,10 @@ int read_capacity(int sg_fd, int * num_sect, int * sect_sz)
io_hdr.cmd_len = sizeof(rcCmdBlk);
io_hdr.mx_sb_len = sizeof(sense_b);
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
- io_hdr.dxfer_len = RCAP_REPLY_LEN;
+ io_hdr.dxfer_len = sizeof(rcBuff);
io_hdr.dxferp = rcBuff;
io_hdr.cmdp = rcCmdBlk;
- io_hdr.sbp = sense_b;
+ io_hdr.sbp = sense_b;
io_hdr.timeout = DEF_TIMEOUT;
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
@@ -154,22 +302,145 @@ int read_capacity(int sg_fd, int * num_sect, int * sect_sz)
*sect_sz = (rcBuff[4] << 24) | (rcBuff[5] << 16) |
(rcBuff[6] << 8) | rcBuff[7];
#ifdef SG_DEBUG
- printf("number of sectors=%d, sector size=%d\n", *num_sect, *sect_sz);
+ fprintf(stderr, "number of sectors=%d, sector size=%d\n",
+ *num_sect, *sect_sz);
#endif
return 0;
}
-/* -ve -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM) */
-int sg_start_io(Rq_coll * clp, Rq_elem * rep)
+/* 0 -> ok, 1 -> short read, -1 -> error */
+int normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks)
+{
+ int res;
+ int stop_after_write = 0;
+
+ rep->qstate = QS_IN_STARTED;
+ if (rep->debug > 8)
+ fprintf(stderr, "normal_in_operation: start blk=%d num_blks=%d\n",
+ rep->blk, rep->num_blks);
+ while (((res = read(rep->infd, rep->buffp,
+ blocks * rep->bs)) < 0) && (EINTR == errno))
+ ;
+ if (res < 0) {
+ fprintf(stderr, "sgq_dd: reading, in_blk=%d, errno=%d\n", rep->blk,
+ errno);
+ return -1;
+ }
+ if (res < blocks * rep->bs) {
+ int o_blocks = blocks;
+ stop_after_write = 1;
+ blocks = res / rep->bs;
+ if ((res % rep->bs) > 0) {
+ blocks++;
+ clp->in_partial++;
+ }
+ /* Reverse out + re-apply blocks on clp */
+ clp->in_blk -= o_blocks;
+ clp->in_count += o_blocks;
+ rep->num_blks = blocks;
+ clp->in_blk += blocks;
+ clp->in_count -= blocks;
+ }
+ clp->in_done_count -= blocks;
+ rep->qstate = QS_IN_FINISHED;
+ return stop_after_write;
+}
+
+/* 0 -> ok, -1 -> error */
+int normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks)
+{
+ int res;
+
+ rep->qstate = QS_OUT_STARTED;
+ if (rep->debug > 8)
+ fprintf(stderr, "normal_out_operation: start blk=%d num_blks=%d\n",
+ rep->blk, rep->num_blks);
+ while (((res = write(rep->outfd, rep->buffp,
+ rep->num_blks * rep->bs)) < 0) && (EINTR == errno))
+ ;
+ if (res < 0) {
+ fprintf(stderr, "sgq_dd: output, out_blk=%d, errno=%d\n", rep->blk,
+ errno);
+ return -1;
+ }
+ if (res < blocks * rep->bs) {
+ blocks = res / rep->bs;
+ if ((res % rep->bs) > 0) {
+ blocks++;
+ clp->out_partial++;
+ }
+ rep->num_blks = blocks;
+ }
+ clp->out_done_count -= blocks;
+ rep->qstate = QS_IDLE;
+ return 0;
+}
+
+/* Returns 1 for retryable, 0 for ok, -ve for error */
+int sg_fin_in_operation(Rq_coll * clp, Rq_elem * rep)
+{
+ int res;
+
+ rep->qstate = QS_IN_FINISHED;
+ res = sg_finish_io(rep->wr, rep);
+ if (res < 0) {
+ if (clp->coe) {
+ memset(rep->buffp, 0, rep->num_blks * rep->bs);
+ fprintf(stderr, ">> substituted zeros for in blk=%d for "
+ "%d bytes\n", rep->blk, rep->num_blks * rep->bs);
+ res = 0;
+ }
+ else {
+ fprintf(stderr, "error finishing sg in command\n");
+ return res;
+ }
+ }
+ if (0 == res) { /* looks good, going to return */
+ if (rep->dio_incomplete || rep->resid) {
+ clp->dio_incomplete += rep->dio_incomplete;
+ clp->sum_of_resids += rep->resid;
+ }
+ clp->in_done_count -= rep->num_blks;
+ }
+ return res;
+}
+
+/* Returns 1 for retryable, 0 for ok, -ve for error */
+int sg_fin_out_operation(Rq_coll * clp, Rq_elem * rep)
+{
+ int res;
+
+ rep->qstate = QS_IDLE;
+ res = sg_finish_io(rep->wr, rep);
+ if (res < 0) {
+ if (clp->coe) {
+ fprintf(stderr, ">> ignored error for out blk=%d for "
+ "%d bytes\n", rep->blk, rep->num_blks * rep->bs);
+ res = 0;
+ }
+ else {
+ fprintf(stderr, "error finishing sg out command\n");
+ return res;
+ }
+ }
+ if (0 == res) {
+ if (rep->dio_incomplete || rep->resid) {
+ clp->dio_incomplete += rep->dio_incomplete;
+ clp->sum_of_resids += rep->resid;
+ }
+ clp->out_done_count -= rep->num_blks;
+ }
+ return res;
+}
+
+int sg_start_io(Rq_elem * rep)
{
sg_io_hdr_t * hp = &rep->io_hdr;
int res;
-#if 0
- static int testing = 0;
-#endif
+ rep->qstate = rep->wr ? QS_OUT_STARTED : QS_IN_STARTED;
memset(rep->cmd, 0, sizeof(rep->cmd));
- rep->cmd[0] = rep->wr ? 0x2a : 0x28;
+ rep->cmd[0] = rep->wr ? SGP_WRITE10 : SGP_READ10;
rep->cmd[2] = (unsigned char)((rep->blk >> 24) & 0xFF);
rep->cmd[3] = (unsigned char)((rep->blk >> 16) & 0xFF);
rep->cmd[4] = (unsigned char)((rep->blk >> 8) & 0xFF);
@@ -181,428 +452,275 @@ int sg_start_io(Rq_coll * clp, Rq_elem * rep)
hp->cmd_len = sizeof(rep->cmd);
hp->cmdp = rep->cmd;
hp->dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
- hp->dxfer_len = clp->bs * rep->num_blks;
+ hp->dxfer_len = rep->bs * rep->num_blks;
hp->dxferp = rep->buffp;
hp->mx_sb_len = sizeof(rep->sb);
hp->sbp = rep->sb;
hp->timeout = DEF_TIMEOUT;
hp->usr_ptr = rep;
hp->pack_id = rep->blk;
- if (clp->dio)
+ if (rep->dio)
hp->flags |= SG_FLAG_DIRECT_IO;
-#ifdef SG_DEBUG
- printf("sg_start_io: SCSI %s, blk=%d num_blks=%d\n",
- rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks);
- sg_print_command(hp->cmdp);
- printf("dir=%d, len=%d, dxfrp=%p, cmd_len=%d\n", hp->dxfer_direction,
- hp->dxfer_len, hp->dxferp, hp->cmd_len);
-#endif
-
-#if 0
- testing++;
- if (0 == (testing % 10)) {
- rep->state = SGQ_IO_WAIT; /* busy so wait */
- return 0;
+ if (rep->debug > 8) {
+ fprintf(stderr, "sg_start_io: SCSI %s, blk=%d num_blks=%d\n",
+ rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks);
+ sg_print_command(hp->cmdp);
+ fprintf(stderr, "dir=%d, len=%d, dxfrp=%p, cmd_len=%d\n",
+ hp->dxfer_direction, hp->dxfer_len, hp->dxferp, hp->cmd_len);
}
-#endif
- while (((res = write(rep->wr ? clp->outfd : clp->infd, hp,
+ while (((res = write(rep->wr ? rep->outfd : rep->infd, hp,
sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno))
;
if (res < 0) {
if (ENOMEM == errno)
return 1;
- if ((EDOM == errno) || (EAGAIN == errno)) {
- rep->state = SGQ_IO_WAIT; /* busy so wait */
- return 0;
- }
- perror("starting io on sg device, error");
- rep->state = SGQ_IO_ERR;
return res;
}
- rep->state = SGQ_IO_STARTED;
return 0;
}
/* -1 -> unrecoverable error, 0 -> successful, 1 -> try again */
-int sg_finish_io(Rq_coll * clp, int wr, Rq_elem ** repp)
+int sg_finish_io(int wr, Rq_elem * rep)
{
int res;
sg_io_hdr_t io_hdr;
sg_io_hdr_t * hp;
- Rq_elem * rep;
+#if 0
+ static int testing = 0; /* thread dubious! */
+#endif
memset(&io_hdr, 0 , sizeof(sg_io_hdr_t));
- while (((res = read(wr ? clp->outfd : clp->infd, &io_hdr,
+ /* FORCE_PACK_ID active set only read packet with matching pack_id */
+ io_hdr.interface_id = 'S';
+ io_hdr.dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
+ io_hdr.pack_id = rep->blk;
+
+ while (((res = read(wr ? rep->outfd : rep->infd, &io_hdr,
sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno))
;
- rep = (Rq_elem *)io_hdr.usr_ptr;
if (res < 0) {
perror("finishing io on sg device, error");
- rep->state = SGQ_IO_ERR;
return -1;
}
- if (! (rep && (SGQ_IO_STARTED == rep->state))) {
- printf("sg_finish_io: bad usr_ptr\n");
- rep->state = SGQ_IO_ERR;
- return -1;
+ if (rep != (Rq_elem *)io_hdr.usr_ptr) {
+ fprintf(stderr,
+ "sg_finish_io: bad usr_ptr, request-response mismatch\n");
+ exit(1);
}
memcpy(&rep->io_hdr, &io_hdr, sizeof(sg_io_hdr_t));
hp = &rep->io_hdr;
- if (repp)
- *repp = rep;
switch (sg_err_category3(hp)) {
case SG_ERR_CAT_CLEAN:
break;
case SG_ERR_CAT_RECOVERED:
- printf("Recovered error on block=%d, num=%d\n",
- rep->blk, rep->num_blks);
+ fprintf(stderr, "Recovered error on block=%d, num=%d\n",
+ rep->blk, rep->num_blks);
break;
case SG_ERR_CAT_MEDIA_CHANGED:
return 1;
default:
- sg_chk_n_print3(rep->wr ? "writing": "reading", hp);
- rep->state = SGQ_IO_ERR;
- return -1;
+ {
+ char ebuff[64];
+ sprintf(ebuff, "%s blk=%d", rep->wr ? "writing": "reading",
+ rep->blk);
+ sg_chk_n_print3(ebuff, hp);
+ return -1;
+ }
}
- if (clp->dio &&
- ((hp->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
- ++clp->dio_incomplete; /* count dios done as indirect IO */
- clp->sum_of_resids += hp->resid;
- rep->state = SGQ_IO_FINISHED;
-#ifdef SG_DEBUG
- printf("sg_finish_io: %s ", wr ? "writing" : "reading");
- printf(" SGQ_IO_FINISHED elem idx=%d\n", rep - clp->elem);
+#if 0
+ if (0 == (++testing % 100)) return -1;
#endif
+ if (rep->dio &&
+ ((hp->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
+ rep->dio_incomplete = 1; /* count dios done as indirect IO */
+ else
+ rep->dio_incomplete = 0;
+ rep->resid = hp->resid;
+ if (rep->debug > 8)
+ fprintf(stderr, "sg_finish_io: completed %s\n", wr ? "WRITE" : "READ");
return 0;
}
-int sz_reserve(int fd, int bs, int bpt)
+/* Returns scsi_type or -1 for error */
+int sg_prepare(int fd, int sz)
{
int res, t;
+ struct sg_scsi_id info;
res = ioctl(fd, SG_GET_VERSION_NUM, &t);
if ((res < 0) || (t < 30000)) {
- printf("sgq_dd: sg driver prior to 3.x.y\n");
- return 1;
+ fprintf(stderr, "sgq_dd: sg driver prior to 3.x.y\n");
+ return -1;
}
- res = 0;
- t = bs * bpt;
- res = ioctl(fd, SG_SET_RESERVED_SIZE, &t);
- if (res < 0)
+ res = ioctl(fd, SG_SET_RESERVED_SIZE, &sz);
+ if (res < 0)
perror("sgq_dd: SG_SET_RESERVED_SIZE error");
- return 0;
-}
-
-int get_num(char * buf)
-{
- int res, num;
- char c, cc;
-
- res = sscanf(buf, "%d%c", &num, &c);
- if (0 == res)
- return -1;
- else if (1 == res)
- return num;
- else {
- cc = (char)toupper(c);
- if ('B' == cc)
- return num * 512;
- else if ('C' == cc)
- return num;
- else if ('K' == cc)
- return num * 1024;
- else if ('M' == cc)
- return num * 1024 * 1024;
- else {
- printf("unrecognized multiplier\n");
- return -1;
- }
+#if 0
+ t = 1;
+ res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t);
+ if (res < 0)
+ perror("sgq_dd: SG_SET_FORCE_PACK_ID error");
+#endif
+ res = ioctl(fd, SG_GET_SCSI_ID, &info);
+ if (res < 0) {
+ perror("sgq_dd: SG_SET_SCSI_ID error");
+ return -1;
}
+ else
+ return info.scsi_type;
}
-void init_elems(Rq_coll * clp)
+/* Return 0 for ok, anything else for errors */
+int prepare_rq_elems(Rq_coll * clp, const char * inf, const char * outf)
{
- Rq_elem * rep;
int k;
- int off = 0;
+ Rq_elem * rep;
+ size_t psz;
+ char ebuff[256];
int sz = clp->bpt * clp->bs;
+ int scsi_type;
- if (clp->dio) {
- off = getpagesize();
- sz += off;
- }
- clp->wr_posp = &clp->elem[0]; /* making ring buffer */
- clp->rd_posp = clp->wr_posp;
- for (k = 0; k < SGQ_NUM_ELEMS - 1; ++k)
- clp->elem[k].nextp = &clp->elem[k + 1];
- clp->elem[SGQ_NUM_ELEMS - 1].nextp = &clp->elem[0];
- for (k = 0; k < SGQ_NUM_ELEMS; ++k) {
- rep = &clp->elem[k];
- rep->state = SGQ_FREE;
- if (NULL == (rep->alloc_bp = malloc(sz))) {
- printf("out of memory creating user buffers\n");
- exit(1);
- }
- else
- rep->buffp = rep->alloc_bp + off;
- }
-}
-
-int start_read(Rq_coll * clp)
-{
- int blocks = (clp->in_count > clp->bpt) ? clp->bpt : clp->in_count;
- Rq_elem * rep = clp->rd_posp;
- int buf_sz, res;
- char ebuff[256];
+ clp->req_arr = malloc(sizeof(Rq_elem) * clp->num_rq_elems);
+ if (NULL == clp->req_arr)
+ return 1;
+ for (k = 0; k < clp->num_rq_elems; ++k) {
+ rep = &clp->req_arr[k];
+ memset(rep, 0, sizeof(Rq_elem));
+ psz = getpagesize();
+ if (NULL == (rep->alloc_bp = malloc(sz + psz)))
+ return 1;
+ rep->buffp = (unsigned char *)
+ (((unsigned long)rep->alloc_bp + psz - 1) & (~(psz - 1)));
+ rep->qstate = QS_IDLE;
+ rep->bs = clp->bs;
+ rep->dio = clp->dio;
+ rep->debug = clp->debug;
+ rep->out_scsi_type = clp->out_scsi_type;
+ if (FT_SG == clp->in_type) {
+ if (0 == k)
+ rep->infd = clp->infd;
+ else {
+ if ((rep->infd = open(inf, O_RDWR)) < 0) {
+ sprintf(ebuff, "sgq_dd: could not open %s for sg reading",
+ inf);
+ perror(ebuff);
+ return 1;
+ }
+ }
+ in_pollfd_arr[k].fd = rep->infd;
+ in_pollfd_arr[k].events = POLLIN;
+ if ((scsi_type = sg_prepare(rep->infd, sz)) < 0)
+ return 1;
+ if (0 == k)
+ clp->in_scsi_type = scsi_type;
+ rep->in_scsi_type = clp->in_scsi_type;
+ }
+ else
+ rep->infd = clp->infd;
-#ifdef SG_DEBUG
- printf("start_read, elem idx=%d\n", rep - clp->elem);
-#endif
- rep->wr = 0;
- rep->blk = clp->in_blk;
- rep->num_blks = blocks;
- clp->in_blk += blocks;
- clp->in_count -= blocks;
- if (clp->in_is_sg) {
- res = sg_start_io(clp, rep);
- if (1 == res) { /* ENOMEM, find what's available+try that */
- if ((res = ioctl(clp->infd, SG_GET_RESERVED_SIZE, &buf_sz)) < 0) {
- perror("RESERVED_SIZE ioctls failed");
- return res;
- }
- clp->bpt = (buf_sz + clp->bs - 1) / clp->bs;
- printf("Reducing blocks per transfer to %d\n", clp->bpt);
- if (clp->bpt < 1)
- return -ENOMEM;
- res = sg_start_io(clp, rep);
- if (1 == res)
- res = -ENOMEM;
- }
- else if (res < 0) {
- printf("sgq_dd inputting from sg failed, blk=%d\n", rep->blk);
- rep->state = SGQ_IO_ERR;
- return res;
- }
- }
- else {
- rep->state = SGQ_IO_STARTED;
- while (((res = read(clp->infd, rep->buffp, blocks * clp->bs)) < 0) &&
- (EINTR == errno))
- ;
- if (res < 0) {
- sprintf(ebuff, "sgq_dd: reading, in_blk=%d ", rep->blk);
- perror(ebuff);
- rep->state = SGQ_IO_ERR;
- return res;
- }
- if (res < blocks * clp->bs) {
- int o_blocks = blocks;
- rep->stop_after_wr = 1;
- blocks = res / clp->bs;
- if ((res % clp->bs) > 0) {
- blocks++;
- clp->in_partial++;
- }
- /* Reverse out + re-apply blocks on clp */
- clp->in_blk -= o_blocks;
- clp->in_count += o_blocks;
- rep->num_blks = blocks;
- clp->in_blk += blocks;
- clp->in_count -= blocks;
- }
- clp->in_done_count -= blocks;
- rep->state = SGQ_IO_FINISHED;
+ if (FT_SG == clp->out_type) {
+ if (0 == k)
+ rep->outfd = clp->outfd;
+ else {
+ if ((rep->outfd = open(outf, O_RDWR)) < 0) {
+ sprintf(ebuff, "sgq_dd: could not open %s for sg writing",
+ outf);
+ perror(ebuff);
+ return 1;
+ }
+ }
+ out_pollfd_arr[k].fd = rep->outfd;
+ out_pollfd_arr[k].events = POLLIN;
+ if ((scsi_type = sg_prepare(rep->outfd, sz)) < 0)
+ return 1;
+ if (0 == k)
+ clp->out_scsi_type = scsi_type;
+ rep->out_scsi_type = clp->out_scsi_type;
+ }
+ else
+ rep->outfd = clp->outfd;
}
- clp->rd_posp = rep->nextp;
- return blocks;
+ return 0;
}
-int start_write(Rq_coll * clp)
+/* Returns a "QS" code and req index, or QS_IDLE and position of first idle
+ (-1 if no idle position). Returns -1 on poll error. */
+int decider(Rq_coll * clp, int first_xfer, int * req_indexp)
{
- Rq_elem * rep = clp->wr_posp;
- int res, blocks;
- char ebuff[256];
+ int k, res;
+ Rq_elem * rep;
+ int first_idle_index = -1;
+ int lowest_blk_index = -1;
+ int times;
+ int try_poll = 0;
+ int lowest_blk = INT_MAX;
- while ((0 != rep->wr) || (SGQ_IO_FINISHED != rep->state)) {
- rep = rep->nextp;
- if (rep == clp->rd_posp)
- return -1;
- }
-#ifdef SG_DEBUG
- printf("start_write, elem idx=%d\n", rep - clp->elem);
-#endif
- rep->wr = 1;
- blocks = rep->num_blks;
- rep->blk = clp->out_blk;
- clp->out_blk += blocks;
- clp->out_count -= blocks;
- if (clp->out_is_sg) {
- res = sg_start_io(clp, rep);
- if (1 == res) /* ENOMEM, give up */
- return -ENOMEM;
- else if (res < 0) {
- printf("sgq_dd output to sg failed, blk=%d\n", rep->blk);
- rep->state = SGQ_IO_ERR;
- return res;
- }
+ times = first_xfer ? 1 : clp->num_rq_elems;
+ for (k = 0; k < times; ++k) {
+ rep = &clp->req_arr[k];
+ if ((QS_IN_STARTED == rep->qstate) ||
+ (QS_OUT_STARTED == rep->qstate))
+ try_poll = 1;
+ else if ((QS_IN_FINISHED == rep->qstate) && (rep->blk < lowest_blk)) {
+ lowest_blk = rep->blk;
+ lowest_blk_index = k;
+ }
+ else if ((QS_IDLE == rep->qstate) && (first_idle_index < 0))
+ first_idle_index = k;
}
- else {
- rep->state = SGQ_IO_STARTED;
- while (((res = write(clp->outfd, rep->buffp,
- rep->num_blks * clp->bs)) < 0) && (EINTR == errno))
- ;
- if (res < 0) {
- sprintf(ebuff, "sgq_dd: output, out_blk=%d ", rep->blk);
- perror(ebuff);
- rep->state = SGQ_IO_ERR;
- return res;
- }
- if (res < blocks * clp->bs) {
- blocks = res / clp->bs;
- if ((res % clp->bs) > 0) {
- blocks++;
- clp->out_partial++;
- }
- rep->num_blks = blocks;
- }
- rep->state = SGQ_IO_FINISHED;
+ if (try_poll) {
+ res = do_poll(clp, 0, req_indexp);
+ if (0 != res)
+ return res;
}
- return blocks;
-}
-int do_poll(int fd)
-{
- struct pollfd a_pollfd = {0, POLLIN | POLLOUT, 0};
-
- a_pollfd.fd = fd;
- if (poll(&a_pollfd, 1, 0) < 0) {
- perror("poll error");
- return 0;
+ if (lowest_blk_index >= 0) {
+ if (req_indexp)
+ *req_indexp = lowest_blk_index;
+ return QS_IN_FINISHED;
}
- /* printf("do_poll: revents=0x%x\n", (int)a_pollfd.revents); */
- return (a_pollfd.revents & POLLIN) ? 1 : 0;
+ if (req_indexp)
+ *req_indexp = first_idle_index;
+ return QS_IDLE;
}
-int can_read_write(Rq_coll * clp)
+int get_num(char * buf)
{
- Rq_elem * rep = NULL;
- int res = 0;
- int reading = 0;
- int writing = 0;
- int writeable = 0;
- int rd_waiting = 0;
- int wr_waiting = 0;
- int sg_finished = 0;
-
- /* if write completion pending, then complete it + start read */
- if (clp->out_is_sg) {
- while ((res = do_poll(clp->outfd))) {
- if (res < 0)
- return res;
- res = sg_finish_io(clp, 1, &rep);
- if (res < 0)
- return res;
- else if (1 == res) {
- res = sg_start_io(clp, rep);
- if (0 != res)
- return -1; /* give up if any problems with retry */
- }
- else
- sg_finished++;
- }
- while ((rep = clp->wr_posp) && (SGQ_IO_FINISHED == rep->state) &&
- (1 == rep->wr) && (rep != clp->rd_posp)) {
- rep->state = SGQ_FREE;
- clp->out_done_count -= rep->num_blks;
- clp->wr_posp = rep->nextp;
- if (rep->stop_after_wr)
- return -1;
- }
- }
- else if ((rep = clp->wr_posp) && (1 == rep->wr) &&
- (SGQ_IO_FINISHED == rep->state)) {
- rep->state = SGQ_FREE;
- clp->out_done_count -= rep->num_blks;
- clp->wr_posp = rep->nextp;
- if (rep->stop_after_wr)
- return -1;
- }
-
- /* if read completion pending, then complete it + start maybe write */
- if (clp->in_is_sg) {
- while ((res = do_poll(clp->infd))) {
- if (res < 0)
- return res;
- res = sg_finish_io(clp, 0, &rep);
- if (res < 0)
- return res;
- if (1 == res) {
- res = sg_start_io(clp, rep);
- if (0 != res)
- return -1; /* give up if any problems with retry */
- }
- else {
- sg_finished++;
- clp->in_done_count -= rep->num_blks;
- }
- }
- }
+ int res, num;
+ char c;
- for (rep = clp->wr_posp, res = 1;
- rep != clp->rd_posp; rep = rep->nextp) {
- if (SGQ_IO_STARTED == rep->state) {
- if (rep->wr)
- ++writing;
- else {
- res = 0;
- ++reading;
- }
- }
- else if ((0 == rep->wr) && (SGQ_IO_FINISHED == rep->state)) {
- if (res)
- writeable = 1;
- }
- else if (SGQ_IO_WAIT == rep->state) {
- res = 0;
- if (rep->wr)
- ++wr_waiting;
- else
- ++rd_waiting;
- }
- else
- res = 0;
- }
- if (clp->debug) {
- if ((clp->debug >= 9) || wr_waiting || rd_waiting)
- printf("%d/%d (nwb/nrb): read=%d/%d (do/wt) "
- "write=%d/%d (do/wt) writeable=%d sg_fin=%d\n",
- clp->out_blk, clp->in_blk, reading, rd_waiting,
- writing, wr_waiting, writeable, sg_finished);
- }
- if (writeable && (writing < SGQ_MAX_WR_AHEAD) && (clp->out_count > 0))
- return SGQ_CAN_WRITE;
- if ((reading < SGQ_MAX_RD_AHEAD) && (clp->in_count > 0) &&
- (0 == rd_waiting) && (clp->rd_posp->nextp != clp->wr_posp))
- return SGQ_CAN_READ;
-
- if (clp->out_done_count <= 0)
- return SGQ_CAN_DO_NOTHING;
-
- usleep(3000); /* hang about for 10 milliseconds */
- /* Now check the _whole_ buffer for pending requests */
- for (rep = clp->rd_posp->nextp; rep != clp->rd_posp; rep = rep->nextp) {
- if (SGQ_IO_WAIT == rep->state) {
- res = sg_start_io(clp, rep);
- if (res < 0)
- return res;
- if (res > 0)
- return -1;
- break;
+ res = sscanf(buf, "%d%c", &num, &c);
+ if (0 == res)
+ return -1;
+ else if (1 == res)
+ return num;
+ else {
+ switch (c) {
+ case 'c':
+ case 'C':
+ return num;
+ case 'b':
+ case 'B':
+ return num * 512;
+ case 'k':
+ return num * 1024;
+ case 'K':
+ return num * 1000;
+ case 'm':
+ return num * 1024 * 1024;
+ case 'M':
+ return num * 1000000;
+ case 'g':
+ return num * 1024 * 1024 * 1024;
+ case 'G':
+ return num * 1000000000;
+ default:
+ fprintf(stderr, "unrecognized multiplier\n");
+ return -1;
}
}
- return SGQ_CAN_DO_NOTHING;
}
@@ -612,7 +730,6 @@ int main(int argc, char * argv[])
int seek = 0;
int ibs = 0;
int obs = 0;
- int count = -1;
char str[512];
char * key;
char * buf;
@@ -621,12 +738,17 @@ int main(int argc, char * argv[])
int res, k;
int in_num_sect = 0;
int out_num_sect = 0;
- int in_sect_sz, out_sect_sz, crw;
+ int num_threads = DEF_NUM_THREADS;
+ int gen = 0;
+ int in_sect_sz, out_sect_sz, first_xfer, qstate, req_index, seek_skip;
+ int blocks, stop_after_write, terminate;
char ebuff[256];
- Rq_coll rcoll;
+ Rq_elem * rep;
memset(&rcoll, 0, sizeof(Rq_coll));
rcoll.bpt = DEF_BLOCKS_PER_TRANSFER;
+ rcoll.in_type = FT_OTHER;
+ rcoll.out_type = FT_OTHER;
inf[0] = '\0';
outf[0] = '\0';
if (argc < 2) {
@@ -660,49 +782,79 @@ int main(int argc, char * argv[])
else if (0 == strcmp(key,"seek"))
seek = get_num(buf);
else if (0 == strcmp(key,"count"))
- count = get_num(buf);
+ dd_count = get_num(buf);
else if (0 == strcmp(key,"dio"))
rcoll.dio = get_num(buf);
- else if (0 == strcmp(key,"deb"))
+ else if (0 == strcmp(key,"thr"))
+ num_threads = get_num(buf);
+ else if (0 == strcmp(key,"coe"))
+ rcoll.coe = get_num(buf);
+ else if (0 == strcmp(key,"gen"))
+ gen = get_num(buf);
+ else if (0 == strncmp(key,"deb", 3))
rcoll.debug = get_num(buf);
+ else if (0 == strncmp(key, "--vers", 6)) {
+ fprintf(stderr, "sgq_dd for sg version 3 driver: %s\n",
+ version_str);
+ return 0;
+ }
else {
- printf("Unrecognized argument '%s'\n", key);
+ fprintf(stderr, "Unrecognized argument '%s'\n", key);
usage();
return 1;
}
}
if (rcoll.bs <= 0) {
rcoll.bs = DEF_BLOCK_SIZE;
- printf("Assume default 'bs' (block size) of %d bytes\n", rcoll.bs);
+ fprintf(stderr, "Assume default 'bs' (block size) of %d bytes\n",
+ rcoll.bs);
}
if ((ibs && (ibs != rcoll.bs)) || (obs && (obs != rcoll.bs))) {
- printf("If 'ibs' or 'obs' given must be same as 'bs'\n");
+ fprintf(stderr, "If 'ibs' or 'obs' given must be same as 'bs'\n");
usage();
return 1;
}
if ((skip < 0) || (seek < 0)) {
- printf("skip and seek cannot be negative\n");
+ fprintf(stderr, "skip and seek cannot be negative\n");
return 1;
}
-#ifdef SG_DEBUG
- printf("sgq_dd: if=%s skip=%d of=%s seek=%d count=%d\n",
- inf, skip, outf, seek, count);
-#endif
+ if ((num_threads < 1) || (num_threads > MAX_NUM_THREADS)) {
+ fprintf(stderr, "too few or too many threads requested\n");
+ usage();
+ return 1;
+ }
+ if (rcoll.debug)
+ fprintf(stderr, "sgq_dd: if=%s skip=%d of=%s seek=%d count=%d\n",
+ inf, skip, outf, seek, dd_count);
+ install_handler (SIGINT, interrupt_handler);
+ install_handler (SIGQUIT, interrupt_handler);
+ install_handler (SIGPIPE, interrupt_handler);
+ install_handler (SIGUSR1, siginfo_handler);
+
rcoll.infd = STDIN_FILENO;
rcoll.outfd = STDOUT_FILENO;
if (inf[0] && ('-' != inf[0])) {
- if ((rcoll.infd = open(inf, O_RDONLY)) < 0) {
- sprintf(ebuff, "sgq_dd: could not open %s for reading", inf);
- perror(ebuff);
- return 1;
+ rcoll.in_type = dd_filetype(inf);
+
+ if (FT_SG == rcoll.in_type) {
+ if ((rcoll.infd = open(inf, O_RDWR)) < 0) {
+ sprintf(ebuff, "sgq_dd: could not open %s for sg reading",
+ inf);
+ perror(ebuff);
+ return 1;
+ }
}
- if (ioctl(rcoll.infd, SG_GET_TIMEOUT, 0) < 0) {
- rcoll.in_is_sg = 0;
- if (skip > 0) {
- off_t offset = skip;
+ if (FT_SG != rcoll.in_type) {
+ if ((rcoll.infd = open(inf, O_RDONLY)) < 0) {
+ sprintf(ebuff, "sgq_dd: could not open %s for reading", inf);
+ perror(ebuff);
+ return 1;
+ }
+ else if (skip > 0) {
+ llse_loff_t offset = skip;
- offset *= rcoll.bs; /* could overflow here! */
- if (lseek(rcoll.infd, offset, SEEK_SET) < 0) {
+ offset *= rcoll.bs; /* could exceed 32 here! */
+ if (llse_llseek(rcoll.infd, offset, SEEK_SET) < 0) {
sprintf(ebuff,
"sgq_dd: couldn't skip to required position on %s", inf);
perror(ebuff);
@@ -710,69 +862,67 @@ int main(int argc, char * argv[])
}
}
}
- else { /* looks like sg device so close then re-open it RW */
- close(rcoll.infd);
- if ((rcoll.infd = open(inf, O_RDWR | O_NONBLOCK)) < 0) {
- printf("If %s is a sg device, need read+write permissions,"
- " even to read it!\n", inf);
- return 1;
- }
- rcoll.in_is_sg = 1;
- if (sz_reserve(rcoll.infd, rcoll.bs, rcoll.bpt))
- return 1;
- }
}
if (outf[0] && ('-' != outf[0])) {
- if ((rcoll.outfd = open(outf, O_RDWR | O_NONBLOCK)) >= 0) {
- if (ioctl(rcoll.outfd, SG_GET_TIMEOUT, 0) < 0) {
- /* not a scsi generic device so now try and open RDONLY */
- close(rcoll.outfd);
- }
- else {
- rcoll.out_is_sg = 1;
- if (sz_reserve(rcoll.outfd, rcoll.bs, rcoll.bpt))
- return 1;
- }
- }
- if (! rcoll.out_is_sg) {
- if ((rcoll.outfd = open(outf, O_WRONLY | O_CREAT, 0666)) < 0) {
- sprintf(ebuff,
- "sgq_dd: could not open %s for writing", outf);
+ rcoll.out_type = dd_filetype(outf);
+
+ if (FT_SG == rcoll.out_type) {
+ if ((rcoll.outfd = open(outf, O_RDWR)) < 0) {
+ sprintf(ebuff,
+ "sgq_dd: could not open %s for sg writing", outf);
perror(ebuff);
return 1;
}
- else if (seek > 0) {
- off_t offset = seek;
+ }
+ else {
+ if (FT_OTHER == rcoll.out_type) {
+ if ((rcoll.outfd = open(outf, O_WRONLY | O_CREAT, 0666)) < 0) {
+ sprintf(ebuff,
+ "sgq_dd: could not open %s for writing", outf);
+ perror(ebuff);
+ return 1;
+ }
+ }
+ else {
+ if ((rcoll.outfd = open(outf, O_WRONLY)) < 0) {
+ sprintf(ebuff,
+ "sgq_dd: could not open %s for raw writing", outf);
+ perror(ebuff);
+ return 1;
+ }
+ }
+ if (seek > 0) {
+ llse_loff_t offset = seek;
- offset *= rcoll.bs; /* could overflow here! */
- if (lseek(rcoll.outfd, offset, SEEK_SET) < 0) {
+ offset *= rcoll.bs; /* could exceed 32 bits here! */
+ if (llse_llseek(rcoll.outfd, offset, SEEK_SET) < 0) {
sprintf(ebuff,
"sgq_dd: couldn't seek to required position on %s", outf);
perror(ebuff);
return 1;
}
}
- }
+ }
}
if ((STDIN_FILENO == rcoll.infd) && (STDOUT_FILENO == rcoll.outfd)) {
- printf("Can't have both 'if' as stdin _and_ 'of' as stdout\n");
+ fprintf(stderr, "Disallow both if and of to be stdin and stdout");
return 1;
}
- if (! (rcoll.in_is_sg || rcoll.out_is_sg)) {
- printf("Either 'if' or 'of' must be a scsi generic device\n");
+ if ((FT_OTHER == rcoll.in_type) && (FT_OTHER == rcoll.out_type) && !gen) {
+ fprintf(stderr, "Either 'if' or 'of' must be a sg or raw device\n");
return 1;
}
- if (0 == count)
+ if (0 == dd_count)
return 0;
- else if (count < 0) {
- if (rcoll.in_is_sg) {
+ else if (dd_count < 0) {
+ if (FT_SG == rcoll.in_type) {
res = read_capacity(rcoll.infd, &in_num_sect, &in_sect_sz);
if (2 == res) {
- printf("Unit attention, media changed(in), try again\n");
+ fprintf(stderr, "Unit attention, media changed(in), repeat\n");
res = read_capacity(rcoll.infd, &in_num_sect, &in_sect_sz);
}
if (0 != res) {
- printf("Unable to read capacity on %s\n", inf);
+ fprintf(stderr, "Unable to read capacity on %s\n", inf);
in_num_sect = -1;
}
else {
@@ -780,14 +930,14 @@ int main(int argc, char * argv[])
in_num_sect -= skip;
}
}
- if (rcoll.out_is_sg) {
+ if (FT_SG == rcoll.out_type) {
res = read_capacity(rcoll.outfd, &out_num_sect, &out_sect_sz);
if (2 == res) {
- printf("Unit attention, media changed(out), try again\n");
+ fprintf(stderr, "Unit attention, media changed(out), repeat\n");
res = read_capacity(rcoll.outfd, &out_num_sect, &out_sect_sz);
}
if (0 != res) {
- printf("Unable to read capacity on %s\n", outf);
+ fprintf(stderr, "Unable to read capacity on %s\n", outf);
out_num_sect = -1;
}
else {
@@ -795,71 +945,178 @@ int main(int argc, char * argv[])
out_num_sect -= seek;
}
}
-#ifdef SG_DEBUG
- printf("Start of loop, count=%d, in_num_sect=%d, out_num_sect=%d\n",
- count, in_num_sect, out_num_sect);
-#endif
if (in_num_sect > 0) {
if (out_num_sect > 0)
- count = (in_num_sect > out_num_sect) ? out_num_sect :
+ dd_count = (in_num_sect > out_num_sect) ? out_num_sect :
in_num_sect;
else
- count = in_num_sect;
+ dd_count = in_num_sect;
}
else
- count = out_num_sect;
+ dd_count = out_num_sect;
+ }
+ if (rcoll.debug > 1)
+ fprintf(stderr, "Start of loop, count=%d, in_num_sect=%d, "
+ "out_num_sect=%d\n", dd_count, in_num_sect, out_num_sect);
+ if (dd_count <= 0) {
+ fprintf(stderr, "Couldn't calculate count, please give one\n");
+ return 1;
}
-#ifdef SG_DEBUG
- printf("Start of loop, count=%d, bpt=%d\n", count, rcoll.bpt);
-#endif
-
- rcoll.in_count = count;
- rcoll.in_done_count = count;
+ rcoll.in_count = dd_count;
+ rcoll.in_done_count = dd_count;
+ rcoll.skip = skip;
rcoll.in_blk = skip;
- rcoll.out_count = count;
- rcoll.out_done_count = count;
+ rcoll.out_count = dd_count;
+ rcoll.out_done_count = dd_count;
+ rcoll.seek = seek;
rcoll.out_blk = seek;
- init_elems(&rcoll);
-/* vvvvvvvvvvvvvvvvv Main Loop vvvvvvvvvvvvvvvvvvvvvvvv */
- while (rcoll.out_done_count > 0) {
- crw = can_read_write(&rcoll);
- if (crw < 0)
- break;
- if (SGQ_CAN_READ & crw) {
- res = start_read(&rcoll);
- if (res <= 0) {
- printf("start_read: res=%d\n", res);
- break;
- }
- }
- if (SGQ_CAN_WRITE & crw) {
- res = start_write(&rcoll);
- if (res <= 0) {
- printf("start_write: res=%d\n", res);
- break;
- }
- }
+ if ((FT_SG == rcoll.in_type) || (FT_SG == rcoll.out_type))
+ rcoll.num_rq_elems = num_threads;
+ else
+ rcoll.num_rq_elems = 1;
+ if (prepare_rq_elems(&rcoll, inf, outf)) {
+ fprintf(stderr, "Setup failure, perhaps no memory\n");
+ return 1;
}
+ first_xfer = 1;
+ stop_after_write = 0;
+ terminate = 0;
+ seek_skip = rcoll.seek - rcoll.skip;
+ while (rcoll.out_done_count > 0) { /* >>>>>>>>> main loop */
+ req_index = -1;
+ qstate = decider(&rcoll, first_xfer, &req_index);
+ rep = (req_index < 0) ? NULL : (rcoll.req_arr + req_index);
+ switch (qstate) {
+ case QS_IDLE:
+ if ((NULL == rep) || (rcoll.in_count <= 0)) {
+ /* usleep(1000); */
+ do_poll(&rcoll, 10, NULL);
+ break;
+ }
+ if (rcoll.debug > 8)
+ fprintf(stderr, " sgq_dd: non-sleeping QS_IDLE state, "
+ "req_index=%d\n", req_index);
+ if (first_xfer >= 2)
+ first_xfer = 0;
+ else if (1 == first_xfer)
+ ++first_xfer;
+ if (stop_after_write) {
+ terminate = 1;
+ break;
+ }
+ blocks = (rcoll.in_count > rcoll.bpt) ? rcoll.bpt : rcoll.in_count;
+ rep->wr = 0;
+ rep->blk = rcoll.in_blk;
+ rep->num_blks = blocks;
+ rcoll.in_blk += blocks;
+ rcoll.in_count -= blocks;
+
+ if (FT_SG == rcoll.in_type) {
+ res = sg_start_io(rep);
+ if (0 != res) {
+ if (1 == res)
+ fprintf(stderr, "Out of memory starting sg io\n");
+ terminate = 1;
+ }
+ }
+ else {
+ res = normal_in_operation(&rcoll, rep, blocks);
+ if (res < 0)
+ terminate = 1;
+ else if (res > 0)
+ stop_after_write = 1;
+ }
+ break;
+ case QS_IN_FINISHED:
+ if (rcoll.debug > 8)
+ fprintf(stderr, " sgq_dd: state is QS_IN_FINISHED, "
+ "req_index=%d\n", req_index);
+ if ((rep->blk + seek_skip) != rcoll.out_blk) {
+ /* if write would be out of sequence then wait */
+ usleep(1000);
+ break;
+ }
+ rep->wr = 1;
+ rep->blk = rcoll.out_blk;
+ blocks = rep->num_blks;
+ rcoll.out_blk += blocks;
+ rcoll.out_count -= blocks;
+
+ if (FT_SG == rcoll.out_type) {
+ res = sg_start_io(rep);
+ if (0 != res) {
+ if (1 == res)
+ fprintf(stderr, "Out of memory starting sg io\n");
+ terminate = 1;
+ }
+ }
+ else {
+ if (normal_out_operation(&rcoll, rep, blocks) < 0)
+ terminate = 1;
+ }
+ break;
+ case QS_IN_POLL:
+ if (rcoll.debug > 8)
+ fprintf(stderr, " sgq_dd: state is QS_IN_POLL, "
+ "req_index=%d\n", req_index);
+ res = sg_fin_in_operation(&rcoll, rep);
+ if (res < 0)
+ terminate = 1;
+ else if (res > 1) {
+ if (first_xfer) {
+ /* only retry on first xfer */
+ if (0 != sg_start_io(rep))
+ terminate = 1;
+ }
+ else
+ terminate = 1;
+ }
+ break;
+ case QS_OUT_POLL:
+ if (rcoll.debug > 8)
+ fprintf(stderr, " sgq_dd: state is QS_OUT_POLL, "
+ "req_index=%d\n", req_index);
+ res = sg_fin_out_operation(&rcoll, rep);
+ if (res < 0)
+ terminate = 1;
+ else if (res > 1) {
+ if (first_xfer) {
+ /* only retry on first xfer */
+ if (0 != sg_start_io(rep))
+ terminate = 1;
+ }
+ else
+ terminate = 1;
+ }
+ break;
+ default:
+ if (rcoll.debug > 8)
+ fprintf(stderr, " sgq_dd: state is ?????\n");
+ terminate = 1;
+ break;
+ }
+ if (terminate)
+ break;
+ } /* >>>>>>>>>>>>> end of main loop */
+
if (STDIN_FILENO != rcoll.infd)
close(rcoll.infd);
if (STDOUT_FILENO != rcoll.outfd)
close(rcoll.outfd);
+ res = 0;
if (0 != rcoll.out_count) {
- printf("Some error occurred, remaining blocks=%d\n", rcoll.out_count);
- return 1;
+ fprintf(stderr, ">>>> Some error occurred,\n");
+ res = 2;
}
- printf("%d+%d records in\n", count - rcoll.in_done_count,
- rcoll.in_partial);
- printf("%d+%d records out\n", count - rcoll.out_done_count,
- rcoll.out_partial);
+ print_stats();
if (rcoll.dio_incomplete)
- printf(">> Direct IO requested but incomplete %d times\n",
+ fprintf(stderr, ">> Direct IO requested but incomplete %d times\n",
rcoll.dio_incomplete);
if (rcoll.sum_of_resids)
- printf(">> Non-zero sum of residual counts=%d\n",
+ fprintf(stderr, ">> Non-zero sum of residual counts=%d\n",
rcoll.sum_of_resids);
- return 0;
+ return res;
}
diff --git a/archive/sgq_old_dd.c b/archive/sgq_old_dd.c
new file mode 100644
index 00000000..2af81058
--- /dev/null
+++ b/archive/sgq_old_dd.c
@@ -0,0 +1,865 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/../scsi/sg.h> /* cope with silly includes */
+#include "sg_err.h"
+
+/* A utility program for the Linux OS SCSI generic ("sg") device driver.
+* Copyright (C) 1999 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)
+* any later version.
+
+ This program is a specialization of the Unix "dd" command in which
+ one or both of the given files is a scsi generic device. A block size
+ ('bs') is assumed to be 512 if not given. This program complains if
+ 'ibs' or 'obs' are given with some other value than 'bs'.
+ If 'if' is not given or 'if=-' then stdin is assumed. If 'of' is
+ not given of 'of=-' then stdout assumed. The multipliers "c, b, k, m"
+ are recognized on numeric arguments.
+
+ A non-standard argument "bpt" (blocks per transfer) is added to control
+ the maximum number of blocks in each transfer. The default value is 128.
+ For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16KB
+ in this case) are transferred to or from the sg device in a single SCSI
+ command.
+
+ This version should compile with Linux sg drivers with version numbers
+ >= 30000 . This version uses usleep() for a delay loop.
+
+ Version 3.982 20000827
+*/
+
+#define DEF_BLOCK_SIZE 512
+#define DEF_BLOCKS_PER_TRANSFER 128
+
+/* #define SG_DEBUG */
+
+#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
+#define DEF_TIMEOUT 40000 /* 40,000 millisecs == 40 seconds */
+#define S_RW_LEN 10 /* Use SCSI READ(10) and WRITE(10) */
+#define RCAP_REPLY_LEN 8
+#define SGQ_MAX_RD_AHEAD 4
+#define SGQ_MAX_WR_AHEAD 4
+#define SGQ_NUM_ELEMS (SGQ_MAX_RD_AHEAD+ SGQ_MAX_WR_AHEAD + 1)
+
+#define SGQ_FREE 0
+#define SGQ_IO_STARTED 1
+#define SGQ_IO_FINISHED 2
+#define SGQ_IO_ERR 3
+#define SGQ_IO_WAIT 4
+
+#define SGQ_CAN_DO_NOTHING 0 /* only temporarily in use */
+#define SGQ_CAN_READ 1
+#define SGQ_CAN_WRITE 2
+#define SGQ_TIMEOUT 4
+
+typedef struct request_element
+{
+ struct request_element * nextp;
+ int state;
+ int wr;
+ int blk;
+ int num_blks;
+ unsigned char * buffp;
+ unsigned char * alloc_bp;
+ sg_io_hdr_t io_hdr;
+ unsigned char cmd[S_RW_LEN];
+ unsigned char sb[SENSE_BUFF_LEN];
+ int result;
+ int stop_after_wr;
+} Rq_elem;
+
+typedef struct request_collection
+{
+ int infd;
+ int in_is_sg;
+ int in_blk; /* next block address to read */
+ int in_count; /* blocks remaining for next read */
+ int in_done_count; /* count of completed in blocks */
+ int in_partial;
+ int outfd;
+ int out_is_sg;
+ int lowest_seek;
+ int out_blk; /* next block address to write */
+ int out_count; /* blocks remaining for next write */
+ int out_done_count; /* count of completed out blocks */
+ int out_partial;
+ int bs;
+ int bpt;
+ int dio;
+ int dio_incomplete;
+ int sum_of_resids;
+ int debug;
+ Rq_elem * rd_posp;
+ Rq_elem * wr_posp;
+ Rq_elem elem[SGQ_NUM_ELEMS];
+} Rq_coll;
+
+
+void usage()
+{
+ printf("Usage: "
+ "sgq_dd [if=<infile>] [skip=<n>] [of=<ofile>] [seek=<n>]\n"
+ " [bs=<num>] [bpt=<num>] [count=<n>]"
+ " [dio=<n>] [deb=<n>]\n"
+ " either 'if' or 'of' must be a scsi generic device\n"
+ " 'bpt' is blocks_per_transfer (default is 128)\n"
+ " 'dio' is direct IO, 1->attempt, 0->indirect IO (def)\n"
+ " 'deb' is debug, 1->output some, 0->no debug (def)\n");
+}
+
+/* Return of 0 -> success, -1 -> failure, 2 -> try again */
+int read_capacity(int sg_fd, int * num_sect, int * sect_sz)
+{
+ int res;
+ unsigned char rcCmdBlk [10] = {0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ unsigned char rcBuff[64];
+ unsigned char sense_b[64];
+ sg_io_hdr_t io_hdr;
+
+ memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+ io_hdr.interface_id = 'S';
+ io_hdr.cmd_len = sizeof(rcCmdBlk);
+ io_hdr.mx_sb_len = sizeof(sense_b);
+ io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_hdr.dxfer_len = RCAP_REPLY_LEN;
+ io_hdr.dxferp = rcBuff;
+ io_hdr.cmdp = rcCmdBlk;
+ io_hdr.sbp = sense_b;
+ io_hdr.timeout = DEF_TIMEOUT;
+
+ if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+ perror("read_capacity (SG_IO) error");
+ return -1;
+ }
+ res = sg_err_category3(&io_hdr);
+ if (SG_ERR_CAT_MEDIA_CHANGED == res)
+ return 2; /* probably have another go ... */
+ else if (SG_ERR_CAT_CLEAN != res) {
+ sg_chk_n_print3("read capacity", &io_hdr);
+ return -1;
+ }
+ *num_sect = 1 + ((rcBuff[0] << 24) | (rcBuff[1] << 16) |
+ (rcBuff[2] << 8) | rcBuff[3]);
+ *sect_sz = (rcBuff[4] << 24) | (rcBuff[5] << 16) |
+ (rcBuff[6] << 8) | rcBuff[7];
+#ifdef SG_DEBUG
+ printf("number of sectors=%d, sector size=%d\n", *num_sect, *sect_sz);
+#endif
+ return 0;
+}
+
+/* -ve -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM) */
+int sg_start_io(Rq_coll * clp, Rq_elem * rep)
+{
+ sg_io_hdr_t * hp = &rep->io_hdr;
+ int res;
+#if 0
+ static int testing = 0;
+#endif
+
+ memset(rep->cmd, 0, sizeof(rep->cmd));
+ rep->cmd[0] = rep->wr ? 0x2a : 0x28;
+ rep->cmd[2] = (unsigned char)((rep->blk >> 24) & 0xFF);
+ rep->cmd[3] = (unsigned char)((rep->blk >> 16) & 0xFF);
+ rep->cmd[4] = (unsigned char)((rep->blk >> 8) & 0xFF);
+ rep->cmd[5] = (unsigned char)(rep->blk & 0xFF);
+ rep->cmd[7] = (unsigned char)((rep->num_blks >> 8) & 0xff);
+ rep->cmd[8] = (unsigned char)(rep->num_blks & 0xff);
+ memset(hp, 0, sizeof(sg_io_hdr_t));
+ hp->interface_id = 'S';
+ hp->cmd_len = sizeof(rep->cmd);
+ hp->cmdp = rep->cmd;
+ hp->dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
+ hp->dxfer_len = clp->bs * rep->num_blks;
+ hp->dxferp = rep->buffp;
+ hp->mx_sb_len = sizeof(rep->sb);
+ hp->sbp = rep->sb;
+ hp->timeout = DEF_TIMEOUT;
+ hp->usr_ptr = rep;
+ hp->pack_id = rep->blk;
+ if (clp->dio)
+ hp->flags |= SG_FLAG_DIRECT_IO;
+#ifdef SG_DEBUG
+ printf("sg_start_io: SCSI %s, blk=%d num_blks=%d\n",
+ rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks);
+ sg_print_command(hp->cmdp);
+ printf("dir=%d, len=%d, dxfrp=%p, cmd_len=%d\n", hp->dxfer_direction,
+ hp->dxfer_len, hp->dxferp, hp->cmd_len);
+#endif
+
+#if 0
+ testing++;
+ if (0 == (testing % 10)) {
+ rep->state = SGQ_IO_WAIT; /* busy so wait */
+ return 0;
+ }
+#endif
+
+ while (((res = write(rep->wr ? clp->outfd : clp->infd, hp,
+ sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno))
+ ;
+ if (res < 0) {
+ if (ENOMEM == errno)
+ return 1;
+ if ((EDOM == errno) || (EAGAIN == errno)) {
+ rep->state = SGQ_IO_WAIT; /* busy so wait */
+ return 0;
+ }
+ perror("starting io on sg device, error");
+ rep->state = SGQ_IO_ERR;
+ return res;
+ }
+ rep->state = SGQ_IO_STARTED;
+ return 0;
+}
+
+/* -1 -> unrecoverable error, 0 -> successful, 1 -> try again */
+int sg_finish_io(Rq_coll * clp, int wr, Rq_elem ** repp)
+{
+ int res;
+ sg_io_hdr_t io_hdr;
+ sg_io_hdr_t * hp;
+ Rq_elem * rep;
+
+ memset(&io_hdr, 0 , sizeof(sg_io_hdr_t));
+ while (((res = read(wr ? clp->outfd : clp->infd, &io_hdr,
+ sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno))
+ ;
+ rep = (Rq_elem *)io_hdr.usr_ptr;
+ if (res < 0) {
+ perror("finishing io on sg device, error");
+ rep->state = SGQ_IO_ERR;
+ return -1;
+ }
+ if (! (rep && (SGQ_IO_STARTED == rep->state))) {
+ printf("sg_finish_io: bad usr_ptr\n");
+ rep->state = SGQ_IO_ERR;
+ return -1;
+ }
+ memcpy(&rep->io_hdr, &io_hdr, sizeof(sg_io_hdr_t));
+ hp = &rep->io_hdr;
+ if (repp)
+ *repp = rep;
+
+ switch (sg_err_category3(hp)) {
+ case SG_ERR_CAT_CLEAN:
+ break;
+ case SG_ERR_CAT_RECOVERED:
+ printf("Recovered error on block=%d, num=%d\n",
+ rep->blk, rep->num_blks);
+ break;
+ case SG_ERR_CAT_MEDIA_CHANGED:
+ return 1;
+ default:
+ sg_chk_n_print3(rep->wr ? "writing": "reading", hp);
+ rep->state = SGQ_IO_ERR;
+ return -1;
+ }
+ if (clp->dio &&
+ ((hp->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
+ ++clp->dio_incomplete; /* count dios done as indirect IO */
+ clp->sum_of_resids += hp->resid;
+ rep->state = SGQ_IO_FINISHED;
+#ifdef SG_DEBUG
+ printf("sg_finish_io: %s ", wr ? "writing" : "reading");
+ printf(" SGQ_IO_FINISHED elem idx=%d\n", rep - clp->elem);
+#endif
+ return 0;
+}
+
+int sz_reserve(int fd, int bs, int bpt)
+{
+ int res, t;
+
+ res = ioctl(fd, SG_GET_VERSION_NUM, &t);
+ if ((res < 0) || (t < 30000)) {
+ printf("sgq_dd: sg driver prior to 3.x.y\n");
+ return 1;
+ }
+ res = 0;
+ t = bs * bpt;
+ res = ioctl(fd, SG_SET_RESERVED_SIZE, &t);
+ if (res < 0)
+ perror("sgq_dd: SG_SET_RESERVED_SIZE error");
+ return 0;
+}
+
+int get_num(char * buf)
+{
+ int res, num;
+ char c, cc;
+
+ res = sscanf(buf, "%d%c", &num, &c);
+ if (0 == res)
+ return -1;
+ else if (1 == res)
+ return num;
+ else {
+ cc = (char)toupper(c);
+ if ('B' == cc)
+ return num * 512;
+ else if ('C' == cc)
+ return num;
+ else if ('K' == cc)
+ return num * 1024;
+ else if ('M' == cc)
+ return num * 1024 * 1024;
+ else {
+ printf("unrecognized multiplier\n");
+ return -1;
+ }
+ }
+}
+
+void init_elems(Rq_coll * clp)
+{
+ Rq_elem * rep;
+ int k;
+ int off = 0;
+ int sz = clp->bpt * clp->bs;
+
+ if (clp->dio) {
+ off = getpagesize();
+ sz += off;
+ }
+ clp->wr_posp = &clp->elem[0]; /* making ring buffer */
+ clp->rd_posp = clp->wr_posp;
+ for (k = 0; k < SGQ_NUM_ELEMS - 1; ++k)
+ clp->elem[k].nextp = &clp->elem[k + 1];
+ clp->elem[SGQ_NUM_ELEMS - 1].nextp = &clp->elem[0];
+ for (k = 0; k < SGQ_NUM_ELEMS; ++k) {
+ rep = &clp->elem[k];
+ rep->state = SGQ_FREE;
+ if (NULL == (rep->alloc_bp = malloc(sz))) {
+ printf("out of memory creating user buffers\n");
+ exit(1);
+ }
+ else
+ rep->buffp = rep->alloc_bp + off;
+ }
+}
+
+int start_read(Rq_coll * clp)
+{
+ int blocks = (clp->in_count > clp->bpt) ? clp->bpt : clp->in_count;
+ Rq_elem * rep = clp->rd_posp;
+ int buf_sz, res;
+ char ebuff[256];
+
+#ifdef SG_DEBUG
+ printf("start_read, elem idx=%d\n", rep - clp->elem);
+#endif
+ rep->wr = 0;
+ rep->blk = clp->in_blk;
+ rep->num_blks = blocks;
+ clp->in_blk += blocks;
+ clp->in_count -= blocks;
+ if (clp->in_is_sg) {
+ res = sg_start_io(clp, rep);
+ if (1 == res) { /* ENOMEM, find what's available+try that */
+ if ((res = ioctl(clp->infd, SG_GET_RESERVED_SIZE, &buf_sz)) < 0) {
+ perror("RESERVED_SIZE ioctls failed");
+ return res;
+ }
+ clp->bpt = (buf_sz + clp->bs - 1) / clp->bs;
+ printf("Reducing blocks per transfer to %d\n", clp->bpt);
+ if (clp->bpt < 1)
+ return -ENOMEM;
+ res = sg_start_io(clp, rep);
+ if (1 == res)
+ res = -ENOMEM;
+ }
+ else if (res < 0) {
+ printf("sgq_dd inputting from sg failed, blk=%d\n", rep->blk);
+ rep->state = SGQ_IO_ERR;
+ return res;
+ }
+ }
+ else {
+ rep->state = SGQ_IO_STARTED;
+ while (((res = read(clp->infd, rep->buffp, blocks * clp->bs)) < 0) &&
+ (EINTR == errno))
+ ;
+ if (res < 0) {
+ sprintf(ebuff, "sgq_dd: reading, in_blk=%d ", rep->blk);
+ perror(ebuff);
+ rep->state = SGQ_IO_ERR;
+ return res;
+ }
+ if (res < blocks * clp->bs) {
+ int o_blocks = blocks;
+ rep->stop_after_wr = 1;
+ blocks = res / clp->bs;
+ if ((res % clp->bs) > 0) {
+ blocks++;
+ clp->in_partial++;
+ }
+ /* Reverse out + re-apply blocks on clp */
+ clp->in_blk -= o_blocks;
+ clp->in_count += o_blocks;
+ rep->num_blks = blocks;
+ clp->in_blk += blocks;
+ clp->in_count -= blocks;
+ }
+ clp->in_done_count -= blocks;
+ rep->state = SGQ_IO_FINISHED;
+ }
+ clp->rd_posp = rep->nextp;
+ return blocks;
+}
+
+int start_write(Rq_coll * clp)
+{
+ Rq_elem * rep = clp->wr_posp;
+ int res, blocks;
+ char ebuff[256];
+
+ while ((0 != rep->wr) || (SGQ_IO_FINISHED != rep->state)) {
+ rep = rep->nextp;
+ if (rep == clp->rd_posp)
+ return -1;
+ }
+#ifdef SG_DEBUG
+ printf("start_write, elem idx=%d\n", rep - clp->elem);
+#endif
+ rep->wr = 1;
+ blocks = rep->num_blks;
+ rep->blk = clp->out_blk;
+ clp->out_blk += blocks;
+ clp->out_count -= blocks;
+ if (clp->out_is_sg) {
+ res = sg_start_io(clp, rep);
+ if (1 == res) /* ENOMEM, give up */
+ return -ENOMEM;
+ else if (res < 0) {
+ printf("sgq_dd output to sg failed, blk=%d\n", rep->blk);
+ rep->state = SGQ_IO_ERR;
+ return res;
+ }
+ }
+ else {
+ rep->state = SGQ_IO_STARTED;
+ while (((res = write(clp->outfd, rep->buffp,
+ rep->num_blks * clp->bs)) < 0) && (EINTR == errno))
+ ;
+ if (res < 0) {
+ sprintf(ebuff, "sgq_dd: output, out_blk=%d ", rep->blk);
+ perror(ebuff);
+ rep->state = SGQ_IO_ERR;
+ return res;
+ }
+ if (res < blocks * clp->bs) {
+ blocks = res / clp->bs;
+ if ((res % clp->bs) > 0) {
+ blocks++;
+ clp->out_partial++;
+ }
+ rep->num_blks = blocks;
+ }
+ rep->state = SGQ_IO_FINISHED;
+ }
+ return blocks;
+}
+
+int do_poll(int fd)
+{
+ struct pollfd a_pollfd = {0, POLLIN | POLLOUT, 0};
+
+ a_pollfd.fd = fd;
+ if (poll(&a_pollfd, 1, 0) < 0) {
+ perror("poll error");
+ return 0;
+ }
+ /* printf("do_poll: revents=0x%x\n", (int)a_pollfd.revents); */
+ return (a_pollfd.revents & POLLIN) ? 1 : 0;
+}
+
+int can_read_write(Rq_coll * clp)
+{
+ Rq_elem * rep = NULL;
+ int res = 0;
+ int reading = 0;
+ int writing = 0;
+ int writeable = 0;
+ int rd_waiting = 0;
+ int wr_waiting = 0;
+ int sg_finished = 0;
+
+ /* if write completion pending, then complete it + start read */
+ if (clp->out_is_sg) {
+ while ((res = do_poll(clp->outfd))) {
+ if (res < 0)
+ return res;
+ res = sg_finish_io(clp, 1, &rep);
+ if (res < 0)
+ return res;
+ else if (1 == res) {
+ res = sg_start_io(clp, rep);
+ if (0 != res)
+ return -1; /* give up if any problems with retry */
+ }
+ else
+ sg_finished++;
+ }
+ while ((rep = clp->wr_posp) && (SGQ_IO_FINISHED == rep->state) &&
+ (1 == rep->wr) && (rep != clp->rd_posp)) {
+ rep->state = SGQ_FREE;
+ clp->out_done_count -= rep->num_blks;
+ clp->wr_posp = rep->nextp;
+ if (rep->stop_after_wr)
+ return -1;
+ }
+ }
+ else if ((rep = clp->wr_posp) && (1 == rep->wr) &&
+ (SGQ_IO_FINISHED == rep->state)) {
+ rep->state = SGQ_FREE;
+ clp->out_done_count -= rep->num_blks;
+ clp->wr_posp = rep->nextp;
+ if (rep->stop_after_wr)
+ return -1;
+ }
+
+ /* if read completion pending, then complete it + start maybe write */
+ if (clp->in_is_sg) {
+ while ((res = do_poll(clp->infd))) {
+ if (res < 0)
+ return res;
+ res = sg_finish_io(clp, 0, &rep);
+ if (res < 0)
+ return res;
+ if (1 == res) {
+ res = sg_start_io(clp, rep);
+ if (0 != res)
+ return -1; /* give up if any problems with retry */
+ }
+ else {
+ sg_finished++;
+ clp->in_done_count -= rep->num_blks;
+ }
+ }
+ }
+
+ for (rep = clp->wr_posp, res = 1;
+ rep != clp->rd_posp; rep = rep->nextp) {
+ if (SGQ_IO_STARTED == rep->state) {
+ if (rep->wr)
+ ++writing;
+ else {
+ res = 0;
+ ++reading;
+ }
+ }
+ else if ((0 == rep->wr) && (SGQ_IO_FINISHED == rep->state)) {
+ if (res)
+ writeable = 1;
+ }
+ else if (SGQ_IO_WAIT == rep->state) {
+ res = 0;
+ if (rep->wr)
+ ++wr_waiting;
+ else
+ ++rd_waiting;
+ }
+ else
+ res = 0;
+ }
+ if (clp->debug) {
+ if ((clp->debug >= 9) || wr_waiting || rd_waiting)
+ printf("%d/%d (nwb/nrb): read=%d/%d (do/wt) "
+ "write=%d/%d (do/wt) writeable=%d sg_fin=%d\n",
+ clp->out_blk, clp->in_blk, reading, rd_waiting,
+ writing, wr_waiting, writeable, sg_finished);
+ }
+ if (writeable && (writing < SGQ_MAX_WR_AHEAD) && (clp->out_count > 0))
+ return SGQ_CAN_WRITE;
+ if ((reading < SGQ_MAX_RD_AHEAD) && (clp->in_count > 0) &&
+ (0 == rd_waiting) && (clp->rd_posp->nextp != clp->wr_posp))
+ return SGQ_CAN_READ;
+
+ if (clp->out_done_count <= 0)
+ return SGQ_CAN_DO_NOTHING;
+
+ usleep(3000); /* hang about for 10 milliseconds */
+ /* Now check the _whole_ buffer for pending requests */
+ for (rep = clp->rd_posp->nextp; rep != clp->rd_posp; rep = rep->nextp) {
+ if (SGQ_IO_WAIT == rep->state) {
+ res = sg_start_io(clp, rep);
+ if (res < 0)
+ return res;
+ if (res > 0)
+ return -1;
+ break;
+ }
+ }
+ return SGQ_CAN_DO_NOTHING;
+}
+
+
+int main(int argc, char * argv[])
+{
+ int skip = 0;
+ int seek = 0;
+ int ibs = 0;
+ int obs = 0;
+ int count = -1;
+ char str[512];
+ char * key;
+ char * buf;
+ char inf[512];
+ char outf[512];
+ int res, k;
+ int in_num_sect = 0;
+ int out_num_sect = 0;
+ int in_sect_sz, out_sect_sz, crw;
+ char ebuff[256];
+ Rq_coll rcoll;
+
+ memset(&rcoll, 0, sizeof(Rq_coll));
+ rcoll.bpt = DEF_BLOCKS_PER_TRANSFER;
+ inf[0] = '\0';
+ outf[0] = '\0';
+ if (argc < 2) {
+ usage();
+ return 1;
+ }
+
+ for(k = 1; k < argc; k++) {
+ if (argv[k])
+ strcpy(str, argv[k]);
+ else
+ continue;
+ for(key = str, buf = key; *buf && *buf != '=';)
+ buf++;
+ if (*buf)
+ *buf++ = '\0';
+ if (strcmp(key,"if") == 0)
+ strcpy(inf, buf);
+ else if (strcmp(key,"of") == 0)
+ strcpy(outf, buf);
+ else if (0 == strcmp(key,"ibs"))
+ ibs = get_num(buf);
+ else if (0 == strcmp(key,"obs"))
+ obs = get_num(buf);
+ else if (0 == strcmp(key,"bs"))
+ rcoll.bs = get_num(buf);
+ else if (0 == strcmp(key,"bpt"))
+ rcoll.bpt = get_num(buf);
+ else if (0 == strcmp(key,"skip"))
+ skip = get_num(buf);
+ else if (0 == strcmp(key,"seek"))
+ seek = get_num(buf);
+ else if (0 == strcmp(key,"count"))
+ count = get_num(buf);
+ else if (0 == strcmp(key,"dio"))
+ rcoll.dio = get_num(buf);
+ else if (0 == strcmp(key,"deb"))
+ rcoll.debug = get_num(buf);
+ else {
+ printf("Unrecognized argument '%s'\n", key);
+ usage();
+ return 1;
+ }
+ }
+ if (rcoll.bs <= 0) {
+ rcoll.bs = DEF_BLOCK_SIZE;
+ printf("Assume default 'bs' (block size) of %d bytes\n", rcoll.bs);
+ }
+ if ((ibs && (ibs != rcoll.bs)) || (obs && (obs != rcoll.bs))) {
+ printf("If 'ibs' or 'obs' given must be same as 'bs'\n");
+ usage();
+ return 1;
+ }
+ if ((skip < 0) || (seek < 0)) {
+ printf("skip and seek cannot be negative\n");
+ return 1;
+ }
+#ifdef SG_DEBUG
+ printf("sgq_dd: if=%s skip=%d of=%s seek=%d count=%d\n",
+ inf, skip, outf, seek, count);
+#endif
+ rcoll.infd = STDIN_FILENO;
+ rcoll.outfd = STDOUT_FILENO;
+ if (inf[0] && ('-' != inf[0])) {
+ if ((rcoll.infd = open(inf, O_RDONLY)) < 0) {
+ sprintf(ebuff, "sgq_dd: could not open %s for reading", inf);
+ perror(ebuff);
+ return 1;
+ }
+ if (ioctl(rcoll.infd, SG_GET_TIMEOUT, 0) < 0) {
+ rcoll.in_is_sg = 0;
+ if (skip > 0) {
+ off_t offset = skip;
+
+ offset *= rcoll.bs; /* could overflow here! */
+ if (lseek(rcoll.infd, offset, SEEK_SET) < 0) {
+ sprintf(ebuff,
+ "sgq_dd: couldn't skip to required position on %s", inf);
+ perror(ebuff);
+ return 1;
+ }
+ }
+ }
+ else { /* looks like sg device so close then re-open it RW */
+ close(rcoll.infd);
+ if ((rcoll.infd = open(inf, O_RDWR | O_NONBLOCK)) < 0) {
+ printf("If %s is a sg device, need read+write permissions,"
+ " even to read it!\n", inf);
+ return 1;
+ }
+ rcoll.in_is_sg = 1;
+ if (sz_reserve(rcoll.infd, rcoll.bs, rcoll.bpt))
+ return 1;
+ }
+ }
+ if (outf[0] && ('-' != outf[0])) {
+ if ((rcoll.outfd = open(outf, O_RDWR | O_NONBLOCK)) >= 0) {
+ if (ioctl(rcoll.outfd, SG_GET_TIMEOUT, 0) < 0) {
+ /* not a scsi generic device so now try and open RDONLY */
+ close(rcoll.outfd);
+ }
+ else {
+ rcoll.out_is_sg = 1;
+ if (sz_reserve(rcoll.outfd, rcoll.bs, rcoll.bpt))
+ return 1;
+ }
+ }
+ if (! rcoll.out_is_sg) {
+ if ((rcoll.outfd = open(outf, O_WRONLY | O_CREAT, 0666)) < 0) {
+ sprintf(ebuff,
+ "sgq_dd: could not open %s for writing", outf);
+ perror(ebuff);
+ return 1;
+ }
+ else if (seek > 0) {
+ off_t offset = seek;
+
+ offset *= rcoll.bs; /* could overflow here! */
+ if (lseek(rcoll.outfd, offset, SEEK_SET) < 0) {
+ sprintf(ebuff,
+ "sgq_dd: couldn't seek to required position on %s", outf);
+ perror(ebuff);
+ return 1;
+ }
+ }
+ }
+ }
+ if ((STDIN_FILENO == rcoll.infd) && (STDOUT_FILENO == rcoll.outfd)) {
+ printf("Can't have both 'if' as stdin _and_ 'of' as stdout\n");
+ return 1;
+ }
+ if (! (rcoll.in_is_sg || rcoll.out_is_sg)) {
+ printf("Either 'if' or 'of' must be a scsi generic device\n");
+ return 1;
+ }
+ if (0 == count)
+ return 0;
+ else if (count < 0) {
+ if (rcoll.in_is_sg) {
+ res = read_capacity(rcoll.infd, &in_num_sect, &in_sect_sz);
+ if (2 == res) {
+ printf("Unit attention, media changed(in), try again\n");
+ res = read_capacity(rcoll.infd, &in_num_sect, &in_sect_sz);
+ }
+ if (0 != res) {
+ printf("Unable to read capacity on %s\n", inf);
+ in_num_sect = -1;
+ }
+ else {
+ if (in_num_sect > skip)
+ in_num_sect -= skip;
+ }
+ }
+ if (rcoll.out_is_sg) {
+ res = read_capacity(rcoll.outfd, &out_num_sect, &out_sect_sz);
+ if (2 == res) {
+ printf("Unit attention, media changed(out), try again\n");
+ res = read_capacity(rcoll.outfd, &out_num_sect, &out_sect_sz);
+ }
+ if (0 != res) {
+ printf("Unable to read capacity on %s\n", outf);
+ out_num_sect = -1;
+ }
+ else {
+ if (out_num_sect > seek)
+ out_num_sect -= seek;
+ }
+ }
+#ifdef SG_DEBUG
+ printf("Start of loop, count=%d, in_num_sect=%d, out_num_sect=%d\n",
+ count, in_num_sect, out_num_sect);
+#endif
+ if (in_num_sect > 0) {
+ if (out_num_sect > 0)
+ count = (in_num_sect > out_num_sect) ? out_num_sect :
+ in_num_sect;
+ else
+ count = in_num_sect;
+ }
+ else
+ count = out_num_sect;
+ }
+
+#ifdef SG_DEBUG
+ printf("Start of loop, count=%d, bpt=%d\n", count, rcoll.bpt);
+#endif
+
+ rcoll.in_count = count;
+ rcoll.in_done_count = count;
+ rcoll.in_blk = skip;
+ rcoll.out_count = count;
+ rcoll.out_done_count = count;
+ rcoll.out_blk = seek;
+ init_elems(&rcoll);
+
+/* vvvvvvvvvvvvvvvvv Main Loop vvvvvvvvvvvvvvvvvvvvvvvv */
+ while (rcoll.out_done_count > 0) {
+ crw = can_read_write(&rcoll);
+ if (crw < 0)
+ break;
+ if (SGQ_CAN_READ & crw) {
+ res = start_read(&rcoll);
+ if (res <= 0) {
+ printf("start_read: res=%d\n", res);
+ break;
+ }
+ }
+ if (SGQ_CAN_WRITE & crw) {
+ res = start_write(&rcoll);
+ if (res <= 0) {
+ printf("start_write: res=%d\n", res);
+ break;
+ }
+ }
+ }
+
+ if (STDIN_FILENO != rcoll.infd)
+ close(rcoll.infd);
+ if (STDOUT_FILENO != rcoll.outfd)
+ close(rcoll.outfd);
+ if (0 != rcoll.out_count) {
+ printf("Some error occurred, remaining blocks=%d\n", rcoll.out_count);
+ return 1;
+ }
+ printf("%d+%d records in\n", count - rcoll.in_done_count,
+ rcoll.in_partial);
+ printf("%d+%d records out\n", count - rcoll.out_done_count,
+ rcoll.out_partial);
+ if (rcoll.dio_incomplete)
+ printf(">> Direct IO requested but incomplete %d times\n",
+ rcoll.dio_incomplete);
+ if (rcoll.sum_of_resids)
+ printf(">> Non-zero sum of residual counts=%d\n",
+ rcoll.sum_of_resids);
+ return 0;
+}