From 82bf391c933cd14b92f0ac96b7937497353c1b30 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Tue, 16 Aug 2022 13:39:59 +0200 Subject: [PATCH] xf86: allow DDX driver for GPU/PCI hot-plug The current X server infrastructure sets modesetting driver as default driver to handle PCI-hotplug of a GPU device. This prevents the respective DDX driver (like AMDGPU DDX driver) to take control of the card. This patch: - Adds a few functions and fine-tunes the GPU hotplug infrastructure to allow the DDX driver to be loaded, if it is configured in the X config file options as "hotplug-driver". - Scans and updates the PCI device list before adding the new GPU device in platform, so that the association of the platform device and PCI device is in place (dev->pdev). - Adds documentation of this new option An example usage in the config file would look like: Section "OutputClass" Identifier "AMDgpu" MatchDriver "amdgpu" Driver "amdgpu" HotplugDriver "amdgpu" EndSection V2: Fixed typo in commit message (Martin) Added R-B from Adam. Added ACK from Alex and Martin. V3: Added an output class based approach for finding the DDX driver (Aaron) Rebase V4: Addressed review comment from Aaron: GPU hot-plug handling driver's name to be read from the DDX config file options. In this way only the DDX drivers interested in handling GPU hot-plug will be picked and loaded, for others modesetting driver will be used as usual. V5: Addressed review comments from Aaron: - X config option to be listed in CamelCase. - Indentation fix at one place. - Code readability related optimization. V6: Addressed review comments from Aaron: - Squash the doc in the same patch - Doc formatting changes Cc: Alex Deucher Suggested-by: Aaron Plattner aplattner@nvidia.com (v3) Acked-by: Martin Roukala martin.roukala@mupuf.org(v1) Acked-by: Alex Deucher alexander.deucher@amd.com (v1) Reviewed-by: Adam Jackson ajax@redhat.com(v1) Signed-off-by: Shashank Sharma shashank.sharma@amd.com --- hw/xfree86/common/xf86platformBus.c | 55 +++++++++++++++++++--- hw/xfree86/common/xf86platformBus.h | 4 +- hw/xfree86/man/xorg.conf.man | 15 +++++- hw/xfree86/os-support/linux/lnx_platform.c | 10 +++- 4 files changed, 74 insertions(+), 10 deletions(-) diff --git a/hw/xfree86/common/xf86platformBus.c b/hw/xfree86/common/xf86platformBus.c index 071f44b2a..1d1f87c74 100644 --- a/hw/xfree86/common/xf86platformBus.c +++ b/hw/xfree86/common/xf86platformBus.c @@ -272,6 +272,22 @@ xf86PlatformMatchDriver(XF86MatchedDrivers *md) } } +void xf86PlatformScanPciDev(void) +{ + int i; + + if (!xf86scanpci()) + return; + + xf86Msg(X_CONFIG, "Scanning the platform PCI devices\n"); + for (i = 0; i < xf86_num_platform_devices; i++) { + char *busid = xf86_platform_odev_attributes(i)->busid; + + if (strncmp(busid, "pci:", 4) == 0) + platform_find_pci_info(&xf86_platform_devices[i], busid); + } +} + int xf86platformProbe(void) { @@ -613,31 +629,58 @@ xf86platformAddGPUDevices(DriverPtr drvp) return foundScreen; } +const char * +xf86PlatformFindHotplugDriver(int dev_index) +{ + XF86ConfOutputClassPtr cl; + const char *hp_driver = NULL; + struct xf86_platform_device *dev = &xf86_platform_devices[dev_index]; + + for (cl = xf86configptr->conf_outputclass_lst; cl; cl = cl->list.next) { + if (!OutputClassMatches(cl, dev) || !cl->option_lst) + continue; + + hp_driver = xf86FindOptionValue(cl->option_lst, "HotplugDriver"); + if (hp_driver) + xf86MarkOptionUsed(cl->option_lst); + } + + /* Return the first driver from the match list */ + xf86Msg(X_INFO, "matching hotplug-driver is %s\n", + hp_driver ? hp_driver : "none"); + return hp_driver; +} + int -xf86platformAddDevice(int index) +xf86platformAddDevice(const char *driver_name, int index) { int i, old_screens, scr_index, scrnum; DriverPtr drvp = NULL; screenLayoutPtr layout; - static const char *hotplug_driver_name = "modesetting"; if (!xf86Info.autoAddGPU) return -1; - /* force load the driver for now */ - xf86LoadOneModule(hotplug_driver_name, NULL); + /* Load modesetting driver if no driver given, or driver open failed */ + if (!driver_name || !xf86LoadOneModule(driver_name, NULL)) { + driver_name = "modesetting"; + xf86LoadOneModule(driver_name, NULL); + } for (i = 0; i < xf86NumDrivers; i++) { if (!xf86DriverList[i]) continue; - if (!strcmp(xf86DriverList[i]->driverName, hotplug_driver_name)) { + if (!strcmp(xf86DriverList[i]->driverName, driver_name)) { drvp = xf86DriverList[i]; break; } } - if (i == xf86NumDrivers) + + if (i == xf86NumDrivers) { + ErrorF("can't find driver %s for hotplugged device\n", driver_name); return -1; + } old_screens = xf86NumGPUScreens; doPlatformProbe(&xf86_platform_devices[index], drvp, NULL, diff --git a/hw/xfree86/common/xf86platformBus.h b/hw/xfree86/common/xf86platformBus.h index 1e75e6352..9979106a1 100644 --- a/hw/xfree86/common/xf86platformBus.h +++ b/hw/xfree86/common/xf86platformBus.h @@ -44,6 +44,8 @@ int xf86platformProbe(void); int xf86platformProbeDev(DriverPtr drvp); int xf86platformAddGPUDevices(DriverPtr drvp); void xf86MergeOutputClassOptions(int entityIndex, void **options); +void xf86PlatformScanPciDev(void); +const char *xf86PlatformFindHotplugDriver(int dev_index); extern int xf86_num_platform_devices; extern struct xf86_platform_device *xf86_platform_devices; @@ -56,7 +58,7 @@ extern Bool xf86_get_platform_device_unowned(int index); extern int -xf86platformAddDevice(int index); +xf86platformAddDevice(const char *driver_name, int index); extern void xf86platformRemoveDevice(int index); diff --git a/hw/xfree86/man/xorg.conf.man b/hw/xfree86/man/xorg.conf.man index ac88d7e7a..01b47247e 100644 --- a/hw/xfree86/man/xorg.conf.man +++ b/hw/xfree86/man/xorg.conf.man @@ -1296,7 +1296,20 @@ This option specifies that the matched device should be treated as the primary GPU, replacing the selection of the GPU used as output by the firmware. If multiple output devices match an OutputClass section with the PrimaryGPU option set, the first one enumerated becomes the primary GPU. -.PP +.TP 7 +.BI "Option \*qHotplugDriver\*q \*q" driver \*q +This option specifies that the matched driver should be used to handle a +hot-plugged GPU device. +The module specified by +.I driver +will be loaded during setup of the GPU device. +If loading of this module fails or there is no driver by that name, the +modesetting driver will be used, which is the default behavior. +If multiple output devices match an +.B OutputClass +section with the +.B HotplugDriver +option, the first one enumerated becomes the hotplug driver. A .B OutputClass Section may contain diff --git a/hw/xfree86/os-support/linux/lnx_platform.c b/hw/xfree86/os-support/linux/lnx_platform.c index 8a6be97aa..90013573c 100644 --- a/hw/xfree86/os-support/linux/lnx_platform.c +++ b/hw/xfree86/os-support/linux/lnx_platform.c @@ -125,7 +125,7 @@ xf86PlatformReprobeDevice(int index, struct OdevAttributes *attribs) xf86_remove_platform_device(index); return; } - ret = xf86platformAddDevice(index); + ret = xf86platformAddDevice(xf86PlatformFindHotplugDriver(index), index); if (ret == -1) xf86_remove_platform_device(index); } @@ -173,6 +173,8 @@ void NewGPUDeviceRequest(struct OdevAttributes *attribs) { int old_num = xf86_num_platform_devices; int ret; + const char *driver_name; + xf86PlatformDeviceProbe(attribs); if (old_num == xf86_num_platform_devices) @@ -181,7 +183,11 @@ void NewGPUDeviceRequest(struct OdevAttributes *attribs) if (xf86_get_platform_device_unowned(xf86_num_platform_devices - 1) == TRUE) return; - ret = xf86platformAddDevice(xf86_num_platform_devices-1); + /* Scan and update PCI devices before adding new platform device */ + xf86PlatformScanPciDev(); + driver_name = xf86PlatformFindHotplugDriver(xf86_num_platform_devices - 1); + + ret = xf86platformAddDevice(driver_name, xf86_num_platform_devices-1); if (ret == -1) xf86_remove_platform_device(xf86_num_platform_devices-1);