Initial networking VM

Also general improvements around VMs
This commit is contained in:
2022-05-16 00:05:02 +01:00
parent 5563d1be46
commit 009dec03cf
18 changed files with 487 additions and 111 deletions

View File

@@ -1,6 +1,9 @@
{ lib, pkgs, config, ... }:
let
inherit (lib) optional optionals optionalString flatten concatStringsSep mapAttrsToList mapAttrs' mkIf mkDefault;
inherit (builtins) filter any attrNames attrValues fetchGit;
inherit (lib)
unique optional optionals optionalString flatten concatStringsSep
concatMapStringsSep mapAttrsToList mapAttrs' mkIf mkDefault;
inherit (lib.my) mkOpt' mkBoolOpt';
flattenQEMUOpts = attrs:
@@ -38,6 +41,29 @@ let
pass
'';
# TODO: Upstream or something...
vfio-pci-bind = pkgs.stdenv.mkDerivation rec {
pname = "vfio-pci-bind";
version = "b41e4545b21de434fc51a34a9bf1d72e3ac66cc8";
src = fetchGit {
url = "https://github.com/andre-richter/vfio-pci-bind";
rev = version;
};
prePatch = ''
substituteInPlace vfio-pci-bind.sh \
--replace modprobe ${pkgs.kmod}/bin/modprobe
substituteInPlace 25-vfio-pci-bind.rules \
--replace vfio-pci-bind.sh "$out"/bin/vfio-pci-bind.sh
'';
installPhase = ''
mkdir -p "$out"/bin/ "$out"/lib/udev/rules.d
cp vfio-pci-bind.sh "$out"/bin/
cp 25-vfio-pci-bind.rules "$out"/lib/udev/rules.d/
'';
};
cfg = config.my.vms;
netOpts = with lib.types; { name, ... }: {
@@ -61,6 +87,12 @@ let
};
};
hostDevOpts = with lib.types; {
options = {
bindVFIO = mkBoolOpt' true "Whether to automatically bind the device to vfio-pci.";
};
};
vmOpts = with lib.types; { name, ... }: {
options = {
qemuBin = mkOpt' path "${pkgs.qemu_kvm}/bin/qemu-kvm" "Path to QEMU executable.";
@@ -85,9 +117,16 @@ let
spice.enable = mkBoolOpt' true "Whether to enable SPICE.";
networks = mkOpt' (attrsOf (submodule netOpts)) { } "Networks to attach VM to.";
drives = mkOpt' (attrsOf (submodule driveOpts)) { } "Drives to attach to VM.";
hostDevices = mkOpt' (attrsOf (submodule hostDevOpts)) { } "Host PCI devices to pass to the VM.";
};
};
allHostDevs =
flatten
(map
(i: mapAttrsToList (bdf: c: { inherit bdf; inherit (c) bindVFIO; }) i.hostDevices)
(attrValues cfg.instances));
mkQemuCommand = n: i:
let
flags =
@@ -122,7 +161,8 @@ let
"blockdev node-name=${dn}-backend,${c.backend}"
"blockdev node-name=${dn}-format,${c.formatBackendProp}=${dn}-backend,${c.format}"
("device ${c.frontend},id=${dn},drive=${dn}-format" + (extraQEMUOpts c.frontendOpts))
]) i.drives));
]) i.drives)) ++
(map (bdf: "device vfio-pci,host=${bdf}") (attrNames i.hostDevices));
args = map (v: "-${v}") flags;
in
concatStringsSep " " ([ i.qemuBin ] ++ args);
@@ -134,6 +174,30 @@ in
};
config = mkIf (cfg.instances != { }) {
assertions = [
{
assertion = let bdfs = map (d: d.bdf) allHostDevs; in (unique bdfs) == bdfs;
message = "VMs cannot share host devices!";
}
];
services.udev = {
packages =
optionals
(any (d: d.bindVFIO) allHostDevs)
[
vfio-pci-bind
(pkgs.writeTextDir
"etc/udev/rules.d/20-vfio-tags.rules"
(concatMapStringsSep
"\n"
(d: ''ACTION=="add", SUBSYSTEM=="pci", KERNEL=="0000:${d.bdf}", TAG="vfio-pci-bind"'')
(filter (d: d.bindVFIO) allHostDevs)))
];
};
my.tmproot.persistence.config.directories = [ "/var/lib/vms" ];
# qemu-bridge-helper will fail otherwise
environment.etc."qemu/bridge.conf".text = "allow all";
systemd = {