]>
Commit | Line | Data |
---|---|---|
e246ba5f | 1 | /* |
da23017d NS |
2 | * Copyright (c) 2003-2005 Silicon Graphics, Inc. |
3 | * All Rights Reserved. | |
dfc130f3 | 4 | * |
da23017d NS |
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 | |
e246ba5f | 7 | * published by the Free Software Foundation. |
dfc130f3 | 8 | * |
da23017d NS |
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. | |
dfc130f3 | 13 | * |
da23017d NS |
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 | |
e246ba5f NS |
17 | */ |
18 | ||
64a93371 | 19 | #include <sys/uio.h> |
6b803e5a CH |
20 | #include "command.h" |
21 | #include "input.h" | |
e246ba5f | 22 | #include "init.h" |
48c46ee3 | 23 | #include "io.h" |
e246ba5f NS |
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" | |
48c46ee3 | 35 | " 'pwrite 512 20' - writes 20 bytes at 512 bytes into the open file\n" |
e246ba5f NS |
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" | |
e246ba5f | 39 | " The writes are performed in sequential blocks starting at offset, with the\n" |
8fb2237e NS |
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" | |
197d5828 | 53 | #ifdef HAVE_PWRITEV |
10899f17 | 54 | " -V N -- use vectored IO with N iovecs of blocksize each (pwritev)\n" |
197d5828 | 55 | #endif |
e246ba5f NS |
56 | "\n")); |
57 | } | |
58 | ||
197d5828 | 59 | #ifdef HAVE_PWRITEV |
10899f17 | 60 | static int |
197d5828 | 61 | do_pwritev( |
10899f17 DC |
62 | int fd, |
63 | off64_t offset, | |
64 | ssize_t count, | |
65 | ssize_t buffer_size) | |
66 | { | |
197d5828 ES |
67 | int vecs = 0; |
68 | ssize_t oldlen = 0; | |
69 | ssize_t bytes = 0; | |
10899f17 DC |
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 | } | |
197d5828 ES |
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 pwrite64(fd, buffer, min(count, buffer_size), offset); | |
105 | ||
106 | return do_pwritev(fd, offset, count, buffer_size); | |
107 | } | |
108 | ||
8fb2237e NS |
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) { | |
2ab8ecbe DM |
131 | if (range) |
132 | off = ((offset + (random() % range)) / buffersize) * | |
133 | buffersize; | |
134 | else | |
135 | off = offset; | |
10899f17 | 136 | bytes = do_pwrite(file->fd, off, buffersize, buffersize); |
8fb2237e NS |
137 | if (bytes == 0) |
138 | break; | |
139 | if (bytes < 0) { | |
140 | perror("pwrite64"); | |
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; | |
10899f17 | 174 | bytes = do_pwrite(file->fd, off, bytes_requested, buffersize); |
8fb2237e NS |
175 | if (bytes == 0) |
176 | return ops; | |
177 | if (bytes < 0) { | |
178 | perror("pwrite64"); | |
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; | |
10899f17 | 192 | bytes = do_pwrite(file->fd, off, cnt, buffersize); |
8fb2237e NS |
193 | if (bytes == 0) |
194 | break; | |
195 | if (bytes < 0) { | |
196 | perror("pwrite64"); | |
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 | ||
e246ba5f NS |
208 | static int |
209 | write_buffer( | |
210 | off64_t offset, | |
92d9b902 | 211 | long long count, |
2c2f6d79 | 212 | size_t bs, |
e246ba5f NS |
213 | int fd, |
214 | off64_t skip, | |
92d9b902 | 215 | long long *total) |
e246ba5f | 216 | { |
2c2f6d79 | 217 | ssize_t bytes; |
92d9b902 NS |
218 | long long bar = min(bs, count); |
219 | int ops = 0; | |
e246ba5f NS |
220 | |
221 | *total = 0; | |
8e2153bf | 222 | while (count >= 0) { |
e246ba5f | 223 | if (fd > 0) { /* input file given, read buffer first */ |
92d9b902 | 224 | if (read_buffer(fd, skip + *total, bs, &bar, 0, 1) < 0) |
e246ba5f NS |
225 | break; |
226 | } | |
10899f17 | 227 | bytes = do_pwrite(file->fd, offset, count, bar); |
e246ba5f NS |
228 | if (bytes == 0) |
229 | break; | |
230 | if (bytes < 0) { | |
231 | perror("pwrite64"); | |
92d9b902 | 232 | return -1; |
e246ba5f | 233 | } |
92d9b902 | 234 | ops++; |
e246ba5f | 235 | *total += bytes; |
10899f17 | 236 | if (bytes < min(count, bar)) |
e246ba5f NS |
237 | break; |
238 | offset += bytes; | |
239 | count -= bytes; | |
8e2153bf ES |
240 | if (count == 0) |
241 | break; | |
e246ba5f | 242 | } |
92d9b902 | 243 | return ops; |
e246ba5f NS |
244 | } |
245 | ||
246 | static int | |
247 | pwrite_f( | |
248 | int argc, | |
249 | char **argv) | |
250 | { | |
8fb2237e | 251 | size_t bsize; |
e246ba5f | 252 | off64_t offset, skip = 0; |
2c2f6d79 | 253 | long long count, total, tmp; |
8fb2237e | 254 | unsigned int zeed = 0, seed = 0xcdcdcdcd; |
d026b19e | 255 | size_t fsblocksize, fssectsize; |
92d9b902 | 256 | struct timeval t1, t2; |
e246ba5f | 257 | char *sp, *infile = NULL; |
d347f827 | 258 | int Cflag, qflag, uflag, dflag, wflag, Wflag; |
8fb2237e | 259 | int direction = IO_FORWARD; |
5c7bef67 | 260 | int c, fd = -1; |
e246ba5f | 261 | |
d347f827 | 262 | Cflag = qflag = uflag = dflag = wflag = Wflag = 0; |
8fb2237e NS |
263 | init_cvtnum(&fsblocksize, &fssectsize); |
264 | bsize = fsblocksize; | |
265 | ||
00d50425 | 266 | while ((c = getopt(argc, argv, "b:BCdf:Fi:qRs:S:uV:wWZ:")) != EOF) { |
e246ba5f NS |
267 | switch (c) { |
268 | case 'b': | |
8fb2237e | 269 | tmp = cvtnum(fsblocksize, fssectsize, optarg); |
2c2f6d79 | 270 | if (tmp < 0) { |
e246ba5f NS |
271 | printf(_("non-numeric bsize -- %s\n"), optarg); |
272 | return 0; | |
273 | } | |
8fb2237e | 274 | bsize = tmp; |
e246ba5f | 275 | break; |
5c7bef67 NS |
276 | case 'C': |
277 | Cflag = 1; | |
278 | break; | |
8fb2237e NS |
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; | |
e246ba5f NS |
288 | case 'd': |
289 | dflag = 1; | |
290 | break; | |
291 | case 'f': | |
292 | case 'i': | |
293 | infile = optarg; | |
294 | break; | |
295 | case 's': | |
8fb2237e | 296 | skip = cvtnum(fsblocksize, fssectsize, optarg); |
638473d8 | 297 | if (skip < 0) { |
e246ba5f NS |
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; | |
d347f827 NS |
309 | case 'q': |
310 | qflag = 1; | |
311 | break; | |
48c46ee3 NS |
312 | case 'u': |
313 | uflag = 1; | |
314 | break; | |
10899f17 DC |
315 | case 'V': |
316 | vectors = strtoul(optarg, &sp, 0); | |
317 | if (!sp || sp == optarg) { | |
197d5828 | 318 | printf(_("non-numeric vector count == %s\n"), |
10899f17 DC |
319 | optarg); |
320 | return 0; | |
321 | } | |
322 | break; | |
5ecb3de2 NS |
323 | case 'w': |
324 | wflag = 1; | |
325 | break; | |
326 | case 'W': | |
327 | Wflag = 1; | |
328 | break; | |
8fb2237e NS |
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; | |
e246ba5f | 336 | default: |
48c46ee3 | 337 | return command_usage(&pwrite_cmd); |
e246ba5f NS |
338 | } |
339 | } | |
8fb2237e | 340 | if (((skip || dflag) && !infile) || (optind != argc - 2)) |
48c46ee3 | 341 | return command_usage(&pwrite_cmd); |
8fb2237e NS |
342 | if (infile && direction != IO_FORWARD) |
343 | return command_usage(&pwrite_cmd); | |
344 | offset = cvtnum(fsblocksize, fssectsize, argv[optind]); | |
638473d8 | 345 | if (offset < 0) { |
e246ba5f NS |
346 | printf(_("non-numeric offset argument -- %s\n"), argv[optind]); |
347 | return 0; | |
348 | } | |
349 | optind++; | |
8fb2237e | 350 | count = cvtnum(fsblocksize, fssectsize, argv[optind]); |
638473d8 | 351 | if (count < 0) { |
e246ba5f NS |
352 | printf(_("non-numeric length argument -- %s\n"), argv[optind]); |
353 | return 0; | |
354 | } | |
355 | ||
8fb2237e | 356 | if (alloc_buffer(bsize, uflag, seed) < 0) |
e246ba5f NS |
357 | return 0; |
358 | ||
24cd0fec | 359 | c = IO_READONLY | (dflag ? IO_DIRECT : 0); |
48c46ee3 | 360 | if (infile && ((fd = openfile(infile, NULL, c, 0)) < 0)) |
e246ba5f NS |
361 | return 0; |
362 | ||
92d9b902 | 363 | gettimeofday(&t1, NULL); |
8fb2237e NS |
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: | |
9234d416 | 377 | total = 0; |
8fb2237e NS |
378 | ASSERT(0); |
379 | } | |
d347f827 NS |
380 | if (c < 0) |
381 | goto done; | |
5ecb3de2 NS |
382 | if (Wflag) |
383 | fsync(file->fd); | |
384 | if (wflag) | |
385 | fdatasync(file->fd); | |
d347f827 NS |
386 | if (qflag) |
387 | goto done; | |
92d9b902 NS |
388 | gettimeofday(&t2, NULL); |
389 | t2 = tsub(t2, t1); | |
390 | ||
a9b61ce9 DW |
391 | report_io_times("wrote", &t2, (long long)offset, count, total, c, |
392 | Cflag); | |
d347f827 | 393 | done: |
886578b4 LM |
394 | if (infile) |
395 | close(fd); | |
e246ba5f NS |
396 | return 0; |
397 | } | |
398 | ||
399 | void | |
400 | pwrite_init(void) | |
401 | { | |
ad765595 AM |
402 | pwrite_cmd.name = "pwrite"; |
403 | pwrite_cmd.altname = "w"; | |
e246ba5f NS |
404 | pwrite_cmd.cfunc = pwrite_f; |
405 | pwrite_cmd.argmin = 2; | |
406 | pwrite_cmd.argmax = -1; | |
48c46ee3 | 407 | pwrite_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; |
e246ba5f | 408 | pwrite_cmd.args = |
10899f17 | 409 | _("[-i infile [-d] [-s skip]] [-b bs] [-S seed] [-wW] [-FBR [-Z N]] [-V N] off len"); |
e246ba5f NS |
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 | } |