]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - io/pwrite.c
configure: don't check for preadv and pwritev
[thirdparty/xfsprogs-dev.git] / io / pwrite.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2003-2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5 */
6
7 #include <sys/uio.h>
8 #include "command.h"
9 #include "input.h"
10 #include "init.h"
11 #include "io.h"
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"
23 " 'pwrite 512 20' - writes 20 bytes at 512 bytes into the open file\n"
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"
27 " The writes are performed in sequential blocks starting at offset, with the\n"
28 " blocksize tunable using the -b option (default blocksize is 4096 bytes),\n"
29 " unless a different write pattern is requested.\n"
30 " -q -- quiet mode, do not write anything to standard output.\n"
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"
39 " -O -- perform pwrite call once and return (maybe partial) bytes written\n"
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"
43 " -V N -- use vectored IO with N iovecs of blocksize each (pwritev)\n"
44 #ifdef HAVE_PWRITEV2
45 " -N -- Perform the pwritev2() with RWF_NOWAIT\n"
46 " -D -- Perform the pwritev2() with RWF_DSYNC\n"
47 #endif
48 "\n"));
49 }
50
51 static ssize_t
52 do_pwritev(
53 int fd,
54 off_t offset,
55 long long count,
56 int pwritev2_flags)
57 {
58 int vecs = 0;
59 ssize_t oldlen = 0;
60 ssize_t bytes = 0;
61
62 /* trim the iovec if necessary */
63 if (count < io_buffersize) {
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 }
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
81 bytes = pwritev(fd, iov, vectors, offset);
82 #endif
83
84 /* restore trimmed iov */
85 if (oldlen)
86 iov[vecs - 1].iov_len = oldlen;
87
88 return bytes;
89 }
90
91 static ssize_t
92 do_pwrite(
93 int fd,
94 off_t offset,
95 long long count,
96 size_t buffer_size,
97 int pwritev2_flags)
98 {
99 if (!vectors)
100 return pwrite(fd, io_buffer, min(count, buffer_size), offset);
101
102 return do_pwritev(fd, offset, count, pwritev2_flags);
103 }
104
105 static int
106 write_random(
107 off_t offset,
108 long long count,
109 unsigned int seed,
110 long long *total,
111 int pwritev2_flags)
112 {
113 off_t off, range;
114 ssize_t bytes;
115 int ops = 0;
116
117 srandom(seed);
118 if ((bytes = (offset % io_buffersize)))
119 offset -= bytes;
120 offset = max(0, offset);
121 if ((bytes = (count % io_buffersize)))
122 count += bytes;
123 count = max(io_buffersize, count);
124 range = count - io_buffersize;
125
126 *total = 0;
127 while (count > 0) {
128 if (range)
129 off = ((offset + (random() % range)) / io_buffersize) *
130 io_buffersize;
131 else
132 off = offset;
133 bytes = do_pwrite(file->fd, off, io_buffersize, io_buffersize,
134 pwritev2_flags);
135 if (bytes == 0)
136 break;
137 if (bytes < 0) {
138 perror("pwrite");
139 return -1;
140 }
141 ops++;
142 *total += bytes;
143 if (bytes < io_buffersize)
144 break;
145 count -= bytes;
146 }
147 return ops;
148 }
149
150 static int
151 write_backward(
152 off_t offset,
153 long long *count,
154 long long *total,
155 int pwritev2_flags)
156 {
157 off_t end, off = offset;
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 */
170 if ((bytes_requested = (off % io_buffersize))) {
171 bytes_requested = min(cnt, bytes_requested);
172 off -= bytes_requested;
173 bytes = do_pwrite(file->fd, off, bytes_requested, io_buffersize,
174 pwritev2_flags);
175 if (bytes == 0)
176 return ops;
177 if (bytes < 0) {
178 perror("pwrite");
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) {
190 bytes_requested = min(cnt, io_buffersize);
191 off -= bytes_requested;
192 bytes = do_pwrite(file->fd, off, cnt, io_buffersize,
193 pwritev2_flags);
194 if (bytes == 0)
195 break;
196 if (bytes < 0) {
197 perror("pwrite");
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
209 static int
210 write_buffer(
211 off_t offset,
212 long long count,
213 size_t bs,
214 int fd,
215 off_t skip,
216 long long *total,
217 int pwritev2_flags)
218 {
219 ssize_t bytes;
220 long long bar = min(bs, count);
221 int ops = 0;
222
223 *total = 0;
224 while (count >= 0) {
225 if (fd > 0) { /* input file given, read buffer first */
226 if (read_buffer(fd, skip + *total, bs, &bar, 0, 1) < 0)
227 break;
228 }
229 bytes = do_pwrite(file->fd, offset, count, bar, pwritev2_flags);
230 if (bytes == 0)
231 break;
232 if (bytes < 0) {
233 perror("pwrite");
234 return -1;
235 }
236 ops++;
237 *total += bytes;
238 if (bytes < min(count, bar))
239 break;
240 offset += bytes;
241 count -= bytes;
242 if (count == 0)
243 break;
244 }
245 return ops;
246 }
247
248 static int
249 write_once(
250 off_t offset,
251 long long count,
252 long long *total,
253 int pwritev2_flags)
254 {
255 ssize_t bytes;
256 bytes = do_pwrite(file->fd, offset, count, count, pwritev2_flags);
257 if (bytes < 0) {
258 perror("pwrite");
259 return -1;
260 }
261 *total = bytes;
262 return 1;
263 }
264
265
266 static int
267 pwrite_f(
268 int argc,
269 char **argv)
270 {
271 size_t bsize;
272 off_t offset, skip = 0;
273 long long count, total, tmp;
274 unsigned int zeed = 0, seed = 0xcdcdcdcd;
275 size_t fsblocksize, fssectsize;
276 struct timeval t1, t2;
277 char *sp, *infile = NULL;
278 int Cflag, qflag, uflag, dflag, wflag, Wflag;
279 int direction = IO_FORWARD;
280 int c, fd = -1;
281 int pwritev2_flags = 0;
282
283 Cflag = qflag = uflag = dflag = wflag = Wflag = 0;
284 init_cvtnum(&fsblocksize, &fssectsize);
285 bsize = fsblocksize;
286
287 while ((c = getopt(argc, argv, "b:BCdDf:Fi:NqRs:OS:uV:wWZ:")) != EOF) {
288 switch (c) {
289 case 'b':
290 tmp = cvtnum(fsblocksize, fssectsize, optarg);
291 if (tmp < 0) {
292 printf(_("non-numeric bsize -- %s\n"), optarg);
293 exitcode = 1;
294 return 0;
295 }
296 bsize = tmp;
297 break;
298 case 'C':
299 Cflag = 1;
300 break;
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;
310 case 'O':
311 direction = IO_ONCE;
312 break;
313 case 'd':
314 dflag = 1;
315 break;
316 case 'f':
317 case 'i':
318 infile = optarg;
319 break;
320 #ifdef HAVE_PWRITEV2
321 case 'N':
322 pwritev2_flags |= RWF_NOWAIT;
323 break;
324 case 'D':
325 pwritev2_flags |= RWF_DSYNC;
326 break;
327 #endif
328 case 's':
329 skip = cvtnum(fsblocksize, fssectsize, optarg);
330 if (skip < 0) {
331 printf(_("non-numeric skip -- %s\n"), optarg);
332 exitcode = 1;
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);
340 exitcode = 1;
341 return 0;
342 }
343 break;
344 case 'q':
345 qflag = 1;
346 break;
347 case 'u':
348 uflag = 1;
349 break;
350 case 'V':
351 vectors = strtoul(optarg, &sp, 0);
352 if (!sp || sp == optarg) {
353 printf(_("non-numeric vector count == %s\n"),
354 optarg);
355 exitcode = 1;
356 return 0;
357 }
358 break;
359 case 'w':
360 wflag = 1;
361 break;
362 case 'W':
363 Wflag = 1;
364 break;
365 case 'Z':
366 zeed = strtoul(optarg, &sp, 0);
367 if (!sp || sp == optarg) {
368 printf(_("non-numeric seed -- %s\n"), optarg);
369 exitcode = 1;
370 return 0;
371 }
372 break;
373 default:
374 /* Handle ifdef'd-out options above */
375 exitcode = 1;
376 if (c != '?')
377 printf(_("%s: command -%c not supported\n"), argv[0], c);
378 else
379 command_usage(&pwrite_cmd);
380 return 0;
381 }
382 }
383 if (((skip || dflag) && !infile) || (optind != argc - 2)) {
384 exitcode = 1;
385 return command_usage(&pwrite_cmd);
386 }
387 if (infile && direction != IO_FORWARD) {
388 exitcode = 1;
389 return command_usage(&pwrite_cmd);
390 }
391 offset = cvtnum(fsblocksize, fssectsize, argv[optind]);
392 if (offset < 0) {
393 printf(_("non-numeric offset argument -- %s\n"), argv[optind]);
394 exitcode = 1;
395 return 0;
396 }
397 optind++;
398 count = cvtnum(fsblocksize, fssectsize, argv[optind]);
399 if (count < 0) {
400 printf(_("non-numeric length argument -- %s\n"), argv[optind]);
401 exitcode = 1;
402 return 0;
403 }
404
405 if (alloc_buffer(bsize, uflag, seed) < 0) {
406 exitcode = 1;
407 return 0;
408 }
409
410 c = IO_READONLY | (dflag ? IO_DIRECT : 0);
411 if (infile && ((fd = openfile(infile, NULL, c, 0, NULL)) < 0)) {
412 exitcode = 1;
413 return 0;
414 }
415
416 gettimeofday(&t1, NULL);
417 switch (direction) {
418 case IO_RANDOM:
419 if (!zeed) /* srandom seed */
420 zeed = time(NULL);
421 c = write_random(offset, count, zeed, &total, pwritev2_flags);
422 break;
423 case IO_FORWARD:
424 c = write_buffer(offset, count, bsize, fd, skip, &total,
425 pwritev2_flags);
426 break;
427 case IO_BACKWARD:
428 c = write_backward(offset, &count, &total, pwritev2_flags);
429 break;
430 case IO_ONCE:
431 c = write_once(offset, count, &total, pwritev2_flags);
432 break;
433 default:
434 total = 0;
435 ASSERT(0);
436 }
437 if (c < 0) {
438 exitcode = 1;
439 goto done;
440 }
441 if (Wflag) {
442 if (fsync(file->fd) < 0) {
443 perror("fsync");
444 exitcode = 1;
445 goto done;
446 }
447 }
448 if (wflag) {
449 if (fdatasync(file->fd) < 0) {
450 perror("fdatasync");
451 exitcode = 1;
452 goto done;
453 }
454 }
455
456 if (qflag)
457 goto done;
458 gettimeofday(&t2, NULL);
459 t2 = tsub(t2, t1);
460
461 report_io_times("wrote", &t2, (long long)offset, count, total, c,
462 Cflag);
463 done:
464 if (infile)
465 close(fd);
466 return 0;
467 }
468
469 void
470 pwrite_init(void)
471 {
472 pwrite_cmd.name = "pwrite";
473 pwrite_cmd.altname = "w";
474 pwrite_cmd.cfunc = pwrite_f;
475 pwrite_cmd.argmin = 2;
476 pwrite_cmd.argmax = -1;
477 pwrite_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
478 pwrite_cmd.args =
479 _("[-i infile [-qdDwNOW] [-s skip]] [-b bs] [-S seed] [-FBR [-Z N]] [-V N] off len");
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 }