On Mon, Jan 30, 2023 at 12:32:22PM -0500, Dave Voutila wrote:
> 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?
> 

This needs to be widely tested. IIRC there were old linux kernels that
behaved differently and were sensitive to where/when irqs were injected
for the serial ports, leading to various issues like needing to press a
key for the console to advance, and/or seeing "too much work" messages
from the kernel.

I'd recommend testing on a bunch of platforms.

-ml

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