Without MSI-X, vi_interrupt() takes the device's vs_mtx to set the ISR
bit and assert INTx. Most callers already hold that mutex: vi_pci_write()
calls the queue notification handler with it held, and backends such as
pci_vtblk_done() take it before vq_endchains().
With FreeBSD's default (error-checking) mutexes the second lock fails
with EDEADLK, which is ignored, and the matching unlock then releases
the caller's lock early. With a normal mutex the thread deadlocks: a
Linux guest that has no MSI domain hangs at its first block read.
The lock dates from 3cbf3585cbab (January 2014), which added INTx
support to the virtio backends and took VS_LOCK() around the ISR update
and pci_lintr_assert(); c06676bee3d2 (June 2021) moved that code into
vi_interrupt() unchanged. Because the relock only returns EDEADLK on
FreeBSD, it went unnoticed until bhyve was built against a pthread
implementation with non-error-checking mutexes.
Give the ISR state and the INTx level a small mutex of their own, taken
last, and use it in the three places that touch them.
Fixes: 3cbf3585cbab ("Enhance the support for PCI legacy INTx interrupts and enable them in the virtio backends.")
Signed-off-by: Wanpeng Qian <wanpengqian@gmail.com>
Sponsored by: keelos.dev