diff --git a/src/cmd/linuxkit/run_qemu.go b/src/cmd/linuxkit/run_qemu.go index 70ea91639..7a7066264 100644 --- a/src/cmd/linuxkit/run_qemu.go +++ b/src/cmd/linuxkit/run_qemu.go @@ -23,28 +23,30 @@ const ( // QemuConfig contains the config for Qemu type QemuConfig struct { - Path string - ISOBoot bool - UEFI bool - SquashFS bool - Kernel bool - GUI bool - Disks Disks - ISOImages []string - StatePath string - FWPath string - Arch string - CPUs string - Memory string - Accel string - Detached bool - QemuBinPath string - QemuImgPath string - PublishedPorts []string - NetdevConfig string - UUID uuid.UUID - USB bool - Devices []string + Path string + ISOBoot bool + UEFI bool + SquashFS bool + Kernel bool + GUI bool + Disks Disks + ISOImages []string + StatePath string + FWPath string + Arch string + CPUs string + Memory string + Accel string + Detached bool + QemuBinPath string + QemuImgPath string + PublishedPorts []string + NetdevConfig string + UUID uuid.UUID + USB bool + Devices []string + VirtiofsdBinPath string + VirtiofsShares []string } const ( @@ -119,23 +121,25 @@ func generateMAC() net.HardwareAddr { func runQEMUCmd() *cobra.Command { var ( - enableGUI bool - uefiBoot bool - isoBoot bool - squashFSBoot bool - kernelBoot bool - state string - data string - dataPath string - fw string - accel string - arch string - qemuCmd string - qemuDetached bool - networking string - usbEnabled bool - deviceFlags multipleFlag - publishFlags multipleFlag + enableGUI bool + uefiBoot bool + isoBoot bool + squashFSBoot bool + kernelBoot bool + state string + data string + dataPath string + fw string + accel string + arch string + qemuCmd string + qemuDetached bool + networking string + usbEnabled bool + deviceFlags multipleFlag + publishFlags multipleFlag + virtiofsdCmd string + virtiofsShares []string ) cmd := &cobra.Command{ @@ -279,34 +283,43 @@ func runQEMUCmd() *cobra.Command { } config := QemuConfig{ - Path: path, - ISOBoot: isoBoot, - UEFI: uefiBoot, - SquashFS: squashFSBoot, - Kernel: kernelBoot, - GUI: enableGUI, - Disks: disks, - ISOImages: isoPaths, - StatePath: state, - FWPath: fw, - Arch: arch, - CPUs: fmt.Sprintf("%d", cpus), - Memory: fmt.Sprintf("%d", mem), - Accel: accel, - Detached: qemuDetached, - QemuBinPath: qemuCmd, - PublishedPorts: publishFlags, - NetdevConfig: netdevConfig, - UUID: vmUUID, - USB: usbEnabled, - Devices: deviceFlags, + Path: path, + ISOBoot: isoBoot, + UEFI: uefiBoot, + SquashFS: squashFSBoot, + Kernel: kernelBoot, + GUI: enableGUI, + Disks: disks, + ISOImages: isoPaths, + StatePath: state, + FWPath: fw, + Arch: arch, + CPUs: fmt.Sprintf("%d", cpus), + Memory: fmt.Sprintf("%d", mem), + Accel: accel, + Detached: qemuDetached, + QemuBinPath: qemuCmd, + PublishedPorts: publishFlags, + NetdevConfig: netdevConfig, + UUID: vmUUID, + USB: usbEnabled, + Devices: deviceFlags, + VirtiofsdBinPath: virtiofsdCmd, + VirtiofsShares: virtiofsShares, } - config, err = discoverBinaries(config) + config, err = discoverQemu(config) if err != nil { return err } + if len(config.VirtiofsShares) > 0 { + config, err = discoverVirtiofsd(config) + if err != nil { + return err + } + } + if err = runQemuLocal(config); err != nil { return err } @@ -351,6 +364,10 @@ func runQEMUCmd() *cobra.Command { cmd.Flags().BoolVar(&usbEnabled, "usb", false, "Enable USB controller") cmd.Flags().Var(&deviceFlags, "device", "Add USB host device(s). Format driver[,prop=value][,...] -- add device, like -device on the qemu command line.") + // Filesystems + cmd.Flags().StringVar(&virtiofsdCmd, "virtiofsd", "", "Path to virtiofsd binary (otherwise look in /usr/lib/qemu and /usr/local/lib/qemu)") + cmd.Flags().StringArrayVar(&virtiofsShares, "virtiofs", []string{}, "Directory shared on virtiofs") + return cmd } @@ -398,6 +415,25 @@ func runQemuLocal(config QemuConfig) error { return fmt.Errorf("Detached mode is only supported when running in a container, not locally") } + if len(config.VirtiofsShares) > 0 { + args = append(args, "-object", "memory-backend-memfd,id=mem,size="+config.Memory+"M,share=on", "-numa", "node,memdev=mem") + } + for index, source := range config.VirtiofsShares { + socket := filepath.Join(config.StatePath, fmt.Sprintf("%s%d", "virtiofs", index)) + + cmd := exec.Command(config.VirtiofsdBinPath, + "--socket-path="+socket, + "-o", fmt.Sprintf("source=%s", source)) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Start(); err != nil { + return fmt.Errorf("virtiofs server cannot start: %v", err) + } + args = append(args, "-chardev", fmt.Sprintf("socket,id=char%d,path=%s", index, socket)) + args = append(args, "-device", + fmt.Sprintf("vhost-user-fs-pci,chardev=char%d,tag=virtiofs%d", index, index)) + } + qemuCmd := exec.Command(config.QemuBinPath, args...) // If verbosity is enabled print out the full path/arguments log.Debugf("%v\n", qemuCmd.Args) @@ -589,7 +625,7 @@ func buildQemuCmdline(config QemuConfig) (QemuConfig, []string) { return config, qemuArgs } -func discoverBinaries(config QemuConfig) (QemuConfig, error) { +func discoverQemu(config QemuConfig) (QemuConfig, error) { if config.QemuImgPath != "" { return config, nil } @@ -611,6 +647,16 @@ func discoverBinaries(config QemuConfig) (QemuConfig, error) { return config, nil } +func discoverVirtiofsd(config QemuConfig) (QemuConfig, error) { + if config.VirtiofsdBinPath != "" { + return config, nil + } + + virtiofsdPath := filepath.Dir(config.QemuBinPath) + config.VirtiofsdBinPath = filepath.Join(virtiofsdPath, "..", "lib", "qemu", "virtiofsd") + return config, nil +} + func buildQemuForwardings(publishFlags multipleFlag) (string, error) { if len(publishFlags) == 0 { return "", nil diff --git a/src/cmd/linuxkit/run_virtualizationframework.go b/src/cmd/linuxkit/run_virtualizationframework.go index 59b5ed083..d47f474ad 100644 --- a/src/cmd/linuxkit/run_virtualizationframework.go +++ b/src/cmd/linuxkit/run_virtualizationframework.go @@ -14,23 +14,25 @@ const ( ) type virtualizationFramwworkConfig struct { - cpus uint - mem uint64 - disks Disks - data string - dataPath string - state string - networking string - kernelBoot bool + cpus uint + mem uint64 + disks Disks + data string + dataPath string + state string + networking string + kernelBoot bool + virtiofsShares []string } func runVirtualizationFrameworkCmd() *cobra.Command { var ( - data string - dataPath string - state string - networking string - kernelBoot bool + data string + dataPath string + state string + networking string + kernelBoot bool + virtiofsShares []string ) cmd := &cobra.Command{ @@ -43,14 +45,15 @@ func runVirtualizationFrameworkCmd() *cobra.Command { Example: "linuxkit run virtualization [options] prefix", RunE: func(cmd *cobra.Command, args []string) error { cfg := virtualizationFramwworkConfig{ - cpus: uint(cpus), - mem: uint64(mem) * 1024 * 1024, - disks: disks, - data: data, - dataPath: dataPath, - state: state, - networking: networking, - kernelBoot: kernelBoot, + cpus: uint(cpus), + mem: uint64(mem) * 1024 * 1024, + disks: disks, + data: data, + dataPath: dataPath, + state: state, + networking: networking, + kernelBoot: kernelBoot, + virtiofsShares: virtiofsShares, } return runVirtualizationFramework(cfg, args[0]) }, @@ -63,6 +66,7 @@ func runVirtualizationFrameworkCmd() *cobra.Command { cmd.Flags().StringVar(&networking, "networking", virtualizationNetworkingDefault, "Networking mode. Valid options are 'default', 'vmnet' and 'none'. 'vmnet' uses the Apple vmnet framework. 'none' disables networking.`") cmd.Flags().BoolVar(&kernelBoot, "kernel", false, "Boot image is kernel+initrd+cmdline 'path'-kernel/-initrd/-cmdline") + cmd.Flags().StringArrayVar(&virtiofsShares, "virtiofs", []string{}, "Directory shared on virtiofs") return cmd } diff --git a/src/cmd/linuxkit/run_virtualizationframework_darwin_cgo_enabled.go b/src/cmd/linuxkit/run_virtualizationframework_darwin_cgo_enabled.go index fe132ea26..d1345fae7 100644 --- a/src/cmd/linuxkit/run_virtualizationframework_darwin_cgo_enabled.go +++ b/src/cmd/linuxkit/run_virtualizationframework_darwin_cgo_enabled.go @@ -239,6 +239,30 @@ func runVirtualizationFramework(cfg virtualizationFramwworkConfig, path string) config.SetSocketDevicesVirtualMachineConfiguration([]vz.SocketDeviceConfiguration{ socketDeviceConfiguration, }) + + if len(cfg.virtiofsShares) > 0 { + var cs []vz.DirectorySharingDeviceConfiguration + + for idx, share := range cfg.virtiofsShares { + tag := fmt.Sprintf("virtiofs%d", idx) + device, err := vz.NewVirtioFileSystemDeviceConfiguration(tag) + if err != nil { + log.Fatal("virtiofs device configuration failed", err) + } + dir, err := vz.NewSharedDirectory(share, false) + if err != nil { + log.Fatal("virtiofs shared directory failed", err) + } + single, err := vz.NewSingleDirectoryShare(dir) + if err != nil { + log.Fatal("virtiofs single directory share failed", err) + } + device.SetDirectoryShare(single) + cs = append(cs, device) + } + config.SetDirectorySharingDevicesVirtualMachineConfiguration(cs) + } + validated, err := config.Validate() if !validated || err != nil { log.Fatal("validation failed", err)