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);