Describe the bug
On a hybrid AMD + NVIDIA laptop where the HDMI port is wired to the NVIDIA dGPU, ExternalDisplayAutoSwitch does not provide reliable display hotplug behavior from Smart mode.
I observed three related failures:
- Plugging HDMI while in Smart mode often leaves Cardwire in Smart. The NVIDIA GPU remains blocked and the monitor is not exposed to GNOME.
- On another attempt Cardwire automatically switched to Hybrid, but the external display still did not appear until I logged out and back in. If the GNOME session starts in Smart, Mutter only enumerates the AMD GPU.
- Repeated unplug/replug attempts caused reproducible
gnome-shell SIGSEGV crashes in Mutter's KMS hotplug path, returning the session to GDM. This happened twice with coredumps present.
There is also a separate restore problem: after an automatic switch to Hybrid, unplugging HDMI did not restore the persisted Smart mode. cardwire get continued to report Hybrid even though the NVIDIA HDMI connector was disconnected and the dGPU had returned to D3Cold.
To Reproduce
-
Use a Wayland GNOME session on a hybrid laptop whose HDMI port is owned by the NVIDIA dGPU.
-
Enable external-display switching:
ExternalDisplayAutoSwitch: true
-
Select Smart mode and wait for the NVIDIA GPU to reach D3Cold.
-
Plug an HDMI monitor.
-
Observe one of the following:
- Cardwire remains in Smart and the display does not appear.
- Cardwire switches to Hybrid, but the display still does not appear in GNOME Settings.
-
Unplug and reconnect HDMI. In my case, repeating this caused gnome-shell to segfault and the graphical session to terminate.
-
If Cardwire did switch automatically to Hybrid, unplug HDMI and wait. It remains in Hybrid instead of restoring Smart.
The external monitor is detected reliably if I start/login to GNOME while already in Hybrid with HDMI connected, but requiring logout/login defeats hotplug.
Expected behavior
- HDMI connection should be detected reliably while Smart mode has blocked and runtime-suspended the dGPU.
- Cardwire should temporarily expose the dGPU early enough for the running Wayland compositor to use it.
- The external monitor should appear without logout/login.
- Disconnecting the last dGPU-owned external display should restore the persisted Smart mode.
- Repeated hotplug must not produce conflicting DRM/KMS state changes or crash Mutter.
Environment details
OS: Fedora Linux 44 (Workstation Edition) x86_64
Host: ROG Zephyrus G14 GA402XV_GA402XV (1.0)
Kernel: Linux 7.1.7-200.fc44.x86_64
DE: GNOME 50.4
WM: Mutter 50.4 (Wayland)
CPU: AMD Ryzen 9 7940HS
iGPU: AMD Radeon 780M Graphics (amdgpu), PCI 0000:65:00.0
dGPU: NVIDIA GeForce RTX 4060 Laptop GPU, PCI 0000:01:00.0
NVIDIA driver: 610.57.04 (open kernel module)
External monitor: Philips PHL32M1N5500V over HDMI, port owned by NVIDIA card0
Cardwire version (cardwire --version):
cardwire-cli 0.12.0
RPM: cardwire-0.12.0-1.fc44.x86_64
Relevant configuration:
ExternalDisplayAutoSwitch: true
ExperimentalNvidiaBlock: true
Available Mode: integrated, hybrid, smart
cardwire list output
Captured in Smart mode:
ID NAME PCI RENDER CARD DEFAULT DISCRETE BLOCKED
-- ---------------------------------- ------------ ---------- ----- ------- ------- -------
0 AMD Radeon 780M Graphics 0000:65:00.0 renderD128 card1 (*) ( ) false
1 NVIDIA GeForce RTX 4060 Laptop GPU 0000:01:00.0 renderD129 card0 ( ) (*) true
ls -la /dev/dri/by-path/
Run with CARDWIRE_ALLOW=1 because Smart mode hides the NVIDIA entries from an unclassified process:
total 0
drwxr-xr-x. 2 root root 120 Aug 10 13:03 .
drwxr-xr-x. 3 root root 140 Aug 10 13:03 ..
lrwxrwxrwx. 1 root root 8 Aug 10 13:03 pci-0000:01:00.0-card -> ../card0
lrwxrwxrwx. 1 root root 13 Aug 10 13:03 pci-0000:01:00.0-render -> ../renderD129
lrwxrwxrwx. 1 root root 8 Aug 10 13:03 pci-0000:65:00.0-card -> ../card1
lrwxrwxrwx. 1 root root 13 Aug 10 13:03 pci-0000:65:00.0-render -> ../renderD128
cardwired logs
First automatic transition and the first crash sequence:
Aug 10 13:03:53 cardwired[996]: gnome-shell[4561] tried to access GPU 1 (blocked by cardwire)
Aug 10 13:28:51 cardwired[996]: NVIDIA GeForce RTX 4060 Laptop GPU: Power state changed: D0
Aug 10 13:28:56 cardwired[996]: (ignoring) failed to get inode for hwmon 0000:01:00.0: No such file or directory
Aug 10 13:28:56 cardwired[996]: Switched to Hybrid
...
Aug 10 13:44:03 cardwired[996]: NVIDIA GeForce RTX 4060 Laptop GPU: Power state changed: D0
Aug 10 13:44:06 cardwired[996]: Switched to Hybrid
Aug 10 13:44:40 cardwired[996]: NVIDIA GeForce RTX 4060 Laptop GPU: Power state changed: D3Cold
Aug 10 13:44:41 cardwired[996]: NVIDIA GeForce RTX 4060 Laptop GPU: Power state changed: D0
Aug 10 13:44:43 kernel: gnome-shell[60435]: segfault ...
Aug 10 13:44:45 systemd-coredump: Process 60435 (gnome-shell) dumped core
Second reproduction:
Aug 10 13:47:54 cardwired[996]: Switched to Smart
Aug 10 13:47:56 cardwired[996]: gnome-shell[81907] tried to access GPU 1 (blocked by cardwire)
Aug 10 13:48:03 cardwired[996]: NVIDIA GeForce RTX 4060 Laptop GPU: Power state changed: D3Cold
Aug 10 13:49:58 cardwired[996]: NVIDIA GeForce RTX 4060 Laptop GPU: Power state changed: D0
Aug 10 13:50:01 cardwired[996]: Switched to Hybrid
Aug 10 13:50:20 cardwired[996]: NVIDIA GeForce RTX 4060 Laptop GPU: Power state changed: D3Cold
Aug 10 13:51:09 cardwired[996]: NVIDIA GeForce RTX 4060 Laptop GPU: Power state changed: D0
Aug 10 13:51:10 kernel: gnome-shell[81907]: segfault ... in libmutter-18.so.0.0.0
Aug 10 13:51:12 systemd-coredump: Process 81907 (gnome-shell) dumped core
Mutter coredumps
Two coredumps are present:
13:44:43 PID 60435 gnome-shell SIGSEGV
13:51:10 PID 81907 gnome-shell SIGSEGV
The first main-thread stack:
#0 __strlen_evex
#1 __printf_buffer
#2 __vasprintf_internal
#3 g_vasprintf
#4 g_strdup_vprintf
#5 g_error_new_valist
#6 g_set_error
#7 meta_crtc_kms_assign_extra
#8 assign_monitor_crtc
#9 meta_monitor_mode_foreach_crtc
#10 meta_monitor_config_manager_assign
#11 meta_monitor_manager_native_apply_monitors_config
#12 meta_monitor_manager_apply_monitors_config
#13 meta_monitor_manager_ensure_configured
#14 on_kms_resources_changed
#20 on_udev_hotplug
#25 on_uevent
The second reproduction crashed even more directly in the same path:
#0 meta_kms_device_get_planes
#1 find_unassigned_plane
#2 meta_crtc_kms_assign_extra
#3 assign_monitor_crtc
#4 meta_monitor_mode_foreach_crtc
#5 meta_monitor_config_manager_assign
#6 meta_monitor_manager_native_apply_monitors_config
#7 meta_monitor_manager_apply_monitors_config
#8 meta_monitor_manager_ensure_configured
#9 on_kms_resources_changed
#15 on_udev_hotplug
#20 on_uevent
No NVIDIA Xid, GPU reset, amdgpu reset, OOM event, or kernel panic was recorded around either crash.
Additional context / suspected implementation issues
-
In Smart mode, an unclassified process cannot see the NVIDIA connector paths at all. For example, /sys/class/drm/card0-HDMI-A-1/status returns No such file or directory, while the same connector exists in Hybrid. Because is_gpu_active() enumerates /sys/class/drm, it may be racing with or affected by Cardwire's own GPU hiding. This would explain why HDMI detection sometimes does nothing until another process wakes the dGPU.
-
The restore logic appears unable to run after the temporary override. internal_set_mode(Hybrid, false) updates the in-memory mode_state to Hybrid. On disconnect, reconcile_gpu() reads that applied mode and matches Modes::Hybrid, whose branch does nothing. Therefore it never reaches the Smart/Integrated branch that reloads the persisted mode from mode.json.
-
internal_set_mode() emits DRM change uevents for each GPU after applying a mode. Physical HDMI hotplug also produces uevents. The two coredumps show Mutter crashing while processing KMS resource changes shortly after the dGPU transitions from D3Cold to D0. This may indicate an unsafe or inconsistent topology sequence, though the immediate null/stale-object handling failure is inside Mutter.
Relevant source files:
crates/cardwire-daemon/src/tasks/monitor_display.rs
crates/cardwire-daemon/src/core/gpu/display.rs
crates/cardwire-daemon/src/interface/mode.rs
crates/cardwire-daemon/src/file/state.rs
I can provide the full Cardwire journal and both systemd coredumps if needed.
Describe the bug
On a hybrid AMD + NVIDIA laptop where the HDMI port is wired to the NVIDIA dGPU,
ExternalDisplayAutoSwitchdoes not provide reliable display hotplug behavior from Smart mode.I observed three related failures:
gnome-shellSIGSEGV crashes in Mutter's KMS hotplug path, returning the session to GDM. This happened twice with coredumps present.There is also a separate restore problem: after an automatic switch to Hybrid, unplugging HDMI did not restore the persisted Smart mode.
cardwire getcontinued to report Hybrid even though the NVIDIA HDMI connector was disconnected and the dGPU had returned to D3Cold.To Reproduce
Use a Wayland GNOME session on a hybrid laptop whose HDMI port is owned by the NVIDIA dGPU.
Enable external-display switching:
Select Smart mode and wait for the NVIDIA GPU to reach D3Cold.
Plug an HDMI monitor.
Observe one of the following:
Unplug and reconnect HDMI. In my case, repeating this caused
gnome-shellto segfault and the graphical session to terminate.If Cardwire did switch automatically to Hybrid, unplug HDMI and wait. It remains in Hybrid instead of restoring Smart.
The external monitor is detected reliably if I start/login to GNOME while already in Hybrid with HDMI connected, but requiring logout/login defeats hotplug.
Expected behavior
Environment details
Cardwire version (
cardwire --version):Relevant configuration:
cardwire listoutputCaptured in Smart mode:
ls -la /dev/dri/by-path/Run with
CARDWIRE_ALLOW=1because Smart mode hides the NVIDIA entries from an unclassified process:cardwired logs
First automatic transition and the first crash sequence:
Second reproduction:
Mutter coredumps
Two coredumps are present:
The first main-thread stack:
The second reproduction crashed even more directly in the same path:
No NVIDIA Xid, GPU reset, amdgpu reset, OOM event, or kernel panic was recorded around either crash.
Additional context / suspected implementation issues
In Smart mode, an unclassified process cannot see the NVIDIA connector paths at all. For example,
/sys/class/drm/card0-HDMI-A-1/statusreturnsNo such file or directory, while the same connector exists in Hybrid. Becauseis_gpu_active()enumerates/sys/class/drm, it may be racing with or affected by Cardwire's own GPU hiding. This would explain why HDMI detection sometimes does nothing until another process wakes the dGPU.The restore logic appears unable to run after the temporary override.
internal_set_mode(Hybrid, false)updates the in-memorymode_stateto Hybrid. On disconnect,reconcile_gpu()reads that applied mode and matchesModes::Hybrid, whose branch does nothing. Therefore it never reaches the Smart/Integrated branch that reloads the persisted mode frommode.json.internal_set_mode()emits DRMchangeuevents for each GPU after applying a mode. Physical HDMI hotplug also produces uevents. The two coredumps show Mutter crashing while processing KMS resource changes shortly after the dGPU transitions from D3Cold to D0. This may indicate an unsafe or inconsistent topology sequence, though the immediate null/stale-object handling failure is inside Mutter.Relevant source files:
crates/cardwire-daemon/src/tasks/monitor_display.rscrates/cardwire-daemon/src/core/gpu/display.rscrates/cardwire-daemon/src/interface/mode.rscrates/cardwire-daemon/src/file/state.rsI can provide the full Cardwire journal and both systemd coredumps if needed.