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