Initial disktools commit. Added dops.c
[disktools.git] / dops.c
CommitLineData
1f961edd
JA
1/*
2 * direct disk operation exerciser
3 *
4 * (C) 2005 Jens Axboe <axboe@suse.de>
5 *
6 */
7#include <stdio.h>
8#include <stdlib.h>
9#include <unistd.h>
10#include <fcntl.h>
11#include <errno.h>
12#include <sys/time.h>
13#include <sys/types.h>
14#include <sys/stat.h>
15#include <sys/ioctl.h>
16#include <libaio.h>
17#include <getopt.h>
18#include <locale.h>
19
20#include <linux/fs.h>
21
22#define MAX_DEPTH 128
23
24static int io_size = 512; /* bytes */
25static int sequential = 1; /* 0 for random, 1 for sequential */
26static unsigned long long range = 2; /* megabytes */
27static char filename[256] = "/dev/sdb";
28static int depth = 32;
29static int total_run = 15000;
30static int display_stats = 0;
31static int writing = 0;
32static int force = 0;
33
34struct iocb *iocbs[MAX_DEPTH];
35struct io_event *events;
36
37struct drand48_data random_state;
38static unsigned long long next_block;
39static unsigned long long blocks_done;
40static unsigned long long nr_blocks;
41
42#define ALIGN(buf) (((unsigned long) (buf) + (io_size-1)) & ~((io_size-1)))
43
44unsigned long long get_next_offset(void)
45{
46 unsigned long long block;
47 long r;
48
49 if (sequential) {
50 if (next_block >= nr_blocks)
51 next_block = 0;
52
53 block = next_block;
54 next_block += (256 * 1024 / io_size);
55 return block;
56 }
57
58 lrand48_r(&random_state, &r);
59 block = (1 + (double) (nr_blocks) * (unsigned long long) r / (RAND_MAX+1.0));
60 return block - 1;
61}
62
63void init_randomizer(void)
64{
65 unsigned long seed = 0;
66
67 if (sequential)
68 return;
69
70 srand48_r(seed, &random_state);
71}
72
73unsigned long time_elapsed(struct timeval *start, struct timeval *end)
74{
75 int seconds = end->tv_sec - start->tv_sec;
76 int useconds = end->tv_usec - start->tv_usec;
77
78 return (1000 * seconds) + (useconds / 1000);
79}
80
81void do_io(io_context_t io_ctx, int fd, char *buffer)
82{
83 struct timeval real_start, start, end;
84 int i, ret, cur_depth;
85 unsigned long ops, best_ops;
86 unsigned long bytes, best_bytes;
87
88 for (i = 0; i < depth; i++) {
89 struct iocb *io = iocbs[i];
90 char *b = buffer + i * io_size;
91 unsigned long long off = get_next_offset();
92 unsigned long long offset = off * io_size;
93
94 if (writing)
95 io_prep_pwrite(io, fd, b, io_size, offset);
96 else
97 io_prep_pread(io, fd, b, io_size, offset);
98 }
99
100 ret = io_submit(io_ctx, depth, iocbs);
101 if (ret < 0) {
102 fprintf(stderr, "io_submit: %d\n", ret);
103 return;
104 }
105
106 cur_depth = ret;
107 gettimeofday(&start, NULL);
108 gettimeofday(&real_start, NULL);
109
110 best_bytes = best_ops = bytes = ops = 0;
111 do {
112 struct timespec ts = { .tv_sec = 30, .tv_nsec = 0 };
113 unsigned long elapsed;
114
115 ret = io_getevents(io_ctx, 1, cur_depth, events, &ts);
116 if (ret < 0) {
117 if (ret == EINTR)
118 continue;
119
120 fprintf(stderr, "io_getevents: %d\n", ret);
121 break;
122 } else if (!ret) {
123 fprintf(stderr, "No events completed, depth %d\n", cur_depth);
124 cur_depth = 0;
125 break;
126 }
127
128 cur_depth -= ret;
129 ops += (ret * (io_size >> 9));
130 bytes += (ret * io_size);
131 blocks_done += ret;
132
133 gettimeofday(&end, NULL);
134
135 elapsed = time_elapsed(&start, &end);
136 if (elapsed >= 1000) {
137 unsigned long kb = bytes >> 10;
138 unsigned long o, b;
139
140 o = ops * 1000 / elapsed;
141 b = kb * 1000 / elapsed;
142
143 if (display_stats)
144 printf("stat: ops/sec = %8lu, KiB/sec = %8lu (depth=%2d)\n", o, b, cur_depth);
145 if (o > best_ops)
146 best_ops = o;
147 if (b > best_bytes)
148 best_bytes = b;
149
150 bytes = ops = 0;
151 gettimeofday(&start, NULL);
152 }
153
154 if (total_run) {
155 elapsed = time_elapsed(&real_start, &end);
156 if (total_run && (elapsed >= total_run)) {
157 printf("OPS_SEC=%'lu RATE=%'lu\n",best_ops, best_bytes);
158 break;
159 }
160 }
161
162 for (i = 0; i < ret; i++) {
163 struct io_event *ev = events + i;
164 struct iocb *io = ev->obj;
165 unsigned long long offset = get_next_offset();
166 int err;
167
168 offset *= io_size;
169
170 if (writing) {
171 io_prep_pwrite(io, fd, io->u.c.buf, io_size, offset);
172 } else
173 io_prep_pread(io, fd, io->u.c.buf, io_size, offset);
174
175 err = io_submit(io_ctx, 1, &io);
176 if (err != 1) {
177 fprintf(stderr, "io_submit: %d\n", err);
178 goto error;
179 }
180
181 cur_depth++;
182 }
183
184 } while (1);
185
186error:
187 if (cur_depth)
188 io_getevents(io_ctx, cur_depth, cur_depth, events, NULL);
189}
190
191int parse_options(int argc, char **argv)
192{
193 static struct option longoptions[] = {
194 { "device", 1, NULL, 'd' },
195 { "area", 1, NULL, 'a' },
196 { "random", 0, NULL, 'r' },
197 { "blocks", 1, NULL, 'b' },
198 { "queue depth",1, NULL, 'q' },
199 { "time", 1, NULL, 't' },
200 { "stats", 1, NULL, 's' },
201 { "write", 0, NULL, 'w' },
202 { "force", 0, NULL, 'w' },
203 };
204 int c, res;
205
206 while (1) {
207 c = getopt_long(argc, argv, "d:a:rb:q:t:swf", longoptions,&res);
208 if (c == -1)
209 break;
210
211 switch (c) {
212 case 'd':
213 strncpy(filename, optarg, sizeof(filename)-1);
214 break;
215 case 'a':
216 range = atoi(optarg);
217 break;
218 case 'r':
219 sequential = 0;
220 break;
221 case 'b':
222 io_size = atoi(optarg) * 512;
223 break;
224 case 'q':
225 depth = atoi(optarg);
226 if (depth <= 0 || depth > MAX_DEPTH) {
227 fprintf(stderr, "queue depth out-of-range %d\n", depth);
228 return 1;
229 }
230 break;
231 case 't':
232 total_run = atoi(optarg) * 1000;
233 if (total_run < 0) {
234 fprintf(stderr, "bad runtime\n");
235 return 1;
236 }
237 break;
238 case 's':
239 display_stats = 1;
240 break;
241 case 'w':
242 writing = 1;
243 break;
244 case 'f':
245 force = 1;
246 break;
247 }
248 }
249
250 printf("Dev=%s, %s, a=%LuMiB, %s, b=%u, q=%u, t=%u\n", filename, writing ? "write" : "read", range, sequential ? "sequential" : "random", io_size / 512, depth, total_run / 1000);
251 return 0;
252}
253
254unsigned long get_size(int fd)
255{
256 unsigned long long blocks;
257 struct stat sb;
258
259 if (fstat(fd, &sb) == -1) {
260 perror("fstat");
261 return -1;
262 }
263
264 if (S_ISBLK(sb.st_mode)) {
265 /*
266 * be a little cautious about writing to a device. if
267 * the user didn't use -f for force, ask for confirmation
268 */
269 if (writing && !force) {
270 printf("Writing directly to block device, are you sure? CTRL-C to cancel now\n");
271 (void) getc(stdin);
272 }
273 if (ioctl(fd, BLKGETSIZE64, &blocks) == -1) {
274 perror("BLKGETSIZE64");
275 return -1;
276 }
277 return blocks >> 20ULL;
278 } else if (S_ISREG(sb.st_mode))
279 return sb.st_size >> 20ULL;
280
281 fprintf(stderr, "Bad file type (must be file or block device)\n");
282 return -1;
283}
284
285int main(int argc, char *argv[])
286{
287 unsigned long mb;
288 io_context_t *io_ctx;
289 char *orig, *buffer;
290 int fd, i;
291
292 if (parse_options(argc, argv))
293 return 1;
294
295 if (writing)
296 fd = open(filename, O_WRONLY | O_DIRECT);
297 else
298 fd = open(filename, O_RDONLY | O_DIRECT);
299
300 if (fd == -1) {
301 perror("open");
302 return 1;
303 }
304
305 mb = get_size(fd);
306 if (nr_blocks == -1)
307 return 1;
308
309 if (!range || (range > mb))
310 range = mb;
311
312 nr_blocks = (range << 20ULL) / (unsigned long long) io_size;
313
314 printf("nr_blocks=%Lu (size=%u)\n", nr_blocks, io_size);
315
316 io_ctx = malloc(sizeof(*io_ctx));
317
318 i = io_queue_init(depth, io_ctx);
319 if (i) {
320 fprintf(stderr, "io_queue_init: %d\n", i);
321 return 1;
322 }
323
324 init_randomizer();
325
326 orig = malloc((depth + 1) * io_size);
327 buffer = (char *) ALIGN(orig);
328
329 for (i = 0; i < depth; i++)
330 iocbs[i] = malloc(sizeof(struct iocb));
331
332 setlocale(LC_NUMERIC, "en_US");
333
334 events = malloc(depth * sizeof(struct io_event));
335
336 do_io(*io_ctx, fd, buffer);
337
338 io_destroy(*io_ctx);
339
340 for (i = 0; i < depth; i++)
341 free(iocbs[i]);
342
343 free(io_ctx);
344 free(events);
345 free(orig);
346 close(fd);
347 return 0;
348}