directiotest.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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. /* For large-file support */
  20. #define _FILE_OFFSET_BITS 64
  21. #define _LARGEFILE_SOURCE
  22. #define _LARGEFILE64_SOURCE
  23. /* For O_DIRECT */
  24. #define _GNU_SOURCE
  25. #include <ctype.h>
  26. #include <errno.h>
  27. #include <fcntl.h>
  28. #include <inttypes.h>
  29. #include <limits.h>
  30. #include <stdint.h>
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #include <sys/ioctl.h>
  35. #include <sys/mman.h>
  36. #include <sys/stat.h>
  37. #include <sys/types.h>
  38. #include <unistd.h>
  39. #include <linux/fs.h>
  40. #define NUM_TEST_BLKS 128
  41. /*
  42. * Allocate page-aligned memory. Could use posix_memalign(3), but some
  43. * systems don't support it. Also pre-faults memory since we'll be using
  44. * it all right away anyway.
  45. */
  46. static void *pagealign_alloc(size_t size)
  47. {
  48. void *ret = mmap(NULL, size, PROT_READ | PROT_WRITE,
  49. MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE | MAP_LOCKED,
  50. -1, 0);
  51. if (ret == MAP_FAILED) {
  52. perror("mmap");
  53. ret = NULL;
  54. }
  55. return ret;
  56. }
  57. static void pagealign_free(void *addr, size_t size)
  58. {
  59. int ret = munmap(addr, size);
  60. if (ret == -1)
  61. perror("munmap");
  62. }
  63. static ssize_t do_read(int fd, void *buf, off64_t start, size_t count)
  64. {
  65. ssize_t ret;
  66. size_t bytes_read = 0;
  67. lseek64(fd, start, SEEK_SET);
  68. do {
  69. ret = read(fd, (char *)buf + bytes_read, count - bytes_read);
  70. if (ret == -1) {
  71. perror("read");
  72. return -1;
  73. } else if (ret == 0) {
  74. fprintf(stderr, "Unexpected end-of-file\n");
  75. return -1;
  76. }
  77. bytes_read += ret;
  78. } while (bytes_read < count);
  79. return bytes_read;
  80. }
  81. static ssize_t do_write(int fd, const void *buf, off64_t start, size_t count)
  82. {
  83. ssize_t ret;
  84. size_t bytes_out = 0;
  85. lseek64(fd, start, SEEK_SET);
  86. do {
  87. ret = write(fd, (char *)buf + bytes_out, count - bytes_out);
  88. if (ret == -1) {
  89. perror("write");
  90. return -1;
  91. } else if (ret == 0) {
  92. fprintf(stderr, "write returned 0\n");
  93. return -1;
  94. }
  95. bytes_out += ret;
  96. } while (bytes_out < count);
  97. return bytes_out;
  98. }
  99. /*
  100. * Initializes test buffer with locally-unique test pattern. High 16-bits of
  101. * each 32-bit word contain first disk block number of the test area, low
  102. * 16-bits contain word offset into test area. The goal is that a given test
  103. * area should never contain the same data as a nearby test area, and that the
  104. * data for a given test area be easily reproducable given the start block and
  105. * test area size.
  106. */
  107. static void init_test_buf(void *buf, uint64_t start_blk, size_t len)
  108. {
  109. uint32_t *data = buf;
  110. size_t i;
  111. len /= sizeof(uint32_t);
  112. for (i = 0; i < len; i++)
  113. data[i] = (start_blk & 0xFFFF) << 16 | (i & 0xFFFF);
  114. }
  115. static void dump_hex(const void *buf, int len)
  116. {
  117. const uint8_t *data = buf;
  118. int i;
  119. char ascii_buf[17];
  120. ascii_buf[16] = '\0';
  121. for (i = 0; i < len; i++) {
  122. int val = data[i];
  123. int off = i % 16;
  124. if (off == 0)
  125. printf("%08x ", i);
  126. printf("%02x ", val);
  127. ascii_buf[off] = isprint(val) ? val : '.';
  128. if (off == 15)
  129. printf(" %-16s\n", ascii_buf);
  130. }
  131. i %= 16;
  132. if (i) {
  133. ascii_buf[i] = '\0';
  134. while (i++ < 16)
  135. printf(" ");
  136. printf(" %-16s\n", ascii_buf);
  137. }
  138. }
  139. static void update_progress(int current, int total)
  140. {
  141. double pct_done = (double)current * 100 / total;
  142. printf("Testing area %d/%d (%6.2f%% complete)\r", current, total,
  143. pct_done);
  144. fflush(stdout);
  145. }
  146. int main(int argc, const char *argv[])
  147. {
  148. int ret = 1;
  149. const char *path;
  150. int fd;
  151. struct stat stat;
  152. void *read_buf = NULL, *write_buf = NULL;
  153. int blk_size;
  154. uint64_t num_blks;
  155. size_t test_size;
  156. int test_areas, i;
  157. if (argc != 2) {
  158. printf("Usage: directiotest blkdev_path\n");
  159. exit(1);
  160. }
  161. path = argv[1];
  162. fd = open(path, O_RDWR | O_DIRECT | O_LARGEFILE);
  163. if (fd == -1) {
  164. perror("open");
  165. exit(1);
  166. }
  167. if (fstat(fd, &stat) == -1) {
  168. perror("stat");
  169. goto cleanup;
  170. } else if (!S_ISBLK(stat.st_mode)) {
  171. fprintf(stderr, "%s is not a block device\n", path);
  172. goto cleanup;
  173. }
  174. if (ioctl(fd, BLKSSZGET, &blk_size) == -1) {
  175. perror("ioctl");
  176. goto cleanup;
  177. }
  178. if (ioctl(fd, BLKGETSIZE64, &num_blks) == -1) {
  179. perror("ioctl");
  180. goto cleanup;
  181. }
  182. num_blks /= blk_size;
  183. test_size = (size_t)blk_size * NUM_TEST_BLKS;
  184. read_buf = pagealign_alloc(test_size);
  185. write_buf = pagealign_alloc(test_size);
  186. if (!read_buf || !write_buf) {
  187. fprintf(stderr, "Error allocating test buffers\n");
  188. goto cleanup;
  189. }
  190. /*
  191. * Start the actual test. Go through the entire device, writing
  192. * locally-unique patern to each test block and then reading it
  193. * back.
  194. */
  195. if (num_blks / NUM_TEST_BLKS > INT_MAX) {
  196. printf("Warning: Device too large for test variables\n");
  197. printf("Entire device will not be tested\n");
  198. test_areas = INT_MAX;
  199. } else {
  200. test_areas = num_blks / NUM_TEST_BLKS;
  201. }
  202. printf("Starting test\n");
  203. for (i = 0; i < test_areas; i++) {
  204. uint64_t cur_blk = (uint64_t)i * NUM_TEST_BLKS;
  205. update_progress(i + 1, test_areas);
  206. init_test_buf(write_buf, cur_blk, test_size);
  207. if (do_write(fd, write_buf, cur_blk * blk_size, test_size) !=
  208. (ssize_t)test_size) {
  209. fprintf(stderr, "write failed, aborting test\n");
  210. goto cleanup;
  211. }
  212. if (do_read(fd, read_buf, cur_blk * blk_size, test_size) !=
  213. (ssize_t)test_size) {
  214. fprintf(stderr, "read failed, aborting test\n");
  215. goto cleanup;
  216. }
  217. if (memcmp(write_buf, read_buf, test_size)) {
  218. printf("Readback verification failed at block %" PRIu64 "\n\n",
  219. cur_blk);
  220. printf("Written data:\n");
  221. dump_hex(write_buf, test_size);
  222. printf("\nRead data:\n");
  223. dump_hex(read_buf, test_size);
  224. goto cleanup;
  225. }
  226. }
  227. printf("\nTest complete\n");
  228. ret = 0;
  229. cleanup:
  230. if (read_buf)
  231. pagealign_free(read_buf, test_size);
  232. if (write_buf)
  233. pagealign_free(write_buf, test_size);
  234. close(fd);
  235. return ret;
  236. }