@@ -2,6 +2,7 @@ package qemu
22
33import (
44 "bytes"
5+ "encoding/json"
56 "errors"
67 "fmt"
78 "io/fs"
@@ -478,6 +479,12 @@ func Cmdline(cfg Config) (string, []string, error) {
478479 memBytes = adjustMemBytesDarwinARM64HVF (memBytes , accel , features )
479480 args = appendArgsIfNoConflict (args , "-m" , strconv .Itoa (int (memBytes >> 20 )))
480481
482+ if * y .MountType == limayaml .VIRTIOFS {
483+ args = appendArgsIfNoConflict (args , "-object" ,
484+ fmt .Sprintf ("memory-backend-file,id=virtiofs-shm,size=%s,mem-path=/dev/shm,share=on" , strconv .Itoa (int (memBytes ))))
485+ args = appendArgsIfNoConflict (args , "-numa" , "node,memdev=virtiofs-shm" )
486+ }
487+
481488 // CPU
482489 cpu := y .CPUType [* y .Arch ]
483490 if runtime .GOOS == "darwin" && runtime .GOARCH == "amd64" {
@@ -775,7 +782,7 @@ func Cmdline(cfg Config) (string, []string, error) {
775782
776783 // We also want to enable vsock here, but QEMU does not support vsock for macOS hosts
777784
778- if * y .MountType == limayaml .NINEP {
785+ if * y .MountType == limayaml .NINEP || * y . MountType == limayaml . VIRTIOFS {
779786 for i , f := range y .Mounts {
780787 tag := fmt .Sprintf ("mount%d" , i )
781788 location , err := localpathutil .Expand (f .Location )
@@ -785,14 +792,30 @@ func Cmdline(cfg Config) (string, []string, error) {
785792 if err := os .MkdirAll (location , 0755 ); err != nil {
786793 return "" , nil , err
787794 }
788- options := "local"
789- options += fmt .Sprintf (",mount_tag=%s" , tag )
790- options += fmt .Sprintf (",path=%s" , location )
791- options += fmt .Sprintf (",security_model=%s" , * f .NineP .SecurityModel )
792- if ! * f .Writable {
793- options += ",readonly"
795+
796+ switch * y .MountType {
797+ case limayaml .NINEP :
798+ options := "local"
799+ options += fmt .Sprintf (",mount_tag=%s" , tag )
800+ options += fmt .Sprintf (",path=%s" , location )
801+ options += fmt .Sprintf (",security_model=%s" , * f .NineP .SecurityModel )
802+ if ! * f .Writable {
803+ options += ",readonly"
804+ }
805+ args = append (args , "-virtfs" , options )
806+ case limayaml .VIRTIOFS :
807+ // Note that read-only mode is not supported on the QEMU/virtiofsd side yet:
808+ // https://gitlab.com/virtio-fs/virtiofsd/-/issues/97
809+ chardev := fmt .Sprintf ("char-virtiofs-%d" , i )
810+ vhostSock := filepath .Join (cfg .InstanceDir , fmt .Sprintf (filenames .VhostSock , i ))
811+ args = append (args , "-chardev" , fmt .Sprintf ("socket,id=%s,path=%s" , chardev , vhostSock ))
812+
813+ options := "vhost-user-fs-pci"
814+ options += fmt .Sprintf (",queue-size=%d" , * f .Virtiofs .QueueSize )
815+ options += fmt .Sprintf (",chardev=%s" , chardev )
816+ options += fmt .Sprintf (",tag=%s" , tag )
817+ args = append (args , "-device" , options )
794818 }
795- args = append (args , "-virtfs" , options )
796819 }
797820 }
798821
@@ -812,6 +835,99 @@ func Cmdline(cfg Config) (string, []string, error) {
812835 return exe , args , nil
813836}
814837
838+ func FindVirtiofsd (qemuExe string ) (string , error ) {
839+ type vhostUserBackend struct {
840+ BackendType string `json:"type"`
841+ Binary string `json:"binary"`
842+ }
843+
844+ currentUser , err := user .Current ()
845+ if err != nil {
846+ return "" , err
847+ }
848+
849+ const relativePath = "share/qemu/vhost-user"
850+
851+ binDir := filepath .Dir (qemuExe ) // "/usr/local/bin"
852+ usrDir := filepath .Dir (binDir ) // "/usr/local"
853+ userLocalDir := filepath .Join (currentUser .HomeDir , ".local" ) // "$HOME/.local"
854+
855+ candidates := []string {
856+ filepath .Join (userLocalDir , relativePath ),
857+ filepath .Join (usrDir , relativePath ),
858+ }
859+
860+ if usrDir != "/usr" {
861+ candidates = append (candidates , filepath .Join ("/usr" , relativePath ))
862+ }
863+
864+ for _ , vhostConfigsDir := range candidates {
865+ logrus .Debugf ("Checking vhost directory %s" , vhostConfigsDir )
866+
867+ configEntries , err := os .ReadDir (vhostConfigsDir )
868+ if err != nil {
869+ logrus .Debugf ("Failed to list vhost directory: %v" , err )
870+ continue
871+ }
872+
873+ for _ , configEntry := range configEntries {
874+ logrus .Debugf ("Checking vhost config %s" , configEntry .Name ())
875+ if ! strings .HasSuffix (configEntry .Name (), ".json" ) {
876+ continue
877+ }
878+
879+ var config vhostUserBackend
880+ contents , err := os .ReadFile (filepath .Join (vhostConfigsDir , configEntry .Name ()))
881+ if err == nil {
882+ err = json .Unmarshal (contents , & config )
883+ }
884+
885+ if err != nil {
886+ logrus .Warnf ("Failed to load vhost-user config %s: %v" , configEntry .Name (), err )
887+ continue
888+ }
889+ logrus .Debugf ("%v" , config )
890+
891+ if config .BackendType != "fs" {
892+ continue
893+ }
894+
895+ // Only rust virtiofsd supports --version, so use that to make sure this isn't
896+ // QEMU's virtiofsd, which requires running as root.
897+ cmd := exec .Command (config .Binary , "--version" )
898+ output , err := cmd .CombinedOutput ()
899+ if err != nil {
900+ logrus .Warnf ("Failed to run %s --version (is this QEMU virtiofsd?): %s: %s" ,
901+ config .Binary , err , output )
902+ continue
903+ }
904+
905+ return config .Binary , nil
906+ }
907+ }
908+
909+ return "" , errors .New ("Failed to locate virtiofsd" )
910+ }
911+
912+ func VirtiofsdCmdline (cfg Config , mountIndex int ) ([]string , error ) {
913+ mount := cfg .LimaYAML .Mounts [mountIndex ]
914+ location , err := localpathutil .Expand (mount .Location )
915+ if err != nil {
916+ return nil , err
917+ }
918+
919+ vhostSock := filepath .Join (cfg .InstanceDir , fmt .Sprintf (filenames .VhostSock , mountIndex ))
920+ // qemu_driver has to wait for the socket to appear, so make sure any old ones are removed here.
921+ if err := os .Remove (vhostSock ); err != nil && ! errors .Is (err , fs .ErrNotExist ) {
922+ logrus .Warnf ("Failed to remove old vhost socket: %v" , err )
923+ }
924+
925+ return []string {
926+ "--socket-path" , vhostSock ,
927+ "--shared-dir" , location ,
928+ }, nil
929+ }
930+
815931func getExe (arch limayaml.Arch ) (string , []string , error ) {
816932 exeBase := "qemu-system-" + arch
817933 var args []string
0 commit comments