From d2ae636f52c0a5de50e397592711b3c775f10962 Mon Sep 17 00:00:00 2001 From: Seungjae Yoo Date: Mon, 20 Oct 2025 14:11:53 +0900 Subject: [PATCH 1/3] Canonical config supports vhost_user_vsock option --- .../commands/cvd/cli/parser/instance/cf_vm_configs.cpp | 7 +++++++ .../host/commands/cvd/cli/parser/load_config.proto | 1 + 2 files changed, 8 insertions(+) diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_vm_configs.cpp b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_vm_configs.cpp index 10ee89b926e..ef1469981f1 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_vm_configs.cpp +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/instance/cf_vm_configs.cpp @@ -113,6 +113,12 @@ static std::string V4l2Proxy(const Instance& instance) { return crosvm.has_v4l2_proxy() ? crosvm.v4l2_proxy() : default_val; } +static std::string VhostUserVsock(const Instance& instance) { + const auto& crosvm = instance.vm().crosvm(); + const auto& default_val = CF_DEFAULTS_VHOST_USER_VSOCK; + return crosvm.has_vhost_user_vsock() ? crosvm.vhost_user_vsock() : default_val; +} + static Result> CustomConfigsFlagValue( const Instance& instance) { if (instance.vm().custom_actions().empty()) { @@ -155,6 +161,7 @@ Result> GenerateVmFlags( GenerateInstanceFlag("enable_sandbox", cfg, EnableSandbox), GenerateInstanceFlag("crosvm_simple_media_device", cfg, SimpleMediaDevice), GenerateInstanceFlag("crosvm_v4l2_proxy", cfg, V4l2Proxy), + GenerateInstanceFlag("vhost_user_vsock", cfg, VhostUserVsock), }; return MergeResults(std::move(flags), CF_EXPECT(CustomConfigsFlags(cfg))); } diff --git a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/load_config.proto b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/load_config.proto index d871e6a7781..65f957bce0a 100644 --- a/base/cvd/cuttlefish/host/commands/cvd/cli/parser/load_config.proto +++ b/base/cvd/cuttlefish/host/commands/cvd/cli/parser/load_config.proto @@ -150,6 +150,7 @@ message Crosvm { optional bool enable_sandbox = 1; optional bool simple_media_device = 2; optional string v4l2_proxy = 3; + optional string vhost_user_vsock = 4; } message Gem5 {} From 236f47f241fddc3fe8a1f68fb48138596a95ce27 Mon Sep 17 00:00:00 2001 From: Seungjae Yoo Date: Tue, 21 Oct 2025 10:37:30 +0900 Subject: [PATCH 2/3] Host Orchestrator can specify default value of vhost user vsock option. --- ...ation.cuttlefish-host_orchestrator.default | 4 ++ ...stration.cuttlefish-host_orchestrator.init | 3 ++ frontend/src/host_orchestrator/main.go | 2 + .../orchestrator/controller.go | 2 + .../orchestrator/createcvdaction.go | 53 +++++++++++++++++++ .../host_orchestrator/orchestrator/cvd/cvd.go | 4 ++ .../orchestrator/instancemanager.go | 3 ++ 7 files changed, 71 insertions(+) diff --git a/frontend/debian/cuttlefish-orchestration.cuttlefish-host_orchestrator.default b/frontend/debian/cuttlefish-orchestration.cuttlefish-host_orchestrator.default index 43da5db5c5e..9b7c422782f 100644 --- a/frontend/debian/cuttlefish-orchestration.cuttlefish-host_orchestrator.default +++ b/frontend/debian/cuttlefish-orchestration.cuttlefish-host_orchestrator.default @@ -28,3 +28,7 @@ orchestrator_cvd_artifacts_dir=/var/lib/cuttlefish-common # Indicates whether to use the GCE metadata to get the Build API credentials. # Defaults to false. # build_api_credentials_use_gce_metadata= +# +# Select a default option of vhost_user_vsock for launching CVD. +# Defailts to "" +# vhost_user_vsock= diff --git a/frontend/debian/cuttlefish-orchestration.cuttlefish-host_orchestrator.init b/frontend/debian/cuttlefish-orchestration.cuttlefish-host_orchestrator.init index 02fd245b497..70c359d80d0 100755 --- a/frontend/debian/cuttlefish-orchestration.cuttlefish-host_orchestrator.init +++ b/frontend/debian/cuttlefish-orchestration.cuttlefish-host_orchestrator.init @@ -95,6 +95,9 @@ start_orchestrator() { if [[ -n "${build_api_credentials_use_gce_metadata}" ]]; then args+=("--build_api_credentials_use_gce_metadata=${build_api_credentials_use_gce_metadata}") fi + if [[ -n "${vhost_user_vsock}" ]]; then + args+=("--vhost_user_vsock=${vhost_user_vsock}") + fi start-stop-daemon --start \ --pidfile "${ORCHESTRATOR_PIDFILE}" \ diff --git a/frontend/src/host_orchestrator/main.go b/frontend/src/host_orchestrator/main.go index 45fd9e66bd9..dff74d3cf30 100644 --- a/frontend/src/host_orchestrator/main.go +++ b/frontend/src/host_orchestrator/main.go @@ -130,6 +130,7 @@ func main() { address := flag.String("listen_addr", DefaultListenAddress, "IP address to listen for requests.") logFile := flag.String("log_file", "", "Path to file to write logs to.") buildAPICredsUseGCEMetadata := flag.Bool("build_api_credentials_use_gce_metadata", false, "Indicates whether to use the GCE metadata to get the Build API credentials") + vhostUserVsock := flag.String("vhost_user_vsock", "", "Selected default option of vhost_user_vsock for launching CVD.") flag.Parse() @@ -179,6 +180,7 @@ func main() { BuildAPICredentials: orchestrator.BuildAPICredentialsConfig{ UseGCEMetadata: *buildAPICredsUseGCEMetadata, }, + VhostUserVsock: *vhostUserVsock, }, OperationManager: om, WaitOperationDuration: 2 * time.Minute, diff --git a/frontend/src/host_orchestrator/orchestrator/controller.go b/frontend/src/host_orchestrator/orchestrator/controller.go index 511f304a971..daf59f338d4 100644 --- a/frontend/src/host_orchestrator/orchestrator/controller.go +++ b/frontend/src/host_orchestrator/orchestrator/controller.go @@ -46,6 +46,7 @@ type Config struct { Paths IMPaths AndroidBuildServiceURL string BuildAPICredentials BuildAPICredentialsConfig + VhostUserVsock string } type BuildAPICredentialsConfig struct { @@ -255,6 +256,7 @@ func (h *createCVDHandler) Handle(r *http.Request) (interface{}, error) { UserArtifactsDirResolver: h.UADirResolver, FetchCredentials: creds, BuildAPIBaseURL: h.Config.AndroidBuildServiceURL, + VhostUserVsock: h.Config.VhostUserVsock, } return NewCreateCVDAction(opts).Run() } diff --git a/frontend/src/host_orchestrator/orchestrator/createcvdaction.go b/frontend/src/host_orchestrator/orchestrator/createcvdaction.go index 4b841f56164..53d46322c9b 100644 --- a/frontend/src/host_orchestrator/orchestrator/createcvdaction.go +++ b/frontend/src/host_orchestrator/orchestrator/createcvdaction.go @@ -16,6 +16,7 @@ package orchestrator import ( "encoding/json" + "errors" "io/ioutil" "log" "os" @@ -38,6 +39,7 @@ type CreateCVDActionOpts struct { UserArtifactsDirResolver UserArtifactsDirResolver FetchCredentials cvd.FetchCredentials BuildAPIBaseURL string + VhostUserVsock string } type CreateCVDAction struct { @@ -51,6 +53,7 @@ type CreateCVDAction struct { userArtifactsDirResolver UserArtifactsDirResolver fetchCredentials cvd.FetchCredentials buildAPIBaseURL string + VhostUserVsock string } func NewCreateCVDAction(opts CreateCVDActionOpts) *CreateCVDAction { @@ -64,6 +67,7 @@ func NewCreateCVDAction(opts CreateCVDActionOpts) *CreateCVDAction { userArtifactsDirResolver: opts.UserArtifactsDirResolver, fetchCredentials: opts.FetchCredentials, buildAPIBaseURL: opts.BuildAPIBaseURL, + VhostUserVsock: opts.VhostUserVsock, execContext: execCtx, cvdCLI: cvd.NewCLI(execCtx), } @@ -97,6 +101,9 @@ func (a *CreateCVDAction) launchCVD(op apiv1.Operation) { } func (a *CreateCVDAction) launchWithCanonicalConfig(op apiv1.Operation) (*apiv1.CreateCVDResponse, error) { + if err := a.applyVhostUserVsockOptionIfNeeded(); err != nil { + return nil, err + } data, err := json.MarshalIndent(a.req.EnvConfig, "", " ") if err != nil { return nil, err @@ -178,6 +185,7 @@ func (a *CreateCVDAction) launchFromAndroidCI( startParams := startCVDParams{ InstanceCount: instancesCount, MainArtifactsDir: targetDir, + VhostUserVsock: a.VhostUserVsock, } if buildSource.KernelBuild != nil { startParams.KernelDir = targetDir @@ -230,3 +238,48 @@ func createTempFile(pattern string, data string, mode os.FileMode) (*os.File, er } return file, nil } + +func (a *CreateCVDAction) applyVhostUserVsockOptionIfNeeded() error { + if a.VhostUserVsock == "" { + // Skip this function if VhostUserVsock option is not specified. + return nil + } + instances, exists := a.req.EnvConfig["instances"] + if !exists { + return nil + } + instancesArr, ok := instances.([]interface{}) + if !ok { + return errors.New("unexpected type different from JSON array on 'instances'") + } + for _, ins := range instancesArr { + insMap, ok := ins.(map[string]interface{}) + if !ok { + return errors.New("unexpected type different from JSON object on the member of 'instances'") + } + vm, exists := insMap["vm"] + if !exists { + vm = make(map[string]interface{}) + insMap["vm"] = vm + } + vmMap, ok := vm.(map[string]interface{}) + if !ok { + return errors.New("unexpected type different from JSON object on 'vm'") + } + crosvm, exists := vmMap["crosvm"] + if !exists { + crosvm = make(map[string]interface{}) + vmMap["crosvm"] = crosvm + } + crosvmMap, ok := crosvm.(map[string]interface{}) + if !ok { + return errors.New("unexpected type different from JSON object on 'crosvm'") + } + if _, exists := crosvmMap["vhost_user_vsock"]; !exists { + // Specify VhostUserVsock option only when canonical config doesn't + // specify any options. + crosvmMap["vhost_user_vsock"] = a.VhostUserVsock + } + } + return nil +} diff --git a/frontend/src/host_orchestrator/orchestrator/cvd/cvd.go b/frontend/src/host_orchestrator/orchestrator/cvd/cvd.go index 7e62faf6719..bd9c14693cf 100644 --- a/frontend/src/host_orchestrator/orchestrator/cvd/cvd.go +++ b/frontend/src/host_orchestrator/orchestrator/cvd/cvd.go @@ -537,6 +537,7 @@ type StartOptions struct { InitramfsImage string BootloaderRom string ReportUsageStats bool + VhostUserVsock string } func (o *StartOptions) toArgs() []string { @@ -558,6 +559,9 @@ func (o *StartOptions) toArgs() []string { } else { args = append(args, "--report_anonymous_usage_stats=n") } + if o.VhostUserVsock != "" { + args = append(args, "--vhost_user_vsock", o.VhostUserVsock) + } return args } diff --git a/frontend/src/host_orchestrator/orchestrator/instancemanager.go b/frontend/src/host_orchestrator/orchestrator/instancemanager.go index 59b8582b310..d66d0a312d6 100644 --- a/frontend/src/host_orchestrator/orchestrator/instancemanager.go +++ b/frontend/src/host_orchestrator/orchestrator/instancemanager.go @@ -158,6 +158,8 @@ type startCVDParams struct { KernelDir string // OPTIONAL. If set, bootloader relevant artifacts will be pulled from this dir. BootloaderDir string + // OPTIONAL. + VhostUserVsock string } func CreateCVD(ctx hoexec.ExecContext, p startCVDParams) (*cvd.Group, error) { @@ -169,6 +171,7 @@ func CreateCVD(ctx hoexec.ExecContext, p startCVDParams) (*cvd.Group, error) { startOpts := cvd.StartOptions{ ReportUsageStats: reportAnonymousUsageStats, + VhostUserVsock: p.VhostUserVsock, } if p.KernelDir != "" { startOpts.KernelImage = fmt.Sprintf("%s/bzImage", p.KernelDir) From d248bf835166cd215efd520214a1156c3f55c3b4 Mon Sep 17 00:00:00 2001 From: Seungjae Yoo Date: Tue, 21 Oct 2025 10:38:49 +0900 Subject: [PATCH 3/3] Docker instances created by CO enables vhost user vsock as default --- docker/guest/run_services.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker/guest/run_services.sh b/docker/guest/run_services.sh index 51ea3b3838c..bfa600580e3 100644 --- a/docker/guest/run_services.sh +++ b/docker/guest/run_services.sh @@ -1,5 +1,9 @@ #!/usr/bin/env bash +if [[ -n "$HANDLED_BY_CLOUD_ORCHESTRATOR" ]]; then + echo -n 'vhost_user_vsock="true"' >> /etc/default/cuttlefish-host_orchestrator +fi + service nginx start service cuttlefish-host-resources start service cuttlefish-operator start