Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx> --- x86/chaos.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/x86/chaos.c b/x86/chaos.c index 0b1e29c..8d421b5 100644 --- a/x86/chaos.c +++ b/x86/chaos.c @@ -8,9 +8,11 @@ #include "alloc_page.h" #include "asm/page.h" #include "processor.h" +#include "pci-edu.h" #define MAX_NR_CPUS 256 +#define EDU_MSI 0xc4 #define TIMER_IRQ 0x44 struct chaos_args { @@ -20,21 +22,32 @@ struct chaos_args { int hz; bool hlt; + + bool edu; + int edu_hz; }; struct counters { int ticks_left; + int edu_fraction; + int edu_msi; }; int ncpus; struct chaos_args all_args[MAX_NR_CPUS]; struct counters cnt[MAX_NR_CPUS]; +bool have_edu; +struct pci_edu_dev edu_dev; + static void parse_arg(struct chaos_args *args, const char *arg) { char *s = strdup(arg); char *p = s; + /* By default generate 17 MSIs per second (if enabled). */ + args->edu_hz = 17; + while (*p) { char *word = p; char delim = strdelim(&p, ",="); @@ -84,10 +97,33 @@ static void parse_arg(struct chaos_args *args, const char *arg) } args->hlt = i; printf("CPU %d: hlt=%ld\n", smp_id(), i); + } else if (!strcmp(word, "edu")) { + if (!have_arg) + i = 1; + else if (i != 0 && i != 1) { + printf("edu argument must be 0 or 1\n"); + i = 1; + } + if (i != 0 && !have_edu) { + printf("edu device not found\n"); + i = 0; + } + args->edu = i; + printf("CPU %d: edu=%ld\n", smp_id(), i); + } else if (!strcmp(word, "edu_hz")) { + if (!have_arg || !i) + i = 100; + args->edu_hz = i; + printf("CPU %d: edu_hz=%ld\n", smp_id(), i); } else { printf("invalid argument %s\n", word); } } + if (args->edu && args->edu_hz > args->hz) { + printf("MSI rate limited to the CPU's hz value\n"); + args->edu_hz = args->hz; + } + free(s); } @@ -97,12 +133,27 @@ static void do_timer(void) struct counters *c = &cnt[cpu]; char out[4]; if (c->ticks_left > 0) { + /* + * Bresenham algorithm, generate edu_hz MSIs interrupts + * every hz timer ticks. See the other half in the + * stress function. + */ + if (all_args[cpu].edu) + c->edu_fraction += all_args[cpu].edu_hz; + c->ticks_left--; return; } c->ticks_left = all_args[cpu].hz; + if (all_args[cpu].edu) { + if (!c->edu_msi) { + puts("!!! no MSI received for edu device\n"); + } + c->edu_msi = 0; + } + /* Print current CPU number. */ out[2] = (cpu % 10) + '0'; cpu /= 10; out[1] = (cpu % 10) + '0'; cpu /= 10; @@ -116,14 +167,33 @@ static void timer(isr_regs_t *regs) eoi(); } +static void edu(isr_regs_t *regs) +{ + int cpu = smp_id(); + struct counters *c = &cnt[cpu]; + c->edu_msi++; + eoi(); +} + +static void x86_setup_msi(struct pci_dev *pci_dev, int dest) +{ + u64 address = 0xFEE00000 + (dest << 12); + u32 data = EDU_MSI; + pci_setup_msi(pci_dev, address, data); +} + static void __attribute__((noreturn)) stress(void *data) { const char *arg = data; struct chaos_args *args = &all_args[smp_id()]; + struct counters *c = &cnt[smp_id()]; printf("starting CPU %d workload: %s\n", smp_id(), arg); parse_arg(args, arg); + /* Do not print errors the first time through. */ + c->edu_msi = 1; + apic_write(APIC_TDCR, 0x0000000b); if (args->hz) { /* FIXME: assumes that the LAPIC timer counts in nanoseconds. */ @@ -132,6 +202,11 @@ static void __attribute__((noreturn)) stress(void *data) apic_write(APIC_LVTT, TIMER_IRQ | APIC_LVT_TIMER_PERIODIC); } + if (args->edu) { + printf("starting edu device\n"); + x86_setup_msi(&edu_dev.pci_dev, apic_id()); + } + irq_enable(); for (;;) { if (args->mem) { @@ -147,6 +222,13 @@ static void __attribute__((noreturn)) stress(void *data) } if (args->hlt) asm volatile("hlt"); + + if (c->edu_fraction > args->hz) { + c->edu_fraction -= args->hz; + edu_reg_writel(&edu_dev, EDU_REG_INTR_RAISE, 1); + while (!c->edu_msi) + cpu_relax(); + } } } @@ -159,12 +241,15 @@ int main(int argc, char *argv[]) return 1; } + have_edu = edu_init(&edu_dev); + argv++; argc--; ncpus = cpu_count(); if (ncpus > MAX_NR_CPUS) ncpus = MAX_NR_CPUS; + handle_irq(EDU_MSI, edu); handle_irq(TIMER_IRQ, timer); for (i = 1; i < ncpus; ++i) { -- 2.29.2