mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-10-02 13:00:23 +00:00
nixos/qemu-vm: 9p -> virtiofs (#552774)
This commit is contained in:
@@ -324,6 +324,24 @@ let
|
||||
''
|
||||
)}
|
||||
|
||||
echo "Starting virtiofs daemons..."
|
||||
NIX_VIRTIOFS_DIR=$(mktemp -d)
|
||||
${lib.concatLines (
|
||||
lib.mapAttrsToList (tag: share: ''
|
||||
${lib.getExe hostPkgs.virtiofsd} \
|
||||
--socket-path="$NIX_VIRTIOFS_DIR"/"${tag}" \
|
||||
--shared-dir="${share.source}" \
|
||||
${if share.writable then "--writeback" else "--readonly"} \
|
||||
--sandbox=none \
|
||||
--seccomp=none \
|
||||
--cache=always \
|
||||
--no-announce-submounts \
|
||||
--translate-uid=host:65534:0:1 \
|
||||
--translate-gid=host:65534:0:1 \
|
||||
&
|
||||
'') cfg.sharedDirectories
|
||||
)}
|
||||
|
||||
# Start QEMU.
|
||||
exec ${
|
||||
qemu-common.qemuBinaryWith {
|
||||
@@ -336,14 +354,6 @@ let
|
||||
-smp ${toString config.virtualisation.cores} \
|
||||
-device virtio-rng-pci \
|
||||
${concatStringsSep " " config.virtualisation.qemu.networkingOptions} \
|
||||
${
|
||||
concatStringsSep " \\\n " (
|
||||
mapAttrsToList (
|
||||
tag: share:
|
||||
"-virtfs local,path=${share.source},security_model=${share.securityModel},mount_tag=${tag}"
|
||||
) config.virtualisation.sharedDirectories
|
||||
)
|
||||
} \
|
||||
${drivesCmdLine config.virtualisation.qemu.drives} \
|
||||
${concatStringsSep " \\\n " config.virtualisation.qemu.options} \
|
||||
$QEMU_OPTS \
|
||||
@@ -424,6 +434,14 @@ in
|
||||
"virtualisation"
|
||||
"useSecureBoot"
|
||||
] "The default OVMF now always supports Secure Boot.")
|
||||
(mkRemovedOptionModule [
|
||||
"virtualisation"
|
||||
"msize"
|
||||
] "9p was replaced with virtiofs and thus this option is obsolete.")
|
||||
(mkRemovedOptionModule [
|
||||
"virtualisation"
|
||||
"nixStore9pCache"
|
||||
] "9p was replaced with virtiofs and thus this option is obsolete.")
|
||||
];
|
||||
|
||||
options = {
|
||||
@@ -438,16 +456,6 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.msize = mkOption {
|
||||
type = types.ints.positive;
|
||||
default = 16384;
|
||||
description = ''
|
||||
The msize (maximum packet size) option passed to 9p file systems, in
|
||||
bytes. Increasing this should increase performance significantly,
|
||||
at the cost of higher RAM usage.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.diskImage = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = "./${config.system.name}.qcow2";
|
||||
@@ -570,22 +578,8 @@ in
|
||||
type = types.path;
|
||||
description = "The mount point of the directory inside the virtual machine";
|
||||
};
|
||||
options.securityModel = mkOption {
|
||||
type = types.enum [
|
||||
"passthrough"
|
||||
"mapped-xattr"
|
||||
"mapped-file"
|
||||
"none"
|
||||
];
|
||||
default = "mapped-xattr";
|
||||
description = ''
|
||||
The security model to use for this share:
|
||||
|
||||
- `passthrough`: files are stored using the same credentials as they are created on the guest (this requires QEMU to run as root)
|
||||
- `mapped-xattr`: some of the file attributes like uid, gid, mode bits and link target are stored as file attributes
|
||||
- `mapped-file`: the attributes are stored in the hidden .virtfs_metadata directory. Directories exported by this security model cannot interact with other unix tools
|
||||
- `none`: same as "passthrough" except the sever won't report failures if it fails to set file attributes like ownership
|
||||
'';
|
||||
options.writable = lib.mkEnableOption "" // {
|
||||
description = "Whether the directory is writable on the host and guest.";
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -610,11 +604,10 @@ in
|
||||
A list of paths whose closure should be made available to
|
||||
the VM.
|
||||
|
||||
When 9p is used, the closure is registered in the Nix
|
||||
database in the VM. All other paths in the host Nix store
|
||||
appear in the guest Nix store as well, but are considered
|
||||
garbage (because they are not registered in the Nix
|
||||
database of the guest).
|
||||
When the Nix store is mounted from the host, the closure is registered
|
||||
in the Nix database in the VM. All other paths in the host Nix store
|
||||
appear in the guest Nix store as well, but are considered garbage
|
||||
(because they are not registered in the Nix database of the guest).
|
||||
|
||||
When {option}`virtualisation.useNixStoreImage` is
|
||||
set, the closure is copied to the Nix store image.
|
||||
@@ -867,7 +860,7 @@ in
|
||||
default = false;
|
||||
description = ''
|
||||
Build and use a disk image for the Nix store, instead of
|
||||
accessing the host's one through 9p.
|
||||
accessing the host's one.
|
||||
|
||||
For applications which do a lot of reads from the store,
|
||||
this can drastically improve performance, but at the cost of
|
||||
@@ -889,24 +882,7 @@ in
|
||||
default = !cfg.useNixStoreImage && !cfg.useBootLoader;
|
||||
defaultText = literalExpression "!cfg.useNixStoreImage && !cfg.useBootLoader";
|
||||
description = ''
|
||||
Mount the host Nix store as a 9p mount.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.nixStore9pCache = mkOption {
|
||||
type = types.enum [
|
||||
"loose"
|
||||
"none"
|
||||
"fscache"
|
||||
];
|
||||
default = "loose";
|
||||
description = ''
|
||||
Type of 9p cache to use when mounting host nix store. "none" provides
|
||||
no caching. "loose" enables Linux's local VFS cache. "fscache" uses Linux's
|
||||
fscache subsystem.
|
||||
|
||||
This option is only respected when {option}`virtualisation.mountHostNixStore`
|
||||
is enabled.
|
||||
Mount the host Nix store via a virtual filesystem.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -1242,22 +1218,20 @@ in
|
||||
# Always mount this to /nix/.ro-store because we never want to actually
|
||||
# write to the host Nix Store.
|
||||
target = "/nix/.ro-store";
|
||||
securityModel = "none";
|
||||
};
|
||||
xchg = {
|
||||
source = ''"$TMPDIR"/xchg'';
|
||||
securityModel = "none";
|
||||
target = "/tmp/xchg";
|
||||
writable = true;
|
||||
};
|
||||
shared = {
|
||||
source = ''"''${SHARED_DIR:-$TMPDIR/xchg}"'';
|
||||
target = "/tmp/shared";
|
||||
securityModel = "none";
|
||||
writable = true;
|
||||
};
|
||||
certs = mkIf cfg.useHostCerts {
|
||||
source = ''"$TMPDIR"/certs'';
|
||||
target = "/etc/ssl/certs";
|
||||
securityModel = "none";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1303,6 +1277,12 @@ in
|
||||
"-object memory-backend-memfd,id=mem0,size=${toString config.virtualisation.memorySize}M,share=on"
|
||||
"-machine memory-backend=mem0"
|
||||
])
|
||||
(lib.flatten (
|
||||
lib.mapAttrsToList (tag: share: [
|
||||
"-chardev socket,id=${tag},path=$NIX_VIRTIOFS_DIR/${tag}"
|
||||
"-device vhost-user-fs-pci,chardev=${tag},tag=${tag}"
|
||||
]) cfg.sharedDirectories
|
||||
))
|
||||
(
|
||||
let
|
||||
alphaNumericChars = lowerChars ++ upperChars ++ (map toString (range 0 9));
|
||||
@@ -1388,86 +1368,78 @@ in
|
||||
|
||||
virtualisation.diskSizeAutoSupported = false;
|
||||
|
||||
virtualisation.fileSystems =
|
||||
let
|
||||
mkSharedDir = tag: share: {
|
||||
name = share.target;
|
||||
value.device = tag;
|
||||
value.fsType = "9p";
|
||||
value.neededForBoot = true;
|
||||
value.options = [
|
||||
"trans=virtio"
|
||||
"version=9p2000.L"
|
||||
"msize=${toString cfg.msize}"
|
||||
"x-systemd.requires=modprobe@9pnet_virtio.service"
|
||||
]
|
||||
++ lib.optional (tag == "nix-store") "cache=${cfg.nixStore9pCache}";
|
||||
virtualisation.fileSystems = lib.mkMerge [
|
||||
(lib.mapAttrs' (tag: share: {
|
||||
name = share.target;
|
||||
value = {
|
||||
device = tag;
|
||||
fsType = "virtiofs";
|
||||
neededForBoot = true;
|
||||
options = lib.mkIf (!share.writable) [ "ro" ];
|
||||
};
|
||||
in
|
||||
lib.mkMerge [
|
||||
(lib.mapAttrs' mkSharedDir cfg.sharedDirectories)
|
||||
{
|
||||
"/" = lib.mkIf cfg.useDefaultFilesystems (
|
||||
if cfg.diskImage == null then
|
||||
{
|
||||
device = "tmpfs";
|
||||
fsType = "tmpfs";
|
||||
options = [ "mode=755" ];
|
||||
}
|
||||
else
|
||||
{
|
||||
device = cfg.rootDevice;
|
||||
fsType = "ext4";
|
||||
}
|
||||
);
|
||||
"/tmp" = lib.mkIf config.boot.tmp.useTmpfs {
|
||||
device = "tmpfs";
|
||||
fsType = "tmpfs";
|
||||
neededForBoot = true;
|
||||
# Sync with systemd's tmp.mount;
|
||||
options = [
|
||||
"mode=1777"
|
||||
"strictatime"
|
||||
"nosuid"
|
||||
"nodev"
|
||||
"size=${toString config.boot.tmp.tmpfsSize}"
|
||||
];
|
||||
};
|
||||
"/nix/store" = lib.mkIf (cfg.useNixStoreImage || cfg.mountHostNixStore) (
|
||||
if cfg.writableStore then
|
||||
{
|
||||
overlay = {
|
||||
lowerdir = [ "/nix/.ro-store" ];
|
||||
upperdir = "/nix/.rw-store/upper";
|
||||
workdir = "/nix/.rw-store/work";
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
device = "/nix/.ro-store";
|
||||
fsType = "none";
|
||||
options = [ "bind" ];
|
||||
}
|
||||
);
|
||||
"/nix/.ro-store" = lib.mkIf cfg.useNixStoreImage {
|
||||
device = "/dev/disk/by-label/${nixStoreFilesystemLabel}";
|
||||
fsType = "erofs";
|
||||
neededForBoot = true;
|
||||
options = [ "ro" ];
|
||||
};
|
||||
"/nix/.rw-store" = lib.mkIf (cfg.writableStore && cfg.writableStoreUseTmpfs) {
|
||||
fsType = "tmpfs";
|
||||
options = [ "mode=0755" ];
|
||||
neededForBoot = true;
|
||||
};
|
||||
"${config.boot.loader.efi.efiSysMountPoint}" =
|
||||
lib.mkIf (cfg.useBootLoader && cfg.bootPartition != null)
|
||||
{
|
||||
device = cfg.bootPartition;
|
||||
fsType = "vfat";
|
||||
}) cfg.sharedDirectories)
|
||||
{
|
||||
"/" = lib.mkIf cfg.useDefaultFilesystems (
|
||||
if cfg.diskImage == null then
|
||||
{
|
||||
device = "tmpfs";
|
||||
fsType = "tmpfs";
|
||||
options = [ "mode=755" ];
|
||||
}
|
||||
else
|
||||
{
|
||||
device = cfg.rootDevice;
|
||||
fsType = "ext4";
|
||||
}
|
||||
);
|
||||
"/tmp" = lib.mkIf config.boot.tmp.useTmpfs {
|
||||
device = "tmpfs";
|
||||
fsType = "tmpfs";
|
||||
neededForBoot = true;
|
||||
# Sync with systemd's tmp.mount;
|
||||
options = [
|
||||
"mode=1777"
|
||||
"strictatime"
|
||||
"nosuid"
|
||||
"nodev"
|
||||
"size=${toString config.boot.tmp.tmpfsSize}"
|
||||
];
|
||||
};
|
||||
"/nix/store" = lib.mkIf (cfg.useNixStoreImage || cfg.mountHostNixStore) (
|
||||
if cfg.writableStore then
|
||||
{
|
||||
overlay = {
|
||||
lowerdir = [ "/nix/.ro-store" ];
|
||||
upperdir = "/nix/.rw-store/upper";
|
||||
workdir = "/nix/.rw-store/work";
|
||||
};
|
||||
}
|
||||
];
|
||||
}
|
||||
else
|
||||
{
|
||||
device = "/nix/.ro-store";
|
||||
fsType = "none";
|
||||
options = [ "bind" ];
|
||||
}
|
||||
);
|
||||
"/nix/.ro-store" = lib.mkIf cfg.useNixStoreImage {
|
||||
device = "/dev/disk/by-label/${nixStoreFilesystemLabel}";
|
||||
fsType = "erofs";
|
||||
neededForBoot = true;
|
||||
options = [ "ro" ];
|
||||
};
|
||||
"/nix/.rw-store" = lib.mkIf (cfg.writableStore && cfg.writableStoreUseTmpfs) {
|
||||
fsType = "tmpfs";
|
||||
options = [ "mode=0755" ];
|
||||
neededForBoot = true;
|
||||
};
|
||||
"${config.boot.loader.efi.efiSysMountPoint}" =
|
||||
lib.mkIf (cfg.useBootLoader && cfg.bootPartition != null)
|
||||
{
|
||||
device = cfg.bootPartition;
|
||||
fsType = "vfat";
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
swapDevices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) [ ];
|
||||
boot.initrd.luks.devices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) { };
|
||||
|
||||
@@ -840,9 +840,7 @@ in
|
||||
healthchecks = runTest ./web-apps/healthchecks.nix;
|
||||
hedgedoc = runTest ./hedgedoc.nix;
|
||||
herbstluftwm = runTest ./herbstluftwm.nix;
|
||||
# 9pnet_virtio used to mount /nix partition doesn't support
|
||||
# hibernation. This test happens to work on x86_64-linux but
|
||||
# not on other platforms.
|
||||
# This test happens to work on x86_64-linux but not on other platforms.
|
||||
hibernate = handleTestOn [ "x86_64-linux" ] ./hibernate.nix {
|
||||
systemdStage1 = false;
|
||||
};
|
||||
|
||||
@@ -29,7 +29,9 @@ makeTest {
|
||||
powerManagement.resumeCommands = "systemctl --no-block restart backdoor.service";
|
||||
|
||||
virtualisation.emptyDiskImages = [ (2 * config.virtualisation.memorySize) ];
|
||||
# virtiofs doesn't support hibernation
|
||||
virtualisation.useNixStoreImage = true;
|
||||
virtualisation.sharedDirectories = lib.mkForce { };
|
||||
|
||||
swapDevices = lib.mkOverride 0 [
|
||||
{
|
||||
|
||||
@@ -55,17 +55,16 @@
|
||||
sharedReadOnly.fail(build_derivation)
|
||||
imageReadOnly.fail(build_derivation)
|
||||
|
||||
# Checking whether the fs type is 9P is just a proxy to test whether the
|
||||
# Nix Store is shared. If we switch to a different technology (e.g.
|
||||
# virtiofs) for sharing, we need to adjust these tests.
|
||||
# Checking whether the fs type is virtiofs is just a proxy to test whether the
|
||||
# Nix Store is shared.
|
||||
|
||||
with subtest("Nix store is shared from the host via 9P"):
|
||||
sharedWritable.succeed("findmnt --kernel --type 9P /nix/.ro-store")
|
||||
sharedReadOnly.succeed("findmnt --kernel --type 9P /nix/.ro-store")
|
||||
with subtest("Nix store is shared from the host via virtiofs"):
|
||||
sharedWritable.succeed("findmnt --kernel --type virtiofs /nix/.ro-store")
|
||||
sharedReadOnly.succeed("findmnt --kernel --type virtiofs /nix/.ro-store")
|
||||
|
||||
with subtest("Nix store is not shared via 9P"):
|
||||
imageWritable.fail("findmnt --kernel --type 9P /nix/.ro-store")
|
||||
imageReadOnly.fail("findmnt --kernel --type 9P /nix/.ro-store")
|
||||
with subtest("Nix store is not shared via virtiofs"):
|
||||
imageWritable.fail("findmnt --kernel --type virtiofs /nix/.ro-store")
|
||||
imageReadOnly.fail("findmnt --kernel --type virtiofs /nix/.ro-store")
|
||||
|
||||
with subtest("Nix store is not mounted separately"):
|
||||
rootDevice = fullDisk.succeed("stat -c %d /")
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
)
|
||||
}
|
||||
mkdir -p /tmp/shared
|
||||
mount -t 9p shared -o trans=virtio,version=9p2000.L /tmp/shared
|
||||
mount -t virtiofs shared /tmp/shared
|
||||
touch /tmp/shared/shutdown-test
|
||||
umount /tmp/shared
|
||||
'';
|
||||
|
||||
Reference in New Issue
Block a user