disk.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /*
  2. * disk - calculate zone bandwidths and seek times
  3. *
  4. * Usage: disk device
  5. *
  6. * Copyright (c) 1994-1997 Larry McVoy. All rights reserved.
  7. * Bits of this are derived from work by Ethan Solomita.
  8. */
  9. #include <stdio.h>
  10. #include <sys/types.h>
  11. #include <unistd.h>
  12. #include <stdlib.h>
  13. #include "bench.h"
  14. #ifndef sgi
  15. #define NO_LSEEK64
  16. #define off64_t long long
  17. #endif
  18. #define SEEKPOINTS 2000
  19. #define ZONEPOINTS 150
  20. uint64 disksize(char *);
  21. int seekto(int, uint64);
  22. int zone(char *disk, int oflag, int bsize);
  23. int seek(char *disk, int oflag);
  24. #ifdef linux
  25. int flushdisk(int);
  26. #endif
  27. int
  28. main(int ac, char **av)
  29. {
  30. fprintf(stderr, "\"Seek times for %s\n", av[1]);
  31. seek(av[1], 0);
  32. fprintf(stderr, "\n");
  33. fprintf(stderr, "\"Zone bandwidth for %s\n", av[1]);
  34. zone(av[1], 0, (1<<20));
  35. return (0);
  36. }
  37. int
  38. zone(char *disk, int oflag, int bsize)
  39. {
  40. char *buf;
  41. int usecs;
  42. int error;
  43. int n;
  44. int fd;
  45. uint64 off;
  46. int stride;
  47. if ((fd = open(disk, oflag)) == -1) {
  48. perror(disk);
  49. exit(1);
  50. }
  51. buf = valloc(bsize);
  52. if (!buf) {
  53. perror("valloc");
  54. exit(1);
  55. }
  56. bzero(buf, bsize);
  57. #ifdef linux
  58. flushdisk(fd);
  59. #endif
  60. /*
  61. * We want ZONEPOINTS data points
  62. * but the stride has to be at least 512 and a 512 multiple.
  63. * Weird code below for precision.
  64. */
  65. off = disksize(disk);
  66. off /= ZONEPOINTS;
  67. stride = off;
  68. if (stride < 512) stride = 512;
  69. stride += 511;
  70. stride >>= 9;
  71. stride <<= 9;
  72. /*
  73. * Very small disks such as ZIP drives get a 256K blocksize.
  74. * As measured on my SCSI ZIP, there seems to be no
  75. * difference between 256K and 1MB for sequential reads.
  76. * XXX - there is a rotational delay difference but that's tough.
  77. */
  78. if (bsize > stride) bsize = 256<<10;
  79. if (bsize > stride) stride = bsize;
  80. off *= ZONEPOINTS;
  81. debug((stdout, "stride=%d bs=%d size=%dM points=%d\n",
  82. stride, bsize, (int)(off >> 20), (int)(off/stride)));
  83. /*
  84. * Read buf's worth of data every stride and time it.
  85. * Don't include the rotational delay.
  86. * This first I/O outside the loop is to catch read/write permissions.
  87. */
  88. #define IO(a,b,c) (oflag == 0 ? (n = read(a,b,c)) : (n = write(a,b,c)))
  89. error = IO(fd, buf, 512);
  90. if (error == -1) {
  91. perror(disk);
  92. exit(1);
  93. }
  94. off = 512;
  95. for ( ;; ) {
  96. if (IO(fd, buf, 1024) != 1024) {
  97. exit(0);
  98. }
  99. off += 1024;
  100. start(0);
  101. if (IO(fd, buf, bsize) != bsize) {
  102. exit(0);
  103. }
  104. usecs = stop(0, 0);
  105. off += bsize;
  106. fprintf(stderr, "%.01f %.2f\n",
  107. off/1000000.0, (double)bsize/usecs);
  108. off += stride;
  109. if (seekto(fd, off)) {
  110. exit(0);
  111. }
  112. }
  113. exit(0);
  114. }
  115. /*
  116. * Seek - calculate seeks as a function of distance.
  117. */
  118. #undef IO
  119. #define IO(a,b,c) error = (oflag == 0 ? read(a,b,c) : write(a,b,c)); \
  120. if (error == -1) { perror("io"); exit(1); }
  121. #define IOSIZE 512
  122. #define TOOSMALL 1000 /* seeks this small are cached */
  123. #define TOOBIG 1000000 /* seeks this big are remapped or weirdos */
  124. /* zip drives have seeks this long */
  125. int
  126. seek(char *disk, int oflag)
  127. {
  128. char *buf;
  129. int fd;
  130. off64_t size;
  131. off64_t begin, end;
  132. int usecs;
  133. int error;
  134. int tot_msec = 0, tot_io = 0;
  135. int stride;
  136. if ((fd = open(disk, oflag)) == -1) {
  137. perror(disk);
  138. return (-1);
  139. }
  140. #ifdef linux
  141. flushdisk(fd);
  142. #endif
  143. size = disksize(disk);
  144. buf = valloc(IOSIZE);
  145. bzero(buf, IOSIZE);
  146. /*
  147. * We flip back and forth, in strides of 1MB (typically).
  148. * If we have a 100MB fd, that means we do
  149. * 1, 99, 2, 98, etc.
  150. *
  151. * We want around SEEK POINTS data points
  152. * but the stride has to be at least 512 and a 512 multiple.
  153. */
  154. stride = size / SEEKPOINTS;
  155. if (stride < 512) stride = 512;
  156. stride += 511;
  157. stride >>= 9;
  158. stride <<= 9;
  159. debug((stdout, "stride=%d size=%dM points=%d\n",
  160. stride, (int)(size >> 20), (int)(size/stride)));
  161. end = size;
  162. begin = 0;
  163. seekto(fd, begin);
  164. IO(fd, buf, IOSIZE);
  165. while (end >= begin + stride*2) {
  166. end -= stride;
  167. start(0);
  168. seekto(fd, end);
  169. IO(fd, buf, IOSIZE);
  170. usecs = stop(0, 0);
  171. if (usecs > TOOSMALL && usecs < TOOBIG) {
  172. tot_io++; tot_msec += usecs/1000;
  173. fprintf(stderr, "%.01f %.02f\n",
  174. (end - begin - stride) / 1000000., usecs/1000.);
  175. }
  176. begin += stride;
  177. start(0);
  178. seekto(fd, begin);
  179. IO(fd, buf, IOSIZE);
  180. usecs = stop(0, 0);
  181. if (usecs > TOOSMALL && usecs < TOOBIG) {
  182. tot_io++; tot_msec += usecs/1000;
  183. fprintf(stderr, "%.01f %.02f\n",
  184. (end + stride - begin) / 1000000., usecs/1000.);
  185. }
  186. }
  187. /*
  188. * This is wrong, it should take the 1/3 stroke seek average.
  189. avg_msec = (double)tot_msec/tot_io;
  190. fprintf(stderr, "Average time == %.04f\n", avg_msec);
  191. */
  192. return (0);
  193. }
  194. /*
  195. * Calculate how big a device is.
  196. *
  197. * To avoid 32 bit problems, our units are MB.
  198. */
  199. #define FORWARD (512<<20)
  200. #define FORWARD1 (64<<20)
  201. #define FORWARD2 (1<<20)
  202. /*
  203. * Go forward in 1GB chunks until you can't.
  204. * Go backwards in 128MB chunks until you can.
  205. * Go forwards in 1MB chunks until you can't and return that -1.
  206. */
  207. uint64
  208. disksize(char *disk)
  209. {
  210. int fd = open(disk, 0);
  211. char buf[512];
  212. uint64 off = 0;
  213. if (fd == -1) {
  214. perror("usage: disksize device");
  215. return(0);
  216. }
  217. /*
  218. * Go forward until it doesn't work.
  219. */
  220. for ( ;; ) {
  221. off += FORWARD;
  222. if (seekto(fd, off)) {
  223. debug((stdout, "seekto(%dM) failed\n", (int)(off>>20)));
  224. off -= FORWARD;
  225. break;
  226. }
  227. if ((read(fd, buf, sizeof(buf)) != sizeof(buf))) {
  228. debug((stdout, "read @ %dM failed\n", (int)(off>>20)));
  229. off -= FORWARD;
  230. break;
  231. }
  232. }
  233. for ( ;; ) {
  234. off += FORWARD1;
  235. if (seekto(fd, off)) {
  236. debug((stdout, "seekto(%dM) failed\n", (int)(off>>20)));
  237. off -= FORWARD1;
  238. break;
  239. }
  240. if ((read(fd, buf, sizeof(buf)) != sizeof(buf))) {
  241. debug((stdout, "read @ %dM failed\n", (int)(off>>20)));
  242. off -= FORWARD1;
  243. break;
  244. }
  245. }
  246. for ( ;; ) {
  247. off += FORWARD2;
  248. if (seekto(fd, off)) {
  249. debug((stdout, "seekto(%dM) failed\n", (int)(off>>20)));
  250. off -= FORWARD2;
  251. break;
  252. }
  253. if ((read(fd, buf, sizeof(buf)) != sizeof(buf))) {
  254. debug((stdout, "read @ %dM failed\n", (int)(off>>20)));
  255. off -= FORWARD2;
  256. break;
  257. }
  258. }
  259. debug((stdout, "disksize(%s) = %d MB\n", disk, (int)(off >> 20)));
  260. return (off);
  261. }
  262. #define BIGSEEK (1<<30)
  263. int
  264. seekto(int fd, uint64 off)
  265. {
  266. #ifdef __linux__
  267. extern loff_t llseek(int, loff_t, int);
  268. if (llseek(fd, (loff_t)off, SEEK_SET) == (loff_t)-1) {
  269. return(-1);
  270. }
  271. return (0);
  272. #else
  273. uint64 here = 0;
  274. lseek(fd, 0, 0);
  275. while ((uint64)(off - here) > (uint64)BIGSEEK) {
  276. if (lseek(fd, BIGSEEK, SEEK_CUR) == -1) break;
  277. here += BIGSEEK;
  278. }
  279. assert((uint64)(off - here) <= (uint64)BIGSEEK);
  280. if (lseek(fd, (int)(off - here), SEEK_CUR) == -1) return (-1);
  281. return (0);
  282. #endif
  283. }