/* * Positioning time estimator for any disk * premise: Non-destructive measurements, with disk cache turned off */ #include #include #include #include #include #include #include extern void diskcache(const char *devicename, int action); int verbose = 1; #define vprintf(x...) do { if (verbose) fprintf(stderr , ## x); } while(0) int fd; int MHz; int rpm, size; char buf[512]; #define NOW (rdtsc()/MHz) #define SEEK(n) lseek(fd, (n)*512LL, SEEK_SET) #define READ ({ int a, b, t; \ b = NOW; \ if (read(fd, buf, sizeof(buf)) < 0) pdie("read"); \ a = NOW; \ t = (a - b); \ (int)t; }) #define SREAD(n) ({ SEEK(n); READ; }) int determine_MHz() { int possible[] = { 0, 25, 33, 50, 66, 75 }; quad_t r1, r2; struct timeval t1, t2; int x, y, z = INT_MAX; int e; MHz = 1; gettimeofday(&t1, 0); r1 = NOW; sleep(1); gettimeofday(&t2, 0); r2 = NOW; e = (r2 - r1)/((t2.tv_sec - t1.tv_sec) * 1000000 + t2.tv_usec - t1.tv_usec); for (x = 0; ; x += 100) { for (y = 0; y < sizeof(possible)/sizeof(possible[0]); y++) { int p = x + possible[y], d = e - p; if (d < 0) d = -d; if (z > d) z = d, MHz = p; else { vprintf("CPU benchmarked at %d MHz\n", MHz); return MHz; } } } } int determine_size(void) { int f = 1, t = 1, m; /* binary search */ while (1) { SEEK(t); if (read(fd, buf, sizeof(buf)) < 0) break; f = t; t = t * 2; } while (f != t-1) { m = (f + t) / 2; SEEK(m); if (read(fd, buf, sizeof(buf)) < 0) t = m; else f = m; } vprintf(" logical size measured to be %.2f GB (%d sectors)\n", ((double)t)/2.0/1024.0/1024.0, t); return t; } void random_reads() { int i; for (i = 0; i < 32; i++) SREAD(random() % size); vprintf(" performing random reads to settle head\n"); } /* * prerequisite: the controller cache should be turned off. kernel module. */ int determine_rotational_speed(void) { static int possible[] = { 3600, 4400, 5400, 7200, 10000, 12000, 15000 }; int x, y, z, i, e; int n = random() % size; SREAD(n); x = 0; for (i = 0; i < 32; i++) x += SREAD(n); e = 1000 * 1000 * 60 / (x / 32); z = INT_MAX; x = e; for (i = 0; i < sizeof(possible)/sizeof(possible[0]); i++) { y = possible[i] - e; if (y < 0) y = -y; if (z > y) z = y, x = possible[i]; } vprintf(" rotational speed of %d rpm\n", x); return x; } #define SR(n) ({ SREAD(0); SREAD(n); }) void find_start_positions() { int i, j; int d = 8333/2, t1, t2, u1, u2; #define N 10000 static int pos[N] = {0}, hi[N] = {0}, lo[N] = {0}, n = 0; int est = 0, avg = 0; for (i = 0, j = 0; j < 2; j++) { quad_t tmp = NOW; for (t1 = 0; i < size; i++) { if ((u1 = SR(i)) < t1 - d) { pos[n] = i; hi[n] = t1; lo[n] = u1; break; } t1 = u1; } i += 30; printf("%d\t%d \tpos %d\thi %d\tlo %d\n", (int)((NOW - tmp)/1000), n, pos[n], hi[n], lo[n]); n++; } est = pos[1] - pos[0]; avg = (hi[1] + lo[1])/2; for (i = pos[1] + est; i < size; i += est) { quad_t tmp = NOW; t1 = u2 = SR(i); /* printf("SR(%d) = %d, est %d, avg %d\n", i, u2, est, avg); */ if (u2 > avg) { for (j = 0; j < est/2; j++) { if ((u1 = SR(i+j+1)) < t1 - d) { pos[n] = i+j+1; hi[n] = t1; lo[n] = u1; break; } t1 = u1; } if (j == est/2) printf("bang+ i=%d,j=%d,est=%d,n=%d\n",i,j,est,n); } else { for (j = 0; j < est/2; j++) { if ((t2 = SR(i-j-1)) > u2 + d) { pos[n] = i-j; hi[n] = t2; lo[n] = u2; break; } u2 = t2; } if (j == est/2) printf("bang- i=%d,j=%d,est=%d,n=%d\n",i,j,est,n); } printf("%d\t%d \tpos %d\thi %d\tlo %d\n", (int)((NOW - tmp)/1000), n, pos[n], hi[n], lo[n]); fflush(stdout); if (pos[n] > pos[n-1]) { est = pos[n] - pos[n-1]; avg = (hi[n] + lo[n])/2; } n++; } } void random_measurements() { int i, m = 0, n; SREAD(m); for (i = 0; i < 100; i++) { n = random() % size; printf("%d %d %d\n", m, n, SREAD(n)); m = n; } } void measure_seek_curve() { int pos, i, iter = 50, k = 0, l = 0, d = -1, d0 = -1, ln = 0; double leap; vprintf(" estimating seek curve via measurements (may take several minutes) "); for (leap = 1; leap < size; leap = leap * 1.1) k++; for (leap = 1; leap < size; leap = leap * 1.1) { quad_t tot = 0; for (i = 0; i < iter; i++) { pos = random() % (size - (int)leap); SREAD(pos); tot += SREAD(pos + leap); } printf("%d %d\n", (int)leap, (int)(tot/iter)); d = (l++*100)/k; if (d0 != d) { char s[1024]; int p; for (p = 0; p < ln; p++) vprintf("\010"); d0 = d; sprintf(s, "%d%%", d0); ln = strlen(s); vprintf("%s", s); } } { int p; for (p = 0; p < ln; p++) vprintf("\010"); vprintf("100%% done.\n"); } } void calibrate_disk(const char *devicename) { fd = open(devicename, O_RDONLY); if (fd < 0) pdie("open"); diskcache(devicename, 0); size = determine_size(); random_reads(); rpm = determine_rotational_speed(); diskcache(devicename, 1); measure_seek_curve(); } #if 0 int main(int argc, char *argv[]) { int i, j; char *devtypes[] = { "da", "ad" }; srandom(getpid() + time(0)); MHz = determine_MHz(); for (i = 0; i < sizeof(devtypes)/sizeof(devtypes[0]); i++) { for (j = 0; ; j++) { char s[1024]; int fd; snprintf(s, sizeof(s), "/dev/%s%d", devtypes[i], j); fd = open(s, O_RDONLY); if (fd < 0) { if (errno == ENOENT) break; else if (errno == EPERM) printf("warning: no permissions on %s; not sure what's inside\n", s); } else { vprintf("disk %s detected\n", s); calibrate_disk(s); return 0; /* only one disk */ } } } return 0; } #endif #include "../config.h" int main(int argc, char *argv[]) { char s[1024]; int fd; srandom(getpid() + time(0)); snprintf(s, sizeof(s), "/dev/%s%d", (MAJOR == 0xd ? "da" : MAJOR == 0x74 ? "ad" : "unknown"), UNIT); fd = open(s, O_RDONLY); if (fd < 0) pdie("postime: %s", s); MHz = determine_MHz(); calibrate_disk(s); return 0; }