aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShaun Ren <shaun.ren@linux.com>2025-08-30 20:42:46 -0400
committerIvan Molodetskikh <yalterz@gmail.com>2025-10-02 10:02:16 +0300
commit5c91e3191dff08177740d642344d9eb67122139d (patch)
tree7722c928bbc9e855c28266a65939bb9d2026525e
parentb7f1e382a28961216afa6916c9c704e08a3c9617 (diff)
downloadniri-5c91e3191dff08177740d642344d9eb67122139d.tar.gz
niri-5c91e3191dff08177740d642344d9eb67122139d.tar.bz2
niri-5c91e3191dff08177740d642344d9eb67122139d.zip
tty: Add support for disabling DRM devices
-rw-r--r--docs/wiki/Configuration:-Debug-Options.md16
-rw-r--r--niri-config/src/debug.rs6
-rw-r--r--niri-config/src/lib.rs6
-rw-r--r--src/backend/tty.rs81
-rw-r--r--src/niri.rs4
5 files changed, 109 insertions, 4 deletions
diff --git a/docs/wiki/Configuration:-Debug-Options.md b/docs/wiki/Configuration:-Debug-Options.md
index 3bf2d70e..5dea65f2 100644
--- a/docs/wiki/Configuration:-Debug-Options.md
+++ b/docs/wiki/Configuration:-Debug-Options.md
@@ -18,6 +18,8 @@ debug {
disable-direct-scanout
restrict-primary-scanout-to-matching-format
render-drm-device "/dev/dri/renderD129"
+ ignore-drm-device "/dev/dri/renderD128"
+ ignore-drm-device "/dev/dri/renderD130"
force-pipewire-invalid-modifier
dbus-interfaces-in-non-session-instances
wait-for-frame-completion-before-queueing
@@ -115,6 +117,20 @@ debug {
}
```
+### `ignore-drm-device`
+
+<sup>Since: next release</sup>
+
+List DRM devices that niri will ignore.
+Useful for GPU passthrough when you don't want niri to open a certain device.
+
+```kdl
+debug {
+ ignore-drm-device "/dev/dri/renderD128"
+ ignore-drm-device "/dev/dri/renderD130"
+}
+```
+
### `force-pipewire-invalid-modifier`
<sup>Since: 25.01</sup>
diff --git a/niri-config/src/debug.rs b/niri-config/src/debug.rs
index 82bfe875..d69dd2bf 100644
--- a/niri-config/src/debug.rs
+++ b/niri-config/src/debug.rs
@@ -13,6 +13,7 @@ pub struct Debug {
pub keep_max_bpc_unchanged: bool,
pub restrict_primary_scanout_to_matching_format: bool,
pub render_drm_device: Option<PathBuf>,
+ pub ignored_drm_devices: Vec<PathBuf>,
pub force_pipewire_invalid_modifier: bool,
pub emulate_zero_presentation_time: bool,
pub disable_resize_throttling: bool,
@@ -45,6 +46,8 @@ pub struct DebugPart {
pub restrict_primary_scanout_to_matching_format: Option<Flag>,
#[knuffel(child, unwrap(argument))]
pub render_drm_device: Option<PathBuf>,
+ #[knuffel(children(name = "ignore-drm-device"), unwrap(argument))]
+ pub ignored_drm_devices: Vec<PathBuf>,
#[knuffel(child)]
pub force_pipewire_invalid_modifier: Option<Flag>,
#[knuffel(child)]
@@ -91,6 +94,9 @@ impl MergeWith<DebugPart> for Debug {
);
merge_clone_opt!((self, part), preview_render, render_drm_device);
+
+ self.ignored_drm_devices
+ .extend(part.ignored_drm_devices.iter().cloned());
}
}
diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs
index ac133c62..2ba2a121 100644
--- a/niri-config/src/lib.rs
+++ b/niri-config/src/lib.rs
@@ -831,6 +831,8 @@ mod tests {
debug {
render-drm-device "/dev/dri/renderD129"
+ ignore-drm-device "/dev/dri/renderD128"
+ ignore-drm-device "/dev/dri/renderD130"
}
workspace "workspace-1" {
@@ -2008,6 +2010,10 @@ mod tests {
render_drm_device: Some(
"/dev/dri/renderD129",
),
+ ignored_drm_devices: [
+ "/dev/dri/renderD128",
+ "/dev/dri/renderD130",
+ ],
force_pipewire_invalid_modifier: false,
emulate_zero_presentation_time: false,
disable_resize_throttling: false,
diff --git a/src/backend/tty.rs b/src/backend/tty.rs
index 6c55098a..08aef704 100644
--- a/src/backend/tty.rs
+++ b/src/backend/tty.rs
@@ -83,6 +83,8 @@ pub struct Tty {
primary_node: DrmNode,
// DRM render node corresponding to the primary GPU.
primary_render_node: DrmNode,
+ // Ignored DRM nodes.
+ ignored_nodes: HashSet<DrmNode>,
// Devices indexed by DRM node (not necessarily the render node).
devices: HashMap<DrmNode, OutputDevice>,
// The dma-buf global corresponds to the output device (the primary GPU). It is only `Some()`
@@ -328,6 +330,11 @@ impl Tty {
}
info!("using as the render node: {node_path}");
+ let mut ignored_nodes = ignored_nodes_from_config(&config.borrow());
+ if ignored_nodes.remove(&primary_node) || ignored_nodes.remove(&primary_render_node) {
+ warn!("ignoring the primary node or render node is not allowed");
+ }
+
Ok(Self {
config,
session,
@@ -336,6 +343,7 @@ impl Tty {
gpu_manager,
primary_node,
primary_render_node,
+ ignored_nodes,
devices: HashMap::new(),
dmabuf_global: None,
update_output_config_on_resume: false,
@@ -504,6 +512,11 @@ impl Tty {
let node = DrmNode::from_dev_id(device_id)?;
+ if self.ignored_nodes.contains(&node) {
+ debug!("node is ignored, skipping");
+ return Ok(());
+ }
+
let open_flags = OFlags::RDWR | OFlags::CLOEXEC | OFlags::NOCTTY | OFlags::NONBLOCK;
let fd = self.session.open(path, open_flags)?;
let device_fd = DrmDeviceFd::new(DeviceFd::from(fd));
@@ -1822,6 +1835,48 @@ impl Tty {
}
self.update_output_config_on_resume = false;
+ // Update ignored nodes.
+ let mut ignored_nodes = ignored_nodes_from_config(&self.config.borrow());
+ if ignored_nodes.remove(&self.primary_node)
+ || ignored_nodes.remove(&self.primary_render_node)
+ {
+ warn!("ignoring the primary node or render node is not allowed");
+ }
+ if ignored_nodes != self.ignored_nodes {
+ self.ignored_nodes = ignored_nodes;
+
+ let mut device_list = self
+ .udev_dispatcher
+ .as_source_ref()
+ .device_list()
+ .map(|(device_id, path)| (device_id, path.to_owned()))
+ .collect::<HashMap<_, _>>();
+
+ let removed_devices = self
+ .devices
+ .keys()
+ .filter(|node| {
+ self.ignored_nodes.contains(node) || !device_list.contains_key(&node.dev_id())
+ })
+ .copied()
+ .collect::<Vec<_>>();
+
+ for node in removed_devices {
+ device_list.remove(&node.dev_id());
+ self.device_removed(node.dev_id(), niri);
+ }
+
+ for node in self.devices.keys() {
+ device_list.remove(&node.dev_id());
+ }
+
+ for (device_id, path) in device_list {
+ if let Err(err) = self.device_added(device_id, &path, niri) {
+ warn!("error adding device {path:?}: {err:?}");
+ }
+ }
+ }
+
// Figure out if we should disable laptop panels.
let mut disable_laptop_panels = false;
if niri.is_lid_closed {
@@ -2181,10 +2236,7 @@ impl GammaProps {
}
}
-fn primary_node_from_config(config: &Config) -> Option<(DrmNode, DrmNode)> {
- let path = config.debug.render_drm_device.as_ref()?;
- debug!("attempting to use render node from config: {path:?}");
-
+fn primary_node_from_render_node(path: &Path) -> Option<(DrmNode, DrmNode)> {
match DrmNode::from_path(path) {
Ok(node) => {
if node.ty() == NodeType::Render {
@@ -2215,9 +2267,30 @@ fn primary_node_from_config(config: &Config) -> Option<(DrmNode, DrmNode)> {
warn!("error opening {path:?} as DRM node: {err:?}");
}
}
+
None
}
+fn primary_node_from_config(config: &Config) -> Option<(DrmNode, DrmNode)> {
+ let path = config.debug.render_drm_device.as_ref()?;
+ debug!("attempting to use render node from config: {path:?}");
+
+ primary_node_from_render_node(path)
+}
+
+fn ignored_nodes_from_config(config: &Config) -> HashSet<DrmNode> {
+ let mut disabled_nodes = HashSet::new();
+
+ for path in &config.debug.ignored_drm_devices {
+ if let Some((primary_node, render_node)) = primary_node_from_render_node(path) {
+ disabled_nodes.insert(primary_node);
+ disabled_nodes.insert(render_node);
+ }
+ }
+
+ disabled_nodes
+}
+
fn surface_dmabuf_feedback(
compositor: &GbmDrmCompositor,
primary_formats: FormatSet,
diff --git a/src/niri.rs b/src/niri.rs
index 3702c69d..b972f5c1 100644
--- a/src/niri.rs
+++ b/src/niri.rs
@@ -1504,6 +1504,10 @@ impl State {
output_config_changed = true;
}
+ if config.debug.ignored_drm_devices != old_config.debug.ignored_drm_devices {
+ output_config_changed = true;
+ }
+
// FIXME: move backdrop rendering into layout::Monitor, then this will become unnecessary.
if config.overview.backdrop_color != old_config.overview.backdrop_color {
output_config_changed = true;