git: 21b556557e46 - main - linuxkpi: Fix `pci_upstream_bridge()` with DRM devices

From: Jean-Sébastien Pédron <dumbbell_at_FreeBSD.org>
Date: Sun, 13 Apr 2025 10:00:57 UTC
The branch main has been updated by dumbbell:

URL: https://cgit.FreeBSD.org/src/commit/?id=21b556557e464701960c4a8b4dcc062cb3954ddc

commit 21b556557e464701960c4a8b4dcc062cb3954ddc
Author:     Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
AuthorDate: 2025-03-03 23:48:20 +0000
Commit:     Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
CommitDate: 2025-04-13 09:51:13 +0000

    linuxkpi: Fix `pci_upstream_bridge()` with DRM devices
    
    In the case of DRM drivers, the passed device is a child of `vgapci`. We
    want to start the lookup from `vgapci`, so the parent of the passed
    `drmn`. We use the `isdrm` flag to determine if we are in this
    situation.
    
    This fixes an infinite loop with the amdgpu DRM driver that started to
    use this function in Linux 6.8: `pci_upstream_bridge()` was returning
    itself and the code in amdgpu was calling it again, hoping to get a
    device with a vendor that is not "ATI".
    
    Reviewed by:    bz
    Sponsored by:   The FreeBSD Foundation
    Differential Revision: https://reviews.freebsd.org/D49388
---
 sys/compat/linuxkpi/common/include/linux/pci.h | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/sys/compat/linuxkpi/common/include/linux/pci.h b/sys/compat/linuxkpi/common/include/linux/pci.h
index afd6c827b3b4..e4e8e5c0abd1 100644
--- a/sys/compat/linuxkpi/common/include/linux/pci.h
+++ b/sys/compat/linuxkpi/common/include/linux/pci.h
@@ -523,7 +523,20 @@ pci_upstream_bridge(struct pci_dev *pdev)
 	if (pdev == pdev->bus->self) {
 		device_t bridge;
 
-		bridge = device_get_parent(pdev->dev.bsddev);
+		/*
+		 * In the case of DRM drivers, the passed device is a child of
+		 * `vgapci`. We want to start the lookup from `vgapci`, so the
+		 * parent of the passed `drmn`.
+		 *
+		 * We can use the `isdrm` flag to determine this.
+		 */
+		bridge = pdev->dev.bsddev;
+		if (pdev->pdrv != NULL && pdev->pdrv->isdrm)
+			bridge = device_get_parent(bridge);
+		if (bridge == NULL)
+			goto done;
+
+		bridge = device_get_parent(bridge);
 		if (bridge == NULL)
 			goto done;
 		bridge = device_get_parent(bridge);