1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 
12 #include <block-client/client.h>
13 #include <fs-management/ramdisk.h>
14 #include <zircon/device/block.h>
15 #include <zircon/device/ramdisk.h>
16 #include <zircon/syscalls.h>
17 #include <zircon/time.h>
18 #include <zircon/types.h>
19 
number(const char * str)20 static uint64_t number(const char* str) {
21     char* end;
22     uint64_t n = strtoull(str, &end, 10);
23 
24     uint64_t m = 1;
25     switch (*end) {
26     case 'G':
27     case 'g':
28         m = 1024*1024*1024;
29         break;
30     case 'M':
31     case 'm':
32         m = 1024*1024;
33         break;
34     case 'K':
35     case 'k':
36         m = 1024;
37         break;
38     }
39     return m * n;
40 }
41 
bytes_per_second(uint64_t bytes,uint64_t nanos)42 static void bytes_per_second(uint64_t bytes, uint64_t nanos) {
43     double s = ((double)nanos) / ((double)1000000000);
44     double rate = ((double)bytes) / s;
45 
46     const char* unit = "B";
47     if (rate > 1024*1024) {
48         unit = "MB";
49         rate /= 1024*1024;
50     } else if (rate > 1024) {
51         unit = "KB";
52         rate /= 1024;
53     }
54     fprintf(stderr, "%g %s/s\n", rate, unit);
55 }
56 
iotime_posix(int is_read,int fd,size_t total,size_t bufsz)57 static zx_duration_t iotime_posix(int is_read, int fd, size_t total, size_t bufsz) {
58     void* buffer = malloc(bufsz);
59     if (buffer == NULL) {
60         fprintf(stderr, "error: out of memory\n");
61         return ZX_TIME_INFINITE;
62     }
63 
64     zx_time_t t0 = zx_clock_get_monotonic();
65     size_t n = total;
66     const char* fn_name = is_read ? "read" : "write";
67     while (n > 0) {
68         size_t xfer = (n > bufsz) ? bufsz : n;
69         ssize_t r = is_read ? read(fd, buffer, xfer) : write(fd, buffer, xfer);
70         if (r < 0) {
71             fprintf(stderr, "error: %s() error %d\n", fn_name, errno);
72             return ZX_TIME_INFINITE;
73         }
74         if ((size_t)r != xfer) {
75             fprintf(stderr, "error: %s() %zu of %zu bytes processed\n", fn_name, r, xfer);
76             return ZX_TIME_INFINITE;
77         }
78         n -= xfer;
79     }
80     zx_time_t t1 = zx_clock_get_monotonic();
81 
82     return zx_time_sub_time(t1, t0);
83 }
84 
85 
make_ramdisk(size_t blocks)86 static int make_ramdisk(size_t blocks) {
87     char ramdisk_path[PATH_MAX];
88     if (create_ramdisk(512, blocks / 512, ramdisk_path) != ZX_OK) {
89         return -1;
90     }
91 
92     return open(ramdisk_path, O_RDWR);
93 }
94 
iotime_block(int is_read,int fd,size_t total,size_t bufsz)95 static zx_duration_t iotime_block(int is_read, int fd, size_t total, size_t bufsz) {
96     if ((total % 4096) || (bufsz % 4096)) {
97         fprintf(stderr, "error: total and buffer size must be multiples of 4K\n");
98         return ZX_TIME_INFINITE;
99     }
100 
101     return iotime_posix(is_read, fd, total, bufsz);
102 }
103 
iotime_fifo(char * dev,int is_read,int fd,size_t total,size_t bufsz)104 static zx_duration_t iotime_fifo(char* dev, int is_read, int fd, size_t total, size_t bufsz) {
105     zx_status_t r;
106     zx_handle_t vmo;
107     if ((r = zx_vmo_create(bufsz, 0, &vmo)) != ZX_OK) {
108         fprintf(stderr, "error: out of memory %d\n", r);
109         return ZX_TIME_INFINITE;
110     }
111 
112     block_info_t info;
113     if (ioctl_block_get_info(fd, &info) < 0) {
114         fprintf(stderr, "error: cannot get info for '%s'\n", dev);
115         return ZX_TIME_INFINITE;
116     }
117 
118     zx_handle_t fifo;
119     if (ioctl_block_get_fifos(fd, &fifo) != sizeof(fifo)) {
120         fprintf(stderr, "error: cannot get fifo for '%s'\n", dev);
121         return ZX_TIME_INFINITE;
122     }
123 
124     groupid_t group = 0;
125 
126     zx_handle_t dup;
127     if ((r = zx_handle_duplicate(vmo, ZX_RIGHT_SAME_RIGHTS, &dup)) != ZX_OK) {
128         fprintf(stderr, "error: cannot duplicate handle %d\n", r);
129         return ZX_TIME_INFINITE;
130     }
131 
132     vmoid_t vmoid;
133     if (ioctl_block_attach_vmo(fd, &dup, &vmoid) != sizeof(vmoid)) {
134         fprintf(stderr, "error: cannot attach vmo for '%s'\n", dev);
135         return ZX_TIME_INFINITE;
136     }
137 
138     fifo_client_t* client;
139     if ((r = block_fifo_create_client(fifo, &client)) != ZX_OK) {
140         fprintf(stderr, "error: cannot create block client for '%s' %d\n", dev, r);
141         return ZX_TIME_INFINITE;
142     }
143 
144     zx_time_t t0 = zx_clock_get_monotonic();
145     size_t n = total;
146     while (n > 0) {
147         size_t xfer = (n > bufsz) ? bufsz : n;
148         block_fifo_request_t request = {
149             .group = group,
150             .vmoid = vmoid,
151             .opcode = is_read ? BLOCKIO_READ : BLOCKIO_WRITE,
152             .length = xfer / info.block_size,
153             .vmo_offset = 0,
154             .dev_offset = (total - n) / info.block_size,
155         };
156         if ((r = block_fifo_txn(client, &request, 1)) != ZX_OK) {
157             fprintf(stderr, "error: block_fifo_txn error %d\n", r);
158             return ZX_TIME_INFINITE;
159         }
160         n -= xfer;
161     }
162     zx_time_t t1 = zx_clock_get_monotonic();
163     return zx_time_sub_time(t1, t0);
164 }
165 
usage(void)166 static int usage(void) {
167     fprintf(stderr,
168             "usage: iotime <read|write> <posix|block|fifo> <device|--ramdisk> <bytes> <bufsize>\n\n"
169             "        <bytes> and <bufsize> must be a multiple of 4k for block mode\n"
170             "        --ramdisk only supported for block mode\n");
171     return -1;
172 }
173 
174 
main(int argc,char ** argv)175 int main(int argc, char** argv) {
176     if (argc != 6) {
177         return usage();
178     }
179 
180     int is_read = !strcmp(argv[1], "read");
181     size_t total = number(argv[4]);
182     size_t bufsz = number(argv[5]);
183 
184     int fd;
185     if (!strcmp(argv[3], "--ramdisk")) {
186         if (strcmp(argv[2], "block")) {
187             fprintf(stderr, "ramdisk only supported for block\n");
188             return -1;
189         }
190         if ((fd = make_ramdisk(total)) < 0) {
191             fprintf(stderr, "error: cannot create %zu-byte ramdisk\n", total);
192             return -1;
193         }
194     } else {
195         if ((fd = open(argv[3], is_read ? O_RDONLY : O_WRONLY)) < 0) {
196             fprintf(stderr, "error: cannot open '%s'\n", argv[3]);
197             return -1;
198         }
199     }
200 
201     zx_duration_t res;
202     if (!strcmp(argv[2], "posix")) {
203         res = iotime_posix(is_read, fd, total, bufsz);
204     } else if (!strcmp(argv[2], "block")) {
205         res = iotime_block(is_read, fd, total, bufsz);
206     } else if (!strcmp(argv[2], "fifo")) {
207         res = iotime_fifo(argv[3], is_read, fd, total, bufsz);
208     } else {
209         fprintf(stderr, "error: unknown mode '%s'\n", argv[2]);
210         return -1;
211     }
212 
213     if (res != ZX_TIME_INFINITE) {
214         fprintf(stderr, "%s %zu bytes in %zu ns: ", is_read ? "read" : "write", total, res);
215         bytes_per_second(total, res);
216         return 0;
217     } else {
218         return -1;
219     }
220 }
221