A GPU manager for Linux using eBPF LSM hooks to block GPUs
- This project is in early development. Expect bugs and incomplete functionality
- The Makefile was AI-generated, i only use the flake.nix, be careful
- Linux Kernel: version 5.7 or later
- eBPF LSM: Your kernel must have
CONFIG_BPF_LSM=y, and you must enable LSM on your system
Arch:
yay -S cardwireNix with flakes:
flake.nix:
cardwire = {
url = "github:luytan/cardwire";
inputs.nixpkgs.follows = "nixpkgs";
};then import:
imports = [
inputs.cardwire.nixosModules.default
];
services.cardwire.enable = true;The cardwire CLI lets you manage GPU states and system modes
- Integrated: Blocks the discrete GPU
- Hybrid: Unblocks the discrete GPU
- Manual: Default mode for safety, allows individual GPU blocking/unblocking
Note: Integrated/Hybrid modes only work on host with two GPUs
Note 2: Manual mode is not implemented
# Set system mode
cardwire set integrated / hybrid / manual
# Get current mode status
cardwire get
# List all detected GPUs and their status
cardwire list
# Manually block/unblock a specific GPU by ID
cardwire gpu 1 --block
cardwire gpu 1 --unblockThe daemon reads its configuration from /var/lib/cardwire/cardwire.toml. If the file is missing, it defaults to Manual mode.
# /var/lib/cardwire/cardwire.toml
mode = "Manual"
block_vulkan = falseblock_vulkan is an experimental feature that blocks the nvidia's vulkan icd, must be used with caution
# Enter development shell
nix develop
# Build the project
nix build
# Run formatting checks
nix build .#checks.x86_64-linux.pre-commit-check
# Run integration tests in VM
nix build .#checks.x86_64-linux.vm-test
# Build the vm and enter
nix run .#nixosConfigurations.x86_64-linux.config.system.build.vmIf you don't use Nix, ensure you have clang, libbpf and cargo installed (needed for eBPF compilation during the Rust build)
# Build the project
make
# Install binaries, systemd service, and D-Bus config (requires sudo)
sudo make installCardwire uses eBPF with LSM hooks to intercept file operations on GPU device nodes, such as /dev/dri/renderDX, /dev/dri/cardX, sysfs config and nvidiaX
When a GPU is "blocked," the eBPF program returns -ENOENT for any syscall targeting that device. This provides several key benefits:
- Instant App Startup: Prevents applications (like Electron apps or GTK apps) from attempting to initialize the GPU, this eliminates the 3–4 second "hang" typically caused by waiting for a sleeping GPU to power up
- Power Efficiency: By blocking access at the syscall level, the GPU is never woken from its lowest power state (D3cold), extending battery life on laptops
- Non-Invasive: Unlike traditional methods that might require driver unloading, risky unbind or complex X11/Wayland setups, this approach is transparent to the rest of the system and easy to toggle
- Also works with games
crates/cardwire-cli: User CLI to interact with the daemoncrates/cardwire-core: Low-level GPU manager and IOMMU discoverycrates/cardwire-daemon: System daemon managing state and D-Bus communicationcrates/cardwire-ebpf: BPF program and LSM hooks
- I'm still learning Rust, if some parts of the code are bad or unoptimized, feel free to open a PR
- Asus-linux Discord for helping me find the ebpf method
- Caelestia shell for the flake.nix, i used it as a reference
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.