]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - io/pwrite.c
Provide further debugging options and tweaks for analysing the read/write paths.
[thirdparty/xfsprogs-dev.git] / io / pwrite.c
1 /*
2 * Copyright (c) 2003-2005 Silicon Graphics, Inc. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * Further, this software is distributed without any warranty that it is
13 * free of the rightful claim of any third person regarding infringement
14 * or the like. Any license provided herein, whether implied or
15 * otherwise, applies only to this software file. Patent licenses, if
16 * any, provided herein do not apply to combinations of this program with
17 * other software, or any other product whatsoever.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write the Free Software Foundation, Inc., 59
21 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
22 *
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA 94043, or:
25 *
26 * http://www.sgi.com
27 *
28 * For further information regarding this notice, see:
29 *
30 * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
31 */
32
33 #include <xfs/libxfs.h>
34 #include <xfs/command.h>
35 #include <xfs/input.h>
36 #include "init.h"
37 #include "io.h"
38
39 static cmdinfo_t pwrite_cmd;
40
41 static void
42 pwrite_help(void)
43 {
44 printf(_(
45 "\n"
46 " writes a range of bytes (in block size increments) from the given offset\n"
47 "\n"
48 " Example:\n"
49 " 'pwrite 512 20' - writes 20 bytes at 512 bytes into the open file\n"
50 "\n"
51 " Writes into a segment of the currently open file, using either a buffer\n"
52 " filled with a set pattern (0xcdcdcdcd) or data read from an input file.\n"
53 " The writes are performed in sequential blocks starting at offset, with the\n"
54 " blocksize tunable using the -b option (default blocksize is 4096 bytes),\n"
55 " unless a different write pattern is requested.\n"
56 " -S -- use an alternate seed number for filling the write buffer\n"
57 " -i -- input file, source of data to write (used when writing forward)\n"
58 " -d -- open the input file for direct IO\n"
59 " -s -- skip a number of bytes at the start of the input file\n"
60 " -w -- call fdatasync(2) at the end (included in timing results)\n"
61 " -W -- call fsync(2) at the end (included in timing results)\n"
62 " -B -- write backwards through the range from offset (backwards N bytes)\n"
63 " -F -- write forwards through the range of bytes from offset (default)\n"
64 " -R -- write at random offsets in the specified range of bytes\n"
65 " -Z N -- zeed the random number generator (used when writing randomly)\n"
66 " (heh, zorry, the -s/-S arguments were already in use in pwrite)\n"
67 "\n"));
68 }
69
70 static int
71 write_random(
72 off64_t offset,
73 long long count,
74 unsigned int seed,
75 long long *total)
76 {
77 off64_t off, range;
78 ssize_t bytes;
79 int ops = 0;
80
81 srandom(seed);
82 if ((bytes = (offset % buffersize)))
83 offset -= bytes;
84 offset = max(0, offset);
85 if ((bytes = (count % buffersize)))
86 count += bytes;
87 count = max(buffersize, count);
88 range = count - buffersize;
89
90 *total = 0;
91 while (count > 0) {
92 off = ((random() % range) / buffersize) * buffersize;
93 bytes = pwrite64(file->fd, buffer, buffersize, off);
94 if (bytes == 0)
95 break;
96 if (bytes < 0) {
97 perror("pwrite64");
98 return -1;
99 }
100 ops++;
101 *total += bytes;
102 if (bytes < buffersize)
103 break;
104 count -= bytes;
105 }
106 return ops;
107 }
108
109 static int
110 write_backward(
111 off64_t offset,
112 long long *count,
113 long long *total)
114 {
115 off64_t end, off = offset;
116 ssize_t bytes = 0, bytes_requested;
117 long long cnt = *count;
118 int ops = 0;
119
120 if ((end = off - cnt) < 0) {
121 cnt += end; /* subtraction, end is negative */
122 end = 0;
123 }
124 *total = 0;
125 *count = cnt;
126
127 /* Do initial unaligned write if needed */
128 if ((bytes_requested = (off % buffersize))) {
129 bytes_requested = min(cnt, bytes_requested);
130 off -= bytes_requested;
131 bytes = pwrite(file->fd, buffer, bytes_requested, off);
132 if (bytes == 0)
133 return ops;
134 if (bytes < 0) {
135 perror("pwrite64");
136 return -1;
137 }
138 ops++;
139 *total += bytes;
140 if (bytes < bytes_requested)
141 return ops;
142 cnt -= bytes;
143 }
144
145 /* Iterate backward through the rest of the range */
146 while (cnt > end) {
147 bytes_requested = min(cnt, buffersize);
148 off -= bytes_requested;
149 bytes = pwrite64(file->fd, buffer, bytes_requested, off);
150 if (bytes == 0)
151 break;
152 if (bytes < 0) {
153 perror("pwrite64");
154 return -1;
155 }
156 ops++;
157 *total += bytes;
158 if (bytes < bytes_requested)
159 break;
160 cnt -= bytes;
161 }
162 return ops;
163 }
164
165 static int
166 write_buffer(
167 off64_t offset,
168 long long count,
169 size_t bs,
170 int fd,
171 off64_t skip,
172 long long *total)
173 {
174 size_t bytes_requested;
175 ssize_t bytes;
176 long long bar = min(bs, count);
177 int ops = 0;
178
179 *total = 0;
180 while (count > 0) {
181 if (fd > 0) { /* input file given, read buffer first */
182 if (read_buffer(fd, skip + *total, bs, &bar, 0, 1) < 0)
183 break;
184 }
185 bytes_requested = min(bar, count);
186 bytes = pwrite64(file->fd, buffer, bytes_requested, offset);
187 if (bytes == 0)
188 break;
189 if (bytes < 0) {
190 perror("pwrite64");
191 return -1;
192 }
193 ops++;
194 *total += bytes;
195 if (bytes < bytes_requested)
196 break;
197 offset += bytes;
198 count -= bytes;
199 }
200 return ops;
201 }
202
203 static int
204 pwrite_f(
205 int argc,
206 char **argv)
207 {
208 size_t bsize;
209 off64_t offset, skip = 0;
210 long long count, total, tmp;
211 unsigned int zeed = 0, seed = 0xcdcdcdcd;
212 unsigned int fsblocksize, fssectsize;
213 struct timeval t1, t2;
214 char s1[64], s2[64], ts[64];
215 char *sp, *infile = NULL;
216 int Cflag, uflag, dflag, wflag, Wflag;
217 int direction = IO_FORWARD;
218 int c, fd = -1;
219
220 Cflag = uflag = dflag = wflag = Wflag = 0;
221 init_cvtnum(&fsblocksize, &fssectsize);
222 bsize = fsblocksize;
223
224 while ((c = getopt(argc, argv, "b:Cdf:i:s:S:uwWZ:")) != EOF) {
225 switch (c) {
226 case 'b':
227 tmp = cvtnum(fsblocksize, fssectsize, optarg);
228 if (tmp < 0) {
229 printf(_("non-numeric bsize -- %s\n"), optarg);
230 return 0;
231 }
232 bsize = tmp;
233 break;
234 case 'C':
235 Cflag = 1;
236 break;
237 case 'F':
238 direction = IO_FORWARD;
239 break;
240 case 'B':
241 direction = IO_BACKWARD;
242 break;
243 case 'R':
244 direction = IO_RANDOM;
245 break;
246 case 'd':
247 dflag = 1;
248 break;
249 case 'f':
250 case 'i':
251 infile = optarg;
252 break;
253 case 's':
254 skip = cvtnum(fsblocksize, fssectsize, optarg);
255 if (skip < 0) {
256 printf(_("non-numeric skip -- %s\n"), optarg);
257 return 0;
258 }
259 break;
260 case 'S':
261 seed = strtoul(optarg, &sp, 0);
262 if (!sp || sp == optarg) {
263 printf(_("non-numeric seed -- %s\n"), optarg);
264 return 0;
265 }
266 break;
267 case 'u':
268 uflag = 1;
269 break;
270 case 'w':
271 wflag = 1;
272 break;
273 case 'W':
274 Wflag = 1;
275 break;
276 case 'Z':
277 zeed = strtoul(optarg, &sp, 0);
278 if (!sp || sp == optarg) {
279 printf(_("non-numeric seed -- %s\n"), optarg);
280 return 0;
281 }
282 break;
283 default:
284 return command_usage(&pwrite_cmd);
285 }
286 }
287 if (((skip || dflag) && !infile) || (optind != argc - 2))
288 return command_usage(&pwrite_cmd);
289 if (infile && direction != IO_FORWARD)
290 return command_usage(&pwrite_cmd);
291 offset = cvtnum(fsblocksize, fssectsize, argv[optind]);
292 if (offset < 0) {
293 printf(_("non-numeric offset argument -- %s\n"), argv[optind]);
294 return 0;
295 }
296 optind++;
297 count = cvtnum(fsblocksize, fssectsize, argv[optind]);
298 if (count < 0) {
299 printf(_("non-numeric length argument -- %s\n"), argv[optind]);
300 return 0;
301 }
302
303 if (alloc_buffer(bsize, uflag, seed) < 0)
304 return 0;
305
306 c = IO_READONLY | (dflag ? IO_DIRECT : 0);
307 if (infile && ((fd = openfile(infile, NULL, c, 0)) < 0))
308 return 0;
309
310 gettimeofday(&t1, NULL);
311 switch (direction) {
312 case IO_RANDOM:
313 if (!zeed) /* srandom seed */
314 zeed = time(NULL);
315 c = write_random(offset, count, zeed, &total);
316 break;
317 case IO_FORWARD:
318 c = write_buffer(offset, count, bsize, fd, skip, &total);
319 break;
320 case IO_BACKWARD:
321 c = write_backward(offset, &count, &total);
322 break;
323 default:
324 ASSERT(0);
325 }
326 if (c < 0) {
327 close(fd);
328 return 0;
329 }
330 if (Wflag)
331 fsync(file->fd);
332 if (wflag)
333 fdatasync(file->fd);
334 gettimeofday(&t2, NULL);
335 t2 = tsub(t2, t1);
336
337 /* Finally, report back -- -C gives a parsable format */
338 timestr(&t2, ts, sizeof(ts), Cflag ? VERBOSE_FIXED_TIME : 0);
339 if (!Cflag) {
340 cvtstr((double)total, s1, sizeof(s1));
341 cvtstr(tdiv((double)total, t2), s2, sizeof(s2));
342 printf(_("wrote %lld/%lld bytes at offset %lld\n"),
343 total, count, (long long)offset);
344 printf(_("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n"),
345 s1, c, ts, s2, tdiv((double)c, t2));
346 } else {/* bytes,ops,time,bytes/sec,ops/sec */
347 printf("%lld,%d,%s,%.3f,%.3f\n",
348 total, c, ts,
349 tdiv((double)total, t2), tdiv((double)c, t2));
350 }
351 close(fd);
352 return 0;
353 }
354
355 void
356 pwrite_init(void)
357 {
358 pwrite_cmd.name = _("pwrite");
359 pwrite_cmd.altname = _("w");
360 pwrite_cmd.cfunc = pwrite_f;
361 pwrite_cmd.argmin = 2;
362 pwrite_cmd.argmax = -1;
363 pwrite_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK;
364 pwrite_cmd.args =
365 _("[-i infile [-d] [-s skip]] [-b bs] [-S seed] [-wW] off len");
366 pwrite_cmd.oneline =
367 _("writes a number of bytes at a specified offset");
368 pwrite_cmd.help = pwrite_help;
369
370 add_command(&pwrite_cmd);
371 }