diff options
author | Douglas Gilbert <dgilbert@interlog.com> | 2020-11-10 01:46:05 +0000 |
---|---|---|
committer | Douglas Gilbert <dgilbert@interlog.com> | 2020-11-10 01:46:05 +0000 |
commit | 6276daf9b89e1d27b91438ff1e92482a1d2e7179 (patch) | |
tree | 4803611223ef48163d2a0fb6cbf717639d992271 /testing | |
parent | 4ca8449c2826be469bc9558f2a037236c6703d64 (diff) | |
download | sg3_utils-6276daf9b89e1d27b91438ff1e92482a1d2e7179.tar.gz |
tweak transport error handling in Linux; sg_compare_and_write: add examples section to its manpage; testing
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@866 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'testing')
-rw-r--r-- | testing/Makefile | 12 | ||||
-rw-r--r-- | testing/sg_iovec_tst.c | 271 | ||||
-rw-r--r-- | testing/sg_iovec_tst.cpp | 594 | ||||
-rw-r--r-- | testing/sg_mrq_dd.cpp | 20 | ||||
-rw-r--r-- | testing/sgh_dd.cpp | 4 |
5 files changed, 616 insertions, 285 deletions
diff --git a/testing/Makefile b/testing/Makefile index 0a34ddec..8383cc5b 100644 --- a/testing/Makefile +++ b/testing/Makefile @@ -4,10 +4,10 @@ PREFIX=/usr/local INSTDIR=$(DESTDIR)/$(PREFIX)/bin MANDIR=$(DESTDIR)/$(PREFIX)/man -EXECS = sg_iovec_tst sg_sense_test sg_queue_tst bsg_queue_tst sg_chk_asc \ - sg_tst_nvme sg_tst_ioctl sg_tst_bidi tst_sg_lib sgs_dd sg_tst_excl \ +EXECS = sg_sense_test sg_queue_tst bsg_queue_tst sg_chk_asc sg_tst_nvme \ + sg_tst_ioctl sg_tst_bidi tst_sg_lib sgs_dd sg_tst_excl \ sg_tst_excl2 sg_tst_excl3 sg_tst_context sg_tst_async sgh_dd \ - sg_mrq_dd + sg_mrq_dd sg_iovec_tst EXTRAS = @@ -62,9 +62,6 @@ depend dep: clean: /bin/rm -f *.o $(EXECS) $(EXTRAS) $(BSG_EXTRAS) core .depend -sg_iovec_tst: sg_iovec_tst.o $(LIBFILESOLD) - $(LD) -o $@ $(LDFLAGS) $^ - sg_sense_test: sg_sense_test.o $(LIBFILESOLD) $(LD) -o $@ $(LDFLAGS) $^ @@ -114,6 +111,9 @@ sgh_dd: sgh_dd.o $(LIBFILESNEW) sg_mrq_dd: sg_mrq_dd.o sg_scat_gath.o $(LIBFILESNEW) $(CXXLD) -o $@ $(LDFLAGS) -pthread -latomic $^ +sg_iovec_tst: sg_iovec_tst.o sg_scat_gath.o $(LIBFILESNEW) + $(CXXLD) -o $@ $(LDFLAGS) -pthread -latomic $^ + install: $(EXECS) install -d $(INSTDIR) diff --git a/testing/sg_iovec_tst.c b/testing/sg_iovec_tst.c deleted file mode 100644 index f95dc67f..00000000 --- a/testing/sg_iovec_tst.c +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (C) 2003-2019 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. - * - * SPDX-License-Identifier: GPL-2.0-or-later - * - * Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg") - * device driver. - * This program will read a certain number of blocks of a given block size - * from a given sg device node and write what is retrieved out to a - * normal file. The purpose is to test the sg_iovec mechanism within the - * sg_io_hdr structure. - * - * Version 0.17 (20181207) - */ - -#include <unistd.h> -#include <signal.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <poll.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/stat.h> - -#include "sg_lib.h" -#include "sg_io_linux.h" -#include "sg_unaligned.h" - - - -#define ME "sg_iovec_tst: " - -#define A_PRIME 509 -#define IOVEC_ELEMS 2048 - -#define SENSE_BUFF_LEN 32 -#define DEF_TIMEOUT 40000 /* 40,000 milliseconds */ - -struct sg_iovec iovec[IOVEC_ELEMS]; - - -static void -usage(void) -{ - printf("Usage: sg_iovec_tst [-a] [-b=bs] -c=num [-e=es] [-h]\n" - " <generic_device> <output_filename>\n"); - printf(" where: -a async sg use (def: use ioctl(SGIO) )\n"); - printf(" -b=bs block size (default 512 Bytes)\n"); - printf(" -c=num count of blocks to transfer\n"); - printf(" -e=es iovec element size (def: 509)\n"); - printf(" -h this usage message\n"); - printf(" reads from <generic_device> and sends to " - "<output_filename>\nUses iovec (a scatter list) in linear " - "mode\n"); -} - -/* Returns 0 if everything ok */ -static int sg_read(int sg_fd, uint8_t * buff, int num_blocks, - int from_block, int bs, int elem_size, int async) -{ - uint8_t rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - uint8_t senseBuff[SENSE_BUFF_LEN]; - struct sg_io_hdr io_hdr; - struct pollfd a_poll; - int dxfer_len = bs * num_blocks; - int k, pos, rem, res; - - sg_put_unaligned_be32((uint32_t)from_block, rdCmd + 2); - sg_put_unaligned_be16((uint16_t)from_block, rdCmd + 7); - - for (k = 0, pos = 0, rem = dxfer_len; k < IOVEC_ELEMS; ++k) { - iovec[k].iov_base = buff + pos; - iovec[k].iov_len = (rem > elem_size) ? elem_size : rem; - if (rem <= elem_size) - break; - pos += elem_size; - rem -= elem_size; - } - if (k >= IOVEC_ELEMS) { - fprintf(stderr, "Can't fit dxfer_len=%d bytes in iovec\n", dxfer_len); - return -1; - } - memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); - io_hdr.interface_id = 'S'; - io_hdr.cmd_len = sizeof(rdCmd); - io_hdr.cmdp = rdCmd; - io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; - io_hdr.dxfer_len = dxfer_len; - io_hdr.iovec_count = k + 1; - io_hdr.dxferp = iovec; - io_hdr.mx_sb_len = SENSE_BUFF_LEN; - io_hdr.sbp = senseBuff; - io_hdr.timeout = DEF_TIMEOUT; - io_hdr.pack_id = from_block; - - if (async) { - res = write(sg_fd, &io_hdr, sizeof(io_hdr)); - if (res < 0) { - perror("write(<sg_device>), error"); - return -1; - } else if (res < (int)sizeof(io_hdr)) { - fprintf(stderr, "write(<sg_device>) returned %d, expected %d\n", - res, (int)sizeof(io_hdr)); - return -1; - } - a_poll.fd = sg_fd; - a_poll.events = POLLIN; - a_poll.revents = 0; - res = poll(&a_poll, 1, 2000 /* millisecs */ ); - if (res < 0) { - perror("poll error on <sg_device>"); - return -1; - } - if (0 == (POLLIN & a_poll.revents)) { - fprintf(stderr, "strange, poll() completed without data to " - "read\n"); - return -1; - } - res = read(sg_fd, &io_hdr, sizeof(io_hdr)); - if (res < 0) { - perror("read(<sg_device>), error"); - return -1; - } else if (res < (int)sizeof(io_hdr)) { - fprintf(stderr, "read(<sg_device>) returned %d, expected %d\n", - res, (int)sizeof(io_hdr)); - return -1; - } - } else if (ioctl(sg_fd, SG_IO, &io_hdr)) { - perror("reading (SG_IO) on sg device, error"); - return -1; - } - switch (sg_err_category3(&io_hdr)) { - case SG_LIB_CAT_CLEAN: - break; - case SG_LIB_CAT_RECOVERED: - fprintf(stderr, "Recovered error while reading block=%d, num=%d\n", - from_block, num_blocks); - break; - case SG_LIB_CAT_UNIT_ATTENTION: - fprintf(stderr, "Unit attention\n"); - return -1; - default: - sg_chk_n_print3("reading", &io_hdr, 1); - return -1; - } - return 0; -} - - -int main(int argc, char * argv[]) -{ - int sg_fd, fd, res, j, m, dxfer_len; - unsigned int k, num; - int do_async = 0; - int do_help = 0; - int blk_size = 512; - int elem_size = A_PRIME; - int count = 0; - char * sg_file_name = 0; - char * out_file_name = 0; - uint8_t * buffp; - - for (j = 1; j < argc; ++j) { - if (0 == strcmp("-a", argv[j])) - do_async = 1; - else if (0 == strncmp("-b=", argv[j], 3)) { - m = 3; - num = sscanf(argv[j] + m, "%d", &blk_size); - if ((1 != num) || (blk_size <= 0)) { - printf("Couldn't decode number after '-b' switch\n"); - sg_file_name = 0; - break; - } - } else if (0 == strncmp("-c=", argv[j], 3)) { - m = 3; - num = sscanf(argv[j] + m, "%d", &count); - if (1 != num) { - printf("Couldn't decode number after '-c' switch\n"); - sg_file_name = 0; - break; - } - } else if (0 == strncmp("-e=", argv[j], 3)) { - m = 3; - num = sscanf(argv[j] + m, "%d", &elem_size); - if (1 != num) { - printf("Couldn't decode number after '-e' switch\n"); - sg_file_name = 0; - break; - } - } else if (0 == strcmp("-h", argv[j])) - do_help = 1; - else if (*argv[j] == '-') { - printf("Unrecognized switch: %s\n", argv[j]); - sg_file_name = 0; - break; - } else if (NULL == sg_file_name) - sg_file_name = argv[j]; - else - out_file_name = argv[j]; - } - if (do_help) { - usage(); - return 0; - } - if (NULL == sg_file_name) { - printf(">>> need sg node name (e.g. /dev/sg3)\n\n"); - usage(); - return 1; - } - if (NULL == out_file_name) { - printf(">>> need out filename (to place what is fetched by READ\n\n"); - usage(); - return 1; - } - if (0 == count) { - printf(">>> need count of blocks to READ\n\n"); - usage(); - return 1; - } - - if (do_async) - sg_fd = open(sg_file_name, O_RDWR); - else - sg_fd = open(sg_file_name, O_RDONLY); - if (sg_fd < 0) { - perror(ME "sg device node open error"); - return 1; - } - /* Don't worry, being very careful not to write to a none-sg file ... */ - res = ioctl(sg_fd, SG_GET_VERSION_NUM, &k); - if ((res < 0) || (k < 30000)) { - printf(ME "not a sg device, or driver prior to 3.x\n"); - return 1; - } - fd = open(out_file_name, O_WRONLY | O_CREAT, 0666); - if (fd < 0) { - perror(ME "output file open error"); - return 1; - } - dxfer_len = count * blk_size; - buffp = (uint8_t *)calloc(count, blk_size); - if (buffp) { - if (0 == sg_read(sg_fd, buffp, count, 0, blk_size, elem_size, - do_async)) { - if (write(fd, buffp, dxfer_len) < 0) - perror(ME "output write failed"); - } - free(buffp); - } else - fprintf(stderr, "user space calloc for %d bytes failed\n", - dxfer_len); - res = close(fd); - if (res < 0) { - perror(ME "output file close error"); - close(sg_fd); - return 1; - } - res = close(sg_fd); - if (res < 0) { - perror(ME "sg device close error"); - return 1; - } - return 0; -} diff --git a/testing/sg_iovec_tst.cpp b/testing/sg_iovec_tst.cpp new file mode 100644 index 00000000..3199dc8d --- /dev/null +++ b/testing/sg_iovec_tst.cpp @@ -0,0 +1,594 @@ +/* + * Copyright (C) 2003-2020 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. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg") + * device driver. + * This C++ program will read a certain number of blocks of a given block + * size from a given sg device node using struct sg_iovec and write what is + * retrieved out to a normal file. The purpose is to test the sg_iovec + * mechanism within the sg_io_hdr and sg_io_v4 structures. + * + * struct sg_iovec and struct iovec [in include/uapi/uio.h] are basically + * the same thing: a pointer followed by a length (of type size_t). If + * applied to a disk then the pointer will hold a LBA and 'length' will + * be a number of logical blocks (which usually cannot exceed 2**32-1 . + * + */ + +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <errno.h> +#include <poll.h> +#include <limits.h> +#include <time.h> +#define __STDC_FORMAT_MACROS 1 +#include <inttypes.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <linux/bsg.h> + +#ifndef HAVE_LINUX_SG_V4_HDR + +/* Kernel uapi header contain __user decorations on user space pointers + * to indicate they are unsafe in the kernel space. However glibc takes + * all those __user decorations out from headers in /usr/include/linux . + * So to stop compile errors when directly importing include/uapi/scsi/sg.h + * undef __user before doing that include. */ +#define __user + +/* Want to block the original sg.h header from also being included. That + * causes lots of multiple definition errors. This will only work if this + * header is included _before_ the original sg.h header. */ +#define _SCSI_GENERIC_H /* original kernel header guard */ +#define _SCSI_SG_H /* glibc header guard */ + +#include "uapi_sg.h" /* local copy of include/uapi/scsi/sg.h */ + +#else +#define __user +#endif /* end of: ifndef HAVE_LINUX_SG_V4_HDR */ + +#include "sg_lib.h" +#include "sg_io_linux.h" +#include "sg_unaligned.h" + +// C++ local header +#include "sg_scat_gath.h" + +static const char * version_str = "1.07 20201108"; + +#define ME "sg_iovec_tst: " + +#define IOVEC_ELEMS 1024 /* match current UIO_MAXIOV in <linux/uio.h> */ + +#define DEF_BLK_SZ 512 +#define SENSE_BUFF_LEN 32 +#define DEF_TIMEOUT 40000 /* 40,000 milliseconds */ + +static struct sg_iovec iovec[IOVEC_ELEMS]; + +static int verbose; + +static struct option long_options[] = { + {"async", no_argument, 0, 'a'}, + {"bs", required_argument, 0, 'b'}, + {"elem_size", required_argument, 0, 'e'}, + {"elem-size", required_argument, 0, 'e'}, + {"elemsz", required_argument, 0, 'e'}, + {"fill", required_argument, 0, 'f'}, + {"from_skip", no_argument, 0, 'F'}, + {"from-skip", no_argument, 0, 'F'}, + {"help", no_argument, 0, 'h'}, + {"num", required_argument, 0, 'n'}, + {"num_blks", required_argument, 0, 'n'}, + {"num-blks", required_argument, 0, 'n'}, + {"sgl", required_argument, 0, 'S'}, + {"sgv4", no_argument, 0, '4'}, + {"skip", required_argument, 0, 's'}, + {"verbose", no_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {0, 0, 0, 0}, +}; + + +static void +usage(void) +{ + printf("Usage: sg_iovec_tst [--async] [--bs=BS] [--elem_sz=ES] " + "[--fill=F_ELEMS]\n" + " [from_skip] [--help] --num=NUM [--sgl=SFN] " + "[--sgv4]\n" + " [--skip=SKIP] [--verbose] [--version] " + "SG_DEV OUT_F\n"); + printf("where:\n" + " --async|-a async sg use (def: use ioctl(SGIO) )\n"); + printf(" --bs=BS|-b BS logical block size of SG_DEV (def: 512 " + "bytes)\n"); + printf(" --elem_sz=ES|-e ES iovec element size (def: BS bytes)\n"); + printf(" --fill=F_ELEMS|-f F_ELEMS append F_ELEMS*ES zero bytes " + "onto OUT_F\n" + " after each iovec element (def: " + "0)\n"); + printf(" --from_skip|-F sgl output starts from SKIP (def: 0)\n"); + printf(" --help|-h this usage message\n"); + printf(" --num=NUM|-n NUM number of blocks to read from SG_DEV\n"); + printf(" --sgl=SFN|-S SFN Sgl FileName (SFN) that written to, with " + "addresses\n" + " and lengths having ES as their unit\n"); + printf(" --sgv4|-4 use the sg v4 interface (def: v3 " + "interface)\n"); + printf(" --skip=SKIP|-s SKIP SKIP blocks before reading S_DEV " + "(def: 0)\n"); + printf(" --verbose|-v increase verbosity\n"); + printf(" --version|-V print version and exit\n\n"); + printf("Reads from SG_DEV and writes that data to OUT_F in binary. Uses " + "iovec\n(a scatter gather list) in linear mode (i.e. it cuts up " + "a contiguous\nbuffer). Example:\n" + " sg_iovec_tst -n 8k -e 4k /dev/sg3 out.bin\n"); +} + +/* Returns 0 if everything ok */ +static int +sg_read(int sg_fd, uint8_t * buff, int num_blocks, int from_block, int bs, + int elem_size, int async) +{ + uint8_t rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t senseBuff[SENSE_BUFF_LEN]; + struct sg_io_hdr io_hdr; + struct pollfd a_poll; + int dxfer_len = bs * num_blocks; + int k, pos, rem, res; + char b[128]; + + sg_put_unaligned_be32((uint32_t)from_block, rdCmd + 2); + sg_put_unaligned_be16((uint16_t)num_blocks, rdCmd + 7); + + for (k = 0, pos = 0, rem = dxfer_len; k < IOVEC_ELEMS; ++k) { + iovec[k].iov_base = buff + pos; + iovec[k].iov_len = (rem > elem_size) ? elem_size : rem; + if (rem <= elem_size) + break; + pos += elem_size; + rem -= elem_size; + } + if (k >= IOVEC_ELEMS) { + fprintf(stderr, "Can't fit dxfer_len=%d bytes in %d iovec elements " + "(would need %d)\n", dxfer_len, IOVEC_ELEMS, + dxfer_len / elem_size); + fprintf(stderr, "Try expanding elem_size which is currently %d " + "bytes\n", elem_size); + return -1; + } + memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof(rdCmd); + io_hdr.cmdp = rdCmd; + io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; + io_hdr.dxfer_len = dxfer_len; + io_hdr.iovec_count = k + 1; + io_hdr.dxferp = iovec; + io_hdr.mx_sb_len = SENSE_BUFF_LEN; + io_hdr.sbp = senseBuff; + io_hdr.timeout = DEF_TIMEOUT; + io_hdr.pack_id = from_block; + if (verbose) + fprintf(stderr, "cdb: %s\n", sg_get_command_str(rdCmd, 10, true, + sizeof(b), b)); + + if (async) { + res = write(sg_fd, &io_hdr, sizeof(io_hdr)); + if (res < 0) { + perror("write(<sg_device>), error"); + return -1; + } else if (res < (int)sizeof(io_hdr)) { + fprintf(stderr, "write(<sg_device>) returned %d, expected %d\n", + res, (int)sizeof(io_hdr)); + return -1; + } + a_poll.fd = sg_fd; + a_poll.events = POLLIN; + a_poll.revents = 0; + res = poll(&a_poll, 1, 2000 /* millisecs */ ); + if (res < 0) { + perror("poll error on <sg_device>"); + return -1; + } + if (0 == (POLLIN & a_poll.revents)) { + fprintf(stderr, "strange, poll() completed without data to " + "read\n"); + return -1; + } + res = read(sg_fd, &io_hdr, sizeof(io_hdr)); + if (res < 0) { + perror("read(<sg_device>), error"); + return -1; + } else if (res < (int)sizeof(io_hdr)) { + fprintf(stderr, "read(<sg_device>) returned %d, expected %d\n", + res, (int)sizeof(io_hdr)); + return -1; + } + } else if (ioctl(sg_fd, SG_IO, &io_hdr)) { + perror("reading (SG_IO) on sg device, error"); + return -1; + } + switch (sg_err_category3(&io_hdr)) { + case SG_LIB_CAT_CLEAN: + break; + case SG_LIB_CAT_RECOVERED: + fprintf(stderr, "Recovered error while reading block=%d, num=%d\n", + from_block, num_blocks); + break; + case SG_LIB_CAT_UNIT_ATTENTION: + fprintf(stderr, "Unit attention\n"); + return -1; + default: + sg_chk_n_print3("reading", &io_hdr, 1); + return -1; + } + return 0; +} + +/* Returns 0 if everything ok */ +static int +sg_read_v4(int sg_fd, uint8_t * buff, int num_blocks, int from_block, int bs, + int elem_size, int async) +{ + uint8_t rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t senseBuff[SENSE_BUFF_LEN]; + struct sg_io_v4 io_hdr; + struct pollfd a_poll; + int dxfer_len = bs * num_blocks; + int k, pos, rem, res; + char b[128]; + + sg_put_unaligned_be32((uint32_t)from_block, rdCmd + 2); + sg_put_unaligned_be16((uint16_t)num_blocks, rdCmd + 7); + + for (k = 0, pos = 0, rem = dxfer_len; k < IOVEC_ELEMS; ++k) { + iovec[k].iov_base = buff + pos; + iovec[k].iov_len = (rem > elem_size) ? elem_size : rem; + if (rem <= elem_size) + break; + pos += elem_size; + rem -= elem_size; + } + if (k >= IOVEC_ELEMS) { + fprintf(stderr, "Can't fit dxfer_len=%d bytes in %d iovec elements " + "(would need %d)\n", dxfer_len, IOVEC_ELEMS, + dxfer_len / elem_size); + fprintf(stderr, "Try expanding elem_size which is currently %d " + "bytes\n", elem_size); + return -1; + } + memset(&io_hdr, 0, sizeof(struct sg_io_v4)); + io_hdr.guard = 'Q'; + io_hdr.request_len = sizeof(rdCmd); + io_hdr.request = (uint64_t)(uintptr_t)rdCmd; + io_hdr.din_xfer_len = dxfer_len; + io_hdr.din_xferp = (uint64_t)(uintptr_t)iovec; + io_hdr.din_iovec_count = k + 1; + io_hdr.max_response_len = SG_DXFER_FROM_DEV; + io_hdr.response = (uint64_t)(uintptr_t)senseBuff; + io_hdr.timeout = DEF_TIMEOUT; + io_hdr.request_extra = from_block; /* pack_id */ + if (verbose) + fprintf(stderr, "cdb: %s\n", sg_get_command_str(rdCmd, 10, true, + sizeof(b), b)); + + if (async) { + res = ioctl(sg_fd, SG_IOSUBMIT, &io_hdr); + if (res < 0) { + perror("ioctl(SG_IOSUBMIT <sg_device>), error"); + return -1; + } + a_poll.fd = sg_fd; + a_poll.events = POLLIN; + a_poll.revents = 0; + res = poll(&a_poll, 1, 2000 /* millisecs */ ); + if (res < 0) { + perror("poll error on <sg_device>"); + return -1; + } + if (0 == (POLLIN & a_poll.revents)) { + fprintf(stderr, "strange, poll() completed without data to " + "read\n"); + return -1; + } + res = ioctl(sg_fd, SG_IORECEIVE, &io_hdr); + if (res < 0) { + perror("ioctl(SG_IORECEIVE <sg_device>), error"); + return -1; + } + } else if (ioctl(sg_fd, SG_IO, &io_hdr)) { + perror("ioctl(SG_IO) on sg device, error"); + return -1; + } + + res = sg_err_category_new(io_hdr.device_status, io_hdr.transport_status, + io_hdr.driver_status, + (const uint8_t *)(unsigned long)io_hdr.response, + io_hdr.response_len); + switch (res) { + case SG_LIB_CAT_CLEAN: + break; + case SG_LIB_CAT_RECOVERED: + fprintf(stderr, "Recovered error while reading block=%d, num=%d\n", + from_block, num_blocks); + break; + case SG_LIB_CAT_UNIT_ATTENTION: + fprintf(stderr, "Unit attention\n"); + return -1; + default: + sg_linux_sense_print("reading", io_hdr.device_status, + io_hdr.transport_status, io_hdr.driver_status, + senseBuff, io_hdr.response_len, true); + return -1; + } + return 0; +} + + +int +main(int argc, char * argv[]) +{ + bool do_sgv4 = false; + bool do_async = false; + bool do_help = false; + bool from_skip = false; + bool blk_size_given = false; + bool elem_size_given = false; + int sg_fd, fd, c, res, res2, err, dxfer_len; + unsigned int k; + int blk_size = DEF_BLK_SZ; + int elem_size = blk_size; + int num_blks = 0; + int f_elems = 0; + int64_t start_blk = 0; + char * sg_dev_name = 0; + char * out_file_name = 0; + char * sgl_fn = 0; + uint8_t * buffp; + uint8_t * fillp = NULL; + FILE * fp = NULL; + + while (1) { + int option_index = 0; + + c = getopt_long(argc, argv, "4ab:e:f:Fhn:s:S:vV", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case '4': + do_sgv4 = true; + break; + case 'a': + do_async = true; + break; + case 'b': + blk_size = sg_get_num(optarg); + if (blk_size < 1) { + printf("Couldn't decode positive number after '--bs=' " + "option\n"); + sg_dev_name = 0; + } else + blk_size_given = true; + break; + case 'e': + elem_size = sg_get_num(optarg); + if (elem_size < 1) { + printf("Couldn't decode positive number after '--elem_size=' " + "option\n"); + sg_dev_name = 0; + } else + elem_size_given = true; + break; + case 'f': + f_elems = sg_get_num(optarg); + if (f_elems < 0) { + printf("Couldn't decode number after '--fill=' option\n"); + sg_dev_name = 0; + } + break; + case 'F': + from_skip = true; + break; + case 'h': + do_help = true; + break; + case 'n': + num_blks = sg_get_num(optarg); + if (num_blks < 1) { + printf("Couldn't decode positive number after '--num=' " + "option\n"); + sg_dev_name = 0; + } + break; + case 's': + start_blk = sg_get_llnum(optarg); + if ((start_blk < 0) || (start_blk > INT_MAX)) { + printf("Couldn't decode number after '--skip=' option\n"); + sg_dev_name = 0; + } + break; + case 'S': + if (sgl_fn) { + printf("Looks like --sgl=SFN has been given twice\n"); + sg_dev_name = 0; + } else + sgl_fn = optarg; + break; + case 'v': + ++verbose; + break; + case 'V': + printf("Version: %s\n", version_str); + return 0; + default: + fprintf(stderr, "unrecognised option code 0x%x ??\n", c); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + } + if (optind < argc) { + if (NULL == sg_dev_name) { + sg_dev_name = argv[optind]; + ++optind; + } + if (optind < argc) { + if (sg_dev_name) { + out_file_name = argv[optind]; + ++optind; + } + if (optind < argc) { + for (; optind < argc; ++optind) + fprintf(stderr, "Unexpected extra argument: %s\n", + argv[optind]); + usage(); + return SG_LIB_SYNTAX_ERROR; + } + } + } + if (do_help) { + usage(); + return 0; + } + if (NULL == sg_dev_name) { + printf(">>> need sg node name (e.g. /dev/sg3)\n\n"); + usage(); + return 1; + } + if (NULL == out_file_name) { + printf(">>> need out filename (to place what is fetched by READ\n\n"); + usage(); + return 1; + } + if (0 == num_blks) { + printf(">>> need number of blocks to READ\n\n"); + usage(); + return 1; + } + + if ((! elem_size_given) && blk_size_given) + elem_size = blk_size; + + if (do_async) + sg_fd = open(sg_dev_name, O_RDWR); + else + sg_fd = open(sg_dev_name, O_RDONLY); + if (sg_fd < 0) { + perror(ME "sg device node open error"); + return 1; + } + /* Don't worry, being very careful not to write to a none-sg file ... */ + res = ioctl(sg_fd, SG_GET_VERSION_NUM, &k); + if ((res < 0) || (k < 30000)) { + printf(ME "not a sg device, or driver prior to 3.x\n"); + return 1; + } + fd = open(out_file_name, O_WRONLY | O_CREAT, 0666); + if (fd < 0) { + perror(ME "output file open error"); + return 1; + } + if (f_elems > 0) { + fillp = (uint8_t *)calloc(f_elems, elem_size); + if (NULL == fillp) { + fprintf(stderr, "fill calloc for %d bytes failed\n", + f_elems * elem_size); + goto fini; + } + } + if (sgl_fn) { + time_t t = time(NULL); + struct tm *tm = localtime(&t); + char s[128]; + + fp = fopen(sgl_fn, "w"); + if (NULL == fp) { + err = errno; + fprintf(stderr, "Unable to open %s, error: %s\n", sgl_fn, + strerror(err)); + res = sg_convert_errno(err); + goto fini; + } + strftime(s, sizeof(s), "%c", tm); + fprintf(fp, "# Scatter gather list generated by sg_iovec_tst " + "%s\n#\n", s); + } + + dxfer_len = num_blks * blk_size; + buffp = (uint8_t *)calloc(num_blks, blk_size); + if (buffp) { + int dx_len; + int64_t curr_blk = from_skip ? start_blk : 0; + + if (do_sgv4) { + if (sg_read(sg_fd, buffp, num_blks, (int)start_blk, blk_size, + elem_size, do_async)) + goto free_buff; + } else { + if (sg_read_v4(sg_fd, buffp, num_blks, (int)start_blk, blk_size, + elem_size, do_async)) + goto free_buff; + } + if (f_elems > 0) { + int fill_len = f_elems * elem_size; + + for (dx_len = 0; dx_len < dxfer_len; dx_len += elem_size) { + if (write(fd, buffp + dx_len, elem_size) < 0) { + perror(ME "partial dxfer output write failed"); + break; + } + if (sgl_fn) { + fprintf(fp, "%" PRId64 ",1\n", curr_blk); + curr_blk += f_elems + 1; + } + if (write(fd, fillp, fill_len) < 0) { + perror(ME "partial fill output write failed"); + break; + } + } + } else if (write(fd, buffp, dxfer_len) < 0) + perror(ME "full output write failed"); + else if (sgl_fn) { + for (dx_len = 0; dx_len < dxfer_len; dx_len += elem_size) + fprintf(fp, "%" PRId64 ",1\n", curr_blk++); + } +free_buff: + free(buffp); + } else + fprintf(stderr, "user space calloc for %d bytes failed\n", + dxfer_len); + res = close(fd); + if (res < 0) { + perror(ME "output file close error"); + close(sg_fd); + return 1; + } +fini: + res2 = close(sg_fd); + if (res2 < 0) { + err = errno; + perror(ME "sg device close error"); + if (0 == res) + res = sg_convert_errno(err); + } + if (fp) + fclose(fp); + return res; +} diff --git a/testing/sg_mrq_dd.cpp b/testing/sg_mrq_dd.cpp index f304eb51..27424a02 100644 --- a/testing/sg_mrq_dd.cpp +++ b/testing/sg_mrq_dd.cpp @@ -30,7 +30,7 @@ * */ -static const char * version_str = "1.15 20201012"; +static const char * version_str = "1.16 20201105"; #define _XOPEN_SOURCE 600 #ifndef _GNU_SOURCE @@ -241,6 +241,7 @@ struct global_collection /* one instance visible to all threads */ off_t outreg_st_size; atomic<int> dio_incomplete_count; atomic<int> sum_of_resids; + atomic<int> reason_res; int verbose; int dry_run; bool mrq_eq_0; /* true when user gives mrq=0 */ @@ -279,7 +280,6 @@ typedef struct request_element uint8_t sb[SENSE_BUFF_LEN]; int dio_incomplete_count; int mmap_active; - int resid; int rd_p_id; int rep_count; int rq_id; @@ -853,7 +853,7 @@ usage(int pg_num) " from dd it defaults to stdout). If 'of=.' " "uses /dev/null\n" " oflag comma separated list from: [append,nocreat,\n" - " <<list from iflag>>]\n" + " <<list from iflag>>]\n" " seek block position to start writing to OFILE\n" " skip block position to start reading from IFILE\n" " --help|-h output this usage message then exit\n" @@ -1212,7 +1212,7 @@ read_write_thread(struct global_collection * clp, int id, bool singleton) if (clp->in_flags.random) { ssize_t ssz; - ssz = getrandom(&rep->seed, sizeof(rep->seed), 0); + ssz = getrandom(&rep->seed, sizeof(rep->seed), GRND_NONBLOCK); if (ssz < (ssize_t)sizeof(rep->seed)) pr2serr_lk("[%d] %s: getrandom() failed, ret=%d\n", id, __func__, (int)ssz); @@ -1398,6 +1398,7 @@ fini: clp->out_rem_count -= rep->out_local_count; clp->in_partial += rep->in_local_partial; clp->out_partial += rep->out_local_partial; + clp->sum_of_resids += rep->in_resid_bytes; if (rep->alloc_bp) free(rep->alloc_bp); } @@ -1665,6 +1666,7 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p, int n_cmpl = ctl_v4p->info; int n_good = 0; int hole_count = 0; + int cat = 0; int vb = clp->verbose; int k, j, f1, slen, sstatus; char b[160]; @@ -1731,7 +1733,8 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p, if (ssh.response_code & 0x1) { ok = true; last_err_on_in = false; - } + } else + cat = sg_err_category_sense(sbp, slen); if (SPC_SK_MISCOMPARE == ssh.sense_key) ++num_miscompare; @@ -1739,7 +1742,8 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p, if (vb) lk_chk_n_print4(" >>", a_v4p, vb > 4); } - } + } else if (! ok) + cat = SG_LIB_CAT_OTHER; if (ok && f1) { ++n_good; if (a_v4p->dout_xfer_len >= (uint32_t)clp->bs) @@ -1775,6 +1779,8 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p, if (all_good) pr2serr_lk(" ... all good\n"); fini: + if (cat > 0) + clp->reason_res.store(cat); return n_good; } @@ -4016,5 +4022,7 @@ fini: (num_miscompare > 1) ? "s" : "", num_miscompare.load()); if (clp->verify && (SG_LIB_CAT_MISCOMPARE == res)) pr2serr("Verify/compare failed due to miscompare\n"); + if (0 == res) + res = clp->reason_res.load(); return (res >= 0) ? res : SG_LIB_CAT_OTHER; } diff --git a/testing/sgh_dd.cpp b/testing/sgh_dd.cpp index b7381f44..9a53b98a 100644 --- a/testing/sgh_dd.cpp +++ b/testing/sgh_dd.cpp @@ -36,7 +36,7 @@ * renamed [20181221] */ -static const char * version_str = "1.95 20201012"; +static const char * version_str = "1.96 20201019"; #define _XOPEN_SOURCE 600 #ifndef _GNU_SOURCE @@ -1200,7 +1200,7 @@ mrq_abort_thread(void * v_maip) Mrq_abort_info l_mai = *(Mrq_abort_info *)v_maip; struct sg_io_v4 ctl_v4; - ssz = getrandom(&rn, sizeof(rn), 0); + ssz = getrandom(&rn, sizeof(rn), GRND_NONBLOCK); if (ssz < (ssize_t)sizeof(rn)) pr2serr_lk("%s: getrandom() failed, returned %d\n", __func__, (int)ssz); |