]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0 |
e246ba5f | 2 | /* |
da23017d NS |
3 | * Copyright (c) 2003-2005 Silicon Graphics, Inc. |
4 | * All Rights Reserved. | |
e246ba5f NS |
5 | */ |
6 | ||
c1436573 LR |
7 | #define _BSD_SOURCE |
8 | #define _DEFAULT_SOURCE | |
64a93371 | 9 | #include <sys/uio.h> |
6b803e5a CH |
10 | #include "command.h" |
11 | #include "input.h" | |
e246ba5f | 12 | #include <ctype.h> |
e246ba5f | 13 | #include "init.h" |
48c46ee3 | 14 | #include "io.h" |
e246ba5f NS |
15 | |
16 | static cmdinfo_t pread_cmd; | |
17 | ||
18 | static void | |
19 | pread_help(void) | |
20 | { | |
21 | printf(_( | |
22 | "\n" | |
23 | " reads a range of bytes in a specified block size from the given offset\n" | |
24 | "\n" | |
25 | " Example:\n" | |
48c46ee3 | 26 | " 'pread -v 512 20' - dumps 20 bytes read from 512 bytes into the file\n" |
e246ba5f NS |
27 | "\n" |
28 | " Reads a segment of the currently open file, optionally dumping it to the\n" | |
29 | " standard output stream (with -v option) for subsequent inspection.\n" | |
30 | " The reads are performed in sequential blocks starting at offset, with the\n" | |
8fb2237e NS |
31 | " blocksize tunable using the -b option (default blocksize is 4096 bytes),\n" |
32 | " unless a different pattern is requested.\n" | |
0388cfb1 | 33 | " -q -- quiet mode, do not write anything to standard output.\n" |
8fb2237e NS |
34 | " -B -- read backwards through the range from offset (backwards N bytes)\n" |
35 | " -F -- read forwards through the range of bytes from offset (default)\n" | |
36 | " -v -- be verbose, dump out buffers (used when reading forwards)\n" | |
37 | " -R -- read at random offsets in the range of bytes\n" | |
38 | " -Z N -- zeed the random number generator (used when reading randomly)\n" | |
39 | " (heh, zorry, the -s/-S arguments were already in use in pwrite)\n" | |
e115d3b2 DC |
40 | " -V N -- use vectored IO with N iovecs of blocksize each (preadv)\n" |
41 | "\n" | |
8fb2237e NS |
42 | " When in \"random\" mode, the number of read operations will equal the\n" |
43 | " number required to do a complete forward/backward scan of the range.\n" | |
44 | " Note that the offset within the range is chosen at random each time\n" | |
45 | " (an offset may be read more than once when operating in this mode).\n" | |
e246ba5f NS |
46 | "\n")); |
47 | } | |
48 | ||
86715ccb | 49 | void *io_buffer; |
2c2f6d79 | 50 | size_t highwater; |
86715ccb | 51 | size_t io_buffersize; |
e115d3b2 DC |
52 | int vectors; |
53 | struct iovec *iov; | |
54 | ||
55 | static int | |
56 | alloc_iovec( | |
57 | size_t bsize, | |
58 | int uflag, | |
59 | unsigned int seed) | |
60 | { | |
61 | int i; | |
62 | ||
63 | iov = calloc(vectors, sizeof(struct iovec)); | |
64 | if (!iov) | |
65 | return -1; | |
66 | ||
86715ccb | 67 | io_buffersize = 0; |
e115d3b2 DC |
68 | for (i = 0; i < vectors; i++) { |
69 | iov[i].iov_base = memalign(pagesize, bsize); | |
09bd34dd | 70 | if (!iov[i].iov_base) { |
e115d3b2 DC |
71 | perror("memalign"); |
72 | goto unwind; | |
73 | } | |
74 | iov[i].iov_len = bsize; | |
75 | if (!uflag) | |
76 | memset(iov[i].iov_base, seed, bsize); | |
77 | } | |
86715ccb | 78 | io_buffersize = bsize * vectors; |
e115d3b2 DC |
79 | return 0; |
80 | unwind: | |
81 | for( ; i >= 0; i--) | |
82 | free(iov[i].iov_base); | |
83 | free(iov); | |
84 | iov = NULL; | |
85 | return -1; | |
86 | } | |
e246ba5f NS |
87 | |
88 | int | |
89 | alloc_buffer( | |
2c2f6d79 | 90 | size_t bsize, |
48c46ee3 | 91 | int uflag, |
e246ba5f NS |
92 | unsigned int seed) |
93 | { | |
e115d3b2 DC |
94 | if (vectors) |
95 | return alloc_iovec(bsize, uflag, seed); | |
96 | ||
105f2cc2 | 97 | if (bsize > highwater) { |
86715ccb ES |
98 | if (io_buffer) |
99 | free(io_buffer); | |
100 | io_buffer = memalign(pagesize, bsize); | |
101 | if (!io_buffer) { | |
ae8d6a95 | 102 | perror("memalign"); |
86715ccb | 103 | highwater = io_buffersize = 0; |
48c46ee3 | 104 | return -1; |
e246ba5f | 105 | } |
105f2cc2 | 106 | highwater = bsize; |
e246ba5f | 107 | } |
86715ccb | 108 | io_buffersize = bsize; |
48c46ee3 | 109 | if (!uflag) |
86715ccb | 110 | memset(io_buffer, seed, io_buffersize); |
48c46ee3 | 111 | return 0; |
e246ba5f NS |
112 | } |
113 | ||
b6fef47a | 114 | static void |
e115d3b2 DC |
115 | __dump_buffer( |
116 | void *buf, | |
9e726740 | 117 | off_t offset, |
e246ba5f NS |
118 | ssize_t len) |
119 | { | |
120 | int i, j; | |
121 | char *p; | |
122 | ||
09bd34dd | 123 | for (i = 0, p = (char *)buf; i < len; i += 16) { |
e246ba5f NS |
124 | char *s = p; |
125 | ||
638473d8 | 126 | printf("%08llx: ", (unsigned long long)offset + i); |
e246ba5f NS |
127 | for (j = 0; j < 16 && i + j < len; j++, p++) |
128 | printf("%02x ", *p); | |
129 | printf(" "); | |
130 | for (j = 0; j < 16 && i + j < len; j++, s++) { | |
93d9f139 | 131 | if (isalnum((int)*s)) |
e246ba5f NS |
132 | printf("%c", *s); |
133 | else | |
134 | printf("."); | |
135 | } | |
136 | printf("\n"); | |
137 | } | |
138 | } | |
139 | ||
e115d3b2 DC |
140 | void |
141 | dump_buffer( | |
9e726740 | 142 | off_t offset, |
e115d3b2 DC |
143 | ssize_t len) |
144 | { | |
145 | int i, l; | |
146 | ||
147 | if (!vectors) { | |
86715ccb | 148 | __dump_buffer(io_buffer, offset, len); |
e115d3b2 DC |
149 | return; |
150 | } | |
151 | ||
152 | for (i = 0; len > 0 && i < vectors; i++) { | |
153 | l = min(len, iov[i].iov_len); | |
154 | ||
155 | __dump_buffer(iov[i].iov_base, offset, l); | |
156 | len -= l; | |
157 | offset += l; | |
158 | } | |
159 | } | |
160 | ||
421c9220 | 161 | static ssize_t |
197d5828 | 162 | do_preadv( |
e115d3b2 | 163 | int fd, |
9e726740 | 164 | off_t offset, |
4eafc4d3 | 165 | long long count) |
e115d3b2 DC |
166 | { |
167 | int vecs = 0; | |
168 | ssize_t oldlen = 0; | |
169 | ssize_t bytes = 0; | |
170 | ||
e115d3b2 | 171 | /* trim the iovec if necessary */ |
86715ccb | 172 | if (count < io_buffersize) { |
e115d3b2 DC |
173 | size_t len = 0; |
174 | while (len + iov[vecs].iov_len < count) { | |
175 | len += iov[vecs].iov_len; | |
176 | vecs++; | |
177 | } | |
178 | oldlen = iov[vecs].iov_len; | |
179 | iov[vecs].iov_len = count - len; | |
180 | vecs++; | |
181 | } else { | |
182 | vecs = vectors; | |
183 | } | |
184 | bytes = preadv(fd, iov, vectors, offset); | |
185 | ||
186 | /* restore trimmed iov */ | |
187 | if (oldlen) | |
188 | iov[vecs - 1].iov_len = oldlen; | |
189 | ||
190 | return bytes; | |
191 | } | |
197d5828 | 192 | |
421c9220 | 193 | static ssize_t |
197d5828 ES |
194 | do_pread( |
195 | int fd, | |
9e726740 | 196 | off_t offset, |
4eafc4d3 | 197 | long long count, |
421c9220 | 198 | size_t buffer_size) |
197d5828 ES |
199 | { |
200 | if (!vectors) | |
86715ccb | 201 | return pread(fd, io_buffer, min(count, buffer_size), offset); |
197d5828 | 202 | |
248b6f9a | 203 | return do_preadv(fd, offset, count); |
197d5828 | 204 | } |
e115d3b2 | 205 | |
8fb2237e NS |
206 | static int |
207 | read_random( | |
208 | int fd, | |
9e726740 | 209 | off_t offset, |
8fb2237e NS |
210 | long long count, |
211 | long long *total, | |
212 | unsigned int seed, | |
213 | int eof) | |
214 | { | |
9e726740 | 215 | off_t end, off, range; |
8fb2237e NS |
216 | ssize_t bytes; |
217 | int ops = 0; | |
218 | ||
219 | srandom(seed); | |
dc8878f4 | 220 | end = lseek(fd, 0, SEEK_END); |
8fb2237e | 221 | offset = (eof || offset > end) ? end : offset; |
86715ccb | 222 | if ((bytes = (offset % io_buffersize))) |
8fb2237e NS |
223 | offset -= bytes; |
224 | offset = max(0, offset); | |
86715ccb | 225 | if ((bytes = (count % io_buffersize))) |
8fb2237e | 226 | count += bytes; |
86715ccb ES |
227 | count = max(io_buffersize, count); |
228 | range = count - io_buffersize; | |
8fb2237e NS |
229 | |
230 | *total = 0; | |
231 | while (count > 0) { | |
2ab8ecbe | 232 | if (range) |
86715ccb ES |
233 | off = ((offset + (random() % range)) / io_buffersize) * |
234 | io_buffersize; | |
2ab8ecbe DM |
235 | else |
236 | off = offset; | |
86715ccb | 237 | bytes = do_pread(fd, off, io_buffersize, io_buffersize); |
8fb2237e NS |
238 | if (bytes == 0) |
239 | break; | |
240 | if (bytes < 0) { | |
2f9a125c | 241 | perror("pread"); |
8fb2237e NS |
242 | return -1; |
243 | } | |
244 | ops++; | |
245 | *total += bytes; | |
86715ccb | 246 | if (bytes < io_buffersize) |
8fb2237e NS |
247 | break; |
248 | count -= bytes; | |
249 | } | |
250 | return ops; | |
251 | } | |
252 | ||
253 | static int | |
254 | read_backward( | |
255 | int fd, | |
9e726740 | 256 | off_t *offset, |
8fb2237e NS |
257 | long long *count, |
258 | long long *total, | |
259 | int eof) | |
260 | { | |
9e726740 | 261 | off_t end, off = *offset; |
8fb2237e NS |
262 | ssize_t bytes = 0, bytes_requested; |
263 | long long cnt = *count; | |
264 | int ops = 0; | |
265 | ||
dc8878f4 FJ |
266 | end = lseek(fd, 0, SEEK_END); |
267 | off = eof ? end : min(end, lseek(fd, off, SEEK_SET)); | |
8fb2237e NS |
268 | if ((end = off - cnt) < 0) { |
269 | cnt += end; /* subtraction, end is negative */ | |
270 | end = 0; | |
271 | } | |
272 | *total = 0; | |
273 | *count = cnt; | |
274 | *offset = off; | |
275 | ||
276 | /* Do initial unaligned read if needed */ | |
86715ccb | 277 | if ((bytes_requested = (off % io_buffersize))) { |
8fb2237e | 278 | off -= bytes_requested; |
86715ccb | 279 | bytes = do_pread(fd, off, bytes_requested, io_buffersize); |
8fb2237e NS |
280 | if (bytes == 0) |
281 | return ops; | |
282 | if (bytes < 0) { | |
2f9a125c | 283 | perror("pread"); |
8fb2237e NS |
284 | return -1; |
285 | } | |
286 | ops++; | |
287 | *total += bytes; | |
288 | if (bytes < bytes_requested) | |
289 | return ops; | |
290 | cnt -= bytes; | |
291 | } | |
292 | ||
293 | /* Iterate backward through the rest of the range */ | |
294 | while (cnt > end) { | |
86715ccb | 295 | bytes_requested = min(cnt, io_buffersize); |
8fb2237e | 296 | off -= bytes_requested; |
86715ccb | 297 | bytes = do_pread(fd, off, cnt, io_buffersize); |
8fb2237e NS |
298 | if (bytes == 0) |
299 | break; | |
300 | if (bytes < 0) { | |
2f9a125c | 301 | perror("pread"); |
8fb2237e NS |
302 | return -1; |
303 | } | |
304 | ops++; | |
305 | *total += bytes; | |
306 | if (bytes < bytes_requested) | |
307 | break; | |
308 | cnt -= bytes; | |
309 | } | |
310 | return ops; | |
311 | } | |
312 | ||
313 | static int | |
314 | read_forward( | |
e246ba5f | 315 | int fd, |
9e726740 | 316 | off_t offset, |
92d9b902 NS |
317 | long long count, |
318 | long long *total, | |
e246ba5f | 319 | int verbose, |
8fb2237e NS |
320 | int onlyone, |
321 | int eof) | |
e246ba5f | 322 | { |
2c2f6d79 | 323 | ssize_t bytes; |
92d9b902 | 324 | int ops = 0; |
e246ba5f NS |
325 | |
326 | *total = 0; | |
8fb2237e | 327 | while (count > 0 || eof) { |
86715ccb | 328 | bytes = do_pread(fd, offset, count, io_buffersize); |
e246ba5f NS |
329 | if (bytes == 0) |
330 | break; | |
331 | if (bytes < 0) { | |
2f9a125c | 332 | perror("pread"); |
92d9b902 | 333 | return -1; |
e246ba5f | 334 | } |
92d9b902 | 335 | ops++; |
e246ba5f NS |
336 | if (verbose) |
337 | dump_buffer(offset, bytes); | |
338 | *total += bytes; | |
86715ccb | 339 | if (onlyone || bytes < min(count, io_buffersize)) |
e246ba5f NS |
340 | break; |
341 | offset += bytes; | |
342 | count -= bytes; | |
343 | } | |
92d9b902 | 344 | return ops; |
e246ba5f NS |
345 | } |
346 | ||
8fb2237e NS |
347 | int |
348 | read_buffer( | |
349 | int fd, | |
9e726740 | 350 | off_t offset, |
8fb2237e NS |
351 | long long count, |
352 | long long *total, | |
353 | int verbose, | |
354 | int onlyone) | |
355 | { | |
356 | return read_forward(fd, offset, count, total, verbose, onlyone, 0); | |
357 | } | |
358 | ||
e246ba5f NS |
359 | static int |
360 | pread_f( | |
361 | int argc, | |
362 | char **argv) | |
363 | { | |
8fb2237e | 364 | size_t bsize; |
9e726740 | 365 | off_t offset; |
8fb2237e | 366 | unsigned int zeed = 0; |
2c2f6d79 | 367 | long long count, total, tmp; |
d026b19e | 368 | size_t fsblocksize, fssectsize; |
92d9b902 | 369 | struct timeval t1, t2; |
8fb2237e | 370 | char *sp; |
d347f827 | 371 | int Cflag, qflag, uflag, vflag; |
8fb2237e | 372 | int eof = 0, direction = IO_FORWARD; |
e246ba5f NS |
373 | int c; |
374 | ||
d347f827 | 375 | Cflag = qflag = uflag = vflag = 0; |
8fb2237e NS |
376 | init_cvtnum(&fsblocksize, &fssectsize); |
377 | bsize = fsblocksize; | |
378 | ||
e115d3b2 | 379 | while ((c = getopt(argc, argv, "b:BCFRquvV:Z:")) != EOF) { |
e246ba5f NS |
380 | switch (c) { |
381 | case 'b': | |
8fb2237e | 382 | tmp = cvtnum(fsblocksize, fssectsize, optarg); |
2c2f6d79 | 383 | if (tmp < 0) { |
e246ba5f | 384 | printf(_("non-numeric bsize -- %s\n"), optarg); |
9e1595e6 | 385 | exitcode = 1; |
e246ba5f NS |
386 | return 0; |
387 | } | |
8fb2237e | 388 | bsize = tmp; |
e246ba5f | 389 | break; |
5c7bef67 NS |
390 | case 'C': |
391 | Cflag = 1; | |
392 | break; | |
8fb2237e NS |
393 | case 'F': |
394 | direction = IO_FORWARD; | |
395 | break; | |
396 | case 'B': | |
397 | direction = IO_BACKWARD; | |
398 | break; | |
399 | case 'R': | |
400 | direction = IO_RANDOM; | |
401 | break; | |
d347f827 NS |
402 | case 'q': |
403 | qflag = 1; | |
404 | break; | |
48c46ee3 NS |
405 | case 'u': |
406 | uflag = 1; | |
407 | break; | |
e246ba5f NS |
408 | case 'v': |
409 | vflag = 1; | |
410 | break; | |
e115d3b2 DC |
411 | case 'V': |
412 | vectors = strtoul(optarg, &sp, 0); | |
413 | if (!sp || sp == optarg) { | |
197d5828 | 414 | printf(_("non-numeric vector count == %s\n"), |
e115d3b2 | 415 | optarg); |
9e1595e6 | 416 | exitcode = 1; |
e115d3b2 DC |
417 | return 0; |
418 | } | |
419 | break; | |
8fb2237e NS |
420 | case 'Z': |
421 | zeed = strtoul(optarg, &sp, 0); | |
422 | if (!sp || sp == optarg) { | |
423 | printf(_("non-numeric seed -- %s\n"), optarg); | |
9e1595e6 | 424 | exitcode = 1; |
8fb2237e NS |
425 | return 0; |
426 | } | |
427 | break; | |
e246ba5f | 428 | default: |
9e1595e6 | 429 | exitcode = 1; |
48c46ee3 | 430 | return command_usage(&pread_cmd); |
e246ba5f NS |
431 | } |
432 | } | |
9e1595e6 DC |
433 | if (optind != argc - 2) { |
434 | exitcode = 1; | |
48c46ee3 | 435 | return command_usage(&pread_cmd); |
9e1595e6 | 436 | } |
48c46ee3 | 437 | |
8fb2237e NS |
438 | offset = cvtnum(fsblocksize, fssectsize, argv[optind]); |
439 | if (offset < 0 && (direction & (IO_RANDOM|IO_BACKWARD))) { | |
440 | eof = -1; /* read from EOF */ | |
441 | } else if (offset < 0) { | |
442 | printf(_("non-numeric length argument -- %s\n"), argv[optind]); | |
9e1595e6 | 443 | exitcode = 1; |
e246ba5f NS |
444 | return 0; |
445 | } | |
446 | optind++; | |
8fb2237e NS |
447 | count = cvtnum(fsblocksize, fssectsize, argv[optind]); |
448 | if (count < 0 && (direction & (IO_RANDOM|IO_FORWARD))) { | |
449 | eof = -1; /* read to EOF */ | |
450 | } else if (count < 0) { | |
e246ba5f | 451 | printf(_("non-numeric length argument -- %s\n"), argv[optind]); |
9e1595e6 | 452 | exitcode = 1; |
e246ba5f NS |
453 | return 0; |
454 | } | |
d347f827 | 455 | |
9e1595e6 DC |
456 | if (alloc_buffer(bsize, uflag, 0xabababab) < 0) { |
457 | exitcode = 1; | |
e246ba5f | 458 | return 0; |
9e1595e6 | 459 | } |
e246ba5f | 460 | |
92d9b902 | 461 | gettimeofday(&t1, NULL); |
8fb2237e NS |
462 | switch (direction) { |
463 | case IO_RANDOM: | |
464 | if (!zeed) /* srandom seed */ | |
465 | zeed = time(NULL); | |
466 | c = read_random(file->fd, offset, count, &total, zeed, eof); | |
467 | break; | |
468 | case IO_FORWARD: | |
469 | c = read_forward(file->fd, offset, count, &total, vflag, 0, eof); | |
470 | if (eof) | |
471 | count = total; | |
472 | break; | |
473 | case IO_BACKWARD: | |
474 | c = read_backward(file->fd, &offset, &count, &total, eof); | |
475 | break; | |
476 | default: | |
477 | ASSERT(0); | |
478 | } | |
9e1595e6 DC |
479 | if (c < 0) { |
480 | exitcode = 1; | |
e246ba5f | 481 | return 0; |
9e1595e6 DC |
482 | } |
483 | ||
d347f827 NS |
484 | if (qflag) |
485 | return 0; | |
92d9b902 NS |
486 | gettimeofday(&t2, NULL); |
487 | t2 = tsub(t2, t1); | |
e246ba5f | 488 | |
a9b61ce9 | 489 | report_io_times("read", &t2, (long long)offset, count, total, c, Cflag); |
e246ba5f NS |
490 | return 0; |
491 | } | |
492 | ||
493 | void | |
494 | pread_init(void) | |
495 | { | |
ad765595 AM |
496 | pread_cmd.name = "pread"; |
497 | pread_cmd.altname = "r"; | |
e246ba5f NS |
498 | pread_cmd.cfunc = pread_f; |
499 | pread_cmd.argmin = 2; | |
500 | pread_cmd.argmax = -1; | |
48c46ee3 | 501 | pread_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; |
0388cfb1 | 502 | pread_cmd.args = _("[-b bs] [-qv] [-i N] [-FBR [-Z N]] off len"); |
e246ba5f NS |
503 | pread_cmd.oneline = _("reads a number of bytes at a specified offset"); |
504 | pread_cmd.help = pread_help; | |
505 | ||
506 | add_command(&pread_cmd); | |
507 | } |