]>
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 | ||
64a93371 | 7 | #include <sys/uio.h> |
6b803e5a CH |
8 | #include "command.h" |
9 | #include "input.h" | |
e246ba5f | 10 | #include "init.h" |
48c46ee3 | 11 | #include "io.h" |
e246ba5f NS |
12 | |
13 | static cmdinfo_t pwrite_cmd; | |
14 | ||
15 | static void | |
16 | pwrite_help(void) | |
17 | { | |
18 | printf(_( | |
19 | "\n" | |
20 | " writes a range of bytes (in block size increments) from the given offset\n" | |
21 | "\n" | |
22 | " Example:\n" | |
48c46ee3 | 23 | " 'pwrite 512 20' - writes 20 bytes at 512 bytes into the open file\n" |
e246ba5f NS |
24 | "\n" |
25 | " Writes into a segment of the currently open file, using either a buffer\n" | |
26 | " filled with a set pattern (0xcdcdcdcd) or data read from an input file.\n" | |
e246ba5f | 27 | " The writes are performed in sequential blocks starting at offset, with the\n" |
8fb2237e NS |
28 | " blocksize tunable using the -b option (default blocksize is 4096 bytes),\n" |
29 | " unless a different write pattern is requested.\n" | |
0388cfb1 | 30 | " -q -- quiet mode, do not write anything to standard output.\n" |
8fb2237e NS |
31 | " -S -- use an alternate seed number for filling the write buffer\n" |
32 | " -i -- input file, source of data to write (used when writing forward)\n" | |
33 | " -d -- open the input file for direct IO\n" | |
34 | " -s -- skip a number of bytes at the start of the input file\n" | |
35 | " -w -- call fdatasync(2) at the end (included in timing results)\n" | |
36 | " -W -- call fsync(2) at the end (included in timing results)\n" | |
37 | " -B -- write backwards through the range from offset (backwards N bytes)\n" | |
38 | " -F -- write forwards through the range of bytes from offset (default)\n" | |
43ba1d61 | 39 | " -O -- perform pwrite call once and return (maybe partial) bytes written\n" |
8fb2237e NS |
40 | " -R -- write at random offsets in the specified range of bytes\n" |
41 | " -Z N -- zeed the random number generator (used when writing randomly)\n" | |
42 | " (heh, zorry, the -s/-S arguments were already in use in pwrite)\n" | |
197d5828 | 43 | #ifdef HAVE_PWRITEV |
10899f17 | 44 | " -V N -- use vectored IO with N iovecs of blocksize each (pwritev)\n" |
197d5828 | 45 | #endif |
0799d5cf GR |
46 | #ifdef HAVE_PWRITEV2 |
47 | " -N -- Perform the pwritev2() with RWF_NOWAIT\n" | |
d9763825 | 48 | " -D -- Perform the pwritev2() with RWF_DSYNC\n" |
0799d5cf | 49 | #endif |
e246ba5f NS |
50 | "\n")); |
51 | } | |
52 | ||
197d5828 | 53 | #ifdef HAVE_PWRITEV |
421c9220 | 54 | static ssize_t |
197d5828 | 55 | do_pwritev( |
10899f17 | 56 | int fd, |
9e726740 | 57 | off_t offset, |
4eafc4d3 DW |
58 | long long count, |
59 | int pwritev2_flags) | |
10899f17 | 60 | { |
197d5828 ES |
61 | int vecs = 0; |
62 | ssize_t oldlen = 0; | |
63 | ssize_t bytes = 0; | |
10899f17 DC |
64 | |
65 | /* trim the iovec if necessary */ | |
86715ccb | 66 | if (count < io_buffersize) { |
10899f17 DC |
67 | size_t len = 0; |
68 | while (len + iov[vecs].iov_len < count) { | |
69 | len += iov[vecs].iov_len; | |
70 | vecs++; | |
71 | } | |
72 | oldlen = iov[vecs].iov_len; | |
73 | iov[vecs].iov_len = count - len; | |
74 | vecs++; | |
75 | } else { | |
76 | vecs = vectors; | |
77 | } | |
c5deeac9 GR |
78 | #ifdef HAVE_PWRITEV2 |
79 | if (pwritev2_flags) | |
80 | bytes = pwritev2(fd, iov, vectors, offset, pwritev2_flags); | |
81 | else | |
82 | bytes = pwritev(fd, iov, vectors, offset); | |
83 | #else | |
10899f17 | 84 | bytes = pwritev(fd, iov, vectors, offset); |
c5deeac9 | 85 | #endif |
10899f17 DC |
86 | |
87 | /* restore trimmed iov */ | |
88 | if (oldlen) | |
89 | iov[vecs - 1].iov_len = oldlen; | |
90 | ||
91 | return bytes; | |
92 | } | |
197d5828 | 93 | #else |
248b6f9a | 94 | #define do_pwritev(fd, offset, count, pwritev2_flags) (0) |
197d5828 ES |
95 | #endif |
96 | ||
421c9220 | 97 | static ssize_t |
197d5828 ES |
98 | do_pwrite( |
99 | int fd, | |
9e726740 | 100 | off_t offset, |
4eafc4d3 | 101 | long long count, |
421c9220 | 102 | size_t buffer_size, |
c5deeac9 | 103 | int pwritev2_flags) |
197d5828 ES |
104 | { |
105 | if (!vectors) | |
86715ccb | 106 | return pwrite(fd, io_buffer, min(count, buffer_size), offset); |
197d5828 | 107 | |
248b6f9a | 108 | return do_pwritev(fd, offset, count, pwritev2_flags); |
197d5828 ES |
109 | } |
110 | ||
8fb2237e NS |
111 | static int |
112 | write_random( | |
9e726740 | 113 | off_t offset, |
8fb2237e NS |
114 | long long count, |
115 | unsigned int seed, | |
c5deeac9 GR |
116 | long long *total, |
117 | int pwritev2_flags) | |
8fb2237e | 118 | { |
9e726740 | 119 | off_t off, range; |
8fb2237e NS |
120 | ssize_t bytes; |
121 | int ops = 0; | |
122 | ||
123 | srandom(seed); | |
86715ccb | 124 | if ((bytes = (offset % io_buffersize))) |
8fb2237e NS |
125 | offset -= bytes; |
126 | offset = max(0, offset); | |
86715ccb | 127 | if ((bytes = (count % io_buffersize))) |
8fb2237e | 128 | count += bytes; |
86715ccb ES |
129 | count = max(io_buffersize, count); |
130 | range = count - io_buffersize; | |
8fb2237e NS |
131 | |
132 | *total = 0; | |
133 | while (count > 0) { | |
2ab8ecbe | 134 | if (range) |
86715ccb ES |
135 | off = ((offset + (random() % range)) / io_buffersize) * |
136 | io_buffersize; | |
2ab8ecbe DM |
137 | else |
138 | off = offset; | |
86715ccb | 139 | bytes = do_pwrite(file->fd, off, io_buffersize, io_buffersize, |
c5deeac9 | 140 | pwritev2_flags); |
8fb2237e NS |
141 | if (bytes == 0) |
142 | break; | |
143 | if (bytes < 0) { | |
2f9a125c | 144 | perror("pwrite"); |
8fb2237e NS |
145 | return -1; |
146 | } | |
147 | ops++; | |
148 | *total += bytes; | |
86715ccb | 149 | if (bytes < io_buffersize) |
8fb2237e NS |
150 | break; |
151 | count -= bytes; | |
152 | } | |
153 | return ops; | |
154 | } | |
155 | ||
156 | static int | |
157 | write_backward( | |
9e726740 | 158 | off_t offset, |
8fb2237e | 159 | long long *count, |
c5deeac9 GR |
160 | long long *total, |
161 | int pwritev2_flags) | |
8fb2237e | 162 | { |
9e726740 | 163 | off_t end, off = offset; |
8fb2237e NS |
164 | ssize_t bytes = 0, bytes_requested; |
165 | long long cnt = *count; | |
166 | int ops = 0; | |
167 | ||
168 | if ((end = off - cnt) < 0) { | |
169 | cnt += end; /* subtraction, end is negative */ | |
170 | end = 0; | |
171 | } | |
172 | *total = 0; | |
173 | *count = cnt; | |
174 | ||
175 | /* Do initial unaligned write if needed */ | |
86715ccb | 176 | if ((bytes_requested = (off % io_buffersize))) { |
8fb2237e NS |
177 | bytes_requested = min(cnt, bytes_requested); |
178 | off -= bytes_requested; | |
86715ccb | 179 | bytes = do_pwrite(file->fd, off, bytes_requested, io_buffersize, |
c5deeac9 | 180 | pwritev2_flags); |
8fb2237e NS |
181 | if (bytes == 0) |
182 | return ops; | |
183 | if (bytes < 0) { | |
2f9a125c | 184 | perror("pwrite"); |
8fb2237e NS |
185 | return -1; |
186 | } | |
187 | ops++; | |
188 | *total += bytes; | |
189 | if (bytes < bytes_requested) | |
190 | return ops; | |
191 | cnt -= bytes; | |
192 | } | |
193 | ||
194 | /* Iterate backward through the rest of the range */ | |
195 | while (cnt > end) { | |
86715ccb | 196 | bytes_requested = min(cnt, io_buffersize); |
8fb2237e | 197 | off -= bytes_requested; |
86715ccb | 198 | bytes = do_pwrite(file->fd, off, cnt, io_buffersize, |
c5deeac9 | 199 | pwritev2_flags); |
8fb2237e NS |
200 | if (bytes == 0) |
201 | break; | |
202 | if (bytes < 0) { | |
2f9a125c | 203 | perror("pwrite"); |
8fb2237e NS |
204 | return -1; |
205 | } | |
206 | ops++; | |
207 | *total += bytes; | |
208 | if (bytes < bytes_requested) | |
209 | break; | |
210 | cnt -= bytes; | |
211 | } | |
212 | return ops; | |
213 | } | |
214 | ||
e246ba5f NS |
215 | static int |
216 | write_buffer( | |
9e726740 | 217 | off_t offset, |
92d9b902 | 218 | long long count, |
2c2f6d79 | 219 | size_t bs, |
e246ba5f | 220 | int fd, |
9e726740 | 221 | off_t skip, |
c5deeac9 GR |
222 | long long *total, |
223 | int pwritev2_flags) | |
e246ba5f | 224 | { |
2c2f6d79 | 225 | ssize_t bytes; |
92d9b902 NS |
226 | long long bar = min(bs, count); |
227 | int ops = 0; | |
e246ba5f NS |
228 | |
229 | *total = 0; | |
8e2153bf | 230 | while (count >= 0) { |
e246ba5f | 231 | if (fd > 0) { /* input file given, read buffer first */ |
92d9b902 | 232 | if (read_buffer(fd, skip + *total, bs, &bar, 0, 1) < 0) |
e246ba5f NS |
233 | break; |
234 | } | |
c5deeac9 | 235 | bytes = do_pwrite(file->fd, offset, count, bar, pwritev2_flags); |
e246ba5f NS |
236 | if (bytes == 0) |
237 | break; | |
238 | if (bytes < 0) { | |
2f9a125c | 239 | perror("pwrite"); |
92d9b902 | 240 | return -1; |
e246ba5f | 241 | } |
92d9b902 | 242 | ops++; |
e246ba5f | 243 | *total += bytes; |
10899f17 | 244 | if (bytes < min(count, bar)) |
e246ba5f NS |
245 | break; |
246 | offset += bytes; | |
247 | count -= bytes; | |
8e2153bf ES |
248 | if (count == 0) |
249 | break; | |
e246ba5f | 250 | } |
92d9b902 | 251 | return ops; |
e246ba5f NS |
252 | } |
253 | ||
43ba1d61 GR |
254 | static int |
255 | write_once( | |
9e726740 | 256 | off_t offset, |
43ba1d61 GR |
257 | long long count, |
258 | long long *total, | |
259 | int pwritev2_flags) | |
260 | { | |
421c9220 | 261 | ssize_t bytes; |
43ba1d61 | 262 | bytes = do_pwrite(file->fd, offset, count, count, pwritev2_flags); |
99d6e84f GR |
263 | if (bytes < 0) { |
264 | perror("pwrite"); | |
43ba1d61 | 265 | return -1; |
99d6e84f | 266 | } |
43ba1d61 GR |
267 | *total = bytes; |
268 | return 1; | |
269 | } | |
270 | ||
271 | ||
e246ba5f NS |
272 | static int |
273 | pwrite_f( | |
274 | int argc, | |
275 | char **argv) | |
276 | { | |
8fb2237e | 277 | size_t bsize; |
9e726740 | 278 | off_t offset, skip = 0; |
2c2f6d79 | 279 | long long count, total, tmp; |
8fb2237e | 280 | unsigned int zeed = 0, seed = 0xcdcdcdcd; |
d026b19e | 281 | size_t fsblocksize, fssectsize; |
92d9b902 | 282 | struct timeval t1, t2; |
e246ba5f | 283 | char *sp, *infile = NULL; |
d347f827 | 284 | int Cflag, qflag, uflag, dflag, wflag, Wflag; |
8fb2237e | 285 | int direction = IO_FORWARD; |
5c7bef67 | 286 | int c, fd = -1; |
c5deeac9 | 287 | int pwritev2_flags = 0; |
e246ba5f | 288 | |
d347f827 | 289 | Cflag = qflag = uflag = dflag = wflag = Wflag = 0; |
8fb2237e NS |
290 | init_cvtnum(&fsblocksize, &fssectsize); |
291 | bsize = fsblocksize; | |
292 | ||
d9763825 | 293 | while ((c = getopt(argc, argv, "b:BCdDf:Fi:NqRs:OS:uV:wWZ:")) != EOF) { |
e246ba5f NS |
294 | switch (c) { |
295 | case 'b': | |
8fb2237e | 296 | tmp = cvtnum(fsblocksize, fssectsize, optarg); |
2c2f6d79 | 297 | if (tmp < 0) { |
e246ba5f | 298 | printf(_("non-numeric bsize -- %s\n"), optarg); |
9e1595e6 | 299 | exitcode = 1; |
e246ba5f NS |
300 | return 0; |
301 | } | |
8fb2237e | 302 | bsize = tmp; |
e246ba5f | 303 | break; |
5c7bef67 NS |
304 | case 'C': |
305 | Cflag = 1; | |
306 | break; | |
8fb2237e NS |
307 | case 'F': |
308 | direction = IO_FORWARD; | |
309 | break; | |
310 | case 'B': | |
311 | direction = IO_BACKWARD; | |
312 | break; | |
313 | case 'R': | |
314 | direction = IO_RANDOM; | |
315 | break; | |
43ba1d61 GR |
316 | case 'O': |
317 | direction = IO_ONCE; | |
318 | break; | |
e246ba5f NS |
319 | case 'd': |
320 | dflag = 1; | |
321 | break; | |
322 | case 'f': | |
323 | case 'i': | |
324 | infile = optarg; | |
325 | break; | |
0799d5cf GR |
326 | #ifdef HAVE_PWRITEV2 |
327 | case 'N': | |
328 | pwritev2_flags |= RWF_NOWAIT; | |
329 | break; | |
d9763825 DC |
330 | case 'D': |
331 | pwritev2_flags |= RWF_DSYNC; | |
332 | break; | |
0799d5cf | 333 | #endif |
e246ba5f | 334 | case 's': |
8fb2237e | 335 | skip = cvtnum(fsblocksize, fssectsize, optarg); |
638473d8 | 336 | if (skip < 0) { |
e246ba5f | 337 | printf(_("non-numeric skip -- %s\n"), optarg); |
9e1595e6 | 338 | exitcode = 1; |
e246ba5f NS |
339 | return 0; |
340 | } | |
341 | break; | |
342 | case 'S': | |
343 | seed = strtoul(optarg, &sp, 0); | |
344 | if (!sp || sp == optarg) { | |
345 | printf(_("non-numeric seed -- %s\n"), optarg); | |
9e1595e6 | 346 | exitcode = 1; |
e246ba5f NS |
347 | return 0; |
348 | } | |
349 | break; | |
d347f827 NS |
350 | case 'q': |
351 | qflag = 1; | |
352 | break; | |
48c46ee3 NS |
353 | case 'u': |
354 | uflag = 1; | |
355 | break; | |
68c269ca | 356 | #ifdef HAVE_PWRITEV |
10899f17 DC |
357 | case 'V': |
358 | vectors = strtoul(optarg, &sp, 0); | |
359 | if (!sp || sp == optarg) { | |
197d5828 | 360 | printf(_("non-numeric vector count == %s\n"), |
10899f17 | 361 | optarg); |
9e1595e6 | 362 | exitcode = 1; |
10899f17 DC |
363 | return 0; |
364 | } | |
365 | break; | |
68c269ca | 366 | #endif |
5ecb3de2 NS |
367 | case 'w': |
368 | wflag = 1; | |
369 | break; | |
370 | case 'W': | |
371 | Wflag = 1; | |
372 | break; | |
8fb2237e NS |
373 | case 'Z': |
374 | zeed = strtoul(optarg, &sp, 0); | |
375 | if (!sp || sp == optarg) { | |
376 | printf(_("non-numeric seed -- %s\n"), optarg); | |
9e1595e6 | 377 | exitcode = 1; |
8fb2237e NS |
378 | return 0; |
379 | } | |
380 | break; | |
e246ba5f | 381 | default: |
68c269ca | 382 | /* Handle ifdef'd-out options above */ |
9e1595e6 | 383 | exitcode = 1; |
68c269ca GR |
384 | if (c != '?') |
385 | printf(_("%s: command -%c not supported\n"), argv[0], c); | |
386 | else | |
387 | command_usage(&pwrite_cmd); | |
388 | return 0; | |
e246ba5f NS |
389 | } |
390 | } | |
9e1595e6 DC |
391 | if (((skip || dflag) && !infile) || (optind != argc - 2)) { |
392 | exitcode = 1; | |
48c46ee3 | 393 | return command_usage(&pwrite_cmd); |
9e1595e6 DC |
394 | } |
395 | if (infile && direction != IO_FORWARD) { | |
396 | exitcode = 1; | |
8fb2237e | 397 | return command_usage(&pwrite_cmd); |
9e1595e6 | 398 | } |
8fb2237e | 399 | offset = cvtnum(fsblocksize, fssectsize, argv[optind]); |
638473d8 | 400 | if (offset < 0) { |
e246ba5f | 401 | printf(_("non-numeric offset argument -- %s\n"), argv[optind]); |
9e1595e6 | 402 | exitcode = 1; |
e246ba5f NS |
403 | return 0; |
404 | } | |
405 | optind++; | |
8fb2237e | 406 | count = cvtnum(fsblocksize, fssectsize, argv[optind]); |
638473d8 | 407 | if (count < 0) { |
e246ba5f | 408 | printf(_("non-numeric length argument -- %s\n"), argv[optind]); |
9e1595e6 | 409 | exitcode = 1; |
e246ba5f NS |
410 | return 0; |
411 | } | |
412 | ||
9e1595e6 DC |
413 | if (alloc_buffer(bsize, uflag, seed) < 0) { |
414 | exitcode = 1; | |
e246ba5f | 415 | return 0; |
9e1595e6 | 416 | } |
e246ba5f | 417 | |
24cd0fec | 418 | c = IO_READONLY | (dflag ? IO_DIRECT : 0); |
9e1595e6 DC |
419 | if (infile && ((fd = openfile(infile, NULL, c, 0, NULL)) < 0)) { |
420 | exitcode = 1; | |
e246ba5f | 421 | return 0; |
9e1595e6 | 422 | } |
e246ba5f | 423 | |
92d9b902 | 424 | gettimeofday(&t1, NULL); |
8fb2237e NS |
425 | switch (direction) { |
426 | case IO_RANDOM: | |
427 | if (!zeed) /* srandom seed */ | |
428 | zeed = time(NULL); | |
c5deeac9 | 429 | c = write_random(offset, count, zeed, &total, pwritev2_flags); |
8fb2237e NS |
430 | break; |
431 | case IO_FORWARD: | |
c5deeac9 GR |
432 | c = write_buffer(offset, count, bsize, fd, skip, &total, |
433 | pwritev2_flags); | |
8fb2237e NS |
434 | break; |
435 | case IO_BACKWARD: | |
c5deeac9 | 436 | c = write_backward(offset, &count, &total, pwritev2_flags); |
8fb2237e | 437 | break; |
43ba1d61 GR |
438 | case IO_ONCE: |
439 | c = write_once(offset, count, &total, pwritev2_flags); | |
440 | break; | |
8fb2237e | 441 | default: |
9234d416 | 442 | total = 0; |
8fb2237e NS |
443 | ASSERT(0); |
444 | } | |
9e1595e6 DC |
445 | if (c < 0) { |
446 | exitcode = 1; | |
d347f827 | 447 | goto done; |
9e1595e6 | 448 | } |
74919c4e LB |
449 | if (Wflag) { |
450 | if (fsync(file->fd) < 0) { | |
451 | perror("fsync"); | |
9e1595e6 | 452 | exitcode = 1; |
74919c4e LB |
453 | goto done; |
454 | } | |
455 | } | |
456 | if (wflag) { | |
457 | if (fdatasync(file->fd) < 0) { | |
458 | perror("fdatasync"); | |
9e1595e6 | 459 | exitcode = 1; |
74919c4e LB |
460 | goto done; |
461 | } | |
462 | } | |
9e1595e6 | 463 | |
d347f827 NS |
464 | if (qflag) |
465 | goto done; | |
92d9b902 NS |
466 | gettimeofday(&t2, NULL); |
467 | t2 = tsub(t2, t1); | |
468 | ||
a9b61ce9 DW |
469 | report_io_times("wrote", &t2, (long long)offset, count, total, c, |
470 | Cflag); | |
d347f827 | 471 | done: |
886578b4 LM |
472 | if (infile) |
473 | close(fd); | |
e246ba5f NS |
474 | return 0; |
475 | } | |
476 | ||
477 | void | |
478 | pwrite_init(void) | |
479 | { | |
ad765595 AM |
480 | pwrite_cmd.name = "pwrite"; |
481 | pwrite_cmd.altname = "w"; | |
e246ba5f NS |
482 | pwrite_cmd.cfunc = pwrite_f; |
483 | pwrite_cmd.argmin = 2; | |
484 | pwrite_cmd.argmax = -1; | |
48c46ee3 | 485 | pwrite_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; |
e246ba5f | 486 | pwrite_cmd.args = |
0388cfb1 | 487 | _("[-i infile [-qdDwNOW] [-s skip]] [-b bs] [-S seed] [-FBR [-Z N]] [-V N] off len"); |
e246ba5f NS |
488 | pwrite_cmd.oneline = |
489 | _("writes a number of bytes at a specified offset"); | |
490 | pwrite_cmd.help = pwrite_help; | |
491 | ||
492 | add_command(&pwrite_cmd); | |
493 | } |