Skip to content

Commit

Permalink
Added shared memory support for Wayland
Browse files Browse the repository at this point in the history
Signed-off-by: Jaroslaw Kurowski <[email protected]>
  • Loading branch information
gangaram-tii authored and jkuro-tii committed Oct 16, 2024
1 parent 6474f07 commit 0811965
Show file tree
Hide file tree
Showing 16 changed files with 622 additions and 11 deletions.
7 changes: 7 additions & 0 deletions modules/common/services/desktop.nix
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,13 @@ in
icon = "${pkgs.icon-pack}/system-file-manager.svg";
}

{
name = "Network Settings";
description = "Manage Network & Wi-Fi Settings";
path = "${pkgs.nm-launcher.override { inherit (config.ghaf.users.accounts) uid; }}/bin/nm-launcher";
icon = "${pkgs.icon-pack}/preferences-system-network.svg";
}

{
name = "Bluetooth Settings";
description = "Manage Bluetooth Devices & Settings";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
ProtectSystem = "full";
ProtectProc = "noaccess";
# ReadWritePaths=[ "/etc"];
PrivateTmp = true;
PrivateTmp = false;

# Not applicable for the service runs as root
# PrivateMounts=true;
Expand Down
8 changes: 8 additions & 0 deletions modules/common/users/accounts.nix
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ in
A default user to create in the system.
'';
};
uid = mkOption {
default = 1000;
type = with types; int;
description = ''
A default user id for the user.
'';
};
password = mkOption {
default = "ghaf";
type = with types; str;
Expand All @@ -38,6 +45,7 @@ in
users."${cfg.user}" = {
isNormalUser = true;
inherit (cfg) password;
inherit (cfg) uid;
#TODO add "docker" use "lib.optionals"
extraGroups = [
"wheel"
Expand Down
4 changes: 3 additions & 1 deletion modules/desktop/graphics/waybar.config.nix
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ in
},
"custom/admin": {
"format": " ",
"on-click": "${pkgs.nm-launcher}/bin/nm-launcher",
"on-click": "${
pkgs.nm-launcher.override { inherit (config.ghaf.users.accounts) uid; }
}/bin/nm-launcher",
"tooltip": false
},
"pulseaudio": {
Expand Down
1 change: 1 addition & 0 deletions modules/hardware/common/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
./devices.nix
./kernel.nix
./qemu.nix
./shared-mem.nix
];
}
229 changes: 229 additions & 0 deletions modules/hardware/common/shared-mem.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors
# SPDX-License-Identifier: Apache-2.0
#
# Module for Shared Memory Definitions
#
{
config,
lib,
pkgs,
...
}:
with lib;
{
options.ghaf.shm = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = mdDoc ''
Enables using shared memory between VMs.
'';
};
memSize = mkOption {
type = types.int;
default = 16;
description = mdDoc ''
Defines shared memory size in MBytes
'';
};
hostSocketPath = mkOption {
type = types.path;
default = "/tmp/ivshmem_socket"; # The value is hardcoded in the application
description = mdDoc ''
Defines location of the shared memory socket. It's used by qemu
instances for memory sharing and sending interrupts.
'';
};
flataddr = mkOption {
type = types.str;
default = "0x920000000";
description = mdDoc ''
If set to a non-zero value, it maps the shared memory
into this physical address. The value is arbitrary chosen, platform
specific, in order not to conflict with other memory areas (e.g. PCI).
'';
};
vms_enabled = mkOption {
type = types.listOf types.str;
default = [ ];
description = mdDoc ''
If set to a non-zero value, it maps the shared memory
into this physical address. The value is arbitrary chosen, platform
specific, in order not to conflict with other memory areas (e.g. PCI).
'';
};
enable_host = lib.mkOption {
type = lib.types.bool;
default = false;
description = mdDoc ''
Enables memsocket on host.
'';
apply =
enable:
if enable && !config.ghaf.shm.enable then
builtins.throw "Enable shared memory in order to use shm on host"
else
enable;
};
instancesCount = mkOption {
type = types.int;
default =
if config.ghaf.shm.enable_host then
(builtins.length config.ghaf.shm.vms_enabled) + 1
else
builtins.length config.ghaf.shm.vms_enabled;
};
serverSocketPath = mkOption {
type = types.path;
default = "/run/user/${builtins.toString config.ghaf.users.accounts.uid}/memsocket-server.sock";
description = mdDoc ''
Defines location of the listening socket.
It's used by waypipe as an output socket when running in server mode
'';
};
clientSocketPath = mkOption {
type = types.path;
default = "/run/user/${builtins.toString config.ghaf.users.accounts.uid}/memsocket-client.sock";
description = mdDoc ''
Defines location of the output socket. It's fed
with data coming from AppVMs.
It's used by waypipe as an input socket when running in client mode
'';
};
display = mkOption {
type = types.bool;
default = false;
description = "Display VMs using shared memory";
apply =
enable:
if enable && !config.ghaf.shm.enable then
builtins.throw "Enable shared memory in order to use shm display"
else
enable;
};
};

config.boot.kernelParams =
let
hugepagesz = "2M"; # valid values: "2M" and "1G", as kernel supports these huge pages' size
hugepages =
if hugepagesz == "2M" then config.ghaf.shm.memSize / 2 else config.ghaf.shm.memSize / 1024;
in
optionals config.ghaf.shm.enable [
"hugepagesz=${hugepagesz}"
"hugepages=${toString hugepages}"
];
config.environment.systemPackages = optionals config.ghaf.shm.enable_host [
(pkgs.callPackage ../../../packages/memsocket { vms = config.ghaf.shm.instancesCount; })
];

config.systemd.services.ivshmemsrv =
let
pidFilePath = "/tmp/ivshmem-server.pid";
ivShMemSrv =
let
vectors = toString (2 * config.ghaf.shm.instancesCount);
in
pkgs.writeShellScriptBin "ivshmemsrv" ''
chown microvm /dev/hugepages
chgrp kvm /dev/hugepages
if [ -S ${config.ghaf.shm.hostSocketPath} ]; then
echo Erasing ${config.ghaf.shm.hostSocketPath} ${pidFilePath}
rm -f ${config.ghaf.shm.hostSocketPath}
fi
${pkgs.sudo}/sbin/sudo -u microvm -g kvm ${pkgs.qemu_kvm}/bin/ivshmem-server -p ${pidFilePath} -n ${vectors} -m /dev/hugepages/ -l ${
(toString config.ghaf.shm.memSize) + "M"
}
'';
in
lib.mkIf config.ghaf.shm.enable {
enable = true;
description = "Start qemu ivshmem memory server";
path = [ ivShMemSrv ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
StandardOutput = "journal";
StandardError = "journal";
ExecStart = "${ivShMemSrv}/bin/ivshmemsrv";
};
};
config.microvm.vms =
let
memsocket = pkgs.callPackage ../../../packages/memsocket { vms = config.ghaf.shm.instancesCount; };
vectors = toString (2 * config.ghaf.shm.instancesCount);
makeAssignment = vmName: {
${vmName} = {
config = {
config = {
microvm = {
qemu = {
extraArgs = [
"-device"
"ivshmem-doorbell,vectors=${vectors},chardev=ivs_socket,flataddr=${config.ghaf.shm.flataddr}"
"-chardev"
"socket,path=${config.ghaf.shm.hostSocketPath},id=ivs_socket"
];
};
kernelParams = [ "kvm_ivshmem.flataddr=${config.ghaf.shm.flataddr}" ];
};
boot.extraModulePackages = [
(pkgs.linuxPackages.callPackage ../../../packages/memsocket/module.nix {
inherit (config.microvm.vms.${vmName}.config.config.boot.kernelPackages) kernel;
vmCount = config.ghaf.shm.instancesCount;
})
];
services = {
udev = {
extraRules = ''
SUBSYSTEM=="misc",KERNEL=="ivshmem",GROUP="kvm",MODE="0666"
'';
};
};
environment.systemPackages = optionals config.ghaf.shm.enable [
memsocket
];
systemd.user.services.memsocket =
if vmName == "gui-vm" then
lib.mkIf config.ghaf.shm.enable {
enable = true;
description = "memsocket";
after = [ "labwc.service" ];
serviceConfig = {
Type = "simple";
ExecStart = "${memsocket}/bin/memsocket -c ${config.ghaf.shm.clientSocketPath}";
Restart = "always";
RestartSec = "1";
};
wantedBy = [ "ghaf-session.target" ];
}
else
# machines connecting to gui-vm
let
vmIndex = lib.lists.findFirstIndex (vm: vm == vmName) null config.ghaf.shm.vms_enabled;
in
lib.mkIf config.ghaf.shm.enable {
enable = true;
description = "memsocket";
serviceConfig = {
Type = "simple";
ExecStart = "${memsocket}/bin/memsocket -s ${config.ghaf.shm.serverSocketPath} ${builtins.toString vmIndex}";
Restart = "always";
RestartSec = "1";
};
wantedBy = [ "default.target" ];
};
};
};
};
};
in
mkIf config.ghaf.shm.enable (
foldl' lib.attrsets.recursiveUpdate { } (map makeAssignment config.ghaf.shm.vms_enabled)
);

config.ghaf.hardware.definition.gpu.kernelConfig.kernelParams = optionals config.ghaf.shm.enable [
"kvm_ivshmem.flataddr=${config.ghaf.shm.flataddr}"
];
}
1 change: 1 addition & 0 deletions modules/microvm/flake-module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
./virtualization/microvm/modules.nix
./networking.nix
./power-control.nix
../hardware/common/shared-mem.nix
];
};
}
15 changes: 11 additions & 4 deletions modules/microvm/virtualization/microvm/appvm.nix
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,17 @@ let
}:
let
waypipeBorder = if vm.borderColor != null then "--border \"${vm.borderColor}\"" else "";
runWaypipe = pkgs.writeScriptBin "run-waypipe" ''
#!${pkgs.runtimeShell} -e
${pkgs.waypipe}/bin/waypipe --vsock -s ${toString configHost.ghaf.virtualization.microvm.guivm.waypipePort} ${waypipeBorder} server "$@"
'';
runWaypipe =
if configHost.ghaf.shm.display then
pkgs.writeScriptBin "run-waypipe" ''
#!${pkgs.runtimeShell} -e
${pkgs.waypipe}/bin/waypipe -s ${configHost.ghaf.shm.serverSocketPath} ${waypipeBorder} server "$@"
''
else
pkgs.writeScriptBin "run-waypipe" ''
#!${pkgs.runtimeShell} -e
${pkgs.waypipe}/bin/waypipe --vsock -s ${toString configHost.ghaf.virtualization.microvm.guivm.waypipePort} ${waypipeBorder} server "$@"
'';
in
{
ghaf = {
Expand Down
9 changes: 7 additions & 2 deletions modules/microvm/virtualization/microvm/guivm.nix
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ let
pkgs.sticky-notes
])
++ [
pkgs.nm-launcher
(pkgs.nm-launcher.override { inherit (config.ghaf.users.accounts) uid; })
pkgs.bt-launcher
pkgs.pamixer
pkgs.eww
Expand Down Expand Up @@ -206,7 +206,11 @@ let
description = "waypipe";
serviceConfig = {
Type = "simple";
ExecStart = "${pkgs.waypipe}/bin/waypipe --vsock -s ${toString cfg.waypipePort} client";
ExecStart =
if config.ghaf.shm.display then
"${pkgs.waypipe}/bin/waypipe -s ${config.ghaf.shm.clientSocketPath} client"
else
"${pkgs.waypipe}/bin/waypipe --vsock -s ${toString cfg.waypipePort} client";
Restart = "always";
RestartSec = "1";
};
Expand Down Expand Up @@ -277,6 +281,7 @@ in
};

config = lib.mkIf cfg.enable {
ghaf.shm.vms_enabled = [ vmName ]; # Allow access to VMs shared memory
microvm.vms."${vmName}" = {
autostart = true;
config = guivmBaseConfiguration // {
Expand Down
4 changes: 4 additions & 0 deletions modules/reference/appvms/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,9 @@ in
++ (lib.optionals cfg.comms-vm [ (import ./comms.nix { inherit pkgs lib config; }) ])
++ (lib.optionals cfg.business-vm [ (import ./business.nix { inherit pkgs lib config; }) ]);
};
ghaf.shm.vms_enabled = [
"chromium-vm"
"element-vm"
]; # Allow access to VMs shared memory
};
}
2 changes: 1 addition & 1 deletion overlays/custom-packages/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
element-web = final.callPackage ../../packages/element-web { };
waypipe = import ./waypipe { inherit final prev; };
qemu_kvm = import ./qemu { inherit final prev; };
nm-launcher = final.callPackage ../../packages/nm-launcher { };
nm-launcher = final.callPackage ../../packages/nm-launcher { uid = null; };
bt-launcher = final.callPackage ../../packages/bt-launcher { };
icon-pack = final.callPackage ../../packages/icon-pack { };
labwc = import ./labwc { inherit prev; };
Expand Down
Loading

0 comments on commit 0811965

Please sign in to comment.