Description: Modesetting: do not stop on EnterVT when connectors have disapeared Because of hotpluggeable GPUs and DP-MST, it is possible that CRTCs, connectors, and encoders may appear/disappear at run time. Normally, we would receive a uevent coming from Linux's DRM subsystem, which would trigger the check for disappearing/appearing resources. However, this event is not received when X would not be master (another VT is selected, or the machine is suspended), and so, when gaining back control, the modesetting driver would try to re-set the modes on all the connectors, fail to do so because the kernel would reject it on the disappeared connectors, and graceful stop the X-Server. To fix the issue, this series splits the kms-checking code from the uevent handling into its own (exported) function called drmmode_update_kms_state. This function is then called from both the uevent-handling function, and on EnterVT right before restoring the modes. The second patch then prevents setting the mode on disconnected/missing connectors, and thus restoring a working X-Server. Author: Martin Peres Origin: upstream, https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/443 --- This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ --- a/hw/xfree86/drivers/modesetting/driver.c +++ b/hw/xfree86/drivers/modesetting/driver.c @@ -1801,6 +1801,8 @@ SetMaster(pScrn); + drmmode_update_kms_state(&ms->drmmode); + if (!drmmode_set_desired_modes(pScrn, &ms->drmmode, TRUE)) return FALSE; --- a/hw/xfree86/drivers/modesetting/drmmode_display.c +++ b/hw/xfree86/drivers/modesetting/drmmode_display.c @@ -3438,6 +3438,7 @@ xf86CrtcPtr crtc = config->crtc[c]; drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; xf86OutputPtr output = NULL; + drmmode_output_private_ptr drmmode_output; int o; /* Skip disabled CRTCs */ @@ -3462,6 +3463,11 @@ if (!output) continue; + /* Make sure the connector is still backed by a KMS id */ + drmmode_output = output->driver_private; + if (!drmmode_output->mode_output || drmmode_output->mode_output->connection == DRM_MODE_DISCONNECTED) + continue; + /* Mark that we'll need to re-set the mode for sure */ memset(&crtc->mode, 0, sizeof(crtc->mode)); if (!crtc->desiredMode.CrtcHDisplay) { @@ -3579,30 +3585,20 @@ return TRUE; } -#ifdef CONFIG_UDEV_KMS #define DRM_MODE_LINK_STATUS_GOOD 0 #define DRM_MODE_LINK_STATUS_BAD 1 -static void -drmmode_handle_uevents(int fd, void *closure) +void +drmmode_update_kms_state(drmmode_ptr drmmode) { - drmmode_ptr drmmode = closure; ScrnInfoPtr scrn = drmmode->scrn; - struct udev_device *dev; drmModeResPtr mode_res; xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); int i, j; Bool found = FALSE; Bool changed = FALSE; - while ((dev = udev_monitor_receive_device(drmmode->uevent_monitor))) { - udev_device_unref(dev); - found = TRUE; - } - if (!found) - return; - /* Try to re-set the mode on all the connectors with a BAD link-state: * This may happen if a link degrades and a new modeset is necessary, using * different link-training parameters. If the kernel found that the current @@ -3716,6 +3712,25 @@ #undef DRM_MODE_LINK_STATUS_BAD #undef DRM_MODE_LINK_STATUS_GOOD +#ifdef CONFIG_UDEV_KMS + +static void +drmmode_handle_uevents(int fd, void *closure) +{ + drmmode_ptr drmmode = closure; + struct udev_device *dev; + Bool found = FALSE; + + while ((dev = udev_monitor_receive_device(drmmode->uevent_monitor))) { + udev_device_unref(dev); + found = TRUE; + } + if (!found) + return; + + drmmode_update_kms_state(drmmode); +} + #endif void --- a/hw/xfree86/drivers/modesetting/drmmode_display.h +++ b/hw/xfree86/drivers/modesetting/drmmode_display.h @@ -269,6 +269,7 @@ extern Bool drmmode_set_desired_modes(ScrnInfoPtr pScrn, drmmode_ptr drmmode, Bool set_hw); extern Bool drmmode_setup_colormap(ScreenPtr pScreen, ScrnInfoPtr pScrn); +extern void drmmode_update_kms_state(drmmode_ptr drmmode); extern void drmmode_uevent_init(ScrnInfoPtr scrn, drmmode_ptr drmmode); extern void drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode);