-
Notifications
You must be signed in to change notification settings - Fork 9
Update docs for 0.11.0 and include new changes #119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9197ee5
d6ec006
cdb4b62
2569f40
2f03087
8df62a2
dc3913b
4c9e2e8
967deee
19911f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,8 @@ | ||
| [book] | ||
| src = "docs" | ||
|
|
||
| [preprocessor.mermaid] | ||
| command = "mdbook-mermaid" | ||
|
|
||
| [output.html] | ||
| additional-js = ["./docs/lib/mermaid/mermaid.min.js", "./docs/lib/mermaid/mermaid-init.js"] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,80 +2,42 @@ | |
|
|
||
| ## Introduction | ||
|
|
||
| Cardwire use the Kernel eBPF + LSM features to block syscall to the dGPU | ||
| Cardwire uses Linux eBPF along with Linux Security Modules (LSM) and Syscall tracepoints to intercept and block applications. By intercepting these operations directly in the kernel, Cardwire provides a fast and seamless blocking without needing to unload drivers or modify user applications/files. | ||
|
|
||
| ## List of used LSM | ||
| ## eBPF Hooks | ||
|
|
||
| - lsm/file_open | ||
| - lsm/inode_permission | ||
| - lsm/inode_getattr | ||
| Cardwire utilizes two main types of eBPF hooks: | ||
|
|
||
| ## List of used MAPS | ||
| ### 1. LSM Hooks | ||
|
|
||
| **BLOCKED_RENDERID** | ||
| LSM hooks are used to intercept and block permission checks or file openings on device files (like `/dev/dri/*`). This stops applications from accessing a GPU simply by checking file stats. | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| - Used for the renderD minor | ||
| - `lsm/file_open`: Intercepts the actual opening of blocked device files. | ||
| - `lsm/inode_permission`: Prevents permissions checks on blocked devices. | ||
| - `lsm/inode_getattr`: Prevents `stat()` calls on blocked devices. | ||
|
|
||
| **BLOCKED_CARDID** | ||
| ### 2. Syscall Tracepoints | ||
|
|
||
| - For the card minor | ||
| Tracepoints are used to monitor process lifecycle and manipulate the directory listings applications see. | ||
|
|
||
| **BLOCKED_PCI** | ||
| - `tracepoint/sched/sched_process_exec`: In Smart mode, this signals the Cardwire daemon that a new process is starting so it can be analyzed. | ||
| - `tracepoint/sched/sched_process_exit`: Signals when a process dies, cleaning up its entries in the allowed process maps. | ||
| - `tp/syscalls/sys_enter_getdents64` and `sys_exit_getdents64`: Intercepts directory listings. This is the core magic behind dynamically hiding device files from applications. | ||
|
|
||
| - For the PCI address | ||
| ## eBPF Maps | ||
|
|
||
| **BLOCKED_PCI_FILES** | ||
| The eBPF programs communicate with the Cardwire userspace daemon using several BPF maps: | ||
|
|
||
| - For the list of blocked PCI files | ||
| - **`cw_mode`**: Stores the current Cardwire mode (0=Integrated, 1=Hybrid, 2=Manual, 3=Smart). | ||
| - **`cw_blocked_ino`**: A hash map containing the inodes of blocked DRM devices (`/dev/dri/cardX`, `/dev/dri/renderDX`). The value indicates the GPU ID (0 for iGPU, 1 for dGPU). | ||
| - **`cw_exp_blk_ino`**: Contains inodes of blocked NVIDIA-specific files when `experimental_nvidia_block` is enabled. | ||
| - **`cw_allowed_pid`**: Used in Smart mode. Contains the PIDs of applications that have been analyzed and allowed to use the dGPU. The stored value (`__u8`) is used to identify if PID is meant for iGPU(0) or dGPU(1) | ||
| - **`cw_allowed_comm`**: A whitelist of process names (like `udev` or `pacman`) that bypass blocking entirely. | ||
| - **`cw_daemon_pid`**: Cardwire's own PID so it doesn't block itself. | ||
| - **`cw_exec_events`**, **`cw_close_events`**, **`cw_report_events`**: Ring buffers used to send process and block events back to userspace. | ||
|
Comment on lines
+31
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Unify the Smart-mode state machine across the development guides. The environment flags, allowed-PID values, and GPU visibility states currently describe incompatible mappings. Define one canonical table and apply it at every affected site.
📍 Affects 3 files
🤖 Prompt for AI Agents |
||
|
|
||
| **BLOCKED_NVIDIA_FILES** | ||
| ## Directory Hiding (`getdents64`) | ||
|
|
||
| - For the list of blocked NVIDIA files | ||
| Across all blocking modes (Integrated, Manual, Smart), Cardwire uses the `getdents64` syscall hooks to manipulate the contents of directories (like `/dev/dri/`) on the fly. | ||
|
|
||
| **SETTINGS** | ||
|
|
||
| - For experimental_nvidia_block | ||
|
|
||
| ## Block list | ||
|
|
||
| ### PCI files | ||
|
|
||
| Files that get blocked when a gpu's PCI address is blocked: | ||
|
|
||
| - config | ||
| - current_link_speed | ||
| - current_link_width | ||
| - max_link_speed | ||
| - max_link_width | ||
|
|
||
| ### NVIDIA files | ||
|
|
||
| These files are only blocked when the `experimental_nvidia_block` setting is enabled | ||
|
|
||
| - libGLX_nvidia.so.0 | ||
| - nvidia_icd.json | ||
| - nvidia_icd.x86_64.json | ||
| - nvidiactl | ||
|
|
||
| /dev/nvidia? using the minor | ||
|
|
||
| Example: | ||
|
|
||
| ```bash | ||
| /dev/nvidia0 | ||
| ``` | ||
|
|
||
| Will be blocked using the major `195` and the minor `0` | ||
|
|
||
| ### DRM | ||
|
|
||
| DRM node (card + renderD) are blocked using their major + minor ID | ||
|
|
||
| Example: | ||
|
|
||
| ```bash | ||
| /dev/dri/card1 | ||
| /dev/dri/renderD128 | ||
| ``` | ||
|
|
||
| Will be blocked using the major `226` and the minor `1` || `128` | ||
| When an application calls `getdents64` to list available GPUs, the eBPF program `patch_dirent_if_found` loops through the directory entries in memory. If it spots an inode belonging to a blocked GPU, it overwrites the previous entry's length field, effectively "jumping over" the blocked device. To the application, the blocked GPU simply does not exist and is omitted from directory listings rather than causing an error. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| # Smart | ||
|
|
||
| ## Introduction | ||
|
|
||
| Having an integrated and hybrid mode is good, but what if we could have the best of both worlds? | ||
|
|
||
| This is what cardwire's smart mode was made for. Cardwire uses a mix of kernel-space + userspace to directly allow processes on the fly | ||
|
|
||
| ### Kernel-Space | ||
|
|
||
| Using the eBPF program and the `tracepoint/sched/sched_process_exec` hooks, the kernel program notifies `cardwired` when a new process is executed, sending its pid using `cw_exec_events` RING_BUF, once the process is received by `cardwired`, it will be analyzed in real-time and if it's a process that should be allowed, its pid will be inserted into the `cw_allowed_pid` map | ||
|
|
||
| When a process exits, a notification is sent to `cardwired`, cardwired will remove the PID from its map to prevent the map from overflowing | ||
|
|
||
| If you want to dive deeper into the kernel code, take a look at [BPF](bpf.md) | ||
|
|
||
| ### Userspace | ||
|
|
||
| The userspace of Smart mode acts as the brain. It is responsible for making the actual decisions about whether a process is allowed to use a GPU. It is divided into three main components: | ||
|
|
||
| - **`CardwireAnalyzer`**: A dedicated background task that listens to the `cw_exec_events` and `cw_close_events` ring buffers. When it receives a new PID from the kernel, it invokes the analysis helpers. If the application passes, it populates the `cw_allowed_pid` map with a value of `1` (normal) or `0` (`iGPU`). | ||
| - **`dynamic_analysis.rs`**: A set of helper functions used to analyze a process in real-time. By reading `/proc/<pid>/environ` and `/proc/<pid>/cmdline`, it checks for explicitly requested GPUs (like `CARDWIRE_ALLOW=1`, `CARDWIRE_FORCE_DGPU=1`, `DRI_PRIME=1`) or implicit signs like Steam games (`SteamAppId`) and Flatpak wrappers. | ||
| - **`static_analysis.rs`**: A set of helper functions that analyze system data when the daemon starts. Specifically, it scans the XDG data directories for `.desktop` files containing `PrefersNonDefaultGPU=true` or `X-KDE-RunOnDiscreteGpu=true`, building a whitelist of application names that should automatically be granted dGPU access when they launch. | ||
|
|
||
| ## Complete Execution Flow | ||
|
|
||
| Here is a comprehensive breakdown of how the Kernel and Userspace interact in real-time when an application launches: | ||
|
|
||
| ```mermaid | ||
| sequenceDiagram | ||
| participant Proc as Process | ||
| participant Kernel as eBPF Kernel Hooks | ||
| participant Map as BPF Maps | ||
| participant Daemon as CardwireAnalyzer (Userspace) | ||
|
|
||
| Note over Proc,Daemon: 1. Process Launch | ||
| Proc->>Kernel: sched_process_exec | ||
| Kernel->>Map: Send PID via cw_exec_events (RingBuf) | ||
| Map->Daemon: Listen to cw_exec_events and wait for new events | ||
|
|
||
| Note over Daemon: 2. Real-time Analysis | ||
| Daemon->>Daemon: Read /proc/<pid>/environ & cmdline | ||
| Daemon->>Daemon: Check env vars, Steam, Flatpak, XDG lists | ||
|
|
||
| alt Is Allowed? | ||
| Daemon->>Map: Insert PID into cw_allowed_pid | ||
| else Not Allowed | ||
| Daemon->>Daemon: Do nothing | ||
| end | ||
|
|
||
| Note over Proc,Kernel: 3. GPU Access & Directory Listing | ||
| Proc->>Kernel: getdents64 / file_open (/dev/dri/) | ||
| Kernel->>Map: Check cw_allowed_pid | ||
|
|
||
| alt PID not in cw_allowed_pid | ||
| Kernel-->>Proc: hide GPU (Return -ENOENT) | ||
| Kernel->>Daemon: Send block event (cw_report_events) | ||
| else PID in cw_allowed_pid (Value 1 = Normal) | ||
| Kernel-->>Proc: Allow dGPU and iGPU | ||
| else PID in cw_allowed_pid (Value 0 = FORCE_DGPU) | ||
| Kernel-->>Proc: Allow dGPU, Hide iGPU (-ENOENT) | ||
| end | ||
|
|
||
| Note over Proc,Daemon: 4. Application Exit | ||
| Proc->>Kernel: sched_process_exit | ||
| Kernel->>Map: Send PID via cw_close_events (RingBuf) | ||
| Map->Daemon: Listen to cw_close_events and wait for new events | ||
| Daemon->>Map: Remove PID from cw_allowed_pid | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| # Switcheroo Shim | ||
|
|
||
| Cardwire implements a compatibility shim for the `net.hadess.SwitcherooControl` D-Bus interface. This allows desktop environments (like GNOME(gio-launch-desktop) and KDE) to natively offer "Launch using Discrete Graphics Card" options in their application menus without needing any Cardwire-specific plugins. | ||
|
|
||
| _(Having our own integration would've been better tbh)_ | ||
|
|
||
| ## Service | ||
|
|
||
| - **Interface:** `net.hadess.SwitcherooControl` | ||
|
|
||
| --- | ||
|
|
||
| ## Properties | ||
|
|
||
| ### `HasDualGpu` | ||
|
|
||
| Indicates whether the system has exactly two GPUs. | ||
|
Comment on lines
+15
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Complete the markdown formatting incantations. MD022 flags missing blank lines after the subsection headings, and MD047 reports that the file lacks its final newline. Add the required spacing and exactly one trailing newline. Also applies to: 20-21, 25-26, 41-42, 50-51, 53-53 🧰 Tools🪛 markdownlint-cli2 (0.23.1)[warning] 15-15: Headings should be surrounded by blank lines (MD022, blanks-around-headings) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
| - **Type:** `b` (boolean) | ||
| - **Access:** Read | ||
|
|
||
| ### `NumGPUs` | ||
|
|
||
| The number of GPUs detected on the system. | ||
| - **Type:** `u` (uint32) | ||
| - **Access:** Read | ||
|
|
||
| ### `GPUs` | ||
|
|
||
| A list of all available GPUs and their configurations. | ||
| - **Type:** `aa{sv}` (Array of dictionaries mapping strings to variants) | ||
| - **Access:** Read | ||
| - **Dictionary Keys:** | ||
| - `Name`: `s` - The name of the GPU. | ||
| - `Environment`: `as` - An array of environment variable key-value pairs to set when launching an application on this GPU (e.g., `["CARDWIRE_FORCE_DGPU", "1"]`). | ||
| - `Default`: `b` - Whether this is the default display GPU (usually the iGPU). | ||
| - `Discrete`: `b` - Whether this is a discrete GPU. | ||
|
|
||
| --- | ||
|
|
||
| ## Environment Variables Explained | ||
|
|
||
| The `Environment` property provides the exact environment variables the desktop environment should inject into the application when the user selects a specific GPU. | ||
|
|
||
| ### `CARDWIRE_FORCE_DGPU=1` | ||
|
|
||
| This is provided when the user selects the **Discrete GPU**. | ||
|
|
||
| When Cardwire detects this environment variable during the application's launch in Smart Mode, it does two things: | ||
| 1. **Unblocks the dGPU**: The eBPF hooks allow the application to access the discrete GPU's device files. | ||
| 2. **Hides the iGPU**: It actively intercepts and blocks the application from seeing the integrated GPU. | ||
|
|
||
| Hiding the iGPU ensures that the application is forced to use the discrete GPU, preventing issues where an application might get confused by seeing two GPUs and accidentally select the weaker one. | ||
|
|
||
| ### `CARDWIRE_ALLOW=0` | ||
|
|
||
| This is provided when the user selects the **Default/Integrated GPU**. | ||
|
|
||
| It explicitly tells Cardwire's Smart Mode to keep the dGPU blocked for this application, ensuring it runs solely on the integrated graphics to save power. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Pin
mdbook-mermaidfor reproducible builds.The workflow pins
mdbookbut installs an unconstrained Mermaid preprocessor, so future releases can change CI behavior without any repository change. Pin the version tested withmdbook 0.5.2and use--locked; the upstream project publishes versioned releases and supports Cargo installation. (github.com)🪄 Proposed fix
🤖 Prompt for AI Agents