diff options
Diffstat (limited to 'tools/perf/arch/x86')
| -rw-r--r-- | tools/perf/arch/x86/tests/Build | 1 | ||||
| -rw-r--r-- | tools/perf/arch/x86/tests/arch-tests.c | 2 | ||||
| -rw-r--r-- | tools/perf/arch/x86/tests/intel-cqm.c | 2 | ||||
| -rw-r--r-- | tools/perf/arch/x86/tests/rdpmc.c | 182 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/cpuid.h | 34 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/evlist.c | 64 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/evsel.c | 72 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/header.c | 27 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/intel-bts.c | 2 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/intel-pt.c | 2 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/iostat.c | 2 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/topdown.c | 51 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/topdown.h | 1 | ||||
| -rw-r--r-- | tools/perf/arch/x86/util/tsc.c | 77 |
14 files changed, 305 insertions, 214 deletions
diff --git a/tools/perf/arch/x86/tests/Build b/tools/perf/arch/x86/tests/Build index 28d793390198..70b5bcbc15df 100644 --- a/tools/perf/arch/x86/tests/Build +++ b/tools/perf/arch/x86/tests/Build @@ -2,7 +2,6 @@ perf-$(CONFIG_DWARF_UNWIND) += regs_load.o perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o perf-y += arch-tests.o -perf-y += rdpmc.o perf-y += sample-parsing.o perf-$(CONFIG_AUXTRACE) += insn-x86.o intel-pt-pkt-decoder-test.o perf-$(CONFIG_X86_64) += bp-modify.o diff --git a/tools/perf/arch/x86/tests/arch-tests.c b/tools/perf/arch/x86/tests/arch-tests.c index 64fb73d14d2f..04018b8aa85b 100644 --- a/tools/perf/arch/x86/tests/arch-tests.c +++ b/tools/perf/arch/x86/tests/arch-tests.c @@ -3,7 +3,6 @@ #include "tests/tests.h" #include "arch-tests.h" -DEFINE_SUITE("x86 rdpmc", rdpmc); #ifdef HAVE_AUXTRACE_SUPPORT DEFINE_SUITE("x86 instruction decoder - new instructions", insn_x86); DEFINE_SUITE("Intel PT packet decoder", intel_pt_pkt_decoder); @@ -14,7 +13,6 @@ DEFINE_SUITE("x86 bp modify", bp_modify); DEFINE_SUITE("x86 Sample parsing", x86_sample_parsing); struct test_suite *arch_tests[] = { - &suite__rdpmc, #ifdef HAVE_DWARF_UNWIND_SUPPORT &suite__dwarf_unwind, #endif diff --git a/tools/perf/arch/x86/tests/intel-cqm.c b/tools/perf/arch/x86/tests/intel-cqm.c index cb5b2c6c3b3b..360a082fc928 100644 --- a/tools/perf/arch/x86/tests/intel-cqm.c +++ b/tools/perf/arch/x86/tests/intel-cqm.c @@ -56,7 +56,7 @@ int test__intel_cqm_count_nmi_context(struct test_suite *test __maybe_unused, in return TEST_FAIL; } - ret = parse_events(evlist, "intel_cqm/llc_occupancy/", NULL); + ret = parse_event(evlist, "intel_cqm/llc_occupancy/"); if (ret) { pr_debug("parse_events failed, is \"intel_cqm/llc_occupancy/\" available?\n"); err = TEST_SKIP; diff --git a/tools/perf/arch/x86/tests/rdpmc.c b/tools/perf/arch/x86/tests/rdpmc.c deleted file mode 100644 index 498413ad9c97..000000000000 --- a/tools/perf/arch/x86/tests/rdpmc.c +++ /dev/null @@ -1,182 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include <errno.h> -#include <unistd.h> -#include <stdlib.h> -#include <signal.h> -#include <sys/mman.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <linux/string.h> -#include <linux/types.h> -#include "perf-sys.h" -#include "debug.h" -#include "tests/tests.h" -#include "cloexec.h" -#include "event.h" -#include <internal/lib.h> // page_size -#include "arch-tests.h" - -static u64 rdpmc(unsigned int counter) -{ - unsigned int low, high; - - asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter)); - - return low | ((u64)high) << 32; -} - -static u64 rdtsc(void) -{ - unsigned int low, high; - - asm volatile("rdtsc" : "=a" (low), "=d" (high)); - - return low | ((u64)high) << 32; -} - -static u64 mmap_read_self(void *addr) -{ - struct perf_event_mmap_page *pc = addr; - u32 seq, idx, time_mult = 0, time_shift = 0; - u64 count, cyc = 0, time_offset = 0, enabled, running, delta; - - do { - seq = pc->lock; - barrier(); - - enabled = pc->time_enabled; - running = pc->time_running; - - if (enabled != running) { - cyc = rdtsc(); - time_mult = pc->time_mult; - time_shift = pc->time_shift; - time_offset = pc->time_offset; - } - - idx = pc->index; - count = pc->offset; - if (idx) - count += rdpmc(idx - 1); - - barrier(); - } while (pc->lock != seq); - - if (enabled != running) { - u64 quot, rem; - - quot = (cyc >> time_shift); - rem = cyc & (((u64)1 << time_shift) - 1); - delta = time_offset + quot * time_mult + - ((rem * time_mult) >> time_shift); - - enabled += delta; - if (idx) - running += delta; - - quot = count / running; - rem = count % running; - count = quot * enabled + (rem * enabled) / running; - } - - return count; -} - -/* - * If the RDPMC instruction faults then signal this back to the test parent task: - */ -static void segfault_handler(int sig __maybe_unused, - siginfo_t *info __maybe_unused, - void *uc __maybe_unused) -{ - exit(-1); -} - -static int __test__rdpmc(void) -{ - volatile int tmp = 0; - u64 i, loops = 1000; - int n; - int fd; - void *addr; - struct perf_event_attr attr = { - .type = PERF_TYPE_HARDWARE, - .config = PERF_COUNT_HW_INSTRUCTIONS, - .exclude_kernel = 1, - }; - u64 delta_sum = 0; - struct sigaction sa; - char sbuf[STRERR_BUFSIZE]; - - sigfillset(&sa.sa_mask); - sa.sa_sigaction = segfault_handler; - sa.sa_flags = 0; - sigaction(SIGSEGV, &sa, NULL); - - fd = sys_perf_event_open(&attr, 0, -1, -1, - perf_event_open_cloexec_flag()); - if (fd < 0) { - pr_err("Error: sys_perf_event_open() syscall returned " - "with %d (%s)\n", fd, - str_error_r(errno, sbuf, sizeof(sbuf))); - return -1; - } - - addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); - if (addr == (void *)(-1)) { - pr_err("Error: mmap() syscall returned with (%s)\n", - str_error_r(errno, sbuf, sizeof(sbuf))); - goto out_close; - } - - for (n = 0; n < 6; n++) { - u64 stamp, now, delta; - - stamp = mmap_read_self(addr); - - for (i = 0; i < loops; i++) - tmp++; - - now = mmap_read_self(addr); - loops *= 10; - - delta = now - stamp; - pr_debug("%14d: %14Lu\n", n, (long long)delta); - - delta_sum += delta; - } - - munmap(addr, page_size); - pr_debug(" "); -out_close: - close(fd); - - if (!delta_sum) - return -1; - - return 0; -} - -int test__rdpmc(struct test_suite *test __maybe_unused, int subtest __maybe_unused) -{ - int status = 0; - int wret = 0; - int ret; - int pid; - - pid = fork(); - if (pid < 0) - return -1; - - if (!pid) { - ret = __test__rdpmc(); - - exit(ret); - } - - wret = waitpid(pid, &status, 0); - if (wret < 0 || status) - return -1; - - return 0; -} diff --git a/tools/perf/arch/x86/util/cpuid.h b/tools/perf/arch/x86/util/cpuid.h new file mode 100644 index 000000000000..0a3ae0ace7e9 --- /dev/null +++ b/tools/perf/arch/x86/util/cpuid.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef PERF_CPUID_H +#define PERF_CPUID_H 1 + + +static inline void +cpuid(unsigned int op, unsigned int op2, unsigned int *a, unsigned int *b, + unsigned int *c, unsigned int *d) +{ + /* + * Preserve %ebx/%rbx register by either placing it in %rdi or saving it + * on the stack - x86-64 needs to avoid the stack red zone. In PIC + * compilations %ebx contains the address of the global offset + * table. %rbx is occasionally used to address stack variables in + * presence of dynamic allocas. + */ + asm( +#if defined(__x86_64__) + "mov %%rbx, %%rdi\n" + "cpuid\n" + "xchg %%rdi, %%rbx\n" +#else + "pushl %%ebx\n" + "cpuid\n" + "movl %%ebx, %%edi\n" + "popl %%ebx\n" +#endif + : "=a"(*a), "=D"(*b), "=c"(*c), "=d"(*d) + : "a"(op), "2"(op2)); +} + +void get_cpuid_0(char *vendor, unsigned int *lvl); + +#endif diff --git a/tools/perf/arch/x86/util/evlist.c b/tools/perf/arch/x86/util/evlist.c index 68f681ad54c1..cb59ce9b9638 100644 --- a/tools/perf/arch/x86/util/evlist.c +++ b/tools/perf/arch/x86/util/evlist.c @@ -3,20 +3,66 @@ #include "util/pmu.h" #include "util/evlist.h" #include "util/parse-events.h" +#include "util/event.h" +#include "util/pmu-hybrid.h" #include "topdown.h" -#define TOPDOWN_L1_EVENTS "{slots,topdown-retiring,topdown-bad-spec,topdown-fe-bound,topdown-be-bound}" -#define TOPDOWN_L2_EVENTS "{slots,topdown-retiring,topdown-bad-spec,topdown-fe-bound,topdown-be-bound,topdown-heavy-ops,topdown-br-mispredict,topdown-fetch-lat,topdown-mem-bound}" +static int ___evlist__add_default_attrs(struct evlist *evlist, + struct perf_event_attr *attrs, + size_t nr_attrs) +{ + struct perf_cpu_map *cpus; + struct evsel *evsel, *n; + struct perf_pmu *pmu; + LIST_HEAD(head); + size_t i = 0; + + for (i = 0; i < nr_attrs; i++) + event_attr_init(attrs + i); + + if (!perf_pmu__has_hybrid()) + return evlist__add_attrs(evlist, attrs, nr_attrs); + + for (i = 0; i < nr_attrs; i++) { + if (attrs[i].type == PERF_TYPE_SOFTWARE) { + evsel = evsel__new(attrs + i); + if (evsel == NULL) + goto out_delete_partial_list; + list_add_tail(&evsel->core.node, &head); + continue; + } + + perf_pmu__for_each_hybrid_pmu(pmu) { + evsel = evsel__new(attrs + i); + if (evsel == NULL) + goto out_delete_partial_list; + evsel->core.attr.config |= (__u64)pmu->type << PERF_PMU_TYPE_SHIFT; + cpus = perf_cpu_map__get(pmu->cpus); + evsel->core.cpus = cpus; + evsel->core.own_cpus = perf_cpu_map__get(cpus); + evsel->pmu_name = strdup(pmu->name); + list_add_tail(&evsel->core.node, &head); + } + } + + evlist__splice_list_tail(evlist, &head); + + return 0; + +out_delete_partial_list: + __evlist__for_each_entry_safe(&head, n, evsel) + evsel__delete(evsel); + return -1; +} -int arch_evlist__add_default_attrs(struct evlist *evlist) +int arch_evlist__add_default_attrs(struct evlist *evlist, + struct perf_event_attr *attrs, + size_t nr_attrs) { - if (!pmu_have_event("cpu", "slots")) - return 0; + if (nr_attrs) + return ___evlist__add_default_attrs(evlist, attrs, nr_attrs); - if (pmu_have_event("cpu", "topdown-heavy-ops")) - return parse_events(evlist, TOPDOWN_L2_EVENTS, NULL); - else - return parse_events(evlist, TOPDOWN_L1_EVENTS, NULL); + return topdown_parse_events(evlist); } struct evsel *arch_evlist__leader(struct list_head *list) diff --git a/tools/perf/arch/x86/util/evsel.c b/tools/perf/arch/x86/util/evsel.c index 3501399cef35..ea3972d785d1 100644 --- a/tools/perf/arch/x86/util/evsel.c +++ b/tools/perf/arch/x86/util/evsel.c @@ -6,6 +6,10 @@ #include "util/pmu.h" #include "linux/string.h" #include "evsel.h" +#include "util/debug.h" + +#define IBS_FETCH_L3MISSONLY (1ULL << 59) +#define IBS_OP_L3MISSONLY (1ULL << 16) void arch_evsel__set_sample_weight(struct evsel *evsel) { @@ -61,3 +65,71 @@ bool arch_evsel__must_be_in_group(const struct evsel *evsel) (strcasestr(evsel->name, "slots") || strcasestr(evsel->name, "topdown")); } + +int arch_evsel__hw_name(struct evsel *evsel, char *bf, size_t size) +{ + u64 event = evsel->core.attr.config & PERF_HW_EVENT_MASK; + u64 pmu = evsel->core.attr.config >> PERF_PMU_TYPE_SHIFT; + const char *event_name; + + if (event < PERF_COUNT_HW_MAX && evsel__hw_names[event]) + event_name = evsel__hw_names[event]; + else + event_name = "unknown-hardware"; + + /* The PMU type is not required for the non-hybrid platform. */ + if (!pmu) + return scnprintf(bf, size, "%s", event_name); + + return scnprintf(bf, size, "%s/%s/", + evsel->pmu_name ? evsel->pmu_name : "cpu", + event_name); +} + +static void ibs_l3miss_warn(void) +{ + pr_warning( +"WARNING: Hw internally resets sampling period when L3 Miss Filtering is enabled\n" +"and tagged operation does not cause L3 Miss. This causes sampling period skew.\n"); +} + +void arch__post_evsel_config(struct evsel *evsel, struct perf_event_attr *attr) +{ + struct perf_pmu *evsel_pmu, *ibs_fetch_pmu, *ibs_op_pmu; + static int warned_once; + /* 0: Uninitialized, 1: Yes, -1: No */ + static int is_amd; + + if (warned_once || is_amd == -1) + return; + + if (!is_amd) { + struct perf_env *env = evsel__env(evsel); + + if (!perf_env__cpuid(env) || !env->cpuid || + !strstarts(env->cpuid, "AuthenticAMD")) { + is_amd = -1; + return; + } + is_amd = 1; + } + + evsel_pmu = evsel__find_pmu(evsel); + if (!evsel_pmu) + return; + + ibs_fetch_pmu = perf_pmu__find("ibs_fetch"); + ibs_op_pmu = perf_pmu__find("ibs_op"); + + if (ibs_fetch_pmu && ibs_fetch_pmu->type == evsel_pmu->type) { + if (attr->config & IBS_FETCH_L3MISSONLY) { + ibs_l3miss_warn(); + warned_once = 1; + } + } else if (ibs_op_pmu && ibs_op_pmu->type == evsel_pmu->type) { + if (attr->config & IBS_OP_L3MISSONLY) { + ibs_l3miss_warn(); + warned_once = 1; + } + } +} diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c index 578c8c568ffd..a51444a77a5f 100644 --- a/tools/perf/arch/x86/util/header.c +++ b/tools/perf/arch/x86/util/header.c @@ -9,18 +9,17 @@ #include "../../../util/debug.h" #include "../../../util/header.h" +#include "cpuid.h" -static inline void -cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c, - unsigned int *d) +void get_cpuid_0(char *vendor, unsigned int *lvl) { - __asm__ __volatile__ (".byte 0x53\n\tcpuid\n\t" - "movl %%ebx, %%esi\n\t.byte 0x5b" - : "=a" (*a), - "=S" (*b), - "=c" (*c), - "=d" (*d) - : "a" (op)); + unsigned int b, c, d; + + cpuid(0, 0, lvl, &b, &c, &d); + strncpy(&vendor[0], (char *)(&b), 4); + strncpy(&vendor[4], (char *)(&d), 4); + strncpy(&vendor[8], (char *)(&c), 4); + vendor[12] = '\0'; } static int @@ -31,14 +30,10 @@ __get_cpuid(char *buffer, size_t sz, const char *fmt) int nb; char vendor[16]; - cpuid(0, &lvl, &b, &c, &d); - strncpy(&vendor[0], (char *)(&b), 4); - strncpy(&vendor[4], (char *)(&d), 4); - strncpy(&vendor[8], (char *)(&c), 4); - vendor[12] = '\0'; + get_cpuid_0(vendor, &lvl); if (lvl >= 1) { - cpuid(1, &a, &b, &c, &d); + cpuid(1, 0, &a, &b, &c, &d); family = (a >> 8) & 0xf; /* bits 11 - 8 */ model = (a >> 4) & 0xf; /* Bits 7 - 4 */ diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c index bcccfbade5c6..439c2956f3e7 100644 --- a/tools/perf/arch/x86/util/intel-bts.c +++ b/tools/perf/arch/x86/util/intel-bts.c @@ -233,7 +233,7 @@ static int intel_bts_recording_options(struct auxtrace_record *itr, struct evsel *tracking_evsel; int err; - err = parse_events(evlist, "dummy:u", NULL); + err = parse_event(evlist, "dummy:u"); if (err) return err; diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index 06c2cdfd8f2f..13933020a79e 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c @@ -426,7 +426,7 @@ static int intel_pt_track_switches(struct evlist *evlist) if (!evlist__can_select_event(evlist, sched_switch)) return -EPERM; - err = parse_events(evlist, sched_switch, NULL); + err = parse_event(evlist, sched_switch); if (err) { pr_debug2("%s: failed to parse %s, error %d\n", __func__, sched_switch, err); diff --git a/tools/perf/arch/x86/util/iostat.c b/tools/perf/arch/x86/util/iostat.c index 792cd75ade33..404de795ec0b 100644 --- a/tools/perf/arch/x86/util/iostat.c +++ b/tools/perf/arch/x86/util/iostat.c @@ -316,7 +316,7 @@ static int iostat_event_group(struct evlist *evl, sprintf(iostat_cmd, iostat_cmd_template, list->rps[idx]->pmu_idx, list->rps[idx]->pmu_idx, list->rps[idx]->pmu_idx, list->rps[idx]->pmu_idx); - ret = parse_events(evl, iostat_cmd, NULL); + ret = parse_event(evl, iostat_cmd); if (ret) goto err; } diff --git a/tools/perf/arch/x86/util/topdown.c b/tools/perf/arch/x86/util/topdown.c index f81a7cfe4d63..54810f9acd6f 100644 --- a/tools/perf/arch/x86/util/topdown.c +++ b/tools/perf/arch/x86/util/topdown.c @@ -3,9 +3,17 @@ #include "api/fs/fs.h" #include "util/pmu.h" #include "util/topdown.h" +#include "util/evlist.h" +#include "util/debug.h" +#include "util/pmu-hybrid.h" #include "topdown.h" #include "evsel.h" +#define TOPDOWN_L1_EVENTS "{slots,topdown-retiring,topdown-bad-spec,topdown-fe-bound,topdown-be-bound}" +#define TOPDOWN_L1_EVENTS_CORE "{slots,cpu_core/topdown-retiring/,cpu_core/topdown-bad-spec/,cpu_core/topdown-fe-bound/,cpu_core/topdown-be-bound/}" +#define TOPDOWN_L2_EVENTS "{slots,topdown-retiring,topdown-bad-spec,topdown-fe-bound,topdown-be-bound,topdown-heavy-ops,topdown-br-mispredict,topdown-fetch-lat,topdown-mem-bound}" +#define TOPDOWN_L2_EVENTS_CORE "{slots,cpu_core/topdown-retiring/,cpu_core/topdown-bad-spec/,cpu_core/topdown-fe-bound/,cpu_core/topdown-be-bound/,cpu_core/topdown-heavy-ops/,cpu_core/topdown-br-mispredict/,cpu_core/topdown-fetch-lat/,cpu_core/topdown-mem-bound/}" + /* Check whether there is a PMU which supports the perf metrics. */ bool topdown_sys_has_perf_metrics(void) { @@ -73,3 +81,46 @@ bool arch_topdown_sample_read(struct evsel *leader) return false; } + +const char *arch_get_topdown_pmu_name(struct evlist *evlist, bool warn) +{ + const char *pmu_name; + + if (!perf_pmu__has_hybrid()) + return "cpu"; + + if (!evlist->hybrid_pmu_name) { + if (warn) + pr_warning("WARNING: default to use cpu_core topdown events\n"); + evlist->hybrid_pmu_name = perf_pmu__hybrid_type_to_pmu("core"); + } + + pmu_name = evlist->hybrid_pmu_name; + + return pmu_name; +} + +int topdown_parse_events(struct evlist *evlist) +{ + const char *topdown_events; + const char *pmu_name; + + if (!topdown_sys_has_perf_metrics()) + return 0; + + pmu_name = arch_get_topdown_pmu_name(evlist, false); + + if (pmu_have_event(pmu_name, "topdown-heavy-ops")) { + if (!strcmp(pmu_name, "cpu_core")) + topdown_events = TOPDOWN_L2_EVENTS_CORE; + else + topdown_events = TOPDOWN_L2_EVENTS; + } else { + if (!strcmp(pmu_name, "cpu_core")) + topdown_events = TOPDOWN_L1_EVENTS_CORE; + else + topdown_events = TOPDOWN_L1_EVENTS; + } + + return parse_event(evlist, topdown_events); +} diff --git a/tools/perf/arch/x86/util/topdown.h b/tools/perf/arch/x86/util/topdown.h index 46bf9273e572..7eb81f042838 100644 --- a/tools/perf/arch/x86/util/topdown.h +++ b/tools/perf/arch/x86/util/topdown.h @@ -3,5 +3,6 @@ #define _TOPDOWN_H 1 bool topdown_sys_has_perf_metrics(void); +int topdown_parse_events(struct evlist *evlist); #endif diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c index 559365f8fe52..eb2b5195bd02 100644 --- a/tools/perf/arch/x86/util/tsc.c +++ b/tools/perf/arch/x86/util/tsc.c @@ -1,7 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 #include <linux/types.h> +#include <math.h> +#include <string.h> +#include "../../../util/debug.h" #include "../../../util/tsc.h" +#include "cpuid.h" u64 rdtsc(void) { @@ -11,3 +15,76 @@ u64 rdtsc(void) return low | ((u64)high) << 32; } + +/* + * Derive the TSC frequency in Hz from the /proc/cpuinfo, for example: + * ... + * model name : Intel(R) Xeon(R) Gold 6154 CPU @ 3.00GHz + * ... + * will return 3000000000. + */ +static double cpuinfo_tsc_freq(void) +{ + double result = 0; + FILE *cpuinfo; + char *line = NULL; + size_t len = 0; + + cpuinfo = fopen("/proc/cpuinfo", "r"); + if (!cpuinfo) { + pr_err("Failed to read /proc/cpuinfo for TSC frequency"); + return NAN; + } + while (getline(&line, &len, cpuinfo) > 0) { + if (!strncmp(line, "model name", 10)) { + char *pos = strstr(line + 11, " @ "); + + if (pos && sscanf(pos, " @ %lfGHz", &result) == 1) { + result *= 1000000000; + goto out; + } + } + } +out: + if (fpclassify(result) == FP_ZERO) + pr_err("Failed to find TSC frequency in /proc/cpuinfo"); + + free(line); + fclose(cpuinfo); + return result; +} + +double arch_get_tsc_freq(void) +{ + unsigned int a, b, c, d, lvl; + static bool cached; + static double tsc; + char vendor[16]; + + if (cached) + return tsc; + + cached = true; + get_cpuid_0(vendor, &lvl); + if (!strstr(vendor, "Intel")) + return 0; + + /* + * Don't support Time Stamp Counter and + * Nominal Core Crystal Clock Information Leaf. + */ + if (lvl < 0x15) { + tsc = cpuinfo_tsc_freq(); + return tsc; + } + + cpuid(0x15, 0, &a, &b, &c, &d); + /* TSC frequency is not enumerated */ + if (!a || !b || !c) { + tsc = cpuinfo_tsc_freq(); + return tsc; + } + + tsc = (double)c * (double)b / (double)a; + return tsc; +} |