vmd's serial console has a race condition and can generate interrupt
storms as the ns8250 device constantly asserts and deasserts its irq.

Easiest way to see this is on older, slower hardware OR running nested
such as OpenBSD guest inside OpenBSD vmm atop Linux KVM. I don't know
enough about how consoles work to understand why, but if you use a shell
with history that lets you hit the up-arrow it can trigger this fairly
consistently.

When triggered, you will experience the serial console "lock up" and the
vmd process for the vm spike > 100% (the cpu thread is constantly
exiting due to interrupt and the event thread is constantly firing
ioctls). Anywhere from 10s to a few minutes later, it may stop and
control in the console resumes.

btrace shows the vm exit rate spike with the exit reason being external
interrupt.

AFAICT what happens is the kevent for data available on the com1 fd
fires and, if it was !EV_WRITE, we assert/deassert the irq even if we
weren't ready to actually receive data off the fd. This keeps kicking
the poor vcpu for no reason making it slower to get the ns8250 into a
state where it is slow to get back into a LSR_RXRDY state.

OK?


diff refs/heads/master refs/heads/vmd-ns8250
commit - ef14a9e8cae106563ff9ce15d913365f9ad3fa0e
commit + 5c76914a8c33243ec5ccc82689d0dadaa7cae666
blob - dbb6568714c192447a99017fcf92bcc4bcc90ba6
blob + 96e1f2533691205bdeb8e8b6dafee92603494c44
--- usr.sbin/vmd/ns8250.c
+++ usr.sbin/vmd/ns8250.c
@@ -153,14 +153,15 @@ com_rcv_event(int fd, short kind, void *arg)
                return;
        }

-       if ((com1_dev.regs.lsr & LSR_RXRDY) == 0)
+       if ((com1_dev.regs.lsr & LSR_RXRDY) == 0) {
                com_rcv(&com1_dev, (uintptr_t)arg, 0);

-       /* If pending interrupt, inject */
-       if ((com1_dev.regs.iir & IIR_NOPEND) == 0) {
-               /* XXX: vcpu_id */
-               vcpu_assert_pic_irq((uintptr_t)arg, 0, com1_dev.irq);
-               vcpu_deassert_pic_irq((uintptr_t)arg, 0, com1_dev.irq);
+               /* If pending interrupt, inject */
+               if ((com1_dev.regs.iir & IIR_NOPEND) == 0) {
+                       /* XXX: vcpu_id */
+                       vcpu_assert_pic_irq((uintptr_t)arg, 0, com1_dev.irq);
+                       vcpu_deassert_pic_irq((uintptr_t)arg, 0, com1_dev.irq);
+               }
        }

        mutex_unlock(&com1_dev.mutex);

Reply via email to