/* Copyright (C) 2003,2004 Andi Kleen, SuSE Labs. numamon is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2. numamon is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should find a copy of v2 of the GNU General Public License somewhere on your Linux system; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Display some numa statistics collected by the CPU. Opteron specific. Also not reliable because the counters are not quite correct in hardware. */ #define _LARGE_FILE_SOURCE 1 #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include enum { LOCALLOCAL = 0, LOCALREMOTE = 1, REMOTELOCAL = 2 }; static int mem[] = { [LOCALLOCAL] = 0xa8, [LOCALREMOTE] = 0x98, [REMOTELOCAL] = 0x68 }; static int io[] = { [LOCALLOCAL] = 0xa4, [LOCALREMOTE] = 0x94, [REMOTELOCAL] = 0x64 }; static int *masks = mem; #define err(x) perror(x),exit(1) #define PERFEVTSEL0 0xc0010000 #define PERFEVTSEL1 0xc0010001 #define PERFEVTSEL2 0xc0010002 #define PERFEVTSEL3 0xc0010003 #define PERFCTR0 0xc0010004 #define PERFCTR1 0xc0010005 #define PERFCTR2 0xc0010006 #define PERFCTR3 0xc0010007 #define EVENT 0xe9 #define PERFEVTSEL_EN (1 << 22) #define PERFEVTSEL_OS (1 << 17) #define PERFEVTSEL_USR (1 << 16) #define BASE (EVENT | PERFEVTSEL_EN | PERFEVTSEL_OS | PERFEVTSEL_USR) #define MAXCPU 8 int force = 0; int msrfd[MAXCPU]; int delay; int absolute; char *cfilter; int verbose; void usage(void); void Vprintf(char *fmt, ...) { va_list ap; va_start(ap,fmt); if (verbose) vfprintf(stderr,fmt,ap); va_end(ap); } unsigned long long rdmsr(int cpu, unsigned long msr) { unsigned long long val; if (pread(msrfd[cpu], &val, 8, msr) != 8) { fprintf(stderr, "rdmsr of %lx failed: %s\n", msr, strerror(errno)); exit(1); } return val; } void wrmsr(int cpu, unsigned long msr, unsigned long long value) { if (pwrite(msrfd[cpu], &value, 8, msr) != 8) { fprintf(stderr, "wdmsr of %lx failed: %s\n", msr, strerror(errno)); exit(1); } } int cpufilter(int cpu) { long num; char *end; char *s; if (!cfilter) return 1; for (s = cfilter;;) { num = strtoul(s, &end, 0); if (end == s) usage(); if (cpu == num) return 1; if (*end == ',') s = end+1; else if (*end == 0) break; else usage(); } return 0; } void checkcounter(int cpu, int clear) { int i; for (i = 1; i < 4; i++) { int clear_this = clear; unsigned long long evtsel = rdmsr(cpu, PERFEVTSEL0 + i); Vprintf("%d: %x %Lx\n", cpu, PERFEVTSEL0 + i, evtsel); if (!(evtsel & PERFEVTSEL_EN)) { Vprintf("reinit %d\n", cpu); wrmsr(cpu, PERFEVTSEL0 + i, BASE | masks[i - 1]); clear_this = 1; } else if (evtsel == (BASE | (masks[i-1] << 8))) { /* everything fine */ } else if (force) { Vprintf("reinit force %d\n", cpu); wrmsr(cpu, PERFEVTSEL0 + i, BASE | (masks[i - 1] << 8)); clear_this = 1; } else { fprintf(stderr, "perfctr %d cpu %d already used with %Lx\n", i, cpu, evtsel); fprintf(stderr, "Consider using -f if you know what you're doing.\n"); exit(1); } if (clear_this) { Vprintf("clearing %d\n", cpu); wrmsr(cpu, PERFCTR0 + i, 0); } } } void setup(int clear) { DIR *dir; struct dirent *d; int numcpus = 0; memset(msrfd, -1, sizeof(msrfd)); dir = opendir("/dev/cpu"); if (!dir) err("cannot open /dev/cpu"); while ((d = readdir(dir)) != NULL) { char buf[64]; char *end; long cpunum = strtoul(d->d_name, &end, 0); if (*end != 0) continue; if (cpunum > MAXCPU) { fprintf(stderr, "too many cpus %ld %s\n", cpunum, d->d_name); continue; } if (!cpufilter(cpunum)) continue; snprintf(buf, 63, "/dev/cpu/%ld/msr", cpunum); msrfd[cpunum] = open64(buf, O_RDWR); if (msrfd[cpunum] < 0) continue; numcpus++; checkcounter(cpunum, clear); } closedir(dir); if (numcpus == 0) { fprintf(stderr, "No CPU found using MSR driver.\n"); exit(1); } } void printf_padded(int pad, char *fmt, ...) { char buf[pad + 1]; va_list ap; va_start(ap, fmt); vsnprintf(buf, pad, fmt, ap); printf("%-*s", pad, buf); va_end(ap); } void print_header(void) { printf_padded(4, "CPU "); printf_padded(16, "LOCAL"); printf_padded(16, "LOCAL->REMOTE"); printf_padded(16, "REMOTE->LOCAL"); putchar('\n'); } void print_cpu(int cpu) { int i; static unsigned long long lastval[4]; printf_padded(4, "%d", cpu); for (i = 1; i < 4; i++) { unsigned long long val = rdmsr(cpu, PERFCTR0 + i); if (absolute) printf_padded(16, "%Lu", val); else printf_padded(16, "%Lu", val - lastval[i]); lastval[i] = val; } putchar('\n'); } void dumpall(void) { int cnt = 0; int cpu; print_header(); for (;;) { for (cpu = 0; cpu < MAXCPU; ++cpu) { if (msrfd[cpu] < 0) continue; print_cpu(cpu); } if (!delay) break; sleep(delay); if (++cnt > 40) { cnt = 0; print_header(); } } } void checkk8(void) { char *line = NULL; size_t size = 0; int bad = 0; FILE *f = fopen("/proc/cpuinfo", "r"); if (!f) return; while (getline(&line, &size, f) > 0) { if (!strncmp("vendor_id", line, 9)) { if (!strstr(line, "AMD")) bad++; } if (!strncmp("cpu family", line, 10)) { char *s = line + strcspn(line,":"); int family; if (*s == ':') ++s; family = strtoul(s, NULL, 0); if (family != 15) bad++; } } if (bad) { printf("not a opteron cpu\n"); exit(1); } free(line); fclose(f); } void usage(void) { fprintf(stderr, "usage: numamon [args] [delay]\n"); fprintf(stderr, " -f forcibly overwrite counters\n"); fprintf(stderr, " -i count IO (default memory)\n"); fprintf(stderr, " -a print absolute counter values (with delay)\n"); fprintf(stderr, " -s setup counters and exit\n"); fprintf(stderr, " -c clear counters and exit\n"); fprintf(stderr, " -m Print memory traffic (default)\n"); fprintf(stderr, " -C cpu{,cpu} only print for cpus\n"); fprintf(stderr, " -v Be verbose\n"); exit(1); } int main(int ac, char **av) { int opt; checkk8(); while ((opt = getopt(ac,av,"ifscmaC:v")) != -1) { switch (opt) { case 'f': force = 1; break; case 'c': setup(1); exit(0); case 's': setup(0); exit(0); case 'm': masks = mem; break; case 'i': masks = io; break; case 'a': absolute = 1; break; case 'C': cfilter = optarg; break; case 'v': verbose = 1; break; default: usage(); } } if (av[optind]) { char *end; delay = strtoul(av[optind], &end, 10); if (*end) usage(); if (av[optind+1]) usage(); } setup(0); dumpall(); return 0; }