Compare commits
2 commits
db4e7809d1
...
add1c4f6bd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
add1c4f6bd | ||
|
|
cf95fd33b0 |
7 changed files with 285 additions and 177 deletions
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
lib,
|
|
||||||
writers,
|
|
||||||
}:
|
|
||||||
writers.writePython3Bin "uvms-guest" { } ./guest.py
|
|
||||||
|
|
@ -13,6 +13,8 @@
|
||||||
strace,
|
strace,
|
||||||
util-linux,
|
util-linux,
|
||||||
virtiofsd,
|
virtiofsd,
|
||||||
|
python3Packages,
|
||||||
|
uvmslib,
|
||||||
|
|
||||||
taps,
|
taps,
|
||||||
baseImage,
|
baseImage,
|
||||||
|
|
@ -36,7 +38,11 @@ let
|
||||||
};
|
};
|
||||||
toolsClosure = writeClosure toolsFarm;
|
toolsClosure = writeClosure toolsFarm;
|
||||||
in
|
in
|
||||||
writers.writePython3Bin "uvms" { } (
|
writers.writePython3Bin "uvms"
|
||||||
|
{
|
||||||
|
libraries = [ uvmslib ];
|
||||||
|
}
|
||||||
|
(
|
||||||
replaceVars ./uvms.py {
|
replaceVars ./uvms.py {
|
||||||
BWRAP = "${lib.getExe bubblewrap}";
|
BWRAP = "${lib.getExe bubblewrap}";
|
||||||
TOOLS = "${toolsFarm}/bin";
|
TOOLS = "${toolsFarm}/bin";
|
||||||
|
|
@ -53,4 +59,4 @@ writers.writePython3Bin "uvms" { } (
|
||||||
baseImage.config.system.build.ch
|
baseImage.config.system.build.ch
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import socket
|
import socket
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from contextlib import contextmanager, closing, ExitStack
|
from contextlib import contextmanager, closing, ExitStack
|
||||||
|
|
||||||
|
|
@ -20,6 +21,7 @@ parser.add_argument("--prefix", default="$HOME/uvms/$VM")
|
||||||
parser.add_argument("--vm-config", default="@BASE_CONFIG@") # noqa: E501
|
parser.add_argument("--vm-config", default="@BASE_CONFIG@") # noqa: E501
|
||||||
parser.add_argument("--persist-home", action="store_true")
|
parser.add_argument("--persist-home", action="store_true")
|
||||||
parser.add_argument("--run", action="append")
|
parser.add_argument("--run", action="append")
|
||||||
|
parser.add_argument("--mem", default=None)
|
||||||
parser.add_argument("app", nargs="*", default=())
|
parser.add_argument("app", nargs="*", default=())
|
||||||
|
|
||||||
TOOLS_DIR = "@TOOLS@" # noqa: E501
|
TOOLS_DIR = "@TOOLS@" # noqa: E501
|
||||||
|
|
@ -297,6 +299,7 @@ class Processes:
|
||||||
# "-Z",
|
# "-Z",
|
||||||
# "-ff",
|
# "-ff",
|
||||||
CH,
|
CH,
|
||||||
|
# "-v",
|
||||||
"--api-socket",
|
"--api-socket",
|
||||||
"fd=0",
|
"fd=0",
|
||||||
# f"fd={s.fileno()}"
|
# f"fd={s.fileno()}"
|
||||||
|
|
@ -318,6 +321,8 @@ class Processes:
|
||||||
# ro_bind=["/nix/store"], # I give up
|
# ro_bind=["/nix/store"], # I give up
|
||||||
unshare_net=False,
|
unshare_net=False,
|
||||||
shell=False,
|
shell=False,
|
||||||
|
stdout=None,
|
||||||
|
stderr=None,
|
||||||
# pass_fds=(s.fileno(),)
|
# pass_fds=(s.fileno(),)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -338,29 +343,23 @@ class Processes:
|
||||||
):
|
):
|
||||||
sock_path = self.prefix + "/gpu.sock"
|
sock_path = self.prefix + "/gpu.sock"
|
||||||
args = [
|
args = [
|
||||||
SOCKETBINDER,
|
|
||||||
"-b",
|
|
||||||
"1",
|
|
||||||
sock_path,
|
|
||||||
"s6-ipcserverd",
|
|
||||||
"-1c1",
|
|
||||||
# "@STRACE@", # noqa: E501
|
|
||||||
# "-Z",
|
|
||||||
# "-ff",
|
|
||||||
"@CROSVM@", # noqa: E501
|
"@CROSVM@", # noqa: E501
|
||||||
"--no-syslog",
|
"--no-syslog",
|
||||||
|
"--log-level",
|
||||||
|
"debug",
|
||||||
"device",
|
"device",
|
||||||
"gpu",
|
"gpu",
|
||||||
"--fd",
|
"--socket-path",
|
||||||
"0",
|
sock_path,
|
||||||
"--wayland-sock",
|
"--wayland-sock",
|
||||||
f'{PASSTHRU_ENV["XDG_RUNTIME_DIR"]}/{PASSTHRU_ENV["WAYLAND_DISPLAY"]}', # noqa: E501
|
f'{PASSTHRU_ENV["XDG_RUNTIME_DIR"]}/{PASSTHRU_ENV["WAYLAND_DISPLAY"]}', # noqa: E501
|
||||||
"--params",
|
"--params",
|
||||||
'{ "context-types": "cross-domain:virgl2:venus" }',
|
'{ "context-types": "cross-domain" }',
|
||||||
]
|
]
|
||||||
with self.popen(
|
with self.popen(
|
||||||
*args,
|
*args,
|
||||||
stderr=None,
|
stderr=None,
|
||||||
|
stdout=None,
|
||||||
) as proc, removing(sock_path, sock_path + ".lock"):
|
) as proc, removing(sock_path, sock_path + ".lock"):
|
||||||
yield proc, sock_path
|
yield proc, sock_path
|
||||||
|
|
||||||
|
|
@ -469,6 +468,21 @@ def connect_ch_vsock(
|
||||||
yield s
|
yield s
|
||||||
|
|
||||||
|
|
||||||
|
BYTES_PATTERN = re.compile(r"^([0-9]+)([MmGgKk]?)$")
|
||||||
|
BYTES_UNITS = {
|
||||||
|
"k": 1024,
|
||||||
|
"m": 1048576,
|
||||||
|
"g": 1024 * 1048576,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def parse_bytes(s):
|
||||||
|
m = BYTES_PATTERN.match(s)
|
||||||
|
assert m, s
|
||||||
|
size, unit = m.groups()
|
||||||
|
return int(size) * BYTES_UNITS.get(unit.lower(), 1)
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def listen_ch_vsock(
|
def listen_ch_vsock(
|
||||||
vsock_sock_path,
|
vsock_sock_path,
|
||||||
|
|
@ -564,6 +578,16 @@ def main(args, args_next, cleanup, ps):
|
||||||
)
|
)
|
||||||
virtiofs_socks.append(("home", sock_path))
|
virtiofs_socks.append(("home", sock_path))
|
||||||
config["payload"]["cmdline"] += " uvms.persist-home=1"
|
config["payload"]["cmdline"] += " uvms.persist-home=1"
|
||||||
|
if args.mem is not None:
|
||||||
|
config["memory"]["size"] = parse_bytes(args.mem)
|
||||||
|
config["memory"]["hotplug_size"] = parse_bytes(args.mem)
|
||||||
|
|
||||||
|
if "platform" not in config:
|
||||||
|
config["platform"] = {}
|
||||||
|
config["platform"]["oem_strings"] = [
|
||||||
|
"io.systemd.credential:vmm.notify_socket=vsock-stream:2:8888",
|
||||||
|
*config["platform"].get("oem_strings", []),
|
||||||
|
]
|
||||||
|
|
||||||
gpud, gpud_path = cleanup.enter_context(ps.start_gpu())
|
gpud, gpud_path = cleanup.enter_context(ps.start_gpu())
|
||||||
|
|
||||||
|
|
@ -590,21 +614,39 @@ def main(args, args_next, cleanup, ps):
|
||||||
ps.exec(*ch_remote, "boot")
|
ps.exec(*ch_remote, "boot")
|
||||||
ps.exec(*ch_remote, "info")
|
ps.exec(*ch_remote, "info")
|
||||||
|
|
||||||
|
ready = False
|
||||||
with ready_sock:
|
with ready_sock:
|
||||||
ready_sock.settimeout(20.0)
|
ready_sock.settimeout(8.0)
|
||||||
|
for _ in range(1048576):
|
||||||
|
if ready:
|
||||||
|
break
|
||||||
try:
|
try:
|
||||||
con, _ = ready_sock.accept()
|
con, _ = ready_sock.accept()
|
||||||
except: # noqa: E722
|
except: # noqa: E722
|
||||||
print(
|
print(
|
||||||
"CH didn't try connecting to the readiness notification socket"
|
"WARNING: CH didn't try connecting to the readiness notification socket" # noqa: E501
|
||||||
) # noqa: E501
|
)
|
||||||
|
ready = True
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
with con:
|
with con:
|
||||||
msg = con.recv(128)
|
msg = con.recv(1024)
|
||||||
assert msg.startswith(b"READY=1"), msg
|
for ln in msg.split(b"\n"):
|
||||||
|
ln = ln.strip()
|
||||||
|
print(ln)
|
||||||
|
# if ln.startswith(b"X_SYSTEMD_UNIT_ACTIVE=uvms-guest.service"): # noqa: E501
|
||||||
|
if ln.startswith(b"READY=1"): # noqa: E501
|
||||||
|
ready = True
|
||||||
|
break
|
||||||
|
|
||||||
|
assert ready
|
||||||
|
|
||||||
with connect_ch_vsock(ps.prefix + "/vsock.sock", 24601) as guest:
|
with connect_ch_vsock(ps.prefix + "/vsock.sock", 24601) as guest:
|
||||||
for r in args.run:
|
for r in args.run:
|
||||||
|
res = {}
|
||||||
|
for _ in range(1):
|
||||||
|
if "status" in res:
|
||||||
|
break
|
||||||
try:
|
try:
|
||||||
guest.send(
|
guest.send(
|
||||||
json.dumps(
|
json.dumps(
|
||||||
|
|
@ -622,16 +664,21 @@ def main(args, args_next, cleanup, ps):
|
||||||
try:
|
try:
|
||||||
res = json.loads(guest.recv(8192))
|
res = json.loads(guest.recv(8192))
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
print(f"Couldn't interpret --run {r} response: {e} {res}")
|
print(
|
||||||
continue
|
f"Couldn't interpret --run {r} response: {e} {res}"
|
||||||
|
) # noqa: E501
|
||||||
|
res = {}
|
||||||
|
# res = {"status": "failed"}
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Couldn't --run {r}: {repr(e)}")
|
||||||
|
if "status" not in res:
|
||||||
|
res["status"] = "fail"
|
||||||
adverb = (
|
adverb = (
|
||||||
"Successfully"
|
"Successfully"
|
||||||
if res["status"] == "exec succeeded"
|
if res["status"] == "exec succeeded"
|
||||||
else "Failed to" # noqa: E501
|
else "Failed to" # noqa: E501
|
||||||
)
|
)
|
||||||
print(f"{adverb} --run {r}: {res}")
|
print(f"{adverb} --run {r}: {res}")
|
||||||
except Exception as e:
|
|
||||||
print(f"Couldn't --run {r}: {repr(e)}")
|
|
||||||
try:
|
try:
|
||||||
ch.wait()
|
ch.wait()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
|
|
||||||
21
pkgs/uvmslib/package.nix
Normal file
21
pkgs/uvmslib/package.nix
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
python3Packages,
|
||||||
|
}:
|
||||||
|
python3Packages.buildPythonPackage {
|
||||||
|
pname = "uvmslib";
|
||||||
|
version = "0.0.0";
|
||||||
|
pyproject = true;
|
||||||
|
src =
|
||||||
|
let
|
||||||
|
fs = lib.fileset;
|
||||||
|
in
|
||||||
|
fs.toSource {
|
||||||
|
root = ./.;
|
||||||
|
fileset = fs.unions [
|
||||||
|
./pyproject.toml
|
||||||
|
./uvmslib.py
|
||||||
|
];
|
||||||
|
};
|
||||||
|
build-system = [ python3Packages.setuptools ];
|
||||||
|
}
|
||||||
10
pkgs/uvmslib/pyproject.toml
Normal file
10
pkgs/uvmslib/pyproject.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[build-system]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
requires = [ "setuptools" ]
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "uvms"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
"uvms-guest" = "uvmslib:guest_main"
|
||||||
|
|
@ -59,7 +59,7 @@ class Processes:
|
||||||
proc = None
|
proc = None
|
||||||
try:
|
try:
|
||||||
proc = self.popen(
|
proc = self.popen(
|
||||||
req["run"]["argv"],
|
run["argv"],
|
||||||
text=text,
|
text=text,
|
||||||
env=env,
|
env=env,
|
||||||
cwd="/home/user",
|
cwd="/home/user",
|
||||||
|
|
@ -82,14 +82,14 @@ class Processes:
|
||||||
return res, proc
|
return res, proc
|
||||||
|
|
||||||
def accept_vsock(self, s):
|
def accept_vsock(self, s):
|
||||||
con, (cid, port) = serv.accept()
|
con, (cid, port) = s.accept()
|
||||||
assert cid == 2, cid
|
assert cid == 2, cid
|
||||||
self.sources.append(con)
|
self.sources.append(con)
|
||||||
self.client_fds.add(con.fileno())
|
self.client_fds.add(con.fileno())
|
||||||
return con, (cid, port)
|
return con, (cid, port)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def guest_main():
|
||||||
ps = Processes()
|
ps = Processes()
|
||||||
serv = socket.fromfd(3, socket.AF_VSOCK, socket.SOCK_STREAM)
|
serv = socket.fromfd(3, socket.AF_VSOCK, socket.SOCK_STREAM)
|
||||||
ps.sources.append(serv)
|
ps.sources.append(serv)
|
||||||
|
|
@ -134,3 +134,7 @@ if __name__ == "__main__":
|
||||||
con.send(res)
|
con.send(res)
|
||||||
else:
|
else:
|
||||||
assert False, con.fileno()
|
assert False, con.fileno()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
guest_main()
|
||||||
|
|
@ -42,7 +42,7 @@ in
|
||||||
_module.args.uvmsPkgs = lib.mkDefault (pkgs.callPackage ../pkgs { });
|
_module.args.uvmsPkgs = lib.mkDefault (pkgs.callPackage ../pkgs { });
|
||||||
# some.failure-handler.enable = true;
|
# some.failure-handler.enable = true;
|
||||||
hardware.graphics.enable = true;
|
hardware.graphics.enable = true;
|
||||||
boot.kernelPackages = pkgs.linuxPackagesFor uvmsPkgs.linux-uvm;
|
# boot.kernelPackages = pkgs.linuxPackagesFor uvmsPkgs.linux-uvm;
|
||||||
# boot.isContainer = true;
|
# boot.isContainer = true;
|
||||||
boot.initrd.kernelModules = [
|
boot.initrd.kernelModules = [
|
||||||
"drm"
|
"drm"
|
||||||
|
|
@ -251,19 +251,20 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.sockets."uvms-guest" = {
|
systemd.sockets."uvms-guest" = {
|
||||||
wantedBy = [ "default.target" ];
|
requiredBy = [ "multi-user.target" ];
|
||||||
|
before = [ "multi-user.target" ];
|
||||||
listenStreams = [
|
listenStreams = [
|
||||||
"vsock::24601"
|
"vsock::24601"
|
||||||
];
|
];
|
||||||
partOf = [ "uvms-guest.service" ];
|
partOf = [ "uvms-guest.service" ];
|
||||||
};
|
};
|
||||||
systemd.services."uvms-guest" = {
|
systemd.services."uvms-guest" = {
|
||||||
requiredBy = [ "multi-user.target" ];
|
before = [ "multi-user.target" ];
|
||||||
onFailure = [ "shutdown.service" ];
|
onFailure = [ "shutdown.service" ];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
User = "user";
|
User = "user";
|
||||||
Group = "users";
|
Group = "users";
|
||||||
ExecStart = "${lib.getExe uvmsPkgs.uvms-guest}";
|
ExecStart = "${lib.getExe' uvmsPkgs.uvmslib "uvms-guest"}";
|
||||||
ExecStop = [
|
ExecStop = [
|
||||||
"/run/current-system/sw/bin/echo GUEST DOWN"
|
"/run/current-system/sw/bin/echo GUEST DOWN"
|
||||||
"/run/current-system/sw/bin/systemctl poweroff"
|
"/run/current-system/sw/bin/systemctl poweroff"
|
||||||
|
|
@ -288,7 +289,6 @@ in
|
||||||
"console=ttyS0"
|
"console=ttyS0"
|
||||||
"reboot=t"
|
"reboot=t"
|
||||||
"panic=-1"
|
"panic=-1"
|
||||||
"io.systemd.credential:vmm.notify_socket=vsock-stream:2:8888"
|
|
||||||
# "rootfstype=virtiofs"
|
# "rootfstype=virtiofs"
|
||||||
# "root=rootstore"
|
# "root=rootstore"
|
||||||
];
|
];
|
||||||
|
|
@ -301,20 +301,25 @@ in
|
||||||
};
|
};
|
||||||
uvms.ch.settings = mkOption {
|
uvms.ch.settings = mkOption {
|
||||||
default = { };
|
default = { };
|
||||||
type = types.submodule {
|
type = types.submodule (
|
||||||
|
let
|
||||||
|
osConfig = config;
|
||||||
|
in
|
||||||
|
{ config, ... }:
|
||||||
|
{
|
||||||
freeformType = jsonType;
|
freeformType = jsonType;
|
||||||
options = {
|
options = {
|
||||||
payload = {
|
payload = {
|
||||||
cmdline = mkOption {
|
cmdline = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = concatStringsSep " " (
|
default = concatStringsSep " " (
|
||||||
config.boot.kernelParams
|
osConfig.boot.kernelParams
|
||||||
++ [
|
++ [
|
||||||
# "init=${lib.removePrefix "/nix/store" "${config.system.build.toplevel}"}/init"
|
# "init=${lib.removePrefix "/nix/store" "${osConfig.system.build.toplevel}"}/init"
|
||||||
"init=${config.system.build.toplevel}/init"
|
"init=${osConfig.system.build.toplevel}/init"
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
defaultText = ''concatStringsSep " " ${config.boot.kernelParams}'';
|
defaultText = ''concatStringsSep " " ${osConfig.boot.kernelParams}'';
|
||||||
};
|
};
|
||||||
kernel = mkOption {
|
kernel = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
|
|
@ -385,7 +390,7 @@ in
|
||||||
options = {
|
options = {
|
||||||
size = mkOption {
|
size = mkOption {
|
||||||
type = types.int;
|
type = types.int;
|
||||||
default = 3 * 1024 * 1048576;
|
default = 4 * 1024 * 1048576;
|
||||||
};
|
};
|
||||||
shared = mkOption {
|
shared = mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
|
|
@ -395,6 +400,25 @@ in
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = true;
|
default = true;
|
||||||
};
|
};
|
||||||
|
hotplug_method = mkOption {
|
||||||
|
default = "VirtioMem";
|
||||||
|
type = types.enum [
|
||||||
|
"Acpi"
|
||||||
|
"VirtioMem"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
hotplugged_size = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
default = 512 * 1048576;
|
||||||
|
};
|
||||||
|
hotplug_size = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
default = config.memory.size;
|
||||||
|
};
|
||||||
|
# hugepages = mkOption {
|
||||||
|
# type = types.bool;
|
||||||
|
# default = true;
|
||||||
|
# };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -415,7 +439,8 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue