OSDN Git Service

merge in nyc-release history after reset to nyc-dev
[android-x86/system-extras.git] / tests / directiotest / directiotest.c
1 /*
2  * Copyright 2010 by Garmin Ltd. or its subsidiaries
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * Performs a simple write/readback test to verify correct functionality
17  * of direct i/o on a block device node.
18  */
19
20 /* For large-file support */
21 #define _FILE_OFFSET_BITS 64
22 #define _LARGEFILE_SOURCE
23 #define _LARGEFILE64_SOURCE
24
25 /* For O_DIRECT */
26 #define _GNU_SOURCE
27
28 #include <ctype.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <inttypes.h>
32 #include <limits.h>
33 #include <stdint.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/ioctl.h>
38 #include <sys/mman.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <unistd.h>
42
43 #include <linux/fs.h>
44
45 #define NUM_TEST_BLKS 128
46
47 /*
48  * Allocate page-aligned memory.  Could use posix_memalign(3), but some
49  * systems don't support it.  Also pre-faults memory since we'll be using
50  * it all right away anyway.
51  */
52 static void *pagealign_alloc(size_t size)
53 {
54         void *ret = mmap(NULL, size, PROT_READ | PROT_WRITE,
55                         MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE | MAP_LOCKED,
56                         -1, 0);
57         if (ret == MAP_FAILED) {
58                 perror("mmap");
59                 ret = NULL;
60         }
61         return ret;
62 }
63
64 static void pagealign_free(void *addr, size_t size)
65 {
66         int ret = munmap(addr, size);
67         if (ret == -1)
68                 perror("munmap");
69 }
70
71 static ssize_t do_read(int fd, void *buf, off64_t start, size_t count)
72 {
73         ssize_t ret;
74         size_t bytes_read = 0;
75
76         lseek64(fd, start, SEEK_SET);
77
78         do {
79                 ret = read(fd, (char *)buf + bytes_read, count - bytes_read);
80                 if (ret == -1) {
81                         perror("read");
82                         return -1;
83                 } else if (ret == 0) {
84                         fprintf(stderr, "Unexpected end-of-file\n");
85                         return -1;
86                 }
87                 bytes_read += ret;
88         } while (bytes_read < count);
89
90         return bytes_read;
91 }
92
93 static ssize_t do_write(int fd, const void *buf, off64_t start, size_t count)
94 {
95         ssize_t ret;
96         size_t bytes_out = 0;
97
98         lseek64(fd, start, SEEK_SET);
99
100         do {
101                 ret = write(fd, (char *)buf + bytes_out, count - bytes_out);
102                 if (ret == -1) {
103                         perror("write");
104                         return -1;
105                 } else if (ret == 0) {
106                         fprintf(stderr, "write returned 0\n");
107                         return -1;
108                 }
109                 bytes_out += ret;
110         } while (bytes_out < count);
111
112         return bytes_out;
113 }
114
115 /*
116  * Initializes test buffer with locally-unique test pattern.  High 16-bits of
117  * each 32-bit word contain first disk block number of the test area, low
118  * 16-bits contain word offset into test area.  The goal is that a given test
119  * area should never contain the same data as a nearby test area, and that the
120  * data for a given test area be easily reproducable given the start block and
121  * test area size.
122  */
123 static void init_test_buf(void *buf, uint64_t start_blk, size_t len)
124 {
125         uint32_t *data = buf;
126         size_t i;
127
128         len /= sizeof(uint32_t);
129         for (i = 0; i < len; i++)
130                 data[i] = (start_blk & 0xFFFF) << 16 | (i & 0xFFFF);
131 }
132
133 static void dump_hex(const void *buf, int len)
134 {
135         const uint8_t *data = buf;
136         int i;
137         char ascii_buf[17];
138
139         ascii_buf[16] = '\0';
140
141         for (i = 0; i < len; i++) {
142                 int val = data[i];
143                 int off = i % 16;
144
145                 if (off == 0)
146                         printf("%08x  ", i);
147                 printf("%02x ", val);
148                 ascii_buf[off] = isprint(val) ? val : '.';
149                 if (off == 15)
150                         printf(" %-16s\n", ascii_buf);
151         }
152
153         i %= 16;
154         if (i) {
155                 ascii_buf[i] = '\0';
156                 while (i++ < 16)
157                         printf("   ");
158                 printf(" %-16s\n", ascii_buf);
159         }
160 }
161
162 static void update_progress(int current, int total)
163 {
164         double pct_done = (double)current * 100 / total;
165         printf("Testing area %d/%d (%6.2f%% complete)\r", current, total,
166                         pct_done);
167         fflush(stdout);
168 }
169
170 int main(int argc, const char *argv[])
171 {
172         int ret = 1;
173         const char *path;
174         int fd;
175         struct stat stat;
176         void *read_buf = NULL, *write_buf = NULL;
177         int blk_size;
178         uint64_t num_blks;
179         size_t test_size;
180         int test_areas, i;
181
182         if (argc != 2) {
183                 printf("Usage: directiotest blkdev_path\n");
184                 exit(1);
185         }
186
187         path = argv[1];
188         fd = open(path, O_RDWR | O_DIRECT | O_LARGEFILE);
189         if (fd == -1) {
190                 perror("open");
191                 exit(1);
192         }
193         if (fstat(fd, &stat) == -1) {
194                 perror("stat");
195                 goto cleanup;
196         } else if (!S_ISBLK(stat.st_mode)) {
197                 fprintf(stderr, "%s is not a block device\n", path);
198                 goto cleanup;
199         }
200
201         if (ioctl(fd, BLKSSZGET, &blk_size) == -1) {
202                 perror("ioctl");
203                 goto cleanup;
204         }
205         if (ioctl(fd, BLKGETSIZE64, &num_blks) == -1) {
206                 perror("ioctl");
207                 goto cleanup;
208         }
209         num_blks /= blk_size;
210
211         test_size = (size_t)blk_size * NUM_TEST_BLKS;
212         read_buf = pagealign_alloc(test_size);
213         write_buf = pagealign_alloc(test_size);
214         if (!read_buf || !write_buf) {
215                 fprintf(stderr, "Error allocating test buffers\n");
216                 goto cleanup;
217         }
218
219         /*
220          * Start the actual test.  Go through the entire device, writing
221          * locally-unique patern to each test block and then reading it
222          * back.
223          */
224         if (num_blks / NUM_TEST_BLKS > INT_MAX) {
225                 printf("Warning: Device too large for test variables\n");
226                 printf("Entire device will not be tested\n");
227                 test_areas = INT_MAX;
228         } else {
229                 test_areas = num_blks / NUM_TEST_BLKS;
230         }
231
232         printf("Starting test\n");
233
234         for (i = 0; i < test_areas; i++) {
235                 uint64_t cur_blk = (uint64_t)i * NUM_TEST_BLKS;
236
237                 update_progress(i + 1, test_areas);
238
239                 init_test_buf(write_buf, cur_blk, test_size);
240
241                 if (do_write(fd, write_buf, cur_blk * blk_size, test_size) !=
242                                 (ssize_t)test_size) {
243                         fprintf(stderr, "write failed, aborting test\n");
244                         goto cleanup;
245                 }
246                 if (do_read(fd, read_buf, cur_blk * blk_size, test_size) !=
247                                 (ssize_t)test_size) {
248                         fprintf(stderr, "read failed, aborting test\n");
249                         goto cleanup;
250                 }
251
252                 if (memcmp(write_buf, read_buf, test_size)) {
253                         printf("Readback verification failed at block %" PRIu64 "\n\n",
254                                         cur_blk);
255                         printf("Written data:\n");
256                         dump_hex(write_buf, test_size);
257                         printf("\nRead data:\n");
258                         dump_hex(read_buf, test_size);
259                         goto cleanup;
260                 }
261         }
262
263         printf("\nTest complete\n");
264         ret = 0;
265
266 cleanup:
267         if (read_buf)
268                 pagealign_free(read_buf, test_size);
269         if (write_buf)
270                 pagealign_free(write_buf, test_size);
271         close(fd);
272         return ret;
273 }