1/**
2 * \file
3 * \brief APIC timer drift benchmark
4 */
5
6/*
7 * Copyright (c) 2009, 2010, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <stdio.h>
16#include <inttypes.h>
17#include <barrelfish/barrelfish.h>
18#include <barrelfish/waitset.h>
19#include <barrelfish/sys_debug.h>
20#include <bench/bench.h>
21
22/// Number of iterations (experiment will run for 12 hours)
23#define ITERATIONS      43200
24//#define ITERATIONS      10
25
26/// Delay between iterations in milliseconds
27#define DELAY           1000
28
29typedef volatile uintptr_t spinflag_t;
30
31union padstruct {
32    spinflag_t start;
33    uint8_t pad[64];
34};
35
36static int init_done = 1;
37static union padstruct startflag[MAX_CPUS] __attribute__ ((aligned(64)));
38static uint32_t timestamp[MAX_CPUS][ITERATIONS];
39static int nthreads;
40static uint64_t tscperms = 0;
41
42static void domain_spanned(void *arg, errval_t reterr)
43{
44  assert(err_is_ok(reterr));
45  init_done++;
46}
47
48static int apic_measure_loop(void *arg)
49{
50    coreid_t mycore = disp_get_core_id(),
51        nextcore = (mycore + 1) % nthreads;
52    uint64_t lasttsc = 0, iteration = 0, tscdelay = DELAY * tscperms;
53
54    for(;;) {
55        if(mycore != 0 || lasttsc != 0) {
56            // Wait for signal and reset
57            while(startflag[mycore].start == 0);
58            startflag[mycore].start = 0;
59        }
60
61        // Get local APIC timer value
62        errval_t err = sys_debug_get_apic_timer(&timestamp[mycore][iteration]);
63        assert(err_is_ok(err));
64
65        iteration++;
66
67        if(mycore == 0 && lasttsc != 0) {       // Master
68            if(iteration == ITERATIONS) {
69                // We're done
70                return 0;
71            }
72
73            // Wait for rest of DELAY
74            uint64_t tscnow;
75
76            do {
77                tscnow = rdtsc();
78            } while(tscnow < lasttsc + tscdelay);
79
80            lasttsc = tscnow;
81        } else {
82            lasttsc = rdtsc();
83        }
84
85        // Signal next core
86        startflag[nextcore].start = 1;
87    }
88
89    return 0;
90}
91
92int main(int argc, char *argv[])
93{
94    int my_core_id = disp_get_core_id();
95    errval_t err;
96
97    if(argc < 2) {
98        printf("Usage: %s threads\n", argv[0]);
99    }
100    nthreads = atoi(argv[1]);
101
102    err = sys_debug_get_tsc_per_ms(&tscperms);
103    assert(err_is_ok(err));
104
105    uint32_t tickspersec;
106    err = sys_debug_get_apic_ticks_per_sec(&tickspersec);
107    assert(err_is_ok(err));
108    printf("APIC ticks per second: %" PRIu32 "\n", tickspersec);
109
110    bench_init();
111
112    /* Span domain to all cores */
113    for (int i = my_core_id + 1; i < nthreads + my_core_id; i++) {
114        err = domain_new_dispatcher(i, domain_spanned, NULL);
115        if (err_is_fail(err)) {
116            DEBUG_ERR(err, "failed to span domain");
117        }
118        assert(err_is_ok(err));
119    }
120
121    while(init_done < nthreads) {
122        thread_yield();
123    }
124
125    // Start all threads
126    for (int i = my_core_id + 1; i < nthreads + my_core_id; i++) {
127        err = domain_thread_create_on(i, apic_measure_loop, NULL, NULL);
128        assert(err_is_ok(err));
129    }
130
131    // Start locally
132    apic_measure_loop(NULL);
133
134    printf("Running on %d cores.\n", nthreads);
135
136    // Output data
137    for(uint64_t i = 0; i < ITERATIONS; i++) {
138        printf("%" PRIu64 ": ", i);
139        for(int n = 0; n < nthreads; n++) {
140            printf("%" PRIu32 " ", timestamp[n][i]);
141        }
142        printf("\n");
143    }
144
145    printf("client done.\n");
146    return 0;
147}
148