@@ -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) {
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- x86/chaos.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+)