1 /*
2 * Copyright (c) 2018-2020 Douglas Gilbert.
3 * All rights reserved.
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the BSD_LICENSE file.
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 */
9
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdbool.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <errno.h>
18 #include <getopt.h>
19 #define __STDC_FORMAT_MACROS 1
20 #include <inttypes.h>
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
27 #include <time.h>
28 #elif defined(HAVE_GETTIMEOFDAY)
29 #include <time.h>
30 #include <sys/time.h>
31 #endif
32
33 #include "sg_lib.h"
34 #include "sg_lib_data.h"
35 #include "sg_cmds_basic.h"
36 #include "sg_cmds_extra.h"
37 #include "sg_unaligned.h"
38 #include "sg_pr2serr.h"
39
40 /*
41 * This program issues one or more SCSI SEEK(10), PRE-FETCH(10) or
42 * PRE-FETCH(16) commands. Both PRE-FETCH commands are current and appear
43 * in the most recent SBC-4 draft (sbc4r15.pdf at time of writing) while
44 * SEEK(10) has been obsolete since SBC-2 (2004). Currently more hard disks
45 * and SSDs support SEEK(10) than PRE-FETCH. It is even unclear what
46 * SEEK(10) means (defined in SBC-1 as moving the hard disk heads to the
47 * track containing the given LBA) for a SSD. But if the manufacturers'
48 * support it, then it must have a use, presumably to speed the next access
49 * to that LBA ...
50 */
51
52 static const char * version_str = "1.08 20200115";
53
54 #define BACKGROUND_CONTROL_SA 0x15
55
56 #define CMD_ABORT_TIMEOUT 60 /* 60 seconds */
57
58
59 static struct option long_options[] = {
60 {"10", no_argument, 0, 'T'},
61 {"count", required_argument, 0, 'c'},
62 {"grpnum", required_argument, 0, 'g'},
63 {"help", no_argument, 0, 'h'},
64 {"immed", no_argument, 0, 'i'},
65 {"lba", required_argument, 0, 'l'},
66 {"num-blocks", required_argument, 0, 'n'},
67 {"num_blocks", required_argument, 0, 'n'},
68 {"pre-fetch", no_argument, 0, 'p'},
69 {"pre_fetch", no_argument, 0, 'p'},
70 {"readonly", no_argument, 0, 'r'},
71 {"skip", required_argument, 0, 's'},
72 {"time", required_argument, 0, 't'},
73 {"verbose", no_argument, 0, 'v'},
74 {"version", no_argument, 0, 'V'},
75 {"wrap-offset", required_argument, 0, 'w'},
76 {"wrap_offset", required_argument, 0, 'w'},
77 {0, 0, 0, 0},
78 };
79
80
81 static void
usage()82 usage()
83 {
84 pr2serr("Usage: "
85 "sg_seek [--10] [--count=NC] [--grpnum=GN] [--help] [--immed]\n"
86 " [--lba=LBA] [--num-blocks=NUM] [--pre-fetch] "
87 "[--readonly]\n"
88 " [--skip=SB] [--time] [--verbose] [--version]\n"
89 " [--wrap-offset=WO] DEVICE\n");
90 pr2serr(" where:\n"
91 " --10|-T do PRE-FETCH(10) command (def: "
92 "SEEK(10), or\n"
93 " PRE-FETCH(16) if --pre-fetch also "
94 "given)\n"
95 " --count=NC|-c NC NC is number of commands to execute "
96 "(def: 1)\n"
97 " --grpnum=GN|-g GN GN is group number to place in "
98 "PRE-FETCH\n"
99 " cdb; 0 to 63 (def: 0)\n"
100 " --help|-h print out usage message\n"
101 " --immed|-i set IMMED bit in PRE-FETCH command\n"
102 " --lba=LBA|-l LBA starting Logical Block Address (LBA) "
103 "(def: 0)\n"
104 " --num-blocks=NUM|-n NUM number of blocks to cache (for "
105 "PRE-FETCH)\n"
106 " (def: 1). Ignored by "
107 "SEEK(10)\n");
108 pr2serr(" --pre-fetch|-p do PRE-FETCH command, 16 byte variant if "
109 "--10 not\n"
110 " given (def: do SEEK(10))\n"
111 " --readonly|-r open DEVICE read-only (if supported)\n"
112 " --skip=SB|-s SB when NC>1 skip SB blocks to next LBA "
113 "(def: 1)\n"
114 " --time|-t time the command(s) and if NC>1 show "
115 "usecs/command\n"
116 " (def: don't time)\n"
117 " --verbose|-v increase verbosity\n"
118 " --version|-V print version string and exit\n"
119 " --wrap-offset=WO|-w WO if SB>0 and WO>0 then if "
120 "LBAn>LBA+WO\n"
121 " then reset LBAn back to LBA (def: 0)\n\n"
122 "Performs SCSI SEEK(10), PRE-FETCH(10) or PRE-FETCH(16) "
123 "command(s).If no\noptions are given does one SEEK(10) command "
124 "with an LBA of 0 . If NC>1\nthen a tally is kept of successes, "
125 "'condition-met's and errors that is\nprinted on completion. "
126 "'condition-met' is from PRE-FETCH when NUM blocks\nfit in "
127 "the DEVICE's cache.\n"
128 );
129 }
130
131
132 int
main(int argc,char * argv[])133 main(int argc, char * argv[])
134 {
135 bool cdb10 = false;
136 bool count_given = false;
137 bool do_time = false;
138 bool immed = false;
139 bool prefetch = false;
140 bool readonly = false;
141 bool start_tm_valid = false;
142 bool verbose_given = false;
143 bool version_given = false;
144 int res, c;
145 int sg_fd = -1;
146 int first_err = 0;
147 int last_err = 0;
148 int ret = 0;
149 int verbose = 0;
150 uint32_t count = 1;
151 int32_t l;
152 uint32_t grpnum = 0;
153 uint32_t k;
154 uint32_t num_cond_met = 0;
155 uint32_t num_err = 0;
156 uint32_t num_good = 0;
157 uint32_t numblocks = 1;
158 uint32_t skip = 1;
159 uint32_t wrap_offs = 0;
160 int64_t ll;
161 int64_t elapsed_usecs = 0;
162 uint64_t lba = 0;
163 uint64_t lba_n;
164 const char * device_name = NULL;
165 const char * cdb_name = NULL;
166 char b[64];
167 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
168 struct timespec start_tm, end_tm;
169 #elif defined(HAVE_GETTIMEOFDAY)
170 struct timeval start_tm, end_tm;
171 #endif
172
173 while (1) {
174 int option_index = 0;
175
176 c = getopt_long(argc, argv, "c:g:hil:n:prs:tTvVw:", long_options,
177 &option_index);
178 if (c == -1)
179 break;
180
181 switch (c) {
182 case 'c':
183 l = sg_get_num(optarg);
184 if (l < 0) {
185 pr2serr("--count= unable to decode argument, want 0 or "
186 "higher\n");
187 return SG_LIB_SYNTAX_ERROR;
188 }
189 count = (uint32_t)l;
190 count_given = true;
191 break;
192 case 'g':
193 l = sg_get_num(optarg);
194 if ((l > 63) || (l < 0)) {
195 pr2serr("--grpnum= expect argument in range 0 to 63\n");
196 return SG_LIB_SYNTAX_ERROR;
197 }
198 grpnum = (uint32_t)l;
199 break;
200 case 'h':
201 case '?':
202 usage();
203 return 0;
204 case 'i':
205 immed = true;
206 break;
207 case 'l':
208 ll = sg_get_llnum(optarg);
209 if (-1 == ll) {
210 pr2serr("--lba= unable to decode argument\n");
211 return SG_LIB_SYNTAX_ERROR;
212 }
213 lba = (uint64_t)ll;
214 break;
215 case 'n':
216 l = sg_get_num(optarg);
217 if (-1 == l) {
218 pr2serr("--num= unable to decode argument\n");
219 return SG_LIB_SYNTAX_ERROR;
220 }
221 numblocks = (uint32_t)l;
222 break;
223 case 'p':
224 prefetch = true;
225 break;
226 case 'r':
227 readonly = true;
228 break;
229 case 's':
230 l = sg_get_num(optarg);
231 if (-1 == l) {
232 pr2serr("--skip= unable to decode argument\n");
233 return SG_LIB_SYNTAX_ERROR;
234 }
235 skip = (uint32_t)l;
236 break;
237 case 't':
238 do_time = true;
239 break;
240 case 'T':
241 cdb10 = true;
242 break;
243 case 'v':
244 verbose_given = true;
245 ++verbose;
246 break;
247 case 'V':
248 version_given = true;
249 break;
250 case 'w':
251 l = sg_get_num(optarg);
252 if (-1 == l) {
253 pr2serr("--wrap-offset= unable to decode argument\n");
254 return SG_LIB_SYNTAX_ERROR;
255 }
256 wrap_offs = (uint32_t)l;
257 break;
258 default:
259 pr2serr("unrecognised option code 0x%x ??\n", c);
260 usage();
261 return SG_LIB_SYNTAX_ERROR;
262 }
263 }
264 if (optind < argc) {
265 if (NULL == device_name) {
266 device_name = argv[optind];
267 ++optind;
268 }
269 if (optind < argc) {
270 for (; optind < argc; ++optind)
271 pr2serr("Unexpected extra argument: %s\n",
272 argv[optind]);
273 usage();
274 return SG_LIB_SYNTAX_ERROR;
275 }
276 }
277
278 #ifdef DEBUG
279 pr2serr("In DEBUG mode, ");
280 if (verbose_given && version_given) {
281 pr2serr("but override: '-vV' given, zero verbose and continue\n");
282 verbose_given = false;
283 version_given = false;
284 verbose = 0;
285 } else if (! verbose_given) {
286 pr2serr("set '-vv'\n");
287 verbose = 2;
288 } else
289 pr2serr("keep verbose=%d\n", verbose);
290 #else
291 if (verbose_given && version_given)
292 pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
293 #endif
294 if (version_given) {
295 pr2serr("version: %s\n", version_str);
296 return 0;
297 }
298
299 if (NULL == device_name) {
300 pr2serr("Missing device name!\n\n");
301 usage();
302 return SG_LIB_SYNTAX_ERROR;
303 }
304
305 if (prefetch) {
306 if (cdb10)
307 cdb_name = "Pre-fetch(10)";
308 else
309 cdb_name = "Pre-fetch(16)";
310 } else
311 cdb_name = "Seek(10)";
312
313 sg_fd = sg_cmds_open_device(device_name, readonly, verbose);
314 if (sg_fd < 0) {
315 if (verbose)
316 pr2serr("open error: %s: %s %s\n", device_name, cdb_name,
317 safe_strerror(-sg_fd));
318 ret = sg_convert_errno(-sg_fd);
319 goto fini;
320 }
321 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
322 if (do_time) {
323 start_tm.tv_sec = 0;
324 start_tm.tv_nsec = 0;
325 if (0 == clock_gettime(CLOCK_MONOTONIC, &start_tm))
326 start_tm_valid = true;
327 else
328 perror("clock_gettime(CLOCK_MONOTONIC)\n");
329 }
330 #elif defined(HAVE_GETTIMEOFDAY)
331 if (do_time) {
332 start_tm.tv_sec = 0;
333 start_tm.tv_usec = 0;
334 gettimeofday(&start_tm, NULL);
335 start_tm_valid = true;
336 }
337 #else
338 start_tm_valid = false;
339 #endif
340
341 for (k = 0, lba_n = lba; k < count; ++k, lba_n += skip) {
342 if (wrap_offs && (lba_n > lba) && ((lba_n - lba) > wrap_offs))
343 lba_n = lba;
344 res = sg_ll_pre_fetch_x(sg_fd, ! prefetch, ! cdb10, immed, lba_n,
345 numblocks, grpnum, 0, (verbose > 0), verbose);
346 ret = res; /* last command executed sets exit status */
347 if (SG_LIB_CAT_CONDITION_MET == res)
348 ++num_cond_met;
349 else if (res) {
350 ++num_err;
351 if (0 == first_err)
352 first_err = res;
353 last_err = res;
354 } else
355 ++num_good;
356 }
357
358 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
359 if ((count > 0) && start_tm_valid &&
360 (start_tm.tv_sec || start_tm.tv_nsec)) {
361 int err;
362
363 res = clock_gettime(CLOCK_MONOTONIC, &end_tm);
364 if (res < 0) {
365 err = errno;
366 perror("clock_gettime");
367 if (EINVAL == err)
368 pr2serr("clock_gettime(CLOCK_MONOTONIC) not supported\n");
369 }
370 elapsed_usecs = (end_tm.tv_sec - start_tm.tv_sec) * 1000000;
371 /* Note that (end_tm.tv_nsec - start_tm.tv_nsec) may be negative */
372 elapsed_usecs += (end_tm.tv_nsec - start_tm.tv_nsec) / 1000;
373 }
374 #elif defined(HAVE_GETTIMEOFDAY)
375 if ((count > 0) && start_tm_valid &&
376 (start_tm.tv_sec || start_tm.tv_usec)) {
377 gettimeofday(&end_tm, NULL);
378 elapsed_usecs = (end_tm.tv_sec - start_tm.tv_sec) * 1000000;
379 elapsed_usecs += (end_tm.tv_usec - start_tm.tv_usec);
380 }
381 #endif
382
383 if (elapsed_usecs > 0) {
384 if (elapsed_usecs > 1000000)
385 snprintf(b, sizeof(b), " (over %d seconds)",
386 (int)elapsed_usecs / 1000000);
387 else
388 b[0] = '\0';
389 printf("Elapsed time: %" PRId64 " microseconds%s, per command time: "
390 "%" PRId64 "\n", elapsed_usecs, b, elapsed_usecs / count);
391 }
392
393 if (count_given && verbose_given)
394 printf("Command count=%u, number of condition_mets=%u, number of "
395 "goods=%u\n", count, num_cond_met, num_good);
396 if (first_err) {
397 bool printed;
398
399 printf(" number of errors=%d\n", num_err);
400 printf(" first error");
401 printed = sg_if_can2stdout(": ", first_err);
402 if (! printed)
403 printf(" code: %d\n", first_err);
404 if (num_err > 1) {
405 printf(" last error");
406 printed = sg_if_can2stdout(": ", last_err);
407 if (! printed)
408 printf(" code: %d\n", last_err);
409 }
410 }
411 fini:
412 if (sg_fd >= 0) {
413 res = sg_cmds_close_device(sg_fd);
414 if (res < 0) {
415 pr2serr("close error: %s\n", safe_strerror(-res));
416 if (0 == ret)
417 ret = sg_convert_errno(-res);
418 }
419 }
420 if (0 == verbose) {
421 const char * e_str = (SG_LIB_CAT_CONDITION_MET == ret) ?
422 "sg_seek: " : "sg_seek: failed";
423
424 if (! sg_if_can2stderr(e_str, ret))
425 pr2serr("Some error occurred, try again with '-v' "
426 "or '-vv' for more information\n");
427 }
428 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
429 }
430