@@ -27,18 +27,17 @@ use tinytemplate::{format_unescaped, TinyTemplate};
2727use crate :: output:: Output ;
2828use crate :: qga:: QgaWrapper ;
2929use crate :: util:: gen_sock;
30+ use crate :: virtiofsd:: Virtiofsd ;
3031use crate :: { Mount , Target , VMConfig } ;
3132
3233const INIT_TEMPLATE : & str = include_str ! ( "init/init.sh.template" ) ;
3334const COMMAND_TEMPLATE : & str = include_str ! ( "init/command.template" ) ;
34- // Needs to be `/dev/root` for kernel to "find" the 9pfs as rootfs
35- const ROOTFS_9P_FS_MOUNT_TAG : & str = "/dev/root" ;
36- const SHARED_9P_FS_MOUNT_TAG : & str = "vmtest-shared" ;
35+ const ROOT_FS_MOUNT_TAG : & str = "rootfs" ;
36+ const SHARED_FS_MOUNT_TAG : & str = "vmtest-shared" ;
3737const COMMAND_OUTPUT_PORT_NAME : & str = "org.qemu.virtio_serial.0" ;
3838const MAGIC_INTERACTIVE_COMMAND : & str = "-" ;
3939
40- const SHARED_9P_FS_MOUNT_PATH : & str = "/mnt/vmtest" ;
41- const MOUNT_OPTS_9P_FS : & str = "trans=virtio,cache=mmap,msize=1048576" ;
40+ const SHARED_FS_MOUNT_PATH : & str = "/mnt/vmtest" ;
4241const OVMF_PATHS : & [ & str ] = & [
4342 // Fedora
4443 "/usr/share/edk2/ovmf/OVMF_CODE.fd" ,
@@ -55,6 +54,8 @@ type QmpUnixStream = qapi::Stream<BufReader<UnixStream>, UnixStream>;
5554/// Represents a single QEMU instance
5655pub struct Qemu {
5756 process : Command ,
57+ /// `virtiofsd` instances for each of the mounts in use.
58+ virtiofsds : Vec < Virtiofsd > ,
5859 qga_sock : PathBuf ,
5960 qmp_sock : PathBuf ,
6061 command : String ,
@@ -241,6 +242,18 @@ fn guest_agent_args(sock: &Path) -> Vec<OsString> {
241242 args
242243}
243244
245+ /// Generate general arguments necessary for working with `virtiofs`.
246+ fn virtiofs_general_args ( vm : & VMConfig ) -> Vec < OsString > {
247+ let mut args: Vec < OsString > = Vec :: new ( ) ;
248+
249+ args. push ( "-object" . into ( ) ) ;
250+ args. push ( format ! ( "memory-backend-memfd,id=mem,share=on,size={}" , vm. memory. as_str( ) ) . into ( ) ) ;
251+ args. push ( "-numa" . into ( ) ) ;
252+ args. push ( "node,memdev=mem" . into ( ) ) ;
253+
254+ args
255+ }
256+
244257/// Generate arguments for full KVM virtualization if host supports it
245258fn kvm_args ( arch : & str ) -> Vec < & ' static str > {
246259 let mut args = Vec :: new ( ) ;
@@ -291,30 +304,17 @@ fn machine_protocol_args(sock: &Path) -> Vec<OsString> {
291304 args
292305}
293306
294- /// Generate arguments for setting up 9p FS server on host
307+ /// Generate per-file-system arguments necessary for working with `virtiofs`.
295308///
296- /// `id` is the ID for the FS export (currently unused AFAICT)
309+ /// `id` is the ID for the FS export
297310/// `mount_tag` is used inside guest to find the export
298- fn plan9_fs_args ( host_shared : & Path , id : & str , mount_tag : & str , ro : bool ) -> Vec < OsString > {
311+ fn virtiofs_per_fs_args ( virtiofsd : & Virtiofsd , id : & str , mount_tag : & str ) -> Vec < OsString > {
299312 let mut args: Vec < OsString > = Vec :: new ( ) ;
300313
301- args. push ( "-virtfs" . into ( ) ) ;
302-
303- let mut arg = OsString :: new ( ) ;
304- arg. push ( format ! ( "local,id={id},path=" ) ) ;
305- arg. push ( if host_shared. as_os_str ( ) . is_empty ( ) {
306- // This case occurs when the config file path is just "vmtest.toml"
307- Path :: new ( "." )
308- } else {
309- host_shared
310- } ) ;
311- arg. push ( format ! (
312- ",mount_tag={mount_tag},security_model=none,multidevs=remap"
313- ) ) ;
314- if ro {
315- arg. push ( ",readonly=on" )
316- }
317- args. push ( arg) ;
314+ args. push ( "-chardev" . into ( ) ) ;
315+ args. push ( format ! ( "socket,id={id},path={}" , virtiofsd. socket_path( ) . display( ) ) . into ( ) ) ;
316+ args. push ( "-device" . into ( ) ) ;
317+ args. push ( format ! ( "vhost-user-fs-pci,queue-size=1024,chardev={id},tag={mount_tag}" ) . into ( ) ) ;
318318
319319 args
320320}
@@ -371,9 +371,9 @@ fn kernel_args(
371371 // The guest kernel command line args
372372 let mut cmdline: Vec < OsString > = Vec :: new ( ) ;
373373
374- // Tell kernel the rootfs is 9p
375- cmdline. push ( "rootfstype=9p " . into ( ) ) ;
376- cmdline. push ( format ! ( "rootflags={}" , MOUNT_OPTS_9P_FS ) . into ( ) ) ;
374+ // Tell kernel the rootfs is on a virtiofs and what "tag" it uses.
375+ cmdline. push ( "rootfstype=virtiofs " . into ( ) ) ;
376+ cmdline. push ( format ! ( "root={ROOT_FS_MOUNT_TAG}" ) . into ( ) ) ;
377377
378378 // Mount rootfs readable/writable to make experience more smooth.
379379 // Lots of tools expect to be able to write logs or change global
@@ -455,16 +455,6 @@ fn vmconfig_args(vm: &VMConfig) -> Vec<OsString> {
455455 vm. memory. clone( ) . into( ) ,
456456 ] ;
457457
458- for mount in vm. mounts . values ( ) {
459- let name = format ! ( "mount{}" , hash( & mount. host_path) ) ;
460- args. append ( & mut plan9_fs_args (
461- & mount. host_path ,
462- & name,
463- & name,
464- !mount. writable ,
465- ) ) ;
466- }
467-
468458 let mut extra_args = vm
469459 . extra_args
470460 . clone ( )
@@ -650,6 +640,7 @@ impl Qemu {
650640 let command_sock = gen_sock ( "cmdout" ) ;
651641 let ( init, guest_init) = gen_init ( & target. rootfs ) . context ( "Failed to generate init" ) ?;
652642
643+ let mut virtiofsds = Vec :: new ( ) ;
653644 let mut c = Command :: new ( format ! ( "qemu-system-{}" , target. arch) ) ;
654645
655646 c. args ( QEMU_DEFAULT_ARGS )
@@ -660,6 +651,7 @@ impl Qemu {
660651 . args ( machine_args ( & target. arch ) )
661652 . args ( machine_protocol_args ( & qmp_sock) )
662653 . args ( guest_agent_args ( & qga_sock) )
654+ . args ( virtiofs_general_args ( & target. vm ) )
663655 . args ( virtio_serial_args ( & command_sock) ) ;
664656 // Always ensure the rootfs is first.
665657 if let Some ( image) = & target. image {
@@ -668,28 +660,42 @@ impl Qemu {
668660 c. args ( uefi_firmware_args ( target. vm . bios . as_deref ( ) ) ) ;
669661 }
670662 } else if let Some ( kernel) = & target. kernel {
671- c. args ( plan9_fs_args (
672- target. rootfs . as_path ( ) ,
663+ let virtiofsd = Virtiofsd :: new ( target. rootfs . as_path ( ) ) ?;
664+ c. args ( virtiofs_per_fs_args (
665+ & virtiofsd,
673666 "root" ,
674- ROOTFS_9P_FS_MOUNT_TAG ,
675- false ,
667+ ROOT_FS_MOUNT_TAG ,
676668 ) ) ;
677669 c. args ( kernel_args (
678670 kernel,
679671 & target. arch ,
680672 guest_init. as_path ( ) ,
681673 target. kernel_args . as_ref ( ) ,
682674 ) ) ;
675+ virtiofsds. push ( virtiofsd) ;
683676 } else {
684677 panic ! ( "Config validation should've enforced XOR" ) ;
685678 }
679+
686680 // Now add the shared mount and other extra mounts.
687- c. args ( plan9_fs_args (
688- host_shared,
681+ let virtiofsd = Virtiofsd :: new ( host_shared) ?;
682+ c. args ( virtiofs_per_fs_args (
683+ & virtiofsd,
689684 "shared" ,
690- SHARED_9P_FS_MOUNT_TAG ,
691- false ,
685+ SHARED_FS_MOUNT_TAG ,
692686 ) ) ;
687+ virtiofsds. push ( virtiofsd) ;
688+
689+ for mount in target. vm . mounts . values ( ) {
690+ let name = format ! ( "mount{}" , hash( & mount. host_path) ) ;
691+ let virtiofsd = Virtiofsd :: new ( & mount. host_path ) ?;
692+ c. args ( virtiofs_per_fs_args (
693+ & virtiofsd,
694+ & name,
695+ & name,
696+ ) ) ;
697+ virtiofsds. push ( virtiofsd) ;
698+ }
693699 c. args ( vmconfig_args ( & target. vm ) ) ;
694700
695701 if log_enabled ! ( Level :: Error ) {
@@ -706,6 +712,7 @@ impl Qemu {
706712
707713 let mut qemu = Self {
708714 process : c,
715+ virtiofsds,
709716 qga_sock,
710717 qmp_sock,
711718 command : target. command . to_string ( ) ,
@@ -838,16 +845,18 @@ impl Qemu {
838845 // We can race with VM/qemu coming up. So retry a few times with growing backoff.
839846 let mut rc = 0 ;
840847 for i in 0 ..5 {
841- let mount_opts = if ro {
842- format ! ( "{},ro" , MOUNT_OPTS_9P_FS )
843- } else {
844- MOUNT_OPTS_9P_FS . into ( )
845- } ;
848+ let mut args = vec ! [
849+ "-t" , "virtiofs" , mount_tag, guest_path
850+ ] ;
851+ if ro {
852+ args. push ( "-oro" )
853+ }
854+
846855 rc = run_in_vm (
847856 qga,
848857 & output_fn,
849858 "mount" ,
850- & [ "-t" , "9p" , "-o" , & mount_opts , mount_tag , guest_path ] ,
859+ & args ,
851860 false ,
852861 None ,
853862 ) ?;
@@ -1052,7 +1061,7 @@ impl Qemu {
10521061 // Mount shared directory inside guest
10531062 let _ = self . updates . send ( Output :: SetupStart ) ;
10541063 if let Err ( e) =
1055- self . mount_in_guest ( qga, SHARED_9P_FS_MOUNT_PATH , SHARED_9P_FS_MOUNT_TAG , false )
1064+ self . mount_in_guest ( qga, SHARED_FS_MOUNT_PATH , SHARED_FS_MOUNT_TAG , false )
10561065 {
10571066 return Err ( e) . context ( "Failed to mount shared directory in guest" ) ;
10581067 }
@@ -1075,6 +1084,20 @@ impl Qemu {
10751084 /// Errors and return status are reported through the `updates` channel passed into the
10761085 /// constructor.
10771086 pub fn run ( mut self ) {
1087+ let _ = self . updates . send ( Output :: InitializeStart ) ;
1088+ for virtiofsd in self . virtiofsds . iter_mut ( ) {
1089+ match virtiofsd. launch ( ) {
1090+ Ok ( ( ) ) => ( ) ,
1091+ Err ( e) => {
1092+ let _ = self . updates . send ( Output :: InitializeEnd ( Err ( e) ) ) ;
1093+ return ;
1094+ }
1095+ }
1096+ }
1097+
1098+ // TODO: Wait for all sockets to appear as well.
1099+ let _ = self . updates . send ( Output :: InitializeEnd ( Ok ( ( ) ) ) ) ;
1100+
10781101 // Start QEMU
10791102 let ( mut child, qga, mut qmp) = match self . boot_vm ( ) {
10801103 Ok ( ( c, qga, qmp) ) => ( c, qga, qmp) ,
0 commit comments