From 782f5a21b85ad08f15117fc048076a67a7bc6d1b Mon Sep 17 00:00:00 2001 From: Nick Pegg Date: Fri, 8 Jan 2021 21:42:13 -0800 Subject: [PATCH] [post] KVM USB Auto-Passthrough --- .../2021-01-08_kvm-usb-auto-passthrough.yaml | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 posts/2021-01-08_kvm-usb-auto-passthrough.yaml diff --git a/posts/2021-01-08_kvm-usb-auto-passthrough.yaml b/posts/2021-01-08_kvm-usb-auto-passthrough.yaml new file mode 100644 index 0000000..8991f2a --- /dev/null +++ b/posts/2021-01-08_kvm-usb-auto-passthrough.yaml @@ -0,0 +1,77 @@ +date: 2021-01-08 +tags: +- linux +- kvm +title: KVM USB Auto-Passthrough +--- +I do all of my PC gaming on a Windows VM which I run on a Linux host running +KVM. With a new gaming monitor on the way, I got the idea of using a +DisplayPort/USB switcher to switch my keyboard, mouse, and monitor between my +laptop and Windows VM (via KVM's USB passthrough). That way I can connect +the monitor directly to the Windows VM and use its 144 Hz refresh rate instead +of being stuck at what Linux is displaying along with the 30 or 60 fps provided +by [Parsec](https://parsec.app/). + +Before I buy that USB switcher, I need to make sure that I can have my USB +devices to auto-connect to the VM when they get connected to my Linux host. + +Through my cursory searching I couldn't find a way native to libvirt/qemu to do +this, so udev to the rescue! Through udev rules, I'm able to detect when a +device matching some parameters is connected, and then run a command. This +command will get a bunch of environment variables set from udev. The +interesting ones to us are: + +* `ACTION` - add, remove, etc. +* `ID_VENDOR_ID` - the USB vendor ID +* `ID_MODEL_ID` - the USB model ID. + +To test this idea out, I set up a simple rule that matches against my keyboard +and saved this as `/etc/udev/rules.d/90-libvirt-usb.rules`: +``` +ACTION=="bind", \ + SUBSYSTEM=="usb", \ + ENV{ID_VENDOR_ID}=="6b62", \ + ENV{ID_MODEL_ID}=="6869", \ + RUN+="/usr/local/bin/kvm-udev attach steam" +ACTION=="remove", \ + SUBSYSTEM=="usb", \ + ENV{ID_VENDOR_ID}=="6b62", \ + ENV{ID_MODEL_ID}=="6869", \ + RUN+="/usr/local/bin/kvm-udev detach steam" +``` + +And of course, here's the script `/usr/local/bin/kvm-udev` which takes care of +the auto-connecting. It takes two parameters, first either being "attach" or +"detach" and the second being the name of the KVM domain (aka VM). +``` +#!/bin/bash + +# Usage: ./kvm-udev.sh attach|detach + +set -e + +ACTION=$1 +DOMAIN=$2 + +CONF_FILE=$(mktemp --suffix=.kvm-udev) +cat << EOF >$CONF_FILE + + + + + + +EOF + +virsh "${ACTION}-device" "$DOMAIN" "$conf_file" +rm "$CONF_FILE" +``` + +This writes out a temporary file which contains some libvirt XML config for doing +device passthrough, picking details from the environment variables that udev +sets. It then calls `virsh` to attach/detach the device to the given domain +using that temporary config file. + +And with that, every time I plug my keyboard into my KVM host, it automatically +routes it to my Windows VM! Now that this works, I can add an additional rule +for my mouse, so that switching these devices between computers works smoothly.