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