diff --git a/vmrunner/vm.schema.json b/vmrunner/vm.schema.json index 8153faaa5..ddee20511 100644 --- a/vmrunner/vm.schema.json +++ b/vmrunner/vm.schema.json @@ -80,6 +80,52 @@ } }, + "virtiocon" : { + "description" : "VirtioCON device. Only used for testing Virtio queues for now", + "type" : "object", + "properties" : { + "path" : { + "description" : "The path to redirect the guest output", + "type" : "string" + } + }, + + "required" : ["path"] + }, + + "virtiofs" : { + "description" : "VirtioFS device", + "type" : "object", + "properties" : { + "shared" : { + "description" : "Directory to be shared with guest", + "type" : "string" + } + }, + + "required" : ["shared"] + }, + + "virtiopmem": { + "description" : "VirtioPMEM devices", + "type" : "array", + "items" : { + "description" : "Device configuration", + "type" : "object", + "properties" : { + "image" : { + "description" : "Path to persistent image file", + "type" : "string" + }, + "size" : { + "description" : "Persistent memory size in megabytes", + "type" : "number" + } + } + }, + + "required" : ["image", "size"] + }, "mem" : { "description" : "Amount of memory in megabytes", diff --git a/vmrunner/vmrunner.py b/vmrunner/vmrunner.py index 27237cc1c..5a167a74b 100755 --- a/vmrunner/vmrunner.py +++ b/vmrunner/vmrunner.py @@ -20,6 +20,7 @@ import re import traceback import signal +import tempfile from enum import Enum import platform import psutil @@ -184,8 +185,9 @@ def __init__(self, config): self._config = config self._allow_sudo = False # must be explicitly turned on at boot. self._enable_kvm = False # must be explicitly turned on at boot. - self._sudo = False # Set to true if sudo is available - self._proc = None # A running subprocess + self._sudo = False # Set to true if sudo is available + self._proc = None # A running subprocess + self._tmp_dirs = [] # A list of tmp dirs created using tempfile module. Used for socket creation for automatic cleanup and garbage collection # pylint: disable-next=unused-argument def boot_in_hypervisor(self, multiboot=False, debug=False, kernel_args="", image_name="", allow_sudo = False, enable_kvm = False): @@ -418,6 +420,7 @@ class qemu(hypervisor): def __init__(self, config): super().__init__(config) self._proc = None + self._virtiofsd_proc = None self._stopped = False self._sudo = False self._image_name = self._config if "image" in self._config else self.name() + " vm" @@ -516,6 +519,41 @@ def net_arg(self, backend, device, if_name = "net0", mac = None, bridge = None, return ["-device", device, "-netdev", netdev] + def init_virtiocon(self, path): + """ creates a console device and redirects to the path given """ + qemu_args = ["-device", "virtio-serial-pci,disable-legacy=on,id=virtio-serial0"] + qemu_args += ["-device", "virtserialport,chardev=virtiocon0"] + qemu_args += ["-chardev", f"file,id=virtiocon0,path={path}"] + + return qemu_args + + def init_virtiofs(self, socket, shared, mem): + """ initializes virtiofs by launching virtiofsd and creating a virtiofs device """ + virtiofsd_args = ["virtiofsd", "--socket", socket, "--shared-dir", shared, "--sandbox", "none"] + self._virtiofsd_proc = subprocess.Popen(virtiofsd_args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) # pylint: disable=consider-using-with + + if self._virtiofsd_proc.poll(): + raise Exception("VirtioFSD failed to start") + + info("Successfully started VirtioFSD!") + + while not os.path.exists(socket): + ... + + qemu_args = ["-machine", "memory-backend=mem0"] + qemu_args += ["-chardev", f"socket,id=virtiofsd0,path={socket}"] + qemu_args += ["-device", "vhost-user-fs-pci,chardev=virtiofsd0,tag=vfs"] + qemu_args += ["-object", f"memory-backend-memfd,id=mem0,size={mem}M,share=on"] + + return qemu_args + + def init_pmem(self, path, size, pmem_id): + """ creates a pmem device with image path as memory mapped backend """ + qemu_args = ["-object", f"memory-backend-file,id=pmemdev{pmem_id},mem-path={path},size={size}M,share=on"] + qemu_args += ["-device", f"virtio-pmem-pci,memdev=pmemdev{pmem_id}"] + + return qemu_args + def kvm_present(self): """ returns true if KVM is present and available """ if not self._enable_kvm: @@ -660,7 +698,7 @@ def boot_in_hypervisor(self, multiboot=True, debug = False, kernel_args = "", im mem_arg = [] if "mem" in self._config: - mem_arg = ["-m", str(self._config["mem"])] + mem_arg = ["-m", f"size={self._config["mem"]},maxmem=1000G"] vga_arg = ["-nographic" ] if "vga" in self._config: @@ -674,6 +712,27 @@ def boot_in_hypervisor(self, multiboot=True, debug = False, kernel_args = "", im if "vfio" in self._config: pci_arg = ["-device", "vfio-pci,host=" + self._config["vfio"]] + virtiocon_args = [] + if "virtiocon" in self._config: + virtiocon_args = self.init_virtiocon(self._config["virtiocon"]["path"]) + + virtiofs_args = [] + if "virtiofs" in self._config: + tmp_virtiofs_dir = tempfile.TemporaryDirectory(prefix="virtiofs-") # pylint: disable=consider-using-with + self._tmp_dirs.append(tmp_virtiofs_dir) + socket_path = os.path.join(tmp_virtiofs_dir.name, "virtiofsd.sock") + + shared = self._config["virtiofs"]["shared"] + + virtiofs_args = self.init_virtiofs(socket_path, shared, self._config["mem"]) + + virtiopmem_args = [] + if "virtiopmem" in self._config: + for pmem_id, virtiopmem in enumerate(self._config["virtiopmem"]): + image = virtiopmem["image"] + size = virtiopmem["size"] + virtiopmem_args += self.init_pmem(image, size, pmem_id) + # custom qemu binary/location qemu_binary = "qemu-system-x86_64" if "qemu" in self._config: @@ -703,7 +762,8 @@ def boot_in_hypervisor(self, multiboot=True, debug = False, kernel_args = "", im command += kernel_args command += disk_args + debug_args + net_args + mem_arg + mod_args - command += vga_arg + trace_arg + pci_arg + command += vga_arg + trace_arg + pci_arg + virtiocon_args + virtiofs_args + command += virtiopmem_args #command_str = " ".join(command) #command_str.encode('ascii','ignore')