| /* |
| * qemu command line | grep latency | cut -f 2 -d ":" > latency |
| * |
| * In octave: |
| * load latency |
| * min(latency) |
| * max(latency) |
| * mean(latency) |
| * hist(latency, 50) |
| */ |
| |
| /* |
| * for host tracing of breakmax option: |
| * |
| * # cd /sys/kernel/debug/tracing/ |
| * # echo x86-tsc > trace_clock |
| * # echo "kvm_exit kvm_entry kvm_msr" > set_event |
| * # echo "sched_switch $extratracepoints" >> set_event |
| * # echo apic_timer_fn > set_ftrace_filter |
| * # echo "function" > current_tracer |
| */ |
| |
| #include "libcflat.h" |
| #include "apic.h" |
| #include "vm.h" |
| #include "smp.h" |
| #include "desc.h" |
| #include "isr.h" |
| #include "msr.h" |
| |
| static void test_lapic_existence(void) |
| { |
| u32 lvr; |
| |
| lvr = apic_read(APIC_LVR); |
| printf("apic version: %x\n", lvr); |
| report("apic existence", (u16)lvr == 0x14); |
| } |
| |
| #define TSC_DEADLINE_TIMER_VECTOR 0xef |
| |
| static int tdt_count; |
| u64 exptime; |
| int delta; |
| #define TABLE_SIZE 10000 |
| u64 table[TABLE_SIZE]; |
| volatile int table_idx; |
| volatile int hitmax = 0; |
| int breakmax = 0; |
| |
| static void tsc_deadline_timer_isr(isr_regs_t *regs) |
| { |
| u64 now = rdtsc(); |
| ++tdt_count; |
| |
| if (table_idx < TABLE_SIZE && tdt_count > 1) |
| table[table_idx++] = now - exptime; |
| |
| if (breakmax && tdt_count > 1 && (now - exptime) > breakmax) { |
| hitmax = 1; |
| apic_write(APIC_EOI, 0); |
| return; |
| } |
| |
| exptime = now+delta; |
| wrmsr(MSR_IA32_TSCDEADLINE, now+delta); |
| apic_write(APIC_EOI, 0); |
| } |
| |
| static void start_tsc_deadline_timer(void) |
| { |
| handle_irq(TSC_DEADLINE_TIMER_VECTOR, tsc_deadline_timer_isr); |
| irq_enable(); |
| |
| wrmsr(MSR_IA32_TSCDEADLINE, rdmsr(MSR_IA32_TSC)+delta); |
| asm volatile ("nop"); |
| } |
| |
| static int enable_tsc_deadline_timer(void) |
| { |
| uint32_t lvtt; |
| |
| if (cpuid(1).c & (1 << 24)) { |
| lvtt = APIC_LVT_TIMER_TSCDEADLINE | TSC_DEADLINE_TIMER_VECTOR; |
| apic_write(APIC_LVTT, lvtt); |
| start_tsc_deadline_timer(); |
| return 1; |
| } else { |
| return 0; |
| } |
| } |
| |
| static void test_tsc_deadline_timer(void) |
| { |
| if(enable_tsc_deadline_timer()) { |
| printf("tsc deadline timer enabled\n"); |
| } else { |
| printf("tsc deadline timer not detected, aborting\n"); |
| abort(); |
| } |
| } |
| |
| int main(int argc, char **argv) |
| { |
| int i, size; |
| |
| setup_vm(); |
| smp_init(); |
| |
| test_lapic_existence(); |
| |
| mask_pic_interrupts(); |
| |
| delta = argc <= 1 ? 200000 : atol(argv[1]); |
| size = argc <= 2 ? TABLE_SIZE : atol(argv[2]); |
| breakmax = argc <= 3 ? 0 : atol(argv[3]); |
| printf("breakmax=%d\n", breakmax); |
| test_tsc_deadline_timer(); |
| irq_enable(); |
| |
| do { |
| asm volatile("hlt"); |
| } while (!hitmax && table_idx < size); |
| |
| for (i = 0; i < table_idx; i++) { |
| if (hitmax && i == table_idx-1) |
| printf("hit max: %d < ", breakmax); |
| printf("latency: %" PRId64 "\n", table[i]); |
| } |
| |
| return report_summary(); |
| } |