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