/* gcc -m32 -O3 -Wall -fomit-frame-pointer -funroll-loops -g -o time-segops time-segops.c -lrt */ #include #include #include #include #include #include #include #define GTOD 0 #define SYNC 0 #define COUNT 50000000 /* different glibc's call this different things, so define our own */ struct desc { unsigned int entry_number; unsigned long base_addr; unsigned int limit; unsigned int seg_32bit:1; unsigned int contents:2; unsigned int read_exec_only:1; unsigned int limit_in_pages:1; unsigned int seg_not_present:1; unsigned int useable:1; }; /* These don't seem to be consistently defined in glibc */ static int set_thread_area(struct desc *desc) { int ret; asm("int $0x80" : "=a" (ret) : "0" (__NR_set_thread_area), "b" (desc) : "memory"); if (ret < 0) { errno = -ret; ret = -1; } return ret; } static int modify_ldt(int func, struct desc *desc, int size) { int ret; asm("int $0x80" : "=a" (ret) : "0" (__NR_modify_ldt), "b" (func), "c" (desc), "d" (size) : "memory"); if (ret < 0) { errno = -ret; ret = -1; } return ret; } static inline unsigned long long now(void) { #if GTOD struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000000000ull + tv.tv_usec * 1000ull; #else struct timespec ts; clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); return ts.tv_sec * 1000000000ull + ts.tv_nsec; #endif } /* Simulate an exception's effect on the pipeline? */ static inline void sync(void) { if (0) { int a,b,c,d; asm volatile("cpuid" : "=a" (a), "=b" (b), "=c" (c), "=d" (d) : "0" (0), "2" (0) : "memory"); } else asm volatile("" : : : "memory"); } static const char *test_none(int seg, int *offset) { int i; for(i = 0; i < COUNT; i++) { sync(); } return ""; } static const char *test_fs(int seg, int *offset) { int i; for(i = 0; i < COUNT; i++) { asm volatile("push %%fs; mov %1, %%fs; addl $1, %%fs:%0; popl %%fs" : "+m" (*offset): "r" (seg) : "memory"); sync(); } return "fs"; } static const char *test_gs(int seg, int *offset) { int i; for(i = 0; i < COUNT; i++) { asm volatile("push %%gs; mov %1, %%gs; addl $1, %%gs:%0; popl %%gs" : "+m" (*offset): "r" (seg) : "memory"); sync(); } return "gs"; } typedef const char *(*test_t)(int, int *); static const test_t tests[] = { test_none, test_fs, test_gs, NULL, }; static int segment[1]; static void test(int seg, int *offset, const char *segdesc) { int i; for(i = 0; tests[i]; i++) { unsigned long long start, end; unsigned long long delta; const char *t; start = now(); t = (*tests[i])(seg, offset); end = now(); delta = (end - start); printf(" %s with %s selector: %lluns/iteration\n", t, segdesc, delta / COUNT); } } struct cpu { char modelname[100]; int family, model, stepping; float speed; }; static int cpu_details(struct cpu *cpu) { FILE *fp = fopen("/proc/cpuinfo", "r"); char buf[500]; if (fp == NULL) { perror("open /proc/cpuinfo"); return 0; } while(fgets(buf, sizeof(buf), fp) != NULL) { char *col = strchr(buf, ':'); char *val; if (col == NULL) continue; val = col+1; while(*val == ' ') val++; col--; while(col > buf && isspace(*col)) col--; col[1] = 0; col = strchr(val, '\n'); if (col) *col = 0; //printf("name=%s val=%s\n", buf, val); if (strcmp(buf, "model name") == 0) strcpy(cpu->modelname, val); if (strcmp(buf, "cpu family") == 0) sscanf(val, "%d", &cpu->family); if (strcmp(buf, "model") == 0) sscanf(val, "%d", &cpu->model); if (strcmp(buf, "stepping") == 0) sscanf(val, "%d", &cpu->stepping); if (strcmp(buf, "cpu MHz") == 0) sscanf(val, "%f", &cpu->speed); if (strcmp(buf, "processor") == 0 && strcmp(val, "0") != 0) break; } fclose(fp); return 1; } int main() { int ds, fs, gs; static struct desc desc = { .entry_number = 1, .base_addr = (unsigned long)segment, .limit = sizeof(segment)-1, .seg_32bit = 1, .contents = 0, .read_exec_only = 0, .limit_in_pages = 0, .seg_not_present = 0, .useable = 1, }; int gdtseg, ldtseg; struct cpu cpu; float speed; if (!cpu_details(&cpu)) { printf("can't read CPU details"); return 1; } speed = cpu.speed; if (modify_ldt(1, &desc, sizeof(desc)) == -1) perror("modify ldt"); ldtseg = desc.entry_number * 8 | 4 | 3; desc.entry_number = -1; if (set_thread_area(&desc) == -1) perror("set_thread_area"); gdtseg = desc.entry_number * 8 | 3; asm volatile("mov %%ds, %0; " "mov %%fs, %1; " "mov %%gs, %2" : "=r" (ds), "=r" (fs), "=r" (gs) : : "memory"); printf("\"%s\" @%gMhz (%d,%d,%d):\n", cpu.modelname, cpu.speed, cpu.family, cpu.model, cpu.stepping); printf("ds=%x fs=%x gs=%x ldt=%x gdt=%x %s %s\n", ds, fs, gs, ldtseg, gdtseg, GTOD ? "GTOD" : "CPUTIME", SYNC ? "SYNC" : ""); test(ds, segment, "data"); printf("\n"); test(ldtseg, 0, "LDT"); printf("\n"); test(gdtseg, 0, "GDT"); if (cpu_details(&cpu)) { if (speed != cpu.speed) printf("cpu speed changed %f->%f?! disable CPUFREQ\n", speed, cpu.speed); } return 0; }