diff options
author | Douglas Gilbert <dgilbert@interlog.com> | 2020-09-28 03:47:37 +0000 |
---|---|---|
committer | Douglas Gilbert <dgilbert@interlog.com> | 2020-09-28 03:47:37 +0000 |
commit | 20eb07694951b0760f741e7b74c0a363f8ed2b3b (patch) | |
tree | 59c16f7951bd6901001c8a460c67958a2151f8dc /testing | |
parent | a37cbfd6b346caf0af438f405a8e72fe69454005 (diff) | |
download | sg3_utils-20eb07694951b0760f741e7b74c0a363f8ed2b3b.tar.gz |
sg_dd: tweak unrecovered error reporting; testing sg*_dd work, new uapi_sg.h
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@863 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'testing')
-rw-r--r-- | testing/sg_mrq_dd.cpp | 138 | ||||
-rw-r--r-- | testing/sg_scat_gath.cpp | 15 | ||||
-rw-r--r-- | testing/sgh_dd.cpp | 111 | ||||
-rw-r--r-- | testing/sgs_dd.c | 98 | ||||
-rw-r--r-- | testing/uapi_sg.h | 13 |
5 files changed, 249 insertions, 126 deletions
diff --git a/testing/sg_mrq_dd.cpp b/testing/sg_mrq_dd.cpp index 5049b0b2..2d4515a4 100644 --- a/testing/sg_mrq_dd.cpp +++ b/testing/sg_mrq_dd.cpp @@ -30,7 +30,7 @@ * */ -static const char * version_str = "1.10 20200830"; +static const char * version_str = "1.13 20200927"; #define _XOPEN_SOURCE 600 #ifndef _GNU_SOURCE @@ -127,7 +127,7 @@ using namespace std; #define DEF_BLOCKS_PER_TRANSFER 128 #define DEF_BLOCKS_PER_2048TRANSFER 32 #define DEF_SCSI_CDB_SZ 10 -#define MAX_SCSI_CDB_SZ 16 +#define MAX_SCSI_CDB_SZ 16 /* could be 32 */ #define PACK_ID_TID_MULTIPLIER (0x1000000) /* 16,777,216 */ #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ @@ -310,7 +310,6 @@ struct sg_io_extra { #define MONO_MRQ_ID_INIT 0x10000 -typedef array<uint8_t, 32> big_cdb; /* allow up to a 32 byte cdb */ /* Use this class to wrap C++11 <random> features to produce uniform random @@ -331,6 +330,7 @@ private: static atomic<int> num_ebusy(0); static atomic<int> num_start_eagain(0); static atomic<int> num_fin_eagain(0); +static atomic<int> num_miscompare(0); static atomic<bool> vb_first_time(true); static sigset_t signal_set; @@ -360,7 +360,7 @@ static mutex strerr_mut; static bool have_sg_version = false; static int sg_version = 0; -static bool sg_version_ge_40030 = false; +static bool sg_version_ge_40045 = false; static atomic<bool> shutting_down{false}; static bool do_sync = false; static int do_time = 1; @@ -421,6 +421,13 @@ lk_chk_n_print4(const char * leadin, const struct sg_io_v4 * h4p, { lock_guard<mutex> lk(strerr_mut); + if (h4p->usr_ptr) { + const cdb_arr_t * cdbp = (const cdb_arr_t *)h4p->usr_ptr; + + pr2serr("Failed cdb: "); + sg_print_command(cdbp->data()); + } else + pr2serr("cdb: <null>\n"); sg_linux_sense_print(leadin, h4p->device_status, h4p->transport_status, h4p->driver_status, (const uint8_t *)h4p->response, h4p->response_len, raw_sinfo); @@ -813,16 +820,22 @@ usage(int pg_num) " [iflag=FLAGS]\n" " [obs=BS] [of=OFILE] [oflag=FLAGS] " "[seek=SEEK]\n" - " [skip=SKIP] [--help] [--version]\n\n"); + " [skip=SKIP] [--help] [--verify] " + "[--version]\n\n"); pr2serr(" [bpt=BPT] [cdbsz=6|10|12|16] [dio=0|1] " "[elemsz_kb=EKB]\n" " [fua=0|1|2|3] [mrq=MRQ] [no_waitq=0|1] " "[ofreg=OFREG]\n" " [sync=0|1] [thr=THR] [time=0|1|2] " "[verbose=VERB]\n" - " [--dry-run] [--verbose] [--verify] " + " [--dry-run] [--pre-fetch] [--verbose] " "[--version]\n\n" - " where the main options (shown in first group above) are:\n" + " where: operands have the form name=value and are pecular to " + "'dd'\n" + " style commands, and options start with one or " + "two hyphens;\n" + " the main operands and options (shown in first group " + "above) are:\n" " bs must be device logical block size (default " "512)\n" " count number of blocks to copy (def: device size)\n" @@ -831,10 +844,6 @@ usage(int pg_num) "direct,dpo,\n" " dsync,excl,fua,masync,mmap,nodur,\n" " null,order,qtail,serial,wq_excl]\n" - " mrq number of cmds placed in each sg call " - "(def: 16)\n" - " if mrq=0 does single, blocking ioctl(SG_IO)s " - "for all IO\n" " of file or device to write to (def: /dev/null " "N.B. different\n" " from dd it defaults to stdout). If 'of=.' " @@ -844,7 +853,6 @@ usage(int pg_num) " 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" - " --prefetch|-p with verify: do pre-fetch first\n" " --verify|-x do a verify (compare) operation [def: do a " "copy]\n" " --version|-V output version string then exit\n\n" @@ -860,10 +868,6 @@ usage(int pg_num) return; page2: pr2serr("Syntax: sg_mrq_dd [operands] [options]\n\n" - " where: operands have the form name=value and are pecular to " - "'dd'\n" - " style commands, and options start with one or " - "two hyphens;\n" " the lesser used operands and option are:\n\n" " bpt is blocks_per_transfer (default is 128)\n" " cdbsz size of SCSI READ, WRITE or VERIFY cdb_s " @@ -876,8 +880,16 @@ page2: " fua force unit access: 0->don't(def), 1->OFILE, " "2->IFILE,\n" " 3->OFILE+IFILE\n" + " ibs IFILE logical block size, cannot differ from " + "obs or bs\n" + " mrq number of cmds placed in each sg call " + "(def: 16)\n" + " if mrq=0 does one-by-one, blocking " + "ioctl(SG_IO)s\n" " no_waitq=0|1 poll for completion when 1; def: 0 (use " "wait queue)\n" + " obs OFILE logical block size, cannot differ from " + "ibs or bs\n" " ofreg OFREG is regular file or pipe to send what is " "read from\n" " IFILE in the first half of each shared element\n" @@ -888,8 +900,9 @@ page2: " time 0->no timing; 1/2->millisec/nanosec precision " "(def: 1)\n" " verbose increase verbosity (def: VERB=0)\n" - " --dry-run|-d prepare but bypass copy/read\n" - " --verbose|-v increase verbosity of utility\n\n" + " --dry-run|-d prepare but bypass copy/read\n" + " --prefetch|-p with verify: do pre-fetch first\n" + " --verbose|-v increase verbosity of utility\n\n" "Use '-hhh' or '-hhhh' for more information about flags.\n" ); return; @@ -915,8 +928,6 @@ page3: " masync set 'more async' flag on this sg device\n" " mmap setup mmap IO on IFILE or OFILE\n" " mmap,mmap when used twice, doesn't call munmap()\n" - " mrq_svb if mrq and sg->sg copy, do shared_variable_" - "blocking\n" " nodur turns off command duration calculations\n" " order require write ordering on sg->sg copy; only " "for oflag\n" @@ -970,7 +981,8 @@ page4: "For comparing IFILE with OFILE. Does repeated sequences of: " "READ(ifile)\nand uses data returned to send to VERIFY(ofile, " "BYTCHK=1). So the OFILE\ndevice/disk is doing the actual " - "comparison. Stops on first miscompare.\n\n"); + "comparison. Stops on first miscompare\nunless oflag=coe is " + "given\n\n"); pr2serr("--prefetch :\n" "Used with --verify option. Prepends a PRE-FETCH(ofile, IMMED) " "to verify\nsequence. This should speed the trailing VERIFY by " @@ -1247,7 +1259,7 @@ read_write_thread(struct global_collection * clp, int id, bool singleton) // share_and_ofreg = (rep->has_share && (rep->outregfd >= 0)); /* vvvvvvvvvvvvvv Main segment copy loop vvvvvvvvvvvvvvvvvvvvvvv */ - while (1) { + while (! shutting_down) { get_next_res gnr = clp->get_next(clp->mrq_num * clp->bpt); seg_blks = gnr.second; @@ -1309,9 +1321,14 @@ read_write_thread(struct global_collection * clp, int id, bool singleton) clp->infant_cv.notify_one(); singleton = false; } - if (rep->stop_after_write) + if (rep->stop_after_write) { + shutting_down = true; break; + } } /* ^^^^^^^^^^ end of main while loop which copies segments ^^^^^^ */ + + if (shutting_down) + goto fini; if (singleton) { { lock_guard<mutex> lk(clp->infant_mut); @@ -1644,10 +1661,9 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p, int n_good = 0; int hole_count = 0; int vb = clp->verbose; - int k, j, f1, slen, sstatus, blen; + int k, j, f1, slen, sstatus; char b[160]; - blen = sizeof(b); good_inblks = 0; good_outblks = 0; if (vb > 2) @@ -1707,34 +1723,30 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p, if (sg_scsi_normalize_sense(sbp, slen, &ssh) && (ssh.response_code >= 0x70)) { - char b[256]; - if (ssh.response_code & 0x1) { ok = true; last_err_on_in = false; } - if (vb) { - sg_get_sense_str(" ", sbp, slen, false, blen, b); - pr2serr_lk("[%d] a_v4[%d]:\n%s\n", id, k, b); - } + if (SPC_SK_MISCOMPARE == ssh.sense_key) + ++num_miscompare; + + pr2serr_lk("[%d] a_v4[%d]:\n", id, k); + if (vb) + lk_chk_n_print4(" >>", a_v4p, vb > 4); } } if (ok && f1) { ++n_good; - if (a_v4p->dout_xfer_len >= (uint32_t)clp->bs) { - if (a_v4p->dout_resid) - good_outblks += - (a_v4p->dout_xfer_len - a_v4p->dout_resid) / clp->bs; - else /* avoid division in common case of resid==0 */ - good_outblks += (uint32_t)a_v4p->usr_ptr; - } - if (a_v4p->din_xfer_len >= (uint32_t)clp->bs) { - if (a_v4p->din_resid) - good_inblks += (a_v4p->din_xfer_len - a_v4p->din_resid) / - clp->bs; - else - good_inblks += (uint32_t)a_v4p->usr_ptr; - } + if (a_v4p->dout_xfer_len >= (uint32_t)clp->bs) + good_outblks += (a_v4p->dout_xfer_len - a_v4p->dout_resid) / + clp->bs; + if (a_v4p->din_xfer_len >= (uint32_t)clp->bs) + good_inblks += (a_v4p->din_xfer_len - a_v4p->din_resid) / + clp->bs; + } + if (! ok) { + if ((a_v4p->dout_xfer_len > 0) || (! clp->in_flags.coe)) + rep->stop_after_write = true; } } /* end of request array scan loop */ if ((n_subm == num_mrq) || (vb < 3)) @@ -1815,6 +1827,7 @@ sg_half_segment_mrq0(Rq_elem * rep, scat_gath_iter & sg_it, bool is_wr, memset(t_v4p, 0, sizeof(*t_v4p)); t_v4p->guard = 'Q'; t_v4p->request = (uint64_t)t_cdb.data(); + t_v4p->usr_ptr = t_v4p->request; t_v4p->response = (uint64_t)rep->sb; t_v4p->flags = rflags; t_v4p->request_len = cdbsz; @@ -1826,7 +1839,6 @@ sg_half_segment_mrq0(Rq_elem * rep, scat_gath_iter & sg_it, bool is_wr, t_v4p->din_xferp = (uint64_t)(dp + (q_blks * clp->bs)); } t_v4p->timeout = DEF_TIMEOUT; - t_v4p->usr_ptr = num; /* pass number blocks requested */ t_v4p->request_extra = pack_id_base + ++rep->mrq_pack_id_off; mrq0_again: res = ioctl(fd, SG_IO, t_v4p); @@ -1932,6 +1944,7 @@ sg_half_segment(Rq_elem * rep, scat_gath_iter & sg_it, bool is_wr, t_v4p->guard = 'Q'; t_v4p->flags = rflags; t_v4p->request_len = cdbsz; + t_v4p->usr_ptr = (uint64_t)&a_cdb[a_cdb.size() - 1]; if (is_wr) { t_v4p->dout_xfer_len = num * clp->bs; t_v4p->dout_xferp = (uint64_t)(dp + (mrq_q_blks * clp->bs)); @@ -1940,7 +1953,6 @@ sg_half_segment(Rq_elem * rep, scat_gath_iter & sg_it, bool is_wr, t_v4p->din_xferp = (uint64_t)(dp + (mrq_q_blks * clp->bs)); } t_v4p->timeout = DEF_TIMEOUT; - t_v4p->usr_ptr = num; /* pass number blocks requested */ mrq_q_blks += num; t_v4p->request_extra = mrq_pack_id_base + ++rep->mrq_pack_id_off; a_v4.push_back(t_v4); @@ -2338,12 +2350,12 @@ do_both_sg_segment_mrq0(Rq_elem * rep, scat_gath_iter & i_sg_it, memset(t_v4p, 0, sizeof(*t_v4p)); t_v4p->guard = 'Q'; t_v4p->request = (uint64_t)t_cdb.data(); + t_v4p->usr_ptr = t_v4p->request; t_v4p->response = (uint64_t)rep->sb; t_v4p->flags = iflags; t_v4p->request_len = cdbsz; t_v4p->din_xfer_len = num * clp->bs; t_v4p->timeout = DEF_TIMEOUT; - t_v4p->usr_ptr = num; /* pass number blocks requested */ t_v4p->request_extra = pack_id_base + ++rep->mrq_pack_id_off; mrq0_again: res = ioctl(rep->infd, SG_IO, t_v4p); @@ -2387,12 +2399,12 @@ mrq0_again: memset(t_v4p, 0, sizeof(*t_v4p)); t_v4p->guard = 'Q'; t_v4p->request = (uint64_t)t_cdb.data(); + t_v4p->usr_ptr = t_v4p->request; t_v4p->response = (uint64_t)rep->sb; t_v4p->flags = oflags; t_v4p->request_len = cdbsz; t_v4p->dout_xfer_len = num * clp->bs; t_v4p->timeout = DEF_TIMEOUT; - t_v4p->usr_ptr = num; /* pass number blocks requested */ t_v4p->request_extra = pack_id_base + ++rep->mrq_pack_id_off; mrq0_again2: res = ioctl(rep->outfd, SG_IO, t_v4p); @@ -2517,9 +2529,9 @@ do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it, t_v4p->guard = 'Q'; t_v4p->flags = iflags; t_v4p->request_len = cdbsz; + t_v4p->usr_ptr = (uint64_t)&a_cdb[a_cdb.size() - 1]; t_v4p->din_xfer_len = num * clp->bs; t_v4p->timeout = DEF_TIMEOUT; - t_v4p->usr_ptr = num; /* pass number blocks requested */ in_mrq_q_blks += num; t_v4p->request_extra = mrq_pack_id_base + ++rep->mrq_pack_id_off; a_v4.push_back(t_v4); @@ -2540,9 +2552,9 @@ do_both_sg_segment(Rq_elem * rep, scat_gath_iter & i_sg_it, t_v4p->guard = 'Q'; t_v4p->flags = oflags; t_v4p->request_len = cdbsz; + t_v4p->usr_ptr = (uint64_t)&a_cdb[a_cdb.size() - 1]; t_v4p->dout_xfer_len = num * clp->bs; t_v4p->timeout = DEF_TIMEOUT; - t_v4p->usr_ptr = num; /* pass number blocks requested */ out_mrq_q_blks += num; t_v4p->request_extra = mrq_pack_id_base + ++rep->mrq_pack_id_off; a_v4.push_back(t_v4); @@ -2779,6 +2791,17 @@ bypass: errno, strerror(errno)); } } + if (clp->no_waitq) { + memset(seip, 0, sizeof(*seip)); + seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS; + seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_NO_WAIT_POLL; + seip->ctl_flags |= SG_CTL_FLAGM_NO_WAIT_POLL; + if (ioctl(fd, SG_SET_GET_EXTENDED, seip) < 0) { + res = -1; + pr2serr_lk("ioctl(EXTENDED(NO_WAIT_POLL)) failed, errno=%d %s\n", + errno, strerror(errno)); + } + } if (clp->verbose) { t = 1; /* more info in /proc/scsi/sg/debug */ @@ -3058,6 +3081,10 @@ parse_cmdline_sanity(int argc, char * argv[], struct global_collection * clp, clp->cdbsz_in = sg_get_num(buf); clp->cdbsz_out = clp->cdbsz_in; clp->cdbsz_given = true; + } else if (0 == strcmp(key, "coe")) { + /* not documented, for compat with sgh_dd */ + clp->in_flags.coe = !! sg_get_num(buf); + clp->out_flags.coe = clp->in_flags.coe; } else if (0 == strcmp(key, "count")) { if (clp->count_given) { pr2serr("second 'count=' argument detected, only one " @@ -3607,10 +3634,10 @@ main(int argc, char * argv[]) outf[0] = '\0'; outregf[0] = '\0'; fetch_sg_version(); - if (sg_version >= 40030) - sg_version_ge_40030 = true; + if (sg_version >= 40045) + sg_version_ge_40045 = true; else { - pr2serr(">>> %srequires an sg driver version of 4.0.30 or later\n\n", + pr2serr(">>> %srequires an sg driver version of 4.0.45 or later\n\n", my_name); fail_after_cli = true; } @@ -3930,6 +3957,9 @@ fini: pr2serr("Number of finish EAGAINs: %d\n", num_fin_eagain.load()); if (clp->verbose && (num_ebusy > 0)) pr2serr("Number of EBUSYs: %d\n", num_ebusy.load()); + if (clp->verbose && (num_miscompare > 0)) + pr2serr("Number of miscompare%s: %d\n", + (num_miscompare > 1) ? "s" : "", num_miscompare.load()); if (clp->verify && (SG_LIB_CAT_MISCOMPARE == res)) pr2serr("Verify/compare failed due to miscompare\n"); return (res >= 0) ? res : SG_LIB_CAT_OTHER; diff --git a/testing/sg_scat_gath.cpp b/testing/sg_scat_gath.cpp index 832cbed1..91e9d37a 100644 --- a/testing/sg_scat_gath.cpp +++ b/testing/sg_scat_gath.cpp @@ -560,7 +560,7 @@ scat_gath_list::append_1or(int64_t extra_blks, int64_t start_lba) int64_t cnt = 0; class scat_gath_elem sge; - if ((extra_blks <= 0) || (start_lba < 0)) + if ((extra_blks <= 0) && (start_lba < 0)) return o_num; /* nothing to do */ if ((o_num > 0) && (! sum_hard)) { sge = sgl[o_num - 1]; /* assume sge.num==0 */ @@ -578,9 +578,16 @@ scat_gath_list::append_1or(int64_t extra_blks, int64_t start_lba) return o_num; } } - } else if (0 == o_num) + } else if (0 == o_num) { lowest_lba = start_lba; - + if (0 == extra_blks) { + sge.lba = start_lba; + sge.num = 0; + sgl.push_back(sge); + high_lba_p1 = sge.lba; + return sgl.size(); + } + } for ( ; cnt < extra_blks; cnt += max_nbs) { sge.lba = start_lba + cnt; if ((extra_blks - cnt) <= max_nbs) @@ -599,10 +606,12 @@ int scat_gath_list::append_1or(int64_t extra_blks) { int o_num = sgl.size(); + if (o_num < 1) return append_1or(extra_blks, 0); class scat_gath_elem sge = sgl[o_num - 1]; + return append_1or(extra_blks, sge.lba + sge.num); } diff --git a/testing/sgh_dd.cpp b/testing/sgh_dd.cpp index 8221eb25..82d9869d 100644 --- a/testing/sgh_dd.cpp +++ b/testing/sgh_dd.cpp @@ -36,7 +36,7 @@ * renamed [20181221] */ -static const char * version_str = "1.91 20200829"; +static const char * version_str = "1.94 20200927"; #define _XOPEN_SOURCE 600 #ifndef _GNU_SOURCE @@ -357,6 +357,7 @@ static atomic<int> num_abort_req(0); static atomic<int> num_abort_req_success(0); static atomic<int> num_mrq_abort_req(0); static atomic<int> num_mrq_abort_req_success(0); +static atomic<int> num_miscompare(0); static atomic<long> num_waiting_calls(0); static atomic<bool> vb_first_time(true); @@ -388,7 +389,7 @@ static pthread_mutex_t strerr_mut = PTHREAD_MUTEX_INITIALIZER; static bool have_sg_version = false; static int sg_version = 0; static bool sg_version_lt_4 = false; -static bool sg_version_ge_40030 = false; +static bool sg_version_ge_40045 = false; static bool shutting_down = false; static bool do_sync = false; static int do_time = 1; @@ -1296,7 +1297,7 @@ sg_unshare(int sg_fd, int id, bool vb_b) static void sg_noshare_enlarge(int sg_fd, bool vb_b) { - if (sg_version_ge_40030) { + if (sg_version_ge_40045) { struct sg_extended_info sei; struct sg_extended_info * seip; @@ -1507,7 +1508,7 @@ read_write_thread(void * v_tip) pr2serr_lk("thread=%d: using global sg OFILE2, fd=%d\n", rep->id, rep->out2fd); } - if (!sg_version_ge_40030) { + if (!sg_version_ge_40045) { if (vb > 4) pr2serr_lk("thread=%d: Skipping share because driver too old\n", rep->id); @@ -1515,7 +1516,7 @@ read_write_thread(void * v_tip) if (vb > 4) pr2serr_lk("thread=%d: Skipping IFILE share with OFILE due to " "noshare=1\n", rep->id); - } else if (sg_version_ge_40030 && in_is_sg && out_is_sg) + } else if (sg_version_ge_40045 && in_is_sg && out_is_sg) rep->has_share = sg_share_prepare(rep->outfd, rep->infd, rep->id, vb > 9); if (vb > 9) @@ -1689,7 +1690,7 @@ fini: rep->mmap_active = 0; } - if (sg_version_ge_40030) { + if (sg_version_ge_40045) { if (clp->noshare) { if ((clp->nmrqs > 0) && clp->unshare) sg_unshare(rep->infd, rep->id, vb > 9); @@ -2079,6 +2080,7 @@ sg_out_wr_cmd(Rq_elem * rep, mrq_arr_t & def_arr, bool is_wr2, bool prefetch) pthread_mutex_t * mutexp = is_wr2 ? &clp->out2_mutex : &clp->out_mutex; struct sg_io_extra xtr; struct sg_io_extra * xtrp = &xtr; + const char * wr_or_ver = clp->verify ? "verify" : "out"; memset(xtrp, 0, sizeof(*xtrp)); xtrp->is_wr2 = is_wr2; @@ -2093,8 +2095,8 @@ again: if (1 == res) err_exit(ENOMEM, "sg starting out command"); else if (res < 0) { - pr2serr_lk("%soutputting from sg failed, blk=%" PRId64 "\n", - my_name, rep->oblk); + pr2serr_lk("%ssg %s failed, blk=%" PRId64 "\n", + my_name, wr_or_ver, rep->oblk); status = pthread_mutex_unlock(mutexp); if (0 != status) err_exit(status, "unlock out_mutex"); stop_both(clp); @@ -2143,8 +2145,8 @@ split_upper: if (1 == res) err_exit(ENOMEM, "sg starting out command"); else if (res < 0) { - pr2serr_lk("%soutputting from sg failed, blk=%" PRId64 "\n", - my_name, rep->oblk); + pr2serr_lk("%ssg %s failed, blk=%" PRId64 "\n", my_name, + wr_or_ver, rep->oblk); status = pthread_mutex_unlock(mutexp); if (0 != status) err_exit(status, "unlock out_mutex"); stop_both(clp); @@ -2167,14 +2169,14 @@ split_upper: case SG_LIB_CAT_MEDIUM_HARD: if (0 == clp->out_flags.coe) { pr2serr_lk("error finishing sg %s command (medium)\n", - (clp->verify ? "verify" : "out")); + wr_or_ver); if (exit_status <= 0) exit_status = res; stop_both(clp); goto fini; } else - pr2serr_lk(">> ignored error for out blk=%" PRId64 " for %d " - "bytes\n", rep->oblk, nblks * clp->bs); + pr2serr_lk(">> ignored error for %s blk=%" PRId64 " for %d " + "bytes\n", wr_or_ver, rep->oblk, nblks * clp->bs); #if defined(__GNUC__) #if (__GNUC__ >= 7) __attribute__((fallthrough)); @@ -2195,9 +2197,12 @@ split_upper: if (0 != status) err_exit(status, "unlock out_mutex"); } goto fini; + case SG_LIB_CAT_MISCOMPARE: + ++num_miscompare; + // fall through default: - pr2serr_lk("error finishing sg %s command (%d)\n", - (clp->verify ? "verify" : "out"), res); + pr2serr_lk("error finishing sg %s command (%d)\n", wr_or_ver, + res); if (exit_status <= 0) exit_status = res; stop_both(clp); @@ -2311,20 +2316,12 @@ process_mrq_response(Rq_elem * rep, const struct sg_io_v4 * ctl_v4p, } if (ok && f1) { ++n_good; - if (a_v4p->dout_xfer_len >= (uint32_t)clp->bs) { - if (a_v4p->dout_resid) - good_outblks += - (a_v4p->dout_xfer_len - a_v4p->dout_resid) / clp->bs; - else /* avoid division in common case of resid==0 */ - good_outblks += (uint32_t)a_v4p->usr_ptr; - } - if (a_v4p->din_xfer_len >= (uint32_t)clp->bs) { - if (a_v4p->din_resid) - good_inblks += (a_v4p->din_xfer_len - a_v4p->din_resid) / - clp->bs; - else - good_inblks += (uint32_t)a_v4p->usr_ptr; - } + if (a_v4p->dout_xfer_len >= (uint32_t)clp->bs) + good_outblks += (a_v4p->dout_xfer_len - a_v4p->dout_resid) / + clp->bs; + if (a_v4p->din_xfer_len >= (uint32_t)clp->bs) + good_inblks += (a_v4p->din_xfer_len - a_v4p->din_resid) / + clp->bs; } } /* end of request array scan loop */ if ((n_subm == num_mrq) || (vb < 3)) @@ -3362,6 +3359,9 @@ sg_finish_io(bool wr, Rq_elem * rep, int pack_id, struct sg_io_extra *xtrp) if (clp->verbose > 3) lk_chk_n_print3(cp, hp, false); return res; + case SG_LIB_CAT_MISCOMPARE: + ++num_miscompare; + // fall through case SG_LIB_CAT_NOT_READY: default: { @@ -3428,6 +3428,9 @@ do_v4: if (clp->verbose > 3) lk_chk_n_print4(cp, h4p, false); return res; + case SG_LIB_CAT_MISCOMPARE: + ++num_miscompare; + // fall through case SG_LIB_CAT_NOT_READY: default: { @@ -3468,11 +3471,15 @@ do_v4: /* Returns reserved_buffer_size/mmap_size if success, else 0 for failure */ static int -sg_prepare_resbuf(int fd, int bs, int bpt, bool def_res, int elem_sz, - bool unit_nano, bool no_dur, bool masync, bool wq_excl, +sg_prepare_resbuf(int fd, bool is_in, struct global_collection *clp, uint8_t **mmpp) { static bool done = false; + bool def_res = is_in ? clp->in_flags.defres : clp->out_flags.defres; + bool no_dur = is_in ? clp->in_flags.no_dur : clp->out_flags.no_dur; + bool masync = is_in ? clp->in_flags.masync : clp->out_flags.masync; + bool wq_excl = is_in ? clp->in_flags.wq_excl : clp->out_flags.wq_excl; + bool no_waitq = is_in ? clp->in_flags.no_waitq : clp->out_flags.no_waitq; int res, t, num; uint8_t *mmp; struct sg_extended_info sei; @@ -3493,19 +3500,19 @@ sg_prepare_resbuf(int fd, int bs, int bpt, bool def_res, int elem_sz, } goto bypass; } - if (! sg_version_ge_40030) + if (! sg_version_ge_40045) goto bypass; - if (elem_sz >= 4096) { + if (clp->elem_sz >= 4096) { memset(seip, 0, sizeof(*seip)); seip->sei_rd_mask |= SG_SEIM_SGAT_ELEM_SZ; res = ioctl(fd, SG_SET_GET_EXTENDED, seip); if (res < 0) pr2serr_lk("sgh_dd: %s: SG_SET_GET_EXTENDED(SGAT_ELEM_SZ) rd " "error: %s\n", __func__, strerror(errno)); - if (elem_sz != (int)seip->sgat_elem_sz) { + if (clp->elem_sz != (int)seip->sgat_elem_sz) { memset(seip, 0, sizeof(*seip)); seip->sei_wr_mask |= SG_SEIM_SGAT_ELEM_SZ; - seip->sgat_elem_sz = elem_sz; + seip->sgat_elem_sz = clp->elem_sz; res = ioctl(fd, SG_SET_GET_EXTENDED, seip); if (res < 0) pr2serr_lk("sgh_dd: %s: SG_SET_GET_EXTENDED(SGAT_ELEM_SZ) " @@ -3534,7 +3541,7 @@ sg_prepare_resbuf(int fd, int bs, int bpt, bool def_res, int elem_sz, } bypass: if (! def_res) { - num = bs * bpt; + num = clp->bs * clp->bpt; res = ioctl(fd, SG_SET_RESERVED_SIZE, &num); if (res < 0) { perror("sgh_dd: SG_SET_RESERVED_SIZE error"); @@ -3570,7 +3577,7 @@ bypass: res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t); if (res < 0) perror("sgh_dd: SG_SET_FORCE_PACK_ID error"); - if (unit_nano && sg_version_ge_40030) { + if (clp->unit_nanosec && sg_version_ge_40045) { memset(seip, 0, sizeof(*seip)); seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS; seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_TIME_IN_NS; @@ -3581,6 +3588,17 @@ bypass: errno, strerror(errno)); } } + if (no_waitq && sg_version_ge_40045) { + memset(seip, 0, sizeof(*seip)); + seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS; + seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_NO_WAIT_POLL; + seip->ctl_flags |= SG_CTL_FLAGM_NO_WAIT_POLL; + if (ioctl(fd, SG_SET_GET_EXTENDED, seip) < 0) { + res = -1; + pr2serr_lk("ioctl(EXTENDED(NO_WAIT_POLL)) failed, errno=%d %s\n", + errno, strerror(errno)); + } + } t = 1; res = ioctl(fd, SG_SET_DEBUG, &t); /* more info in /proc/scsi/sg/debug */ if (res < 0) @@ -3652,6 +3670,8 @@ process_flags(const char * arg, struct flags_t * fp) fp->no_unshare = true; else if (0 == strcmp(cp, "no_waitq")) fp->no_waitq = true; + else if (0 == strcmp(cp, "no-waitq")) + fp->no_waitq = true; else if (0 == strcmp(cp, "nowaitq")) fp->no_waitq = true; else if (0 == strcmp(cp, "noxfer")) @@ -3722,10 +3742,7 @@ sg_in_open(struct global_collection *clp, const char *inf, uint8_t **mmpp, perror(ebuff); return -sg_convert_errno(err); } - n = sg_prepare_resbuf(fd, clp->bs, clp->bpt, clp->in_flags.defres, - clp->elem_sz, clp->unit_nanosec, - clp->in_flags.no_dur, clp->in_flags.masync, - clp->in_flags.wq_excl, mmpp); + n = sg_prepare_resbuf(fd, true, clp, mmpp); if (n <= 0) return -SG_LIB_FILE_ERROR; if (clp->noshare) @@ -3757,10 +3774,7 @@ sg_out_open(struct global_collection *clp, const char *outf, uint8_t **mmpp, perror(ebuff); return -sg_convert_errno(err); } - n = sg_prepare_resbuf(fd, clp->bs, clp->bpt, clp->out_flags.defres, - clp->elem_sz, clp->unit_nanosec, - clp->out_flags.no_dur, clp->out_flags.masync, - clp->out_flags.wq_excl, mmpp); + n = sg_prepare_resbuf(fd, false, clp, mmpp); if (n <= 0) return -SG_LIB_FILE_ERROR; if (clp->noshare) @@ -4205,8 +4219,8 @@ main(int argc, char * argv[]) if (sg_version > 40000) { clp->in_flags.v4 = true; clp->out_flags.v4 = true; - if (sg_version >= 40030) - sg_version_ge_40030 = true; + if (sg_version >= 40045) + sg_version_ge_40045 = true; } res = parse_cmdline_sanity(argc, argv, clp, inf, outf, out2f, outregf); @@ -4775,6 +4789,9 @@ fini: pr2serr("Number of successful MRQ Aborts: %d\n", num_mrq_abort_req_success.load()); } + if (clp->verbose && (num_miscompare > 0)) + pr2serr("Number of miscompare%s: %d\n", + (num_miscompare > 1) ? "s" : "", num_miscompare.load()); if (clp->verbose > 1) { if (clp->verbose > 3) pr2serr("Final pack_id=%d, mrq_id=%d\n", mono_pack_id.load(), diff --git a/testing/sgs_dd.c b/testing/sgs_dd.c index cca5330c..ccae39e0 100644 --- a/testing/sgs_dd.c +++ b/testing/sgs_dd.c @@ -1,7 +1,7 @@ /* * Test code for the extensions to the Linux OS SCSI generic ("sg") * device driver. - * Copyright (C) 1999-2019 D. Gilbert and P. Allworth + * Copyright (C) 1999-2020 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 @@ -83,7 +83,7 @@ #include "sg_unaligned.h" -static const char * version_str = "4.14 20200330"; +static const char * version_str = "4.15 20200916"; static const char * my_name = "sgs_dd"; #define DEF_BLOCK_SIZE 512 @@ -121,6 +121,7 @@ struct flags_t { bool immed; bool mmap; bool noxfer; + bool no_waitq; bool pack; bool tag; bool v3; @@ -169,6 +170,8 @@ typedef struct request_collection int bpt; int dio_incomplete; int sum_of_resids; + int poll_ms; + int pollerr_count; int debug; sigset_t blocked_sigs; int sigs_waiting; @@ -187,31 +190,54 @@ static bool sgs_nanosec_unit = false; static void -usage(void) +usage(int pg_num) { + if (pg_num > 1) + goto second_page; printf("Usage: " "sgs_dd [bpt=BPT] [bs=BS] [count=NUM] [deb=DEB] [if=IFILE]\n" " [iflag=FLAGS] [no_sig=0|1] [of=OFILE] " "[oflag=FLAGS]\n" - " [rt_sig=0|1] [seek=SEEK] [skip=SKIP] " - "[--version]\n" + " [poll_ms=MS] [rt_sig=0|1] [seek=SEEK] " + "[skip=SKIP]\n" + " [--help] [--version]\n" "where:\n" " bpt blocks_per_transfer (default: 65536/bs (or 128 for " "bs=512))\n" " bs must be the logical block size of device (def: 512)\n" " deb debug: 0->no debug (def); > 0 -> more debug\n" " iflag comma separated list from: dio,evfd,excl,immed,mmap," - "noxfer,\n" - " null,pack,tag,v3,v4 bound to IFILE\n" + "no_waitq,\n" + " noxfer,null,pack,tag,v3,v4 bound to IFILE\n" " no_sig 0-> use signals (def); 1-> no signals, hard polling " "instead\n" " oflag same flags as iflag but bound to OFILE\n" + " poll_ms number of milliseconds to wait on poll (def: 0)\n" " rt_sig 0->use SIGIO (def); 1->use RT sig (SIGRTMIN + 1)\n" " <other operands> as per dd command\n\n"); printf("dd clone for testing Linux sg driver SIGPOLL and/or polling. " "Either\nIFILE or OFILE must be a scsi generic device. If OFILE " "not given then\n/dev/null assumed (rather than stdout like " - "dd).\n"); + "dd). Use '-hh' or '-hhh'\nfor more information.\n"); + return; +second_page: + printf("flag description:\n" + " dio this driver's version of O_DIRECT\n" + " evfd when poll() gives POLLIN, use eventfd to find " + "out how many\n" + " excl open IFILE or OFILE with O_EXCL\n" + " immed use SGV4_FLAG_IMMED flag on each request\n" + " mmap use mmap()-ed IO on IFILE or OFILE\n" + " no_waitq use SGV4_FLAG_NO_WAIQ flag on each request\n" + " noxfer no transfer between user space and kernel IO " + "buffers\n" + " null does nothing, placeholder\n" + " pack submit with rising pack_id, complete matching " + "each pack_id\n" + " tag use tag (from block layer) rather than " + "pack_id\n" + " v3 use sg v3 interface (default)\n" + " v4 use sg vr interface (i.e. struct sg_io_v4)\n"); } /* Return of 0 -> success, -1 -> failure, 2 -> try again */ @@ -296,6 +322,8 @@ sg_start_io(Rq_coll * clp, Rq_elem * rep) hp->flags |= SG_FLAG_MMAP_IO; if (flagp->evfd) hp->flags |= SGV4_FLAG_EVENTFD; + if (flagp->no_waitq) + hp->flags |= SGV4_FLAG_NO_WAITQ; #ifdef SG_DEBUG pr2serr("%s: SCSI %s, blk=%d num_blks=%d\n", __func__, rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks); @@ -559,7 +587,7 @@ sz_reserve(Rq_coll * clp, bool is_in) if (vb) pr2serr("sgs_dd: warning: sg driver prior to 4.0.00\n"); sgs_old_sg_driver = true; - } else if (t < 40030) { + } else if (t < 40045) { sgs_old_sg_driver = false; sgs_full_v4_sg_driver = false; } else @@ -625,6 +653,20 @@ sz_reserve(Rq_coll * clp, bool is_in) return 1; } } + if (flagsp->no_waitq) { + memset(seip, 0, sizeof(*seip)); + seip->sei_wr_mask |= SG_SEIM_CTL_FLAGS; + seip->ctl_flags_wr_mask |= SG_CTL_FLAGM_NO_WAIT_POLL; + seip->ctl_flags |= SG_CTL_FLAGM_NO_WAIT_POLL; + if (ioctl(fd, SG_SET_GET_EXTENDED, seip) < 0) { + pr2serr("ioctl(EXTENDED(NO_WAIT_POLL)) failed, errno=%d %s\n", + errno, strerror(errno)); + return 1; + } + } + } else if (flagsp->no_waitq) { + pr2serr("need sg version >= 4.0.45 for no_waitq flag\n"); + return 1; } if (!clp->no_sig) { if (-1 == fcntl(fd, F_SETOWN, getpid())) { @@ -868,7 +910,7 @@ do_num_poll_in(Rq_coll * clp, int fd, bool is_evfd) } } a_pollfd.fd = fd; - if (poll(&a_pollfd, 1, 0) < 0) { + if (poll(&a_pollfd, 1, clp->poll_ms) < 0) { err = errno; pr2serr("%s: poll(): %s [%d]\n", __func__, strerror(err), err); return -err; @@ -887,8 +929,10 @@ do_num_poll_in(Rq_coll * clp, int fd, bool is_evfd) return (int)count; } else return 1; /* could be more but don't know without evfd */ - } else - return 0; + } else if (a_pollfd.revents & POLLERR) + ++clp->pollerr_count; + + return 0; } static int @@ -1055,6 +1099,9 @@ process_flags(const char * arg, struct flags_t * fp) fp->immed = true; else if (0 == strcmp(cp, "mmap")) fp->mmap = true; + else if ((0 == strcmp(cp, "no_waitq")) || + (0 == strcmp(cp, "no-waitq"))) + fp->no_waitq = true; else if (0 == strcmp(cp, "noxfer")) fp->noxfer = true; else if (0 == strcmp(cp, "null")) @@ -1088,6 +1135,7 @@ main(int argc, char * argv[]) int count = -1; int in_num_sect = 0; int out_num_sect = 0; + int help_pg = 0; int res, k, in_sect_sz, out_sect_sz, crw, open_fl; char str[STR_SZ]; char * key; @@ -1105,7 +1153,7 @@ main(int argc, char * argv[]) inf[0] = '\0'; outf[0] = '\0'; if (argc < 2) { - usage(); + usage(1); return 1; } sgs_nanosec_unit = !!getenv("SG3_UTILS_LINUX_NANO"); @@ -1151,7 +1199,9 @@ main(int argc, char * argv[]) pr2serr("%sbad argument to 'oflag='\n", my_name); return SG_LIB_SYNTAX_ERROR; } - } else if (0 == strcmp(key,"rt_sig")) + } else if (0 == strcmp(key,"poll_ms")) + clp->poll_ms = sg_get_num(buf); + else if (0 == strcmp(key,"rt_sig")) clp->use_rt_sig = !!sg_get_num(buf); else if (0 == strcmp(key,"seek")) seek = sg_get_num(buf); @@ -1168,9 +1218,17 @@ main(int argc, char * argv[]) clp->debug +=2; else if ((0 == strcmp(key,"-v")) || (0 == strcmp(key,"--verbose"))) ++clp->debug; + else if (0 == strcmp(key,"-hhhh")) + help_pg +=4; + else if (0 == strcmp(key,"-hhh")) + help_pg +=3; + else if (0 == strcmp(key,"-hh")) + help_pg +=2; + else if ((0 == strcmp(key,"-h")) || (0 == strcmp(key,"--help"))) + ++help_pg; else { pr2serr("Unrecognized argument '%s'\n", key); - usage(); + usage(help_pg); return 1; } } @@ -1179,9 +1237,14 @@ main(int argc, char * argv[]) } else bs_given = true; + if (help_pg > 0) { + usage(help_pg); + return 0; + } + if ((ibs && (ibs != clp->bs)) || (obs && (obs != clp->bs))) { pr2serr("If 'ibs' or 'obs' given must be same as 'bs'\n"); - usage(); + usage(1); return 1; } if (clp->bpt <= 0) { @@ -1420,6 +1483,9 @@ main(int argc, char * argv[]) if ((! clp->no_sig) && clp->debug > 0) pr2serr("SIGIO/SIGPOLL signals received: %d, RT sigs: %d\n", clp->sigs_io_received, clp->sigs_rt_received); + if (clp->pollerr_count > 0) + pr2serr(">> poll() system call gave POLLERR %d times\n", + clp->pollerr_count); remove_elems(clp); return res < 0 ? 99 : res; } diff --git a/testing/uapi_sg.h b/testing/uapi_sg.h index 2a7e6d9f..fc793dca 100644 --- a/testing/uapi_sg.h +++ b/testing/uapi_sg.h @@ -14,7 +14,7 @@ * Later extensions (versions 2, 3 and 4) to driver: * Copyright (C) 1998 - 2020 Douglas Gilbert * - * Version 4.0.44 (20200818) + * Version 4.0.45 (20200913) * This version is for Linux 4 and 5 series kernels. * * Documentation @@ -114,18 +114,18 @@ typedef struct sg_io_hdr { #define SGV4_FLAG_YIELD_TAG 0x8 /* sg_io_v4::generated_tag set after SG_IOS */ #define SGV4_FLAG_Q_AT_TAIL SG_FLAG_Q_AT_TAIL #define SGV4_FLAG_Q_AT_HEAD SG_FLAG_Q_AT_HEAD -#define SGV4_FLAG_NO_WAITQ 0x40 /* implies SGV4_FLAG_IMMED */ +#define SGV4_FLAG_NO_WAITQ 0x40 /* need to poll for completion */ #define SGV4_FLAG_DOUT_OFFSET 0x80 /* dout byte offset in v4::spare_in */ -#define SGV4_FLAG_COMPLETE_B4 0x100 +#define SGV4_FLAG_COMPLETE_B4 0x100 /* mrq: complete this rq before next */ #define SGV4_FLAG_SIGNAL 0x200 /* v3: ignored; v4 signal on completion */ -#define SGV4_FLAG_IMMED 0x400 /* for polling with SG_IOR, ignored in SG_IOS */ +#define SGV4_FLAG_IMMED 0x400 /* issue request and return immediately ... */ #define SGV4_FLAG_STOP_IF 0x800 /* Stops sync mrq if error or warning */ #define SGV4_FLAG_DEV_SCOPE 0x1000 /* permit SG_IOABORT to have wider scope */ #define SGV4_FLAG_SHARE 0x2000 /* share IO buffer; needs SG_SEIM_SHARE_FD */ #define SGV4_FLAG_DO_ON_OTHER 0x4000 /* available on either of shared pair */ #define SGV4_FLAG_KEEP_SHARE 0x8000 /* ... buffer for another dout command */ #define SGV4_FLAG_NO_DXFER SG_FLAG_NO_DXFER /* needed for sharing */ -#define SGV4_FLAG_MULTIPLE_REQS 0x20000 /* n sg_io_v4s in data-in */ +#define SGV4_FLAG_MULTIPLE_REQS 0x20000 /* 1 or more sg_io_v4-s in data-in */ #define SGV4_FLAG_EVENTFD 0x40000 /* signal completion on ... */ #define SGV4_FLAG_ORDERED_WR 0x80000 /* svb: issue in-order writes */ #define SGV4_FLAG_REC_ORDER 0x100000 /* receive order in v4:request_priority */ @@ -218,7 +218,8 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */ #define SG_CTL_FLAGM_EXCL_WAITQ 0x1000 /* only 1 wake up per response */ #define SG_CTL_FLAGM_SNAP_DEV 0x2000 /* output to debugfs::snapped */ #define SG_CTL_FLAGM_RM_EVENTFD 0x4000 /* only if new eventfd wanted */ -#define SG_CTL_FLAGM_ALL_BITS 0x7fff /* should be OR of previous items */ +#define SG_CTL_FLAGM_NO_WAIT_POLL 0x8000 /* POLLERR on poll(2)s that wait */ +#define SG_CTL_FLAGM_ALL_BITS 0xffff /* should be OR of previous items */ /* Write one of the following values to sg_extended_info::read_value, get... */ #define SG_SEIRV_INT_MASK 0x0 /* get SG_SEIM_ALL_BITS */ |