]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - io/pwrite.c
xfs_io: support the new getfsmap ioctl
[thirdparty/xfsprogs-dev.git] / io / pwrite.c
1 /*
2 * Copyright (c) 2003-2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include <sys/uio.h>
20 #include "command.h"
21 #include "input.h"
22 #include "init.h"
23 #include "io.h"
24
25 static cmdinfo_t pwrite_cmd;
26
27 static void
28 pwrite_help(void)
29 {
30 printf(_(
31 "\n"
32 " writes a range of bytes (in block size increments) from the given offset\n"
33 "\n"
34 " Example:\n"
35 " 'pwrite 512 20' - writes 20 bytes at 512 bytes into the open file\n"
36 "\n"
37 " Writes into a segment of the currently open file, using either a buffer\n"
38 " filled with a set pattern (0xcdcdcdcd) or data read from an input file.\n"
39 " The writes are performed in sequential blocks starting at offset, with the\n"
40 " blocksize tunable using the -b option (default blocksize is 4096 bytes),\n"
41 " unless a different write pattern is requested.\n"
42 " -S -- use an alternate seed number for filling the write buffer\n"
43 " -i -- input file, source of data to write (used when writing forward)\n"
44 " -d -- open the input file for direct IO\n"
45 " -s -- skip a number of bytes at the start of the input file\n"
46 " -w -- call fdatasync(2) at the end (included in timing results)\n"
47 " -W -- call fsync(2) at the end (included in timing results)\n"
48 " -B -- write backwards through the range from offset (backwards N bytes)\n"
49 " -F -- write forwards through the range of bytes from offset (default)\n"
50 " -R -- write at random offsets in the specified range of bytes\n"
51 " -Z N -- zeed the random number generator (used when writing randomly)\n"
52 " (heh, zorry, the -s/-S arguments were already in use in pwrite)\n"
53 #ifdef HAVE_PWRITEV
54 " -V N -- use vectored IO with N iovecs of blocksize each (pwritev)\n"
55 #endif
56 "\n"));
57 }
58
59 #ifdef HAVE_PWRITEV
60 static int
61 do_pwritev(
62 int fd,
63 off64_t offset,
64 ssize_t count,
65 ssize_t buffer_size)
66 {
67 int vecs = 0;
68 ssize_t oldlen = 0;
69 ssize_t bytes = 0;
70
71 /* trim the iovec if necessary */
72 if (count < buffersize) {
73 size_t len = 0;
74 while (len + iov[vecs].iov_len < count) {
75 len += iov[vecs].iov_len;
76 vecs++;
77 }
78 oldlen = iov[vecs].iov_len;
79 iov[vecs].iov_len = count - len;
80 vecs++;
81 } else {
82 vecs = vectors;
83 }
84 bytes = pwritev(fd, iov, vectors, offset);
85
86 /* restore trimmed iov */
87 if (oldlen)
88 iov[vecs - 1].iov_len = oldlen;
89
90 return bytes;
91 }
92 #else
93 #define do_pwritev(fd, offset, count, buffer_size) (0)
94 #endif
95
96 static int
97 do_pwrite(
98 int fd,
99 off64_t offset,
100 ssize_t count,
101 ssize_t buffer_size)
102 {
103 if (!vectors)
104 return pwrite(fd, buffer, min(count, buffer_size), offset);
105
106 return do_pwritev(fd, offset, count, buffer_size);
107 }
108
109 static int
110 write_random(
111 off64_t offset,
112 long long count,
113 unsigned int seed,
114 long long *total)
115 {
116 off64_t off, range;
117 ssize_t bytes;
118 int ops = 0;
119
120 srandom(seed);
121 if ((bytes = (offset % buffersize)))
122 offset -= bytes;
123 offset = max(0, offset);
124 if ((bytes = (count % buffersize)))
125 count += bytes;
126 count = max(buffersize, count);
127 range = count - buffersize;
128
129 *total = 0;
130 while (count > 0) {
131 if (range)
132 off = ((offset + (random() % range)) / buffersize) *
133 buffersize;
134 else
135 off = offset;
136 bytes = do_pwrite(file->fd, off, buffersize, buffersize);
137 if (bytes == 0)
138 break;
139 if (bytes < 0) {
140 perror("pwrite");
141 return -1;
142 }
143 ops++;
144 *total += bytes;
145 if (bytes < buffersize)
146 break;
147 count -= bytes;
148 }
149 return ops;
150 }
151
152 static int
153 write_backward(
154 off64_t offset,
155 long long *count,
156 long long *total)
157 {
158 off64_t end, off = offset;
159 ssize_t bytes = 0, bytes_requested;
160 long long cnt = *count;
161 int ops = 0;
162
163 if ((end = off - cnt) < 0) {
164 cnt += end; /* subtraction, end is negative */
165 end = 0;
166 }
167 *total = 0;
168 *count = cnt;
169
170 /* Do initial unaligned write if needed */
171 if ((bytes_requested = (off % buffersize))) {
172 bytes_requested = min(cnt, bytes_requested);
173 off -= bytes_requested;
174 bytes = do_pwrite(file->fd, off, bytes_requested, buffersize);
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, buffersize);
191 off -= bytes_requested;
192 bytes = do_pwrite(file->fd, off, cnt, buffersize);
193 if (bytes == 0)
194 break;
195 if (bytes < 0) {
196 perror("pwrite");
197 return -1;
198 }
199 ops++;
200 *total += bytes;
201 if (bytes < bytes_requested)
202 break;
203 cnt -= bytes;
204 }
205 return ops;
206 }
207
208 static int
209 write_buffer(
210 off64_t offset,
211 long long count,
212 size_t bs,
213 int fd,
214 off64_t skip,
215 long long *total)
216 {
217 ssize_t bytes;
218 long long bar = min(bs, count);
219 int ops = 0;
220
221 *total = 0;
222 while (count >= 0) {
223 if (fd > 0) { /* input file given, read buffer first */
224 if (read_buffer(fd, skip + *total, bs, &bar, 0, 1) < 0)
225 break;
226 }
227 bytes = do_pwrite(file->fd, offset, count, bar);
228 if (bytes == 0)
229 break;
230 if (bytes < 0) {
231 perror("pwrite");
232 return -1;
233 }
234 ops++;
235 *total += bytes;
236 if (bytes < min(count, bar))
237 break;
238 offset += bytes;
239 count -= bytes;
240 if (count == 0)
241 break;
242 }
243 return ops;
244 }
245
246 static int
247 pwrite_f(
248 int argc,
249 char **argv)
250 {
251 size_t bsize;
252 off64_t offset, skip = 0;
253 long long count, total, tmp;
254 unsigned int zeed = 0, seed = 0xcdcdcdcd;
255 size_t fsblocksize, fssectsize;
256 struct timeval t1, t2;
257 char *sp, *infile = NULL;
258 int Cflag, qflag, uflag, dflag, wflag, Wflag;
259 int direction = IO_FORWARD;
260 int c, fd = -1;
261
262 Cflag = qflag = uflag = dflag = wflag = Wflag = 0;
263 init_cvtnum(&fsblocksize, &fssectsize);
264 bsize = fsblocksize;
265
266 while ((c = getopt(argc, argv, "b:BCdf:Fi:qRs:S:uV:wWZ:")) != EOF) {
267 switch (c) {
268 case 'b':
269 tmp = cvtnum(fsblocksize, fssectsize, optarg);
270 if (tmp < 0) {
271 printf(_("non-numeric bsize -- %s\n"), optarg);
272 return 0;
273 }
274 bsize = tmp;
275 break;
276 case 'C':
277 Cflag = 1;
278 break;
279 case 'F':
280 direction = IO_FORWARD;
281 break;
282 case 'B':
283 direction = IO_BACKWARD;
284 break;
285 case 'R':
286 direction = IO_RANDOM;
287 break;
288 case 'd':
289 dflag = 1;
290 break;
291 case 'f':
292 case 'i':
293 infile = optarg;
294 break;
295 case 's':
296 skip = cvtnum(fsblocksize, fssectsize, optarg);
297 if (skip < 0) {
298 printf(_("non-numeric skip -- %s\n"), optarg);
299 return 0;
300 }
301 break;
302 case 'S':
303 seed = strtoul(optarg, &sp, 0);
304 if (!sp || sp == optarg) {
305 printf(_("non-numeric seed -- %s\n"), optarg);
306 return 0;
307 }
308 break;
309 case 'q':
310 qflag = 1;
311 break;
312 case 'u':
313 uflag = 1;
314 break;
315 case 'V':
316 vectors = strtoul(optarg, &sp, 0);
317 if (!sp || sp == optarg) {
318 printf(_("non-numeric vector count == %s\n"),
319 optarg);
320 return 0;
321 }
322 break;
323 case 'w':
324 wflag = 1;
325 break;
326 case 'W':
327 Wflag = 1;
328 break;
329 case 'Z':
330 zeed = strtoul(optarg, &sp, 0);
331 if (!sp || sp == optarg) {
332 printf(_("non-numeric seed -- %s\n"), optarg);
333 return 0;
334 }
335 break;
336 default:
337 return command_usage(&pwrite_cmd);
338 }
339 }
340 if (((skip || dflag) && !infile) || (optind != argc - 2))
341 return command_usage(&pwrite_cmd);
342 if (infile && direction != IO_FORWARD)
343 return command_usage(&pwrite_cmd);
344 offset = cvtnum(fsblocksize, fssectsize, argv[optind]);
345 if (offset < 0) {
346 printf(_("non-numeric offset argument -- %s\n"), argv[optind]);
347 return 0;
348 }
349 optind++;
350 count = cvtnum(fsblocksize, fssectsize, argv[optind]);
351 if (count < 0) {
352 printf(_("non-numeric length argument -- %s\n"), argv[optind]);
353 return 0;
354 }
355
356 if (alloc_buffer(bsize, uflag, seed) < 0)
357 return 0;
358
359 c = IO_READONLY | (dflag ? IO_DIRECT : 0);
360 if (infile && ((fd = openfile(infile, NULL, c, 0, NULL)) < 0))
361 return 0;
362
363 gettimeofday(&t1, NULL);
364 switch (direction) {
365 case IO_RANDOM:
366 if (!zeed) /* srandom seed */
367 zeed = time(NULL);
368 c = write_random(offset, count, zeed, &total);
369 break;
370 case IO_FORWARD:
371 c = write_buffer(offset, count, bsize, fd, skip, &total);
372 break;
373 case IO_BACKWARD:
374 c = write_backward(offset, &count, &total);
375 break;
376 default:
377 total = 0;
378 ASSERT(0);
379 }
380 if (c < 0)
381 goto done;
382 if (Wflag)
383 fsync(file->fd);
384 if (wflag)
385 fdatasync(file->fd);
386 if (qflag)
387 goto done;
388 gettimeofday(&t2, NULL);
389 t2 = tsub(t2, t1);
390
391 report_io_times("wrote", &t2, (long long)offset, count, total, c,
392 Cflag);
393 done:
394 if (infile)
395 close(fd);
396 return 0;
397 }
398
399 void
400 pwrite_init(void)
401 {
402 pwrite_cmd.name = "pwrite";
403 pwrite_cmd.altname = "w";
404 pwrite_cmd.cfunc = pwrite_f;
405 pwrite_cmd.argmin = 2;
406 pwrite_cmd.argmax = -1;
407 pwrite_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
408 pwrite_cmd.args =
409 _("[-i infile [-d] [-s skip]] [-b bs] [-S seed] [-wW] [-FBR [-Z N]] [-V N] off len");
410 pwrite_cmd.oneline =
411 _("writes a number of bytes at a specified offset");
412 pwrite_cmd.help = pwrite_help;
413
414 add_command(&pwrite_cmd);
415 }