2 * Copyright 2010 by Garmin Ltd. or its subsidiaries
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 * Performs a simple write/readback test to verify correct functionality
17 * of direct i/o on a block device node.
20 /* For large-file support */
21 #define _FILE_OFFSET_BITS 64
22 #define _LARGEFILE_SOURCE
23 #define _LARGEFILE64_SOURCE
37 #include <sys/ioctl.h>
40 #include <sys/types.h>
45 #define NUM_TEST_BLKS 128
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.
52 static void *pagealign_alloc(size_t size)
54 void *ret = mmap(NULL, size, PROT_READ | PROT_WRITE,
55 MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE | MAP_LOCKED,
57 if (ret == MAP_FAILED) {
64 static void pagealign_free(void *addr, size_t size)
66 int ret = munmap(addr, size);
71 static ssize_t do_read(int fd, void *buf, off64_t start, size_t count)
74 size_t bytes_read = 0;
76 lseek64(fd, start, SEEK_SET);
79 ret = read(fd, (char *)buf + bytes_read, count - bytes_read);
83 } else if (ret == 0) {
84 fprintf(stderr, "Unexpected end-of-file\n");
88 } while (bytes_read < count);
93 static ssize_t do_write(int fd, const void *buf, off64_t start, size_t count)
98 lseek64(fd, start, SEEK_SET);
101 ret = write(fd, (char *)buf + bytes_out, count - bytes_out);
105 } else if (ret == 0) {
106 fprintf(stderr, "write returned 0\n");
110 } while (bytes_out < count);
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
123 static void init_test_buf(void *buf, uint64_t start_blk, size_t len)
125 uint32_t *data = buf;
128 len /= sizeof(uint32_t);
129 for (i = 0; i < len; i++)
130 data[i] = (start_blk & 0xFFFF) << 16 | (i & 0xFFFF);
133 static void dump_hex(const void *buf, int len)
135 const uint8_t *data = buf;
139 ascii_buf[16] = '\0';
141 for (i = 0; i < len; i++) {
147 printf("%02x ", val);
148 ascii_buf[off] = isprint(val) ? val : '.';
150 printf(" %-16s\n", ascii_buf);
158 printf(" %-16s\n", ascii_buf);
162 static void update_progress(int current, int total)
164 double pct_done = (double)current * 100 / total;
165 printf("Testing area %d/%d (%6.2f%% complete)\r", current, total,
170 int main(int argc, const char *argv[])
176 void *read_buf = NULL, *write_buf = NULL;
183 printf("Usage: directiotest blkdev_path\n");
188 fd = open(path, O_RDWR | O_DIRECT | O_LARGEFILE);
193 if (fstat(fd, &stat) == -1) {
196 } else if (!S_ISBLK(stat.st_mode)) {
197 fprintf(stderr, "%s is not a block device\n", path);
201 if (ioctl(fd, BLKSSZGET, &blk_size) == -1) {
205 if (ioctl(fd, BLKGETSIZE64, &num_blks) == -1) {
209 num_blks /= blk_size;
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");
220 * Start the actual test. Go through the entire device, writing
221 * locally-unique patern to each test block and then reading it
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;
229 test_areas = num_blks / NUM_TEST_BLKS;
232 printf("Starting test\n");
234 for (i = 0; i < test_areas; i++) {
235 uint64_t cur_blk = (uint64_t)i * NUM_TEST_BLKS;
237 update_progress(i + 1, test_areas);
239 init_test_buf(write_buf, cur_blk, test_size);
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");
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");
252 if (memcmp(write_buf, read_buf, test_size)) {
253 printf("Readback verification failed at block %" PRIu64 "\n\n",
255 printf("Written data:\n");
256 dump_hex(write_buf, test_size);
257 printf("\nRead data:\n");
258 dump_hex(read_buf, test_size);
263 printf("\nTest complete\n");
268 pagealign_free(read_buf, test_size);
270 pagealign_free(write_buf, test_size);