Skip to content

Commit 468711a

Browse files
floatiousbjorn-helgaas
authored andcommitted
PCI: dwc: ep: Refresh MSI Message Address cache on change
Endpoint drivers use dw_pcie_ep_raise_msi_irq() to raise MSI interrupts to the host. After 8719c64 ("PCI: dwc: ep: Cache MSI outbound iATU mapping"), dw_pcie_ep_raise_msi_irq() caches the Message Address from the MSI Capability in ep->msi_msg_addr. But that Message Address is controlled by the host, and it may change. For example, if: - firmware on the host configures the Message Address and triggers an MSI, - a driver on the Endpoint raises the MSI via dw_pcie_ep_raise_msi_irq(), which caches the Message Address, - a kernel on the host reconfigures the Message Address and the host kernel driver triggers another MSI, dw_pcie_ep_raise_msi_irq() notices that the Message Address no longer matches the cached ep->msi_msg_addr, warns about it, and returns error instead of raising the MSI. The host kernel may hang because it never receives the MSI. This was seen with the nvmet_pci_epf_driver: the host UEFI performs NVMe commands, e.g. Identify Controller to get the name of the controller, nvmet-pci-epf posts the completion queue entry and raises an IRQ using dw_pcie_ep_raise_msi_irq(). When the host boots Linux, we see a WARN_ON_ONCE() from dw_pcie_ep_raise_msi_irq(), and the host kernel hangs because the nvme driver never gets an IRQ. Remove the warning when dw_pcie_ep_raise_msi_irq() notices that Message Address has changed, remap using the new address, and update the ep->msi_msg_addr cache. Fixes: 8719c64 ("PCI: dwc: ep: Cache MSI outbound iATU mapping") Signed-off-by: Niklas Cassel <cassel@kernel.org> [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Tested-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com> Tested-by: Koichiro Den <den@valinux.co.jp> Acked-by: Manivannan Sadhasivam <mani@kernel.org> Link: https://patch.msgid.link/20260210181225.3926165-2-cassel@kernel.org
1 parent 56e0a83 commit 468711a

1 file changed

Lines changed: 13 additions & 9 deletions

File tree

drivers/pci/controller/dwc/pcie-designware-ep.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,19 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
905905
* supported, so we avoid reprogramming the region on every MSI,
906906
* specifically unmapping immediately after writel().
907907
*/
908+
if (ep->msi_iatu_mapped && (ep->msi_msg_addr != msg_addr ||
909+
ep->msi_map_size != map_size)) {
910+
/*
911+
* The host changed the MSI target address or the required
912+
* mapping size changed. Reprogramming the iATU when there are
913+
* operations in flight is unsafe on this controller. However,
914+
* there is no unified way to check if we have operations in
915+
* flight, thus we don't know if we should WARN() or not.
916+
*/
917+
dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys);
918+
ep->msi_iatu_mapped = false;
919+
}
920+
908921
if (!ep->msi_iatu_mapped) {
909922
ret = dw_pcie_ep_map_addr(epc, func_no, 0,
910923
ep->msi_mem_phys, msg_addr,
@@ -915,15 +928,6 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
915928
ep->msi_iatu_mapped = true;
916929
ep->msi_msg_addr = msg_addr;
917930
ep->msi_map_size = map_size;
918-
} else if (WARN_ON_ONCE(ep->msi_msg_addr != msg_addr ||
919-
ep->msi_map_size != map_size)) {
920-
/*
921-
* The host changed the MSI target address or the required
922-
* mapping size changed. Reprogramming the iATU at runtime is
923-
* unsafe on this controller, so bail out instead of trying to
924-
* update the existing region.
925-
*/
926-
return -EINVAL;
927931
}
928932

929933
writel(msg_data | (interrupt_num - 1), ep->msi_mem + offset);

0 commit comments

Comments
 (0)