Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 8719c64e76bf ("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: 8719c64e76bf ("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

authored by

Niklas Cassel and committed by
Bjorn Helgaas
468711a4 56e0a838

+13 -9
+13 -9
drivers/pci/controller/dwc/pcie-designware-ep.c
··· 905 905 * supported, so we avoid reprogramming the region on every MSI, 906 906 * specifically unmapping immediately after writel(). 907 907 */ 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 + 908 921 if (!ep->msi_iatu_mapped) { 909 922 ret = dw_pcie_ep_map_addr(epc, func_no, 0, 910 923 ep->msi_mem_phys, msg_addr, ··· 928 915 ep->msi_iatu_mapped = true; 929 916 ep->msi_msg_addr = msg_addr; 930 917 ep->msi_map_size = map_size; 931 - } else if (WARN_ON_ONCE(ep->msi_msg_addr != msg_addr || 932 - ep->msi_map_size != map_size)) { 933 - /* 934 - * The host changed the MSI target address or the required 935 - * mapping size changed. Reprogramming the iATU at runtime is 936 - * unsafe on this controller, so bail out instead of trying to 937 - * update the existing region. 938 - */ 939 - return -EINVAL; 940 918 } 941 919 942 920 writel(msg_data | (interrupt_num - 1), ep->msi_mem + offset);