<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged kernel</title>
<link>https://coderlegion.com/tag/kernel</link>
<description>Powered by Question2Answer</description>
<item>
<title>Patch These Four Linux Kernel Local Root Vulns Now: DirtyAH6, PPPoEject, TUNderflow, and DiagSpill</title>
<link>https://coderlegion.com/32434/patch-these-four-linux-kernel-local-root-vulns-dirtyah6-pppoeject-tunderflow-diagspill</link>
<description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a rel=&quot;nofollow&quot; href=&quot;https://codewithromi.com/patch-these-four-linux-kernel-local-root-vulns-now-dirtyah6-pppoeject-tunderflow-and-diagspill/&quot;&gt;codewithromi.com&lt;/a&gt;.&lt;/em&gt;&lt;br&gt;
Four Linux kernel vulnerabilities went public on 18 September 2026, and they are the kind you should not sit on. DirtyAH6 (CVE-2026-80844), PPPoEject (CVE-2026-68121), TUNderflow (CVE-2026-81000) and DiagSpill (CVE-2026-74469) are all local privilege escalation bugs, meaning an attacker or malicious process with a foothold on your machine can escalate to root. Working proof of concept exploits for all four are public. If you are running Proxmox, a Raspberry Pi, or any self-hosted Linux node, this is a patching weekend.&lt;/p&gt;
&lt;p&gt;Security researcher Asim Manizada reported the four bugs in mid July 2026 and published a technical write-up plus working exploits on 18 September 2026, after a coordinated hold so distributions could ship fixes first. The &lt;a rel=&quot;nofollow&quot; href=&quot;https://seclists.org/oss-sec/2026/q3/822&quot;&gt;oss-security post&lt;/a&gt; and the accompanying &lt;a rel=&quot;nofollow&quot; href=&quot;https://heyitsas.im/posts/lpe-quartet/&quot;&gt;write-up&lt;/a&gt; carry the per-CVE detail. Upstream, the earliest stable releases that contain all four fixes are 5.10.270, 5.15.221, 6.1.188, 6.6.157, 6.12.109, 6.18.50 and 7.2.4. Note that the 7.0 series reached end of life on 27 June 2026, so a 7.0 kernel only carries these fixes if your vendor backported them.&lt;/p&gt;
&lt;h3 id=&quot;-why-this-matters-for-homelabbers&quot;&gt;💡 Why This Matters for Homelabbers&lt;/h3&gt;
&lt;p&gt;Local root exploits are often dismissed as &quot;low risk&quot; because they require existing access. That framing misses the threat model for homelabs and self-hosted infrastructure. If any service you run gets compromised, a web app, an LXC container with a shared kernel, or even a rogue package, a local root bug turns that into full node ownership.&lt;/p&gt;
&lt;p&gt;Proxmox LXC containers share the host kernel. A container breakout combined with a local priv-esc is a realistic attack chain. Raspberry Pi nodes running exposed services are in the same boat. Patch the kernel, then verify.&lt;/p&gt;
&lt;h3 id=&quot;-prerequisites&quot;&gt;🧰 Prerequisites&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;SSH or console access to each node&lt;/li&gt;
&lt;li&gt;sudo or root privileges&lt;/li&gt;
&lt;li&gt;A maintenance window, since patching requires a reboot&lt;/li&gt;
&lt;li&gt;Proxmox nodes: access to the Proxmox web UI or CLI for VM/container shutdown sequencing&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-step-by-step-patching-your-nodes&quot;&gt;🔧 Step-by-Step: Patching Your Nodes&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Check your current kernel version on every node so you know what you are starting from.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;uname -r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note this output. You will compare it after the update to confirm the new kernel is running.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Update your package index and apply all available security updates. On Debian and Ubuntu based systems, including Proxmox and Raspberry Pi OS:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;sudo apt update &amp;amp;&amp;amp; sudo apt full-upgrade -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On Proxmox specifically, you also want to pull from the Proxmox repositories. If you are on the no-subscription repo, this is already covered by the command above. Run &lt;code&gt;pveversion -v&lt;/code&gt; afterward to confirm the Proxmox packages are current too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; If you are running Raspberry Pi OS, check the kernel package by its current name. On Bullseye and earlier the kernel shipped in the &lt;code&gt;raspberrypi-kernel&lt;/code&gt; package, but Raspberry Pi OS moved to Debian style &lt;code&gt;linux-image-rpi-*&lt;/code&gt; packages with Bookworm, so on a current install you want &lt;code&gt;linux-image-rpi-v8&lt;/code&gt; or &lt;code&gt;linux-image-rpi-2712&lt;/code&gt; depending on the board. The &lt;code&gt;apt full-upgrade&lt;/code&gt; command above will still catch it, but confirm with:&lt;/p&gt;
&lt;p&gt;On Bookworm and newer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;apt-cache policy linux-image-rpi-v8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On Bullseye and older:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;apt-cache policy raspberrypi-kernel
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &quot;Installed&quot; and &quot;Candidate&quot; versions should match after your upgrade. If they do not, run the upgrade again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Reboot to load the new kernel. This is not optional. The running kernel in memory is still the old one until you reboot.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;sudo reboot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Proxmox hosts, shut down or migrate your VMs and containers before rebooting the host. Use the Proxmox web UI, or list what is running:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;qm list
pct list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then shut down each VM or container by its ID:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;qm shutdown [vmid]
pct shutdown [ctid]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Step 5:&lt;/strong&gt; After the reboot, confirm the new kernel is loaded:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;uname -r
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The version string should be newer than what you recorded in Step 1. Cross-reference it against your distro's security advisory to confirm the patched version is in place. For Debian, check &lt;a rel=&quot;nofollow&quot; href=&quot;https://security-tracker.debian.org/tracker/&quot;&gt;security-tracker.debian.org&lt;/a&gt;. For Ubuntu, check &lt;a rel=&quot;nofollow&quot; href=&quot;https://ubuntu.com/security/notices&quot;&gt;ubuntu.com/security/notices&lt;/a&gt;. For Raspberry Pi OS, check the &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.raspberrypi.com/news/&quot;&gt;Raspberry Pi blog and release notes&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;-patch-status-checked-29-september-2026&quot;&gt;🛡️ Patch Status, Checked 29 September 2026&lt;/h3&gt;
&lt;p&gt;This is the part of the post that goes stale fastest, so here is a dated snapshot. Everything below was checked against the Debian security tracker, the Ubuntu CVE tracker and the Proxmox kernel changelog on 29 September 2026. Re-check before you decide you are covered.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Debian 13 trixie:&lt;/strong&gt; all four first fixed in linux 6.12.111-1, shipped as DSA-6528-1.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Debian 12 bookworm:&lt;/strong&gt; DirtyAH6, PPPoEject and DiagSpill first fixed in linux 6.1.187-1 from bookworm-security. TUNderflow is the exception. The Debian tracker still lists bookworm as vulnerable, 6.1.187-1 included, so there is no fix for CVE-2026-81000 on bookworm yet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ubuntu 22.04, 24.04 and 26.04:&lt;/strong&gt; at the time of this check the Ubuntu CVE tracker showed no released kernel for any of the four on the main &lt;code&gt;linux&lt;/code&gt; packages. They sit at needed or pending. Watch &lt;a rel=&quot;nofollow&quot; href=&quot;https://ubuntu.com/security/notices&quot;&gt;ubuntu.com/security/notices&lt;/a&gt; instead of assuming an upgrade fixed it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Proxmox VE:&lt;/strong&gt; the Proxmox kernel changelog names only TUNderflow, first fixed in proxmox-kernel-7.0 version 7.0.14-19, released 18 September 2026. The newest build at the time of checking was 7.0.14-20 from 24 September 2026. The other three CVEs are not named anywhere in that changelog, and the Proxmox 7.0 kernel follows the Ubuntu kernel, which has not released them, so do not assume DirtyAH6, PPPoEject and DiagSpill are closed on a Proxmox host. Run &lt;code&gt;pveversion -v&lt;/code&gt; and compare against the changelog.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Raspberry Pi OS:&lt;/strong&gt; no Raspberry Pi OS kernel build carrying all four fixes could be confirmed at the time of this check. On the 6.12 branch you need 6.12.109 or newer for the full set, so check the installed package version rather than assuming.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-what-each-vulnerability-affects&quot;&gt;🛡️ What Each Vulnerability Affects&lt;/h3&gt;
&lt;p&gt;Here is what each bug actually is, taken from the disclosure and the researcher's write-up. Three of the four need &lt;code&gt;CAP_NET_ADMIN&lt;/code&gt; in a network namespace the attacker controls, which an ordinary local user obtains by creating an unprivileged user namespace first. DiagSpill is the exception and needs no special privileges at all.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DirtyAH6 (CVE-2026-80844):&lt;/strong&gt; The IPv6 Authentication Header code in the IPsec and XFRM path does not check that a routing header's &lt;code&gt;segments_left&lt;/code&gt; field is no larger than the number of addresses actually present, so pointer arithmetic in &lt;code&gt;ipv6_rearrange_rthdr()&lt;/code&gt; walks out of bounds and an oversized &lt;code&gt;memmove()&lt;/code&gt; corrupts kernel memory. It needs AH6 and XFRM support plus &lt;code&gt;CAP_NET_ADMIN&lt;/code&gt; and &lt;code&gt;CAP_NET_RAW&lt;/code&gt; in a namespace the attacker controls. The researcher also reports a remote crash, and in his own lab remote root, against a host acting as an IPv6 router that applies AH in transport mode.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PPPoEject (CVE-2026-68121):&lt;/strong&gt; A use after free in &lt;code&gt;pppoe_sendmsg()&lt;/code&gt;. The function holds a pointer into the socket buffer head across a &lt;code&gt;dev_hard_header()&lt;/code&gt; callback that can reallocate that head, then writes six bytes through the stale pointer. It needs a lower device whose header callback expands the buffer head, not merely a loadable PPPoE module, which makes it the narrowest of the four in practice.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TUNderflow (CVE-2026-81000):&lt;/strong&gt; The TUN/TAP driver stores a receive headroom value without bounding it. A device path that propagates oversized headroom (the proof of concept chains a netkit device through Open vSwitch to a raw TUN port) underflows a size calculation and leaves packet data 64 bytes past the end of the allocation. Simply having a TUN or TAP interface is not enough, you need that headroom propagation path. Worth correcting a common assumption here: Docker's default bridge networking uses veth pairs rather than TUN/TAP, and the in kernel WireGuard module is its own device type, so the usual homelab exposure is QEMU tap interfaces and OpenVPN style TUN devices, not containers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DiagSpill (CVE-2026-74469):&lt;/strong&gt; An SCTP association can hold up to 65,536 peer transports but the counter is 16 bits wide, so it wraps to zero at the limit. &lt;code&gt;sctp_diag&lt;/code&gt; then reserves buffer space based on the wrapped count and copies the full transport list anyway, writing roughly 8 MiB past the end of the response buffer. This one needs no capabilities and no user namespace, only SCTP and &lt;code&gt;sctp_diag&lt;/code&gt; support, which is what makes it the priority of the four.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DiagSpill is the one to move fastest on if you are triaging. It is the only one of the four that an ordinary local user can reach without creating a user namespace, which also means the usual advice to restrict unprivileged user namespaces does nothing for it. If you do not need SCTP, keep the module out of the kernel.&lt;/p&gt;
&lt;h3 id=&quot;-hardening-beyond-the-patch&quot;&gt;🛡️ Hardening Beyond the Patch&lt;/h3&gt;
&lt;p&gt;Patching is the fix, but a few additional controls reduce your exposure, which matters more than usual here because some distributions have not shipped all four yet. Restricting unprivileged user namespaces closes the ordinary user path to three of the four. It does nothing for DiagSpill.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Restrict unprivileged user namespaces:&lt;/strong&gt; Three of these four bugs need a user namespace for an ordinary local user to reach them. Ubuntu introduced AppArmor based restrictions in 23.10 and enabled them by default from 24.04, controlled by &lt;code&gt;kernel.apparmor_restrict_unprivileged_userns&lt;/code&gt;, so 24.04 and 26.04 are covered out of the box. Debian is not. The Debian and Ubuntu specific &lt;code&gt;kernel.unprivileged_userns_clone&lt;/code&gt; knob has been discussed for deprecation, so confirm it exists on your kernel with &lt;code&gt;sysctl -a | grep userns&lt;/code&gt; before relying on it; the portable upstream equivalent is &lt;code&gt;sysctl -w user.max_user_namespaces=0&lt;/code&gt;. Either way you will break things that legitimately use user namespaces, including Flatpak, rootless containers and browser sandboxes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use AppArmor or SELinux profiles:&lt;/strong&gt; Proxmox ships with AppArmor. Make sure it is enforcing for your LXC containers. Unprivileged containers with AppArmor confinement are significantly harder to break out of.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Limit local access:&lt;/strong&gt; Do not run untrusted code or expose shell access to untrusted users on these nodes. Local privesc only matters if someone or something local can trigger it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unload unused kernel modules:&lt;/strong&gt; If you are not using PPPoE or SCTP, keep both out. Add &lt;code&gt;blacklist pppoe&lt;/code&gt; and &lt;code&gt;blacklist sctp&lt;/code&gt; to a file in &lt;code&gt;/etc/modprobe.d/&lt;/code&gt; and run &lt;code&gt;update-initramfs -u&lt;/code&gt;. Blacklisting stops the on demand autoload that a socket call would trigger, and for DiagSpill that is the only mitigation short of patching, since restricting user namespaces does not touch it. Verify with &lt;code&gt;lsmod | grep -E 'pppoe|sctp'&lt;/code&gt; afterwards.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;-troubleshooting&quot;&gt;🔧 Troubleshooting&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Kernel version did not change after reboot:&lt;/strong&gt; Your package manager may have the patched package available but it was not installed. Run &lt;code&gt;apt list --upgradable | grep linux-image&lt;/code&gt; to check, then install explicitly with &lt;code&gt;sudo apt install linux-image-[version]&lt;/code&gt; matching your architecture.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Proxmox node is stuck on an older kernel:&lt;/strong&gt; Proxmox sometimes pins the kernel for stability. Check &lt;code&gt;/etc/default/grub&lt;/code&gt; and the GRUB menu to ensure the new kernel is selected as default. Run &lt;code&gt;update-grub&lt;/code&gt; after confirming the package is installed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Raspberry Pi will not boot after upgrade:&lt;/strong&gt; This is rare but can happen if the bootloader and kernel fall out of sync. Boot from a backup SD card or use &lt;code&gt;rpi-update&lt;/code&gt; with caution, and check the Raspberry Pi OS forums for your specific board and OS version before using it.&lt;/p&gt;
&lt;h3 id=&quot;-wrap-up&quot;&gt;✅ Wrap Up&lt;/h3&gt;
&lt;p&gt;Four local root bugs in one disclosure batch is a bad day for Linux security. Updating and rebooting is the right move, but as of 29 September 2026 it is not the whole answer: Debian trixie has all four, Debian bookworm is still missing the TUNderflow fix, and Ubuntu has not released kernels for any of them. Prioritize DiagSpill, since it is the one an unprivileged local user can reach without a user namespace, and check the tracker for your distribution instead of assuming a fresh upgrade covered you.&lt;/p&gt;
&lt;p&gt;After you patch, take five minutes to check your other nodes. It is easy to forget a Raspberry Pi tucked behind the TV or a secondary Proxmox node you set up months ago. Cross-reference the patched kernel version against your distro's security tracker to make sure you are actually covered, not just up to date on packages.&lt;/p&gt;
&lt;p&gt;Stay subscribed to &lt;a rel=&quot;nofollow&quot; href=&quot;https://oss-security.openwall.org/wiki/mailing-lists/oss-security&quot;&gt;oss-sec&lt;/a&gt; if you want these disclosures in your inbox before they make the rounds on aggregators.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;related-from-the-lab&quot;&gt;Related from the lab&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://codewithromi.com/linux-kernel-security-gaps-why-your-homelab-isnt-safe-until-you-know-this/&quot;&gt;Linux Kernel Security Gaps: Why Your Homelab Isn't Safe Until You Know This&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://codewithromi.com/patching-dnsmasq-cves-securing-your-pi-hole-homelab-dns/&quot;&gt;Patching Dnsmasq CVEs: Securing Your Pi-hole &amp;amp; Homelab DNS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://codewithromi.com/crowdsec-community-powered-security/&quot;&gt;CrowdSec: Community-Powered Security&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32434/patch-these-four-linux-kernel-local-root-vulns-dirtyah6-pppoeject-tunderflow-diagspill</guid>
<pubDate>Sun, 25 Oct 2026 23:46:09 +0000</pubDate>
</item>
<item>
<title>KVM vs KVM - what is the question?</title>
<link>https://coderlegion.com/23702/kvm-vs-kvm-what-is-the-question</link>
<description>&lt;p&gt;Last weekend I was looking for a new project for my Raspberry Pi. I was looking for ways to set up a KVM on my Pi5 since it has 8GB of RAM, which is beefy enough for additional virtualisation tasks. Once I started Googling how I should start this task, I would look up “&lt;em&gt;KVM on Raspberry Pi&lt;/em&gt;”. Most of the search results were for another entirely different &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://pikvm.org&quot;&gt;KVM&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A Keyboard, Video, and Mouse (&lt;strong&gt;KVM&lt;/strong&gt;) switch is a hardware device that allows a user to control multiple computers or servers using a single set of keyboard, video display, and mouse devices. While the KVM switch is a great technology that allows you to control multiple computers using your existing setup, I currently use Tailscale for accessing my remote Pis via my home laptop. So that wasn’t what I was looking for. Instead, I was interested in seeing whether I could run a Kernel Virtual Machine (&lt;strong&gt;KVM&lt;/strong&gt;) on my Pi5.&lt;/p&gt;
&lt;p&gt;Despite sharing the same acronym, a KVM switch and the KVM software operate in entirely different realms of technology: one is a physical hardware device for managing physical computers, while the other is open-source software built into Linux to create and manage virtual machines. The KVM Switch routes peripheral and display signals between different systems with the press of a button or a keyboard shortcut.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=9303357946188213631&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-is-a-kvm-vm-&quot;&gt;What is a KVM VM?&lt;/h3&gt;
&lt;p&gt;Kernel-based VM is an open source virtualisation technology for Linux. With KVM, Linux can function as a hypervisor that runs multiple, isolated VMs. KVM was announced way back in 2006 and actually merged into the Linux kernel in 2007. &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Virtualization&quot;&gt;Virtualisation&lt;/a&gt;&lt;/strong&gt;, which lets you run an operating OS inside another OS, is possible thanks to hypervisors. &lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.edera.dev/technical-overview/concepts/hypervisor/&quot;&gt;hypervisor&lt;/a&gt;&lt;/strong&gt; pools computing resources (like processing, memory, and storage) and reallocates them among VMs. The hypervisor can run multiple VMs at once, manage them, and support the creation of new ones. The physical hardware, when used as a hypervisor, is called the host, while the many VMs that use its resources are guests. Hypervisors need OS-level components such as memory management, process scheduling, I/O, device drivers, security management, a network stack, and more to run VMs. &lt;/p&gt;
&lt;p&gt;KVM has all these components because it’s part of the Linux kernel. Every VM is implemented as a regular Linux process, scheduled by the standard Linux scheduler, with dedicated virtual hardware like a network card, graphics adapter, CPU, memory, and disks.&lt;/p&gt;
&lt;h3 id=&quot;similarities-between-kvm-and-kvm&quot;&gt;Similarities between KVM and KVM&lt;/h3&gt;
&lt;p&gt;Both technologies fundamentally exist to solve an efficiency problem: enabling a user to manage multiple OSs from a single workstation without buying duplicate input devices or displays. Whether toggling between two physical tower PCs or switching focus between multiple virtual instances, both implementations allow one operator to interact with distinct, isolated computing environments. Furthermore, both solutions preserve system isolation, ensuring that actions taken within one environment do not interfere with or crash the other operating systems running alongside it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=169879252453172863&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;differences&quot;&gt;Differences&lt;/h3&gt;
&lt;p&gt;The key distinction lies in hardware physical routing versus software virtualisation. A KVM switch is a tangible piece of hardware that physically redirects signal cables (HDMI/DisplayPort and USB) so that a single set of peripherals can control multiple physically distinct servers or PCs. It does not alter how the computers operate; it merely changes which machine receives your keypresses and sends video output to your monitor.&lt;/p&gt;
&lt;p&gt;In contrast, a Kernel-based VM is a software module integrated directly into the Linux kernel that turns the host OS into a &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://aws.amazon.com/compare/the-difference-between-type-1-and-type-2-hypervisors/&quot;&gt;Type-1 hypervisor.&lt;/a&gt;&lt;/strong&gt; Instead of connecting separate physical machines, the kernel-based KVM partitions a single computer's underlying processor, memory, and storage to simulate entirely separate virtual computers (&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.reddit.com/r/sysadmin/comments/a7mofe/who_came_up_with_guest_vm_and_why_do_we_call_it/&quot;&gt;Guest VMs&lt;/a&gt;&lt;/strong&gt;) on the same bare metal.&lt;/p&gt;
&lt;h3 id=&quot;the-question-should-ve-been-kvm-or-xen-&quot;&gt;The question should’ve been KVM or Xen?&lt;/h3&gt;
&lt;p&gt;And that brings us to a totally different question. KVM and Xen - what are the differences and which one can I run on my Pi5 for virtualisation? While both KVM and Xen are open-source Type-1 hypervisors that deliver high-performance virtualisation, their core designs and operational structures differ significantly.&lt;/p&gt;
&lt;p&gt;KVM is built directly into the Linux kernel as a standard kernel module (&lt;code&gt;kvm.ko&lt;/code&gt;). Installing KVM turns an existing Linux OS into a hypervisor, granting you immediate access to standard Linux drivers, process schedulers, and existing system management tools without having to modify the host kernel. Conversely, Xen sits below the OS as a &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Microkernel&quot;&gt;microkernel&lt;/a&gt;&lt;/strong&gt;-based layer that boots first, before any OS. It relies on a special, privileged Guest VM known as &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.xenproject.org/wiki/Dom0&quot;&gt;Dom0&lt;/a&gt;&lt;/strong&gt; (Domain 0) to handle hardware access, boot management, and admin control over other Guest VMs (&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.oracle.com/cd/E50245_01/E50249/html/vmcon-ovm-server-domains-domu.html&quot;&gt;DomU&lt;/a&gt;&lt;/strong&gt;).&lt;/p&gt;
&lt;p&gt;KVM relies purely on hardware-assisted virtualisation (using processor extensions like Intel VT-x or ARM &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://support.arm.com/documentation/ddi0438/d/Programmers-Model/Virtualization-Extensions-architecture&quot;&gt;Virtualisation Extensions&lt;/a&gt;&lt;/strong&gt;) to run unmodified Guest OSs. Xen historically pioneered &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.edera.dev/technical-overview/concepts/paravirtualization/&quot;&gt;paravirtualisation&lt;/a&gt;&lt;/strong&gt; (where guest kernels are modified to communicate directly with the hypervisor API for higher efficiency), though it now supports hardware-assisted virtualisation as well. Furthermore, Xen’s strict separation of the hypervisor microkernel from the control domain (Dom0) isolates hardware drivers away from guest VMs, which makes it a popular architecture for high-security environments like &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.qubes-os.org/&quot;&gt;Qubes OS&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For almost all Raspberry Pi 5 use cases, &lt;strong&gt;KVM is the far more practical and seamless option.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In the case of the Raspberry Pi, you’ll need to verify &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.archlinux.org/title/KVM#Hardware_support&quot;&gt;hardware virtualisation support&lt;/a&gt;&lt;/strong&gt; before doing anything. Check that ARM CPU virtualisation extensions are available. Check that the KVM hypervisor kernel module is present and accessible with the below command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ls -l /dev/kvm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You should see an output showing &lt;code&gt;crw-rw---- 1 root kvm ... /dev/kvm&lt;/code&gt;. &lt;br&gt;
If &lt;code&gt;/dev/kvm&lt;/code&gt; exists, hardware acceleration is supported and active.&lt;/p&gt;
&lt;p&gt;The Raspberry Pi’s boot process relies on a proprietary &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/EEPROM&quot;&gt;EEPROM&lt;/a&gt;&lt;/strong&gt;-based bootloader that loads GPU firmware and kernel binaries directly from an SD card or NVMe storage (often chainloaded through U-Boot for standard Linux environments). Because KVM is integrated into standard ARM64 Linux kernels (like Raspberry Pi OS 64-bit or Ubuntu), it requires no special bootloader tweaks or &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/UEFI&quot;&gt;UEFI&lt;/a&gt;&lt;/strong&gt; emulation layer. You simply install &lt;code&gt;`&lt;/code&gt;qemu-kvm&lt;code&gt;`&lt;/code&gt; (&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.qemu.org/&quot;&gt;QEMU&lt;/a&gt;&lt;/strong&gt;) and &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://libvirt.org/&quot;&gt;libvirt&lt;/a&gt;&lt;/strong&gt;, and the Pi instantly acts as a hypervisor using built-in ARM hardware extensions, allowing me to easily spin up VMs using standard Linux tools like &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://virt-manager.org/&quot;&gt;virt-manager&lt;/a&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://cockpit-project.org/&quot;&gt;cockpit&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You can run the following command to install the hypervisor, daemon services, and CLI management tools like QEMU core binaries. Since I’m running Raspberry Pi OS Desktop with the graphical interface, I also appended &lt;code&gt;virt-manager&lt;/code&gt; to the below command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install -y qemu-system-arm qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virtinst virt-manager
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the packages are installed, I just need to startup/enable the &lt;code&gt;libvirtd&lt;/code&gt; service to ensure the hypervisor daemon starts automatically on boot:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl enable --now libvirtd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Libvirt includes a default NAT network interface (&lt;code&gt;virbr0&lt;/code&gt;) for guest VMs. As soon as the default virtual network was configured, I can simply create my first Guest VM on the Pi5 and use an Ubuntu ARM64 ISO image, sourced from the Canonical website. The &lt;code&gt;virt-install&lt;/code&gt; looks a little like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;virt-install \
  --name debian-vm \
  --memory 2048 \
  --vcpus 2 \
  --disk size=10,bus=virtio \
  --os-variant debian12 \
  --cdrom /path/to/debian-arm64.iso \
  --network network=default \
  --graphics vnc,listen=0.0.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In contrast, setting up Xen on a Pi 5 requires a complex boot architecture. Because Xen acts as a microkernel that must load before any OS, booting it on an &lt;strong&gt;SBC&lt;/strong&gt; (Single Board Computer) generally requires using &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.u-boot.org/&quot;&gt;U-Boot&lt;/a&gt;&lt;/strong&gt; to load the Xen binary alongside device-tree overlays, a customised Dom0 control domain, and a secondary driver domain. While projects like &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/xen-troops&quot;&gt;Xen-Troops&lt;/a&gt;&lt;/strong&gt; exist specifically to compile custom &lt;strong&gt;Yocto-based&lt;/strong&gt; Xen images for embedded systems and automotive apps, setting this up for home or homelab experiments involves significant compilation overhead, non-standard driver configs, and manual partition setups. While maybe not suited to Pi5, it’s not impossible on previous versions like the Pi4, as stated on the &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linux.com/news/xen-on-raspberry-pi-4-adventures/&quot;&gt;Linux blog&lt;/a&gt;&lt;/strong&gt;. For edge use-cases you can certainly stick with KVM for a smooth setup unless you specifically need Xen's compartmentalised driver architecture for complex embedded development. If you’d like to read why Edera specifically opted for Xen when building a secure microservice foundation, check out their &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://edera.dev/stories/why-edera-built-on-xen-a-secure-container-foundation&quot;&gt;blog post&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23702/kvm-vs-kvm-what-is-the-question</guid>
<pubDate>Tue, 28 Jul 2026 22:04:05 +0000</pubDate>
</item>
<item>
<title>How to Detect and Monitor Multiple GPUs on Linux</title>
<link>https://coderlegion.com/12885/how-to-detect-and-monitor-multiple-gpus-on-linux</link>
<description>&lt;p&gt;If you run Linux on a system with both an &lt;strong&gt;integrated GPU&lt;/strong&gt; and a &lt;strong&gt;dedicated GPU&lt;/strong&gt;, you may have noticed something strange: some monitoring tools only show one GPU.&lt;/p&gt;
&lt;p&gt;This situation is common on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;laptops with hybrid graphics&lt;/li&gt;
&lt;li&gt;desktops with iGPU + dedicated GPU&lt;/li&gt;
&lt;li&gt;systems using different driver stacks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, a monitoring tool like &lt;strong&gt;btop&lt;/strong&gt; might show your AMD integrated GPU but completely ignore your NVIDIA card.&lt;/p&gt;
&lt;p&gt;In this article we will walk through how to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;detect all GPUs installed in your system&lt;/li&gt;
&lt;li&gt;verify that the correct drivers are loaded&lt;/li&gt;
&lt;li&gt;identify which GPU is rendering your desktop&lt;/li&gt;
&lt;li&gt;monitor usage for each GPU&lt;/li&gt;
&lt;li&gt;automate the entire diagnostic process with a small script&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-this-happens&quot;&gt;Why This Happens&lt;/h3&gt;
&lt;p&gt;Linux systems with multiple GPUs may behave differently depending on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;installed drivers (open source vs proprietary)&lt;/li&gt;
&lt;li&gt;kernel configuration&lt;/li&gt;
&lt;li&gt;which GPU is configured as the primary renderer&lt;/li&gt;
&lt;li&gt;monitoring tool capabilities&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example setup used in this article:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AMD Vega (integrated)&lt;/strong&gt; — Ryzen 5 5600G  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NVIDIA GTX 1050 Ti (dedicated)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this configuration, &lt;code&gt;btop&lt;/code&gt; showed only the AMD GPU while the NVIDIA card remained invisible in the default monitoring view.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-1-check-if-both-gpus-are-detected&quot;&gt;Step 1 — Check if Both GPUs Are Detected&lt;/h3&gt;
&lt;h4 id=&quot;1-1-list-gpus-via-pci&quot;&gt;1.1 List GPUs via PCI&lt;/h4&gt;
&lt;p&gt;Run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;lspci | grep -i vga
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;01:00.0 VGA compatible controller: NVIDIA Corporation GP107 [GeForce GTX 1050 Ti]
09:00.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] Cezanne [Radeon Vega Series]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If two entries appear, your &lt;strong&gt;hardware is being detected correctly&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;1-2-check-loaded-drivers&quot;&gt;1.2 Check Loaded Drivers&lt;/h4&gt;
&lt;p&gt;Run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;lsmod | grep -E 'nvidia|amdgpu|radeon'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Typical modules:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;GPU &lt;/th&gt;&lt;th&gt;Kernel Module&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;NVIDIA &lt;/td&gt;&lt;td&gt;&lt;code&gt;nvidia&lt;/code&gt;, &lt;code&gt;nvidia_drm&lt;/code&gt;, &lt;code&gt;nvidia_modeset&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;AMD (modern) &lt;/td&gt;&lt;td&gt;&lt;code&gt;amdgpu&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;AMD (legacy) &lt;/td&gt;&lt;td&gt;&lt;code&gt;radeon&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If the modules appear, the &lt;strong&gt;drivers are loaded by the kernel&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;1-3-check-which-gpu-is-rendering-your-desktop&quot;&gt;1.3 Check Which GPU Is Rendering Your Desktop&lt;/h4&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;glxinfo | grep &quot;OpenGL renderer&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;OpenGL renderer string: NVIDIA GeForce GTX 1050 Ti
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If &lt;code&gt;glxinfo&lt;/code&gt; is missing, install it.&lt;/p&gt;
&lt;p&gt;Ubuntu / Debian:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt install mesa-utils
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Arch Linux:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo pacman -S mesa-utils
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command reveals &lt;strong&gt;which GPU is rendering the graphical session&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In hybrid setups, the integrated GPU often renders the desktop while the dedicated GPU activates only for demanding workloads.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-2-monitoring-each-gpu&quot;&gt;Step 2 — Monitoring Each GPU&lt;/h3&gt;
&lt;h4 id=&quot;2-1-integrated-gpu-amd-intel-using-btop&quot;&gt;2.1 Integrated GPU (AMD / Intel) — Using btop&lt;/h4&gt;
&lt;p&gt;Launch:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;btop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside &lt;code&gt;btop&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;press &lt;strong&gt;g&lt;/strong&gt; to toggle GPU views&lt;/li&gt;
&lt;li&gt;press &lt;strong&gt;5 / 6 / 7&lt;/strong&gt; for specific GPU panels&lt;/li&gt;
&lt;li&gt;configure via &lt;strong&gt;F2 → Options → CPU box → Show GPU info&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-2-nvidia-dedicated-gpu-using-nvidia-smi&quot;&gt;2.2 NVIDIA Dedicated GPU — Using nvidia-smi&lt;/h3&gt;
&lt;p&gt;For NVIDIA GPUs the official tool is the most reliable:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;watch -n 1 nvidia-smi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This updates every second and displays:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GPU utilization&lt;/li&gt;
&lt;li&gt;memory usage&lt;/li&gt;
&lt;li&gt;running processes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Structured output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;nvidia-smi --query-gpu=name,memory.used,memory.total,utilization.gpu --format=csv -l 1
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-3-automating-gpu-diagnostics&quot;&gt;Step 3 — Automating GPU Diagnostics&lt;/h3&gt;
&lt;p&gt;Create a script called:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;check_gpus.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Script:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/bin/bash

echo &quot;=== GPUs Detected ===&quot;
lspci | grep -E &quot;VGA|3D&quot;

echo -e &quot;\n=== Loaded Drivers ===&quot;
lsmod | grep -E &quot;nvidia|amdgpu|radeon&quot;

echo -e &quot;\n=== NVIDIA GPU ===&quot;
nvidia-smi --query-gpu=name,memory.total --format=csv 2&amp;gt;/dev/null || echo &quot;NVIDIA not detected&quot;

echo -e &quot;\n=== Rendering GPU ===&quot;
glxinfo | grep &quot;OpenGL renderer&quot; 2&amp;gt;/dev/null || echo &quot;glxinfo not available&quot;

echo -e &quot;\n=== DRM Devices ===&quot;
ls /sys/class/drm | grep card
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make executable:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;chmod +x check_gpus.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;./check_gpus.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This quickly shows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;detected GPUs&lt;/li&gt;
&lt;li&gt;active drivers&lt;/li&gt;
&lt;li&gt;rendering GPU&lt;/li&gt;
&lt;li&gt;NVIDIA status&lt;/li&gt;
&lt;li&gt;DRM device nodes&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;recommended-tools-summary&quot;&gt;Recommended Tools Summary&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;GPU Type&lt;/th&gt;
      &lt;th&gt;Recommended Tool&lt;/th&gt;
      &lt;th&gt;Command&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;AMD / Intel (integrated)&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;btop&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;btop&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;NVIDIA (dedicated)&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;nvidia-smi&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;watch -n 1 nvidia-smi&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Hardware detection&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;lspci&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;lspci | grep -i vga&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Rendering check&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;glxinfo&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;glxinfo | grep &quot;OpenGL renderer&quot;&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Working with multiple GPUs on Linux doesn't have to be confusing.&lt;/p&gt;
&lt;p&gt;Using the commands shown above you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;verify hardware detection&lt;/li&gt;
&lt;li&gt;confirm driver loading&lt;/li&gt;
&lt;li&gt;identify which GPU renders your desktop&lt;/li&gt;
&lt;li&gt;monitor GPU usage in real time&lt;/li&gt;
&lt;li&gt;automate diagnostics with a small script&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Different GPU stacks require different tools, but combining a few commands provides &lt;strong&gt;full visibility into your graphics subsystem&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;additional-resources&quot;&gt;Additional Resources&lt;/h2&gt;
&lt;p&gt;btop documentation&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/aristocratos/btop&quot;&gt;https://github.com/aristocratos/btop&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;NVIDIA System Management Interface&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.nvidia.com/nvidia-system-management-interface&quot;&gt;https://developer.nvidia.com/nvidia-system-management-interface&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Arch Linux Wiki — NVIDIA&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.archlinux.org/title/NVIDIA&quot;&gt;https://wiki.archlinux.org/title/NVIDIA&lt;/a&gt;&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/12885/how-to-detect-and-monitor-multiple-gpus-on-linux</guid>
<pubDate>Sun, 15 Mar 2026 00:22:28 +0000</pubDate>
</item>
<item>
<title>Unlocking the Power of Linux Device Drivers</title>
<link>https://coderlegion.com/467/unlocking-the-power-of-linux-device-drivers</link>
<description>&lt;h3 id=&quot;introduction&quot;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;Linux is a versatile and widely-used operating system, from enterprise servers to embedded systems. At the heart of its functionality is the ability to interact with hardware, which is made possible by &lt;strong&gt;device drivers&lt;/strong&gt;. These drivers are specialized software that facilitate communication between the operating system and hardware, enabling the OS to manage peripherals efficiently.&lt;/p&gt;
&lt;p&gt;In this blog, we will delve into the world of Linux device drivers, discussing their architecture, types, security concerns, and how to develop simple drivers for character and block devices with practical examples.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-what-is-a-device-driver-&quot;&gt;1. What is a Device Driver?&lt;/h3&gt;
&lt;p&gt;A &lt;strong&gt;device driver&lt;/strong&gt; is software that enables communication between the operating system and hardware. It acts as an intermediary, allowing user applications to access hardware components like storage devices, network interfaces, and peripherals. Without device drivers, the OS would not be able to control or communicate with hardware efficiently.&lt;/p&gt;
&lt;h4 id=&quot;key-functions-of-a-device-driver-&quot;&gt;Key Functions of a Device Driver:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Configuration:&lt;/strong&gt; Initializes and configures hardware components.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Transfer:&lt;/strong&gt; Manages &lt;code&gt;read&lt;/code&gt; and &lt;code&gt;write&lt;/code&gt; operations between the OS and the hardware.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Request Handling:&lt;/strong&gt; Processes commands from the OS and hardware interrupts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Interface Provisioning:&lt;/strong&gt; Provides standardized interfaces to user-space applications for communication with hardware.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=9024566792902475150&quot; alt=&quot;Figure 01: Linux Kernel Architecture Diagram&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-types-of-device-drivers-in-linux&quot;&gt;2. Types of Device Drivers in Linux&lt;/h3&gt;
&lt;p&gt;Linux drivers are categorized based on the type of hardware they control:&lt;/p&gt;
&lt;h4 id=&quot;a-character-device-drivers&quot;&gt;a. &lt;strong&gt;Character Device Drivers&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Character devices, like serial ports, sensors, and keyboards, transfer data sequentially as a stream of bytes. The driver provides basic operations like &lt;code&gt;read()&lt;/code&gt;, &lt;code&gt;write()&lt;/code&gt;, and &lt;code&gt;ioctl()&lt;/code&gt; to interact with the device.&lt;/p&gt;
&lt;h4 id=&quot;b-block-device-drivers&quot;&gt;b. &lt;strong&gt;Block Device Drivers&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Block devices, such as hard drives and USB storage, operate by reading and writing data in blocks. Block drivers enable random access to data, and they interface with the Linux file system and I/O subsystems.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=307099118774726044&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;c-network-device-drivers&quot;&gt;c. &lt;strong&gt;Network Device Drivers&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Network devices, such as Ethernet and Wi-Fi adapters, transmit and receive packets. Network drivers must handle packet transmission and reception through functions like &lt;code&gt;ndo_start_xmit()&lt;/code&gt; for sending packets and interrupt handling for receiving packets.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-kernel-space-user-space-and-security&quot;&gt;3. Kernel Space, User Space, and Security&lt;/h3&gt;
&lt;h4 id=&quot;kernel-space&quot;&gt;&lt;strong&gt;Kernel Space&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Kernel space is where the Linux kernel operates, with full access to system resources and hardware. Drivers, which reside in kernel space, can directly manage hardware and system memory. Because kernel code runs with high privileges, a bug in the driver can crash the system or expose it to security vulnerabilities.&lt;/p&gt;
&lt;h4 id=&quot;user-space&quot;&gt;&lt;strong&gt;User Space&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;User space is where user applications run, with restricted access to system resources. To communicate with the kernel (and device drivers), user programs make system calls such as &lt;code&gt;open()&lt;/code&gt;, &lt;code&gt;read()&lt;/code&gt;, and &lt;code&gt;write()&lt;/code&gt;. These calls allow user-space applications to interact indirectly with hardware through the device drivers.&lt;/p&gt;
&lt;h3 id=&quot;section&quot;&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=5332079144598808016&quot; alt=&quot;&quot;&gt;&lt;/h3&gt;
&lt;h3 id=&quot;4-linux-device-driver-architecture&quot;&gt;4. Linux Device Driver Architecture&lt;/h3&gt;
&lt;p&gt;The architecture of Linux device drivers revolves around the following components:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Driver Initialization:&lt;/strong&gt; When a driver is loaded into the kernel, it registers itself with the kernel, associating its functions with specific devices.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Handling User Requests:&lt;/strong&gt; The driver exposes system calls to user-space applications, which can interact with hardware devices through the driver.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Interrupt Handling:&lt;/strong&gt; Drivers register interrupt handlers to respond to hardware interrupts, enabling efficient asynchronous communication.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Direct Memory Access (DMA):&lt;/strong&gt; Some drivers use DMA to facilitate data transfer between devices and memory without involving the CPU, optimizing performance.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-security-considerations-in-device-driver-development&quot;&gt;5. Security Considerations in Device Driver Development&lt;/h3&gt;
&lt;p&gt;Device drivers operate at a critical layer of the system and are often a target for attacks. This section explores some common security concerns when developing Linux device drivers and best practices to mitigate these risks.&lt;/p&gt;
&lt;h4 id=&quot;a-buffer-overflows&quot;&gt;a. &lt;strong&gt;Buffer Overflows&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Buffer overflows occur when a driver writes more data to a buffer than it can hold, causing data to overwrite adjacent memory. This can lead to unpredictable behavior, system crashes, or even code execution. For example, when handling user inputs in the &lt;code&gt;write()&lt;/code&gt; function, the driver must check the length of the data to ensure it doesn't exceed the allocated buffer size.&lt;/p&gt;
&lt;h5 id=&quot;solution-&quot;&gt;Solution:&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Always validate the size of input data.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use safe memory handling functions such as &lt;code&gt;copy_from_user()&lt;/code&gt; and &lt;code&gt;copy_to_user()&lt;/code&gt; to transfer data between kernel space and user space.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;b-race-conditions&quot;&gt;b. &lt;strong&gt;Race Conditions&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Race conditions happen when multiple processes try to access or modify shared resources concurrently without proper synchronization, leading to unpredictable system behavior. In drivers, this often involves interrupt handlers and user-space requests that share the same data structures.&lt;/p&gt;
&lt;h5 id=&quot;solution--1&quot;&gt;Solution:&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use kernel synchronization primitives like spinlocks, mutexes, and semaphores to ensure consistent access to shared resources.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Carefully design interrupt handlers to prevent race conditions.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;c-privilege-escalation&quot;&gt;c. &lt;strong&gt;Privilege Escalation&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Since device drivers run in kernel space, a bug or vulnerability in a driver can allow an attacker to execute arbitrary code with elevated privileges. For example, if a driver improperly validates user input, malicious code can trigger unintended behavior, leading to a system compromise.&lt;/p&gt;
&lt;h5 id=&quot;solution--2&quot;&gt;Solution:&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Always validate inputs from user space.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensure drivers operate with the least privileges necessary to perform their tasks.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement access control mechanisms to restrict who can interact with the driver.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;d-denial-of-service-dos-attacks&quot;&gt;d. &lt;strong&gt;Denial of Service (DoS) Attacks&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;A poorly written driver can be exploited to cause a denial of service by overwhelming system resources (e.g., flooding the driver with requests or triggering infinite loops). This can crash the system or degrade performance.&lt;/p&gt;
&lt;h5 id=&quot;solution--3&quot;&gt;Solution:&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Implement robust error handling and input validation to avoid scenarios where the driver can be overwhelmed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use timeouts and limits to prevent resource exhaustion.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;e-memory-leaks&quot;&gt;e. &lt;strong&gt;Memory Leaks&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Memory leaks occur when a driver allocates memory but fails to release it after use. Over time, this can exhaust system memory, causing the kernel to run out of resources.&lt;/p&gt;
&lt;h5 id=&quot;solution--4&quot;&gt;Solution:&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Ensure that all allocated memory is properly released when no longer needed, especially when the driver is unloaded.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Regularly test drivers using tools like &lt;code&gt;kmemleak&lt;/code&gt; to detect memory leaks.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;6-writing-a-simple-character-device-driver&quot;&gt;6. Writing a Simple Character Device Driver&lt;/h3&gt;
&lt;p&gt;A &lt;strong&gt;character device driver&lt;/strong&gt; handles devices like serial ports that send and receive data as a stream of bytes. Below is an example of a simple character device driver that can be loaded as a kernel module.&lt;/p&gt;
&lt;h4 id=&quot;example-simple-character-device-driver&quot;&gt;Example: Simple Character Device Driver&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;linux/module.h&amp;gt;
#include &amp;lt;linux/fs.h&amp;gt;
#include &amp;lt;linux/uaccess.h&amp;gt;

#define DEVICE_NAME &quot;simple_char_dev&quot;
#define BUF_LEN 80

static char msg[BUF_LEN]; // Buffer to hold the data

// Function prototypes for character driver
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);

// File operations structure
static struct file_operations fops = {
    .read = device_read,
    .write = device_write,
    .open = device_open,
    .release = device_release,
};

static int major_num;

// Driver initialization function
static int __init simple_char_init(void) {
    major_num = register_chrdev(0, DEVICE_NAME, &amp;amp;fops);
    if (major_num &amp;lt; 0) {
        printk(KERN_ALERT &quot;Failed to register character device\n&quot;);
        return major_num;
    }
    printk(KERN_INFO &quot;Simple char driver loaded with major number %d\n&quot;, major_num);
    return 0;
}

// Driver cleanup function
static void __exit simple_char_exit(void) {
    unregister_chrdev(major_num, DEVICE_NAME);
    printk(KERN_INFO &quot;Simple char driver unloaded\n&quot;);
}

static int device_open(struct inode *inode, struct file *file) {
    printk(KERN_INFO &quot;Device opened\n&quot;);
    return 0;
}

static ssize_t device_read(struct file *filp, char *buffer, size_t len, loff_t *offset) {
    int bytes_read = 0;
    if (*msg == 0)
        return 0;
    while (len &amp;amp;&amp;amp; *msg) {
        put_user(*(msg++), buffer++);
        len--;
        bytes_read++;
    }
    return bytes_read;
}

static ssize_t device_write(struct file *filp, const char *buffer, size_t len, loff_t *off) {
    int i;
    for (i = 0; i &amp;lt; len &amp;amp;&amp;amp; i &amp;lt; BUF_LEN; i++)
        get_user(msg[i], buffer + i);
    return i;
}

static int device_release(struct inode *inode, struct file *file) {
    printk(KERN_INFO &quot;Device closed\n&quot;);
    return 0;
}

module_init(simple_char_init);
module_exit(simple_char_exit);

MODULE_LICENSE(&quot;GPL&quot;);
MODULE_AUTHOR(&quot;Author&quot;);
MODULE_DESCRIPTION(&quot;Simple Character Device Driver&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example demonstrates how to implement basic operations (&lt;code&gt;open()&lt;/code&gt;, &lt;code&gt;read()&lt;/code&gt;, &lt;code&gt;write()&lt;/code&gt;, and &lt;code&gt;release()&lt;/code&gt;) for a character device. It can be compiled as a loadable kernel module and communicates with user-space applications via &lt;code&gt;/dev&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Security tip: &lt;strong&gt;Always validate the size of incoming data to prevent buffer overflow attacks&lt;/strong&gt;, as shown in the &lt;code&gt;device_write()&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=12080154466300240299&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;7-writing-a-simple-block-device-driver&quot;&gt;7. Writing a Simple Block Device Driver&lt;/h3&gt;
&lt;p&gt;A &lt;strong&gt;block device driver&lt;/strong&gt; manages devices like hard drives that perform I/O in blocks of data. Block drivers typically interface with the file system and handle requests from the kernel's block layer.&lt;/p&gt;
&lt;p&gt;To write a simple block device driver, you would:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Register the block device with the kernel.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set up request queues&lt;/strong&gt; for managing I/O requests.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Handle data read and write operations&lt;/strong&gt; in blocks, managing memory appropriately.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Block device drivers must also handle interrupts and use Direct Memory Access (DMA) for efficient data transfer. Proper error handling is critical to ensure data integrity.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;8-network-device-drivers&quot;&gt;8. Network Device Drivers&lt;/h3&gt;
&lt;p&gt;Network device drivers manage devices like Ethernet and Wi-Fi adapters. They are responsible for packet transmission and reception, interfacing with the Linux network stack.&lt;/p&gt;
&lt;h4 id=&quot;security-considerations-in-network-drivers-&quot;&gt;Security Considerations in Network Drivers:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Input Validation:&lt;/strong&gt; Ensure that packet sizes and formats are properly validated to prevent buffer overflow and packet injection attacks.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rate Limiting:&lt;/strong&gt; Implement mechanisms to prevent flooding attacks, which can exhaust system resources.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access Control:&lt;/strong&gt; Restrict which processes and users can send and receive network packets to prevent unauthorized network access.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;9-conclusion&quot;&gt;9. Conclusion&lt;/h3&gt;
&lt;p&gt;Linux device drivers are essential to the functionality of the OS, enabling it to interact with hardware efficiently. In this blog, we covered the different types of Linux device drivers, kernel space vs. user space, and the security challenges that arise during driver development. With the example of a simple character driver, you now have a basic understanding of how drivers operate.&lt;/p&gt;
&lt;p&gt;The security of device drivers is paramount, as they run in kernel space with high privileges. By adhering to best practices such as input validation, memory management, and proper error handling, developers can build secure and efficient drivers that protect the system from potential vulnerabilities.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/467/unlocking-the-power-of-linux-device-drivers</guid>
<pubDate>Sun, 03 Nov 2024 21:33:35 +0000</pubDate>
</item>
</channel>
</rss>